Skip to content
Snippets Groups Projects
ShashlikCalorimeter.cpp 8.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • //==========================================================================
    //  Implementation for shashlik calorimeter modules
    //  it supports disk placements with (rmin, rmax), and (phimin, phimax)
    //--------------------------------------------------------------------------
    //  Author: Chao Peng (ANL)
    //  Date: 06/22/2021
    //==========================================================================
    
    #include "GeometryHelpers.h"
    #include "DD4hep/DetFactoryHelper.h"
    #include <XML/Layering.h>
    #include <XML/Helper.h>
    #include <iostream>
    #include <algorithm>
    #include <tuple>
    #include <math.h>
    
    using namespace dd4hep;
    
    static void add_disk_shashlik(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;
    }
    
    static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDetector sens)
    {
        static const std::string func = "ShashlikCalorimeter";
        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(disk)); mod; ++mod) {
            add_disk_shashlik(desc, assembly, mod, 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, int, double, double> build_shashlik(Detector &desc, xml::Collection_t &plm, SensitiveDetector &sens)
    {
        auto mod = plm.child(_Unicode(module));
        // a modular volume
        std::string shape = dd4hep::getAttrOrDefault(mod, _Unicode(shape), "square");
        std::transform(shape.begin(), shape.end(), shape.begin(), [](char c) { return std::tolower(c); });
        int nsides = 4;
        if (shape == "hexagon") {
            nsides = 6;
        } else if (shape != "square") {
            std::cerr << "ShashlikCalorimeter Error: Unsupported shape of module " << shape
                      << ". Please choose from (square, hexagon). Proceed with square shape." << std::endl;
        }
        double slen = mod.attr<double>(_Unicode(side_length));
        double rmax = slen/2./std::sin(M_PI/nsides);
        Layering layering(mod);
        auto len = layering.totalThickness();
    
        // wrapper info
        PolyhedraRegular mpoly(nsides, 0., rmax, len);
        Volume mvol("shashlik_module_vol", mpoly, desc.air());
        mvol.setVisAttributes(desc.visAttributes(dd4hep::getAttrOrDefault(mod, _Unicode(vis), "GreenVis")));
    
        double gap = 0.;
        Volume wvol("shashlik_wrapper_vol");
        if (plm.hasChild(_Unicode(wrapper))) {
            auto wrap = plm.child(_Unicode(wrapper));
            gap = wrap.attr<double>(_Unicode(thickness));
            if (gap > 1e-6*mm) {
                wvol.setSolid(PolyhedraRegular(nsides, 0., rmax + gap, len));
                wvol.setMaterial(desc.material(wrap.attr<std::string>(_Unicode(material))));
                wvol.setVisAttributes(desc.visAttributes(dd4hep::getAttrOrDefault(wrap, _Unicode(vis), "WhiteVis")));
                wvol.placeVolume(mvol, Position{0., 0., 0.});
            }
        }
    
        // layer start point
        double lz  = -len/2.;
        int lnum = 1;
        // Loop over the sets of layer elements in the detector.
        for (xml_coll_t li(mod, _U(layer)); li; ++li) {
            int repeat = li.attr<int>(_Unicode(repeat));
            // Loop over number of repeats for this layer.
            for (int j = 0; j < repeat; j++) {
                std::string lname = Form("layer%d", lnum);
                double lthick = layering.layer(lnum - 1)->thickness();  // Layer's thickness.
                PolyhedraRegular lpoly(nsides, 0., rmax, lthick);
                Volume lvol(lname, lpoly, desc.air());
    
                // Loop over the sublayers or slices for this layer.
                int snum = 1;
                double sz = -lthick/2.;
                for (xml_coll_t si(li, _U(slice)); si; ++si) {
                    std::string sname = Form("slice%d", snum);
                    double sthick = si.attr<double>(_Unicode(thickness));
                    PolyhedraRegular spoly(nsides, 0., rmax, sthick);
                    Volume svol(sname, spoly, desc.material(si.attr<std::string>(_Unicode(material))));
    
                    std::string issens = dd4hep::getAttrOrDefault(si, _Unicode(sensitive), "no");
                    std::transform(issens.begin(), issens.end(), issens.begin(), [](char c) { return std::tolower(c); });
                    if ((issens == "yes") || (issens == "y") || (issens == "true")) {
                        svol.setSensitiveDetector(sens);
                    }
                    svol.setAttributes(desc,
                            dd4hep::getAttrOrDefault(si, _Unicode(region), ""),
                            dd4hep::getAttrOrDefault(si, _Unicode(limits), ""),
                            dd4hep::getAttrOrDefault(si, _Unicode(vis), "InvisibleNoDaughters"));
    
                    // Slice placement.
                    auto slicePV = lvol.placeVolume(svol, Position(0, 0, sz + sthick/2.));
                    slicePV.addPhysVolID("slice", snum++);
                    // Increment Z position of slice.
                    sz += sthick;
                }
    
                // Set region, limitset, and vis of layer.
                lvol.setAttributes(desc,
                        dd4hep::getAttrOrDefault(li, _Unicode(region), ""),
                        dd4hep::getAttrOrDefault(li, _Unicode(limits), ""),
                        dd4hep::getAttrOrDefault(li, _Unicode(vis), "InvisibleNoDaughters"));
    
                auto layerPV = mvol.placeVolume(lvol, Position(0, 0, lz + lthick/2));
                layerPV.addPhysVolID("layer", lnum++);
                // Increment to next layer Z position.
                lz += lthick;
            }
        }
    
        if (gap > 1e-6*mm) {
            return std::make_tuple(wvol, nsides, 2.*std::sin(M_PI/nsides)*(rmax + gap), len);
        } else {
            return std::make_tuple(mvol, nsides, slen, len);
        }
    }
    
    // place disk of modules
    static void add_disk_shashlik(Detector& desc, Assembly &env, xml::Collection_t &plm, SensitiveDetector &sens, int sid)
    {
        auto [mvol, nsides, sidelen, len]  = build_shashlik(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 = (nsides == 6) ? athena::geo::fillHexagons({0., 0.}, sidelen, rmin, rmax, phimin, phimax)
                                    : athena::geo::fillSquares({0., 0.}, sidelen*1.414, 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 = 0;
        for (auto &p : points) {
            Transform3D tr = RotationZYX(rot.z(), rot.y(), rot.x())
                           * Translation3D(pos.x() + p.x(), pos.y() + p.y(), pos.z() + len/2.)
                           * RotationZ((nsides == 4) ? 45*degree : 0);
            auto modPV = env.placeVolume(mvol, tr);
            modPV.addPhysVolID("sector", sector_id).addPhysVolID("module", id_begin + mid++);
        }
    }
    
    
    DECLARE_DETELEMENT(ShashlikCalorimeter, create_detector)