Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-06-30 07:56:09

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022 Wouter Deconinck, Whitney Armstrong, Sylvester Joosten
0003 
0004 //==========================================================================
0005 //
0006 //      <detector name ="DetName" type="Beampipe" >
0007 //      <layer id="#(int)" inner_r="#(double)" outer_z="#(double)" >
0008 //      <slice material="string" thickness="#(double)" >
0009 //      </layer>
0010 //      </detector>
0011 //==========================================================================
0012 #include "DD4hep/DetFactoryHelper.h"
0013 #include "DD4hep/Printout.h"
0014 #include "TMath.h"
0015 #include <XML/Helper.h>
0016 #include "XML/Utilities.h"
0017 #include "DD4hepDetectorHelper.h"
0018 #include <tuple>
0019 
0020 using namespace std;
0021 using namespace dd4hep;
0022 
0023 /** \addtogroup beamline Beamline Instrumentation
0024  */
0025 
0026 /** \addtogroup IRChamber Interaction Region Vacuum Chamber.
0027  * \brief Type: **IRChamber**.
0028  * \ingroup beamline
0029  *
0030  *
0031  * \code
0032  *   <detector>
0033  *   </detector>
0034  * \endcode
0035  *
0036  */
0037 static Ref_t create_detector(Detector& det, xml_h e, SensitiveDetector /* sens */) {
0038   using namespace ROOT::Math;
0039   xml_det_t x_det       = e;
0040   string det_name       = x_det.nameStr();
0041   xml_comp_t x_dettype  = x_det.child(dd4hep::xml::Strng_t("type_flags"));
0042   unsigned int typeFlag = x_dettype.type();
0043   DetElement sdet(det_name, x_det.id());
0044   Assembly assembly(det_name + "_assembly");
0045 
0046   xml::Component IP_pipe_c = x_det.child(_Unicode(IP_pipe));
0047 
0048   Material IP_beampipe_wall_material =
0049       det.material(IP_pipe_c.attr<string>(_Unicode(wall_material)));
0050   Material IP_beampipe_coating_material =
0051       det.material(IP_pipe_c.attr<string>(_Unicode(coating_material)));
0052   Material m_Vacuum  = det.material("Vacuum");
0053   Material m_Wall    = det.material("StainlessSteelP506");
0054   Material m_Coating = det.material("Copper");
0055 
0056   // IP Beampipe
0057   double IP_beampipe_ID                = IP_pipe_c.attr<double>(_Unicode(ID));
0058   double IP_beampipe_wall_thickness    = IP_pipe_c.attr<double>(_Unicode(wall_thickness));
0059   double IP_beampipe_coating_thickness = IP_pipe_c.attr<double>(_Unicode(coating_thickness));
0060   double IP_acts_beampipe_OD           = IP_beampipe_ID - 5.0 * mm;
0061   double IP_acts_beampipe_ID           = IP_acts_beampipe_OD - 1.0 * mm;
0062 
0063   double upstream_straight_length   = IP_pipe_c.attr<double>(_Unicode(upstream_straight_length));
0064   double downstream_straight_length = IP_pipe_c.attr<double>(_Unicode(downstream_straight_length));
0065 
0066   // visualization
0067   auto wallVis      = det.visAttributes(x_det.attr<std::string>(_Unicode(vis_wall)));
0068   auto coatingVis   = det.visAttributes(x_det.attr<std::string>(_Unicode(vis_coating)));
0069   auto IPwallVis    = det.visAttributes(x_det.attr<std::string>(_Unicode(vis_IPwall)));
0070   auto IPcoatingVis = det.visAttributes(x_det.attr<std::string>(_Unicode(vis_IPcoating)));
0071 
0072   // colors: (r, g, b, alpha)
0073   wallVis.setColor(0.0, 0.0, 1.0, 1.0);      // blue
0074   coatingVis.setColor(1.0, 0.0, 0.0, 1.0);   // red
0075   IPwallVis.setColor(0.0, 1.0, 0.0, 1.0);    // green
0076   IPcoatingVis.setColor(1.0, 1.0, 0.0, 1.0); // yellow
0077 
0078   // central acts beampipe volume
0079   Tube central_tube(0.5 * IP_acts_beampipe_ID, 0.5 * IP_acts_beampipe_OD,
0080                     0.5 * (upstream_straight_length + downstream_straight_length));
0081   Volume central_volume("acts_central_beampipe_vol", central_tube, m_Vacuum);
0082   const double central_offset = -.5 * (upstream_straight_length - downstream_straight_length);
0083   DetElement central_det(sdet, "acts_beampipe_central", 1);
0084 
0085   // Set dd4hep variant parameters for conversion to ACTS tracking geometry
0086   central_det.setTypeFlag(typeFlag);
0087   auto& params = DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(central_det);
0088   int nBinPhi  = 144; // fix later. Should take this from a xml tag
0089   int nBinZ    = 10;  // fix later. Should take this from a xml tag
0090   params.set<bool>("layer_material", true);
0091   params.set<bool>("layer_material_representing", true);
0092   params.set<int>("layer_material_representing_binPhi", nBinPhi);
0093   params.set<int>("layer_material_representing_binZ", nBinZ);
0094 
0095   // -----------------------------
0096   // IP beampipe
0097   //
0098   // setup for the central IP beampipe:
0099   //
0100   // /-------\ Be wall
0101   //  /-----\  Au coating
0102   //   /---\   Vacuum padding (5mm)
0103   //    /-\    Fake vacuum beampipe (1mm)
0104   //     -     Vacuum filled inner beampipe
0105   //
0106   Tube downstream_IP_tube(IP_beampipe_ID / 2.0, IP_beampipe_ID / 2.0 + IP_beampipe_wall_thickness,
0107                           downstream_straight_length / 2.0);
0108   Tube downstream_IP_coating(IP_beampipe_ID / 2.0 - IP_beampipe_coating_thickness,
0109                              IP_beampipe_ID / 2.0, downstream_straight_length / 2.0);
0110   Tube downstream_IP_vacuum_padding(IP_acts_beampipe_OD / 2.0,
0111                                     IP_beampipe_ID / 2.0 - IP_beampipe_coating_thickness,
0112                                     downstream_straight_length / 2.0);
0113   Tube downstream_IP_acts_beampipe(IP_acts_beampipe_ID / 2.0, IP_acts_beampipe_OD / 2.0,
0114                                    downstream_straight_length / 2.0);
0115   Tube downstream_IP_vacuum_fill(0.0, IP_acts_beampipe_ID / 2.0, downstream_straight_length / 2.0);
0116 
0117   Tube upstream_IP_tube(IP_beampipe_ID / 2.0, IP_beampipe_ID / 2.0 + IP_beampipe_wall_thickness,
0118                         upstream_straight_length / 2.0);
0119   Tube upstream_IP_coating(IP_beampipe_ID / 2.0 - IP_beampipe_coating_thickness,
0120                            IP_beampipe_ID / 2.0, upstream_straight_length / 2.0);
0121   Tube upstream_IP_vacuum_padding(IP_acts_beampipe_OD / 2.0,
0122                                   IP_beampipe_ID / 2.0 - IP_beampipe_coating_thickness,
0123                                   upstream_straight_length / 2.0);
0124   Tube upstream_IP_acts_beampipe(IP_acts_beampipe_ID / 2.0, IP_acts_beampipe_OD / 2.0,
0125                                  upstream_straight_length / 2.0);
0126   Tube upstream_IP_vacuum_fill(0.0, IP_acts_beampipe_ID / 2.0, upstream_straight_length / 2.0);
0127 
0128   // create volumes
0129   Volume v_downstream_IP_vacuum_fill("v_downstream_IP_vacuum_fill", downstream_IP_vacuum_fill,
0130                                      m_Vacuum);
0131   Volume v_downstream_IP_acts_beampipe("v_downstream_IP_acts_beampipe", downstream_IP_acts_beampipe,
0132                                        m_Vacuum);
0133   Volume v_downstream_IP_vacuum_padding("v_downstream_IP_vacuum_padding",
0134                                         downstream_IP_vacuum_padding, m_Vacuum);
0135   Volume v_downstream_IP_coating("v_downstream_IP_coating", downstream_IP_coating,
0136                                  IP_beampipe_coating_material);
0137   Volume v_downstream_IP_tube("v_downstream_IP_tube", downstream_IP_tube,
0138                               IP_beampipe_wall_material);
0139   Volume v_upstream_IP_vacuum_fill("v_upstream_IP_vacuum_fill", upstream_IP_vacuum_fill, m_Vacuum);
0140   Volume v_upstream_IP_acts_beampipe("v_upstream_IP_acts_beampipe", upstream_IP_acts_beampipe,
0141                                      m_Vacuum);
0142   Volume v_upstream_IP_vacuum_padding("v_upstream_IP_vacuum_padding", upstream_IP_vacuum_padding,
0143                                       m_Vacuum);
0144   Volume v_upstream_IP_coating("v_upstream_IP_coating", upstream_IP_coating,
0145                                IP_beampipe_coating_material);
0146   Volume v_upstream_IP_tube("v_upstream_IP_tube", upstream_IP_tube, IP_beampipe_wall_material);
0147 
0148   // set names
0149   sdet.setAttributes(det, v_upstream_IP_coating, x_det.regionStr(), x_det.limitsStr(),
0150                      x_det.attr<std::string>(_Unicode(vis_IPcoating)));
0151   sdet.setAttributes(det, v_upstream_IP_tube, x_det.regionStr(), x_det.limitsStr(),
0152                      x_det.attr<std::string>(_Unicode(vis_IPwall)));
0153   sdet.setAttributes(det, v_downstream_IP_coating, x_det.regionStr(), x_det.limitsStr(),
0154                      x_det.attr<std::string>(_Unicode(vis_IPcoating)));
0155   sdet.setAttributes(det, v_downstream_IP_tube, x_det.regionStr(), x_det.limitsStr(),
0156                      x_det.attr<std::string>(_Unicode(vis_IPwall)));
0157 
0158   // place volumes
0159   assembly.placeVolume(v_upstream_IP_vacuum_fill, Position(0, 0, -upstream_straight_length / 2.0));
0160   central_volume.placeVolume(v_upstream_IP_acts_beampipe,
0161                              Position(0, 0, -upstream_straight_length / 2.0 - central_offset));
0162   assembly.placeVolume(v_upstream_IP_vacuum_padding,
0163                        Position(0, 0, -upstream_straight_length / 2.0));
0164   assembly.placeVolume(v_upstream_IP_coating, Position(0, 0, -upstream_straight_length / 2.0));
0165   assembly.placeVolume(v_upstream_IP_tube, Position(0, 0, -upstream_straight_length / 2.0));
0166 
0167   assembly.placeVolume(v_downstream_IP_vacuum_fill,
0168                        Position(0, 0, downstream_straight_length / 2.0));
0169   central_volume.placeVolume(v_downstream_IP_acts_beampipe,
0170                              Position(0, 0, downstream_straight_length / 2.0 - central_offset));
0171   assembly.placeVolume(v_downstream_IP_vacuum_padding,
0172                        Position(0, 0, downstream_straight_length / 2.0));
0173   assembly.placeVolume(v_downstream_IP_coating, Position(0, 0, downstream_straight_length / 2.0));
0174   assembly.placeVolume(v_downstream_IP_tube, Position(0, 0, downstream_straight_length / 2.0));
0175 
0176   auto central_pv = assembly.placeVolume(central_volume, Position(0, 0, +central_offset));
0177   central_det.setPlacement(central_pv);
0178 
0179   //---------------------------------------------------------------------------------------------------------
0180   // Helper function to create polycone pairs (wall, coating, and vacuum)
0181   auto zplane_to_polycones = [](xml::Component& x_pipe) {
0182     std::vector<double> zero, z;
0183     std::vector<double> rmax_wall, rmax_coating, rmax_vacuum;
0184     std::vector<double> rmin_wall, rmin_coating, rmin_vacuum;
0185     // thickness
0186     auto wall_thickness    = getAttrOrDefault(x_pipe, _Unicode(wall_thickness), 1 * mm);
0187     auto coating_thickness = getAttrOrDefault(x_pipe, _Unicode(coating_thickness), 30 * um);
0188 
0189     for (xml_coll_t x_zplane_i(x_pipe, _Unicode(zplane)); x_zplane_i; ++x_zplane_i) {
0190       xml_comp_t x_zplane = x_zplane_i;
0191       // z position
0192       z.push_back(x_zplane.attr<double>(_Unicode(z)));
0193       // outer radius
0194       rmax_wall.push_back(x_zplane.attr<double>(_Unicode(ID)) / 2.0 + wall_thickness);
0195       rmax_coating.push_back(x_zplane.attr<double>(_Unicode(ID)) / 2.0);
0196       rmax_vacuum.push_back(x_zplane.attr<double>(_Unicode(ID)) / 2.0 - coating_thickness);
0197       // inner radius
0198       rmin_wall.push_back(x_zplane.attr<double>(_Unicode(ID)) / 2.0);
0199       rmin_coating.push_back(x_zplane.attr<double>(_Unicode(ID)) / 2.0 - coating_thickness);
0200       rmin_vacuum.push_back(0);
0201     }
0202     return std::tuple<Polycone, Polycone, Polycone>({0, 2.0 * M_PI, rmin_wall, rmax_wall, z},
0203                                                     {0, 2.0 * M_PI, rmin_coating, rmax_coating, z},
0204                                                     {0, 2.0 * M_PI, rmin_vacuum, rmax_vacuum, z});
0205   };
0206   //---------------------------------------------------------------------------------------------------------
0207   // Helper function to create a racetrack volumes
0208   auto create_racetrack_solids = [](xml::Component& x_racetrack) {
0209     // get geometry parameters
0210     double semiCircle_rmin   = getAttrOrDefault(x_racetrack, _Unicode(semiCircle_rmin), 0.0);
0211     double wall_thickness    = getAttrOrDefault(x_racetrack, _Unicode(wall_thickness), 0.0);
0212     double coating_thickness = getAttrOrDefault(x_racetrack, _Unicode(coating_thickness), 0.0);
0213     double length            = getAttrOrDefault(x_racetrack, _Unicode(length), 0.0);
0214     double rectangle_h       = getAttrOrDefault(x_racetrack, _Unicode(rectangle_h), 0.0);
0215 
0216     // create semicylinders and boxes
0217     Tube wall_semiCircle(0, semiCircle_rmin + wall_thickness, length / 2.);
0218     Box wall_rectangle(semiCircle_rmin + wall_thickness, rectangle_h / 2., length / 2.);
0219 
0220     Tube coating_semiCircle(0, semiCircle_rmin, length / 2.);
0221     Box coating_rectangle(semiCircle_rmin, rectangle_h / 2., length / 2.);
0222 
0223     Tube vacuum_semiCircle(0, semiCircle_rmin - coating_thickness, length / 2.);
0224     Box vacuum_rectangle(semiCircle_rmin - coating_thickness, rectangle_h / 2., length / 2.);
0225 
0226     // unite semicylinders and boxes
0227     Transform3D upShift_tf(RotationZYX(0, 0, 0), Position(0, rectangle_h / 2., 0));
0228     Transform3D downShift_tf(RotationZYX(0, 0, 0), Position(0, -rectangle_h / 2., 0));
0229 
0230     UnionSolid wall_solid, coating_solid, vacuum_solid;
0231 
0232     wall_solid = UnionSolid("wall_solid_tmp", wall_rectangle, wall_semiCircle, upShift_tf);
0233     wall_solid = UnionSolid("wall_solid", wall_solid, wall_semiCircle, downShift_tf);
0234 
0235     coating_solid =
0236         UnionSolid("coating_solid_tmp", coating_rectangle, coating_semiCircle, upShift_tf);
0237     coating_solid = UnionSolid("coating_solid", coating_solid, coating_semiCircle, downShift_tf);
0238 
0239     vacuum_solid = UnionSolid("vacuum_solid_tmp", vacuum_rectangle, vacuum_semiCircle, upShift_tf);
0240     vacuum_solid = UnionSolid("vacuum_solid", vacuum_solid, vacuum_semiCircle, downShift_tf);
0241 
0242     // subtract vacuum
0243     BooleanSolid wall_subtract, coating_subtract, vacuum_subtract;
0244 
0245     wall_subtract = SubtractionSolid("wall_subtract", wall_solid, coating_solid, Transform3D());
0246     coating_subtract =
0247         SubtractionSolid("coating_subtract", coating_solid, vacuum_solid, Transform3D());
0248     vacuum_subtract = vacuum_solid;
0249 
0250     return std::tuple<Solid, Solid, Solid>(wall_subtract, coating_subtract, vacuum_subtract);
0251   };
0252   //---------------------------------------------------------------------------------------------------------
0253   // Helper function to build cones for interface
0254   auto build_interface_cones = [](std::vector<double>& wall_interface_cone1_rmax,
0255                                   std::vector<double>& coating_interface_cone1_rmax,
0256                                   std::vector<double>& vacuum_interface_cone1_rmax,
0257                                   std::vector<double>& wall_interface_cone2_rmax,
0258                                   std::vector<double>& coating_interface_cone2_rmax,
0259                                   std::vector<double>& vacuum_interface_cone2_rmax,
0260                                   std::vector<double>& interface_rmin,
0261                                   std::vector<double>& interface_z, double offset1_y,
0262                                   double offset2_y) {
0263     // wall cones
0264     Polycone wall_interface_cone1(0, 2.0 * M_PI, interface_rmin, wall_interface_cone1_rmax,
0265                                   interface_z);
0266     Polycone wall_interface_cone2(0, 2.0 * M_PI, interface_rmin, wall_interface_cone2_rmax,
0267                                   interface_z);
0268     // coating cones
0269     Polycone coating_interface_cone1(0, 2.0 * M_PI, interface_rmin, coating_interface_cone1_rmax,
0270                                      interface_z);
0271     Polycone coating_interface_cone2(0, 2.0 * M_PI, interface_rmin, coating_interface_cone2_rmax,
0272                                      interface_z);
0273     // vacuum cones
0274     Polycone vacuum_interface_cone1(0, 2.0 * M_PI, interface_rmin, vacuum_interface_cone1_rmax,
0275                                     interface_z);
0276     Polycone vacuum_interface_cone2(0, 2.0 * M_PI, interface_rmin, vacuum_interface_cone2_rmax,
0277                                     interface_z);
0278 
0279     Transform3D tf1(RotationZYX(0, 0, 0), Position(0, offset1_y, 0));
0280     Transform3D tf2(RotationZYX(0, 0, 0), Position(0, offset2_y, 0));
0281 
0282     // ---- Create wall
0283     // unite wall cones
0284     auto wall_interface_final = UnionSolid(wall_interface_cone1, wall_interface_cone2, tf1);
0285     wall_interface_final      = UnionSolid(wall_interface_final, wall_interface_cone2, tf2);
0286     Solid wall_interface_vac_cut(
0287         wall_interface_final); // cut cut out the interface volume from lepton beam pipe vac
0288     // subtract coating cones
0289     wall_interface_final =
0290         SubtractionSolid(wall_interface_final, coating_interface_cone1, Transform3D());
0291     wall_interface_final = SubtractionSolid(wall_interface_final, coating_interface_cone2, tf1);
0292     wall_interface_final = SubtractionSolid(wall_interface_final, coating_interface_cone2, tf2);
0293 
0294     // --- Create coating
0295     // unite coating cones
0296     auto coating_interface_final =
0297         UnionSolid(coating_interface_cone1, coating_interface_cone2, tf1);
0298     coating_interface_final = UnionSolid(coating_interface_final, coating_interface_cone2, tf2);
0299     // subtract vacuum cones
0300     coating_interface_final =
0301         SubtractionSolid(coating_interface_final, vacuum_interface_cone1, Transform3D());
0302     coating_interface_final =
0303         SubtractionSolid(coating_interface_final, vacuum_interface_cone2, tf1);
0304     coating_interface_final =
0305         SubtractionSolid(coating_interface_final, vacuum_interface_cone2, tf2);
0306 
0307     // --- Create vacuum
0308     // unite vacuum cones
0309     auto vacuum_interface_final = UnionSolid(vacuum_interface_cone1, vacuum_interface_cone2, tf1);
0310     vacuum_interface_final      = UnionSolid(vacuum_interface_final, vacuum_interface_cone2, tf2);
0311 
0312     return std::tuple<Solid, Solid, Solid, Solid>(wall_interface_final, coating_interface_final,
0313                                                   vacuum_interface_final, wall_interface_vac_cut);
0314   };
0315   //---------------------------------------------------------------------------------------------------------
0316   // Helper function to create an interface between racetrack and circular pipes
0317   auto create_interface_solids = [&](xml::Component& x_racetrack) {
0318     // get geometry parameters
0319     double semiCircle_rmin   = getAttrOrDefault(x_racetrack, _Unicode(semiCircle_rmin), 2.3 * cm);
0320     double wall_thickness    = getAttrOrDefault(x_racetrack, _Unicode(wall_thickness), 1.0 * mm);
0321     double coating_thickness = getAttrOrDefault(x_racetrack, _Unicode(coating_thickness), 30 * um);
0322     double rectangle_h       = getAttrOrDefault(x_racetrack, _Unicode(rectangle_h), 1.6 * cm);
0323     double cylRadius1        = getAttrOrDefault(x_racetrack, _Unicode(cylRadius1), 6.2 / 2. * cm);
0324     double cylRadius2        = getAttrOrDefault(x_racetrack, _Unicode(cylRadius2), 2.6 / 2. * cm);
0325     double straight_pipe_endz =
0326         getAttrOrDefault(x_racetrack, _Unicode(straight_pipe_endz), 66.385 * cm);
0327     double offset_z = getAttrOrDefault(x_racetrack, _Unicode(offset_z), 72.385 * cm);
0328     double length   = getAttrOrDefault(x_racetrack, _Unicode(length), 125.420 * cm);
0329     double interface_length_1 =
0330         getAttrOrDefault(x_racetrack, _Unicode(interface_length_1), 6.0 * cm);
0331     double interface_length_2 =
0332         getAttrOrDefault(x_racetrack, _Unicode(interface_length_2), 13.495 * cm);
0333 
0334     if (cylRadius2 - rectangle_h / 2. < 0) {
0335       printout(ERROR, "IP6BeamPipe_geo", "Negative cone base size: (%f - %f/2) < 0", cylRadius2,
0336                rectangle_h);
0337       throw std::runtime_error("IP6BeamPipe failed to build a cone");
0338     }
0339 
0340     std::vector<double> interface_rmin = {0, 0};
0341 
0342     // interface-1
0343     std::vector<double> interface_z_1 = {straight_pipe_endz,
0344                                          straight_pipe_endz + interface_length_1};
0345     // interface-2
0346     std::vector<double> interface_z_2 = {offset_z + length, offset_z + length + interface_length_2};
0347 
0348     // --- Central cones
0349     // interface-1
0350     std::vector<double> wall_interface_cone1_rmax_1    = {cylRadius1 + wall_thickness,
0351                                                           semiCircle_rmin + wall_thickness};
0352     std::vector<double> coating_interface_cone1_rmax_1 = {cylRadius1, semiCircle_rmin};
0353     std::vector<double> vacuum_interface_cone1_rmax_1  = {cylRadius1 - coating_thickness,
0354                                                           semiCircle_rmin - coating_thickness};
0355     // interface-2
0356     std::vector<double> wall_interface_cone1_rmax_2    = {semiCircle_rmin + wall_thickness,
0357                                                           cylRadius2 + wall_thickness};
0358     std::vector<double> coating_interface_cone1_rmax_2 = {semiCircle_rmin, cylRadius2};
0359     std::vector<double> vacuum_interface_cone1_rmax_2  = {semiCircle_rmin - coating_thickness,
0360                                                           cylRadius2 - coating_thickness};
0361 
0362     // --- Shifted cones
0363     // interface-1
0364     std::vector<double> wall_interface_cone2_rmax_1    = {semiCircle_rmin + wall_thickness,
0365                                                           semiCircle_rmin + wall_thickness};
0366     std::vector<double> coating_interface_cone2_rmax_1 = {semiCircle_rmin, semiCircle_rmin};
0367     std::vector<double> vacuum_interface_cone2_rmax_1  = {semiCircle_rmin - coating_thickness,
0368                                                           semiCircle_rmin - coating_thickness};
0369     // interface-2
0370     std::vector<double> wall_interface_cone2_rmax_2 = {
0371         semiCircle_rmin + wall_thickness, (cylRadius2 - rectangle_h / 2.) + wall_thickness};
0372     std::vector<double> coating_interface_cone2_rmax_2 = {semiCircle_rmin,
0373                                                           (cylRadius2 - rectangle_h / 2.)};
0374     std::vector<double> vacuum_interface_cone2_rmax_2  = {
0375         semiCircle_rmin - coating_thickness, (cylRadius2 - rectangle_h / 2.) - coating_thickness};
0376 
0377     // vertical shifts
0378     double offset1_y = rectangle_h / 2.;
0379     double offset2_y = -rectangle_h / 2.;
0380 
0381     // -- Build cones
0382     // interface-1
0383     auto interface_cones_1 = build_interface_cones(
0384         wall_interface_cone1_rmax_1, coating_interface_cone1_rmax_1, vacuum_interface_cone1_rmax_1,
0385         wall_interface_cone2_rmax_1, coating_interface_cone2_rmax_1, vacuum_interface_cone2_rmax_1,
0386         interface_rmin, interface_z_1, offset1_y, offset2_y);
0387     // interface-2
0388     auto interface_cones_2 = build_interface_cones(
0389         wall_interface_cone1_rmax_2, coating_interface_cone1_rmax_2, vacuum_interface_cone1_rmax_2,
0390         wall_interface_cone2_rmax_2, coating_interface_cone2_rmax_2, vacuum_interface_cone2_rmax_2,
0391         interface_rmin, interface_z_2, offset1_y, offset2_y);
0392 
0393     return std::tuple<Solid, Solid, Solid, Solid, Solid, Solid, Solid, Solid>(
0394         std::get<0>(interface_cones_1), std::get<1>(interface_cones_1),
0395         std::get<2>(interface_cones_1), std::get<3>(interface_cones_1),
0396         std::get<0>(interface_cones_2), std::get<1>(interface_cones_2),
0397         std::get<2>(interface_cones_2), std::get<3>(interface_cones_2));
0398   };
0399   //---------------------------------------------------------------------------------------------------------
0400   // Helper function to create union of lepton and hadron volumes
0401   auto create_volumes = [&](const std::string& name, xml::Component& x_pipe1,
0402                             xml::Component& x_pipe2, xml_coll_t& x_additional_subtraction_i) {
0403     auto pipe1_polycones = zplane_to_polycones(x_pipe1);
0404     auto pipe2_polycones = zplane_to_polycones(x_pipe2);
0405 
0406     auto crossing_angle    = getAttrOrDefault(x_pipe2, _Unicode(crossing_angle), 0.0);
0407     auto axis_intersection = getAttrOrDefault(x_pipe2, _Unicode(axis_intersection), 0.0);
0408 
0409     auto tf = Transform3D(Position(0, 0, axis_intersection)) *
0410               Transform3D(RotationY(crossing_angle)) *
0411               Transform3D(Position(0, 0, -axis_intersection));
0412 
0413     // union of wall, coating, and vacuum
0414     BooleanSolid wall_union =
0415         UnionSolid(std::get<0>(pipe1_polycones), std::get<0>(pipe2_polycones), tf);
0416     BooleanSolid coating_union =
0417         UnionSolid(std::get<1>(pipe1_polycones), std::get<1>(pipe2_polycones), tf);
0418     BooleanSolid vacuum_union =
0419         UnionSolid(std::get<2>(pipe1_polycones), std::get<2>(pipe2_polycones), tf);
0420 
0421     BooleanSolid wall_racetrack_final, coating_racetrack_final;
0422     BooleanSolid wall_interface_final_1, coating_interface_final_1;
0423     BooleanSolid wall_interface_final_2, coating_interface_final_2;
0424     BooleanSolid vacuum_interface, lepton_pipe_vac;
0425 
0426     // --- Create a vacuum cylinder to place a lepton pipe inside and cut it out of the hadron cone vacuum
0427     // --- This helps to avoid problems with nested solid unions and subtractions for the hadron vacuum
0428     std::vector<double> posz_vac = {
0429         getAttrOrDefault(x_pipe1, _Unicode(lepton_pipe_vac_tube_startz), 66.10 * cm),
0430         getAttrOrDefault(x_pipe1, _Unicode(lepton_pipe_vac_tube_endz), 494.556 * cm)};
0431     std::vector<double> rmin_vac = {0, 0};
0432     std::vector<double> rmax_vac = {
0433         getAttrOrDefault(x_pipe1, _Unicode(ipBeampipe_ID), 6.2 / 2. * cm) / 2. +
0434             getAttrOrDefault(x_pipe1, _Unicode(wall_thickness), 1.0 * mm),
0435         getAttrOrDefault(x_pipe1, _Unicode(ipBeampipe_ID), 6.2 / 2. * cm) / 2. +
0436             getAttrOrDefault(x_pipe1, _Unicode(wall_thickness), 1.0 * mm)};
0437     Polycone lepton_pipe_vac_tube(0, 2.0 * M_PI, rmin_vac, rmax_vac, posz_vac);
0438 
0439     // downstream side is more complex and requires additional effort to create all the volumes
0440     if (name == "downstream") {
0441       // subtract the lepton beam pipe vacuum from the hadron beam pipe vacuum
0442       vacuum_union = SubtractionSolid("vacuum_union", vacuum_union, lepton_pipe_vac_tube);
0443 
0444       xml::Component racetrack_lepton_c = x_pipe1.child(_Unicode(racetrack_lepton));
0445 
0446       // ---- Read geometry parameters ----
0447       double cylRadius1 = // cylinder radius on the IP side
0448           getAttrOrDefault(racetrack_lepton_c, _Unicode(cylRadius1), 6.2 / 2. * cm);
0449       double rtRadius = // racetrack radius
0450           getAttrOrDefault(racetrack_lepton_c, _Unicode(semiCircle_rmin), 2.3 * cm);
0451       double wall_thickness = // wall thickness
0452           getAttrOrDefault(racetrack_lepton_c, _Unicode(wall_thickness), 1.0 * mm);
0453       double coating_thickness = // coating thickness
0454           getAttrOrDefault(racetrack_lepton_c, _Unicode(coating_thickness), 30.0 * um);
0455       double straight_pipe_startz = // straight pipe on the IP side, start position
0456           getAttrOrDefault(racetrack_lepton_c, _Unicode(straight_pipe_startz), 66.10 * cm);
0457       double straight_pipe_endz = // straight pipe on the IP side, end position
0458           getAttrOrDefault(racetrack_lepton_c, _Unicode(straight_pipe_endz), 66.385 * cm);
0459       double elliptical_cut_rx_1 = // elliptical cut (IP side) rX for the hadron beam opening
0460           getAttrOrDefault(racetrack_lepton_c, _Unicode(elliptical_cut_rx_1), 0.305 * m);
0461       double elliptical_cut_ry_1 = // elliptical cut (IP side) rY for the hadron beam opening
0462           getAttrOrDefault(racetrack_lepton_c, _Unicode(elliptical_cut_ry_1), 0.021 * m);
0463       double elliptical_cut_rx_2 = // elliptical cut (non-IP side) rX for the hadron beam opening
0464           getAttrOrDefault(racetrack_lepton_c, _Unicode(elliptical_cut_rx_2), 0.152 * m);
0465       double elliptical_cut_ry_2 = // elliptical cut (non-IP side) rY for the hadron beam opening
0466           getAttrOrDefault(racetrack_lepton_c, _Unicode(elliptical_cut_ry_2), 0.021 * m);
0467       double rectangular_cut_a = // rectangular cut A for the hadron beam opening
0468           getAttrOrDefault(racetrack_lepton_c, _Unicode(rectangular_cut_a), 0.81 / 2. * m);
0469       double rectangular_cut_b = // rectangular cut B for the hadron beam opening
0470           getAttrOrDefault(racetrack_lepton_c, _Unicode(rectangular_cut_b), 0.021 * m);
0471       double elliptical_cut_dz = // thickness of the cut for the hadron beam opening
0472           getAttrOrDefault(racetrack_lepton_c, _Unicode(elliptical_cut_dz), 0.7 * cm);
0473       double elliptical_cut_offset_z_1 = // offset of the elliptical cut (IP side)
0474           getAttrOrDefault(racetrack_lepton_c, _Unicode(elliptical_cut_offset_z_1), (0.976) * m);
0475       double elliptical_cut_offset_z_2 = // offset of the elliptical cut (non-IP side)
0476           getAttrOrDefault(racetrack_lepton_c, _Unicode(elliptical_cut_offset_z_2),
0477                            (0.976 + 0.810) * m);
0478       double rectangular_cut_offset_z = // offset of the rectangular cut
0479           getAttrOrDefault(racetrack_lepton_c, _Unicode(rectangular_cut_offset_z),
0480                            (0.976 + 0.810 / 2.) * m);
0481 
0482       // ---- Create racetrack solids ----
0483       auto racetrack_solids = create_racetrack_solids(racetrack_lepton_c);
0484 
0485       // ---- Create an interface between racetrack and cylindrical beam pipe ----
0486       auto interface_solids = create_interface_solids(racetrack_lepton_c);
0487       vacuum_interface      = // unite interface vacuum solids
0488           UnionSolid("vacuum_interface", std::get<2>(interface_solids),
0489                      std::get<6>(interface_solids), Transform3D());
0490 
0491       // --- Create a straight pipe on the IP side ---
0492       std::vector<double> z            = {straight_pipe_startz, straight_pipe_endz};
0493       std::vector<double> wall_rmin    = {cylRadius1, cylRadius1};
0494       std::vector<double> wall_rmax    = {cylRadius1 + wall_thickness, cylRadius1 + wall_thickness};
0495       std::vector<double> coating_rmin = {cylRadius1 - coating_thickness,
0496                                           cylRadius1 - coating_thickness};
0497       std::vector<double> coating_rmax = wall_rmin;
0498 
0499       Polycone wall_pipe(0, 2.0 * M_PI, wall_rmin, wall_rmax, z);
0500       Polycone coating_pipe(0, 2.0 * M_PI, coating_rmin, coating_rmax, z);
0501 
0502       Polycone wall_pipe_2(0, 2.0 * M_PI, wall_rmin, wall_rmax, z);
0503 
0504       // --- Unite racetrack and straight pipe with the rest of the pipe
0505       double offset_z = getAttrOrDefault(racetrack_lepton_c, _Unicode(offset_z), 0.0);
0506       double length   = getAttrOrDefault(racetrack_lepton_c, _Unicode(length), 0.0);
0507       Transform3D racetrack_tf(RotationZYX(0, 0, 0), Position(0, 0, offset_z + length / 2.));
0508 
0509       UnionSolid wall_racetrack_pipe =
0510           UnionSolid("wall_racetrack_pipe", wall_pipe, std::get<0>(racetrack_solids), racetrack_tf);
0511       UnionSolid coating_racetrack_pipe = UnionSolid("coating_racetrack_pipe", coating_pipe,
0512                                                      std::get<1>(racetrack_solids), racetrack_tf);
0513 
0514       // --- Subtract racetrack and interface sections from vacuum
0515       lepton_pipe_vac = SubtractionSolid("vacuum_racetrack_cut_1", lepton_pipe_vac_tube,
0516                                          wall_racetrack_pipe, Transform3D());
0517       lepton_pipe_vac = SubtractionSolid("vacuum_racetrack_cut_2", lepton_pipe_vac,
0518                                          coating_racetrack_pipe, Transform3D());
0519       lepton_pipe_vac = SubtractionSolid("vacuum_interface_cut_3", lepton_pipe_vac,
0520                                          std::get<3>(interface_solids), Transform3D());
0521       lepton_pipe_vac = SubtractionSolid("vacuum_interface_cut_4", lepton_pipe_vac,
0522                                          std::get<7>(interface_solids), Transform3D());
0523       // --- Subtract wall and coating from vacuum
0524       lepton_pipe_vac =
0525           SubtractionSolid("vacuum_interface_cut_5", lepton_pipe_vac, wall_union, Transform3D());
0526       lepton_pipe_vac =
0527           SubtractionSolid("vacuum_interface_cut_6", lepton_pipe_vac, coating_union, Transform3D());
0528 
0529       // create a cut volume - vacuum = two ellipses + rectangle
0530       EllipticalTube elliptical_cut_1("elliptical_cut_1", elliptical_cut_rx_1, elliptical_cut_ry_1,
0531                                       elliptical_cut_dz);
0532       EllipticalTube elliptical_cut_2("elliptical_cut_2", elliptical_cut_rx_2, elliptical_cut_ry_2,
0533                                       elliptical_cut_dz);
0534       Box rectangular_cut_3("rectangular_cut_3", rectangular_cut_a, rectangular_cut_b,
0535                             elliptical_cut_dz);
0536       Transform3D tf_cut_1(RotationZYX(0, M_PI_2, 0),
0537                            Position(-rtRadius, 0, elliptical_cut_offset_z_1));
0538       Transform3D tf_cut_2(RotationZYX(0, M_PI_2, 0),
0539                            Position(-rtRadius, 0, elliptical_cut_offset_z_2));
0540       Transform3D tf_cut_3(RotationZYX(0, M_PI_2, 0),
0541                            Position(-rtRadius, 0, rectangular_cut_offset_z));
0542 
0543       // subtract from racetrack wall
0544       SubtractionSolid wall_racetrack_cut_1 =
0545           SubtractionSolid("wall_racetrack_cut_1", wall_racetrack_pipe, elliptical_cut_1, tf_cut_1);
0546       SubtractionSolid wall_racetrack_cut_2 = SubtractionSolid(
0547           "wall_racetrack_cut_1", wall_racetrack_cut_1, elliptical_cut_2, tf_cut_2);
0548       wall_racetrack_final = SubtractionSolid("wall_racetrack_final", wall_racetrack_cut_2,
0549                                               rectangular_cut_3, tf_cut_3);
0550 
0551       // subtract from racetrack coating
0552       SubtractionSolid coating_racetrack_cut_1 = SubtractionSolid(
0553           "coating_racetrack_cut_1", coating_racetrack_pipe, elliptical_cut_1, tf_cut_1);
0554       SubtractionSolid coating_racetrack_cut_2 = SubtractionSolid(
0555           "coating_racetrack_cut_2", coating_racetrack_cut_1, elliptical_cut_2, tf_cut_2);
0556       coating_racetrack_final = SubtractionSolid("coating_racetrack_final", coating_racetrack_cut_2,
0557                                                  rectangular_cut_3, tf_cut_3);
0558       // unite with lepton pipe vacuum
0559       lepton_pipe_vac =
0560           UnionSolid("lepton_pipe_vac_uni_7", lepton_pipe_vac, elliptical_cut_1, tf_cut_1);
0561       lepton_pipe_vac =
0562           UnionSolid("lepton_pipe_vac_uni_8", lepton_pipe_vac, elliptical_cut_2, tf_cut_2);
0563       lepton_pipe_vac =
0564           UnionSolid("lepton_pipe_vac_uni_9", lepton_pipe_vac, rectangular_cut_3, tf_cut_3);
0565 
0566       // subtract from hadron cone pipe vacuum
0567       vacuum_union =
0568           SubtractionSolid("vacuum_union_sub_1", vacuum_union, elliptical_cut_1, tf_cut_1);
0569       vacuum_union =
0570           SubtractionSolid("vacuum_union_sub_2", vacuum_union, elliptical_cut_2, tf_cut_2);
0571       vacuum_union =
0572           SubtractionSolid("vacuum_union_sub_3", vacuum_union, rectangular_cut_3, tf_cut_3);
0573 
0574       // subtract from interface-1 wall
0575       wall_interface_final_1 = SubtractionSolid(
0576           "wall_interface_final_1", std::get<0>(interface_solids), elliptical_cut_1, tf_cut_1);
0577 
0578       // subtract from interface-1 coating
0579       coating_interface_final_1 = SubtractionSolid(
0580           "coating_interface_final_1", std::get<1>(interface_solids), elliptical_cut_1, tf_cut_1);
0581 
0582       // subtract from interface vacuum
0583       vacuum_interface =
0584           SubtractionSolid("vacuum_interface", vacuum_interface, elliptical_cut_1, tf_cut_1);
0585 
0586       // get  interface-2 w/o subtraction
0587       wall_interface_final_2    = std::get<4>(interface_solids);
0588       coating_interface_final_2 = std::get<5>(interface_solids);
0589     }
0590 
0591     Solid wall_racetrack(wall_racetrack_final);
0592     Solid coating_racetrack(coating_racetrack_final);
0593     Solid wall_interface_1(wall_interface_final_1);
0594     Solid coating_interface_1(coating_interface_final_1);
0595     Solid wall_interface_2(wall_interface_final_2);
0596     Solid coating_interface_2(coating_interface_final_2);
0597 
0598     // subtract additional vacuum from wall and coating
0599     for (; x_additional_subtraction_i; ++x_additional_subtraction_i) {
0600       xml_comp_t x_additional_subtraction = x_additional_subtraction_i;
0601       auto additional_polycones           = zplane_to_polycones(x_additional_subtraction);
0602       auto additional_crossing_angle =
0603           getAttrOrDefault(x_additional_subtraction, _Unicode(crossing_angle), 0.0);
0604       auto additional_axis_intersection =
0605           getAttrOrDefault(x_additional_subtraction, _Unicode(axis_intersection), 0.0);
0606       auto additional_tf = Transform3D(Position(0, 0, additional_axis_intersection)) *
0607                            Transform3D(RotationY(additional_crossing_angle)) *
0608                            Transform3D(Position(0, 0, -additional_axis_intersection));
0609 
0610       wall_union = SubtractionSolid(wall_union, std::get<2>(additional_polycones), additional_tf);
0611       coating_union =
0612           SubtractionSolid(coating_union, std::get<2>(additional_polycones), additional_tf);
0613       vacuum_union =
0614           SubtractionSolid(vacuum_union, std::get<2>(additional_polycones), additional_tf);
0615     }
0616 
0617     Solid wall, coating, vacuum, wall_ipflange, coating_ipflange, vacuum_ipflange;
0618 
0619     if (name == "upstream") // upstream
0620     {
0621       // subtract vacuum from coating
0622       coating = SubtractionSolid(coating_union, vacuum_union);
0623 
0624       // subtract vacuum from wall
0625       wall = SubtractionSolid(wall_union, vacuum_union);
0626 
0627       // get vacuum
0628       vacuum = vacuum_union;
0629     } else // downstream
0630     {
0631       // subtract walls and coatings from vacuum
0632       vacuum_union =
0633           SubtractionSolid("vacuum_union_sub_2", vacuum_union, wall_union, Transform3D());
0634       vacuum_union =
0635           SubtractionSolid("vacuum_union_sub_3", vacuum_union, coating_union, Transform3D());
0636 
0637       // get vacuum
0638       vacuum = vacuum_union;
0639 
0640       // get wall
0641       wall = wall_union;
0642 
0643       // get coating
0644       coating = coating_union;
0645 
0646       // get a flange between the FWD and IP beam pipes
0647       xml::Component fwdipflange_c = x_pipe1.child(_Unicode(fwdipflange));
0648       auto fwdipflange_polycones   = zplane_to_polycones(fwdipflange_c);
0649 
0650       wall_ipflange    = std::get<0>(fwdipflange_polycones);
0651       coating_ipflange = std::get<1>(fwdipflange_polycones);
0652       vacuum_ipflange  = std::get<2>(fwdipflange_polycones);
0653     }
0654 
0655     return std::tuple<Volume, Volume, Volume, Volume, Volume, Volume, Volume, Volume, Volume,
0656                       Volume, Volume, Volume, Volume, Volume>(
0657         {"v_" + name + "_wall", wall, m_Wall}, {"v_" + name + "_coating", coating, m_Coating},
0658         {"v_" + name + "_vacuum", vacuum, m_Vacuum},
0659         {"v_" + name + "_vacuum_ipflange", vacuum_ipflange, m_Vacuum},
0660         {"v_" + name + "_wall_racetrack", wall_racetrack, m_Wall},
0661         {"v_" + name + "_coating_racetrack", coating_racetrack, m_Coating},
0662         {"v_" + name + "_wall_ipflange", wall_ipflange, m_Wall},
0663         {"v_" + name + "_coating_ipflange", coating_ipflange, m_Coating},
0664         {"v_" + name + "_wall_interface_1", wall_interface_1, m_Wall},
0665         {"v_" + name + "_coating_interface_1", coating_interface_1, m_Coating},
0666         {"v_" + name + "_wall_interface_2", wall_interface_2, m_Wall},
0667         {"v_" + name + "_coating_interface_2", coating_interface_2, m_Coating},
0668         {"v_" + name + "_vacuum_interface", vacuum_interface, m_Vacuum},
0669         {"v_" + name + "_lepton_pipe_vac", lepton_pipe_vac, m_Vacuum});
0670   };
0671 
0672   // -----------------------------
0673   // Upstream/BWD/Rear Side:
0674   // - incoming hadron tube: straight
0675   // - outgoing electron tube: tapering
0676 
0677   xml::Component upstream_c        = x_det.child(_Unicode(upstream));
0678   xml::Component incoming_hadron_c = upstream_c.child(_Unicode(incoming_hadron));
0679   xml::Component outgoing_lepton_c = upstream_c.child(_Unicode(outgoing_lepton));
0680   xml_coll_t additional_subtractions_upstream(upstream_c, _Unicode(additional_subtraction));
0681   auto volumes_upstream = create_volumes("upstream", outgoing_lepton_c, incoming_hadron_c,
0682                                          additional_subtractions_upstream);
0683 
0684   std::get<0>(volumes_upstream).setVisAttributes(wallVis);
0685   std::get<1>(volumes_upstream).setVisAttributes(coatingVis);
0686 
0687   auto tf_upstream = Transform3D(RotationZYX(0, 0, 0));
0688   if (getAttrOrDefault<bool>(upstream_c, _Unicode(reflect), true)) {
0689     tf_upstream = Transform3D(RotationZYX(0, M_PI, 0));
0690   }
0691   // place wall
0692   assembly.placeVolume(std::get<0>(volumes_upstream), tf_upstream);
0693   // place coating
0694   assembly.placeVolume(std::get<1>(volumes_upstream), tf_upstream);
0695   // place vacuum
0696   if (getAttrOrDefault<bool>(upstream_c, _Unicode(place_vacuum), true)) {
0697     assembly.placeVolume(std::get<2>(volumes_upstream), tf_upstream);
0698   }
0699 
0700   // -----------------------------
0701   // Downstream/FWD Side:
0702   // - incoming electron tube: tube with tube cut out
0703   // - outgoing hadron tube: cone centered at scattering angle
0704 
0705   xml::Component downstream_c      = x_det.child(_Unicode(downstream));
0706   xml::Component incoming_lepton_c = downstream_c.child(_Unicode(incoming_lepton));
0707   xml::Component outgoing_hadron_c = downstream_c.child(_Unicode(outgoing_hadron));
0708   xml_coll_t additional_subtractions_downstream(downstream_c, _Unicode(additional_subtraction));
0709   auto volumes_downstream = create_volumes("downstream", incoming_lepton_c, outgoing_hadron_c,
0710                                            additional_subtractions_downstream);
0711 
0712   // add colors
0713   std::get<0>(volumes_downstream).setVisAttributes(wallVis);
0714   std::get<1>(volumes_downstream).setVisAttributes(coatingVis);
0715   std::get<4>(volumes_downstream).setVisAttributes(wallVis);
0716   std::get<5>(volumes_downstream).setVisAttributes(coatingVis);
0717   std::get<6>(volumes_downstream).setVisAttributes(wallVis);
0718   std::get<7>(volumes_downstream).setVisAttributes(coatingVis);
0719   std::get<8>(volumes_downstream).setVisAttributes(wallVis);
0720   std::get<9>(volumes_downstream).setVisAttributes(coatingVis);
0721   std::get<10>(volumes_downstream).setVisAttributes(wallVis);
0722   std::get<11>(volumes_downstream).setVisAttributes(coatingVis);
0723 
0724   auto tf_downstream = Transform3D(RotationZYX(0, 0, 0));
0725   if (getAttrOrDefault<bool>(downstream_c, _Unicode(reflect), true)) {
0726     tf_downstream = Transform3D(RotationZYX(0, M_PI, 0));
0727   }
0728 
0729   // place wall
0730   assembly.placeVolume(std::get<0>(volumes_downstream), tf_downstream);
0731   // place coating
0732   assembly.placeVolume(std::get<1>(volumes_downstream), tf_downstream);
0733   // place vacuum
0734   if (getAttrOrDefault<bool>(downstream_c, _Unicode(place_vacuum), true)) {
0735     assembly.placeVolume(std::get<2>(volumes_downstream), tf_downstream);
0736     assembly.placeVolume(std::get<3>(volumes_downstream), tf_downstream);
0737     assembly.placeVolume(std::get<12>(volumes_downstream), tf_downstream);
0738     assembly.placeVolume(std::get<13>(volumes_downstream), tf_downstream);
0739   }
0740   // place racetrack wall
0741   assembly.placeVolume(std::get<4>(volumes_downstream), tf_downstream);
0742   // place racetrack coating
0743   assembly.placeVolume(std::get<5>(volumes_downstream), tf_downstream);
0744   // place FWD IP flange wall
0745   assembly.placeVolume(std::get<6>(volumes_downstream), tf_downstream);
0746   // place FWD IP flange coating
0747   assembly.placeVolume(std::get<7>(volumes_downstream), tf_downstream);
0748   // place interface wall
0749   assembly.placeVolume(std::get<8>(volumes_downstream), tf_downstream);
0750   assembly.placeVolume(std::get<10>(volumes_downstream), tf_downstream);
0751   // place interface coating
0752   assembly.placeVolume(std::get<9>(volumes_downstream), tf_downstream);
0753   assembly.placeVolume(std::get<11>(volumes_downstream), tf_downstream);
0754 
0755   // -----------------------------
0756   // final placement
0757   auto pv_assembly = det.pickMotherVolume(sdet).placeVolume(assembly);
0758   pv_assembly.addPhysVolID("system", sdet.id());
0759   sdet.setPlacement(pv_assembly);
0760   assembly->GetShape()->ComputeBBox();
0761 
0762   return sdet;
0763 }
0764 
0765 DECLARE_DETELEMENT(IP6BeamPipe, create_detector)