00001 #include "SimPmtUTM16.h"
00002 #include "SimPmtMaker.h"
00003 #include "SimPmtM16CrosstalkTable.h"
00004 #include "SimPmtM16Crosstalk.h"
00005 #include "MessageService/MsgService.h"
00006 #include "MessageService/MsgFormat.h"
00007 #include "Conventions/Munits.h"
00008
00009
00010 #include <cmath>
00011 using std::pow;
00012 using std::sqrt;
00013
00014 #include <cassert>
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 const double SimPmtUTM16::gkDefaultSECValues[12] = {4.9407, 4.9407, 4.9407,
00035 2.53997, 2.53997, 2.53997,
00036 2.53997, 2.53997, 2.53997,
00037 2.53997, 2.91747, 4.9407};
00038
00039
00040 const double SimPmtUTM16::gkDefaultGain = SimPmtUTM16::gkDefaultSECValues[0]
00041 *SimPmtUTM16::gkDefaultSECValues[1]
00042 *SimPmtUTM16::gkDefaultSECValues[2]
00043 *SimPmtUTM16::gkDefaultSECValues[3]
00044 *SimPmtUTM16::gkDefaultSECValues[4]
00045 *SimPmtUTM16::gkDefaultSECValues[5]
00046 *SimPmtUTM16::gkDefaultSECValues[6]
00047 *SimPmtUTM16::gkDefaultSECValues[7]
00048 *SimPmtUTM16::gkDefaultSECValues[8]
00049 *SimPmtUTM16::gkDefaultSECValues[9]
00050 *SimPmtUTM16::gkDefaultSECValues[10]
00051 *SimPmtUTM16::gkDefaultSECValues[11];
00052
00053
00054 double SimPmtUTM16::gOpticalXTScale=1.75;
00055 double SimPmtUTM16::gOpticalXTOffset=0.0;
00056 double SimPmtUTM16::gElectricXTScale=0.75;
00057 double SimPmtUTM16::gElectricXTOffset=-0.001;
00058
00059
00060 double SimPmtUTM16::gTransitTimeNS=8.8;
00061 double SimPmtUTM16::gTransitTimeJitterNS=0.180;
00062
00063
00064 SimPmtM16CrosstalkTable* SimPmtUTM16::gCrosstalkTable=0;
00065
00066
00067 SimPmtMakerProxy<SimPmtUTM16> gSimPmtUTM16Proxy("SimPmtUTM16");
00068
00069 CVSID("$Id: SimPmtUTM16.cxx,v 1.23 2005/05/13 10:57:19 tagg Exp $");
00070
00071 ClassImp(SimPmtUTM16)
00072
00073 SimPmtUTM16::SimPmtUTM16(PlexPixelSpotId tube,
00074 VldContext context,
00075 TRandom* random) :
00076 SimPmt(tube,context,random,16,8),
00077 fLastGainValidityId(-999)
00078 {
00079
00080
00081
00082
00083
00084
00085 fSECValuesBuilt = false;
00086
00087 MSG("DetSim",Msg::kDebug) << "SimPmtUTM16 Constructor, Tube :" << fTube.AsString() << endl;
00088
00089 }
00090
00091 void SimPmtUTM16::Reset( const VldContext& context )
00092 {
00093
00094
00095
00096
00097
00098
00099 SimPmt::Reset(context);
00100
00101
00102
00103 if(gCrosstalkTable) {
00104 gCrosstalkTable->Reset(context);
00105 } else {
00106 gCrosstalkTable = new SimPmtM16CrosstalkTable(context);
00107 }
00108
00109
00110 if(fsRebuildGainMap || (!fSECValuesBuilt))
00111 fSECValuesBuilt = InitSECValues();
00112
00113 if(!fSECValuesBuilt){
00114 MSG("DetSim", Msg::kFatal)<<"Could not initialize SEC values!"<<endl;
00115 assert(0);
00116 }
00117 }
00118
00119 void SimPmtUTM16::SimulatePmt()
00120 {
00121
00122
00123 if(fsPmtDoOpticalCrosstalk) SimulateOpticalXtalk();
00124 else CopyPEtoPEXtalk();
00125
00126 if(fsPmtDoDarkNoise) SimulateDarkNoise();
00127 SimulateCharges();
00128
00129 }
00130
00131
00132 void SimPmtUTM16::Print(Option_t* option) const
00133 {
00134 printf(" SimPmtUTM16::Print() -- Tube: %s\n",fTube.AsString("t"));
00135
00136 std::string opt = option;
00137 Bool_t spots = (opt.find("spot") != std::string::npos);;
00138
00139 for(SimPmtBucketIterator it(*this); !it.End(); it.Next()) {
00140 int ibucket = it.BucketId();
00141
00142
00143 printf(" Time bucket %d:\n",ibucket);
00144
00145
00146
00147
00148
00149
00150
00151 if(spots) {
00152 printf(" Photoelectrons(Npe)\n");
00153 for(int row=0; row < 4; row++) {
00154 printf(" %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f\n",
00155 GetPe(16-row*4,8,ibucket), GetPe(16-row*4,7,ibucket), GetPe(16-row*4,6,ibucket),
00156 GetPe(15-row*4,8,ibucket), GetPe(15-row*4,7,ibucket), GetPe(15-row*4,6,ibucket),
00157 GetPe(14-row*4,8,ibucket), GetPe(14-row*4,7,ibucket), GetPe(14-row*4,6,ibucket),
00158 GetPe(13-row*4,8,ibucket), GetPe(13-row*4,7,ibucket), GetPe(13-row*4,6,ibucket));
00159
00160 printf(" %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f\n",
00161 GetPe(16-row*4,5,ibucket), GetPe(16-row*4,4,ibucket),
00162 GetPe(15-row*4,5,ibucket), GetPe(15-row*4,4,ibucket),
00163 GetPe(14-row*4,5,ibucket), GetPe(14-row*4,4,ibucket),
00164 GetPe(13-row*4,5,ibucket), GetPe(13-row*4,4,ibucket));
00165
00166 printf(" %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f\n",
00167 GetPe(16-row*4,3,ibucket), GetPe(16-row*4,2,ibucket), GetPe(16-row*4,1,ibucket),
00168 GetPe(15-row*4,3,ibucket), GetPe(15-row*4,2,ibucket), GetPe(15-row*4,1,ibucket),
00169 GetPe(14-row*4,3,ibucket), GetPe(14-row*4,2,ibucket), GetPe(14-row*4,1,ibucket),
00170 GetPe(13-row*4,3,ibucket), GetPe(13-row*4,2,ibucket), GetPe(13-row*4,1,ibucket));
00171
00172 printf("\n");
00173 }
00174 }
00175
00176
00177 printf("\n PEs PE with Xtalk Charges (fC)\n");
00178 for(int row=0; row < 4; row++) {
00179 printf(" %4d %4d %4d %4d",
00180 (int)GetPe(16-row*4,0,ibucket), (int)GetPe(15-row*4,0,ibucket),
00181 (int)GetPe(14-row*4,0,ibucket), (int)GetPe(13-row*4,0,ibucket));
00182
00183 printf(" %4d %4d %4d %4d",
00184 (int)GetPeXtalk(16-row*4,0,ibucket), (int)GetPeXtalk(15-row*4,0,ibucket),
00185 (int)GetPeXtalk(14-row*4,0,ibucket), (int)GetPeXtalk(13-row*4,0,ibucket));
00186
00187 printf(" %6.0f %6.0f %6.0f %6.0f\n",
00188 GetCharge(16-row*4,ibucket)/Munits::fC, GetCharge(15-row*4,ibucket)/Munits::fC,
00189 GetCharge(14-row*4,ibucket)/Munits::fC, GetCharge(13-row*4,ibucket)/Munits::fC);
00190 }
00191
00192 }
00193
00194 printf("\n");
00195 }
00196
00197 double SimPmtUTM16::FractionalWidthToSEC(double fw)
00198 {
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229 const double default_sec=-1.0;
00230
00231
00232
00233
00234
00235 if((fw<0.01) || (fw>3.0)){
00236 MAXMSG("DetSim", Msg::kWarning,20)
00237 <<"FractionalWidthToSEC was passed the fractional width: "<<fw
00238 <<" which is not a reasonable value.\n"
00239 <<"This routine will return "<<default_sec
00240 <<" as the secondary emmission coefficient."<<endl;
00241
00242 return default_sec;
00243 }
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263 const double Q = -2.0/9.0;
00264 const double R = (-7.0 - 27.0*fw*fw)/54.0;
00265
00266 double secinv=1.0/default_sec;
00267 if( (R*R)<(Q*Q*Q)) {
00268
00269 MSG("DetSim", Msg::kVerbose)<<"FractionalWidthToSEC("<<fw<<") has 3 real roots!\n"
00270 <<"Will use first order value: "<<1.0/(fw*fw)<<endl;
00271 return 1.0/(fw*fw);
00272 }
00273 else{
00274 const double A = pow(fabs(R) + sqrt(R*R - Q*Q*Q),1.0/3.0);
00275 double B=0.0; if(A!=0.0) B=Q/A;
00276 secinv = (A+B) - 1.0/3.0;
00277 MSG("DetSim", Msg::kVerbose)<<"FractionalWidthToSEC("<<fw<<") has 1 real root="
00278 <<1.0/secinv<<endl;
00279 }
00280
00281 const double sec=1.0/secinv;
00282 return sec;
00283
00284 }
00285
00286 void SimPmtUTM16::SimulateCharges()
00287 {
00288
00289 MSG("DetSim", Msg::kVerbose)
00290 <<"Calling SimPmtUTM16::SimulateCharges()"<<endl;
00291
00292
00293
00294
00295
00296
00297
00298 fTotalCharge = 0;
00299
00300 MSG("DetSim", Msg::kVerbose)<<"Iterating over time buckets now."<<endl;
00301
00302 int cntr=1;
00303 for(SimPmtBucketIterator it(*this); !it.End(); it.Next()) {
00304 SimPmtTimeBucket& pmttb = it.Bucket();
00305 MSG("DetSim", Msg::kVerbose)<<"Working on time bucket: "<<cntr<<endl;
00306 cntr++;
00307
00308
00309
00310 for(int pix=1; pix<=fNPixels; pix++) pmttb.GetPixelBucket(pix).SetCharge(0);
00311
00312 MSG("DetSim", Msg::kVerbose)<<"Iterating over hit pixels."<<endl;
00313 for(Int_t hitpix = 1; hitpix <= fNPixels; hitpix++) {
00314 MSG("DetSim", Msg::kVerbose)<<"Working on pixel: "<<hitpix<<endl;
00315 bool oksim=false;
00316 for(Int_t spot = 1; spot <= fNSpots; spot++) {
00317 const double hitpe = pmttb.GetPixelBucket(hitpix).GetPEXtalk(spot);
00318 MSG("DetSim", Msg::kVerbose)<<"Working on spot: "<<spot
00319 <<" got hitpe= "<<hitpe<<endl;
00320 if(hitpe<=0.0) continue;
00321
00322 std::vector<double> qvec(fNPixels+1, 0.0);
00323 std::vector<double> tvec(fNPixels+1, 0.0);
00324 MSG("DetSim", Msg::kVerbose)<<"Calling OneHitSimDynodeChain("
00325 <<hitpix<<", "<<spot<<", "<<hitpe
00326 <<", qvec, tvec)"<<endl;
00327 oksim = OneHitSimDynodeChain(hitpix, spot, hitpe, qvec, tvec);
00328
00329 if(oksim){
00330 for(int outpix=1; outpix<=fNPixels; outpix++){
00331 const double generated_charge = qvec[outpix-1];
00332 MSG("DetSim", Msg::kVerbose)<<"Generated charge="<<generated_charge
00333 <<" on pixel "<<outpix
00334 <<endl;
00335 pmttb.GetPixelBucket(outpix).AddCharge(generated_charge);
00336
00337 if(generated_charge>0 && outpix!=hitpix){
00338
00339 pmttb.GetPixelBucket(outpix).
00340 SetTruthBit(DigiSignal::kCrosstalk);
00341 }
00342 }
00343 pmttb.AddTotalCharge(qvec[fNPixels]);
00344 fTotalCharge+=qvec[fNPixels];
00345
00346 }
00347 else{
00348 MSG("DetSim", Msg::kError)<<"Error in OneHitSimDynodeChain.\nDid not place any charge into the time bucket.\nWill try to continue."<<endl;
00349 }
00350 }
00351 }
00352 }
00353 }
00354
00355 bool SimPmtUTM16::OneHitSimDynodeChain(Int_t hp, Int_t hs, Float_t hpe,
00356 std::vector<double>& qvec,
00357 std::vector<double>& tvec)
00358 {
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382 if(hpe<=0.0) return true;
00383
00384
00385 const int hpit=hp-1;
00386 const int hsit=hs-1;
00387 MSG("DetSim", Msg::kVerbose)<<"Calculating gains."<<endl;
00388
00389 double gains[16];
00390 for(int pxit=0; pxit<16; pxit++){
00391 gains[pxit]=1.0;
00392 for(int stage=0; stage<12; stage++){
00393
00394
00395 if(pxit==hpit) gains[pxit]*=fSECValues[hpit][hsit][stage];
00396
00397 else gains[pxit]*=fAverageSECValues[pxit][stage];
00398 }
00399 }
00400
00401
00402
00403
00404
00405
00406 unsigned int qarray[16]={0};
00407
00408
00409
00410 MSG("DetSim", Msg::kVerbose)<<"Looking up xtalk fractions."<<endl;
00411 double xtfrac[16]={0.0};
00412 for(int pxit=0; pxit<16; pxit++){
00413 if(pxit==hpit) continue;
00414 const double got_xtfrac=RetrieveElectricXtalkFraction(hp,hs,pxit+1);
00415 if(got_xtfrac<0.0) xtfrac[pxit]=0.0;
00416 else xtfrac[pxit]=got_xtfrac;
00417 }
00418
00419
00420 const unsigned int begin_charge=
00421 (unsigned int) ( fRandom->PoissonD(hpe*fSECValues[hpit][hsit][0]) );
00422 qarray[hpit]=begin_charge;
00423 MSG("DetSim", Msg::kVerbose)<<"Charge after stage 1"<<endl;
00424 PrintCharge(qarray);
00425
00426
00427
00428
00429
00430
00431 for(int stage=1; stage<12; stage++){
00432
00433 if(fsPmtDoChargeCrosstalk){
00434 for(int pxit=0; pxit<16; pxit++){
00435 if(pxit==hpit) continue;
00436 if(qarray[hpit]<=0.0) break;
00437
00438 double working_xt_fraction
00439 =(xtfrac[pxit]+gElectricXTOffset)*gElectricXTScale/11.0;
00440
00441 working_xt_fraction *= fsPmtScaleChargeCrosstalk;
00442 qarray[pxit]+=(unsigned int)(fRandom->PoissonD(working_xt_fraction*qarray[hpit]));
00443 }
00444 }
00445
00446 for(int pxit=0; pxit<16; pxit++){
00447 if(qarray[pxit]<=0) continue;
00448 double current_sec=0.0;
00449 if(pxit==hpit) current_sec=fSECValues[hpit][hsit][stage];
00450 else current_sec=fAverageSECValues[hpit][stage];
00451
00452 if(stage==11 && fsPmtDoNonlinearity){
00453 current_sec=
00454 LastStageChargeNonLinearity(qarray[pxit],gains[pxit],current_sec);
00455 }
00456
00457 qarray[pxit]=(unsigned int)(fRandom->PoissonD(qarray[pxit]*current_sec));
00458
00459 }
00460 MSG("DetSim", Msg::kVerbose)<<"Charge after stage "<<(stage+1)<<endl;
00461 PrintCharge(qarray);
00462 }
00463
00464
00465
00466
00467 const double dynode_fraction=0.425;
00468
00469 for(int pxit=0; pxit<16; pxit++){
00470
00471 qvec[16]+=(qarray[pxit]/dynode_fraction)*Munits::e_SI;
00472
00473 qvec[pxit]+=qarray[pxit]*Munits::e_SI;
00474 }
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484 const double transit_time =
00485 fRandom->Gaus(gTransitTimeNS,
00486 (gTransitTimeJitterNS)/sqrt(hpe))*Munits::ns;
00487 for(int pxit=0; pxit<17; pxit++){
00488 tvec[pxit]+=transit_time;
00489 }
00490
00491 return true;
00492 }
00493
00494 double SimPmtUTM16::RetrieveElectricXtalkFraction(Int_t hp,
00495 Int_t hs,
00496 Int_t xp) const
00497 {
00498
00499
00500
00501
00502
00503
00504 double result=0.0;
00505 if(gCrosstalkTable){
00506 const SimPmtM16Crosstalk* row = gCrosstalkTable->GetRow(hp,hs,xp);
00507 if(row){
00508 result = row->GetElecFrac();
00509 static const MsgFormat ffmt("%9.3e");
00510 MSG("DetSim", Msg::kVerbose)
00511 <<"RetrieveElectricXtalkFraction("<<hp<<", "<<hs<<", "<<xp<<") "
00512 <<"will return "<<ffmt(result)<<" ."<<endl;
00513 }
00514 else{
00515 MSG("DetSim", Msg::kError)
00516 <<"RetrieveElectricXtalkFraction "
00517 <<"could not find a db row for:\n"
00518 <<"hit_pixel: "<< hp
00519 <<", hit_spot: "<<hs
00520 <<", xtalk_pixel: "<<xp
00521 <<"."<<endl;
00522 }
00523 }
00524 else{
00525 MSG("DetSim", Msg::kFatal)
00526 <<"Somehow RetrieveElectricXtalkFraction was called with "
00527 "gCrosstalkTable==0!\nThis is a fatal error.";
00528 assert(0);
00529 }
00530
00531 return result;
00532 }
00533
00534 double SimPmtUTM16::RetrieveOpticalXtalkFraction(Int_t hp,
00535 Int_t hs,
00536 Int_t xp) const
00537 {
00538
00539
00540
00541
00542
00543
00544 double result=0.0;
00545 if(gCrosstalkTable){
00546 const SimPmtM16Crosstalk* row = gCrosstalkTable->GetRow(hp,hs,xp);
00547 if(row){
00548 result = row->GetOptFrac();
00549 static const MsgFormat ffmt("%9.3e");
00550 MSG("DetSim", Msg::kVerbose)
00551 <<"RetrieveOpticalXtalkFraction("<<hp<<", "<<hs<<", "<<xp<<") "
00552 <<"will return "<<ffmt(result)<<" ."<<endl;
00553 }
00554 else{
00555 MSG("DetSim", Msg::kError)
00556 <<"RetrieveOpticalXtalkFraction "
00557 <<"could not find a db row for:\n"
00558 <<"hit_pixel: "<< hp
00559 <<", hit_spot: "<<hs
00560 <<", xtalk_pixel: "<<xp
00561 <<"."<<endl;
00562 }
00563 }
00564 else{
00565 MSG("DetSim", Msg::kFatal)
00566 <<"Somehow RetrieveOpticalXtalkFraction was called with "
00567 "gCrosstalkTable==0!\nThis is a fatal error.";
00568 assert(0);
00569 }
00570
00571 return result * fsPmtScaleOpticalCrosstalk;
00572 }
00573
00574
00575 bool SimPmtUTM16::InitSECValues()
00576 {
00577
00578
00579
00580
00581
00582
00583
00584 MSG("DetSim",Msg::kDebug) << "InitSECValues() Tube " << fTube.AsString() << endl;
00585
00586 static const double default_gain_last=gkDefaultSECValues[3]
00587 *gkDefaultSECValues[4]
00588 *gkDefaultSECValues[5]
00589 *gkDefaultSECValues[6]
00590 *gkDefaultSECValues[7]
00591 *gkDefaultSECValues[8]
00592 *gkDefaultSECValues[9]
00593 *gkDefaultSECValues[10]
00594 *gkDefaultSECValues[11];
00595
00596
00597
00598 for(int pxit=0; pxit<16; pxit++){
00599 const int pixel=pxit+1;
00600
00601 double ave_width=0.0;
00602 double ave_gain=0.0;
00603 for(int spit=0; spit<8; spit++){
00604 const int spot=spit+1;
00605 double pixel_gain, fractional_width;
00606 GetGainAndWidth(pixel,spot,pixel_gain,fractional_width);
00607
00608 const double default_fractional_width=0.5;
00609
00610 if(fractional_width<0.05 || fractional_width>5.0){
00611
00612 MAXMSG("DetSim", Msg::kError,50)
00613 <<"InitSECValues: GetGainAndWidth("<<pixel<<", "<<spot<<")"
00614 <<" returned fractional width " << fractional_width
00615 <<" which is outside the reasonable range!\n"
00616 <<" Will use the width "<< default_fractional_width
00617 <<" in further computations."<<endl;
00618 fractional_width=default_fractional_width;
00619 }
00620 const double sec = FractionalWidthToSEC(fractional_width);
00621 double width = fractional_width * pixel_gain;
00622 ave_width+=(width*width);
00623 ave_gain+=pixel_gain;
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633 fSECValues[pxit][spit][0]=sec;
00634 fSECValues[pxit][spit][1]=sec;
00635 fSECValues[pxit][spit][2]=sec;
00636
00637 const double pixel_gain_first=sec*sec*sec;
00638
00639 const double pixel_gain_last = pixel_gain/pixel_gain_first;
00640 const double rescale_factor =
00641 pow((pixel_gain_last/default_gain_last),1.0/9.0);
00642 for(int stage=3; stage<12; stage++) {
00643 fSECValues[pxit][spit][stage] =
00644 rescale_factor*gkDefaultSECValues[stage];
00645 }
00646 }
00647
00648
00649
00650
00651
00652 ave_gain/=8.0;
00653
00654
00655
00656 ave_width/=8.0;
00657 ave_width=sqrt(ave_width);
00658
00659
00660
00661 double ave_sec=FractionalWidthToSEC(ave_width/ave_gain);
00662
00663
00664 fAverageSECValues[pxit][0]=ave_sec;
00665 fAverageSECValues[pxit][1]=ave_sec;
00666 fAverageSECValues[pxit][2]=ave_sec;
00667
00668
00669
00670 const double ave_pixel_gain_first=ave_sec*ave_sec*ave_sec;
00671 const double ave_pixel_gain_last=ave_gain/ave_pixel_gain_first;
00672
00673
00674
00675
00676 const double ave_rescale_factor =
00677 pow((ave_pixel_gain_last/default_gain_last),1.0/9.0);
00678 for(int stage=3; stage<12; stage++) {
00679 fAverageSECValues[pxit][stage] =
00680 ave_rescale_factor*gkDefaultSECValues[stage];
00681 }
00682
00683 }
00684
00685
00686 MSG("DetSim", Msg::kVerbose)<<"Printing SEC values: "<<endl;
00687 for(int pxit=0; pxit<16; pxit++){
00688 for(int spit=0; spit<8; spit++){
00689 MSG("DetSim", Msg::kVerbose)
00690 <<"Stage SECs for pixel "<<pxit+1<<", and spot "<<spit+1<<endl;
00691 for(int stit=0; stit<12; stit++){
00692 MSG("DetSim", Msg::kVerbose)
00693 <<"stage "<<stit+1<<" : "<<fSECValues[pxit][spit][stit]<<" : (avg="
00694 <<fAverageSECValues[pxit][stit]<<")"<<endl;
00695 }
00696 }
00697 }
00698
00699 return true;
00700 }
00701
00702 double SimPmtUTM16::LastStageNonLinearity(double q, double gain, double lsec)
00703 {
00704 MSG("DetSim",Msg::kError) << "YOU ARE CALLING A DEPRECATED FUNCTION!"<<endl;
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738 double result=lsec;
00739 const double approx_npe=q/(gain/lsec);
00740 if(approx_npe>50.0 && approx_npe<=300.0){
00741 const double frac_gain_change=M16NonLinearity(approx_npe);
00742 result = (1.0+frac_gain_change)*lsec;
00743 }
00744 else if(approx_npe>300.0){
00745 const double frac_gain_change=M16NonLinearity(300.0);
00746 result = (1.0+frac_gain_change)*lsec;
00747 }
00748
00749 return result;
00750 }
00751
00752 double SimPmtUTM16::M16NonLinearity(double pe)
00753 {
00754 MSG("DetSim",Msg::kError) << "YOU ARE CALLING A DEPRECATED FUNCTION!"<<endl;
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777 static const double p[4]={5.0E-2,
00778 -1.4E-3,
00779 5.6E-6,
00780 -7.5E-9};
00781
00782 return p[0] + p[1]*pe + p[2]*pe*pe + p[3]*pe*pe*pe;
00783 }
00784
00785
00786 double SimPmtUTM16::LastStageChargeNonLinearity(double q,
00787 double , double lsec)
00788 {
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816 double result=lsec;
00817 const double provisional_charge=q*lsec;
00818 const double frac_gain_change = M16ChargeNonLinearity(provisional_charge);
00819 result = (1.0+frac_gain_change)*lsec;
00820
00821 return result;
00822 }
00823
00824 double SimPmtUTM16::M16ChargeNonLinearity(double nelectrons)
00825 {
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845 const double adc_per_electron = fsVaGain*Munits::e_SI;
00846
00847
00848
00849
00850 const double provisional_adc = nelectrons*adc_per_electron;
00851
00852
00853
00854
00855
00856
00857
00858 static const double p[2]={0.0166, -4.51e-6};
00859
00860 double gain_dip = p[0] + p[1]*provisional_adc;
00861
00862 const double linear_adc = 3600;
00863 const double high_adc = 60*300;
00864 if(provisional_adc<=linear_adc) {
00865 MSG("DetSim",Msg::kDebug) << " Tube in linear region: "
00866 <<" provisional adc = "<<provisional_adc
00867 <<" < linear_adc = "<<linear_adc<<"\n"
00868 <<" gain_dip set to zero "<<endl;
00869 gain_dip=0.0;
00870 }
00871 else if(provisional_adc>high_adc){
00872 gain_dip = p[0] + high_adc*p[1];
00873 MSG("DetSim",Msg::kDebug) << " Tube outside of parameterized region: "
00874 <<" provisional adc = "<<provisional_adc
00875 <<" < high_adc = "<<high_adc<<"\n"
00876 <<" gain_dip set to value at"
00877 <<high_adc<<" ADCs : "<<gain_dip<<endl;
00878
00879 }
00880
00881 MSG("DetSim",Msg::kDebug) << "[nelectrons, provisional adc, %gain_dip] : [ "
00882 << nelectrons<<" , "
00883 << provisional_adc<<" , "
00884 << gain_dip*100.0 <<" ]"<<endl;
00885
00886
00887 return gain_dip;
00888
00889 }
00890
00891
00892 Float_t SimPmtUTM16::GenDarkNoiseCharge( Int_t , Float_t )
00893 {
00894
00895 return 0;
00896 }
00897
00898 Float_t SimPmtUTM16::GenOpticalCrosstalk( Int_t hp, Int_t hs,
00899 Int_t xp, Float_t hpe )
00900 {
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927 if(hpe<=0.0) return 0.0;
00928
00929
00930 double k = RetrieveOpticalXtalkFraction(hp, hs, xp);
00931
00932 if(k<=0.0) return 0.0;
00933
00934
00935 k=(k+gOpticalXTOffset)*gOpticalXTScale;
00936
00937
00938 if(k<=0.0) return 0.0;
00939
00940
00941 Int_t xpe = fRandom->Poisson(k*hpe);
00942 if(xpe<0) xpe=0;
00943
00944 return xpe;
00945
00946 }
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962 void SimPmtUTM16::PrintCharge(unsigned int* qarray){
00963 static const MsgFormat ifmt("%10d");
00964
00965 MSG("DetSim", Msg::kVerbose)
00966 <<"================================================================================\n"
00967 <<ifmt(qarray[3])<<" "<<ifmt(qarray[2])<<" "<<ifmt(qarray[1])<<" "<<ifmt(qarray[0])
00968 <<"\n"
00969 <<ifmt(qarray[7])<<" "<<ifmt(qarray[6])<<" "<<ifmt(qarray[5])<<" "<<ifmt(qarray[4])
00970 <<"\n"
00971 <<ifmt(qarray[11])<<" "<<ifmt(qarray[10])<<" "<<ifmt(qarray[9])<<" "<<ifmt(qarray[8])
00972 <<"\n"
00973 <<ifmt(qarray[15])<<" "<<ifmt(qarray[14])<<" "<<ifmt(qarray[13])<<" "<<ifmt(qarray[12])
00974 <<"\n"
00975 <<"================================================================================\n"
00976
00977 <<endl;
00978 }
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996