From 7dd79da83d61ce4100ac4538c13bf392006c9934 Mon Sep 17 00:00:00 2001 From: lintao <lintao51@gmail.com> Date: Mon, 10 Aug 2020 11:26:26 +0800 Subject: [PATCH] Import SEcal05 detector constructor from Chengdong Fu. --- Detector/DetCEPCv4/CMakeLists.txt | 4 + .../src/calorimeter/SEcal05_Barrel.cpp | 444 +++++++++++ .../src/calorimeter/SEcal05_ECRing.cpp | 689 +++++++++++++++++ .../src/calorimeter/SEcal05_Endcaps.cpp | 390 ++++++++++ .../src/calorimeter/SEcal05_Helpers.cpp | 729 ++++++++++++++++++ .../src/calorimeter/SEcal05_Helpers.h | 309 ++++++++ 6 files changed, 2565 insertions(+) create mode 100644 Detector/DetCEPCv4/src/calorimeter/SEcal05_Barrel.cpp create mode 100644 Detector/DetCEPCv4/src/calorimeter/SEcal05_ECRing.cpp create mode 100644 Detector/DetCEPCv4/src/calorimeter/SEcal05_Endcaps.cpp create mode 100644 Detector/DetCEPCv4/src/calorimeter/SEcal05_Helpers.cpp create mode 100644 Detector/DetCEPCv4/src/calorimeter/SEcal05_Helpers.h diff --git a/Detector/DetCEPCv4/CMakeLists.txt b/Detector/DetCEPCv4/CMakeLists.txt index 7211736c..ff3a8a70 100644 --- a/Detector/DetCEPCv4/CMakeLists.txt +++ b/Detector/DetCEPCv4/CMakeLists.txt @@ -26,6 +26,10 @@ set(DetCEPCv4_src src/tracker/SIT_Simple_Pixel_geo.cpp src/tracker/TPC10_geo.cpp src/tracker/SET_Simple_Planar_geo.cpp + src/calorimeter/SEcal05_Helpers.cpp + src/calorimeter/SEcal05_Barrel.cpp + src/calorimeter/SEcal05_Endcaps.cpp + src/calorimeter/SEcal05_ECRing.cpp src/other/BoxSupport_o1_v01_geo.cpp src/other/TubeSupport_o1_v01_geo.cpp ) diff --git a/Detector/DetCEPCv4/src/calorimeter/SEcal05_Barrel.cpp b/Detector/DetCEPCv4/src/calorimeter/SEcal05_Barrel.cpp new file mode 100644 index 00000000..a0329b4e --- /dev/null +++ b/Detector/DetCEPCv4/src/calorimeter/SEcal05_Barrel.cpp @@ -0,0 +1,444 @@ +//==================================================================== +// lcgeo - LC detector models in DD4hep +//-------------------------------------------------------------------- +// DD4hep Geometry driver for SiWEcalBarrel +// Ported from Mokka +//-------------------------------------------------------------------- +// S.Lu, DESY +// D.Jeans, Tokyo <-- to 05: add possibility to remove preshower layer +//==================================================================== + +#include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/DetType.h" + +#include "XML/Layering.h" +#include "TGeoTrd2.h" + +#include "XML/Utilities.h" +#include "DDRec/DetectorData.h" + +#include "DDSegmentation/MegatileLayerGridXY.h" +#include "DDSegmentation/WaferGridXY.h" + +#include "SEcal05_Helpers.h" + +#include <sstream> + +#undef NDEBUG +#include <assert.h> + +using namespace std; + +using dd4hep::BUILD_ENVELOPE; +using dd4hep::DetElement; +using dd4hep::Detector; +using dd4hep::Layering; +using dd4hep::Material; +using dd4hep::PlacedVolume; +using dd4hep::Position; +using dd4hep::Readout; +using dd4hep::Ref_t; +using dd4hep::RotationZYX; +using dd4hep::Segmentation; +using dd4hep::SensitiveDetector; +using dd4hep::Transform3D; +using dd4hep::Translation3D; +using dd4hep::Trapezoid; +using dd4hep::Volume; +using dd4hep::_toString; + +using dd4hep::rec::LayeredCalorimeterData; + +#define VERBOSE 1 + +// workaround for DD4hep v00-14 (and older) +#ifndef DD4HEP_VERSION_GE +#define DD4HEP_VERSION_GE(a,b) 0 +#endif + +/** SEcal05.cc + * + * new SEcal05 barrel driver: allows removal of preshower layer. DJeans UTokyo, sep/2016 + * based on SEcal04 + * + * @author: Shaojun Lu, DESY + * @version $Id: SEcal04_Barrel.cpp 1060 2016-09-05 07:48:41Z /C=JP/O=KEK/OU=CRC/CN=JEANS Daniel Thomelin Dietrich $ + * Ported from Mokka SEcal04 Barrel part. Read the constants from XML + * instead of the DB. Then build the Barrel in the same way with DD4hep + * construct. + * + * @history: F.Gaede, CERN/DESY, Nov. 10, 2014 + * added information for reconstruction: LayeringExtension and surfaces (experimental) + * removed DetElement for slices (not needed) increased multiplicity for layer DetElement + * along tower index + * F.Gaede: 03/2015: + * create the envelope volume with create_placed_envelope() using the xml + * + * D. Jeans: 03/2015: + * generalise to non-octagonal barrel shape + * allow dead region between end of each slab and the module edge (e.g. for the slab fastening system) + */ + +/* + + + + + y + ^ + | z is perpendicular to page (along beamline) + | + -----> x + + original coord system was very mixed up. + Daniel tried to rationalise it to make it more understandable (hopefully) + + absorber layers are wrapped in a number of layers of carbon fibre composite (CF) + + structure of slab (which is inserted into the structure) is defined in the compact description + + general structure (from +ve y) + ------------------------------- + + if preshower, front face (CF) \\\\\\\\\\\\\\\\\\ + + if no preshower, wrapped absorber ================== + + __________________ + alveolus, containing the slab |__________________| + + wrapped absorber =================== + __________________ + alveolus, containing the slab |__________________| + + . + repeat as required + . + + wrapped absorber =================== + __________________ + alveolus, containing the slab |__________________| + + back support plate (CF) \\\\\\\\\\\\\\\\\\\\ + //////////////////// + + */ + + + +static Ref_t create_detector(Detector& theDetector, xml_h element, SensitiveDetector sens) { + + cout << "-----------------------" << endl; + cout << "creating SEcal05_Barrel" << endl; + cout << "-----------------------" << endl; + + xml_det_t x_det = element; + string det_name = x_det.nameStr(); + Layering layering (element); + + Material carbon_fibre = theDetector.material("CarbonFiber"); + + int det_id = x_det.id(); + DetElement sdet (det_name,det_id); + + xml_comp_t x_dim = x_det.dimensions(); + int nsides = x_dim.numsides(); + + double dphi = (2*M_PI/nsides); + double hphi = dphi/2; + + // --- create an envelope volume and position it into the world --------------------- + + Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector, element , sdet ) ; + + dd4hep::xml::setDetectorTypeFlag( element, sdet ) ; + + if( theDetector.buildType() == BUILD_ENVELOPE ) return sdet ; + + //----------------------------------------------------------------------------------- + + sens.setType("calorimeter"); + + DetElement stave_det("module1stave1",det_id); + + //==================================================================== + // + // Read all the constant from ILD_o1_v05.xml + // Use them to build HcalBarrel + // + //==================================================================== + + // some hardcoded values! + // const int N_FIBERS_W_STRUCTURE = 2; // number of CF layers around absorber layers in the structure + // const int N_FIBERS_ALVEOLUS = 3; // number of CF layers used to make alveolus + + // read other parameters from compact.xml file + + // overall size + double Ecal_inner_radius = theDetector.constant<double>("Ecal_inner_radius"); + + double Ecal_Barrel_halfZ = theDetector.constant<double>("Ecal_Barrel_halfZ"); + int Ecal_barrel_z_modules = theDetector.constant<int>("Ecal_barrel_z_modules"); + int Ecal_barrel_number_of_towers = theDetector.constant<int>("Ecal_barrel_number_of_towers"); + + double Ecal_barrel_thickness = theDetector.constant<double>("Ecal_barrel_thickness"); // what's assumed in the compact description + + // absorber layers + int Ecal_nlayers1 = theDetector.constant<int>("Ecal_nlayers1"); + int Ecal_nlayers2 = theDetector.constant<int>("Ecal_nlayers2"); + int Ecal_nlayers3 = theDetector.constant<int>("Ecal_nlayers3"); + + double Ecal_radiator_thickness1 = theDetector.constant<double>("Ecal_radiator_layers_set1_thickness"); + double Ecal_radiator_thickness2 = theDetector.constant<double>("Ecal_radiator_layers_set2_thickness"); + double Ecal_radiator_thickness3 = theDetector.constant<double>("Ecal_radiator_layers_set3_thickness"); + + // CF support dimensions + double Ecal_support_thickness = theDetector.constant<double>("Ecal_support_thickness"); + double Ecal_front_face_thickness = theDetector.constant<double>("Ecal_front_face_thickness"); + double Ecal_lateral_face_thickness = theDetector.constant<double>("Ecal_lateral_face_thickness"); + double Ecal_Slab_H_fiber_thickness = theDetector.constant<double>("Ecal_Slab_H_fiber_thickness"); + + + // double Ecal_fiber_thickness = theDetector.constant<double>("Ecal_fiber_thickness"); + // some hardcoded values! + // const int N_FIBERS_W_STRUCTURE = 2; // number of CF layers around absorber layers in the structure + // const int N_FIBERS_ALVEOLUS = 3; // number of CF layers used to make alveolus + double Ecal_fiber_thickness_structure = theDetector.constant<double>("Ecal_fiber_thickness_structure"); // absorber wrapping thickness + double Ecal_fiber_thickness_alveolus = theDetector.constant<double>("Ecal_fiber_thickness_alveolus"); // alveolar wall thickness + + double Ecal_Slab_shielding = theDetector.constant<double>("Ecal_Slab_shielding"); + + // first layer is preshower? + bool Ecal_Barrel_PreshowerLayer = theDetector.constant<int>("Ecal_Barrel_Preshower") > 0; + + // internal dimensions of slab + double Ecal_guard_ring_size = theDetector.constant<double>("Ecal_guard_ring_size"); + int Ecal_n_wafers_per_tower = theDetector.constant<int>("Ecal_n_wafers_per_tower"); + + std::string Ecal_layerConfig = theDetector.constant<string>("Ecal_layer_pattern"); + + int Ecal_end_of_slab_strategy = theDetector.constant<int>("Ecal_end_of_slab_strategy"); + + int Ecal_cells_across_megatile = theDetector.constant <int> ("Ecal_cells_across_megatile" ); + int Ecal_strips_across_megatile = theDetector.constant <int> ("Ecal_strips_across_megatile"); + int Ecal_strips_along_megatile = theDetector.constant <int> ("Ecal_strips_along_megatile" ); + + float Ecal_plugLength = 0.; + try { + Ecal_plugLength = theDetector.constant<double>("Ecal_Slab_Plug_length"); + } catch (std::runtime_error&e) { + cout << "SEcal05_Barrel: Ecal_plugLength not found, using " << Ecal_plugLength << endl; + } + + //--------------------------------- + + // set up the helper, which will make a module for us + + SEcal05_Helpers helper; + helper.setDet( & x_det ); + + // absorber layer structure + helper.setPreshower( Ecal_Barrel_PreshowerLayer ); + + cout << "SEcal05_Barrel: Preshower ? " << Ecal_Barrel_PreshowerLayer << endl; + + helper.setAbsLayers( Ecal_nlayers1, Ecal_radiator_thickness1, + Ecal_nlayers2, Ecal_radiator_thickness2, + Ecal_nlayers3, Ecal_radiator_thickness3 ); + + cout << "SEcal05_Barrel: absorber layers " << + Ecal_nlayers1 << "*" << Ecal_radiator_thickness1/dd4hep::mm << " mm + " << + Ecal_nlayers2 << "*" << Ecal_radiator_thickness2/dd4hep::mm << " mm + " << + Ecal_nlayers3 << "*" << Ecal_radiator_thickness3/dd4hep::mm << " mm " << endl; + + helper.checkLayerConsistency(); + + // structural CF thicknesses + helper.setCFthickness( Ecal_fiber_thickness_structure, // was N_FIBERS_W_STRUCTURE*Ecal_fiber_thickness, : updated DJ + Ecal_fiber_thickness_alveolus, // N_FIBERS_ALVEOLUS*Ecal_fiber_thickness, + Ecal_front_face_thickness, + Ecal_support_thickness); + + helper.setPlugLength( Ecal_plugLength ); + + // check resulting thickness is consistent with what's in compact description + float module_thickness = helper.getTotalThickness(); + if ( fabs( Ecal_barrel_thickness - module_thickness ) > 0.01 ) { + cout << "ERROR SEcal05_Barrel : ECAL barrel thickness in comapct decription not consistent with what I calculate!" << endl; + cout << " calculated = " << module_thickness << " compact description: " << Ecal_barrel_thickness << endl; + assert( 0 ); // exit + } + + cout << "SEcal05_Barrel : module thickness = " << module_thickness << endl; + + // set up the sensitive layer segmentation + + Readout readout = sens.readout(); + Segmentation seg = readout.segmentation(); + helper.setSegmentation( &seg ); + + helper.setNCells( Ecal_cells_across_megatile, Ecal_strips_across_megatile, Ecal_strips_along_megatile); + helper.setMagicMegatileStrategy ( Ecal_end_of_slab_strategy ); + + // layer configuration + int ntemp; + stringstream stream(Ecal_layerConfig); + std::vector < int > layerConfig; + while ( stream >> ntemp ) { + assert (ntemp>=0 ); + layerConfig.push_back( ntemp ); + } + cout << "SEcal05_Barrel : layer config: "; + for (size_t i=0; i<layerConfig.size(); i++) cout << layerConfig[i] << " "; + cout << endl; + + helper.setLayerConfig( layerConfig ); + + + // set the alveolar structure + + // get width of alveolus + double Ecal_Barrel_module_dim_z = 2 * Ecal_Barrel_halfZ / Ecal_barrel_z_modules ; + double alv_width = ( Ecal_Barrel_module_dim_z - 2.*Ecal_lateral_face_thickness ) / Ecal_barrel_number_of_towers; + + cout << "SEcal05_Barrel : width of module, alveolus = " << Ecal_Barrel_module_dim_z << " " << alv_width << endl; + + std::vector < int > ntowers; ntowers.push_back(Ecal_barrel_number_of_towers); + + helper.setTowersUnits( ntowers, + alv_width, + Ecal_n_wafers_per_tower, + Ecal_lateral_face_thickness, + Ecal_fiber_thickness_alveolus + Ecal_Slab_H_fiber_thickness + Ecal_Slab_shielding, + Ecal_guard_ring_size ); + + // shape of barrel module + // to get alignment as in ECAL interface design doc fig10 + double module_thickness_noSupport = module_thickness - Ecal_support_thickness; + + // double max_dim_x = 2. * tan(M_PI/8.) * Ecal_inner_radius + module_thickness_noSupport/sin(M_PI/4.); // longest side assumes oct + // double min_dim_x = max_dim_x - 2*module_thickness; // shorter one (assumes octagon) + + // double max_dim_x = 2. * tan(M_PI/nsides) * Ecal_inner_radius + module_thickness_noSupport / tan( 2*M_PI/nsides ); // longest side + double max_dim_x = 2. * tan(M_PI/nsides) * Ecal_inner_radius + module_thickness_noSupport / sin( 2*M_PI/nsides ); // longest side : fix DJeans 03/2017 + double min_dim_x = max_dim_x - 2*module_thickness/tan( 2*M_PI/nsides ) ; // shorter one + + if ( min_dim_x<0 || max_dim_x<0 ) { + std::cout << "SEcal05_Barrel ERROR : requesting too many sides! barrel modules have max/min extent " << max_dim_x << " " << min_dim_x << std::endl; + std::cout << "exiting" << std::endl; + assert(0); // exit gracefully + } + + Trapezoid trd(max_dim_x / 2, + min_dim_x / 2, + Ecal_Barrel_module_dim_z / 2, + Ecal_Barrel_module_dim_z / 2, + module_thickness/2); + + Volume mod_vol(det_name+"_module", trd, carbon_fibre); // DJeans 5-sep-2016 + + //========== fill data for reconstruction ============================ + LayeredCalorimeterData* caloData = new LayeredCalorimeterData ; + + helper.setModuleDimensions( 0, // int XYtype, // module shape in XY + 1, // int XZtype, // module shape in XZ + max_dim_x, // double dX_max, // maximum extent in X + -999, // dummy + 2.*M_PI/nsides + ); + + // traslation to get layers within the envelope + helper.setTranslation ( Position ( -max_dim_x/2. , -Ecal_Barrel_module_dim_z/2., -module_thickness/2. ) ); + + // make the module + + helper.makeModule( mod_vol, stave_det, + *caloData, + theDetector, sens ); + + for (size_t i=0; i<caloData->layers.size(); i++) { + caloData->layers[i].distance += Ecal_inner_radius; // add IP->front face distance + } + + // cout << "SEcal05_Barrel : cell sizes: " << endl; + // for (size_t i=0; i<caloData->layers.size(); i++) { + // cout << "sensitive layer " << i << " : x,y = " << caloData->layers[i].cellSize0 << " " << caloData->layers[i].cellSize1 << endl; + // } + + // ------------- create extension objects for reconstruction ----------------- + + caloData->layoutType = LayeredCalorimeterData::BarrelLayout ; + caloData->inner_symmetry = nsides ; + caloData->outer_symmetry = nsides ; + caloData->phi0 = 0 ; // hardcoded + + // extent of the calorimeter in the r-z-plane [ rmin, rmax, zmin, zmax ] in mm. + caloData->extent[0] = Ecal_inner_radius ; + caloData->extent[1] = ( Ecal_inner_radius + module_thickness ); + caloData->extent[2] = 0. ; + caloData->extent[3] = Ecal_Barrel_halfZ ; + //------------------------------------------------------- + + //==================================================================== + // Place ECAL Barrel stave module into the envelope volume + //==================================================================== + + double X = Ecal_inner_radius + module_thickness / 2.; + + // altered to get alignment as in ECAL interface design doc fig10 + // end of slabs aligned to inner face of support plate in next stave (not the outer surface) + // double Y = (module_thickness/2.) / sin(M_PI/4.); + double Y = (module_thickness_noSupport/2.) / sin(2.*M_PI/nsides); + + // stave numbering from 1->8 + // stave = 1 is in +ve x direction + // stave = 3 is in +ve y direction (ie at top of barrel) + // module index from 1->5 + // module = 1 is at -ve z + + for (int istave = 0; istave < nsides ; istave++) { + //for (int istave = 0; istave < 1 ; istave++) { + int stave_id = istave+1; + + // dstave is the change in stave index from the top module to the one with smallest positive phi (which has istave=0) + int dstave = int( nsides/4. ); + + + // rotations around the center to get the modules in the correct orientation + // top module (dstave) shits by hphi + pi/4 + double phirot = hphi + M_PI/2.; // + phirot += (istave - dstave)*dphi; + + + // this angle is used to get the stave in the right position wrt the origin + double phirot2 = (istave - dstave ) * dphi + hphi; + + for (int imodule = 0; imodule < Ecal_barrel_z_modules; imodule++) { + int module_id = imodule+1; + Transform3D tr( RotationZYX( 0 , phirot, M_PI/2.), // magic rotation! + Translation3D( + ( X*cos(phirot2)-Y*sin(phirot2) ) , + ( X*sin(phirot2)+Y*cos(phirot2) ) , + ( imodule+0.5 )*Ecal_Barrel_module_dim_z - Ecal_Barrel_halfZ ) + ); + PlacedVolume pv = envelope.placeVolume(mod_vol,tr); + pv.addPhysVolID("module",module_id); + pv.addPhysVolID("stave",stave_id); + + DetElement sd = (imodule==0 && istave==0) ? stave_det : stave_det.clone(_toString(module_id,"module%d")+_toString(stave_id,"stave%d")); + sd.setPlacement(pv); + sdet.add(sd); + } + } + + // Set envelope volume attributes. + envelope.setAttributes(theDetector,x_det.regionStr(),x_det.limitsStr(),x_det.visStr()); + + sdet.addExtension< LayeredCalorimeterData >( caloData ) ; + + // cout << "finished SEcal05_Barrel" << endl; + + return sdet; +} + +DECLARE_DETELEMENT(SEcal05_Barrel,create_detector) diff --git a/Detector/DetCEPCv4/src/calorimeter/SEcal05_ECRing.cpp b/Detector/DetCEPCv4/src/calorimeter/SEcal05_ECRing.cpp new file mode 100644 index 00000000..9499c1fa --- /dev/null +++ b/Detector/DetCEPCv4/src/calorimeter/SEcal05_ECRing.cpp @@ -0,0 +1,689 @@ +//==================================================================== +// lcgeo - LC detector models in DD4hep +//-------------------------------------------------------------------- +// DD4hep Geometry driver for SiWEcalEndcaps +// Ported from Mokka +//-------------------------------------------------------------------- +// S.Lu, DESY +// $Id$ +//==================================================================== + +/* History: + +// ******************************************************* +// * * +// * Mokka * +// * - the detailed geant4 simulation for ILC - * +// * * +// * For more information about Mokka, visit the * +// * * +// * Mokka.in2p3.fr Mokka home page. * +// * * +// ******************************************************* +// +// $Id$ +// $Name: mokka-07-00 $ +// +// +// +// SEcal04.cc +// +Shaojun Lu: Ported from Mokka SEcal04 Endcaps part. Read the constants from XML +instead of the DB. Then build the Endcap in the same way with DD4hep +construct. +Inside SEcal04, some parameters, which used by Ecal Endcaps, come from +Ecal Barrel. They can be seen here again. +Start ECRing ... +*/ + + +/* + + ============== + SEcal05_ECRing + =============== + SEcal04_ECRing driver originally assumend 2 identical modules in +/- z + this is not true, because lumical (and therefore hole in EC plug) is not centred, but offset in +z position + + SEcal05_ECRing driver corrects this (applies offset according to the crossing algle, to center it on the outgoing beampipe). + it also deals with the presence or not of a preshower layer + + this is a hack of the 04 version, not a clean rewrite as was done for barrel and endcap, so it's not very clean & tidy....but I think it works + + DJeans jan 2017 + + ======== + + + - fixed dd4hep::rec::LayeredCalorimeterData for case with no preshower + - small fixes to layer thicknesses (now take from compact description) to get exactly same structure as main endcaps + + DJeans July 2017 + + + ====== + + - fixes to LayeredCalorimeterData: "distance" defined to start of layer, some other issues + - fix to use correct absorber thickness at transition between stacks + - LayeredCalorimeterData material describtion: overall structure made in CF (previously air) + + DJeans Sep 2017 + +*/ + + +#include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/DetType.h" +#include "XML/Layering.h" +#include "XML/Utilities.h" +#include "DDRec/DetectorData.h" + +using namespace std; + +using dd4hep::BUILD_ENVELOPE; +using dd4hep::Box; +using dd4hep::DetElement; +using dd4hep::Detector; +using dd4hep::Layering; +using dd4hep::Material; +using dd4hep::PlacedVolume; +using dd4hep::Position; +using dd4hep::Readout; +using dd4hep::Ref_t; +using dd4hep::Rotation3D; +using dd4hep::RotationZYX; +using dd4hep::Segmentation; +using dd4hep::SensitiveDetector; +using dd4hep::SubtractionSolid; +using dd4hep::Transform3D; +using dd4hep::Tube; +using dd4hep::Volume; +using dd4hep::_toString; + +using dd4hep::rec::LayeredCalorimeterData; + +//#define VERBOSE 1 + +// workaround for DD4hep v00-14 (and older) +#ifndef DD4HEP_VERSION_GE +#define DD4HEP_VERSION_GE(a,b) 0 +#endif + +static Ref_t create_detector(Detector& theDetector, xml_h element, SensitiveDetector sens) { + static double tolerance = 0e0; + + cout << "---------------------------------" << endl; + cout << " creating Ecal ECRing ( SEcal05_ECRing ) " << endl; + cout << "---------------------------------" << endl; + + xml_det_t x_det = element; + string det_name = x_det.nameStr(); + Layering layering (element); + + Material air = theDetector.air(); + Material CF = theDetector.material("CarbonFiber"); + + int det_id = x_det.id(); + xml_comp_t x_staves = x_det.staves(); + DetElement sdet (det_name,det_id); + + // --- create an envelope volume and position it into the world --------------------- + + Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector, element , sdet ) ; + + dd4hep::xml::setDetectorTypeFlag( element, sdet ) ; + + if( theDetector.buildType() == BUILD_ENVELOPE ) return sdet ; + + //----------------------------------------------------------------------------------- + + sens.setType("calorimeter"); + + Material stave_material = theDetector.material(x_staves.materialStr()); + + Readout readout = sens.readout(); + Segmentation seg = readout.segmentation(); + + std::vector<double> cellSizeVector = seg.segmentation()->cellDimensions(0); //Assume uniform cell sizes, provide dummy cellID + double cell_sizeX = cellSizeVector[0]; + double cell_sizeY = cellSizeVector[1]; + + //==================================================================== + // + // Read all the constant from ILD_o1_v05.xml + // Use them to build Ecal ECRing + // + //==================================================================== + + // read parametere from compact.xml file + double Ecal_fiber_thickness_alveolus = theDetector.constant<double>("Ecal_fiber_thickness_alveolus"); + double Ecal_fiber_thickness_structure = theDetector.constant<double>("Ecal_fiber_thickness_structure"); + + double Ecal_radiator_thickness1 = theDetector.constant<double>("Ecal_radiator_layers_set1_thickness"); + double Ecal_radiator_thickness2 = theDetector.constant<double>("Ecal_radiator_layers_set2_thickness"); + double Ecal_radiator_thickness3 = theDetector.constant<double>("Ecal_radiator_layers_set3_thickness"); + double Ecal_Barrel_halfZ = theDetector.constant<double>("Ecal_Barrel_halfZ"); + + double Ecal_support_thickness = theDetector.constant<double>("Ecal_support_thickness"); + double Ecal_front_face_thickness = theDetector.constant<double>("Ecal_front_face_thickness"); + double Ecal_lateral_face_thickness = theDetector.constant<double>("Ecal_lateral_face_thickness"); + + double Ecal_EC_Ring_gap = theDetector.constant<double>("Ecal_EC_Ring_gap"); + + double Ecal_cables_gap = theDetector.constant<double>("Ecal_cables_gap"); + double Lcal_outer_radius = theDetector.constant<double>("Lcal_outer_radius"); + double Ecal_Lcal_ring_gap = theDetector.constant<double>("Ecal_Lcal_ring_gap"); + double Ecal_endcap_center_box_size = theDetector.constant<double>("Ecal_endcap_center_box_size"); + + int Ecal_nlayers1 = theDetector.constant<int>("Ecal_nlayers1"); + int Ecal_nlayers2 = theDetector.constant<int>("Ecal_nlayers2"); + int Ecal_nlayers3 = theDetector.constant<int>("Ecal_nlayers3"); + + double EcalEndcapRing_inner_radius = theDetector.constant<double>("EcalEndcapRing_inner_radius"); + double EcalEndcapRing_outer_radius = theDetector.constant<double>("EcalEndcapRing_outer_radius"); + double EcalEndcapRing_min_z = theDetector.constant<double>("EcalEndcapRing_min_z"); + double EcalEndcapRing_max_z = theDetector.constant<double>("EcalEndcapRing_max_z"); + + + double crossing_angle = theDetector.constant<double>("CepC_Main_Crossing_Angle"); + bool Ecal_Barrel_PreshowerLayer = theDetector.constant<int>("Ecal_Barrel_Preshower") > 0; + + double Ecal_barrel_thickness = theDetector.constant<double>("Ecal_barrel_thickness"); + + //========== fill data for reconstruction ============================ + dd4hep::rec::LayeredCalorimeterData* caloData = new dd4hep::rec::LayeredCalorimeterData ; + caloData->layoutType = dd4hep::rec::LayeredCalorimeterData::EndcapLayout ; + caloData->inner_symmetry = 0 ; // hard code cernter pipe hole + caloData->outer_symmetry = 4 ; // outer box + caloData->phi0 = 0 ; + + /// extent of the calorimeter in the r-z-plane [ rmin, rmax, zmin, zmax ] in mm. + caloData->extent[0] = EcalEndcapRing_inner_radius ; + caloData->extent[1] = EcalEndcapRing_outer_radius ; + caloData->extent[2] = EcalEndcapRing_min_z ; + caloData->extent[3] = EcalEndcapRing_max_z ; + + + //==================================================================== + // + // general calculated parameters + // + //==================================================================== + + int Number_of_Si_Layers_in_Barrel = 0; + +#ifdef VERBOSE + std::cout << " Ecal total number of Silicon layers = " << Number_of_Si_Layers_in_Barrel << std::endl; +#endif + + int n_total_layers = Ecal_nlayers1 + Ecal_nlayers2 + Ecal_nlayers3; + Number_of_Si_Layers_in_Barrel = n_total_layers; // take account of preshower on/off (djeans) + if ( Ecal_Barrel_PreshowerLayer ) Number_of_Si_Layers_in_Barrel++; + + // calculate total thickness of ECAL + // updated to take info from xml description - djeans: 12 July 2017 + double module_thickness = Ecal_support_thickness + Ecal_front_face_thickness;// front and back supports + + // the absorber in the structure + for (int i=0; i<Ecal_nlayers1+Ecal_nlayers2+Ecal_nlayers3; i++) { + bool inStructure = Ecal_Barrel_PreshowerLayer==1 ? i%2==1 : i%2==0 ; + if ( inStructure ) { + double thickness (Ecal_radiator_thickness1); + if ( i>=Ecal_nlayers1 ) thickness = Ecal_radiator_thickness2; + if ( i>=Ecal_nlayers1+Ecal_nlayers2 ) thickness = Ecal_radiator_thickness3; + module_thickness += thickness + 2*Ecal_fiber_thickness_structure; // the absorber and its wrapping + } + } + + // the slabs + int l_num2(0); + for(xml_coll_t li(x_det,_U(layer)); li; ++li) { // types of layers (i.e. thin/thick absorber) or "stack" + xml_comp_t x_layer = li; + // Loop over number of repeats for this layer. + for (int j=0; j< x_layer.repeat(); j++) { // layers within this type (or "stack") + float thisthick = layering.layer(l_num2)->thickness(); + module_thickness+=thisthick + 2*Ecal_fiber_thickness_alveolus; // slab thickness, and the alveolar wall around it + l_num2++; + } + } + + + if ( fabs( Ecal_barrel_thickness - module_thickness) > 0.1 ) { + cout << "ERROR EC ecal thickness inconsistency: calculated, nominal = " << module_thickness << " " << Ecal_barrel_thickness << endl; + assert(0); + } + +#ifdef VERBOSE + std::cout << " module_thickness = " << module_thickness << std::endl; +#endif + + + double ECRingSiplateSize = Ecal_endcap_center_box_size + - 2 * Ecal_EC_Ring_gap + - 2 * Ecal_lateral_face_thickness; + + + // central hole is not centred on detector axis, but on outgoing beampipe (as is the lumical) + double hole_x_offset = tan(crossing_angle/2.) * (EcalEndcapRing_min_z + EcalEndcapRing_max_z)/2.; + + + // ========= Create Ecal end cap ring ==================================== + // It will be the volume for palcing the Ecal endcaps alveolus(i.e. Layers). + // And the structure W plate. + // Itself will be placed into the world volume. + // ========================================================================== + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + //~ EndcapRing ~ + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + for(int module_num=0;module_num<2;module_num++) { // move module loop to here (modules not identical) + + DetElement module_det(_toString(module_num,"module%d"),det_id); + + double thismodule_hole_dx = hole_x_offset; + if ( module_num==0 ) thismodule_hole_dx*=-1; + Position hole_position(thismodule_hole_dx, 0, 0); + + double Ecal_endcap_Tube_rmax = Lcal_outer_radius + Ecal_Lcal_ring_gap; + + Tube CenterECTub(0., Ecal_endcap_Tube_rmax, module_thickness); + Box CenterECBox((Ecal_endcap_center_box_size/2. - Ecal_EC_Ring_gap), + (Ecal_endcap_center_box_size/2. - Ecal_EC_Ring_gap), + module_thickness/2.); + + SubtractionSolid ECRingSolid( CenterECBox, CenterECTub, hole_position); + + // Volume EnvLogECRing("ECRing",ECRingSolid,theDetector.material("g10")); + Volume EnvLogECRing("ECRing",ECRingSolid,theDetector.material("CarbonFiber")); // DJeans 5-sep-2016 + + //============================================================== + // build the layer and place into the ECRing + //============================================================== + + + //------------------------------------------------------- + // Radiator and towers placements inside the ECRing module + //------------------------------------------------------- + + // We count the layers starting from IP and from 1, + // so odd layers should be inside slabs and + // even ones on the structure. + // + + + double distance_ip_to_front_face = Ecal_Barrel_halfZ + Ecal_cables_gap; + double dist_from_front_face_tally(0); + + double z_floor = - module_thickness/2; + + double l_pos_z = z_floor; + + dd4hep::rec::LayeredCalorimeterData::Layer caloLayer ; + caloLayer.cellSize0 = cell_sizeX; + caloLayer.cellSize1 = cell_sizeY; + + //-------------------- start loop over ECAL layers ---------------------- + // Loop over the sets of layer elements in the detector. + double radiator_dim_y = Ecal_radiator_thickness1; //to be updated with slice radiator thickness + + double nRadiationLengths=0.; + double nInteractionLengths=0.; + double thickness_sum=0; + + int l_num = 1; + bool isFirstSens = true; + int myLayerNum = 0 ; + bool isFrontFace=true; + int absorber_index(0); + + for(xml_coll_t li(x_det,_U(layer)); li; ++li) { // types of layer + xml_comp_t x_layer = li; + int repeat = x_layer.repeat(); + + // Loop over number of repeats for this layer. + for (int j=0; j<repeat; j++) { + + // move structural layer to before the slabs - djeans + + // deal with preshower or lack thereof ------------------ + double radiator_dim_Z(0); + //double rad_pos_Z(0); + double this_struct_CFthick_beforeAbs(0); + double this_struct_CFthick_afterAbs(0); + double _CF_absWrap = Ecal_fiber_thickness_structure; + double _CF_alvWall = Ecal_fiber_thickness_alveolus; + if ( isFrontFace ) { // the first part of the module depends on whether we have preshwer or not + if ( Ecal_Barrel_PreshowerLayer==1 ) { // don't include W+CF wrapping; only one side of alveolus + this_struct_CFthick_beforeAbs = 0; + this_struct_CFthick_afterAbs = Ecal_front_face_thickness + _CF_alvWall; + radiator_dim_Z = 0; + } else { // include W+CF wrapping; only one side of alveolus + this_struct_CFthick_beforeAbs = Ecal_front_face_thickness + _CF_absWrap; + this_struct_CFthick_afterAbs = _CF_absWrap + _CF_alvWall; + radiator_dim_Z = Ecal_radiator_thickness1; // this line implicitly assumes Ecal_nlayers1>0... + //rad_pos_Z = radiator_dim_Z/2. + this_struct_CFthick_beforeAbs; // distance from top surface of structure to centre of radiator + absorber_index++; + } + + thickness_sum += radiator_dim_Z; + nRadiationLengths += radiator_dim_Z/stave_material.radLength(); + nInteractionLengths += radiator_dim_Z/stave_material.intLength(); + + thickness_sum += ( this_struct_CFthick_beforeAbs + this_struct_CFthick_afterAbs ); + nRadiationLengths += ( this_struct_CFthick_beforeAbs + this_struct_CFthick_afterAbs ) / CF.radLength(); + nInteractionLengths += ( this_struct_CFthick_beforeAbs + this_struct_CFthick_afterAbs ) / CF.intLength(); + + isFrontFace=false; + } else { // internal layer: include W+CF wrapping; both sides of alveolus + this_struct_CFthick_beforeAbs = _CF_alvWall+_CF_absWrap; + this_struct_CFthick_afterAbs = _CF_alvWall+_CF_absWrap; + + if ( absorber_index<Ecal_nlayers1 ) radiator_dim_Z=Ecal_radiator_thickness1; + else if ( absorber_index<Ecal_nlayers1+Ecal_nlayers2 ) radiator_dim_Z=Ecal_radiator_thickness2; + else if ( absorber_index<Ecal_nlayers1+Ecal_nlayers2+Ecal_nlayers3 ) radiator_dim_Z=Ecal_radiator_thickness3; + else { + assert(0 && "ERROR cannot determine absorber thickness for layer "); + } + + absorber_index++; + + assert( radiator_dim_Z>0 && "no radiator!" ); + } + //------------------------------- + + radiator_dim_y = radiator_dim_Z; // ahh different axis conventions.... + + l_pos_z += this_struct_CFthick_beforeAbs + radiator_dim_y/2; + + // ######################### + // BuildECRingStructureLayer + // ######################### + + Tube CenterECTubForSi(0., + Lcal_outer_radius + Ecal_Lcal_ring_gap + 0.001, // + tolerance, + module_thickness, + 0., + 2 * M_PI); + + + string l_name = _toString(l_num,"layer%d"); + RotationZYX rot(0,0,0); // a null rotation + + if ( radiator_dim_y>0 ) { + + string bs_name="bs"; + Box EndcapStructureLayer_box(ECRingSiplateSize/ 2. - tolerance, ECRingSiplateSize/ 2. - tolerance, radiator_dim_y/2.); + SubtractionSolid EndcapStructureLayerSolid( EndcapStructureLayer_box, CenterECTubForSi, hole_position); + Volume EndcapStructureLayer_vol(det_name+"_"+l_name+"_"+bs_name,EndcapStructureLayerSolid,theDetector.material(x_staves.materialStr())); + EndcapStructureLayer_vol.setVisAttributes(theDetector.visAttributes( x_staves.visStr())); + + // this is where we place the tungsten into the envelope + double bsl_pos_z = l_pos_z; + Position bsl_pos(0,0,bsl_pos_z); + Transform3D bsl_tran3D(rot,bsl_pos); + EnvLogECRing.placeVolume(EndcapStructureLayer_vol,bsl_tran3D); + } + + // Increment to next layer Z position. + l_pos_z += radiator_dim_y/2 + this_struct_CFthick_afterAbs; + + // now move to the alveolus and what's inside + double l_thickness = layering.layer(l_num-1)->thickness(); // Layer's thickness. this is the internal thickness of the alveolus + + l_pos_z += l_thickness/2.; // add in half the alveolus thickness + + int EC_Number_of_towers = 0; + + // We use the same method able to create the Barrel + // Slabs, so we have to rotate it later when placing + // into the EC modules. + // + // We use the same method able to create the Barrel + // radiator plates between slabs, but with the good + // dimensions to avoid to rotate it later when placing + // into the EC modules. + + // While the towers have the same shape use the same + // logical volumes and parameters. + + string tower_name = _toString(EC_Number_of_towers,"tower%d"); + + // build the ECRing layer, a box with hole in the middle + Box ECRingSiBox( ECRingSiplateSize/ 2. - tolerance, ECRingSiplateSize/ 2. - tolerance, l_thickness/2.0-tolerance); + + SubtractionSolid ECRingSiSolid( ECRingSiBox, CenterECTubForSi, hole_position); + + Volume l_vol(det_name+"_"+l_name+"_"+tower_name,ECRingSiSolid,air); + DetElement layer(module_det, l_name+tower_name, det_id); + + l_vol.setVisAttributes(theDetector.visAttributes( x_layer.visStr() )); + + // Loop over the sublayers or slices for this layer. + int s_num = 1; + double s_pos_z = -(l_thickness / 2); + + //-------------------------------------------------------------------------------- + // BuildECRing keep the Alveolus structure, the same as the Barrel and Endcap + //-------------------------------------------------------------------------------- + + for(xml_coll_t si(x_layer,_U(slice)); si; ++si) { + xml_comp_t x_slice = si; + string s_name = _toString(s_num,"slice%d"); + double s_thick = x_slice.thickness(); + Material slice_material = theDetector.material(x_slice.materialStr()); + + double slab_dim_x = ECRingSiplateSize/2.-tolerance; + double slab_dim_y = s_thick/2.-tolerance; + double slab_dim_z = ECRingSiplateSize/2.-tolerance; + + Box s_box(slab_dim_x,slab_dim_z,slab_dim_y); + + SubtractionSolid ECRingSiSliceSolid( s_box, CenterECTubForSi, hole_position); + + Volume s_vol(det_name+"_"+l_name+"_"+s_name,ECRingSiSliceSolid,slice_material); + DetElement slice(layer,s_name,det_id); + + s_vol.setVisAttributes(theDetector.visAttributes(x_slice.visStr())); + +#ifdef VERBOSE + std::cout<<"x_slice.materialStr(): "<< x_slice.materialStr() <<std::endl; +#endif + if (x_slice.materialStr().compare(x_staves.materialStr()) == 0){ + radiator_dim_y = s_thick; + + absorber_index++; + +#if DD4HEP_VERSION_GE( 0, 15 ) + caloLayer.outer_nRadiationLengths = nRadiationLengths; + caloLayer.outer_nInteractionLengths = nInteractionLengths; + caloLayer.outer_thickness = thickness_sum; + caloLayer.distance = distance_ip_to_front_face + dist_from_front_face_tally; +#endif + if (Ecal_Barrel_PreshowerLayer==0 || !isFirstSens ){ // add this layer to the caloData (unless its the first absorber layer of a no-preshower calo) + if ( module_num==0 ) { + caloData->layers.push_back( caloLayer ) ; +#ifdef VERBOSE +#if DD4HEP_VERSION_GE( 0, 15 ) + std::cout<<" caloLayer.distance: "<< caloLayer.distance <<std::endl; + std::cout<<" caloLayer.inner_nRadiationLengths: "<< caloLayer.inner_nRadiationLengths <<std::endl; + std::cout<<" caloLayer.inner_nInteractionLengths: "<< caloLayer.inner_nInteractionLengths <<std::endl; + std::cout<<" caloLayer.inner_thickness: "<< caloLayer.inner_thickness <<std::endl; + std::cout<<" caloLayer.sensitive_thickness: "<< caloLayer.sensitive_thickness <<std::endl; + std::cout<<" caloLayer.cellSize0, 1: " << caloLayer.cellSize0 << " " << caloLayer.cellSize1 << std::endl; + std::cout<<" caloLayer.outer_nRadiationLengths: "<< caloLayer.outer_nRadiationLengths <<std::endl; + std::cout<<" caloLayer.outer_nInteractionLengths: "<< caloLayer.outer_nInteractionLengths <<std::endl; + std::cout<<" caloLayer.outer_thickness: "<< caloLayer.outer_thickness <<std::endl; + std::cout<<" EcalECRing[1]==>caloLayer.inner_thickness + caloLayer.outer_thickness: " + << caloLayer.inner_thickness + caloLayer.outer_thickness <<std::endl; +#endif +#endif + dist_from_front_face_tally += caloLayer.inner_thickness+caloLayer.outer_thickness; + } + } + + nRadiationLengths = 0. ; + nInteractionLengths = 0. ; + thickness_sum = 0. ; + isFirstSens = false; + } // if radiator + + nRadiationLengths += s_thick/(2.*slice_material.radLength()); + nInteractionLengths += s_thick/(2.*slice_material.intLength()); + thickness_sum += s_thick/2; + + if ( x_slice.isSensitive() ) { + s_vol.setSensitiveDetector(sens); + +#if DD4HEP_VERSION_GE( 0, 15 ) + //Store "inner" quantities + caloLayer.inner_nRadiationLengths = nRadiationLengths; + caloLayer.inner_nInteractionLengths = nInteractionLengths; + caloLayer.inner_thickness = thickness_sum; + //Store sensitive slice thickness + caloLayer.sensitive_thickness = s_thick; +#ifdef VERBOSE + std::cout<<" l_num: "<<l_num <<std::endl; + std::cout<<" s_num: "<<s_num <<std::endl; + std::cout<<" module_thickness: "<< module_thickness <<std::endl; + std::cout<<" l_pos_z: "<< l_pos_z <<std::endl; + std::cout<<" l_thickness: "<< l_thickness <<std::endl; + std::cout<<" s_pos_z: "<< s_pos_z <<std::endl; + std::cout<<" s_thick: "<< s_thick <<std::endl; + std::cout<<" radiator_dim_y: "<< radiator_dim_y <<std::endl; +#endif + caloLayer.absorberThickness = radiator_dim_y ; +#endif + nRadiationLengths = 0. ; + nInteractionLengths = 0. ; + thickness_sum = 0. ; + } // if sensitive + + nRadiationLengths += s_thick/(2.*slice_material.radLength()); + nInteractionLengths += s_thick/(2.*slice_material.intLength()); + thickness_sum += s_thick/2; + + slice.setAttributes(theDetector,s_vol,x_slice.regionStr(),x_slice.limitsStr(),x_slice.visStr()); + + // Slice placement. + PlacedVolume slice_phv = l_vol.placeVolume(s_vol,Position(0,0,s_pos_z+s_thick/2)); + + if ( x_slice.isSensitive() ) { + slice_phv.addPhysVolID("layer", myLayerNum++); + } + + slice.setPlacement(slice_phv); + // Increment Z position of slice. + s_pos_z += s_thick; + + // Increment slice number. + ++s_num; + } + + // add material between the "slabs" + + // we need to use correct thickness here! especially at the interface between stacks. + if ( absorber_index < Ecal_nlayers1 ) radiator_dim_y=Ecal_radiator_thickness1; + else if ( absorber_index < Ecal_nlayers1+Ecal_nlayers2 ) radiator_dim_y=Ecal_radiator_thickness2; + else if ( absorber_index < Ecal_nlayers1+Ecal_nlayers2+Ecal_nlayers3 ) radiator_dim_y=Ecal_radiator_thickness3; + else { + radiator_dim_y=0; + } + + // deal with final support plate + float cf_thick = Ecal_fiber_thickness_alveolus; + if ( absorber_index<Ecal_nlayers1+Ecal_nlayers2+Ecal_nlayers3 ) { + cf_thick += Ecal_fiber_thickness_structure; + } else { + cf_thick += Ecal_support_thickness; + } + + if ( module_num==0 ) { +#if DD4HEP_VERSION_GE( 0, 15 ) + caloLayer.distance = distance_ip_to_front_face + dist_from_front_face_tally; + caloLayer.outer_nRadiationLengths = nRadiationLengths + cf_thick/CF.radLength(); + caloLayer.outer_nInteractionLengths = nInteractionLengths + cf_thick/CF.intLength(); + caloLayer.outer_thickness = thickness_sum + cf_thick; +#endif + caloData->layers.push_back( caloLayer ) ; + +#ifdef VERBOSE +#if DD4HEP_VERSION_GE( 0, 15 ) + std::cout<<" caloLayer.distance: "<< caloLayer.distance <<std::endl; + std::cout<<" caloLayer.inner_nRadiationLengths: "<< caloLayer.inner_nRadiationLengths <<std::endl; + std::cout<<" caloLayer.inner_nInteractionLengths: "<< caloLayer.inner_nInteractionLengths <<std::endl; + std::cout<<" caloLayer.inner_thickness: "<< caloLayer.inner_thickness <<std::endl; + std::cout<<" caloLayer.sensitive_thickness: "<< caloLayer.sensitive_thickness <<std::endl; + std::cout<<" caloLayer.cellSize0, 1: " << caloLayer.cellSize0 << " " << caloLayer.cellSize1 << std::endl; + std::cout<<" caloLayer.outer_nRadiationLengths: "<< caloLayer.outer_nRadiationLengths <<std::endl; + std::cout<<" caloLayer.outer_nInteractionLengths: "<< caloLayer.outer_nInteractionLengths <<std::endl; + std::cout<<" caloLayer.outer_thickness: "<< caloLayer.outer_thickness <<std::endl; + + std::cout<<" EcalECRing[2]==>caloLayer.inner_thickness + caloLayer.outer_thickness: " + << caloLayer.inner_thickness + caloLayer.outer_thickness <<std::endl; +#endif +#endif + dist_from_front_face_tally += caloLayer.inner_thickness+caloLayer.outer_thickness; + } + + nRadiationLengths = radiator_dim_y/stave_material.radLength() + cf_thick/CF.radLength(); + nInteractionLengths = radiator_dim_y/stave_material.intLength() + cf_thick/CF.intLength(); + thickness_sum = radiator_dim_y + cf_thick; + + // this is where we place the slab (l_vol) into the envelope + int i_stave = 1; + int i_tower = 1; + Position l_pos(0,0,l_pos_z); + Transform3D tran3D(rot,l_pos); + PlacedVolume layer_phv = EnvLogECRing.placeVolume(l_vol,tran3D); + layer_phv.addPhysVolID("tower", i_tower); + layer_phv.addPhysVolID("stave", i_stave); + layer.setPlacement(layer_phv); + + l_pos_z += l_thickness/2.; // add in the other half of the alveolus + + ++l_num; + + } + } + + + // Set stave visualization. + if (x_staves) { + EnvLogECRing.setVisAttributes(theDetector.visAttributes(x_staves.visStr())); + } + + + //==================================================================== + // Place Ecal Endcap module into the assembly envelope volume + //==================================================================== + + double EC_module_z_offset = (EcalEndcapRing_min_z + EcalEndcapRing_max_z)/2.; + + int module_id = ( module_num == 0 ) ? 0:6; + double this_module_z_offset = ( module_id == 0 ) ? - EC_module_z_offset : EC_module_z_offset; + double this_module_rotY = ( module_id == 0 ) ? M_PI:0; + + Position xyzVec(0,0,this_module_z_offset); + RotationZYX rot(0,this_module_rotY,0); + Rotation3D rot3D(rot); + Transform3D tran3D(rot3D,xyzVec); + + PlacedVolume pv = envelope.placeVolume(EnvLogECRing,tran3D); + pv.addPhysVolID("module",module_id); // z: -/+ 0/6 + + DetElement sd = module_det; + sd.setPlacement(pv); + + } + + sdet.addExtension< dd4hep::rec::LayeredCalorimeterData >( caloData ) ; + + return sdet; + +} + + + +DECLARE_DETELEMENT(SEcal05_ECRing, create_detector) + diff --git a/Detector/DetCEPCv4/src/calorimeter/SEcal05_Endcaps.cpp b/Detector/DetCEPCv4/src/calorimeter/SEcal05_Endcaps.cpp new file mode 100644 index 00000000..a0f4c69a --- /dev/null +++ b/Detector/DetCEPCv4/src/calorimeter/SEcal05_Endcaps.cpp @@ -0,0 +1,390 @@ +//==================================================================== +// lcgeo - LC detector models in DD4hep +//-------------------------------------------------------------------- +// DD4hep Geometry driver for SiWEcalEndcaps +// Ported from Mokka +//-------------------------------------------------------------------- +// S.Lu, DESY +// $Id: SEcal04_Endcaps.cpp 1060 2016-09-05 07:48:41Z /C=JP/O=KEK/OU=CRC/CN=JEANS Daniel Thomelin Dietrich $ +//==================================================================== + + /* History: + +// ******************************************************* +// * * +// * Mokka * +// * - the detailed geant4 simulation for ILC - * +// * * +// * For more information about Mokka, visit the * +// * * +// * Mokka.in2p3.fr Mokka home page. * +// * * +// ******************************************************* +// +// $Id: SEcal04_Endcaps.cpp 1060 2016-09-05 07:48:41Z /C=JP/O=KEK/OU=CRC/CN=JEANS Daniel Thomelin Dietrich $ +// $Name: mokka-07-00 $ +// +// +// +// SEcal04.cc +// + Shaojun Lu: Ported from Mokka SEcal04 Endcaps part. Read the constants from XML + instead of the DB. Then build the Endcap in the same way with DD4hep + construct. + Inside SEcal04, some parameters, which used by Ecal Endcaps, come from + Ecal Barrel. They can be ssen here again. + */ + +#include "DD4hep/DetFactoryHelper.h" +#include "XML/Layering.h" +#include "DD4hep/Shapes.h" +#include "XML/Utilities.h" +#include "DDRec/DetectorData.h" +#include "DDSegmentation/WaferGridXY.h" +#include <sstream> + +using namespace std; + +using dd4hep::BUILD_ENVELOPE; +using dd4hep::Box; +using dd4hep::DetElement; +using dd4hep::Detector; +using dd4hep::IntersectionSolid; +using dd4hep::Layering; +using dd4hep::Material; +using dd4hep::PlacedVolume; +using dd4hep::PolyhedraRegular; +using dd4hep::Position; +using dd4hep::Readout; +using dd4hep::Ref_t; +using dd4hep::Rotation3D; +using dd4hep::RotationZYX; +using dd4hep::Segmentation; +using dd4hep::SensitiveDetector; +using dd4hep::Transform3D; +using dd4hep::Volume; +using dd4hep::_toString; + +using dd4hep::rec::LayeredCalorimeterData; + +#include "SEcal05_Helpers.h" + +#undef NDEBUG +#include <assert.h> + + +//#define VERBOSE 1 + +// workaround for DD4hep v00-14 (and older) +#ifndef DD4HEP_VERSION_GE +#define DD4HEP_VERSION_GE(a,b) 0 +#endif + +static Ref_t create_detector(Detector& theDetector, xml_h element, SensitiveDetector sens) { + + cout << "------------------------" << endl; + cout << "creating SEcal05_Endcaps" << endl; + cout << "------------------------" << endl; + + xml_det_t x_det = element; + string det_name = x_det.nameStr(); + Layering layering (element); + + int det_id = x_det.id(); + // xml_comp_t x_staves = x_det.staves(); + DetElement sdet (det_name,det_id); + // Volume motherVol = theDetector.pickMotherVolume(sdet); + + // --- create an envelope volume and position it into the world --------------------- + + Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector, element , sdet ) ; + + dd4hep::xml::setDetectorTypeFlag( element, sdet ) ; + + if( theDetector.buildType() == BUILD_ENVELOPE ) return sdet ; + //----------------------------------------------------------------------------------- + + sens.setType("calorimeter"); + + Readout readout = sens.readout(); + Segmentation seg = readout.segmentation(); + + //==================================================================== + // + // Read all the constant from ILD_o1_v05.xml + // Use them to build Ecal endcaps + // + //==================================================================== + + // read parameters from compact.xml file + double Ecal_Slab_shielding = theDetector.constant<double>("Ecal_Slab_shielding"); + double Ecal_fiber_thickness_structure = theDetector.constant<double>("Ecal_fiber_thickness_structure"); // absorber wrapping thickness + double Ecal_fiber_thickness_alveolus = theDetector.constant<double>("Ecal_fiber_thickness_alveolus"); // alveolar wall thickness + + double EcalBarrel_inner_radius = theDetector.constant<double>("TPC_outer_radius") +theDetector.constant<double>("Ecal_Tpc_gap"); + int Ecal_barrel_z_modules = theDetector.constant<int>("Ecal_barrel_z_modules"); + + double Ecal_radiator_thickness1 = theDetector.constant<double>("Ecal_radiator_layers_set1_thickness"); + double Ecal_radiator_thickness2 = theDetector.constant<double>("Ecal_radiator_layers_set2_thickness"); + double Ecal_radiator_thickness3 = theDetector.constant<double>("Ecal_radiator_layers_set3_thickness"); + double Ecal_Barrel_halfZ = theDetector.constant<double>("Ecal_Barrel_halfZ"); + + int Ecal_barrel_number_of_towers = theDetector.constant<int>("Ecal_barrel_number_of_towers"); + + double Ecal_support_thickness = theDetector.constant<double>("Ecal_support_thickness"); + double Ecal_front_face_thickness = theDetector.constant<double>("Ecal_front_face_thickness"); + double Ecal_lateral_face_thickness = theDetector.constant<double>("Ecal_lateral_face_thickness"); + double Ecal_Slab_H_fiber_thickness = theDetector.constant<double>("Ecal_Slab_H_fiber_thickness"); + + double Ecal_endcap_extra_size = theDetector.constant<double>("Ecal_endcap_extra_size"); + double Ecal_cables_gap = theDetector.constant<double>("Ecal_cables_gap"); + + int Ecal_nlayers1 = theDetector.constant<int>("Ecal_nlayers1"); + int Ecal_nlayers2 = theDetector.constant<int>("Ecal_nlayers2"); + int Ecal_nlayers3 = theDetector.constant<int>("Ecal_nlayers3"); + + // first layer is preshower? + bool Ecal_Endcap_PreshowerLayer = theDetector.constant<int>("Ecal_Endcap_Preshower") > 0; + + std::string Ecal_endcap_number_of_towers = theDetector.constant<string>("Ecal_endcap_number_of_towers"); + + int Ecal_end_of_slab_strategy = theDetector.constant<int>("Ecal_end_of_slab_strategy"); + + + int Ecal_n_wafers_per_tower = theDetector.constant<int>("Ecal_n_wafers_per_tower"); + + double Ecal_guard_ring_size = theDetector.constant<double>("Ecal_guard_ring_size"); + + double EcalEndcap_inner_radius = theDetector.constant<double>("EcalEndcap_inner_radius"); + double EcalEndcap_outer_radius = theDetector.constant<double>("EcalEndcap_outer_radius"); + double EcalEndcap_min_z = theDetector.constant<double>("EcalEndcap_min_z"); + double EcalEndcap_max_z = theDetector.constant<double>("EcalEndcap_max_z"); + + std::string Ecal_layerConfig = theDetector.constant<string>("Ecal_layer_pattern"); + + int Ecal_cells_across_megatile = theDetector.constant <int> ("Ecal_cells_across_megatile" ); + int Ecal_strips_across_megatile= theDetector.constant <int> ("Ecal_strips_across_megatile"); + int Ecal_strips_along_megatile = theDetector.constant <int> ("Ecal_strips_along_megatile" ); + + double Ecal_barrel_thickness = theDetector.constant<double>("Ecal_barrel_thickness"); // what's assumed in the compact description + + + //==================================================================== + // + // set up the helper + // + //==================================================================== + + SEcal05_Helpers helper; + + helper.setDet( & x_det ); + + // layer configuration + helper.setPreshower( Ecal_Endcap_PreshowerLayer ); + + cout << "Preshower ? " << Ecal_Endcap_PreshowerLayer << endl; + + helper.setAbsLayers( Ecal_nlayers1, Ecal_radiator_thickness1, + Ecal_nlayers2, Ecal_radiator_thickness2, + Ecal_nlayers3, Ecal_radiator_thickness3 ); + + cout << "absorber layers " << + Ecal_nlayers1 << "*" << Ecal_radiator_thickness1 << "mm + " << + Ecal_nlayers2 << "*" << Ecal_radiator_thickness2 << "mm + " << + Ecal_nlayers3 << "*" << Ecal_radiator_thickness3 << "mm " << endl; + + // check this setup is self-consistent + helper.checkLayerConsistency(); + + // set structural CF thicknesses + helper.setCFthickness( Ecal_fiber_thickness_structure, + Ecal_fiber_thickness_alveolus, + Ecal_front_face_thickness, + Ecal_support_thickness); + + // check that resulting total thickness is consistent with what's in the compact file + // n.b. this assumes same thickness in barrel and endcap! + float module_thickness = helper.getTotalThickness(); + if ( fabs( Ecal_barrel_thickness - module_thickness ) > 0.1*dd4hep::mm ) { + cout << "ERROR : thickness in comapct decription not consistent with what I calculate!" << endl; + cout << " calculated = " << module_thickness << " compact description: " << Ecal_barrel_thickness << endl; + assert( 0 ); // exit + } + + cout << "module thickness = " << module_thickness << endl; + + // set up the sensitive layer segmentation + helper.setSegmentation( &seg ); + + helper.setNCells( Ecal_cells_across_megatile, Ecal_strips_across_megatile, Ecal_strips_along_megatile); + helper.setMagicMegatileStrategy ( Ecal_end_of_slab_strategy ); + + + int ntemp; + std::stringstream stream(Ecal_layerConfig); + std::vector < int > layerConfig; + while ( stream >> ntemp ) { + assert( ntemp>=0 ); + layerConfig.push_back( ntemp ); + } + cout << "layer config: "; + for (size_t i=0; i<layerConfig.size(); i++) cout << layerConfig[i] << " "; + cout << endl; + + helper.setLayerConfig( layerConfig ); + + // set the number of towers/modules + std::vector < int > ntowers; + std::stringstream stream2( Ecal_endcap_number_of_towers ); + while ( stream2 >> ntemp ) { + ntowers.push_back( ntemp ); + } + + cout << "ECAL endcap tower configuration " << Ecal_endcap_number_of_towers << " : "; + for (size_t i=0; i<ntowers.size(); i++) + cout << ntowers[i] << " "; + cout << endl; + + // check that resulting quadrant size is consistent with compact description + // here we assume that the width of an alveolus in the endcaps is the same as that in the barrel. + double barrel_alv_width = ( Ecal_Barrel_halfZ*2./Ecal_barrel_z_modules - 2.*Ecal_lateral_face_thickness ) / Ecal_barrel_number_of_towers; + double calc_endcap_rout(EcalEndcap_inner_radius); + for (size_t i=0; i<ntowers.size(); i++) { + calc_endcap_rout+=ntowers[i]*barrel_alv_width + 2.*Ecal_lateral_face_thickness; + } + + cout << "alveolus width = " << barrel_alv_width << endl; + + // compare calculated size vs that in compact description + if ( fabs( calc_endcap_rout - EcalEndcap_outer_radius ) > 0.1*dd4hep::mm ) { + cout << "WARNING, inconsistent ECAL endcap radial extent! calculated: " << calc_endcap_rout << + " cm , nominal (from compact descrition): " << EcalEndcap_outer_radius << endl; + cout << "consider changing Ecal_endcap_extra_size from " << Ecal_endcap_extra_size << + " to " << calc_endcap_rout - EcalBarrel_inner_radius - Ecal_barrel_thickness << endl; + assert(0); + } + + + helper.setTowersUnits( ntowers, + barrel_alv_width, + Ecal_n_wafers_per_tower, + Ecal_lateral_face_thickness, + Ecal_fiber_thickness_alveolus + Ecal_Slab_H_fiber_thickness + Ecal_Slab_shielding, + Ecal_guard_ring_size ); + + + + // ========= Create Ecal endcaps ==================================== + // It will be the volume for palcing the Ecal endcaps alveolus(i.e. Layers). + // And the structure W plate. + // Itself will be placed into the world volume. + // ========================================================================== + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + //~ EndcapStandardModule ~ + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // octagon + PolyhedraRegular ECPolyHedra(8, M_PI/8., 0., calc_endcap_rout, module_thickness); + + // take just one quadrant of the octagon, and remove the centre box + double quadr = calc_endcap_rout; // anything which is big enough + Box quadrant( quadr, quadr , module_thickness); + IntersectionSolid EndCapSolid( ECPolyHedra, + quadrant, + Position( quadr - EcalEndcap_inner_radius, + quadr + EcalEndcap_inner_radius, 0 ) ); + + Volume EnvLogEndCap("EcalEndcapQuadrant",EndCapSolid,theDetector.material("CarbonFiber")); + + // kink position wrt bottom of quadrant (inside the lateral support) + // Y==0 is defined as outer edge of lateral face of inner module of quadrant + double kink_y = calc_endcap_rout*tan(M_PI/8.) - EcalEndcap_inner_radius; + + helper.setModuleDimensions(1, // xytype + 0, // xztype + calc_endcap_rout + EcalEndcap_inner_radius, // dxMax + kink_y + ); + + helper.setTranslation ( Position ( -EcalEndcap_inner_radius , EcalEndcap_inner_radius, -module_thickness/2. ) ); + + // make the module + + LayeredCalorimeterData* caloData = new LayeredCalorimeterData ; + caloData->layoutType = LayeredCalorimeterData::EndcapLayout ; + + DetElement mod_det ("quad0",det_id); + + helper.makeModule( EnvLogEndCap, + mod_det, + *caloData, + theDetector, + sens ); + + for (size_t i=0; i<caloData->layers.size(); i++) { + caloData->layers[i].distance += Ecal_Barrel_halfZ + Ecal_cables_gap; // add IP->front face distance + } + + // cout << "cell sizes: " << endl; + // for (size_t i=0; i<caloData->layers.size(); i++) { + // cout << "sensitive layer " << i << " : x,y = " << caloData->layers[i].cellSize0 << " " << caloData->layers[i].cellSize1 << endl; + // } + + caloData->layoutType = LayeredCalorimeterData::EndcapLayout ; + caloData->inner_symmetry = 4 ; // hard code cernter box hole + caloData->outer_symmetry = 8 ; // outer Octagun + caloData->phi0 = 0 ; + + /// extent of the calorimeter in the r-z-plane [ rmin, rmax, zmin, zmax ] in mm. + caloData->extent[0] = EcalEndcap_inner_radius ; + caloData->extent[1] = EcalEndcap_outer_radius ; + caloData->extent[2] = EcalEndcap_min_z ; + caloData->extent[3] = EcalEndcap_max_z ; + + + + //==================================================================== + // Place Ecal Endcap modules into the assembly envelope volume + //==================================================================== + + for (int iend=0; iend<2; iend++) { // the 2 endcaps + int module_id = ( iend == 0 ) ? 0:6; + + double this_module_z_offset = (EcalEndcap_min_z + EcalEndcap_max_z)/2.; + if ( iend == 0 ) this_module_z_offset*=-1; + + double this_module_rotY = iend==0 ? M_PI : 0; + double rotZ_offset = iend==0 ? M_PI/8. - M_PI/2. : M_PI/8. + 3.*M_PI/4.; // magic rotation to get modules in right place + for (int iquad=0; iquad<4; iquad++) { // the 4 quadrants per endcap + int stave_id=iquad+1; + double this_module_rotZ(0); + if ( iend==0 ) { + this_module_rotZ = rotZ_offset - (iquad-2) * M_PI/2.; + } else { + this_module_rotZ = rotZ_offset + (iquad+1) * M_PI/2.; + } + Position xyzVec(0,0,this_module_z_offset); + RotationZYX rot( this_module_rotZ ,this_module_rotY, 0); + Rotation3D rot3D(rot); + Transform3D tran3D(rot3D,xyzVec); + PlacedVolume pv = envelope.placeVolume(EnvLogEndCap,tran3D); + pv.addPhysVolID("module",module_id); // z: -/+ 0/6 + pv.addPhysVolID("stave",stave_id); + DetElement sd = (iend==0 && iquad==0) ? mod_det : mod_det.clone(_toString(module_id,"module%d")+_toString(stave_id,"stave%d")); + sd.setPlacement(pv); + } + } + + sdet.addExtension< LayeredCalorimeterData >( caloData ) ; + + // cout << "finished SEcal05_Endcaps" << endl; + + return sdet; + +} + + + +DECLARE_DETELEMENT(SEcal05_Endcaps, create_detector) + diff --git a/Detector/DetCEPCv4/src/calorimeter/SEcal05_Helpers.cpp b/Detector/DetCEPCv4/src/calorimeter/SEcal05_Helpers.cpp new file mode 100644 index 00000000..f5b57dcf --- /dev/null +++ b/Detector/DetCEPCv4/src/calorimeter/SEcal05_Helpers.cpp @@ -0,0 +1,729 @@ +#include "SEcal05_Helpers.h" + +SEcal05_Helpers::SEcal05_Helpers() { + // constructor, initialise internal variables + _x_det=NULL; + _layering=NULL; + _preshower=-1; + + _CF_absWrap=-1; + _CF_alvWall=-1; + _CF_front=-1; + _CF_back=-1; + + _ntowers.clear(); + _towerGap=-999; + _unitsPerTower=0; + _unitDeadEdge=-999; + + _cells_across_megatile=0; + _strips_across_megatile=0; + _strips_along_megatile=0; + + _constantSlabXYDimensions.clear(); + + _caloLayer.absorberThickness = -999; + _caloLayer.sensitive_thickness = -999; + _caloLayer.distance = 0; + + _totThick=0; + + _plugLength=0; + + _magicMegatileStrategy=-1; +} + + +void SEcal05_Helpers::setAbsLayers( int nl1, double th1, int nl2, double th2, int nl3, double th3 ) { + // set the number and thicknesses of absorber layers + _nlayers1=nl1; + _nlayers2=nl2; + _nlayers3=nl3; + _radiator_thickness1=th1; + _radiator_thickness2=th2; + _radiator_thickness3=th3; + return; +} + +void SEcal05_Helpers::checkLayerConsistency() { + // check requested number of absorber layers is cinsistent with preshower status + // we are constrained to have an even number of sensitive layers [ 2 sens. layers / slab ] + assert( (_preshower==0 || _preshower==1) && "_preshower not set" ); + int n_total_abs_layers = _nlayers1 + _nlayers2 + _nlayers3; // total number of Absorber layers + // check that number of requested absober layers is consistent + // if we want a preshower layer, total number of Absorber layers should be odd; otherwise even + if ( _preshower==1 && n_total_abs_layers%2==0 ) { + std::cout << "SEcal05_Helpers ERROR: inconsistent ECAL model !! if you request a preshower layer, the number of absorber layers = _nlayers1 + _nlayers2 + _nlayers3 must be odd" << std::endl; + std::cout << " Ecal_PreshowerLayer = " << _preshower << " ; _nlayers1/2/3 = " << _nlayers1 << " " << _nlayers2 << " " << _nlayers3 << std::endl; + assert(0); + } else if ( _preshower==0 && n_total_abs_layers%2==1 ) { + std::cout << "SEcal05_Helpers ERROR: inconsistent ECAL model !! if you request no preshower layer, the number of absorber layers = _nlayers1 + _nlayers2 + _nlayers3 must be even" << std::endl; + std::cout << " Ecal_PreshowerLayer = " << _preshower << " ; _nlayers1/2/3 = " << _nlayers1 << " " << _nlayers2 << " " << _nlayers3 << std::endl; + assert(0); + } + return; +} + +float SEcal05_Helpers::getTotalThickness() { + // calculate total thickness of ECAL + checkLayerConsistency(); + assert( _x_det && _layering && "_x_det or _layering not set" ); + assert( (_preshower==0 || _preshower==1) && "_preshower not set" ); + assert( _CF_absWrap>=0. && _CF_alvWall>=0. && _CF_front>=0. && _CF_back>=0. && "CF thicknesses not set" ); + float totalThickness = _CF_front+_CF_back;// front and back supports + + // the absorber in the structure + for (unsigned int i=0; i<_nlayers1+_nlayers2+_nlayers3; i++) { + bool inStructure = _preshower ? i%2==1 : i%2==0 ; + if ( inStructure ) { + double thickness (_radiator_thickness1); + if ( i>=_nlayers1 ) thickness = _radiator_thickness2; + if ( i>=_nlayers1+_nlayers2 ) thickness = _radiator_thickness3; + totalThickness += thickness + 2*_CF_absWrap; // the absorber and its wrapping + } + } + // the slabs + int l_num(0); + for(xml_coll_t li(*_x_det,_U(layer)); li; ++li) { // types of layers (i.e. thin/thick absorber) or "stack" + xml_comp_t x_layer = li; + // Loop over number of repeats for this layer. + for (int j=0; j< x_layer.repeat(); j++) { // layers within this type (or "stack") + float thisthick = _layering->layer(l_num)->thickness(); + totalThickness+=thisthick + 2*_CF_alvWall; // slab thickness, and the alveolar wall around it + l_num++; + } + } + return totalThickness; +} + +void SEcal05_Helpers::printSEcal05LayerInfo( dd4hep::rec::LayeredCalorimeterData::Layer & caloLayer) { + std::cout<<"SEcal05_Helpers === CALOLAYER printout: " << std::endl; + std::cout<<" caloLayer.distance: " << caloLayer.distance <<std::endl; + std::cout<<" caloLayer.inner_nRadiationLengths: " << caloLayer.inner_nRadiationLengths <<std::endl; + std::cout<<" caloLayer.inner_nInteractionLengths: "<< caloLayer.inner_nInteractionLengths <<std::endl; + std::cout<<" caloLayer.inner_thickness: " << caloLayer.inner_thickness <<std::endl; + std::cout<<" caloLayer.sensitive_thickness: " << caloLayer.sensitive_thickness <<std::endl; + std::cout<<" caloLayer.cellSize0, 1: " << caloLayer.cellSize0 << " " << caloLayer.cellSize1 << std::endl; + std::cout<<" caloLayer.outer_nRadiationLengths: " << caloLayer.outer_nRadiationLengths <<std::endl; + std::cout<<" caloLayer.outer_nInteractionLengths: "<< caloLayer.outer_nInteractionLengths <<std::endl; + std::cout<<" caloLayer.outer_thickness: " << caloLayer.outer_thickness <<std::endl; + return; +} + +double SEcal05_Helpers::getAbsThickness( unsigned int iAbsLay ) { //, int n1, int n2, int n3, double t1, double t2, double t3) { + // get the thickness of a given absorber layer + if ( iAbsLay < _nlayers1 ) return _radiator_thickness1; + else if ( iAbsLay - _nlayers1 < _nlayers2 ) return _radiator_thickness2; + else if ( iAbsLay - _nlayers1 - _nlayers2 < _nlayers3 ) return _radiator_thickness3; + assert(0 && "impossible layer number"); // should never get here + return -999.; +} + + + +std::vector <SEcal05_Helpers::dimposXYStruct> SEcal05_Helpers::getAbsPlateXYDimensions( double ztop ) { + // get the dimensions of absorber plates + // for trapeziodal case, this depends on the Z position + // CF wrap not taken into account, so this width constains both absorber and its wrapping + + if ( _module_XZtype==1 ) assert( ztop>=0 && "getAbsPlateXYDimensions: ztop not specified" ); // must specify Z position for case with non-uniform layers + + std::vector <dimposXYStruct> absorbersheets; + + if ( _module_XYtype == 1 ) { // each slab is different: calculate slab-by-slab (eg in endcap) + absorbersheets = getSlabXYDimensions( ztop ); + } else { // single sheet over all slabs (eg in barrel) + dimposXYStruct ss; + if ( _module_XZtype==0 ) { // no taper + ss.sizeX = _module_dX_max; + ss.posX = ss.sizeX/2.; + } else { // size varies by laer + + // ss.sizeX = _module_dX_max - 2.*ztop; // this assumes octagon + + ss.sizeX = _module_dX_max - 2.*ztop/tan(_module_angle); // for general shape + + ss.posX = _module_dX_max/2.; // keep it centered + } + ss.sizeY = _module_dY_total; + ss.posY = _module_dY_total/2.; + absorbersheets.push_back(ss); + } + return absorbersheets; +} + +std::vector <SEcal05_Helpers::dimposXYStruct> SEcal05_Helpers::getSlabXYDimensions( double ztop ) { + // calculate size and position of slab volumes + // size includes slab and dead area around it + + // ztop is Z of upper face of this slab (the face shortest in X) + if ( _module_XZtype==1 ) assert( ztop>=0 && "getSlabXYDimensions: ztop not specified" ); // must specify Z position for case with non-uniform layers + + std::vector <dimposXYStruct> layerslabdims; + if ( _module_XZtype == 0 && _constantSlabXYDimensions.size()>0 ) { // every layer is the same, and has already been calculated + layerslabdims = _constantSlabXYDimensions; + } else { + dimposXYStruct ss; + int totTow(0); + for (size_t imod=0; imod<_ntowers.size(); imod++) { // loop over "modules" [nb, for barrel, we only make a single module; for endcaps, typically have 3 per quadrant] + for (int itow = 0; itow<_ntowers[imod]; itow++) { // towers within the module + // each slab has same width + ss.sizeY = _alveolus_total_dim_Y; // this size include edge-of-tower dead space + ss.posY = + _moduleGap * ( 1 + 2*imod ) + // edge-of-module gaps from Y=0 to this slab + _alveolus_total_dim_Y * (totTow+0.5); // position of slab centre w.r.t (X,Y) = (0,0) + + if ( _module_XYtype==0 ) { // all slabs in a layer have same length + + if ( _module_XZtype == 0 ) { // all layers have same length + ss.sizeX = _module_dX_max; + ss.posX = ss.sizeX/2.; + } else { // layers vary in length : barrel module + + // ss.sizeX = _module_dX_max - 2.*ztop; // assumes octagon + + ss.sizeX = _module_dX_max - 2.*ztop/tan(_module_angle); // for general shape + + ss.posX = _module_dX_max/2.; // slabs all centred + } + + ss.sizeX -= _plugLength; // DANIELHACK + ss.posX += _plugLength/2.; // DANIELHACK + + + + } else if ( _module_XYtype==1 ) { // slabs within layer have different lengths : this is for endcap + // placing in Y + double upperY = ss.posY + 0.5*_alveolus_total_dim_Y; // Y position of upper edge + + if ( upperY<=_module_dY_kink ) { // straight edge part under kink + ss.sizeX = _module_dX_max; + } else { // sloping edge: take length at upperY, which is the shortest one + ss.sizeX = _module_dX_max - ( upperY - _module_dY_kink ); + } + + ss.posX = ss.sizeX/2.; // this aligns the -X end of slab + + ss.sizeX -= _plugLength; // DANIELHACK + ss.posX += _plugLength/2.; // DANIELHACK + + } else { + cout << " SEcal05_Helpers ERROR _module_XYtype = " << _module_XYtype << "!!!" << endl; + assert(0); + } + layerslabdims.push_back(ss); + totTow++; + } // towers + } // modules + + // if slab dimensions do not change layer-by-layer, memorise for next time + if ( _module_XZtype == 0 ) _constantSlabXYDimensions = layerslabdims; + } + return layerslabdims; +} + + +void SEcal05_Helpers::updateCaloLayers(double thickness, + dd4hep::Material mat, + bool isAbsorber, + bool isSensitive, + double cell_size_x, double cell_size_y, + bool isFinal + ) { + + if ( isFinal ) { // add material before saving layer + _layer_thickness += (thickness); + _layer_nRadiationLengths += (thickness)/mat.radLength() ; + _layer_nInteractionLengths += (thickness)/mat.intLength() ; + } + + // should we finish off the current layer? + if ( isFinal || // last slice of calo + (isAbsorber && _caloLayer.sensitive_thickness > 0 ) // end of a caloLayer + ) { + + // finalise caloLayer entry, add to caloData + _caloLayer.outer_thickness = _layer_thickness; + _caloLayer.outer_nRadiationLengths = _layer_nRadiationLengths; + _caloLayer.outer_nInteractionLengths = _layer_nInteractionLengths; + //_caloLayer.thickness = _caloLayer.inner_thickness + _caloLayer.outer_thickness ; + + // push it back + _caloData->layers.push_back( _caloLayer ) ; + // printSEcal05LayerInfo( _caloLayer ); + + // reset layer thicknesses + _layer_thickness=0; + _layer_nRadiationLengths=0; + _layer_nInteractionLengths=0; + + // update the calolayer.distance ( DJeans 12 sep 2017) + // here this is distance from ECAL start to the layer start; the Barrel and Endcap drivers add the distance from IP + _caloLayer.distance += _caloLayer.inner_thickness+_caloLayer.outer_thickness; + + + } + + if (!isFinal) { + + // first add half the material + _layer_thickness += (thickness/2.); + _layer_nRadiationLengths += (thickness/2.)/mat.radLength() ; + _layer_nInteractionLengths += (thickness/2.)/mat.intLength() ; + + // then we store material before and after centre of this layer + if ( isSensitive ) { + _caloLayer.cellSize0 = cell_size_x; + _caloLayer.cellSize1 = cell_size_y; + _caloLayer.sensitive_thickness = thickness ; + _caloLayer.inner_nRadiationLengths = _layer_nRadiationLengths ; + _caloLayer.inner_nInteractionLengths = _layer_nInteractionLengths ; + _caloLayer.inner_thickness = _layer_thickness ; + + // reset layer thicknesses + _layer_thickness=0; + _layer_nRadiationLengths=0; + _layer_nInteractionLengths=0; + } + + // add in remaining half of layer + _layer_thickness += (thickness/2.); + _layer_nRadiationLengths += (thickness/2.)/mat.radLength() ; + _layer_nInteractionLengths += (thickness/2.)/mat.intLength() ; + } + + _totThick+=thickness; + + return; +} + + +SEcal05_Helpers::dxinfo SEcal05_Helpers::getNormalMagicUnitsInX( double dx_total, double dx_unit, double dx_cell, double dx_dead , + int magicStrategy ) { + + dxinfo dxInf; + dxInf.normal_nX=-1; + dxInf.magic1_unitDX=-1; + dxInf.magic1_ncellsX=-1; + dxInf.magic2_unitDX=-1; + + int nNormalUnit = int( floor( dx_total / dx_unit ) ); + + if ( magicStrategy==0 ) { // just integer number of standard units (wafers/megatiles) + dxInf.normal_nX = nNormalUnit; + } else { + + double extraSpace = dx_total - nNormalUnit*dx_unit; + double extraSensitiveSpace = extraSpace - 2.*dx_dead; + double extraNCells = extraSensitiveSpace / dx_cell; + + if ( magicStrategy==1 ) { // last magic megatile has integer number of standard-sized cells + dxInf.normal_nX = nNormalUnit; + if ( extraNCells > 1.0 ) { + int iext = int( floor( extraNCells ) ); + dxInf.magic1_unitDX = iext*dx_cell + 2.*dx_dead; + dxInf.magic1_ncellsX = iext; + } + } else if ( magicStrategy==2 ) { + + // one magic megatile with integer number of standard cells, + // second with non-standard cell size to fill space exactly + // last magic cell size in range shortestMacigCell<magicCellsSize/standardCellSize<shortestMagicCell + + // the shortest magic cell (in terms of the usual cell size) + const double shortestMagicCell = 1.0; // this means that the last magic cell is at least as long as the standard cell + + extraSensitiveSpace = extraSpace - 4.*dx_dead; // because both the magic tiles may have a dead area + extraNCells = extraSensitiveSpace / dx_cell; + + if ( extraNCells < shortestMagicCell ) { // too small to make magic unit. remove one normal unit + nNormalUnit-=1; + extraSpace = dx_total - nNormalUnit*dx_unit; + extraSensitiveSpace = extraSpace - 4.*dx_dead; + extraNCells = extraSensitiveSpace / dx_cell; + } + + int imagic1 = int( floor( extraNCells ) ); // rounded down number of standard-sized cells + + double magic1size = imagic1>0 ? imagic1*dx_cell + 2*dx_dead : 0.; + + double magic2cellsize = extraSpace - magic1size - 2*dx_dead; + + if ( magic2cellsize/dx_cell < shortestMagicCell ) { // too small + imagic1 -= 1; // remove last standard cell + magic1size = imagic1>0 ? imagic1*dx_cell + 2*dx_dead : 0; + magic2cellsize = extraSpace - magic1size - 2*dx_dead; + } + + assert( magic2cellsize/dx_cell >= shortestMagicCell && magic2cellsize/dx_cell <= shortestMagicCell+1.0 && "problem in deciding magic unit size" ); + + dxInf.normal_nX = nNormalUnit; + dxInf.magic1_ncellsX = imagic1; + dxInf.magic1_unitDX = imagic1>0 ? imagic1*dx_cell + 2*dx_dead : 0; + + dxInf.magic2_unitDX = magic2cellsize + 2*dx_dead; + + } + } + + if ( magicStrategy==2 ) { + // check it fills exactly + if ( fabs( dxInf.normal_nX*dxInf.magic1_unitDX + dxInf.magic1_unitDX + dxInf.magic2_unitDX - dx_total ) < 0.01*dd4hep::mm ) { + cout << " SEcal05_Helpers ERROR : " << endl; + cout << dxInf.normal_nX*dxInf.magic1_unitDX << " " << dxInf.magic1_unitDX << " " << dxInf.magic2_unitDX << endl; + cout << dxInf.normal_nX*dxInf.magic1_unitDX + dxInf.magic1_unitDX + dxInf.magic2_unitDX << " " << dx_total << endl; + cout << dxInf.normal_nX*dxInf.magic1_unitDX + dxInf.magic1_unitDX + dxInf.magic2_unitDX - dx_total << endl; + assert(0 && "magic unit does not fill space exactly"); + } + + } + + return dxInf; +} + + +void SEcal05_Helpers::makeModule( dd4hep::Volume & mod_vol, // the volume we'll fill + dd4hep::DetElement & stave_det, // the detector element + dd4hep::rec::LayeredCalorimeterData & caloData, // the reco data we'll fill + dd4hep::Detector & theDetector, + dd4hep::SensitiveDetector & sens + ) { + // make the module + _caloData = &caloData; + + // calculate widths of module/tower/unit (in z-direction for barrel: across the slab/module + assert ( _ntowers.size()>0 && _unitsPerTower>0 && "_ntowers or _unitsPerTower not set" ); + + _module_dY_total=0; + for (size_t i=0; i<_ntowers.size(); i++) { + _module_dY_total = _ntowers[i]*_alveolus_total_dim_Y + 2.*_moduleGap; + } + + double alveolus_active_dim_Y = _alveolus_total_dim_Y - 2.*_towerGap; + double unit_dim_Y = alveolus_active_dim_Y/_unitsPerTower; + double unit_sensitive_dim_Y = unit_dim_Y - 2*_unitDeadEdge; // width of the sensitive area in each sensor + assert( unit_sensitive_dim_Y>0 && "negative-sized sensitive area..." ); // otherwise really weird! + + // get detector stuff + assert ( _x_det && "_x_det not set"); + int det_id = _x_det->id(); + xml_comp_t x_staves = _x_det->staves(); + + _carbon_fibre_material = theDetector.material("CarbonFiber"); + _radiator_material = theDetector.material(x_staves.materialStr()); + _air_material = theDetector.air(); + + dd4hep::VisAttr _radiator_visatt = theDetector.visAttributes( x_staves.visStr() ); + + // get segmentation stuff + assert (_geomseg && "segmentation not set"); + + dd4hep::DDSegmentation::WaferGridXY* waferSeg = dynamic_cast< dd4hep::DDSegmentation::WaferGridXY* > ( _geomseg->segmentation() ) ; + + + dd4hep::DDSegmentation::MegatileLayerGridXY* megatileSeg = dynamic_cast< dd4hep::DDSegmentation::MegatileLayerGridXY* > ( _geomseg->segmentation() ) ; + assert( (waferSeg || megatileSeg) && "no segmentation found" ); + + // set up the standard megatile size and offset + if ( megatileSeg ) { + megatileSeg->setMegaTileSizeXY( unit_sensitive_dim_Y, unit_sensitive_dim_Y ); + megatileSeg->setMegaTileOffsetXY( -unit_sensitive_dim_Y/2., -unit_sensitive_dim_Y/2. ); + } + + _module_thickness = getTotalThickness(); + + double currentLayerBase_pos_Z = 0; // this gets updated: it's the position of the bottom face of the next detector slice + + int layer_index = 0; // 1;// layer number - Daniel changes to c-type counting from 0...lets get rid of fortran habits! + + // to deal with initial layers + bool isFrontFace = true; + int myLayerNum = 0 ; // this is the sensitive layer number (one per sensitive) + unsigned int absorber_index(0); // keep track of which absorber layer we're in + + // keep track of thickness within a layer + // initialise + _layer_thickness = 0. ; + _layer_nRadiationLengths = 0. ; + _layer_nInteractionLengths = 0. ; + + //-------------------------------- + // loop over the layers + //--------------------------------- + + for(xml_coll_t li(*_x_det,_U(layer)); li; ++li) { // types of layers (i.e. thin/thick absorber) or "stack" + xml_comp_t x_layer = li; + // cout << " ---- NEW LAYER TYPE " << layer_index << " repeat " << x_layer.repeat() << endl; + + // Loop over number of repeats for this layer type + for (int j=0; j< x_layer.repeat(); j++) { // layers within this type (or "stack") + std::string l_name = dd4hep::_toString(layer_index,"layer%d"); + + // ######################### + // Build Structure Layer + // ######################### + + //---------------------------------------------------- + // position and thickness of absorber plates in the structure + //---------------------------------------------------- + double radiator_dim_Z(0); + double rad_pos_Z(0); + double this_struct_CFthick_beforeAbs(0); + double this_struct_CFthick_afterAbs(0); + + if ( isFrontFace ) { // the first part of the module depends on whether we have preshwer or not + if ( _preshower==1 ) { // don't include W+CF wrapping; only one side of alveolus + this_struct_CFthick_beforeAbs = 0; + this_struct_CFthick_afterAbs = _CF_front + _CF_alvWall; + radiator_dim_Z = 0; + } else { // include W+CF wrapping; only one side of alveolus + // cout << " -- no preshower, including absorber" << endl; + this_struct_CFthick_beforeAbs = _CF_front + _CF_absWrap; // allow for initial CF front plate also in no preshower case - djeans 6 july 2017 + this_struct_CFthick_afterAbs = _CF_absWrap + _CF_alvWall; + radiator_dim_Z = getAbsThickness( absorber_index++ ); + rad_pos_Z = radiator_dim_Z/2. + this_struct_CFthick_beforeAbs; // distance from top surface of structure to centre of radiator + } + isFrontFace=false; + } else { // internal layer: include W+CF wrapping; both sides of alveolus + this_struct_CFthick_beforeAbs = _CF_alvWall+_CF_absWrap; + this_struct_CFthick_afterAbs = _CF_alvWall+_CF_absWrap; + radiator_dim_Z = getAbsThickness( absorber_index++ ); + assert( radiator_dim_Z>0 && "no radiator!" ); + rad_pos_Z = this_struct_CFthick_beforeAbs + radiator_dim_Z/2.; // distance from top surface of structure to centre of radiator + } + + + // create the radiator volume + if ( radiator_dim_Z>0 ) { // only create the volume if we have radiator + std::vector < dimposXYStruct > absorbersheets = getAbsPlateXYDimensions( currentLayerBase_pos_Z + this_struct_CFthick_beforeAbs + radiator_dim_Z ); // add CF before abs. added djeans 21 nov 2016 + // create and place the absorber sheets + for ( size_t ipl=0; ipl<absorbersheets.size(); ipl++) { + dimposXYStruct plSize = absorbersheets[ipl]; + dd4hep::Box barrelStructureLayer_box( plSize.sizeX/2., + plSize.sizeY/2. - _CF_absWrap, // remove CF wrapping + radiator_dim_Z/2.); + + dd4hep::Volume barrelStructureLayer_vol( _det_name+"_"+l_name+"_"+dd4hep::_toString(int(ipl),"bs%02d"), + barrelStructureLayer_box, _radiator_material); + + barrelStructureLayer_vol.setVisAttributes( _radiator_visatt ); + + // Position the layer. + dd4hep::Position bsl_pos = getTranslatedPosition(plSize.posX, plSize.posY, currentLayerBase_pos_Z + rad_pos_Z ); + + // dd4hep::PlacedVolume barrelStructureLayer_phv = + mod_vol.placeVolume(barrelStructureLayer_vol, bsl_pos); + + } // loop over sheets + } + + // update layer thickness until front face of absorber + updateCaloLayers( this_struct_CFthick_beforeAbs, _carbon_fibre_material, false, false); + updateCaloLayers( radiator_dim_Z, _radiator_material, true, false ); + updateCaloLayers( this_struct_CFthick_afterAbs, _carbon_fibre_material, false, false); + + // update position within module + currentLayerBase_pos_Z += this_struct_CFthick_beforeAbs + radiator_dim_Z + this_struct_CFthick_afterAbs; + + // ######################### + // Build Slab + // ######################### + + //------------------------------- + // first sum the various materials for the caloData/caloLayer + //------------------------------- + int myLayerNumTemp = myLayerNum; + for(xml_coll_t si(x_layer,_U(slice)); si; ++si) { + xml_comp_t x_slice = si; + double s_thick = x_slice.thickness(); + dd4hep::Material slice_material = theDetector.material( x_slice.materialStr() ); + if (x_slice.materialStr().compare(x_staves.materialStr()) == 0){ + // this is absorber material + // check it's consistent with what we expect from the detector parameters + assert ( fabs( s_thick - getAbsThickness( absorber_index++ ) ) < 1e-5 && "inconsistent radiator thickness" ); + updateCaloLayers( s_thick, slice_material, true, false ); // absorber + } else if ( x_slice.isSensitive() ) { + double cell_size_x(0), cell_size_y(0); + if ( waferSeg ) { + cell_size_x = waferSeg->cellDimensions(0)[0]; + cell_size_y = waferSeg->cellDimensions(0)[1]; + } else if ( megatileSeg ) { + // setup megatile + int laytype = _layerConfig [ myLayerNumTemp%_layerConfig.size() ]; + if ( laytype==0 ) { + megatileSeg->setMegaTileCellsXY( myLayerNumTemp, _cells_across_megatile , _cells_across_megatile ); + } else if ( laytype==1 ) { + megatileSeg->setMegaTileCellsXY( myLayerNumTemp, _strips_across_megatile , _strips_along_megatile ); // strips in one orientation + } else if ( laytype==2 ) { + megatileSeg->setMegaTileCellsXY( myLayerNumTemp, _strips_along_megatile , _strips_across_megatile ); // and in the other + } else { + assert(0 && "unknown layer type"); + } + cell_size_x = megatileSeg->cellDimensions(myLayerNumTemp, 0)[0]; // dummy wafer + cell_size_y = megatileSeg->cellDimensions(myLayerNumTemp, 0)[1]; + } + updateCaloLayers( s_thick, slice_material, false, true, cell_size_x, cell_size_y ); // sensitive + myLayerNumTemp++; + } else { + updateCaloLayers( s_thick, slice_material, false, false ); + } + } + + //------------------------------------ + // then actually construct the slabs + //------------------------------------ + double slab_dim_Z = _layering->layer(layer_index)->thickness(); + double slab_pos_Z = currentLayerBase_pos_Z + slab_dim_Z/2.; // centre position of slab + + std::vector <dimposXYStruct> slabDims = getSlabXYDimensions( currentLayerBase_pos_Z + slab_dim_Z ); + + for (size_t islab = 0; islab<slabDims.size(); islab++) { + myLayerNumTemp = myLayerNum; + double slab_dim_X = slabDims[islab].sizeX; + + // make an air volume for the alveolus + dd4hep::Box l_box( slab_dim_X/2. , + slabDims[islab].sizeY/2. - _CF_alvWall, + slab_dim_Z/2. ); + + dd4hep::Volume l_vol( _det_name+"_alveolus_"+l_name, l_box, _air_material); + l_vol.setVisAttributes(theDetector.visAttributes( "GrayVis" ) ); + + dd4hep::DetElement l_det( stave_det, l_name+dd4hep::_toString(int(islab),"tower%02d") , det_id ); + dd4hep::Position l_pos = getTranslatedPosition(slabDims[islab].posX, slabDims[islab].posY, slab_pos_Z ); + dd4hep::PlacedVolume l_phv = mod_vol.placeVolume(l_vol,l_pos); + l_phv.addPhysVolID("tower", int(islab) ); + l_det.setPlacement(l_phv); + + // then fill it with the slab sublayers + int s_num(0); + double s_pos_Z = -slab_dim_Z / 2.; // position of sub-layer with respect to centre of this slab + + for(xml_coll_t si(x_layer,_U(slice)); si; ++si) { // the sub-layers + xml_comp_t x_slice = si; + std::string s_name = dd4hep::_toString(s_num,"slice%d"); + double s_thick = x_slice.thickness(); + + dd4hep::Material slice_material = theDetector.material( x_slice.materialStr() ); + + std::string vis_str = x_slice.visStr(); + + if ( !x_slice.isSensitive() ) { // not the sensitive slice: just a layer of stuff + + dd4hep::Box s_box( slab_dim_X/2. , slabDims[islab].sizeY/2. - _CF_alvWall, s_thick/2. ); + dd4hep::Volume s_vol(_det_name+"_"+l_name+"_"+s_name, s_box, slice_material); + s_vol.setVisAttributes(theDetector.visAttributes( vis_str )); + + dd4hep::Position s_pos( 0, 0, s_pos_Z + s_thick/2. ); + // dd4hep::PlacedVolume slice_phv = + l_vol.placeVolume(s_vol, s_pos ); + + } else { // sensitive slice + + // Normal squared wafers - this is just the sensitive part + // square piece of silicon, not including guard ring. guard ring material is not included + + dd4hep::Box WaferSiSolid( unit_sensitive_dim_Y/2., unit_sensitive_dim_Y/2., s_thick/2.); + + // get the standard cell size in X for this layer + double cell_size_x = waferSeg ? waferSeg->cellDimensions(0)[0] : megatileSeg->cellDimensions(myLayerNumTemp, 0)[0]; + double cell_size_y = waferSeg ? waferSeg->cellDimensions(0)[1] : megatileSeg->cellDimensions(myLayerNumTemp, 0)[1]; + + // work out how to make the magic units, if requested + dxinfo xseg = getNormalMagicUnitsInX( slab_dim_X, + unit_dim_Y, + cell_size_x, + _unitDeadEdge, + _magicMegatileStrategy ); + + int n_wafers_x = xseg.normal_nX; + int ncellsy = int( unit_sensitive_dim_Y/cell_size_y + 0.5 ); // should be exactly integer. take nearest integer to account for possible rounding errors. + + int wafer_num = 0; + + double wafer_pos_X = -slab_dim_X/2.; // keep track of wafer position in X + + for (int n_wafer_x = 0; n_wafer_x < n_wafers_x+2 ; n_wafer_x++) { // loop along slab, including magic megatile/wafer + + double megatile_size_x = unit_dim_Y; + int ncellsx(-1); + + bool isMagic = n_wafer_x >= n_wafers_x; + + if ( n_wafer_x==n_wafers_x ) { // first magic unit + megatile_size_x = xseg.magic1_unitDX; + ncellsx = xseg.magic1_ncellsX; + } else if ( n_wafer_x==n_wafers_x+1 ) { // second magic unit + megatile_size_x = xseg.magic2_unitDX; + ncellsx = 1; + } + + // cout << "ismagic " << isMagic << " wafer " << n_wafer_x << " / " << n_wafers_x << " : sizex " << megatile_size_x << " nx " << ncellsx << endl; + + if ( megatile_size_x<=0 ) continue; + + // cout << " PASSED" << endl; + + double megatile_sensitive_size_x = megatile_size_x - 2*_unitDeadEdge; + + for (int n_wafer_Y = 0; n_wafer_Y < _unitsPerTower; n_wafer_Y++) { // loop across slab (usually 2 wafers, or 1 EBU) [ie along beam dir for barrel module] + + double wafer_pos_Y = -alveolus_active_dim_Y/2.0 + (n_wafer_Y+0.5)*unit_dim_Y; + wafer_num++; + std::string Wafer_name; + if ( isMagic ) Wafer_name="magic"; + Wafer_name += dd4hep::_toString(wafer_num,"wafer%d"); + + dd4hep::Box* box = isMagic ? new dd4hep::Box( megatile_sensitive_size_x/2,unit_sensitive_dim_Y/2,s_thick/2.) : &WaferSiSolid; + dd4hep::Volume WaferSiLog(_det_name+"_"+l_name+"_"+s_name+"_"+Wafer_name,*box,slice_material); + + std::string wafer_vis_str = isMagic ? "YellowVis" : vis_str; + + WaferSiLog.setVisAttributes(theDetector.visAttributes( wafer_vis_str )); + WaferSiLog.setSensitiveDetector(sens); + + dd4hep::Position w_pos(wafer_pos_X + megatile_size_x/2., wafer_pos_Y, s_pos_Z + s_thick/2. ); + dd4hep::PlacedVolume wafer_phv = l_vol.placeVolume(WaferSiLog, w_pos ); + wafer_phv.addPhysVolID("wafer", wafer_num); + wafer_phv.addPhysVolID("layer", myLayerNumTemp ); + + if ( isMagic ) { + if ( megatileSeg ) { // define the special megatile + megatileSeg->setSpecialMegaTile( myLayerNumTemp, wafer_num, + megatile_sensitive_size_x, unit_sensitive_dim_Y, + -megatile_size_x/2., -unit_sensitive_dim_Y/2., // the offset + ncellsx, ncellsy ); // the segmentation + } + } else { // not magic + if ( waferSeg ) { // Normal squared wafers, this waferOffsetX is 0.0 // if its an odd number of cells, need to do something? + waferSeg->setWaferOffsetX(myLayerNumTemp, wafer_num, 0.0); + } + } // isMagic + + } // y-wafers + + wafer_pos_X += megatile_size_x; + + } // x-wafers + myLayerNumTemp++; + } // end sensitive slice + + // Increment Z position of slice. + s_pos_Z += s_thick; + + // Increment slice number. + ++s_num; + + } // end slice within one slab + } // slabs + myLayerNum = myLayerNumTemp; + currentLayerBase_pos_Z += slab_dim_Z; + layer_index++; // this is the structural layer counter (one per slab, not per sensitive layer) + } // layers in compact file + } // layer types in compact file + + // add material after last slab. Just CF + updateCaloLayers( _CF_alvWall + _CF_back, _carbon_fibre_material, false, false, -1, -1, true ); // the last layer + + return; +} diff --git a/Detector/DetCEPCv4/src/calorimeter/SEcal05_Helpers.h b/Detector/DetCEPCv4/src/calorimeter/SEcal05_Helpers.h new file mode 100644 index 00000000..80e8e829 --- /dev/null +++ b/Detector/DetCEPCv4/src/calorimeter/SEcal05_Helpers.h @@ -0,0 +1,309 @@ +#ifndef __SECAL05_HELPERS_H__ +#define __SECAL05_HELPERS_H__ 1 + +#include "DD4hep/DetFactoryHelper.h" + +#include "DDRec/DetectorData.h" + +#include "XML/Layering.h" +#include "XML/Utilities.h" + +#include "DD4hep/Segmentations.h" + +#include "DDSegmentation/MegatileLayerGridXY.h" + +#include "DDSegmentation/WaferGridXY.h" + +#include <iostream> + +#undef NDEBUG +#include <assert.h> + +using std::cout; +using std::endl; + +/* + +we build general ECAL modules on a horizontal table. + +X-Y are on the table +Z is vertically upwards +slabs are aligned along X +the lower Z side will face the IP + +2 shapes when looking down on table: + +XYtype = 0: (barrel and endcap) + ----------------------------- ^ Y + | | | + | | | + ----------------------------- ----> X + +XYtype = 1: (endcap) + ---------------------- + | \ + | \ + | \ + | | ^ + | | | dY_kink + ----------------------------- | + +2 shapes when looking from side: + +XZtype = 0: (endcap) + --------------------------------- + | | + | | + --------------------------------- + +XZtype = 1: (barrel) + -------------------------- ^ Z + / \ | + / \ | + / \ | + -------------------------------- ----> X + +the slabs are prefectly aligned at -ve X side +"magic" wafers are at +ve X + +local position: 0,0 defined as -veX,Y,Z corner + +we start building from Z=0, the face nearest IP, moving in the +ve Z direction + + +D.Jeans update: 03/2015 +dead space between slab end and module edge +other than 8-fold symmetry for barrel + + */ + +class SEcal05_Helpers { + + public: + + SEcal05_Helpers(); + + ~SEcal05_Helpers() { + delete _layering; + } + + // SET THE PARAMETERS + + void setDet( xml_det_t* x_det ) { + _x_det = x_det; + _layering = new dd4hep::Layering(*x_det); + _det_name = x_det->nameStr(); + } + + void setAbsLayers( int nl1, double th1, int nl2, double th2, int nl3=0, double th3=0 ); + + void setLayerConfig( std::vector < int > layerConfig ) { + _layerConfig=layerConfig; + } + + void setSegmentation( dd4hep::Segmentation & seg ) { + _geomseg = &seg; + } + + void setSegmentation( dd4hep::Segmentation * seg ) { + _geomseg = seg; + } + + void setNCells( int Ecal_cells_across_megatile, int Ecal_strips_across_megatile, int Ecal_strips_along_megatile) { + _cells_across_megatile = Ecal_cells_across_megatile ; + _strips_across_megatile = Ecal_strips_across_megatile ; + _strips_along_megatile = Ecal_strips_along_megatile ; + } + + void setMagicMegatileStrategy ( int i ) { + // magic megatile strategy + // 0: no magic megatile at lend of slab + // 1: magic megatile includes integer number of standard-sized cells + // 2: last cell of magic megatile adjusted to fill up to end of slab (size between 1 and 2 times normal cell size) + assert ( i>=0 && i<=2 ); + _magicMegatileStrategy = i; + } + + void setPreshower( bool p ) { + _preshower = p? 1 : 0; // do we have preshower layer? (if 1, first layer is sensitive; if 0, first layer is absorber) + } + + void setCFthickness( double absWrap, double alvWall, double front, double back ) { + _CF_absWrap = absWrap; // wrapping around absorber + _CF_alvWall = alvWall; // alveolar wall + _CF_front = front; // front (IP side) support plate + _CF_back = back; // back support plate + } + + void setModuleDimensions( int XYtype, // module shape in XY + int XZtype, // module shape in XZ + double dX_max, // maximum extent in X + double dY_kink = -999, // distance from lowerY edge to kink for XYtype=2 + double angle = M_PI/4. // angle, if not rectangular. pi/4 for octagon + ) { + if ( XYtype>0 ) assert ( XZtype==0 ); // endcap module has vertical edges + if ( XZtype>0 ) assert ( XYtype==0 ); // barrel module should have rectangular shadow + + _module_XYtype = XYtype; + _module_XZtype = XZtype; + _module_dX_max = dX_max; + _module_dY_kink = dY_kink; + _module_angle = angle; + + } + + void setTowersUnits( std::vector <int> ntowers, // a vector of modules, containing the specified # of towers/alveolii + double towerWidth, + int unitsPerTower, // # megatiles/units per tower + double moduleDeadEdge, // dead region at edge of module + double towerDeadEdge, // dead region at edge of tower + double unitDeadEdge // width of dead region at edge of each unit (e.g. wafer's guard ring) + ) { + _ntowers = ntowers; // number of towers (or alveoli) across Y + _alveolus_total_dim_Y = towerWidth; // width of tower (including dead area) + _unitsPerTower = unitsPerTower; // how many units (megatiles/wafers) across one tower + _moduleGap = moduleDeadEdge; // distance from tower boundary to edge of unit + _towerGap = towerDeadEdge; // distance from tower boundary to edge of unit + _unitDeadEdge = unitDeadEdge; // width of dead area around edge of unit + } + + void checkLayerConsistency(); + + float getTotalThickness(); + + void setTranslation( dd4hep::Position trans ) {_trans = trans;} + + // ---- this is the main workhorse + void makeModule( dd4hep::Volume & mod_vol, // the volume we'll fill + dd4hep::DetElement & stave_det, // the detector element + dd4hep::rec::LayeredCalorimeterData & caloData, // the reco data we'll fill + dd4hep::Detector & theDetector, + dd4hep::SensitiveDetector & sens + ); + + void setPlugLength( float ll ) { _plugLength = ll; } + + + private: + + void printSEcal05LayerInfo( dd4hep::rec::LayeredCalorimeterData::Layer & caloLayer); + + double getAbsThickness( unsigned int iAbsLay ); + + int getNumberOfAbsorberLayers() { + // total number of absorber layers + return _nlayers1+_nlayers2+_nlayers2; + } + + int getNumberOfStructuralAbsorberLayers() { + // number of absorber layers in the module (not in the slabs) + assert( _preshower==0 || _preshower==1 ); + return _preshower ? getNumberOfAbsorberLayers()/2 : (getNumberOfAbsorberLayers()-1)/2 ; + } + + struct dimposXYStruct { + double sizeX, sizeY; + double posX, posY; + }; + + struct dxinfo { + int normal_nX; + + double magic1_unitDX; + int magic1_ncellsX; + + double magic2_unitDX; + }; + + xml_det_t* _x_det; + dd4hep::Layering* _layering=NULL; + std::string _det_name; + + std::vector <dimposXYStruct> getAbsPlateXYDimensions( double ztop=-999 ); + + std::vector <dimposXYStruct> getSlabXYDimensions( double ztop=-999 ); + + + dd4hep::Position getTranslatedPosition(double x, double y, double z) { + return dd4hep::Position ( x, y, z ) + _trans; + } + + dxinfo getNormalMagicUnitsInX( double dx_total, double dx_unit, double dx_cell, double dx_dead , + int magicStrategy ); + + void updateCaloLayers(double thickness, + dd4hep::Material mat, + bool isAbsorber, + bool isSensitive, + double cell_size_x=0, double cell_size_y=0, + bool isFinal=false + ); + + + dd4hep::Segmentation* _geomseg; + + dd4hep::Material _air_material; + dd4hep::Material _carbon_fibre_material; + dd4hep::Material _radiator_material; + + unsigned int _cells_across_megatile; + unsigned int _strips_across_megatile; + unsigned int _strips_along_megatile; + + int _preshower; + + unsigned int _nlayers1; + unsigned int _nlayers2; + unsigned int _nlayers3; + + double _radiator_thickness1; + double _radiator_thickness2; + double _radiator_thickness3; + + std::vector < int > _layerConfig; // square or X or Y strips + + double _CF_absWrap; + double _CF_alvWall; + double _CF_front; + double _CF_back; + + // overall module size + int _module_XYtype; + int _module_XZtype; + double _module_dX_max; + double _module_dY_total; + double _module_dY_kink; + double _module_angle; + + // internal details + std::vector <int> _ntowers; + double _towerGap; + double _moduleGap; + int _unitsPerTower; + double _unitDeadEdge; + + dd4hep::rec::LayeredCalorimeterData* _caloData; + dd4hep::rec::LayeredCalorimeterData::Layer _caloLayer ; // this is the output info which is passed to reconstruction + + double _layer_thickness; + double _layer_nRadiationLengths; + double _layer_nInteractionLengths; + + double _totThick; + + double _alveolus_total_dim_Y; + double _module_thickness; + + int _magicMegatileStrategy; + + dd4hep::Position _trans; + + std::vector <dimposXYStruct> _constantSlabXYDimensions; + + + float _plugLength; + + +}; + +#endif -- GitLab