From fbb0f7061ca9013cf43f4167905004a869b95404 Mon Sep 17 00:00:00 2001 From: Chao Peng <cpeng@anl.gov> Date: Fri, 11 Jun 2021 04:15:19 +0000 Subject: [PATCH] Add a generic plugin for homogeneous calorimeter --- src/GeometryHelpers.cpp | 5 + src/HomogeneousCalorimeter_geo.cpp | 310 +++++++++++++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 src/HomogeneousCalorimeter_geo.cpp diff --git a/src/GeometryHelpers.cpp b/src/GeometryHelpers.cpp index c60c239b..201b88d6 100644 --- a/src/GeometryHelpers.cpp +++ b/src/GeometryHelpers.cpp @@ -60,6 +60,11 @@ void add_square(Point p, std::vector<Point> &res, double lside, double rmin, dou // fill squares std::vector<Point> fillSquares(Point ref, double lside, double rmin, double rmax, double phmin, double phmax) { + // convert (0, 2pi) to (-pi, pi) + if (phmax > M_PI) { + phmin -= M_PI; + phmax -= M_PI; + } // start with a seed square and find one in the ring // move to center ref = ref - Point(int(ref.x()/lside)*lside, int(ref.y()/lside)*lside); diff --git a/src/HomogeneousCalorimeter_geo.cpp b/src/HomogeneousCalorimeter_geo.cpp new file mode 100644 index 00000000..f3d74c7f --- /dev/null +++ b/src/HomogeneousCalorimeter_geo.cpp @@ -0,0 +1,310 @@ +//========================================================================== +// A general implementation for homogeneous calorimeter +// it supports three types of placements +// 1. Module placement with module dimensions and positions +// 2. Array placement with module dimensions and numbers of row and columns +// 3. Disk placement with module dimensions and (Rmin, Rmax), and (Phimin, Phimax) +//-------------------------------------------------------------------------- +// Author: Chao Peng (ANL) +// Date: 06/09/2021 +//========================================================================== +#include "GeometryHelpers.h" +#include "DD4hep/DetFactoryHelper.h" +#include <XML/Helper.h> +#include <iostream> +#include <algorithm> +#include <math.h> + +using namespace dd4hep; +using namespace dd4hep::detail; + +/** \addtogroup calorimeters Calorimeters + */ + +/** \addtogroup Homogeneous Calorimeter + * \brief Type: **HomogeneousCalorimeter**. + * \author C. Peng + * \ingroup calorimeters + * + * + * \code + * <detector id="1" name="HyCal" type="HomogeneousCalorimeter" readout="EcalHits" vis="GreenVis"> + * <dimensions shape="box" sizex="120*cm" sizey="120*cm" sizez="46*cm"/> + * <position x="0" y="0" z="0"/> + * <rotation x="0" y="0" z="0"/> + * <placements> + * <array nrow="34" ncol="34" sector="1"> + * <position x="0" y="0" z="-9.73*cm"/> + * <module sizex="2.05*cm" sizey="2.05*cm" sizez="18*cm" vis="GreenVis" material="PbWO4"/> + * <wrapper thickness="0.015*cm" material="Epoxy" vis="WhiteVis"/> + * <removal row="16" col="16"/> + * <removal row="16" col="17"/> + * <removal row="17" col="16"/> + * <removal row="17" col="17"/> + * </array> + * <array nrow="6" ncol="24" sector="2"> + * <position x="-17*(2.05+0.015)*cm+12*(3.8+0.015)*cm" y="17*(2.05+0.015)*cm+3*(3.8+0.015)*cm" z="0"/> + * <module sizex="3.8*cm" sizey="3.8*cm" sizez="45*cm" vis="BlueVis" material="PbGlass"/> + * <wrapper thickness="0.015*cm" material="Epoxy" vis="WhiteVis"/> + * </array> + * <array nrow="24" ncol="6" sector="3"> + * <position x="17*(2.05+0.015)*cm+3*(3.8+0.015)*cm" y="17*(2.05+0.015)*cm-12*(3.8+0.015)*cm" z="0"/> + * <module sizex="3.8*cm" sizey="3.8*cm" sizez="45*cm" vis="BlueVis" material="PbGlass"/> + * <wrapper thickness="0.015*cm" material="Epoxy" vis="WhiteVis"/> + * </array> + * <array nrow="6" ncol="24" sector="4"> + * <position x="17*(2.05+0.015)*cm-12*(3.8+0.015)*cm" y="-17*(2.05+0.015)*cm-3*(3.8+0.015)*cm" z="0"/> + * <module sizex="3.8*cm" sizey="3.8*cm" sizez="45*cm" vis="BlueVis" material="PbGlass"/> + * <wrapper thickness="0.015*cm" material="Epoxy" vis="WhiteVis"/> + * </array> + * <array nrow="24" ncol="6" sector="3"> + * <position x="-17*(2.05+0.015)*cm-3*(3.8+0.015)*cm" y="-17*(2.05+0.015)*cm+12*(3.8+0.015)*cm" z="0"/> + * <module sizex="3.8*cm" sizey="3.8*cm" sizez="45*cm" vis="BlueVis" material="PbGlass"/> + * <wrapper thickness="0.015*cm" material="Epoxy" vis="WhiteVis"/> + * </array> + * </placements> + * </detector> + * + * <detector id="2" name="SomeBlocks" type="HomogeneousCalorimeter" readout="EcalHits" vis="GreenVis"> + * <dimensions shape="box" sizex="100*cm" sizey="100*cm" sizez="20.5*cm"/> + * <position x="0" y="0" z="30*cm"/> + * <rotation x="0" y="0" z="0"/> + * <placements> + * <blocks sector="1"/> + * <module sizex="2.05*cm" sizey="2.05*cm" sizez="20*cm" vis="GreenVis" material="PbWO4"/> + * <wrapper thickness="0.015*cm" material="Epoxy" vis="WhiteVis"/> + * <placement x="1*cm" y="1*cm" z="0"/> + * <placement x="-1*cm" y="1*cm" z="0"/> + * <placement x="1*cm" y="-1*cm" z="0"/> + * <placement x="-1*cm" y="-1*cm" z="0"/> + * </blocks> + * </placements> + * </detector> + * + * <detector id="2" name="DiskShapeCalorimeter" type="HomogeneousCalorimeter" readout="EcalHits" vis="GreenVis"> + * <dimensions shape="disk" rmin="25*cm" rmax="125*cm" length="20.5*cm" phimin="0" phimax="360*degree"/> + * <position x="0" y="0" z="-30*cm"/> + * <rotation x="0" y="0" z="0"/> + * <placements> + * <disk rmin="25*cm" rmax="125*cm" length="20.5*cm" phimin="0" phimax="360*degree" sector="1"/> + * <module sizex="2.05*cm" sizey="2.05*cm" sizez="20*cm" vis="GreenVis" material="PbWO4"/> + * <wrapper thickness="0.015*cm" material="Epoxy" vis="WhiteVis"/> + * </placements> + * </detector> + * \endcode + * + * @{ + */ + +// headers +static void add_blocks(Detector& desc, Volume &env, xml::Collection_t &plm, SensitiveDetector &sens, int id); +static void add_array(Detector& desc, Volume &env, xml::Collection_t &plm, SensitiveDetector &sens, int id); +static void add_disk(Detector& desc, Volume &env, xml::Collection_t &plm, SensitiveDetector &sens, int id); + +// helper function to get x, y, z if defined in a xml component +template<class XmlComp> +Position get_xml_xyz(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) +{ + static const std::string func = "HomogeneousCalorimeter"; + xml::DetElement detElem = handle; + std::string detName = detElem.nameStr(); + int detID = detElem.id(); + DetElement det(detName, detID); + sens.setType("calorimeter"); + + // top-level children + xml::Component dims = detElem.dimensions(); + + // build envelop from dimensions + std::string shape = dd4hep::getAttrOrDefault(dims, _Unicode(shape), ""); + // no shape input, try to determine shape from dimension variables + if (shape.empty()) { + if (dims.hasAttr(_Unicode(rmin)) && dims.hasAttr(_Unicode(rmax)) && dims.hasAttr(_Unicode(length))) { + shape = "disk"; + } else if (dims.hasAttr(_Unicode(sizex)) && dims.hasAttr(_Unicode(sizey)) && dims.hasAttr(_Unicode(sizez))) { + shape = "box"; + } else { + std::cerr << func << " Error: Cannot determine shape of the calorimeter. " + "Add shape (box, or disk) into dimensions\n"; + return det; + } + } + + Volume envVol(detName + "_envelope"); + std::string filler = dd4hep::getAttrOrDefault(detElem, _Unicode(filler), "Air"); + envVol.setMaterial(desc.material(filler)); + envVol.setVisAttributes(desc.visAttributes(detElem.visStr())); + + // convert to lower case + std::transform(shape.begin(), shape.end(), shape.begin(), [](unsigned char c){ return std::tolower(c); }); + + if (shape == "box") { + double sx = dims.attr<double>(_Unicode(sizex)); + double sy = dims.attr<double>(_Unicode(sizey)); + double sz = dims.attr<double>(_Unicode(sizez)); + envVol.setSolid(Box(sx/2., sy/2., sz/2.)); + } else if (shape == "disk") { + double rmin = dims.rmin(); + double rmax = dims.rmax(); + double length = dims.length(); + double phimin = dd4hep::getAttrOrDefault<double>(dims, _Unicode(phimin), 0.); + double phimax = dd4hep::getAttrOrDefault<double>(dims, _Unicode(phimax), 2*M_PI); + envVol.setSolid(Tube(rmin, rmax, length/2., phimin, phimax)); + } else { + std::cerr << func << " Error: Unsupported shape " << shape << ", use box or disk\n"; + return det; + } + + // module placement + xml::Component plm = detElem.child(_Unicode(placements)); + int sector = 1; + for (xml::Collection_t arr(plm, _Unicode(array)); arr; ++arr) { + add_array(desc, envVol, arr, sens, sector++); + } + for (xml::Collection_t mod(plm, _Unicode(blocks)); mod; ++mod) { + add_blocks(desc, envVol, mod, sens, sector++); + } + for (xml::Collection_t disk(plm, _Unicode(disk)); disk; ++disk) { + add_disk(desc, envVol, disk, sens, sector++); + } + + // 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(envVol, tr); + envPV.addPhysVolID("system", detID); + det.setPlacement(envPV); + return det; +} + +// helper function to build module with or w/o wrapper +Volume build_module(Detector &desc, xml::Collection_t &plm, SensitiveDetector &sens, Position &dim) +{ + auto mod = plm.child(_Unicode(module)); + auto sx = mod.attr<double>(_Unicode(sizex)); + auto sy = mod.attr<double>(_Unicode(sizey)); + auto sz = mod.attr<double>(_Unicode(sizez)); + dim = Position{sx, sy, sz}; + Box modShape(sx/2., sy/2., sz/2.); + auto modMat = desc.material(mod.attr<std::string>(_Unicode(material))); + Volume modVol("module_vol", modShape, modMat); + modVol.setSensitiveDetector(sens); + modVol.setVisAttributes(desc.visAttributes(mod.attr<std::string>(_Unicode(vis)))); + + // no wrapper + if (!plm.hasChild(_Unicode(wrapper))) { + return modVol; + // build wrapper + } else { + auto wrp = plm.child(_Unicode(wrapper)); + auto thickness = wrp.attr<double>(_Unicode(thickness)); + auto wrpMat = desc.material(wrp.attr<std::string>(_Unicode(material))); + Box wrpShape((sx + thickness)/2., (sy + thickness)/2., sz/2.); + Volume wrpVol("wrapper_vol", wrpShape, wrpMat); + wrpVol.placeVolume(modVol, Position(0., 0., 0.)); + wrpVol.setVisAttributes(desc.visAttributes(wrp.attr<std::string>(_Unicode(vis)))); + dim = Position{sx + thickness, sy + thickness, sz}; + return wrpVol; + } +} + +// place array of modules +static void add_array(Detector& desc, Volume &env, xml::Collection_t &plm, SensitiveDetector &sens, int sid) +{ + Position modSize; + auto modVol = build_module(desc, plm, sens, modSize); + int sector_id = dd4hep::getAttrOrDefault<int>(plm, _Unicode(sector), sid); + int nrow = plm.attr<int>(_Unicode(nrow)); + int ncol = plm.attr<int>(_Unicode(ncol)); + + // compute array position + double begx = -modSize.x()*ncol/2. + modSize.x()/2.; + double begy = modSize.y()*nrow/2. - modSize.y()/2.; + + std::vector<std::pair<int, int>> removals; + // get the removal list + for (xml::Collection_t rm(plm, _Unicode(removal)); rm; ++rm) { + removals.push_back({rm.attr<int>(_Unicode(row)), rm.attr<int>(_Unicode(col))}); + } + + // placement to mother + auto pos = get_xml_xyz(plm, _Unicode(position)); + auto rot = get_xml_xyz(plm, _Unicode(rotation)); + for (int i = 0; i < nrow; ++i) { + for (int j = 0; j < ncol; ++j) { + if (std::find(removals.begin(), removals.end(), std::pair<int, int>(i, j)) != removals.end()) { + continue; + } + double px = begx + modSize.x()*j; + double py = begy - modSize.y()*i; + Transform3D tr = Translation3D(pos.x() + px, pos.y() + py, pos.z()) + * RotationZYX(rot.z(), rot.y(), rot.x()); + auto modPV = env.placeVolume(modVol, tr); + modPV.addPhysVolID("sector", sector_id).addPhysVolID("module", i*ncol + j); + } + } +} + +// place modules +static void add_blocks(Detector& desc, Volume &env, xml::Collection_t &plm, SensitiveDetector &sens, int sid) +{ + Position modSize; + auto modVol = build_module(desc, plm, sens, modSize); + int sector_id = dd4hep::getAttrOrDefault<int>(plm, _Unicode(sector), sid); + + int mid = 1; + for (xml::Collection_t pl(plm, _Unicode(placement)); pl; ++pl, ++mid) { + Position pos(dd4hep::getAttrOrDefault<double>(pl, _Unicode(x), 0.), + dd4hep::getAttrOrDefault<double>(pl, _Unicode(y), 0.), + dd4hep::getAttrOrDefault<double>(pl, _Unicode(z), 0.)); + Position rot(dd4hep::getAttrOrDefault<double>(pl, _Unicode(rotx), 0.), + dd4hep::getAttrOrDefault<double>(pl, _Unicode(roty), 0.), + dd4hep::getAttrOrDefault<double>(pl, _Unicode(rotz), 0.)); + Transform3D tr = Translation3D(pos.x(), pos.y(), pos.z()) + * RotationZYX(rot.z(), rot.y(), rot.x()); + auto modPV = env.placeVolume(modVol, tr); + modPV.addPhysVolID("sector", sector_id).addPhysVolID("module", mid); + } +} + +// place disk of modules +static void add_disk(Detector& desc, Volume &env, xml::Collection_t &plm, SensitiveDetector &sens, int sid) +{ + Position modSize; + auto modVol = build_module(desc, plm, sens, modSize); + int sector_id = dd4hep::getAttrOrDefault<int>(plm, _Unicode(sector), sid); + double rmin = plm.attr<double>(_Unicode(rmin)); + double rmax = plm.attr<double>(_Unicode(rmax)); + double phimin = dd4hep::getAttrOrDefault<double>(plm, _Unicode(phimin), 0.); + double phimax = dd4hep::getAttrOrDefault<double>(plm, _Unicode(phimax), 2.*M_PI); + + auto points = ref::utils::fillSquares({0., 0.}, modSize.x(), rmin, rmax, phimin, phimax); + // placement to mother + auto pos = get_xml_xyz(plm, _Unicode(position)); + auto rot = get_xml_xyz(plm, _Unicode(rotation)); + int mid = 1; + for (auto &p : points) { + Transform3D tr = Translation3D(pos.x() + p.x(), pos.y() + p.y(), pos.z()) + * RotationZYX(rot.z(), rot.y(), rot.x()); + auto modPV = env.placeVolume(modVol, tr); + modPV.addPhysVolID("sector", sector_id).addPhysVolID("module", mid++); + } +} +//@} +DECLARE_DETELEMENT(HomogeneousCalorimeter, create_detector) + -- GitLab