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],
31std::string GetDescription(
const unsigned int i1,
const unsigned int i2,
33 return std::string(scrpt[i1][i2],
40const int MediumMagboltz::DxcTypeRad = 0;
41const int MediumMagboltz::DxcTypeCollIon = 1;
42const int MediumMagboltz::DxcTypeCollNonIon = -1;
47 m_eStep(m_eMax / Magboltz::nEnergySteps),
49 m_eHighLog(log(m_eHigh)),
52 m_eStepGamma(m_eFinalGamma / nEnergyStepsGamma) {
82 m_cfTotLog.assign(nEnergyStepsLog, 0.);
85 m_cfLog.assign(nEnergyStepsLog,
99 std::cerr <<
m_className <<
"::SetMaxElectronEnergy: Invalid energy.\n";
104 std::lock_guard<std::mutex> guard(m_mutex);
116 std::cerr <<
m_className <<
"::SetMaxPhotonEnergy: Invalid energy.\n";
122 m_eStepGamma = m_eFinalGamma / nEnergyStepsGamma;
131 m_useOpalBeaty =
true;
132 m_useGreenSawada =
false;
136 m_useOpalBeaty =
false;
137 m_useGreenSawada =
true;
142 if (!m_hasGreenSawada[i]) {
144 std::cout <<
m_className <<
"::SetSplittingFunctionGreenSawada:\n";
147 std::cout <<
" Fit parameters for " <<
m_gas[i] <<
" not available.\n"
148 <<
" Using Opal-Beaty formula instead.\n";
154 m_useOpalBeaty =
false;
155 m_useGreenSawada =
false;
160 std::cout <<
m_className <<
"::EnableDeexcitation:\n"
161 <<
" Penning transfer will be switched off.\n";
169 m_useDeexcitation =
true;
171 m_dxcProducts.clear();
176 if (!m_useDeexcitation) {
177 std::cout <<
m_className <<
"::EnableRadiationTrapping:\n "
178 <<
"Radiation trapping is enabled but de-excitation is not.\n";
185 const double lambda) {
190 m_lambdaPenning.fill(0.);
195 PrintErrorMixer(
m_className +
"::EnablePenningTransfer");
200 unsigned int nLevelsFound = 0;
201 for (
unsigned int i = 0; i < m_nTerms; ++i) {
202 if (m_csType[i] % nCsTypes == ElectronCollisionTypeExcitation) {
209 if (nLevelsFound > 0) {
210 std::cout <<
m_className <<
"::EnablePenningTransfer:\n "
211 <<
"Updated Penning transfer parameters for " << nLevelsFound
212 <<
" excitation cross-sections.\n";
214 std::cerr <<
m_className <<
"::EnablePenningTransfer:\n Warning: "
215 <<
"mismatch between number of excitation cross-sections ("
216 << nLevelsFound <<
")\n and number of excitation rates in "
217 <<
"the gas table (" <<
m_excLevels.size() <<
").\n "
218 <<
"The gas table was probably calculated using a different "
219 <<
"version of Magboltz.\n";
222 std::cerr <<
m_className <<
"::EnablePenningTransfer:\n "
223 <<
"No excitation cross-sections in the present energy range.\n";
226 if (m_useDeexcitation) {
227 std::cout <<
m_className <<
"::EnablePenningTransfer:\n "
228 <<
"Deexcitation handling will be switched off.\n";
235 std::string gasname) {
241 if (gasname.empty())
return false;
246 if (
m_gas[i] == gasname) {
255 PrintErrorMixer(
m_className +
"::EnablePenningTransfer");
260 unsigned int nLevelsFound = 0;
261 for (
unsigned int i = 0; i < m_nTerms; ++i) {
262 if (
int(m_csType[i] / nCsTypes) != iGas)
continue;
263 if (m_csType[i] % nCsTypes == ElectronCollisionTypeExcitation) {
270 if (nLevelsFound > 0) {
271 std::cout <<
m_className <<
"::EnablePenningTransfer:\n"
272 <<
" Penning transfer parameters for " << nLevelsFound
273 <<
" excitation levels set to:\n"
277 std::cerr <<
m_className <<
"::EnablePenningTransfer:\n"
278 <<
" Specified gas (" << gasname
279 <<
") has no excitation levels in the present energy range.\n";
290 m_lambdaPenning.fill(0.);
300 if (gasname.empty())
return false;
305 if (
m_gas[i] == gasname) {
311 if (iGas < 0)
return false;
313 unsigned int nLevelsFound = 0;
314 for (
unsigned int i = 0; i < m_nTerms; ++i) {
315 if (
int(m_csType[i] / nCsTypes) == iGas) {
317 m_lambdaPenning[i] = 0.;
319 if (m_csType[i] % nCsTypes == ElectronCollisionTypeExcitation &&
320 m_rPenning[i] > Small) {
326 if (nLevelsFound == 0) {
328 std::cout <<
m_className <<
"::DisablePenningTransfer:\n"
329 <<
" Penning transfer switched off for all excitations.\n";
337 std::cerr <<
m_className <<
"::SetExcitationScaling: Incorrect value.\n";
343 if (gasname.empty()) {
344 std::cerr <<
m_className <<
"::SetExcitationScaling: Unknown gas name.\n";
351 if (
m_gas[i] == gasname) {
359 std::cerr <<
m_className <<
"::SetExcitationScaling:\n"
360 <<
" Specified gas (" << gasname
361 <<
") is not part of the present gas mixture.\n";
372 std::cerr <<
m_className <<
"::Initialise: Nothing changed.\n";
376 if (!Mixer(verbose)) {
391 std::cout <<
" Electron cross-sections:\n";
393 for (
unsigned int i = 0; i < m_nTerms; ++i) {
395 int type = m_csType[i] % nCsTypes;
396 if (igas !=
int(m_csType[i] / nCsTypes)) {
397 igas = int(m_csType[i] / nCsTypes);
398 std::cout <<
" " <<
m_gas[igas] <<
"\n";
402 double e = m_rgas[igas] * m_energyLoss[i];
403 std::cout <<
" Level " << i <<
": " << m_description[i] <<
"\n";
404 std::cout <<
" Type " << type;
405 if (type == ElectronCollisionTypeElastic) {
406 std::cout <<
" (elastic)\n";
407 }
else if (type == ElectronCollisionTypeIonisation) {
408 std::cout <<
" (ionisation). Ionisation threshold: " << e <<
" eV.\n";
409 }
else if (type == ElectronCollisionTypeAttachment) {
410 std::cout <<
" (attachment)\n";
411 }
else if (type == ElectronCollisionTypeInelastic) {
412 std::cout <<
" (inelastic). Energy loss: " << e <<
" eV.\n";
413 }
else if (type == ElectronCollisionTypeExcitation) {
414 std::cout <<
" (excitation). Excitation energy: " << e <<
" eV.\n";
415 }
else if (type == ElectronCollisionTypeSuperelastic) {
416 std::cout <<
" (super-elastic). Energy gain: " << -e <<
" eV.\n";
417 }
else if (type == ElectronCollisionTypeVirtual) {
418 std::cout <<
" (virtual)\n";
420 std::cout <<
" (unknown)\n";
422 if (type == ElectronCollisionTypeExcitation &&
m_usePenning &&
424 std::cout <<
" Penning transfer coefficient: "
425 << m_rPenning[i] <<
"\n";
426 }
else if (type == ElectronCollisionTypeExcitation && m_useDeexcitation) {
427 const int idxc = m_iDeexcitation[i];
428 if (idxc < 0 || idxc >= (
int)m_deexcitations.size()) {
429 std::cout <<
" Deexcitation cascade not implemented.\n";
432 const auto& dxc = m_deexcitations[idxc];
434 std::cout <<
" Oscillator strength: " << dxc.osc <<
"\n";
436 std::cout <<
" Decay channels:\n";
437 const int nChannels = dxc.type.size();
438 for (
int j = 0; j < nChannels; ++j) {
439 if (dxc.type[j] == DxcTypeRad) {
440 std::cout <<
" Radiative decay to ";
441 if (dxc.final[j] < 0) {
442 std::cout <<
"ground state: ";
444 std::cout << m_deexcitations[dxc.final[j]].label <<
": ";
446 }
else if (dxc.type[j] == DxcTypeCollIon) {
447 if (dxc.final[j] < 0) {
448 std::cout <<
" Penning ionisation: ";
450 std::cout <<
" Associative ionisation: ";
452 }
else if (dxc.type[j] == DxcTypeCollNonIon) {
453 if (dxc.final[j] >= 0) {
454 std::cout <<
" Collision-induced transition to "
455 << m_deexcitations[dxc.final[j]].label <<
": ";
457 std::cout <<
" Loss: ";
460 const double br = j == 0 ? dxc.p[j] : dxc.p[j] - dxc.p[j - 1];
461 std::cout << std::setprecision(5) << br * 100. <<
"%\n";
471 PrintErrorMixer(
m_className +
"::GetElectronNullCollisionRate");
478 std::cerr <<
m_className <<
"::GetElectronNullCollisionRate: Band > 0.\n";
488 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Invalid energy.\n";
491 if (e > m_eMax && m_useAutoAdjust) {
492 std::cerr <<
m_className <<
"::GetElectronCollisionRate:\n Rate at " << e
493 <<
" eV is not included in the current table.\n "
494 <<
"Increasing energy range to " << 1.05 * e <<
" eV.\n";
501 PrintErrorMixer(
m_className +
"::GetElectronCollisionRate");
508 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Band > 0.\n";
515 const int iE = std::min(std::max(
int(e / m_eStep), 0), iemax);
520 const double eLog = log(e);
521 int iE = int((eLog - m_eHighLog) / m_lnStep);
523 const double fmax = m_cfTotLog[iE];
524 const double fmin = iE == 0 ? log(m_cfTot.back()) : m_cfTotLog[iE - 1];
525 const double emin = m_eHighLog + iE * m_lnStep;
526 const double f = fmin + (eLog - emin) * (fmax - fmin) / m_lnStep;
531 const unsigned int level,
535 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Invalid energy.\n";
540 if (level >= m_nTerms) {
541 std::cerr <<
m_className <<
"::GetElectronCollisionRate: Invalid level.\n";
551 const int iE = std::min(std::max(
int(e / m_eStep), 0), iemax);
555 rate *= m_cf[iE][level] - m_cf[iE][level - 1];
559 const int iE = int((log(e) - m_eHighLog) / m_lnStep);
561 rate *= m_cfLog[iE][0];
563 rate *= m_cfLog[iE][level] - m_cfLog[iE][level - 1];
570 const double e,
int& type,
int& level,
double& e1,
double& dx,
double& dy,
571 double& dz, std::vector<std::pair<int, double> >& secondaries,
int& ndxc,
575 std::cerr <<
m_className <<
"::GetElectronCollision: Invalid energy.\n";
579 if (e > m_eMax && m_useAutoAdjust) {
580 std::cerr <<
m_className <<
"::GetElectronCollision:\n Provided energy ("
581 << e <<
" eV) exceeds current energy range.\n"
582 <<
" Increasing energy range to " << 1.05 * e <<
" eV.\n";
589 PrintErrorMixer(
m_className +
"::GetElectronCollision");
596 std::cerr <<
m_className <<
"::GetElectronCollision: Band > 0.\n";
606 const int iE = std::min(std::max(
int(e / m_eStep), 0), iemax);
610 if (r <= m_cf[iE][0]) {
612 }
else if (r >= m_cf[iE][m_nTerms - 1]) {
613 level = m_nTerms - 1;
615 const auto begin = m_cf[iE].cbegin();
616 level = std::lower_bound(begin, begin + m_nTerms, r) - begin;
619 angCut = m_scatCut[iE][level];
620 angPar = m_scatPar[iE][level];
624 const int iE = std::min(std::max(
int(log(e / m_eHigh) / m_lnStep), 0),
625 nEnergyStepsLog - 1);
628 if (r <= m_cfLog[iE][0]) {
630 }
else if (r >= m_cfLog[iE][m_nTerms - 1]) {
631 level = m_nTerms - 1;
633 const auto begin = m_cfLog[iE].cbegin();
634 level = std::lower_bound(begin, begin + m_nTerms, r) - begin;
637 angCut = m_scatCutLog[iE][level];
638 angPar = m_scatParLog[iE][level];
642 type = m_csType[level] % nCsTypes;
643 const int igas = int(m_csType[level] / nCsTypes);
645 ++m_nCollisions[type];
646 ++m_nCollisionsDetailed[level];
649 double loss = m_energyLoss[level];
651 if (type == ElectronCollisionTypeVirtual)
return true;
653 if (type == ElectronCollisionTypeIonisation) {
657 if (e < loss) loss = e - 0.0001;
658 if (m_useOpalBeaty) {
660 const double w = m_wOpalBeaty[level];
661 esec = w * tan(
RndmUniform() * atan(0.5 * (e - loss) / w));
664 }
else if (m_useGreenSawada) {
665 const double gs = m_parGreenSawada[igas][0];
666 const double gb = m_parGreenSawada[igas][1];
667 const double w = gs * e / (e + gb);
668 const double ts = m_parGreenSawada[igas][2];
669 const double ta = m_parGreenSawada[igas][3];
670 const double tb = m_parGreenSawada[igas][4];
671 const double esec0 = ts - ta / (e + tb);
674 w * tan((r - 1.) * atan(esec0 / w) +
675 r * atan((0.5 * (e - loss) - esec0) / w));
679 if (esec <= 0) esec = Small;
682 secondaries.emplace_back(std::make_pair(IonProdTypeElectron, esec));
684 secondaries.emplace_back(std::make_pair(IonProdTypeIon, 0.));
685 bool fluorescence =
false;
686 if (m_yFluorescence[level] > Small) {
687 if (
RndmUniform() < m_yFluorescence[level]) fluorescence =
true;
691 if (m_nAuger2[level] > 0) {
692 const double eav = m_eAuger2[level] / m_nAuger2[level];
693 for (
unsigned int i = 0; i < m_nAuger2[level]; ++i) {
694 secondaries.emplace_back(std::make_pair(IonProdTypeElectron, eav));
697 if (m_nFluorescence[level] > 0) {
698 const double eav = m_eFluorescence[level] / m_nFluorescence[level];
699 for (
unsigned int i = 0; i < m_nFluorescence[level]; ++i) {
700 secondaries.emplace_back(std::make_pair(IonProdTypeElectron, eav));
703 }
else if (m_nAuger1[level] > 0) {
704 const double eav = m_eAuger1[level] / m_nAuger1[level];
705 for (
unsigned int i = 0; i < m_nAuger1[level]; ++i) {
706 secondaries.emplace_back(std::make_pair(IonProdTypeElectron, eav));
709 }
else if (type == ElectronCollisionTypeExcitation) {
711 if (m_useDeexcitation && m_iDeexcitation[level] >= 0) {
713 ComputeDeexcitationInternal(m_iDeexcitation[level], fLevel);
714 ndxc = m_dxcProducts.size();
716 m_dxcProducts.clear();
722 std::cout <<
m_className <<
"::GetElectronCollision:\n"
723 <<
" Level: " << level <<
"\n"
724 <<
" Ionization potential: " << m_minIonPot <<
"\n"
725 <<
" Excitation energy: " << loss * m_rgas[igas] <<
"\n"
726 <<
" Penning probability: " << m_rPenning[level] <<
"\n";
728 if (loss * m_rgas[igas] > m_minIonPot &&
732 double esec = loss * m_rgas[igas] - m_minIonPot;
733 if (esec <= 0) esec = Small;
738 if (m_lambdaPenning[level] > Small) {
740 newDxcProd.s = m_lambdaPenning[level] * std::cbrt(
RndmUniformPos());
742 newDxcProd.energy = esec;
743 newDxcProd.type = DxcProdTypeElectron;
744 m_dxcProducts.push_back(std::move(newDxcProd));
751 if (e < loss) loss = e - 0.0001;
755 if (m_useAnisotropic) {
756 switch (m_scatModel[level]) {
764 ctheta0 = (ctheta0 + angPar) / (1. + angPar * ctheta0);
767 std::cerr <<
m_className <<
"::GetElectronCollision:\n"
768 <<
" Unknown scattering model.\n"
769 <<
" Using isotropic distribution.\n";
774 const double s1 = m_rgas[igas];
775 const double s2 = (s1 * s1) / (s1 - 1.);
776 const double theta0 = acos(ctheta0);
777 const double arg = std::max(1. - s1 * loss / e, Small);
778 const double d = 1. - ctheta0 * sqrt(arg);
781 e1 = std::max(e * (1. - loss / (s1 * e) - 2. * d / s2), Small);
782 double q = std::min(sqrt((e / e1) * arg) / s1, 1.);
783 const double theta = asin(q * sin(theta0));
784 double ctheta = cos(theta);
786 const double u = (s1 - 1.) * (s1 - 1.) / arg;
787 if (ctheta0 * ctheta0 > u) ctheta = -ctheta;
789 const double stheta = sin(theta);
791 dz = std::min(dz, 1.);
792 const double argZ = sqrt(dx * dx + dy * dy);
796 const double cphi = cos(phi);
797 const double sphi = sin(phi);
803 const double a = stheta / argZ;
804 const double dz1 = dz * ctheta + argZ * stheta * sphi;
805 const double dy1 = dy * ctheta + a * (dx * cphi - dy * dz * sphi);
806 const double dx1 = dx * ctheta - a * (dy * cphi + dx * dz * sphi);
816 double& s,
int& type,
817 double& energy)
const {
818 if (i >= m_dxcProducts.size() || !(m_useDeexcitation ||
m_usePenning)) {
821 t = m_dxcProducts[i].t;
822 s = m_dxcProducts[i].s;
823 type = m_dxcProducts[i].type;
824 energy = m_dxcProducts[i].energy;
830 std::cerr <<
m_className <<
"::GetPhotonCollisionRate: Invalid energy.\n";
831 return m_cfTotGamma[0];
833 if (e > m_eFinalGamma && m_useAutoAdjust) {
834 std::cerr <<
m_className <<
"::GetPhotonCollisionRate:\n Rate at " << e
835 <<
" eV is not included in the current table.\n"
836 <<
" Increasing energy range to " << 1.05 * e <<
" eV.\n";
842 PrintErrorMixer(
m_className +
"::GetPhotonCollisionRate");
849 std::min(std::max(
int(e / m_eStepGamma), 0), nEnergyStepsGamma - 1);
851 double cfSum = m_cfTotGamma[iE];
852 if (m_useDeexcitation && m_useRadTrap && !m_deexcitations.empty()) {
854 for (
const auto& dxc : m_deexcitations) {
855 if (dxc.cf > 0. && fabs(e - dxc.energy) <= dxc.width) {
857 TMath::Voigt(e - dxc.energy, dxc.sDoppler, 2 * dxc.gPressure);
866 double& e1,
double& ctheta,
int& nsec,
869 std::cerr <<
m_className <<
"::GetPhotonCollision: Invalid energy.\n";
872 if (e > m_eFinalGamma && m_useAutoAdjust) {
873 std::cerr <<
m_className <<
"::GetPhotonCollision:\n Provided energy ("
874 << e <<
" eV) exceeds current energy range.\n"
875 <<
" Increasing energy range to " << 1.05 * e <<
" eV.\n";
881 PrintErrorMixer(
m_className +
"::GetPhotonCollision");
889 std::min(std::max(
int(e / m_eStepGamma), 0), nEnergyStepsGamma - 1);
891 double r = m_cfTotGamma[iE];
892 if (m_useDeexcitation && m_useRadTrap && !m_deexcitations.empty()) {
894 std::vector<double> pLine(0);
895 std::vector<int> iLine(0);
897 const unsigned int nDeexcitations = m_deexcitations.size();
898 for (
unsigned int i = 0; i < nDeexcitations; ++i) {
899 const auto& dxc = m_deexcitations[i];
900 if (dxc.cf > 0. && fabs(e - dxc.energy) <= dxc.width) {
902 TMath::Voigt(e - dxc.energy, dxc.sDoppler, 2 * dxc.gPressure);
909 if (nLines > 0 && r >= m_cfTotGamma[iE]) {
911 for (
int i = 0; i < nLines; ++i) {
913 ++m_nPhotonCollisions[PhotonCollisionTypeExcitation];
915 ComputeDeexcitationInternal(iLine[i], fLevel);
916 type = PhotonCollisionTypeExcitation;
917 nsec = m_dxcProducts.size();
921 std::cerr <<
m_className <<
"::GetPhotonCollision:\n";
922 std::cerr <<
" Random sampling of deexcitation line failed.\n";
923 std::cerr <<
" Program bug!\n";
930 if (r <= m_cfGamma[iE][0]) {
932 }
else if (r >= m_cfGamma[iE][m_nPhotonTerms - 1]) {
933 level = m_nPhotonTerms - 1;
935 const auto begin = m_cfGamma[iE].cbegin();
936 level = std::lower_bound(begin, begin + m_nPhotonTerms, r) - begin;
941 type = csTypeGamma[level];
943 type = type % nCsTypesGamma;
944 int ngas = int(csTypeGamma[level] / nCsTypesGamma);
945 ++m_nPhotonCollisions[type];
948 esec = std::max(e - m_ionPot[ngas], Small);
959 m_nCollisions.fill(0);
960 m_nCollisionsDetailed.assign(m_nTerms, 0);
962 m_nPhotonCollisions.fill(0);
966 return std::accumulate(std::begin(m_nCollisions), std::end(m_nCollisions), 0);
970 unsigned int& nElastic,
unsigned int& nIonisation,
971 unsigned int& nAttachment,
unsigned int& nInelastic,
972 unsigned int& nExcitation,
unsigned int& nSuperelastic)
const {
973 nElastic = m_nCollisions[ElectronCollisionTypeElastic];
974 nIonisation = m_nCollisions[ElectronCollisionTypeIonisation];
975 nAttachment = m_nCollisions[ElectronCollisionTypeAttachment];
976 nInelastic = m_nCollisions[ElectronCollisionTypeInelastic];
977 nExcitation = m_nCollisions[ElectronCollisionTypeExcitation];
978 nSuperelastic = m_nCollisions[ElectronCollisionTypeSuperelastic];
979 return nElastic + nIonisation + nAttachment + nInelastic + nExcitation +
986 PrintErrorMixer(
m_className +
"::GetNumberOfLevels");
996 std::string& descr,
double& e) {
1005 if (i >= m_nTerms) {
1006 std::cerr <<
m_className <<
"::GetLevel: Index out of range.\n";
1011 type = m_csType[i] % nCsTypes;
1012 ngas = int(m_csType[i] / nCsTypes);
1014 descr = m_description[i];
1016 e = m_rgas[ngas] * m_energyLoss[i];
1019 <<
" Level " << i <<
": " << descr <<
"\n"
1020 <<
" Type " << type <<
"\n"
1021 <<
" Threshold energy: " << e <<
" eV\n";
1022 if (type == ElectronCollisionTypeExcitation &&
m_usePenning &&
1024 std::cout <<
" Penning transfer coefficient: " << m_rPenning[i]
1026 }
else if (type == ElectronCollisionTypeExcitation && m_useDeexcitation) {
1027 const int idxc = m_iDeexcitation[i];
1028 if (idxc < 0 || idxc >= (
int)m_deexcitations.size()) {
1029 std::cout <<
" Deexcitation cascade not implemented.\n";
1032 const auto& dxc = m_deexcitations[idxc];
1034 std::cout <<
" Oscillator strength: " << dxc.osc <<
"\n";
1036 std::cout <<
" Decay channels:\n";
1037 const int nChannels = dxc.type.size();
1038 for (
int j = 0; j < nChannels; ++j) {
1039 if (dxc.type[j] == DxcTypeRad) {
1040 std::cout <<
" Radiative decay to ";
1041 if (dxc.final[j] < 0) {
1042 std::cout <<
"ground state: ";
1044 std::cout << m_deexcitations[dxc.final[j]].label <<
": ";
1046 }
else if (dxc.type[j] == DxcTypeCollIon) {
1047 if (dxc.final[j] < 0) {
1048 std::cout <<
" Penning ionisation: ";
1050 std::cout <<
" Associative ionisation: ";
1052 }
else if (dxc.type[j] == DxcTypeCollNonIon) {
1053 if (dxc.final[j] >= 0) {
1054 std::cout <<
" Collision-induced transition to "
1055 << m_deexcitations[dxc.final[j]].label <<
": ";
1057 std::cout <<
" Loss: ";
1060 const double br = j == 0 ? dxc.p[j] : dxc.p[j] - dxc.p[j - 1];
1061 std::cout << std::setprecision(5) << br * 100. <<
"%\n";
1070 const unsigned int level)
const {
1071 if (level >= m_nTerms) {
1072 std::cerr <<
m_className <<
"::GetNumberOfElectronCollisions: "
1073 <<
"Level " << level <<
" does not exist.\n";
1076 return m_nCollisionsDetailed[level];
1080 return std::accumulate(std::begin(m_nPhotonCollisions),
1081 std::end(m_nPhotonCollisions), 0);
1085 unsigned int& nElastic,
unsigned int& nIonising,
1086 unsigned int& nInelastic)
const {
1087 nElastic = m_nPhotonCollisions[0];
1088 nIonising = m_nPhotonCollisions[1];
1089 nInelastic = m_nPhotonCollisions[2];
1090 return nElastic + nIonising + nInelastic;
1093int MediumMagboltz::GetGasNumberMagboltz(
const std::string& input)
const {
1095 if (input.empty())
return 0;
1097 if (input ==
"CF4") {
1099 }
else if (input ==
"Ar") {
1101 }
else if (input ==
"He" || input ==
"He-4") {
1104 }
else if (input ==
"He-3") {
1107 }
else if (input ==
"Ne") {
1109 }
else if (input ==
"Kr") {
1111 }
else if (input ==
"Xe") {
1113 }
else if (input ==
"CH4") {
1116 }
else if (input ==
"C2H6") {
1119 }
else if (input ==
"C3H8") {
1122 }
else if (input ==
"iC4H10") {
1125 }
else if (input ==
"CO2") {
1127 }
else if (input ==
"neoC5H12") {
1130 }
else if (input ==
"H2O") {
1132 }
else if (input ==
"O2") {
1134 }
else if (input ==
"N2") {
1136 }
else if (input ==
"NO") {
1139 }
else if (input ==
"N2O") {
1142 }
else if (input ==
"C2H4") {
1145 }
else if (input ==
"C2H2") {
1148 }
else if (input ==
"H2") {
1151 }
else if (input ==
"D2") {
1154 }
else if (input ==
"CO") {
1157 }
else if (input ==
"Methylal") {
1160 }
else if (input ==
"DME") {
1162 }
else if (input ==
"Reid-Step") {
1164 }
else if (input ==
"Maxwell-Model") {
1166 }
else if (input ==
"Reid-Ramp") {
1168 }
else if (input ==
"C2F6") {
1170 }
else if (input ==
"SF6") {
1172 }
else if (input ==
"NH3") {
1174 }
else if (input ==
"C3H6") {
1177 }
else if (input ==
"cC3H6") {
1180 }
else if (input ==
"CH3OH") {
1183 }
else if (input ==
"C2H5OH") {
1186 }
else if (input ==
"C3H7OH") {
1189 }
else if (input ==
"Cs") {
1191 }
else if (input ==
"F2") {
1194 }
else if (input ==
"CS2") {
1196 }
else if (input ==
"COS") {
1198 }
else if (input ==
"CD4") {
1201 }
else if (input ==
"BF3") {
1203 }
else if (input ==
"C2HF5" || input ==
"C2H2F4") {
1205 }
else if (input ==
"TMA") {
1207 }
else if (input ==
"nC3H7OH") {
1210 }
else if (input ==
"paraH2") {
1213 }
else if (input ==
"orthoD2") {
1216 }
else if (input ==
"CHF3") {
1218 }
else if (input ==
"CF3Br") {
1220 }
else if (input ==
"C3F8") {
1222 }
else if (input ==
"O3") {
1225 }
else if (input ==
"Hg") {
1228 }
else if (input ==
"H2S") {
1230 }
else if (input ==
"nC4H10") {
1233 }
else if (input ==
"nC5H12") {
1236 }
else if (input ==
"N2 (Phelps)") {
1238 }
else if (input ==
"GeH4") {
1241 }
else if (input ==
"SiH4") {
1246 std::cerr <<
m_className <<
"::GetGasNumberMagboltz:\n"
1247 <<
" Gas " << input <<
" is not defined.\n";
1251bool MediumMagboltz::Mixer(
const bool verbose) {
1253 std::lock_guard<std::mutex> guard(m_mutex);
1270 const double en = (i + 0.5) * m_eStep;
1280 const double prefactor = dens * SpeedOfLight *
sqrt(2. / ElectronMass);
1287 m_parGreenSawada.fill({1., 0., 0., 0., 0.});
1288 m_hasGreenSawada.fill(
false);
1290 m_wOpalBeaty.fill(1.);
1291 m_energyLoss.fill(0.);
1294 m_yFluorescence.fill(0.);
1299 m_nFluorescence.fill(0);
1300 m_eFluorescence.fill(0.);
1302 m_scatModel.fill(0);
1304 m_rPenning.fill(0.);
1305 m_lambdaPenning.fill(0.);
1307 m_deexcitations.clear();
1308 m_iDeexcitation.fill(-1);
1312 m_cfTotLog.assign(nEnergyStepsLog, 0.);
1316 m_cfLog.assign(nEnergyStepsLog,
1324 m_scatParLog.assign(nEnergyStepsLog,
1326 m_scatCutLog.assign(nEnergyStepsLog,
1358 const int ng = GetGasNumberMagboltz(
m_gas[i]);
1361 <<
" does not have a gas number in Magboltz.\n";
1369 <<
" linear energy steps between 0 and "
1370 << std::min(m_eMax, m_eHigh) <<
" eV.\n";
1371 if (m_eMax > m_eHigh) {
1372 std::cout <<
" " << nEnergyStepsLog <<
" logarithmic steps between "
1373 << m_eHigh <<
" and " << m_eMax <<
" eV\n";
1378 std::ofstream outfile;
1379 if (m_useCsOutput) {
1380 outfile.open(
"cs.txt", std::ios::out);
1381 outfile <<
"# energy [eV] vs. cross-section [cm2]\n";
1392 static std::int64_t nIn = 0;
1394 static std::int64_t nIon = 0;
1396 static std::int64_t nAtt = 0;
1398 static std::int64_t nNull = 0;
1400 static double virial = 0.;
1409 static std::int64_t kEl[6];
1424 std::int64_t ngs = gasNumber[iGas];
1426 &ngs, q[0], qIn[0], &nIn, &e[0], eIn, name, &virial, eoby, pEqEl[0],
1427 pEqIn[0], penFra[0], kEl, kIn, qIon[0], pEqIon[0], eIon, &nIon,
1428 qAtt[0], &nAtt, qNull[0], &nNull, scln, nc0, ec0, wklm, efl,
1429 ng1, eg1, ng2, eg2, scrpt, scrptn,
1432 const double m = (2. / e[1]) * ElectronMass / AtomicMassUnitElectronVolt;
1433 std::cout <<
" " << name <<
"\n"
1434 <<
" mass: " << m <<
" amu\n";
1436 std::cout <<
" ionisation threshold: " << eIon[0] <<
" eV\n";
1438 std::cout <<
" ionisation threshold: " << e[2] <<
" eV\n";
1440 if (e[3] > 0. && e[4] > 0.) {
1441 std::cout <<
" cross-sections at minimum ionising energy:\n"
1442 <<
" excitation: " << e[3] * 1.e18 <<
" Mbarn\n"
1443 <<
" ionisation: " << e[4] * 1.e18 <<
" Mbarn\n";
1454 const double van =
m_fraction[iGas] * prefactor;
1456 if (m_useCsOutput) {
1457 outfile <<
"# cross-sections for " << name <<
"\n";
1458 outfile <<
"# cross-section types:\n";
1459 outfile <<
"# elastic\n";
1463 m_scatModel[np] = kEl[1];
1464 const double r = 1. + 0.5 * e[1];
1466 m_energyLoss[np] = 0.;
1467 m_description[np] = GetDescription(1, scrpt);
1468 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeElastic;
1469 bool withIon =
false;
1472 for (
int j = 0; j < nIon; ++j) {
1473 if (m_eMax < eIon[j])
continue;
1477 m_scatModel[np] = kEl[2];
1478 m_energyLoss[np] = eIon[j] / r;
1479 m_wOpalBeaty[np] = eoby[j];
1480 m_yFluorescence[np] = wklm[j];
1481 m_nAuger1[np] = nc0[j];
1482 m_eAuger1[np] = ec0[j];
1483 m_nFluorescence[np] = ng1[j];
1484 m_eFluorescence[np] = eg1[j];
1485 m_nAuger2[np] = ng2[j];
1486 m_eAuger2[np] = eg2[j];
1487 m_description[np] = GetDescription(2 + j, scrpt);
1488 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeIonisation;
1489 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1491 m_parGreenSawada[iGas][0] = eoby[0];
1492 m_parGreenSawada[iGas][4] = 2 * eIon[0];
1493 m_ionPot[iGas] = eIon[0];
1495 if (m_eMax >= e[2]) {
1499 m_scatModel[np] = kEl[2];
1500 m_energyLoss[np] = e[2] / r;
1501 m_wOpalBeaty[np] = eoby[0];
1502 m_parGreenSawada[iGas][0] = eoby[0];
1503 m_parGreenSawada[iGas][4] = 2 * e[2];
1504 m_ionPot[iGas] = e[2];
1505 m_description[np] = GetDescription(2, scrpt);
1506 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeIonisation;
1507 if (m_useCsOutput) outfile <<
"# ionisation (gross)\n";
1511 for (
int j = 0; j < nAtt; ++j) {
1514 m_scatModel[np] = 0;
1515 m_energyLoss[np] = 0.;
1516 m_description[np] = GetDescription(2 + nIon + j, scrpt);
1517 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeAttachment;
1518 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1521 int nExc = 0, nSuperEl = 0;
1522 for (
int j = 0; j < nIn; ++j) {
1524 m_scatModel[np] = kIn[j];
1525 m_energyLoss[np] = eIn[j] / r;
1526 m_description[np] = GetDescription(4 + nIon + nAtt + j, scrpt);
1527 if ((m_description[np][1] ==
'E' && m_description[np][2] ==
'X') ||
1528 (m_description[np][0] ==
'E' && m_description[np][1] ==
'X') ||
1529 (
m_gas[iGas] ==
"N2" && eIn[j] > 6.)) {
1531 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeExcitation;
1533 }
else if (eIn[j] < 0.) {
1535 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeSuperelastic;
1539 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeInelastic;
1541 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1545 for (
int j = 0; j < nNull; ++j) {
1548 m_scatModel[np] = 0;
1549 m_energyLoss[np] = 0.;
1550 m_description[np] = GetDescription(j, scrptn);
1551 m_csType[np] = nCsTypes * iGas + ElectronCollisionTypeVirtual;
1552 if (m_useCsOutput) outfile <<
"# " << m_description[np] <<
"\n";
1558 if (m_useCsOutput) {
1559 outfile << (iE + 0.5) * m_eStep <<
" " << q[iE][1] <<
" ";
1562 m_cf[iE][np] = q[iE][1] * van;
1563 SetScatteringParameters(m_scatModel[np], pEqEl[iE][1], m_scatCut[iE][np],
1568 for (
int j = 0; j < nIon; ++j) {
1569 if (m_eMax < eIon[j])
continue;
1571 m_cf[iE][np] = qIon[iE][j] * van;
1572 SetScatteringParameters(m_scatModel[np], pEqIon[iE][j],
1573 m_scatCut[iE][np], m_scatPar[iE][np]);
1574 if (m_useCsOutput) outfile << qIon[iE][j] <<
" ";
1578 m_cf[iE][np] = q[iE][2] * van;
1579 SetScatteringParameters(m_scatModel[np], pEqEl[iE][2],
1580 m_scatCut[iE][np], m_scatPar[iE][np]);
1581 if (m_useCsOutput) outfile << q[iE][2] <<
" ";
1585 for (
int j = 0; j < nAtt; ++j) {
1587 m_cf[iE][np] = qAtt[iE][j] * van;
1589 m_scatPar[iE][np] = 0.5;
1590 if (m_useCsOutput) outfile << qAtt[iE][j] <<
" ";
1593 for (
int j = 0; j < nIn; ++j) {
1595 if (m_useCsOutput) outfile << qIn[iE][j] <<
" ";
1596 m_cf[iE][np] = qIn[iE][j] * van;
1598 m_cf[iE][np] *= m_scaleExc[iGas];
1599 if (m_cf[iE][np] < 0.) {
1601 <<
" Negative inelastic cross-section at "
1602 << (iE + 0.5) * m_eStep <<
" eV. Set to zero.\n";
1605 SetScatteringParameters(m_scatModel[np], pEqIn[iE][j],
1606 m_scatCut[iE][np], m_scatPar[iE][np]);
1608 if ((
m_debug || verbose) && nIn > 0 && iE == iemax) {
1609 std::cout <<
" " << nIn <<
" inelastic terms (" << nExc
1610 <<
" excitations, " << nSuperEl <<
" superelastic, "
1611 << nIn - nExc - nSuperEl <<
" other)\n";
1614 for (
int j = 0; j < nNull; ++j) {
1616 m_cf[iE][np] = qNull[iE][j] * van * scln[j];
1617 if (m_useCsOutput) outfile << qNull[iE][j] <<
" ";
1620 if (m_useCsOutput) outfile <<
"\n";
1622 if (m_eMax <= m_eHigh)
continue;
1625 const double rLog =
pow(m_eMax / m_eHigh, 1. / nEnergyStepsLog);
1626 m_lnStep = log(rLog);
1628 double emax = m_eHigh * rLog;
1630 for (
int iE = 0; iE < nEnergyStepsLog; ++iE) {
1636 &ngs, q[0], qIn[0], &nIn, e, eIn, name, &virial, eoby, pEqEl[0],
1637 pEqIn[0], penFra[0], kEl, kIn, qIon[0], pEqIon[0], eIon, &nIon,
1638 qAtt[0], &nAtt, qNull[0], &nNull, scln, nc0, ec0, wklm, efl,
1639 ng1, eg1, ng2, eg2, scrpt, scrptn,
1642 if (m_useCsOutput) outfile << emax <<
" " << q[iemax][1] <<
" ";
1644 m_cfLog[iE][np] = q[iemax][1] * van;
1645 SetScatteringParameters(m_scatModel[np], pEqEl[iemax][1],
1646 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1650 for (
int j = 0; j < nIon; ++j) {
1651 if (m_eMax < eIon[j])
continue;
1653 m_cfLog[iE][np] = qIon[iemax][j] * van;
1654 SetScatteringParameters(m_scatModel[np], pEqIon[iemax][j],
1655 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1656 if (m_useCsOutput) outfile << qIon[iemax][j] <<
" ";
1661 m_cfLog[iE][np] = q[iemax][2] * van;
1664 SetScatteringParameters(m_scatModel[np], pEqEl[iemax][2],
1665 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1666 if (m_useCsOutput) outfile << q[iemax][2] <<
" ";
1670 for (
int j = 0; j < nAtt; ++j) {
1672 m_cfLog[iE][np] = qAtt[iemax][j] * van;
1674 if (m_useCsOutput) outfile << qAtt[iemax][j] <<
" ";
1677 for (
int j = 0; j < nIn; ++j) {
1679 if (m_useCsOutput) outfile << qIn[iemax][j] <<
" ";
1680 m_cfLog[iE][np] = qIn[iemax][j] * van;
1682 m_cfLog[iE][np] *= m_scaleExc[iGas];
1683 if (m_cfLog[iE][np] < 0.) {
1685 <<
" Negative inelastic cross-section at " << emax
1686 <<
" eV. Set to zero.\n";
1687 m_cfLog[iE][np] = 0.;
1689 SetScatteringParameters(m_scatModel[np], pEqIn[iemax][j],
1690 m_scatCutLog[iE][np], m_scatParLog[iE][np]);
1693 for (
int j = 0; j < nNull; ++j) {
1695 m_cfLog[iE][np] = qNull[iemax][j] * van * scln[j];
1696 if (m_useCsOutput) outfile << qNull[iemax][j] <<
" ";
1699 if (m_useCsOutput) outfile <<
"\n";
1704 if (m_useCsOutput) outfile.close();
1707 auto it = std::min_element(std::begin(m_ionPot),
1710 std::string minIonPotGas =
m_gas[std::distance(std::begin(m_ionPot), it)];
1714 <<
" Lowest ionisation threshold in the mixture: "
1715 << m_minIonPot <<
" eV (" << minIonPotGas <<
")\n";
1720 for (
unsigned int k = 0; k < m_nTerms; ++k) {
1721 if (m_cf[iE][k] < 0.) {
1723 <<
" Negative collision rate at " << (iE + 0.5) * m_eStep
1724 <<
" eV, cross-section " << k <<
". Set to zero.\n";
1725 std::cout << m_description[k] <<
"\n";
1728 m_cfTot[iE] += m_cf[iE][k];
1731 if (m_cfTot[iE] > 0.) {
1732 for (
unsigned int k = 0; k < m_nTerms; ++k) m_cf[iE][k] /= m_cfTot[iE];
1734 for (
unsigned int k = 1; k < m_nTerms; ++k) {
1735 m_cf[iE][k] += m_cf[iE][k - 1];
1737 const double ekin = m_eStep * (iE + 0.5);
1738 m_cfTot[iE] *=
sqrt(ekin);
1741 const double re = ekin / ElectronMass;
1742 m_cfTot[iE] *=
sqrt(1. + 0.5 * re) / (1. + re);
1746 if (m_eMax > m_eHigh) {
1747 const double rLog =
pow(m_eMax / m_eHigh, 1. / nEnergyStepsLog);
1748 for (
int iE = 0; iE < nEnergyStepsLog; ++iE) {
1750 for (
unsigned int k = 0; k < m_nTerms; ++k) {
1751 if (m_cfLog[iE][k] < 0.) m_cfLog[iE][k] = 0.;
1752 m_cfTotLog[iE] += m_cfLog[iE][k];
1755 if (m_cfTotLog[iE] > 0.) {
1756 for (
unsigned int k = 0; k < m_nTerms; ++k) {
1757 m_cfLog[iE][k] /= m_cfTotLog[iE];
1760 for (
unsigned int k = 1; k < m_nTerms; ++k) {
1761 m_cfLog[iE][k] += m_cfLog[iE][k - 1];
1763 const double ekin = m_eHigh *
pow(rLog, iE + 1);
1764 const double re = ekin / ElectronMass;
1765 m_cfTotLog[iE] *=
sqrt(ekin) *
sqrt(1. + re) / (1. + re);
1767 m_cfTotLog[iE] = log(m_cfTotLog[iE]);
1774 if (m_cfTot[j] > m_cfNull) m_cfNull = m_cfTot[j];
1776 if (m_eMax > m_eHigh) {
1777 for (
int j = 0; j < nEnergyStepsLog; ++j) {
1778 const double r =
exp(m_cfTotLog[j]);
1779 if (r > m_cfNull) m_cfNull = r;
1784 m_nCollisionsDetailed.assign(m_nTerms, 0);
1785 m_nCollisions.fill(0);
1789 <<
" Energy [eV] Collision Rate [ns-1]\n";
1790 const double emax = std::min(m_eHigh, m_eMax);
1791 for (
int i = 0; i < 8; ++i) {
1792 const double en = (2 * i + 1) * emax / 16;
1794 std::printf(
" %10.2f %18.2f\n", en, cf);
1799 if (m_useDeexcitation) {
1800 ComputeDeexcitationTable(verbose);
1801 for (
const auto& dxc : m_deexcitations) {
1802 if (dxc.p.size() == dxc.final.size() && dxc.p.size() == dxc.type.size())
1805 <<
" Mismatch in deexcitation channel count. Program bug!\n"
1806 <<
" Deexcitation handling is switched off.\n";
1807 m_useDeexcitation =
false;
1813 if (!ComputePhotonCollisionTable(verbose)) {
1815 <<
" Photon collision rates could not be calculated.\n";
1816 if (m_useDeexcitation) {
1817 std::cerr <<
" Deexcitation handling is switched off.\n";
1818 m_useDeexcitation =
false;
1824 std::cout <<
m_className <<
"::Mixer: Resetting transfer probabilities.\n"
1827 std::cout <<
" Component " << i <<
": " <<
m_rPenningGas[i] <<
"\n";
1834 for (
unsigned int i = 0; i < m_nTerms; ++i) {
1835 int iGas = int(m_csType[i] / nCsTypes);
1848void MediumMagboltz::SetupGreenSawada() {
1850 const double ta = 1000.;
1851 const double tb = m_parGreenSawada[i][4];
1852 m_hasGreenSawada[i] =
true;
1853 if (
m_gas[i] ==
"He" ||
m_gas[i] ==
"He-3") {
1854 m_parGreenSawada[i] = {15.5, 24.5, -2.25, ta, tb};
1855 }
else if (
m_gas[i] ==
"Ne") {
1856 m_parGreenSawada[i] = {24.3, 21.6, -6.49, ta, tb};
1857 }
else if (
m_gas[i] ==
"Ar") {
1858 m_parGreenSawada[i] = {6.92, 7.85, 6.87, ta, tb};
1859 }
else if (
m_gas[i] ==
"Kr") {
1860 m_parGreenSawada[i] = {7.95, 13.5, 3.90, ta, tb};
1861 }
else if (
m_gas[i] ==
"Xe") {
1862 m_parGreenSawada[i] = {7.93, 11.5, 3.81, ta, tb};
1863 }
else if (
m_gas[i] ==
"H2" ||
m_gas[i] ==
"D2") {
1864 m_parGreenSawada[i] = {7.07, 7.7, 1.87, ta, tb};
1865 }
else if (
m_gas[i] ==
"N2") {
1866 m_parGreenSawada[i] = {13.8, 15.6, 4.71, ta, tb};
1867 }
else if (
m_gas[i] ==
"O2") {
1868 m_parGreenSawada[i] = {18.5, 12.1, 1.86, ta, tb};
1869 }
else if (
m_gas[i] ==
"CH4") {
1870 m_parGreenSawada[i] = {7.06, 12.5, 3.45, ta, tb};
1871 }
else if (
m_gas[i] ==
"H2O") {
1872 m_parGreenSawada[i] = {12.8, 12.6, 1.28, ta, tb};
1873 }
else if (
m_gas[i] ==
"CO") {
1874 m_parGreenSawada[i] = {13.3, 14.0, 2.03, ta, tb};
1875 }
else if (
m_gas[i] ==
"C2H2") {
1876 m_parGreenSawada[i] = {9.28, 5.8, 1.37, ta, tb};
1877 }
else if (
m_gas[i] ==
"NO") {
1878 m_parGreenSawada[i] = {10.4, 9.5, -4.30, ta, tb};
1879 }
else if (
m_gas[i] ==
"CO2") {
1880 m_parGreenSawada[i] = {12.3, 13.8, -2.46, ta, tb};
1882 m_parGreenSawada[i][3] = 0.;
1883 m_hasGreenSawada[i] =
false;
1884 if (m_useGreenSawada) {
1885 std::cout <<
m_className <<
"::SetupGreenSawada:\n"
1886 <<
" Fit parameters for " <<
m_gas[i]
1887 <<
" not available.\n"
1888 <<
" Opal-Beaty formula is used instead.\n";
1894void MediumMagboltz::SetScatteringParameters(
const int model,
1895 const double parIn,
double& cut,
1896 double& parOut)
const {
1899 if (model <= 0)
return;
1914 constexpr double rads = 2. / Pi;
1915 const double cns = parIn - 0.5;
1916 const double thetac =
asin(2. *
sqrt(cns - cns * cns));
1917 const double fac = (1. -
cos(thetac)) /
pow(
sin(thetac), 2.);
1918 parOut = cns * fac + 0.5;
1919 cut = thetac * rads;
1922void MediumMagboltz::ComputeDeexcitationTable(
const bool verbose) {
1923 m_iDeexcitation.fill(-1);
1924 m_deexcitations.clear();
1927 OpticalData optData;
1933 std::map<std::string, std::string> levelNamesAr = {
1934 {
"1S5 ",
"Ar_1S5"}, {
"1S4 ",
"Ar_1S4"},
1935 {
"1S3 ",
"Ar_1S3"}, {
"1S2 ",
"Ar_1S2"},
1936 {
"2P10 ",
"Ar_2P10"}, {
"2P9 ",
"Ar_2P9"},
1937 {
"2P8 ",
"Ar_2P8"}, {
"2P7 ",
"Ar_2P7"},
1938 {
"2P6 ",
"Ar_2P6"}, {
"2P5 ",
"Ar_2P5"},
1939 {
"2P4 ",
"Ar_2P4"}, {
"2P3 ",
"Ar_2P3"},
1940 {
"2P2 ",
"Ar_2P2"}, {
"2P1 ",
"Ar_2P1"},
1941 {
"3D6 ",
"Ar_3D6"}, {
"3D5 ",
"Ar_3D5"},
1942 {
"3D3 ",
"Ar_3D3"}, {
"3D4! ",
"Ar_3D4!"},
1943 {
"3D4 ",
"Ar_3D4"}, {
"3D1!! ",
"Ar_3D1!!"},
1944 {
"2S5 ",
"Ar_2S5"}, {
"2S4 ",
"Ar_2S4"},
1945 {
"3D1! ",
"Ar_3D1!"}, {
"3D2 ",
"Ar_3D2"},
1946 {
"3S1!!!!",
"Ar_3S1!!!!"}, {
"3S1!! ",
"Ar_3S1!!"},
1947 {
"3S1!!! ",
"Ar_3S1!!!"}, {
"2S3 ",
"Ar_2S3"},
1948 {
"2S2 ",
"Ar_2S2"}, {
"3S1! ",
"Ar_3S1!"},
1949 {
"4D5 ",
"Ar_4D5"}, {
"3S4 ",
"Ar_3S4"},
1950 {
"4D2 ",
"Ar_4D2"}, {
"4S1! ",
"Ar_4S1!"},
1951 {
"3S2 ",
"Ar_3S2"}, {
"5D5 ",
"Ar_5D5"},
1952 {
"4S4 ",
"Ar_4S4"}, {
"5D2 ",
"Ar_5D2"},
1953 {
"6D5 ",
"Ar_6D5"}, {
"5S1! ",
"Ar_5S1!"},
1954 {
"4S2 ",
"Ar_4S2"}, {
"5S4 ",
"Ar_5S4"},
1955 {
"6D2 ",
"Ar_6D2"}, {
"HIGH ",
"Ar_Higher"}};
1957 std::map<std::string, int> mapLevels;
1959 for (
unsigned int i = 0; i < m_nTerms; ++i) {
1961 if (m_csType[i] % nCsTypes != ElectronCollisionTypeExcitation)
continue;
1963 const int ngas = int(m_csType[i] / nCsTypes);
1964 if (
m_gas[ngas] ==
"Ar") {
1966 if (iAr < 0) iAr = ngas;
1968 std::string level =
" ";
1969 for (
int j = 0; j < 7; ++j) level[j] = m_description[i][5 + j];
1970 if (levelNamesAr.find(level) != levelNamesAr.end()) {
1971 mapLevels[levelNamesAr[level]] = i;
1973 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n"
1974 <<
" Unknown Ar excitation level: " << level <<
"\n";
1980 unsigned int nDeexcitations = 0;
1981 std::map<std::string, int> lvl;
1982 for (
auto it = mapLevels.cbegin(), end = mapLevels.cend(); it != end; ++it) {
1983 std::string level = (*it).first;
1984 lvl[level] = nDeexcitations;
1985 m_iDeexcitation[(*it).second] = nDeexcitations;
1990 constexpr double f2A =
1991 2. * SpeedOfLight * FineStructureConstant / (3. * ElectronMass * HbarC);
2001 for (
auto it = mapLevels.cbegin(), end = mapLevels.cend(); it != end; ++it) {
2002 std::string level = (*it).first;
2004 dxc.gas = int(m_csType[(*it).second] / nCsTypes);
2005 dxc.level = (*it).second;
2008 dxc.energy = m_energyLoss[(*it).second] * m_rgas[dxc.gas];
2010 dxc.osc = dxc.cf = 0.;
2011 dxc.sDoppler = dxc.gPressure = dxc.width = 0.;
2012 const std::vector<int> levelsAr4s = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"],
2013 lvl[
"Ar_1S3"], lvl[
"Ar_1S2"]};
2014 if (level ==
"Ar_1S5" || level ==
"Ar_1S3") {
2016 }
else if (level ==
"Ar_1S4") {
2021 }
else if (level ==
"Ar_1S2") {
2026 }
else if (level ==
"Ar_2P10") {
2027 dxc.p = {0.0189, 5.43e-3, 9.8e-4, 1.9e-4};
2028 dxc.final = levelsAr4s;
2029 }
else if (level ==
"Ar_2P9") {
2031 dxc.final = {lvl[
"Ar_1S5"]};
2032 }
else if (level ==
"Ar_2P8") {
2033 dxc.p = {9.28e-3, 0.0215, 1.47e-3};
2034 dxc.final = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"], lvl[
"Ar_1S2"]};
2035 }
else if (level ==
"Ar_2P7") {
2036 dxc.p = {5.18e-3, 0.025, 2.43e-3, 1.06e-3};
2037 dxc.final = levelsAr4s;
2038 }
else if (level ==
"Ar_2P6") {
2039 dxc.p = {0.0245, 4.9e-3, 5.03e-3};
2040 dxc.final = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"], lvl[
"Ar_1S2"]};
2041 }
else if (level ==
"Ar_2P5") {
2043 dxc.final = {lvl[
"Ar_1S4"]};
2044 }
else if (level ==
"Ar_2P4") {
2045 dxc.p = {6.25e-4, 2.2e-5, 0.0186, 0.0139};
2046 dxc.final = levelsAr4s;
2047 }
else if (level ==
"Ar_2P3") {
2048 dxc.p = {3.8e-3, 8.47e-3, 0.0223};
2049 dxc.final = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"], lvl[
"Ar_1S2"]};
2050 }
else if (level ==
"Ar_2P2") {
2051 dxc.p = {6.39e-3, 1.83e-3, 0.0117, 0.0153};
2052 dxc.final = levelsAr4s;
2053 }
else if (level ==
"Ar_2P1") {
2054 dxc.p = {2.36e-4, 0.0445};
2055 dxc.final = {lvl[
"Ar_1S4"], lvl[
"Ar_1S2"]};
2056 }
else if (level ==
"Ar_3D6") {
2058 dxc.p = {8.1e-3, 7.73e-4, 1.2e-4, 3.6e-4};
2059 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P7"], lvl[
"Ar_2P4"], lvl[
"Ar_2P2"]};
2060 }
else if (level ==
"Ar_3D5") {
2064 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2065 dxc.p = {7.4e-3, 3.9e-5, 3.09e-4, 1.37e-3, 5.75e-4,
2066 3.2e-5, 1.4e-4, 1.7e-4, 2.49e-6, p0};
2067 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"],
2068 lvl[
"Ar_2P7"], lvl[
"Ar_2P6"],
2069 lvl[
"Ar_2P5"], lvl[
"Ar_2P4"],
2070 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"],
2072 }
else if (level ==
"Ar_3D3") {
2074 dxc.p = {4.9e-3, 9.82e-5, 1.2e-4, 2.6e-4,
2075 2.5e-3, 9.41e-5, 3.9e-4, 1.1e-4};
2076 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2077 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2078 }
else if (level ==
"Ar_3D4!") {
2081 dxc.final = {lvl[
"Ar_2P9"]};
2082 }
else if (level ==
"Ar_3D4") {
2084 dxc.p = {2.29e-3, 0.011, 8.8e-5, 2.53e-6};
2085 dxc.final = {lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P6"], lvl[
"Ar_2P3"]};
2086 }
else if (level ==
"Ar_3D1!!") {
2088 dxc.p = {5.85e-6, 1.2e-4, 5.7e-3, 7.3e-3,
2089 2.e-4, 1.54e-6, 2.08e-5, 6.75e-7};
2090 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2091 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2092 }
else if (level ==
"Ar_2S5") {
2093 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};
2094 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2095 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2096 }
else if (level ==
"Ar_2S4") {
2099 dxc.p = {0.077, 2.44e-3, 8.9e-3, 4.6e-3, 2.7e-3,
2100 1.3e-3, 4.5e-4, 2.9e-5, 3.e-5, 1.6e-4};
2111 }
else if (level ==
"Ar_3D1!") {
2113 dxc.p = {3.1e-3, 2.e-3, 0.015, 9.8e-6};
2114 dxc.final = {lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P6"], lvl[
"Ar_2P3"]};
2115 }
else if (level ==
"Ar_3D2") {
2119 dxc.p = {0.27, 1.35e-5, 9.52e-4, 0.011, 4.01e-5,
2120 4.3e-3, 8.96e-4, 4.45e-5, 5.87e-5, 8.77e-4};
2131 }
else if (level ==
"Ar_3S1!!!!") {
2133 dxc.p = {7.51e-6, 4.3e-5, 8.3e-4, 5.01e-5,
2134 2.09e-4, 0.013, 2.2e-3, 3.35e-6};
2135 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2136 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2137 }
else if (level ==
"Ar_3S1!!") {
2139 dxc.p = {1.89e-4, 1.52e-4, 7.21e-4, 3.69e-4,
2140 3.76e-3, 1.72e-4, 5.8e-4, 6.2e-3};
2141 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2142 lvl[
"Ar_2P6"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"]};
2143 }
else if (level ==
"Ar_3S1!!!") {
2145 dxc.p = {7.36e-4, 4.2e-5, 9.3e-5, 0.015};
2146 dxc.final = {lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P6"], lvl[
"Ar_2P3"]};
2147 }
else if (level ==
"Ar_2S3") {
2148 dxc.p = {3.26e-3, 2.22e-3, 0.01, 5.1e-3};
2149 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P7"], lvl[
"Ar_2P4"], lvl[
"Ar_2P2"]};
2150 }
else if (level ==
"Ar_2S2") {
2153 dxc.p = {0.035, 1.76e-3, 2.1e-4, 2.8e-4, 1.39e-3,
2154 3.8e-4, 2.0e-3, 8.9e-3, 3.4e-3, 1.9e-3};
2165 }
else if (level ==
"Ar_3S1!") {
2169 dxc.p = {0.313, 2.05e-5, 8.33e-5, 3.9e-4, 3.96e-4,
2170 4.2e-4, 4.5e-3, 4.84e-5, 7.1e-3, 5.2e-3};
2181 }
else if (level ==
"Ar_4D5") {
2184 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2185 dxc.p = {2.78e-3, 2.8e-4, 8.6e-4, 9.2e-4, 4.6e-4, 1.6e-4, p0};
2186 dxc.final = {lvl[
"Ar_2P10"],
2193 }
else if (level ==
"Ar_3S4") {
2195 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2196 dxc.p = {4.21e-4, 2.e-3, 1.7e-3, 7.2e-4, 3.5e-4,
2197 1.2e-4, 4.2e-6, 3.3e-5, 9.7e-5, p0};
2198 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"],
2199 lvl[
"Ar_2P7"], lvl[
"Ar_2P6"],
2200 lvl[
"Ar_2P5"], lvl[
"Ar_2P4"],
2201 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"],
2203 }
else if (level ==
"Ar_4D2") {
2205 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2206 dxc.p = {1.7e-4, p0};
2207 dxc.final = {lvl[
"Ar_2P7"], -1};
2208 }
else if (level ==
"Ar_4S1!") {
2210 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2211 dxc.p = {1.05e-3, 3.1e-5, 2.5e-5, 4.0e-4, 5.8e-5, 1.2e-4, p0};
2212 dxc.final = {lvl[
"Ar_2P10"],
2219 }
else if (level ==
"Ar_3S2") {
2221 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2222 dxc.p = {2.85e-4, 5.1e-5, 5.3e-5, 1.6e-4, 1.5e-4,
2223 6.0e-4, 2.48e-3, 9.6e-4, 3.59e-4, p0};
2224 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"],
2225 lvl[
"Ar_2P7"], lvl[
"Ar_2P6"],
2226 lvl[
"Ar_2P5"], lvl[
"Ar_2P4"],
2227 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"],
2229 }
else if (level ==
"Ar_5D5") {
2231 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2232 dxc.p = {2.2e-3, 1.1e-4, 7.6e-5, 4.2e-4, 2.4e-4,
2233 2.1e-4, 2.4e-4, 1.2e-4, p0};
2234 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2235 lvl[
"Ar_2P6"], lvl[
"Ar_2P5"], lvl[
"Ar_2P4"],
2236 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"], -1};
2237 }
else if (level ==
"Ar_4S4") {
2239 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2240 dxc.p = {1.9e-4, 1.1e-3, 5.2e-4, 5.1e-4, 9.4e-5, 5.4e-5, p0};
2241 dxc.final = {lvl[
"Ar_2P10"],
2248 }
else if (level ==
"Ar_5D2") {
2250 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2251 dxc.p = {5.9e-5, 9.0e-6, 1.5e-4, 3.1e-5, p0};
2252 dxc.final = {lvl[
"Ar_2P8"], lvl[
"Ar_2P7"], lvl[
"Ar_2P5"], lvl[
"Ar_2P2"],
2254 }
else if (level ==
"Ar_6D5") {
2258 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2259 dxc.p = {1.9e-3, 4.2e-4, 3.e-4, 5.1e-5, 6.6e-5, 1.21e-4, p0};
2260 dxc.final = {lvl[
"Ar_2P10"],
2267 }
else if (level ==
"Ar_5S1!") {
2271 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2272 dxc.p = {7.7e-5, p0};
2273 dxc.final = {lvl[
"Ar_2P5"], -1};
2274 }
else if (level ==
"Ar_4S2") {
2278 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2279 dxc.p = {4.5e-4, 2.e-4, 2.1e-4, 1.2e-4, 1.8e-4, 9.e-4, 3.3e-4, p0};
2280 dxc.final = {lvl[
"Ar_2P10"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"], lvl[
"Ar_2P5"],
2281 lvl[
"Ar_2P4"], lvl[
"Ar_2P3"], lvl[
"Ar_2P2"], -1};
2282 }
else if (level ==
"Ar_5S4") {
2287 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2288 dxc.p = {3.6e-4, 1.2e-4, 1.5e-4, 1.4e-4, 7.5e-5, p0};
2289 dxc.final = {lvl[
"Ar_2P8"], lvl[
"Ar_2P6"], lvl[
"Ar_2P4"],
2290 lvl[
"Ar_2P3"], lvl[
"Ar_2P2"], -1};
2291 }
else if (level ==
"Ar_6D2") {
2297 const double p0 = f2A * dxc.energy * dxc.energy * dxc.osc;
2298 dxc.p = {3.33e-3, p0};
2299 dxc.final = {lvl[
"Ar_2P7"], -1};
2300 }
else if (level ==
"Ar_Higher") {
2305 dxc.type.assign(5, DxcTypeCollNonIon);
2306 dxc.p = {100., 100., 100., 100., 100.};
2307 dxc.final = {lvl[
"Ar_6D5"], lvl[
"Ar_5S1!"], lvl[
"Ar_4S2"], lvl[
"Ar_5S4"],
2310 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n"
2311 <<
" Missing de-excitation data for level " << level
2312 <<
". Program bug!\n";
2315 if (level !=
"Ar_Higher") dxc.type.assign(dxc.p.size(), DxcTypeRad);
2316 m_deexcitations.push_back(std::move(dxc));
2320 std::cout <<
m_className <<
"::ComputeDeexcitationTable:\n";
2321 std::cout <<
" Found " << m_deexcitations.size() <<
" levels "
2322 <<
"with available radiative de-excitation data.\n";
2329 dimer.label =
"Ar_Dimer";
2332 dimer.energy = 14.71;
2333 dimer.osc = dimer.cf = 0.;
2334 dimer.sDoppler = dimer.gPressure = dimer.width = 0.;
2335 lvl[
"Ar_Dimer"] = m_deexcitations.size();
2336 m_deexcitations.push_back(std::move(dimer));
2339 Deexcitation excimer;
2340 excimer.label =
"Ar_Excimer";
2343 excimer.energy = 14.71;
2344 excimer.osc = excimer.cf = 0.;
2345 excimer.sDoppler = excimer.gPressure = excimer.width = 0.;
2346 lvl[
"Ar_Excimer"] = m_deexcitations.size();
2347 m_deexcitations.push_back(std::move(excimer));
2353 constexpr bool useTachibanaData =
false;
2354 constexpr bool useCollMixing =
true;
2355 for (
auto& dxc : m_deexcitations) {
2356 const std::string level = dxc.label;
2357 if (level ==
"Ar_1S5") {
2360 constexpr double k3b = useTachibanaData ? 1.4e-41 : 1.1e-41;
2361 dxc.p.push_back(k3b * nAr * nAr);
2362 dxc.final.push_back(lvl[
"Ar_Excimer"]);
2363 if (useCollMixing) {
2364 constexpr double k2b = useTachibanaData ? 2.3e-24 : 2.1e-24;
2365 dxc.p.push_back(k2b * nAr);
2366 dxc.final.push_back(lvl[
"Ar_1S4"]);
2367 dxc.type.push_back(DxcTypeCollNonIon);
2369 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2370 }
else if (level ==
"Ar_1S3") {
2373 constexpr double k3b = useTachibanaData ? 1.5e-41 : 0.83e-41;
2374 dxc.p.push_back(k3b * nAr * nAr);
2375 dxc.final.push_back(lvl[
"Ar_Excimer"]);
2376 if (useCollMixing) {
2377 constexpr double k2b = useTachibanaData ? 4.3e-24 : 5.3e-24;
2378 dxc.p.push_back(k2b * nAr);
2379 dxc.final.push_back(lvl[
"Ar_1S4"]);
2381 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2383 const std::vector<int> levels4s = {lvl[
"Ar_1S5"], lvl[
"Ar_1S4"],
2384 lvl[
"Ar_1S3"], lvl[
"Ar_1S2"]};
2385 if (level ==
"Ar_2P1") {
2390 constexpr double k4s = 1.6e-20;
2391 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2392 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2393 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2394 }
else if (level ==
"Ar_2P2") {
2397 constexpr double k23 = 0.5e-21;
2398 dxc.p.push_back(k23 * nAr);
2399 dxc.final.push_back(lvl[
"Ar_2P3"]);
2404 constexpr double k4s = 5.3e-20;
2405 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2406 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2407 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2408 }
else if (level ==
"Ar_2P3") {
2411 constexpr double k34 = 27.5e-21;
2412 constexpr double k35 = 0.3e-21;
2413 constexpr double k36 = 44.0e-21;
2414 constexpr double k37 = 1.4e-21;
2415 constexpr double k38 = 1.9e-21;
2416 constexpr double k39 = 0.8e-21;
2417 dxc.p.insert(dxc.p.end(), {k34 * nAr, k35 * nAr, k36 * nAr, k37 * nAr,
2418 k38 * nAr, k39 * nAr});
2419 dxc.final.insert(dxc.final.end(),
2420 {lvl[
"Ar_2P4"], lvl[
"Ar_2P5"], lvl[
"Ar_2P6"],
2421 lvl[
"Ar_2P7"], lvl[
"Ar_2P8"], lvl[
"Ar_2P9"]});
2424 constexpr double k4s = 4.7e-20;
2425 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2426 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2427 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2428 }
else if (level ==
"Ar_2P4") {
2431 constexpr double k43 = 23.0e-21;
2432 constexpr double k45 = 0.7e-21;
2433 constexpr double k46 = 4.8e-21;
2434 constexpr double k47 = 3.2e-21;
2435 constexpr double k48 = 1.4e-21;
2436 constexpr double k49 = 3.3e-21;
2437 dxc.p.insert(dxc.p.end(), {k43 * nAr, k45 * nAr, k46 * nAr, k47 * nAr,
2438 k48 * nAr, k49 * nAr});
2439 dxc.final.insert(dxc.final.end(),
2440 {lvl[
"Ar_2P3"], lvl[
"Ar_2P5"], lvl[
"Ar_2P6"],
2441 lvl[
"Ar_2P7"], lvl[
"Ar_2P8"], lvl[
"Ar_2P9"]});
2444 constexpr double k4s = 3.9e-20;
2445 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2446 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2447 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2448 }
else if (level ==
"Ar_2P5") {
2451 constexpr double k54 = 1.7e-21;
2452 constexpr double k56 = 11.3e-21;
2453 constexpr double k58 = 9.5e-21;
2454 dxc.p.insert(dxc.p.end(), {k54 * nAr, k56 * nAr, k58 * nAr});
2455 dxc.final.insert(dxc.final.end(),
2456 {lvl[
"Ar_2P4"], lvl[
"Ar_2P6"], lvl[
"Ar_2P8"]});
2457 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2458 }
else if (level ==
"Ar_2P6") {
2461 constexpr double k67 = 4.1e-21;
2462 constexpr double k68 = 6.0e-21;
2463 constexpr double k69 = 1.0e-21;
2464 dxc.p.insert(dxc.p.end(), {k67 * nAr, k68 * nAr, k69 * nAr});
2465 dxc.final.insert(dxc.final.end(),
2466 {lvl[
"Ar_2P7"], lvl[
"Ar_2P8"], lvl[
"Ar_2P9"]});
2467 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2468 }
else if (level ==
"Ar_2P7") {
2471 constexpr double k76 = 2.5e-21;
2472 constexpr double k78 = 14.3e-21;
2473 constexpr double k79 = 23.3e-21;
2474 dxc.p.insert(dxc.p.end(), {k76 * nAr, k78 * nAr, k79 * nAr});
2475 dxc.final.insert(dxc.final.end(),
2476 {lvl[
"Ar_2P6"], lvl[
"Ar_2P8"], lvl[
"Ar_2P9"]});
2479 constexpr double k4s = 5.5e-20;
2480 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2481 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2482 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2483 }
else if (level ==
"Ar_2P8") {
2486 constexpr double k86 = 0.3e-21;
2487 constexpr double k87 = 0.8e-21;
2488 constexpr double k89 = 18.2e-21;
2489 constexpr double k810 = 1.0e-21;
2490 dxc.p.insert(dxc.p.end(),
2491 {k86 * nAr, k87 * nAr, k89 * nAr, k810 * nAr});
2492 dxc.final.insert(dxc.final.end(), {lvl[
"Ar_2P6"], lvl[
"Ar_2P7"],
2493 lvl[
"Ar_2P9"], lvl[
"Ar_2P10"]});
2496 constexpr double k4s = 3.e-20;
2497 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2498 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2499 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2500 }
else if (level ==
"Ar_2P9") {
2503 constexpr double k98 = 6.8e-21;
2504 constexpr double k910 = 5.1e-21;
2505 dxc.p.insert(dxc.p.end(), {k98 * nAr, k910 * nAr});
2506 dxc.final.insert(dxc.final.end(), {lvl[
"Ar_2P8"], lvl[
"Ar_2P10"]});
2509 constexpr double k4s = 3.5e-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);
2513 }
else if (level ==
"Ar_2P10") {
2516 constexpr double k4s = 2.0e-20;
2517 dxc.p.resize(dxc.p.size() + levels4s.size(), 0.25 * k4s * nAr);
2518 dxc.final.insert(dxc.final.end(), levels4s.begin(), levels4s.end());
2519 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2521 const std::vector<int> levels4p = {
2522 lvl[
"Ar_2P10"], lvl[
"Ar_2P9"], lvl[
"Ar_2P8"], lvl[
"Ar_2P7"],
2523 lvl[
"Ar_2P6"], lvl[
"Ar_2P5"], lvl[
"Ar_2P4"], lvl[
"Ar_2P3"],
2524 lvl[
"Ar_2P2"], lvl[
"Ar_2P1"]};
2525 if (level ==
"Ar_3D6" || level ==
"Ar_3D5" || level ==
"Ar_3D3" ||
2526 level ==
"Ar_3D4!" || level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2527 level ==
"Ar_3D1!" || level ==
"Ar_3D2" || level ==
"Ar_3S1!!!!" ||
2528 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!" || level ==
"Ar_3S1!" ||
2529 level ==
"Ar_2S5" || level ==
"Ar_2S4" || level ==
"Ar_2S3" ||
2530 level ==
"Ar_2S2") {
2534 constexpr double k4p = 1.e-20;
2535 dxc.p.resize(dxc.p.size() + levels4p.size(), 0.1 * k4p * nAr);
2536 dxc.final.insert(dxc.final.end(), levels4p.begin(), levels4p.end());
2537 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2538 }
else if (level ==
"Ar_4D5" || level ==
"Ar_3S4" || level ==
"Ar_4D2" ||
2539 level ==
"Ar_4S1!" || level ==
"Ar_3S2" || level ==
"Ar_5D5" ||
2540 level ==
"Ar_4S4" || level ==
"Ar_5D2" || level ==
"Ar_6D5" ||
2541 level ==
"Ar_5S1!" || level ==
"Ar_4S2" || level ==
"Ar_5S4" ||
2542 level ==
"Ar_6D2") {
2545 constexpr double k4p = 1.e-20;
2546 dxc.p.resize(dxc.p.size() + levels4p.size(), 0.1 * k4p * nAr);
2547 dxc.final.insert(dxc.final.end(), levels4p.begin(), levels4p.end());
2548 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2553 constexpr double kHM = 2.e-18;
2554 constexpr bool useHornbeckMolnar =
true;
2555 if (useHornbeckMolnar) {
2556 dxc.p.push_back(kHM * nAr);
2557 dxc.final.push_back(lvl[
"Ar_Dimer"]);
2558 dxc.type.push_back(DxcTypeCollIon);
2572 if (
m_gas[i] ==
"CO2")
2574 else if (
m_gas[i] ==
"CH4")
2576 else if (
m_gas[i] ==
"C2H6")
2578 else if (
m_gas[i] ==
"C2H2")
2580 else if (
m_gas[i] ==
"CF4")
2582 else if (
m_gas[i] ==
"iC4H10")
2587 constexpr double rAr3d = 436.e-10;
2588 constexpr double rAr5s = 635.e-10;
2590 if (iAr >= 0 && iCO2 >= 0) {
2594 constexpr double rCO2 = 165.e-10;
2595 for (
auto& dxc : m_deexcitations) {
2596 std::string level = dxc.label;
2598 double pacs = 0., eta = 0.;
2599 optData.GetPhotoabsorptionCrossSection(
"CO2", dxc.energy, pacs, eta);
2600 const double pPenningWK =
pow(eta, 0.4);
2601 if (level ==
"Ar_1S5") {
2603 constexpr double kQ = 5.3e-19;
2604 dxc.p.push_back(kQ * nQ);
2605 dxc.type.push_back(DxcTypeCollNonIon);
2606 }
else if (level ==
"Ar_1S4") {
2608 constexpr double kQ = 5.0e-19;
2609 dxc.p.push_back(kQ * nQ);
2610 dxc.type.push_back(DxcTypeCollNonIon);
2611 }
else if (level ==
"Ar_1S3") {
2612 constexpr double kQ = 5.9e-19;
2613 dxc.p.push_back(kQ * nQ);
2614 dxc.type.push_back(DxcTypeCollNonIon);
2615 }
else if (level ==
"Ar_1S2") {
2616 constexpr double kQ = 7.4e-19;
2617 dxc.p.push_back(kQ * nQ);
2618 dxc.type.push_back(DxcTypeCollNonIon);
2619 }
else if (level ==
"Ar_2P8") {
2621 constexpr double kQ = 6.4e-19;
2622 dxc.p.push_back(kQ * nQ);
2623 dxc.type.push_back(DxcTypeCollNonIon);
2624 }
else if (level ==
"Ar_2P6") {
2626 constexpr double kQ = 6.1e-19;
2627 dxc.p.push_back(kQ * nQ);
2628 dxc.type.push_back(DxcTypeCollNonIon);
2629 }
else if (level ==
"Ar_2P5") {
2631 constexpr double kQ = 6.6e-19;
2632 dxc.p.push_back(kQ * nQ);
2633 dxc.type.push_back(DxcTypeCollNonIon);
2634 }
else if (level ==
"Ar_2P1") {
2636 constexpr double kQ = 6.2e-19;
2637 dxc.p.push_back(kQ * nQ);
2638 dxc.type.push_back(DxcTypeCollNonIon);
2639 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2640 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2642 constexpr double kQ = 6.33e-19;
2643 dxc.p.push_back(kQ * nQ);
2644 dxc.type.push_back(DxcTypeCollNonIon);
2645 }
else if (dxc.osc > 0.) {
2648 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iCO2);
2649 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2650 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2651 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2652 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2653 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2655 const double kQ = RateConstantHardSphere(rAr3d, rCO2, iAr, iCO2);
2656 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2657 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2659 const double kQ = RateConstantHardSphere(rAr5s, rCO2, iAr, iCO2);
2660 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2662 dxc.final.resize(dxc.p.size(), -1);
2665 if (iAr >= 0 && iCH4 >= 0) {
2669 constexpr double rCH4 = 190.e-10;
2670 for (
auto& dxc : m_deexcitations) {
2671 std::string level = dxc.label;
2673 double pacs = 0., eta = 0.;
2674 optData.GetPhotoabsorptionCrossSection(
"CH4", dxc.energy, pacs, eta);
2675 const double pPenningWK =
pow(eta, 0.4);
2676 if (level ==
"Ar_1S5") {
2678 constexpr double kQ = 4.55e-19;
2679 dxc.p.push_back(kQ * nQ);
2680 dxc.type.push_back(DxcTypeCollNonIon);
2681 }
else if (level ==
"Ar_1S4") {
2683 constexpr double kQ = 4.5e-19;
2684 dxc.p.push_back(kQ * nQ);
2685 dxc.type.push_back(DxcTypeCollNonIon);
2686 }
else if (level ==
"Ar_1S3") {
2688 constexpr double kQ = 5.30e-19;
2689 dxc.p.push_back(kQ * nQ);
2690 dxc.type.push_back(DxcTypeCollNonIon);
2691 }
else if (level ==
"Ar_1S2") {
2693 constexpr double kQ = 5.7e-19;
2694 dxc.p.push_back(kQ * nQ);
2695 dxc.type.push_back(DxcTypeCollNonIon);
2696 }
else if (level ==
"Ar_2P8") {
2698 constexpr double kQ = 7.4e-19;
2699 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2700 }
else if (level ==
"Ar_2P6") {
2701 constexpr double kQ = 3.4e-19;
2702 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2703 }
else if (level ==
"Ar_2P5") {
2704 constexpr double kQ = 6.0e-19;
2705 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2706 }
else if (level ==
"Ar_2P1") {
2707 constexpr double kQ = 9.3e-19;
2708 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2709 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2710 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2712 constexpr double kQ = 6.53e-19;
2713 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2714 }
else if (dxc.osc > 0.) {
2717 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iCH4);
2718 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2719 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2720 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2721 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2722 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2724 const double kQ = RateConstantHardSphere(rAr3d, rCH4, iAr, iCH4);
2725 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2726 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2728 const double kQ = RateConstantHardSphere(rAr5s, rCH4, iAr, iCH4);
2729 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2731 dxc.final.resize(dxc.p.size(), -1);
2734 if (iAr >= 0 && iC2H6 >= 0) {
2738 constexpr double rC2H6 = 195.e-10;
2739 for (
auto& dxc : m_deexcitations) {
2740 std::string level = dxc.label;
2742 double pacs = 0., eta = 0.;
2743 optData.GetPhotoabsorptionCrossSection(
"C2H6", dxc.energy, pacs, eta);
2744 const double pPenningWK =
pow(eta, 0.4);
2745 if (level ==
"Ar_1S5") {
2747 constexpr double kQ = 5.29e-19;
2748 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2749 }
else if (level ==
"Ar_1S4") {
2751 constexpr double kQ = 6.2e-19;
2752 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2753 }
else if (level ==
"Ar_1S3") {
2755 constexpr double kQ = 6.53e-19;
2756 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2757 }
else if (level ==
"Ar_1S2") {
2759 constexpr double kQ = 10.7e-19;
2760 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2761 }
else if (level ==
"Ar_2P8") {
2763 constexpr double kQ = 9.2e-19;
2764 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2765 }
else if (level ==
"Ar_2P6") {
2766 constexpr double kQ = 4.8e-19;
2767 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2768 }
else if (level ==
"Ar_2P5") {
2769 constexpr double kQ = 9.9e-19;
2770 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2771 }
else if (level ==
"Ar_2P1") {
2772 constexpr double kQ = 11.0e-19;
2773 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2774 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2775 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2777 constexpr double kQ = 8.7e-19;
2778 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2779 }
else if (dxc.osc > 0.) {
2782 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iC2H6);
2783 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2784 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2785 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2786 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2787 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2789 const double kQ = RateConstantHardSphere(rAr3d, rC2H6, iAr, iC2H6);
2790 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2791 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2793 const double kQ = RateConstantHardSphere(rAr5s, rC2H6, iAr, iC2H6);
2794 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2796 dxc.final.resize(dxc.p.size(), -1);
2799 if (iAr >= 0 && iIso >= 0) {
2803 constexpr double rIso = 250.e-10;
2807 constexpr double r4p = 340.;
2809 constexpr double fr = (r4p + 250.) / (r4p + 195.);
2811 constexpr double mAr = 39.9;
2812 constexpr double mEth = 30.1;
2813 constexpr double mIso = 58.1;
2816 fr * fr *
sqrt((mEth / mIso) * (mAr + mIso) / (mAr + mEth));
2817 for (
auto& dxc : m_deexcitations) {
2818 std::string level = dxc.label;
2820 double pacs = 0., eta = 0.;
2822 optData.GetPhotoabsorptionCrossSection(
"nC4H10", dxc.energy, pacs, eta);
2823 const double pPenningWK =
pow(eta, 0.4);
2824 if (level ==
"Ar_1S5") {
2827 constexpr double kQ = 7.1e-19;
2828 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2829 }
else if (level ==
"Ar_1S4") {
2831 constexpr double kQ = 6.1e-19;
2832 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2833 }
else if (level ==
"Ar_1S3") {
2836 constexpr double kQ = 8.5e-19;
2837 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2838 }
else if (level ==
"Ar_1S2") {
2840 constexpr double kQ = 11.0e-19;
2841 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2842 }
else if (level ==
"Ar_2P8") {
2843 constexpr double kEth = 9.2e-19;
2844 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2845 }
else if (level ==
"Ar_2P6") {
2846 constexpr double kEth = 4.8e-19;
2847 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2848 }
else if (level ==
"Ar_2P5") {
2849 const double kEth = 9.9e-19;
2850 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2851 }
else if (level ==
"Ar_2P1") {
2852 const double kEth = 11.0e-19;
2853 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2854 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2855 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2856 constexpr double kEth = 5.5e-19;
2857 AddPenningDeexcitation(dxc, f4p * kEth * nQ, pPenningWK);
2858 }
else if (dxc.osc > 0.) {
2861 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iIso);
2862 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2863 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2864 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2865 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2866 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2868 const double kQ = RateConstantHardSphere(rAr3d, rIso, iAr, iIso);
2869 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2870 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2872 const double kQ = RateConstantHardSphere(rAr5s, rIso, iAr, iIso);
2873 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2875 dxc.final.resize(dxc.p.size(), -1);
2878 if (iAr >= 0 && iC2H2 >= 0) {
2882 constexpr double rC2H2 = 165.e-10;
2883 for (
auto& dxc : m_deexcitations) {
2884 std::string level = dxc.label;
2886 double pacs = 0., eta = 0.;
2887 optData.GetPhotoabsorptionCrossSection(
"C2H2", dxc.energy, pacs, eta);
2888 const double pPenningWK =
pow(eta, 0.4);
2889 if (level ==
"Ar_1S5") {
2891 constexpr double kQ = 5.6e-19;
2895 AddPenningDeexcitation(dxc, kQ * nQ, 0.61);
2896 }
else if (level ==
"Ar_1S4") {
2898 constexpr double kQ = 4.6e-19;
2899 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2900 }
else if (level ==
"Ar_1S3") {
2901 constexpr double kQ = 5.6e-19;
2902 AddPenningDeexcitation(dxc, kQ * nQ, 0.61);
2903 }
else if (level ==
"Ar_1S2") {
2905 constexpr double kQ = 8.7e-19;
2906 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2907 }
else if (level ==
"Ar_2P8") {
2909 constexpr double kQ = 5.0e-19;
2910 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2911 }
else if (level ==
"Ar_2P6") {
2912 constexpr double kQ = 5.7e-19;
2913 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2914 }
else if (level ==
"Ar_2P5") {
2915 constexpr double kQ = 6.0e-19;
2916 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2917 }
else if (level ==
"Ar_2P1") {
2918 constexpr double kQ = 5.3e-19;
2919 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2920 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2921 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2923 constexpr double kQ = 5.5e-19;
2924 AddPenningDeexcitation(dxc, kQ * nQ, 0.3);
2925 }
else if (dxc.osc > 0.) {
2928 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iC2H2);
2929 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2930 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2931 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2932 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2933 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2935 const double kQ = RateConstantHardSphere(rAr3d, rC2H2, iAr, iC2H2);
2936 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2937 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2939 const double kQ = RateConstantHardSphere(rAr5s, rC2H2, iAr, iC2H2);
2940 AddPenningDeexcitation(dxc, kQ * nQ, pPenningWK);
2942 dxc.final.resize(dxc.p.size(), -1);
2945 if (iAr >= 0 && iCF4 >= 0) {
2949 constexpr double rCF4 = 235.e-10;
2950 for (
auto& dxc : m_deexcitations) {
2951 std::string level = dxc.label;
2953 double pacs = 0., eta = 0.;
2954 optData.GetPhotoabsorptionCrossSection(
"CF4", dxc.energy, pacs, eta);
2955 if (level ==
"Ar_1S5") {
2957 constexpr double kQ = 0.33e-19;
2958 dxc.p.push_back(kQ * nQ);
2959 }
else if (level ==
"Ar_1S3") {
2961 constexpr double kQ = 0.26e-19;
2962 dxc.p.push_back(kQ * nQ);
2963 }
else if (level ==
"Ar_2P8") {
2965 constexpr double kQ = 1.7e-19;
2966 dxc.p.push_back(kQ * nQ);
2967 }
else if (level ==
"Ar_2P6") {
2968 constexpr double kQ = 1.7e-19;
2969 dxc.p.push_back(kQ * nQ);
2970 }
else if (level ==
"Ar_2P5") {
2971 constexpr double kQ = 1.6e-19;
2972 dxc.p.push_back(kQ * nQ);
2973 }
else if (level ==
"Ar_2P1") {
2974 constexpr double kQ = 2.2e-19;
2975 dxc.p.push_back(kQ * nQ);
2976 }
else if (level ==
"Ar_2P10" || level ==
"Ar_2P9" || level ==
"Ar_2P7" ||
2977 level ==
"Ar_2P4" || level ==
"Ar_2P3" || level ==
"Ar_2P2") {
2979 constexpr double kQ = 1.8e-19;
2980 dxc.p.push_back(kQ * nQ);
2981 }
else if (dxc.osc > 0.) {
2984 const double kQ = RateConstantWK(dxc.energy, dxc.osc, pacs, iAr, iCF4);
2985 dxc.p.push_back(kQ * nQ);
2986 }
else if (level ==
"Ar_3D6" || level ==
"Ar_3D3" || level ==
"Ar_3D4!" ||
2987 level ==
"Ar_3D4" || level ==
"Ar_3D1!!" ||
2988 level ==
"Ar_3D1!" || level ==
"Ar_3S1!!!!" ||
2989 level ==
"Ar_3S1!!" || level ==
"Ar_3S1!!!") {
2991 const double kQ = RateConstantHardSphere(rAr3d, rCF4, iAr, iCF4);
2992 dxc.p.push_back(kQ * nQ);
2993 }
else if (level ==
"Ar_2S5" || level ==
"Ar_2S3") {
2995 const double kQ = RateConstantHardSphere(rAr5s, rCF4, iAr, iCF4);
2996 dxc.p.push_back(kQ * nQ);
2998 dxc.type.resize(dxc.p.size(), DxcTypeCollNonIon);
2999 dxc.final.resize(dxc.p.size(), -1);
3003 if ((
m_debug || verbose) && nDeexcitations > 0) {
3004 std::cout <<
m_className <<
"::ComputeDeexcitationTable:\n"
3005 <<
" Level Energy [eV] Lifetimes [ns]\n"
3006 <<
" Total Radiative "
3009 <<
" Ionisation Transfer Loss\n";
3012 for (
auto& dxc : m_deexcitations) {
3016 double fCollIon = 0., fCollTransfer = 0., fCollLoss = 0.;
3017 const unsigned int nChannels = dxc.type.size();
3018 for (
unsigned int j = 0; j < nChannels; ++j) {
3019 dxc.rate += dxc.p[j];
3020 if (dxc.type[j] == DxcTypeRad) {
3022 }
else if (dxc.type[j] == DxcTypeCollIon) {
3023 fCollIon += dxc.p[j];
3024 }
else if (dxc.type[j] == DxcTypeCollNonIon) {
3025 if (dxc.final[j] < 0) {
3026 fCollLoss += dxc.p[j];
3028 fCollTransfer += dxc.p[j];
3031 std::cerr <<
m_className <<
"::ComputeDeexcitationTable:\n "
3032 <<
"Unknown type of deexcitation channel (level " << dxc.label
3033 <<
"). Program bug!\n";
3036 if (dxc.rate > 0.) {
3039 std::cout << std::setw(12) << dxc.label <<
" " << std::fixed
3040 << std::setprecision(3) << std::setw(7) << dxc.energy <<
" "
3041 << std::setw(10) << 1. / dxc.rate <<
" ";
3043 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
3044 << 1. / fRad <<
" ";
3046 std::cout <<
"---------- ";
3048 if (fCollIon > 0.) {
3049 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
3050 << 1. / fCollIon <<
" ";
3052 std::cout <<
"---------- ";
3054 if (fCollTransfer > 0.) {
3055 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
3056 << 1. / fCollTransfer <<
" ";
3058 std::cout <<
"---------- ";
3060 if (fCollLoss > 0.) {
3061 std::cout << std::fixed << std::setprecision(3) << std::setw(10)
3062 << 1. / fCollLoss <<
"\n";
3064 std::cout <<
"---------- \n";
3068 for (
unsigned int j = 0; j < nChannels; ++j) {
3069 dxc.p[j] /= dxc.rate;
3070 if (j > 0) dxc.p[j] += dxc.p[j - 1];
3076double MediumMagboltz::RateConstantWK(
const double energy,
const double osc,
3077 const double pacs,
const int igas1,
3078 const int igas2)
const {
3080 const double m1 = ElectronMassGramme / (m_rgas[igas1] - 1.);
3081 const double m2 = ElectronMassGramme / (m_rgas[igas2] - 1.);
3083 double mR = (m1 * m2 / (m1 + m2)) / AtomicMassUnit;
3084 const double uA = (RydbergEnergy / energy) * osc;
3085 const double uQ = (2 * RydbergEnergy / energy) * pacs /
3086 (4 * Pi2 * FineStructureConstant * BohrRadius * BohrRadius);
3090double MediumMagboltz::RateConstantHardSphere(
const double r1,
const double r2,
3092 const int igas2)
const {
3094 const double r = r1 + r2;
3095 const double sigma = r * r * Pi;
3097 const double m1 = ElectronMass / (m_rgas[igas1] - 1.);
3098 const double m2 = ElectronMass / (m_rgas[igas2] - 1.);
3099 const double mR = m1 * m2 / (m1 + m2);
3107 if (!m_useDeexcitation) {
3108 std::cerr <<
m_className <<
"::ComputeDeexcitation: Not enabled.\n";
3115 PrintErrorMixer(
m_className +
"::ComputeDeexcitation");
3121 if (iLevel < 0 || iLevel >= (
int)m_nTerms) {
3122 std::cerr <<
m_className <<
"::ComputeDeexcitation: Index out of range.\n";
3126 iLevel = m_iDeexcitation[iLevel];
3127 if (iLevel < 0 || iLevel >= (
int)m_deexcitations.size()) {
3128 std::cerr <<
m_className <<
"::ComputeDeexcitation:\n"
3129 <<
" Level is not deexcitable.\n";
3133 ComputeDeexcitationInternal(iLevel, fLevel);
3134 if (fLevel >= 0 && fLevel < (
int)m_deexcitations.size()) {
3135 fLevel = m_deexcitations[fLevel].level;
3139void MediumMagboltz::ComputeDeexcitationInternal(
int iLevel,
int& fLevel) {
3140 m_dxcProducts.clear();
3144 while (iLevel >= 0 && iLevel < (
int)m_deexcitations.size()) {
3145 const auto& dxc = m_deexcitations[iLevel];
3146 const int nChannels = dxc.p.size();
3147 if (dxc.rate <= 0. || nChannels <= 0) {
3156 int type = DxcTypeRad;
3158 for (
int j = 0; j < nChannels; ++j) {
3159 if (r <= dxc.p[j]) {
3160 fLevel = dxc.final[j];
3165 if (type == DxcTypeRad) {
3170 photon.type = DxcProdTypePhoton;
3171 photon.energy = dxc.energy;
3174 photon.energy -= m_deexcitations[fLevel].energy;
3175 if (photon.energy < Small) photon.energy = Small;
3176 m_dxcProducts.push_back(std::move(photon));
3181 double delta =
RndmVoigt(0., dxc.sDoppler, dxc.gPressure);
3182 while (photon.energy + delta < Small ||
fabs(delta) >= dxc.width) {
3183 delta =
RndmVoigt(0., dxc.sDoppler, dxc.gPressure);
3185 photon.energy += delta;
3186 m_dxcProducts.push_back(std::move(photon));
3191 }
else if (type == DxcTypeCollIon) {
3196 electron.type = DxcProdTypeElectron;
3197 electron.energy = dxc.energy;
3200 electron.energy -= m_deexcitations[fLevel].energy;
3201 if (electron.energy < Small) electron.energy = Small;
3203 m_dxcProducts.push_back(std::move(electron));
3208 electron.energy -= m_minIonPot;
3209 if (electron.energy < Small) electron.energy = Small;
3211 m_dxcProducts.push_back(std::move(electron));
3216 }
else if (type == DxcTypeCollNonIon) {
3220 std::cerr <<
m_className <<
"::ComputeDeexcitationInternal:\n"
3221 <<
" Unknown deexcitation type (" << type <<
"). Bug!\n";
3229bool MediumMagboltz::ComputePhotonCollisionTable(
const bool verbose) {
3238 m_cfTotGamma.assign(nEnergyStepsGamma, 0.);
3239 m_cfGamma.assign(nEnergyStepsGamma, std::vector<double>());
3240 csTypeGamma.clear();
3244 const double prefactor = dens * SpeedOfLight *
m_fraction[i];
3246 std::string gasname =
m_gas[i];
3247 if (gasname ==
"iC4H10") {
3250 std::cout <<
m_className <<
"::ComputePhotonCollisionTable:\n"
3251 <<
" Photoabsorption cross-section for "
3252 <<
"iC4H10 not available.\n"
3253 <<
" Using n-butane cross-section instead.\n";
3256 if (!data.IsAvailable(gasname))
return false;
3257 csTypeGamma.push_back(i * nCsTypesGamma + PhotonCollisionTypeIonisation);
3258 csTypeGamma.push_back(i * nCsTypesGamma + PhotonCollisionTypeInelastic);
3259 m_nPhotonTerms += 2;
3260 for (
int j = 0; j < nEnergyStepsGamma; ++j) {
3262 data.GetPhotoabsorptionCrossSection(gasname, (j + 0.5) * m_eStepGamma, cs,
3264 m_cfTotGamma[j] += cs * prefactor;
3266 m_cfGamma[j].push_back(cs * prefactor * eta);
3268 m_cfGamma[j].push_back(cs * prefactor * (1. - eta));
3273 if (m_useCsOutput) {
3274 std::ofstream csfile;
3275 csfile.open(
"csgamma.txt", std::ios::out);
3276 for (
int j = 0; j < nEnergyStepsGamma; ++j) {
3277 csfile << (j + 0.5) * m_eStepGamma <<
" ";
3278 for (
unsigned int i = 0; i < m_nPhotonTerms; ++i)
3279 csfile << m_cfGamma[j][i] <<
" ";
3286 for (
int j = 0; j < nEnergyStepsGamma; ++j) {
3287 for (
unsigned int i = 0; i < m_nPhotonTerms; ++i) {
3288 if (i > 0) m_cfGamma[j][i] += m_cfGamma[j][i - 1];
3293 std::cout <<
m_className <<
"::ComputePhotonCollisionTable:\n";
3294 std::cout <<
" Energy [eV] Mean free path [um]\n";
3295 for (
int i = 0; i < 10; ++i) {
3296 const int j = (2 * i + 1) * nEnergyStepsGamma / 20;
3297 const double en = (2 * i + 1) * m_eFinalGamma / 20;
3298 const double imfp = m_cfTotGamma[j] / SpeedOfLight;
3300 printf(
" %10.2f %18.4f\n", en, 1.e4 / imfp);
3302 printf(
" %10.2f ------------\n", en);
3307 if (!m_useDeexcitation)
return true;
3310 constexpr double f2cs =
3311 FineStructureConstant * 2 * Pi2 * HbarC * HbarC / ElectronMass;
3313 int nResonanceLines = 0;
3314 for (
auto& dxc : m_deexcitations) {
3315 if (dxc.osc < Small)
continue;
3316 const double prefactor = dens * SpeedOfLight *
m_fraction[dxc.gas];
3317 dxc.cf = prefactor * f2cs * dxc.osc;
3319 const double mgas = ElectronMass / (m_rgas[dxc.gas] - 1.);
3321 dxc.sDoppler = wDoppler * dxc.energy;
3325 const double kResBroad = 1.92 * Pi *
sqrt(1. / 3.);
3326 dxc.gPressure = kResBroad * FineStructureConstant *
pow(HbarC, 3) *
3328 (ElectronMass * dxc.energy);
3331 constexpr double nWidths = 1000.;
3335 const double fwhmGauss = dxc.sDoppler *
sqrt(2. * log(2.));
3336 const double fwhmLorentz = dxc.gPressure;
3337 const double fwhmVoigt =
3338 0.5 * (1.0692 * fwhmLorentz +
sqrt(0.86639 * fwhmLorentz * fwhmLorentz +
3339 4 * fwhmGauss * fwhmGauss));
3340 dxc.width = nWidths * fwhmVoigt;
3344 if (nResonanceLines <= 0) {
3345 std::cerr <<
m_className <<
"::ComputePhotonCollisionTable:\n"
3346 <<
" No resonance lines found.\n";
3350 if (!(
m_debug || verbose))
return true;
3351 std::cout <<
m_className <<
"::ComputePhotonCollisionTable:\n "
3352 <<
"Discrete absorption lines:\n Energy [eV] "
3353 <<
"Line width (FWHM) [eV] Mean free path [um]\n "
3354 <<
" Doppler Pressure (peak)\n";
3355 for (
const auto& dxc : m_deexcitations) {
3356 if (dxc.osc < Small)
continue;
3357 const double wp = 2 * dxc.gPressure;
3358 const double wd = 2 *
sqrt(2 * log(2.)) * dxc.sDoppler;
3359 const double imfpP =
3360 (dxc.cf / SpeedOfLight) * TMath::Voigt(0., dxc.sDoppler, wp);
3362 printf(
" %6.3f +/- %6.1e %6.2e %6.3e %14.4f\n", dxc.energy,
3363 dxc.width, wd, wp, 1.e4 / imfpP);
3365 printf(
" %6.3f +/- %6.1e %6.2e %6.3e -------------\n", dxc.energy,
3373 const double emag,
const double bmag,
const double btheta,
const int ncoll,
3374 bool verbose,
double& vx,
double& vy,
double& vz,
double& dl,
double& dt,
3375 double& alpha,
double& eta,
double& lor,
double& vxerr,
double& vyerr,
3376 double& vzerr,
double& dlerr,
double& dterr,
double& alphaerr,
3377 double& etaerr,
double& lorerr,
double& alphatof,
3378 std::array<double, 6>& difftens) {
3382 alpha = eta = alphatof = 0.;
3384 vxerr = vyerr = vzerr = 0.;
3386 alphaerr = etaerr = 0.;
3392 if (m_autoEnergyLimit) {
3412 const int ng = GetGasNumberMagboltz(
m_gas[i]);
3415 <<
" does not have a gas number in Magboltz.\n";
3434 const double vt = sqrt(vx * vx + vy * vy);
3435 const double v2 = (vx * vx + vy * vy + vz * vz);
3436 lor = atan2(vt, vz);
3437 if (vt > 0. && v2 > 0. && fabs(lor) > 0.) {
3438 const double dvx = vx * vxerr;
3439 const double dvy = vy * vyerr;
3440 const double dvz = vz * vzerr;
3441 const double a = vz / vt;
3442 lorerr = sqrt(a * a * (vx * vx * dvx * dvx + vy * vy * dvy * dvy) +
3443 vt * vt * dvz * dvz) /
3477 alphatof = fc1 - sqrt(fc1 * fc1 - fc2);
3480 if (!(
m_debug || verbose))
return;
3481 std::cout <<
m_className <<
"::RunMagboltz: Results:\n";
3482 printf(
" Drift velocity along E: %12.8f cm/ns +/- %5.2f%%\n", vz, vzerr);
3483 printf(
" Drift velocity along Bt: %12.8f cm/ns +/- %5.2f%%\n", vx, vxerr);
3484 printf(
" Drift velocity along ExB: %12.8f cm/ns +/- %5.2f%%\n", vy, vyerr);
3485 printf(
" Lorentz angle: %12.3f degree\n", lor * RadToDegree);
3486 printf(
" Longitudinal diffusion: %12.8f cm1/2 +/- %5.2f%%\n", dl, dlerr);
3487 printf(
" Transverse diffusion: %12.8f cm1/2 +/- %5.2f%%\n", dt, dterr);
3488 printf(
" Townsend coefficient: %12.4f cm-1 +/- %5.2f%%\n", alpha,
3490 printf(
" Attachment coefficient: %12.4f cm-1 +/- %5.2f%%\n", eta,
3492 if (alphatof > 0.) {
3493 printf(
" TOF effective Townsend: %12.4f cm-1 (alpha - eta)\n",
3504 const unsigned int nEfields =
m_eFields.size();
3505 const unsigned int nBfields =
m_bFields.size();
3506 const unsigned int nAngles =
m_bAngles.size();
3512 Init(nEfields, nBfields, nAngles,
m_eLor, 0.);
3513 Init(nEfields, nBfields, nAngles,
m_eAlp, -30.);
3515 Init(nEfields, nBfields, nAngles,
m_eAtt, -30.);
3516 Init(nEfields, nBfields, nAngles, 6,
m_eDifM, 0.);
3521 GetExcitationIonisationLevels();
3522 std::cout <<
m_className <<
"::GenerateGasTable: Found "
3526 std::cout <<
" " << exc.label <<
", energy = " << exc.energy <<
" eV.\n";
3529 std::cout <<
" " << ion.label <<
", energy = " << ion.energy <<
" eV.\n";
3537 double vx = 0., vy = 0., vz = 0.;
3538 double difl = 0., dift = 0.;
3539 double alpha = 0., eta = 0.;
3541 double vxerr = 0., vyerr = 0., vzerr = 0.;
3542 double diflerr = 0., difterr = 0.;
3543 double alphaerr = 0., etaerr = 0.;
3544 double alphatof = 0.;
3546 std::array<double, 6> difftens;
3549 for (
unsigned int i = 0; i < nEfields; ++i) {
3551 for (
unsigned int j = 0; j < nAngles; ++j) {
3553 for (
unsigned int k = 0; k < nBfields; ++k) {
3555 std::cout <<
m_className <<
"::GenerateGasTable: E = " << e
3556 <<
" V/cm, B = " << b <<
" T, angle: " << a <<
" rad\n";
3557 RunMagboltz(e, b, a, numColl, verbose, vx, vy, vz, difl, dift, alpha,
3558 eta, lor, vxerr, vyerr, vzerr, diflerr, difterr, alphaerr,
3559 etaerr, lorerr, alphatof, difftens);
3566 m_eAlp[j][k][i] = alpha > 0. ? log(alpha) : -30.;
3567 m_eAlp0[j][k][i] = alpha > 0. ? log(alpha) : -30.;
3568 m_eAtt[j][k][i] = eta > 0. ? log(eta) : -30.;
3569 for (
unsigned int l = 0; l < 6; ++l) {
3570 m_eDifM[l][j][k][i] = difftens[l];
3573 unsigned int nNonZero = 0;
3574 if (m_useGasMotion) {
3576 double ftot = 0., fel = 0., fion = 0., fatt = 0., fin = 0.;
3577 std::int64_t ntotal = 0;
3579 if (ntotal == 0)
continue;
3581 const double scale = 1.e3 * ftot / ntotal;
3584 for (std::int64_t il = 0; il < nL; ++il) {
3588 if (cstype != 1 && cstype != 3)
continue;
3591 descr =
m_gas[ig] + descr;
3594 for (
unsigned int ie = 0; ie < nExc; ++ie) {
3598 if (ncoll > 0) ++nNonZero;
3601 }
else if (cstype == 1) {
3603 for (
unsigned int ii = 0; ii < nIon; ++ii) {
3607 if (ncoll > 0) ++nNonZero;
3615 double ftot = 0., fel = 0., fion = 0., fatt = 0., fin = 0.;
3616 std::int64_t ntotal = 0;
3618 if (ntotal == 0)
continue;
3620 const double scale = 1.e3 * ftot / ntotal;
3625 if (cstype != 1 && cstype != 3)
continue;
3628 descr =
m_gas[igas] + descr;
3631 for (
unsigned int ie = 0; ie < nExc; ++ie) {
3637 }
else if (cstype == 1) {
3639 for (
unsigned int ii = 0; ii < nIon; ++ii) {
3649 std::cout <<
" Excitation and ionisation rates:\n";
3650 std::cout <<
" Level "
3651 <<
" Rate [ns-1]\n";
3653 for (
unsigned int ie = 0; ie < nExc; ++ie) {
3655 std::cout << std::setw(60) <<
m_excLevels[ie].label;
3656 std::printf(
" %15.8f\n",
m_excRates[ie][j][k][i]);
3659 for (
unsigned int ii = 0; ii < nIon; ++ii) {
3661 std::cout << std::setw(60) <<
m_ionLevels[ii].label;
3662 std::printf(
" %15.8f\n",
m_ionRates[ii][j][k][i]);
3673void MediumMagboltz::GetExcitationIonisationLevels() {
3697 const double emax = 400.;
3702 std::int64_t nIn = 0, nIon = 0, nAtt = 1, nNull = 0;
3712 static std::int64_t kEl[6];
3728 std::int64_t ng = GetGasNumberMagboltz(
m_gas[i]);
3730 std::cerr <<
m_className <<
"::GetExcitationIonisationLevels:\n\n"
3731 <<
" Gas " <<
m_gas[i] <<
" not available in Magboltz.\n";
3735 &ng, q[0], qIn[0], &nIn, e, eIn, name, &virial, eoby, pEqEl[0],
3736 pEqIn[0], penFra[0], kEl, kIn, qIon[0], pEqIon[0], eIon, &nIon,
3737 qAtt[0], &nAtt, qNull[0], &nNull, scln, nc0, ec0, wklm, efl,
3738 ng1, eg1, ng2, eg2, scrpt, scrptn,
3740 const double r = 1. + 0.5 * e[1];
3742 for (
int j = 0; j < nIon; ++j) {
3743 const std::string descr = GetDescription(2 + j, scrpt);
3745 ion.label =
m_gas[i] + descr;
3746 ion.energy = eIon[j] / r;
3750 for (
int j = 0; j < nIn; ++j) {
3751 const std::string descr = GetDescription(4 + nIon + nAtt + j, scrpt);
3752 if ((descr[1] ==
'E' && descr[2] ==
'X') ||
3753 (descr[0] ==
'E' && descr[1] ==
'X')) {
3756 exc.label =
m_gas[i] + descr;
3757 exc.energy = eIn[j] / r;
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
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.
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
struct Garfield::Magboltz::@17 diflab_
struct Garfield::Magboltz::@6 dens_
struct Garfield::Magboltz::@11 large_
constexpr unsigned int nMaxInelasticTerms
constexpr unsigned int nEnergySteps
struct Garfield::Magboltz::@23 tofout_
struct Garfield::Magboltz::@16 velerr_
struct Garfield::Magboltz::@12 larget_
struct Garfield::Magboltz::@15 vel_
struct Garfield::Magboltz::@9 scrip_
constexpr unsigned int nMaxAttachmentTerms
constexpr unsigned int nCharDescr
double cf[nMaxLevels][nEnergySteps]
constexpr unsigned int nMaxIonisationTerms
constexpr unsigned int nCharName
struct Garfield::Magboltz::@7 outpt_
struct Garfield::Magboltz::@22 ctwner_
struct Garfield::Magboltz::@0 bfld_
struct Garfield::Magboltz::@8 outptt_
struct Garfield::Magboltz::@14 ratio_
struct Garfield::Magboltz::@21 ctowns_
struct Garfield::Magboltz::@2 setp_
constexpr unsigned int nMaxLevels
void colf_(double *freq, double *freel, double *freion, double *freatt, double *frein, std::int64_t *ntotal)
struct Garfield::Magboltz::@1 inpt_
constexpr unsigned int nMaxNullTerms
struct Garfield::Magboltz::@10 script_
struct Garfield::Magboltz::@20 diferl_
struct Garfield::Magboltz::@5 mix2_
struct Garfield::Magboltz::@3 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::@4 cnsts_
struct Garfield::Magboltz::@13 gasn_
constexpr unsigned int nMaxLevelsPerComponent
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)