Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-22 09:38:59

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022 - 2025 Whitney Armstrong, Wouter Deconinck, Sylvester Joosten, Dmitry Romanov, Yann Bedfer
0003 
0004 /*
0005   Digitization specific to MPGDs.
0006   - What's special in MPGDs is their 2D-strip readout: i.e. simultaneous
0007    registering along two sets of coordinate strips.
0008   - The "process" method involves a combination of segmentation, simulation and
0009    digitization.
0010   - The segmentation done when producing "sim_hits", and stored as a pixel
0011    cellID, is overwritten by strip cellIDs, via "dd4hep::MultiSegmentation".
0012   - The simulation will eventually involve simulating the amplitude and timing
0013    correlation of the two coordinates, and the spreading of the charge on
0014    adjacent strips producing multi-hit clusters.
0015     The preliminary version is rudimentary: single-hit clusters, with identical
0016    timing and uncorrelated amplitudes.
0017   - The digitization follows the standard steps, only that it needs to be
0018    convoluted with the simulation.
0019   NOTA BENE: The file could be simplified, see issue #1702.
0020  */
0021 
0022 
0023 #include "MPGDTrackerDigi.h"
0024 
0025 #include <DD4hep/Alignments.h>
0026 #include <DD4hep/DetElement.h>
0027 #include <DD4hep/Handle.h>
0028 #include <DD4hep/IDDescriptor.h>
0029 #include <DD4hep/Objects.h>
0030 #include <DD4hep/Readout.h>
0031 #include <DD4hep/VolumeManager.h>
0032 #include <DD4hep/config.h>
0033 #include <DD4hep/detail/SegmentationsInterna.h>
0034 #include <DDSegmentation/BitFieldCoder.h>
0035 #include <Evaluator/DD4hepUnits.h>
0036 #include <JANA/JException.h>
0037 #include <Math/GenVector/Cartesian3D.h>
0038 #include <Math/GenVector/DisplacementVector3D.h>
0039 #include <Parsers/Primitives.h>
0040 // Access "algorithms:GeoSvc"
0041 #include <algorithms/geo.h>
0042 #include <algorithms/logger.h>
0043 #include <edm4hep/EDM4hepVersion.h>
0044 #include <edm4eic/EDM4eicVersion.h>
0045 #include <edm4hep/MCParticleCollection.h>
0046 #include <edm4hep/Vector3d.h>
0047 #include <edm4hep/Vector3f.h>
0048 #include <fmt/core.h>
0049 #include <algorithm>
0050 #include <cmath>
0051 #include <cstdint>
0052 #include <gsl/pointers>
0053 #include <initializer_list>
0054 #include <unordered_map>
0055 #include <utility>
0056 #include <vector>
0057 
0058 #include "algorithms/digi/MPGDTrackerDigiConfig.h"
0059 
0060 using namespace dd4hep;
0061 
0062 namespace eicrecon {
0063 
0064 void MPGDTrackerDigi::init() {
0065     // Create random gauss function
0066     m_gauss = [&](){
0067         return m_random.Gaus(0, m_cfg.timeResolution);
0068         //return m_rng.gaussian<double>(0., m_cfg.timeResolution);
0069     };
0070 
0071     // Access id decoder
0072     m_detector = algorithms::GeoSvc::instance().detector();
0073     const dd4hep::BitFieldCoder* m_id_dec;
0074     if (m_cfg.readout.empty()) {
0075         throw JException("Readout is empty");
0076     }
0077     try {
0078         m_seg =    m_detector->readout(m_cfg.readout).segmentation();
0079         m_id_dec = m_detector->readout(m_cfg.readout).idSpec().decoder();
0080     } catch (...) {
0081         critical("Failed to load ID decoder for \"{}\" readout.", m_cfg.readout);
0082         throw JException("Failed to load ID decoder");
0083     }
0084     // Method "process" relies on a strict assumption on the IDDescriptor:
0085     // - Must have a "strip" field.
0086     // - That "strip" field includes bits 30|31.
0087     // Let's check.
0088     if (m_id_dec->get(((CellID)0x3)<<30,"strip") != 0x3) {
0089         critical("Missing or invalid \"strip\" field in IDDescriptor for \"{}\" readout.",
0090                  m_cfg.readout);
0091         throw JException("Invalid IDDescriptor");
0092     }
0093     debug("Find valid \"strip\" field in IDDescriptor for \"{}\" readout.",
0094           m_cfg.readout);
0095 }
0096 
0097 
0098 void MPGDTrackerDigi::process(
0099         const MPGDTrackerDigi::Input& input,
0100         const MPGDTrackerDigi::Output& output) const {
0101 
0102     // ********** SIMULATE THE 2D-strip READOUT of MPGDs.
0103     // - Overwrite and extend segmentation stored in "sim_hit", which is anyway
0104     //  expected to be along a single coordinate (this happens to allow one to
0105     //  reconstruct data w/ a segmentation differing from that used when
0106     //  generating the data).
0107     // - New segmentation is along two coordinates, described by two cellID's
0108     //  with each a distinctive "strip" field.
0109     //   N.B.: Assumptions on the IDDescriptor: the "strip" specification
0110     //  is fixed = cellID>>32&0x3.
0111     // - The simulation is simplistic: single-hit cluster per coordinate.
0112 
0113     const auto [sim_hits] = input;
0114     auto [raw_hits,associations] = output;
0115 
0116     // A map of unique cellIDs with temporary structure RawHit
0117     std::unordered_map<std::uint64_t, edm4eic::MutableRawTrackerHit> cell_hit_map;
0118     // Prepare for strip segmentation
0119     const Position dummy(0,0,0);
0120     const VolumeManager& volman = m_detector->volumeManager();
0121 
0122     using CellIDs = std::pair<CellID,CellID>;
0123     using Sim2IDs = std::vector<CellIDs>; Sim2IDs sim2IDs;
0124     for (const edm4hep::SimTrackerHit& sim_hit : *sim_hits) {
0125 
0126         // ***** TIME SMEARING
0127         // - Simplistic treatment.
0128         // - A more realistic one would have to distinguish a smearing common to
0129         //  both coordinates of the 2D-strip readout (due to the drifting of the
0130         //  leading primary electrons) from other smearing effects, specific to
0131         //  each coordinate.
0132         double time_smearing = m_gauss();
0133         double result_time = sim_hit.getTime() + time_smearing;
0134         auto hit_time_stamp = (std::int32_t) (result_time * 1e3);
0135 
0136         // ***** SEGMENTATION
0137         // - The two cellID's are encoded via a "dd4hep::MultiSegmentation"
0138         //  discriminating on the strip field, w/ "strip" setting of 0x1 (
0139         //  called 'p') and 0x2 (called 'n').
0140         // - They are evaluated based on "sim_hit" Cartesian coordinates
0141         //  positions
0142         //   Given that all the segmentation classes foreseen for MPGDs (
0143         //  "CartesianGrid.." for Outer and EndCaps, "CylindricalGridPhiZ" for
0144         //  "CyMBaL") disregard the _global_ position argument to
0145         //  "dd4hep::Segmentation::cellID", we need the _local_ position and
0146         //  only that.
0147         const edm4hep::Vector3d &pos = sim_hit.getPosition();
0148         using dd4hep::mm;
0149         Position gpos(pos.x*mm,pos.y*mm,pos.z*mm);
0150         CellID vID = // Note: Only the bits corresponding to the volumeID will
0151           // be used. The rest, encoding the segmentation stored in "sim_hit",
0152           // being disregared.
0153           sim_hit.getCellID();
0154         DetElement local = volman.lookupDetElement(vID);
0155         const auto lpos = local.nominal().worldToLocal(gpos);
0156         // p "strip"
0157         CellID stripBitp = ((CellID)0x1)<<30, vIDp = vID|stripBitp;
0158         CellID cIDp = m_seg->cellID(lpos,dummy,vIDp);
0159         // n "strip"
0160         CellID stripBitn = ((CellID)0x2)<<30, vIDn = vID|stripBitn;
0161         CellID cIDn = m_seg->cellID(lpos,dummy,vIDn);
0162 
0163         sim2IDs.push_back({cIDp,cIDn}); // Remember cellIDs.
0164         // ***** DEBUGGING INFO
0165         if(level() >= algorithms::LogLevel::kDebug) {
0166             CellID hIDp = cIDp>>32, sIDp = cIDp>>30&0x3;
0167             debug("--------------------");
0168             debug("Hit cellIDp  = 0x{:08x}, 0x{:08x} 0x{:02x}", hIDp, vIDp, sIDp);
0169             CellID hIDn = cIDn>>32, sIDn = cIDn>>30&0x3;
0170             debug("Hit cellIDn  = 0x{:08x}, 0x{:08x} 0x{:02x}", hIDn, vIDn, sIDn);
0171             debug("   position  = ({:.2f}, {:.2f}, {:.2f})", sim_hit.getPosition().x, sim_hit.getPosition().y, sim_hit.getPosition().z);
0172             debug("   xy_radius = {:.2f}", std::hypot(sim_hit.getPosition().x, sim_hit.getPosition().y));
0173             debug("   momentum  = ({:.2f}, {:.2f}, {:.2f})", sim_hit.getMomentum().x, sim_hit.getMomentum().y, sim_hit.getMomentum().z);
0174             debug("   edep = {:.2f}", sim_hit.getEDep());
0175             debug("   time = {:.4f}[ns]", sim_hit.getTime());
0176 #if EDM4HEP_BUILD_VERSION >= EDM4HEP_VERSION(0, 99, 0)
0177             debug("   particle time = {}[ns]", sim_hit.getParticle().getTime());
0178 #else
0179             debug("   particle time = {}[ns]", sim_hit.getMCParticle().getTime());
0180 #endif
0181             debug("   time smearing: {:.4f}, resulting time = {:.4f} [ns]", time_smearing, result_time);
0182             debug("   hit_time_stamp: {} [~ps]", hit_time_stamp);
0183         }
0184 
0185         // ***** APPLY THRESHOLD
0186         if (sim_hit.getEDep() < m_cfg.threshold) {
0187             debug("  edep is below threshold of {:.2f} [keV]", m_cfg.threshold / keV);
0188             continue;
0189         }
0190 
0191         // ***** HIT ACCUMULATION
0192         for (CellID cID : {cIDp,cIDn}) {
0193             if (cell_hit_map.count(cID) == 0) {
0194                 // This cell doesn't have hits
0195                 cell_hit_map[cID] = {
0196                     cID,
0197                     (std::int32_t) std::llround(sim_hit.getEDep() * 1e6),
0198                     hit_time_stamp  // ns->ps
0199                 };
0200             } else {
0201                 // There is previous values in the cell
0202                 auto& hit = cell_hit_map[cID];
0203                 debug("  Hit already exists in cell ID={}, prev. hit time: {}", cID, hit.getTimeStamp());
0204 
0205                 // keep earliest time for hit
0206                 auto time_stamp = hit.getTimeStamp();
0207                 hit.setTimeStamp(std::min(hit_time_stamp, hit.getTimeStamp()));
0208 
0209                 // sum deposited energy
0210                 auto charge = hit.getCharge();
0211                 hit.setCharge(charge + (std::int32_t) std::llround(sim_hit.getEDep() * 1e6));
0212             }
0213         }
0214     }
0215 
0216     // ***** raw_hit INSTANTIATION AND raw<-sim_hit's ASSOCIATION
0217     for (auto item : cell_hit_map) {
0218         raw_hits->push_back(item.second);
0219         Sim2IDs::const_iterator sim_it = sim2IDs.cbegin();
0220         for (const auto& sim_hit : *sim_hits) {
0221             CellIDs cIDs = *sim_it++;
0222             for (CellID cID : {cIDs.first,cIDs.second}) {
0223                 if (item.first == cID) {
0224                     // set association
0225                     auto hitassoc = associations->create();
0226                     hitassoc.setWeight(1.0);
0227                     hitassoc.setRawHit(item.second);
0228 #if EDM4EIC_VERSION_MAJOR >= 6
0229                     hitassoc.setSimHit(sim_hit);
0230 #else
0231                     hitassoc.addToSimHits(sim_hit);
0232 #endif
0233                 }
0234             }
0235         }
0236 
0237     }
0238 }
0239 
0240 } // namespace eicrecon