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; }
/** 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; }
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; }
/** 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; }
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; }
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; }