static Ref_t create_detector(Detector& theDetector, xml_h e, SensitiveDetector sens)  {
    xml_det_t   x_det     = e;
    int         det_id    = x_det.id();
    string      det_name  = x_det.nameStr();
    DetElement    sdet      (det_name,det_id);
    
    // --- create an envelope volume and position it into the world ---------------------
    
    Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector,  e , sdet ) ;
    dd4hep::xml::setDetectorTypeFlag( e, sdet ) ;
    
    if( theDetector.buildType() == BUILD_ENVELOPE ) return sdet ;
    
    //-----------------------------------------------------------------------------------
    
    xml_dim_t   dim       = x_det.dimensions();
    Material    air       = theDetector.air();
    int         nsides_inner = dim.nsides_inner();
    int         nsides_outer = dim.nsides_outer();
    double      rmin      = dim.rmin();
    double      rmax      = dim.rmax(); /// FIXME: IS THIS RIGHT?
    double      zmin      = dim.zmin();
    
    double       rcutout   = dim.hasAttr(_U(rmin2)) ? dim.rmin2() : 0.;
    double       zcutout   = dim.hasAttr(_U(z2)) ? dim.z2() : 0.;
    
    Layering    layering(x_det);
    double      totalThickness = layering.totalThickness();
    Readout readout = sens.readout();
    Segmentation seg = readout.segmentation();
    
    std::vector<double> cellSizeVector = seg.segmentation()->cellDimensions(0); //Assume uniform cell sizes, provide dummy cellID
    double cell_sizeX      = cellSizeVector[0];
    double cell_sizeY      = cellSizeVector[1];
    
    
    PolyhedraRegular polyVolume(nsides_outer,rmin,rmax,totalThickness);
    Volume      endcapVol("endcap",polyVolume,air);
    
    
    if(zcutout >0. || rcutout > 0.){
        PolyhedraRegular cutoutPolyVolume(nsides_inner,0,rmin+rcutout,zcutout);
        Position cutoutPos(0,0,(zcutout-totalThickness)/2.0);
        std::cout<<"Cutout z width will be  "<<zcutout<<std::endl; 
        endcapVol=Volume("endcap",SubtractionSolid(polyVolume,cutoutPolyVolume,cutoutPos),air);
        
    }
    
    
    DetElement  endcapA(sdet,"endcap",det_id);
    Ref_t(endcapA)->SetName((det_name+"_A").c_str());
    
    int layer_num = 0;
    int layerType   = 0;
    double layerZ   = -totalThickness/2;
    
    //Create caloData object to extend driver with data required for reconstruction
    LayeredCalorimeterData* caloData = new LayeredCalorimeterData ;
    caloData->layoutType = LayeredCalorimeterData::EndcapLayout ;
    caloData->inner_symmetry = nsides_inner;
    caloData->outer_symmetry = nsides_outer; 
    
    /** NOTE: phi0=0 means lower face flat parallel to experimental floor
     *  This is achieved by rotating the modules with respect to the envelope
     *  which is assumed to be a Polyhedron and has its axes rotated with respect
     *  to the world by 180/nsides. In any other case (e.g. if you want to have
     *  a tip of the calorimeter touching the ground) this value needs to be computed
     */
    
    caloData->inner_phi0 = 0.; 
    caloData->outer_phi0 = 0.; 
    caloData->gap0 = 0.; //FIXME
    caloData->gap1 = 0.; //FIXME
    caloData->gap2 = 0.; //FIXME  
    
    
    /// extent of the calorimeter in the r-z-plane [ rmin, rmax, zmin, zmax ] in mm.
    caloData->extent[0] = rmin ;
    caloData->extent[1] = rmax ; ///FIXME: CHECK WHAT IS NEEDED (EXSCRIBED?)
    caloData->extent[2] = zmin ;
    caloData->extent[3] = zmin + totalThickness;
    
    endcapVol.setAttributes(theDetector,x_det.regionStr(),x_det.limitsStr(),x_det.visStr());
    
    for(xml_coll_t c(x_det,_U(layer)); c; ++c)  {
        xml_comp_t       x_layer  = c;
        double           layer_thick  = layering.layer(layer_num)->thickness();
        string           layer_type_name   = _toString(layerType,"layerType%d");
        int              layer_repeat = x_layer.repeat();
        double            layer_rcutout = x_layer.hasAttr(_U(gap)) ? x_layer.gap() : 0;
        
        std::cout<<"Number of layers in group "<<layerType<<" : "<<layer_repeat<<std::endl; 
        
        Volume           layer_vol(layer_type_name,PolyhedraRegular(nsides_outer,rmin+layer_rcutout,rmax,layer_thick),air);
        
        int slice_num = 0;
        double sliceZ = -layer_thick/2;
        
        //Create a caloLayer struct for thiss layer type to store copies of in the parent struct
        LayeredCalorimeterData::Layer caloLayer ;
        caloLayer.cellSize0 = cell_sizeX;
        caloLayer.cellSize1 = cell_sizeY; 
        
        double nRadiationLengths=0.;
        double nInteractionLengths=0.;
        double thickness_sum=0;
        
        for(xml_coll_t s(x_layer,_U(slice)); s; ++s)  {
            xml_comp_t x_slice = s;
            string     slice_name  = _toString(slice_num,"slice%d");
            double     slice_thickness = x_slice.thickness();
            Material   slice_material   = theDetector.material(x_slice.materialStr());
            Volume     slice_vol(slice_name,PolyhedraRegular(nsides_outer,rmin+layer_rcutout,rmax,slice_thickness),slice_material);
            
            slice_vol.setVisAttributes(theDetector.visAttributes(x_slice.visStr()));
            sliceZ += slice_thickness/2;
            layer_vol.placeVolume(slice_vol,Position(0,0,sliceZ));
            
            nRadiationLengths += slice_thickness/(2.*slice_material.radLength());
            nInteractionLengths += slice_thickness/(2.*slice_material.intLength());
            thickness_sum += slice_thickness/2;
            
            if ( x_slice.isSensitive() )  {
                sens.setType("calorimeter");
                slice_vol.setSensitiveDetector(sens);
                
#if DD4HEP_VERSION_GE( 0, 15 )
                //Store "inner" quantities
                caloLayer.inner_nRadiationLengths = nRadiationLengths;
                caloLayer.inner_nInteractionLengths = nInteractionLengths;
                caloLayer.inner_thickness = thickness_sum;
                //Store scintillator thickness
                caloLayer.sensitive_thickness = slice_thickness;
#endif
                //Reset counters to measure "outside" quantitites
                nRadiationLengths=0.;
                nInteractionLengths=0.;
                thickness_sum = 0.;
            } 
            
            nRadiationLengths += slice_thickness/(2.*slice_material.radLength());
            nInteractionLengths += slice_thickness/(2.*slice_material.intLength());
            thickness_sum += slice_thickness/2;

            sliceZ += slice_thickness/2;
            slice_num++;
        }
        
#if DD4HEP_VERSION_GE( 0, 15 )
        //Store "outer" quantities
        caloLayer.outer_nRadiationLengths = nRadiationLengths;
        caloLayer.outer_nInteractionLengths = nInteractionLengths;
        caloLayer.outer_thickness = thickness_sum;
#endif        
        layer_vol.setVisAttributes(theDetector.visAttributes(x_layer.visStr()));
        
        
        if ( layer_repeat <= 0 ) throw std::runtime_error(x_det.nameStr()+"> Invalid repeat value");
        
        for(int j=0; j<layer_repeat; ++j) {
            string phys_lay = _toString(layer_num,"layer%d");
            
            //The rest of the data is constant; only the distance needs to be updated  
            //Store the position up to the inner face of the layer
            caloLayer.distance = zmin +  totalThickness/2 + layerZ;
            //Push back a copy to the caloData structure
            caloData->layers.push_back( caloLayer );
            
            layerZ += layer_thick/2;
            DetElement    layer_elt(endcapA, phys_lay, layer_num);
            PlacedVolume  pv = endcapVol.placeVolume(layer_vol,Position(0,0,layerZ));
            pv.addPhysVolID("layer", layer_num);
            layer_elt.setPlacement(pv);
            
            layerZ += layer_thick/2;
            ++layer_num;
        }
        ++layerType;
    }
    
    double z_pos = zmin+totalThickness/2;
    PlacedVolume pv;
    // Reflect it.
    
    DetElement  endcapB = endcapA.clone(det_name+"_B",x_det.id());
    
    //Removed rotations to align with envelope
    //NOTE: If the envelope is not a polyhedron (eg. if you use a tube)
    //you may need to rotate so the axes match
    
    pv = envelope.placeVolume(endcapVol,Transform3D(RotationZYX(0,0,0),
                                                    Position(0,0,z_pos)));
    pv.addPhysVolID("side", 1);
    endcapA.setPlacement(pv);
    
    //Removed rotations
    pv = envelope.placeVolume(endcapVol,Transform3D(RotationZYX(0,M_PI,0),
                                                    Position(0,0,-z_pos)));
    pv.addPhysVolID("side", 2);
    endcapB.setPlacement(pv);
    
    sdet.add(endcapB);
    
    sdet.addExtension< LayeredCalorimeterData >( caloData ) ;
    
    return sdet;
    
}
Example #2
0
static Ref_t create_detector(Detector& theDetector,
                             xml_h element,
                             SensitiveDetector sens) {
    
    std::cout << __PRETTY_FUNCTION__  << std::endl;
    std::cout << "Here is my LumiCal"  << std::endl;
    std::cout << " and this is the sensitive detector: " << &sens  << std::endl;
    sens.setType("calorimeter");
    //Materials
    Material air = theDetector.air();
    
    //Access to the XML File
    xml_det_t     xmlLumiCal    = element;
    const std::string detName   = xmlLumiCal.nameStr();
    
    DetElement sdet ( detName, xmlLumiCal.id() );
    
    // --- create an envelope volume and position it into the world ---------------------
    
    Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector, element , sdet ) ;
    DetElement lumiCalDE_1(sdet,"Calorimeter1",1);
    DetElement lumiCalDE_2(sdet,"Calorimeter2",2);

    sdet.setTypeFlag( DetType::CALORIMETER |  DetType::ENDCAP  | DetType::ELECTROMAGNETIC |  DetType::FORWARD ) ;

    if( theDetector.buildType() == BUILD_ENVELOPE ) return sdet ;
    
    //-----------------------------------------------------------------------------------
    
    dd4hep::xml::Dimension dimensions =  xmlLumiCal.dimensions();
    
    //LumiCal Dimensions
    const double lcalInnerR = dimensions.inner_r();
    const double lcalOuterR = dimensions.outer_r();
    const double lcalInnerZ = dimensions.inner_z();
    const double lcalThickness = Layering(xmlLumiCal).totalThickness();
    const double lcalCentreZ = lcalInnerZ+lcalThickness*0.5;
    
    double LumiCal_cell_size      = theDetector.constant<double>("LumiCal_cell_size");
    //========== fill data for reconstruction ============================
    LayeredCalorimeterData* caloData = new LayeredCalorimeterData ;
    caloData->layoutType = LayeredCalorimeterData::EndcapLayout ;
    caloData->inner_symmetry = 0  ; // hardcoded tube
    caloData->outer_symmetry = 0  ; 
    caloData->phi0 = 0 ;
    
    /// extent of the calorimeter in the r-z-plane [ rmin, rmax, zmin, zmax ] in mm.
    caloData->extent[0] = lcalInnerR ;
    caloData->extent[1] = lcalOuterR ;
    caloData->extent[2] = lcalInnerZ ;
    caloData->extent[3] = lcalInnerZ + lcalThickness ;
    
    // counter for the current layer to be placed
    int thisLayerId = 0;
    
    //Parameters we have to know about
    dd4hep::xml::Component xmlParameter = xmlLumiCal.child(_Unicode(parameter));
    const double fullCrossingAngle  = xmlParameter.attr< double >(_Unicode(crossingangle));
    std::cout << " The crossing angle is: " << fullCrossingAngle << " radian"  << std::endl;
    
    
    //Envelope to place the layers in
    Tube envelopeTube (lcalInnerR, lcalOuterR, lcalThickness*0.5 );
    Volume     envelopeVol(detName+"_module",envelopeTube,air);
    envelopeVol.setVisAttributes(theDetector,xmlLumiCal.visStr());
    
    ////////////////////////////////////////////////////////////////////////////////
    // Create all the layers
    ////////////////////////////////////////////////////////////////////////////////
    
    //Loop over all the layer (repeat=NN) sections
    //This is the starting point to place all layers, we need this when we have more than one layer block
    double referencePosition = -lcalThickness*0.5;
    for(dd4hep::xml::Collection_t coll(xmlLumiCal,_U(layer)); coll; ++coll)  {
        dd4hep::xml::Component xmlLayer(coll); //we know this thing is a layer
        
        
        //This just calculates the total size of a single layer
        //Why no convenience function for this?
        double layerThickness = 0;
        for(dd4hep::xml::Collection_t l(xmlLayer,_U(slice)); l; ++l)
            layerThickness += xml_comp_t(l).thickness();
        
        std::cout << "Total Length "    << lcalThickness/dd4hep::cm  << " cm" << std::endl;
        std::cout << "Layer Thickness " << layerThickness/dd4hep::cm << " cm" << std::endl;
        
        //Loop for repeat=NN
        for(int i=0, repeat=xmlLayer.repeat(); i<repeat; ++i)  {
            
            std::string layer_name = detName + dd4hep::xml::_toString(thisLayerId,"_layer%d");
            Tube layer_base(lcalInnerR,lcalOuterR,layerThickness*0.5);
            
            Volume layer_vol(layer_name,layer_base,air);
            
            
            int sliceID=0;
            double inThisLayerPosition = -layerThickness*0.5;
            
            double nRadiationLengths=0.;
            double nInteractionLengths=0.;
            double thickness_sum=0;
            
            LayeredCalorimeterData::Layer caloLayer ;
            
            for(dd4hep::xml::Collection_t collSlice(xmlLayer,_U(slice)); collSlice; ++collSlice)  {
                dd4hep::xml::Component compSlice = collSlice;
                const double      slice_thickness = compSlice.thickness();
                const std::string sliceName = layer_name + dd4hep::xml::_toString(sliceID,"slice%d");
                Material   slice_material  = theDetector.material(compSlice.materialStr());
                
                Tube sliceBase(lcalInnerR,lcalOuterR,slice_thickness/2);
                
                Volume slice_vol (sliceName,sliceBase,slice_material);
                
                nRadiationLengths += slice_thickness/(2.*slice_material.radLength());
                nInteractionLengths += slice_thickness/(2.*slice_material.intLength());
                thickness_sum += slice_thickness/2;
                
                if ( compSlice.isSensitive() )  {

 #if DD4HEP_VERSION_GE( 0, 15 )
                   //Store "inner" quantities
                    caloLayer.inner_nRadiationLengths = nRadiationLengths;
                    caloLayer.inner_nInteractionLengths = nInteractionLengths;
                    caloLayer.inner_thickness = thickness_sum;
                    //Store scintillator thickness
                    caloLayer.sensitive_thickness = slice_thickness;
#endif                    
                    //Reset counters to measure "outside" quantitites
                    nRadiationLengths=0.;
                    nInteractionLengths=0.;
                    thickness_sum = 0.;
                    slice_vol.setSensitiveDetector(sens);
                }
                
                nRadiationLengths += slice_thickness/(2.*slice_material.radLength());
                nInteractionLengths += slice_thickness/(2.*slice_material.intLength());
                thickness_sum += slice_thickness/2;
                
                slice_vol.setAttributes(theDetector,compSlice.regionStr(),compSlice.limitsStr(),compSlice.visStr());
                layer_vol.placeVolume(slice_vol,Position(0,0,inThisLayerPosition+slice_thickness*0.5));
                    
                inThisLayerPosition += slice_thickness;
                ++sliceID;
            }//For all slices in this layer
            
            //-----------------------------------------------------------------------------------------
            
            ///Needs to be innermost face distance
            caloLayer.distance = lcalCentreZ + referencePosition;

#if DD4HEP_VERSION_GE( 0, 15 )
            caloLayer.outer_nRadiationLengths = nRadiationLengths;
            caloLayer.outer_nInteractionLengths = nInteractionLengths;
            caloLayer.outer_thickness = thickness_sum;
#endif            
            
            caloLayer.cellSize0 = LumiCal_cell_size ;
            caloLayer.cellSize1 = LumiCal_cell_size ;
            
            caloData->layers.push_back( caloLayer ) ;
            //-----------------------------------------------------------------------------------------
            
            //Why are we doing this for each layer, this just needs to be done once and then placed multiple times
            //Do we need unique IDs for each piece?
            layer_vol.setVisAttributes(theDetector,xmlLayer.visStr());
            
            Position layer_pos(0,0,referencePosition+0.5*layerThickness);
            referencePosition += layerThickness;
            PlacedVolume pv = envelopeVol.placeVolume(layer_vol,layer_pos);
            pv.addPhysVolID("layer",thisLayerId);
            
            ++thisLayerId;
            
        }//for all layers
        
    }// for all layer collections
    
    const Position bcForwardPos (std::tan(0.5*fullCrossingAngle)*lcalCentreZ,0.0, lcalCentreZ);
    const Position bcBackwardPos(std::tan(0.5*fullCrossingAngle)*lcalCentreZ,0.0,-lcalCentreZ);
    const Rotation3D bcForwardRot ( RotationY(fullCrossingAngle*0.5 ) );
    const Rotation3D bcBackwardRot( RotationZYX ( (M_PI), (M_PI-fullCrossingAngle*0.5), (0.0)));
    
    PlacedVolume pv =
    envelope.placeVolume(envelopeVol, Transform3D( bcForwardRot, bcForwardPos ) );
    pv.addPhysVolID("barrel", 1);
    lumiCalDE_1.setPlacement(pv);

    PlacedVolume pv2 =
    envelope.placeVolume(envelopeVol, Transform3D( bcBackwardRot, bcBackwardPos ) );
    pv2.addPhysVolID("barrel", 2);
    lumiCalDE_2.setPlacement(pv2);
    
    sdet.addExtension< LayeredCalorimeterData >( caloData ) ;
    
    return sdet;
}
Example #3
0
/** Construction of VTX detector, ported from Mokka driver TubeX01.cc
 *
 *  Mokka History:
 * - first implementation as Tube00: Paulo Mora de Freitas, Sep 2002
 * - modified from Tube00 to Tube01DT: Ties Behnke, 2003-02-11
 * - modified for a crossing angle as TubeX00: Adrian Vogel, 2005-05-18
 * - modified for fancier geometries as TubeX01: Adrian Vogel, 2006-04-20
 *
 *  @author: F.Gaede, DESY, Jan 2014
 *
 */
