28bool IsComment(
const std::string& line) {
29 if (line.empty())
return false;
30 if (line[0] ==
'#')
return true;
34std::string GetDescription(
const unsigned int index,
36 return std::string(scrpt[index],
40std::string GetDescription(
const unsigned int i1,
const unsigned int i2,
42 return std::string(scrpt[i1][i2],
46void SetScatteringParameters(
const int model,
const double parIn,
double& cut,
50 if (model <= 0)
return;
64 const double cns = parIn - 0.5;
65 const double thetac =
asin(2. *
sqrt(cns - cns * cns));
66 const double fac = (1. -
cos(thetac)) /
pow(
sin(thetac), 2.);
67 parOut = cns * fac + 0.5;
68 cut = thetac * 2. / Garfield::Pi;
75const int MediumMagboltz::DxcTypeRad = 0;
76const int MediumMagboltz::DxcTypeCollIon = 1;
77const int MediumMagboltz::DxcTypeCollNonIon = -1;
82 m_eStep(m_eMax /
Magboltz::nEnergySteps),
83 m_eStepInv(1. / m_eStep),
85 m_eHighLog(log(m_eHigh)),
88 m_eStepGamma(m_eFinalGamma / nEnergyStepsGamma) {
118 m_cfTotLog.assign(nEnergyStepsLog, 0.);
121 m_cfLog.assign(nEnergyStepsLog,
133 const std::string& gas2,
const double f2,
134 const std::string& gas3,
const double f3,
135 const std::string& gas4,
const double f4,
136 const std::string& gas5,
const double f5,
137 const std::string& gas6,
const double f6)
140 gas4, f4, gas5, f5, gas6, f6);
145 std::cerr <<
m_className <<
"::SetMaxElectronEnergy: Invalid energy.\n";
150 std::lock_guard<std::mutex> guard(m_mutex);
153 m_eStepInv = 1. / m_eStep;
163 std::cerr <<
m_className <<
"::SetMaxPhotonEnergy: Invalid energy.\n";
169 m_eStepGamma = m_eFinalGamma / nEnergyStepsGamma;
178 m_useOpalBeaty =
true;
179 m_useGreenSawada =
false;
183 m_useOpalBeaty =
false;
184 m_useGreenSawada =
true;
189 if (!m_hasGreenSawada[i]) {
191 std::cout <<
m_className <<
"::SetSplittingFunctionGreenSawada:\n";
194 std::cout <<
" Fit parameters for " <<
m_gas[i] <<
" not available.\n"
195 <<
" Using Opal-Beaty formula instead.\n";
201 m_useOpalBeaty =
false;
202 m_useGreenSawada =
false;
207 std::cout <<
m_className <<
"::EnableDeexcitation:\n"
208 <<
" Penning transfer will be switched off.\n";
216 m_useDeexcitation =
true;
218 m_dxcProducts.clear();
223 if (!m_useDeexcitation) {
224 std::cout <<
m_className <<
"::EnableRadiationTrapping:\n "
225 <<
"Radiation trapping is enabled but de-excitation is not.\n";
233 m_lambdaPenning.fill(0.);
241 const double lambda) {
246 m_lambdaPenning.fill(0.);
249 if (!Update())
return false;
250 unsigned int nLevelsFound = 0;
251 for (
unsigned int i = 0; i < m_nTerms; ++i) {
252 if (m_csType[i] % nCsTypes == ElectronCollisionTypeExcitation) {
259 if (nLevelsFound > 0) {
260 std::cout <<
m_className <<
"::EnablePenningTransfer:\n "
261 <<
"Updated Penning transfer parameters for " << nLevelsFound
262 <<
" excitation cross-sections.\n";
264 std::cerr <<
m_className <<
"::EnablePenningTransfer:\n Warning: "
265 <<
"mismatch between number of excitation cross-sections ("
266 << nLevelsFound <<
")\n and number of excitation rates in "
267 <<
"the gas table (" <<
m_excLevels.size() <<
").\n "
268 <<
"The gas table was probably calculated using a different "
269 <<
"version of Magboltz.\n";
272 std::cerr <<
m_className <<
"::EnablePenningTransfer:\n "
273 <<
"No excitation cross-sections in the present energy range.\n";
276 if (m_useDeexcitation) {
277 std::cout <<
m_className <<
"::EnablePenningTransfer:\n "
278 <<
"Deexcitation handling will be switched off.\n";
285 std::string gasname) {
291 if (gasname.empty())
return false;
296 if (
m_gas[i] == gasname) {
303 if (!Update())
return false;
304 unsigned int nLevelsFound = 0;
305 for (
unsigned int i = 0; i < m_nTerms; ++i) {
306 if (
int(m_csType[i] / nCsTypes) != iGas)
continue;
307 if (m_csType[i] % nCsTypes == ElectronCollisionTypeExcitation) {
314 if (nLevelsFound > 0) {
315 std::cout <<
m_className <<
"::EnablePenningTransfer:\n";
317 std::cout <<
" Penning transfer parameters for " << nLevelsFound
318 <<
" " << gasname <<
" excitation levels set to:\n"
322 std::cout <<
" Penning transfer probability for " << nLevelsFound
323 <<
" " << gasname <<
" excitation levels set to r = "
327 std::cerr <<
m_className <<
"::EnablePenningTransfer:\n " << gasname
328 <<
" has no excitation levels in the present energy range.\n";
339 m_lambdaPenning.fill(0.);
349 if (gasname.empty())
return false;
354 if (
m_gas[i] == gasname) {
360 if (iGas < 0)
return false;
362 unsigned int nLevelsFound = 0;
363 for (
unsigned int i = 0; i < m_nTerms; ++i) {
364 if (
int(m_csType[i] / nCsTypes) == iGas) {
366 m_lambdaPenning[i] = 0.;
368 if (m_csType[i] % nCsTypes == ElectronCollisionTypeExcitation &&
369 m_rPenning[i] > Small) {
375 if (nLevelsFound == 0) {
377 std::cout <<
m_className <<
"::DisablePenningTransfer:\n"
378 <<
" Penning transfer switched off for all excitations.\n";
386 std::cerr <<
m_className <<
"::SetExcitationScaling: Incorrect value.\n";
392 if (gasname.empty()) {
393 std::cerr <<
m_className <<
"::SetExcitationScaling: Unknown gas name.\n";
400 if (
m_gas[i] == gasname) {
408 std::cerr <<
m_className <<
"::SetExcitationScaling:\n"
409 <<
" Specified gas (" << gasname
410 <<
") is not part of the present gas mixture.\n";
421 std::cerr <<
m_className <<
"::Initialise: Nothing changed.\n";
425 return Update(verbose);
435 std::cout <<
" Electron cross-sections:\n";
437 for (
unsigned int i = 0; i < m_nTerms; ++i) {
439 int type = m_csType[i] % nCsTypes;
440 if (igas !=
int(m_csType[i] / nCsTypes)) {
441 igas = int(m_csType[i] / nCsTypes);
442 std::cout <<
" " <<
m_gas[igas] <<
"\n";
446 double e = m_rgas[igas] * m_energyLoss[i];
447 std::cout <<
" Level " << i <<
": " << m_description[i] <<
"\n";
448 std::cout <<
" Type " << type;
449 if (type == ElectronCollisionTypeElastic) {
450 std::cout <<
" (elastic)\n";
451 }
else if (type == ElectronCollisionTypeIonisation) {
452 std::cout <<
" (ionisation). Ionisation threshold: " << e <<
" eV.\n";
453 }
else if (type == ElectronCollisionTypeAttachment) {
454 std::cout <<
" (attachment)\n";
455 }
else if (type == ElectronCollisionTypeInelastic) {
456 std::cout <<
" (inelastic). Energy loss: " << e <<
" eV.\n";
457 }
else if (type == ElectronCollisionTypeExcitation) {
458 std::cout <<
" (excitation). Excitation energy: " << e <<
" eV.\n";
459 }
else if (type == ElectronCollisionTypeSuperelastic) {
460 std::cout <<
" (super-elastic). Energy gain: " << -e <<
" eV.\n";
461 }
else if (type == ElectronCollisionTypeVirtual) {
462 std::cout <<
" (virtual)\n";
464 std::cout <<
" (unknown)\n";
466 if (type == ElectronCollisionTypeExcitation &&
m_usePenning &&
468 std::cout <<
" Penning transfer coefficient: "
469 << m_rPenning[i] <<
"\n";
470 }
else if (type == ElectronCollisionTypeExcitation && m_useDeexcitation) {
471 const int idxc = m_iDeexcitation[i];
472 if (idxc < 0 || idxc >= (
int)m_deexcitations.size()) {
473 std::cout <<
" Deexcitation cascade not implemented.\n";
476 const auto& dxc = m_deexcitations[idxc];
478 std::cout <<
" Oscillator strength: " << dxc.osc <<
"\n";
480 std::cout <<
" Decay channels:\n";
481 const int nChannels = dxc.type.size();
482 for (
int j = 0; j < nChannels; ++j) {
483 if (dxc.type[j] == DxcTypeRad) {
484 std::cout <<
" Radiative decay to ";
485 if (dxc.final[j] < 0) {
486 std::cout <<
"ground state: ";
488 std::cout << m_deexcitations[dxc.final[j]].label <<
": ";
490 }
else if (dxc.type[j] == DxcTypeCollIon) {
491 if (dxc.final[j] < 0) {
492 std::cout <<
" Penning ionisation: ";
494 std::cout <<
" Associative ionisation: ";
496 }
else if (dxc.type[j] == DxcTypeCollNonIon) {
497 if (dxc.final[j] >= 0) {
498 std::cout <<
" Collision-induced transition to "
499 << m_deexcitations[dxc.final[j]].label <<
": ";
501 std::cout <<
" Loss: ";
504 const double br = j == 0 ? dxc.p[j] : dxc.p[j] - dxc.p[j - 1];
505 std::cout << std::setprecision(5) << br * 100. <<
"%\n";
513 if (!Update())
return 0.;
521 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Invalid energy.\n";
525 std::cerr <<
m_className <<
"::GetElectronCollisionRate:\n Rate at " << e
526 <<
" eV is not included in the current table.\n "
527 <<
"Increasing energy range to " << 1.05 * e <<
" eV.\n";
532 if (!Update())
return 0.;
537 return m_cfTot[int(e * m_eStepInv)];
541 const double eLog = log(e);
542 int iE = int((eLog - m_eHighLog) / m_lnStep);
544 const double fmax = m_cfTotLog[iE];
545 const double fmin = iE == 0 ? log(m_cfTot.back()) : m_cfTotLog[iE - 1];
546 const double emin = m_eHighLog + iE * m_lnStep;
547 const double f = fmin + (eLog - emin) * (fmax - fmin) / m_lnStep;
552 const unsigned int level,
556 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Invalid energy.\n";
561 if (level >= m_nTerms) {
562 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Invalid level.\n";
571 const int iE = int(e * m_eStepInv);
575 rate *= m_cf[iE][level] - m_cf[iE][level - 1];
579 const int iE = int((log(e) - m_eHighLog) / m_lnStep);
581 rate *= m_cfLog[iE][0];
583 rate *= m_cfLog[iE][level] - m_cfLog[iE][level - 1];
590 int& level,
double& e1,
double& dx,
double& dy,
double& dz,
591 std::vector<std::pair<Particle, double> >& secondaries,
int& ndxc,
596 std::cerr <<
m_className <<
"::ElectronCollision: Invalid energy.\n";
601 std::cerr <<
m_className <<
"::ElectronCollision:\n Requested energy ("
602 << e <<
" eV) exceeds current energy range.\n"
603 <<
" Increasing energy range to " << 1.05 * e <<
" eV.\n";
608 if (!Update())
return false;
616 const int iE = int(e * m_eStepInv);
621 if (r > m_cf[iE][0]) {
623 int iUp = m_nTerms - 1;
624 while (iUp - iLow > 1) {
625 int iMid = (iLow + iUp) >> 1;
626 if (r < m_cf[iE][iMid]) {
635 angCut = m_scatCut[iE][level];
636 angPar = m_scatPar[iE][level];
640 const int iE = std::min(std::max(
int(log(e / m_eHigh) / m_lnStep), 0),
641 nEnergyStepsLog - 1);
645 if (r > m_cfLog[iE][0]) {
647 int iUp = m_nTerms - 1;
648 while (iUp - iLow > 1) {
649 int iMid = (iLow + iUp) >> 1;
650 if (r < m_cfLog[iE][iMid]) {
659 angCut = m_scatCutLog[iE][level];
660 angPar = m_scatParLog[iE][level];
664 type = m_csType[level] % nCsTypes;
665 const int igas = int(m_csType[level] / nCsTypes);
667 ++m_nCollisions[type];
668 ++m_nCollisionsDetailed[level];
671 double loss = m_energyLoss[level];
673 if (type == ElectronCollisionTypeVirtual)
return true;
675 if (type == ElectronCollisionTypeIonisation) {
679 if (e < loss) loss = e - 0.0001;
680 if (m_useOpalBeaty) {
682 const double w = m_wOpalBeaty[level];
683 esec = w * tan(
RndmUniform() * atan(0.5 * (e - loss) / w));
686 }
else if (m_useGreenSawada) {
687 const double gs = m_parGreenSawada[igas][0];
688 const double gb = m_parGreenSawada[igas][1];
689 const double w = gs * e / (e + gb);
690 const double ts = m_parGreenSawada[igas][2];
691 const double ta = m_parGreenSawada[igas][3];
692 const double tb = m_parGreenSawada[igas][4];
693 const double esec0 = ts - ta / (e + tb);
696 w * tan((r - 1.) * atan(esec0 / w) +
697 r * atan((0.5 * (e - loss) - esec0) / w));
701 if (esec <= 0) esec = Small;
707 bool fluorescence =
false;
708 if (m_yFluorescence[level] > Small) {
709 if (
RndmUniform() < m_yFluorescence[level]) fluorescence =
true;
713 if (m_nAuger2[level] > 0) {
714 const double eav = m_eAuger2[level] / m_nAuger2[level];
715 for (
unsigned int i = 0; i < m_nAuger2[level]; ++i) {
719 if (m_nFluorescence[level] > 0) {
720 const double eav = m_eFluorescence[level] / m_nFluorescence[level];
721 for (
unsigned int i = 0; i < m_nFluorescence[level]; ++i) {
725 }
else if (m_nAuger1[level] > 0) {
726 const double eav = m_eAuger1[level] / m_nAuger1[level];
727 for (
unsigned int i = 0; i < m_nAuger1[level]; ++i) {
731 }
else if (type == ElectronCollisionTypeExcitation) {
733 if (m_useDeexcitation && m_iDeexcitation[level] >= 0) {
735 ComputeDeexcitationInternal(m_iDeexcitation[level], fLevel);
736 ndxc = m_dxcProducts.size();
738 m_dxcProducts.clear();
744 std::cout <<
m_className <<
"::ElectronCollision:\n"
745 <<
" Level: " << level <<
"\n"
746 <<
" Ionization potential: " << m_minIonPot <<
"\n"
747 <<
" Excitation energy: " << loss * m_rgas[igas] <<
"\n"
748 <<
" Penning probability: " << m_rPenning[level] <<
"\n";
750 if (loss * m_rgas[igas] > m_minIonPot &&
754 double esec = loss * m_rgas[igas] - m_minIonPot;
755 if (esec <= 0) esec = Small;
760 if (m_lambdaPenning[level] > Small) {
762 newDxcProd.s = m_lambdaPenning[level] * std::cbrt(
RndmUniformPos());
764 newDxcProd.energy = esec;
765 newDxcProd.type = DxcProdTypeElectron;
766 m_dxcProducts.push_back(std::move(newDxcProd));
773 if (e < loss) loss = e - 0.0001;
777 if (m_useAnisotropic) {
778 switch (m_scatModel[level]) {
786 ctheta0 = (ctheta0 + angPar) / (1. + angPar * ctheta0);
789 std::cerr <<
m_className <<
"::ElectronCollision:\n"
790 <<
" Unknown scattering model.\n"
791 <<
" Using isotropic distribution.\n";
796 const double s1 = m_rgas[igas];
797 const double theta0 = acos(ctheta0);
798 const double arg = std::max(1. - s1 * loss / e, Small);
799 const double d = 1. - ctheta0 * sqrt(arg);
802 e1 = std::max(e * (1. - loss / (s1 * e) - 2. * d * m_s2[igas]), Small);
803 double q = std::min(sqrt((e / e1) * arg) / s1, 1.);
804 const double theta = asin(q * sin(theta0));
805 double ctheta = cos(theta);
807 const double u = (s1 - 1.) * (s1 - 1.) / arg;
808 if (ctheta0 * ctheta0 > u) ctheta = -ctheta;
810 const double stheta = sin(theta);
812 dz = std::min(dz, 1.);
813 const double argZ = sqrt(dx * dx + dy * dy);
817 const double cphi = cos(phi);
818 const double sphi = sin(phi);
820 const double a = stheta / argZ;
821 const double dz1 = dz * ctheta + argZ * stheta * sphi;
822 const double dy1 = dy * ctheta + a * (dx * cphi - dy * dz * sphi);
823 const double dx1 = dx * ctheta - a * (dy * cphi + dx * dz * sphi);
836 double& s,
int& type,
837 double& energy)
const {
838 if (i >= m_dxcProducts.size() || !(m_useDeexcitation ||
m_usePenning)) {
841 t = m_dxcProducts[i].t;
842 s = m_dxcProducts[i].s;
843 type = m_dxcProducts[i].type;
844 energy = m_dxcProducts[i].energy;
850 std::cerr <<
m_className <<
"::GetPhotonCollisionRate: Invalid energy.\n";
851 return m_cfTotGamma[0];
853 if (e > m_eFinalGamma) {
854 std::cerr <<
m_className <<
"::GetPhotonCollisionRate:\n Rate at " << e
855 <<
" eV is not included in the current table.\n"
856 <<
" Increasing energy range to " << 1.05 * e <<
" eV.\n";
860 if (!Update())
return 0.;
863 std::min(std::max(
int(e / m_eStepGamma), 0), nEnergyStepsGamma - 1);
865 double cfSum = m_cfTotGamma[iE];
866 if (m_useDeexcitation && m_useRadTrap && !m_deexcitations.empty()) {
868 for (
const auto& dxc : m_deexcitations) {
869 if (dxc.cf > 0. && fabs(e - dxc.energy) <= dxc.width) {
871 TMath::Voigt(e - dxc.energy, dxc.sDoppler, 2 * dxc.gPressure);
880 double& e1,
double& ctheta,
int& nsec,
883 std::cerr <<
m_className <<
"::GetPhotonCollision: Invalid energy.\n";
886 if (e > m_eFinalGamma) {
887 std::cerr <<
m_className <<
"::GetPhotonCollision:\n Provided energy ("
888 << e <<
" eV) exceeds current energy range.\n"
889 <<
" Increasing energy range to " << 1.05 * e <<
" eV.\n";
893 if (!Update())
return false;
897 std::min(std::max(
int(e / m_eStepGamma), 0), nEnergyStepsGamma - 1);
899 double r = m_cfTotGamma[iE];
900 if (m_useDeexcitation && m_useRadTrap && !m_deexcitations.empty()) {
902 std::vector<double> pLine(0);
903 std::vector<int> iLine(0);
905 const unsigned int nDeexcitations = m_deexcitations.size();
906 for (
unsigned int i = 0; i < nDeexcitations; ++i) {
907 const auto& dxc = m_deexcitations[i];
908 if (dxc.cf > 0. && fabs(e - dxc.energy) <= dxc.width) {
910 TMath::Voigt(e - dxc.energy, dxc.sDoppler, 2 * dxc.gPressure);
917 if (nLines > 0 && r >= m_cfTotGamma[iE]) {
919 for (
int i = 0; i < nLines; ++i) {
921 ++m_nPhotonCollisions[PhotonCollisionTypeExcitation];
923 ComputeDeexcitationInternal(iLine[i], fLevel);
924 type = PhotonCollisionTypeExcitation;
925 nsec = m_dxcProducts.size();
929 std::cerr <<
m_className <<
"::GetPhotonCollision:\n";
930 std::cerr <<
" Random sampling of deexcitation line failed.\n";
931 std::cerr <<
" Program bug!\n";
938 if (r <= m_cfGamma[iE][0]) {
940 }
else if (r >= m_cfGamma[iE][m_nPhotonTerms - 1]) {
941 level = m_nPhotonTerms - 1;
943 const auto begin = m_cfGamma[iE].cbegin();
944 level = std::lower_bound(begin, begin + m_nPhotonTerms, r) - begin;
949 type = csTypeGamma[level];
951 type = type % nCsTypesGamma;
952 int ngas = int(csTypeGamma[level] / nCsTypesGamma);
953 ++m_nPhotonCollisions[type];
956 esec = std::max(e - m_ionPot[ngas], Small);
967 m_nCollisions.fill(0);
968 m_nCollisionsDetailed.assign(m_nTerms, 0);
970 m_nPhotonCollisions.fill(0);
974 return std::accumulate(std::begin(m_nCollisions), std::end(m_nCollisions), 0);
978 unsigned int& nElastic,
unsigned int& nIonisation,
979 unsigned int& nAttachment,
unsigned int& nInelastic,
980 unsigned int& nExcitation,
unsigned int& nSuperelastic)
const {
981 nElastic = m_nCollisions[ElectronCollisionTypeElastic];
982 nIonisation = m_nCollisions[ElectronCollisionTypeIonisation];
983 nAttachment = m_nCollisions[ElectronCollisionTypeAttachment];
984 nInelastic = m_nCollisions[ElectronCollisionTypeInelastic];
985 nExcitation = m_nCollisions[ElectronCollisionTypeExcitation];
986 nSuperelastic = m_nCollisions[ElectronCollisionTypeSuperelastic];
987 return nElastic + nIonisation + nAttachment + nInelastic + nExcitation +
992 if (!Update())
return 0;
997 std::string& descr,
double& e) {
998 if (!Update())
return false;
1000 if (i >= m_nTerms) {
1001 std::cerr <<
m_className <<
"::GetLevel: Index out of range.\n";
1006 type = m_csType[i] % nCsTypes;
1007 ngas = int(m_csType[i] / nCsTypes);
1009 descr = m_description[i];
1011 e = m_rgas[ngas] * m_energyLoss[i];
1014 <<
" Level " << i <<
": " << descr <<
"\n"
1015 <<
" Type " << type <<
"\n"
1016 <<
" Threshold energy: " << e <<
" eV\n";
1017 if (type == ElectronCollisionTypeExcitation &&
m_usePenning &&
1019 std::cout <<
" Penning transfer coefficient: " << m_rPenning[i]
1021 }
else if (type == ElectronCollisionTypeExcitation && m_useDeexcitation) {
1022 const int idxc = m_iDeexcitation[i];
1023 if (idxc < 0 || idxc >= (
int)m_deexcitations.size()) {
1024 std::cout <<
" Deexcitation cascade not implemented.\n";
1027 const auto& dxc = m_deexcitations[idxc];
1029 std::cout <<
" Oscillator strength: " << dxc.osc <<
"\n";
1031 std::cout <<
" Decay channels:\n";
1032 const int nChannels = dxc.type.size();
1033 for (
int j = 0; j < nChannels; ++j) {
1034 if (dxc.type[j] == DxcTypeRad) {
1035 std::cout <<
" Radiative decay to ";
1036 if (dxc.final[j] < 0) {
1037 std::cout <<
"ground state: ";
1039 std::cout << m_deexcitations[dxc.final[j]].label <<
": ";
1041 }
else if (dxc.type[j] == DxcTypeCollIon) {
1042 if (dxc.final[j] < 0) {
1043 std::cout <<
" Penning ionisation: ";
1045 std::cout <<
" Associative ionisation: ";
1047 }
else if (dxc.type[j] == DxcTypeCollNonIon) {
1048 if (dxc.final[j] >= 0) {
1049 std::cout <<
" Collision-induced transition to "
1050 << m_deexcitations[dxc.final[j]].label <<
": ";
1052 std::cout <<
" Loss: ";
1055 const double br = j == 0 ? dxc.p[j] : dxc.p[j] - dxc.p[j - 1];
1056 std::cout << std::setprecision(5) << br * 100. <<
"%\n";
1065 double& r,
double& lambda) {
1068 if (!Update())
return false;
1069 if (i >= m_nTerms)
return false;
1071 lambda = m_lambdaPenning[i];
1076 const unsigned int level)
const {
1077 if (level >= m_nTerms) {
1078 std::cerr <<
m_className <<
"::GetNumberOfElectronCollisions: "
1079 <<
"Level " << level <<
" does not exist.\n";
1082 return m_nCollisionsDetailed[level];
1086 return std::accumulate(std::begin(m_nPhotonCollisions),
1087 std::end(m_nPhotonCollisions), 0);
1091 unsigned int& nElastic,
unsigned int& nIonising,
1092 unsigned int& nInelastic)
const {
1093 nElastic = m_nPhotonCollisions[0];
1094 nIonising = m_nPhotonCollisions[1];
1095 nInelastic = m_nPhotonCollisions[2];
1096 return nElastic + nIonising + nInelastic;
1101 if (input.empty())
return 0;
1103 if (input ==
"CF4") {
1105 }
else if (input ==
"Ar") {
1107 }
else if (input ==
"He" || input ==
"He-4") {
1110 }
else if (input ==
"He-3") {
1113 }
else if (input ==
"Ne") {
1115 }
else if (input ==
"Kr") {
1117 }
else if (input ==
"Xe") {
1119 }
else if (input ==
"CH4") {
1122 }
else if (input ==
"C2H6") {
1125 }
else if (input ==
"C3H8") {
1128 }
else if (input ==
"iC4H10") {
1131 }
else if (input ==
"CO2") {
1133 }
else if (input ==
"neoC5H12") {
1136 }
else if (input ==
"H2O") {
1138 }
else if (input ==
"O2") {
1140 }
else if (input ==
"N2") {
1142 }
else if (input ==
"NO") {
1145 }
else if (input ==
"N2O") {
1148 }
else if (input ==
"C2H4") {
1151 }
else if (input ==
"C2H2") {
1154 }
else if (input ==
"H2") {
1157 }
else if (input ==
"D2") {
1160 }
else if (input ==
"CO") {
1163 }
else if (input ==
"Methylal") {
1166 }
else if (input ==
"DME") {
1168 }
else if (input ==
"Reid-Step") {
1170 }
else if (input ==
"Maxwell-Model") {
1172 }
else if (input ==
"Reid-Ramp") {
1174 }
else if (input ==
"C2F6") {
1176 }
else if (input ==
"SF6") {
1178 }
else if (input ==
"NH3") {
1180 }
else if (input ==
"C3H6") {
1183 }
else if (input ==
"cC3H6") {
1186 }
else if (input ==
"CH3OH") {
1189 }
else if (input ==
"C2H5OH") {
1192 }
else if (input ==
"C3H7OH") {
1195 }
else if (input ==
"Cs") {
1197 }
else if (input ==
"F2") {
1200 }
else if (input ==
"CS2") {
1202 }
else if (input ==
"COS") {
1204 }
else if (input ==
"CD4") {
1207 }
else if (input ==
"BF3") {
1209 }
else if (input ==
"C2HF5" || input ==
"C2H2F4") {
1211 }
else if (input ==
"TMA") {
1213 }
else if (input ==
"nC3H7OH") {
1216 }
else if (input ==
"paraH2") {
1219 }
else if (input ==
"orthoD2") {
1222 }
else if (input ==
"CHF3") {
1224 }
else if (input ==
"CF3Br") {
1226 }
else if (input ==
"C3F8") {
1228 }
else if (input ==
"O3") {
1231 }
else if (input ==
"Hg") {
1234 }
else if (input ==
"H2S") {
1236 }
else if (input ==
"nC4H10") {
1239 }
else if (input ==
"nC5H12") {
1242 }
else if (input ==
"N2 (Phelps)") {
1244 }
else if (input ==
"GeH4") {
1247 }
else if (input ==
"SiH4") {
1250 }
else if (input ==
"CCl4") {
1254 std::cerr <<
"MediumMagboltz::GetGasNumberMagboltz:\n"
1255 <<
" Gas " << input <<
" is not defined.\n";
1259bool MediumMagboltz::Update(
const bool verbose) {
1262 std::lock_guard<std::mutex> guard(m_mutex);
1263 if (!Mixer(verbose)) {
1265 <<
"::Update: Error calculating the collision rates table.\n";
1273bool MediumMagboltz::Mixer(
const bool verbose) {
1291 const double en = (i + 0.5) * m_eStep;
1301 const double prefactor = dens * SpeedOfLight *
sqrt(2. / ElectronMass);
1309 m_parGreenSawada.fill({1., 0., 0., 0., 0.});
1310 m_hasGreenSawada.fill(
false);
1312 m_wOpalBeaty.fill(1.);
1313 m_energyLoss.fill(0.);
1316 m_yFluorescence.fill(0.);
1321 m_nFluorescence.fill(0);
1322 m_eFluorescence.fill(0.);
1324 m_scatModel.fill(0);
1326 m_rPenning.fill(0.);
1327 m_lambdaPenning.fill(0.);
1329 m_deexcitations.clear();
1330 m_iDeexcitation.fill(-1);
1334 m_cfTotLog.assign(nEnergyStepsLog, 0.);
1338 m_cfLog.assign(nEnergyStepsLog,
1346 m_scatParLog.assign(nEnergyStepsLog,
1348 m_scatCutLog.assign(nEnergyStepsLog,
1383 <<
" does not have a gas number in Magboltz.\n";
1391 <<
" linear energy steps between 0 and "
1392 << std::min(m_eMax, m_eHigh) <<
" eV.\n";
1393 if (m_eMax > m_eHigh) {
1394 std::cout <<
" " << nEnergyStepsLog <<
" logarithmic steps between "
1395 << m_eHigh <<
" and " << m_eMax <<
" eV\n";
1400 std::ofstream outfile;
1401 if (m_useCsOutput) {
1402 outfile.open(
"cs.txt", std::ios::out);
1403 outfile <<
"# energy [eV] vs. cross-section [cm2]\n";
1414 static std::int64_t nIn = 0;
1416 static std::int64_t nIon = 0;
1418 static std::int64_t nAtt = 0;
1420 static std::int64_t nNull = 0;
1422 static double virial = 0.;
1431 static std::int64_t kEl[6];
1446 std::int64_t ngs = gasNumber[iGas];
1448 &ngs, q[0], qIn[0], &nIn, &e[0], eIn, name, &virial, eoby, pEqEl[0],
1449 pEqIn[0], penFra[0], kEl, kIn, qIon[0], pEqIon[0], eIon, &nIon,
1450 qAtt[0], &nAtt, qNull[0], &nNull, scln, nc0, ec0, wklm, efl,
1451 ng1, eg1, ng2, eg2, scrpt, scrptn,
1454 const double m = (2. / e[1]) * ElectronMass / AtomicMassUnitElectronVolt;
1455 std::cout <<
" " << name <<
"\n"
1456 <<
" mass: " << m <<
" amu\n";
1458 std::cout <<
" ionisation threshold: " << eIon[0] <<
" eV\n";
1460 std::cout <<
" ionisation threshold: " << e[2] <<
" eV\n";
1462 if (e[3] > 0. && e[4] > 0.) {
1463 std::cout <<
" cross-sections at minimum ionising energy:\n"
1464 <<
" excitation: " << e[3] * 1.e18 <<
" Mbarn\n"
1465 <<
" ionisation: " << e[4] * 1.e18 <<
" Mbarn\n";
1476 const double van =
m_fraction[iGas] * prefactor;
1478 if (m_useCsOutput) {
1479 outfile <<
"# cross-sections for " << name <<
"\n";
1480 outfile <<
"# cross-section types:\n";
1481 outfile <<
"# elastic\n";
1485 m_scatModel[np] = kEl[1];
1486 const double r = 1. + 0.5 * e[1];
1488 m_s2[iGas] = (r - 1.) / (r * r);
1489 m_energyLoss[np] = 0.;
1490 m_description[np] = GetDescription(1, scrpt);
1491 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeElastic;
1492 bool withIon =
false;
1495 for (
int j = 0; j < nIon; ++j) {
1496 if (m_eMax < eIon[j])
continue;
1500 m_scatModel[np] = kEl[2];
1501 m_energyLoss[np] = eIon[j] / r;
1502 m_wOpalBeaty[np] = eoby[j];
1503 m_yFluorescence[np] = wklm[j];
1504 m_nAuger1[np] = nc0[j];
1505 m_eAuger1[np] = ec0[j];
1506 m_nFluorescence[np] = ng1[j];
1507 m_eFluorescence[np] = eg1[j];
1508 m_nAuger2[np] = ng2[j];
1509 m_eAuger2[np] = eg2[j];
1510 m_description[np] = GetDescription(2 + j, scrpt);
1511 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeIonisation;
1512 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1514 m_parGreenSawada[iGas][0] = eoby[0];
1515 m_parGreenSawada[iGas][4] = 2 * eIon[0];
1516 m_ionPot[iGas] = eIon[0];
1518 if (m_eMax >= e[2]) {
1522 m_scatModel[np] = kEl[2];
1523 m_energyLoss[np] = e[2] / r;
1524 m_wOpalBeaty[np] = eoby[0];
1525 m_parGreenSawada[iGas][0] = eoby[0];
1526 m_parGreenSawada[iGas][4] = 2 * e[2];
1527 m_ionPot[iGas] = e[2];
1528 m_description[np] = GetDescription(2, scrpt);
1529 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeIonisation;
1530 if (m_useCsOutput) outfile <<
"# ionisation (gross)\n";
1534 for (
int j = 0; j < nAtt; ++j) {
1537 m_scatModel[np] = 0;
1538 m_energyLoss[np] = 0.;
1539 m_description[np] = GetDescription(2 + nIon + j, scrpt);
1540 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeAttachment;
1541 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1544 int nExc = 0, nSuperEl = 0;
1545 for (
int j = 0; j < nIn; ++j) {
1547 m_scatModel[np] = kIn[j];
1548 m_energyLoss[np] = eIn[j] / r;
1549 m_description[np] = GetDescription(4 + nIon + nAtt + j, scrpt);
1550 if ((m_description[np][1] ==
'E' && m_description[np][2] ==
'X') ||
1551 (m_description[np][0] ==
'E' && m_description[np][1] ==
'X') ||
1552 (
m_gas[iGas] ==
"N2" && eIn[j] > 6.)) {
1554 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeExcitation;
1556 }
else if (eIn[j] < 0.) {
1558 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeSuperelastic;
1562 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeInelastic;
1564 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1568 for (
int j = 0; j < nNull; ++j) {
1571 m_scatModel[np] = 0;
1572 m_energyLoss[np] = 0.;
1573 m_description[np] = GetDescription(j, scrptn);
1574 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeVirtual;
1575 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1581 if (m_useCsOutput) {
1582 outfile << (iE + 0.5) * m_eStep <<
" " << q[iE][1] <<
" ";
1585 m_cf[iE][np] = q[iE][1] * van;
1586 SetScatteringParameters(m_scatModel[np], pEqEl[iE][1], m_scatCut[iE][np],
1591 for (
int j = 0; j < nIon; ++j) {
1592 if (m_eMax < eIon[j])
continue;
1594 m_cf[iE][np] = qIon[iE][j] * van;
1595 SetScatteringParameters(m_scatModel[np], pEqIon[iE][j],
1596 m_scatCut[iE][np], m_scatPar[iE][np]);
1597 if (m_useCsOutput) outfile << qIon[iE][j] <<
" ";
1601 m_cf[iE][np] = q[iE][2] * van;
1602 SetScatteringParameters(m_scatModel[np], pEqEl[iE][2],
1603 m_scatCut[iE][np], m_scatPar[iE][np]);
1604 if (m_useCsOutput) outfile << q[iE][2] <<
" ";
1608 for (
int j = 0; j < nAtt; ++j) {
1610 m_cf[iE][np] = qAtt[iE][j] * van;
1612 m_scatPar[iE][np] = 0.5;
1613 if (m_useCsOutput) outfile << qAtt[iE][j] <<
" ";
1616 for (
int j = 0; j < nIn; ++j) {
1618 if (m_useCsOutput) outfile << qIn[iE][j] <<
" ";
1619 m_cf[iE][np] = qIn[iE][j] * van;
1621 m_cf[iE][np] *= m_scaleExc[iGas];
1622 if (m_cf[iE][np] < 0.) {
1624 <<
" Negative inelastic cross-section at "
1625 << (iE + 0.5) * m_eStep <<
" eV. Set to zero.\n";
1628 SetScatteringParameters(m_scatModel[np], pEqIn[iE][j],
1629 m_scatCut[iE][np], m_scatPar[iE][np]);
1631 if ((
m_debug || verbose) && nIn > 0 && iE == iemax) {
1632 std::cout <<
" " << nIn <<
" inelastic terms (" << nExc
1633 <<
" excitations, " << nSuperEl <<
" superelastic, "
1634 << nIn - nExc - nSuperEl <<
" other)\n";
1637 for (
int j = 0; j < nNull; ++j) {
1639 m_cf[iE][np] = qNull[iE][j] * van * scln[j];
1640 if (m_useCsOutput) outfile << qNull[iE][j] <<
" ";
1643 if (m_useCsOutput) outfile <<
"\n";
1645 if (m_eMax <= m_eHigh)
continue;
1648 const double rLog =
pow(m_eMax / m_eHigh, 1. / nEnergyStepsLog);
1649 m_lnStep = log(rLog);
1651 double emax = m_eHigh * rLog;
1653 for (
int iE = 0; iE < nEnergyStepsLog; ++iE) {
1659 &ngs, q[0], qIn[0], &nIn, e, eIn, name, &virial, eoby, pEqEl[0],
1660 pEqIn[0], penFra[0], kEl, kIn, qIon[0], pEqIon[0], eIon, &nIon,
1661 qAtt[0], &nAtt, qNull[0], &nNull, scln, nc0, ec0, wklm, efl,
1662 ng1, eg1, ng2, eg2, scrpt, scrptn,
1665 if (m_useCsOutput) outfile << emax <<
" " << q[iemax][1] <<
" ";
1667 m_cfLog[iE][np] = q[iemax][1] * van;
1668 SetScatteringParameters(m_scatModel[np], pEqEl[iemax][1],
1669 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1673 for (
int j = 0; j < nIon; ++j) {
1674 if (m_eMax < eIon[j])
continue;
1676 m_cfLog[iE][np] = qIon[iemax][j] * van;
1677 SetScatteringParameters(m_scatModel[np], pEqIon[iemax][j],
1678 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1679 if (m_useCsOutput) outfile << qIon[iemax][j] <<
" ";
1684 m_cfLog[iE][np] = q[iemax][2] * van;
1687 SetScatteringParameters(m_scatModel[np], pEqEl[iemax][2],
1688 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1689 if (m_useCsOutput) outfile << q[iemax][2] <<
" ";
1693 for (
int j = 0; j < nAtt; ++j) {
1695 m_cfLog[iE][np] = qAtt[iemax][j] * van;
1697 if (m_useCsOutput) outfile << qAtt[iemax][j] <<
" ";
1700 for (
int j = 0; j < nIn; ++j) {
1702 if (m_useCsOutput) outfile << qIn[iemax][j] <<
" ";
1703 m_cfLog[iE][np] = qIn[iemax][j] * van;
1705 m_cfLog[iE][np] *= m_scaleExc[iGas];
1706 if (m_cfLog[iE][np] < 0.) {
1708 <<
" Negative inelastic cross-section at " << emax
1709 <<
" eV. Set to zero.\n";
1710 m_cfLog[iE][np] = 0.;
1712 SetScatteringParameters(m_scatModel[np], pEqIn[iemax][j],
1713 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1716 for (
int j = 0; j < nNull; ++j) {
1718 m_cfLog[iE][np] = qNull[iemax][j] * van * scln[j];
1719 if (m_useCsOutput) outfile << qNull[iemax][j] <<
" ";
1722 if (m_useCsOutput) outfile <<
"\n";
1727 if (m_useCsOutput) outfile.close();
1730 auto it = std::min_element(std::begin(m_ionPot),
1733 std::string minIonPotGas =
m_gas[std::distance(std::begin(m_ionPot), it)];
1737 <<
" Lowest ionisation threshold in the mixture: "
1738 << m_minIonPot <<
" eV (" << minIonPotGas <<
")\n";
1743 for (
unsigned int k = 0; k < m_nTerms; ++k) {
1744 if (m_cf[iE][k] < 0.) {
1746 <<
" Negative collision rate at " << (iE + 0.5) * m_eStep
1747 <<
" eV, cross-section " << k <<
". Set to zero.\n";
1748 std::cout << m_description[k] <<
"\n";
1751 m_cfTot[iE] += m_cf[iE][k];
1754 if (m_cfTot[iE] > 0.) {
1755 for (
unsigned int k = 0; k < m_nTerms; ++k) m_cf[iE][k] /= m_cfTot[iE];
1757 for (
unsigned int k = 1; k < m_nTerms; ++k) {
1758 m_cf[iE][k] += m_cf[iE][k - 1];
1760 const double ekin = m_eStep * (iE + 0.5);
1761 m_cfTot[iE] *=
sqrt(ekin);
1764 const double re = ekin / ElectronMass;
1765 m_cfTot[iE] *=
sqrt(1. + 0.5 * re) / (1. + re);
1769 if (m_eMax > m_eHigh) {
1770 const double rLog =
pow(m_eMax / m_eHigh, 1. / nEnergyStepsLog);
1771 for (
int iE = 0; iE < nEnergyStepsLog; ++iE) {
1773 for (
unsigned int k = 0; k < m_nTerms; ++k) {
1774 if (m_cfLog[iE][k] < 0.) m_cfLog[iE][k] = 0.;
1775 m_cfTotLog[iE] += m_cfLog[iE][k];
1778 if (m_cfTotLog[iE] > 0.) {
1779 for (
unsigned int k = 0; k < m_nTerms; ++k) {
1780 m_cfLog[iE][k] /= m_cfTotLog[iE];
1783 for (
unsigned int k = 1; k < m_nTerms; ++k) {
1784 m_cfLog[iE][k] += m_cfLog[iE][k - 1];
1786 const double ekin = m_eHigh *
pow(rLog, iE + 1);
1787 const double re = ekin / ElectronMass;
1788 m_cfTotLog[iE] *=
sqrt(ekin) *
sqrt(1. + re) / (1. + re);
1790 m_cfTotLog[iE] = log(m_cfTotLog[iE]);
1797 if (m_cfTot[j] > m_cfNull) m_cfNull = m_cfTot[j];
1799 if (m_eMax > m_eHigh) {
1800 for (
int j = 0; j < nEnergyStepsLog; ++j) {
1801 const double r =
exp(m_cfTotLog[j]);
1802 if (r > m_cfNull) m_cfNull = r;
1807 m_nCollisionsDetailed.assign(m_nTerms, 0);
1808 m_nCollisions.fill(0);
1812 <<
" Energy [eV] Collision Rate [ns-1]\n";
1813 const double emax = std::min(m_eHigh, m_eMax);
1814 for (
int i = 0; i < 8; ++i) {
1815 const double en = (2 * i + 1) * emax / 16;
1817 std::printf(
" %10.2f %18.2f\n", en, cf);
1822 if (m_useDeexcitation) {
1823 ComputeDeexcitationTable(verbose);
1824 for (
const auto& dxc : m_deexcitations) {
1825 if (dxc.p.size() == dxc.final.size() && dxc.p.size() == dxc.type.size())
1828 <<
" Mismatch in deexcitation channel count. Program bug!\n"
1829 <<
" Deexcitation handling is switched off.\n";
1830 m_useDeexcitation =
false;
1836 if (!ComputePhotonCollisionTable(verbose)) {
1839 if (m_useDeexcitation) {
1840 std::cerr <<
" Deexcitation handling is switched off.\n";
1841 m_useDeexcitation =
false;
1847 std::cout <<
m_className <<
"::Mixer: Resetting transfer probabilities.\n"
1850 std::cout <<
" Component " << i <<
": " <<
m_rPenningGas[i] <<
"\n";
1857 for (
unsigned int i = 0; i < m_nTerms; ++i) {
1858 int iGas = int(m_csType[i] / nCsTypes);
1873 if (!Update())
return;
1875 std::array<float, Magboltz::nEnergySteps> en;
1877 en[k] = (k + 0.5) * m_eStep;
1879 std::array<std::array<float, Magboltz::nEnergySteps>, 5> cs;
1881 for (
size_t j = 0; j < 5; ++j) cs[j].fill(0.);
1883 const double scale = sqrt(0.5 * ElectronMass) / (density * SpeedOfLight);
1884 for (
unsigned int j = 0; j < m_nTerms; ++j) {
1885 if (
int(m_csType[j] / nCsTypes) !=
int(i))
continue;
1886 int cstype = m_csType[j] % nCsTypes;
1887 if (cstype >= ElectronCollisionTypeVirtual)
continue;
1889 if (cstype == 5) cstype = 3;
1891 double cf = m_cf[k][j];
1892 if (j > 0) cf -= m_cf[k][j - 1];
1893 cs[cstype][k] += cf * 1.e18 * scale;
1897 TCanvas* canvas =
new TCanvas(name.c_str(),
m_gas[i].c_str(), 800, 600);
1903 canvas->DrawFrame(en[0], 0.01, en.back(), 100.,
1904 ";energy [eV];#sigma [Mbarn]");
1907 const std::array<short, 5> cols = {kBlack, kCyan - 2, kRed + 2,
1908 kGreen + 3, kMagenta + 3};
1909 for (
size_t j = 0; j < 5; ++j) {
1910 if (*std::max_element(cs[j].begin(), cs[j].end()) < 1.e-10)
continue;
1911 gr.SetLineColor(cols[j]);
1919void MediumMagboltz::SetupGreenSawada() {
1921 const double ta = 1000.;
1922 const double tb = m_parGreenSawada[i][4];
1923 m_hasGreenSawada[i] =
true;
1924 if (
m_gas[i] ==
"He" ||
m_gas[i] ==
"He-3") {
1925 m_parGreenSawada[i] = {15.5, 24.5, -2.25, ta, tb};
1926 }
else if (
m_gas[i] ==
"Ne") {
1927 m_parGreenSawada[i] = {24.3, 21.6, -6.49, ta, tb};
1928 }
else if (
m_gas[i] ==
"Ar") {
1929 m_parGreenSawada[i] = {6.92, 7.85, 6.87, ta, tb};
1930 }
else if (
m_gas[i] ==
"Kr") {
1931 m_parGreenSawada[i] = {7.95, 13.5, 3.90, ta, tb};
1932 }
else if (
m_gas[i] ==
"Xe") {
1933 m_parGreenSawada[i] = {7.93, 11.5, 3.81, ta, tb};
1934 }
else if (
m_gas[i] ==
"H2" ||
m_gas[i] ==
"D2") {
1935 m_parGreenSawada[i] = {7.07, 7.7, 1.87, ta, tb};
1936 }
else if (
m_gas[i] ==
"N2") {
1937 m_parGreenSawada[i] = {13.8, 15.6, 4.71, ta, tb};
1938 }
else if (
m_gas[i] ==
"O2") {
1939 m_parGreenSawada[i] = {18.5, 12.1, 1.86, ta, tb};
1940 }
else if (
m_gas[i] ==
"CH4") {
1941 m_parGreenSawada[i] = {7.06, 12.5, 3.45, ta, tb};
1942 }
else if (
m_gas[i] ==
"H2O") {
1943 m_parGreenSawada[i] = {12.8, 12.6, 1.28, ta, tb};
1944 }
else if (
m_gas[i] ==
"CO") {
1945 m_parGreenSawada[i] = {13.3, 14.0, 2.03, ta, tb};
1946 }
else if (
m_gas[i] ==
"C2H2") {
1947 m_parGreenSawada[i] = {9.28, 5.8, 1.37, ta, tb};
1948 }
else if (
m_gas[i] ==
"NO") {
1949 m_parGreenSawada[i] = {10.4, 9.5, -4.30, ta, tb};
1950 }
else if (
m_gas[i] ==
"CO2") {
1951 m_parGreenSawada[i] = {12.3, 13.8, -2.46, ta, tb};
1953 m_parGreenSawada[i][3] = 0.;
1954 m_hasGreenSawada[i] =
false;
1955 if (m_useGreenSawada) {
1956 std::cout <<
m_className <<
"::SetupGreenSawada:\n"
1957 <<
" Fit parameters for " <<
m_gas[i]
1958 <<
" not available.\n"
1959 <<
" Opal-Beaty formula is used instead.\n";
1965void MediumMagboltz::ComputeDeexcitationTable(
const bool verbose) {
1966 m_iDeexcitation.fill(-1);
1967 m_deexcitations.clear();
1972 std::map<std::string, int> lvl;
1973 for (
unsigned int i = 0; i < m_nTerms; ++i) {
1975 if (m_csType[i] % nCsTypes != ElectronCollisionTypeExcitation)
continue;
1977 const int ngas = int(m_csType[i] / nCsTypes);
1979 if (
m_gas[ngas] ==
"Ar") {
1981 if (iAr < 0) iAr = ngas;
1984 for (
int j = 0; j < 7; ++j) level[j] = m_description[i][5 + j];
1986 level =
"Ar_" + level;
1991 lvl[level] = m_deexcitations.size();
1992 m_iDeexcitation[i] = lvl[level];
1999 dxc.energy = m_energyLoss[i] * m_rgas[ngas];
2001 dxc.osc = dxc.cf = 0.;
2002 dxc.sDoppler = dxc.gPressure = dxc.width = 0.;
2004 if (level ==
"Ar_HIGH") {
2008 dxc.type.assign(5, DxcTypeCollNonIon);
2009 dxc.p = {100., 100., 100., 100., 100.};
2010 dxc.final = {lvl[
"Ar_6D5"], lvl[
"Ar_5S1!"], lvl[
"Ar_4S2"], lvl[
"Ar_5S4"],
2013 m_deexcitations.push_back(std::move(dxc));
2016 if (m_deexcitations.empty())
return;
2018 std::string path =
"";
2019 auto installdir = std::getenv(
"GARFIELD_INSTALL");
2021 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n"
2022 <<
" Environment variable GARFIELD_INSTALL not set.\n";
2024 path = std::string(installdir) +
"/share/Garfield/Data/Deexcitation/";
2027 std::string filename = path +
"OscillatorStrengths_Ar.txt";
2028 std::ifstream infile(filename);
2030 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n"
2031 <<
" Could not open " << filename <<
".\n";
2034 for (std::string line; std::getline(infile, line);) {
2036 if (line.empty() || IsComment(line))
continue;
2038 if (words.size() < 2)
continue;
2039 std::string level =
"Ar_" + words[0];
2040 if (lvl.count(level) == 0) {
2041 std::cout <<
" Unexpected level " << level <<
"\n";
2044 m_deexcitations[lvl[level]].osc = std::stod(words[1]);
2048 filename = path +
"TransitionRates_Ar.txt";
2049 infile.open(filename);
2050 if (!infile.is_open()) {
2051 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n"
2052 <<
" Could not open " << filename <<
".\n";
2055 for (std::string line; std::getline(infile, line);) {
2057 if (line.empty() || IsComment(line))
continue;
2059 if (words.size() < 3)
continue;
2060 std::string level0 =
"Ar_" + words[0];
2061 if (lvl.count(level0) == 0) {
2062 std::cout <<
" Unexpected level " << level0 <<
"\n";
2065 auto& dxc = m_deexcitations[lvl[level0]];
2066 if (words[1] ==
"Ground") {
2067 dxc.final.push_back(-1);
2069 std::string level1 =
"Ar_" + words[1];
2070 if (lvl.count(level1) == 0) {
2071 std::cout <<
" Unexpected level " << level1 <<
"\n";
2074 dxc.final.push_back(lvl[level1]);
2076 dxc.p.push_back(std::stod(words[2]));
2077 dxc.type.push_back(DxcTypeRad);
2082 std::cout <<
m_className <<
"::ComputeDeexcitationTable:\n";
2083 std::cout <<
" Found " << m_deexcitations.size() <<
" levels "
2084 <<
"with available radiative de-excitation data.\n";
2091 dimer.label =
"Ar_Dimer";
2094 dimer.energy = 14.71;
2095 dimer.osc = dimer.cf = 0.;
2096 dimer.sDoppler = dimer.gPressure = dimer.width = 0.;
2097 lvl[
"Ar_Dimer"] = m_deexcitations.size();
2098 m_deexcitations.push_back(std::move(dimer));
2100 Deexcitation excimer;
2101 excimer.label =
"Ar_Excimer";
2104 excimer.energy = 14.71;
2105 excimer.osc = excimer.cf = 0.;
2106 excimer.sDoppler = excimer.gPressure = excimer.width = 0.;
2107 lvl[
"Ar_Excimer"] = m_deexcitations.size();
2108 m_deexcitations.push_back(std::move(excimer));
2111 filename = path +
"RateConstants_Ar_Ar.txt";
2112 infile.open(filename);
2113 if (!infile.is_open()) {
2114 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n"
2115 <<
" Could not open " << filename <<
".\n";
2118 for (std::string line; std::getline(infile, line);) {
2120 if (line.empty() || IsComment(line))
continue;
2122 if (words.size() < 3)
continue;
2123 std::string level0 =
"Ar_" + words[0];
2124 if (lvl.count(level0) == 0) {
2125 std::cout <<
" Unexpected level " << level0 <<
"\n";
2128 auto& dxc = m_deexcitations[lvl[level0]];
2129 std::string level1 =
"Ar_" + words[1];
2130 if (lvl.count(level1) == 0) {
2131 std::cout <<
" Unexpected level " << level1 <<
"\n";
2134 const double k = std::stod(words[2]);
2137 if (level1 ==
"Ar_Excimer") {
2138 dxc.p.push_back(k * nAr * nAr);
2140 dxc.p.push_back(k * nAr);
2142 dxc.final.push_back(lvl[level1]);
2143 dxc.type.push_back(DxcTypeCollNonIon);
2148 std::vector<std::string> levels3d5s = {
2149 "3D6",
"3D5",
"3D3",
"3D4!",
"3D4",
"3D1!!",
"3D1!",
"3D2",
2150 "3S1!!!!",
"3S1!!",
"3S1!!!",
"3S1!",
"2S5",
"2S4",
"2S3",
"2S2"
2152 std::vector<int> levels4p;
2153 for (
unsigned int j = 1; j <= 10; ++j) {
2154 std::string level =
"Ar_2P" + std::to_string(j);
2155 if (lvl.count(level) == 0) {
2156 std::cout <<
" Unexpected level " << level <<
".\n";
2158 levels4p.push_back(lvl[level]);
2161 for (
const std::string& level0 : levels3d5s) {
2162 if (lvl.count(
"Ar_" + level0) == 0) {
2163 std::cout <<
" Unexpected level " << level0 <<
".\n";
2166 auto& dxc = m_deexcitations[lvl[
"Ar_" + level0]];
2168 constexpr double k4p = 1.e-20;
2169 const double p4p = 0.1 * k4p * nAr;
2170 for (
const auto level1 : levels4p) {
2171 dxc.p.push_back(p4p);
2172 dxc.final.push_back(level1);
2173 dxc.type.push_back(DxcTypeCollNonIon);
2176 std::vector<std::string> levels = {
2177 "4D5",
"3S4",
"4D2",
"4S1!",
"3S2",
"5D5",
"4S4",
"5D2",
2178 "6D5",
"5S1!",
"4S2",
"5S4",
"6D2"};
2179 for (
const std::string& level0 : levels) {
2180 if (lvl.count(
"Ar_" + level0) == 0) {
2181 std::cout <<
" Unexpected level " << level0 <<
".\n";
2184 auto& dxc = m_deexcitations[lvl[
"Ar_" + level0]];
2186 constexpr double k4p = 1.e-20;
2187 const double p4p = 0.1 * k4p * nAr;
2188 for (
const auto level1 : levels4p) {
2189 dxc.p.push_back(p4p);
2190 dxc.final.push_back(level1);
2191 dxc.type.push_back(DxcTypeCollNonIon);
2197 constexpr double kHM = 2.e-18;
2198 dxc.p.push_back(kHM * nAr);
2199 dxc.final.push_back(lvl[
"Ar_Dimer"]);
2200 dxc.type.push_back(DxcTypeCollIon);
2206 std::string gas =
m_gas[i];
2209 if (
m_gas[i] ==
"CO2") {
2211 }
else if (
m_gas[i] ==
"CH4") {
2213 }
else if (
m_gas[i] ==
"C2H6") {
2215 }
else if (
m_gas[i] ==
"iC4H10") {
2218 }
else if (
m_gas[i] ==
"C2H2") {
2220 }
else if (
m_gas[i] ==
"CF4") {
2229 filename = path +
"RateConstants_Ar_" +
m_gas[i] +
".txt";
2230 infile.open(filename);
2231 if (!infile.is_open()) {
2232 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n"
2233 <<
" Could not open " << filename <<
".\n";
2236 for (std::string line; std::getline(infile, line);) {
2238 if (line.empty() || IsComment(line))
continue;
2240 if (words.size() < 2)
continue;
2241 std::string level0 =
"Ar_" + words[0];
2242 if (lvl.count(level0) == 0) {
2243 std::cout <<
" Unexpected level " << level0 <<
"\n";
2246 auto& dxc = m_deexcitations[lvl[level0]];
2247 if (dxc.energy < m_ionPot[i]) {
2248 AddPenningDeexcitation(dxc, std::stod(words[1]) * nQ, 0.);
2251 double pIon =
pow(eta, 0.4);
2252 if (words.size() > 2 && !IsComment(words[2])) {
2253 pIon = std::stod(words[2]);
2255 AddPenningDeexcitation(dxc, std::stod(words[1]) * nQ, pIon);
2259 for (
auto& dxc : m_deexcitations) {
2260 std::string level = dxc.label;
2261 if (level.find(
"Ar_1S") == 0 || level.find(
"Ar_2P") == 0) {
2265 const double pIon =
pow(eta, 0.4);
2270 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, i);
2271 if (dxc.energy < m_ionPot[i]) {
2272 AddPenningDeexcitation(dxc, kQ * nQ, 0.);
2274 AddPenningDeexcitation(dxc, kQ * nQ, pIon);
2276 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2277 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2278 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2279 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2281 constexpr double rAr3d = 436.e-10;
2282 const double kQ = RateConstantHardSphere(rAr3d, rQ, iAr, i);
2283 if (dxc.energy < m_ionPot[i]) {
2284 AddPenningDeexcitation(dxc, kQ * nQ, 0.);
2286 AddPenningDeexcitation(dxc, kQ * nQ, pIon);
2288 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2290 constexpr double rAr5s = 635.e-10;
2291 const double kQ = RateConstantHardSphere(rAr5s, rQ, iAr, i);
2292 if (dxc.energy < m_ionPot[i]) {
2293 AddPenningDeexcitation(dxc, kQ * nQ, 0.);
2295 AddPenningDeexcitation(dxc, kQ * nQ, pIon);
2302 std::cout <<
m_className <<
"::ComputeDeexcitationTable:\n"
2303 <<
" Level Energy [eV] Lifetimes [ns]\n"
2304 <<
" Total Radiative "
2307 <<
" Ionisation Transfer Loss\n";
2310 for (
auto& dxc : m_deexcitations) {
2314 double fCollIon = 0., fCollTransfer = 0., fCollLoss = 0.;
2315 const unsigned int nChannels = dxc.type.size();
2316 for (
unsigned int j = 0; j < nChannels; ++j) {
2317 dxc.rate += dxc.p[j];
2318 if (dxc.type[j] == DxcTypeRad) {
2320 }
else if (dxc.type[j] == DxcTypeCollIon) {
2321 fCollIon += dxc.p[j];
2322 }
else if (dxc.type[j] == DxcTypeCollNonIon) {
2323 if (dxc.final[j] < 0) {
2324 fCollLoss += dxc.p[j];
2326 fCollTransfer += dxc.p[j];
2329 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n "
2330 <<
"Unknown type of deexcitation channel (level " << dxc.label
2331 <<
"). Program bug!\n";
2334 if (dxc.rate <= 0.)
continue;
2337 std::cout << std::setw(12) << dxc.label <<
" " << std::fixed
2338 << std::setprecision(3) << std::setw(7) << dxc.energy <<
" "
2339 << std::setw(10) << 1. / dxc.rate <<
" ";
2341 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
2342 << 1. / fRad <<
" ";
2344 std::cout <<
"---------- ";
2346 if (fCollIon > 0.) {
2347 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
2348 << 1. / fCollIon <<
" ";
2350 std::cout <<
"---------- ";
2352 if (fCollTransfer > 0.) {
2353 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
2354 << 1. / fCollTransfer <<
" ";
2356 std::cout <<
"---------- ";
2358 if (fCollLoss > 0.) {
2359 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
2360 << 1. / fCollLoss <<
"\n";
2362 std::cout <<
"---------- \n";
2366 for (
unsigned int j = 0; j < nChannels; ++j) {
2367 dxc.p[j] /= dxc.rate;
2368 if (j > 0) dxc.p[j] += dxc.p[j - 1];
2373double MediumMagboltz::RateConstantWK(
const double energy,
const double osc,
2374 const double pacs,
const int igas1,
2375 const int igas2)
const {
2377 const double m1 = ElectronMassGramme / (m_rgas[igas1] - 1.);
2378 const double m2 = ElectronMassGramme / (m_rgas[igas2] - 1.);
2380 double mR = (m1 * m2 / (m1 + m2)) / AtomicMassUnit;
2381 const double uA = (RydbergEnergy / energy) * osc;
2382 const double uQ = (2 * RydbergEnergy / energy) * pacs /
2383 (4 * Pi2 * FineStructureConstant * BohrRadius * BohrRadius);
2387double MediumMagboltz::RateConstantHardSphere(
const double r1,
const double r2,
2389 const int igas2)
const {
2391 const double r = r1 + r2;
2392 const double sigma = r * r * Pi;
2394 const double m1 = ElectronMass / (m_rgas[igas1] - 1.);
2395 const double m2 = ElectronMass / (m_rgas[igas2] - 1.);
2396 const double mR = m1 * m2 / (m1 + m2);
2404 if (!m_useDeexcitation) {
2405 std::cerr <<
m_className <<
"::ComputeDeexcitation: Not enabled.\n";
2410 if (!Update())
return;
2412 if (iLevel < 0 || iLevel >= (
int)m_nTerms) {
2413 std::cerr <<
m_className <<
"::ComputeDeexcitation: Index out of range.\n";
2417 iLevel = m_iDeexcitation[iLevel];
2418 if (iLevel < 0 || iLevel >= (
int)m_deexcitations.size()) {
2419 std::cerr <<
m_className <<
"::ComputeDeexcitation:\n"
2420 <<
" Level is not deexcitable.\n";
2424 ComputeDeexcitationInternal(iLevel, fLevel);
2425 if (fLevel >= 0 && fLevel < (
int)m_deexcitations.size()) {
2426 fLevel = m_deexcitations[fLevel].level;
2430void MediumMagboltz::ComputeDeexcitationInternal(
int iLevel,
int& fLevel) {
2431 m_dxcProducts.clear();
2435 while (iLevel >= 0 && iLevel < (
int)m_deexcitations.size()) {
2436 const auto& dxc = m_deexcitations[iLevel];
2437 const int nChannels = dxc.p.size();
2438 if (dxc.rate <= 0. || nChannels <= 0) {
2447 int type = DxcTypeRad;
2449 for (
int j = 0; j < nChannels; ++j) {
2450 if (r <= dxc.p[j]) {
2451 fLevel = dxc.final[j];
2456 if (type == DxcTypeRad) {
2461 photon.type = DxcProdTypePhoton;
2462 photon.energy = dxc.energy;
2465 photon.energy -= m_deexcitations[fLevel].energy;
2466 if (photon.energy < Small) photon.energy = Small;
2467 m_dxcProducts.push_back(std::move(photon));
2472 double delta =
RndmVoigt(0., dxc.sDoppler, dxc.gPressure);
2473 while (photon.energy + delta < Small ||
fabs(delta) >= dxc.width) {
2474 delta =
RndmVoigt(0., dxc.sDoppler, dxc.gPressure);
2476 photon.energy += delta;
2477 m_dxcProducts.push_back(std::move(photon));
2482 }
else if (type == DxcTypeCollIon) {
2487 electron.type = DxcProdTypeElectron;
2488 electron.energy = dxc.energy;
2491 electron.energy -= m_deexcitations[fLevel].energy;
2492 if (electron.energy < Small) electron.energy = Small;
2494 m_dxcProducts.push_back(std::move(electron));
2499 electron.energy -= m_minIonPot;
2500 if (electron.energy < Small) electron.energy = Small;
2502 m_dxcProducts.push_back(std::move(electron));
2507 }
else if (type == DxcTypeCollNonIon) {
2511 std::cerr <<
m_className <<
"::ComputeDeexcitationInternal:\n"
2512 <<
" Unknown deexcitation type (" << type <<
"). Bug!\n";
2520bool MediumMagboltz::ComputePhotonCollisionTable(
const bool verbose) {
2526 m_cfTotGamma.assign(nEnergyStepsGamma, 0.);
2527 m_cfGamma.assign(nEnergyStepsGamma, std::vector<double>());
2528 csTypeGamma.clear();
2532 const double prefactor = dens * SpeedOfLight *
m_fraction[i];
2534 std::string gasname =
m_gas[i];
2535 if (gasname ==
"iC4H10") {
2538 std::cout <<
m_className <<
"::ComputePhotonCollisionTable:\n"
2539 <<
" Photoabsorption cross-section for "
2540 <<
"iC4H10 not available.\n"
2541 <<
" Using n-butane cross-section instead.\n";
2545 csTypeGamma.push_back(i * nCsTypesGamma + PhotonCollisionTypeIonisation);
2546 csTypeGamma.push_back(i * nCsTypesGamma + PhotonCollisionTypeInelastic);
2547 m_nPhotonTerms += 2;
2548 for (
int j = 0; j < nEnergyStepsGamma; ++j) {
2549 const double en = (j + 0.5) * m_eStepGamma;
2553 m_cfTotGamma[j] += cs * prefactor;
2555 m_cfGamma[j].push_back(cs * prefactor * eta);
2557 m_cfGamma[j].push_back(cs * prefactor * (1. - eta));
2562 if (m_useCsOutput) {
2563 std::ofstream csfile;
2564 csfile.open(
"csgamma.txt", std::ios::out);
2565 for (
int j = 0; j < nEnergyStepsGamma; ++j) {
2566 csfile << (j + 0.5) * m_eStepGamma <<
" ";
2567 for (
unsigned int i = 0; i < m_nPhotonTerms; ++i)
2568 csfile << m_cfGamma[j][i] <<
" ";
2575 for (
int j = 0; j < nEnergyStepsGamma; ++j) {
2576 for (
unsigned int i = 0; i < m_nPhotonTerms; ++i) {
2577 if (i > 0) m_cfGamma[j][i] += m_cfGamma[j][i - 1];
2582 std::cout <<
m_className <<
"::ComputePhotonCollisionTable:\n";
2583 std::cout <<
" Energy [eV] Mean free path [um]\n";
2584 for (
int i = 0; i < 10; ++i) {
2585 const int j = (2 * i + 1) * nEnergyStepsGamma / 20;
2586 const double en = (2 * i + 1) * m_eFinalGamma / 20;
2587 const double imfp = m_cfTotGamma[j] / SpeedOfLight;
2589 printf(
" %10.2f %18.4f\n", en, 1.e4 / imfp);
2591 printf(
" %10.2f ------------\n", en);
2596 if (!m_useDeexcitation)
return true;
2599 constexpr double f2cs =
2600 FineStructureConstant * 2 * Pi2 * HbarC * HbarC / ElectronMass;
2602 int nResonanceLines = 0;
2603 for (
auto& dxc : m_deexcitations) {
2604 if (dxc.osc < Small)
continue;
2605 const double prefactor = dens * SpeedOfLight *
m_fraction[dxc.gas];
2606 dxc.cf = prefactor * f2cs * dxc.osc;
2608 const double mgas = ElectronMass / (m_rgas[dxc.gas] - 1.);
2610 dxc.sDoppler = wDoppler * dxc.energy;
2614 const double kResBroad = 1.92 * Pi *
sqrt(1. / 3.);
2615 dxc.gPressure = kResBroad * FineStructureConstant *
pow(HbarC, 3) *
2617 (ElectronMass * dxc.energy);
2620 constexpr double nWidths = 1000.;
2624 const double fwhmGauss = dxc.sDoppler *
sqrt(2. * log(2.));
2625 const double fwhmLorentz = dxc.gPressure;
2626 const double fwhmVoigt =
2627 0.5 * (1.0692 * fwhmLorentz +
sqrt(0.86639 * fwhmLorentz * fwhmLorentz +
2628 4 * fwhmGauss * fwhmGauss));
2629 dxc.width = nWidths * fwhmVoigt;
2633 if (nResonanceLines <= 0) {
2634 std::cerr <<
m_className <<
"::ComputePhotonCollisionTable:\n"
2635 <<
" No resonance lines found.\n";
2639 if (!(
m_debug || verbose))
return true;
2640 std::cout <<
m_className <<
"::ComputePhotonCollisionTable:\n "
2641 <<
"Discrete absorption lines:\n Energy [eV] "
2642 <<
"Line width (FWHM) [eV] Mean free path [um]\n "
2643 <<
" Doppler Pressure (peak)\n";
2644 for (
const auto& dxc : m_deexcitations) {
2645 if (dxc.osc < Small)
continue;
2646 const double wp = 2 * dxc.gPressure;
2647 const double wd = 2 *
sqrt(2 * log(2.)) * dxc.sDoppler;
2648 const double imfpP =
2649 (dxc.cf / SpeedOfLight) * TMath::Voigt(0., dxc.sDoppler, wp);
2651 printf(
" %6.3f +/- %6.1e %6.2e %6.3e %14.4f\n", dxc.energy,
2652 dxc.width, wd, wp, 1.e4 / imfpP);
2654 printf(
" %6.3f +/- %6.1e %6.2e %6.3e -------------\n", dxc.energy,
2662 const double emag,
const double bmag,
const double btheta,
const int ncoll,
2663 bool verbose,
double& vx,
double& vy,
double& vz,
double& dl,
double& dt,
2664 double& alpha,
double& eta,
double& lor,
double& vxerr,
double& vyerr,
2665 double& vzerr,
double& dlerr,
double& dterr,
double& alphaerr,
2666 double& etaerr,
double& lorerr,
double& alphatof,
2667 std::array<double, 6>& difftens) {
2671 alpha = eta = alphatof = 0.;
2673 vxerr = vyerr = vzerr = 0.;
2675 alphaerr = etaerr = 0.;
2681 if (m_autoEnergyLimit) {
2704 <<
" does not have a gas number in Magboltz.\n";
2723 const double vt = sqrt(vx * vx + vy * vy);
2724 const double v2 = (vx * vx + vy * vy + vz * vz);
2725 lor = atan2(vt, vz);
2726 if (vt > 0. && v2 > 0. && fabs(lor) > 0.) {
2727 const double dvx = vx * vxerr;
2728 const double dvy = vy * vyerr;
2729 const double dvz = vz * vzerr;
2730 const double a = vz / vt;
2731 lorerr = sqrt(a * a * (vx * vx * dvx * dvx + vy * vy * dvy * dvy) +
2732 vt * vt * dvz * dvz) /
2766 alphatof = fc1 - sqrt(fc1 * fc1 - fc2);
2769 if (!(
m_debug || verbose))
return;
2770 std::cout <<
m_className <<
"::RunMagboltz: Results:\n";
2771 printf(
" Drift velocity along E: %12.8f cm/ns +/- %5.2f%%\n", vz, vzerr);
2772 printf(
" Drift velocity along Bt: %12.8f cm/ns +/- %5.2f%%\n", vx, vxerr);
2773 printf(
" Drift velocity along ExB: %12.8f cm/ns +/- %5.2f%%\n", vy, vyerr);
2774 printf(
" Lorentz angle: %12.3f degree\n", lor * RadToDegree);
2775 printf(
" Longitudinal diffusion: %12.8f cm1/2 +/- %5.2f%%\n", dl, dlerr);
2776 printf(
" Transverse diffusion: %12.8f cm1/2 +/- %5.2f%%\n", dt, dterr);
2777 printf(
" Townsend coefficient: %12.4f cm-1 +/- %5.2f%%\n", alpha,
2779 printf(
" Attachment coefficient: %12.4f cm-1 +/- %5.2f%%\n", eta,
2781 if (alphatof > 0.) {
2782 printf(
" TOF effective Townsend: %12.4f cm-1 (alpha - eta)\n",
2793 const unsigned int nEfields =
m_eFields.size();
2794 const unsigned int nBfields =
m_bFields.size();
2795 const unsigned int nAngles =
m_bAngles.size();
2801 Init(nEfields, nBfields, nAngles,
m_eLor, 0.);
2802 Init(nEfields, nBfields, nAngles,
m_eAlp, -30.);
2804 Init(nEfields, nBfields, nAngles,
m_eAtt, -30.);
2805 Init(nEfields, nBfields, nAngles, 6,
m_eDifM, 0.);
2810 GetExcitationIonisationLevels();
2811 std::cout <<
m_className <<
"::GenerateGasTable: Found "
2815 std::cout <<
" " << exc.label <<
", energy = " << exc.energy <<
" eV.\n";
2818 std::cout <<
" " << ion.label <<
", energy = " << ion.energy <<
" eV.\n";
2826 double vx = 0., vy = 0., vz = 0.;
2827 double difl = 0., dift = 0.;
2828 double alpha = 0., eta = 0.;
2830 double vxerr = 0., vyerr = 0., vzerr = 0.;
2831 double diflerr = 0., difterr = 0.;
2832 double alphaerr = 0., etaerr = 0.;
2833 double alphatof = 0.;
2835 std::array<double, 6> difftens;
2838 for (
unsigned int i = 0; i < nEfields; ++i) {
2840 for (
unsigned int j = 0; j < nAngles; ++j) {
2842 for (
unsigned int k = 0; k < nBfields; ++k) {
2844 std::cout <<
m_className <<
"::GenerateGasTable: E = " << e
2845 <<
" V/cm, B = " << b <<
" T, angle: " << a <<
" rad\n";
2846 RunMagboltz(e, b, a, numColl, verbose, vx, vy, vz, difl, dift, alpha,
2847 eta, lor, vxerr, vyerr, vzerr, diflerr, difterr, alphaerr,
2848 etaerr, lorerr, alphatof, difftens);
2855 m_eAlp[j][k][i] = alpha > 0. ? log(alpha) : -30.;
2857 m_eAtt[j][k][i] = eta > 0. ? log(eta) : -30.;
2858 for (
unsigned int l = 0; l < 6; ++l) {
2859 m_eDifM[l][j][k][i] = difftens[l];
2862 unsigned int nNonZero = 0;
2863 if (m_useGasMotion) {
2865 double ftot = 0., fel = 0., fion = 0., fatt = 0., fin = 0.;
2866 std::int64_t ntotal = 0;
2868 if (ntotal == 0)
continue;
2870 const double scale = 1.e3 * ftot / ntotal;
2873 for (std::int64_t il = 0; il < nL; ++il) {
2877 if (cstype != 1 && cstype != 3)
continue;
2880 descr =
m_gas[ig] + descr;
2883 for (
unsigned int ie = 0; ie < nExc; ++ie) {
2887 if (ncoll > 0) ++nNonZero;
2890 }
else if (cstype == 1) {
2892 for (
unsigned int ii = 0; ii < nIon; ++ii) {
2896 if (ncoll > 0) ++nNonZero;
2904 double ftot = 0., fel = 0., fion = 0., fatt = 0., fin = 0.;
2905 std::int64_t ntotal = 0;
2907 if (ntotal == 0)
continue;
2909 const double scale = 1.e3 * ftot / ntotal;
2914 if (cstype != 1 && cstype != 3)
continue;
2917 descr =
m_gas[igas] + descr;
2920 for (
unsigned int ie = 0; ie < nExc; ++ie) {
2926 }
else if (cstype == 1) {
2928 for (
unsigned int ii = 0; ii < nIon; ++ii) {
2938 std::cout <<
" Excitation and ionisation rates:\n";
2939 std::cout <<
" Level "
2940 <<
" Rate [ns-1]\n";
2942 for (
unsigned int ie = 0; ie < nExc; ++ie) {
2944 std::cout << std::setw(60) <<
m_excLevels[ie].label;
2945 std::printf(
" %15.8f\n",
m_excRates[ie][j][k][i]);
2948 for (
unsigned int ii = 0; ii < nIon; ++ii) {
2950 std::cout << std::setw(60) <<
m_ionLevels[ii].label;
2951 std::printf(
" %15.8f\n",
m_ionRates[ii][j][k][i]);
2962void MediumMagboltz::GetExcitationIonisationLevels() {
2986 const double emax = 400.;
2991 std::int64_t nIn = 0, nIon = 0, nAtt = 1, nNull = 0;
3001 static std::int64_t kEl[6];
3019 std::cerr <<
m_className <<
"::GetExcitationIonisationLevels:\n\n"
3020 <<
" Gas " <<
m_gas[i] <<
" not available in Magboltz.\n";
3024 &ng, q[0], qIn[0], &nIn, e, eIn, name, &virial, eoby, pEqEl[0],
3025 pEqIn[0], penFra[0], kEl, kIn, qIon[0], pEqIon[0], eIon, &nIon,
3026 qAtt[0], &nAtt, qNull[0], &nNull, scln, nc0, ec0, wklm, efl,
3027 ng1, eg1, ng2, eg2, scrpt, scrptn,
3029 const double r = 1. + 0.5 * e[1];
3031 for (
int j = 0; j < nIon; ++j) {
3032 const std::string descr = GetDescription(2 + j, scrpt);
3034 ion.label =
m_gas[i] + descr;
3035 ion.energy = eIon[j] / r;
3039 for (
int j = 0; j < nIn; ++j) {
3040 const std::string descr = GetDescription(4 + nIon + nAtt + j, scrpt);
3041 if ((descr[1] ==
'E' && descr[2] ==
'X') ||
3042 (descr[0] ==
'E' && descr[1] ==
'X')) {
3046 exc.energy = eIn[j] / r;
double GetNumberDensity() const override
Get the number density [cm-3].
virtual bool EnablePenningTransfer()
static constexpr unsigned int m_nMaxGases
std::vector< std::vector< std::vector< std::vector< double > > > > m_excRates
double m_lambdaPenningGlobal
std::vector< IonLevel > m_ionLevels
std::array< double, m_nMaxGases > m_rPenningGas
std::vector< std::vector< std::vector< std::vector< double > > > > m_ionRates
std::vector< ExcLevel > m_excLevels
double m_temperatureTable
virtual void PrintGas()
Print information about the present gas mixture and available data.
static std::string GetGasName(const int gasnumber, const int version)
std::array< double, m_nMaxGases > m_lambdaPenningGas
virtual void DisablePenningTransfer()
Switch the simulation of Penning transfers off globally.
bool SetComposition(const std::string &gas1, const double f1=1., const std::string &gas2="", const double f2=0., const std::string &gas3="", const double f3=0., const std::string &gas4="", const double f4=0., const std::string &gas5="", const double f5=0., const std::string &gas6="", const double f6=0.)
Set the gas mixture.
std::vector< std::vector< std::vector< double > > > m_eAlp0
std::array< std::string, m_nMaxGases > m_gas
std::array< double, m_nMaxGases > m_fraction
void SetSplittingFunctionGreenSawada()
Sample the secondary electron energy according to the Green-Sawada model.
bool EnablePenningTransfer() override
unsigned int GetNumberOfLevels()
Get the number of cross-section terms.
void SetExcitationScaling(const double r, std::string gasname)
Multiply all excitation cross-sections by a uniform scaling factor.
bool GetLevel(const unsigned int i, int &ngas, int &type, std::string &descr, double &e)
Get detailed information about a given cross-section term i.
double GetElectronNullCollisionRate(const int band) override
Get the overall null-collision rate [ns-1].
void ResetCollisionCounters()
Reset the collision counters.
void EnableRadiationTrapping()
Switch on discrete photoabsorption levels.
unsigned int GetNumberOfElectronCollisions() const
Get the total number of electron collisions.
double GetPhotonCollisionRate(const double e) override
bool ElectronCollision(const double e, int &type, int &level, double &e1, double &dx, double &dy, double &dz, std::vector< std::pair< Particle, double > > &secondaries, int &ndxc, int &band) override
Sample the collision type.
void RunMagboltz(const double e, const double b, const double btheta, const int ncoll, bool verbose, double &vx, double &vy, double &vz, double &dl, double &dt, double &alpha, double &eta, double &lor, double &vxerr, double &vyerr, double &vzerr, double &dlerr, double &dterr, double &alphaerr, double &etaerr, double &lorerr, double &alphatof, std::array< double, 6 > &difftens)
void ComputeDeexcitation(int iLevel, int &fLevel)
void SetSplittingFunctionOpalBeaty()
Sample the secondary electron energy according to the Opal-Beaty model.
static int GetGasNumberMagboltz(const std::string &input)
void GenerateGasTable(const int numCollisions=10, const bool verbose=true)
bool GetDeexcitationProduct(const unsigned int i, double &t, double &s, int &type, double &energy) const override
void SetSplittingFunctionFlat()
Sample the secondary electron energy from a flat distribution.
MediumMagboltz()
Default constructor.
unsigned int GetNumberOfPhotonCollisions() const
Get the total number of photon collisions.
bool SetMaxPhotonEnergy(const double e)
void DisablePenningTransfer() override
Switch the simulation of Penning transfers off globally.
bool GetPhotonCollision(const double e, int &type, int &level, double &e1, double &ctheta, int &nsec, double &esec) override
bool GetPenningTransfer(const unsigned int i, double &r, double &lambda)
Get the Penning transfer probability and distance of a specific level.
void PlotElectronCrossSections()
bool Initialise(const bool verbose=false)
double GetElectronCollisionRate(const double e, const int band) override
Get the (real) collision rate [ns-1] at a given electron energy e [eV].
bool SetMaxElectronEnergy(const double e)
void PrintGas() override
Print information about the present gas mixture and available data.
void EnableDeexcitation()
Switch on (microscopic) de-excitation handling.
std::vector< double > m_bFields
std::vector< std::vector< std::vector< double > > > m_eAlp
size_t SetThreshold(const std::vector< std::vector< std::vector< double > > > &tab) const
std::vector< std::vector< std::vector< double > > > m_eVelE
std::vector< std::vector< std::vector< double > > > m_eVelX
std::vector< std::vector< std::vector< double > > > m_eDifL
void Init(const size_t nE, const size_t nB, const size_t nA, std::vector< std::vector< std::vector< double > > > &tab, const double val)
std::vector< double > m_eFields
std::vector< std::vector< std::vector< double > > > m_eAtt
std::vector< double > m_bAngles
std::vector< std::vector< std::vector< double > > > m_eLor
std::vector< std::vector< std::vector< double > > > m_eDifT
std::vector< std::vector< std::vector< double > > > m_eVelB
unsigned int m_nComponents
std::vector< std::vector< std::vector< std::vector< double > > > > m_eDifM
static bool IsAvailable(const std::string &material)
Check whether optical data have been implemented for a given gas.
static double PhotoionisationYield(const std::string &material, const double energy)
Photo-ionisation yield at a given energy.
static bool PhotoabsorptionCrossSection(const std::string &material, const double energy, double &cs, double &eta)
Photo-absorption cross-section and ionisation yield at a given energy.
static std::string FindUnusedCanvasName(const std::string &s)
Find an unused canvas name.
struct Garfield::Magboltz::@060222016335007357276230325267072324307245075335 velerr_
struct Garfield::Magboltz::@071266015331337350342074140245340115315173206173 script_
constexpr unsigned int nMaxInelasticTerms
constexpr unsigned int nEnergySteps
struct Garfield::Magboltz::@272077330053140267135131315064054111215365003061 inpt_
constexpr unsigned int nMaxAttachmentTerms
struct Garfield::Magboltz::@236122006074061251067327101212023331117065272002 larget_
constexpr unsigned int nCharDescr
struct Garfield::Magboltz::@016351066267021123171150005363312076230027317210 outpt_
double cf[nMaxLevels][nEnergySteps]
struct Garfield::Magboltz::@367143224022215036160202274360277173043324053167 diferl_
constexpr unsigned int nMaxIonisationTerms
constexpr unsigned int nCharName
struct Garfield::Magboltz::@157012065177270115006254216365004043341121046005 vel_
struct Garfield::Magboltz::@245266170301200003106052333313347204035061117342 ratio_
struct Garfield::Magboltz::@010063252323105360345105253327133025274175326133 bfld_
struct Garfield::Magboltz::@367172176335371151066116323064254124322366107157 diflab_
struct Garfield::Magboltz::@057270266311332313123374024066154010246130331023 cnsts_
struct Garfield::Magboltz::@222150133151105054154302130362347315126001152334 dens_
struct Garfield::Magboltz::@304340026334031267143114007126147372164074306062 outptt_
constexpr unsigned int nMaxLevels
void colf_(double *freq, double *freel, double *freion, double *freatt, double *frein, std::int64_t *ntotal)
constexpr unsigned int nMaxNullTerms
struct Garfield::Magboltz::@043341123353356340117075350116005072335377127162 gasn_
struct Garfield::Magboltz::@152023163236327126074317062211255304316001105103 tofout_
struct Garfield::Magboltz::@261142307054266253060114162042322335114302253152 ctowns_
struct Garfield::Magboltz::@343242004052315221243247367236006336135121373020 setp_
struct Garfield::Magboltz::@363161251140110014365135052375245247262302340235 large_
struct Garfield::Magboltz::@243337270271362237030277156013024035163331176157 thrm_
void gasmix_(std::int64_t *ngs, double *q, double *qin, std::int64_t *nin, double *e, double *ei, char *name, double *virl, double *eb, double *peqel, double *peqin, double *penfra, std::int64_t *kel, std::int64_t *kin, double *qion, double *peqion, double *eion, std::int64_t *nion, double *qatt, std::int64_t *natt, double *qnull, std::int64_t *nnull, double *scln, std::int64_t *nc0, double *ec0, double *wk, double *efl, std::int64_t *ng1, double *eg1, std::int64_t *ng2, double *eg2, char scrpt[nMaxLevelsPerComponent][nCharDescr], char scrptn[nMaxNullTerms][nCharDescr], short namelen, short scrpt_len, short scrptn_len)
void colft_(double *freq, double *freel, double *freion, double *freatt, double *frein, std::int64_t *ntotal)
struct Garfield::Magboltz::@301116172155326317070225220046057374317172177377 ctwner_
constexpr unsigned int nMaxLevelsPerComponent
struct Garfield::Magboltz::@276025017301256117263211055021110262335047200203 mix2_
struct Garfield::Magboltz::@024075325050032167066064232170013161226346016145 scrip_
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
std::vector< std::string > tokenize(const std::string &line)
void ltrim(std::string &line)
double RndmVoigt(const double mu, const double sigma, const double gamma)
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
void rtrim(std::string &line)
DoubleAc cos(const DoubleAc &f)
DoubleAc pow(const DoubleAc &f, double p)
DoubleAc exp(const DoubleAc &f)
DoubleAc fabs(const DoubleAc &f)
DoubleAc sin(const DoubleAc &f)
DoubleAc asin(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)