// $Id$ // // File: DBCALTimeSpectrum.cc // Created: Mon Aug 1 08:55:45 EDT 2011 // Creator: davidl (on Linux ifarm1102 2.6.18-128.7.1.el5 x86_64) // #include #include using namespace std; #include "DBCALTimeSpectrum.h" // These are defined in timewalk.cc which is // auto-generated from timewalk_calibrate.C static double BCAL_TimeWalkUp(double fADC, int layer); static double BCAL_TimeWalkDn(double fADC, int layer); #include "timewalk_FINE.h" // This is defined in DBCAL_TimeSpectrum_factory.cc. // It can be changed via configuration parameter // MEV_PER_PE. extern double MeV_per_PE; // This is defined in DBCAL_TimeSpectrum_factory.cc. // It can be changed via configuration parameter // APPLY_ELECTRONIC_NOISE. extern bool APPLY_ELECTRONIC_NOISE; // This is defined in DBCAL_TimeSpectrum_factory.cc. // It can be changed via configuration parameter // ADDITIONAL_GAIN. extern double ADDITIONAL_GAIN; // Sampling fluctuation parameters. Can be set via // configuration parameter. extern double BCAL_SAMPLINGCOEFA; extern double BCAL_SAMPLINGCOEFB; // The preamp for the final BCAL has 2 stages. The first // has a gain of 2 according to Fernando (conversation // on 10/10/2011). double preamp_gain_stage1 = 2.0; // Set in DBCALTimeSpectrum_factory.cc via config parameter. extern double PREAMP_GAIN_TDC; // Conversion factor from charge in pC to fADC counts // This is based on a single sample(4ns) of the 12-bit // fADC250 being 2V maximum: // I = (2V/50Ohms) ; Q = I*4ns ; LSB = Q/4096 double LSB_pC_fADC = 2.0/50.0*4.0E3/4096.0; //--------------- // DBCALTimeSpectrum //--------------- DBCALTimeSpectrum::DBCALTimeSpectrum(int layer, int sector): layer(layer), sector(sector), Eup(NBINS_E, E_LO, E_HI), Edn(NBINS_E, E_LO, E_HI), electronic_up(NBINS_ELECTRONIC, ELECTRONIC_LO, ELECTRONIC_HI), electronic_dn(NBINS_ELECTRONIC, ELECTRONIC_LO, ELECTRONIC_HI), rnd(0) { Reset(); } //--------------- // DBCALTimeSpectrum //--------------- DBCALTimeSpectrum::DBCALTimeSpectrum(double thresh_mV, vector &spectra, int layer, int sector): layer(layer), sector(sector), Eup(NBINS_E, E_LO, E_HI), Edn(NBINS_E, E_LO, E_HI), electronic_up(NBINS_ELECTRONIC, ELECTRONIC_LO, ELECTRONIC_HI), electronic_dn(NBINS_ELECTRONIC, ELECTRONIC_LO, ELECTRONIC_HI), rnd(0) { /// This constructor is used to sum together multiple SiPM /// timing distributions into a single distribution. It is /// assumed that the inputs already have filled histograms. /// The FindTimes() method is called automatically after /// the summing is complete. Reset(); for(unsigned int i=0; iEup); Edn.Add(&spectrum->Edn); electronic_up.Add(&spectrum->electronic_up); electronic_dn.Add(&spectrum->electronic_dn); Etot += spectrum->Etot; } // Optionally save the before and after electronic pulse shapes bool save_elec_pulse = false; if(save_elec_pulse){ char hname[256]; sprintf(hname, "elec_up_before_lay%0d_sec%0d", layer,sector); electronic_up.MakeTH1D(hname, "Upstream Before"); sprintf(hname, "elec_dn_before_lay%0d_sec%0d", layer,sector); electronic_dn.MakeTH1D(hname, "Downstream Before"); } // Add in electronic noise due to summing circuit plus cable pickup if(APPLY_ELECTRONIC_NOISE){ double noise_rate_GHz = 1.0; double noise_amp_mV = 1.0; AddElectronicNoise(noise_rate_GHz, noise_amp_mV, 2.0); } // Optionally save the before and after electronic pulse shapes if(save_elec_pulse){ char hname[256]; sprintf(hname, "elec_up_after_lay%0d_sec%0d", layer,sector); electronic_up.MakeTH1D(hname, "Upstream After"); sprintf(hname, "elec_dn_after_lay%0d_sec%0d", layer,sector); electronic_dn.MakeTH1D(hname, "Downstream After"); } // Calculate fADC values double Qup = GetQ(electronic_up); double Qdn = GetQ(electronic_dn); fADC_up = (int)(Qup/0.100); // 100fC LSB for CAEN module (n.b for 4096 counts/2V, this will be 2.5 times larger!) fADC_dn = (int)(Qdn/0.100); // 100fC LSB for CAEN module (n.b for 4096 counts/2V, this will be 2.5 times larger!) geometric_mean = sqrt((double)fADC_up*(double)fADC_dn); FindTimes(thresh_mV); } //--------------- // AddDarkPulses //--------------- void DBCALTimeSpectrum::AddDarkPulses(void) { // Add random dark hits to every event in the given histogram. double tmin = ELECTRONIC_LO; double tmax = ELECTRONIC_HI; double BCAL_DARKRATE_GHZ = 0.0176; double BCAL_XTALK_FRACT = 0.157; double BCAL_INTWINDOW_NS = tmax - tmin; // Get primary dark pulses int darkPulse_up = (int)floor(0.5+rnd.Poisson( BCAL_DARKRATE_GHZ* BCAL_INTWINDOW_NS )); int darkPulse_dn = (int)floor(0.5+rnd.Poisson( BCAL_DARKRATE_GHZ* BCAL_INTWINDOW_NS )); // Randomly place photo-electrons in time for(int i=0; iEval(t-t0+toffset); } } delete pulse_shape; cout<<"Done."<0 ? electronic_up.GetBinCenter(bin_up):-1000.0; tdn = bin_dn>0 ? electronic_dn.GetBinCenter(bin_dn):-1000.0; tup_corrected = tup - (bin_up>0 ? BCAL_TimeWalkUp(fADC_up, layer):0.0); tdn_corrected = tdn - (bin_dn>0 ? BCAL_TimeWalkDn(fADC_dn, layer):0.0); } //--------------- // Reset //--------------- void DBCALTimeSpectrum::Reset(void) { Eup.Reset(); Edn.Reset(); electronic_up.Reset(); electronic_dn.Reset(); Etot = 0.0; fADC_up = 0; fADC_dn = 0; geometric_mean = 0.0; tup = tdn = -1000.0; tup_corrected = tdn_corrected = -1000.0; } //--------------- // SamplingSmear //--------------- void DBCALTimeSpectrum::SamplingSmear(double Emax, double theta_thrown) { // Here we apply a systematic error due to the sampling // fluctuations. The total energy (Emax) is taken from the // highest energy particle in the DMCTrajectoryPoints, but is // not used. We calculate a sigma based on the deposited energy // (coming from class member Etot) and theta_thrown. // // The error is applied by finding the ratio of the smeared // cell energy to unsmeared cell energy and scaling both Eup and Edn // by it. Note that this does not affect the electronic // histograms since it is assumed they will be calculated // afterwards from Eup and Edn. // // This has been reworked a bit (10/20/2011) to add the floor term // and stochastic term in quadrature rather than linearly. // If no energy is deposited, return now if(Etot==0.0)return; // Etot must be converted to GeV double Etot_GeV = Etot/1000.0; // Theta dependence of sampling fluctuations // From plots in an e-mail sent by Matt S. on July 14th or 15th // indicating an angular dependance for 150 MeV photons double BCAL_SAMPLING_THETA_A = 0.01046; double BCAL_SAMPLING_THETA_B = 0.11260; double sigma_scale = (BCAL_SAMPLING_THETA_A/sin(theta_thrown) + BCAL_SAMPLING_THETA_B)/(BCAL_SAMPLING_THETA_A + BCAL_SAMPLING_THETA_B); double sigma_stochastic = BCAL_SAMPLINGCOEFA*sqrt( Etot_GeV ); sigma_stochastic *= sigma_scale; // include theta dependence double dE_stochastic = rnd.Gaus(0.0, sigma_stochastic); double sigma_floor = BCAL_SAMPLINGCOEFB*Etot_GeV; sigma_floor *= sigma_scale; // include theta dependence double dE_floor = rnd.Gaus(0.0, sigma_floor); double dE = dE_stochastic + dE_floor; double Esmeared = Etot_GeV + dE; // Calculate ratio of smeared to unsmeared double ratio = Esmeared/Etot_GeV; // Scale attenuated energy histos for each end Eup.Scale(ratio); Edn.Scale(ratio); } //--------------- // PoissonSmear //--------------- void DBCALTimeSpectrum::PoissonSmear(void) { // Apply Poisson statistics due to photo-electrons // to the attenuated energy spectra. We do this by // converting the integral of the attenuated energy // into photo electrons and then sampling from a // Poisson distribution with that mean. The ratio // of the quantized, sampled value to the unquantized // integral (in PE) is used to scale the spectrum. // Integrate Eup and Edn double Iup = Eup.Integral(); // in attenuated MeV double Idn = Edn.Integral(); // in attenuated MeV if(Iup>0.0){ // Convert to number of PE double mean_pe_up = Iup/MeV_per_PE; int Npe_up = rnd.Poisson(mean_pe_up); double ratio_up = (double)Npe_up/mean_pe_up; Eup.Scale(ratio_up); } if(Idn>0.0){ // Convert to number of PE double mean_pe_dn = Idn/MeV_per_PE; int Npe_dn = rnd.Poisson(mean_pe_dn); double ratio_dn = (double)Npe_dn/mean_pe_dn; Edn.Scale(ratio_dn); } } //--------------- // DetectorJitterSmear //--------------- void DBCALTimeSpectrum::DetectorJitterSmear(void) { // The photo detection device has an intrinsic timing resolution // caused by jitter in the time between the input signal (light) // and the generated electronic pulse. This is quoted in the // spec sheet as "Time Resolution (FWHM)" for a single photon. // (see page 2 of the following link: // http://sales.hamamatsu.com/assets/pdf/parts_S/s10362-33series_kapd1023e05.pdf) // // n.b. XP2020 PMT's have a very similar time jitter as the SiPMs // // To include this effect, each bin is converted into a number // of photo-electrons and each of them is randomly displaced in time // with the specified width. Because the photo-statistics is // applied on a global scale, we do not re-quantize the bin contents // into an integer number of photo-electrons. Rather, we split it // into a number of pieces that is equal to the approximate number // of photo-electrons coming from this bin and shift those pieces. // Bins with trace amounts of energy are still kept and shifted // as well to keep the energy integral constant. double fwhm_jitter = 0.600; // ns double sigma_jitter = fwhm_jitter/2.35; // ns // Upstream DHistogram tmp(NBINS_E, E_LO, E_HI); for(int ibin=1; ibin<=NBINS_E; ibin++){ double E = Eup.GetBinContent(ibin); if(E==0.0)continue; int Npe = 1 + (int)floor(E/MeV_per_PE); double dE = E/(double)Npe; double tbin = Eup.GetBinCenter(ibin); for(int i=0; iEval(t); c_dn += sp_dn->Eval(t); electronic_up.SetBinContent(ibin, c_up); electronic_dn.SetBinContent(ibin, c_dn); } delete sp_up; delete sp_dn; } //--------------- // MakeEnoiseTSpline //--------------- TSpline* DBCALTimeSpectrum::MakeEnoiseTSpline(double t_width, double rate_GHz, double amp_sigma_mV, double max_ramp_rate_mV_per_ns) { // This creates a TSpline representing electronic noise // defined by the input parameters. See comments at top of // AddElectronicNoise() for more. // Start off at t=0 and randomly choose a time difference // to the next point assuming an exponential time structure // until we get past the end of the time window. double t = 0.0; vector times; vector amps; times.push_back(0); // add first point as 0 amplitude amps.push_back(0.0); do{ double s = rnd.Rndm(); double delta_t = -1.0/rate_GHz * log(1.0-s); t += delta_t; if(t