diff --git a/CMakeLists.txt b/CMakeLists.txt index 06c9db9f..723321bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ find_package(larreco REQUIRED) find_package(nugen REQUIRED) find_package(nutools REQUIRED) find_package(Eigen3 REQUIRED) +find_package( TorchScatter QUIET EXPORT) cet_cmake_module_directories(Modules BINARY) diff --git a/ubreco/BlipReco/Alg/BlipRecoAlg.cc b/ubreco/BlipReco/Alg/BlipRecoAlg.cc index 485829ba..fa87f34c 100644 --- a/ubreco/BlipReco/Alg/BlipRecoAlg.cc +++ b/ubreco/BlipReco/Alg/BlipRecoAlg.cc @@ -1,4 +1,5 @@ #include "ubreco/BlipReco/Alg/BlipRecoAlg.h" +#include "lardataobj/AnalysisBase/Calorimetry.h" #include namespace blip { @@ -19,7 +20,7 @@ namespace blip { kTickPeriod = clockData.TPCClock().TickPeriod(); kNominalRecombFactor = ModBoxRecomb(fCalodEdx,kNominalEfield); kWion = 1000./util::kGeVToElectrons; - + // initialize channel list fBadChanMask .resize(8256,false); fBadChanMaskPerEvt = fBadChanMask; @@ -45,7 +46,7 @@ namespace blip { int NBadChansFromFile = std::count(fBadChanMask.begin(),fBadChanMask.end(),true); EvtBadChanCount = 0; - + printf("******************************************\n"); printf("Initializing BlipRecoAlg...\n"); printf(" - Efield: %.4f kV/cm\n",detProp.Efield()); @@ -147,18 +148,22 @@ namespace blip { //########################################################### void BlipRecoAlg::reconfigure( fhicl::ParameterSet const& pset ){ + fDebugMode = pset.get ("DebugMode",false); + // initialize MC flags (will be determined automatically // later after the first event is processed) isMC = false; isMCOverlay = false; fHitProducer = pset.get ("HitProducer",""); + fHitProducerOG = pset.get ("HitProducerOG",""); fHitProducerData = pset.get ("HitProducerData", "gaushit::DataRecoStage1Test"); fHitProducerOverlay = pset.get ("HitProducerOverlay", "gaushit::OverlayStage1a"); - fHitProducerOG = fHitProducerData; - if( fHitProducer=="" ) fHitProducer = fHitProducerData; - fHitTruthMatch = pset.get ("HitTruthMatch", "gaushitTruthMatch::OverlayRecoStage1b"); + if( fHitProducerOG=="" ) fHitProducerOG = fHitProducerData; + if( fHitProducer=="" ) fHitProducer = fHitProducerData; + fHitTruthMatch = pset.get ("HitTruthMatch", ""); //"gaushitTruthMatch::OverlayRecoStage1b"); fTrkProducer = pset.get ("TrkProducer", "pandoraInit"); + fTrkProducerCal = pset.get ("TrkProducerCal", "pandoracaliInit"); fGeantProducer = pset.get ("GeantProducer", "largeant"); fSimDepProducer = pset.get ("SimEDepProducer", "ionization"); fSimChanProducer = pset.get ("SimChanProducer", "driftWC:simpleSC"); @@ -193,8 +198,8 @@ namespace blip { fMinMatchedPlanes = pset.get ("MinMatchedPlanes", 2); fPickyBlips = pset.get ("PickyBlips", false); - fApplyTrkCylinderCut= pset.get ("ApplyTrkCylinderCut", false); - fCylinderRadius = pset.get ("CylinderRadius", 15); + //fApplyTrkCylinderCut= pset.get ("ApplyTrkCylinderCut", false); + //fCylinderRadius = pset.get ("CylinderRadius", 15); fIgnoreDataTrks = pset.get ("IgnoreDataTrks", false); fCaloPlane = pset.get ("CaloPlane", 2); @@ -210,7 +215,7 @@ namespace blip { fBadChanProducer = pset.get ("BadChanProducer", "nfspl1:badchannels"); fBadChanFile = pset.get ("BadChanFile", ""); fMinDeadWireGap = pset.get ("MinDeadWireGap", 1); - + //fKeepAllClusts[0] = pset.get ("KeepAllClustersInd", false); //fKeepAllClusts[1] = pset.get ("KeepAllClustersInd", false); //fKeepAllClusts[2] = pset.get ("KeepAllClustersCol", true); @@ -276,12 +281,15 @@ namespace blip { //===================================================== // Record PDG for every G4 Track ID //===================================================== - //std::map map_g4trkid_pdg; + map_g4trkid_index.clear(); map_g4trkid_pdg.clear(); map_g4trkid_chan.clear(); map_g4trkid_chan_energy.clear(); map_g4trkid_chan_charge.clear(); - for(size_t i = 0; iTrackId()] = plist[i]->PdgCode(); + for(size_t i = 0; iTrackId()] = plist[i]->PdgCode(); + map_g4trkid_index[plist[i]->TrackId()] = i; + } //std::map> map_g4trkid_chan; //std::map > map_g4trkid_chan_energy; //std::map > map_g4trkid_chan_charge; @@ -365,10 +373,7 @@ namespace blip { } } - - - - } + }// RunBlipTruth @@ -380,10 +385,12 @@ namespace blip { // this function is run. //########################################################### void BlipRecoAlg::RunBlipReco( const art::Event& evt ) { - - //std::cout<<"\n" - //<<"=========== BlipRecoAlg =========================\n" - //<<"Event "<()->GetProvider(); auto const& chanFilt = art::ServiceHandle()->GetProvider(); + // lifetime (ms) + kLifetime = lifetime_provider.Lifetime(); + //==================================================== // Update map of bad channels for this event //==================================================== @@ -457,7 +473,8 @@ namespace blip { // -- associations art::FindManyP fmtrk(hitHandle,evt,fTrkProducer); art::FindManyP fmtrkOG(hitHandleOG,evt,fTrkProducer); - + art::FindManyP fmcal(tracklistHandle,evt,fTrkProducerCal); + //=============================================================== // Map of each hit to its gaushit index (needed if the provided // hit collection is some filtered subset of gaushit, in order to @@ -485,7 +502,7 @@ namespace blip { //======================================= // Map track IDs to the index in the vector //======================================= - //std::cout<<"Looping over tracks...\n"; + if( fDebugMode ) std::cout<<"Looping over "< map_trkid_isMC; map_trkid_isMC.clear(); map_trkid_index.clear(); @@ -508,7 +525,7 @@ namespace blip { std::map> planehitsMap; int nhits_untracked = 0; - //std::cout<<"Looping over the hits... "<Channel(); @@ -558,9 +575,46 @@ namespace blip { //======================================== if( pinfo.size() ) { + // If specific association tag label was not provided, check which ones are + // in the event and identify which is most compatible with the hit collection. + // This allows (for example) the wiremod workflow to not require a separate FCL + // configuration file specifying a different association label. + // + // If a valid association is found, fHitTruthMatch is updated, so this check + // is done only once. This assumes the data files being analyzed all have consistent + // data product labels. + // + if( fHitTruthMatch.label() == "" ) { + + int best_match_count = 0; + + // Find all MCParticle <-> Hit association products in the event + auto tags = evt.getInputTags>(); + for (auto &tag : tags) { + if( tag.label() != "gaushitTruthMatch") continue; + art::FindMany fmhh(hitHandleOG,evt,tag); + + // is it valid? + if( !fmhh.isValid() ) continue; + + // how many matches are there? + int nmatches = 0; + for(size_t i=0; i hit assn: "< fmhh(hitHandleOG,evt,fHitTruthMatch); - + // Loop the hits for(size_t i=0; i pvec; - std::vector btvec; - fmhh.get(igh,pvec,btvec); - hitinfo[i].g4energy = 0; - hitinfo[i].g4charge = 0; - float maxQ = -9; - for(size_t j=0; jenergy; - hitinfo[i].g4charge += btvec.at(j)->numElectrons; - if( btvec.at(j)->numElectrons <= maxQ ) continue; - maxQ = btvec.at(j)->numElectrons; - hitinfo[i].g4trkid = pvec.at(j)->TrackId(); - hitinfo[i].g4pdg = pvec.at(j)->PdgCode(); - hitinfo[i].g4frac = btvec.at(j)->ideNFraction; - } + if( fmhh.isValid() ) { - // ### uB behavior as of Nov 2022 ### - // WireCell's detsim implements its gain "fudge factor" - // by scaling the SimChannel electrons instead of the gain - // response. So we need to correct for this effect to get - // accurate count of 'true' electrons collected on channel. - if( fSimGainFactor > 0 ) hitinfo[i].g4charge /= fSimGainFactor; - - /* - // Some old code to measure wire-by-wire thresholds - if( map_g4trkid_chan_energy[hitinfo[i].g4trkid][chan] > 0 ) { - double trueEnergyDep = map_g4trkid_chan_energy[hitinfo[i].g4trkid][chan]; - h_recoWireEff_num->Fill(trueEnergyDep);} - if( map_g4trkid_chan_charge[hitinfo[i].g4trkid][chan] > 0 ) { - double trueChargeDep = map_g4trkid_chan_charge[hitinfo[i].g4trkid][chan]; - h_recoWireEffQ_num->Fill(trueChargeDep);} - */ - } + if( fmhh.at(igh).size() ) { + std::vector pvec; + std::vector btvec; + fmhh.get(igh,pvec,btvec); + hitinfo[i].g4energy = 0; + hitinfo[i].g4charge = 0; + float maxQ = -9; + for(size_t j=0; jenergy; + hitinfo[i].g4charge += btvec.at(j)->numElectrons; + if( btvec.at(j)->numElectrons <= maxQ ) continue; + maxQ = btvec.at(j)->numElectrons; + hitinfo[i].g4trkid = pvec.at(j)->TrackId(); + hitinfo[i].g4pdg = pvec.at(j)->PdgCode(); + hitinfo[i].g4frac = btvec.at(j)->ideNFraction; + } + + // ### uB behavior as of Nov 2022 ### + // WireCell's detsim implements its gain "fudge factor" + // by scaling the SimChannel electrons instead of the gain + // response. So we need to correct for this effect to get + // accurate count of 'true' electrons collected on channel. + if( fSimGainFactor > 0 ) hitinfo[i].g4charge /= fSimGainFactor; + + /* + // Some old code to measure wire-by-wire thresholds + if( map_g4trkid_chan_energy[hitinfo[i].g4trkid][chan] > 0 ) { + double trueEnergyDep = map_g4trkid_chan_energy[hitinfo[i].g4trkid][chan]; + h_recoWireEff_num->Fill(trueEnergyDep);} + if( map_g4trkid_chan_charge[hitinfo[i].g4trkid][chan] > 0 ) { + double trueChargeDep = map_g4trkid_chan_charge[hitinfo[i].g4trkid][chan]; + h_recoWireEffQ_num->Fill(trueChargeDep);} + */ + } + + }//fmhh isValid // IF this hit was (a) matched to a track, and (b) matched to a truth // energy deposit, then keep the tally @@ -856,7 +914,7 @@ namespace blip { } } - + if( fDebugMode ) std::cout<<"Created "<5cm tracks in the event, find point + // of closest approach + //for(auto& trk : tracklist ){ + for (size_t itrk = 0; itrkLength() < fMaxHitTrkLength ) continue; if( fIgnoreDataTrks && !map_trkid_isMC[trk->ID()] ) continue; auto& a = trk->Vertex(); auto& b = trk->End(); TVector3 p1(a.X(), a.Y(), a.Z() ); TVector3 p2(b.X(), b.Y(), b.Z() ); - // TO-DO: if this track starts or ends at a TPC boundary, - // we should extend p1 or p2 to outside the AV to avoid blind spots - TVector3 bp = newBlip.Position; - float dToLine = BlipUtils::DistToLine(p1,p2,bp); - float d = dToLine; - if( dToLine < 0 ) d = std::min( (bp-p1).Mag(), (bp-p2).Mag() ); - if( d > 0 ) { + float L = (p2-p1).Mag(); + + float d = -9; + + // First determine if the blip is closer to an interior point + // of the track, or to one of the endpoints + float d1 = (newBlip.Position-p1).Mag(); + float d2 = (newBlip.Position-p2).Mag(); + if( fabs( pow(d1,2) - pow(d2,2) ) > pow(L,2) ){ + d = std::min(d1,d2); + + // if closer to an interior point, check track spacepoints + // and look for closest approach + } else if (fmcal.isValid()) { + float closestApproach = 9999; + //float maxDeviation = 0; + std::vector > caloObjs = fmcal.at(itrk); + for(auto &caloObj : caloObjs ) { + //std::cout<<" found "<XYZ().size()<<" 3D points for a track length of "<Length()<<" ("<XYZ().size()/trk->Length()<<")\n"; + for(auto &xyz : caloObj->XYZ() ) { + TVector3 sp(xyz.X(),xyz.Y(),xyz.Z()); + float dist = (newBlip.Position-sp).Mag(); + if( dist < closestApproach) closestApproach = dist; + //float deviation = BlipUtils::DistToLine(p1,p2,sp); + //if( deviation > maxDeviation ) maxDeviation = deviation; + } + } - // update closest trkdist - if( newBlip.ProxTrkDist < 0 || d < newBlip.ProxTrkDist ) { + d = closestApproach; + + + // if no spacepoints available, just project a line between start/end + // of the track and calculate the point of closest approach algebraically + } else { + d = BlipUtils::DistToLine(p1,p2,newBlip.Position); + } + + if (d<0) continue; + + // update closest trkdist + if( newBlip.ProxTrkDist < 0 || d < newBlip.ProxTrkDist ) { newBlip.ProxTrkDist = d; newBlip.ProxTrkID = trk->ID(); - } + } + // need to do some math to figure out if this is in // the 45 degreee "cone" relative to the start/end - if( dToLine > 0 && !newBlip.inCylinder && d < fCylinderRadius ) { - float angle1 = asin( d / (p1-bp).Mag() ) * 180./3.14159; - float angle2 = asin( d / (p2-bp).Mag() ) * 180./3.14159; - if( angle1 < 45. && angle2 < 45. ) newBlip.inCylinder = true; - } - } + //if( dToLine > 0 && !newBlip.inCylinder && d < fCylinderRadius ) { + // float angle1 = asin( d / (p1-bp).Mag() ) * 180./3.14159; + // float angle2 = asin( d / (p2-bp).Mag() ) * 180./3.14159; + // if( angle1 < 45. && angle2 < 45. ) newBlip.inCylinder = true; + //} + + }//endloop over trks - - if( fApplyTrkCylinderCut && newBlip.inCylinder ) continue; + + //if( fApplyTrkCylinderCut && newBlip.inCylinder ) continue; // In the case that a cluster appeared to be touching a track in // one of the 3 views, but after 3D evaluation is found to be positioned // far away from that track, we must update the "TouchTrk" status. if( (newBlip.ProxTrkID != newBlip.TouchTrkID ) - // || (newBlip.ProxTrkDist > 1.5*newBlip.dX ) - // || (newBlip.ProxTrkDist > 1.5*newBlip.dYZ ) ) - ) { + || (newBlip.ProxTrkDist > 1.5*newBlip.dX ) + || (newBlip.ProxTrkDist > 1.5*newBlip.dYZ ) ) + { newBlip.TouchTrkID = -9; } @@ -1091,7 +1185,10 @@ namespace blip { blips.push_back(newBlip); for(auto& hc : hcGroup ) { hitclust[hc.ID].BlipID = newBlip.ID; - for( auto& h : hc.HitIDs ) hitinfo[h].blipid = newBlip.ID; + for( auto h : hc.HitIDs ) { + hitinfo[h].blipid = newBlip.ID; + map_blip_hitids[newBlip.ID].push_back(h); + } } @@ -1099,6 +1196,8 @@ namespace blip { }//endloop over caloplane ("Plane A") clusters }//endif calo plane has clusters }//endloop over TPCs + + if( fDebugMode ) std::cout<<"Created "<= 0 ) continue; h_chan_nhits->Fill(wireReadout.PlaneWireToChannel(geo::WireID(0, 0, hitinfo[i].plane, hitinfo[i].wire))); - int clustid = hitinfo[i].clustid; if( clustid >= 0 ) { if( hitclust[clustid].NWires > 1 ) continue; @@ -1132,7 +1230,159 @@ namespace blip { //h_chan_nclusts->Fill(wireReadout.PlaneWireToChannel(hitinfo[i].plane,hitinfo[i].wire)); } + + + + //************************************************************************* + // Additional blip-level calculations + //************************************************************************* + for(size_t i=0; i map_btid_count; + for(auto bh : map_blip_hitids[blip.ID] ) { + if( hitinfo[bh].trkid < 0 ) continue; + nbtids++; + map_btid_count[hitinfo[bh].trkid]++; + } + auto max_val_it = std::max_element(std::begin(map_btid_count), std::end(map_btid_count), + [](const std::pair& p1, const std::pair& p2) { return p1.second < p2.second;}); + if (nbtids>0 && max_val_it != std::end(map_btid_count)) { + map_blip_trkID[blip.ID] = max_val_it->first; + map_blip_trkIDfrac[blip.ID] = float(max_val_it->second) / nhits; + } + + + // ================================================ + // Save the true blip into the object; + // each cluster must match to the same energy dep + // ================================================ + std::set set_edepids; + for(auto& hc : blip.clusters ) { + if( !hc.isValid ) continue; + if( hc.EdepID < 0 ) break; + set_edepids.insert( hc.EdepID ); + } + + // initialize some dummy values + map_blip_primaryPDG[blip.ID] = 0; + map_blip_primaryG4ID[blip.ID] = -99; + map_blip_ncategory[blip.ID] = -9; + blip.truth.LeadG4PDG = 0; + blip.truth.LeadG4ID = -99; + blip.truth.LeadG4Index = -99; + + if( set_edepids.size() == 1 ){ + + // save the true blip into the object + blip.truth = trueblips[*set_edepids.begin()]; + + // trace ancestry of this blip + auto part = BlipUtils::ReturnMCParticle(blip.truth.LeadG4ID); + int blipPDG = part.PdgCode(); + auto blipProc = part.Process(); + auto ancestors = BlipUtils::ReturnAllAncestors(blip.truth.LeadG4ID); + //std::cout<<"Blip "< 0 ) { + map_blip_ncategory[blip.ID] = 0; + + for(auto p : ancestors ) { + double x1 = p.Vx(); + double y1 = p.Vy(); + double z1 = p.Vz(); + double x2 = p.EndX(); + double y2 = p.EndY(); + double z2 = p.EndZ(); + if(fDebugMode) std::cout<<" - G4TrkID: "< 1 ) map_blip_ncategory[blip.ID] = 2; + else if ( !primaryneut && np == 1 ) map_blip_ncategory[blip.ID] = 3; + else if ( !primaryneut && np > 1 ) map_blip_ncategory[blip.ID] = 4; + } + + //---------------------------------------------- + // electron-based blips from gammas + else if ( abs(blipPDG) == 11 && blipProc != "primary" && ancestors.size() >= 2 ) { + + // if the immediate ancestor is a photon... + if( ancestors[0].PdgCode() == 22 ) { + + // Loop back over ancestors + for(size_t k=0; k0 ) { - float t = blip.Time*1e-3; - float tau = lifetime_provider.Lifetime(); - blip.ChargeCorr = std::max(0.,(double)blip.Charge) * exp( t / tau ); + if( fLifetimeCorr && blip.Time>0 && kLifetime > 0 ) { + //float t = blip.Time*1e-3; + //float tau = lifetime_provider.Lifetime(); + blip.ChargeCorr = std::max(0.,(double)blip.Charge) * exp( blip.Time*1e-3 / kLifetime ); } // --- SCE corrections --- @@ -1216,22 +1466,11 @@ namespace blip { // METHOD 2 (TODO) //std::cout<<"Calculating ESTAR energy dep... "<Interpolate(depEl, Efield); - - // ================================================ - // Save the true blip into the object; - // each cluster must match to the same energy dep - // ================================================ - std::set set_edepids; - for(auto& hc : blip.clusters ) { - if( !hc.isValid ) continue; - if( hc.EdepID < 0 ) break; - set_edepids.insert( hc.EdepID ); - } - if( set_edepids.size() == 1 ) - blip.truth = trueblips[*set_edepids.begin()]; - }//endloop over blip vector + + + }//End main blip reco function @@ -1274,8 +1513,8 @@ namespace blip { printf(" Charge ratio maximum : %.1f\n", fMatchMaxQRatio); printf(" Minimum match score : %.2f\n", fMatchMinScore); printf(" Ignoring data tracks? : %i\n", fIgnoreDataTrks); - printf(" Track-cylinder radius : %.1f cm\n", fCylinderRadius); - printf(" Applying cylinder cut? : %i\n", fApplyTrkCylinderCut); + //printf(" Track-cylinder radius : %.1f cm\n", fCylinderRadius); + //printf(" Applying cylinder cut? : %i\n", fApplyTrkCylinderCut); /* printf(" Min cluster overlap : "); diff --git a/ubreco/BlipReco/Alg/BlipRecoAlg.h b/ubreco/BlipReco/Alg/BlipRecoAlg.h index 2e974a83..69681eac 100644 --- a/ubreco/BlipReco/Alg/BlipRecoAlg.h +++ b/ubreco/BlipReco/Alg/BlipRecoAlg.h @@ -63,7 +63,6 @@ #include #include - namespace blip { //-------------------------------------------- @@ -108,7 +107,8 @@ namespace blip { //TH1D* h_recoWireEff_num; //TH1D* h_recoWireEffQ_denom; //TH1D* h_recoWireEffQ_num; - + + std::map map_g4trkid_index; std::map map_g4trkid_pdg; std::map> map_g4trkid_chan; std::map > map_g4trkid_chan_energy; @@ -120,10 +120,23 @@ namespace blip { float kNominalEfield; float kDriftVelocity; float kTickPeriod; - int kNumChannels; + //int kNumChannels; float kLifetime; art::InputTag fHitProducer; + + // Map of blip hit IDs + std::map> map_blip_hitids; + + // TrackID for every blip (if there's a pandora track that shares hits) + std::map map_blip_trkID; + std::map map_blip_trkIDfrac; + + // Primary particle that blip descends from + std::map map_blip_primaryPDG; + std::map map_blip_primaryG4ID; + std::map map_blip_ncategory; + private: @@ -133,11 +146,12 @@ namespace blip { art::InputTag fHitProducerOG; art::InputTag fHitTruthMatch; std::string fTrkProducer; + std::string fTrkProducerCal; std::string fGeantProducer; std::string fSimDepProducer; std::string fSimChanProducer; float fSimGainFactor; - //bool fDebugMode; + bool fDebugMode; float fTrueBlipMergeDist; bool fDoHitFiltering; float fMaxHitTrkLength; @@ -177,6 +191,8 @@ namespace blip { //bool keepAllClusts; //bool fKeepAllClusts[kNplanes]; + + //blip::BlipRNNAlg* fRNNAlg; // --- Calorimetry configs --- int fCaloPlane; @@ -187,7 +203,6 @@ namespace blip { float fModBoxA; float fModBoxB; - // --- Histograms --- //TH1D* h_chanstatus; diff --git a/ubreco/BlipReco/BlipAnaReader_module.cc b/ubreco/BlipReco/BlipAnaReader_module.cc index 49b8c0ed..a49ccf05 100644 --- a/ubreco/BlipReco/BlipAnaReader_module.cc +++ b/ubreco/BlipReco/BlipAnaReader_module.cc @@ -36,6 +36,9 @@ #include "ubevt/Database/UbooneElectronLifetimeService.h" #include "larevt/SpaceChargeServices/SpaceChargeService.h" #include "ubreco/BlipReco/Alg/BlipRecoAlg.h" +#include "ubreco/BlipReco/Utils/NuSelectionToolBase.h" +#include "ubreco/BlipReco/Utils/NuSelectionSCECorrections.h" +#include "ubreco/BlipReco/Utils/NuSelectionTrackShowerScoreFuncs.h" // C++ includes #include @@ -101,6 +104,15 @@ class BlipAnaReaderTreeDataStruct int badchans; // #bad chans according to wirecell int longtrks; // tracks > 5 cm + // --- MCTruth neutrino info --- + int mctruth_nu_pdg; // Neutrino PDG (if present, otherwise = 0) + int mctruth_nu_ccnc; // CC (0) or NC (1) + int mctruth_nu_mode; // interaction mode from Genie + float mctruth_nu_vtx_x; // vertex X + float mctruth_nu_vtx_y; // vertex Y + float mctruth_nu_vtx_z; // vertex Z + float mctruth_nu_KE; // kinetic energy + // --- 3D Blip information --- int nblips; // number of blips in event int blip_id[kMaxBlips]; // blip ID / index @@ -127,6 +139,17 @@ class BlipAnaReaderTreeDataStruct int blip_touchtrkid[kMaxBlips]; // track ID of touched track int blip_clustid[kNplanes][kMaxBlips]; // cluster ID per plane + // --- Reconstructed neutrino slice information (Pandora) -- + bool nu_isNeutrino; // neutrino slice identified by Pandora + vfloat_t nu_nuscore; // neutrino score + int nu_pfp_pdg; // PDG particle best matching with reco slice + float nu_reco_vtx_x; // reconstructed vertex X [cm] + float nu_reco_vtx_y; // reconstructed vertex Y [cm] + float nu_reco_vtx_z; // reconstructed vertex Z [cm] + vint_t nu_trk_id; // trackIDs for tracks in this PFP + vfloat_t nu_trk_score; // track scores for tracks in this PFP + vfloat_t nu_shwr_score; // shower scores in this PFP + // === Function for resetting data === void Clear(){ event = -999; // --- event-wide info --- @@ -136,7 +159,23 @@ class BlipAnaReaderTreeDataStruct badchans = -99; longtrks = -99; timestamp = -999; - //timestamp_hr = -999; + + mctruth_nu_pdg = 0; + mctruth_nu_ccnc = -9; + mctruth_nu_mode = -9; + mctruth_nu_vtx_x = -999; + mctruth_nu_vtx_y = -999; + mctruth_nu_vtx_z = -999; + mctruth_nu_KE = -999; + nu_isNeutrino = false; + nu_nuscore .clear(); + nu_pfp_pdg = -999; + nu_reco_vtx_x = -999; + nu_reco_vtx_y = -999; + nu_reco_vtx_z = -999; + nu_trk_score .clear(); + nu_trk_id .clear(); + nu_shwr_score .clear(); nblips = 0; FillWith(blip_id, -9); @@ -164,6 +203,8 @@ class BlipAnaReaderTreeDataStruct void MakeTree(){ art::ServiceHandle tfs; + auto vf = "std::vector"; + auto vi = "std::vector"; evtTree = tfs->make(treeName.c_str(),"analysis tree"); evtTree->Branch("event",&event,"event/I"); @@ -174,6 +215,23 @@ class BlipAnaReaderTreeDataStruct evtTree->Branch("lifetime",&lifetime,"lifetime/F"); evtTree->Branch("badchans",&badchans,"badchans/I"); evtTree->Branch("longtrks",&longtrks,"longtrks/I"); + evtTree->Branch("mctruth_nu_pdg",&mctruth_nu_pdg,"mctruth_nu_pdg/I"); + evtTree->Branch("mctruth_nu_ccnc",&mctruth_nu_ccnc,"mctruth_nu_ccnc/I"); + evtTree->Branch("mctruth_nu_mode",&mctruth_nu_mode,"mctruth_nu_mode/I"); + evtTree->Branch("mctruth_nu_vtx_x",&mctruth_nu_vtx_x,"mctruth_nu_vtx_x/F"); + evtTree->Branch("mctruth_nu_vtx_y",&mctruth_nu_vtx_y,"mctruth_nu_vtx_y/F"); + evtTree->Branch("mctruth_nu_vtx_z",&mctruth_nu_vtx_z,"mctruth_nu_vtx_z/F"); + evtTree->Branch("mctruth_nu_KE",&mctruth_nu_KE,"mctruth_nu_KE/F"); + evtTree->Branch("nu_isNeutrino",&nu_isNeutrino,"nu_isNeutrino/O"); + //evtTree->Branch("nu_nuscore",&nu_nuscore,"nu_nuscore/F"); + evtTree->Branch("nu_nuscore", vf, &nu_nuscore); + evtTree->Branch("nu_pfp_pdg",&nu_pfp_pdg,"nu_pfp_pdg/I"); + evtTree->Branch("nu_reco_vtx_x",&nu_reco_vtx_x,"nu_reco_vtx_x/F"); + evtTree->Branch("nu_reco_vtx_y",&nu_reco_vtx_y,"nu_reco_vtx_y/F"); + evtTree->Branch("nu_reco_vtx_z",&nu_reco_vtx_z,"nu_reco_vtx_z/F"); + evtTree->Branch("nu_trk_id", vi, &nu_trk_id); + evtTree->Branch("nu_trk_score", vf, &nu_trk_score); + evtTree->Branch("nu_shwr_score", vf, &nu_shwr_score); evtTree->Branch("nblips",&nblips,"nblips/I"); evtTree->Branch("blip_nplanes",blip_nplanes,"blip_nplanes[nblips]/I"); evtTree->Branch("blip_x",blip_x,"blip_x[nblips]/F"); @@ -229,6 +287,27 @@ class BlipAnaReader : public art::EDAnalyzer int fNum3DBlips = 0; int fNum3DBlips3Plane = 0; int fNum3DBlipsTrue = 0; + + // --- Neutrino selection tools + using ProxyPfpColl_t = selection::ProxyPfpColl_t; + using ProxyPfpElem_t = selection::ProxyPfpElem_t; + art::InputTag fPFPproducer; + art::InputTag fCLSproducer; // cluster associated to PFP + art::InputTag fSLCproducer; // slice associated to PFP + art::InputTag fHITproducer; // hit associated to cluster + art::InputTag fSHRproducer; // shower associated to PFP + art::InputTag fVTXproducer; // vertex associated to PFP + art::InputTag fPCAproducer; // PCAxis associated to PFP + art::InputTag fMCTproducer; + art::InputTag fTRKproducer; + + //void BuildPFPMap(const ProxyPfpColl_t&); + //void AddDaughters(const ProxyPfpElem_t &pfp_pxy, + // const ProxyPfpColl_t &pfp_pxy_col, + // std::vector &slice_v); + + // a map linking the PFP Self() attribute used for hierarchy building to the PFP index in the event record + std::map _pfpmap; // --- Histograms --- TH1D* h_nblips; @@ -290,9 +369,20 @@ BlipAnaReader::BlipAnaReader(fhicl::ParameterSet const& pset) : fData = new BlipAnaReaderTreeDataStruct(); fData ->Clear(); fData ->MakeTree(); - + // initialize histograms InitializeHistograms(); + + fPFPproducer = pset.get("PFPproducer","pandora"); + fSHRproducer = pset.get("SHRproducer","shrreco3d"); + fHITproducer = pset.get("HITproducer","pandora"); + fVTXproducer = pset.get("VTXproducer","pandora"); + fPCAproducer = pset.get("PCAproducer","pandora"); + fCLSproducer = pset.get("CLSproducer","pandora"); + fSLCproducer = pset.get("SLCproducer","pandora"); + fMCTproducer = pset.get("MCTproducer","generator"); + fTRKproducer = pset.get("TRKproducer","pandora"); + } BlipAnaReader::~BlipAnaReader(){} @@ -332,6 +422,140 @@ void BlipAnaReader::analyze(const art::Event& evt) <<"Event "< > truthHandle; + std::vector > truthlist; + if (evt.getByLabel("generator",truthHandle)) + art::fill_ptr_vector(truthlist, truthHandle); + if( truthlist.size() > 0 ) { + auto& nu = truthlist[0]->GetNeutrino(); + auto& part = truthlist[0]->GetParticle(0); + auto PDG = fabs(part.PdgCode()); + if ( (PDG == 12) || (PDG == 14) ) { + fData->mctruth_nu_pdg = part.PdgCode(); + fData->mctruth_nu_ccnc = nu.CCNC(); + fData->mctruth_nu_mode = nu.Mode(); + fData->mctruth_nu_vtx_x = part.EndPosition()[0]; + fData->mctruth_nu_vtx_y = part.EndPosition()[1]; + fData->mctruth_nu_vtx_z = part.EndPosition()[2]; + fData->mctruth_nu_KE = /*GeV->MeV*/1e3 * (part.E()-part.Mass()); + } + } + + + //======================================================= + // Check if the event contains neutrino information + // (much of this copied from ubana/searchingfornues) + //======================================================= + // -- PFPs + art::Handle< std::vector > pfpHandle; + std::vector > pfplist; + if (evt.getByLabel(fPFPproducer,pfpHandle)) + art::fill_ptr_vector(pfplist, pfpHandle); + + // -- associated tracks/vertex + if( pfplist.size() ) { + // grab PFParticles in event + ProxyPfpColl_t const &pfp_proxy = proxy::getCollection>(evt, fPFPproducer, + proxy::withAssociated(fPFPproducer), + proxy::withAssociated(fCLSproducer), + proxy::withAssociated(fSLCproducer), + proxy::withAssociated(fTRKproducer), + proxy::withAssociated(fVTXproducer), + proxy::withAssociated(fPCAproducer), + proxy::withAssociated(fSHRproducer), + proxy::withAssociated(fPFPproducer)); + selection::BuildPFPMap(pfp_proxy); + // loopthrough PFParticles + for (const ProxyPfpElem_t &pfp_pxy : pfp_proxy) + { + // get metadata for this PFP + const auto &pfParticleMetadataList = pfp_pxy.get(); + + // find neutrino candidate + if (pfp_pxy->IsPrimary() == false) continue; + auto PDG = fabs(pfp_pxy->PdgCode()); + fData->nu_pfp_pdg=PDG; + if ( (PDG == 12) || (PDG == 14) ) + { + //std::cout<<"Found a neutrino PFP\n"; + if (pfParticleMetadataList.size() != 0) + { + for (unsigned int j = 0; j < pfParticleMetadataList.size(); ++j) + { + const art::Ptr &pfParticleMetadata(pfParticleMetadataList.at(j)); + auto pfParticlePropertiesMap = pfParticleMetadata->GetPropertiesMap(); + if (!pfParticlePropertiesMap.empty()) + { + for (std::map::const_iterator it = pfParticlePropertiesMap.begin(); it != pfParticlePropertiesMap.end(); ++it) + { + if( it->first == "IsNeutrino" ) fData->nu_isNeutrino = it->second; + if( it->first == "NuScore" ) fData->nu_nuscore.push_back(it->second); + } + } + } + } // if PFP metadata exists! + + + // Get vertex info + double xyz[3] = {}; + auto vtx = pfp_pxy.get(); + if (vtx.size() == 1) + { + // save vertex to array + vtx.at(0)->XYZ(xyz); + auto nuvtx = TVector3(xyz[0], xyz[1], xyz[2]); + float _reco_nu_vtx_sce[3]; + nuselection::ApplySCECorrectionXYZ(nuvtx.X(),nuvtx.Y(),nuvtx.Z(), _reco_nu_vtx_sce); + fData->nu_reco_vtx_x = _reco_nu_vtx_sce[0]; + fData->nu_reco_vtx_y = _reco_nu_vtx_sce[1]; + fData->nu_reco_vtx_z = _reco_nu_vtx_sce[2]; + //std::cout<<"Vertex: "< slice_pfp_v; + selection::AddDaughters(pfp_pxy, pfp_proxy, slice_pfp_v); + //std::cout << "This slice has " << slice_pfp_v.size() << " daughter PFParticles" << std::endl; + // create list of tracks and showers associated to this slice + std::vector trkscore_v; + std::vector trkid_v; + std::vector shwrscore_v; + + for (auto pfp : slice_pfp_v) + { + auto const &ass_trk_v = pfp.get(); + auto const &ass_shr_v = pfp.get(); + float score = nuselection::GetTrackShowerScore(pfp); + if (ass_trk_v.size() == 1) { + trkid_v .push_back(ass_trk_v.at(0)->ID()); + trkscore_v.push_back(score); + } + if (ass_shr_v.size() == 1) { + shwrscore_v.push_back(score); + } + } // for all PFParticles in the slice + //std::cout<<" - "<nu_trk_id .push_back(trkid_v[itrk]); + fData->nu_trk_score .push_back(trkscore_v[itrk]); + } + for(size_t ishwr = 0; ishwr < shwrscore_v.size(); ishwr++){ + fData->nu_shwr_score .push_back(shwrscore_v[ishwr]); + } + + }//if PDG of neutrino + }//end loop over PFPs + } + //=========================================== // Check if blip objects were saved to the event; diff --git a/ubreco/BlipReco/BlipAna_module.cc b/ubreco/BlipReco/BlipAna_module.cc index 5aa2f868..614d2901 100644 --- a/ubreco/BlipReco/BlipAna_module.cc +++ b/ubreco/BlipReco/BlipAna_module.cc @@ -292,7 +292,7 @@ class BlipAnaTreeDataStruct int blip_proxtrkid[kMaxBlips]; // index of nearest trk bool blip_touchtrk[kMaxBlips]; // is blip touching track? int blip_touchtrkid[kMaxBlips]; // track ID of touched track - bool blip_incylinder[kMaxBlips]; // is blip within a cylinder near a track + //bool blip_incylinder[kMaxBlips]; // is blip within a cylinder near a track int blip_clustid[kNplanes][kMaxBlips]; // cluster ID per plane // --- Reconstructed neutrino slice information (Pandora) -- @@ -483,7 +483,7 @@ class BlipAnaTreeDataStruct FillWith(blip_proxtrkid, -9); FillWith(blip_touchtrk, false); FillWith(blip_touchtrkid, -9); - FillWith(blip_incylinder, false); + //FillWith(blip_incylinder, false); FillWith(blip_edepid, -9); FillWith(blip_g4id, -9); for(int i=0; iBranch("blip_energy",blip_energy,"blip_energy[nblips]/F"); evtTree->Branch("blip_yzcorr",blip_yzcorr,"blip_yzcorr[nblips]/F"); //evtTree->Branch("blip_energyTrue",blip_energyTrue,"blip_energyTrue[nblips]/F"); - evtTree->Branch("blip_incylinder",blip_incylinder,"blip_incylinder[nblips]/O"); + //evtTree->Branch("blip_incylinder",blip_incylinder,"blip_incylinder[nblips]/O"); evtTree->Branch("blip_proxtrkdist",blip_proxtrkdist,"blip_proxtrkdist[nblips]/F"); evtTree->Branch("blip_touchtrk",blip_touchtrk,"blip_touchtrk[nblips]/O"); if( saveTrkInfo ) { @@ -812,6 +812,10 @@ class BlipAna : public art::EDAnalyzer //std::vector> _analysisToolsVec; // --- Histograms --- + + TH1D* h_ncapture_gammaE; + TH1D* h_ncapture_gammaEsum; + TH1D* h_part_process; TH1D* h_nhits[kNplanes]; @@ -971,7 +975,10 @@ class BlipAna : public art::EDAnalyzer // MC histograms related to truth art::TFileDirectory dir_truth = dir_diag.mkdir("Truth"); - + + h_ncapture_gammaE = dir_truth.make("ncapture_gammaE", "Gammas from neutron capture;True gamma energy [MeV]",300,0,15); + h_ncapture_gammaEsum = dir_truth.make("ncapture_gammaEsum","Gammas from neutron capture;Summed gamma energy [MeV]",300,0,15); + h_part_process = dir_truth.make("part_process","MCParticle->Process()",5,0,5); auto xa = h_part_process->GetXaxis(); xa->SetBinLabel(1,"primary"); @@ -1462,6 +1469,8 @@ void BlipAna::analyze(const art::Event& evt) } + std::map< int, std::vector > map_neutron_gammas; + std::map< int, std::vector > map_neutron_gammaE; //==================================== // Save MCParticle information @@ -1479,7 +1488,7 @@ void BlipAna::analyze(const art::Event& evt) map_g4trkid_index[pPart->TrackId()] = i; total_depEnergy += pinfo[i].depEnergy; total_depElectrons += pinfo[i].depElectrons; - + // Save to TTree object if(ipart_depElectrons[i] = pinfo[i].depElectrons; fData->part_isPrimary[i] = pinfo[i].isPrimary; // check containment - bool startInAV = BlipUtils::IsPointInAV(pPart->Vx(),pPart->Vy(),pPart->Vz()); - bool endInAV = BlipUtils::IsPointInAV(pPart->EndPosition()[0],pPart->EndPosition()[1],pPart->EndPosition()[2]); + bool startInAV = BlipUtils::IsPointInAV(pPart->Vx(),pPart->Vy(),pPart->Vz(),2); + bool endInAV = BlipUtils::IsPointInAV(pPart->EndPosition()[0],pPart->EndPosition()[1],pPart->EndPosition()[2],2); if( startInAV && endInAV ) fData->part_isContained[i] = true; - if( fDebugMode && pPart->Process() != "muIoni" ) { PrintParticleInfo(i); printed++; } + + //----------------------------------------------- + // Check contained neutron captures + if( pPart->PdgCode() == 2112 && fData->part_isContained[i] && pPart->EndProcess() == "nCapture" ) { + map_neutron_gammas[pPart->TrackId()].clear(); + } + // Check for neutron capture products + if( pPart->PdgCode() == 22 && pPart->Process() == "nCapture" ) { + if( map_neutron_gammas.count(pPart->Mother()) ) { + map_neutron_gammas[pPart->Mother()].push_back(pPart->TrackId()); + map_neutron_gammaE[pPart->Mother()].push_back(pPart->P()*1e3); + } + } + //----------------------------------------------- + + } + } // endloop over G4 particles - + + + if( fDebugMode ) std::cout<<"True total energy deposited: "< 0 ) { + for(auto cascade : map_neutron_gammaE ) { + //std::cout<<" - "<Fill(gammaE); + //std::cout<Fill(sumE); + //std::cout<<"SumE = "<blip_proxtrkid[i] = blp.ProxTrkID; fData->blip_touchtrk[i] = (blp.TouchTrkID >= 0 ); fData->blip_touchtrkid[i] = blp.TouchTrkID; - fData->blip_incylinder[i] = blp.inCylinder; + //fData->blip_incylinder[i] = blp.inCylinder; fData->blip_charge[i] = blp.Charge; fData->blip_energy[i] = blp.Energy; fData->blip_yzcorr[i] = tpcCalib.YZdqdxCorrection(fCaloPlane,blp.Position.Y(),blp.Position.Z()); diff --git a/ubreco/BlipReco/CMakeLists.txt b/ubreco/BlipReco/CMakeLists.txt index 4cb531bb..d8e128cb 100644 --- a/ubreco/BlipReco/CMakeLists.txt +++ b/ubreco/BlipReco/CMakeLists.txt @@ -82,6 +82,7 @@ cet_build_plugin( add_subdirectory(Utils) add_subdirectory(Alg) +add_subdirectory(RNN) add_subdirectory(job) install_fhicl() install_source() diff --git a/ubreco/BlipReco/RNN/BlipRNNAlg.cc b/ubreco/BlipReco/RNN/BlipRNNAlg.cc new file mode 100644 index 00000000..f3fd8d40 --- /dev/null +++ b/ubreco/BlipReco/RNN/BlipRNNAlg.cc @@ -0,0 +1,166 @@ +#include "ubreco/BlipReco/RNN/BlipRNNAlg.h" + +namespace blip { + + //########################################################### + // Constructor + //########################################################### + BlipRNNAlg::BlipRNNAlg( fhicl::ParameterSet const& pset ): + device(torch::kCPU) + { + fModelFilename = pset.get ("ModelFilename", "model_blipRNN.pt"); + model_loaded=false; + loadModel(); + } + + //==================================================== + // Destructor + BlipRNNAlg::~BlipRNNAlg() + { + } + + //==================================================== + // Load model + bool BlipRNNAlg::loadModel() { + // Skip if already loaded with same path + if (model_loaded && loaded_model_path == fModelFilename ) { + std::cout << "Model already loaded, skipping...\n"; + return true; + } + try { + cet::search_path sp("FW_SEARCH_PATH"); + std::cout<<"Searching for file...\n"; + std::cout< BlipRNNAlg::predict(vvfloat_t const& blip_hit_seq){ + if( !model_loaded ) return {}; + try{ + + int nFeatures=5; + int nHits=blip_hit_seq.size(); + auto bliphits = torch::zeros({1, nHits, nFeatures}); + for(int i=0; i input; + input.push_back(bliphits); + input.push_back(length); + + // feed it to the model + at::Tensor output = model.forward(input).toTensor(); + + // get the result and normalize magnitude + auto accessor = output.accessor(); + TVector3 vdir(accessor[0][0],accessor[0][1],accessor[0][2]); + vdir.SetMag(1.); + + return { (float)vdir.X(), (float)vdir.Y(), (float)vdir.Z() }; + + } catch (const std::exception& e) { + std::cerr << "RNN Prediction failed: "< BlipRNNAlg::predict(blipobj::Blip const& blp, std::vector const& hitinfo){ + + // 3D direction (initialized to dummy) + std::vector dir{-9,-9,-9}; + + // Basic cuts: require blip be valid + if( !blp.isValid ) return dir; + + // Conversion factor for casting ticks into units of + // wire spacings (TODO: make this not hard-coded?) + float tick_to_us = 0.5; // microsecond + float drift_speed = 1.1; // mm per microsecond + float wire_sep = 3.0; // mm + float convfact = tick_to_us * drift_speed / wire_sep; + + // Collections of hit info info per plane + std::array, kNplanes > blip_pl_wire; // [plane] -> vector of wires + std::array, kNplanes > blip_pl_time; + std::array, kNplanes > blip_pl_rms; + std::array, kNplanes > blip_pl_amp; + + // Loop over the planes + int nhits = 0; + float sum_driftT = 0; + float min_driftT = 999999; + for(size_t i=0; i hit_features; + hit_features.push_back( blip_pl_rms[i][j] ); + hit_features.push_back( blip_pl_amp[i][j] ); + hit_features.push_back( blip_pl_wire[i][j] ); + hit_features.push_back( i ); + hit_features.push_back( blip_pl_time[i][j] ); + blip_hit_seq.push_back( hit_features ); + } + } + + return predict(blip_hit_seq); + + } + +} + + diff --git a/ubreco/BlipReco/RNN/BlipRNNAlg.h b/ubreco/BlipReco/RNN/BlipRNNAlg.h new file mode 100644 index 00000000..a5f231e4 --- /dev/null +++ b/ubreco/BlipReco/RNN/BlipRNNAlg.h @@ -0,0 +1,48 @@ +#ifndef BLIPRNNALG_H +#define BLIPRNNALG_H + +// Framework, libtorch, etc +#include "fhiclcpp/ParameterSet.h" +#include +#include + +// Blip data types +#include "ubreco/BlipReco/Utils/BlipCore.h" + + +typedef std::vector vfloat_t; +typedef std::vector vvfloat_t; + +namespace blip { + + //-------------------------------------------- + class BlipRNNAlg { + + public: + + //Constructor/destructor + BlipRNNAlg( fhicl::ParameterSet const& pset ); + BlipRNNAlg(); + ~BlipRNNAlg(); + + bool loadModel(); + vfloat_t predict(vvfloat_t const&); + vfloat_t predict(blipobj::Blip const&, std::vector const&); + + bool model_loaded; + std::string loaded_model_path; + + private: + + // FCL configs + std::string fModelFilename; + + // PyTorch things + torch::jit::script::Module model; + torch::Device device; + + }; + +} + +#endif diff --git a/ubreco/BlipReco/RNN/CMakeLists.txt b/ubreco/BlipReco/RNN/CMakeLists.txt new file mode 100644 index 00000000..093902bc --- /dev/null +++ b/ubreco/BlipReco/RNN/CMakeLists.txt @@ -0,0 +1,17 @@ +cet_make_library( + SOURCE BlipRNNAlg.cc + LIBRARIES + PUBLIC + art_root_io::TFileService_service + ubobj::DataOverlay + lardata::Utilities + nusimdata::SimulationBase + TorchScatter::TorchScatter +) + +install_headers() +install_fhicl() +install_source() + +file(GLOB model_file *.pt) +install_fw(LIST ${model_file}) diff --git a/ubreco/BlipReco/RNN/model_blipRNN.pt b/ubreco/BlipReco/RNN/model_blipRNN.pt new file mode 100644 index 00000000..01b1888d Binary files /dev/null and b/ubreco/BlipReco/RNN/model_blipRNN.pt differ diff --git a/ubreco/BlipReco/Utils/BlipCore.h b/ubreco/BlipReco/Utils/BlipCore.h new file mode 100644 index 00000000..b6e029ec --- /dev/null +++ b/ubreco/BlipReco/Utils/BlipCore.h @@ -0,0 +1,12 @@ +/////////////////////////////////////////////////////// +// BlipCore.h +// +// include-guard for 'DataTypes.h' +// +////////////////////////////////////////////////////// +#ifndef BLIPCORE_H_SEEN +#define BLIPCORE_H_SEEN + +#include "ubobj/Blip/DataTypes.h" + +#endif diff --git a/ubreco/BlipReco/Utils/BlipUtils.cc b/ubreco/BlipReco/Utils/BlipUtils.cc index a6e97a56..8965e61e 100644 --- a/ubreco/BlipReco/Utils/BlipUtils.cc +++ b/ubreco/BlipReco/Utils/BlipUtils.cc @@ -147,7 +147,6 @@ namespace BlipUtils { float w2 = pinfo.depEnergy/totE; tblip.Position = w1*tblip.Position + w2*pinfo.position; tblip.Time = w1*tblip.Time + w2*pinfo.time; - tblip.LeadCharge = pinfo.depElectrons; // ... if the particle isn't a match, show's over } else { return; @@ -158,8 +157,7 @@ namespace BlipUtils { tblip.NumElectrons+= std::max(0.,pinfo.numElectrons); auto const detProp = art::ServiceHandle()->DataForJob(); - float driftVel = detProp.DriftVelocity(detProp.Efield(0),detProp.Temperature()); - tblip.DriftTime = tblip.Position.X() / driftVel; + tblip.DriftTime = tblip.Position.X() / detProp.DriftVelocity(detProp.Efield(0),detProp.Temperature()); tblip.G4ChargeMap[part.TrackId()] += pinfo.depElectrons; if(pinfo.depElectrons > tblip.LeadCharge ) { @@ -434,12 +432,42 @@ namespace BlipUtils { return newblip; } + + MCP_t ReturnMCParticle(int particleID){ + art::ServiceHandle pi_serv; + return pi_serv->TrackIdToParticle(particleID); + } + + MCPVec_t ReturnAllAncestors(int particleID) { + MCPVec_t out; + art::ServiceHandle pi_serv; + const sim::ParticleList& plist = pi_serv->ParticleList(); + if( !plist.HasParticle(particleID) ) return out; + auto p = pi_serv->TrackIdToParticle(particleID); + while( particleID > 0 ){ + if( !plist.HasParticle(p.Mother() ) ) { return out; } + p = pi_serv->TrackIdToParticle(p.Mother()); + out.push_back(p); + } + return out; + } + + /* + MCPVec_t ReturnAllDaughters(int particleID) { + MCPVec_t out; + art::ServiceHandle pi_serv; + const sim::ParticleList& plist = pi_serv->ParticleList(); + // under construction + for( + return out; + } + */ //==================================================================== // Function to determine if a particle descended from another particle. // Allows option to break lineage at photons for contiguous parentage. - bool IsAncestorOf(int particleID, int ancestorID, bool breakAtPhots = false){ + bool IsAncestorOf(int particleID, int ancestorID, bool breakAtPhots){ art::ServiceHandle pi_serv; const sim::ParticleList& plist = pi_serv->ParticleList(); if( particleID == ancestorID ) return true; @@ -671,12 +699,19 @@ namespace BlipUtils { } //=========================================================================== - bool IsPointInAV(float x, float y, float z){ + bool IsPointInAV(float x, float y, float z, float margin){ // Get geo boundaries double xmin, xmax, ymin, ymax, zmin, zmax; GetGeoBoundaries(xmin,xmax,ymin,ymax,zmin,zmax); - + + // add 2cm margin + if(margin){ + xmin += margin; xmax -= margin; + ymin += margin; ymax -= margin; + zmin += margin; zmax -= margin; + } + if( x >= xmin && x <= xmax && y >= ymin && y <= ymax && z >= zmin && z <= zmax ) { @@ -687,8 +722,8 @@ namespace BlipUtils { } - bool IsPointInAV(TVector3& v){ - return IsPointInAV(v.X(), v.Y(), v.Z()); + bool IsPointInAV(TVector3& v,float margin){ + return IsPointInAV(v.X(), v.Y(), v.Z(), margin); } diff --git a/ubreco/BlipReco/Utils/BlipUtils.h b/ubreco/BlipReco/Utils/BlipUtils.h index 612cfc04..5e7afba5 100644 --- a/ubreco/BlipReco/Utils/BlipUtils.h +++ b/ubreco/BlipReco/Utils/BlipUtils.h @@ -30,11 +30,16 @@ #include #include -#include "ubobj/Blip/DataTypes.h" +// BlipReco data types +#include "ubreco/BlipReco/Utils/BlipCore.h" + +// ROOT #include "TH1D.h" typedef std::vector> SEDVec_t; +typedef std::vector MCPVec_t; +typedef simb::MCParticle MCP_t; namespace BlipUtils { @@ -68,19 +73,22 @@ namespace BlipUtils { //bool G4IdToMCTruth( int const, art::Ptr&); double PathLength(const simb::MCParticle&, TVector3&, TVector3&); double PathLength(const simb::MCParticle&); - bool IsAncestorOf(int, int, bool); + bool IsAncestorOf(int, int, bool breakAtPhots=false); double DistToBoundary(const recob::Track::Point_t&); double DistToLine(TVector3&, TVector3&, TVector3&); double DistToLine2D(TVector2&, TVector2&, TVector2&); void GetGeoBoundaries(double&,double&,double&,double&,double&,double&); - bool IsPointInAV(float,float,float); - bool IsPointInAV(TVector3&); + bool IsPointInAV(float x,float y,float z,float margin=0); + bool IsPointInAV(TVector3& p,float margin=0); bool IsPointAtBnd(float,float,float); bool IsPointAtBnd(TVector3&); void NormalizeHist(TH1D*); float FindMedian(std::vector&); float FindMean(std::vector&); - + MCP_t ReturnMCParticle(int); + + MCPVec_t ReturnAllAncestors(int); + //MCPVec_t ReturnAllDaughters(int); } #endif diff --git a/ubreco/BlipReco/Utils/NuSelectionToolBase.h b/ubreco/BlipReco/Utils/NuSelectionToolBase.h index 724ccc6b..b34bb6b0 100755 --- a/ubreco/BlipReco/Utils/NuSelectionToolBase.h +++ b/ubreco/BlipReco/Utils/NuSelectionToolBase.h @@ -33,6 +33,15 @@ namespace selection { using ProxyPfpElem_t = nuselection::ProxyPfpElem_t; using ProxyClusColl_t = nuselection::ProxyClusColl_t; using ProxyClusElem_t = nuselection::ProxyClusElem_t; + + // a map linking the PFP Self() attribute used for hierarchy building to the PFP index in the event record + std::map _pfpmap; + + void BuildPFPMap(const ProxyPfpColl_t &pfp_pxy_col); + void AddDaughters(const ProxyPfpElem_t &pfp_pxy, + const ProxyPfpColl_t &pfp_pxy_col, + std::vector &slice_v); + class SelectionToolBase { @@ -84,6 +93,42 @@ class SelectionToolBase { }; + + +void BuildPFPMap(const ProxyPfpColl_t &pfp_pxy_col) +{ + _pfpmap.clear(); + unsigned int p = 0; + for (const auto &pfp_pxy : pfp_pxy_col) + { _pfpmap[pfp_pxy->Self()] = p; p++; } + return; +} // BuildPFPMap + +void AddDaughters(const ProxyPfpElem_t &pfp_pxy, + const ProxyPfpColl_t &pfp_pxy_col, + std::vector &slice_v) +{ + auto daughters = pfp_pxy->Daughters(); + slice_v.push_back(pfp_pxy); + //std::cout << "\t PFP w/ PdgCode " << pfp_pxy->PdgCode() << " has " << daughters.size() << " daughters" << std::endl; + for (auto const &daughterid : daughters){ + if (_pfpmap.find(daughterid) == _pfpmap.end()) + { + //std::cout << "Did not find DAUGHTERID in map! error" << std::endl; + continue; + } + + // const art::Ptr pfp_pxy(pfp_pxy_col, _pfpmap.at(daughterid) ); + auto pfp_pxy2 = pfp_pxy_col.begin(); + for (size_t j = 0; j < _pfpmap.at(daughterid); ++j) + ++pfp_pxy2; + // const T& pfp_pxy2 = (pfp_pxy_col.begin()+_pfpmap.at(daughterid)); + AddDaughters(*pfp_pxy2, pfp_pxy_col, slice_v); + } // for all daughters + + return; +} // AddDaughters + } // selection namespace #endif diff --git a/ubreco/BlipReco/microboone_blipreco.fcl b/ubreco/BlipReco/microboone_blipreco.fcl index 5565cd5b..f434b862 100644 --- a/ubreco/BlipReco/microboone_blipreco.fcl +++ b/ubreco/BlipReco/microboone_blipreco.fcl @@ -52,8 +52,8 @@ microboone_blipalg: ClustMatchQDiffLimit: 10e3 #// if charge-diff >, clusters are subject to QRatio cut above [e-] MinMatchedPlanes: 2 #// minimum number of planes matched for 3D blip PickyBlips: false #// require 3 planes that all intersect at ~same point - ApplyTrkCylinderCut: false #// reject blips in track cone-cylinder regions - CylinderRadius: 15.0 #// 3D cylinder cut radius [cm] + #ApplyTrkCylinderCut: false #// reject blips in track cone-cylinder regions + #CylinderRadius: 15.0 #// 3D cylinder cut radius [cm] IgnoreDataTrks: false #// only consider MC tracks when determining blip-track distances CaloPlane: 2 #// plane used for calorimetry (2=collection) CalodEdx: 2.8 #// assumed dE/dx for MeV-scale recombination corrections