From 22e5f68814528715f8cab4babf5a3c7ef55df7a4 Mon Sep 17 00:00:00 2001 From: Tommy Chun Yuen Tsang Date: Tue, 24 Sep 2024 11:13:12 -0400 Subject: [PATCH] Modified Barrel TOF geometry to add dead spaces between sensors and between support ring structure (#787) ### Briefly, what does this PR introduce? The BTOF active volume is modified to be more realistic. Now there are dead spaces between each sensors on a stave and extra dead space is reserved in the center of the BTOF to make space for the central supporting ring structure. Variables relevant to the dead spaces, sensor sizes and the width of the ring are adjustable from tof_barrel.xml. ![image](https://github.com/user-attachments/assets/7bd98ce5-5f2d-4ae9-9fcb-8b878973e9b4) ### What kind of change does this PR introduce? - [ ] Bug fix (issue #__) - [ ] New feature (issue #__) - [ ] Documentation update - [x] Other: BTOF geometry is modified to accommodate individual sensors. ### Please check if this PR fulfills the following: - [ ] Tests for the changes have been added - [ ] Documentation has been added / updated - [ ] Changes have been communicated to collaborators ### Does this PR introduce breaking changes? What changes might users need to make to their code? ### Does this PR change default behavior? --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Dmitry Kalinkin --- compact/tracking/tof_barrel.xml | 17 +- src/BarrelTOFTracker_geo.cpp | 399 +++++++++++++++++++++++++++++ src/BarrelTrackerWithFrame_geo.cpp | 1 - 3 files changed, 412 insertions(+), 5 deletions(-) create mode 100644 src/BarrelTOFTracker_geo.cpp diff --git a/compact/tracking/tof_barrel.xml b/compact/tracking/tof_barrel.xml index d916f0265..7a1b6949a 100644 --- a/compact/tracking/tof_barrel.xml +++ b/compact/tracking/tof_barrel.xml @@ -68,6 +68,15 @@ + + + + + + + + + @@ -89,8 +98,8 @@ Tracker Barrel Modules - - + + @@ -140,8 +149,8 @@ - - system:8,layer:4,module:12,sensor:2,x:32:-16,y:-16 + + system:8,layer:4,module:12,sensor:10,x:40:-8,y:-16 diff --git a/src/BarrelTOFTracker_geo.cpp b/src/BarrelTOFTracker_geo.cpp new file mode 100644 index 000000000..6a9cf6c75 --- /dev/null +++ b/src/BarrelTOFTracker_geo.cpp @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2022 - 2024, Whitney Armstrong, Chun Yuen Tsang + +/** \addtogroup Trackers Trackers + * \brief Type: **TOFBarrel**. + * \author W. Armstrong + * \modified by C.Y Tsang 3rd Aug, 2024 + * + * \ingroup trackers + * + * @{ + */ +#include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/Printout.h" +#include "DD4hep/Shapes.h" +#include "DDRec/DetectorData.h" +#include "DDRec/Surface.h" +#include "XML/Layering.h" +#include "XML/Utilities.h" +#include +#include +#include "DD4hepDetectorHelper.h" + +using namespace std; +using namespace dd4hep; +using namespace dd4hep::rec; +using namespace dd4hep::detail; + +/** Barrel Tracker with space frame. + * + * - Optional "support" tag within the detector element. + * + * The shapes are created using createShape which can be one of many basic geomtries. + * See the examples Check_shape_*.xml in + * [dd4hep's examples/ClientTests/compact](https://github.com/AIDASoft/DD4hep/tree/master/examples/ClientTests/compact) + * directory. + * + * + * - Optional "frame" tag within the module element. + * + * \ingroup trackers + * + * \code + * \endcode + * + * + * @author Whitney Armstrong + */ +static Ref_t create_TOFBarrel(Detector& description, xml_h e, SensitiveDetector sens) { + typedef vector Placements; + xml_det_t x_det = e; + Material air = description.air(); + int det_id = x_det.id(); + string det_name = x_det.nameStr(); + DetElement sdet(det_name, det_id); + + map volumes; + map sensitives; + map> volplane_surfaces; + map> module_thicknesses; + + PlacedVolume pv; + + // Set detector type flag + dd4hep::xml::setDetectorTypeFlag(x_det, sdet); + auto& params = DD4hepDetectorHelper::ensureExtension(sdet); + + // Add the volume boundary material if configured + for (xml_coll_t bmat(x_det, _Unicode(boundary_material)); bmat; ++bmat) { + xml_comp_t x_boundary_material = bmat; + DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_boundary_material, params, + "boundary_material"); + } + + // dd4hep::xml::Dimension dimensions(x_det.dimensions()); + // Tube topVolumeShape(dimensions.rmin(), dimensions.rmax(), dimensions.length() * 0.5); + // Volume assembly(det_name,topVolumeShape,air); + Assembly assembly(det_name); + + sens.setType("tracker"); + + // Loop over the suports + for (xml_coll_t su(x_det, _U(support)); su; ++su) { + xml_comp_t x_support = su; + double support_thickness = getAttrOrDefault(x_support, _U(thickness), 2.0 * mm); + double support_length = getAttrOrDefault(x_support, _U(length), 2.0 * mm); + double support_rmin = getAttrOrDefault(x_support, _U(rmin), 2.0 * mm); + double support_zstart = getAttrOrDefault(x_support, _U(zstart), 2.0 * mm); + std::string support_name = + getAttrOrDefault(x_support, _Unicode(name), "support_tube"); + std::string support_vis = getAttrOrDefault(x_support, _Unicode(vis), "AnlRed"); + xml_dim_t pos(x_support.child(_U(position), false)); + xml_dim_t rot(x_support.child(_U(rotation), false)); + Solid support_solid; + if (x_support.hasChild(_U(shape))) { + xml_comp_t shape(x_support.child(_U(shape))); + string shape_type = shape.typeStr(); + support_solid = xml::createShape(description, shape_type, shape); + } else { + support_solid = Tube(support_rmin, support_rmin + support_thickness, support_length / 2); + } + Transform3D tr = + Transform3D(Rotation3D(), Position(0, 0, (support_zstart + support_length / 2))); + if (pos.ptr() && rot.ptr()) { + Rotation3D rot3D(RotationZYX(rot.z(0), rot.y(0), rot.x(0))); + Position pos3D(pos.x(0), pos.y(0), pos.z(0)); + tr = Transform3D(rot3D, pos3D); + } else if (pos.ptr()) { + tr = Transform3D(Rotation3D(), Position(pos.x(0), pos.y(0), pos.z(0))); + } else if (rot.ptr()) { + Rotation3D rot3D(RotationZYX(rot.z(0), rot.y(0), rot.x(0))); + tr = Transform3D(rot3D, Position()); + } + Material support_mat = description.material(x_support.materialStr()); + Volume support_vol(support_name, support_solid, support_mat); + support_vol.setVisAttributes(description.visAttributes(support_vis)); + pv = assembly.placeVolume(support_vol, tr); + // pv = assembly.placeVolume(support_vol, Position(0, 0, support_zstart + support_length / 2)); + } + + // loop over the modules + for (xml_coll_t mi(x_det, _U(module)); mi; ++mi) { + xml_comp_t x_mod = mi; + string m_nam = x_mod.nameStr(); + + if (volumes.find(m_nam) != volumes.end()) { + printout(ERROR, "TOFBarrel", + string((string("Module with named ") + m_nam + string(" already exists."))).c_str()); + throw runtime_error("Logics error in building modules."); + } + + int ncomponents = 0; + int sensor_number = 1; + double total_thickness = 0; + + // 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(); + } + // the module assembly volume + Assembly m_vol(m_nam); + volumes[m_nam] = m_vol; + m_vol.setVisAttributes(description.visAttributes(x_mod.visStr())); + + // Optional module frame. + if (x_mod.hasChild(_U(frame))) { + xml_comp_t m_frame = x_mod.child(_U(frame)); + // xmleles[m_nam] = x_mod; + double frame_thickness = m_frame.thickness(); + double frame_width = m_frame.width(); + double frame_height = getAttrOrDefault(m_frame, _U(height), 5.0 * mm); + double tanth = frame_height / (frame_width / 2.0); + double costh = 1. / sqrt(1 + tanth * tanth); + double frame_height2 = frame_height - frame_thickness - frame_thickness / costh; + double frame_width2 = 2.0 * frame_height2 / tanth; + + Trd1 moduleframe_part1(frame_width / 2, 0.001 * mm, m_frame.length() / 2, frame_height / 2); + Trd1 moduleframe_part2(frame_width2 / 2, 0.001 * mm, m_frame.length() / 2 + 0.01 * mm, + frame_height2 / 2); + + SubtractionSolid moduleframe(moduleframe_part1, moduleframe_part2, + Position(0.0, frame_thickness, 0.0)); + Volume v_moduleframe(m_nam + "_vol", moduleframe, + description.material(m_frame.materialStr())); + v_moduleframe.setVisAttributes(description, m_frame.visStr()); + m_vol.placeVolume(v_moduleframe, + Position(0.0, 0.0, frame_height / 2 + total_thickness / 2.0)); + } + + double thickness_so_far = 0.0; + double thickness_sum = -total_thickness / 2.0; + for (xml_coll_t mci(x_mod, _U(module_component)); mci; ++mci) { + 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) { + 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())); + + // Utility variable for the relative z-offset based off the previous components + const double zoff = thickness_sum + x_comp.thickness() / 2.0; + 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()); + if (x_comp.isSensitive()) { + 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}; + + // -------- 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.); + // Vector3D o( 0. , 0. , 0. ) ; + + // 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); + + // if( isStripDetector ) + // type.setProperty( SurfaceType::Measurement1D , true ) ; + + VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n); //,o ) ; + volplane_surfaces[m_nam].push_back(surf); + + //-------------------------------------------- + } + if (z_stacking) { + thickness_sum += x_comp.thickness(); + thickness_so_far += x_comp.thickness(); + // apply relative offsets in z-position used to stack components side-by-side + thickness_sum += pos_z; + thickness_so_far += pos_z; + } + }; + + double pos_x = 0, pos_y = 0, pos_z = 0; + double rot_x = 0, rot_y = 0, rot_z = 0; + if (x_rot) { + rot_x = x_rot.x(0); + rot_y = x_rot.y(0); + rot_z = x_rot.z(0); + } + if (x_pos) { + pos_x = x_pos.x(0); + pos_y = x_pos.y(0); + pos_z = x_pos.z(0); + } + if (x_comp.hasChild(_Unicode(GridSensors))) { + auto x_comp_t = x_comp.child(_Unicode(GridSensors)); + // x-distance between centers of neighboring sensors + double sensors_xdist = getAttrOrDefault(x_comp_t, _Unicode(xdist), x_comp.width()); + // y-distance between centers of neighboring sensors + double sensors_ydist = getAttrOrDefault(x_comp_t, _Unicode(ydist), x_comp.length()); + // number of rows of sensors in a stave + int nsensors_x = getAttrOrDefault(x_comp_t, _Unicode(nx), 1); + // number of column of sensors in a stave + int nsensors_y = getAttrOrDefault(x_comp_t, _Unicode(ny), 1); + // x-location of the center of the leftmost sensor + double start_x = getAttrOrDefault(x_comp_t, _Unicode(start_x), 0); + // y-location of the center of the uppermost sensor + double start_y = getAttrOrDefault(x_comp_t, _Unicode(start_y), 0); + // z-locatino of the center of all sensors (All sensors appears at the same z-layer + double start_z = getAttrOrDefault(x_comp_t, _Unicode(start_z), 0); + // central ring is located to the right of the ny_before_ring th sensor + int ny_before_ring = getAttrOrDefault(x_comp_t, _Unicode(ny_before_ring), 0); + // Extra width caused by the ring + // |<--sensors_ydist-->|<--sensors_ydist-->|<-----ring_extra_width------->|<--sensors_ydist-->| + // || || || + // 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); + + 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 + // increment z-layers only at the end, after the last sensor is added + current_y += 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); + } + } + + // now build the layers + for (xml_coll_t li(x_det, _U(layer)); li; ++li) { + xml_comp_t x_layer = li; + xml_comp_t x_barrel = x_layer.child(_U(barrel_envelope)); + xml_comp_t x_layout = x_layer.child(_U(rphi_layout)); + xml_comp_t z_layout = x_layer.child(_U(z_layout)); // Get the element. + int lay_id = x_layer.id(); + string m_nam = x_layer.moduleStr(); + string lay_nam = det_name + _toString(x_layer.id(), "_layer%d"); + Tube lay_tub(x_barrel.inner_r(), x_barrel.outer_r(), x_barrel.z_length() / 2.0); + Volume lay_vol(lay_nam, lay_tub, air); // Create the layer envelope volume. + Position lay_pos(0, 0, getAttrOrDefault(x_barrel, _U(z0), 0.)); + lay_vol.setVisAttributes(description.visAttributes(x_layer.visStr())); + + double phi0 = x_layout.phi0(); // Starting phi of first module. + double phi_tilt = x_layout.phi_tilt(); // Phi tilt of a module. + double rc = x_layout.rc(); // Radius of the module center. + int nphi = x_layout.nphi(); // Number of modules in phi. + double rphi_dr = x_layout.dr(); // The delta radius of every other module. + double phi_incr = (M_PI * 2) / nphi; // Phi increment for one module. + double phic = phi0; // Phi of the module center. + double z0 = z_layout.z0(); // Z position of first module in phi. + double nz = z_layout.nz(); // Number of modules to place in z. + double z_dr = z_layout.dr(); // Radial displacement parameter, of every other module. + + Volume module_env = volumes[m_nam]; + DetElement lay_elt(sdet, lay_nam, lay_id); + Placements& sensVols = sensitives[m_nam]; + + // the local coordinate systems of modules in dd4hep and acts differ + // see http://acts.web.cern.ch/ACTS/latest/doc/group__DD4hepPlugins.html + auto& layerParams = + DD4hepDetectorHelper::ensureExtension(lay_elt); + + for (xml_coll_t lmat(x_layer, _Unicode(layer_material)); lmat; ++lmat) { + xml_comp_t x_layer_material = lmat; + DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_layer_material, layerParams, + "layer_material"); + } + + // Z increment for module placement along Z axis. + // Adjust for z0 at center of module rather than + // the end of cylindrical envelope. + double z_incr = nz > 1 ? (2.0 * z0) / (nz - 1) : 0.0; + // Starting z for module placement along Z axis. + double module_z = -z0; + int module = 1; + + // Loop over the number of modules in phi. + for (int ii = 0; ii < nphi; ii++) { + double dx = z_dr * std::cos(phic + phi_tilt); // Delta x of module position. + double dy = z_dr * std::sin(phic + phi_tilt); // Delta y of module position. + double x = rc * std::cos(phic); // Basic x module position. + double y = rc * std::sin(phic); // Basic y module position. + + // Loop over the number of modules in z. + for (int j = 0; j < nz; j++) { + string module_name = _toString(module, "module%d"); + DetElement mod_elt(lay_elt, module_name, module); + + Transform3D tr(RotationZYX(0, ((M_PI / 2) - phic - phi_tilt), -M_PI / 2), + Position(x, y, module_z)); + + pv = lay_vol.placeVolume(module_env, tr); + pv.addPhysVolID("module", module); + mod_elt.setPlacement(pv); + for (size_t ic = 0; ic < sensVols.size(); ++ic) { + PlacedVolume sens_pv = sensVols[ic]; + DetElement comp_de(mod_elt, std::string("de_") + sens_pv.volume().name(), module); + comp_de.setPlacement(sens_pv); + + auto& comp_de_params = + DD4hepDetectorHelper::ensureExtension(comp_de); + comp_de_params.set("axis_definitions", "XYZ"); + // comp_de.setAttributes(description, sens_pv.volume(), x_layer.regionStr(), x_layer.limitsStr(), + // xml_det_t(xmleles[m_nam]).visStr()); + // + + volSurfaceList(comp_de)->push_back(volplane_surfaces[m_nam][ic]); + } + + /// Increase counters etc. + module++; + // Adjust the x and y coordinates of the module. + x += dx; + y += dy; + // Flip sign of x and y adjustments. + dx *= -1; + dy *= -1; + // Add z increment to get next z placement pos. + module_z += z_incr; + } + phic += phi_incr; // Increment the phi placement of module. + rc += rphi_dr; // Increment the center radius according to dr parameter. + rphi_dr *= -1; // Flip sign of dr parameter. + module_z = -z0; // Reset the Z placement parameter for module. + } + // 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. + lay_elt.setAttributes(description, lay_vol, x_layer.regionStr(), x_layer.limitsStr(), + x_layer.visStr()); + lay_elt.setPlacement(pv); + } + sdet.setAttributes(description, assembly, x_det.regionStr(), x_det.limitsStr(), x_det.visStr()); + assembly.setVisAttributes(description.invisible()); + pv = description.pickMotherVolume(sdet).placeVolume(assembly); + pv.addPhysVolID("system", det_id); // Set the subdetector system ID. + sdet.setPlacement(pv); + return sdet; +} + +//@} +// clang-format off +DECLARE_DETELEMENT(epic_TOFBarrel, create_TOFBarrel) diff --git a/src/BarrelTrackerWithFrame_geo.cpp b/src/BarrelTrackerWithFrame_geo.cpp index e8fa910c0..0d0665279 100644 --- a/src/BarrelTrackerWithFrame_geo.cpp +++ b/src/BarrelTrackerWithFrame_geo.cpp @@ -349,5 +349,4 @@ static Ref_t create_BarrelTrackerWithFrame(Detector& description, xml_h e, Sensi DECLARE_DETELEMENT(epic_BarrelTrackerWithFrame, create_BarrelTrackerWithFrame) DECLARE_DETELEMENT(epic_TrackerBarrel, create_BarrelTrackerWithFrame) DECLARE_DETELEMENT(epic_VertexBarrel, create_BarrelTrackerWithFrame) -DECLARE_DETELEMENT(epic_TOFBarrel, create_BarrelTrackerWithFrame) DECLARE_DETELEMENT(epic_InnerMPGDBarrel, create_BarrelTrackerWithFrame)