diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9896f2f564d6a31035e4b61c05cb8de6aa3be421..5f0625d6402a1866a20c29535558a48235c51c05 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -192,6 +192,8 @@ overlap_check_geant4:full_fast: script: ## disable fibers in ECAL for normal overlap check - sed -i '/<fiber/,+6d' ${DETECTOR_PATH}/compact/ecal_barrel_interlayers.xml + ## reduce the number of fibers in Hadron EMCal for overlap check + - sed -i 's/radius="EcalEndcapP_FiberRadius"/radius="EcalEndcapP_FiberRadius*10"/' ${DETECTOR_PATH}/compact/ci_ecal_scfi.xml - python scripts/checkOverlaps.py -c ${DETECTOR_PATH}/athena.xml | tee doc/overlap_check_geant4.out - echo "$(cat doc/overlap_check_geant4.out | grep GeomVol1002 | wc -l) overlaps..." - if [[ "$(cat doc/overlap_check_geant4.out | grep GeomVol1002 | wc -l)" -gt "0" ]] ; then echo "Overlaps exist!" && false ; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d97ae91e55f6d0654a093c8ee3d5e0faae5f441..5ec35297e96c143e440b4fcb3d9ed3199b0a0e71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ dd4hep_add_plugin(${a_lib_name} SOURCES src/HybridCalorimeter_geo.cpp src/MRich_geo.cpp src/PolyhedraEndcapCalorimeter2_geo.cpp + src/ScFiCalorimeter_geo.cpp src/ShashlikCalorimeter_geo.cpp src/SimpleDiskDetector_geo.cpp src/SolenoidCoil_geo.cpp diff --git a/bin/generate_prim_file b/bin/generate_prim_file index 2274f06101c6293258debbdcf18aa4d12deb5116..a0593210bd2e13fd48f68ba165f57bee0e4c18b3 100755 --- a/bin/generate_prim_file +++ b/bin/generate_prim_file @@ -54,7 +54,7 @@ parser.add_argument('-t', '--tag', type=str,dest='file_tag', parser.add_argument('--timeout', type=int, default=60, help='Timeout in seconds') - + parser.add_argument('passthrough', nargs='*') args = parser.parse_args() @@ -71,6 +71,11 @@ args.out_dir = os.path.abspath(args.out_dir) args.compact = os.path.abspath(args.compact) macro = os.path.abspath(macro) +# adjust fiber radius to reduce the number of fibers +compact_dir = os.path.dirname(os.path.realpath(args.compact)) +ci_ecal = os.path.join(compact_dir, 'compact', 'ci_ecal_scfi.xml') +os.system('sed -i \'s/radius=\"EcalEndcapP_FiberRadius\"/radius=\"EcalEndcapP_FiberRadius*10\"/\' {}'.format(ci_ecal)) + prim_file = 'g4_0000.prim' dawn_env = os.environ.copy() dawn_env['DAWN_BATCH'] = 'a' @@ -138,6 +143,9 @@ for proc in psutil.process_iter(): print('kill {}, generated from {}'.format(pinfo, p.pid)) os.kill(pinfo['pid'], signal.SIGTERM) +# revert the change +os.system('sed -i \'s/radius=\"EcalEndcapP_FiberRadius*10\"/radius=\"EcalEndcapP_FiberRadius\"/\' {}'.format(ci_ecal)) + line = b'stderr outputs:\n' while line: print(line.decode('utf-8'), end='') diff --git a/compact/ce_ecal_crystal_glass.xml b/compact/ce_ecal_crystal_glass.xml index 2634afa199e7ba06251f6311af02329577e82d7a..cea47efc7029ef807bc8f20ba56e4304645104cf 100644 --- a/compact/ce_ecal_crystal_glass.xml +++ b/compact/ce_ecal_crystal_glass.xml @@ -60,32 +60,29 @@ <lines sector="2" mirrorx="true" mirrory="true"> <module sizex="GlassModule_sx" sizey="GlassModule_sy" sizez="GlassModule_sz" material="PbGlass" vis="EcalEndcapNModuleVis"/> <wrapper thickness="GlassModule_wrap" material="Epoxy" vis="InvisibleWithDaughters"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*1/2." z="GlassModule_z0" begin="13" nmods="13"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*3/2." z="GlassModule_z0" begin="13" nmods="13"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*5/2." z="GlassModule_z0" begin="13" nmods="13"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*7/2." z="GlassModule_z0" begin="12" nmods="14"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*9/2." z="GlassModule_z0" begin="12" nmods="14"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*11/2." z="GlassModule_z0" begin="12" nmods="14"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*13/2." z="GlassModule_z0" begin="11" nmods="15"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*15/2." z="GlassModule_z0" begin="10" nmods="15"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*17/2." z="GlassModule_z0" begin="9" nmods="15"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*19/2." z="GlassModule_z0" begin="8" nmods="16"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*21/2." z="GlassModule_z0" begin="7" nmods="16"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*23/2." z="GlassModule_z0" begin="6" nmods="17"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*25/2." z="GlassModule_z0" begin="3" nmods="19"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*27/2." z="GlassModule_z0" begin="0" nmods="22"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*29/2." z="GlassModule_z0" begin="0" nmods="21"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*31/2." z="GlassModule_z0" begin="0" nmods="20"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*33/2." z="GlassModule_z0" begin="0" nmods="20"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*35/2." z="GlassModule_z0" begin="0" nmods="19"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*37/2." z="GlassModule_z0" begin="0" nmods="19"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*39/2." z="GlassModule_z0" begin="0" nmods="17"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*41/2." z="GlassModule_z0" begin="0" nmods="15"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*43/2." z="GlassModule_z0" begin="0" nmods="14"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*45/2." z="GlassModule_z0" begin="0" nmods="12"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*47/2." z="GlassModule_z0" begin="0" nmods="10"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*49/2." z="GlassModule_z0" begin="0" nmods="8"/> - <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*51/2." z="GlassModule_z0" begin="0" nmods="7"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*1/2." z="GlassModule_z0" begin="13" nmods="10"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*3/2." z="GlassModule_z0" begin="13" nmods="10"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*5/2." z="GlassModule_z0" begin="13" nmods="10"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*7/2." z="GlassModule_z0" begin="12" nmods="10"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*9/2." z="GlassModule_z0" begin="12" nmods="10"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*11/2." z="GlassModule_z0" begin="12" nmods="10"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*13/2." z="GlassModule_z0" begin="11" nmods="11"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*15/2." z="GlassModule_z0" begin="10" nmods="11"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*17/2." z="GlassModule_z0" begin="9" nmods="12"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*19/2." z="GlassModule_z0" begin="8" nmods="13"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*21/2." z="GlassModule_z0" begin="7" nmods="13"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*23/2." z="GlassModule_z0" begin="6" nmods="14"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*25/2." z="GlassModule_z0" begin="3" nmods="16"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*27/2." z="GlassModule_z0" begin="0" nmods="18"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*29/2." z="GlassModule_z0" begin="0" nmods="18"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*31/2." z="GlassModule_z0" begin="0" nmods="16"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*33/2." z="GlassModule_z0" begin="0" nmods="16"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*35/2." z="GlassModule_z0" begin="0" nmods="14"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*37/2." z="GlassModule_z0" begin="0" nmods="13"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*39/2." z="GlassModule_z0" begin="0" nmods="11"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*41/2." z="GlassModule_z0" begin="0" nmods="10"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*43/2." z="GlassModule_z0" begin="0" nmods="7"/> + <line axis="x" x="GlassModule_dx/2." y="GlassModule_dy*45/2." z="GlassModule_z0" begin="0" nmods="3"/> </lines> </placements> </detector> diff --git a/compact/ci_ecal_scfi.xml b/compact/ci_ecal_scfi.xml new file mode 100644 index 0000000000000000000000000000000000000000..a55596439d6146055952dd457bec2acc91eb1903 --- /dev/null +++ b/compact/ci_ecal_scfi.xml @@ -0,0 +1,59 @@ +<lccdd> + <define> + <constant name="EcalEndcapP_rmax" value="Solenoid_rmax"/> + <constant name="EcalEndcapP_FiberRadius" value="0.235*mm"/> + <constant name="EcalEndcapP_FiberOffset" value="0.5*mm"/> + <constant name="EcalEndcapP_FiberSpaceX" value="0.265*mm"/> + <constant name="EcalEndcapP_FiberSpaceY" value="0.425*mm"/> + </define> + + + <limits> + </limits> + + <regions> + </regions> + + <!-- Common Generic visualization attributes --> + <comment>Common Generic visualization attributes</comment> + <display> + </display> + + <detectors> + + <comment> + ------------------------------------------ + Forward (Positive Z) Endcap EM Calorimeter + ------------------------------------------ + An EM calorimeter with shashlik hexagon modules + </comment> + <detector id="ECalEndcapP_ID" + name="EcalEndcapP" + type="ScFiCalorimeter" + vis="InvisibleWithDaughters" + readout="EcalEndcapPHits"> + <position x="0" y="0" z="EcalEndcapP_zmin + EcalEndcapP_length/2."/> + <dimensions rmin="EcalEndcapP_rmin" rmax="EcalEndcapP_rmax" length="EcalEndcapP_length"/> + <module sizex="25*mm" sizey="25*mm" sizez="170*mm" material="TungstenDens24" vis="EcalEndcapVis"> + <fiber material="Polystyrene" + radius="EcalEndcapP_FiberRadius" + offset="EcalEndcapP_FiberOffset" + spacex="EcalEndcapP_FiberSpaceX" + spacey="EcalEndcapP_FiberSpaceY"/> + </module> + </detector> + </detectors> + + <!-- Definition of the readout segmentation/definition --> + <readouts> + <readout name="EcalEndcapPHits"> + <segmentation type="NoSegmentation"/> + <id>system:8,ring:8,module:20,fiber_x:8,fiber_y:8</id> + </readout> + </readouts> + + <plugins> + </plugins> + +</lccdd> + diff --git a/compact/ecal.xml b/compact/ecal.xml index 4f54d2058245118836b03b37f9164b98f1c9f326..0b743c8854bf7e2dda8286e3d3775597555bb3bb 100644 --- a/compact/ecal.xml +++ b/compact/ecal.xml @@ -8,13 +8,15 @@ <documentation level="10"> ### Ecal configuration </documentation> - <include ref="ci_ecal.xml"/> - <!--<include ref="compact/ci_ecal_shashlik.xml"/>--> - <!--<include ref="compact/ce_ecal.xml"/>--> -<!-- <include ref="ce_ecal_crystal_glass.xml"/>--> + <include ref="ci_ecal_scfi.xml"/> + <!--<include ref="ci_ecal.xml"/>--> + + <!--<include ref="ce_ecal.xml"/>--> + <!--<include ref="ce_ecal_crystal_glass.xml"/>--> <include ref="hybrid_ecal.xml"/> - <!-- <include ref="compact/ecal_barrel.xml"/> --> - <!-- <include ref="compact/ecal_barrel_hybrid.xml"/> --> + + <!-- <include ref="ecal_barrel.xml"/> --> + <!-- <include ref="ecal_barrel_hybrid.xml"/> --> <include ref="ecal_barrel_interlayers.xml"/> </lccdd> diff --git a/src/ScFiCalorimeter_geo.cpp b/src/ScFiCalorimeter_geo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5fc14cef776e270a52bb3b9e5c2fc5ede9756dc4 --- /dev/null +++ b/src/ScFiCalorimeter_geo.cpp @@ -0,0 +1,174 @@ +//========================================================================== +// Scintillating fiber calorimeter with tower shape blocks +// reference: https://github.com/adamjaro/lmon/blob/master/calo/src/WScFiZXv3.cxx +// Support disk placement +//-------------------------------------------------------------------------- +// Author: Chao Peng (ANL) +// Date: 07/19/2021 +//========================================================================== + +#include "GeometryHelpers.h" +#include "DD4hep/DetFactoryHelper.h" +#include <XML/Helper.h> +#include <iostream> +#include <algorithm> +#include <tuple> +#include <math.h> + +using namespace dd4hep; +using Point = ROOT::Math::XYPoint; + +std::tuple<Volume, Position> build_module(const Detector &desc, const xml::Component &mod_x, SensitiveDetector &sens); + +// helper function to get x, y, z if defined in a xml component +template<class XmlComp> +Position get_xml_xyz(const XmlComp &comp, dd4hep::xml::Strng_t name) +{ + Position pos(0., 0., 0.); + if (comp.hasChild(name)) { + auto child = comp.child(name); + pos.SetX(dd4hep::getAttrOrDefault<double>(child, _Unicode(x), 0.)); + pos.SetY(dd4hep::getAttrOrDefault<double>(child, _Unicode(y), 0.)); + pos.SetZ(dd4hep::getAttrOrDefault<double>(child, _Unicode(z), 0.)); + } + return pos; +} + +// main +static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDetector sens) +{ + xml::DetElement detElem = handle; + std::string detName = detElem.nameStr(); + int detID = detElem.id(); + DetElement det(detName, detID); + sens.setType("calorimeter"); + auto dim = detElem.dimensions(); + auto rmin = dim.rmin(); + auto rmax = dim.rmax(); + auto length = dim.length(); + auto phimin = dd4hep::getAttrOrDefault<double>(dim, _Unicode(phimin), 0.); + auto phimax = dd4hep::getAttrOrDefault<double>(dim, _Unicode(phimax), 2.*M_PI); + // envelope + Tube envShape(rmin, rmax, length/2., phimin, phimax); + Volume env(detName + "_envelope", envShape, desc.material("Air")); + env.setVisAttributes(desc.visAttributes(detElem.visStr())); + + // build shashlik module + auto [modVol, modSize] = build_module(desc, detElem.child(_Unicode(module)), sens); + double modSizeR = std::sqrt(modSize.x() * modSize.x() + modSize.y() * modSize.y()); + double assembly_rwidth = modSizeR*2.; + int nas = int((rmax - rmin) / assembly_rwidth) + 1; + std::vector<Assembly> assemblies; + for (int i = 0; i < nas; ++i) { + Assembly assembly(detName + Form("_ring%d", i + 1)); + auto assemblyPV = env.placeVolume(assembly, Position{0., 0., 0.}); + assemblyPV.addPhysVolID("ring", i + 1); + assemblies.emplace_back(std::move(assembly)); + } + // std::cout << assemblies.size() << std::endl; + + int modid = 1; + for (int ix = 0; ix < int(2.*rmax / modSize.x()) + 1; ++ix) { + double mx = modSize.x() * ix - rmax; + for (int iy = 0; iy < int(2.*rmax / modSize.y()) + 1; ++iy) { + double my = modSize.y() * iy - rmax; + double mr = std::sqrt(mx*mx + my*my); + if (mr - modSizeR >= rmin && mr + modSizeR <= rmax) { + int ias = int((mr - rmin) / assembly_rwidth); + auto &assembly = assemblies[ias]; + auto modPV = assembly.placeVolume(modVol, Position(mx, my, 0.)); + modPV.addPhysVolID("module", modid++); + } + } + } + + desc.add(Constant(detName + "_NModules", std::to_string(modid - 1))); + + for (auto &assembly : assemblies) { + assembly.ptr()->Voxelize(""); + } + + // detector position and rotation + auto pos = get_xml_xyz(detElem, _Unicode(position)); + auto rot = get_xml_xyz(detElem, _Unicode(rotation)); + Volume motherVol = desc.pickMotherVolume(det); + Transform3D tr = Translation3D(pos.x(), pos.y(), pos.z()) * RotationZYX(rot.z(), rot.y(), rot.x()); + PlacedVolume envPV = motherVol.placeVolume(env, tr); + envPV.addPhysVolID("system", detID); + det.setPlacement(envPV); + return det; +} + +// helper function to build module with scintillating fibers +std::tuple<Volume, Position> build_module(const Detector &desc, const xml::Component &mod_x, SensitiveDetector &sens) +{ + auto sx = mod_x.attr<double>(_Unicode(sizex)); + auto sy = mod_x.attr<double>(_Unicode(sizey)); + auto sz = mod_x.attr<double>(_Unicode(sizez)); + + Box modShape(sx/2., sy/2., sz/2.); + auto modMat = desc.material(mod_x.attr<std::string>(_Unicode(material))); + Volume modVol("module_vol", modShape, modMat); + if (mod_x.hasAttr(_Unicode(vis))) { + modVol.setVisAttributes(desc.visAttributes(mod_x.attr<std::string>(_Unicode(vis)))); + } + + auto fiber_x = mod_x.child(_Unicode(fiber)); + auto fr = fiber_x.attr<double>(_Unicode(radius)); + auto fsx = fiber_x.attr<double>(_Unicode(spacex)); + auto fsy = fiber_x.attr<double>(_Unicode(spacey)); + auto foff = dd4hep::getAttrOrDefault<double>(fiber_x, _Unicode(offset), 0.5*mm); + auto fiberMat = desc.material(fiber_x.attr<std::string>(_Unicode(material))); + Tube fiberShape(0., fr, sz/2.); + Volume fiberVol("fiber_vol", fiberShape, fiberMat); + fiberVol.setSensitiveDetector(sens); + + // Fibers are placed in a honeycomb with the radius = sqrt(3)/2. * hexagon side length + // So each fiber is fully contained in a regular hexagon, which are placed as + // ______________________________________ + // | ____ ____ | + // even: | / \ / \ | + // | ____/ \____/ \____ | + // | / \ / \ / \ | + // odd: | / \____/ \____/ \ | + // | \ / \ / \ / | + // | \____/ \____/ \____/ | + // even: | \ / \ / | + // | \____/ \____/ ___|___ + // |____________________________________|___offset + // | | + // |offset + // the parameters space x and space y are used to add additional spaces between the hexagons + double fside = 2. / std::sqrt(3.) * fr; + double fdistx = 2. * fside + fsx; + double fdisty = 2. * fr + fsy; + + // maximum numbers of the fibers, help narrow the loop range + int nx = int(sx / (2.*fr)) + 1; + int ny = int(sy / (2.*fr)) + 1; + + // std::cout << sx << ", " << sy << ", " << fr << ", " << nx << ", " << ny << std::endl; + + // place the fibers + double y0 = (foff + fside); + int nfibers = 0; + for (int iy = 0; iy < ny; ++iy) { + double y = y0 + fdisty * iy; + // about to touch the boundary + if ((sy - y) < y0) { break; } + double x0 = (iy % 2) ? (foff + fside) : (foff + fside + fdistx / 2.); + for (int ix = 0; ix < nx; ++ix) { + double x = x0 + fdistx * ix; + // about to touch the boundary + if ((sx - x) < x0) { break; } + auto fiberPV = modVol.placeVolume(fiberVol, nfibers++, Position{x - sx/2., y - sy/2., 0}); + std::cout << "(" << ix << ", " << iy << ", " << x - sx/2. << ", " << y - sy/2. << ", " << fr << "),\n"; + fiberPV.addPhysVolID("fiber_x", ix + 1).addPhysVolID("fiber_y", iy + 1); + } + } + + return std::make_tuple(modVol, Position{sx, sy, sz}); +} + +DECLARE_DETELEMENT(ScFiCalorimeter, create_detector) +