Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: eic/EICrecon
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 20cb0b4f4feed097227e60da5dd94ac26eb10388
Choose a base ref
..
head repository: eic/EICrecon
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 9786e24e3793537f7e57a5f0a86493e3afbf5570
Choose a head ref
33 changes: 27 additions & 6 deletions .github/workflows/linux-eic-shell.yml
Original file line number Diff line number Diff line change
@@ -713,21 +713,28 @@ jobs:
export DETECTOR_CONFIG=${DETECTOR}_${{ matrix.detector_config }}
export LD_LIBRARY_PATH=$PWD/install/lib:$LD_LIBRARY_PATH
export JANA_PLUGIN_PATH=$PWD/install/lib/EICrecon/plugins:/usr/local/plugins
prmon --json-summary rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.prmon.json -- \
$PWD/install/bin/eicrecon $JANA_OPTIONS -Ppodio:output_file=rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root sim_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4hep.root -Pplugins=dump_flags,janadot,janatop $(<${{ github.workspace }}/.github/janadot.groups) -Pdump_flags:json=${{ matrix.particle }}_${{ matrix.detector_config }}_flags.json
- uses: actions/upload-artifact@v4
with:
name: rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root
path: rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root
if-no-files-found: error
- uses: actions/upload-artifact@v4
with:
name: rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.prmon.json
path: rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.prmon.json
if-no-files-found: error
- name: Convert execution graphs
uses: eic/run-cvmfs-osg-eic-shell@main
with:
platform-release: "${{ env.platform }}:${{ env.release }}"
setup: "/opt/detector/epic-${{ env.detector-version }}/bin/thisepic.sh"
run: |
sed '/rank=sink/s/"podio::Frame";//; /podio::Frame/d; /rank=source/s/"JEventProcessorPODIO";//; /JEventProcessorPODIO/d' jana.dot | dot -Tsvg > jana.svg
mv jana.dot rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.dot
sed '/rank=sink/s/"podio::Frame";//; /podio::Frame/d; /rank=source/s/"JEventProcessorPODIO";//; /JEventProcessorPODIO/d' *.dot | dot -Tsvg > jana.svg
mv jana.svg rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.svg
continue-on-error: true
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.particle }}_${{ matrix.detector_config }}_flags.json
@@ -759,7 +766,9 @@ jobs:
run: |
export PYTHONPATH=$HOME/.local/lib/python3.10/site-packages:$PYTHONPATH
mkdir capybara-reports
capybara bara ref/rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root
mkdir new
ln -sf ../rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root new/
capybara bara ref/rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root new/rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}.edm4eic.root
mv capybara-reports rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}
touch .rec_${{ matrix.particle }}_1GeV_20GeV_${{ matrix.detector_config }}
- uses: actions/upload-artifact@v4
@@ -821,9 +830,10 @@ jobs:
platform-release: "${{ env.platform }}:${{ env.release }}"
setup: "/opt/detector/epic-${{ env.detector-version }}/bin/thisepic.sh"
run: |
sed '/rank=sink/s/"podio::Frame";//; /podio::Frame/d; /rank=source/s/"JEventProcessorPODIO";//; /JEventProcessorPODIO/d' jana.dot | dot -Tsvg > jana.svg
mv jana.dot rec_${{ matrix.particle }}_EcalLumiSpec_${{ matrix.detector_config }}.dot
sed '/rank=sink/s/"podio::Frame";//; /podio::Frame/d; /rank=source/s/"JEventProcessorPODIO";//; /JEventProcessorPODIO/d' *.dot | dot -Tsvg > jana.svg
mv jana.svg rec_${{ matrix.particle }}_EcalLumiSpec_${{ matrix.detector_config }}.svg
continue-on-error: true
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.particle }}_${{ matrix.detector_config }}_flags.json
@@ -892,21 +902,28 @@ jobs:
export DETECTOR_CONFIG=${DETECTOR}_${{ matrix.detector_config }}
export LD_LIBRARY_PATH=$PWD/install/lib:$LD_LIBRARY_PATH
export JANA_PLUGIN_PATH=$PWD/install/lib/EICrecon/plugins:/usr/local/plugins
prmon --json-summary rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.prmon.json -- \
$PWD/install/bin/eicrecon $JANA_OPTIONS -Ppodio:output_file=rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.edm4eic.root sim_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.edm4hep.root -Pacts:WriteObj=true -Pacts:WritePly=true -Pplugins=janadot,janatop $(<${{ github.workspace }}/.github/janadot.groups)
- uses: actions/upload-artifact@v4
with:
name: rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.edm4eic.root
path: rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.edm4eic.root
if-no-files-found: error
- uses: actions/upload-artifact@v4
with:
name: rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.prmon.json
path: rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.prmon.json
if-no-files-found: error
- name: Convert execution graphs
uses: eic/run-cvmfs-osg-eic-shell@main
with:
platform-release: "${{ env.platform }}:${{ env.release }}"
setup: "/opt/detector/epic-${{ env.detector-version }}/bin/thisepic.sh"
run: |
sed '/rank=sink/s/"podio::Frame";//; /podio::Frame/d; /rank=source/s/"JEventProcessorPODIO";//; /JEventProcessorPODIO/d' jana.dot | dot -Tsvg > jana.svg
mv jana.dot rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.dot
sed '/rank=sink/s/"podio::Frame";//; /podio::Frame/d; /rank=source/s/"JEventProcessorPODIO";//; /JEventProcessorPODIO/d' *.dot | dot -Tsvg > jana.svg
mv jana.svg rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.svg
continue-on-error: true
- uses: actions/upload-artifact@v4
with:
name: rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.dot
@@ -947,7 +964,9 @@ jobs:
pip install 'pygithub>=2' 'bokeh>=3'
export PYTHONPATH=$HOME/.local/lib/python3.10/site-packages:$PYTHONPATH
mkdir capybara-reports
capybara bara ref/rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.edm4eic.root rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.edm4eic.root
mkdir new
ln -sf ../rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.edm4eic.root new/
capybara bara ref/rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.edm4eic.root new/rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}.edm4eic.root
mv capybara-reports rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}
touch .rec_dis_${{matrix.beam}}_minQ2=${{matrix.minq2}}_${{ matrix.detector_config }}
- uses: actions/upload-artifact@v4
@@ -1163,7 +1182,9 @@ jobs:
- get-docs-from-open-prs
steps:
- name: Merge GitHub Pages staging artifact
uses: actions/upload-artifact/merge@v4
# FIXME pinned per https://github.com/actions/upload-artifact/issues/485#issuecomment-2271527517
# to avoid https://github.com/eic/EICrecon/actions/runs/10486195490/job/29045183375#step:2:111
uses: actions/upload-artifact/merge@v4.3.5
with:
name: github-pages-staging
pattern: |
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ cmake_minimum_required(VERSION 3.24)
# 3.27: find_package() uses upper-case <PACKAGENAME>_ROOT variables
cmake_policy(SET CMP0144 NEW)

