From 7d595abfd52acfda4f88cc60e28ceac991e966f7 Mon Sep 17 00:00:00 2001 From: Tommy Chun Yuen Tsang Date: Sun, 20 Oct 2024 11:00:23 -0400 Subject: [PATCH] Tof new geom (#796) ### Briefly, what does this PR introduce? Endcap TOF geometry modified to match the newest technical drawings from https://indico.bnl.gov/event/24949/attachments/57520/98759/ePICTOF_WP2_FTOFLayout_09272024v2.pdf. The current epic geometry has each ETOF module located separately. But the newest drawing shows that a single power board house multiple modules together. This PR groups modules together according to the aforementioned drawing. Furthermore, the current epic geometry shows that sensors are placed facing front and backing alternatively across rows. However, the latest drawing shows that ETOF are double sided on every row, but with sensors placed below the service hybrid on the front side, and above the service hybrid on the other side. Each module now contains 4 small sensors instead of one big sensor. Here's the updated ETOF power board configuration, ![image](https://github.com/user-attachments/assets/2f02bd94-cada-4e14-b908-b6b00ce42066) Here's the design drawing, ![image](https://github.com/user-attachments/assets/a5216fd1-b873-49b0-bca1-c08996ed336b) Barrel TOF is also modified. Staves are broken into two so an engagement ring can fit in the middle. LGAD is also located on both sides alternatively, ![image](https://github.com/user-attachments/assets/f72eb346-aaa1-408d-8953-2c8ebc7fe873) Cooling tubes are also incorporated into the staves, ![image](https://github.com/user-attachments/assets/1c12d07f-9741-4849-8d72-80d266574b54) Latest technical drawing shows that sensor width is half of what was previously proposed. Sensors right next to the central engagement ring are only half sized, ![image](https://github.com/user-attachments/assets/3d4dfcca-def7-41db-bf8a-9001a78c751e) ### What kind of change does this PR introduce? - [ ] Bug fix (issue #__) - [ ] New feature (issue #__) - [ ] Documentation update - [ x] Other: BTOF and ETOF geometry modified according to new technical drawing ### Please check if this PR fulfills the following: - [ ] Tests for the changes have been added - [ ] Documentation has been added / updated - [x] Changes have been communicated to collaborators ### Does this PR introduce breaking changes? What changes might users need to make to their code? No ### Does this PR change default behavior? BTOF ETOF geometries are changed. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Dmitry Kalinkin --- compact/tracking/definitions_craterlake.xml | 11 +- compact/tracking/tof_barrel.xml | 122 ++++-- compact/tracking/tof_endcap.xml | 434 ++++++++++++++++++-- src/BarrelTOFTracker_geo.cpp | 87 +++- src/EndcapTOF_geo.cpp | 334 +++++++++------ 5 files changed, 772 insertions(+), 216 deletions(-) diff --git a/compact/tracking/definitions_craterlake.xml b/compact/tracking/definitions_craterlake.xml index e11e6dbe8..3963c0ec5 100644 --- a/compact/tracking/definitions_craterlake.xml +++ b/compact/tracking/definitions_craterlake.xml @@ -41,15 +41,18 @@ Main parameters for Barrel TOF layers Barrel TOF region - - + + + + + Forward TOF region - - + + diff --git a/compact/tracking/tof_barrel.xml b/compact/tracking/tof_barrel.xml index 7a1b6949a..1e8bfee61 100644 --- a/compact/tracking/tof_barrel.xml +++ b/compact/tracking/tof_barrel.xml @@ -63,19 +63,21 @@ - - - - + + + + + + - - - + + + + + + + - - - - @@ -91,52 +93,104 @@ insideTrackingVolume="true"> + rmin="BarrelTOF_rmin - 1*mm" + rmax="BarrelTOF_rmax + 1*mm" + length="BarrelTOF_length" + /> Tracker Barrel Modules - - + + keep_layer means the next module is located at the same thickness level + so you can place component side by side + Just make sure all components you placed side by side share the same thickness + + + - - + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + - - + + + - - + + - - + + + - - + + - - + + + - - + + + - + + diff --git a/compact/tracking/tof_endcap.xml b/compact/tracking/tof_endcap.xml index e0c78530b..ebec385ff 100644 --- a/compact/tracking/tof_endcap.xml +++ b/compact/tracking/tof_endcap.xml @@ -107,7 +107,7 @@ 1 um padding to not have layer and module touch (ACTS requirement) - + @@ -122,8 +122,8 @@ - - + + @@ -133,19 +133,25 @@ - - - - - - - + + + + + + + + + + + - + + + @@ -155,12 +161,14 @@ - + + - + + @@ -205,7 +213,7 @@ reflect="false"> - + @@ -218,19 +226,99 @@ - - + + + + + + + + + + + - + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -244,13 +332,16 @@ - + + + zstart="ForwardTOF_zmin" + xoffset="EndcapTOF_Module_length_design/2" + /> @@ -261,13 +352,300 @@ rmin="ForwardTOFLayer_rmin" rmax="ForwardTOFLayer_rmax" length="12*mm" - zstart="ForwardTOF_zmin" /> + zstart="ForwardTOF_zmin" + xoffset="EndcapTOF_Module_length_design/2" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + spacing="EndcapTOF_Module_spacing" + board_gap="EndcapTOF_Board_spacing" /> @@ -280,8 +658,8 @@ - - system:8,layer:4,module:8,idx:7,idy:5,x:32:-16,y:-16 + + system:8,layer:4,module:2,idx:5,idy:5,ids:6,x:36:-12,y:-16 diff --git a/src/BarrelTOFTracker_geo.cpp b/src/BarrelTOFTracker_geo.cpp index 6a9cf6c75..6598bc961 100644 --- a/src/BarrelTOFTracker_geo.cpp +++ b/src/BarrelTOFTracker_geo.cpp @@ -136,7 +136,8 @@ static Ref_t create_TOFBarrel(Detector& description, xml_h e, SensitiveDetector // Compute module total thickness from components xml_coll_t ci(x_mod, _U(module_component)); for (ci.reset(), total_thickness = 0.0; ci; ++ci) { - total_thickness += xml_comp_t(ci).thickness(); + if (!getAttrOrDefault(xml_comp_t(ci), _Unicode(keep_layer), false)) + total_thickness += xml_comp_t(ci).thickness(); } // the module assembly volume Assembly m_vol(m_nam); @@ -174,18 +175,49 @@ static Ref_t create_TOFBarrel(Detector& description, xml_h e, SensitiveDetector xml_comp_t x_comp = mci; xml_comp_t x_pos = x_comp.position(false); xml_comp_t x_rot = x_comp.rotation(false); - auto make_box = [&](double pos_x = 0, double pos_y = 0, double pos_z = 0, double rot_x = 0, - double rot_y = 0, double rot_z = 0, bool z_stacking = true) { + auto make_box = [&](double width, double length, double thickness, double pos_x = 0, + double pos_y = 0, double pos_z = 0, double rot_x = 0, double rot_y = 0, + double rot_z = 0, bool z_stacking = true) { + // Utility variable for the relative z-offset based off the previous components + const double zoff = thickness_sum + thickness / 2.0; + const string c_nam = _toString(ncomponents, "component%d"); ++ncomponents; - Box c_box(x_comp.width() / 2, x_comp.length() / 2, x_comp.thickness() / 2); - Volume c_vol(c_nam, c_box, description.material(x_comp.materialStr())); + Box c_box(width / 2, length / 2, thickness / 2); + Volume c_vol; + + xml_coll_t ci_tube(x_comp, _Unicode(inner_tube)); + if (ci_tube) { + double max_r = 0; + for (; ci_tube; ++ci_tube) { + // fill the hole with tube + xml_comp_t ct = ci_tube; + max_r = std::max(max_r, ct.rmax()); + Tube c_tube(ct.rmin(), ct.rmax(), length / 2); + Volume c_tubevol(c_nam + ct.nameStr(), c_tube, description.material(ct.materialStr())); + if (ct.visStr() != "") + c_tubevol.setVisAttributes(description, ct.visStr()); + m_vol.placeVolume(c_tubevol, Transform3D(RotationZYX(0, 0, -M_PI / 2), + Position(pos_x, pos_y, pos_z + zoff))); + } - // Utility variable for the relative z-offset based off the previous components - const double zoff = thickness_sum + x_comp.thickness() / 2.0; + Tube c_fbox(0, max_r, length / 2 + 1); + SubtractionSolid c_sbox(c_box, c_fbox, + Transform3D(RotationZYX(0, 0, -M_PI / 2), + Position(0, 0, 0))); //pos_x, pos_y, pos_z + zoff))); + + c_vol = Volume(c_nam, c_sbox, description.material(x_comp.materialStr())); + } else + c_vol = Volume(c_nam, c_box, description.material(x_comp.materialStr())); + + Volume test; + test = c_vol; + + // center if off by half the box length if box length is cut in half Position c_pos(pos_x, pos_y, pos_z + zoff); RotationZYX c_rot(rot_z, rot_y, rot_x); pv = m_vol.placeVolume(c_vol, Transform3D(c_rot, c_pos)); + c_vol.setRegion(description, x_comp.regionStr()); c_vol.setLimitSet(description, x_comp.limitsStr()); c_vol.setVisAttributes(description, x_comp.visStr()); @@ -193,9 +225,8 @@ static Ref_t create_TOFBarrel(Detector& description, xml_h e, SensitiveDetector pv.addPhysVolID("sensor", sensor_number++); c_vol.setSensitiveDetector(sens); sensitives[m_nam].push_back(pv); - module_thicknesses[m_nam] = {thickness_so_far + x_comp.thickness() / 2.0, - total_thickness - thickness_so_far - - x_comp.thickness() / 2.0}; + module_thicknesses[m_nam] = {thickness_so_far + thickness / 2.0, + total_thickness - thickness_so_far - thickness / 2.0}; // -------- create a measurement plane for the tracking surface attched to the sensitive volume ----- Vector3D u(-1., 0., 0.); @@ -219,8 +250,8 @@ static Ref_t create_TOFBarrel(Detector& description, xml_h e, SensitiveDetector //-------------------------------------------- } if (z_stacking) { - thickness_sum += x_comp.thickness(); - thickness_so_far += x_comp.thickness(); + thickness_sum += thickness; + thickness_so_far += thickness; // apply relative offsets in z-position used to stack components side-by-side thickness_sum += pos_z; thickness_so_far += pos_z; @@ -239,6 +270,11 @@ static Ref_t create_TOFBarrel(Detector& description, xml_h e, SensitiveDetector pos_y = x_pos.y(0); pos_z = x_pos.z(0); } + double width = x_comp.width(); + double length = x_comp.length(); + double thickness = x_comp.thickness(); + bool keep_layer = getAttrOrDefault(x_comp, _Unicode(keep_layer), false); + if (x_comp.hasChild(_Unicode(GridSensors))) { auto x_comp_t = x_comp.child(_Unicode(GridSensors)); // x-distance between centers of neighboring sensors @@ -262,23 +298,40 @@ static Ref_t create_TOFBarrel(Detector& description, xml_h e, SensitiveDetector // || || || // ring_extra_width is the extra width between boundaries of the sensor boundaries (including dead space) double ring_extra_width = getAttrOrDefault(x_comp_t, _Unicode(ring_extra_width), 0); + auto half_length_str = + getAttrOrDefault(x_comp_t, _Unicode(half_length), "none"); double current_x = start_x; for (int nx = 0; nx < nsensors_x; ++nx) { double current_y = start_y; for (int ny = 0; ny < nsensors_y; ++ny) { - make_box(current_x, current_y, start_z, rot_x, rot_y, rot_z, - ((nx == nsensors_x - 1) && - (ny == nsensors_y - 1))); // all sensors are located at the same z-layer + double sensor_length = length; + double tmp_sensors_ydist = sensors_ydist; + // when we draw half a sensor, the center has to be shifted by 0.25 times the length of a sensor + // distance between centers to the next sensor also has to be reduced by 0.25 times the length of a sensor + if ((half_length_str == "left" || half_length_str == "both") && ny == 0) { + sensor_length = 0.5 * length; + current_y += 0.25 * length; + tmp_sensors_ydist -= 0.25 * length; + } + // same idea, but when you are drawing to the right, the right sensor center has to move in -y direction + if ((half_length_str == "right" || half_length_str == "both") && ny == nsensors_y - 1) { + sensor_length = 0.5 * length; + current_y -= 0.25 * length; + } + make_box(width, sensor_length, thickness, current_x, current_y, start_z, rot_x, rot_y, + rot_z, + (((nx == nsensors_x - 1) && (ny == nsensors_y - 1))) && + !keep_layer); // all sensors are located at the same z-layer // increment z-layers only at the end, after the last sensor is added - current_y += sensors_ydist; + current_y += tmp_sensors_ydist; if (ny + 1 == ny_before_ring) current_y += ring_extra_width; } current_x += sensors_xdist; } } else - make_box(pos_x, pos_y, pos_z, rot_x, rot_y, rot_z); + make_box(width, length, thickness, pos_x, pos_y, pos_z, rot_x, rot_y, rot_z, !keep_layer); } } diff --git a/src/EndcapTOF_geo.cpp b/src/EndcapTOF_geo.cpp index c4edc64a8..5e6c0c01c 100644 --- a/src/EndcapTOF_geo.cpp +++ b/src/EndcapTOF_geo.cpp @@ -1,9 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright (C) 2022 Nicolas Schmidt +// Copyright (C) 2022 - 2024, Nicolas Schmidt, Chun Yuen Tsang /** \addtogroup Trackers Trackers * \brief Type: **Endcap Tracker with TOF**. - * \author N. Schmidt * * \ingroup trackers * @@ -19,6 +18,7 @@ #include "XML/Utilities.h" #include #include +#include using namespace std; using namespace dd4hep; @@ -83,152 +83,220 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s double module_y = x_modsz.width(); double module_overlap = getAttrOrDefault(x_modsz, _Unicode(overlap), 0.); // x_modsz.overlap(); double module_spacing = getAttrOrDefault(x_modsz, _Unicode(spacing), 0.); // x_modsz.overlap(); + double board_gap = getAttrOrDefault(x_modsz, _Unicode(board_gap), 0.); //! Add support structure xml_comp_t x_supp = x_det.child(_Unicode(support)); xml_comp_t x_supp_envelope = x_supp.child(_Unicode(envelope), false); - double total_thickness = 0; - xml_comp_t x_modFront = x_det.child(_Unicode(moduleFront)); - xml_comp_t x_modBack = x_det.child(_Unicode(moduleBack)); - - // Compute module total thickness from components - xml_coll_t ci(x_modFront, _U(module_component)); - for (ci.reset(), total_thickness = 0.0; ci; ++ci) { - total_thickness += xml_comp_t(ci).thickness(); - } - - int module = 0; - int nx = 25; - int ny = 15; - for (int ix = 0; ix < 2 * nx; ix++) { - float xcoord = (ix - nx) * (module_x + module_spacing); - for (int iy = 0; iy < 2 * ny; iy++) { - float ycoord = (iy - ny) * (module_y - module_overlap); - //! Note the module ordering is different for front and back side - xml_comp_t x_modCurr = iy % 2 == 0 ? x_modFront : x_modBack; - - double module_z = x_supp_envelope.length() / 2.0 + total_thickness / 2; - if (iy % 2 == 0) { - module_z *= -1; - } - float corner_x1 = xcoord + module_x / 2; - float corner_x2 = xcoord - module_x / 2; - float corner_y1 = ycoord + module_y / 2; - float corner_y2 = ycoord - module_y / 2; - float maxRadius = - std::max(std::max(std::hypot(corner_x1, corner_y1), std::hypot(corner_x2, corner_y2)), - std::max(std::hypot(corner_x1, corner_y2), std::hypot(corner_x2, corner_y1))); - float minRadius = - std::min(std::min(std::hypot(corner_x1, corner_y1), std::hypot(corner_x2, corner_y2)), - std::min(std::hypot(corner_x1, corner_y2), std::hypot(corner_x2, corner_y1))); - if (maxRadius > envelope.rmax() || minRadius < envelope.rmin()) { - continue; - } - - string module_name = Form("module%d_%d_%d", module, ix, iy); - DetElement mod_elt(lay_elt, module_name, module); - - // create individual sensor layers here - string m_nam = Form("EndcapTOF_Module1_%d_%d", ix, iy); - - int ncomponents = 0; - // the module assembly volume - Assembly m_vol(m_nam); - m_vol.setVisAttributes(description.visAttributes(x_modCurr.visStr())); - - double thickness_so_far = 0.0; - double thickness_sum = -total_thickness / 2.0; - double thickness_carbonsupp = 0.0; - for (xml_coll_t mci(x_modCurr, _U(module_component)); mci; ++mci, ++ncomponents) { - xml_comp_t x_comp = mci; - xml_comp_t x_pos = x_comp.position(false); - xml_comp_t x_rot = x_comp.rotation(false); - const string c_nam = Form("component_%d_%d", ix, iy); - Box c_box(x_comp.width() / 2, x_comp.length() / 2, x_comp.thickness() / 2); - Volume c_vol(c_nam, c_box, description.material(x_comp.materialStr())); - if (x_comp.materialStr() == "CarbonFiber") { - thickness_carbonsupp = x_comp.thickness(); - } - // Utility variable for the relative z-offset based off the previous components - const double zoff = thickness_sum + x_comp.thickness() / 2.0; - if (x_pos && x_rot) { - Position c_pos(x_pos.x(0), x_pos.y(0), x_pos.z(0) + zoff); - RotationZYX c_rot(x_rot.z(0), x_rot.y(0), x_rot.x(0)); - pv = m_vol.placeVolume(c_vol, Transform3D(c_rot, c_pos)); - } else if (x_rot) { - Position c_pos(0, 0, zoff); - pv = m_vol.placeVolume( - c_vol, Transform3D(RotationZYX(x_rot.z(0), x_rot.y(0), x_rot.x(0)), c_pos)); - } else if (x_pos) { - pv = m_vol.placeVolume(c_vol, Position(x_pos.x(0), x_pos.y(0), x_pos.z(0) + zoff)); + xml_comp_t x_modFrontLeft = x_det.child(_Unicode(moduleFrontLeft)); + xml_comp_t x_modFrontRight = x_det.child(_Unicode(moduleFrontRight)); + xml_comp_t x_modBackLeft = x_det.child(_Unicode(moduleBackLeft)); + xml_comp_t x_modBackRight = x_det.child(_Unicode(moduleBackRight)); + + xml_comp_t x_sensor_layout_front_left = x_det.child(_Unicode(sensor_layout_front_left)); + xml_comp_t x_sensor_layout_back_left = x_det.child(_Unicode(sensor_layout_back_left)); + xml_comp_t x_sensor_layout_front_right = x_det.child(_Unicode(sensor_layout_front_right)); + xml_comp_t x_sensor_layout_back_right = x_det.child(_Unicode(sensor_layout_back_right)); + + for (bool left : std::vector{true, false}) { + for (bool front : std::vector{true, false}) { + int module = (front << 1) + left; + float ycoord = envelope.rmax() - + module_y / 2.; // y-center-coord of the top sensor. Start from the top row + int iy = 0; + xml_comp_t x_sensor_layout = x_sensor_layout_front_left; + xml_comp_t x_modCurr = x_modFrontLeft; + + if (front) { + if (left) { + x_sensor_layout = x_sensor_layout_front_left; + x_modCurr = x_modFrontLeft; } else { - pv = m_vol.placeVolume(c_vol, Position(0, 0, zoff)); - } - c_vol.setRegion(description, x_comp.regionStr()); - c_vol.setLimitSet(description, x_comp.limitsStr()); - c_vol.setVisAttributes(description, x_comp.visStr()); - if (x_comp.isSensitive()) { - pv.addPhysVolID("idx", ix); - pv.addPhysVolID("idy", iy); - c_vol.setSensitiveDetector(sens); - module_thicknesses[m_nam] = {thickness_so_far + x_comp.thickness() / 2.0, - total_thickness - thickness_so_far - - x_comp.thickness() / 2.0}; - - // -------- create a measurement plane for the tracking surface attched to the sensitive volume ----- - Vector3D u(-1., 0., 0.); - Vector3D v(0., -1., 0.); - Vector3D n(0., 0., 1.); - - // compute the inner and outer thicknesses that need to be assigned to the tracking surface - // depending on wether the support is above or below the sensor - double inner_thickness = module_thicknesses[m_nam][0]; - double outer_thickness = module_thicknesses[m_nam][1]; - - SurfaceType type(SurfaceType::Sensitive); - - VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n); - - DetElement comp_de(mod_elt, std::string("de_") + pv.volume().name(), module); - comp_de.setPlacement(pv); - - auto& comp_de_params = - DD4hepDetectorHelper::ensureExtension(comp_de); - comp_de_params.set("axis_definitions", "XYZ"); - volSurfaceList(comp_de)->push_back(surf); - - //-------------------------------------------- + x_sensor_layout = x_sensor_layout_front_right; + x_modCurr = x_modFrontRight; } - thickness_sum += x_comp.thickness(); - thickness_so_far += x_comp.thickness(); - // apply relative offsets in z-position used to stack components side-by-side - if (x_pos) { - thickness_sum += x_pos.z(0); - thickness_so_far += x_pos.z(0); + } else { + if (left) { + x_sensor_layout = x_sensor_layout_back_left; + x_modCurr = x_modBackLeft; + } else { + x_sensor_layout = x_sensor_layout_back_right; + x_modCurr = x_modBackRight; } } - const string suppb_nam = - Form("suppbar_%d_%d", ix, iy); //_toString(ncomponents, "component%d"); - Box suppb_box((module_x + module_spacing) / 2, thickness_carbonsupp / 2, - x_supp_envelope.length() / 2); - Volume suppb_vol(suppb_nam, suppb_box, carbon); - Transform3D trsupp(RotationZYX(0, 0, 0), - Position(xcoord, ycoord + module_y / 2 - module_overlap / 2, 0)); - suppb_vol.setVisAttributes(description, "AnlGray"); + double total_thickness = 0; + // Compute module total thickness from components + xml_coll_t ci(x_modCurr, _U(module_component)); - pv = lay_vol.placeVolume(suppb_vol, trsupp); - // module built! + for (ci.reset(), total_thickness = 0.0; ci; ++ci) { + xml_comp_t x_comp = ci; + bool keep_same_layer = getAttrOrDefault(x_comp, _Unicode(keep_layer), false); + if (!keep_same_layer) + total_thickness += x_comp.thickness(); + } - Transform3D tr(RotationZYX(M_PI / 2, 0, 0), Position(xcoord, ycoord, module_z)); + for (xml_coll_t lrow(x_sensor_layout, _Unicode(row)); lrow; ++lrow) { + xml_comp_t x_row = lrow; + double deadspace = getAttrOrDefault(x_row, _Unicode(deadspace), 0); + if (deadspace > 0) { + ycoord -= deadspace; + continue; + } + double x_offset = getAttrOrDefault(x_row, _Unicode(x_offset), 0); + int nsensors = getAttrOrDefault(x_row, _Unicode(nsensors), 0); + + // find the sensor id that corrsponds to the rightmost sensor in a board + // we need to know where to apply additional spaces between neighboring board + std::unordered_set sensors_id_board_edge; + int curr_ix = nsensors; // the first sensor to the right of center has ix of nsensors + for (xml_coll_t lboard(x_row, _Unicode(board)); lboard; ++lboard) { + xml_comp_t x_board = lboard; + int nboard_sensors = getAttrOrDefault(x_board, _Unicode(nsensors), 1); + curr_ix += nboard_sensors; + sensors_id_board_edge.insert(curr_ix); + sensors_id_board_edge.insert(2 * nsensors - curr_ix - + 1); // reflected to sensor id on the left + } - pv = lay_vol.placeVolume(m_vol, tr); - pv.addPhysVolID("module", module); - mod_elt.setPlacement(pv); + double accum_xoffset = x_offset; + for (int ix = (left ? nsensors - 1 : nsensors); (ix >= 0) && (ix < 2 * nsensors); + ix = ix + (left ? -1 : 1)) { + // add board spacing + if (sensors_id_board_edge.find(ix) != sensors_id_board_edge.end()) + accum_xoffset = accum_xoffset + board_gap; + + // there is a hole in the middle, with radius = x_offset + float xcoord = (ix - nsensors + 0.5) * (module_x + module_spacing) + + +(left ? -accum_xoffset : accum_xoffset); + //! Note the module ordering is different for front and back side + + double module_z = x_supp_envelope.length() / 2.0 + total_thickness / 2; + if (front) + module_z *= -1; + + string module_name = Form("module%d_%d_%d", module, ix, iy); + DetElement mod_elt(lay_elt, module_name, module); + + // create individual sensor layers here + string m_nam = Form("EndcapTOF_Module%d_%d_%d", module, ix, iy); + + int ncomponents = 0; + // the module assembly volume + Assembly m_vol(m_nam); + m_vol.setVisAttributes(description.visAttributes(x_modCurr.visStr())); + + double thickness_so_far = 0.0; + double thickness_sum = -total_thickness / 2.0; + double thickness_carbonsupp = 0.0; + int sensitive_id = 0; + for (xml_coll_t mci(x_modCurr, _U(module_component)); mci; ++mci, ++ncomponents) { + xml_comp_t x_comp = mci; + xml_comp_t x_pos = x_comp.position(false); + xml_comp_t x_rot = x_comp.rotation(false); + const string c_nam = Form("component_%d_%d_%d", module, ix, iy); + + Box c_box(x_comp.width() / 2, x_comp.length() / 2, x_comp.thickness() / 2); + Volume c_vol(c_nam, c_box, description.material(x_comp.materialStr())); + if (x_comp.materialStr() == "CarbonFiber") { + thickness_carbonsupp = x_comp.thickness(); + } + // Utility variable for the relative z-offset based off the previous components + const double zoff = thickness_sum + x_comp.thickness() / 2.0; + if (x_pos && x_rot) { + Position c_pos(x_pos.x(0), x_pos.y(0), x_pos.z(0) + zoff); + RotationZYX c_rot(x_rot.z(0), x_rot.y(0), x_rot.x(0)); + pv = m_vol.placeVolume(c_vol, Transform3D(c_rot, c_pos)); + } else if (x_rot) { + Position c_pos(0, 0, zoff); + pv = m_vol.placeVolume( + c_vol, Transform3D(RotationZYX(x_rot.z(0), x_rot.y(0), x_rot.x(0)), c_pos)); + } else if (x_pos) { + pv = m_vol.placeVolume(c_vol, Position(x_pos.x(0), x_pos.y(0), x_pos.z(0) + zoff)); + } else { + pv = m_vol.placeVolume(c_vol, Position(0, 0, zoff)); + } + c_vol.setRegion(description, x_comp.regionStr()); + c_vol.setLimitSet(description, x_comp.limitsStr()); + c_vol.setVisAttributes(description, x_comp.visStr()); + if (x_comp.isSensitive()) { + pv.addPhysVolID("idx", ix); + pv.addPhysVolID("idy", iy); + pv.addPhysVolID("ids", sensitive_id); + ++sensitive_id; + + c_vol.setSensitiveDetector(sens); + module_thicknesses[m_nam] = {thickness_so_far + x_comp.thickness() / 2.0, + total_thickness - thickness_so_far - + x_comp.thickness() / 2.0}; + + // -------- create a measurement plane for the tracking surface attched to the sensitive volume ----- + Vector3D u(-1., 0., 0.); + Vector3D v(0., -1., 0.); + Vector3D n(0., 0., 1.); + + // compute the inner and outer thicknesses that need to be assigned to the tracking surface + // depending on wether the support is above or below the sensor + double inner_thickness = module_thicknesses[m_nam][0]; + double outer_thickness = module_thicknesses[m_nam][1]; + + SurfaceType type(SurfaceType::Sensitive); + + VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n); + + DetElement comp_de(mod_elt, + std::string("de_") + pv.volume().name() + "_" + + std::to_string(sensitive_id), + module); + comp_de.setPlacement(pv); + + auto& comp_de_params = + DD4hepDetectorHelper::ensureExtension(comp_de); + comp_de_params.set("axis_definitions", "XYZ"); + volSurfaceList(comp_de)->push_back(surf); + + //-------------------------------------------- + } + bool keep_same_layer = getAttrOrDefault(x_comp, _Unicode(keep_layer), false); + if (!keep_same_layer) { + thickness_sum += x_comp.thickness(); + thickness_so_far += x_comp.thickness(); + // apply relative offsets in z-position used to stack components side-by-side + if (x_pos) { + thickness_sum += x_pos.z(0); + thickness_so_far += x_pos.z(0); + } + } + } + + if (front) { + // only draw support bar on one side + // if you draw on both sides, they may overlap + const string suppb_nam = + Form("suppbar_%d_%d", ix, iy); //_toString(ncomponents, "component%d"); + Box suppb_box((module_x + module_spacing) / 2, thickness_carbonsupp / 2, + x_supp_envelope.length() / 2); + Volume suppb_vol(suppb_nam, suppb_box, carbon); + Transform3D trsupp(RotationZYX(0, 0, 0), + Position(xcoord, ycoord + module_y / 2 - module_overlap / 2, 0)); + suppb_vol.setVisAttributes(description, "AnlGray"); + + pv = lay_vol.placeVolume(suppb_vol, trsupp); + } + // module built! + + Transform3D tr(RotationZYX(M_PI / 2, 0, 0), Position(xcoord, ycoord, module_z)); + + pv = lay_vol.placeVolume(m_vol, tr); + pv.addPhysVolID("module", module); + mod_elt.setPlacement(pv); + } + ycoord -= (module_y - module_overlap); + ++iy; + } } } - // Create the PhysicalVolume for the layer. pv = assembly.placeVolume(lay_vol, lay_pos); // Place layer in mother pv.addPhysVolID("layer", lay_id); // Set the layer ID.