File indexing completed on 2026-04-05 07:48:37
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019 #include "DD4hep/DetFactoryHelper.h"
0020 #include "DD4hep/Printout.h"
0021 #include "DD4hep/Shapes.h"
0022 #include "DD4hepDetectorHelper.h"
0023 #include "DDRec/DetectorData.h"
0024 #include "DDRec/Surface.h"
0025 #include "XML/Layering.h"
0026 #include "XML/Utilities.h"
0027 #include <array>
0028
0029 using namespace std;
0030 using namespace dd4hep;
0031 using namespace dd4hep::rec;
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045 static Ref_t create_BarrelPlanarMPGDTracker_geo(Detector& description, xml_h e,
0046 SensitiveDetector sens) {
0047 xml_det_t x_det = e;
0048
0049 int det_id = x_det.id();
0050 string det_name = x_det.nameStr();
0051 DetElement sdet(det_name, det_id);
0052
0053 Volume* volume;
0054
0055
0056 int sensitiveVolumeSet = 5;
0057 vector<PlacedVolume> sensitives;
0058 vector<VolPlane> volplane_surfaces;
0059
0060 PlacedVolume pv;
0061
0062
0063 #ifdef DEBUG_BarrelPlanarMPGDTracker
0064
0065 PrintLevel priorPrintLevel = printLevel();
0066 setPrintLevel(DEBUG);
0067 #endif
0068
0069 dd4hep::xml::Dimension dimensions(x_det.dimensions());
0070 xml_dim_t mpgd_pos = x_det.position();
0071 Assembly assembly(det_name);
0072
0073 double pcb_feb_ext = 0.0;
0074
0075
0076 dd4hep::xml::setDetectorTypeFlag(x_det, sdet);
0077 auto& params = DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(sdet);
0078
0079
0080 for (xml_coll_t bmat(x_det, _Unicode(boundary_material)); bmat; ++bmat) {
0081 xml_comp_t x_boundary_material = bmat;
0082 DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_boundary_material, params,
0083 "boundary_material");
0084 }
0085
0086 sens.setType("tracker");
0087
0088
0089
0090 xml_coll_t modules(x_det, _U(module));
0091 if (modules.size() != 1) {
0092
0093 printout(ERROR, "BarrelPlanarMPGDTracker_geo", "Number of modules = %u. Must be = 1",
0094 modules.size());
0095 throw runtime_error("Logics error in building modules.");
0096 }
0097 xml_comp_t x_mod = modules;
0098 string m_nam = x_mod.nameStr();
0099
0100 int ncomponents = 0;
0101 int sensor_number = 0;
0102 double total_thickness = 0;
0103
0104
0105 xml_coll_t ci(x_mod, _U(module_component));
0106 for (ci.reset(), total_thickness = 0.0; ci; ++ci) {
0107 total_thickness += xml_comp_t(ci).thickness();
0108 }
0109
0110 Assembly m_vol(m_nam);
0111 volume = &m_vol;
0112 m_vol.setVisAttributes(description, x_mod.visStr());
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124 double frame_width = 0;
0125 if (x_mod.hasChild(_U(frame))) {
0126 xml_comp_t m_frame = x_mod.child(_U(frame));
0127 frame_width = m_frame.width();
0128 }
0129
0130 double thickness_so_far = 0.0;
0131 double thickness_sum = -total_thickness / 2.0;
0132 double max_component_width = 0;
0133 double max_component_length = 0;
0134 double gas_thickness = 0.0;
0135
0136
0137
0138 int nSensitives = 0;
0139 for (xml_coll_t mci(x_mod, _U(module_component)); mci; ++mci, ++ncomponents) {
0140 xml_comp_t x_comp = mci;
0141 string c_nam = _toString(ncomponents, "component%d");
0142 string comp_name = x_comp.nameStr();
0143 double comp_thickness = x_comp.thickness();
0144 double box_width = x_comp.width();
0145 double box_length = x_comp.length();
0146 Box c_box;
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160 bool isDriftGap = comp_name == "DriftGap" ||
0161 comp_name.find("ThinGap") != std::string::npos ||
0162 comp_name.find("Radiator") != std::string::npos;
0163 if (isDriftGap || comp_name == "WindowGasGap") {
0164 box_width = x_comp.width() - 2.0 * frame_width;
0165 box_length = x_comp.length() - 2.0 * frame_width;
0166 max_component_width = box_width;
0167 max_component_length = box_length;
0168 gas_thickness += comp_thickness;
0169 c_box = {box_width / 2, box_length / 2, comp_thickness / 2};
0170 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "gas: %s", comp_name.c_str());
0171 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_width: %f", box_width);
0172 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_length: %f", box_length);
0173 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_thickness: %f", comp_thickness);
0174 } else {
0175 c_box = {x_comp.width() / 2, x_comp.length() / 2, comp_thickness / 2};
0176 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "Not gas: %s", comp_name.c_str());
0177 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_width: %f", x_comp.width());
0178 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_length: %f", x_comp.length());
0179 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "box_comp_thickness: %f", comp_thickness);
0180 }
0181 Volume c_vol{c_nam, c_box, description.material(x_comp.materialStr())};
0182
0183 c_vol.setRegion(description, x_comp.regionStr());
0184 c_vol.setLimitSet(description, x_comp.limitsStr());
0185 c_vol.setVisAttributes(description, x_comp.visStr());
0186
0187 pcb_feb_ext = x_comp.offset();
0188
0189 pv = m_vol.placeVolume(c_vol,
0190 Position(0, -pcb_feb_ext / 2.0, thickness_sum + comp_thickness / 2.0));
0191
0192 if (x_comp.isSensitive()) {
0193
0194 if (nSensitives >= 5) {
0195 sensitiveVolumeSet = -1;
0196 break;
0197 }
0198 pv.addPhysVolID("sensor", sensor_number);
0199
0200 if (comp_name == "DriftGap") {
0201 if (nSensitives != 0) {
0202 sensitiveVolumeSet = -1;
0203 break;
0204 }
0205 sensitiveVolumeSet = 1;
0206 }
0207 int strip_id = x_comp.key();
0208 pv.addPhysVolID("strip", strip_id);
0209 c_vol.setSensitiveDetector(sens);
0210 sensitives.push_back(pv);
0211
0212
0213 Vector3D u(-1., 0., 0.);
0214 Vector3D v(0., -1., 0.);
0215 Vector3D n(0., 0., 1.);
0216
0217
0218
0219
0220
0221 double inner_thickness, outer_thickness;
0222 if (sensitiveVolumeSet == 1) {
0223 inner_thickness = thickness_so_far + comp_thickness / 2;
0224 outer_thickness = total_thickness - thickness_so_far - comp_thickness / 2;
0225 } else if (nSensitives == 0) {
0226 inner_thickness = thickness_so_far + comp_thickness / 2;
0227 outer_thickness = comp_thickness / 2;
0228 } else if (nSensitives == 4) {
0229 inner_thickness = comp_thickness / 2;
0230 outer_thickness = total_thickness - thickness_so_far - comp_thickness / 2;
0231 } else {
0232 inner_thickness = outer_thickness = comp_thickness / 2;
0233 }
0234 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "Sensitive surface @ R = %.4f (%.4f,%.4f) cm",
0235 (thickness_sum + comp_thickness / 2) / cm, inner_thickness / cm,
0236 outer_thickness / cm);
0237
0238 SurfaceType type(rec::SurfaceType::Sensitive);
0239
0240 VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n);
0241 volplane_surfaces.push_back(surf);
0242 nSensitives++;
0243 }
0244 thickness_sum += comp_thickness;
0245 thickness_so_far += comp_thickness;
0246 }
0247 if (sensitiveVolumeSet < 0 || sensitiveVolumeSet != nSensitives) {
0248 printout(ERROR, "BarrelPlanarMPGDTracker_geo",
0249 "Invalid set of Sensitive Volumes: it's either one (named \"DriftGap\") or 5");
0250 throw runtime_error("Logics error in building modules.");
0251 }
0252
0253 if (x_mod.hasChild(_U(frame))) {
0254 xml_comp_t m_frame = x_mod.child(_U(frame));
0255 double frame_thickness = getAttrOrDefault<double>(m_frame, _U(thickness), total_thickness);
0256
0257 Box lframe_box{m_frame.width() / 2.0, (max_component_length + 2.0 * m_frame.width()) / 2.0,
0258 frame_thickness / 2.0};
0259 Box rframe_box{m_frame.width() / 2.0, (max_component_length + 2.0 * m_frame.width()) / 2.0,
0260 frame_thickness / 2.0};
0261 Box tframe_box{max_component_width / 2.0, m_frame.width() / 2.0, frame_thickness / 2.0};
0262 Box bframe_box{max_component_width / 2.0, m_frame.width() / 2.0, frame_thickness / 2.0};
0263
0264
0265
0266 Volume lframe_vol{"left_frame", lframe_box, description.material(m_frame.materialStr())};
0267 Volume rframe_vol{"right_frame", rframe_box, description.material(m_frame.materialStr())};
0268 Volume tframe_vol{"top_frame", tframe_box, description.material(m_frame.materialStr())};
0269 Volume bframe_vol{"bottom_frame", bframe_box, description.material(m_frame.materialStr())};
0270
0271 lframe_vol.setVisAttributes(description, m_frame.visStr());
0272 rframe_vol.setVisAttributes(description, m_frame.visStr());
0273 tframe_vol.setVisAttributes(description, m_frame.visStr());
0274 bframe_vol.setVisAttributes(description, m_frame.visStr());
0275
0276 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "frame_thickness: %f", frame_thickness);
0277 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "total_thickness: %f", total_thickness);
0278 printout(DEBUG, "BarrelPlanarMPGDTracker_geo", "frame_thickness + total_thickness: %f",
0279 frame_thickness + total_thickness);
0280
0281 m_vol.placeVolume(
0282 lframe_vol, Position(frame_width / 2.0 + max_component_width / 2, 0.0,
0283 frame_thickness / 2.0 - total_thickness / 2.0 - gas_thickness / 2.0));
0284 m_vol.placeVolume(
0285 rframe_vol, Position(-frame_width / 2.0 - max_component_width / 2.0, 0.0,
0286 frame_thickness / 2.0 - total_thickness / 2.0 - gas_thickness / 2.0));
0287 m_vol.placeVolume(
0288 tframe_vol, Position(0.0, frame_width / 2.0 + max_component_length / 2,
0289 frame_thickness / 2.0 - total_thickness / 2.0 - gas_thickness / 2.0));
0290 m_vol.placeVolume(
0291 bframe_vol, Position(0.0, -frame_width / 2.0 - max_component_length / 2.0,
0292 frame_thickness / 2.0 - total_thickness / 2.0 - gas_thickness / 2.0));
0293 }
0294
0295
0296
0297 xml_coll_t li(x_det, _U(layer));
0298 if (li.size() != 1) {
0299 printout(ERROR, "BarrelPlanarMPGDTracker_geo", "Number of layers = %d. Must be = 1",
0300 (int)li.size());
0301 throw runtime_error("Logics error in building modules.");
0302 }
0303
0304 xml_comp_t x_layer = li;
0305 xml_comp_t x_layout = x_layer.child(_U(rphi_layout));
0306 xml_comp_t z_layout = x_layer.child(_U(z_layout));
0307 int lay_id = x_layer.id();
0308 string lay_nam = det_name + _toString(x_layer.id(), "_layer%d");
0309 xml_comp_t envelope_tolerance = x_layer.child(_Unicode(envelope_tolerance), false);
0310 double envelope_r_min = 0;
0311 double envelope_r_max = 0;
0312 double envelope_z_min = 0;
0313 double envelope_z_max = 0;
0314 if (envelope_tolerance) {
0315 envelope_r_min = getAttrOrDefault(envelope_tolerance, _Unicode(r_min), 0);
0316 envelope_r_max = getAttrOrDefault(envelope_tolerance, _Unicode(r_max), 0);
0317 envelope_z_min = getAttrOrDefault(envelope_tolerance, _Unicode(z_min), 0);
0318 envelope_z_max = getAttrOrDefault(envelope_tolerance, _Unicode(z_max), 0);
0319 }
0320
0321 double phi0 = x_layout.phi0();
0322 double phi_tilt = x_layout.phi_tilt();
0323 double rc = x_layout.rc();
0324 int nphi = x_layout.nphi();
0325 double rphi_dr = x_layout.dr();
0326 double phi_incr = (2 * M_PI) / nphi;
0327 double phic = phi0;
0328 int nz = 2;
0329 double z_dr = z_layout.dr();
0330 double z0 = z_layout.z0();
0331
0332 Assembly layer_assembly(lay_nam);
0333 Volume module_env = *volume;
0334 DetElement lay_elt(sdet, lay_nam, lay_id);
0335 auto& layerParams =
0336 DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(lay_elt);
0337
0338 pv = assembly.placeVolume(layer_assembly);
0339 pv.addPhysVolID("layer", lay_id);
0340 lay_elt.setPlacement(pv);
0341
0342 int module = 0;
0343
0344 for (int ii = 0; ii < nphi; ii++) {
0345 double xc = rc * std::cos(phic);
0346 double yc = rc * std::sin(phic);
0347 double dx = z_dr * std::cos(phic + phi_tilt);
0348 double dy = z_dr * std::sin(phic + phi_tilt);
0349
0350 for (int j = 0; j < nz; j++) {
0351 string module_name = _toString(module, "module%02d");
0352 DetElement mod_elt(lay_elt, module_name, module);
0353 double mod_z = 0.5 * dimensions.length();
0354 double z_placement = mod_z - 0.5 * pcb_feb_ext -
0355 j * (nz * mod_z - pcb_feb_ext);
0356 double z_offset =
0357 z_placement > 0 ? -z0 / 2.0
0358 : z0 / 2.0;
0359
0360 Transform3D tr(
0361 RotationZYX(0, ((M_PI / 2) - phic - phi_tilt), -M_PI / 2) * RotationZ(j * M_PI),
0362 Position(
0363 xc, yc,
0364 mpgd_pos.z() + z_placement +
0365 z_offset));
0366
0367 pv = layer_assembly.placeVolume(module_env, tr);
0368 pv.addPhysVolID("module", module);
0369 mod_elt.setPlacement(pv);
0370 for (int iSensitive = 0; iSensitive < sensitiveVolumeSet; iSensitive++) {
0371
0372 PlacedVolume& sens_pv = sensitives[iSensitive];
0373 int de_id = nphi * nz * iSensitive + module;
0374 DetElement comp_de(mod_elt,
0375 std::string("de_") + sens_pv.volume().name() + _toString(de_id, "%02d"),
0376 de_id);
0377 comp_de.setPlacement(sens_pv);
0378 auto& comp_de_params =
0379 DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(comp_de);
0380 comp_de_params.set<string>("axis_definitions", "XYZ");
0381 volSurfaceList(comp_de)->push_back(volplane_surfaces[iSensitive]);
0382 }
0383
0384 module++;
0385
0386 xc += dx;
0387 yc += dy;
0388 }
0389
0390 phic += phi_incr;
0391 rc += rphi_dr;
0392 }
0393 layer_assembly->GetShape()->ComputeBBox();
0394 layerParams.set<double>("envelope_r_min", envelope_r_min);
0395 layerParams.set<double>("envelope_r_max", envelope_r_max);
0396 layerParams.set<double>("envelope_z_min", envelope_z_min);
0397 layerParams.set<double>("envelope_z_max", envelope_z_max);
0398
0399 for (xml_coll_t lmat(x_layer, _Unicode(layer_material)); lmat; ++lmat) {
0400 xml_comp_t x_layer_material = lmat;
0401 DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_layer_material, layerParams,
0402 "layer_material");
0403 }
0404 sdet.setAttributes(description, assembly, x_det.regionStr(), x_det.limitsStr(), x_det.visStr());
0405 assembly.setVisAttributes(description.invisible());
0406 pv = description.pickMotherVolume(sdet).placeVolume(assembly);
0407 pv.addPhysVolID("system", det_id);
0408 sdet.setPlacement(pv);
0409
0410 #ifdef DEBUG_BarrelPlanarMPGDTracker
0411
0412 setPrintLevel(priorPrintLevel);
0413 #endif
0414
0415 return sdet;
0416 }
0417
0418
0419
0420 DECLARE_DETELEMENT(epic_OuterMPGDBarrel, create_BarrelPlanarMPGDTracker_geo)