diff --git a/CMakeLists.txt b/CMakeLists.txt index a5fc00744..c02ec4a9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ endif() file(GLOB sources ./detector/tracker/*.cpp ./detector/calorimeter/*.cpp + ./detector/calorimeter/dual-readout/src/*.cpp ./detector/fcal/*.cpp ./detector/other/*.cpp ./detector/CaloTB/*.cpp @@ -106,9 +107,15 @@ if(NOT DCH_INFO_H_EXIST) message(WARNING "Subdetector ${FILES_DEPENDINGON_DCH_INFO_H} will not be built because header file ${DCH_INFO_H} was not found") endif() +find_package(EDM4HEP) file(GLOB G4sources ./plugins/TPCSDAction.cpp ./plugins/CaloPreShowerSDAction.cpp + ./plugins/FiberDRCaloSDAction.h + ./plugins/FiberDRCaloSDAction.cpp + ./plugins/Geant4Output2EDM4hep_DRC.cpp + ./plugins/DRCaloFastSimModel.cpp + ./plugins/DRCaloFastSimModel.h ) if(DD4HEP_USE_PYROOT) @@ -124,14 +131,18 @@ add_library(lcgeo ALIAS k4geo) target_include_directories(${PackageName} PRIVATE ${PROJECT_SOURCE_DIR}/detector/include ) target_include_directories(${PackageName}G4 PRIVATE ${PROJECT_SOURCE_DIR}/detector/include ) +target_include_directories(${PackageName} PRIVATE ${PROJECT_SOURCE_DIR}/detector/calorimeter/dual-readout/include ) +target_include_directories(${PackageName}G4 PRIVATE ${PROJECT_SOURCE_DIR}/detector/calorimeter/dual-readout/include ) + target_link_libraries(${PackageName} DD4hep::DDCore DD4hep::DDRec DD4hep::DDParsers ROOT::Core detectorSegmentations) -target_link_libraries(${PackageName}G4 DD4hep::DDCore DD4hep::DDRec DD4hep::DDParsers DD4hep::DDG4 ROOT::Core ${Geant4_LIBRARIES}) +target_link_libraries(${PackageName}G4 DD4hep::DDCore DD4hep::DDRec DD4hep::DDParsers DD4hep::DDG4 ROOT::Core EDM4HEP::edm4hep EDM4HEP::edm4hepDict podio::podio podio::podioDict podio::podioRootIO ${Geant4_LIBRARIES}) if(K4GEO_USE_LCIO) target_link_libraries(${PackageName} LCIO::lcio) target_link_libraries(${PackageName}G4 LCIO::lcio) endif() + #Create this_package.sh file, and install dd4hep_instantiate_package(${PackageName}) diff --git a/FCCee/IDEA/compact/IDEA_o1_v02/DectDimensions_IDEA_o1_v01.xml b/FCCee/IDEA/compact/IDEA_o1_v02/DectDimensions_IDEA_o1_v01.xml index 90dc7d53a..04b8a020a 100644 --- a/FCCee/IDEA/compact/IDEA_o1_v02/DectDimensions_IDEA_o1_v01.xml +++ b/FCCee/IDEA/compact/IDEA_o1_v02/DectDimensions_IDEA_o1_v01.xml @@ -103,45 +103,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -212,6 +173,7 @@ + @@ -228,6 +190,8 @@ + + diff --git a/FCCee/IDEA/compact/IDEA_o1_v02/IDEA_o1_v02.xml b/FCCee/IDEA/compact/IDEA_o1_v02/IDEA_o1_v02.xml index 4619c7cfb..7716d8087 100644 --- a/FCCee/IDEA/compact/IDEA_o1_v02/IDEA_o1_v02.xml +++ b/FCCee/IDEA/compact/IDEA_o1_v02/IDEA_o1_v02.xml @@ -52,7 +52,7 @@ - + diff --git a/FCCee/IDEA/compact/IDEA_o1_v03/DectDimensions_IDEA_o1_v03.xml b/FCCee/IDEA/compact/IDEA_o1_v03/DectDimensions_IDEA_o1_v03.xml index f3779cf19..090f19d87 100644 --- a/FCCee/IDEA/compact/IDEA_o1_v03/DectDimensions_IDEA_o1_v03.xml +++ b/FCCee/IDEA/compact/IDEA_o1_v03/DectDimensions_IDEA_o1_v03.xml @@ -53,6 +53,9 @@ + + + @@ -105,45 +108,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -214,6 +178,15 @@ + + + + + + + + + @@ -230,6 +203,8 @@ + + @@ -262,6 +237,15 @@ + + + + + + + + + diff --git a/FCCee/IDEA/compact/IDEA_o1_v03/FiberDualReadoutCalo_o1_v01.xml b/FCCee/IDEA/compact/IDEA_o1_v03/FiberDualReadoutCalo_o1_v01.xml new file mode 100644 index 000000000..8fa6606ee --- /dev/null +++ b/FCCee/IDEA/compact/IDEA_o1_v03/FiberDualReadoutCalo_o1_v01.xml @@ -0,0 +1,703 @@ + + + + + + + + The compact format for the dual-readout calorimeter (for FCCee IDEA) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + system:5,eta:-8,phi:9,x:32:-11,y:-9,c:1,module:2 + + + + diff --git a/FCCee/IDEA/compact/IDEA_o1_v03/IDEA_o1_v03.xml b/FCCee/IDEA/compact/IDEA_o1_v03/IDEA_o1_v03.xml index ff2bbf3db..7113e8182 100644 --- a/FCCee/IDEA/compact/IDEA_o1_v03/IDEA_o1_v03.xml +++ b/FCCee/IDEA/compact/IDEA_o1_v03/IDEA_o1_v03.xml @@ -42,7 +42,7 @@ - + @@ -53,6 +53,9 @@ + + + diff --git a/detector/calorimeter/dual-readout/include/DRconstructor.h b/detector/calorimeter/dual-readout/include/DRconstructor.h new file mode 100644 index 000000000..eddc2b098 --- /dev/null +++ b/detector/calorimeter/dual-readout/include/DRconstructor.h @@ -0,0 +1,76 @@ +#ifndef DRconstructor_h +#define DRconstructor_h 1 + +#include "detectorSegmentations/DRparamBarrel_k4geo.h" +#include "detectorSegmentations/GridDRcaloHandle_k4geo.h" + +#include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/OpticalSurfaces.h" +#include "DD4hep/Printout.h" +#include "DD4hep/Detector.h" + +namespace ddDRcalo { + class DRconstructor { + public: + DRconstructor(xml_det_t& x_det); + ~DRconstructor() {} + + void setExpHall(dd4hep::Assembly* experimentalHall) { fExperimentalHall = experimentalHall; } + void setDRparamBarrel(dd4hep::DDSegmentation::DRparamBarrel_k4geo* paramBarrel) { fParamBarrel = paramBarrel; } + void setDRparamEndcap(dd4hep::DDSegmentation::DRparamEndcap_k4geo* paramEndcap) { fParamEndcap = paramEndcap; } + void setDescription(dd4hep::Detector* description) { fDescription = description; } + void setDetElement(dd4hep::DetElement* drDet) { fDetElement = drDet; } + void setSipmSurf(dd4hep::OpticalSurface* sipmSurf) { fSipmSurf = sipmSurf; } + void setMirrorSurf(dd4hep::OpticalSurface* mirrorSurf) { fMirrorSurf = mirrorSurf; } + void setSensDet(dd4hep::SensitiveDetector* sensDet) { + fSensDet = sensDet; + fSegmentation = dynamic_cast( sensDet->readout().segmentation().segmentation() ); + } + + void construct(); + + private: + void implementTowers(xml_comp_t& x_theta, dd4hep::DDSegmentation::DRparamBase_k4geo* param); + void placeAssembly(xml_comp_t& x_theta, xml_comp_t& x_wafer, dd4hep::DDSegmentation::DRparamBase_k4geo* param, + dd4hep::Trap& assemblyEnvelop, dd4hep::Volume& towerVol, dd4hep::Volume& sipmWaferVol, + int towerNo, int nPhi, bool isRHS=true); + void implementFibers(xml_comp_t& x_theta, dd4hep::Volume& towerVol, dd4hep::Trap& trap, dd4hep::DDSegmentation::DRparamBase_k4geo* param, int towerNo); + void implementFiber(dd4hep::Volume& towerVol, dd4hep::Trap& trap, dd4hep::Position pos, int col, int row, + dd4hep::Tube& fiberEnv, dd4hep::Tube& fiber, dd4hep::Tube& fiberC, dd4hep::Tube& fiberS); + void implementSipms(dd4hep::Volume& sipmLayerVol, dd4hep::Trap& trap); + double calculateDistAtZ(TGeoTrap* rootTrap, dd4hep::Position& pos, double* norm, double z); + float calculateFiberLen(TGeoTrap* rootTrap, dd4hep::Position& pos, double* norm, double z1, double diff, double towerHeight); + dd4hep::Box calculateFullBox(TGeoTrap* rootTrap, int& rmin, int& rmax, int& cmin, int& cmax, double dz); + bool checkContained(TGeoTrap* rootTrap, dd4hep::Position& pos, double z, bool throwExcept=false); + void getNormals(TGeoTrap* rootTrap, int numxBl2, double z, double* norm1, double* norm2, double* norm3, double* norm4); + void placeUnitBox(dd4hep::Volume& fullBox, dd4hep::Volume& unitBox, int rmin, int rmax, int cmin, int cmax, bool& isEvenRow, bool& isEvenCol); + + xml_det_t fX_det; + xml_comp_t fX_barrel; + xml_comp_t fX_endcap; + xml_comp_t fX_sipmDim; + xml_comp_t fX_struct; + xml_comp_t fX_dim; + xml_comp_t fX_cladC; + xml_comp_t fX_coreC; + xml_comp_t fX_coreS; + xml_comp_t fX_hole; + xml_comp_t fX_dark; + xml_comp_t fX_mirror; + dd4hep::Assembly* fExperimentalHall; + dd4hep::Detector* fDescription; + dd4hep::DDSegmentation::DRparamBarrel_k4geo* fParamBarrel; + dd4hep::DDSegmentation::DRparamEndcap_k4geo* fParamEndcap; + dd4hep::DetElement* fDetElement; + dd4hep::SensitiveDetector* fSensDet; + dd4hep::OpticalSurface* fSipmSurf; + dd4hep::OpticalSurface* fMirrorSurf; + dd4hep::DDSegmentation::GridDRcalo_k4geo* fSegmentation; + + bool fVis; + int fNumx, fNumy; + std::vector< std::pair > fFiberCoords; + }; +} + +#endif diff --git a/detector/calorimeter/dual-readout/src/DRconstructor.cpp b/detector/calorimeter/dual-readout/src/DRconstructor.cpp new file mode 100644 index 000000000..4c6b09f56 --- /dev/null +++ b/detector/calorimeter/dual-readout/src/DRconstructor.cpp @@ -0,0 +1,367 @@ +#include "DRconstructor.h" + +ddDRcalo::DRconstructor::DRconstructor(xml_det_t& x_det) +: fX_det(x_det), + // no default initializer for xml_comp_t + fX_barrel( x_det.child( _Unicode(barrel) ) ), + fX_endcap( x_det.child( _Unicode(endcap) ) ), + fX_sipmDim( x_det.child( _Unicode(sipmDim) ) ), + fX_struct( x_det.child( _Unicode(structure) ) ), + fX_dim( fX_struct.child( _Unicode(dim) ) ), + fX_cladC( fX_struct.child( _Unicode(cladC) ) ), + fX_coreC( fX_struct.child( _Unicode(coreC) ) ), + fX_coreS( fX_struct.child( _Unicode(coreS) ) ), + fX_hole( fX_struct.child( _Unicode(hole) ) ), + fX_dark( fX_struct.child( _Unicode(dark) ) ), + fX_mirror( fX_struct.child( _Unicode(mirror) ) ) { + fExperimentalHall = nullptr; + fParamBarrel = nullptr; + fDescription = nullptr; + fDetElement = nullptr; + fSensDet = nullptr; + fSipmSurf = nullptr; + fMirrorSurf = nullptr; + fSegmentation = nullptr; + fVis = false; + fNumx = 0; + fNumy = 0; + fFiberCoords.reserve(100000); +} + +void ddDRcalo::DRconstructor::construct() { + // set vis on/off + fVis = fDescription->visAttributes(fX_det.visStr()).showDaughters(); + + implementTowers(fX_barrel, fParamBarrel); + implementTowers(fX_endcap, fParamEndcap); +} + +void ddDRcalo::DRconstructor::implementTowers(xml_comp_t& x_theta, dd4hep::DDSegmentation::DRparamBase_k4geo* param) { + double currentTheta = x_theta.theta(); + int towerNo = x_theta.start(); + for (xml_coll_t x_dThetaColl(x_theta,_U(deltatheta)); x_dThetaColl; ++x_dThetaColl, ++towerNo ) { + xml_comp_t x_deltaTheta = x_dThetaColl; + + std::cout << "test Tower No : " << towerNo << std::endl; + + // always use RHS for the reference + param->SetIsRHS(true); + param->SetDeltaTheta(x_deltaTheta.deltatheta()); + + double currentToC = currentTheta + x_deltaTheta.deltatheta()/2.; + currentTheta += x_deltaTheta.deltatheta(); + param->SetThetaOfCenter(currentToC); + param->init(); + + dd4hep::Trap assemblyEnvelop( (x_theta.height()+param->GetSipmHeight())/2., 0., 0., param->GetH1(), param->GetBl1(), param->GetTl1(), 0., + param->GetH2sipm(), param->GetBl2sipm(), param->GetTl2sipm(), 0. ); + + dd4hep::Trap tower( x_theta.height()/2., 0., 0., param->GetH1(), param->GetBl1(), param->GetTl1(), 0., + param->GetH2(), param->GetBl2(), param->GetTl2(), 0. ); + + dd4hep::Volume towerVol( "tower", tower, fDescription->material(x_theta.materialStr()) ); + towerVol.setVisAttributes(*fDescription, x_theta.visStr()); + + implementFibers(x_theta, towerVol, tower, param, towerNo); + + xml_comp_t x_wafer ( fX_sipmDim.child( _Unicode(sipmWafer) ) ); + + // Photosensitive wafer + dd4hep::Trap sipmWaferBox( x_wafer.height()/2., 0., 0., param->GetH2(), param->GetBl2(), param->GetTl2(), 0., + param->GetH2(), param->GetBl2(), param->GetTl2(), 0. ); + dd4hep::Volume sipmWaferVol( "sipmWafer", sipmWaferBox, fDescription->material(x_wafer.materialStr()) ); + if (fVis) sipmWaferVol.setVisAttributes(*fDescription, x_wafer.visStr()); + dd4hep::SkinSurface(*fDescription, *fDetElement, "SiPMSurf_Tower"+std::to_string(towerNo), *fSipmSurf, sipmWaferVol); + + if (x_wafer.isSensitive()) { + sipmWaferVol.setSensitiveDetector(*fSensDet); + } + + // Remove sipmLayer, clear fFiberCoords instead of implementSipms() + fFiberCoords.clear(); + + for (int nPhi = 0; nPhi < x_theta.nphi(); nPhi++) { + placeAssembly(x_theta,x_wafer,param,assemblyEnvelop,towerVol,sipmWaferVol,towerNo,nPhi); + + if ( fX_det.reflect() ) + placeAssembly(x_theta,x_wafer,param,assemblyEnvelop,towerVol,sipmWaferVol,towerNo,nPhi,false); + } + } + + param->filled(); + param->SetTotTowerNum( towerNo - x_theta.start() ); +} + +void ddDRcalo::DRconstructor::placeAssembly(xml_comp_t& x_theta, xml_comp_t& x_wafer, dd4hep::DDSegmentation::DRparamBase_k4geo* param, + dd4hep::Trap& assemblyEnvelop, dd4hep::Volume& towerVol, dd4hep::Volume& sipmWaferVol, + int towerNo, int nPhi, bool isRHS) { + param->SetIsRHS(isRHS); + int towerNoLR = param->signedTowerNo(towerNo); + auto towerId64 = fSegmentation->setVolumeID( towerNoLR, nPhi ); + int towerId32 = fSegmentation->getFirst32bits(towerId64); + + // copy number of assemblyVolume is unpredictable, use dummy volume to make use of copy number of afterwards + dd4hep::Volume assemblyEnvelopVol( std::string("assembly") + (isRHS ? "" : "_refl") , assemblyEnvelop, fDescription->material("Vacuum") ); + fExperimentalHall->placeVolume( assemblyEnvelopVol, param->GetAssembleTransform3D(nPhi) ); + + assemblyEnvelopVol.placeVolume( towerVol, towerId32, dd4hep::Position(0.,0.,-param->GetSipmHeight()/2.) ); + + // Remove sipmLayer + + dd4hep::PlacedVolume sipmWaferPhys = assemblyEnvelopVol.placeVolume( sipmWaferVol, towerId32, dd4hep::Position(0.,0.,(x_theta.height()+param->GetSipmHeight()-x_wafer.height())/2.) ); + sipmWaferPhys.addPhysVolID("eta", towerNoLR); + sipmWaferPhys.addPhysVolID("phi", nPhi); + sipmWaferPhys.addPhysVolID("module", 0); + + return; +} + +void ddDRcalo::DRconstructor::implementFibers(xml_comp_t& x_theta, dd4hep::Volume& towerVol, dd4hep::Trap& trap, dd4hep::DDSegmentation::DRparamBase_k4geo* param, int towerNo) { + dd4hep::Tube fiberEnv = dd4hep::Tube(0.,fX_cladC.rmax(),x_theta.height()/2.); + dd4hep::Tube fiber = dd4hep::Tube(0.,fX_cladC.rmax(),x_theta.height()/2.-fX_mirror.height()/2.); + dd4hep::Tube fiberC = dd4hep::Tube(0.,fX_coreC.rmin(),x_theta.height()/2.-fX_mirror.height()/2.); + dd4hep::Tube fiberS = dd4hep::Tube(0.,fX_coreS.rmin(),x_theta.height()/2.-fX_mirror.height()/2.); + + auto rootTrap = trap.access(); + + float sipmSize = fX_dim.dx(); + float gridSize = fX_dim.distance(); + float towerHeight = x_theta.height(); + + float diff = fX_cladC.rmax(); // can be arbitrary small number + float z1 = towerHeight/2.-2*diff; // can be arbitrary number slightly smaller than towerHeight/2-diff + + fNumx = static_cast( std::floor( ( param->GetTl2()*2. - sipmSize )/gridSize ) ) + 1; // in phi direction + fNumy = static_cast( std::floor( ( param->GetH2()*2. - sipmSize )/gridSize ) ) + 1; // in eta direction + int numxBl2 = static_cast( std::floor( ( param->GetBl2()*2. - sipmSize )/gridSize ) ) + 1; // only used for estimating normals + + // full length fibers + int rmin = 0, rmax = 0, cmin = 0, cmax = 0; + dd4hep::Box fullBox = calculateFullBox(rootTrap,rmin,rmax,cmin,cmax,rootTrap->GetDz()); + dd4hep::Volume fullBoxVol("fullBox",fullBox,fDescription->material(x_theta.materialStr())); + fullBoxVol.setVisAttributes(*fDescription, x_theta.visStr()); + + dd4hep::Box unitBox = dd4hep::Box(gridSize,gridSize,x_theta.height()/2.); + dd4hep::Volume unitBoxVol("unitBox",unitBox,fDescription->material(x_theta.materialStr())); + + if (fVis) + unitBoxVol.setVisAttributes(*fDescription, x_theta.visStr()); + + // // Remove cap (mirror or black paint in front of the fiber) + implementFiber(unitBoxVol, trap, dd4hep::Position(-gridSize/2.,-gridSize/2.,0.), cmin, rmin, fiberEnv, fiber, fiberC, fiberS); + implementFiber(unitBoxVol, trap, dd4hep::Position(gridSize/2.,-gridSize/2.,0.), cmin+1, rmin, fiberEnv, fiber, fiberC, fiberS); + implementFiber(unitBoxVol, trap, dd4hep::Position(-gridSize/2.,gridSize/2.,0.), cmin, rmin+1, fiberEnv, fiber, fiberC, fiberS); + implementFiber(unitBoxVol, trap, dd4hep::Position(gridSize/2.,gridSize/2.,0.), cmin+1, rmin+1, fiberEnv, fiber, fiberC, fiberS); + + bool isEvenRow = false, isEvenCol = false; + placeUnitBox(fullBoxVol,unitBoxVol,rmin,rmax,cmin,cmax,isEvenRow,isEvenCol); + towerVol.placeVolume(fullBoxVol); + + // get normals to each side + double norm1[3] = {0.,0.,0.}, norm2[3] = {0.,0.,0.}, norm3[3] = {0.,0.,0.}, norm4[3] = {0.,0.,0.}; + getNormals(rootTrap,numxBl2,z1,norm1,norm2,norm3,norm4); + + for (int row = 0; row < fNumy; row++) { + for (int column = 0; column < fNumx; column++) { + auto localPosition = fSegmentation->localPosition(fNumx,fNumy,column,row); + dd4hep::Position pos = dd4hep::Position(localPosition); + + if ( row >= rmin && row <= rmax && column >= cmin && column <= cmax ) { + if ( ( !isEvenRow && row==rmax ) || ( !isEvenCol && column==cmax ) ) { + double pos_[3] = {pos.x(),pos.y(),-fullBox.z()+TGeoShape::Tolerance()}; + bool check = fullBox.access()->Contains(pos_); + + if (check) { + implementFiber(fullBoxVol, trap, pos, column, row, fiberEnv, fiber, fiberC, fiberS); + fFiberCoords.push_back( std::make_pair(column,row) ); + } + } + } else { + // outside tower + if (!checkContained(rootTrap,pos,z1)) continue; + + double* normX = nullptr; + double* normY = nullptr; + + // select two closest orthogonal sides + if (column > fNumx/2) normX = norm2; + else normX = norm4; + + if (row > fNumy/2) normY = norm3; + else normY = norm1; + + // compare and choose the shortest fiber length + float cand1 = calculateFiberLen(rootTrap, pos, normX, z1, diff, towerHeight); + float cand2 = calculateFiberLen(rootTrap, pos, normY, z1, diff, towerHeight); + float fiberLen = std::min(cand1,cand2); + + // not enough space to place fiber + if ( fiberLen < 0. ) continue; + + // trim fiber length in the case calculated length is longer than tower height + if (fiberLen > towerHeight) fiberLen = towerHeight; + float centerZ = towerHeight/2. - fiberLen/2.; + + // final check + if ( checkContained(rootTrap,pos,towerHeight/2.-fiberLen) ) { + dd4hep::Position centerPos( pos.x(),pos.y(),centerZ ); + + dd4hep::Tube shortFiberEnv = dd4hep::Tube(0.,fX_cladC.rmax(),fiberLen/2.); + dd4hep::Tube shortFiber = dd4hep::Tube(0.,fX_cladC.rmax(),fiberLen/2.-fX_mirror.height()/2.); + dd4hep::Tube shortFiberC = dd4hep::Tube(0.,fX_coreC.rmin(),fiberLen/2.-fX_mirror.height()/2.); + dd4hep::Tube shortFiberS = dd4hep::Tube(0.,fX_coreS.rmin(),fiberLen/2.-fX_mirror.height()/2.); + + implementFiber(towerVol, trap, centerPos, column, row, shortFiberEnv, shortFiber, shortFiberC, shortFiberS); + fFiberCoords.push_back( std::make_pair(column,row) ); + } + } + } + } +} + +// Remove cap (mirror or black paint in front of the fiber) +void ddDRcalo::DRconstructor::implementFiber(dd4hep::Volume& towerVol, dd4hep::Trap& trap, dd4hep::Position pos, int col, int row, + dd4hep::Tube& fiberEnv, dd4hep::Tube& fiber, dd4hep::Tube& fiberC, dd4hep::Tube& fiberS) { + dd4hep::Volume fiberEnvVol("fiberEnv", fiberEnv, fDescription->material(fX_hole.materialStr())); + towerVol.placeVolume( fiberEnvVol, pos ); + + if ( fSegmentation->IsCerenkov(col,row) ) { //c fiber + dd4hep::Volume cladVol("cladC", fiber, fDescription->material(fX_cladC.materialStr())); + fiberEnvVol.placeVolume( cladVol, dd4hep::Position(0.,0.,fX_mirror.height()/2.) ); + if (fVis) cladVol.setVisAttributes(*fDescription, fX_cladC.visStr()); // high CPU consumption! + + dd4hep::Volume coreVol("coreC", fiberC, fDescription->material(fX_coreC.materialStr())); + if (fVis) coreVol.setVisAttributes(*fDescription, fX_coreC.visStr()); + cladVol.placeVolume( coreVol ); + + coreVol.setRegion(*fDescription, fX_det.regionStr()); + cladVol.setRegion(*fDescription, fX_det.regionStr()); + } else { // s fiber + dd4hep::Volume cladVol("cladS", fiber, fDescription->material(fX_coreC.materialStr())); + fiberEnvVol.placeVolume( cladVol, dd4hep::Position(0.,0.,fX_mirror.height()/2.) ); + if (fVis) cladVol.setVisAttributes(*fDescription, fX_coreC.visStr()); + + dd4hep::Volume coreVol("coreS", fiberS, fDescription->material(fX_coreS.materialStr())); + if (fVis) coreVol.setVisAttributes(*fDescription, fX_coreS.visStr()); + cladVol.placeVolume( coreVol ); + + coreVol.setRegion(*fDescription, fX_det.regionStr()); + cladVol.setRegion(*fDescription, fX_det.regionStr()); + } +} + +double ddDRcalo::DRconstructor::calculateDistAtZ(TGeoTrap* rootTrap, dd4hep::Position& pos, double* norm, double z) { + double pos_[3] = {pos.x(),pos.y(),z}; + + return rootTrap->DistFromInside(pos_,norm); +} + +float ddDRcalo::DRconstructor::calculateFiberLen(TGeoTrap* rootTrap, dd4hep::Position& pos, double* norm, double z1, double diff, double towerHeight) { + float z2 = z1+diff; + float y1 = calculateDistAtZ(rootTrap,pos,norm,z1); + float y2 = calculateDistAtZ(rootTrap,pos,norm,z2); + float ymin = std::min(y1,y2); + + // return if the distance is smaller than fiber diameter + if ( ymin < 2.*fX_cladC.rmax() ) return -1.; + + // find the point where the fiber reaches a side of the tower + float slope = (y2-y1)/diff; + float y0 = (y1*z2-y2*z1)/diff; + float z = (fX_cladC.rmax()-y0)/slope; + float fiberLen = towerHeight/2. - z; + + return fiberLen; +} + +bool ddDRcalo::DRconstructor::checkContained(TGeoTrap* rootTrap, dd4hep::Position& pos, double z, bool throwExcept) { + double pos_[3] = {pos.x(),pos.y(),z}; + bool check = rootTrap->Contains(pos_); + + if ( throwExcept && !check ) throw std::runtime_error("Fiber must be in the tower!"); + return check; +} + +void ddDRcalo::DRconstructor::getNormals(TGeoTrap* rootTrap, int numxBl2, double z, double* norm1, double* norm2, double* norm3, double* norm4) { + dd4hep::Position pos1 = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,fNumx/2,0) ); + dd4hep::Position pos2 = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,fNumx/2+numxBl2/2-1,fNumy/2) ); + dd4hep::Position pos3 = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,fNumx/2,fNumy-1) ); + dd4hep::Position pos4 = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,fNumx/2-numxBl2/2+1,fNumy/2) ); + double pos1_[3] = {pos1.x(),pos1.y(),z}; + double pos2_[3] = {pos2.x(),pos2.y(),z}; + double pos3_[3] = {pos3.x(),pos3.y(),z}; + double pos4_[3] = {pos4.x(),pos4.y(),z}; + double dir[3] = {0.,0.,0.}; + + rootTrap->ComputeNormal(pos1_,dir,norm1); + rootTrap->ComputeNormal(pos2_,dir,norm2); + rootTrap->ComputeNormal(pos3_,dir,norm3); + rootTrap->ComputeNormal(pos4_,dir,norm4); + norm1[2] = 0.; // check horizontal distance only + norm2[2] = 0.; + norm3[2] = 0.; + norm4[2] = 0.; +} + +dd4hep::Box ddDRcalo::DRconstructor::calculateFullBox(TGeoTrap* rootTrap, int& rmin, int& rmax, int& cmin, int& cmax, double dz) { + float gridSize = fX_dim.distance(); + double zmin = -rootTrap->GetDz() + TGeoShape::Tolerance(); + float xmin = 0., xmax = 0., ymin = 0., ymax = 0.; + + for (int row = 0; row < fNumy; row++) { // bottom-up + auto localPosition = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,fNumx/2,row) ); + auto pos = localPosition + dd4hep::Position(0.,-gridSize/2.,0.); + if ( checkContained(rootTrap,pos,zmin) ) { + ymin = pos.y(); + rmin = row; + break; + } + } + + for (int row = fNumy-1; row !=0 ; row--) { // top-down + auto localPosition = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,fNumx/2,row) ); + auto pos = localPosition + dd4hep::Position(0.,gridSize/2.,0.); + if ( checkContained(rootTrap,pos,zmin) ) { + ymax = pos.y(); + rmax = row; + break; + } + } + + for (int col = 0; col < fNumx; col++) { // left-right + auto localPosition = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,col,rmin) ); + auto pos = localPosition + dd4hep::Position(-gridSize/2.,-gridSize/2.,0.); + if ( checkContained(rootTrap,pos,zmin) ) { + xmin = pos.x(); + cmin = col; + break; + } + } + + for (int col = fNumx-1; col!=0; col--) { // right-left + auto localPosition = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,col,rmin) ); + auto pos = localPosition + dd4hep::Position(gridSize/2.,-gridSize/2.,0.); + if ( checkContained(rootTrap,pos,zmin) ) { + xmax = pos.x(); + cmax = col; + break; + } + } + + return dd4hep::Box( (xmax-xmin)/2., (ymax-ymin)/2., dz ); +} + +void ddDRcalo::DRconstructor::placeUnitBox(dd4hep::Volume& fullBox, dd4hep::Volume& unitBox, int rmin, int rmax, int cmin, int cmax, bool& isEvenRow, bool& isEvenCol) { + for (int row = rmin; row < rmax; row+=2) { + for (int col = cmin; col < cmax; col+=2) { + auto pos0 = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,col,row) ); + auto pos3 = dd4hep::Position( fSegmentation->localPosition(fNumx,fNumy,col+1,row+1) ); + fullBox.placeVolume(unitBox,(pos0+pos3)/2.); + } + } + + isEvenRow = (rmax-rmin+1)%2==0; + isEvenCol = (cmax-cmin+1)%2==0; + + return; +} diff --git a/detector/calorimeter/dual-readout/src/FiberDualReadoutCalo_o1_v01.cpp b/detector/calorimeter/dual-readout/src/FiberDualReadoutCalo_o1_v01.cpp new file mode 100644 index 000000000..8e6076265 --- /dev/null +++ b/detector/calorimeter/dual-readout/src/FiberDualReadoutCalo_o1_v01.cpp @@ -0,0 +1,76 @@ +#include "detectorSegmentations/DRparamBarrel_k4geo.h" +#include "detectorSegmentations/DRparamEndcap_k4geo.h" +#include "detectorSegmentations/GridDRcaloHandle_k4geo.h" + +#include "DRconstructor.h" + +#include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/OpticalSurfaces.h" +#include "DD4hep/Printout.h" +#include "DD4hep/Detector.h" + +namespace ddDRcalo { + static dd4hep::Ref_t create_detector( dd4hep::Detector &description, xml_h xmlElement, dd4hep::SensitiveDetector sensDet ) { + + // Get the detector description from the xml-tree + xml_det_t x_det = xmlElement; + std::string name = x_det.nameStr(); + // Create the detector element + dd4hep::DetElement drDet( name, x_det.id() ); + // set the sensitive detector type to the DD4hep calorimeter + dd4hep::xml::Dimension sensDetType = xmlElement.child(_Unicode(sensitive)); + sensDet.setType(sensDetType.typeStr()); + // Get the world volume + dd4hep::Assembly experimentalHall("hall"); + // Get the dimensions defined in the xml-tree + xml_comp_t x_barrel ( x_det.child( _Unicode(barrel) ) ); + xml_comp_t x_endcap ( x_det.child( _Unicode(endcap) ) ); + xml_comp_t x_structure ( x_det.child( _Unicode(structure) ) ); + xml_comp_t x_dim ( x_structure.child( _Unicode(dim) ) ); + xml_comp_t x_sipmDim ( x_det.child( _Unicode(sipmDim) ) ); + + dd4hep::OpticalSurfaceManager surfMgr = description.surfaceManager(); + dd4hep::OpticalSurface sipmSurfProp = surfMgr.opticalSurface("/world/"+name+"#SiPMSurf"); + dd4hep::OpticalSurface mirrorSurfProp = surfMgr.opticalSurface("/world/"+name+"#MirrorSurf"); + surfMgr.opticalSurface("/world/"+name+"#FilterSurf"); // actual filtering applied in the stepping action + + auto segmentation = dynamic_cast( sensDet.readout().segmentation().segmentation() ); + segmentation->setGridSize( x_dim.distance() ); + segmentation->setSipmSize( x_dim.dx() ); + + auto paramBarrel = segmentation->paramBarrel(); + paramBarrel->SetInnerX(x_barrel.rmin()); + paramBarrel->SetTowerH(x_barrel.height()); + paramBarrel->SetNumZRot(x_barrel.nphi()); + paramBarrel->SetSipmHeight(x_sipmDim.height()); + + auto paramEndcap = segmentation->paramEndcap(); + paramEndcap->SetInnerX(x_endcap.rmin()); + paramEndcap->SetTowerH(x_endcap.height()); + paramEndcap->SetNumZRot(x_endcap.nphi()); + paramEndcap->SetSipmHeight(x_sipmDim.height()); + + auto constructor = DRconstructor(x_det); + constructor.setExpHall(&experimentalHall); + constructor.setDRparamBarrel(paramBarrel); + constructor.setDRparamEndcap(paramEndcap); + constructor.setDescription(&description); + constructor.setDetElement(&drDet); + constructor.setSipmSurf(&sipmSurfProp); + constructor.setMirrorSurf(&mirrorSurfProp); + constructor.setSensDet(&sensDet); + constructor.construct(); // right + + dd4hep::Volume worldVol = description.pickMotherVolume(drDet); + dd4hep::PlacedVolume hallPlace = worldVol.placeVolume(experimentalHall); + hallPlace.addPhysVolID("system",x_det.id()); + // connect placed volume and physical volume + drDet.setPlacement( hallPlace ); + + paramBarrel->finalized(); + paramEndcap->finalized(); + + return drDet; + } +} // namespace detector +DECLARE_DETELEMENT(FiberDualReadoutCalo_o1_v01, ddDRcalo::create_detector) // factory method \ No newline at end of file diff --git a/detectorSegmentations/include/detectorSegmentations/DRparamBarrel_k4geo.h b/detectorSegmentations/include/detectorSegmentations/DRparamBarrel_k4geo.h new file mode 100644 index 000000000..47b2bb6af --- /dev/null +++ b/detectorSegmentations/include/detectorSegmentations/DRparamBarrel_k4geo.h @@ -0,0 +1,27 @@ +#ifndef DETSEGMENTATION_DRPARAMBARREL_H +#define DETSEGMENTATION_DRPARAMBARREL_H + +#include "detectorSegmentations/DRparamBase_k4geo.h" + +#include "TVector3.h" +#include "DD4hep/DetFactoryHelper.h" + +#include +#include + +namespace dd4hep { +namespace DDSegmentation { + class DRparamBarrel_k4geo : public DRparamBase_k4geo { + public: + DRparamBarrel_k4geo(); + virtual ~DRparamBarrel_k4geo(); + + virtual void SetDeltaThetaByTowerNo(int signedTowerNo, int) override; + virtual void SetThetaOfCenterByTowerNo(int signedTowerNo, int) override; + + virtual void init() override; + }; +} +} + +#endif diff --git a/detectorSegmentations/include/detectorSegmentations/DRparamBase_k4geo.h b/detectorSegmentations/include/detectorSegmentations/DRparamBase_k4geo.h new file mode 100644 index 000000000..c072eabb5 --- /dev/null +++ b/detectorSegmentations/include/detectorSegmentations/DRparamBase_k4geo.h @@ -0,0 +1,99 @@ +#ifndef DETSEGMENTATION_DRPARAMBASE_H +#define DETSEGMENTATION_DRPARAMBASE_H + +#include "TVector3.h" +#include "DD4hep/DetFactoryHelper.h" + +#include +#include + +namespace dd4hep { +namespace DDSegmentation { + class DRparamBase_k4geo { + public: + DRparamBase_k4geo(); + virtual ~DRparamBase_k4geo(); + + void SetIsRHS(bool isRHS) { fIsRHS = isRHS; } + void SetInnerX(double innerX) { fInnerX = innerX; } + void SetTowerH(double towerH) { fTowerH = towerH; } + void SetNumZRot(int num) { fNumZRot = num; fPhiZRot = 2*M_PI/(double)num; } + void SetDeltaTheta(double theta) { fDeltaTheta = theta; } + void SetThetaOfCenter(double theta) { fThetaOfCenter = theta; } + void SetSipmHeight(double SipmHeight) { fSipmHeight = SipmHeight; } + + bool GetIsRHS() { return fIsRHS; } + double GetCurrentInnerR() { return fCurrentInnerR; } + double GetTowerH() { return fTowerH; } + double GetSipmHeight() { return fSipmHeight; } + double GetH1() { return fCurrentInnerHalf; } + double GetBl1() { return fV3.X()*std::tan(fPhiZRot/2.); } + double GetTl1() { return fV1.X()*std::tan(fPhiZRot/2.); } + double GetH2() { return fCurrentOuterHalf; } + double GetBl2() { return fV4.X()*std::tan(fPhiZRot/2.); } + double GetTl2() { return fV2.X()*std::tan(fPhiZRot/2.); } + + double GetH2sipm() { return fCurrentOuterHalfSipm; } + double GetBl2sipm() { return fV4sipm.X()*std::tan(fPhiZRot/2.); } + double GetTl2sipm() { return fV2sipm.X()*std::tan(fPhiZRot/2.); } + + dd4hep::RotationZYX GetRotationZYX(int numPhi); + dd4hep::Position GetTowerPos(int numPhi); + dd4hep::Position GetAssemblePos(int numPhi); + dd4hep::Position GetSipmLayerPos(int numPhi); + + dd4hep::Transform3D GetTransform3D(int numPhi); + dd4hep::Transform3D GetAssembleTransform3D(int numPhi); + dd4hep::Transform3D GetSipmTransform3D(int numPhi); + + int signedTowerNo(int unsignedTowerNo) { return fIsRHS ? unsignedTowerNo : -unsignedTowerNo-1; } + int unsignedTowerNo(int signedTowerNo) { return signedTowerNo >= 0 ? signedTowerNo : -signedTowerNo-1; } + + virtual void SetDeltaThetaByTowerNo(int , int ) {} + virtual void SetThetaOfCenterByTowerNo(int , int ) {} + void SetIsRHSByTowerNo(int signedTowerNo) { fIsRHS = ( signedTowerNo >=0 ? true : false ); } + + int GetTotTowerNum() { return fTotNum; } + void SetTotTowerNum(int totNum) { fTotNum = totNum; } + + int GetCurrentTowerNum() { return fCurrentTowerNum; } + void SetCurrentTowerNum(int numEta) { fCurrentTowerNum = numEta; } + + virtual void init() {}; + void filled() { fFilled = true; } + void finalized() { fFinalized = true; } + bool IsFinalized() { return fFinalized; } + + protected: + bool fIsRHS; + double fPhiZRot; + double fInnerX; + double fTowerH; + int fNumZRot; + double fDeltaTheta; + double fThetaOfCenter; + double fCurrentInnerR; + TVector3 fCurrentCenter; + TVector3 fV1; + TVector3 fV2; + TVector3 fV3; + TVector3 fV4; + TVector3 fV2sipm; + TVector3 fV4sipm; + double fSipmHeight; + + double fCurrentInnerHalf; + double fCurrentOuterHalf; + double fCurrentOuterHalfSipm; + + int fTotNum; + int fCurrentTowerNum; + std::vector fDeltaThetaVec; + std::vector fThetaOfCenterVec; + bool fFilled; + bool fFinalized; + }; +} +} + +#endif diff --git a/detectorSegmentations/include/detectorSegmentations/DRparamEndcap_k4geo.h b/detectorSegmentations/include/detectorSegmentations/DRparamEndcap_k4geo.h new file mode 100644 index 000000000..d8fb48d4d --- /dev/null +++ b/detectorSegmentations/include/detectorSegmentations/DRparamEndcap_k4geo.h @@ -0,0 +1,27 @@ +#ifndef DETSEGMENTATION_DRPARAMENDCAP_H +#define DETSEGMENTATION_DRPARAMENDCAP_H + +#include "detectorSegmentations/DRparamBase_k4geo.h" + +#include "TVector3.h" +#include "DD4hep/DetFactoryHelper.h" + +#include +#include + +namespace dd4hep { +namespace DDSegmentation { + class DRparamEndcap_k4geo : public DRparamBase_k4geo { + public: + DRparamEndcap_k4geo(); + virtual ~DRparamEndcap_k4geo(); + + virtual void SetDeltaThetaByTowerNo(int signedTowerNo, int BEtrans) override; + virtual void SetThetaOfCenterByTowerNo(int signedTowerNo, int BEtrans) override; + + virtual void init() override; + }; +} +} + +#endif diff --git a/detectorSegmentations/include/detectorSegmentations/GridDRcaloHandle_k4geo.h b/detectorSegmentations/include/detectorSegmentations/GridDRcaloHandle_k4geo.h new file mode 100644 index 000000000..0f777c3dd --- /dev/null +++ b/detectorSegmentations/include/detectorSegmentations/GridDRcaloHandle_k4geo.h @@ -0,0 +1,90 @@ +#ifndef DD4HEP_DDCORE_GRIDDRCALO_H +#define DD4HEP_DDCORE_GRIDDRCALO_H 1 + +#include "detectorSegmentations/GridDRcalo_k4geo.h" + +#include "DD4hep/Segmentations.h" +#include "DD4hep/detail/SegmentationsInterna.h" + +namespace dd4hep { + class Segmentation; + template class SegmentationWrapper; + + typedef Handle> GridDRcaloHandle; + + class GridDRcalo_k4geo : public GridDRcaloHandle { + typedef GridDRcaloHandle::Object Object; + + public: + /// Default constructor + GridDRcalo_k4geo() = default; + /// Copy constructor + GridDRcalo_k4geo(const GridDRcalo_k4geo& e) = default; + /// Copy Constructor from segmentation base object + GridDRcalo_k4geo(const Segmentation& e) : Handle(e) {} + /// Copy constructor from handle + GridDRcalo_k4geo(const Handle& e) : Handle(e) {} + /// Copy constructor from other polymorph/equivalent handle + template + GridDRcalo_k4geo(const Handle& e) : Handle(e) {} + /// Assignment operator + GridDRcalo_k4geo& operator=(const GridDRcalo_k4geo& seg) = default; + /// Equality operator + bool operator==(const GridDRcalo_k4geo& seg) const { return m_element == seg.m_element; } + /// determine the position based on the cell ID + inline Position position(const CellID& id) const { return Position(access()->implementation->position(id)); } + inline Position localPosition(const CellID& id) const { return Position(access()->implementation->localPosition(id)); } + inline Position localPosition(int numx, int numy, int x_, int y_) const { return Position(access()->implementation->localPosition(numx,numy,x_,y_)); } + + /// determine the cell ID based on the position + inline dd4hep::CellID cellID(const Position& local, const Position& global, const VolumeID& volID) const { + return access()->implementation->cellID(local, global, volID); + } + + inline VolumeID setVolumeID(int numEta, int numPhi) const { return access()->implementation->setVolumeID(numEta,numPhi); } + inline CellID setCellID(int numEta, int numPhi, int x, int y) const { return access()->implementation->setCellID(numEta, numPhi, x, y); } + + inline void setGridSize(double grid) { access()->implementation->setGridSize(grid); } + + // Get the identifier number of a mother tower in eta or phi direction + inline int numEta(const CellID& aCellID) const { return access()->implementation->numEta(aCellID); } + inline int numPhi(const CellID& aCellID) const { return access()->implementation->numPhi(aCellID); } + + // Get the total number of SiPMs of the mother tower in x or y direction (local coordinate) + inline int numX(const CellID& aCellID) const { return access()->implementation->numX(aCellID); } + inline int numY(const CellID& aCellID) const { return access()->implementation->numY(aCellID); } + + // Get the identifier number of a SiPM in x or y direction (local coordinate) + inline int x(const CellID& aCellID) const { return access()->implementation->x(aCellID); } // approx eta direction + inline int y(const CellID& aCellID) const { return access()->implementation->y(aCellID); } // approx phi direction + + inline bool IsCerenkov(const CellID& aCellID) const { return access()->implementation->IsCerenkov(aCellID); } + inline bool IsCerenkov(int col, int row) const { return access()->implementation->IsCerenkov(col, row); } + + inline bool IsTower(const CellID& aCellID) const { return access()->implementation->IsTower(aCellID); } + inline bool IsSiPM(const CellID& aCellID) const { return access()->implementation->IsSiPM(aCellID); } + + inline int getFirst32bits(const CellID& aCellID) const { return access()->implementation->getFirst32bits(aCellID); } + inline int getLast32bits(const CellID& aCellID) const { return access()->implementation->getLast32bits(aCellID); } + + inline CellID convertFirst32to64(const int aId32) const { return access()->implementation->convertFirst32to64(aId32); } + inline CellID convertLast32to64(const int aId32) const { return access()->implementation->convertLast32to64(aId32); } + + // Methods for 32bit to 64bit en/decoder + inline int numEta(const int& aId32) const { return access()->implementation->numEta(aId32); } + inline int numPhi(const int& aId32) const { return access()->implementation->numPhi(aId32); } + + inline int numX(const int& aId32) const { return access()->implementation->numX(aId32); } + inline int numY(const int& aId32) const { return access()->implementation->numY(aId32); } + + inline int x(const int& aId32) const { return access()->implementation->x(aId32); } + inline int y(const int& aId32) const { return access()->implementation->y(aId32); } + + inline bool IsCerenkov(const int& aId32) const { return access()->implementation->IsCerenkov(aId32); } + + inline bool IsTower(const int& aId32) const { return access()->implementation->IsTower(aId32); } + inline bool IsSiPM(const int& aId32) const { return access()->implementation->IsSiPM(aId32); } + }; +} + +#endif diff --git a/detectorSegmentations/include/detectorSegmentations/GridDRcalo_k4geo.h b/detectorSegmentations/include/detectorSegmentations/GridDRcalo_k4geo.h new file mode 100644 index 000000000..4df5e4f41 --- /dev/null +++ b/detectorSegmentations/include/detectorSegmentations/GridDRcalo_k4geo.h @@ -0,0 +1,109 @@ +#ifndef DETSEGMENTATION_GRIDDRCALO_H +#define DETSEGMENTATION_GRIDDRCALO_H + +#include "detectorSegmentations/DRparamBarrel_k4geo.h" +#include "detectorSegmentations/DRparamEndcap_k4geo.h" + +#include "DDSegmentation/Segmentation.h" + +namespace dd4hep { +namespace DDSegmentation { +class GridDRcalo_k4geo : public Segmentation { +public: + /// default constructor using an arbitrary type + GridDRcalo_k4geo(const std::string& aCellEncoding); + /// Default constructor used by derived classes passing an existing decoder + GridDRcalo_k4geo(const BitFieldCoder* decoder); + /// destructor + virtual ~GridDRcalo_k4geo() override; + + // Determine the global(local) position based on the cell ID. + virtual Vector3D position(const CellID& aCellID) const; + Vector3D localPosition(const CellID& aCellID) const; + Vector3D localPosition(int numx, int numy, int x_, int y_) const; + + virtual CellID cellID(const Vector3D& aLocalPosition, const Vector3D& aGlobalPosition, + const VolumeID& aVolumeID) const; + + VolumeID setVolumeID(int numEta, int numPhi) const; + CellID setCellID(int numEta, int numPhi, int x, int y) const; + + void setGridSize(double grid) { fGridSize = grid; } + void setSipmSize(double sipm) { fSipmSize = sipm; } + + // Get the identifier number of a mother tower in eta or phi direction + int numEta(const CellID& aCellID) const; + int numPhi(const CellID& aCellID) const; + + // Get the total number of SiPMs of the mother tower in x or y direction (local coordinate) + int numX(const CellID& aCellID) const; + int numY(const CellID& aCellID) const; + + // Get the identifier number of a SiPM in x or y direction (local coordinate) + int x(const CellID& aCellID) const; // approx eta direction + int y(const CellID& aCellID) const; // approx phi direction + + bool IsCerenkov(const CellID& aCellID) const; + bool IsCerenkov(int col, int row) const; + + bool IsTower(const CellID& aCellID) const; + bool IsSiPM(const CellID& aCellID) const; + + int getFirst32bits(const CellID& aCellID) const { return (int)aCellID; } + int getLast32bits(const CellID& aCellID) const; + CellID convertFirst32to64(const int aId32) const { return (CellID)aId32; } + CellID convertLast32to64(const int aId32) const; + + // Methods for 32bit to 64bit en/decoder + int numEta(const int& aId32) const { return numEta( convertFirst32to64(aId32) ); } + int numPhi(const int& aId32) const { return numPhi( convertFirst32to64(aId32) ); } + + int numX(const int& aId32) const { return numX( convertFirst32to64(aId32) ); } + int numY(const int& aId32) const { return numY( convertFirst32to64(aId32) ); } + + int x(const int& aId32) const { return x( convertLast32to64(aId32) ); } + int y(const int& aId32) const { return y( convertLast32to64(aId32) ); } + + bool IsCerenkov(const int& aId32) const { return IsCerenkov( convertLast32to64(aId32) ); } + + bool IsTower(const int& aId32) const { return IsTower( convertLast32to64(aId32) ); } + bool IsSiPM(const int& aId32) const { return IsSiPM( convertLast32to64(aId32) ); } + + inline const std::string& fieldNameNumEta() const { return fNumEtaId; } + inline const std::string& fieldNameNumPhi() const { return fNumPhiId; } + inline const std::string& fieldNameX() const { return fXId; } + inline const std::string& fieldNameY() const { return fYId; } + inline const std::string& fieldNameIsCerenkov() const { return fIsCerenkovId; } + inline const std::string& fieldNameModule() const { return fModule; } + + inline void setFieldNameNumEta(const std::string& fieldName) { fNumEtaId = fieldName; } + inline void setFieldNameNumPhi(const std::string& fieldName) { fNumPhiId = fieldName; } + inline void setFieldNameX(const std::string& fieldName) { fXId = fieldName; } + inline void setFieldNameY(const std::string& fieldName) { fYId = fieldName; } + inline void setFieldNameIsCerenkov(const std::string& fieldName) { fIsCerenkovId = fieldName; } + inline void setFieldNameModule(const std::string& fieldName) { fModule = fieldName; } + + DRparamBarrel_k4geo* paramBarrel() { return fParamBarrel; } + DRparamEndcap_k4geo* paramEndcap() { return fParamEndcap; } + + DRparamBase_k4geo* setParamBase(int noEta) const; + +protected: + std::string fNumEtaId; + std::string fNumPhiId; + std::string fXId; + std::string fYId; + std::string fIsCerenkovId; + std::string fModule; + + double fGridSize; + double fSipmSize; + +private: + DRparamBarrel_k4geo* fParamBarrel; + DRparamEndcap_k4geo* fParamEndcap; +}; +} +} + +#endif diff --git a/detectorSegmentations/src/DRparamBarrel_k4geo.cpp b/detectorSegmentations/src/DRparamBarrel_k4geo.cpp new file mode 100644 index 000000000..7d5f617e0 --- /dev/null +++ b/detectorSegmentations/src/DRparamBarrel_k4geo.cpp @@ -0,0 +1,98 @@ +#include "detectorSegmentations/DRparamBarrel_k4geo.h" + +#include "Math/GenVector/RotationZYX.h" + +#include + +namespace dd4hep { +namespace DDSegmentation { + +DRparamBarrel_k4geo::DRparamBarrel_k4geo() { + fIsRHS = 0; + fPhiZRot = 0.; + fInnerX = 0.; + fTowerH = 0.; + fNumZRot = 0; + fDeltaTheta = 0.; + fThetaOfCenter = 0.; + fCurrentInnerR = 0.; + fPhiZRot = 0; + fCurrentCenter = TVector3(); + fV1 = TVector3(); + fV2 = TVector3(); + fV3 = TVector3(); + fV4 = TVector3(); + fSipmHeight = 0.; + fCurrentInnerHalf = 0.; + fCurrentOuterHalf = 0.; + fFilled = false; + fFinalized = false; +} + +DRparamBarrel_k4geo::~DRparamBarrel_k4geo() {} + +void DRparamBarrel_k4geo::init() { + fCurrentInnerR = fInnerX/std::cos(fThetaOfCenter); + double trnsLength = fTowerH/2.+fCurrentInnerR; + fCurrentCenter = TVector3(std::cos(fThetaOfCenter)*trnsLength,0.,std::sin(fThetaOfCenter)*trnsLength); + + fCurrentInnerHalf = fCurrentInnerR*std::tan(fDeltaTheta/2.); + fCurrentOuterHalf = (fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.); + fCurrentOuterHalfSipm = (fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.); + + fV1 = TVector3( + std::cos(fThetaOfCenter)*fCurrentInnerR+std::sin(fThetaOfCenter)*fCurrentInnerR*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*fCurrentInnerR-std::cos(fThetaOfCenter)*fCurrentInnerR*std::tan(fDeltaTheta/2.) + ); + + fV2 = TVector3( + std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH)+std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH)-std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.) + ); + + fV3 = TVector3( + std::cos(fThetaOfCenter)*fCurrentInnerR-std::sin(fThetaOfCenter)*fCurrentInnerR*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*fCurrentInnerR+std::cos(fThetaOfCenter)*fCurrentInnerR*std::tan(fDeltaTheta/2.) + ); + + fV4 = TVector3( + std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH)-std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH)+std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.) + ); + + fV2sipm = TVector3( + std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)+std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)-std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.) + ); + + fV4sipm = TVector3( + std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)-std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)+std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.) + ); + + if (!fFilled) { + fDeltaThetaVec.push_back(fDeltaTheta); + fThetaOfCenterVec.push_back(fThetaOfCenter); + } +} + +void DRparamBarrel_k4geo::SetDeltaThetaByTowerNo(int signedTowerNo, int) { + if (!fFilled) throw std::runtime_error("Attempt to set by tower num while barrel parameter is not filled!"); + + fDeltaTheta = fDeltaThetaVec.at( unsignedTowerNo(signedTowerNo) ); +} + +void DRparamBarrel_k4geo::SetThetaOfCenterByTowerNo(int signedTowerNo, int) { + if (!fFilled) throw std::runtime_error("Attempt to set by tower num while barrel parameter is not filled!"); + + fThetaOfCenter = fThetaOfCenterVec.at( unsignedTowerNo(signedTowerNo) ); +} + +} +} diff --git a/detectorSegmentations/src/DRparamBase_k4geo.cpp b/detectorSegmentations/src/DRparamBase_k4geo.cpp new file mode 100644 index 000000000..e5912f025 --- /dev/null +++ b/detectorSegmentations/src/DRparamBase_k4geo.cpp @@ -0,0 +1,100 @@ +#include "detectorSegmentations/DRparamBase_k4geo.h" + +#include "Math/GenVector/RotationZYX.h" + +#include + +namespace dd4hep { +namespace DDSegmentation { + +DRparamBase_k4geo::DRparamBase_k4geo() { + fIsRHS = 0; + fPhiZRot = 0.; + fInnerX = 0.; + fTowerH = 0.; + fNumZRot = 0; + fDeltaTheta = 0.; + fThetaOfCenter = 0.; + fCurrentInnerR = 0.; + fPhiZRot = 0; + fCurrentCenter = TVector3(); + fV1 = TVector3(); + fV2 = TVector3(); + fV3 = TVector3(); + fV4 = TVector3(); + fSipmHeight = 0.; + fCurrentInnerHalf = 0.; + fCurrentOuterHalf = 0.; + fCurrentTowerNum = 0; + fFilled = false; + fFinalized = false; +} + +DRparamBase_k4geo::~DRparamBase_k4geo() {} + +dd4hep::RotationZYX DRparamBase_k4geo::GetRotationZYX(int numPhi) { + double numPhi_ = (double)numPhi; + double xRot = fIsRHS ? -fThetaOfCenter : fThetaOfCenter; + double zRot = fIsRHS ? -M_PI/2. : M_PI/2.; + dd4hep::RotationZYX rot = dd4hep::RotationZYX(zRot, M_PI/2.+xRot, 0.); + ROOT::Math::RotationZ rotZ = ROOT::Math::RotationZ(numPhi_*fPhiZRot); + rot = rotZ*rot; + + return rot; +} + +dd4hep::Position DRparamBase_k4geo::GetTowerPos(int numPhi) { + double numPhi_ = (double)numPhi; + double x = std::cos(numPhi_*fPhiZRot)*fCurrentCenter.X(); + double y = std::sin(numPhi_*fPhiZRot)*fCurrentCenter.X(); + double z = fIsRHS ? fCurrentCenter.Z() : -fCurrentCenter.Z(); + dd4hep::Position pos = dd4hep::Position(x,y,z); + + return pos; +} + +dd4hep::Position DRparamBase_k4geo::GetAssemblePos(int numPhi) { + double numPhi_ = (double)numPhi; + double x = std::cos(numPhi_*fPhiZRot)*fCurrentCenter.X()*(fCurrentCenter.Mag()+fSipmHeight/2.)/fCurrentCenter.Mag(); + double y = std::sin(numPhi_*fPhiZRot)*fCurrentCenter.X()*(fCurrentCenter.Mag()+fSipmHeight/2.)/fCurrentCenter.Mag(); + double z_abs = fCurrentCenter.Z()*(fCurrentCenter.Mag()+fSipmHeight/2.)/fCurrentCenter.Mag(); + double z = fIsRHS ? z_abs : -z_abs; + dd4hep::Position pos = dd4hep::Position(x,y,z); + + return pos; +} + +dd4hep::Position DRparamBase_k4geo::GetSipmLayerPos(int numPhi) { + double numPhi_ = (double)numPhi; + double x = std::cos(numPhi_*fPhiZRot)*fCurrentCenter.X()*(fCurrentCenter.Mag()+fTowerH/2.+fSipmHeight/2.)/fCurrentCenter.Mag(); + double y = std::sin(numPhi_*fPhiZRot)*fCurrentCenter.X()*(fCurrentCenter.Mag()+fTowerH/2.+fSipmHeight/2.)/fCurrentCenter.Mag(); + double z_abs = fCurrentCenter.Z()*(fCurrentCenter.Mag()+fTowerH/2.+fSipmHeight/2.)/fCurrentCenter.Mag(); + double z = fIsRHS ? z_abs : -z_abs; + dd4hep::Position pos = dd4hep::Position(x,y,z); + + return pos; +} + +dd4hep::Transform3D DRparamBase_k4geo::GetTransform3D(int numPhi) { + auto rot = GetRotationZYX(numPhi); + auto pos = GetTowerPos(numPhi); + + return dd4hep::Transform3D(rot,pos); +} + +dd4hep::Transform3D DRparamBase_k4geo::GetAssembleTransform3D(int numPhi) { + auto rot = GetRotationZYX(numPhi); + auto pos = GetAssemblePos(numPhi); + + return dd4hep::Transform3D(rot,pos); +} + +dd4hep::Transform3D DRparamBase_k4geo::GetSipmTransform3D(int numPhi) { + auto rot = GetRotationZYX(numPhi); + auto pos = GetSipmLayerPos(numPhi); + + return dd4hep::Transform3D(rot,pos); +} + +} +} diff --git a/detectorSegmentations/src/DRparamEndcap_k4geo.cpp b/detectorSegmentations/src/DRparamEndcap_k4geo.cpp new file mode 100644 index 000000000..d8b9e2b0d --- /dev/null +++ b/detectorSegmentations/src/DRparamEndcap_k4geo.cpp @@ -0,0 +1,98 @@ +#include "detectorSegmentations/DRparamEndcap_k4geo.h" + +#include "Math/GenVector/RotationZYX.h" + +#include + +namespace dd4hep { +namespace DDSegmentation { + +DRparamEndcap_k4geo::DRparamEndcap_k4geo() { + fIsRHS = 0; + fPhiZRot = 0.; + fInnerX = 0.; + fTowerH = 0.; + fNumZRot = 0; + fDeltaTheta = 0.; + fThetaOfCenter = 0.; + fCurrentInnerR = 0.; + fPhiZRot = 0; + fCurrentCenter = TVector3(); + fV1 = TVector3(); + fV2 = TVector3(); + fV3 = TVector3(); + fV4 = TVector3(); + fSipmHeight = 0.; + fCurrentInnerHalf = 0.; + fCurrentOuterHalf = 0.; + fFilled = false; + fFinalized = false; +} + +DRparamEndcap_k4geo::~DRparamEndcap_k4geo() {} + +void DRparamEndcap_k4geo::init() { + fCurrentInnerR = fInnerX/std::sin(fThetaOfCenter); + double trnsLength = fTowerH/2.+fCurrentInnerR; + fCurrentCenter = TVector3(std::cos(fThetaOfCenter)*trnsLength,0.,std::sin(fThetaOfCenter)*trnsLength); + + fCurrentInnerHalf = fCurrentInnerR*std::tan(fDeltaTheta/2.); + fCurrentOuterHalf = (fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.); + fCurrentOuterHalfSipm = (fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.); + + fV1 = TVector3( + std::cos(fThetaOfCenter)*fCurrentInnerR+std::sin(fThetaOfCenter)*fCurrentInnerR*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*fCurrentInnerR-std::cos(fThetaOfCenter)*fCurrentInnerR*std::tan(fDeltaTheta/2.) + ); + + fV2 = TVector3( + std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH)+std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH)-std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.) + ); + + fV3 = TVector3( + std::cos(fThetaOfCenter)*fCurrentInnerR-std::sin(fThetaOfCenter)*fCurrentInnerR*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*fCurrentInnerR+std::cos(fThetaOfCenter)*fCurrentInnerR*std::tan(fDeltaTheta/2.) + ); + + fV4 = TVector3( + std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH)-std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH)+std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH)*std::tan(fDeltaTheta/2.) + ); + + fV2sipm = TVector3( + std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)+std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)-std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.) + ); + + fV4sipm = TVector3( + std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)-std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.), + 0., + std::sin(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)+std::cos(fThetaOfCenter)*(fCurrentInnerR+fTowerH+fSipmHeight)*std::tan(fDeltaTheta/2.) + ); + + if (!fFilled) { + fDeltaThetaVec.push_back(fDeltaTheta); + fThetaOfCenterVec.push_back(fThetaOfCenter); + } +} + +void DRparamEndcap_k4geo::SetDeltaThetaByTowerNo(int signedTowerNo, int BEtrans) { + if (!fFilled) throw std::runtime_error("Attempt to set by tower num while endcap parameter is not filled!"); + + fDeltaTheta = fDeltaThetaVec.at( unsignedTowerNo(signedTowerNo) - unsignedTowerNo(BEtrans) ); +} + +void DRparamEndcap_k4geo::SetThetaOfCenterByTowerNo(int signedTowerNo, int BEtrans) { + if (!fFilled) throw std::runtime_error("Attempt to set by tower num while endcap parameter is not filled!"); + + fThetaOfCenter = fThetaOfCenterVec.at( unsignedTowerNo(signedTowerNo) - unsignedTowerNo(BEtrans) ); +} + +} +} diff --git a/detectorSegmentations/src/GridDRcaloHandle_k4geo.cpp b/detectorSegmentations/src/GridDRcaloHandle_k4geo.cpp new file mode 100644 index 000000000..88bc40586 --- /dev/null +++ b/detectorSegmentations/src/GridDRcaloHandle_k4geo.cpp @@ -0,0 +1,4 @@ +#include "detectorSegmentations/GridDRcaloHandle_k4geo.h" +#include "DD4hep/detail/Handle.inl" + +DD4HEP_INSTANTIATE_HANDLE_UNNAMED(dd4hep::DDSegmentation::GridDRcalo_k4geo); diff --git a/detectorSegmentations/src/GridDRcalo_k4geo.cpp b/detectorSegmentations/src/GridDRcalo_k4geo.cpp new file mode 100644 index 000000000..069de02be --- /dev/null +++ b/detectorSegmentations/src/GridDRcalo_k4geo.cpp @@ -0,0 +1,232 @@ +#include "detectorSegmentations/GridDRcalo_k4geo.h" + +#include +#include +#include + +namespace dd4hep { +namespace DDSegmentation { + +/// default constructor using an encoding string +GridDRcalo_k4geo::GridDRcalo_k4geo(const std::string& cellEncoding) : Segmentation(cellEncoding) { + // define type and description + _type = "GridDRcalo_k4geo"; + _description = "DRcalo segmentation based on the tower / (Cerenkov or Scintillation) fiber / SiPM hierarchy"; + + // register all necessary parameters + registerIdentifier("identifier_eta", "Cell ID identifier for numEta", fNumEtaId, "eta"); + registerIdentifier("identifier_phi", "Cell ID identifier for numPhi", fNumPhiId, "phi"); + registerIdentifier("identifier_x", "Cell ID identifier for x", fXId, "x"); + registerIdentifier("identifier_y", "Cell ID identifier for y", fYId, "y"); + registerIdentifier("identifier_IsCerenkov", "Cell ID identifier for IsCerenkov", fIsCerenkovId, "c"); + registerIdentifier("identifier_module", "Cell ID identifier for module", fModule, "module"); + + fParamBarrel = new DRparamBarrel_k4geo(); + fParamEndcap = new DRparamEndcap_k4geo(); +} + +GridDRcalo_k4geo::GridDRcalo_k4geo(const BitFieldCoder* decoder) : Segmentation(decoder) { + // define type and description + _type = "GridDRcalo_k4geo"; + _description = "DRcalo segmentation based on the tower / (Cerenkov or Scintillation) fiber / SiPM hierarchy"; + + // register all necessary parameters + registerIdentifier("identifier_eta", "Cell ID identifier for numEta", fNumEtaId, "eta"); + registerIdentifier("identifier_phi", "Cell ID identifier for numPhi", fNumPhiId, "phi"); + registerIdentifier("identifier_x", "Cell ID identifier for x", fXId, "x"); + registerIdentifier("identifier_y", "Cell ID identifier for y", fYId, "y"); + registerIdentifier("identifier_IsCerenkov", "Cell ID identifier for IsCerenkov", fIsCerenkovId, "c"); + registerIdentifier("identifier_module", "Cell ID identifier for module", fModule, "module"); + + fParamBarrel = new DRparamBarrel_k4geo(); + fParamEndcap = new DRparamEndcap_k4geo(); +} + +GridDRcalo_k4geo::~GridDRcalo_k4geo() { + if (fParamBarrel) delete fParamBarrel; + if (fParamEndcap) delete fParamEndcap; +} + +Vector3D GridDRcalo_k4geo::position(const CellID& cID) const { + int noEta = numEta(cID); + int noPhi = numPhi(cID); + + DRparamBase_k4geo* paramBase = setParamBase(noEta); + + auto transformA = paramBase->GetSipmTransform3D(noPhi); + dd4hep::Position localPos = dd4hep::Position(0.,0.,0.); + if ( IsSiPM(cID) ) localPos = dd4hep::Position( localPosition(cID) ); + + dd4hep::RotationZYX rot = dd4hep::RotationZYX(M_PI, 0., 0.); // AdHoc rotation, potentially bug + dd4hep::Transform3D transformB = dd4hep::Transform3D(rot,localPos); + auto total = transformA*transformB; + + dd4hep::Position translation = dd4hep::Position(0.,0.,0.); + total.GetTranslation(translation); + + return Vector3D(translation.x(),translation.y(),translation.z()); +} + +Vector3D GridDRcalo_k4geo::localPosition(const CellID& cID) const { + int numx = numX(cID); + int numy = numY(cID); + int x_ = x(cID); + int y_ = y(cID); + + return localPosition(numx,numy,x_,y_); +} + +Vector3D GridDRcalo_k4geo::localPosition(int numx, int numy, int x_, int y_) const { + float ptX = -fGridSize*static_cast(numx/2) + static_cast(x_)*fGridSize + ( numx%2==0 ? fGridSize/2. : 0. ); + float ptY = -fGridSize*static_cast(numy/2) + static_cast(y_)*fGridSize + ( numy%2==0 ? fGridSize/2. : 0. ); + + return Vector3D(ptX,ptY,0.); +} + +/// determine the cell ID based on the position +CellID GridDRcalo_k4geo::cellID(const Vector3D& localPosition, const Vector3D& /*globalPosition*/, const VolumeID& vID) const { + int numx = numX(vID); + int numy = numY(vID); + + auto localX = localPosition.x(); + auto localY = localPosition.y(); + + int x = std::floor( ( localX + ( numx%2==0 ? 0. : fGridSize/2. ) ) / fGridSize ) + numx/2; + int y = std::floor( ( localY + ( numy%2==0 ? 0. : fGridSize/2. ) ) / fGridSize ) + numy/2; + + return setCellID( numEta(vID), numPhi(vID), x, y ); +} + +VolumeID GridDRcalo_k4geo::setVolumeID(int numEta, int numPhi) const { + VolumeID numEtaId = static_cast(numEta); + VolumeID numPhiId = static_cast(numPhi); + VolumeID vID = 0; + _decoder->set(vID, fNumEtaId, numEtaId); + _decoder->set(vID, fNumPhiId, numPhiId); + + VolumeID module = 0; // Tower, SiPM layer attached to the tower, etc. + _decoder->set(vID, fModule, module); + + return vID; +} + +CellID GridDRcalo_k4geo::setCellID(int numEta, int numPhi, int x, int y) const { + VolumeID numEtaId = static_cast(numEta); + VolumeID numPhiId = static_cast(numPhi); + VolumeID xId = static_cast(x); + VolumeID yId = static_cast(y); + VolumeID vID = 0; + _decoder->set(vID, fNumEtaId, numEtaId); + _decoder->set(vID, fNumPhiId, numPhiId); + _decoder->set(vID, fXId, xId); + _decoder->set(vID, fYId, yId); + + VolumeID module = 1; // Fiber, SiPM, etc. + _decoder->set(vID, fModule, module); + + VolumeID isCeren = IsCerenkov(x,y) ? 1 : 0; + _decoder->set(vID, fIsCerenkovId, isCeren); + + return vID; +} + +// Get the identifier number of a mother tower in eta or phi direction +int GridDRcalo_k4geo::numEta(const CellID& aCellID) const { + VolumeID numEta = static_cast(_decoder->get(aCellID, fNumEtaId)); + return static_cast(numEta); +} + +int GridDRcalo_k4geo::numPhi(const CellID& aCellID) const { + VolumeID numPhi = static_cast(_decoder->get(aCellID, fNumPhiId)); + return static_cast(numPhi); +} + +// Get the total number of SiPMs of the mother tower in x or y direction (local coordinate) +int GridDRcalo_k4geo::numX(const CellID& aCellID) const { + int noEta = numEta(aCellID); + + DRparamBase_k4geo* paramBase = setParamBase(noEta); + + int noX = static_cast( std::floor( ( paramBase->GetTl2()*2. - fSipmSize )/fGridSize ) ) + 1; // in phi direction + + return noX; +} + +int GridDRcalo_k4geo::numY(const CellID& aCellID) const { + int noEta = numEta(aCellID); + + DRparamBase_k4geo* paramBase = setParamBase(noEta); + + int noY = static_cast( std::floor( ( paramBase->GetH2()*2. - fSipmSize )/fGridSize ) ) + 1; // in eta direction + + return noY; +} + +// Get the identifier number of a SiPM in x or y direction (local coordinate) +int GridDRcalo_k4geo::x(const CellID& aCellID) const { // approx eta direction + VolumeID x = static_cast(_decoder->get(aCellID, fXId)); + return static_cast(x); +} +int GridDRcalo_k4geo::y(const CellID& aCellID) const { // approx phi direction + VolumeID y = static_cast(_decoder->get(aCellID, fYId)); + return static_cast(y); +} + +bool GridDRcalo_k4geo::IsCerenkov(const CellID& aCellID) const { + VolumeID isCeren = static_cast(_decoder->get(aCellID, fIsCerenkovId)); + return static_cast(isCeren); +} + +bool GridDRcalo_k4geo::IsCerenkov(int col, int row) const { + bool isCeren = false; + if ( col%2 == 1 ) { isCeren = !isCeren; } + if ( row%2 == 1 ) { isCeren = !isCeren; } + return isCeren; +} + +bool GridDRcalo_k4geo::IsTower(const CellID& aCellID) const { + VolumeID module = static_cast(_decoder->get(aCellID, fModule)); + return module==0; +} + +bool GridDRcalo_k4geo::IsSiPM(const CellID& aCellID) const { + VolumeID module = static_cast(_decoder->get(aCellID, fModule)); + return module==1; +} + +int GridDRcalo_k4geo::getLast32bits(const CellID& aCellID) const { + CellID aId64 = aCellID >> sizeof(int)*CHAR_BIT; + int aId32 = (int)aId64; + + return aId32; +} + +CellID GridDRcalo_k4geo::convertLast32to64(const int aId32) const { + CellID aId64 = (CellID)aId32; + aId64 <<= sizeof(int)*CHAR_BIT; + + return aId64; +} + +DRparamBase_k4geo* GridDRcalo_k4geo::setParamBase(int noEta) const { + DRparamBase_k4geo* paramBase = nullptr; + + if ( fParamEndcap->unsignedTowerNo(noEta) >= fParamBarrel->GetTotTowerNum() ) paramBase = static_cast(fParamEndcap); + else paramBase = static_cast(fParamBarrel); + + if ( paramBase->GetCurrentTowerNum()==noEta ) return paramBase; + + // This should not be called while building detector geometry + if (!paramBase->IsFinalized()) throw std::runtime_error("GridDRcalo_k4geo::position should not be called while building detector geometry!"); + + paramBase->SetDeltaThetaByTowerNo(noEta, fParamBarrel->GetTotTowerNum()); + paramBase->SetThetaOfCenterByTowerNo(noEta, fParamBarrel->GetTotTowerNum()); + paramBase->SetIsRHSByTowerNo(noEta); + paramBase->SetCurrentTowerNum(noEta); + paramBase->init(); + + return paramBase; +} + +} +} diff --git a/detectorSegmentations/src/plugins/SegmentationFactories.cpp b/detectorSegmentations/src/plugins/SegmentationFactories.cpp index 4dd15369a..4a5f78fb7 100644 --- a/detectorSegmentations/src/plugins/SegmentationFactories.cpp +++ b/detectorSegmentations/src/plugins/SegmentationFactories.cpp @@ -29,4 +29,5 @@ DECLARE_SEGMENTATION(GridRPhiEta_k4geo, create_segmentation) - +#include "detectorSegmentations/GridDRcalo_k4geo.h" +DECLARE_SEGMENTATION(GridDRcalo_k4geo, create_segmentation) diff --git a/plugins/DRCaloFastSimModel.cpp b/plugins/DRCaloFastSimModel.cpp new file mode 100644 index 000000000..7661fb981 --- /dev/null +++ b/plugins/DRCaloFastSimModel.cpp @@ -0,0 +1,382 @@ +// Framework include files +#include +#include +#include + +// Geant4 include files +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// C/C++ include files +#include "DRCaloFastSimModel.h" + +/// Namespace for the AIDA detector description toolkit +namespace dd4hep +{ + + /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit + namespace sim + { + + class DRCFiberModel + { + public: + // G4FastSimHitMaker hitMaker { }; + G4OpBoundaryProcess *pOpBoundaryProc{nullptr}; + G4OpAbsorption *pOpAbsorption{nullptr}; + G4OpWLS *pOpWLS{nullptr}; + G4bool fProcAssigned{false}; + + FastFiberData mDataPrevious{FastFiberData(0, 0., 0., 0., G4ThreeVector(0), G4ThreeVector(0), G4ThreeVector(0))}; + FastFiberData mDataCurrent{FastFiberData(0, 0., 0., 0., G4ThreeVector(0), G4ThreeVector(0), G4ThreeVector(0))}; + + G4int fSafety{2}; + G4double mNtransport{0.}; + G4double mTransportUnit{0.}; + G4ThreeVector mFiberPos{G4ThreeVector(0)}; + G4ThreeVector mFiberAxis{G4ThreeVector(0)}; + G4bool fKill{false}; + G4bool fTransported{false}; + G4bool fSwitch{true}; + G4int fVerbose{0}; + + G4bool checkTotalInternalReflection(const G4Track *track) + { + if (!fProcAssigned) + setPostStepProc(track); // locate OpBoundaryProcess only once + + if (track->GetTrackStatus() == fStopButAlive || track->GetTrackStatus() == fStopAndKill) + return false; + + // accumulate step length + mDataCurrent.AddStepLengthInterval(track->GetStepLength()); + + G4int theStatus = pOpBoundaryProc->GetStatus(); + + if (fVerbose > 1) + { + G4cout << "DRCFiberModel::checkTotalInternalReflection | TrackID = " << std::setw(4) << track->GetTrackID(); + G4cout << " | G4OpBoundaryProcessStatus = " << std::setw(2) << theStatus; + G4cout << " | StepLength = " << std::setw(9) << track->GetStepLength() << G4endl; + } + + // skip exceptional iteration with FresnelReflection + if (theStatus == G4OpBoundaryProcessStatus::FresnelReflection) + mDataCurrent.SetOpBoundaryStatus(theStatus); + + // some cases have a status StepTooSmall when the reflection happens between the boundary of cladding & outer volume (outside->cladding) since the outer volume is not a G4Region + if (theStatus == G4OpBoundaryProcessStatus::TotalInternalReflection || theStatus == G4OpBoundaryProcessStatus::StepTooSmall) + { + if (theStatus != G4OpBoundaryProcessStatus::TotalInternalReflection) + { // skip StepTooSmall if the track already has TotalInternalReflection + if (mDataCurrent.GetOpBoundaryStatus() == G4OpBoundaryProcessStatus::TotalInternalReflection) + return false; + if (mDataPrevious.GetOpBoundaryStatus() == G4OpBoundaryProcessStatus::TotalInternalReflection) + return false; + } + + G4int trackID = track->GetTrackID(); + G4double kineticEnergy = track->GetKineticEnergy(); + G4double globalTime = track->GetGlobalTime(); + G4double pathLength = track->GetStepLength(); + G4ThreeVector globalPosition = track->GetPosition(); + G4ThreeVector momentumDirection = track->GetMomentumDirection(); + G4ThreeVector polarization = track->GetPolarization(); + + auto candidate = FastFiberData(trackID, kineticEnergy, globalTime, pathLength, globalPosition, momentumDirection, polarization, theStatus); + if (pOpAbsorption != nullptr) + candidate.SetAbsorptionNILL(pOpAbsorption->GetNumberOfInteractionLengthLeft()); + if (pOpWLS != nullptr) + candidate.SetWLSNILL(pOpWLS->GetNumberOfInteractionLengthLeft()); + + G4bool repetitive = false; + if (candidate.checkRepetitive(mDataCurrent, false) && mDataCurrent.checkRepetitive(mDataPrevious)) + repetitive = true; + + mDataPrevious = mDataCurrent; + mDataCurrent = candidate; + + return repetitive; + } + + return false; + } + + G4bool checkAbsorption(const G4double prevNILL, const G4double currentNILL) + { + if (prevNILL < 0. || currentNILL < 0.) + return false; // the number of interaction length left has to be reset + if (prevNILL == currentNILL) + return false; // no absorption + if (prevNILL == DBL_MAX || currentNILL == DBL_MAX) + return false; // NILL is re-initialized + + G4double deltaNILL = prevNILL - currentNILL; + + if (currentNILL - deltaNILL * (mNtransport + fSafety) < 0.) + return true; // absorbed before reaching fiber end + + return false; + } + + G4bool checkNILL() + { + if (!fTransported) + return true; // do nothing if the track is not already transported + + G4double wlsNILL = DBL_MAX; + G4double absorptionNILL = DBL_MAX; + + if (pOpWLS != nullptr) + { + wlsNILL = pOpWLS->GetNumberOfInteractionLengthLeft(); + if (mDataPrevious.GetWLSNILL() == DBL_MAX || mDataCurrent.GetWLSNILL() == DBL_MAX) + return true; // NILL is re-initialized + } + + if (pOpAbsorption != nullptr) + { + absorptionNILL = pOpAbsorption->GetNumberOfInteractionLengthLeft(); + if (mDataPrevious.GetAbsorptionNILL() == DBL_MAX || mDataCurrent.GetAbsorptionNILL() == DBL_MAX) + return true; // NILL is re-initialized + } + + if (wlsNILL < 0. || absorptionNILL < 0.) + return true; // let GEANT4 to reset them + + G4double deltaWlsNILL = mDataPrevious.GetWLSNILL() - mDataCurrent.GetWLSNILL(); + G4double deltaAbsorptionNILL = mDataPrevious.GetAbsorptionNILL() - mDataCurrent.GetAbsorptionNILL(); + + G4double finalWlsNILL = wlsNILL - deltaWlsNILL * fSafety; + G4double finalAbsorptionNILL = absorptionNILL - deltaAbsorptionNILL * fSafety; + + // prevent double counting of the probability of getting absorbed (which already estimated before transportation) + // reset NILL again + if (finalWlsNILL < 0. || finalAbsorptionNILL < 0.) + return false; + + return true; + } + + void setPostStepProc(const G4Track *track) + { + G4ProcessManager *pm = track->GetDefinition()->GetProcessManager(); + auto postStepProcessVector = pm->GetPostStepProcessVector(); + + for (unsigned int np = 0; np < postStepProcessVector->entries(); np++) + { + auto theProcess = (*postStepProcessVector)[np]; + + auto theType = theProcess->GetProcessType(); + + if (theType != fOptical) + continue; + + if (theProcess->GetProcessSubType() == G4OpProcessSubType::fOpBoundary) + pOpBoundaryProc = dynamic_cast(theProcess); + else if (theProcess->GetProcessSubType() == G4OpProcessSubType::fOpAbsorption) + pOpAbsorption = dynamic_cast(theProcess); + else if (theProcess->GetProcessSubType() == G4OpProcessSubType::fOpWLS) + pOpWLS = dynamic_cast(theProcess); + } + + fProcAssigned = true; + + return; + } + + void reset() + { + mNtransport = 0.; + mTransportUnit = 0.; + mFiberPos = G4ThreeVector(0); + mFiberAxis = G4ThreeVector(0); + fKill = false; + fTransported = false; + mDataCurrent.reset(); + mDataPrevious.reset(); + } + + void print() + { + if (fVerbose > 1) + { + G4cout << G4endl; + + G4cout << "mDataPrevious.trackID = " << mDataPrevious.trackID; + G4cout << " | .mOpBoundaryStatus = " << std::setw(4) << mDataPrevious.GetOpBoundaryStatus(); + G4cout << " | .mStepLengthInterval = " << mDataPrevious.GetStepLengthInterval() << G4endl; + + if (fVerbose > 2) + { + G4cout << " | globalPosition = (" << std::setw(9) << mDataPrevious.globalPosition.x(); + G4cout << "," << std::setw(9) << mDataPrevious.globalPosition.y(); + G4cout << "," << std::setw(9) << mDataPrevious.globalPosition.z() << ")" << G4endl; + + G4cout << " | momentumDirection = (" << std::setw(9) << mDataPrevious.momentumDirection.x(); + G4cout << "," << std::setw(9) << mDataPrevious.momentumDirection.y(); + G4cout << "," << std::setw(9) << mDataPrevious.momentumDirection.z() << ")" << G4endl; + + G4cout << " | polarization = (" << std::setw(9) << mDataPrevious.polarization.x(); + G4cout << "," << std::setw(9) << mDataPrevious.polarization.y(); + G4cout << "," << std::setw(9) << mDataPrevious.polarization.z() << ")" << G4endl; + + G4cout << " | globalTime = " << std::setw(9) << mDataPrevious.globalTime << G4endl; + G4cout << " | WLSNILL = " << std::setw(9) << mDataPrevious.GetWLSNILL() << G4endl; + G4cout << " | AbsorptionNILL = " << std::setw(9) << mDataPrevious.GetAbsorptionNILL() << G4endl; + } + + G4cout << "mDataCurrent.trackID = " << mDataCurrent.trackID; + G4cout << " | .mOpBoundaryStatus = " << std::setw(4) << mDataCurrent.GetOpBoundaryStatus() << G4endl; + + if (fVerbose > 2) + { + G4cout << " | globalPosition = (" << std::setw(9) << mDataCurrent.globalPosition.x(); + G4cout << "," << std::setw(9) << mDataCurrent.globalPosition.y(); + G4cout << "," << std::setw(9) << mDataCurrent.globalPosition.z() << ")" << G4endl; + + G4cout << " | momentumDirection = (" << std::setw(9) << mDataCurrent.momentumDirection.x(); + G4cout << "," << std::setw(9) << mDataCurrent.momentumDirection.y(); + G4cout << "," << std::setw(9) << mDataCurrent.momentumDirection.z() << ")" << G4endl; + + G4cout << " | polarization = (" << std::setw(9) << mDataCurrent.polarization.x(); + G4cout << "," << std::setw(9) << mDataCurrent.polarization.y(); + G4cout << "," << std::setw(9) << mDataCurrent.polarization.z() << ")" << G4endl; + + G4cout << " | globalTime = " << std::setw(9) << mDataCurrent.globalTime << G4endl; + G4cout << " | WLSNILL = " << std::setw(9) << mDataCurrent.GetWLSNILL() << G4endl; + G4cout << " | AbsorptionNILL = " << std::setw(9) << mDataCurrent.GetAbsorptionNILL() << G4endl; + } + + G4cout << G4endl; + } + } + }; + + template <> + void Geant4FSShowerModel::initialize() + { + this->m_applicablePartNames.emplace_back("opticalphoton"); + } + + template <> + void Geant4FSShowerModel::constructSensitives(Geant4DetectorConstructionContext *ctxt) + { + this->Geant4FastSimShowerModel::constructSensitives(ctxt); + } + + template <> + void Geant4FSShowerModel::modelShower(const G4FastTrack &fasttrack, G4FastStep &faststep) + { + auto *track = fasttrack.GetPrimaryTrack(); + + if (locals.fKill) + { // absorption + faststep.ProposeTotalEnergyDeposited(track->GetKineticEnergy()); + faststep.KillPrimaryTrack(); + + return; + } + + if (locals.fTransported) + return; // reset NILL if the track did not meet NILL check + + double timeUnit = locals.mDataCurrent.globalTime - locals.mDataPrevious.globalTime; + auto posShift = locals.mTransportUnit * locals.mNtransport * locals.mFiberAxis; // #TODO apply shift for xy direction as well + double timeShift = timeUnit * locals.mNtransport; + + faststep.ProposePrimaryTrackFinalPosition(track->GetPosition() + posShift, false); + faststep.ProposePrimaryTrackFinalTime(track->GetGlobalTime() + timeShift); + faststep.ProposePrimaryTrackFinalKineticEnergy(track->GetKineticEnergy()); + faststep.ProposePrimaryTrackFinalMomentumDirection(track->GetMomentumDirection(), false); + faststep.ProposePrimaryTrackFinalPolarization(track->GetPolarization(), false); + locals.fTransported = true; + return; + } + + template <> + bool Geant4FSShowerModel::check_applicability(const G4ParticleDefinition &particle) + { + return &particle == G4OpticalPhoton::OpticalPhotonDefinition(); + } + + template <> + bool Geant4FSShowerModel::check_trigger(const G4FastTrack &fasttrack) + { + if (!locals.fSwitch) + return false; // turn on/off the model + + const G4Track *track = fasttrack.GetPrimaryTrack(); + + // reset when moving to the next track + if (locals.mDataCurrent.trackID != track->GetTrackID()) + locals.reset(); + + // make sure that the track does not get absorbed after transportation, as number of interaction length left is reset when doing transportation + if (!locals.checkNILL()) + return true; // track is already transported but did not pass NILL check, attempt to reset NILL + + if (locals.fTransported) + { // track is already transported and did pass NILL check, nothing to do + if (locals.mFiberAxis.dot(track->GetMomentumDirection()) * locals.mFiberAxis.dot(locals.mDataCurrent.momentumDirection) < 0) // different propagation direction (e.g. mirror) + locals.reset(); + + return false; + } + + if (!locals.checkTotalInternalReflection(track)) + return false; // nothing to do if the track has no repetitive total internal reflection + + auto theTouchable = track->GetTouchableHandle(); + auto solid = theTouchable->GetSolid(); + + if (solid->GetEntityType() != "G4Tubs") + return false; // only works for G4Tubs at the moment + + if (locals.fVerbose > 0) + locals.print(); // at this point, the track should have passed all prerequisites before entering computationally heavy operations + + G4Tubs *tubs = static_cast(solid); + G4double fiberLen = 2. * tubs->GetZHalfLength(); + + locals.mFiberPos = theTouchable->GetHistory()->GetTopTransform().Inverse().TransformPoint(G4ThreeVector(0., 0., 0.)); + locals.mFiberAxis = theTouchable->GetHistory()->GetTopTransform().Inverse().TransformAxis(G4ThreeVector(0., 0., 1.)); + + auto delta = locals.mDataCurrent.globalPosition - locals.mDataPrevious.globalPosition; + locals.mTransportUnit = delta.dot(locals.mFiberAxis); + + // estimate the number of expected total internal reflections before reaching fiber end + auto fiberEnd = (locals.mTransportUnit > 0.) ? locals.mFiberPos + locals.mFiberAxis * fiberLen / 2. : locals.mFiberPos - locals.mFiberAxis * fiberLen / 2.; + auto toEnd = fiberEnd - track->GetPosition(); + G4double toEndAxis = toEnd.dot(locals.mFiberAxis); + G4double maxTransport = std::floor(toEndAxis / locals.mTransportUnit); + locals.mNtransport = maxTransport - locals.fSafety; + + if (locals.mNtransport < 1.) + return false; // require at least n = fSafety of total internal reflections at the end + + if (locals.checkAbsorption(locals.mDataPrevious.GetWLSNILL(), locals.mDataCurrent.GetWLSNILL())) + return false; // do nothing if WLS happens before reaching fiber end + if (locals.checkAbsorption(locals.mDataPrevious.GetAbsorptionNILL(), locals.mDataCurrent.GetAbsorptionNILL())) + locals.fKill = true; // absorbed before reaching fiber end + + return true; + } + + typedef Geant4FSShowerModel Geant4DRCFiberModel; + } +} + +#include +DECLARE_GEANT4ACTION_NS(dd4hep::sim, Geant4DRCFiberModel) diff --git a/plugins/DRCaloFastSimModel.h b/plugins/DRCaloFastSimModel.h new file mode 100644 index 000000000..cf94952d8 --- /dev/null +++ b/plugins/DRCaloFastSimModel.h @@ -0,0 +1,78 @@ +#include "G4OpBoundaryProcess.hh" +#include "G4GenericMessenger.hh" +#include "G4OpBoundaryProcess.hh" +#include "G4OpAbsorption.hh" +#include "G4OpWLS.hh" +#include "G4Material.hh" + +struct FastFiberData +{ +public: + FastFiberData(G4int, G4double, G4double, G4double, G4ThreeVector, G4ThreeVector, G4ThreeVector, G4int status = G4OpBoundaryProcessStatus::Undefined); + ~FastFiberData() {} + + void reset(); + + G4double GetAbsorptionNILL() { return mOpAbsorptionNumIntLenLeft; } + void SetAbsorptionNILL(G4double in) { mOpAbsorptionNumIntLenLeft = in; } + + G4double GetWLSNILL() { return mOpWLSNumIntLenLeft; } + void SetWLSNILL(G4double in) { mOpWLSNumIntLenLeft = in; } + + G4int GetOpBoundaryStatus() { return mOpBoundaryStatus; } + void SetOpBoundaryStatus(G4int in) { mOpBoundaryStatus = in; } + + G4double GetStepLengthInterval() { return mStepLengthInterval; } + void AddStepLengthInterval(G4double in) { mStepLengthInterval += in; } + + G4bool checkRepetitive(const FastFiberData, G4bool checkInterval = true); + + G4int trackID; + G4double kineticEnergy; + G4double globalTime; + G4double pathLength; + G4ThreeVector globalPosition; + G4ThreeVector momentumDirection; + G4ThreeVector polarization; + +private: + G4int mOpBoundaryStatus; + G4double mOpAbsorptionNumIntLenLeft; + G4double mOpWLSNumIntLenLeft; + G4double mStepLengthInterval; +}; + +FastFiberData::FastFiberData(G4int id, G4double en, G4double globTime, G4double path, G4ThreeVector pos, G4ThreeVector mom, G4ThreeVector pol, G4int status) +{ + trackID = id; + kineticEnergy = en; + globalTime = globTime; + pathLength = path; + globalPosition = pos; + momentumDirection = mom; + polarization = pol; + mOpBoundaryStatus = status; + mOpAbsorptionNumIntLenLeft = DBL_MAX; + mOpWLSNumIntLenLeft = DBL_MAX; + mStepLengthInterval = 0.; +} + +G4bool FastFiberData::checkRepetitive(const FastFiberData theData, G4bool checkInterval) +{ + if (this->trackID != theData.trackID) + return false; + if (this->mOpBoundaryStatus != theData.mOpBoundaryStatus) + return false; + if (checkInterval && std::abs(this->mStepLengthInterval - theData.mStepLengthInterval) > G4GeometryTolerance::GetInstance()->GetSurfaceTolerance()) + return false; + + return true; +} + +void FastFiberData::reset() +{ + this->mOpBoundaryStatus = G4OpBoundaryProcessStatus::Undefined; + this->mOpAbsorptionNumIntLenLeft = DBL_MAX; + this->mOpWLSNumIntLenLeft = DBL_MAX; + this->mStepLengthInterval = 0.; +} \ No newline at end of file diff --git a/plugins/FiberDRCaloSDAction.cpp b/plugins/FiberDRCaloSDAction.cpp new file mode 100644 index 000000000..327e6ba53 --- /dev/null +++ b/plugins/FiberDRCaloSDAction.cpp @@ -0,0 +1,245 @@ +// DD4hep Framework include files +#include "DD4hep/Version.h" +#include "DD4hep/Objects.h" +#include "DD4hep/Segmentations.h" +#include "DD4hep/DD4hepUnits.h" + +// k4geo Framework include files + +#include "FiberDRCaloSDAction.h" + +// Geant4 include files +#include "G4THitsCollection.hh" +#include "G4SystemOfUnits.hh" +#include "G4PhysicalConstants.hh" +#include "G4Step.hh" +#include "G4TouchableHistory.hh" +#include "G4ThreeVector.hh" +#include "G4HCofThisEvent.hh" +#include "G4SDManager.hh" +#include "G4ParticleDefinition.hh" +#include "G4ParticleTypes.hh" +#include "G4OpticalPhoton.hh" +#include "G4VProcess.hh" + +#if DD4HEP_VERSION_GE(1, 21) +#define GEANT4_CONST_STEP const +#else +#define GEANT4_CONST_STEP +#endif + +/// Namespace for the AIDA detector description toolkit +namespace dd4hep +{ + + /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit + namespace sim + { + + /* + * Geant4SensitiveAction sensitive detector for the Dual-readout calorimeter + * + * \author Sungwon Kim + * \version 1.0 + * \ingroup DD4HEP_SIMULATION + */ + + struct DRCData + { + Geant4HitCollection *fHitCollection; + G4int fHCID; + + G4int fWavBin; + G4int fTimeBin; + G4float fWavlenStart; + G4float fWavlenEnd; + G4float fTimeStart; + G4float fTimeEnd; + G4float fWavlenStep; + G4float fTimeStep; + + G4double wavToE(G4double wav) { return CLHEP::h_Planck * CLHEP::c_light / wav; } + + int findWavBin(G4double en) + { + int i = 0; + for (; i < fWavBin + 1; i++) + { + if (en < wavToE((fWavlenStart - static_cast(i) * fWavlenStep) * CLHEP::nm)) + break; + } + + return fWavBin + 1 - i; + } + + int findTimeBin(G4double stepTime) + { + int i = 0; + for (; i < fTimeBin + 1; i++) + { + if (stepTime < ((fTimeStart + static_cast(i) * fTimeStep) * CLHEP::ns)) + break; + } + + return i; + } + + bool applyYellowFilter(G4double en) + { + double energy = (en * 1000000.); // MeV -> eV unit change + + if (energy >= 2.69531) + return true; // Photon w/ E > 2.69531 eV filtered + + TF1 *uniform = new TF1("f1", "pol0", 0, 1); // TODO :: Pointer -> change to unique pointer + uniform->SetParameter(0, 1); + double randVal = uniform->GetRandom(0, 1); // TODO :: Change random engine to DDSim or G4 random! + const int N = 24; + double graph_X[N] = {1.37760, + 1.45864, + 1.54980, + 1.65312, + 1.71013, + 1.77120, + 1.83680, + 1.90745, + 1.98375, + 2.06640, + 2.10143, + 2.13766, + 2.17516, + 2.21400, + 2.25426, + 2.29600, + 2.33932, + 2.38431, + 2.43106, + 2.47968, + 2.53029, + 2.58300, + 2.63796, + 2.69531}; + double graph_Y[N] = {0.903 * 0.903, + 0.903 * 0.903, + 0.903 * 0.903, + 0.903 * 0.903, + 0.903 * 0.903, + 0.903 * 0.903, + 0.902 * 0.902, + 0.901 * 0.901, + 0.898 * 0.898, + 0.895 * 0.895, + 0.893 * 0.893, + 0.891 * 0.891, + 0.888 * 0.888, + 0.883 * 0.883, + 0.87 * 0.87, + 0.838 * 0.838, + 0.76 * 0.76, + 0.62 * 0.62, + 0.488 * 0.488, + 0.345 * 0.345, + 0.207 * 0.207, + 0.083 * 0.083, + 0.018 * 0.018, + 0.}; + TGraph *FilterEffGraph = new TGraph(N, graph_X, graph_Y); // TODO :: Pointer -> change to unique pointer + double FilterEff = FilterEffGraph->Eval((double)energy); + + delete uniform; + delete FilterEffGraph; + + // filter efficiency == probability of photon passing filter + // == Probability of random value (from uniform distribution with range 0 ~ 1) larger than filter efficiency + return (randVal > FilterEff); + } + + DRCData() + : fHitCollection(0), fHCID(-1), fWavBin(120), fTimeBin(650), + fWavlenStart(900.), fWavlenEnd(300.), fTimeStart(5.), fTimeEnd(70.) + { + fWavlenStep = (fWavlenStart - fWavlenEnd) / (float)fWavBin; + fTimeStep = (fTimeEnd - fTimeStart) / (float)fTimeBin; + } + }; + + /// Define collections created by this sensitive action object + template <> + void Geant4SensitiveAction::defineCollections() + { + std::string readout_name = m_sensitive.readout().name(); + m_collectionID = defineCollection(m_sensitive.readout().name()); + std::cout << "defineCollection Geant4DRCalorimeter readout_name : " << readout_name << std::endl; + std::cout << "defineCollection Geant4DRCalorimeter m_collectionID : " << m_collectionID << std::endl; + } + + /// Method for generating hit(s) using the information of G4Step object. + template <> + G4bool Geant4SensitiveAction::process(G4Step GEANT4_CONST_STEP *step, G4TouchableHistory *history) + { + if (step->GetTrack()->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition()) + return false; + + typedef Geant4DRCalorimeter::Hit Hit; + + Geant4StepHandler h(step); + Geant4HitCollection *coll = collection(m_collectionID); + HitContribution contrib = Hit::extractContribution(step); + + auto theTouchable = step->GetPostStepPoint()->GetTouchable(); + dd4hep::sim::Geant4VolumeManager volMgr = dd4hep::sim::Geant4Mapping::instance().volumeManager(); + dd4hep::VolumeID volID = volMgr.volumeID(theTouchable); + G4ThreeVector global = step->GetPostStepPoint()->GetPosition(); + G4ThreeVector local = theTouchable->GetHistory()->GetTopTransform().TransformPoint(global); + dd4hep::Position loc(local.x() * dd4hep::millimeter / CLHEP::millimeter, local.y() * dd4hep::millimeter / CLHEP::millimeter, local.z() * dd4hep::millimeter / CLHEP::millimeter); + dd4hep::Position glob(global.x() * dd4hep::millimeter / CLHEP::millimeter, global.y() * dd4hep::millimeter / CLHEP::millimeter, global.z() * dd4hep::millimeter / CLHEP::millimeter); + + auto cID = m_segmentation->cellID(loc, glob, volID); // This returns cID corresponding to SiPM Wafer + Hit *hit = coll->find(CellIDCompare(cID)); + + G4double hitTime = step->GetPostStepPoint()->GetGlobalTime(); + G4double energy = step->GetTrack()->GetTotalEnergy(); + + // Apply yellow filter here + // Get random number from (0~1) uniform distribution + // If the photon is from Scint. process, calculate the filter eff. based on its energy + // If (random number) > (Eff), apply yellow filter (= do not make hit (or count photon) for that photon) + dd4hep::DDSegmentation::VolumeID ceren = static_cast(m_segmentation->decoder()->get(cID, "c")); + bool IsCeren = static_cast(ceren); + if (!(IsCeren)) + { + if (m_userData.applyYellowFilter(energy)) + return true; + } + + if (!hit) + { + hit = new Geant4DRCalorimeter::Hit(m_userData.fWavlenStep, m_userData.fTimeStep); + hit->cellID = cID; + hit->SetSiPMnum(cID); + hit->SetTimeStart(m_userData.fTimeStart); + hit->SetTimeEnd(m_userData.fTimeEnd); + hit->SetWavlenMax(m_userData.fWavlenStart); + hit->SetWavlenMin(m_userData.fWavlenEnd); + coll->add(hit); + } + + hit->photonCount(); + int wavBin = m_userData.findWavBin(energy); + hit->CountWavlenSpectrum(wavBin); + int timeBin = m_userData.findTimeBin(hitTime); + hit->CountTimeStruct(timeBin); + + hit->position = glob; + hit->energyDeposit += contrib.deposit; + hit->truth.emplace_back(contrib); + + return true; + } + + typedef Geant4SensitiveAction DRCaloSDAction; + } +} + +#include "DDG4/Factories.h" +DECLARE_GEANT4SENSITIVE(DRCaloSDAction) \ No newline at end of file diff --git a/plugins/FiberDRCaloSDAction.h b/plugins/FiberDRCaloSDAction.h new file mode 100644 index 000000000..3da9a518b --- /dev/null +++ b/plugins/FiberDRCaloSDAction.h @@ -0,0 +1,172 @@ +#ifndef FiberDRCaloSDAction_H +#define FiberDRCaloSDAction_H + +// DD4hep Framework include files +#include "DD4hep/Version.h" +#include "DD4hep/Objects.h" +#include "DD4hep/Segmentations.h" +#include "DD4hep/DD4hepUnits.h" + +#include "DDG4/Geant4SensDetAction.inl" +#include "DDG4/Geant4EventAction.h" +#include "DDG4/Geant4HitCollection.h" +#include "DDG4/Geant4Mapping.h" +#include "DDG4/Geant4VolumeManager.h" +#include "DDG4/Geant4Data.h" + +// Geant4 include files +#include "G4THitsCollection.hh" +#include "G4SystemOfUnits.hh" +#include "G4PhysicalConstants.hh" +#include "G4Step.hh" +#include "G4TouchableHistory.hh" +#include "G4ThreeVector.hh" +#include "G4HCofThisEvent.hh" +#include "G4SDManager.hh" +#include "G4ParticleDefinition.hh" +#include "G4ParticleTypes.hh" +#include "G4OpticalPhoton.hh" +#include "G4VProcess.hh" + +#include "TF1.h" +#include "TGraph.h" + +#if DD4HEP_VERSION_GE(1, 21) +#define GEANT4_CONST_STEP const +#else +#define GEANT4_CONST_STEP +#endif + +/// Namespace for the AIDA detector description toolkit +namespace dd4hep +{ + + /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit + namespace sim + { + + class Geant4DRCalorimeter + { + public: + class Hit : public Geant4HitData + { + public: + typedef Geant4HitData base_t; + typedef std::map DRsimTimeStruct; + typedef std::map DRsimWavlenSpectrum; + + /// Hit position + ROOT::Math::XYZVector position; + /// Hit contributions by individual particles + std::vector truth; + /// Total energy deposit + double energyDeposit; + + public: + /// Default constructor (for ROOT) + Hit(); + // Constructor + Hit(float wavSampling, float timeSampling) + : Geant4HitData(), + fSiPMnum(0), + fPhotons(0), + mWavSampling(wavSampling), + mTimeSampling(timeSampling) + { + } + /// copy constructor + Hit(const Hit &right) + : Geant4HitData() + { + fSiPMnum = right.fSiPMnum; + fPhotons = right.fPhotons; + fWavlenSpectrum = right.fWavlenSpectrum; + fTimeStruct = right.fTimeStruct; + mWavSampling = right.mWavSampling; + mTimeSampling = right.mTimeSampling; + } + /// Copy assignment operator + Hit &operator=(Hit &right) + { + fSiPMnum = right.fSiPMnum; + fPhotons = right.fPhotons; + fWavlenSpectrum = right.fWavlenSpectrum; + fTimeStruct = right.fTimeStruct; + mWavSampling = right.mWavSampling; + mTimeSampling = right.mTimeSampling; + return *this; + } + G4bool operator==(const Hit &right) const + { + return (fSiPMnum == right.fSiPMnum); + } + /// Move constructor + Hit(Hit &&c) = delete; + + /// Standard constructor + Hit(const Position &cell_pos); + /// Default destructor + virtual ~Hit(){}; + /// Move assignment operator + Hit &operator=(Hit &&c) = delete; + + void photonCount() { fPhotons++; } + unsigned long GetPhotonCount() const { return fPhotons; } + + void SetSiPMnum(dd4hep::DDSegmentation::CellID n) { fSiPMnum = n; } + const dd4hep::DDSegmentation::CellID &GetSiPMnum() const { return fSiPMnum; } + + void CountWavlenSpectrum(int ibin) + { + auto it = fWavlenSpectrum.find(ibin); + + if (it == fWavlenSpectrum.end()) + fWavlenSpectrum.insert(std::make_pair(ibin, 1)); + else + it->second++; + }; + const DRsimWavlenSpectrum &GetWavlenSpectrum() const { return fWavlenSpectrum; } + + void CountTimeStruct(int ibin) + { + auto it = fTimeStruct.find(ibin); + + if (it == fTimeStruct.end()) + fTimeStruct.insert(std::make_pair(ibin, 1)); + else + it->second++; + }; + const DRsimTimeStruct &GetTimeStruct() const { return fTimeStruct; } + + float GetSamplingTime() const { return mTimeSampling; } + float GetSamplingWavlen() const { return mWavSampling; } + + float GetTimeStart() const { return fTimeStart; } + void SetTimeStart(float val) { fTimeStart = val; } + + float GetTimeEnd() const { return fTimeEnd; } + void SetTimeEnd(float val) { fTimeEnd = val; } + + float GetWavlenMax() const { return fWavlenMax; } + void SetWavlenMax(float val) { fWavlenMax = val; } + + float GetWavlenMin() const { return fWavlenMin; } + void SetWavlenMin(float val) { fWavlenMin = val; } + + private: + dd4hep::DDSegmentation::CellID fSiPMnum; + unsigned long fPhotons; + DRsimWavlenSpectrum fWavlenSpectrum; + DRsimTimeStruct fTimeStruct; + float mWavSampling; + float mTimeSampling; + float fWavlenMax; + float fWavlenMin; + float fTimeStart; + float fTimeEnd; + }; + }; + + } +} +#endif \ No newline at end of file diff --git a/plugins/Geant4Output2EDM4hep_DRC.cpp b/plugins/Geant4Output2EDM4hep_DRC.cpp new file mode 100644 index 000000000..b9ceee002 --- /dev/null +++ b/plugins/Geant4Output2EDM4hep_DRC.cpp @@ -0,0 +1,690 @@ +#ifndef DD4HEP_DDG4_GEANT4OUTPUT2EDM4hep_DRC_H +#define DD4HEP_DDG4_GEANT4OUTPUT2EDM4hep_DRC_H + +/// Framework include files +#include +#include +#include +#include + +#include "FiberDRCaloSDAction.h" + +/// edm4hep include files +#include +#include +#include +#include +#include "edm4hep/RawCalorimeterHitCollection.h" +#include "edm4hep/RawTimeSeriesCollection.h" +#include +/// podio include files +#include +#if PODIO_VERSION_MAJOR > 0 || (PODIO_VERSION_MAJOR == 0 && PODIO_VERSION_MINOR >= 99) +#include +#else +#include +#endif +#include + +/// Namespace for the AIDA detector description toolkit +namespace dd4hep { + + class ComponentCast; + + /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit + namespace sim { + + class Geant4ParticleMap; + class Geant4Output2EDM4hep_DRC : public Geant4OutputAction { + protected: +#if PODIO_VERSION_MAJOR > 0 || (PODIO_VERSION_MAJOR == 0 && PODIO_VERSION_MINOR >= 99) + using writer_t = podio::ROOTWriter; +#else + using writer_t = podio::ROOTFrameWriter; +#endif + using stringmap_t = std::map< std::string, std::string >; + using trackermap_t = std::map< std::string, edm4hep::SimTrackerHitCollection >; + using calorimeterpair_t = std::pair< edm4hep::SimCalorimeterHitCollection, edm4hep::CaloHitContributionCollection >; + using calorimetermap_t = std::map< std::string, calorimeterpair_t >; + + using drcalopair_t = std::pair< edm4hep::RawCalorimeterHitCollection, edm4hep::RawTimeSeriesCollection> ; // Required info for IDEA DRC sim hit + using drcalomap_t = std::map< std::string, drcalopair_t >; // Required info for IDEA DRC sim hit + using drcaloWavmap_t = std::map< std::string, edm4hep::RawTimeSeriesCollection >; + std::unique_ptr m_file { }; + podio::Frame m_frame { }; + edm4hep::MCParticleCollection m_particles { }; + trackermap_t m_trackerHits; + calorimetermap_t m_calorimeterHits; + drcalomap_t m_drcaloHits; + drcaloWavmap_t m_drcaloWaves; + stringmap_t m_runHeader; + stringmap_t m_eventParametersInt; + stringmap_t m_eventParametersFloat; + stringmap_t m_eventParametersString; + stringmap_t m_cellIDEncodingStrings{}; + std::string m_section_name { "events" }; + int m_runNo { 0 }; + int m_runNumberOffset { 0 }; + int m_eventNo { 0 }; + int m_eventNumberOffset { 0 }; + bool m_filesByRun { false }; + + /// Data conversion interface for MC particles to EDM4hep format + void saveParticles(Geant4ParticleMap* particles); + /// Store the metadata frame with e.g. the cellID encoding strings + void saveFileMetaData(); + public: + /// Standard constructor + Geant4Output2EDM4hep_DRC(Geant4Context* ctxt, const std::string& nam); + /// Default destructor + virtual ~Geant4Output2EDM4hep_DRC(); + /// Callback to store the Geant4 run information + virtual void beginRun(const G4Run* run); + /// Callback to store the Geant4 run information + virtual void endRun(const G4Run* run); + + /// Callback to store the Geant4 run information + virtual void saveRun(const G4Run* run); + /// Callback to store the Geant4 event + virtual void saveEvent( OutputContext& ctxt); + /// Callback to store each Geant4 hit collection + virtual void saveCollection( OutputContext& ctxt, G4VHitsCollection* collection); + /// Commit data at end of filling procedure + virtual void commit( OutputContext& ctxt); + + /// begin-of-event callback - creates EDM4hep event and adds it to the event context + virtual void begin(const G4Event* event); + protected: + /// Fill event parameters in EDM4hep event + template + void saveEventParameters(const std::map& parameters) { + for(const auto& p : parameters) { + info("Saving event parameter: %-32s = %s", p.first.c_str(), p.second.c_str()); + m_frame.putParameter(p.first, p.second); + } + } + }; + + template <> void EventParameters::extractParameters(podio::Frame& frame) { + for(auto const& p: this->intParameters()) { + printout(DEBUG, "Geant4OutputEDM4hep", "Saving event parameter: %s", p.first.c_str()); + frame.putParameter(p.first, p.second); + } + for(auto const& p: this->fltParameters()) { + printout(DEBUG, "Geant4OutputEDM4hep", "Saving event parameter: %s", p.first.c_str()); + frame.putParameter(p.first, p.second); + } + for(auto const& p: this->strParameters()) { + printout(DEBUG, "Geant4OutputEDM4hep", "Saving event parameter: %s", p.first.c_str()); + frame.putParameter(p.first, p.second); + } +#if podio_VERSION_MAJOR > 0 || podio_VERSION_MINOR > 16 || podio_VERSION_PATCH > 2 + // This functionality is only present in podio > 0.16.2 + for (auto const& p: this->dblParameters()) { + printout(DEBUG, "Geant4OutputEDM4hep", "Saving event parameter: %s", p.first.c_str()); + frame.putParameter(p.first, p.second); + } +#endif + } + + template <> void RunParameters::extractParameters(podio::Frame& frame) { + for(auto const& p: this->intParameters()) { + printout(DEBUG, "Geant4OutputEDM4hep", "Saving run parameter: %s", p.first.c_str()); + frame.putParameter(p.first, p.second); + } + for(auto const& p: this->fltParameters()) { + printout(DEBUG, "Geant4OutputEDM4hep", "Saving run parameter: %s", p.first.c_str()); + frame.putParameter(p.first, p.second); + } + for(auto const& p: this->strParameters()) { + printout(DEBUG, "Geant4OutputEDM4hep", "Saving run parameter: %s", p.first.c_str()); + frame.putParameter(p.first, p.second); + } +#if podio_VERSION_MAJOR > 0 || podio_VERSION_MINOR > 16 || podio_VERSION_PATCH > 2 + // This functionality is only present in podio > 0.16.2 + for (auto const& p: this->dblParameters()) { + printout(DEBUG, "Geant4OutputEDM4hep", "Saving run parameter: %s", p.first.c_str()); + frame.putParameter(p.first, p.second); + } +#endif + } + + } // End namespace sim +} // End namespace dd4hep +#endif // DD4HEP_DDG4_GEANT4OUTPUT2EDM4hep_DRC_H + +//========================================================================== +// AIDA Detector description implementation +//-------------------------------------------------------------------------- +// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN) +// All rights reserved. +// +// For the licensing terms see $DD4hepINSTALL/LICENSE. +// For the list of contributors see $DD4hepINSTALL/doc/CREDITS. +// +// Author : F.Gaede, DESY +// +//========================================================================== + +/// Framework include files +#include +#include + +#include +#include +#include +#include +#include +#include + +///#include +/// Geant4 headers +#include +#include +#include +#include +#include +#include +#include +/// use the Geant4 units in namespace CLHEP +#include + +/// edm4hep include files +#include + +using namespace dd4hep::sim; +using namespace dd4hep; + +namespace { + G4Mutex action_mutex = G4MUTEX_INITIALIZER; +} + +#include +DECLARE_GEANT4ACTION(Geant4Output2EDM4hep_DRC) + +/// Standard constructor +Geant4Output2EDM4hep_DRC::Geant4Output2EDM4hep_DRC(Geant4Context* ctxt, const std::string& nam) +: Geant4OutputAction(ctxt,nam), m_runNo(0), m_runNumberOffset(0), m_eventNumberOffset(0) +{ + declareProperty("RunHeader", m_runHeader); + declareProperty("EventParametersInt", m_eventParametersInt); + declareProperty("EventParametersFloat", m_eventParametersFloat); + declareProperty("EventParametersString", m_eventParametersString); + declareProperty("RunNumberOffset", m_runNumberOffset); + declareProperty("EventNumberOffset", m_eventNumberOffset); + declareProperty("SectionName", m_section_name); + declareProperty("FilesByRun", m_filesByRun); + info("Writer is now instantiated ..." ); + InstanceCount::increment(this); +} + +/// Default destructor +Geant4Output2EDM4hep_DRC::~Geant4Output2EDM4hep_DRC() { + G4AutoLock protection_lock(&action_mutex); + m_file.reset(); + InstanceCount::decrement(this); +} + +// Callback to store the Geant4 run information +void Geant4Output2EDM4hep_DRC::beginRun(const G4Run* run) { + G4AutoLock protection_lock(&action_mutex); + std::string fname = m_output; + m_runNo = run->GetRunID(); + if ( m_filesByRun ) { + std::size_t idx = m_output.rfind("."); + if ( idx != std::string::npos ) { + fname = m_output.substr(0, idx) + _toString(m_runNo, ".run%08d") + m_output.substr(idx); + } + } + if ( !fname.empty() ) { +#if PODIO_VERSION_MAJOR > 0 || (PODIO_VERSION_MAJOR == 0 && PODIO_VERSION_MINOR >= 99) + m_file = std::make_unique(fname); +#else + m_file = std::make_unique(fname); +#endif + if ( !m_file ) { + fatal("+++ Failed to open output file: %s", fname.c_str()); + } + printout( INFO, "Geant4Output2EDM4hep_DRC" ,"Opened %s for output", fname.c_str() ) ; + } +} + +/// Callback to store the Geant4 run information +void Geant4Output2EDM4hep_DRC::endRun(const G4Run* run) { + saveRun(run); + saveFileMetaData(); + if ( m_file ) { + m_file->finish(); + m_file.reset(); + } +} + +void Geant4Output2EDM4hep_DRC::saveFileMetaData() { + podio::Frame metaFrame{}; + for (const auto& [name, encodingStr] : m_cellIDEncodingStrings) { + metaFrame.putParameter(name + "__CellIDEncoding", encodingStr); + } + + m_file->writeFrame(metaFrame, "metadata"); +} + +/// Commit data at end of filling procedure +void Geant4Output2EDM4hep_DRC::commit( OutputContext& /* ctxt */) { + if ( m_file ) { + G4AutoLock protection_lock(&action_mutex); + m_frame.put( std::move(m_particles), "MCParticles"); + for (auto it = m_trackerHits.begin(); it != m_trackerHits.end(); ++it) { + m_frame.put( std::move(it->second), it->first); + } + for (auto& [colName, calorimeterHits] : m_calorimeterHits) { + m_frame.put( std::move(calorimeterHits.first), colName); + m_frame.put( std::move(calorimeterHits.second), colName + "Contributions"); + } + for (auto& [colName, calorimeterHits] : m_drcaloHits) { + m_frame.put( std::move(calorimeterHits.first), colName + "RawHit"); + m_frame.put( std::move(calorimeterHits.second), colName + "TimeStruct"); + } + for (auto it = m_drcaloWaves.begin(); it != m_drcaloWaves.end(); ++it) { + m_frame.put( std::move(it->second), it->first + "WaveLen"); + } + m_file->writeFrame(m_frame, m_section_name); + m_particles.clear(); + m_trackerHits.clear(); + m_calorimeterHits.clear(); + m_drcaloHits.clear(); + m_drcaloWaves.clear(); + m_frame = {}; + return; + } + except("+++ Failed to write output file. [Stream is not open]"); +} + +/// Callback to store the Geant4 run information +void Geant4Output2EDM4hep_DRC::saveRun(const G4Run* run) { + G4AutoLock protection_lock(&action_mutex); + // --- write an edm4hep::RunHeader --------- + // Runs are just Frames with different contents in EDM4hep / podio. We simply + // store everything as parameters for now + podio::Frame runHeader {}; + for (const auto& [key, value] : m_runHeader) + runHeader.putParameter(key, value); + + m_runNo = m_runNumberOffset > 0 ? m_runNumberOffset + run->GetRunID() : run->GetRunID(); + runHeader.putParameter("runNumber", m_runNo); + runHeader.putParameter("GEANT4Version", G4Version); + runHeader.putParameter("DD4hepVersion", versionString()); + runHeader.putParameter("detectorName", context()->detectorDescription().header().name()); + + RunParameters* parameters = context()->run().extension(false); + if ( parameters ) { + parameters->extractParameters(runHeader); + } + + m_file->writeFrame(runHeader, "runs"); +} + +void Geant4Output2EDM4hep_DRC::begin(const G4Event* event) { + /// Create event frame object + m_eventNo = event->GetEventID(); + m_frame = {}; + m_particles = {}; + m_trackerHits.clear(); + m_calorimeterHits.clear(); + m_drcaloHits.clear(); + m_drcaloWaves.clear(); +} + +/// Data conversion interface for MC particles to EDM4hep format +void Geant4Output2EDM4hep_DRC::saveParticles(Geant4ParticleMap* particles) { + typedef detail::ReferenceBitMask PropertyMask; + typedef Geant4ParticleMap::ParticleMap ParticleMap; + const ParticleMap& pm = particles->particleMap; + + m_particles.clear(); + if ( pm.size() > 0 ) { + size_t cnt = 0; + // Mapping of ids in the ParticleMap to indices in the MCParticle collection + std::map p_ids; + std::vector p_part; + p_part.reserve(pm.size()); + // First create the particles + for (const auto& iParticle : pm) { + int id = iParticle.first; + const Geant4ParticleHandle p = iParticle.second; + PropertyMask mask(p->status); + // std::cout << " ********** mcp status : 0x" << std::hex << p->status << ", mask.isSet(G4PARTICLE_GEN_STABLE) x" << std::dec << mask.isSet(G4PARTICLE_GEN_STABLE) <pdgID); + // Because EDM4hep is switching between vector3f[loat] and vector3d[ouble] + using MT = decltype(std::declval().getMomentum().x); + mcp.setMomentum( {MT(p->psx/CLHEP::GeV),MT(p->psy/CLHEP::GeV),MT(p->psz/CLHEP::GeV)} ); + mcp.setMomentumAtEndpoint( {MT(p->pex/CLHEP::GeV),MT(p->pey/CLHEP::GeV),MT(p->pez/CLHEP::GeV)} ); + + double vs_fa[3] = { p->vsx/CLHEP::mm, p->vsy/CLHEP::mm, p->vsz/CLHEP::mm } ; + mcp.setVertex( vs_fa ); + + double ve_fa[3] = { p->vex/CLHEP::mm, p->vey/CLHEP::mm, p->vez/CLHEP::mm } ; + mcp.setEndpoint( ve_fa ); + + mcp.setTime(p->time/CLHEP::ns); + mcp.setMass(p->mass/CLHEP::GeV); + mcp.setCharge(def ? def->GetPDGCharge() : 0); // Charge(e+) = 1 ! + + // Set generator status + mcp.setGeneratorStatus(0); + if( p->genStatus ) { + mcp.setGeneratorStatus( p->genStatus ) ; + } else { + if ( mask.isSet(G4PARTICLE_GEN_STABLE) ) mcp.setGeneratorStatus(1); + else if ( mask.isSet(G4PARTICLE_GEN_DECAYED) ) mcp.setGeneratorStatus(2); + else if ( mask.isSet(G4PARTICLE_GEN_DOCUMENTATION) ) mcp.setGeneratorStatus(3); + else if ( mask.isSet(G4PARTICLE_GEN_BEAM) ) mcp.setGeneratorStatus(4); + else if ( mask.isSet(G4PARTICLE_GEN_OTHER) ) mcp.setGeneratorStatus(9); + } + + // Set simulation status + mcp.setCreatedInSimulation( mask.isSet(G4PARTICLE_SIM_CREATED) ); + mcp.setBackscatter( mask.isSet(G4PARTICLE_SIM_BACKSCATTER) ); + mcp.setVertexIsNotEndpointOfParent( mask.isSet(G4PARTICLE_SIM_PARENT_RADIATED) ); + mcp.setDecayedInTracker( mask.isSet(G4PARTICLE_SIM_DECAY_TRACKER) ); + mcp.setDecayedInCalorimeter( mask.isSet(G4PARTICLE_SIM_DECAY_CALO) ); + mcp.setHasLeftDetector( mask.isSet(G4PARTICLE_SIM_LEFT_DETECTOR) ); + mcp.setStopped( mask.isSet(G4PARTICLE_SIM_STOPPED) ); + mcp.setOverlay( false ); + + //fg: if simstatus !=0 we have to set the generator status to 0: + if( mcp.isCreatedInSimulation() ) + mcp.setGeneratorStatus( 0 ) ; + + mcp.setSpin(p->spin); + mcp.setColorFlow(p->colorFlow); + + p_ids[id] = cnt++; + p_part.push_back(p); + } + + // Now establish parent-daughter relationships + for(size_t i=0; i < p_ids.size(); ++i) { + const Geant4Particle* p = p_part[i]; + auto q = m_particles[i]; + + for (const auto& idau : p->daughters) { + const auto k = p_ids.find(idau); + if (k == p_ids.end()) { + fatal("+++ Particle %d: FAILED to find daughter with ID:%d",p->id,idau); + continue; + } + int iqdau = (*k).second; + auto qdau = m_particles[iqdau]; + q.addToDaughters(qdau); + } + + for (const auto& ipar : p->parents) { + if (ipar >= 0) { // A parent ID of -1 means NO parent, because a base of 0 is perfectly legal + const auto k = p_ids.find(ipar); + if (k == p_ids.end()) { + fatal("+++ Particle %d: FAILED to find parent with ID:%d",p->id,ipar); + continue; + } + int iqpar = (*k).second; + auto qpar = m_particles[iqpar]; + q.addToParents(qpar); + } + } + } + } +} + +/// Callback to store the Geant4 event +void Geant4Output2EDM4hep_DRC::saveEvent(OutputContext& ctxt) { + EventParameters* parameters = context()->event().extension(false); + int runNumber(0), eventNumber(0); + const int eventNumberOffset(m_eventNumberOffset > 0 ? m_eventNumberOffset : 0); + const int runNumberOffset(m_runNumberOffset > 0 ? m_runNumberOffset : 0); + double eventWeight{0}; + // Get event number, run number and parameters from extension ... + if ( parameters ) { + runNumber = parameters->runNumber() + runNumberOffset; + eventNumber = parameters->eventNumber() + eventNumberOffset; + parameters->extractParameters(m_frame); +#if podio_VERSION_MAJOR > 0 || podio_VERSION_MINOR > 16 || podio_VERSION_PATCH > 2 + // This functionality is only present in podio > 0.16.2 + eventWeight = m_frame.getParameter("EventWeights"); +#endif + } else { // ... or from DD4hep framework + runNumber = m_runNo + runNumberOffset; + eventNumber = ctxt.context->GetEventID() + eventNumberOffset; + } + printout(INFO,"Geant4Output2EDM4hep_DRC","+++ Saving EDM4hep event %d run %d.", eventNumber, runNumber); + + // this does not compile as create() is we only get a const ref - need to review PODIO EventStore API + edm4hep::EventHeaderCollection header_collection; + auto header = header_collection.create(); + header.setRunNumber(runNumber); + header.setEventNumber(eventNumber); + header.setWeight(eventWeight); + //not implemented in EDM4hep ? header.setDetectorName(context()->detectorDescription().header().name()); + header.setTimeStamp( std::time(nullptr) ) ; + m_frame.put( std::move(header_collection), "EventHeader"); + + saveEventParameters(m_eventParametersInt); + saveEventParameters(m_eventParametersFloat); + saveEventParameters(m_eventParametersString); + + Geant4ParticleMap* part_map = context()->event().extension(false); + if ( part_map ) { + print("+++ Saving %d EDM4hep particles....",int(part_map->particleMap.size())); + if ( part_map->particleMap.size() > 0 ) { + saveParticles(part_map); + } + } +} + +/** + * Helper struct that can be used together with map::try_emplace to construct + * the encoding only once per collection (name). + */ +struct LazyEncodingExtraction { + /// Constructor that does effectively nothing. This will be called in every + /// try_emplace call + LazyEncodingExtraction(Geant4HitCollection* coll) : m_coll(coll) {} + /// Defer the real work to the implicit conversion to std::string that will + /// only be called if the value is actually emplaced into the map + operator std::string() const { + const auto* sd = m_coll->sensitive(); + return dd4hep::sim::Geant4ConversionHelper::encoding(sd->sensitiveDetector()); + } +private: + Geant4HitCollection* m_coll{nullptr}; +}; + + +/// Callback to store each Geant4 hit collection +void Geant4Output2EDM4hep_DRC::saveCollection(OutputContext& /*ctxt*/, G4VHitsCollection* collection) { + Geant4HitCollection* coll = dynamic_cast(collection); + std::string colName = collection->GetName(); + if( coll == nullptr ){ + error(" no Geant4HitCollection: %s ", colName.c_str()); + return ; + } + size_t nhits = collection->GetSize(); + Geant4ParticleMap* pm = context()->event().extension(false); + debug("+++ Saving EDM4hep collection %s with %d entries.", colName.c_str(), int(nhits)); + + // Using try_emplace here to only fill this the first time we come across + m_cellIDEncodingStrings.try_emplace(colName, LazyEncodingExtraction{coll}); + + //------------------------------------------------------------------- + if( typeid( Geant4Tracker::Hit ) == coll->type().type() ){ + // Create the hit container even if there are no entries! + Geant4Sensitive* sd = coll->sensitive(); + int hit_creation_mode = sd->hitCreationMode(); + + auto& hits = m_trackerHits[colName]; + for(unsigned i=0 ; i < nhits ; ++i){ + auto sth = hits->create(); + const Geant4Tracker::Hit* hit = coll->hit(i); + const Geant4Tracker::Hit::Contribution& t = hit->truth; + int trackID = pm->particleID(t.trackID); + auto mcp = m_particles.at(trackID); + const auto& mom = hit->momentum; + const auto& pos = hit->position; + edm4hep::Vector3f(); + sth.setCellID( hit->cellID ) ; + sth.setEDep(hit->energyDeposit/CLHEP::GeV); + sth.setPathLength(hit->length/CLHEP::mm); + sth.setTime(hit->truth.time/CLHEP::ns); + sth.setMCParticle(mcp); + // sth.setParticle(std::move(mcp)); + sth.setPosition( {pos.x()/CLHEP::mm, pos.y()/CLHEP::mm, pos.z()/CLHEP::mm} ); + sth.setMomentum( {float(mom.x()/CLHEP::GeV),float(mom.y()/CLHEP::GeV),float(mom.z()/CLHEP::GeV)} ); + auto particleIt = pm->particles().find(trackID); + if( ( particleIt != pm->particles().end()) ){ + // if the original track ID of the particle is not the same as the + // original track ID of the hit it was produced by an MCParticle that + // is no longer stored + sth.setProducedBySecondary( (particleIt->second->originalG4ID != t.trackID) ); + } + } + //------------------------------------------------------------------- + } + else if( typeid( Geant4Calorimeter::Hit ) == coll->type().type() ){ + + Geant4Sensitive* sd = coll->sensitive(); + int hit_creation_mode = sd->hitCreationMode(); + + // Create the hit container even if there are no entries! + auto& hits = m_calorimeterHits[colName]; + for(unsigned i=0 ; i < nhits ; ++i){ + auto sch = hits.first->create(); + const Geant4Calorimeter::Hit* hit = coll->hit(i); + const auto& pos = hit->position; + sch.setCellID( hit->cellID ); + sch.setPosition({float(pos.x()/CLHEP::mm), float(pos.y()/CLHEP::mm), float(pos.z()/CLHEP::mm)}); + sch.setEnergy( hit->energyDeposit/CLHEP::GeV ); + + + // now add the individual step contributions + for(auto ci=hit->truth.begin(); ci != hit->truth.end(); ++ci){ + + auto sCaloHitCont = hits.second->create(); + sch.addToContributions( sCaloHitCont ); + + const Geant4HitData::Contribution& c = *ci; + int trackID = pm->particleID(c.trackID); + auto mcp = m_particles.at(trackID); + sCaloHitCont.setEnergy( c.deposit/CLHEP::GeV ); + sCaloHitCont.setTime( c.time/CLHEP::ns ); + sCaloHitCont.setParticle( mcp ); + + if ( hit_creation_mode == Geant4Sensitive::DETAILED_MODE ) { + edm4hep::Vector3f p(c.x/CLHEP::mm, c.y/CLHEP::mm, c.z/CLHEP::mm); + sCaloHitCont.setPDG( c.pdgID ); + sCaloHitCont.setStepPosition( p ); + } + } + } + //------------------------------------------------------------------- + } + else if( typeid( Geant4DRCalorimeter::Hit ) == coll->type().type() ){ + Geant4Sensitive* sd = coll->sensitive(); + int hit_creation_mode = sd->hitCreationMode(); + // Create the hit container even if there are no entries! + auto& hits = m_calorimeterHits[colName]; + auto& DRhits = m_drcaloHits[colName]; + auto& DRwaves = m_drcaloWaves[colName]; + + for(unsigned i=0 ; i < nhits ; ++i){ + auto sch = hits.first->create(); + const Geant4DRCalorimeter::Hit* hit = coll->hit(i); + const auto& pos = hit->position; + sch.setCellID( hit->cellID ); + sch.setPosition({float(pos.x()/CLHEP::mm), float(pos.y()/CLHEP::mm), float(pos.z()/CLHEP::mm)}); + sch.setEnergy( hit->energyDeposit/CLHEP::GeV ); + + // now add the individual step contributions + for(auto ci=hit->truth.begin(); ci != hit->truth.end(); ++ci){ + + auto sCaloHitCont = hits.second->create(); + sch.addToContributions( sCaloHitCont ); + + const Geant4HitData::Contribution& c = *ci; + int trackID = pm->particleID(c.trackID); + auto mcp = m_particles.at(trackID); + sCaloHitCont.setEnergy( c.deposit/CLHEP::GeV ); + sCaloHitCont.setTime( c.time/CLHEP::ns ); + sCaloHitCont.setParticle( mcp ); + + if ( hit_creation_mode == Geant4Sensitive::DETAILED_MODE ) { + edm4hep::Vector3f p(c.x/CLHEP::mm, c.y/CLHEP::mm, c.z/CLHEP::mm); + sCaloHitCont.setPDG( c.pdgID ); + sCaloHitCont.setStepPosition( p ); + } + } + + // For DRC raw calo hit & raw time series + auto rawCaloHits = DRhits.first->create(); + auto rawTimeStruct = DRhits.second->create(); + auto rawWaveStruct = DRwaves->create(); + + float samplingT = hit->GetSamplingTime(); + float timeStart = hit->GetTimeStart(); + float timeEnd = hit->GetTimeEnd(); + auto& timemap = hit->GetTimeStruct(); + + rawTimeStruct.setInterval(samplingT); + rawTimeStruct.setTime(timeStart); + rawTimeStruct.setCharge( static_cast(hit->GetPhotonCount()) ); + rawTimeStruct.setCellID( hit->cellID ); + + // abuse time series for saving wavelength spectrum (for R&D purpose) + float samplingW = hit->GetSamplingWavlen(); + float wavMax = hit->GetWavlenMax(); + float wavMin = hit->GetWavlenMin(); + auto& wavmap = hit->GetWavlenSpectrum(); + rawWaveStruct.setInterval(samplingW); + rawWaveStruct.setTime(wavMin); + rawWaveStruct.setCharge( static_cast(hit->GetPhotonCount()) ); + rawWaveStruct.setCellID( hit->cellID ); + + unsigned nbinTime = static_cast((timeEnd-timeStart)/samplingT); + unsigned nbinWav = static_cast((wavMax-wavMin)/samplingW); + int peakTime = 0.; + int peakVal = 0; + + // same as the ROOT TH1 binning scheme (0: underflow, nbin+1:overflow) + for (unsigned itime = 1; itime < nbinTime+1; itime++) { + int count = 0; + + if ( timemap.find(itime)!=timemap.end() ) + count = timemap.at(itime); + + int candidate = std::max( peakVal, count ); + + if ( peakVal < candidate ) { + peakVal = candidate; + peakTime = itime; + } + + rawTimeStruct.addToAdcCounts(count); + } + + for (unsigned iwav = 1; iwav < nbinWav+1; iwav++) { + int count = 0; + + if ( wavmap.find(iwav)!=wavmap.end() ) + count = wavmap.at(iwav); + + rawWaveStruct.addToAdcCounts(count); + } + + rawCaloHits.setCellID( hit->cellID ); + rawCaloHits.setAmplitude( hit->GetPhotonCount() ); + rawCaloHits.setTimeStamp( peakTime-1 + static_cast(timeStart/samplingT) ); + } + //------------------------------------------------------------------- + } else { + error("+++ unknown type in Geant4HitCollection %s ", coll->type().type().name()); + } +}