diff --git a/CMakeLists.txt b/CMakeLists.txt index f93a660ef..54621576a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ cmake_minimum_required(VERSION 3.20 FATAL_ERROR) -set(${PROJECT_NAME}_CMAKE_PROJECT_VERSION_STRING 10.14.02.02) +set(${PROJECT_NAME}_CMAKE_PROJECT_VERSION_STRING 10.20.03) find_package(cetmodules REQUIRED) project(sbndcode LANGUAGES CXX) diff --git a/sbndcode/CMakeLists.txt b/sbndcode/CMakeLists.txt index f90ed87a6..1bb4a3c03 100755 --- a/sbndcode/CMakeLists.txt +++ b/sbndcode/CMakeLists.txt @@ -38,6 +38,7 @@ add_subdirectory(FlashMatch) add_subdirectory(Filters) add_subdirectory(ToFStudies) +add_subdirectory(Calorimetry) # for wirecell add_subdirectory(WireCell) diff --git a/sbndcode/CRT/CRTAna/ADRIFT_module.cc b/sbndcode/CRT/CRTAna/ADRIFT_module.cc new file mode 100644 index 000000000..8725c472d --- /dev/null +++ b/sbndcode/CRT/CRTAna/ADRIFT_module.cc @@ -0,0 +1,1424 @@ +//////////////////////////////////////////////////////////////////////// +// Class: ADRIFT +// Plugin Type: analyzer +// File: ADRIFT_module.cc +// +// Author: Henry Lay (h.lay@sheffield.ac.uk) +// +// ADC +// Dynamic +// Range +// Improvement +// Facilitation +// Trees +//////////////////////////////////////////////////////////////////////// + +#include "art/Framework/Core/EDAnalyzer.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Principal/Run.h" +#include "art/Framework/Principal/SubRun.h" +#include "canvas/Utilities/InputTag.h" +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "art_root_io/TFileService.h" +#include "canvas/Persistency/Common/FindManyP.h" +#include "canvas/Persistency/Common/FindOneP.h" + +#include "TTree.h" +#include "TH1D.h" +#include "TF1.h" +#include "TFitResultPtr.h" +#include "TFitResult.h" +#include "TCanvas.h" +#include "TSystem.h" +#include "TStyle.h" +#include "TText.h" + +#include "artdaq-core/Data/RawEvent.hh" + +#include "sbnobj/SBND/CRT/FEBData.hh" +#include "sbnobj/SBND/CRT/CRTStripHit.hh" +#include "sbnobj/SBND/CRT/CRTCluster.hh" +#include "sbnobj/SBND/CRT/CRTSpacePoint.hh" +#include "sbnobj/SBND/CRT/CRTTrack.hh" + +#include "sbndcode/Geometry/GeometryWrappers/CRTGeoService.h" +#include "sbndcode/ChannelMaps/CRT/CRTChannelMapService.h" + +namespace sbnd { + namespace crt { + class ADRIFT; + } +} + + +class sbnd::crt::ADRIFT : public art::EDAnalyzer { +public: + explicit ADRIFT(fhicl::ParameterSet const& p); + // The compiler-generated destructor is fine for non-base + // classes without bare pointers or other resource use. + + // Plugins should not be copied or assigned. + ADRIFT(ADRIFT const&) = delete; + ADRIFT(ADRIFT&&) = delete; + ADRIFT& operator=(ADRIFT const&) = delete; + ADRIFT& operator=(ADRIFT&&) = delete; + + // Required functions. + void analyze(art::Event const& e) override; + + // Selected optional functions. + void beginJob() override; + void endJob() override; + +private: + + void MakeSaveDirectories(art::Event const &e); + void AnalyseFEBDatas(art::Event const &e, const int window); + void AnalyseStripHits(art::Event const &e, const int window); + void AnalyseSpacePoints(art::Event const &e, const int window); + void AnalyseTracks(art::Event const &e, const int window); + void ProcessEntry(const int ch, const int window); + void PedestalFit(TH1D* hADC, double &fit, double &std, double &chi2, bool &converged, + bool badChannel, const int window); + void PedestalPeak(TH1D* hADC, double &peak); + void Rate(TH1D* hADC, double &rate, int window, double &period); + void PeakPeak(TH1D* hADC, const double &ped, double &peak); + void PeakFit(TH1D* hADC, const double &peak, const double &ped, double &fit, + double &chi2, bool &converged, bool badChannel, const int window); + double Saturation(TH1D* hADC); + void ResetVars(); + void SaveHist(TH1D *hADC, std::string &saveDir, std::string saveName, int rebin, bool badChannel); + static double LanGau(double *x, double *par); + + art::ServiceHandle fCRTGeoService; + + // Inputs + std::string fFEBDataModuleLabel, fCRTStripHitModuleLabel, fCRTClusterModuleLabel, + fCRTSpacePointModuleLabel, fCRTTrackModuleLabel, fDAQHeaderModuleLabel, fDAQHeaderInstanceLabel, + fTopSaveDirectory; + + bool fOnly2HitSpacePoints, fSaveAllFits, fSaveBadFits, fSaveSubset, fFEBs, fStripHits, fSpacePoints, + fTracks, fTrackLA, fSaveROOTHists, fAnalysePE; + + double fTrackAngleLimit, fPullWindow, fReconstructionWindow; + + uint32_t fRawTSCorrection; + + std::vector> fUnixWindows; + + // Other Global Parameters + std::vector fNEvents; + + TTree* fChannelTree; + + uint64_t _unix_start, _unix_end; + int _n_events, _channel, _gdml_id, _mac5, _raw_channel, _tagger, _channel_status; + double _area, _y_average, _ped_calib, _gain_calib, _ped_fit, _ped_fit_std, _ped_fit_chi2, _ped_peak, + _ped_reset_fit, _ped_reset_fit_std, _ped_reset_fit_chi2, _ped_reset_peak, _raw_max_chan_rate, _sh_rate, _sp_rate, _tr_rate, _tr_lim_angle_rate, + _sh_peak_fit, _sh_peak_fit_chi2, _sh_peak_peak, _sh_pe_peak_fit, _sh_pe_peak_fit_chi2, _sh_pe_peak_peak, + _sh_sat_rate, _sh_sat_ratio_total, _sh_sat_ratio_peak, _sp_peak_fit, _sp_peak_fit_chi2, _sp_peak_peak, + _sp_pe_peak_fit, _sp_pe_peak_fit_chi2, _sp_pe_peak_peak, _sp_sat_rate, _sp_sat_ratio_total, _sp_sat_ratio_peak, + _tr_peak_fit, _tr_peak_fit_chi2, _tr_peak_peak, _tr_pe_peak_fit, _tr_pe_peak_fit_chi2, _tr_pe_peak_peak, + _tr_sat_rate, _tr_sat_ratio_total, _tr_sat_ratio_peak, _tr_lim_angle_peak_fit, _tr_lim_angle_peak_fit_chi2, _tr_lim_angle_peak_peak, + _tr_lim_angle_pe_peak_fit, _tr_lim_angle_pe_peak_fit_chi2, _tr_lim_angle_pe_peak_peak, _tr_lim_angle_sat_rate, + _tr_lim_angle_sat_ratio_total, _tr_lim_angle_sat_ratio_peak, _sh_ped_to_peak_fit, _sh_ped_reset_to_peak_fit, + _tr_by_length_peak_fit, _tr_by_length_peak_fit_chi2, _tr_by_length_peak_peak, + _tr_by_length_pe_peak_fit, _tr_by_length_pe_peak_fit_chi2, _tr_by_length_pe_peak_peak; + bool _horizontal, _ped_fit_converged, _ped_reset_fit_converged, _sh_peak_fit_converged, _sh_pe_peak_fit_converged, + _sp_peak_fit_converged, _sp_pe_peak_fit_converged, _tr_peak_fit_converged, _tr_pe_peak_fit_converged, _tr_lim_angle_peak_fit_converged, + _tr_lim_angle_pe_peak_fit_converged, _tr_by_length_peak_fit_converged, _tr_by_length_pe_peak_fit_converged; + + std::map> hADCPed, hADCPedReset, hADCMaxChan, hADCSH, hPESH, hADCSP, hPESP, hADCTr, hPETr, + hADCTrLA, hPETrLA, hADCTrByLength, hPETrByLength; + + std::map fPedestalSaveDirectory, fPedestalResetSaveDirectory, fPeakSaveDirectory, fBadPedestalSaveDirectory, + fBadPedestalResetSaveDirectory, fBadPeakSaveDirectory, fPedestalSubsetSaveDirectory, fPedestalResetSubsetSaveDirectory, + fStripHitSubsetSaveDirectory, fStripHitPESubsetSaveDirectory, fSpacePointSubsetSaveDirectory, fSpacePointPESubsetSaveDirectory, + fTrackSubsetSaveDirectory, fTrackPESubsetSaveDirectory, fTrackLASubsetSaveDirectory, fTrackLAPESubsetSaveDirectory, + fTrackByLengthSubsetSaveDirectory, fTrackByLengthPESubsetSaveDirectory; +}; + + +sbnd::crt::ADRIFT::ADRIFT(fhicl::ParameterSet const& p) + : EDAnalyzer{p} +{ + fFEBDataModuleLabel = p.get("FEBDataModuleLabel"); + fCRTStripHitModuleLabel = p.get("CRTStripHitModuleLabel"); + fCRTClusterModuleLabel = p.get("CRTClusterModuleLabel"); + fCRTSpacePointModuleLabel = p.get("CRTSpacePointModuleLabel"); + fCRTTrackModuleLabel = p.get("CRTTrackModuleLabel"); + fDAQHeaderModuleLabel = p.get("DAQHeaderModuleLabel"); + fDAQHeaderInstanceLabel = p.get("DAQHeaderInstanceLabel"); + fTopSaveDirectory = p.get("TopSaveDirectory"); + fOnly2HitSpacePoints = p.get("Only2HitSpacePoints"); + fSaveAllFits = p.get("SaveAllFits"); + fSaveBadFits = p.get("SaveBadFits"); + fSaveSubset = p.get("SaveSubset"); + fFEBs = p.get("FEBs"); + fStripHits = p.get("StripHits"); + fSpacePoints = p.get("SpacePoints"); + fTracks = p.get("Tracks"); + fTrackLA = p.get("TrackLA"); + fSaveROOTHists = p.get("SaveROOTHists"); + fAnalysePE = p.get("AnalysePE"); + fTrackAngleLimit = p.get("TrackAngleLimit"); + fPullWindow = p.get("PullWindow"); + fReconstructionWindow = p.get("ReconstructionWindow"); + fRawTSCorrection = p.get("RawTSCorrection"); + fUnixWindows = p.get>>("UnixWindows", { {0, std::numeric_limits::max()} }); + + art::ServiceHandle fs; + + fChannelTree = fs->make("channel_tree", ""); + fChannelTree->Branch("unix_start", &_unix_start); + fChannelTree->Branch("unix_end", &_unix_end); + fChannelTree->Branch("n_events", &_n_events); + fChannelTree->Branch("channel", &_channel); + fChannelTree->Branch("gdml_id", &_gdml_id); + fChannelTree->Branch("mac5", &_mac5); + fChannelTree->Branch("raw_channel", &_raw_channel); + fChannelTree->Branch("tagger", &_tagger); + fChannelTree->Branch("channel_status", &_channel_status); + fChannelTree->Branch("area", &_area); + fChannelTree->Branch("y_average", &_y_average); + fChannelTree->Branch("horizontal", &_horizontal); + fChannelTree->Branch("ped_calib", &_ped_calib); + fChannelTree->Branch("gain_calib", &_gain_calib); + if(fFEBs) + { + fChannelTree->Branch("ped_fit", &_ped_fit); + fChannelTree->Branch("ped_fit_std", &_ped_fit_std); + fChannelTree->Branch("ped_fit_chi2", &_ped_fit_chi2); + fChannelTree->Branch("ped_fit_converged", &_ped_fit_converged); + fChannelTree->Branch("ped_peak", &_ped_peak); + fChannelTree->Branch("ped_reset_fit", &_ped_reset_fit); + fChannelTree->Branch("ped_reset_fit_std", &_ped_reset_fit_std); + fChannelTree->Branch("ped_reset_fit_chi2", &_ped_reset_fit_chi2); + fChannelTree->Branch("ped_reset_fit_converged", &_ped_reset_fit_converged); + fChannelTree->Branch("ped_reset_peak", &_ped_reset_peak); + fChannelTree->Branch("raw_max_chan_rate", &_raw_max_chan_rate); + } + if(fStripHits) + { + fChannelTree->Branch("sh_rate", &_sh_rate); + fChannelTree->Branch("sh_peak_fit", &_sh_peak_fit); + fChannelTree->Branch("sh_peak_fit_chi2", &_sh_peak_fit_chi2); + fChannelTree->Branch("sh_peak_fit_converged", &_sh_peak_fit_converged); + fChannelTree->Branch("sh_peak_peak", &_sh_peak_peak); + fChannelTree->Branch("sh_sat_rate", &_sh_sat_rate); + fChannelTree->Branch("sh_sat_ratio_total", &_sh_sat_ratio_total); + fChannelTree->Branch("sh_sat_ratio_peak", &_sh_sat_ratio_peak); + fChannelTree->Branch("sh_ped_to_peak_fit", &_sh_ped_to_peak_fit); + fChannelTree->Branch("sh_ped_reset_to_peak_fit", &_sh_ped_reset_to_peak_fit); + + if(fAnalysePE) + { + fChannelTree->Branch("sh_pe_peak_fit", &_sh_pe_peak_fit); + fChannelTree->Branch("sh_pe_peak_fit_chi2", &_sh_pe_peak_fit_chi2); + fChannelTree->Branch("sh_pe_peak_fit_converged", &_sh_pe_peak_fit_converged); + fChannelTree->Branch("sh_pe_peak_peak", &_sh_pe_peak_peak); + } + } + if(fSpacePoints) + { + fChannelTree->Branch("sp_rate", &_sp_rate); + fChannelTree->Branch("sp_peak_fit", &_sp_peak_fit); + fChannelTree->Branch("sp_peak_fit_chi2", &_sp_peak_fit_chi2); + fChannelTree->Branch("sp_peak_fit_converged", &_sp_peak_fit_converged); + fChannelTree->Branch("sp_peak_peak", &_sp_peak_peak); + fChannelTree->Branch("sp_sat_rate", &_sp_sat_rate); + fChannelTree->Branch("sp_sat_ratio_total", &_sp_sat_ratio_total); + fChannelTree->Branch("sp_sat_ratio_peak", &_sp_sat_ratio_peak); + + if(fAnalysePE) + { + fChannelTree->Branch("sp_pe_peak_fit", &_sp_pe_peak_fit); + fChannelTree->Branch("sp_pe_peak_fit_chi2", &_sp_pe_peak_fit_chi2); + fChannelTree->Branch("sp_pe_peak_fit_converged", &_sp_pe_peak_fit_converged); + fChannelTree->Branch("sp_pe_peak_peak", &_sp_pe_peak_peak); + } + } + if(fTracks) + { + fChannelTree->Branch("tr_rate", &_tr_rate); + fChannelTree->Branch("tr_peak_fit", &_tr_peak_fit); + fChannelTree->Branch("tr_peak_fit_chi2", &_tr_peak_fit_chi2); + fChannelTree->Branch("tr_peak_fit_converged", &_tr_peak_fit_converged); + fChannelTree->Branch("tr_peak_peak", &_tr_peak_peak); + fChannelTree->Branch("tr_sat_rate", &_tr_sat_rate); + fChannelTree->Branch("tr_sat_ratio_total", &_tr_sat_ratio_total); + fChannelTree->Branch("tr_sat_ratio_peak", &_tr_sat_ratio_peak); + fChannelTree->Branch("tr_by_length_peak_fit", &_tr_by_length_peak_fit); + fChannelTree->Branch("tr_by_length_peak_fit_chi2", &_tr_by_length_peak_fit_chi2); + fChannelTree->Branch("tr_by_length_peak_fit_converged", &_tr_by_length_peak_fit_converged); + fChannelTree->Branch("tr_by_length_peak_peak", &_tr_by_length_peak_peak); + + if(fAnalysePE) + { + fChannelTree->Branch("tr_pe_peak_fit", &_tr_pe_peak_fit); + fChannelTree->Branch("tr_pe_peak_fit_chi2", &_tr_pe_peak_fit_chi2); + fChannelTree->Branch("tr_pe_peak_fit_converged", &_tr_pe_peak_fit_converged); + fChannelTree->Branch("tr_pe_peak_peak", &_tr_pe_peak_peak); + fChannelTree->Branch("tr_by_length_pe_peak_fit", &_tr_by_length_pe_peak_fit); + fChannelTree->Branch("tr_by_length_pe_peak_fit_chi2", &_tr_by_length_pe_peak_fit_chi2); + fChannelTree->Branch("tr_by_length_pe_peak_fit_converged", &_tr_by_length_pe_peak_fit_converged); + fChannelTree->Branch("tr_by_length_pe_peak_peak", &_tr_by_length_pe_peak_peak); + } + } + if(fTrackLA) + { + fChannelTree->Branch("tr_lim_angle_rate", &_tr_lim_angle_rate); + fChannelTree->Branch("tr_lim_angle_peak_fit", &_tr_lim_angle_peak_fit); + fChannelTree->Branch("tr_lim_angle_peak_fit_chi2", &_tr_lim_angle_peak_fit_chi2); + fChannelTree->Branch("tr_lim_angle_peak_fit_converged", &_tr_lim_angle_peak_fit_converged); + fChannelTree->Branch("tr_lim_angle_peak_peak", &_tr_lim_angle_peak_peak); + fChannelTree->Branch("tr_lim_angle_sat_rate", &_tr_lim_angle_sat_rate); + fChannelTree->Branch("tr_lim_angle_sat_ratio_total", &_tr_lim_angle_sat_ratio_total); + fChannelTree->Branch("tr_lim_angle_sat_ratio_peak", &_tr_lim_angle_sat_ratio_peak); + + if(fAnalysePE) + { + fChannelTree->Branch("tr_lim_angle_pe_peak_fit", &_tr_lim_angle_pe_peak_fit); + fChannelTree->Branch("tr_lim_angle_pe_peak_fit_chi2", &_tr_lim_angle_pe_peak_fit_chi2); + fChannelTree->Branch("tr_lim_angle_pe_peak_fit_converged", &_tr_lim_angle_pe_peak_fit_converged); + fChannelTree->Branch("tr_lim_angle_pe_peak_peak", &_tr_lim_angle_pe_peak_peak); + } + } + + for(uint window = 0; window < fUnixWindows.size(); ++window) + { + if(fSaveROOTHists) + { + for(int ch = 0; ch < 4480; ++ch) + { + if(fFEBs) + { + hADCPed[window][ch] = fs->make(Form("hADCPed_Window%i_Channel%i", window, ch), ";ADC;Readouts", 1000, -.5, 999.5); + hADCPedReset[window][ch] = fs->make(Form("hADCPedReset_Window%i_Channel%i", window, ch), ";ADC;Readouts", 1000, -.5, 999.5); + hADCMaxChan[window][ch] = fs->make(Form("hADCMaxChan_Window%i_Channel%i", window, ch), ";ADC;Readouts - Max Channel", 4300, -.5, 4299.5); + } + + if(fStripHits) + { + hADCSH[window][ch] = fs->make(Form("hADCSH_Window%i_Channel%i", window, ch), ";ADC;Strip Hits", 4300, -.5, 4299.5); + + if(fAnalysePE) + hPESH[window][ch] = fs->make(Form("hPESH_Window%i_Channel%i", window, ch), ";PE;Strip Hits", 4000, 0, 200); + } + + if(fSpacePoints) + { + hADCSP[window][ch] = fs->make(Form("hADCSP_Window%i_Channel%i", window, ch), ";ADC;Space Points", 4300, -.5, 4299.5); + + if(fAnalysePE) + hPESP[window][ch] = fs->make(Form("hPESP_Window%i_Channel%i", window, ch), ";PE;Space Points", 4000, 0, 200); + } + + if(fTracks) + { + hADCTr[window][ch] = fs->make(Form("hADCTr_Window%i_Channel%i", window, ch), ";ADC;Tracks", 4300, -.5, 4299.5); + hADCTrByLength[window][ch] = fs->make(Form("hADCTrByLength_Window%i_Channel%i", window, ch), ";ADC/Path Length;Tracks", 4300, -.5, 4299.5); + + if(fAnalysePE) + { + hPETr[window][ch] = fs->make(Form("hPETr_Window%i_Channel%i", window, ch), ";PE;Tracks", 4000, 0, 200); + hPETrByLength[window][ch] = fs->make(Form("hPETrByLength_Window%i_Channel%i", window, ch), ";PE/Path Length;Tracks", 4000, 0, 200); + } + } + + if(fTrackLA) + { + hADCTrLA[window][ch] = fs->make(Form("hADCTrLA_Window%i_Channel%i", window, ch), ";ADC;Tracks", 4300, -.5, 4299.5); + + if(fAnalysePE) + hPETrLA[window][ch] = fs->make(Form("hPETrLA_Window%i_Channel%i", window, ch), ";PE;Tracks", 4000, 0, 200); + } + } + } + else + { + for(int ch = 0; ch < 4480; ++ch) + { + if(fFEBs) + { + hADCPed[window][ch] = new TH1D(Form("hADCPed_Window%i_Channel%i", window, ch), ";ADC;Readouts", 1000, -.5, 999.5); + hADCPedReset[window][ch] = new TH1D(Form("hADCPedReset_Window%i_Channel%i", window, ch), ";ADC;Readouts", 1000, -.5, 999.5); + hADCMaxChan[window][ch] = new TH1D(Form("hADCMaxChan_Window%i_Channel%i", window, ch), ";ADC;Readouts - Max Channel", 4300, -.5, 4299.5); + } + + if(fStripHits) + { + hADCSH[window][ch] = new TH1D(Form("hADCSH_Window%i_Channel%i", window, ch), ";ADC;Strip Hits", 4300, -.5, 4299.5); + + if(fAnalysePE) + hPESH[window][ch] = new TH1D(Form("hPESH_Window%i_Channel%i", window, ch), ";PE;Strip Hits", 4000, 0, 200); + } + + if(fSpacePoints) + { + hADCSP[window][ch] = new TH1D(Form("hADCSP_Window%i_Channel%i", window, ch), ";ADC;Space Points", 4300, -.5, 4299.5); + + if(fAnalysePE) + hPESP[window][ch] = new TH1D(Form("hPESP_Window%i_Channel%i", window, ch), ";PE;Space Points", 4000, 0, 200); + } + + if(fTracks) + { + hADCTr[window][ch] = new TH1D(Form("hADCTr_Window%i_Channel%i", window, ch), ";ADC;Tracks", 4300, -.5, 4299.5); + hADCTrByLength[window][ch] = new TH1D(Form("hADCTrByLength_Window%i_Channel%i", window, ch), ";ADC/Path Length;Tracks", 4300, -.5, 4299.5); + + if(fAnalysePE) + { + hPETr[window][ch] = new TH1D(Form("hPETr_Window%i_Channel%i", window, ch), ";PE;Tracks", 4000, 0, 200); + hPETrByLength[window][ch] = new TH1D(Form("hPETrByLength_Window%i_Channel%i", window, ch), ";PE/Path Length;Tracks", 4000, 0, 200); + } + } + + if(fTrackLA) + { + hADCTrLA[window][ch] = new TH1D(Form("hADCTrLA_Window%i_Channel%i", window, ch), ";ADC;Tracks", 4300, -.5, 4299.5); + + if(fAnalysePE) + hPETrLA[window][ch] = new TH1D(Form("hPETrLA_Window%i_Channel%i", window, ch), ";PE;Tracks", 4000, 0, 200); + } + } + } + } + + for(uint i = 0; i < fUnixWindows.size(); ++i) + { + for(uint ii = i + 1; ii < fUnixWindows.size(); ++ii) + { + if(fUnixWindows[i].second > fUnixWindows[ii].first) + { + std::cout << "Unix windows overlap!" << std::endl; + throw std::exception(); + } + } + } +} + +void sbnd::crt::ADRIFT::analyze(art::Event const& e) +{ + // Get Raw TS + art::Handle DAQHeaderHandle; + e.getByLabel(fDAQHeaderModuleLabel, fDAQHeaderInstanceLabel, DAQHeaderHandle); + if(!DAQHeaderHandle.isValid()){ + std::cout << "RawEventHeader product " << fDAQHeaderModuleLabel << " - " << fDAQHeaderInstanceLabel << " not found..." << std::endl; + throw std::exception(); + } + artdaq::RawEvent rawHeaderEvent = artdaq::RawEvent(*DAQHeaderHandle); + uint64_t raw_ts = rawHeaderEvent.timestamp() - fRawTSCorrection; + + uint window = 0; + bool found = false; + + for(uint i = 0; i < fUnixWindows.size(); ++i) + { + if(raw_ts > fUnixWindows[i].first && raw_ts < fUnixWindows[i].second) + { + window = i; + found = true; + break; + } + } + + if(!found) + return; + + if(std::accumulate(fNEvents.begin(), fNEvents.end(), 0) == 0) + MakeSaveDirectories(e); + + if(fFEBs) + AnalyseFEBDatas(e, window); + + if(fStripHits) + AnalyseStripHits(e, window); + + if(fSpacePoints) + AnalyseSpacePoints(e, window); + + if(fTracks || fTrackLA) + AnalyseTracks(e, window); + + ++fNEvents[window]; +} + +void sbnd::crt::ADRIFT::MakeSaveDirectories(art::Event const &e) +{ + for(uint window = 0; window < fUnixWindows.size(); ++window) + { + std::string directory_structure = fUnixWindows.size() == 1 ? Form("run%i", e.run()) : Form("run%i/window%i", e.run(), window); + + if((fSaveAllFits || fSaveBadFits)) + { + gSystem->Exec(Form("mkdir -p %s", fTopSaveDirectory.c_str())); + + if(fFEBs) + { + fPedestalSaveDirectory[window] = Form("%s/%s/pedestals", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fPedestalSaveDirectory[window].c_str())); + + fPedestalResetSaveDirectory[window] = Form("%s/%s/pedestals_reset", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fPedestalResetSaveDirectory[window].c_str())); + + fBadPedestalSaveDirectory[window] = Form("%s/%s/pedestals/bad", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fBadPedestalSaveDirectory[window].c_str())); + + fBadPedestalResetSaveDirectory[window] = Form("%s/%s/pedestals_reset/bad", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fBadPedestalResetSaveDirectory[window].c_str())); + } + + if(fStripHits || fSpacePoints || fTracks || fTrackLA) + { + fPeakSaveDirectory[window] = Form("%s/%s/peaks", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fPeakSaveDirectory[window].c_str())); + + fBadPeakSaveDirectory[window] = Form("%s/%s/peaks/bad", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fBadPeakSaveDirectory[window].c_str())); + } + } + + if(fSaveSubset) + { + gSystem->Exec(Form("mkdir -p %s", fTopSaveDirectory.c_str())); + + if(fFEBs) + { + fPedestalSubsetSaveDirectory[window] = Form("%s/%s/subset/pedestals", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fPedestalSubsetSaveDirectory[window].c_str())); + + fPedestalResetSubsetSaveDirectory[window] = Form("%s/%s/subset/pedestals_reset", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fPedestalResetSubsetSaveDirectory[window].c_str())); + } + + if(fStripHits) + { + fStripHitSubsetSaveDirectory[window] = Form("%s/%s/subset/striphits", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fStripHitSubsetSaveDirectory[window].c_str())); + + if(fAnalysePE) + { + fStripHitPESubsetSaveDirectory[window] = Form("%s/%s/subset/striphits_pe", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fStripHitPESubsetSaveDirectory[window].c_str())); + } + } + + if(fSpacePoints) + { + fSpacePointSubsetSaveDirectory[window] = Form("%s/%s/subset/spacepoints", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fSpacePointSubsetSaveDirectory[window].c_str())); + + if(fAnalysePE) + { + fSpacePointPESubsetSaveDirectory[window] = Form("%s/%s/subset/spacepoints_pe", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fSpacePointPESubsetSaveDirectory[window].c_str())); + } + } + + if(fTracks) + { + fTrackSubsetSaveDirectory[window] = Form("%s/%s/subset/tracks", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fTrackSubsetSaveDirectory[window].c_str())); + + fTrackByLengthSubsetSaveDirectory[window] = Form("%s/%s/subset/tracks_by_length", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fTrackByLengthSubsetSaveDirectory[window].c_str())); + + if(fAnalysePE) + { + fTrackPESubsetSaveDirectory[window] = Form("%s/%s/subset/tracks_pe", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fTrackPESubsetSaveDirectory[window].c_str())); + + fTrackByLengthPESubsetSaveDirectory[window] = Form("%s/%s/subset/tracks_by_length_pe", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fTrackByLengthPESubsetSaveDirectory[window].c_str())); + } + } + + if(fTrackLA) + { + fTrackLASubsetSaveDirectory[window] = Form("%s/%s/subset/tracks_limited_angle", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fTrackLASubsetSaveDirectory[window].c_str())); + + if(fAnalysePE) + { + fTrackLAPESubsetSaveDirectory[window] = Form("%s/%s/subset/tracks_limited_angle_pe", fTopSaveDirectory.c_str(), directory_structure.c_str()); + gSystem->Exec(Form("mkdir -p %s", fTrackLAPESubsetSaveDirectory[window].c_str())); + } + } + } + } + + if((fSaveAllFits || fSaveBadFits || fSaveSubset)) + { + gStyle->SetOptStat(0); + gStyle->SetFrameLineWidth(2); + gStyle->SetTextFont(62); + gStyle->SetTextSize(0.07); + gStyle->SetLabelFont(62, "xyz"); + gStyle->SetLabelSize(0.05, "xyz"); + gStyle->SetTitleSize(0.05, "xyz"); + gStyle->SetTitleFont(62, "xyz"); + } +} + +void sbnd::crt::ADRIFT::AnalyseFEBDatas(art::Event const &e, const int window) +{ + // Get FEBDatas + art::Handle> FEBDataHandle; + e.getByLabel(fFEBDataModuleLabel, FEBDataHandle); + if(!FEBDataHandle.isValid()){ + std::cout << "FEBData product " << fFEBDataModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + std::vector> FEBDataVec; + art::fill_ptr_vector(FEBDataVec, FEBDataHandle); + + // Get FEBData to CRTStripHit Assns + art::FindManyP febDatasToStripHits(FEBDataHandle, e, fCRTStripHitModuleLabel); + + for(const art::Ptr &feb_data : FEBDataVec) + { + const std::vector> strip_hits = febDatasToStripHits.at(feb_data.key()); + const uint m5 = feb_data->Mac5(); + const uint flags = feb_data->Flags(); + + std::set mask_channels; + + for(const art::Ptr &strip_hit : strip_hits) + { + mask_channels.insert(strip_hit->Channel()); + mask_channels.insert(strip_hit->Channel() + 1); + + if(strip_hit->Channel() % 2) + std::cout << "ODD Strip Hit Channel Number" << std::endl; + } + + int max_adc = -1, max_chan = -1; + + for(int i = 0; i < 32; ++i) + { + if(feb_data->ADC(i) > max_adc) + { + max_adc = feb_data->ADC(i); + max_chan = i; + } + + const int ch = m5 * 32 + i; + + if(!mask_channels.count(ch)) + hADCPed[window][ch]->Fill(feb_data->ADC(i)); + + if(flags != 1 && flags != 3) + hADCPedReset[window][ch]->Fill(feb_data->ADC(i)); + } + + if((flags == 1 || flags == 3) && max_chan != -1) + hADCMaxChan[window][m5 * 32 + max_chan]->Fill(feb_data->ADC(max_chan)); + } +} + +void sbnd::crt::ADRIFT::AnalyseStripHits(art::Event const &e, const int window) +{ + // Get CRTStripHits + art::Handle> CRTStripHitHandle; + e.getByLabel(fCRTStripHitModuleLabel, CRTStripHitHandle); + if(!CRTStripHitHandle.isValid()){ + std::cout << "CRTStripHit product " << fCRTStripHitModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + std::vector> CRTStripHitVec; + art::fill_ptr_vector(CRTStripHitVec, CRTStripHitHandle); + + for(const art::Ptr &strip_hit : CRTStripHitVec) + { + if(strip_hit->Channel() % 2) + std::cout << "ODD Strip Hit Channel Number" << std::endl; + + CRTSiPMGeo sipm1 = fCRTGeoService->GetSiPM(strip_hit->Channel()); + CRTSiPMGeo sipm2 = fCRTGeoService->GetSiPM(strip_hit->Channel() + 1); + + hADCSH[window][strip_hit->Channel()]->Fill(strip_hit->ADC1() + sipm1.pedestal); + hADCSH[window][strip_hit->Channel() + 1]->Fill(strip_hit->ADC2() + sipm2.pedestal); + + if(fAnalysePE) + { + if((strip_hit->ADC1() + sipm1.pedestal) < 4089) + hPESH[window][strip_hit->Channel()]->Fill(strip_hit->ADC1() * sipm1.gain); + if((strip_hit->ADC2() + sipm2.pedestal) < 4089) + hPESH[window][strip_hit->Channel() + 1]->Fill(strip_hit->ADC2() * sipm2.gain); + } + } +} + +void sbnd::crt::ADRIFT::AnalyseSpacePoints(art::Event const &e, const int window) +{ + // Get CRTSpacePoints + art::Handle> CRTSpacePointHandle; + e.getByLabel(fCRTSpacePointModuleLabel, CRTSpacePointHandle); + if(!CRTSpacePointHandle.isValid()){ + std::cout << "CRTSpacePoint product " << fCRTSpacePointModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + std::vector> CRTSpacePointVec; + art::fill_ptr_vector(CRTSpacePointVec, CRTSpacePointHandle); + + // Get CRTClusters + art::Handle> CRTClusterHandle; + e.getByLabel(fCRTClusterModuleLabel, CRTClusterHandle); + if(!CRTClusterHandle.isValid()){ + std::cout << "CRTCluster product " << fCRTClusterModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + + // Get CRTSpacePoint to CRTCluster Assns + art::FindOneP spacepointsToClusters(CRTSpacePointHandle, e, fCRTSpacePointModuleLabel); + + // Get CRTCluster to CRTStripHit Assns + art::FindManyP clustersToStripHits(CRTClusterHandle, e, fCRTClusterModuleLabel); + + for(const art::Ptr &space_point : CRTSpacePointVec) + { + const art::Ptr cluster = spacepointsToClusters.at(space_point.key()); + + if(fOnly2HitSpacePoints && cluster->NHits() > 2) + continue; + + const std::vector> strip_hits = clustersToStripHits.at(cluster.key()); + + for(const art::Ptr &strip_hit : strip_hits) + { + if(strip_hit->Channel() % 2) + std::cout << "ODD Strip Hit Channel Number" << std::endl; + + CRTSiPMGeo sipm1 = fCRTGeoService->GetSiPM(strip_hit->Channel()); + CRTSiPMGeo sipm2 = fCRTGeoService->GetSiPM(strip_hit->Channel() + 1); + + hADCSP[window][strip_hit->Channel()]->Fill(strip_hit->ADC1() + sipm1.pedestal); + hADCSP[window][strip_hit->Channel() + 1]->Fill(strip_hit->ADC2() + sipm2.pedestal); + + if(fAnalysePE) + { + if((strip_hit->ADC1() + sipm1.pedestal) < 4089) + hPESP[window][strip_hit->Channel()]->Fill(strip_hit->ADC1() * sipm1.gain); + if((strip_hit->ADC2() + sipm2.pedestal) < 4089) + hPESP[window][strip_hit->Channel() + 1]->Fill(strip_hit->ADC2() * sipm2.gain); + } + } + } +} + +void sbnd::crt::ADRIFT::AnalyseTracks(art::Event const &e, const int window) +{ + // Get CRTTracks + art::Handle> CRTTrackHandle; + e.getByLabel(fCRTTrackModuleLabel, CRTTrackHandle); + if(!CRTTrackHandle.isValid()){ + std::cout << "CRTTrack product " << fCRTTrackModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + std::vector> CRTTrackVec; + art::fill_ptr_vector(CRTTrackVec, CRTTrackHandle); + + // Get CRTSpacePoints + art::Handle> CRTSpacePointHandle; + e.getByLabel(fCRTSpacePointModuleLabel, CRTSpacePointHandle); + if(!CRTSpacePointHandle.isValid()){ + std::cout << "CRTSpacePoint product " << fCRTSpacePointModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + + // Get CRTClusters + art::Handle> CRTClusterHandle; + e.getByLabel(fCRTClusterModuleLabel, CRTClusterHandle); + if(!CRTClusterHandle.isValid()){ + std::cout << "CRTCluster product " << fCRTClusterModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + + // Get CRTTrack to CRTSpacePoint Assns + art::FindManyP tracksToSpacePoints(CRTTrackHandle, e, fCRTTrackModuleLabel); + + // Get CRTSpacePoint to CRTCluster Assns + art::FindOneP spacepointsToClusters(CRTSpacePointHandle, e, fCRTSpacePointModuleLabel); + + // Get CRTCluster to CRTStripHit Assns + art::FindManyP clustersToStripHits(CRTClusterHandle, e, fCRTClusterModuleLabel); + + for(const art::Ptr &track : CRTTrackVec) + { + const std::vector> space_points = tracksToSpacePoints.at(track.key()); + + const geo::Vector_t tr_dir_vec = track->Direction(); + const TVector3 tr_dir = TVector3(tr_dir_vec.X(), tr_dir_vec.Y(), tr_dir_vec.Z()); + + for(const art::Ptr &space_point : space_points) + { + const art::Ptr cluster = spacepointsToClusters.at(space_point.key()); + + const std::vector> strip_hits = clustersToStripHits.at(cluster.key()); + const CRTTagger tagger = cluster->Tagger(); + TVector3 normal; + if(tagger == kBottomTagger || tagger == kTopLowTagger || tagger == kTopHighTagger) + normal = TVector3(0, 1, 0); + else if(tagger == kWestTagger || tagger == kEastTagger) + normal = TVector3(1, 0, 0); + else if(tagger == kSouthTagger || tagger == kNorthTagger) + normal = TVector3(0, 0, 1); + + const double angle = TMath::RadToDeg() * normal.Angle(tr_dir); + const double path_length = 1 / TMath::Cos(normal.Angle(tr_dir)); + + for(const art::Ptr &strip_hit : strip_hits) + { + if(strip_hit->Channel() % 2) + std::cout << "ODD Strip Hit Channel Number" << std::endl; + + CRTSiPMGeo sipm1 = fCRTGeoService->GetSiPM(strip_hit->Channel()); + CRTSiPMGeo sipm2 = fCRTGeoService->GetSiPM(strip_hit->Channel() + 1); + + if(fTracks) + { + hADCTr[window][strip_hit->Channel()]->Fill(strip_hit->ADC1() + sipm1.pedestal); + hADCTr[window][strip_hit->Channel() + 1]->Fill(strip_hit->ADC2() + sipm2.pedestal); + + if(fAnalysePE) + { + if((strip_hit->ADC1() + sipm1.pedestal) < 4089) + hPETr[window][strip_hit->Channel()]->Fill(strip_hit->ADC1() * sipm1.gain); + if((strip_hit->ADC2() + sipm2.pedestal) < 4089) + hPETr[window][strip_hit->Channel() + 1]->Fill(strip_hit->ADC2() * sipm2.gain); + } + + if((strip_hit->ADC1() + sipm1.pedestal) < 4089) + { + hADCTrByLength[window][strip_hit->Channel()]->Fill((strip_hit->ADC1() + sipm1.pedestal) / path_length); + + if(fAnalysePE && (strip_hit->ADC1() + sipm1.pedestal) < 4089) + hPETrByLength[window][strip_hit->Channel()]->Fill((strip_hit->ADC1() * sipm1.gain) / path_length); + } + if((strip_hit->ADC2() + sipm2.pedestal) < 4089) + { + hADCTrByLength[window][strip_hit->Channel() + 1]->Fill((strip_hit->ADC2() + sipm2.pedestal) / path_length); + + if(fAnalysePE && (strip_hit->ADC2() + sipm2.pedestal) < 4089) + hPETrByLength[window][strip_hit->Channel() + 1]->Fill((strip_hit->ADC2() * sipm2.gain) / path_length); + } + } + + if(fTrackLA && angle < fTrackAngleLimit) + { + hADCTrLA[window][strip_hit->Channel()]->Fill(strip_hit->ADC1() + sipm1.pedestal); + hADCTrLA[window][strip_hit->Channel() + 1]->Fill(strip_hit->ADC2() + sipm2.pedestal); + + if(fAnalysePE) + { + if((strip_hit->ADC1() + sipm1.pedestal) < 4089) + hPETrLA[window][strip_hit->Channel()]->Fill(strip_hit->ADC1() * sipm1.gain); + if((strip_hit->ADC2() + sipm2.pedestal) < 4089) + hPETrLA[window][strip_hit->Channel() + 1]->Fill(strip_hit->ADC2() * sipm2.gain); + } + } + } + } + } +} + +void sbnd::crt::ADRIFT::beginJob() +{ + fNEvents = std::vector(fUnixWindows.size(), 0); +} + +void sbnd::crt::ADRIFT::endJob() +{ + art::ServiceHandle ChannelMapService; + + for(int gdml_i = 0; gdml_i < 140; ++gdml_i) + { + ResetVars(); + + std::cout << "=== Processing module " << gdml_i << std::endl; + + uint mac5 = ChannelMapService->GetMAC5FromOfflineModuleID(gdml_i); + bool invert = ChannelMapService->GetInversionFromOfflineModuleID(gdml_i); + + for(int ch_i = 0; ch_i < 32; ++ch_i) + { + const int ch = ChannelMapService->ConstructOfflineChannelIDFromOfflineModuleIDAndOfflineLocalChannel(gdml_i, ch_i); + + _channel = ch; + _gdml_id = gdml_i; + _mac5 = mac5; + _raw_channel = invert ? 31 - ch_i : ch_i; + _tagger = fCRTGeoService->ChannelToTaggerEnum(ch); + _channel_status = fCRTGeoService->GetSiPM(ch).status; + _area = fCRTGeoService->StripArea(ch); + _y_average = fCRTGeoService->StripAverageY(ch); + _ped_calib = fCRTGeoService->GetSiPM(ch).pedestal; + _gain_calib = fCRTGeoService->GetSiPM(ch).gain; + _horizontal = _tagger == kBottomTagger || _tagger == kTopLowTagger || _tagger == kTopHighTagger; + + for(uint window = 0; window < fUnixWindows.size(); ++window) + ProcessEntry(ch, window); + } + } +} + +void sbnd::crt::ADRIFT::ProcessEntry(const int ch, const int window) +{ + _unix_start = fUnixWindows[window].first; + _unix_end = fUnixWindows[window].second; + _n_events = fNEvents[window]; + + if(fSaveSubset && (ch > 1471 && ch < 1728)) + { + if(fFEBs) + { + SaveHist(hADCPed[window][ch], fPedestalSubsetSaveDirectory[window], Form("pedestal_channel_%i", ch), 2, _channel_status); + SaveHist(hADCPedReset[window][ch], fPedestalResetSubsetSaveDirectory[window], Form("pedestal_reset_channel_%i", ch), 2, _channel_status); + } + + if(fStripHits) + { + SaveHist(hADCSH[window][ch], fStripHitSubsetSaveDirectory[window], Form("strip_hit_channel_%i", ch), 20, _channel_status); + + if(fAnalysePE) + SaveHist(hPESH[window][ch], fStripHitPESubsetSaveDirectory[window], Form("strip_hit_pe_channel_%i", ch), 20, _channel_status); + } + + if(fSpacePoints) + { + SaveHist(hADCSP[window][ch], fSpacePointSubsetSaveDirectory[window], Form("space_point_channel_%i", ch), 20, _channel_status); + if(fAnalysePE) + SaveHist(hPESP[window][ch], fSpacePointPESubsetSaveDirectory[window], Form("space_point_pe_channel_%i", ch), 20, _channel_status); + } + + if(fTracks) + { + SaveHist(hADCTr[window][ch], fTrackSubsetSaveDirectory[window], Form("track_channel_%i", ch), 20, _channel_status); + SaveHist(hADCTrByLength[window][ch], fTrackByLengthSubsetSaveDirectory[window], Form("track_by_length_channel_%i", ch), 20, _channel_status); + if(fAnalysePE) + { + SaveHist(hPETr[window][ch], fTrackPESubsetSaveDirectory[window], Form("track_pe_channel_%i", ch), 20, _channel_status); + SaveHist(hPETrByLength[window][ch], fTrackByLengthPESubsetSaveDirectory[window], Form("track_by_length_pe_channel_%i", ch), 20, _channel_status); + } + } + + if(fTrackLA) + { + SaveHist(hADCTrLA[window][ch], fTrackLASubsetSaveDirectory[window], Form("track_limited_angle_channel_%i", ch), 20, _channel_status); + if(fAnalysePE) + SaveHist(hPETrLA[window][ch], fTrackLAPESubsetSaveDirectory[window], Form("track_limited_angle_pe_channel_%i", ch), 20, _channel_status); + } + + } + + if(fFEBs) + { + PedestalFit(hADCPed[window][ch], _ped_fit, _ped_fit_std, _ped_fit_chi2, _ped_fit_converged, _channel_status, window); + PedestalPeak(hADCPed[window][ch], _ped_peak); + + PedestalFit(hADCPedReset[window][ch], _ped_reset_fit, _ped_reset_fit_std, _ped_reset_fit_chi2, _ped_reset_fit_converged, _channel_status, window); + PedestalPeak(hADCPedReset[window][ch], _ped_reset_peak); + + Rate(hADCMaxChan[window][ch], _raw_max_chan_rate, window, fPullWindow); + } + + if(fStripHits) + { + Rate(hADCSH[window][ch], _sh_rate, window, fReconstructionWindow); + + PeakPeak(hADCSH[window][ch], _ped_calib, _sh_peak_peak); + PeakFit(hADCSH[window][ch], _sh_peak_peak, _ped_calib, _sh_peak_fit, _sh_peak_fit_chi2, _sh_peak_fit_converged, _channel_status, window); + _sh_ped_to_peak_fit = _sh_peak_fit - _ped_fit; + _sh_ped_reset_to_peak_fit = _sh_peak_fit - _ped_reset_fit; + + if(fAnalysePE) + { + PeakPeak(hPESH[window][ch], _ped_calib, _sh_pe_peak_peak); + PeakFit(hPESH[window][ch], _sh_pe_peak_peak, _ped_calib, _sh_pe_peak_fit, _sh_pe_peak_fit_chi2, _sh_pe_peak_fit_converged, _channel_status, window); + } + + const double sh_sat = Saturation(hADCSH[window][ch]); + _sh_sat_rate = sh_sat / (fNEvents[window] * fReconstructionWindow); + _sh_sat_ratio_total = sh_sat / hADCSH[window][ch]->GetEntries(); + _sh_sat_ratio_peak = sh_sat / hADCSH[window][ch]->GetBinContent(hADCSH[window][ch]->FindBin(_sh_peak_peak)); + } + + if(fSpacePoints) + { + Rate(hADCSP[window][ch], _sp_rate, window, fReconstructionWindow); + + PeakPeak(hADCSP[window][ch], _ped_calib, _sp_peak_peak); + PeakFit(hADCSP[window][ch], _sp_peak_peak, _ped_calib, _sp_peak_fit, _sp_peak_fit_chi2, _sp_peak_fit_converged, _channel_status, window); + + if(fAnalysePE) + { + PeakPeak(hPESP[window][ch], _ped_calib, _sp_pe_peak_peak); + PeakFit(hPESP[window][ch], _sp_pe_peak_peak, _ped_calib, _sp_pe_peak_fit, _sp_pe_peak_fit_chi2, _sp_pe_peak_fit_converged, _channel_status, window); + } + + const double sp_sat = Saturation(hADCSP[window][ch]); + _sp_sat_rate = sp_sat / (fNEvents[window] * fReconstructionWindow); + _sp_sat_ratio_total = sp_sat / hADCSP[window][ch]->GetEntries(); + _sp_sat_ratio_peak = sp_sat / hADCSP[window][ch]->GetBinContent(hADCSP[window][ch]->FindBin(_sp_peak_peak)); + } + + if(fTracks && _tagger != kBottomTagger) + { + Rate(hADCTr[window][ch], _tr_rate, window, fReconstructionWindow); + + PeakPeak(hADCTr[window][ch], _ped_calib, _tr_peak_peak); + PeakFit(hADCTr[window][ch], _tr_peak_peak, _ped_calib, _tr_peak_fit, _tr_peak_fit_chi2, _tr_peak_fit_converged, _channel_status, window); + + PeakPeak(hADCTrByLength[window][ch], _ped_calib, _tr_by_length_peak_peak); + PeakFit(hADCTrByLength[window][ch], _tr_by_length_peak_peak, _ped_calib, _tr_by_length_peak_fit, _tr_by_length_peak_fit_chi2, _tr_by_length_peak_fit_converged, _channel_status, window); + + if(fAnalysePE) + { + PeakPeak(hPETr[window][ch], _ped_calib, _tr_pe_peak_peak); + PeakFit(hPETr[window][ch], _tr_pe_peak_peak, _ped_calib, _tr_pe_peak_fit, _tr_pe_peak_fit_chi2, _tr_pe_peak_fit_converged, _channel_status, window); + + PeakPeak(hPETrByLength[window][ch], _ped_calib, _tr_by_length_pe_peak_peak); + PeakFit(hPETrByLength[window][ch], _tr_by_length_pe_peak_peak, _ped_calib, _tr_by_length_pe_peak_fit, _tr_by_length_pe_peak_fit_chi2, _tr_by_length_pe_peak_fit_converged, _channel_status, window); + } + + const double tr_sat = Saturation(hADCTr[window][ch]); + _tr_sat_rate = tr_sat / (fNEvents[window] * fReconstructionWindow); + _tr_sat_ratio_total = tr_sat / hADCTr[window][ch]->GetEntries(); + _tr_sat_ratio_peak = tr_sat / hADCTr[window][ch]->GetBinContent(hADCTr[window][ch]->FindBin(_tr_peak_peak)); + } + + if(fTrackLA && _tagger != kBottomTagger) + { + Rate(hADCTrLA[window][ch], _tr_lim_angle_rate, window, fReconstructionWindow); + + PeakPeak(hADCTrLA[window][ch], _ped_calib, _tr_lim_angle_peak_peak); + PeakFit(hADCTrLA[window][ch], _tr_lim_angle_peak_peak, _ped_calib, _tr_lim_angle_peak_fit, _tr_lim_angle_peak_fit_chi2, _tr_lim_angle_peak_fit_converged, _channel_status, window); + + if(fAnalysePE) + { + PeakPeak(hPETrLA[window][ch], _ped_calib, _tr_lim_angle_pe_peak_peak); + PeakFit(hPETrLA[window][ch], _tr_lim_angle_pe_peak_peak, _ped_calib, _tr_lim_angle_pe_peak_fit, _tr_lim_angle_pe_peak_fit_chi2, _tr_lim_angle_pe_peak_fit_converged, _channel_status, window); + } + + const double tr_lim_angle_sat = Saturation(hADCTrLA[window][ch]); + _tr_lim_angle_sat_rate = tr_lim_angle_sat / (fNEvents[window] * fReconstructionWindow); + _tr_lim_angle_sat_ratio_total = tr_lim_angle_sat / hADCTrLA[window][ch]->GetEntries(); + _tr_lim_angle_sat_ratio_peak = tr_lim_angle_sat / hADCTrLA[window][ch]->GetBinContent(hADCTrLA[window][ch]->FindBin(_tr_lim_angle_peak_peak)); + } + + fChannelTree->Fill(); +} + +void sbnd::crt::ADRIFT::PedestalFit(TH1D* hADC, double &fit, double &std, double &chi2, bool &converged, + bool badChannel, const int window) +{ + TF1 *gaus = new TF1("gaus", "gaus", 0, 500); + const TString name = hADC->GetName(); + TString ch_name = name; + TString type = ""; + + const int window_log = window == 0 ? 0 : std::floor(std::log10(window)); + + if(name.Contains("Reset")) + { + ch_name.Remove(0, 27 + window_log + 1); + type = "reset"; + } + else + ch_name.Remove(0,22 + window_log + 1); + + int ch = std::stoi(ch_name.Data()); + + TH1D* hADC2 = (TH1D*) hADC->Clone(name + "_for_fit"); + hADC2->Rebin(5); + + TFitResultPtr fit_result = hADC2->Fit(gaus, "QRS"); + converged = !(bool)(int(fit_result)); + + if(!converged && !badChannel) + std::cout << "Pedestal fit has not converged - " << hADC->GetName() << std::endl; + + fit = gaus->GetParameter("Mean"); + std = gaus->GetParameter("Sigma"); + chi2 = gaus->GetChisquare() / gaus->GetNDF(); + + if(fSaveAllFits || (fSaveBadFits && !converged) || (fSaveSubset && ch > 1471 && ch < 1728)) + { + TCanvas *c = new TCanvas(Form("c%s", name.Data()), Form("c%s", name.Data())); + c->cd(); + + hADC2->SetLineColor(kOrange+2); + hADC2->SetLineWidth(2); + hADC2->Draw("histe"); + gaus->SetLineColor(kSpring-6); + gaus->Draw("same"); + + if(badChannel) + { + TText *t = new TText(.5, .75, "Bad Channel"); + t->SetTextSize(0.1); + t->SetTextColor(kRed); + t->Draw(); + } + + if(converged) + { + if(type.Contains("reset")) + { + c->SaveAs(Form("%s/pedestal_reset_fit_channel_%s.png", fPedestalResetSaveDirectory[window].c_str(), ch_name.Data())); + c->SaveAs(Form("%s/pedestal_reset_fit_channel_%s.pdf", fPedestalResetSaveDirectory[window].c_str(), ch_name.Data())); + } + else + { + c->SaveAs(Form("%s/pedestal_fit_channel_%s.png", fPedestalSaveDirectory[window].c_str(), ch_name.Data())); + c->SaveAs(Form("%s/pedestal_fit_channel_%s.pdf", fPedestalSaveDirectory[window].c_str(), ch_name.Data())); + } + } + else + { + if(type.Contains("reset")) + { + c->SaveAs(Form("%s/pedestal_reset_fit_channel_%s.png", fBadPedestalResetSaveDirectory[window].c_str(), ch_name.Data())); + c->SaveAs(Form("%s/pedestal_reset_fit_channel_%s.pdf", fBadPedestalResetSaveDirectory[window].c_str(), ch_name.Data())); + } + else + { + c->SaveAs(Form("%s/pedestal_fit_channel_%s.png", fBadPedestalSaveDirectory[window].c_str(), ch_name.Data())); + c->SaveAs(Form("%s/pedestal_fit_channel_%s.pdf", fBadPedestalSaveDirectory[window].c_str(), ch_name.Data())); + } + } + } +} + +void sbnd::crt::ADRIFT::PedestalPeak(TH1D* hADC, double &peak) +{ + const int bin = hADC->GetMaximumBin(); + + peak = hADC->GetBinCenter(bin); +} + +void sbnd::crt::ADRIFT::Rate(TH1D* hADC, double &rate, int window, double &period) +{ + rate = hADC->GetEntries() / (fNEvents[window] * period); +} + +void sbnd::crt::ADRIFT::PeakPeak(TH1D* hist, const double &ped, double &peak) +{ + int bin = 0; + double max = std::numeric_limits::lowest(); + + const TString name = hist->GetName(); + + const int start = name.Contains("PE") ? 0 : (int)ped + 20; + + for(int i = start; i < 4000; ++i) + { + if(hist->GetBinContent(i) > max) + { + max = hist->GetBinContent(i); + bin = i; + } + } + + peak = hist->GetBinCenter(bin); +} + +void sbnd::crt::ADRIFT::PeakFit(TH1D* hist, const double &peak, const double &ped, double &fit, + double &chi2, bool &converged, bool badChannel, const int window) +{ + const TString name = hist->GetName(); + const bool pe = name.Contains("PE"); + TString ch_name = name; + TString type = ""; + + const double start = pe ? 0 : ped + 20; + const double end = pe ? 200 : 4000; + const double width = pe ? 0.25 : 10; + const double sigma = pe ? 1 : 50; + + TF1 *langau = new TF1("langau", LanGau, start, end, 4); + double params[4] = { width, peak, hist->GetEntries(), sigma }; + langau->SetParameters(params); + + const int window_log = window == 0 ? 0 : std::floor(std::log10(window)); + + if(name.Contains("SH")) + { + const int end = pe ? 20 + window_log + 1 : 21 + window_log + 1; + ch_name.Remove(0, end); + type = "strip_hit"; + } + else if(name.Contains("SP")) + { + const int end = pe ? 20 + window_log + 1 : 21 + window_log + 1; + ch_name.Remove(0, end); + type = "space_point"; + } + else if(name.Contains("TrLA")) + { + const int end = pe ? 22 + window_log + 1 : 23 + window_log + 1; + ch_name.Remove(0, end); + type = "track_limited_angle"; + } + else if(name.Contains("TrByLength")) + { + const int end = pe ? 28 + window_log + 1 : 29 + window_log + 1; + ch_name.Remove(0, end); + type = "track_by_length"; + } + else if(name.Contains("Tr")) + { + const int end = pe ? 20 + window_log + 1 : 21 + window_log + 1; + ch_name.Remove(0, end); + type = "track"; + } + else + std::cout << "Cannot identify histogram type (" << name << ")" << std::endl; + + int ch = std::stoi(ch_name.Data()); + + TH1D* hist2 = (TH1D*) hist->Clone(name + "_for_fit"); + hist2->Rebin(20); + + TFitResultPtr fit_result = hist2->Fit(langau, "QRS"); + converged = !(bool)(int(fit_result)); + + if(!converged && !badChannel) + std::cout << "Peak fit has not converged - " << hist->GetName() << std::endl; + + fit = langau->GetParameter(1); + chi2 = langau->GetChisquare() / langau->GetNDF(); + + if(fSaveAllFits || (fSaveBadFits && !converged) || (fSaveSubset && ch > 1471 && ch < 1728)) + { + TCanvas *c = new TCanvas(Form("c%s", name.Data()), Form("c%s", name.Data())); + c->cd(); + + hist2->SetLineColor(kOrange+2); + hist2->SetLineWidth(2); + hist2->Draw("histe"); + langau->SetLineColor(kSpring-6); + langau->Draw("same"); + + if(badChannel) + { + TText *t = new TText(.5, .75, "Bad Channel"); + t->SetTextSize(0.1); + t->SetTextColor(kRed); + t->Draw(); + } + + if(pe) + { + if(converged) + { + c->SaveAs(Form("%s/peak_fit_%s_pe_channel_%s.png", fPeakSaveDirectory[window].c_str(), type.Data(), ch_name.Data())); + c->SaveAs(Form("%s/peak_fit_%s_pe_channel_%s.pdf", fPeakSaveDirectory[window].c_str(), type.Data(), ch_name.Data())); + } + else + { + c->SaveAs(Form("%s/peak_fit_%s_pe_channel_%s.png", fBadPeakSaveDirectory[window].c_str(), type.Data(), ch_name.Data())); + c->SaveAs(Form("%s/peak_fit_%s_pe_channel_%s.pdf", fBadPeakSaveDirectory[window].c_str(), type.Data(), ch_name.Data())); + } + } + else + { + if(converged) + { + c->SaveAs(Form("%s/peak_fit_%s_channel_%s.png", fPeakSaveDirectory[window].c_str(), type.Data(), ch_name.Data())); + c->SaveAs(Form("%s/peak_fit_%s_channel_%s.pdf", fPeakSaveDirectory[window].c_str(), type.Data(), ch_name.Data())); + } + else + { + c->SaveAs(Form("%s/peak_fit_%s_channel_%s.png", fBadPeakSaveDirectory[window].c_str(), type.Data(), ch_name.Data())); + c->SaveAs(Form("%s/peak_fit_%s_channel_%s.pdf", fBadPeakSaveDirectory[window].c_str(), type.Data(), ch_name.Data())); + } + } + } +} + +double sbnd::crt::ADRIFT::Saturation(TH1D* hADC) +{ + const double sat = hADC->GetBinContent(4090); + + for(int i = 4080; i < 4101; ++i) + { + if(i == 4090) + continue; + + if(hADC->GetBinContent(i) > sat) + std::cout << "Saturation appears to be in bin " << i << " (" << hADC->GetBinContent(i) + << ") not bin 4090 (" << sat << ")" << std::endl; + } + + return sat; +} + +void sbnd::crt::ADRIFT::ResetVars() +{ + _channel = std::numeric_limits::lowest(); + _gdml_id = std::numeric_limits::lowest(); + _mac5 = std::numeric_limits::lowest(); + _raw_channel = std::numeric_limits::lowest(); + _tagger = std::numeric_limits::lowest(); + _channel_status = std::numeric_limits::lowest(); + + _area = std::numeric_limits::lowest(); + _y_average = std::numeric_limits::lowest(); + _ped_calib = std::numeric_limits::lowest(); + _gain_calib = std::numeric_limits::lowest(); + _ped_fit = std::numeric_limits::lowest(); + _ped_fit_std = std::numeric_limits::lowest(); + _ped_fit_chi2 = std::numeric_limits::lowest(); + _ped_peak = std::numeric_limits::lowest(); + _ped_reset_fit = std::numeric_limits::lowest(); + _ped_reset_fit_std = std::numeric_limits::lowest(); + _ped_reset_fit_chi2 = std::numeric_limits::lowest(); + _ped_reset_peak = std::numeric_limits::lowest(); + _sh_rate = std::numeric_limits::lowest(); + _sp_rate = std::numeric_limits::lowest(); + _tr_rate = std::numeric_limits::lowest(); + _sh_peak_fit = std::numeric_limits::lowest(); + _sh_peak_fit_chi2 = std::numeric_limits::lowest(); + _sh_peak_peak = std::numeric_limits::lowest(); + _sh_sat_rate = std::numeric_limits::lowest(); + _sh_sat_ratio_total = std::numeric_limits::lowest(); + _sh_sat_ratio_peak = std::numeric_limits::lowest(); + _sh_ped_to_peak_fit = std::numeric_limits::lowest(); + _sh_ped_reset_to_peak_fit = std::numeric_limits::lowest(); + _sp_peak_fit = std::numeric_limits::lowest(); + _sp_peak_fit_chi2 = std::numeric_limits::lowest(); + _sp_peak_peak = std::numeric_limits::lowest(); + _sp_sat_rate = std::numeric_limits::lowest(); + _sp_sat_ratio_total = std::numeric_limits::lowest(); + _sp_sat_ratio_peak = std::numeric_limits::lowest(); + _tr_peak_fit = std::numeric_limits::lowest(); + _tr_peak_fit_chi2 = std::numeric_limits::lowest(); + _tr_peak_peak = std::numeric_limits::lowest(); + _tr_sat_rate = std::numeric_limits::lowest(); + _tr_sat_ratio_total = std::numeric_limits::lowest(); + _tr_sat_ratio_peak = std::numeric_limits::lowest(); + _tr_by_length_peak_fit = std::numeric_limits::lowest(); + _tr_by_length_peak_fit_chi2 = std::numeric_limits::lowest(); + _tr_by_length_peak_peak = std::numeric_limits::lowest(); + _tr_lim_angle_rate = std::numeric_limits::lowest(); + _tr_lim_angle_peak_fit = std::numeric_limits::lowest(); + _tr_lim_angle_peak_fit_chi2 = std::numeric_limits::lowest(); + _tr_lim_angle_peak_peak = std::numeric_limits::lowest(); + _tr_lim_angle_sat_rate = std::numeric_limits::lowest(); + _tr_lim_angle_sat_ratio_total = std::numeric_limits::lowest(); + _tr_lim_angle_sat_ratio_peak = std::numeric_limits::lowest(); + _sh_pe_peak_fit = std::numeric_limits::lowest(); + _sh_pe_peak_fit_chi2 = std::numeric_limits::lowest(); + _sh_pe_peak_peak = std::numeric_limits::lowest(); + _sp_pe_peak_fit = std::numeric_limits::lowest(); + _sp_pe_peak_fit_chi2 = std::numeric_limits::lowest(); + _sp_pe_peak_peak = std::numeric_limits::lowest(); + _tr_pe_peak_fit = std::numeric_limits::lowest(); + _tr_pe_peak_fit_chi2 = std::numeric_limits::lowest(); + _tr_pe_peak_peak = std::numeric_limits::lowest(); + _tr_by_length_pe_peak_fit = std::numeric_limits::lowest(); + _tr_by_length_pe_peak_fit_chi2 = std::numeric_limits::lowest(); + _tr_by_length_pe_peak_peak = std::numeric_limits::lowest(); + _tr_lim_angle_pe_peak_fit = std::numeric_limits::lowest(); + _tr_lim_angle_pe_peak_fit_chi2 = std::numeric_limits::lowest(); + _tr_lim_angle_pe_peak_peak = std::numeric_limits::lowest(); + + _horizontal = false; + _ped_fit_converged = false; + _ped_reset_fit_converged = false; + _sh_peak_fit_converged = false; + _sp_peak_fit_converged = false; + _tr_peak_fit_converged = false; + _tr_by_length_peak_fit_converged = false; + _tr_lim_angle_peak_fit_converged = false; + _sh_pe_peak_fit_converged = false; + _sp_pe_peak_fit_converged = false; + _tr_pe_peak_fit_converged = false; + _tr_by_length_pe_peak_fit_converged = false; + _tr_lim_angle_pe_peak_fit_converged = false; +} + +void sbnd::crt::ADRIFT::SaveHist(TH1D *hist, std::string &saveDir, std::string saveName, int rebin, bool badChannel) +{ + TCanvas *c = new TCanvas(Form("c%s", saveName.c_str()), Form("c%s", saveName.c_str())); + c->cd(); + + const TString name = hist->GetName(); + TH1D* hist2 = (TH1D*) hist->Clone(name + "_for_save"); + + hist2->SetLineColor(kOrange+2); + hist2->SetLineWidth(2); + hist2->Rebin(rebin); + hist2->Draw("histe"); + + if(badChannel) + { + TText *t = new TText(.5, .75, "Bad Channel"); + t->SetTextSize(0.1); + t->SetTextColor(kRed); + t->Draw(); + } + + c->SaveAs(Form("%s/%s.png", saveDir.c_str(), saveName.c_str())); + c->SaveAs(Form("%s/%s.pdf", saveDir.c_str(), saveName.c_str())); +} + +double sbnd::crt::ADRIFT::LanGau(double *x, double *par) +{ + //Fit parameters: + //par[0]=Width (scale) parameter of Landau density + //par[1]=Most Probable (MP, location) parameter of Landau density + //par[2]=Total area (integral -inf to inf, normalization constant) + //par[3]=Width (sigma) of convoluted Gaussian function + // + //In the Landau distribution (represented by the CERNLIB approximation), + //the maximum is located at x=-0.22278298 with the location parameter=0. + //This shift is corrected within this function, so that the actual + //maximum is identical to the MP parameter. + + // Numeric constants + double invsq2pi = 0.3989422804014; // (2 pi)^(-1/2) + double mpshift = -0.22278298; // Landau maximum location + + // Control constants + double np = 100.0; // number of convolution steps + double sc = 5.0; // convolution extends to +-sc Gaussian sigmas + + // Variables + double xx; + double mpc; + double fland; + double sum = 0.0; + double xlow,xupp; + double step; + double i; + + // MP shift correction + mpc = par[1] - mpshift * par[0]; + + // Range of convolution integral + xlow = x[0] - sc * par[3]; + xupp = x[0] + sc * par[3]; + + step = (xupp-xlow) / np; + + // Convolution integral of Landau and Gaussian by sum + for(i=1.0; i<=np/2; i++) { + xx = xlow + (i-.5) * step; + fland = TMath::Landau(xx,mpc,par[0]) / par[0]; + sum += fland * TMath::Gaus(x[0],xx,par[3]); + + xx = xupp - (i-.5) * step; + fland = TMath::Landau(xx,mpc,par[0]) / par[0]; + sum += fland * TMath::Gaus(x[0],xx,par[3]); + } + + return (par[2] * step * sum * invsq2pi / par[3]); +} + +DEFINE_ART_MODULE(sbnd::crt::ADRIFT) diff --git a/sbndcode/CRT/CRTAna/CMakeLists.txt b/sbndcode/CRT/CRTAna/CMakeLists.txt index 76a0241af..ad4284386 100644 --- a/sbndcode/CRT/CRTAna/CMakeLists.txt +++ b/sbndcode/CRT/CRTAna/CMakeLists.txt @@ -1,12 +1,17 @@ art_make( MODULE_LIBRARIES ROOT::Tree + ROOT::Graf3d art_root_io::TFileService_service larsim::Utils lardata::DetectorClocksService + sbndaq_artdaq_core::sbndaq-artdaq-core_Obj_SBND sbnobj::SBND_CRT sbnobj::SBND_Timing + sbnobj::Common_Reco sbndcode_CRT_CRTBackTracker + sbndcode_CRT_CRTReco + sbndcode_ChannelMaps_CRT_CRTChannelMapService_service ) install_fhicl() diff --git a/sbndcode/CRT/CRTAna/CRTAnalysis_module.cc b/sbndcode/CRT/CRTAna/CRTAnalysis_module.cc index 948d3fd4c..7c26b2421 100644 --- a/sbndcode/CRT/CRTAna/CRTAnalysis_module.cc +++ b/sbndcode/CRT/CRTAna/CRTAnalysis_module.cc @@ -33,16 +33,17 @@ #include "sbnobj/SBND/CRT/CRTCluster.hh" #include "sbnobj/SBND/CRT/CRTSpacePoint.hh" #include "sbnobj/SBND/CRT/CRTTrack.hh" +#include "sbnobj/SBND/CRT/CRTBlob.hh" #include "sbnobj/SBND/Timing/DAQTimestamp.hh" #include "sbndcode/Geometry/GeometryWrappers/CRTGeoService.h" -#include "sbndcode/Geometry/GeometryWrappers/TPCGeoAlg.h" #include "sbndcode/ChannelMaps/CRT/CRTChannelMapService.h" #include "sbndcode/CRT/CRTBackTracker/CRTBackTrackerAlg.h" #include "sbndcode/CRT/CRTUtils/CRTCommonUtils.h" #include "sbndcode/CRT/CRTUtils/TPCGeoUtil.h" #include "sbndcode/Decoders/PTB/sbndptb.h" #include "sbndcode/Timing/SBNDRawTimingObj.h" +#include "sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.h" namespace sbnd::crt { class CRTAnalysis; @@ -87,6 +88,8 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { void AnalyseCRTTracks(const art::Event &e, const std::vector> &CRTTrackVec, const art::FindManyP &tracksToSpacePoints, const art::FindOneP &spacePointsToClusters, const art::FindManyP &clustersToStripHits); + void AnalyseCRTBlobs(std::vector> &CRTBlobVec); + void AnalyseTPCMatching(const art::Event &e, const art::Handle> &TPCTrackHandle, const art::Handle> &CRTSpacePointHandle, const art::Handle> &CRTClusterHandle, const art::Handle> &PFPHandle, @@ -100,16 +103,14 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { art::ServiceHandle fCRTGeoService; art::ServiceHandle fCRTChannelMapService; - TPCGeoAlg fTPCGeoAlg; - CRTBackTrackerAlg fCRTBackTrackerAlg; + CRTBackTrackerAlg fCRTBackTrackerAlg; + CRTClusterCharacterisationAlg fCRTClusterCharacAlg; std::string fMCParticleModuleLabel, fSimDepositModuleLabel, fFEBDataModuleLabel, fCRTStripHitModuleLabel, - fCRTClusterModuleLabel, fCRTSpacePointModuleLabel, fCRTTrackModuleLabel, fTPCTrackModuleLabel, + fCRTClusterModuleLabel, fCRTSpacePointModuleLabel, fCRTTrackModuleLabel, fCRTBlobModuleLabel, fTPCTrackModuleLabel, fCRTSpacePointMatchingModuleLabel, fCRTTrackMatchingModuleLabel, fPFPModuleLabel, fPTBModuleLabel, fTDCModuleLabel, fTimingReferenceModuleLabel; - bool fDebug, fDataMode, fNoTPC, fHasPTB, fHasTDC, fTruthMatch; - //! Adding some of the reco parameters to save corrections - double fPEAttenuation, fTimeWalkNorm, fTimeWalkScale, fPropDelay; + bool fDebug, fDataMode, fNoTPC, fHasPTB, fHasTDC, fHasBlobs, fTruthMatch; TTree* fTree; @@ -121,7 +122,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { int _crt_timing_reference_type; int _crt_timing_reference_channel; - //mc truth + // True Particles (from G4) std::vector _mc_trackid; std::vector _mc_pdg; std::vector _mc_status; @@ -144,7 +145,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _mc_endpz; std::vector _mc_ende; - //G4 detector id + // True Energy Depositions std::vector _ide_trackid; std::vector _ide_e; std::vector _ide_entryx; @@ -156,7 +157,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _ide_exitz; std::vector _ide_exitt; - //front end mother board + // Raw Readouts (FEBDatas) std::vector _feb_mac5; std::vector _feb_tagger; std::vector _feb_flags; @@ -166,7 +167,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector> _feb_adc; std::vector _feb_coinc; - //strip hit to select the strip which has ADC above threshold + // Strip Hits std::vector _sh_channel; std::vector _sh_tagger; std::vector _sh_ts0; @@ -185,7 +186,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _sh_truth_energy; std::vector _sh_truth_time; - //cluster from x-y coincidence for CRTSpacePoint , this is what we normally call a CRT hit + // Clusters and their corresponding SpacePoints std::vector _cl_ts0; std::vector _cl_ts1; std::vector _cl_unixs; @@ -194,11 +195,11 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _cl_composition; std::vector> _cl_channel_set; std::vector> _cl_adc_set; - std::vector> _cl_sh_ts0_set; //! To store t0 from x-y coincidences - std::vector> _cl_sh_ts1_set; //! To store t1 from x-y coincidences - std::vector> _cl_sh_feb_mac5_set; //! MAC5 addresses of StripHit FEBs - std::vector> _cl_sh_time_walk_set; //! Time walk correction - std::vector> _cl_sh_prop_delay_set; //! Light propagation correction + std::vector> _cl_sh_ts0_set; + std::vector> _cl_sh_ts1_set; + std::vector> _cl_sh_feb_mac5_set; + std::vector> _cl_sh_time_walk_set; + std::vector> _cl_sh_prop_delay_set; std::vector _cl_truth_trackid; std::vector _cl_truth_completeness; std::vector _cl_truth_purity; @@ -231,7 +232,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _cl_sp_dts1; std::vector _cl_sp_complete; - //backtrack truth information from reco level + // True Deposits per particle per tagger std::vector _td_tag_trackid; std::vector _td_tag_pdg; std::vector _td_tag_tagger; @@ -242,6 +243,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _td_tag_z; std::vector _td_tag_reco_status; + // True Deposits per particle std::vector _td_trackid; std::vector _td_pdg; std::vector _td_energy; @@ -250,7 +252,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _td_reco_status; std::vector _td_reco_triple; - //track level information + // Tracks std::vector _tr_start_x; std::vector _tr_start_y; std::vector _tr_start_z; @@ -296,6 +298,16 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _tr_truth_theta; std::vector _tr_truth_phi; + // Blobs + std::vector _bl_ts0; + std::vector _bl_ets0; + std::vector _bl_ts1; + std::vector _bl_ets1; + std::vector _bl_pe; + std::vector _bl_nsps; + std::vector> _bl_nsps_per_tagger; + + // TPC Tracks and their CRT matches std::vector _tpc_start_x; std::vector _tpc_start_y; std::vector _tpc_start_z; @@ -310,6 +322,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _tpc_end_dir_z; std::vector _tpc_length; std::vector _tpc_track_score; + std::vector _tpc_whichtpc; std::vector _tpc_truth_trackid; std::vector _tpc_truth_pdg; std::vector _tpc_truth_energy; @@ -330,6 +343,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _tpc_tr_matchable; std::vector _tpc_tr_matched; std::vector _tpc_tr_good_match; + std::vector _tpc_tr_xshift; std::vector _tpc_tr_ts0; std::vector _tpc_tr_ts1; std::vector> _tpc_tr_taggers; @@ -341,12 +355,15 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { std::vector _tpc_tr_end_z; std::vector _tpc_tr_score; + // Penn Trigger Board HLTs std::vector _ptb_hlt_trigger; std::vector _ptb_hlt_timestamp; + // Penn Trigger Board LLTs std::vector _ptb_llt_trigger; std::vector _ptb_llt_timestamp; + // SPEC TDC Timestamps std::vector _tdc_channel; std::vector _tdc_timestamp; std::vector _tdc_offset; @@ -356,6 +373,7 @@ class sbnd::crt::CRTAnalysis : public art::EDAnalyzer { sbnd::crt::CRTAnalysis::CRTAnalysis(fhicl::ParameterSet const& p) : EDAnalyzer{p} , fCRTBackTrackerAlg(p.get("CRTBackTrackerAlg", fhicl::ParameterSet())) + , fCRTClusterCharacAlg(p.get("CRTClusterCharacterisationAlg", fhicl::ParameterSet())) { fMCParticleModuleLabel = p.get("MCParticleModuleLabel", "largeant"); fSimDepositModuleLabel = p.get("SimDepositModuleLabel", "genericcrt"); @@ -364,6 +382,7 @@ sbnd::crt::CRTAnalysis::CRTAnalysis(fhicl::ParameterSet const& p) fCRTClusterModuleLabel = p.get("CRTClusterModuleLabel", "crtclustering"); fCRTSpacePointModuleLabel = p.get("CRTSpacePointModuleLabel", "crtspacepoints"); fCRTTrackModuleLabel = p.get("CRTTrackModuleLabel", "crttracks"); + fCRTBlobModuleLabel = p.get("CRTBlobModuleLabel", "crtblobs"); fTPCTrackModuleLabel = p.get("TPCTrackModuleLabel", "pandoraSCETrack"); fCRTSpacePointMatchingModuleLabel = p.get("CRTSpacePointMatchingModuleLabel", "crtspacepointmatchingSCE"); fCRTTrackMatchingModuleLabel = p.get("CRTTrackMatchingModuleLabel", "crttrackmatchingSCE"); @@ -376,12 +395,8 @@ sbnd::crt::CRTAnalysis::CRTAnalysis(fhicl::ParameterSet const& p) fNoTPC = p.get("NoTPC", false); fHasPTB = p.get("HasPTB", false); fHasTDC = p.get("HasTDC", false); + fHasBlobs = p.get("HasBlobs", false); fTruthMatch = p.get("TruthMatch", true); - //! Adding some of the reco parameters to save corrections - fPEAttenuation = p.get("PEAttenuation", 1.0); - fTimeWalkNorm = p.get("TimeWalkNorm", 0.0); - fTimeWalkScale = p.get("TimeWalkScale", 0.0); - fPropDelay = p.get("PropDelay", 0.0); if(!fDataMode && fTruthMatch) fCRTBackTrackerAlg = CRTBackTrackerAlg(p.get("CRTBackTrackerAlg", fhicl::ParameterSet())); @@ -578,6 +593,17 @@ sbnd::crt::CRTAnalysis::CRTAnalysis(fhicl::ParameterSet const& p) fTree->Branch("tr_truth_phi", "std::vector", &_tr_truth_phi); } + if(fHasBlobs) + { + fTree->Branch("bl_ts0", "std::vector", &_bl_ts0); + fTree->Branch("bl_ets0", "std::vector", &_bl_ets0); + fTree->Branch("bl_ts1", "std::vector", &_bl_ts1); + fTree->Branch("bl_ets1", "std::vector", &_bl_ets1); + fTree->Branch("bl_pe", "std::vector", &_bl_pe); + fTree->Branch("bl_nsps", "std::vector", &_bl_nsps); + fTree->Branch("bl_nsps_per_tagger", "std::vector>", &_bl_nsps_per_tagger); + } + if(!fNoTPC) { fTree->Branch("tpc_start_x", "std::vector", &_tpc_start_x); @@ -594,6 +620,7 @@ sbnd::crt::CRTAnalysis::CRTAnalysis(fhicl::ParameterSet const& p) fTree->Branch("tpc_end_dir_z", "std::vector", &_tpc_end_dir_z); fTree->Branch("tpc_length", "std::vector", &_tpc_length); fTree->Branch("tpc_track_score", "std::vector", &_tpc_track_score); + fTree->Branch("tpc_whichtpc", "std::vector", &_tpc_whichtpc); fTree->Branch("tpc_sp_matched", "std::vector", &_tpc_sp_matched); fTree->Branch("tpc_sp_channel_set", "std::vector>", &_tpc_sp_channel_set); fTree->Branch("tpc_sp_xshift", "std::vector", &_tpc_sp_xshift); @@ -606,6 +633,7 @@ sbnd::crt::CRTAnalysis::CRTAnalysis(fhicl::ParameterSet const& p) fTree->Branch("tpc_sp_z", "std::vector", &_tpc_sp_z); fTree->Branch("tpc_sp_score", "std::vector", &_tpc_sp_score); fTree->Branch("tpc_tr_matched", "std::vector", &_tpc_tr_matched); + fTree->Branch("tpc_tr_xshift", "std::vector", &_tpc_tr_xshift); fTree->Branch("tpc_tr_ts0", "std::vector", &_tpc_tr_ts0); fTree->Branch("tpc_tr_ts1", "std::vector", &_tpc_tr_ts1); fTree->Branch("tpc_tr_taggers", "std::vector>", &_tpc_tr_taggers); @@ -835,6 +863,23 @@ void sbnd::crt::CRTAnalysis::analyze(art::Event const& e) // Fill CRTTrack variables AnalyseCRTTracks(e, CRTTrackVec, tracksToSpacePoints, spacepointsToClusters, clustersToStripHits); + if(fHasBlobs) + { + // Get CRTBlobs + art::Handle> CRTBlobHandle; + e.getByLabel(fCRTBlobModuleLabel, CRTBlobHandle); + if(!CRTBlobHandle.isValid()){ + std::cout << "CRTBlob product " << fCRTBlobModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + + std::vector> CRTBlobVec; + art::fill_ptr_vector(CRTBlobVec, CRTBlobHandle); + + // Fill CRTBlob variables + AnalyseCRTBlobs(CRTBlobVec); + } + if(fNoTPC) { // Fill the Tree @@ -1282,25 +1327,10 @@ void sbnd::crt::CRTAnalysis::AnalyseCRTClusters(const art::Event &e, const std:: _cl_sh_ts1_set[i][ii] = striphit->Ts1(); _cl_sh_feb_mac5_set[i][ii] = fCRTChannelMapService->GetMAC5FromOfflineChannelID(striphit->Channel()); - /* - * The below segment reimplements the CorrectTime() method - * from CRTReco/CRTClusterCharacterisationAlg.cc . - * Because the Ts0(), Ts1() getters invoked in _cl_sp_ts*, _cl_sh_ts*_set are raw T0/1 - * counters, the time walk and propagation delay are saved as explicit branches here. - */ if(spacepoints.size() == 1) { - double pe0 = fCRTGeoService->GetSiPM( striphit->Channel() ).gain * striphit->ADC1(); - double pe1 = fCRTGeoService->GetSiPM( striphit->Channel() + 1 ).gain * striphit->ADC2(); - double pe = pe0 + pe1; - - double dist = fCRTGeoService->DistanceDownStrip( spacepoints[0]->Pos(), striphit->Channel() ); - - double corr = std::pow( dist - fPEAttenuation, 2.0 ) / std::pow( fPEAttenuation, 2.0 ); - double tw_pe = pe * corr; - - _cl_sh_time_walk_set[i][ii] = fTimeWalkNorm * std::exp( -fTimeWalkScale * tw_pe ); - _cl_sh_prop_delay_set[i][ii] = fPropDelay * dist; + _cl_sh_time_walk_set[i][ii] = fCRTClusterCharacAlg.TimeWalk(striphit, spacepoints[0]->Pos()); + _cl_sh_prop_delay_set[i][ii] = fCRTClusterCharacAlg.PropagationDelay(striphit, spacepoints[0]->Pos()); ts0_set.push_back({_cl_sh_feb_mac5_set[i][ii], _cl_sh_ts0_set[i][ii] - _cl_sh_time_walk_set[i][ii] - _cl_sh_prop_delay_set[i][ii]}); ts1_set.push_back({_cl_sh_feb_mac5_set[i][ii], _cl_sh_ts1_set[i][ii] - _cl_sh_time_walk_set[i][ii] - _cl_sh_prop_delay_set[i][ii]}); @@ -1585,6 +1615,34 @@ void sbnd::crt::CRTAnalysis::AnalyseCRTTracks(const art::Event &e, const std::ve } } +void sbnd::crt::CRTAnalysis::AnalyseCRTBlobs(std::vector> &CRTBlobVec) +{ + const unsigned nBlobs = CRTBlobVec.size(); + + _bl_ts0.resize(nBlobs); + _bl_ets0.resize(nBlobs); + _bl_ts1.resize(nBlobs); + _bl_ets1.resize(nBlobs); + _bl_pe.resize(nBlobs); + _bl_nsps.resize(nBlobs); + _bl_nsps_per_tagger.resize(nBlobs, std::vector(7)); + + for(unsigned i = 0; i < nBlobs; ++i) + { + const auto blob = CRTBlobVec[i]; + + _bl_ts0[i] = blob->Ts0(); + _bl_ets0[i] = blob->Ts0Err(); + _bl_ts1[i] = blob->Ts1(); + _bl_ets1[i] = blob->Ts1Err(); + _bl_pe[i] = blob->PE(); + _bl_nsps[i] = blob->TotalSpacePoints(); + + for(unsigned j = 0; j < 7; ++j) + _bl_nsps_per_tagger[i][j] = blob->SpacePointsInTagger((CRTTagger)j); + } +} + void sbnd::crt::CRTAnalysis::AnalyseTPCMatching(const art::Event &e, const art::Handle> &TPCTrackHandle, const art::Handle> &CRTSpacePointHandle, const art::Handle> &CRTClusterHandle, const art::Handle> &PFPHandle, @@ -1610,6 +1668,7 @@ void sbnd::crt::CRTAnalysis::AnalyseTPCMatching(const art::Event &e, const art:: _tpc_end_dir_z.resize(nTracks); _tpc_length.resize(nTracks); _tpc_track_score.resize(nTracks); + _tpc_whichtpc.resize(nTracks); _tpc_truth_trackid.resize(nTracks); _tpc_truth_pdg.resize(nTracks); _tpc_truth_energy.resize(nTracks); @@ -1630,6 +1689,7 @@ void sbnd::crt::CRTAnalysis::AnalyseTPCMatching(const art::Event &e, const art:: _tpc_tr_matchable.resize(nTracks); _tpc_tr_matched.resize(nTracks); _tpc_tr_good_match.resize(nTracks); + _tpc_tr_xshift.resize(nTracks); _tpc_tr_ts0.resize(nTracks); _tpc_tr_ts1.resize(nTracks); _tpc_tr_taggers.resize(nTracks); @@ -1699,6 +1759,7 @@ void sbnd::crt::CRTAnalysis::AnalyseTPCMatching(const art::Event &e, const art:: const art::Ptr crttrack = tracksToTrackMatches.at(track.key()); const std::vector> trackHits = tracksToHits.at(track.key()); + _tpc_whichtpc[nActualTracks] = TPCGeoUtil::DetectedInTPC(trackHits); if(spacepoint.isNonnull()) { @@ -1741,7 +1802,11 @@ void sbnd::crt::CRTAnalysis::AnalyseTPCMatching(const art::Event &e, const art:: { const anab::T0 trackMatch = tracksToTrackMatches.data(track.key()).ref(); + const int driftDirection = TPCGeoUtil::DriftDirectionFromHits(geometryService, trackHits); + const double crtShiftingTime = fDataMode ? crttrack->Ts0() * 1e-3 : crttrack->Ts1() * 1e-3; + _tpc_tr_matched[nActualTracks] = true; + _tpc_tr_xshift[nActualTracks] = driftDirection * crtShiftingTime * detProp.DriftVelocity(); _tpc_tr_ts0[nActualTracks] = crttrack->Ts0(); _tpc_tr_ts1[nActualTracks] = crttrack->Ts1(); _tpc_tr_score[nActualTracks] = trackMatch.TriggerConfidence(); @@ -1765,6 +1830,7 @@ void sbnd::crt::CRTAnalysis::AnalyseTPCMatching(const art::Event &e, const art:: else { _tpc_tr_matched[nActualTracks] = false; + _tpc_tr_xshift[nActualTracks] = -std::numeric_limits::max(); _tpc_tr_ts0[nActualTracks] = -std::numeric_limits::max(); _tpc_tr_ts1[nActualTracks] = -std::numeric_limits::max(); _tpc_tr_score[nActualTracks] = -std::numeric_limits::max(); @@ -1843,6 +1909,7 @@ void sbnd::crt::CRTAnalysis::AnalyseTPCMatching(const art::Event &e, const art:: _tpc_end_dir_z.resize(nActualTracks); _tpc_length.resize(nActualTracks); _tpc_track_score.resize(nActualTracks); + _tpc_whichtpc.resize(nActualTracks); _tpc_truth_trackid.resize(nActualTracks); _tpc_truth_pdg.resize(nActualTracks); _tpc_truth_energy.resize(nActualTracks); diff --git a/sbndcode/CRT/CRTAna/CRTRateAnalysis_module.cc b/sbndcode/CRT/CRTAna/CRTRateAnalysis_module.cc new file mode 100644 index 000000000..418c9c250 --- /dev/null +++ b/sbndcode/CRT/CRTAna/CRTRateAnalysis_module.cc @@ -0,0 +1,292 @@ +//////////////////////////////////////////////////////////////////////// +// Class: CRTRateAnalysis +// Plugin Type: analyzer (Unknown Unknown) +// File: CRTRateAnalysis_module.cc +// +// Generated at Tue Jan 7 05:28:06 2025 by Henry Lay using cetskelgen +// from cetlib version 3.18.02. +//////////////////////////////////////////////////////////////////////// + +#include "art/Framework/Core/EDAnalyzer.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Principal/Run.h" +#include "art/Framework/Principal/SubRun.h" +#include "canvas/Utilities/InputTag.h" +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "art_root_io/TFileService.h" + +#include "TTree.h" + +#include "canvas/Persistency/Common/FindOneP.h" +#include "canvas/Persistency/Common/FindManyP.h" + +#include "artdaq-core/Data/RawEvent.hh" + +#include "sbnobj/SBND/CRT/FEBData.hh" +#include "sbnobj/SBND/CRT/CRTSpacePoint.hh" +#include "sbnobj/SBND/CRT/CRTCluster.hh" +#include "sbnobj/SBND/CRT/CRTBlob.hh" + +#include "sbndcode/Geometry/GeometryWrappers/CRTGeoService.h" +#include "sbndcode/ChannelMaps/CRT/CRTChannelMapService.h" + +namespace sbnd { + namespace crt { + class CRTRateAnalysis; + } +} + + +class sbnd::crt::CRTRateAnalysis : public art::EDAnalyzer { +public: + explicit CRTRateAnalysis(fhicl::ParameterSet const& p); + // The compiler-generated destructor is fine for non-base + // classes without bare pointers or other resource use. + + // Plugins should not be copied or assigned. + CRTRateAnalysis(CRTRateAnalysis const&) = delete; + CRTRateAnalysis(CRTRateAnalysis&&) = delete; + CRTRateAnalysis& operator=(CRTRateAnalysis const&) = delete; + CRTRateAnalysis& operator=(CRTRateAnalysis&&) = delete; + + // Required functions. + void analyze(art::Event const& e) override; + + void ResetEventVars(); + void ResetRawVars(); + void ResetSpacePointVars(); + void ResetBlobVars(); + +private: + + art::ServiceHandle fCRTGeoService; + art::ServiceHandle fCRTChannelMapService; + + std::string fFEBDataModuleLabel, fCRTSpacePointModuleLabel, + fCRTBlobModuleLabel, fDAQHeaderModuleLabel, fDAQHeaderInstanceLabel; + + TTree *fEventTree, *fRawTree, *fSpacePointTree, *fBlobTree; + + int32_t _run, _subrun, _event; + uint32_t _event_header_ts; + + int16_t _tagger, _module; + uint16_t _max_channel; + + int16_t _n_hits; + double _x, _y, _z, _pe; + + int16_t _n_total_sps, _n_bottom_sps, _n_bottom_sps_twohits, _n_south_sps, + _n_north_sps, _n_west_sps, _n_east_sps, _n_toplow_sps, _n_tophigh_sps; +}; + + +sbnd::crt::CRTRateAnalysis::CRTRateAnalysis(fhicl::ParameterSet const& p) + : EDAnalyzer{p} + , fFEBDataModuleLabel(p.get("FEBDataModuleLabel")) + , fCRTSpacePointModuleLabel(p.get("CRTSpacePointModuleLabel")) + , fCRTBlobModuleLabel(p.get("CRTBlobModuleLabel")) + , fDAQHeaderModuleLabel(p.get("DAQHeaderModuleLabel")) + , fDAQHeaderInstanceLabel(p.get("DAQHeaderInstanceLabel")) +{ + art::ServiceHandle fs; + + fEventTree = fs->make("events", ""); + fEventTree->Branch("run", &_run); + fEventTree->Branch("subrun", &_subrun); + fEventTree->Branch("event", &_event); + fEventTree->Branch("event_header_ts", &_event_header_ts); + + fRawTree = fs->make("readouts", ""); + fRawTree->Branch("run", &_run); + fRawTree->Branch("subrun", &_subrun); + fRawTree->Branch("event", &_event); + fRawTree->Branch("event_header_ts", &_event_header_ts); + fRawTree->Branch("tagger", &_tagger); + fRawTree->Branch("module", &_module); + fRawTree->Branch("max_channel", &_max_channel); + + fSpacePointTree = fs->make("spacepoints", ""); + fSpacePointTree->Branch("run", &_run); + fSpacePointTree->Branch("subrun", &_subrun); + fSpacePointTree->Branch("event", &_event); + fSpacePointTree->Branch("event_header_ts", &_event_header_ts); + fSpacePointTree->Branch("tagger", &_tagger); + fSpacePointTree->Branch("n_hits", &_n_hits); + fSpacePointTree->Branch("x", &_x); + fSpacePointTree->Branch("y", &_y); + fSpacePointTree->Branch("z", &_z); + fSpacePointTree->Branch("pe", &_pe); + + fBlobTree = fs->make("blobs", ""); + fBlobTree->Branch("run", &_run); + fBlobTree->Branch("subrun", &_subrun); + fBlobTree->Branch("event", &_event); + fBlobTree->Branch("event_header_ts", &_event_header_ts); + fBlobTree->Branch("n_total_sps", &_n_total_sps); + fBlobTree->Branch("n_bottom_sps", &_n_bottom_sps); + fBlobTree->Branch("n_bottom_sps_twohits", &_n_bottom_sps_twohits); + fBlobTree->Branch("n_south_sps", &_n_south_sps); + fBlobTree->Branch("n_north_sps", &_n_north_sps); + fBlobTree->Branch("n_west_sps", &_n_west_sps); + fBlobTree->Branch("n_east_sps", &_n_east_sps); + fBlobTree->Branch("n_toplow_sps", &_n_toplow_sps); + fBlobTree->Branch("n_tophigh_sps", &_n_tophigh_sps); + fBlobTree->Branch("pe", &_pe); +} + +void sbnd::crt::CRTRateAnalysis::analyze(art::Event const& e) +{ + ResetEventVars(); + + _run = e.id().run(); + _subrun = e.id().subRun(); + _event = e.id().event(); + + art::Handle DAQHeaderHandle; + e.getByLabel(fDAQHeaderModuleLabel, fDAQHeaderInstanceLabel, DAQHeaderHandle); + + if(DAQHeaderHandle.isValid()) + { + artdaq::RawEvent rawHeaderEvent = artdaq::RawEvent(*DAQHeaderHandle); + uint64_t raw_ts = rawHeaderEvent.timestamp(); + _event_header_ts = raw_ts / static_cast(1e9); + } + + fEventTree->Fill(); + + art::Handle> FEBDataHandle; + e.getByLabel(fFEBDataModuleLabel, FEBDataHandle); + + std::vector> FEBDataVec; + art::fill_ptr_vector(FEBDataVec, FEBDataHandle); + + for(auto const& data : FEBDataVec) + { + ResetRawVars(); + _tagger = fCRTGeoService->AuxDetIndexToTaggerEnum(data->Mac5()); + _module = data->Mac5(); + + int max_adc = -1, max_ch = -1; + + for(int ch = 0; ch < 32; ++ch) + { + int adc = data->ADC(ch); + + if(adc > max_adc) + { + max_adc = adc; + max_ch = ch; + } + } + + _max_channel = fCRTChannelMapService->ConstructOfflineChannelIDFromOfflineModuleIDAndOfflineLocalChannel(_module, max_ch); + + fRawTree->Fill(); + } + + art::Handle> CRTSpacePointHandle; + e.getByLabel(fCRTSpacePointModuleLabel, CRTSpacePointHandle); + + std::vector> CRTSpacePointVec; + art::fill_ptr_vector(CRTSpacePointVec, CRTSpacePointHandle); + + art::FindOneP spacePointsToClusters(CRTSpacePointHandle, e, fCRTSpacePointModuleLabel); + + for(auto const& spacePoint : CRTSpacePointVec) + { + ResetSpacePointVars(); + + const art::Ptr cluster = spacePointsToClusters.at(spacePoint.key()); + + _tagger = cluster->Tagger(); + _n_hits = cluster->NHits(); + _x = spacePoint->X(); + _y = spacePoint->Y(); + _z = spacePoint->Z(); + _pe = spacePoint->PE(); + + fSpacePointTree->Fill(); + } + + art::Handle> CRTBlobHandle; + e.getByLabel(fCRTBlobModuleLabel, CRTBlobHandle); + + std::vector> CRTBlobVec; + art::fill_ptr_vector(CRTBlobVec, CRTBlobHandle); + + art::FindManyP blobsToSpacePoints(CRTBlobHandle, e, fCRTBlobModuleLabel); + + for(auto const& blob : CRTBlobVec) + { + ResetBlobVars(); + + _n_total_sps = blob->TotalSpacePoints(); + _n_bottom_sps = blob->SpacePointsInTagger(kBottomTagger); + _n_south_sps = blob->SpacePointsInTagger(kSouthTagger); + _n_north_sps = blob->SpacePointsInTagger(kNorthTagger); + _n_west_sps = blob->SpacePointsInTagger(kWestTagger); + _n_east_sps = blob->SpacePointsInTagger(kEastTagger); + _n_toplow_sps = blob->SpacePointsInTagger(kTopLowTagger); + _n_tophigh_sps = blob->SpacePointsInTagger(kTopHighTagger); + _pe = blob->PE(); + + _n_bottom_sps_twohits = 0; + + const std::vector> spacePoints = blobsToSpacePoints.at(blob.key()); + + for(auto const& spacePoint : spacePoints) + { + const art::Ptr cluster = spacePointsToClusters.at(spacePoint.key()); + + if(cluster->Tagger() == kBottomTagger && cluster->NHits() > 1) + ++_n_bottom_sps_twohits; + } + + fBlobTree->Fill(); + } +} + +void sbnd::crt::CRTRateAnalysis::ResetEventVars() +{ + _run = -1; _subrun = -1; _event = -1; + _event_header_ts = std::numeric_limits::max(); +} + +void sbnd::crt::CRTRateAnalysis::ResetRawVars() +{ + _tagger = -1; _module = -1; + _max_channel = std::numeric_limits::max(); +} + +void sbnd::crt::CRTRateAnalysis::ResetSpacePointVars() +{ + _tagger = -1; + + _n_hits = -1; + + _x = std::numeric_limits::lowest(); + _y = std::numeric_limits::lowest(); + _z = std::numeric_limits::lowest(); + _pe = std::numeric_limits::lowest(); +} + +void sbnd::crt::CRTRateAnalysis::ResetBlobVars() +{ + _n_total_sps = -1; + _n_bottom_sps = -1; + _n_bottom_sps_twohits = -1; + _n_south_sps = -1; + _n_north_sps = -1; + _n_west_sps = -1; + _n_east_sps = -1; + _n_toplow_sps = -1; + _n_tophigh_sps = -1; + + _pe = std::numeric_limits::lowest(); +} + +DEFINE_ART_MODULE(sbnd::crt::CRTRateAnalysis) diff --git a/sbndcode/CRT/CRTAna/CRTTimingAnalysis_module.cc b/sbndcode/CRT/CRTAna/CRTTimingAnalysis_module.cc new file mode 100644 index 000000000..f050cce6c --- /dev/null +++ b/sbndcode/CRT/CRTAna/CRTTimingAnalysis_module.cc @@ -0,0 +1,1186 @@ +//////////////////////////////////////////////////////////////////////// +// Class: CRTTimingAnalysis +// Plugin Type: analyzer +// File: CRTTimingAnalysis_module.cc +// Author: Henry Lay (h.lay@sheffield.ac.uk) +//////////////////////////////////////////////////////////////////////// + +#include "art/Framework/Core/EDAnalyzer.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Principal/Run.h" +#include "art/Framework/Principal/SubRun.h" +#include "canvas/Utilities/InputTag.h" +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" +#include "art_root_io/TFileService.h" +#include "canvas/Persistency/Common/FindManyP.h" +#include "canvas/Persistency/Common/FindOneP.h" + +#include "TTree.h" + +#include "lardataobj/RecoBase/Slice.h" +#include "lardataobj/RecoBase/PFParticle.h" +#include "lardataobj/RecoBase/Track.h" +#include "lardataobj/AnalysisBase/T0.h" + +#include "sbnobj/SBND/CRT/CRTStripHit.hh" +#include "sbnobj/SBND/CRT/CRTCluster.hh" +#include "sbnobj/SBND/CRT/CRTSpacePoint.hh" +#include "sbnobj/SBND/CRT/CRTTrack.hh" +#include "sbnobj/SBND/Timing/DAQTimestamp.hh" +#include "sbnobj/Common/Reco/CorrectedOpFlashTiming.h" + +#include "sbndcode/Geometry/GeometryWrappers/CRTGeoService.h" +#include "sbndcode/Decoders/PTB/sbndptb.h" +#include "sbndcode/Timing/SBNDRawTimingObj.h" +#include "sbndcode/Calibration/CRTDatabaseInterface/CRTCalibrationDatabase.h" +#include "sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.h" + +namespace sbnd::crt { + class CRTTimingAnalysis; + + constexpr double fSpeedOfLightNanosecondPerCentimeter = 3.3356e-2; // ns / cm + constexpr double fMicrosecondToNanosecond = 1e3; // ns +} + +class sbnd::crt::CRTTimingAnalysis : public art::EDAnalyzer { +public: + explicit CRTTimingAnalysis(fhicl::ParameterSet const& p); + // The compiler-generated destructor is fine for non-base + // classes without bare pointers or other resource use. + + // Plugins should not be copied or assigned. + CRTTimingAnalysis(CRTTimingAnalysis const&) = delete; + CRTTimingAnalysis(CRTTimingAnalysis&&) = delete; + CRTTimingAnalysis& operator=(CRTTimingAnalysis const&) = delete; + CRTTimingAnalysis& operator=(CRTTimingAnalysis&&) = delete; + + // Required functions. + void analyze(art::Event const& e) override; + + void ResetMaps(); + + void AnalysePTBs(std::vector> &PTBVec); + + void AnalyseTDCs(std::vector> &TDCVec); + + void SortReferencing(); + + void AnalyseCRTSpacePoints(const std::vector> &CRTSpacePointVec, + const art::FindOneP &spacepointsToClusters, + const art::FindManyP &clustersToStripHits, + const art::FindOneP &spacepointsToTracks); + + void ResetSPVariables(); + + void ResizeSPSHVecs(const unsigned n); + + double IntrinsicResolution(const std::vector &_sp_sh_channel_set, + const std::vector &_sp_sh_ts_set, + const std::vector &_sp_sh_time_walk_set, + const std::vector &_sp_sh_prop_delay_set); + + void AnalyseCRTTracks(const std::vector> &CRTTrackVec, + const art::FindManyP &tracksToSpacePoints); + + void ResetTrVariables(); + + void AnalyseTPCSlices(const std::vector> &TPCSliceVec, + const art::FindOneP &sliceToCorrectedOpFlash, + const art::FindManyP &sliceToPFPs, + const art::FindOneP &pfpToTrack, + const art::FindOneP &trackToCRTSpacePoint); + + void ResetTPCVariables(); + +private: + + art::ServiceHandle fCRTGeoService; + art::ServiceHandle fCRTChannelMapService; + sbndDB::CRTCalibrationDatabase const *fCRTCalibrationDatabaseService; + CRTClusterCharacterisationAlg fCRTClusterCharacAlg; + + // fcl Controlled Variables + std::string fCRTClusterModuleLabel, fCRTSpacePointModuleLabel, fCRTTrackModuleLabel, + fPTBModuleLabel, fTDCModuleLabel, fTimingReferenceModuleLabel, fTPCSliceModuleLabel, + fCorrectedOpFlashModuleLabel, fTPCTrackModuleLabel, fCRTSpacePointMatchingModuleLabel; + std::vector fAllowedPTBHLTs; + + // Global Storage + std::vector _ptb_hlt_trigger; + std::vector _ptb_hlt_timestamp; + + std::vector _ptb_llt_trigger; + std::vector _ptb_llt_timestamp; + + std::vector _tdc_channel; + std::vector _tdc_timestamp; + std::vector _tdc_offset; + std::vector _tdc_name; + + // Trees + TTree *fSPTree, *fTrTree, *fTPCTree; + + // Tree Variables + int _run; + int _subrun; + int _event; + int _crt_timing_reference_type; + int _crt_timing_reference_channel; + + bool _etrig_good; + bool _rwm_good; + bool _ptb_hlt_beam_gate_good; + bool _crt_t1_reset_good; + double _rwm_etrig_diff; + double _ptb_hlt_beam_gate_etrig_diff; + double _rwm_crt_t1_reset_diff; + double _ptb_hlt_beam_gate_crt_t1_reset_diff; + double _rwm_ptb_hlt_beam_gate_diff; + + uint16_t _sp_nhits; + int16_t _sp_tagger; + double _sp_x; + double _sp_y; + double _sp_z; + double _sp_front_face_adjustment; + double _sp_pe; + double _sp_ts0; + double _sp_ts0_rwm_ref; + double _sp_ts0_ptb_hlt_beam_gate_ref; + double _sp_ts0_rwm_ref_front_face; + double _sp_ts0_ptb_hlt_beam_gate_ref_front_face; + double _sp_dts0; + double _sp_ts1; + double _sp_ts1_rwm_ref; + double _sp_ts1_ptb_hlt_beam_gate_ref; + double _sp_ts1_rwm_ref_front_face; + double _sp_ts1_ptb_hlt_beam_gate_ref_front_face; + double _sp_dts1; + bool _sp_single_timing_chain; + int16_t _sp_timing_chain; + std::vector _sp_sh_channel_set; + std::vector _sp_sh_mac5_set; + std::vector _sp_sh_timing_chain_set; + std::vector _sp_sh_ts0_set; + std::vector _sp_sh_ts1_set; + std::vector _sp_sh_time_walk_set; + std::vector _sp_sh_prop_delay_set; + std::vector _sp_sh_cable_length_ts0_set; + std::vector _sp_sh_calib_offset_ts0_set; + std::vector _sp_sh_cable_length_ts1_set; + std::vector _sp_sh_calib_offset_ts1_set; + bool _sp_has_track; + double _sp_norm_angle; + double _sp_path_length; + + double _tr_start_x; + double _tr_start_y; + double _tr_start_z; + double _tr_end_x; + double _tr_end_y; + double _tr_end_z; + double _tr_dir_x; + double _tr_dir_y; + double _tr_dir_z; + double _tr_ts0; + double _tr_ts0_rwm_ref; + double _tr_ts0_ptb_hlt_beam_gate_ref; + double _tr_ts1; + double _tr_ts1_rwm_ref; + double _tr_ts1_ptb_hlt_beam_gate_ref; + double _tr_pe; + double _tr_length; + double _tr_length_tof; + double _tr_tof_ts0; + double _tr_tof_diff_ts0; + double _tr_tof_ts1; + double _tr_tof_diff_ts1; + double _tr_theta; + double _tr_phi; + bool _tr_triple; + int16_t _tr_tagger1; + int16_t _tr_tagger2; + int16_t _tr_tagger3; + int16_t _tr_start_tagger; + uint16_t _tr_start_nhits; + double _tr_start_dts0; + double _tr_start_dts1; + bool _tr_start_single_timing_chain; + int16_t _tr_start_timing_chain; + int16_t _tr_end_tagger; + uint16_t _tr_end_nhits; + double _tr_end_dts0; + double _tr_end_dts1; + bool _tr_end_single_timing_chain; + int16_t _tr_end_timing_chain; + + bool _tpc_has_corrected_opflash; + bool _tpc_has_crt_sp_match; + double _tpc_opflash_t0; + double _tpc_opflash_nutof_light; + double _tpc_opflash_nutof_charge; + double _tpc_opflash_t0_corrected; + double _tpc_opflash_interaction_time_rwm; + double _tpc_opflash_front_face_rwm; + double _tpc_crt_sp_score; + int16_t _tpc_crt_sp_tagger; + uint16_t _tpc_crt_sp_nhits; + bool _tpc_crt_sp_single_timing_chain; + int16_t _tpc_crt_sp_timing_chain; + double _tpc_crt_sp_front_face_adjustment; + double _tpc_crt_sp_interaction_time_adjustment; + double _tpc_crt_sp_ts0; + double _tpc_crt_sp_ts0_rwm_ref; + double _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref; + double _tpc_crt_sp_ts0_rwm_ref_front_face; + double _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_front_face; + double _tpc_crt_sp_ts0_rwm_ref_interaction_time; + double _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_interaction_time; + double _tpc_crt_sp_dts0; + double _tpc_crt_sp_ts1; + double _tpc_crt_sp_ts1_rwm_ref; + double _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref; + double _tpc_crt_sp_ts1_rwm_ref_front_face; + double _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_front_face; + double _tpc_crt_sp_ts1_rwm_ref_interaction_time; + double _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_interaction_time; + double _tpc_crt_sp_dts1; + double _tpc_crt_ts0_pmt_diff; + double _tpc_crt_ts1_pmt_diff; + + // Maps + std::map fSPTaggerMap; + std::map fSPNHitsMap; + std::map fSPdTs0Map; + std::map fSPdTs1Map; + std::map fSPTimingChainMap; +}; + +sbnd::crt::CRTTimingAnalysis::CRTTimingAnalysis(fhicl::ParameterSet const& p) + : EDAnalyzer{p} + , fCRTClusterCharacAlg(p.get("CRTClusterCharacterisationAlg", fhicl::ParameterSet())) +{ + fCRTClusterModuleLabel = p.get("CRTClusterModuleLabel"); + fCRTSpacePointModuleLabel = p.get("CRTSpacePointModuleLabel"); + fCRTTrackModuleLabel = p.get("CRTTrackModuleLabel"); + fPTBModuleLabel = p.get("PTBModuleLabel"); + fTDCModuleLabel = p.get("TDCModuleLabel"); + fTimingReferenceModuleLabel = p.get("TimingReferenceModuleLabel"); + fTPCSliceModuleLabel = p.get("TPCSliceModuleLabel"); + fCorrectedOpFlashModuleLabel = p.get("CorrectedOpFlashModuleLabel"); + fTPCTrackModuleLabel = p.get("TPCTrackModuleLabel"); + fCRTSpacePointMatchingModuleLabel = p.get("CRTSpacePointMatchingModuleLabel"); + fAllowedPTBHLTs = p.get>("AllowedPTBHLTs"); + + fCRTCalibrationDatabaseService = lar::providerFrom(); + + art::ServiceHandle fs; + + fSPTree = fs->make("spacepoints",""); + fSPTree->Branch("run", &_run); + fSPTree->Branch("subrun", &_subrun); + fSPTree->Branch("event", &_event); + fSPTree->Branch("crt_timing_reference_type", &_crt_timing_reference_type); + fSPTree->Branch("crt_timing_reference_channel", &_crt_timing_reference_channel); + + fSPTree->Branch("etrig_good", &_etrig_good); + fSPTree->Branch("rwm_good", &_rwm_good); + fSPTree->Branch("ptb_hlt_beam_gate_good", &_ptb_hlt_beam_gate_good); + fSPTree->Branch("crt_t1_reset_good", &_crt_t1_reset_good); + fSPTree->Branch("rwm_etrig_diff", &_rwm_etrig_diff); + fSPTree->Branch("ptb_hlt_beam_gate_etrig_diff", &_ptb_hlt_beam_gate_etrig_diff); + fSPTree->Branch("rwm_crt_t1_reset_diff", &_rwm_crt_t1_reset_diff); + fSPTree->Branch("ptb_hlt_beam_gate_crt_t1_reset_diff", &_ptb_hlt_beam_gate_crt_t1_reset_diff); + fSPTree->Branch("rwm_ptb_hlt_beam_gate_diff", &_rwm_ptb_hlt_beam_gate_diff); + + fSPTree->Branch("sp_nhits", &_sp_nhits); + fSPTree->Branch("sp_tagger", &_sp_tagger); + fSPTree->Branch("sp_x", &_sp_x); + fSPTree->Branch("sp_y", &_sp_y); + fSPTree->Branch("sp_z", &_sp_z); + fSPTree->Branch("sp_front_face_adjustment", &_sp_front_face_adjustment); + fSPTree->Branch("sp_pe", &_sp_pe); + fSPTree->Branch("sp_ts0", &_sp_ts0); + fSPTree->Branch("sp_ts0_rwm_ref", &_sp_ts0_rwm_ref); + fSPTree->Branch("sp_ts0_ptb_hlt_beam_gate_ref", &_sp_ts0_ptb_hlt_beam_gate_ref); + fSPTree->Branch("sp_ts0_rwm_ref_front_face", &_sp_ts0_rwm_ref_front_face); + fSPTree->Branch("sp_ts0_ptb_hlt_beam_gate_ref_front_face", &_sp_ts0_ptb_hlt_beam_gate_ref_front_face); + fSPTree->Branch("sp_dts0", &_sp_dts0); + fSPTree->Branch("sp_ts1", &_sp_ts1); + fSPTree->Branch("sp_ts1_rwm_ref", &_sp_ts1_rwm_ref); + fSPTree->Branch("sp_ts1_ptb_hlt_beam_gate_ref", &_sp_ts1_ptb_hlt_beam_gate_ref); + fSPTree->Branch("sp_ts1_rwm_ref_front_face", &_sp_ts1_rwm_ref_front_face); + fSPTree->Branch("sp_ts1_ptb_hlt_beam_gate_ref_front_face", &_sp_ts1_ptb_hlt_beam_gate_ref_front_face); + fSPTree->Branch("sp_dts1", &_sp_dts1); + fSPTree->Branch("sp_single_timing_chain", &_sp_single_timing_chain); + fSPTree->Branch("sp_timing_chain", &_sp_timing_chain); + fSPTree->Branch("sp_sh_channel_set", "std::vector", &_sp_sh_channel_set); + fSPTree->Branch("sp_sh_mac5_set", "std::vector", &_sp_sh_mac5_set); + fSPTree->Branch("sp_sh_timing_chain_set", "std::vector", &_sp_sh_timing_chain_set); + fSPTree->Branch("sp_sh_ts0_set", "std::vector", &_sp_sh_ts0_set); + fSPTree->Branch("sp_sh_ts1_set", "std::vector", &_sp_sh_ts1_set); + fSPTree->Branch("sp_sh_time_walk_set", "std::vector", &_sp_sh_time_walk_set); + fSPTree->Branch("sp_sh_prop_delay_set", "std::vector", &_sp_sh_prop_delay_set); + fSPTree->Branch("sp_sh_cable_length_ts0_set", "std::vector", &_sp_sh_cable_length_ts0_set); + fSPTree->Branch("sp_sh_calib_offset_ts0_set", "std::vector", &_sp_sh_calib_offset_ts0_set); + fSPTree->Branch("sp_sh_cable_length_ts1_set", "std::vector", &_sp_sh_cable_length_ts1_set); + fSPTree->Branch("sp_sh_calib_offset_ts1_set", "std::vector", &_sp_sh_calib_offset_ts1_set); + fSPTree->Branch("sp_has_track", &_sp_has_track); + fSPTree->Branch("sp_norm_angle", &_sp_norm_angle); + fSPTree->Branch("sp_path_length", &_sp_path_length); + + fTrTree = fs->make("tracks",""); + fTrTree->Branch("run", &_run); + fTrTree->Branch("subrun", &_subrun); + fTrTree->Branch("event", &_event); + fTrTree->Branch("crt_timing_reference_type", &_crt_timing_reference_type); + fTrTree->Branch("crt_timing_reference_channel", &_crt_timing_reference_channel); + + fTrTree->Branch("etrig_good", &_etrig_good); + fTrTree->Branch("rwm_good", &_rwm_good); + fTrTree->Branch("ptb_hlt_beam_gate_good", &_ptb_hlt_beam_gate_good); + fTrTree->Branch("crt_t1_reset_good", &_crt_t1_reset_good); + fTrTree->Branch("rwm_etrig_diff", &_rwm_etrig_diff); + fTrTree->Branch("ptb_hlt_beam_gate_etrig_diff", &_ptb_hlt_beam_gate_etrig_diff); + fTrTree->Branch("rwm_crt_t1_reset_diff", &_rwm_crt_t1_reset_diff); + fTrTree->Branch("ptb_hlt_beam_gate_crt_t1_reset_diff", &_ptb_hlt_beam_gate_crt_t1_reset_diff); + fTrTree->Branch("rwm_ptb_hlt_beam_gate_diff", &_rwm_ptb_hlt_beam_gate_diff); + + fTrTree->Branch("tr_start_x", &_tr_start_x); + fTrTree->Branch("tr_start_y", &_tr_start_y); + fTrTree->Branch("tr_start_z", &_tr_start_z); + fTrTree->Branch("tr_end_x", &_tr_end_x); + fTrTree->Branch("tr_end_y", &_tr_end_y); + fTrTree->Branch("tr_end_z", &_tr_end_z); + fTrTree->Branch("tr_dir_x", &_tr_dir_x); + fTrTree->Branch("tr_dir_y", &_tr_dir_y); + fTrTree->Branch("tr_dir_z", &_tr_dir_z); + fTrTree->Branch("tr_ts0", &_tr_ts0); + fTrTree->Branch("tr_ts0_rwm_ref", &_tr_ts0_rwm_ref); + fTrTree->Branch("tr_ts0_ptb_hlt_beam_gate_ref", &_tr_ts0_ptb_hlt_beam_gate_ref); + fTrTree->Branch("tr_ts1", &_tr_ts1); + fTrTree->Branch("tr_ts1_rwm_ref", &_tr_ts1_rwm_ref); + fTrTree->Branch("tr_ts1_ptb_hlt_beam_gate_ref", &_tr_ts1_ptb_hlt_beam_gate_ref); + fTrTree->Branch("tr_pe", &_tr_pe); + fTrTree->Branch("tr_length", &_tr_length); + fTrTree->Branch("tr_length_tof", &_tr_length_tof); + fTrTree->Branch("tr_tof_ts0", &_tr_tof_ts0); + fTrTree->Branch("tr_tof_diff_ts0", &_tr_tof_diff_ts0); + fTrTree->Branch("tr_tof_ts1", &_tr_tof_ts1); + fTrTree->Branch("tr_tof_diff_ts1", &_tr_tof_diff_ts1); + fTrTree->Branch("tr_theta", &_tr_theta); + fTrTree->Branch("tr_phi", &_tr_phi); + fTrTree->Branch("tr_triple", &_tr_triple); + fTrTree->Branch("tr_tagger1", &_tr_tagger1); + fTrTree->Branch("tr_tagger2", &_tr_tagger2); + fTrTree->Branch("tr_tagger3", &_tr_tagger3); + fTrTree->Branch("tr_start_tagger", &_tr_start_tagger); + fTrTree->Branch("tr_start_nhits", &_tr_start_nhits); + fTrTree->Branch("tr_start_dts0", &_tr_start_dts0); + fTrTree->Branch("tr_start_dts1", &_tr_start_dts1); + fTrTree->Branch("tr_start_single_timing_chain", &_tr_start_single_timing_chain); + fTrTree->Branch("tr_start_timing_chain", &_tr_start_timing_chain); + fTrTree->Branch("tr_end_tagger", &_tr_end_tagger); + fTrTree->Branch("tr_end_nhits", &_tr_end_nhits); + fTrTree->Branch("tr_end_dts0", &_tr_end_dts0); + fTrTree->Branch("tr_end_dts1", &_tr_end_dts1); + fTrTree->Branch("tr_end_single_timing_chain", &_tr_end_single_timing_chain); + fTrTree->Branch("tr_end_timing_chain", &_tr_end_timing_chain); + + fTPCTree = fs->make("slices",""); + fTPCTree->Branch("run", &_run); + fTPCTree->Branch("subrun", &_subrun); + fTPCTree->Branch("event", &_event); + fTPCTree->Branch("crt_timing_reference_type", &_crt_timing_reference_type); + fTPCTree->Branch("crt_timing_reference_channel", &_crt_timing_reference_channel); + + fTPCTree->Branch("etrig_good", &_etrig_good); + fTPCTree->Branch("rwm_good", &_rwm_good); + fTPCTree->Branch("ptb_hlt_beam_gate_good", &_ptb_hlt_beam_gate_good); + fTPCTree->Branch("crt_t1_reset_good", &_crt_t1_reset_good); + fTPCTree->Branch("rwm_etrig_diff", &_rwm_etrig_diff); + fTPCTree->Branch("ptb_hlt_beam_gate_etrig_diff", &_ptb_hlt_beam_gate_etrig_diff); + fTPCTree->Branch("rwm_crt_t1_reset_diff", &_rwm_crt_t1_reset_diff); + fTPCTree->Branch("ptb_hlt_beam_gate_crt_t1_reset_diff", &_ptb_hlt_beam_gate_crt_t1_reset_diff); + fTPCTree->Branch("rwm_ptb_hlt_beam_gate_diff", &_rwm_ptb_hlt_beam_gate_diff); + + fTPCTree->Branch("tpc_has_corrected_opflash", &_tpc_has_corrected_opflash); + fTPCTree->Branch("tpc_has_crt_sp_match", &_tpc_has_crt_sp_match); + fTPCTree->Branch("tpc_opflash_t0", &_tpc_opflash_t0); + fTPCTree->Branch("tpc_opflash_nutof_light", &_tpc_opflash_nutof_light); + fTPCTree->Branch("tpc_opflash_nutof_charge", &_tpc_opflash_nutof_charge); + fTPCTree->Branch("tpc_opflash_t0_corrected", &_tpc_opflash_t0_corrected); + fTPCTree->Branch("tpc_opflash_interaction_time_rwm", &_tpc_opflash_interaction_time_rwm); + fTPCTree->Branch("tpc_opflash_front_face_rwm", &_tpc_opflash_front_face_rwm); + fTPCTree->Branch("tpc_crt_sp_score", &_tpc_crt_sp_score); + fTPCTree->Branch("tpc_crt_sp_tagger", &_tpc_crt_sp_tagger); + fTPCTree->Branch("tpc_crt_sp_nhits", &_tpc_crt_sp_nhits); + fTPCTree->Branch("tpc_crt_sp_single_timing_chain", &_tpc_crt_sp_single_timing_chain); + fTPCTree->Branch("tpc_crt_sp_timing_chain", &_tpc_crt_sp_timing_chain); + fTPCTree->Branch("tpc_crt_sp_front_face_adjustment", &_tpc_crt_sp_front_face_adjustment); + fTPCTree->Branch("tpc_crt_sp_interaction_time_adjustment", &_tpc_crt_sp_interaction_time_adjustment); + fTPCTree->Branch("tpc_crt_sp_ts0", &_tpc_crt_sp_ts0); + fTPCTree->Branch("tpc_crt_sp_ts0_rwm_ref", &_tpc_crt_sp_ts0_rwm_ref); + fTPCTree->Branch("tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref", &_tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref); + fTPCTree->Branch("tpc_crt_sp_ts0_rwm_ref_front_face", &_tpc_crt_sp_ts0_rwm_ref_front_face); + fTPCTree->Branch("tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_front_face", &_tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_front_face); + fTPCTree->Branch("tpc_crt_sp_ts0_rwm_ref_interaction_time", &_tpc_crt_sp_ts0_rwm_ref_interaction_time); + fTPCTree->Branch("tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_interaction_time", &_tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_interaction_time); + fTPCTree->Branch("tpc_crt_sp_dts0", &_tpc_crt_sp_dts0); + fTPCTree->Branch("tpc_crt_sp_ts1", &_tpc_crt_sp_ts1); + fTPCTree->Branch("tpc_crt_sp_ts1_rwm_ref", &_tpc_crt_sp_ts1_rwm_ref); + fTPCTree->Branch("tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref", &_tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref); + fTPCTree->Branch("tpc_crt_sp_ts1_rwm_ref_front_face", &_tpc_crt_sp_ts1_rwm_ref_front_face); + fTPCTree->Branch("tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_front_face", &_tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_front_face); + fTPCTree->Branch("tpc_crt_sp_ts1_rwm_ref_interaction_time", &_tpc_crt_sp_ts1_rwm_ref_interaction_time); + fTPCTree->Branch("tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_interaction_time", &_tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_interaction_time); + fTPCTree->Branch("tpc_crt_sp_dts1", &_tpc_crt_sp_dts1); + fTPCTree->Branch("tpc_crt_ts0_pmt_diff", &_tpc_crt_ts0_pmt_diff); + fTPCTree->Branch("tpc_crt_ts1_pmt_diff", &_tpc_crt_ts1_pmt_diff); +} + +void sbnd::crt::CRTTimingAnalysis::analyze(art::Event const& e) +{ + ResetMaps(); + + _run = e.id().run(); + _subrun = e.id().subRun(); + _event = e.id().event(); + + _crt_timing_reference_type = -1; + _crt_timing_reference_channel = -1; + + art::Handle TimingReferenceHandle; + e.getByLabel(fTimingReferenceModuleLabel, TimingReferenceHandle); + if(TimingReferenceHandle.isValid()) + { + _crt_timing_reference_type = TimingReferenceHandle->timingType; + _crt_timing_reference_channel = TimingReferenceHandle->timingChannel; + } + + // Get PTBs + art::Handle> PTBHandle; + e.getByLabel(fPTBModuleLabel, PTBHandle); + if(!PTBHandle.isValid()){ + std::cout << "PTB product " << fPTBModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + std::vector> PTBVec; + art::fill_ptr_vector(PTBVec, PTBHandle); + + // Fill PTB variables + AnalysePTBs(PTBVec); + + // Get TDCs + art::Handle> TDCHandle; + e.getByLabel(fTDCModuleLabel, TDCHandle); + if(!TDCHandle.isValid()){ + std::cout << "TDC product " << fTDCModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + std::vector> TDCVec; + art::fill_ptr_vector(TDCVec, TDCHandle); + + // Fill TDC variables + AnalyseTDCs(TDCVec); + + SortReferencing(); + + // Get CRTSpacePoints + art::Handle> CRTSpacePointHandle; + e.getByLabel(fCRTSpacePointModuleLabel, CRTSpacePointHandle); + if(!CRTSpacePointHandle.isValid()){ + std::cout << "CRTSpacePoint product " << fCRTSpacePointModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + std::vector> CRTSpacePointVec; + art::fill_ptr_vector(CRTSpacePointVec, CRTSpacePointHandle); + + // Get CRTSpacePoint to CRTCluster Assns + art::FindOneP spacepointsToClusters(CRTSpacePointHandle, e, fCRTSpacePointModuleLabel); + + // Get CRTSpacePoint to CRTTrack Assns + art::FindOneP spacepointsToTracks(CRTSpacePointHandle, e, fCRTTrackModuleLabel); + + // Get CRTClusters + art::Handle> CRTClusterHandle; + e.getByLabel(fCRTClusterModuleLabel, CRTClusterHandle); + if(!CRTClusterHandle.isValid()){ + std::cout << "CRTCluster product " << fCRTClusterModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + + // Get CRTCluster to CRTStripHit Assns + art::FindManyP clustersToStripHits(CRTClusterHandle, e, fCRTClusterModuleLabel); + + // Fill CRTSpacePoint variables + AnalyseCRTSpacePoints(CRTSpacePointVec, spacepointsToClusters, clustersToStripHits, spacepointsToTracks); + + // Get CRTTracks + art::Handle> CRTTrackHandle; + e.getByLabel(fCRTTrackModuleLabel, CRTTrackHandle); + if(!CRTTrackHandle.isValid()){ + std::cout << "CRTTrack product " << fCRTTrackModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + std::vector> CRTTrackVec; + art::fill_ptr_vector(CRTTrackVec, CRTTrackHandle); + + // Get CRTTrack to CRTSpacePoint Assns + art::FindManyP tracksToSpacePoints(CRTTrackHandle, e, fCRTTrackModuleLabel); + + // Fill CRTTrack variables + AnalyseCRTTracks(CRTTrackVec, tracksToSpacePoints); + + // Get TPCSlices + art::Handle> TPCSliceHandle; + e.getByLabel(fTPCSliceModuleLabel, TPCSliceHandle); + if(!TPCSliceHandle.isValid()){ + std::cout << "TPCSlice product " << fTPCSliceModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + std::vector> TPCSliceVec; + art::fill_ptr_vector(TPCSliceVec, TPCSliceHandle); + + // Get TPCSlice to CorrectedOpFlash Assns + art::FindOneP sliceToCorrectedOpFlash(TPCSliceHandle, e, fCorrectedOpFlashModuleLabel); + + // Get TPCSlice to PFP Assns + art::FindManyP sliceToPFPs(TPCSliceHandle, e, fTPCSliceModuleLabel); + + // Get TPCPFPs + art::Handle> TPCPFPHandle; + e.getByLabel(fTPCSliceModuleLabel, TPCPFPHandle); + if(!TPCPFPHandle.isValid()){ + std::cout << "TPCPFP product " << fTPCSliceModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + + // Get PFP to Track Assns + art::FindOneP pfpToTrack(TPCPFPHandle, e, fTPCTrackModuleLabel); + + // Get TPCTracks + art::Handle> TPCTrackHandle; + e.getByLabel(fTPCTrackModuleLabel, TPCTrackHandle); + if(!TPCTrackHandle.isValid()){ + std::cout << "TPCTrack product " << fTPCTrackModuleLabel << " not found..." << std::endl; + throw std::exception(); + } + + // Get Track to CRTSpacePoint Assns + art::FindOneP trackToCRTSpacePoint(TPCTrackHandle, e, fCRTSpacePointMatchingModuleLabel); + + AnalyseTPCSlices(TPCSliceVec, sliceToCorrectedOpFlash, sliceToPFPs, pfpToTrack, trackToCRTSpacePoint); +} + +void sbnd::crt::CRTTimingAnalysis::ResetMaps() +{ + fSPTaggerMap.clear(); + fSPNHitsMap.clear(); + fSPdTs0Map.clear(); + fSPdTs1Map.clear(); + fSPTimingChainMap.clear(); +} + +void sbnd::crt::CRTTimingAnalysis::AnalysePTBs(std::vector> &PTBVec) +{ + unsigned nHLTs = 0; + + for(auto const& ptb : PTBVec) + nHLTs += ptb->GetNHLTriggers(); + + _ptb_hlt_trigger.resize(nHLTs); + _ptb_hlt_timestamp.resize(nHLTs); + + unsigned hlt_i = 0; + + for(auto const& ptb : PTBVec) + { + for(unsigned i = 0; i < ptb->GetNHLTriggers(); ++i) + { + _ptb_hlt_trigger[hlt_i] = ptb->GetHLTrigger(i).trigger_word; + _ptb_hlt_timestamp[hlt_i] = ptb->GetHLTrigger(i).timestamp * 20; + + ++hlt_i; + } + } + + unsigned nLLTs = 0; + + for(auto const& ptb : PTBVec) + nLLTs += ptb->GetNLLTriggers(); + + _ptb_llt_trigger.resize(nLLTs); + _ptb_llt_timestamp.resize(nLLTs); + + unsigned llt_i = 0; + + for(auto const& ptb : PTBVec) + { + for(unsigned i = 0; i < ptb->GetNLLTriggers(); ++i) + { + _ptb_llt_trigger[llt_i] = ptb->GetLLTrigger(i).trigger_word; + _ptb_llt_timestamp[llt_i] = ptb->GetLLTrigger(i).timestamp * 20; + + ++llt_i; + } + } +} + +void sbnd::crt::CRTTimingAnalysis::AnalyseTDCs(std::vector> &TDCVec) +{ + const unsigned nTDCs = TDCVec.size(); + + _tdc_channel.resize(nTDCs); + _tdc_timestamp.resize(nTDCs); + _tdc_offset.resize(nTDCs); + _tdc_name.resize(nTDCs); + + unsigned tdc_i = 0; + + for(auto const& tdc : TDCVec) + { + _tdc_channel[tdc_i] = tdc->Channel(); + _tdc_timestamp[tdc_i] = tdc->Timestamp(); + _tdc_offset[tdc_i] = tdc->Offset(); + _tdc_name[tdc_i] = tdc->Name(); + + ++tdc_i; + } +} + +void sbnd::crt::CRTTimingAnalysis::SortReferencing() +{ + _etrig_good = false; _rwm_good = false; _ptb_hlt_beam_gate_good = false; _crt_t1_reset_good = false; + _rwm_etrig_diff = std::numeric_limits::max(); _ptb_hlt_beam_gate_etrig_diff = std::numeric_limits::max(); + _rwm_crt_t1_reset_diff = std::numeric_limits::max(); _ptb_hlt_beam_gate_crt_t1_reset_diff = std::numeric_limits::max(); + _rwm_ptb_hlt_beam_gate_diff = std::numeric_limits::max(); + + int etrig_count = 0, etrig_id = -1, rwm_count = 0, rwm_id = -1, crt_t1_reset_count = 0, crt_t1_reset_id = -1; + + for(unsigned int tdc_i = 0; tdc_i < _tdc_channel.size(); ++tdc_i) + { + if(_tdc_channel[tdc_i] == 4) + { + ++etrig_count; + etrig_id = tdc_i; + } + else if(_tdc_channel[tdc_i] == 2) + { + ++rwm_count; + rwm_id = tdc_i; + } + else if(_tdc_channel[tdc_i] == 0) + { + ++crt_t1_reset_count; + crt_t1_reset_id = tdc_i; + } + } + + uint64_t etrig = std::numeric_limits::max(), rwm = std::numeric_limits::max(), + hlt = std::numeric_limits::max(), crt_t1_reset = std::numeric_limits::max(); + + if(etrig_count == 1) + { + _etrig_good = true; + etrig = _tdc_timestamp[etrig_id]; + } + + if(rwm_count == 1) + { + _rwm_good = true; + rwm = _tdc_timestamp[rwm_id]; + } + + if(etrig_count == 1) + { + double closest_diff = std::numeric_limits::max(); + + for(unsigned int ptb_i = 0; ptb_i < _ptb_hlt_trigger.size(); ++ptb_i) + { + std::bitset<32> hlt_bitmask = std::bitset<32>(_ptb_hlt_trigger[ptb_i]); + + for(uint32_t allowed_hlt : fAllowedPTBHLTs) + { + if(hlt_bitmask[allowed_hlt]) + { + _ptb_hlt_beam_gate_good = true; + + uint64_t temp_hlt = _ptb_hlt_timestamp[ptb_i]; + double diff = etrig > temp_hlt ? etrig - temp_hlt : -1. * (temp_hlt - etrig); + + if(std::abs(diff) < closest_diff) + { + closest_diff = diff; + hlt = temp_hlt; + } + } + } + } + } + + if(crt_t1_reset_count == 1) + { + _crt_t1_reset_good = true; + crt_t1_reset = _tdc_timestamp[crt_t1_reset_id]; + } + + if(_etrig_good && _rwm_good) + _rwm_etrig_diff = etrig > rwm ? etrig - rwm : -1. * (rwm - etrig); + + if(_etrig_good && _ptb_hlt_beam_gate_good) + _ptb_hlt_beam_gate_etrig_diff = etrig > hlt ? etrig - hlt : -1. * (hlt - etrig); + + if(_crt_t1_reset_good && _rwm_good) + _rwm_crt_t1_reset_diff = crt_t1_reset > rwm ? crt_t1_reset - rwm : -1. * (rwm - crt_t1_reset); + + if(_etrig_good && _crt_t1_reset_good && _ptb_hlt_beam_gate_good) + _ptb_hlt_beam_gate_crt_t1_reset_diff = crt_t1_reset > hlt ? crt_t1_reset - hlt : -1. * (hlt - crt_t1_reset); + + if(_etrig_good && _rwm_good && _ptb_hlt_beam_gate_good) + _rwm_ptb_hlt_beam_gate_diff = hlt > rwm ? hlt - rwm : -1. * (rwm - hlt); +} + +void sbnd::crt::CRTTimingAnalysis::AnalyseCRTSpacePoints(const std::vector> &CRTSpacePointVec, + const art::FindOneP &spacepointsToClusters, + const art::FindManyP &clustersToStripHits, + const art::FindOneP &spacepointsToTracks) +{ + for(auto const& sp : CRTSpacePointVec) + { + ResetSPVariables(); + + const art::Ptr cl = spacepointsToClusters.at(sp.key()); + const art::Ptr tr = spacepointsToTracks.at(sp.key()); + + const std::vector> shs = clustersToStripHits.at(cl.key()); + const unsigned n_shs = shs.size(); + ResizeSPSHVecs(n_shs); + + _sp_nhits = cl->NHits(); + _sp_tagger = cl->Tagger(); + _sp_x = sp->X(); + _sp_y = sp->Y(); + _sp_z = sp->Z(); + _sp_front_face_adjustment = _sp_z * fSpeedOfLightNanosecondPerCentimeter; + _sp_pe = sp->PE(); + _sp_ts0 = sp->Ts0(); + _sp_ts0_rwm_ref = _sp_ts0 + _rwm_etrig_diff; + _sp_ts0_ptb_hlt_beam_gate_ref = _sp_ts0 + _ptb_hlt_beam_gate_etrig_diff; + _sp_ts0_rwm_ref_front_face = _sp_ts0_rwm_ref - _sp_front_face_adjustment; + _sp_ts0_ptb_hlt_beam_gate_ref_front_face = _sp_ts0_ptb_hlt_beam_gate_ref - _sp_front_face_adjustment; + _sp_ts1 = sp->Ts1(); + _sp_ts1_rwm_ref = _sp_ts1 + _rwm_crt_t1_reset_diff; + _sp_ts1_ptb_hlt_beam_gate_ref = _sp_ts1 + _ptb_hlt_beam_gate_crt_t1_reset_diff; + _sp_ts1_rwm_ref_front_face = _sp_ts1_rwm_ref - _sp_front_face_adjustment; + _sp_ts1_ptb_hlt_beam_gate_ref_front_face = _sp_ts1_ptb_hlt_beam_gate_ref - _sp_front_face_adjustment; + + for(unsigned i = 0; i < n_shs; ++i) + { + const art::Ptr sh = shs[i]; + + _sp_sh_channel_set[i] = sh->Channel(); + _sp_sh_mac5_set[i] = fCRTChannelMapService->GetMAC5FromOfflineChannelID(_sp_sh_channel_set[i]); + _sp_sh_timing_chain_set[i] = fCRTChannelMapService->GetTimingChainFromOfflineChannelID(_sp_sh_channel_set[i]); + _sp_sh_ts0_set[i] = sh->Ts0(); + _sp_sh_ts1_set[i] = sh->Ts1(); + _sp_sh_time_walk_set[i] = fCRTClusterCharacAlg.TimeWalk(sh, {_sp_x, _sp_y, _sp_z}); + _sp_sh_prop_delay_set[i] = fCRTClusterCharacAlg.PropagationDelay(sh, {_sp_x, _sp_y, _sp_z}); + _sp_sh_cable_length_ts0_set[i] = fCRTCalibrationDatabaseService->getT0CableLengthOffset(_sp_sh_mac5_set[i]); + _sp_sh_calib_offset_ts0_set[i] = fCRTCalibrationDatabaseService->getT0CalibratedOffset(_sp_sh_mac5_set[i]); + _sp_sh_cable_length_ts1_set[i] = fCRTCalibrationDatabaseService->getT1CableLengthOffset(_sp_sh_mac5_set[i]); + _sp_sh_calib_offset_ts1_set[i] = fCRTCalibrationDatabaseService->getT1CalibratedOffset(_sp_sh_mac5_set[i]); + } + + _sp_dts0 = IntrinsicResolution(_sp_sh_channel_set, _sp_sh_ts0_set, _sp_sh_time_walk_set, _sp_sh_prop_delay_set); + _sp_dts1 = IntrinsicResolution(_sp_sh_channel_set, _sp_sh_ts1_set, _sp_sh_time_walk_set, _sp_sh_prop_delay_set); + + std::set timing_chain_set(_sp_sh_timing_chain_set.begin(), _sp_sh_timing_chain_set.end()); + _sp_single_timing_chain = timing_chain_set.size() == 1; + _sp_timing_chain = _sp_single_timing_chain ? *timing_chain_set.begin() : -1; + + if(tr.isNonnull()) + { + _sp_has_track = true; + + TVector3 normal; + if(_sp_tagger == kBottomTagger || _sp_tagger == kTopLowTagger || _sp_tagger == kTopHighTagger) + normal = TVector3(0, 1, 0); + else if(_sp_tagger == kWestTagger || _sp_tagger == kEastTagger) + normal = TVector3(1, 0, 0); + else if(_sp_tagger == kSouthTagger || _sp_tagger == kNorthTagger) + normal = TVector3(0, 0, 1); + + const TVector3 tr_dir(tr->Direction().X(), tr->Direction().Y(), tr->Direction().Z()); + _sp_norm_angle = TMath::RadToDeg() * normal.Angle(tr_dir); + _sp_path_length = 1. / TMath::Cos(normal.Angle(tr_dir)); + } + + fSPTree->Fill(); + + fSPTaggerMap[sp.key()] = _sp_tagger; + fSPNHitsMap[sp.key()] = _sp_nhits; + fSPdTs0Map[sp.key()] = _sp_dts0; + fSPdTs1Map[sp.key()] = _sp_dts1; + fSPTimingChainMap[sp.key()] = _sp_timing_chain; + } +} + +void sbnd::crt::CRTTimingAnalysis::ResetSPVariables() +{ + _sp_nhits = std::numeric_limits::max(); + + _sp_tagger = std::numeric_limits::lowest(); + _sp_timing_chain = std::numeric_limits::lowest(); + + _sp_x = std::numeric_limits::lowest(); + _sp_y = std::numeric_limits::lowest(); + _sp_z = std::numeric_limits::lowest(); + _sp_front_face_adjustment = std::numeric_limits::lowest(); + _sp_pe = std::numeric_limits::lowest(); + _sp_ts0 = std::numeric_limits::lowest(); + _sp_ts0_rwm_ref = std::numeric_limits::lowest(); + _sp_ts0_ptb_hlt_beam_gate_ref = std::numeric_limits::lowest(); + _sp_ts0_rwm_ref_front_face = std::numeric_limits::lowest(); + _sp_ts0_ptb_hlt_beam_gate_ref_front_face = std::numeric_limits::lowest(); + _sp_dts0 = std::numeric_limits::lowest(); + _sp_ts1 = std::numeric_limits::lowest(); + _sp_ts1_rwm_ref = std::numeric_limits::lowest(); + _sp_ts1_ptb_hlt_beam_gate_ref = std::numeric_limits::lowest(); + _sp_ts1_rwm_ref_front_face = std::numeric_limits::lowest(); + _sp_ts1_ptb_hlt_beam_gate_ref_front_face = std::numeric_limits::lowest(); + _sp_dts1 = std::numeric_limits::lowest(); + _sp_norm_angle = std::numeric_limits::lowest(); + _sp_path_length = std::numeric_limits::lowest(); + + _sp_single_timing_chain = false; + _sp_has_track = false; + + _sp_sh_channel_set.clear(); + _sp_sh_mac5_set.clear(); + _sp_sh_timing_chain_set.clear(); + _sp_sh_ts0_set.clear(); + _sp_sh_ts1_set.clear(); + _sp_sh_time_walk_set.clear(); + _sp_sh_prop_delay_set.clear(); + _sp_sh_cable_length_ts0_set.clear(); + _sp_sh_calib_offset_ts0_set.clear(); + _sp_sh_cable_length_ts1_set.clear(); + _sp_sh_calib_offset_ts1_set.clear(); +} + +void sbnd::crt::CRTTimingAnalysis::ResizeSPSHVecs(const unsigned n) +{ + _sp_sh_channel_set.resize(n); + _sp_sh_mac5_set.resize(n); + _sp_sh_timing_chain_set.resize(n); + _sp_sh_ts0_set.resize(n); + _sp_sh_ts1_set.resize(n); + _sp_sh_time_walk_set.resize(n); + _sp_sh_prop_delay_set.resize(n); + _sp_sh_cable_length_ts0_set.resize(n); + _sp_sh_calib_offset_ts0_set.resize(n); + _sp_sh_cable_length_ts1_set.resize(n); + _sp_sh_calib_offset_ts1_set.resize(n); +} + +double sbnd::crt::CRTTimingAnalysis::IntrinsicResolution(const std::vector &_sp_sh_channel_set, + const std::vector &_sp_sh_ts_set, + const std::vector &_sp_sh_time_walk_set, + const std::vector &_sp_sh_prop_delay_set) +{ + struct SH { + int32_t channel; + double ts; + }; + + std::vector shs; + + for(unsigned i = 0; i < _sp_sh_channel_set.size(); ++i) + shs.push_back(SH({_sp_sh_channel_set[i], _sp_sh_ts_set[i] - _sp_sh_time_walk_set[i] - _sp_sh_prop_delay_set[i]})); + + std::sort(shs.begin(), shs.end(), [](auto &a, auto &b) + { return a.channel < b.channel; }); + + double sum = 0.; + + for(unsigned int i = 0; i < shs.size(); ++i) + { + for(unsigned int ii = i + 1; ii < shs.size(); ++ii) + sum += (shs[i].ts - shs[ii].ts); + } + + sum /= (shs.size() - 1); + + return sum; +} + +void sbnd::crt::CRTTimingAnalysis::AnalyseCRTTracks(const std::vector> &CRTTrackVec, + const art::FindManyP &tracksToSpacePoints) +{ + for(auto const &tr : CRTTrackVec) + { + ResetTrVariables(); + + _tr_start_x = tr->Start().X(); + _tr_start_y = tr->Start().Y(); + _tr_start_z = tr->Start().Z(); + _tr_end_x = tr->End().X(); + _tr_end_y = tr->End().Y(); + _tr_end_z = tr->End().Z(); + _tr_dir_x = tr->Direction().X(); + _tr_dir_y = tr->Direction().Y(); + _tr_dir_z = tr->Direction().Z(); + _tr_ts0 = tr->Ts0(); + _tr_ts0_rwm_ref = _tr_ts0 + _rwm_etrig_diff; + _tr_ts0_ptb_hlt_beam_gate_ref = _tr_ts0 + _ptb_hlt_beam_gate_etrig_diff; + _tr_ts1 = tr->Ts1(); + _tr_ts1_rwm_ref = _tr_ts1 + _rwm_crt_t1_reset_diff; + _tr_ts1_ptb_hlt_beam_gate_ref = _tr_ts1 + _ptb_hlt_beam_gate_crt_t1_reset_diff; + _tr_pe = tr->PE(); + _tr_length = tr->Length(); + _tr_length_tof = _tr_length * fSpeedOfLightNanosecondPerCentimeter; + _tr_theta = tr->Theta(); + _tr_phi = tr->Phi(); + _tr_triple = tr->Triple(); + _tr_tagger1 = tr->Taggers()[0]; + _tr_tagger2 = tr->Taggers()[1]; + + if(_tr_triple) + _tr_tagger3 = tr->Taggers()[2]; + + std::vector> sps = tracksToSpacePoints.at(tr.key()); + std::sort(sps.begin(), sps.end(), [](auto &a, auto &b) + { return a->Ts0() < b->Ts0(); }); + + if((_tr_triple && sps.size() != 3) || (!_tr_triple && sps.size() != 2)) + { + const int expectation = _tr_triple ? 3 : 2; + std::cout << "CRTSpacePoint vector wrong size (" << sps.size() + << ") for track expectation (" << expectation << ")" << std::endl; + throw std::exception(); + } + + const art::Ptr start = sps[0]; + const art::Ptr end = _tr_triple ? sps[2] : sps[1]; + + _tr_tof_ts0 = end->Ts0() - start->Ts0(); + _tr_tof_diff_ts0 = _tr_tof_ts0 - _tr_length_tof; + _tr_tof_ts1 = end->Ts1() - start->Ts1(); + _tr_tof_diff_ts1 = _tr_tof_ts1 - _tr_length_tof; + _tr_start_tagger = fSPTaggerMap[start.key()]; + _tr_start_nhits = fSPNHitsMap[start.key()]; + _tr_start_dts0 = fSPdTs0Map[start.key()]; + _tr_start_dts1 = fSPdTs1Map[start.key()]; + _tr_start_timing_chain = fSPTimingChainMap[start.key()]; + _tr_start_single_timing_chain = _tr_start_timing_chain != -1; + _tr_end_tagger = fSPTaggerMap[end.key()]; + _tr_end_nhits = fSPNHitsMap[start.key()]; + _tr_end_dts0 = fSPdTs0Map[end.key()]; + _tr_end_dts1 = fSPdTs1Map[end.key()]; + _tr_end_timing_chain = fSPTimingChainMap[end.key()]; + _tr_end_single_timing_chain = _tr_end_timing_chain != -1; + + if(_tr_start_tagger != _tr_tagger1) + { + std::cout << "CRTTrack start tagger inconsistent between direct access (" << _tr_tagger1 + << ") and associated CRTSpacePoint (" << _tr_start_tagger << ")" << std::endl; + throw std::exception(); + } + + if((_tr_triple && _tr_end_tagger != _tr_tagger3) || (!_tr_triple && _tr_end_tagger != _tr_tagger2)) + { + const int expectation = _tr_triple ? _tr_tagger3 : _tr_tagger2; + std::cout << "CRTTrack end tagger inconsistent between direct access (" << expectation + << ") and associated CRTSpacePoint (" << _tr_end_tagger << ")" << std::endl; + throw std::exception(); + } + + fTrTree->Fill(); + } +} + +void sbnd::crt::CRTTimingAnalysis::ResetTrVariables() +{ + _tr_start_x = std::numeric_limits::lowest(); + _tr_start_y = std::numeric_limits::lowest(); + _tr_start_z = std::numeric_limits::lowest(); + _tr_end_x = std::numeric_limits::lowest(); + _tr_end_y = std::numeric_limits::lowest(); + _tr_end_z = std::numeric_limits::lowest(); + _tr_dir_x = std::numeric_limits::lowest(); + _tr_dir_y = std::numeric_limits::lowest(); + _tr_dir_z = std::numeric_limits::lowest(); + _tr_ts0 = std::numeric_limits::lowest(); + _tr_ts0_rwm_ref = std::numeric_limits::lowest(); + _tr_ts0_ptb_hlt_beam_gate_ref = std::numeric_limits::lowest(); + _tr_ts1 = std::numeric_limits::lowest(); + _tr_ts1_rwm_ref = std::numeric_limits::lowest(); + _tr_ts1_ptb_hlt_beam_gate_ref = std::numeric_limits::lowest(); + _tr_pe = std::numeric_limits::lowest(); + _tr_length = std::numeric_limits::lowest(); + _tr_length_tof = std::numeric_limits::lowest(); + _tr_tof_ts0 = std::numeric_limits::lowest(); + _tr_tof_diff_ts0 = std::numeric_limits::lowest(); + _tr_tof_ts1 = std::numeric_limits::lowest(); + _tr_tof_diff_ts1 = std::numeric_limits::lowest(); + _tr_theta = std::numeric_limits::lowest(); + _tr_phi = std::numeric_limits::lowest(); + _tr_start_dts0 = std::numeric_limits::lowest(); + _tr_start_dts1 = std::numeric_limits::lowest(); + _tr_end_dts0 = std::numeric_limits::lowest(); + _tr_end_dts1 = std::numeric_limits::lowest(); + + _tr_triple = false; + _tr_start_single_timing_chain = false; + _tr_end_single_timing_chain = false; + + _tr_tagger1 = std::numeric_limits::lowest(); + _tr_tagger2 = std::numeric_limits::lowest(); + _tr_tagger3 = std::numeric_limits::lowest(); + _tr_start_tagger = std::numeric_limits::lowest(); + _tr_start_timing_chain = std::numeric_limits::lowest(); + _tr_end_tagger = std::numeric_limits::lowest(); + _tr_end_timing_chain = std::numeric_limits::lowest(); + + _tr_start_nhits = std::numeric_limits::max(); + _tr_end_nhits = std::numeric_limits::max(); + +} + +void sbnd::crt::CRTTimingAnalysis::AnalyseTPCSlices(const std::vector> &TPCSliceVec, + const art::FindOneP &sliceToCorrectedOpFlash, + const art::FindManyP &sliceToPFPs, + const art::FindOneP &pfpToTrack, + const art::FindOneP &trackToCRTSpacePoint) +{ + for(auto const &sl : TPCSliceVec) + { + ResetTPCVariables(); + + const art::Ptr copflash = sliceToCorrectedOpFlash.at(sl.key()); + + if(copflash.isNonnull()) + { + _tpc_has_corrected_opflash = true; + _tpc_opflash_t0 = copflash->OpFlashT0 * fMicrosecondToNanosecond; + _tpc_opflash_nutof_light = copflash->NuToFLight * fMicrosecondToNanosecond; + _tpc_opflash_nutof_charge = copflash->NuToFCharge * fMicrosecondToNanosecond; + _tpc_opflash_t0_corrected = copflash->OpFlashT0Corrected * fMicrosecondToNanosecond; + _tpc_opflash_interaction_time_rwm = _tpc_opflash_t0_corrected; + _tpc_opflash_front_face_rwm = _tpc_opflash_interaction_time_rwm - _tpc_opflash_nutof_charge; + } + + const std::vector> pfps = sliceToPFPs.at(sl.key()); + + for(auto const &pfp : pfps) + { + const art::Ptr track = pfpToTrack.at(pfp.key()); + + if(track.isNonnull()) + { + const art::Ptr sp = trackToCRTSpacePoint.at(track.key()); + + if(sp.isNonnull()) + { + const anab::T0 spMatch = trackToCRTSpacePoint.data(track.key()).ref(); + + const geo::Point_t track_start = track->Start(); + const geo::Point_t track_end = track->End(); + const geo::Point_t sp_pos = sp->Pos(); + + geo::Point_t interaction_point = (track_start - sp_pos).R() > (track_end - sp_pos).R() ? track_start : track_end; + const double dist = (interaction_point - sp_pos).R(); + + if(_tpc_has_crt_sp_match) + { + const double diff = _tpc_crt_sp_ts0_rwm_ref_interaction_time - _tpc_opflash_interaction_time_rwm - 120; + const double new_crt = sp->Ts0() + _rwm_etrig_diff - dist * fSpeedOfLightNanosecondPerCentimeter; + const double new_diff = new_crt - _tpc_opflash_interaction_time_rwm - 120; + + if(new_diff > diff) + continue; + } + + _tpc_has_crt_sp_match = true; + _tpc_crt_sp_score = spMatch.TriggerConfidence(); + _tpc_crt_sp_tagger = fSPTaggerMap[sp.key()]; + _tpc_crt_sp_nhits = fSPNHitsMap[sp.key()]; + _tpc_crt_sp_timing_chain = fSPTimingChainMap[sp.key()]; + _tpc_crt_sp_single_timing_chain = _tpc_crt_sp_timing_chain != -1; + _tpc_crt_sp_front_face_adjustment = sp_pos.Z() * fSpeedOfLightNanosecondPerCentimeter; + _tpc_crt_sp_interaction_time_adjustment = dist * fSpeedOfLightNanosecondPerCentimeter; + _tpc_crt_sp_ts0 = sp->Ts0(); + _tpc_crt_sp_ts0_rwm_ref = _tpc_crt_sp_ts0 + _rwm_etrig_diff; + _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref = _tpc_crt_sp_ts0 + _ptb_hlt_beam_gate_etrig_diff; + _tpc_crt_sp_ts0_rwm_ref_front_face = _tpc_crt_sp_ts0_rwm_ref - _tpc_crt_sp_front_face_adjustment; + _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_front_face = _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref - _tpc_crt_sp_front_face_adjustment; + _tpc_crt_sp_ts0_rwm_ref_interaction_time = _tpc_crt_sp_ts0_rwm_ref - _tpc_crt_sp_interaction_time_adjustment; + _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_interaction_time = _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref - _tpc_crt_sp_interaction_time_adjustment; + _tpc_crt_sp_dts0 = fSPdTs0Map[sp.key()]; + _tpc_crt_sp_ts1 = sp->Ts1(); + _tpc_crt_sp_ts1_rwm_ref = _tpc_crt_sp_ts1 + _rwm_crt_t1_reset_diff; + _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref = _tpc_crt_sp_ts1 + _ptb_hlt_beam_gate_crt_t1_reset_diff; + _tpc_crt_sp_ts1_rwm_ref_front_face = _tpc_crt_sp_ts1_rwm_ref - _tpc_crt_sp_front_face_adjustment; + _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_front_face = _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref - _tpc_crt_sp_front_face_adjustment; + _tpc_crt_sp_ts1_rwm_ref_interaction_time = _tpc_crt_sp_ts1_rwm_ref - _tpc_crt_sp_interaction_time_adjustment; + _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_interaction_time = _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref - _tpc_crt_sp_interaction_time_adjustment; + _tpc_crt_sp_dts1 = fSPdTs1Map[sp.key()]; + } + } + } + + if(_tpc_has_corrected_opflash && _tpc_has_crt_sp_match) + { + _tpc_crt_ts0_pmt_diff = _tpc_crt_sp_ts0_rwm_ref_interaction_time - _tpc_opflash_interaction_time_rwm; + _tpc_crt_ts1_pmt_diff = _tpc_crt_sp_ts1_rwm_ref_interaction_time - _tpc_opflash_interaction_time_rwm; + } + + fTPCTree->Fill(); + } +} + +void sbnd::crt::CRTTimingAnalysis::ResetTPCVariables() +{ + _tpc_has_corrected_opflash = false; + _tpc_has_crt_sp_match = false; + _tpc_crt_sp_single_timing_chain = false; + + _tpc_opflash_t0 = std::numeric_limits::lowest(); + _tpc_opflash_nutof_light = std::numeric_limits::lowest(); + _tpc_opflash_nutof_charge = std::numeric_limits::lowest(); + _tpc_opflash_t0_corrected = std::numeric_limits::lowest(); + _tpc_opflash_interaction_time_rwm = std::numeric_limits::lowest(); + _tpc_opflash_front_face_rwm = std::numeric_limits::lowest(); + _tpc_crt_sp_score = std::numeric_limits::lowest(); + _tpc_crt_sp_front_face_adjustment = std::numeric_limits::lowest(); + _tpc_crt_sp_interaction_time_adjustment = std::numeric_limits::lowest(); + _tpc_crt_sp_ts0 = std::numeric_limits::lowest(); + _tpc_crt_sp_ts0_rwm_ref = std::numeric_limits::lowest(); + _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref = std::numeric_limits::lowest(); + _tpc_crt_sp_ts0_rwm_ref_front_face = std::numeric_limits::lowest(); + _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_front_face = std::numeric_limits::lowest(); + _tpc_crt_sp_ts0_rwm_ref_interaction_time = std::numeric_limits::lowest(); + _tpc_crt_sp_ts0_ptb_hlt_beam_gate_ref_interaction_time = std::numeric_limits::lowest(); + _tpc_crt_sp_dts0 = std::numeric_limits::lowest(); + _tpc_crt_sp_ts1 = std::numeric_limits::lowest(); + _tpc_crt_sp_ts1_rwm_ref = std::numeric_limits::lowest(); + _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref = std::numeric_limits::lowest(); + _tpc_crt_sp_ts1_rwm_ref_front_face = std::numeric_limits::lowest(); + _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_front_face = std::numeric_limits::lowest(); + _tpc_crt_sp_ts1_rwm_ref_interaction_time = std::numeric_limits::lowest(); + _tpc_crt_sp_ts1_ptb_hlt_beam_gate_ref_interaction_time = std::numeric_limits::lowest(); + _tpc_crt_sp_dts1 = std::numeric_limits::lowest(); + _tpc_crt_ts0_pmt_diff = std::numeric_limits::lowest(); + _tpc_crt_ts1_pmt_diff = std::numeric_limits::lowest(); + + _tpc_crt_sp_tagger = std::numeric_limits::lowest(); + _tpc_crt_sp_timing_chain = std::numeric_limits::lowest(); + + _tpc_crt_sp_nhits = std::numeric_limits::max(); +} + +DEFINE_ART_MODULE(sbnd::crt::CRTTimingAnalysis) diff --git a/sbndcode/CRT/CRTAna/README.md b/sbndcode/CRT/CRTAna/README.md new file mode 100644 index 000000000..7b7e26cf3 --- /dev/null +++ b/sbndcode/CRT/CRTAna/README.md @@ -0,0 +1,3 @@ +# CRT Analysis Modules + +Details of these modules and the outputs (trees, branches, histograms) can be found in [docDB#45886](https://sbn-docdb.fnal.gov/cgi-bin/sso/ShowDocument?docid=45886) diff --git a/sbndcode/CRT/CRTAna/adrift_sbnd.fcl b/sbndcode/CRT/CRTAna/adrift_sbnd.fcl new file mode 100644 index 000000000..8113032ed --- /dev/null +++ b/sbndcode/CRT/CRTAna/adrift_sbnd.fcl @@ -0,0 +1,31 @@ +BEGIN_PROLOG + +adrift_data_sbnd: +{ + module_type: "ADRIFT" + FEBDataModuleLabel: "crtdecoder" + CRTStripHitModuleLabel: "crtstrips" + CRTClusterModuleLabel: "crtclustering" + CRTSpacePointModuleLabel: "crtspacepoints" + CRTTrackModuleLabel: "crttracks" + DAQHeaderModuleLabel: "daq" + DAQHeaderInstanceLabel: "RawEventHeader" + TopSaveDirectory: "/ENTER/YOUR/SAVE/DIRECTORY/HERE" + Only2HitSpacePoints: false + SaveAllFits: false + SaveBadFits: false + SaveSubset: false + FEBs: true + StripHits: true + SpacePoints: true + Tracks: true + TrackLA: true + SaveROOTHists: true + AnalysePE: false + TrackAngleLimit: 20 + PullWindow: 0.05 + ReconstructionWindow: 3.22e-3 + RawTSCorrection: 367000 +} + +END_PROLOG diff --git a/sbndcode/CRT/CRTAna/crtana_sbnd.fcl b/sbndcode/CRT/CRTAna/crtana_sbnd.fcl index f0a36b111..f319bc4df 100644 --- a/sbndcode/CRT/CRTAna/crtana_sbnd.fcl +++ b/sbndcode/CRT/CRTAna/crtana_sbnd.fcl @@ -1,23 +1,22 @@ #include "crtbacktrackeralg_sbnd.fcl" -#include "crtsimmodules_sbnd.fcl" +#include "crtrecoproducers_sbnd.fcl" BEGIN_PROLOG crtana_sbnd: { - CRTBackTrackerAlg: @local::crtbacktrackeralg_sbnd - PEAttenuation: @local::sbnd_crtsim.DetSimParams.NpeScaleShift - PropDelay: @local::sbnd_crtsim.DetSimParams.PropDelay - TimeWalkNorm: @local::sbnd_crtsim.DetSimParams.TDelayNorm - TimeWalkScale: @local::sbnd_crtsim.DetSimParams.TDelayScale - TruthMatch: false - module_type: "CRTAnalysis" + CRTBackTrackerAlg: @local::crtbacktrackeralg_sbnd + CRTClusterCharacterisationAlg: @local::crtclustercharacterisationalg_sbnd + TruthMatch: false + module_type: "CRTAnalysis" } crtana_data_sbnd: @local::crtana_sbnd -crtana_data_sbnd.FEBDataModuleLabel: "crtdecoder" -crtana_data_sbnd.DataMode: true -crtana_data_sbnd.HasPTB: true -crtana_data_sbnd.HasTDC: true +crtana_data_sbnd.FEBDataModuleLabel: "crtdecoder" +crtana_data_sbnd.DataMode: true +crtana_data_sbnd.NoTPC: false +crtana_data_sbnd.HasPTB: true +crtana_data_sbnd.HasTDC: true +crtana_data_sbnd.CRTClusterCharacterisationAlg: @local::crtclustercharacterisationalg_data_sbnd END_PROLOG diff --git a/sbndcode/CRT/CRTAna/crtrateana_sbnd.fcl b/sbndcode/CRT/CRTAna/crtrateana_sbnd.fcl new file mode 100644 index 000000000..b9ffc2186 --- /dev/null +++ b/sbndcode/CRT/CRTAna/crtrateana_sbnd.fcl @@ -0,0 +1,13 @@ +BEGIN_PROLOG + +crtrateana_data_sbnd: +{ + FEBDataModuleLabel: "crtdecoder" + CRTSpacePointModuleLabel: "crtspacepoints" + CRTBlobModuleLabel: "crtblobs" + DAQHeaderModuleLabel: "daq" + DAQHeaderInstanceLabel: "RawEventHeader" + module_type: "CRTRateAnalysis" +} + +END_PROLOG diff --git a/sbndcode/CRT/CRTAna/crttimingana_sbnd.fcl b/sbndcode/CRT/CRTAna/crttimingana_sbnd.fcl new file mode 100644 index 000000000..406e07264 --- /dev/null +++ b/sbndcode/CRT/CRTAna/crttimingana_sbnd.fcl @@ -0,0 +1,22 @@ +#include "crtrecoproducers_sbnd.fcl" + +BEGIN_PROLOG + +crttimingana_data_sbnd: +{ + module_type: "CRTTimingAnalysis" + CRTClusterModuleLabel: "crtclustering" + CRTSpacePointModuleLabel: "crtspacepoints" + CRTTrackModuleLabel: "crttracks" + PTBModuleLabel: "ptbdecoder" + TDCModuleLabel: "tdcdecoder" + TimingReferenceModuleLabel: "crtstrips" + TPCSliceModuleLabel: "pandoraSCE" + CorrectedOpFlashModuleLabel: "lightpropagationcorrectionSCE" + TPCTrackModuleLabel: "pandoraSCETrack" + CRTSpacePointMatchingModuleLabel: "crtspacepointmatchingSCE" + AllowedPTBHLTs: [ 26, 27 ] + CRTClusterCharacterisationAlg: @local::crtclustercharacterisationalg_data_sbnd +} + +END_PROLOG diff --git a/sbndcode/CRT/CRTAna/run_adrift_data.fcl b/sbndcode/CRT/CRTAna/run_adrift_data.fcl new file mode 100644 index 000000000..237978055 --- /dev/null +++ b/sbndcode/CRT/CRTAna/run_adrift_data.fcl @@ -0,0 +1,28 @@ +#include "services_sbnd.fcl" +#include "crt_services_sbnd.fcl" +#include "adrift_sbnd.fcl" + +process_name: ADRIFT + +services: +{ + TFileService: { fileName: "adrift_sbnd.root" } + @table::sbnd_basic_services + @table::crt_services_data_sbnd +} + +source: +{ + module_type: RootInput +} + +physics: +{ + analyzers: + { + adrift: @local::adrift_data_sbnd + } + + ana: [ adrift ] + end_paths: [ ana ] +} diff --git a/sbndcode/CRT/CRTAna/run_crtrateana_data.fcl b/sbndcode/CRT/CRTAna/run_crtrateana_data.fcl new file mode 100644 index 000000000..b3c6f1c4e --- /dev/null +++ b/sbndcode/CRT/CRTAna/run_crtrateana_data.fcl @@ -0,0 +1,28 @@ +#include "services_sbnd.fcl" +#include "crt_services_sbnd.fcl" +#include "crtrateana_sbnd.fcl" + +process_name: CRTRateAnalysis + +services: +{ + @table::sbnd_basic_services + @table::crt_services_data_sbnd + TFileService: { fileName: "crtrateana_sbnd.root" } +} + +source: +{ + module_type: RootInput +} + +physics: +{ + analyzers: + { + crtrateana: @local::crtrateana_data_sbnd + } + + ana: [ crtrateana ] + end_paths: [ ana ] +} diff --git a/sbndcode/CRT/CRTAna/run_crttimingana_data.fcl b/sbndcode/CRT/CRTAna/run_crttimingana_data.fcl new file mode 100644 index 000000000..bcdcfd52a --- /dev/null +++ b/sbndcode/CRT/CRTAna/run_crttimingana_data.fcl @@ -0,0 +1,28 @@ +#include "services_sbnd.fcl" +#include "crt_services_sbnd.fcl" +#include "crttimingana_sbnd.fcl" + +process_name: CRTTimingAna + +services: +{ + TFileService: { fileName: "crttimingana_sbnd.root" } + @table::sbnd_basic_services + @table::crt_services_data_sbnd +} + +source: +{ + module_type: RootInput +} + +physics: +{ + analyzers: + { + crttimingana: @local::crttimingana_data_sbnd + } + + ana: [ crttimingana ] + end_paths: [ ana ] +} diff --git a/sbndcode/CRT/CRTEventDisplay/CMakeLists.txt b/sbndcode/CRT/CRTEventDisplay/CMakeLists.txt index 0a8aa82af..c245b2729 100644 --- a/sbndcode/CRT/CRTEventDisplay/CMakeLists.txt +++ b/sbndcode/CRT/CRTEventDisplay/CMakeLists.txt @@ -13,4 +13,9 @@ simple_plugin( sbndcode_CRT_CRTEventDisplay ) +simple_plugin( + CRTChannelMappingEventDisplay module + sbndcode_CRT_CRTEventDisplay +) + install_fhicl() diff --git a/sbndcode/CRT/CRTEventDisplay/CRTChannelMappingEventDisplay_module.cc b/sbndcode/CRT/CRTEventDisplay/CRTChannelMappingEventDisplay_module.cc new file mode 100644 index 000000000..cfa00cd45 --- /dev/null +++ b/sbndcode/CRT/CRTEventDisplay/CRTChannelMappingEventDisplay_module.cc @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////// +// Class: CRTChannelMappingEventDisplay +// Plugin Type: analyzer (Unknown Unknown) +// File: CRTChannelMappingEventDisplay_module.cc +// +// Generated at Thu Oct 6 09:32:09 2022 by Henry Lay using cetskelgen +// from version . +//////////////////////////////////////////////////////////////////////// + +#include "art/Framework/Core/EDAnalyzer.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Principal/Run.h" +#include "art/Framework/Principal/SubRun.h" +#include "canvas/Utilities/InputTag.h" +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" + +#include "lardata/DetectorInfoServices/DetectorClocksService.h" +#include "sbndcode/CRT/CRTEventDisplay/CRTEventDisplayAlg.h" + +#include "TSystem.h" + +namespace sbnd::crt { + class CRTChannelMappingEventDisplay; +} + +class sbnd::crt::CRTChannelMappingEventDisplay : public art::EDAnalyzer { +public: + + struct Config { + using Name = fhicl::Name; + using Comment = fhicl::Comment; + + fhicl::Table EventDisplayConfig { + Name("EventDisplayConfig"), + }; + + fhicl::Atom SaveDir { + Name("SaveDir"), + }; + }; + + using Parameters = art::EDAnalyzer::Table; + + explicit CRTChannelMappingEventDisplay(Parameters const &config); + + CRTChannelMappingEventDisplay(CRTChannelMappingEventDisplay const&) = delete; + CRTChannelMappingEventDisplay(CRTChannelMappingEventDisplay&&) = delete; + CRTChannelMappingEventDisplay& operator=(CRTChannelMappingEventDisplay const&) = delete; + CRTChannelMappingEventDisplay& operator=(CRTChannelMappingEventDisplay&&) = delete; + + void analyze(art::Event const& e) override; + +private: + + CRTEventDisplayAlg fCRTEventDisplayAlg; + art::ServiceHandle fCRTGeoService; + std::string fSaveDir; + std::vector fChosenTaggers; +}; + + +sbnd::crt::CRTChannelMappingEventDisplay::CRTChannelMappingEventDisplay(Parameters const& config) + : EDAnalyzer{config} + , fCRTEventDisplayAlg(config().EventDisplayConfig()) + , fSaveDir(config().SaveDir()) + , fChosenTaggers(config().EventDisplayConfig().ChosenTaggers()) + { + gSystem->Exec(Form("mkdir -p %s", fSaveDir.c_str())); + } + +void sbnd::crt::CRTChannelMappingEventDisplay::analyze(art::Event const& e) +{ + auto const clockData = art::ServiceHandle()->DataFor(e); + + for(auto const& [ name, module ] : fCRTGeoService->GetModules()) + { + if(std::find(fChosenTaggers.begin(), fChosenTaggers.end(), CRTCommonUtils::GetTaggerEnum(module.taggerName)) == fChosenTaggers.end()) + continue; + + fCRTEventDisplayAlg.SetHighlightedModules({module.adID}); + + fCRTEventDisplayAlg.Draw(clockData, e, Form("%s/%s", fSaveDir.c_str(), name.c_str())); + } +} + +DEFINE_ART_MODULE(sbnd::crt::CRTChannelMappingEventDisplay) diff --git a/sbndcode/CRT/CRTEventDisplay/build_tex.sh b/sbndcode/CRT/CRTEventDisplay/build_tex.sh new file mode 100644 index 000000000..235039478 --- /dev/null +++ b/sbndcode/CRT/CRTEventDisplay/build_tex.sh @@ -0,0 +1,83 @@ +version=$1 +gdml=$2 +author=$3 +email=$4 +basedir=$5 + +echo $version +echo $gdml +echo $author +echo $email +echo $basedir + +mkdir ${basedir}/tex_work + +echo "\documentclass{article} +\usepackage[a4paper, margin=3cm]{geometry} +\usepackage{graphicx} +\usepackage{pgffor} +\usepackage[hidelinks]{hyperref} + +\title{SBND CRT Channel Mapping Displays \\\\ \vspace{1em} \small \textit{Produced using} \texttt{sbndcode ${version}} \textit{\&} \texttt{${gdml}}} +\author{${author} \\\\ \small ${email}}" > ${basedir}/tex_work/crt_channel_mapping_evds.tex + +walls=(bottom south north west east toplow tophigh) +wallnames=(Bottom South North West East "Top Low" "Top High") + +for wall in "${walls[@]}" +do + list=$(ls ${basedir}/${wall}_wall/*_front.pdf) + echo -n "\newcommand*{\\"$wall"ids}{" >> ${basedir}/tex_work/crt_channel_mapping_evds.tex + + for item in ${list} + do + name=$(echo $item | cut -d '/' -f 10) + number=$(echo $name | cut -d '_' -f 2) + echo -n $number, >> ${basedir}/tex_work/crt_channel_mapping_evds.tex + done + + sed -i '$ s/.$//' ${basedir}/tex_work/crt_channel_mapping_evds.tex + echo "}" >> ${basedir}/tex_work/crt_channel_mapping_evds.tex +done + +echo "\begin{document} + +\maketitle + +\centering +\vspace{2em} + +\includegraphics[width=.6\textwidth]{/nashome/h/hlay/UOSLogo_Primary_Violet_RGB.png} +\vspace{2em} + +\includegraphics[width=.5\textwidth]{/nashome/h/hlay/sbnd_pride_transparent.png} +\flushleft +\newpage +\tableofcontents +\newpage +\section{Explanation} +This document contains a series of illustrations created using the \texttt{CRTEventDisplay} tool originally written by Tom Brooks \& heavily developed by myself. It shows the position of the various CRT modules according to the gdml file used in SBND simulation and reconstruction. The document is split into sections for the different tagger walls. For each module three illustrations are provided: front, top and side views. The axes show detector coordinates (X, Y and Z) and \`\`building coordinates\" (North, West and Up). The relevant module is shown in green. The TPCs are shown in grey in the centre for reference. The black outer is the full tagger wall. The thin grey are other modules in the wall. The red is the FEB position and the blue corresponds to the end of the FEB with channel 0 (the ethernet ports). +" >> ${basedir}/tex_work/crt_channel_mapping_evds.tex + +for i in "${!walls[@]}" +do + echo "\newpage +\section{${wallnames[i]} Wall} +\begingroup +\foreach \x in \\${walls[i]}ids +{ + \newpage + \subsection{volCRTModule\x\_\x} + \begin{center} + \includegraphics[width=.85\textwidth]{${basedir}/${walls[i]}_wall/volCRTModule\x_\x_front.pdf}\\\\ + \includegraphics[width=.85\textwidth]{${basedir}/${walls[i]}_wall/volCRTModule\x_\x_top.pdf}\\\\ + \includegraphics[width=.85\textwidth]{${basedir}/${walls[i]}_wall/volCRTModule\x_\x_side.pdf} + \end{center} +} +\endgroup" >> ${basedir}/tex_work/crt_channel_mapping_evds.tex +done + +echo "\end{document}" >> ${basedir}/tex_work/crt_channel_mapping_evds.tex + +pdflatex --shell-escape -output-directory ${basedir}/tex_work ${basedir}/tex_work/crt_channel_mapping_evds.tex +pdflatex --shell-escape -output-directory ${basedir}/tex_work ${basedir}/tex_work/crt_channel_mapping_evds.tex diff --git a/sbndcode/CRT/CRTEventDisplay/crteventdisplay_sbnd.fcl b/sbndcode/CRT/CRTEventDisplay/crteventdisplay_sbnd.fcl index 03f656d3b..a64ede384 100644 --- a/sbndcode/CRT/CRTEventDisplay/crteventdisplay_sbnd.fcl +++ b/sbndcode/CRT/CRTEventDisplay/crteventdisplay_sbnd.fcl @@ -4,14 +4,20 @@ BEGIN_PROLOG crteventdisplay_sbnd: { - EventDisplayConfig: @local::crteventdisplayalg_sbnd - module_type: "CRTEventDisplay" + EventDisplayConfig: @local::crteventdisplayalg_sbnd + module_type: "CRTEventDisplay" } crteventdisplay_sbnd_data: { - EventDisplayConfig: @local::crteventdisplayalg_sbnd_data - module_type: "CRTEventDisplay" + EventDisplayConfig: @local::crteventdisplayalg_sbnd_data + module_type: "CRTEventDisplay" +} + +crteventdisplay_sbnd_channel_mapping: +{ + EventDisplayConfig: @local::crteventdisplayalg_sbnd_channel_mapping + module_type: "CRTChannelMappingEventDisplay" } END_PROLOG diff --git a/sbndcode/CRT/CRTEventDisplay/crteventdisplayalg_sbnd.fcl b/sbndcode/CRT/CRTEventDisplay/crteventdisplayalg_sbnd.fcl index a2bc90e24..0f7a523c8 100644 --- a/sbndcode/CRT/CRTEventDisplay/crteventdisplayalg_sbnd.fcl +++ b/sbndcode/CRT/CRTEventDisplay/crteventdisplayalg_sbnd.fcl @@ -97,4 +97,28 @@ crteventdisplayalg_sbnd_data.UseTs0: true crteventdisplayalg_sbnd_data.MinTime: -1.5e6 crteventdisplayalg_sbnd_data.MaxTime: 1.5e6 +crteventdisplayalg_sbnd_channel_mapping: @local::crteventdisplayalg_sbnd + +crteventdisplayalg_sbnd_channel_mapping.MC: false +crteventdisplayalg_sbnd_channel_mapping.SaveRoot: false +crteventdisplayalg_sbnd_channel_mapping.SaveViews: true + +crteventdisplayalg_sbnd_channel_mapping.ChoseTaggers: true +crteventdisplayalg_sbnd_channel_mapping.HighlightModules: true + +crteventdisplayalg_sbnd_channel_mapping.DrawTaggers: true +crteventdisplayalg_sbnd_channel_mapping.DrawModules: true +crteventdisplayalg_sbnd_channel_mapping.DrawFEBs: true +crteventdisplayalg_sbnd_channel_mapping.DrawFEBEnds: true +crteventdisplayalg_sbnd_channel_mapping.DrawStrips: false +crteventdisplayalg_sbnd_channel_mapping.DrawTPC: true +crteventdisplayalg_sbnd_channel_mapping.DrawTrueTracks: false +crteventdisplayalg_sbnd_channel_mapping.DrawSimDeposits: false +crteventdisplayalg_sbnd_channel_mapping.DrawStripHits: false +crteventdisplayalg_sbnd_channel_mapping.DrawClusters: false +crteventdisplayalg_sbnd_channel_mapping.DrawSpacePoints: false +crteventdisplayalg_sbnd_channel_mapping.DrawTracks: false + +crteventdisplayalg_sbnd_channel_mapping.Print: false + END_PROLOG diff --git a/sbndcode/CRT/CRTEventDisplay/run_crteventdisplay_channel_mapping_all.fcl b/sbndcode/CRT/CRTEventDisplay/run_crteventdisplay_channel_mapping_all.fcl new file mode 100644 index 000000000..6da8eadc1 --- /dev/null +++ b/sbndcode/CRT/CRTEventDisplay/run_crteventdisplay_channel_mapping_all.fcl @@ -0,0 +1,53 @@ +#include "services_sbnd.fcl" +#include "particleinventoryservice.fcl" +#include "crteventdisplay_sbnd.fcl" +#include "crt_services_sbnd.fcl" + +process_name: CRTEventDisplay + +services: +{ + @table::sbnd_services + @table::crt_services_sbnd + ParticleInventoryService: @local::standard_particleinventoryservice + CRTChannelMapService: @local::crt_channel_map_no_inversion +} + +source: +{ + module_type: RootInput + maxEvents: -1 +} + +physics: +{ + analyzers: + { + crtevdbot: @local::crteventdisplay_sbnd_channel_mapping + crtevdsou: @local::crteventdisplay_sbnd_channel_mapping + crtevdnor: @local::crteventdisplay_sbnd_channel_mapping + crtevdwes: @local::crteventdisplay_sbnd_channel_mapping + crtevdeas: @local::crteventdisplay_sbnd_channel_mapping + crtevdtpl: @local::crteventdisplay_sbnd_channel_mapping + crtevdtph: @local::crteventdisplay_sbnd_channel_mapping + } + + ana: [ crtevdbot, crtevdsou, crtevdnor, crtevdwes, crtevdeas, crtevdtpl, crtevdtph ] + + end_paths: [ ana ] +} + +physics.analyzers.crtevdbot.SaveDir: "/YOUR/DIRECTORY/NAME/bottom_wall" +physics.analyzers.crtevdbot.EventDisplayConfig.ChosenTaggers: [ 0 ] +physics.analyzers.crtevdsou.SaveDir: "/YOUR/DIRECTORY/NAME/south_wall" +physics.analyzers.crtevdsou.EventDisplayConfig.ChosenTaggers: [ 1 ] +physics.analyzers.crtevdnor.SaveDir: "/YOUR/DIRECTORY/NAME/north_wall" +physics.analyzers.crtevdnor.EventDisplayConfig.ChosenTaggers: [ 2 ] +physics.analyzers.crtevdwes.SaveDir: "/YOUR/DIRECTORY/NAME/west_wall" +physics.analyzers.crtevdwes.EventDisplayConfig.ChosenTaggers: [ 3 ] +physics.analyzers.crtevdeas.SaveDir: "/YOUR/DIRECTORY/NAME/east_wall" +physics.analyzers.crtevdeas.EventDisplayConfig.ChosenTaggers: [ 4 ] +physics.analyzers.crtevdtpl.SaveDir: "/YOUR/DIRECTORY/NAME/toplow_wall" +physics.analyzers.crtevdtpl.EventDisplayConfig.ChosenTaggers: [ 5 ] +physics.analyzers.crtevdtph.SaveDir: "/YOUR/DIRECTORY/NAME/tophigh_wall" +physics.analyzers.crtevdtph.EventDisplayConfig.ChosenTaggers: [ 6 ] diff --git a/sbndcode/CRT/CRTReco/CMakeLists.txt b/sbndcode/CRT/CRTReco/CMakeLists.txt index 3ea86c28c..ad5cfc7cb 100644 --- a/sbndcode/CRT/CRTReco/CMakeLists.txt +++ b/sbndcode/CRT/CRTReco/CMakeLists.txt @@ -37,4 +37,10 @@ simple_plugin( Eigen3::Eigen ) +simple_plugin( + CRTBlobProducer module + lardata::Utilities + sbnobj::SBND_CRT +) + install_fhicl() diff --git a/sbndcode/CRT/CRTReco/CRTBlobProducer_module.cc b/sbndcode/CRT/CRTReco/CRTBlobProducer_module.cc new file mode 100644 index 000000000..0ea91e935 --- /dev/null +++ b/sbndcode/CRT/CRTReco/CRTBlobProducer_module.cc @@ -0,0 +1,219 @@ +//////////////////////////////////////////////////////////////////////// +// Class: CRTBlobProducer +// Plugin Type: producer +// File: CRTBlobProducer_module.cc +// +// Author: Henry Lay (h.lay@sheffield.ac.uk) +//////////////////////////////////////////////////////////////////////// + +#include "art/Framework/Core/EDProducer.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Principal/Run.h" +#include "art/Framework/Principal/SubRun.h" +#include "canvas/Utilities/InputTag.h" +#include "canvas/Persistency/Common/FindOneP.h" +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" + +#include "lardata/Utilities/AssociationUtil.h" + +#include "sbnobj/SBND/CRT/CRTCluster.hh" +#include "sbnobj/SBND/CRT/CRTSpacePoint.hh" +#include "sbnobj/SBND/CRT/CRTBlob.hh" + +#include + +namespace sbnd::crt { + class CRTBlobProducer; +} + +class sbnd::crt::CRTBlobProducer : public art::EDProducer { +public: + explicit CRTBlobProducer(fhicl::ParameterSet const& p); + + CRTBlobProducer(CRTBlobProducer const&) = delete; + CRTBlobProducer(CRTBlobProducer&&) = delete; + CRTBlobProducer& operator=(CRTBlobProducer const&) = delete; + CRTBlobProducer& operator=(CRTBlobProducer&&) = delete; + + void produce(art::Event& e) override; + + void OrderSpacePoints(std::vector> &spacePointVec, const art::FindOneP &spacePointsToCluster); + + std::vector>> CreateBlobCandidates(const std::vector> &spacePointVec, + const art::FindOneP &spacePointsToCluster); + + void TimeErrorCalculator(const std::vector ×, double &mean, double &err); + + void OrderBlobCandidates(std::vector>> &blobCandidates); + + std::vector>> ChoseBlobs(std::vector>> &blobCandidates); + +private: + + std::string fCRTSpacePointModuleLabel; + double fCoincidenceTimeRequirement; + bool fUseTs0; +}; + + +sbnd::crt::CRTBlobProducer::CRTBlobProducer(fhicl::ParameterSet const& p) + : EDProducer{p} + , fCRTSpacePointModuleLabel(p.get("CRTSpacePointModuleLabel")) + , fCoincidenceTimeRequirement(p.get("CoincidenceTimeRequirement")) + , fUseTs0(p.get("UseTs0")) +{ + produces>(); + produces>(); +} + +void sbnd::crt::CRTBlobProducer::produce(art::Event& e) +{ + auto blobVec = std::make_unique>(); + auto blobSpacePointAssn = std::make_unique>(); + + art::Handle> CRTSpacePointHandle; + e.getByLabel(fCRTSpacePointModuleLabel, CRTSpacePointHandle); + + std::vector> CRTSpacePointVec; + art::fill_ptr_vector(CRTSpacePointVec, CRTSpacePointHandle); + + art::FindOneP spacePointsToCluster(CRTSpacePointHandle, e, fCRTSpacePointModuleLabel); + + OrderSpacePoints(CRTSpacePointVec, spacePointsToCluster); + + std::vector>> blobCandidates = CreateBlobCandidates(CRTSpacePointVec, spacePointsToCluster); + + OrderBlobCandidates(blobCandidates); + + std::vector>> chosenBlobs = ChoseBlobs(blobCandidates); + + for(auto const& [blob, spIDs] : chosenBlobs) + { + blobVec->push_back(blob); + + for(auto const& spID : spIDs) + util::CreateAssn(*this, e, *blobVec, CRTSpacePointVec[spID], *blobSpacePointAssn); + } + + e.put(std::move(blobVec)); + e.put(std::move(blobSpacePointAssn)); +} + +void sbnd::crt::CRTBlobProducer::OrderSpacePoints(std::vector> &spacePointVec, const art::FindOneP &spacePointsToCluster) +{ + std::sort(spacePointVec.begin(), spacePointVec.end(), + [&](const art::Ptr &a, const art::Ptr &b) -> bool { + if(fUseTs0) + return a->Ts0() < b->Ts0(); + else + return a->Ts1() < b->Ts1(); + }); +} + +std::vector>> sbnd::crt::CRTBlobProducer::CreateBlobCandidates(const std::vector> &spacePointVec, + const art::FindOneP &spacePointsToCluster) +{ + std::vector>> candidates; + + for(unsigned i = 0; i < spacePointVec.size(); ++i) + { + const art::Ptr primarySpacePoint = spacePointVec[i]; + const art::Ptr primaryCluster = spacePointsToCluster.at(primarySpacePoint.key()); + + std::set used_spacepoints = { i }; + std::vector t0s = { primarySpacePoint->Ts0() }; + std::vector t1s = { primarySpacePoint->Ts1() }; + std::vector pes = { primarySpacePoint->PE() }; + + std::map taggers; + for(int i = 0; i < 7; ++i) + taggers[(CRTTagger)i] = 0; + ++taggers[primaryCluster->Tagger()]; + + for(unsigned ii = i+1; ii < spacePointVec.size(); ++ii) + { + const art::Ptr secondarySpacePoint = spacePointVec[ii]; + const art::Ptr secondaryCluster = spacePointsToCluster.at(secondarySpacePoint.key()); + + const double tdiff_prim_sec = fUseTs0 ? secondarySpacePoint->Ts0() - primarySpacePoint->Ts0() : secondarySpacePoint->Ts1() - primarySpacePoint->Ts1(); + + if(tdiff_prim_sec > fCoincidenceTimeRequirement) + break; + + used_spacepoints.insert(ii); + t0s.push_back(secondarySpacePoint->Ts0()); + t1s.push_back(secondarySpacePoint->Ts1()); + pes.push_back(secondarySpacePoint->PE()); + ++taggers[secondaryCluster->Tagger()]; + } + + double t0, et0; + TimeErrorCalculator(t0s, t0, et0); + + double t1, et1; + TimeErrorCalculator(t1s, t1, et1); + + const double pe = std::accumulate(pes.begin(), pes.end(), 0.); + + const CRTBlob blob(t0, et0, t1, et1, pe, taggers); + + candidates.emplace_back(blob, used_spacepoints); + } + + return candidates; +} + +void sbnd::crt::CRTBlobProducer::TimeErrorCalculator(const std::vector ×, double &mean, double &err) +{ + double sum = 0.; + for(auto const &time : times) + sum += time; + + mean = sum / times.size(); + + double summed_var = 0.; + for(auto const &time : times) + summed_var += std::pow((time - mean), 2); + + err = std::sqrt(summed_var / (times.size() - 1)); +} + +void sbnd::crt::CRTBlobProducer::OrderBlobCandidates(std::vector>> &blobCandidates) +{ + std::sort(blobCandidates.begin(), blobCandidates.end(), + [&](const std::pair> &a, const std::pair> &b) -> bool { + return fUseTs0 ? a.first.Ts0Err() < b.first.Ts0Err() : a.first.Ts1Err() < b.first.Ts1Err(); + }); +} + +std::vector>> sbnd::crt::CRTBlobProducer::ChoseBlobs(std::vector>> &blobCandidates) +{ + std::vector>> chosenBlobs; + + std::set used; + + for(auto const& [blob, spIDs] : blobCandidates) + { + bool keep = true; + for(auto const& spID : spIDs) + { + if(used.count(spID) != 0) + keep = false; + } + + if(keep) + { + chosenBlobs.emplace_back(blob, spIDs); + + for(auto const& spID : spIDs) + used.insert(spID); + } + } + + return chosenBlobs; +} + +DEFINE_ART_MODULE(sbnd::crt::CRTBlobProducer) diff --git a/sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.cc b/sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.cc index 1b7e296cd..49c6dbb4c 100644 --- a/sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.cc +++ b/sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.cc @@ -198,14 +198,8 @@ namespace sbnd::crt { void CRTClusterCharacterisationAlg::CorrectTime(const art::Ptr &hit0, const art::Ptr &hit1, const geo::Point_t &pos, double &t0, double &et0, double &t1, double &et1) { - const double dist0 = fCRTGeoService->DistanceDownStrip(pos, hit0->Channel()); - const double dist1 = fCRTGeoService->DistanceDownStrip(pos, hit1->Channel()); - - const double pe0 = ReconstructPE(hit0, dist0); - const double pe1 = ReconstructPE(hit1, dist1); - - const double corr0 = TimingCorrectionOffset(dist0, pe0); - const double corr1 = TimingCorrectionOffset(dist1, pe1); + const double corr0 = TimingCorrectionOffset(hit0, pos); + const double corr1 = TimingCorrectionOffset(hit1, pos); t0 = (hit0->Ts0() - corr0 + hit1->Ts0() - corr1) / 2.; et0 = std::abs((hit0->Ts0() - corr0) - (hit1->Ts0() - corr1)) / 2.; @@ -214,14 +208,27 @@ namespace sbnd::crt { et1 = std::abs((hit0->Ts1() - corr0) - (hit1->Ts1() - corr1)) / 2.; } - double CRTClusterCharacterisationAlg::TimingCorrectionOffset(const double &dist, const double &pe) + double CRTClusterCharacterisationAlg::TimingCorrectionOffset(const art::Ptr &hit, const geo::Point_t &pos) { - - double t_TimeWalk = fTimeWalkNorm * std::exp(- fTimeWalkScale * pe); - double t_PropDelay = fPropDelay * dist; + double t_TimeWalk = TimeWalk(hit, pos); + double t_PropDelay = PropagationDelay(hit, pos); return t_PropDelay + t_TimeWalk; } + double CRTClusterCharacterisationAlg::TimeWalk(const art::Ptr &hit, const geo::Point_t &pos) + { + const double dist = fCRTGeoService->DistanceDownStrip(pos, hit->Channel()); + const double pe = ReconstructPE(hit, dist); + + return fTimeWalkNorm * std::exp(-fTimeWalkScale * pe); + } + + double CRTClusterCharacterisationAlg::PropagationDelay(const art::Ptr &hit, const geo::Point_t &pos) + { + const double dist = fCRTGeoService->DistanceDownStrip(pos, hit->Channel()); + return fPropDelay * dist; + } + void CRTClusterCharacterisationAlg::AggregatePositions(const std::vector &complete_spacepoints, geo::Point_t &pos, geo::Point_t &err) { double sum_x = 0., sum_y = 0., sum_z = 0.; diff --git a/sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.h b/sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.h index a622ef28d..97b3133f7 100644 --- a/sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.h +++ b/sbndcode/CRT/CRTReco/CRTClusterCharacterisationAlg.h @@ -1,10 +1,9 @@ -#ifndef CRTBACKTRACKERALG_H_SEEN -#define CRTBACKTRACKERALG_H_SEEN +#ifndef CRTCLUSTERCHARACTERISATIONALG_H_SEEN +#define CRTCLUSTERCHARACTERISATIONALG_H_SEEN /////////////////////////////////////////////// // CRTClusterCharacterisationAlg.h // -// Truth Matching Utilities for CRT analysis // Henry Lay (h.lay@lancaster.ac.uk) // November 2022 /////////////////////////////////////////////// @@ -65,7 +64,11 @@ namespace sbnd::crt { void CorrectTime(const art::Ptr &hit0, const art::Ptr &hit1, const geo::Point_t &pos, double &t0, double &et0, double &t1, double &et1); - double TimingCorrectionOffset(const double &dist, const double &pe); + double TimingCorrectionOffset(const art::Ptr &hit, const geo::Point_t &pos); + + double TimeWalk(const art::Ptr &hit, const geo::Point_t &pos); + + double PropagationDelay(const art::Ptr &hit, const geo::Point_t &pos); void AggregatePositions(const std::vector &complete_spacepoints, geo::Point_t &pos, geo::Point_t &err); diff --git a/sbndcode/CRT/CRTReco/CRTStripHitProducer_module.cc b/sbndcode/CRT/CRTReco/CRTStripHitProducer_module.cc index 1f212abd1..7a4891dab 100644 --- a/sbndcode/CRT/CRTReco/CRTStripHitProducer_module.cc +++ b/sbndcode/CRT/CRTReco/CRTStripHitProducer_module.cc @@ -62,6 +62,7 @@ class sbnd::crt::CRTStripHitProducer : public art::EDProducer { std::string fFEBDataModuleLabel; uint16_t fADCThreshold; + uint16_t fADCSaturation; std::vector fErrorCoeff; bool fAllowFlag1; bool fApplyTs0Window; @@ -88,6 +89,7 @@ sbnd::crt::CRTStripHitProducer::CRTStripHitProducer(fhicl::ParameterSet const& p : EDProducer{p} , fFEBDataModuleLabel(p.get("FEBDataModuleLabel")) , fADCThreshold(p.get("ADCThreshold")) + , fADCSaturation(p.get("ADCSaturation")) , fErrorCoeff(p.get>("ErrorCoeff")) , fAllowFlag1(p.get("AllowFlag1")) , fApplyTs0Window(p.get("ApplyTs0Window")) @@ -273,6 +275,10 @@ std::vector sbnd::crt::CRTStripHitProducer::CreateStripH const uint16_t adc1 = sipm1.pedestal < sipm_adcs[adc_i] ? sipm_adcs[adc_i] - sipm1.pedestal : 0; const uint16_t adc2 = sipm2.pedestal < sipm_adcs[adc_i+1] ? sipm_adcs[adc_i+1] - sipm2.pedestal : 0; + // Saturated? + const bool sat1 = sipm_adcs[adc_i] >= fADCSaturation; + const bool sat2 = sipm_adcs[adc_i+1] >= fADCSaturation; + // Keep hit if both SiPMs above threshold if(adc1 > fADCThreshold && adc2 > fADCThreshold) { @@ -290,7 +296,7 @@ std::vector sbnd::crt::CRTStripHitProducer::CreateStripH if(pos - err < 0) err = pos; - stripHits.emplace_back(offline_channel_id, t0, t1, ref_time_s, pos, err, adc1, adc2); + stripHits.emplace_back(offline_channel_id, t0, t1, ref_time_s, pos, err, adc1, adc2, sat1, sat2); } } diff --git a/sbndcode/CRT/CRTReco/crtrecoproducers_sbnd.fcl b/sbndcode/CRT/CRTReco/crtrecoproducers_sbnd.fcl index 9aa8e2a26..db328eb54 100644 --- a/sbndcode/CRT/CRTReco/crtrecoproducers_sbnd.fcl +++ b/sbndcode/CRT/CRTReco/crtrecoproducers_sbnd.fcl @@ -6,6 +6,7 @@ crtstriphitproducer_sbnd: { FEBDataModuleLabel: "crtsim" ADCThreshold: 60 + ADCSaturation: @local::sbnd_crtsim.DetSimParams.AdcSaturation ErrorCoeff: [ 0.26, -0.27, 0.025 ] AllowFlag1: false ApplyTs0Window: false @@ -54,6 +55,9 @@ crtclustercharacterisationalg_sbnd: TimeWalkScale: @local::sbnd_crtsim.DetSimParams.TDelayScale } +crtclustercharacterisationalg_data_sbnd: @local::crtclustercharacterisationalg_sbnd +crtclustercharacterisationalg_data_sbnd.TimeOffset: 0 + crtspacepointproducer_sbnd: { ClusterCharacterisationAlg: @local::crtclustercharacterisationalg_sbnd @@ -62,7 +66,7 @@ crtspacepointproducer_sbnd: } crtspacepointproducer_data_sbnd: @local::crtspacepointproducer_sbnd -crtspacepointproducer_data_sbnd.ClusterCharacterisationAlg.TimeOffset: 0 +crtspacepointproducer_data_sbnd.ClusterCharacterisationAlg: @local::crtclustercharacterisationalg_data_sbnd crttrackproducer_sbnd: { @@ -78,4 +82,15 @@ crttrackproducer_data_sbnd: @local::crttrackproducer_sbnd crttrackproducer_data_sbnd.UseTs0: true crttrackproducer_data_sbnd.MaskedTaggers: [ ] +crtblobproducer_sbnd: +{ + CRTSpacePointModuleLabel: "crtspacepoints" + CoincidenceTimeRequirement: 100. + UseTs0: false + module_type: "CRTBlobProducer" +} + +crtblobproducer_data_sbnd: @local::crtblobproducer_sbnd +crtblobproducer_data_sbnd.UseTs0: true + END_PROLOG diff --git a/sbndcode/CRT/CRTReco/run_crtblobreco.fcl b/sbndcode/CRT/CRTReco/run_crtblobreco.fcl new file mode 100644 index 000000000..7d46331d2 --- /dev/null +++ b/sbndcode/CRT/CRTReco/run_crtblobreco.fcl @@ -0,0 +1,38 @@ +#include "services_sbnd.fcl" +#include "rootoutput_sbnd.fcl" +#include "crtrecoproducers_sbnd.fcl" + +process_name: CRTBlobReco + +services: +{ + @table::sbnd_services +} + +source: +{ + module_type: RootInput +} + +outputs: +{ + out1: + { + @table::sbnd_rootoutput + dataTier: "reconstructed" + } +} + +physics: +{ + producers: + { + crtblobs: @local::crtblobproducer_sbnd + } + + reco: [ crtblobs ] + stream1: [ out1 ] + + trigger_paths: [ reco ] + end_paths: [ stream1 ] +} diff --git a/sbndcode/CRT/CRTReco/run_crtreco.fcl b/sbndcode/CRT/CRTReco/run_crtreco.fcl index 3a625c1b7..585238a0a 100644 --- a/sbndcode/CRT/CRTReco/run_crtreco.fcl +++ b/sbndcode/CRT/CRTReco/run_crtreco.fcl @@ -33,9 +33,10 @@ physics: crtclustering: @local::crtclusterproducer_sbnd crtspacepoints: @local::crtspacepointproducer_sbnd crttracks: @local::crttrackproducer_sbnd + crtblobs: @local::crtblobproducer_sbnd } - reco: [ crtstrips, crtclustering, crtspacepoints, crttracks ] + reco: [ crtstrips, crtclustering, crtspacepoints, crttracks, crtblobs ] stream1: [ out1 ] trigger_paths: [ reco ] diff --git a/sbndcode/CRT/CRTReco/run_crtreco_data.fcl b/sbndcode/CRT/CRTReco/run_crtreco_data.fcl index 2493c9b33..9e179b108 100644 --- a/sbndcode/CRT/CRTReco/run_crtreco_data.fcl +++ b/sbndcode/CRT/CRTReco/run_crtreco_data.fcl @@ -10,6 +10,7 @@ physics.producers.crtstrips: @local::crtstriphitproducer_data_sbnd physics.producers.crtclustering: @local::crtclusterproducer_data_sbnd physics.producers.crtspacepoints: @local::crtspacepointproducer_data_sbnd physics.producers.crttracks: @local::crttrackproducer_data_sbnd +physics.producers.crtblobs: @local::crtblobproducer_data_sbnd outputs.out1.outputCommands: [ "keep *_*_*_*", "drop *_daq_*_*", diff --git a/sbndcode/CRT/CRTReco/run_crtrecoana.fcl b/sbndcode/CRT/CRTReco/run_crtrecoana.fcl index 983502ca8..15a37b22d 100644 --- a/sbndcode/CRT/CRTReco/run_crtrecoana.fcl +++ b/sbndcode/CRT/CRTReco/run_crtrecoana.fcl @@ -37,6 +37,7 @@ physics: crtclustering: @local::crtclusterproducer_sbnd crtspacepoints: @local::crtspacepointproducer_sbnd crttracks: @local::crttrackproducer_sbnd + crtblobs: @local::crtblobproducer_sbnd } analyzers: @@ -44,10 +45,13 @@ physics: crtana: @local::crtana_sbnd } - reco: [ crtstrips, crtclustering, crtspacepoints, crttracks ] + reco: [ crtstrips, crtclustering, crtspacepoints, crttracks, crtblobs ] ana: [ crtana ] stream1: [ out1 ] trigger_paths: [ reco ] end_paths: [ ana, stream1 ] } + +physics.analyzers.crtana.HasBlobs: true +physics.analyzers.crtana.NoTPC: true diff --git a/sbndcode/CRT/CRTReco/run_crtrecoana_data.fcl b/sbndcode/CRT/CRTReco/run_crtrecoana_data.fcl index 11bbc53cf..c249a7d99 100644 --- a/sbndcode/CRT/CRTReco/run_crtrecoana_data.fcl +++ b/sbndcode/CRT/CRTReco/run_crtrecoana_data.fcl @@ -10,5 +10,8 @@ physics.producers.crtstrips: @local::crtstriphitproducer_data_sbnd physics.producers.crtclustering: @local::crtclusterproducer_data_sbnd physics.producers.crtspacepoints: @local::crtspacepointproducer_data_sbnd physics.producers.crttracks: @local::crttrackproducer_data_sbnd +physics.producers.crtblobs: @local::crtblobproducer_data_sbnd physics.analyzers.crtana: @local::crtana_data_sbnd +physics.analyzers.crtana.HasBlobs: true +physics.analyzers.crtana.NoTPC: true diff --git a/sbndcode/CRT/CRTReco/run_crtrecoana_notpc.fcl b/sbndcode/CRT/CRTReco/run_crtrecoana_notpc.fcl deleted file mode 100644 index 621b8d45a..000000000 --- a/sbndcode/CRT/CRTReco/run_crtrecoana_notpc.fcl +++ /dev/null @@ -1,3 +0,0 @@ -#include "run_crtrecoana.fcl" - -physics.analyzers.crtana.NoTPC: true diff --git a/sbndcode/CRT/CRTReco/run_crtrecoana_notpc_no_flat_tracks.fcl b/sbndcode/CRT/CRTReco/run_crtrecoana_notpc_no_flat_tracks.fcl deleted file mode 100644 index 74a8504d7..000000000 --- a/sbndcode/CRT/CRTReco/run_crtrecoana_notpc_no_flat_tracks.fcl +++ /dev/null @@ -1,3 +0,0 @@ -#include "run_crtrecoana_notpc.fcl" - -physics.producers.crttracks.MaskedTaggers: [ 0 ] diff --git a/sbndcode/CRT/CRTSimulation/crtsimmodules_sbnd.fcl b/sbndcode/CRT/CRTSimulation/crtsimmodules_sbnd.fcl index 2d9acb68e..ca546268b 100644 --- a/sbndcode/CRT/CRTSimulation/crtsimmodules_sbnd.fcl +++ b/sbndcode/CRT/CRTSimulation/crtsimmodules_sbnd.fcl @@ -58,7 +58,7 @@ standard_sbnd_crtsimparams: { # Minimum time between energy deposits that SiPMs can resolve [ns] SipmTimeResponse: 2.0 - AdcSaturation: 4095 + AdcSaturation: 4089 DeadTime: 22000 diff --git a/sbndcode/Calorimetry/CMakeLists.txt b/sbndcode/Calorimetry/CMakeLists.txt new file mode 100644 index 000000000..28452cab4 --- /dev/null +++ b/sbndcode/Calorimetry/CMakeLists.txt @@ -0,0 +1,28 @@ +set (MODULE_LIBRARIES + larsim::PhotonPropagation + larsim::OpticalPath + larcorealg::Geometry + larcore::Geometry_Geometry_service + larevt::CalibrationDBI_Providers + wda::wda + lardata::Utilities + lardataobj::RecoBase + art::Framework_Core + art::Framework_Principal + art::Framework_Services_Registry + art::Utilities canvas::canvas + art_root_io::TFileService_service + art_root_io::tfile_support + messagefacility::MF_MessageLogger + sbnobj::Common_Reco + fhiclcpp::fhiclcpp + sbndcode_OpDetSim + ROOT::Core + ROOT::Tree +) + +cet_build_plugin(LightCaloProducer art::module SOURCE LightCaloProducer_module.cc LIBRARIES ${MODULE_LIBRARIES}) + +install_headers() +install_fhicl() +install_source() diff --git a/sbndcode/Calorimetry/LightCaloProducer_module.cc b/sbndcode/Calorimetry/LightCaloProducer_module.cc new file mode 100644 index 000000000..a4a1040a4 --- /dev/null +++ b/sbndcode/Calorimetry/LightCaloProducer_module.cc @@ -0,0 +1,850 @@ +//////////////////////////////////////////////////////////////////////// +// Class: LightCaloProducer +// Plugin Type: producer (Unknown Unknown) +// File: LightCaloProducer_module.cc +// +// Generated at Wed Apr 19 16:49:20 2023 by Lynn Tung using cetskelgen +// from version . +//////////////////////////////////////////////////////////////////////// + +#include "art/Framework/Core/EDProducer.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "art/Framework/Principal/Run.h" +#include "art/Framework/Principal/SubRun.h" +#include "art/Utilities/make_tool.h" +#include "canvas/Utilities/InputTag.h" +#include "fhiclcpp/ParameterSet.h" +#include "messagefacility/MessageLogger/MessageLogger.h" + +// Additional framework includes +#include "art_root_io/TFileService.h" +#include "canvas/Persistency/Common/FindMany.h" +#include "canvas/Persistency/Common/FindManyP.h" +#include "canvas/Persistency/Common/FindOne.h" +#include "canvas/Persistency/Common/FindOneP.h" +#include "canvas/Persistency/Common/Ptr.h" +#include "canvas/Persistency/Common/PtrVector.h" + +// LArSoft includes +#include "lardata/Utilities/AssociationUtil.h" +#include "lardataobj/RecoBase/Slice.h" +#include "lardataobj/RecoBase/PFParticle.h" +#include "lardataobj/RecoBase/SpacePoint.h" +#include "lardataobj/RecoBase/Hit.h" +#include "lardataobj/RecoBase/OpFlash.h" + +#include "larcore/CoreUtils/ServiceUtil.h" +#include "larcore/Geometry/Geometry.h" +#include "larcorealg/Geometry/GeometryCore.h" +#include "larsim/PhotonPropagation/SemiAnalyticalModel.h" +#include "larsim/PhotonPropagation/OpticalPathTools/OpticalPath.h" +#include "larsim/Simulation/LArG4Parameters.h" +#include "lardata/DetectorInfoServices/DetectorClocksService.h" +#include "lardata/DetectorInfoServices/DetectorPropertiesService.h" + +// Calibration database includes for electron lifetime +#include "larevt/CalibrationDBI/Providers/DBFolder.h" +#include "wda.h" + +// SBND includes +#include "sbndcode/OpDetSim/sbndPDMapAlg.hh" +#include "sbnobj/Common/Reco/LightCalo.h" +#include "sbnobj/Common/Reco/OpT0FinderResult.h" +#include "sbnobj/Common/Reco/TPCPMTBarycenterMatch.h" + +// Calibration database includes +#include "sbndcode/Calibration/PDSDatabaseInterface/PMTCalibrationDatabase.h" +#include "sbndcode/Calibration/PDSDatabaseInterface/IPMTCalibrationDatabaseService.h" + +// ROOT includes +#include "TFile.h" +#include "TTree.h" + +// C++ includes +#include +#include // sort +#include +#include +#include + +namespace sbnd { + class LightCaloProducer; +} + + +class sbnd::LightCaloProducer : public art::EDProducer { +public: + explicit LightCaloProducer(fhicl::ParameterSet const& p); + // The compiler-generated destructor is fine for non-base + // classes without bare pointers or other resource use. + + // Plugins should not be copied or assigned. + LightCaloProducer(LightCaloProducer const&) = delete; + LightCaloProducer(LightCaloProducer&&) = delete; + LightCaloProducer& operator=(LightCaloProducer const&) = delete; + LightCaloProducer& operator=(LightCaloProducer&&) = delete; + + // Required functions. + void produce(art::Event& e) override; + +private: + + void CalculateCalorimetry(art::Event& e, + std::unique_ptr< std::vector >& lightcalo_v, + std::unique_ptr< art::Assns >& slice_assn_v, + std::unique_ptr< art::Assns >& flash_assn_v); + + // Templated member function to collect matched slices and opflashes + // The CheckFunc is a callable that takes a Ptr and returns bool + // to determine if the match passes selection criteria + template + void CollectMatches(const art::Handle> &handle, + const std::vector> &fm_v, + const std::string &label, + art::Event &e, + std::vector> &match_slices_v, + std::vector> &match_op0, + std::vector> &match_op1, + CheckFunc check); + + // Finds the opflash in flash_v that is closest in time to ref_time + art::Ptr FindMatchingFlash(const std::vector> &flash_v, + double ref_time); + // Returns visibility vector for all opdets given charge/position information + std::vector> CalcVisibility(std::vector xyz_v, + std::vector charge_v); + + // Fills reconstructed photon count vector (total_gamma_v) for all opdets given charge/position information + void CalcLight(std::vector flash_pe_v, + std::vector dir_visibility, + std::vector ref_visibility, + std::vector &total_gamma_v); + + // Returns the weighted mean of the light vector + double CalcMean(std::vector total_light, std::vector total_err); + + // Struct to hold electron lifetime data from database + struct ELifetimeInfo { + double tau_tpc0; + double tau_tpc1; + }; + + // Helper to get electron lifetime from database + ELifetimeInfo GetELifetimeFromDB(uint64_t run); + + // fcl parameters + std::vector fopflash_producer_v; + std::vector fopflash_ara_producer_v; + std::vector fpd_types; + std::string fslice_producer; + std::string fopt0_producer; + std::string fbcfm_producer; + + bool fuse_bcfm; + bool fuse_opt0; + bool fverbose; + bool ffill_tree; + + float fbcfmscore_cut; + float fopt0score_cut; + + float fopflash_min; + float fopflash_max; + float fopt0_frac_diff_cut; + + float fflash_offset; + std::vector fnoise_thresh; + std::vector fupper_thresh; + + std::vector fcal_area_const; + + double fpmtcoated_vuveff_tpc0; + double fpmtcoated_viseff_tpc0; + double fpmtuncoated_eff_tpc0; + + double fpmtcoated_vuveff_tpc1; + double fpmtcoated_viseff_tpc1; + double fpmtuncoated_eff_tpc1; + + double fxarapucavuv_vuveff; + double fxarapucavuv_viseff; + double fxarapucavis_eff; + + bool fuse_elifetime_db; + std::string felifetime_db_file; + std::string felifetime_db_tag; + std::unique_ptr felifetime_db; + std::map felifetime_cache; + + std::vector fopdet_vuv_eff; + std::vector fopdet_vis_eff; + std::vector fopdet_mask; + + std::unique_ptr fsemi_model; + fhicl::ParameterSet fvuv_params; + fhicl::ParameterSet fvis_params; + std::shared_ptr foptical_path_tool; + + sbndDB::PMTCalibrationDatabase const* fpmt_calib_db; + + opdet::sbndPDMapAlg opdetmap; //map for photon detector types + unsigned int nchan = opdetmap.size(); + + geo::GeometryCore const* geom; + + TTree* _tree; + int _run, _subrun, _event; + int _pfpid; // ID of the matched slice + double _opflash_time; // time of matched opflash + + std::vector _dep_pe; // vector of measured photo-electron (PE), one entry = one channel + std::vector _rec_gamma; // vector of reconstructed photon count, one entry = one channel + std::vector _visibility; + + double _slice_L; // reconstructed photon count + double _slice_Q; // reconstructed electron count + double _slice_E; // reconstructed deposited energy +}; + + +sbnd::LightCaloProducer::LightCaloProducer(fhicl::ParameterSet const& p) + : EDProducer{p} // , + // More initializers here. +{ + fvuv_params = p.get("VUVHits"); + fvis_params = p.get("VIVHits"); + foptical_path_tool = std::shared_ptr(art::make_tool(p.get("OpticalPathTool"))); + fsemi_model = std::make_unique(fvuv_params, fvis_params, foptical_path_tool, true, false); + fpmt_calib_db = lar::providerFrom(); + + fopflash_producer_v = p.get>("OpFlashProducers"); + fopflash_ara_producer_v = p.get>("OpFlashAraProducers"); + fpd_types = p.get>("PDTypes"); + fslice_producer = p.get("SliceProducer"); + fopt0_producer = p.get("OpT0FinderProducer"); + fbcfm_producer = p.get("BCFMProducer"); + fuse_opt0 = p.get("UseOpT0Finder"); + fuse_bcfm = p.get("UseBCFM"); + + fverbose = p.get("Verbose"); + ffill_tree = p.get("FillTree"); + + fbcfmscore_cut = p.get("bcfmScoreCut"); + fopt0score_cut = p.get("opt0ScoreCut"); + fopt0_frac_diff_cut = p.get("opt0FractionalCut"); + + fopflash_min = p.get("OpFlashMin"); + fopflash_max = p.get("OpFlashMax"); + + fflash_offset = p.get("FlashOffset"); + fnoise_thresh = p.get>("FlashNoiseThreshold"); + fupper_thresh = p.get>("OpDetMaxPEThreshold"); + + fcal_area_const = p.get>("CalAreaConstants"); + fopdet_mask = p.get>("OpDetMask"); + + fpmtcoated_viseff_tpc0 = p.get("PMTCoatedVISEff_tpc0"); + fpmtcoated_vuveff_tpc0 = p.get("PMTCoatedVUVEff_tpc0"); + fpmtuncoated_eff_tpc0 = p.get("PMTUncoatedEff_tpc0"); + fpmtcoated_viseff_tpc1 = p.get("PMTCoatedVISEff_tpc1"); + fpmtcoated_vuveff_tpc1 = p.get("PMTCoatedVUVEff_tpc1"); + fpmtuncoated_eff_tpc1 = p.get("PMTUncoatedEff_tpc1"); + + fxarapucavuv_vuveff = p.get("XArapucaVUVVUVEff"); + fxarapucavuv_viseff = p.get("XArapucaVUVVISEff"); + fxarapucavis_eff = p.get("XArapucaVISEff"); + + // Electron lifetime database configuration + fuse_elifetime_db = p.get("UseELifetimeDB", false); + if (fuse_elifetime_db) { + felifetime_db_file = p.get("ELifetimeDBFile"); + felifetime_db_tag = p.get("ELifetimeDBTag"); + felifetime_db = std::make_unique(felifetime_db_file, "", "", felifetime_db_tag, true, false); + } + + // fill efficiency vectors + fopdet_vuv_eff.resize(nchan,0.); + fopdet_vis_eff.resize(nchan,0.); + for (unsigned int ch = 0; ch < nchan; ++ch) { + std::string pd_type = opdetmap.pdType(ch); + if (pd_type == "pmt_coated") { + if (ch%2==0){ + fopdet_vuv_eff[ch] = fpmtcoated_vuveff_tpc0; + fopdet_vis_eff[ch] = fpmtcoated_viseff_tpc0; + } + else{ + fopdet_vuv_eff[ch] = fpmtcoated_vuveff_tpc1; + fopdet_vis_eff[ch] = fpmtcoated_viseff_tpc1; + } + } else if (pd_type == "pmt_uncoated") { + if (ch%2==0) + fopdet_vis_eff[ch] = fpmtuncoated_eff_tpc0; + else + fopdet_vis_eff[ch] = fpmtuncoated_eff_tpc1; + } + else if (pd_type == "xarapuca_vuv") { + fopdet_vuv_eff[ch] = fxarapucavuv_vuveff; + fopdet_vis_eff[ch] = fxarapucavuv_viseff; + } + else if (pd_type == "xarapuca_vis") { + fopdet_vis_eff[ch] = fxarapucavis_eff; + } + } + + geom = lar::providerFrom(); + + art::ServiceHandle fs; + if (ffill_tree){ + _tree = fs->make("lightcalo",""); + _tree->Branch("run", &_run, "run/I"); + _tree->Branch("subrun", &_subrun, "subrun/I"); + _tree->Branch("event", &_event, "event/I"); + _tree->Branch("pfpid", &_pfpid, "pfpid/I"); + _tree->Branch("opflash_time", &_opflash_time, "opflash_time/D"); + + _tree->Branch("rec_gamma", "std::vector", &_rec_gamma); + _tree->Branch("dep_pe", "std::vector", &_dep_pe); + _tree->Branch("visibility", "std::vector", &_visibility); + + _tree->Branch("slice_Q", &_slice_Q, "slice_Q/D"); + _tree->Branch("slice_L", &_slice_L, "slice_L/D"); + _tree->Branch("slice_E", &_slice_E, "slice_E/D"); + } + // Call appropriate produces<>() functions here. + produces>(); + produces>(); + produces>(); + + // Call appropriate consumes<>() for any products to be retrieved by this module. +} + +void sbnd::LightCaloProducer::produce(art::Event& e) +{ + + std::unique_ptr> lightcalo_v (new std::vector); + std::unique_ptr< art::Assns> slice_assn_v (new art::Assns); + std::unique_ptr< art::Assns> flash_assn_v (new art::Assns); + + CalculateCalorimetry(e, lightcalo_v, slice_assn_v, flash_assn_v); + + e.put(std::move(lightcalo_v)); + e.put(std::move(slice_assn_v)); + e.put(std::move(flash_assn_v)); +} + +void sbnd::LightCaloProducer::CalculateCalorimetry(art::Event& e, + std::unique_ptr>& lightcalo_v, + std::unique_ptr< art::Assns>& slice_assn_v, + std::unique_ptr< art::Assns>& flash_assn_v) +{ + // services + auto const clock_data = art::ServiceHandle()->DataFor(e); + auto const det_prop = art::ServiceHandle()->DataFor(e, clock_data); + + art::ServiceHandle g4param; + + _run = e.id().run(); + _subrun = e.id().subRun(); + _event = e.id().event(); + + // get slices + ::art::Handle> slice_h; + e.getByLabel(fslice_producer, slice_h); + if(!slice_h.isValid() || slice_h->empty()){ + std::cout << "[LightCaloProducer] : " << fslice_producer << " doesn't have good slices!" << std::endl; + return; + } + + ::art::Handle> pfp_h; + e.getByLabel(fslice_producer, pfp_h); + if(!pfp_h.isValid() || pfp_h->empty()) { + std::cout << "[LightCaloProducer] : " << fslice_producer << " doesn't have good PFParticles!" << std::endl; + return; + } + + ::art::Handle> spacepoint_h; + e.getByLabel(fslice_producer, spacepoint_h); + if(!spacepoint_h.isValid() || spacepoint_h->empty()) { + std::cout << "[LightCaloProducer] : " << fslice_producer << " don't have good SpacePoints!" << std::endl; + return; + } + + std::vector> flash0_v; + std::vector> flash1_v; + + for (size_t i=0; i> flash_h; + e.getByLabel(fopflash_producer_v[i], flash_h); + if (!flash_h.isValid() || flash_h->empty()) { + std::cout << "[LightCaloProducer] : " << "don't have good PMT flashes from producer " << fopflash_producer_v[i] << std::endl; + } + else{ + if (fopflash_producer_v[i].find("tpc0") != std::string::npos) + art::fill_ptr_vector(flash0_v, flash_h); + else if (fopflash_producer_v[i].find("tpc1") != std::string::npos) + art::fill_ptr_vector(flash1_v, flash_h); + } + } + + art::FindManyP slice_to_pfp (slice_h, e, fslice_producer); + art::FindManyP slice_to_hit (slice_h, e, fslice_producer); + art::FindManyP pfp_to_spacepoint(pfp_h, e, fslice_producer); + art::FindManyP spacepoint_to_hit(spacepoint_h, e, fslice_producer); + + std::vector> match_slices_v; + std::vector> match_op0; + std::vector> match_op1; + + std::map, std::vector>> match_slice_opflash_map; + + // use templated member helper to fill match vectors + if (fuse_bcfm) { + ::art::Handle> bcfm_h; + e.getByLabel(fbcfm_producer, bcfm_h); + if(!bcfm_h.isValid() || bcfm_h->empty()) { + std::cout << "[LightCaloProducer] : " << "don't have good barycenter matches!" << std::endl; + return; + } + std::vector> bcfm_v; + art::fill_ptr_vector(bcfm_v, bcfm_h); + + CollectMatches(bcfm_h, bcfm_v, fbcfm_producer, e, match_slices_v, match_op0, match_op1, + [this](art::Ptr bcfm) { + if (bcfm->flashTime > fopflash_max || bcfm->flashTime < fopflash_min) return false; + if (bcfm->score < fbcfmscore_cut) return false; + return true; + }); + } + else if (fuse_opt0){ + ::art::Handle> opt0_h; + e.getByLabel(fopt0_producer, opt0_h); + if(!opt0_h.isValid() || opt0_h->empty()) { + std::cout << "[LightCaloProducer] : " << "don't have good OpT0Finder matches!" << std::endl; + return; + } + std::vector> opt0_v; + art::fill_ptr_vector(opt0_v, opt0_h); + + CollectMatches(opt0_h, opt0_v, fopt0_producer, e, match_slices_v, match_op0, match_op1, + [this](art::Ptr opt0){ + auto opt0_measPE = opt0->measPE; + auto opt0_hypoPE = opt0->hypoPE; + auto opt0_frac_diff = std::abs((opt0_hypoPE - opt0_measPE)/opt0_measPE); + if (opt0->time < fopflash_min || opt0->time > fopflash_max) return false; + if (opt0->score < fopt0score_cut) return false; + if (opt0_frac_diff > fopt0_frac_diff_cut) return false; + return true; + }); + } + + if (match_slices_v.empty()) return; + + bool use_arapucas; + if (std::find(fpd_types.begin(), fpd_types.end(), "xarapuca_vuv") != fpd_types.end() || + std::find(fpd_types.begin(), fpd_types.end(), "xarapuca_vis") != fpd_types.end()){ + use_arapucas = true; + } + else use_arapucas = false; + + std::vector> flash0_ara_v; + std::vector> flash1_ara_v; + if (use_arapucas){ + ::art::Handle> flash0_ara_h; + ::art::Handle> flash1_ara_h; + + for (size_t i=0; i> flash_ara_h; + e.getByLabel(fopflash_ara_producer_v[i], flash_ara_h); + if (!flash_ara_h.isValid() || flash_ara_h->empty()) { + std::cout << "[LightCaloProducer] : " << "don't have good X-ARAPUCA flashes from producer " << fopflash_ara_producer_v[i] << std::endl; + } + else{ + if (fopflash_ara_producer_v[i].find("tpc0") != std::string::npos) + art::fill_ptr_vector(flash0_ara_v, flash_ara_h); + else if (fopflash_ara_producer_v[i].find("tpc1") != std::string::npos) + art::fill_ptr_vector(flash1_ara_v, flash_ara_h); + } + } + } + + // Get electron lifetime (from database if enabled, otherwise from detector properties) + double elifetime_tpc0, elifetime_tpc1; + if (fuse_elifetime_db) { + ELifetimeInfo elife_info = GetELifetimeFromDB(e.id().run()); + elifetime_tpc0 = elife_info.tau_tpc0; + elifetime_tpc1 = elife_info.tau_tpc1; + } else { + elifetime_tpc0 = det_prop.ElectronLifetime(); + elifetime_tpc1 = det_prop.ElectronLifetime(); + } + + for (size_t n_slice=0; n_slice < match_slices_v.size(); n_slice++){ + // initialize tree variables + _pfpid = -1; + _opflash_time=-1e9; + _rec_gamma.clear(); + _dep_pe.clear(); + _visibility.clear(); + + _slice_Q = 0; + _slice_L = 0; + _slice_E = 0; + + std::vector sp_xyz; + std::vector sp_charge; // vector of charge info for charge-weighting + + auto slice = match_slices_v[n_slice]; + // sum charge information (without position info) for Q + // find which plane has the most integrated charge for this slice + std::vector> slice_hits_v = slice_to_hit.at(slice.key()); + std::vector plane_charge{0.,0.,0.}; + std::vector plane_hits{0,0,0}; + bool has_sp0 = false; + bool has_sp1 = false; + + for (size_t i=0; i < slice_hits_v.size(); i++){ + auto hit = slice_hits_v[i]; + auto drift_time = clock_data.TPCTick2TrigTime(hit->PeakTime()); + double elifetime = (hit->WireID().TPC == 0) ? elifetime_tpc0 : elifetime_tpc1; + double atten_correction = std::exp(drift_time/elifetime); // exp(us/us) + auto hit_plane = hit->View(); + plane_charge.at(hit_plane) += hit->Integral()*atten_correction*(1/fcal_area_const.at(hit_plane)); + plane_hits.at(hit_plane)++; + } + + int bestHits = std::max_element(plane_hits.begin(), plane_hits.end()) - plane_hits.begin(); + + _slice_Q = plane_charge.at(bestHits); + + // get charge information to create the weighted map + std::vector> pfp_v = slice_to_pfp.at(slice.key()); + for (size_t n_pfp=0; n_pfp < pfp_v.size(); n_pfp++){ + auto pfp = pfp_v[n_pfp]; + if (pfp->IsPrimary()) _pfpid = pfp->Self(); + + std::vector> sp_v = pfp_to_spacepoint.at(pfp.key()); + for (size_t n_sp=0; n_sp < sp_v.size(); n_sp++){ + auto sp = sp_v[n_sp]; + std::vector> hit_v = spacepoint_to_hit.at(sp.key()); + for (size_t n_hit=0; n_hit < hit_v.size(); n_hit++){ + auto hit = hit_v[n_hit]; + // + if (hit->View() !=bestHits) continue; + const auto &position(sp->XYZ()); + geo::Point_t xyz(position[0],position[1],position[2]); + auto drift_time = clock_data.TPCTick2TrigTime(hit->PeakTime()); + double elifetime = (hit->WireID().TPC == 0) ? elifetime_tpc0 : elifetime_tpc1; + double atten_correction = std::exp(drift_time/elifetime); // exp(us/us) + double charge = (1/fcal_area_const.at(hit->View()))*atten_correction*hit->Integral(); + if (xyz.X() < 0) has_sp0 = true; + else has_sp1 = true; + + sp_xyz.push_back(xyz); + sp_charge.push_back(charge); + } + } // end spacepoint loop + } // end pfp loop + + double flash_time = -999; + auto opflash0 = (match_op0.at(n_slice)); + auto opflash1 = (match_op1.at(n_slice)); + bool flash_in_0 = false; + bool flash_in_1 = false; + // find matching flashes if slice has reco spacepoints in both TPCs + if ((has_sp0 && has_sp1) && (opflash0.isNull() || opflash1.isNull())){ + if (opflash0.isNull() && opflash1.isNull()){ + if (fverbose) std::cout << "[LightCaloProducer] : " << "No opflashes found for slice with spacepoints in both TPCs." << std::endl; + return; + } + else if (opflash0.isNull()) opflash0 = FindMatchingFlash(flash0_v, opflash1->Time()); + else if (opflash1.isNull()) opflash1 = FindMatchingFlash(flash1_v, opflash0->Time()); + } + + if (!opflash0.isNull() && opflash0->TotalPE() > fnoise_thresh[0]){ + flash_in_0 = true; + flash_time = opflash0->Time(); + } + if (!opflash1.isNull() && opflash1->TotalPE() > fnoise_thresh[0]){ + flash_in_1 = true; + flash_time = opflash1->Time(); + } + + if (flash_in_0==false && flash_in_1==false) + return; + + // get total L count + std::vector> visibility_maps = CalcVisibility(sp_xyz,sp_charge); + auto dir_visibility_map = visibility_maps[0]; + auto ref_visibility_map = visibility_maps[1]; + + std::vector total_pe(nchan,0.); + std::vector total_err(nchan,0.); + std::vector total_gamma(nchan, 0.); + + // combining flash PE information from separate TPCs into a single vector + for (int tpc=0; tpc<2; tpc++){ + bool found_flash = (tpc==0)? flash_in_0 : flash_in_1; + if (found_flash){ + auto flash_pe_v = (tpc==0)? opflash0->PEs() : opflash1->PEs(); + // if using arapucas, need to combine PMT and arapuca PE information into a single vector + if (use_arapucas){ + auto flash_ara_v = (tpc==0)? flash0_ara_v : flash1_ara_v; + // for PMT flashes, the PE vector is shortened and don't include the last 6 entries for ARAPUCAs + if (flash_pe_v.size()!= nchan) flash_pe_v.resize(nchan,0); + // find matching xarapuca flash + auto flash_ara = FindMatchingFlash(flash_ara_v, flash_time); + if (flash_ara.isNull()) continue; + // add arapuca PEs to the main vector + for (size_t ich=0; ich < (flash_ara->PEs()).size(); ich++){ + if ( (flash_ara->PEs()).at(ich) > fupper_thresh[1]) continue; // skip if above max PE threshold + flash_pe_v.at(ich) += (flash_ara->PEs()).at(ich); + } + } // end of arapuca if + for (size_t ich=0; ich fupper_thresh[0]) continue; // skip if above max PE threshold + total_pe[ich] += flash_pe_v[ich]; + } + } + } // end of TPC loop + + // error is proportional to the amount of light + // that actually reached the optical detector + for (size_t ich=0; ichgetReconstructChannel(ich) || !fpmt_calib_db->getOnPMT(ich)) + total_pe.at(ich) = 0; + } + // # if ich is in fopdet_mask, set to zero + if (fopdet_mask.end() != std::find(fopdet_mask.begin(), fopdet_mask.end(), ich)) + total_pe.at(ich) = 0; + total_err.at(ich) = std::sqrt(total_pe.at(ich)); + } + _visibility.resize(nchan,0); + // calculate the photon estimates for every entry in total_pe + CalcLight(total_pe, dir_visibility_map, ref_visibility_map, total_gamma); + + // fill tree variables + _opflash_time = flash_time; + _dep_pe = total_pe; + _rec_gamma = total_gamma; + + // calculate final light estimate + _slice_L = CalcMean(total_gamma,total_err); + _slice_E = (_slice_L + _slice_Q)*1e-9*g4param->Wph(); // GeV, Wph = 19.5 eV + + if (fverbose){ + std::cout << "[LightCaloProducer] : " << "charge: " << _slice_Q << std::endl; + std::cout << "[LightCaloProducer] : " << "light: " << _slice_L << std::endl; + std::cout << "[LightCaloProducer] : " << "energy: " << _slice_E << std::endl; + } + + sbn::LightCalo lightcalo{_slice_Q,_slice_L,_slice_E,bestHits}; + lightcalo_v->push_back(lightcalo); + util::CreateAssn(*this, e, *lightcalo_v, slice, *slice_assn_v); + util::CreateAssn(*this, e, *lightcalo_v, opflash0, *flash_assn_v); + util::CreateAssn(*this, e, *lightcalo_v, opflash1, *flash_assn_v); + + if (ffill_tree) + _tree->Fill(); + } // end slice loop +} // end produce + + +// define functions +template +void sbnd::LightCaloProducer::CollectMatches(const art::Handle> &handle, + const std::vector> &fm_v, + const std::string &label, + art::Event &e, + std::vector> &match_slices_v, + std::vector> &match_op0, + std::vector> &match_op1, + CheckFunc check) +{ + std::map, std::vector>> match_slice_opflash_map; + art::FindManyP fm_to_slice(handle, e, label); + art::FindManyP fm_to_flash(handle, e, label); + + for (size_t n_fm = 0; n_fm < fm_v.size(); ++n_fm) { + auto fm = fm_v[n_fm]; + if (!check(fm)) continue; + + std::vector> slice_v = fm_to_slice.at(fm.key()); + std::vector> flash_v = fm_to_flash.at(fm.key()); + + assert(slice_v.size() == 1); + assert(flash_v.size() == 1); + + auto slice = slice_v.front(); + auto flash = flash_v.front(); + + auto it = match_slice_opflash_map.find(slice); + if (it == match_slice_opflash_map.end()){ + std::vector> fv; + fv.push_back(flash); + match_slice_opflash_map.insert(std::pair, std::vector>>(slice, fv)); + } + else{ + it->second.push_back(flash); + } + } + + for (auto it = match_slice_opflash_map.begin(); it != match_slice_opflash_map.end(); ++it){ + auto slice = it->first; + auto flash_v = it->second; + if (flash_v.size() > 2){ + std::cout << "[LightCaloProducer] : " << "more than two opflash matched to this slice!" << std::endl; + continue; + } + bool found_opflash0 = false; + bool found_opflash1 = false; + + for (size_t n_flash=0; n_flash < flash_v.size(); n_flash++){ + auto flash = flash_v[n_flash]; + if (flash->XCenter() > 0){ + found_opflash1 = true; + match_op1.push_back(flash); + } + else if (flash->XCenter() < 0){ + found_opflash0 = true; + match_op0.push_back(flash); + } + } // end opflash loop + if (found_opflash0 == false && found_opflash1 == false) + continue; + else if (found_opflash0 || found_opflash1){ + match_slices_v.push_back(slice); + art::Ptr nullOpFlash; + if (found_opflash0==false) { + match_op0.push_back(nullOpFlash); + } + else if (found_opflash1==false){ + match_op1.push_back(nullOpFlash); + } + } + } +} + +art::Ptr sbnd::LightCaloProducer::FindMatchingFlash(const std::vector> &flash_v, + double ref_time) +{ + double best_dt = std::numeric_limits::max(); + art::Ptr best; + for (size_t i = 0; i < flash_v.size(); ++i) { + art::Ptr cand = flash_v[i]; + double dt = std::abs(cand->Time() - ref_time); + if (dt < best_dt && dt < fflash_offset) { best_dt = dt; best = cand; } + } + return best; +} + + +std::vector> sbnd::LightCaloProducer::CalcVisibility(std::vector xyz_v, + std::vector charge_v){ + // returns of two vectors (len is # of opdet) for the visibility for every opdet + std::vector dir_visibility_map(nchan, 0); + std::vector ref_visibility_map(nchan, 0); + double sum_charge0 = 0; + double sum_charge1 = 0; + + for (size_t i=0; i direct_visibility; + std::vector reflect_visibility; + fsemi_model->detectedDirectVisibilities(direct_visibility, xyz); + fsemi_model->detectedReflectedVisibilities(reflect_visibility, xyz); + + // weight by charge + for (size_t ch=0; ch flash_pe_v, + std::vector dir_visibility, + std::vector ref_visibility, + std::vector &total_gamma_v){ + for (size_t ch = 0; ch < flash_pe_v.size(); ch++){ + auto pe = flash_pe_v[ch]; + auto vuv_eff = fopdet_vuv_eff.at(ch); + auto vis_eff = fopdet_vis_eff.at(ch); + auto tot_visibility = vuv_eff*dir_visibility[ch] + vis_eff*ref_visibility[ch]; + _visibility.at(ch) = tot_visibility; + if((pe == 0) || std::isinf(1/tot_visibility)) + continue; + // deposited light is inverse of visibility * PE count + total_gamma_v[ch] += (1/tot_visibility)*pe; + } +} + +double sbnd::LightCaloProducer::CalcMean(std::vector total_gamma, std::vector total_err){ + // calculates a weighted average, loops over per tpc and then per channel + double total_mean=0; + for (int tpc=0; tpc<2; tpc++){ + double wgt_num = 0; + double wgt_denom = 0; + + for (size_t ich=0; ichUpdateData((run + 1000000000) * 1000000000); + + ELifetimeInfo info; + double tau_E, tau_W; + felifetime_db->GetNamedChannelData(0, "etau_sce_spatial_east", tau_E); + felifetime_db->GetNamedChannelData(0, "etau_sce_spatial_west", tau_W); + + info.tau_tpc0 = tau_E*1e3; // the db value is in ms, convert to us + info.tau_tpc1 = tau_W*1e3; // the db value is in ms, convert to us + + if (fverbose) { + std::cout << "[LightCaloProducer] : Electron lifetime from DB for run " << run << std::endl; + std::cout << "[LightCaloProducer] : TPC0 (East): " << info.tau_tpc0 << " us" << std::endl; + std::cout << "[LightCaloProducer] : TPC1 (West): " << info.tau_tpc1 << " us" << std::endl; + } + + // Cache the result + felifetime_cache[run] = info; + return info; +} + +DEFINE_ART_MODULE(sbnd::LightCaloProducer) diff --git a/sbndcode/Calorimetry/lightcalo_sbnd.fcl b/sbndcode/Calorimetry/lightcalo_sbnd.fcl new file mode 100644 index 000000000..29b773bc6 --- /dev/null +++ b/sbndcode/Calorimetry/lightcalo_sbnd.fcl @@ -0,0 +1,62 @@ +#include "opticalsimparameterisations_sbnd.fcl" +#include "sbndopticalpath_tool.fcl" +#include "digi_pmt_sbnd.fcl" +#include "digi_arapuca_sbnd.fcl" +#include "calorimetry_sbnd.fcl" + +BEGIN_PROLOG +sbnd_lightcalo: +{ + module_type: "LightCaloProducer" + + VUVHits: @local::sbnd_vuv_RS100cm_hits_parameterization + VIVHits: @local::sbnd_vis_RS100cm_hits_parameterization + OpticalPathTool: @local::SBNDOpticalPath + + OpFlashProducers: ["opflashtpc0", "opflashtpc1"] + OpFlashAraProducers: ["opflashtpc0xarapuca", "opflashtpc1xarapuca"] + # only validated for coated PMTs for now + PDTypes: ["pmt_coated"] # "pmt_uncoated", "xarapuca_vuv", "xarapuca_vis" + + SliceProducer: "pandora" + OpT0FinderProducer: "opt0finder" + BCFMProducer: "tpcpmtbarycentermatching" + + UseBCFM: true + UseOpT0Finder: false + Verbose: false + FillTree: false + + bcfmScoreCut: 0.02 # default: accept bcfm match > bcfmScoreCut + opt0ScoreCut: 200 # default: accept opt0 match > opt0ScoreCut + opt0FractionalCut: 0.5 + + OpFlashMin: 0.0 # in us, simulation [0.0], light-triggered data [-0.8] + OpFlashMax: 2.0 # in us, simulation [2.0], light-triggered data [2.0] + + # flash parameters + FlashOffset: 0.05 # in us, acceptable offset between the t0 between TPCs or between PMTs/X-ARAPUCAs + FlashNoiseThreshold: [ 600., 1500. ] # in PE, {PMT, xARAPUCA}, flash total PE threshold to ignore a flash + OpDetMaxPEThreshold: [4000., 1e9] # in PE, PE in a single opdet to ignore (due to saturation effects) + OpDetMask: [] # list of opdet channels to ignore (off/non-reconstructable PMTs are already identified via calibration db) + + # calibration constants & simulation parameters + CalAreaConstants: @local::sbnd_calorimetryalgmc.CalAreaConstants + PMTCoatedVISEff_tpc0: @local::sbnd_digipmt_alg.PMTCoatedVISEff_tpc0 + PMTCoatedVUVEff_tpc0: @local::sbnd_digipmt_alg.PMTCoatedVUVEff_tpc0 + PMTUncoatedEff_tpc0: @local::sbnd_digipmt_alg.PMTUncoatedEff_tpc0 + PMTCoatedVISEff_tpc1: @local::sbnd_digipmt_alg.PMTCoatedVISEff_tpc1 + PMTCoatedVUVEff_tpc1: @local::sbnd_digipmt_alg.PMTCoatedVUVEff_tpc1 + PMTUncoatedEff_tpc1: @local::sbnd_digipmt_alg.PMTUncoatedEff_tpc1 + + XArapucaVUVVUVEff: @local::sbnd_digiarapuca_alg.XArapucaVUVEffVUV + XArapucaVUVVISEff: @local::sbnd_digiarapuca_alg.XArapucaVUVEffVis + XArapucaVISEff: @local::sbnd_digiarapuca_alg.XArapucaVISEff +} + +sbnd_lightcalo_sce: @local::sbnd_lightcalo +sbnd_lightcalo_sce.SliceProducer: "pandoraSCE" +sbnd_lightcalo_sce.OpT0FinderProducer: "opt0finderSCE" +sbnd_lightcalo_sce.BCFMProducer: "tpcpmtbarycentermatchingSCE" + +END_PROLOG diff --git a/sbndcode/Calorimetry/lightcalo_sbnd_data.fcl b/sbndcode/Calorimetry/lightcalo_sbnd_data.fcl new file mode 100644 index 000000000..f3bc835e2 --- /dev/null +++ b/sbndcode/Calorimetry/lightcalo_sbnd_data.fcl @@ -0,0 +1,19 @@ +#include "lightcalo_sbnd.fcl" +#include "calibration_database_GlobalTags_sbnd.fcl" + +BEGIN_PROLOG +sbnd_lightcalo_data: @local::sbnd_lightcalo +sbnd_lightcalo_data.CalAreaConstants: @local::sbnd_calorimetryalgdata.CalAreaConstants +sbnd_lightcalo_data.OpFlashMin: -0.8 # assumption is light-triggered data +sbnd_lightcalo_data.OpFlashMax: 2.0 # assumption is light-triggered data + +sbnd_lightcalo_data.UseELifetimeDB: true +sbnd_lightcalo_data.ELifetimeDBFile: "tpc_elifetime" +sbnd_lightcalo_data.ELifetimeDBTag: @local::SBND_Calibration_GlobalTags.tpc_elifetime_data + +sbnd_lightcalo_data_sce: @local::sbnd_lightcalo_data +sbnd_lightcalo_data_sce.SliceProducer: "pandoraSCE" +sbnd_lightcalo_data_sce.OpT0FinderProducer: "opt0finderSCE" +sbnd_lightcalo_data_sce.BCFMProducer: "tpcpmtbarycentermatchingSCE" + +END_PROLOG \ No newline at end of file diff --git a/sbndcode/Calorimetry/run_lightcalo.fcl b/sbndcode/Calorimetry/run_lightcalo.fcl new file mode 100644 index 000000000..ba0791771 --- /dev/null +++ b/sbndcode/Calorimetry/run_lightcalo.fcl @@ -0,0 +1,59 @@ +#include "services_sbnd.fcl" +#include "simulationservices_sbnd.fcl" +#include "messages_sbnd.fcl" +#include "sam_sbnd.fcl" +#include "lightcalo_sbnd.fcl" + +process_name: LightCaloProducer + +services: +{ + TFileService: { fileName: "lightcalo_tree.root" } + RandomNumberGenerator: {} + message: @local::sbnd_message_services_prod # from messages_sbnd.fcl + FileCatalogMetadata: @local::sbnd_file_catalog_mc # from sam_sbnd.fcl + @table::sbnd_services # from services_sbnd.fcl + @table::sbnd_g4_services + +} + +source: +{ + module_type: RootInput + maxEvents: -1 +} + +outputs: +{ + out1: + { + module_type: RootOutput + fileName: "%ifb_lightcalo-art.root" + dataTier: "reconstructed" + compressionLevel:1 + } +} + +physics: +{ + producers: + { + lightcalo: @local::sbnd_lightcalo + } + + #define the producer and filter modules for this path, order matters, + reco: [lightcalo] + + # define the output stream, there could be more than one if using filters + stream1: [out1] + + # trigger_paths is a keyword and contains the paths that modify the art::event, + # ie filters and producers + trigger_paths: [reco] + + # end_paths is a keyword and contains the paths that do not modify the art::Event, + # ie analyzers and output streams. these all run simultaneously + end_paths: [stream1] +} + +physics.producers.lightcalo.FillTree: true \ No newline at end of file diff --git a/sbndcode/ChannelMaps/CRT/CMakeLists.txt b/sbndcode/ChannelMaps/CRT/CMakeLists.txt index b67ef20a5..9e6a43cfa 100644 --- a/sbndcode/ChannelMaps/CRT/CMakeLists.txt +++ b/sbndcode/ChannelMaps/CRT/CMakeLists.txt @@ -2,7 +2,7 @@ install_headers() install_fhicl() install_source() -file(GLOB channel_map_file *ChannelMap*.txt) +file(GLOB channel_map_file *Map*.txt) install_fw( LIST ${channel_map_file} ) art_make(SERVICE_LIBRARIES diff --git a/sbndcode/ChannelMaps/CRT/CRTChannelMapService.h b/sbndcode/ChannelMaps/CRT/CRTChannelMapService.h index fc22880d1..6fce72894 100644 --- a/sbndcode/ChannelMaps/CRT/CRTChannelMapService.h +++ b/sbndcode/ChannelMaps/CRT/CRTChannelMapService.h @@ -59,6 +59,12 @@ class SBND::CRTChannelMapService { unsigned int GetMAC5FromOfflineChannelID(const unsigned int offline_channel_id); + unsigned int GetTimingChainFromOfflineModuleID(unsigned int offline_module_id); + + unsigned int GetTimingChainFromOfflineChannelID(unsigned int offline_channel_id); + + unsigned int GetTimingChainFromMAC5(unsigned int mac5); + private: typedef struct ModuleInfo { @@ -75,6 +81,12 @@ class SBND::CRTChannelMapService { // look up channel info by MAC5 std::unordered_map fModuleInfoFromMAC5; ModuleInfo_t GetModuleInfoFromMAC5(unsigned int mac5) const; + + // look up timing chain by offline module number + std::unordered_map fTimingChainFromOfflineID; + + // look up channel info by MAC5 + std::unordered_map fTimingChainFromMAC5; }; DECLARE_ART_SERVICE(SBND::CRTChannelMapService, LEGACY) diff --git a/sbndcode/ChannelMaps/CRT/CRTChannelMapService_service.cc b/sbndcode/ChannelMaps/CRT/CRTChannelMapService_service.cc index 3e3d45cfa..63f27ad88 100644 --- a/sbndcode/ChannelMaps/CRT/CRTChannelMapService_service.cc +++ b/sbndcode/ChannelMaps/CRT/CRTChannelMapService_service.cc @@ -17,28 +17,30 @@ SBND::CRTChannelMapService::CRTChannelMapService(fhicl::ParameterSet const& pset) { - const std::string channelMapFile = pset.get("FileName"); + const std::string channelMapFile = pset.get("ChannelMapFileName"); + const std::string timingChainMapFile = pset.get("TimingChainMapFileName"); - std::string fullname; + std::string channelMapFileFullName, timingChainMapFileFullName; cet::search_path sp("FW_SEARCH_PATH"); - sp.find_file(channelMapFile, fullname); + sp.find_file(channelMapFile, channelMapFileFullName); + sp.find_file(timingChainMapFile, timingChainMapFileFullName); - if(fullname.empty()) + if(channelMapFileFullName.empty()) { std::cout << "SBND::CRTChannelMapService Input file " << channelMapFile << " not found" << std::endl; throw cet::exception("File not found"); } std::cout << "SBND CRT Channel Map: Building map from file " << channelMapFile << std::endl; - std::ifstream inFile(fullname, std::ios::in); + std::ifstream channelMapInFile(channelMapFileFullName, std::ios::in); std::string line; - while(std::getline(inFile,line)) + while(std::getline(channelMapInFile, line)) { std::stringstream linestream(line); SBND::CRTChannelMapService::ModuleInfo_t m; - linestream + linestream >> m.offline_module_id >> m.mac5 >> m.channel_order_swapped; @@ -49,7 +51,38 @@ SBND::CRTChannelMapService::CRTChannelMapService(fhicl::ParameterSet const& pset fModuleInfoFromMAC5[m.mac5] = m; } - inFile.close(); + channelMapInFile.close(); + + if(timingChainMapFileFullName.empty()) + { + std::cout << "SBND::CRTChannelMapService Input file " << timingChainMapFile << " not found" << std::endl; + throw cet::exception("File not found"); + } + + std::cout << "SBND CRT Channel Map: Building timing chain map from file " << timingChainMapFile << std::endl; + std::ifstream timingChainMapInFile(timingChainMapFileFullName, std::ios::in); + + while(std::getline(timingChainMapInFile, line)) + { + std::stringstream linestream(line); + + unsigned int timing_chain = std::numeric_limits::max(); + unsigned int n = 0; + + linestream + >> timing_chain + >> n; + + for(unsigned int i = 0; i < n; ++i) + { + unsigned int offline_module_id = std::numeric_limits::max(); + linestream >> offline_module_id; + fTimingChainFromOfflineID[offline_module_id] = timing_chain; + fTimingChainFromMAC5[GetMAC5FromOfflineModuleID(offline_module_id)] = timing_chain; + } + } + + timingChainMapInFile.close(); } SBND::CRTChannelMapService::ModuleInfo_t SBND::CRTChannelMapService::GetModuleInfoFromMAC5(unsigned int mac5) const @@ -202,4 +235,19 @@ unsigned int SBND::CRTChannelMapService::GetMAC5FromOfflineChannelID(const unsig return GetMAC5FromOfflineModuleID(GetOfflineModuleIDFromOfflineChannelID(offline_channel_id)); } +unsigned int SBND::CRTChannelMapService::GetTimingChainFromOfflineModuleID(unsigned int offline_module_id) +{ + return fTimingChainFromOfflineID[offline_module_id]; +} + +unsigned int SBND::CRTChannelMapService::GetTimingChainFromOfflineChannelID(unsigned int offline_channel_id) +{ + return GetTimingChainFromOfflineModuleID(GetOfflineModuleIDFromOfflineChannelID(offline_channel_id)); +} + +unsigned int SBND::CRTChannelMapService::GetTimingChainFromMAC5(unsigned int mac5) +{ + return fTimingChainFromMAC5[mac5]; +} + DEFINE_ART_SERVICE(SBND::CRTChannelMapService) diff --git a/sbndcode/ChannelMaps/CRT/SBNDCRTChannelMap_v5_no_inversion.txt b/sbndcode/ChannelMaps/CRT/SBNDCRTChannelMap_v5_no_inversion.txt new file mode 100644 index 000000000..7e0478c14 --- /dev/null +++ b/sbndcode/ChannelMaps/CRT/SBNDCRTChannelMap_v5_no_inversion.txt @@ -0,0 +1,87 @@ +40 173 0 +41 172 0 +42 168 0 +43 170 0 +44 176 0 +45 59 0 +46 171 0 +47 61 0 +48 166 0 +49 169 0 +50 56 0 +51 60 0 +52 34 0 +53 33 0 +54 57 0 +55 24 0 +56 159 0 +57 153 0 +58 156 0 +59 152 0 +60 182 0 +61 158 0 +62 157 0 +63 136 0 +64 150 0 +65 151 0 +66 134 0 +67 135 0 +68 149 0 +69 58 0 +70 238 0 +71 155 0 +72 222 0 +73 220 0 +74 81 0 +75 85 0 +76 79 0 +77 206 0 +78 204 0 +79 200 0 +80 18 0 +81 132 0 +82 133 0 +83 162 0 +84 143 0 +85 131 0 +86 146 0 +87 147 0 +88 44 0 +89 160 0 +90 19 0 +91 202 0 +92 199 0 +93 197 0 +94 207 0 +95 203 0 +96 45 0 +97 198 0 +98 174 0 +99 148 0 +100 163 0 +101 164 0 +102 165 0 +103 80 0 +104 193 0 +105 42 0 +106 138 0 +107 130 0 +108 77 0 +109 78 0 +110 98 0 +111 97 0 +112 87 0 +113 95 0 +114 94 0 +115 93 0 +116 86 0 +117 83 0 +118 84 0 +119 104 0 +120 103 0 +121 102 0 +122 101 0 +123 100 0 +124 99 0 +125 90 0 +126 91 0 diff --git a/sbndcode/ChannelMaps/CRT/SBNDCRTTimingChainMap_v1.txt b/sbndcode/ChannelMaps/CRT/SBNDCRTTimingChainMap_v1.txt new file mode 100644 index 000000000..e37aa53cb --- /dev/null +++ b/sbndcode/ChannelMaps/CRT/SBNDCRTTimingChainMap_v1.txt @@ -0,0 +1,20 @@ +0 8 127 126 125 124 123 122 120 121 +1 6 139 128 137 138 136 135 +2 6 134 133 132 131 130 129 +3 8 63 62 54 55 56 57 58 59 +4 8 64 65 50 51 52 53 61 60 +5 6 81 80 82 79 83 78 +6 6 66 67 68 69 77 76 +7 6 70 71 72 73 74 75 +8 9 115 106 107 108 109 110 111 112 113 +9 9 116 117 118 119 102 103 104 105 114 +10 9 101 84 85 86 87 96 95 94 93 +11 9 100 99 98 97 88 89 90 91 92 +12 6 33 32 31 30 40 41 +13 6 42 43 44 25 26 27 +14 7 37 38 39 29 28 49 48 +15 6 36 35 34 45 46 47 +16 6 21 22 23 24 9 8 +17 6 7 6 5 19 18 17 +18 7 12 11 10 15 16 0 1 +19 6 13 14 20 4 3 2 diff --git a/sbndcode/ChannelMaps/CRT/crt_channel_map_service_sbnd.fcl b/sbndcode/ChannelMaps/CRT/crt_channel_map_service_sbnd.fcl index a34f136cd..811ca92d0 100644 --- a/sbndcode/ChannelMaps/CRT/crt_channel_map_service_sbnd.fcl +++ b/sbndcode/ChannelMaps/CRT/crt_channel_map_service_sbnd.fcl @@ -2,7 +2,13 @@ BEGIN_PROLOG crt_channel_map_sbnd: { - FileName: "SBNDCRTChannelMap_Commissioning_v5.txt" + ChannelMapFileName: "SBNDCRTChannelMap_Commissioning_v5.txt" + TimingChainMapFileName: "SBNDCRTTimingChainMap_v1.txt" +} + +crt_channel_map_no_inversion: +{ + FileName: "SBNDCRTChannelMap_v5_no_inversion.txt" } END_PROLOG diff --git a/sbndcode/Decoders/XARAPUCA/SBNDXARAPUCADecoder_module.cc b/sbndcode/Decoders/XARAPUCA/SBNDXARAPUCADecoder_module.cc index ac94c56e1..76d26660a 100644 --- a/sbndcode/Decoders/XARAPUCA/SBNDXARAPUCADecoder_module.cc +++ b/sbndcode/Decoders/XARAPUCA/SBNDXARAPUCADecoder_module.cc @@ -10,9 +10,10 @@ * @brief Defines and implements the SBNDXARAPUCADecoder class which inherits from an art::EDProducer * as the decoder for V1740B digitizers, intended for the X-ARAPUCAs. * @details The current version of the SBND X-ARAPUCAs decoder implements the updates shown in - * the SBN Document 38475-v1 in the SBN Document Database. + * the SBN Document 43891-v1 in the SBN Document Database. * @note A Python version of the binary decoding is available for testing purposes. You can find * it [here: V1740 binary decoder](https://github.com/aliciavr/V1740_binary_decoder). + * @version 4.0 */ #include "art/Framework/Core/EDProducer.h" @@ -74,7 +75,6 @@ class sbndaq::SBNDXARAPUCADecoder : public art::EDProducer { void produce(art::Event& e) override; private: - constexpr static uint64_t NANOSEC_IN_SEC = 1'000'000'000; /**< Number of nanoseconds in one second. */ constexpr static uint64_t MICROSEC_IN_NANOSEC = 1'000; /**< Number of nanoseconds in one microsecond. */ constexpr static double NANOSEC_TO_MICROSEC = 1E-3; /**< Conversion factor from nanoseconds to microseconds. */ @@ -91,6 +91,7 @@ class sbndaq::SBNDXARAPUCADecoder : public art::EDProducer { constexpr static uint16_t PTB_TIMING = 1; /**< Timing reference frame: HLT (High Level Trigger) timestamp from the PTB (Penn Trigger Board). */ constexpr static uint16_t CAEN_ONLY_TIMING = 2; /**< Timing reference frame: CAEN-only. */ + constexpr static uint16_t TTT_DEFAULT = 0; /**< Default integer value for the nominal TTT. */ constexpr static uint16_t HLT_NOT_FOUND = 999; /**< Random value to indicate no HL trigger found. */ constexpr static uint16_t HLT_TOO_FAR = 1000; /**< Random value to indicate HL trigger found but too far from the raw timestamp. */ @@ -124,30 +125,46 @@ class sbndaq::SBNDXARAPUCADecoder : public art::EDProducer { art::ServiceHandle tfs; /**< ServiceHandle object to store the histograms in the decoder_hist.root output file. */ int fstore_debug_waveforms; /**< Number of waveforms to store in the ServiceHandle object for debugging purposes (0: none, -1: all, n: first n waveforms each event). */ + bool fcombine_ext_frag; /**< If `true` combines extended fragments into a single raw::OpDetWaveform object. */ + int32_t fallowed_jittering; /**< Allowed jittering (in ns) between fragments to be combined. */ + bool fdebug_tdc_handle; /**< If `true` SPEC-TDC information is printed. */ bool fdebug_ptb_handle; /**< If `true` PTB information is printed. */ bool fdebug_fragments_handle; /**< If `true` V1740B CAEN fragments art::Handle information is printed. */ bool fdebug_timing; /**< If `true` timing data is printed. */ bool fdebug_buffer; /**< If `true` the buffer status is printed. */ bool fdebug_waveforms; /**< If `true` waveforms decoding data is printed. */ + bool fdebug_extended_fragments; /**< If `true` extended fragments information is printed. */ + bool fdebug_jittering; /**< If `true` trigger jittering information is printed. */ bool fverbose; /**< If `true` it increases verbosity of console output for detailed processing steps. */ - // Class methods. - void decode_fragment(uint64_t timestamp, std::vector & fragment_indices, const artdaq::Fragment& fragment, std::vector & prod_wvfms); + // Main processing method. + void decode_fragment(uint64_t timestamp, uint64_t& first_frag_timestamp, size_t& first_frag_idx, int32_t& first_TTT, int32_t& prev_TTT, std::vector & fragment_indices, const artdaq::Fragment& fragment, std::vector & prod_wvfms, std::vector>& wvfms, bool last_one); - bool get_ptb_hlt_timestamp(art::Event& e, uint64_t corr_raw_timestamp, uint64_t & timestamp, uint16_t & hlt_code); + // Timing. + void shift_time(uint64_t TTT_ticks, int64_t TTT_end_ns, uint64_t frag_timestamp, uint64_t timestamp, uint32_t num_samples_per_wvfm, double& ini_wvfm_timestamp, double& end_wvfm_timestamp); bool get_spec_tdc_etrig_timestamp(art::Event& e, uint64_t corr_raw_timestamp, uint64_t & timestamp); + bool get_ptb_hlt_timestamp(art::Event& e, uint64_t corr_raw_timestamp, uint64_t & timestamp, uint16_t & hlt_code); - void save_prod_wvfm(size_t board_idx, size_t ch, double ini_wvfm_timestamp, const std::vector > & wvfms, std::vector & prod_wvfms); - void save_debug_wvfm(size_t board_idx, size_t fragment_idx, int ch, double ini_wvfm_timestamp, double end_wvfm_timestamp, const std::vector > & wvfms); - + // Waveforms decoding. + void decode_waveforms(const artdaq::Fragment& fragment, std::vector>& wvfms, size_t header_size, uint32_t num_channels, uint32_t num_samples_per_wvfm, uint32_t num_words_per_wvfms, uint32_t num_samples_per_group); uint16_t get_sample(uint64_t buffer, uint32_t msb, uint32_t lsb); uint32_t read_word(const uint32_t* & data_ptr); unsigned int get_channel_id(unsigned int board, unsigned int board_channel); - std::string print_timestamp(uint64_t timestamp); + // Combines decoded waveforms. + void append_waveforms(std::vector>& wvfms, const std::vector>& fragment_wvfms, uint32_t num_channels); + + // Dumps and saves waveforms. + void dump_waveforms(std::vector & prod_wvfms, std::vector>& wvfms, size_t first_frag_idx, size_t board_index, uint32_t num_channels, double ini_wvfm_timestamp, double end_wvfm_timestamp); + void save_prod_wvfm(size_t board_idx, size_t ch, double ini_wvfm_timestamp, const std::vector > & wvfms, std::vector & prod_wvfms); + void save_debug_wvfm(size_t board_idx, size_t fragment_idx, int ch, double ini_wvfm_timestamp, double end_wvfm_timestamp, const std::vector > & wvfms); + + // Auxiliary methods. int64_t signed_difference(uint64_t t1, uint64_t t2); uint64_t abs_difference(uint64_t t1, uint64_t t2); + std::string print_timestamp(uint64_t timestamp); + }; /** @@ -199,12 +216,18 @@ sbndaq::SBNDXARAPUCADecoder::SBNDXARAPUCADecoder(fhicl::ParameterSet const& p) // Gets the number of waveforms to store in the debug output file. fstore_debug_waveforms = p.get ("store_debug_waveforms", 0); + // Gets the combination of extended fragments option. + fcombine_ext_frag = p.get ("combine_ext_frag", true); + fallowed_jittering = p.get ("allowed_jittering", 64); + // Gets the debug and verbose options. fdebug_ptb_handle = p.get ("debug_ptb_handle", false); fdebug_tdc_handle = p.get ("debug_tdc_handle", false); fdebug_fragments_handle = p.get ("debug_fragments_handle", false); fdebug_timing = p.get ("debug_timing", false); fdebug_waveforms = p.get ("debug_waveforms", false); + fdebug_extended_fragments = p.get ("debug_extended_fragments", false); + fdebug_jittering = p.get ("debug_jittering", false); fdebug_buffer = p.get ("debug_buffer", false); fverbose = p.get ("verbose", false); @@ -224,8 +247,7 @@ sbndaq::SBNDXARAPUCADecoder::SBNDXARAPUCADecoder(fhicl::ParameterSet const& p) * 3. It searches for CAEN V1740 fragments in the event, decodes them and creates the output products: a vector of raw::OpDetWaveform. * 4. It dumps the products in the event. */ -void sbndaq::SBNDXARAPUCADecoder::produce(art::Event& e) -{ +void sbndaq::SBNDXARAPUCADecoder::produce(art::Event& e) { if (fverbose) std::cout << "\n > SBNDXARAPUCADecoder::produce: entering the produce function." << std::endl; // Advances the event counter. @@ -300,7 +322,17 @@ void sbndaq::SBNDXARAPUCADecoder::produce(art::Event& e) // Flag to track if valid CAEN fragments are found. bool found_caen = false; - std::vector fragment_indices(fnum_caen_boards, 0); + std::vector fragment_indices(fnum_caen_boards, -1); + std::vector> wvfms; + if (fdebug_extended_fragments) { + std::cout << " Waveforms size: " << wvfms.size() << std::endl; + } + + size_t first_frag_idx = 0; + int32_t first_TTT = TTT_DEFAULT; + int32_t prev_TTT = 0; + uint64_t first_frag_timestamp = TTT_DEFAULT; + bool last_one = false; if (fverbose | fdebug_fragments_handle) std::cout << "\n > SBNDXARAPUCADecoder::produce: searching for V1740 fragments..." << std::endl; @@ -338,7 +370,8 @@ void sbndaq::SBNDXARAPUCADecoder::produce(art::Event& e) for (size_t f = 0; f < num_caen_fragments; f++) { const artdaq::Fragment fragment = *container_fragment[f].get(); - decode_fragment(timestamp, fragment_indices, fragment, *prod_wvfms); + last_one = f == (num_caen_fragments - 1); + decode_fragment(timestamp, first_frag_timestamp, first_frag_idx, first_TTT, prev_TTT, fragment_indices, fragment, *prod_wvfms, wvfms, last_one); } // End CAEN V1740 fragments loop. } } // End Container fragments loop. @@ -350,7 +383,8 @@ void sbndaq::SBNDXARAPUCADecoder::produce(art::Event& e) // It searches for all CAEN V1740 fragments. for (size_t f = 0; f < frag_handle_size; f++) { const artdaq::Fragment fragment = fragment_handle->at(f); - decode_fragment(timestamp, fragment_indices, fragment, *prod_wvfms); + last_one = f == (frag_handle_size - 1); + decode_fragment(timestamp, first_frag_timestamp, first_frag_idx, first_TTT, prev_TTT, fragment_indices, fragment, *prod_wvfms, wvfms, last_one); } // End CAEN V1740 fragments loop. } } // End extracting CAEN V1740 fragments. @@ -371,16 +405,297 @@ void sbndaq::SBNDXARAPUCADecoder::produce(art::Event& e) } } +// =============== Main fragment processing method =============== // + /** -* @brief Searches for the SPEC-TDC ETRIG timestamp closest to the raw timestamp if any SPEC-TDC ETRIG product is found in the event. -* @param[in] e The event to be processed. -* @param[in] corr_raw_timestamp The corrected raw timestamp from the artdaq::RawEventHeader product. -* @param[in,out] timestamp The closest ETRIG timestamp to the raw timestamp (if found). -* @return A boolean indicating if a valid ETRIG timestamp was found close enough to the raw timestamp. -* @details It searches for the SPEC-TDC products in the event and looks for the ETRIG timestamps. If any ETRIG -* timestamp is found, it checks which one is the closest to the raw timestamp and if it is close enough (i.e. -* within fraw_trig_max_diff) it returns it as output. -*/ + * @brief Decodes a CAEN V1740 fragment, extracts the waveforms and combines them into the output product. + * @param[in] timestamp The valid timestamp used as reference for the event. + * @param[in,out] first_frag_timestamp The nominal timestamp calculated for the fragment being processed. + * @param[in, out] first_frag_idx The first index of the fragments being combined. + * @param[in,out] first_TTT The nominal Trigger Time Tag (TTT) calculated for the fragment being processed. + * @param[in,out] fragment_indices A vector containing the indices of the fragments already processed for each board. + * @param[in] fragment The artdaq::Fragment object to be processed. + * @param[in,out] prod_wvfms The vector of raw::OpDetWaveform objects to be filled with the decoded waveforms. + * @param[in,out] wvfms A 2D vector containing the waveforms for all channels and boards decoded so far. + * @param[in] last_one A boolean flag indicating if the fragment being processed is the last one in the event. + + * @details This method decodes a CAEN V1740 fragment, extracts the waveforms for each channel, shifts them in time according to the + * timing reference and combines them into the output product. + * 1. It checks if the fragment ID corresponds to a valid board. + * 2. It accesses the metadata and header of the fragment to get information about the number of channels, samples, words, etc. + * 3. It calculates the number of samples per waveform and checks if it is a nominal or extended fragment. + * 4. It decodes the waveforms for each channel in the fragment. + * 5. It shifts the waveforms in time according to the timing reference. + * 6. It combines the decoded waveforms into the output product. + * 7. It dumps the waveforms if required. + * + * @pre The art::Event object containing the fragment has been processed to get a valid timing reference. + * @post The vector of raw::OpDetWaveform objects is filled with the decoded waveforms from the fragment. + * @see shift_time + * @see decode_waveforms + * @see append_waveforms + * @see dump_waveforms + */ + +void sbndaq::SBNDXARAPUCADecoder::decode_fragment(uint64_t timestamp, uint64_t& first_frag_timestamp, size_t& first_frag_idx, int32_t& first_TTT, int32_t& prev_TTT, std::vector & fragment_indices, const artdaq::Fragment& fragment, std::vector & prod_wvfms, std::vector>& wvfms, bool last_one) { + auto fragment_id = fragment.fragmentID() - ffragment_id_offset; + auto it = std::find(fboard_id_list.begin(), fboard_id_list.end(), fragment_id); + size_t board_idx; + bool valid_fragment = false; + + if (it != fboard_id_list.end()) { + board_idx = it - fboard_id_list.begin(); + if (board_idx >= fnum_caen_boards) { + if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: fragment ID " << fragment_id << " (" << board_idx << ") is out of range. Skipping this fragment..." << std::endl; + } else { + valid_fragment = true; + } + } else { + if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: fragment ID " << fragment_id << " is not valid. Skipping this fragment..." << std::endl; + } + + if (valid_fragment) { + fragment_indices[board_idx]++; + if (fverbose) std::cout << "\n > SBNDXARAPUCADecoder::decode_fragment: decoding V1740 CAEN fragment " << fragment_indices[board_idx] << " from the board " << board_idx << " (slot " << fboard_id_list[board_idx] << "):" << std::endl; + + // =============== Accesses Event metadata and Event header for this fragment =============== // + + CAENV1740Fragment caen_fragment(fragment); + CAENV1740FragmentMetadata const* metadata = caen_fragment.Metadata(); + uint32_t num_channels = metadata->nChannels; + if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: number of channels: " << num_channels << std::endl; + + // Accesses the event and header data of the CAEN fragment. + CAENV1740Event const* event = caen_fragment.Event(); + CAENV1740EventHeader header = event->Header; + + // Gets the number of words of the header and the waveforms. + uint32_t num_words_per_event = header.eventSize; + size_t header_size = sizeof(CAENV1740EventHeader); + uint32_t num_words_per_header = header_size / sizeof(uint32_t); + uint32_t num_words_per_wvfms = (num_words_per_event - num_words_per_header); + + uint32_t num_bits_per_all_wvfms = num_words_per_wvfms * BITS_PER_WORD; + uint32_t num_samples_per_all_wvfms = num_bits_per_all_wvfms / BITS_PER_SAMPLE; + uint32_t num_remaining_bits = num_bits_per_all_wvfms % BITS_PER_SAMPLE; + uint32_t num_samples_per_wvfm = num_samples_per_all_wvfms / num_channels; + uint32_t num_samples_per_group = num_samples_per_wvfm * NUM_CHANNELS_PER_GROUP; + uint32_t num_nominal_samples_per_wvfm = metadata->nSamples; + + if (fverbose | fdebug_waveforms) { + if (num_nominal_samples_per_wvfm == num_samples_per_wvfm) { + std::cout << " > SBNDXARAPUCADecoder::decode_fragment: [NOMINAL FRAGMENT] " << num_samples_per_wvfm << " samples/waveform." << " (" << num_samples_per_group << " samples per group - 8 channels per group -)." << std::endl; + } else { + std::cout << " > SBNDXARAPUCADecoder::decode_fragment: [EXTENDED FRAGMENT] " << num_samples_per_wvfm << " samples/waveform." << " (" << num_samples_per_group << " samples per group - 8 channels per group -)." << std::endl; + } + std::cout << " > SBNDXARAPUCADecoder::decode_fragment: nominal number of samples per waveform: " << num_nominal_samples_per_wvfm << "." << std::endl; + std::cout << " > SBNDXARAPUCADecoder::decode_fragment: number of words for this fragment: " << num_words_per_event << " (Header: " << num_words_per_header << ", Waveform: " << num_words_per_wvfms << ") words." << std::endl; + } + + if (fdebug_waveforms) { + std::cout << "\t Number of bits for all the waveforms of this fragment: " << BITS_PER_WORD << "\t" << num_bits_per_all_wvfms << std::endl; + std::cout << "\t Number of samples for all the waveforms of this fragment: " << num_samples_per_all_wvfms << std::endl; + std::cout << "\t Number of remaining bits for this fragment: " << num_remaining_bits << std::endl; + std::cout << "\t Number of samples per wvfm (this fragment): " << num_samples_per_wvfm << std::endl; + std::cout << "\t Number of samples per group (this fragment): " << num_samples_per_group << std::endl; + } + + // =============== Extracts timing information for this fragment =============== // + + uint64_t frag_timestamp = fragment.timestamp(); // ns. + uint32_t TTT_ticks = header.triggerTime(); + int64_t TTT_end_ns = TTT_ticks * NANOSEC_PER_TICK; // ns. + + if (fdebug_timing) { + std::cout << "\t\t TTT header.extendedTriggerTime() [TTT_ticks] = " << header.extendedTriggerTime() << " ticks. \t TTT_end_ns = " << print_timestamp(header.extendedTriggerTime() * NANOSEC_PER_TICK) << "." << std::endl; + std::cout << "\t\t TTT header.triggerTimeRollOver(): " << header.triggerTimeRollOver() << std::endl; + } + + //// =============== Start decoding the waveforms =============== // + std::vector > fragment_wvfms(num_channels, std::vector(num_samples_per_wvfm, 0)); + decode_waveforms(fragment, fragment_wvfms, header_size, num_channels, num_samples_per_wvfm, num_words_per_wvfms, num_samples_per_group); + + double ini_wvfm_timestamp = 0; + double end_wvfm_timestamp = 0; + + if (fverbose) { + std::cout << " > SBNDXARAPUCADecoder::decode_fragment: processing the decoded fragment and combines the extended ones to their nominal ones if needed." << std::endl; + } + + if (fcombine_ext_frag) { + + // Combination variables and checks. + + bool is_nominal = (num_nominal_samples_per_wvfm == num_samples_per_wvfm); + bool is_first = (fragment_indices[board_idx] == 0); + + int32_t frag_length = num_samples_per_wvfm * fns_per_sample; // ns. + + // - Computation of the difference w.r.t. previous TTT difference. + int32_t TTT_dif = 0; + if (prev_TTT > TTT_end_ns) { + if (fverbose | fdebug_timing) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: CAEN TTT rollover occurred." << std::endl; + TTT_dif = prev_TTT - (NANOSEC_IN_SEC - TTT_end_ns); + } else { + TTT_dif = TTT_end_ns - prev_TTT; + } + + // - Computation of the difference between TTTs difference and the length of the fragment in ns (to detect jittering between fragments). + int32_t J = TTT_dif - frag_length; + + bool dump_comb_wvfms = !is_first && (is_nominal || (!is_nominal && (TTT_dif > (frag_length + fallowed_jittering)))); + + if (is_first) { + first_frag_idx = fragment_indices[board_idx]; + first_TTT = TTT_end_ns; + first_frag_timestamp = frag_timestamp; + } + + if (dump_comb_wvfms) { + shift_time(TTT_ticks, first_TTT, first_frag_timestamp, timestamp, num_nominal_samples_per_wvfm, ini_wvfm_timestamp, end_wvfm_timestamp); + dump_waveforms(prod_wvfms, wvfms, first_frag_idx, board_idx, num_channels, ini_wvfm_timestamp, end_wvfm_timestamp); + first_frag_idx = fragment_indices[board_idx]; + first_TTT = TTT_end_ns; + first_frag_timestamp = frag_timestamp; + if (fdebug_jittering) std::cout << "NC - "; + } else { + if (fdebug_jittering) std::cout << "C - "; + } + append_waveforms(wvfms, fragment_wvfms, num_channels); + + if (last_one) { + if (fdebug_extended_fragments) std::cout << "\t\t LAST fragment " << std::endl; + shift_time(TTT_ticks, first_TTT, first_frag_timestamp, timestamp, num_nominal_samples_per_wvfm, ini_wvfm_timestamp, end_wvfm_timestamp); + dump_waveforms(prod_wvfms, wvfms, first_frag_idx, board_idx, num_channels, ini_wvfm_timestamp, end_wvfm_timestamp); + } + + if (fdebug_jittering) { + if (is_first) { + std::cout << "\t\t FIRST "; + } + if (is_nominal) { + int32_t TTT_ini_ns = TTT_end_ns - static_cast(num_nominal_samples_per_wvfm * fns_per_sample); + std::cout << "NOMINAL fragment " << fboard_id_list[board_idx] << " - " << fragment_indices[board_idx] << " (" << num_samples_per_wvfm << "): [" << TTT_ini_ns << ", " << TTT_end_ns << "] diff with prev: " << TTT_dif << " ns, length: " << frag_length << " ns, diff_with_length (jitt): " << J << std::endl; + } else { + int32_t TTT_ini_ns = TTT_end_ns - static_cast(num_nominal_samples_per_wvfm * fns_per_sample); + std::cout << "EXTENDED fragment " << fboard_id_list[board_idx] << " - " << fragment_indices[board_idx] << " (" << num_samples_per_wvfm << "): [" << TTT_ini_ns << ", " << TTT_end_ns << "] diff with prev: " << TTT_dif << " ns, length: " << frag_length << " ns, diff_with_length (jitt): " << J << std::endl; + } + } + + // WARNINGS + if (TTT_dif < frag_length - fallowed_jittering) { + if (!is_first) { + if (!is_nominal) std::cout << "\t\t WARNING: EXTENDED fragment too close! This fragment is being combined." << std::endl; + if (is_nominal) std::cout << "\t\t WARNING: NOMINAL fragment too close! This fragment is not being combined." << std::endl; + } + } + + prev_TTT = TTT_end_ns; + + } else { // Combination of extended fragments disabled. + first_frag_idx = fragment_indices[board_idx]; + shift_time(TTT_ticks, TTT_end_ns, frag_timestamp, timestamp, num_samples_per_wvfm, ini_wvfm_timestamp, end_wvfm_timestamp); + dump_waveforms(prod_wvfms, fragment_wvfms, first_frag_idx, board_idx, num_channels, ini_wvfm_timestamp, end_wvfm_timestamp); + } + } + +} + +// =============== Timing functions =============== // + +/** + * @brief This function shifts the initial and end timestamps of a waveform based on the provided timing information. + * + * @param[in] TTT_ticks The trigger time tag in ticks from the CAEN V1740 header. + * @param[in] TTT_end_ns The end time of the Trigger Time Tag (TTT) in nanoseconds. + * @param[in] frag_timestamp The timestamp of the fragment in nanoseconds. + * @param[in] timestamp The reference timestamp (ETRIG from the SPEC-TDC or HLT from the PTB) in nanoseconds. + * @param[in] num_samples_per_wvfm The number of samples per waveform. + * @param[out] ini_wvfm_timestamp The initial timestamp of the waveform in microseconds (output). + * @param[out] end_wvfm_timestamp The end timestamp of the waveform in microseconds (output). + * + * @details + * This function calculates the initial and end timestamps for a waveform based on the provided timing information. It takes into account + * potential rollovers in the trigger time tag and adjusts the timestamps accordingly. The function supports different timing frames, including + * SPEC-TDC, PTB, and CAEN-only timing. The calculated timestamps are returned in microseconds. + * + * @see get_spec_tdc_etrig_timestamp + * @see get_ptb_hlt_timestamp + */ +void sbndaq::SBNDXARAPUCADecoder::shift_time(uint64_t TTT_ticks, int64_t TTT_end_ns, uint64_t frag_timestamp, uint64_t timestamp, uint32_t num_samples_per_wvfm, double& ini_wvfm_timestamp, double& end_wvfm_timestamp) { + + int64_t pulse_duration_ns = num_samples_per_wvfm * fns_per_sample; // ns. + int64_t frag_timestamp_s = frag_timestamp / NANOSEC_IN_SEC; // s. + int64_t frag_timestamp_ns = frag_timestamp % NANOSEC_IN_SEC; // ns. + + // Gets the full TTT timestamp. + uint64_t full_TTT = 0; + // If the fragment timestamp is greater than the TTT end timestamp, it means that rollover occurred. + if (frag_timestamp_ns > TTT_end_ns) { + if (fverbose | fdebug_timing) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: CAEN TTT rollover occurred w.r.t. the fragment timestamp (FTS)." << std::endl; + full_TTT = (frag_timestamp_s + 1) * NANOSEC_IN_SEC + TTT_end_ns; + } else { + full_TTT = frag_timestamp_s * NANOSEC_IN_SEC + TTT_end_ns; + } + + int64_t ref_timestamp = 0; + + // If an ETRIG or HLT timestamp was found it restarts the time from it. Otherwise the CAEN time frame is assigned. + if (factive_timing_frame != CAEN_ONLY_TIMING) { + ref_timestamp = signed_difference(full_TTT, timestamp); // ns. + + ini_wvfm_timestamp = (ref_timestamp - pulse_duration_ns) * NANOSEC_TO_MICROSEC; // us. + end_wvfm_timestamp = ref_timestamp * NANOSEC_TO_MICROSEC; // us. + } else { + ref_timestamp = full_TTT; // ns. + + ini_wvfm_timestamp = ((ref_timestamp - pulse_duration_ns) % NANOSEC_IN_SEC) * NANOSEC_TO_MICROSEC; // ns. + end_wvfm_timestamp = (ref_timestamp % NANOSEC_IN_SEC) * NANOSEC_TO_MICROSEC; // us. + } + + if (fdebug_timing) { + std::cout << std::fixed << std::setprecision(0); + std::cout << "\t\t ns/tick = " << NANOSEC_PER_TICK << ", ns/sample = " << fns_per_sample << std::endl; + std::cout << "\t\t TTT header.TriggerTime() [TTT_ticks] = " << TTT_ticks << " ticks. \t TTT_end_ns = " << print_timestamp(TTT_end_ns) << "." << std::endl; + std::cout << "\t\t Full Fragment timestamp: " << print_timestamp(frag_timestamp) << " = " << frag_timestamp_s << " s " << frag_timestamp_ns << " ns." << std::endl; + std::cout << "\t\t Full TTT - fragment timestamp = "<< abs_difference(full_TTT, frag_timestamp) << " ns." << " Post-percent: " << (double(abs_difference(full_TTT, frag_timestamp)) / double(pulse_duration_ns)) * 100 << "%." << std::endl; + if (factive_timing_frame == SPEC_TDC_TIMING) { + std::cout << "\t ETRIG (SPEC-TDC) timestamp of the fragment: " << std::endl; + std::cout << "\t\t Full UTC ETRIG timestamp: " << print_timestamp(timestamp) << "." << std::endl; + std::cout << "\t\t ETRIG SPEC-TDC difference applied to the CAEN frame (full timestamps): " << print_timestamp(full_TTT) << " - " << print_timestamp(timestamp) << " = " << ref_timestamp << " ns." << std::endl; + } else if (factive_timing_frame == PTB_TIMING) { + std::cout << "\t HLT ETRIG (PTB) timestamp of the fragment: " << std::endl; + std::cout << "\t\t Full UTC HLT ETRIG timestamp: " << print_timestamp(timestamp) << "." << std::endl; + std::cout << "\t\t HLT ETRIG (PTB) difference applied to the CAEN frame (full timestamps): " << print_timestamp(full_TTT) << " - " << print_timestamp(timestamp) << " = " << ref_timestamp << " ns." << std::endl; + } else if (factive_timing_frame == CAEN_ONLY_TIMING) { + std::cout << "\t CAEN trigger timestamp (TTT) of the fragment: " << std::endl; + std::cout << "\t\t Full UTC TTT timestamp: " << print_timestamp(full_TTT) << " = " << full_TTT / NANOSEC_IN_SEC << " s " << TTT_end_ns << " ns." << std::endl; + } + } + + if (fverbose | fdebug_timing) { + std::cout << std::fixed << std::setprecision(3); + if (factive_timing_frame == SPEC_TDC_TIMING) { + std::cout << " > SBNDXARAPUCADecoder::decode_fragment: SPEC-TDC time window of " << end_wvfm_timestamp - ini_wvfm_timestamp << " us: [" << ini_wvfm_timestamp << ", " << end_wvfm_timestamp << "] us." << std::endl; + } else if (factive_timing_frame == PTB_TIMING) { + std::cout << " > SBNDXARAPUCADecoder::decode_fragment: PTB time window of " << end_wvfm_timestamp - ini_wvfm_timestamp << " us: [" << ini_wvfm_timestamp << ", " << end_wvfm_timestamp << "] us." << std::endl; + } else { // CAEN_ONLY_TIMING + std::cout << " > SBNDXARAPUCADecoder::decode_fragment: CAEN time window of " << end_wvfm_timestamp - ini_wvfm_timestamp << " us: [" << ini_wvfm_timestamp << ", " << end_wvfm_timestamp << "] us." << std::endl; + } + std::cout << " > SBNDXARAPUCADecoder::decode_fragment: TTT_end_ticks = " << TTT_ticks << " ticks. \t TTT_end_ns = " << print_timestamp(TTT_end_ns) << "." << std::endl; + } +} + +/** + * @brief Searches for the SPEC-TDC ETRIG timestamp closest to the raw timestamp if any SPEC-TDC ETRIG product is found in the event. + * @param[in] e The event to be processed. + * @param[in] corr_raw_timestamp The corrected raw timestamp from the artdaq::RawEventHeader product. + * @param[in,out] timestamp The closest ETRIG timestamp to the raw timestamp (if found). + * @return A boolean indicating if a valid ETRIG timestamp was found close enough to the raw timestamp. + * @details It searches for the SPEC-TDC products in the event and looks for the ETRIG timestamps. If any ETRIG + * timestamp is found, it checks which one is the closest to the raw timestamp and if it is close enough (i.e. + * within fraw_trig_max_diff) it returns it as output. + */ bool sbndaq::SBNDXARAPUCADecoder::get_spec_tdc_etrig_timestamp(art::Event& e, uint64_t corr_raw_timestamp, uint64_t& timestamp) { bool ett_found = false; @@ -565,238 +880,227 @@ bool sbndaq::SBNDXARAPUCADecoder::get_ptb_hlt_timestamp(art::Event& e, uint64_t return hlt_found; } +// =============== Decodes the waveforms =============== // + /** - * @brief This function processes a single fragment from a CAEN V1740 and stores the decoded waveforms in the provided product - * container. + * @brief Decodes the waveforms from a CAEN V1740 fragment (binary decoding stage). * - * @param[in,out] fragment_indices A 1D vector tracking the number of fragments processed for each board. * @param[in] fragment The input CAEN V1740 fragment containing raw data to be decoded. - * @param[out] prod_wvfms Vector where the decoded waveforms will be stored as raw::OpDetWaveform objects. - * - * @details - * - Identifies the board index corresponding to the fragment ID. - * - Verifies the fragment ID against known boards and ensures it is within a valid range. - * - - * - Decodes the fragment reading raw 32-bit words from the fragment, storing them in a buffer, - * and extracting 12-bit samples. The decoding includes: - * - Accessing metadata for the number of channels and samples. - * - Getting the initial timestamp with respect to the selected timing frame. - * - Binary decoding of the raw waveform. To assign efficiently each sequential sample extracted. These indices formulas are - * applied: - * - The board channel index: - * \f[ - * c = \left( \frac{S}{3} \mod 8 \right) + g \times 8 - * \f] - * - The channel sample index: - * \f[ - * s = (S \mod 3) + \left( \frac{S}{24} \times 3 \right) \mod s_{w}} - * \f] - * Where the group index is computed as \f$ \frac{S}{s_{g}} \f$. - * - Mapping samples to corresponding channels. - * - Populates the output vector (`prod_wvfms`) with decoded waveforms and optionally generates debug waveforms output. + * @param[out] wvfms A 2D vector where the decoded waveforms will be stored. Each inner vector corresponds to a channel's waveform. + * @param[in] header_size The size of the event header in bytes. + * @param[in] num_channels The number of channels in the fragment. + * @param[in] num_samples_per_wvfm The number of samples per waveform for each channel. + * @param[in] num_words_per_wvfms The total number of 32-bit words containing waveform data in the fragment. + * @param[in] num_samples_per_group The number of samples per group of channels (8 channels per group). * + * @details This function reads raw 32-bit words from the fragment, storing them in a buffer, and extracts 12-bit samples. + * It assigns each sample to the appropriate channel and sample index based on its position in the sequence. The decoding process includes: + * - Initializing a buffer to hold incoming data and tracking the number of bits currently stored. + * - Iterating over each 32-bit word in the waveform data section of the fragment, adding it to the buffer. + * - Extracting 12-bit samples from the buffer as long as there are enough bits available, and assigning them to their respective channels and sample indices using calculated formulas. + * The function ensures that all samples are correctly mapped to their channels, taking into account the grouping of channels and the interleaving of samples. + * @note The function assumes that the input fragment is valid and contains the expected structure for a CAEN V1740 device. + * @see SBN Document 38475-v1 for more details on the binary decoding. */ -void sbndaq::SBNDXARAPUCADecoder::decode_fragment(uint64_t timestamp, std::vector & fragment_indices, const artdaq::Fragment& fragment, std::vector & prod_wvfms) { - auto fragment_id = fragment.fragmentID() - ffragment_id_offset; - auto it = std::find(fboard_id_list.begin(), fboard_id_list.end(), fragment_id); - size_t board_idx; - bool valid_fragment = false; - - if (it != fboard_id_list.end()) { - board_idx = it - fboard_id_list.begin(); - if (board_idx >= fnum_caen_boards) { - if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: fragment ID " << fragment_id << " (" << board_idx << ") is out of range. Skipping this fragment..." << std::endl; - } else { - valid_fragment = true; +void sbndaq::SBNDXARAPUCADecoder::decode_waveforms(const artdaq::Fragment& fragment, std::vector>& wvfms, size_t header_size, uint32_t num_channels, uint32_t num_samples_per_wvfm, uint32_t num_words_per_wvfms, uint32_t num_samples_per_group) { + // =============== Start decoding the waveforms =============== // + if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: binary decoding of the waveforms starting... " << std::endl; + + // Absolute sample number [0, TOTAL_NUM_SAMPLES] where TOTAL_NUM_SAMPLES is the total number of samples stored for an event. + uint32_t S = 0; + // Buffer variables. + uint64_t buffer = 0; + uint32_t bits_in_buffer = 0; + + // Data pointer to the beggining of the waveforms stores in the event. + const uint32_t* data_ptr = reinterpret_cast(fragment.dataBeginBytes() + header_size); + // Accesses each word, stores it in the buffer and then the samples are extracted from the buffer. + for (size_t j = 0; j < num_words_per_wvfms; j++) { + uint64_t word = read_word(data_ptr); + + // Adds the new word to the buffer and increments the number of bits stored in it. + if (fdebug_buffer) std::cout << buffer << "[word: " << word << "]" << std::endl; + buffer |= word << bits_in_buffer; + bits_in_buffer += BITS_PER_WORD; // bytes * 8 bits/byte + if (fdebug_buffer) std::cout << " +" << buffer << " [bits in buffer: "<< bits_in_buffer << "]" << std::endl; + + // Obtains 12-bit sequences from the buffer and assigns each sample to the channel and channel sample it belongs to. + while (bits_in_buffer >= BITS_PER_SAMPLE) { + // Computes board channel, channel sample and group channel and assigns the sample to those indices. + uint32_t g = (S / num_samples_per_group); // Group index. + uint32_t c = ((S / NUM_CONSECUTIVE_SAMPLES) % NUM_CHANNELS_PER_GROUP) + g * NUM_GROUPS; // Channel index. + uint32_t s = (S % NUM_CONSECUTIVE_SAMPLES) + ((S / NUM_SAMPLES_PER_ROUND) * NUM_CONSECUTIVE_SAMPLES) % num_samples_per_wvfm; // Sample/channel index. + uint16_t sample = get_sample(buffer, BITS_PER_SAMPLE - 1, 0); + wvfms[c][s] = sample; + if (fdebug_waveforms) std::cout << "\tSample: " << sample << "\tg: " << g << "\tch: " << c << "\ts:" << s << "\tS: " << S << std::endl; + + // Updates the buffer status removing the read bits and decreasing the number of bits stored in it. + buffer >>= BITS_PER_SAMPLE; + bits_in_buffer -= BITS_PER_SAMPLE; + if (fdebug_buffer) std::cout << " -" << buffer << " [bits in buffer: "<< bits_in_buffer << "]" << std::endl; + + // Increments the absolute sample step. + S++; } - } else { - if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: fragment ID " << fragment_id << " is not valid. Skipping this fragment..." << std::endl; } +} - if (valid_fragment) { - if (fverbose) std::cout << "\n > SBNDXARAPUCADecoder::decode_fragment: decoding V1740 CAEN fragment " << fragment_indices[board_idx] << " from the board " << board_idx << " (slot " << fboard_id_list[board_idx] << "):" << std::endl; - - // =============== Accesses Event metadata and Event header for this fragment =============== // - - CAENV1740Fragment caen_fragment(fragment); - CAENV1740FragmentMetadata const* metadata = caen_fragment.Metadata(); - uint32_t num_channels = metadata->nChannels; - if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: number of channels: " << num_channels << std::endl; - - // Accesses the event and header data of the CAEN fragment. - CAENV1740Event const* event = caen_fragment.Event(); - CAENV1740EventHeader header = event->Header; - - // Gets the number of words of the header and the waveforms. - uint32_t num_words_per_event = header.eventSize; - uint32_t num_words_per_header = sizeof(CAENV1740EventHeader) / sizeof(uint32_t); - uint32_t num_words_per_wvfms = (num_words_per_event - num_words_per_header); - - uint32_t num_bits_per_all_wvfms = num_words_per_wvfms * BITS_PER_WORD; - uint32_t num_samples_per_all_wvfms = num_bits_per_all_wvfms / BITS_PER_SAMPLE; - uint32_t num_remaining_bits = num_bits_per_all_wvfms % BITS_PER_SAMPLE; - uint32_t num_samples_per_wvfm = num_samples_per_all_wvfms / num_channels; - uint32_t num_samples_per_group = num_samples_per_wvfm * NUM_CHANNELS_PER_GROUP; - - if (fverbose | fdebug_waveforms) { - if (metadata->nSamples == num_samples_per_wvfm) { - std::cout << " > SBNDXARAPUCADecoder::decode_fragment: [NOMINAL FRAGMENT] " << num_samples_per_wvfm << " samples/waveform." << " (" << num_samples_per_group << " samples per group - 8 channels per group -)." << std::endl; - } else { - std::cout << " > SBNDXARAPUCADecoder::decode_fragment: [EXTENDED FRAGMENT] " << num_samples_per_wvfm << " samples/waveform." << " (" << num_samples_per_group << " samples per group - 8 channels per group -)." << std::endl; - } - std::cout << " > SBNDXARAPUCADecoder::decode_fragment: nominal number of samples per waveform: " << metadata->nSamples << "." << std::endl; - std::cout << " > SBNDXARAPUCADecoder::decode_fragment: number of words for this fragment: " << num_words_per_event << " (Header: " << num_words_per_header << ", Waveform: " << num_words_per_wvfms << ") words." << std::endl; - } - - if (fdebug_waveforms) { - std::cout << "\t Number of bits for all the waveforms of this fragment: " << BITS_PER_WORD << "\t" << num_bits_per_all_wvfms << std::endl; - std::cout << "\t Number of samples for all the waveforms of this fragment: " << num_samples_per_all_wvfms << std::endl; - std::cout << "\t Number of remaining bits for this fragment: " << num_remaining_bits << std::endl; - std::cout << "\t Number of samples per wvfm (this fragment): " << num_samples_per_wvfm << std::endl; - std::cout << "\t Number of samples per group (this fragment): " << num_samples_per_group << std::endl; - } +/** + * @brief Extract a sample from a 64-bit buffer using the specified bit positions. + * + * @param[in] buffer An unsigned 64-bit integer which represents a temporal buffer for the read words and where the samples are extracted from. + * @param[in] msb An unsigned 32-bit integer representing the most significative bit (MSB) where the readout from the buffer paramter. + * @param[in] lsb An unsigned 32-bit integer representing the less significative bit (LSB) from we end read + * + * @details The function shifts the buffer to the right by the number of positions specified by `lsb` so that the least significant bit of the + * sample aligns with bit 0. It then applies a mask to isolate the bits between `lsb` and `msb`, inclusive. + * + * @return The extracted sample as a 16-bit unsigned integer. + */ +uint16_t sbndaq::SBNDXARAPUCADecoder::get_sample(uint64_t buffer, uint32_t msb, uint32_t lsb) { + uint64_t mask = (1U << (msb - lsb + 1)) - 1; + uint64_t sample = buffer >> lsb; + return sample & mask; +} - // =============== Extracts timing information for this fragment =============== // +/** + * @brief Read a 32-bit word from the data pointer and advances the pointer. + * + * @param[in, out] data_ptr A reference to a pointer pointing to the current position in the data. + * + * @details This function retrieves a 32-bit word from the memory location pointed to by `data_ptr`. After reading, it advances `data_ptr` to + * the next 32-bit word location. + * + * @return The 32-bit word read from the location pointed to by `data_ptr`. + */ +uint32_t sbndaq::SBNDXARAPUCADecoder::read_word(const uint32_t* & data_ptr) { + uint32_t word = *data_ptr; + data_ptr += 1; + return word; +} - // Gets the timing information of the CAEN fragment. - int64_t pulse_duration_ns = num_samples_per_wvfm * fns_per_sample; // ns. - uint64_t frag_timestamp = fragment.timestamp(); // ns. - int64_t frag_timestamp_s = frag_timestamp / NANOSEC_IN_SEC; // s. - int64_t frag_timestamp_ns = frag_timestamp % NANOSEC_IN_SEC; // ns. +/** + * @brief Generates a unique global channel identifier using the board slot and the channel number of that board. + * + * @param[in] board Index of the board in `fboard_id_list` from which to derive the board slot. + * @param[in] board_channel The specific channel number on the given board. + * + * @details This function computes a `channel_id` by combining the board slot and the specific + * channel number on that board. + * The unique identifier `channel_id` (\f$ CH_{ID} $\f) is computed as follows: + * \f[ + * CH\_{ID} = B\_{ID} \times 100 + CH\_{B} + * \f] + * + * Where: + * - \f$ B\_{ID} \f$ is the fragment ID retrieved from `fboard_id_list` based on the slot. + * - \f$ CH\_B \f$ is the channel number on that board. + * + * @return A unique identifier for the specified channel as an unsigned integer. + */ +unsigned int sbndaq::SBNDXARAPUCADecoder::get_channel_id(unsigned int board, unsigned int board_channel) { + unsigned int channel_id = fboard_id_list[board] * 100 + board_channel; + return channel_id; +} - uint32_t TTT_ticks = header.triggerTime(); - int64_t TTT_end_ns = TTT_ticks * NANOSEC_PER_TICK; // ns. +// =============== Combines the waveforms from extended fragments =============== // - // Gets the full TTT timestamp. - uint64_t full_TTT = 0; - // If the fragment timestamp is greater than the TTT end timestamp, it means that rollover occurred. - if (frag_timestamp_ns > TTT_end_ns) { - if (fverbose | fdebug_timing) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: CAEN TTT rollover occurred w.r.t. the fragment timestamp (FTS)." << std::endl; - full_TTT = (frag_timestamp_s + 1) * NANOSEC_IN_SEC + TTT_end_ns; - } else { - full_TTT = frag_timestamp_s * NANOSEC_IN_SEC + TTT_end_ns; +/** + * @brief Combines the waveforms from the current fragment with the previously stored waveforms. + * @param[in,out] wvfms A 2D vector containing the (combined if it was needed before) waveforms. + * @param[in] fragment_wvfms A 2D vector containing the waveforms from the current fragment to be combined. + * @param[in] num_channels The number of channels per board. + * + * @details + * The function performs the following steps: + * 1. Checks if the `wvfms` vector is empty. If it is, it resizes it to accommodate `num_channels` channels. + * 2. Iterates over each channel from 0 to `num_channels - 1`. + * 3. For each channel, it appends the samples from `fragment_wvfms` to the corresponding channel in `wvfms`. + * + * This approach ensures that waveforms from multiple fragments are concatenated correctly, maintaining the order of samples for each channel. + * + * @pre The `fragment_wvfms` vector should contain waveforms for all channels of the board being processed. + * @pre The `wvfms` vector should be either empty or already contain waveforms for all channels of the board being processed. + */ +void sbndaq::SBNDXARAPUCADecoder::append_waveforms(std::vector>& wvfms, const std::vector>& fragment_wvfms, uint32_t num_channels) { + if (fdebug_extended_fragments) std::cout << " > SBNDXARAPUCADecoder::append_waveforms: combining waveforms from extended fragments..." << std::endl; + if (wvfms.empty()) { + if (fdebug_extended_fragments) { + std::cout << "\t\t Empty waveforms, resizing to " << num_channels << " channels." << std::endl; } - - int64_t ref_timestamp = 0; - - double ini_wvfm_timestamp = 0; - double end_wvfm_timestamp = 0; - - // If an ETRIG or HLT timestamp was found it restarts the time from it. Otherwise the CAEN time frame is assigned. - if (factive_timing_frame != CAEN_ONLY_TIMING) { - ref_timestamp = signed_difference(full_TTT, timestamp); // ns. - - ini_wvfm_timestamp = (ref_timestamp - pulse_duration_ns) * NANOSEC_TO_MICROSEC; // us. - end_wvfm_timestamp = ref_timestamp * NANOSEC_TO_MICROSEC; // us. - } else { - ref_timestamp = full_TTT; // ns. - - ini_wvfm_timestamp = ((ref_timestamp - pulse_duration_ns) % NANOSEC_IN_SEC) * NANOSEC_TO_MICROSEC; // ns. - end_wvfm_timestamp = (ref_timestamp % NANOSEC_IN_SEC) * NANOSEC_TO_MICROSEC; // us. + wvfms.resize(num_channels); + if (fverbose | fdebug_extended_fragments) { + std::cout << "\t\t Waveforms size BEFORE combining = " << wvfms.size() << " x " << wvfms[0].size() << std::endl; } - - if (fdebug_timing) { - std::cout << std::fixed << std::setprecision(0); - std::cout << "\t\t ns/tick = " << NANOSEC_PER_TICK << ", ns/sample = " << fns_per_sample << std::endl; - std::cout << "\t\t TTT header.TriggerTime() [TTT_ticks] = " << TTT_ticks << " ticks. \t TTT_end_ns = " << print_timestamp(TTT_end_ns) << "." << std::endl; - std::cout << "\t\t TTT header.extendedTriggerTime() [TTT_ticks] = " << header.extendedTriggerTime() << " ticks. \t TTT_end_ns = " << print_timestamp(header.extendedTriggerTime() * NANOSEC_PER_TICK) << "." << std::endl; - std::cout << "\t\t TTT header.triggerTimeRollOver(): " << header.triggerTimeRollOver() << std::endl; - std::cout << "\t\t Full Fragment timestamp: " << print_timestamp(frag_timestamp) << " = " << frag_timestamp_s << " s " << frag_timestamp_ns << " ns." << std::endl; - std::cout << "\t\t Full TTT - fragment timestamp = "<< abs_difference(full_TTT, frag_timestamp) << " ns." << " Post-percent: " << (double(abs_difference(full_TTT, frag_timestamp)) / double(pulse_duration_ns)) * 100 << "%." << std::endl; - if (factive_timing_frame == SPEC_TDC_TIMING) { - std::cout << "\t ETRIG (SPEC-TDC) timestamp of the fragment: " << std::endl; - std::cout << "\t\t Full UTC ETRIG timestamp: " << print_timestamp(timestamp) << "." << std::endl; - std::cout << "\t\t ETRIG SPEC-TDC difference applied to the CAEN frame (full timestamps): " << print_timestamp(full_TTT) << " - " << print_timestamp(timestamp) << " = " << ref_timestamp << " ns." << std::endl; - } else if (factive_timing_frame == PTB_TIMING) { - std::cout << "\t HLT ETRIG (PTB) timestamp of the fragment: " << std::endl; - std::cout << "\t\t Full UTC HLT ETRIG timestamp: " << print_timestamp(timestamp) << "." << std::endl; - std::cout << "\t\t HLT ETRIG (PTB) difference applied to the CAEN frame (full timestamps): " << print_timestamp(full_TTT) << " - " << print_timestamp(timestamp) << " = " << ref_timestamp << " ns." << std::endl; - } else if (factive_timing_frame == CAEN_ONLY_TIMING) { - std::cout << "\t CAEN trigger timestamp (TTT) of the fragment: " << std::endl; - std::cout << "\t\t Full UTC TTT timestamp: " << print_timestamp(full_TTT) << " = " << full_TTT / NANOSEC_IN_SEC << " s " << TTT_end_ns << " ns." << std::endl; - } + } + for (uint32_t ch = 0; ch < num_channels; ch++) { + wvfms[ch].insert(wvfms[ch].end(), fragment_wvfms[ch].begin(), fragment_wvfms[ch].end()); + } + if (fverbose | fdebug_extended_fragments) { + std::cout << "\t\t Waveforms size AFTER combining = " << wvfms.size() << " x " << wvfms[0].size() << std::endl; } +} - if (fverbose | fdebug_timing) { - std::cout << std::fixed << std::setprecision(3) << std::endl; - if (factive_timing_frame == SPEC_TDC_TIMING) { - std::cout << " > SBNDXARAPUCADecoder::decode_fragment: SPEC-TDC time window of " << end_wvfm_timestamp - ini_wvfm_timestamp << " us: [" << ini_wvfm_timestamp << ", " << end_wvfm_timestamp << "] us." << std::endl; - } else if (factive_timing_frame == PTB_TIMING) { - std::cout << " > SBNDXARAPUCADecoder::decode_fragment: PTB time window of " << end_wvfm_timestamp - ini_wvfm_timestamp << " us: [" << ini_wvfm_timestamp << ", " << end_wvfm_timestamp << "] us." << std::endl; - } else { // CAEN_ONLY_TIMING - std::cout << " > SBNDXARAPUCADecoder::decode_fragment: CAEN time window of " << end_wvfm_timestamp - ini_wvfm_timestamp << " us: [" << ini_wvfm_timestamp << ", " << end_wvfm_timestamp << "] us." << std::endl; - } - } +// =============== Dumps the decoded waveforms =============== // - // =============== Start decoding the waveforms =============== // - if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: binary decoding of the waveforms starting... " << std::endl; - - std::vector > wvfms(num_channels, std::vector(num_samples_per_wvfm, 0)); - - // Absolute sample number [0, TOTAL_NUM_SAMPLES] where TOTAL_NUM_SAMPLES is the total number of samples stored for an event. - uint32_t S = 0; - // Buffer variables. - uint64_t buffer = 0; - uint32_t bits_in_buffer = 0; - - // Data pointer to the beggining of the waveforms stores in the event. - const uint32_t* data_ptr = reinterpret_cast(fragment.dataBeginBytes() + sizeof(CAENV1740EventHeader)); - // Accesses each word, stores it in the buffer and then the samples are extracted from the buffer. - for (size_t j = 0; j < num_words_per_wvfms; j++) { - uint64_t word = read_word(data_ptr); - - // Adds the new word to the buffer and increments the number of bits stored in it. - if (fdebug_buffer) std::cout << buffer << "[word: " << word << "]" << std::endl; - buffer |= word << bits_in_buffer; - bits_in_buffer += BITS_PER_WORD; // bytes * 8 bits/byte - if (fdebug_buffer) std::cout << " +" << buffer << " [bits in buffer: "<< bits_in_buffer << "]" << std::endl; - - // Obtains 12-bit sequences from the buffer and assigns each sample to the channel and channel sample it belongs to. - while (bits_in_buffer >= BITS_PER_SAMPLE) { - // Computes board channel, channel sample and group channel and assigns the sample to those indices. - uint32_t g = (S / num_samples_per_group); // Group index. - uint32_t c = ((S / NUM_CONSECUTIVE_SAMPLES) % NUM_CHANNELS_PER_GROUP) + g * NUM_GROUPS; // Channel index. - uint32_t s = (S % NUM_CONSECUTIVE_SAMPLES) + ((S / NUM_SAMPLES_PER_ROUND) * NUM_CONSECUTIVE_SAMPLES) % num_samples_per_wvfm; // Sample/channel index. - uint16_t sample = get_sample(buffer, BITS_PER_SAMPLE - 1, 0); - wvfms[c][s] = sample; - if (fdebug_waveforms) std::cout << "\tSample: " << sample << "\tg: " << g << "\tch: " << c << "\ts:" << s << "\tS: " << S << std::endl; - - // Updates the buffer status removing the read bits and decreasing the number of bits stored in it. - buffer >>= BITS_PER_SAMPLE; - bits_in_buffer -= BITS_PER_SAMPLE; - if (fdebug_buffer) std::cout << " -" << buffer << " [bits in buffer: "<< bits_in_buffer << "]" << std::endl; - - // Increments the absolute sample step. - S++; - } - } - - // The decoded waveforms are dumped into two products: - // - A xarapucadecoder-art.root file with the OpDetWaveforms as the product of this producer for further analysis. - // - A decoder_hist.root file gathering a waveform histograms. - if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: binary decoding complete, dumping products..." << std::endl; +/** + * @brief Dump products into a `raw::OpDetWaveform` object and into a debug histogram file. + * @param[in,out] prod_wvfms A reference to the vector where the produced `raw::OpDetWaveform` objects are dumped into products. + * @param[in,out] wvfms A 2D vector containing the (combined if needed) waveforms. + * @param[in,out] fragment_indices A reference to a vector keeping track of the number of fragments decoded per board. + * @param[in] board_idx The board index (position in the list of boards). + * @param[in] num_channels The number of channels per board. + * @param[in] ini_wvfm_timestamp The initial timestamp of the waveform in microseconds. + * @param[in] end_wvfm_timestamp The final timestamp of the waveform in microseconds. + * + * @details + * The function performs the following steps: + * 1. Determines the number of debug waveforms to be stored based on the configuration parameter `fstore_debug_waveforms`. + * - If `fstore_debug_waveforms` is set to -1, all channels are considered for debug storage. + * - Otherwise, it takes the minimum between `num_channels` and `fstore_debug_waveforms`. + * 2. Iterates over each channel up to the determined number of debug waveforms: + * - Calls `save_prod_wvfm` to convert and store the waveform in the products. + * - Calls `save_debug_wvfm` to save the waveform as a histogram for debugging purposes. + * 3. For any remaining channels beyond the debug limit, it only calls `save_prod_wvfm` to store the waveform in the products. + * 4. Clears the `wvfms` vector to free up memory after processing. + * + * @pre ini_wvfm_timestamp and end_wvfm_timestamp is assumed to be given in microseconds when the timing frame is not CAEN_ONLY_TIMING. + * @pre The `wvfms` vector should contain waveforms for all channels of the board specified by `board_idx`. + * @pre The `fragment_indices` vector should have been initialized with a size equal to the number of boards being processed. + * @pre The `prod_wvfms` vector should be ready to accept new `raw::OpDetWaveform` objects. + * + * @see raw::OpDetWaveform + * @see save_prod_wvfm + * @see save_debug_wvfm + */ +void sbndaq::SBNDXARAPUCADecoder::dump_waveforms(std::vector & prod_wvfms, std::vector>& wvfms, size_t first_frag_idx, size_t board_idx, uint32_t num_channels, double ini_wvfm_timestamp, double end_wvfm_timestamp) { - uint32_t num_debug_wvfms; - - if (fstore_debug_waveforms == -1) { - num_debug_wvfms = num_channels; - } else { - num_debug_wvfms = std::min(num_channels, fstore_debug_waveforms); - } + // The decoded waveforms are dumped into two products: + // - A xarapucadecoder-art.root file with the OpDetWaveforms as the product of this producer for further analysis. + // - A decoder_hist.root file gathering a waveform histograms. + if (fverbose) std::cout << " > SBNDXARAPUCADecoder::decode_fragment: binary decoding complete, dumping products..." << std::endl; + + uint32_t num_debug_wvfms; - uint32_t ch; + if (fstore_debug_waveforms == -1) { + num_debug_wvfms = num_channels; + } else { + num_debug_wvfms = std::min(num_channels, fstore_debug_waveforms); + } - for (ch = 0; ch < num_debug_wvfms; ch++) { - save_prod_wvfm(board_idx, ch, ini_wvfm_timestamp, wvfms, prod_wvfms); - save_debug_wvfm(board_idx, fragment_indices[board_idx], ch, ini_wvfm_timestamp, end_wvfm_timestamp, wvfms); - } + uint32_t ch; - for (;ch < num_channels; ch++) { - save_prod_wvfm(board_idx, ch, ini_wvfm_timestamp, wvfms, prod_wvfms); - } + for (ch = 0; ch < num_debug_wvfms; ch++) { + save_prod_wvfm(board_idx, ch, ini_wvfm_timestamp, wvfms, prod_wvfms); + save_debug_wvfm(board_idx, first_frag_idx, ch, ini_wvfm_timestamp, end_wvfm_timestamp, wvfms); + } - fragment_indices[board_idx]++; + for (;ch < num_channels; ch++) { + save_prod_wvfm(board_idx, ch, ini_wvfm_timestamp, wvfms, prod_wvfms); } + + wvfms.clear(); + } /** @@ -877,63 +1181,7 @@ void sbndaq::SBNDXARAPUCADecoder::save_debug_wvfm(size_t board_idx, size_t fragm } -/** - * @brief Extract a sample from a 64-bit buffer using the specified bit positions. - * - * @param[in] buffer An unsigned 64-bit integer which represents a temporal buffer for the read words and where the samples are extracted from. - * @param[in] msb An unsigned 32-bit integer representing the most significative bit (MSB) where the readout from the buffer paramter. - * @param[in] lsb An unsigned 32-bit integer representing the less significative bit (LSB) from we end read - * - * @details The function shifts the buffer to the right by the number of positions specified by `lsb` so that the least significant bit of the - * sample aligns with bit 0. It then applies a mask to isolate the bits between `lsb` and `msb`, inclusive. - * - * @return The extracted sample as a 16-bit unsigned integer. - */ -uint16_t sbndaq::SBNDXARAPUCADecoder::get_sample(uint64_t buffer, uint32_t msb, uint32_t lsb) { - uint64_t mask = (1U << (msb - lsb + 1)) - 1; - uint64_t sample = buffer >> lsb; - return sample & mask; -} - -/** - * @brief Read a 32-bit word from the data pointer and advances the pointer. - * - * @param[in, out] data_ptr A reference to a pointer pointing to the current position in the data. - * - * @details This function retrieves a 32-bit word from the memory location pointed to by `data_ptr`. After reading, it advances `data_ptr` to - * the next 32-bit word location. - * - * @return The 32-bit word read from the location pointed to by `data_ptr`. - */ -uint32_t sbndaq::SBNDXARAPUCADecoder::read_word(const uint32_t* & data_ptr) { - uint32_t word = *data_ptr; - data_ptr += 1; - return word; -} - -/** - * @brief Generates a unique global channel identifier using the board slot and the channel number of that board. - * - * @param[in] board Index of the board in `fboard_id_list` from which to derive the board slot. - * @param[in] board_channel The specific channel number on the given board. - * - * @details This function computes a `channel_id` by combining the board slot and the specific - * channel number on that board. - * The unique identifier `channel_id` (\f$ CH_{ID} $\f) is computed as follows: - * \f[ - * CH\_{ID} = B\_{ID} \times 100 + CH\_{B} - * \f] - * - * Where: - * - \f$ B\_{ID} \f$ is the fragment ID retrieved from `fboard_id_list` based on the slot. - * - \f$ CH\_B \f$ is the channel number on that board. - * - * @return A unique identifier for the specified channel as an unsigned integer. - */ -unsigned int sbndaq::SBNDXARAPUCADecoder::get_channel_id(unsigned int board, unsigned int board_channel) { - unsigned int channel_id = fboard_id_list[board] * 100 + board_channel; - return channel_id; -} +// ==================== Auxiliary functions ==================== // /** * @brief Returns the signed difference between two timestamps (t1 - t2). @@ -974,11 +1222,11 @@ uint64_t sbndaq::SBNDXARAPUCADecoder::abs_difference(uint64_t t1, uint64_t t2) { } /** -* @brief Formats a timestamp in nanoseconds into a easily readable string format. -* @param[in] timestamp The timestamp in nanoseconds to be formatted. -* @return A string representation of the timestamp in the format "(seconds)nanoseconds ns". -* @details The function divides the input timestamp by 1,000,000,000 to obtain the seconds component and uses the modulus operator to get the remaining nanoseconds. -*/ + * @brief Formats a timestamp in nanoseconds into a easily readable string format. + * @param[in] timestamp The timestamp in nanoseconds to be formatted. + * @return A string representation of the timestamp in the format "(seconds)nanoseconds ns". + * @details The function divides the input timestamp by 1,000,000,000 to obtain the seconds component and uses the modulus operator to get the remaining nanoseconds. + */ std::string sbndaq::SBNDXARAPUCADecoder::print_timestamp(uint64_t timestamp) { return "(" + std::to_string(timestamp / NANOSEC_IN_SEC) + ")" + std::to_string(timestamp % NANOSEC_IN_SEC) + " ns"; } diff --git a/sbndcode/Decoders/XARAPUCA/xarapucadecoder.fcl b/sbndcode/Decoders/XARAPUCA/xarapucadecoder.fcl index 3456450f9..adeb66bd8 100644 --- a/sbndcode/Decoders/XARAPUCA/xarapucadecoder.fcl +++ b/sbndcode/Decoders/XARAPUCA/xarapucadecoder.fcl @@ -33,6 +33,9 @@ xarapucadecoder: waveforms_instance_name: "XARAPUCAChannels" # Name for the instance product containing the raw decoded waveforms. timing_ref_instance_name: "" # Name for the instance product containing the timing reference information. store_debug_waveforms: 0 # Number of waveforms to store (0: none, -1: all, n: first n waveforms). + # Combination of extended fragments. + combine_ext_frag: true # (De)activates the combination of extended fragments into a single raw::OpDetWaveform object. + allowed_jittering: 64 # Allowed jittering (in ns) between fragments to be combined. # - Debug options. debug_tdc_handle: false # (De)activates SPEC-TDC art::Handle information printing. debug_ptb_handle: false # (De)activates PTB art::Handle information printing. @@ -40,6 +43,8 @@ xarapucadecoder: debug_timing: false # (De)activates timing data printing. debug_buffer: false # (De)activates buffer status printing. debug_waveforms: false # (De)activates waveforms decoding printing. + debug_extended_fragments: false # (De)activates extended fragments information printing. + debug_jittering: false # (De)activates trigger jittering information printing. # - Verbose option. verbose: false # (De)activates verbosity. } diff --git a/sbndcode/Geometry/GeometryWrappers/CRTGeoService.h b/sbndcode/Geometry/GeometryWrappers/CRTGeoService.h index 7b76b4fb0..c60b95b80 100644 --- a/sbndcode/Geometry/GeometryWrappers/CRTGeoService.h +++ b/sbndcode/Geometry/GeometryWrappers/CRTGeoService.h @@ -430,6 +430,10 @@ namespace sbnd::crt { bool IsPointInsideCRTLimits(const geo::Point_t &point); + double StripArea(const uint16_t channel); + + double StripAverageY(const uint16_t channel); + private: std::map fTaggers; diff --git a/sbndcode/Geometry/GeometryWrappers/CRTGeoService_service.cc b/sbndcode/Geometry/GeometryWrappers/CRTGeoService_service.cc index a8b64a01b..76be4b46f 100644 --- a/sbndcode/Geometry/GeometryWrappers/CRTGeoService_service.cc +++ b/sbndcode/Geometry/GeometryWrappers/CRTGeoService_service.cc @@ -562,6 +562,24 @@ namespace sbnd::crt { (point.Y() > lims[1] && point.Y() < lims[4]) && (point.Z() > lims[2] && point.Z() < lims[5]); } + + double CRTGeoService::StripArea(const uint16_t channel) + { + CRTStripGeo strip = GetStrip(channel); + const double x = abs(strip.maxX - strip.minX) / 2; + const double y = abs(strip.maxY - strip.minY) / 2; + const double z = abs(strip.maxZ - strip.minZ) / 2; + + // One will be 1cm so this gives the area in cm^2 + return x * y * z; + } + + double CRTGeoService::StripAverageY(const uint16_t channel) + { + CRTStripGeo strip = GetStrip(channel); + + return (strip.maxY + strip.minY) / 2; + } } DEFINE_ART_SERVICE(sbnd::crt::CRTGeoService) diff --git a/sbndcode/JobConfigurations/standard/caf/cafmakerjob_sbnd_data_sce.fcl b/sbndcode/JobConfigurations/standard/caf/cafmakerjob_sbnd_data_sce.fcl index 6520f36bd..246df11a0 100644 --- a/sbndcode/JobConfigurations/standard/caf/cafmakerjob_sbnd_data_sce.fcl +++ b/sbndcode/JobConfigurations/standard/caf/cafmakerjob_sbnd_data_sce.fcl @@ -15,6 +15,8 @@ physics.producers.cafmaker.OpT0Label: "opt0finderSCE" physics.producers.cafmaker.TPCPMTBarycenterMatchLabel: "tpcpmtbarycentermatchingSCE" physics.producers.cafmaker.CVNLabel: "cvnSCE" physics.producers.cafmaker.CorrectedOpFlashLabel: "lightpropagationcorrectionSCE" +physics.producers.cafmaker.LightCaloLabel: "lightcaloSCE" + physics.producers.cnnid.WireLabel: "sptpc2d:dnnsp" physics.producers.cnnid.ClusterModuleLabel: "pandoraSCE" diff --git a/sbndcode/JobConfigurations/standard/caf/cafmakerjob_sbnd_sce.fcl b/sbndcode/JobConfigurations/standard/caf/cafmakerjob_sbnd_sce.fcl index fb4a26030..d9833c723 100644 --- a/sbndcode/JobConfigurations/standard/caf/cafmakerjob_sbnd_sce.fcl +++ b/sbndcode/JobConfigurations/standard/caf/cafmakerjob_sbnd_sce.fcl @@ -16,6 +16,7 @@ physics.producers.cafmaker.CRTTrackMatchLabel: "pandoraSCETrackCRTTrack" physics.producers.cafmaker.OpT0Label: "opt0finderSCE" physics.producers.cafmaker.TPCPMTBarycenterMatchLabel: "tpcpmtbarycentermatchingSCE" physics.producers.cafmaker.CVNLabel: "cvnSCE" +physics.producers.cafmaker.LightCaloLabel: "lightcaloSCE" physics.producers.cnnid.ClusterModuleLabel: "pandoraSCE" physics.producers.cnnid.PFParticleModuleLabel: "pandoraSCE" diff --git a/sbndcode/JobConfigurations/standard/detsim/CMakeLists.txt b/sbndcode/JobConfigurations/standard/detsim/CMakeLists.txt index 0b3773a0c..5803f01d3 100644 --- a/sbndcode/JobConfigurations/standard/detsim/CMakeLists.txt +++ b/sbndcode/JobConfigurations/standard/detsim/CMakeLists.txt @@ -4,5 +4,6 @@ FILE(GLOB fcl_files *.fcl) install_source(EXTRAS ${fcl_files} ) add_subdirectory(detector_variations) -add_subdirectory(legacy) +# LegacyLArG4 is not supported when using geant4 4.11 +##add_subdirectory(legacy) diff --git a/sbndcode/JobConfigurations/standard/g4/CMakeLists.txt b/sbndcode/JobConfigurations/standard/g4/CMakeLists.txt index 91660cfa0..a82cc1a6b 100644 --- a/sbndcode/JobConfigurations/standard/g4/CMakeLists.txt +++ b/sbndcode/JobConfigurations/standard/g4/CMakeLists.txt @@ -7,4 +7,5 @@ add_subdirectory(recomb_variations) add_subdirectory(crt_filter) add_subdirectory(optical_sim) -add_subdirectory(legacy) +# LegacyLArG4 is not supported when using geant4 4.11 +##add_subdirectory(legacy) diff --git a/sbndcode/JobConfigurations/standard/reco/ci_reco2_sbnd.fcl b/sbndcode/JobConfigurations/standard/reco/ci_reco2_sbnd.fcl new file mode 100644 index 000000000..0ae1c3621 --- /dev/null +++ b/sbndcode/JobConfigurations/standard/reco/ci_reco2_sbnd.fcl @@ -0,0 +1,4 @@ +#include "standard_reco2_sbnd.fcl" + +physics.producers.pandora.ConfigFile: "PandoraSettings_CI_SBND.xml" +physics.producers.pandora.EnableMCParticles: true diff --git a/sbndcode/JobConfigurations/standard/reco/config/workflow_reco2.fcl b/sbndcode/JobConfigurations/standard/reco/config/workflow_reco2.fcl index f89f6fba0..fd2083d46 100755 --- a/sbndcode/JobConfigurations/standard/reco/config/workflow_reco2.fcl +++ b/sbndcode/JobConfigurations/standard/reco/config/workflow_reco2.fcl @@ -20,6 +20,7 @@ #include "SBNDCVNMapper.fcl" #include "blipreco_configs.fcl" +#include "lightcalo_sbnd.fcl" BEGIN_PROLOG @@ -94,6 +95,9 @@ sbnd_reco2_producers:{ module_type: "BlipRecoProducer" BlipAlg: @local::sbnd_blipalg } + + ### light calorimtery + lightcaloSCE : @local::sbnd_lightcalo_sce } sbnd_reco2_producer_sequence: [ @@ -135,6 +139,7 @@ sbnd_reco2_producer_sequence: [ , cvn , cvnSCE , blipreco + , lightcaloSCE ] #FIXME override the producer labels. This should really happen in the module's config fcl diff --git a/sbndcode/JobConfigurations/standard/reco/reco2_data.fcl b/sbndcode/JobConfigurations/standard/reco/reco2_data.fcl index 13452e2ff..a5214dbf4 100755 --- a/sbndcode/JobConfigurations/standard/reco/reco2_data.fcl +++ b/sbndcode/JobConfigurations/standard/reco/reco2_data.fcl @@ -2,6 +2,7 @@ #include "sbnd_tpcpmt3dbarycentermatching_config.fcl" #include "sbnd_lightpropagationcorrection_config.fcl" #include "frameshift_sbnd_data.fcl" +#include "lightcalo_sbnd_data.fcl" #include "standard_reco2_sbnd.fcl" services: @@ -34,6 +35,7 @@ physics.producers: pandoraSCEShower: @local::sbnd_sce_incremental_pandoraModularShowerCreationData pandoraSCEShowerSBN: @local::sbnd_sce_sbn_pandoraModularShowerCreationData + lightcaloSCE: @local::sbnd_lightcalo_data_sce frameshift: @local::frameshift_data } @@ -41,7 +43,7 @@ physics.reco2: [ pandora, pandoraTrack, blipPandoraCopy, blipPandoraTrackCopy, p cvn, opt0finder, crtveto, crtspacepointmatching, crttrackmatching, tpcpmtbarycentermatching, pandoraSCE, pandoraSCETrack, pandoraSCEShower, pandoraSCEShowerSBN, pandoraSCECaloData, pandoraSCEPidData, pandoraSCELikePidData, cvnSCE, opt0finderSCE, tpcpmtbarycentermatchingSCE, crtspacepointmatchingSCE, crttrackmatchingSCE, - caloskimCalorimetry, blipreco, lightpropagationcorrectionSCE, frameshift] + caloskimCalorimetry, blipreco, lightpropagationcorrectionSCE, lightcaloSCE, frameshift] physics.analyzers.caloskim.G4producer: "" diff --git a/sbndcode/LArG4/legacy_largeantmodules_sbnd.fcl b/sbndcode/LArG4/legacy_largeantmodules_sbnd.fcl deleted file mode 100644 index c6fe393c6..000000000 --- a/sbndcode/LArG4/legacy_largeantmodules_sbnd.fcl +++ /dev/null @@ -1,8 +0,0 @@ -#include "largeantmodules.fcl" - -BEGIN_PROLOG - -sbnd_largeant: @local::standard_largeant -sbnd_largeantana: @local::standard_largeantana - -END_PROLOG diff --git a/sbndcode/LArG4/legacy_simulationservices_sbnd.fcl b/sbndcode/LArG4/legacy_simulationservices_sbnd.fcl deleted file mode 100644 index d359ac6e6..000000000 --- a/sbndcode/LArG4/legacy_simulationservices_sbnd.fcl +++ /dev/null @@ -1,146 +0,0 @@ -# -# File: simulationservices_sbnd.fcl -# Purpose: collection of standard simulation service settings for SBND -# Version: 1.3 -# -# Provides: -# -# - sbnd_g4_services: services needed by LArG4 -# - sbnd_detsim_services: services needed by readout simulation -# - sbnd_simulation_services: bundle of services including Geant4, optical -# and electronics simulation (also includes the core ones) -# -# Use as: -# -# services: { -# @table::sbnd_simulation_services -# # ... -# } -# -# Changes: -# 20160601 (petrillo@fnal.gov) [v1.1] -# content moved from services_sbnd.fcl -# -# 20190807 (dbrailsf@fnal.gov) [v1.2] -# PhotonVisibilityService included in g4 servces table -# OpDetResponseInterface included in detsim services table -# Disabled sbnd_opsimulation_services table -# Enabled 'FastOptical' in the custom physics list (in largeantparameters) -# 20190807 (dbrailsf@fnal.gov) [v1.3] -# Added an optional largeantparameters config which does not run the optical sim -# 20200616 (ascarff@fnal.gov) [v1.4] -# Change diffusion constants to match ProtoDUNE data - - -#include "services_sbnd.fcl" -#include "simulationservices.fcl" -#include "spacecharge_sbnd.fcl" -#include "larfft_sbnd.fcl" -#include "signalservices_sbnd.fcl" -#include "magfield_larsoft.fcl" -#include "particleinventoryservice.fcl" -#include "backtrackerservice.fcl" -#include "spacecharge.fcl" -#include "noiseservices_sbnd.fcl" -#include "photpropservices_sbnd.fcl" - -BEGIN_PROLOG - -sbnd_largeantparameters: @local::standard_largeantparameters # from simulationservices.fcl -sbnd_largeantparameters.ElectronClusterSize: 20.0 -#Brailsford 27/10/09: Enable the custom physics list and disable its neutron killer -sbnd_largeantparameters.UseCustomPhysics: true -sbnd_largeantparameters.EnabledPhysics: [ "Em", "FastOptical", "SynchrotronAndGN", "Ion", "Hadron", "Decay", "HadronElastic", "Stopping" ] #Removed the neutron killer -sbnd_largeantparameters.UseLitePhotons: true -sbnd_largeantparameters.UseModBoxRecomb: false -sbnd_largeantparameters.UseEllipsModBoxRecomb: true - -#Define a largeantparameters which doesn't use the optical simulation -sbnd_largeantparameters_noopticalsim: @local::sbnd_largeantparameters -sbnd_largeantparameters_noopticalsim.EnabledPhysics: [ "Em", "SynchrotronAndGN", "Ion", "Hadron", "Decay", "HadronElastic", "Stopping" ] -sbnd_largeantparameters_noopticalsim.UseModBoxRecomb: false -sbnd_largeantparameters_noopticalsim.UseEllipsModBoxRecomb: true - -# ascarff 16Jun2020: Change the diffusion constants to match ProtoDUNE results -sbnd_largeantparameters.LongitudinalDiffusion: 4.0e-9 #in cm^2/ns (was 6.2e-9) -sbnd_largeantparameters.TransverseDiffusion: 8.8e-9 #in cm^2/ns (was 16.3e-9) -sbnd_largeantparameters_noopticalsim.LongitudinalDiffusion: 4.0e-9 #in cm^2/ns (was 6.2e-9) -sbnd_largeantparameters_noopticalsim.TransverseDiffusion: 8.8e-9 #in cm^2/ns (was 16.3e-9) - -# gputnam 19Aug2024: Add in service configuration without EMB recombination -sbnd_largeantparameters_modbox: @local::sbnd_largeantparameters -sbnd_largeantparameters_modbox.UseModBoxRecomb: true -sbnd_largeantparameters_modbox.UseEllipsModBoxRecomb: false - - -sbnd_larvoxelcalculator: @local::standard_larvoxelcalculator # from simulationservices.fcl - -sbnd_backtrackerservice: @local::standard_backtrackerservice # from backtrackerservice.fcl - -sbnd_backtrackerservice.BackTracker.G4ModuleLabel: "largeant" -sbnd_backtrackerservice.BackTracker.MinimumHitEnergyFraction: 1e-1 -sbnd_backtrackerservice.BackTracker.OverrideRealData: true - -sbnd_particleinventoryservice: @local::standard_particleinventoryservice - -# Define sbnd_g4_services -sbnd_g4_services: -{ - @table::sbnd_services - @table::sbnd_random_services - LArG4Parameters: @local::sbnd_largeantparameters - LArVoxelCalculator: @local::sbnd_larvoxelcalculator - MagneticField: @local::no_mag_larsoft - PhotonVisibilityService: @local::sbnd_Nhits_vuv_vis_prop_timing_photonvisibilityservice -} # sbnd_g4_services - - - -# Define sbnd_detsim_services -sbnd_detsim_services: -{ - @table::sbnd_services - @table::sbnd_random_services - LArFFT: @local::sbnd_larfft - SignalShapingServiceSBND: @local::sbnd_signalshapingservice # from signalservices_sbnd.fcl - NoiseModel: @local::sbnd_uboonedatadrivennoiseservice - OpDetResponseInterface: @local::sbnd_opdetresponse -} # sbnd_detsim_services - - -# Define sbnd_simulation_services -sbnd_simulation_services: -{ - @table::sbnd_services - @table::sbnd_random_services - @table::sbnd_g4_services - @table::sbnd_detsim_services - SignalShapingServiceSBND: @local::sbnd_signalshapingservice # from signalservices_sbnd.fcl - SpaceCharge: @local::sbnd_spacecharge -} - - -#sbnd_opsimulation_services: -#{ -# @table::sbnd_simulation_services -# OpDetResponseInterface: @local::sbnd_opdetresponse -## PhotonVisibilityService: @local::sbnd_photonvisibilityservice -## OpDigiProperties: @local::sbnd_opdigiproperties -#} - - -# Define sbnd_optical_simulation_services -#sbnd_optical_simulation_services.LArPropertiesService.ScintYield: 24000 -#sbnd_optical_simulation_services.LArPropertiesService.EnableCerenkovLight: false -#sbnd_optical_simulation_services.LArG4Parameters.UseCustomPhysics: true -#sbnd_optical_simulation_services.LArG4Parameters.EnabledPhysics: [ "Em", -# "FastOptical", -# "SynchrotronAndGN", -# "Ion", -# "Hadron", -# "Decay", -# "HadronElastic", -# "Stopping", -# "NeutronTrackingCut" ] - -END_PROLOG diff --git a/sbndcode/SBNDPandora/scripts/PandoraSettings_CI_SBND.xml b/sbndcode/SBNDPandora/scripts/PandoraSettings_CI_SBND.xml new file mode 100644 index 000000000..fede0b0e7 --- /dev/null +++ b/sbndcode/SBNDPandora/scripts/PandoraSettings_CI_SBND.xml @@ -0,0 +1,52 @@ + + + true + false + true + + + + CaloHitListU + CaloHitListV + CaloHitListW + CaloHitList2D + CaloHitList2D + + + + PandoraSettings_Cosmic_SBND.xml + PandoraSettings_Neutrino_SBND.xml + PandoraSettings_Slicing_Standard.xml + + true + false + + + + + + + PandoraMVAs/PandoraBdt_SBND.xml + NeutrinoId + 0 + -1 + 999 + true + + + CaloHitList2D + RecreatedPfos + RecreatedClusters + RecreatedVertices + false + + + + ci_pandora_clusters.root + pandora_clusters + RecreatedClusters + CaloHitList2D + 5 + true + + diff --git a/sbndcode/SBNDPandora/scripts/PandoraSettings_Master_SBND.xml b/sbndcode/SBNDPandora/scripts/PandoraSettings_Master_SBND.xml index 93658e987..aefbce4da 100644 --- a/sbndcode/SBNDPandora/scripts/PandoraSettings_Master_SBND.xml +++ b/sbndcode/SBNDPandora/scripts/PandoraSettings_Master_SBND.xml @@ -1,3 +1,7 @@ + false diff --git a/sbndcode/SBNDPandora/scripts/PandoraSettings_Neutrino_SBND.xml b/sbndcode/SBNDPandora/scripts/PandoraSettings_Neutrino_SBND.xml index 0fb9f13d0..1c177a684 100644 --- a/sbndcode/SBNDPandora/scripts/PandoraSettings_Neutrino_SBND.xml +++ b/sbndcode/SBNDPandora/scripts/PandoraSettings_Neutrino_SBND.xml @@ -447,6 +447,39 @@ TrackParticles3D ShowerParticles3D DaughterVertices3D ClustersU ClustersV ClustersW TrackClusters3D ShowerClusters3D + + + TrackParticles3D ShowerParticles3D + TrackParticles3D ShowerParticles3D DaughterVertices3D ClustersU ClustersV ClustersW TrackClusters3D ShowerClusters3D + + + + + TrackParticles3D + ShowerParticles3D + true + PandoraMVAs/PandoraBdt_SBND.xml + PfoCharBDT2 + PandoraMVAs/PandoraBdt_SBND.xml + PfoCharBDTNoChargeInfo2 + 0.51 + true + + + + + + + + + + + + + + + + NeutrinoParticles3D diff --git a/test/ci/ci_tests.cfg b/test/ci/ci_tests.cfg index b46e5fb1b..bb48e0c77 100644 --- a/test/ci/ci_tests.cfg +++ b/test/ci/ci_tests.cfg @@ -319,8 +319,8 @@ STAGE_NAME=detsim INPUT_STAGE_NAME=g4 NEVENTS=2 # calibrated on sbndbuild01.fnal.gov on 20180305 -cpu_usage_range=200:700 -mem_usage_range=1000000:3000000 +cpu_usage_range=200:2390 +mem_usage_range=1000000:7232311 script=%(EXPSCRIPT_SBNDCODE)s FHiCL_FILE=%(CI_FHICL_PREFIX_SBNDCODE)ssingle_%(STAGE_NAME)s_quick_test_sbndcode.fcl @@ -383,7 +383,7 @@ STAGE_NAME=caf INPUT_STAGE_NAME=reco2 NEVENTS=5 # calibrated on sbndbuild01.fnal.gov on 20180306 -cpu_usage_range=50:200 +cpu_usage_range=50:213 mem_usage_range=500000:3500000 script=%(EXPSCRIPT_SBNDCODE)s @@ -454,7 +454,7 @@ STAGE_NAME=gen NEVENTS=%(NEVENTS_SEQ_SINGLE_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180305 cpu_usage_range=10:102 -mem_usage_range=500000:700000 +mem_usage_range=500000:1069026 script=%(EXPSCRIPT_SBNDCODE)s @@ -475,8 +475,8 @@ STAGE_NAME=g4 INPUT_STAGE_NAME=gen NEVENTS=%(NEVENTS_SEQ_SINGLE_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180305 -cpu_usage_range=1200:1600 -mem_usage_range=3500000:4500000 +cpu_usage_range=812:1600 +mem_usage_range=2543381:4500000 script=%(EXPSCRIPT_SBNDCODE)s requires=single_%(INPUT_STAGE_NAME)s_seq_test_sbndcode @@ -498,8 +498,8 @@ STAGE_NAME=detsim INPUT_STAGE_NAME=g4 NEVENTS=%(NEVENTS_SEQ_SINGLE_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180305 -cpu_usage_range=1050:1200 -mem_usage_range=3000000:4500000 +cpu_usage_range=1050:3593 +mem_usage_range=3000000:7518172 script=%(EXPSCRIPT_SBNDCODE)s requires=single_%(INPUT_STAGE_NAME)s_seq_test_sbndcode @@ -521,7 +521,7 @@ STAGE_NAME=reco1 INPUT_STAGE_NAME=detsim NEVENTS=%(NEVENTS_SEQ_SINGLE_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180306 -cpu_usage_range=300:450 +cpu_usage_range=300:1204 mem_usage_range=2500000:4000000 script=%(EXPSCRIPT_SBNDCODE)s @@ -544,8 +544,8 @@ STAGE_NAME=reco2 INPUT_STAGE_NAME=reco1 NEVENTS=%(NEVENTS_SEQ_SINGLE_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180306 -cpu_usage_range=1900:2200 -mem_usage_range=2000000:3500000 +cpu_usage_range=195:2200 +mem_usage_range=1866932:3500000 script=%(EXPSCRIPT_SBNDCODE)s requires=single_%(INPUT_STAGE_NAME)s_seq_test_sbndcode @@ -684,8 +684,8 @@ STAGE_NAME=detsim INPUT_STAGE_NAME=g4 NEVENTS=2 # calibrated on sbndbuild01.fnal.gov on 20180305 -cpu_usage_range=400:1000 -mem_usage_range=1000000:3000000 +cpu_usage_range=400:2573 +mem_usage_range=1000000:7354036 script=%(EXPSCRIPT_SBNDCODE)s FHiCL_FILE=%(CI_FHICL_PREFIX_SBNDCODE)snucosmics_%(STAGE_NAME)s_quick_test_sbndcode.fcl @@ -728,7 +728,7 @@ STAGE_NAME=reco2 INPUT_STAGE_NAME=reco1 NEVENTS=2 # calibrated on sbndbuild01.fnal.gov on 20180305 -cpu_usage_range=50:1250 +cpu_usage_range=50:1366 mem_usage_range=1000000:3000000 script=%(EXPSCRIPT_SBNDCODE)s @@ -820,8 +820,8 @@ testlist=nucosmics_gen_quick_test_sbndcode nucosmics_g4_quick_test_sbndcode nuco STAGE_NAME=gen NEVENTS=%(NEVENTS_SEQ_NUCOSMICS_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180305 -cpu_usage_range=600:900 -mem_usage_range=2500000:3500000 +cpu_usage_range=206:900 +mem_usage_range=1290400:3500000 script=%(EXPSCRIPT_SBNDCODE)s FHiCL_FILE=%(CI_FHICL_PREFIX_SBNDCODE)snucosmics_%(STAGE_NAME)s_seq_test_sbndcode.fcl @@ -841,8 +841,8 @@ STAGE_NAME=g4 INPUT_STAGE_NAME=gen NEVENTS=%(NEVENTS_SEQ_NUCOSMICS_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180305 -cpu_usage_range=3500:4200 -mem_usage_range=4500000:5500000 +cpu_usage_range=1854:4200 +mem_usage_range=3473101:5500000 script=%(EXPSCRIPT_SBNDCODE)s requires=nucosmics_%(INPUT_STAGE_NAME)s_seq_test_sbndcode @@ -864,8 +864,8 @@ STAGE_NAME=detsim INPUT_STAGE_NAME=g4 NEVENTS=%(NEVENTS_SEQ_NUCOSMICS_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180305 -cpu_usage_range=1100:1400 -mem_usage_range=3500000:4500000 +cpu_usage_range=1100:3809 +mem_usage_range=3500000:7083228 script=%(EXPSCRIPT_SBNDCODE)s requires=nucosmics_%(INPUT_STAGE_NAME)s_seq_test_sbndcode @@ -888,7 +888,7 @@ INPUT_STAGE_NAME=detsim NEVENTS=%(NEVENTS_SEQ_NUCOSMICS_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180305 cpu_usage_range=600:800 -mem_usage_range=3000000:4000000 +mem_usage_range=1945911:4000000 script=%(EXPSCRIPT_SBNDCODE)s requires=nucosmics_%(INPUT_STAGE_NAME)s_seq_test_sbndcode @@ -910,8 +910,8 @@ STAGE_NAME=reco2 INPUT_STAGE_NAME=reco1 NEVENTS=%(NEVENTS_SEQ_NUCOSMICS_SBNDCODE)s # calibrated on sbndbuild01.fnal.gov on 20180305 -cpu_usage_range=2200:2500 -mem_usage_range=2500000:3500000 +cpu_usage_range=1342:2500 +mem_usage_range=2157647:3500000 script=%(EXPSCRIPT_SBNDCODE)s requires=nucosmics_%(INPUT_STAGE_NAME)s_seq_test_sbndcode @@ -1022,8 +1022,8 @@ output1=%(TFILENAME)s STAGE_NAME=reco1 INPUT_STAGE_NAME=decoders NEVENTS=2 -cpu_usage_range=500:1500 -mem_usage_range=1500000:3000000 +cpu_usage_range=500:2303 +mem_usage_range=1500000:7159462 script=%(EXPSCRIPT_SBNDCODE)s FHiCL_FILE=%(CI_FHICL_PREFIX_SBNDCODE)sdata_offBeamZeroBias_%(STAGE_NAME)s_quick_test_sbndcode.fcl @@ -1043,7 +1043,7 @@ STAGE_NAME=reco2 INPUT_STAGE_NAME=reco1 NEVENTS=2 cpu_usage_range=250:1000 -mem_usage_range=1000000:2000000 +mem_usage_range=1000000:2274627 script=%(EXPSCRIPT_SBNDCODE)s FHiCL_FILE=%(CI_FHICL_PREFIX_SBNDCODE)sdata_offBeamZeroBias_%(STAGE_NAME)s_quick_test_sbndcode.fcl @@ -1062,7 +1062,7 @@ output1=%(TFILENAME)s STAGE_NAME=caf INPUT_STAGE_NAME=reco2 NEVENTS=5 -cpu_usage_range=100:1000 +cpu_usage_range=74:1000 mem_usage_range=1000000:3000000 script=%(EXPSCRIPT_SBNDCODE)s @@ -1090,7 +1090,7 @@ testlist=data_offBeamZeroBias_decoders_quick_test_sbndcode data_offBeamZeroBias_ STAGE_NAME=decoders NEVENTS=%(NEVENTS_SEQ_DATA_SBNDCODE)s cpu_usage_range=10:102 -mem_usage_range=500000:700000 +mem_usage_range=500000:1262633 script=%(EXPSCRIPT_SBNDCODE)s @@ -1110,8 +1110,8 @@ output1=%(TFILENAME)s STAGE_NAME=reco1 INPUT_STAGE_NAME=decoders NEVENTS=%(NEVENTS_SEQ_DATA_SBNDCODE)s -cpu_usage_range=1200:1600 -mem_usage_range=3500000:4500000 +cpu_usage_range=1200:3465 +mem_usage_range=3500000:7417141 script=%(EXPSCRIPT_SBNDCODE)s requires=data_offBeamZeroBias_%(INPUT_STAGE_NAME)s_seq_test_sbndcode @@ -1132,8 +1132,8 @@ output1=%(TFILENAME)s STAGE_NAME=reco2 INPUT_STAGE_NAME=reco1 NEVENTS=%(NEVENTS_SEQ_DATA_SBNDCODE)s -cpu_usage_range=1200:1600 -mem_usage_range=3500000:4500000 +cpu_usage_range=711:1600 +mem_usage_range=2008524:4500000 script=%(EXPSCRIPT_SBNDCODE)s requires=data_offBeamZeroBias_%(INPUT_STAGE_NAME)s_seq_test_sbndcode @@ -1204,7 +1204,7 @@ testlist=compilation_test_sbndcode [test fcl_checks_sbndcode] cpu_usage_range=0:100 -mem_usage_range=10000:60000 +mem_usage_range=10000:66742 script=${SBNDCODE_DIR}/test/fcl_checks.sh STAGE_NAME=fcl EXTRA_DIR=%(FCLCHECK_INPUT_DIRNAME_SBNDCODE)s @@ -1214,7 +1214,7 @@ parse_art_output=False [test update_refs_fcl_checks_sbndcode] cpu_usage_range=0:100 -mem_usage_range=10000:50000 +mem_usage_range=10000:62189 script=${SBNDCODE_DIR}/test/fcl_checks.sh STAGE_NAME=fcl EXTRA_DIR=%(FCLCHECK_INPUT_DIRNAME_SBNDCODE)s diff --git a/test/fcl/testFHiCLfiles_sbnd.sh b/test/fcl/testFHiCLfiles_sbnd.sh index b9eda6ec0..b2f1695a2 100755 --- a/test/fcl/testFHiCLfiles_sbnd.sh +++ b/test/fcl/testFHiCLfiles_sbnd.sh @@ -46,13 +46,14 @@ if [[ -n "$MRB_BUILDDIR" ]]; then fi fi -if [[ -n "$MRB_SOURCE" ]]; then - SourceFHiCLdir="${MRB_SOURCE}/sbndcode/sbndcode/JobConfigurations" - if [[ -d "$SourceFHiCLdir" ]]; then - echo "Will test the job configuration directory in MRB source area ('${SourceFHiCLdir}')" - TestDirs+=( "$SourceFHiCLdir" ) - fi -fi +# DO NOT test files that are not installed +#if [[ -n "$MRB_SOURCE" ]]; then +# SourceFHiCLdir="${MRB_SOURCE}/sbndcode/sbndcode/JobConfigurations" +# if [[ -d "$SourceFHiCLdir" ]]; then +# echo "Will test the job configuration directory in MRB source area ('${SourceFHiCLdir}')" +# TestDirs+=( "$SourceFHiCLdir" ) +# fi +#fi if [[ "${#TestDirs[@]}]" == 0 ]]; then echo "FATAL: no suitable FHiCL directory found to be tested!" >&2 diff --git a/ups/product_deps b/ups/product_deps index e21a2f2aa..8b02783e4 100644 --- a/ups/product_deps +++ b/ups/product_deps @@ -253,7 +253,7 @@ wpdir product_dir wire-cell-cfg # #################################### product version qual flags -sbncode v10_14_02_02 - +sbncode v10_20_03 - cetmodules v3_24_01 - only_for_build sbnd_data v01_42_00 - sbndutil v10_06_01 - optional