-
Notifications
You must be signed in to change notification settings - Fork 29
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
base: main
Are you sure you want to change the base?
Conversation
for more information, see https://pre-commit.ci
…con into add-merge-matrix-to-hit-merger
for more information, see https://pre-commit.ci
Capybara summary for PR 1668
|
…con into add-merge-matrix-to-hit-merger
I think this is ready to go. I'll post some plots here showing the test case in the BHCal at work shortly... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if we should just have only one function that can map any cellID to any cellID, and to that function we'd provide the precomputed bit masks. That would be most general and simple implementation, at the cost of complexitry. E.g. your example could look like: (cellID & ~_phi_mask) | ((((cellID & _phi_mask) >> _phi_mask_shift) / 5) << _phi_mask_shift)
and we could introduce some helper functions to not have to deal with most raw C++. Just throwing this in as an idea.
@@ -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}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mappings sounds a bit generic to me, and it doesn't self-describe that there should be as many mappings as fields. Should we call it something like field transformations or rewrites?
ParameterRef<std::vector<std::string>> m_mappings {this, "mappings", config().mappings}; | |
ParameterRef<std::vector<std::string>> m_field_transformations {this, "fieldTransformations", config().field_transformations}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I also like that name!
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we put mask operations as closures to the same ref_maps, so that the application of transofrmation is uniform?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite follow... So by closure do you mean if a user provides both a bitmask and a mapping, they both get applied?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allow me to clarify. I meant that this code can be replaced with something like
for (size_t ix = 0; std::string &field : m_cfg.fields) {
uint64_t ref = m_cfg.refs.at(ix);
ref_maps[field] = [ref](const edm4eic::CalorimeterHit&) -> int {
return ref;
};
ix++;
}
Then we can remove all the different logic for handling ref_mask and id_mask.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this it's more like:
for (size_t ix = 0; std::string &field : m_cfg.fields) {
uint64_t ref_mask = m_cfg.refs.at(ix);
auto field_element = id_desc.field(m_cfg.fields[i]);
ref_maps[field] = [ref_mask,field_element](const edm4eic::CalorimeterHit& hit) -> int {
return (field_element->value(hit.cellID()) & ~ref_mask) | ref_mask;
};
ix++;
}
for (std::size_t iField = 0; const auto& name_field : id_desc.fields()) { | ||
|
||
// check if field has associated mapping | ||
const bool foundMapping = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks a bit complicated, and I doubt that it actually works for mixing of "mappings" transformations and "refs" bitmasks. Could it be simplified assuming 1-to-1 loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, as-is it's set up so that if a user provides a mapping, it defaults to using that and will ignore any provided bitmasks. But it can definitely be just turned into a 1-to-1 loop where it checks for the mapping or the bitmask.
for (std::size_t iMap = 0; const auto& mapping : m_cfg.mappings) { | ||
if (iMap < m_cfg.fields.size()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also breaks my brain a bit. A check for matching length and a 1-to-1 loop would be nicer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! This was trying to work in the above design choice that providing a map makes the algorithm ignores any provided bitmasks.
Definitely an option! And it might be nice to have that functionality available even if we keep the field-specific mappings... To be frank, I still find bitwise operations a bit mystifying and opaque, which is part of why I shied away from using them here 😅 |
Agreed. |
@Chao1009 Is that not the suggestion from #1668 (comment) ? |
It's similar. The only difference is that I suggest using a single input for the |
I think the current implementation in this PR already allows to provide arbitrary functions of arbitrary other fields to re-define fields. The old merging behavior was to just mask out bits of the original values and set them to all binary 1. |
Briefly, what does this PR introduce?
This PR extends the functionality of the
CalorimeterHitsMerger
algorithm. Previously, reconstructed hits could only be merged across a given field of the readout of a calorimeter. This presents a challenge for calorimeters such as the Barrel HCal whereThis PR addresses this point by utilizing the
EvaluatorSvc
in a manner similar to theadjacencyMatrix
,peakNeighbourhoodMatrix
of theCalorimeterIslandClustering
and thesampFrac
ofCalorimeterHitReco
. Now the user has an option to specify a mapping for a specific field of the readout via themappings
option, which defines a function to map the indices of a field onto the desired reference indices.For example:
The
HcalBarrelMergedHits
collection will merge 5 hits (i.e. scintillator tiles for the BHCal) adjacent in phi into a one with the position and cellID of the 1st of the 5. However, no hits will be merged along eta in this example.If a mapping is provided, the algorithm will construct the map of which hits to merge according the provided functions. If not, it will attempt to merge hits across the provided fields into the indices defined by
refs
.An example script of to change a mapping (and how to update the adjacency matrix accordingly) from the command-line is provided in the snippets repo here.
What kind of change does this PR introduce?
Please check if this PR fulfills the following:
Does this PR introduce breaking changes? What changes might users need to make to their code?
No.
Does this PR change default behavior?
No.