Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

SimVaElectronics.cxx

Go to the documentation of this file.
00001 #include <cmath>
00002 #include <TMath.h>
00003 #include "MessageService/MsgService.h"
00004 #include "Digitization/DigiSignal.h"
00005 #include "SimVaElectronics.h"
00006 #include "SimElecMaker.h"
00007 #include "Calibrator/Calibrator.h"
00008 #include "Calibrator/CalVaLinearity.h"
00009 
00010 // Register this class with the factory.
00011 SimElecMakerProxy<SimVaElectronics> gSimVaElec("SimVaElectronics");
00012 
00013 CVSID("$Id: SimVaElectronics.cxx,v 1.30 2008/10/24 12:01:43 blake Exp $");
00014 
00015 ClassImp(SimVaElectronics)
00016 
00017 SimVaElectronics::SimVaElectronics(  VldContext context, TRandom* random )
00018   : SimElectronics( context, random ),
00019     fVaGain(0.375/Munits::fC),
00020     fVaPedWidth(2.8),
00021     fTimeSmearingMode(SimTimeSmearingMode::kBreitWigner),
00022     fTimingWidth(0.0),
00023     fVaSparsifyThresh(17),
00024     fVaDynodeThresh(57),
00025     fVaNonlinearity(1),
00026     fVaLinearityP0(24),
00027     fVaLinearityP1(16091.3),
00028     fVaLinearityP2(4.63),
00029     fVaLinearityP3(13683.9),
00030     fVaLinearityP4(6650.11),
00031     fVarcTriggerMode(SimVarcTriggerMode::k2of36),
00032     fVarcTriggerWindow(400.*Munits::ns),
00033     fVaChipRandomDeadRate(700*Munits::hertz),
00034     fVaChipDeadTime(5.*Munits::microsecond),
00035     fDoLookupNonlinearity(0),
00036     fCalVaLinearity(0)
00037 {
00038   fCalVaLinearity = new CalVaLinearity(RawChannelId(),
00039                                        fVaLinearityP0, fVaLinearityP1,
00040                                        fVaLinearityP2, fVaLinearityP3,
00041                                        fVaLinearityP4
00042                                        );
00043 }
00044 
00045 
00046 SimVaElectronics::~SimVaElectronics()
00047 {
00048   if(fCalVaLinearity) delete fCalVaLinearity;
00049 }
00050 
00051 
00052 void SimVaElectronics::ReadoutDetector( SimPmtList& pmtList )
00053 {
00054   PlexHandle plex(fContext);
00055 
00056   // For timing-cut version of varc pretrigger.
00057   std::map<int,std::vector<double> > varcTrigTimes;
00058   std::map<int,std::vector<double> >::iterator it;
00059   
00060   // For Dead Chips.
00061   std::map<int,bool> deadChips;
00062   
00063   // Do the dead chips:
00064   if(fVaChipRandomDeadRate>0.) { // don't bother if turned off
00065     SimPmtList::iterator PmtIt;
00066     for(PmtIt = pmtList.begin(); PmtIt!= pmtList.end(); PmtIt++)  {
00067       // how likely is this pmt to be dead at this time?
00068       // Ignores finite event length
00069       double deadProb = fVaChipRandomDeadRate*fVaChipDeadTime;
00070       // Roll the dice:
00071       bool isDead = false;
00072       if(fRandom->Rndm()<deadProb) isDead = true;
00073       // Remember this result:
00074       deadChips[PmtIt->first] = isDead;
00075     }
00076   }
00077 
00078   Int_t  crate, varc, vmm;
00079   
00080   if(fVarcTriggerMode != SimVarcTriggerMode::kNone) {    
00081     // First pass: find dynode trigger times
00082     
00083     SimPmtList::iterator PmtIt;
00084     for(PmtIt = pmtList.begin(); PmtIt!= pmtList.end(); PmtIt++)  {
00085       SimPmt* pmt = PmtIt->second;
00086       if(pmt) {
00087         // Make sure the PMT reads out to VA electronics:
00088         RawChannelId rcid = plex.GetRawChannelId(pmt->GetPixelSpotId(1));
00089         if ( (rcid.GetElecType()==ElecType::kVA) ) {
00090 
00091           // Apply dynode trigger; don't read out if below thresh.
00092           // Don't read out if dead chip, either.
00093           if((DynodeTrigger(pmt->GetDynodeCharge())) && (!deadChips[PmtIt->first])) {
00094             crate = rcid.GetCrate();
00095             varc  = rcid.GetVarcId();
00096             vmm   = rcid.GetVmm();
00097             
00098             // Compose a unique index for this varc (or vmm)
00099             int index = crate*100 + varc*10;
00100             if(fVarcTriggerMode == SimVarcTriggerMode::k2of6) {
00101               index += vmm;
00102             }
00103             
00104             Double_t time = pmt->GetDynodeTime();
00105             varcTrigTimes[index].push_back(time);
00106           }
00107         }      
00108       }
00109     }
00110         
00111     // For each varc or vmm:
00112     for(it = varcTrigTimes.begin(); it!=varcTrigTimes.end(); it++) {
00113       sort(it->second.begin(),it->second.end()); // Sort the trigger times.
00114       // Debugging output...
00115       //for(UInt_t i=0;i<it->second.size();i++) {
00116       //        cout << it->first << "\t" 
00117       //             << it->second[i]*1e9 << endl;
00118       //}
00119     }    
00120   }
00121   
00122 
00123   // Apply pretrigger and read out
00124   SimPmtList::iterator PmtIt;
00125   for(PmtIt = pmtList.begin(); PmtIt!= pmtList.end(); PmtIt++)  {
00126     SimPmt* pmt = PmtIt->second;
00127     if(pmt) {
00128       // Make sure the PMT reads out to VA electronics:
00129       RawChannelId rcid = plex.GetRawChannelId(pmt->GetPixelSpotId(1));
00130       if ( rcid.GetElecType()==ElecType::kVA ) {
00131         // Apply dynode trigger; don't read out if below thresh.
00132         crate = rcid.GetCrate();
00133         varc  = rcid.GetVarcId();
00134         vmm   = rcid.GetVmm();
00135         
00136         bool trig = true;
00137 
00138         if(fVarcTriggerMode != SimVarcTriggerMode::kNone) {
00139           // Compose a unique index for this varc (or vmm)
00140           int index = crate*100 + varc*10;
00141           if(fVarcTriggerMode == SimVarcTriggerMode::k2of6) {
00142             index += vmm;
00143           }
00144           
00145           // Assume it's bad
00146           trig = false;
00147           
00148           std::vector<double> &tVec = varcTrigTimes[index];
00149           // Look for another hit within the trigger window.
00150           int nhits = 0;
00151           for(UInt_t i=0; i<tVec.size(); i++) {
00152             double dt = tVec[i] - pmt->GetDynodeTime();
00153             if(fabs(dt)<fVarcTriggerWindow)  // There's another trigger in the window
00154               nhits++;
00155           }
00156           if(nhits>1) trig=true; // Don't count itself
00157         }
00158         
00159         if(DynodeTrigger(pmt->GetDynodeCharge()))
00160           ReadoutPmt(pmt,trig, deadChips[PmtIt->first]);          
00161       }
00162     }
00163   }
00164   
00165 }
00166 
00167 
00168 
00169 void SimVaElectronics::ReadoutPmt( SimPmt* pmt, bool trig, bool isDead)
00170 {
00171   // Reads out a single PMT.
00172   // Adds all readout to the list of SimDigits.
00173 
00174   MSG("DetSim",Msg::kDebug) << "SimVaElectronics::ReadoutPmt " 
00175                             << pmt->GetTubeId().AsString() 
00176                             << " type " << pmt->GetType()
00177                             << endl;
00178 
00179   PlexHandle plex(fContext);
00180 
00181   // Interesting statistics:
00182   if((trig)&&(!isDead)) {
00183     AddDigitsAfterFETrigger( pmt->GetTotalHitPixels(true) );
00184     AddAdcsAfterFETrigger( pmt->GetTotalCharge() * fVaGain );
00185   }
00186 
00187   for(int ipixel = 1; ipixel<= pmt->GetNumberOfPixels(); ipixel++) {
00188     RawChannelId rcid = plex.GetRawChannelId(pmt->GetPixelSpotId(ipixel));
00189 
00190     // Find which spot had the biggest charge.
00191     // We use this spot to look up the nonlinearity calibration.
00192     Float_t bigpe=0;
00193     Int_t   bigspot=1;
00194     for(int ispot=1;ispot<=pmt->GetNumberOfSpots();ispot++) {
00195       float pe = pmt->GetPeXtalk(ipixel, ispot);
00196       if(pe > bigpe) {
00197         bigpe = pe;
00198         bigspot = ispot;
00199       }
00200     }
00201 
00202     DigiSignal* signal = pmt->CreateSignal(ipixel);
00203     AddSignal(signal);
00204     int errorbits = 0;
00205     if(!trig) errorbits+=1;   // Pretrigger failed
00206     if(isDead) errorbits+=2;  // Dead chip
00207 
00208     if(!(rcid.IsNull())) {
00209       Int_t adc = GenSimulatedADC(pmt->GetCharge(ipixel),
00210                                   rcid, 
00211                                   pmt->GetPixelSpotId(ipixel, bigspot) );
00212       Int_t tdc = GenSimulatedTDC(pmt->GetDynodeTime(), rcid);
00213       
00214       SimDigit d(  pmt->GetPixelSpotId(ipixel),
00215                    rcid,
00216                    adc,
00217                    tdc,
00218                    signal,
00219                    errorbits
00220                    );
00221       MSG("DetSim",Msg::kVerbose) << "ReadoutPmt: " << d.AsString() << std::endl;    
00222       
00223       // Sparsify and record.
00224       if(d.GetADC() > fVaSparsifyThresh) { 
00225         AddDigit(d);
00226         
00227         // Stats:
00228         AddDigitsAfterSpars(1);
00229         AddAdcsAfterSpars(d.GetADC());
00230       }
00231     }
00232   }
00233 }
00234 
00235 
00236 Bool_t SimVaElectronics::DynodeTrigger( double dynCharge )
00237 {
00238   // Returns true if the PMT would fire the dynode trigger.
00239   
00240   // ASDLite DAC setttings: (snip email by Roy Lee)
00241   //The DAC setting for FD dynode thresholds go from 63 (no threshold) to 0
00242   //(maximum thresold), where the maximum corresponds to 160 fC +- 10%,
00243   //assuming linearity over full scale.
00244 
00245   // According to Alfons, from dynode trigger scans, 1 dynode trigger count ~= 2.4 ADC counts.
00246   // Remember that the charge on the dynode is 0.8 of the charge on the anode, so 
00247   // the the maximum corresponds to 63 * 2.4 * 0.8 ADCs.
00248 
00249   // These two numbers seem to disagree. This is resolved because Roy forgot to mention that the
00250   // dynode charge is split into TWO capacitors on the base, so the trigger level of the ASDLite
00251   // is effectively 1/2 of what he said.
00252 
00253   // Thus, the full-scale trigger threshold is 322 fC (dynode) or ~160 fC (on the asdlite cap)
00254   
00255   // Threshold for max settting:
00256   const float kThresh0 = 63.0 * 2.4 *0.8 /(fVaGain); // fC
00257   
00258   double thresh = (double)(63 - fVaDynodeThresh)/63. * kThresh0;
00259 
00260   if(dynCharge > thresh) return true;
00261   else return false;
00262 }
00263 
00264 int SimVaElectronics::GenSimulatedADC( double inCharge, 
00265                                        const RawChannelId&,
00266                                        const PlexPixelSpotId& psid)
00267 {
00268   // Apply nonlinearity.
00269   double adc = inCharge * fVaGain;
00270 
00271   if(fDoLookupNonlinearity) {
00272     PlexHandle plex(fContext);
00273     PlexStripEndId seid = plex.GetStripEndId(psid);
00274     if(seid.IsValid()) {
00275       adc = Calibrator::Instance().DecalLinearity(adc, seid);
00276     }
00277   }
00278   else if(fVaNonlinearity) {
00279     if(adc > 16384) adc = 16384; // Truncate to region where UnLinearize doesn't work.
00280     adc = fCalVaLinearity->UnLinearize(adc);
00281   }
00282 
00283   double x = fRandom->Gaus(adc, fVaPedWidth);
00284   return (int)(TMath::Floor(x));
00285 }
00286 
00287 int SimVaElectronics::GenSimulatedTDC( double inTime, const RawChannelId& rcid  )
00288 {
00289   
00290   // VA electronics, tick = 1.5625 ns (640MHz)
00291   if(inTime>=kPmtTime_Never) {
00292     MSG("DetSim",Msg::kWarning) << "GenSimulatedTDC got a hit with a PMT that never fired!" << endl;
00293     // Return a false number so this hit doesn't correlate with anything real.
00294     return 650000000;
00295   }
00296 
00297   // smear the raw time by fTimingWidth (in nanoseconds)
00298   double deltaTime = 0.0;
00299   if( fTimeSmearingMode>0 && fTimingWidth>0.0 ) {
00300 
00301     // smearing with Breit-Wigner function
00302     if( fTimeSmearingMode==SimTimeSmearingMode::kBreitWigner ){
00303       deltaTime = ( fRandom->BreitWigner( 0.0, fTimingWidth ) )*Munits::ns;
00304     }
00305 
00306     // smearing with Gaussian function
00307     else if( fTimeSmearingMode==SimTimeSmearingMode::kGaussian ){
00308       deltaTime = ( fRandom->Gaus( 0.0, fTimingWidth ) )*Munits::ns;
00309     }
00310 
00311     // smearing with Uniform function
00312     else if( fTimeSmearingMode==SimTimeSmearingMode::kUniform ){
00313       deltaTime = ( fRandom->Uniform( -fTimingWidth, +fTimingWidth ) )*Munits::ns;
00314     }
00315     
00316   }
00317 
00318   //const double tdc_convert = 1.0/1.5625e-9;
00319   double x = Calibrator::Instance().GetTDCFromTime(inTime+deltaTime,rcid);
00320 
00321   return (int)(TMath::Floor(x));
00322 }
00323 
00324 void
00325 SimVaElectronics::Print(Option_t*) const
00326 {
00327   printf("VA Front End:  vaGain               %f ADC/fC\n",fVaGain*Munits::fC);
00328   printf("               vaPedWidth           %f ADCs\n",fVaPedWidth);
00329   printf("               vaTimingWidth        %f ns\n",fTimingWidth);
00330   printf("               vaSparsifyThresh     %d ADCs\n",fVaSparsifyThresh);
00331   printf("               vaDynodeThresh       %d DACs\n",fVaDynodeThresh);
00332   printf("               vaNoninearity        %d (%s)\n",fVaNonlinearity,(fVaNonlinearity)?"Nonlinear":"Linear");
00333   printf("               vaLinearityParams:   %f %.1f %.1f %.1f %.1f\n",fVaLinearityP0, fVaLinearityP1,fVaLinearityP2,fVaLinearityP3,fVaLinearityP4);
00334   printf("VA Back End:   varcTriggerMode      %d (0=none, 1=2/6, 2=2/36)\n",fVarcTriggerMode);
00335   printf("               varcTriggerWindow    %f ns\n",fVarcTriggerWindow);
00336   printf("               vaChipRandomDeadRate %f Hz\n",fVaChipRandomDeadRate);
00337   printf("               vaChipDeadTime       %f us\n",fVaChipDeadTime/Munits::microsecond);
00338   printf("               doLookupNonlinearity  %s \n",fDoLookupNonlinearity?("on"):("off") );
00339 
00340 } 
00341  
00342 void   
00343 SimVaElectronics::Config( Registry& config )
00344 {
00345   // Modify the configuration.
00346   //
00347   // Use this method to set static members with the class configuration.
00348   config.Get("vaGain",fVaGain);
00349   config.Get("vaPedWidth",fVaPedWidth);
00350   config.Get("vaTimeSmearingMode",fTimeSmearingMode);
00351   config.Get("vaTimingWidth",fTimingWidth);
00352   config.Get("vaSparsifyThresh",fVaSparsifyThresh);
00353   config.Get("vaDynodeThresh",fVaDynodeThresh);
00354   config.Get("vaNonlinearity",fVaNonlinearity);
00355   config.Get("vaLinearityP0",fVaLinearityP0);
00356   config.Get("vaLinearityP1",fVaLinearityP1);
00357   config.Get("vaLinearityP2",fVaLinearityP2);
00358   config.Get("vaLinearityP3",fVaLinearityP3);
00359   config.Get("vaLinearityP4",fVaLinearityP4);  
00360   config.Get("varcTriggerMode",fVarcTriggerMode);
00361   config.Get("varcTriggerWindow",fVarcTriggerWindow);
00362   config.Get("vaChipRandomDeadRate",fVaChipRandomDeadRate);
00363   config.Get("vaChipDeadTime",fVaChipDeadTime);
00364   config.Get("doLookupNonlinearity",fDoLookupNonlinearity);
00365   
00366   if(fCalVaLinearity) delete fCalVaLinearity;
00367   fCalVaLinearity = new CalVaLinearity(RawChannelId(),
00368                                        fVaLinearityP0,
00369                                        fVaLinearityP1,
00370                                        fVaLinearityP2,
00371                                        fVaLinearityP3,
00372                                        fVaLinearityP4
00373                                        );
00374 
00375   if(fVaSparsifyThresh<=0) 
00376     MSG("DetSim",Msg::kWarning) << "WARNING: The VA sparsification threshold " << std::endl
00377                                 << "         is less than zero!" << std::endl;                 
00378   SimElectronics::Config(config);
00379 }

Generated on Mon Feb 15 11:07:38 2010 for loon by  doxygen 1.3.9.1