Skip to content
Snippets Groups Projects
HomogeneousCalorimeter_geo.cpp 16 KiB
Newer Older
//==========================================================================
//  A general implementation for homogeneous calorimeter
//  it supports three types of placements
//  1. Individual 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)
//  4. Lines placement with module dimensions and (mirrorx, mirrory)
//     (NOTE: anchor point is the 0th block of the line instead of line center)
//--------------------------------------------------------------------------
//  Author: Chao Peng (ANL)
//  Date: 06/09/2021
//==========================================================================
#include "GeometryHelpers.h"
#include "DD4hep/DetFactoryHelper.h"
#include <XML/Helper.h>
#include <iostream>
#include <algorithm>
#include <tuple>
#include <math.h>

using namespace dd4hep;

/** \addtogroup calorimeters Calorimeters
 */

/** \addtogroup Homogeneous Calorimeter
 * \brief Type: **HomogeneousCalorimeter**.
 * \author C. Peng
 * \ingroup calorimeters
 *
 *
 * \code
 *   <detector id="1" name="HyCal" type="HomogeneousCalorimeter" readout="EcalHits">
 *     <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">
 *     <position x="0" y="0" z="30*cm"/>
 *     <rotation x="0" y="0" z="0"/>
 *     <placements>
 *         <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" id="1"/>
 *         <placement x="-1*cm" y="1*cm" z="0" id="2"/>
 *         <placement x="1*cm" y="-1*cm" z="0" id="3"/>
 *         <placement x="-1*cm" y="-1*cm" z="0" id="4"/>
 *       </individuals>
 *     </placements>
 *   </detector>
 *
 *   <detector id="2" name="DiskShapeCalorimeter" type="HomogeneousCalorimeter" readout="EcalHits">
 *     <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>
 *
 *   <detector id="3" name="SomeLines" type="HomogeneousCalorimeter" readout="EcalHits">
 *     <position x="0" y="0" z="60*cm"/>
 *     <rotation x="0" y="0" z="0"/>
 *     <placements>
 *       <lines sector="1" mirrorx="true" mirrory="true"/>
 *         <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"/>
Chao Peng's avatar
Chao Peng committed
 *         <line x="10.25*mm" y="10.25*mm" axis="x" begin="8" nmods="16"/>
 *         <line x="10.25*mm" y="30.75*mm" axis="y" begin="8" nmods="16"/>
 *         <line x="10.25*mm" y="51.25*mm" axis="z" begin="8" nmods="16"/>
 *       </individuals>
 *     </placements>
 *   </detector>
static void add_individuals(Detector& desc, Assembly &env, xml::Collection_t &plm, SensitiveDetector &sens, int id);
static void add_array(Detector& desc, Assembly &env, xml::Collection_t &plm, SensitiveDetector &sens, int id);
static void add_disk(Detector& desc, Assembly &env, xml::Collection_t &plm, SensitiveDetector &sens, int id);
static void add_lines(Detector& desc, Assembly &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");
    // envelope
    Assembly assembly(detName);

    // module placement
    xml::Component plm = detElem.child(_Unicode(placements));
    int sector = 1;
    for (xml::Collection_t mod(plm, _Unicode(individuals)); mod; ++mod) {
        add_individuals(desc, assembly, mod, sens, sector++);
    for (xml::Collection_t arr(plm, _Unicode(array)); arr; ++arr) {
        add_array(desc, assembly, arr, sens, sector++);
    }
    for (xml::Collection_t disk(plm, _Unicode(disk)); disk; ++disk) {
        add_disk(desc, assembly, disk, sens, sector++);
    }
    for (xml::Collection_t lines(plm, _Unicode(lines)); lines; ++lines) {
        add_lines(desc, assembly, lines, 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(assembly, tr);
    envPV.addPhysVolID("system", detID);
    det.setPlacement(envPV);
    return det;
}

// helper function to build module with or w/o wrapper
std::tuple<Volume, Position> build_module(Detector &desc, xml::Collection_t &plm, SensitiveDetector &sens)
{
    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));
    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 std::make_tuple(modVol, Position{sx, sy, sz});
    // build wrapper
    } else {
        auto wrp = plm.child(_Unicode(wrapper));
        auto thickness = wrp.attr<double>(_Unicode(thickness));
        if (thickness < 1e-12*mm) {
            return std::make_tuple(modVol, Position{sx, sy, sz});
        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))));
        return std::make_tuple(wrpVol, Position{sx + thickness, sy + thickness, sz});
// place modules, id must be provided
static void add_individuals(Detector& desc, Assembly &env, xml::Collection_t &plm, SensitiveDetector &sens, int sid)
{
    auto [modVol, modSize] = build_module(desc, plm, sens);
    int sector_id = dd4hep::getAttrOrDefault<int>(plm, _Unicode(sector), sid);

    for (xml::Collection_t pl(plm, _Unicode(placement)); pl; ++pl) {
        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.));
        auto mid = pl.attr<int>(_Unicode(id));
        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 array of modules
static void add_array(Detector& desc, Assembly &env, xml::Collection_t &plm, SensitiveDetector &sens, int sid)
    auto [modVol, modSize] = build_module(desc, plm, sens);
    int sector_id = dd4hep::getAttrOrDefault<int>(plm, _Unicode(sector), sid);
    int id_begin = dd4hep::getAttrOrDefault<int>(plm, _Unicode(id_begin), 1);
    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 = RotationZYX(rot.z(), rot.y(), rot.x())
                           * Translation3D(pos.x() + px, pos.y() + py, pos.z());
            auto modPV = env.placeVolume(modVol, tr);
            modPV.addPhysVolID("sector", sector_id).addPhysVolID("module", i*ncol + j + id_begin);
static void add_disk(Detector& desc, Assembly &env, xml::Collection_t &plm, SensitiveDetector &sens, int sid)
    auto [modVol, modSize] = build_module(desc, plm, sens);
    int sector_id = dd4hep::getAttrOrDefault<int>(plm, _Unicode(sector), sid);
    int id_begin = dd4hep::getAttrOrDefault<int>(plm, _Unicode(id_begin), 1);
    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 = athena::geo::fillRectangles({0., 0.}, modSize.x(), modSize.y(), rmin, rmax, phimin, phimax);
    // placement to mother
    auto pos = get_xml_xyz(plm, _Unicode(position));
    auto rot = get_xml_xyz(plm, _Unicode(rotation));
    for (auto &p : points) {
        Transform3D tr = RotationZYX(rot.z(), rot.y(), rot.x())
                       * Translation3D(pos.x() + p.x(), pos.y() + p.y(), pos.z());
        auto modPV = env.placeVolume(modVol, tr);
        modPV.addPhysVolID("sector", sector_id).addPhysVolID("module", id_begin + mid++);
    }
}

// place lines of modules (anchor point is the 0th module of this line)
static void add_lines(Detector& desc, Assembly &env, xml::Collection_t &plm, SensitiveDetector &sens, int sid)
{
    auto [modVol, modSize] = build_module(desc, plm, sens);
    int sector_id = dd4hep::getAttrOrDefault<int>(plm, _Unicode(sector), sid);
    int id_begin = dd4hep::getAttrOrDefault<int>(plm, _Unicode(id_begin), 1);
    bool mirrorx = dd4hep::getAttrOrDefault<bool>(plm, _Unicode(mirrorx), false);
    bool mirrory = dd4hep::getAttrOrDefault<bool>(plm, _Unicode(mirrory), false);
Chao Peng's avatar
Chao Peng committed
    bool mirrorz = dd4hep::getAttrOrDefault<bool>(plm, _Unicode(mirrorz), false);

    // line placement
    int mid = 1;
    for (xml::Collection_t pl(plm, _Unicode(line)); pl; ++pl) {
        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.));
Chao Peng's avatar
Chao Peng committed
        // determine axis
        std::string axis = dd4hep::getAttrOrDefault(pl, _Unicode(axis), "x");
        std::transform(axis.begin(), axis.end(), axis.begin(), [](char c) { return std::tolower(c); });
        if ((axis != "x") && (axis != "y") && (axis != "z")) {
            std::cerr << "HomogeneousCalorimeter Error: cannot determine axis of line " << axis
                      << ", abort placement of this line." << std::endl;
            continue;
        }

        int begin = dd4hep::getAttrOrDefault<int>(pl, _Unicode(begin), 0);
        int nmods = pl.attr<int>(_Unicode(nmods));

Chao Peng's avatar
Chao Peng committed
        std::vector<Position> trans;
Chao Peng's avatar
Chao Peng committed
            Position tran{ (axis == "x") ? pos.x() + (begin + i)*modSize.x() : pos.x(),
                           (axis == "y") ? pos.y() + (begin + i)*modSize.y() : pos.y(),
                           (axis == "z") ? pos.z() + (begin + i)*modSize.z() : pos.z() };
            trans.push_back(tran);
        }

        // mirror placement
        if (mirrorx) {
            int curr_size = trans.size();
            for (size_t i = 0; i < curr_size; ++i) {
                trans.push_back(Position{-trans[i].x(), trans[i].y(), trans[i].z()});
Chao Peng's avatar
Chao Peng committed
        }
        if (mirrory) {
            int curr_size = trans.size();
            for (size_t i = 0; i < curr_size; ++i) {
                trans.push_back(Position{trans[i].x(), -trans[i].y(), trans[i].z()});
Chao Peng's avatar
Chao Peng committed
        }
        if (mirrorz) {
            int curr_size = trans.size();
            for (size_t i = 0; i < curr_size; ++i) {
                trans.push_back(Position{trans[i].x(), trans[i].y(), -trans[i].z()});
Chao Peng's avatar
Chao Peng committed
        // place volume
        for (auto &p : trans) {
            Transform3D tr = RotationZYX(rot.z(), rot.y(), rot.x())
Chao Peng's avatar
Chao Peng committed
                           * Translation3D(p.x(), p.y(), p.z());
            auto modPV = env.placeVolume(modVol, tr);
            modPV.addPhysVolID("sector", sector_id).addPhysVolID("module", id_begin + mid++);
        }
    }
}
//@}
DECLARE_DETELEMENT(HomogeneousCalorimeter, create_detector)