File indexing completed on 2026-05-27 07:24:12
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012 #include "detray/builders/cylinder_portal_generator.hpp"
0013 #include "detray/builders/detector_builder.hpp"
0014 #include "detray/builders/grid_builder.hpp"
0015 #include "detray/builders/homogeneous_material_builder.hpp"
0016 #include "detray/builders/homogeneous_material_generator.hpp"
0017 #include "detray/builders/material_map_builder.hpp"
0018 #include "detray/builders/material_map_generator.hpp"
0019 #include "detray/builders/surface_factory.hpp"
0020 #include "detray/builders/volume_builder.hpp"
0021 #include "detray/core/detector.hpp"
0022 #include "detray/definitions/algebra.hpp"
0023 #include "detray/definitions/indexing.hpp"
0024 #include "detray/definitions/units.hpp"
0025 #include "detray/detectors/toy_metadata.hpp"
0026 #include "detray/geometry/tracking_volume.hpp"
0027 #include "detray/material/mixture.hpp"
0028 #include "detray/material/predefined_materials.hpp"
0029 #include "detray/utils/consistency_checker.hpp"
0030 #include "detray/utils/print_detector.hpp"
0031 #include "detray/utils/ranges.hpp"
0032
0033
0034 #include "detray/test/common/factories/barrel_generator.hpp"
0035 #include "detray/test/common/factories/endcap_generator.hpp"
0036
0037
0038 #include <vecmem/memory/memory_resource.hpp>
0039
0040
0041 #include <limits>
0042 #include <stdexcept>
0043 #include <string>
0044 #include <type_traits>
0045 #include <utility>
0046
0047 namespace detray {
0048
0049
0050 template <concepts::scalar scalar_t>
0051 struct toy_det_config {
0052
0053 toy_det_config() {
0054
0055 m_barrel_factory_cfg.half_length(500.f * unit<scalar_t>::mm)
0056 .module_bounds({8.4f * unit<scalar_t>::mm, 36.f * unit<scalar_t>::mm})
0057 .tilt_phi(0.14f )
0058 .radial_stagger(0.5f * unit<scalar_t>::mm )
0059 .z_overlap(2.f * unit<scalar_t>::mm );
0060
0061
0062 m_endcap_factory_cfg.inner_radius(m_beampipe_volume_radius)
0063 .outer_radius(m_outer_radius)
0064 .module_bounds({{3.f * unit<scalar_t>::mm, 9.5f * unit<scalar_t>::mm,
0065 39.f * unit<scalar_t>::mm},
0066 {6.f * unit<scalar_t>::mm, 10.f * unit<scalar_t>::mm,
0067 39.f * unit<scalar_t>::mm}})
0068 .ring_stagger(2.f * unit<scalar_t>::mm)
0069 .phi_stagger({4.f * unit<scalar_t>::mm, 4.f * unit<scalar_t>::mm})
0070 .phi_sub_stagger({0.5f * unit<scalar_t>::mm, 0.5f * unit<scalar_t>::mm})
0071 .module_tilt({0.f, 0.f})
0072 .binning({40u, 68u});
0073
0074
0075 m_material_config.sensitive_material(silicon_tml<scalar_t>())
0076 .passive_material(beryllium_tml<scalar_t>())
0077 .portal_material(vacuum<scalar_t>())
0078 .thickness(1.5f * unit<scalar_t>::mm);
0079
0080
0081 m_beampipe_map_cfg.n_bins = {20u, 20u};
0082 m_beampipe_map_cfg.axis_index = 1u;
0083 m_beampipe_map_cfg.mapped_material = beryllium_tml<scalar_t>();
0084 m_beampipe_map_cfg.thickness = 0.8f * unit<scalar_t>::mm;
0085
0086 m_beampipe_map_cfg.scalor = 0.f;
0087 m_beampipe_map_cfg.mat_generator =
0088 detray::detail::generate_cyl_mat<scalar_t>;
0089
0090 m_disc_map_cfg.n_bins = {5u, 20u};
0091 m_disc_map_cfg.axis_index = 0u;
0092 m_disc_map_cfg.mapped_material =
0093 mixture<scalar_t, silicon_tml<scalar_t, std::ratio<9, 10>>,
0094 aluminium<scalar_t, std::ratio<1, 10>>>{};
0095 m_disc_map_cfg.thickness = 1.f * unit<scalar_t>::mm;
0096 m_disc_map_cfg.scalor = 1e-4f;
0097 m_disc_map_cfg.mat_generator = detray::detail::generate_disc_mat<scalar_t>;
0098
0099 m_cyl_map_cfg.n_bins = {20u, 20u};
0100 m_cyl_map_cfg.axis_index = 1u;
0101 m_cyl_map_cfg.mapped_material =
0102 mixture<scalar_t, silicon_tml<scalar_t, std::ratio<9, 10>>,
0103 aluminium<scalar_t, std::ratio<1, 10>>>{};
0104 m_cyl_map_cfg.thickness = 5.f * unit<scalar_t>::mm;
0105 m_cyl_map_cfg.scalor = 1e-8f;
0106 m_cyl_map_cfg.mat_generator = detray::detail::generate_cyl_mat<scalar_t>;
0107 }
0108
0109
0110 unsigned int m_n_brl_layers{4u};
0111
0112 unsigned int m_n_edc_layers{3u};
0113
0114 scalar_t m_outer_radius{180.f * unit<scalar_t>::mm};
0115
0116 scalar_t m_beampipe_volume_radius{25.f * unit<scalar_t>::mm};
0117
0118 scalar_t m_portal_envelope{2.f * unit<scalar_t>::mm};
0119
0120 hom_material_config<scalar_t> m_material_config{};
0121
0122 bool m_use_material_maps{false};
0123
0124 typename material_map_config<scalar_t>::map_config m_beampipe_map_cfg{};
0125
0126 typename material_map_config<scalar_t>::map_config m_disc_map_cfg{};
0127
0128 typename material_map_config<scalar_t>::map_config m_cyl_map_cfg{};
0129
0130 scalar_t m_beampipe_mat_thickness{0.8f * unit<scalar_t>::mm};
0131
0132 scalar_t m_module_mat_thickness{1.5f * unit<scalar_t>::mm};
0133
0134 std::vector<scalar_t> m_barrel_layer_radii = {
0135 19.f * unit<scalar_t>::mm, 32.f * unit<scalar_t>::mm,
0136 72.f * unit<scalar_t>::mm, 116.f * unit<scalar_t>::mm,
0137 172.f * unit<scalar_t>::mm};
0138
0139 std::vector<std::pair<unsigned int, unsigned int>> m_barrel_binning = {
0140 {0u, 0u}, {16u, 14u}, {32u, 14u}, {52u, 14u}, {78u, 14u}};
0141
0142 std::vector<scalar_t> m_endcap_layer_positions = {
0143 600.f * unit<scalar_t>::mm, 700.f * unit<scalar_t>::mm,
0144 820.f * unit<scalar_t>::mm, 960.f * unit<scalar_t>::mm,
0145 1100.f * unit<scalar_t>::mm, 1300.f * unit<scalar_t>::mm,
0146 1500.f * unit<scalar_t>::mm};
0147
0148 barrel_generator_config<scalar_t> m_barrel_factory_cfg{};
0149
0150 endcap_generator_config<scalar_t> m_endcap_factory_cfg{};
0151
0152 bool m_do_check{true};
0153
0154
0155
0156 constexpr toy_det_config &n_brl_layers(const unsigned int n) {
0157 m_n_brl_layers = n;
0158 return *this;
0159 }
0160 constexpr toy_det_config &n_edc_layers(const unsigned int n) {
0161 m_n_edc_layers = n;
0162 return *this;
0163 }
0164 constexpr toy_det_config &envelope(const scalar_t env) {
0165 m_portal_envelope = env;
0166 return *this;
0167 }
0168 constexpr toy_det_config &use_material_maps(const bool b) {
0169 m_use_material_maps = b;
0170 return *this;
0171 }
0172 constexpr toy_det_config &cyl_map_bins(const std::size_t n_phi,
0173 const std::size_t n_z) {
0174 m_cyl_map_cfg.n_bins = {n_phi, n_z};
0175 return *this;
0176 }
0177 constexpr toy_det_config &disc_map_bins(const std::size_t n_r,
0178 const std::size_t n_phi) {
0179 m_disc_map_cfg.n_bins = {n_r, n_phi};
0180 return *this;
0181 }
0182 constexpr toy_det_config &material_map_min_thickness(const scalar_t t) {
0183 assert(t > 0.f);
0184 m_cyl_map_cfg.thickness = t;
0185 m_disc_map_cfg.thickness = t;
0186 return *this;
0187 }
0188 constexpr toy_det_config &beampipe_mat_thickness(const scalar_t t) {
0189 assert(t > 0.f);
0190 m_beampipe_mat_thickness = t;
0191 return *this;
0192 }
0193 constexpr toy_det_config &module_mat_thickness(const scalar_t t) {
0194 assert(t > 0.f);
0195 m_module_mat_thickness = t;
0196 return *this;
0197 }
0198 constexpr toy_det_config &mapped_material(const material<scalar_t> &mat) {
0199 m_cyl_map_cfg.mapped_material = mat;
0200 m_disc_map_cfg.mapped_material = mat;
0201 return *this;
0202 }
0203 constexpr toy_det_config &do_check(const bool check) {
0204 m_do_check = check;
0205 return *this;
0206 }
0207
0208
0209
0210
0211 constexpr unsigned int n_brl_layers() const { return m_n_brl_layers; }
0212 constexpr unsigned int n_edc_layers() const { return m_n_edc_layers; }
0213 constexpr const auto &outer_radius() const { return m_outer_radius; }
0214 constexpr scalar_t envelope() const { return m_portal_envelope; }
0215 constexpr scalar_t beampipe_vol_radius() const {
0216 return m_beampipe_volume_radius;
0217 }
0218 constexpr auto &material_config() { return m_material_config; }
0219 constexpr const auto &material_config() const { return m_material_config; }
0220 constexpr bool use_material_maps() const { return m_use_material_maps; }
0221 constexpr auto &beampipe_material_map() { return m_beampipe_map_cfg; }
0222 constexpr const auto &beampipe_material_map() const {
0223 return m_beampipe_map_cfg;
0224 }
0225 constexpr const auto &cyl_material_map() const { return m_cyl_map_cfg; }
0226 constexpr auto &cyl_material_map() { return m_cyl_map_cfg; }
0227 constexpr const auto &disc_material_map() const { return m_disc_map_cfg; }
0228 constexpr auto &disc_material_map() { return m_disc_map_cfg; }
0229 constexpr const darray<std::size_t, 2> &cyl_map_bins() const {
0230 return m_cyl_map_cfg.n_bins;
0231 }
0232 constexpr const darray<std::size_t, 2> &disc_map_bins() const {
0233 return m_disc_map_cfg.n_bins;
0234 }
0235 constexpr scalar_t material_map_min_thickness() const {
0236 assert(m_cyl_map_cfg.thickness == m_disc_map_cfg.thickness);
0237 return m_cyl_map_cfg.thickness;
0238 }
0239 constexpr scalar_t beampipe_mat_thickness() const {
0240 return m_beampipe_mat_thickness;
0241 }
0242 constexpr scalar_t module_mat_thickness() const {
0243 return m_module_mat_thickness;
0244 }
0245 auto barrel_mat_generator() const { return m_cyl_map_cfg.mat_generator; }
0246 auto edc_mat_generator() const { return m_disc_map_cfg.mat_generator; }
0247 constexpr material<scalar_t> mapped_material() const {
0248 assert(m_cyl_map_cfg.mapped_material == m_disc_map_cfg.mapped_material);
0249 return m_cyl_map_cfg.mapped_material;
0250 }
0251 constexpr const auto &barrel_layer_radii() const {
0252 return m_barrel_layer_radii;
0253 }
0254 constexpr const auto &endcap_layer_positions() const {
0255 return m_endcap_layer_positions;
0256 }
0257 constexpr const auto &barrel_layer_binning() const {
0258 return m_barrel_binning;
0259 }
0260 constexpr barrel_generator_config<scalar_t> &barrel_config() {
0261 return m_barrel_factory_cfg;
0262 }
0263 constexpr endcap_generator_config<scalar_t> &endcap_config() {
0264 return m_endcap_factory_cfg;
0265 }
0266 constexpr bool do_check() const { return m_do_check; }
0267
0268
0269
0270 friend std::ostream &operator<<(std::ostream &out,
0271 const toy_det_config &cfg) {
0272 out << "\nToy Detector\n"
0273 << "----------------------------\n"
0274 << " No. barrel layers : " << cfg.n_brl_layers() << "\n"
0275 << " No. endcap layers : " << cfg.n_edc_layers() << "\n"
0276 << " Portal envelope : " << cfg.envelope() << " [mm]\n";
0277
0278 if (cfg.use_material_maps()) {
0279 const auto &cyl_map_bins = cfg.cyl_map_bins();
0280 const auto &disc_map_bins = cfg.disc_map_bins();
0281
0282 out << " Material maps \n"
0283 << " -> cyl. map bins : (phi: " << cyl_map_bins[0]
0284 << ", z: " << cyl_map_bins[1] << ")\n"
0285 << " -> disc map bins : (r: " << disc_map_bins[0]
0286 << ", phi: " << disc_map_bins[1] << ")\n"
0287 << " -> cyl. min. thickness: "
0288 << cfg.cyl_material_map().thickness / detray::unit<scalar_t>::mm
0289 << " [mm]\n"
0290 << " -> disc min. thickness: "
0291 << cfg.disc_material_map().thickness / detray::unit<scalar_t>::mm
0292 << " [mm]\n"
0293 << " -> Material : " << cfg.mapped_material() << "\n";
0294 } else {
0295 out << " Homogeneous material \n"
0296 << " -> Thickness : "
0297 << cfg.module_mat_thickness() / detray::unit<scalar_t>::mm
0298 << " [mm]\n"
0299 << " -> Material : " << silicon_tml<scalar_t>() << "\n";
0300 }
0301
0302 return out;
0303 }
0304 };
0305
0306 namespace detail {
0307
0308
0309 template <concepts::scalar scalar_t>
0310 struct extent2D {
0311 scalar_t lower;
0312 scalar_t upper;
0313 };
0314
0315
0316
0317
0318
0319
0320
0321
0322 template <typename detector_builder_t, typename detector_t, typename config_t>
0323 volume_builder_interface<detector_t> *decorate_material(
0324 config_t &cfg, detector_builder_t &det_builder,
0325 volume_builder_interface<detector_t> *v_builder) {
0326 static_assert(
0327 std::is_same_v<detector_t, typename detector_builder_t::detector_type>,
0328 "Detector builder and volume builder/surface factory have different "
0329 "detector type");
0330
0331 volume_builder_interface<detector_t> *vm_builder{v_builder};
0332
0333
0334 if (cfg.use_material_maps()) {
0335
0336 vm_builder =
0337 det_builder.template decorate<material_map_builder<detector_t>>(
0338 v_builder);
0339 } else {
0340
0341 vm_builder =
0342 det_builder.template decorate<homogeneous_material_builder<detector_t>>(
0343 v_builder);
0344 }
0345
0346 if (!vm_builder) {
0347 throw std::runtime_error("Material decoration failed");
0348 }
0349
0350 return vm_builder;
0351 }
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361 template <typename detector_t>
0362 std::shared_ptr<surface_factory_interface<detector_t>> decorate_material(
0363 toy_det_config<typename detector_t::scalar_type> &cfg,
0364 std::unique_ptr<surface_factory_interface<detector_t>> sf_factory,
0365 bool is_module_factory = false) {
0366 using scalar_t = dscalar<typename detector_t::algebra_type>;
0367 using mask_id = typename detector_t::masks::id;
0368 using material_id = typename detector_t::material::id;
0369
0370
0371 if (cfg.use_material_maps()) {
0372
0373 material_map_config<scalar_t> material_map_config{};
0374
0375
0376 cfg.beampipe_material_map().map_id =
0377 static_cast<dindex>(material_id::e_concentric_cylinder2D_map);
0378 material_map_config.set_map_config(mask_id::e_concentric_cylinder2D,
0379 surface_id::e_passive,
0380 cfg.beampipe_material_map());
0381
0382
0383 cfg.disc_material_map().map_id =
0384 static_cast<dindex>(material_id::e_ring2D_map);
0385 material_map_config.set_map_config(mask_id::e_ring2D, surface_id::e_portal,
0386 cfg.disc_material_map());
0387
0388
0389 cfg.cyl_material_map().map_id =
0390 static_cast<dindex>(material_id::e_concentric_cylinder2D_map);
0391 material_map_config.set_map_config(mask_id::e_concentric_cylinder2D,
0392 surface_id::e_portal,
0393 cfg.cyl_material_map());
0394
0395 auto mat_generator = std::make_shared<material_map_generator<detector_t>>(
0396 std::move(sf_factory), material_map_config);
0397
0398 return mat_generator;
0399
0400 } else if (!cfg.use_material_maps() && is_module_factory) {
0401
0402 auto mat_generator =
0403 std::make_shared<homogeneous_material_generator<detector_t>>(
0404 std::move(sf_factory), cfg.material_config());
0405
0406 return mat_generator;
0407 } else {
0408
0409 return sf_factory;
0410 }
0411 }
0412
0413
0414
0415
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426 template <typename detector_t>
0427 void add_cylinder_portals(volume_builder_interface<detector_t> *v_builder,
0428 toy_det_config<typename detector_t::scalar_type> &cfg,
0429 const typename detector_t::scalar_type lower_z,
0430 const typename detector_t::scalar_type upper_z,
0431 const typename detector_t::scalar_type inner_r,
0432 const typename detector_t::scalar_type outer_r,
0433 const dindex link_north, const dindex link_south,
0434 const dindex link_east, const dindex link_west) {
0435 using algebra_t = typename detector_t::algebra_type;
0436 using transform3_t = dtransform3D<algebra_t>;
0437 using scalar_t = dscalar<algebra_t>;
0438 using point3_t = dpoint3D<algebra_t>;
0439 using nav_link_t = typename detector_t::surface_type::navigation_link;
0440
0441 const transform3_t identity{};
0442 const dindex vol_idx{v_builder->vol_index()};
0443
0444 scalar_t min_r{math::min(inner_r, outer_r)};
0445 scalar_t max_r{math::max(inner_r, outer_r)};
0446 scalar_t min_z{math::min(lower_z, upper_z)};
0447 scalar_t max_z{math::max(lower_z, upper_z)};
0448
0449 using factory_interface_t = surface_factory_interface<detector_t>;
0450 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
0451 using disc_factory_t = surface_factory<detector_t, ring2D>;
0452
0453 std::shared_ptr<factory_interface_t> pt_cyl_factory{nullptr};
0454 std::shared_ptr<factory_interface_t> pt_disc_factory{nullptr};
0455
0456 if (cfg.use_material_maps()) {
0457 pt_cyl_factory =
0458 decorate_material<detector_t>(cfg, std::make_unique<cyl_factory_t>());
0459 pt_disc_factory =
0460 decorate_material<detector_t>(cfg, std::make_unique<disc_factory_t>());
0461 } else {
0462 pt_cyl_factory = std::make_shared<cyl_factory_t>();
0463 pt_disc_factory = std::make_shared<disc_factory_t>();
0464 }
0465
0466
0467 pt_cyl_factory->push_back({surface_id::e_portal, identity,
0468 static_cast<nav_link_t>(link_south),
0469 std::vector<scalar_t>{min_r, min_z, max_z}});
0470
0471 pt_cyl_factory->push_back({surface_id::e_portal, identity,
0472 static_cast<nav_link_t>(link_north),
0473 std::vector<scalar_t>{max_r, min_z, max_z}});
0474
0475
0476 pt_disc_factory->push_back(
0477 {surface_id::e_portal,
0478 transform3_t{
0479 point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0), min_z}},
0480 static_cast<nav_link_t>(link_west),
0481 std::vector<scalar_t>{min_r, max_r}});
0482
0483 pt_disc_factory->push_back(
0484 {surface_id::e_portal,
0485 transform3_t{
0486 point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0), max_z}},
0487 static_cast<nav_link_t>(link_east),
0488 std::vector<scalar_t>{min_r, max_r}});
0489
0490 v_builder->add_surfaces(pt_cyl_factory);
0491 v_builder->add_surfaces(pt_disc_factory);
0492
0493 v_builder->set_name("gap_" + std::to_string(vol_idx));
0494 }
0495
0496
0497
0498
0499
0500
0501 template <typename detector_builder_t>
0502 inline void add_cylinder_grid(
0503 detector_builder_t &det_builder,
0504 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
0505 &cfg,
0506 const dindex vol_index) {
0507 using detector_t = typename detector_builder_t::detector_type;
0508 using scalar_t = dscalar<typename detector_t::algebra_type>;
0509
0510 constexpr auto grid_id =
0511 detector_t::accel::id::e_surface_concentric_cylinder2D_grid;
0512 using cyl_grid_t = types::get<typename detector_t::accel, grid_id>;
0513
0514 using grid_builder_t =
0515 grid_builder<detector_t, cyl_grid_t, detray::fill_by_pos>;
0516
0517 const auto &barrel_cfg{cfg.barrel_config()};
0518 const scalar_t h_z{barrel_cfg.half_length()};
0519
0520 auto vgr_builder = det_builder.template decorate<grid_builder_t>(vol_index);
0521
0522 if (!vgr_builder) {
0523 throw std::runtime_error("Grid decoration failed");
0524 }
0525
0526 vgr_builder->set_type(detector_t::geo_obj_ids::e_sensitive);
0527 vgr_builder->init_grid(
0528 {-constant<scalar_t>::pi, constant<scalar_t>::pi, -h_z, h_z},
0529 {barrel_cfg.binning().first, barrel_cfg.binning().second});
0530 }
0531
0532
0533
0534
0535
0536
0537 template <typename detector_builder_t>
0538 inline void add_disc_grid(
0539 detector_builder_t &det_builder,
0540 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
0541 &cfg,
0542 const dindex vol_index) {
0543 using detector_t = typename detector_builder_t::detector_type;
0544 using scalar_t = dscalar<typename detector_t::algebra_type>;
0545
0546 constexpr auto grid_id = detector_t::accel::id::e_surface_ring2D_grid;
0547 using disc_grid_t = types::get<typename detector_t::accel, grid_id>;
0548
0549 using grid_builder_t =
0550 grid_builder<detector_t, disc_grid_t, detray::fill_by_pos>;
0551
0552 const auto &endcap_cfg{cfg.endcap_config()};
0553 const scalar_t inner_r{cfg.beampipe_vol_radius()};
0554 const scalar_t outer_r{cfg.outer_radius()};
0555
0556 auto vgr_builder = det_builder.template decorate<grid_builder_t>(vol_index);
0557
0558 if (!vgr_builder) {
0559 throw std::runtime_error("Grid decoration failed");
0560 }
0561
0562 vgr_builder->set_type(detector_t::geo_obj_ids::e_sensitive);
0563 vgr_builder->init_grid(
0564 {inner_r, outer_r, -constant<scalar_t>::pi, constant<scalar_t>::pi},
0565 {endcap_cfg.binning().size(), endcap_cfg.binning().back()});
0566 }
0567
0568
0569
0570
0571
0572
0573
0574 template <typename detector_t>
0575 inline void get_volume_extent(
0576 toy_det_config<typename detector_t::scalar_type> &cfg,
0577 const std::shared_ptr<surface_factory_interface<detector_t>> &sf_factory,
0578 typename cylinder_portal_generator<detector_t>::boundaries &vol_bounds) {
0579
0580 const cylinder_portal_generator<detector_t> *cyl_factory{nullptr};
0581 if (cfg.use_material_maps()) {
0582
0583 auto decorator =
0584 std::dynamic_pointer_cast<const factory_decorator<detector_t>>(
0585 sf_factory);
0586 if (!decorator) {
0587 throw std::bad_cast();
0588 }
0589 cyl_factory = dynamic_cast<const cylinder_portal_generator<detector_t> *>(
0590 decorator->get_factory());
0591 } else {
0592
0593 cyl_factory = dynamic_cast<const cylinder_portal_generator<detector_t> *>(
0594 sf_factory.get());
0595 }
0596 if (!cyl_factory) {
0597 throw std::bad_cast();
0598 }
0599
0600
0601 vol_bounds = cyl_factory->volume_boundaries();
0602 }
0603
0604
0605
0606
0607
0608
0609
0610
0611
0612 template <typename detector_builder_t>
0613 inline auto add_barrel_detector(
0614 detector_builder_t &det_builder,
0615 typename detector_builder_t::detector_type::geometry_context &gctx,
0616 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
0617 &cfg,
0618 dindex beampipe_idx) {
0619 using detector_t = typename detector_builder_t::detector_type;
0620 using algebra_t = typename detector_t::algebra_type;
0621 using scalar_t = dscalar<algebra_t>;
0622 using transform3_t = dtransform3D<algebra_t>;
0623 using nav_link_t = typename detector_t::surface_type::navigation_link;
0624
0625
0626 std::vector<std::pair<dindex, extent2D<scalar_t>>> volume_sizes{};
0627
0628
0629 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
0630 const transform3_t identity{};
0631
0632
0633
0634
0635 auto link_east{end_of_world};
0636 auto link_west{end_of_world};
0637
0638 if (cfg.n_edc_layers() > 0) {
0639 link_east = static_cast<nav_link_t>(det_builder.n_volumes() +
0640 2u * cfg.n_brl_layers() + 2u);
0641 link_west = static_cast<nav_link_t>(det_builder.n_volumes() -
0642 2u * cfg.n_edc_layers() + 1u);
0643 }
0644
0645 const scalar_t h_z{cfg.barrel_config().half_length()};
0646
0647 scalar_t gap_inner_r{cfg.beampipe_vol_radius()};
0648
0649 typename cylinder_portal_generator<detector_t>::boundaries vol_bounds{};
0650
0651
0652 bool is_gap = true;
0653 for (unsigned int i = 0u; i < 2u * cfg.n_brl_layers(); ++i) {
0654
0655 auto v_builder = det_builder.new_volume(volume_id::e_cylinder);
0656 auto vm_builder = decorate_material(cfg, det_builder, v_builder);
0657 const dindex vol_idx{vm_builder->vol_index()};
0658
0659
0660 vm_builder->add_volume_placement(identity);
0661
0662
0663 is_gap = !is_gap;
0664 if (is_gap) {
0665 auto link_north{vol_idx - 1};
0666 auto link_south{vol_idx - 3};
0667
0668
0669 link_south = (i == 1u) ? beampipe_idx : link_south;
0670
0671 detail::add_cylinder_portals(vm_builder, cfg, -h_z, h_z, gap_inner_r,
0672 vol_bounds.inner_radius, link_north,
0673 link_south, link_east, link_west);
0674 volume_sizes.push_back({vol_idx, {gap_inner_r, vol_bounds.inner_radius}});
0675
0676
0677 gap_inner_r = vol_bounds.outer_radius;
0678
0679 vm_builder->set_name("gap_" + std::to_string(vol_idx));
0680
0681 } else {
0682
0683 auto link_north{std::min(
0684 vol_idx + 3, 2 * cfg.n_edc_layers() + 2 * cfg.n_brl_layers() + 1)};
0685 auto link_south{vol_idx + 1};
0686
0687
0688 auto &barrel_cfg = cfg.barrel_config();
0689
0690 const unsigned int j{(i + 2u) / 2u};
0691 barrel_cfg.binning(cfg.barrel_layer_binning().at(j))
0692 .radius(cfg.barrel_layer_radii().at(j));
0693
0694
0695 cylinder_portal_config<scalar_t> portal_cfg{};
0696
0697 portal_cfg.envelope(cfg.envelope())
0698 .fixed_half_length(h_z)
0699
0700 .link_north(link_north)
0701 .link_south(link_south)
0702 .link_east(link_east)
0703 .link_west(link_west);
0704
0705
0706 cfg.material_config().thickness(cfg.module_mat_thickness());
0707
0708
0709 auto module_mat_factory = decorate_material<detector_t>(
0710 cfg,
0711 std::make_unique<barrel_generator<detector_t, rectangle2D>>(
0712 barrel_cfg),
0713 true);
0714
0715
0716
0717 auto portal_mat_factory = decorate_material<detector_t>(
0718 cfg,
0719 std::make_unique<cylinder_portal_generator<detector_t>>(portal_cfg));
0720
0721 vm_builder->add_surfaces(module_mat_factory, gctx);
0722 vm_builder->add_surfaces(portal_mat_factory);
0723
0724
0725 get_volume_extent(cfg, portal_mat_factory, vol_bounds);
0726
0727 volume_sizes.push_back(
0728 {vol_idx, {vol_bounds.inner_radius, vol_bounds.outer_radius}});
0729
0730 vm_builder->set_name("barrel_" + std::to_string(vol_idx));
0731
0732
0733 add_cylinder_grid(det_builder, cfg, vol_idx);
0734 }
0735 }
0736
0737
0738 auto v_builder = det_builder.new_volume(volume_id::e_cylinder);
0739 const dindex vol_idx{v_builder->vol_index()};
0740 v_builder->add_volume_placement(identity);
0741
0742 detail::add_cylinder_portals(
0743 v_builder, cfg, -h_z, h_z, vol_bounds.outer_radius, cfg.outer_radius(),
0744 end_of_world, vol_idx - 2u, link_east, link_west);
0745 volume_sizes.push_back(
0746 {vol_idx, {vol_bounds.outer_radius, cfg.outer_radius()}});
0747
0748 v_builder->set_name("gap_" + std::to_string(vol_idx));
0749
0750 return volume_sizes;
0751 }
0752
0753
0754
0755
0756
0757
0758
0759
0760
0761 template <typename detector_builder_t>
0762 inline auto add_endcap_detector(
0763 detector_builder_t &det_builder,
0764 typename detector_builder_t::detector_type::geometry_context &gctx,
0765 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
0766 &cfg,
0767 dindex beampipe_idx) {
0768 using detector_t = typename detector_builder_t::detector_type;
0769 using algebra_t = typename detector_t::algebra_type;
0770 using scalar_t = dscalar<algebra_t>;
0771 using point3_t = dpoint3D<algebra_t>;
0772 using nav_link_t = typename detector_t::surface_type::navigation_link;
0773
0774 std::vector<std::pair<dindex, extent2D<scalar_t>>> volume_sizes{};
0775
0776
0777 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
0778
0779
0780
0781 const dindex connector_link{det_builder.n_volumes() + 1u};
0782
0783 const auto sign{static_cast<scalar_t>(cfg.endcap_config().side())};
0784
0785
0786 auto link_north{end_of_world};
0787 auto link_south{beampipe_idx};
0788
0789
0790 const scalar_t inner_radius{cfg.beampipe_vol_radius()};
0791 const scalar_t outer_radius{cfg.outer_radius()};
0792
0793 scalar_t gap_east_z{sign * cfg.barrel_config().half_length()};
0794
0795 typename cylinder_portal_generator<detector_t>::boundaries vol_bounds{};
0796
0797
0798 bool is_gap = true;
0799 for (dindex i = 0u; i < 2u * cfg.n_edc_layers(); ++i) {
0800
0801 auto v_builder = det_builder.new_volume(volume_id::e_cylinder);
0802 auto vm_builder = decorate_material(cfg, det_builder, v_builder);
0803 const dindex vol_idx{vm_builder->vol_index()};
0804
0805
0806
0807 if (i == 1u) {
0808 const scalar_t gap_west_z{sign * std::min(std::abs(vol_bounds.upper_z),
0809 std::abs(vol_bounds.lower_z))};
0810
0811 volume_sizes.push_back({vol_idx, {gap_east_z, gap_west_z}});
0812
0813
0814 gap_east_z = sign * std::max(std::abs(vol_bounds.upper_z),
0815 std::abs(vol_bounds.lower_z));
0816
0817 is_gap = !is_gap;
0818 continue;
0819 }
0820
0821
0822 is_gap = !is_gap;
0823 if (is_gap) {
0824 auto link_east{static_cast<dindex>(static_cast<int>(vol_idx) +
0825 cfg.endcap_config().side() - 2)};
0826 auto link_west{static_cast<dindex>(static_cast<int>(vol_idx) -
0827 cfg.endcap_config().side() - 2)};
0828
0829 const scalar_t gap_west_z{sign * std::min(std::abs(vol_bounds.upper_z),
0830 std::abs(vol_bounds.lower_z))};
0831
0832 const point3_t gap_center{static_cast<scalar_t>(0),
0833 static_cast<scalar_t>(0),
0834 0.5f * (gap_east_z + gap_west_z)};
0835 vm_builder->add_volume_placement({gap_center});
0836
0837 detail::add_cylinder_portals(vm_builder, cfg, gap_west_z, gap_east_z,
0838 inner_radius, outer_radius, link_north,
0839 link_south, link_east, link_west);
0840
0841 volume_sizes.push_back({vol_idx, {gap_east_z, gap_west_z}});
0842
0843
0844 gap_east_z = sign * std::max(std::abs(vol_bounds.upper_z),
0845 std::abs(vol_bounds.lower_z));
0846
0847 vm_builder->set_name("gap_" + std::to_string(vol_idx));
0848
0849 } else {
0850 const dindex j{i / 2u};
0851
0852 auto link_east{static_cast<dindex>(static_cast<int>(vol_idx) +
0853 cfg.endcap_config().side() + 2)};
0854 auto link_west{static_cast<dindex>(static_cast<int>(vol_idx) -
0855 cfg.endcap_config().side() + 2)};
0856
0857
0858
0859 if (sign < 0) {
0860 link_east = (i == 0u) ? connector_link : link_east;
0861 link_west = (j == cfg.n_edc_layers() - 1u) ? end_of_world : link_west;
0862 } else {
0863 link_west = (i == 0u) ? connector_link : link_west;
0864 link_east = (j == cfg.n_edc_layers() - 1u) ? end_of_world : link_east;
0865 }
0866
0867
0868 const scalar_t center_z{sign * cfg.endcap_layer_positions().at(j)};
0869 const point3_t vol_center{static_cast<scalar_t>(0),
0870 static_cast<scalar_t>(0), center_z};
0871 vm_builder->add_volume_placement({vol_center});
0872
0873
0874 auto &endcap_cfg = cfg.endcap_config();
0875
0876 endcap_cfg.center(center_z)
0877 .inner_radius(inner_radius)
0878 .outer_radius(outer_radius - cfg.envelope());
0879
0880
0881 cylinder_portal_config<scalar_t> portal_cfg{};
0882
0883 portal_cfg.envelope(cfg.envelope())
0884 .fixed_inner_radius(inner_radius)
0885 .fixed_outer_radius(outer_radius)
0886
0887 .link_north(link_north)
0888 .link_south(link_south)
0889 .link_east(link_east)
0890 .link_west(link_west);
0891
0892
0893 cfg.material_config().thickness(cfg.module_mat_thickness());
0894
0895 const scalar_t t{cfg.cyl_material_map().thickness};
0896 cfg.cyl_material_map().thickness = 0.15f * unit<scalar_t>::mm;
0897
0898
0899 auto module_mat_factory = decorate_material<detector_t>(
0900 cfg,
0901 std::make_unique<endcap_generator<detector_t, trapezoid2D>>(
0902 endcap_cfg),
0903 true);
0904
0905
0906
0907 auto portal_mat_factory = decorate_material<detector_t>(
0908 cfg,
0909 std::make_unique<cylinder_portal_generator<detector_t>>(portal_cfg));
0910
0911
0912 cfg.cyl_material_map().thickness = t;
0913
0914 vm_builder->add_surfaces(module_mat_factory, gctx);
0915 vm_builder->add_surfaces(portal_mat_factory);
0916
0917
0918 get_volume_extent(cfg, portal_mat_factory, vol_bounds);
0919
0920
0921 volume_sizes.push_back(
0922 {vol_idx, {vol_bounds.lower_z, vol_bounds.upper_z}});
0923
0924 vm_builder->set_name("endcap_" + std::to_string(vol_idx));
0925
0926
0927 add_disc_grid(det_builder, cfg, vol_idx);
0928 }
0929 }
0930 return volume_sizes;
0931 }
0932
0933
0934
0935
0936
0937
0938
0939
0940 template <typename detector_builder_t, typename vol_extent_data_t>
0941 inline void add_connector_portals(
0942 detector_builder_t &det_builder,
0943 toy_det_config<typename detector_builder_t::detector_type::scalar_type>
0944 &cfg,
0945 const dindex beampipe_idx, const vol_extent_data_t &edc_vol_extents,
0946 const vol_extent_data_t &brl_vol_extents) {
0947 using detector_t = typename detector_builder_t::detector_type;
0948 using algebra_t = typename detector_t::algebra_type;
0949 using transform3_t = dtransform3D<algebra_t>;
0950 using scalar_t = dscalar<algebra_t>;
0951 using point3_t = dpoint3D<algebra_t>;
0952 using nav_link_t = typename detector_t::surface_type::navigation_link;
0953
0954 using factory_interface_t = surface_factory_interface<detector_t>;
0955 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
0956 using disc_factory_t = surface_factory<detector_t, ring2D>;
0957
0958 std::shared_ptr<factory_interface_t> pt_cyl_factory{nullptr};
0959 std::shared_ptr<factory_interface_t> pt_disc_factory{nullptr};
0960
0961 if (cfg.use_material_maps()) {
0962 pt_cyl_factory =
0963 decorate_material<detector_t>(cfg, std::make_unique<cyl_factory_t>());
0964 pt_disc_factory =
0965 decorate_material<detector_t>(cfg, std::make_unique<disc_factory_t>());
0966 } else {
0967 pt_cyl_factory = std::make_shared<cyl_factory_t>();
0968 pt_disc_factory = std::make_shared<disc_factory_t>();
0969 }
0970
0971
0972 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
0973 const transform3_t identity{};
0974
0975
0976 const scalar_t inner_r{cfg.beampipe_vol_radius()};
0977 const scalar_t outer_r{cfg.outer_radius()};
0978
0979
0980
0981 const auto &connector_gap_data = edc_vol_extents[1];
0982 const dindex connector_gap_idx{connector_gap_data.first};
0983
0984 const scalar_t side{std::copysign(1.f, connector_gap_data.second.lower)};
0985 const scalar_t gap_east_z{connector_gap_data.second.lower};
0986 const scalar_t gap_west_z{connector_gap_data.second.upper};
0987 const scalar_t min_z{math::min(gap_east_z, gap_west_z)};
0988 const scalar_t max_z{math::max(gap_east_z, gap_west_z)};
0989 const point3_t gap_center{static_cast<scalar_t>(0), static_cast<scalar_t>(0),
0990 0.5f * (gap_east_z + gap_west_z)};
0991
0992
0993 assert(std::abs(gap_east_z) == cfg.barrel_config().half_length() ||
0994 std::abs(gap_west_z) == cfg.barrel_config().half_length());
0995
0996 volume_builder_interface<detector_t> *connector_builder =
0997 det_builder[connector_gap_idx];
0998
0999 connector_builder->set_name("connector_gap_" +
1000 std::to_string(connector_gap_idx));
1001 connector_builder->add_volume_placement({gap_center});
1002
1003
1004 std::vector<std::vector<scalar_t>> boundaries{};
1005 std::vector<nav_link_t> vol_links{};
1006 for (const auto &e : brl_vol_extents) {
1007 const scalar_t min_r{math::min(e.second.lower, e.second.upper)};
1008 const scalar_t max_r{math::max(e.second.lower, e.second.upper)};
1009
1010 boundaries.push_back(std::vector<scalar_t>{min_r, max_r});
1011 vol_links.push_back(static_cast<nav_link_t>(e.first));
1012 }
1013 assert(boundaries.size() == vol_links.size());
1014
1015
1016 pt_disc_factory->push_back(
1017 {surface_id::e_portal,
1018 transform3_t{point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0),
1019 (side < 0.f) ? max_z : min_z}},
1020 vol_links, boundaries});
1021
1022
1023 pt_disc_factory->push_back(
1024 {surface_id::e_portal,
1025 transform3_t{point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0),
1026 (side < 0.f) ? min_z : max_z}},
1027 static_cast<nav_link_t>(connector_gap_idx - 1u),
1028 std::vector<scalar_t>{inner_r, outer_r}});
1029
1030
1031 pt_cyl_factory->push_back({surface_id::e_portal, identity,
1032 static_cast<nav_link_t>(beampipe_idx),
1033 std::vector<scalar_t>{inner_r, min_z, max_z}});
1034
1035
1036 pt_cyl_factory->push_back({surface_id::e_portal, identity, end_of_world,
1037 std::vector<scalar_t>{outer_r, min_z, max_z}});
1038
1039
1040 connector_builder->add_surfaces(pt_disc_factory);
1041 connector_builder->add_surfaces(pt_cyl_factory);
1042 }
1043
1044
1045
1046
1047
1048 template <typename detector_t>
1049 inline void add_beampipe_portals(
1050 volume_builder_interface<detector_t> *beampipe_builder,
1051 toy_det_config<typename detector_t::scalar_type> &cfg) {
1052 using algebra_t = typename detector_t::algebra_type;
1053 using transform3_t = dtransform3D<algebra_t>;
1054 using scalar_t = dscalar<algebra_t>;
1055 using point3_t = dpoint3D<algebra_t>;
1056 using nav_link_t = typename detector_t::surface_type::navigation_link;
1057
1058 using factory_interface_t = surface_factory_interface<detector_t>;
1059 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
1060 using disc_factory_t = surface_factory<detector_t, ring2D>;
1061
1062 std::shared_ptr<factory_interface_t> pt_cyl_factory{nullptr};
1063
1064 if (cfg.use_material_maps()) {
1065 pt_cyl_factory =
1066 decorate_material<detector_t>(cfg, std::make_unique<cyl_factory_t>());
1067 } else {
1068 pt_cyl_factory = std::make_shared<cyl_factory_t>();
1069 }
1070
1071
1072 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
1073
1074 const scalar_t h_z{cfg.barrel_config().half_length()};
1075 const scalar_t inner_r{0.f};
1076 const scalar_t outer_r{cfg.beampipe_vol_radius()};
1077
1078
1079
1080 if (cfg.n_edc_layers() == 0u) {
1081 std::shared_ptr<factory_interface_t> pt_disc_factory{nullptr};
1082 if (cfg.use_material_maps()) {
1083 pt_disc_factory = decorate_material<detector_t>(
1084 cfg, std::make_unique<disc_factory_t>());
1085 } else {
1086 pt_disc_factory = std::make_shared<disc_factory_t>();
1087 }
1088
1089
1090 pt_disc_factory->push_back(
1091 {surface_id::e_portal,
1092 transform3_t{point3_t{static_cast<scalar_t>(0),
1093 static_cast<scalar_t>(0), -h_z}},
1094 end_of_world, std::vector<scalar_t>{inner_r, outer_r}});
1095
1096
1097 pt_disc_factory->push_back(
1098 {surface_id::e_portal,
1099 transform3_t{
1100 point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0), h_z}},
1101 end_of_world, std::vector<scalar_t>{inner_r, outer_r}});
1102
1103 beampipe_builder->add_surfaces(pt_disc_factory);
1104 }
1105
1106
1107 dindex first_barrel_idx{
1108 cfg.n_brl_layers() == 0u ? end_of_world : 2u * cfg.n_edc_layers() + 2u};
1109 pt_cyl_factory->push_back(
1110 {surface_id::e_portal, transform3_t{},
1111 static_cast<nav_link_t>(first_barrel_idx),
1112 std::vector<scalar_t>{outer_r,
1113 -h_z + std::numeric_limits<scalar_t>::epsilon(),
1114 h_z - std::numeric_limits<scalar_t>::epsilon()}});
1115
1116 beampipe_builder->add_surfaces(pt_cyl_factory);
1117 }
1118
1119
1120
1121
1122
1123
1124
1125
1126 template <typename detector_t, typename layer_size_cont_t>
1127 inline void add_beampipe_portals(
1128 volume_builder_interface<detector_t> *beampipe_builder,
1129 toy_det_config<typename detector_t::scalar_type> &cfg,
1130 const layer_size_cont_t &edc_lay_sizes) {
1131 using algebra_t = typename detector_t::algebra_type;
1132 using transform3_t = dtransform3D<algebra_t>;
1133 using scalar_t = dscalar<algebra_t>;
1134 using point3_t = dpoint3D<algebra_t>;
1135 using nav_link_t = typename detector_t::surface_type::navigation_link;
1136
1137 using factory_interface_t = surface_factory_interface<detector_t>;
1138 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
1139 using disc_factory_t = surface_factory<detector_t, ring2D>;
1140
1141
1142 constexpr auto end_of_world{detail::invalid_value<nav_link_t>()};
1143
1144 const scalar_t side{std::copysign(1.f, edc_lay_sizes.front().second.lower)};
1145
1146 std::shared_ptr<factory_interface_t> pt_cyl_factory{nullptr};
1147 std::shared_ptr<factory_interface_t> pt_disc_factory{nullptr};
1148
1149 if (cfg.use_material_maps()) {
1150 pt_cyl_factory =
1151 decorate_material<detector_t>(cfg, std::make_unique<cyl_factory_t>());
1152 pt_disc_factory =
1153 decorate_material<detector_t>(cfg, std::make_unique<disc_factory_t>());
1154 } else {
1155 pt_cyl_factory = std::make_shared<cyl_factory_t>();
1156 pt_disc_factory = std::make_shared<disc_factory_t>();
1157 }
1158
1159 const scalar_t inner_r{0.f};
1160 const scalar_t outer_r{cfg.beampipe_vol_radius()};
1161 scalar_t disc_pos_z{-side * std::numeric_limits<scalar_t>::max()};
1162
1163
1164 std::vector<std::vector<scalar_t>> boundaries{};
1165 std::vector<nav_link_t> vol_links{};
1166 for (const auto &e : edc_lay_sizes) {
1167 const scalar_t min_z{math::min(e.second.lower, e.second.upper)};
1168 const scalar_t max_z{math::max(e.second.lower, e.second.upper)};
1169
1170 if (side < 0.f) {
1171 disc_pos_z = (disc_pos_z > min_z) ? min_z : disc_pos_z;
1172 } else {
1173 disc_pos_z = (disc_pos_z < max_z) ? max_z : disc_pos_z;
1174 }
1175
1176 boundaries.push_back(std::vector<scalar_t>{outer_r, min_z, max_z});
1177 vol_links.push_back(static_cast<nav_link_t>(e.first));
1178 }
1179 assert(boundaries.size() == vol_links.size());
1180
1181 pt_cyl_factory->push_back(
1182 {surface_id::e_portal, transform3_t{}, vol_links, boundaries});
1183
1184
1185 pt_disc_factory->push_back(
1186 {surface_id::e_portal,
1187 transform3_t{point3_t{static_cast<scalar_t>(0), static_cast<scalar_t>(0),
1188 disc_pos_z}},
1189 end_of_world, std::vector<scalar_t>{inner_r, outer_r}});
1190
1191
1192 beampipe_builder->add_surfaces(pt_disc_factory);
1193 beampipe_builder->add_surfaces(pt_cyl_factory);
1194 }
1195
1196 }
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207 template <concepts::algebra algebra_t>
1208 inline auto build_toy_detector(vecmem::memory_resource &resource,
1209 toy_det_config<dscalar<algebra_t>> cfg = {}) {
1210 using scalar_t = dscalar<algebra_t>;
1211 using transform3_t = dtransform3D<algebra_t>;
1212
1213 using builder_t = detector_builder<toy_metadata<algebra_t>, volume_builder>;
1214 using detector_t = typename builder_t::detector_type;
1215 using nav_link_t = typename detector_t::surface_type::navigation_link;
1216 using cyl_factory_t = surface_factory<detector_t, concentric_cylinder2D>;
1217 using vol_extent_container_t =
1218 std::vector<std::pair<dindex, detail::extent2D<scalar_t>>>;
1219
1220
1221 if (cfg.n_edc_layers() > cfg.endcap_layer_positions().size()) {
1222 throw std::invalid_argument(
1223 "ERROR: Too many endcap layers requested (max " +
1224 std::to_string(cfg.endcap_layer_positions().size()) + ")!");
1225 }
1226 if (cfg.n_brl_layers() > cfg.barrel_layer_radii().size() - 1u) {
1227 throw std::invalid_argument(
1228 "ERROR: Too many barrel layers requested (max " +
1229 std::to_string(cfg.barrel_layer_radii().size() - 1u) + ")!");
1230 }
1231 if (cfg.n_edc_layers() > 0 && cfg.n_brl_layers() < 4) {
1232 throw std::invalid_argument(
1233 "ERROR: All four barrel layers need to be present in order to add "
1234 "endcap layers");
1235 }
1236
1237
1238 builder_t det_builder;
1239 det_builder.set_name("toy_detector");
1240
1241
1242 typename detector_t::geometry_context gctx{};
1243
1244
1245 cfg.material_config().thickness(cfg.beampipe_mat_thickness());
1246 auto beampipe_builder = detail::decorate_material(
1247 cfg, det_builder, det_builder.new_volume(volume_id::e_cylinder));
1248
1249 const dindex beampipe_idx{beampipe_builder->vol_index()};
1250 beampipe_builder->add_volume_placement(transform3_t{});
1251 beampipe_builder->set_name("beampipe_" + std::to_string(beampipe_idx));
1252
1253
1254 auto beampipe_factory = detail::decorate_material<detector_t>(
1255 cfg, std::make_unique<cyl_factory_t>(), true);
1256
1257 scalar_t max_z{cfg.n_edc_layers() == 0u ? cfg.barrel_config().half_length()
1258 : cfg.endcap_layer_positions().at(
1259 cfg.n_edc_layers() - 1u)};
1260 scalar_t min_z{-max_z};
1261
1262 beampipe_factory->push_back(
1263 {surface_id::e_passive, transform3_t{},
1264 static_cast<nav_link_t>(beampipe_idx),
1265 std::vector<scalar_t>{cfg.barrel_layer_radii().at(0), min_z, max_z}});
1266
1267 beampipe_builder->add_surfaces(beampipe_factory);
1268
1269
1270 vol_extent_container_t neg_edc_vol_extents;
1271 if (cfg.n_edc_layers() > 0u) {
1272 cfg.endcap_config().side(-1);
1273
1274 neg_edc_vol_extents =
1275 detail::add_endcap_detector(det_builder, gctx, cfg, beampipe_idx);
1276
1277
1278 detail::add_beampipe_portals(beampipe_builder, cfg, neg_edc_vol_extents);
1279 }
1280
1281 vol_extent_container_t brl_vol_extents;
1282 if (cfg.n_brl_layers() > 0u) {
1283 brl_vol_extents =
1284 detail::add_barrel_detector(det_builder, gctx, cfg, beampipe_idx);
1285 }
1286
1287 detail::add_beampipe_portals(beampipe_builder, cfg);
1288
1289
1290 vol_extent_container_t pos_edc_vol_extents;
1291 if (cfg.n_edc_layers() > 0u) {
1292 cfg.endcap_config().side(1);
1293
1294 pos_edc_vol_extents =
1295 detail::add_endcap_detector(det_builder, gctx, cfg, beampipe_idx);
1296
1297
1298 detail::add_beampipe_portals(beampipe_builder, cfg, pos_edc_vol_extents);
1299
1300
1301
1302 add_connector_portals(det_builder, cfg, beampipe_idx, neg_edc_vol_extents,
1303 brl_vol_extents);
1304
1305 add_connector_portals(det_builder, cfg, beampipe_idx, pos_edc_vol_extents,
1306 brl_vol_extents);
1307 }
1308
1309
1310 typename detector_t::name_map name_map{};
1311 auto det = det_builder.build(resource, name_map);
1312
1313 if (cfg.do_check()) {
1314 const bool verbose_check{false};
1315 detray::detail::check_consistency(det, verbose_check, name_map);
1316 }
1317
1318 DETRAY_DEBUG_HOST("\n" << detray::utils::print_detector(det, name_map));
1319
1320 return std::make_pair(std::move(det), std::move(name_map));
1321 }
1322
1323 }