ARTS  2.3.1285(git:92a29ea9-dirty)
linemixing.cc
Go to the documentation of this file.
1 /* Copyright 2018, Richard Larsson
2  *
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the
5  * Free Software Foundation; either version 2, or (at your option) any
6  * later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 */
17 
29 #include "linemixing.h"
30 #include "abs_species_tags.h"
31 #include "complex.h"
32 #include "lin_alg.h"
33 #include "linefunctions.h"
34 #include "linescaling.h"
35 #include "sorting.h"
36 #include "species_info.h"
37 #include "wigner_functions.h"
38 
39 inline Numeric getB0(const SpeciesTag& main) {
40  if (main.IsSpecies("CO2"))
41  return Conversion::kaycm2freq(0.39021); // Herzberg 1966
42  else if (main.IsSpecies("O2")) {
43  if (main.IsIsotopologue("66"))
44  return 43100.44276e6; // B.J. Drouin et al. Journal of Quantitative Spectroscopy & Radiative Transfer 111 (2010) 1167–1173
45  else if (main.IsIsotopologue("67"))
46  return Conversion::kaycm2freq(1.35);
47  else if (main.IsIsotopologue("68"))
48  return 40707.38657e6; // B.J. Drouin et al. Journal of Quantitative Spectroscopy & Radiative Transfer 111 (2010) 1167–1173
49  else if (main.IsIsotopologue("88"))
50  return 38313.72938e6; // B.J. Drouin et al. Journal of Quantitative Spectroscopy & Radiative Transfer 111 (2010) 1167–1173
51  } else if (main.IsSpecies("O2")) {
52  return -1e99;
53  } else if (main.IsSpecies("CH4"))
54  return Conversion::kaycm2freq(5.2);
55 
56  throw std::runtime_error(
57  "Error in getB0: Unsupported main species/isotopologue. Check your broadening data");
58 }
59 
61  const SpeciesTag& collider) {
62  if (main.IsSpecies("CO2") and collider.IsSpecies("N2"))
64  else if (main.IsSpecies("CO2") and collider.IsSpecies("O2"))
66  else if (main.IsSpecies("O2")) {
68  }
69 
70  throw std::runtime_error(
71  "Error in adiabatic_factor: Unsupported species pairs, main-collider. Check your broadening data");
72 }
73 
75  const SpeciesTag& collider,
76  const Numeric& T,
77  const Numeric& T0) {
78  if (main.IsSpecies("CO2") and collider.IsSpecies("N2"))
80  std::pow(T0 / T, 0.85), // Hz/Pa
81  0.81 * std::pow(T0 / T, 0.0152), // unitless
82  0.008}, // unitless
84  else if (main.IsSpecies("CO2") and collider.IsSpecies("O2"))
86  std::pow(T0 / T, 0.50), // Hz/Pa
87  0.82 * std::pow(T0 / T, -0.091), // unitless
88  0.007}, // unitless
90  else if (main.IsSpecies("O2"))
91  return BasisRate({-1e99, -1e99, -1e99}, BasisRate::Type::Hartmann);
92 
93  throw std::runtime_error(
94  "Error in basis_rate: Unsupported species pairs, main-collider. Check your broadening data");
95 }
96 
97 enum class Species { CO2, O2_66 };
98 
100  const Vector& population,
101  const SpeciesTag& collider,
102  const Numeric& collider_vmr,
103  const Numeric& T,
104  const Index& size) try {
105  const Index n = band.NumLines();
106  Matrix W(n, n);
107 
108  auto main = SpeciesTag(band.SpeciesName());
109 
110  const BasisRate br = basis_rate(main, collider, T, band.T0());
111  const AdiabaticFactor af = adiabatic_factor(main, collider);
112  const Numeric B0 = getB0(main);
113  const ArrayOfArrayOfSpeciesTag pseudo_species(
114  {ArrayOfSpeciesTag(1, collider), ArrayOfSpeciesTag(1, main)});
115  const Vector pseudo_vmrs({1, 0});
116 
117  Vector vmrs = LineShape::vmrs(pseudo_vmrs, pseudo_species, band.QuantumIdentity(), band.BroadeningSpecies(), band.Self(), band.Bath(), band.LineShapeType());
118 
119  Species spec;
120  if (main.IsSpecies("CO2"))
121  spec = Species::CO2;
122  else if (main.IsSpecies("O2") and main.IsIsotopologue("66"))
123  spec = Species::O2_66;
124  else
125  throw "Unsupported species";
126 
127 #pragma omp parallel for schedule( \
128  guided, 1) if (DO_FAST_WIGNER && !arts_omp_in_parallel())
129  for (Index i = 0; i < n; i++) {
130  // Create a temporary table to allow openmp
131  wig_temp_init(2 * int(size));
132 
133  auto shape_parameters = band.ShapeParameters(i, T, 1, vmrs);
134  W(i, i) = shape_parameters.G0;
135 
136  const Numeric& popi = population[i];
137  for (Index j = 0; j < n; j++) {
138  const Numeric& popj = population[j];
139 
140  if (i not_eq j) {
142  switch (spec) {
143  case Species::CO2:
152  popi,
153  popj,
154  br,
155  af,
156  T,
157  B0,
158  main.SpeciesMass(),
159  collider.SpeciesMass());
160  break;
161  case Species::O2_66:
170  popi, popj, T, collider.SpeciesMass());
171  break;
172  default:
173  throw "DEVELOPER BUG: Add species here as well, the other one is to check if the computations are valid at all...";
174  }
175 
176  W(i, j) = X.ij;
177  W(j, i) = X.ji;
178  }
179  }
180 
181  // Remove the temporary table
182  wig_temp_free();
183  }
184 
185  // rescale by the VMR
186  W *= collider_vmr;
187  return W;
188 } catch (const char* e) {
190  os << "Errors raised by *relaxation_matrix_calculations*:\n";
191  os << "\tError: " << e << '\n';
192  throw std::runtime_error(os.str());
193 } catch (const std::exception& e) {
195  os << "Errors in calls by *relaxation_matrix_calculations*:\n";
196  os << e.what();
197  throw std::runtime_error(os.str());
198 }
199 
201  const Vector& population,
202  const Vector& /* d0 */,
203  const AbsorptionLines& band,
204  const SpeciesAuxData& partition_functions,
205  const Numeric& T) try {
206  const Index n = band.NumLines();
207 
209 
210  // Index list showing sorting of W
211  ArrayOfIndex sorted(n, 0);
212  Vector test(n);
213  if (n) {
214  const Numeric QT =
216  partition_functions.getParamType(band.QuantumIdentity()),
217  partition_functions.getParam(band.QuantumIdentity()));
218  for (Index i = 0; i < n; i++) {
219  const Numeric QT0 =
221  partition_functions.getParamType(band.QuantumIdentity()),
222  partition_functions.getParam(band.QuantumIdentity()));
223  test[i] = Linefunctions::lte_linestrength(band.I0(i),
224  band.E0(i),
225  band.F0(i),
226  QT0,
227  band.T0(),
228  QT,
229  T);
230  }
231  }
232 
233  get_sorted_indexes(sorted, test);
234  for (Index i = 0; i < n - i - 1; i++) std::swap(sorted[i], sorted[n - i - 1]);
235 
236  // Sorted matrix
237  Matrix Wr(n, n);
238  for (Index i = 0; i < n; i++) {
239  Wr(i, i) = W(sorted[i], sorted[i]);
240  for (Index j = 0; j < n; j++) {
241  if (i not_eq j) {
242  Wr(i, j) = -std::abs(W(sorted[i], sorted[j]));
243  }
244  }
245  }
246 
247  // Renormalization procedure
248  for (Index i = 0; i < n; i++) {
249  // Sum up upper and lower contributions
250  Numeric Sup = 0, Slo = 0;
251  for (Index j = 0; j < n; j++) {
252  if (j <= i)
253  Sup += std::abs(d[sorted[j]]) * Wr(i, j);
254  else
255  Slo += std::abs(d[sorted[j]]) * Wr(i, j);
256  }
257 
258  Numeric UL = Sup / Slo;
259  if (not std::isnormal(UL)) UL = 1.0;
260 
261  // Rescale to fulfill sum-rule, note how the loop for the upper triangle so the last row cannot be renormalized properly
262  for (Index j = i; j < n; j++) {
263  const Numeric r = population[sorted[i]] / population[sorted[j]];
264  Wr(j, i) = r * Wr(i, j);
265  if (j not_eq i) Wr(i, j) *= -UL;
266  }
267  }
268 
269  for (Index i = 0; i < n - 1; i++) Wr(n - 1, i) = 0; // TEST!
270 
271  // Backsort matrix before returning
272  for (Index i = 0; i < n; i++)
273  for (Index j = 0; j < n; j++) W(sorted[i], sorted[j]) = Wr(i, j);
274 } catch (const char* e) {
276  os << "Errors raised by *normalize_relaxation_matrix*:\n";
277  os << "\tError: " << e << '\n';
278  throw std::runtime_error(os.str());
279 } catch (const std::exception& e) {
281  os << "Errors in calls by *normalize_relaxation_matrix*:\n";
282  os << e.what();
283  throw std::runtime_error(os.str());
284 }
285 
287  const AbsorptionLines& band,
288  const Vector& population,
289  const Vector& d0,
290  const ArrayOfSpeciesTag& colliders,
291  const Vector& colliders_vmr,
292  const SpeciesAuxData& partition_functions,
293  const Numeric& T,
294  const Index& size) try {
295  // Size of problem
296  const Index n = band.NumLines();
297  const Index c = colliders.nelem();
298 
299  // Create and normalize the matrix
300  Matrix W(n, n, 0);
301  if (n) {
302  for (Index ic = 0; ic < c; ic++)
304  population,
305  colliders[ic],
306  colliders_vmr[ic],
307  T,
308  size);
310  W, population, d0, band, partition_functions, T);
311  }
312 
313  return W;
314 } catch (const char* e) {
316  os << "Errors raised by *hartmann_relaxation_matrix*:\n";
317  os << "\tError: " << e << '\n';
318  throw std::runtime_error(os.str());
319 } catch (const std::exception& e) {
321  os << "Errors in calls by *hartmann_relaxation_matrix*:\n";
322  os << e.what();
323  throw std::runtime_error(os.str());
324 }
325 
327  Numeric E0,
328  Numeric F0,
329  Numeric QT) {
330  return (1.0 - stimulated_emission(T, F0)) * boltzman_factor(T, E0) / QT;
331 }
332 
334  Numeric T, Numeric E0, Numeric F0, Numeric QT, Numeric dQTdT) {
335  using namespace Constant;
336  return (1.0 - dstimulated_emissiondT(T, F0)) * boltzman_factor(T, E0) / QT +
337  (1.0 - stimulated_emission(T, F0)) * dboltzman_factordT(T, E0) / QT +
338  (1.0 - stimulated_emission(T, F0)) * boltzman_factor(T, E0) *
339  (-dQTdT) / pow2(QT);
340 }
341 
343  const SpeciesAuxData& partition_functions,
344  const Numeric& T) try {
345  auto n = band.NumLines();
346  Vector p(n);
347 
348  if (n) {
349  const Numeric QT = single_partition_function(T,
350  partition_functions.getParamType(band.QuantumIdentity()),
351  partition_functions.getParam(band.QuantumIdentity()));
352 
353  for (auto k = 0; k < n; k++) {
354  p[k] = population_density(T, band.E0(k), band.F0(k), QT);
355  }
356  }
357 
358  return p;
359 } catch (const char* e) {
361  os << "Errors raised by *population_density_vector*:\n";
362  os << "\tError: " << e << '\n';
363  throw std::runtime_error(os.str());
364 } catch (const std::exception& e) {
366  os << "Errors in calls by *population_density_vector*:\n";
367  os << e.what();
368  throw std::runtime_error(os.str());
369 }
370 
372  const SpeciesAuxData& partition_functions) try {
373  const Index n = band.NumLines();
374  Vector d(n, 0);
375  if (not n) return d;
376 
377  const Numeric T0 = band.T0();
378  const Vector p0 =
379  population_density_vector(band, partition_functions, T0);
380  for (Index k = 0; k < n; k++) {
381  d[k] = std::sqrt(band.I0(k) / p0[k]);
382  }
383 
384  return d;
385 } catch (const char* e) {
387  os << "Errors raised by *dipole_vector*:\n";
388  os << "\tError: " << e << '\n';
389  throw std::runtime_error(os.str());
390 } catch (const std::exception& e) {
392  os << "Errors in calls by *dipole_vector*:\n";
393  os << e.what();
394  throw std::runtime_error(os.str());
395 }
396 
402  const RedPoleType type) try {
403  const Index n = band.NumLines();
404  Vector d(n, 0);
405  if (not n) return d;
406 
407  switch (type) {
409  for (Index i = 0; i < n; i++)
412  break;
414  for (Index i = 0; i < n; i++)
416  break;
417  }
418  return d;
419 } catch (const char* e) {
421  os << "Errors raised by *reduced_dipole_vector*:\n";
422  os << "\tError: " << e << '\n';
423  throw std::runtime_error(os.str());
424 } catch (const std::exception& e) {
426  os << "Errors in calls by *reduced_dipole_vector*:\n";
427  os << e.what();
428  throw std::runtime_error(os.str());
429 }
430 
432  const Matrix& W,
433  const Vector& d0) {
434  const Index n = band.NumLines();
435  Vector Y(n, 0);
436 
437  for (Index i = 0; i < n; i++) {
438  Numeric sum = 0;
439  for (Index j = 0; j < n; j++) {
440  const Numeric df = band.F0(i) - band.F0(j);
441  if (std::isnormal(df)) sum += d0[j] / d0[i] * W(i, j) / df;
442  }
443  Y[i] = -2 * sum; // Sign change because ARTS uses (1-iY)
444  }
445 
446  return Y;
447 }
448 
450  const Matrix& W) {
451  const Index n = band.NumLines();
452  Vector DV(n, 0);
453 
454  for (Index i = 0; i < n; i++) {
455  Numeric sum = 0;
456  for (Index j = 0; j < n; j++) {
457  const Numeric df = band.F0(j) - band.F0(i);
458  if (std::isnormal(df)) sum += W(i, j) * W(j, i) / df;
459  }
460  DV[i] = sum; // Does this require a sign change????
461  }
462 
463  return DV;
464 }
465 
467  const Matrix& W,
468  const Vector& d0) {
469  const Index n = band.NumLines();
470  Vector G(n, 0);
471 
472  for (Index i = 0; i < n; i++) {
473  const Numeric& di = d0[i];
474  Numeric sum1 = 0;
475  Numeric sum2 = 0;
476  Numeric sum3 = 0;
477  Numeric sum4 = 0;
478  for (Index j = 0; j < n; j++) {
479  const Numeric df = band.F0(j) - band.F0(i);
480  if (std::isnormal(df)) {
481  const Numeric& dj = d0[j];
482  const Numeric r = dj / di;
483 
484  sum1 += W(i, j) * W(j, i) / Constant::pow2(df);
485  sum2 += r * W(i, j) / df;
486  sum3 += r * W(i, j) * W(i, i) / Constant::pow2(df);
487 
488  Numeric sum_tmp = 0;
489  for (Index k = 0; k < n; k++) {
490  const Numeric dfk = band.F0(k) - band.F0(i);
491  if (std::isnormal(dfk)) sum_tmp += W(k, j) * W(i, k) / (dfk * df);
492  }
493  sum4 += r * sum_tmp;
494  }
495  }
496  G[i] = sum1 - Constant::pow2(sum2) + 2 * sum3 - 2 * sum4;
497  }
498  return G;
499 }
500 
502  const ArrayOfSpeciesTag& collider_species,
503  const Vector& collider_species_vmr,
504  const SpeciesAuxData& partition_functions,
505  const Numeric& T,
506  const Index& size) try {
507  const Vector population =
508  population_density_vector(band, partition_functions, T);
509  const Vector dipole = dipole_vector(band, partition_functions);
510  const Matrix W = hartmann_relaxation_matrix(band,
511  population,
512  dipole,
513  collider_species,
514  collider_species_vmr,
515  partition_functions,
516  T,
517  size);
518  return W;
519 } catch (const std::exception& e) {
521  os << "Errors in calls by *hartmann_ecs_interface*:\n";
522  os << e.what();
523  throw std::runtime_error(os.str());
524 }
525 
527  const Rational& Jku,
528  const Rational& Jju,
529  const Rational& Jkl,
530  const Rational& Jjl,
531  const Rational& l2ku,
532  const Rational& l2ju,
533  const Rational& l2kl,
534  const Rational& l2jl,
535  const Numeric& j_rho,
536  const Numeric& k_rho,
537  const BasisRate& br,
538  const AdiabaticFactor& af,
539  const Numeric& T,
540  const Numeric& B0,
541  const Numeric& main_mass,
542  const Numeric& collider_mass) {
543  const bool jbig = Jjl >= Jkl;
544 
545  // Prepare for the wigner calculations --- NOTE: twice the values of J and l2 required for fast Wigner-solver
546  // Also, prepare for initial and final phase to go from j to k if Jj >= Jk and vice verse
547  const int Ji = (2 * (jbig ? Jju : Jku)).toInt();
548  const int Jf = (2 * (jbig ? Jjl : Jkl)).toInt();
549  const int Ji_p = (2 * (jbig ? Jku : Jju)).toInt();
550  const int Jf_p = (2 * (jbig ? Jkl : Jjl)).toInt();
551  const int li = (2 * (jbig ? l2ju : l2ku)).toInt();
552  const int lf = (2 * (jbig ? l2jl : l2kl)).toInt();
553 
554  // Find best start and end-point of the summing loop
555  // const int st = std::max(Ji - Ji_p, Jf - Jf_p);
556  const int en = std::min(Ji + Ji_p, Jf + Jf_p);
557 
558  // Adiabatic factor for Ji
559  const Numeric AF1 = af.get(Ji / 2, B0, T, main_mass, collider_mass);
560 
561  // Scale constant for final state NOTE: "%4" and lack of 2*J because Fast library already doubles these numbers
562  const Numeric K1 = (((li + lf) % 4) ? 1.0 : -1.0) * Numeric(Ji_p + 1) *
563  sqrt(Numeric((Jf + 1) * (Jf_p + 1))) * AF1;
564 
565  Numeric sum = 0;
566  for (int L = 4; L <= en; L += 4) {
567  // for(int L = st>4?st:4; L <= en; L+=4) {
568  // Basis rate for L
569  const Numeric QL = br.get(L / 2, B0, T);
570 
571  // Adiabatic factor for L
572  const Numeric AF2 = af.get(L / 2, B0, T, main_mass, collider_mass);
573 
574  // The wigner-symbol following Niro etal 2004
575  const Numeric y = co2_ecs_wigner_symbol(Ji, Jf, Ji_p, Jf_p, L, li, lf);
576 
577  // Sum to the total
578  sum += QL * y / AF2;
579  }
580 
581  sum *= K1;
582 
583  const Numeric r = k_rho / j_rho;
584  return {jbig ? sum : sum * r, jbig ? sum / r : sum};
585 }
586 
587 constexpr auto params = 10;
588 
590  const ArrayOfRational& Ji,
591  const ArrayOfRational& Jf,
592  const ArrayOfRational& l2i,
593  const ArrayOfRational& l2f,
594  [[maybe_unused]] const Vector& F0,
595  const Vector& d,
596  const Vector& rho,
597  const Numeric& T,
598  const Vector& a = Vector(params, 1)) {
599  const Index n = Ji.nelem();
600  Numeric c[params];
601 
602  Rational rmax = Ji[0];
603  for (auto& r : Ji) rmax = max(r, rmax);
604  for (auto& r : Jf) rmax = max(r, rmax);
605 
606  M = 0;
607 
608 #pragma omp parallel for schedule( \
609  guided, 1) if (DO_FAST_WIGNER && !arts_omp_in_parallel())
610  for (Index i = 0; i < n; i++) {
611  wig_temp_init(int(2 * rmax));
612  for (Index j = 0; j < n; j++) {
613  if (i == j) continue; // to next loop
614  if (Ji[i] < Ji[j]) continue; // because we renormalize this
615 
616  const Numeric K1 = (((l2i[i] + l2f[i]) % 2) ? 1 : -1) *
617  Numeric(2 * Ji[j] + 1) *
618  sqrt((2 * Jf[i] + 1) * (2 * Jf[j] + 1));
619  const Numeric d_ratio = d[j] / d[i];
620  [[maybe_unused]] const Numeric exp =
621  (Ji[i] == 0) ? Numeric(Ji[j] / Ji[i]) : Numeric(Ji[j]) / 0.5;
622 
623  const int en = std::min(int(Ji[i] * 2) + int(Ji[j] * 2),
624  int(Jf[i] * 2) + int(Jf[j] * 2));
625  for (int L = 4; L <= en; L += 4) {
626  const Numeric K2 = co2_ecs_wigner_symbol(int(2 * Ji[i]),
627  int(2 * Jf[i]),
628  int(2 * Ji[j]),
629  int(2 * Jf[j]),
630  L,
631  int(2 * l2i[i]),
632  int(2 * l2f[i]));
633 
634  const Numeric dE = L / 2 * (L / 2 + 1);
635  c[0] = 1;
636  c[1] = 1 / T;
637  c[2] = dE;
638  c[3] = dE / T;
639  c[4] = dE * dE;
640  c[5] = std::log(dE);
641  c[6] = std::log(dE) / T;
642  c[7] = std::log(dE) * std::log(dE);
643  c[8] = 1 / dE;
644  c[9] = 1 / dE / dE;
645 
646  for (auto kk = 0; kk < params; kk++)
647  M(i, j, kk) += a[kk] * K2 * (std::isnormal(c[kk]) ? c[kk] : 0);
648  }
649 
650  if (std::isnormal(d_ratio))
651  M(i, j, joker) *= d_ratio * K1;
652  else
653  M(i, j, joker) *= K1;
654  M(j, i, joker) = M(i, j, joker);
655 
656  M(i, j, joker) *= rho[j] / rho[i];
657  M(j, i, joker) *= rho[i] / rho[j];
658  }
659 
660  // Remove the temporary table
661  wig_temp_free();
662  }
663 }
664 
666  const ArrayOfRational& Jf,
667  const ArrayOfRational& l2i,
668  const ArrayOfRational& l2f,
669  const Vector& F0,
670  const Vector& d,
671  const Vector& rho,
672  const Vector& gamma,
673  const Numeric& T) {
674  const auto n = Ji.nelem();
675 
676  Vector a(params, 0);
677  Matrix A(n, params, 0);
678  Matrix W(n, n, 0);
679  Tensor3 M(n, n, params, 0);
680 
681  // Create the training-tensor
682  linearized_relaxation_matrix(M, Ji, Jf, l2i, l2f, F0, d, rho, T);
683 
684  // Set the over-determined matrix for least-square-fit
685  for (Index i = 0; i < n; i++) {
686  for (Index j = 0; j < n; j++) {
687  A(i, joker) += M(i, j, joker);
688  }
689  }
690 
691  // Perform least-square-fit
692  [[maybe_unused]] const Numeric R2 = lsf(a, A, gamma);
693 
694  // Create the proper tensor by recomputing the training tensor with new a-ratios
695  linearized_relaxation_matrix(M, Ji, Jf, l2i, l2f, F0, d, rho, T, a);
696 
697  // Fill the relaxation matrix
698  for (Index i = 0; i < n; i++) {
699  for (Index j = 0; j < n; j++) {
700  if (i == j)
701  W(i, j) = gamma[i];
702  else
703  W(i, j) = M(i, j, joker).sum();
704  }
705  }
706 
707  return W;
708 }
709 
711  using namespace Constant;
712  using namespace Molecule::O2_66;
713 
714  Numeric const1 = 0.086 + 8154e-7 * T;
715  static constexpr Numeric const2 = 0.5805;
716 
717  return (2 * L + 1) / std::pow(L * L + L, const1) *
718  std::exp(-const2 * h * hamiltonian_freq(L) / (k * T));
719 }
720 
722  Numeric T,
723  Numeric collider_mass) {
724  using namespace Constant;
725  using namespace Conversion;
726  using namespace Molecule::O2_66;
727 
728  static constexpr Numeric const1 = 0.545e-10;
729  static constexpr Numeric constant = 2000 * R * inv_pi * pow2(inv_ln_2);
730 
731  // Mean speed of collisions
732  const Numeric invmu = (1 / mass + 1 / collider_mass);
733  const Numeric vm2 = constant * T * invmu;
734 
735  return 1 / pow2(1.0 + pow2(freq2angfreq(hamiltonian_freq(L) -
736  hamiltonian_freq(L - 2)) *
737  const1) /
738  vm2 / 24.0);
739 }
740 
742  const Rational& J1u,
743  const Rational& N1u,
744  const Rational& J1l,
745  const Rational& N1l,
746  const Rational& J2u,
747  const Rational& N2u,
748  const Rational& J2l,
749  const Rational& N2l,
750  const Numeric& rho1,
751  const Numeric& rho2,
752  const Numeric& T,
753  const Numeric& collider_mass) {
754 
755  // Find which is the 'upper' transition
756  const bool onebig =
758  J1u.toNumeric(), (J1u - N1u).toInt(), (J1u - N1u).toInt()) >
760  J2u.toNumeric(), (J2u - N2u).toInt(), (J2u - N2u).toInt());
761 
762  // Define the transitions as in Makarov etal 2013, double the value for wiglib
763  const int Nk = (2 * (onebig ? N1u : N2u).toInt());
764  const int Nkp = (2 * (onebig ? N1l : N2l).toInt());
765  const int Jk = (2 * (onebig ? J1u : J2u).toInt());
766  const int Jkp = (2 * (onebig ? J1l : J2l).toInt());
767  const int Nl = (2 * (onebig ? N2u : N1u).toInt());
768  const int Nlp = (2 * (onebig ? N2l : N1l).toInt());
769  const int Jl = (2 * (onebig ? J2u : J1u).toInt());
770  const int Jlp = (2 * (onebig ? J2l : J1l).toInt());
771 
772  if (Nl not_eq Nlp or Nk not_eq Nkp) throw "ERRROR, bad N-values";
773 
774  // 'length' of numbers
775  const Numeric lNk = std::sqrt(Nk + 1.0);
776  const Numeric lNl = std::sqrt(Nl + 1.0);
777  const Numeric lJk = std::sqrt(Jk + 1.0);
778  const Numeric lJl = std::sqrt(Jl + 1.0);
779  const Numeric lJkp = std::sqrt(Jkp + 1.0);
780  const Numeric lJlp = std::sqrt(Jlp + 1.0);
781 
782  // Constant independenf of L
783  const Numeric const1 = lNk * lNl * std::sqrt(lJk * lJl * lJkp * lJlp) *
785 
786  Numeric sum = 0;
787  for (int L = 4; L < 400; L += 4) {
788  const int sgn = ((Jk + Jl + L + 2) % 4) ? 1 : -1;
789 
790  const Numeric const2 =
791  sgn * const1 * o2_66_adiabatic_factor_makarov(L / 2, T, collider_mass) /
793 
794  sum += o2_ecs_wigner_symbol(Nl, Nk, Jl, Jk, Jlp, Jkp, L) * const2;
795  }
796 
797  return {onebig ? sum : sum * rho2 / rho1, onebig ? sum * rho1 / rho2 : sum};
798 }
799 
801  const Numeric& B0,
802  const Numeric& T,
803  const Numeric& main_mass,
804  const Numeric& collider_mass) const {
805  using namespace Constant;
806 
807  using namespace Conversion;
808 
809  if (L < 1) return 0.;
810 
811  static constexpr Numeric constant = 2000 * R * inv_pi * pow2(inv_ln_2);
812 
813  const Numeric invmu = (1 / main_mass + 1 / collider_mass);
814 
815  const Numeric vm2 = constant * T * invmu;
816 
817  return 1 / pow2(1.0 + pow2(freq2angfreq(B0) * (4 * L - 2) *
818  mdata[Index(HartmannPos::dc)]) /
819  vm2 / 24.0);
820 }
821 
823  const Numeric& B0,
824  const Numeric& T) const {
825  using namespace Constant;
826 
827  const Numeric& a1 = mdata[Index(HartmannPos::a1)];
828  const Numeric& a2 = mdata[Index(HartmannPos::a2)];
829  const Numeric& a3 = mdata[Index(HartmannPos::a3)];
830 
831  const Numeric EL = L * L + L;
832  return a1 / std::pow(EL, a2) * std::exp(-a3 * h * B0 * EL / (k * T));
833 }
834 
836  ConstVectorView x,
837  const Numeric exp,
838  const Numeric x0) {
839  const auto n = y.nelem();
840 
841  if (n not_eq x.nelem())
842  throw std::runtime_error("Bad input to compute_2nd_order_lm_coeff");
843 
844  Index best_x = 0;
845  for (auto i = 0; i < n - 1; i++) {
846  if (x[i] >= x[i + 1])
847  throw std::runtime_error(
848  "Bad structure on x-input; must be constantly increasing");
849  if (x[i] > x0) best_x++;
850  }
851 
852  Matrix A(n, 2);
853  Vector ans(2, y[best_x] * 0.5), dans(2, 0), delta(n, 0);
854 
855  Numeric rel_res = 1e99;
856  Numeric rel_res_limit = 1e-16;
857  auto loop_count = 0;
858  constexpr auto max_loop_count = 20;
859 
860  do {
861  for (auto i = 0; i < n; i++) {
862  const Numeric theta = x0 / x[i];
863  const Numeric TP = pow(theta, exp);
864  A(i, 0) = TP;
865  A(i, 1) = (theta - 1.0) * TP;
866  const Numeric f = ans[0] * TP + ans[1] * (theta - 1.0) * TP;
867  delta[i] = y[i] - f;
868  }
869 
870  // Least square fit
871  const Numeric res = lsf(dans, A, delta);
872 
873  if (not std::isnormal(res)) break;
874 
875  rel_res = std::abs(dans[0] / ans[0]) + std::abs(dans[1] / ans[1]);
876  ans += dans;
877  loop_count += 1;
878 
879  } while (rel_res > rel_res_limit and loop_count < max_loop_count);
880 
881  return {ans[0], ans[1]};
882 }
883 
885  const Vector& population,
886  const Vector& dipole,
887  const Eigen::ComplexEigenSolver<Eigen::MatrixXcd>& M) {
888  const auto n = population.nelem();
889 
890  auto& V = M.eigenvectors();
891  auto Vinv = V.inverse().eval();
892 
893  ComplexVector B(n);
894 
895  for (auto i = 0; i < n; i++) {
896  for (auto j1 = 0; j1 < n; j1++) {
897  for (auto j2 = 0; j2 < n; j2++) {
898  B[i] +=
899  population[j1] * dipole[j1] * dipole[j2] * V(i, j2) * Vinv(j1, i);
900  }
901  }
902  }
903  return B;
904 }
905 
906 Numeric total_linestrengths(const Vector& population, const Vector& dipole) {
907  using namespace Constant;
908  const auto n = population.nelem();
909 
910  Numeric I0 = 0;
911  for (auto i = 0; i < n; i++) I0 += pi * population[i] * pow2(dipole[i]);
912  return I0;
913 }
914 
915 void relmatInAir(Matrix& relmat,
916  const AbsorptionLines& band,
917  const SpeciesAuxData& partition_functions,
918  const Index& wigner_initialized,
919  const Numeric& temperature) try {
920  // Only for Earth's atmosphere
921  const ArrayOfSpeciesTag collider_species = {SpeciesTag("O2-66"),
922  SpeciesTag("N2-44")};
923  const Vector collider_species_vmr = {0.21, 0.79};
924 
925 
926  if (band.Species() == SpeciesTag("CO2-626").Species() and
927  band.Isotopologue() == SpeciesTag("CO2-626").Isotopologue()) {
928  } else if (band.Species() == SpeciesTag("O2-66").Species() and
929  band.Isotopologue() == SpeciesTag("O2-66").Isotopologue()) {
930  } else
931  throw "Limit in functionality encountered. We only support CO2-626 and O2-66 for now.";
932 
933  if (band.BroadeningSpecies().nelem() not_eq 2)
934  throw "Bad species length, only works for self+air broadening for now";
935 
937  relmat = hartmann_ecs_interface(band,
938  collider_species,
939  collider_species_vmr,
940  partition_functions,
941  temperature,
942  wigner_initialized);
943  else
944  throw "Population type has not been implemented";
945 } catch (const char* e) {
947  os << "Errors raised by *relmatInAir*:\n";
948  os << "\tError: " << e << '\n';
949  throw std::runtime_error(os.str());
950 } catch (const std::exception& e) {
952  os << "Errors in calls by *relmatInAir*:\n";
953  os << e.what();
954  throw std::runtime_error(os.str());
955 }
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
Namespace containing several constants, physical and mathematical.
Definition: constants.h:61
AdiabaticFactor adiabatic_factor(const SpeciesTag &main, const SpeciesTag &collider)
Definition: linemixing.cc:60
Struct to help keep position of the two matched outputs clear.
Definition: linemixing.h:304
Vector vmrs(const ConstVectorView &atmospheric_vmrs, const ArrayOfArrayOfSpeciesTag &atmospheric_species, const QuantumIdentifier &self, const ArrayOfSpeciesTag &lineshape_species, bool self_in_list, bool bath_in_list, Type type)
Returns a VMR vector for this model&#39;s main calculations.
constexpr Numeric gamma
Definition: linemixing.h:121
Numeric dstimulated_emissiondT(Numeric T, Numeric F0)
Computes temperature derivative of exp(-hf/kT)
Definition: linescaling.cc:116
O2-66 constants.
Definition: linemixing.h:108
Numeric o2_66_adiabatic_factor_makarov(int L, Numeric T, Numeric collider_mass)
Definition: linemixing.cc:721
Numeric dboltzman_factordT(Numeric T, Numeric E0)
Computes temperature derivatives exp(- E0/kT)
Definition: linescaling.cc:182
const ArrayOfSpeciesTag & BroadeningSpecies() const noexcept
Returns the broadening species.
Numeric lsf(VectorView x, ConstMatrixView A, ConstVectorView y, bool residual) noexcept
Least squares fitting by solving x for known A and y.
Definition: lin_alg.cc:2387
A class implementing complex numbers for ARTS.
Numeric T0() const noexcept
Returns reference temperature.
Numeric E0(size_t k) const noexcept
Lower level energy.
Index nelem() const
Number of elements.
Definition: array.h:194
#define x0
void linearized_relaxation_matrix(Tensor3 &M, const ArrayOfRational &Ji, const ArrayOfRational &Jf, const ArrayOfRational &l2i, const ArrayOfRational &l2f, [[maybe_unused]] const Vector &F0, const Vector &d, const Vector &rho, const Numeric &T, const Vector &a=Vector(params, 1))
Definition: linemixing.cc:589
G0 G2 FVC Y DV Numeric E0
constexpr auto params
Definition: linemixing.cc:587
Numeric population_density(Numeric T, Numeric E0, Numeric F0, Numeric QT)
Definition: linemixing.cc:326
Rational LowerQuantumNumber(size_t k, QuantumNumberType qnt) const noexcept
Quantum number lower level.
The Vector class.
Definition: matpackI.h:782
Numeric o2_66_inelastic_cross_section_makarov(int L, Numeric T)
Definition: linemixing.cc:710
#define abs(x)
Index Species() const
Molecular species index.
Matrix CO2_ir_training(const ArrayOfRational &Ji, const ArrayOfRational &Jf, const ArrayOfRational &l2i, const ArrayOfRational &l2f, const Vector &F0, const Vector &d, const Vector &rho, const Vector &gamma, const Numeric &T)
CO2 IR training algorithm for linearization.
Definition: linemixing.cc:665
constexpr T hitran2arts_broadening(T x)
Definition: constants.h:474
Index Isotopologue() const noexcept
Isotopologue Index.
Numeric reduced_magnetic_quadrapole(Rational Jf, Rational Ji, Rational N)
Compute the reduced magnetic quadrapole moment.
Numeric lte_linestrength(Numeric S0, Numeric E0, Numeric F0, Numeric QT0, Numeric T0, Numeric QT, Numeric T)
Gets the local thermodynamic equilibrium line strength.
Linear algebra functions.
Vector rosenkranz_shifting_second_order(const AbsorptionLines &band, const Matrix &W)
Computes DV for Rosenkranz&#39;s line mixing coefficients.
Definition: linemixing.cc:449
Numeric boltzman_factor(Numeric T, Numeric E0)
Computes exp(- E0/kT)
Definition: linescaling.cc:176
invlib::Vector< ArtsVector > Vector
invlib wrapper type for ARTS vectors.
Definition: oem.h:32
ComplexVector equivalent_linestrengths(const Vector &population, const Vector &dipole, const Eigen::ComplexEigenSolver< Eigen::MatrixXcd > &M)
Equivalent line strengths.
Definition: linemixing.cc:884
Some molecular constants.
Index NumLines() const noexcept
Number of lines.
Line mixing calculation implementation.
Vector population_density_vector(const AbsorptionLines &band, const SpeciesAuxData &partition_functions, const Numeric &T)
Compute the population density.
Definition: linemixing.cc:342
Numeric stimulated_emission(Numeric T, Numeric F0)
Computes exp(-hf/kT)
Definition: linescaling.cc:110
Matrix relaxation_matrix_calculations(const AbsorptionLines &band, const Vector &population, const SpeciesTag &collider, const Numeric &collider_vmr, const Numeric &T, const Index &size)
Definition: linemixing.cc:99
#define min(a, b)
constexpr T pow2(T x)
power of two
Definition: constants.h:64
PopulationType Population() const noexcept
Returns population style.
Wigner symbol interactions.
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:140
bool Self() const noexcept
Returns self broadening status.
Contains sorting routines.
Numeric I0(size_t k) const noexcept
Reference line strength.
Numeric o2_ecs_wigner_symbol(int Nl, int Nk, int Jl, int Jk, int Jl_p, int Jk_p, int L)
Returns the wigner symbol used in Makarov etal 2013.
Rational UpperQuantumNumber(size_t k, QuantumNumberType qnt) const noexcept
Quantum number upper level.
Stuff related to lineshape functions.
const AuxType & getParamType(const Index species, const Index isotopologue) const
Return a constant reference to the parameter types.
Definition: absorption.h:276
The Tensor3 class.
Definition: matpackIII.h:339
void swap(ComplexVector &v1, ComplexVector &v2)
Swaps two objects.
Definition: complex.cc:731
#define d0
bool IsSpecies(const String &s) const
Check if the species is same as SpeciesTag(s).Species()
RedPoleType
Type of reduced dipole.
Definition: linemixing.h:419
Numeric reduced_rovibrational_dipole(Rational Jf, Rational Ji, Rational lf, Rational li, Rational k=Rational(1))
Compute the reduced rovibrational dipole moment.
Numeric co2_ecs_wigner_symbol(int Ji, int Jf, int Ji_p, int Jf_p, int L, int li, int lf)
Returns the wigner symbol used in Niro etal 2004.
Numeric get(const Numeric &L, const Numeric &B0, const Numeric &T) const
Get the basis rate.
Definition: linemixing.h:290
OffDiagonalElementOutput O2_66_MW(const Rational &J1u, const Rational &N1u, const Rational &J1l, const Rational &N1l, const Rational &J2u, const Rational &N2u, const Rational &J2l, const Rational &N2l, const Numeric &rho1, const Numeric &rho2, const Numeric &T, const Numeric &collider_mass)
O2-66 MW off diagonal element computer.
Definition: linemixing.cc:741
Basis rate of transitions.
Definition: linemixing.h:245
Numeric get(const Numeric &L, const Numeric &B0, const Numeric &T, const Numeric &main_mass, const Numeric &collider_mass) const
Get AF.
Definition: linemixing.h:227
void relmatInAir(Matrix &relmat, const AbsorptionLines &band, const SpeciesAuxData &partition_functions, const Index &wigner_initialized, const Numeric &temperature)
Compute the relaxation matrix in air mixture.
Definition: linemixing.cc:915
Implements rational numbers to work with other ARTS types.
Definition: rational.h:54
bool Bath() const noexcept
Returns bath broadening status.
Numeric hamiltonian_freq(T J, int dcol=0, int drow=0)
Hamiltonian frequency.
Definition: linemixing.h:135
Index Species() const noexcept
Species Index.
A tag group can consist of the sum of several of these.
Numeric mol_X(const Numeric &L, const Numeric &B0, const Numeric &T) const
Computes the basis rate using Hartman method.
Definition: linemixing.cc:822
constexpr Numeric kaycm2freq(T x)
Definition: constants.h:383
Vector dipole_vector(const AbsorptionLines &band, const SpeciesAuxData &partition_functions)
Dipole vector.
Definition: linemixing.cc:371
#define a1
Definition: complex.h:55
const Joker joker
const ArrayOfGriddedField1 & getParam(const Index species, const Index isotopologue) const
Return a constant reference to the parameters.
Definition: absorption.cc:145
Numeric getB0(const SpeciesTag &main)
Definition: linemixing.cc:39
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
Numeric SpeciesMass() const
Mass of main species.
The Matrix class.
Definition: matpackI.h:1113
bool IsIsotopologue(const String &i) const
Check if the isotopologue is same as SpeciesTag(s).Isotopologue()
To keep track of (y0 + y1 * (T0 / T - 1.)) * pow(T0 / T, n)
Definition: linemixing.h:462
int main(int argc, char **argv)
This is the main function of ARTS.
Definition: main.cc:612
Numeric dpopulation_densitydT(Numeric T, Numeric E0, Numeric F0, Numeric QT, Numeric dQTdT)
Definition: linemixing.cc:333
BasisRate basis_rate(const SpeciesTag &main, const SpeciesTag &collider, const Numeric &T, const Numeric &T0)
Definition: linemixing.cc:74
constexpr Numeric toNumeric() const
Converts this to a Numeric.
Definition: rational.h:146
Numeric pow(const Rational base, Numeric exp)
Power of.
Definition: rational.h:628
void get_sorted_indexes(ArrayOfIndex &sorted, const T &data)
get_sorted_indexes
Definition: sorting.h:73
LineShape::Output ShapeParameters(size_t k, Numeric T, Numeric P, const Vector &vmrs) const noexcept
Line shape parameters.
The ComplexVector class.
Definition: complex.h:572
Vector reduced_dipole_vector(const AbsorptionLines &band, const RedPoleType type)
Reduced dipole vector.
Definition: linemixing.cc:401
Matrix hartmann_ecs_interface(const AbsorptionLines &band, const ArrayOfSpeciesTag &collider_species, const Vector &collider_species_vmr, const SpeciesAuxData &partition_functions, const Numeric &T, const Index &size)
Energy corrected sudden relaxation matrix using Hartmann&#39;s method.
Definition: linemixing.cc:501
basic_ostringstream< char, string_char_traits< char >, alloc > ostringstream
Definition: sstream.h:204
Vector rosenkranz_first_order(const AbsorptionLines &band, const Matrix &W, const Vector &d0)
Computes Y for Rosenkranz&#39;s line mixing coefficients.
Definition: linemixing.cc:431
Adiabatic factor computations.
Definition: linemixing.h:174
G0 G2 FVC Y DV F0
Constains various line scaling functions.
OffDiagonalElementOutput CO2_IR(const Rational &Jku, const Rational &Jju, const Rational &Jkl, const Rational &Jjl, const Rational &l2ku, const Rational &l2ju, const Rational &l2kl, const Rational &l2jl, const Numeric &j_rho, const Numeric &k_rho, const BasisRate &br, const AdiabaticFactor &af, const Numeric &T, const Numeric &B0, const Numeric &main_mass, const Numeric &collider_mass)
CO2 IR off diagonal element computer.
Definition: linemixing.cc:526
Numeric F0(size_t k) const noexcept
Central frequency.
Namespace containing several practical unit conversions, physical and mathematical.
Definition: constants.h:319
constexpr Numeric freq2angfreq(T x)
Definition: constants.h:409
#define max(a, b)
Numeric mol_X(const Numeric &L, const Numeric &B0, const Numeric &T, const Numeric &main_mass, const Numeric &collider_mass) const
Hartmann AF.
Definition: linemixing.cc:800
SecondOrderLineMixingCoeffs compute_2nd_order_lm_coeff(ConstVectorView y, ConstVectorView x, const Numeric exp, const Numeric x0)
Fit to a second order line mixing formula the input.
Definition: linemixing.cc:835
A constant view of a Vector.
Definition: matpackI.h:400
Matrix hartmann_relaxation_matrix(const AbsorptionLines &band, const Vector &population, const Vector &d0, const ArrayOfSpeciesTag &colliders, const Vector &colliders_vmr, const SpeciesAuxData &partition_functions, const Numeric &T, const Index &size)
Definition: linemixing.cc:286
Header file for stuff related to absorption species tags.
#define a2
Definition: complex.h:57
constexpr Numeric mass
Definition: linemixing.h:125
const QuantumIdentifier & QuantumIdentity() const noexcept
Returns identity status.
constexpr Numeric B
Definition: linemixing.h:113
Vector rosenkranz_scaling_second_order(const AbsorptionLines &band, const Matrix &W, const Vector &d0)
Computes G for Rosenkranz&#39;s line mixing coefficients.
Definition: linemixing.cc:466
Numeric single_partition_function(const Numeric &T, const SpeciesAuxData::AuxType &partition_type, const ArrayOfGriddedField1 &partition_data)
Computes the partition function at one temperature.
Definition: linescaling.cc:72
Auxiliary data for isotopologues.
Definition: absorption.h:217
Array< SpeciesTag > ArrayOfSpeciesTag
A tag group is an array of SpeciesTags.
constexpr int toInt(int n=1) const
Converts the value to int by n-scaled division in Index form.
Definition: rational.h:157
Numeric total_linestrengths(const Vector &population, const Vector &dipole)
Sum of line strengths.
Definition: linemixing.cc:906
String SpeciesName() const noexcept
Species Name.
Index Isotopologue() const
Isotopologue species index.
LineShape::Type LineShapeType() const noexcept
Returns lineshapetype style.
Numeric sqrt(const Rational r)
Square root.
Definition: rational.h:620
void normalize_relaxation_matrix(Matrix &W, const Vector &population, const Vector &, const AbsorptionLines &band, const SpeciesAuxData &partition_functions, const Numeric &T)
Definition: linemixing.cc:200
Species
Definition: linemixing.cc:97