Skip to content

Commit 69a761a

Browse files
Merge branch 'develop' into feature/aliciavr_XARAPUCA_offline_decoder_v4.0-extended_fragments
2 parents 177f2b4 + 8693f80 commit 69a761a

293 files changed

Lines changed: 94518 additions & 11183 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/pull_request_template.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
## Description
22
Please provide a detailed description of the changes this pull request introduces.
33

4-
$${\color{red}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!}$$
4+
$${\color{blue}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!}$$
55

6-
$${\color{red}\bf{\textrm{IMPORTANT UPDATE June 22nd 2025:}}}$$ If you are making a PR which is intended as a patch for the CURRENT production (which started in Spring 2025), you must make two PRs: one for develop and one for the production/v10_06_00 branch.
6+
$${\color{blue}\bf{\textrm{IMPORTANT UPDATE Feb 2nd 2026:}}}$$ If you are making a PR which is intended as a patch for the CURRENT production for gen 2 SBND analyses, you must make two PRs: one for develop and one for the production/sbnd-gen2 branch.
77

8-
$${\color{red}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!}$$
8+
$${\color{blue}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!}$$
99

1010
## Checklist
1111
- [ ] Added at least 1 label from [available labels](https://github.com/SBNSoftware/sbndcode/issues/labels?sort=name-asc).

CMakeLists.txt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
1717

18-
set(${PROJECT_NAME}_CMAKE_PROJECT_VERSION_STRING 10.10.03.01)
18+
set(${PROJECT_NAME}_CMAKE_PROJECT_VERSION_STRING 10.14.02.02)
1919
find_package(cetmodules REQUIRED)
2020
project(sbndcode LANGUAGES CXX)
2121

@@ -88,11 +88,8 @@ find_package( Eigen3 REQUIRED )
8888
find_package( Geant4 REQUIRED )
8989
find_package( Boost COMPONENTS system REQUIRED )
9090
find_package( ROOT REQUIRED )
91-
92-
if(DEFINED ENV{HEP_HPC_DIR} )
93-
find_ups_product( hdf5 )
94-
find_ups_product( hep_hpc )
95-
endif()
91+
find_package( hdf5 REQUIRED )
92+
find_package( hep_hpc REQUIRED )
9693

9794
# macros for dictionary and simple_plugin
9895
include(ArtDictionary)

sbndcode/BlipRecoSBND/Alg/BlipRecoAlg.cc

Lines changed: 64 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ namespace blip {
3232

3333

3434
// Loop over cryostats
35-
std::cout<<"NCryostats: "<<fGeom.Ncryostats()<<"\n";
3635
for(size_t cstat=0; cstat<fGeom.Ncryostats(); cstat++){
3736
auto const& cryoid = geo::CryostatID(cstat);
3837

@@ -41,16 +40,14 @@ namespace blip {
4140
auto const& tpcid = geo::TPCID(cryoid,tpc);
4241

4342
// Loop planes in TPC 'tpc'
44-
auto const& plane0id = geo::PlaneID(cstat,tpc,0);
45-
auto const& plane0geo = wireReadoutGeom->Get().Plane(plane0id);
43+
auto const& plane0id = geo::PlaneID(cstat,tpc,0);
44+
auto const& plane0geo = wireReadoutGeom->Get().Plane(plane0id);
4645
for(size_t pl=0; pl<wireReadoutGeom->Get().Nplanes(tpcid); pl++){
4746
auto const& planeid = geo::PlaneID(cstat,tpc,pl);
4847
auto const& planegeo = wireReadoutGeom->Get().Plane(planeid);
4948
kNumChannels += planegeo.Nwires();
5049

5150
float offset = detProp.GetXTicksOffset(pl,tpc,cstat);
52-
std::cout<<"CRYOSTAT "<<cstat<<" / TPC "<<tpc<<" / PLANE "<<pl<<": "<<planegeo.Nwires()<<" wires\n";
53-
std::cout<<" XTicksOffset (from detProp): "<<offset<<"\n";
5451

5552
kXTicksOffsets[cstat][tpc][pl] = 0;
5653

@@ -66,12 +63,9 @@ namespace blip {
6663
auto const& tpcgeom = cryostat.TPC(tpc);
6764
auto const xyz = plane0geo.GetCenter();
6865
const double dir((tpcgeom.DriftSign() == geo::DriftSign::Negative) ? +1.0 : -1.0);
69-
float x_ticks_coefficient = kDriftVelocity*kTickPeriod;
66+
float x_ticks_coefficient = kDriftVelocity*kTickPeriod;
7067
float goofy_offset = -xyz.X() / (dir * x_ticks_coefficient);
71-
std::cout<<" After geometric correction: "<<offset - goofy_offset<<"\n";
72-
7368
kXTicksOffsets[cstat][tpc][pl] = offset - goofy_offset;
74-
7569
} else {
7670

7771
// for the case of 2D wirecell workflow, the plane-to-plane
@@ -82,12 +76,7 @@ namespace blip {
8276
// we still want to correct for global trigger offset though:
8377
kXTicksOffsets[cstat][tpc][pl] = clockData.TriggerTime()/kTickPeriod;
8478

85-
}
86-
87-
// additional ad-hoc corrections supplied by user
88-
//kXTicksOffsets[cstat][tpc][pl] += fTimeOffset[pl];
89-
std::cout << " offsetting plane " << pl << " by " << fTimeOffset[pl] << " ticks " << std::endl;
90-
79+
}
9180
}
9281
}
9382
}
@@ -164,18 +153,18 @@ namespace blip {
164153
for(int i=0; i<kNplanes; i++) {
165154
if( i == fCaloPlane ) continue;
166155
h_clust_overlap[iTPC][i] = hdir.make<TH1D>(Form("t%i_p%i_clust_overlap",iTPC,i), Form("TPC %i, Plane %i clusters;Overlap fraction",iTPC,i),101,0,1.01);
167-
h_clust_dt[iTPC][i] = hdir.make<TH1D>(Form("t%i_p%i_clust_dt",iTPC,i), Form("TPC %i, Plane %i clusters;dT [ticks]",iTPC,i),200,-10,10);
156+
h_clust_dt[iTPC][i] = hdir.make<TH1D>(Form("t%i_p%i_clust_dt",iTPC,i), Form("TPC %i, Plane %i clusters;dT [ticks]",iTPC,i),200,-10,10);
168157
h_clust_dtfrac[iTPC][i] = hdir.make<TH1D>(Form("t%i_p%i_clust_dtfrac",iTPC,i), Form("TPC %i, Plane %i clusters;Charge-weighted mean dT/RMS",iTPC,i),150,-1.5,1.5);
169158

170159
h_clust_q[iTPC][i] = hdir.make<TH2D>(Form("t%i_p%i_clust_charge",iTPC,i),
171160
Form("Pre-cut, TPC %i;Plane %i cluster charge [#times10^{3} e-];Plane %i cluster charge [#times10^{3} e-]",iTPC,fCaloPlane,i),
172161
qbins,0,qmax,qbins,0,qmax);
173-
h_clust_q[iTPC][i]->SetOption("colz");
162+
h_clust_q[iTPC][i]->SetOption("colz");
174163

175164
h_clust_q_cut[iTPC][i] = hdir.make<TH2D>(Form("t%i_p%i_clust_charge_cut",iTPC,i),
176165
Form("Post-cut, TPC %i;Plane %i cluster charge [#times10^{3} e-];Plane %i cluster charge [#times10^{3}]",iTPC,fCaloPlane,i),
177166
qbins,0,qmax,qbins,0,qmax);
178-
h_clust_q_cut[iTPC][i]->SetOption("colz");
167+
h_clust_q_cut[iTPC][i]->SetOption("colz");
179168

180169
h_clust_score[iTPC][i] = hdir.make<TH1D>(Form("t%i_p%i_clust_matchscore",iTPC,i), Form("TPC %i, Plane %i clusters;Match score",iTPC,i),101,0,1.01);
181170

@@ -189,7 +178,7 @@ namespace blip {
189178
h_clust_truematch_q[iTPC][i] = hdir.make<TH2D>(Form("t%i_p%i_clust_truematch_charge",iTPC,i),
190179
Form("Pre-cut, TPC %i;Plane %i cluster charge [#times10^{3} e-];Plane %i cluster charge [#times10^{3} e-]",iTPC,fCaloPlane,i),
191180
qbins,0,qmax,qbins,0,qmax);
192-
h_clust_truematch_q[iTPC][i]->SetOption("colz");
181+
h_clust_truematch_q[iTPC][i]->SetOption("colz");
193182

194183
h_clust_truematch_score[iTPC][i] = hdir.make<TH1D>(Form("t%i_p%i_clust_truematch_matchscore",iTPC,i), Form("TPC %i, Plane %i clusters;Match score",iTPC,i),101,0,1.01);
195184

@@ -239,6 +228,7 @@ namespace blip {
239228
void BlipRecoAlg::reconfigure( fhicl::ParameterSet const& pset ){
240229

241230
fHitProducer = pset.get<std::string> ("HitProducer", "gaushit");
231+
fHitTruthMatcher = pset.get<std::string> ("HitTruthMatcher", "blipgaushitTruthMatch");
242232
fTrkProducer = pset.get<std::string> ("TrkProducer", "pandora");
243233
fGeantProducer = pset.get<std::string> ("GeantProducer", "largeant");
244234
fSimDepProducer = pset.get<std::string> ("SimEDepProducer", "ionandscint");
@@ -357,31 +347,39 @@ namespace blip {
357347
if (evt.getByLabel(fGeantProducer,pHandle))
358348
art::fill_ptr_vector(plist, pHandle);
359349

360-
// -- SimEnergyDeposits
350+
// -- SimEnergyDeposits
361351
art::Handle<std::vector<sim::SimEnergyDeposit> > sedHandle;
362352
std::vector<art::Ptr<sim::SimEnergyDeposit> > sedlist;
363353
if (evt.getByLabel(fSimDepProducer,sedHandle)){
364354
art::fill_ptr_vector(sedlist, sedHandle);
365-
}
366-
367-
// -- SimChannels (usually dropped in reco)
355+
}
356+
std::vector<sim::IDE > sIDElist;
357+
// -- SimChannels
368358
art::Handle<std::vector<sim::SimChannel> > simchanHandle;
369359
std::vector<art::Ptr<sim::SimChannel> > simchanlist;
370-
if (evt.getByLabel(fSimChanProducer,simchanHandle))
360+
if (evt.getByLabel(fSimChanProducer,simchanHandle))
361+
{
371362
art::fill_ptr_vector(simchanlist, simchanHandle);
372-
363+
//Loop over channels to get full sIDElist
364+
for(int chIndex=0; chIndex<int(simchanlist.size()); chIndex++)
365+
{
366+
std::vector<geo::WireID> wids = wireReadoutGeom->Get().ChannelToWire( (*(simchanlist[chIndex])).Channel() ); //Not sure why this is a vector, but it should have len 1
367+
const geo::PlaneID& planeID = wids[0].planeID();
368+
if(int(planeID.Plane) != fCaloPlane) continue; //only take calorimetry plane IDE values
369+
unsigned int MaxTDCTick = UINT_MAX;
370+
std::vector< sim::IDE > TempChIDE = (*simchanlist[chIndex]).TrackIDsAndEnergies(0, MaxTDCTick);
371+
for(int ideIndex=0; ideIndex<int(TempChIDE.size()); ideIndex++)
372+
{
373+
sIDElist.push_back( TempChIDE[ideIndex] ); //may need to add a &
374+
}
375+
}
376+
}
373377
// -- hits (from input module, usually track-masked subset of gaushit)
374378
art::Handle< std::vector<recob::Hit> > hitHandle;
375379
std::vector<art::Ptr<recob::Hit> > hitlist;
376380
if (evt.getByLabel(fHitProducer,hitHandle))
377381
art::fill_ptr_vector(hitlist, hitHandle);
378382

379-
// -- hits (from gaushit), these are used in truth-matching of hits
380-
art::Handle< std::vector<recob::Hit> > hitHandleGH;
381-
std::vector<art::Ptr<recob::Hit> > hitlistGH;
382-
if (evt.getByLabel("gaushit",hitHandleGH))
383-
art::fill_ptr_vector(hitlistGH, hitHandleGH);
384-
385383
// -- tracks
386384
art::Handle< std::vector<recob::Track> > tracklistHandle;
387385
std::vector<art::Ptr<recob::Track> > tracklist;
@@ -390,8 +388,8 @@ namespace blip {
390388

391389
// -- associations
392390
art::FindManyP<recob::Track> fmtrk(hitHandle,evt,fTrkProducer);
393-
art::FindManyP<recob::Track> fmtrkGH(hitHandleGH,evt,fTrkProducer);
394-
art::FindMany<simb::MCParticle,anab::BackTrackerHitMatchingData> fmhh(hitHandleGH,evt,"gaushitTruthMatch");
391+
art::FindManyP<recob::Track> fmtrkGH(hitHandle,evt,fTrkProducer);
392+
art::FindMany<simb::MCParticle, anab::BackTrackerHitMatchingData> fmhh(hitHandle,evt,fHitTruthMatcher);
395393
/*
396394
//====================================================
397395
// Update map of bad channels for this event
@@ -427,23 +425,7 @@ namespace blip {
427425
// use gaushitTruthMatch later on)
428426
//===============================================================
429427
std::map< int, int > map_gh;
430-
// if input collection is already gaushit, this is trivial
431-
if( fHitProducer == "gaushit" ) {
432-
for(auto& h : hitlist ) map_gh[h.key()] = h.key();
433-
// ... but if not, find the matching gaushit. There's no convenient
434-
// hit ID, so we must loop through and compare channel/time (ugh)
435-
} else {
436-
std::map<int,std::vector<int>> map_chan_ghid;
437-
for(auto& gh : hitlistGH ) map_chan_ghid[gh->Channel()].push_back(gh.key());
438-
for(auto& h : hitlist ) {
439-
for(auto& igh : map_chan_ghid[h->Channel()]){
440-
if( hitlistGH[igh]->PeakTime() != h->PeakTime() ) continue;
441-
map_gh[h.key()] = igh;
442-
break;
443-
}
444-
}
445-
}
446-
428+
for(auto& h : hitlist ) map_gh[h.key()] = h.key();
447429
//=====================================================
448430
// Record PDG for every G4 Track ID
449431
//=====================================================
@@ -512,15 +494,23 @@ namespace blip {
512494
if( plist.size() ) {
513495
pinfo.resize(plist.size());
514496
for(size_t i = 0; i<plist.size(); i++){
515-
BlipUtils::FillParticleInfo( *plist[i], pinfo[i], sedlist, fCaloPlane);
497+
//use sim::EnergyDeposits by default. This is heavy and may be dropped
498+
if(sedlist.size()>0)
499+
{
500+
BlipUtils::FillParticleInfo( *plist[i], pinfo[i], sedlist, fCaloPlane);
501+
}
502+
else //use sim::Channel -> IDE otherwise. This is usually kept but results in strange bugs.
503+
{
504+
BlipUtils::FillParticleInfo( *plist[i], pinfo[i], sIDElist, fCaloPlane);
505+
}
516506
if( map_g4trkid_charge[pinfo[i].trackId] ) pinfo[i].numElectrons = (int)map_g4trkid_charge[pinfo[i].trackId];
507+
else pinfo[i].numElectrons = pinfo[i].depElectrons; // JACOB ADDITION Jan22, 2026 for strange channel IDE results
517508
pinfo[i].index = i;
518509
}
519510
BlipUtils::MakeTrueBlips(pinfo, trueblips);
520511
BlipUtils::MergeTrueBlips(trueblips, fTrueBlipMergeDist);
521512
}
522513

523-
524514
//=======================================
525515
// Map track IDs to the index in the vector
526516
//=======================================
@@ -633,14 +623,8 @@ namespace blip {
633623

634624

635625
// find associated track
636-
if( fHitProducer == "gaushit" && fmtrk.isValid() ) {
626+
if( fmtrk.isValid() ) {
637627
if(fmtrk.at(i).size()) hitinfo[i].trkid = fmtrk.at(i)[0]->ID();
638-
639-
// if the hit collection didn't have associations made
640-
// to the tracks, try gaushit instead
641-
} else if ( fmtrkGH.isValid() && map_gh.size() ) {
642-
int gi = map_gh[i];
643-
if (fmtrkGH.at(gi).size()) hitinfo[i].trkid= fmtrkGH.at(gi)[0]->ID();
644628
}
645629

646630
// add to the map
@@ -650,8 +634,7 @@ namespace blip {
650634
if( hitinfo[i].trkid < 0 ) nhits_untracked++;
651635
//printf(" %lu plane: %i, wire: %i, time: %i\n",i,hitinfo[i].plane,hitinfo[i].wire,int(hitinfo[i].driftTime));
652636

653-
}//endloop over hits
654-
637+
}//endloop over hits
655638
//for(auto& a : tpc_plane_hitsMap ) {
656639
//for(auto& b : a.second )
657640
//std::cout<<"TPC "<<a.first<<", plane "<<b.first<<": "<<b.second.size()<<" hits\n";
@@ -676,6 +659,7 @@ namespace blip {
676659

677660

678661
// Basic track inclusion cut: exclude hits that were tracked
662+
int Tracked=0;
679663
for(size_t i=0; i<hitlist.size(); i++){
680664
if( hitinfo[i].trkid < 0 ) continue;
681665
auto it = map_trkid_index.find(hitinfo[i].trkid);
@@ -684,9 +668,9 @@ namespace blip {
684668
if( tracklist[trkindex]->Length() > fMaxHitTrkLength ) {
685669
hitIsTracked[i] = true;
686670
hitIsGood[i] = false;
671+
Tracked++;
687672
}
688-
}
689-
673+
}
690674

691675
// Filter based on hit properties. For hits that are a part of
692676
// multi-gaussian fits (multiplicity > 1), need to re-think this.
@@ -717,7 +701,6 @@ namespace blip {
717701
// Hit clustering
718702
// ---------------------------------------------------
719703
std::map<int,std::map<int,std::vector<int>>> tpc_planeclustsMap;
720-
721704
for(auto const& tpc_plane_hitsMap : cryo_tpc_plane_hitsMap ) {
722705

723706
for(auto const& plane_hitsMap : tpc_plane_hitsMap.second ) {
@@ -878,7 +861,6 @@ namespace blip {
878861

879862
float _matchQDiffLimit= (fMatchQDiffLimit <= 0 ) ? std::numeric_limits<float>::max() : fMatchQDiffLimit;
880863
float _matchMaxQRatio = (fMatchMaxQRatio <= 0 ) ? std::numeric_limits<float>::max() : fMatchMaxQRatio;
881-
882864
for(auto& tpcMap : tpc_planeclustsMap ) { // loop on TPCs
883865

884866
//std::cout
@@ -921,15 +903,15 @@ namespace blip {
921903
// Check that the two central wires intersect
922904
// *******************************************
923905
double y, z;
924-
geo::Point_t intsec_p;
925-
std::vector<geo::WireID> A_wireids = wireReadoutGeom->Get().ChannelToWire((unsigned int)hcA.CenterChan);
926-
std::vector<geo::WireID> B_wireids = wireReadoutGeom->Get().ChannelToWire((unsigned int)hcB.CenterChan);
906+
geo::Point_t intsec_p;
907+
std::vector<geo::WireID> A_wireids = wireReadoutGeom->Get().ChannelToWire((unsigned int)hcA.CenterChan);
908+
std::vector<geo::WireID> B_wireids = wireReadoutGeom->Get().ChannelToWire((unsigned int)hcB.CenterChan);
927909

928-
if( !wireReadoutGeom->Get().WireIDsIntersect(A_wireids.at(0),B_wireids.at(0),intsec_p)) continue;
929-
// Save intersect location, so we don't have to
910+
if( !wireReadoutGeom->Get().WireIDsIntersect(A_wireids.at(0),B_wireids.at(0),intsec_p)) continue;
911+
// Save intersect location, so we don't have to
930912
// make another call to the Geometry service later
931-
y = intsec_p.Y();
932-
z = intsec_p.Z();
913+
y = intsec_p.Y();
914+
z = intsec_p.Z();
933915
TVector3 xloc(0,y,z);
934916
hcA.IntersectLocations[hcB.ID] = xloc;
935917
hcB.IntersectLocations[hcA.ID] = xloc;
@@ -1032,7 +1014,6 @@ namespace blip {
10321014
for(auto& hc : hcGroup ) {
10331015
hitclust[hc.ID].isMatched = true;
10341016
for(auto hit : hitclust[hc.ID].HitIDs) hitinfo[hit].ismatch = true;
1035-
10361017
// Diagnostic plots for successful 3-plane matches
10371018
//if( picky && hc.Plane != fCaloPlane ) {
10381019
//float q1 = (float)newBlip.clusters[fCaloPlane].Charge;
@@ -1082,18 +1063,26 @@ namespace blip {
10821063
// if we made it this far, the blip is good!
10831064
// associate this blip with the hits and clusters within it
10841065
newBlip.ID = blips.size();
1085-
blips.push_back(newBlip);
10861066
for(auto& hc : hcGroup ) {
10871067
hitclust[hc.ID].BlipID = newBlip.ID;
10881068
for( auto& h : hc.HitIDs ) hitinfo[h].blipid = newBlip.ID;
10891069
}
1070+
//BLIPS HAVE A COPY OF HITCLUSTERS NOT A POINTER
1071+
//UPDATE THE HITCLUSTER VARS THAT HAVE CHANGED SINCE CONSTRUCTION
1072+
for(int iclust=0; iclust<int(sizeof(newBlip.clusters)/sizeof(newBlip.clusters[0])); iclust++)
1073+
{
1074+
if(newBlip.clusters[iclust].ID>-1){
1075+
newBlip.clusters[iclust].BlipID = newBlip.ID;
1076+
newBlip.clusters[iclust].isMatched = true;
1077+
}
1078+
}
1079+
blips.push_back(newBlip);
10901080

10911081

10921082
}//endif ncands > 0
10931083
}//endloop over caloplane ("Plane A") clusters
10941084
}//endif calo plane has clusters
10951085
}//endloop over TPCs
1096-
10971086
// Re-index the clusters after removing unmatched
10981087
if( !keepAllClusts ) {
10991088
std::vector<blip::HitClust> hitclust_filt;

0 commit comments

Comments
 (0)