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.24 2008/05/14 22:40:10 pawloski 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
00088
00089
00090
00091
00092
00093
00094
00095 adjacentPixels[0].insert(1);
00096 adjacentPixels[0].insert(4);
00097 adjacentPixels[1].insert(0);
00098 adjacentPixels[1].insert(2);
00099 adjacentPixels[1].insert(5);
00100 adjacentPixels[2].insert(1);
00101 adjacentPixels[2].insert(3);
00102 adjacentPixels[2].insert(6);
00103 adjacentPixels[3].insert(2);
00104 adjacentPixels[3].insert(7);
00105 adjacentPixels[4].insert(0);
00106 adjacentPixels[4].insert(5);
00107 adjacentPixels[4].insert(8);
00108 adjacentPixels[5].insert(1);
00109 adjacentPixels[5].insert(4);
00110 adjacentPixels[5].insert(6);
00111 adjacentPixels[5].insert(9);
00112 adjacentPixels[6].insert(2);
00113 adjacentPixels[6].insert(5);
00114 adjacentPixels[6].insert(7);
00115 adjacentPixels[6].insert(10);
00116 adjacentPixels[7].insert(3);
00117 adjacentPixels[7].insert(6);
00118 adjacentPixels[7].insert(11);
00119 adjacentPixels[8].insert(4);
00120 adjacentPixels[8].insert(9);
00121 adjacentPixels[8].insert(12);
00122 adjacentPixels[9].insert(5);
00123 adjacentPixels[9].insert(8);
00124 adjacentPixels[9].insert(10);
00125 adjacentPixels[9].insert(13);
00126 adjacentPixels[10].insert(6);
00127 adjacentPixels[10].insert(9);
00128 adjacentPixels[10].insert(11);
00129 adjacentPixels[10].insert(14);
00130 adjacentPixels[11].insert(7);
00131 adjacentPixels[11].insert(10);
00132 adjacentPixels[11].insert(15);
00133 adjacentPixels[12].insert(8);
00134 adjacentPixels[12].insert(13);
00135 adjacentPixels[13].insert(9);
00136 adjacentPixels[13].insert(12);
00137 adjacentPixels[13].insert(14);
00138 adjacentPixels[14].insert(10);
00139 adjacentPixels[14].insert(13);
00140 adjacentPixels[14].insert(15);
00141 adjacentPixels[15].insert(11);
00142 adjacentPixels[15].insert(14);
00143
00144 diagonalPixels[0].insert(5);
00145 diagonalPixels[1].insert(4);
00146 diagonalPixels[1].insert(6);
00147 diagonalPixels[2].insert(5);
00148 diagonalPixels[2].insert(7);
00149 diagonalPixels[3].insert(6);
00150 diagonalPixels[4].insert(1);
00151 diagonalPixels[4].insert(9);
00152 diagonalPixels[5].insert(0);
00153 diagonalPixels[5].insert(2);
00154 diagonalPixels[5].insert(8);
00155 diagonalPixels[5].insert(10);
00156 diagonalPixels[6].insert(1);
00157 diagonalPixels[6].insert(3);
00158 diagonalPixels[6].insert(9);
00159 diagonalPixels[6].insert(11);
00160 diagonalPixels[7].insert(2);
00161 diagonalPixels[7].insert(10);
00162 diagonalPixels[8].insert(5);
00163 diagonalPixels[8].insert(13);
00164 diagonalPixels[9].insert(4);
00165 diagonalPixels[9].insert(6);
00166 diagonalPixels[9].insert(12);
00167 diagonalPixels[9].insert(14);
00168 diagonalPixels[10].insert(5);
00169 diagonalPixels[10].insert(7);
00170 diagonalPixels[10].insert(13);
00171 diagonalPixels[10].insert(15);
00172 diagonalPixels[11].insert(6);
00173 diagonalPixels[11].insert(14);
00174 diagonalPixels[12].insert(9);
00175 diagonalPixels[13].insert(8);
00176 diagonalPixels[13].insert(10);
00177 diagonalPixels[14].insert(9);
00178 diagonalPixels[14].insert(11);
00179 diagonalPixels[15].insert(10);
00180
00181 MSG("DetSim",Msg::kDebug) << "SimPmtUTM16 Constructor, Tube :" << fTube.AsString() << endl;
00182
00183 }
00184
00185 void SimPmtUTM16::Reset( const VldContext& context )
00186 {
00187
00188
00189
00190
00191
00192
00193 SimPmt::Reset(context);
00194
00195
00196
00197 if(gCrosstalkTable) {
00198 gCrosstalkTable->Reset(context);
00199 } else {
00200 gCrosstalkTable = new SimPmtM16CrosstalkTable(context);
00201 }
00202
00203
00204 if(fsRebuildGainMap || (!fSECValuesBuilt))
00205 fSECValuesBuilt = InitSECValues();
00206
00207 if(!fSECValuesBuilt){
00208 MSG("DetSim", Msg::kFatal)<<"Could not initialize SEC values!"<<endl;
00209 assert(0);
00210 }
00211 }
00212
00213 void SimPmtUTM16::SimulatePmt()
00214 {
00215
00216
00217 if(fsPmtDoOpticalCrosstalk) SimulateOpticalXtalk();
00218 else CopyPEtoPEXtalk();
00219
00220 if(fsPmtDoDarkNoise) SimulateDarkNoise();
00221 SimulateCharges();
00222
00223 }
00224
00225
00226 void SimPmtUTM16::Print(Option_t* option) const
00227 {
00228 printf(" SimPmtUTM16::Print() -- Tube: %s\n",fTube.AsString("t"));
00229
00230 std::string opt = option;
00231 Bool_t spots = (opt.find("spot") != std::string::npos);;
00232
00233 for(SimPmtBucketIterator it(*this); !it.End(); it.Next()) {
00234 int ibucket = it.BucketId();
00235
00236
00237 printf(" Time bucket %d:\n",ibucket);
00238
00239
00240
00241
00242
00243
00244
00245 if(spots) {
00246 printf(" Photoelectrons(Npe)\n");
00247 for(int row=0; row < 4; row++) {
00248 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",
00249 GetPe(16-row*4,8,ibucket), GetPe(16-row*4,7,ibucket), GetPe(16-row*4,6,ibucket),
00250 GetPe(15-row*4,8,ibucket), GetPe(15-row*4,7,ibucket), GetPe(15-row*4,6,ibucket),
00251 GetPe(14-row*4,8,ibucket), GetPe(14-row*4,7,ibucket), GetPe(14-row*4,6,ibucket),
00252 GetPe(13-row*4,8,ibucket), GetPe(13-row*4,7,ibucket), GetPe(13-row*4,6,ibucket));
00253
00254 printf(" %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f %5.0f\n",
00255 GetPe(16-row*4,5,ibucket), GetPe(16-row*4,4,ibucket),
00256 GetPe(15-row*4,5,ibucket), GetPe(15-row*4,4,ibucket),
00257 GetPe(14-row*4,5,ibucket), GetPe(14-row*4,4,ibucket),
00258 GetPe(13-row*4,5,ibucket), GetPe(13-row*4,4,ibucket));
00259
00260 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",
00261 GetPe(16-row*4,3,ibucket), GetPe(16-row*4,2,ibucket), GetPe(16-row*4,1,ibucket),
00262 GetPe(15-row*4,3,ibucket), GetPe(15-row*4,2,ibucket), GetPe(15-row*4,1,ibucket),
00263 GetPe(14-row*4,3,ibucket), GetPe(14-row*4,2,ibucket), GetPe(14-row*4,1,ibucket),
00264 GetPe(13-row*4,3,ibucket), GetPe(13-row*4,2,ibucket), GetPe(13-row*4,1,ibucket));
00265
00266 printf("\n");
00267 }
00268 }
00269
00270
00271 printf("\n PEs PE with Xtalk Charges (fC)\n");
00272 for(int row=0; row < 4; row++) {
00273 printf(" %4d %4d %4d %4d",
00274 (int)GetPe(16-row*4,0,ibucket), (int)GetPe(15-row*4,0,ibucket),
00275 (int)GetPe(14-row*4,0,ibucket), (int)GetPe(13-row*4,0,ibucket));
00276
00277 printf(" %4d %4d %4d %4d",
00278 (int)GetPeXtalk(16-row*4,0,ibucket), (int)GetPeXtalk(15-row*4,0,ibucket),
00279 (int)GetPeXtalk(14-row*4,0,ibucket), (int)GetPeXtalk(13-row*4,0,ibucket));
00280
00281 printf(" %6.0f %6.0f %6.0f %6.0f\n",
00282 GetCharge(16-row*4,ibucket)/Munits::fC, GetCharge(15-row*4,ibucket)/Munits::fC,
00283 GetCharge(14-row*4,ibucket)/Munits::fC, GetCharge(13-row*4,ibucket)/Munits::fC);
00284 }
00285
00286 }
00287
00288 printf("\n");
00289 }
00290
00291 double SimPmtUTM16::FractionalWidthToSEC(double fw)
00292 {
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323 const double default_sec=-1.0;
00324
00325
00326
00327
00328
00329 if((fw<0.01) || (fw>3.0)){
00330 MAXMSG("DetSim", Msg::kWarning,20)
00331 <<"FractionalWidthToSEC was passed the fractional width: "<<fw
00332 <<" which is not a reasonable value.\n"
00333 <<"This routine will return "<<default_sec
00334 <<" as the secondary emmission coefficient."<<endl;
00335
00336 return default_sec;
00337 }
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357 const double Q = -2.0/9.0;
00358 const double R = (-7.0 - 27.0*fw*fw)/54.0;
00359
00360 double secinv=1.0/default_sec;
00361 if( (R*R)<(Q*Q*Q)) {
00362
00363 MSG("DetSim", Msg::kVerbose)<<"FractionalWidthToSEC("<<fw<<") has 3 real roots!\n"
00364 <<"Will use first order value: "<<1.0/(fw*fw)<<endl;
00365 return 1.0/(fw*fw);
00366 }
00367 else{
00368 const double A = pow(fabs(R) + sqrt(R*R - Q*Q*Q),1.0/3.0);
00369 double B=0.0; if(A!=0.0) B=Q/A;
00370 secinv = (A+B) - 1.0/3.0;
00371 MSG("DetSim", Msg::kVerbose)<<"FractionalWidthToSEC("<<fw<<") has 1 real root="
00372 <<1.0/secinv<<endl;
00373 }
00374
00375 const double sec=1.0/secinv;
00376 return sec;
00377
00378 }
00379
00380 void SimPmtUTM16::SimulateCharges()
00381 {
00382
00383 MSG("DetSim", Msg::kVerbose)
00384 <<"Calling SimPmtUTM16::SimulateCharges()"<<endl;
00385
00386
00387
00388
00389
00390
00391
00392 fTotalCharge = 0;
00393
00394 MSG("DetSim", Msg::kVerbose)<<"Iterating over time buckets now."<<endl;
00395
00396 int cntr=1;
00397 for(SimPmtBucketIterator it(*this); !it.End(); it.Next()) {
00398 SimPmtTimeBucket& pmttb = it.Bucket();
00399 MSG("DetSim", Msg::kVerbose)<<"Working on time bucket: "<<cntr<<endl;
00400 cntr++;
00401
00402
00403
00404 for(int pix=1; pix<=fNPixels; pix++) pmttb.GetPixelBucket(pix).SetCharge(0);
00405
00406 MSG("DetSim", Msg::kVerbose)<<"Iterating over hit pixels."<<endl;
00407 for(Int_t hitpix = 1; hitpix <= fNPixels; hitpix++) {
00408 MSG("DetSim", Msg::kVerbose)<<"Working on pixel: "<<hitpix<<endl;
00409 bool oksim=false;
00410 for(Int_t spot = 1; spot <= fNSpots; spot++) {
00411 const double hitpe = pmttb.GetPixelBucket(hitpix).GetPEXtalk(spot);
00412 MSG("DetSim", Msg::kVerbose)<<"Working on spot: "<<spot
00413 <<" got hitpe= "<<hitpe<<endl;
00414 if(hitpe<=0.0) continue;
00415
00416 std::vector<double> qvec(fNPixels+1, 0.0);
00417 std::vector<double> tvec(fNPixels+1, 0.0);
00418 MSG("DetSim", Msg::kVerbose)<<"Calling OneHitSimDynodeChain("
00419 <<hitpix<<", "<<spot<<", "<<hitpe
00420 <<", qvec, tvec)"<<endl;
00421 oksim = OneHitSimDynodeChain(hitpix, spot, hitpe, qvec, tvec);
00422
00423 if(oksim){
00424 for(int outpix=1; outpix<=fNPixels; outpix++){
00425 const double generated_charge = qvec[outpix-1];
00426 MSG("DetSim", Msg::kVerbose)<<"Generated charge="<<generated_charge
00427 <<" on pixel "<<outpix
00428 <<endl;
00429 pmttb.GetPixelBucket(outpix).AddCharge(generated_charge);
00430
00431 if(generated_charge>0 && outpix!=hitpix){
00432
00433 pmttb.GetPixelBucket(outpix).
00434 SetTruthBit(DigiSignal::kCrosstalk);
00435 }
00436 }
00437 pmttb.AddTotalCharge(qvec[fNPixels]);
00438 fTotalCharge+=qvec[fNPixels];
00439
00440 }
00441 else{
00442 MSG("DetSim", Msg::kError)<<"Error in OneHitSimDynodeChain.\nDid not place any charge into the time bucket.\nWill try to continue."<<endl;
00443 }
00444 }
00445 }
00446 }
00447 }
00448
00449 bool SimPmtUTM16::OneHitSimDynodeChain(Int_t hp, Int_t hs, Float_t hpe,
00450 std::vector<double>& qvec,
00451 std::vector<double>& tvec)
00452 {
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476 if(hpe<=0.0) return true;
00477
00478
00479 const int hpit=hp-1;
00480 const int hsit=hs-1;
00481 MSG("DetSim", Msg::kVerbose)<<"Calculating gains."<<endl;
00482
00483 double gains[16];
00484 for(int pxit=0; pxit<16; pxit++){
00485 gains[pxit]=1.0;
00486 for(int stage=0; stage<12; stage++){
00487
00488
00489 if(pxit==hpit) gains[pxit]*=fSECValues[hpit][hsit][stage];
00490
00491 else gains[pxit]*=fAverageSECValues[pxit][stage];
00492 }
00493 }
00494
00495
00496
00497
00498
00499
00500 unsigned int qarray[16]={0};
00501
00502
00503
00504 MSG("DetSim", Msg::kVerbose)<<"Looking up xtalk fractions."<<endl;
00505 double xtfrac[16]={0.0};
00506 for(int pxit=0; pxit<16; pxit++){
00507 if(pxit==hpit) continue;
00508 const double got_xtfrac=RetrieveElectricXtalkFraction(hp,hs,pxit+1);
00509 if(got_xtfrac<0.0) xtfrac[pxit]=0.0;
00510 else xtfrac[pxit]=got_xtfrac;
00511 }
00512
00513
00514 const unsigned int begin_charge=
00515 (unsigned int) ( fRandom->PoissonD(hpe*fSECValues[hpit][hsit][0]) );
00516 qarray[hpit]=begin_charge;
00517 MSG("DetSim", Msg::kVerbose)<<"Charge after stage 1"<<endl;
00518 PrintCharge(qarray);
00519
00520
00521
00522
00523
00524
00525 for(int stage=1; stage<12; stage++){
00526
00527 if(fsPmtDoChargeCrosstalk){
00528 for(int pxit=0; pxit<16; pxit++){
00529 if(pxit==hpit) continue;
00530 if(qarray[hpit]<=0.0) break;
00531
00532 double working_xt_fraction
00533 =(xtfrac[pxit]+gElectricXTOffset)*gElectricXTScale/11.0;
00534
00535 working_xt_fraction *= fsPmtScaleChargeCrosstalk;
00536
00537
00538 working_xt_fraction *= ScaleDiagonalAdjacentChargeCrosstalk(pxit,hpit);
00539
00540 qarray[pxit]+=(unsigned int)(fRandom->PoissonD(working_xt_fraction*qarray[hpit]));
00541 }
00542 }
00543
00544 for(int pxit=0; pxit<16; pxit++){
00545 if(qarray[pxit]<=0) continue;
00546 double current_sec=0.0;
00547 if(pxit==hpit) current_sec=fSECValues[hpit][hsit][stage];
00548 else current_sec=fAverageSECValues[hpit][stage];
00549
00550 if(stage==11 && fsPmtDoNonlinearity){
00551 current_sec=
00552 LastStageChargeNonLinearity(qarray[pxit],gains[pxit],current_sec);
00553 }
00554
00555 qarray[pxit]=(unsigned int)(fRandom->PoissonD(qarray[pxit]*current_sec));
00556
00557 }
00558 MSG("DetSim", Msg::kVerbose)<<"Charge after stage "<<(stage+1)<<endl;
00559 PrintCharge(qarray);
00560 }
00561
00562
00563
00564
00565 const double dynode_fraction=0.425;
00566
00567 for(int pxit=0; pxit<16; pxit++){
00568
00569 qvec[16]+=(qarray[pxit]/dynode_fraction)*Munits::e_SI;
00570
00571 qvec[pxit]+=qarray[pxit]*Munits::e_SI;
00572 }
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582 const double transit_time =
00583 fRandom->Gaus(gTransitTimeNS,
00584 (gTransitTimeJitterNS)/sqrt(hpe))*Munits::ns;
00585 for(int pxit=0; pxit<17; pxit++){
00586 tvec[pxit]+=transit_time;
00587 }
00588
00589 return true;
00590 }
00591
00592
00593
00594
00595 double SimPmtUTM16::ScaleDiagonalAdjacentChargeCrosstalk(Int_t pxit, Int_t hpit) const
00596 {
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610 double scale=1.0;
00611 if(
00612 pxit < 0
00613 || pxit > 15
00614 || hpit < 0
00615 || hpit > 15
00616 )
00617 {
00618 return(1.0);
00619 }
00620 bool isAdjacentPixels = (adjacentPixels[hpit].find(pxit) != adjacentPixels[hpit].end());
00621 bool isDiagonalPixels = (diagonalPixels[hpit].find(pxit) != diagonalPixels[hpit].end());
00622
00623 if(isAdjacentPixels)
00624 {
00625 scale = fsPmtScaleAdjacentChargeCrosstalk;
00626 }
00627 else if(isDiagonalPixels)
00628 {
00629 scale = fsPmtScaleDiagonalChargeCrosstalk;
00630 }
00631
00632 return(scale);
00633
00634 }
00635
00636 double SimPmtUTM16::ScaleDiagonalAdjacentOpticalCrosstalk(Int_t pxit, Int_t hpit) const
00637 {
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651 double scale=1.0;
00652 if(
00653 pxit < 0
00654 || pxit > 15
00655 || hpit < 0
00656 || hpit > 15
00657 )
00658 {
00659 return(1.0);
00660 }
00661 bool isAdjacentPixels = (adjacentPixels[hpit].find(pxit) != adjacentPixels[hpit].end());
00662 bool isDiagonalPixels = (diagonalPixels[hpit].find(pxit) != diagonalPixels[hpit].end());
00663
00664 if(isAdjacentPixels)
00665 {
00666 scale = fsPmtScaleAdjacentOpticalCrosstalk;
00667 }
00668 else if(isDiagonalPixels)
00669 {
00670 scale = fsPmtScaleDiagonalOpticalCrosstalk;
00671 }
00672
00673 return(scale);
00674
00675 }
00676
00677
00678
00679
00680
00681 double SimPmtUTM16::RetrieveElectricXtalkFraction(Int_t hp,
00682 Int_t hs,
00683 Int_t xp) const
00684 {
00685
00686
00687
00688
00689
00690
00691 double result=0.0;
00692 if(gCrosstalkTable){
00693 const SimPmtM16Crosstalk* row = gCrosstalkTable->GetRow(hp,hs,xp);
00694 if(row){
00695 result = row->GetElecFrac();
00696 static const MsgFormat ffmt("%9.3e");
00697 MSG("DetSim", Msg::kVerbose)
00698 <<"RetrieveElectricXtalkFraction("<<hp<<", "<<hs<<", "<<xp<<") "
00699 <<"will return "<<ffmt(result)<<" ."<<endl;
00700 }
00701 else{
00702 MSG("DetSim", Msg::kError)
00703 <<"RetrieveElectricXtalkFraction "
00704 <<"could not find a db row for:\n"
00705 <<"hit_pixel: "<< hp
00706 <<", hit_spot: "<<hs
00707 <<", xtalk_pixel: "<<xp
00708 <<"."<<endl;
00709 }
00710 }
00711 else{
00712 MSG("DetSim", Msg::kFatal)
00713 <<"Somehow RetrieveElectricXtalkFraction was called with "
00714 "gCrosstalkTable==0!\nThis is a fatal error.";
00715 assert(0);
00716 }
00717
00718 return result;
00719 }
00720
00721 double SimPmtUTM16::RetrieveOpticalXtalkFraction(Int_t hp,
00722 Int_t hs,
00723 Int_t xp) const
00724 {
00725
00726
00727
00728
00729
00730
00731 double result=0.0;
00732 if(gCrosstalkTable){
00733 const SimPmtM16Crosstalk* row = gCrosstalkTable->GetRow(hp,hs,xp);
00734 if(row){
00735 result = row->GetOptFrac();
00736 static const MsgFormat ffmt("%9.3e");
00737 MSG("DetSim", Msg::kVerbose)
00738 <<"RetrieveOpticalXtalkFraction("<<hp<<", "<<hs<<", "<<xp<<") "
00739 <<"will return "<<ffmt(result)<<" ."<<endl;
00740 }
00741 else{
00742 MSG("DetSim", Msg::kError)
00743 <<"RetrieveOpticalXtalkFraction "
00744 <<"could not find a db row for:\n"
00745 <<"hit_pixel: "<< hp
00746 <<", hit_spot: "<<hs
00747 <<", xtalk_pixel: "<<xp
00748 <<"."<<endl;
00749 }
00750 }
00751 else{
00752 MSG("DetSim", Msg::kFatal)
00753 <<"Somehow RetrieveOpticalXtalkFraction was called with "
00754 "gCrosstalkTable==0!\nThis is a fatal error.";
00755 assert(0);
00756 }
00757
00758
00759
00760
00761
00762 result *= fsPmtScaleOpticalCrosstalk;
00763
00764 result *= ScaleDiagonalAdjacentOpticalCrosstalk(xp-1,hp-1);
00765 return result;
00766
00767 }
00768
00769
00770 bool SimPmtUTM16::InitSECValues()
00771 {
00772
00773
00774
00775
00776
00777
00778
00779 MSG("DetSim",Msg::kDebug) << "InitSECValues() Tube " << fTube.AsString() << endl;
00780
00781 static const double default_gain_last=gkDefaultSECValues[3]
00782 *gkDefaultSECValues[4]
00783 *gkDefaultSECValues[5]
00784 *gkDefaultSECValues[6]
00785 *gkDefaultSECValues[7]
00786 *gkDefaultSECValues[8]
00787 *gkDefaultSECValues[9]
00788 *gkDefaultSECValues[10]
00789 *gkDefaultSECValues[11];
00790
00791
00792
00793 for(int pxit=0; pxit<16; pxit++){
00794 const int pixel=pxit+1;
00795
00796 double ave_width=0.0;
00797 double ave_gain=0.0;
00798 for(int spit=0; spit<8; spit++){
00799 const int spot=spit+1;
00800 double pixel_gain, fractional_width;
00801 GetGainAndWidth(pixel,spot,pixel_gain,fractional_width);
00802
00803 const double default_fractional_width=0.5;
00804
00805 if(fractional_width<0.05 || fractional_width>5.0){
00806
00807 MAXMSG("DetSim", Msg::kError,50)
00808 <<"InitSECValues: GetGainAndWidth("<<pixel<<", "<<spot<<")"
00809 <<" returned fractional width " << fractional_width
00810 <<" which is outside the reasonable range!\n"
00811 <<" Will use the width "<< default_fractional_width
00812 <<" in further computations."<<endl;
00813 fractional_width=default_fractional_width;
00814 }
00815 const double sec = FractionalWidthToSEC(fractional_width);
00816 double width = fractional_width * pixel_gain;
00817 ave_width+=(width*width);
00818 ave_gain+=pixel_gain;
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828 fSECValues[pxit][spit][0]=sec;
00829 fSECValues[pxit][spit][1]=sec;
00830 fSECValues[pxit][spit][2]=sec;
00831
00832 const double pixel_gain_first=sec*sec*sec;
00833
00834 const double pixel_gain_last = pixel_gain/pixel_gain_first;
00835 const double rescale_factor =
00836 pow((pixel_gain_last/default_gain_last),1.0/9.0);
00837 for(int stage=3; stage<12; stage++) {
00838 fSECValues[pxit][spit][stage] =
00839 rescale_factor*gkDefaultSECValues[stage];
00840 }
00841 }
00842
00843
00844
00845
00846
00847 ave_gain/=8.0;
00848
00849
00850
00851 ave_width/=8.0;
00852 ave_width=sqrt(ave_width);
00853
00854
00855
00856 double ave_sec=FractionalWidthToSEC(ave_width/ave_gain);
00857
00858
00859 fAverageSECValues[pxit][0]=ave_sec;
00860 fAverageSECValues[pxit][1]=ave_sec;
00861 fAverageSECValues[pxit][2]=ave_sec;
00862
00863
00864
00865 const double ave_pixel_gain_first=ave_sec*ave_sec*ave_sec;
00866 const double ave_pixel_gain_last=ave_gain/ave_pixel_gain_first;
00867
00868
00869
00870
00871 const double ave_rescale_factor =
00872 pow((ave_pixel_gain_last/default_gain_last),1.0/9.0);
00873 for(int stage=3; stage<12; stage++) {
00874 fAverageSECValues[pxit][stage] =
00875 ave_rescale_factor*gkDefaultSECValues[stage];
00876 }
00877
00878 }
00879
00880
00881 MSG("DetSim", Msg::kVerbose)<<"Printing SEC values: "<<endl;
00882 for(int pxit=0; pxit<16; pxit++){
00883 for(int spit=0; spit<8; spit++){
00884 MSG("DetSim", Msg::kVerbose)
00885 <<"Stage SECs for pixel "<<pxit+1<<", and spot "<<spit+1<<endl;
00886 for(int stit=0; stit<12; stit++){
00887 MSG("DetSim", Msg::kVerbose)
00888 <<"stage "<<stit+1<<" : "<<fSECValues[pxit][spit][stit]<<" : (avg="
00889 <<fAverageSECValues[pxit][stit]<<")"<<endl;
00890 }
00891 }
00892 }
00893
00894 return true;
00895 }
00896
00897 double SimPmtUTM16::LastStageNonLinearity(double q, double gain, double lsec)
00898 {
00899 MSG("DetSim",Msg::kError) << "YOU ARE CALLING A DEPRECATED FUNCTION!"<<endl;
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
00928
00929
00930
00931
00932
00933 double result=lsec;
00934 const double approx_npe=q/(gain/lsec);
00935 if(approx_npe>50.0 && approx_npe<=300.0){
00936 const double frac_gain_change=M16NonLinearity(approx_npe);
00937 result = (1.0+frac_gain_change)*lsec;
00938 }
00939 else if(approx_npe>300.0){
00940 const double frac_gain_change=M16NonLinearity(300.0);
00941 result = (1.0+frac_gain_change)*lsec;
00942 }
00943
00944 return result;
00945 }
00946
00947 double SimPmtUTM16::M16NonLinearity(double pe)
00948 {
00949 MSG("DetSim",Msg::kError) << "YOU ARE CALLING A DEPRECATED FUNCTION!"<<endl;
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972 static const double p[4]={5.0E-2,
00973 -1.4E-3,
00974 5.6E-6,
00975 -7.5E-9};
00976
00977 return p[0] + p[1]*pe + p[2]*pe*pe + p[3]*pe*pe*pe;
00978 }
00979
00980
00981 double SimPmtUTM16::LastStageChargeNonLinearity(double q,
00982 double , double lsec)
00983 {
00984
00985
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011 double result=lsec;
01012 const double provisional_charge=q*lsec;
01013 const double frac_gain_change = M16ChargeNonLinearity(provisional_charge);
01014 result = (1.0+frac_gain_change)*lsec;
01015
01016 return result;
01017 }
01018
01019 double SimPmtUTM16::M16ChargeNonLinearity(double nelectrons)
01020 {
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037
01038
01039
01040 const double adc_per_electron = fsVaGain*Munits::e_SI;
01041
01042
01043
01044
01045 const double provisional_adc = nelectrons*adc_per_electron;
01046
01047
01048
01049
01050
01051
01052
01053 static const double p[2]={0.0166, -4.51e-6};
01054
01055 double gain_dip = p[0] + p[1]*provisional_adc;
01056
01057 const double linear_adc = 3600;
01058 const double high_adc = 60*300;
01059 if(provisional_adc<=linear_adc) {
01060 MSG("DetSim",Msg::kDebug) << " Tube in linear region: "
01061 <<" provisional adc = "<<provisional_adc
01062 <<" < linear_adc = "<<linear_adc<<"\n"
01063 <<" gain_dip set to zero "<<endl;
01064 gain_dip=0.0;
01065 }
01066 else if(provisional_adc>high_adc){
01067 gain_dip = p[0] + high_adc*p[1];
01068 MSG("DetSim",Msg::kDebug) << " Tube outside of parameterized region: "
01069 <<" provisional adc = "<<provisional_adc
01070 <<" < high_adc = "<<high_adc<<"\n"
01071 <<" gain_dip set to value at"
01072 <<high_adc<<" ADCs : "<<gain_dip<<endl;
01073
01074 }
01075
01076 MSG("DetSim",Msg::kDebug) << "[nelectrons, provisional adc, %gain_dip] : [ "
01077 << nelectrons<<" , "
01078 << provisional_adc<<" , "
01079 << gain_dip*100.0 <<" ]"<<endl;
01080
01081
01082 return gain_dip;
01083
01084 }
01085
01086
01087 Float_t SimPmtUTM16::GenDarkNoiseCharge( Int_t , Float_t )
01088 {
01089
01090 return 0;
01091 }
01092
01093 Float_t SimPmtUTM16::GenOpticalCrosstalk( Int_t hp, Int_t hs,
01094 Int_t xp, Float_t hpe )
01095 {
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122 if(hpe<=0.0) return 0.0;
01123
01124
01125 double k = RetrieveOpticalXtalkFraction(hp, hs, xp);
01126
01127 if(k<=0.0) return 0.0;
01128
01129
01130 k=(k+gOpticalXTOffset)*gOpticalXTScale;
01131
01132
01133 if(k<=0.0) return 0.0;
01134
01135
01136 Int_t xpe = fRandom->Poisson(k*hpe);
01137 if(xpe<0) xpe=0;
01138
01139 return xpe;
01140
01141 }
01142
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157 void SimPmtUTM16::PrintCharge(unsigned int* qarray){
01158 static const MsgFormat ifmt("%10d");
01159
01160 MSG("DetSim", Msg::kVerbose)
01161 <<"================================================================================\n"
01162 <<ifmt(qarray[3])<<" "<<ifmt(qarray[2])<<" "<<ifmt(qarray[1])<<" "<<ifmt(qarray[0])
01163 <<"\n"
01164 <<ifmt(qarray[7])<<" "<<ifmt(qarray[6])<<" "<<ifmt(qarray[5])<<" "<<ifmt(qarray[4])
01165 <<"\n"
01166 <<ifmt(qarray[11])<<" "<<ifmt(qarray[10])<<" "<<ifmt(qarray[9])<<" "<<ifmt(qarray[8])
01167 <<"\n"
01168 <<ifmt(qarray[15])<<" "<<ifmt(qarray[14])<<" "<<ifmt(qarray[13])<<" "<<ifmt(qarray[12])
01169 <<"\n"
01170 <<"================================================================================\n"
01171
01172 <<endl;
01173 }
01174
01175
01176
01177
01178
01179
01180
01181
01182
01183
01184
01185
01186
01187
01188
01189
01190
01191