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