CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_6_2_5/src/SimMuon/CSCDigitizer/src/CSCDriftSim.cc

Go to the documentation of this file.
00001 #include "FWCore/MessageLogger/interface/MessageLogger.h"
00002 #include "SimMuon/CSCDigitizer/src/CSCDriftSim.h"
00003 #include "SimMuon/CSCDigitizer/src/CSCDetectorHit.h"
00004 #include "Geometry/CSCGeometry/interface/CSCChamberSpecs.h"
00005 #include "Geometry/CSCGeometry/interface/CSCLayer.h"
00006 #include "Geometry/CSCGeometry/interface/CSCLayerGeometry.h"
00007 #include "DataFormats/GeometryVector/interface/LocalVector.h"
00008 #include "SimDataFormats/TrackingHit/interface/PSimHit.h"
00009 #include "MagneticField/Engine/interface/MagneticField.h"
00010 #include "DataFormats/Math/interface/Point3D.h"
00011 #include "DataFormats/Math/interface/Vector3D.h"
00012 #include "Math/GenVector/RotationZ.h"
00013 #include "CLHEP/Units/GlobalPhysicalConstants.h"
00014 #include "CLHEP/Units/GlobalSystemOfUnits.h"  
00015 #include <cmath>
00016 #include <iostream>
00017 
00018 static const int N_INTEGRAL_STEPS = 700;
00019 
00020 CSCDriftSim::CSCDriftSim() 
00021 : bz(0.), // should make these local variables
00022   ycell(0.),
00023   zcell(0.),
00024   dNdEIntegral(N_INTEGRAL_STEPS, 0.),
00025   STEP_SIZE(0.01),
00026   ELECTRON_DIFFUSION_COEFF(0.0161),
00027   theMagneticField(0),
00028   theRandGaussQ(0),
00029   theRandFlat(0)
00030 {
00031   // just initialize avalanche sim.  There has to be a better
00032   // way to take the integral of a function!
00033   double sum = 0.;
00034   int i;
00035   for(i = 0; i < N_INTEGRAL_STEPS; ++i) {
00036     if(i > 1) {
00037       double xx = STEP_SIZE * (double(i) - 0.5 );
00038       double dNdE = pow( xx, 0.38) * exp(-1.38*xx);
00039 
00040       sum += dNdE;
00041     }
00042     // store this value in the map
00043     dNdEIntegral[i] = sum;
00044   }
00045 
00046   // now normalize the whole map
00047   for(i =  0; i < N_INTEGRAL_STEPS; ++i) {
00048     dNdEIntegral[i] /= sum;
00049   }
00050 }
00051 
00052 
00053 CSCDriftSim::~CSCDriftSim()
00054 {
00055   delete theRandGaussQ;
00056   delete theRandFlat;
00057 }
00058 
00059 
00060 void CSCDriftSim::setRandomEngine(CLHEP::HepRandomEngine& engine)
00061 {
00062   theRandGaussQ = new CLHEP::RandGaussQ(engine);
00063   theRandFlat = new CLHEP::RandFlat(engine);
00064 }
00065 
00066 
00067 CSCDetectorHit 
00068 CSCDriftSim::getWireHit(const Local3DPoint & pos, const CSCLayer * layer,
00069                           int nearestWire, const PSimHit & simHit) {
00070 
00071   const CSCChamberSpecs * specs = layer->chamber()->specs();
00072   const CSCLayerGeometry * geom = layer->geometry();
00073   math::LocalPoint clusterPos(pos.x(), pos.y(), pos.z());
00074   LogTrace("CSCDriftSim") << "CSCDriftSim: ionization cluster at: " <<  pos; 
00075   // set the coordinate system with the x-axis along the nearest wire,
00076   // with the origin in the center of the chamber, on that wire.
00077   math::LocalVector yShift(0, -1.*geom->yOfWire(nearestWire), 0.);
00078   ROOT::Math::RotationZ rotation(-1.*geom->wireAngle());
00079 
00080   clusterPos = yShift + clusterPos;
00081   clusterPos = rotation * clusterPos;
00082   GlobalPoint globalPosition = layer->surface().toGlobal(pos);
00083   assert(theMagneticField != 0);
00084  
00085   //  bz = theMagneticField->inTesla(globalPosition).z() * 10.;
00086 
00087   // We need magnetic field in _local_ coordinates
00088   // Interface now allows access in kGauss directly.
00089   bz = layer->toLocal( theMagneticField->inKGauss( globalPosition ) ).z();
00090 
00091   // these subroutines label the coordinates in GEANT coords...
00092   ycell = clusterPos.z() / specs->anodeCathodeSpacing();
00093   zcell = 2.*clusterPos.y() / specs->wireSpacing();
00094 
00095   LogTrace("CSCDriftSim") << "CSCDriftSim: bz " << bz <<" avgDrift " << avgDrift()
00096        << " wireAngle " << geom->wireAngle()
00097        << " ycell " << ycell << " zcell " << zcell;
00098 
00099   double avgPathLength, pathSigma, avgDriftTime, driftTimeSigma;
00100   static const float B_FIELD_CUT = 15.f;
00101   if(fabs(bz) < B_FIELD_CUT) {
00102     avgPathLength  = avgPathLengthLowB();
00103     pathSigma      = pathSigmaLowB();
00104     avgDriftTime   = avgDriftTimeLowB();
00105     driftTimeSigma = driftTimeSigmaLowB();
00106   }
00107   else {
00108     avgPathLength  = avgPathLengthHighB();
00109     pathSigma      = pathSigmaHighB();
00110     avgDriftTime   = avgDriftTimeHighB();
00111     driftTimeSigma = driftTimeSigmaHighB();
00112   }
00113 
00114   // electron drift path length 
00115   double pathLength = std::max(theRandGaussQ->fire(avgPathLength, pathSigma), 0.);
00116 
00117   // electron drift distance along the anode wire, including diffusion
00118   double diffusionSigma = ELECTRON_DIFFUSION_COEFF * sqrt(pathLength);
00119   double x = clusterPos.x() + theRandGaussQ->fire(avgDrift(), driftSigma())
00120                             + theRandGaussQ->fire(0., diffusionSigma);
00121 
00122   // electron drift time
00123   double driftTime  = std::max(theRandGaussQ->fire(avgDriftTime, driftTimeSigma), 0.);
00124 
00125   //@@ Parameters which should be defined outside the code
00126   // f_att is the fraction of drift electrons lost due to attachment
00127   //static const double f_att = 0.5;
00128   static const double f_collected = 0.82;
00129 
00130   // Avalanche charge, with fluctuation ('avalancheCharge()' is the fluctuation generator!)
00131   //double charge = avalancheCharge() * f_att * f_collected * gasGain(layer->id()) * e_SI * 1.e15;
00132   // doing fattachment by random chance of killing
00133   double charge = avalancheCharge() * f_collected * gasGain(layer->id()) * e_SI * 1.e15;
00134 
00135   float t = simHit.tof() + driftTime;
00136   LogTrace("CSCDriftSim") << "CSCDriftSim: tof = " << simHit.tof() << 
00137       " driftTime = " << driftTime <<
00138       " MEDH = " << CSCDetectorHit(nearestWire, charge, x, t, &simHit);
00139   return CSCDetectorHit(nearestWire, charge, x, t, &simHit);
00140 }
00141 
00142 // Generate avalanche fluctuation
00143 #include <algorithm>
00144 double CSCDriftSim::avalancheCharge() {
00145   double returnVal = 0.;
00146   // pick a random value along the dNdE integral
00147   double x = theRandFlat->fire();
00148   size_t i;
00149   size_t isiz = dNdEIntegral.size();
00150   /*
00151   for(i = 0; i < isiz-1; ++i) {
00152     if(dNdEIntegral[i] > x) break;
00153   }
00154   */
00155   // return position of first element with a value >= x
00156   std::vector<double>::const_iterator p=lower_bound(dNdEIntegral.begin(),dNdEIntegral.end(),x);
00157   if (p==dNdEIntegral.end()) i=isiz-1;
00158   else i = p-dNdEIntegral.begin();
00159                               
00160 
00161   // now extrapolate between values
00162   if( i ==  isiz-1 ) {
00163     //edm::LogInfo("CSCDriftSim") << "Funky integral in CSCDriftSim " << x;
00164     returnVal = STEP_SIZE * double(i) * dNdEIntegral[i];
00165   }
00166   else {
00167     double x1 = dNdEIntegral[i];
00168     double x2 = dNdEIntegral[i+1];
00169     returnVal = STEP_SIZE * (double(i) + (x-x1)/(x2-x1)); 
00170   }
00171   LogTrace("CSCDriftSim") << "CSCDriftSim: avalanche fluc " << returnVal << "  " << x ;
00172 
00173   return returnVal;  
00174 }
00175 
00176 
00177 double CSCDriftSim::gasGain(const CSCDetId & detId) const
00178 {
00179   double result = 130000.;  // 1.30 E05
00180   // if ME1/1, add some extra gas gain to compensate
00181   // for a smaller gas gap
00182   int ring = detId.ring();
00183   if(detId.station() == 1 && (ring == 1 || ring == 4))
00184   {
00185     result = 213000.; // 2.13 E05 ( to match real world as of Jan-2011)
00186   }
00187   return result;
00188 }