diff --git a/.clang-format b/.clang-format index 6fd6f902b661fe1114f04c8d482b7059b3c2a980..5737aca1ec7df306124ffc6d805befe3ff6ade12 100644 --- a/.clang-format +++ b/.clang-format @@ -1,112 +1,15 @@ --- -Language: Cpp -BasedOnStyle: Chromium -AccessModifierOffset: -2 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: true -AlignConsecutiveDeclarations: true -AlignEscapedNewlines: Right -AlignOperands: true -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: true -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterClass: false - AfterControlStatement: false - AfterEnum: false - AfterFunction: true - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Custom -BreakBeforeInheritanceComma: false -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeColon -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: true -ColumnLimit: 120 -CommentPragmas: '^ IWYU pragma:' -CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 +BasedOnStyle: LLVM +BreakConstructorInitializersBeforeComma: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: true -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH +Standard: Cpp11 +#SpaceBeforeParens: ControlStatements +SpaceAfterControlStatementKeyword: true +PointerBindsToType: true IncludeBlocks: Preserve -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - - Regex: '.*' - Priority: 1 -IncludeIsMainRegex: '(Test)?$' -IndentCaseLabels: false -IndentPPDirectives: None -IndentWidth: 2 -IndentWrappedFunctionNames: false -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: All -ObjCBlockIndentWidth: 2 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 2 -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Left -ReflowComments: true -SortIncludes: true -SortUsingDeclarations: true -SpaceAfterCStyleCast: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -#SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 4 UseTab: Never +ColumnLimit: 120 +NamespaceIndentation: Inner +AlignConsecutiveAssignments: true ... - diff --git a/JugDigi/src/components/CalorimeterHitDigi.cpp b/JugDigi/src/components/CalorimeterHitDigi.cpp index 2dc231974219e6e45f6a3c945e0dadcabd8b3a0f..ec07b3892e21b71752ee757a6b79b9fb4ac128c4 100644 --- a/JugDigi/src/components/CalorimeterHitDigi.cpp +++ b/JugDigi/src/components/CalorimeterHitDigi.cpp @@ -88,6 +88,7 @@ namespace Jug::Digi { const auto simhits = m_inputHitCollection.get(); // Create output collections auto rawhits = m_outputHitCollection.createAndPut(); + int nhits = 0; for (const auto& ahit : *simhits) { // Note: juggler internal unit of energy is GeV double eResRel = std::sqrt(std::pow(m_normDist() * eRes[0] / sqrt(ahit.energyDeposit()), 2) + @@ -95,8 +96,11 @@ namespace Jug::Digi { 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. + eResRel) / dyRangeADC * m_capADC); - eic::RawCalorimeterHit rawhit((long long)ahit.cellID(), (adc > m_capADC.value() ? m_capADC.value() : adc), - (double)ahit.truth().time + m_normDist() * tRes); + eic::RawCalorimeterHit rawhit( + (long long)ahit.cellID(), + (adc > m_capADC.value() ? m_capADC.value() : adc), + static_cast<int64_t>(1e6*(double)ahit.truth().time + m_normDist() * tRes), // @FIXME: this shouldn't be hardcoded, but should still be stored as an integer type + nhits++); rawhits->push_back(rawhit); } return StatusCode::SUCCESS; diff --git a/JugDigi/src/components/CrystalEndcapsDigi.cpp b/JugDigi/src/components/CrystalEndcapsDigi.cpp index b6bff48bd83a682429dcd590764af70a30058eef..9f3abfe09fbf81e21072d07ac7c75d463094a7c7 100644 --- a/JugDigi/src/components/CrystalEndcapsDigi.cpp +++ b/JugDigi/src/components/CrystalEndcapsDigi.cpp @@ -57,13 +57,14 @@ namespace Jug { // Create output collections auto rawhits = m_outputHitCollection.createAndPut(); eic::RawCalorimeterHitCollection* rawHitCollection = new eic::RawCalorimeterHitCollection(); + int nhits = 0; for (const auto& ahit : *simhits) { double res = m_gaussDist()/sqrt(ahit.energyDeposit()); eic::RawCalorimeterHit rawhit( (long long) ahit.cellID(), std::llround(ahit.energyDeposit() * (1. + res)*1.0e6), // convert to keV integer - (double) ahit.truth().time); - rawhits->push_back(rawhit); + (double) ahit.truth().time, nhits++); + rawhits->push_back(rawhit); } return StatusCode::SUCCESS; } diff --git a/JugDigi/src/components/EMCalorimeterDigi.cpp b/JugDigi/src/components/EMCalorimeterDigi.cpp index 5a678d5c1e89f095d67afbb2cce870db21c268a3..0be573682c6c62b8c056ef5db9a2ff2867e94154 100644 --- a/JugDigi/src/components/EMCalorimeterDigi.cpp +++ b/JugDigi/src/components/EMCalorimeterDigi.cpp @@ -57,13 +57,14 @@ namespace Jug { // Create output collections auto rawhits = m_outputHitCollection.createAndPut(); eic::RawCalorimeterHitCollection* rawHitCollection = new eic::RawCalorimeterHitCollection(); + int nhits = 0; for (const auto& ahit : *simhits) { // std::cout << ahit << "\n"; double sqrtE = std::sqrt(ahit.energyDeposit()) ; double aterm = m_gaussDist()*sqrtE; eic::RawCalorimeterHit rawhit((long long)ahit.cellID(), std::llround((ahit.energyDeposit() + aterm) * 1e6), - ahit.truth().time * 1e6); + ahit.truth().time * 1e6, nhits++); rawhits->push_back(rawhit); } return StatusCode::SUCCESS; diff --git a/JugDigi/src/components/EcalTungstenSamplingDigi.cpp b/JugDigi/src/components/EcalTungstenSamplingDigi.cpp index 452634075465aeba6f8d919bf49b6ba8f80d549a..88d67871ed32ec1bf618ff51b9c33ace92b86f03 100644 --- a/JugDigi/src/components/EcalTungstenSamplingDigi.cpp +++ b/JugDigi/src/components/EcalTungstenSamplingDigi.cpp @@ -77,6 +77,7 @@ namespace Jug { // Create output collections auto rawhits = m_outputHitCollection.createAndPut(); eic::RawCalorimeterHitCollection* rawHitCollection = new eic::RawCalorimeterHitCollection(); + int nhits = 0; for (const auto& ahit : *simhits) { double resval = std::pow(m_normDist()*res[0] / sqrt(ahit.energyDeposit()*m_eUnit/GeV), 2) + std::pow(m_normDist()*res[1], 2) @@ -87,7 +88,8 @@ namespace Jug { 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); + (double)ahit.truth().time*m_tUnit/ns + m_normDist()*m_tRes/ns, + nhits++); rawhits->push_back(rawhit); } return StatusCode::SUCCESS; diff --git a/JugDigi/src/components/ExampleCaloDigi.cpp b/JugDigi/src/components/ExampleCaloDigi.cpp index 7e248182e23084928ea02535504d524e855512c6..4152034a24e381f12f690b5f5e2974e5777f05c0 100644 --- a/JugDigi/src/components/ExampleCaloDigi.cpp +++ b/JugDigi/src/components/ExampleCaloDigi.cpp @@ -110,9 +110,10 @@ namespace Jug { // Create output collections auto rawhits = m_outputHitCollection.createAndPut(); eic::RawCalorimeterHitCollection* rawHitCollection = new eic::RawCalorimeterHitCollection(); + int nhits = 0; for(const auto& ahit : *simhits) { //std::cout << ahit << "\n"; - eic::RawCalorimeterHit rawhit((long long)ahit.cellID(), std::llround(ahit.energyDeposit() * 100), 0); + eic::RawCalorimeterHit rawhit((long long)ahit.cellID(), std::llround(ahit.energyDeposit() * 100), 0, nhits++); rawhits->push_back(rawhit); } return StatusCode::SUCCESS; diff --git a/JugDigi/src/components/HadronicCalDigi.cpp b/JugDigi/src/components/HadronicCalDigi.cpp index b0572782a809cc65181dfd63ef01eaae7253f130..a11a2ac95ef878d95450a6a9cf77125c462a6bc5 100644 --- a/JugDigi/src/components/HadronicCalDigi.cpp +++ b/JugDigi/src/components/HadronicCalDigi.cpp @@ -71,6 +71,7 @@ namespace Jug { // Create output collections auto rawhits = m_outputHitCollection.createAndPut(); eic::RawCalorimeterHitCollection* rawHitCollection = new eic::RawCalorimeterHitCollection(); + int nhits = 0; for (const auto& ahit : *simhits) { // std::cout << ahit << "\n"; double sqrtE = std::sqrt(ahit.energyDeposit()) ; @@ -78,7 +79,7 @@ namespace Jug { double bterm = ahit.energyDeposit()*m_gaussDist_b(); // here 1000 is arbitrary scale factor eic::RawCalorimeterHit rawhit((long long)ahit.cellID(), - std::llround(ahit.energyDeposit() +aterm + bterm * 1000), 0); + std::llround(ahit.energyDeposit() +aterm + bterm * 1000), 0, nhits++); rawhits->push_back(rawhit); } return StatusCode::SUCCESS; diff --git a/JugReco/src/components/:wq b/JugReco/src/components/:wq new file mode 100644 index 0000000000000000000000000000000000000000..7c16d5534de6a009709619bb8cf7790561a5fc4f --- /dev/null +++ b/JugReco/src/components/:wq @@ -0,0 +1,229 @@ +/* + * Reconstruct the cluster with Center of Gravity method + * Logarithmic weighting is used for mimicing energy deposit in transverse direction + * + * Author: Chao Peng (ANL), 09/27/2020 + */ +#include <algorithm> +#include <cstring> +#include <functional> +#include <map> + +#include "boost/algorithm/string/join.hpp" +#include "fmt/format.h" +#include <boost/range/adaptor/map.hpp> + +#include "Gaudi/Property.h" +#include "GaudiAlg/GaudiAlgorithm.h" +#include "GaudiAlg/GaudiTool.h" +#include "GaudiAlg/Transformer.h" +#include "GaudiKernel/PhysicalConstants.h" +#include "GaudiKernel/RndmGenerators.h" +#include "GaudiKernel/ToolHandle.h" + +#include "DDRec/CellIDPositionConverter.h" +#include "DDRec/Surface.h" +#include "DDRec/SurfaceManager.h" + +// FCCSW +#include "JugBase/DataHandle.h" +#include "JugBase/IGeoSvc.h" + +// Event Model related classes +#include "eicd/CalorimeterHitCollection.h" +#include "eicd/ProtoClusterCollection.h" +#include "eicd/ClusterCollection.h" +#include "eicd/Cluster2DInfoCollection.h" +#include "eicd/VectorPolar.h" + +using namespace Gaudi::Units; + +namespace Jug::Reco { + + // weighting functions (with place holders for hit energy, total energy, one parameter and module + // type enum + static double constWeight(double /*E*/, double /*tE*/, double /*p*/, int /*type*/) { return 1.0; } + static double linearWeight(double E, double /*tE*/, double /*p*/, int /*type*/) { return E; } + static double logWeight(double E, double tE, double base, int /*type*/) + { + return std::max(0., base + std::log(E / tE)); + } + + static const std::map<std::string, std::function<double(double, double, double, int)>> + weightMethods { + {"none", constWeight}, + {"linear", linearWeight}, + {"log", logWeight}, + }; + + /** Clustering with center of gravity method. + * + * Reconstruct the cluster with Center of Gravity method + * Logarithmic weighting is used for mimicing energy deposit in transverse direction + * + * \ingroup reco + */ + class ClusterRecoCoG : public GaudiAlgorithm { + public: + Gaudi::Property<double> m_sampFrac{this, "samplingFraction", 1.0}; + Gaudi::Property<double> m_logWeightBase{this, "logWeightBase", 3.6}; + Gaudi::Property<double> m_depthCorrection{this, "depthCorrection", 0.0}; + Gaudi::Property<std::string> m_energyWeight{this, "energyWeight", "log"}; + Gaudi::Property<std::string> m_moduleDimZName{this, "moduleDimZName", ""}; + DataHandle<eic::CalorimeterHitCollection> m_inputHits{"inputHitCollection", + Gaudi::DataHandle::Reader, this}; + DataHandle<eic::ProtoClusterCollection> m_inputProto{"inputProtoClusterCollection", + Gaudi::DataHandle::Reader, this}; + DataHandle<eic::ClusterCollection> m_outputClusters{"outputClusterCollection", + Gaudi::DataHandle::Writer, this}; + DataHandle<eic::Cluster2DInfoCollection> m_outputInfo{"outputInfoCollection", + Gaudi::DataHandle::Writer, this}; + // Pointer to the geometry service + SmartIF<IGeoSvc> m_geoSvc; + double m_depthCorr; + std::function<double(double, double, double, int)> weightFunc; + + ClusterRecoCoG(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) + { + declareProperty("inputHitCollection", m_inputHits, ""); + declareProperty("inputProtoClusterCollection", m_inputProto, ""); + declareProperty("outputClusterCollection", m_outputClusters, ""); + declareProperty("outputInfoCollection", m_outputInfo, ""); + } + + StatusCode initialize() override + { + if (GaudiAlgorithm::initialize().isFailure()) { + return StatusCode::FAILURE; + } + m_geoSvc = service("GeoSvc"); + if (!m_geoSvc) { + error() << "Unable to locate Geometry Service. " + << "Make sure you have GeoSvc and SimSvc in the right order in the configuration." + << endmsg; + return StatusCode::FAILURE; + } + // update depth correction if a name is provided + if (!m_moduleDimZName.value().empty()) { + m_depthCorrection = m_geoSvc->detector()->constantAsDouble(m_moduleDimZName); + } + + // select weighting method + std::string ew = m_energyWeight.value(); + // make it case-insensitive + std::transform(ew.begin(), ew.end(), ew.begin(), [](char s) { return std::tolower(s); }); + auto it = weightMethods.find(ew); + if (it == weightMethods.end()) { + error() << fmt::format( + "Cannot find energy weighting method {}, choose one from [{}]", + m_energyWeight, + boost::algorithm::join(weightMethods | boost::adaptors::map_keys, ", ")) + << endmsg; + return StatusCode::FAILURE; + } + weightFunc = it->second; + // info() << "z_length " << depth << endmsg; + return StatusCode::SUCCESS; + } + + StatusCode execute() override + { + // input collections + const auto& hits = *m_inputHits.get(); + const auto& proto = *m_inputProto.get(); + auto& clusters = *m_outputClusters.createAndPut(); + auto& info = *m_outputInfo.createAndPut(); + + // Create a map of clusterID --> associated hits by looping over our clustered hits + std::map<int, std::vector<std::pair<eic::ConstProtoCluster, + eic::ConstCalorimeterHit>>> cluster_map; + for (const auto& pc : proto) { + const size_t clusterID = pc.clusterID(); + if (!cluster_map.count(clusterID)) { + cluster_map[clusterID] = {}; + } + size_t idx; + for (idx = 0; idx < hits.size(); ++idx) { + if (hits[idx].ID() == pc.hitID()) { + break; + } + } + if (idx >= hits.size()) { + continue; + } + cluster_map[clusterID].push_back({pc, hits[idx]}); + } + + for (const auto& [idx, hit_info] : cluster_map) { + auto cl = reconstruct(hit_info, idx); + + debug() << cl.nhits() << " hits: " << cl.energy() / GeV << " GeV, (" << cl.position().x / mm + << ", " << cl.position().y / mm << ", " << cl.position().z / mm << ")" << endmsg; + clusters.push_back(cl); + info.push_back({cl.ID(), cl.position(), cl.position().eta()}); + } + + return StatusCode::SUCCESS; + } + + private: + template <typename T1> + eic::VectorPolar cart_to_polar(const T1& cart) + { + auto r = std::sqrt(cart.x * cart.x + cart.y * cart.y + cart.z * cart.z); + return eic::VectorPolar{r, std::acos(cart.z / r), std::atan2(cart.y, cart.x)}; + } + + eic::Cluster reconstruct(const std::vector<std::pair<eic::ConstProtoCluster, + eic::ConstCalorimeterHit>>& hit_info, + const int idx) const + { + eic::Cluster cl; + cl.ID(idx); + cl.nhits(hit_info.size()); + + // no hits + debug() << "hit size = " << hit_info.size() << endmsg; + if (hit_info.empty()) { + return cl; + } + + // calculate total energy, find the cell with the maximum energy deposit + float totalE = 0.; + float maxE = 0.; + auto time = hit_info[0].second.time(); + for (const auto& [proto, hit] : hit_info) { + debug() << "hit energy = " << hit.energy() << " hit weight: " << proto.weight() << endmsg; + auto energy = hit.energy() * proto.weight(); + totalE += energy; + if (energy > maxE) { + maxE = energy; + time = hit.time(); + } + } + cl.energy(totalE/m_sampFrac); + cl.energyError(0.); + cl.time(time); + + // center of gravity with logarithmic weighting + float tw = 0.; + eic::VectorXYZ v; + for (const auto& [proto, hit] : hit_info) { + float w = weightFunc(hit.energy()*proto.weight(), totalE, m_logWeightBase.value(), 0); + tw += w; + v = v.add(hit.position().scale(w)); + } + if (tw == 0.) { + warning() << "zero total weights encountered, you may want to adjust your weighting parameter." << endmsg; + } + cl.position(v.scale(1/tw)); + cl.positionError({}); // @TODO: Covariance matrix + + return cl; + } + }; + + DECLARE_COMPONENT(ClusterRecoCoG) + +} // namespace Jug::Reco + diff --git a/JugReco/src/components/CalorimeterHitReco.cpp b/JugReco/src/components/CalorimeterHitReco.cpp index 5efbc0b21bf618014ac2f5cc95cb4493c5c72203..5dc5cfc2323dbb46083e5611a1ff5f44a3a10642 100644 --- a/JugReco/src/components/CalorimeterHitReco.cpp +++ b/JugReco/src/components/CalorimeterHitReco.cpp @@ -51,6 +51,9 @@ namespace Jug::Reco { // zero suppression values Gaudi::Property<double> m_thresholdADC{this, "thresholdFactor", 3.0}; + // Calibration! + Gaudi::Property<double> m_sampFrac{this, "samplingFraction", 1.0}; + // unitless counterparts of the input parameters double dyRangeADC; @@ -165,35 +168,34 @@ namespace Jug::Reco { } // convert ADC -> energy - float energy = (rh.amplitude() - m_pedMeanADC) / static_cast<float>(m_capADC.value()) * dyRangeADC; + float energy = (rh.amplitude() - m_pedMeanADC) / static_cast<float>(m_capADC.value()) * dyRangeADC / m_sampFrac; - float time = rh.timeStamp(); // ns - auto id = rh.cellID(); + float time = rh.time()*1.e-6; // ns @FIXME: this should not be hardcoded + auto cellID = rh.cellID(); int lid = ((id_dec != nullptr) && m_layerField.value().size()) - ? static_cast<int>(id_dec->get(id, layer_idx)) + ? static_cast<int>(id_dec->get(cellID, layer_idx)) : -1; int sid = ((id_dec != nullptr) && m_sectorField.value().size()) - ? static_cast<int>(id_dec->get(id, sector_idx)) + ? static_cast<int>(id_dec->get(cellID, sector_idx)) : -1; // global positions - auto gpos = converter->position(id); + auto gpos = converter->position(cellID); // local positions if (m_localDetElement.value().empty()) { auto volman = m_geoSvc->detector()->volumeManager(); - local = volman.lookupDetElement(id & local_mask); + local = volman.lookupDetElement(cellID & local_mask); } auto pos = local.nominal().worldToLocal(dd4hep::Position(gpos.x(), gpos.y(), gpos.z())); - // auto pos = m_geoSvc->cellIDPositionConverter()->findContext(id)->volumePlacement().position(); // cell dimension std::vector<double> cdim; // get segmentation dimensions if (converter->findReadout(local).segmentation().type() != "NoSegmentation") { - cdim = converter->cellDimensions(id); + cdim = converter->cellDimensions(cellID); // get volume dimensions (multiply by two to get fullsize) } else { // cdim = converter->findContext(id)->volumePlacement().volume().solid().dimensions(); // Using bounding box instead of actual solid so the dimensions are always in dim_x, dim_y, dim_z - cdim = converter->findContext(id)->volumePlacement().volume().boundingBox().dimensions(); + cdim = converter->findContext(cellID)->volumePlacement().volume().boundingBox().dimensions(); std::transform(cdim.begin(), cdim.end(), cdim.begin(), std::bind(std::multiplies<double>(), std::placeholders::_1, 2)); } @@ -206,16 +208,17 @@ namespace Jug::Reco { // m_geoSvc->cellIDPositionConverter()->findContext(id)->volumePlacement().volIDs().str() // << endmsg; hits.push_back({ - id, - -1, + rh.cellID(), + rh.ID(), lid, - sid, // cell id, cluster id, layer id, sector id + sid, // cell id, cluster id, layer id, sector id + 0, // @TODO: hit type energy, + 0, // @TODO: energy error time, // energy, time {gpos.x() / m_lUnit, gpos.y() / m_lUnit, gpos.z() / m_lUnit}, {pos.x() / m_lUnit, pos.y() / m_lUnit, pos.z() / m_lUnit}, - {dim[0], dim[1], dim[2]}, - 0 // @TODO: hit type + {dim[0], dim[1], dim[2]} }); } diff --git a/JugReco/src/components/CalorimeterHitsEtaPhiProjector.cpp b/JugReco/src/components/CalorimeterHitsEtaPhiProjector.cpp index 71e86e4f80a24a4d732561664618444f69e0e551..5faaa418266ce4fc5cfed3dac4fa55355a26a638 100644 --- a/JugReco/src/components/CalorimeterHitsEtaPhiProjector.cpp +++ b/JugReco/src/components/CalorimeterHitsEtaPhiProjector.cpp @@ -107,8 +107,8 @@ namespace Jug::Reco { const auto ref = hits.front(); eic::CalorimeterHit hit; hit.cellID(ref.cellID()); - hit.sectorID(ref.sectorID()); - hit.layerID(ref.layerID()); + hit.sector(ref.sector()); + hit.layer(ref.layer()); // TODO, we can do timing cut to reject noises hit.time(ref.time()); double r = ref.position().mag(); diff --git a/JugReco/src/components/CalorimeterHitsMerger.cpp b/JugReco/src/components/CalorimeterHitsMerger.cpp index e2e95a4f48e8c00f691252c1b1f1f6f399c10190..5d18447ce02a4ec1be90e5c1f64c9a793a0d5dbc 100644 --- a/JugReco/src/components/CalorimeterHitsMerger.cpp +++ b/JugReco/src/components/CalorimeterHitsMerger.cpp @@ -132,6 +132,7 @@ namespace Jug::Reco { auto poscon = m_geoSvc->cellIDPositionConverter(); auto volman = m_geoSvc->detector()->volumeManager(); + int nresults = 0; for (auto &[id, hits] : merge_map) { // reference fields id int64_t ref_id = id | ref_mask; @@ -151,16 +152,17 @@ namespace Jug::Reco { } 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()}); + ref_id, + nresults++, + href.layer(), + href.sector(), + href.type(), + energy, + 0, //@TODO: energy uncertainty + 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()}); } debug() << "Size before = " << inputs.size() << ", after = " << outputs.size() << endmsg; diff --git a/JugReco/src/components/CalorimeterIslandCluster.cpp b/JugReco/src/components/CalorimeterIslandCluster.cpp index b1930c49f9e3978e92780681aaee7fd2b967a614..7c05213b6eb3fb1a7d99a21139bd78115ef1715f 100644 --- a/JugReco/src/components/CalorimeterIslandCluster.cpp +++ b/JugReco/src/components/CalorimeterIslandCluster.cpp @@ -34,6 +34,7 @@ // Event Model related classes #include "eicd/CalorimeterHitCollection.h" #include "eicd/ClusterCollection.h" +#include "eicd/ProtoClusterCollection.h" #include "eicd/VectorXY.h" #include "eicd/VectorXYZ.h" @@ -93,7 +94,7 @@ namespace Jug::Reco { 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", + DataHandle<eic::ProtoClusterCollection> m_outputProtoCollection{"outputProtoClusterCollection", Gaudi::DataHandle::Writer, this}; // neighbour checking distances @@ -104,7 +105,7 @@ namespace Jug::Reco { Gaudi::Property<std::vector<double>> u_globalDistRPhi{this, "globalDistRPhi", {}}; Gaudi::Property<std::vector<double>> u_globalDistEtaPhi{this, "globalDistEtaPhi", {}}; Gaudi::Property<std::vector<double>> u_dimScaledLocalDistXY{this, "dimScaledLocalDistXY", {1.8, 1.8}}; - // neightbor checking function + // neighbor checking function std::function<eic::VectorXY(eic::ConstCalorimeterHit, eic::ConstCalorimeterHit)> hitsDist; // unitless counterparts of the input parameters @@ -115,7 +116,7 @@ namespace Jug::Reco { : GaudiAlgorithm(name, svcLoc) { declareProperty("inputHitCollection", m_inputHitCollection, ""); - declareProperty("outputHitCollection", m_splitHitCollection, ""); + declareProperty("outputProtoClusterCollection", m_outputProtoCollection, ""); } StatusCode initialize() override @@ -175,10 +176,10 @@ namespace Jug::Reco { // input collections const auto& hits = *m_inputHitCollection.get(); // Create output collections - auto& clustered_hits = *m_splitHitCollection.createAndPut(); + auto& proto = *m_outputProtoCollection.createAndPut(); // group neighboring hits - std::vector<std::vector<eic::CalorimeterHit>> groups; + std::vector<std::vector<eic::ConstCalorimeterHit>> groups; std::vector<bool> visits(hits.size(), false); for (size_t i = 0; i < hits.size(); ++i) { @@ -186,7 +187,7 @@ namespace Jug::Reco { "global=({:.4f}, {:.4f}, {:.4f}) mm, layer = {:d}, sector = {:d}.", i, hits[i].energy()*1000., hits[i].local().x, hits[i].local().y, hits[i].position().x, hits[i].position().y, hits[i].position().z, - hits[i].layerID(), hits[i].sectorID()) << endmsg; + hits[i].layer(), hits[i].sector()) << endmsg; // already in a group if (visits[i]) { continue; @@ -202,7 +203,7 @@ namespace Jug::Reco { continue; } auto maxima = find_maxima(group, !m_splitCluster.value()); - split_group(group, maxima, clusterID, clustered_hits); + split_group(group, maxima, clusterID, proto); debug() << "hits in a group: " << group.size() << ", " << "local maxima: " << maxima.size() << endmsg; debug() << "total number of clusters so far: " << clusterID << ", " << endmsg; @@ -216,7 +217,7 @@ namespace Jug::Reco { inline bool is_neighbour(const eic::ConstCalorimeterHit& h1, const eic::ConstCalorimeterHit& h2) const { // in the same sector - if (h1.sectorID() == h2.sectorID()) { + if (h1.sector() == h2.sector()) { auto dist = hitsDist(h1, h2); return (dist.x <= neighbourDist[0]) && (dist.y <= neighbourDist[1]); // different sector, local coordinates do not work, using global coordinates @@ -227,7 +228,7 @@ namespace Jug::Reco { } // grouping function with Depth-First Search - void dfs_group(std::vector<eic::CalorimeterHit>& group, int idx, + void dfs_group(std::vector<eic::ConstCalorimeterHit>& group, int idx, const eic::CalorimeterHitCollection& hits, std::vector<bool>& visits) const { // not a qualified hit to particpate clustering, stop here @@ -236,15 +237,10 @@ namespace Jug::Reco { return; } - eic::CalorimeterHit hit{hits[idx].cellID(), hits[idx].clusterID(), - hits[idx].layerID(), hits[idx].sectorID(), - hits[idx].energy(), hits[idx].time(), - hits[idx].position(), hits[idx].local(), - hits[idx].dimension(), 1}; - group.push_back(hit); + group.push_back(hits[idx]); visits[idx] = true; for (size_t i = 0; i < hits.size(); ++i) { - if (visits[i] || !is_neighbour(hit, hits[i])) { + if (visits[i] || !is_neighbour(hits[idx], hits[i])) { continue; } dfs_group(group, i, hits, visits); @@ -252,7 +248,7 @@ namespace Jug::Reco { } // find local maxima that above a certain threshold - std::vector<eic::ConstCalorimeterHit> find_maxima(const std::vector<eic::CalorimeterHit>& group, + std::vector<eic::ConstCalorimeterHit> find_maxima(const std::vector<eic::ConstCalorimeterHit>& group, bool global = false) const { std::vector<eic::ConstCalorimeterHit> maxima; @@ -312,17 +308,16 @@ 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, + void split_group(std::vector<eic::ConstCalorimeterHit>& group, const std::vector<eic::ConstCalorimeterHit>& maxima, - size_t& clusterID, eic::CalorimeterHitCollection& clustered_hits) const + size_t& clusterID, eic::ProtoClusterCollection& proto) const { // special cases if (maxima.size() == 0) { return; } else if (maxima.size() == 1) { for (auto& hit : group) { - hit.clusterID(clusterID); - clustered_hits.push_back(hit); + proto.create(hit.ID(), clusterID, 1.); } clusterID += 1; return; @@ -331,11 +326,9 @@ namespace Jug::Reco { // split between maxima // TODO, here we can implement iterations with profile, or even ML for better splits 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.begin(); cit != maxima.end(); ++cit, ++j) { @@ -357,19 +350,15 @@ namespace Jug::Reco { vec_normalize(weights); // split energy between local maxima - for (size_t k = 0; k < weights.size(); ++k) { + for (size_t k = 0; k < maxima.size(); ++k) { double weight = weights[k]; if (weight <= 1e-6) { continue; } - auto hit = it->clone(); - hit.energy(hedep * weight); - hit.type(1); - hit.clusterID(n_clus + k); - clustered_hits.push_back(hit); + proto.create(it->ID(), n_clus + k, weight); } } - clusterID += splits.size(); + clusterID += maxima.size(); return; } }; diff --git a/JugReco/src/components/ClusterRecoCoG.cpp b/JugReco/src/components/ClusterRecoCoG.cpp index b3764a92977d308ff1c7ba2563a5b6f6593796c9..7c16d5534de6a009709619bb8cf7790561a5fc4f 100644 --- a/JugReco/src/components/ClusterRecoCoG.cpp +++ b/JugReco/src/components/ClusterRecoCoG.cpp @@ -31,7 +31,10 @@ // Event Model related classes #include "eicd/CalorimeterHitCollection.h" +#include "eicd/ProtoClusterCollection.h" #include "eicd/ClusterCollection.h" +#include "eicd/Cluster2DInfoCollection.h" +#include "eicd/VectorPolar.h" using namespace Gaudi::Units; @@ -69,8 +72,12 @@ namespace Jug::Reco { Gaudi::Property<std::string> m_moduleDimZName{this, "moduleDimZName", ""}; DataHandle<eic::CalorimeterHitCollection> m_inputHits{"inputHitCollection", Gaudi::DataHandle::Reader, this}; + DataHandle<eic::ProtoClusterCollection> m_inputProto{"inputProtoClusterCollection", + Gaudi::DataHandle::Reader, this}; DataHandle<eic::ClusterCollection> m_outputClusters{"outputClusterCollection", - Gaudi::DataHandle::Writer, this}; + Gaudi::DataHandle::Writer, this}; + DataHandle<eic::Cluster2DInfoCollection> m_outputInfo{"outputInfoCollection", + Gaudi::DataHandle::Writer, this}; // Pointer to the geometry service SmartIF<IGeoSvc> m_geoSvc; double m_depthCorr; @@ -79,7 +86,9 @@ namespace Jug::Reco { ClusterRecoCoG(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) { declareProperty("inputHitCollection", m_inputHits, ""); + declareProperty("inputProtoClusterCollection", m_inputProto, ""); declareProperty("outputClusterCollection", m_outputClusters, ""); + declareProperty("outputInfoCollection", m_outputInfo, ""); } StatusCode initialize() override @@ -121,33 +130,37 @@ namespace Jug::Reco { { // input collections const auto& hits = *m_inputHits.get(); + const auto& proto = *m_inputProto.get(); auto& clusters = *m_outputClusters.createAndPut(); + auto& info = *m_outputInfo.createAndPut(); // Create a map of clusterID --> associated hits by looping over our clustered hits - std::map<int, std::vector<eic::ConstCalorimeterHit>> cluster_map; - for (const auto& hit : hits) { - const size_t idx = hit.clusterID(); - if (!cluster_map.count(idx)) { - cluster_map[idx] = {}; + std::map<int, std::vector<std::pair<eic::ConstProtoCluster, + eic::ConstCalorimeterHit>>> cluster_map; + for (const auto& pc : proto) { + const size_t clusterID = pc.clusterID(); + if (!cluster_map.count(clusterID)) { + cluster_map[clusterID] = {}; } - cluster_map[idx].push_back(hit); + size_t idx; + for (idx = 0; idx < hits.size(); ++idx) { + if (hits[idx].ID() == pc.hitID()) { + break; + } + } + if (idx >= hits.size()) { + continue; + } + cluster_map[clusterID].push_back({pc, hits[idx]}); } - for (const auto& [idx, clhits] : cluster_map) { - auto clhit = reconstruct(clhits); - eic::Cluster cl{idx, - static_cast<float>(clhit.energy() / m_sampFrac), - clhit.energy(), - static_cast<int>(clhits.size()), - clhit.position(), - {}, - cart_to_polar(clhit.position()), - 0., - 0.}; + for (const auto& [idx, hit_info] : cluster_map) { + auto cl = reconstruct(hit_info, idx); debug() << cl.nhits() << " hits: " << cl.energy() / GeV << " GeV, (" << cl.position().x / mm << ", " << cl.position().y / mm << ", " << cl.position().z / mm << ")" << endmsg; clusters.push_back(cl); + info.push_back({cl.ID(), cl.position(), cl.position().eta()}); } return StatusCode::SUCCESS; @@ -161,54 +174,52 @@ namespace Jug::Reco { return eic::VectorPolar{r, std::acos(cart.z / r), std::atan2(cart.y, cart.x)}; } - eic::CalorimeterHit reconstruct(const std::vector<eic::ConstCalorimeterHit>& hits) const + eic::Cluster reconstruct(const std::vector<std::pair<eic::ConstProtoCluster, + eic::ConstCalorimeterHit>>& hit_info, + const int idx) const { - eic::CalorimeterHit res; + eic::Cluster cl; + cl.ID(idx); + cl.nhits(hit_info.size()); + // no hits - debug() << "hit size = " << hits.size() << endmsg; - if (hits.empty()) { - return res; - ; + debug() << "hit size = " << hit_info.size() << endmsg; + if (hit_info.empty()) { + return cl; } // calculate total energy, find the cell with the maximum energy deposit float totalE = 0.; float maxE = 0.; - auto centerID = hits.begin()->cellID(); - for (const auto& hit : hits) { - debug() << "hit energy = " << hit.energy() << endmsg; - auto energy = hit.energy(); + auto time = hit_info[0].second.time(); + for (const auto& [proto, hit] : hit_info) { + debug() << "hit energy = " << hit.energy() << " hit weight: " << proto.weight() << endmsg; + auto energy = hit.energy() * proto.weight(); totalE += energy; if (energy > maxE) { maxE = energy; - centerID = hit.cellID(); + time = hit.time(); } } - res.cellID(centerID); - res.energy(totalE); + cl.energy(totalE/m_sampFrac); + cl.energyError(0.); + cl.time(time); // center of gravity with logarithmic weighting float tw = 0.; eic::VectorXYZ v; - for (auto& hit : hits) { - // suppress low energy contributions - // info() << std::log(hit.energy()/totalE) << endmsg; - float w = weightFunc(hit.energy(), totalE, m_logWeightBase.value(), 0); + for (const auto& [proto, hit] : hit_info) { + float w = weightFunc(hit.energy()*proto.weight(), totalE, m_logWeightBase.value(), 0); tw += w; v = v.add(hit.position().scale(w)); } if (tw == 0.) { warning() << "zero total weights encountered, you may want to adjust your weighting parameter." << endmsg; } - res.position(v.scale(1/tw)); - // convert global position to local position, use the cell with max edep as a reference - const auto volman = m_geoSvc->detector()->volumeManager(); - const auto alignment = volman.lookupDetElement(centerID).nominal(); - const auto lpos = alignment.worldToLocal(dd4hep::Position(res.position().x, res.position().y, res.position().z)); - - // TODO: may need convert back to have depthCorrection in global positions - res.local({lpos.x(), lpos.y(), lpos.z() + m_depthCorrection}); - return res; + cl.position(v.scale(1/tw)); + cl.positionError({}); // @TODO: Covariance matrix + + return cl; } }; diff --git a/JugReco/src/components/CrystalEndcapsReco.cpp b/JugReco/src/components/CrystalEndcapsReco.cpp index ac58cd6341915f1ac1c91b2b6a638c91e1ed0819..9c28f8a62d176e38776311153dcb45f22446357a 100644 --- a/JugReco/src/components/CrystalEndcapsReco.cpp +++ b/JugReco/src/components/CrystalEndcapsReco.cpp @@ -70,10 +70,11 @@ namespace Jug::Reco { auto& hits = *m_outputHitCollection.createAndPut(); // energy time reconstruction + int nhits = 0; for (const auto& rh : rawhits) { float energy = rh.amplitude() / 1.0e6; // convert keV -> GeV if (energy >= (m_minModuleEdep / GeV)) { - float time = rh.timeStamp(); + float time = rh.time(); auto id = rh.cellID(); // global positions auto gpos = m_geoSvc->cellIDPositionConverter()->position(id); @@ -82,16 +83,9 @@ namespace Jug::Reco { m_geoSvc->cellIDPositionConverter()->findContext(id)->volumePlacement().position(); // cell dimension auto dim = m_geoSvc->cellIDPositionConverter()->cellDimensions(id); - hits.push_back(eic::CalorimeterHit{id, - -1, - -1, - -1, - energy, - time, - {gpos.x(), gpos.y(), gpos.z()}, - {pos.x(), pos.y(), pos.z()}, - {dim[0], dim[1], 0.0}, - 0}); + hits.push_back(eic::CalorimeterHit{id, nhits++, -1, -1, 0, energy, 0., time, + {gpos.x(), gpos.y(), gpos.z()}, + {pos.x(), pos.y(), pos.z()}, {dim[0], dim[1], 0.}}); } } diff --git a/JugReco/src/components/EMCalReconstruction.cpp b/JugReco/src/components/EMCalReconstruction.cpp index 7396392c0acddfe6ad4799f17dce8b5195da93d6..b4ff87369f8f15ae7122a61917136a87b18a9a5c 100644 --- a/JugReco/src/components/EMCalReconstruction.cpp +++ b/JugReco/src/components/EMCalReconstruction.cpp @@ -76,10 +76,11 @@ namespace Jug::Reco { auto& hits = *m_outputHitCollection.createAndPut(); // energy time reconstruction + int nhits = 0; for (const auto& rh : rawhits) { float energy = rh.amplitude() / 1e6; // GeV if (energy >= m_minModuleEdep) { - float time = rh.timeStamp() / 1e6; // ns + float time = rh.time() / 1e6; // ns auto id = rh.cellID(); // global positions auto gpos = m_geoSvc->cellIDPositionConverter()->position(id); @@ -90,15 +91,16 @@ namespace Jug::Reco { auto dim = m_geoSvc->cellIDPositionConverter()->cellDimensions(id); hits.push_back(eic::CalorimeterHit{ id, + nhits++, -1, -1, - -1, - static_cast<float>(energy / m_samplingFraction), + 0, + static_cast<float>(energy / m_samplingFraction), // Why are we applying the sampling fraction already here? + 0, 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}, - {dim[0] / dd4hep::mm, dim[1] / dd4hep::mm, 0.0}, - 0}); + {dim[0] / dd4hep::mm, dim[1] / dd4hep::mm, 0.0}}); } } diff --git a/JugReco/src/components/EcalTungstenSamplingCluster.cpp b/JugReco/src/components/EcalTungstenSamplingCluster.cpp index 4a59df2cd86de8d2e7c2d2e9b3444e7d070bfc97..39d0dce573cf00b1864d73e84649a26f1b26eea2 100644 --- a/JugReco/src/components/EcalTungstenSamplingCluster.cpp +++ b/JugReco/src/components/EcalTungstenSamplingCluster.cpp @@ -96,7 +96,7 @@ namespace Jug::Reco { std::vector<bool>& visits) const { visits[index] = true; - auto tot_edep = hits[index].energy(); + auto tot_energy = hits[index].energy(); double pos_x = hits[index].position().x; double pos_y = hits[index].position().y; double pos_z = hits[index].position().z; @@ -110,14 +110,14 @@ namespace Jug::Reco { } // Add up energy deposit based on the same z position and above energy threshold if ((double)hits[i].position().z == ref_pos_z && hits[i].energy() > m_minModuleEdep / GeV) { - tot_edep += hits[i].energy(); + tot_energy += hits[i].energy(); visits[i] = true; } } // Save info as a cluster // TODO: position x and y determination eic::Cluster cl; - cl.edep(tot_edep); + cl.energy(tot_energy); cl.position({pos_x, pos_y, pos_z}); return cl; } diff --git a/JugReco/src/components/EcalTungstenSamplingReco.cpp b/JugReco/src/components/EcalTungstenSamplingReco.cpp index c9bd7975a92687bb811e3e38e905251a7fc4d800..e565f683036a08d692d9e58a2e781225790710ff 100644 --- a/JugReco/src/components/EcalTungstenSamplingReco.cpp +++ b/JugReco/src/components/EcalTungstenSamplingReco.cpp @@ -106,6 +106,7 @@ namespace Jug::Reco { auto& hits = *m_outputHitCollection.createAndPut(); // energy time reconstruction + int nhits = 0; for (const auto& rh : rawhits) { // did not pass the threshold if ((rh.amplitude() - m_pedMeanADC) < m_thresholdADC * m_pedSigmaADC) { @@ -113,7 +114,7 @@ namespace Jug::Reco { } float energy = (rh.amplitude() - m_pedMeanADC) / (float)m_capADC * m_dyRangeADC; // convert ADC -> energy - float time = rh.timeStamp(); // ns + float time = rh.time(); // ns auto id = rh.cellID(); int lid = ((id_dec != nullptr) & m_layerField.value().size()) ? static_cast<int>(id_dec->get(id, layer_idx)) @@ -142,15 +143,16 @@ namespace Jug::Reco { // << endmsg; hits.push_back( eic::CalorimeterHit{id, - -1, + nhits++, lid, sid, + 0, energy, + 0., time, {gpos.x() / m_lUnit, gpos.y() / m_lUnit, gpos.z() / m_lUnit}, {pos.x() / m_lUnit, pos.y() / m_lUnit, pos.z() / m_lUnit}, - {dim[0], dim[1], dim[2]}, - 0}); + {dim[0], dim[1], dim[2]}}); } return StatusCode::SUCCESS; diff --git a/JugReco/src/components/HCalReconstruction.cpp b/JugReco/src/components/HCalReconstruction.cpp index b85fd222caf4e238deb07dc4e0d82b9556bbb2b7..e834af473b2c23a9f0ab19ceef79ae398b95deb0 100644 --- a/JugReco/src/components/HCalReconstruction.cpp +++ b/JugReco/src/components/HCalReconstruction.cpp @@ -76,10 +76,11 @@ namespace Jug::Reco { auto& hits = *m_outputHitCollection.createAndPut(); // energy time reconstruction + int nhits = 0; for (const auto& rh : rawhits) { float energy = rh.amplitude() / 1e6; // GeV if (energy >= m_minModuleEdep) { - float time = rh.timeStamp() / 1e6; // ns + float time = rh.time() / 1e6; // ns auto id = rh.cellID(); // global positions auto gpos = m_geoSvc->cellIDPositionConverter()->position(id); @@ -90,15 +91,16 @@ namespace Jug::Reco { auto dim = m_geoSvc->cellIDPositionConverter()->cellDimensions(id); hits.push_back(eic::CalorimeterHit{ id, + nhits++, -1, -1, - -1, + 0, static_cast<float>(energy / m_samplingFraction), + 0, 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}, - {dim[0] / dd4hep::mm, dim[1] / dd4hep::mm, 0.0}, - 0}); + {dim[0] / dd4hep::mm, dim[1] / dd4hep::mm, 0.0}}); } } diff --git a/JugReco/src/components/ImagingClusterReco.cpp b/JugReco/src/components/ImagingClusterReco.cpp index 84dde0bb64b4056a829b16c6b77ac9365acee324..b563f271e7233a84cb35dc08c7a7d4874f98be95 100644 --- a/JugReco/src/components/ImagingClusterReco.cpp +++ b/JugReco/src/components/ImagingClusterReco.cpp @@ -26,239 +26,242 @@ #include "JugBase/Utilities/Utils.hpp" // Event Model related classes -#include "eicd/ImagingClusterCollection.h" -#include "eicd/ImagingLayerCollection.h" -#include "eicd/ImagingPixelCollection.h" +#include "eicd/CalorimeterHitCollection.h" +#include "eicd/Cluster3DInfoCollection.h" +#include "eicd/ClusterCollection.h" +#include "eicd/ClusterLayerCollection.h" +#include "eicd/ProtoClusterCollection.h" +#include "eicd/VectorPolar.h" +#include "eicd/VectorXYZ.h" using namespace Gaudi::Units; using namespace Eigen; namespace Jug::Reco { - /** Imaging cluster reconstruction. - * - * Reconstruct the cluster/layer info for imaging calorimeter - * Logarithmic weighting is used to describe energy deposit in transverse direction - * - * \ingroup reco - */ - class ImagingClusterReco : public GaudiAlgorithm { - public: - Gaudi::Property<double> m_sampFrac{this, "samplingFraction", 1.0}; - Gaudi::Property<int> m_trackStopLayer{this, "trackStopLayer", 9}; - - DataHandle<eic::ImagingPixelCollection> m_inputHitCollection{"inputHitCollection", - Gaudi::DataHandle::Reader, this}; - DataHandle<eic::ImagingLayerCollection> m_outputLayerCollection{ - "outputLayerCollection", Gaudi::DataHandle::Writer, this}; - DataHandle<eic::ImagingClusterCollection> m_outputClusterCollection{ - "outputClusterCollection", Gaudi::DataHandle::Reader, this}; - - ImagingClusterReco(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) - { - declareProperty("inputHitCollection", m_inputHitCollection, ""); - declareProperty("outputLayerCollection", m_outputLayerCollection, ""); - declareProperty("outputClusterCollection", m_outputClusterCollection, ""); +/** Imaging cluster reconstruction. + * + * Reconstruct the cluster/layer info for imaging calorimeter + * Logarithmic weighting is used to describe energy deposit in transverse direction + * + * \ingroup reco + */ +class ImagingClusterReco : public GaudiAlgorithm { +public: + Gaudi::Property<double> m_sampFrac{this, "samplingFraction", 1.0}; + Gaudi::Property<int> m_trackStopLayer{this, "trackStopLayer", 9}; + + DataHandle<eic::ProtoClusterCollection> m_inputProtoClusterCollection{"inputProtoClusterCollection", + Gaudi::DataHandle::Reader, this}; + DataHandle<eic::CalorimeterHitCollection> m_inputHitCollection{"inputHitCollection", Gaudi::DataHandle::Reader, this}; + DataHandle<eic::ClusterLayerCollection> m_outputLayerCollection{"outputLayerCollection", Gaudi::DataHandle::Writer, + this}; + DataHandle<eic::ClusterCollection> m_outputClusterCollection{"outputClusterCollection", Gaudi::DataHandle::Reader, + this}; + DataHandle<eic::Cluster3DInfoCollection> m_outputInfoCollection{"outputInfoCollection", Gaudi::DataHandle::Reader, + this}; + + ImagingClusterReco(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) { + declareProperty("inputProtoClusterCollection", m_inputProtoClusterCollection, ""); + declareProperty("inputHitCollection", m_inputHitCollection, ""); + declareProperty("outputLayerCollection", m_outputLayerCollection, ""); + declareProperty("outputClusterCollection", m_outputClusterCollection, ""); + declareProperty("outputInfoCollection", m_outputInfoCollection, ""); + } + + StatusCode initialize() override { + if (GaudiAlgorithm::initialize().isFailure()) { + return StatusCode::FAILURE; } - StatusCode initialize() override - { - if (GaudiAlgorithm::initialize().isFailure()) { - return StatusCode::FAILURE; + return StatusCode::SUCCESS; + } + + StatusCode execute() override { + // input collections + const auto& proto = *m_inputProtoClusterCollection.get(); + const auto& hits = *m_inputHitCollection.get(); + // output collections + auto& layers = *m_outputLayerCollection.createAndPut(); + auto& clusters = *m_outputClusterCollection.createAndPut(); + auto& info = *m_outputInfoCollection.createAndPut(); + + // Create a map of clusterID --> associated ProtoCluster by looping over our clustered hits + std::map<int, std::vector<std::pair<eic::ConstProtoCluster, + eic::ConstCalorimeterHit>>> cluster_map; + for (const auto& pc : proto) { + const size_t clusterID = pc.clusterID(); + if (!cluster_map.count(clusterID)) { + cluster_map[clusterID] = {}; } - - return StatusCode::SUCCESS; - } - - StatusCode execute() override - { - // input collections - const auto& hits = *m_inputHitCollection.get(); - // output collections - auto& layers = *m_outputLayerCollection.createAndPut(); - auto& clusters = *m_outputClusterCollection.createAndPut(); - - // Create a map of clusterID --> associated hits by looping over our clustered hits - std::map<int, std::vector<eic::ConstImagingPixel>> cluster_map; - for (const auto& hit : hits) { - const size_t cid = hit.clusterID(); - if (!cluster_map.count(cid)) { - cluster_map[cid] = {}; + size_t idx; + for (idx = 0; idx < hits.size(); ++idx) { + if (hits[idx].ID() == pc.hitID()) { + break; } - cluster_map[cid].push_back(hit); } + cluster_map[clusterID].push_back({pc, hits[idx]}); + } - for (const auto& [cid, clhits] : cluster_map) { - // get cluster and associated layers - auto cl = reconstruct_cluster(clhits, cid); - auto cl_layers = reconstruct_cluster_layers(clhits, cid); - - // reconstruct cluster direction - const auto [cl_theta, cl_phi] = fit_track(cl_layers, m_trackStopLayer); - cl.cl_theta(cl_theta); - cl.cl_phi(cl_phi); + for (const auto& [cid, hit_info] : cluster_map) { + // get cluster and associated layers + auto cl = reconstruct_cluster(hit_info, cid); + auto cl_layers = reconstruct_cluster_layers(hit_info, cid); - // store layer and clusters on the datastore - for (auto& layer : cl_layers) { - layers.push_back(layer); - // cl.addlayers(layer); // deprectated - } - clusters.push_back(cl); - } + // reconstruct cluster direction + eic::Cluster3DInfo cl_info{cl.ID(), cl.position(), cl.position().eta(), fit_track(cl_layers, m_trackStopLayer)}; - // debug output - for (const auto& cl : clusters) { - debug() << fmt::format("Cluster {:d}: Edep = {:.3f} MeV, Dir = ({:.3f}, {:.3f}) deg", - cl.clusterID(), cl.edep()*1000., cl.cl_theta() / M_PI * 180., - cl.cl_phi() / M_PI * 180.) - << endmsg; + // store layer and clusters on the datastore + for (auto& layer : cl_layers) { + layer.ID(layers.size()); // unique ID for this clusterlayer + layers.push_back(layer); + // cl.addlayers(layer); // deprectated } - - return StatusCode::SUCCESS; + clusters.push_back(cl); + info.push_back(cl_info); } - private: - template <typename T> - static inline T pow2(const T& x) - { - return x * x; + // debug output + int idx = 0; + for (const auto& cl : clusters) { + debug() << fmt::format("Cluster {:d}: Edep = {:.3f} MeV, Dir = ({:.3f}, {:.3f}) deg", cl.ID(), cl.energy() * 1000., + info[idx].direction().theta / M_PI * 180., info[idx].direction().phi / M_PI * 180.) + << endmsg; + idx += 1; } - std::vector<eic::ImagingLayer> - reconstruct_cluster_layers(const std::vector<eic::ConstImagingPixel>& hits, const int cid) const - { - // using map to have hits sorted by layer - std::map<int, std::vector<eic::ConstImagingPixel>> layer_map; - for (const auto& hit : hits) { - auto lid = hit.layerID(); - if (!layer_map.count(lid)) { - layer_map[lid] = {}; - } - layer_map[lid].push_back(hit); - } - - // create layers - std::vector<eic::ImagingLayer> cl_layers; - for (const auto& [lid, lhits] : layer_map) { - auto layer = reconstruct_layer(lhits, cid, lid); - cl_layers.push_back(layer); + return StatusCode::SUCCESS; + } + +private: + template <typename T> static inline T pow2(const T& x) { return x * x; } + + std::vector<eic::ClusterLayer> + reconstruct_cluster_layers(const std::vector<std::pair<eic::ConstProtoCluster, eic::ConstCalorimeterHit>>& hit_info, + const int cid) const { + // using map to have hits sorted by layer + std::map<int, std::vector<std::pair<eic::ConstProtoCluster, + eic::ConstCalorimeterHit>>> layer_map; + for (const auto& [proto, hit] : hit_info) { + auto lid = hit.layer(); + if (!layer_map.count(lid)) { + layer_map[lid] = {}; } - return cl_layers; + layer_map[lid].push_back({proto, hit}); } - eic::ImagingLayer reconstruct_layer(const std::vector<eic::ConstImagingPixel>& hits, - const int cid, const int lid) const - { - // use full members initialization here so it could catch changes in ecid - eic::ImagingLayer layer{cid, lid, static_cast<int>(hits.size()), 0., 0., 0., 0., {}, {}}; - - // mean position and total edep - double mx = 0.; - double my = 0.; - double mz = 0.; - double edep = 0.; - for (const auto& hit : hits) { - mx += hit.position().x; - my += hit.position().y; - mz += hit.position().z; - edep += hit.edep(); - } - - layer.position({mx / layer.nhits(), my / layer.nhits(), mz / layer.nhits()}); - layer.edep(edep); - - double radius = 0.; - for (const auto& hit : hits) { - radius += std::sqrt(pow2(hit.position().x - layer.position().x) + pow2(hit.position().y - layer.position().y) + - pow2(hit.position().z - layer.position().z)); - } - layer.radius(radius / layer.nhits()); - return layer; + // create layers + std::vector<eic::ClusterLayer> cl_layers; + for (const auto& [lid, layer_hit_info] : layer_map) { + auto layer = reconstruct_layer(layer_hit_info, cid, lid); + cl_layers.push_back(layer); + } + return cl_layers; + } + + eic::ClusterLayer reconstruct_layer(const std::vector<std::pair<eic::ConstProtoCluster, eic::ConstCalorimeterHit>>& hit_info, + const int cid, const int lid) const { + // use full members initialization here so it could catch changes in ecid + eic::ClusterLayer layer{-1, cid, lid, static_cast<int>(hit_info.size()), 0, 0., 0., 0., 0., {}}; + + // mean position and total energy + eic::VectorXYZ pos; + double energy = 0.; + for (const auto& [proto, hit] : hit_info) { + pos = pos.add(hit.position()); + energy += hit.energy() * proto.weight(); } - eic::ImagingCluster reconstruct_cluster(const std::vector<eic::ConstImagingPixel>& hits, - const int cid) const - { - eic::ImagingCluster cluster; - cluster.clusterID(cid); - // eta, phi center, weighted by energy - double meta = 0.; - double mphi = 0.; - double edep = 0.; - float r = 9999 * cm; - for (const auto& hit : hits) { - meta += hit.eta() * hit.edep(); - mphi += hit.polar().phi * hit.edep(); - edep += hit.edep(); - r = std::min(hit.polar().r, r); - } - const double eta = meta / edep; - const double phi = mphi / edep; - const double theta = 2. * std::atan(std::exp(-eta)); - cluster.nhits(hits.size()); - cluster.edep(edep); - cluster.energy(edep / m_sampFrac); // simple energy reconstruction - cluster.eta(eta); - cluster.polar({r, phi, theta}); - // cartesian coordinates - ROOT::Math::Polar3DVectorD polar{r, theta, (phi > M_PI ? phi - M_PI : phi)}; - cluster.position({polar.x(), polar.y(),polar.z()}); - - // shower radius estimate (eta-phi plane) - double radius = 0.; - for (auto hit : hits) { - radius += std::sqrt(pow2(hit.eta() - cluster.eta()) + pow2(hit.polar().phi - cluster.polar().phi)); - } - cluster.radius(radius / cluster.nhits()); + pos = pos.scale(1 / layer.nhits()); + layer.energy(energy); - return cluster; + double radius = 0.; + for (const auto& [proto, hit] : hit_info) { + radius += std::sqrt(pow2(hit.position().x - layer.position().x) + pow2(hit.position().y - layer.position().y) + + pow2(hit.position().z - layer.position().z)); } - - std::pair<double /* theta */, double /* phi */> - fit_track(const std::vector<eic::ImagingLayer>& layers, const int stop_layer) const - { - int nrows = 0; - double mx = 0.; - double my = 0.; - double mz = 0.; - for (const auto& layer : layers) { - if ((layer.layerID() <= stop_layer) && (layer.nhits() > 0)) { - mx += layer.position().x; - my += layer.position().y; - mz += layer.position().z; - nrows += 1; - } - } - // cannot fit - if (nrows < 2) { - return {}; + layer.radius(radius / layer.nhits()); + return layer; + } + + eic::Cluster + reconstruct_cluster(const std::vector<std::pair<eic::ConstProtoCluster, eic::ConstCalorimeterHit>>& hit_info, + const int cid) const { + eic::Cluster cluster; + cluster.ID(cid); + // eta, phi center, weighted by energy + double meta = 0.; + double mphi = 0.; + double energy = 0.; + float r = 9999 * cm; + for (const auto& [proto, hit] : hit_info) { + meta += hit.position().eta() * hit.energy() * proto.weight(); + mphi += hit.position().phi() * hit.energy() * proto.weight(); + energy += hit.energy() * proto.weight(); + r = std::min(hit.position().r(), r); + } + const double eta = meta / energy; + const double phi = mphi / energy; + const double theta = 2. * std::atan(std::exp(-eta)); + cluster.nhits(hit_info.size()); + cluster.energy(energy / m_sampFrac); // simple energy reconstruction //DEPRECATED + eic::VectorPolar polar{r, theta, phi > M_PI ? phi - M_PI : phi}; + cluster.position(polar); + + // shower radius estimate (eta-phi plane) + double radius = 0.; + for (const auto& [proto, hit] : hit_info) { + radius += std::sqrt(pow2(hit.position().eta() - cluster.position().eta()) + + pow2(hit.position().phi() - cluster.position().phi())); + } + cluster.radius(radius / cluster.nhits()); + + return cluster; + } + + eic::Direction fit_track(const std::vector<eic::ClusterLayer>& layers, const int stop_layer) const { + int nrows = 0; + double mx = 0.; + double my = 0.; + double mz = 0.; + for (const auto& layer : layers) { + if ((layer.layer() <= stop_layer) && (layer.nhits() > 0)) { + mx += layer.position().x; + my += layer.position().y; + mz += layer.position().z; + nrows += 1; } + } + // cannot fit + if (nrows < 2) { + return {}; + } - mx /= nrows; - my /= nrows; - mz /= nrows; - // fill position data - MatrixXd pos(nrows, 3); - int ir = 0; - for (const auto& layer : layers) { - if ((layer.layerID() <= stop_layer) && (layer.nhits() > 0)) { - pos(ir, 0) = layer.position().x - mx; - pos(ir, 1) = layer.position().y - my; - pos(ir, 2) = layer.position().z - mz; - ir += 1; - } + mx /= nrows; + my /= nrows; + mz /= nrows; + // fill position data + MatrixXd pos(nrows, 3); + int ir = 0; + for (const auto& layer : layers) { + if ((layer.layer() <= stop_layer) && (layer.nhits() > 0)) { + pos(ir, 0) = layer.position().x - mx; + pos(ir, 1) = layer.position().y - my; + pos(ir, 2) = layer.position().z - mz; + ir += 1; } - - JacobiSVD<MatrixXd> svd(pos, ComputeThinU | ComputeThinV); - // debug() << pos << endmsg; - // debug() << svd.matrixV() << endmsg; - const auto dir = svd.matrixV().col(0); - // theta and phi - return {std::acos(dir(2)), std::atan2(dir(1), dir(0))}; } - }; - DECLARE_COMPONENT(ImagingClusterReco) + JacobiSVD<MatrixXd> svd(pos, ComputeThinU | ComputeThinV); + // debug() << pos << endmsg; + // debug() << svd.matrixV() << endmsg; + const auto dir = svd.matrixV().col(0); + // theta and phi + return {std::acos(dir(2)), std::atan2(dir(1), dir(0))}; + } +}; + +DECLARE_COMPONENT(ImagingClusterReco) } // namespace Jug::Reco diff --git a/JugReco/src/components/ImagingPixelMerger.cpp b/JugReco/src/components/ImagingPixelMerger.cpp index e616ea87c448b9cc6a64afbae3bcefddef47912b..a971c58fcc73c58a348057ae30f2b947bcc672ad 100644 --- a/JugReco/src/components/ImagingPixelMerger.cpp +++ b/JugReco/src/components/ImagingPixelMerger.cpp @@ -29,8 +29,10 @@ #include "JugBase/Utilities/Utils.hpp" // Event Model related classes -#include "eicd/ImagingPixel.h" -#include "eicd/ImagingPixelCollection.h" +#include "eicd/VectorPolar.h" +#include "eicd/VectorXYZ.h" +#include "eicd/CalorimeterHit.h" +#include "eicd/CalorimeterHitCollection.h" using namespace Gaudi::Units; @@ -60,9 +62,9 @@ namespace Jug::Reco { Gaudi::Property<int> m_nLayers{this, "numberOfLayers", 20}; Gaudi::Property<double> m_etaSize{this, "etaSize", 0.001}; Gaudi::Property<double> m_phiSize{this, "phiSize", 0.001}; - DataHandle<eic::ImagingPixelCollection> m_inputHitCollection{"inputHitCollection", + DataHandle<eic::CalorimeterHitCollection> m_inputHitCollection{"inputHitCollection", Gaudi::DataHandle::Reader, this}; - DataHandle<eic::ImagingPixelCollection> m_outputHitCollection{"outputHitCollection", + DataHandle<eic::CalorimeterHitCollection> m_outputHitCollection{"outputHitCollection", Gaudi::DataHandle::Writer, this}; ImagingPixelMerger(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) @@ -90,7 +92,7 @@ namespace Jug::Reco { // group the hits by layer std::vector<std::unordered_map<std::pair<int, int>, double, pair_hash>> group_hits(m_nLayers); for (const auto& h : hits) { - auto k = h.layerID(); + auto k = h.layer(); if ((int)k >= m_nLayers) { continue; } @@ -105,9 +107,9 @@ namespace Jug::Reco { auto it = layer.find(g); // merge energy if (it != layer.end()) { - it->second += h.edep(); + it->second += h.energy(); } else { - layer[g] = h.edep(); + layer[g] = h.energy(); } } @@ -132,12 +134,13 @@ namespace Jug::Reco { if (k < grids.size()) { grid = grids[k]; } + eic::VectorXYZ pos {eic::VectorPolar(0., 0., grid.phi)}; + // @TODO: This seems incomplete... auto h = mhits.create(); - h.edep(grid.energy); - h.eta(grid.eta); - h.polar({0., 0., grid.phi}); - h.layerID(i); - h.hitID(k); + h.ID(k); + h.layer(i); + h.energy(grid.energy); + h.position(pos); } } diff --git a/JugReco/src/components/ImagingPixelReco.cpp b/JugReco/src/components/ImagingPixelReco.cpp index 90a3de4add3089f2066b44ab975badc7b3c47f83..aa6730ede888f3baeb0d645de2336159163ee5ed 100644 --- a/JugReco/src/components/ImagingPixelReco.cpp +++ b/JugReco/src/components/ImagingPixelReco.cpp @@ -22,7 +22,7 @@ #include "JugBase/IGeoSvc.h" // Event Model related classes -#include "eicd/ImagingPixelCollection.h" +#include "eicd/CalorimeterHitCollection.h" #include "eicd/RawCalorimeterHitCollection.h" using namespace Gaudi::Units; @@ -51,6 +51,8 @@ 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}; + // Calibration! + Gaudi::Property<double> m_sampFrac{this, "samplingFraction", 1.0}; // unitless counterparts for the input parameters double dyRangeADC; @@ -58,8 +60,8 @@ namespace Jug::Reco { // hits containers DataHandle<eic::RawCalorimeterHitCollection> m_inputHitCollection{ "inputHitCollection", Gaudi::DataHandle::Reader, this}; - DataHandle<eic::ImagingPixelCollection> m_outputHitCollection{"outputHitCollection", - Gaudi::DataHandle::Writer, this}; + DataHandle<eic::CalorimeterHitCollection> m_outputHitCollection{"outputHitCollection", + Gaudi::DataHandle::Writer, this}; // Pointer to the geometry service SmartIF<IGeoSvc> m_geoSvc; @@ -115,13 +117,14 @@ namespace Jug::Reco { auto& hits = *m_outputHitCollection.createAndPut(); // energy time reconstruction + int nhits = 0; for (const auto& rh : rawhits) { // did not pass the threshold if ((rh.amplitude() - m_pedMeanADC) < m_thresholdADC * m_pedSigmaADC) { continue; } - double edep = (rh.amplitude() - m_pedMeanADC) / (double)m_capADC * dyRangeADC; // convert ADC -> energy - double time = rh.timeStamp(); // ns + double energy = (rh.amplitude() - m_pedMeanADC) / (double)m_capADC * dyRangeADC / m_sampFrac; // convert ADC -> energy + double time = rh.time() * 1.e-6; // ns auto id = rh.cellID(); int lid = (int)id_dec->get(id, layer_idx); int sid = (int)id_dec->get(id, sector_idx); @@ -132,24 +135,19 @@ namespace Jug::Reco { auto volman = m_geoSvc->detector()->volumeManager(); auto alignment = volman.lookupDetElement(id).nominal(); auto pos = alignment.worldToLocal(dd4hep::Position(gpos.x(), gpos.y(), gpos.z())); - // polar coordinates - double r = std::sqrt(gpos.x() * gpos.x() + gpos.y() * gpos.y() + gpos.z() * gpos.z()); - double th = std::acos(gpos.z() / r); - double eta = -std::log(std::tan(th / 2.)); - double phi = std::atan2(gpos.y(), gpos.x()); - - hits.push_back(eic::ImagingPixel{ - -1, + + hits.push_back(eic::CalorimeterHit{ + id, + nhits++, lid, sid, - -1, // cluster id, layer id, sector id, hit id - edep, - time, - eta, // edep, time, pseudo-rapidity + 0, + static_cast<float>(energy), + 0, + static_cast<float>(time), {pos.x() / m_lUnit, pos.y() / m_lUnit, pos.z() / m_lUnit}, // local pos {gpos.x() / m_lUnit, gpos.y() / m_lUnit, gpos.z() / m_lUnit}, // global pos - {r, th, phi} // polar global pos - }); + {0, 0, 0}}); // @TODO: add dimension } return StatusCode::SUCCESS; } diff --git a/JugReco/src/components/ImagingTopoCluster.cpp b/JugReco/src/components/ImagingTopoCluster.cpp index 916f26030dfcba65f0bb197ed9838ba9bbce3f34..4c70715b04c5591deef29164676c6934478a196f 100644 --- a/JugReco/src/components/ImagingTopoCluster.cpp +++ b/JugReco/src/components/ImagingTopoCluster.cpp @@ -27,8 +27,8 @@ #include "JugBase/IGeoSvc.h" // Event Model related classes -#include "eicd/ImagingClusterCollection.h" -#include "eicd/ImagingPixelCollection.h" +#include "eicd/ProtoClusterCollection.h" +#include "eicd/CalorimeterHitCollection.h" using namespace Gaudi::Units; @@ -64,11 +64,11 @@ namespace Jug::Reco { // minimum number of hits (to save this cluster) Gaudi::Property<int> m_minClusterNhits{this, "minClusterNhits", 10}; // input hits collection - DataHandle<eic::ImagingPixelCollection> m_inputHitCollection{"inputHitCollection", - Gaudi::DataHandle::Reader, this}; + DataHandle<eic::CalorimeterHitCollection> m_inputHitCollection{"inputHitCollection", + Gaudi::DataHandle::Reader, this}; // output clustered hits - DataHandle<eic::ImagingPixelCollection> m_outputHitCollection{"outputHitCollection", - Gaudi::DataHandle::Writer, this}; + DataHandle<eic::ProtoClusterCollection> m_outputProtoClusterCollection{"outputProtoClusterCollection", + Gaudi::DataHandle::Writer, this}; // unitless counterparts of the input parameters double localDistXY[2], layerDistEtaPhi[2], sectorDist; @@ -77,7 +77,7 @@ namespace Jug::Reco { ImagingTopoCluster(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) { declareProperty("inputHitCollection", m_inputHitCollection, ""); - declareProperty("outputHitCollection", m_outputHitCollection, ""); + declareProperty("outputProtoClusterCollection", m_outputProtoClusterCollection, ""); } StatusCode initialize() override @@ -126,11 +126,11 @@ namespace Jug::Reco { // input collections const auto& hits = *m_inputHitCollection.get(); // Create output collections - auto& clustered_hits = *m_outputHitCollection.createAndPut(); + auto& proto = *m_outputProtoClusterCollection.createAndPut(); // group neighboring hits std::vector<bool> visits(hits.size(), false); - std::vector<std::vector<eic::ImagingPixel>> groups; + std::vector<std::vector<eic::ConstCalorimeterHit>> groups; for (size_t i = 0; i < hits.size(); ++i) { /* debug() << fmt::format("hit {:d}: local position = ({}, {}, {}), global position = ({}, {}, {})", @@ -140,7 +140,7 @@ namespace Jug::Reco { << endmsg; */ // already in a group, or not energetic enough to form a cluster - if (visits[i] || hits[i].edep() < minClusterCenterEdep) { + if (visits[i] || hits[i].energy() < minClusterCenterEdep) { continue; } groups.emplace_back(); @@ -160,16 +160,15 @@ namespace Jug::Reco { if (static_cast<int>(group.size()) < m_minClusterNhits.value()) { continue; } - double edep = 0.; + double energy = 0.; for (const auto& hit : group) { - edep += hit.edep(); + energy += hit.energy(); } - if (edep < minClusterEdep) { + if (energy < minClusterEdep) { continue; } - for (auto hit : group) { - hit.clusterID(clusterID); - clustered_hits.push_back(hit); + for (const auto& hit : group) { + proto.create(hit.ID(), clusterID, 1.); } clusterID += 1; } @@ -185,23 +184,23 @@ namespace Jug::Reco { } // helper function to group hits - bool is_neighbor(const eic::ConstImagingPixel& h1, const eic::ConstImagingPixel& h2) const + bool is_neighbor(const eic::ConstCalorimeterHit& h1, const eic::ConstCalorimeterHit& h2) const { // different sectors, simple distance check - if (h1.sectorID() != h2.sectorID()) { + if (h1.sector() != h2.sector()) { return std::sqrt(pow2(h1.position().x - h2.position().x) + pow2(h1.position().y - h2.position().y) + pow2(h1.position().z - h2.position().z)) <= sectorDist; } // layer check - int ldiff = std::abs(h1.layerID() - h2.layerID()); + int ldiff = std::abs(h1.layer() - h2.layer()); // same layer, check local positions if (!ldiff) { 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.polar().phi - h2.polar().phi) <= layerDistEtaPhi[1]); + return (std::abs(h1.position().eta() - h2.position().eta()) <= layerDistEtaPhi[0]) && + (std::abs(h1.position().phi() - h2.position().phi()) <= layerDistEtaPhi[1]); } // not in adjacent layers @@ -209,24 +208,20 @@ namespace Jug::Reco { } // grouping function with Depth-First Search - void dfs_group(std::vector<eic::ImagingPixel>& group, int idx, - const eic::ImagingPixelCollection& hits, std::vector<bool>& visits) const + void dfs_group(std::vector<eic::ConstCalorimeterHit>& group, int idx, + const eic::CalorimeterHitCollection& hits, std::vector<bool>& visits) const { // not a qualified hit to participate in clustering, stop here - if (hits[idx].edep() < minClusterHitEdep) { + if (hits[idx].energy() < 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(), - hits[idx].polar()}; - group.push_back(hit); + group.push_back(hits[idx]); visits[idx] = true; for (size_t i = 0; i < hits.size(); ++i) { // visited, or not a neighbor - if (visits[i] || !is_neighbor(hit, hits[i])) { + if (visits[i] || !is_neighbor(hits[idx], hits[i])) { continue; } dfs_group(group, i, hits, visits); diff --git a/JugReco/src/components/SimpleClustering.cpp b/JugReco/src/components/SimpleClustering.cpp index 8863a9357012b460b460da36ff2e320e0bb85cb8..c20ec353765b0d9ec255d6c07a8f2ea38e16be89 100644 --- a/JugReco/src/components/SimpleClustering.cpp +++ b/JugReco/src/components/SimpleClustering.cpp @@ -19,6 +19,7 @@ // Event Model related classes #include "eicd/CalorimeterHitCollection.h" #include "eicd/ClusterCollection.h" +#include "eicd/ProtoClusterCollection.h" #include "eicd/RawCalorimeterHitCollection.h" using namespace Gaudi::Units; @@ -32,12 +33,15 @@ namespace Jug::Reco { class SimpleClustering : public GaudiAlgorithm { public: using RecHits = eic::CalorimeterHitCollection; + using ProtoClusters = eic::ProtoClusterCollection; using Clusters = eic::ClusterCollection; - DataHandle<RecHits> m_inputHitCollection{"inputHitCollection", Gaudi::DataHandle::Reader, this}; - DataHandle<Clusters> m_outputClusters{"outputClusters", Gaudi::DataHandle::Writer, this}; - Gaudi::Property<double> m_minModuleEdep{this, "minModuleEdep", 5.0 * MeV}; - Gaudi::Property<double> m_maxDistance{this, "maxDistance", 20.0 * cm}; + DataHandle<RecHits> m_inputHitCollection{"inputHitCollection", Gaudi::DataHandle::Reader, this}; + DataHandle<ProtoClusters> m_outputProtoClusters{"outputProtoCluster", Gaudi::DataHandle::Writer, this}; + DataHandle<Clusters> m_outputClusters{"outputClusterCollection", Gaudi::DataHandle::Writer, this}; + + Gaudi::Property<double> m_minModuleEdep{this, "minModuleEdep", 5.0 * MeV}; + Gaudi::Property<double> m_maxDistance{this, "maxDistance", 20.0 * cm}; /// Pointer to the geometry service SmartIF<IGeoSvc> m_geoSvc; @@ -45,7 +49,8 @@ namespace Jug::Reco { SimpleClustering(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) { declareProperty("inputHitCollection", m_inputHitCollection, ""); - declareProperty("outputClusters", m_outputClusters, "Output clusters"); + declareProperty("outputProtoClusterCollection", m_outputClusters, "Output proto clusters"); + declareProperty("outputClusterCollection", m_outputClusters, "Output clusters"); } StatusCode initialize() override @@ -67,19 +72,19 @@ namespace Jug::Reco { // input collections const auto& hits = *m_inputHitCollection.get(); // Create output collections + auto& proto = *m_outputProtoClusters.createAndPut(); auto& clusters = *m_outputClusters.createAndPut(); - std::vector<eic::CalorimeterHit> the_hits; - std::vector<eic::CalorimeterHit> remaining_hits; + std::vector<eic::ConstCalorimeterHit> the_hits; + std::vector<eic::ConstCalorimeterHit> remaining_hits; double max_dist = m_maxDistance.value() / mm; double min_energy = m_minModuleEdep.value() / GeV; - eic::CalorimeterHit ref_hit; + eic::ConstCalorimeterHit ref_hit; bool have_ref = false; - for (const auto& ch : hits) { - const eic::CalorimeterHit h{ch.cellID(), ch.clusterID(), ch.layerID(), ch.sectorID(), ch.energy(), - ch.time(), ch.position(), ch.local(), ch.dimension(), 1}; + for (const auto& h : hits) { + //const eic::CalorimeterHit h = ch.clone(); if (!have_ref || h.energy() > ref_hit.energy()) { ref_hit = h; have_ref = true; @@ -91,31 +96,31 @@ namespace Jug::Reco { while (have_ref && ref_hit.energy() > min_energy) { - std::vector<eic::CalorimeterHit> cluster_hits; + std::vector<eic::ConstCalorimeterHit> cluster_hits; for (const auto& h : the_hits) { if (std::hypot(h.position().x - ref_hit.position().x, h.position().y - ref_hit.position().y, h.position().z - ref_hit.position().z) < max_dist) { cluster_hits.push_back(h); } else { - remaining_hits.push_back(h); - } + remaining_hits.push_back(h); } } debug() << " cluster size " << cluster_hits.size() << endmsg; double total_energy = std::accumulate(std::begin(cluster_hits), std::end(cluster_hits), 0.0, - [](double t, const eic::CalorimeterHit& h1) { return (t + h1.energy()); }); + [](double t, const eic::ConstCalorimeterHit& h1) { return (t + h1.energy()); }); debug() << " total_energy = " << total_energy << endmsg; - eic::Cluster cluster0; + eic::Cluster cl; + cl.ID(clusters.size()); + cl.nhits(cluster_hits.size()); for (const auto& h : cluster_hits) { - cluster0.energy(cluster0.energy() + h.energy()); - cluster0.position({cluster0.position().x + h.energy() * h.position().x / total_energy, - cluster0.position().y + h.energy() * h.position().y / total_energy, - cluster0.position().z + h.energy() * h.position().z / total_energy}); + cl.energy(cl.energy() + h.energy()); + cl.position(cl.position().add(h.position().scale(h.energy()/total_energy))); + proto.create(h.ID(), cl.ID()); } - clusters.push_back(cluster0); + clusters.push_back(cl); have_ref = false; if ((remaining_hits.size() > 5) && (clusters.size() < 10)) { diff --git a/JugReco/src/components/TopologicalCellCluster.cpp b/JugReco/src/components/TopologicalCellCluster.cpp index fc858af64e5c9cda6b22592b6eaf001842c0f3d5..da8f1e96af9d2c43e24aa9a6d04cd8558cddf2bd 100644 --- a/JugReco/src/components/TopologicalCellCluster.cpp +++ b/JugReco/src/components/TopologicalCellCluster.cpp @@ -8,6 +8,9 @@ * References: https://arxiv.org/pdf/1603.02934.pdf * */ + +// @FIXME: Deprecated? + #include "fmt/format.h" #include <algorithm> @@ -64,8 +67,8 @@ namespace Jug::Reco { // maximum global distance to be considered as neighbors in different sectors Gaudi::Property<double> m_adjSectorDist{this, "adjSectorDist", 1.0 * cm}; // minimum cluster center energy (to be considered as a seed for cluster) - // @TODO One can not simply find a center by edep with extremely fine granularity. - // Projecting to (eta, phi) with crude pixel size may help determine the center edep, which can + // @TODO One can not simply find a center by energy with extremely fine granularity. + // Projecting to (eta, phi) with crude pixel size may help determine the center energy, which can // happen in the following reconstruction step Gaudi::Property<double> m_minClusterCenterEdep{this, "minClusterCenterEdep", 50 * keV}; // input hits collection @@ -154,7 +157,7 @@ namespace Jug::Reco { // group neighboring hits std::vector<bool> visits(hits.size(), false); - std::vector<std::vector<eic::CalorimeterHit>> groups; + std::vector<std::vector<eic::ConstCalorimeterHit>> 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].energy() < m_minClusterCenterEdep) { @@ -222,19 +225,14 @@ namespace Jug::Reco { } // grouping function with Depth-First Search - void dfs_group(std::vector<eic::CalorimeterHit>& group, int idx, + void dfs_group(std::vector<eic::ConstCalorimeterHit>& group, int idx, const eic::CalorimeterHitCollection& hits, std::vector<bool>& visits) const { - const eic::CalorimeterHit hit{hits[idx].cellID(), hits[idx].clusterID(), - hits[idx].layerID(), hits[idx].sectorID(), - hits[idx].energy(), hits[idx].time(), - hits[idx].position(), hits[idx].local(), - hits[idx].dimension(), 1}; - group.push_back(hit); + group.push_back(hits[idx]); visits[idx] = true; for (size_t i = 0; i < hits.size(); ++i) { // visited, or not a neighbor - if (visits[i] || !is_neighbor(hit, hits[i])) { + if (visits[i] || !is_neighbor(hits[idx], hits[i])) { continue; } dfs_group(group, i, hits, visits); diff --git a/JugTrack/src/components/TrackParamImagingClusterInit.cpp b/JugTrack/src/components/TrackParamImagingClusterInit.cpp index a0eb8d5c81b1a9604f144a110467f6be677831b8..d893cde9754b979968e1ff60142818e6bf28b549 100644 --- a/JugTrack/src/components/TrackParamImagingClusterInit.cpp +++ b/JugTrack/src/components/TrackParamImagingClusterInit.cpp @@ -14,7 +14,7 @@ #include "Acts/Definitions/Common.hpp" #include "eicd/TrackerHitCollection.h" -#include "eicd/ImagingClusterCollection.h" +#include "eicd/ClusterCollection.h" #include "Math/Vector3D.h" #include "Acts/Surfaces/PerigeeSurface.hpp" @@ -41,7 +41,7 @@ namespace Jug::Reco { */ class TrackParamImagingClusterInit : public GaudiAlgorithm { public: - using ImagingClusters = eic::ImagingClusterCollection; + using ImagingClusters = eic::ClusterCollection; DataHandle<ImagingClusters> m_inputClusters{"inputClusters", Gaudi::DataHandle::Reader, this}; DataHandle<TrackParametersContainer> m_outputInitialTrackParameters{"outputInitialTrackParameters", @@ -65,7 +65,7 @@ namespace Jug::Reco { StatusCode execute() override { // input collection - const eic::ImagingClusterCollection* clusters = m_inputClusters.get(); + const eic::ClusterCollection* clusters = m_inputClusters.get(); // Create output collections auto init_trk_params = m_outputInitialTrackParameters.createAndPut(); @@ -77,10 +77,11 @@ namespace Jug::Reco { using Acts::UnitConstants::ns; double p = c.energy()*GeV; + // FIXME hardcoded value if( p < 0.1*GeV) { continue; } - double len = std::hypot( c.position().x , c.position().y , c.position().z ); + double len = c.position().mag(); ROOT::Math::XYZVector momentum(c.position().x * p / len, c.position().y * p / len, c.position().z * p / len); // build some track cov matrix