Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable calorimeter hit merging by functions #1668

Merged
merged 38 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
73dd28e
Add merge matrix to configuration + initialization
ruse-traveler Nov 5, 2024
edf5174
Add hit merger to BHCal plugin
ruse-traveler Nov 5, 2024
9d145b5
Fill in merge-map-building using provided mappings
ruse-traveler Nov 5, 2024
54aa6b0
Add test case in the BHCal (merging towers-into-tiles)
ruse-traveler Nov 5, 2024
4e9f901
Fix compile time errors
ruse-traveler Nov 6, 2024
0155a7b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 6, 2024
903fe3e
Add debugging statements
ruse-traveler Nov 6, 2024
b650a62
Merge branch 'add-merge-matrix-to-hit-merger' of github.com:eic/EICre…
ruse-traveler Nov 6, 2024
19079b5
Fix seg fault by tying mappings to fields
ruse-traveler Nov 6, 2024
2930d2c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 6, 2024
6586f07
Implement expression workaround
ruse-traveler Nov 7, 2024
94b4395
Merge branch 'add-merge-matrix-to-hit-merger' of github.com:eic/EICre…
ruse-traveler Nov 7, 2024
b346c13
Add helper methods to consistently make phi mapping + adjacency matrix
ruse-traveler Nov 13, 2024
7bf1b8c
Merge branch 'main' of github.com:eic/EICrecon into add-merge-matrix-…
ruse-traveler Nov 14, 2024
a6d8617
Finish wiring in merged hits
ruse-traveler Nov 14, 2024
eba0c58
Clean up WIP comments
ruse-traveler Nov 14, 2024
174e38f
Remove spurious line break
ruse-traveler Nov 14, 2024
ec9c5ec
Merge branch 'main' into add-merge-matrix-to-hit-merger
ruse-traveler Dec 19, 2024
3b6e445
Move mapping/matrix makers upstream
ruse-traveler Dec 19, 2024
b4511b4
Make parameter names more descriptive
ruse-traveler Dec 30, 2024
6976ee2
Fix missed field renames
ruse-traveler Dec 30, 2024
6af0992
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 30, 2024
0730282
Store both reference masks and transformations in ref_map
ruse-traveler Dec 30, 2024
e8b4b94
Merge upstream changes
ruse-traveler Dec 30, 2024
34f194b
Fix compiler errors
ruse-traveler Dec 30, 2024
6c0bc83
Propagate merge map simplification to rest of algorithm
ruse-traveler Dec 30, 2024
7735e6d
Fix typo
ruse-traveler Dec 30, 2024
73202d4
Fix more typos
ruse-traveler Dec 30, 2024
faf20b1
Propagate changes to plugins
ruse-traveler Dec 30, 2024
2130713
Remove field refs
ruse-traveler Jan 7, 2025
6d650ee
Merge branch 'main' into add-merge-matrix-to-hit-merger
ruse-traveler Jan 7, 2025
75bb4c5
Merge fields and transformation fields
ruse-traveler Jan 7, 2025
07af3db
Update BHCAL plugin
ruse-traveler Jan 7, 2025
01e99e3
Fix typo: slices not layers
ruse-traveler Jan 8, 2025
ae5d8aa
Update copyright dates
ruse-traveler Jan 8, 2025
2a1a8de
Update src/algorithms/calorimetry/CalorimeterHitsMerger.h
veprbl Jan 8, 2025
c7a0285
Update src/detectors/BHCAL/BHCAL.cc
veprbl Jan 8, 2025
ebc3547
IWYU
veprbl Jan 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 98 additions & 20 deletions src/algorithms/calorimetry/CalorimeterHitsMerger.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2022 Chao Peng, Jihee Kim, Sylvester Joosten, Whitney Armstrong, Wouter Deconinck, David Lawrence
// Copyright (C) 2022 Chao Peng, Jihee Kim, Sylvester Joosten, Whitney Armstrong, Wouter Deconinck, David Lawrence, Derek Anderson
ruse-traveler marked this conversation as resolved.
Show resolved Hide resolved

