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