All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ROIDeconvolution_tool.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 /// \file ROIDeconvolution.cc
3 /// \author T. Usher
4 ////////////////////////////////////////////////////////////////////////
5 
6 #include <cmath>
8 #include "art/Utilities/make_tool.h"
9 #include "art/Utilities/ToolMacros.h"
10 #include "art_root_io/TFileService.h"
11 #include "messagefacility/MessageLogger/MessageLogger.h"
12 #include "cetlib_except/exception.h"
15 #include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom()
17 
19 #include "icarus_signal_processing/Filters/ICARUSFFT.h"
20 
21 #include "TH1D.h"
22 
23 #include <fstream>
24 
25 namespace icarus_tool
26 {
27 
29 {
30 public:
31  explicit ROIDeconvolution(const fhicl::ParameterSet& pset);
32 
34 
35  void configure(const fhicl::ParameterSet& pset) override;
36  void initializeHistograms(art::TFileDirectory&) const override;
37 
38  void Deconvolve(const IROIFinder::Waveform&,
39  double samplingRate,
42  recob::Wire::RegionsOfInterest_t& ) const override;
43 
44 private:
45  // Member variables from the fhicl file
46  size_t fFFTSize; ///< FFT size for ROI deconvolution
47  bool fDodQdxCalib; ///< Do we apply wire-by-wire calibration?
48  std::string fdQdxCalibFileName; ///< Text file for constants to do wire-by-wire calibration
49  std::map<unsigned int, float> fdQdxCalib; ///< Map to do wire-by-wire calibration, key is channel
50  ///< number, content is correction factor
51 
52  std::unique_ptr<icarus_tool::IBaseline> fBaseline;
53 
54  const geo::GeometryCore* fGeometry = lar::providerFrom<geo::Geometry>();
55  art::ServiceHandle<icarusutil::SignalShapingICARUSService> fSignalShaping;
56  std::unique_ptr<icarus_signal_processing::ICARUSFFT<double>> fFFT; ///< Object to handle thread safe FFT
57 };
58 
59 //----------------------------------------------------------------------
60 // Constructor.
61 ROIDeconvolution::ROIDeconvolution(const fhicl::ParameterSet& pset)
62 {
63  configure(pset);
64 }
65 
67 {
68 }
69 
70 void ROIDeconvolution::configure(const fhicl::ParameterSet& pset)
71 {
72  // Start by recovering the parameters
73  fFFTSize = pset.get< size_t >("FFTSize" );
74 
75  //wire-by-wire calibration
76  fDodQdxCalib = pset.get< bool >("DodQdxCalib", false);
77 
78  if (fDodQdxCalib)
79  {
80  fdQdxCalibFileName = pset.get< std::string >("dQdxCalibFileName");
81  std::string fullname;
82  cet::search_path sp("FW_SEARCH_PATH");
83  sp.find_file(fdQdxCalibFileName, fullname);
84 
85  if (fullname.empty())
86  {
87  std::cout << "Input file " << fdQdxCalibFileName << " not found" << std::endl;
88  throw cet::exception("File not found");
89  }
90  else
91  std::cout << "Applying wire-by-wire calibration using file " << fdQdxCalibFileName << std::endl;
92 
93  std::ifstream inFile(fullname, std::ios::in);
94  std::string line;
95 
96  while (std::getline(inFile,line))
97  {
98  unsigned int channel;
99  float constant;
100  std::stringstream linestream(line);
101  linestream >> channel >> constant;
102  fdQdxCalib[channel] = constant;
103  if (channel%1000==0) std::cout<<"Channel "<<channel<<" correction factor "<<fdQdxCalib[channel]<<std::endl;
104  }
105  }
106 
107  // Recover the baseline tool
108  fBaseline = art::make_tool<icarus_tool::IBaseline> (pset.get<fhicl::ParameterSet>("Baseline"));
109 
110  // Get signal shaping service.
111  fSignalShaping = art::ServiceHandle<icarusutil::SignalShapingICARUSService>();
112 
113  // Now set up our plans for doing the convolution
114  auto const detProp = art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataForJob();
115  fFFT = std::make_unique<icarus_signal_processing::ICARUSFFT<double>>(detProp.NumberTimeSamples());
116 
117  return;
118 }
120  double const samplingRate,
121  raw::ChannelID_t channel,
122  IROIFinder::CandidateROIVec const& roiVec,
123  recob::Wire::RegionsOfInterest_t& ROIVec) const
124 {
125  double deconNorm = fSignalShaping->GetDeconNorm();
126 
127  // And now process them
128  for(auto const& roi : roiVec)
129  {
130  // First up: copy out the relevent ADC bins into the ROI holder
131  size_t roiLen = roi.second - roi.first;
132 
133  // We want the deconvolution buffer size to be a power of 2 in length
134  // to facilitate the FFT
135  size_t deconSize = fFFTSize;
136 
137  while(1)
138  {
139  if (roiLen > deconSize ) deconSize *= 2;
140  else break;
141  }
142 
143  // In theory, most ROI's are around the same size so this should mostly be a noop
144  fSignalShaping->SetDecon(samplingRate, deconSize, channel);
145 
146  deconSize = fFFTSize;
147 
148  icarusutil::TimeVec deconVec(deconSize);
149 
150  // Pad with zeroes if the deconvolution buffer is larger than the input waveform
151  if (deconSize > waveform.size()) deconVec.resize(deconSize, 0.);
152 
153  // Watch for the case where the input ROI is long enough to want an deconvolution buffer that is
154  // larger than the input waveform.
155  size_t maxActualSize = std::min(deconSize, waveform.size());
156 
157  // Extend the ROI to accommodate the extra bins for the FFT
158  // The idea is to try to center the desired ROI in the buffer used by deconvolution
159  size_t halfLeftOver = (maxActualSize - roiLen) / 2; // Number bins either side of ROI
160  int roiStartInt = halfLeftOver; // Start in the buffer of the ROI
161  int roiStopInt = halfLeftOver + roiLen; // Stop in the buffer of the ROI
162  int firstOffset = roi.first - halfLeftOver; // Offset into the ADC vector of buffer start
163  int secondOffset = roi.second + halfLeftOver + roiLen % 2; // Offset into the ADC vector of buffer end
164 
165  // Check for the two edge conditions - starting before the ADC vector or running off the end
166  // In either case we shift the actual roi within the FFT buffer
167  // First is the case where we would be starting before the ADC vector
168  if (firstOffset < 0)
169  {
170  roiStartInt += firstOffset; // remember that firstOffset is negative
171  roiStopInt += firstOffset;
172  secondOffset -= firstOffset;
173  firstOffset = 0;
174  }
175  // Second is the case where we would overshoot the end
176  else if (size_t(secondOffset) > waveform.size())
177  {
178  size_t overshoot = secondOffset - waveform.size();
179 
180  roiStartInt += overshoot;
181  roiStopInt += overshoot;
182  firstOffset -= overshoot;
183  secondOffset = waveform.size();
184  }
185 
186  size_t roiStart(roiStartInt);
187  size_t roiStop(roiStopInt);
188  size_t holderOffset = 0; //deconSize > waveform.size() ? (deconSize - waveform.size()) / 2 : 0;
189 
190  // Fill the buffer and do the deconvolution
191  std::copy(waveform.begin()+firstOffset, waveform.begin()+secondOffset, deconVec.begin() + holderOffset);
192 
193  // Deconvolute the raw signal using the channel's nominal response
194  fFFT->deconvolute(deconVec, fSignalShaping->GetResponse(channel).getDeconvKernel(), fSignalShaping->ResponseTOffset(channel));
195 
196  std::vector<float> holder(deconVec.size());
197 
198  // Get rid of the leading and trailing "extra" bins needed to keep the FFT happy
199  if (roiStart > 0 || holderOffset > 0) std::copy(deconVec.begin() + holderOffset + roiStart, deconVec.begin() + holderOffset + roiStop, holder.begin());
200 
201  // Resize the holder to the ROI length
202  holder.resize(roiLen);
203 
204  // "normalize" the vector
205  std::transform(holder.begin(),holder.end(),holder.begin(),[deconNorm](auto& deconVal){return deconVal/deconNorm;});
206 
207  // Now we do the baseline determination and correct the ROI
208  //float base = fBaseline->GetBaseline(holder, channel, roiStart, roiLen);
209  float base = fBaseline->GetBaseline(holder, channel, 0, roiLen);
210 
211  std::transform(holder.begin(),holder.end(),holder.begin(),[base](const auto& adcVal){return adcVal - base;});
212 
213  // apply wire-by-wire calibration
214  if (fDodQdxCalib)
215  {
216  if(fdQdxCalib.find(channel) != fdQdxCalib.end())
217  {
218  float constant = fdQdxCalib.at(channel);
219 
220  for (size_t iholder = 0; iholder < holder.size(); ++iholder) holder[iholder] *= constant;
221  }
222  }
223 
224  // add the range into ROIVec
225  ROIVec.add_range(roi.first, std::move(holder));
226  } // loop over candidate roi's
227 
228  return;
229 }
230 
231 
232 
233 void ROIDeconvolution::initializeHistograms(art::TFileDirectory& histDir) const
234 {
235  // It is assumed that the input TFileDirectory has been set up to group histograms into a common
236  // folder at the calling routine's level. Here we create one more level of indirection to keep
237  // histograms made by this tool separate.
238 /*
239  std::string dirName = "ROIDeconvolutionPlane_" + std::to_string(fPlane);
240 
241  art::TFileDirectory dir = histDir.mkdir(dirName.c_str());
242 
243  auto const* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
244  double samplingRate = detprop->SamplingRate();
245  double numBins = fROIDeconvolutionVec.size();
246  double maxFreq = 500. / samplingRate;
247  std::string histName = "ROIDeconvolutionPlane_" + std::to_string(fPlane);
248 
249  TH1D* hist = dir.make<TH1D>(histName.c_str(), "ROIDeconvolution;Frequency(MHz)", numBins, 0., maxFreq);
250 
251  for(int bin = 0; bin < numBins; bin++)
252  {
253  double freq = maxFreq * double(bin + 0.5) / double(numBins);
254 
255  hist->Fill(freq, fROIDeconvolutionVec.at(bin).Re());
256  }
257 */
258 
259  return;
260 }
261 
262 DEFINE_ART_CLASS_TOOL(ROIDeconvolution)
263 }
const geo::GeometryCore * fGeometry
ROIDeconvolution(const fhicl::ParameterSet &pset)
Utilities related to art service access.
static constexpr Sample_t transform(Sample_t sample)
This provides an interface for tools which are tasked with running the deconvolution algorithm...
std::map< unsigned int, float > fdQdxCalib
number, content is correction factor
const datarange_t & add_range(size_type offset, ITER first, ITER last)
Adds a sequence of elements as a range with specified offset.
This provides an interface for tools which are tasked with finding the baselines in input waveforms...
void configure(const fhicl::ParameterSet &pset) override
TString inFile
std::unique_ptr< icarus_tool::IBaseline > fBaseline
void initializeHistograms(art::TFileDirectory &) const override
std::vector< SigProcPrecision > TimeVec
size_t fFFTSize
FFT size for ROI deconvolution.
Description of geometry of one entire detector.
std::string fdQdxCalibFileName
Text file for constants to do wire-by-wire calibration.
std::unique_ptr< icarus_signal_processing::ICARUSFFT< double > > fFFT
Object to handle thread safe FFT.
if &&[-z"$BASH_VERSION"] then echo Attempting to switch to bash bash shellSwitch exit fi &&["$1"= 'shellSwitch'] shift declare a IncludeDirectives for Dir in
void Deconvolve(const IROIFinder::Waveform &, double samplingRate, raw::ChannelID_t, IROIFinder::CandidateROIVec const &, recob::Wire::RegionsOfInterest_t &) const override
art::ServiceHandle< icarusutil::SignalShapingICARUSService > fSignalShaping
T copy(T const &v)
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:28
bool fDodQdxCalib
Do we apply wire-by-wire calibration?
art framework interface to geometry description
BEGIN_PROLOG could also be cout
auto const detProp