/*
* An algorithm to group readout hits from a calorimeter
Expand All @@ -12,16 +12,17 @@

#include <DD4hep/Alignments.h>
#include <DD4hep/DetElement.h>
#include <DD4hep/IDDescriptor.h>
#include <DD4hep/Objects.h>
#include <DD4hep/Readout.h>
#include <DD4hep/VolumeManager.h>
#include <DDSegmentation/BitFieldCoder.h>
#include <edm4eic/CalorimeterHit.h>
#include <Evaluator/DD4hepUnits.h>
#include <Math/GenVector/Cartesian3D.h>
#include <Math/GenVector/DisplacementVector3D.h>
#include <fmt/core.h>
#include <algorithm>
#include <algorithms/service.h>
#include <cmath>
#include <cstddef>
#include <gsl/pointers>
Expand All @@ -31,6 +32,7 @@
#include <vector>

#include "algorithms/calorimetry/CalorimeterHitsMergerConfig.h"
#include "services/evaluator/EvaluatorSvc.h"

namespace eicrecon {

Expand All @@ -41,24 +43,57 @@ void CalorimeterHitsMerger::init() {
return;
}

// initialize descriptor + decoders
try {
auto id_desc = m_detector->readout(m_cfg.readout).idSpec();
id_mask = 0;
std::vector<std::pair<std::string, int>> ref_fields;
for (size_t i = 0; i < m_cfg.fields.size(); ++i) {
id_mask |= id_desc.field(m_cfg.fields[i])->mask();
// use the provided id number to find ref cell, or use 0
int ref = i < m_cfg.refs.size() ? m_cfg.refs[i] : 0;
ref_fields.emplace_back(m_cfg.fields[i], ref);
}
ref_mask = id_desc.encode(ref_fields);
id_desc = m_detector->readout(m_cfg.readout).idSpec();
id_decoder = id_desc.decoder();
for (const auto& field : m_cfg.fields) {
const short index = id_decoder->index(field);
}
} catch (...) {
auto mess = fmt::format("Failed to load ID decoder for {}", m_cfg.readout);
warning(mess);
auto mess = fmt::format("Failed to load ID decoder for {}", m_cfg.readout);
warning(mess);
// throw std::runtime_error(mess);
}
id_mask = ~id_mask;
debug("ID mask in {:s}: {:#064b}", m_cfg.readout, id_mask);

// if no field-by-field mappings provided, initialize relevant bitmasks
// otherwise intialize relevant functionals
if (m_cfg.mappings.empty()) {
id_mask = 0;
std::vector<RefField> ref_fields;
for (size_t i = 0; i < m_cfg.fields.size(); ++i) {
id_mask |= id_desc.field(m_cfg.fields[i])->mask();
// use the provided id number to find ref cell, or use 0
int ref = i < m_cfg.refs.size() ? m_cfg.refs[i] : 0;
ref_fields.emplace_back(m_cfg.fields[i], ref);
}
ref_mask = id_desc.encode(ref_fields);
id_mask = ~id_mask;
debug("ID mask in {:s}: {:#064b}", m_cfg.readout, id_mask);
ruse-traveler marked this conversation as resolved.
Show resolved Hide resolved
} else {

// lambda to translate IDDescriptor fields into function parameters
std::function hit_to_map = [this](const edm4eic::CalorimeterHit& hit) {
std::unordered_map<std::string, double> params;
for (const auto& name_field : id_desc.fields()) {
params.emplace(name_field.first, name_field.second->value(hit.getCellID()));
trace("{} = {}", name_field.first, name_field.second->value(hit.getCellID()));
}
return params;
};

// intialize functions
// - NOTE this assumes provided fields are 1-to-1!
auto& svc = algorithms::ServiceSvc::instance();
for (std::size_t iMap = 0; const auto& mapping : m_cfg.mappings) {
if (iMap < m_cfg.fields.size()) {
ruse-traveler marked this conversation as resolved.
Show resolved Hide resolved
ref_maps[m_cfg.fields.at(iMap)] = svc.service<EvaluatorSvc>("EvaluatorSvc")->compile(mapping, hit_to_map);
trace("Mapping for field {} = {}", m_cfg.fields.at(iMap), mapping);
}
++iMap;
} // end loop over mappings
}

}

void CalorimeterHitsMerger::process(
Expand All @@ -69,14 +104,17 @@ void CalorimeterHitsMerger::process(
auto [out_hits] = output;

// find the hits that belong to the same group (for merging)
std::unordered_map<uint64_t, std::vector<std::size_t>> merge_map;
std::size_t ix = 0;
for (const auto &h : *in_hits) {
MergeMap merge_map;
if (m_cfg.mappings.empty()) {
for (std::size_t ix = 0; const auto &h : *in_hits) {
uint64_t id = h.getCellID() & id_mask;
merge_map[id].push_back(ix);

ix++;
}
} else {
build_map_via_funcs(in_hits,merge_map);
}
debug("Merge map built: merging {} hits into {} merged hits", in_hits->size(), merge_map.size());

// sort hits by energy from large to small
for (auto &it : merge_map) {
Expand Down Expand Up @@ -140,4 +178,44 @@ void CalorimeterHitsMerger::process(
debug("Size before = {}, after = {}", in_hits->size(), out_hits->size());
}

void CalorimeterHitsMerger::build_map_via_funcs(
const edm4eic::CalorimeterHitCollection* in_hits,
MergeMap& merge_map
) const {

std::vector<RefField> ref_fields;
for (std::size_t iHit = 0; const auto& hit : *in_hits) {

// make sure vector is clear
ref_fields.clear();
for (std::size_t iField = 0; const auto& name_field : id_desc.fields()) {

// check if field has associated mapping
const bool foundMapping = (
ruse-traveler marked this conversation as resolved.
Show resolved Hide resolved
std::find(m_cfg.fields.begin(), m_cfg.fields.end(), name_field.first) != m_cfg.fields.end()
);

// if mapping provided for field, apply it
// otherwise just copy index
if (foundMapping) {
ref_fields.push_back(
{name_field.first, ref_maps[name_field.first](hit)}
);
} else {
ref_fields.push_back(
{name_field.first, id_decoder->get(hit.getCellID(), name_field.first)}
);
}
++iField;
}
const uint64_t ref_id = id_desc.encode(ref_fields);

// add hit to appropriate group
merge_map[ref_id].push_back(iHit);
++iHit;

} // end hit loop

} // end 'build_map_via_funcs(edm4eic::CalorimeterHitsCollection*, MergeMap&)'

} // namespace eicrecon
21 changes: 20 additions & 1 deletion src/algorithms/calorimetry/CalorimeterHitsMerger.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2022 Chao Peng, Jihee Kim, Sylvester Joosten, Whitney Armstrong, Wouter Deconinck, David Lawrence
// Copyright (C) 2022 Chao Peng, Jihee Kim, Sylvester Joosten, Whitney Armstrong, Wouter Deconinck, David Lawrence, Derek Anderson
veprbl marked this conversation as resolved.
Show resolved Hide resolved

/*
* An algorithm to group readout hits from a calorimeter
Expand All @@ -10,21 +10,32 @@

#pragma once

#include <DD4hep/BitFieldCoder.h>
#include <DD4hep/Detector.h>
#include <DD4hep/IDDescriptor.h>
#include <DDRec/CellIDPositionConverter.h>
#include <algorithms/algorithm.h>
#include <algorithms/geo.h>
#include <edm4eic/CalorimeterHitCollection.h>
#include <stdint.h>
#include <functional>
#include <gsl/pointers>
#include <map>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

#include "CalorimeterHitsMergerConfig.h"
#include "algorithms/interfaces/WithPodConfig.h"

namespace eicrecon {

// aliases for convenience
using MergeMap = std::unordered_map<uint64_t, std::vector<std::size_t>>;
using RefField = std::pair<std::string, int>;
using MapFunc = std::function<int(const edm4eic::CalorimeterHit&)>;

using CalorimeterHitsMergerAlgorithm = algorithms::Algorithm<
algorithms::Input<
edm4eic::CalorimeterHitCollection
Expand All @@ -51,10 +62,18 @@ namespace eicrecon {
private:
uint64_t id_mask{0}, ref_mask{0};

private:
mutable std::map<std::string, MapFunc> ref_maps;
dd4hep::IDDescriptor id_desc;
dd4hep::BitFieldCoder* id_decoder;

private:
const dd4hep::Detector* m_detector{algorithms::GeoSvc::instance().detector()};
const dd4hep::rec::CellIDPositionConverter* m_converter{algorithms::GeoSvc::instance().cellIDPositionConverter()};

private:
void build_map_via_funcs(const edm4eic::CalorimeterHitCollection* in_hits, MergeMap& merge_map) const;

};

} // namespace eicrecon
1 change: 1 addition & 0 deletions src/algorithms/calorimetry/CalorimeterHitsMergerConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace eicrecon {
std::string readout{""};
std::vector<std::string> fields{};
std::vector<int> refs{};
std::vector<std::string> mappings{};

};

Expand Down
92 changes: 80 additions & 12 deletions src/detectors/BHCAL/BHCAL.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,66 @@
#include "factories/calorimetry/CalorimeterClusterRecoCoG_factory.h"
#include "factories/calorimetry/CalorimeterHitDigi_factory.h"
#include "factories/calorimetry/CalorimeterHitReco_factory.h"
#include "factories/calorimetry/CalorimeterHitsMerger_factory.h"
#include "factories/calorimetry/CalorimeterIslandCluster_factory.h"
#include "factories/calorimetry/CalorimeterTruthClustering_factory.h"
#include "factories/calorimetry/TrackClusterMergeSplitter_factory.h"

namespace eicrecon {

// --------------------------------------------------------------------------
// Helper method to generate appropriate phi map
// --------------------------------------------------------------------------
std::string MakePhiMap(const std::size_t nMerge = 1) {
ruse-traveler marked this conversation as resolved.
Show resolved Hide resolved

// convert num to merge to string
const std::string sNum = std::to_string(nMerge);

// construct mapping
std::string map = "";
if (nMerge > 1) {
map = "phi-(" + sNum + "*((phi/" + sNum + ")-floor(phi/" + sNum + ")))";
} else {
map = "phi";
}
return map;

} // end 'MakePhiMap(std::size_t)'

// --------------------------------------------------------------------------
// Helper method to generate appropriate adjacency matrix
// --------------------------------------------------------------------------
std::string MakeAdjacencyMatrix(const std::size_t nMerge = 1) {

// set up checks at wraparound.
// magic constants:
// 320 - number of tiles per row
const std::string sMerge = std::to_string(nMerge);
const std::string wrapDef = "(abs(phi_1 - phi_2) == (320 - 1))";
const std::string wrapMerge = "(abs(320 - abs(phi_1 - phi_2)) <= " + sMerge + ")";

// combine strings into horizontal adjacency conditions
const std::string phiAdjacent = "(abs(phi_1 - phi_2) == " + sMerge + ")";
const std::string wrapAdjacent = nMerge > 1 ? wrapMerge : wrapDef;
ruse-traveler marked this conversation as resolved.
Show resolved Hide resolved

// put everything together
std::string matrix("");
matrix += "(";
// check for vertically adjacent tiles
matrix += " ( (abs(eta_1 - eta_2) == 1) && (abs(phi_1 - phi_2) == 0) ) ||";
// check for horizontally adjacent tiles
matrix += " ( (abs(eta_1 - eta_2) == 0) && " + phiAdjacent + " ) ||";
// check for horizontally adjacent tiles at wraparound
matrix += " ( (abs(eta_1 - eta_2) == 0) && " + wrapAdjacent + " )";
matrix += ") == 1";
return matrix;

} // end 'MakeAdjacencyMatrix(std::size_t)'

}



extern "C" {

void InitPlugin(JApplication *app) {
Expand All @@ -31,17 +87,15 @@ extern "C" {
decltype(CalorimeterHitDigiConfig::pedSigmaADC) HcalBarrel_pedSigmaADC = 2;
decltype(CalorimeterHitDigiConfig::resolutionTDC) HcalBarrel_resolutionTDC = 1 * dd4hep::picosecond;

// Set default adjacency matrix. Magic constants:
// 320 - number of tiles per row
decltype(CalorimeterIslandClusterConfig::adjacencyMatrix) HcalBarrel_adjacencyMatrix =
"("
// check for vertically adjacent tiles
" ( (abs(eta_1 - eta_2) == 1) && (abs(phi_1 - phi_2) == 0) ) ||"
// check for horizontally adjacent tiles
" ( (abs(eta_1 - eta_2) == 0) && (abs(phi_1 - phi_2) == 1) ) ||"
// check for horizontally adjacent tiles at wraparound
" ( (abs(eta_1 - eta_2) == 0) && (abs(phi_1 - phi_2) == (320 - 1)) )"
") == 1";
// ---------------------------------------------------------------------
// if needed, merge adjacent tiles into a tower and adjust maps/matrices
// ---------------------------------------------------------------------
const std::size_t nPhiToMerge = 1;
const std::string phiMap = MakePhiMap(nPhiToMerge);
const std::string adjMatrix = MakeAdjacencyMatrix(nPhiToMerge);

// set matrix
decltype(CalorimeterIslandClusterConfig::adjacencyMatrix) HcalBarrel_adjacencyMatrix = adjMatrix;

app->Add(new JOmniFactoryGeneratorT<CalorimeterHitDigi_factory>(
"HcalBarrelRawHits",
Expand All @@ -66,6 +120,7 @@ extern "C" {
},
app // TODO: Remove me once fixed
));

app->Add(new JOmniFactoryGeneratorT<CalorimeterHitReco_factory>(
"HcalBarrelRecHits", {"HcalBarrelRawHits"}, {"HcalBarrelRecHits"},
{
Expand All @@ -83,12 +138,24 @@ extern "C" {
},
app // TODO: Remove me once fixed
));

app->Add(new JOmniFactoryGeneratorT<CalorimeterHitsMerger_factory>(
"HcalBarrelMergedHits", {"HcalBarrelRecHits"}, {"HcalBarrelMergedHits"},
{
.readout = "HcalBarrelHits",
.fields = {"phi"},
.mappings = {phiMap}
Chao1009 marked this conversation as resolved.
Show resolved Hide resolved
},
app // TODO: Remove me once fixed
));

app->Add(new JOmniFactoryGeneratorT<CalorimeterTruthClustering_factory>(
"HcalBarrelTruthProtoClusters", {"HcalBarrelRecHits", "HcalBarrelHits"}, {"HcalBarrelTruthProtoClusters"},
app // TODO: Remove me once fixed
));

app->Add(new JOmniFactoryGeneratorT<CalorimeterIslandCluster_factory>(
"HcalBarrelIslandProtoClusters", {"HcalBarrelRecHits"}, {"HcalBarrelIslandProtoClusters"},
"HcalBarrelIslandProtoClusters", {"HcalBarrelMergedHits"}, {"HcalBarrelIslandProtoClusters"},
veprbl marked this conversation as resolved.
Show resolved Hide resolved
{
.adjacencyMatrix = HcalBarrel_adjacencyMatrix,
.readout = "HcalBarrelHits",
Expand Down Expand Up @@ -179,5 +246,6 @@ extern "C" {
app // TODO: Remove me once fixed
)
);

}
}
1 change: 1 addition & 0 deletions src/factories/calorimetry/CalorimeterHitsMerger_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class CalorimeterHitsMerger_factory : public JOmniFactory<CalorimeterHitsMerger_
ParameterRef<std::string> m_readout {this, "readout", config().readout};
ParameterRef<std::vector<std::string>> m_fields {this, "fields", config().fields};
ParameterRef<std::vector<int>> m_refs {this, "refs", config().refs};
ParameterRef<std::vector<std::string>> m_mappings {this, "mappings", config().mappings};
ruse-traveler marked this conversation as resolved.
Show resolved Hide resolved

Service<AlgorithmsInit_service> m_algorithmsInit {this};

Expand Down
1 change: 1 addition & 0 deletions src/services/io/podio/JEventProcessorPODIO.cc
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ JEventProcessorPODIO::JEventProcessorPODIO() {
"LFHCALSplitMergeClusterAssociations",
"HcalBarrelRawHits",
"HcalBarrelRecHits",
"HcalBarrelMergedHits",
"HcalBarrelClusters",
"HcalBarrelClusterAssociations",
"HcalBarrelSplitMergeClusters",
Expand Down
Loading