project(EICrecon)
project(EICrecon LANGUAGES CXX)

# CMake includes
include(CheckCXXCompilerFlag)
5 changes: 3 additions & 2 deletions src/algorithms/calorimetry/CalorimeterClusterRecoCoG.cc
Original file line number Diff line number Diff line change
@@ -161,14 +161,15 @@ std::optional<edm4eic::MutableCluster> CalorimeterClusterRecoCoG::reconstruct(co
// Used to optionally constrain the cluster eta to those of the contributing hits
float minHitEta = std::numeric_limits<float>::max();
float maxHitEta = std::numeric_limits<float>::min();
auto time = pcl.getHits()[0].getTime();
auto timeError = pcl.getHits()[0].getTimeError();
auto time = 0;
auto timeError = 0;
for (unsigned i = 0; i < pcl.getHits().size(); ++i) {
const auto& hit = pcl.getHits()[i];
const auto weight = pcl.getWeights()[i];
debug("hit energy = {} hit weight: {}", hit.getEnergy(), weight);
auto energy = hit.getEnergy() * weight;
totalE += energy;
time += (hit.getTime() - time) * energy / totalE;
cl.addToHits(hit);
cl.addToHitContributions(energy);
const float eta = edm4hep::utils::eta(hit.getPosition());
17 changes: 16 additions & 1 deletion src/algorithms/calorimetry/CalorimeterHitReco.cc
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
#include "CalorimeterHitReco.h"

#include <DD4hep/Alignments.h>
#include <DD4hep/Handle.h>
#include <DD4hep/IDDescriptor.h>
#include <DD4hep/Objects.h>
#include <DD4hep/Readout.h>
@@ -16,7 +17,10 @@
#include <DD4hep/VolumeManager.h>
#include <DD4hep/Volumes.h>
#include <DD4hep/config.h>
#include <DD4hep/detail/SegmentationsInterna.h>
#include <DDSegmentation/BitFieldCoder.h>
#include <DDSegmentation/MultiSegmentation.h>
#include <DDSegmentation/Segmentation.h>
#include <Evaluator/DD4hepUnits.h>
#include <Math/GenVector/Cartesian3D.h>
#include <Math/GenVector/DisplacementVector3D.h>
@@ -242,7 +246,18 @@ void CalorimeterHitReco::process(
const auto pos = local.nominal().worldToLocal(gpos);
std::vector<double> cdim;
// get segmentation dimensions
auto segmentation_type = m_converter->findReadout(local).segmentation().type();

const dd4hep::DDSegmentation::Segmentation* segmentation = m_converter->findReadout(local).segmentation()->segmentation;
auto segmentation_type = segmentation->type();

while (segmentation_type == "MultiSegmentation"){
const auto* multi_segmentation = dynamic_cast<const dd4hep::DDSegmentation::MultiSegmentation*>(segmentation);
const dd4hep::DDSegmentation::Segmentation& sub_segmentation = multi_segmentation->subsegmentation(cellID);

segmentation = &sub_segmentation;
segmentation_type = segmentation->type();
}

if (segmentation_type == "CartesianGridXY" || segmentation_type == "HexGridXY") {
auto cell_dim = m_converter->cellDimensions(cellID);
cdim.resize(3);
2 changes: 1 addition & 1 deletion src/algorithms/calorimetry/CalorimeterHitsMerger.cc
Original file line number Diff line number Diff line change
@@ -121,7 +121,7 @@ void CalorimeterHitsMerger::process(
gpos.x() / dd4hep::mm, gpos.y() / dd4hep::mm, gpos.z() / dd4hep::mm
);
const decltype(edm4eic::CalorimeterHitData::local) local(
pos.x(), pos.y(), pos.z()
pos.x() / dd4hep::mm, pos.y() / dd4hep::mm, pos.z() / dd4hep::mm
);

out_hits->create(
150 changes: 101 additions & 49 deletions src/algorithms/calorimetry/ImagingTopoCluster.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2022 Chao Peng, Sylvester Joosten, Whitney Armstrong
// Copyright (C) 2022 Chao Peng, Sylvester Joosten, Whitney Armstrong, Wouter Deconinck

/*
* Topological Cell Clustering Algorithm for Imaging Calorimetry
* 1. group all the adjacent pixels
*
* Author: Chao Peng (ANL), 06/02/2021
* References: https://arxiv.org/pdf/1603.02934.pdf
* Original reference: https://arxiv.org/pdf/1603.02934.pdf
*
* Modifications:
*
* Wouter Deconinck (Manitoba), 08/24/2024
* - converted hit storage model from std::vector to std::set sorted on layer
* where only hits remaining to be assigned to a group are in the set
* - erase hits that are too low in energy to be part of a cluster
* - converted group storage model from std::set to std::list to allow adding
* hits while keeping iterators valid
*
*/
#pragma once

#include <algorithm>
#include <numeric>

#include <algorithms/algorithm.h>
#include <DD4hep/BitFieldCoder.h>
@@ -40,16 +50,6 @@ namespace eicrecon {
>
>;

/** Topological Cell Clustering Algorithm.
*
* Topological Cell Clustering Algorithm for Imaging Calorimetry
* 1. group all the adjacent pixels
*
* Author: Chao Peng (ANL), 06/02/2021
* References: https://arxiv.org/pdf/1603.02934.pdf
*
* \ingroup reco
*/
class ImagingTopoCluster
: public ImagingTopoClusterAlgorithm,
public WithPodConfig<ImagingTopoClusterConfig> {
@@ -64,8 +64,13 @@ namespace eicrecon {
private:

// unitless counterparts of the input parameters
double localDistXY[2]{0, 0}, layerDistEtaPhi[2]{0, 0}, layerDistXY[2]{0, 0}, sectorDist{0};
double minClusterHitEdep{0}, minClusterCenterEdep{0}, minClusterEdep{0}, minClusterNhits{0};
std::array<double,2> localDistXY{0, 0};
std::array<double,2> layerDistEtaPhi{0, 0};
std::array<double,2> layerDistXY{0, 0};
double sectorDist{0};
double minClusterHitEdep{0};
double minClusterCenterEdep{0};
double minClusterEdep{0};

public:
void init() {
@@ -79,6 +84,10 @@ namespace eicrecon {
error( "Expected 2 values (eta_dist, phi_dist) for layerDistEtaPhi" );
return;
}
if (m_cfg.minClusterCenterEdep < m_cfg.minClusterHitEdep) {
error( "minClusterCenterEdep must be greater than or equal to minClusterHitEdep" );
return;
}

// using juggler internal units (GeV, dd4hep::mm, dd4hep::ns, dd4hep::rad)
localDistXY[0] = m_cfg.localDistXY[0] / dd4hep::mm;
@@ -124,21 +133,55 @@ namespace eicrecon {
const auto [hits] = input;
auto [proto] = output;

// group neighbouring hits
std::vector<bool> visits(hits->size(), false);
std::vector<std::set<std::size_t>> groups;
for (size_t i = 0; i < hits->size(); ++i) {
debug("hit {:d}: local position = ({}, {}, {}), global position = ({}, {}, {})", i + 1,
(*hits)[i].getLocal().x, (*hits)[i].getLocal().y, (*hits)[i].getPosition().z,
(*hits)[i].getPosition().x, (*hits)[i].getPosition().y, (*hits)[i].getPosition().z
// Sort hit indices (podio collections do not support std::sort)
auto compare = [&hits](const auto& a, const auto& b) {
// if !(a < b) and !(b < a), then a and b are equivalent
// and only one of them will be allowed in a set
if ((*hits)[a].getLayer() == (*hits)[b].getLayer()) {
return (*hits)[a].getObjectID().index < (*hits)[b].getObjectID().index;
}
return (*hits)[a].getLayer() < (*hits)[b].getLayer();
};
// indices contains the remaining hit indices that have not
// been assigned to a group yet
std::set<std::size_t, decltype(compare)> indices(compare);
// set does not have a size yet, so cannot fill with iota
for (std::size_t i = 0; i < hits->size(); ++i) {
indices.insert(i);
}
// ensure no hits were dropped due to equivalency in set
if (hits->size() != indices.size()) {
error("equivalent hits were dropped: #hits {:d}, #indices {:d}", hits->size(), indices.size());
}

// Group neighbouring hits
std::vector<std::list<std::size_t>> groups;
// because indices changes, the loop over indices requires some care:
// - we must use iterators instead of range-for
// - erase returns an incremented iterator and therefore acts as idx++
// - when the set becomes empty on erase, idx is invalid and idx++ will be too
// (also applies to loop in bfs_group below)
for (auto idx = indices.begin(); idx != indices.end();
indices.empty() ? idx = indices.end() : idx) {

debug("hit {:d}: local position = ({}, {}, {}), global position = ({}, {}, {}), energy = {}", *idx,
(*hits)[*idx].getLocal().x, (*hits)[*idx].getLocal().y, (*hits)[*idx].getPosition().z,
(*hits)[*idx].getPosition().x, (*hits)[*idx].getPosition().y, (*hits)[*idx].getPosition().z,
(*hits)[*idx].getEnergy()
);
// already in a group, or not energetic enough to form a cluster
if (visits[i] || (*hits)[i].getEnergy() < minClusterCenterEdep) {
continue;

// not energetic enough for cluster center, but could still be cluster hit
if ((*hits)[*idx].getEnergy() < minClusterCenterEdep) {
idx++;
continue;
}

// create a new group, and group all the neighbouring hits
groups.emplace_back();
bfs_group(*hits, groups.back(), i, visits);
groups.emplace_back(std::list{*idx});
bfs_group(*hits, indices, groups.back(), *idx);

// wait with erasing until after bfs_group to ensure iterator is not invalidated in bfs_group
idx = indices.erase(idx); // takes role of idx++
}
debug("found {} potential clusters (groups of hits)", groups.size());
for (size_t i = 0; i < groups.size(); ++i) {
@@ -147,7 +190,7 @@ namespace eicrecon {

// form clusters
for (const auto &group : groups) {
if (static_cast<int>(group.size()) < m_cfg.minClusterNhits) {
if (group.size() < m_cfg.minClusterNhits) {
continue;
}
double energy = 0.;
@@ -198,35 +241,44 @@ namespace eicrecon {
}

// grouping function with Breadth-First Search
void bfs_group(const edm4eic::CalorimeterHitCollection &hits, std::set<std::size_t> &group, std::size_t idx, std::vector<bool> &visits) const {
visits[idx] = true;
// note: template to allow Compare only known in local scope of caller
template<typename Compare>
void bfs_group(const edm4eic::CalorimeterHitCollection &hits, std::set<std::size_t,Compare>& indices, std::list<std::size_t> &group, const std::size_t idx) const {

// not a qualified hit to participate clustering, stop here
if (hits[idx].getEnergy() < m_cfg.minClusterHitEdep) {
return;
}
// loop over group as it grows, until the end is stable and we reach it
for (auto idx1 = group.begin(); idx1 != group.end(); ++idx1) {
// check neighbours (note comments on loop over set above)
for (auto idx2 = indices.begin(); idx2 != indices.end();
indices.empty() ? idx2 = indices.end() : idx2) {

group.insert(idx);
size_t prev_size = 0;
// skip idx1 and original idx
// (we cannot erase idx since it would invalidate iterator in calling scope)
if (*idx2 == *idx1 || *idx2 == idx) {
idx2++;
continue;
}

while (prev_size != group.size()) {
prev_size = group.size();
for (std::size_t idx1 : group) {
// check neighbours
for (std::size_t idx2 = 0; idx2 < hits.size(); ++idx2) {
// not a qualified hit to participate clustering, skip
if (hits[idx2].getEnergy() < m_cfg.minClusterHitEdep) {
continue;
}
if ((!visits[idx2])
&& is_neighbour(hits[idx1], hits[idx2])) {
group.insert(idx2);
visits[idx2] = true;
}
// skip rest of list of hits when we're past relevant layers
//if (hits[*idx2].getLayer() - hits[*idx1].getLayer() > m_cfg.neighbourLayersRange) {
// break;
//}

// not energetic enough for cluster hit
if (hits[*idx2].getEnergy() < m_cfg.minClusterHitEdep) {
idx2 = indices.erase(idx2);
continue;
}

if (is_neighbour(hits[*idx1], hits[*idx2])) {
group.push_back(*idx2);
idx2 = indices.erase(idx2); // takes role of idx2++
} else {
idx2++;
}
}
}
}

};

} // namespace eicrecon
Loading