Garfield++ 3.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
MediumGas.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <cstdio>
3#include <cstdlib>
4#include <cstring>
5#include <ctime>
6#include <fstream>
7#include <iomanip>
8#include <iostream>
9#include <sstream>
10#include <numeric>
11
14#include "Garfield/MediumGas.hh"
16#include "Garfield/Utilities.hh"
17
18namespace {
19
20std::string FmtFloat(const double x, const unsigned int width = 15,
21 const unsigned int precision = 8) {
22 char buffer[256];
23 std::snprintf(buffer, width + 1, "%*.*E", width, precision, x);
24 return std::string(buffer);
25}
26
27std::string FmtInt(const int n, const unsigned int width) {
28 char buffer[256];
29 std::snprintf(buffer, width + 1, "%*d", width, n);
30 return std::string(buffer);
31}
32
33void PrintArray(const std::vector<double>& values, std::ofstream& outfile,
34 int& col, const int ncols) {
35 for (const auto value : values) {
36 outfile << FmtFloat(value);
37 ++col;
38 if (col % ncols == 0) outfile << "\n";
39 }
40}
41
42void PrintExtrapolation(const std::pair<unsigned int, unsigned int>& extr) {
43 std::cout << " Low field extrapolation: ";
44 if (extr.first == 0)
45 std::cout << " constant\n";
46 else if (extr.first == 1)
47 std::cout << " linear\n";
48 else if (extr.first == 2)
49 std::cout << " exponential\n";
50 else
51 std::cout << " unknown\n";
52 std::cout << " High field extrapolation: ";
53 if (extr.second == 0)
54 std::cout << " constant\n";
55 else if (extr.second == 1)
56 std::cout << " linear\n";
57 else if (extr.second == 2)
58 std::cout << " exponential\n";
59 else
60 std::cout << " unknown\n";
61}
62
63void PrintAbsentInNew(const std::string& par) {
64 std::cout << " Warning: The " << par << " is absent in the dataset "
65 << "to be added; data reset.\n";
66}
67
68void PrintAbsentInExisting(const std::string& par) {
69 std::cout << " Warning: The " << par << " is absent in the existing data; "
70 << "new data not used.\n";
71}
72
73bool Similar(const double v1, const double v2, const double eps) {
74 const double dif = v1 - v2;
75 const double sum = fabs(v1) + fabs(v2);
76 return fabs(dif) < std::max(eps * sum, Garfield::Small);
77}
78
79int Equal(const std::vector<double>& fields1,
80 const std::vector<double>& fields2, const double eps) {
81 if (fields1.size() != fields2.size()) return 0;
82 const unsigned int n = fields1.size();
83 for (unsigned int i = 0; i < n; ++i) {
84 if (!Similar(fields1[i], fields2[i], eps)) return 0;
85 }
86 return 1;
87}
88
89int FindIndex(const double field, const std::vector<double>& fields,
90 const double eps) {
91
92 const int n = fields.size();
93 for (int i = 0; i < n; ++i) {
94 if (Similar(field, fields[i], eps)) return i;
95 }
96 return -1;
97}
98
99void ResizeA(std::vector<std::vector<std::vector<double> > >& tab,
100 const int ne, const int nb, const int na) {
101 if (tab.empty()) return;
102 tab.resize(na,
103 std::vector<std::vector<double> >(nb,
104 std::vector<double>(ne, 0.)));
105}
106
107}
108
109namespace Garfield {
110
112 : Medium(), m_pressureTable(m_pressure), m_temperatureTable(m_temperature) {
113 m_className = "MediumGas";
114
115 m_gas.fill("");
116 m_fraction.fill(0.);
117 m_atWeight.fill(0.);
118 m_atNum.fill(0.);
119 // Default gas mixture: pure argon
120 m_gas[0] = "Ar";
121 m_fraction[0] = 1.;
122 m_name = m_gas[0];
124
125 m_rPenningGas.fill(0.);
126 m_lambdaPenningGas.fill(0.);
127
128 m_isChanged = true;
129
130 EnableDrift();
132}
133
134bool MediumGas::SetComposition(const std::string& gas1, const double f1,
135 const std::string& gas2, const double f2,
136 const std::string& gas3, const double f3,
137 const std::string& gas4, const double f4,
138 const std::string& gas5, const double f5,
139 const std::string& gas6, const double f6) {
140 std::array<std::string, 6> gases = {gas1, gas2, gas3, gas4, gas5, gas6};
141 std::array<double, 6> fractions = {f1, f2, f3, f4, f5, f6};
142
143 // Make a backup copy of the gas composition.
144 const std::array<std::string, m_nMaxGases> gasOld = m_gas;
145 const unsigned int nComponentsOld = m_nComponents;
146
147 // Reset all transport parameters.
148 ResetTables();
149 // Force a recalculation of the collision rates.
150 m_isChanged = true;
151
152 m_nComponents = 0;
153 m_gas.fill("");
154 m_fraction.fill(0.);
155 m_atWeight.fill(0.);
156 m_atNum.fill(0.);
157 for (unsigned int i = 0; i < 6; ++i) {
158 if (fractions[i] < Small) continue;
159 // Find the gas name corresponding to the input string.
160 const std::string gasname = GetGasName(gases[i]);
161 if (!gasname.empty()) {
162 m_gas[m_nComponents] = gasname;
163 m_fraction[m_nComponents] = fractions[i];
165 }
166 }
167
168 // Check if at least one valid ingredient was specified.
169 if (m_nComponents == 0) {
170 std::cerr << m_className << "::SetComposition:\n"
171 << " Error setting the composition. No valid components.\n";
172 return false;
173 }
174
175 // Establish the name.
176 m_name = "";
177 double sum = 0.;
178 for (unsigned int i = 0; i < m_nComponents; ++i) {
179 if (i > 0) m_name += "/";
180 m_name += m_gas[i];
181 sum += m_fraction[i];
182 }
183 // Normalise the fractions to one.
184 for (unsigned int i = 0; i < m_nComponents; ++i) {
185 m_fraction[i] /= sum;
186 }
187
188 // Set the atomic weight and number.
189 for (unsigned int i = 0; i < m_nComponents; ++i) {
191 }
192
193 // Print the composition.
194 std::cout << m_className << "::SetComposition:\n " << m_name;
195 if (m_nComponents > 1) {
196 std::cout << " (" << m_fraction[0] * 100;
197 for (unsigned int i = 1; i < m_nComponents; ++i) {
198 std::cout << "/" << m_fraction[i] * 100;
199 }
200 std::cout << ")";
201 }
202 std::cout << "\n";
203
204
205 // Copy the previous Penning transfer parameters.
206 std::array<double, m_nMaxGases> rPenningGasOld;
207 std::array<double, m_nMaxGases> lambdaPenningGasOld;
208 rPenningGasOld.fill(0.);
209 lambdaPenningGasOld.fill(0.);
210 rPenningGasOld.swap(m_rPenningGas);
211 lambdaPenningGasOld.swap(m_lambdaPenningGas);
212 for (unsigned int i = 0; i < m_nComponents; ++i) {
213 for (unsigned int j = 0; j < nComponentsOld; ++j) {
214 if (m_gas[i] != gasOld[j]) continue;
215 if (rPenningGasOld[j] < Small) continue;
216 m_rPenningGas[i] = rPenningGasOld[j];
217 m_lambdaPenningGas[i] = lambdaPenningGasOld[i];
218 std::cout << m_className << "::SetComposition:\n"
219 << " Using Penning transfer parameters for " << m_gas[i]
220 << " from previous mixture.\n"
221 << " r = " << m_rPenningGas[i] << "\n"
222 << " lambda = " << m_lambdaPenningGas[i] << " cm\n";
223 }
224 }
225 return true;
226}
227
228void MediumGas::GetComposition(std::string& gas1, double& f1, std::string& gas2,
229 double& f2, std::string& gas3, double& f3,
230 std::string& gas4, double& f4, std::string& gas5,
231 double& f5, std::string& gas6, double& f6) {
232 gas1 = m_gas[0];
233 gas2 = m_gas[1];
234 gas3 = m_gas[2];
235 gas4 = m_gas[3];
236 gas5 = m_gas[4];
237 gas6 = m_gas[5];
238 f1 = m_fraction[0];
239 f2 = m_fraction[1];
240 f3 = m_fraction[2];
241 f4 = m_fraction[3];
242 f5 = m_fraction[4];
243 f6 = m_fraction[5];
244}
245
246void MediumGas::GetComponent(const unsigned int i, std::string& label,
247 double& f) {
248 if (i >= m_nComponents) {
249 std::cerr << m_className << "::GetComponent: Index out of range.\n";
250 label = "";
251 f = 0.;
252 return;
253 }
254
255 label = m_gas[i];
256 f = m_fraction[i];
257}
258
259void MediumGas::SetAtomicNumber(const double z) {
260 std::cerr << m_className << "::SetAtomicNumber:\n"
261 << " Effective Z cannot be changed directly to " << z << ".\n"
262 << " Use SetComposition to define the gas mixture.\n";
263}
264
265void MediumGas::SetAtomicWeight(const double a) {
266 std::cerr << m_className << "::SetAtomicWeight:\n"
267 << " Effective A cannot be changed directly to " << a << ".\n"
268 << " Use SetComposition to define the gas mixture.\n";
269}
270
271void MediumGas::SetNumberDensity(const double n) {
272 std::cerr << m_className << "::SetNumberDensity:\n"
273 << " Density cannot directly be changed to " << n << ".\n"
274 << " Use SetTemperature and SetPressure.\n";
275}
276
277void MediumGas::SetMassDensity(const double rho) {
278 std::cerr << m_className << "::SetMassDensity:\n"
279 << " Density cannot directly be changed to " << rho << ".\n"
280 << " Use SetTemperature, SetPressure and SetComposition.\n";
281}
282
284 // Effective A, weighted by the fractions of the components.
285 double a = 0.;
286 for (unsigned int i = 0; i < m_nComponents; ++i) {
287 a += m_atWeight[i] * m_fraction[i];
288 }
289 return a;
290}
291
293 // Ideal gas law.
294 return LoschmidtNumber * (m_pressure / AtmosphericPressure) *
295 (ZeroCelsius / m_temperature);
296}
297
299 return GetNumberDensity() * GetAtomicWeight() * AtomicMassUnit;
300}
301
303 // Effective Z, weighted by the fractions of the components.
304 double z = 0.;
305 for (unsigned int i = 0; i < m_nComponents; ++i) {
306 z += m_atNum[i] * m_fraction[i];
307 }
308 return z;
309}
310
311bool MediumGas::LoadGasFile(const std::string& filename) {
312
313 // -----------------------------------------------------------------------
314 // GASGET
315 // -----------------------------------------------------------------------
316
317 std::ifstream gasfile;
318 // Open the file.
319 gasfile.open(filename.c_str());
320 // Make sure the file could be opened.
321 if (!gasfile.is_open()) {
322 std::cerr << m_className << "::LoadGasFile:\n"
323 << " Cannot open file " << filename << ".\n";
324 return false;
325 }
326 std::cout << m_className << "::LoadGasFile: Reading " << filename << ".\n";
327
328 ResetTables();
329
330 // Start reading the data.
331 if (m_debug) std::cout << m_className << "::LoadGasFile: Reading header.\n";
332 int version = 12;
333 // GASOK bits
334 std::bitset<20> gasok;
335 // Gas composition
336 constexpr int nMagboltzGases = 60;
337 std::vector<double> mixture(nMagboltzGases, 0.);
338 if (!ReadHeader(gasfile, version, gasok, m_tab2d, mixture,
340 gasfile.close();
341 return false;
342 }
343 std::cout << m_className << "::LoadGasFile: Version " << version << "\n";
344
345 // Check the gas mixture.
346 std::vector<std::string> gasnames;
347 std::vector<double> percentages;
348 if (!GetMixture(mixture, version, gasnames, percentages)) {
349 std::cerr << m_className << "::LoadGasFile:\n "
350 << "Cannot determine the gas composition.\n";
351 gasfile.close();
352 return false;
353 }
354
355 m_name = "";
356 m_nComponents = gasnames.size();
357 for (unsigned int i = 0; i < m_nComponents; ++i) {
358 if (i > 0) m_name += "/";
359 m_name += gasnames[i];
360 m_gas[i] = gasnames[i];
361 m_fraction[i] = percentages[i] / 100.;
363 }
364 std::cout << m_className << "::LoadGasFile:\n"
365 << " Gas composition set to " << m_name;
366 if (m_nComponents > 1) {
367 std::cout << " (" << m_fraction[0] * 100;
368 for (unsigned int i = 1; i < m_nComponents; ++i) {
369 std::cout << "/" << m_fraction[i] * 100;
370 }
371 std::cout << ")";
372 }
373 std::cout << "\n";
374
375 const int nE = m_eFields.size();
376 const int nB = m_bFields.size();
377 const int nA = m_bAngles.size();
378 if (m_debug) {
379 std::cout << m_className << "::LoadGasFile:\n " << nE
380 << " electric field(s), " << nB
381 << " magnetic field(s), " << nA << " angle(s).\n";
382 }
383
384 // Decode the GASOK bits.
385 // GASOK(I) : .TRUE. if present
386 // (1) electron drift velocity || E
387 // (2) ion mobility,
388 // (3) longitudinal diffusion || E
389 // (4) Townsend coefficient,
390 // (5) cluster size distribution.
391 // (6) attachment coefficient,
392 // (7) Lorentz angle,
393 // (8) transverse diffusion || ExB and Bt
394 // (9) electron drift velocity || Bt
395 // (10) electron drift velocity || ExB
396 // (11) diffusion tensor
397 // (12) ion dissociation
398 // (13) allocated for SRIM data (not used)
399 // (14) allocated for HEED data (not used)
400 // (15) excitation rates
401 // (16) ionisation rates
402
403 if (gasok[0]) Init(nE, nB, nA, m_eVelE, 0.);
404 if (gasok[1]) Init(nE, nB, nA, m_iMob, 0.);
405 if (gasok[2]) Init(nE, nB, nA, m_eDifL, 0.);
406 if (gasok[3]) {
407 Init(nE, nB, nA, m_eAlp, -30.);
408 Init(nE, nB, nA, m_eAlp0, -30.);
409 }
410 if (gasok[5]) Init(nE, nB, nA, m_eAtt, -30.);
411 if (gasok[6]) Init(nE, nB, nA, m_eLor, -30.);
412 if (gasok[7]) Init(nE, nB, nA, m_eDifT, 0.);
413 if (gasok[8]) Init(nE, nB, nA, m_eVelB, 0.);
414 if (gasok[9]) Init(nE, nB, nA, m_eVelX, 0.);
415 if (gasok[10]) Init(nE, nB, nA, 6, m_eDifM, 0.);
416 if (gasok[11]) Init(nE, nB, nA, m_iDis, -30.);
417 if (gasok[14]) Init(nE, nB, nA, m_excLevels.size(), m_excRates, 0.);
418 if (gasok[15]) Init(nE, nB, nA, m_ionLevels.size(), m_ionRates, 0.);
419
420 // Force re-initialisation of collision rates etc.
421 m_isChanged = true;
422
423 if (m_debug) {
424 const std::string fmt = m_tab2d ? "3D" : "1D";
425 std::cout << m_className << "::LoadGasFile: Reading " << fmt << " table.\n";
426 }
427
428 // Drift velocity along E, Bt and ExB
429 double ve = 0., vb = 0., vx = 0.;
430 // Lorentz angle
431 double lor = 0.;
432 // Longitudinal and transverse diffusion coefficients
433 double dl = 0., dt = 0.;
434 // Townsend and attachment coefficients
435 double alpha = 0., alpha0 = 0., eta = 0.;
436 // Ion mobility and dissociation coefficient
437 double mu = 0., dis = 0.;
438 // Diffusion tensor.
439 std::array<double, 6> diff;
440 // Excitation and ionization rates.
441 const unsigned int nexc = m_excLevels.size();
442 std::vector<double> rexc(nexc, 0.);
443 const unsigned int nion = m_ionLevels.size();
444 std::vector<double> rion(nion, 0.);
445 for (int i = 0; i < nE; i++) {
446 for (int j = 0; j < nA; j++) {
447 for (int k = 0; k < nB; k++) {
448 if (m_tab2d) {
449 ReadRecord3D(gasfile, ve, vb, vx, dl, dt, alpha, alpha0, eta, mu,
450 lor, dis, diff, rexc, rion);
451 } else {
452 ReadRecord1D(gasfile, ve, vb, vx, dl, dt, alpha, alpha0, eta, mu,
453 lor, dis, diff, rexc, rion);
454 }
455 if (!m_eVelE.empty()) m_eVelE[j][k][i] = ve;
456 if (!m_eVelB.empty()) m_eVelB[j][k][i] = vb;
457 if (!m_eVelX.empty()) m_eVelX[j][k][i] = vx;
458 if (!m_eDifL.empty()) m_eDifL[j][k][i] = dl;
459 if (!m_eDifT.empty()) m_eDifT[j][k][i] = dt;
460 if (!m_eAlp.empty()) {
461 m_eAlp[j][k][i] = alpha;
462 m_eAlp0[j][k][i] = alpha0;
463 }
464 if (!m_eAtt.empty()) m_eAtt[j][k][i] = eta;
465 if (!m_iMob.empty()) m_iMob[j][k][i] = mu;
466 if (!m_eLor.empty()) m_eLor[j][k][i] = lor;
467 if (!m_iDis.empty()) m_iDis[j][k][i] = dis;
468 if (!m_eDifM.empty()) {
469 for (int l = 0; l < 6; l++) {
470 m_eDifM[l][j][k][i] = diff[l];
471 }
472 }
473 if (!m_excRates.empty()) {
474 for (unsigned int l = 0; l < nexc; ++l) {
475 m_excRates[l][j][k][i] = rexc[l];
476 }
477 }
478 if (!m_ionRates.empty()) {
479 for (unsigned int l = 0; l < nion; ++l) {
480 m_ionRates[l][j][k][i] = rion[l];
481 }
482 }
483 }
484 }
485 }
486
487 // Extrapolation methods
488 std::array<unsigned int, 13> extrapH = {{0}};
489 std::array<unsigned int, 13> extrapL = {{1}};
490 // Interpolation methods
491 std::array<unsigned int, 13> interp = {{2}};
492 // Ion diffusion coefficients.
493 double ionDiffL = 0.;
494 double ionDiffT = 0.;
495 // Gas pressure [Torr] and temperature [K].
496 double pgas = 0.;
497 double tgas = 0.;
498 // Moving on to the file footer
499 gasfile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
500 if (m_debug) std::cout << m_className << "::LoadGasFile: Reading footer.\n";
501 ReadFooter(gasfile, extrapH, extrapL, interp,
502 m_eThrAlp, m_eThrAtt, m_iThrDis, ionDiffL, ionDiffT, pgas, tgas);
503 gasfile.close();
504
505 // Decrement the threshold indices (compatibility with Fortran).
506 if (m_eThrAlp > 0) --m_eThrAlp;
507 if (m_eThrAtt > 0) --m_eThrAtt;
508 if (m_iThrDis > 0) --m_iThrDis;
509
510 // Set the reference pressure and temperature.
511 if (pgas > 0.) m_pressure = m_pressureTable = pgas;
512 if (tgas > 0.) m_temperature = m_temperatureTable = tgas;
513
514 // Multiply the E/p values by the pressure.
515 for (auto& field : m_eFields) field *= m_pressureTable;
516
517 // Scale the parameters.
518 const double sqrp = sqrt(m_pressureTable);
519 const double logp = log(m_pressureTable);
520 for (int i = nE; i--;) {
521 for (int j = nA; j--;) {
522 for (int k = nB; k--;) {
523 if (!m_eDifL.empty()) m_eDifL[j][k][i] /= sqrp;
524 if (!m_eDifT.empty()) m_eDifT[j][k][i] /= sqrp;
525 if (!m_eDifM.empty()) {
526 for (int l = 6; l--;) m_eDifM[l][j][k][i] /= m_pressureTable;
527 }
528 if (!m_eAlp.empty()) {
529 m_eAlp[j][k][i] += logp;
530 m_eAlp0[j][k][i] += logp;
531 }
532 if (!m_eAtt.empty()) m_eAtt[j][k][i] += logp;
533 if (!m_iDis.empty()) m_iDis[j][k][i] += logp;
534 /*
535 for (auto& exc : m_excRates) {
536 exc[j][k][i] /= m_pressureTable;
537 }
538 for (auto& ion : m_ionRates) {
539 ion[j][k][i] /= m_pressureTable;
540 }
541 */
542 }
543 }
544 }
545
546 // Decode the extrapolation and interpolation tables.
547 m_extrVel = {extrapL[0], extrapH[0]};
548 // Indices 1 and 2 correspond to velocities along Bt and ExB.
549 m_extrDif = {extrapL[3], extrapH[3]};
550 m_extrAlp = {extrapL[4], extrapH[4]};
551 m_extrAtt = {extrapL[5], extrapH[5]};
552 m_extrMob = {extrapL[6], extrapH[6]};
553 m_extrLor = {extrapL[7], extrapH[7]};
554 // Index 8: transverse diffusion.
555 m_extrDis = {extrapL[9], extrapH[9]};
556 // Index 10: diff. tensor
557 m_extrExc = {extrapL[11], extrapH[11]};
558 m_extrIon = {extrapL[12], extrapH[12]};
559 m_intpVel = interp[0];
560 m_intpDif = interp[3];
561 m_intpAlp = interp[4];
562 m_intpAtt = interp[5];
563 m_intpMob = interp[6];
564 m_intpLor = interp[7];
565 m_intpDis = interp[9];
566 m_intpExc = interp[11];
567 m_intpIon = interp[12];
568
569 // Ion diffusion
570 if (ionDiffL > 0.) Init(nE, nB, nA, m_iDifL, ionDiffL);
571 if (ionDiffT > 0.) Init(nE, nB, nA, m_iDifT, ionDiffT);
572
573 if (m_debug) std::cout << m_className << "::LoadGasFile: Done.\n";
574
575 return true;
576}
577
578bool MediumGas::ReadHeader(std::ifstream& gasfile, int& version,
579 std::bitset<20>& gasok, bool& is3d, std::vector<double>& mixture,
580 std::vector<double>& efields, std::vector<double>& bfields,
581 std::vector<double>& angles, std::vector<ExcLevel>& excLevels,
582 std::vector<IonLevel>& ionLevels) {
583
584 gasok.reset();
585 bool done = false;
586 while (!done) {
587 char line[256];
588 gasfile.getline(line, 256);
589 const bool quotes = (strstr(line, "\"") != NULL);
590 if (strncmp(line, " The gas tables follow:", 8) == 0 ||
591 strncmp(line, "The gas tables follow:", 7) == 0) {
592 done = true;
593 break;
594 }
595 char* token = strtok(line, " :,%");
596 while (token) {
597 if (strcmp(token, "Version") == 0) {
598 token = strtok(NULL, " :,%");
599 version = atoi(token);
600 // Check the version number.
601 if (version != 10 && version != 11 && version != 12) {
602 std::cerr << m_className << "::ReadHeader:\n"
603 << " The file has version number " << version << ".\n"
604 << " Files written in this format cannot be read.\n";
605 return false;
606 }
607 } else if (strcmp(token, "GASOK") == 0) {
608 // Get the GASOK bits indicating if a parameter
609 // is present in the table (T) or not (F).
610 token = strtok(NULL, " :,%\t");
611 token = strtok(NULL, " :,%\t");
612 std::string okstr(token);
613 if (m_debug) std::cout << " GASOK bits: " << okstr << "\n";
614 if (okstr.size() < 20) {
615 std::cerr << m_className << "::ReadHeader:\n"
616 << " Unexpected size of GASOK string ("
617 << okstr.size() << ").\n";
618 return false;
619 }
620 for (unsigned int i = 0; i < 20; ++i) {
621 if (okstr[i] == 'T') gasok.set(i);
622 }
623 } else if (strcmp(token, "Identifier") == 0) {
624 // Get the identification string.
625 std::string identifier = "";
626 token = strtok(NULL, "\n");
627 if (token != NULL) identifier += token;
628 if (m_debug) std::cout << " Identifier: " << identifier << "\n";
629 } else if (strcmp(token, "Dimension") == 0) {
630 token = strtok(NULL, " :,%\t");
631 if (strcmp(token, "F") == 0) {
632 is3d = false;
633 } else {
634 is3d = true;
635 }
636 token = strtok(NULL, " :,%\t");
637 const int nE = atoi(token);
638 // Check the number of E points.
639 if (nE <= 0) {
640 std::cerr << m_className << "::ReadHeader:\n"
641 << " Number of E fields out of range.\n";
642 return false;
643 }
644 token = strtok(NULL, " :,%\t");
645 const int nA = atoi(token);
646 // Check the number of angles.
647 if (is3d && nA <= 0) {
648 std::cerr << m_className << "::ReadHeader:\n"
649 << " Number of E-B angles out of range.\n";
650 return false;
651 }
652 token = strtok(NULL, " :,%\t");
653 const int nB = atoi(token);
654 // Check the number of B points.
655 if (is3d && nB <= 0) {
656 std::cerr << m_className << "::ReadHeader:\n"
657 << " Number of B fields out of range.\n";
658 return false;
659 }
660 efields.resize(nE);
661 angles.resize(nA);
662 bfields.resize(nB);
663 // Fill in the excitation/ionisation structs
664 // Excitation
665 token = strtok(NULL, " :,%\t");
666 const int nexc = atoi(token);
667 // Ionization
668 token = strtok(NULL, " :,%\t");
669 const int nion = atoi(token);
670 if (m_debug) {
671 std::cout << " " << nexc << " excitations, " << nion
672 << " ionisations.\n";
673 }
674 } else if (strcmp(token, "E") == 0) {
675 token = strtok(NULL, " :,%");
676 if (strncmp(token, "fields", 6) == 0) {
677 const int nE = efields.size();
678 for (int i = 0; i < nE; ++i) gasfile >> efields[i];
679 }
680 } else if (strcmp(token, "E-B") == 0) {
681 token = strtok(NULL, " :,%");
682 if (strncmp(token, "angles", 6) == 0) {
683 const int nA = angles.size();
684 for (int i = 0; i < nA; ++i) gasfile >> angles[i];
685 }
686 } else if (strcmp(token, "B") == 0) {
687 token = strtok(NULL, " :,%");
688 if (strncmp(token, "fields", 6) == 0) {
689 double bstore = 0.;
690 const int nB = bfields.size();
691 for (int i = 0; i < nB; i++) {
692 gasfile >> bstore;
693 bfields[i] = bstore / 100.;
694 }
695 }
696 } else if (strcmp(token, "Mixture") == 0) {
697 const unsigned int nMagboltzGases = mixture.size();
698 for (unsigned int i = 0; i < nMagboltzGases; ++i) {
699 gasfile >> mixture[i];
700 }
701 } else if (strcmp(token, "Excitation") == 0) {
702 // Skip number.
703 token = strtok(NULL, " :,%");
704 ExcLevel exc;
705 // Get label.
706 if (quotes) {
707 token = strtok(NULL, "\"");
708 token = strtok(NULL, "\"");
709 } else {
710 token = strtok(NULL, " :,%");
711 }
712 exc.label = token;
713 // Get energy.
714 token = strtok(NULL, " :,%");
715 exc.energy = atof(token);
716 // Get Penning probability.
717 token = strtok(NULL, " :,%");
718 exc.prob = atof(token);
719 exc.rms = 0.;
720 exc.dt = 0.;
721 if (version >= 11) {
722 // Get Penning rms distance.
723 token = strtok(NULL, " :,%");
724 if (token) {
725 exc.rms = atof(token);
726 // Get decay time.
727 token = strtok(NULL, " :,%");
728 if (token) exc.dt = atof(token);
729 }
730 }
731 excLevels.push_back(std::move(exc));
732 } else if (strcmp(token, "Ionisation") == 0) {
733 // Skip number.
734 token = strtok(NULL, " :,%");
735 // Get label.
736 IonLevel ion;
737 if (quotes) {
738 token = strtok(NULL, "\"");
739 token = strtok(NULL, "\"");
740 } else {
741 token = strtok(NULL, " :,%");
742 }
743 ion.label += token;
744 // Get energy.
745 token = strtok(NULL, " :,%");
746 ion.energy = atof(token);
747 ionLevels.push_back(std::move(ion));
748 }
749 token = strtok(NULL, " :,%");
750 }
751 }
752 return true;
753}
754
755void MediumGas::ReadRecord3D(std::ifstream& gasfile,
756 double& ve, double& vb, double& vx, double& dl, double& dt,
757 double& alpha, double& alpha0, double& eta, double& mu, double& lor,
758 double& dis, std::array<double, 6>& dif,
759 std::vector<double>& rexc, std::vector<double>& rion) {
760
761 // Drift velocity along E, Bt and ExB
762 gasfile >> ve >> vb >> vx;
763 // Convert from cm / us to cm / ns.
764 ve *= 1.e-3;
765 vb *= 1.e-3;
766 vx *= 1.e-3;
767 // Longitudinal and transverse diffusion coefficients
768 gasfile >> dl >> dt;
769 // Townsend and attachment coefficients
770 gasfile >> alpha >> alpha0 >> eta;
771 // Ion mobility
772 gasfile >> mu;
773 // Convert from cm2 / (V us) to cm2 / (V ns)
774 mu *= 1.e-3;
775 // Lorentz angle
776 gasfile >> lor;
777 // Ion dissociation
778 gasfile >> dis;
779 // Diffusion tensor
780 for (int l = 0; l < 6; l++) gasfile >> dif[l];
781 // Excitation rates
782 const unsigned int nexc = rexc.size();
783 for (unsigned int l = 0; l < nexc; ++l) gasfile >> rexc[l];
784 // Ionization rates
785 const unsigned int nion = rion.size();
786 for (unsigned int l = 0; l < nion; ++l) gasfile >> rion[l];
787}
788
789void MediumGas::ReadRecord1D(std::ifstream& gasfile,
790 double& ve, double& vb, double& vx, double& dl, double& dt,
791 double& alpha, double& alpha0, double& eta, double& mu, double& lor,
792 double& dis, std::array<double, 6>& dif,
793 std::vector<double>& rexc, std::vector<double>& rion) {
794
795 double waste = 0.;
796 gasfile >> ve >> waste >> vb >> waste >> vx >> waste;
797 ve *= 1.e-3;
798 vb *= 1.e-3;
799 vx *= 1.e-3;
800 gasfile >> dl >> waste >> dt >> waste;
801 gasfile >> alpha >> waste >> alpha0 >> eta >> waste;
802 gasfile >> mu >> waste;
803 mu *= 1.e-3;
804 gasfile >> lor >> waste;
805 gasfile >> dis >> waste;
806 for (int j = 0; j < 6; j++) gasfile >> dif[j] >> waste;
807 const unsigned int nexc = rexc.size();
808 for (unsigned int j = 0; j < nexc; ++j) gasfile >> rexc[j] >> waste;
809 const unsigned int nion = rion.size();
810 for (unsigned int j = 0; j < nion; ++j) gasfile >> rion[j] >> waste;
811}
812
813void MediumGas::ReadFooter(std::ifstream& gasfile,
814 std::array<unsigned int, 13>& extrapH,
815 std::array<unsigned int, 13>& extrapL,
816 std::array<unsigned int, 13>& interp,
817 unsigned int& thrAlp, unsigned int& thrAtt, unsigned int& thrDis,
818 double& ionDiffL, double& ionDiffT,
819 double& pgas, double& tgas) {
820
821 bool done = false;
822 while (!done) {
823 char line[256];
824 gasfile.getline(line, 256);
825 char* token = strtok(line, " :,%=\t");
826 while (token) {
827 if (strcmp(token, "H") == 0) {
828 token = strtok(NULL, " :,%=\t");
829 for (int i = 0; i < 13; i++) {
830 token = strtok(NULL, " :,%=\t");
831 if (token != NULL) extrapH[i] = atoi(token);
832 }
833 } else if (strcmp(token, "L") == 0) {
834 token = strtok(NULL, " :,%=\t");
835 for (int i = 0; i < 13; i++) {
836 token = strtok(NULL, " :,%=\t");
837 if (token != NULL) extrapL[i] = atoi(token);
838 }
839 } else if (strcmp(token, "Thresholds") == 0) {
840 token = strtok(NULL, " :,%=\t");
841 if (token != NULL) thrAlp = atoi(token);
842 token = strtok(NULL, " :,%=\t");
843 if (token != NULL) thrAtt = atoi(token);
844 token = strtok(NULL, " :,%=\t");
845 if (token != NULL) thrDis = atoi(token);
846 } else if (strcmp(token, "Interp") == 0) {
847 for (int i = 0; i < 13; i++) {
848 token = strtok(NULL, " :,%=\t");
849 if (token != NULL) interp[i] = atoi(token);
850 }
851 } else if (strcmp(token, "A") == 0) {
852 token = strtok(NULL, " :,%=\t");
853 // Parameter for energy loss distribution, currently not used
854 // double a;
855 // if (token != NULL) a = atof(token);
856 } else if (strcmp(token, "Z") == 0) {
857 // Parameter for energy loss distribution, currently not used
858 token = strtok(NULL, " :,%=\t");
859 // double z;
860 // if (token != NULL) z = atof(token);
861 } else if (strcmp(token, "EMPROB") == 0) {
862 // Parameter for energy loss distribution, currently not used
863 token = strtok(NULL, " :,%=\t");
864 // double emprob;
865 // if (token != NULL) emprob = atof(token);
866 } else if (strcmp(token, "EPAIR") == 0) {
867 // Parameter for energy loss distribution, currently not used
868 token = strtok(NULL, " :,%=\t");
869 // double epair;
870 // if (token != NULL) epair = atof(token);
871 } else if (strcmp(token, "Ion") == 0) {
872 // Ion diffusion coefficients
873 token = strtok(NULL, " :,%=\t");
874 token = strtok(NULL, " :,%=\t");
875 if (token != NULL) ionDiffL = atof(token);
876 token = strtok(NULL, " :,%=\t");
877 if (token != NULL) ionDiffT = atof(token);
878 } else if (strcmp(token, "CMEAN") == 0) {
879 // Cluster parameter, currently not used
880 token = strtok(NULL, " :,%=\t");
881 // double clsPerCm;
882 // if (token != NULL) clsPerCm = atof(token);
883 } else if (strcmp(token, "RHO") == 0) {
884 // Parameter for energy loss distribution, currently not used
885 token = strtok(NULL, " :,%=\t");
886 // double rho;
887 // if (token != NULL) rho = atof(token);
888 } else if (strcmp(token, "PGAS") == 0) {
889 // Pressure [Torr]
890 token = strtok(NULL, " :,%=\t");
891 if (token != NULL) pgas = atof(token);
892 } else if (strcmp(token, "TGAS") == 0) {
893 // Temperature [K]
894 token = strtok(NULL, " :,%=\t");
895 if (token != NULL) tgas = atof(token);
896 done = true;
897 break;
898 } else {
899 done = true;
900 break;
901 }
902 token = strtok(NULL, " :,%=\t");
903 }
904 }
905}
906
907bool MediumGas::GetMixture(const std::vector<double>& mixture,
908 const int version, std::vector<std::string>& gasnames,
909 std::vector<double>& percentages) const {
910
911 gasnames.clear();
912 percentages.clear();
913 const unsigned int nMagboltzGases = mixture.size();
914 for (unsigned int i = 0; i < nMagboltzGases; ++i) {
915 if (mixture[i] < Small) continue;
916 const std::string gasname = GetGasName(i + 1, version);
917 if (gasname.empty()) {
918 std::cerr << m_className << "::GetMixture:\n"
919 << " Unknown gas (gas number " << i + 1 << ").\n";
920 return false;
921 }
922 gasnames.push_back(gasname);
923 percentages.push_back(mixture[i]);
924 }
925 if (gasnames.size() > m_nMaxGases) {
926 std::cerr << m_className << "::GetMixture:\n"
927 << " Gas mixture has " << gasnames.size() << " components.\n"
928 << " Number of gases is limited to " << m_nMaxGases << ".\n";
929 return false;
930 } else if (gasnames.empty()) {
931 std::cerr << m_className << "::GetMixture:\n"
932 << " Gas mixture is not defined (zero components).\n";
933 return false;
934 }
935 double sum = std::accumulate(percentages.begin(), percentages.end(), 0.);
936 if (sum != 100.) {
937 std::cout << m_className << "::GetMixture:\n"
938 << " Renormalizing the percentages.\n";
939 for (auto& percentage : percentages) percentage *= 100. / sum;
940 }
941 return true;
942}
943
944bool MediumGas::MergeGasFile(const std::string& filename,
945 const bool replaceOld) {
946
947 // -----------------------------------------------------------------------
948 // GASMRG - Merges gas data from a file with existing gas tables.
949 // (Last changed on 16/ 2/11.)
950 // -----------------------------------------------------------------------
951
952 constexpr double eps = 1.e-3;
953
954 std::ifstream gasfile;
955 // Open the file.
956 gasfile.open(filename.c_str());
957 // Make sure the file could be opened.
958 if (!gasfile.is_open()) {
959 std::cerr << m_className << "::MergeGasFile:\n"
960 << " Cannot open file " << filename << ".\n";
961 return false;
962 }
963
964 int version = 12;
965 std::bitset<20> gasok;
966 bool is3d = false;
967 constexpr int nMagboltzGases = 60;
968 std::vector<double> mixture(nMagboltzGases, 0.);
969 std::vector<double> efields;
970 std::vector<double> bfields;
971 std::vector<double> angles;
972 std::vector<ExcLevel> excLevels;
973 std::vector<IonLevel> ionLevels;
974 if (!ReadHeader(gasfile, version, gasok, is3d, mixture, efields, bfields,
975 angles, excLevels, ionLevels)) {
976 std::cerr << m_className << "::MergeGasFile: Error reading header.\n",
977 gasfile.close();
978 return false;
979 }
980 // Check the version.
981 if (version != 12) {
982 std::cout << m_className << "::MergeGasFile:\n "
983 << "This dataset cannot be read because of a change in format.\n";
984 gasfile.close();
985 return false;
986 }
987
988 // Check the gas composition.
989 std::vector<std::string> gasnames;
990 std::vector<double> percentages;
991 if (!GetMixture(mixture, version, gasnames, percentages)) {
992 std::cerr << m_className << "::MergeGasFile:\n "
993 << "Cannot determine the gas composition.\n";
994 gasfile.close();
995 return false;
996 }
997 if (m_nComponents != gasnames.size()) {
998 std::cerr << m_className << "::MergeGasFile:\n "
999 << "Composition of the dataset differs from the present one.\n";
1000 gasfile.close();
1001 return false;
1002 }
1003
1004 for (unsigned int i = 0; i < m_nComponents; ++i) {
1005 const auto it = std::find(gasnames.begin(), gasnames.end(), m_gas[i]);
1006 if (it == gasnames.end()) {
1007 std::cerr << m_className << "::MergeGasFile:\n "
1008 << "Composition of the dataset differs from the present one.\n";
1009 gasfile.close();
1010 return false;
1011 }
1012 const double f2 = m_fraction[i];
1013 const double f1 = 0.01 * percentages[it - gasnames.begin()];
1014 if (fabs(f1 - f2) > 1.e-6 * (1. + fabs(f1) + fabs(f2))) {
1015 std::cerr << m_className << "::MergeGasFile:\n "
1016 << "Percentages of " << m_gas[i] << " differ.\n";
1017 gasfile.close();
1018 return false;
1019 }
1020 }
1021
1022 // Check that the excitations and ionisations match.
1023 const unsigned int nexc = excLevels.size();
1024 const unsigned int nion = ionLevels.size();
1025 bool excMatch = (m_excLevels.size() == nexc);
1026 if (excMatch) {
1027 for (unsigned int i = 0; i < nexc; ++i) {
1028 if (m_excLevels[i].label == excLevels[i].label) continue;
1029 excMatch = false;
1030 break;
1031 }
1032 }
1033 bool ionMatch = (m_ionLevels.size() == nion);
1034 if (ionMatch) {
1035 for (unsigned int i = 0; i < nion; ++i) {
1036 if (m_ionLevels[i].label == ionLevels[i].label) continue;
1037 ionMatch = false;
1038 break;
1039 }
1040 }
1041
1042 // Drift velocity along E, Bt and ExB
1043 double ve = 0., vb = 0., vx = 0.;
1044 // Lorentz angle
1045 double lor = 0.;
1046 // Longitudinal and transverse diffusion coefficients
1047 double dl = 0., dt = 0.;
1048 // Townsend and attachment coefficients
1049 double alpha = 0., alpha0 = 0., eta = 0.;
1050 // Ion mobility and dissociation coefficient
1051 double mu = 0., dis = 0.;
1052 // Diffusion tensor.
1053 std::array<double, 6> diff;
1054 // Excitation and ionization rates.
1055 std::vector<double> rexc(nexc, 0.);
1056 std::vector<double> rion(nion, 0.);
1057 // Loop through the gas tables to fast-forward to the footer.
1058 const unsigned int nNewE = efields.size();
1059 const unsigned int nNewB = bfields.size();
1060 const unsigned int nNewA = angles.size();
1061 for (unsigned int i = 0; i < nNewE; i++) {
1062 for (unsigned int j = 0; j < nNewA; j++) {
1063 for (unsigned int k = 0; k < nNewB; k++) {
1064 if (m_tab2d) {
1065 ReadRecord3D(gasfile, ve, vb, vx, dl, dt, alpha, alpha0, eta, mu,
1066 lor, dis, diff, rexc, rion);
1067 } else {
1068 ReadRecord1D(gasfile, ve, vb, vx, dl, dt, alpha, alpha0, eta, mu,
1069 lor, dis, diff, rexc, rion);
1070 }
1071 }
1072 }
1073 }
1074
1075 // Extrapolation methods
1076 std::array<unsigned int, 13> extrapH = {{0}};
1077 std::array<unsigned int, 13> extrapL = {{1}};
1078 // Interpolation methods
1079 std::array<unsigned int, 13> interp = {{2}};
1080 // Thresholds.
1081 unsigned int thrAlp = 0, thrAtt = 0, thrDis = 0;
1082 // Ion diffusion coefficients.
1083 double ionDiffL = 0., ionDiffT = 0.;
1084 // Gas pressure [Torr] and temperature [K].
1085 double pgas = 0., tgas = 0.;
1086 // Read the footer.
1087 gasfile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
1088 ReadFooter(gasfile, extrapH, extrapL, interp, thrAlp, thrAtt, thrDis,
1089 ionDiffL, ionDiffT, pgas, tgas);
1090
1091 // Check the pressure and temperature.
1092 if (!Similar(pgas, m_pressureTable, eps)) {
1093 std::cerr << m_className << "::MergeGasFile:\n "
1094 << "The gas pressure of the dataset to be read differs\n "
1095 << "from the current reference pressure; stop.\n";
1096 gasfile.close();
1097 return false;
1098 }
1099 if (!Similar(tgas, m_temperatureTable, eps)) {
1100 std::cerr << m_className << "::MergeGasFile:\n "
1101 << "The gas temperature of the dataset to be read differs\n "
1102 << "from the current reference temperature; stop.\n";
1103 gasfile.close();
1104 return false;
1105 }
1106
1107 // Go back to the start and re-read the header to get to the gas tables.
1108 gasfile.clear();
1109 gasfile.seekg(0);
1110 if (!ReadHeader(gasfile, version, gasok, is3d, mixture, efields, bfields,
1111 angles, excLevels, ionLevels)) {
1112 std::cerr << m_className << "::MergeGasFile: Error re-reading header.\n",
1113 gasfile.close();
1114 return false;
1115 }
1116
1117 // Multiply the E/p values by the pressure.
1118 for (auto& field : efields) field *= pgas;
1119
1120 if (m_debug) {
1121 std::cout << m_className << "::MergeGasFile:\n "
1122 << "Dataset to be merged has the following dimensions:\n "
1123 << "3D = " << is3d << " nE = " << nNewE << ", nB = " << nNewB
1124 << ", nA = " << nNewA << ", nExc = "
1125 << excLevels.size() << ", nIon = " << ionLevels.size() << "\n";
1126 }
1127
1128 unsigned int nE = m_eFields.size();
1129 unsigned int nB = m_bFields.size();
1130 unsigned int nA = m_bAngles.size();
1131 // Determine which mode we have to use for the E field.
1132 const int iemode = Equal(efields, m_eFields, eps);
1133 // Determine which mode we have to use for the angles.
1134 const int iamode = Equal(angles, m_bAngles, eps);
1135 // Determine which mode we have to use for the B field.
1136 const int ibmode = Equal(bfields, m_bFields, eps);
1137 if (m_debug) {
1138 std::cout << m_className << "::MergeGasFile:\n";
1139 if (iemode == 0) std::cout << " The E vectors differ.\n";
1140 else std::cout << " The E vectors are identical.\n";
1141 if (iamode == 0) std::cout << " The angle vectors differ.\n";
1142 else std::cout << " The angle vectors are identical.\n";
1143 if (ibmode == 0) std::cout << " The B vectors differ.\n";
1144 else std::cout << " The B vectors are identical.\n";
1145 }
1146 // Ensure there is a common mode.
1147 if (iemode + iamode + ibmode < 2) {
1148 std::cerr << m_className << "::MergeGasFile:\n Existing data and data "
1149 << "in the file don't have two common axes; not merged.\n";
1150 gasfile.close();
1151 return false;
1152 }
1153 // Decide whether we have to produce a 3D table or a 1D table.
1154 if ((is3d || ibmode * iamode == 0) && !m_tab2d) {
1155 if (m_debug) std::cout << " Expanding existing table to 3D mode.\n";
1156 m_tab2d = true;
1157 }
1158
1159 // Determine which data are currently present.
1160 std::bitset<20> existing;
1161 GetGasBits(existing);
1162 // If the grids don't match, warn for data being lost in the merge.
1163 if (iemode * ibmode * iamode == 0) {
1164 // Check for data currently present which are absent in the new data.
1165 if (existing[0] && !gasok[0]) {
1166 existing.reset(0);
1167 m_eVelE.clear();
1168 PrintAbsentInNew("drift velocity");
1169 }
1170 if (existing[1] && !gasok[1]) {
1171 existing.reset(1);
1172 m_iMob.clear();
1173 PrintAbsentInNew("ion mobility");
1174 }
1175 if (existing[2] && !gasok[2]) {
1176 existing.reset(2);
1177 m_eDifL.clear();
1178 PrintAbsentInNew("longitudinal diffusion");
1179 }
1180 if (existing[3] && !gasok[3]) {
1181 existing.reset(3);
1182 m_eAlp.clear();
1183 PrintAbsentInNew("Townsend coefficient");
1184 }
1185 if (existing[5] && !gasok[5]) {
1186 existing.reset(5);
1187 m_eAtt.clear();
1188 PrintAbsentInNew("attachment coefficient");
1189 }
1190 if (existing[6] && !gasok[6]) {
1191 existing.reset(6);
1192 m_eLor.clear();
1193 PrintAbsentInNew("Lorentz angle");
1194 }
1195 if (existing[7] && !gasok[7]) {
1196 existing.reset(7);
1197 m_eDifT.clear();
1198 PrintAbsentInNew("transverse diffusion");
1199 }
1200 if (existing[8] && !gasok[8]) {
1201 existing.reset(8);
1202 m_eVelB.clear();
1203 PrintAbsentInNew("velocity along Bt");
1204 }
1205 if (existing[9] && !gasok[9]) {
1206 existing.reset(9);
1207 m_eVelX.clear();
1208 PrintAbsentInNew("velocity along ExB");
1209 }
1210 if (existing[10] && !gasok[10]) {
1211 existing.reset(10);
1212 m_eDifM.clear();
1213 PrintAbsentInNew("diffusion tensor");
1214 }
1215 if (existing[11] && !gasok[11]) {
1216 existing.reset(11);
1217 m_iDis.clear();
1218 PrintAbsentInNew("ion dissociation data");
1219 }
1220 if (existing[14] && !gasok[14]) {
1221 existing.reset(14);
1222 m_excLevels.clear();
1223 m_excRates.clear();
1224 PrintAbsentInNew("excitation data");
1225 }
1226 if (existing[15] && !gasok[15]) {
1227 existing.reset(15);
1228 m_ionLevels.clear();
1229 m_ionRates.clear();
1230 PrintAbsentInNew("ionisation data");
1231 }
1232 // And for data present in the file but not currently present.
1233 if (!existing[0] && gasok[0]) {
1234 gasok.reset(0);
1235 PrintAbsentInExisting("drift velocity");
1236 }
1237 if (!existing[1] && gasok[1]) {
1238 gasok.reset(1);
1239 PrintAbsentInExisting("ion mobility");
1240 }
1241 if (!existing[2] && gasok[2]) {
1242 gasok.reset(2);
1243 PrintAbsentInExisting("longitudinal diffusion");
1244 }
1245 if (!existing[3] && gasok[3]) {
1246 gasok.reset(3);
1247 PrintAbsentInExisting("Townsend coefficient");
1248 }
1249 if (!existing[5] && gasok[5]) {
1250 gasok.reset(5);
1251 PrintAbsentInExisting("attachment coefficient");
1252 }
1253 if (!existing[6] && gasok[6]) {
1254 gasok.reset(6);
1255 PrintAbsentInExisting("Lorentz angle");
1256 }
1257 if (!existing[7] && gasok[7]) {
1258 gasok.reset(7);
1259 PrintAbsentInExisting("transverse diffusion");
1260 }
1261 if (!existing[8] && gasok[8]) {
1262 gasok.reset(8);
1263 PrintAbsentInExisting("velocity along Bt");
1264 }
1265 if (!existing[9] && gasok[9]) {
1266 gasok.reset(9);
1267 PrintAbsentInExisting("velocity along ExB");
1268 }
1269 if (!existing[10] && gasok[10]) {
1270 gasok.reset(10);
1271 PrintAbsentInExisting("diffusion tensor");
1272 }
1273 if (!existing[11] && gasok[11]) {
1274 gasok.reset(11);
1275 PrintAbsentInExisting("ion dissociation data");
1276 }
1277 if (!existing[14] && gasok[14]) {
1278 gasok.reset(14);
1279 PrintAbsentInExisting("excitation data");
1280 }
1281 if (!existing[15] && gasok[15]) {
1282 gasok.reset(15);
1283 PrintAbsentInExisting("ionisation data");
1284 }
1285 if (existing[14] && gasok[14] && !excMatch) {
1286 std::cerr << " Excitation levels of the two datasets don't match.\n"
1287 << " Deleting excitation data.\n";
1288 m_excLevels.clear();
1289 m_excRates.clear();
1290 existing.reset(14);
1291 gasok.reset(14);
1292 }
1293 if (existing[15] && gasok[15] && !ionMatch) {
1294 std::cerr << " Ionisation levels of the two datasets don't match.\n"
1295 << " Deleting ionisation data.\n";
1296 m_ionLevels.clear();
1297 m_ionRates.clear();
1298 existing.reset(15);
1299 gasok.reset(15);
1300 }
1301 // TODO: if the existing dataset is 1D (B = 0), then there's no need
1302 // to reset the Lorentz angle and the drift velocities || Bt and || ExB.
1303 } else {
1304 // If the grids are identical, initialise the tables that are only present
1305 // in the new dataset but not in existing one.
1306 if (gasok[0] && !existing[0]) Init(nE, nB, nA, m_eVelE, 0.);
1307 if (gasok[1] && !existing[1]) Init(nE, nB, nA, m_iMob, 0.);
1308 if (gasok[2] && !existing[2]) Init(nE, nB, nA, m_eDifL, 0.);
1309 if (gasok[3] && !existing[3]) {
1310 Init(nE, nB, nA, m_eAlp, -30.);
1311 Init(nE, nB, nA, m_eAlp0, -30.);
1312 }
1313 if (gasok[5] && !existing[5]) Init(nE, nB, nA, m_eAtt, -30.);
1314 if (gasok[6] && !existing[6]) Init(nE, nB, nA, m_eLor, -30.);
1315 if (gasok[7] && !existing[7]) Init(nE, nB, nA, m_eDifT, 0.);
1316 if (gasok[8] && !existing[8]) Init(nE, nB, nA, m_eVelB, 0.);
1317 if (gasok[9] && !existing[9]) Init(nE, nB, nA, m_eVelX, 0.);
1318 if (gasok[10] && !existing[10]) Init(nE, nB, nA, 6, m_eDifM, 0.);
1319 if (gasok[11] && !existing[11]) Init(nE, nB, nA, m_iDis, -30.);
1320 if (gasok[14] && (!existing[14] || replaceOld)) {
1321 Init(nE, nB, nA, nexc, m_excRates, 0.);
1322 }
1323 if (gasok[15] && (!existing[15] || replaceOld)) {
1324 Init(nE, nB, nA, nion, m_ionRates, 0.);
1325 }
1326 }
1327
1328 // Initialise the "new" flags.
1329 std::vector<bool> newE(nE, false);
1330 std::vector<bool> newA(nA, false);
1331 std::vector<bool> newB(nB, false);
1332 // Extend the existing tables.
1333 std::cout << m_className << "::MergeGasFile: Extending the tables.\n";
1334 // Insert room in the tables for new columns in E.
1335 if (iemode == 0) {
1336 // Loop over the new values.
1337 for (const auto efield : efields) {
1338 // Loop over the old values.
1339 bool found = false;
1340 for (unsigned int j = 0; j < nE; ++j) {
1341 // If it overlaps with existing E, either keep old or new data.
1342 if (Similar(efield, m_eFields[j], eps)) {
1343 if (replaceOld) {
1344 std::cout << " Replacing existing data for E = "
1345 << m_eFields[j] << " V/cm by data from file.\n";
1346 m_eFields[j] = efield;
1347 newE[j] = true;
1348 ZeroRowE(j, nB, nA);
1349 } else {
1350 std::cout << " Keeping existing data for E = " << m_eFields[j]
1351 << " V/cm, not using data from the file.\n";
1352 }
1353 found = true;
1354 break;
1355 } else if (efield < m_eFields[j]) {
1356 // Otherwise shift all data at higher E values.
1357 if (m_debug) {
1358 std::cout << " Inserting E = " << efield
1359 << " V/cm at slot " << j << ".\n";
1360 }
1361 InsertE(j, nE, nB, nA);
1362 m_eFields.insert(m_eFields.begin() + j, efield);
1363 newE.insert(newE.begin() + j, true);
1364 ZeroRowE(j, nB, nA);
1365 ++nE;
1366 found = true;
1367 break;
1368 }
1369 }
1370 if (found) continue;
1371 // If there is no higher E, then add the line at the end.
1372 if (m_debug) {
1373 std::cout << " Adding E = " << efield << " V/cm at the end.\n";
1374 }
1375 InsertE(nE, nE, nB, nA);
1376 m_eFields.push_back(efield);
1377 newE.push_back(true);
1378 ZeroRowE(nE, nB, nA);
1379 ++nE;
1380 }
1381 }
1382 // Insert room in the tables for new columns in B.
1383 if (ibmode == 0) {
1384 // Loop over the new values.
1385 for (const auto bfield : bfields) {
1386 // Loop over the old values.
1387 bool found = false;
1388 for (unsigned int j = 0; j < nB; ++j) {
1389 // If it overlaps with existing B, either keep old or new data.
1390 if (Similar(bfield, m_bFields[j], eps)) {
1391 if (replaceOld) {
1392 std::cout << " Replacing old data for B = " << m_bFields[j]
1393 << " T by data from file.\n";
1394 m_bFields[j] = bfield;
1395 newB[j] = true;
1396 ZeroRowB(j, nE, nA);
1397 } else {
1398 std::cout << " Keeping old data for B = " << m_bFields[j]
1399 << " T, not using data from file.\n";
1400 }
1401 found = true;
1402 break;
1403 } else if (bfield < m_bFields[j]) {
1404 // Otherwise shift all data at higher B values.
1405 if (m_debug) {
1406 std::cout << " Inserting B = " << bfield << " T at slot "
1407 << j << ".\n";
1408 }
1409 InsertB(j, nE, nB, nA);
1410 m_bFields.insert(m_bFields.begin() + j, bfield);
1411 newB.insert(newB.begin() + j, true);
1412 ZeroRowB(j, nE, nA);
1413 ++nB;
1414 found = true;
1415 break;
1416 }
1417 }
1418 if (found) continue;
1419 // If there is no higher B, then add the line at the end.
1420 if (m_debug) {
1421 std::cout << " Adding B = " << bfield << " T at the end.\n";
1422 }
1423 InsertB(nB, nE, nB, nA);
1424 m_bFields.push_back(bfield);
1425 newB.push_back(true);
1426 ZeroRowB(nB, nE, nA);
1427 ++nB;
1428 }
1429 }
1430 // Insert room in the tables for new columns in angle.
1431 if (iamode == 0) {
1432 // Loop over the new values.
1433 for (const auto angle : angles) {
1434 // Loop over the old values.
1435 bool found = false;
1436 for (unsigned int j = 0; j < nA; ++j) {
1437 // If it overlaps with an existing angle, either keep old or new data.
1438 if (Similar(angle, m_bAngles[j], eps)) {
1439 if (replaceOld) {
1440 std::cout << " Replacing old data for angle(E,B) = "
1441 << m_bAngles[j] * RadToDegree
1442 << " degrees by data from the file.\n";
1443 m_bAngles[j] = angle;
1444 newA[j] = true;
1445 ZeroRowA(j, nE, nB);
1446 } else {
1447 std::cout << " Keeping old data for angle(E,B) = "
1448 << m_bAngles[j] * RadToDegree
1449 << " degrees, not using data from file.\n";
1450 }
1451 found = true;
1452 break;
1453 } else if (angle < m_bAngles[j]) {
1454 // Otherwise shift all data at higher angles.
1455 if (m_debug) {
1456 std::cout << " Inserting angle = " << angle * RadToDegree
1457 << " degrees at slot " << j << ".\n";
1458 }
1459 InsertA(j, nE, nB, nA);
1460 m_bAngles.insert(m_bAngles.begin() + j, angle);
1461 newA.insert(newA.begin() + j, true);
1462 ZeroRowA(j, nE, nB);
1463 ++nA;
1464 found = true;
1465 break;
1466 }
1467 }
1468 if (found) continue;
1469 // If there is no higher angle, then add the line at the end.
1470 if (m_debug) {
1471 std::cout << " Adding angle = " << angle * RadToDegree
1472 << " degrees at the end.\n";
1473 }
1474 InsertA(nA, nE, nB, nA);
1475 m_bAngles.push_back(angle);
1476 newA.push_back(true);
1477 ZeroRowA(nA, nE, nB);
1478 ++nA;
1479 }
1480 }
1481
1482 const double sqrp = sqrt(pgas);
1483 const double logp = log(pgas);
1484
1485 // Read the gas table.
1486 for (const auto efield : efields) {
1487 // Locate the index at which these values are to be stored.
1488 const int inde = FindIndex(efield, m_eFields, eps);
1489 for (const auto angle : angles) {
1490 const int inda = FindIndex(angle, m_bAngles, eps);
1491 for (const auto bfield : bfields) {
1492 // Read the record.
1493 if (is3d) {
1494 ReadRecord3D(gasfile, ve, vb, vx, dl, dt, alpha, alpha0, eta, mu,
1495 lor, dis, diff, rexc, rion);
1496 } else {
1497 ReadRecord1D(gasfile, ve, vb, vx, dl, dt, alpha, alpha0, eta, mu,
1498 lor, dis, diff, rexc, rion);
1499 }
1500 const int indb = FindIndex(bfield, m_bFields, eps);
1501 if (inde < 0 || inda < 0 || indb < 0) {
1502 std::cerr << m_className << "::MergeGasFile:\n Unable to locate"
1503 << " the (E,angle,B) insertion point; no gas data read.\n";
1504 std::cout << "BFIELD = " << bfield << ", IB = " << indb << "\n";
1505 ResetTables();
1506 gasfile.close();
1507 return false;
1508 }
1509 const bool update = newE[inde] || newA[inda] || newB[indb] || replaceOld;
1510 // Store the data.
1511 if (gasok[0] && (update || !existing[0])) {
1512 m_eVelE[inda][indb][inde] = ve;
1513 }
1514 if (gasok[1] && (update || !existing[1])) {
1515 m_iMob[inda][indb][inde] = mu;
1516 }
1517 if (gasok[2] && (update || !existing[2])) {
1518 m_eDifL[inda][indb][inde] = dl / sqrp;
1519 }
1520 if (gasok[3] && (update || !existing[3])) {
1521 m_eAlp[inda][indb][inde] = alpha + logp;
1522 m_eAlp0[inda][indb][inde] = alpha0 + logp;
1523 }
1524 if (gasok[5] && (update || !existing[5])) {
1525 m_eAtt[inda][indb][inde] = eta + logp;
1526 }
1527 if (gasok[6] && (update || !existing[6])) {
1528 m_eLor[inda][indb][inde] = lor;
1529 }
1530 if (gasok[7] && (update || !existing[7])) {
1531 m_eDifT[inda][indb][inde] = dt / sqrp;
1532 }
1533 if (gasok[8] && (update || !existing[8])) {
1534 m_eVelB[inda][indb][inde] = vb;
1535 }
1536 if (gasok[9] && (update || !existing[9])) {
1537 m_eVelX[inda][indb][inde] = vx;
1538 }
1539 if (gasok[10] && (update || !existing[10])) {
1540 for (unsigned int l = 0; l < 6; ++l) {
1541 m_eDifM[l][inda][indb][inde] = diff[l] / pgas;
1542 }
1543 }
1544 if (gasok[11] && (update || !existing[11])) {
1545 m_iDis[inda][indb][inde] = dis + logp;
1546 }
1547 if (gasok[14] && (update || !existing[14])) {
1548 for (unsigned int l = 0; l < nexc; ++l) {
1549 m_excRates[l][inda][indb][inde] = rexc[l];
1550 }
1551 }
1552 if (gasok[15] && (update || !existing[15])) {
1553 for (unsigned int l = 0; l < nion; ++l) {
1554 m_ionRates[l][inda][indb][inde] = rion[l];
1555 }
1556 }
1557 }
1558 }
1559 }
1560 // if (iemode + iamode + ibmode == 3) { ... }
1561 if (replaceOld) {
1562 if (m_debug) {
1563 std::cout << m_className << "::MergeGasFile: "
1564 << "Replacing extrapolation and interpolation data.\n";
1565 }
1566 if (gasok[0]) m_extrVel = {extrapL[0], extrapH[0]};
1567 if (gasok[1]) m_extrMob = {extrapL[6], extrapH[6]};
1568 if (gasok[2]) m_extrDif = {extrapL[3], extrapH[3]};
1569 if (gasok[3]) m_extrAlp = {extrapL[4], extrapH[4]};
1570 if (gasok[5]) m_extrAtt = {extrapL[5], extrapH[5]};
1571 if (gasok[6]) m_extrLor = {extrapL[7], extrapH[7]};
1572 if (gasok[11]) m_extrDis = {extrapL[9], extrapH[9]};
1573 if (gasok[14]) m_extrExc = {extrapL[11], extrapH[11]};
1574 if (gasok[15]) m_extrIon = {extrapL[12], extrapH[12]};
1575
1576 if (gasok[0]) m_intpVel = interp[0];
1577 if (gasok[1]) m_intpMob = interp[6];
1578 if (gasok[2]) m_intpDif = interp[3];
1579 if (gasok[3]) m_intpAlp = interp[4];
1580 if (gasok[5]) m_intpAtt = interp[5];
1581 if (gasok[6]) m_intpLor = interp[7];
1582 if (gasok[11]) m_intpDis = interp[9];
1583 if (gasok[14]) m_intpExc = interp[11];
1584 if (gasok[15]) m_intpIon = interp[12];
1585
1586 // Ion diffusion.
1587 if (m_debug && (ionDiffL > 0. || ionDiffT > 0.)) {
1588 std::cout << m_className << "::MergeGasFile: Replacing ion diffusion.\n";
1589 }
1590 if (ionDiffL > 0.) Init(nE, nB, nA, m_iDifL, ionDiffL);
1591 if (ionDiffT > 0.) Init(nE, nB, nA, m_iDifT, ionDiffT);
1592 }
1593 // Update the Townsend and attachment threshold indices.
1596 return true;
1597}
1598
1599void MediumGas::InsertE(const int ie, const int ne, const int nb,
1600 const int na) {
1601 for (int k = 0; k < na; ++k) {
1602 for (int j = 0; j < nb; ++j) {
1603 if (!m_eVelE.empty()) m_eVelE[k][j].resize(ne + 1, 0.);
1604 if (!m_eVelB.empty()) m_eVelB[k][j].resize(ne + 1, 0.);
1605 if (!m_eVelX.empty()) m_eVelX[k][j].resize(ne + 1, 0.);
1606 if (!m_eDifL.empty()) m_eDifL[k][j].resize(ne + 1, 0.);
1607 if (!m_eDifT.empty()) m_eDifT[k][j].resize(ne + 1, 0.);
1608 if (!m_eAlp.empty()) m_eAlp[k][j].resize(ne + 1, 0.);
1609 if (!m_eAlp0.empty()) m_eAlp0[k][j].resize(ne + 1, 0.);
1610 if (!m_eAtt.empty()) m_eAtt[k][j].resize(ne + 1, 0.);
1611 if (!m_eLor.empty()) m_eLor[k][j].resize(ne + 1, 0.);
1612 if (!m_iMob.empty()) m_iMob[k][j].resize(ne + 1, 0.);
1613 if (!m_iDis.empty()) m_iDis[k][j].resize(ne + 1, 0.);
1614 if (!m_iDifL.empty()) m_iDifL[k][j].resize(ne + 1, 0.);
1615 if (!m_iDifT.empty()) m_iDifT[k][j].resize(ne + 1, 0.);
1616 for (auto& dif : m_eDifM) dif[k][j].resize(ne + 1, 0.);
1617 for (auto& exc : m_excRates) exc[k][j].resize(ne + 1, 0.);
1618 for (auto& ion : m_ionRates) ion[k][j].resize(ne + 1, 0.);
1619 for (int i = ne; i > ie; --i) {
1620 if (!m_eVelE.empty()) m_eVelE[k][j][i] = m_eVelE[k][j][i - 1];
1621 if (!m_eVelB.empty()) m_eVelB[k][j][i] = m_eVelB[k][j][i - 1];
1622 if (!m_eVelX.empty()) m_eVelX[k][j][i] = m_eVelX[k][j][i - 1];
1623 if (!m_eDifL.empty()) m_eDifL[k][j][i] = m_eDifL[k][j][i - 1];
1624 if (!m_eDifT.empty()) m_eDifT[k][j][i] = m_eDifT[k][j][i - 1];
1625 if (!m_eAlp.empty()) m_eAlp[k][j][i] = m_eAlp[k][j][i - 1];
1626 if (!m_eAlp0.empty()) m_eAlp0[k][j][i] = m_eAlp0[k][j][i - 1];
1627 if (!m_eAtt.empty()) m_eAtt[k][j][i] = m_eAtt[k][j][i - 1];
1628 if (!m_eLor.empty()) m_eLor[k][j][i] = m_eLor[k][j][i - 1];
1629 if (!m_iMob.empty()) m_iMob[k][j][i] = m_iMob[k][j][i - 1];
1630 if (!m_iDis.empty()) m_iDis[k][j][i] = m_iDis[k][j][i - 1];
1631 if (!m_iDifL.empty()) m_iDifL[k][j][i] = m_iDifL[k][j][i - 1];
1632 if (!m_iDifT.empty()) m_iDifT[k][j][i] = m_iDifT[k][j][i - 1];
1633 for (auto& dif : m_eDifM) dif[k][j][i] = dif[k][j][i - 1];
1634 for (auto& exc : m_excRates) exc[k][j][i] = exc[k][j][i - 1];
1635 for (auto& ion : m_ionRates) ion[k][j][i] = ion[k][j][i - 1];
1636 }
1637 }
1638 }
1639}
1640
1641void MediumGas::InsertB(const int ib, const int ne, const int nb,
1642 const int na) {
1643 for (int k = 0; k < na; ++k) {
1644 if (!m_eVelE.empty()) m_eVelE[k].resize(nb + 1, std::vector<double>(ne, 0.));
1645 if (!m_eVelB.empty()) m_eVelB[k].resize(nb + 1, std::vector<double>(ne, 0.));
1646 if (!m_eVelX.empty()) m_eVelX[k].resize(nb + 1, std::vector<double>(ne, 0.));
1647 if (!m_eDifL.empty()) m_eDifL[k].resize(nb + 1, std::vector<double>(ne, 0.));
1648 if (!m_eDifT.empty()) m_eDifT[k].resize(nb + 1, std::vector<double>(ne, 0.));
1649 if (!m_eAlp.empty()) m_eAlp[k].resize(nb + 1, std::vector<double>(ne, 0.));
1650 if (!m_eAlp0.empty()) m_eAlp0[k].resize(nb + 1, std::vector<double>(ne, 0.));
1651 if (!m_eAtt.empty()) m_eAtt[k].resize(nb + 1, std::vector<double>(ne, 0.));
1652 if (!m_eLor.empty()) m_eLor[k].resize(nb + 1, std::vector<double>(ne, 0.));
1653 if (!m_iMob.empty()) m_iMob[k].resize(nb + 1, std::vector<double>(ne, 0.));
1654 if (!m_iDis.empty()) m_iDis[k].resize(nb + 1, std::vector<double>(ne, 0.));
1655 if (!m_iDifL.empty()) m_iDifL[k].resize(nb + 1, std::vector<double>(ne, 0.));
1656 if (!m_iDifT.empty()) m_iDifT[k].resize(nb + 1, std::vector<double>(ne, 0.));
1657 for (auto& dif : m_eDifM) {
1658 dif[k].resize(nb + 1, std::vector<double>(ne, 0.));
1659 }
1660 for (auto& exc : m_excRates) {
1661 exc[k].resize(nb + 1, std::vector<double>(ne, 0.));
1662 }
1663 for (auto& ion : m_ionRates) {
1664 ion[k].resize(nb + 1, std::vector<double>(ne, 0.));
1665 }
1666 for (int i = 0; i < ne; ++i) {
1667 for (int j = nb; j > ib; j--) {
1668 if (!m_eVelE.empty()) m_eVelE[k][j][i] = m_eVelE[k][j - 1][i];
1669 if (!m_eVelB.empty()) m_eVelB[k][j][i] = m_eVelB[k][j - 1][i];
1670 if (!m_eVelX.empty()) m_eVelX[k][j][i] = m_eVelX[k][j - 1][i];
1671 if (!m_eDifL.empty()) m_eDifL[k][j][i] = m_eDifL[k][j - 1][i];
1672 if (!m_eDifT.empty()) m_eDifT[k][j][i] = m_eDifT[k][j - 1][i];
1673 if (!m_eAlp.empty()) m_eAlp[k][j][i] = m_eAlp[k][j - 1][i];
1674 if (!m_eAlp0.empty()) m_eAlp0[k][j][i] = m_eAlp0[k][j - 1][i];
1675 if (!m_eAtt.empty()) m_eAtt[k][j][i] = m_eAtt[k][j - 1][i];
1676 if (!m_eLor.empty()) m_eLor[k][j][i] = m_eLor[k][j - 1][i];
1677 if (!m_iMob.empty()) m_iMob[k][j][i] = m_iMob[k][j - 1][i];
1678 if (!m_iDis.empty()) m_iDis[k][j][i] = m_iDis[k][j - 1][i];
1679 if (!m_iDifL.empty()) m_iDifL[k][j][i] = m_iDifL[k][j - 1][i];
1680 if (!m_iDifT.empty()) m_iDifT[k][j][i] = m_iDifT[k][j - 1][i];
1681 for (auto& dif : m_eDifM) dif[k][j][i] = dif[k][j - 1][i];
1682 for (auto& exc : m_excRates) exc[k][j][i] = exc[k][j - 1][i];
1683 for (auto& ion : m_ionRates) ion[k][j][i] = ion[k][j - 1][i];
1684 }
1685 }
1686 }
1687}
1688
1689void MediumGas::InsertA(const int ia, const int ne, const int nb,
1690 const int na) {
1691 ResizeA(m_eVelE, ne, nb, na + 1);
1692 ResizeA(m_eVelB, ne, nb, na + 1);
1693 ResizeA(m_eVelX, ne, nb, na + 1);
1694 ResizeA(m_eDifL, ne, nb, na + 1);
1695 ResizeA(m_eDifT, ne, nb, na + 1);
1696 ResizeA(m_eAlp, ne, nb, na + 1);
1697 ResizeA(m_eAlp0, ne, nb, na + 1);
1698 ResizeA(m_eAtt, ne, nb, na + 1);
1699 ResizeA(m_eLor, ne, nb, na + 1);
1700 ResizeA(m_iMob, ne, nb, na + 1);
1701 ResizeA(m_iDis, ne, nb, na + 1);
1702 ResizeA(m_iDifL, ne, nb, na + 1);
1703 ResizeA(m_iDifT, ne, nb, na + 1);
1704 for (auto& dif : m_eDifM) ResizeA(dif, ne, nb, na + 1);
1705 for (auto& exc : m_excRates) ResizeA(exc, ne, nb, na + 1);
1706 for (auto& ion : m_ionRates) ResizeA(ion, ne, nb, na + 1);
1707 for (int j = 0; j < nb; ++j) {
1708 for (int i = 0; i < ne; ++i) {
1709 for (int k = na; k > ia; k--) {
1710 if (!m_eVelE.empty()) m_eVelE[k][j][i] = m_eVelE[k - 1][j][i];
1711 if (!m_eVelB.empty()) m_eVelB[k][j][i] = m_eVelB[k - 1][j][i];
1712 if (!m_eVelX.empty()) m_eVelX[k][j][i] = m_eVelX[k - 1][j][i];
1713 if (!m_eDifL.empty()) m_eDifL[k][j][i] = m_eDifL[k - 1][j][i];
1714 if (!m_eDifT.empty()) m_eDifT[k][j][i] = m_eDifT[k - 1][j][i];
1715 if (!m_eAlp.empty()) m_eAlp[k][j][i] = m_eAlp[k - 1][j][i];
1716 if (!m_eAlp0.empty()) m_eAlp0[k][j][i] = m_eAlp0[k - 1][j][i];
1717 if (!m_eAtt.empty()) m_eAtt[k][j][i] = m_eAtt[k - 1][j][i];
1718 if (!m_eLor.empty()) m_eLor[k][j][i] = m_eLor[k - 1][j][i];
1719 if (!m_iMob.empty()) m_iMob[k][j][i] = m_iMob[k - 1][j][i];
1720 if (!m_iDis.empty()) m_iDis[k][j][i] = m_iDis[k - 1][j][i];
1721 if (!m_iDifL.empty()) m_iDifL[k][j][i] = m_iDifL[k - 1][j][i];
1722 if (!m_iDifT.empty()) m_iDifT[k][j][i] = m_iDifT[k - 1][j][i];
1723 for (auto& dif : m_eDifM) dif[k][j][i] = dif[k - 1][j][i];
1724 for (auto& exc : m_excRates) exc[k][j][i] = exc[k - 1][j][i];
1725 for (auto& ion : m_ionRates) ion[k][j][i] = ion[k - 1][j][i];
1726 }
1727 }
1728 }
1729}
1730
1731void MediumGas::ZeroRowE(const int ie, const int nb, const int na) {
1732 for (int k = 0; k < na; ++k) {
1733 for (int j = 0; j < nb; ++j) {
1734 if (!m_eVelE.empty()) m_eVelE[k][j][ie] = 0.;
1735 }
1736 }
1737}
1738
1739void MediumGas::ZeroRowB(const int ib, const int ne, const int na) {
1740 for (int k = 0; k < na; ++k) {
1741 for (int i = 0; i < ne; ++i) {
1742 if (!m_eVelE.empty()) m_eVelE[k][ib][i] = 0.;
1743 }
1744 }
1745}
1746
1747void MediumGas::ZeroRowA(const int ia, const int ne, const int nb) {
1748 for (int j = 0; j < nb; ++j) {
1749 for (int i = 0; i < ne; ++i) {
1750 if (!m_eVelE.empty()) m_eVelE[ia][j][i] = 0.;
1751 }
1752 }
1753}
1754
1755bool MediumGas::WriteGasFile(const std::string& filename) {
1756
1757 // -----------------------------------------------------------------------
1758 // GASWRT
1759 // -----------------------------------------------------------------------
1760
1761 // Set the gas mixture.
1762 constexpr int nMagboltzGases = 60;
1763 std::vector<double> mixture(nMagboltzGases, 0.);
1764 for (unsigned int i = 0; i < m_nComponents; ++i) {
1765 const int ng = GetGasNumberGasFile(m_gas[i]);
1766 if (ng <= 0) {
1767 std::cerr << m_className << "::WriteGasFile:\n"
1768 << " Error retrieving gas number for " << m_gas[i] << ".\n";
1769 continue;
1770 }
1771 mixture[ng - 1] = m_fraction[i] * 100.;
1772 }
1773
1774 if (m_debug) {
1775 std::cout << m_className << "::WriteGasFile:\n"
1776 << " Writing gas tables to file " << filename << "\n";
1777 }
1778
1779 std::ofstream outfile;
1780 outfile.open(filename.c_str(), std::ios::out);
1781 if (!outfile.is_open()) {
1782 std::cerr << m_className << "::WriteGasFile:\n"
1783 << " Cannot open file " << filename << ".\n";
1784 outfile.close();
1785 return false;
1786 }
1787
1788 // Assemble the GASOK bits.
1789 std::bitset<20> gasok;
1790 GetGasBits(gasok);
1791 std::string okstr(20, 'F');
1792 for (unsigned int i = 0; i < 20; ++i) {
1793 if (gasok[i]) okstr[i] = 'T';
1794 }
1795 if (m_debug) std::cout << " GASOK bits: " << okstr << "\n";
1796 // Get the current time.
1797 time_t rawtime = time(0);
1798 tm timeinfo = *localtime(&rawtime);
1799 char datebuf[80] = {0};
1800 char timebuf[80] = {0};
1801 // Format date and time.
1802 strftime(datebuf, sizeof(datebuf) - 1, "%d/%m/%y", &timeinfo);
1803 strftime(timebuf, sizeof(timebuf) - 1, "%H.%M.%S", &timeinfo);
1804 // Set the member name.
1805 std::string member = "< none >";
1806 // Write the header.
1807 outfile << "*----.----1----.----2----.----3----.----4----.----"
1808 << "5----.----6----.----7----.----8----.----9----.---"
1809 << "10----.---11----.---12----.---13--\n";
1810 outfile << "% Created " << datebuf << " at " << timebuf << " ";
1811 outfile << member << " GAS ";
1812 // Add remark.
1813 std::string buffer;
1814 outfile << "\"none" << std::string(25, ' ') << "\"\n";
1815 const int version = 12;
1816 outfile << " Version : " << version << "\n";
1817 outfile << " GASOK bits: " << okstr << "\n";
1818 std::stringstream ids;
1819 ids.str("");
1820 for (unsigned int i = 0; i < m_nComponents; ++i) {
1821 ids << m_gas[i] << " " << 100. * m_fraction[i] << "%, ";
1822 }
1823 ids << "T=" << m_temperatureTable << " K, "
1824 << "p=" << m_pressureTable / AtmosphericPressure << " atm";
1825 outfile << " Identifier: " << std::setw(80) << std::left << ids.str() << "\n";
1826 outfile << " Clusters : " << std::string(80, ' ') << "\n";
1827 outfile << " Dimension : ";
1828 if (m_tab2d) {
1829 outfile << "T ";
1830 } else {
1831 outfile << "F ";
1832 }
1833
1834 const unsigned int nE = m_eFields.size();
1835 const unsigned int nB = m_bFields.size();
1836 const unsigned int nA = m_bAngles.size();
1837 if (m_debug) {
1838 std::cout << m_className << "::WriteGasFile:\n "
1839 << "Dataset has the following dimensions:\n "
1840 << "3D = " << m_tab2d << " nE = " << nE << ", nB = " << nB
1841 << ", nA = " << nA << ", nExc = "
1842 << m_excLevels.size() << ", nIon = " << m_ionLevels.size() << "\n";
1843 }
1844 outfile << FmtInt(nE, 9) << " " << FmtInt(nA, 9) << " "
1845 << FmtInt(nB, 9) << " " << FmtInt(m_excLevels.size(), 9) << " "
1846 << FmtInt(m_ionLevels.size(), 9) << "\n";
1847 // Store reduced electric fields (E/p).
1848 outfile << " E fields \n";
1849 std::vector<double> efields = m_eFields;
1850 for (auto& field : efields) field /= m_pressureTable;
1851 int cnt = 0;
1852 // List 5 values, then new line.
1853 PrintArray(efields, outfile, cnt, 5);
1854 if (nE % 5 != 0) outfile << "\n";
1855 // Store angles.
1856 outfile << " E-B angles \n";
1857 cnt = 0;
1858 PrintArray(m_bAngles, outfile, cnt, 5);
1859 if (nA % 5 != 0) outfile << "\n";
1860 // Store B fields (convert to hGauss).
1861 outfile << " B fields \n";
1862 std::vector<double> bfields = m_bFields;
1863 for (auto& field : bfields) field *= 100.;
1864 cnt = 0;
1865 PrintArray(bfields, outfile, cnt, 5);
1866 if (nB % 5 != 0) outfile << "\n";
1867
1868 // Store the gas composition.
1869 outfile << " Mixture: \n";
1870 cnt = 0;
1871 PrintArray(mixture, outfile, cnt, 5);
1872 if (nMagboltzGases % 5 != 0) outfile << "\n";
1873
1874 cnt = 0;
1875 for (const auto& exc : m_excLevels) {
1876 ++cnt;
1877 outfile << " Excitation " << FmtInt(cnt, 5) << ": " << std::setw(45);
1878 // If the label contains white space, enclose it in quotes.
1879 if (exc.label.find(" ") != std::string::npos) {
1880 outfile << "\"" + exc.label + "\"";
1881 } else {
1882 outfile << exc.label;
1883 }
1884 outfile << " " << FmtFloat(exc.energy) << FmtFloat(exc.prob)
1885 << FmtFloat(exc.rms) << FmtFloat(exc.dt) << "\n";
1886 }
1887 cnt = 0;
1888 for (const auto& ion : m_ionLevels) {
1889 ++cnt;
1890 outfile << " Ionisation " << FmtInt(cnt, 5) << ": " << std::setw(45);
1891 // If the label contains white space, enclose it in quotes.
1892 if (ion.label.find(" ") != std::string::npos) {
1893 outfile << "\"" + ion.label << "\"";
1894 } else {
1895 outfile << ion.label;
1896 }
1897 outfile << " " << FmtFloat(ion.energy) << "\n";
1898 }
1899
1900 const double sqrp = sqrt(m_pressureTable);
1901 const double logp = log(m_pressureTable);
1902 outfile << " The gas tables follow:\n";
1903 cnt = 0;
1904 for (unsigned int i = 0; i < nE; ++i) {
1905 for (unsigned int j = 0; j < nA; ++j) {
1906 for (unsigned int k = 0; k < nB; ++k) {
1907 // Get the velocities.
1908 double ve = m_eVelE.empty() ? 0. : m_eVelE[j][k][i];
1909 double vb = m_eVelB.empty() ? 0. : m_eVelB[j][k][i];
1910 double vx = m_eVelX.empty() ? 0. : m_eVelX[j][k][i];
1911 // Convert from cm / ns to cm / us.
1912 ve *= 1.e3;
1913 vb *= 1.e3;
1914 vx *= 1.e3;
1915 // Make a list of the values to be written, start with the velocities.
1916 std::vector<double> val;
1917 if (m_tab2d) {
1918 val = {ve, vb, vx};
1919 } else {
1920 // Add dummy spline values in case of a 1D table.
1921 val = {ve, 0., vb, 0., vx, 0.};
1922 }
1923 // Get the diffusion coefficients.
1924 double dl = m_eDifL.empty() ? 0. : m_eDifL[j][k][i] * sqrp;
1925 double dt = m_eDifT.empty() ? 0. : m_eDifT[j][k][i] * sqrp;
1926 // Get the Townsend and attachment coefficients.
1927 double alpha = m_eAlp.empty() ? -30. : m_eAlp[j][k][i] - logp;
1928 double alpha0 = m_eAlp0.empty() ? -30. : m_eAlp0[j][k][i] - logp;
1929 double eta = m_eAtt.empty() ? -30. : m_eAtt[j][k][i] - logp;
1930 // Add them to the list.
1931 if (m_tab2d) {
1932 val.insert(val.end(), {dl, dt, alpha, alpha0, eta});
1933 } else {
1934 val.insert(val.end(), {dl, 0., dt, 0., alpha, 0., alpha0, eta, 0.});
1935 }
1936 // Get the ion mobility and convert from cm2 / (V ns) to cm2 / (V us).
1937 double mu = m_iMob.empty() ? 0. : 1.e3 * m_iMob[j][k][i];
1938 // Get the Lorentz angle.
1939 double lor = m_eLor.empty() ? 0 : m_eLor[j][k][i];
1940 // Get the dissociation coefficient.
1941 double diss = m_iDis.empty() ? -30. : m_iDis[j][k][i] - logp;
1942 // Add them to the list.
1943 if (m_tab2d) {
1944 val.insert(val.end(), {mu, lor, diss});
1945 } else {
1946 val.insert(val.end(), {mu, 0., lor, 0., diss, 0.});
1947 }
1948 // Get the components of the diffusion tensor.
1949 for (int l = 0; l < 6; ++l) {
1950 if (!m_eDifM.empty()) {
1951 const double cov = m_eDifM[l][j][k][i] * m_pressureTable;
1952 val.push_back(cov);
1953 } else {
1954 val.push_back(0.);
1955 }
1956 if (!m_tab2d) val.push_back(0.);
1957 }
1958 // Get the excitation and ionisation rates.
1959 for (const auto& rexc : m_excRates) {
1960 if (rexc[j][k][i] > Small) {
1961 val.push_back(rexc[j][k][i]);
1962 } else {
1963 val.push_back(0.);
1964 }
1965 if (!m_tab2d) val.push_back(0.);
1966 }
1967 for (const auto& rion : m_ionRates) {
1968 if (rion[j][k][i] > Small) {
1969 val.push_back(rion[j][k][i]);
1970 } else {
1971 val.push_back(0.);
1972 }
1973 if (!m_tab2d) val.push_back(0.);
1974 }
1975 PrintArray(val, outfile, cnt, 8);
1976 }
1977 if (cnt % 8 != 0) outfile << "\n";
1978 cnt = 0;
1979 }
1980 }
1981
1982 if (!m_tab2d) {
1983 // Extrapolation methods
1984 int extrapH[13], extrapL[13];
1985 extrapL[0] = extrapL[1] = extrapL[2] = m_extrVel.first;
1986 extrapH[0] = extrapH[1] = extrapH[2] = m_extrVel.second;
1987 extrapL[3] = extrapL[8] = extrapL[10] = m_extrDif.first;
1988 extrapH[3] = extrapH[8] = extrapH[10] = m_extrDif.second;
1989 extrapL[4] = m_extrAlp.first;
1990 extrapH[4] = m_extrAlp.second;
1991 extrapL[5] = m_extrAtt.first;
1992 extrapH[5] = m_extrAtt.second;
1993 extrapL[6] = m_extrMob.first;
1994 extrapH[6] = m_extrMob.second;
1995 // Lorentz angle
1996 extrapL[7] = m_extrLor.first;
1997 extrapH[7] = m_extrLor.second;
1998 extrapL[9] = m_extrDis.first;
1999 extrapH[9] = m_extrDis.second;
2000 extrapL[11] = m_extrExc.first;
2001 extrapH[11] = m_extrExc.second;
2002 extrapL[12] = m_extrIon.first;
2003 extrapH[12] = m_extrIon.second;
2004 outfile << " H Extr: ";
2005 for (int i = 0; i < 13; i++) outfile << FmtInt(extrapH[i], 5);
2006 outfile << "\n";
2007 outfile << " L Extr: ";
2008 for (int i = 0; i < 13; i++) outfile << FmtInt(extrapL[i], 5);
2009 outfile << "\n";
2010 }
2011 // Increment the threshold indices for compatibility with Fortran.
2012 outfile << " Thresholds: " << FmtInt(m_eThrAlp + 1, 10)
2013 << FmtInt(m_eThrAtt + 1, 10) << FmtInt(m_iThrDis + 1, 10) << "\n";
2014 // Interpolation methods.
2015 int interp[13];
2016 interp[0] = interp[1] = interp[2] = m_intpVel;
2017 interp[3] = interp[8] = interp[10] = m_intpDif;
2018 interp[4] = m_intpAlp;
2019 interp[5] = m_intpAtt;
2020 interp[6] = m_intpMob;
2021 interp[7] = m_intpLor;
2022 interp[9] = m_intpDis;
2023 interp[11] = m_intpExc;
2024 interp[12] = m_intpIon;
2025 outfile << " Interp: ";
2026 for (int i = 0; i < 13; i++) outfile << FmtInt(interp[i], 5);
2027 outfile << "\n";
2028 outfile << " A =" << FmtFloat(0.) << ", Z =" << FmtFloat(0.) << ","
2029 << " EMPROB=" << FmtFloat(0.) << ", EPAIR =" << FmtFloat(0.) << "\n";
2030 const double dli = m_iDifL.empty() ? 0. : m_iDifL[0][0][0];
2031 const double dti = m_iDifT.empty() ? 0. : m_iDifT[0][0][0];
2032 outfile << " Ion diffusion: " << FmtFloat(dli) << FmtFloat(dti) << "\n";
2033 outfile << " CMEAN =" << FmtFloat(0.) << ", RHO =" << FmtFloat(0.) << ","
2034 << " PGAS =" << FmtFloat(m_pressureTable) << ","
2035 << " TGAS =" << FmtFloat(m_temperatureTable) << "\n";
2036 outfile << " CLSTYP : NOT SET \n"
2037 << " FCNCLS : " << std::string(80, ' ') << "\n"
2038 << " NCLS : " << FmtInt(0, 10) << "\n"
2039 << " Average : " << FmtFloat(0., 25, 18) << "\n"
2040 << " Heed initialisation done: F\n"
2041 << " SRIM initialisation done: F\n";
2042 outfile.close();
2043
2044 return true;
2045}
2046
2047void MediumGas::GetGasBits(std::bitset<20>& gasok) const {
2048
2049 gasok.reset();
2050 if (!m_eVelE.empty()) gasok.set(0);
2051 if (!m_iMob.empty()) gasok.set(1);
2052 if (!m_eDifL.empty()) gasok.set(2);
2053 if (!m_eAlp.empty()) gasok.set(3);
2054 // Cluster size distribution; skipped
2055 if (!m_eAtt.empty()) gasok.set(5);
2056 if (!m_eLor.empty()) gasok.set(6);
2057 if (!m_eDifT.empty()) gasok.set(7);
2058 if (!m_eVelB.empty()) gasok.set(8);
2059 if (!m_eVelX.empty()) gasok.set(9);
2060 if (!m_eDifM.empty()) gasok.set(10);
2061 if (!m_iDis.empty()) gasok.set(11);
2062 // SRIM, HEED; skipped
2063 if (!m_excRates.empty()) gasok.set(14);
2064 if (!m_ionRates.empty()) gasok.set(15);
2065}
2066
2068 // Print a summary.
2069 std::cout << m_className << "::PrintGas:\n"
2070 << " Gas composition: " << m_name;
2071 if (m_nComponents > 1) {
2072 std::cout << " (" << m_fraction[0] * 100;
2073 for (unsigned int i = 1; i < m_nComponents; ++i) {
2074 std::cout << "/" << m_fraction[i] * 100;
2075 }
2076 std::cout << ")";
2077 }
2078 std::cout << "\n";
2079 std::cout << " Pressure: " << m_pressure << " Torr\n"
2080 << " Temperature: " << m_temperature << " K\n"
2081 << " Gas file:\n"
2082 << " Pressure: " << m_pressureTable << " Torr\n"
2083 << " Temperature: " << m_temperatureTable << " K\n";
2084 if (m_eFields.size() > 1) {
2085 std::cout << " Electric field range: " << m_eFields[0] << " - "
2086 << m_eFields.back() << " V/cm in " << m_eFields.size() - 1
2087 << " steps.\n";
2088 } else if (m_eFields.size() == 1) {
2089 std::cout << " Electric field: " << m_eFields[0] << " V/cm\n";
2090 } else {
2091 std::cout << " Electric field range: not set\n";
2092 }
2093 if (m_bFields.size() > 1) {
2094 std::cout << " Magnetic field range: " << m_bFields[0] << " - "
2095 << m_bFields.back() << " T in " << m_bFields.size() - 1
2096 << " steps.\n";
2097 } else if (m_bFields.size() == 1) {
2098 std::cout << " Magnetic field: " << m_bFields[0] << " T\n";
2099 } else {
2100 std::cout << " Magnetic field range: not set\n";
2101 }
2102 if (m_bAngles.size() > 1) {
2103 std::cout << " Angular range: " << m_bAngles[0] << " - "
2104 << m_bAngles.back() << " rad in " << m_bAngles.size() - 1
2105 << " steps.\n";
2106 } else if (m_bAngles.size() == 1) {
2107 std::cout << " Angle between E and B: " << m_bAngles[0] << " rad\n";
2108 } else {
2109 std::cout << " Angular range: not set\n";
2110 }
2111
2112 std::cout << " Available electron transport data:\n";
2113 if (!m_eVelE.empty()) {
2114 std::cout << " Velocity along E\n";
2115 }
2116 if (!m_eVelB.empty()) {
2117 std::cout << " Velocity along Bt\n";
2118 }
2119 if (!m_eVelX.empty()) {
2120 std::cout << " Velocity along ExB\n";
2121 }
2122 if (!(m_eVelE.empty() && m_eVelB.empty() &&
2123 m_eVelX.empty())) {
2124 PrintExtrapolation(m_extrVel);
2125 std::cout << " Interpolation order: " << m_intpVel << "\n";
2126 }
2127 if (!m_eDifL.empty()) {
2128 std::cout << " Longitudinal diffusion coefficient\n";
2129 }
2130 if (!m_eDifT.empty()) {
2131 std::cout << " Transverse diffusion coefficient\n";
2132 }
2133 if (!m_eDifM.empty()) {
2134 std::cout << " Diffusion tensor\n";
2135 }
2136 if (!m_eDifL.empty() || !m_eDifT.empty() || !m_eDifM.empty()) {
2137 PrintExtrapolation(m_extrDif);
2138 std::cout << " Interpolation order: " << m_intpDif << "\n";
2139 }
2140 if (!m_eAlp.empty()) {
2141 std::cout << " Townsend coefficient\n";
2142 PrintExtrapolation(m_extrAlp);
2143 std::cout << " Interpolation order: " << m_intpAlp << "\n";
2144 }
2145 if (!m_eAtt.empty()) {
2146 std::cout << " Attachment coefficient\n";
2147 PrintExtrapolation(m_extrAtt);
2148 std::cout << " Interpolation order: " << m_intpAtt << "\n";
2149 }
2150 if (!m_eLor.empty()) {
2151 std::cout << " Lorentz Angle\n";
2152 PrintExtrapolation(m_extrLor);
2153 std::cout << " Interpolation order: " << m_intpLor << "\n";
2154 }
2155 if (!m_excRates.empty()) {
2156 std::cout << " Excitation rates\n";
2157 for (const auto& exc : m_excLevels) {
2158 std::cout << " " << exc.label << "\n";
2159 std::cout << " Energy = " << exc.energy << " eV";
2160 if (exc.prob > 0.) {
2161 std::cout << ", Penning transfer probability = " << exc.prob;
2162 }
2163 std::cout << "\n";
2164 }
2165 PrintExtrapolation(m_extrExc);
2166 std::cout << " Interpolation order: " << m_intpExc << "\n";
2167 }
2168 if (!m_ionRates.empty()) {
2169 std::cout << " Ionisation rates\n";
2170 for (const auto& ion : m_ionLevels) {
2171 std::cout << " " << ion.label << "\n";
2172 std::cout << " Threshold = " << ion.energy << " eV\n";
2173 }
2174 PrintExtrapolation(m_extrIon);
2175 std::cout << " Interpolation order: " << m_intpIon << "\n";
2176 }
2177 if (m_eVelE.empty() && m_eVelB.empty() && m_eVelX.empty() &&
2178 m_eDifL.empty() && m_eDifT.empty() && m_eDifM.empty() &&
2179 m_eAlp.empty() && m_eAtt.empty() && m_excRates.empty() &&
2180 m_ionRates.empty() && m_eLor.empty()) {
2181 std::cout << " none\n";
2182 }
2183
2184 std::cout << " Available ion transport data:\n";
2185 if (!m_iMob.empty()) {
2186 std::cout << " Mobility\n";
2187 PrintExtrapolation(m_extrMob);
2188 std::cout << " Interpolation order: " << m_intpMob << "\n";
2189 }
2190 if (!m_iDifL.empty()) {
2191 std::cout << " Longitudinal diffusion coefficient\n";
2192 }
2193 if (!m_iDifT.empty()) {
2194 std::cout << " Transverse diffusion coefficient\n";
2195 }
2196 if (!m_iDifL.empty() || !m_iDifT.empty()) {
2197 PrintExtrapolation(m_extrDif);
2198 std::cout << " Interpolation order: " << m_intpDif << "\n";
2199 }
2200 if (!m_iDis.empty()) {
2201 std::cout << " Dissociation coefficient\n";
2202 PrintExtrapolation(m_extrDis);
2203 std::cout << " Interpolation order: " << m_intpDis << "\n";
2204 }
2205 if (m_iMob.empty() && m_iDifL.empty() && m_iDifT.empty() && m_iDis.empty()) {
2206 std::cout << " none\n";
2207 }
2208}
2209
2210bool MediumGas::LoadIonMobility(const std::string& filename) {
2211 // Open the file.
2212 std::ifstream infile;
2213 infile.open(filename.c_str(), std::ios::in);
2214 // Make sure the file could actually be opened.
2215 if (!infile) {
2216 std::cerr << m_className << "::LoadIonMobility:\n"
2217 << " Error opening file " << filename << ".\n";
2218 ;
2219 return false;
2220 }
2221
2222 double field = -1., mu = -1.;
2223 double lastField = field;
2224 std::vector<double> efields;
2225 std::vector<double> mobilities;
2226
2227 // Read the file line by line.
2228 int i = 0;
2229 while (!infile.eof()) {
2230 ++i;
2231 // Read the next line.
2232 char line[100];
2233 infile.getline(line, 100);
2234 char* token = strtok(line, " ,\t");
2235 if (!token) {
2236 break;
2237 } else if (strcmp(token, "#") == 0 || strcmp(token, "*") == 0 ||
2238 strcmp(token, "//") == 0) {
2239 continue;
2240 } else {
2241 field = atof(token);
2242 token = strtok(NULL, " ,\t");
2243 if (!token) {
2244 std::cerr << m_className << "::LoadIonMobility:\n"
2245 << " Found E/N but no mobility before the end-of-line.\n"
2246 << " Skipping line " << i << ".\n";
2247 continue;
2248 }
2249 mu = atof(token);
2250 }
2251 token = strtok(NULL, " ,\t");
2252 if (token && strcmp(token, "//") != 0) {
2253 std::cerr << m_className << "::LoadIonMobility:\n"
2254 << " Unexpected non-comment characters after the mobility.\n"
2255 << " Skipping line " << i << ".\n";
2256 continue;
2257 }
2258 if (m_debug) {
2259 std::cout << " E/N = " << field << " Td: mu = " << mu << " cm2/(Vs)\n";
2260 }
2261 // Check if the data has been read correctly.
2262 if (infile.fail() && !infile.eof()) {
2263 std::cerr << m_className << "::LoadIonMobility:\n";
2264 std::cerr << " Error reading file\n";
2265 std::cerr << " " << filename << " (line " << i << ").\n";
2266 return false;
2267 }
2268 // Make sure the values make sense.
2269 // Negative field values are not allowed.
2270 if (field < 0.) {
2271 std::cerr << m_className << "::LoadIonMobility:\n";
2272 std::cerr << " Negative electric field (line " << i << ").\n";
2273 return false;
2274 }
2275 // The table has to be in ascending order.
2276 if (field <= lastField) {
2277 std::cerr << m_className << "::LoadIonMobility:\n";
2278 std::cerr << " Table is not in ascending order (line " << i << ").\n";
2279 return false;
2280 }
2281 // Add the values to the list.
2282 efields.push_back(field);
2283 mobilities.push_back(mu);
2284 lastField = field;
2285 }
2286
2287 const int ne = efields.size();
2288 if (ne <= 0) {
2289 std::cerr << m_className << "::LoadIonMobilities:\n";
2290 std::cerr << " No valid data found.\n";
2291 return false;
2292 }
2293
2294 // The E/N values in the file are supposed to be in Td (10^-17 V cm2).
2295 const double scaleField = 1.e-17 * GetNumberDensity();
2296 // The reduced mobilities in the file are supposed to be in cm2/(V s).
2297 const double scaleMobility = 1.e-9 * (AtmosphericPressure / m_pressure) *
2298 (m_temperature / ZeroCelsius);
2299 for (int j = ne; j--;) {
2300 // Scale the fields and mobilities.
2301 efields[j] *= scaleField;
2302 mobilities[j] *= scaleMobility;
2303 }
2304
2305 std::cout << m_className << "::LoadIonMobility:\n";
2306 std::cout << " Read " << ne << " values from file " << filename << "\n";
2307
2308 return SetIonMobility(efields, mobilities);
2309}
2310
2312
2314 m_eAlp0.clear();
2315 m_excLevels.clear();
2316 m_ionLevels.clear();
2317 m_excRates.clear();
2318 m_ionRates.clear();
2319}
2320
2322 const double lambda) {
2323
2324 if (r < 0. || r > 1.) {
2325 std::cerr << m_className << "::EnablePenningTransfer:\n"
2326 << " Transfer probability must be in the range [0, 1].\n";
2327 return false;
2328 }
2329
2330 m_rPenningGlobal = r;
2331 m_lambdaPenningGlobal = lambda > Small ? lambda : 0.;
2332
2333 std::cout << m_className << "::EnablePenningTransfer:\n"
2334 << " Global Penning transfer parameters set to:\n"
2335 << " r = " << m_rPenningGlobal << "\n"
2336 << " lambda = " << m_lambdaPenningGlobal << " cm\n";
2337
2338 // Find the min. ionisation energy.
2339 if (m_ionLevels.empty()) {
2340 std::cerr << m_className << "::EnablePenningTransfer:\n Warning: present"
2341 << " gas table has no ionisation rates.\n Ignore this message "
2342 << "if you are using microscopic tracking only.\n";
2343 return true;
2344 }
2345 double minIonPot = -1.;
2346 for (const auto& ion : m_ionLevels) {
2347 if (minIonPot < 0.) {
2348 minIonPot = ion.energy;
2349 } else {
2350 minIonPot = std::min(minIonPot, ion.energy);
2351 }
2352 }
2353
2354 // Update the transfer probabilities of the excitation levels in the table.
2355 unsigned int nLevelsFound = 0;
2356 for (auto& exc : m_excLevels) {
2357 if (exc.energy < minIonPot) continue;
2358 exc.prob = m_rPenningGlobal;
2359 exc.rms = m_lambdaPenningGlobal;
2360 ++nLevelsFound;
2361 }
2362 if (nLevelsFound > 0) {
2363 std::cout << m_className << "::EnablePenningTransfer:\n"
2364 << " Updated transfer probabilities for " << nLevelsFound
2365 << " excitation rates.\n";
2367 } else {
2368 std::cerr << m_className << "::EnablePenningTransfer:\n Warning: present"
2369 << " gas table has no eligible excitation rates.\n Ignore this"
2370 << " message if you are using microscopic tracking only.\n";
2371 }
2372 return true;
2373}
2374
2375bool MediumGas::EnablePenningTransfer(const double r, const double lambda,
2376 std::string gasname) {
2377
2378 if (r < 0. || r > 1.) {
2379 std::cerr << m_className << "::EnablePenningTransfer:\n"
2380 << " Transfer probability must be in the range [0, 1].\n";
2381 return false;
2382 }
2383
2384 // Get the "standard" name of this gas.
2385 gasname = GetGasName(gasname);
2386 if (gasname.empty()) {
2387 std::cerr << m_className << "::EnablePenningTransfer: Unknown gas name.\n";
2388 return false;
2389 }
2390
2391 // Look for this gas in the present gas mixture.
2392 int iGas = -1;
2393 for (unsigned int i = 0; i < m_nComponents; ++i) {
2394 if (m_gas[i] == gasname) {
2395 m_rPenningGas[i] = r;
2396 m_lambdaPenningGas[i] = lambda > Small ? lambda : 0.;
2397 iGas = i;
2398 break;
2399 }
2400 }
2401
2402 if (iGas < 0) {
2403 std::cerr << m_className << "::EnablePenningTransfer:\n"
2404 << " Requested gas (" << gasname
2405 << ") is not part of the present gas mixture.\n";
2406 return false;
2407 }
2408
2409 // Find the min. ionisation energy.
2410 if (m_ionLevels.empty()) {
2411 std::cerr << m_className << "::EnablePenningTransfer:\n Warning: present"
2412 << " gas table has no ionisation rates.\n Ignore this message"
2413 << " if you are using microscopic tracking only.\n";
2414 return true;
2415 }
2416 double minIonPot = -1.;
2417 for (const auto& ion : m_ionLevels) {
2418 if (minIonPot < 0.) {
2419 minIonPot = ion.energy;
2420 } else {
2421 minIonPot = std::min(minIonPot, ion.energy);
2422 }
2423 }
2424 // Update the transfer probabilities of the excitation levels in the table.
2425 unsigned int nLevelsFound = 0;
2426 for (auto& exc : m_excLevels) {
2427 if (exc.energy < minIonPot) continue;
2428 // Try to extract the gas name from the label.
2429 // TODO: test if this works for all gases and excitation levels.
2430 const auto pos = exc.label.find('-');
2431 if (pos == std::string::npos) continue;
2432 if (GetGasName(exc.label.substr(0, pos)) != gasname) continue;
2433 exc.prob = r;
2434 exc.rms = lambda;
2435 ++nLevelsFound;
2436 }
2437 if (nLevelsFound > 0) {
2438 std::cout << m_className << "::EnablePenningTransfer:\n"
2439 << " Updated transfer probabilities for " << nLevelsFound
2440 << " excitation rates.\n";
2442 } else {
2443 std::cerr << m_className << "::EnablePenningTransfer:\n Warning: present"
2444 << " gas table has no eligible excitation rates.\n Ignore this"
2445 << " message if you are using microscopic tracking only.\n";
2446 }
2447 return true;
2448}
2449
2451
2452 m_rPenningGlobal = 0.;
2454
2455 m_rPenningGas.fill(0.);
2456 m_lambdaPenningGas.fill(0.);
2457
2458 if (m_excLevels.empty()) return;
2459 for (auto& exc : m_excLevels) {
2460 exc.prob = 0.;
2461 }
2463}
2464
2465bool MediumGas::DisablePenningTransfer(std::string gasname) {
2466
2467 // Get the "standard" name of this gas.
2468 gasname = GetGasName(gasname);
2469 if (gasname.empty()) {
2470 std::cerr << m_className << "::DisablePenningTransfer: Unknown gas name.\n";
2471 return false;
2472 }
2473
2474 // Look for this gas in the present gas mixture.
2475 int iGas = -1;
2476 for (unsigned int i = 0; i < m_nComponents; ++i) {
2477 if (m_gas[i] == gasname) {
2478 m_rPenningGas[i] = 0.;
2479 m_lambdaPenningGas[i] = 0.;
2480 iGas = i;
2481 break;
2482 }
2483 }
2484
2485 if (iGas < 0) {
2486 std::cerr << m_className << "::DisablePenningTransfer:\n"
2487 << " Requested gas (" << gasname
2488 << ") is not part of the present gas mixture.\n";
2489 return false;
2490 }
2491
2492 if (m_excLevels.empty()) return true;
2493 for (auto& exc : m_excLevels) {
2494 // Try to extract the gas name from the label.
2495 const auto pos = exc.label.find('-');
2496 if (pos == std::string::npos) continue;
2497 if (GetGasName(exc.label.substr(0, pos)) != gasname) continue;
2498 exc.prob = 0.;
2499 }
2501 return true;
2502}
2503
2504
2506
2507 // -----------------------------------------------------------------------
2508 // GASSPT
2509 // -----------------------------------------------------------------------
2510
2511 // Make sure there are Townsend coefficients.
2512 if (m_eAlp.empty() || m_eAlp0.empty()) {
2513 std::cerr << m_className << "::AdjustTownsendCoefficient:\n "
2514 << "Present gas table does not include Townsend coefficients.\n";
2515 return false;
2516 }
2517 // Make sure there are excitation and ionisation rates.
2518 if (m_excLevels.empty() || m_excRates.empty()) {
2519 std::cerr << m_className << "::AdjustTownsendCoefficient:\n "
2520 << "Present gas table does not include excitation rates.\n";
2521 return false;
2522 }
2523 if (m_ionLevels.empty() || m_ionRates.empty()) {
2524 std::cerr << m_className << "::AdjustTownsendCoefficient:\n "
2525 << "Present gas table does not include ionisation rates.\n";
2526 return false;
2527 }
2528 const unsigned int nE = m_eFields.size();
2529 const unsigned int nB = m_bFields.size();
2530 const unsigned int nA = m_bAngles.size();
2531 if (m_debug) {
2532 std::cout << m_className << "::AdjustTownsendCoefficient:\n"
2533 << " Entry Exc. Ion.\n";
2534 }
2535 for (unsigned int i = 0; i < nE; ++i) {
2536 for (unsigned int j = 0; j < nA; ++j) {
2537 for (unsigned int k = 0; k < nB; ++k) {
2538 // Compute total ionisation rate.
2539 double rion = 0.;
2540 for (const auto& ion : m_ionRates) {
2541 rion += ion[j][k][i];
2542 }
2543 // Compute rate of Penning ionisations.
2544 double rexc = 0.;
2545 const unsigned int nexc = m_excLevels.size();
2546 for (unsigned int ie = 0; ie < nexc; ++ie) {
2547 rexc += m_excLevels[ie].prob * m_excRates[ie][j][k][i];
2548 }
2549 if (m_debug) {
2550 std::cout << FmtInt(i, 4) << FmtInt(j, 4) << FmtInt(k, 4)
2551 << FmtFloat(rexc, 12, 5) << FmtFloat(rion, 12, 5) << "\n";
2552 }
2553 // Adjust the Townsend coefficient.
2554 double alpha0 = m_eAlp0[j][k][i];
2555 if (alpha0 < -20.) {
2556 alpha0 = 0.;
2557 } else {
2558 alpha0 = m_pressure * exp(alpha0);
2559 }
2560 double alpha1 = alpha0;
2561 if (rion > 0.) alpha1 *= (rexc + rion) / rion;
2562 m_eAlp[j][k][i] = alpha1 > 0. ? log(alpha1 / m_pressure) : -30.;
2563 }
2564 }
2565 }
2566 // Update the threshold index.
2568 return true;
2569}
2570
2571bool MediumGas::GetGasInfo(const std::string& gasname, double& a,
2572 double& z) const {
2573 if (gasname == "CF4") {
2574 a = 12.0107 + 4 * 18.9984032;
2575 z = 6 + 4 * 9;
2576 return true;
2577 } else if (gasname == "Ar") {
2578 a = 39.948;
2579 z = 18;
2580 } else if (gasname == "He") {
2581 a = 4.002602;
2582 z = 2;
2583 } else if (gasname == "He-3") {
2584 a = 3.01602931914;
2585 z = 2;
2586 } else if (gasname == "Ne") {
2587 a = 20.1797;
2588 z = 10;
2589 } else if (gasname == "Kr") {
2590 a = 37.798;
2591 z = 36;
2592 } else if (gasname == "Xe") {
2593 a = 131.293;
2594 z = 54;
2595 } else if (gasname == "CH4") {
2596 a = 12.0107 + 4 * 1.00794;
2597 z = 6 + 4;
2598 } else if (gasname == "C2H6") {
2599 a = 2 * 12.0107 + 6 * 1.00794;
2600 z = 2 * 6 + 6;
2601 } else if (gasname == "C3H8") {
2602 a = 3 * 12.0107 + 8 * 1.00794;
2603 z = 3 * 6 + 8;
2604 } else if (gasname == "iC4H10") {
2605 a = 4 * 12.0107 + 10 * 1.00794;
2606 z = 4 * 6 + 10;
2607 } else if (gasname == "CO2") {
2608 a = 12.0107 + 2 * 15.9994;
2609 z = 6 + 2 * 8;
2610 } else if (gasname == "neoC5H12") {
2611 a = 5 * 12.0107 + 12 * 1.00794;
2612 z = 5 * 6 + 12;
2613 } else if (gasname == "H2O") {
2614 a = 2 * 1.00794 + 15.9994;
2615 z = 2 + 8;
2616 } else if (gasname == "O2") {
2617 a = 2 * 15.9994;
2618 z = 2 * 8;
2619 } else if (gasname == "N2") {
2620 a = 2 * 14.0067;
2621 z = 2 * 7;
2622 } else if (gasname == "NO") {
2623 a = 14.0067 + 15.9994;
2624 z = 7 + 8;
2625 } else if (gasname == "N2O") {
2626 a = 2 * 14.0067 + 15.9994;
2627 z = 2 * 7 + 8;
2628 } else if (gasname == "C2H4") {
2629 a = 2 * 12.0107 + 4 * 1.00794;
2630 z = 2 * 6 + 4;
2631 } else if (gasname == "C2H2") {
2632 a = 2 * 12.0107 + 2 * 1.00794;
2633 z = 2 * 6 + 2;
2634 } else if (gasname == "H2" || gasname == "paraH2") {
2635 a = 2 * 1.00794;
2636 z = 2;
2637 } else if (gasname == "D2" || gasname == "orthoD2") {
2638 a = 2 * 2.01410177785;
2639 z = 2;
2640 } else if (gasname == "CO") {
2641 a = 12.0107 + 15.9994;
2642 z = 6 + 8;
2643 } else if (gasname == "Methylal") {
2644 a = 3 * 12.0107 + 8 * 1.00794 + 2 * 15.9994;
2645 z = 3 * 6 + 8 + 2 * 8;
2646 } else if (gasname == "DME") {
2647 a = 4 * 12.0107 + 10 * 1.00794 + 2 * 15.9994;
2648 z = 4 * 6 + 10 + 2 * 8;
2649 } else if (gasname == "Reid-Step" || gasname == "Mawell-Model" ||
2650 gasname == "Reid-Ramp") {
2651 a = 1.;
2652 z = 1.;
2653 } else if (gasname == "C2F6") {
2654 a = 2 * 12.0107 + 6 * 18.9984032;
2655 z = 2 * 6 + 6 * 9;
2656 } else if (gasname == "SF6") {
2657 a = 32.065 + 6 * 18.9984032;
2658 z = 16 + 6 * 9;
2659 } else if (gasname == "NH3") {
2660 a = 14.0067 + 3 * 1.00794;
2661 z = 7 + 3;
2662 } else if (gasname == "C3H6") {
2663 a = 3 * 12.0107 + 6 * 1.00794;
2664 z = 3 * 6 + 6;
2665 } else if (gasname == "cC3H6") {
2666 a = 3 * 12.0107 + 6 * 1.00794;
2667 z = 3 * 6 + 6;
2668 } else if (gasname == "CH3OH") {
2669 a = 12.0107 + 4 * 1.00794 + 15.9994;
2670 z = 6 + 4 + 8;
2671 } else if (gasname == "C2H5OH") {
2672 a = 2 * 12.0107 + 6 * 1.00794 + 15.9994;
2673 z = 2 * 6 + 6 + 8;
2674 } else if (gasname == "C3H7OH" || gasname == "nC3H7OH") {
2675 a = 3 * 12.0107 + 8 * 1.00794 + 15.9994;
2676 z = 3 * 6 + 8 * 8;
2677 } else if (gasname == "Cs") {
2678 a = 132.9054519;
2679 z = 55;
2680 } else if (gasname == "F2") {
2681 a = 2 * 18.9984032;
2682 z = 2 * 9;
2683 } else if (gasname == "CS2") {
2684 a = 12.0107 + 2 * 32.065;
2685 z = 6 + 2 * 16;
2686 } else if (gasname == "COS") {
2687 a = 12.0107 + 15.9994 + 32.065;
2688 z = 6 + 8 + 16;
2689 } else if (gasname == "CD4") {
2690 a = 12.0107 + 4 * 2.01410177785;
2691 z = 6 + 4;
2692 } else if (gasname == "BF3") {
2693 a = 10.811 + 3 * 18.9984032;
2694 z = 5 + 3 * 9;
2695 } else if (gasname == "C2H2F4") {
2696 a = 2 * 12.0107 + 2 * 1.00794 + 4 * 18.9984032;
2697 z = 2 * 6 + 2 + 4 * 9;
2698 } else if (gasname == "CHF3") {
2699 a = 12.0107 + 1.00794 + 3 * 18.9984032;
2700 z = 6 + 1 + 3 * 9;
2701 } else if (gasname == "CF3Br") {
2702 a = 12.0107 + 3 * 18.9984032 + 79.904;
2703 z = 6 + 3 * 9 + 35;
2704 } else if (gasname == "C3F8") {
2705 a = 3 * 12.0107 + 8 * 18.9984032;
2706 z = 3 * 6 + 8 * 9;
2707 } else if (gasname == "O3") {
2708 a = 3 * 15.9994;
2709 z = 3 * 8;
2710 } else if (gasname == "Hg") {
2711 a = 2 * 200.59;
2712 z = 80;
2713 } else if (gasname == "H2S") {
2714 a = 2 * 1.00794 + 32.065;
2715 z = 2 + 16;
2716 } else if (gasname == "nC4H10") {
2717 a = 4 * 12.0107 + 10 * 1.00794;
2718 z = 4 * 6 + 10;
2719 } else if (gasname == "nC5H12") {
2720 a = 5 * 12.0107 + 12 * 1.00794;
2721 z = 5 * 6 + 12;
2722 } else if (gasname == "N2") {
2723 a = 2 * 14.0067;
2724 z = 2 * 7;
2725 } else if (gasname == "GeH4") {
2726 a = 72.64 + 4 * 1.00794;
2727 z = 32 + 4;
2728 } else if (gasname == "SiH4") {
2729 a = 28.0855 + 4 * 1.00794;
2730 z = 14 + 4;
2731 } else {
2732 a = 0.;
2733 z = 0.;
2734 return false;
2735 }
2736
2737 return true;
2738}
2739
2740std::string MediumGas::GetGasName(const int gasnumber, const int version) const {
2741
2742 switch (gasnumber) {
2743 case 1:
2744 return "CF4";
2745 break;
2746 case 2:
2747 return "Ar";
2748 break;
2749 case 3:
2750 return "He";
2751 break;
2752 case 4:
2753 return "He-3";
2754 break;
2755 case 5:
2756 return "Ne";
2757 break;
2758 case 6:
2759 return "Kr";
2760 break;
2761 case 7:
2762 return "Xe";
2763 break;
2764 case 8:
2765 return "CH4";
2766 break;
2767 case 9:
2768 return "C2H6";
2769 break;
2770 case 10:
2771 return "C3H8";
2772 break;
2773 case 11:
2774 return "iC4H10";
2775 break;
2776 case 12:
2777 return "CO2";
2778 break;
2779 case 13:
2780 return "neoC5H12";
2781 break;
2782 case 14:
2783 return "H2O";
2784 break;
2785 case 15:
2786 return "O2";
2787 break;
2788 case 16:
2789 return "N2";
2790 break;
2791 case 17:
2792 return "NO";
2793 break;
2794 case 18:
2795 return "N2O";
2796 break;
2797 case 19:
2798 return "C2H4";
2799 break;
2800 case 20:
2801 return "C2H2";
2802 break;
2803 case 21:
2804 return "H2";
2805 break;
2806 case 22:
2807 return "D2";
2808 break;
2809 case 23:
2810 return "CO";
2811 break;
2812 case 24:
2813 return "Methylal";
2814 break;
2815 case 25:
2816 return "DME";
2817 break;
2818 case 26:
2819 return "Reid-Step";
2820 break;
2821 case 27:
2822 return "Maxwell-Model";
2823 break;
2824 case 28:
2825 return "Reid-Ramp";
2826 break;
2827 case 29:
2828 return "C2F6";
2829 break;
2830 case 30:
2831 return "SF6";
2832 break;
2833 case 31:
2834 return "NH3";
2835 break;
2836 case 32:
2837 return "C3H6";
2838 break;
2839 case 33:
2840 return "cC3H6";
2841 break;
2842 case 34:
2843 return "CH3OH";
2844 break;
2845 case 35:
2846 return "C2H5OH";
2847 break;
2848 case 36:
2849 return "C3H7OH";
2850 break;
2851 case 37:
2852 return "Cs";
2853 break;
2854 case 38:
2855 return "F2";
2856 break;
2857 case 39:
2858 return "CS2";
2859 break;
2860 case 40:
2861 return "COS";
2862 break;
2863 case 41:
2864 return "CD4";
2865 break;
2866 case 42:
2867 return "BF3";
2868 break;
2869 case 43:
2870 return "C2H2F4";
2871 break;
2872 case 44:
2873 return version <= 11 ? "He-3" : "TMA";
2874 break;
2875 case 45:
2876 return version <= 11 ? "He" : "paraH2";
2877 break;
2878 case 46:
2879 return version <= 11 ? "Ne" : "nC3H7OH";
2880 break;
2881 case 47:
2882 return "Ar";
2883 break;
2884 case 48:
2885 return version <= 11 ? "Kr" : "orthoD2";
2886 break;
2887 case 49:
2888 return "Xe";
2889 break;
2890 case 50:
2891 return "CHF3";
2892 break;
2893 case 51:
2894 return "CF3Br";
2895 break;
2896 case 52:
2897 return "C3F8";
2898 break;
2899 case 53:
2900 return "O3";
2901 break;
2902 case 54:
2903 return "Hg";
2904 break;
2905 case 55:
2906 return "H2S";
2907 break;
2908 case 56:
2909 return "nC4H10";
2910 break;
2911 case 57:
2912 return "nC5H12";
2913 break;
2914 case 58:
2915 return "N2";
2916 break;
2917 case 59:
2918 return "GeH4";
2919 break;
2920 case 60:
2921 return "SiH4";
2922 break;
2923 default:
2924 return "";
2925 break;
2926 }
2927 return "";
2928}
2929
2930std::string MediumGas::GetGasName(std::string input) const {
2931 // Convert to upper-case
2932 for (unsigned int i = 0; i < input.length(); ++i) {
2933 input[i] = toupper(input[i]);
2934 }
2935
2936 if (input.empty()) return "";
2937
2938 if (input == "CF4" || input == "FREON" || input == "FREON-14" ||
2939 input == "TETRAFLUOROMETHANE") {
2940 return "CF4";
2941 } else if (input == "AR" || input == "ARGON") {
2942 return "Ar";
2943 } else if (input == "HE" || input == "HELIUM" || input == "HE-4" ||
2944 input == "HE 4" || input == "HE4" || input == "4-HE" ||
2945 input == "4 HE" || input == "4HE" || input == "HELIUM-4" ||
2946 input == "HELIUM 4" || input == "HELIUM4") {
2947 return "He";
2948 } else if (input == "HE-3" || input == "HE3" || input == "HELIUM-3" ||
2949 input == "HELIUM 3" || input == "HELIUM3") {
2950 return "He-3";
2951 } else if (input == "NE" || input == "NEON") {
2952 return "Ne";
2953 } else if (input == "KR" || input == "KRYPTON") {
2954 return "Kr";
2955 } else if (input == "XE" || input == "XENON") {
2956 return "Xe";
2957 } else if (input == "CH4" || input == "METHANE") {
2958 return "CH4";
2959 } else if (input == "C2H6" || input == "ETHANE") {
2960 return "C2H6";
2961 } else if (input == "C3H8" || input == "PROPANE") {
2962 return "C3H8";
2963 } else if (input == "C4H10" || input == "ISOBUTANE" || input == "ISO" ||
2964 input == "IC4H10" || input == "ISO-C4H10" || input == "ISOC4H10") {
2965 return "iC4H10";
2966 } else if (input == "CO2" || input == "CARBON-DIOXIDE" ||
2967 input == "CARBON DIOXIDE" || input == "CARBONDIOXIDE") {
2968 return "CO2";
2969 } else if (input == "NEOPENTANE" || input == "NEO-PENTANE" ||
2970 input == "NEO-C5H12" || input == "NEOC5H12" ||
2971 input == "DIMETHYLPROPANE" || input == "C5H12") {
2972 return "neoC5H12";
2973 } else if (input == "H2O" || input == "WATER" || input == "WATER-VAPOUR" ||
2974 input == "WATER VAPOUR") {
2975 return "H2O";
2976 } else if (input == "O2" || input == "OXYGEN") {
2977 return "O2";
2978 } else if (input == "NI" || input == "NITRO" || input == "N2" ||
2979 input == "NITROGEN") {
2980 return "N2";
2981 } else if (input == "NO" || input == "NITRIC-OXIDE" || input == "NITRIC OXIDE" ||
2982 input == "NITROGEN-MONOXIDE" || input == "NITROGEN MONOXIDE") {
2983 return "NO";
2984 } else if (input == "N2O" || input == "NITROUS-OXIDE" || input == "NITROUS OXIDE" ||
2985 input == "DINITROGEN-MONOXIDE" || input == "LAUGHING-GAS") {
2986 return "N2O";
2987 } else if (input == "C2H4" || input == "ETHENE" || input == "ETHYLENE") {
2988 return "C2H4";
2989 } else if (input == "C2H2" || input == "ACETYL" || input == "ACETYLENE" ||
2990 input == "ETHYNE") {
2991 return "C2H2";
2992 } else if (input == "H2" || input == "HYDROGEN") {
2993 return "H2";
2994 } else if (input == "PARA H2" || input == "PARA-H2" ||
2995 input == "PARAH2" || input == "PARA HYDROGEN" ||
2996 input == "PARA-HYDROGEN" || input == "PARAHYDROGEN") {
2997 return "paraH2";
2998 } else if (input == "D2" || input == "DEUTERIUM") {
2999 return "D2";
3000 } else if (input == "ORTHO D2" || input == "ORTHO-D2" ||
3001 input == "ORTHOD2" || input == "ORTHO DEUTERIUM" ||
3002 input == "ORTHO-DEUTERIUM" || input == "ORTHODEUTERIUM") {
3003 return "orthoD2";
3004 } else if (input == "CO" || input == "CARBON-MONOXIDE" ||
3005 input == "CARBON MONOXIDE") {
3006 return "CO";
3007 } else if (input == "METHYLAL" || input == "METHYLAL-HOT" || input == "DMM" ||
3008 input == "DIMETHOXYMETHANE" || input == "FORMAL" || input == "C3H8O2") {
3009 // Methylal (dimethoxymethane, CH3-O-CH2-O-CH3, "hot" version)
3010 return "Methylal";
3011 } else if (input == "DME" || input == "DIMETHYL-ETHER" || input == "DIMETHYLETHER" ||
3012 input == "DIMETHYL ETHER" || input == "METHYL ETHER" ||
3013 input == "METHYL-ETHER" || input == "METHYLETHER" ||
3014 input == "WOOD-ETHER" || input == "WOODETHER" || input == "WOOD ETHER" ||
3015 input == "DIMETHYL OXIDE" || input == "DIMETHYL-OXIDE" ||
3016 input == "DEMEON" || input == "METHOXYMETHANE" || input == "C4H10O2") {
3017 return "DME";
3018 } else if (input == "REID-STEP") {
3019 return "Reid-Step";
3020 } else if (input == "MAXWELL-MODEL") {
3021 return "Maxwell-Model";
3022 } else if (input == "REID-RAMP") {
3023 return "Reid-Ramp";
3024 } else if (input == "C2F6" || input == "FREON-116" || input == "ZYRON-116" ||
3025 input == "ZYRON-116-N5" || input == "HEXAFLUOROETHANE") {
3026 return "C2F6";
3027 } else if (input == "SF6" || input == "SULPHUR-HEXAFLUORIDE" ||
3028 input == "SULFUR-HEXAFLUORIDE" || input == "SULPHUR HEXAFLUORIDE" ||
3029 input == "SULFUR HEXAFLUORIDE") {
3030 return "SF6";
3031 } else if (input == "NH3" || input == "AMMONIA") {
3032 return "NH3";
3033 } else if (input == "C3H6" || input == "PROPENE" || input == "PROPYLENE") {
3034 return "C3H6";
3035 } else if (input == "C-PROPANE" || input == "CYCLO-PROPANE" ||
3036 input == "CYCLO PROPANE" || input == "CYCLOPROPANE" ||
3037 input == "C-C3H6" || input == "CC3H6" || input == "CYCLO-C3H6") {
3038 return "cC3H6";
3039 } else if (input == "METHANOL" || input == "METHYL-ALCOHOL" ||
3040 input == "METHYL ALCOHOL" || input == "WOOD ALCOHOL" ||
3041 input == "WOOD-ALCOHOL" || input == "CH3OH") {
3042 return "CH3OH";
3043 } else if (input == "ETHANOL" || input == "ETHYL-ALCOHOL" ||
3044 input == "ETHYL ALCOHOL" || input == "GRAIN ALCOHOL" ||
3045 input == "GRAIN-ALCOHOL" || input == "C2H5OH") {
3046 return "C2H5OH";
3047 } else if (input == "PROPANOL" || input == "2-PROPANOL" || input == "ISOPROPYL" ||
3048 input == "ISO-PROPANOL" || input == "ISOPROPANOL" ||
3049 input == "ISOPROPYL ALCOHOL" || input == "ISOPROPYL-ALCOHOL" ||
3050 input == "C3H7OH") {
3051 return "C3H7OH";
3052 } else if (input == "NPROPANOL" || input == "N-PROPANOL" ||
3053 input == "1-PROPANOL" || input == "PROPYL ALCOHOL" ||
3054 input == "PROPYL-ALCOHOL" || input == "N-PROPYL ALCOHOL" ||
3055 input == "NC3H7OH" || input == "N-C3H7OH") {
3056 return "nC3H7OH";
3057 } else if (input == "CS" || input == "CESIUM" || input == "CAESIUM") {
3058 return "Cs";
3059 } else if (input == "F2" || input == "FLUOR" || input == "FLUORINE") {
3060 return "F2";
3061 } else if (input == "CS2" || input == "CARBON-DISULPHIDE" ||
3062 input == "CARBON-DISULFIDE" || input == "CARBON DISULPHIDE" ||
3063 input == "CARBON DISULFIDE") {
3064 return "CS2";
3065 } else if (input == "COS" || input == "CARBONYL-SULPHIDE" ||
3066 input == "CARBONYL-SULFIDE" || input == "CARBONYL SULFIDE") {
3067 return "COS";
3068 } else if (input == "DEUT-METHANE" || input == "DEUTERIUM-METHANE" ||
3069 input == "DEUTERATED-METHANE" || input == "DEUTERATED METHANE" ||
3070 input == "DEUTERIUM METHANE" || input == "CD4") {
3071 return "CD4";
3072 } else if (input == "BF3" || input == "BORON-TRIFLUORIDE" ||
3073 input == "BORON TRIFLUORIDE") {
3074 return "BF3";
3075 } else if (input == "C2HF5" || input == "C2H2F4" || input == "C2F5H" ||
3076 input == "C2F4H2" || input == "FREON 134" || input == "FREON 134A" ||
3077 input == "FREON-134" || input == "FREON-134-A" || input == "FREON 125" ||
3078 input == "ZYRON 125" || input == "FREON-125" || input == "ZYRON-125" ||
3079 input == "TETRAFLUOROETHANE" || input == "PENTAFLUOROETHANE") {
3080 // C2H2F4 (and C2HF5).
3081 return "C2H2F4";
3082 } else if (input == "TMA" || input == "TRIMETHYLAMINE" || input == "N(CH3)3" ||
3083 input == "N-(CH3)3") {
3084 return "TMA";
3085 } else if (input == "CHF3" || input == "FREON-23" || input == "TRIFLUOROMETHANE" ||
3086 input == "FLUOROFORM") {
3087 return "CHF3";
3088 } else if (input == "CF3BR" || input == "TRIFLUOROBROMOMETHANE" ||
3089 input == "BROMOTRIFLUOROMETHANE" || input == "HALON-1301" ||
3090 input == "HALON 1301" || input == "FREON-13B1" || input == "FREON 13BI") {
3091 return "CF3Br";
3092 } else if (input == "C3F8" || input == "OCTAFLUOROPROPANE" || input == "R218" ||
3093 input == "R-218" || input == "FREON 218" || input == "FREON-218" ||
3094 input == "PERFLUOROPROPANE" || input == "RC 218" || input == "PFC 218" ||
3095 input == "RC-218" || input == "PFC-218" || input == "FLUTEC PP30" ||
3096 input == "GENETRON 218") {
3097 return "C3F8";
3098 } else if (input == "OZONE" || input == "O3") {
3099 return "O3";
3100 } else if (input == "MERCURY" || input == "HG" || input == "HG2") {
3101 return "Hg";
3102 } else if (input == "H2S" || input == "HYDROGEN SULPHIDE" || input == "SEWER GAS" ||
3103 input == "HYDROGEN-SULPHIDE" || input == "SEWER-GAS" ||
3104 input == "HYDROGEN SULFIDE" || input == "HEPATIC ACID" ||
3105 input == "HYDROGEN-SULFIDE" || input == "HEPATIC-ACID" ||
3106 input == "SULFUR HYDRIDE" || input == "DIHYDROGEN MONOSULFIDE" ||
3107 input == "SULFUR-HYDRIDE" || input == "DIHYDROGEN-MONOSULFIDE" ||
3108 input == "DIHYDROGEN MONOSULPHIDE" || input == "SULPHUR HYDRIDE" ||
3109 input == "DIHYDROGEN-MONOSULPHIDE" || input == "SULPHUR-HYDRIDE" ||
3110 input == "STINK DAMP" || input == "SULFURATED HYDROGEN" ||
3111 input == "STINK-DAMP" || input == "SULFURATED-HYDROGEN") {
3112 return "H2S";
3113 } else if (input == "N-BUTANE" || input == "N-C4H10" || input == "NBUTANE" ||
3114 input == "NC4H10") {
3115 return "nC4H10";
3116 } else if (input == "N-PENTANE" || input == "N-C5H12" || input == "NPENTANE" ||
3117 input == "NC5H12") {
3118 return "nC5H12";
3119 } else if (input == "NI-PHELPS" || input == "NI PHELPS" ||
3120 input == "NITROGEN-PHELPS" || input == "NITROGEN PHELPHS" ||
3121 input == "N2-PHELPS" || input == "N2 PHELPS" || input == "N2 (PHELPS)") {
3122 // Nitrogen
3123 return "N2 (Phelps)";
3124 } else if (input == "GERMANE" || input == "GERM" || input == "GERMANIUM-HYDRIDE" ||
3125 input == "GERMANIUM HYDRIDE" || input == "GERMANIUM TETRAHYDRIDE" ||
3126 input == "GERMANIUM-TETRAHYDRIDE" || input == "GERMANOMETHANE" ||
3127 input == "MONOGERMANE" || input == "GEH4") {
3128 return "GeH4";
3129 } else if (input == "SILANE" || input == "SIL" || input == "SILICON-HYDRIDE" ||
3130 input == "SILICON HYDRIDE" || input == "SILICON-TETRAHYDRIDE" ||
3131 input == "SILICANE" || input == "MONOSILANE" || input == "SIH4") {
3132 return "SiH4";
3133 }
3134
3135 std::cerr << m_className << "::GetGasName:\n"
3136 << " Gas " << input << " is not recognized.\n";
3137 return "";
3138}
3139
3140int MediumGas::GetGasNumberGasFile(const std::string& input) const {
3141
3142 if (input.empty()) return 0;
3143
3144 if (input == "CF4") {
3145 return 1;
3146 } else if (input == "Ar") {
3147 return 2;
3148 } else if (input == "He" || input == "He-4") {
3149 return 3;
3150 } else if (input == "He-3") {
3151 return 4;
3152 } else if (input == "Ne") {
3153 return 5;
3154 } else if (input == "Kr") {
3155 return 6;
3156 } else if (input == "Xe") {
3157 return 7;
3158 } else if (input == "CH4") {
3159 // Methane
3160 return 8;
3161 } else if (input == "C2H6") {
3162 // Ethane
3163 return 9;
3164 } else if (input == "C3H8") {
3165 // Propane
3166 return 10;
3167 } else if (input == "iC4H10") {
3168 // Isobutane
3169 return 11;
3170 } else if (input == "CO2") {
3171 return 12;
3172 } else if (input == "neoC5H12") {
3173 // Neopentane
3174 return 13;
3175 } else if (input == "H2O") {
3176 return 14;
3177 } else if (input == "O2") {
3178 return 15;
3179 } else if (input == "N2") {
3180 return 16;
3181 } else if (input == "NO") {
3182 // Nitric oxide
3183 return 17;
3184 } else if (input == "N2O") {
3185 // Nitrous oxide
3186 return 18;
3187 } else if (input == "C2H4") {
3188 // Ethene
3189 return 19;
3190 } else if (input == "C2H2") {
3191 // Acetylene
3192 return 20;
3193 } else if (input == "H2") {
3194 return 21;
3195 } else if (input == "D2") {
3196 // Deuterium
3197 return 22;
3198 } else if (input == "CO") {
3199 return 23;
3200 } else if (input == "Methylal") {
3201 // Methylal (dimethoxymethane, CH3-O-CH2-O-CH3, "hot" version)
3202 return 24;
3203 } else if (input == "DME") {
3204 return 25;
3205 } else if (input == "Reid-Step") {
3206 return 26;
3207 } else if (input == "Maxwell-Model") {
3208 return 27;
3209 } else if (input == "Reid-Ramp") {
3210 return 28;
3211 } else if (input == "C2F6") {
3212 return 29;
3213 } else if (input == "SF6") {
3214 return 30;
3215 } else if (input == "NH3") {
3216 return 31;
3217 } else if (input == "C3H6") {
3218 // Propene
3219 return 32;
3220 } else if (input == "cC3H6") {
3221 // Cyclopropane
3222 return 33;
3223 } else if (input == "CH3OH") {
3224 // Methanol
3225 return 34;
3226 } else if (input == "C2H5OH") {
3227 // Ethanol
3228 return 35;
3229 } else if (input == "C3H7OH") {
3230 // Propanol
3231 return 36;
3232 } else if (input == "Cs") {
3233 return 37;
3234 } else if (input == "F2") {
3235 // Fluorine
3236 return 38;
3237 } else if (input == "CS2") {
3238 return 39;
3239 } else if (input == "COS") {
3240 return 40;
3241 } else if (input == "CD4") {
3242 // Deuterated methane
3243 return 41;
3244 } else if (input == "BF3") {
3245 return 42;
3246 } else if (input == "C2HF5" || input == "C2H2F4") {
3247 return 43;
3248 } else if (input == "TMA") {
3249 return 44;
3250 } else if (input == "paraH2") {
3251 return 45;
3252 } else if (input == "nC3H7OH") {
3253 return 46;
3254 } else if (input == "orthoD2") {
3255 return 48;
3256 } else if (input == "CHF3") {
3257 return 50;
3258 } else if (input == "CF3Br") {
3259 return 51;
3260 } else if (input == "C3F8") {
3261 return 52;
3262 } else if (input == "O3") {
3263 // Ozone
3264 return 53;
3265 } else if (input == "Hg") {
3266 return 54;
3267 } else if (input == "H2S") {
3268 return 55;
3269 } else if (input == "nC4H10") {
3270 // n-butane
3271 return 56;
3272 } else if (input == "nC5H12") {
3273 // n-pentane
3274 return 57;
3275 } else if (input == "N2 (Phelps)") {
3276 return 58;
3277 } else if (input == "GeH4") {
3278 // Germane
3279 return 59;
3280 } else if (input == "SiH4") {
3281 // Silane
3282 return 60;
3283 }
3284
3285 std::cerr << m_className << "::GetGasNumberGasFile:\n"
3286 << " Gas " << input << " not found.\n";
3287 return 0;
3288}
3289
3290bool MediumGas::GetPhotoAbsorptionCrossSection(const double e, double& sigma,
3291 const unsigned int i) {
3292 if (i >= m_nMaxGases) {
3293 std::cerr << m_className
3294 << "::GetPhotoAbsorptionCrossSection: Index out of range.\n";
3295 return false;
3296 }
3297
3298 OpticalData optData;
3299 if (!optData.IsAvailable(m_gas[i])) return false;
3300 double eta = 0.;
3301 return optData.GetPhotoabsorptionCrossSection(m_gas[i], e, sigma, eta);
3302}
3303}
double GetMassDensity() const override
Get the mass density [g/cm3].
Definition: MediumGas.cc:298
double GetNumberDensity() const override
Get the number density [cm-3].
Definition: MediumGas.cc:292
void ReadRecord3D(std::ifstream &gasfile, double &ve, double &vb, double &vx, double &dl, double &dt, double &alpha, double &alpha0, double &eta, double &mu, double &lor, double &dis, std::array< double, 6 > &dif, std::vector< double > &rexc, std::vector< double > &rion)
Definition: MediumGas.cc:755
bool AdjustTownsendCoefficient()
Definition: MediumGas.cc:2505
std::pair< unsigned int, unsigned int > m_extrIon
Definition: MediumGas.hh:176
void GetComposition(std::string &gas1, double &f1, std::string &gas2, double &f2, std::string &gas3, double &f3, std::string &gas4, double &f4, std::string &gas5, double &f5, std::string &gas6, double &f6)
Retrieve the gas mixture.
Definition: MediumGas.cc:228
void SetAtomicNumber(const double z) override
Set the effective atomic number.
Definition: MediumGas.cc:259
bool GetGasInfo(const std::string &gasname, double &a, double &z) const
Definition: MediumGas.cc:2571
static constexpr unsigned int m_nMaxGases
Definition: MediumGas.hh:126
std::vector< std::vector< std::vector< std::vector< double > > > > m_excRates
Definition: MediumGas.hh:155
void InsertB(const int ib, const int ne, const int nb, const int na)
Definition: MediumGas.cc:1641
double m_lambdaPenningGlobal
Definition: MediumGas.hh:140
unsigned int m_intpIon
Definition: MediumGas.hh:178
std::vector< IonLevel > m_ionLevels
Definition: MediumGas.hh:172
std::array< double, m_nMaxGases > m_rPenningGas
Definition: MediumGas.hh:142
bool LoadIonMobility(const std::string &filename)
Read a table of ion mobilities as function of electric field from file.
Definition: MediumGas.cc:2210
double GetAtomicNumber() const override
Get the effective atomic number.
Definition: MediumGas.cc:302
std::array< double, m_nMaxGases > m_atNum
Definition: MediumGas.hh:132
void GetGasBits(std::bitset< 20 > &gasok) const
Definition: MediumGas.cc:2047
std::vector< std::vector< std::vector< std::vector< double > > > > m_ionRates
Definition: MediumGas.hh:156
void ZeroRowA(const int ia, const int ne, const int nb)
Definition: MediumGas.cc:1747
void ResetTables() override
Reset all tables of transport parameters.
Definition: MediumGas.cc:2311
std::vector< ExcLevel > m_excLevels
Definition: MediumGas.hh:166
void ZeroRowB(const int ib, const int ne, const int na)
Definition: MediumGas.cc:1739
MediumGas()
Constructor.
Definition: MediumGas.cc:111
int GetGasNumberGasFile(const std::string &input) const
Definition: MediumGas.cc:3140
void ReadFooter(std::ifstream &gasfile, std::array< unsigned int, 13 > &extrapH, std::array< unsigned int, 13 > &extrapL, std::array< unsigned int, 13 > &interp, unsigned int &thrAlp, unsigned int &thrAtt, unsigned int &thrDis, double &ionDiffL, double &ionDiffT, double &pgas, double &tgas)
Definition: MediumGas.cc:813
double m_temperatureTable
Definition: MediumGas.hh:149
virtual void PrintGas()
Print information about the present gas mixture and available data.
Definition: MediumGas.cc:2067
void ReadRecord1D(std::ifstream &gasfile, double &ve, double &vb, double &vx, double &dl, double &dt, double &alpha, double &alpha0, double &eta, double &mu, double &lor, double &dis, std::array< double, 6 > &dif, std::vector< double > &rexc, std::vector< double > &rion)
Definition: MediumGas.cc:789
std::array< double, m_nMaxGases > m_atWeight
Definition: MediumGas.hh:131
void GetComponent(const unsigned int i, std::string &label, double &f) override
Get the name and fraction of a given component.
Definition: MediumGas.cc:246
bool WriteGasFile(const std::string &filename)
Save the present table of gas properties (transport parameters) to a file.
Definition: MediumGas.cc:1755
bool GetMixture(const std::vector< double > &mixture, const int version, std::vector< std::string > &gasnames, std::vector< double > &percentages) const
Definition: MediumGas.cc:907
std::array< double, m_nMaxGases > m_lambdaPenningGas
Definition: MediumGas.hh:144
std::pair< unsigned int, unsigned int > m_extrExc
Definition: MediumGas.hh:175
bool GetPhotoAbsorptionCrossSection(const double e, double &sigma, const unsigned int i) override
Definition: MediumGas.cc:3290
bool MergeGasFile(const std::string &filename, const bool replaceOld)
Read table of gas properties from and merge with the existing dataset.
Definition: MediumGas.cc:944
virtual void DisablePenningTransfer()
Switch the simulation of Penning transfers off globally.
Definition: MediumGas.cc:2450
std::string GetGasName(const int gasnumber, const int version) const
Definition: MediumGas.cc:2740
double GetAtomicWeight() const override
Get the effective atomic weight.
Definition: MediumGas.cc:283
void SetNumberDensity(const double n) override
Set the number density [cm-3].
Definition: MediumGas.cc:271
bool SetComposition(const std::string &gas1, const double f1=1., const std::string &gas2="", const double f2=0., const std::string &gas3="", const double f3=0., const std::string &gas4="", const double f4=0., const std::string &gas5="", const double f5=0., const std::string &gas6="", const double f6=0.)
Set the gas mixture.
Definition: MediumGas.cc:134
void ZeroRowE(const int ie, const int nb, const int na)
Definition: MediumGas.cc:1731
void SetMassDensity(const double rho) override
Set the mass density [g/cm3].
Definition: MediumGas.cc:277
virtual bool EnablePenningTransfer(const double r, const double lambda)
Definition: MediumGas.cc:2321
std::vector< std::vector< std::vector< double > > > m_eAlp0
Definition: MediumGas.hh:152
void InsertA(const int ia, const int ne, const int nb, const int na)
Definition: MediumGas.cc:1689
bool LoadGasFile(const std::string &filename)
Read table of gas properties (transport parameters) from file.
Definition: MediumGas.cc:311
void InsertE(const int ie, const int ne, const int nb, const int na)
Definition: MediumGas.cc:1599
std::array< std::string, m_nMaxGases > m_gas
Definition: MediumGas.hh:129
bool ReadHeader(std::ifstream &gasfile, int &version, std::bitset< 20 > &gasok, bool &is3d, std::vector< double > &mixture, std::vector< double > &efields, std::vector< double > &bfields, std::vector< double > &angles, std::vector< ExcLevel > &excLevels, std::vector< IonLevel > &ionLevels)
Definition: MediumGas.cc:578
void SetAtomicWeight(const double a) override
Set the effective atomic weight.
Definition: MediumGas.cc:265
unsigned int m_intpExc
Definition: MediumGas.hh:177
std::array< double, m_nMaxGases > m_fraction
Definition: MediumGas.hh:130
Abstract base class for media.
Definition: Medium.hh:13
std::vector< double > m_bFields
Definition: Medium.hh:547
double m_pressure
Definition: Medium.hh:517
unsigned int m_intpMob
Definition: Medium.hh:603
unsigned int SetThreshold(const std::vector< std::vector< std::vector< double > > > &tab) const
Definition: Medium.cc:1146
unsigned int m_intpDis
Definition: Medium.hh:604
std::vector< std::vector< std::vector< double > > > m_eAlp
Definition: Medium.hh:558
virtual void EnableDrift(const bool on=true)
Switch electron/ion/hole on/off.
Definition: Medium.hh:67
std::string m_name
Definition: Medium.hh:513
unsigned int m_intpDif
Definition: Medium.hh:599
virtual void EnablePrimaryIonisation(const bool on=true)
Make the medium ionisable or non-ionisable.
Definition: Medium.hh:69
std::pair< unsigned int, unsigned int > m_extrAtt
Definition: Medium.hh:592
std::vector< std::vector< std::vector< double > > > m_iDifT
Definition: Medium.hh:578
std::pair< unsigned int, unsigned int > m_extrVel
Definition: Medium.hh:589
std::vector< std::vector< std::vector< double > > > m_eVelE
Definition: Medium.hh:553
std::vector< std::vector< std::vector< double > > > m_eVelX
Definition: Medium.hh:554
std::vector< std::vector< std::vector< double > > > m_eDifL
Definition: Medium.hh:556
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)
Definition: Medium.cc:1304
unsigned int m_intpVel
Definition: Medium.hh:598
std::vector< double > m_eFields
Definition: Medium.hh:546
std::vector< std::vector< std::vector< double > > > m_eAtt
Definition: Medium.hh:559
std::vector< double > m_bAngles
Definition: Medium.hh:548
std::vector< std::vector< std::vector< double > > > m_iDifL
Definition: Medium.hh:577
std::vector< std::vector< std::vector< double > > > m_eLor
Definition: Medium.hh:560
std::vector< std::vector< std::vector< double > > > m_eDifT
Definition: Medium.hh:557
unsigned int m_intpAtt
Definition: Medium.hh:601
std::pair< unsigned int, unsigned int > m_extrDis
Definition: Medium.hh:595
std::pair< unsigned int, unsigned int > m_extrAlp
Definition: Medium.hh:591
std::vector< std::vector< std::vector< double > > > m_eVelB
Definition: Medium.hh:555
unsigned int m_eThrAtt
Definition: Medium.hh:583
std::pair< unsigned int, unsigned int > m_extrLor
Definition: Medium.hh:593
unsigned int m_nComponents
Definition: Medium.hh:521
unsigned int m_intpLor
Definition: Medium.hh:602
std::string m_className
Definition: Medium.hh:506
std::pair< unsigned int, unsigned int > m_extrDif
Definition: Medium.hh:590
unsigned int m_iThrDis
Definition: Medium.hh:586
virtual void ResetTables()
Reset all tables of transport parameters.
Definition: Medium.cc:905
std::pair< unsigned int, unsigned int > m_extrMob
Definition: Medium.hh:594
std::vector< std::vector< std::vector< std::vector< double > > > > m_eDifM
Definition: Medium.hh:562
bool m_isChanged
Definition: Medium.hh:540
std::vector< std::vector< std::vector< double > > > m_iMob
Definition: Medium.hh:576
unsigned int m_eThrAlp
Definition: Medium.hh:582
double m_temperature
Definition: Medium.hh:515
std::vector< std::vector< std::vector< double > > > m_iDis
Definition: Medium.hh:579
bool SetIonMobility(const std::vector< double > &fields, const std::vector< double > &mobilities)
Definition: Medium.cc:1044
unsigned int m_intpAlp
Definition: Medium.hh:600
Photoabsorption cross-sections for some gases.
Definition: OpticalData.hh:11
bool GetPhotoabsorptionCrossSection(const std::string &material, const double e, double &cs, double &eta)
Photo-absorption cross-section and ionisation yield at a given energy.
Definition: OpticalData.cc:46
bool IsAvailable(const std::string &material) const
Check whether optical data have been implemented for a given gas.
Definition: OpticalData.cc:39
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615