Example #1
0
static DD4hep::Geometry::Ref_t createSimpleBarrel(DD4hep::Geometry::LCDD& aLcdd,
                                                  DD4hep::XML::Handle_t aXmlElement,
                                                  DD4hep::Geometry::SensitiveDetector aSensDet) {
  DD4hep::XML::DetElement x_det = aXmlElement;
  std::string name = x_det.nameStr();
  DD4hep::XML::Dimension dim(x_det.dimensions());
  DD4hep::XML::Dimension pos(x_det.position());
  DD4hep::XML::Dimension rot(x_det.rotation());
  DD4hep::Geometry::DetElement det(name, x_det.id());
  DD4hep::Geometry::Volume det_vol(name + "_vol", DD4hep::Geometry::Tube(dim.rmin(), dim.rmax(), dim.dz()),
                                   aLcdd.material(x_det.materialStr()));
  DD4hep::Geometry::Volume mother = aLcdd.pickMotherVolume(det);
  DD4hep::Geometry::Transform3D transform(
      DD4hep::Geometry::Rotation3D(DD4hep::Geometry::RotationZYX(rot.z(), rot.y(), rot.x())),
      DD4hep::Geometry::Position(pos.x(), pos.y(), pos.z()));
  DD4hep::Geometry::PlacedVolume phv = mother.placeVolume(det_vol, transform);
  det_vol.setVisAttributes(aLcdd, x_det.visStr());
  det_vol.setLimitSet(aLcdd, x_det.limitsStr());
  det_vol.setRegion(aLcdd, x_det.regionStr());
  if (x_det.isSensitive()) {
    DD4hep::Geometry::SensitiveDetector sd = aSensDet;
    DD4hep::XML::Dimension sd_typ = x_det.child(_U(sensitive));
    det_vol.setSensitiveDetector(aSensDet);
    sd.setType(sd_typ.typeStr());
  }
  if (x_det.hasAttr(_U(id))) {
    phv.addPhysVolID("system", x_det.id());
  }
  det.setPlacement(phv);
  return det;
}
static DD4hep::Geometry::Ref_t createCaloDiscs(DD4hep::Geometry::LCDD& aLcdd,
                                                     DD4hep::XML::Handle_t aXmlElement,
                                                     DD4hep::Geometry::SensitiveDetector aSensDet) {
  ServiceHandle<IMessageSvc> msgSvc("MessageSvc", "CalDiscsConstruction");
  MsgStream lLog(&(*msgSvc), "CalDiscsConstruction");

  DD4hep::XML::DetElement xmlDetElem = aXmlElement;
  std::string nameDet = xmlDetElem.nameStr();
  int idDet = xmlDetElem.id();
  DD4hep::XML::Dimension dim(xmlDetElem.dimensions());
  DD4hep::Geometry::DetElement caloDetElem(nameDet, idDet);

  // Create air envelope for the whole endcap
  DD4hep::Geometry::Cone envelopePositive(dim.dz(), dim.rmin1(), dim.rmax(), dim.rmin2(), dim.rmax());
  DD4hep::Geometry::Cone envelopeNegative(dim.dz(), dim.rmin2(), dim.rmax(), dim.rmin1(), dim.rmax());
  DD4hep::Geometry::UnionSolid envelopeShape(envelopePositive, envelopeNegative,
                                             DD4hep::Geometry::Position(0, 0, -2 * dim.z_offset()));
  DD4hep::Geometry::Volume envelopeVol(nameDet + "_vol", envelopeShape, aLcdd.material("Air"));
  DD4hep::Geometry::Volume envelopePositiveVol(nameDet + "_positive_vol", envelopePositive, aLcdd.material("Air"));
  DD4hep::Geometry::Volume envelopeNegativeVol(nameDet + "_negative_vol", envelopeNegative, aLcdd.material("Air"));

  lLog << MSG::DEBUG << "Placing dector on the positive side: (cm) " << dim.z_offset() << endmsg;
  buildOneSide(lLog, aLcdd, aSensDet, envelopePositiveVol, aXmlElement, 1);
  lLog << MSG::DEBUG << "Placing dector on the negative side: (cm) " << -dim.z_offset() << endmsg;
  buildOneSide(lLog, aLcdd, aSensDet, envelopeNegativeVol, aXmlElement, -1);

  // Place the envelope
  DD4hep::Geometry::PlacedVolume envelopePositivePhysVol = envelopeVol.placeVolume(envelopePositiveVol);
  envelopePositivePhysVol.addPhysVolID("subsystem", 0);
  DD4hep::Geometry::DetElement caloPositiveDetElem(caloDetElem, "positive", 0);
  caloPositiveDetElem.setPlacement(envelopePositivePhysVol);
  DD4hep::Geometry::PlacedVolume envelopeNegativePhysVol =
      envelopeVol.placeVolume(envelopeNegativeVol, DD4hep::Geometry::Position(0, 0, -2 * dim.z_offset()));
  envelopeNegativePhysVol.addPhysVolID("subsystem", 1);
  DD4hep::Geometry::DetElement caloNegativeDetElem(caloDetElem, "negative", 0);
  caloNegativeDetElem.setPlacement(envelopeNegativePhysVol);
  DD4hep::Geometry::Volume motherVol = aLcdd.pickMotherVolume(caloDetElem);
  DD4hep::Geometry::PlacedVolume envelopePhysVol =
      motherVol.placeVolume(envelopeVol, DD4hep::Geometry::Position(0., 0., dim.z_offset()));
  caloDetElem.setPlacement(envelopePhysVol);
  envelopePhysVol.addPhysVolID("system", idDet);
  return caloDetElem;
}
Example #3
0
/**
  Simple cone using dimensions to be used to define cone composed of 1 single material
  @author Clement Helsens
**/
static DD4hep::Geometry::Ref_t
createSimpleCone(DD4hep::Geometry::LCDD& lcdd, xml_h e, DD4hep::Geometry::SensitiveDetector sensDet) {
  xml_det_t x_det = e;
  std::string name = x_det.nameStr();
  DD4hep::Geometry::DetElement coneDet(name, x_det.id());

  DD4hep::Geometry::Volume experimentalHall = lcdd.pickMotherVolume(coneDet);

  xml_comp_t coneDim(x_det.child(_U(dimensions)));
  DD4hep::Geometry::Cone cone(coneDim.dz(), coneDim.rmin1(), coneDim.rmax1(), coneDim.rmin2(), coneDim.rmax2());

  DD4hep::Geometry::Volume coneVol(x_det.nameStr() + "_SimpleCone", cone, lcdd.material(coneDim.materialStr()));

  if (x_det.isSensitive()) {
    DD4hep::XML::Dimension sdType(x_det.child(_U(sensitive)));
    coneVol.setSensitiveDetector(sensDet);
    sensDet.setType(sdType.typeStr());  
  }

  DD4hep::Geometry::PlacedVolume conePhys;

  double zoff = coneDim.z_offset();
  if (fabs(zoff) > 0.000000000001) {
    double reflectionAngle = 0.;
    if (coneDim.hasAttr(_Unicode(reflect))) {
      if (coneDim.reflect()) {
        reflectionAngle = M_PI;
        }
    }
    DD4hep::Geometry::Position trans(0., 0., zoff);
    conePhys =
        experimentalHall.placeVolume(coneVol, DD4hep::Geometry::Transform3D(DD4hep::Geometry::RotationX(reflectionAngle), trans));
  } else
    conePhys = experimentalHall.placeVolume(coneVol);

  conePhys.addPhysVolID("system", x_det.id());

  coneDet.setPlacement(conePhys);

  coneDet.setVisAttributes(lcdd, x_det.visStr(), coneVol);
  return coneDet;
}
Example #4
0
static DD4hep::Geometry::Ref_t createTkLayoutTrackerEndcap(DD4hep::Geometry::LCDD& lcdd,
        DD4hep::XML::Handle_t xmlElement,
        DD4hep::Geometry::SensitiveDetector sensDet) {
    // shorthands
    DD4hep::XML::DetElement xmlDet = static_cast<DD4hep::XML::DetElement>(xmlElement);
    Dimension dimensions(xmlDet.dimensions());

    // get sensitive detector type from xml
    DD4hep::XML::Dimension sdTyp = xmlElement.child("sensitive");  // retrieve the type
    sensDet.setType(sdTyp.typeStr());  // set for the whole detector

    // definition of top volume
    std::string detName = xmlDet.nameStr();
    DetElement GenericTrackerEndcapWorld(detName, xmlDet.id());

    // envelope volume with the max dimensions of tracker for visualization etc.
    // contains both endcaps, in forward and in backwards direction
    // the part between -z1 and z1 is subtracted from the envelope
    DD4hep::Geometry::Tube posnegEnvelopeShape_add(dimensions.rmin(), dimensions.rmax(), dimensions.zmax());
    // make the negative shape slighly larger in the radial direction
    // to be sure that everything is subtracted between -z1 and z1
    DD4hep::Geometry::Box posnegEnvelopeShape_subtract(
        dimensions.rmax() * 1.001, dimensions.rmax() * 1.001, dimensions.zmin());
    DD4hep::Geometry::SubtractionSolid posnegEnvelopeShape(posnegEnvelopeShape_add, posnegEnvelopeShape_subtract);
    Volume posnegEnvelopeVolume(detName, posnegEnvelopeShape, lcdd.air());
    posnegEnvelopeVolume.setVisAttributes(lcdd.invisible());

    // envelope volume for one of the endcaps, either forward or backward
    DD4hep::Geometry::Tube envelopeShape(
        dimensions.rmin(), dimensions.rmax(), 0.5 * (dimensions.zmax() - dimensions.zmin()));
    Volume envelopeVolume(detName, envelopeShape, lcdd.air());
    envelopeVolume.setVisAttributes(lcdd.invisible());

    Component xDiscs = xmlElement.child("discs");
    Component xFirstDisc = xDiscs.child("discZPls");
    Component xFirstDiscRings = xFirstDisc.child("rings");

    // create disc volume
    double discThickness = (xFirstDisc.zmax() - xFirstDisc.zmin());
    DD4hep::Geometry::Tube discShape(dimensions.rmin(), dimensions.rmax(), 0.5 * discThickness);
    Volume discVolume("disc", discShape, lcdd.air());
    discVolume.setVisAttributes(lcdd.invisible());

    // generate rings and place in  discs
    int moduleCounter = 0;
    for (DD4hep::XML::Collection_t xRingColl(xFirstDiscRings, _U(ring)); nullptr != xRingColl; ++xRingColl) {
        Component xRing = static_cast<Component>(xRingColl);
        Component xRingModules = xRing.child("modules");
        Component xModuleOdd = xRingModules.child("moduleOdd");
        Component xModuleEven = xRingModules.child("moduleEven");
        Component xModuleProperties = xRing.child("moduleProperties");
        Component xModulePropertiesComp = xModuleProperties.child("components");
        Component xSensorProperties = xRing.child("sensorProperties");
        Volume moduleVolume("module",
                            DD4hep::Geometry::Trapezoid(0.5 * xModuleProperties.attr<double>("modWidthMin"),
                                    0.5 * xModuleProperties.attr<double>("modWidthMax"),
                                    0.5 * xModuleProperties.attr<double>("modThickness"),
                                    0.5 * xModuleProperties.attr<double>("modThickness"),
                                    0.5 * xSensorProperties.attr<double>("sensorLength")),
                            lcdd.material("Air"));

        // place components in module
        double integratedCompThickness = 0;
        int componentCounter = 0;
        for (DD4hep::XML::Collection_t xCompColl(xModulePropertiesComp, _U(component)); nullptr != xCompColl; ++xCompColl) {
            Component xComp = static_cast<Component>(xCompColl);
            Volume componentVolume("component",
                                   DD4hep::Geometry::Trapezoid(0.5 * xModuleProperties.attr<double>("modWidthMin"),
                                           0.5 * xModuleProperties.attr<double>("modWidthMax"),
                                           0.5 * xComp.thickness(),
                                           0.5 * xComp.thickness(),
                                           0.5 * xSensorProperties.attr<double>("sensorLength")),
                                   lcdd.material(xComp.materialStr()));
            PlacedVolume placedComponentVolume = moduleVolume.placeVolume(
                    componentVolume,
                    DD4hep::Geometry::Position(
                        0, integratedCompThickness - 0.5 * xModuleProperties.attr<double>("modThickness"), 0));
            placedComponentVolume.addPhysVolID("component", componentCounter);

            componentVolume.setSensitiveDetector(sensDet);
            integratedCompThickness += xComp.thickness();
            ++componentCounter;
        }
        unsigned int nPhi = xRing.attr<int>("nModules");
        double lX, lY, lZ;
        double phi = 0;
        double phiTilt, thetaTilt;
        for (unsigned int phiIndex = 0; phiIndex < nPhi; ++phiIndex) {
            if (0 == phiIndex % 2) {
                // the rotation for the odd module is already taken care
                // of by the position in tklayout xml
                phi = 2 * dd4hep::pi * static_cast<double>(phiIndex) / static_cast<double>(nPhi);
                lX = xModuleEven.X();
                lY = xModuleEven.Y();
                lZ = xModuleEven.Z() - dimensions.zmin() - discThickness * 0.5;
                phiTilt = xModuleEven.attr<double>("phiTilt");
                thetaTilt = xModuleEven.attr<double>("thetaTilt");
            } else {
                lX = xModuleOdd.X();
                lY = xModuleOdd.Y();
                lZ = xModuleOdd.Z() - dimensions.zmin() - discThickness * 0.5;
                phiTilt = xModuleOdd.attr<double>("phiTilt");
                thetaTilt = xModuleOdd.attr<double>("thetaTilt");
            }
            // position module in the x-y plane, smaller end inward
            // and incorporate phi tilt if any
            DD4hep::Geometry::RotationY lRotation1(M_PI * 0.5);
            DD4hep::Geometry::RotationX lRotation2(M_PI * 0.5 + phiTilt);
            // align radially
            DD4hep::Geometry::RotationZ lRotation3(atan(lY / lX));
            // theta tilt, if any -- note the different convention between
            // tklayout and here, thus the subtraction of pi / 2
            DD4hep::Geometry::RotationY lRotation4(thetaTilt - M_PI * 0.5);
            DD4hep::Geometry::RotationZ lRotation_PhiPos(phi);
            // position in  disk
            DD4hep::Geometry::Translation3D lTranslation(lX, lY, lZ);
            DD4hep::Geometry::Transform3D myTrafo(lRotation4 * lRotation3 * lRotation2 * lRotation1, lTranslation);
            PlacedVolume placedModuleVolume = discVolume.placeVolume(moduleVolume, lRotation_PhiPos * myTrafo);
            placedModuleVolume.addPhysVolID("module", moduleCounter);
            ++moduleCounter;
        }
    }

    unsigned int discCounter = 0;
    double currentZ;
    for (DD4hep::XML::Collection_t xDiscColl(xDiscs, "discZPls"); nullptr != xDiscColl; ++xDiscColl) {
        Component xDisc = static_cast<Component>(xDiscColl);
        currentZ = xDisc.z() - dimensions.zmin() - 0.5 * (dimensions.zmax() - dimensions.zmin());
        PlacedVolume placedDiscVolume = envelopeVolume.placeVolume(discVolume, DD4hep::Geometry::Position(0, 0, currentZ));
        placedDiscVolume.addPhysVolID("disc", discCounter);
        ++discCounter;
    }

    // top of the hierarchy
    Volume motherVol = lcdd.pickMotherVolume(GenericTrackerEndcapWorld);
    PlacedVolume placedEnvelopeVolume = motherVol.placeVolume(posnegEnvelopeVolume);
    placedEnvelopeVolume.addPhysVolID("system", xmlDet.id());

    // place everything twice -- forward / backward
    DD4hep::Geometry::Translation3D lTranslation_posEnvelope(
        0, 0, -dimensions.zmin() - 0.5 * (dimensions.zmax() - dimensions.zmin()));
    PlacedVolume placedGenericTrackerEndcap_pos = posnegEnvelopeVolume.placeVolume(
                envelopeVolume,
                DD4hep::Geometry::Position(0, 0, dimensions.zmin() + 0.5 * (dimensions.zmax() - dimensions.zmin())));
    PlacedVolume placedGenericTrackerEndcap_neg = posnegEnvelopeVolume.placeVolume(
                envelopeVolume, lTranslation_posEnvelope * DD4hep::Geometry::RotationX(dd4hep::pi));
    placedGenericTrackerEndcap_pos.addPhysVolID("posneg", 0);
    placedGenericTrackerEndcap_neg.addPhysVolID("posneg", 1);
    GenericTrackerEndcapWorld.setPlacement(placedEnvelopeVolume);
    return GenericTrackerEndcapWorld;
}
static DD4hep::Geometry::Ref_t createTkLayoutTrackerBarrel(DD4hep::Geometry::LCDD& lcdd,
                                                           DD4hep::XML::Handle_t xmlElement,
                                                           DD4hep::Geometry::SensitiveDetector sensDet) {
  // shorthands
  DD4hep::XML::DetElement xmlDet = static_cast<DD4hep::XML::DetElement>(xmlElement);
  Dimension dimensions(xmlDet.dimensions());
  // get sensitive detector type from xml
  DD4hep::XML::Dimension sdTyp = xmlElement.child(_Unicode(sensitive));
  // sensitive detector used for all sensitive parts of this detector
  sensDet.setType(sdTyp.typeStr());

  // definition of top volume
  // has min/max dimensions of tracker for visualization etc.
  std::string detectorName = xmlDet.nameStr();
  DetElement topDetElement(detectorName, xmlDet.id());
  Acts::ActsExtension::Config barrelConfig;
  barrelConfig.isBarrel = true;
  // detElement owns extension
  Acts::ActsExtension* detWorldExt = new Acts::ActsExtension(barrelConfig);
  topDetElement.addExtension<Acts::IActsExtension>(detWorldExt);
  DD4hep::Geometry::Tube topVolumeShape(
      dimensions.rmin(), dimensions.rmax(), (dimensions.zmax() - dimensions.zmin()) * 0.5);
  Volume topVolume(detectorName, topVolumeShape, lcdd.air());
  topVolume.setVisAttributes(lcdd.invisible());

  // counts all layers - incremented in the inner loop over repeat - tags
  unsigned int layerCounter = 0;
  double integratedModuleComponentThickness = 0;
  double phi = 0;
  // loop over 'layer' nodes in xml
  DD4hep::XML::Component xLayers = xmlElement.child(_Unicode(layers));
  for (DD4hep::XML::Collection_t xLayerColl(xLayers, _U(layer)); nullptr != xLayerColl; ++xLayerColl) {
    DD4hep::XML::Component xLayer = static_cast<DD4hep::XML::Component>(xLayerColl);
    DD4hep::XML::Component xRods = xLayer.child("rods");
    DD4hep::XML::Component xRodEven = xRods.child("rodOdd");
    DD4hep::XML::Component xRodOdd = xRods.child("rodEven");
    DD4hep::XML::Component xModulesEven = xRodEven.child("modules");
    DD4hep::XML::Component xModulePropertiesOdd = xRodOdd.child("moduleProperties");
    DD4hep::XML::Component xModulesOdd = xRodOdd.child("modules");
    DD4hep::Geometry::Tube layerShape(xLayer.rmin(), xLayer.rmax(), dimensions.zmax());
    Volume layerVolume("layer", layerShape, lcdd.material("Air"));
    layerVolume.setVisAttributes(lcdd.invisible());
    PlacedVolume placedLayerVolume = topVolume.placeVolume(layerVolume);
    placedLayerVolume.addPhysVolID("layer", layerCounter);
    DetElement lay_det(topDetElement, "layer" + std::to_string(layerCounter), layerCounter);
    Acts::ActsExtension::Config layConfig;
    layConfig.isLayer = true;
    // the local coordinate systems of modules in dd4hep and acts differ
    // see http://acts.web.cern.ch/ACTS/latest/doc/group__DD4hepPlugins.html
    layConfig.axes = "XzY"; // correct translation of local x axis in dd4hep to local x axis in acts
    // detElement owns extension
    Acts::ActsExtension* layerExtension = new Acts::ActsExtension(layConfig);
    lay_det.addExtension<Acts::IActsExtension>(layerExtension);
    lay_det.setPlacement(placedLayerVolume);
    DD4hep::XML::Component xModuleComponentsOdd = xModulePropertiesOdd.child("components");
    integratedModuleComponentThickness = 0;
    int moduleCounter = 0;
    Volume moduleVolume;
    for (DD4hep::XML::Collection_t xModuleComponentOddColl(xModuleComponentsOdd, _U(component));
         nullptr != xModuleComponentOddColl;
         ++xModuleComponentOddColl) {
      DD4hep::XML::Component xModuleComponentOdd = static_cast<DD4hep::XML::Component>(xModuleComponentOddColl);
      moduleVolume = Volume("module",
                            DD4hep::Geometry::Box(0.5 * xModulePropertiesOdd.attr<double>("modWidth"),
                                                  0.5 * xModuleComponentOdd.thickness(),
                                                  0.5 * xModulePropertiesOdd.attr<double>("modLength")),
                            lcdd.material(xModuleComponentOdd.materialStr()));
      unsigned int nPhi = xRods.repeat();
      DD4hep::XML::Handle_t currentComp;
      for (unsigned int phiIndex = 0; phiIndex < nPhi; ++phiIndex) {
        double lX = 0;
        double lY = 0;
        double lZ = 0;
        if (0 == phiIndex % 2) {
          phi = 2 * M_PI * static_cast<double>(phiIndex) / static_cast<double>(nPhi);
          currentComp = xModulesEven;
        } else {
          currentComp = xModulesOdd;
        }
        for (DD4hep::XML::Collection_t xModuleColl(currentComp, _U(module)); nullptr != xModuleColl; ++xModuleColl) {
          DD4hep::XML::Component xModule = static_cast<DD4hep::XML::Component>(xModuleColl);
          double currentPhi = atan2(xModule.Y(), xModule.X());
          double componentOffset =  integratedModuleComponentThickness - 0.5 * xModulePropertiesOdd.attr<double>("modThickness") + 0.5 * xModuleComponentOdd.thickness();
          lX = xModule.X() + cos(currentPhi) * componentOffset;
          lY = xModule.Y() + sin(currentPhi) * componentOffset;
          lZ = xModule.Z();
          DD4hep::Geometry::Translation3D moduleOffset(lX, lY, lZ);
          DD4hep::Geometry::Transform3D lTrafo(DD4hep::Geometry::RotationZ(atan2(lY,  lX) + 0.5 * M_PI), moduleOffset);
          DD4hep::Geometry::RotationZ lRotation(phi);
          PlacedVolume placedModuleVolume = layerVolume.placeVolume(moduleVolume, lRotation * lTrafo);
          if (xModuleComponentOdd.isSensitive()) {
            placedModuleVolume.addPhysVolID("module", moduleCounter);
            moduleVolume.setSensitiveDetector(sensDet);
            DetElement mod_det(lay_det, "module" + std::to_string(moduleCounter), moduleCounter);
            mod_det.setPlacement(placedModuleVolume);
            ++moduleCounter;
          }
        }
      }
      integratedModuleComponentThickness += xModuleComponentOdd.thickness();
    }
    ++layerCounter;
  }
  Volume motherVol = lcdd.pickMotherVolume(topDetElement);
  PlacedVolume placedGenericTrackerBarrel = motherVol.placeVolume(topVolume);
  placedGenericTrackerBarrel.addPhysVolID("system", topDetElement.id());
  topDetElement.setPlacement(placedGenericTrackerBarrel);
  return topDetElement;
}
Example #6
0
/**
Factory for a configurable, generic tracker endcap.
@author: Valentin Volkl
*/
static DD4hep::Geometry::Ref_t createGenericTrackerEndcap(DD4hep::Geometry::LCDD& lcdd,
                                                          DD4hep::XML::Handle_t xmlElement,
                                                          DD4hep::Geometry::SensitiveDetector sensDet) {
  // shorthands
  DD4hep::XML::DetElement xmlDet = static_cast<DD4hep::XML::DetElement>(xmlElement);
  Dimension dimensions(xmlDet.dimensions());

  // get sensitive detector type from xml
  DD4hep::XML::Dimension sdTyp = xmlElement.child("sensitive");  // retrieve the type
  if (xmlDet.isSensitive()) {
    sensDet.setType(sdTyp.typeStr());  // set for the whole detector
  }

  // definition of top volume
  std::string detName = xmlDet.nameStr();
  DetElement GenericTrackerEndcapWorld(detName, xmlDet.id());

  // envelope volume with the max dimensions of tracker for visualization etc.
  // contains both endcaps, in forward and in backwards direction
  // the part between -z1 and z1 is subtracted from the envelope
  DD4hep::Geometry::Tube posnegEnvelopeShape_add(dimensions.rmin(), dimensions.rmax(), (dimensions.z2()));
  // make the negative shape slighly larger in the radial direction 
  // to be sure that everything is subtracted between -z1 and z1
  DD4hep::Geometry::Box posnegEnvelopeShape_subtract(
      dimensions.rmax() * 1.001, dimensions.rmax() * 1.001, dimensions.z1());
  DD4hep::Geometry::SubtractionSolid posnegEnvelopeShape(posnegEnvelopeShape_add, posnegEnvelopeShape_subtract);
  Volume posnegEnvelopeVolume(detName, posnegEnvelopeShape, lcdd.air());
  posnegEnvelopeVolume.setVisAttributes(lcdd.invisible());

  // envelope volume for one of the endcaps, either forward or backward
  DD4hep::Geometry::Tube envelopeShape(dimensions.rmin(), dimensions.rmax(), 0.5 * (dimensions.z2() - dimensions.z1()));
  Volume envelopeVolume(detName, envelopeShape, lcdd.air());
  envelopeVolume.setVisAttributes(lcdd.invisible());

  // loop over 'layer' nodes in xml
  unsigned int layerCounter = 0;
  for (DD4hep::XML::Collection_t xLayerColl(xmlElement, _U(layers)); nullptr != xLayerColl; ++xLayerColl) {
    DD4hep::XML::Component xLayer = static_cast<DD4hep::XML::Component>(xLayerColl);


    // create petals
    unsigned int nPhi = static_cast<unsigned int>(getAttrValueWithFallback(xLayer, "nPhi", 16));

    const double lModuleTwistAngle = getAttrValueWithFallback(xLayer, "module_twist_angle", 0.05 * M_PI);
    double dr = xLayer.rmax() - xLayer.rmin();
    double dphi = 2 * dd4hep::pi / static_cast<double>(nPhi);
    double tn = tan(dphi);
    Volume petalVolume(
        "petal",
        DD4hep::Geometry::Trapezoid(
            0.5 * xLayer.rmin() * tn, 0.5 * xLayer.rmax() * tn, xLayer.thickness(), xLayer.thickness(), 0.5 * dr),
        lcdd.material("Silicon"));
    petalVolume.setVisAttributes(lcdd, xLayer.visStr());
    petalVolume.setSensitiveDetector(sensDet);

    // handle repeat attribute in xml
    double layerThickness;
    unsigned int numLayers;
    double current_z;
    // "repeat" layers  equidistant between rmin and rmax
    numLayers = xLayer.repeat();
    layerThickness = (xLayer.z2() - xLayer.z1()) / numLayers;
    // create layers.
    for (unsigned int repeatIndex = 0; repeatIndex < numLayers; ++repeatIndex) {
      DD4hep::Geometry::Tube layerShape(xLayer.rmin(), xLayer.rmax(), 0.5 * layerThickness);
      Volume layerVolume("layer" + std::to_string(layerCounter), layerShape, lcdd.air());
      layerVolume.setVisAttributes(lcdd.invisible());
      ++layerCounter;
      // place layers not at center, but at z1 value of containing envelope
      // subtract half of the envelope length
      current_z = (repeatIndex + 0.5) * layerThickness + xLayer.z1() - dimensions.z1();
      PlacedVolume placedLayerVolume = envelopeVolume.placeVolume(
          layerVolume, DD4hep::Geometry::Position(0, 0, current_z - 0.5 * (dimensions.z2() - dimensions.z1())));
      placedLayerVolume.addPhysVolID("layer", layerCounter);

      double phi;
      double r = xLayer.rmin();
      for (unsigned int phiIndex = 0; phiIndex < nPhi; ++phiIndex) {
        phi = 2 * dd4hep::pi * static_cast<double>(phiIndex) / static_cast<double>(nPhi);
        // oriented along z at first
        DD4hep::Geometry::Translation3D lTranslation_ringPhiPos(0, 0, r + 0.5 * dr);
        DD4hep::Geometry::RotationY lRotation_ringPhiPos(phi);
        DD4hep::Geometry::RotationX lRotation_orientRing(0.5 * dd4hep::pi);
        // twist petals slightly so they can overlap
        DD4hep::Geometry::RotationZ lRotation_twist(lModuleTwistAngle);
        PlacedVolume placedPetalVolume = layerVolume.placeVolume(
            petalVolume, lRotation_orientRing * lRotation_ringPhiPos * lTranslation_ringPhiPos * lRotation_twist);
        placedPetalVolume.addPhysVolID("petal", phiIndex);
      }
    }
  }

  // top of the hierarchy
  Volume motherVol = lcdd.pickMotherVolume(GenericTrackerEndcapWorld);
  PlacedVolume placedEnvelopeVolume = motherVol.placeVolume(posnegEnvelopeVolume);
  placedEnvelopeVolume.addPhysVolID("system", xmlDet.id());

  // place everything twice -- forward / backward
  DD4hep::Geometry::Translation3D lTranslation_posEnvelope(
      0, 0, -dimensions.z1() - 0.5 * (dimensions.z2() - dimensions.z1()));
  PlacedVolume placedGenericTrackerEndcap_pos = posnegEnvelopeVolume.placeVolume(
      envelopeVolume, DD4hep::Geometry::Position(0, 0, dimensions.z1() + 0.5 * (dimensions.z2() - dimensions.z1())));
  PlacedVolume placedGenericTrackerEndcap_neg = posnegEnvelopeVolume.placeVolume(
      envelopeVolume, lTranslation_posEnvelope * DD4hep::Geometry::RotationX(dd4hep::pi));
  placedGenericTrackerEndcap_pos.addPhysVolID("posneg", 0);
  placedGenericTrackerEndcap_neg.addPhysVolID("posneg", 1);
  GenericTrackerEndcapWorld.setPlacement(placedEnvelopeVolume);
  return GenericTrackerEndcapWorld;
}
Example #7
0
static DD4hep::Geometry::Ref_t createHCal (
  DD4hep::Geometry::LCDD& lcdd,
  xml_h xmlElement,
  DD4hep::Geometry::SensitiveDetector sensDet
  ) {
  // Get the Gaudi message service and message stream:
  ServiceHandle<IMessageSvc> msgSvc("MessageSvc", "HCalConstruction");
  MsgStream lLog(&(*msgSvc), "HCalConstruction");

  xml_det_t xmlDet = xmlElement;
  std::string detName = xmlDet.nameStr();
  //Make DetElement
  DetElement hCal(detName, xmlDet.id());
  // Get status for the RecoGeometry what is status?
  // xml_comp_t xmlStatus = xmlDet.child(_U(status));
  // int status = xmlStatus.id();
  // add Extension to Detlement for the RecoGeometry
  // Let's skip this for now...
  // Det::DetCylinderVolume* detVolume = new Det::DetCylinderVolume(status);
  // hCal.addExtension<Det::IDetExtension>(detVolume);

  // Make volume that envelopes the whole barrel; set material to air
  Dimension dimensions(xmlDet.dimensions());
  DD4hep::Geometry::Tube envelopeShape(dimensions.rmin(), dimensions.rmax(), dimensions.dz());
  Volume envelopeVolume(detName, envelopeShape, lcdd.air());
  // Invisibility seems to be broken in visualisation tags, have to hardcode that
  // envelopeVolume.setVisAttributes(lcdd, dimensions.visStr());
  envelopeVolume.setVisAttributes(lcdd.invisible());

  // set the sensitive detector type to the DD4hep calorimeter
  sensDet.setType("SimpleCalorimeterSD");

  // Add structural support made of steel inside of HCal
  xml_comp_t xFacePlate = xmlElement.child("face_plate");
  double dRhoFacePlate = xFacePlate.thickness();
  double sensitiveBarrelRmin = dimensions.rmin() + dRhoFacePlate;
  DetElement facePlate("facePlate", 0);
  DD4hep::Geometry::Tube facePlateShape(dimensions.rmin(), sensitiveBarrelRmin, dimensions.dz());
  Volume facePlateVol("facePlate", facePlateShape, lcdd.material(xFacePlate.materialStr()));
  facePlateVol.setVisAttributes(lcdd, xFacePlate.visStr());
  PlacedVolume placedFacePlate = envelopeVolume.placeVolume(facePlateVol);
  placedFacePlate.addPhysVolID("facePlate", facePlate.id());
  facePlate.setPlacement(placedFacePlate);


  // Add structural support made of steel at both ends of HCal
  xml_comp_t xEndPlate = xmlElement.child("end_plate");
  double dZEndPlate = xEndPlate.thickness();

  DD4hep::Geometry::Tube endPlateShape(dimensions.rmin(), dimensions.rmax(), dZEndPlate);
  Volume endPlateVol("endPlate", endPlateShape, lcdd.material(xEndPlate.materialStr()));
  endPlateVol.setVisAttributes(lcdd, xEndPlate.visStr());

  DetElement endPlatePos("endPlate", 0);
  DD4hep::Geometry::Position posOffset(0, 0, dimensions.dz() -  dZEndPlate);
  PlacedVolume placedEndPlatePos = envelopeVolume.placeVolume(endPlateVol, posOffset);
  placedEndPlatePos.addPhysVolID("endPlatePos", endPlatePos.id());
  endPlatePos.setPlacement(placedEndPlatePos);

  DetElement endPlateNeg("endPlate", 1);
  DD4hep::Geometry::Position negOffset(0, 0, -dimensions.dz() +  dZEndPlate);
  PlacedVolume placedEndPlateNeg = envelopeVolume.placeVolume(endPlateVol, negOffset);
  placedEndPlateNeg.addPhysVolID("endPlateNeg", endPlateNeg.id());
  endPlateNeg.setPlacement(placedEndPlateNeg);


  // Hard-coded assumption that we have two different sequences for the modules
  std::vector<xml_comp_t> sequences = {xmlElement.child("sequence_a"), xmlElement.child("sequence_b")};
  // NOTE: This assumes that both have the same dimensions!
  Dimension moduleDimensions(sequences[0].dimensions());
  double dzModule = moduleDimensions.dz();

  // calculate the number of modules fitting in phi, Z and Rho
  unsigned int numModulesPhi = moduleDimensions.phiBins();
  unsigned int numModulesZ = static_cast<unsigned>(dimensions.dz() / dzModule);
  unsigned int numModulesR = static_cast<unsigned>((dimensions.rmax() - sensitiveBarrelRmin) / moduleDimensions.dr());
  lLog << MSG::DEBUG << "constructing " << numModulesPhi << " modules per ring in phi, "
                     << numModulesZ << " rings in Z, "
                     << numModulesR << " rings (layers) in Rho"
                     << numModulesR*numModulesZ*numModulesPhi << " modules" << endmsg;

  // Calculate correction along z based on the module size (can only have natural number of modules)
  double dzDetector = numModulesZ * dzModule + dZEndPlate;
  lLog << MSG::INFO << "correction of dz:" << dimensions.dz() - dzDetector << endmsg;

  // calculate the dimensions of one module:
  double dphi = 2 * dd4hep::pi / static_cast<double>(numModulesPhi);
  double tn = tan(dphi / 2.);
  double spacing = moduleDimensions.x();
  double dy0 = moduleDimensions.dz();
  double dz0 = moduleDimensions.dr() / 2.;

  double drWedge = cos(dphi / 2.) * (dimensions.rmax() - sensitiveBarrelRmin) * 0.5;

  double dxWedge1 = tn * sensitiveBarrelRmin - spacing;
  double dxWedge2 = tn * cos(dphi / 2.) * dimensions.rmax() - spacing;

  // First we construct one wedge with width of one module:
  Volume subWedgeVolume("subWedge", DD4hep::Geometry::Trapezoid(
        dxWedge1, dxWedge2, dzModule, dzModule, drWedge
      ), lcdd.material("Air")
  );
  for (unsigned int idxLayer = 0; idxLayer < numModulesR; ++idxLayer) {
    auto layerName = std::string("wedge") + DD4hep::XML::_toString(idxLayer, "layer%d");
    unsigned int sequenceIdx = idxLayer % 2;
    double rminLayer = idxLayer * moduleDimensions.dr();
    double rmaxLayer = (idxLayer + 1) * cos(dphi / 2.) * moduleDimensions.dr();
    double dx1 = tn * (rminLayer + sensitiveBarrelRmin) - spacing;
    double dx2 = tn * cos(dphi / 2.) * (rmaxLayer + sensitiveBarrelRmin) - spacing;
    // -drWedge to place it in the middle of the wedge-volume
    double rMiddle = rminLayer + 0.5 * moduleDimensions.dr() - drWedge;
    Volume moduleVolume(layerName, DD4hep::Geometry::Trapezoid(
        dx1, dx2, dy0, dy0, dz0
      ), lcdd.material("Air")
    );
    moduleVolume.setVisAttributes(lcdd.invisible());
    unsigned int idxSubMod = 0;
    // DetElement moduleDet(wedgeDet, layerName, idxLayer);
    double modCompZOffset = -moduleDimensions.dz();
    for (xml_coll_t xCompColl(sequences[sequenceIdx], _U(module_component)); xCompColl; ++xCompColl, ++idxSubMod) {
      xml_comp_t xComp = xCompColl;
      std::string subModuleName = layerName+DD4hep::XML::_toString(idxSubMod, "module_component%d");
      double dyComp = xComp.thickness();
      Volume modCompVol(subModuleName, DD4hep::Geometry::Trapezoid(
          dx1, dx2, dyComp, dyComp, dz0
        ), lcdd.material(xComp.materialStr())
      );
      if (xComp.isSensitive()) {
        modCompVol.setSensitiveDetector(sensDet);
      }
      modCompVol.setVisAttributes(lcdd, xComp.visStr());
      // modCompVol.setVisAttributes(lcdd.invisible());
      // DetElement modCompDet(wedgeDet, subModuleName, idxSubMod);
      DD4hep::Geometry::Position offset(0, modCompZOffset + dyComp + xComp.y_offset()*2, 0);
      PlacedVolume placedModCompVol = moduleVolume.placeVolume(modCompVol, offset);
      placedModCompVol.addPhysVolID("sub_module", idxSubMod);
      // modCompDet.setPlacement(placedModCompVol);
      modCompZOffset += xComp.thickness()*2 + xComp.y_offset()*2;
    }
    DD4hep::Geometry::Position modOffset(0, 0, rMiddle);
    PlacedVolume placedModuleVol = subWedgeVolume.placeVolume(moduleVolume, modOffset);
    placedModuleVol.addPhysVolID("layer", idxLayer);
    // moduleDet.setPlacement(placedModuleVol);
  }

  // Now we place the components along z within the wedge
  Volume wedgeVolume("wedge", DD4hep::Geometry::Trapezoid(
        dxWedge1, dxWedge2, dzDetector, dzDetector, drWedge
      ), lcdd.material("Air")
  );
  wedgeVolume.setVisAttributes(lcdd.invisible());

  for (unsigned int idxZRow = 0; idxZRow < numModulesZ; ++idxZRow) {
    double zOffset = -dzDetector + dZEndPlate * 2 + (2*idxZRow + 1) * dzModule;
    auto wedgeRowName = DD4hep::XML::_toString(idxZRow, "row%d");
    DD4hep::Geometry::Position wedgeOffset(0, zOffset, 0);
    PlacedVolume placedRowVolume = wedgeVolume.placeVolume(subWedgeVolume, wedgeOffset);
    placedRowVolume.addPhysVolID("row", idxZRow);
    // wedgeDet.setPlacement(placedWedgeVol);
  }

  // Finally we place all the wedges around phi
  for (unsigned int idxPhi = 0; idxPhi < numModulesPhi; ++idxPhi) {
    auto modName = DD4hep::XML::_toString(idxPhi, "mod%d");
    // Volume and DetElement for one row in Z
    DetElement wedgeDet(hCal, modName, idxPhi);
    // moduleVolume.setVisAttributes(lcdd, sequences[sequenceIdx].visStr());
    // moduleVolume.setVisAttributes(lcdd.invisible());
    // calculate position and rotation of this wedge;
    // first rotation due to default rotation of trapezoid
    double phi = 0.5 * dphi - idxPhi * dphi; // 0.5*dphi for middle of module
    double yPosModule = (sensitiveBarrelRmin + drWedge) * cos(phi);
    double xPosModule = (sensitiveBarrelRmin + drWedge) * sin(phi);
    DD4hep::Geometry::Position moduleOffset(xPosModule, yPosModule, 0);
    DD4hep::Geometry::Transform3D trans(
      DD4hep::Geometry::RotationX(-0.5*dd4hep::pi)*
      DD4hep::Geometry::RotationY(phi),
      moduleOffset
    );
    PlacedVolume placedWedgeVol = envelopeVolume.placeVolume(wedgeVolume, trans);
    placedWedgeVol.addPhysVolID("wedge", idxPhi);
    wedgeDet.setPlacement(placedWedgeVol);
  }

  //Place envelope (or barrel) volume
  Volume motherVol = lcdd.pickMotherVolume(hCal);
  PlacedVolume placedHCal = motherVol.placeVolume(envelopeVolume);
  placedHCal.addPhysVolID("system", hCal.id());
  hCal.setPlacement(placedHCal);
  return hCal;

}
Example #8
0
static DD4hep::Geometry::Ref_t createGenericTrackerBarrel(DD4hep::Geometry::LCDD& lcdd,
                                                          DD4hep::XML::Handle_t xmlElement,
                                                          DD4hep::Geometry::SensitiveDetector sensDet) {
  // shorthands
  DD4hep::XML::DetElement xmlDet = static_cast<DD4hep::XML::DetElement>(xmlElement);
  Dimension dimensions(xmlDet.dimensions());
  // get sensitive detector type from xml
  DD4hep::XML::Dimension sdTyp = xmlElement.child("sensitive");
  if (xmlDet.isSensitive()) {
    // sensitive detector used for all sensitive parts of this detector
    sensDet.setType(sdTyp.typeStr());
  }
  // definition of top volume
  // has min/max dimensions of tracker for visualization etc.
  std::string detectorName = xmlDet.nameStr();
  DetElement topDetElement(detectorName, xmlDet.id());
  DD4hep::Geometry::Tube topVolumeShape(dimensions.rmin(), dimensions.rmax(), dimensions.dz());
  Volume topVolume(detectorName, topVolumeShape, lcdd.air());
  topVolume.setVisAttributes(lcdd.invisible());

  // counts all layers - incremented in the inner loop over repeat - tags
  unsigned int layerCounter = 0;
  // loop over 'layer' nodes in xml
  for (DD4hep::XML::Collection_t xLayerColl(xmlElement, _U(layers)); nullptr != xLayerColl; ++xLayerColl) {
    DD4hep::XML::Component xLayer = static_cast<DD4hep::XML::Component>(xLayerColl);

    DD4hep::XML::Component xModuleComponents = xmlElement.child("module_components");
    DD4hep::XML::Component xModule =
        utils::getNodeByStrAttr(xmlElement, "module", "name", xLayer.attr<std::string>("module"));

    // optional parameters
    double stereo_offset = utils::getAttrValueWithFallback(xLayer, "stereo_offset", 0.0);
    double module_twist_angle = utils::getAttrValueWithFallback(xLayer, "module_twist_angle", 0.1 * M_PI);
    double stereo_module_overlap = utils::getAttrValueWithFallback(xLayer, "stereo_module_overlap", 0.0);

    // get total thickness of module
    unsigned int idxSubMod = 0;
    double totalModuleComponentThickness = 0;
    for (DD4hep::XML::Collection_t xCompColl(xModuleComponents, _U(module_component)); nullptr != xCompColl;
         ++xCompColl, ++idxSubMod) {
      DD4hep::XML::Component xComp = static_cast<DD4hep::XML::Component>(xCompColl);
      totalModuleComponentThickness += xComp.thickness();
    }
    // now that thickness is known: define module components volumes
    idxSubMod = 0;
    double integratedModuleComponentThickness = 0;
    std::vector<Volume> moduleComponentVector;
    for (DD4hep::XML::Collection_t xCompColl(xModuleComponents, _U(module_component)); nullptr != xCompColl;
         ++xCompColl, ++idxSubMod) {
      DD4hep::XML::Component xComp = static_cast<DD4hep::XML::Component>(xCompColl);
      std::string moduleComponentName = "layer" + std::to_string(layerCounter) + "_rod_module_component" +
          std::to_string(idxSubMod) + "_" + xComp.materialStr();
      Volume moduleComponentVolume(moduleComponentName,
                                   DD4hep::Geometry::Box(xModule.width(), xComp.thickness(), xModule.length()),
                                   lcdd.material(xComp.materialStr()));
      moduleComponentVolume.setVisAttributes(lcdd, xComp.visStr());
      if (xComp.isSensitive()) {
        moduleComponentVolume.setSensitiveDetector(sensDet);
      }
      moduleComponentVector.push_back(moduleComponentVolume);
    }

    // definition of module volume (smallest independent subdetector)
    // define the module whose name was given in the "layer" xml Element
    Volume moduleVolume("module", DD4hep::Geometry::Box(xModule.width(), xModule.thickness(), xModule.length()),
                        lcdd.material("Air"));
    moduleVolume.setVisAttributes(lcdd, xModule.visStr());

    // definition of rod volume (longitudinal arrangement of modules)
    Volume rodVolume("GenericTrackerBarrel_layer" + std::to_string(layerCounter) + "_rod",
                     DD4hep::Geometry::Box(xModule.width(), xModule.thickness(), xLayer.dz()),
                     lcdd.material("Air"));
    rodVolume.setVisAttributes(lcdd.invisible());

    /// @todo: allow for more than one type of module components
    // analogous to module
    // place module substructure in module
    std::string moduleComponentName = "moduleComponent";
    idxSubMod = 0;
    for (DD4hep::XML::Collection_t xCompColl(xModuleComponents, _U(module_component)); nullptr != xCompColl;
         ++xCompColl, ++idxSubMod) {
      DD4hep::XML::Component xComp = static_cast<DD4hep::XML::Component>(xCompColl);
      DD4hep::Geometry::Position offset(0, -0.5 * totalModuleComponentThickness + integratedModuleComponentThickness,
                                        0);
      integratedModuleComponentThickness += xComp.thickness();
      PlacedVolume placedModuleComponentVolume = moduleVolume.placeVolume(moduleComponentVector[idxSubMod], offset);
      placedModuleComponentVolume.addPhysVolID("module_component", idxSubMod);
    }

    // handle repeat attribute in xml
    // "repeat" layers  equidistant between rmin and rmax
    double numRepeat = xLayer.repeat();
    double layerThickness = (xLayer.rmax() - xLayer.rmin()) / numRepeat;
    double layer_rmin = xLayer.rmin();
    unsigned int nPhi = 0;
    double r = 0;
    double phi = 0;
    // loop over repeated layers defined by one layer tag
    for (unsigned int repeatIndex = 0; repeatIndex < numRepeat; ++repeatIndex) {
      ++layerCounter;
      // let r be the middle between two equidistant layer boundaries
      r = layer_rmin + (0.5 + repeatIndex) * layerThickness;
      // definition of layer volumes
      DD4hep::Geometry::Tube layerShape(r - 0.5*layerThickness, r + 0.5*layerThickness, xLayer.dz());
      std::string layerName = "layer" + std::to_string(layerCounter);
      Volume layerVolume(layerName, layerShape, lcdd.material("Silicon"));
      layerVolume.setVisAttributes(lcdd.invisible());
      PlacedVolume placedLayerVolume = topVolume.placeVolume(layerVolume);
      placedLayerVolume.addPhysVolID("layer", layerCounter);
      // approximation of tklayout values
      double phiOverlapFactor = utils::getAttrValueWithFallback(xLayer, "phi_overlap_factor", 1.15);
      nPhi = static_cast<unsigned int>( phiOverlapFactor * 2 * M_PI * r / (2 * xModule.width()));
      for (unsigned int phiIndex = 0; phiIndex < nPhi; ++phiIndex) {
        phi = 2 * M_PI * static_cast<double>(phiIndex) / static_cast<double>(nPhi);
        DD4hep::Geometry::Translation3D lTranslation(r * cos(phi), r * sin(phi), 0);
        DD4hep::Geometry::RotationZ lRotation(phi + module_twist_angle + 0.5 * M_PI);
        PlacedVolume placedRodVolume = layerVolume.placeVolume(rodVolume, lTranslation * lRotation);
        placedRodVolume.addPhysVolID("rod", phiIndex);
      }
    }
    // placement of modules within rods
    unsigned int zRepeat = static_cast<int>(xLayer.dz() / (xModule.length() - stereo_module_overlap));
    // stereo overlap
    for (unsigned int zIndex = 0; zIndex < zRepeat; ++zIndex) {
      stereo_offset *= -1.;
      DD4hep::Geometry::Position moduleOffset(0, stereo_offset,
                                              zIndex * 2 * (xModule.length() - stereo_module_overlap) - xLayer.dz() +
                                                  xModule.length() - stereo_module_overlap);
      PlacedVolume placedModuleVolume = rodVolume.placeVolume(moduleVolume, moduleOffset);
      placedModuleVolume.addPhysVolID("module", zIndex);
    }
  }
  Volume motherVol = lcdd.pickMotherVolume(topDetElement);
  PlacedVolume placedGenericTrackerBarrel = motherVol.placeVolume(topVolume);
  placedGenericTrackerBarrel.addPhysVolID("system", topDetElement.id());
  topDetElement.setPlacement(placedGenericTrackerBarrel);
  return topDetElement;
}
void buildOneSide(MsgStream& lLog, DD4hep::Geometry::LCDD& aLcdd, DD4hep::Geometry::SensitiveDetector& aSensDet,
                    DD4hep::Geometry::Volume& aEnvelope, DD4hep::XML::Handle_t& aXmlElement, int sign) {

  DD4hep::XML::Dimension dim(aXmlElement.child(_Unicode(dimensions)));

  DD4hep::XML::DetElement active = aXmlElement.child(_Unicode(active));
  std::string activeMaterial = active.materialStr();
  double activeThickness = active.thickness();

  DD4hep::XML::DetElement readout = aXmlElement.child(_Unicode(readout));
  std::string readoutMaterial = readout.materialStr();
  double readoutThickness = readout.thickness();

  DD4hep::XML::DetElement passive = aXmlElement.child(_Unicode(passive));
  DD4hep::XML::DetElement passiveInner = passive.child(_Unicode(inner));
  DD4hep::XML::DetElement passiveOuter = passive.child(_Unicode(outer));
  DD4hep::XML::DetElement passiveGlue = passive.child(_Unicode(glue));
  double passiveInnerThickness = passiveInner.thickness();
  double passiveOuterThickness = passiveOuter.thickness();
  double passiveGlueThickness = passiveGlue.thickness();
  double passiveThickness = passiveInnerThickness + passiveOuterThickness + passiveGlueThickness;
  std::string passiveInnerMaterial = passiveInner.materialStr();
  std::string passiveOuterMaterial = passiveOuter.materialStr();
  std::string passiveGlueMaterial = passiveGlue.materialStr();
  std::string passiveMaterial;
  if (passiveInnerThickness < passiveThickness) {
    passiveMaterial = "Air";
  } else {
    passiveMaterial = passiveInnerMaterial;
  }

  DD4hep::Geometry::SensitiveDetector sensDet = aSensDet;
  DD4hep::XML::Dimension sensDetType = aXmlElement.child(_U(sensitive));
  sensDet.setType(sensDetType.typeStr());
  lLog << MSG::INFO << " rmin (cm) = " << dim.rmin1() << " rmin (cm) = " << dim.rmin2() << " rmax (cm) = " << dim.rmax()
       << " length (cm) = " << dim.dz() << " Sensitive volume of type: " << sensDetType.typeStr() << endmsg;
  double length = dim.dz() * 2;
  // First disc set is different: readout is first (HV), then half of active material, then absorber (GND)
  // Next disc sets have active material on both sides
  double lengthWithoutFirst = length - readoutThickness - activeThickness / 2. - passiveThickness;
  uint numDiscs = floor(lengthWithoutFirst / (activeThickness + readoutThickness + passiveThickness));
  double marginOutside = (lengthWithoutFirst - numDiscs * (activeThickness + readoutThickness + passiveThickness)) / 2.;
  // add the first disc set to the number of all discs
  numDiscs += 1;
  lLog << MSG::INFO << "Thickness of active material in between absorbers (cm) = " << activeThickness
       << "\nThickness of absorber discs (cm) = " << passiveThickness
       << "\nThickness of readout disc placed in between absorber plates (cm) = " << readoutThickness
       << "\nNumber of absorber/readout discs: " << numDiscs
       << "\nMargin outside first readout disc and last absorber disc, filled with non-sensitive active medium (cm) = "
       << marginOutside << endmsg;
  lLog << MSG::INFO << "Detector length: (cm) " << length << endmsg;
  // Place components starting from closer to the collision-point
  double zOffset = (length / 2. - marginOutside) * -sign;
  double rMax = dim.rmax();
  // First disc to place is readout
  zOffset += sign * (readoutThickness / 2.);
  double nonAbsorberRmin = std::min(dim.rmin1(), dim.rmin2());
  double tanTheta = fabs(dim.rmin2() - dim.rmin1()) / (2 * dim.dz());
  nonAbsorberRmin +=
      (marginOutside + readoutThickness + activeThickness / 2.) * tanTheta;         // for first readout position
  double dR1 = passiveThickness * tanTheta;                                         // between readout and passive
  double dR2 = (activeThickness + readoutThickness + passiveThickness) * tanTheta;  // between two readout discs
  DD4hep::Geometry::Tube readoutShapePre(nonAbsorberRmin, rMax, readoutThickness / 2.);
  DD4hep::Geometry::Tube activeShapePre(nonAbsorberRmin, rMax, activeThickness / 4.);
  DD4hep::Geometry::Volume readoutVolPre("readoutPre", readoutShapePre, aLcdd.material(readoutMaterial));
  if (readout.isSensitive()) {
    lLog << MSG::INFO << "Readout volume set as sensitive" << endmsg;
    readoutVolPre.setSensitiveDetector(aSensDet);
  }
  DD4hep::Geometry::Volume activeVolPre("activePre", activeShapePre, aLcdd.material(activeMaterial));
  activeVolPre.setSensitiveDetector(sensDet);
  DD4hep::Geometry::PlacedVolume readoutPhysVolPre =
      aEnvelope.placeVolume(readoutVolPre, DD4hep::Geometry::Position(0, 0, zOffset));
  readoutPhysVolPre.addPhysVolID("layer", 0);
  readoutPhysVolPre.addPhysVolID("type", 2);  // 0 = active, 1 = passive, 2 = readout
  std::vector<DD4hep::Geometry::PlacedVolume> activePhysVols;
  activePhysVols.reserve(numDiscs * 2);
  activePhysVols.push_back(aEnvelope.placeVolume(
      activeVolPre, DD4hep::Geometry::Position(0, 0, zOffset + sign * (readoutThickness / 2. + activeThickness / 4.))));
  lLog << MSG::DEBUG << "Placing first readout at " << zOffset
       << " and active at z= " << zOffset + sign * (activeThickness / 4. + readoutThickness / 2.) << endmsg;
  // Now place complete sets of discs: absorber|active|readout|active
  zOffset += sign * (readoutThickness / 2. + activeThickness / 2. + passiveThickness / 2.);
  // Loop placing readout, active and passive discs
  for (uint iDiscs = 0; iDiscs < numDiscs - 1; iDiscs++) {
    nonAbsorberRmin += dR2;
    // readout and active discs have the same radius, but different thickness
    DD4hep::Geometry::Tube activeShapeBeforeSubtraction(nonAbsorberRmin, rMax,
                                                        activeThickness / 2. + readoutThickness / 2.);
    DD4hep::Geometry::Tube readoutShape(nonAbsorberRmin, rMax, readoutThickness / 2.);
    DD4hep::Geometry::SubtractionSolid activeShape(activeShapeBeforeSubtraction, readoutShape);
    DD4hep::Geometry::Tube passiveShape(nonAbsorberRmin + dR1, rMax, passiveThickness / 2.);
    DD4hep::Geometry::Volume activeVol("active", activeShape, aLcdd.material(activeMaterial));
    DD4hep::Geometry::Volume readoutVol("readout", readoutShape, aLcdd.material(readoutMaterial));
    DD4hep::Geometry::Volume passiveVol("passive", passiveShape, aLcdd.material(passiveMaterial));
    activeVol.setSensitiveDetector(sensDet);
    if (readout.isSensitive()) {
      lLog << MSG::DEBUG << "Passive inner volume set as sensitive" << endmsg;
      readoutVol.setSensitiveDetector(aSensDet);
    }
    if (passive.isSensitive()) {
      lLog << MSG::DEBUG << "Passive volume set as sensitive" << endmsg;
      passiveVol.setSensitiveDetector(aSensDet);
    }
    // absorber may consist of inner and outer material
    if (passiveInnerThickness < passiveThickness) {
      // create shapes
      DD4hep::Geometry::Tube passiveInnerShape(nonAbsorberRmin + dR1, rMax, passiveInnerThickness / 2.);
      DD4hep::Geometry::Tube passiveGlueShape(nonAbsorberRmin + dR1, rMax, passiveGlueThickness / 4.);
      DD4hep::Geometry::Tube passiveOuterShape(nonAbsorberRmin + dR1, rMax, passiveOuterThickness / 4.);
      // create volumes
      DD4hep::Geometry::Volume passiveInnerVol(passiveInnerMaterial + "_passive", passiveInnerShape,
                                               aLcdd.material(passiveInnerMaterial));
      DD4hep::Geometry::Volume passiveOuterVol(passiveOuterMaterial + "_passive", passiveOuterShape,
                                               aLcdd.material(passiveOuterMaterial));
      DD4hep::Geometry::Volume passiveGlueVol(passiveGlueMaterial + "_passive", passiveGlueShape,
                                              aLcdd.material(passiveGlueMaterial));
      if (passive.isSensitive()) {
        lLog << MSG::INFO << "Passive volumes (inner, outer, glue) set as sensitive" << endmsg;
        passiveInnerVol.setSensitiveDetector(aSensDet);
        passiveOuterVol.setSensitiveDetector(aSensDet);
        passiveGlueVol.setSensitiveDetector(aSensDet);
      }
      // place volumes
      DD4hep::Geometry::PlacedVolume passiveInnerPhysVol =
          passiveVol.placeVolume(passiveInnerVol, DD4hep::Geometry::Position(0, 0, 0));
      DD4hep::Geometry::PlacedVolume passiveOuterPhysVolBelow = passiveVol.placeVolume(
          passiveOuterVol,
          DD4hep::Geometry::Position(0, 0, passiveInnerThickness / 2. + passiveGlueThickness / 2. +
                                         passiveOuterThickness / 4.));
      DD4hep::Geometry::PlacedVolume passiveOuterPhysVolAbove = passiveVol.placeVolume(
          passiveOuterVol,
          DD4hep::Geometry::Position(0, 0, -passiveInnerThickness / 2. - passiveGlueThickness / 2. -
                                         passiveOuterThickness / 4.));
      DD4hep::Geometry::PlacedVolume passiveGluePhysVolBelow = passiveVol.placeVolume(
          passiveGlueVol, DD4hep::Geometry::Position(0, 0, -passiveInnerThickness / 2. - passiveGlueThickness / 4.));
      DD4hep::Geometry::PlacedVolume passiveGluePhysVolAbove = passiveVol.placeVolume(
          passiveGlueVol, DD4hep::Geometry::Position(0, 0, passiveInnerThickness / 2. + passiveGlueThickness / 4.));
      passiveInnerPhysVol.addPhysVolID("subtype", 0);
      passiveOuterPhysVolBelow.addPhysVolID("subtype", 1);
      passiveOuterPhysVolAbove.addPhysVolID("subtype", 2);
      passiveGluePhysVolBelow.addPhysVolID("subtype", 3);
      passiveGluePhysVolAbove.addPhysVolID("subtype", 4);
    }
    DD4hep::Geometry::PlacedVolume passivePhysVol =
        aEnvelope.placeVolume(passiveVol, DD4hep::Geometry::Position(0, 0, zOffset));
    passivePhysVol.addPhysVolID("layer", iDiscs);
    passivePhysVol.addPhysVolID("type", 1);  // 0 = active, 1 = passive, 2 = readout
    DD4hep::Geometry::PlacedVolume readoutPhysVol = aEnvelope.placeVolume(
        readoutVol,
        DD4hep::Geometry::Position(0, 0, zOffset +
                                       sign * (passiveThickness / 2. + activeThickness / 2. + readoutThickness / 2.)));
    readoutPhysVol.addPhysVolID("layer", iDiscs + 1);  // +1 because first readout is placed before that loop
    readoutPhysVol.addPhysVolID("type", 2);            // 0 = active, 1 = passive, 2 = readout
    activePhysVols.push_back(aEnvelope.placeVolume(
        activeVol,
        DD4hep::Geometry::Position(0, 0, zOffset +
                                       sign * (passiveThickness / 2. + activeThickness / 2. + readoutThickness / 2.))));
    lLog << MSG::DEBUG << "Placing passive at z= " << zOffset
         << " readout at z= " << zOffset + sign * (passiveThickness / 2. + activeThickness / 2. + readoutThickness / 2.)
         << " active at " << zOffset + sign * (passiveThickness / 2. + activeThickness / 2. + readoutThickness / 2.)
         << endmsg;
    zOffset += sign * (readoutThickness + activeThickness + passiveThickness);
    if (iDiscs == numDiscs - 2) {
      // finish detector with the last disc of abosrber (for GND layer)
      DD4hep::Geometry::PlacedVolume passivePhysVolPost =
          aEnvelope.placeVolume(passiveVol, DD4hep::Geometry::Position(0, 0, zOffset));
      passivePhysVolPost.addPhysVolID("layer", iDiscs + 1);
      passivePhysVolPost.addPhysVolID("type", 2);  // 0 = active, 1 = passive, 2 = readout
      lLog << MSG::DEBUG << "Placing last passive disc at z= " << zOffset << endmsg;
    }
    for (uint iActive = 0; iActive < activePhysVols.size(); iActive++) {
      activePhysVols[iActive].addPhysVolID("layer", iActive + 1);  // +1 because first active is placed before that loop
      activePhysVols[iActive].addPhysVolID("type", 0);             // 0 = active, 1 = passive, 2 = readout
    }
  }
  return;
}