static Ref_t create_detector(Detector& lcdd, xml_h e, SensitiveDetector /*sens*/) { xml_det_t x_det = e; int det_id = x_det.id(); string det_name = x_det.nameStr(); DetElement sdet(det_name, det_id); bool reflect = x_det.reflect(); Volume envelope = dd4hep::xml::createPlacedEnvelope(lcdd, e , sdet) ; if (lcdd.buildType() == BUILD_ENVELOPE) return sdet ; const double phi1 = 0 ; const double phi2 = 360.0 * dd4hep::degree; for (xml_coll_t c(x_det, Unicode("section")); c; ++c) { xml_comp_t xmlSection(c); const double zStart = xmlSection.attr< double > (_Unicode(start)); const double zEnd = xmlSection.attr< double > (_Unicode(end)); const double rInnerStart = xmlSection.attr< double > (_Unicode(rMin1)); const double rInnerEnd = xmlSection.attr< double > (_Unicode(rMin2)); const double rOuterStart = xmlSection.attr< double > (_Unicode(rMax1)); const double rOuterEnd = xmlSection.attr< double > (_Unicode(rMax2)); const double thickness = rOuterStart - rInnerStart; Material sectionMat = lcdd.material(xmlSection.materialStr()); const std::string volName = "section_" + xmlSection.nameStr(); const double zHalf = fabs(zEnd - zStart) * 0.5; // half z length of the cone const double zPosition = fabs(zEnd + zStart) * 0.5; // middle z position // solid for the tube (including vacuum and wall): a solid cone ConeSegment tubeSolid(zHalf, rInnerStart, rOuterStart, rInnerEnd, rOuterEnd , phi1, phi2); Volume tubeVol(volName + "_pos", tubeSolid, sectionMat); tubeVol.setAttributes(lcdd, xmlSection.regionStr(), xmlSection.limitsStr(), xmlSection.visStr()); envelope.placeVolume(tubeVol, Position(0, 0, zPosition)); const double dr = rInnerEnd - rInnerStart ; const double theta = atan2(dr , 2.* zHalf) ; Vector3D ocon(rInnerStart + 0.5 * (dr + thickness), 0. , 0.); Vector3D v(1. , 0. , theta, Vector3D::spherical) ; VolCone conSurf1(tubeVol , SurfaceType(SurfaceType::Helper) , 0.5 * thickness , 0.5 * thickness , v, ocon); volSurfaceList(sdet)->push_back(conSurf1); if (reflect) { Volume tubeVol2(volName + "_neg", tubeSolid, sectionMat); tubeVol2.setAttributes(lcdd, xmlSection.regionStr(), xmlSection.limitsStr(), xmlSection.visStr()); Transform3D Vol2Place(RotationY(-180.0 * dd4hep::degree), Position(0, 0, -1.*zPosition)); envelope.placeVolume(tubeVol2, Vol2Place); VolCone conSurf2(tubeVol2, SurfaceType(SurfaceType::Helper) , 0.5 * thickness , 0.5 * thickness , v, ocon); volSurfaceList(sdet)->push_back(conSurf2); } } return sdet; }
/** Construction of VTX detector, ported from Mokka driver TubeX01.cc * * Mokka History: * - first implementation as Tube00: Paulo Mora de Freitas, Sep 2002 * - modified from Tube00 to Tube01DT: Ties Behnke, 2003-02-11 * - modified for a crossing angle as TubeX00: Adrian Vogel, 2005-05-18 * - modified for fancier geometries as TubeX01: Adrian Vogel, 2006-04-20 * * @author: F.Gaede, DESY, Jan 2014 * */ static Ref_t create_element(Detector& theDetector, xml_h element, SensitiveDetector /*sens*/) { //------------------------------------------ // See comments starting with '//**' for // hints on porting issues //------------------------------------------ std::cout << "This is the Beampipe:" << std::endl; //Access to the XML File xml_det_t xmlBeampipe = element; const std::string name = xmlBeampipe.nameStr(); DetElement tube( name, xmlBeampipe.id() ) ; // --- create an envelope volume and position it into the world --------------------- Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector, element , tube ) ; dd4hep::xml::setDetectorTypeFlag( element, tube ) ; if( theDetector.buildType() == BUILD_ENVELOPE ) return tube ; //----------------------------------------------------------------------------------- ConicalSupportData* beampipeData = new ConicalSupportData ; //###################################################################################################################################################################### // code ported from TubeX01::construct() : //################################## //** DD4hep/TGeo seems to need rad (as opposed to the manual) const double phi1 = 0 ; const double phi2 = 360.0*dd4hep::degree; //Parameters we have to know about dd4hep::xml::Component xmlParameter = xmlBeampipe.child(_Unicode(parameter)); const double crossingAngle = xmlParameter.attr< double >(_Unicode(crossingangle))*0.5; // only half the angle double min_radius = 1.e99 ; for(xml_coll_t c( xmlBeampipe ,Unicode("section")); c; ++c) { xml_comp_t xmlSection( c ); ODH::ECrossType crossType = ODH::getCrossType(xmlSection.attr< std::string >(_Unicode(type))); const double zStart = xmlSection.attr< double > (_Unicode(start)); const double zEnd = xmlSection.attr< double > (_Unicode(end)); const double rInnerStart = xmlSection.attr< double > (_Unicode(rMin1)); const double rInnerEnd = xmlSection.attr< double > (_Unicode(rMin2)); const double rOuterStart = xmlSection.attr< double > (_Unicode(rMax1)); const double rOuterEnd = xmlSection.attr< double > (_Unicode(rMax2)); const double thickness = rOuterStart - rInnerStart; Material sectionMat = theDetector.material(xmlSection.materialStr()); const std::string volName = "tube_" + xmlSection.nameStr(); std::cout << std::setw(8) << zStart /dd4hep::mm << std::setw(8) << zEnd /dd4hep::mm << std::setw(8) << rInnerStart /dd4hep::mm << std::setw(8) << rInnerEnd /dd4hep::mm << std::setw(8) << rOuterStart /dd4hep::mm << std::setw(8) << rOuterEnd /dd4hep::mm << std::setw(8) << thickness /dd4hep::mm << std::setw(8) << crossType << std::setw(35) << volName << std::setw(15) << sectionMat.name() << std::endl; if( crossType == ODH::kCenter ) { // store only the central sections ! ConicalSupportData::Section section ; section.rInner = rInnerStart ; section.rOuter = rOuterStart ; section.zPos = zStart ; beampipeData->sections.push_back( section ) ; } // things which can be calculated immediately const double zHalf = fabs(zEnd - zStart) * 0.5; // half z length of the cone const double zPosition = fabs(zEnd + zStart) * 0.5; // middle z position Material coreMaterial = theDetector.material("beam"); // always the same Material wallMaterial = sectionMat; // this could mess up your geometry, so better check it if (not checkForSensibleGeometry(crossingAngle, crossType)){ throw std::runtime_error( " Beampipe_o1_v01_geo.cpp : checkForSensibleGeometry() failed " ) ; // return false; } const double rotateAngle = getCurrentAngle(crossingAngle, crossType); // for the placement at +z (better make it const now) const double mirrorAngle = M_PI - rotateAngle; // for the "mirrored" placement at -z // the "mirroring" in fact is done by a rotation of (almost) 180 degrees around the y-axis switch (crossType) { case ODH::kCenter: case ODH::kUpstream: case ODH::kDnstream: { // a volume on the z-axis, on the upstream branch, or on the downstream branch // absolute transformations for the placement in the world Transform3D transformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition), rotateAngle) ); Transform3D transmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition), mirrorAngle) ); // solid for the tube (including vacuum and wall): a solid cone ConeSegment tubeSolid( zHalf, 0, rOuterStart, 0, rOuterEnd , phi1, phi2); // tube consists of vacuum // place tube twice explicitely so we can attach surfaces to each one Volume tubeLog( volName, tubeSolid, coreMaterial ) ; Volume tubeLog2( volName, tubeSolid, coreMaterial ) ; // placement of the tube in the world, both at +z and -z envelope.placeVolume( tubeLog, transformer ); envelope.placeVolume( tubeLog2, transmirror ); // if inner and outer radii are equal, then omit the tube wall if (rInnerStart != rOuterStart || rInnerEnd != rOuterEnd) { // the wall solid: a tubular cone ConeSegment wallSolid( zHalf, rInnerStart, rOuterStart, rInnerEnd, rOuterEnd, phi1, phi2); // the wall consists of the material given in the XML Volume wallLog ( volName + "_wall", wallSolid, wallMaterial); Volume wallLog2( volName + "_wall2", wallSolid, wallMaterial); if( crossType == ODH::kCenter ) { // add surface for tracking .... const bool isCylinder = ( rInnerStart == rInnerEnd ); if (isCylinder) { // cylinder Vector3D ocyl( rInnerStart + thickness/2. , 0. , 0. ) ; VolCylinder cylSurf1( wallLog , SurfaceType( SurfaceType::Helper ) , 0.5*thickness , 0.5*thickness , ocyl ); VolCylinder cylSurf2( wallLog2, SurfaceType( SurfaceType::Helper ) , 0.5*thickness , 0.5*thickness , ocyl ); volSurfaceList( tube )->push_back( cylSurf1 ); volSurfaceList( tube )->push_back( cylSurf2 ); }else{ // cone const double dr = rInnerEnd - rInnerStart ; const double theta = atan2( dr , 2.* zHalf ) ; Vector3D ocon( rInnerStart + 0.5 * ( dr + thickness ), 0. , 0. ); Vector3D v( 1. , 0. , theta, Vector3D::spherical ) ; VolCone conSurf1( wallLog , SurfaceType( SurfaceType::Helper ) , 0.5*thickness , 0.5*thickness , v, ocon ); VolCone conSurf2( wallLog2, SurfaceType( SurfaceType::Helper ) , 0.5*thickness , 0.5*thickness , v, ocon ); volSurfaceList( tube )->push_back( conSurf1 ); volSurfaceList( tube )->push_back( conSurf2 ); } if( rInnerStart < min_radius ) min_radius = rInnerStart ; if( rOuterStart < min_radius ) min_radius = rOuterStart ; } wallLog.setVisAttributes(theDetector, "TubeVis"); wallLog2.setVisAttributes(theDetector, "TubeVis"); tubeLog.setVisAttributes(theDetector, "VacVis"); tubeLog2.setVisAttributes(theDetector, "VacVis"); // placement as a daughter volume of the tube, will appear in both placements of the tube tubeLog.placeVolume( wallLog, Transform3D() ); tubeLog2.placeVolume( wallLog2, Transform3D() ); } } break; case ODH::kPunchedCenter: { // a volume on the z-axis with one or two inner holes // (implemented as a cone from which tubes are punched out) const double rUpstreamPunch = rInnerStart; // just alias names denoting what is meant here const double rDnstreamPunch = rInnerEnd; // (the database entries are "abused" in this case) // relative transformations for the composition of the SubtractionVolumes Transform3D upstreamTransformer(RotationY(-crossingAngle), Position(zPosition * tan(-crossingAngle), 0, 0)); Transform3D dnstreamTransformer(RotationY(+crossingAngle), Position(zPosition * tan(+crossingAngle), 0, 0)); // absolute transformations for the final placement in the world (angles always equal zero and 180 deg) Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition) , rotateAngle) ); Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition) , mirrorAngle) ); // solid for the tube (including vacuum and wall): a solid cone ConeSegment tubeSolid( zHalf, 0, rOuterStart, 0, rOuterEnd, phi1, phi2); // tube consists of vacuum (will later have two different daughters) Volume tubeLog0( volName + "_0", tubeSolid, coreMaterial ); Volume tubeLog1( volName + "_1", tubeSolid, coreMaterial ); // placement of the tube in the world, both at +z and -z envelope.placeVolume( tubeLog0, placementTransformer ); envelope.placeVolume( tubeLog1, placementTransmirror ); // the wall solid and placeholders for possible SubtractionSolids ConeSegment wholeSolid( zHalf, 0, rOuterStart, 0, rOuterEnd, phi1, phi2); Solid tmpSolid0, tmpSolid1, wallSolid0, wallSolid1; // the punched subtraction solids can be asymmetric and therefore have to be created twice: // one time in the "right" way, another time in the "reverse" way, because the "mirroring" // rotation around the y-axis will not only exchange +z and -z, but also +x and -x if ( rUpstreamPunch > 1e-6 ) { // do we need a hole on the upstream branch? Tube upstreamPunch( 0, rUpstreamPunch, 5 * zHalf, phi1, phi2); // a bit longer tmpSolid0 = SubtractionSolid( wholeSolid, upstreamPunch, upstreamTransformer); tmpSolid1 = SubtractionSolid( wholeSolid, upstreamPunch, dnstreamTransformer); // [sic] } else { // dont't do anything, just pass on the unmodified shape tmpSolid0 = wholeSolid; tmpSolid1 = wholeSolid; } if (rDnstreamPunch > 1e-6 ) { // do we need a hole on the downstream branch? Tube dnstreamPunch( 0, rDnstreamPunch, 5 * zHalf, phi1, phi2); // a bit longer wallSolid0 = SubtractionSolid( tmpSolid0, dnstreamPunch, dnstreamTransformer); wallSolid1 = SubtractionSolid( tmpSolid1, dnstreamPunch, upstreamTransformer); // [sic] } else { // dont't do anything, just pass on the unmodified shape wallSolid0 = tmpSolid0; wallSolid1 = tmpSolid1; } // the wall consists of the material given in the XML Volume wallLog0( volName + "_wall_0", wallSolid0, wallMaterial ); Volume wallLog1( volName + "_wall_1", wallSolid1, wallMaterial ); wallLog0.setVisAttributes(theDetector, "TubeVis"); wallLog1.setVisAttributes(theDetector, "TubeVis"); tubeLog0.setVisAttributes(theDetector, "VacVis"); tubeLog1.setVisAttributes(theDetector, "VacVis"); // placement as a daughter volumes of the tube tubeLog0.placeVolume( wallLog0, Position() ); tubeLog1.placeVolume( wallLog1, Position() ); break; } case ODH::kPunchedUpstream: case ODH::kPunchedDnstream: { // a volume on the upstream or downstream branch with two inner holes // (implemented as a cone from which another tube is punched out) const double rCenterPunch = (crossType == ODH::kPunchedUpstream) ? (rInnerStart) : (rInnerEnd); // just alias names denoting what is meant here const double rOffsetPunch = (crossType == ODH::kPunchedDnstream) ? (rInnerStart) : (rInnerEnd); // (the database entries are "abused" in this case) // relative transformations for the composition of the SubtractionVolumes Transform3D punchTransformer(RotationY(-2 * rotateAngle), Position(zPosition * tan(-2 * rotateAngle), 0, 0)); Transform3D punchTransmirror(RotationY(+2 * rotateAngle), Position(zPosition * tan(+2 * rotateAngle), 0, 0)); // absolute transformations for the final placement in the world Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition) , rotateAngle) ); Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition) , mirrorAngle) ); // solid for the tube (including vacuum and wall): a solid cone ConeSegment tubeSolid( zHalf, 0, rOuterStart, 0, rOuterEnd, phi1, phi2); // tube consists of vacuum (will later have two different daughters) Volume tubeLog0( volName + "_0", tubeSolid, coreMaterial ); Volume tubeLog1( volName + "_1", tubeSolid, coreMaterial ); // placement of the tube in the world, both at +z and -z envelope.placeVolume( tubeLog0, placementTransformer ); envelope.placeVolume( tubeLog1, placementTransmirror ); // the wall solid and the piece (only a tube, for the moment) which will be punched out ConeSegment wholeSolid( zHalf, rCenterPunch , rOuterStart, rCenterPunch, rOuterEnd, phi1, phi2); Tube punchSolid( 0, rOffsetPunch, 5 * zHalf, phi1, phi2); // a bit longer // the punched subtraction solids can be asymmetric and therefore have to be created twice: // one time in the "right" way, another time in the "reverse" way, because the "mirroring" // rotation around the y-axis will not only exchange +z and -z, but also +x and -x SubtractionSolid wallSolid0( wholeSolid, punchSolid, punchTransformer); SubtractionSolid wallSolid1( wholeSolid, punchSolid, punchTransmirror); // the wall consists of the material given in the database Volume wallLog0( volName + "_wall_0", wallSolid0, wallMaterial ); Volume wallLog1( volName + "_wall_1", wallSolid1, wallMaterial ); wallLog0.setVisAttributes(theDetector, "TubeVis"); wallLog1.setVisAttributes(theDetector, "TubeVis"); tubeLog0.setVisAttributes(theDetector, "VacVis"); tubeLog1.setVisAttributes(theDetector, "VacVis"); // placement as a daughter volumes of the tube tubeLog0.placeVolume( wallLog0 , Position() ); tubeLog1.placeVolume( wallLog1 , Position() ); break; } case ODH::kUpstreamClippedFront: case ODH::kDnstreamClippedFront: case ODH::kUpstreamSlicedFront: case ODH::kDnstreamSlicedFront: { // a volume on the upstream or donwstream branch, but with the front face parallel to the xy-plane // or to a piece tilted in the other direction ("sliced" like a salami with 2 * rotateAngle) // (implemented as a slightly longer cone from which the end is clipped off) // the volume which will be used for clipping: a solid tube const double clipSize = rOuterStart; // the right order of magnitude for the clipping volume (alias name) Tube clipSolid( 0, 2 * clipSize, clipSize, phi1, phi2); // should be large enough // relative transformations for the composition of the SubtractionVolumes const double clipAngle = (crossType == ODH::kUpstreamClippedFront || crossType == ODH::kDnstreamClippedFront) ? (rotateAngle) : (2 * rotateAngle); const double clipShift = (zStart - clipSize) / cos(clipAngle) - (zPosition - clipSize / 2); // question: why is this correct? Transform3D clipTransformer(RotationY(-clipAngle), Position(0, 0, clipShift)); Transform3D clipTransmirror(RotationY(+clipAngle), Position(0, 0, clipShift)); // absolute transformations for the final placement in the world Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition - clipSize / 2) , rotateAngle) ); Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition - clipSize / 2) , mirrorAngle) ); // solid for the tube (including vacuum and wall): a solid cone ConeSegment wholeSolid( zHalf + clipSize / 2, 0, rOuterStart, 0, rOuterEnd, phi1, phi2); // a bit longer // clip away the protruding end SubtractionSolid tubeSolid0( wholeSolid, clipSolid, clipTransformer); SubtractionSolid tubeSolid1( wholeSolid, clipSolid, clipTransmirror); // tube consists of vacuum (will later have two different daughters) Volume tubeLog0( volName + "_0", tubeSolid0, coreMaterial ); Volume tubeLog1( volName + "_1", tubeSolid1, coreMaterial ); // placement of the tube in the world, both at +z and -z envelope.placeVolume( tubeLog0, placementTransformer ); envelope.placeVolume( tubeLog1, placementTransmirror ); if (rInnerStart != rOuterStart || rInnerEnd != rOuterEnd) { // the wall solid: a tubular cone ConeSegment wallWholeSolid( zHalf + clipSize / 2, rInnerStart, rOuterStart, rInnerEnd, rOuterEnd, phi1, phi2); // a bit longer // clip away the protruding end SubtractionSolid wallSolid0( wallWholeSolid, clipSolid, clipTransformer); SubtractionSolid wallSolid1( wallWholeSolid, clipSolid, clipTransmirror); // the wall consists of the material given in the database Volume wallLog0( volName + "_wall_0", wallSolid0, wallMaterial ); Volume wallLog1( volName + "_wall_1", wallSolid1, wallMaterial ); wallLog0.setVisAttributes(theDetector, "TubeVis"); wallLog1.setVisAttributes(theDetector, "TubeVis"); tubeLog0.setVisAttributes(theDetector, "VacVis"); tubeLog1.setVisAttributes(theDetector, "VacVis"); // placement as a daughter volumes of the tube tubeLog0.placeVolume( wallLog0, Position() ); tubeLog1.placeVolume( wallLog1, Position() ); } } break; case ODH::kUpstreamClippedRear: case ODH::kDnstreamClippedRear: case ODH::kUpstreamSlicedRear: case ODH::kDnstreamSlicedRear: { // a volume on the upstream or donwstream branch, but with the rear face parallel to the xy-plane // or to a piece tilted in the other direction ("sliced" like a salami with 2 * rotateAngle) // (implemented as a slightly longer cone from which the end is clipped off) // the volume which will be used for clipping: a solid tube const double clipSize = rOuterEnd; // the right order of magnitude for the clipping volume (alias name) Tube clipSolid( 0, 2 * clipSize, clipSize, phi1, phi2); // should be large enough // relative transformations for the composition of the SubtractionVolumes const double clipAngle = (crossType == ODH::kUpstreamClippedRear || crossType == ODH::kDnstreamClippedRear) ? (rotateAngle) : (2 * rotateAngle); const double clipShift = (zEnd + clipSize) / cos(clipAngle) - (zPosition + clipSize / 2); // question: why is this correct? Transform3D clipTransformer(RotationY(-clipAngle), Position(0, 0, clipShift)); Transform3D clipTransmirror(RotationY(+clipAngle), Position(0, 0, clipShift)); // absolute transformations for the final placement in the world Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition + clipSize / 2) , rotateAngle) ); Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition + clipSize / 2) , mirrorAngle) ); // solid for the tube (including vacuum and wall): a solid cone ConeSegment wholeSolid( 0, rOuterStart, 0, rOuterEnd, zHalf + clipSize / 2, phi1, phi2); // a bit longer // clip away the protruding end SubtractionSolid tubeSolid0( wholeSolid, clipSolid, clipTransformer); SubtractionSolid tubeSolid1( wholeSolid, clipSolid, clipTransmirror); // tube consists of vacuum (will later have two different daughters) Volume tubeLog0( volName + "_0", tubeSolid0, coreMaterial ); Volume tubeLog1( volName + "_1", tubeSolid1, coreMaterial ); // placement of the tube in the world, both at +z and -z envelope.placeVolume( tubeLog0, placementTransformer ); envelope.placeVolume( tubeLog1, placementTransmirror ); if (rInnerStart != rOuterStart || rInnerEnd != rOuterEnd) { // the wall solid: a tubular cone ConeSegment wallWholeSolid( rInnerStart, rOuterStart, rInnerEnd, rOuterEnd, zHalf + clipSize / 2, phi1, phi2); // a bit longer // clip away the protruding end SubtractionSolid wallSolid0( wallWholeSolid, clipSolid, clipTransformer); SubtractionSolid wallSolid1( wallWholeSolid, clipSolid, clipTransmirror); // the wall consists of the material given in the database Volume wallLog0( volName + "_wall_0", wallSolid0, wallMaterial ); Volume wallLog1( volName + "_wall_1", wallSolid1, wallMaterial ); wallLog0.setVisAttributes(theDetector, "TubeVis"); wallLog1.setVisAttributes(theDetector, "TubeVis"); tubeLog0.setVisAttributes(theDetector, "VacVis"); tubeLog1.setVisAttributes(theDetector, "VacVis"); // placement as a daughter volumes of the tube tubeLog0.placeVolume( wallLog0, Transform3D() ); tubeLog1.placeVolume( wallLog1, Transform3D() ); } break; } case ODH::kUpstreamClippedBoth: case ODH::kDnstreamClippedBoth: case ODH::kUpstreamSlicedBoth: case ODH::kDnstreamSlicedBoth: { // a volume on the upstream or donwstream branch, but with both faces parallel to the xy-plane // or to a piece tilted in the other direction ("sliced" like a salami with 2 * rotateAngle) // (implemented as a slightly longer cone from which the end is clipped off) // the volume which will be used for clipping: a solid tube const double clipSize = rOuterEnd; // the right order of magnitude for the clipping volume (alias name) Tube clipSolid( 0, 2 * clipSize, clipSize, phi1, phi2); // should be large enough // relative transformations for the composition of the SubtractionVolumes const double clipAngle = (crossType == ODH::kUpstreamClippedBoth || crossType == ODH::kDnstreamClippedBoth) ? (rotateAngle) : (2 * rotateAngle); const double clipShiftFrnt = (zStart - clipSize) / cos(clipAngle) - zPosition; const double clipShiftRear = (zEnd + clipSize) / cos(clipAngle) - zPosition; Transform3D clipTransformerFrnt(RotationY(-clipAngle), Position(0, 0, clipShiftFrnt)); Transform3D clipTransformerRear(RotationY(-clipAngle), Position(0, 0, clipShiftRear)); Transform3D clipTransmirrorFrnt(RotationY(+clipAngle), Position(0, 0, clipShiftFrnt)); Transform3D clipTransmirrorRear(RotationY(+clipAngle), Position(0, 0, clipShiftRear)); // absolute transformations for the final placement in the world Transform3D placementTransformer(RotationY(rotateAngle), RotateY( Position(0, 0, zPosition) , rotateAngle) ); Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY( Position(0, 0, zPosition) , mirrorAngle) ); // solid for the tube (including vacuum and wall): a solid cone ConeSegment wholeSolid( 0, rOuterStart, 0, rOuterEnd, zHalf + clipSize, phi1, phi2); // a bit longer // clip away the protruding ends SubtractionSolid tmpSolid0 ( wholeSolid, clipSolid, clipTransformerFrnt); SubtractionSolid tmpSolid1 ( wholeSolid, clipSolid, clipTransmirrorFrnt); SubtractionSolid tubeSolid0( tmpSolid0, clipSolid, clipTransformerRear); SubtractionSolid tubeSolid1( tmpSolid1, clipSolid, clipTransmirrorRear); // tube consists of vacuum (will later have two different daughters) Volume tubeLog0( volName + "_0", tubeSolid0, coreMaterial ); Volume tubeLog1( volName + "_1", tubeSolid1, coreMaterial ); // placement of the tube in the world, both at +z and -z envelope.placeVolume( tubeLog0, placementTransformer ); envelope.placeVolume( tubeLog1, placementTransmirror ); if (rInnerStart != rOuterStart || rInnerEnd != rOuterEnd) { // the wall solid: a tubular cone ConeSegment wallWholeSolid( rInnerStart, rOuterStart, rInnerEnd, rOuterEnd, zHalf + clipSize, phi1, phi2); // a bit longer // clip away the protruding ends SubtractionSolid wallTmpSolid0( wallWholeSolid, clipSolid, clipTransformerFrnt); SubtractionSolid wallTmpSolid1( wallWholeSolid, clipSolid, clipTransmirrorFrnt); SubtractionSolid wallSolid0 ( wallTmpSolid0, clipSolid, clipTransformerRear); SubtractionSolid wallSolid1 ( wallTmpSolid1, clipSolid, clipTransmirrorRear); // the wall consists of the material given in the database Volume wallLog0(volName + "_wall_0", wallSolid0, wallMaterial ); Volume wallLog1(volName + "_wall_1", wallSolid1, wallMaterial ); wallLog0.setVisAttributes(theDetector, "TubeVis"); wallLog1.setVisAttributes(theDetector, "TubeVis"); tubeLog0.setVisAttributes(theDetector, "VacVis"); tubeLog1.setVisAttributes(theDetector, "VacVis"); // placement as a daughter volumes of the tube tubeLog0.placeVolume( wallLog0, Transform3D() ); tubeLog1.placeVolume( wallLog1, Transform3D() ); } break; } default: { throw std::runtime_error( " Beampipe_o1_v01_geo.cpp : fatal failure !! ?? " ) ; // return false; // fatal failure } }//end switch }//for all xmlSections //###################################################################################################################################################################### // add a surface just inside the beampipe for tracking: Vector3D oIPCyl( (min_radius-1.e-3) , 0. , 0. ) ; SimpleCylinder ipCylSurf( envelope , SurfaceType( SurfaceType::Helper ) , 1.e-5 , 1e-5 , oIPCyl ) ; // the length does not really matter here as long as it is long enough for all tracks ... ipCylSurf->setHalfLength( 100*dd4hep::cm ) ; volSurfaceList( tube )->push_back( ipCylSurf ) ; tube.addExtension< ConicalSupportData >( beampipeData ) ; //-------------------------------------- tube.setVisAttributes( theDetector, xmlBeampipe.visStr(), envelope ); // // tube.setPlacement(pv); return tube; }
static Ref_t create_detector(Detector& theDetector, xml_h element, SensitiveDetector sens) { std::cout << __PRETTY_FUNCTION__ << std::endl; std::cout << "Here is my LumiCal" << std::endl; std::cout << " and this is the sensitive detector: " << &sens << std::endl; sens.setType("calorimeter"); //Materials Material air = theDetector.air(); //Access to the XML File xml_det_t xmlLumiCal = element; const std::string detName = xmlLumiCal.nameStr(); DetElement sdet ( detName, xmlLumiCal.id() ); // --- create an envelope volume and position it into the world --------------------- Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector, element , sdet ) ; DetElement lumiCalDE_1(sdet,"Calorimeter1",1); DetElement lumiCalDE_2(sdet,"Calorimeter2",2); sdet.setTypeFlag( DetType::CALORIMETER | DetType::ENDCAP | DetType::ELECTROMAGNETIC | DetType::FORWARD ) ; if( theDetector.buildType() == BUILD_ENVELOPE ) return sdet ; //----------------------------------------------------------------------------------- dd4hep::xml::Dimension dimensions = xmlLumiCal.dimensions(); //LumiCal Dimensions const double lcalInnerR = dimensions.inner_r(); const double lcalOuterR = dimensions.outer_r(); const double lcalInnerZ = dimensions.inner_z(); const double lcalThickness = Layering(xmlLumiCal).totalThickness(); const double lcalCentreZ = lcalInnerZ+lcalThickness*0.5; double LumiCal_cell_size = theDetector.constant<double>("LumiCal_cell_size"); //========== fill data for reconstruction ============================ LayeredCalorimeterData* caloData = new LayeredCalorimeterData ; caloData->layoutType = LayeredCalorimeterData::EndcapLayout ; caloData->inner_symmetry = 0 ; // hardcoded tube caloData->outer_symmetry = 0 ; caloData->phi0 = 0 ; /// extent of the calorimeter in the r-z-plane [ rmin, rmax, zmin, zmax ] in mm. caloData->extent[0] = lcalInnerR ; caloData->extent[1] = lcalOuterR ; caloData->extent[2] = lcalInnerZ ; caloData->extent[3] = lcalInnerZ + lcalThickness ; // counter for the current layer to be placed int thisLayerId = 0; //Parameters we have to know about dd4hep::xml::Component xmlParameter = xmlLumiCal.child(_Unicode(parameter)); const double fullCrossingAngle = xmlParameter.attr< double >(_Unicode(crossingangle)); std::cout << " The crossing angle is: " << fullCrossingAngle << " radian" << std::endl; //Envelope to place the layers in Tube envelopeTube (lcalInnerR, lcalOuterR, lcalThickness*0.5 ); Volume envelopeVol(detName+"_module",envelopeTube,air); envelopeVol.setVisAttributes(theDetector,xmlLumiCal.visStr()); //////////////////////////////////////////////////////////////////////////////// // Create all the layers //////////////////////////////////////////////////////////////////////////////// //Loop over all the layer (repeat=NN) sections //This is the starting point to place all layers, we need this when we have more than one layer block double referencePosition = -lcalThickness*0.5; for(dd4hep::xml::Collection_t coll(xmlLumiCal,_U(layer)); coll; ++coll) { dd4hep::xml::Component xmlLayer(coll); //we know this thing is a layer //This just calculates the total size of a single layer //Why no convenience function for this? double layerThickness = 0; for(dd4hep::xml::Collection_t l(xmlLayer,_U(slice)); l; ++l) layerThickness += xml_comp_t(l).thickness(); std::cout << "Total Length " << lcalThickness/dd4hep::cm << " cm" << std::endl; std::cout << "Layer Thickness " << layerThickness/dd4hep::cm << " cm" << std::endl; //Loop for repeat=NN for(int i=0, repeat=xmlLayer.repeat(); i<repeat; ++i) { std::string layer_name = detName + dd4hep::xml::_toString(thisLayerId,"_layer%d"); Tube layer_base(lcalInnerR,lcalOuterR,layerThickness*0.5); Volume layer_vol(layer_name,layer_base,air); int sliceID=0; double inThisLayerPosition = -layerThickness*0.5; double nRadiationLengths=0.; double nInteractionLengths=0.; double thickness_sum=0; LayeredCalorimeterData::Layer caloLayer ; for(dd4hep::xml::Collection_t collSlice(xmlLayer,_U(slice)); collSlice; ++collSlice) { dd4hep::xml::Component compSlice = collSlice; const double slice_thickness = compSlice.thickness(); const std::string sliceName = layer_name + dd4hep::xml::_toString(sliceID,"slice%d"); Material slice_material = theDetector.material(compSlice.materialStr()); Tube sliceBase(lcalInnerR,lcalOuterR,slice_thickness/2); Volume slice_vol (sliceName,sliceBase,slice_material); nRadiationLengths += slice_thickness/(2.*slice_material.radLength()); nInteractionLengths += slice_thickness/(2.*slice_material.intLength()); thickness_sum += slice_thickness/2; if ( compSlice.isSensitive() ) { #if DD4HEP_VERSION_GE( 0, 15 ) //Store "inner" quantities caloLayer.inner_nRadiationLengths = nRadiationLengths; caloLayer.inner_nInteractionLengths = nInteractionLengths; caloLayer.inner_thickness = thickness_sum; //Store scintillator thickness caloLayer.sensitive_thickness = slice_thickness; #endif //Reset counters to measure "outside" quantitites nRadiationLengths=0.; nInteractionLengths=0.; thickness_sum = 0.; slice_vol.setSensitiveDetector(sens); } nRadiationLengths += slice_thickness/(2.*slice_material.radLength()); nInteractionLengths += slice_thickness/(2.*slice_material.intLength()); thickness_sum += slice_thickness/2; slice_vol.setAttributes(theDetector,compSlice.regionStr(),compSlice.limitsStr(),compSlice.visStr()); layer_vol.placeVolume(slice_vol,Position(0,0,inThisLayerPosition+slice_thickness*0.5)); inThisLayerPosition += slice_thickness; ++sliceID; }//For all slices in this layer //----------------------------------------------------------------------------------------- ///Needs to be innermost face distance caloLayer.distance = lcalCentreZ + referencePosition; #if DD4HEP_VERSION_GE( 0, 15 ) caloLayer.outer_nRadiationLengths = nRadiationLengths; caloLayer.outer_nInteractionLengths = nInteractionLengths; caloLayer.outer_thickness = thickness_sum; #endif caloLayer.cellSize0 = LumiCal_cell_size ; caloLayer.cellSize1 = LumiCal_cell_size ; caloData->layers.push_back( caloLayer ) ; //----------------------------------------------------------------------------------------- //Why are we doing this for each layer, this just needs to be done once and then placed multiple times //Do we need unique IDs for each piece? layer_vol.setVisAttributes(theDetector,xmlLayer.visStr()); Position layer_pos(0,0,referencePosition+0.5*layerThickness); referencePosition += layerThickness; PlacedVolume pv = envelopeVol.placeVolume(layer_vol,layer_pos); pv.addPhysVolID("layer",thisLayerId); ++thisLayerId; }//for all layers }// for all layer collections const Position bcForwardPos (std::tan(0.5*fullCrossingAngle)*lcalCentreZ,0.0, lcalCentreZ); const Position bcBackwardPos(std::tan(0.5*fullCrossingAngle)*lcalCentreZ,0.0,-lcalCentreZ); const Rotation3D bcForwardRot ( RotationY(fullCrossingAngle*0.5 ) ); const Rotation3D bcBackwardRot( RotationZYX ( (M_PI), (M_PI-fullCrossingAngle*0.5), (0.0))); PlacedVolume pv = envelope.placeVolume(envelopeVol, Transform3D( bcForwardRot, bcForwardPos ) ); pv.addPhysVolID("barrel", 1); lumiCalDE_1.setPlacement(pv); PlacedVolume pv2 = envelope.placeVolume(envelopeVol, Transform3D( bcBackwardRot, bcBackwardPos ) ); pv2.addPhysVolID("barrel", 2); lumiCalDE_2.setPlacement(pv2); sdet.addExtension< LayeredCalorimeterData >( caloData ) ; return sdet; }
static Ref_t create_element(Detector& theDetector, xml_h xmlHandle, SensitiveDetector /*sens*/) { //------------------------------------------ // See comments starting with '//**' for // hints on porting issues //------------------------------------------ std::cout << "This is the Mask:" << std::endl; // Access to the XML File xml_det_t xmlMask = xmlHandle; const std::string name = xmlMask.nameStr(); //-------------------------------- Assembly envelope(name + "_assembly"); //-------------------------------- DetElement tube(name, xmlMask.id()); // const double phi1 = 0 ; // const double phi2 = 360.0*dd4hep::degree; // Parameters we have to know about dd4hep::xml::Component xmlParameter = xmlMask.child(_Unicode(parameter)); const double crossingAngle = xmlParameter.attr<double>(_Unicode(crossingangle)) * 0.5; // only half the angle for (xml_coll_t c(xmlMask, Unicode("section")); c; ++c) { xml_comp_t xmlSection(c); ODH::ECrossType crossType = ODH::getCrossType(xmlSection.attr<std::string>(_Unicode(type))); const double zStart = xmlSection.attr<double>(_Unicode(start)); const double zEnd = xmlSection.attr<double>(_Unicode(end)); const double rInnerStart = xmlSection.attr<double>(_Unicode(rMin1)); const double rInnerEnd = xmlSection.attr<double>(_Unicode(rMin2)); const double rOuterStart = xmlSection.attr<double>(_Unicode(rMax1)); const double rOuterEnd = xmlSection.attr<double>(_Unicode(rMax2)); const double phi1 = xmlSection.attr<double>(_Unicode(Phi1)); const double phi2 = xmlSection.attr<double>(_Unicode(Phi2)); const double thickness = rOuterStart - rInnerStart; Material sectionMat = theDetector.material(xmlSection.materialStr()); const std::string volName = "tube_" + xmlSection.nameStr(); std::cout << std::setw(8) << zStart << std::setw(8) << zEnd << std::setw(8) << rInnerStart << std::setw(8) << rInnerEnd << std::setw(8) << rOuterStart << std::setw(8) << rOuterEnd << std::setw(8) << thickness << std::setw(8) << crossType << std::setw(8) << phi1 << std::setw(8) << phi2 << std::setw(15) << volName << std::setw(15) << sectionMat.name() << std::endl; // things which can be calculated immediately const double zHalf = fabs(zEnd - zStart) * 0.5; // half z length of the cone const double zPosition = fabs(zEnd + zStart) * 0.5; // middle z position Material material = sectionMat; // this could mess up your geometry, so better check it if (not ODH::checkForSensibleGeometry(crossingAngle, crossType)) { throw std::runtime_error(" Mask_o1_v01_noRot_geo.cpp : checkForSensibleGeometry() failed "); } const double rotateAngle = getCurrentAngle(crossingAngle, crossType); // for the placement at +z (better make it const now) const double mirrorAngle = M_PI - rotateAngle; // for the "mirrored" placement at -z // the "mirroring" in fact is done by a rotation of (almost) 180 degrees around the y-axis switch (crossType) { case ODH::kCenter: case ODH::kUpstream: case ODH::kDnstream: { // a volume on the z-axis, on the upstream branch, or on the downstream branch // absolute transformations for the placement in the world, rotate over X Transform3D transformer(RotationX(rotateAngle), RotateX(Position(0, 0, zPosition), rotateAngle)); Transform3D transmirror(RotationX(mirrorAngle), RotateX(Position(0, 0, zPosition), mirrorAngle)); // solid for the tube (including vacuum and wall): a solid cone ConeSegment tubeSolid(zHalf, rInnerStart, rOuterStart, rInnerEnd, rOuterEnd, phi1, phi2); // tube consists of vacuum Volume tubeLog(volName, tubeSolid, material); tubeLog.setVisAttributes(theDetector, xmlMask.visStr()); // placement of the tube in the world, both at +z and -z envelope.placeVolume(tubeLog, transformer); envelope.placeVolume(tubeLog, transmirror); } break; case ODH::kPunchedCenter: { // a cone with one or two inner holes (two tubes are punched out) const double rUpstreamPunch = rInnerStart; // just alias names denoting what is meant here const double rDnstreamPunch = rInnerEnd; // (the database entries are "abused" in this case) // relative transformations for the composition of the SubtractionVolumes Transform3D upstreamTransformer(RotationY(-crossingAngle), Position(zPosition * tan(-crossingAngle), 0, 0)); Transform3D dnstreamTransformer(RotationY(+crossingAngle), Position(zPosition * tan(+crossingAngle), 0, 0)); // absolute transformations for the final placement in the world (angles always equal zero and 180 deg) Transform3D placementTransformer(RotationY(rotateAngle), RotateY(Position(0, 0, zPosition), rotateAngle)); Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY(Position(0, 0, zPosition), mirrorAngle)); // the main solid and the two pieces (only tubes, for the moment) which will be punched out ConeSegment wholeSolid(zHalf, 0, rOuterStart, 0, rOuterEnd, phi1, phi2); Solid tmpSolid0, tmpSolid1, finalSolid0, finalSolid1; // the punched subtraction solids can be asymmetric and therefore have to be created twice: // one time in the "right" way, another time in the "reverse" way, because the "mirroring" // rotation around the y-axis will not only exchange +z and -z, but also +x and -x if (rUpstreamPunch > 1e-6) { // do we need a hole on the upstream branch? Tube upstreamPunch(0, rUpstreamPunch, 5 * zHalf, phi1, phi2); // a bit longer tmpSolid0 = SubtractionSolid(wholeSolid, upstreamPunch, upstreamTransformer); tmpSolid1 = SubtractionSolid(wholeSolid, upstreamPunch, dnstreamTransformer); // [sic] } else { // dont't do anything, just pass on the unmodified shape tmpSolid0 = wholeSolid; tmpSolid1 = wholeSolid; } if (rDnstreamPunch > 1e-6) { // do we need a hole on the downstream branch? Tube dnstreamPunch(0, rDnstreamPunch, 5 * zHalf, phi1, phi2); // a bit longer finalSolid0 = SubtractionSolid(tmpSolid0, dnstreamPunch, dnstreamTransformer); finalSolid1 = SubtractionSolid(tmpSolid1, dnstreamPunch, upstreamTransformer); // [sic] } else { // dont't do anything, just pass on the unmodified shape finalSolid0 = tmpSolid0; finalSolid1 = tmpSolid1; } // tube consists of vacuum (will later have two different daughters) Volume tubeLog0(volName + "_0", finalSolid0, material); Volume tubeLog1(volName + "_1", finalSolid1, material); tubeLog0.setVisAttributes(theDetector, xmlMask.visStr()); tubeLog1.setVisAttributes(theDetector, xmlMask.visStr()); // placement of the tube in the world, both at +z and -z envelope.placeVolume(tubeLog0, placementTransformer); envelope.placeVolume(tubeLog1, placementTransmirror); break; } case ODH::kPunchedUpstream: case ODH::kPunchedDnstream: { // a volume on the upstream or downstream branch with two inner holes // (implemented as a cone from which another tube is punched out) const double rCenterPunch = (crossType == ODH::kPunchedUpstream) ? (rInnerStart) : (rInnerEnd); // just alias names denoting what is meant here const double rOffsetPunch = (crossType == ODH::kPunchedDnstream) ? (rInnerStart) : (rInnerEnd); // (the database entries are "abused" in this case) // relative transformations for the composition of the SubtractionVolumes Transform3D punchTransformer(RotationY(-2 * rotateAngle), Position(zPosition * tan(-2 * rotateAngle), 0, 0)); Transform3D punchTransmirror(RotationY(+2 * rotateAngle), Position(zPosition * tan(+2 * rotateAngle), 0, 0)); // absolute transformations for the final placement in the world Transform3D placementTransformer(RotationY(rotateAngle), RotateY(Position(0, 0, zPosition), rotateAngle)); Transform3D placementTransmirror(RotationY(mirrorAngle), RotateY(Position(0, 0, zPosition), mirrorAngle)); // the main solid and the piece (only a tube, for the moment) which will be punched out ConeSegment wholeSolid(zHalf, rCenterPunch, rOuterStart, rCenterPunch, rOuterEnd, phi1, phi2); Tube punchSolid(0, rOffsetPunch, 5 * zHalf, phi1, phi2); // a bit longer // the punched subtraction solids can be asymmetric and therefore have to be created twice: // one time in the "right" way, another time in the "reverse" way, because the "mirroring" // rotation around the y-axis will not only exchange +z and -z, but also +x and -x SubtractionSolid finalSolid0(wholeSolid, punchSolid, punchTransformer); SubtractionSolid finalSolid1(wholeSolid, punchSolid, punchTransmirror); // tube consists of vacuum (will later have two different daughters) Volume tubeLog0(volName + "_0", finalSolid0, material); Volume tubeLog1(volName + "_1", finalSolid1, material); tubeLog0.setVisAttributes(theDetector, xmlMask.visStr()); tubeLog1.setVisAttributes(theDetector, xmlMask.visStr()); // placement of the tube in the world, both at +z and -z envelope.placeVolume(tubeLog0, placementTransformer); envelope.placeVolume(tubeLog1, placementTransmirror); break; } default: { throw std::runtime_error(" Mask_o1_v01_geo.cpp : fatal failure !! ?? "); } } // end switch } // for all xmlSections //-------------------------------------- Volume mother = theDetector.pickMotherVolume(tube); PlacedVolume pv(mother.placeVolume(envelope)); pv.addPhysVolID("system", xmlMask.id()); //.addPhysVolID("side", 0 ) ; tube.setVisAttributes(theDetector, xmlMask.visStr(), envelope); tube.setPlacement(pv); return tube; }