00001
00002 #include "SimG4Core/Notification/interface/TrackInformation.h"
00003 #include "DetectorDescription/Core/interface/DDFilter.h"
00004 #include "DetectorDescription/Core/interface/DDFilteredView.h"
00005 #include "DetectorDescription/Core/interface/DDSolid.h"
00006 #include "DetectorDescription/Core/interface/DDSplit.h"
00007 #include "DetectorDescription/Core/interface/DDValue.h"
00008
00009 #include "G4LogicalVolumeStore.hh"
00010 #include "G4LogicalVolume.hh"
00011 #include "G4Step.hh"
00012 #include "G4Track.hh"
00013 #include "G4VProcess.hh"
00014 #include "G4Poisson.hh"
00015
00016
00017 #include "FWCore/ServiceRegistry/interface/Service.h"
00018 #include "CommonTools/UtilAlgos/interface/TFileService.h"
00019 #include <TTree.h>
00020
00021
00022 #include "SimG4CMS/CherenkovAnalysis/interface/DreamSD.h"
00023 #include "SimG4CMS/CherenkovAnalysis/interface/PMTResponse.h"
00024
00025
00026 DreamSD::DreamSD(G4String name, const DDCompactView & cpv,
00027 SensitiveDetectorCatalog & clg,
00028 edm::ParameterSet const & p, const SimTrackManager* manager) :
00029 CaloSD(name, cpv, clg, p, manager) {
00030
00031 edm::ParameterSet m_EC = p.getParameter<edm::ParameterSet>("ECalSD");
00032 useBirk= m_EC.getParameter<bool>("UseBirkLaw");
00033 doCherenkov_ = m_EC.getParameter<bool>("doCherenkov");
00034 birk1 = m_EC.getParameter<double>("BirkC1")*(g/(MeV*cm2));
00035 birk2 = m_EC.getParameter<double>("BirkC2");
00036 birk3 = m_EC.getParameter<double>("BirkC3");
00037 slopeLY= m_EC.getParameter<double>("SlopeLightYield");
00038 readBothSide_ = m_EC.getUntrackedParameter<bool>("ReadBothSide", false);
00039
00040 edm::LogInfo("EcalSim") << "Constructing a DreamSD with name " << GetName() << "\n"
00041 << "DreamSD:: Use of Birks law is set to "
00042 << useBirk << " with three constants kB = "
00043 << birk1 << ", C1 = " << birk2 << ", C2 = "
00044 << birk3 << "\n"
00045 << " Slope for Light yield is set to "
00046 << slopeLY << "\n"
00047 << " Parameterization of Cherenkov is set to "
00048 << doCherenkov_ << " and readout both sides is "
00049 << readBothSide_;
00050
00051 initMap(name,cpv);
00052
00053
00054 edm::Service<TFileService> tfile;
00055
00056 if ( !tfile.isAvailable() )
00057 throw cms::Exception("BadConfig") << "TFileService unavailable: "
00058 << "please add it to config file";
00059
00060 ntuple_ = tfile->make<TTree>("tree","Cherenkov photons");
00061 if (doCherenkov_) {
00062 ntuple_->Branch("nphotons",&nphotons_,"nphotons/I");
00063 ntuple_->Branch("px",px_,"px[nphotons]/F");
00064 ntuple_->Branch("py",py_,"py[nphotons]/F");
00065 ntuple_->Branch("pz",pz_,"pz[nphotons]/F");
00066 ntuple_->Branch("x",x_,"x[nphotons]/F");
00067 ntuple_->Branch("y",y_,"y[nphotons]/F");
00068 ntuple_->Branch("z",z_,"z[nphotons]/F");
00069 }
00070
00071 }
00072
00073
00074 bool DreamSD::ProcessHits(G4Step * aStep, G4TouchableHistory *) {
00075
00076 if (aStep == NULL) {
00077 return true;
00078 } else {
00079 side = 1;
00080 if (getStepInfo(aStep)) {
00081 if (hitExists() == false && edepositEM+edepositHAD>0.)
00082 currentHit = createNewHit();
00083 if (readBothSide_) {
00084 side = -1;
00085 getStepInfo(aStep);
00086 if (hitExists() == false && edepositEM+edepositHAD>0.)
00087 currentHit = createNewHit();
00088 }
00089 }
00090 }
00091 return true;
00092 }
00093
00094
00095
00096 bool DreamSD::getStepInfo(G4Step* aStep) {
00097
00098 preStepPoint = aStep->GetPreStepPoint();
00099 theTrack = aStep->GetTrack();
00100 G4String nameVolume = preStepPoint->GetPhysicalVolume()->GetName();
00101
00102
00103 double weight = 1.;
00104 weight *= curve_LY(aStep, side);
00105 if (useBirk) weight *= getAttenuation(aStep, birk1, birk2, birk3);
00106 edepositEM = aStep->GetTotalEnergyDeposit() * weight;
00107 LogDebug("EcalSim") << "DreamSD:: " << nameVolume << " Side " << side
00108 <<" Light Collection Efficiency " << weight
00109 << " Weighted Energy Deposit " << edepositEM/MeV
00110 << " MeV";
00111
00112 if ( doCherenkov_ ) {
00113 edepositHAD = cherenkovDeposit_( aStep );
00114 } else {
00115 edepositHAD = 0;
00116 }
00117
00118 double time = (aStep->GetPostStepPoint()->GetGlobalTime())/nanosecond;
00119 unsigned int unitID= setDetUnitId(aStep);
00120 if (side < 0) unitID++;
00121 TrackInformation * trkInfo = (TrackInformation *)(theTrack->GetUserInformation());
00122 int primaryID;
00123
00124 if (trkInfo)
00125 primaryID = trkInfo->getIDonCaloSurface();
00126 else
00127 primaryID = 0;
00128
00129 if (primaryID == 0) {
00130 edm::LogWarning("EcalSim") << "CaloSD: Problem with primaryID **** set by "
00131 << "force to TkID **** "
00132 << theTrack->GetTrackID() << " in Volume "
00133 << preStepPoint->GetTouchable()->GetVolume(0)->GetName();
00134 primaryID = theTrack->GetTrackID();
00135 }
00136
00137 bool flag = (unitID > 0);
00138 G4TouchableHistory* touch =(G4TouchableHistory*)(theTrack->GetTouchable());
00139 if (flag) {
00140 currentID.setID(unitID, time, primaryID, 0);
00141
00142 LogDebug("EcalSim") << "CaloSD:: GetStepInfo for"
00143 << " PV " << touch->GetVolume(0)->GetName()
00144 << " PVid = " << touch->GetReplicaNumber(0)
00145 << " MVid = " << touch->GetReplicaNumber(1)
00146 << " Unit " << currentID.unitID()
00147 << " Edeposit = " << edepositEM << " " << edepositHAD;
00148 } else {
00149 LogDebug("EcalSim") << "CaloSD:: GetStepInfo for"
00150 << " PV " << touch->GetVolume(0)->GetName()
00151 << " PVid = " << touch->GetReplicaNumber(0)
00152 << " MVid = " << touch->GetReplicaNumber(1)
00153 << " Unit " << std::hex << unitID << std::dec
00154 << " Edeposit = " << edepositEM << " " << edepositHAD;
00155 }
00156 return flag;
00157
00158 }
00159
00160
00161
00162 void DreamSD::initRun() {
00163
00164
00165 DimensionMap::const_iterator ite = xtalLMap.begin();
00166 G4LogicalVolume* lv = (ite->first);
00167 G4Material* material = lv->GetMaterial();
00168 edm::LogInfo("EcalSim") << "DreamSD::initRun: Initializes for material "
00169 << material->GetName() << " in " << lv->GetName();
00170 materialPropertiesTable = material->GetMaterialPropertiesTable();
00171 if ( !materialPropertiesTable ) {
00172 if ( !setPbWO2MaterialProperties_( material ) ) {
00173 edm::LogWarning("EcalSim") << "Couldn't retrieve material properties table\n"
00174 << " Material = " << material->GetName();
00175 }
00176 materialPropertiesTable = material->GetMaterialPropertiesTable();
00177 }
00178 }
00179
00180
00181
00182 uint32_t DreamSD::setDetUnitId(G4Step * aStep) {
00183 const G4VTouchable* touch = aStep->GetPreStepPoint()->GetTouchable();
00184 uint32_t id = (touch->GetReplicaNumber(1))*10 + (touch->GetReplicaNumber(0));
00185 LogDebug("EcalSim") << "DreamSD:: ID " << id;
00186 return id;
00187 }
00188
00189
00190
00191 void DreamSD::initMap(G4String sd, const DDCompactView & cpv) {
00192
00193 G4String attribute = "ReadOutName";
00194 DDSpecificsFilter filter;
00195 DDValue ddv(attribute,sd,0);
00196 filter.setCriteria(ddv,DDSpecificsFilter::equals);
00197 DDFilteredView fv(cpv);
00198 fv.addFilter(filter);
00199 fv.firstChild();
00200
00201 const G4LogicalVolumeStore * lvs = G4LogicalVolumeStore::GetInstance();
00202 std::vector<G4LogicalVolume *>::const_iterator lvcite;
00203 bool dodet=true;
00204 while (dodet) {
00205 const DDSolid & sol = fv.logicalPart().solid();
00206 std::vector<double> paras(sol.parameters());
00207 G4String name = sol.name().name();
00208 G4LogicalVolume* lv=0;
00209 for (lvcite = lvs->begin(); lvcite != lvs->end(); lvcite++)
00210 if ((*lvcite)->GetName() == name) {
00211 lv = (*lvcite);
00212 break;
00213 }
00214 LogDebug("EcalSim") << "DreamSD::initMap (for " << sd << "): Solid "
00215 << name << " Shape " << sol.shape() <<" Parameter 0 = "
00216 << paras[0] << " Logical Volume " << lv;
00217 double length = 0, width = 0;
00218
00219 std::sort( paras.begin(), paras.end() );
00220 length = 2.0*paras.back();
00221 width = 2.0*paras.front();
00222 xtalLMap.insert( std::pair<G4LogicalVolume*,Doubles>(lv,Doubles(length,width)) );
00223 dodet = fv.next();
00224 }
00225 LogDebug("EcalSim") << "DreamSD: Length Table for " << attribute << " = "
00226 << sd << ":";
00227 DimensionMap::const_iterator ite = xtalLMap.begin();
00228 int i=0;
00229 for (; ite != xtalLMap.end(); ite++, i++) {
00230 G4String name = "Unknown";
00231 if (ite->first != 0) name = (ite->first)->GetName();
00232 LogDebug("EcalSim") << " " << i << " " << ite->first << " " << name
00233 << " L = " << ite->second.first
00234 << " W = " << ite->second.second;
00235 }
00236 }
00237
00238
00239 double DreamSD::curve_LY(G4Step* aStep, int flag) {
00240
00241 G4StepPoint* stepPoint = aStep->GetPreStepPoint();
00242 G4LogicalVolume* lv = stepPoint->GetTouchable()->GetVolume(0)->GetLogicalVolume();
00243 G4String nameVolume= lv->GetName();
00244
00245 double weight = 1.;
00246 G4ThreeVector localPoint = setToLocal(stepPoint->GetPosition(),
00247 stepPoint->GetTouchable());
00248 double crlength = crystalLength(lv);
00249 double localz = localPoint.x();
00250 double dapd = 0.5 * crlength - flag*localz;
00251 if (dapd >= -0.1 || dapd <= crlength+0.1) {
00252 if (dapd <= 100.)
00253 weight = 1.0 + slopeLY - dapd * 0.01 * slopeLY;
00254 } else {
00255 edm::LogWarning("EcalSim") << "DreamSD: light coll curve : wrong distance "
00256 << "to APD " << dapd << " crlength = "
00257 << crlength << " crystal name = " << nameVolume
00258 << " z of localPoint = " << localz
00259 << " take weight = " << weight;
00260 }
00261 LogDebug("EcalSim") << "DreamSD, light coll curve : " << dapd
00262 << " crlength = " << crlength
00263 << " crystal name = " << nameVolume
00264 << " z of localPoint = " << localz
00265 << " take weight = " << weight;
00266 return weight;
00267 }
00268
00269
00270 const double DreamSD::crystalLength(G4LogicalVolume* lv) const {
00271
00272 double length= -1.;
00273 DimensionMap::const_iterator ite = xtalLMap.find(lv);
00274 if (ite != xtalLMap.end()) length = ite->second.first;
00275 return length;
00276
00277 }
00278
00279
00280 const double DreamSD::crystalWidth(G4LogicalVolume* lv) const {
00281
00282 double width= -1.;
00283 DimensionMap::const_iterator ite = xtalLMap.find(lv);
00284 if (ite != xtalLMap.end()) width = ite->second.second;
00285 return width;
00286
00287 }
00288
00289
00290
00291
00292
00293 double DreamSD::cherenkovDeposit_( G4Step* aStep ) {
00294
00295 double cherenkovEnergy = 0;
00296 if (!materialPropertiesTable) return cherenkovEnergy;
00297 G4Material* material = aStep->GetTrack()->GetMaterial();
00298
00299
00300 G4MaterialPropertyVector* Rindex = materialPropertiesTable->GetProperty("RINDEX");
00301 if ( Rindex == NULL ) {
00302 edm::LogWarning("EcalSim") << "Couldn't retrieve refractive index";
00303 return cherenkovEnergy;
00304 }
00305
00306
00307
00308 int Rlength = Rindex->GetVectorLength() - 1;
00309 double Pmin = Rindex->Energy(0);
00310 double Pmax = Rindex->Energy(Rlength);
00311 LogDebug("EcalSim") << "Material properties: " << "\n"
00312 << " Pmin = " << Pmin
00313 << " Pmax = " << Pmax;
00314
00315
00316 G4StepPoint* pPreStepPoint = aStep->GetPreStepPoint();
00317 G4StepPoint* pPostStepPoint = aStep->GetPostStepPoint();
00318 G4ThreeVector x0 = pPreStepPoint->GetPosition();
00319 G4ThreeVector p0 = aStep->GetDeltaPosition().unit();
00320 const G4DynamicParticle* aParticle = aStep->GetTrack()->GetDynamicParticle();
00321 const double charge = aParticle->GetDefinition()->GetPDGCharge();
00322
00323 double beta = 0.5*( pPreStepPoint->GetBeta() + pPostStepPoint->GetBeta() );
00324 double BetaInverse = 1.0/beta;
00325
00326 LogDebug("EcalSim") << "Particle properties: " << "\n"
00327 << " charge = " << charge
00328 << " beta = " << beta;
00329
00330
00331 double meanNumberOfPhotons = getAverageNumberOfPhotons_( charge, beta, material, Rindex );
00332 if ( meanNumberOfPhotons <= 0.0 ) {
00333 LogDebug("EcalSim") << "Mean number of photons is zero: " << meanNumberOfPhotons
00334 << ", stopping here";
00335 return cherenkovEnergy;
00336 }
00337
00338
00339 meanNumberOfPhotons *= aStep->GetStepLength();
00340
00341
00342 int numPhotons = static_cast<int>( G4Poisson(meanNumberOfPhotons) );
00343
00344 if ( numPhotons <= 0 ) {
00345 LogDebug("EcalSim") << "Poission number of photons is zero: " << numPhotons
00346 << ", stopping here";
00347 return cherenkovEnergy;
00348 }
00349
00350
00351 double dp = Pmax - Pmin;
00352 double maxCos = BetaInverse / (*Rindex)[Rlength];
00353 double maxSin2 = (1.0 - maxCos) * (1.0 + maxCos);
00354
00355
00356 for ( int iPhoton = 0; iPhoton<numPhotons; ++iPhoton ) {
00357
00358
00359 double randomNumber;
00360 double sampledMomentum, sampledRI;
00361 double cosTheta, sin2Theta;
00362
00363
00364 do {
00365 randomNumber = G4UniformRand();
00366 sampledMomentum = Pmin + randomNumber * dp;
00367 sampledRI = Rindex->Value(sampledMomentum);
00368 cosTheta = BetaInverse / sampledRI;
00369
00370 sin2Theta = (1.0 - cosTheta)*(1.0 + cosTheta);
00371 randomNumber = G4UniformRand();
00372
00373 } while (randomNumber*maxSin2 > sin2Theta);
00374
00375
00376
00377 randomNumber = G4UniformRand();
00378
00379 double phi = twopi*randomNumber;
00380 double sinPhi = sin(phi);
00381 double cosPhi = cos(phi);
00382
00383
00384
00385
00386 double sinTheta = sqrt(sin2Theta);
00387 double px = sinTheta*cosPhi;
00388 double py = sinTheta*sinPhi;
00389 double pz = cosTheta;
00390 G4ThreeVector photonDirection(px, py, pz);
00391
00392
00393 photonDirection.rotateUz(p0);
00394
00395
00396 randomNumber = G4UniformRand();
00397 G4ThreeVector photonPosition = x0 + randomNumber * aStep->GetDeltaPosition();
00398 G4ThreeVector photonMomentum = sampledMomentum*photonDirection;
00399
00400
00401 cherenkovEnergy += getPhotonEnergyDeposit_( photonMomentum, photonPosition, aStep );
00402
00403
00404 nphotons_ = numPhotons;
00405 px_[iPhoton] = photonMomentum.x();
00406 py_[iPhoton] = photonMomentum.y();
00407 pz_[iPhoton] = photonMomentum.z();
00408 x_[iPhoton] = photonPosition.x();
00409 y_[iPhoton] = photonPosition.y();
00410 z_[iPhoton] = photonPosition.z();
00411 }
00412
00413
00414 ntuple_->Fill();
00415
00416
00417 return cherenkovEnergy;
00418
00419 }
00420
00421
00422
00423
00424
00425 double DreamSD::getAverageNumberOfPhotons_( const double charge,
00426 const double beta,
00427 const G4Material* aMaterial,
00428 G4MaterialPropertyVector* Rindex )
00429 {
00430 const G4double rFact = 369.81/(eV * cm);
00431
00432 if( beta <= 0.0 ) return 0.0;
00433
00434 double BetaInverse = 1./beta;
00435
00436
00437
00438
00439
00440
00441 int Rlength = Rindex->GetVectorLength() - 1;
00442 double Pmin = Rindex->Energy(0);
00443 double Pmax = Rindex->Energy(Rlength);
00444
00445
00446 double nMin = (*Rindex)[0];
00447 double nMax = (*Rindex)[Rlength];
00448
00449
00450 double CAImax = chAngleIntegrals_->GetMaxValue();
00451
00452 double dp = 0., ge = 0., CAImin = 0.;
00453
00454
00455 if ( nMax < BetaInverse) { }
00456
00457
00458 else if (nMin > BetaInverse) {
00459 dp = Pmax - Pmin;
00460 ge = CAImax;
00461 }
00462
00463
00464
00465
00466
00467 else {
00468 Pmin = Rindex->Value(BetaInverse);
00469 dp = Pmax - Pmin;
00470
00471
00472 double CAImin = chAngleIntegrals_->Value(Pmin);
00473 ge = CAImax - CAImin;
00474
00475 }
00476
00477
00478 double numPhotons = rFact * charge/eplus * charge/eplus *
00479 (dp - ge * BetaInverse*BetaInverse);
00480
00481 LogDebug("EcalSim") << "@SUB=getAverageNumberOfPhotons"
00482 << "CAImin = " << CAImin << "\n"
00483 << "CAImax = " << CAImax << "\n"
00484 << "dp = " << dp << ", ge = " << ge << "\n"
00485 << "numPhotons = " << numPhotons;
00486
00487
00488
00489 return numPhotons;
00490
00491 }
00492
00493
00494
00495
00496
00497 bool DreamSD::setPbWO2MaterialProperties_( G4Material* aMaterial ) {
00498
00499 std::string pbWO2Name("E_PbWO4");
00500 if ( pbWO2Name != aMaterial->GetName() ) {
00501 edm::LogWarning("EcalSim") << "This is not the right material: "
00502 << "expecting " << pbWO2Name
00503 << ", got " << aMaterial->GetName();
00504 return false;
00505 }
00506
00507 G4MaterialPropertiesTable* table = new G4MaterialPropertiesTable();
00508
00509
00510
00511 const int nEntries = 14;
00512 double PhotonEnergy[nEntries] = { 1.7712*eV, 1.8368*eV, 1.90745*eV, 1.98375*eV, 2.0664*eV,
00513 2.15625*eV, 2.25426*eV, 2.3616*eV, 2.47968*eV, 2.61019*eV,
00514 2.75521*eV, 2.91728*eV, 3.09961*eV, 3.30625*eV };
00515 double RefractiveIndex[nEntries] = { 2.17728, 2.18025, 2.18357, 2.18753, 2.19285,
00516 2.19813, 2.20441, 2.21337, 2.22328, 2.23619,
00517 2.25203, 2.27381, 2.30282, 2.34666 };
00518
00519 table->AddProperty( "RINDEX", PhotonEnergy, RefractiveIndex, nEntries );
00520 aMaterial->SetMaterialPropertiesTable(table);
00521
00522
00523
00524 chAngleIntegrals_ =
00525 std::auto_ptr<G4PhysicsOrderedFreeVector>( new G4PhysicsOrderedFreeVector() );
00526
00527 int index = 0;
00528 double currentRI = RefractiveIndex[index];
00529 double currentPM = PhotonEnergy[index];
00530 double currentCAI = 0.0;
00531 chAngleIntegrals_->InsertValues(currentPM, currentCAI);
00532 double prevPM = currentPM;
00533 double prevCAI = currentCAI;
00534 double prevRI = currentRI;
00535 while ( ++index < nEntries ) {
00536 currentRI = RefractiveIndex[index];
00537 currentPM = PhotonEnergy[index];
00538 currentCAI = 0.5*(1.0/(prevRI*prevRI) + 1.0/(currentRI*currentRI));
00539 currentCAI = prevCAI + (currentPM - prevPM) * currentCAI;
00540
00541 chAngleIntegrals_->InsertValues(currentPM, currentCAI);
00542
00543 prevPM = currentPM;
00544 prevCAI = currentCAI;
00545 prevRI = currentRI;
00546 }
00547
00548 LogDebug("EcalSim") << "Material properties set for " << aMaterial->GetName();
00549
00550 return true;
00551
00552 }
00553
00554
00555
00556
00557
00558
00559
00560 double DreamSD::getPhotonEnergyDeposit_( const G4ThreeVector& p,
00561 const G4ThreeVector& x,
00562 const G4Step* aStep )
00563 {
00564
00565 double energy = 0;
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575 G4StepPoint* stepPoint = aStep->GetPreStepPoint();
00576 G4LogicalVolume* lv = stepPoint->GetTouchable()->GetVolume(0)->GetLogicalVolume();
00577 G4String nameVolume= lv->GetName();
00578
00579 double crlength = crystalLength(lv);
00580 double crwidth = crystalWidth(lv);
00581 double dapd = 0.5 * crlength - x.x();
00582 double y = p.y()/p.x()*dapd;
00583
00584 LogDebug("EcalSim") << "Distance to APD: " << dapd
00585 << " - y at APD: " << y;
00586
00587
00588 if ( fabs(y)>crwidth*0.5 ) {
00589
00590 }
00591
00592
00593 double waveLength = p.mag()*1.239e8;
00594
00595
00596 energy = p.mag()*PMTResponse::getEfficiency(waveLength);
00597
00598 LogDebug("EcalSim") << "Wavelength: " << waveLength << " - Energy: " << energy;
00599
00600 return energy;
00601
00602 }