All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Overlay1D_tool.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 /// \file Overlay1D.cc
3 /// \author F. Varanini
4 ////////////////////////////////////////////////////////////////////////
5 
6 #include <cmath>
7 #include "IOverlay.h"
8 #include "art/Framework/Core/EDProducer.h"
9 #include "art/Utilities/ToolMacros.h"
10 #include "art/Utilities/make_tool.h"
11 #include "messagefacility/MessageLogger/MessageLogger.h"
12 #include "cetlib_except/exception.h"
15 #include "art_root_io/TFileService.h"
16 
17 // art extensions
18 #include "nurandom/RandomUtils/NuRandomService.h"
19 
20 #include "icarus_signal_processing/WaveformTools.h"
21 
22 // CLHEP libraries
23 #include "CLHEP/Random/RandFlat.h"
24 #include "CLHEP/Random/RandGeneral.h"
25 #include "CLHEP/Random/RandGaussQ.h"
26 
27 #include "TH1F.h"
28 #include "TProfile.h"
29 #include "TFile.h"
30 
31 #include <Eigen/Core>
32 #include <unsupported/Eigen/FFT>
33 
34 #include <fstream>
35 
36 namespace icarus_tool
37 {
38 
40 {
41 public:
42  explicit Overlay1D(const fhicl::ParameterSet& pset);
43 
44  ~Overlay1D();
45 
46  void configure(const fhicl::ParameterSet& pset) override;
47 
48  void nextEvent() override;
49 
50  void generateNoise(CLHEP::HepRandomEngine& noise_engine,
51  CLHEP::HepRandomEngine& cornoise_engine,
52  icarusutil::TimeVec& noise,
53  double noise_factor,
54  unsigned int wire) override;
55 
56 private:
57  void GenerateOverlay1D(CLHEP::HepRandomEngine&, icarusutil::TimeVec&, double, unsigned int);
58  void GenNoise(std::function<void (double[])>&, const icarusutil::TimeVec&, icarusutil::TimeVec&, double);
59  void ExtractCorrelatedAmplitude(float&, int) const;
61  void FindPeaks() ;
62  void makeHistograms();
63 
64  // Member variables from the fhicl file
65  size_t fPlane;
67  float fNoiseRand;
73  std::string fHistogramName;
74  std::string fCorrAmpHistFileName;
75  std::string fCorrAmpHistogramName;
76 
77  using WaveformTools = icarus_signal_processing::WaveformTools<icarusutil::SigProcPrecision>;
78 
80 
81  // We'll recover the bin contents and store in a vector
82  // with the likely false hope this will be faster...
83  icarusutil::TimeVec fNoiseHistVec; //< Input full noise frequency distribution
84  icarusutil::TimeVec fCoherentNoiseVec; //< Peak distribution for coherent noise
85  icarusutil::TimeVec fIncoherentNoiseVec; //< Incoherent frequency distribution
86  icarusutil::TimeVec fCorrAmpDistVec; //< Keep track of motherboard contributions
87 
88  icarusutil::SigProcPrecision fIncoherentNoiseRMS; //< RMS of full noise waveform
89  icarusutil::SigProcPrecision fCoherentNoiseRMS; //< RMS of full noise waveform
90 
91  // Container for doing the work
93 
94  // Keep track of seed initialization for uncorrelated noise
95  bool fNeedFirstSeed=true;
96 
97  // Histograms
98  TProfile* fInputNoiseHist;
99  TProfile* fMedianNoiseHist;
100  TProfile* fPeakNoiseHist;
101  TProfile* fCorAmpDistHist;
102 
103  // Keep instance of the eigen FFT
104  Eigen::FFT<double> fEigenFFT;
105 
106 };
107 
108 //----------------------------------------------------------------------
109 // Constructor.
110 Overlay1D::Overlay1D(const fhicl::ParameterSet& pset)
111 {
112  // Recover the configuration of the tool from the input fhicl file and set up
113  configure(pset);
114 
115  // Now break out the coherent from the incoherent using the input overall spectrum
117 
118  // Output some histograms to catalogue what's been done
119  makeHistograms();
120 }
121 
123 {
124 }
125 
126 void Overlay1D::configure(const fhicl::ParameterSet& pset)
127 {
128  // Recover the histogram used for noise generation
129  fPlane = pset.get< size_t >("Plane");
130  fMedianNumBins = pset.get< int >("MedianNumBins");
131  fNoiseRand = pset.get< float >("NoiseRand");
132  fCorrelatedSeed = pset.get< long >("CorrelatedSeed",1000);
133  fUncorrelatedSeed = pset.get< long >("UncorrelatedSeed",5000);
134  fIncoherentNoiseFrac = pset.get< float >("IncoherentNoiseFraction",0.5);
135  fStoreHistograms = pset.get< bool >("StoreHistograms");
136  fInputNoiseHistFileName = pset.get< std::string >("NoiseHistFileName");
137  fHistogramName = pset.get< std::string >("HistogramName");
138  fCorrAmpHistFileName = pset.get< std::string >("CorrAmpHistFileName");
139  fCorrAmpHistogramName = pset.get< std::string >("CorrAmpHistogramName");
140 
141  // Initialize the work vector
142  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService>()->DataForJob();
143  auto const detProp = art::ServiceHandle<detinfo::DetectorPropertiesService>()->DataForJob(clockData);
144  fNoiseFrequencyVec.resize(detProp.NumberTimeSamples(),std::complex<float>(0.,0.));
145 
146  // Set up to input the histogram with the overall noise spectrum
147  std::string fullFileName;
148  std::string corrAmpFileName;
149 
150  cet::search_path searchPath("FW_SEARCH_PATH");
151  searchPath.find_file(fInputNoiseHistFileName, fullFileName);
152 
153  TFile inputFile(fullFileName.c_str(), "READ");
154 
155  if (!inputFile.IsOpen())
156  throw cet::exception("NoiseFromHist::configure") << "Unable to open input file: " << fInputNoiseHistFileName << std::endl;
157 
158  TH1D* histPtr = (TH1D*)inputFile.Get(fHistogramName.c_str());
159 
160  if (!histPtr)
161  throw cet::exception("NoiseFromHist::configure") << "Unable to recover desired histogram: " << fHistogramName << std::endl;
162 
163  // Close the input file
164  inputFile.Close();
165 
166  // Now grab the histogram givng the observed "strenght" of the coherent noise
167  searchPath.find_file(fCorrAmpHistFileName, corrAmpFileName);
168 
169  TFile corrAmpInputFile(corrAmpFileName.c_str(), "READ");
170 
171  if (!corrAmpInputFile.IsOpen())
172  throw cet::exception("Overlay1D::configure") << "Unable to open input file: " << fCorrAmpHistFileName << std::endl;
173 
174  TH1D* corrAmpHistPtr = (TH1D*)corrAmpInputFile.Get(fCorrAmpHistogramName.c_str());
175 
176  if (!corrAmpHistPtr)
177  throw cet::exception("Overlay1D::configure") << "Unable to recover desired histogram: " << fCorrAmpHistogramName << std::endl;
178 
179  // Close the input file
180  corrAmpInputFile.Close();
181 
182  fNoiseHistVec.resize(histPtr->GetNbinsX(), 0.);
183  fCorrAmpDistVec.resize(corrAmpHistPtr->GetNbinsX(),0.);
184 
185  // Recover the bin contents into local vectors
186  for(size_t histIdx = 0; histIdx < size_t(histPtr->GetNbinsX()); histIdx++)
187  fNoiseHistVec[histIdx] = histPtr->GetBinContent(histIdx+1);
188 
189  for(size_t histIdx = 0; histIdx < size_t(corrAmpHistPtr->GetNbinsX()); histIdx++) // was 200
190  fCorrAmpDistVec[histIdx] = corrAmpHistPtr->GetBinContent(histIdx+1);
191 
192  // Should we store hists?
193  if (fStoreHistograms)
194  {
195  // Define histograms
196  art::ServiceHandle<art::TFileService> tfs;
197 
198  art::TFileDirectory* histDirectory = tfs.get();
199 
200  // Make a directory for these histograms
201  art::TFileDirectory dir = histDirectory->mkdir(Form("CorNoisePlane%1zu",fPlane));
202 
203  float sampleRate = sampling_rate(clockData);
204  float readOutSize = detProp.ReadOutWindowSize();
205  float maxFreq = 1.e6 / (2. * sampleRate);
206  float minFreq = 1.e6 / (2. * sampleRate * readOutSize);
207  int numSamples = readOutSize / 2;
208 
209  fInputNoiseHist = dir.make<TProfile>("InNoise", ";freq(kHz)", numSamples, minFreq, maxFreq);
210  fMedianNoiseHist = dir.make<TProfile>("MedNoise", ";freq(kHz)", numSamples, minFreq, maxFreq);;
211  fPeakNoiseHist = dir.make<TProfile>("PeakNoise", ";freq(kHz)", numSamples, minFreq, maxFreq);;
212 
213  fCorAmpDistHist = dir.make<TProfile>("CorAmp", ";Motherboard", fCorrAmpDistVec.size(),0.,fCorrAmpDistVec.size());
214  }
215 
216  return;
217 }
218 
220 {
221  // We update the correlated seed because we want to see different noise event-by-event
222  fCorrelatedSeed = (333 * fCorrelatedSeed) % 900000000;
223 
224  return;
225 }
226 
227 void Overlay1D::generateNoise(CLHEP::HepRandomEngine& engine_unc,
228  CLHEP::HepRandomEngine& engine_corr,
229  icarusutil::TimeVec& noise,
230  double noise_factor,
231  unsigned int channel)
232 {
233  // Define a couple of vectors to hold intermediate work
234  icarusutil::TimeVec noise_unc(noise.size(),0.);
235  icarusutil::TimeVec noise_corr(noise.size(),0.);
236 
237  // Make sure the work vector is size right with the output
238  if (fNoiseFrequencyVec.size() != noise.size()) fNoiseFrequencyVec.resize(noise.size(),std::complex<float>(0.,0.));
239 
240  // If applying incoherent noise call the generator
241  if (fIncoherentNoiseFrac > 0.) GenerateOverlay1D(engine_unc,noise_unc,noise_factor,channel);
242 
243  int board=channel/32;
244 
245  // If applying coherent noise call the generator
246  if (fIncoherentNoiseFrac < 1.) GenerateOverlay1D(engine_corr, noise_corr, noise_factor, board);
247 
248  // Take the noise as the simple sum of the two contributions
249  std::transform(noise_unc.begin(),noise_unc.end(),noise_corr.begin(),noise.begin(),std::plus<float>());
250 
251  return;
252 }
253 
254 void Overlay1D::GenerateOverlay1D(CLHEP::HepRandomEngine& engine, icarusutil::TimeVec& noise, double noise_factor, unsigned int channel)
255 {
256  // Here we aim to produce a waveform consisting of incoherent noise
257  // Note that this is expected to be the dominate noise contribution
258  // Check for seed initialization
259  if (fNeedFirstSeed)
260  {
261  engine.setSeed(fUncorrelatedSeed,0);
262  fNeedFirstSeed = false;
263  }
264 
265  // Get the generator
266  CLHEP::RandFlat noiseGen(engine,0,1);
267 
268  std::function<void (double[])> randGenFunc = [&noiseGen](double randArray[]){noiseGen.fireArray(2,randArray);};
269 
270  double scaleFactor = fIncoherentNoiseFrac * noise_factor / fIncoherentNoiseRMS;
271 
272  GenNoise(randGenFunc, fIncoherentNoiseVec, noise, scaleFactor);
273 
274  return;
275 }
276 
277 void Overlay1D::GenNoise(std::function<void (double[])>& gen,const icarusutil::TimeVec& freqDist, icarusutil::TimeVec& noise, double scaleFactor)
278 {
279  double rnd_corr[2] = {0.,0.};
280 
281  // Build out the frequency vector
282  for(size_t i=0; i< noise.size()/2; ++i)
283  {
284  // exponential noise spectrum
285  gen(rnd_corr);
286 
287  double pval = freqDist[i] * ((1-fNoiseRand) + 2 * fNoiseRand*rnd_corr[0]) * scaleFactor;
288  double phase = rnd_corr[1] * 2. * M_PI;
289 
290  std::complex<double> tc(pval*cos(phase),pval*sin(phase));
291 
292  fNoiseFrequencyVec[i] = tc;
293  }
294 
295  // inverse FFT MCSignal
296  fEigenFFT.inv(noise, fNoiseFrequencyVec);
297 
298  return;
299 }
300 
302 {
303  float dThr=50;
304  std::vector<float> dHeight;
305  dHeight.resize(fNoiseHistVec.size(), 0.);
306 
307  dHeight[0]=0;
308  fCoherentNoiseVec.resize(fNoiseHistVec.size(),0);
309 
310  for(size_t histIdx = 1; histIdx < fNoiseHistVec.size(); histIdx++) {
311  dHeight[histIdx]=fNoiseHistVec[histIdx]-fNoiseHistVec[histIdx-1];
312  }
313 
314  for(size_t histIdx = 1; histIdx < fNoiseHistVec.size(); histIdx++)
315  if(dHeight[histIdx]>dThr&&dHeight[histIdx+1]<dThr&&histIdx>5) fCoherentNoiseVec[histIdx]=1;
316 
317  return;
318 }
319 
321 {
322  // In the below we take the overall noise spectrum and perform a long bin smoothing to get its
323  // general shape. We can then subtract this from the input spectrum to return a spectrum containing
324  // the "spikes" which are associated to the coherent noise.
325  // Generally, the inchorent noise model is then that with the spikes subtracted,
326  // the coherent model is derived from the spikes...
327  fIncoherentNoiseVec.resize(fNoiseHistVec.size(), 0.);
328 
329  // This does a median smoothing based on the number of bins we input
331 
332  icarusutil::TimeVec peakVec(fNoiseHistVec.size());
333 
334  // Subtract the smoothed from the input to get the peaks
335  std::transform(fNoiseHistVec.begin(),fNoiseHistVec.end(),fIncoherentNoiseVec.begin(),peakVec.begin(),std::minus<float>());
336 
337  // Try to clean up the "peak" vector a bit
338  icarusutil::TimeVec::iterator maxItr = std::max_element(peakVec.begin(),peakVec.end());
339 
340  float maxPeak = *maxItr;
341  float threshold = std::min(0.1 * maxPeak, 20.);
342 
343  fCoherentNoiseVec.clear();
344  fCoherentNoiseVec.resize(peakVec.size(),0.);
345 
346  auto const samplingRate = sampling_rate(art::ServiceHandle<detinfo::DetectorClocksService>()->DataForJob());
347  for(size_t idx = 0; idx < peakVec.size(); idx++)
348  {
349  if (peakVec[idx] > threshold) fCoherentNoiseVec[idx] = peakVec[idx];
350 
351  if (fStoreHistograms)
352  {
353  float freq = 1.e6 * float(idx)/ (2. * samplingRate * fCoherentNoiseVec.size());
354 
355  fInputNoiseHist->Fill(freq,fNoiseHistVec.at(idx),1.);
356  fMedianNoiseHist->Fill(freq,fIncoherentNoiseVec.at(idx),1.);
357  fPeakNoiseHist->Fill(freq,peakVec.at(idx),1.);
358  }
359  }
360 
361  // Let's get the rms we expect from the incoherent noise contribution to the waveform
362  // A couple of ways to do this, let's basically invert the frequency spectrum to
363  // produce a waveform and then get the rms from that
364  std::function<void (double[])> randGenFunc = [](double randArray[]){randArray[0]=0.5; randArray[1]=0.5;};
365 
366  icarusutil::TimeVec waveNoise(fIncoherentNoiseVec.size());
367  float scaleFactor = 1.;
368 
369  GenNoise(randGenFunc, fIncoherentNoiseVec, waveNoise, scaleFactor);
370 
371  // Now get the details...
374  int nTrunc;
375  int range;
376 
377  // Use the waveform tool to recover the full rms
378  fWaveformTool.getTruncatedMean(waveNoise, mean, nTrunc, range);
379  fWaveformTool.getTruncatedRMS(waveNoise, nSig, fIncoherentNoiseRMS, rmsTrunc, nTrunc);
380 
381  // Do the same for the coherent term
382  GenNoise(randGenFunc, fCoherentNoiseVec, waveNoise, scaleFactor);
383 
384  fWaveformTool.getTruncatedMean(waveNoise, mean, nTrunc, range);
385  fWaveformTool.getTruncatedRMS(waveNoise, nSig, fCoherentNoiseRMS, rmsTrunc, nTrunc);
386 
387  if (fStoreHistograms)
388  {
389  for(size_t idx = 0; idx < fCorrAmpDistVec.size(); idx++)
390  fCorAmpDistHist->Fill(idx, fCorrAmpDistVec[idx], 1.);
391  }
392 
393  return;
394 }
395 
396 void Overlay1D::ExtractCorrelatedAmplitude(float& corrFactor, int board) const
397 {
398  CLHEP::RandGeneral amp_corr(fCorrAmpDistVec.data(),fCorrAmpDistVec.size(),0);
399  amp_corr.setTheSeed(board+1);
400  double rnd_corr[1] = {0.};
401 
402  amp_corr.fireArray(1,rnd_corr);
403 
404  float cfmedio=0.2287;
405  corrFactor=rnd_corr[0]/cfmedio;
406 }
407 
409 {
410 
411  return;
412 }
413 
414 DEFINE_ART_CLASS_TOOL(Overlay1D)
415 }
void configure(const fhicl::ParameterSet &pset) override
std::string fInputNoiseHistFileName
void ExtractCorrelatedAmplitude(float &, int) const
void nextEvent() override
icarusutil::TimeVec fCoherentNoiseVec
Utilities related to art service access.
static constexpr Sample_t transform(Sample_t sample)
void GenerateOverlay1D(CLHEP::HepRandomEngine &, icarusutil::TimeVec &, double, unsigned int)
std::vector< ComplexVal > FrequencyVec
std::string fCorrAmpHistogramName
icarusutil::FrequencyVec fNoiseFrequencyVec
icarusutil::SigProcPrecision fIncoherentNoiseRMS
This is the interface class for a tool to handle a GenNoise for the overall response.
Eigen::FFT< double > fEigenFFT
icarusutil::TimeVec fNoiseHistVec
std::vector< SigProcPrecision > TimeVec
icarusutil::TimeVec fIncoherentNoiseVec
void GenNoise(std::function< void(double[])> &, const icarusutil::TimeVec &, icarusutil::TimeVec &, double)
Overlay1D(const fhicl::ParameterSet &pset)
void generateNoise(CLHEP::HepRandomEngine &noise_engine, CLHEP::HepRandomEngine &cornoise_engine, icarusutil::TimeVec &noise, double noise_factor, unsigned int wire) override
tuple dir
Definition: dropbox.py:28
double mean(const std::vector< short > &wf, size_t start, size_t nsample)
Definition: UtilFunc.cxx:13
icarusutil::SigProcPrecision fCoherentNoiseRMS
icarusutil::TimeVec fCorrAmpDistVec
WaveformTools fWaveformTool
art::ServiceHandle< art::TFileService > tfs
double sampling_rate(DetectorClocksData const &data)
Returns the period of the TPC readout electronics clock.
std::string fCorrAmpHistFileName
auto const detProp
icarus_signal_processing::WaveformTools< icarusutil::SigProcPrecision > WaveformTools