File indexing completed on 2026-05-18 07:52:58
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <DD4hep/DetFactoryHelper.h>
0013 #include <DD4hep/Printout.h>
0014 #include <DD4hep/Shapes.h>
0015 #include <XML/Layering.h>
0016 #include <XML/Utilities.h>
0017
0018 #include <cassert>
0019
0020 using namespace std;
0021 using namespace dd4hep;
0022
0023 namespace {
0024 std::pair<Volume, Transform3D> build_shape(const Detector& descr, const xml_det_t& x_det,
0025 const xml_comp_t& x_support, const xml_comp_t& x_child,
0026 const double offset = 0) {
0027
0028 xml_dim_t x_pos(x_child.child(_U(position), false));
0029 xml_dim_t x_rot(x_child.child(_U(rotation), false));
0030 Position pos3D{0, 0, 0};
0031 Rotation3D rot3D;
0032
0033 if (x_rot) {
0034 rot3D = RotationZYX(x_rot.z(0), x_rot.y(0), x_rot.x(0));
0035 }
0036 if (x_pos) {
0037 pos3D = Position(x_pos.x(0), x_pos.y(0), x_pos.z(0));
0038 }
0039
0040
0041 Solid solid;
0042 const std::string type = x_support.attr<std::string>(_U(type));
0043 if (type == "Tube" || type == "Cylinder") {
0044 const double thickness = getAttrOrDefault(x_child, _U(thickness), x_support.thickness());
0045 const double length = getAttrOrDefault(x_child, _U(length), x_support.length());
0046 const double rmin = getAttrOrDefault(x_child, _U(rmin), x_support.rmin()) + offset;
0047 const double phimin = getAttrOrDefault(
0048 x_child, _Unicode(phimin), getAttrOrDefault(x_support, _Unicode(phimin), 0.0 * deg));
0049 const double phimax = getAttrOrDefault(
0050 x_child, _Unicode(phimax), getAttrOrDefault(x_support, _Unicode(phimax), 360.0 * deg));
0051 solid = Tube(rmin, rmin + thickness, length / 2, phimin, phimax);
0052 } else if (type == "Box") {
0053 const double box_x = getAttrOrDefault(x_child, _U(x), x_support.x());
0054 const double box_y = getAttrOrDefault(x_child, _U(y), x_support.y());
0055 const double box_z = getAttrOrDefault(x_child, _U(z), x_support.z());
0056 solid = Box(box_x / 2.0, box_y / 2.0, box_z / 2.0);
0057 }
0058
0059 else if (type == "Disk") {
0060 const double thickness = getAttrOrDefault(x_child, _U(thickness), x_support.thickness());
0061 const double rmin = getAttrOrDefault(x_child, _U(rmin), x_support.rmin());
0062 const double rmax = getAttrOrDefault(x_child, _U(rmax), x_support.rmax());
0063 const double phimin = getAttrOrDefault(
0064 x_child, _Unicode(phimin), getAttrOrDefault(x_support, _Unicode(phimin), 0.0 * deg));
0065 const double phimax = getAttrOrDefault(
0066 x_child, _Unicode(phimax), getAttrOrDefault(x_support, _Unicode(phimax), 360.0 * deg));
0067 pos3D = pos3D + Position(0, 0, -x_support.thickness() / 2 + thickness / 2 + offset);
0068 solid = Tube(rmin, rmax, thickness / 2, phimin, phimax);
0069 } else if (type == "Cone") {
0070 const double base_rmin1 = getAttrOrDefault(x_child, _U(rmin1), x_support.rmin1());
0071 const double base_rmin2 = getAttrOrDefault(x_child, _U(rmin2), x_support.rmin2());
0072 const double length = getAttrOrDefault(x_child, _U(length), x_support.length());
0073
0074
0075 const double thickness = getAttrOrDefault(x_child, _U(thickness), x_support.thickness());
0076 const double transverse_thickness =
0077 thickness / cos(atan2(fabs(base_rmin2 - base_rmin1), length));
0078
0079 const double transverse_offset = offset / cos(atan2(fabs(base_rmin2 - base_rmin1), length));
0080 const double rmin1 = base_rmin1 + transverse_offset;
0081 const double rmin2 = base_rmin2 + transverse_offset;
0082 const double rmax1 = rmin1 + transverse_thickness;
0083 const double rmax2 = rmin2 + transverse_thickness;
0084
0085 const double rmin = getAttrOrDefault(
0086 x_child, _U(rmin), getAttrOrDefault(x_support, _Unicode(rmin), min(rmin1, rmin2)));
0087 const double rmax = getAttrOrDefault(
0088 x_child, _U(rmax), getAttrOrDefault(x_support, _Unicode(rmax), max(rmax1, rmax2)));
0089 if (rmin > min(rmax1, rmax2)) {
0090 printout(ERROR, x_det.nameStr(),
0091 "%s: rmin (%f mm) must be smaller than the smallest rmax (%f %f mm)",
0092 x_support.nameStr().c_str(), rmin / mm, rmax1 / mm, rmax2 / mm);
0093 std::exit(1);
0094 }
0095 if (rmax < max(base_rmin1, base_rmin2)) {
0096 printout(ERROR, x_det.nameStr(),
0097 "%s: rmax (%f mm) must be larger than the largest rmin (%f %f mm)",
0098 x_support.nameStr().c_str(), rmax / mm, base_rmin1 / mm, base_rmin2 / mm);
0099 std::exit(1);
0100 }
0101 const double zmin = -length / 2 + length * (rmin - rmin1) / (rmin2 - rmin1);
0102 const double zmax = -length / 2 + length * (rmax - rmax1) / (rmax2 - rmax1);
0103 const auto rmin_at = [&](const double z) {
0104 return rmin1 + (z + length / 2) * (rmin2 - rmin1) / length;
0105 };
0106 const auto rmax_at = [&](const double z) {
0107 return rmax1 + (z + length / 2) * (rmax2 - rmax1) / length;
0108 };
0109
0110 const double phimin = getAttrOrDefault<double>(
0111 x_child, _Unicode(phimin), getAttrOrDefault(x_support, _Unicode(phimin), 0.0 * deg));
0112 const double phimax = getAttrOrDefault<double>(
0113 x_child, _Unicode(phimax), getAttrOrDefault(x_support, _Unicode(phimax), 360.0 * deg));
0114 const double deltaphi = phimax - phimin;
0115 const double epsilon{TGeoShape::Tolerance()};
0116 if (fabs(zmin) >= length / 2 - epsilon && fabs(zmax) >= length / 2 - epsilon) {
0117 if (fabs(phimax - phimin - 360 * deg) < epsilon) {
0118 solid = Cone(length / 2, rmin1, rmax1, rmin2, rmax2);
0119 } else {
0120 solid = ConeSegment(length / 2, rmin1, rmax1, rmin2, rmax2, phimin, phimax);
0121 }
0122 } else {
0123 std::vector<double> v_rmin{max(rmin1, rmin), max(rmin2, rmin)},
0124 v_rmax{min(rmax1, rmax), min(rmax2, rmax)}, v_z{-length / 2, +length / 2};
0125 for (const auto& z :
0126 (zmin < zmax ? std::vector<double>{zmin, zmax} : std::vector<double>{zmax, zmin})) {
0127 if (-length / 2 + epsilon < z && z < -epsilon + length / 2) {
0128 v_rmin.insert(std::prev(v_rmin.end()), std::max(rmin, rmin_at(z)));
0129 v_rmax.insert(std::prev(v_rmax.end()), std::min(rmax, rmax_at(z)));
0130 v_z.insert(std::prev(v_z.end()), z);
0131 }
0132 }
0133 solid = Polycone(phimin, deltaphi, v_rmin, v_rmax, v_z);
0134 }
0135 } else if (type == "Disk") {
0136 const double thickness = getAttrOrDefault(x_child, _U(thickness), x_support.thickness());
0137 const double rmin = getAttrOrDefault(x_child, _U(rmin), x_support.rmin());
0138 const double rmax = getAttrOrDefault(x_child, _U(rmax), x_support.rmax());
0139 const double phimin = getAttrOrDefault(
0140 x_child, _Unicode(phimin), getAttrOrDefault(x_support, _Unicode(phimin), 0.0 * deg));
0141 const double phimax = getAttrOrDefault(
0142 x_child, _Unicode(phimax), getAttrOrDefault(x_support, _Unicode(phimax), 360.0 * deg));
0143 pos3D = pos3D + Position(0, 0, -x_support.thickness() / 2 + thickness / 2 + offset);
0144 solid = Tube(rmin, rmax, thickness / 2, phimin, phimax);
0145 } else {
0146 printout(ERROR, x_det.nameStr(), "Unknown support type: %s", type.c_str());
0147 std::exit(1);
0148 }
0149
0150 Material mat = descr.material(getAttrOrDefault<std::string>(x_child, _U(material), "Air"));
0151
0152 Volume vol{getAttrOrDefault<std::string>(x_child, _U(name), "support_vol"), solid, mat};
0153
0154
0155 Transform3D tr(rot3D, pos3D);
0156
0157
0158 if (x_child.hasAttr(_U(vis))) {
0159 vol.setVisAttributes(descr.visAttributes(x_child.visStr()));
0160 }
0161 return {vol, tr};
0162 }
0163
0164
0165 std::pair<Volume, Transform3D> build_subtraction(const Detector& descr, const xml_det_t& x_det,
0166 const xml_comp_t& x_subtraction) {
0167
0168
0169 Solid baseSolid;
0170 bool baseSet = false;
0171 bool haveGenericShapes = false;
0172 std::vector<std::pair<Solid, Transform3D>> holes;
0173
0174 for (xml_coll_t s{x_subtraction, _U(shape)}; s; ++s) {
0175 haveGenericShapes = true;
0176 xml_comp_t x_s = s;
0177
0178
0179 auto [v, tr_unused] = build_shape(descr, x_det, x_s, x_s);
0180 Solid sld = v.solid();
0181
0182
0183 xml_dim_t x_pos(x_s.child(_U(position), false));
0184 xml_dim_t x_rot(x_s.child(_U(rotation), false));
0185 Position pos{0, 0, 0};
0186 Rotation3D rot;
0187 if (x_rot)
0188 rot = RotationZYX(x_rot.z(0), x_rot.y(0), x_rot.x(0));
0189 if (x_pos)
0190 pos = Position(x_pos.x(0), x_pos.y(0), x_pos.z(0));
0191 Transform3D tr_rel(rot, pos);
0192
0193 const std::string role = getAttrOrDefault<std::string>(x_s, _Unicode(role), "hole");
0194 if (role == "base" || role == "primary") {
0195 baseSolid = sld;
0196 baseSet = true;
0197 } else {
0198 holes.emplace_back(sld, tr_rel);
0199 }
0200 }
0201
0202
0203 if (!haveGenericShapes) {
0204 xml_comp_t x_shape1 = x_subtraction.child(_Unicode(shape1));
0205 xml_comp_t x_shape2 = x_subtraction.child(_Unicode(shape2));
0206 if (!x_shape1 || !x_shape2) {
0207 printout(ERROR, x_det.nameStr(),
0208 "Subtraction %s: expected shape1/shape2 or generic <shape> children.",
0209 x_subtraction.nameStr().c_str());
0210 std::exit(1);
0211 }
0212 auto [vol1, tr1_unused] = build_shape(descr, x_det, x_shape1, x_shape1);
0213 auto [vol2, tr2_unused] = build_shape(descr, x_det, x_shape2, x_shape2);
0214 baseSolid = vol1.solid();
0215 baseSet = true;
0216
0217
0218 xml_dim_t x_pos2(x_shape2.child(_U(position), false));
0219 xml_dim_t x_rot2(x_shape2.child(_U(rotation), false));
0220 Position pos2{0, 0, 0};
0221 Rotation3D rot2;
0222 if (x_rot2)
0223 rot2 = RotationZYX(x_rot2.z(0), x_rot2.y(0), x_rot2.x(0));
0224 if (x_pos2)
0225 pos2 = Position(x_pos2.x(0), x_pos2.y(0), x_pos2.z(0));
0226 holes.emplace_back(vol2.solid(), Transform3D(rot2, pos2));
0227 }
0228
0229 if (!baseSet) {
0230 printout(ERROR, x_det.nameStr(), "Subtraction %s: no base shape found (role=\"base\").",
0231 x_subtraction.nameStr().c_str());
0232 std::exit(1);
0233 }
0234
0235
0236 Solid current = baseSolid;
0237 for (auto& h : holes) {
0238 current = SubtractionSolid(current, h.first, h.second);
0239 }
0240
0241
0242 Material mat =
0243 descr.material(getAttrOrDefault<std::string>(x_subtraction, _Unicode(material), "Air"));
0244 std::string vol_name =
0245 getAttrOrDefault<std::string>(x_subtraction, _Unicode(name), "subtraction_vol");
0246 Volume vol{vol_name, current, mat};
0247
0248
0249 xml_dim_t x_pos(x_subtraction.child(_U(position), false));
0250 xml_dim_t x_rot(x_subtraction.child(_U(rotation), false));
0251 Position pos3D{0, 0, 0};
0252 Rotation3D rot3D;
0253 if (x_rot)
0254 rot3D = RotationZYX(x_rot.z(0), x_rot.y(0), x_rot.x(0));
0255 if (x_pos)
0256 pos3D = Position(x_pos.x(0), x_pos.y(0), x_pos.z(0));
0257 Transform3D tr(rot3D, pos3D);
0258
0259
0260 if (x_subtraction.hasAttr(_U(vis))) {
0261 vol.setVisAttributes(descr.visAttributes(x_subtraction.visStr()));
0262 }
0263
0264
0265 printout(DEBUG, "SupportServiceMaterial", "Created subtraction volume: %s (holes=%d)",
0266 vol_name.c_str(), int(holes.size()));
0267
0268 return {vol, tr};
0269 }
0270
0271 std::pair<Volume, Transform3D> build_shape(const Detector& descr, const xml_det_t& x_det,
0272 const xml_comp_t& x_support, const double offset = 0) {
0273 return build_shape(descr, x_det, x_support, x_support, offset);
0274 }
0275 }
0276
0277
0278
0279
0280
0281 static Ref_t create_SupportServiceMaterial(Detector& description, xml_h e,
0282 [[maybe_unused]] SensitiveDetector sens) {
0283 const xml_det_t x_det = e;
0284 const int det_id = x_det.id();
0285 const string det_name = x_det.nameStr();
0286
0287
0288 const double offset = getAttrOrDefault(x_det, _U(offset), 0.);
0289
0290 DetElement det(det_name, det_id);
0291 Assembly assembly(det_name + "_assembly");
0292
0293
0294 for (xml_coll_t su{x_det, _U(support)}; su; ++su) {
0295 xml_comp_t x_sup = su;
0296 const double rot_z = getAttrOrDefault(x_sup, _U(phi0), 0.);
0297 RotationZ rot(rot_z);
0298 Transform3D tr_rot(
0299 rot, Position(0, 0, 0));
0300
0301 auto [vol, tr] = build_shape(description, x_det, x_sup);
0302 [[maybe_unused]] auto pv = assembly.placeVolume(vol, tr_rot * tr);
0303
0304 double cumulative_thickness = 0;
0305 for (xml_coll_t com{x_sup, _U(component)}; com; ++com) {
0306 xml_comp_t x_com = com;
0307 auto [cvol, ctr] = build_shape(description, x_det, x_sup, x_com, cumulative_thickness);
0308 vol.placeVolume(cvol, ctr);
0309 cumulative_thickness += x_com.thickness();
0310 }
0311 }
0312
0313 for (xml_coll_t sub{x_det, _Unicode(subtraction)}; sub; ++sub) {
0314 xml_comp_t x_sub = sub;
0315 auto [vol, tr] = build_subtraction(description, x_det, x_sub);
0316 assembly.placeVolume(vol, tr);
0317 }
0318
0319
0320 Volume motherVol = description.pickMotherVolume(det);
0321 Position pos(0, 0, offset);
0322 PlacedVolume pv = motherVol.placeVolume(assembly, pos);
0323 pv.addPhysVolID("system", det.id());
0324 det.setPlacement(pv);
0325
0326 return det;
0327 }
0328
0329
0330 DECLARE_DETELEMENT(epic_SupportServiceMaterial, create_SupportServiceMaterial)