File indexing completed on 2024-04-06 12:30:42
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020 #include "DataFormats/GeometryVector/interface/LocalVector.h"
0021 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0022 #include "FWCore/ParameterSet/interface/FileInPath.h"
0023 #include "FWCore/Utilities/interface/Exception.h"
0024 #include "SimMuon/CSCDigitizer/src/CSCGasCollisions.h"
0025
0026 #include "CLHEP/Random/RandExponential.h"
0027 #include "CLHEP/Random/RandFlat.h"
0028
0029
0030
0031 #include <cassert>
0032 #include <fstream>
0033
0034
0035
0036 #include <cstdlib>
0037
0038 #include <cmath>
0039
0040
0041
0042 #include <algorithm>
0043
0044 #include <functional>
0045
0046 #include <iterator>
0047 #include <numeric>
0048
0049 using namespace std;
0050
0051
0052
0053
0054
0055
0056
0057
0058 CSCGasCollisions::CSCGasCollisions(const edm::ParameterSet &pset)
0059 : me("CSCGasCollisions"),
0060 gasDensity(2.1416e-03),
0061 deCut(1.e05),
0062 eion(14.95),
0063 ework(34.0),
0064 clusterExtent(0.001),
0065 theGammaBins(N_GAMMA, 0.),
0066 theEnergyBins(N_ENERGY, 0.),
0067 theCollisionTable(N_ENTRIES, 0.),
0068 theCrossGap(nullptr),
0069 theParticleDataTable(nullptr),
0070 saveGasCollisions_(false),
0071 dumpGasCollisions_(false) {
0072 dumpGasCollisions_ = pset.getUntrackedParameter<bool>("dumpGasCollisions");
0073
0074 edm::LogInfo(me) << "Constructing a " << me << ":";
0075 edm::LogInfo(me) << "gas density = " << gasDensity << " g/cm3";
0076 edm::LogInfo(me) << "max eloss per collision allowed = " << deCut / 1000.
0077 << " keV (for higher elosses, hits should have been simulated.)";
0078 edm::LogInfo(me) << "ionization threshold = " << eion << " eV";
0079 edm::LogInfo(me) << "effective work function = " << ework << " eV";
0080 edm::LogInfo(me) << "cluster extent = " << clusterExtent * 1.e04 << " micrometres";
0081 edm::LogInfo(me) << "dump gas collision and simhit information? " << dumpGasCollisions();
0082 edm::LogInfo(me) << "save gas collision information? -NOT YET IMPLEMENTED- " << saveGasCollisions();
0083
0084 readCollisionTable();
0085 }
0086
0087 CSCGasCollisions::~CSCGasCollisions() {
0088 edm::LogInfo(me) << "Destructing a " << me;
0089 delete theCrossGap;
0090 }
0091
0092 void CSCGasCollisions::readCollisionTable() {
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107 string colliFile = "SimMuon/CSCDigitizer/data/collisions.dat";
0108 edm::FileInPath f1{colliFile};
0109 edm::LogInfo(me) << ": reading " << f1.fullPath();
0110
0111 ifstream fin{f1.fullPath()};
0112
0113 if (fin.fail()) {
0114 string errorMessage = "Cannot open input file " + f1.fullPath();
0115 edm::LogError("CSCGasCollisions") << errorMessage;
0116 throw cms::Exception(errorMessage);
0117 }
0118
0119 fin.clear();
0120 fin.seekg(0, ios::beg);
0121
0122
0123
0124 LogTrace(me) << "Reading gamma bins";
0125 for (int i = 0; i < N_GAMMA; ++i) {
0126 fin >> theGammaBins[i];
0127 LogTrace(me) << i + 1 << " " << theGammaBins[i];
0128 }
0129
0130 LogTrace(me) << "Reading energy bins \n";
0131 for (int i = 0; i < N_ENERGY; ++i) {
0132 fin >> theEnergyBins[i];
0133 LogTrace(me) << i + 1 << " " << theEnergyBins[i];
0134 }
0135
0136 LogTrace(me) << "Reading collisions table \n";
0137
0138 for (int i = 0; i < N_ENTRIES; ++i) {
0139 fin >> theCollisionTable[i];
0140 LogTrace(me) << i + 1 << " " << theCollisionTable[i];
0141 }
0142
0143 fin.close();
0144 }
0145
0146 void CSCGasCollisions::setParticleDataTable(const ParticleDataTable *pdt) { theParticleDataTable = pdt; }
0147
0148 void CSCGasCollisions::simulate(const PSimHit &simhit,
0149 std::vector<LocalPoint> &positions,
0150 std::vector<int> &electrons,
0151 CLHEP::HepRandomEngine *engine) {
0152 const float epsilonL = 0.01;
0153
0154
0155
0156
0157
0158
0159
0160 double mom = simhit.pabs();
0161
0162 delete theCrossGap;
0163 assert(theParticleDataTable != nullptr);
0164 ParticleData const *particle = theParticleDataTable->particle(simhit.particleType());
0165 double mass = 0.105658;
0166 if (particle == nullptr) {
0167 edm::LogError("CSCGasCollisions") << "Cannot find particle of type " << simhit.particleType() << " in the PDT";
0168 } else {
0169 mass = particle->mass();
0170 LogTrace(me) << "[CSCGasCollisions] Found particle of type " << simhit.particleType()
0171 << " in the PDT; mass = " << mass;
0172 }
0173
0174 theCrossGap = new CSCCrossGap(mass, mom, simhit.exitPoint() - simhit.entryPoint());
0175 float gapSize = theCrossGap->length();
0176
0177
0178
0179 if (gapSize <= epsilonL) {
0180 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] WARNING! simhit entry and exit are too close - "
0181 "skipping simhit:"
0182 << "\n entry = " << simhit.entryPoint() << ": exit = " << simhit.exitPoint()
0183 << "\n particle type = " << simhit.particleType()
0184 << " : momentum = " << simhit.pabs()
0185 << " GeV/c : energy loss = " << simhit.energyLoss() * 1.E06 << " keV"
0186 << ", gapSize = " << gapSize << " cm (< epsilonL = " << epsilonL << " cm)";
0187 return;
0188 }
0189
0190
0191
0192 std::vector<float> collisions(N_ENERGY);
0193 double loggam = theCrossGap->logGamma();
0194 fillCollisionsForThisGamma(static_cast<float>(loggam), collisions);
0195
0196 double anmin = exp(collisions[N_ENERGY - 1]);
0197 double anmax = exp(collisions[0]);
0198 double amu = anmax - anmin;
0199
0200 LogTrace(me) << "collisions extremes = " << collisions[N_ENERGY - 1] << ", " << collisions[0] << "\n"
0201 << "anmin = " << anmin << ", anmax = " << anmax << "\n"
0202 << "amu = " << amu << "\n";
0203
0204 float dedx = 0.;
0205 double sum_steps = 0.;
0206 int n_steps = 0;
0207 int n_try = 0;
0208 double step = -1.;
0209
0210 LocalPoint layerLocalPoint(simhit.entryPoint());
0211
0212
0213 while (sum_steps < gapSize) {
0214 ++n_try;
0215 if (n_try > MAX_STEPS) {
0216 int maxst = MAX_STEPS;
0217 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] WARNING! n_try = " << n_try
0218 << " is > MAX_STEPS = " << maxst << " - skipping simhit:"
0219 << "\n entry = " << simhit.entryPoint() << ": exit = " << simhit.exitPoint()
0220 << "\n particle type = " << simhit.particleType()
0221 << " : momentum = " << simhit.pabs()
0222 << " GeV/c : energy loss = " << simhit.energyLoss() * 1.E06 << " keV"
0223 << "\n gapSize = " << gapSize << " cm, last step = " << step
0224 << " cm, sum_steps = " << sum_steps << " cm, n_steps = " << n_steps;
0225 break;
0226 }
0227 step = generateStep(amu, engine);
0228 if (sum_steps + step > gapSize)
0229 break;
0230
0231 float eloss = generateEnergyLoss(amu, anmin, anmax, collisions, engine);
0232
0233
0234 if (eloss > deCut) {
0235 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] WARNING! eloss = " << eloss << " eV is too large (> "
0236 << deCut << " eV) - trying another collision";
0237 continue;
0238 }
0239
0240 dedx += eloss;
0241 sum_steps += step;
0242 ++n_steps;
0243
0244 if (n_steps > MAX_STEPS) {
0245 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] WARNING! " << n_steps
0246 << " is too many steps - skipping simhit:"
0247 << "\n entry = " << simhit.entryPoint() << ": exit = " << simhit.exitPoint()
0248 << "\n particle type = " << simhit.particleType()
0249 << " : momentum = " << simhit.pabs()
0250 << " GeV/c : energy loss = " << simhit.energyLoss() * 1.E06 << " keV"
0251 << "\ngapSize=" << gapSize << " cm, last step=" << step
0252 << " cm, sum_steps=" << sum_steps << " cm";
0253 break;
0254 }
0255 LogTrace(me) << "sum_steps = " << sum_steps << " cm , dedx = " << dedx << " eV";
0256
0257
0258
0259 if (eloss > eion) {
0260 layerLocalPoint += step * theCrossGap->unitVector();
0261 ionize(eloss, layerLocalPoint);
0262 } else {
0263 LogTrace(me) << "Energy available = " << eloss << " eV is too low for ionization (< eion = " << eion << " eV)";
0264 }
0265
0266 }
0267
0268 if (dumpGasCollisions())
0269 writeSummary(n_try, n_steps, sum_steps, dedx, simhit);
0270
0271
0272 positions = theCrossGap->ionClusters();
0273 electrons = theCrossGap->electrons();
0274 return;
0275 }
0276
0277 double CSCGasCollisions::generateStep(double avCollisions, CLHEP::HepRandomEngine *engine) const {
0278
0279 double step = (CLHEP::RandExponential::shoot(engine)) / avCollisions;
0280
0281
0282
0283
0284
0285 LogTrace(me) << " step = " << step << " cm";
0286
0287
0288 if (dumpGasCollisions())
0289 theCrossGap->addStep(step);
0290 return step;
0291 }
0292
0293 float CSCGasCollisions::generateEnergyLoss(double avCollisions,
0294 double anmin,
0295 double anmax,
0296 const std::vector<float> &collisions,
0297 CLHEP::HepRandomEngine *engine) const {
0298
0299 float lnColl = log(CLHEP::RandFlat::shoot(engine, anmin, anmax));
0300
0301
0302
0303
0304
0305
0306
0307 float lnE = lnEnergyLoss(lnColl, collisions);
0308 float eloss = exp(lnE);
0309
0310 if (theCrossGap->gamma() < 1.1)
0311 eloss = eloss * 0.173554 / theCrossGap->beta2();
0312 LogTrace(me) << "eloss = " << eloss << " eV";
0313
0314 if (dumpGasCollisions())
0315 theCrossGap->addEloss(eloss);
0316 return eloss;
0317 }
0318
0319 void CSCGasCollisions::ionize(double energyAvailable, LocalPoint startHere) const {
0320 while (energyAvailable > eion) {
0321 LogTrace(me) << " NEW CLUSTER " << theCrossGap->noOfClusters() + 1 << " AT " << startHere;
0322 LocalPoint newCluster(startHere);
0323 theCrossGap->addCluster(newCluster);
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338 double range = 0.5 * (0.71 / gasDensity) * pow(energyAvailable * 1.E-6, 1.72);
0339 LogTrace(me) << " range = " << range << " cm";
0340 if (range < clusterExtent) {
0341
0342
0343
0344 int nelec = static_cast<int>(energyAvailable / ework);
0345 LogTrace(me) << "short-range delta energy in = " << energyAvailable << " eV";
0346
0347 energyAvailable -= nelec * ework;
0348
0349 if (energyAvailable > eion) {
0350 ++nelec;
0351 energyAvailable -= eion;
0352 }
0353 LogTrace(me) << "short-range delta energy out = " << energyAvailable << " eV, nelec = " << nelec;
0354 theCrossGap->addElectrons(nelec);
0355 break;
0356
0357 } else {
0358
0359 LogTrace(me) << "long-range delta \n"
0360 << "no. of electrons in cluster now = " << theCrossGap->noOfElectrons();
0361 theCrossGap->addElectrons(1);
0362
0363 bool new_range = false;
0364 while (!new_range && (energyAvailable > ework)) {
0365 energyAvailable -= ework;
0366 while (energyAvailable > eion) {
0367 double range2 = 0.5 * 0.71 / gasDensity * pow(1.E-6 * energyAvailable, 1.72);
0368 double drange = range - range2;
0369 LogTrace(me) << " energy left = " << energyAvailable << " eV, range2 = " << range2
0370 << " cm, drange = " << drange << " cm";
0371 if (drange < clusterExtent) {
0372 theCrossGap->addElectronToBack();
0373 } else {
0374 startHere += drange * theCrossGap->unitVector();
0375 range = range2;
0376 new_range = true;
0377 LogTrace(me) << "reset range to range2 = " << range << " from startHere = " << startHere << " and iterate";
0378 }
0379 break;
0380
0381 }
0382
0383 }
0384
0385
0386
0387 if (energyAvailable > eion) {
0388 energyAvailable -= ework;
0389 theCrossGap->addElectronToBack();
0390 }
0391
0392 }
0393
0394 }
0395 }
0396
0397 void CSCGasCollisions::writeSummary(int n_try, int n_steps, double sum_steps, float dedx, const PSimHit &simhit) const {
0398
0399
0400 std::vector<LocalPoint> ion_clusters = theCrossGap->ionClusters();
0401 std::vector<int> electrons = theCrossGap->electrons();
0402 std::vector<float> elosses = theCrossGap->eLossPerStep();
0403 std::vector<double> steps = theCrossGap->stepLengths();
0404
0405 edm::LogVerbatim("CSCDigitizer") << "------------------"
0406 << "\nAFTER CROSSING GAP";
0407
0408
0409
0410
0411
0412
0413
0414
0415
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425 size_t nsteps = n_steps;
0426 size_t mstep = steps.size() - 1;
0427
0428 if ((nsteps != MAX_STEPS) && (nsteps != mstep)) {
0429 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] WARNING! no. of steps = " << nsteps
0430 << " .ne. steps.size()-1 = " << mstep;
0431 }
0432 size_t meloss = elosses.size();
0433
0434 if (nsteps != meloss) {
0435 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] WARNING! no. of steps = " << nsteps
0436 << " .ne. no. of elosses = " << meloss;
0437 } else {
0438 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] # / length of step / energy loss per collision:";
0439 for (size_t i = 0; i != nsteps; ++i) {
0440 edm::LogVerbatim("CSCDigitizer") << i + 1 << " / S: " << steps[i] << " / E: " << elosses[i];
0441 }
0442 }
0443
0444 size_t mclus = ion_clusters.size();
0445 size_t melec = electrons.size();
0446
0447 if (mclus != melec) {
0448 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] WARNING! size of cluster vector = " << mclus
0449 << " .ne. size of electrons vector = " << melec;
0450 } else {
0451 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] # / postion of "
0452 "cluster / electrons per cluster: ";
0453 for (size_t i = 0; i != mclus; ++i) {
0454 edm::LogVerbatim("CSCDigitizer") << i + 1 << " / I: " << ion_clusters[i] << " / E: " << electrons[i];
0455 }
0456 }
0457
0458 int n_ic = count_if(elosses.begin(), elosses.end(), [&](auto c) { return c > this->eion; });
0459
0460 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] total no. of collision steps = " << n_steps;
0461 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] total sum of steps = " << sum_steps << " cm";
0462 if (nsteps > 0)
0463 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] average step length = " << sum_steps / float(nsteps)
0464 << " cm";
0465 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] total energy loss across gap = " << dedx
0466 << " eV = " << dedx / 1000. << " keV";
0467 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] no. of primary ionizing collisions across gap = "
0468 "no. with eloss > eion = "
0469 << eion << " eV = " << n_ic;
0470 if (nsteps > 0)
0471 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] average energy loss/collision = " << dedx / float(nsteps)
0472 << " eV";
0473
0474 std::vector<int>::const_iterator bigger = find(electrons.begin(), electrons.end(), 0);
0475 if (bigger != electrons.end()) {
0476 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] TROUBLE! There is a cluster with 0 electrons.";
0477 }
0478
0479 int n_e = accumulate(electrons.begin(), electrons.end(), 0);
0480
0481 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] SUMMARY: simhit"
0482 << "\n entry = " << simhit.entryPoint() << ": exit = " << simhit.exitPoint()
0483 << "\n particle type = " << simhit.particleType()
0484 << " : momentum = " << simhit.pabs()
0485 << " GeV/c : energy loss = " << simhit.energyLoss() * 1.E06 << " keV";
0486
0487 if (nsteps > 0) {
0488 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisions] SUMMARY: ionization"
0489 << " : steps= " << nsteps << " : sum(steps)= " << sum_steps
0490 << " cm : <step>= " << sum_steps / float(nsteps) << " cm"
0491 << " : ionizing= " << n_ic << " : ionclus= " << mclus << " : total e= " << n_e
0492 << " : <dedx/step>= " << dedx / float(nsteps)
0493 << " eV : <e/ionclus>= " << float(n_e) / float(mclus)
0494 << " : dedx= " << dedx / 1000. << " keV";
0495 } else {
0496 edm::LogVerbatim("CSCDigitizer") << "[CSCGasCollisons] ERROR? no collision steps!";
0497 }
0498
0499
0500
0501
0502
0503
0504
0505
0506
0507
0508
0509
0510
0511
0512
0513
0514
0515
0516
0517 }
0518
0519 float CSCGasCollisions::lnEnergyLoss(float lnCollisions, const std::vector<float> &collisions) const {
0520 float lnE = -1.;
0521
0522
0523 std::vector<float>::const_iterator it = find(collisions.begin(), collisions.end(), lnCollisions);
0524
0525 if (it != collisions.end()) {
0526
0527 std::vector<float>::difference_type ihi = it - collisions.begin();
0528 LogTrace(me) << ": using one energy bin " << ihi << " = " << theEnergyBins[ihi]
0529 << " for lnCollisions = " << lnCollisions;
0530 lnE = theEnergyBins[ihi];
0531 } else {
0532
0533 std::vector<float>::const_iterator loside =
0534 find_if(collisions.begin(), collisions.end(), [&lnCollisions](auto c) { return c < lnCollisions; });
0535 std::vector<float>::difference_type ilo = loside - collisions.begin();
0536 if (ilo > 0) {
0537 LogTrace(me) << ": using energy bin " << ilo - 1 << " and " << ilo;
0538 lnE = theEnergyBins[ilo - 1] + (lnCollisions - collisions[ilo - 1]) *
0539 (theEnergyBins[ilo] - theEnergyBins[ilo - 1]) /
0540 (collisions[ilo] - collisions[ilo - 1]);
0541 } else {
0542 LogTrace(me) << ": using one energy bin 0 = " << theEnergyBins[0] << " for lnCollisions = " << lnCollisions;
0543 lnE = theEnergyBins[0];
0544 }
0545 }
0546
0547 return lnE;
0548 }
0549
0550 void CSCGasCollisions::fillCollisionsForThisGamma(float logGamma, std::vector<float> &collisions) const {
0551 std::vector<float>::const_iterator bigger =
0552 find_if(theGammaBins.begin(), theGammaBins.end(), [&logGamma](auto c) { return c > logGamma; });
0553
0554 if (bigger == theGammaBins.end()) {
0555
0556 LogTrace(me) << ": using highest gamma bin"
0557 << " for logGamma = " << logGamma;
0558 for (int i = 0; i < N_ENERGY; ++i)
0559 collisions[i] = theCollisionTable[i * N_GAMMA];
0560 } else {
0561
0562 std::vector<float>::difference_type ihi = bigger - theGammaBins.begin();
0563 if (ihi > 0) {
0564 double dlg2 = *bigger--;
0565
0566
0567 double dlg1 = *bigger;
0568 double dlg = (logGamma - dlg1) / (dlg2 - dlg1);
0569 double omdlg = 1. - dlg;
0570 for (int i = 0; i < N_ENERGY; ++i)
0571 collisions[i] = theCollisionTable[i * N_GAMMA + ihi - 1] * omdlg + theCollisionTable[i * N_GAMMA + ihi] * dlg;
0572 } else {
0573
0574 LogTrace(me) << ": using lowest gamma bin"
0575 << " for logGamma = " << logGamma;
0576
0577 for (int i = 0; i < N_ENERGY; ++i)
0578 collisions[i] = theCollisionTable[i * N_GAMMA];
0579 }
0580 }
0581 }