diff --git a/FCCee/ALLEGRO/compact/ALLEGRO_o1_v03/ECalEndcaps_Turbine.xml b/FCCee/ALLEGRO/compact/ALLEGRO_o1_v03/ECalEndcaps_Turbine.xml index 547053962..ac5b7f2ef 100644 --- a/FCCee/ALLEGRO/compact/ALLEGRO_o1_v03/ECalEndcaps_Turbine.xml +++ b/FCCee/ALLEGRO/compact/ALLEGRO_o1_v03/ECalEndcaps_Turbine.xml @@ -43,6 +43,11 @@ + + + + + @@ -51,6 +56,7 @@ + @@ -108,7 +114,7 @@ - + diff --git a/FCCee/ALLEGRO/compact/ALLEGRO_o1_v03/ECalEndcaps_Turbine_calibration.xml b/FCCee/ALLEGRO/compact/ALLEGRO_o1_v03/ECalEndcaps_Turbine_calibration.xml index 3d07d3671..1df7dd3af 100644 --- a/FCCee/ALLEGRO/compact/ALLEGRO_o1_v03/ECalEndcaps_Turbine_calibration.xml +++ b/FCCee/ALLEGRO/compact/ALLEGRO_o1_v03/ECalEndcaps_Turbine_calibration.xml @@ -35,6 +35,11 @@ + + + + + @@ -43,6 +48,7 @@ + @@ -100,7 +106,7 @@ - + diff --git a/detector/calorimeter/ECalEndcap_Turbine_o1_v01_geo.cpp b/detector/calorimeter/ECalEndcap_Turbine_o1_v01_geo.cpp index 51d79a656..4a4c43822 100644 --- a/detector/calorimeter/ECalEndcap_Turbine_o1_v01_geo.cpp +++ b/detector/calorimeter/ECalEndcap_Turbine_o1_v01_geo.cpp @@ -1,4 +1,5 @@ #include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/Printout.h" #include "TMatrixT.h" // todo: remove gaudi logging and properly capture output @@ -83,18 +84,18 @@ namespace det { float BladeAngle = genericBladeElem.attr(_Unicode(angle)); bool decreaseAnglePerWheel = genericBladeElem.attr(_Unicode(decreaseAnglePerWheel)); - lLog << MSG::DEBUG << "Making wheel with inner, outer radii " << ri << ", " << ro << std:: endl; - lLog << MSG::DEBUG << "Blade angle is " << BladeAngle << "; decrease angle per wheel? " << decreaseAnglePerWheel << endmsg; + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "Making wheel with inner, outer radii %f, %f", ri, ro); + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "Blade angle is %f; decrease angle per wheel? ", BladeAngle, decreaseAnglePerWheel); dd4hep::xml::Dimension dim(aXmlElement.child(_Unicode(dimensions))); double grmin = dim.rmin1(); - lLog << MSG::DEBUG << "delZ is " << delZ << endmsg; + dd4hep::printout( dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "delZ is %f", delZ); if (decreaseAnglePerWheel) { float tubeFracCovered = delZ/(2*grmin*TMath::Tan(BladeAngle)); BladeAngle = TMath::ATan(delZ/(2*ri*tubeFracCovered)); } if (TMath::Abs(TMath::Tan(BladeAngle)) < delZ/(2.*ri)) { - lLog << MSG::ERROR << "The requested blade angle is too small for the given delZ and ri values. Please adjust to at least " << TMath::ATan(delZ/(2.*ri))*180./TMath::Pi() << " degrees!" << endmsg; + dd4hep::printout(dd4hep::ERROR, "ECalEndcap_Turbine_o1_v01", "The requested blade angle is too small for the given delZ and ri values. Please adjust to at least %f degrees!", TMath::ATan(delZ/(2.*ri))*180./TMath::Pi() ); return; } @@ -106,7 +107,7 @@ namespace det { float CladdingThick = claddingElem.attr(_Unicode(thickness)); float AbsThickMin = absBladeElem.attr(_Unicode(thickness))-(GlueThick+CladdingThick); if (AbsThickMin < 0.) { - lLog << MSG::ERROR << "Error: requested absorber thickness is negative after accounting for glue and cladding thickness" << endmsg; + dd4hep::printout(dd4hep::ERROR, "ECalEndcap_Turbine_o1_v01", "Error: requested absorber thickness is negative after accounting for glue and cladding thickness"); } float ElectrodeThick = electrodeBladeElem.attr(_Unicode(thickness)); float LArgapi = nobleLiquidElem.attr(_Unicode(gap)); @@ -127,7 +128,7 @@ namespace det { bool scaleBladeThickness = absBladeElem.attr(_Unicode(scaleThickness)); float bladeThicknessScaleFactor = absBladeElem.attr(_Unicode(thicknessScaleFactor)); - lLog << MSG::DEBUG << "nUnitCells: " << nUnitCells << endmsg; + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "nUnitCells: %d", nUnitCells); float AbsThicki = AbsThickMin; // make volumes for the noble liquid, electrode, and absorber blades @@ -148,7 +149,7 @@ namespace det { double leftoverS = (circ - nUnitCells*delrPhiNoGap); double delrPhiGapOnly = leftoverS/(2*nUnitCells); LArgapi = delrPhiGapOnly*TMath::Sin(BladeAngle); - lLog << MSG::DEBUG << "LArGap at inner radius is " << LArgapi << endmsg; + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "LArGap at inner radius is %f", LArgapi); // now find gap at outer radius circ = 2*TMath::Pi()*ro; @@ -193,7 +194,7 @@ namespace det { } else if (allNonActiveNotSensitive) { numNonActiveLayers = 1; } else { - lLog << MSG::ERROR << "Some non-active layers are sensitive and others are not -- this is likely a misconfiguration"; + dd4hep::printout(dd4hep::ERROR, "ECalEndcap_Turbine_o1_v01", "Some non-active layers are sensitive and others are not -- this is likely a misconfiguration"); } float delrNonActive = (ro-ri)/numNonActiveLayers; @@ -201,14 +202,14 @@ namespace det { for (unsigned iLayer = 0; iLayer < numNonActiveLayers; iLayer++) { float roLayer = riLayer + delrNonActive; - lLog << MSG::INFO << "Making layer in inner, outer radii " << riLayer << " " << roLayer << endmsg; + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "Making layer with inner, outer radii %f, %f", riLayer, roLayer); if (scaleBladeThickness) { AbsThicko = AbsThicki + bladeThicknessScaleFactor*((roLayer/riLayer)-1.)*AbsThicki; } else { AbsThicko = AbsThicki; } - lLog << MSG::DEBUG << "Inner and outer absorber thicknesses " << AbsThicki << " " << AbsThicko << endmsg; + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "Inner and outer absorber thicknesses %f, %f ", AbsThicki, AbsThicko); dd4hep::Solid claddingLayer = buildOneBlade(AbsThicki+GlueThick+CladdingThick, AbsThicko+GlueThick+CladdingThick, xRange, roLayer, riLayer, BladeAngle, delZ ); dd4hep::Solid glueLayer = buildOneBlade(AbsThicki+GlueThick, AbsThicko+GlueThick, xRange, roLayer, riLayer, BladeAngle, delZ ); @@ -264,8 +265,8 @@ namespace det { leftoverS = (circ - nUnitCells*delrPhiNoGap); delrPhiGapOnly = leftoverS/(2*nUnitCells); LArgapo = delrPhiGapOnly*TMath::Sin(BladeAngle); - lLog << MSG::DEBUG << "Outer LAr gap is " << LArgapo << endmsg ; - lLog << MSG::INFO << "Inner and outer thicknesses of noble liquid volume " << ElectrodeThick+LArgapi*2 << " " << ElectrodeThick+LArgapo*2 << endmsg; + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "Outer LAr gap is %f", LArgapo) ; + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "Inner and outer thicknesses of noble liquid volume %f, %f", ElectrodeThick+LArgapi*2, ElectrodeThick+LArgapo*2); dd4hep::Solid electrodeBladeAndGapLayer = buildOneBlade(ElectrodeThick+LArgapi*2, ElectrodeThick+LArgapo*2, xRange, roLayer, riLayer, BladeAngle, delZ); @@ -289,12 +290,12 @@ namespace det { LArgapi = LArgapo; AbsThicki = AbsThicko; } - lLog << MSG::INFO << "ECal endcap materials: nobleLiquid: " << nobleLiquidElem.materialStr() << " absorber: " << absBladeElem.materialStr() << " electrode: " << electrodeBladeElem.materialStr() << endmsg; + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "ECal endcap materials: nobleLiquid: %s absorber %s electrode %s", nobleLiquidElem.materialStr().c_str(), absBladeElem.materialStr().c_str(), electrodeBladeElem.materialStr().c_str() ); int nUnitCellsToDraw = nUnitCells; // nUnitCellsToDraw = 2; - lLog << MSG::INFO << "Number of unit cells "<< nUnitCells << endmsg; + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "Number of unit cells %d", nUnitCells); // place all components of the absorber blade inside passive volume @@ -310,7 +311,7 @@ namespace det { dd4hep::PlacedVolume absBladeVol_pv = glueLayerVols[iLayer].placeVolume(absBladeLayerVol, posLayer); absBladeVol_pv.addPhysVolID("subtype", 0); // 0 = absorber, 1 = glue, 2 = cladding - lLog << MSG::DEBUG << "Blade layer, rho is " << iLayer << " " << absBladeVol_pv.position().Rho() << " " << roLayer/2. << endmsg; + dd4hep::printout( dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01_geo", "Blade layer, rho is %d, %f, %f", iLayer, absBladeVol_pv.position().Rho(), roLayer/2.); absBladeVol_pv.addPhysVolID("layer", iWheel*numNonActiveLayers+iLayer); riLayer = roLayer; @@ -387,10 +388,9 @@ namespace det { double zminLayer = getZmin(riLayer, BladeAngle, delZ); dd4hep::Position posLayer(0,0,(zminLayer-zminri+roLayer-ro)/2.); - std::cout << "for active, riLayer, ri, roLayer, ro = " << riLayer << " " << ri << " " << roLayer << " " << ro << std::endl; dd4hep::PlacedVolume LArVol_pv(activeVol.placeVolume(LArTotalLayerVol, posLayer)); - lLog << MSG::DEBUG << "LAr layer: " << iLayer << endmsg; + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "LAr layer: %d", iLayer ); LArVol_pv.addPhysVolID("layer", iWheel*ECalEndcapNumCalibLayers+iLayer); riLayer = roLayer; @@ -404,7 +404,7 @@ namespace det { float phi = (iUnitCell-nUnitCellsToDraw/2)*2*TMath::Pi()/nUnitCells; float delPhi = 2*TMath::Pi()/nUnitCells; - lLog << MSG::DEBUG << "Placing blade, ro, ri = " << ro << " " << ri << endmsg; + dd4hep::printout( dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "Placing blade, ro, ri = %f %f", ro, ri); TGeoRotation tgr; tgr.RotateZ(BladeAngle*180/TMath::Pi()); tgr.RotateX(-phi*180/TMath::Pi()); @@ -466,7 +466,7 @@ namespace det { - lLog << MSG::DEBUG << "LArTotalLayerVols.size = " << LArTotalLayerVols.size() << endmsg; + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "LArTotalLayerVols.size = %d", LArTotalLayerVols.size()); } @@ -510,17 +510,17 @@ namespace det { dd4hep::Tube bathOuterShape(bathRmin, bathRmax, bathDelZ); // make it 4 volumes + 5th for detector envelope dd4hep::Tube bathAndServicesOuterShape(cryoDim.rmin2(), cryoDim.rmax1(), caloDim.dz()); // make it 4 volumes + 5th for detector envelope - lLog << MSG::INFO << "Cryostat front thickness is " << cryoDim.rmin2() << endmsg; + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "Cryostat front thickness is %f", cryoDim.rmin2() ); if (cryoThicknessFront > 0) { // 1. Create cryostat dd4hep::Tube cryoFrontShape(cryoDim.rmin1(), cryoDim.rmin2(), cryoDim.dz()); dd4hep::Tube cryoBackShape(cryoDim.rmax1(), cryoDim.rmax2(), cryoDim.dz()); dd4hep::Tube cryoSideOuterShape(cryoDim.rmin2(), cryoDim.rmax1(), cryoDim.dz()); dd4hep::SubtractionSolid cryoSideShape(cryoSideOuterShape, bathAndServicesOuterShape); - lLog << MSG::INFO << "ECAL endcap cryostat: front: rmin (cm) = " << cryoDim.rmin1() << " rmax (cm) = " << cryoDim.rmin2() << " dz (cm) = " << cryoDim.dz() << endmsg; - lLog << MSG::INFO << "ECAL encdap cryostat: back: rmin (cm) = " << cryoDim.rmax1() << " rmax (cm) = " << cryoDim.rmax2() << " dz (cm) = " << cryoDim.dz() << endmsg; - lLog << MSG::INFO << "ECAL endcap cryostat: side: rmin (cm) = " << cryoDim.rmin2() << " rmax (cm) = " << cryoDim.rmax1() << " dz (cm) = " << cryoDim.dz() - caloDim.dz() << endmsg; - lLog << MSG::INFO << "Cryostat is made out of " << cryostat.materialStr() << endmsg; + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "ECAL endcap cryostat: front: rmin (cm) = %f rmax (cm) = %f dz (cm) = %f ", cryoDim.rmin1(), cryoDim.rmin2(), cryoDim.dz()); + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "ECAL encdap cryostat: back: rmin (cm) = %f rmax (cm) = %f dz (cm) = %f", cryoDim.rmax1(), cryoDim.rmax2(), cryoDim.dz()); + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "ECAL endcap cryostat: side: rmin (cm) = %f rmax (cm) = %f dz (cm) = %f", cryoDim.rmin2(), cryoDim.rmax1(), cryoDim.dz() - caloDim.dz()); + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "Cryostat is made out of %s", cryostat.materialStr().c_str() ); dd4hep::Volume cryoFrontVol(cryostat.nameStr()+"_front", cryoFrontShape, aLcdd.material(cryostat.materialStr())); dd4hep::Volume cryoBackVol(cryostat.nameStr()+"_back", cryoBackShape, aLcdd.material(cryostat.materialStr())); @@ -533,19 +533,19 @@ namespace det { cryoFrontVol.setSensitiveDetector(aSensDet); cryoFrontPhysVol.addPhysVolID("cryo", 1); cryoFrontPhysVol.addPhysVolID("type", sidetype+1); - lLog << MSG::INFO << "Cryostat front volume set as sensitive" << endmsg; + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "Cryostat front volume set as sensitive"); } if (cryoBackSensitive) { cryoBackVol.setSensitiveDetector(aSensDet); cryoBackPhysVol.addPhysVolID("cryo", 1); cryoBackPhysVol.addPhysVolID("type", sidetype+2); - lLog << MSG::INFO << "Cryostat back volume set as sensitive" << endmsg; + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "Cryostat back volume set as sensitive" ); } if (cryoSideSensitive) { cryoSideVol.setSensitiveDetector(aSensDet); cryoSidePhysVol.addPhysVolID("cryo", 1); cryoSidePhysVol.addPhysVolID("type", sidetype+3); - lLog << MSG::INFO << "Cryostat front volume set as sensitive" << endmsg; + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "Cryostat front volume set as sensitive"); } dd4hep::DetElement cryoFrontDetElem(caloDetElem, "cryo_front", 0); cryoFrontDetElem.setPlacement(cryoFrontPhysVol); @@ -558,9 +558,7 @@ namespace det { // 2. Create noble liquid bath std::string nobleLiquidMaterial = nobleLiquid.materialStr(); dd4hep::Volume bathVol(nobleLiquidMaterial + "_bath", bathOuterShape, aLcdd.material(nobleLiquidMaterial)); - lLog << MSG::INFO << "ECAL endcap bath: material = " << nobleLiquidMaterial << " rmin (cm) = " << bathRmin - << " rmax (cm) = " << bathRmax << " dz (cm) = " << caloDim.dz() << " thickness in front of ECal (cm) = " << caloDim.rmin() - cryoDim.rmin2() - << " thickness behind ECal (cm) = " << cryoDim.rmax1() - caloDim.rmax() << endmsg; + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "ECAL endcap bath: material = %s rmin (cm) = %f rmax (cm) = %f, dz (cm) = %f, thickness in front of ECal (cm) = %f, thickness behind ECal (cm) = %f", nobleLiquidMaterial.c_str(), bathRmin, bathRmax, caloDim.dz(), caloDim.rmin() - cryoDim.rmin2(), cryoDim.rmax1() - caloDim.rmax()); dd4hep::DetElement bathDetElem(caloDetElem, "bath", 1); // 3. Create detector structure @@ -569,7 +567,7 @@ namespace det { dd4hep::xml::DetElement supportTubeElem = calo.child(_Unicode(supportTube)); unsigned nWheels = supportTubeElem.attr(_Unicode(nWheels)); - lLog << MSG::INFO << "Will build " << nWheels << " wheels" << endmsg; + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v01", "Will build %d wheels", nWheels); double rmin = bathRmin; double rmax = bathRmax; float radiusRatio = pow(rmax/rmin, 1./nWheels); @@ -604,7 +602,7 @@ namespace det { dd4hep::PlacedVolume bathPhysVol = aEnvelope.placeVolume(bathVol); bathDetElem.setPlacement(bathPhysVol); - lLog << MSG::DEBUG << "Total number of modules: " << iModule << endmsg; + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "Total number of modules: %d", iModule); return; } @@ -634,11 +632,9 @@ createECalEndcapTurbine(dd4hep::Detector& aLcdd, dd4hep::xml::Handle_t aXmlEleme // dd4hep::DetElement caloPositiveDetElem(caloDetElem, "positive", 0); // dd4hep::DetElement caloNegativeDetElem(caloDetElem, "negative", 0); - lLog << MSG::DEBUG << "Placing dector on the positive side: (cm) " << dim.z_offset() << " with min, max radii " << dim.rmin1() << " " << dim.rmax1() << endmsg; + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v01", "Placing detector on the positive side: (cm) %f with min, max radii %f %f",dim.z_offset(), dim.rmin1(), dim.rmax1() ); unsigned iModule = 0; buildOneSide_Turbine(aLcdd, aSensDet, envelopeVol, aXmlElement, iModule); - // lLog << MSG::DEBUG << "Placing dector on the negative side: (cm) " << -dim.z_offset() << " with min, max radii " << dim.rmin1() << " " << dim.rmax() << endmsg; - // buildOneSide_Turbine(aLcdd, aSensDet, envelopeNegativeVol, aXmlElement, -1, iModule); dd4hep::Assembly endcapsAssembly("ECalEndcaps_turbine"); diff --git a/detector/calorimeter/ECalEndcap_Turbine_o1_v02_geo.cpp b/detector/calorimeter/ECalEndcap_Turbine_o1_v02_geo.cpp new file mode 100644 index 000000000..a8fcafdc5 --- /dev/null +++ b/detector/calorimeter/ECalEndcap_Turbine_o1_v02_geo.cpp @@ -0,0 +1,671 @@ +#include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/Printout.h" +#include "TMatrixT.h" + +// todo: remove gaudi logging and properly capture output +#define endmsg std::endl +#define lLog std::cout +namespace MSG { + const std::string ERROR = " Error: "; + const std::string DEBUG = " Debug: "; + const std::string INFO = " Info: "; +} + +namespace det { + + namespace ECalEndcap_Turbine_o1_v02 { + unsigned ECalEndCapElementCounter = 0; + + unsigned ECalEndcapNumCalibLayers; + + double tForArcLength(double s, double bladeangle, double delZ, double r) { + + // some intermediate constants + double zpos = delZ/2.; + double zp = zpos/TMath::Tan(bladeangle); + double b = zp/(TMath::Sqrt(r*r-zp*zp)); + double c = (TMath::Tan(s/r) +b)/(1.-b*TMath::Tan(s/r)); + double d = c*c*r*r/(1+c*c); + return (TMath::Sqrt(d)-zp)*TMath::Sin(bladeangle); + + // try approximating the arclength as dx. Less accurate, but that + // approximation is used in calculating the LAr gap, so maybe this + // will make it more consistent? + //return s*TMath::Sin(bladeangle); + + } + + // return position of the inner edge of a blade + double getZmin(double r, double bladeangle, double delZ) { + // r: distance from the beamline + // bladeangle: angle of turbine blades wrt xy plane, in radians + // delZ: z extent of the blades + return TMath::Sqrt(r*r - ((delZ/2)/TMath::Tan(bladeangle))*((delZ/2)/TMath::Tan(bladeangle))); + } + + dd4hep::Solid buildOneBlade(double thickness_inner, + double thickness_outer, + double width, + double ro, double ri, + double bladeangle, + double delZ) + { + + dd4hep::Solid shapeBeforeSubtraction; + + // set max and min extent of the blade (along the z axis in the body frame) + double zmax = ro; + double zmin = getZmin(ri, bladeangle, delZ); + + dd4hep::Trd2 tmp1(thickness_inner/2., thickness_outer/2., width/2., width/2., (zmax-zmin)/2. ); + shapeBeforeSubtraction = tmp1; + + dd4hep::Tube allowedTube(ri, ro, delZ); + + return dd4hep::IntersectionSolid (shapeBeforeSubtraction, allowedTube, dd4hep::Transform3D(dd4hep::RotationZYX( 0, TMath::Pi()/2.-bladeangle, TMath::Pi()/2.),dd4hep::Position(0,0, -(zmin+zmax)/2.))); + + } + + void buildWheel(dd4hep::Detector& aLcdd, + dd4hep::SensitiveDetector& aSensDet, + dd4hep::Volume& aEnvelope, + dd4hep::xml::Handle_t& aXmlElement, + dd4hep::DetElement& bathDetElem, + float ri, float ro, float delZ, + unsigned iWheel) { + + + dd4hep::xml::DetElement calorimeterElem = aXmlElement.child(_Unicode(calorimeter)); + dd4hep::xml::DetElement genericBladeElem = calorimeterElem.child(_Unicode(turbineBlade)); + dd4hep::xml::DetElement absBladeElem = genericBladeElem.child(_Unicode(absorberBlade)); + dd4hep::xml::DetElement claddingElem = genericBladeElem.child(_Unicode(cladding)); + dd4hep::xml::DetElement glueElem = genericBladeElem.child(_Unicode(glue)); + dd4hep::xml::DetElement electrodeBladeElem = genericBladeElem.child(_Unicode(electrodeBlade)); + dd4hep::xml::DetElement nobleLiquidElem = genericBladeElem.child(_Unicode(nobleLiquidGap)); + + float BladeAngle = 0.0, AbsThickMin = 0.0, BladeThicknessScaleFactor=0.0; + // hardcode for three wheels + if (iWheel == 0) { + BladeAngle = genericBladeElem.attr(_Unicode(angle1)); + AbsThickMin = absBladeElem.attr(_Unicode(thickness1)); + BladeThicknessScaleFactor = absBladeElem.attr(_Unicode(thicknessScaleFactor1)); + } + if (iWheel == 1) { + BladeAngle = genericBladeElem.attr(_Unicode(angle2)); + AbsThickMin = absBladeElem.attr(_Unicode(thickness2)); + BladeThicknessScaleFactor = absBladeElem.attr(_Unicode(thicknessScaleFactor2)); + } + if (iWheel == 2) { + BladeAngle = genericBladeElem.attr(_Unicode(angle3)); + AbsThickMin = absBladeElem.attr(_Unicode(thickness3)); + BladeThicknessScaleFactor = absBladeElem.attr(_Unicode(thicknessScaleFactor3)); + } + + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "Making wheel with inner, outer radii %f, %f", ri, ro); + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "Blade angle is %f ", BladeAngle); + dd4hep::xml::Dimension dim(aXmlElement.child(_Unicode(dimensions))); + dd4hep::printout( dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "delZ is %f", delZ); + + if (TMath::Abs(TMath::Tan(BladeAngle)) < delZ/(2.*ri)) { + dd4hep::printout(dd4hep::ERROR, "ECalEndcap_Turbine_o1_v02", "The requested blade angle is too small for the given delZ and ri values. Please adjust to at least %f degrees!", TMath::ATan(delZ/(2.*ri))*180./TMath::Pi() ); + return; + } + + Float_t xRange = delZ/(TMath::Sin(BladeAngle)); + + double delrPhiNoGap; + + float GlueThick = glueElem.attr(_Unicode(thickness)); + float CladdingThick = claddingElem.attr(_Unicode(thickness)); + + AbsThickMin = AbsThickMin-(GlueThick+CladdingThick); + if (AbsThickMin < 0.) { + dd4hep::printout(dd4hep::ERROR, "ECalEndcap_Turbine_o1_v02", "Error: requested absorber thickness is negative after accounting for glue and cladding thickness"); + } + float ElectrodeThick = electrodeBladeElem.attr(_Unicode(thickness)); + float LArgapi = nobleLiquidElem.attr(_Unicode(gap)); + + bool sameNUnitCells = genericBladeElem.attr(_Unicode(sameNUnitCells)); + char* nUnitCellsStrArr = (char*)genericBladeElem.attr(_Unicode(nUnitCells)).c_str(); + char* nUnitCellsCStr = strtok(nUnitCellsStrArr, " "); + int nUnitCells = -1; + if (!sameNUnitCells) { + for (unsigned i = 0; i < iWheel; i++) { + nUnitCellsCStr = strtok(NULL, " "); + } + std::string nUnitCellsStr = nUnitCellsCStr; + nUnitCells = std::stoi(nUnitCellsStr); + } + int nUnitCellsLeastCommonMultiple = genericBladeElem.attr(_Unicode(nUnitCellsLeastCommonMultiple)); + + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "nUnitCells: %d", nUnitCells); + + float AbsThicki = AbsThickMin; + // make volumes for the noble liquid, electrode, and absorber blades + float AbsThicko; + AbsThicko = AbsThicki + BladeThicknessScaleFactor*((ro/ri)-1.)*AbsThicki; + + // Calculate gap thickness at inner layer + double circ = 2*TMath::Pi()*ri; + double x2 =(AbsThickMin+(GlueThick+CladdingThick)+ElectrodeThick)/TMath::Sin(BladeAngle); + double y2 = TMath::Sqrt(ri*ri-x2*x2); + double rPhi1 = ri*TMath::Pi()/2.; + double rPhi2 = ri*TMath::ATan(y2/x2); + delrPhiNoGap = TMath::Abs(rPhi1-rPhi2); + double leftoverS = (circ - nUnitCells*delrPhiNoGap); + double delrPhiGapOnly = leftoverS/(2*nUnitCells); + LArgapi = delrPhiGapOnly*TMath::Sin(BladeAngle); + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "LArGap at inner radius is %f", LArgapi); + + // now find gap at outer radius + circ = 2*TMath::Pi()*ro; + x2 = (AbsThicko+GlueThick+CladdingThick+ElectrodeThick)/TMath::Sin(BladeAngle); + y2 = TMath::Sqrt(ro*ro-x2*x2); + rPhi1 = ro*TMath::Pi()/2.; + rPhi2 = ro*TMath::ATan(y2/x2); + delrPhiNoGap = TMath::Abs(rPhi1-rPhi2); + leftoverS = (circ - nUnitCells*delrPhiNoGap); + delrPhiGapOnly = leftoverS/(2*nUnitCells); + float LArgapo = delrPhiGapOnly*TMath::Sin(BladeAngle); + // LArgapo *= 2.; + + dd4hep::Solid absBlade; + float riLayer = ri; + + std::vector claddingLayerVols; + std::vector glueLayerVols; + std::vector absBladeLayerVols; + std::vector LArTotalLayerVols; + std::vector electrodeBladeLayerVols; + + + dd4hep::Solid passiveShape = buildOneBlade(AbsThicki+GlueThick+CladdingThick, AbsThicko+GlueThick+CladdingThick, xRange, ro, ri, BladeAngle, delZ ); + dd4hep::Volume passiveVol("passive", passiveShape, aLcdd.material("Air")); + + dd4hep::Solid activeShape = buildOneBlade(ElectrodeThick+LArgapi*2, ElectrodeThick+LArgapo*2, xRange, ro, ri, BladeAngle, delZ); + dd4hep::Volume activeVol("active", activeShape, aLcdd.material("Air")); + + unsigned numNonActiveLayers = 1; + // check that either all non-active volumes are set to sensitive (for + // sampling fraction calculations) or none are (for normal running) + bool allNonActiveSensitive = ( claddingElem.isSensitive() && + glueElem.isSensitive() && + absBladeElem.isSensitive() && + electrodeBladeElem.isSensitive() ); + bool allNonActiveNotSensitive = ( !claddingElem.isSensitive() && + !glueElem.isSensitive() && + !absBladeElem.isSensitive() && + !electrodeBladeElem.isSensitive() ); + if (allNonActiveSensitive) { + numNonActiveLayers = ECalEndcapNumCalibLayers; + } else if (allNonActiveNotSensitive) { + numNonActiveLayers = 1; + } else { + dd4hep::printout(dd4hep::ERROR, "ECalEndcap_Turbine_o1_v02", "Some non-active layers are sensitive and others are not -- this is likely a misconfiguration"); + } + + float delrNonActive = (ro-ri)/numNonActiveLayers; + float delrActive = (ro-ri)/ECalEndcapNumCalibLayers; + + for (unsigned iLayer = 0; iLayer < numNonActiveLayers; iLayer++) { + float roLayer = riLayer + delrNonActive; + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "Making layer with inner, outer radii %f, %f", riLayer, roLayer); + + AbsThicko = AbsThicki + BladeThicknessScaleFactor*((roLayer/riLayer)-1.)*AbsThicki; + + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "Inner and outer absorber thicknesses %f, %f ", AbsThicki, AbsThicko); + dd4hep::Solid claddingLayer = buildOneBlade(AbsThicki+GlueThick+CladdingThick, AbsThicko+GlueThick+CladdingThick, xRange, roLayer, riLayer, BladeAngle, delZ ); + + dd4hep::Solid glueLayer = buildOneBlade(AbsThicki+GlueThick, AbsThicko+GlueThick, xRange, roLayer, riLayer, BladeAngle, delZ ); + + // dd4hep::SubtractionSolid claddingLayer(absGlueCladdingLayer, absGlueLayer); + dd4hep::Solid absBladeLayer = buildOneBlade(AbsThicki, AbsThicko, xRange, roLayer, riLayer, BladeAngle, delZ ); + + // dd4hep::SubtractionSolid glueLayer(absGlueLayer, absBladeLayer); + dd4hep::Volume claddingLayerVol("claddingLayer", claddingLayer, aLcdd.material(claddingElem.materialStr())); + if (claddingElem.isSensitive()) { + claddingLayerVol.setSensitiveDetector(aSensDet); + } + claddingLayerVols.push_back(claddingLayerVol); + + dd4hep::Volume glueLayerVol("glueLayer", glueLayer, aLcdd.material(glueElem.materialStr())); + if (glueElem.isSensitive()) { + glueLayerVol.setSensitiveDetector(aSensDet); + } + glueLayerVols.push_back(glueLayerVol); + + dd4hep::Volume absBladeLayerVol("absBladeLayer", absBladeLayer, aLcdd.material(absBladeElem.materialStr())); + if (absBladeElem.isSensitive()) { + absBladeLayerVol.setSensitiveDetector(aSensDet); + } + absBladeLayerVols.push_back(absBladeLayerVol); + + riLayer = roLayer; + AbsThicki = AbsThicko; + } + + riLayer = ri; + + AbsThicki = AbsThickMin; + + for (unsigned iLayer = 0; iLayer < ECalEndcapNumCalibLayers; iLayer++) { + + float roLayer = riLayer + delrActive; + + AbsThicko = AbsThicki + BladeThicknessScaleFactor*((roLayer/riLayer)-1.)*AbsThicki; + + // now find gap at outer layer + circ = 2*TMath::Pi()*roLayer; + x2 = (AbsThicko+GlueThick+CladdingThick+ElectrodeThick)/TMath::Sin(BladeAngle); + y2 = TMath::Sqrt(roLayer*roLayer-x2*x2); + rPhi1 = roLayer*TMath::Pi()/2.; + rPhi2 = roLayer*TMath::ATan(y2/x2); + delrPhiNoGap = TMath::Abs(rPhi1-rPhi2); + leftoverS = (circ - nUnitCells*delrPhiNoGap); + delrPhiGapOnly = leftoverS/(2*nUnitCells); + LArgapo = delrPhiGapOnly*TMath::Sin(BladeAngle); + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "Outer LAr gap is %f", LArgapo) ; + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "Inner and outer thicknesses of noble liquid volume %f, %f", ElectrodeThick+LArgapi*2, ElectrodeThick+LArgapo*2); + + dd4hep::Solid electrodeBladeAndGapLayer = buildOneBlade(ElectrodeThick+LArgapi*2, ElectrodeThick+LArgapo*2, xRange, roLayer, riLayer, BladeAngle, delZ); + + dd4hep::Solid electrodeBladeLayer = buildOneBlade(ElectrodeThick, ElectrodeThick, xRange, roLayer, riLayer, BladeAngle, delZ); + + dd4hep::Volume electrodeBladeLayerVol("electrodeBladeLayer", electrodeBladeLayer, aLcdd.material(electrodeBladeElem.materialStr())); + if (electrodeBladeElem.isSensitive()) { + electrodeBladeLayerVol.setSensitiveDetector(aSensDet); + } + electrodeBladeLayerVols.push_back(electrodeBladeLayerVol); + + // dd4hep::SubtractionSolid LArShapeTotalLayer(electrodeBladeAndGapLayer, electrodeBladeLayer); + dd4hep::Volume LArTotalLayerVol("LArTotalLayerVol", electrodeBladeAndGapLayer, aLcdd.material(nobleLiquidElem.materialStr())); + + if ( nobleLiquidElem.isSensitive() ) { + LArTotalLayerVol.setSensitiveDetector(aSensDet); + } + LArTotalLayerVols.push_back(LArTotalLayerVol); + + riLayer = roLayer; + LArgapi = LArgapo; + AbsThicki = AbsThicko; + } + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "ECal endcap materials: nobleLiquid: %s absorber %s electrode %s", nobleLiquidElem.materialStr().c_str(), absBladeElem.materialStr().c_str(), electrodeBladeElem.materialStr().c_str() ); + + int nUnitCellsToDraw = nUnitCells; + // nUnitCellsToDraw = 2; + + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "Number of unit cells %d", nUnitCells); + + // place all components of the absorber blade inside passive volume + + unsigned iLayer = 0; + + riLayer = ri; + + for (auto absBladeLayerVol: absBladeLayerVols) { + + float roLayer = riLayer+delrNonActive; + + dd4hep::Position posLayer(0,0,(riLayer-ri+roLayer-ro)/2.); + dd4hep::PlacedVolume absBladeVol_pv = glueLayerVols[iLayer].placeVolume(absBladeLayerVol, posLayer); + + absBladeVol_pv.addPhysVolID("subtype", 0); // 0 = absorber, 1 = glue, 2 = cladding + dd4hep::printout( dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02_geo", "Blade layer, rho is %d, %f, %f", iLayer, absBladeVol_pv.position().Rho(), roLayer/2.); + absBladeVol_pv.addPhysVolID("layer", iWheel*numNonActiveLayers+iLayer); + + riLayer = roLayer; + iLayer++; + } + + riLayer = ri; + iLayer =0; + + for (auto glueLayerVol: glueLayerVols) { + + float roLayer = riLayer+delrNonActive; + + dd4hep::Position posLayer(0,0,(riLayer-ri+roLayer-ro)/2.); + dd4hep::PlacedVolume glueVol_pv = claddingLayerVols[iLayer].placeVolume(glueLayerVol, posLayer); + + + glueVol_pv.addPhysVolID("subtype", 1); // 0 = absorber, 1 = glue, 2 = cladding + glueVol_pv.addPhysVolID("layer", iWheel*numNonActiveLayers+iLayer); + + // dd4hep::DetElement glueDetElem(passiveDetElem, "glue_",ECalEndCapElementCounter++); + // glueDetElem.setPlacement(glueVol_pv); + + riLayer = roLayer; + iLayer++; + } + + riLayer = ri; + iLayer =0; + + double zminri = getZmin(ri, BladeAngle, delZ); + + for (auto claddingLayerVol: claddingLayerVols) { + + float roLayer = riLayer+delrNonActive; + + double zminLayer = getZmin(riLayer, BladeAngle, delZ); + + dd4hep::Position posLayer(0,0,(zminLayer-zminri+roLayer-ro)/2.); + dd4hep::PlacedVolume claddingVol_pv = passiveVol.placeVolume(claddingLayerVol, posLayer); + + claddingVol_pv.addPhysVolID("subtype", 2); // 0 = absorber, 1 = glue, 2 = cladding + claddingVol_pv.addPhysVolID("layer", iWheel*numNonActiveLayers+iLayer); + + // dd4hep::DetElement claddingDetElem(passiveDetElem, "cladding_", ECalEndCapElementCounter++); + // claddingDetElem.setPlacement(claddingVol_pv); + + riLayer = roLayer; + iLayer++; + } + + + riLayer = ri; + iLayer = 0; + + for (auto electrodeBladeLayerVol: electrodeBladeLayerVols) { + + float roLayer = riLayer+delrActive; + + dd4hep::PlacedVolume electrodeBladeVol_pv = LArTotalLayerVols[iLayer].placeVolume(electrodeBladeLayerVol); + electrodeBladeVol_pv.addPhysVolID("layer", iWheel*numNonActiveLayers+iLayer); + + riLayer = roLayer; + iLayer++; + } + + riLayer = ri; + iLayer = 0; + + for (auto LArTotalLayerVol: LArTotalLayerVols) { + + float roLayer = riLayer+delrActive; + + double zminLayer = getZmin(riLayer, BladeAngle, delZ); + + dd4hep::Position posLayer(0,0,(zminLayer-zminri+roLayer-ro)/2.); + + dd4hep::PlacedVolume LArVol_pv(activeVol.placeVolume(LArTotalLayerVol, posLayer)); + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "LAr layer: %d", iLayer ); + LArVol_pv.addPhysVolID("layer", iWheel*ECalEndcapNumCalibLayers+iLayer); + + riLayer = roLayer; + iLayer++; + } + + for (int iUnitCell = 0; iUnitCell < nUnitCellsToDraw; iUnitCell++) { + + int modIndex = iUnitCell-nUnitCellsToDraw/2; + if (modIndex < 0) modIndex += nUnitCells; + float phi = (iUnitCell-nUnitCellsToDraw/2)*2*TMath::Pi()/nUnitCells; + float delPhi = 2*TMath::Pi()/nUnitCells; + + dd4hep::printout( dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "Placing blade, ro, ri = %f %f", ro, ri); + + TGeoRotation tgr; + tgr.RotateZ(BladeAngle*180/TMath::Pi()); + tgr.RotateX(-phi*180/TMath::Pi()); + tgr.RotateY(90); + + const Double_t *rotMatPtr; + + rotMatPtr = tgr.GetRotationMatrix(); + TMatrixT rotMat(3,3, rotMatPtr); + dd4hep::Rotation3D r3d; + r3d.SetComponents(rotMat(0,0), rotMat(0,1), rotMat(0,2), + rotMat(1,0), rotMat(1,1), rotMat(1,2), + rotMat(2,0), rotMat(2,1), rotMat(2,2)); + + tgr.Clear(); + tgr.RotateZ(BladeAngle*180/TMath::Pi()); + tgr.RotateX(-(phi+delPhi/2.)*180/TMath::Pi()); + tgr.RotateY(90); + + rotMatPtr = tgr.GetRotationMatrix(); + TMatrixT rotMat2(3,3, rotMatPtr); + dd4hep::Rotation3D r3d2; + r3d2.SetComponents(rotMat2(0,0), rotMat2(0,1), rotMat2(0,2), + rotMat2(1,0), rotMat2(1,1), rotMat2(1,2), + rotMat2(2,0), rotMat2(2,1), rotMat2(2,2)); + + riLayer = ri; + + float xCell = ((ro+zminri)/2.)*TMath::Cos(phi); + float yCell = ((ro+zminri)/2.)*TMath::Sin(phi); //ri*TMath::Sin(phi)/6.; + float zCell = 0.; + + dd4hep::Transform3D comCell(r3d, dd4hep::Translation3D(xCell,yCell,zCell)); + + // place passive volume in LAr bath + dd4hep::PlacedVolume passivePhysVol = aEnvelope.placeVolume(passiveVol, comCell); + passivePhysVol.addPhysVolID("module", modIndex*nUnitCellsLeastCommonMultiple/nUnitCells); + passivePhysVol.addPhysVolID("wheel", iWheel); + passivePhysVol.addPhysVolID("type", 1); // 0 = active, 1 = passive, 2 = readout + dd4hep::DetElement passiveDetElem(bathDetElem, "passive_" + std::to_string(iUnitCell)+"_"+std::to_string(iWheel), ECalEndCapElementCounter++); + passiveDetElem.setPlacement(passivePhysVol); + + // place active volume in LAr bath + + xCell = ((ro+zminri)/2.)*TMath::Cos(phi+delPhi/2.); + yCell = ((ro+zminri)/2.)*TMath::Sin(phi+delPhi/2.); //ri*TMath::Sin(phi)/6.; + zCell = 0.; + dd4hep::Transform3D comCell2(r3d2, dd4hep::Translation3D(xCell,yCell,zCell)); + dd4hep::PlacedVolume activePhysVol = aEnvelope.placeVolume(activeVol, comCell2); + activePhysVol.addPhysVolID("module", modIndex*nUnitCellsLeastCommonMultiple/nUnitCells); + activePhysVol.addPhysVolID("wheel", iWheel); + activePhysVol.addPhysVolID("type", 0); // 0 = active, 1 = passive, 2 = readout + + dd4hep::DetElement activeDetElem(bathDetElem, "active_" + std::to_string(iUnitCell)+"_"+std::to_string(iWheel), ECalEndCapElementCounter++); + activeDetElem.setPlacement(activePhysVol); + + riLayer = ri; + iLayer =0; + + + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "LArTotalLayerVols.size = %d", LArTotalLayerVols.size()); + + } + + return; + + } + + void buildOneSide_Turbine(dd4hep::Detector& aLcdd, dd4hep::SensitiveDetector& aSensDet, + dd4hep::Volume& aEnvelope, dd4hep::xml::Handle_t& aXmlElement, + unsigned& iModule) { + + dd4hep::xml::DetElement calo = aXmlElement.child(_Unicode(calorimeter)); + dd4hep::xml::Dimension caloDim(calo.dimensions()); + + + dd4hep::xml::DetElement blade = calo.child(_Unicode(turbineBlade)); + dd4hep::xml::DetElement nobleLiquid = blade.child(_Unicode(nobleLiquidGap)); + + dd4hep::xml::DetElement xmlDetElem = aXmlElement; + std::string nameDet = xmlDetElem.nameStr(); + dd4hep::DetElement caloDetElem(nameDet, xmlDetElem.id()); + + dd4hep::xml::Dimension dim(aXmlElement.child(_Unicode(dimensions))); + + //build cryostat + // Retrieve cryostat data + dd4hep::xml::DetElement cryostat = calo.child(_Unicode(cryostat)); + dd4hep::xml::Dimension cryoDim(cryostat.dimensions()); + double cryoThicknessFront = cryoDim.rmin2() - cryoDim.rmin1(); + + dd4hep::xml::DetElement cryoFront = cryostat.child(_Unicode(front)); + dd4hep::xml::DetElement cryoBack = cryostat.child(_Unicode(back)); + dd4hep::xml::DetElement cryoSide = cryostat.child(_Unicode(side)); + bool cryoFrontSensitive = cryoFront.isSensitive(); + bool cryoBackSensitive = cryoBack.isSensitive(); + bool cryoSideSensitive = cryoSide.isSensitive(); + + double bathRmin = caloDim.rmin(); // - margin for inclination + double bathRmax = caloDim.rmax(); // + margin for inclination + double bathDelZ = caloDim.dz(); + dd4hep::Tube bathOuterShape(bathRmin, bathRmax, bathDelZ); // make it 4 volumes + 5th for detector envelope + dd4hep::Tube bathAndServicesOuterShape(cryoDim.rmin2(), cryoDim.rmax1(), caloDim.dz()); // make it 4 volumes + 5th for detector envelope + + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "Cryostat front thickness is %f", cryoDim.rmin2() ); + + if (cryoThicknessFront > 0) { + // 1. Create cryostat + dd4hep::Tube cryoFrontShape(cryoDim.rmin1(), cryoDim.rmin2(), cryoDim.dz()); + dd4hep::Tube cryoBackShape(cryoDim.rmax1(), cryoDim.rmax2(), cryoDim.dz()); + dd4hep::Tube cryoSideOuterShape(cryoDim.rmin2(), cryoDim.rmax1(), cryoDim.dz()); + dd4hep::SubtractionSolid cryoSideShape(cryoSideOuterShape, bathAndServicesOuterShape); + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "ECAL endcap cryostat: front: rmin (cm) = %f rmax (cm) = %f dz (cm) = %f ", cryoDim.rmin1(), cryoDim.rmin2(), cryoDim.dz()); + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "ECAL encdap cryostat: back: rmin (cm) = %f rmax (cm) = %f dz (cm) = %f", cryoDim.rmax1(), cryoDim.rmax2(), cryoDim.dz()); + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "ECAL endcap cryostat: side: rmin (cm) = %f rmax (cm) = %f dz (cm) = %f", cryoDim.rmin2(), cryoDim.rmax1(), cryoDim.dz() - caloDim.dz()); + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "Cryostat is made out of %s", cryostat.materialStr().c_str() ); + + dd4hep::Volume cryoFrontVol(cryostat.nameStr()+"_front", cryoFrontShape, aLcdd.material(cryostat.materialStr())); + dd4hep::Volume cryoBackVol(cryostat.nameStr()+"_back", cryoBackShape, aLcdd.material(cryostat.materialStr())); + dd4hep::Volume cryoSideVol(cryostat.nameStr()+"_side", cryoSideShape, aLcdd.material(cryostat.materialStr())); + dd4hep::PlacedVolume cryoFrontPhysVol = aEnvelope.placeVolume(cryoFrontVol); + dd4hep::PlacedVolume cryoBackPhysVol = aEnvelope.placeVolume(cryoBackVol); + dd4hep::PlacedVolume cryoSidePhysVol = aEnvelope.placeVolume(cryoSideVol); + unsigned sidetype = 0x4; // probably not needed anymore... + if (cryoFrontSensitive) { + cryoFrontVol.setSensitiveDetector(aSensDet); + cryoFrontPhysVol.addPhysVolID("cryo", 1); + cryoFrontPhysVol.addPhysVolID("type", sidetype+1); + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "Cryostat front volume set as sensitive"); + } + if (cryoBackSensitive) { + cryoBackVol.setSensitiveDetector(aSensDet); + cryoBackPhysVol.addPhysVolID("cryo", 1); + cryoBackPhysVol.addPhysVolID("type", sidetype+2); + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "Cryostat back volume set as sensitive" ); + } + if (cryoSideSensitive) { + cryoSideVol.setSensitiveDetector(aSensDet); + cryoSidePhysVol.addPhysVolID("cryo", 1); + cryoSidePhysVol.addPhysVolID("type", sidetype+3); + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "Cryostat side volume set as sensitive" ); + } + dd4hep::DetElement cryoFrontDetElem(caloDetElem, "cryo_front", 0); + cryoFrontDetElem.setPlacement(cryoFrontPhysVol); + dd4hep::DetElement cryoBackDetElem(caloDetElem, "cryo_back", 0); + cryoBackDetElem.setPlacement(cryoBackPhysVol); + dd4hep::DetElement cryoSideDetElem(caloDetElem, "cryo_side", 0); + cryoSideDetElem.setPlacement(cryoSidePhysVol); + } + + // 2. Create noble liquid bath + std::string nobleLiquidMaterial = nobleLiquid.materialStr(); + dd4hep::Volume bathVol(nobleLiquidMaterial + "_bath", bathOuterShape, aLcdd.material(nobleLiquidMaterial)); + dd4hep::printout( dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "ECAL endcap bath: material = %s rmin (cm) = %f rmax (cm) = %f, dz (cm) = %f, thickness in front of ECal (cm) = %f, thickness behind ECal (cm) = %f", nobleLiquidMaterial.c_str(), bathRmin, bathRmax, caloDim.dz(), caloDim.rmin() - cryoDim.rmin2(), cryoDim.rmax1() - caloDim.rmax()); + dd4hep::DetElement bathDetElem(caloDetElem, "bath", 1); + + // 3. Create detector structure + double length = dim.dz() * 2.; + double zOffsetEnvelope = -length / 2.; + + dd4hep::xml::DetElement supportTubeElem = calo.child(_Unicode(supportTube)); + unsigned nWheels = supportTubeElem.attr(_Unicode(nWheels)); + dd4hep::printout(dd4hep::INFO, "ECalEndcap_Turbine_o1_v02", "Will build %d wheels", nWheels); + double rmin = bathRmin; + double rmax = bathRmax; + float radiusRatio = pow(rmax/rmin, 1./nWheels); + double ro = rmin*radiusRatio; + double ri = rmin; + + float supportTubeThickness=supportTubeElem.thickness(); + unsigned iSupportTube = 0; + + for (unsigned iWheel = 0; iWheel < nWheels; iWheel++) { + + dd4hep::Tube supportTube(ro, ro+supportTubeThickness, bathDelZ ); + + dd4hep::Volume supportTubeVol("supportTube", supportTube, aLcdd.material(supportTubeElem.materialStr())); + if (supportTubeElem.isSensitive()) { + supportTubeVol.setSensitiveDetector(aSensDet); + } + dd4hep::PlacedVolume supportTube_pv = bathVol.placeVolume(supportTubeVol, dd4hep::Position(0,0,zOffsetEnvelope + dim.dz() )); + supportTube_pv.addPhysVolID("cryo", 1); + // supportTube_pv.addPhysVolID("side",sign); + supportTube_pv.addPhysVolID("wheel", iWheel); + dd4hep::DetElement supportTubeDetElem(bathDetElem, "supportTube_"+std::to_string(iWheel), 0); + supportTubeDetElem.setPlacement(supportTube_pv); + + + buildWheel(aLcdd, aSensDet, bathVol, aXmlElement, bathDetElem, ri+supportTubeThickness, ro, bathDelZ*2, iWheel); + ri = ro; + ro *= radiusRatio; + if (ro > rmax) ro = rmax; + iSupportTube++; + } + + + dd4hep::PlacedVolume bathPhysVol = aEnvelope.placeVolume(bathVol); + bathDetElem.setPlacement(bathPhysVol); + + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "Total number of modules: %d", iModule); + + + return; + } + + + + static dd4hep::Ref_t + createECalEndcapTurbine(dd4hep::Detector& aLcdd, dd4hep::xml::Handle_t aXmlElement, dd4hep::SensitiveDetector aSensDet) { + + dd4hep::xml::DetElement xmlDetElem = aXmlElement; + std::string nameDet = xmlDetElem.nameStr(); + int idDet = xmlDetElem.id(); + dd4hep::xml::Dimension dim(xmlDetElem.dimensions()); + dd4hep::DetElement caloDetElem(nameDet, idDet); + dd4hep::xml::Dimension sdType = xmlDetElem.child(_U(sensitive)); + aSensDet.setType(sdType.typeStr()); + + ECalEndcapNumCalibLayers = aLcdd.constant("ECalEndcapNumCalibLayers"); + + + // Create air envelope for one endcap (will be copied to make both endcaps) + dd4hep::Tube endcapShape( dim.rmin1(), dim.rmax1(), dim.dz()); + + dd4hep::Volume envelopeVol(nameDet + "_vol", endcapShape, aLcdd.material("Air")); + + + // dd4hep::DetElement caloPositiveDetElem(caloDetElem, "positive", 0); + // dd4hep::DetElement caloNegativeDetElem(caloDetElem, "negative", 0); + + dd4hep::printout(dd4hep::DEBUG, "ECalEndcap_Turbine_o1_v02", "Placing detector on the positive side: (cm) %f with min, max radii %f %f",dim.z_offset(), dim.rmin1(), dim.rmax1() ); + + unsigned iModule = 0; + buildOneSide_Turbine(aLcdd, aSensDet, envelopeVol, aXmlElement, iModule); + + dd4hep::Assembly endcapsAssembly("ECalEndcaps_turbine"); + + // Place the envelope + dd4hep::Transform3D envelopePositiveVolume_tr(dd4hep::RotationZYX( 0 ,0,0), dd4hep::Translation3D(0, 0, dim.z_offset())); + dd4hep::PlacedVolume envelopePositivePhysVol = endcapsAssembly.placeVolume(envelopeVol, envelopePositiveVolume_tr); + envelopePositivePhysVol.addPhysVolID("side", 1); + + dd4hep::DetElement caloPositiveDetElem(caloDetElem, "positive", 0); + caloPositiveDetElem.setPlacement(envelopePositivePhysVol); + + // make another placement for the negative z endcap + dd4hep::Transform3D envelopeNegativeVolume_tr(dd4hep::RotationZYX( 0 ,0,180*dd4hep::deg), dd4hep::Translation3D(0, 0, -dim.z_offset())); + dd4hep::PlacedVolume envelopeNegativePhysVol = + endcapsAssembly.placeVolume(envelopeVol, envelopeNegativeVolume_tr); + envelopeNegativePhysVol.addPhysVolID("side", -1); + + dd4hep::DetElement caloNegativeDetElem(caloDetElem, "negative", 0); + caloNegativeDetElem.setPlacement(envelopeNegativePhysVol); + + dd4hep::Volume motherVol = aLcdd.pickMotherVolume(caloDetElem); + dd4hep::PlacedVolume envelopePhysVol = motherVol.placeVolume(endcapsAssembly); + caloDetElem.setPlacement(envelopePhysVol); + envelopePhysVol.addPhysVolID("system", idDet); + return caloDetElem; + } + } +} // namespace det + +DECLARE_DETELEMENT(ECalEndcap_Turbine_o1_v02, det::ECalEndcap_Turbine_o1_v02::createECalEndcapTurbine) diff --git a/detector/calorimeter/README.md b/detector/calorimeter/README.md index ce9c3b869..1306a0a87 100644 --- a/detector/calorimeter/README.md +++ b/detector/calorimeter/README.md @@ -26,9 +26,12 @@ Original version taken from [FCCDetectors](https://github.com/HEP-FCC/FCCDetecto Sub-detector for the ecal endcaps, with the absorbers and readout boards arranged in a "turbine-like" geometry. -### 01_v01 +### o1_v01 Initial implementation. A custom segmentation that creates readout cells and constant radius and z is also included (FCCSWEndcapTurbine_k4geo). +### o1_v02 +Changes wrt o1_v01: Added flexibility to configure the wheels individual (to allow for example the possibiliity of having different blade angles in each). This is still a work in progress, so o1_v01 should be used for integrated tests. + ## HCalTileBarrel This sub-detector makes calorimeter barrel. It is used in ALLEGRO detector concept. diff --git a/detectorSegmentations/include/detectorSegmentations/FCCSWEndcapTurbine_k4geo.h b/detectorSegmentations/include/detectorSegmentations/FCCSWEndcapTurbine_k4geo.h index b4abef119..3b1d93bb6 100644 --- a/detectorSegmentations/include/detectorSegmentations/FCCSWEndcapTurbine_k4geo.h +++ b/detectorSegmentations/include/detectorSegmentations/FCCSWEndcapTurbine_k4geo.h @@ -129,7 +129,9 @@ class FCCSWEndcapTurbine_k4geo : public Segmentation { protected: /// turbine blade angle - double m_bladeAngle; + std::vector m_bladeAngle; + /// least common multiple of number of unit cells + int m_nUnitCellsLeastCommonMultiple; /// the number of bins in phi int m_phiBins; /// the coordinate offset in phi diff --git a/detectorSegmentations/src/FCCSWEndcapTurbine_k4geo.cpp b/detectorSegmentations/src/FCCSWEndcapTurbine_k4geo.cpp index c25d00625..a3cf00b35 100644 --- a/detectorSegmentations/src/FCCSWEndcapTurbine_k4geo.cpp +++ b/detectorSegmentations/src/FCCSWEndcapTurbine_k4geo.cpp @@ -21,15 +21,41 @@ namespace DDSegmentation { registerIdentifier("identifier_side", "Cell ID identifier for side", m_sideID, "side"); dd4hep::Detector* dd4hepgeo = &(dd4hep::Detector::getInstance()); + m_bladeAngle.clear(); + + try { + m_bladeAngle.push_back(dd4hepgeo->constant("BladeAngle1")); + std::cout << "Blade angle 1 is " << m_bladeAngle[0] << std::endl; + } + catch(...) { + std::cout << "Blade angle 1 not found in detector metadata, exiting..." << std::endl; + exit(1); + } + try { + m_bladeAngle.push_back(dd4hepgeo->constant("BladeAngle2")); + std::cout << "Blade angle 2 is " << m_bladeAngle[1] << std::endl; + } + catch(...) { + std::cout << "Blade angle 2 not found in detector metadata, exiting..." << std::endl; + exit(1); + } try { - m_bladeAngle = dd4hepgeo->constant("BladeAngle"); - std::cout << "Blade angle is " << m_bladeAngle << std::endl; + m_bladeAngle.push_back(dd4hepgeo->constant("BladeAngle3")); + std::cout << "Blade angle 3 is " << m_bladeAngle[2] << std::endl; } catch(...) { - std::cout << "Blade angle not found in detector metadata, exiting..." << std::endl; + std::cout << "Blade angle 3 not found in detector metadata, exiting..." << std::endl; exit(1); } + try { + m_nUnitCellsLeastCommonMultiple = dd4hepgeo->constant("nUnitCellsLeastCommonMultiple"); + } + catch(...) { + std::cout << "nUnitCellsLeastCommonMultiple not found in detector metadata, exiting..." << std::endl; + exit(1); + } + } FCCSWEndcapTurbine_k4geo::FCCSWEndcapTurbine_k4geo(const BitFieldCoder* decoder) : Segmentation(decoder) { @@ -49,15 +75,40 @@ namespace DDSegmentation { registerIdentifier("identifier_wheel", "Cell ID identifier for wheel", m_wheelID, "wheel"); registerIdentifier("identifier_module", "Cell ID identifier for module", m_moduleID, "module"); dd4hep::Detector* dd4hepgeo = &(dd4hep::Detector::getInstance()); + + m_bladeAngle.clear(); + try { - m_bladeAngle = dd4hepgeo->constant("BladeAngle"); - std::cout << "Blade angle is " << m_bladeAngle << std::endl; + m_bladeAngle.push_back(dd4hepgeo->constant("BladeAngle1")); + std::cout << "Blade angle 1 is " << m_bladeAngle[0] << std::endl; } catch(...) { - std::cout << "Blade angle not found in detector metadata, exiting..." << std::endl; + std::cout << "Blade angle 1 not found in detector metadata, exiting..." << std::endl; + exit(1); + } + try { + m_bladeAngle.push_back(dd4hepgeo->constant("BladeAngle2")); + std::cout << "Blade angle 2 is " << m_bladeAngle[1] << std::endl; + } + catch(...) { + std::cout << "Blade angle 2 not found in detector metadata, exiting..." << std::endl; + exit(1); + } + try { + m_bladeAngle.push_back(dd4hepgeo->constant("BladeAngle3")); + std::cout << "Blade angle 3 is " << m_bladeAngle[2] << std::endl; + } + catch(...) { + std::cout << "Blade angle 3 not found in detector metadata, exiting..." << std::endl; + exit(1); + } + try { + m_nUnitCellsLeastCommonMultiple = dd4hepgeo->constant("nUnitCellsLeastCommonMultiple"); + } + catch(...) { + std::cout << "nUnitCellsLeastCommonMultiple not found in detector metadata, exiting..." << std::endl; exit(1); } - } @@ -90,12 +141,16 @@ CellID FCCSWEndcapTurbine_k4geo::cellID(const Vector3D& /* localPosition */, con double FCCSWEndcapTurbine_k4geo::phi(const CellID& cID) const { CellID iModule = _decoder->get(cID, m_moduleID); - double phiCent = twopi*(iModule+0.5)/78336; + CellID iWheel = _decoder->get(cID, m_wheelID); + + double phiCent = twopi*(iModule+0.5)/m_nUnitCellsLeastCommonMultiple; double rhoLoc = rho(cID); double zLoc = TMath::Abs(z(cID))-m_offsetZ - 45; // hard-code midpoint in z for now - double zCotBladeAngle = zLoc/TMath::Tan(m_bladeAngle); + + double zCotBladeAngle = zLoc/TMath::Tan(m_bladeAngle[iWheel]); + double x = zCotBladeAngle; double y = TMath::Sqrt(rhoLoc*rhoLoc - x*x); // rotate about z axis by phiCent