CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_6_2_5/src/GeneratorInterface/CosmicMuonGenerator/src/CMSCGEN.cc

Go to the documentation of this file.
00001 //
00002 // CMSCGEN.cc   version 3.0    Thomas Hebbeker 2007-05-15  
00003 //
00004 // implemented in CMSSW by P. Biallass 2007-05-28 
00005 // see header for documentation and CMS internal note 2007 "Improved Parametrization of the Cosmic Muon Flux for the generator CMSCGEN" by Biallass + Hebbeker
00006 //
00007 
00008 #include <CLHEP/Random/RandomEngine.h>
00009 #include <CLHEP/Random/JamesRandom.h>
00010 
00011 #include "GeneratorInterface/CosmicMuonGenerator/interface/CMSCGEN.h"
00012  
00013 CMSCGEN::CMSCGEN() : initialization(0), RanGen2(0), delRanGen(false)
00014 {
00015 }
00016 
00017 CMSCGEN::~CMSCGEN()
00018 {
00019   if (delRanGen)
00020     delete RanGen2;
00021 }
00022 
00023 int CMSCGEN::initialize(double pmin_in, double pmax_in, double thetamin_in, double thetamax_in, CLHEP::HepRandomEngine *rnd, bool TIFOnly_constant, bool TIFOnly_linear)  
00024 {
00025   if (delRanGen)
00026     delete RanGen2;
00027   RanGen2 = rnd;
00028   delRanGen = false;
00029 
00030   //set bools for TIFOnly options (E<2GeV with unphysical energy dependence)
00031   TIFOnly_const = TIFOnly_constant;
00032   TIFOnly_lin = TIFOnly_linear;
00033 
00034 
00035   // units: GeV
00036 
00037   // WARNING: coordinate system: 
00038   //   - to outside world define z axis downwards, i.e.
00039   //          muon coming from above, vertically: cos = 1
00040   //      (used for backward compatibility)
00041   //   - internally use frame with z axis upwards, i.e.
00042   //          muon coming from above, vertically: cos = -1
00043   //     (corresponds to CMS note definition)
00044 
00045   //set cmin and cmax, here convert between coordinate systems:
00046   cmin_in = - TMath::Cos(thetamin_in);//input angle already converted from Deg to Rad!
00047   cmax_in = - TMath::Cos(thetamax_in);//input angle already converted from Deg to Rad!
00048 
00049 
00050   //allowed energy range
00051   pmin_min = 3.;
00052   //pmin_max = 100.;
00053   pmin_max = 3000.;
00054   pmax = 3000.;
00055   //allowed angular range
00056   //cmax_max = -0.1,
00057   cmax_max = -0.01,
00058   cmax_min = -0.9999;
00059 
00060  if(TIFOnly_const == true || TIFOnly_lin == true) pmin_min = 0.; //forTIF
00061 
00062   // set pmin  
00063   if(pmin_in < pmin_min || pmin_in > pmin_max){ 
00064     std::cout << " >>> CMSCGEN.initialize <<< warning: illegal pmin_in =" << pmin_in;
00065     return(-1);
00066   } else if(pmax_in > pmax ){
00067     std::cout << " >>> CMSCGEN.initialize <<< warning: illegal pmax_in =" << pmax_in;
00068     return(-1);
00069   }else{     
00070     pmin = pmin_in;
00071     pmax = pmax_in;
00072     xemax = 1./(pmin*pmin);
00073     xemin = 1./(pmax*pmax); 
00074   }
00075 
00076 
00077   // set cmax and cmin
00078   if(cmax_in < cmax_min || cmax_in > cmax_max)
00079     { 
00080       std::cout << " >>> CMSCGEN.initialize <<< warning: illegal cmax_in =" << cmax_in;
00081       return(-1);
00082     }
00083   else 
00084     {
00085       cmax = cmax_in;
00086       cmin = cmin_in;
00087     }
00088 
00089 
00090   initialization = 1;
00091 
00092   if(TIFOnly_const == true || TIFOnly_lin == true) pmin_min = 3.; //forTIF
00093 
00094   //  Lmin = log10(pmin_min);
00095   Lmin = log10(pmin);
00096   Lmax = log10(pmax);
00097   Lfac = 100./(Lmax-Lmin);
00098 
00099   //
00100   // +++ coefficients for energy spectrum
00101   //
00102 
00103   pe[0] = -1.;
00104   pe[1] = 6.22176;
00105   pe[2] = -13.9404;
00106   pe[3] = 18.1643;
00107   pe[4] = -9.22784;
00108   pe[5] = 1.99234;
00109   pe[6] = -0.156434;
00110   pe[7] = 0.;
00111   pe[8] = 0.;
00112 
00113   //
00114   // +++ coefficients for cos theta distribution
00115   //
00116 
00117   b0c[0] = 0.6639;
00118   b0c[1] = -0.9587;
00119   b0c[2] = 0.2772;
00120   
00121   b1c[0] = 5.820;
00122   b1c[1] = -6.864;
00123   b1c[2] = 1.367;
00124   
00125   b2c[0] = 10.39;
00126   b2c[1] = -8.593;
00127   b2c[2] = 1.547;
00128   
00129   //
00130   // +++ calculate correction table for different cos theta dependence!
00131   //     reference range: c1  to  c2 
00132   //
00133   //  explanation: the parametrization of the energy spectrum as used above
00134   //    is the integral over the c = cos(zenith angle) range -1...-0.1 
00135   //    since the c distribution depends on energy, the integrated energy
00136   //    spectrum depends on this range. Here a correction factor is determined,
00137   //    based on the linear c dependence of the c distribution.
00138   //    The correction is calculated for 100 bins in L = log10(energy).
00139   //
00140   // +++ in same loop calculate integrated flux 
00141   //      (integrated over angles and momentum)
00142 
00143   c1 = -1.;
00144   c2 = -0.1;
00145   
00146   double cemax0 = 1.0;
00147   double L, L2; 
00148   double s;
00149   double p, p1, p2;
00150   double integral_here, integral_ref;
00151   double c_cut;
00152 
00153   integrated_flux = 0.;
00154   
00155   for(int k=1; k<=100; k++)
00156     {
00157       L = Lmin + (k-0.5)/Lfac;
00158       L2 = L*L;   
00159       p = pow(10,L);
00160       p1 = pow(10,L-0.5/Lfac);
00161       p2 = pow(10,L+0.5/Lfac);
00162       
00163       b0 = b0c[0] + b0c[1] * L + b0c[2]* L2;
00164       b1 = b1c[0] + b1c[1] * L + b1c[2]* L2;
00165       b2 = b2c[0] + b2c[1] * L + b2c[2]* L2;
00166 
00167       // cut out explicitly regions of zero flux 
00168       // (for low momentum and near horizontal showers)
00169       // since parametrization for z distribution doesn't work here
00170       // (can become negative) 
00171       
00172       c_cut = -0.42 + L*0.35;
00173       
00174       if (c_cut > c2) c_cut = c2; 
00175       
00176       integral_ref = b0 * (c_cut - c1) 
00177         + b1/2. * (c_cut*c_cut - c1*c1) 
00178         + b2/3. * (c_cut*c_cut*c_cut - c1*c1*c1);
00179       
00180       if (c_cut > cmax) c_cut = cmax; 
00181       
00182       integral_here = b0 * (c_cut - cmin) 
00183         + b1/2. * (c_cut*c_cut - cmin*cmin) 
00184         + b2/3. * (c_cut*c_cut*c_cut - cmin*cmin*cmin);
00185   
00186       corr[k] = integral_here/integral_ref;
00187       
00188       s = (((((((pe[8]*L
00189                  +pe[7])*L
00190                 +pe[6])*L
00191                +pe[5])*L
00192               +pe[4])*L
00193              +pe[3])*L
00194             +pe[2])*L
00195            +pe[1])*L
00196         +pe[0];
00197     
00198       integrated_flux += 1./pow(p,3) * s * corr[k] * (p2-p1);
00199 
00200       /*
00201         std::cout << k << " " 
00202         << corr[k] << " " 
00203         << p << " " 
00204         << s << " " 
00205         << p1 << " " 
00206         << p2 << " " 
00207         << integrated_flux << " " 
00208         << std::endl;
00209       */
00210       
00211       // std::cout << k << " " << corr[k] << " " << std::endl;
00212     }
00213 
00214   integrated_flux *= 1.27E3;
00215   std::cout << " >>> CMSCGEN.initialize <<< " <<
00216     " Integrated flux = " << integrated_flux << " /m**2/s " << std::endl;
00217   
00218   //  find approximate peak value, for Monte Carlo sampling
00219   //      peak is near L = 2 
00220 
00221   double ce;
00222   
00223   ce = (((((((pe[8]*2.
00224               +pe[7])*2.
00225              +pe[6])*2.
00226             +pe[5])*2.
00227            +pe[4])*2.
00228           +pe[3])*2.
00229          +pe[2])*2.
00230         +pe[1])*2.
00231     +pe[0];
00232 
00233   // normalize to 0.5 (not 1) to have some margin if peak is not at L=2
00234   //
00235   ce = 0.5/ce;
00236 
00237   for (int k=0; k<9; k++)
00238     {
00239       pe[k] = pe[k]*ce;
00240     }
00241 
00242   cemax = cemax0*corr[50];      
00243 
00244   return initialization;
00245 }
00246 
00247 int CMSCGEN::initialize(double pmin_in, double pmax_in, double thetamin_in, double thetamax_in, int RanSeed, bool TIFOnly_constant, bool TIFOnly_linear)
00248 {
00249   CLHEP::HepRandomEngine *rnd = new CLHEP::HepJamesRandom;
00250   //set seed for Random Generator (seed can be controled by config-file), P.Biallass 2006
00251   rnd->setSeed(RanSeed, 0);
00252   delRanGen = true;
00253   return initialize(pmin_in, pmax_in, thetamin_in, thetamax_in, rnd, TIFOnly_constant, TIFOnly_linear);
00254 }
00255 
00256 int CMSCGEN::generate()
00257 {
00258 
00259   if(initialization==0)
00260     {
00261       std::cout << " >>> CMSCGEN <<< warning: not initialized" << std::endl;
00262       return -1;
00263     }
00264   
00265   // note: use historical notation (fortran version l3cgen.f)
00266 
00267   //
00268   // +++ determine x = 1/e**2
00269   //
00270   //  explanation: the energy distribution 
00271   //        dn/d(1/e**2) = dn/de * e**3 = dn/dlog10(e) * e**2
00272   //     is parametrized by a polynomial. accordingly xe = 1/e**2 is sampled
00273   //     and e calculated 
00274   //
00275   //     need precise random variable with high precison since for 
00276   //     emin = 3 GeV energies around 3000 GeV are very rare!
00277   //
00278 
00279   double r1, r2, r3;
00280   double xe, e, ce, L, L2;
00281   int k;    
00282   double prob; 
00283   
00284   double c_max;
00285   double z, z_max;
00286   
00287   while (1)
00288     {
00289       prob = RanGen2->flat();
00290       r1 = double(prob);
00291       prob = RanGen2->flat();
00292       r2 = double(prob);
00293       
00294       xe = xemin+r1*(xemax-xemin);
00295       
00296       if( (1./sqrt(xe)<3) && TIFOnly_const == true) { //generate constant energy dependence for E<2GeV, only used for TIF
00297         //compute constant to match to CMSCGEN spectrum
00298         e=3.;      
00299         L = log10(e);
00300         L2 = L*L;
00301         
00302         ce = (((((((pe[8]*L
00303                     +pe[7])*L
00304                    +pe[6])*L
00305                   +pe[5])*L
00306                  +pe[4])*L
00307                 +pe[3])*L
00308                +pe[2])*L
00309               +pe[1])*L
00310           +pe[0];
00311         
00312         k = int ((L-Lmin)*Lfac+1.);
00313         k = TMath::Max(1,TMath::Min(k,100));
00314         ce = ce * corr[k];
00315         
00316         e = 1./sqrt(xe);  
00317         if(r2 < ( e*e*e*ce/(cemax*3.*3.*3.) )) break;
00318       
00319       }else if( (1./sqrt(xe)<3) && TIFOnly_lin == true) { //generate linear energy dependence for E<2GeV, only used for TIF
00320         //compute constant to match to CMSCGEN spectrum
00321         e=3.;      
00322         L = log10(e);
00323         L2 = L*L;
00324         
00325         ce = (((((((pe[8]*L
00326                     +pe[7])*L
00327                    +pe[6])*L
00328                   +pe[5])*L
00329                  +pe[4])*L
00330                 +pe[3])*L
00331                +pe[2])*L
00332               +pe[1])*L
00333           +pe[0];
00334       
00335         k = int ((L-Lmin)*Lfac+1.);
00336         k = TMath::Max(1,TMath::Min(k,100));
00337         ce = ce * corr[k];
00338       
00339         e = 1./sqrt(xe);  
00340         if(r2 < ( e*e*e*e*ce/(cemax*3.*3.*3.*3.) )) break;
00341       
00342       }else{ //this is real CMSCGEN energy-dependence
00343 
00344         e = 1./sqrt(xe);       
00345         L = log10(e);
00346         L2 = L*L;
00347         
00348         ce = (((((((pe[8]*L
00349                     +pe[7])*L
00350                    +pe[6])*L
00351                   +pe[5])*L
00352                  +pe[4])*L
00353                 +pe[3])*L
00354                +pe[2])*L
00355               +pe[1])*L
00356           +pe[0];
00357         
00358         k = int ((L-Lmin)*Lfac+1.);
00359         k = TMath::Max(1,TMath::Min(k,100));
00360         ce = ce * corr[k];
00361         
00362         if(cemax*r2 < ce) break;
00363 
00364       } //end of CMSCGEN energy-dependence
00365     } //end of while
00366   
00367   pq = e;
00368   
00369   //
00370   // +++ charge ratio 1.280
00371   //
00372   prob = RanGen2->flat();
00373   r3 = double(prob);
00374   
00375   double charg = 1.;
00376   if(r3 < 0.439) charg=-1.;
00377   
00378   pq = pq*charg;
00379   
00380   //
00381   //  +++ determine cos(angle)
00382   //
00383   //  simple trial and rejection method
00384   //
00385   
00386   // first calculate energy dependent coefficients b_i
00387 
00388   if(TIFOnly_const == true && e<3.){ //forTIF (when E<2GeV use angles of 2GeV cosmic)
00389     L = log10(3.);
00390     L2 = L*L;
00391   }
00392   if(TIFOnly_lin == true && e<3.){ //forTIF (when E<2GeV use angles of 2GeV cosmic)
00393     L = log10(3.);
00394     L2 = L*L; 
00395   }
00396   
00397   b0 = b0c[0] + b0c[1] * L + b0c[2]* L2;
00398   b1 = b1c[0] + b1c[1] * L + b1c[2]* L2;
00399   b2 = b2c[0] + b2c[1] * L + b2c[2]* L2;
00400   
00401   //
00402   // need to know the maximum of z(c)
00403   //
00404   // first calculate c for which c distribution z(c) = maximum 
00405   // 
00406   // (note: maximum of curve is NOT always at c = -1, but never at c = -0.1)   
00407   //
00408   
00409   // try extremal value (from z'(c) = 0), but only if z''(c) < 0  
00410   //
00411   // z'(c) = b1 + b2 * c   =>  at c_max = - b1 / (2 b_2) is z'(c) = 0
00412   //
00413   // z''(c) = b2 
00414   
00415   c_max = -1.;
00416   
00417   if(b2<0.) {
00418     c_max = - 0.5 * b1/b2;
00419     if(c_max < -1.) c_max = -1.;
00420     if(c_max > -0.1) c_max = -0.1;
00421   } 
00422   
00423   z_max = b0 + b1 * c_max + b2 * c_max * c_max;
00424 
00425   // again cut out explicitly regions of zero flux 
00426   double c_cut = -0.42 + L*0.35;
00427   if (c_cut > cmax) c_cut = cmax; 
00428   
00429   // now we throw dice:
00430   
00431   while (1)
00432     {
00433       prob = RanGen2->flat();
00434       r1 = double(prob);
00435       prob = RanGen2->flat();
00436       r2 = double(prob);
00437       c = cmin + (c_cut-cmin)*r1;    
00438       z = b0 + b1 * c + b2 * c*c;
00439       if (z > z_max*r2) break;
00440     }    
00441   
00442   return 0;
00443   
00444 }
00445 
00446 
00447 double CMSCGEN::momentum_times_charge()
00448 {
00449   
00450   if(initialization==1)
00451     { 
00452       return pq;
00453     }
00454   else
00455     {
00456       std::cout << " >>> CMSCGEN <<< warning: not initialized" << std::endl;
00457       return -9999.;
00458     }
00459 
00460 }
00461 
00462 double CMSCGEN::cos_theta()
00463 {
00464   
00465   if(initialization==1)
00466     { 
00467       // here convert between coordinate systems:
00468       return -c;
00469     }
00470   else
00471     {
00472       std::cout << " >>> CMSCGEN <<< warning: not initialized" << std::endl;
00473       return -0.9999;
00474     }
00475 
00476 }
00477 
00478 double CMSCGEN::flux()
00479 {
00480   
00481   if(initialization==1)
00482     { 
00483       return integrated_flux;
00484     }
00485   else
00486     {
00487       std::cout << " >>> CMSCGEN <<< warning: not initialized" << std::endl;
00488       return -0.9999;
00489     }
00490   
00491 }
00492 
00493 
00494 
00495 int CMSCGEN::initializeNuMu(double pmin_in, double pmax_in, double thetamin_in, double thetamax_in, double Enumin_in, double Enumax_in, double Phimin_in, double Phimax_in, double ProdAlt_in, CLHEP::HepRandomEngine *rnd)
00496 {
00497   if (delRanGen)
00498     delete RanGen2;
00499   RanGen2 = rnd;
00500   delRanGen = false;
00501 
00502   ProdAlt = ProdAlt_in;
00503 
00504   Rnunubar = 1.2;
00505 
00506   sigma = (0.72*Rnunubar+0.09)/(1+Rnunubar)*1.e-38; //cm^2GeV^-1
00507 
00508   AR = (0.69+0.06*Rnunubar)/(0.09+0.72*Rnunubar);
00509 
00510 
00511   //set smin and smax, here convert between coordinate systems:
00512   pmin = pmin_in;
00513   pmax = pmax_in;
00514   cmin = TMath::Cos(thetamin_in);//input angle already converted from Deg to Rad!
00515   cmax = TMath::Cos(thetamax_in);//input angle already converted from Deg to Rad!
00516   enumin = (Enumin_in < 10.) ? 10. : Enumin_in; //no nu's below 10GeV
00517   enumax = Enumax_in;
00518 
00519 
00520   //do initial run of flux rate to determine Maximum
00521   integrated_flux = 0.;
00522   dNdEmudEnuMax = 0.;
00523   negabs = 0.;
00524   negfrac = 0.;
00525   int trials = 100000;
00526   for (int i=0; i<trials; ++i) {
00527     double ctheta = cmin + (cmax-cmin)*RanGen2->flat();
00528     double Emu = pmin + (pmax-pmin)*RanGen2->flat();
00529     double Enu = enumin + (enumax-enumin)*RanGen2->flat();
00530     double rate =  dNdEmudEnu(Enu, Emu, ctheta);
00531     //std::cout << "trial=" << i << " ctheta=" << ctheta << " Emu=" << Emu << " Enu=" << Enu 
00532     //      << " rate=" << rate << std::endl;
00533     //std::cout << "cmin=" << cmin << " cmax=" << cmax 
00534     //      << " pmin=" << pmin << " pmax=" << pmax 
00535     //      << " enumin=" << enumin << " enumax=" << enumax << std::endl;
00536     if (rate > 0.) {
00537       integrated_flux += rate;
00538       if (rate > dNdEmudEnuMax)
00539         dNdEmudEnuMax = rate;
00540     }
00541     else negabs++;
00542   }
00543   negfrac = negabs/trials;
00544   integrated_flux /= trials;
00545 
00546   std::cout << "CMSCGEN::initializeNuMu: After " << trials << " trials:" << std::endl;
00547   std::cout << "dNdEmudEnuMax=" << dNdEmudEnuMax << std::endl;
00548   std::cout << "negfrac=" << negfrac << std::endl;
00549 
00550   //multiply by phase space boundaries
00551   integrated_flux *= (cmin-cmax);
00552   integrated_flux *= (Phimax_in-Phimin_in);
00553   integrated_flux *= (pmax-pmin);
00554   integrated_flux *= (enumax-enumin);
00555   //remove negative phase space areas which do not contribute anything
00556   integrated_flux *= (1.-negfrac);
00557   std::cout << " >>> CMSCGEN.initializeNuMu <<< " <<
00558     " Integrated flux = " << integrated_flux << " units??? " << std::endl;
00559 
00560 
00561   initialization = 1;
00562 
00563   return initialization;
00564 
00565 } 
00566 
00567 int CMSCGEN::initializeNuMu(double pmin_in, double pmax_in, double thetamin_in, double thetamax_in, double Enumin_in, double Enumax_in, double Phimin_in, double Phimax_in, double ProdAlt_in, int RanSeed)
00568 {
00569   CLHEP::HepRandomEngine *rnd = new CLHEP::HepJamesRandom;
00570   //set seed for Random Generator (seed can be controled by config-file), P.Biallass 2006
00571   rnd->setSeed(RanSeed, 0);
00572   delRanGen = true;
00573   return initializeNuMu(pmin_in, pmax_in, thetamin_in, thetamax_in, Enumin_in, Enumax_in, Phimin_in, Phimax_in, ProdAlt_in, rnd);
00574 }
00575 
00576 
00577 double CMSCGEN::dNdEmudEnu(double Enu, double Emu, double ctheta) {
00578   double cthetaNu = 1. + ctheta; //swap cos(theta) from down to up range
00579   double thetas = asin(sin(acos(cthetaNu))*(Rearth-SurfaceOfEarth)/(Rearth+ProdAlt));
00580   double costhetas = cos(thetas);
00581   double dNdEnudW = 0.0286*pow(Enu,-2.7)*(1./(1.+(6.*Enu*costhetas)/115.)+0.213/(1.+(1.44*Enu*costhetas)/850.)); //cm^2*s*sr*GeV
00582   double dNdEmudEnu = N_A*sigma/alpha*dNdEnudW*1./(1.+Emu/epsilon)*
00583     (Enu-Emu+AR/3*(Enu*Enu*Enu-Emu*Emu*Emu)/(Enu*Enu));
00584   return dNdEmudEnu;
00585 }
00586 
00587 
00588 int CMSCGEN::generateNuMu() {
00589   if(initialization==0) 
00590     {
00591       std::cout << " >>> CMSCGEN <<< warning: not initialized" << std::endl;
00592       return -1;
00593     }
00594   
00595   double ctheta, Emu;
00596   while (1) {
00597     ctheta = cmin + (cmax-cmin)*RanGen2->flat();
00598     Emu = pmin + (pmax-pmin)*RanGen2->flat();
00599     double Enu = enumin + (enumax-enumin)*RanGen2->flat();
00600     double rate = dNdEmudEnu(Enu, Emu, ctheta);
00601     if (rate > dNdEmudEnuMax*RanGen2->flat()) break;
00602   }
00603 
00604   c = -ctheta; //historical sign convention
00605 
00606   pq = Emu;
00607   //
00608   // +++ nu/nubar ratio (~1.2)
00609   //
00610   double charg = 1.; //nubar -> mu+
00611   if (RanGen2->flat() > Rnunubar/(1.+Rnunubar))
00612     charg = -1.; //neutrino -> mu-
00613 
00614   pq = pq*charg;
00615  
00616   
00617   //int flux += this event rate
00618 
00619   return 1;
00620 }