static Ref_t create_element(Detector& theDetector,
                            xml_h element,
                            SensitiveDetector /*sens*/) {


  //------------------------------------------
  //  See comments starting with '//**' for
  //     hints on porting issues
  //------------------------------------------

  std::cout << "This is the Beampipe:"  << std::endl;

  //Access to the XML File
  xml_det_t xmlBeampipe = element;
  const std::string name = xmlBeampipe.nameStr();


  DetElement tube(  name, xmlBeampipe.id()  ) ;

  // --- create an envelope volume and position it into the world ---------------------
  
  Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector,  element , tube ) ;
  
  dd4hep::xml::setDetectorTypeFlag( element, tube ) ;

  if( theDetector.buildType() == BUILD_ENVELOPE ) return tube ;
  
  //-----------------------------------------------------------------------------------


  ConicalSupportData* beampipeData = new ConicalSupportData ;

  //######################################################################################################################################################################
  //  code ported from TubeX01::construct() :
  //##################################
  
  //** DD4hep/TGeo seems to need rad (as opposed to the manual)
  const double phi1 = 0 ;
  const double phi2 = 360.0*dd4hep::degree;
  
  //Parameters we have to know about
  dd4hep::xml::Component xmlParameter = xmlBeampipe.child(_Unicode(parameter));
  const double crossingAngle  = xmlParameter.attr< double >(_Unicode(crossingangle))*0.5; //  only half the angle


  double min_radius = 1.e99 ;

  for(xml_coll_t c( xmlBeampipe ,Unicode("section")); c; ++c) {

    xml_comp_t xmlSection( c );
    
    ODH::ECrossType crossType = ODH::getCrossType(xmlSection.attr< std::string >(_Unicode(type)));
    const double zStart       = xmlSection.attr< double > (_Unicode(start));
    const double zEnd         = xmlSection.attr< double > (_Unicode(end));
    const double rInnerStart  = xmlSection.attr< double > (_Unicode(rMin1));
    const double rInnerEnd    = xmlSection.attr< double > (_Unicode(rMin2));
    const double rOuterStart  = xmlSection.attr< double > (_Unicode(rMax1));
    const double rOuterEnd    = xmlSection.attr< double > (_Unicode(rMax2));
    const double thickness    = rOuterStart - rInnerStart;
    Material sectionMat  = theDetector.material(xmlSection.materialStr());
    const std::string volName      = "tube_" + xmlSection.nameStr();

    std::cout << std::setw(8) << zStart      /dd4hep::mm
	      << std::setw(8) << zEnd	     /dd4hep::mm
	      << std::setw(8) << rInnerStart /dd4hep::mm
	      << std::setw(8) << rInnerEnd   /dd4hep::mm
	      << std::setw(8) << rOuterStart /dd4hep::mm
	      << std::setw(8) << rOuterEnd   /dd4hep::mm
	      << std::setw(8) << thickness   /dd4hep::mm
	      << std::setw(8) << crossType
	      << std::setw(35) << volName
	      << std::setw(15) << sectionMat.name()  << std::endl;    

    if( crossType == ODH::kCenter ) { // store only the central sections !
      ConicalSupportData::Section section ;
      section.rInner = rInnerStart ;
      section.rOuter = rOuterStart ;
      section.zPos   = zStart ;
      beampipeData->sections.push_back( section ) ;
    }

    // things which can be calculated immediately
    const double zHalf       = fabs(zEnd - zStart) * 0.5; // half z length of the cone
    const double zPosition   = fabs(zEnd + zStart) * 0.5; // middle z position
    Material coreMaterial    = theDetector.material("beam"); // always the same
    Material wallMaterial    = sectionMat;

    // this could mess up your geometry, so better check it
    if (not checkForSensibleGeometry(crossingAngle, crossType)){

      throw std::runtime_error( " Beampipe_o1_v01_geo.cpp : checkForSensibleGeometry() failed " ) ;
      //      return false;
    }
    const double rotateAngle = getCurrentAngle(crossingAngle, crossType); // for the placement at +z (better make it const now)
    const double mirrorAngle = M_PI - rotateAngle; // for the "mirrored" placement at -z
    // the "mirroring" in fact is done by a rotation of (almost) 180 degrees around the y-axis

    switch (crossType) {
    case ODH::kCenter:
    case ODH::kUpstream:
    case ODH::kDnstream: {
      // a volume on the z-axis, on the upstream branch, or on the downstream branch
      
      // absolute transformations for the placement in the world
      Transform3D transformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition), rotateAngle) );
      Transform3D transmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition), mirrorAngle) );
      
      // solid for the tube (including vacuum and wall): a solid cone
      ConeSegment tubeSolid( zHalf, 0, rOuterStart, 0, rOuterEnd , phi1, phi2);
        
      // tube consists of vacuum
      // place tube twice explicitely so we can attach surfaces to each one
      Volume tubeLog( volName, tubeSolid, coreMaterial ) ;
      Volume tubeLog2( volName, tubeSolid, coreMaterial ) ;

      // placement of the tube in the world, both at +z and -z
      envelope.placeVolume( tubeLog,  transformer );
      envelope.placeVolume( tubeLog2,  transmirror );

      // if inner and outer radii are equal, then omit the tube wall
      if (rInnerStart != rOuterStart || rInnerEnd != rOuterEnd) {
	
	// the wall solid: a tubular cone
	ConeSegment wallSolid( zHalf, rInnerStart, rOuterStart, rInnerEnd, rOuterEnd, phi1, phi2);
        
	// the wall consists of the material given in the XML
	Volume wallLog ( volName + "_wall", wallSolid, wallMaterial);
	Volume wallLog2( volName + "_wall2", wallSolid, wallMaterial);
	
	if( crossType == ODH::kCenter ) { 

	  // add surface for tracking ....
	  const bool isCylinder = ( rInnerStart == rInnerEnd );


	  if (isCylinder) {  // cylinder

	    Vector3D ocyl(  rInnerStart + thickness/2.  , 0. , 0. ) ;

	    VolCylinder cylSurf1( wallLog , SurfaceType( SurfaceType::Helper ) , 0.5*thickness  , 0.5*thickness , ocyl );
	    VolCylinder cylSurf2( wallLog2, SurfaceType( SurfaceType::Helper ) , 0.5*thickness  , 0.5*thickness , ocyl );

	    volSurfaceList( tube )->push_back( cylSurf1 );
	    volSurfaceList( tube )->push_back( cylSurf2 );

	  }else{   // cone 
	    
	    const double dr    = rInnerEnd - rInnerStart ;
	    const double theta = atan2( dr , 2.* zHalf ) ;

	    Vector3D ocon( rInnerStart + 0.5 * ( dr + thickness ), 0. , 0. );

	    Vector3D v( 1. , 0. , theta, Vector3D::spherical ) ;

	    VolCone conSurf1( wallLog , SurfaceType( SurfaceType::Helper ) , 0.5*thickness  , 0.5*thickness , v, ocon );
	    VolCone conSurf2( wallLog2, SurfaceType( SurfaceType::Helper ) , 0.5*thickness  , 0.5*thickness , v, ocon );

	    volSurfaceList( tube )->push_back( conSurf1 );
	    volSurfaceList( tube )->push_back( conSurf2 );

	  }

	  if( rInnerStart < min_radius ) min_radius = rInnerStart ;
	  if( rOuterStart < min_radius ) min_radius = rOuterStart ;
	}

	wallLog.setVisAttributes(theDetector, "TubeVis");
	wallLog2.setVisAttributes(theDetector, "TubeVis");
	tubeLog.setVisAttributes(theDetector, "VacVis");
	tubeLog2.setVisAttributes(theDetector, "VacVis");
	
	// placement as a daughter volume of the tube, will appear in both placements of the tube
	tubeLog.placeVolume( wallLog,  Transform3D() );
	tubeLog2.placeVolume( wallLog2,  Transform3D() );
      }
    }  
      break;
      
    case ODH::kPunchedCenter: {
      // a volume on the z-axis with one or two inner holes
      // (implemented as a cone from which tubes are punched out)
      
      const double rUpstreamPunch = rInnerStart; // just alias names denoting what is meant here
      const double rDnstreamPunch = rInnerEnd; // (the database entries are "abused" in this case)
      
      // relative transformations for the composition of the SubtractionVolumes
      Transform3D upstreamTransformer(RotationY(-crossingAngle), Position(zPosition * tan(-crossingAngle), 0, 0));
      Transform3D dnstreamTransformer(RotationY(+crossingAngle), Position(zPosition * tan(+crossingAngle), 0, 0));
  
      // absolute transformations for the final placement in the world (angles always equal zero and 180 deg)
      Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition) , rotateAngle) );
      Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition) , mirrorAngle) );
  
      // solid for the tube (including vacuum and wall): a solid cone
      ConeSegment tubeSolid( zHalf, 0, rOuterStart, 0, rOuterEnd, phi1, phi2);
        
      // tube consists of vacuum (will later have two different daughters)
      Volume tubeLog0( volName + "_0", tubeSolid, coreMaterial );
      Volume tubeLog1( volName + "_1", tubeSolid, coreMaterial );
        
      // placement of the tube in the world, both at +z and -z
      envelope.placeVolume( tubeLog0, placementTransformer );
      envelope.placeVolume( tubeLog1, placementTransmirror );

      // the wall solid and placeholders for possible SubtractionSolids
      ConeSegment wholeSolid(  zHalf, 0, rOuterStart, 0, rOuterEnd, phi1, phi2);
      
      Solid tmpSolid0, tmpSolid1, wallSolid0, wallSolid1;
      
      // the punched subtraction solids can be asymmetric and therefore have to be created twice:
      // one time in the "right" way, another time in the "reverse" way, because the "mirroring"
      // rotation around the y-axis will not only exchange +z and -z, but also +x and -x

      if ( rUpstreamPunch > 1e-6 ) { // do we need a hole on the upstream branch?
       	Tube upstreamPunch( 0, rUpstreamPunch, 5 * zHalf, phi1, phi2); // a bit longer
       	tmpSolid0 = SubtractionSolid( wholeSolid, upstreamPunch, upstreamTransformer);
       	tmpSolid1 = SubtractionSolid( wholeSolid, upstreamPunch, dnstreamTransformer); // [sic]
      } else { // dont't do anything, just pass on the unmodified shape
       	tmpSolid0 = wholeSolid;
       	tmpSolid1 = wholeSolid;
      }
      
      if (rDnstreamPunch > 1e-6 ) { // do we need a hole on the downstream branch?
       	Tube dnstreamPunch( 0, rDnstreamPunch, 5 * zHalf, phi1, phi2); // a bit longer
       	wallSolid0 = SubtractionSolid( tmpSolid0, dnstreamPunch, dnstreamTransformer);
       	wallSolid1 = SubtractionSolid( tmpSolid1, dnstreamPunch, upstreamTransformer); // [sic]
      } else { // dont't do anything, just pass on the unmodified shape
       	wallSolid0 = tmpSolid0;
       	wallSolid1 = tmpSolid1;
      }

      // the wall consists of the material given in the XML
      Volume wallLog0( volName + "_wall_0", wallSolid0, wallMaterial );
      Volume wallLog1( volName + "_wall_1", wallSolid1, wallMaterial );

      wallLog0.setVisAttributes(theDetector, "TubeVis");
      wallLog1.setVisAttributes(theDetector, "TubeVis");
      tubeLog0.setVisAttributes(theDetector, "VacVis");
      tubeLog1.setVisAttributes(theDetector, "VacVis");

      // placement as a daughter volumes of the tube
      tubeLog0.placeVolume( wallLog0, Position() );
      tubeLog1.placeVolume( wallLog1, Position() );
      
      break;
    }

    case ODH::kPunchedUpstream:
    case ODH::kPunchedDnstream: {
      // a volume on the upstream or downstream branch with two inner holes
      // (implemented as a cone from which another tube is punched out)
      
      const double rCenterPunch = (crossType == ODH::kPunchedUpstream) ? (rInnerStart) : (rInnerEnd); // just alias names denoting what is meant here
      const double rOffsetPunch = (crossType == ODH::kPunchedDnstream) ? (rInnerStart) : (rInnerEnd); // (the database entries are "abused" in this case)
      
      // relative transformations for the composition of the SubtractionVolumes
      Transform3D punchTransformer(RotationY(-2 * rotateAngle), Position(zPosition * tan(-2 * rotateAngle), 0, 0));
      Transform3D punchTransmirror(RotationY(+2 * rotateAngle), Position(zPosition * tan(+2 * rotateAngle), 0, 0));
      
      // absolute transformations for the final placement in the world
      Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition) , rotateAngle) );
      Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition) , mirrorAngle) );
      
      // solid for the tube (including vacuum and wall): a solid cone
      ConeSegment tubeSolid( zHalf, 0, rOuterStart, 0, rOuterEnd, phi1, phi2);
      
      // tube consists of vacuum (will later have two different daughters)
      Volume tubeLog0( volName + "_0", tubeSolid, coreMaterial );
      Volume tubeLog1( volName + "_1", tubeSolid, coreMaterial );
      
      // placement of the tube in the world, both at +z and -z
      envelope.placeVolume( tubeLog0, placementTransformer );
      envelope.placeVolume( tubeLog1, placementTransmirror );

      // the wall solid and the piece (only a tube, for the moment) which will be punched out
      ConeSegment wholeSolid( zHalf, rCenterPunch , rOuterStart, rCenterPunch, rOuterEnd, phi1, phi2);
      
      Tube punchSolid( 0, rOffsetPunch, 5 * zHalf, phi1, phi2); // a bit longer
      
      // the punched subtraction solids can be asymmetric and therefore have to be created twice:
      // one time in the "right" way, another time in the "reverse" way, because the "mirroring"
      // rotation around the y-axis will not only exchange +z and -z, but also +x and -x
      SubtractionSolid wallSolid0( wholeSolid, punchSolid, punchTransformer);
      SubtractionSolid wallSolid1( wholeSolid, punchSolid, punchTransmirror);

      // the wall consists of the material given in the database
      Volume wallLog0( volName + "_wall_0", wallSolid0, wallMaterial );
      Volume wallLog1( volName + "_wall_1", wallSolid1, wallMaterial );
      
      wallLog0.setVisAttributes(theDetector, "TubeVis");
      wallLog1.setVisAttributes(theDetector, "TubeVis");

      tubeLog0.setVisAttributes(theDetector, "VacVis");
      tubeLog1.setVisAttributes(theDetector, "VacVis");
      
      // placement as a daughter volumes of the tube
      tubeLog0.placeVolume( wallLog0 , Position() );
      tubeLog1.placeVolume( wallLog1 , Position() );
      
      break;
    }
      
    case ODH::kUpstreamClippedFront:
    case ODH::kDnstreamClippedFront:
    case ODH::kUpstreamSlicedFront:
    case ODH::kDnstreamSlicedFront: {
      // a volume on the upstream or donwstream branch, but with the front face parallel to the xy-plane
      // or to a piece tilted in the other direction ("sliced" like a salami with 2 * rotateAngle)
      // (implemented as a slightly longer cone from which the end is clipped off)
      
      // the volume which will be used for clipping: a solid tube
      const double clipSize = rOuterStart; // the right order of magnitude for the clipping volume (alias name)
      Tube clipSolid( 0, 2 * clipSize, clipSize, phi1, phi2); // should be large enough
        
      // relative transformations for the composition of the SubtractionVolumes
      const double clipAngle = (crossType == ODH::kUpstreamClippedFront || crossType == ODH::kDnstreamClippedFront) ? (rotateAngle) : (2 * rotateAngle);
      const double clipShift = (zStart - clipSize) / cos(clipAngle) - (zPosition - clipSize / 2); // question: why is this correct?
      Transform3D clipTransformer(RotationY(-clipAngle), Position(0, 0, clipShift));
      Transform3D clipTransmirror(RotationY(+clipAngle), Position(0, 0, clipShift));
  
      // absolute transformations for the final placement in the world
      Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition - clipSize / 2) , rotateAngle) );
      Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition - clipSize / 2) , mirrorAngle) );
  
      // solid for the tube (including vacuum and wall): a solid cone

      ConeSegment wholeSolid(  zHalf + clipSize / 2, 0, rOuterStart, 0, rOuterEnd,  phi1, phi2); // a bit longer
  
      // clip away the protruding end
      SubtractionSolid tubeSolid0( wholeSolid, clipSolid, clipTransformer);
      SubtractionSolid tubeSolid1( wholeSolid, clipSolid, clipTransmirror);

      // tube consists of vacuum (will later have two different daughters)
      Volume tubeLog0( volName + "_0", tubeSolid0, coreMaterial );
      Volume tubeLog1( volName + "_1", tubeSolid1, coreMaterial );
        
      // placement of the tube in the world, both at +z and -z
      envelope.placeVolume( tubeLog0, placementTransformer );
      envelope.placeVolume( tubeLog1, placementTransmirror );
       
      if (rInnerStart != rOuterStart || rInnerEnd != rOuterEnd) {
	// the wall solid: a tubular cone
	ConeSegment wallWholeSolid(  zHalf + clipSize / 2, rInnerStart, rOuterStart, rInnerEnd, rOuterEnd, phi1, phi2); // a bit longer
        
	// clip away the protruding end
	SubtractionSolid wallSolid0( wallWholeSolid, clipSolid, clipTransformer);
	SubtractionSolid wallSolid1( wallWholeSolid, clipSolid, clipTransmirror);
        
	// the wall consists of the material given in the database
	Volume wallLog0( volName + "_wall_0", wallSolid0, wallMaterial );
	Volume wallLog1( volName + "_wall_1", wallSolid1, wallMaterial );

	wallLog0.setVisAttributes(theDetector, "TubeVis");
	wallLog1.setVisAttributes(theDetector, "TubeVis");

	tubeLog0.setVisAttributes(theDetector, "VacVis");
	tubeLog1.setVisAttributes(theDetector, "VacVis");

	// placement as a daughter volumes of the tube
	tubeLog0.placeVolume( wallLog0, Position() );
	tubeLog1.placeVolume( wallLog1, Position() );
      }
    }
      break;

    case ODH::kUpstreamClippedRear:
    case ODH::kDnstreamClippedRear:
    case ODH::kUpstreamSlicedRear:
    case ODH::kDnstreamSlicedRear: {
      // a volume on the upstream or donwstream branch, but with the rear face parallel to the xy-plane
      // or to a piece tilted in the other direction ("sliced" like a salami with 2 * rotateAngle)
      // (implemented as a slightly longer cone from which the end is clipped off)
      
      // the volume which will be used for clipping: a solid tube
      const double clipSize = rOuterEnd; // the right order of magnitude for the clipping volume (alias name)
      Tube clipSolid( 0, 2 * clipSize, clipSize, phi1, phi2); // should be large enough
      
      // relative transformations for the composition of the SubtractionVolumes
      const double clipAngle = (crossType == ODH::kUpstreamClippedRear || crossType == ODH::kDnstreamClippedRear) ? (rotateAngle) : (2 * rotateAngle);
      const double clipShift = (zEnd + clipSize) / cos(clipAngle) - (zPosition + clipSize / 2); // question: why is this correct?
      Transform3D clipTransformer(RotationY(-clipAngle), Position(0, 0, clipShift));
      Transform3D clipTransmirror(RotationY(+clipAngle), Position(0, 0, clipShift));
      
      // absolute transformations for the final placement in the world
      Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition + clipSize / 2) , rotateAngle) );
      Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition + clipSize / 2) , mirrorAngle) );
      
      // solid for the tube (including vacuum and wall): a solid cone
      ConeSegment wholeSolid( 0, rOuterStart, 0, rOuterEnd, zHalf + clipSize / 2, phi1, phi2); // a bit longer
      
      // clip away the protruding end
      SubtractionSolid tubeSolid0( wholeSolid, clipSolid, clipTransformer);
      SubtractionSolid tubeSolid1( wholeSolid, clipSolid, clipTransmirror);
      
      // tube consists of vacuum (will later have two different daughters)
      Volume tubeLog0( volName + "_0", tubeSolid0, coreMaterial );
      Volume tubeLog1( volName + "_1", tubeSolid1, coreMaterial );
      
      // placement of the tube in the world, both at +z and -z
      envelope.placeVolume( tubeLog0, placementTransformer );
      envelope.placeVolume( tubeLog1, placementTransmirror );
      
      if (rInnerStart != rOuterStart || rInnerEnd != rOuterEnd) {
	// the wall solid: a tubular cone
	ConeSegment wallWholeSolid( rInnerStart, rOuterStart, rInnerEnd, rOuterEnd, zHalf + clipSize / 2, phi1, phi2); // a bit longer
        
	// clip away the protruding end
	SubtractionSolid wallSolid0( wallWholeSolid, clipSolid, clipTransformer);
	SubtractionSolid wallSolid1( wallWholeSolid, clipSolid, clipTransmirror);
        
	// the wall consists of the material given in the database
	Volume wallLog0( volName + "_wall_0", wallSolid0, wallMaterial );
	Volume wallLog1( volName + "_wall_1", wallSolid1, wallMaterial );

	wallLog0.setVisAttributes(theDetector, "TubeVis");
	wallLog1.setVisAttributes(theDetector, "TubeVis");

	tubeLog0.setVisAttributes(theDetector, "VacVis");
	tubeLog1.setVisAttributes(theDetector, "VacVis");
        
	// placement as a daughter volumes of the tube
	tubeLog0.placeVolume( wallLog0, Transform3D() );
	tubeLog1.placeVolume( wallLog1, Transform3D() );
      }
      break;
    }

    case ODH::kUpstreamClippedBoth:
    case ODH::kDnstreamClippedBoth:
    case ODH::kUpstreamSlicedBoth:
    case ODH::kDnstreamSlicedBoth: {
      // a volume on the upstream or donwstream branch, but with both faces parallel to the xy-plane
      // or to a piece tilted in the other direction ("sliced" like a salami with 2 * rotateAngle)
      // (implemented as a slightly longer cone from which the end is clipped off)
      
      // the volume which will be used for clipping: a solid tube
      const double clipSize = rOuterEnd; // the right order of magnitude for the clipping volume (alias name)
      Tube clipSolid( 0, 2 * clipSize, clipSize, phi1, phi2); // should be large enough
        
      // relative transformations for the composition of the SubtractionVolumes
      const double clipAngle = (crossType == ODH::kUpstreamClippedBoth || crossType == ODH::kDnstreamClippedBoth) ? (rotateAngle) : (2 * rotateAngle);
      const double clipShiftFrnt = (zStart - clipSize) / cos(clipAngle) - zPosition;
      const double clipShiftRear = (zEnd   + clipSize) / cos(clipAngle) - zPosition;
      Transform3D clipTransformerFrnt(RotationY(-clipAngle), Position(0, 0, clipShiftFrnt));
      Transform3D clipTransformerRear(RotationY(-clipAngle), Position(0, 0, clipShiftRear));
      Transform3D clipTransmirrorFrnt(RotationY(+clipAngle), Position(0, 0, clipShiftFrnt));
      Transform3D clipTransmirrorRear(RotationY(+clipAngle), Position(0, 0, clipShiftRear));
  
      // absolute transformations for the final placement in the world
      Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition) , rotateAngle) );
      Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition) , mirrorAngle) );
      
      // solid for the tube (including vacuum and wall): a solid cone
      ConeSegment wholeSolid( 0, rOuterStart, 0, rOuterEnd, zHalf + clipSize, phi1, phi2); // a bit longer
      
      // clip away the protruding ends
      SubtractionSolid tmpSolid0 ( wholeSolid, clipSolid, clipTransformerFrnt);
      SubtractionSolid tmpSolid1 ( wholeSolid, clipSolid, clipTransmirrorFrnt);
      SubtractionSolid tubeSolid0( tmpSolid0,  clipSolid, clipTransformerRear);
      SubtractionSolid tubeSolid1( tmpSolid1,  clipSolid, clipTransmirrorRear);
        
      // tube consists of vacuum (will later have two different daughters)
      Volume tubeLog0( volName + "_0", tubeSolid0, coreMaterial );
      Volume tubeLog1( volName + "_1", tubeSolid1, coreMaterial );
        
      // placement of the tube in the world, both at +z and -z
      envelope.placeVolume( tubeLog0, placementTransformer );
      envelope.placeVolume( tubeLog1, placementTransmirror );
       
      if (rInnerStart != rOuterStart || rInnerEnd != rOuterEnd) {
	// the wall solid: a tubular cone
	ConeSegment wallWholeSolid( rInnerStart, rOuterStart, rInnerEnd, rOuterEnd, zHalf + clipSize, phi1, phi2); // a bit longer
        
	// clip away the protruding ends
	SubtractionSolid wallTmpSolid0( wallWholeSolid, clipSolid, clipTransformerFrnt);
	SubtractionSolid wallTmpSolid1( wallWholeSolid, clipSolid, clipTransmirrorFrnt);
	SubtractionSolid wallSolid0   ( wallTmpSolid0,  clipSolid, clipTransformerRear);
	SubtractionSolid wallSolid1   ( wallTmpSolid1,  clipSolid, clipTransmirrorRear);
        
	// the wall consists of the material given in the database
	Volume wallLog0(volName + "_wall_0", wallSolid0, wallMaterial );
	Volume wallLog1(volName + "_wall_1", wallSolid1, wallMaterial );

	wallLog0.setVisAttributes(theDetector, "TubeVis");
	wallLog1.setVisAttributes(theDetector, "TubeVis");

	tubeLog0.setVisAttributes(theDetector, "VacVis");
	tubeLog1.setVisAttributes(theDetector, "VacVis");
        
	// placement as a daughter volumes of the tube
	tubeLog0.placeVolume( wallLog0, Transform3D() );
	tubeLog1.placeVolume( wallLog1, Transform3D() );
      }
      break;
    }
    default: {
      throw std::runtime_error( " Beampipe_o1_v01_geo.cpp : fatal failure !! ??  " ) ;

      //      return false; // fatal failure
    }

    }//end switch
  }//for all xmlSections

  //######################################################################################################################################################################
  

  // add a surface just inside the beampipe for tracking:
  Vector3D oIPCyl( (min_radius-1.e-3)  , 0. , 0.  ) ;
  SimpleCylinder ipCylSurf( envelope , SurfaceType( SurfaceType::Helper ) , 1.e-5  , 1e-5 , oIPCyl ) ;
  // the length does not really matter here as long as it is long enough for all tracks ...
  ipCylSurf->setHalfLength(  100*dd4hep::cm ) ; 
  volSurfaceList( tube )->push_back( ipCylSurf ) ;

  tube.addExtension< ConicalSupportData >( beampipeData ) ;

  //--------------------------------------
  
  tube.setVisAttributes( theDetector, xmlBeampipe.visStr(), envelope );

  // // tube.setPlacement(pv);
  
  return tube;
}
Example #4
0
static Ref_t create_detector(Detector& theDetector, xml_h element, SensitiveDetector sens)  {
  xml_det_t   x_det     = element;
  Layering    layering(x_det);
  xml_dim_t   dim         = x_det.dimensions();
  string      det_name    = x_det.nameStr();
  //unused: string      det_type    = x_det.typeStr();
  Material    air         = theDetector.air();
  Material    stavesMaterial    = theDetector.material(x_det.materialStr());
  int         numSides    = dim.numsides();

  int           det_id    = x_det.id();

  DetElement   sdet(det_name,det_id);

  PlacedVolume pVol;

  // --- create an envelope volume and position it into the world ---------------------

  Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector,  element , sdet ) ;
  
  sdet.setTypeFlag( DetType::CALORIMETER |  DetType::ENDCAP  | DetType::HADRONIC ) ;

  if( theDetector.buildType() == BUILD_ENVELOPE ) return sdet ;
  //-----------------------------------------------------------------------------------

  sens.setType("calorimeter");

  DetElement    stave_det("module0stave0",det_id);
 
  // The way to reaad constant from XML/Detector file.
  double      Hcal_radiator_thickness          = theDetector.constant<double>("Hcal_radiator_thickness");
  double      Hcal_endcap_lateral_structure_thickness = theDetector.constant<double>("Hcal_endcap_lateral_structure_thickness");
  double      Hcal_endcap_layer_air_gap        = theDetector.constant<double>("Hcal_endcap_layer_air_gap");

  //double      Hcal_cells_size                  = theDetector.constant<double>("Hcal_cells_size");
  double      HcalEndcap_inner_radius          = theDetector.constant<double>("HcalEndcap_inner_radius");
  double      HcalEndcap_outer_radius          = theDetector.constant<double>("HcalEndcap_outer_radius");
  double      HcalEndcap_min_z                 = theDetector.constant<double>("HcalEndcap_min_z");
  double      HcalEndcap_max_z                 = theDetector.constant<double>("HcalEndcap_max_z");

  double   Hcal_steel_cassette_thickness       = theDetector.constant<double>("Hcal_steel_cassette_thickness");
  double   HcalServices_outer_FR4_thickness    = theDetector.constant<double>("HcalServices_outer_FR4_thickness");
  double   HcalServices_outer_Cu_thickness     = theDetector.constant<double>("HcalServices_outer_Cu_thickness");
  double   Hcal_endcap_services_module_width   = theDetector.constant<double>("Hcal_endcap_services_module_width");

  Material  stainless_steel =  theDetector.material("stainless_steel");
  Material  PCB             =  theDetector.material("PCB");
  Material  copper          =  theDetector.material("Cu");

  std::cout <<"\n HcalEndcap_inner_radius = "
	    <<HcalEndcap_inner_radius/dd4hep::mm <<" mm"
	    <<"\n HcalEndcap_outer_radius = "
	    <<HcalEndcap_outer_radius/dd4hep::mm <<" mm"
	    <<"\n HcalEndcap_min_z = "
	    <<HcalEndcap_min_z/dd4hep::mm <<" mm"
	    <<"\n HcalEndcap_max_z = "
	    <<HcalEndcap_max_z/dd4hep::mm <<" mm"
	    <<std::endl;


  Readout readout = sens.readout();
  Segmentation seg = readout.segmentation();
  
  std::vector<double> cellSizeVector = seg.segmentation()->cellDimensions(0); //Assume uniform cell sizes, provide dummy cellID
  double cell_sizeX      = cellSizeVector[0];
  double cell_sizeY      = cellSizeVector[1];

  //========== fill data for reconstruction ============================
  LayeredCalorimeterData* caloData = new LayeredCalorimeterData ;
  caloData->layoutType = LayeredCalorimeterData::EndcapLayout ;
  caloData->inner_symmetry = 4  ; // hard code cernter box hole
  caloData->outer_symmetry = 0  ; // outer tube, or 8 for Octagun
  caloData->phi0 = 0 ;

  /// extent of the calorimeter in the r-z-plane [ rmin, rmax, zmin, zmax ] in mm.
  caloData->extent[0] = HcalEndcap_inner_radius ;
  caloData->extent[1] = HcalEndcap_outer_radius ;
  caloData->extent[2] = HcalEndcap_min_z ;
  caloData->extent[3] = HcalEndcap_max_z ;
  

  int endcapID = 0;
  for(xml_coll_t c(x_det.child(_U(dimensions)),_U(dimensions)); c; ++c) 
    {
      xml_comp_t l(c);
      
      double dim_x = l.attr<double>(_Unicode(dim_x));
      double dim_y = l.attr<double>(_Unicode(dim_y));
      double dim_z = l.attr<double>(_Unicode(dim_z));
      double pos_y = l.attr<double>(_Unicode(y_offset));
    
      // Hcal Endcap module shape
      double box_half_x= dim_x/2.0; // module width, all are same
      double box_half_y= dim_y/2.0; // total thickness, all are same
      double box_half_z= dim_z/2.0; // module length, changed, 
      
      double x_offset = box_half_x*numSides-box_half_x*endcapID*2.0-box_half_x;
      double y_offset = pos_y;
      
      Box    EndcapModule(box_half_x,box_half_y,box_half_z);
      
      // define the name of each endcap Module
      string envelopeVol_name   = det_name+_toString(endcapID,"_EndcapModule%d");
      
      Volume envelopeVol(envelopeVol_name,EndcapModule,stavesMaterial);
      
      // Set envelope volume attributes.
      envelopeVol.setAttributes(theDetector,x_det.regionStr(),x_det.limitsStr(),x_det.visStr());
      

      double FEE_half_x = box_half_x-Hcal_endcap_services_module_width/2.0;
      double FEE_half_y = box_half_y;
      double FEE_half_Z = Hcal_endcap_services_module_width/2.0;

      Box    FEEBox(FEE_half_x,FEE_half_y,FEE_half_Z);
      Volume FEEModule("Hcal_endcap_FEE",FEEBox,air);

      double FEELayer_thickness = Hcal_steel_cassette_thickness + HcalServices_outer_FR4_thickness + HcalServices_outer_Cu_thickness;
      Box    FEELayerBox(FEE_half_x,FEELayer_thickness/2.0,FEE_half_Z);
      Volume FEELayer("FEELayer",FEELayerBox,air);

      Box    FEELayerSteelBox(FEE_half_x,Hcal_steel_cassette_thickness/2.0,FEE_half_Z);
      Volume FEELayerSteel("FEELayerSteel",FEELayerSteelBox,stainless_steel);
      pVol = FEELayer.placeVolume(FEELayerSteel,
				  Position(0,
					   (-FEELayer_thickness/2.0
					    +Hcal_steel_cassette_thickness/2.0),
					   0));

      Box    FEELayerFR4Box(FEE_half_x,HcalServices_outer_FR4_thickness/2.0,FEE_half_Z);
      Volume FEELayerFR4("FEELayerFR4",FEELayerFR4Box,PCB);
      pVol = FEELayer.placeVolume(FEELayerFR4,
				  Position(0,
					   (-FEELayer_thickness/2.0+Hcal_steel_cassette_thickness
					    +HcalServices_outer_FR4_thickness/2.0),
					   0));

      Box    FEELayerCuBox(FEE_half_x,HcalServices_outer_Cu_thickness/2.0,FEE_half_Z);
      Volume FEELayerCu("FEELayerCu",FEELayerCuBox,copper);
      pVol = FEELayer.placeVolume(FEELayerCu,
				  Position(0,
					   (-FEELayer_thickness/2.0+Hcal_steel_cassette_thickness+HcalServices_outer_FR4_thickness
					    +HcalServices_outer_Cu_thickness/2.0),
					   0));


      // ========= Create Hcal Chamber (i.e. Layers) ==============================
      // It will be the sub volume for placing the slices.
      // Itself will be placed into the Hcal Endcap modules envelope.
      // ==========================================================================
      
      // create Layer (air) and place the slices (Polystyrene,Cu,FR4,air) into it. 
      // place the Layer into the Hcal Endcap Modules envelope (stavesMaterial).
      
      // First Hcal Chamber position, start after first radiator
      double layer_pos_y     = - box_half_y + Hcal_radiator_thickness;                      
      
      // Create Hcal Endcap Chamber without radiator
      // Place into the Hcal Encap module envelope, after each radiator 
      int layer_num = 1;
      for(xml_coll_t m(x_det,_U(layer)); m; ++m)  {
	xml_comp_t   x_layer = m;
	int          repeat = x_layer.repeat();          // Get number of layers.

	double layer_thickness = layering.layer(layer_num)->thickness();
	string layer_name      = envelopeVol_name+"_layer";
	DetElement  layer(stave_det,layer_name,det_id);
	
	// Active Layer box & volume
	double active_layer_dim_x = box_half_x - Hcal_endcap_lateral_structure_thickness - Hcal_endcap_layer_air_gap;
	double active_layer_dim_y = layer_thickness/2.0;
	double active_layer_dim_z = box_half_z;
	
	// Build chamber including air gap
	// The Layer will be filled with slices, 
	Volume layer_vol(layer_name, Box((active_layer_dim_x + Hcal_endcap_layer_air_gap),
					 active_layer_dim_y,active_layer_dim_z), air);

	LayeredCalorimeterData::Layer caloLayer ;
	caloLayer.cellSize0 = cell_sizeX;
	caloLayer.cellSize1 = cell_sizeY;
	
	// ========= Create sublayer slices =========================================
	// Create and place the slices into Layer
	// ==========================================================================
	
	// Create the slices (sublayers) within the Hcal Chamber.
	double slice_pos_y = -(layer_thickness / 2.0);
	int slice_number = 0;

	double nRadiationLengths=0.;
	double nInteractionLengths=0.;
	double thickness_sum=0;

	nRadiationLengths   = Hcal_radiator_thickness/(stavesMaterial.radLength());
	nInteractionLengths = Hcal_radiator_thickness/(stavesMaterial.intLength());
	thickness_sum       = Hcal_radiator_thickness;

	for(xml_coll_t k(x_layer,_U(slice)); k; ++k)  {
	  xml_comp_t x_slice = k;
	  string   slice_name      = layer_name + _toString(slice_number,"_slice%d");
	  double   slice_thickness = x_slice.thickness();
	  Material slice_material  = theDetector.material(x_slice.materialStr());
	  DetElement slice(layer,_toString(slice_number,"slice%d"),det_id);
	  
	  slice_pos_y += slice_thickness / 2.0;
	  
	  // Slice volume & box
	  Volume slice_vol(slice_name,Box(active_layer_dim_x,slice_thickness/2.0,active_layer_dim_z),slice_material);
	  
	  nRadiationLengths   += slice_thickness/(2.*slice_material.radLength());
	  nInteractionLengths += slice_thickness/(2.*slice_material.intLength());
	  thickness_sum       += slice_thickness/2;

	  if ( x_slice.isSensitive() ) {
	    sens.setType("calorimeter");
	    slice_vol.setSensitiveDetector(sens);

#if DD4HEP_VERSION_GE( 0, 15 )
	    //Store "inner" quantities
	    caloLayer.inner_nRadiationLengths = nRadiationLengths;
	    caloLayer.inner_nInteractionLengths = nInteractionLengths;
	    caloLayer.inner_thickness = thickness_sum;
	    //Store scintillator thickness
	    caloLayer.sensitive_thickness = slice_thickness;
#endif
	    //Reset counters to measure "outside" quantitites
	    nRadiationLengths=0.;
	    nInteractionLengths=0.;
	    thickness_sum = 0.;
	  }

	  nRadiationLengths += slice_thickness/(2.*slice_material.radLength());
	  nInteractionLengths += slice_thickness/(2.*slice_material.intLength());
	  thickness_sum += slice_thickness/2;

	  // Set region, limitset, and vis.
	  slice_vol.setAttributes(theDetector,x_slice.regionStr(),x_slice.limitsStr(),x_slice.visStr());
	  // slice PlacedVolume
	  PlacedVolume slice_phv = layer_vol.placeVolume(slice_vol,Position(0,slice_pos_y,0));
	  //slice_phv.addPhysVolID("slice",slice_number);
	  
	  slice.setPlacement(slice_phv);
	  // Increment Z position for next slice.
	  slice_pos_y += slice_thickness / 2.0;
	  // Increment slice number.
	  ++slice_number;             
	}
	// Set region, limitset, and vis.
	layer_vol.setAttributes(theDetector,x_layer.regionStr(),x_layer.limitsStr(),x_layer.visStr());

#if DD4HEP_VERSION_GE( 0, 15 )
	//Store "outer" quantities
	caloLayer.outer_nRadiationLengths = nRadiationLengths;
	caloLayer.outer_nInteractionLengths = nInteractionLengths;
	caloLayer.outer_thickness = thickness_sum;
#endif 	
	
	// ========= Place the Layer (i.e. Chamber) =================================
	// Place the Layer into the Hcal Endcap module envelope.
	// with the right position and rotation.
	// Registry the IDs (layer, stave, module).
	// Place the same layer 48 times into Endcap module
	// ==========================================================================
	
	for (int j = 0; j < repeat; j++)    {
	  
	  // Layer position in y within the Endcap Modules.
	  layer_pos_y += layer_thickness / 2.0;
	  
	  PlacedVolume layer_phv = envelopeVol.placeVolume(layer_vol,
							   Position(0,layer_pos_y,0));
	  // registry the ID of Layer, stave and module
	  layer_phv.addPhysVolID("layer",layer_num);

	  // then setPlacement for it.
	  layer.setPlacement(layer_phv);
	  
	  pVol = FEEModule.placeVolume(FEELayer,
				       Position(0,layer_pos_y,0));
	  //-----------------------------------------------------------------------------------------
	  if ( caloData->layers.size() < (unsigned int)repeat ) {

	    caloLayer.distance = HcalEndcap_min_z + box_half_y + layer_pos_y
	      - caloLayer.inner_thickness ; // Will be added later at "DDMarlinPandora/DDGeometryCreator.cc:226" to get center of sensitive element
	    caloLayer.absorberThickness = Hcal_radiator_thickness ;
	    
	    caloData->layers.push_back( caloLayer ) ;
	  }
	  //-----------------------------------------------------------------------------------------
	  
	  
	  // ===== Prepare for next layer (i.e. next Chamber) =========================
	  // Prepare the chamber placement position and the chamber dimension
	  // ==========================================================================
	  
	  // Increment the layer_pos_y
	  // Place Hcal Chamber after each radiator 
	  layer_pos_y += layer_thickness / 2.0;
	  layer_pos_y += Hcal_radiator_thickness;
	  ++layer_num;         
	}
	
	
      }
      
      
      // =========== Place Hcal Endcap envelope ===================================
      // Finally place the Hcal Endcap envelope into the world volume.
      // Registry the stave(up/down), module(left/right) and endcapID.
      // ==========================================================================
      
      // Acording to the number of staves and modules,
      // Place the same Hcal Endcap module volume into the world volume
      // with the right position and rotation.
      for(int stave_num=0;stave_num<2;stave_num++){
	
	double EndcapModule_pos_x = 0;
	double EndcapModule_pos_y = 0;
	double EndcapModule_pos_z = 0;
	double rot_EM = 0;

	double EndcapModule_center_pos_z = HcalEndcap_min_z + box_half_y;

	double FEEModule_pos_x = 0;
	double FEEModule_pos_y = 0;
	double FEEModule_pos_z = 0;
	double FEEModule_center_pos_z = HcalEndcap_min_z + box_half_y;
	
	switch (stave_num)
	  {
	  case 0 : 
	    EndcapModule_pos_x = x_offset;
	    EndcapModule_pos_y = y_offset;
	    FEEModule_pos_x = x_offset;
	    FEEModule_pos_y = y_offset + box_half_z + Hcal_endcap_services_module_width/2.0;
	    break;
	  case 1 : 
	    EndcapModule_pos_x = -x_offset;
	    EndcapModule_pos_y = -y_offset;
	    FEEModule_pos_x = -x_offset;
	    FEEModule_pos_y = -y_offset - box_half_z - Hcal_endcap_services_module_width/2.0;
	    break;
	  }
	
	for(int module_num=0;module_num<2;module_num++) {

	  int module_id = (module_num==0)? 0:6;
	  
	  rot_EM = (module_id==0)?(-M_PI/2.0):(M_PI/2.0);
	  
	  EndcapModule_pos_z = (module_id==0)? -EndcapModule_center_pos_z:EndcapModule_center_pos_z;

	  PlacedVolume env_phv = envelope.placeVolume(envelopeVol,
						      Transform3D(RotationX(rot_EM),
								  Translation3D(EndcapModule_pos_x,
										EndcapModule_pos_y,
										EndcapModule_pos_z)));
	  env_phv.addPhysVolID("tower",endcapID);	  
	  env_phv.addPhysVolID("stave",stave_num);   // y: up /down
	  env_phv.addPhysVolID("module",module_id); // z: -/+ 0/6
	  env_phv.addPhysVolID("system",det_id);


	  FEEModule_pos_z = (module_id==0)? -FEEModule_center_pos_z:FEEModule_center_pos_z;

	  if (!(endcapID==0))
	    env_phv = envelope.placeVolume(FEEModule,
					   Transform3D(RotationX(rot_EM),
						       Translation3D(FEEModule_pos_x,
								     FEEModule_pos_y,
								     FEEModule_pos_z)));


	  DetElement sd = (module_num==0&&stave_num==0) ? stave_det : stave_det.clone(_toString(module_id,"module%d")+_toString(stave_num,"stave%d"));	  
	  sd.setPlacement(env_phv);	  

	}
	
      }

    endcapID++;
      
    }
  
  sdet.addExtension< LayeredCalorimeterData >( caloData ) ;  
  
  return sdet;
}
static Ref_t create_detector(Detector& theDetector,xml_h e,SensitiveDetector sens){
  typedef vector<PlacedVolume>Placements;
  xml_det_t x_det=e;
  Material vacuum=theDetector.vacuum();
  int det_id= x_det.id();
  string det_name=x_det.nameStr();
  bool reflect   = x_det.reflect(false);
  DetElement sdet(det_name,det_id);
  int m_id=0,c_id=0,n_sensor=0;
  map<string,Volume>modules;
  map<string,Placements>sensitives;
  PlacedVolume pv;
  
  //encoding that was missing  
  
  std::string cellIDEncoding=sens.readout().idSpec().fieldDescription();
  UTIL::BitField64 encoder(cellIDEncoding);
  encoder.reset();
  encoder[lcio::LCTrackerCellID::subdet()]=det_id;
  
  // --- create an envelope volume and position it into the world ---------------------
  
  Volume envelope=dd4hep::xml::createPlacedEnvelope(theDetector,e,sdet);
  dd4hep::xml::setDetectorTypeFlag(e,sdet);
  
  if(theDetector.buildType() == BUILD_ENVELOPE)return sdet;
  
  //-----------------------------------------------------------------------------------
  dd4hep::rec::ZDiskPetalsData* zDiskPetalsData=new dd4hep::rec::ZDiskPetalsData;
  //neighbour surfaces added 
  dd4hep::rec::NeighbourSurfacesData* neighbourSurfacesData=new dd4hep::rec::NeighbourSurfacesData();
  //
  std::map< std::string, double > moduleSensThickness;
  
  envelope.setVisAttributes(theDetector.invisible());
  sens.setType("tracker");
  
  for(xml_coll_t mi(x_det,_U(module));mi;++mi,++m_id){
    xml_comp_t x_mod=mi;
    string m_nam=x_mod.nameStr();
    xml_comp_t trd=x_mod.trd();
    double posY;
    double x1=trd.x1();
    double x2=trd.x2();
    double z=trd.z();
    double y1,y2,total_thickness=0.;
    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();
    y1 = y2 = total_thickness / 2;
    Volume m_volume(m_nam, Trapezoid(x1, x2, y1, y2, z), vacuum);      
    m_volume.setVisAttributes(theDetector.visAttributes(x_mod.visStr()));
    // Loop over slices 
    // The first slice (top in the xml) is placed at the "bottom" of the module
    
    for(ci.reset(), n_sensor=1, c_id=0, posY=-y1; ci; ++ci, ++c_id){
      xml_comp_t c=ci;
      double c_thick=c.thickness();
      Material c_mat=theDetector.material(c.materialStr());
      string c_name=_toString(c_id,"component%d");
      Volume c_vol(c_name, Trapezoid(x1,x2,c_thick/2e0,c_thick/2e0,z), c_mat);
      c_vol.setVisAttributes(theDetector.visAttributes(c.visStr()));
      pv = m_volume.placeVolume(c_vol,Position(0,posY+c_thick/2,0));
      if (c.isSensitive()){
        c_vol.setSensitiveDetector(sens);
        sensitives[m_nam].push_back(pv);
        ++n_sensor;
      }
      posY += c_thick;
    }
    modules[m_nam] = m_volume;
  }
  
  for(xml_coll_t li(x_det,_U(layer));li;++li){
    xml_comp_t x_layer(li);
    int l_id=x_layer.id();
    int mod_num=0;
    int ring_num=0;
    double sumZ(0.),innerR(1e100),outerR(0.);
    //loop only to count the number of rings in a disk - it is then needed for looking for neighborous when you are in a "border" cell
    int nrings = 0;
    
    for(xml_coll_t ri(x_layer,_U(ring)); ri; ++ri) { 
      nrings++;
    }
    dd4hep::rec::ZDiskPetalsData::LayerLayout thisLayer;
    
    for(xml_coll_t ri(x_layer,_U(ring)); ri; ++ri)  {
      xml_comp_t x_ring = ri;
      double r=x_ring.r();
      double phi0=x_ring.phi0(0);
      double zstart=x_ring.zstart();
      double dz=x_ring.dz(0);
      int nmodules=x_ring.nmodules();
      string m_nam=x_ring.moduleStr();
      Volume m_vol=modules[m_nam];
      double iphi=2*M_PI/nmodules;
      double phi=phi0;
      Placements& sensVols=sensitives[m_nam];
      Box mod_shape(m_vol.solid());
      
      if(r-mod_shape->GetDZ()<innerR)
	innerR=r-mod_shape->GetDZ();
      if(r+mod_shape->GetDZ()>outerR)
	outerR=r+mod_shape->GetDZ();
      sumZ+=zstart;
      r=r+mod_shape->GetDY();
      
      for(int k=0;k<nmodules;++k){
	string m_base=_toString(l_id,"layer%d")+_toString(mod_num,"_module%d")+_toString(k,"_sensor%d");
	double x=-r*std::cos(phi);
	double y=-r*std::sin(phi);
	DetElement module(sdet,m_base+"_pos",det_id);
	pv=envelope.placeVolume(m_vol,Transform3D(RotationZYX(0,-M_PI/2-phi,-M_PI/2),Position(x,y,zstart+dz)));
	pv.addPhysVolID("side",1).addPhysVolID("layer", l_id).addPhysVolID("module",mod_num).addPhysVolID("sensor",k);
	module.setPlacement(pv);
	
	for(size_t ic=0;ic<sensVols.size();++ic){
	  PlacedVolume sens_pv=sensVols[ic];
	  DetElement comp_elt(module,sens_pv.volume().name(),mod_num);
	  comp_elt.setPlacement(sens_pv);
	}
	
	if(reflect){
	  pv = envelope.placeVolume(m_vol,Transform3D(RotationZYX(M_PI,-M_PI/2-phi,-M_PI/2),Position(x,y,-zstart-dz)));
	  pv.addPhysVolID("side",-1).addPhysVolID("layer",l_id).addPhysVolID("module",mod_num).addPhysVolID("sensor",k);
	  DetElement r_module(sdet,m_base+"_neg",det_id);
	  r_module.setPlacement(pv);
	  for(size_t ic=0;ic<sensVols.size();++ic){
	    PlacedVolume sens_pv=sensVols[ic];
	    DetElement comp_elt(r_module,sens_pv.volume().name(),mod_num);
	    comp_elt.setPlacement(sens_pv);
	  }
	}
	
	//modified on  comparison with  TrackerEndcap_o2_v06_geo.cpp
	//get cellID and fill map< cellID of surface, vector of cellID of neighbouring surfaces >
	dd4hep::long64 cellID_reflect;
	if(reflect){
	  encoder[lcio::LCTrackerCellID::side()]=lcio::ILDDetID::bwd;
	  encoder[lcio::LCTrackerCellID::layer()]=l_id;
	  encoder[lcio::LCTrackerCellID::module()]=mod_num;
	  encoder[lcio::LCTrackerCellID::sensor()]=k;
	  
	  cellID_reflect=encoder.lowWord(); // 32 bits
	}
	
	encoder[lcio::LCTrackerCellID::side()]=lcio::ILDDetID::fwd;
	encoder[lcio::LCTrackerCellID::layer()]=l_id;
	encoder[lcio::LCTrackerCellID::module()]=mod_num;
	encoder[lcio::LCTrackerCellID::sensor()]=k;
	
	dd4hep::long64 cellID = encoder.lowWord(); // 32 bits
	
	//compute neighbours 
	
	int n_neighbours_module = 1; // 1 gives the adjacent modules (i do not think we would like to change this)
	int n_neighbours_sensor = 1;
	int newmodule=0,newsensor=0;
	
	for(int imodule=-n_neighbours_module; imodule<=n_neighbours_module; imodule++){ // neighbouring modules
	  for(int isensor=-n_neighbours_sensor; isensor<=n_neighbours_sensor; isensor++){ // neighbouring sensors
	    
	    if (imodule==0 && isensor==0) continue; // cellID we started with
	    newmodule = mod_num + imodule;
		newsensor = k + isensor;
		
		//compute special case at the boundary  
		//general computation to allow (if necessary) more then adiacent neighbours (ie: +-2)
		if (newsensor < 0) newsensor = nmodules + newsensor;
		if (newsensor >= nmodules) newsensor = newsensor - nmodules;
		if (newmodule < 0 || newmodule >= nrings)continue; //out of disk		
		
		//encoding
		encoder[lcio::LCTrackerCellID::module()] = newmodule;
		encoder[lcio::LCTrackerCellID::sensor()] = newsensor;
		    
		neighbourSurfacesData->sameLayer[cellID].push_back(encoder.lowWord());
		
		if (reflect){
		  encoder[lcio::LCTrackerCellID::side()]=lcio::ILDDetID::bwd;
		  encoder[lcio::LCTrackerCellID::layer()]=l_id;
		  encoder[lcio::LCTrackerCellID::module()]=newmodule;
		  encoder[lcio::LCTrackerCellID::sensor()]=newsensor;
		  neighbourSurfacesData->sameLayer[cellID_reflect].push_back(encoder.lowWord());
		}
	  }
	}
	dz   = -dz;
	phi += iphi;
      }
      ++mod_num;
      ++ring_num;
    }
    
    // Only filling what is needed for CED/DDMarlinPandora
    thisLayer.zPosition=sumZ/ring_num; // average z
    thisLayer.distanceSensitive=innerR;
    thisLayer.lengthSensitive=outerR - innerR;
    thisLayer.petalNumber=ring_num; // number of rings in petalNumber, needed for tracking
    zDiskPetalsData->layers.push_back(thisLayer);
  }
  
  sdet.setAttributes(theDetector,envelope,x_det.regionStr(),x_det.limitsStr(),x_det.visStr());
  
  sdet.addExtension<dd4hep::rec::ZDiskPetalsData>(zDiskPetalsData);
  //added extension 
  sdet.addExtension<dd4hep::rec::NeighbourSurfacesData>(neighbourSurfacesData);
  std::cout<<"XXX Tracker endcap layers:"<<zDiskPetalsData->layers.size()<<std::endl;
  
  return sdet;
}
static Ref_t create_detector(Detector& theDetector, xml_h e, SensitiveDetector sens)  {
  typedef vector<PlacedVolume> Placements;
  xml_det_t   x_det     = e;
  Material    vacuum    = theDetector.vacuum();
  int         det_id    = x_det.id();
  string      det_name  = x_det.nameStr();
  bool        reflect   = x_det.reflect(false);
  DetElement  sdet        (det_name,det_id);
  int         m_id=0, c_id=0, n_sensor=0;
  map<string, Volume> modules;
  map<string, Placements>  sensitives;
  PlacedVolume pv;

  // --- create an envelope volume and position it into the world ---------------------

  Volume envelope = dd4hep::xml::createPlacedEnvelope(theDetector, e, sdet);
  dd4hep::xml::setDetectorTypeFlag(e, sdet);

  if( theDetector.buildType() == BUILD_ENVELOPE ) return sdet;

  //-----------------------------------------------------------------------------------

  envelope.setVisAttributes(theDetector.invisible());
  sens.setType("tracker");

  // Build the sensor units
  // Loop over 'modules' as defined in the XML
  for(xml_coll_t mi(x_det,_U(module)); mi; ++mi, ++m_id) { 
    xml_comp_t x_mod   = mi;
    string     m_nam   = x_mod.nameStr();
    xml_comp_t trd     = x_mod.trd();
    double     posY;
    double     x1      = trd.x1();
    double     x2      = trd.x2();
    double     z       = trd.z();
    double     y1, y2, total_thickness=0.;
    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();
      
    y1 = y2 = total_thickness / 2;
    Volume m_volume(m_nam, Trapezoid(x1, x2, y1, y2, z), vacuum);      
    m_volume.setVisAttributes(theDetector.visAttributes(x_mod.visStr()));

    std::cout << m_nam << ", thickness=" << total_thickness << std::endl;

    // Loop over the module_components ('slices') in the 'module'
    // The first component (top in the XML) is placed at the 'bottom'
    for(ci.reset(), n_sensor=1, c_id=0, posY=-y1; ci; ++ci, ++c_id) {
      xml_comp_t c       = ci;
      double     c_thick = c.thickness();
      Material   c_mat   = theDetector.material(c.materialStr());
      string     c_name  = _toString(c_id, "component%d");
      Volume     c_vol(c_name, Trapezoid(x1,x2,c_thick/2e0,c_thick/2e0,z), c_mat);

      std::cout << " + sensor " << n_sensor << " " << c_name;

      c_vol.setVisAttributes(theDetector.visAttributes(c.visStr()));
      pv = m_volume.placeVolume(c_vol, Position(0, posY + c_thick/2, 0));
      if ( c.isSensitive() ) {
        sdet.check(n_sensor > 2, "SiTrackerEndcap::fromCompact: " + c_name + " Max of 2 modules allowed!");
	pv.addPhysVolID("sensor", n_sensor);
        c_vol.setSensitiveDetector(sens);
        sensitives[m_nam].push_back(pv);
	std::cout << " (" << n_sensor << " is sensitive) ";
        ++n_sensor;
      }
      std::cout << std::endl;
      posY += c_thick;
    }
    modules[m_nam] = m_volume;
  }
  // done building the 2 modules, of 12 layers each

  int mod_count[12] = {0};

  // Build now the detector itself
  // Loop over layers as defined in the XML
  for(xml_coll_t li(x_det, _U(layer)); li; ++li) {
    xml_comp_t x_layer(li);
    int l_id = x_layer.id();
    int ring_num = 0;
    
    std::cout << "Layer " << l_id << ":" << std::endl;

    // Loop over rings, as defined in the XML
    for(xml_coll_t ri(x_layer, _U(ring)); ri; ++ri) {
      xml_comp_t x_ring = ri;
      double r        = x_ring.r();
      double phi0     = x_ring.phi0(0);
      double zstart   = x_ring.zstart();
      double dz       = x_ring.dz(0);
      int    nmodules = x_ring.nmodules();
      string m_nam    = x_ring.moduleStr();
      Volume m_vol    = modules[m_nam];
      double iphi     = 2*M_PI/nmodules;
      double phi      = phi0;
      Placements& sensVols = sensitives[m_nam];

      // This driver version encodes the rings as layers and the
      // petals as modules, such that 'layer' 1 contains all innermost rings
      // and last 'layer' contains the outermost rings in the tracker
      // farthest away on z from the IP (unintuititive, but works)

      std::cout << " Ring " << ring_num << ":" << std::endl;

      // Loop over modules in each ring, modules are either type 1 or 2
      for(int k=0; k < nmodules; ++k) {

        double x = -r*std::cos(phi);
        double y = -r*std::sin(phi);

	for(int s=1-2*int(reflect); s<2; s+=1+int(reflect)){

	  string e_name = _toString(s, "side%d") + _toString(l_id, "_layer%d") + _toString(ring_num, "_ring%d") + _toString(k, "_sensor%d");
	  DetElement module(sdet, e_name, det_id);
	  pv = envelope.placeVolume(m_vol, Transform3D(RotationZYX(0,-M_PI/2-phi,-M_PI/2), Position(x, y, s*(zstart+dz) )));

	  pv.addPhysVolID("side", s).addPhysVolID("layer", ring_num).addPhysVolID("module", mod_count[ring_num] + k);
	  module.setPlacement(pv);

	  for(size_t ic=0; ic<sensVols.size(); ++ic) {
	    PlacedVolume sens_pv = sensVols[ic];
	    DetElement comp_elt(module, sens_pv.volume().name(), det_id);
	    comp_elt.setPlacement(sens_pv);
	    std::cout << "Name: " << e_name << "_" << sens_pv.volume().name() << std::endl;
	    std::cout << "  ID: side " << s << ", layer " << ring_num << ", module " << mod_count[ring_num] + k << ", sensor" << ic+1 << std::endl;
	  }
	}
        dz = -dz;
        phi += iphi;
      }
      mod_count[ring_num] += nmodules;
      ++ring_num;
    }
  }
  std::cout << "Number of modules per 'layer':" << std::endl;
  for(int ii=0; ii<12; ii++){
    std::cout << " mod_count[" << ii << "] = " << mod_count[ii] << std::endl;
  }

  return sdet;
}