File indexing completed on 2026-05-27 07:23:59
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012 #include "detray/core/name_map.hpp"
0013 #include "detray/definitions/containers.hpp"
0014 #include "detray/definitions/detail/qualifiers.hpp"
0015 #include "detray/definitions/geometry.hpp"
0016 #include "detray/definitions/indexing.hpp"
0017 #include "detray/geometry/detail/volume_kernels.hpp"
0018 #include "detray/material/material.hpp"
0019 #include "detray/navigation/accelerators/search_window.hpp"
0020 #include "detray/utils/logging.hpp"
0021 #include "detray/utils/ranges.hpp"
0022
0023
0024 #include <algorithm>
0025 #include <iostream>
0026 #include <sstream>
0027
0028 namespace detray {
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042 template <typename detector_t>
0043 class tracking_volume {
0044
0045 using algebra_type = typename detector_t::algebra_type;
0046 using scalar_type = dscalar<algebra_type>;
0047 using point3_type = dpoint3D<algebra_type>;
0048
0049
0050 using descr_t = typename detector_t::volume_type;
0051 using context_t = typename detector_t::geometry_context;
0052
0053 public:
0054
0055 using name_map = detray::name_map;
0056 using object_id = descr_t::object_id;
0057
0058
0059 tracking_volume() = delete;
0060
0061
0062
0063 constexpr tracking_volume(const detector_t &det, const descr_t &desc)
0064 : m_detector{det}, m_desc{desc} {
0065 assert(m_desc.index() < det.volumes().size());
0066 assert(m_desc.id() != volume_id::e_unknown);
0067 }
0068
0069
0070
0071 constexpr tracking_volume(const detector_t &det, const dindex vol_idx)
0072 : tracking_volume(det, det.volume(vol_idx)) {}
0073
0074
0075 DETRAY_HOST_DEVICE
0076 auto detector() const -> const detector_t & { return m_detector; }
0077
0078
0079
0080
0081 DETRAY_HOST_DEVICE
0082 constexpr auto operator==(const tracking_volume &rhs) const -> bool {
0083 return (&m_detector == &(rhs.m_detector) && m_desc == rhs.m_desc);
0084 }
0085
0086
0087 DETRAY_HOST_DEVICE
0088 constexpr auto id() const -> volume_id { return m_desc.id(); }
0089
0090
0091 DETRAY_HOST_DEVICE
0092 constexpr auto index() const -> dindex { return m_desc.index(); }
0093
0094
0095
0096 DETRAY_HOST_DEVICE
0097 constexpr auto transform() const -> const
0098 typename detector_t::transform3_type & {
0099 return m_detector.transform_store().at(m_desc.transform());
0100 }
0101
0102
0103 DETRAY_HOST_DEVICE
0104 constexpr auto center() const -> point3_type {
0105 return transform().translation();
0106 }
0107
0108
0109
0110 DETRAY_HOST_DEVICE constexpr auto material_parameters(
0111 const point3_type &loc_p) const -> const detray::material<scalar_type> * {
0112 return visit_material<typename detail::get_material_params>(loc_p);
0113 }
0114
0115
0116 DETRAY_HOST_DEVICE
0117 constexpr bool has_material() const { return m_desc.has_material(); }
0118
0119
0120 template <surface_id sf_type = surface_id::e_all>
0121 DETRAY_HOST_DEVICE constexpr decltype(auto) surfaces() const {
0122 if constexpr (sf_type == surface_id::e_all) {
0123 return detray::ranges::subrange{m_detector.surfaces(),
0124 m_desc.full_sf_range()};
0125 } else {
0126 return detray::ranges::subrange{m_detector.surfaces(),
0127 m_desc.template sf_link<sf_type>()};
0128 }
0129 }
0130
0131
0132 DETRAY_HOST_DEVICE constexpr decltype(auto) portals() const {
0133 return surfaces<surface_id::e_portal>();
0134 }
0135
0136
0137 DETRAY_HOST_DEVICE constexpr dindex n_portals() const {
0138 const auto pt_idx_range = m_desc.template sf_link<surface_id::e_portal>();
0139
0140 assert(pt_idx_range[1] > pt_idx_range[0]);
0141 return pt_idx_range[1] - pt_idx_range[0];
0142 }
0143
0144
0145 DETRAY_HOST_DEVICE constexpr dindex n_sensitives() const {
0146 const auto sens_idx_range =
0147 m_desc.template sf_link<surface_id::e_sensitive>();
0148
0149 assert(sens_idx_range[1] >= sens_idx_range[0]);
0150 return sens_idx_range[1] - sens_idx_range[0];
0151 }
0152
0153
0154 DETRAY_HOST_DEVICE constexpr dindex n_passives() const {
0155 const auto ps_idx_range = m_desc.template sf_link<surface_id::e_passive>();
0156
0157 assert(ps_idx_range[1] >= ps_idx_range[0]);
0158 return ps_idx_range[1] - ps_idx_range[0];
0159 }
0160
0161
0162
0163
0164
0165
0166
0167 template <object_id I, typename functor_t, typename... Args>
0168 DETRAY_HOST_DEVICE constexpr void visit_accelerator(Args &&...args) const {
0169 static_assert(I < object_id::e_all);
0170
0171 if (const auto &link{m_desc.template accel_link<I>()}; !link.is_invalid()) {
0172
0173
0174 m_detector.accelerator_store().template visit<functor_t>(
0175 link, std::forward<Args>(args)...);
0176 }
0177 }
0178
0179
0180
0181
0182
0183
0184 template <typename functor_t, int I = static_cast<int>(object_id::e_all) - 1,
0185 typename... Args>
0186 DETRAY_HOST_DEVICE constexpr void visit_accelerators(Args &&...args) const {
0187
0188
0189 for (std::size_t id = 0u; id < static_cast<std::size_t>(object_id::e_size);
0190 ++id) {
0191 if (const auto &link{m_desc.accel_link()[id]}; !link.is_invalid()) {
0192
0193
0194 m_detector.accelerator_store().template visit<functor_t>(
0195 link, std::forward<Args>(args)...);
0196 }
0197 }
0198 }
0199
0200
0201
0202
0203
0204
0205
0206
0207 template <surface_id I, typename functor_t, typename... Args>
0208 DETRAY_HOST_DEVICE constexpr void visit_surfaces(Args &&...args) const {
0209 using surface_getter_t = detail::apply_to_surfaces<functor_t>;
0210
0211
0212 if constexpr (I == surface_id::e_portal) {
0213 visit_accelerator<object_id::e_portal, surface_getter_t>(
0214 std::forward<Args>(args)...);
0215 } else if constexpr (I == surface_id::e_sensitive) {
0216 visit_accelerator<object_id::e_sensitive, surface_getter_t>(
0217 std::forward<Args>(args)...);
0218 } else if constexpr (I == surface_id::e_passive) {
0219 visit_accelerator<object_id::e_passive, surface_getter_t>(
0220 std::forward<Args>(args)...);
0221 } else {
0222
0223
0224 visit_accelerator<object_id::e_portal, surface_getter_t>(
0225 std::forward<Args>(args)...);
0226 if constexpr (object_id::e_portal != object_id::e_passive) {
0227 visit_accelerator<object_id::e_passive, surface_getter_t>(
0228 std::forward<Args>(args)...);
0229 }
0230 visit_accelerator<object_id::e_sensitive, surface_getter_t>(
0231 std::forward<Args>(args)...);
0232 }
0233 }
0234
0235
0236
0237
0238
0239 template <typename functor_t, typename... Args>
0240 DETRAY_HOST_DEVICE constexpr void visit_daughter_volumes(
0241 Args &&...args) const {
0242 using volume_getter_t = detail::apply_to_volumes<functor_t>;
0243
0244 visit_accelerator<object_id::e_volume, volume_getter_t>(
0245 std::forward<Args>(args)...);
0246 }
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259 template <object_id I, typename functor_t, typename track_t,
0260 concepts::arithmetic window_size_t, typename... Args>
0261 DETRAY_HOST_DEVICE constexpr void visit_neighborhood(
0262 const track_t &track, const search_window<window_size_t, 2> &win_size,
0263 const context_t &ctx, Args &&...args) const {
0264 if constexpr (I == object_id::e_all) {
0265 visit_accelerators<detail::apply_to_neighbourhood<functor_t>>(
0266 m_detector, m_desc, track, win_size, ctx,
0267 std::forward<Args>(args)...);
0268 } else {
0269 visit_accelerator<I, detail::apply_to_neighbourhood<functor_t>>(
0270 m_detector, m_desc, track, win_size, ctx,
0271 std::forward<Args>(args)...);
0272 }
0273 }
0274
0275
0276
0277
0278
0279 template <typename functor_t, typename... Args>
0280 DETRAY_HOST_DEVICE constexpr auto visit_material(Args &&...args) const {
0281 assert(has_material());
0282 const auto &materials = m_detector.material_store();
0283 return materials.template visit<functor_t>(m_desc.material(),
0284 std::forward<Args>(args)...);
0285 }
0286
0287
0288
0289
0290
0291
0292 DETRAY_HOST bool self_check(std::ostream &os) const {
0293 if (id() == volume_id::e_unknown) {
0294 os << "DETRAY ERROR (HOST): Unknown volume shape type in volume:\n"
0295 << *this << std::endl;
0296 return false;
0297 }
0298 if (detail::is_invalid_value(index())) {
0299 os << "DETRAY ERROR (HOST): Volume index undefined in volume:\n"
0300 << *this << std::endl;
0301 return false;
0302 }
0303 if (index() >= m_detector.volumes().size()) {
0304 os << "DETRAY ERROR (HOST): Volume index out of bounds in volume:\n"
0305 << *this << std::endl;
0306 return false;
0307 }
0308 if (detail::is_invalid_value(m_desc.transform())) {
0309 os << "DETRAY ERROR (HOST): Volume transform undefined in volume:\n"
0310 << *this << std::endl;
0311 return false;
0312 }
0313 if (m_desc.transform() >= m_detector.transform_store().size()) {
0314 os << "DETRAY ERROR (HOST): Volume transform index out of bounds "
0315 "in volume:\n"
0316 << *this << std::endl;
0317 return false;
0318 }
0319
0320 if (!m_detector.material_store().all_empty() && has_material() &&
0321 m_desc.material().is_invalid_index()) {
0322 os << "DETRAY ERROR (HOST): Volume does not have valid material "
0323 "link:\n"
0324 << *this << std::endl;
0325 return false;
0326 }
0327 const auto &acc_link = m_desc.accel_link();
0328 if (detail::is_invalid_value(acc_link[0])) {
0329 os << "DETRAY ERROR (HOST): Link to portal lookup broken: " << acc_link[0]
0330 << "\n in volume: " << *this << std::endl;
0331 return false;
0332 }
0333 if (const auto &pt_link = m_desc.template sf_link<surface_id::e_portal>();
0334 detail::is_invalid_value(pt_link)) {
0335 os << "DETRAY ERROR (HOST): Link to portal surfaces broken: " << pt_link
0336 << "\n in volume: " << *this << std::endl;
0337 return false;
0338 }
0339
0340 std::vector<dindex_range> sf_ranges = {
0341 m_desc.template sf_link<surface_id::e_portal>()};
0342
0343
0344 if (const auto &sens_range =
0345 m_desc.template sf_link<surface_id::e_sensitive>();
0346 sens_range[0] != sens_range[1]) {
0347 sf_ranges.push_back(sens_range);
0348 }
0349 if (const auto &psv_range =
0350 m_desc.template sf_link<surface_id::e_passive>();
0351 psv_range[0] != psv_range[1]) {
0352 sf_ranges.push_back(psv_range);
0353 }
0354
0355
0356 auto compare_ranges = [](const dindex_range &rg1, const dindex_range &rg2) {
0357 return rg1[0] < rg2[0];
0358 };
0359
0360 std::ranges::sort(sf_ranges, compare_ranges);
0361
0362 if ((sf_ranges.size() > 1 && sf_ranges[0][1] != sf_ranges[1][0]) ||
0363 (sf_ranges.size() > 2 && sf_ranges[1][1] != sf_ranges[2][0])) {
0364 os << "DETRAY ERROR (HOST): Surface index ranges not contiguous: "
0365 << m_desc.sf_link() << "\n in volume: " << *this << std::endl;
0366 return false;
0367 }
0368
0369
0370 bool suspicious_links = false;
0371 std::stringstream warnings{};
0372 for (std::size_t i = 1u; i < acc_link.size(); ++i) {
0373
0374 if (!acc_link[i].is_invalid_id() && acc_link[i].is_invalid_index()) {
0375 suspicious_links = true;
0376 warnings << "Link to acceleration data structure "
0377 << static_cast<int>(acc_link[i].id()) << " is invalid"
0378 << std::endl;
0379 }
0380 }
0381 if (suspicious_links) {
0382 DETRAY_WARN_HOST(warnings.str() << " in volume: " << *this);
0383 }
0384
0385 return true;
0386 }
0387
0388
0389 DETRAY_HOST_DEVICE
0390 auto name(const name_map &names) const -> std::string {
0391 return names.empty() ? "volume " + std::to_string(m_desc.index())
0392 : names.at(m_desc.index());
0393 }
0394
0395
0396 DETRAY_HOST
0397 friend std::ostream &operator<<(std::ostream &os, const tracking_volume &v) {
0398 os << v.m_desc;
0399 return os;
0400 }
0401
0402 private:
0403
0404 const detector_t &m_detector;
0405
0406 const descr_t &m_desc;
0407 };
0408
0409 }