21void PrintErrorMixer(
const std::string& fcn) {
22 std::cerr << fcn <<
": Error calculating the collision rates table.\n";
25std::string GetDescription(
const unsigned int index,
27 return std::string(scrpt[index],
34const int MediumMagboltz::DxcTypeRad = 0;
35const int MediumMagboltz::DxcTypeCollIon = 1;
36const int MediumMagboltz::DxcTypeCollNonIon = -1;
41 m_eStep(m_eMax / Magboltz::nEnergySteps),
43 m_eHighLog(log(m_eHigh)),
46 m_eStepGamma(m_eFinalGamma / nEnergyStepsGamma) {
76 m_cfTotLog.assign(nEnergyStepsLog, 0.);
79 m_cfLog.assign(nEnergyStepsLog,
93 std::cerr <<
m_className <<
"::SetMaxElectronEnergy: Invalid energy.\n";
109 std::cerr <<
m_className <<
"::SetMaxPhotonEnergy: Invalid energy.\n";
115 m_eStepGamma = m_eFinalGamma / nEnergyStepsGamma;
124 m_useOpalBeaty =
true;
125 m_useGreenSawada =
false;
129 m_useOpalBeaty =
false;
130 m_useGreenSawada =
true;
135 if (!m_hasGreenSawada[i]) {
137 std::cout <<
m_className <<
"::SetSplittingFunctionGreenSawada:\n";
140 std::cout <<
" Fit parameters for " <<
m_gas[i] <<
" not available.\n"
141 <<
" Using Opal-Beaty formula instead.\n";
147 m_useOpalBeaty =
false;
148 m_useGreenSawada =
false;
153 std::cout <<
m_className <<
"::EnableDeexcitation:\n"
154 <<
" Penning transfer will be switched off.\n";
162 m_useDeexcitation =
true;
164 m_dxcProducts.clear();
169 if (!m_useDeexcitation) {
170 std::cout <<
m_className <<
"::EnableRadiationTrapping:\n "
171 <<
"Radiation trapping is enabled but de-excitation is not.\n";
178 const double lambda) {
183 m_lambdaPenning.fill(0.);
188 PrintErrorMixer(
m_className +
"::EnablePenningTransfer");
193 unsigned int nLevelsFound = 0;
194 for (
unsigned int i = 0; i < m_nTerms; ++i) {
195 if (m_csType[i] % nCsTypes == ElectronCollisionTypeExcitation) {
202 if (nLevelsFound > 0) {
203 std::cout <<
m_className <<
"::EnablePenningTransfer:\n "
204 <<
"Updated Penning transfer parameters for " << nLevelsFound
205 <<
" excitation cross-sections.\n";
207 std::cerr <<
m_className <<
"::EnablePenningTransfer:\n Warning: "
208 <<
"mismatch between number of excitation cross-sections ("
209 << nLevelsFound <<
")\n and number of excitation rates in "
210 <<
"the gas table (" <<
m_excLevels.size() <<
").\n "
211 <<
"The gas table was probably calculated using a different "
212 <<
"version of Magboltz.\n";
215 std::cerr <<
m_className <<
"::EnablePenningTransfer:\n "
216 <<
"No excitation cross-sections in the present energy range.\n";
219 if (m_useDeexcitation) {
220 std::cout <<
m_className <<
"::EnablePenningTransfer:\n "
221 <<
"Deexcitation handling will be switched off.\n";
228 std::string gasname) {
234 if (gasname.empty())
return false;
239 if (
m_gas[i] == gasname) {
248 PrintErrorMixer(
m_className +
"::EnablePenningTransfer");
253 unsigned int nLevelsFound = 0;
254 for (
unsigned int i = 0; i < m_nTerms; ++i) {
255 if (
int(m_csType[i] / nCsTypes) != iGas)
continue;
256 if (m_csType[i] % nCsTypes == ElectronCollisionTypeExcitation) {
263 if (nLevelsFound > 0) {
264 std::cout <<
m_className <<
"::EnablePenningTransfer:\n"
265 <<
" Penning transfer parameters for " << nLevelsFound
266 <<
" excitation levels set to:\n"
270 std::cerr <<
m_className <<
"::EnablePenningTransfer:\n"
271 <<
" Specified gas (" << gasname
272 <<
") has no excitation levels in the present energy range.\n";
283 m_lambdaPenning.fill(0.);
293 if (gasname.empty())
return false;
298 if (
m_gas[i] == gasname) {
304 if (iGas < 0)
return false;
306 unsigned int nLevelsFound = 0;
307 for (
unsigned int i = 0; i < m_nTerms; ++i) {
308 if (
int(m_csType[i] / nCsTypes) == iGas) {
310 m_lambdaPenning[i] = 0.;
312 if (m_csType[i] % nCsTypes == ElectronCollisionTypeExcitation &&
313 m_rPenning[i] > Small) {
319 if (nLevelsFound == 0) {
321 std::cout <<
m_className <<
"::DisablePenningTransfer:\n"
322 <<
" Penning transfer switched off for all excitations.\n";
330 std::cerr <<
m_className <<
"::SetExcitationScaling: Incorrect value.\n";
336 if (gasname.empty()) {
337 std::cerr <<
m_className <<
"::SetExcitationScaling: Unknown gas name.\n";
344 if (
m_gas[i] == gasname) {
352 std::cerr <<
m_className <<
"::SetExcitationScaling:\n"
353 <<
" Specified gas (" << gasname
354 <<
") is not part of the present gas mixture.\n";
365 std::cerr <<
m_className <<
"::Initialise: Nothing changed.\n";
369 if (!Mixer(verbose)) {
384 std::cout <<
" Electron cross-sections:\n";
386 for (
unsigned int i = 0; i < m_nTerms; ++i) {
388 int type = m_csType[i] % nCsTypes;
389 if (igas !=
int(m_csType[i] / nCsTypes)) {
390 igas = int(m_csType[i] / nCsTypes);
391 std::cout <<
" " <<
m_gas[igas] <<
"\n";
395 double e = m_rgas[igas] * m_energyLoss[i];
396 std::cout <<
" Level " << i <<
": " << m_description[i] <<
"\n";
397 std::cout <<
" Type " << type;
398 if (type == ElectronCollisionTypeElastic) {
399 std::cout <<
" (elastic)\n";
400 }
else if (type == ElectronCollisionTypeIonisation) {
401 std::cout <<
" (ionisation). Ionisation threshold: " << e <<
" eV.\n";
402 }
else if (type == ElectronCollisionTypeAttachment) {
403 std::cout <<
" (attachment)\n";
404 }
else if (type == ElectronCollisionTypeInelastic) {
405 std::cout <<
" (inelastic). Energy loss: " << e <<
" eV.\n";
406 }
else if (type == ElectronCollisionTypeExcitation) {
407 std::cout <<
" (excitation). Excitation energy: " << e <<
" eV.\n";
408 }
else if (type == ElectronCollisionTypeSuperelastic) {
409 std::cout <<
" (super-elastic). Energy gain: " << -e <<
" eV.\n";
410 }
else if (type == ElectronCollisionTypeVirtual) {
411 std::cout <<
" (virtual)\n";
413 std::cout <<
" (unknown)\n";
415 if (type == ElectronCollisionTypeExcitation &&
m_usePenning &&
417 std::cout <<
" Penning transfer coefficient: "
418 << m_rPenning[i] <<
"\n";
419 }
else if (type == ElectronCollisionTypeExcitation && m_useDeexcitation) {
420 const int idxc = m_iDeexcitation[i];
421 if (idxc < 0 || idxc >= (
int)m_deexcitations.size()) {
422 std::cout <<
" Deexcitation cascade not implemented.\n";
425 const auto& dxc = m_deexcitations[idxc];
427 std::cout <<
" Oscillator strength: " << dxc.osc <<
"\n";
429 std::cout <<
" Decay channels:\n";
430 const int nChannels = dxc.type.size();
431 for (
int j = 0; j < nChannels; ++j) {
432 if (dxc.type[j] == DxcTypeRad) {
433 std::cout <<
" Radiative decay to ";
434 if (dxc.final[j] < 0) {
435 std::cout <<
"ground state: ";
437 std::cout << m_deexcitations[dxc.final[j]].label <<
": ";
439 }
else if (dxc.type[j] == DxcTypeCollIon) {
440 if (dxc.final[j] < 0) {
441 std::cout <<
" Penning ionisation: ";
443 std::cout <<
" Associative ionisation: ";
445 }
else if (dxc.type[j] == DxcTypeCollNonIon) {
446 if (dxc.final[j] >= 0) {
447 std::cout <<
" Collision-induced transition to "
448 << m_deexcitations[dxc.final[j]].label <<
": ";
450 std::cout <<
" Loss: ";
453 const double br = j == 0 ? dxc.p[j] : dxc.p[j] - dxc.p[j - 1];
454 std::cout << std::setprecision(5) << br * 100. <<
"%\n";
464 PrintErrorMixer(
m_className +
"::GetElectronNullCollisionRate");
471 std::cerr <<
m_className <<
"::GetElectronNullCollisionRate: Band > 0.\n";
481 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Invalid energy.\n";
484 if (e > m_eMax && m_useAutoAdjust) {
485 std::cerr <<
m_className <<
"::GetElectronCollisionRate:\n Rate at " << e
486 <<
" eV is not included in the current table.\n "
487 <<
"Increasing energy range to " << 1.05 * e <<
" eV.\n";
494 PrintErrorMixer(
m_className +
"::GetElectronCollisionRate");
501 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Band > 0.\n";
508 const int iE = std::min(std::max(
int(e / m_eStep), 0), iemax);
513 const double eLog = log(e);
514 int iE = int((eLog - m_eHighLog) / m_lnStep);
516 const double fmax = m_cfTotLog[iE];
517 const double fmin = iE == 0 ? log(m_cfTot.back()) : m_cfTotLog[iE - 1];
518 const double emin = m_eHighLog + iE * m_lnStep;
519 const double f = fmin + (eLog - emin) * (fmax - fmin) / m_lnStep;
524 const unsigned int level,
528 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Invalid energy.\n";
533 if (level >= m_nTerms) {
534 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Invalid level.\n";
544 const int iE = std::min(std::max(
int(e / m_eStep), 0), iemax);
548 rate *= m_cf[iE][level] - m_cf[iE][level - 1];
552 const int iE = int((log(e) - m_eHighLog) / m_lnStep);
554 rate *= m_cfLog[iE][0];
556 rate *= m_cfLog[iE][level] - m_cfLog[iE][level - 1];
563 const double e,
int& type,
int& level,
double& e1,
double& dx,
double& dy,
564 double& dz, std::vector<std::pair<int, double> >& secondaries,
int& ndxc,
568 std::cerr <<
m_className <<
"::GetElectronCollision: Invalid energy.\n";
572 if (e > m_eMax && m_useAutoAdjust) {
573 std::cerr <<
m_className <<
"::GetElectronCollision:\n Provided energy ("
574 << e <<
" eV) exceeds current energy range.\n"
575 <<
" Increasing energy range to " << 1.05 * e <<
" eV.\n";
582 PrintErrorMixer(
m_className +
"::GetElectronCollision");
589 std::cerr <<
m_className <<
"::GetElectronCollision: Band > 0.\n";
599 const int iE = std::min(std::max(
int(e / m_eStep), 0), iemax);
603 if (r <= m_cf[iE][0]) {
605 }
else if (r >= m_cf[iE][m_nTerms - 1]) {
606 level = m_nTerms - 1;
608 const auto begin = m_cf[iE].cbegin();
609 level = std::lower_bound(begin, begin + m_nTerms, r) - begin;
612 angCut = m_scatCut[iE][level];
613 angPar = m_scatPar[iE][level];
617 const int iE = std::min(std::max(
int(log(e / m_eHigh) / m_lnStep), 0),
618 nEnergyStepsLog - 1);
621 if (r <= m_cfLog[iE][0]) {
623 }
else if (r >= m_cfLog[iE][m_nTerms - 1]) {
624 level = m_nTerms - 1;
626 const auto begin = m_cfLog[iE].cbegin();
627 level = std::lower_bound(begin, begin + m_nTerms, r) - begin;
630 angCut = m_scatCutLog[iE][level];
631 angPar = m_scatParLog[iE][level];
635 type = m_csType[level] % nCsTypes;
636 const int igas = int(m_csType[level] / nCsTypes);
638 ++m_nCollisions[type];
639 ++m_nCollisionsDetailed[level];
642 double loss = m_energyLoss[level];
644 if (type == ElectronCollisionTypeVirtual)
return true;
646 if (type == ElectronCollisionTypeIonisation) {
650 if (e < loss) loss = e - 0.0001;
651 if (m_useOpalBeaty) {
653 const double w = m_wOpalBeaty[level];
654 esec = w * tan(
RndmUniform() * atan(0.5 * (e - loss) / w));
657 }
else if (m_useGreenSawada) {
658 const double gs = m_parGreenSawada[igas][0];
659 const double gb = m_parGreenSawada[igas][1];
660 const double w = gs * e / (e + gb);
661 const double ts = m_parGreenSawada[igas][2];
662 const double ta = m_parGreenSawada[igas][3];
663 const double tb = m_parGreenSawada[igas][4];
664 const double esec0 = ts - ta / (e + tb);
667 w * tan((r - 1.) * atan(esec0 / w) +
668 r * atan((0.5 * (e - loss) - esec0) / w));
672 if (esec <= 0) esec = Small;
675 secondaries.emplace_back(std::make_pair(IonProdTypeElectron, esec));
677 secondaries.emplace_back(std::make_pair(IonProdTypeIon, 0.));
678 bool fluorescence =
false;
679 if (m_yFluorescence[level] > Small) {
680 if (
RndmUniform() < m_yFluorescence[level]) fluorescence =
true;
684 if (m_nAuger2[level] > 0) {
685 const double eav = m_eAuger2[level] / m_nAuger2[level];
686 for (
unsigned int i = 0; i < m_nAuger2[level]; ++i) {
687 secondaries.emplace_back(std::make_pair(IonProdTypeElectron, eav));
690 if (m_nFluorescence[level] > 0) {
691 const double eav = m_eFluorescence[level] / m_nFluorescence[level];
692 for (
unsigned int i = 0; i < m_nFluorescence[level]; ++i) {
693 secondaries.emplace_back(std::make_pair(IonProdTypeElectron, eav));
696 }
else if (m_nAuger1[level] > 0) {
697 const double eav = m_eAuger1[level] / m_nAuger1[level];
698 for (
unsigned int i = 0; i < m_nAuger1[level]; ++i) {
699 secondaries.emplace_back(std::make_pair(IonProdTypeElectron, eav));
702 }
else if (type == ElectronCollisionTypeExcitation) {
704 if (m_useDeexcitation && m_iDeexcitation[level] >= 0) {
706 ComputeDeexcitationInternal(m_iDeexcitation[level], fLevel);
707 ndxc = m_dxcProducts.size();
709 m_dxcProducts.clear();
715 std::cout <<
m_className <<
"::GetElectronCollision:\n"
716 <<
" Level: " << level <<
"\n"
717 <<
" Ionization potential: " << m_minIonPot <<
"\n"
718 <<
" Excitation energy: " << loss * m_rgas[igas] <<
"\n"
719 <<
" Penning probability: " << m_rPenning[level] <<
"\n";
721 if (loss * m_rgas[igas] > m_minIonPot &&
725 double esec = loss * m_rgas[igas] - m_minIonPot;
726 if (esec <= 0) esec = Small;
731 if (m_lambdaPenning[level] > Small) {
733 newDxcProd.s = m_lambdaPenning[level] * std::cbrt(
RndmUniformPos());
735 newDxcProd.energy = esec;
736 newDxcProd.type = DxcProdTypeElectron;
737 m_dxcProducts.push_back(std::move(newDxcProd));
744 if (e < loss) loss = e - 0.0001;
748 if (m_useAnisotropic) {
749 switch (m_scatModel[level]) {
757 ctheta0 = (ctheta0 + angPar) / (1. + angPar * ctheta0);
760 std::cerr <<
m_className <<
"::GetElectronCollision:\n"
761 <<
" Unknown scattering model.\n"
762 <<
" Using isotropic distribution.\n";
767 const double s1 = m_rgas[igas];
768 const double s2 = (s1 * s1) / (s1 - 1.);
769 const double theta0 = acos(ctheta0);
770 const double arg = std::max(1. - s1 * loss / e, Small);
771 const double d = 1. - ctheta0 * sqrt(arg);
774 e1 = std::max(e * (1. - loss / (s1 * e) - 2. * d / s2), Small);
775 double q = std::min(sqrt((e / e1) * arg) / s1, 1.);
776 const double theta = asin(q * sin(theta0));
777 double ctheta = cos(theta);
779 const double u = (s1 - 1.) * (s1 - 1.) / arg;
780 if (ctheta0 * ctheta0 > u) ctheta = -ctheta;
782 const double stheta = sin(theta);
784 dz = std::min(dz, 1.);
785 const double argZ = sqrt(dx * dx + dy * dy);
789 const double cphi = cos(phi);
790 const double sphi = sin(phi);
796 const double a = stheta / argZ;
797 const double dz1 = dz * ctheta + argZ * stheta * sphi;
798 const double dy1 = dy * ctheta + a * (dx * cphi - dy * dz * sphi);
799 const double dx1 = dx * ctheta - a * (dy * cphi + dx * dz * sphi);
809 double& s,
int& type,
810 double& energy)
const {
811 if (i >= m_dxcProducts.size() || !(m_useDeexcitation ||
m_usePenning)) {
814 t = m_dxcProducts[i].t;
815 s = m_dxcProducts[i].s;
816 type = m_dxcProducts[i].type;
817 energy = m_dxcProducts[i].energy;
823 std::cerr <<
m_className <<
"::GetPhotonCollisionRate: Invalid energy.\n";
824 return m_cfTotGamma[0];
826 if (e > m_eFinalGamma && m_useAutoAdjust) {
827 std::cerr <<
m_className <<
"::GetPhotonCollisionRate:\n Rate at " << e
828 <<
" eV is not included in the current table.\n"
829 <<
" Increasing energy range to " << 1.05 * e <<
" eV.\n";
835 PrintErrorMixer(
m_className +
"::GetPhotonCollisionRate");
842 std::min(std::max(
int(e / m_eStepGamma), 0), nEnergyStepsGamma - 1);
844 double cfSum = m_cfTotGamma[iE];
845 if (m_useDeexcitation && m_useRadTrap && !m_deexcitations.empty()) {
847 for (
const auto& dxc : m_deexcitations) {
848 if (dxc.cf > 0. && fabs(e - dxc.energy) <= dxc.width) {
850 TMath::Voigt(e - dxc.energy, dxc.sDoppler, 2 * dxc.gPressure);
859 double& e1,
double& ctheta,
int& nsec,
862 std::cerr <<
m_className <<
"::GetPhotonCollision: Invalid energy.\n";
865 if (e > m_eFinalGamma && m_useAutoAdjust) {
866 std::cerr <<
m_className <<
"::GetPhotonCollision:\n Provided energy ("
867 << e <<
" eV) exceeds current energy range.\n"
868 <<
" Increasing energy range to " << 1.05 * e <<
" eV.\n";
874 PrintErrorMixer(
m_className +
"::GetPhotonCollision");
882 std::min(std::max(
int(e / m_eStepGamma), 0), nEnergyStepsGamma - 1);
884 double r = m_cfTotGamma[iE];
885 if (m_useDeexcitation && m_useRadTrap && !m_deexcitations.empty()) {
887 std::vector<double> pLine(0);
888 std::vector<int> iLine(0);
890 const unsigned int nDeexcitations = m_deexcitations.size();
891 for (
unsigned int i = 0; i < nDeexcitations; ++i) {
892 const auto& dxc = m_deexcitations[i];
893 if (dxc.cf > 0. && fabs(e - dxc.energy) <= dxc.width) {
895 TMath::Voigt(e - dxc.energy, dxc.sDoppler, 2 * dxc.gPressure);
902 if (nLines > 0 && r >= m_cfTotGamma[iE]) {
904 for (
int i = 0; i < nLines; ++i) {
906 ++m_nPhotonCollisions[PhotonCollisionTypeExcitation];
908 ComputeDeexcitationInternal(iLine[i], fLevel);
909 type = PhotonCollisionTypeExcitation;
910 nsec = m_dxcProducts.size();
914 std::cerr <<
m_className <<
"::GetPhotonCollision:\n";
915 std::cerr <<
" Random sampling of deexcitation line failed.\n";
916 std::cerr <<
" Program bug!\n";
923 if (r <= m_cfGamma[iE][0]) {
925 }
else if (r >= m_cfGamma[iE][m_nPhotonTerms - 1]) {
926 level = m_nPhotonTerms - 1;
928 const auto begin = m_cfGamma[iE].cbegin();
929 level = std::lower_bound(begin, begin + m_nPhotonTerms, r) - begin;
934 type = csTypeGamma[level];
936 type = type % nCsTypesGamma;
937 int ngas = int(csTypeGamma[level] / nCsTypesGamma);
938 ++m_nPhotonCollisions[type];
941 esec = std::max(e - m_ionPot[ngas], Small);
952 m_nCollisions.fill(0);
953 m_nCollisionsDetailed.assign(m_nTerms, 0);
955 m_nPhotonCollisions.fill(0);
959 return std::accumulate(std::begin(m_nCollisions), std::end(m_nCollisions), 0);
963 unsigned int& nElastic,
unsigned int& nIonisation,
964 unsigned int& nAttachment,
unsigned int& nInelastic,
965 unsigned int& nExcitation,
unsigned int& nSuperelastic)
const {
966 nElastic = m_nCollisions[ElectronCollisionTypeElastic];
967 nIonisation = m_nCollisions[ElectronCollisionTypeIonisation];
968 nAttachment = m_nCollisions[ElectronCollisionTypeAttachment];
969 nInelastic = m_nCollisions[ElectronCollisionTypeInelastic];
970 nExcitation = m_nCollisions[ElectronCollisionTypeExcitation];
971 nSuperelastic = m_nCollisions[ElectronCollisionTypeSuperelastic];
972 return nElastic + nIonisation + nAttachment + nInelastic + nExcitation +
979 PrintErrorMixer(
m_className +
"::GetNumberOfLevels");
989 std::string& descr,
double& e) {
999 std::cerr <<
m_className <<
"::GetLevel: Index out of range.\n";
1004 type = m_csType[i] % nCsTypes;
1005 ngas = int(m_csType[i] / nCsTypes);
1007 descr = m_description[i];
1009 e = m_rgas[ngas] * m_energyLoss[i];
1012 <<
" Level " << i <<
": " << descr <<
"\n"
1013 <<
" Type " << type <<
"\n"
1014 <<
" Threshold energy: " << e <<
" eV\n";
1015 if (type == ElectronCollisionTypeExcitation &&
m_usePenning &&
1017 std::cout <<
" Penning transfer coefficient: " << m_rPenning[i]
1019 }
else if (type == ElectronCollisionTypeExcitation && m_useDeexcitation) {
1020 const int idxc = m_iDeexcitation[i];
1021 if (idxc < 0 || idxc >= (
int)m_deexcitations.size()) {
1022 std::cout <<
" Deexcitation cascade not implemented.\n";
1025 const auto& dxc = m_deexcitations[idxc];
1027 std::cout <<
" Oscillator strength: " << dxc.osc <<
"\n";
1029 std::cout <<
" Decay channels:\n";
1030 const int nChannels = dxc.type.size();
1031 for (
int j = 0; j < nChannels; ++j) {
1032 if (dxc.type[j] == DxcTypeRad) {
1033 std::cout <<
" Radiative decay to ";
1034 if (dxc.final[j] < 0) {
1035 std::cout <<
"ground state: ";
1037 std::cout << m_deexcitations[dxc.final[j]].label <<
": ";
1039 }
else if (dxc.type[j] == DxcTypeCollIon) {
1040 if (dxc.final[j] < 0) {
1041 std::cout <<
" Penning ionisation: ";
1043 std::cout <<
" Associative ionisation: ";
1045 }
else if (dxc.type[j] == DxcTypeCollNonIon) {
1046 if (dxc.final[j] >= 0) {
1047 std::cout <<
" Collision-induced transition to "
1048 << m_deexcitations[dxc.final[j]].label <<
": ";
1050 std::cout <<
" Loss: ";
1053 const double br = j == 0 ? dxc.p[j] : dxc.p[j] - dxc.p[j - 1];
1054 std::cout << std::setprecision(5) << br * 100. <<
"%\n";
1063 const unsigned int level)
const {
1064 if (level >= m_nTerms) {
1065 std::cerr <<
m_className <<
"::GetNumberOfElectronCollisions: "
1066 <<
"Level " << level <<
" does not exist.\n";
1069 return m_nCollisionsDetailed[level];
1073 return std::accumulate(std::begin(m_nPhotonCollisions),
1074 std::end(m_nPhotonCollisions), 0);
1078 unsigned int& nElastic,
unsigned int& nIonising,
1079 unsigned int& nInelastic)
const {
1080 nElastic = m_nPhotonCollisions[0];
1081 nIonising = m_nPhotonCollisions[1];
1082 nInelastic = m_nPhotonCollisions[2];
1083 return nElastic + nIonising + nInelastic;
1086int MediumMagboltz::GetGasNumberMagboltz(
const std::string& input)
const {
1088 if (input.empty())
return 0;
1090 if (input ==
"CF4") {
1092 }
else if (input ==
"Ar") {
1094 }
else if (input ==
"He" || input ==
"He-4") {
1097 }
else if (input ==
"He-3") {
1100 }
else if (input ==
"Ne") {
1102 }
else if (input ==
"Kr") {
1104 }
else if (input ==
"Xe") {
1106 }
else if (input ==
"CH4") {
1109 }
else if (input ==
"C2H6") {
1112 }
else if (input ==
"C3H8") {
1115 }
else if (input ==
"iC4H10") {
1118 }
else if (input ==
"CO2") {
1120 }
else if (input ==
"neoC5H12") {
1123 }
else if (input ==
"H2O") {
1125 }
else if (input ==
"O2") {
1127 }
else if (input ==
"N2") {
1129 }
else if (input ==
"NO") {
1132 }
else if (input ==
"N2O") {
1135 }
else if (input ==
"C2H4") {
1138 }
else if (input ==
"C2H2") {
1141 }
else if (input ==
"H2") {
1144 }
else if (input ==
"D2") {
1147 }
else if (input ==
"CO") {
1150 }
else if (input ==
"Methylal") {
1153 }
else if (input ==
"DME") {
1155 }
else if (input ==
"Reid-Step") {
1157 }
else if (input ==
"Maxwell-Model") {
1159 }
else if (input ==
"Reid-Ramp") {
1161 }
else if (input ==
"C2F6") {
1163 }
else if (input ==
"SF6") {
1165 }
else if (input ==
"NH3") {
1167 }
else if (input ==
"C3H6") {
1170 }
else if (input ==
"cC3H6") {
1173 }
else if (input ==
"CH3OH") {
1176 }
else if (input ==
"C2H5OH") {
1179 }
else if (input ==
"C3H7OH") {
1182 }
else if (input ==
"Cs") {
1184 }
else if (input ==
"F2") {
1187 }
else if (input ==
"CS2") {
1189 }
else if (input ==
"COS") {
1191 }
else if (input ==
"CD4") {
1194 }
else if (input ==
"BF3") {
1196 }
else if (input ==
"C2HF5" || input ==
"C2H2F4") {
1198 }
else if (input ==
"TMA") {
1200 }
else if (input ==
"nC3H7OH") {
1203 }
else if (input ==
"paraH2") {
1206 }
else if (input ==
"orthoD2") {
1209 }
else if (input ==
"CHF3") {
1211 }
else if (input ==
"CF3Br") {
1213 }
else if (input ==
"C3F8") {
1215 }
else if (input ==
"O3") {
1218 }
else if (input ==
"Hg") {
1221 }
else if (input ==
"H2S") {
1223 }
else if (input ==
"nC4H10") {
1226 }
else if (input ==
"nC5H12") {
1229 }
else if (input ==
"N2 (Phelps)") {
1231 }
else if (input ==
"GeH4") {
1234 }
else if (input ==
"SiH4") {
1239 std::cerr <<
m_className <<
"::GetGasNumberMagboltz:\n"
1240 <<
" Gas " << input <<
" is not defined.\n";
1244bool MediumMagboltz::Mixer(
const bool verbose) {
1261 const double en = (i + 0.5) * m_eStep;
1271 const double prefactor = dens * SpeedOfLight *
sqrt(2. / ElectronMass);
1278 m_parGreenSawada.fill({1., 0., 0., 0., 0.});
1279 m_hasGreenSawada.fill(
false);
1281 m_wOpalBeaty.fill(1.);
1282 m_energyLoss.fill(0.);
1285 m_yFluorescence.fill(0.);
1290 m_nFluorescence.fill(0);
1291 m_eFluorescence.fill(0.);
1293 m_scatModel.fill(0);
1295 m_rPenning.fill(0.);
1296 m_lambdaPenning.fill(0.);
1298 m_deexcitations.clear();
1299 m_iDeexcitation.fill(-1);
1303 m_cfTotLog.assign(nEnergyStepsLog, 0.);
1307 m_cfLog.assign(nEnergyStepsLog,
1315 m_scatParLog.assign(nEnergyStepsLog,
1317 m_scatCutLog.assign(nEnergyStepsLog,
1349 const int ng = GetGasNumberMagboltz(
m_gas[i]);
1352 <<
" does not have a gas number in Magboltz.\n";
1360 <<
" linear energy steps between 0 and "
1361 << std::min(m_eMax, m_eHigh) <<
" eV.\n";
1362 if (m_eMax > m_eHigh) {
1363 std::cout <<
" " << nEnergyStepsLog <<
" logarithmic steps between "
1364 << m_eHigh <<
" and " << m_eMax <<
" eV\n";
1369 std::ofstream outfile;
1370 if (m_useCsOutput) {
1371 outfile.open(
"cs.txt", std::ios::out);
1372 outfile <<
"# energy [eV] vs. cross-section [cm2]\n";
1389 long long nNull = 0;
1393 std::array<double, 6> e;
1395 std::array<double, Magboltz::nMaxInelasticTerms> eIn;
1397 std::array<double, Magboltz::nMaxIonisationTerms> eIon;
1399 std::array<long long, Magboltz::nMaxInelasticTerms> kIn;
1400 std::array<long long, 6> kEl;
1402 std::array<double, Magboltz::nMaxIonisationTerms> eoby;
1404 std::array<double, Magboltz::nMaxNullTerms> scln;
1406 std::array<long long, Magboltz::nMaxIonisationTerms> nc0;
1407 std::array<long long, Magboltz::nMaxIonisationTerms> ng1;
1408 std::array<long long, Magboltz::nMaxIonisationTerms> ng2;
1409 std::array<double, Magboltz::nMaxIonisationTerms> ec0;
1410 std::array<double, Magboltz::nMaxIonisationTerms> wklm;
1411 std::array<double, Magboltz::nMaxIonisationTerms> efl;
1412 std::array<double, Magboltz::nMaxIonisationTerms> eg1;
1413 std::array<double, Magboltz::nMaxIonisationTerms> eg2;
1416 long long ngs = gasNumber[iGas];
1418 &ngs, q[0], qIn[0], &nIn, e.data(), eIn.data(), name, &virial,
1419 eoby.data(), pEqEl[0], pEqIn[0], penFra[0], kEl.data(), kIn.data(),
1420 qIon[0], pEqIon[0], eIon.data(), &nIon, qAtt[0], &nAtt, qNull[0],
1421 &nNull, scln.data(), nc0.data(), ec0.data(), wklm.data(), efl.data(),
1422 ng1.data(), eg1.data(), ng2.data(), eg2.data(), scrpt, scrptn);
1424 const double m = (2. / e[1]) * ElectronMass / AtomicMassUnitElectronVolt;
1425 std::cout <<
" " << name <<
"\n"
1426 <<
" mass: " << m <<
" amu\n";
1428 std::cout <<
" ionisation threshold: " << eIon[0] <<
" eV\n";
1430 std::cout <<
" ionisation threshold: " << e[2] <<
" eV\n";
1432 if (e[3] > 0. && e[4] > 0.) {
1433 std::cout <<
" cross-sections at minimum ionising energy:\n"
1434 <<
" excitation: " << e[3] * 1.e18 <<
" Mbarn\n"
1435 <<
" ionisation: " << e[4] * 1.e18 <<
" Mbarn\n";
1446 const double van =
m_fraction[iGas] * prefactor;
1448 if (m_useCsOutput) {
1449 outfile <<
"# cross-sections for " << name <<
"\n";
1450 outfile <<
"# cross-section types:\n";
1451 outfile <<
"# elastic\n";
1455 m_scatModel[np] = kEl[1];
1456 const double r = 1. + 0.5 * e[1];
1458 m_energyLoss[np] = 0.;
1459 m_description[np] = GetDescription(1, scrpt);
1460 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeElastic;
1461 bool withIon =
false;
1464 for (
int j = 0; j < nIon; ++j) {
1465 if (m_eMax < eIon[j])
continue;
1469 m_scatModel[np] = kEl[2];
1470 m_energyLoss[np] = eIon[j] / r;
1471 m_wOpalBeaty[np] = eoby[j];
1472 m_yFluorescence[np] = wklm[j];
1473 m_nAuger1[np] = nc0[j];
1474 m_eAuger1[np] = ec0[j];
1475 m_nFluorescence[np] = ng1[j];
1476 m_eFluorescence[np] = eg1[j];
1477 m_nAuger2[np] = ng2[j];
1478 m_eAuger2[np] = eg2[j];
1479 m_description[np] = GetDescription(2 + j, scrpt);
1480 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeIonisation;
1481 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1483 m_parGreenSawada[iGas][0] = eoby[0];
1484 m_parGreenSawada[iGas][4] = 2 * eIon[0];
1485 m_ionPot[iGas] = eIon[0];
1487 if (m_eMax >= e[2]) {
1491 m_scatModel[np] = kEl[2];
1492 m_energyLoss[np] = e[2] / r;
1493 m_wOpalBeaty[np] = eoby[0];
1494 m_parGreenSawada[iGas][0] = eoby[0];
1495 m_parGreenSawada[iGas][4] = 2 * e[2];
1496 m_ionPot[iGas] = e[2];
1497 m_description[np] = GetDescription(2, scrpt);
1498 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeIonisation;
1499 if (m_useCsOutput) outfile <<
"# ionisation (gross)\n";
1503 for (
int j = 0; j < nAtt; ++j) {
1506 m_scatModel[np] = 0;
1507 m_energyLoss[np] = 0.;
1508 m_description[np] = GetDescription(2 + nIon + j, scrpt);
1509 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeAttachment;
1510 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1513 int nExc = 0, nSuperEl = 0;
1514 for (
int j = 0; j < nIn; ++j) {
1516 m_scatModel[np] = kIn[j];
1517 m_energyLoss[np] = eIn[j] / r;
1518 m_description[np] = GetDescription(4 + nIon + nAtt + j, scrpt);
1519 if ((m_description[np][1] ==
'E' && m_description[np][2] ==
'X') ||
1520 (m_description[np][0] ==
'E' && m_description[np][1] ==
'X') ||
1521 (
m_gas[iGas] ==
"N2" && eIn[j] > 6.)) {
1523 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeExcitation;
1525 }
else if (eIn[j] < 0.) {
1527 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeSuperelastic;
1531 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeInelastic;
1533 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1537 for (
int j = 0; j < nNull; ++j) {
1540 m_scatModel[np] = 0;
1541 m_energyLoss[np] = 0.;
1542 m_description[np] = GetDescription(j, scrptn);
1543 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeVirtual;
1544 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1550 if (m_useCsOutput) {
1551 outfile << (iE + 0.5) * m_eStep <<
" " << q[iE][1] <<
" ";
1554 m_cf[iE][np] = q[iE][1] * van;
1555 SetScatteringParameters(m_scatModel[np], pEqEl[iE][1], m_scatCut[iE][np],
1560 for (
int j = 0; j < nIon; ++j) {
1561 if (m_eMax < eIon[j])
continue;
1563 m_cf[iE][np] = qIon[iE][j] * van;
1564 SetScatteringParameters(m_scatModel[np], pEqIon[iE][j],
1565 m_scatCut[iE][np], m_scatPar[iE][np]);
1566 if (m_useCsOutput) outfile << qIon[iE][j] <<
" ";
1570 m_cf[iE][np] = q[iE][2] * van;
1571 SetScatteringParameters(m_scatModel[np], pEqEl[iE][2],
1572 m_scatCut[iE][np], m_scatPar[iE][np]);
1573 if (m_useCsOutput) outfile << q[iE][2] <<
" ";
1577 for (
int j = 0; j < nAtt; ++j) {
1579 m_cf[iE][np] = qAtt[iE][j] * van;
1581 m_scatPar[iE][np] = 0.5;
1582 if (m_useCsOutput) outfile << qAtt[iE][j] <<
" ";
1585 for (
int j = 0; j < nIn; ++j) {
1587 if (m_useCsOutput) outfile << qIn[iE][j] <<
" ";
1588 m_cf[iE][np] = qIn[iE][j] * van;
1590 m_cf[iE][np] *= m_scaleExc[iGas];
1591 if (m_cf[iE][np] < 0.) {
1593 <<
" Negative inelastic cross-section at "
1594 << (iE + 0.5) * m_eStep <<
" eV. Set to zero.\n";
1597 SetScatteringParameters(m_scatModel[np], pEqIn[iE][j],
1598 m_scatCut[iE][np], m_scatPar[iE][np]);
1600 if ((
m_debug || verbose) && nIn > 0 && iE == iemax) {
1601 std::cout <<
" " << nIn <<
" inelastic terms (" << nExc
1602 <<
" excitations, " << nSuperEl <<
" superelastic, "
1603 << nIn - nExc - nSuperEl <<
" other)\n";
1606 for (
int j = 0; j < nNull; ++j) {
1608 m_cf[iE][np] = qNull[iE][j] * van * scln[j];
1610 if (m_useCsOutput) outfile << qNull[iE][j] <<
" ";
1613 if (m_useCsOutput) outfile <<
"\n";
1615 if (m_eMax <= m_eHigh)
continue;
1618 const double rLog =
pow(m_eMax / m_eHigh, 1. / nEnergyStepsLog);
1619 m_lnStep = log(rLog);
1621 double emax = m_eHigh * rLog;
1623 for (
int iE = 0; iE < nEnergyStepsLog; ++iE) {
1629 &ngs, q[0], qIn[0], &nIn, e.data(), eIn.data(), name, &virial,
1630 eoby.data(), pEqEl[0], pEqIn[0], penFra[0], kEl.data(), kIn.data(),
1631 qIon[0], pEqIon[0], eIon.data(), &nIon, qAtt[0], &nAtt, qNull[0],
1632 &nNull, scln.data(), nc0.data(), ec0.data(), wklm.data(), efl.data(),
1633 ng1.data(), eg1.data(), ng2.data(), eg2.data(), scrpt, scrptn);
1635 if (m_useCsOutput) outfile << emax <<
" " << q[iemax][1] <<
" ";
1637 m_cfLog[iE][np] = q[iemax][1] * van;
1638 SetScatteringParameters(m_scatModel[np], pEqEl[iemax][1],
1639 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1643 for (
int j = 0; j < nIon; ++j) {
1644 if (m_eMax < eIon[j])
continue;
1646 m_cfLog[iE][np] = qIon[iemax][j] * van;
1647 SetScatteringParameters(m_scatModel[np], pEqIon[iemax][j],
1648 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1649 if (m_useCsOutput) outfile << qIon[iemax][j] <<
" ";
1654 m_cfLog[iE][np] = q[iemax][2] * van;
1657 SetScatteringParameters(m_scatModel[np], pEqEl[iemax][2],
1658 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1659 if (m_useCsOutput) outfile << q[iemax][2] <<
" ";
1663 for (
int j = 0; j < nAtt; ++j) {
1665 m_cfLog[iE][np] = qAtt[iemax][j] * van;
1667 if (m_useCsOutput) outfile << qAtt[iemax][j] <<
" ";
1670 for (
int j = 0; j < nIn; ++j) {
1672 if (m_useCsOutput) outfile << qIn[iemax][j] <<
" ";
1673 m_cfLog[iE][np] = qIn[iemax][j] * van;
1675 m_cfLog[iE][np] *= m_scaleExc[iGas];
1676 if (m_cfLog[iE][np] < 0.) {
1678 <<
" Negative inelastic cross-section at " << emax
1679 <<
" eV. Set to zero.\n";
1680 m_cfLog[iE][np] = 0.;
1682 SetScatteringParameters(m_scatModel[np], pEqIn[iemax][j],
1683 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1686 for (
int j = 0; j < nNull; ++j) {
1688 m_cfLog[iE][np] = qNull[iemax][j] * van * scln[j];
1689 if (m_useCsOutput) outfile << qNull[iemax][j] <<
" ";
1692 if (m_useCsOutput) outfile <<
"\n";
1697 if (m_useCsOutput) outfile.close();
1700 auto it = std::min_element(std::begin(m_ionPot),
1703 std::string minIonPotGas =
m_gas[std::distance(std::begin(m_ionPot), it)];
1707 <<
" Lowest ionisation threshold in the mixture: "
1708 << m_minIonPot <<
" eV (" << minIonPotGas <<
")\n";
1713 for (
unsigned int k = 0; k < m_nTerms; ++k) {
1714 if (m_cf[iE][k] < 0.) {
1716 <<
" Negative collision rate at " << (iE + 0.5) * m_eStep
1717 <<
" eV, cross-section " << k <<
". Set to zero.\n";
1718 std::cout << m_description[k] <<
"\n";
1721 m_cfTot[iE] += m_cf[iE][k];
1724 if (m_cfTot[iE] > 0.) {
1725 for (
unsigned int k = 0; k < m_nTerms; ++k) m_cf[iE][k] /= m_cfTot[iE];
1727 for (
unsigned int k = 1; k < m_nTerms; ++k) {
1728 m_cf[iE][k] += m_cf[iE][k - 1];
1730 const double ekin = m_eStep * (iE + 0.5);
1731 m_cfTot[iE] *=
sqrt(ekin);
1734 const double re = ekin / ElectronMass;
1735 m_cfTot[iE] *=
sqrt(1. + 0.5 * re) / (1. + re);
1739 if (m_eMax > m_eHigh) {
1740 const double rLog =
pow(m_eMax / m_eHigh, 1. / nEnergyStepsLog);
1741 for (
int iE = 0; iE < nEnergyStepsLog; ++iE) {
1743 for (
unsigned int k = 0; k < m_nTerms; ++k) {
1744 if (m_cfLog[iE][k] < 0.) m_cfLog[iE][k] = 0.;
1745 m_cfTotLog[iE] += m_cfLog[iE][k];
1748 if (m_cfTotLog[iE] > 0.) {
1749 for (
unsigned int k = 0; k < m_nTerms; ++k) {
1750 m_cfLog[iE][k] /= m_cfTotLog[iE];
1753 for (
unsigned int k = 1; k < m_nTerms; ++k) {
1754 m_cfLog[iE][k] += m_cfLog[iE][k - 1];
1756 const double ekin = m_eHigh *
pow(rLog, iE + 1);
1757 const double re = ekin / ElectronMass;
1758 m_cfTotLog[iE] *=
sqrt(ekin) *
sqrt(1. + re) / (1. + re);
1760 m_cfTotLog[iE] = log(m_cfTotLog[iE]);
1767 if (m_cfTot[j] > m_cfNull) m_cfNull = m_cfTot[j];
1769 if (m_eMax > m_eHigh) {
1770 for (
int j = 0; j < nEnergyStepsLog; ++j) {
1771 const double r =
exp(m_cfTotLog[j]);
1772 if (r > m_cfNull) m_cfNull = r;
1777 m_nCollisionsDetailed.assign(m_nTerms, 0);
1778 m_nCollisions.fill(0);
1782 <<
" Energy [eV] Collision Rate [ns-1]\n";
1783 const double emax = std::min(m_eHigh, m_eMax);
1784 for (
int i = 0; i < 8; ++i) {
1785 const double en = (2 * i + 1) * emax / 16;
1787 std::printf(
" %10.2f %18.2f\n", en, cf);
1792 if (m_useDeexcitation) {
1793 ComputeDeexcitationTable(verbose);
1794 for (
const auto& dxc : m_deexcitations) {
1795 if (dxc.p.size() == dxc.final.size() && dxc.p.size() == dxc.type.size())
1798 <<
" Mismatch in deexcitation channel count. Program bug!\n"
1799 <<
" Deexcitation handling is switched off.\n";
1800 m_useDeexcitation =
false;
1806 if (!ComputePhotonCollisionTable(verbose)) {
1808 <<
" Photon collision rates could not be calculated.\n";
1809 if (m_useDeexcitation) {
1810 std::cerr <<
" Deexcitation handling is switched off.\n";
1811 m_useDeexcitation =
false;
1817 std::cout <<
m_className <<
"::Mixer: Resetting transfer probabilities.\n"
1820 std::cout <<
" Component " << i <<
": " <<
m_rPenningGas[i] <<
"\n";
1827 for (
unsigned int i = 0; i < m_nTerms; ++i) {
1828 int iGas = int(m_csType[i] / nCsTypes);
1841void MediumMagboltz::SetupGreenSawada() {
1843 const double ta = 1000.;
1844 const double tb = m_parGreenSawada[i][4];
1845 m_hasGreenSawada[i] =
true;
1846 if (
m_gas[i] ==
"He" ||
m_gas[i] ==
"He-3") {
1847 m_parGreenSawada[i] = {15.5, 24.5, -2.25, ta, tb};
1848 }
else if (
m_gas[i] ==
"Ne") {
1849 m_parGreenSawada[i] = {24.3, 21.6, -6.49, ta, tb};
1850 }
else if (
m_gas[i] ==
"Ar") {
1851 m_parGreenSawada[i] = {6.92, 7.85, 6.87, ta, tb};
1852 }
else if (
m_gas[i] ==
"Kr") {
1853 m_parGreenSawada[i] = {7.95, 13.5, 3.90, ta, tb};
1854 }
else if (
m_gas[i] ==
"Xe") {
1855 m_parGreenSawada[i] = {7.93, 11.5, 3.81, ta, tb};
1856 }
else if (
m_gas[i] ==
"H2" ||
m_gas[i] ==
"D2") {
1857 m_parGreenSawada[i] = {7.07, 7.7, 1.87, ta, tb};
1858 }
else if (
m_gas[i] ==
"N2") {
1859 m_parGreenSawada[i] = {13.8, 15.6, 4.71, ta, tb};
1860 }
else if (
m_gas[i] ==
"O2") {
1861 m_parGreenSawada[i] = {18.5, 12.1, 1.86, ta, tb};
1862 }
else if (
m_gas[i] ==
"CH4") {
1863 m_parGreenSawada[i] = {7.06, 12.5, 3.45, ta, tb};
1864 }
else if (
m_gas[i] ==
"H2O") {
1865 m_parGreenSawada[i] = {12.8, 12.6, 1.28, ta, tb};
1866 }
else if (
m_gas[i] ==
"CO") {
1867 m_parGreenSawada[i] = {13.3, 14.0, 2.03, ta, tb};
1868 }
else if (
m_gas[i] ==
"C2H2") {
1869 m_parGreenSawada[i] = {9.28, 5.8, 1.37, ta, tb};
1870 }
else if (
m_gas[i] ==
"NO") {
1871 m_parGreenSawada[i] = {10.4, 9.5, -4.30, ta, tb};
1872 }
else if (
m_gas[i] ==
"CO2") {
1873 m_parGreenSawada[i] = {12.3, 13.8, -2.46, ta, tb};
1875 m_parGreenSawada[i][3] = 0.;
1876 m_hasGreenSawada[i] =
false;
1877 if (m_useGreenSawada) {
1878 std::cout <<
m_className <<
"::SetupGreenSawada:\n"
1879 <<
" Fit parameters for " <<
m_gas[i]
1880 <<
" not available.\n"
1881 <<
" Opal-Beaty formula is used instead.\n";
1887void MediumMagboltz::SetScatteringParameters(
const int model,
1888 const double parIn,
double& cut,
1889 double& parOut)
const {
1892 if (model <= 0)
return;
1907 constexpr double rads = 2. / Pi;
1908 const double cns = parIn - 0.5;
1909 const double thetac =
asin(2. *
sqrt(cns - cns * cns));
1910 const double fac = (1. -
cos(thetac)) /
pow(
sin(thetac), 2.);
1911 parOut = cns * fac + 0.5;
1912 cut = thetac * rads;
1915void MediumMagboltz::ComputeDeexcitationTable(
const bool verbose) {
1916 m_iDeexcitation.fill(-1);
1917 m_deexcitations.clear();
1920 OpticalData optData;
1926 std::map<std::string, std::string> levelNamesAr = {
1927 {
"1S5 ",
"Ar_1S5"}, {
"1S4 ",
"Ar_1S4"},
1928 {
"1S3 ",
"Ar_1S3"}, {
"1S2 ",
"Ar_1S2"},
1929 {
"2P10 ",
"Ar_2P10"}, {
"2P9 ",
"Ar_2P9"},
1930 {
"2P8 ",
"Ar_2P8"}, {
"2P7 ",
"Ar_2P7"},
1931 {
"2P6 ",
"Ar_2P6"}, {
"2P5 ",
"Ar_2P5"},
1932 {
"2P4 ",
"Ar_2P4"}, {
"2P3 ",
"Ar_2P3"},
1933 {
"2P2 ",
"Ar_2P2"}, {
"2P1 ",
"Ar_2P1"},
1934 {
"3D6 ",
"Ar_3D6"}, {
"3D5 ",
"Ar_3D5"},
1935 {
"3D3 ",
"Ar_3D3"}, {
"3D4! ",
"Ar_3D4!"},
1936 {
"3D4 ",
"Ar_3D4"}, {
"3D1!! ",
"Ar_3D1!!"},
1937 {
"2S5 ",
"Ar_2S5"}, {
"2S4 ",
"Ar_2S4"},
1938 {
"3D1! ",
"Ar_3D1!"}, {
"3D2 ",
"Ar_3D2"},
1939 {
"3S1!!!!",
"Ar_3S1!!!!"}, {
"3S1!! ",
"Ar_3S1!!"},
1940 {
"3S1!!! ",
"Ar_3S1!!!"}, {
"2S3 ",
"Ar_2S3"},
1941 {
"2S2 ",
"Ar_2S2"}, {
"3S1! ",
"Ar_3S1!"},
1942 {
"4D5 ",
"Ar_4D5"}, {
"3S4 ",
"Ar_3S4"},
1943 {
"4D2 ",
"Ar_4D2"}, {
"4S1! ",
"Ar_4S1!"},
1944 {
"3S2 ",
"Ar_3S2"}, {
"5D5 ",
"Ar_5D5"},
1945 {
"4S4 ",
"Ar_4S4"}, {
"5D2 ",
"Ar_5D2"},
1946 {
"6D5 ",
"Ar_6D5"}, {
"5S1! ",
"Ar_5S1!"},
1947 {
"4S2 ",
"Ar_4S2"}, {
"5S4 ",
"Ar_5S4"},
1948 {
"6D2 ",
"Ar_6D2"}, {
"HIGH ",
"Ar_Higher"}};
1950 std::map<std::string, int> mapLevels;
1952 for (
unsigned int i = 0; i < m_nTerms; ++i) {
1954 if (m_csType[i] % nCsTypes != ElectronCollisionTypeExcitation)
continue;
1956 const int ngas = int(m_csType[i] / nCsTypes);
1957 if (
m_gas[ngas] ==
"Ar") {
1959 if (iAr < 0) iAr = ngas;
1961 std::string level =
" ";
1962 for (
int j = 0; j < 7; ++j) level[j] = m_description[i][5 + j];
1963 if (levelNamesAr.find(level) != levelNamesAr.end()) {
1964 mapLevels[levelNamesAr[level]] = i;
1966 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n"
1967 <<
" Unknown Ar excitation level: " << level <<
"\n";
1973 unsigned int nDeexcitations = 0;
1974 std::map<std::string, int> lvl;
1975 for (
auto it = mapLevels.cbegin(), end = mapLevels.cend(); it != end; ++it) {
1976 std::string level = (*it).first;
1977 lvl[level] = nDeexcitations;
1978 m_iDeexcitation[(*it).second] = nDeexcitations;
1983 constexpr double f2A =
1984 2. * SpeedOfLight * FineStructureConstant / (3. * ElectronMass * HbarC);
1994 for (
auto it = mapLevels.cbegin(), end = mapLevels.cend(); it != end; ++it) {
1995 std::string level = (*it).first;
1997 dxc.gas = int(m_csType[(*it).second] / nCsTypes);
1998 dxc.level = (*it).second;
2001 dxc.energy = m_energyLoss[(*it).second] * m_rgas[dxc.gas];
2003 dxc.osc = dxc.cf = 0.;
2004 dxc.sDoppler = dxc.gPressure = dxc.width = 0.;
2005 const std::vector<int> levelsAr4s = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"],
2006 lvl[
"Ar_1S3"], lvl[
"Ar_1S2"]};
2007 if (level ==
"Ar_1S5" || level ==
"Ar_1S3") {
2009 }
else if (level ==
"Ar_1S4") {
2014 }
else if (level ==
"Ar_1S2") {
2019 }
else if (level ==
"Ar_2P10") {
2020 dxc.p = {0.0189, 5.43e-3, 9.8e-4, 1.9e-4};
2021 dxc.final = levelsAr4s;
2022 }
else if (level ==
"Ar_2P9") {
2024 dxc.final = {lvl[
"Ar_1S5"]};
2025 }
else if (level ==
"Ar_2P8") {
2026 dxc.p = {9.28e-3, 0.0215, 1.47e-3};
2027 dxc.final = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"], lvl[
"Ar_1S2"]};
2028 }
else if (level ==
"Ar_2P7") {
2029 dxc.p = {5.18e-3, 0.025, 2.43e-3, 1.06e-3};
2030 dxc.final = levelsAr4s;
2031 }
else if (level ==
"Ar_2P6") {
2032 dxc.p = {0.0245, 4.9e-3, 5.03e-3};
2033 dxc.final = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"], lvl[
"Ar_1S2"]};
2034 }
else if (level ==
"Ar_2P5") {
2036 dxc.final = {lvl[
"Ar_1S4"]};
2037 }
else if (level ==
"Ar_2P4") {
2038 dxc.p = {6.25e-4, 2.2e-5, 0.0186, 0.0139};
2039 dxc.final = levelsAr4s;
2040 }
else if (level ==
"Ar_2P3") {
2041 dxc.p = {3.8e-3, 8.47e-3, 0.0223};
2042 dxc.final = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"], lvl[
"Ar_1S2"]};
2043 }
else if (level ==
"Ar_2P2") {
2044 dxc.p = {6.39e-3, 1.83e-3, 0.0117, 0.0153};
2045 dxc.final = levelsAr4s;
2046 }
else if (level ==
"Ar_2P1") {
2047 dxc.p = {2.36e-4, 0.0445};
2048 dxc.final = {lvl[
"Ar_1S4"], lvl[
"Ar_1S2"]};
2049 }
else if (level ==
"Ar_3D6") {
2051 dxc.p = {8.1e-3, 7.73e-4, 1.2e-4, 3.6e-4};
2052 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P7"], lvl[
"Ar_2P4"], lvl[
"Ar_2P2"]};
2053 }
else if (level ==
"Ar_3D5") {
2057 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2058 dxc.p = {7.4e-3, 3.9e-5, 3.09e-4, 1.37e-3, 5.75e-4,
2059 3.2e-5, 1.4e-4, 1.7e-4, 2.49e-6, p0};
2060 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"],
2061 lvl[
"Ar_2P7"], lvl[
"Ar_2P6"],
2062 lvl[
"Ar_2P5"], lvl[
"Ar_2P4"],
2063 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"],
2065 }
else if (level ==
"Ar_3D3") {
2067 dxc.p = {4.9e-3, 9.82e-5, 1.2e-4, 2.6e-4,
2068 2.5e-3, 9.41e-5, 3.9e-4, 1.1e-4};
2069 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2070 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2071 }
else if (level ==
"Ar_3D4!") {
2074 dxc.final = {lvl[
"Ar_2P9"]};
2075 }
else if (level ==
"Ar_3D4") {
2077 dxc.p = {2.29e-3, 0.011, 8.8e-5, 2.53e-6};
2078 dxc.final = {lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P6"], lvl[
"Ar_2P3"]};
2079 }
else if (level ==
"Ar_3D1!!") {
2081 dxc.p = {5.85e-6, 1.2e-4, 5.7e-3, 7.3e-3,
2082 2.e-4, 1.54e-6, 2.08e-5, 6.75e-7};
2083 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2084 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2085 }
else if (level ==
"Ar_2S5") {
2086 dxc.p = {4.9e-3, 0.011, 1.1e-3, 4.6e-4, 3.3e-3, 5.9e-5, 1.2e-4, 3.1e-4};
2087 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2088 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2089 }
else if (level ==
"Ar_2S4") {
2092 dxc.p = {0.077, 2.44e-3, 8.9e-3, 4.6e-3, 2.7e-3,
2093 1.3e-3, 4.5e-4, 2.9e-5, 3.e-5, 1.6e-4};
2104 }
else if (level ==
"Ar_3D1!") {
2106 dxc.p = {3.1e-3, 2.e-3, 0.015, 9.8e-6};
2107 dxc.final = {lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P6"], lvl[
"Ar_2P3"]};
2108 }
else if (level ==
"Ar_3D2") {
2112 dxc.p = {0.27, 1.35e-5, 9.52e-4, 0.011, 4.01e-5,
2113 4.3e-3, 8.96e-4, 4.45e-5, 5.87e-5, 8.77e-4};
2124 }
else if (level ==
"Ar_3S1!!!!") {
2126 dxc.p = {7.51e-6, 4.3e-5, 8.3e-4, 5.01e-5,
2127 2.09e-4, 0.013, 2.2e-3, 3.35e-6};
2128 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2129 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2130 }
else if (level ==
"Ar_3S1!!") {
2132 dxc.p = {1.89e-4, 1.52e-4, 7.21e-4, 3.69e-4,
2133 3.76e-3, 1.72e-4, 5.8e-4, 6.2e-3};
2134 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2135 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2136 }
else if (level ==
"Ar_3S1!!!") {
2138 dxc.p = {7.36e-4, 4.2e-5, 9.3e-5, 0.015};
2139 dxc.final = {lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P6"], lvl[
"Ar_2P3"]};
2140 }
else if (level ==
"Ar_2S3") {
2141 dxc.p = {3.26e-3, 2.22e-3, 0.01, 5.1e-3};
2142 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P7"], lvl[
"Ar_2P4"], lvl[
"Ar_2P2"]};
2143 }
else if (level ==
"Ar_2S2") {
2146 dxc.p = {0.035, 1.76e-3, 2.1e-4, 2.8e-4, 1.39e-3,
2147 3.8e-4, 2.0e-3, 8.9e-3, 3.4e-3, 1.9e-3};
2158 }
else if (level ==
"Ar_3S1!") {
2162 dxc.p = {0.313, 2.05e-5, 8.33e-5, 3.9e-4, 3.96e-4,
2163 4.2e-4, 4.5e-3, 4.84e-5, 7.1e-3, 5.2e-3};
2174 }
else if (level ==
"Ar_4D5") {
2177 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2178 dxc.p = {2.78e-3, 2.8e-4, 8.6e-4, 9.2e-4, 4.6e-4, 1.6e-4, p0};
2179 dxc.final = {lvl[
"Ar_2P10"],
2186 }
else if (level ==
"Ar_3S4") {
2188 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2189 dxc.p = {4.21e-4, 2.e-3, 1.7e-3, 7.2e-4, 3.5e-4,
2190 1.2e-4, 4.2e-6, 3.3e-5, 9.7e-5, p0};
2191 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"],
2192 lvl[
"Ar_2P7"], lvl[
"Ar_2P6"],
2193 lvl[
"Ar_2P5"], lvl[
"Ar_2P4"],
2194 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"],
2196 }
else if (level ==
"Ar_4D2") {
2198 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2199 dxc.p = {1.7e-4, p0};
2200 dxc.final = {lvl[
"Ar_2P7"], -1};
2201 }
else if (level ==
"Ar_4S1!") {
2203 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2204 dxc.p = {1.05e-3, 3.1e-5, 2.5e-5, 4.0e-4, 5.8e-5, 1.2e-4, p0};
2205 dxc.final = {lvl[
"Ar_2P10"],
2212 }
else if (level ==
"Ar_3S2") {
2214 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2215 dxc.p = {2.85e-4, 5.1e-5, 5.3e-5, 1.6e-4, 1.5e-4,
2216 6.0e-4, 2.48e-3, 9.6e-4, 3.59e-4, p0};
2217 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"],
2218 lvl[
"Ar_2P7"], lvl[
"Ar_2P6"],
2219 lvl[
"Ar_2P5"], lvl[
"Ar_2P4"],
2220 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"],
2222 }
else if (level ==
"Ar_5D5") {
2224 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2225 dxc.p = {2.2e-3, 1.1e-4, 7.6e-5, 4.2e-4, 2.4e-4,
2226 2.1e-4, 2.4e-4, 1.2e-4, p0};
2227 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2228 lvl[
"Ar_2P6"], lvl[
"Ar_2P5"], lvl[
"Ar_2P4"],
2229 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"], -1};
2230 }
else if (level ==
"Ar_4S4") {
2232 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2233 dxc.p = {1.9e-4, 1.1e-3, 5.2e-4, 5.1e-4, 9.4e-5, 5.4e-5, p0};
2234 dxc.final = {lvl[
"Ar_2P10"],
2241 }
else if (level ==
"Ar_5D2") {
2243 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2244 dxc.p = {5.9e-5, 9.0e-6, 1.5e-4, 3.1e-5, p0};
2245 dxc.final = {lvl[
"Ar_2P8"], lvl[
"Ar_2P7"], lvl[
"Ar_2P5"], lvl[
"Ar_2P2"],
2247 }
else if (level ==
"Ar_6D5") {
2251 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2252 dxc.p = {1.9e-3, 4.2e-4, 3.e-4, 5.1e-5, 6.6e-5, 1.21e-4, p0};
2253 dxc.final = {lvl[
"Ar_2P10"],
2260 }
else if (level ==
"Ar_5S1!") {
2264 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2265 dxc.p = {7.7e-5, p0};
2266 dxc.final = {lvl[
"Ar_2P5"], -1};
2267 }
else if (level ==
"Ar_4S2") {
2271 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2272 dxc.p = {4.5e-4, 2.e-4, 2.1e-4, 1.2e-4, 1.8e-4, 9.e-4, 3.3e-4, p0};
2273 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"], lvl[
"Ar_2P5"],
2274 lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"], -1};
2275 }
else if (level ==
"Ar_5S4") {
2280 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2281 dxc.p = {3.6e-4, 1.2e-4, 1.5e-4, 1.4e-4, 7.5e-5, p0};
2282 dxc.final = {lvl[
"Ar_2P8"], lvl[
"Ar_2P6"], lvl[
"Ar_2P4"],
2283 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"], -1};
2284 }
else if (level ==
"Ar_6D2") {
2290 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2291 dxc.p = {3.33e-3, p0};
2292 dxc.final = {lvl[
"Ar_2P7"], -1};
2293 }
else if (level ==
"Ar_Higher") {
2298 dxc.type.assign(5, DxcTypeCollNonIon);
2299 dxc.p = {100., 100., 100., 100., 100.};
2300 dxc.final = {lvl[
"Ar_6D5"], lvl[
"Ar_5S1!"], lvl[
"Ar_4S2"], lvl[
"Ar_5S4"],
2303 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n"
2304 <<
" Missing de-excitation data for level " << level
2305 <<
". Program bug!\n";
2308 if (level !=
"Ar_Higher") dxc.type.assign(dxc.p.size(), DxcTypeRad);
2309 m_deexcitations.push_back(std::move(dxc));
2313 std::cout <<
m_className <<
"::ComputeDeexcitationTable:\n";
2314 std::cout <<
" Found " << m_deexcitations.size() <<
" levels "
2315 <<
"with available radiative de-excitation data.\n";
2322 dimer.label =
"Ar_Dimer";
2325 dimer.energy = 14.71;
2326 dimer.osc = dimer.cf = 0.;
2327 dimer.sDoppler = dimer.gPressure = dimer.width = 0.;
2328 lvl[
"Ar_Dimer"] = m_deexcitations.size();
2329 m_deexcitations.push_back(std::move(dimer));
2332 Deexcitation excimer;
2333 excimer.label =
"Ar_Excimer";
2336 excimer.energy = 14.71;
2337 excimer.osc = excimer.cf = 0.;
2338 excimer.sDoppler = excimer.gPressure = excimer.width = 0.;
2339 lvl[
"Ar_Excimer"] = m_deexcitations.size();
2340 m_deexcitations.push_back(std::move(excimer));
2346 constexpr bool useTachibanaData =
false;
2347 constexpr bool useCollMixing =
true;
2348 for (
auto& dxc : m_deexcitations) {
2349 const std::string level = dxc.label;
2350 if (level ==
"Ar_1S5") {
2353 constexpr double k3b = useTachibanaData ? 1.4e-41 : 1.1e-41;
2354 dxc.p.push_back(k3b * nAr * nAr);
2355 dxc.final.push_back(lvl[
"Ar_Excimer"]);
2356 if (useCollMixing) {
2357 constexpr double k2b = useTachibanaData ? 2.3e-24 : 2.1e-24;
2358 dxc.p.push_back(k2b * nAr);
2359 dxc.final.push_back(lvl[
"Ar_1S4"]);
2360 dxc.type.push_back(DxcTypeCollNonIon);
2362 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2363 }
else if (level ==
"Ar_1S3") {
2366 constexpr double k3b = useTachibanaData ? 1.5e-41 : 0.83e-41;
2367 dxc.p.push_back(k3b * nAr * nAr);
2368 dxc.final.push_back(lvl[
"Ar_Excimer"]);
2369 if (useCollMixing) {
2370 constexpr double k2b = useTachibanaData ? 4.3e-24 : 5.3e-24;
2371 dxc.p.push_back(k2b * nAr);
2372 dxc.final.push_back(lvl[
"Ar_1S4"]);
2374 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2376 const std::vector<int> levels4s = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"],
2377 lvl[
"Ar_1S3"], lvl[
"Ar_1S2"]};
2378 if (level ==
"Ar_2P1") {
2383 constexpr double k4s = 1.6e-20;
2384 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2385 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2386 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2387 }
else if (level ==
"Ar_2P2") {
2390 constexpr double k23 = 0.5e-21;
2391 dxc.p.push_back(k23 * nAr);
2392 dxc.final.push_back(lvl[
"Ar_2P3"]);
2397 constexpr double k4s = 5.3e-20;
2398 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2399 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2400 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2401 }
else if (level ==
"Ar_2P3") {
2404 constexpr double k34 = 27.5e-21;
2405 constexpr double k35 = 0.3e-21;
2406 constexpr double k36 = 44.0e-21;
2407 constexpr double k37 = 1.4e-21;
2408 constexpr double k38 = 1.9e-21;
2409 constexpr double k39 = 0.8e-21;
2410 dxc.p.insert(dxc.p.end(), {k34 * nAr, k35 * nAr, k36 * nAr, k37 * nAr,
2411 k38 * nAr, k39 * nAr});
2412 dxc.final.insert(dxc.final.end(),
2413 {lvl[
"Ar_2P4"], lvl[
"Ar_2P5"], lvl[
"Ar_2P6"],
2414 lvl[
"Ar_2P7"], lvl[
"Ar_2P8"], lvl[
"Ar_2P9"]});
2417 constexpr double k4s = 4.7e-20;
2418 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2419 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2420 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2421 }
else if (level ==
"Ar_2P4") {
2424 constexpr double k43 = 23.0e-21;
2425 constexpr double k45 = 0.7e-21;
2426 constexpr double k46 = 4.8e-21;
2427 constexpr double k47 = 3.2e-21;
2428 constexpr double k48 = 1.4e-21;
2429 constexpr double k49 = 3.3e-21;
2430 dxc.p.insert(dxc.p.end(), {k43 * nAr, k45 * nAr, k46 * nAr, k47 * nAr,
2431 k48 * nAr, k49 * nAr});
2432 dxc.final.insert(dxc.final.end(),
2433 {lvl[
"Ar_2P3"], lvl[
"Ar_2P5"], lvl[
"Ar_2P6"],
2434 lvl[
"Ar_2P7"], lvl[
"Ar_2P8"], lvl[
"Ar_2P9"]});
2437 constexpr double k4s = 3.9e-20;
2438 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2439 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2440 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2441 }
else if (level ==
"Ar_2P5") {
2444 constexpr double k54 = 1.7e-21;
2445 constexpr double k56 = 11.3e-21;
2446 constexpr double k58 = 9.5e-21;
2447 dxc.p.insert(dxc.p.end(), {k54 * nAr, k56 * nAr, k58 * nAr});
2448 dxc.final.insert(dxc.final.end(),
2449 {lvl[
"Ar_2P4"], lvl[
"Ar_2P6"], lvl[
"Ar_2P8"]});
2450 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2451 }
else if (level ==
"Ar_2P6") {
2454 constexpr double k67 = 4.1e-21;
2455 constexpr double k68 = 6.0e-21;
2456 constexpr double k69 = 1.0e-21;
2457 dxc.p.insert(dxc.p.end(), {k67 * nAr, k68 * nAr, k69 * nAr});
2458 dxc.final.insert(dxc.final.end(),
2459 {lvl[
"Ar_2P7"], lvl[
"Ar_2P8"], lvl[
"Ar_2P9"]});
2460 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2461 }
else if (level ==
"Ar_2P7") {
2464 constexpr double k76 = 2.5e-21;
2465 constexpr double k78 = 14.3e-21;
2466 constexpr double k79 = 23.3e-21;
2467 dxc.p.insert(dxc.p.end(), {k76 * nAr, k78 * nAr, k79 * nAr});
2468 dxc.final.insert(dxc.final.end(),
2469 {lvl[
"Ar_2P6"], lvl[
"Ar_2P8"], lvl[
"Ar_2P9"]});
2472 constexpr double k4s = 5.5e-20;
2473 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2474 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2475 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2476 }
else if (level ==
"Ar_2P8") {
2479 constexpr double k86 = 0.3e-21;
2480 constexpr double k87 = 0.8e-21;
2481 constexpr double k89 = 18.2e-21;
2482 constexpr double k810 = 1.0e-21;
2483 dxc.p.insert(dxc.p.end(),
2484 {k86 * nAr, k87 * nAr, k89 * nAr, k810 * nAr});
2485 dxc.final.insert(dxc.final.end(), {lvl[
"Ar_2P6"], lvl[
"Ar_2P7"],
2486 lvl[
"Ar_2P9"], lvl[
"Ar_2P10"]});
2489 constexpr double k4s = 3.e-20;
2490 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2491 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2492 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2493 }
else if (level ==
"Ar_2P9") {
2496 constexpr double k98 = 6.8e-21;
2497 constexpr double k910 = 5.1e-21;
2498 dxc.p.insert(dxc.p.end(), {k98 * nAr, k910 * nAr});
2499 dxc.final.insert(dxc.final.end(), {lvl[
"Ar_2P8"], lvl[
"Ar_2P10"]});
2502 constexpr double k4s = 3.5e-20;
2503 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2504 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2505 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2506 }
else if (level ==
"Ar_2P10") {
2509 constexpr double k4s = 2.0e-20;
2510 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2511 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2512 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2514 const std::vector<int> levels4p = {
2515 lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2516 lvl[
"Ar_2P6"], lvl[
"Ar_2P5"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"],
2517 lvl[
"Ar_2P2"], lvl[
"Ar_2P1"]};
2518 if (level ==
"Ar_3D6" || level ==
"Ar_3D5" || level ==
"Ar_3D3" ||
2519 level ==
"Ar_3D4!" || level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2520 level ==
"Ar_3D1!" || level ==
"Ar_3D2" || level ==
"Ar_3S1!!!!" ||
2521 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!" || level ==
"Ar_3S1!" ||
2522 level ==
"Ar_2S5" || level ==
"Ar_2S4" || level ==
"Ar_2S3" ||
2523 level ==
"Ar_2S2") {
2527 constexpr double k4p = 1.e-20;
2528 dxc.p.resize(dxc.p.size() + levels4p.size(), 0.1 * k4p * nAr);
2529 dxc.final.insert(dxc.final.end(), levels4p.begin(), levels4p.end());
2530 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2531 }
else if (level ==
"Ar_4D5" || level ==
"Ar_3S4" || level ==
"Ar_4D2" ||
2532 level ==
"Ar_4S1!" || level ==
"Ar_3S2" || level ==
"Ar_5D5" ||
2533 level ==
"Ar_4S4" || level ==
"Ar_5D2" || level ==
"Ar_6D5" ||
2534 level ==
"Ar_5S1!" || level ==
"Ar_4S2" || level ==
"Ar_5S4" ||
2535 level ==
"Ar_6D2") {
2538 constexpr double k4p = 1.e-20;
2539 dxc.p.resize(dxc.p.size() + levels4p.size(), 0.1 * k4p * nAr);
2540 dxc.final.insert(dxc.final.end(), levels4p.begin(), levels4p.end());
2541 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2546 constexpr double kHM = 2.e-18;
2547 constexpr bool useHornbeckMolnar =
true;
2548 if (useHornbeckMolnar) {
2549 dxc.p.push_back(kHM * nAr);
2550 dxc.final.push_back(lvl[
"Ar_Dimer"]);
2551 dxc.type.push_back(DxcTypeCollIon);
2565 if (
m_gas[i] ==
"CO2")
2567 else if (
m_gas[i] ==
"CH4")
2569 else if (
m_gas[i] ==
"C2H6")
2571 else if (
m_gas[i] ==
"C2H2")
2573 else if (
m_gas[i] ==
"CF4")
2575 else if (
m_gas[i] ==
"iC4H10")
2580 constexpr double rAr3d = 436.e-10;
2581 constexpr double rAr5s = 635.e-10;
2583 if (iAr >= 0 && iCO2 >= 0) {
2587 constexpr double rCO2 = 165.e-10;
2588 for (
auto& dxc : m_deexcitations) {
2589 std::string level = dxc.label;
2591 double pacs = 0., eta = 0.;
2592 optData.GetPhotoabsorptionCrossSection(
"CO2", dxc.energy, pacs, eta);
2593 const double pPenningWK =
pow(eta, 0.4);
2594 if (level ==
"Ar_1S5") {
2596 constexpr double kQ = 5.3e-19;
2597 dxc.p.push_back(kQ * nQ);
2598 dxc.type.push_back(DxcTypeCollNonIon);
2599 }
else if (level ==
"Ar_1S4") {
2601 constexpr double kQ = 5.0e-19;
2602 dxc.p.push_back(kQ * nQ);
2603 dxc.type.push_back(DxcTypeCollNonIon);
2604 }
else if (level ==
"Ar_1S3") {
2605 constexpr double kQ = 5.9e-19;
2606 dxc.p.push_back(kQ * nQ);
2607 dxc.type.push_back(DxcTypeCollNonIon);
2608 }
else if (level ==
"Ar_1S2") {
2609 constexpr double kQ = 7.4e-19;
2610 dxc.p.push_back(kQ * nQ);
2611 dxc.type.push_back(DxcTypeCollNonIon);
2612 }
else if (level ==
"Ar_2P8") {
2614 constexpr double kQ = 6.4e-19;
2615 dxc.p.push_back(kQ * nQ);
2616 dxc.type.push_back(DxcTypeCollNonIon);
2617 }
else if (level ==
"Ar_2P6") {
2619 constexpr double kQ = 6.1e-19;
2620 dxc.p.push_back(kQ * nQ);
2621 dxc.type.push_back(DxcTypeCollNonIon);
2622 }
else if (level ==
"Ar_2P5") {
2624 constexpr double kQ = 6.6e-19;
2625 dxc.p.push_back(kQ * nQ);
2626 dxc.type.push_back(DxcTypeCollNonIon);
2627 }
else if (level ==
"Ar_2P1") {
2629 constexpr double kQ = 6.2e-19;
2630 dxc.p.push_back(kQ * nQ);
2631 dxc.type.push_back(DxcTypeCollNonIon);
2632 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2633 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2635 constexpr double kQ = 6.33e-19;
2636 dxc.p.push_back(kQ * nQ);
2637 dxc.type.push_back(DxcTypeCollNonIon);
2638 }
else if (dxc.osc > 0.) {
2641 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iCO2);
2642 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2643 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2644 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2645 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2646 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2648 const double kQ = RateConstantHardSphere(rAr3d, rCO2, iAr, iCO2);
2649 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2650 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2652 const double kQ = RateConstantHardSphere(rAr5s, rCO2, iAr, iCO2);
2653 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2655 dxc.final.resize(dxc.p.size(), -1);
2658 if (iAr >= 0 && iCH4 >= 0) {
2662 constexpr double rCH4 = 190.e-10;
2663 for (
auto& dxc : m_deexcitations) {
2664 std::string level = dxc.label;
2666 double pacs = 0., eta = 0.;
2667 optData.GetPhotoabsorptionCrossSection(
"CH4", dxc.energy, pacs, eta);
2668 const double pPenningWK =
pow(eta, 0.4);
2669 if (level ==
"Ar_1S5") {
2671 constexpr double kQ = 4.55e-19;
2672 dxc.p.push_back(kQ * nQ);
2673 dxc.type.push_back(DxcTypeCollNonIon);
2674 }
else if (level ==
"Ar_1S4") {
2676 constexpr double kQ = 4.5e-19;
2677 dxc.p.push_back(kQ * nQ);
2678 dxc.type.push_back(DxcTypeCollNonIon);
2679 }
else if (level ==
"Ar_1S3") {
2681 constexpr double kQ = 5.30e-19;
2682 dxc.p.push_back(kQ * nQ);
2683 dxc.type.push_back(DxcTypeCollNonIon);
2684 }
else if (level ==
"Ar_1S2") {
2686 constexpr double kQ = 5.7e-19;
2687 dxc.p.push_back(kQ * nQ);
2688 dxc.type.push_back(DxcTypeCollNonIon);
2689 }
else if (level ==
"Ar_2P8") {
2691 constexpr double kQ = 7.4e-19;
2692 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2693 }
else if (level ==
"Ar_2P6") {
2694 constexpr double kQ = 3.4e-19;
2695 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2696 }
else if (level ==
"Ar_2P5") {
2697 constexpr double kQ = 6.0e-19;
2698 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2699 }
else if (level ==
"Ar_2P1") {
2700 constexpr double kQ = 9.3e-19;
2701 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2702 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2703 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2705 constexpr double kQ = 6.53e-19;
2706 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2707 }
else if (dxc.osc > 0.) {
2710 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iCH4);
2711 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2712 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2713 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2714 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2715 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2717 const double kQ = RateConstantHardSphere(rAr3d, rCH4, iAr, iCH4);
2718 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2719 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2721 const double kQ = RateConstantHardSphere(rAr5s, rCH4, iAr, iCH4);
2722 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2724 dxc.final.resize(dxc.p.size(), -1);
2727 if (iAr >= 0 && iC2H6 >= 0) {
2731 constexpr double rC2H6 = 195.e-10;
2732 for (
auto& dxc : m_deexcitations) {
2733 std::string level = dxc.label;
2735 double pacs = 0., eta = 0.;
2736 optData.GetPhotoabsorptionCrossSection(
"C2H6", dxc.energy, pacs, eta);
2737 const double pPenningWK =
pow(eta, 0.4);
2738 if (level ==
"Ar_1S5") {
2740 constexpr double kQ = 5.29e-19;
2741 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2742 }
else if (level ==
"Ar_1S4") {
2744 constexpr double kQ = 6.2e-19;
2745 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2746 }
else if (level ==
"Ar_1S3") {
2748 constexpr double kQ = 6.53e-19;
2749 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2750 }
else if (level ==
"Ar_1S2") {
2752 constexpr double kQ = 10.7e-19;
2753 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2754 }
else if (level ==
"Ar_2P8") {
2756 constexpr double kQ = 9.2e-19;
2757 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2758 }
else if (level ==
"Ar_2P6") {
2759 constexpr double kQ = 4.8e-19;
2760 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2761 }
else if (level ==
"Ar_2P5") {
2762 constexpr double kQ = 9.9e-19;
2763 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2764 }
else if (level ==
"Ar_2P1") {
2765 constexpr double kQ = 11.0e-19;
2766 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2767 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2768 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2770 constexpr double kQ = 8.7e-19;
2771 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2772 }
else if (dxc.osc > 0.) {
2775 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iC2H6);
2776 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2777 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2778 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2779 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2780 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2782 const double kQ = RateConstantHardSphere(rAr3d, rC2H6, iAr, iC2H6);
2783 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2784 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2786 const double kQ = RateConstantHardSphere(rAr5s, rC2H6, iAr, iC2H6);
2787 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2789 dxc.final.resize(dxc.p.size(), -1);
2792 if (iAr >= 0 && iIso >= 0) {
2796 constexpr double rIso = 250.e-10;
2800 constexpr double r4p = 340.;
2802 constexpr double fr = (r4p + 250.) / (r4p + 195.);
2804 constexpr double mAr = 39.9;
2805 constexpr double mEth = 30.1;
2806 constexpr double mIso = 58.1;
2809 fr * fr *
sqrt((mEth / mIso) * (mAr + mIso) / (mAr + mEth));
2810 for (
auto& dxc : m_deexcitations) {
2811 std::string level = dxc.label;
2813 double pacs = 0., eta = 0.;
2815 optData.GetPhotoabsorptionCrossSection(
"nC4H10", dxc.energy, pacs, eta);
2816 const double pPenningWK =
pow(eta, 0.4);
2817 if (level ==
"Ar_1S5") {
2820 constexpr double kQ = 7.1e-19;
2821 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2822 }
else if (level ==
"Ar_1S4") {
2824 constexpr double kQ = 6.1e-19;
2825 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2826 }
else if (level ==
"Ar_1S3") {
2829 constexpr double kQ = 8.5e-19;
2830 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2831 }
else if (level ==
"Ar_1S2") {
2833 constexpr double kQ = 11.0e-19;
2834 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2835 }
else if (level ==
"Ar_2P8") {
2836 constexpr double kEth = 9.2e-19;
2837 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2838 }
else if (level ==
"Ar_2P6") {
2839 constexpr double kEth = 4.8e-19;
2840 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2841 }
else if (level ==
"Ar_2P5") {
2842 const double kEth = 9.9e-19;
2843 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2844 }
else if (level ==
"Ar_2P1") {
2845 const double kEth = 11.0e-19;
2846 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2847 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2848 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2849 constexpr double kEth = 5.5e-19;
2850 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2851 }
else if (dxc.osc > 0.) {
2854 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iIso);
2855 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2856 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2857 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2858 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2859 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2861 const double kQ = RateConstantHardSphere(rAr3d, rIso, iAr, iIso);
2862 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2863 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2865 const double kQ = RateConstantHardSphere(rAr5s, rIso, iAr, iIso);
2866 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2868 dxc.final.resize(dxc.p.size(), -1);
2871 if (iAr >= 0 && iC2H2 >= 0) {
2875 constexpr double rC2H2 = 165.e-10;
2876 for (
auto& dxc : m_deexcitations) {
2877 std::string level = dxc.label;
2879 double pacs = 0., eta = 0.;
2880 optData.GetPhotoabsorptionCrossSection(
"C2H2", dxc.energy, pacs, eta);
2881 const double pPenningWK =
pow(eta, 0.4);
2882 if (level ==
"Ar_1S5") {
2884 constexpr double kQ = 5.6e-19;
2888 AddPenningDeexcitation(dxc, kQ * nQ, 0.61);
2889 }
else if (level ==
"Ar_1S4") {
2891 constexpr double kQ = 4.6e-19;
2892 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2893 }
else if (level ==
"Ar_1S3") {
2894 constexpr double kQ = 5.6e-19;
2895 AddPenningDeexcitation(dxc, kQ * nQ, 0.61);
2896 }
else if (level ==
"Ar_1S2") {
2898 constexpr double kQ = 8.7e-19;
2899 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2900 }
else if (level ==
"Ar_2P8") {
2902 constexpr double kQ = 5.0e-19;
2903 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2904 }
else if (level ==
"Ar_2P6") {
2905 constexpr double kQ = 5.7e-19;
2906 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2907 }
else if (level ==
"Ar_2P5") {
2908 constexpr double kQ = 6.0e-19;
2909 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2910 }
else if (level ==
"Ar_2P1") {
2911 constexpr double kQ = 5.3e-19;
2912 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2913 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2914 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2916 constexpr double kQ = 5.5e-19;
2917 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2918 }
else if (dxc.osc > 0.) {
2921 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iC2H2);
2922 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2923 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2924 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2925 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2926 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2928 const double kQ = RateConstantHardSphere(rAr3d, rC2H2, iAr, iC2H2);
2929 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2930 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2932 const double kQ = RateConstantHardSphere(rAr5s, rC2H2, iAr, iC2H2);
2933 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2935 dxc.final.resize(dxc.p.size(), -1);
2938 if (iAr >= 0 && iCF4 >= 0) {
2942 constexpr double rCF4 = 235.e-10;
2943 for (
auto& dxc : m_deexcitations) {
2944 std::string level = dxc.label;
2946 double pacs = 0., eta = 0.;
2947 optData.GetPhotoabsorptionCrossSection(
"CF4", dxc.energy, pacs, eta);
2948 if (level ==
"Ar_1S5") {
2950 constexpr double kQ = 0.33e-19;
2951 dxc.p.push_back(kQ * nQ);
2952 }
else if (level ==
"Ar_1S3") {
2954 constexpr double kQ = 0.26e-19;
2955 dxc.p.push_back(kQ * nQ);
2956 }
else if (level ==
"Ar_2P8") {
2958 constexpr double kQ = 1.7e-19;
2959 dxc.p.push_back(kQ * nQ);
2960 }
else if (level ==
"Ar_2P6") {
2961 constexpr double kQ = 1.7e-19;
2962 dxc.p.push_back(kQ * nQ);
2963 }
else if (level ==
"Ar_2P5") {
2964 constexpr double kQ = 1.6e-19;
2965 dxc.p.push_back(kQ * nQ);
2966 }
else if (level ==
"Ar_2P1") {
2967 constexpr double kQ = 2.2e-19;
2968 dxc.p.push_back(kQ * nQ);
2969 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2970 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2972 constexpr double kQ = 1.8e-19;
2973 dxc.p.push_back(kQ * nQ);
2974 }
else if (dxc.osc > 0.) {
2977 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iCF4);
2978 dxc.p.push_back(kQ * nQ);
2979 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2980 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2981 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2982 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2984 const double kQ = RateConstantHardSphere(rAr3d, rCF4, iAr, iCF4);
2985 dxc.p.push_back(kQ * nQ);
2986 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2988 const double kQ = RateConstantHardSphere(rAr5s, rCF4, iAr, iCF4);
2989 dxc.p.push_back(kQ * nQ);
2991 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2992 dxc.final.resize(dxc.p.size(), -1);
2996 if ((
m_debug || verbose) && nDeexcitations > 0) {
2997 std::cout <<
m_className <<
"::ComputeDeexcitationTable:\n"
2998 <<
" Level Energy [eV] Lifetimes [ns]\n"
2999 <<
" Total Radiative "
3002 <<
" Ionisation Transfer Loss\n";
3005 for (
auto& dxc : m_deexcitations) {
3009 double fCollIon = 0., fCollTransfer = 0., fCollLoss = 0.;
3010 const unsigned int nChannels = dxc.type.size();
3011 for (
unsigned int j = 0; j < nChannels; ++j) {
3012 dxc.rate += dxc.p[j];
3013 if (dxc.type[j] == DxcTypeRad) {
3015 }
else if (dxc.type[j] == DxcTypeCollIon) {
3016 fCollIon += dxc.p[j];
3017 }
else if (dxc.type[j] == DxcTypeCollNonIon) {
3018 if (dxc.final[j] < 0) {
3019 fCollLoss += dxc.p[j];
3021 fCollTransfer += dxc.p[j];
3024 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n "
3025 <<
"Unknown type of deexcitation channel (level " << dxc.label
3026 <<
"). Program bug!\n";
3029 if (dxc.rate > 0.) {
3032 std::cout << std::setw(12) << dxc.label <<
" " << std::fixed
3033 << std::setprecision(3) << std::setw(7) << dxc.energy <<
" "
3034 << std::setw(10) << 1. / dxc.rate <<
" ";
3036 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
3037 << 1. / fRad <<
" ";
3039 std::cout <<
"---------- ";
3041 if (fCollIon > 0.) {
3042 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
3043 << 1. / fCollIon <<
" ";
3045 std::cout <<
"---------- ";
3047 if (fCollTransfer > 0.) {
3048 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
3049 << 1. / fCollTransfer <<
" ";
3051 std::cout <<
"---------- ";
3053 if (fCollLoss > 0.) {
3054 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
3055 << 1. / fCollLoss <<
"\n";
3057 std::cout <<
"---------- \n";
3061 for (
unsigned int j = 0; j < nChannels; ++j) {
3062 dxc.p[j] /= dxc.rate;
3063 if (j > 0) dxc.p[j] += dxc.p[j - 1];
3069double MediumMagboltz::RateConstantWK(
const double energy,
const double osc,
3070 const double pacs,
const int igas1,
3071 const int igas2)
const {
3073 const double m1 = ElectronMassGramme / (m_rgas[igas1] - 1.);
3074 const double m2 = ElectronMassGramme / (m_rgas[igas2] - 1.);
3076 double mR = (m1 * m2 / (m1 + m2)) / AtomicMassUnit;
3077 const double uA = (RydbergEnergy / energy) * osc;
3078 const double uQ = (2 * RydbergEnergy / energy) * pacs /
3079 (4 * Pi2 * FineStructureConstant * BohrRadius * BohrRadius);
3083double MediumMagboltz::RateConstantHardSphere(
const double r1,
const double r2,
3085 const int igas2)
const {
3087 const double r = r1 + r2;
3088 const double sigma = r * r * Pi;
3090 const double m1 = ElectronMass / (m_rgas[igas1] - 1.);
3091 const double m2 = ElectronMass / (m_rgas[igas2] - 1.);
3092 const double mR = m1 * m2 / (m1 + m2);
3100 if (!m_useDeexcitation) {
3101 std::cerr <<
m_className <<
"::ComputeDeexcitation: Not enabled.\n";
3108 PrintErrorMixer(
m_className +
"::ComputeDeexcitation");
3114 if (iLevel < 0 || iLevel >= (
int)m_nTerms) {
3115 std::cerr <<
m_className <<
"::ComputeDeexcitation: Index out of range.\n";
3119 iLevel = m_iDeexcitation[iLevel];
3120 if (iLevel < 0 || iLevel >= (
int)m_deexcitations.size()) {
3121 std::cerr <<
m_className <<
"::ComputeDeexcitation:\n"
3122 <<
" Level is not deexcitable.\n";
3126 ComputeDeexcitationInternal(iLevel, fLevel);
3127 if (fLevel >= 0 && fLevel < (
int)m_deexcitations.size()) {
3128 fLevel = m_deexcitations[fLevel].level;
3132void MediumMagboltz::ComputeDeexcitationInternal(
int iLevel,
int& fLevel) {
3133 m_dxcProducts.clear();
3137 while (iLevel >= 0 && iLevel < (
int)m_deexcitations.size()) {
3138 const auto& dxc = m_deexcitations[iLevel];
3139 const int nChannels = dxc.p.size();
3140 if (dxc.rate <= 0. || nChannels <= 0) {
3149 int type = DxcTypeRad;
3151 for (
int j = 0; j < nChannels; ++j) {
3152 if (r <= dxc.p[j]) {
3153 fLevel = dxc.final[j];
3158 if (type == DxcTypeRad) {
3163 photon.type = DxcProdTypePhoton;
3164 photon.energy = dxc.energy;
3167 photon.energy -= m_deexcitations[fLevel].energy;
3168 if (photon.energy < Small) photon.energy = Small;
3169 m_dxcProducts.push_back(std::move(photon));
3174 double delta =
RndmVoigt(0., dxc.sDoppler, dxc.gPressure);
3175 while (photon.energy + delta < Small ||
fabs(delta) >= dxc.width) {
3176 delta =
RndmVoigt(0., dxc.sDoppler, dxc.gPressure);
3178 photon.energy += delta;
3179 m_dxcProducts.push_back(std::move(photon));
3184 }
else if (type == DxcTypeCollIon) {
3189 electron.type = DxcProdTypeElectron;
3190 electron.energy = dxc.energy;
3193 electron.energy -= m_deexcitations[fLevel].energy;
3194 if (electron.energy < Small) electron.energy = Small;
3196 m_dxcProducts.push_back(std::move(electron));
3201 electron.energy -= m_minIonPot;
3202 if (electron.energy < Small) electron.energy = Small;
3204 m_dxcProducts.push_back(std::move(electron));
3209 }
else if (type == DxcTypeCollNonIon) {
3213 std::cerr <<
m_className <<
"::ComputeDeexcitationInternal:\n"
3214 <<
" Unknown deexcitation type (" << type <<
"). Bug!\n";
3222bool MediumMagboltz::ComputePhotonCollisionTable(
const bool verbose) {
3231 m_cfTotGamma.assign(nEnergyStepsGamma, 0.);
3232 m_cfGamma.assign(nEnergyStepsGamma, std::vector<double>());
3233 csTypeGamma.clear();
3237 const double prefactor = dens * SpeedOfLight *
m_fraction[i];
3239 std::string gasname =
m_gas[i];
3240 if (gasname ==
"iC4H10") {
3243 std::cout <<
m_className <<
"::ComputePhotonCollisionTable:\n"
3244 <<
" Photoabsorption cross-section for "
3245 <<
"iC4H10 not available.\n"
3246 <<
" Using n-butane cross-section instead.\n";
3249 if (!data.IsAvailable(gasname))
return false;
3250 csTypeGamma.push_back(i * nCsTypesGamma + PhotonCollisionTypeIonisation);
3251 csTypeGamma.push_back(i * nCsTypesGamma + PhotonCollisionTypeInelastic);
3252 m_nPhotonTerms += 2;
3253 for (
int j = 0; j < nEnergyStepsGamma; ++j) {
3255 data.GetPhotoabsorptionCrossSection(gasname, (j + 0.5) * m_eStepGamma, cs,
3257 m_cfTotGamma[j] += cs * prefactor;
3259 m_cfGamma[j].push_back(cs * prefactor * eta);
3261 m_cfGamma[j].push_back(cs * prefactor * (1. - eta));
3266 if (m_useCsOutput) {
3267 std::ofstream csfile;
3268 csfile.open(
"csgamma.txt", std::ios::out);
3269 for (
int j = 0; j < nEnergyStepsGamma; ++j) {
3270 csfile << (j + 0.5) * m_eStepGamma <<
" ";
3271 for (
unsigned int i = 0; i < m_nPhotonTerms; ++i)
3272 csfile << m_cfGamma[j][i] <<
" ";
3279 for (
int j = 0; j < nEnergyStepsGamma; ++j) {
3280 for (
unsigned int i = 0; i < m_nPhotonTerms; ++i) {
3281 if (i > 0) m_cfGamma[j][i] += m_cfGamma[j][i - 1];
3286 std::cout <<
m_className <<
"::ComputePhotonCollisionTable:\n";
3287 std::cout <<
" Energy [eV] Mean free path [um]\n";
3288 for (
int i = 0; i < 10; ++i) {
3289 const int j = (2 * i + 1) * nEnergyStepsGamma / 20;
3290 const double en = (2 * i + 1) * m_eFinalGamma / 20;
3291 const double imfp = m_cfTotGamma[j] / SpeedOfLight;
3293 printf(
" %10.2f %18.4f\n", en, 1.e4 / imfp);
3295 printf(
" %10.2f ------------\n", en);
3300 if (!m_useDeexcitation)
return true;
3303 constexpr double f2cs =
3304 FineStructureConstant * 2 * Pi2 * HbarC * HbarC / ElectronMass;
3306 int nResonanceLines = 0;
3307 for (
auto& dxc : m_deexcitations) {
3308 if (dxc.osc < Small)
continue;
3309 const double prefactor = dens * SpeedOfLight *
m_fraction[dxc.gas];
3310 dxc.cf = prefactor * f2cs * dxc.osc;
3312 const double mgas = ElectronMass / (m_rgas[dxc.gas] - 1.);
3314 dxc.sDoppler = wDoppler * dxc.energy;
3318 const double kResBroad = 1.92 * Pi *
sqrt(1. / 3.);
3319 dxc.gPressure = kResBroad * FineStructureConstant *
pow(HbarC, 3) *
3321 (ElectronMass * dxc.energy);
3324 constexpr double nWidths = 1000.;
3328 const double fwhmGauss = dxc.sDoppler *
sqrt(2. * log(2.));
3329 const double fwhmLorentz = dxc.gPressure;
3330 const double fwhmVoigt =
3331 0.5 * (1.0692 * fwhmLorentz +
sqrt(0.86639 * fwhmLorentz * fwhmLorentz +
3332 4 * fwhmGauss * fwhmGauss));
3333 dxc.width = nWidths * fwhmVoigt;
3337 if (nResonanceLines <= 0) {
3338 std::cerr <<
m_className <<
"::ComputePhotonCollisionTable:\n"
3339 <<
" No resonance lines found.\n";
3343 if (!(
m_debug || verbose))
return true;
3344 std::cout <<
m_className <<
"::ComputePhotonCollisionTable:\n "
3345 <<
"Discrete absorption lines:\n Energy [eV] "
3346 <<
"Line width (FWHM) [eV] Mean free path [um]\n "
3347 <<
" Doppler Pressure (peak)\n";
3348 for (
const auto& dxc : m_deexcitations) {
3349 if (dxc.osc < Small)
continue;
3350 const double wp = 2 * dxc.gPressure;
3351 const double wd = 2 *
sqrt(2 * log(2.)) * dxc.sDoppler;
3352 const double imfpP =
3353 (dxc.cf / SpeedOfLight) * TMath::Voigt(0., dxc.sDoppler, wp);
3355 printf(
" %6.3f +/- %6.1e %6.2e %6.3e %14.4f\n", dxc.energy,
3356 dxc.width, wd, wp, 1.e4 / imfpP);
3358 printf(
" %6.3f +/- %6.1e %6.2e %6.3e -------------\n", dxc.energy,
3366 const double e,
const double bmag,
const double btheta,
const int ncoll,
3367 bool verbose,
double& vx,
double& vy,
double& vz,
double& dl,
double& dt,
3368 double& alpha,
double& eta,
double& lor,
double& vxerr,
double& vyerr,
3369 double& vzerr,
double& dlerr,
double& dterr,
double& alphaerr,
3370 double& etaerr,
double& lorerr,
double& alphatof,
3371 std::array<double, 6>& difftens) {
3375 alpha = eta = alphatof = 0.;
3377 vxerr = vyerr = vzerr = 0.;
3379 alphaerr = etaerr = 0.;
3386 if (m_autoEnergyLimit) {
3407 const int ng = GetGasNumberMagboltz(
m_gas[i]);
3410 <<
" does not have a gas number in Magboltz.\n";
3429 const double vt = sqrt(vx * vx + vy * vy);
3430 const double v2 = (vx * vx + vy * vy + vz * vz);
3431 lor = atan2(vt, vz);
3432 if (vt > 0. && v2 > 0. && fabs(lor) > 0.) {
3433 const double dvx = vx * vxerr;
3434 const double dvy = vy * vyerr;
3435 const double dvz = vz * vzerr;
3436 const double a = vz / vt;
3437 lorerr = sqrt(a * a * (vx * vx * dvx * dvx + vy * vy * dvy * dvy) +
3438 vt * vt * dvz * dvz) /
3472 alphatof = fc1 - sqrt(fc1 * fc1 - fc2);
3475 if (!(
m_debug || verbose))
return;
3476 std::cout <<
m_className <<
"::RunMagboltz: Results:\n";
3477 printf(
" Drift velocity along E: %12.8f cm/ns +/- %5.2f%%\n", vz, vzerr);
3478 printf(
" Drift velocity along Bt: %12.8f cm/ns +/- %5.2f%%\n", vx, vxerr);
3479 printf(
" Drift velocity along ExB: %12.8f cm/ns +/- %5.2f%%\n", vy, vyerr);
3480 printf(
" Lorentz angle: %12.3f degree\n", lor * RadToDegree);
3481 printf(
" Longitudinal diffusion: %12.8f cm1/2 +/- %5.2f%%\n", dl, dlerr);
3482 printf(
" Transverse diffusion: %12.8f cm1/2 +/- %5.2f%%\n", dt, dterr);
3483 printf(
" Townsend coefficient: %12.4f cm-1 +/- %5.2f%%\n", alpha,
3485 printf(
" Attachment coefficient: %12.4f cm-1 +/- %5.2f%%\n", eta,
3487 if (alphatof > 0.) {
3488 printf(
" TOF effective Townsend: %12.4f cm-1 (alpha - eta)\n",
3499 const unsigned int nEfields =
m_eFields.size();
3500 const unsigned int nBfields =
m_bFields.size();
3501 const unsigned int nAngles =
m_bAngles.size();
3507 Init(nEfields, nBfields, nAngles,
m_eLor, 0.);
3508 Init(nEfields, nBfields, nAngles,
m_eAlp, -30.);
3510 Init(nEfields, nBfields, nAngles,
m_eAtt, -30.);
3511 Init(nEfields, nBfields, nAngles, 6,
m_eDifM, 0.);
3517 std::vector<unsigned int> excLevelIndex;
3518 std::vector<unsigned int> ionLevelIndex;
3520 double vx = 0., vy = 0., vz = 0.;
3521 double difl = 0., dift = 0.;
3522 double alpha = 0., eta = 0.;
3524 double vxerr = 0., vyerr = 0., vzerr = 0.;
3525 double diflerr = 0., difterr = 0.;
3526 double alphaerr = 0., etaerr = 0.;
3527 double alphatof = 0.;
3529 std::array<double, 6> difftens;
3532 for (
unsigned int i = 0; i < nEfields; ++i) {
3534 for (
unsigned int j = 0; j < nAngles; ++j) {
3536 for (
unsigned int k = 0; k < nBfields; ++k) {
3538 std::cout <<
m_className <<
"::GenerateGasTable: E = " << e
3539 <<
" V/cm, B = " << b <<
" T, angle: " << a <<
" rad\n";
3540 RunMagboltz(e, b, a, numColl, verbose, vx, vy, vz, difl, dift, alpha,
3541 eta, lor, vxerr, vyerr, vzerr, diflerr, difterr, alphaerr,
3542 etaerr, lorerr, alphatof, difftens);
3549 m_eAlp[j][k][i] = alpha > 0. ? log(alpha) : -30.;
3550 m_eAlp0[j][k][i] = alpha > 0. ? log(alpha) : -30.;
3551 m_eAtt[j][k][i] = eta > 0. ? log(eta) : -30.;
3552 for (
unsigned int l = 0; l < 6; ++l) {
3553 m_eDifM[l][j][k][i] = difftens[l];
3561 if (cstype != 1 && cstype != 3)
continue;
3566 if (!(descr[1] ==
'E' && descr[2] ==
'X') &&
3567 !(descr[0] ==
'E' && descr[1] ==
'X'))
3570 descr =
m_gas[igas] + descr;
3579 excLevelIndex.push_back(il);
3585 ionLevelIndex.push_back(il);
3588 std::cout <<
m_className <<
"::GenerateGasTable: Found "
3592 std::cout <<
" " << exc.label <<
", energy = " << exc.energy
3596 std::cout <<
" " << ion.label <<
", energy = " << ion.energy
3610 for (
unsigned int ie = 0; ie < nExc; ++ie) {
3611 const unsigned int level = excLevelIndex[ie];
3615 for (
unsigned int ii = 0; ii < nIon; ++ii) {
3616 const unsigned int level = ionLevelIndex[ii];
Base class for gas media.
double GetNumberDensity() const override
Get the number density [cm-3].
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.
std::array< double, m_nMaxGases > m_lambdaPenningGas
virtual void DisablePenningTransfer()
Switch the simulation of Penning transfers off globally.
std::string GetGasName(const int gasnumber, const int version) const
virtual bool EnablePenningTransfer(const double r, const double lambda)
std::vector< std::vector< std::vector< double > > > m_eAlp0
std::array< std::string, m_nMaxGases > m_gas
std::array< double, m_nMaxGases > m_fraction
bool EnablePenningTransfer(const double r, const double lambda) override
void SetSplittingFunctionGreenSawada()
Sample the secondary electron energy according to the Green-Sawada model.
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
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.
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()
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 GetElectronCollision(const double e, int &type, int &level, double &e1, double &dx, double &dy, double &dz, std::vector< std::pair< int, double > > &secondaries, int &ndxc, int &band) override
Sample the collision type.
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
unsigned int SetThreshold(const std::vector< std::vector< std::vector< double > > > &tab) const
std::vector< std::vector< std::vector< double > > > m_eAlp
virtual void EnableDrift(const bool on=true)
Switch electron/ion/hole on/off.
virtual void EnablePrimaryIonisation(const bool on=true)
Make the medium ionisable or non-ionisable.
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
struct Garfield::Magboltz::@8 scrip_
struct Garfield::Magboltz::@6 dens_
constexpr unsigned int nMaxInelasticTerms
constexpr unsigned int nEnergySteps
struct Garfield::Magboltz::@13 velerr_
constexpr unsigned int nMaxAttachmentTerms
constexpr unsigned int nCharDescr
constexpr unsigned int nMaxIonisationTerms
struct Garfield::Magboltz::@19 ctwner_
struct Garfield::Magboltz::@7 outpt_
struct Garfield::Magboltz::@11 ratio_
struct Garfield::Magboltz::@0 bfld_
struct Garfield::Magboltz::@2 setp_
void gasmix_(long long *ngs, double *q, double *qin, long long *nin, double *e, double *ei, char *name, double *virl, double *eb, double *peqel, double *peqin, double *penfra, long long *kel, long long *kin, double *qion, double *peqion, double *eion, long long *nion, double *qatt, long long *natt, double *qnull, long long *nnull, double *scln, long long *nc0, double *ec0, double *wk, double *efl, long long *ng1, double *eg1, long long *ng2, double *eg2, char scrpt[nMaxLevelsPerComponent][nCharDescr], char scrptn[nMaxNullTerms][nCharDescr])
struct Garfield::Magboltz::@12 vel_
constexpr unsigned int nMaxLevels
struct Garfield::Magboltz::@10 gasn_
struct Garfield::Magboltz::@1 inpt_
constexpr unsigned int nMaxNullTerms
struct Garfield::Magboltz::@5 mix2_
struct Garfield::Magboltz::@3 thrm_
struct Garfield::Magboltz::@17 diferl_
double cf[nMaxLevels][nEnergySteps]
struct Garfield::Magboltz::@14 diflab_
struct Garfield::Magboltz::@4 cnsts_
struct Garfield::Magboltz::@20 tofout_
constexpr unsigned int nMaxLevelsPerComponent
struct Garfield::Magboltz::@9 large_
struct Garfield::Magboltz::@18 ctowns_
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
double RndmVoigt(const double mu, const double sigma, const double gamma)
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
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)