From 351d3797a2f94c76a96ec88ca0797aacfce8fefa Mon Sep 17 00:00:00 2001 From: Leonardo Lena Date: Mon, 6 Oct 2025 15:24:42 -0500 Subject: [PATCH 1/3] Updated CAFMaker to work with NuGraph inference on multiple slices. --- sbncode/CAFMaker/CAFMakerParams.h | 8 ++-- sbncode/CAFMaker/CAFMaker_module.cc | 71 +++++++++++++++++++---------- sbncode/CAFMaker/FillReco.cxx | 44 +++++++----------- sbncode/CAFMaker/FillReco.h | 4 +- 4 files changed, 69 insertions(+), 58 deletions(-) diff --git a/sbncode/CAFMaker/CAFMakerParams.h b/sbncode/CAFMaker/CAFMakerParams.h index 253f0d4c2..ce76fa4f5 100644 --- a/sbncode/CAFMaker/CAFMakerParams.h +++ b/sbncode/CAFMaker/CAFMakerParams.h @@ -350,10 +350,10 @@ namespace caf "" //Empty by default, configured in icaruscode cafmaker_defs }; - Atom NuGraphSliceHitLabel { - Name("NuGraphSliceHitLabel"), - Comment("Label of NuGraph slice hit map."), - "" //Empty by default, please set to e.g. art::InputTag("nuslhits") + Atom NCCSlicesLabel { + Name("NCCSlicesLabel"), + Comment("Label of NotClearCosmic slices."), + "" //Empty by default, please set to e.g. art::InputTag("NCCSlices") }; Atom NuGraphFilterLabel { diff --git a/sbncode/CAFMaker/CAFMaker_module.cc b/sbncode/CAFMaker/CAFMaker_module.cc index 48da84e8f..03ab5c886 100644 --- a/sbncode/CAFMaker/CAFMaker_module.cc +++ b/sbncode/CAFMaker/CAFMaker_module.cc @@ -1717,6 +1717,7 @@ void CAFMaker::produce(art::Event& evt) noexcept { // collect the TPC slices std::vector> slices; + std::vector> nccSlices; std::vector slice_tag_suffixes; std::vector slice_tag_indices; for (unsigned i_tag = 0; i_tag < pandora_tag_suffixes.size(); i_tag++) { @@ -1726,6 +1727,8 @@ void CAFMaker::produce(art::Event& evt) noexcept { GetByLabelStrict(evt, fParams.PFParticleLabel() + pandora_tag_suffix, thisSlices); if (thisSlices.isValid()) { art::fill_ptr_vector(slices, thisSlices); + const std::vector>& tempNCCSlices = evt.getProduct>>(fParams.NCCSlicesLabel().label() + pandora_tag_suffix); + nccSlices.insert(nccSlices.end(), tempNCCSlices.begin(), tempNCCSlices.end()); for (unsigned i = 0; i < thisSlices->size(); i++) { slice_tag_suffixes.push_back(pandora_tag_suffix); slice_tag_indices.push_back(i_tag); @@ -1733,17 +1736,6 @@ void CAFMaker::produce(art::Event& evt) noexcept { } } - // nu graph - std::vector< art::Handle> > ng2_slice_hit_map_handle(pandora_tag_suffixes.size()); - std::vector< art::Handle>> > ng2_filter_handle(pandora_tag_suffixes.size()); - std::vector< art::Handle>> > ng2_semantic_handle(pandora_tag_suffixes.size()); - for (unsigned i_tag = 0; i_tag < pandora_tag_suffixes.size(); i_tag++) { - const std::string &pandora_tag_suffix = pandora_tag_suffixes[i_tag]; - GetByLabelIfExists(evt, fParams.NuGraphSliceHitLabel().encode() + pandora_tag_suffix, ng2_slice_hit_map_handle[i_tag]); - GetByLabelIfExists(evt, fParams.NuGraphFilterLabel().label() + pandora_tag_suffix + ":" + fParams.NuGraphFilterLabel().instance(), ng2_filter_handle[i_tag]); - GetByLabelIfExists(evt, fParams.NuGraphSemanticLabel().label() + pandora_tag_suffix + ":" + fParams.NuGraphSemanticLabel().instance(), ng2_semantic_handle[i_tag]); - } - // The Standard Record // Branch entry definition -- contains list of slices, CRT information, and truth information StandardRecord rec; @@ -1799,17 +1791,30 @@ void CAFMaker::produce(art::Event& evt) noexcept { fmatch_assn_map.emplace(std::make_pair(fname_opdet, sfm_assn)); } } + + if (std::find(nccSlices.begin(), nccSlices.end(), slice) != nccSlices.end()) { + // this check is required: if FindOneP does not find an associated item instead of returning nullptr it throws an exception. + // I believe a change of this behaviour could be handy. + std::vector>> ng2_filter_vec; + std::vector>> ng2_semantic_vec; + art::FindOneP> findOneFilter(slcHits, evt, fParams.NuGraphFilterLabel().label() + slice_tag_suff + ":" + fParams.NuGraphFilterLabel().instance()); + art::FindOneP> findOneSemantic(slcHits, evt, fParams.NuGraphSemanticLabel().label() + slice_tag_suff + ":" + fParams.NuGraphSemanticLabel().instance()); + // iteration is the only way to get something out of a FindOne or FindMany... + if (findOneFilter.isValid()) { + for (size_t hitIdx = 0; hitIdx < slcHits.size(); ++hitIdx) { + ng2_filter_vec.emplace_back(findOneFilter.at(hitIdx)); + } + } - std::vector>> ng2_filter_vec; - std::vector>> ng2_semantic_vec; - if (ng2_filter_handle[producer].isValid()) { - art::fill_ptr_vector(ng2_filter_vec,ng2_filter_handle[producer]); - } - if (ng2_semantic_handle[producer].isValid()) { - art::fill_ptr_vector(ng2_semantic_vec,ng2_semantic_handle[producer]); - } - if (ng2_slice_hit_map_handle[producer].isValid()) { - FillSliceNuGraph(slcHits,*ng2_slice_hit_map_handle[producer],ng2_filter_vec,ng2_semantic_vec,recslc); + if (findOneSemantic.isValid()) { + for (size_t hitIdx = 0; hitIdx < slcHits.size(); ++hitIdx) { + ng2_semantic_vec.emplace_back(findOneSemantic.at(hitIdx)); + } + } + + if (ng2_filter_vec.size() > 0 || ng2_semantic_vec.size() > 0) { + FillSliceNuGraph(slcHits, ng2_filter_vec, ng2_semantic_vec, recslc); + } } art::FindManyP fmOpT0 = @@ -2189,8 +2194,28 @@ void CAFMaker::produce(art::Event& evt) noexcept { FillCNNScores(thisParticle, cnnScores, pfp); } - if (ng2_slice_hit_map_handle[producer].isValid()) { - FillPFPNuGraph(*ng2_slice_hit_map_handle[producer], ng2_filter_vec, ng2_semantic_vec, fmPFPartHits.at(iPart), pfp); + if (std::find(nccSlices.begin(), nccSlices.end(), slice) != nccSlices.end()) { + const std::vector>& PFPHits = fmPFPartHits.at(iPart); + art::FindOneP> findOneFilter(PFPHits, evt, fParams.NuGraphFilterLabel().label() + slice_tag_suff + ":" + fParams.NuGraphFilterLabel().instance()); + art::FindOneP> findOneSemantic(PFPHits, evt, fParams.NuGraphSemanticLabel().label() + slice_tag_suff + ":" + fParams.NuGraphSemanticLabel().instance()); + std::vector>> ng2_filter_vec; + std::vector>> ng2_semantic_vec; + + if (findOneFilter.isValid()) { + for (size_t hitIdx = 0; hitIdx < PFPHits.size(); ++hitIdx) { + ng2_filter_vec.emplace_back(findOneFilter.at(hitIdx)); + } + } + + if (findOneSemantic.isValid()) { + for (size_t hitIdx = 0; hitIdx < PFPHits.size(); ++hitIdx) { + ng2_semantic_vec.emplace_back(findOneSemantic.at(hitIdx)); + } + } + + if (ng2_filter_vec.size() > 0 || ng2_semantic_vec.size() > 0) { + FillPFPNuGraph(PFPHits, ng2_filter_vec, ng2_semantic_vec, pfp); + } } if (!thisTrack.empty()) { // it has a track! diff --git a/sbncode/CAFMaker/FillReco.cxx b/sbncode/CAFMaker/FillReco.cxx index d81cb5890..9f14b9d5f 100644 --- a/sbncode/CAFMaker/FillReco.cxx +++ b/sbncode/CAFMaker/FillReco.cxx @@ -581,16 +581,16 @@ namespace caf } void FillSliceNuGraph(const std::vector> &inputHits, - const std::vector &sliceHitsMap, const std::vector>> &ngFilterResult, const std::vector>> &ngSemanticResult, caf::SRSlice &slice) { //need to double check that the slice processed by NuGraph is the same under consideration - //std::cout << "sizes=" << inputHits.size() << " " << sliceHitsMap.size() << " " << ngFilterResult.size() << " " << ngSemanticResult.size() << std::endl; + // std::cout << "sizes=" << inputHits.size() << " " << " " << ngFilterResult.size() << " " << ngSemanticResult.size() << std::endl; unsigned int nHits = inputHits.size(); - if (nHits==0 || nHits!=sliceHitsMap.size() || inputHits[0].key()!=sliceHitsMap[0]) return;//not the same slice! + // implementing this same-slice check with the updated architecture would be a major headache and I believe is not required given how inputHits is retrieved. + // if (nHits==0 || nHits!=sliceHitsMap.size() || inputHits[0].key()!=sliceHitsMap[0]) return;//not the same slice! unsigned int npass = 0; for ( unsigned int i = 0; i < nHits; i++ ) { @@ -1024,38 +1024,26 @@ namespace caf srpfp.cnnscore.nclusters = cnnscore->nClusters; } - void FillPFPNuGraph(const std::vector &sliceHitsMap, + void FillPFPNuGraph(const std::vector> &pfpHits, const std::vector>> &ngFilterResult, const std::vector>> &ngSemanticResult, - const std::vector> &pfpHits, caf::SRPFP& srpfp, bool allowEmpty) { - - // the nugraph elements are ordered the same as the sliceHitsMap - std::vector mappedhits; - for (auto& hit : pfpHits) { - auto it = std::find(sliceHitsMap.begin(), sliceHitsMap.end(), hit.key()); - if (it != sliceHitsMap.end()) { - size_t index = std::distance(sliceHitsMap.begin(), it); - mappedhits.push_back(index); - } - } - - if (mappedhits.size()>0) { + if (pfpHits.size()>0) { std::vector ng2sempfpcounts(5,0); size_t ng2bkgpfpcount = 0; - for (size_t pos : mappedhits) { - auto const& bkgscore = ngFilterResult.at(pos); - if (bkgscore->at(0) ng2semscores; - for (size_t i=0;isize();i++) ng2semscores.push_back(scores->at(i)); - size_t sem_label = std::distance(ng2semscores.begin(), std::max_element(ng2semscores.begin(), ng2semscores.end()));//arg_max(ng2semscores); - ng2sempfpcounts[sem_label]++; - } + for (size_t pos = 0; pos < pfpHits.size(); pos++) { + auto const& bkgscore = ngFilterResult.at(pos); + if (bkgscore->at(0) ng2semscores; + for (size_t i=0;isize();i++) ng2semscores.push_back(scores->at(i)); + size_t sem_label = std::distance(ng2semscores.begin(), std::max_element(ng2semscores.begin(), ng2semscores.end()));//arg_max(ng2semscores); + ng2sempfpcounts[sem_label]++; + } } srpfp.ngscore.sem_cat = SRNuGraphScore::NuGraphCategory(std::distance(ng2sempfpcounts.begin(), std::max_element(ng2sempfpcounts.begin(), ng2sempfpcounts.end())));//arg_max(ng2sempfpcounts); size_t nonBkgHits = (pfpHits.size() > ng2bkgpfpcount ? pfpHits.size()-ng2bkgpfpcount : 0); diff --git a/sbncode/CAFMaker/FillReco.h b/sbncode/CAFMaker/FillReco.h index 6e8ecf292..204dbaaf8 100644 --- a/sbncode/CAFMaker/FillReco.h +++ b/sbncode/CAFMaker/FillReco.h @@ -117,7 +117,6 @@ namespace caf * Hits with filter value (`ngFilterResult`) lower than `ng_filter_cut` are counted as background. */ void FillSliceNuGraph(const std::vector> &inputHits, - const std::vector &sliceHitsMap, const std::vector>> &ngFilterResult, const std::vector>> &ngSemanticResult, caf::SRSlice &slice); @@ -158,10 +157,9 @@ namespace caf * * Hits with filter value (`ngFilterResult`) lower than `ng_filter_cut` are counted as background. */ - void FillPFPNuGraph(const std::vector &sliceHitsMap, + void FillPFPNuGraph(const std::vector> &pfpHits, const std::vector>> &ngFilterResult, const std::vector>> &ngSemanticResult, - const std::vector> &pfpHits, caf::SRPFP& srpfp, bool allowEmpty= false); From 762e6ed3ee21f7761538a9c3091ed72fc965d38e Mon Sep 17 00:00:00 2001 From: Leonardo Lena Date: Tue, 14 Oct 2025 14:21:05 -0500 Subject: [PATCH 2/3] Fixed memory leak issues. --- sbncode/CAFMaker/CAFMakerParams.h | 6 ++--- sbncode/CAFMaker/CAFMaker_module.cc | 34 ++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/sbncode/CAFMaker/CAFMakerParams.h b/sbncode/CAFMaker/CAFMakerParams.h index ce76fa4f5..6d4ffd8db 100644 --- a/sbncode/CAFMaker/CAFMakerParams.h +++ b/sbncode/CAFMaker/CAFMakerParams.h @@ -350,9 +350,9 @@ namespace caf "" //Empty by default, configured in icaruscode cafmaker_defs }; - Atom NCCSlicesLabel { - Name("NCCSlicesLabel"), - Comment("Label of NotClearCosmic slices."), + Atom NuGraphSlicesLabel { + Name("NuGraphSlicesLabel"), + Comment("Label of slices that have NuGraph inference."), "" //Empty by default, please set to e.g. art::InputTag("NCCSlices") }; diff --git a/sbncode/CAFMaker/CAFMaker_module.cc b/sbncode/CAFMaker/CAFMaker_module.cc index 03ab5c886..31fd631dc 100644 --- a/sbncode/CAFMaker/CAFMaker_module.cc +++ b/sbncode/CAFMaker/CAFMaker_module.cc @@ -1717,7 +1717,7 @@ void CAFMaker::produce(art::Event& evt) noexcept { // collect the TPC slices std::vector> slices; - std::vector> nccSlices; + std::vector> nuGraphSlices; std::vector slice_tag_suffixes; std::vector slice_tag_indices; for (unsigned i_tag = 0; i_tag < pandora_tag_suffixes.size(); i_tag++) { @@ -1727,8 +1727,8 @@ void CAFMaker::produce(art::Event& evt) noexcept { GetByLabelStrict(evt, fParams.PFParticleLabel() + pandora_tag_suffix, thisSlices); if (thisSlices.isValid()) { art::fill_ptr_vector(slices, thisSlices); - const std::vector>& tempNCCSlices = evt.getProduct>>(fParams.NCCSlicesLabel().label() + pandora_tag_suffix); - nccSlices.insert(nccSlices.end(), tempNCCSlices.begin(), tempNCCSlices.end()); + const std::vector>& tempNuGraphSlices = evt.getProduct>>(fParams.NuGraphSlicesLabel().label() + pandora_tag_suffix); + nuGraphSlices.insert(nuGraphSlices.end(), tempNuGraphSlices.begin(), tempNuGraphSlices.end()); for (unsigned i = 0; i < thisSlices->size(); i++) { slice_tag_suffixes.push_back(pandora_tag_suffix); slice_tag_indices.push_back(i_tag); @@ -1792,9 +1792,7 @@ void CAFMaker::produce(art::Event& evt) noexcept { } } - if (std::find(nccSlices.begin(), nccSlices.end(), slice) != nccSlices.end()) { - // this check is required: if FindOneP does not find an associated item instead of returning nullptr it throws an exception. - // I believe a change of this behaviour could be handy. + if (std::find(nuGraphSlices.begin(), nuGraphSlices.end(), slice) != nuGraphSlices.end()) { std::vector>> ng2_filter_vec; std::vector>> ng2_semantic_vec; art::FindOneP> findOneFilter(slcHits, evt, fParams.NuGraphFilterLabel().label() + slice_tag_suff + ":" + fParams.NuGraphFilterLabel().instance()); @@ -1802,12 +1800,22 @@ void CAFMaker::produce(art::Event& evt) noexcept { // iteration is the only way to get something out of a FindOne or FindMany... if (findOneFilter.isValid()) { for (size_t hitIdx = 0; hitIdx < slcHits.size(); ++hitIdx) { + if (findOneFilter.at(hitIdx).isNull()) { + slcHits.erase(slcHits.begin()+hitIdx); + hitIdx--; + continue; + } ng2_filter_vec.emplace_back(findOneFilter.at(hitIdx)); } } if (findOneSemantic.isValid()) { for (size_t hitIdx = 0; hitIdx < slcHits.size(); ++hitIdx) { + if (findOneSemantic.at(hitIdx).isNull()) { + slcHits.erase(slcHits.begin()+hitIdx); + hitIdx--; + continue; + } ng2_semantic_vec.emplace_back(findOneSemantic.at(hitIdx)); } } @@ -2194,8 +2202,8 @@ void CAFMaker::produce(art::Event& evt) noexcept { FillCNNScores(thisParticle, cnnScores, pfp); } - if (std::find(nccSlices.begin(), nccSlices.end(), slice) != nccSlices.end()) { - const std::vector>& PFPHits = fmPFPartHits.at(iPart); + if (std::find(nuGraphSlices.begin(), nuGraphSlices.end(), slice) != nuGraphSlices.end()) { + std::vector>& PFPHits = fmPFPartHits.at(iPart); art::FindOneP> findOneFilter(PFPHits, evt, fParams.NuGraphFilterLabel().label() + slice_tag_suff + ":" + fParams.NuGraphFilterLabel().instance()); art::FindOneP> findOneSemantic(PFPHits, evt, fParams.NuGraphSemanticLabel().label() + slice_tag_suff + ":" + fParams.NuGraphSemanticLabel().instance()); std::vector>> ng2_filter_vec; @@ -2203,12 +2211,22 @@ void CAFMaker::produce(art::Event& evt) noexcept { if (findOneFilter.isValid()) { for (size_t hitIdx = 0; hitIdx < PFPHits.size(); ++hitIdx) { + if (findOneFilter.at(hitIdx).isNull()) { + PFPHits.erase(PFPHits.begin() + hitIdx); + hitIdx--; + continue; + } ng2_filter_vec.emplace_back(findOneFilter.at(hitIdx)); } } if (findOneSemantic.isValid()) { for (size_t hitIdx = 0; hitIdx < PFPHits.size(); ++hitIdx) { + if (findOneSemantic.at(hitIdx).isNull()) { + PFPHits.erase(PFPHits.begin() + hitIdx); + hitIdx--; + continue; + } ng2_semantic_vec.emplace_back(findOneSemantic.at(hitIdx)); } } From 78a0667afdad4df68c8459d2dbea36d84b3798ed Mon Sep 17 00:00:00 2001 From: Leonardo Lena Date: Thu, 23 Oct 2025 11:06:18 -0500 Subject: [PATCH 3/3] FEATURE: implemented CAFMaker compatibility with PandoraAfterNuGraph. --- sbncode/CAFMaker/CAFMakerParams.h | 6 ++++++ sbncode/CAFMaker/CAFMaker_module.cc | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sbncode/CAFMaker/CAFMakerParams.h b/sbncode/CAFMaker/CAFMakerParams.h index 6d4ffd8db..1ee49e694 100644 --- a/sbncode/CAFMaker/CAFMakerParams.h +++ b/sbncode/CAFMaker/CAFMakerParams.h @@ -368,6 +368,12 @@ namespace caf "" //Empty by default, please set to e.g. art::InputTag("NuGraph","semantic") }; + Atom UsePandoraAfterNuGraph { + Name("UsePandoraAfterNuGraph"), + Comment("Whether to use the second pass Pandora outputs for NuGraph reco."), + false + }; + Atom OpFlashLabel { Name("OpFlashLabel"), Comment("Label of PMT flash."), diff --git a/sbncode/CAFMaker/CAFMaker_module.cc b/sbncode/CAFMaker/CAFMaker_module.cc index 31fd631dc..32d97b694 100644 --- a/sbncode/CAFMaker/CAFMaker_module.cc +++ b/sbncode/CAFMaker/CAFMaker_module.cc @@ -1727,8 +1727,10 @@ void CAFMaker::produce(art::Event& evt) noexcept { GetByLabelStrict(evt, fParams.PFParticleLabel() + pandora_tag_suffix, thisSlices); if (thisSlices.isValid()) { art::fill_ptr_vector(slices, thisSlices); - const std::vector>& tempNuGraphSlices = evt.getProduct>>(fParams.NuGraphSlicesLabel().label() + pandora_tag_suffix); - nuGraphSlices.insert(nuGraphSlices.end(), tempNuGraphSlices.begin(), tempNuGraphSlices.end()); + nuGraphSlices = evt.getProduct>>(fParams.NuGraphSlicesLabel().label() + pandora_tag_suffix); + if (fParams.UsePandoraAfterNuGraph()) { + nuGraphSlices = slices; + } for (unsigned i = 0; i < thisSlices->size(); i++) { slice_tag_suffixes.push_back(pandora_tag_suffix); slice_tag_indices.push_back(i_tag);