ACTS
Experiment-independent tracking
Loading...
Searching...
No Matches
build_toy_detector.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/builders/cylinder_portal_generator.hpp"
13#include "detray/builders/detector_builder.hpp"
14#include "detray/builders/grid_builder.hpp"
15#include "detray/builders/homogeneous_material_builder.hpp"
16#include "detray/builders/homogeneous_material_generator.hpp"
17#include "detray/builders/material_map_builder.hpp"
18#include "detray/builders/material_map_generator.hpp"
19#include "detray/builders/surface_factory.hpp"
20#include "detray/builders/volume_builder.hpp"
21#include "detray/core/concepts.hpp"
22#include "detray/core/detector.hpp"
23#include "detray/definitions/algebra.hpp"
24#include "detray/definitions/indexing.hpp"
25#include "detray/definitions/units.hpp"
26#include "detray/detectors/toy_metadata.hpp"
27#include "detray/geometry/tracking_volume.hpp"
28#include "detray/material/mixture.hpp"
29#include "detray/material/predefined_materials.hpp"
30#include "detray/utils/consistency_checker.hpp"
31#include "detray/utils/print_detector.hpp"
32#include "detray/utils/ranges.hpp"
33
34// Detray test include(s)
35#include "detray/test/common/factories/barrel_generator.hpp"
36#include "detray/test/common/factories/endcap_generator.hpp"
37
38// Vecmem include(s)
39#include <vecmem/memory/memory_resource.hpp>
40
41// System include(s)
42#include <limits>
43#include <stdexcept>
44#include <string>
45#include <type_traits>
46#include <utility>
47
48namespace detray {
49
51template <concepts::scalar scalar_t>
55 // Barrel module creator
56 m_barrel_factory_cfg.half_length(500.f * unit<scalar_t>::mm)
57 .module_bounds({8.4f * unit<scalar_t>::mm, 36.f * unit<scalar_t>::mm})
58 .tilt_phi(0.14f /*0.145*/)
59 .radial_stagger(0.5f * unit<scalar_t>::mm /*2.f*/)
60 .z_overlap(2.f * unit<scalar_t>::mm /*5.f*/);
61
62 // Endcap module creator
64 .outer_radius(m_outer_radius)
65 .module_bounds({{3.f * unit<scalar_t>::mm, 9.5f * unit<scalar_t>::mm,
66 39.f * unit<scalar_t>::mm},
68 39.f * unit<scalar_t>::mm}})
69 .ring_stagger(2.f * unit<scalar_t>::mm)
70 .phi_stagger({4.f * unit<scalar_t>::mm, 4.f * unit<scalar_t>::mm})
71 .phi_sub_stagger({0.5f * unit<scalar_t>::mm, 0.5f * unit<scalar_t>::mm})
72 .module_tilt({0.f, 0.f})
73 .binning({40u, 68u});
74
75 // Configure the material generation
76 m_material_config.sensitive_material(silicon_tml<scalar_t>())
77 .passive_material(beryllium_tml<scalar_t>()) // < beampipe
78 .portal_material(vacuum<scalar_t>())
79 .thickness(1.5f * unit<scalar_t>::mm);
80
81 // Configure the material map generation
82 m_beampipe_map_cfg.n_bins = {20u, 20u};
83 m_beampipe_map_cfg.axis_index = 1u;
85 m_beampipe_map_cfg.thickness = 0.8f * unit<scalar_t>::mm;
86 // Don't scale the generation of the material thickness
87 m_beampipe_map_cfg.scalor = 0.f;
88 m_beampipe_map_cfg.mat_generator =
89 detray::detail::generate_cyl_mat<scalar_t>;
90
91 m_disc_map_cfg.n_bins = {5u, 20u};
92 m_disc_map_cfg.axis_index = 0u;
93 m_disc_map_cfg.mapped_material =
96 m_disc_map_cfg.thickness = 1.f * unit<scalar_t>::mm;
97 m_disc_map_cfg.scalor = 1e-4f;
98 m_disc_map_cfg.mat_generator = detray::detail::generate_disc_mat<scalar_t>;
99
100 m_cyl_map_cfg.n_bins = {20u, 20u};
101 m_cyl_map_cfg.axis_index = 1u;
102 m_cyl_map_cfg.mapped_material =
105 m_cyl_map_cfg.thickness = 5.f * unit<scalar_t>::mm;
106 m_cyl_map_cfg.scalor = 1e-8f;
107 m_cyl_map_cfg.mat_generator = detray::detail::generate_cyl_mat<scalar_t>;
108 }
109
111 unsigned int m_n_brl_layers{4u};
113 unsigned int m_n_edc_layers{3u};
123 bool m_use_grids{true};
139 std::vector<scalar_t> m_barrel_layer_radii = {
142 172.f * unit<scalar_t>::mm};
143
144 std::vector<std::pair<unsigned int, unsigned int>> m_barrel_binning = {
145 {0u, 0u}, {16u, 14u}, {32u, 14u}, {52u, 14u}, {78u, 14u}};
146
147 std::vector<scalar_t> m_endcap_layer_positions = {
148 600.f * unit<scalar_t>::mm, 700.f * unit<scalar_t>::mm,
149 820.f * unit<scalar_t>::mm, 960.f * unit<scalar_t>::mm,
150 1100.f * unit<scalar_t>::mm, 1300.f * unit<scalar_t>::mm,
151 1500.f * unit<scalar_t>::mm};
152
158 .m_sort = false};
159
160 bool m_do_check{true};
161
167 constexpr toy_det_config &n_brl_layers(const unsigned int n) {
168 m_n_brl_layers = n;
169 return *this;
170 }
171
174 constexpr toy_det_config &n_edc_layers(const unsigned int n) {
175 m_n_edc_layers = n;
176 return *this;
177 }
178
181 constexpr toy_det_config &envelope(const scalar_t env) {
182 m_portal_envelope = env;
183 return *this;
184 }
185
188 constexpr toy_det_config &use_grids(const bool b) {
189 m_use_grids = b;
190 return *this;
191 }
192
195 constexpr toy_det_config &use_homogeneous_material(const bool b) {
197 return *this;
198 }
199
202 constexpr toy_det_config &use_material_maps(const bool b) {
204 return *this;
205 }
206
210 constexpr toy_det_config &cyl_map_bins(const std::size_t n_phi,
211 const std::size_t n_z) {
212 m_cyl_map_cfg.n_bins = {n_phi, n_z};
213 return *this;
214 }
215
219 constexpr toy_det_config &disc_map_bins(const std::size_t n_r,
220 const std::size_t n_phi) {
221 m_disc_map_cfg.n_bins = {n_r, n_phi};
222 return *this;
223 }
224
227 constexpr toy_det_config &material_map_min_thickness(const scalar_t t) {
228 assert(t > 0.f);
229 m_cyl_map_cfg.thickness = t;
230 m_disc_map_cfg.thickness = t;
231 return *this;
232 }
233
236 constexpr toy_det_config &beampipe_mat_thickness(const scalar_t t) {
237 assert(t > 0.f);
239 return *this;
240 }
241
244 constexpr toy_det_config &module_mat_thickness(const scalar_t t) {
245 assert(t > 0.f);
247 return *this;
248 }
249
253 m_cyl_map_cfg.mapped_material = mat;
254 m_disc_map_cfg.mapped_material = mat;
255 return *this;
256 }
257
263 constexpr toy_det_config &do_check(const bool check) {
264 m_do_check = check;
265 return *this;
266 }
267
268
273 constexpr unsigned int n_brl_layers() const { return m_n_brl_layers; }
276 constexpr unsigned int n_edc_layers() const { return m_n_edc_layers; }
279 constexpr const auto &outer_radius() const { return m_outer_radius; }
282 constexpr scalar_t envelope() const { return m_portal_envelope; }
285 constexpr scalar_t beampipe_vol_radius() const {
287 }
288
290 constexpr auto &material_config() { return m_material_config; }
293 constexpr const auto &material_config() const { return m_material_config; }
296 constexpr bool use_grids() const { return m_use_grids; }
299 constexpr bool use_homogeneous_material() const {
301 }
302
304 constexpr bool use_material_maps() const { return m_use_material_maps; }
307 constexpr auto &beampipe_material_map() { return m_beampipe_map_cfg; }
310 constexpr const auto &beampipe_material_map() const {
311 return m_beampipe_map_cfg;
312 }
313
315 constexpr const auto &cyl_material_map() const { return m_cyl_map_cfg; }
318 constexpr auto &cyl_material_map() { return m_cyl_map_cfg; }
321 constexpr const auto &disc_material_map() const { return m_disc_map_cfg; }
324 constexpr auto &disc_material_map() { return m_disc_map_cfg; }
327 constexpr const darray<std::size_t, 2> &cyl_map_bins() const {
328 return m_cyl_map_cfg.n_bins;
329 }
330
332 constexpr const darray<std::size_t, 2> &disc_map_bins() const {
333 return m_disc_map_cfg.n_bins;
334 }
335
337 constexpr scalar_t material_map_min_thickness() const {
338 assert(m_cyl_map_cfg.thickness == m_disc_map_cfg.thickness);
339 return m_cyl_map_cfg.thickness;
340 }
341
343 constexpr scalar_t beampipe_mat_thickness() const {
345 }
346
348 constexpr scalar_t module_mat_thickness() const {
350 }
351
353 auto barrel_mat_generator() const { return m_cyl_map_cfg.mat_generator; }
356 auto edc_mat_generator() const { return m_disc_map_cfg.mat_generator; }
360 assert(m_cyl_map_cfg.mapped_material == m_disc_map_cfg.mapped_material);
361 return m_cyl_map_cfg.mapped_material;
362 }
363
365 constexpr const auto &barrel_layer_radii() const {
367 }
368
370 constexpr const auto &endcap_layer_positions() const {
372 }
373
375 constexpr const auto &barrel_layer_binning() const {
376 return m_barrel_binning;
377 }
378
383
388
391 return m_builder_opts;
392 }
393
395 constexpr bool do_check() const { return m_do_check; }
397
399 friend std::ostream &operator<<(std::ostream &out,
400 const toy_det_config &cfg) {
401 out << "\nToy Detector\n"
402 << "----------------------------\n"
403 << " No. barrel layers : " << cfg.n_brl_layers() << "\n"
404 << " No. endcap layers : " << cfg.n_edc_layers() << "\n"
405 << " Portal envelope : " << cfg.envelope() << " [mm]\n";
406
407 if (cfg.use_material_maps()) {
408 const auto &cyl_map_bins = cfg.cyl_map_bins();
409 const auto &disc_map_bins = cfg.disc_map_bins();
410
411 out << " Material maps \n"
412 << " -> cyl. map bins : (phi: " << cyl_map_bins[0]
413 << ", z: " << cyl_map_bins[1] << ")\n"
414 << " -> disc map bins : (r: " << disc_map_bins[0]
415 << ", phi: " << disc_map_bins[1] << ")\n"
416 << " -> cyl. min. thickness: "
418 << " [mm]\n"
419 << " -> disc min. thickness: "
421 << " [mm]\n"
422 << " -> Material : " << cfg.mapped_material() << "\n";
423 } else if (cfg.use_homogeneous_material()) {
424 out << " Homogeneous material \n"
425 << " -> Thickness : "
427 << " [mm]\n"
428 << " -> Material : " << silicon_tml<scalar_t>() << "\n";
429 } else {
430 out << " No material\n";
431 }
432
433 return out;
434 }
435};
436
437namespace detail {
438
440template <concepts::scalar scalar_t>
441struct extent2D {
443 scalar_t lower;
445 scalar_t upper;
446};
447
455template <typename detector_builder_t, concepts::detector detector_t,
456 typename config_t>
457volume_builder_interface<detector_t> *decorate_material(
458 config_t &cfg, detector_builder_t &det_builder,
460 static_assert(
461 std::is_same_v<detector_t, typename detector_builder_t::detector_type>,
462 "Detector builder and volume builder/surface factory have different "
463 "detector type");
464
465 volume_builder_interface<detector_t> *vm_builder{v_builder};
466
467 // Decorate the builder with material
468 if (cfg.use_material_maps()) {
469 // Build the volume with material maps
470 vm_builder =
471 det_builder.template decorate<material_map_builder<detector_t>>(
472 v_builder);
473 } else if (cfg.use_homogeneous_material()) {
474 // Build the volume with a homogeneous material description
475 vm_builder =
476 det_builder.template decorate<homogeneous_material_builder<detector_t>>(
477 v_builder);
478 }
479
480 if (!vm_builder) {
481 throw std::runtime_error("Material decoration failed");
482 }
483
484 return vm_builder;
485}
486
495template <concepts::detector detector_t>
496std::shared_ptr<surface_factory_interface<detector_t>> decorate_material(
497 toy_det_config<typename detector_t::scalar_type> &cfg,
498 std::unique_ptr<surface_factory_interface<detector_t>> sf_factory,
499 bool is_module_factory = false) {
501 using mask_id = typename detector_t::masks::id;
502 using material_id = typename detector_t::material::id;
503
504 // Decorate the surfaces with material
505 if (cfg.use_material_maps()) {
506 // How to generate the specific material map for every surface
507 material_map_config<scalar_t> material_map_config{};
508
509 // Add the complete configuration for the beampipe map generation
510 cfg.beampipe_material_map().map_id =
511 static_cast<dindex>(material_id::e_concentric_cylinder2D_map);
512 material_map_config.set_map_config(mask_id::e_concentric_cylinder2D,
513 surface_id::e_passive,
514 cfg.beampipe_material_map());
515
516 // Add the complete configuration for disc map generation
517 cfg.disc_material_map().map_id =
518 static_cast<dindex>(material_id::e_ring2D_map);
519 material_map_config.set_map_config(mask_id::e_ring2D, surface_id::e_portal,
520 cfg.disc_material_map());
521
522 // Add the complete configuration for cylinder map generation
523 cfg.cyl_material_map().map_id =
524 static_cast<dindex>(material_id::e_concentric_cylinder2D_map);
525 material_map_config.set_map_config(mask_id::e_concentric_cylinder2D,
526 surface_id::e_portal,
527 cfg.cyl_material_map());
528
529 auto mat_generator = std::make_shared<material_map_generator<detector_t>>(
530 std::move(sf_factory), material_map_config);
531
532 return mat_generator;
533
534 } else if (cfg.use_homogeneous_material() && is_module_factory) {
535 // How to generate the specific material for every surface
536 auto mat_generator =
537 std::make_shared<homogeneous_material_generator<detector_t>>(
538 std::move(sf_factory), cfg.material_config());
539
540 return mat_generator;
541 } else {
542 // If no material should be added, return the factory as is
543 return sf_factory;
544 }
545}
546
559template <concepts::detector detector_t>
560void add_cylinder_portals(volume_builder_interface<detector_t> *v_builder,
561 toy_det_config<typename detector_t::scalar_type> &cfg,
562 const typename detector_t::scalar_type lower_z,
563 const typename detector_t::scalar_type upper_z,
564 const typename detector_t::scalar_type inner_r,
565 const typename detector_t::scalar_type outer_r,
566 const dindex link_north, const dindex link_south,
567 const dindex link_east, const dindex link_west) {
568 using algebra_t = typename detector_t::algebra_type;
569 using transform3_t = dtransform3D<algebra_t>;
570 using scalar_t = dscalar<algebra_t>;
571 using point3_t = dpoint3D<algebra_t>;
572 using nav_link_t = typename detector_t::surface_type::navigation_link;
573
574 const transform3_t identity{};
575 const dindex vol_idx{v_builder->vol_index()};
576
577 scalar_t min_r{math::min(inner_r, outer_r)};
578 scalar_t max_r{math::max(inner_r, outer_r)};
579 scalar_t min_z{math::min(lower_z, upper_z)};
580 scalar_t max_z{math::max(lower_z, upper_z)};
581
582 using factory_interface_t = surface_factory_interface<detector_t>;
583 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
584 using disc_factory_t = surface_factory<detector_t, ring2D>;
585
586 std::shared_ptr<factory_interface_t> pt_cyl_factory{nullptr};
587 std::shared_ptr<factory_interface_t> pt_disc_factory{nullptr};
588
589 if (cfg.use_material_maps()) {
590 pt_cyl_factory =
591 decorate_material<detector_t>(cfg, std::make_unique<cyl_factory_t>());
592 pt_disc_factory =
593 decorate_material<detector_t>(cfg, std::make_unique<disc_factory_t>());
594 } else {
595 pt_cyl_factory = std::make_shared<cyl_factory_t>();
596 pt_disc_factory = std::make_shared<disc_factory_t>();
597 }
598
599 // Inner cylinder portal
600 pt_cyl_factory->push_back({surface_id::e_portal, identity,
601 static_cast<nav_link_t>(link_south),
602 std::vector<scalar_t>{min_r, min_z, max_z}});
603 // Outer cylinder portal
604 pt_cyl_factory->push_back({surface_id::e_portal, identity,
605 static_cast<nav_link_t>(link_north),
606 std::vector<scalar_t>{max_r, min_z, max_z}});
607
608 // Left disc portal
609 pt_disc_factory->push_back(
610 {surface_id::e_portal,
611 transform3_t{
612 point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0), min_z}},
613 static_cast<nav_link_t>(link_west),
614 std::vector<scalar_t>{min_r, max_r}});
615 // Right disc portal
616 pt_disc_factory->push_back(
617 {surface_id::e_portal,
618 transform3_t{
619 point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0), max_z}},
620 static_cast<nav_link_t>(link_east),
621 std::vector<scalar_t>{min_r, max_r}});
622
623 v_builder->add_surfaces(pt_cyl_factory);
624 v_builder->add_surfaces(pt_disc_factory);
625
626 v_builder->set_name("gap_" + std::to_string(vol_idx));
627}
628
634template <typename detector_builder_t>
635inline void add_cylinder_grid(
636 detector_builder_t &det_builder,
637 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
638 &cfg,
639 const dindex vol_index) {
640 using detector_t = typename detector_builder_t::detector_type;
642
643 constexpr auto grid_id =
644 detector_t::accel::id::e_surface_concentric_cylinder2D_grid;
645 using cyl_grid_t = types::get<typename detector_t::accel, grid_id>;
646
647 using grid_builder_t =
648 grid_builder<detector_t, cyl_grid_t, detray::fill_by_pos>;
649
650 const auto &barrel_cfg{cfg.barrel_config()};
651 const scalar_t h_z{barrel_cfg.half_length()};
652
653 auto vgr_builder = det_builder.template decorate<grid_builder_t>(vol_index);
654
655 if (!vgr_builder) {
656 throw std::runtime_error("Grid decoration failed");
657 }
658
659 vgr_builder->set_type(detector_t::geo_obj_ids::e_sensitive);
660 vgr_builder->init_grid(
662 {barrel_cfg.binning().first, barrel_cfg.binning().second});
663}
664
670template <typename detector_builder_t>
671inline void add_disc_grid(
672 detector_builder_t &det_builder,
673 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
674 &cfg,
675 const dindex vol_index) {
676 using detector_t = typename detector_builder_t::detector_type;
678
679 constexpr auto grid_id = detector_t::accel::id::e_surface_ring2D_grid;
680 using disc_grid_t = types::get<typename detector_t::accel, grid_id>;
681
682 using grid_builder_t =
683 grid_builder<detector_t, disc_grid_t, detray::fill_by_pos>;
684
685 const auto &endcap_cfg{cfg.endcap_config()};
686 const scalar_t inner_r{cfg.beampipe_vol_radius()};
687 const scalar_t outer_r{cfg.outer_radius()};
688
689 auto vgr_builder = det_builder.template decorate<grid_builder_t>(vol_index);
690
691 if (!vgr_builder) {
692 throw std::runtime_error("Grid decoration failed");
693 }
694
695 vgr_builder->set_type(detector_t::geo_obj_ids::e_sensitive);
696 vgr_builder->init_grid(
698 {endcap_cfg.binning().size(), endcap_cfg.binning().back()});
699}
700
707template <concepts::detector detector_t>
708inline void get_volume_extent(
709 toy_det_config<typename detector_t::scalar_type> &cfg,
710 const std::shared_ptr<surface_factory_interface<detector_t>> &sf_factory,
711 typename cylinder_portal_generator<detector_t>::boundaries &vol_bounds) {
712 // Get the volume extent from the cylinder factory
713 const cylinder_portal_generator<detector_t> *cyl_factory{nullptr};
714 if (cfg.use_material_maps()) {
715 // Retrieve the underlying portal factory
716 auto decorator =
717 std::dynamic_pointer_cast<const factory_decorator<detector_t>>(
718 sf_factory);
719 if (!decorator) {
720 throw std::bad_cast();
721 }
722 cyl_factory = dynamic_cast<const cylinder_portal_generator<detector_t> *>(
723 decorator->get_factory());
724 } else {
725 // No material was decorated
726 cyl_factory = dynamic_cast<const cylinder_portal_generator<detector_t> *>(
727 sf_factory.get());
728 }
729 if (!cyl_factory) {
730 throw std::bad_cast();
731 }
732
733 // Set the new current boundaries, to construct the next gap
734 vol_bounds = cyl_factory->volume_boundaries();
735}
736
745template <typename detector_builder_t>
746inline auto add_barrel_detector(
747 detector_builder_t &det_builder,
748 typename detector_builder_t::detector_type::geometry_context &gctx,
749 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
750 &cfg,
751 dindex beampipe_idx) {
752 using detector_t = typename detector_builder_t::detector_type;
753 using algebra_t = typename detector_t::algebra_type;
754 using scalar_t = dscalar<algebra_t>;
755 using transform3_t = dtransform3D<algebra_t>;
756 using nav_link_t = typename detector_t::surface_type::navigation_link;
757
758 // Register the sizes in z per volume index
759 std::vector<std::pair<dindex, extent2D<scalar_t>>> volume_sizes{};
760
761 // Mask volume link for portals that exit the detector
762 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
763 const transform3_t identity{};
764
765 // Links to the connector gaps. Their volume index depends on the
766 // number of endcap layer that are being constructed (before and after
767 // the barrel volumes are constructed)
768 auto link_east{end_of_world};
769 auto link_west{end_of_world};
770
771 if (cfg.n_edc_layers() > 0) {
772 link_east = static_cast<nav_link_t>(det_builder.n_volumes() +
773 2u * cfg.n_brl_layers() + 2u);
774 link_west = static_cast<nav_link_t>(det_builder.n_volumes() -
775 2u * cfg.n_edc_layers() + 1u);
776 }
777
778 const scalar_t h_z{cfg.barrel_config().half_length()};
779 // Set the inner radius of the first gap to the radius of the beampipe vol.
780 scalar_t gap_inner_r{cfg.beampipe_vol_radius()};
781
782 typename cylinder_portal_generator<detector_t>::boundaries vol_bounds{};
783
784 // Alternate barrel module layers and gap volumes
785 bool is_gap = true;
786 for (unsigned int i = 0u; i < 2u * cfg.n_brl_layers(); ++i) {
787 // New volume
788 auto v_builder = det_builder.new_volume(volume_id::e_cylinder);
789 auto vm_builder = decorate_material(cfg, det_builder, v_builder);
790 const dindex vol_idx{vm_builder->vol_index()};
791
792 // The barrel volumes are centered at the origin
793 vm_builder->add_volume_placement(identity);
794
795 // Every second layer is a gap volume
796 is_gap = !is_gap;
797 if (is_gap) {
798 auto link_north{vol_idx - 1};
799 auto link_south{vol_idx - 3};
800
801 // The first time a gap is built, it needs to link to the beampipe
802 link_south = (i == 1u) ? beampipe_idx : link_south;
803
804 detail::add_cylinder_portals(vm_builder, cfg, -h_z, h_z, gap_inner_r,
805 vol_bounds.inner_radius, link_north,
806 link_south, link_east, link_west);
807 volume_sizes.push_back({vol_idx, {gap_inner_r, vol_bounds.inner_radius}});
808
809 // Set the inner gap radius for the next gap volume
810 gap_inner_r = vol_bounds.outer_radius;
811
812 vm_builder->set_name("gap_" + std::to_string(vol_idx));
813
814 } else {
815 // Limit to maximum valid link
816 auto link_north{std::min(
817 vol_idx + 3, 2 * cfg.n_edc_layers() + 2 * cfg.n_brl_layers() + 1)};
818 auto link_south{vol_idx + 1};
819
820 // Configure the module factory for this layer
821 auto &barrel_cfg = cfg.barrel_config();
822
823 const unsigned int j{(i + 2u) / 2u};
824 barrel_cfg.binning(cfg.barrel_layer_binning().at(j))
825 .radius(cfg.barrel_layer_radii().at(j));
826
827 // Configure the portal factory
828 cylinder_portal_config<scalar_t> portal_cfg{};
829
830 portal_cfg.envelope(cfg.envelope())
831 .fixed_half_length(h_z)
832 // Link the volume portals to its neighbors
833 .link_north(link_north)
834 .link_south(link_south)
835 .link_east(link_east)
836 .link_west(link_west);
837
838 // Configure the material
839 cfg.material_config().thickness(cfg.module_mat_thickness());
840
841 // Add a layer of module surfaces (may have material)
842 auto module_mat_factory = decorate_material<detector_t>(
843 cfg,
844 std::make_unique<barrel_generator<detector_t, rectangle2D>>(
845 barrel_cfg),
846 true);
847
848 // Add cylinder and disc portals (may have material, depending on
849 // whether material maps are being used or not)
850 auto portal_mat_factory = decorate_material<detector_t>(
851 cfg,
852 std::make_unique<cylinder_portal_generator<detector_t>>(portal_cfg));
853
854 vm_builder->add_surfaces(module_mat_factory, gctx);
855 vm_builder->add_surfaces(portal_mat_factory);
856
857 // Fill the volume extent into 'vol_bounds'
858 get_volume_extent(cfg, portal_mat_factory, vol_bounds);
859
860 volume_sizes.push_back(
861 {vol_idx, {vol_bounds.inner_radius, vol_bounds.outer_radius}});
862
863 vm_builder->set_name("barrel_" + std::to_string(vol_idx));
864
865 if (cfg.use_grids()) {
866 // Add a cylinder grid to every barrel module layer
867 add_cylinder_grid(det_builder, cfg, vol_idx);
868 }
869 }
870 }
871
872 // Add a final gap volume to get to the full barrel radius
873 auto v_builder = det_builder.new_volume(volume_id::e_cylinder);
874 const dindex vol_idx{v_builder->vol_index()};
875 v_builder->add_volume_placement(identity);
876
877 detail::add_cylinder_portals(
878 v_builder, cfg, -h_z, h_z, vol_bounds.outer_radius, cfg.outer_radius(),
879 end_of_world, vol_idx - 2u, link_east, link_west);
880 volume_sizes.push_back(
881 {vol_idx, {vol_bounds.outer_radius, cfg.outer_radius()}});
882
883 v_builder->set_name("gap_" + std::to_string(vol_idx));
884
885 return volume_sizes;
886}
887
896template <typename detector_builder_t>
897inline auto add_endcap_detector(
898 detector_builder_t &det_builder,
899 typename detector_builder_t::detector_type::geometry_context &gctx,
900 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
901 &cfg,
902 dindex beampipe_idx) {
903 using detector_t = typename detector_builder_t::detector_type;
904 using algebra_t = typename detector_t::algebra_type;
905 using scalar_t = dscalar<algebra_t>;
906 using point3_t = dpoint3D<algebra_t>;
907 using nav_link_t = typename detector_t::surface_type::navigation_link;
908
909 std::vector<std::pair<dindex, extent2D<scalar_t>>> volume_sizes{};
910
911 // Mask volume link for portals that exit the detector
912 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
913 // Disc portal link to the barrel section (via connector gap volume, which
914 // is the second volume initialized by this function, but built in a
915 // later step, when all necessaty information is available)
916 const dindex connector_link{det_builder.n_volumes() + 1u};
917
918 const auto sign{static_cast<scalar_t>(cfg.endcap_config().side())};
919
920 // Mask volume links
921 auto link_north{end_of_world};
922 auto link_south{beampipe_idx};
923
924 // Set the inner radius of the first gap to the radius of the beampipe vol.
925 const scalar_t inner_radius{cfg.beampipe_vol_radius()};
926 const scalar_t outer_radius{cfg.outer_radius()};
927
928 scalar_t gap_east_z{sign * cfg.barrel_config().half_length()};
929
930 typename cylinder_portal_generator<detector_t>::boundaries vol_bounds{};
931
932 // Alternate endcap module layers and gap volumes
933 bool is_gap = true;
934 for (dindex i = 0u; i < 2u * cfg.n_edc_layers(); ++i) {
935 // New volume
936 auto v_builder = det_builder.new_volume(volume_id::e_cylinder);
937 auto vm_builder = decorate_material(cfg, det_builder, v_builder);
938 const dindex vol_idx{vm_builder->vol_index()};
939
940 // Don't build the first gap here, as this will be done in a separate
941 // step once all portals of the barrel are constructed
942 if (i == 1u) {
943 const scalar_t gap_west_z{sign * std::min(std::abs(vol_bounds.upper_z),
944 std::abs(vol_bounds.lower_z))};
945
946 volume_sizes.push_back({vol_idx, {gap_east_z, gap_west_z}});
947
948 // Update the gap extent for the next gap
949 gap_east_z = sign * std::max(std::abs(vol_bounds.upper_z),
950 std::abs(vol_bounds.lower_z));
951
952 is_gap = !is_gap;
953 continue;
954 }
955
956 // Every second layer is a gap volume
957 is_gap = !is_gap;
958 if (is_gap) {
959 auto link_east{static_cast<dindex>(static_cast<int>(vol_idx) +
960 cfg.endcap_config().side() - 2)};
961 auto link_west{static_cast<dindex>(static_cast<int>(vol_idx) -
962 cfg.endcap_config().side() - 2)};
963
964 const scalar_t gap_west_z{sign * std::min(std::abs(vol_bounds.upper_z),
965 std::abs(vol_bounds.lower_z))};
966
967 const point3_t gap_center{static_cast<scalar_t>(0),
968 static_cast<scalar_t>(0),
969 0.5f * (gap_east_z + gap_west_z)};
970 vm_builder->add_volume_placement({gap_center});
971
972 detail::add_cylinder_portals(vm_builder, cfg, gap_west_z, gap_east_z,
973 inner_radius, outer_radius, link_north,
974 link_south, link_east, link_west);
975
976 volume_sizes.push_back({vol_idx, {gap_east_z, gap_west_z}});
977
978 // Set the inner gap radius for the next gap volume
979 gap_east_z = sign * std::max(std::abs(vol_bounds.upper_z),
980 std::abs(vol_bounds.lower_z));
981
982 vm_builder->set_name("gap_" + std::to_string(vol_idx));
983
984 } else {
985 const dindex j{i / 2u};
986
987 auto link_east{static_cast<dindex>(static_cast<int>(vol_idx) +
988 cfg.endcap_config().side() + 2)};
989 auto link_west{static_cast<dindex>(static_cast<int>(vol_idx) -
990 cfg.endcap_config().side() + 2)};
991
992 // The first endacp layer needs to link to the connector gap
993 // The last endcap layer needs to exit the detector
994 if (sign < 0) {
995 link_east = (i == 0u) ? connector_link : link_east;
996 link_west = (j == cfg.n_edc_layers() - 1u) ? end_of_world : link_west;
997 } else {
998 link_west = (i == 0u) ? connector_link : link_west;
999 link_east = (j == cfg.n_edc_layers() - 1u) ? end_of_world : link_east;
1000 }
1001
1002 // Position the volume at the respective endcap layer position
1003 const scalar_t center_z{sign * cfg.endcap_layer_positions().at(j)};
1004 const point3_t vol_center{static_cast<scalar_t>(0),
1005 static_cast<scalar_t>(0), center_z};
1006 vm_builder->add_volume_placement({vol_center});
1007
1008 // Configure the module factory for this layer
1009 auto &endcap_cfg = cfg.endcap_config();
1010
1011 endcap_cfg.center(center_z)
1012 .inner_radius(inner_radius)
1013 .outer_radius(outer_radius - cfg.envelope());
1014
1015 // Configure the portal factory
1016 cylinder_portal_config<scalar_t> portal_cfg{};
1017
1018 portal_cfg.envelope(cfg.envelope())
1019 .fixed_inner_radius(inner_radius)
1020 .fixed_outer_radius(outer_radius)
1021 // Link the volume portals to their neighbors
1022 .link_north(link_north)
1023 .link_south(link_south)
1024 .link_east(link_east)
1025 .link_west(link_west);
1026
1027 // Configure the material
1028 cfg.material_config().thickness(cfg.module_mat_thickness());
1029 // Use different material thickness for cylinder portals in endcaps
1030 const scalar_t t{cfg.cyl_material_map().thickness};
1031 cfg.cyl_material_map().thickness = 0.15f * unit<scalar_t>::mm;
1032
1033 // Add a layer of module surfaces (mat have material)
1034 auto module_mat_factory = decorate_material<detector_t>(
1035 cfg,
1036 std::make_unique<endcap_generator<detector_t, trapezoid2D>>(
1037 endcap_cfg),
1038 true);
1039
1040 // Add cylinder and disc portals (may have material, depending on
1041 // whether material maps are being used or not)
1042 auto portal_mat_factory = decorate_material<detector_t>(
1043 cfg,
1044 std::make_unique<cylinder_portal_generator<detector_t>>(portal_cfg));
1045
1046 // Reset cylinder material thickness
1047 cfg.cyl_material_map().thickness = t;
1048
1049 vm_builder->add_surfaces(module_mat_factory, gctx);
1050 vm_builder->add_surfaces(portal_mat_factory);
1051
1052 // Fill the volume extent into 'vol_bounds'
1053 get_volume_extent(cfg, portal_mat_factory, vol_bounds);
1054
1055 // Set the new current boundaries to construct the next gap
1056 volume_sizes.push_back(
1057 {vol_idx, {vol_bounds.lower_z, vol_bounds.upper_z}});
1058
1059 vm_builder->set_name("endcap_" + std::to_string(vol_idx));
1060
1061 if (cfg.use_grids()) {
1062 // Add a disc grid to every endcap module layer
1063 add_disc_grid(det_builder, cfg, vol_idx);
1064 }
1065 }
1066 }
1067 return volume_sizes;
1068}
1069
1079template <typename detector_builder_t, typename vol_extent_data_t>
1080inline void add_connector_portals(
1081 detector_builder_t &det_builder,
1082 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
1083 &cfg,
1084 const dindex beampipe_idx, const vol_extent_data_t &edc_vol_extents,
1085 const vol_extent_data_t &brl_vol_extents) {
1086 using detector_t = typename detector_builder_t::detector_type;
1087 using algebra_t = typename detector_t::algebra_type;
1088 using transform3_t = dtransform3D<algebra_t>;
1089 using scalar_t = dscalar<algebra_t>;
1090 using point3_t = dpoint3D<algebra_t>;
1091 using nav_link_t = typename detector_t::surface_type::navigation_link;
1092
1093 using factory_interface_t = surface_factory_interface<detector_t>;
1094 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
1095 using disc_factory_t = surface_factory<detector_t, ring2D>;
1096
1097 std::shared_ptr<factory_interface_t> pt_cyl_factory{nullptr};
1098 std::shared_ptr<factory_interface_t> pt_disc_factory{nullptr};
1099
1100 if (cfg.use_material_maps()) {
1101 pt_cyl_factory =
1102 decorate_material<detector_t>(cfg, std::make_unique<cyl_factory_t>());
1103 pt_disc_factory =
1104 decorate_material<detector_t>(cfg, std::make_unique<disc_factory_t>());
1105 } else {
1106 pt_cyl_factory = std::make_shared<cyl_factory_t>();
1107 pt_disc_factory = std::make_shared<disc_factory_t>();
1108 }
1109
1110 // Mask volume link for portals that exit the detector
1111 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
1112 const transform3_t identity{};
1113
1114 // Set the inner radius of the gap to the radius of the beampipe vol.
1115 const scalar_t inner_r{cfg.beampipe_vol_radius()};
1116 const scalar_t outer_r{cfg.outer_radius()};
1117
1118 // Get the data of the gap volume
1119 // The connector gap is always the second volume constructed in the endcaps
1120 const auto &connector_gap_data = edc_vol_extents[1];
1121 const dindex connector_gap_idx{connector_gap_data.first};
1122
1123 const scalar_t side{std::copysign(1.f, connector_gap_data.second.lower)};
1124 const scalar_t gap_east_z{connector_gap_data.second.lower};
1125 const scalar_t gap_west_z{connector_gap_data.second.upper};
1126 const scalar_t min_z{math::min(gap_east_z, gap_west_z)};
1127 const scalar_t max_z{math::max(gap_east_z, gap_west_z)};
1128 const point3_t gap_center{static_cast<scalar_t>(0), static_cast<scalar_t>(0),
1129 0.5f * (gap_east_z + gap_west_z)};
1130
1131 // Check that the volume under construction is really the connector gap
1132 assert(std::abs(gap_east_z) == cfg.barrel_config().half_length() ||
1133 std::abs(gap_west_z) == cfg.barrel_config().half_length());
1134
1135 volume_builder_interface<detector_t> *connector_builder =
1136 det_builder[connector_gap_idx];
1137
1138 connector_builder->set_name("connector_gap_" +
1139 std::to_string(connector_gap_idx));
1140 connector_builder->add_volume_placement({gap_center});
1141
1142 // Go over all volume extends and build the corresponding disc portal
1143 std::vector<std::vector<scalar_t>> boundaries{};
1144 std::vector<nav_link_t> vol_links{};
1145 for (const auto &e : brl_vol_extents) {
1146 const scalar_t min_r{math::min(e.second.lower, e.second.upper)};
1147 const scalar_t max_r{math::max(e.second.lower, e.second.upper)};
1148
1149 boundaries.push_back(std::vector<scalar_t>{min_r, max_r});
1150 vol_links.push_back(static_cast<nav_link_t>(e.first));
1151 }
1152 assert(boundaries.size() == vol_links.size());
1153
1154 // Barrel-facing disc portal
1155 pt_disc_factory->push_back(
1156 {surface_id::e_portal,
1157 transform3_t{point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0),
1158 (side < 0.f) ? max_z : min_z}},
1159 vol_links, boundaries});
1160
1161 // Outward-facing disc portal
1162 pt_disc_factory->push_back(
1163 {surface_id::e_portal,
1164 transform3_t{point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0),
1165 (side < 0.f) ? min_z : max_z}},
1166 static_cast<nav_link_t>(connector_gap_idx - 1u),
1167 std::vector<scalar_t>{inner_r, outer_r}});
1168
1169 // Inner cylinder portal
1170 pt_cyl_factory->push_back({surface_id::e_portal, identity,
1171 static_cast<nav_link_t>(beampipe_idx),
1172 std::vector<scalar_t>{inner_r, min_z, max_z}});
1173
1174 // Outer cylinder portal
1175 pt_cyl_factory->push_back({surface_id::e_portal, identity, end_of_world,
1176 std::vector<scalar_t>{outer_r, min_z, max_z}});
1177
1178 // Add all portals to the connector gap volume
1179 connector_builder->add_surfaces(pt_disc_factory);
1180 connector_builder->add_surfaces(pt_cyl_factory);
1181}
1182
1187template <concepts::detector detector_t>
1188inline void add_beampipe_portals(
1189 volume_builder_interface<detector_t> *beampipe_builder,
1190 toy_det_config<typename detector_t::scalar_type> &cfg) {
1191 using algebra_t = typename detector_t::algebra_type;
1192 using transform3_t = dtransform3D<algebra_t>;
1193 using scalar_t = dscalar<algebra_t>;
1194 using point3_t = dpoint3D<algebra_t>;
1195 using nav_link_t = typename detector_t::surface_type::navigation_link;
1196
1197 using factory_interface_t = surface_factory_interface<detector_t>;
1198 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
1199 using disc_factory_t = surface_factory<detector_t, ring2D>;
1200
1201 std::shared_ptr<factory_interface_t> pt_cyl_factory{nullptr};
1202
1203 if (cfg.use_material_maps()) {
1204 pt_cyl_factory =
1205 decorate_material<detector_t>(cfg, std::make_unique<cyl_factory_t>());
1206 } else {
1207 pt_cyl_factory = std::make_shared<cyl_factory_t>();
1208 }
1209
1210 // Mask volume link for portals that exit the detector
1211 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
1212
1213 const scalar_t h_z{cfg.barrel_config().half_length()};
1214 const scalar_t inner_r{0.f};
1215 const scalar_t outer_r{cfg.beampipe_vol_radius()};
1216
1217 // If there are no endcaps, add the beampipe disc portals at the boundaries
1218 // of the barrel section
1219 if (cfg.n_edc_layers() == 0u) {
1220 std::shared_ptr<factory_interface_t> pt_disc_factory{nullptr};
1221 if (cfg.use_material_maps()) {
1222 pt_disc_factory = decorate_material<detector_t>(
1223 cfg, std::make_unique<disc_factory_t>());
1224 } else {
1225 pt_disc_factory = std::make_shared<disc_factory_t>();
1226 }
1227
1228 // Lower dics portal
1229 pt_disc_factory->push_back(
1230 {surface_id::e_portal,
1231 transform3_t{point3_t{static_cast<scalar_t>(0),
1232 static_cast<scalar_t>(0), -h_z}},
1233 end_of_world, std::vector<scalar_t>{inner_r, outer_r}});
1234
1235 // Outward-facing disc portal
1236 pt_disc_factory->push_back(
1237 {surface_id::e_portal,
1238 transform3_t{
1239 point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0), h_z}},
1240 end_of_world, std::vector<scalar_t>{inner_r, outer_r}});
1241
1242 beampipe_builder->add_surfaces(pt_disc_factory);
1243 }
1244
1245 // Cylinder portal that leads into the barrel section
1246 dindex first_barrel_idx{
1247 cfg.n_brl_layers() == 0u ? end_of_world : 2u * cfg.n_edc_layers() + 2u};
1248 pt_cyl_factory->push_back(
1249 {surface_id::e_portal, transform3_t{},
1250 static_cast<nav_link_t>(first_barrel_idx),
1251 std::vector<scalar_t>{outer_r,
1252 -h_z + std::numeric_limits<scalar_t>::epsilon(),
1253 h_z - std::numeric_limits<scalar_t>::epsilon()}});
1254
1255 beampipe_builder->add_surfaces(pt_cyl_factory);
1256}
1257
1264template <concepts::detector detector_t, typename layer_size_cont_t>
1265inline void add_beampipe_portals(
1266 volume_builder_interface<detector_t> *beampipe_builder,
1267 toy_det_config<typename detector_t::scalar_type> &cfg,
1268 const layer_size_cont_t &edc_lay_sizes) {
1269 using algebra_t = typename detector_t::algebra_type;
1270 using transform3_t = dtransform3D<algebra_t>;
1271 using scalar_t = dscalar<algebra_t>;
1272 using point3_t = dpoint3D<algebra_t>;
1273 using nav_link_t = typename detector_t::surface_type::navigation_link;
1274
1275 using factory_interface_t = surface_factory_interface<detector_t>;
1276 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
1277 using disc_factory_t = surface_factory<detector_t, ring2D>;
1278
1279 // Mask volume link for portals that exit the detector
1280 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
1281 // For which endcap side should the portals be constructed?
1282 const scalar_t side{std::copysign(1.f, edc_lay_sizes.front().second.lower)};
1283
1284 std::shared_ptr<factory_interface_t> pt_cyl_factory{nullptr};
1285 std::shared_ptr<factory_interface_t> pt_disc_factory{nullptr};
1286
1287 if (cfg.use_material_maps()) {
1288 pt_cyl_factory =
1289 decorate_material<detector_t>(cfg, std::make_unique<cyl_factory_t>());
1290 pt_disc_factory =
1291 decorate_material<detector_t>(cfg, std::make_unique<disc_factory_t>());
1292 } else {
1293 pt_cyl_factory = std::make_shared<cyl_factory_t>();
1294 pt_disc_factory = std::make_shared<disc_factory_t>();
1295 }
1296
1297 const scalar_t inner_r{0.f};
1298 const scalar_t outer_r{cfg.beampipe_vol_radius()};
1299 scalar_t disc_pos_z{-side * std::numeric_limits<scalar_t>::max()};
1300
1301 // Go over all volume extends and build the corresponding cylinder portals
1302 std::vector<std::vector<scalar_t>> boundaries{};
1303 std::vector<nav_link_t> vol_links{};
1304 for (const auto &e : edc_lay_sizes) {
1305 const scalar_t min_z{math::min(e.second.lower, e.second.upper)};
1306 const scalar_t max_z{math::max(e.second.lower, e.second.upper)};
1307
1308 if (side < 0.f) {
1309 disc_pos_z = (disc_pos_z > min_z) ? min_z : disc_pos_z;
1310 } else {
1311 disc_pos_z = (disc_pos_z < max_z) ? max_z : disc_pos_z;
1312 }
1313
1314 boundaries.push_back(std::vector<scalar_t>{outer_r, min_z, max_z});
1315 vol_links.push_back(static_cast<nav_link_t>(e.first));
1316 }
1317 assert(boundaries.size() == vol_links.size());
1318
1319 pt_cyl_factory->push_back(
1320 {surface_id::e_portal, transform3_t{}, vol_links, boundaries});
1321
1322 // Outward-facing disc portal
1323 pt_disc_factory->push_back(
1324 {surface_id::e_portal,
1325 transform3_t{point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0),
1326 disc_pos_z}},
1327 end_of_world, std::vector<scalar_t>{inner_r, outer_r}});
1328
1329 // Add all portals to the beampipe volume
1330 beampipe_builder->add_surfaces(pt_disc_factory);
1331 beampipe_builder->add_surfaces(pt_cyl_factory);
1332}
1333
1334} // namespace detail
1335
1345template <concepts::algebra algebra_t>
1346inline auto build_toy_detector(vecmem::memory_resource &resource,
1348 using scalar_t = dscalar<algebra_t>;
1349 using transform3_t = dtransform3D<algebra_t>;
1350
1351 using builder_t = detector_builder<toy_metadata<algebra_t>, volume_builder>;
1352 using detector_t = typename builder_t::detector_type;
1353 using nav_link_t = typename detector_t::surface_type::navigation_link;
1354 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
1355 using vol_extent_container_t =
1356 std::vector<std::pair<dindex, detail::extent2D<scalar_t>>>;
1357
1358 // Check config
1359 if (cfg.n_edc_layers() > cfg.endcap_layer_positions().size()) {
1360 throw std::invalid_argument(
1361 "ERROR: Too many endcap layers requested (max " +
1362 std::to_string(cfg.endcap_layer_positions().size()) + ")!");
1363 }
1364 if (cfg.n_brl_layers() > cfg.barrel_layer_radii().size() - 1u) {
1365 throw std::invalid_argument(
1366 "ERROR: Too many barrel layers requested (max " +
1367 std::to_string(cfg.barrel_layer_radii().size() - 1u) + ")!");
1368 }
1369 if (cfg.n_edc_layers() > 0 && cfg.n_brl_layers() < 4) {
1370 throw std::invalid_argument(
1371 "ERROR: All four barrel layers need to be present in order to add "
1372 "endcap layers");
1373 }
1374
1375 // Toy detector builder
1376 builder_t det_builder;
1377 det_builder.set_name("toy_detector");
1378
1379 // Geometry context object
1380 typename detector_t::geometry_context gctx{};
1381
1382 // Add the volume that contains the beampipe
1383 cfg.material_config().thickness(cfg.beampipe_mat_thickness());
1384 auto beampipe_builder = detail::decorate_material(
1385 cfg, det_builder, det_builder.new_volume(volume_id::e_cylinder));
1386
1387 const dindex beampipe_idx{beampipe_builder->vol_index()};
1388 beampipe_builder->add_volume_placement(transform3_t{});
1389 beampipe_builder->set_name("beampipe_" + std::to_string(beampipe_idx));
1390
1391 // Add the beampipe as a passive material surface
1392 auto beampipe_factory = detail::decorate_material<detector_t>(
1393 cfg, std::make_unique<cyl_factory_t>(), true);
1394
1395 scalar_t max_z{cfg.n_edc_layers() == 0u ? cfg.barrel_config().half_length()
1396 : cfg.endcap_layer_positions().at(
1397 cfg.n_edc_layers() - 1u)};
1398 scalar_t min_z{-max_z};
1399
1400 beampipe_factory->push_back(
1401 {surface_id::e_passive, transform3_t{},
1402 static_cast<nav_link_t>(beampipe_idx),
1403 std::vector<scalar_t>{cfg.barrel_layer_radii().at(0), min_z, max_z}});
1404
1405 beampipe_builder->add_surfaces(beampipe_factory);
1406
1407 // Build the negative endcap
1408 vol_extent_container_t neg_edc_vol_extents;
1409 if (cfg.n_edc_layers() > 0u) {
1410 cfg.endcap_config().side(-1);
1411
1412 neg_edc_vol_extents =
1413 detail::add_endcap_detector(det_builder, gctx, cfg, beampipe_idx);
1414
1415 // Add the beampipe volume portals for the negative endcap section
1416 detail::add_beampipe_portals(beampipe_builder, cfg, neg_edc_vol_extents);
1417 }
1418 // Build the barrel section
1419 vol_extent_container_t brl_vol_extents;
1420 if (cfg.n_brl_layers() > 0u) {
1421 brl_vol_extents =
1422 detail::add_barrel_detector(det_builder, gctx, cfg, beampipe_idx);
1423 }
1424 // Add the beampipe volume portals for the barrel section
1425 detail::add_beampipe_portals(beampipe_builder, cfg);
1426
1427 // Build the positive endcap
1428 vol_extent_container_t pos_edc_vol_extents;
1429 if (cfg.n_edc_layers() > 0u) {
1430 cfg.endcap_config().side(1);
1431
1432 pos_edc_vol_extents =
1433 detail::add_endcap_detector(det_builder, gctx, cfg, beampipe_idx);
1434
1435 // Add the beampipe volume portals for the positive endcap section
1436 detail::add_beampipe_portals(beampipe_builder, cfg, pos_edc_vol_extents);
1437
1438 // Add the connection between barrel and both endcaps
1439 // Negative endcap
1440 add_connector_portals(det_builder, cfg, beampipe_idx, neg_edc_vol_extents,
1441 brl_vol_extents);
1442 // Positive endcap
1443 add_connector_portals(det_builder, cfg, beampipe_idx, pos_edc_vol_extents,
1444 brl_vol_extents);
1445 }
1446
1447 // Build and return the detector and fill the name map
1448 typename detector_t::name_map name_map{};
1449 auto det = det_builder.build(resource, cfg.builder_options(), name_map);
1450
1451 if (cfg.do_check()) {
1452 const bool verbose_check{false};
1453 detray::detail::check_consistency(det, verbose_check, name_map);
1454 }
1455
1456 DETRAY_DEBUG_HOST("\n" << detray::utils::print_detector(det, name_map));
1457
1458 return std::make_pair(std::move(det), std::move(name_map));
1459}
1460
1461} // namespace detray
Interface for volume builders (and volume builder decorators).
Definition volume_builder_interface.hpp:66
virtual void set_name(std::string volume_name)=0
Sets the name volume_name for the volume.
virtual auto vol_index() const -> dindex=0
virtual void add_surfaces(std::shared_ptr< surface_factory_interface< detector_t > > sf_factory, typename detector_t::geometry_context ctx={})=0
Add surfaces to the volume.
virtual void add_volume_placement(const dtransform3D< algebra_type > &trf={})=0
Add the transform for the volume placement - copy.
Check if a type can be used as a detector.
Definition concepts.hpp:47
constexpr double e
Charge, native unit e (elementary charge).
Definition Units.hpp:165
constexpr double u
atomic mass unit u
Definition Units.hpp:155
material_id
Enumerate the different material types.
Definition definitions.hpp:59
Detray include(s).
Definition array_operators.hpp:18
get_scalar_t< A, T > dscalar
Scalar type for algebra A and value type T.
Definition algebra.hpp:64
auto build_toy_detector(vecmem::memory_resource &resource, toy_det_config< dscalar< algebra_t > > cfg={})
Builds a detray geometry that contains the innermost tml layers.
Definition build_toy_detector.hpp:1346
unsigned int dindex
Default index type.
Definition indexing.hpp:18
std::array< value_t, kDIM > darray
Fixed-size array container.
Definition containers.hpp:30
get_transform3D_t< A, T > dtransform3D
Three-dimensional transform type for algebra A and value type T.
Definition algebra.hpp:84
get_point3D_t< A, T > dpoint3D
Three-dimensional point type for algebra A and value type T.
Definition algebra.hpp:72
Predefined aluminium material with tabulated bulk properties.
Definition predefined_materials.hpp:108
configuration for the barrel module generator
Definition barrel_generator.hpp:34
Elements Declaration for ACTS Generic detector.
Definition predefined_materials.hpp:204
static constexpr scalar_t pi
π
Definition units.hpp:136
Helper type, used to define the r- or z-extent of detector volumes.
Definition build_toy_detector.hpp:441
scalar_t lower
Lower extent value.
Definition build_toy_detector.hpp:443
scalar_t upper
Upper extent value.
Definition build_toy_detector.hpp:445
configuration for the barrel module generator
Definition endcap_generator.hpp:35
Configuration for the homogeneous material generator.
Definition homogeneous_material_generator.hpp:33
How to configure the generation for a specific type of material map.
Definition material_map_generator.hpp:98
Material composition properties and density data used by interactions.
Definition material.hpp:36
Compile-time material mixture type.
Definition mixture.hpp:26
Predefined silicon material using TrackML bulk properties.
Definition predefined_materials.hpp:212
Configure the toy detector.
Definition build_toy_detector.hpp:52
constexpr const auto & disc_material_map() const
Return the disc material map.
Definition build_toy_detector.hpp:321
constexpr const auto & barrel_layer_binning() const
Return the barrel layer binning.
Definition build_toy_detector.hpp:375
endcap_generator_config< scalar_t > m_endcap_factory_cfg
Config for the module generation (endcaps).
Definition build_toy_detector.hpp:155
constexpr const auto & material_config() const
Return the material configuration.
Definition build_toy_detector.hpp:293
constexpr scalar_t material_map_min_thickness() const
Return the material map min thickness.
Definition build_toy_detector.hpp:337
const volume_builder_options & builder_options() const
Return the builder options.
Definition build_toy_detector.hpp:390
constexpr auto & material_config()
Return the material config.
Definition build_toy_detector.hpp:290
constexpr bool do_check() const
Return the do check.
Definition build_toy_detector.hpp:395
std::vector< scalar_t > m_endcap_layer_positions
Positions at which to place the endcap module layers on either side.
Definition build_toy_detector.hpp:147
volume_builder_options builder_options()
Return the builder options.
Definition build_toy_detector.hpp:259
constexpr const auto & outer_radius() const
Return the outer radius.
Definition build_toy_detector.hpp:279
constexpr toy_det_config & use_homogeneous_material(const bool b)
Set the use homogeneous material.
Definition build_toy_detector.hpp:195
constexpr const auto & beampipe_material_map() const
Return the beampipe material map configuration.
Definition build_toy_detector.hpp:310
constexpr toy_det_config & cyl_map_bins(const std::size_t n_phi, const std::size_t n_z)
Set the cyl map bins.
Definition build_toy_detector.hpp:210
constexpr bool use_homogeneous_material() const
Return the use homogeneous material.
Definition build_toy_detector.hpp:299
constexpr toy_det_config & do_check(const bool check)
Set the do check.
Definition build_toy_detector.hpp:263
std::vector< scalar_t > m_barrel_layer_radii
Radii at which to place the barrel module layers (including beampipe).
Definition build_toy_detector.hpp:139
unsigned int m_n_edc_layers
No. of endcap layers (on either side) the detector should be built with.
Definition build_toy_detector.hpp:113
bool m_use_grids
Build spatial grid acceleration structures (otherwise brute force search).
Definition build_toy_detector.hpp:123
scalar_t m_beampipe_volume_radius
Radius of the innermost volume that contains the beampipe.
Definition build_toy_detector.hpp:117
material_map_config< scalar_t >::map_config m_cyl_map_cfg
Configuration for the material map generator (cylinder).
Definition build_toy_detector.hpp:133
constexpr const auto & cyl_material_map() const
Return the cyl material map.
Definition build_toy_detector.hpp:315
constexpr toy_det_config & n_edc_layers(const unsigned int n)
Set the n edc layers.
Definition build_toy_detector.hpp:174
constexpr toy_det_config & beampipe_mat_thickness(const scalar_t t)
Set the beampipe mat thickness.
Definition build_toy_detector.hpp:236
constexpr toy_det_config & material_map_min_thickness(const scalar_t t)
Set the material map min thickness.
Definition build_toy_detector.hpp:227
constexpr const darray< std::size_t, 2 > & cyl_map_bins() const
Return the cyl map bins.
Definition build_toy_detector.hpp:327
volume_builder_options m_builder_opts
Volume builder configuration.
Definition build_toy_detector.hpp:157
constexpr bool use_grids() const
Return the use grids.
Definition build_toy_detector.hpp:296
hom_material_config< scalar_t > m_material_config
Configuration for the homogeneous material generator.
Definition build_toy_detector.hpp:121
constexpr scalar_t envelope() const
Return the envelope.
Definition build_toy_detector.hpp:282
constexpr material< scalar_t > mapped_material() const
Return the mapped material.
Definition build_toy_detector.hpp:359
constexpr unsigned int n_edc_layers() const
Return the n edc layers.
Definition build_toy_detector.hpp:276
barrel_generator_config< scalar_t > m_barrel_factory_cfg
Config for the module generation (barrel).
Definition build_toy_detector.hpp:153
unsigned int m_n_brl_layers
No. of barrel layers the detector should be built with.
Definition build_toy_detector.hpp:111
bool m_do_check
Run detector consistency check after reading.
Definition build_toy_detector.hpp:160
constexpr scalar_t beampipe_vol_radius() const
Return the beampipe vol radius.
Definition build_toy_detector.hpp:285
constexpr const darray< std::size_t, 2 > & disc_map_bins() const
Return the disc map bins.
Definition build_toy_detector.hpp:332
bool m_use_material_maps
Put material maps on portals or use homogeneous material on modules.
Definition build_toy_detector.hpp:127
constexpr toy_det_config & module_mat_thickness(const scalar_t t)
Set the module mat thickness.
Definition build_toy_detector.hpp:244
constexpr scalar_t module_mat_thickness() const
Return the module mat thickness.
Definition build_toy_detector.hpp:348
auto edc_mat_generator() const
Return the edc mat generator.
Definition build_toy_detector.hpp:356
constexpr toy_det_config & envelope(const scalar_t env)
Set the envelope.
Definition build_toy_detector.hpp:181
constexpr auto & beampipe_material_map()
Return the beampipe material map.
Definition build_toy_detector.hpp:307
scalar_t m_portal_envelope
Envelope around the modules used by the cylinder portal generator.
Definition build_toy_detector.hpp:119
constexpr toy_det_config & use_material_maps(const bool b)
Set the use material maps.
Definition build_toy_detector.hpp:202
constexpr toy_det_config & mapped_material(const material< scalar_t > &mat)
Set the mapped material.
Definition build_toy_detector.hpp:252
constexpr bool use_material_maps() const
Return the use material maps.
Definition build_toy_detector.hpp:304
scalar_t m_beampipe_mat_thickness
Thickness of the beampipe material.
Definition build_toy_detector.hpp:135
scalar_t m_outer_radius
Total outer radius of the pixel subdetector.
Definition build_toy_detector.hpp:115
constexpr endcap_generator_config< scalar_t > & endcap_config()
Return the endcap config.
Definition build_toy_detector.hpp:385
constexpr auto & cyl_material_map()
Return the mutable cylinder material map configuration.
Definition build_toy_detector.hpp:318
std::vector< std::pair< unsigned int, unsigned int > > m_barrel_binning
Number of modules in phi and z for the barrel.
Definition build_toy_detector.hpp:144
constexpr unsigned int n_brl_layers() const
Getters.
Definition build_toy_detector.hpp:273
constexpr const auto & endcap_layer_positions() const
Return the endcap layer positions.
Definition build_toy_detector.hpp:370
constexpr scalar_t beampipe_mat_thickness() const
Return the beampipe mat thickness.
Definition build_toy_detector.hpp:343
constexpr toy_det_config & use_grids(const bool b)
Set the use grids.
Definition build_toy_detector.hpp:188
constexpr const auto & barrel_layer_radii() const
Return the barrel layer radii.
Definition build_toy_detector.hpp:365
constexpr barrel_generator_config< scalar_t > & barrel_config()
Return the barrel config.
Definition build_toy_detector.hpp:380
toy_det_config()
Default toy detector configuration.
Definition build_toy_detector.hpp:54
material_map_config< scalar_t >::map_config m_beampipe_map_cfg
Configuration for the material map generator (beampipe).
Definition build_toy_detector.hpp:129
material_map_config< scalar_t >::map_config m_disc_map_cfg
Configuration for the material map generator (disc).
Definition build_toy_detector.hpp:131
constexpr toy_det_config & n_brl_layers(const unsigned int n)
Setters.
Definition build_toy_detector.hpp:167
constexpr toy_det_config & disc_map_bins(const std::size_t n_r, const std::size_t n_phi)
Set the disc map bins.
Definition build_toy_detector.hpp:219
bool m_use_homogeneous_material
Put homogeneneous material on the sensitive surfaces.
Definition build_toy_detector.hpp:125
friend std::ostream & operator<<(std::ostream &out, const toy_det_config &cfg)
Print the toy detector configuration.
Definition build_toy_detector.hpp:399
constexpr auto & disc_material_map()
Return the mutable disc material map configuration.
Definition build_toy_detector.hpp:324
auto barrel_mat_generator() const
Return the barrel mat generator.
Definition build_toy_detector.hpp:353
scalar_t m_module_mat_thickness
Thickness of the material slabs in the homogeneous material description.
Definition build_toy_detector.hpp:137
static constexpr scalar_t mm
Native length unit, the millimetre.
Definition units.hpp:27
Elements Declaration.
Definition predefined_materials.hpp:34
Options for the volume builder classes.
Definition volume_builder_interface.hpp:29