diff --git a/JugDigi/src/components/CalorimeterHitDigi.cpp b/JugDigi/src/components/CalorimeterHitDigi.cpp index f7ba9a1b9ea9e43f264140ee08cf548a30fa1b56..9ae76a5424dc334e96750a1ba042ea47832fa624 100644 --- a/JugDigi/src/components/CalorimeterHitDigi.cpp +++ b/JugDigi/src/components/CalorimeterHitDigi.cpp @@ -33,10 +33,6 @@ public: Gaudi::Property<std::vector<double>> u_eRes{this, "energyResolutions", {}}; // a/sqrt(E/GeV) + b + c/(E/GeV) Gaudi::Property<double> m_tRes{this, "timineResolution", 0.0*ns}; - // input units, should be fixed - Gaudi::Property<double> m_eUnit{this, "inputEnergyUnit", GeV}; - Gaudi::Property<double> m_tUnit{this, "inputTimeUnit", ns}; - // digitization settings Gaudi::Property<int> m_capADC{this, "capacityADC", 8096}; Gaudi::Property<double> m_dyRangeADC{this, "dynamicRangeADC", 100*MeV}; @@ -48,7 +44,8 @@ public: m_inputHitCollection{"inputHitCollection", Gaudi::DataHandle::Reader, this}; DataHandle<eic::RawCalorimeterHitCollection> m_outputHitCollection{"outputHitCollection", Gaudi::DataHandle::Writer, this}; - double res[3] = {0., 0., 0.}; + // unitless counterparts of inputs + double dyRangeADC, tRes, eRes[3] = {0., 0., 0.}; // ill-formed: using GaudiAlgorithm::GaudiAlgorithm; CalorimeterHitDigi(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) @@ -70,8 +67,13 @@ public: } // set energy resolution numbers for (size_t i = 0; i < u_eRes.size() && i < 3; ++i) { - res[i] = u_eRes[i]; + eRes[i] = u_eRes[i]; } + + // using juggler internal units (GeV, mm, radian, ns) + dyRangeADC = m_dyRangeADC.value()/GeV; + tRes = m_tRes.value()/ns; + return StatusCode::SUCCESS; } @@ -82,15 +84,16 @@ public: // Create output collections auto rawhits = m_outputHitCollection.createAndPut(); for (const auto& ahit : *simhits) { - double eres = std::sqrt(std::pow(m_normDist()*res[0] / sqrt(ahit.energyDeposit()*m_eUnit/GeV), 2) - + std::pow(m_normDist()*res[1], 2) - + std::pow(m_normDist()*res[2] / (ahit.energyDeposit()*m_eUnit/GeV), 2)); + // Note: juggler internal unit of energy is GeV + double eResRel = std::sqrt(std::pow(m_normDist()*eRes[0] / sqrt(ahit.energyDeposit()), 2) + + std::pow(m_normDist()*eRes[1], 2) + + std::pow(m_normDist()*eRes[2] / (ahit.energyDeposit()), 2)); double ped = m_pedMeanADC + m_normDist()*m_pedSigmaADC; - long long adc = std::llround(ped + ahit.energyDeposit()*(1. + eres) * m_eUnit/m_dyRangeADC*m_capADC); + long long adc = std::llround(ped + ahit.energyDeposit()*(1. + eResRel)/dyRangeADC*m_capADC); eic::RawCalorimeterHit rawhit( (long long) ahit.cellID(), - (adc > m_capADC ? m_capADC.value() : adc), - (double) ahit.truth().time*m_tUnit/ns + m_normDist()*m_tRes/ns + (adc > m_capADC.value() ? m_capADC.value() : adc), + (double) ahit.truth().time + m_normDist()*tRes ); rawhits->push_back(rawhit); } diff --git a/JugReco/src/components/CalorimeterHitReco.cpp b/JugReco/src/components/CalorimeterHitReco.cpp index 2bea3613338027097519e684e11c7592b0100edc..c5b849d8d12d8f65deb85a716d8736dfcb628b68 100644 --- a/JugReco/src/components/CalorimeterHitReco.cpp +++ b/JugReco/src/components/CalorimeterHitReco.cpp @@ -44,7 +44,9 @@ namespace Jug::Reco { // zero suppression values Gaudi::Property<double> m_thresholdADC{this, "thresholdFactor", 3.0}; - Gaudi::Property<double> m_minEdep{this, "minimumHitEdep", 0.}; + + // unitless counterparts of the input parameters + double dyRangeADC; DataHandle<eic::RawCalorimeterHitCollection> m_inputHitCollection{ "inputHitCollection", Gaudi::DataHandle::Reader, this}; @@ -87,6 +89,9 @@ namespace Jug::Reco { return StatusCode::FAILURE; } + // unitless conversion + dyRangeADC = m_dyRangeADC.value()/GeV; + // do not get the layer/sector ID if no readout class provided if (m_readout.value().empty()) { return StatusCode::SUCCESS; @@ -97,9 +102,11 @@ namespace Jug::Reco { id_dec = id_spec.decoder(); if (m_sectorField.value().size()) { sector_idx = id_dec->index(m_sectorField); + info() << "Find sector field " << m_sectorField.value() << ", index = " << sector_idx << endmsg; } if (m_layerField.value().size()) { layer_idx = id_dec->index(m_layerField); + info() << "Find layer field " << m_layerField.value() << ", index = " << sector_idx << endmsg; } } catch (...) { error() << "Failed to load ID decoder for " << m_readout << endmsg; @@ -117,7 +124,7 @@ namespace Jug::Reco { << m_localDetElement.value() << endmsg; return StatusCode::FAILURE; } - // or get from fields + // or get from fields } else { std::vector<std::pair<std::string, int>> fields; for (auto& f : u_localDetFields.value()) { @@ -151,20 +158,16 @@ namespace Jug::Reco { } // convert ADC -> energy - float energy = (rh.amplitude() - m_pedMeanADC) / (float)m_capADC * m_dyRangeADC; - if (energy < m_minEdep) { - continue; - } + float energy = (rh.amplitude() - m_pedMeanADC) / static_cast<float>(m_capADC.value()) * dyRangeADC; float time = rh.timeStamp(); // ns auto id = rh.cellID(); - int lid = ((id_dec != nullptr) & m_layerField.value().size()) + int lid = ((id_dec != nullptr) && m_layerField.value().size()) ? static_cast<int>(id_dec->get(id, layer_idx)) : -1; - int sid = ((id_dec != nullptr) & m_sectorField.value().size()) + int sid = ((id_dec != nullptr) && m_sectorField.value().size()) ? static_cast<int>(id_dec->get(id, sector_idx)) : -1; - // global positions auto gpos = m_geoSvc->cellIDPositionConverter()->position(id); // local positions @@ -173,9 +176,8 @@ namespace Jug::Reco { local = volman.lookupDetElement(id & local_mask).nominal(); } auto pos = local.worldToLocal(dd4hep::Position(gpos.x(), gpos.y(), gpos.z())); - // auto pos = - // m_geoSvc->cellIDPositionConverter()->findContext(id)->volumePlacement().position(); cell - // dimension + // auto pos = m_geoSvc->cellIDPositionConverter()->findContext(id)->volumePlacement().position(); + // cell dimension auto cdim = m_geoSvc->cellIDPositionConverter()->cellDimensions(id); double dim[3] = {0., 0., 0.}; for (size_t i = 0; i < cdim.size() && i < 3; ++i) { diff --git a/JugReco/src/components/CalorimeterHitsMerger.cpp b/JugReco/src/components/CalorimeterHitsMerger.cpp index 0d65598c88f9393f94639c843137e1e20adf3e52..f6340b40d6c7a2559fa0303be313a89387e22e02 100644 --- a/JugReco/src/components/CalorimeterHitsMerger.cpp +++ b/JugReco/src/components/CalorimeterHitsMerger.cpp @@ -4,8 +4,9 @@ * * Author: Chao Peng (ANL), 03/31/2021 */ -#include <algorithm> +#include <tuple> #include <bitset> +#include <algorithm> #include <unordered_map> #include "Gaudi/Property.h" @@ -102,50 +103,60 @@ namespace Jug::Reco { StatusCode execute() override { // input collections - const auto& hits = *m_inputHitCollection.get(); + const auto& inputs = *m_inputHitCollection.get(); // Create output collections - auto& mhits = *m_outputHitCollection.createAndPut(); - - // dd4hep decoders - auto poscon = m_geoSvc->cellIDPositionConverter(); - auto volman = m_geoSvc->detector()->volumeManager(); + auto& outputs = *m_outputHitCollection.createAndPut(); - // sum energies that has the same id - std::unordered_map<long long, size_t> merge_map; - for (const auto& h : hits) { + // find the hits that belong to the same group (for merging) + std::unordered_map<long long, std::vector<eic::ConstCalorimeterHit>> merge_map; + for (const auto& h : inputs) { int64_t id = h.cellID() & id_mask; // use the reference field position - int64_t ref_id = id | ref_mask; - // debug() << h.cellID() << " - " << std::bitset<64>(h.cellID()) << endmsg; auto it = merge_map.find(id); - debug() << fmt::format("{:#064b} - {:#064b}, ref: {:#064b}", h.cellID(), id, ref_id) - << endmsg; if (it == merge_map.end()) { - merge_map[id] = mhits.size(); - // global positions - auto gpos = poscon->position(ref_id); - // local positions - auto alignment = volman.lookupDetElement(ref_id).nominal(); - // debug() << volman.lookupDetElement(ref_id).path() << ", " << - // volman.lookupDetector(ref_id).path() << endmsg; - auto pos = alignment.worldToLocal(dd4hep::Position(gpos.x(), gpos.y(), gpos.z())); - - mhits.push_back({ref_id, - h.clusterID(), - h.layerID(), - h.sectorID(), - h.energy(), - h.time(), - {gpos.x() / dd4hep::mm, gpos.y() / dd4hep::mm, gpos.z() / dd4hep::mm}, - {pos.x() / dd4hep::mm, pos.y() / dd4hep::mm, pos.z() / dd4hep::mm}, - h.dimension(), - h.type()}); + merge_map[id] = {h}; } else { - mhits[it->second].energy(mhits[it->second].energy() + h.energy()); + it->second.push_back(h); + } + } + + // reconstruct info for merged hits + // dd4hep decoders + auto poscon = m_geoSvc->cellIDPositionConverter(); + auto volman = m_geoSvc->detector()->volumeManager(); + + for (auto &[id, hits] : merge_map) { + // reference fields id + int64_t ref_id = id | ref_mask; + // global positions + auto gpos = poscon->position(ref_id); + // local positions + auto alignment = volman.lookupDetElement(ref_id).nominal(); + auto pos = alignment.worldToLocal(dd4hep::Position(gpos.x(), gpos.y(), gpos.z())); + // debug() << volman.lookupDetElement(ref_id).path() << ", " + // << volman.lookupDetector(ref_id).path() << endmsg; + // sum energy + float energy = 0.; + for (auto &hit : hits) { + energy += hit.energy(); + // debug() << fmt::format("{:#064b} - {:#064b}, ref: {:#064b}", hit.cellID(), id, ref_id) + // << endmsg; } + const auto &href = hits.front(); + outputs.push_back(eic::CalorimeterHit{ + ref_id, + href.clusterID(), + href.layerID(), + href.sectorID(), + energy, + href.time(), + {gpos.x() / dd4hep::mm, gpos.y() / dd4hep::mm, gpos.z() / dd4hep::mm}, + {pos.x() / dd4hep::mm, pos.y() / dd4hep::mm, pos.z() / dd4hep::mm}, + href.dimension(), + href.type()}); } - debug() << "Size before = " << hits.size() << ", after = " << mhits.size() << endmsg; + debug() << "Size before = " << inputs.size() << ", after = " << outputs.size() << endmsg; return StatusCode::SUCCESS; } diff --git a/JugReco/src/components/CalorimeterIslandCluster.cpp b/JugReco/src/components/CalorimeterIslandCluster.cpp index a6dc5b679455fcced5132d527583039a42f574f1..48ab9d2cd60423b664578095435a181830e6eb57 100644 --- a/JugReco/src/components/CalorimeterIslandCluster.cpp +++ b/JugReco/src/components/CalorimeterIslandCluster.cpp @@ -1,8 +1,8 @@ /* * Island Clustering Algorithm for Calorimeter Blocks * 1. group all the adjacent modules - * 2. split the groups between their local maxima with the energy deposit above - * <minClusterCenterEdep> + * 2. split the groups between their local maxima with the energy deposit above <minClusterCenterEdep> + * Output hits collection with clusterID * * Author: Chao Peng (ANL), 09/27/2020 * References: @@ -11,6 +11,7 @@ */ #include <algorithm> +#include "fmt/format.h" #include "Gaudi/Property.h" #include "GaudiAlg/GaudiAlgorithm.h" #include "GaudiAlg/GaudiTool.h" @@ -37,14 +38,18 @@ namespace Jug::Reco { class CalorimeterIslandCluster : public GaudiAlgorithm { public: - Gaudi::Property<bool> m_splitCluster{this, "splitCluster", true}; - Gaudi::Property<double> m_groupRange{this, "groupRange", 1.8}; - Gaudi::Property<std::vector<double>> u_groupRanges{this, "groupRanges", {}}; - Gaudi::Property<double> m_minClusterCenterEdep{this, "minClusterCenterEdep", 50.0 * MeV}; - DataHandle<eic::CalorimeterHitCollection> m_inputHitCollection{"inputHitCollection", - Gaudi::DataHandle::Reader, this}; - DataHandle<eic::CalorimeterHitCollection> m_splitHitCollection{"outputHitCollection", - Gaudi::DataHandle::Writer, this}; + Gaudi::Property<bool> m_splitCluster{this, "splitCluster", true}; + Gaudi::Property<double> m_dimScaledDist{this, "dimScaledDist", 1.8}; + Gaudi::Property<std::vector<double>> u_localDistXY{this, "localDistXY", {}}; + Gaudi::Property<double> m_minClusterHitEdep{this, "minClusterHitEdep", 0.}; + Gaudi::Property<double> m_minClusterCenterEdep{this, "minClusterCenterEdep", 50.0 * MeV}; + DataHandle<eic::CalorimeterHitCollection> m_inputHitCollection{"inputHitCollection", + Gaudi::DataHandle::Reader, this}; + DataHandle<eic::CalorimeterHitCollection> m_splitHitCollection{"outputHitCollection", + Gaudi::DataHandle::Writer, this}; + + // unitless counterparts of the input parameters + double minClusterHitEdep, minClusterCenterEdep, localDistXY[2] = {0., 0.}; CalorimeterIslandCluster(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) @@ -58,6 +63,21 @@ namespace Jug::Reco { if (GaudiAlgorithm::initialize().isFailure()) { return StatusCode::FAILURE; } + + // unitless conversion, keep consistency with juggler internal units (GeV, mm, ns, rad) + minClusterHitEdep = m_minClusterHitEdep.value() / GeV; + minClusterCenterEdep = m_minClusterCenterEdep.value() / GeV; + // sanity check + if (u_localDistXY.size() == 2) { + localDistXY[0] = u_localDistXY.value()[0] / mm; + localDistXY[1] = u_localDistXY.value()[1] / mm; + info() << fmt::format("Clustering using distance [x, y] <= [{:.4f} mm, {:.4f} mm]", + localDistXY[0], localDistXY[1]) << endmsg; + } else if (u_localDistXY.size() > 0) { + warning() << fmt::format("Expect two values (x, y) from localDistXY, got {}. " + "Using dimScaledDist = {} instead", + u_localDistXY.size(), m_dimScaledDist.value()) << endmsg; + } return StatusCode::SUCCESS; } @@ -73,6 +93,11 @@ namespace Jug::Reco { std::vector<bool> visits(hits.size(), false); for (size_t i = 0; i < hits.size(); ++i) { + debug() << fmt::format("hit {:d}: energy = {:.4f} MeV, local = ({:.4f}, {:.4f}) mm, " + "global=({:.4f}, {:.4f}, {:.4f}) mm, layer = {:d}, sector = {:d}.", + i, hits[i].energy()*1000., hits[i].local_x(), hits[i].local_y(), + hits[i].x(), hits[i].y(), hits[i].z(), + hits[i].layerID(), hits[i].sectorID()) << endmsg; // already in a group if (visits[i]) { continue; @@ -81,14 +106,16 @@ namespace Jug::Reco { // create a new group, and group all the neighboring hits dfs_group(groups.back(), i, hits, visits); } - debug() << "we have " << groups.size() << " groups of hits" << endmsg; size_t clusterID = 0; for (auto& group : groups) { - auto maxima = find_maxima(group, !m_splitCluster); + if (group.empty()) { + continue; + } + auto maxima = find_maxima(group, !m_splitCluster.value()); split_group(group, maxima, clusterID, clustered_hits); debug() << "hits in a group: " << group.size() << ", " - << "local maxima: " << maxima.hits_size() << endmsg; + << "local maxima: " << maxima.size() << endmsg; debug() << "total number of clusters so far: " << clusterID << ", " << endmsg; } @@ -112,23 +139,23 @@ namespace Jug::Reco { { // in the same sector if (h1.sectorID() == h2.sectorID()) { - if (u_groupRanges.size() >= 2) { - return (std::abs(h1.local_x() - h2.local_x()) <= u_groupRanges[0]) && - (std::abs(h1.local_y() - h2.local_y()) <= u_groupRanges[1]); + // use absolute value + if (u_localDistXY.size() == 2) { + return (std::abs(h1.local_x() - h2.local_x()) <= localDistXY[0]) && + (std::abs(h1.local_y() - h2.local_y()) <= localDistXY[1]); + // use scaled value (module size as the scales) } else { return (std::abs(h1.local_x() - h2.local_x()) <= - m_groupRange * (h1.dim_x() + h2.dim_x()) / 2.) && + m_dimScaledDist * (h1.dim_x() + h2.dim_x()) / 2.) && (std::abs(h1.local_y() - h2.local_y()) <= - m_groupRange * (h1.dim_y() + h2.dim_y()) / 2.); + m_dimScaledDist * (h1.dim_y() + h2.dim_y()) / 2.); } - // different sector + // different sector, local coordinates do not work, using global coordinates } else { double range = - (u_groupRanges.size() >= 2) - ? std::sqrt(std::accumulate(u_groupRanges.begin(), u_groupRanges.end(), 0., - [](double s, double x) { return s + x * x; })) - : m_groupRange * - dist2d((h1.dim_x() + h2.dim_x()) / 2., (h1.dim_y() + h2.dim_y()) / 2.); + (u_localDistXY.size() == 2) + ? dist2d(localDistXY[0], localDistXY[1]) + : m_dimScaledDist * dist2d((h1.dim_x() + h2.dim_x()) / 2., (h1.dim_y() + h2.dim_y()) / 2.); // sector may have rotation (barrel), so z is included return dist3d(h1.x() - h2.x(), h1.y() - h2.y(), h1.z() - h2.z()) <= range; } @@ -138,6 +165,12 @@ namespace Jug::Reco { void dfs_group(std::vector<eic::CalorimeterHit>& group, int idx, const eic::CalorimeterHitCollection& hits, std::vector<bool>& visits) const { + // not a qualified hit to particpate clustering, stop here + if (hits[idx].energy() < minClusterHitEdep) { + visits[idx] = true; + return; + } + eic::CalorimeterHit hit{hits[idx].cellID(), hits[idx].clusterID(), hits[idx].layerID(), hits[idx].sectorID(), hits[idx].energy(), hits[idx].time(), @@ -154,10 +187,10 @@ namespace Jug::Reco { } // find local maxima that above a certain threshold - eic::Cluster find_maxima(const std::vector<eic::CalorimeterHit>& group, - bool global = false) const + std::vector<eic::ConstCalorimeterHit> find_maxima(const std::vector<eic::CalorimeterHit>& group, + bool global = false) const { - eic::Cluster maxima; + std::vector<eic::ConstCalorimeterHit> maxima; if (group.empty()) { return maxima; } @@ -169,13 +202,15 @@ namespace Jug::Reco { mpos = i; } } - maxima.addhits(group[mpos]); + if (group[mpos].energy() >= minClusterCenterEdep) { + maxima.push_back(group[mpos]); + } return maxima; } for (auto& hit : group) { // not a qualified center - if (hit.energy() < m_minClusterCenterEdep / GeV) { + if (hit.energy() < minClusterCenterEdep) { continue; } @@ -191,7 +226,7 @@ namespace Jug::Reco { } if (maximum) { - maxima.addhits(hit); + maxima.push_back(hit); } } @@ -212,13 +247,14 @@ namespace Jug::Reco { // split a group of hits according to the local maxima // split_hits is used to persistify the data - void split_group(std::vector<eic::CalorimeterHit>& group, const eic::Cluster& maxima, + void split_group(std::vector<eic::CalorimeterHit>& group, + const std::vector<eic::ConstCalorimeterHit>& maxima, size_t& clusterID, eic::CalorimeterHitCollection& clustered_hits) const { // special cases - if (maxima.hits_size() == 0) { + if (maxima.size() == 0) { return; - } else if (maxima.hits_size() == 1) { + } else if (maxima.size() == 1) { for (auto& hit : group) { hit.clusterID(clusterID); clustered_hits.push_back(hit); @@ -228,15 +264,15 @@ namespace Jug::Reco { } // split between maxima - std::vector<double> weights(maxima.hits_size()); - std::vector<eic::Cluster> splits(maxima.hits_size()); + std::vector<double> weights(maxima.size()); + std::vector<eic::Cluster> splits(maxima.size()); size_t n_clus = clusterID + 1; size_t i = 0; for (auto it = group.begin(); it != group.end(); ++it, ++i) { auto hedep = it->energy(); size_t j = 0; // calculate weights for local maxima - for (auto cit = maxima.hits_begin(); cit != maxima.hits_end(); ++cit, ++j) { + for (auto cit = maxima.begin(); cit != maxima.end(); ++cit, ++j) { double dist_ref = cit->dim_x(); double energy = cit->energy(); double dist = std::sqrt(std::pow(it->local_x() - cit->local_x(), 2) + diff --git a/JugReco/src/components/ImagingClusterReco.cpp b/JugReco/src/components/ImagingClusterReco.cpp index 59bc273240ca66c5d92583abc2c6a62b5b5b1109..afdc4f72f9df196f96a3f176d7603a7b2375425c 100644 --- a/JugReco/src/components/ImagingClusterReco.cpp +++ b/JugReco/src/components/ImagingClusterReco.cpp @@ -102,7 +102,7 @@ namespace Jug::Reco { // debug output for (const auto& cl : clusters) { debug() << fmt::format("Cluster {:d}: Edep = {:.3f} MeV, Dir = ({:.3f}, {:.3f}) deg", - cl.clusterID(), cl.edep() / MeV, cl.cl_theta() / M_PI * 180., + cl.clusterID(), cl.edep()*1000., cl.cl_theta() / M_PI * 180., cl.cl_phi() / M_PI * 180.) << endmsg; } diff --git a/JugReco/src/components/ImagingPixelReco.cpp b/JugReco/src/components/ImagingPixelReco.cpp index 6b411b37012ff6602876b1150e66e853b3d709a5..9dab6d552e873ea49118a6ce7d1e9be544f599f7 100644 --- a/JugReco/src/components/ImagingPixelReco.cpp +++ b/JugReco/src/components/ImagingPixelReco.cpp @@ -44,6 +44,10 @@ namespace Jug::Reco { Gaudi::Property<double> m_dyRangeADC{this, "dynamicRangeADC", 100 * MeV}; Gaudi::Property<double> m_pedSigmaADC{this, "pedestalSigma", 3.2}; Gaudi::Property<double> m_thresholdADC{this, "thresholdFactor", 3.0}; + + // unitless counterparts for the input parameters + double dyRangeADC; + // hits containers DataHandle<eic::RawCalorimeterHitCollection> m_inputHitCollection{ "inputHitCollection", Gaudi::DataHandle::Reader, this}; @@ -90,6 +94,9 @@ namespace Jug::Reco { return StatusCode::FAILURE; } + // unitless conversion + dyRangeADC = m_dyRangeADC.value()/GeV; + return StatusCode::SUCCESS; } @@ -106,8 +113,7 @@ namespace Jug::Reco { if ((rh.amplitude() - m_pedMeanADC) < m_thresholdADC * m_pedSigmaADC) { continue; } - double edep = (rh.amplitude() - m_pedMeanADC) / (double)m_capADC * - m_dyRangeADC; // convert ADC -> energy + double edep = (rh.amplitude() - m_pedMeanADC) / (double)m_capADC * dyRangeADC; // convert ADC -> energy double time = rh.timeStamp(); // ns auto id = rh.cellID(); int lid = (int)id_dec->get(id, layer_idx); diff --git a/JugReco/src/components/ImagingTopoCluster.cpp b/JugReco/src/components/ImagingTopoCluster.cpp index afe8f12cc551efe71ff7d469076a2320e87cbd2c..4922d4571b543fe712250c55dbba52b59a607f93 100644 --- a/JugReco/src/components/ImagingTopoCluster.cpp +++ b/JugReco/src/components/ImagingTopoCluster.cpp @@ -37,20 +37,22 @@ namespace Jug::Reco { class ImagingTopoCluster : public GaudiAlgorithm { public: // maximum difference in layer numbers that can be considered as neighbours - Gaudi::Property<int> m_adjLayerDiff{this, "adjLayerDiff", 1}; + Gaudi::Property<int> m_neighbourLayersRange{this, "neighbourLayersRange", 1}; // maximum distance of local (x, y) to be considered as neighbors at the same layer - Gaudi::Property<std::vector<double>> u_localRanges{this, "localRanges", {1.0 * mm, 1.0 * mm}}; + Gaudi::Property<std::vector<double>> u_localDistXY{this, "localDistXY", {1.0 * mm, 1.0 * mm}}; // maximum distance of global (eta, phi) to be considered as neighbors at different layers - Gaudi::Property<std::vector<double>> u_adjLayerRanges{ - this, "adjLayerRanges", {0.01 * M_PI, 0.01 * M_PI}}; + Gaudi::Property<std::vector<double>> u_layerDistEtaPhi{this, "layerDistEtaPhi", {0.01, 0.01}}; // maximum global distance to be considered as neighbors in different sectors - Gaudi::Property<double> m_adjSectorDist{this, "adjSectorDist", 1.0 * cm}; + Gaudi::Property<double> m_sectorDist{this, "sectorDist", 1.0 * cm}; + + // minimum hit energy to participate clustering + Gaudi::Property<double> m_minClusterHitEdep{this, "minClusterHitEdep", 0.}; // minimum cluster center energy (to be considered as a seed for cluster) Gaudi::Property<double> m_minClusterCenterEdep{this, "minClusterCenterEdep", 0.}; // minimum cluster energy (to save this cluster) - Gaudi::Property<double> m_minEdep{this, "minClusterEdep", 0.5 * MeV}; + Gaudi::Property<double> m_minClusterEdep{this, "minClusterEdep", 0.5 * MeV}; // minimum number of hits (to save this cluster) - Gaudi::Property<int> m_minNhits{this, "minClusterNhits", 10}; + Gaudi::Property<int> m_minClusterNhits{this, "minClusterNhits", 10}; // input hits collection DataHandle<eic::ImagingPixelCollection> m_inputHitCollection{"inputHitCollection", Gaudi::DataHandle::Reader, this}; @@ -58,6 +60,10 @@ namespace Jug::Reco { DataHandle<eic::ImagingPixelCollection> m_outputHitCollection{"outputHitCollection", Gaudi::DataHandle::Writer, this}; + // unitless counterparts of the input parameters + double localDistXY[2], layerDistEtaPhi[2], sectorDist; + double minClusterHitEdep, minClusterCenterEdep, minClusterEdep, minClusterNhits; + ImagingTopoCluster(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) { declareProperty("inputHitCollection", m_inputHitCollection, ""); @@ -70,25 +76,37 @@ namespace Jug::Reco { return StatusCode::FAILURE; } - // check local clustering range - if (u_localRanges.size() < 2) { - error() << "Need 2-dimensional ranges for same-layer clustering, only have " - << u_localRanges.size() << endmsg; + // unitless conversion + // sanity checks + if (u_localDistXY.size() != 2) { + error() << "Expected 2 values (x_dist, y_dist) for localDistXY" << endmsg; return StatusCode::FAILURE; } - info() << "Same layer hits group ranges" - << " (" << u_localRanges[0] / mm << " mm, " << u_localRanges[1] / mm << " mm)" - << endmsg; - - // check adjacent layer clustering range - if (u_adjLayerRanges.size() < 2) { - error() << "Need 2-dimensional ranges for adjacent-layer clustering, only have " - << u_adjLayerRanges.size() << endmsg; + if (u_layerDistEtaPhi.size() != 2) { + error() << "Expected 2 values (eta_dist, phi_dist) for layerDistEtaPhi" << endmsg; return StatusCode::FAILURE; } - info() << "Same layer hits group ranges" - << " (" << u_adjLayerRanges[0] / M_PI * 1000. << " mrad, " - << u_adjLayerRanges[1] / M_PI * 1000. << " mrad)" << endmsg; + + // using juggler internal units (GeV, mm, ns, rad) + localDistXY[0] = u_localDistXY.value()[0]/mm; + localDistXY[1] = u_localDistXY.value()[1]/mm; + layerDistEtaPhi[0] = u_layerDistEtaPhi.value()[0]; + layerDistEtaPhi[1] = u_layerDistEtaPhi.value()[1]/rad; + sectorDist = m_sectorDist.value()/mm; + minClusterHitEdep = m_minClusterHitEdep.value()/GeV; + minClusterCenterEdep = m_minClusterCenterEdep.value()/GeV; + minClusterEdep = m_minClusterEdep.value()/GeV; + + // summarize the clustering parameters + info() << fmt::format("Local clustering (same sector and same layer): " + "Local [x, y] distance between hits <= [{:.4f} mm, {:.4f} mm].", + localDistXY[0], localDistXY[1]) << endmsg; + info() << fmt::format("Neighbour layers clustering (same sector and layer id within +- {:d}: " + "Global [eta, phi] distance between hits <= [{:.4f}, {:.4f} rad].", + m_neighbourLayersRange.value(), layerDistEtaPhi[0], layerDistEtaPhi[1]) << endmsg; + info() << fmt::format("Neighbour sectors clustering (different sector): " + "Global distance between hits <= {:.4f} mm.", + sectorDist) << endmsg; return StatusCode::SUCCESS; } @@ -105,7 +123,7 @@ namespace Jug::Reco { std::vector<std::vector<eic::ImagingPixel>> groups; for (size_t i = 0; i < hits.size(); ++i) { // already in a group, or not energetic enough to form a cluster - if (visits[i] || hits[i].edep() < m_minClusterCenterEdep) { + if (visits[i] || hits[i].edep() < minClusterCenterEdep) { continue; } groups.emplace_back(); @@ -116,14 +134,14 @@ namespace Jug::Reco { size_t clusterID = 0; for (const auto& group : groups) { - if ((int)group.size() < m_minNhits) { + if (static_cast<int>(group.size()) < m_minClusterNhits.value()) { continue; } double edep = 0.; for (const auto& hit : group) { edep += hit.edep(); } - if (edep < m_minEdep) { + if (edep < minClusterEdep) { continue; } for (auto hit : group) { @@ -148,18 +166,18 @@ namespace Jug::Reco { // different sectors, simple distance check if (h1.sectorID() != h2.sectorID()) { return std::sqrt(pow2(h1.x() - h2.x()) + pow2(h1.y() - h2.y()) + pow2(h1.z() - h2.z())) <= - m_adjSectorDist; + sectorDist; } // layer check int ldiff = std::abs(h1.layerID() - h2.layerID()); // same layer, check local positions if (!ldiff) { - return (std::abs(h1.local_x() - h2.local_x()) <= u_localRanges[0]) && - (std::abs(h1.local_y() - h2.local_y()) <= u_localRanges[1]); - } else if (ldiff <= m_adjLayerDiff) { - return (std::abs(h1.eta() - h2.eta()) <= u_adjLayerRanges[0]) && - (std::abs(h1.phi() - h2.phi()) <= u_adjLayerRanges[1]); + return (std::abs(h1.local_x() - h2.local_x()) <= localDistXY[0]) && + (std::abs(h1.local_y() - h2.local_y()) <= localDistXY[1]); + } else if (ldiff <= m_neighbourLayersRange) { + return (std::abs(h1.eta() - h2.eta()) <= layerDistEtaPhi[0]) && + (std::abs(h1.phi() - h2.phi()) <= layerDistEtaPhi[1]); } // not in adjacent layers @@ -170,6 +188,12 @@ namespace Jug::Reco { void dfs_group(std::vector<eic::ImagingPixel>& group, int idx, const eic::ImagingPixelCollection& hits, std::vector<bool>& visits) const { + // not a qualified hit to participate in clustering, stop here + if (hits[idx].edep() < minClusterHitEdep) { + visits[idx] = true; + return; + } + eic::ImagingPixel hit{hits[idx].clusterID(), hits[idx].layerID(), hits[idx].sectorID(), hits[idx].hitID(), hits[idx].edep(), hits[idx].time(), hits[idx].eta(), hits[idx].local(), hits[idx].position(),