ACTS
Experiment-independent tracking
Loading...
Searching...
No Matches
consistency_checker.hpp
1// This file is part of the ACTS project.
2//
3// Copyright (C) 2016 CERN for the benefit of the ACTS project
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9#pragma once
10
11// Project include(s)
12#include "detray/core/concepts.hpp"
13#include "detray/geometry/surface.hpp"
14#include "detray/geometry/tracking_volume.hpp"
15#include "detray/material/concepts.hpp"
16#include "detray/material/predefined_materials.hpp"
17#include "detray/utils/logging.hpp"
18#include "detray/utils/ranges.hpp"
19#include "detray/utils/type_registry.hpp"
20
21// System include(s)
22#include <iostream>
23#include <sstream>
24#include <stdexcept>
25#include <utility>
26
27namespace detray::detail {
28
30template <typename store_t, std::size_t... I>
31void report_empty(const store_t &store,
32 [[maybe_unused]] const std::string &store_name,
33 std::index_sequence<I...> /*seq*/) {
34 ((store.template empty<types::id_cast<typename store_t::value_types, I>>()
35 ?
36#if DETRAY_LOG_LVL < 0
37 std::clog << ""
38#else
39 // The log macro does not compile here...
40 std::clog << "DETRAY WARNING (HOST): " << __FILENAME__ << ":"
41 << __LINE__ << " " << store_name
42 << " has empty collection no. " << I << std::endl
43#endif
44 : std::clog << ""),
45 ...);
46}
47
56 template <concepts::detector detector_t>
57 DETRAY_HOST_DEVICE void operator()(
58 const typename detector_t::surface_type &sf_descr, const detector_t &det,
59 const dindex vol_idx, const typename detector_t::name_map &names) const {
60 const auto sf = geometry::surface{det, sf_descr};
61 const auto vol = tracking_volume{det, vol_idx};
62 std::stringstream err_stream{};
63 err_stream << "Volume \"" << vol.name(names) << "\":\n";
64
65 if (!sf.self_check(err_stream)) {
66 throw std::invalid_argument(err_stream.str());
67 }
68
69 if (sf.volume() != vol_idx) {
70 err_stream << "Incorrect volume index on surface: vol. index " << vol_idx
71 << ", sf: " << sf;
72
73 DETRAY_FATAL_HOST(err_stream.str());
74 throw std::invalid_argument(err_stream.str());
75 }
76
77 // Does the mask link to an existing volume?
78 const auto vol_links = sf.volume_links();
79 for (const auto vol_link : vol_links) {
80 if (!detail::is_invalid_value(vol_link) &&
81 (vol_link >= det.volumes().size())) {
82 err_stream << "Incorrect volume link to non-existent volume "
83 << vol_link << " on surface " << sf;
84
85 DETRAY_FATAL_HOST(err_stream.str());
86 throw std::invalid_argument(err_stream.str());
87 }
88 }
89
90 // A passive surface should have material, if the detector was
91 // constructed with material
92 if (!det.material_store().all_empty() &&
93 (sf.is_passive() && !sf.has_material())) {
94 DETRAY_WARN_HOST(err_stream.str());
95 DETRAY_WARN_HOST("Passive surface without material: " << sf_descr
96 << "\n");
97 }
98
99 // Check that the same surface is registered in the detector surface
100 // lookup
101 const auto sf_from_lkp =
102 geometry::surface{det, det.surface(sf.identifier())};
103 if (sf_from_lkp != sf) {
104 err_stream << "Surfaces in volume and detector lookups "
105 << "differ:\n In volume: " << vol
106 << "\nFound in detector surface lookup: " << sf_from_lkp;
107
108 DETRAY_FATAL_HOST(err_stream.str());
109 throw std::runtime_error(err_stream.str());
110 }
111 }
112
119 template <typename sf_descr_t, typename sf_source_descr_t>
120 DETRAY_HOST_DEVICE void operator()(const sf_descr_t &ref_descr,
121 const sf_source_descr_t &check_descr,
122 bool &success) const {
123 // Check that the other surfaces in the acceleration structure belong
124 // there The volume index of the check_descr must be checked to be
125 // correct beforehand, e.g. by the call operator above
126 if (ref_descr.volume() != check_descr.volume()) {
127 std::stringstream err_stream{};
128 err_stream << "Inconsistent volume index in accel. structure: "
129 << "Expected: " << check_descr.volume()
130 << ". Got surface in accel. structure: " << ref_descr;
131
132 DETRAY_FATAL_HOST(err_stream.str());
133 throw std::invalid_argument(err_stream.str());
134 }
135
136 // Check if it is the surface we are looking for
137 if (ref_descr == check_descr) {
138 success = true;
139 }
140 }
141};
142
149 template <typename material_t>
150 void throw_material_error(const std::string &type, const dindex idx,
151 const material_t &mat) const {
152 std::stringstream err_stream{};
153 err_stream << "Invalid material found in: " << type << " at index " << idx
154 << ": " << mat;
155 DETRAY_FATAL_HOST(err_stream.str());
156 throw std::invalid_argument(err_stream.str());
157 }
158
164 template <typename material_coll_t, typename index_t, typename id_t>
166 DETRAY_HOST_DEVICE void operator()(const material_coll_t &material_coll,
167 const index_t idx, const id_t id) const {
168 try {
169 const auto mat_map = material_coll.at(idx);
170
171 // Check whether there are any entries in the bins
172 if (mat_map.size() == 0u) {
173 std::stringstream err_stream{};
174 err_stream << "Empty material grid: " << static_cast<int>(id)
175 << " at index " << idx;
176 DETRAY_FATAL_HOST(err_stream.str());
177 throw std::invalid_argument(err_stream.str());
178 } else {
179 for (const auto &bin : mat_map.bins()) {
180 if (bin.size() == 0u) {
181 std::stringstream err_stream{};
182 err_stream << "Empty material bin: " << static_cast<int>(id)
183 << " at index " << idx;
184 DETRAY_FATAL_HOST(err_stream.str());
185 throw std::invalid_argument(err_stream.str());
186 }
187 }
188 }
189 } catch (std::out_of_range & /*unused*/) {
190 std::stringstream err_stream{};
191 err_stream << "Out of range material access in: "
192 << "binned material collection at index " << idx;
193 DETRAY_FATAL_HOST(err_stream.str());
194 throw std::invalid_argument(err_stream.str());
195 }
196 }
197
202 template <typename material_coll_t, typename index_t, typename id_t>
204 typename material_coll_t::value_type>
205 DETRAY_HOST_DEVICE void operator()(const material_coll_t &material_coll,
206 const index_t idx,
207 const id_t /*unused*/) const {
208 using material_t = typename material_coll_t::value_type;
209 using scalar_t = typename material_t::scalar_type;
210
211 try {
212 const material_t &mat = material_coll.at(idx);
213
214 // Homogeneous volume material
216 if (mat == detray::vacuum<scalar_t>{}) {
217 throw_material_error("homogeneous volume material", idx, mat);
218 }
219
220 } else {
221 // Material slabs and rods
222 if (!mat) {
223 throw_material_error("homogeneous surface material", idx, mat);
224 }
225 }
226 } catch (std::out_of_range & /*unused*/) {
227 std::stringstream err_stream{};
228 err_stream << "Out of range material access in: "
229 << "homogeneous material collection at index " << idx;
230 DETRAY_FATAL_HOST(err_stream.str());
231 throw std::invalid_argument(err_stream.str());
232 }
233 }
234};
235
240template <concepts::detector detector_t>
241inline void check_empty(const detector_t &det, const bool verbose) {
242 // Check if there is at least one portal in the detector
243 auto find_portals = [&det]() {
244 if (det.portals().empty()) {
245 return false;
246 }
247 // In the brute force finder, also other surfaces can be contained, e.g.
248 // passive surfaces (depends on the detector)
249 return std::ranges::any_of(
250 det.portals(), [](auto pt_desc) { return pt_desc.is_portal(); });
251 };
252
253 // TODO: Check for empty volume acceleration structures
254
255 // Fatal errors
256 if (det.volumes().empty()) {
257 std::string err_str{"No volumes in detector"};
258 DETRAY_FATAL_HOST(err_str);
259 throw std::runtime_error(err_str);
260 }
261 if (det.surfaces().empty()) {
262 std::string err_str{"No surfaces found"};
263 DETRAY_FATAL_HOST(err_str);
264 throw std::runtime_error(err_str);
265 }
266 if (det.transform_store().empty()) {
267 std::string err_str{"No transforms in detector"};
268 DETRAY_FATAL_HOST(err_str);
269 throw std::runtime_error(err_str);
270 }
271 if (det.mask_store().all_empty()) {
272 std::string err_str{"No masks in detector"};
273 DETRAY_FATAL_HOST(err_str);
274 throw std::runtime_error(err_str);
275 }
276 if (!find_portals()) {
277 std::string err_str{"No portals in detector"};
278 DETRAY_FATAL_HOST(err_str);
279 throw std::runtime_error(err_str);
280 }
281
282 // Warnings
283
284 // Check the material description
285 if (det.material_store().all_empty()) {
286 DETRAY_WARN_HOST("No material in detector");
287 } else if (verbose) {
288 // Check for empty material collections
289 detail::report_empty(
290 det.material_store(), "material store",
291 std::make_index_sequence<detector_t::material::n_types>{});
292 }
293
294 // Check for empty acceleration data structure collections (e.g. grids)
295 if (verbose) {
296 // Check for empty mask collections
297 detail::report_empty(
298 det.mask_store(), "mask store",
299 std::make_index_sequence<detector_t::masks::n_types>{});
300
301 detail::report_empty(
302 det.accelerator_store(), "acceleration data structures store",
303 std::make_index_sequence<detector_t::accel::n_types>{});
304 }
305
306 // TODO: Implement volume acceleration structure check
307}
308
310template <concepts::detector detector_t>
311inline bool check_consistency(const detector_t &det, const bool verbose = false,
312 const typename detector_t::name_map &names = {}) {
313 DETRAY_INFO_HOST("Checking detector consistency...");
314
315 check_empty(det, verbose);
316
317 // Check the volumes
318 constexpr bool portal_eq_passive{
319 detector_t::volume_type::object_id::e_portal ==
320 detector_t::volume_type::object_id::e_passive};
321
322 for (const auto &[idx, vol_desc] : detray::views::enumerate(det.volumes())) {
323 const auto vol = tracking_volume{det, vol_desc};
324
325 std::stringstream err_stream{};
326 err_stream << "Volume \"" << vol.name(names) << "\":\n";
327
328 // Check that nothing is obviously broken
329 if (!vol.self_check(err_stream)) {
330 DETRAY_FATAL_HOST(err_stream.str());
331 throw std::invalid_argument(err_stream.str());
332 }
333
334 // Check consistency in the context of the owning detector
335 if (vol.index() != idx) {
336 err_stream << "Incorrect volume index! Found volume:\n"
337 << vol << "\nat index " << idx;
338
339 DETRAY_FATAL_HOST(err_stream.str());
340 throw std::invalid_argument(err_stream.str());
341 }
342
343 // Check that only surfaces belonging to this volume are in the surface
344 // range of the detector
345
346 // Check volume indices
347 for (const auto sf_desc : vol.template surfaces<surface_id::e_all>()) {
348 if (sf_desc.volume() != vol.index()) {
349 err_stream << "Surface of other volume detected:\n\nvolume " << vol
350 << "\n\nsurface " << sf_desc;
351
352 DETRAY_FATAL_HOST(err_stream.str());
353 throw std::invalid_argument(err_stream.str());
354 }
355 }
356 // Check surface type: portal or passive
357 for (const auto sf_desc : vol.portals()) {
358 if (!portal_eq_passive && !sf_desc.is_portal()) {
359 err_stream << "Non-portal surface discovered in portal "
360 "range: "
361 << vol_desc.template sf_link<surface_id::e_portal>()
362 << "\n\nvolume " << vol << "\n\nsurface " << sf_desc;
363
364 DETRAY_FATAL_HOST(err_stream.str());
365 throw std::invalid_argument(err_stream.str());
366 }
367
368 if (portal_eq_passive && !(sf_desc.is_portal() || sf_desc.is_passive())) {
369 err_stream << "Sensitive surface discovered in portal/passive "
370 "range: "
371 << vol_desc.template sf_link<surface_id::e_portal>()
372 << "\n\nvolume " << vol << "\n\nsurface " << sf_desc;
373
374 DETRAY_FATAL_HOST(err_stream.str());
375 throw std::invalid_argument(err_stream.str());
376 }
377 }
378 // Check surface type: passive or portal
379 for (const auto sf_desc : vol.template surfaces<surface_id::e_passive>()) {
380 if (!portal_eq_passive && !sf_desc.is_passive()) {
381 err_stream << "Non-passive surface discovered in passive "
382 "range: "
383 << vol_desc.template sf_link<surface_id::e_passive>()
384 << "\n\nvolume " << vol << "\n\nsurface " << sf_desc;
385
386 DETRAY_FATAL_HOST(err_stream.str());
387 throw std::invalid_argument(err_stream.str());
388 }
389
390 if (portal_eq_passive && !(sf_desc.is_portal() || sf_desc.is_passive())) {
391 err_stream << "Sensitive surface discovered in portal/passive "
392 "range: "
393 << vol_desc.template sf_link<surface_id::e_passive>()
394 << "\n\nvolume " << vol << "\n\nsurface " << sf_desc;
395
396 DETRAY_FATAL_HOST(err_stream.str());
397 throw std::invalid_argument(err_stream.str());
398 }
399 }
400 // Check surface type: sensitive
401 for (const auto sf_desc :
402 vol.template surfaces<surface_id::e_sensitive>()) {
403 if (!sf_desc.is_sensitive()) {
404 err_stream << "Non-sensitive surface discovered in sensitive "
405 "range: "
406 << vol_desc.template sf_link<surface_id::e_sensitive>()
407 << "\n\nvolume " << vol << "\n\nsurface " << sf_desc;
408
409 DETRAY_FATAL_HOST(err_stream.str());
410 throw std::invalid_argument(err_stream.str());
411 }
412 }
413
414 // Go through the acceleration data structures and check the surfaces
415 vol.template visit_surfaces<surface_id::e_all, detail::surface_checker>(
416 det, vol.index(), names);
417
418 // Check the volume material, if present
419 if (vol.has_material()) {
420 vol.template visit_material<detail::material_checker>(
421 vol_desc.material().id());
422 }
423 }
424
425 // Check the surfaces in the detector's surface lookup
426 for (const auto &[idx, sf_desc] : detray::views::enumerate(det.surfaces())) {
427 const auto sf = geometry::surface{det, sf_desc};
428 const auto vol = tracking_volume{det, sf.volume()};
429
430 std::stringstream err_stream{};
431 err_stream << "Volume \"" << vol.name(names) << "\":\n";
432
433 // Check that nothing is obviously broken
434 if (!sf.self_check(err_stream)) {
435 err_stream << "\nat surface no. " << std::to_string(idx);
436 throw std::invalid_argument(err_stream.str());
437 }
438
439 // Check consistency in the context of the owning detector
440 if (sf.index() != idx) {
441 err_stream << "Incorrect surface index! Found surface:\n"
442 << sf << "\nat index " << idx;
443
444 DETRAY_FATAL_HOST(err_stream.str());
445 throw std::invalid_argument(err_stream.str());
446 }
447
448 // Check that the surface can be found in its volume's acceleration
449 // data structures (if there are no grids, must at least be in the
450 // brute force method)
451 bool is_registered = false;
452 vol.template visit_surfaces<surface_id::e_all, detail::surface_checker>(
453 sf_desc, is_registered);
454
455 if (!is_registered) {
456 err_stream << "Found surface that is not part of its "
457 << "volume's navigation acceleration data structures:\n"
458 << "Surface: " << sf;
459
460 DETRAY_FATAL_HOST(err_stream.str());
461 throw std::invalid_argument(err_stream.str());
462 }
463
464 // Check the surface material, if present
465 if (sf.has_material()) {
466 DETRAY_DEBUG_HOST("Checking surface sf=" << sf);
467 sf.template visit_material<detail::material_checker>(
468 sf_desc.material().id());
469 }
470 }
471
472 DETRAY_INFO_HOST("Detector consistency check: OK");
473
474 return true;
475}
476
477} // namespace detray::detail
Facade for a detray detector surface.
Definition surface.hpp:36
Facade for a detray detector volume.
Definition tracking_volume.hpp:44
Homogeneous material.
Definition concepts.hpp:100
Material map concept: Material grid.
Definition concepts.hpp:106
Material parameters.
Definition concepts.hpp:19
unsigned int dindex
Default index type.
Definition indexing.hpp:18
A functor that checks the material parametrization for a surface/volume.
Definition consistency_checker.hpp:144
void operator()(const material_coll_t &material_coll, const index_t idx, const id_t id) const
Test whether a given material map contains invalid material.
Definition consistency_checker.hpp:166
void operator()(const material_coll_t &material_coll, const index_t idx, const id_t) const
Test whether a given collection of material contains invalid material.
Definition consistency_checker.hpp:205
void throw_material_error(const std::string &type, const dindex idx, const material_t &mat) const
Error message for material consistency check.
Definition consistency_checker.hpp:150
A functor that checks the surface descriptor and correct volume index in every acceleration data stru...
Definition consistency_checker.hpp:50
void operator()(const typename detector_t::surface_type &sf_descr, const detector_t &det, const dindex vol_idx, const typename detector_t::name_map &names) const
Test the contained surfaces for consistency.
Definition consistency_checker.hpp:57
void operator()(const sf_descr_t &ref_descr, const sf_source_descr_t &check_descr, bool &success) const
Test whether a given surface check_desc is properly registered at least once in one of the volume acc...
Definition consistency_checker.hpp:120
Elements Declaration.
Definition predefined_materials.hpp:34