diff --git a/compact/tracking/mpgd_barrel.xml b/compact/tracking/mpgd_barrel.xml index 5f33b3643a..0a75fe8e85 100644 --- a/compact/tracking/mpgd_barrel.xml +++ b/compact/tracking/mpgd_barrel.xml @@ -11,12 +11,10 @@ - Inner MPGD tracking layer(s) - - Note: the inner and outer layers are implemented as separate detectors, as they - belong to different ACTS tracking volumes. If this restriction goes away - in the future they could be put together in a single tag. + Inner MPGD tracking layer = CyMBaL + Note: MPGDs come in two distinct flavours: pixel and 2DStrip. + @@ -34,9 +32,9 @@ - FIXME: No support material is here, so fudge factor used to bring material budget to ~0.5% for barrel + FIXME: No support material is here, so fudge factor used to bring material budget to ~0.5% for barrel. - FIXME: No definite plan for connections/services to the inner sectors yet, so guess value + FIXME: No definite plan for connections/services to the inner sectors yet, so guess value. ...Dimensions @@ -51,6 +49,8 @@ In principle, stave count should not be independenty specified: it is fixed by above intrinsic parameters + + @@ -82,7 +82,7 @@ - + @@ -96,8 +96,8 @@ @@ -120,19 +120,11 @@ 768 strips in phi yield a segmentation of 2pi/8/768 ~= 1mrad. Which in turn, yields ~160 um in resolution. - Strip segmentations ("strip" field !=0) are not used at simulation time but only in digitization at reconstruction time, see digitization class, "MPGDTrackerDigi". - - - - - - - - - - + + - system:8,layer:4,module:12,sensor:2,strip:30:2,phi:-16,z:-16 + "strip" field is ineffective... but required. + system:8,layer:4,module:12,sensor:2,strip:28:4,phi:-16,z:-16 diff --git a/compact/tracking/mpgd_barrel_2DStrip.xml b/compact/tracking/mpgd_barrel_2DStrip.xml new file mode 100644 index 0000000000..7d3801d478 --- /dev/null +++ b/compact/tracking/mpgd_barrel_2DStrip.xml @@ -0,0 +1,156 @@ + + + + + + Title: Micro Pattern Gas Detectors + Author: @mposik1983 + Status: development + Version: 2.2 + + + + + Inner MPGD tracking layer (=CyMBaL) with realistic pitch + Note: MPGDs come in two distinct flavours: pixel and 2DStrip. + + + + + + Intrinsic parameters... + ...Thicknesses + + + + + + + + + + + + + FIXME: No support material is here, so fudge factor used to bring material budget to ~0.5% for barrel. + + FIXME: No definite plan for connections/services to the inner sectors yet, so guess value. + + 2DStrip: Multiple Sensitive Volumes. => Subdivide GasGap: 3 thin slices (pixel and (p|n)strips) at the centre, enclosed by 2 thick radiators. + + + ...Dimensions + + + + + + + ...Positioning + + + + In principle, stave count should not be independenty specified: it is fixed by above intrinsic parameters + + + + + "MMModuleWidth" is to be understood as the width at the inner wall. Width expands as one moves away from it. + + + + + Strip layout: Pitch is derived from size and # of strips, assuming sensitive area to fill all available space. + + + + + + + + + + + + + + Models describe the overlapping of staves along phi, either (I) via offsets or (II) via distinct radii ("rmin1/2"). Solution (II) is only partially implemented: would require FOUR distinct "radius" in the readout segmentation. Note that in any case "rsensor" must be set equal the "radius" of the corresponding segmentation and is used internally to double-check the consistency between the stack of "module_component" and the segmentation "radius". + + + + + This includes cables => Kapton as a material is overoptimistic. + Going from the inside (sensitive) side to the readout side + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 768 strips in phi yield a segmentation of 2pi/8/768 ~= 1mrad. Which in turn, yields ~160 um in resolution. + + + + + + + + + + + + + + + + + + system:8,layer:4,module:12,sensor:2,strip:28:4,phi:-16,z:-16 + + + + diff --git a/compact/tracking/mpgd_outerbarrel.xml b/compact/tracking/mpgd_outerbarrel.xml index 09f297b644..944ef7ea25 100644 --- a/compact/tracking/mpgd_outerbarrel.xml +++ b/compact/tracking/mpgd_outerbarrel.xml @@ -10,6 +10,12 @@ + + Outer MPGD tracking layer + Note: MPGDs come in two distinct flavours: pixel and 2DStrip. + + + Frames @@ -25,7 +31,8 @@ - + + Layer parameters @@ -57,7 +64,8 @@ thickness="MPGDOuterBarrelDriftGap_thickness" vis="TrackerMPGDGasVis" offset="0" - length="MPGDOuterBarrelModule_length - MPGDOuterBarrelModule_PCB_offset"/> + length="MPGDOuterBarrelModule_Inset_length" + key="0"/> + length="MPGDOuterBarrelModule_Inset_length"/> + length="MPGDOuterBarrelModule_Inset_length"/> + length="MPGDOuterBarrelModule_Inset_length"/> + length="MPGDOuterBarrelModule_Inset_length"/> + length="MPGDOuterBarrelModule_Inset_length"/> + length="MPGDOuterBarrelModule_Inset_length"/> + length="MPGDOuterBarrelModule_Inset_length"/> + length="MPGDOuterBarrelModule_Inset_length"/> + length="MPGDOuterBarrelModule_Inset_length"/> - - Strip segmentations ("strip" field !=0) are not used at simulation time but only in digitization at reconstruction time, see digitization class, "MPGDTrackerDigi". - - - - - system:8,layer:4,module:12,sensor:2,strip:30:2,u:-16,v:-16 + + "strip" field is ineffective... but required. + system:8,layer:4,module:12,sensor:2,strip:28:4,x:-16,y:-16 diff --git a/compact/tracking/mpgd_outerbarrel_2DStrip.xml b/compact/tracking/mpgd_outerbarrel_2DStrip.xml new file mode 100644 index 0000000000..3adbb0263b --- /dev/null +++ b/compact/tracking/mpgd_outerbarrel_2DStrip.xml @@ -0,0 +1,206 @@ + + + + + + Title: Micro-Pattern Gas Detector Planar Barrel Layer + Author: @mposik1983 + Status: development + Version: 1.2 + + + + + Outer MPGD tracking layer with realistic pitch + Note: MPGDs come in two distinct flavours: pixel and 2DStrip. + + + + Frames + + + + Module constants + + + + + + + + + + + + + + + Layer parameters + + + + + + + + + + 2DStrip: Multiple Sensitive Volumes. => Subdivide DriftGap: 3 thin slices (pixel and (p|n)strips) at the centre, enclosed by 2 thick radiators. + + + Strip layout: Pitch and #channels are taken from CM of 2025 @ Frascati. + #channels is used by the MPGD factory to set up the digitization configuration. + + + + + + + + + + + MPGD DIRC module components + + + + + + + + + + + + + + + + + + + + + Frame width gets subtracted from the gas module volumes + see src/BarrelPlanarMPGDTracker_geo.cpp + + + + + + + Layout for MPGD DIRC layers + + + + + + + + + + + + + + Strip segmentations ("strip" field !=0) are not used at simulation time but only in digitization at reconstruction time, see digitization class, "MPGDTrackerDigi". + + + + + + + system:8,layer:4,module:12,sensor:2,strip:28:4,u:-16,v:-16 + + + + + + + + + + + + + diff --git a/configurations/craterlake_tracking_2DStrip.yml b/configurations/craterlake_tracking_2DStrip.yml new file mode 100644 index 0000000000..bb8e21b631 --- /dev/null +++ b/configurations/craterlake_tracking_2DStrip.yml @@ -0,0 +1,21 @@ +features: + beampipe: + fields: + marco: + tracking: + definitions_craterlake: + vertex_barrel: + vertex_barrel_support: + silicon_barrel: + mpgd_barrel_2DStrip: + support_service_craterlake: + mpgd_outerbarrel_2DStrip: + mpgd_forward_endcap: + mpgd_backward_endcap: + silicon_disks: + tof_barrel: + tof_endcap: + far_forward: + default: + far_backward: + default: diff --git a/src/BarrelPlanarMPGDTracker_geo.cpp b/src/BarrelPlanarMPGDTracker_geo.cpp index 1e13bf7c0b..60f9f79ff1 100644 --- a/src/BarrelPlanarMPGDTracker_geo.cpp +++ b/src/BarrelPlanarMPGDTracker_geo.cpp @@ -36,19 +36,36 @@ using namespace dd4hep::rec; * and surrounds the rectangular perimeter) * - Detector is setup as a "tracker" so we can use the hits * + * - Two distinct versions of ".xml" are covered: + * I) Single Sensitive Volume (which name is then "DriftGap"): "eicrecon" is to + * be executed while setting bit 0x2 of option "MPGD:SiFactoryPattern". + * II) Multiple Sensitive Volume: Bit 0x2 of above-mentioned option not set. + * */ static Ref_t create_BarrelPlanarMPGDTracker_geo(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; + + Volume* volume; + // Sensitive volumes and associated surfaces + // - There can be either one or five. + int sensitiveVolumeSet = 5; // 1: single volume, 5: 5 volumes, -1: error + vector sensitives; + vector volplane_surfaces; + PlacedVolume pv; + + //#define DEBUG_BarrelPlanarMPGDTracker +#ifdef DEBUG_BarrelPlanarMPGDTracker + // TEMPORARILY INCREASE VERBOSITY level for debugging purposes + PrintLevel priorPrintLevel = printLevel(); + setPrintLevel(DEBUG); +#endif + dd4hep::xml::Dimension dimensions(x_det.dimensions()); xml_dim_t mpgd_pos = x_det.position(); Assembly assembly(det_name); @@ -66,277 +83,335 @@ static Ref_t create_BarrelPlanarMPGDTracker_geo(Detector& description, xml_h e, "boundary_material"); } - map> module_thicknesses; sens.setType("tracker"); - // 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(); + // ********** MODULE + // ***** ONE AND ONLY ONE MODULE + xml_coll_t modules(x_det, _U(module)); + if (modules.size() != 1) { + // Present detector constructor can only handle ONE tag + printout(ERROR, "BarrelPlanarMPGDTracker_geo", "Number of modules = %u. Must be = 1", + modules.size()); + throw runtime_error("Logics error in building modules."); + } + xml_comp_t x_mod = modules; + string m_nam = x_mod.nameStr(); - if (volumes.find(m_nam) != volumes.end()) { - printout(ERROR, "BarrelPlanarMPGDTracker_geo", - 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 = 0; + double total_thickness = 0; - 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); + volume = &m_vol; + m_vol.setVisAttributes(description, x_mod.visStr()); + + // Optional module frame. + // frame is 4 bars around the perimeter of the rectangular module. The frame will eat the + // overlapping module area + // + // ___ + // |___| <-- example module cross section (x-y plane), frame is flush with the + // bottom of the module and protrudes on the top if needed + + // Get frame width, as it impacts the main module for being built. We + // construct the actual frame structure later (once we know the module width) + double frame_width = 0; + if (x_mod.hasChild(_U(frame))) { + xml_comp_t m_frame = x_mod.child(_U(frame)); + frame_width = m_frame.width(); + } - // 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, x_mod.visStr()); - - // Optional module frame. - // frame is 4 bars around the perimeter of the rectangular module. The frame will eat the - // overlapping module area + double thickness_so_far = 0.0; + double thickness_sum = -total_thickness / 2.0; + double max_component_width = 0; + double max_component_length = 0; + double gas_thickness = 0.0; + // Pattern of Multiple Sensitive Volumes + // - In order to have one sensitive component per strip coordinate (and + // accessorily, some extras), the "DriftGap" is subdivided into subVolumes. + int nSensitives = 0; + for (xml_coll_t mci(x_mod, _U(module_component)); mci; ++mci, ++ncomponents) { + xml_comp_t x_comp = mci; + string c_nam = _toString(ncomponents, "component%d"); + string comp_name = x_comp.nameStr(); + double comp_thickness = x_comp.thickness(); + double box_width = x_comp.width(); + double box_length = x_comp.length(); + Box c_box; + // Since MPGD frames are layed over the MPGD foils, the foil material is pressent under the frame as well. + // The gas volumes are not present under the frames, so our frames must eat only the gas module areas // - // ___ - // |___| <-- example module cross section (x-y plane), frame is flush with the - // bottom of the module and protrudes on the top if needed - - // Get frame width, as it impacts the main module for being built. We - // construct the actual frame structure later (once we know the module width) - double frame_width = 0; - if (x_mod.hasChild(_U(frame))) { - xml_comp_t m_frame = x_mod.child(_U(frame)); - frame_width = m_frame.width(); + // ------------------- MPGD foil + // -- -- Frame + // -- gas volume -- Frame + // -- -- Frame + // ------------------- MPGD foil + + // Look for gas modules to subtract frame thickness from + // FIXME: these module names are hard coded for now. Should find + // a way to set an attribute via the module tag to flag what components + // need to have frame thickness subtracted. + bool isDriftGap = comp_name == "DriftGap" || + /* */ comp_name.find("ThinGap") != std::string::npos || + /* */ comp_name.find("Radiator") != std::string::npos; + if (isDriftGap || comp_name == "WindowGasGap") { + box_width = x_comp.width() - 2.0 * frame_width; + box_length = x_comp.length() - 2.0 * frame_width; + max_component_width = box_width; + max_component_length = box_length; + gas_thickness += comp_thickness; + c_box = {box_width / 2, box_length / 2, comp_thickness / 2}; + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "gas: %s", comp_name.c_str()); + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_width: %f", box_width); + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_length: %f", box_length); + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_thickness: %f", comp_thickness); + } else { + c_box = {x_comp.width() / 2, x_comp.length() / 2, comp_thickness / 2}; + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "Not gas: %s", comp_name.c_str()); + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_width: %f", x_comp.width()); + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_length: %f", x_comp.length()); + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_thickness: %f", comp_thickness); } + Volume c_vol{c_nam, c_box, description.material(x_comp.materialStr())}; - double thickness_so_far = 0.0; - double thickness_sum = -total_thickness / 2.0; - double max_component_width = 0; - double max_component_length = 0; - double gas_thickness = 0.0; - for (xml_coll_t mci(x_mod, _U(module_component)); mci; ++mci, ++ncomponents) { - xml_comp_t x_comp = mci; - string c_nam = _toString(ncomponents, "component%d"); - string comp_name = x_comp.nameStr(); - - double box_width = x_comp.width(); - double box_length = x_comp.length(); - Box c_box; - // Since MPGD frames are layed over the MPGD foils, the foil material is pressent under the frame as well. - // The gas volumes are not present under the frames, so our frames must eat only the gas module areas - // - // ------------------- MPGD foil - // -- -- Frame - // -- gas volume -- Frame - // -- -- Frame - // ------------------- MPGD foil - - // Look for gas modules to subtract frame thickness from - // FIXME: these module names are hard coded for now. Should find - // a way to set a arribut via the moduel tag to flag what components - // need to have frame thickness subtracted. - if ((comp_name == "DriftGap" || comp_name == "WindowGasGap")) { - box_width = x_comp.width() - 2.0 * frame_width; - box_length = x_comp.length() - 2.0 * frame_width; - max_component_width = box_width; - max_component_length = box_length; - gas_thickness += x_comp.thickness(); - c_box = {box_width / 2, box_length / 2, x_comp.thickness() / 2}; - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "gas: %s", comp_name.c_str()); - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_width: %f", box_width); - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_length: %f", box_length); - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_thickness: %f", x_comp.thickness()); - } else { - c_box = {x_comp.width() / 2, x_comp.length() / 2, x_comp.thickness() / 2}; - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "Not gas: %s", comp_name.c_str()); - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_width: %f", x_comp.width()); - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_length: %f", x_comp.length()); - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_thickness: %f", - x_comp.thickness()); - } - Volume c_vol{c_nam, c_box, description.material(x_comp.materialStr())}; - - c_vol.setRegion(description, x_comp.regionStr()); - c_vol.setLimitSet(description, x_comp.limitsStr()); - c_vol.setVisAttributes(description, x_comp.visStr()); - - pcb_feb_ext = x_comp.offset(); + c_vol.setRegion(description, x_comp.regionStr()); + c_vol.setLimitSet(description, x_comp.limitsStr()); + c_vol.setVisAttributes(description, x_comp.visStr()); - pv = m_vol.placeVolume( - c_vol, Position(0, -pcb_feb_ext / 2.0, thickness_sum + x_comp.thickness() / 2.0)); + pcb_feb_ext = x_comp.offset(); - 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.); + pv = m_vol.placeVolume(c_vol, + Position(0, -pcb_feb_ext / 2.0, thickness_sum + comp_thickness / 2.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]; + if (x_comp.isSensitive()) { + // ***** SENSITIVE VOLUME + if (nSensitives >= 5) { + sensitiveVolumeSet = -1; + break; + } + pv.addPhysVolID("sensor", sensor_number); + // StripID. Single Sensitive Volume? + if (comp_name == "DriftGap") { + if (nSensitives != 0) { + sensitiveVolumeSet = -1; + break; + } + sensitiveVolumeSet = 1; + } + int strip_id = x_comp.key(); + pv.addPhysVolID("strip", strip_id); + c_vol.setSensitiveDetector(sens); + sensitives.push_back(pv); + + // -------- create a measurement plane for the tracking surface attached to the sensitive volume ----- + Vector3D u(-1., 0., 0.); + Vector3D v(0., -1., 0.); + Vector3D n(0., 0., 1.); + + // Compute the inner (i.e. thickness until mid-sensitive-volume) and + // outer (from mid-sensitive-volume to top) + // thicknesses that need to be assigned to the tracking surface + // depending on wether the support is above or below the sensor. + double inner_thickness, outer_thickness; + if (sensitiveVolumeSet == 1) { + inner_thickness = thickness_so_far + comp_thickness / 2; + outer_thickness = total_thickness - thickness_so_far - comp_thickness / 2; + } else if (nSensitives == 0) { + inner_thickness = thickness_so_far + comp_thickness / 2; + outer_thickness = comp_thickness / 2; + } else if (nSensitives == 4) { + inner_thickness = comp_thickness / 2; + outer_thickness = total_thickness - thickness_so_far - comp_thickness / 2; + } else { + inner_thickness = outer_thickness = comp_thickness / 2; + } + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "Sensitive surface @ R = %.4f (%.4f,%.4f) cm", + (thickness_sum + comp_thickness / 2) / cm, inner_thickness / cm, + outer_thickness / cm); - SurfaceType type(rec::SurfaceType::Sensitive); + SurfaceType type(rec::SurfaceType::Sensitive); - VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n); //,o ) ; - volplane_surfaces[m_nam].push_back(surf); - } - thickness_sum += x_comp.thickness(); - thickness_so_far += x_comp.thickness(); - } - // Now add-on the frame - if (x_mod.hasChild(_U(frame))) { - xml_comp_t m_frame = x_mod.child(_U(frame)); - double frame_thickness = getAttrOrDefault(m_frame, _U(thickness), total_thickness); - - Box lframe_box{m_frame.width() / 2.0, (max_component_length + 2.0 * m_frame.width()) / 2.0, - frame_thickness / 2.0}; - Box rframe_box{m_frame.width() / 2.0, (max_component_length + 2.0 * m_frame.width()) / 2.0, - frame_thickness / 2.0}; - Box tframe_box{max_component_width / 2.0, m_frame.width() / 2.0, frame_thickness / 2.0}; - Box bframe_box{max_component_width / 2.0, m_frame.width() / 2.0, frame_thickness / 2.0}; - - // Keep track of frame with so we can adjust the module bars appropriately - - Volume lframe_vol{"left_frame", lframe_box, description.material(m_frame.materialStr())}; - Volume rframe_vol{"right_frame", rframe_box, description.material(m_frame.materialStr())}; - Volume tframe_vol{"top_frame", tframe_box, description.material(m_frame.materialStr())}; - Volume bframe_vol{"bottom_frame", bframe_box, description.material(m_frame.materialStr())}; - - lframe_vol.setVisAttributes(description, m_frame.visStr()); - rframe_vol.setVisAttributes(description, m_frame.visStr()); - tframe_vol.setVisAttributes(description, m_frame.visStr()); - bframe_vol.setVisAttributes(description, m_frame.visStr()); - - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "frame_thickness: %f", frame_thickness); - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "total_thickness: %f", total_thickness); - printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "frame_thickness + total_thickness: %f", - frame_thickness + total_thickness); - - m_vol.placeVolume(lframe_vol, Position(frame_width / 2.0 + max_component_width / 2, 0.0, - frame_thickness / 2.0 - total_thickness / 2.0 - - gas_thickness / 2.0)); - m_vol.placeVolume(rframe_vol, Position(-frame_width / 2.0 - max_component_width / 2.0, 0.0, - frame_thickness / 2.0 - total_thickness / 2.0 - - gas_thickness / 2.0)); - m_vol.placeVolume(tframe_vol, Position(0.0, frame_width / 2.0 + max_component_length / 2, - frame_thickness / 2.0 - total_thickness / 2.0 - - gas_thickness / 2.0)); - m_vol.placeVolume(bframe_vol, Position(0.0, -frame_width / 2.0 - max_component_length / 2.0, - frame_thickness / 2.0 - total_thickness / 2.0 - - gas_thickness / 2.0)); + VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n); //,o ) ; + volplane_surfaces.push_back(surf); + nSensitives++; } + thickness_sum += comp_thickness; + thickness_so_far += comp_thickness; + } + if (sensitiveVolumeSet < 0 || sensitiveVolumeSet != nSensitives) { + printout(ERROR, "BarrelPlanarMPGDTracker_geo", + "Invalid set of Sensitive Volumes: it's either one (named \"DriftGap\") or 5"); + throw runtime_error("Logics error in building modules."); + } + // Now add-on the frame + if (x_mod.hasChild(_U(frame))) { + xml_comp_t m_frame = x_mod.child(_U(frame)); + double frame_thickness = getAttrOrDefault(m_frame, _U(thickness), total_thickness); + + Box lframe_box{m_frame.width() / 2.0, (max_component_length + 2.0 * m_frame.width()) / 2.0, + frame_thickness / 2.0}; + Box rframe_box{m_frame.width() / 2.0, (max_component_length + 2.0 * m_frame.width()) / 2.0, + frame_thickness / 2.0}; + Box tframe_box{max_component_width / 2.0, m_frame.width() / 2.0, frame_thickness / 2.0}; + Box bframe_box{max_component_width / 2.0, m_frame.width() / 2.0, frame_thickness / 2.0}; + + // Keep track of frame with so we can adjust the module bars appropriately + + Volume lframe_vol{"left_frame", lframe_box, description.material(m_frame.materialStr())}; + Volume rframe_vol{"right_frame", rframe_box, description.material(m_frame.materialStr())}; + Volume tframe_vol{"top_frame", tframe_box, description.material(m_frame.materialStr())}; + Volume bframe_vol{"bottom_frame", bframe_box, description.material(m_frame.materialStr())}; + + lframe_vol.setVisAttributes(description, m_frame.visStr()); + rframe_vol.setVisAttributes(description, m_frame.visStr()); + tframe_vol.setVisAttributes(description, m_frame.visStr()); + bframe_vol.setVisAttributes(description, m_frame.visStr()); + + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "frame_thickness: %f", frame_thickness); + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "total_thickness: %f", total_thickness); + printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "frame_thickness + total_thickness: %f", + frame_thickness + total_thickness); + + m_vol.placeVolume( + lframe_vol, Position(frame_width / 2.0 + max_component_width / 2, 0.0, + frame_thickness / 2.0 - total_thickness / 2.0 - gas_thickness / 2.0)); + m_vol.placeVolume( + rframe_vol, Position(-frame_width / 2.0 - max_component_width / 2.0, 0.0, + frame_thickness / 2.0 - total_thickness / 2.0 - gas_thickness / 2.0)); + m_vol.placeVolume( + tframe_vol, Position(0.0, frame_width / 2.0 + max_component_length / 2, + frame_thickness / 2.0 - total_thickness / 2.0 - gas_thickness / 2.0)); + m_vol.placeVolume( + bframe_vol, Position(0.0, -frame_width / 2.0 - max_component_length / 2.0, + frame_thickness / 2.0 - total_thickness / 2.0 - gas_thickness / 2.0)); } + // ********** LAYER + // ***** ONE AND ONLY ONE LAYER + xml_coll_t li(x_det, _U(layer)); + if (li.size() != 1) { + printout(ERROR, "BarrelPlanarMPGDTracker_geo", "Number of layers = %d. Must be = 1", + (int)li.size()); + throw runtime_error("Logics error in building modules."); + } // build the layers the modules will be arranged around - for (xml_coll_t li(x_det, _U(layer)); li; ++li) { - xml_comp_t x_layer = li; - xml_comp_t x_layout = x_layer.child(_U(rphi_layout)); - xml_comp_t z_layout = x_layer.child(_U(z_layout)); - int lay_id = x_layer.id(); - string m_nam = x_layer.moduleStr(); - string lay_nam = det_name + _toString(x_layer.id(), "_layer%d"); - xml_comp_t envelope_tolerance = x_layer.child(_Unicode(envelope_tolerance), false); - double envelope_r_min = 0; - double envelope_r_max = 0; - double envelope_z_min = 0; - double envelope_z_max = 0; - if (envelope_tolerance) { - envelope_r_min = getAttrOrDefault(envelope_tolerance, _Unicode(r_min), 0); - envelope_r_max = getAttrOrDefault(envelope_tolerance, _Unicode(r_max), 0); - envelope_z_min = getAttrOrDefault(envelope_tolerance, _Unicode(z_min), 0); - envelope_z_max = getAttrOrDefault(envelope_tolerance, _Unicode(z_max), 0); - } + xml_comp_t x_layer = li; + xml_comp_t x_layout = x_layer.child(_U(rphi_layout)); + xml_comp_t z_layout = x_layer.child(_U(z_layout)); + int lay_id = x_layer.id(); + string lay_nam = det_name + _toString(x_layer.id(), "_layer%d"); + xml_comp_t envelope_tolerance = x_layer.child(_Unicode(envelope_tolerance), false); + double envelope_r_min = 0; + double envelope_r_max = 0; + double envelope_z_min = 0; + double envelope_z_max = 0; + if (envelope_tolerance) { + envelope_r_min = getAttrOrDefault(envelope_tolerance, _Unicode(r_min), 0); + envelope_r_max = getAttrOrDefault(envelope_tolerance, _Unicode(r_max), 0); + envelope_z_min = getAttrOrDefault(envelope_tolerance, _Unicode(z_min), 0); + envelope_z_max = getAttrOrDefault(envelope_tolerance, _Unicode(z_max), 0); + } - double phi0 = x_layout.phi0(); // starting phi of first module - double phi_tilt = x_layout.phi_tilt(); // Phi tilit of module - double rc = x_layout.rc(); // Radius of the module - 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 = (2 * M_PI) / nphi; // Phi increment for one module - double phic = phi0; // Phi of the module - int nz = 2; // Number of modules placed in z - double z_dr = z_layout.dr(); // Radial offest of modules in z - double z0 = z_layout.z0(); // Sets how much overlap in z the nz modules have - - Assembly layer_assembly(lay_nam); - Volume module_env = volumes[m_nam]; - DetElement lay_elt(sdet, lay_nam, lay_id); - Placements& sensVols = sensitives[m_nam]; - auto& layerParams = - DD4hepDetectorHelper::ensureExtension(lay_elt); - - pv = assembly.placeVolume(layer_assembly); - pv.addPhysVolID("layer", lay_id); - lay_elt.setPlacement(pv); - - int module = 1; - // loop over the modules in phi - for (int ii = 0; ii < nphi; ii++) { - double xc = rc * std::cos(phic); // Basic x position of module - double yc = rc * std::sin(phic); // Basic y position of module - double dx = z_dr * std::cos(phic + phi_tilt); // Deta x of module position - double dy = z_dr * std::sin(phic + phi_tilt); // Deta y of module position - // loop over the 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); - double mod_z = 0.5 * dimensions.length(); - double z_placement = mod_z - 0.5 * pcb_feb_ext - - j * (nz * mod_z - pcb_feb_ext); // z location for module placement - double z_offset = - z_placement > 0 - ? -z0 / 2.0 - : z0 / 2.0; // determine the amount of overlap in z the z nz modules have - - Transform3D tr( - RotationZYX(0, ((M_PI / 2) - phic - phi_tilt), -M_PI / 2) * RotationZ(j * M_PI), - Position( - xc, yc, - mpgd_pos.z() + z_placement + - z_offset)); //RotZYX rotates planes around azimuth, RotZ flips plane so pcb_feb_ext is facing endcaps - - pv = layer_assembly.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); - } - // increas module counter - module++; - // adjust x and y coordinates - xc += dx; - yc += dy; + double phi0 = x_layout.phi0(); // starting phi of first module + double phi_tilt = x_layout.phi_tilt(); // Phi tilt of module + double rc = x_layout.rc(); // Radius of the module + 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 = (2 * M_PI) / nphi; // Phi increment for one module + double phic = phi0; // Phi of the module + int nz = 2; // Number of modules placed in z + double z_dr = z_layout.dr(); // Radial offest of modules in z + double z0 = z_layout.z0(); // Sets how much overlap in z the nz modules have + + Assembly layer_assembly(lay_nam); + Volume module_env = *volume; + DetElement lay_elt(sdet, lay_nam, lay_id); + auto& layerParams = + DD4hepDetectorHelper::ensureExtension(lay_elt); + + pv = assembly.placeVolume(layer_assembly); + pv.addPhysVolID("layer", lay_id); + lay_elt.setPlacement(pv); + + int module = 0; + // loop over the modules in phi + for (int ii = 0; ii < nphi; ii++) { + double xc = rc * std::cos(phic); // Basic x position of module + double yc = rc * std::sin(phic); // Basic y position of module + 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 + // loop over the modules in z + for (int j = 0; j < nz; j++) { + string module_name = _toString(module, "module%02d"); + DetElement mod_elt(lay_elt, module_name, module); + double mod_z = 0.5 * dimensions.length(); + double z_placement = mod_z - 0.5 * pcb_feb_ext - + j * (nz * mod_z - pcb_feb_ext); // z location for module placement + double z_offset = + z_placement > 0 ? -z0 / 2.0 + : z0 / 2.0; // determine the amount of overlap in z the z nz modules have + + Transform3D tr( + RotationZYX(0, ((M_PI / 2) - phic - phi_tilt), -M_PI / 2) * RotationZ(j * M_PI), + Position( + xc, yc, + mpgd_pos.z() + z_placement + + z_offset)); //RotZYX rotates planes around azimuth, RotZ flips plane so pcb_feb_ext is facing endcaps + + pv = layer_assembly.placeVolume(module_env, tr); + pv.addPhysVolID("module", module); + mod_elt.setPlacement(pv); + for (int iSensitive = 0; iSensitive < sensitiveVolumeSet; iSensitive++) { + // ***** SENSITIVE COMPONENTS + PlacedVolume& sens_pv = sensitives[iSensitive]; + int de_id = nphi * nz * iSensitive + module; + DetElement comp_de(mod_elt, + std::string("de_") + sens_pv.volume().name() + _toString(de_id, "%02d"), + de_id); + comp_de.setPlacement(sens_pv); + auto& comp_de_params = + DD4hepDetectorHelper::ensureExtension(comp_de); + comp_de_params.set("axis_definitions", "XYZ"); + volSurfaceList(comp_de)->push_back(volplane_surfaces[iSensitive]); } - // increment counters - phic += phi_incr; - rc += rphi_dr; - } - layer_assembly->GetShape()->ComputeBBox(); - layerParams.set("envelope_r_min", envelope_r_min); - layerParams.set("envelope_r_max", envelope_r_max); - layerParams.set("envelope_z_min", envelope_z_min); - layerParams.set("envelope_z_max", envelope_z_max); - - 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"); + // increas module counter + module++; + // adjust x and y coordinates + xc += dx; + yc += dy; } + // increment counters + phic += phi_incr; + rc += rphi_dr; + } + layer_assembly->GetShape()->ComputeBBox(); + layerParams.set("envelope_r_min", envelope_r_min); + layerParams.set("envelope_r_max", envelope_r_max); + layerParams.set("envelope_z_min", envelope_z_min); + layerParams.set("envelope_z_max", envelope_z_max); + + 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"); } 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); + +#ifdef DEBUG_BarrelPlanarMPGDTracker + // Reset initial print level before exiting + setPrintLevel(priorPrintLevel); +#endif + return sdet; } diff --git a/src/MPGDCylinderBarrelTracker_geo.cpp b/src/MPGDCylinderBarrelTracker_geo.cpp index 2700f887ce..6c3a8e343a 100644 --- a/src/MPGDCylinderBarrelTracker_geo.cpp +++ b/src/MPGDCylinderBarrelTracker_geo.cpp @@ -41,6 +41,11 @@ using ROOT::Math::XYVector; * - Several models of modules, with each a distinct radius of curvature * but a single XML and a single . * + * - Two distinct versions of ".xml" are covered: + * I) Single Sensitive Volume (which name is then "GasGap"): "eicrecon" is to + * be executed while setting bit 0x1 of option "MPGD:SiFactoryPattern". + * II) Multiple Sensitive Volume: Bit 0x1 of above-mentioned option not set. + * * \code * \endcode * @@ -56,8 +61,11 @@ static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e, DetElement sdet(det_name, det_id); vector volumes; - vector sensitives; - vector volplane_surfaces; + // Sensitive volumes and associated surfaces + // - There can be either one or five. + int sensitiveVolumeSet = 5; // 1: single volume, 5: 5 volumes, -1: error + vector sensitives[5]; + vector volplane_surfaces[5]; PlacedVolume pv; @@ -120,28 +128,28 @@ static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e, double barrel_z0 = getAttrOrDefault(x_barrel, _U(z0), 0.); // ***** LAYOUTS xml_comp_t x_layout = x_layer.child(_U(rphi_layout)); - double phi0 = x_layout.phi0(); // Starting phi of first module. - xml_comp_t z_layout = x_layer.child(_U(z_layout)); // Get the element. + double phi0 = x_layout.phi0(); // Starting phi of first module. + xml_comp_t z_layout = x_layer.child(_U(z_layout)); double z_gap = z_layout.gap(); - // ***** UNVALID LAYOUT PROPERTIES + // ***** INVALID LAYOUT PROPERTIES // # of staves (along phi) and sectors (along z) are now derived from stave // width and sector length. Used to be specified directly. In order to remind // the user this is no longer the case, let's forbid the use of the // corresponding (and a few more) tags. - const int nUnvalids = 4; - const xml::Tag_t unvalidTags[nUnvalids] = {_U(phi_tilt), _U(nphi), _U(rc), _U(dr)}; - for (int uv = 0; uv < nUnvalids; uv++) { - if (x_barrel.hasChild(unvalidTags[uv])) { + const int nInvalids = 4; + const xml::Tag_t invalidTags[nInvalids] = {_U(phi_tilt), _U(nphi), _U(rc), _U(dr)}; + for (int uv = 0; uv < nInvalids; uv++) { + if (x_barrel.hasChild(invalidTags[uv])) { const string tag = _U(nphi); printout(ERROR, "MPGDCylinderBarrelTracker", - "Layer \"%s\": Unvalid property \"%s\" in \"rphi_layout\"", m_nam.c_str(), + "Layer \"%s\": Invalid property \"%s\" in \"rphi_layout\"", m_nam.c_str(), tag.c_str()); throw runtime_error("Logics error in building modules."); } } // ***** MODELS - // Model = set of two radii of curvature used alternatingly for staves + // Model = set of two radii of curvature used alternatively for staves // + an offset to be applied radially. // - The offset may be null if the two radii are different enough that // staves adjacent along phi do not overlap. @@ -160,7 +168,8 @@ static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e, double offset; int io; } StaveModel; - StaveModel staveModels[4]; + const int nSectors = 4; + StaveModel staveModels[nSectors]; int sector2Models[2][2]; xml_coll_t mi(x_mod, _U(model)); if (mi.size() != 2) { @@ -303,7 +312,7 @@ static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e, double dPhi = acos(vEdge0.Dot(vEdge1) / Mag0 / Mag1); RMx += total_thickness; if (io == 1) { - RMn -= service_thickness; // Outer sector: acount for services to inner sector + RMn -= service_thickness; // Outer sector: account for services to inner sector outerPhiSuperpos = dPhi; } printout( @@ -393,9 +402,12 @@ static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e, } // ********** LOOP OVER COMPONENTS - double comp_rmin = stave_rmin; - double thickness_so_far = 0; - xml_comp_t* sensitiveComp = 0; + double comp_rmin = stave_rmin; + double thickness_so_far = 0; + // Pattern of Multiple Sensitive Volumes + // - The "GasGap" may be subdivided into SUBVOLUMES (this order to have one + // sensitive component per strip coordinate (and accessorily, some extras). + int nSensitives = 0; for (xml_coll_t mci(x_mod, _U(module_component)); mci; ++mci) { xml_comp_t x_comp = mci; const string c_nam = x_comp.nameStr(); @@ -408,38 +420,58 @@ static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e, c_vol.setVisAttributes(description, x_comp.visStr()); if (x_comp.isSensitive()) { // ***** SENSITIVE VOLUME - if (sensitiveComp) { - printout(ERROR, "MPGDCylinderBarrelTracker", - "Component \"%s\": 2nd sensitive component in module \"%s\" (1st is \"%s\"). " - "One only allowed.", - c_nam.c_str(), m_nam.c_str(), sensitiveComp->nameStr().c_str()); - throw runtime_error("Logics error in building modules."); + if (nSensitives >= 5) { + sensitiveVolumeSet = -1; + break; } - sensitiveComp = &x_comp; // TODO: Add second sensitive pv.addPhysVolID("sensor", sensor_number); + // StripID. Single Sensitive Volume? + if (c_nam == "GasGap") { + if (nSensitives != 0) { + sensitiveVolumeSet = -1; + break; + } + sensitiveVolumeSet = 1; + } + int strip_id = x_comp.key(); + pv.addPhysVolID("strip", strip_id); c_vol.setSensitiveDetector(sens); - sensitives.push_back(pv); + sensitives[nSensitives].push_back(pv); // -------- create a measurement plane for the tracking surface attached to the sensitive volume ----- Vector3D u(-1., 0., 0.); Vector3D v(0., -1., 0.); Vector3D n(0., 0., 1.); + if (strip_id == 0) { + // Consistency(+/-1um) check: segmentation consistent w/ stack of module components? + double rXCheck = comp_rmin + comp_thickness / 2; + if (fabs(staveModel.rsensor - rXCheck) > .0001 / cm) { + printout(ERROR, "MPGDCylinderBarrelTracker", + "Sensitive Component \"%s\" of StaveModel #%d,\"%s\": rsensor(%.4f cm) != " + "radius @ sensitive surface(%.4f cm)", + c_nam.c_str(), iSM, staveModel.name.c_str(), staveModel.rsensor / cm, + rXCheck / cm); + throw runtime_error("Logics error in building modules."); + } + } + // Compute the inner (i.e. thickness until mid-sensitive-volume) and // outer (from mid-sensitive-volume to top) // thicknesses that need to be assigned to the tracking surface - // depending on wether the support is above or below the sensor (!?) - double inner_thickness = thickness_so_far + comp_thickness / 2; - double outer_thickness = total_thickness - inner_thickness; - // Consistency(+/-1um) check: segmentation = stack of module components - double rXCheck = comp_rmin + comp_thickness / 2; - if (fabs(staveModel.rsensor - rXCheck) > .0001 / cm) { - printout(ERROR, "MPGDCylinderBarrelTracker", - "Sensitive Component \"%s\" of StaveModel #%d,\"%s\": rsensor(%.4f cm) != " - "radius @ sensitive surface(%.4f cm)", - iSM, c_nam.c_str(), staveModel.name.c_str(), staveModel.rsensor / cm, - rXCheck / cm); - throw runtime_error("Logics error in building modules."); + // depending on whether the support is above or below the sensor. + double inner_thickness, outer_thickness; + if (sensitiveVolumeSet == 1) { + inner_thickness = thickness_so_far + comp_thickness / 2; + outer_thickness = total_thickness - inner_thickness; + } else if (nSensitives == 0) { + inner_thickness = thickness_so_far + comp_thickness / 2; + outer_thickness = comp_thickness / 2; + } else if (nSensitives == 4) { + inner_thickness = comp_thickness / 2; + outer_thickness = total_thickness - thickness_so_far - comp_thickness / 2; + } else { + inner_thickness = outer_thickness = comp_thickness / 2; } printout(DEBUG, "MPGDCylinderBarrelTracker", "Stave Model #%d,\"%s\": Sensitive surface @ R = %.4f (%.4f,%.4f) cm", iSM, @@ -448,11 +480,17 @@ static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e, SurfaceType type(SurfaceType::Sensitive); VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n); //,o ) ; - volplane_surfaces.push_back(surf); + volplane_surfaces[nSensitives].push_back(surf); + nSensitives++; } comp_rmin += comp_thickness; thickness_so_far += comp_thickness; } //end of module component loop + if (sensitiveVolumeSet < 0 || sensitiveVolumeSet != nSensitives) { + printout(ERROR, "MPGDCylinderBarrelTracker", + "Invalid set of Sensitive Volumes: it's either one (named \"GasGap\") or 5"); + throw runtime_error("Logics error in building modules."); + } } //end of stave model loop // ********** LAYER @@ -477,10 +515,11 @@ static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e, // ***** SECTOR POSITIONS ALONG Z // These are the 4 central values in Z where the four sets of modules, called // sectors, will be placed. - double modz_pos[4] = {-barrel_length / 2 + (total_length) / 2, -(total_length + z_gap) / 2, - +(total_length + z_gap) / 2, +barrel_length / 2 - (total_length) / 2}; - int nModules = 0; - for (int iz = 0; iz < 4; iz++) { + double modz_pos[nSectors] = {-barrel_length / 2 + (total_length) / 2, -(total_length + z_gap) / 2, + +(total_length + z_gap) / 2, + +barrel_length / 2 - (total_length) / 2}; + int nModules = 0; + for (int iz = 0; iz < nSectors; iz++) { int io = (iz == 1 || iz == 2) ? 0 : 1; int iSMs[2] = {sector2Models[io][0], sector2Models[io][1]}; int iSM0 = iSMs[0]; @@ -520,16 +559,19 @@ static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e, pv = lay_vol.placeVolume(module_vol, tr); pv.addPhysVolID("module", nModules); mod_elt.setPlacement(pv); - // ***** SENSITIVE COMPONENT - PlacedVolume& sens_pv = sensitives[iV]; - DetElement comp_de( - mod_elt, std::string("de_") + sens_pv.volume().name() + _toString(8 * iz + iphi, "%02d"), - nModules); - comp_de.setPlacement(sens_pv); - auto& comp_de_params = - DD4hepDetectorHelper::ensureExtension(comp_de); - comp_de_params.set("axis_definitions", "XYZ"); - volSurfaceList(comp_de)->push_back(volplane_surfaces[iV]); + for (int iSensitive = 0; iSensitive < sensitiveVolumeSet; iSensitive++) { + // ***** SENSITIVE COMPONENTS + PlacedVolume& sens_pv = sensitives[iSensitive][iV]; + int de_id = nphi * nSectors * iSensitive + nModules; + DetElement comp_de(mod_elt, + std::string("de_") + sens_pv.volume().name() + _toString(de_id, "%02d"), + de_id); + comp_de.setPlacement(sens_pv); + auto& comp_de_params = + DD4hepDetectorHelper::ensureExtension(comp_de); + comp_de_params.set("axis_definitions", "XYZ"); + volSurfaceList(comp_de)->push_back(volplane_surfaces[iSensitive][iV]); + } } }