ARTS  2.4.0(git:4fb77825)
absorptionlines.cc
Go to the documentation of this file.
1 /* Copyright (C) 2019
2  Richard Larsson <larsson@mps.mpg.de>
3 
4 
5  This program is free software; you can redistribute it and/or modify it
6  under the terms of the GNU General Public License as published by the
7  Free Software Foundation; either version 2, or (at your option) any
8  later version.
9 
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20  USA. */
21 
32 #include "absorptionlines.h"
33 
34 #include "absorption.h"
35 #include "constants.h"
36 #include "file.h"
37 #include "global_data.h"
38 #include "quantum_parser_hitran.h"
39 
41  for(size_t i=0; i<mlocalquanta.size(); i++)
42  if(mlocalquanta[i] == qnt)
43  return mlines[k].LowerQuantumNumber(i);
44  return mquantumidentity.LowerQuantumNumber(qnt);
45 }
46 
48  for(size_t i=0; i<mlocalquanta.size(); i++)
49  if(mlocalquanta[i] == qnt)
50  return mlines[k].UpperQuantumNumber(i);
51  return mquantumidentity.UpperQuantumNumber(qnt);
52 }
53 
55  for(size_t i=0; i<mlocalquanta.size(); i++)
56  if(mlocalquanta[i] == qnt)
57  return mlines[k].LowerQuantumNumber(i);
58  return mquantumidentity.LowerQuantumNumber(qnt);
59 }
60 
62  for(size_t i=0; i<mlocalquanta.size(); i++)
63  if(mlocalquanta[i] == qnt)
64  return mlines[k].UpperQuantumNumber(i);
65  return mquantumidentity.UpperQuantumNumber(qnt);
66 }
67 
69  auto x = mlines[k].LineShape().GetParams(T, mT0, P, vmrs);
70 
71  if (not DoLineMixing(P)) x.Y = x.G = x.DV = 0;
72 
73  return x;
74 }
75 
77  auto x = mlines[k].LineShape().GetTemperatureDerivs(T, mT0, P, vmrs);
78 
79  if (not DoLineMixing(P)) x.Y = x.G = x.DV = 0;
80 
81  return x;
82 }
83 
84 Index Absorption::Lines::LineShapePos(const Index& spec) const noexcept {
85  // Is always first if this is self and self broadening exists
86  if(mselfbroadening and spec == mquantumidentity.Species())
87  return 0;
88 
89  // First and last might be artificial so they should not be checked
90  for(Index i=Index(mselfbroadening); i<Index(mbroadeningspecies.size())-Index(mbathbroadening); i++) {
91  if(spec == mbroadeningspecies[i].Species())
92  return Index(i);
93  }
94 
95  // At this point, the ID is not explicitly among the broadeners, but bath broadening means its VMR still might matter
96  if(mbathbroadening)
97  return Index(mbroadeningspecies.size())-Index(mbathbroadening);
98  else
99  return -1;
100 }
101 
103  const auto self = vmr_qid.Species() == mquantumidentity.Species();
104  const auto& ls = mlines[k].LineShape();
105 
106  if (mselfbroadening and self) {
107  auto x = ls.GetVMRDerivs(T, mT0, P, 0);
108 
109  if (mbathbroadening)
110  x = LineShape::differenceOutput(x, ls.GetVMRDerivs(
111  T, mT0, P, ls.nelem() - 1));
112 
113  if (not DoLineMixing(P)) x.Y = x.G = x.DV = 0;
114 
115  return x;
116  } else if (mbathbroadening and self)
117  return {0, 0, 0, 0, 0, 0, 0, 0, 0};
118  else {
119  auto x = ls.GetVMRDerivs(T, mT0, P, LineShapePos(vmr_qid));
120 
121  if (mbathbroadening)
122  x = LineShape::differenceOutput(x, ls.GetVMRDerivs(
123  T, mT0, P, ls.nelem() - 1));
124 
125  if (not DoLineMixing(P)) x.Y = x.G = x.DV = 0;
126 
127  return x;
128  }
129 }
130 
131 Numeric Absorption::Lines::ShapeParameter_dInternal(size_t k, Numeric T, Numeric P, const Vector& vmrs, const RetrievalQuantity& derivative) const noexcept {
132  const auto self = derivative.Mode() == LineShape::self_broadening;
133  const auto bath = derivative.Mode() == LineShape::bath_broadening;
134  const auto& ls = mlines[k].LineShape();
135 
136  if(derivative.QuantumIdentity().Species() not_eq Species() or
137  derivative.QuantumIdentity().Isotopologue() not_eq Isotopologue())
138  return 0;
139  else if(self and mselfbroadening)
140  return ls.GetInternalDeriv(
141  T, mT0, P, 0, vmrs, derivative.PropMatType());
142  else if(self)
143  return ls.GetInternalDeriv(
144  T, mT0, P, LineShapePos(SpeciesTag(derivative.Mode()).Species()), vmrs, derivative.PropMatType());
145  else if(bath and mbathbroadening)
146  return ls.GetInternalDeriv(
147  T, mT0, P, ls.nelem() - 1, vmrs, derivative.PropMatType());
148  else if(bath)
149  return 0;
150  else
151  return ls.GetInternalDeriv(
152  T, mT0, P, LineShapePos(SpeciesTag(derivative.Mode()).Species()), vmrs, derivative.PropMatType());
153 }
154 
156  // Default data and values for this type
158  data.selfbroadening = true;
159  data.bathbroadening = true;
160  data.lineshapetype = LineShape::Type::VP;
161  data.species.resize(2);
162 
163  // Global species lookup data:
165 
166  // We need a species index sorted by Arts identifier. Keep this in a
167  // static variable, so that we have to do this only once. The ARTS
168  // species index is ArtsMap[<Arts String>].
169  static map<String, SpecIsoMap> ArtsMap;
170 
171  // Remember if this stuff has already been initialized:
172  static bool hinit = false;
173 
174  if (!hinit) {
175  for (Index i = 0; i < species_data.nelem(); ++i) {
176  const SpeciesRecord& sr = species_data[i];
177 
178  for (Index j = 0; j < sr.Isotopologue().nelem(); ++j) {
179  SpecIsoMap indicies(i, j);
180  String buf = sr.Name() + "-" + sr.Isotopologue()[j].Name();
181  ArtsMap[buf] = indicies;
182  }
183  }
184  hinit = true;
185  }
186 
187  // This always contains the rest of the line to parse. At the
188  // beginning the entire line. Line gets shorter and shorter as we
189  // continue to extract stuff from the beginning.
190  String line;
191 
192  // Look for more comments?
193  bool comment = true;
194 
195  while (comment) {
196  // Return true if eof is reached:
197  if (is.eof()) return data;
198 
199  // Throw runtime_error if stream is bad:
200  if (!is) throw runtime_error("Stream bad.");
201 
202  // Read line from file into linebuffer:
203  getline(is, line);
204 
205  // It is possible that we were exactly at the end of the file before
206  // calling getline. In that case the previous eof() was still false
207  // because eof() evaluates only to true if one tries to read after the
208  // end of the file. The following check catches this.
209  if (line.nelem() == 0 && is.eof()) return data;
210 
211  // @ as first character marks catalogue entry
212  char c;
213  extract(c, line, 1);
214 
215  // check for empty line
216  if (c == '@') {
217  comment = false;
218  }
219  }
220 
221  // read the arts identifier String
222  istringstream icecream(line);
223 
224  String artsid;
225  icecream >> artsid;
226 
227  if (artsid.length() != 0) {
228  // ok, now for the cool index map:
229  // is this arts identifier valid?
230  const map<String, SpecIsoMap>::const_iterator i = ArtsMap.find(artsid);
231  if (i == ArtsMap.end()) {
232  ostringstream os;
233  os << "ARTS Tag: " << artsid << " is unknown.";
234  throw runtime_error(os.str());
235  }
236 
237  SpecIsoMap id = i->second;
238 
239  // Set mspecies:
240  data.quantumidentity.Species(id.Speciesindex());
241 
242  // Set misotopologue:
243  data.quantumidentity.Isotopologue(id.Isotopologueindex());
244 
245  // Extract center frequency:
246  icecream >> double_imanip() >> data.line.F0();
247 
248  Numeric psf;
249  // Extract pressure shift:
250  icecream >> double_imanip() >> psf;
251 
252  // Extract intensity:
253  icecream >> double_imanip() >> data.line.I0();
254 
255  // Extract reference temperature for Intensity in K:
256  icecream >> double_imanip() >> data.T0;
257 
258  // Extract lower state energy:
259  icecream >> data.line.E0();
260 
261  // Extract air broadening parameters:
262  Numeric agam, sgam;
263  icecream >> double_imanip() >> agam;
264  icecream >> double_imanip() >> sgam;
265 
266  // Extract temperature coefficient of broadening parameters:
267  Numeric nair, nself;
268  icecream >> double_imanip() >> nair;
269  icecream >> double_imanip() >> nself;
270 
271  // Extract reference temperature for broadening parameter in K:
272  Numeric tgam;
273  icecream >> double_imanip() >> tgam;
274 
275  // Extract the aux parameters:
276  Index naux;
277  icecream >> naux;
278 
279  // resize the aux array and read it
280  ArrayOfNumeric maux;
281  maux.resize(naux);
282 
283  for (Index j = 0; j < naux; j++) {
284  icecream >> maux[j];
285  //cout << "maux" << j << " = " << maux[j] << "\n";
286  }
287 
288  // Extract accuracies:
289  try {
290  Numeric unused_numeric;
291  icecream >> double_imanip() >> unused_numeric /*mdf*/;
292  icecream >> double_imanip() >> unused_numeric /*mdi0*/;
293  icecream >> double_imanip() >> unused_numeric /*dagam*/;
294  icecream >> double_imanip() >> unused_numeric /*dsgam*/;
295  icecream >> double_imanip() >> unused_numeric /*dnair*/;
296  icecream >> double_imanip() >> unused_numeric /*dnself*/;
297  icecream >> double_imanip() >> unused_numeric /*dpsf*/;
298  } catch (const std::runtime_error&) {
299  }
300 
301  // Fix if tgam is different from ti0
302  if (tgam != data.T0) {
303  agam = agam * pow(tgam / data.T0, nair);
304  sgam = sgam * pow(tgam / data.T0, nself);
305  psf = psf * pow(tgam / data.T0, (Numeric).25 + (Numeric)1.5 * nair);
306  }
307 
308  // Set line shape computer
309  data.line.LineShape() = LineShape::Model(sgam, nself, agam, nair, psf);
310  }
311 
312  // That's it!
313  data.bad = false;
314  return data;
315 }
316 
318  // Default data and values for this type
320  data.selfbroadening = true;
321  data.bathbroadening = false;
322  data.lineshapetype = LineShape::Type::VP;
323 
324  // Global species lookup data:
326 
327  // We need a species index sorted by Arts identifier. Keep this in a
328  // static variable, so that we have to do this only once. The ARTS
329  // species index is ArtsMap[<Arts String>].
330  static map<String, SpecIsoMap> ArtsMap;
331 
332  // Remember if this stuff has already been initialized:
333  static bool hinit = false;
334 
335  if (!hinit) {
336  for (Index i = 0; i < species_data.nelem(); ++i) {
337  const SpeciesRecord& sr = species_data[i];
338 
339  for (Index j = 0; j < sr.Isotopologue().nelem(); ++j) {
340  SpecIsoMap indicies(i, j);
341  String buf = sr.Name() + "-" + sr.Isotopologue()[j].Name();
342 
343  ArtsMap[buf] = indicies;
344  }
345  }
346  hinit = true;
347  }
348 
349  // This always contains the rest of the line to parse. At the
350  // beginning the entire line. Line gets shorter and shorter as we
351  // continue to extract stuff from the beginning.
352  String line;
353 
354  // Look for more comments?
355  bool comment = true;
356 
357  while (comment) {
358  // Return true if eof is reached:
359  if (is.eof()) return data;
360 
361  // Throw runtime_error if stream is bad:
362  if (!is) throw runtime_error("Stream bad.");
363 
364  // Read line from file into linebuffer:
365  getline(is, line);
366 
367  // It is possible that we were exactly at the end of the file before
368  // calling getline. In that case the previous eof() was still false
369  // because eof() evaluates only to true if one tries to read after the
370  // end of the file. The following check catches this.
371  if (line.nelem() == 0 && is.eof()) return data;
372 
373  // @ as first character marks catalogue entry
374  char c;
375  extract(c, line, 1);
376 
377  // check for empty line
378  if (c == '@') {
379  comment = false;
380  }
381  }
382 
383  // read the arts identifier String
384  istringstream icecream(line);
385 
386  String artsid;
387  icecream >> artsid;
388 
389  if (artsid.length() != 0) {
390  // ok, now for the cool index map:
391  // is this arts identifier valid?
392  const map<String, SpecIsoMap>::const_iterator i = ArtsMap.find(artsid);
393  if (i == ArtsMap.end()) {
394  ostringstream os;
395  os << "ARTS Tag: " << artsid << " is unknown.";
396  throw runtime_error(os.str());
397  }
398 
399  SpecIsoMap id = i->second;
400 
401  // Set line ID
402  data.quantumidentity.Species(id.Speciesindex());
403  data.quantumidentity.Isotopologue(id.Isotopologueindex());
404 
405  // Extract center frequency:
406  icecream >> double_imanip() >> data.line.F0();
407 
408  // Extract intensity:
409  icecream >> double_imanip() >> data.line.I0();
410 
411  // Extract reference temperature for Intensity in K:
412  icecream >> double_imanip() >> data.T0;
413 
414  // Extract lower state energy:
415  icecream >> double_imanip() >> data.line.E0();
416 
417  // Extract Einstein A-coefficient:
418  icecream >> double_imanip() >> data.line.A();
419 
420  // Extract upper state stat. weight:
421  icecream >> double_imanip() >> data.line.g_upp();
422 
423  // Extract lower state stat. weight:
424  icecream >> double_imanip() >> data.line.g_low();
425 
426  LineShape::from_artscat4(icecream,
427  data.lineshapetype,
428  data.selfbroadening,
429  data.bathbroadening,
430  data.line.LineShape(),
431  data.species,
432  data.quantumidentity);
433 
434  // Remaining entries are the quantum numbers
435  String mquantum_numbers_str;
436  getline(icecream, mquantum_numbers_str);
437 
438  mquantum_numbers_str.trim();
439  // FIXME: OLE: Added this if to catch crash for species like CO, PH3
440  // where the line in the catalog is too short. Better would be to
441  // only read the n and j for Zeeman species, but we don't have that
442  // information here
443 
444  if (species_data[data.quantumidentity.Species()].Name() == "SO") {
445  // Note that atoi converts *** to 0.
446  data.quantumidentity.UpperQuantumNumbers().Set(
448  Rational(atoi(mquantum_numbers_str.substr(0, 3).c_str())));
449  data.quantumidentity.LowerQuantumNumbers().Set(
451  Rational(atoi(mquantum_numbers_str.substr(6, 3).c_str())));
452  data.quantumidentity.UpperQuantumNumbers().Set(
454  Rational(atoi(mquantum_numbers_str.substr(3, 3).c_str())));
455  data.quantumidentity.LowerQuantumNumbers().Set(
457  Rational(atoi(mquantum_numbers_str.substr(9, 3).c_str())));
458  }
459 
460  if (mquantum_numbers_str.nelem() >= 25) {
461  if (species_data[data.quantumidentity.Species()].Name() == "O2") {
462  String vstr = mquantum_numbers_str.substr(0, 3);
463  ArrayOfIndex v(3);
464  for (Index vi = 0; vi < 3; vi++) {
465  if (vstr[0] != ' ')
466  extract(v[vi], vstr, 1);
467  else
468  v[vi] = -1;
469  }
470 
471  if (v[2] > -1) {
472  data.quantumidentity.UpperQuantumNumbers().Set(QuantumNumberType::v1, Rational(v[2]));
473  data.quantumidentity.LowerQuantumNumbers().Set(QuantumNumberType::v1, Rational(v[2]));
474  }
475 
476  String qstr1 = mquantum_numbers_str.substr(4, 12);
477  String qstr2 = mquantum_numbers_str.substr(4 + 12 + 1, 12);
478  ArrayOfIndex q(6);
479  for (Index qi = 0; qi < 3; qi++) {
480  if (qstr1.substr(0, 4) != " ")
481  extract(q[qi], qstr1, 4);
482  else
483  q[qi] = -1;
484  }
485  for (Index qi = 3; qi < 6; qi++) {
486  if (qstr2.substr(0, 4) != " ")
487  extract(q[qi], qstr2, 4);
488  else
489  q[qi] = -1;
490  }
491 
492  if (q[0] > -1)
493  data.quantumidentity.UpperQuantumNumbers().Set(QuantumNumberType::N, Rational(q[0]));
494  if (q[1] > -1)
495  data.quantumidentity.UpperQuantumNumbers().Set(QuantumNumberType::J, Rational(q[1]));
496  if (q[2] > -1)
497  data.quantumidentity.UpperQuantumNumbers().Set(QuantumNumberType::F, q[2] - 1_2);
498  if (q[3] > -1)
499  data.quantumidentity.LowerQuantumNumbers().Set(QuantumNumberType::N, Rational(q[3]));
500  if (q[4] > -1)
501  data.quantumidentity.LowerQuantumNumbers().Set(QuantumNumberType::J, Rational(q[4]));
502  if (q[5] > -1)
503  data.quantumidentity.LowerQuantumNumbers().Set(QuantumNumberType::F, q[5] - 1_2);
504  }
505  }
506  }
507 
508  // That's it!
509  data.bad = false;
510  return data;
511 }
512 
514  // Default data and values for this type
516 
517  // Global species lookup data:
519 
520  // We need a species index sorted by Arts identifier. Keep this in a
521  // static variable, so that we have to do this only once. The ARTS
522  // species index is ArtsMap[<Arts String>].
523  static map<String, SpecIsoMap> ArtsMap;
524 
525  // Remember if this stuff has already been initialized:
526  static bool hinit = false;
527 
528  LineShape::Model line_mixing_model;
529  bool lmd_found = false;
530 
531  if (!hinit) {
532  for (Index i = 0; i < species_data.nelem(); ++i) {
533  const SpeciesRecord& sr = species_data[i];
534 
535  for (Index j = 0; j < sr.Isotopologue().nelem(); ++j) {
536  SpecIsoMap indicies(i, j);
537  String buf = sr.Name() + "-" + sr.Isotopologue()[j].Name();
538 
539  ArtsMap[buf] = indicies;
540  }
541  }
542  hinit = true;
543  }
544 
545  // This always contains the rest of the line to parse. At the
546  // beginning the entire line. Line gets shorter and shorter as we
547  // continue to extract stuff from the beginning.
548  String line;
549 
550  // Look for more comments?
551  bool comment = true;
552 
553  while (comment) {
554  // Return true if eof is reached:
555  if (is.eof()) return data;
556 
557  // Throw runtime_error if stream is bad:
558  if (!is) throw runtime_error("Stream bad.");
559 
560  // Read line from file into linebuffer:
561  getline(is, line);
562 
563  // It is possible that we were exactly at the end of the file before
564  // calling getline. In that case the previous eof() was still false
565  // because eof() evaluates only to true if one tries to read after the
566  // end of the file. The following check catches this.
567  if (line.nelem() == 0 && is.eof()) return data;
568 
569  // @ as first character marks catalogue entry
570  char c;
571  extract(c, line, 1);
572 
573  // check for empty line
574  if (c == '@') {
575  comment = false;
576  }
577  }
578 
579  // read the arts identifier String
580  istringstream icecream(line);
581 
582  try {
583  String artsid;
584  icecream >> artsid;
585 
586  if (artsid.length() != 0) {
587  // ok, now for the cool index map:
588  // is this arts identifier valid?
589  const map<String, SpecIsoMap>::const_iterator i = ArtsMap.find(artsid);
590  if (i == ArtsMap.end()) {
591  ostringstream os;
592  os << "ARTS Tag: " << artsid << " is unknown.";
593  throw runtime_error(os.str());
594  }
595 
596  SpecIsoMap id = i->second;
597 
598  // Set line ID:
599  data.quantumidentity.Species(id.Speciesindex());
600  data.quantumidentity.Isotopologue(id.Isotopologueindex());
601 
602  // Extract center frequency:
603  icecream >> double_imanip() >> data.line.F0();
604 
605  // Extract intensity:
606  icecream >> double_imanip() >> data.line.I0();
607 
608  // Extract reference temperature for Intensity in K:
609  icecream >> double_imanip() >> data.T0;
610 
611  // Extract lower state energy:
612  icecream >> double_imanip() >> data.line.E0();
613 
614  // Extract Einstein A-coefficient:
615  icecream >> double_imanip() >> data.line.A();
616 
617  // Extract upper state stat. weight:
618  icecream >> double_imanip() >> data.line.g_upp();
619 
620  // Extract lower state stat. weight:
621  icecream >> double_imanip() >> data.line.g_low();
622 
623  String token;
624  Index nelem;
625  icecream >> token;
626 
627  while (icecream) {
628  // Read pressure broadening (LEGACY)
629  if (token == "PB") {
631  icecream,
632  data.lineshapetype,
633  data.selfbroadening,
634  data.bathbroadening,
635  data.line.LineShape(),
636  data.species,
637  data.quantumidentity);
638  icecream >> token;
639  } else if (token == "QN") {
640  // Quantum numbers
641 
642  icecream >> token;
643  if (token != "UP") {
644  ostringstream os;
645  os << "Unknown quantum number tag: " << token;
646  throw std::runtime_error(os.str());
647  }
648 
649  icecream >> token;
650  Rational r;
651  while (icecream) {
653  icecream >> r;
654  data.quantumidentity.UpperQuantumNumbers().Set(token, r);
655  icecream >> token;
656  if (token == "LO") break;
657  }
658 
659  if (!is || token != "LO") {
660  std::ostringstream os;
661  os << "Error in catalog. Lower quantum number tag 'LO' not found.";
662  throw std::runtime_error(os.str());
663  }
664 
665  icecream >> token;
666  while (icecream && IsValidQuantumNumberName(token)) {
667  icecream >> r;
668  data.quantumidentity.LowerQuantumNumbers().Set(token, r);
669  icecream >> token;
670  }
671  } else if (token == "LM") { // LEGACY
672  LineShape::from_linemixingdata(icecream, line_mixing_model);
673  icecream >> token;
674  lmd_found = true;
675  } else if (token == "LF") {
677  data.lineshapetype,
678  data.selfbroadening,
679  data.bathbroadening,
680  data.line.LineShape(),
681  data.species);
682  icecream >> token;
683  } else if (token == "ZM") {
684  // Zeeman effect
685  icecream >> data.line.Zeeman();
686  icecream >> token;
687  } else if (token == "LSM") {
688  // Line shape modifications
689 
690  // Starts with the number of modifications
691  icecream >> nelem;
692  for (Index lsm = 0; lsm < nelem; lsm++) {
693  icecream >> token;
694 
695  // cutoff frequency
696  if (token == "CUT") {
697  icecream >> data.cutofffreq;
698  data.cutoff = CutoffType::LineByLineOffset;
699  }
700 
701  // linemixing pressure limit
702  if (token == "LML") {
703  icecream >> data.linemixinglimit;
704  }
705 
706  // mirroring
707  else if (token == "MTM") {
708  String value;
709  icecream >> value;
710 
711  data.mirroring = string2mirroringtype(value);
712  }
713 
714  // line normalization
715  else if (token == "LNT") {
716  String value;
717  icecream >> value;
718 
719  data.normalization = string2normalizationtype(value);
720  }
721 
722  else {
723  ostringstream os;
724  os << "Unknown line modifications given: " << token;
725  throw std::runtime_error(os.str());
726  }
727  }
728  icecream >> token;
729  } else {
730  ostringstream os;
731  os << "Unknown line data tag: " << token;
732  throw std::runtime_error(os.str());
733  }
734  }
735  }
736  } catch (const std::runtime_error& e) {
737  ostringstream os;
738  os << "Parse error in catalog line: " << endl;
739  os << line << endl;
740  os << e.what();
741  throw std::runtime_error(os.str());
742  }
743 
744  if (lmd_found)
745  data.line.LineShape().SetLineMixingModel(line_mixing_model.Data()[0]);
746 
747  // That's it!
748  data.bad = false;
749  return data;
750 }
751 
753  // Default data and values for this type
755  data.selfbroadening = true;
756  data.bathbroadening = true;
757  data.lineshapetype = LineShape::Type::VP;
758  data.species.resize(2);
759 
760  // Global species lookup data:
762 
763  // This value is used to flag missing data both in species and
764  // isotopologue lists. Could be any number, it just has to be made sure
765  // that it is neither the index of a species nor of an isotopologue.
766  const Index missing = species_data.nelem() + 100;
767 
768  // We need a species index sorted by HITRAN tag. Keep this in a
769  // static variable, so that we have to do this only once. The ARTS
770  // species index is hind[<HITRAN tag>].
771  //
772  // Allow for up to 100 species in HITRAN in the future.
773  static Array<Index> hspec(100);
774 
775  // This is an array of arrays for each hitran tag. It contains the
776  // ARTS indices of the HITRAN isotopologues.
777  static Array<ArrayOfIndex> hiso(100);
778 
779  // Remember if this stuff has already been initialized:
780  static bool hinit = false;
781 
782  // Remember, about which missing species we have already issued a
783  // warning:
784  static ArrayOfIndex warned_missing;
785 
786  if (!hinit) {
787  // Initialize hspec.
788  // The value of missing means that we don't have this species.
789  hspec = missing; // Matpack can set all elements like this.
790 
791  for (Index i = 0; i < species_data.nelem(); ++i) {
792  const SpeciesRecord& sr = species_data[i];
793 
794  // We have to be careful and check for the case that all
795  // HITRAN isotopologue tags are -1 (this species is missing in HITRAN).
796 
797  if (sr.Isotopologue().nelem() && 0 < sr.Isotopologue()[0].HitranTag()) {
798  // The HITRAN tags are stored as species plus isotopologue tags
799  // (MO and ISO)
800  // in the Isotopologue() part of the species record.
801  // We can extract the MO part from any of the isotopologue tags,
802  // so we use the first one. We do this by taking an integer
803  // division by 10.
804 
805  Index mo = sr.Isotopologue()[0].HitranTag() / 10;
806  // cout << "mo = " << mo << endl;
807  hspec[mo] = i;
808 
809  // Get a nicer to handle array of HITRAN iso tags:
810  Index n_iso = sr.Isotopologue().nelem();
811  ArrayOfIndex iso_tags;
812  iso_tags.resize(n_iso);
813  for (Index j = 0; j < n_iso; ++j) {
814  iso_tags[j] = sr.Isotopologue()[j].HitranTag();
815  }
816 
817  // Reserve elements for the isotopologue tags. How much do we
818  // need? This depends on the largest HITRAN tag that we know
819  // about!
820  // Also initialize the tags to missing.
821  // cout << "iso_tags = " << iso_tags << endl;
822  // cout << "static_cast<Index>(max(iso_tags))%10 + 1 = "
823  // << static_cast<Index>(max(iso_tags))%10 + 1 << endl;
824  hiso[mo].resize(max(iso_tags) % 10 + 1);
825  hiso[mo] = missing; // Matpack can set all elements like this.
826 
827  // Set the isotopologue tags:
828  for (Index j = 0; j < n_iso; ++j) {
829  if (0 < iso_tags[j]) // ignore -1 elements
830  {
831  // To get the iso tags from HitranTag() we also have to take
832  // modulo 10 to get rid of mo.
833  hiso[mo][iso_tags[j] % 10] = j;
834  }
835  }
836  }
837  }
838 
839  hinit = true;
840  }
841 
842  // This contains the rest of the line to parse. At the beginning the
843  // entire line. Line gets shorter and shorter as we continue to
844  // extract stuff from the beginning.
845  String line;
846 
847  // The first item is the molecule number:
848  Index mo;
849 
850  // Look for more comments?
851  bool comment = true;
852 
853  while (comment) {
854  // Return true if eof is reached:
855  if (is.eof()) return data;
856 
857  // Throw runtime_error if stream is bad:
858  if (!is) throw runtime_error("Stream bad.");
859 
860  // Read line from file into linebuffer:
861  getline(is, line);
862 
863  // It is possible that we were exactly at the end of the file before
864  // calling getline. In that case the previous eof() was still false
865  // because eof() evaluates only to true if one tries to read after the
866  // end of the file. The following check catches this.
867  if (line.nelem() == 0 && is.eof()) return data;
868 
869  // If the catalogue is in dos encoding, throw away the
870  // additional carriage return
871  if (line[line.nelem() - 1] == 13) {
872  line.erase(line.nelem() - 1, 1);
873  }
874 
875  // Because of the fixed FORTRAN format, we need to break up the line
876  // explicitly in apropriate pieces. Not elegant, but works!
877 
878  // Extract molecule number:
879  mo = 0;
880  // Initialization of mo is important, because mo stays the same
881  // if line is empty.
882  extract(mo, line, 2);
883  // cout << "mo = " << mo << endl;
884 
885  // If mo == 0 this is just a comment line:
886  if (0 != mo) {
887  // See if we know this species.
888  if (missing != hspec[mo]) {
889  comment = false;
890 
891  // Check if data record has the right number of characters for the
892  // in Hitran 2004 format
893  Index nChar = line.nelem() + 2; // number of characters in data record;
894  if ((nChar == 161 && line[158] != ' ') || nChar > 161) {
895  ostringstream os;
896  os << "Invalid HITRAN 2004 line data record with " << nChar
897  << " characters (expected: 160).";
898  throw std::runtime_error(os.str());
899  }
900 
901  }
902  }
903  }
904 
905  // Ok, we seem to have a valid species here.
906 
907  // Set mspecies from my cool index table:
908  data.quantumidentity.Species(hspec[mo]);
909 
910  // Extract isotopologue:
911  Index iso;
912  extract(iso, line, 1);
913  // cout << "iso = " << iso << endl;
914 
915  // Set misotopologue from the other cool index table.
916  // We have to be careful to issue an error for unknown iso tags. Iso
917  // could be either larger than the size of hiso[mo], or set
918  // explicitly to missing. Unfortunately we have to test both cases.
919  data.quantumidentity.Isotopologue(missing);
920  if (iso < hiso[mo].nelem())
921  if (missing != hiso[mo][iso]) data.quantumidentity.Isotopologue(hiso[mo][iso]);
922 
923  // Issue error message if misotopologue is still missing:
924  if (missing == data.quantumidentity.Isotopologue()) {
925  ostringstream os;
926  os << "Species: " << species_data[data.quantumidentity.Species()].Name()
927  << ", isotopologue iso = " << iso << " is unknown.";
928  throw std::runtime_error(os.str());
929  }
930 
931  // Position.
932  {
933  // HITRAN position in wavenumbers (cm^-1):
934  Numeric v;
935  // Conversion from wavenumber to Hz. If you multiply a line
936  // position in wavenumber (cm^-1) by this constant, you get the
937  // frequency in Hz.
938  const Numeric w2Hz = Constant::c * 100.;
939 
940  // Extract HITRAN postion:
941  extract(v, line, 12);
942 
943  // ARTS position in Hz:
944  data.line.F0() = v * w2Hz;
945  }
946 
947  // Intensity.
948  {
949  // HITRAN intensity is in cm-1/(molec * cm-2) at 296 Kelvin.
950  // It already includes the isotpic ratio.
951  // The first cm-1 is the frequency unit (it cancels with the
952  // 1/frequency unit of the line shape function).
953  //
954  // We need to do the following:
955  // 1. Convert frequency from wavenumber to Hz (factor 1e2 * c).
956  // 2. Convert [molec * cm-2] to [molec * m-2] (factor 1e-4).
957  // 3. Take out the isotopologue ratio.
958 
959  const Numeric hi2arts = 1e-2 * Constant::c;
960 
961  Numeric s;
962 
963  // Extract HITRAN intensity:
964  extract(s, line, 10);
965  // Convert to ARTS units (Hz / (molec * m-2) ), or shorter: Hz*m^2
966  data.line.I0() = s * hi2arts;
967  // Take out isotopologue ratio:
968  data.line.I0() /= species_data[data.quantumidentity.Species()]
969  .Isotopologue()[data.quantumidentity.Isotopologue()]
970  .Abundance();
971  }
972 
973  // Einstein coefficient
974  {
975  Numeric r;
976  extract(r, line, 10);
977  data.line.A() = r;
978  }
979 
980  // Air broadening parameters.
981  Numeric agam, sgam;
982  {
983  // HITRAN parameter is in cm-1/atm at 296 Kelvin
984  // All parameters are HWHM (I hope this is true!)
985  Numeric gam;
986  // Conversion from wavenumber to Hz. If you multiply a value in
987  // wavenumber (cm^-1) by this constant, you get the value in Hz.
988  const Numeric w2Hz = Constant::c * 1e2;
989  // Ok, put together the end-to-end conversion that we need:
990  const Numeric hi2arts = w2Hz / Conversion::ATM2PA;
991 
992  // Extract HITRAN AGAM value:
993  extract(gam, line, 5);
994 
995  // ARTS parameter in Hz/Pa:
996  agam = gam * hi2arts;
997 
998  // Extract HITRAN SGAM value:
999  extract(gam, line, 5);
1000 
1001  // ARTS parameter in Hz/Pa:
1002  sgam = gam * hi2arts;
1003 
1004  // If zero, set to agam:
1005  if (0 == sgam) sgam = agam;
1006 
1007  // cout << "agam, sgam = " << magam << ", " << msgam << endl;
1008  }
1009 
1010  // Lower state energy.
1011  {
1012  // HITRAN parameter is in wavenumbers (cm^-1).
1013  // We have to convert this to the ARTS unit Joule.
1014 
1015  // Extract from Catalogue line
1016  extract(data.line.E0(), line, 10);
1017 
1018  // Convert to Joule:
1019  data.line.E0() = wavenumber_to_joule(data.line.E0());
1020  }
1021 
1022  // Temperature coefficient of broadening parameters.
1023  Numeric nair, nself;
1024  {
1025  // This is dimensionless, we can also extract directly.
1026  extract(nair, line, 4);
1027 
1028  // Set self broadening temperature coefficient to the same value:
1029  nself = nair;
1030  // cout << "mnair = " << mnair << endl;
1031  }
1032 
1033  // Pressure shift.
1034  Numeric psf;
1035  {
1036  // HITRAN value in cm^-1 / atm. So the conversion goes exactly as
1037  // for the broadening parameters.
1038  Numeric d;
1039  // Conversion from wavenumber to Hz. If you multiply a value in
1040  // wavenumber (cm^-1) by this constant, you get the value in Hz.
1041  const Numeric w2Hz = Constant::c * 1e2;
1042  // Ok, put together the end-to-end conversion that we need:
1043  const Numeric hi2arts = w2Hz / Conversion::ATM2PA;
1044 
1045  // Extract HITRAN value:
1046  extract(d, line, 8);
1047 
1048  // ARTS value in Hz/Pa
1049  psf = d * hi2arts;
1050  }
1051  // Set the accuracies using the definition of HITRAN
1052  // indices. If some are missing, they are set to -1.
1053 
1054  static QuantumParserHITRAN2004 quantum_parser;
1055  const String qstr = line.substr(0, 15 * 4);
1056 
1057  // Upper state global quanta
1058  {
1059  Index eu;
1060  extract(eu, line, 15);
1061  }
1062 
1063  // Lower state global quanta
1064  {
1065  Index el;
1066  extract(el, line, 15);
1067  }
1068 
1069  // Upper state local quanta
1070  {
1071  Index eul;
1072  extract(eul, line, 15);
1073  }
1074 
1075  // Lower state local quanta
1076  {
1077  Index ell;
1078  extract(ell, line, 15);
1079  }
1080 
1081  // Parse quantum numbers.
1082  quantum_parser.Parse(data.quantumidentity, qstr);
1083 
1084  // Accuracy index for frequency
1085  {
1086  Index df;
1087  // Extract HITRAN value:
1088  extract(df, line, 1);
1089  }
1090 
1091  // Accuracy index for intensity
1092  {
1093  Index di0;
1094  // Extract HITRAN value:
1095  extract(di0, line, 1);
1096  }
1097 
1098  // Accuracy index for air-broadened halfwidth
1099  {
1100  Index dgam;
1101  // Extract HITRAN value:
1102  extract(dgam, line, 1);
1103  }
1104 
1105  // Accuracy index for self-broadened half-width
1106  {
1107  Index dgam;
1108  // Extract HITRAN value:
1109  extract(dgam, line, 1);
1110  }
1111 
1112  // Accuracy index for temperature-dependence exponent for agam
1113  {
1114  Index dn;
1115  // Extract HITRAN value:
1116  extract(dn, line, 1);
1117  }
1118 
1119  // Accuracy index for pressure shift
1120  {
1121  Index dpsfi;
1122  // Extract HITRAN value (given in cm-1):
1123  extract(dpsfi, line, 1);
1124  }
1125 
1126  // These were all the parameters that we can extract from
1127  // HITRAN 2004. However, we still have to set the reference temperatures
1128  // to the appropriate value:
1129 
1130  // Reference temperature for Intensity in K.
1131  data.T0 = 296.0;
1132 
1133  // Set line shape computer
1134  data.line.LineShape() = LineShape::Model(sgam, nself, agam, nair, psf);
1135  {
1136  Index garbage;
1137  extract(garbage, line, 13);
1138 
1139  // The statistical weights
1140  extract(data.line.g_upp(), line, 7);
1141  extract(data.line.g_low(), line, 7);
1142  }
1143 
1144  // That's it!
1145  data.bad = false;
1146  return data;
1147 }
1148 
1149 std::vector<std::array<String, 2>> split_quantum_numbers_from_hitran_online(String& qns)
1150 {
1151  std::vector<std::array<String, 2>> out(0);
1152 
1153  if (qns.length()) {
1154  auto pos_semicolon = qns.find(";");
1155  while (pos_semicolon < qns.length() and (pos_semicolon > 0)) {
1156  String var = qns.substr(0, pos_semicolon);
1157  qns.erase(0, pos_semicolon + 1);
1158 
1159  const auto pos_equality = var.find("=");
1160  out.push_back({var.substr(0, pos_equality), var.substr(pos_equality+1, var.length())});
1161  pos_semicolon = qns.find(";");
1162  }
1163 
1164  const auto pos_equality = qns.find("=");
1165  out.push_back({qns.substr(0, pos_equality), qns.substr(pos_equality+1, qns.length())});
1166  }
1167 
1168  return out;
1169 }
1170 
1172  // Default data and values for this type
1174  data.selfbroadening = true;
1175  data.bathbroadening = true;
1176  data.lineshapetype = LineShape::Type::VP;
1177  data.species.resize(2);
1178 
1179  // Global species lookup data:
1181 
1182  // This value is used to flag missing data both in species and
1183  // isotopologue lists. Could be any number, it just has to be made sure
1184  // that it is neither the index of a species nor of an isotopologue.
1185  const Index missing = species_data.nelem() + 100;
1186 
1187  // We need a species index sorted by HITRAN tag. Keep this in a
1188  // static variable, so that we have to do this only once. The ARTS
1189  // species index is hind[<HITRAN tag>].
1190  //
1191  // Allow for up to 100 species in HITRAN in the future.
1192  static Array<Index> hspec(100);
1193 
1194  // This is an array of arrays for each hitran tag. It contains the
1195  // ARTS indices of the HITRAN isotopologues.
1196  static Array<ArrayOfIndex> hiso(100);
1197 
1198  // Remember if this stuff has already been initialized:
1199  static bool hinit = false;
1200 
1201  // Remember, about which missing species we have already issued a
1202  // warning:
1203  static ArrayOfIndex warned_missing;
1204 
1205  if (!hinit) {
1206  // Initialize hspec.
1207  // The value of missing means that we don't have this species.
1208  hspec = missing; // Matpack can set all elements like this.
1209 
1210  for (Index i = 0; i < species_data.nelem(); ++i) {
1211  const SpeciesRecord& sr = species_data[i];
1212 
1213  // We have to be careful and check for the case that all
1214  // HITRAN isotopologue tags are -1 (this species is missing in HITRAN).
1215 
1216  if (sr.Isotopologue().nelem() && 0 < sr.Isotopologue()[0].HitranTag()) {
1217  // The HITRAN tags are stored as species plus isotopologue tags
1218  // (MO and ISO)
1219  // in the Isotopologue() part of the species record.
1220  // We can extract the MO part from any of the isotopologue tags,
1221  // so we use the first one. We do this by taking an integer
1222  // division by 10.
1223 
1224  Index mo = sr.Isotopologue()[0].HitranTag() / 10;
1225  // cout << "mo = " << mo << endl;
1226  hspec[mo] = i;
1227 
1228  // Get a nicer to handle array of HITRAN iso tags:
1229  Index n_iso = sr.Isotopologue().nelem();
1230  ArrayOfIndex iso_tags;
1231  iso_tags.resize(n_iso);
1232  for (Index j = 0; j < n_iso; ++j) {
1233  iso_tags[j] = sr.Isotopologue()[j].HitranTag();
1234  }
1235 
1236  // Reserve elements for the isotopologue tags. How much do we
1237  // need? This depends on the largest HITRAN tag that we know
1238  // about!
1239  // Also initialize the tags to missing.
1240  // cout << "iso_tags = " << iso_tags << endl;
1241  // cout << "static_cast<Index>(max(iso_tags))%10 + 1 = "
1242  // << static_cast<Index>(max(iso_tags))%10 + 1 << endl;
1243  hiso[mo].resize(max(iso_tags) % 10 + 1);
1244  hiso[mo] = missing; // Matpack can set all elements like this.
1245 
1246  // Set the isotopologue tags:
1247  for (Index j = 0; j < n_iso; ++j) {
1248  if (0 < iso_tags[j]) // ignore -1 elements
1249  {
1250  // To get the iso tags from HitranTag() we also have to take
1251  // modulo 10 to get rid of mo.
1252  hiso[mo][iso_tags[j] % 10] = j;
1253  }
1254  }
1255  }
1256  }
1257 
1258  hinit = true;
1259  }
1260 
1261  // This contains the rest of the line to parse. At the beginning the
1262  // entire line. Line gets shorter and shorter as we continue to
1263  // extract stuff from the beginning.
1264  String line;
1265 
1266  // The first item is the molecule number:
1267  Index mo;
1268 
1269  // Look for more comments?
1270  bool comment = true;
1271 
1272  while (comment) {
1273  // Return true if eof is reached:
1274  if (is.eof()) return data;
1275 
1276  // Throw runtime_error if stream is bad:
1277  if (!is) throw runtime_error("Stream bad.");
1278 
1279  // Read line from file into linebuffer:
1280  getline(is, line);
1281 
1282  // It is possible that we were exactly at the end of the file before
1283  // calling getline. In that case the previous eof() was still false
1284  // because eof() evaluates only to true if one tries to read after the
1285  // end of the file. The following check catches this.
1286  if (line.nelem() == 0 && is.eof()) return data;
1287 
1288  // If the catalogue is in dos encoding, throw away the
1289  // additional carriage return
1290  if (line[line.nelem() - 1] == 13) {
1291  line.erase(line.nelem() - 1, 1);
1292  }
1293 
1294  // Because of the fixed FORTRAN format, we need to break up the line
1295  // explicitly in apropriate pieces. Not elegant, but works!
1296 
1297  // Extract molecule number:
1298  mo = 0;
1299  // Initialization of mo is important, because mo stays the same
1300  // if line is empty.
1301  extract(mo, line, 2);
1302  // cout << "mo = " << mo << endl;
1303 
1304  // If mo == 0 this is just a comment line:
1305  if (0 != mo) {
1306  // See if we know this species.
1307  if (missing != hspec[mo]) {
1308  comment = false;
1309  }
1310  }
1311  }
1312 
1313  // Ok, we seem to have a valid species here.
1314 
1315  // Set mspecies from my cool index table:
1316  data.quantumidentity.Species(hspec[mo]);
1317 
1318  // Extract isotopologue:
1319  Index iso;
1320  extract(iso, line, 1);
1321  // cout << "iso = " << iso << endl;
1322 
1323  // Set misotopologue from the other cool index table.
1324  // We have to be careful to issue an error for unknown iso tags. Iso
1325  // could be either larger than the size of hiso[mo], or set
1326  // explicitly to missing. Unfortunately we have to test both cases.
1327  data.quantumidentity.Isotopologue(missing);
1328  if (iso < hiso[mo].nelem())
1329  if (missing != hiso[mo][iso]) data.quantumidentity.Isotopologue(hiso[mo][iso]);
1330 
1331  // Issue error message if misotopologue is still missing:
1332  if (missing == data.quantumidentity.Isotopologue()) {
1333  ostringstream os;
1334  os << "Species: " << species_data[data.quantumidentity.Species()].Name()
1335  << ", isotopologue iso = " << iso << " is unknown.";
1336  throw std::runtime_error(os.str());
1337  }
1338 
1339  // Position.
1340  {
1341  // HITRAN position in wavenumbers (cm^-1):
1342  Numeric v;
1343  // Conversion from wavenumber to Hz. If you multiply a line
1344  // position in wavenumber (cm^-1) by this constant, you get the
1345  // frequency in Hz.
1346  const Numeric w2Hz = Constant::c * 100.;
1347 
1348  // Extract HITRAN postion:
1349  extract(v, line, 12);
1350 
1351  // ARTS position in Hz:
1352  data.line.F0() = v * w2Hz;
1353  }
1354 
1355  // Intensity.
1356  {
1357  // HITRAN intensity is in cm-1/(molec * cm-2) at 296 Kelvin.
1358  // It already includes the isotpic ratio.
1359  // The first cm-1 is the frequency unit (it cancels with the
1360  // 1/frequency unit of the line shape function).
1361  //
1362  // We need to do the following:
1363  // 1. Convert frequency from wavenumber to Hz (factor 1e2 * c).
1364  // 2. Convert [molec * cm-2] to [molec * m-2] (factor 1e-4).
1365  // 3. Take out the isotopologue ratio.
1366 
1367  const Numeric hi2arts = 1e-2 * Constant::c;
1368 
1369  Numeric s;
1370 
1371  // Extract HITRAN intensity:
1372  extract(s, line, 10);
1373  // Convert to ARTS units (Hz / (molec * m-2) ), or shorter: Hz*m^2
1374  data.line.I0() = s * hi2arts;
1375  // Take out isotopologue ratio:
1376  data.line.I0() /= species_data[data.quantumidentity.Species()]
1377  .Isotopologue()[data.quantumidentity.Isotopologue()]
1378  .Abundance();
1379  }
1380 
1381  // Einstein coefficient
1382  {
1383  Numeric r;
1384  extract(r, line, 10);
1385  data.line.A() = r;
1386  }
1387 
1388  // Air broadening parameters.
1389  Numeric agam, sgam;
1390  {
1391  // HITRAN parameter is in cm-1/atm at 296 Kelvin
1392  // All parameters are HWHM (I hope this is true!)
1393  Numeric gam;
1394  // Conversion from wavenumber to Hz. If you multiply a value in
1395  // wavenumber (cm^-1) by this constant, you get the value in Hz.
1396  const Numeric w2Hz = Constant::c * 1e2;
1397  // Ok, put together the end-to-end conversion that we need:
1398  const Numeric hi2arts = w2Hz / Conversion::ATM2PA;
1399 
1400  // Extract HITRAN AGAM value:
1401  extract(gam, line, 5);
1402 
1403  // ARTS parameter in Hz/Pa:
1404  agam = gam * hi2arts;
1405 
1406  // Extract HITRAN SGAM value:
1407  extract(gam, line, 5);
1408 
1409  // ARTS parameter in Hz/Pa:
1410  sgam = gam * hi2arts;
1411 
1412  // If zero, set to agam:
1413  if (0 == sgam) sgam = agam;
1414 
1415  // cout << "agam, sgam = " << magam << ", " << msgam << endl;
1416  }
1417 
1418  // Lower state energy.
1419  {
1420  // HITRAN parameter is in wavenumbers (cm^-1).
1421  // We have to convert this to the ARTS unit Joule.
1422 
1423  // Extract from Catalogue line
1424  extract(data.line.E0(), line, 10);
1425 
1426  // Convert to Joule:
1427  data.line.E0() = wavenumber_to_joule(data.line.E0());
1428  }
1429 
1430  // Temperature coefficient of broadening parameters.
1431  Numeric nair, nself;
1432  {
1433  // This is dimensionless, we can also extract directly.
1434  extract(nair, line, 4);
1435 
1436  // Set self broadening temperature coefficient to the same value:
1437  nself = nair;
1438  // cout << "mnair = " << mnair << endl;
1439  }
1440 
1441  // Pressure shift.
1442  Numeric psf;
1443  {
1444  // HITRAN value in cm^-1 / atm. So the conversion goes exactly as
1445  // for the broadening parameters.
1446  Numeric d;
1447  // Conversion from wavenumber to Hz. If you multiply a value in
1448  // wavenumber (cm^-1) by this constant, you get the value in Hz.
1449  const Numeric w2Hz = Constant::c * 1e2;
1450  // Ok, put together the end-to-end conversion that we need:
1451  const Numeric hi2arts = w2Hz / Conversion::ATM2PA;
1452 
1453  // Extract HITRAN value:
1454  extract(d, line, 8);
1455 
1456  // ARTS value in Hz/Pa
1457  psf = d * hi2arts;
1458  }
1459  // Set the accuracies using the definition of HITRAN
1460  // indices. If some are missing, they are set to -1.
1461 
1462  // Upper state global quanta
1463  {
1464  Index eu;
1465  extract(eu, line, 15);
1466  }
1467 
1468  // Lower state global quanta
1469  {
1470  Index el;
1471  extract(el, line, 15);
1472  }
1473 
1474  // Upper state local quanta
1475  {
1476  Index eul;
1477  extract(eul, line, 15);
1478  }
1479 
1480  // Lower state local quanta
1481  {
1482  Index ell;
1483  extract(ell, line, 15);
1484  }
1485 
1486  // Accuracy index for frequency
1487  {
1488  Index df;
1489  // Extract HITRAN value:
1490  extract(df, line, 1);
1491  }
1492 
1493  // Accuracy index for intensity
1494  {
1495  Index di0;
1496  // Extract HITRAN value:
1497  extract(di0, line, 1);
1498  }
1499 
1500  // Accuracy index for air-broadened halfwidth
1501  {
1502  Index dgam;
1503  // Extract HITRAN value:
1504  extract(dgam, line, 1);
1505  }
1506 
1507  // Accuracy index for self-broadened half-width
1508  {
1509  Index dgam;
1510  // Extract HITRAN value:
1511  extract(dgam, line, 1);
1512  }
1513 
1514  // Accuracy index for temperature-dependence exponent for agam
1515  {
1516  Index dn;
1517  // Extract HITRAN value:
1518  extract(dn, line, 1);
1519  }
1520 
1521  // Accuracy index for pressure shift
1522  {
1523  Index dpsfi;
1524  // Extract HITRAN value (given in cm-1):
1525  extract(dpsfi, line, 1);
1526  }
1527 
1528  // These were all the parameters that we can extract from
1529  // HITRAN 2004. However, we still have to set the reference temperatures
1530  // to the appropriate value:
1531 
1532  // Reference temperature for Intensity in K.
1533  data.T0 = 296.0;
1534 
1535  // Set line shape computer
1536  data.line.LineShape() = LineShape::Model(sgam, nself, agam, nair, psf);
1537  {
1538  Index garbage;
1539  extract(garbage, line, 13);
1540 
1541  // The statistical weights
1542  extract(data.line.g_upp(), line, 7);
1543  extract(data.line.g_low(), line, 7);
1544  }
1545 
1546  // ADD QUANTUM NUMBER PARSING HERE!
1547  String upper, lower;
1548  std::stringstream ss;
1549  ss.str(line);
1550  ss >> upper >> lower;
1551  auto upper_list = split_quantum_numbers_from_hitran_online(upper);
1552  auto lower_list = split_quantum_numbers_from_hitran_online(lower);
1553  update_id(data.quantumidentity, upper_list, lower_list);
1554 
1555  // That's it!
1556  data.bad = false;
1557  return data;
1558 }
1559 
1561  // Default data and values for this type
1563  data.selfbroadening = true;
1564  data.bathbroadening = true;
1565  data.lineshapetype = LineShape::Type::VP;
1566  data.species.resize(2);
1567 
1568  // Global species lookup data:
1570 
1571  // This value is used to flag missing data both in species and
1572  // isotopologue lists. Could be any number, it just has to be made sure
1573  // that it is neither the index of a species nor of an isotopologue.
1574  const Index missing = species_data.nelem() + 100;
1575 
1576  // We need a species index sorted by HITRAN tag. Keep this in a
1577  // static variable, so that we have to do this only once. The ARTS
1578  // species index is hind[<HITRAN tag>].
1579  //
1580  // Allow for up to 100 species in HITRAN in the future.
1581  static Array<Index> hspec(100);
1582 
1583  // This is an array of arrays for each hitran tag. It contains the
1584  // ARTS indices of the HITRAN isotopologues.
1585  static Array<ArrayOfIndex> hiso(100);
1586 
1587  // Remember if this stuff has already been initialized:
1588  static bool hinit = false;
1589 
1590  // Remember, about which missing species we have already issued a
1591  // warning:
1592  static ArrayOfIndex warned_missing;
1593 
1594  if (!hinit) {
1595  // Initialize hspec.
1596  // The value of missing means that we don't have this species.
1597  hspec = missing; // Matpack can set all elements like this.
1598 
1599  for (Index i = 0; i < species_data.nelem(); ++i) {
1600  const SpeciesRecord& sr = species_data[i];
1601 
1602  // We have to be careful and check for the case that all
1603  // HITRAN isotopologue tags are -1 (this species is missing in HITRAN).
1604 
1605  if (sr.Isotopologue().nelem() && 0 < sr.Isotopologue()[0].HitranTag()) {
1606  // The HITRAN tags are stored as species plus isotopologue tags
1607  // (MO and ISO)
1608  // in the Isotopologue() part of the species record.
1609  // We can extract the MO part from any of the isotopologue tags,
1610  // so we use the first one. We do this by taking an integer
1611  // division by 10.
1612 
1613  Index mo = sr.Isotopologue()[0].HitranTag() / 10;
1614  // cout << "mo = " << mo << endl;
1615  hspec[mo] = i;
1616 
1617  // Get a nicer to handle array of HITRAN iso tags:
1618  Index n_iso = sr.Isotopologue().nelem();
1619  ArrayOfIndex iso_tags;
1620  iso_tags.resize(n_iso);
1621  for (Index j = 0; j < n_iso; ++j) {
1622  iso_tags[j] = sr.Isotopologue()[j].HitranTag();
1623  }
1624 
1625  // Reserve elements for the isotopologue tags. How much do we
1626  // need? This depends on the largest HITRAN tag that we know
1627  // about!
1628  // Also initialize the tags to missing.
1629  // cout << "iso_tags = " << iso_tags << endl;
1630  // cout << "static_cast<Index>(max(iso_tags))%10 + 1 = "
1631  // << static_cast<Index>(max(iso_tags))%10 + 1 << endl;
1632  hiso[mo].resize(max(iso_tags) % 10 + 1);
1633  hiso[mo] = missing; // Matpack can set all elements like this.
1634 
1635  // Set the isotopologue tags:
1636  for (Index j = 0; j < n_iso; ++j) {
1637  if (0 < iso_tags[j]) // ignore -1 elements
1638  {
1639  // To get the iso tags from HitranTag() we also have to take
1640  // modulo 10 to get rid of mo.
1641  hiso[mo][iso_tags[j] % 10] = j;
1642  }
1643  }
1644  }
1645  }
1646 
1647  hinit = true;
1648  }
1649 
1650  // This contains the rest of the line to parse. At the beginning the
1651  // entire line. Line gets shorter and shorter as we continue to
1652  // extract stuff from the beginning.
1653  String line;
1654 
1655  // The first item is the molecule number:
1656  Index mo;
1657 
1658  // Look for more comments?
1659  bool comment = true;
1660 
1661  while (comment) {
1662  // Return true if eof is reached:
1663  if (is.eof()) return data;
1664 
1665  // Throw runtime_error if stream is bad:
1666  if (!is) throw runtime_error("Stream bad.");
1667 
1668  // Read line from file into linebuffer:
1669  getline(is, line);
1670 
1671  // It is possible that we were exactly at the end of the file before
1672  // calling getline. In that case the previous eof() was still false
1673  // because eof() evaluates only to true if one tries to read after the
1674  // end of the file. The following check catches this.
1675  if (line.nelem() == 0 && is.eof()) return data;
1676 
1677  // If the catalogue is in dos encoding, throw away the
1678  // additional carriage return
1679  if (line[line.nelem() - 1] == 13) {
1680  line.erase(line.nelem() - 1, 1);
1681  }
1682 
1683  // Because of the fixed FORTRAN format, we need to break up the line
1684  // explicitly in apropriate pieces. Not elegant, but works!
1685 
1686  // Extract molecule number:
1687  mo = 0;
1688  // Initialization of mo is important, because mo stays the same
1689  // if line is empty.
1690  extract(mo, line, 2);
1691  // cout << "mo = " << mo << endl;
1692 
1693  // If mo == 0 this is just a comment line:
1694  if (0 != mo) {
1695  // See if we know this species. Exit with an error if the species is unknown.
1696  if (missing != hspec[mo]) {
1697  comment = false;
1698 
1699  // Check if data record has the right number of characters for the
1700  // in Hitran 1986-2001 format
1701  Index nChar = line.nelem() + 2; // number of characters in data record;
1702  if (nChar != 100) {
1703  ostringstream os;
1704  os << "Invalid HITRAN 1986-2001 line data record with " << nChar
1705  << " characters (expected: 100)." << endl
1706  << line << " n: " << line.nelem();
1707  throw runtime_error(os.str());
1708  }
1709  }
1710  }
1711  }
1712 
1713  // Ok, we seem to have a valid species here.
1714 
1715  // Set mspecies from my cool index table:
1716  data.quantumidentity.Species(hspec[mo]);
1717 
1718  // Extract isotopologue:
1719  Index iso;
1720  extract(iso, line, 1);
1721  // cout << "iso = " << iso << endl;
1722 
1723  // Set misotopologue from the other cool index table.
1724  // We have to be careful to issue an error for unknown iso tags. Iso
1725  // could be either larger than the size of hiso[mo], or set
1726  // explicitly to missing. Unfortunately we have to test both cases.
1727  data.quantumidentity.Isotopologue(missing);
1728  if (iso < hiso[mo].nelem())
1729  if (missing != hiso[mo][iso]) data.quantumidentity.Isotopologue(hiso[mo][iso]);
1730 
1731  // Issue error message if misotopologue is still missing:
1732  if (missing == data.quantumidentity.Isotopologue()) {
1733  ostringstream os;
1734  os << "Species: " << species_data[data.quantumidentity.Species()].Name()
1735  << ", isotopologue iso = " << iso << " is unknown.";
1736  throw runtime_error(os.str());
1737  }
1738 
1739  // Position.
1740  {
1741  // HITRAN position in wavenumbers (cm^-1):
1742  Numeric v;
1743  // Conversion from wavenumber to Hz. If you multiply a line
1744  // position in wavenumber (cm^-1) by this constant, you get the
1745  // frequency in Hz.
1746  const Numeric w2Hz = Constant::c * 100.;
1747 
1748  // Extract HITRAN postion:
1749  extract(v, line, 12);
1750 
1751  // ARTS position in Hz:
1752  data.line.F0() = v * w2Hz;
1753  // cout << "mf = " << mf << endl;
1754  }
1755 
1756  // Intensity.
1757  {
1758  // HITRAN intensity is in cm-1/(molec * cm-2) at 296 Kelvin.
1759  // It already includes the isotpic ratio.
1760  // The first cm-1 is the frequency unit (it cancels with the
1761  // 1/frequency unit of the line shape function).
1762  //
1763  // We need to do the following:
1764  // 1. Convert frequency from wavenumber to Hz (factor 1e2 * c).
1765  // 2. Convert [molec * cm-2] to [molec * m-2] (factor 1e-4).
1766  // 3. Take out the isotopologue ratio.
1767 
1768  const Numeric hi2arts = 1e-2 * Constant::c;
1769 
1770  Numeric s;
1771 
1772  // Extract HITRAN intensity:
1773  extract(s, line, 10);
1774  // Convert to ARTS units (Hz / (molec * m-2) ), or shorter: Hz*m^2
1775  data.line.I0() = s * hi2arts;
1776  // Take out isotopologue ratio:
1777  data.line.I0() /= species_data[data.quantumidentity.Species()]
1778  .Isotopologue()[data.quantumidentity.Isotopologue()]
1779  .Abundance();
1780  }
1781 
1782  // Skip transition probability:
1783  {
1784  Numeric r;
1785  extract(r, line, 10);
1786  }
1787 
1788  // Air broadening parameters.
1789  Numeric agam, sgam;
1790  {
1791  // HITRAN parameter is in cm-1/atm at 296 Kelvin
1792  // All parameters are HWHM (I hope this is true!)
1793  Numeric gam;
1794  // Conversion from wavenumber to Hz. If you multiply a value in
1795  // wavenumber (cm^-1) by this constant, you get the value in Hz.
1796  const Numeric w2Hz = Constant::c * 1e2;
1797  // Ok, put together the end-to-end conversion that we need:
1798  const Numeric hi2arts = w2Hz / Conversion::ATM2PA;
1799 
1800  // Extract HITRAN AGAM value:
1801  extract(gam, line, 5);
1802 
1803  // ARTS parameter in Hz/Pa:
1804  agam = gam * hi2arts;
1805 
1806  // Extract HITRAN SGAM value:
1807  extract(gam, line, 5);
1808 
1809  // ARTS parameter in Hz/Pa:
1810  sgam = gam * hi2arts;
1811 
1812  // If zero, set to agam:
1813  if (0 == sgam) sgam = agam;
1814 
1815  // cout << "agam, sgam = " << magam << ", " << msgam << endl;
1816  }
1817 
1818  // Lower state energy.
1819  {
1820  // HITRAN parameter is in wavenumbers (cm^-1).
1821  // We have to convert this to the ARTS unit Joule.
1822 
1823  // Extract from Catalogue line
1824  extract(data.line.E0(), line, 10);
1825 
1826  // Convert to Joule:
1827  data.line.E0() = wavenumber_to_joule(data.line.E0());
1828  }
1829 
1830  // Temperature coefficient of broadening parameters.
1831  Numeric nair, nself;
1832  {
1833  // This is dimensionless, we can also extract directly.
1834  extract(nair, line, 4);
1835 
1836  // Set self broadening temperature coefficient to the same value:
1837  nself = nair;
1838  // cout << "mnair = " << mnair << endl;
1839  }
1840 
1841  // Pressure shift.
1842  Numeric psf;
1843  {
1844  // HITRAN value in cm^-1 / atm. So the conversion goes exactly as
1845  // for the broadening parameters.
1846  Numeric d;
1847  // Conversion from wavenumber to Hz. If you multiply a value in
1848  // wavenumber (cm^-1) by this constant, you get the value in Hz.
1849  const Numeric w2Hz = Constant::c * 1e2;
1850  // Ok, put together the end-to-end conversion that we need:
1851  const Numeric hi2arts = w2Hz / Conversion::ATM2PA;
1852 
1853  // Extract HITRAN value:
1854  extract(d, line, 8);
1855 
1856  // ARTS value in Hz/Pa
1857  psf = d * hi2arts;
1858  }
1859  // Set the accuracies using the definition of HITRAN
1860  // indices. If some are missing, they are set to -1.
1861 
1862  //Skip upper state global quanta index
1863  {
1864  Index eu;
1865  extract(eu, line, 3);
1866  }
1867 
1868  //Skip lower state global quanta index
1869  {
1870  Index el;
1871  extract(el, line, 3);
1872  }
1873 
1874  //Skip upper state local quanta
1875  {
1876  Index eul;
1877  extract(eul, line, 9);
1878  }
1879 
1880  //Skip lower state local quanta
1881  {
1882  Index ell;
1883  extract(ell, line, 9);
1884  }
1885 
1886  // Accuracy index for frequency reference
1887  {
1888  Index df;
1889  // Extract HITRAN value:
1890  extract(df, line, 1);
1891  }
1892 
1893  // Accuracy index for intensity reference
1894  {
1895  Index di0;
1896  // Extract HITRAN value:
1897  extract(di0, line, 1);
1898  }
1899 
1900  // Accuracy index for halfwidth reference
1901  {
1902  Index dgam;
1903  // Extract HITRAN value:
1904  extract(dgam, line, 1);
1905  }
1906 
1907  // These were all the parameters that we can extract from
1908  // HITRAN. However, we still have to set the reference temperatures
1909  // to the appropriate value:
1910 
1911  // Reference temperature for Intensity in K.
1912  data.T0 = 296.0;
1913 
1914  // Set line shape computer
1915  data.line.LineShape() = LineShape::Model(sgam, nself, agam, nair, psf);
1916 
1917  // That's it!
1918  data.bad = false;
1919  return data;
1920 }
1921 
1923  // Default data and values for this type
1925  data.selfbroadening = true;
1926  data.bathbroadening = true;
1927  data.lineshapetype = LineShape::Type::VP;
1928  data.species.resize(2);
1929 
1930  // Global species lookup data:
1932 
1933  // This value is used to flag missing data both in species and
1934  // isotopologue lists. Could be any number, it just has to be made sure
1935  // that it is neither the index of a species nor of an isotopologue.
1936  const Index missing = species_data.nelem() + 100;
1937 
1938  // We need a species index sorted by HITRAN tag. Keep this in a
1939  // static variable, so that we have to do this only once. The ARTS
1940  // species index is hind[<HITRAN tag>].
1941  //
1942  // Allow for up to 100 species in HITRAN in the future.
1943  static Array<Index> hspec(100);
1944 
1945  // This is an array of arrays for each hitran tag. It contains the
1946  // ARTS indices of the HITRAN isotopologues.
1947  static Array<ArrayOfIndex> hiso(100);
1948 
1949  // Remember if this stuff has already been initialized:
1950  static bool hinit = false;
1951 
1952  // Remember, about which missing species we have already issued a
1953  // warning:
1954  static ArrayOfIndex warned_missing;
1955 
1956  if (!hinit) {
1957  // Initialize hspec.
1958  // The value of missing means that we don't have this species.
1959  hspec = missing; // Matpack can set all elements like this.
1960  for (Index i = 0; i < species_data.nelem(); ++i) {
1961  const SpeciesRecord& sr = species_data[i];
1962  // We have to be careful and check for the case that all
1963  // HITRAN isotopologue tags are -1 (this species is missing in HITRAN).
1964  if (sr.Isotopologue().nelem() && 0 < sr.Isotopologue()[0].HitranTag()) {
1965  // The HITRAN tags are stored as species plus isotopologue tags
1966  // (MO and ISO)
1967  // in the Isotopologue() part of the species record.
1968  // We can extract the MO part from any of the isotopologue tags,
1969  // so we use the first one. We do this by taking an integer
1970  // division by 10.
1971 
1972  Index mo = sr.Isotopologue()[0].HitranTag() / 10;
1973  // cout << "mo = " << mo << endl;
1974  hspec[mo] = i;
1975 
1976  // Get a nicer to handle array of HITRAN iso tags:
1977  Index n_iso = sr.Isotopologue().nelem();
1978  ArrayOfIndex iso_tags;
1979  iso_tags.resize(n_iso);
1980  for (Index j = 0; j < n_iso; ++j) {
1981  iso_tags[j] = sr.Isotopologue()[j].HitranTag();
1982  }
1983 
1984  // Reserve elements for the isotopologue tags. How much do we
1985  // need? This depends on the largest HITRAN tag that we know
1986  // about!
1987  // Also initialize the tags to missing.
1988  // cout << "iso_tags = " << iso_tags << endl;
1989  // cout << "static_cast<Index>(max(iso_tags))%10 + 1 = "
1990  // << static_cast<Index>(max(iso_tags))%10 + 1 << endl;
1991  hiso[mo].resize(max(iso_tags) % 10 + 1);
1992  hiso[mo] = missing; // Matpack can set all elements like this.
1993 
1994  // Set the isotopologue tags:
1995  for (Index j = 0; j < n_iso; ++j) {
1996  if (0 < iso_tags[j]) // ignore -1 elements
1997  {
1998  // To get the iso tags from HitranTag() we also have to take
1999  // modulo 10 to get rid of mo.
2000  hiso[mo][iso_tags[j] % 10] = j;
2001  }
2002  }
2003  }
2004  }
2005 
2006  hinit = true;
2007  }
2008 
2009  // This contains the rest of the line to parse. At the beginning the
2010  // entire line. Line gets shorter and shorter as we continue to
2011  // extract stuff from the beginning.
2012  String line;
2013 
2014  // The first item is the molecule number:
2015  Index mo;
2016 
2017  // Look for more comments?
2018  bool comment = true;
2019 
2020  while (comment) {
2021  // Return true if eof is reached:
2022  if (is.eof()) return data;
2023 
2024  // Throw runtime_error if stream is bad:
2025  if (!is) throw std::runtime_error("Stream bad.");
2026 
2027  // Read line from file into linebuffer:
2028  getline(is, line);
2029 
2030  // It is possible that we were exactly at the end of the file before
2031  // calling getline. In that case the previous eof() was still false
2032  // because eof() evaluates only to true if one tries to read after the
2033  // end of the file. The following check catches this.
2034  if (line.nelem() == 0 && is.eof()) return data;
2035 
2036  // If the catalogue is in dos encoding, throw away the
2037  // additional carriage return
2038  if (line[line.nelem() - 1] == 13) {
2039  line.erase(line.nelem() - 1, 1);
2040  }
2041 
2042  // Because of the fixed FORTRAN format, we need to break up the line
2043  // explicitly in apropriate pieces. Not elegant, but works!
2044 
2045  // Extract molecule number:
2046  mo = 0;
2047  // Initialization of mo is important, because mo stays the same
2048  // if line is empty.
2049  extract(mo, line, 2);
2050  // cout << "mo = " << mo << endl;
2051 
2052  // If mo == 0 this is just a comment line:
2053  if (0 != mo) {
2054  // See if we know this species. Exit with an error if the species is unknown.
2055  if (missing != hspec[mo]) {
2056  comment = false;
2057 
2058  // Check if data record has the right number of characters for the
2059  // in Hitran 1986-2001 format
2060  Index nChar = line.nelem() + 2; // number of characters in data record;
2061  if (nChar != 100) {
2062  ostringstream os;
2063  os << "Invalid HITRAN 1986-2001 line data record with " << nChar
2064  << " characters (expected: 100)." << endl
2065  << line << " n: " << line.nelem();
2066  throw std::runtime_error(os.str());
2067  }
2068 
2069  }
2070  }
2071  }
2072 
2073  // Ok, we seem to have a valid species here.
2074 
2075  // Set mspecies from my cool index table:
2076  data.quantumidentity.Species(hspec[mo]);
2077 
2078  // Extract isotopologue:
2079  Index iso;
2080  extract(iso, line, 1);
2081  // cout << "iso = " << iso << endl;
2082 
2083  // Set misotopologue from the other cool index table.
2084  // We have to be careful to issue an error for unknown iso tags. Iso
2085  // could be either larger than the size of hiso[mo], or set
2086  // explicitly to missing. Unfortunately we have to test both cases.
2087  data.quantumidentity.Isotopologue(missing);
2088  if (iso < hiso[mo].nelem())
2089  if (missing != hiso[mo][iso]) data.quantumidentity.Isotopologue(hiso[mo][iso]);
2090 
2091  // Issue error message if misotopologue is still missing:
2092  if (missing == data.quantumidentity.Isotopologue()) {
2093  ostringstream os;
2094  os << "Species: " << species_data[data.quantumidentity.Species()].Name()
2095  << ", isotopologue iso = " << iso << " is unknown.";
2096  throw std::runtime_error(os.str());
2097  }
2098 
2099  // Position.
2100  {
2101  // HITRAN position in wavenumbers (cm^-1):
2102  Numeric v;
2103  // Conversion from wavenumber to Hz. If you multiply a line
2104  // position in wavenumber (cm^-1) by this constant, you get the
2105  // frequency in Hz.
2106  const Numeric w2Hz = Constant::c * 100.;
2107 
2108  // Extract HITRAN postion:
2109  extract(v, line, 12);
2110 
2111  // ARTS position in Hz:
2112  data.line.F0() = v * w2Hz;
2113  // cout << "mf = " << mf << endl;
2114  }
2115 
2116  // Intensity.
2117  {
2118  // HITRAN intensity is in cm-1/(molec * cm-2) at 296 Kelvin.
2119  // It already includes the isotpic ratio.
2120  // The first cm-1 is the frequency unit (it cancels with the
2121  // 1/frequency unit of the line shape function).
2122  //
2123  // We need to do the following:
2124  // 1. Convert frequency from wavenumber to Hz (factor 1e2 * c).
2125  // 2. Convert [molec * cm-2] to [molec * m-2] (factor 1e-4).
2126  // 3. Take out the isotopologue ratio.
2127 
2128  const Numeric hi2arts = 1e-2 * Constant::c;
2129 
2130  Numeric s;
2131  if (line[6] == 'D') line[6] = 'E';
2132  // Extract HITRAN intensity:
2133  extract(s,
2134  line,
2135  10); // NOTE: If error shooting, FORTRAN "D" is not read properly.
2136  // Convert to ARTS units (Hz / (molec * m-2) ), or shorter: Hz*m^2
2137  data.line.I0() = s * hi2arts;
2138  // Take out isotopologue ratio:
2139  data.line.I0() /= species_data[data.quantumidentity.Species()]
2140  .Isotopologue()[data.quantumidentity.Isotopologue()]
2141  .Abundance();
2142  }
2143 
2144  // Skip transition probability:
2145  {
2146  Numeric r;
2147  extract(r, line, 10);
2148  }
2149 
2150  // Air broadening parameters.
2151  Numeric sgam, agam;
2152  {
2153  // HITRAN parameter is in cm-1/atm at 296 Kelvin
2154  // All parameters are HWHM (I hope this is true!)
2155  Numeric gam;
2156  // Conversion from wavenumber to Hz. If you multiply a value in
2157  // wavenumber (cm^-1) by this constant, you get the value in Hz.
2158  const Numeric w2Hz = Constant::c * 1e2;
2159  // Ok, put together the end-to-end conversion that we need:
2160  const Numeric hi2arts = w2Hz / Conversion::ATM2PA;
2161 
2162  // Extract HITRAN AGAM value:
2163  extract(gam, line, 5);
2164 
2165  // ARTS parameter in Hz/Pa:
2166  agam = gam * hi2arts;
2167 
2168  // Extract HITRAN SGAM value:
2169  extract(gam, line, 5);
2170 
2171  // ARTS parameter in Hz/Pa:
2172  sgam = gam * hi2arts;
2173 
2174  // If zero, set to agam:
2175  if (0 == sgam) sgam = agam;
2176 
2177  // cout << "agam, sgam = " << magam << ", " << msgam << endl;
2178  }
2179 
2180  // Lower state energy.
2181  {
2182  // HITRAN parameter is in wavenumbers (cm^-1).
2183  // We have to convert this to the ARTS unit Joule.
2184 
2185  // Extract from Catalogue line
2186  extract(data.line.E0(), line, 10);
2187 
2188  // Convert to Joule:
2189  data.line.E0() = wavenumber_to_joule(data.line.E0());
2190  }
2191 
2192  // Temperature coefficient of broadening parameters.
2193  Numeric nair, nself;
2194  {
2195  // This is dimensionless, we can also extract directly.
2196  extract(nair, line, 4);
2197 
2198  // Set self broadening temperature coefficient to the same value:
2199  nself = nair;
2200  // cout << "mnair = " << mnair << endl;
2201  }
2202 
2203  // Pressure shift.
2204  Numeric psf;
2205  {
2206  // HITRAN value in cm^-1 / atm. So the conversion goes exactly as
2207  // for the broadening parameters.
2208  Numeric d;
2209  // Conversion from wavenumber to Hz. If you multiply a value in
2210  // wavenumber (cm^-1) by this constant, you get the value in Hz.
2211  const Numeric w2Hz = Constant::c * 1e2;
2212  // Ok, put together the end-to-end conversion that we need:
2213  const Numeric hi2arts = w2Hz / Conversion::ATM2PA;
2214 
2215  // Extract HITRAN value:
2216  extract(d, line, 8);
2217 
2218  // ARTS value in Hz/Pa
2219  psf = d * hi2arts;
2220  }
2221  // Set the accuracies using the definition of HITRAN
2222  // indices. If some are missing, they are set to -1.
2223 
2224  //Skip upper state global quanta index
2225  {
2226  Index eu;
2227  extract(eu, line, 3);
2228  }
2229 
2230  //Skip lower state global quanta index
2231  {
2232  Index el;
2233  extract(el, line, 3);
2234  }
2235 
2236  //Skip upper state local quanta
2237  {
2238  Index eul;
2239  extract(eul, line, 9);
2240  }
2241 
2242  //Skip lower state local quanta
2243  {
2244  Index ell;
2245  if (species_data[data.quantumidentity.Species()].Name() == "O2") {
2246  String helper = line.substr(0, 9);
2247  Index DJ = -helper.compare(3, 1, "Q");
2248  Index DN = -helper.compare(0, 1, "Q");
2249  Index N = atoi(helper.substr(1, 2).c_str());
2250  Index J = atoi(helper.substr(4, 2).c_str());
2251 
2252  data.quantumidentity.LowerQuantumNumbers().Set(QuantumNumberType::N, Rational(N));
2253  data.quantumidentity.LowerQuantumNumbers().Set(QuantumNumberType::J, Rational(J));
2254  data.quantumidentity.UpperQuantumNumbers().Set(QuantumNumberType::N, Rational(N - DN));
2255  data.quantumidentity.UpperQuantumNumbers().Set(QuantumNumberType::J, Rational(J - DJ));
2256  }
2257 
2258  extract(ell, line, 9);
2259  }
2260 
2261  // Accuracy index for frequency reference
2262  {
2263  Index df;
2264  // Extract HITRAN value:
2265  extract(df, line, 1);
2266  }
2267 
2268  // Accuracy index for intensity reference
2269  {
2270  Index di0;
2271  // Extract HITRAN value:
2272  extract(di0, line, 1);
2273  }
2274 
2275  // Accuracy index for halfwidth reference
2276  {
2277  Index dgam;
2278  // Extract HITRAN value:
2279  extract(dgam, line, 1);
2280  }
2281 
2282  // These were all the parameters that we can extract from
2283  // HITRAN. However, we still have to set the reference temperatures
2284  // to the appropriate value:
2285 
2286  // Reference temperature for Intensity in K.
2287  // (This is fix for HITRAN)
2288  data.T0 = 296.0;
2289 
2290  // Skip four
2291  {
2292  Index four;
2293  extract(four, line, 4);
2294  }
2295 
2296  // This is the test for the last two characters of the line
2297  {
2298  /*
2299  * 0 is nothing,
2300  * -1 is linemixing on the next line,
2301  * -3 is the non-resonant line
2302  */
2303  Index test;
2304  extract(test, line, 2);
2305  //If the tag is as it should be, then a minus one means that more should be read
2306  if (test == -1 || test == -3)
2307  getline(is, line);
2308  else // the line is done and we are happy to leave
2309  {
2310  data.line.LineShape() = LineShape::Model(sgam, nself, agam, nair, psf);
2311 
2312  data.bad = false;
2313  return data;
2314  }
2315  }
2316 
2317  // In case we are unable to leave, the next line is a line mixing parameter line
2318 
2319  // First is the molecular number. This should be the same as above.
2320  {
2321  Index mo2;
2322  extract(mo2, line, 2);
2323  // Skip one
2324 
2325  if (mo != mo2)
2326  throw std::runtime_error("There is an error in the line mixing\n");
2327  }
2328 
2329  Vector Y(4), G(4), T(4);
2330 
2331  // These are constants for AER but should be included because we need their grid.
2332  T[0] = 200;
2333  T[1] = 250;
2334  T[2] = 296;
2335  T[3] = 340;
2336 
2337  // Next is the Y and G at various temperatures
2338  {
2339  Numeric Y_200K;
2340  extract(Y_200K, line, 13);
2341  Y[0] = Y_200K;
2342  }
2343  {
2344  Numeric G_200K;
2345  extract(G_200K, line, 11);
2346  G[0] = G_200K;
2347  }
2348  {
2349  Numeric Y_250K;
2350  extract(Y_250K, line, 13);
2351  Y[1] = Y_250K;
2352  }
2353  {
2354  Numeric G_250K;
2355  extract(G_250K, line, 11);
2356  G[1] = G_250K;
2357  }
2358  {
2359  Numeric Y_296K;
2360  extract(Y_296K, line, 13);
2361  Y[2] = Y_296K;
2362  }
2363  {
2364  Numeric G_296K;
2365  extract(G_296K, line, 11);
2366  G[2] = G_296K;
2367  }
2368  {
2369  Numeric Y_340K;
2370  extract(Y_340K, line, 13);
2371  Y[3] = Y_340K;
2372  }
2373  {
2374  Numeric G_340K;
2375  extract(G_340K, line, 11);
2376  G[3] = G_340K;
2377  }
2378 
2379  Y /= Conversion::ATM2PA;
2380  G /= Conversion::ATM2PA / Conversion::ATM2PA;
2381  Y *=
2382  -1; // ARTS uses (1-iY) as line-mixing factor, LBLRTM CO2 uses (1+iY), so we must change sign
2383 
2384  // Test that this is the end
2385  {
2386  Index test;
2387  extract(test, line, 2);
2388  if (test == -1) {
2389  data.line.LineShape() = LineShape::Model(sgam,
2390  nself,
2391  agam,
2392  nair,
2393  psf,
2394  {T[0],
2395  T[1],
2396  T[2],
2397  T[3],
2398  Y[0],
2399  Y[1],
2400  Y[2],
2401  Y[3],
2402  G[0],
2403  G[1],
2404  G[2],
2405  G[3]});
2406 
2407  data.bad = false;
2408  return data;
2409  } else if (test == -3) {
2410  data.line.LineShape() = LineShape::Model(sgam,
2411  nself,
2412  agam,
2413  nair,
2414  psf,
2415  {T[0],
2416  T[1],
2417  T[2],
2418  T[3],
2419  Y[0],
2420  Y[1],
2421  Y[2],
2422  Y[3],
2423  G[0],
2424  G[1],
2425  G[2],
2426  G[3]});
2427 
2428  data.bad = false;
2429  return data;
2430  } else
2431  return data;
2432  }
2433 }
2434 
2435 std::vector<Absorption::Lines> Absorption::split_list_of_external_lines(std::vector<SingleLineExternal>& external_lines,
2436  const std::vector<QuantumNumberType>& localquantas,
2437  const std::vector<QuantumNumberType>& globalquantas)
2438 {
2439  std::vector<Lines> lines(0);
2440  std::vector<Rational> lowerquanta_local(localquantas.size());
2441  std::vector<Rational> upperquanta_local(localquantas.size());
2442  std::vector<Rational> lowerquanta_global(globalquantas.size());
2443  std::vector<Rational> upperquanta_global(globalquantas.size());
2444 
2445  // Loop but make copies because we will need to modify some of the data
2446  while(external_lines.size()) {
2447  auto& sle = external_lines.back();
2448 
2449  // Set the quantum numbers
2450  std::transform(localquantas.cbegin(), localquantas.cend(), lowerquanta_local.begin(),
2451  [&](auto qn){return sle.quantumidentity.LowerQuantumNumber(qn);});
2452  std::transform(localquantas.cbegin(), localquantas.cend(), upperquanta_local.begin(),
2453  [&](auto qn){return sle.quantumidentity.UpperQuantumNumber(qn);});
2454  std::transform(globalquantas.cbegin(), globalquantas.cend(), lowerquanta_global.begin(),
2455  [&](auto qn){return sle.quantumidentity.LowerQuantumNumber(qn);});
2456  std::transform(globalquantas.cbegin(), globalquantas.cend(), upperquanta_global.begin(),
2457  [&](auto qn){return sle.quantumidentity.UpperQuantumNumber(qn);});
2458 
2459  // Set the line
2460  auto line = sle.line;
2461  line.LowerQuantumNumbers() = lowerquanta_local;
2462  line.UpperQuantumNumbers() = upperquanta_local;
2463 
2464  // Set the global quantum numbers
2465  const QuantumIdentifier qid(sle.quantumidentity.Species(), sle.quantumidentity.Isotopologue(),
2466  globalquantas, upperquanta_global, lowerquanta_global);
2467 
2468  // Either find a line like this in the list of lines or start a new Lines
2469  auto band = std::find_if(lines.begin(), lines.end(), [&](const Lines& li){return li.MatchWithExternal(sle, qid);});
2470  if (band not_eq lines.end()) {
2471  band -> AppendSingleLine(line);
2472  } else {
2473  lines.push_back(Lines(sle.selfbroadening, sle.bathbroadening, sle.cutoff,
2474  sle.mirroring, sle.population, sle.normalization,
2475  sle.lineshapetype, sle.T0, sle.cutofffreq,
2476  sle.linemixinglimit, qid, localquantas, sle.species, {line}));
2477  }
2478  external_lines.pop_back();
2479  }
2480 
2481  return lines;
2482 }
2483 
2484 std::ostream& Absorption::operator<<(std::ostream& os, const Absorption::Lines& lines)
2485 {
2486  for(auto& line: lines.AllLines())
2487  os << line << '\n';
2488  return os;
2489 }
2490 
2491 std::istream& Absorption::operator>>(std::istream& is, Lines& lines) {
2492  for(auto& line: lines.AllLines())
2493  is >> line;
2494  return is;
2495 }
2496 
2497 std::ostream & Absorption::operator<<(std::ostream& os, const Absorption::SingleLine& line)
2498 {
2499  os << line.F0() << ' '
2500  << line.I0() << ' '
2501  << line.E0() << ' '
2502  << line.g_low() << ' '
2503  << line.g_upp() << ' '
2504  << line.A() << ' '
2505  << line.Zeeman() << ' '
2506  << line.LineShape();
2507  for(auto& r: line.LowerQuantumNumbers())
2508  os << r << ' ';
2509  for(auto& r: line.UpperQuantumNumbers())
2510  os << r << ' ';
2511  return os;
2512 }
2513 
2514 std::istream& Absorption::operator>>(std::istream& is, Absorption::SingleLine& line)
2515 {
2516  is >> double_imanip()
2517  >> line.F0()
2518  >> line.I0()
2519  >> line.E0()
2520  >> line.g_low()
2521  >> line.g_upp()
2522  >> line.A();
2523 
2524  is >> line.Zeeman()
2525  >> line.LineShape();
2526  for(auto& r: line.LowerQuantumNumbers())
2527  is >> r;
2528  for(auto& r: line.UpperQuantumNumbers())
2529  is >> r;
2530  return is;
2531 }
2532 
2533 std::ostream& operator<<(std::ostream& os, const ArrayOfAbsorptionLines& aol)
2534 {
2535  for(auto& l: aol)
2536  os << l << '\n';
2537  return os;
2538 }
2539 
2540 std::ostream& operator<<(std::ostream& os, const ArrayOfArrayOfAbsorptionLines& aol)
2541 {
2542  for(auto& l: aol)
2543  os << l << '\n';
2544  return os;
2545 }
2546 
2548 {
2549  // Species lookup data:
2551 
2552  // A reference to the relevant record of the species data:
2553  const SpeciesRecord& spr = species_data[Species()];
2554 
2555  // First the species name:
2556  return spr.Name() + "-" +
2557  spr.Isotopologue()[Isotopologue()].Name();;
2558 }
2559 
2561 {
2562  std::ostringstream out;
2563  out << mquantumidentity.UpperQuantumNumbers() << ' ';
2564  String s=out.str();
2565  if(s.back() == ' ')
2566  s.pop_back();
2567  return s;
2568 }
2569 
2571 {
2572  std::ostringstream out;
2573  out << mquantumidentity.LowerQuantumNumbers() << ' ';
2574  String s=out.str();
2575  if(s.back() == ' ')
2576  s.pop_back();
2577  return s;
2578 }
2579 
2581 {
2582  std::ostringstream os;
2583 
2584  os << "\nLines meta-data:\n";
2585  os << '\t' << "Species identity:\n";
2586  os << "\t\tSpecies: "<< SpeciesName() << '\n';
2587  os << "\t\tLower Quantum Numbers: "<< LowerQuantumNumbers() << '\n';
2588  os << "\t\tUpper Quantum Numbers: "<< UpperQuantumNumbers() << '\n';
2589  os << '\t' << cutofftype2metadatastring(mcutoff, mcutofffreq);
2590  os << '\t' << populationtype2metadatastring(mpopulation);
2591  os << '\t' << normalizationtype2metadatastring(mnormalization);
2592  os << '\t' << LineShape::shapetype2metadatastring(mlineshapetype);
2593  os << '\t' << mirroringtype2metadatastring(mmirroring);
2594  os << '\t' << "The reference temperature for all line parameters is "
2595  << mT0 << " K.\n";
2596  if(mlinemixinglimit < 0)
2597  os << '\t' << "If applicable, there is no line mixing limit.\n";
2598  else
2599  os << '\t' << "If applicable, there is a line mixing limit at "
2600  << mlinemixinglimit << " Pa.\n";
2601 
2602  if (not NumLines()) {
2603  os << "\tNo line data is available.\n";
2604  }
2605  else {
2606  os << "\tThere are " << NumLines() << " lines available.\n";
2607 
2608  auto& line = mlines.front();
2609  os << "\tThe front line has:\n";
2610  os << "\t\t" << "f0: " << line.F0() << " Hz\n";
2611  os << "\t\t" << "i0: " << line.I0() << " m^2/Hz\n";
2612  os << "\t\t" << "e0: " << line.E0() << " J\n";
2613  os << "\t\t" << "Lower stat. weight: " << line.g_low() << " [-]\n";
2614  os << "\t\t" << "Upper stat. weight: " << line.g_upp() << " [-]\n";
2615  os << "\t\t" << "A: " << line.A() << " 1/s\n";
2616  os << "\t\t" << "Zeeman splitting of lower state: " << line.Zeeman().gl() << " [-]\n";
2617  os << "\t\t" << "Zeeman splitting of upper state: " << line.Zeeman().gu() << " [-]\n";
2618  os << "\t\t" << "Lower state local quantum numbers:";
2619  for(size_t i=0; i<mlocalquanta.size(); i++)
2620  os << " " << quantumnumbertype2string(mlocalquanta[i])
2621  << "=" << line.LowerQuantumNumber(i) << ";";
2622  os << "\n";
2623  os << "\t\t" << "Upper state local quantum numbers:";
2624  for(size_t i=0; i<mlocalquanta.size(); i++)
2625  os << " " << quantumnumbertype2string(mlocalquanta[i])
2626  << "=" << line.UpperQuantumNumber(i) << ";";
2627  os << "\n";
2628  ArrayOfString ls_meta = LineShape::ModelMetaDataArray(line.LineShape(),
2629  mselfbroadening,
2630  mbathbroadening,
2631  mbroadeningspecies,
2632  mT0);
2633  os << "\t\t" << "Line shape parameters (are normalized by sum(VMR)):\n";
2634  for(auto& ls_form: ls_meta)
2635  os << "\t\t\t" << ls_form << "\n";
2636  }
2637 
2638 
2639  return os.str();
2640 }
2641 
2642 
2644 {
2645  // Find all hits
2646  std::vector<size_t> hits(0);
2647 
2648  //
2649  for (size_t i=0; i<mlocalquanta.size(); i++) {
2650  bool found=false;
2651  for (auto& line: mlines) {
2652  if (line.LowerQuantumNumber(i).isDefined() or line.UpperQuantumNumber(i).isDefined()) {
2653  found = true;
2654  }
2655  }
2656 
2657  if (not found) {
2658  hits.push_back(i);
2659  }
2660  }
2661 
2662  // Remove from behind to not mess up
2663  while (not hits.empty()) {
2664  RemoveLocalQuantum(hits.back());
2665  hits.pop_back();
2666  }
2667 }
2668 
2670 {
2671  mlocalquanta.erase(mlocalquanta.begin() + x);
2672  for (auto& line: mlines) {
2673  line.LowerQuantumNumbers().erase(line.LowerQuantumNumbers().begin() + x);
2674  line.UpperQuantumNumbers().erase(line.UpperQuantumNumbers().begin() + x);
2675  }
2676 }
2677 
2678 
2680 {
2681  return
2682  std::equal(mlowerquanta.cbegin(), mlowerquanta.cend(), sl.mlowerquanta.cbegin(), sl.mlowerquanta.cend()) and
2683  std::equal(mupperquanta.cbegin(), mupperquanta.cend(), sl.mupperquanta.cbegin(), sl.mupperquanta.cend()) ;
2684 }
2685 
2686 
2688 {
2689  mlines.erase(mlines.begin() + i);
2690 }
2691 
2692 
2694 {
2695  auto line = mlines[i];
2696  RemoveLine(i);
2697  return line;
2698 }
2699 
2700 
2702  return Absorption::Lines(al.Self(), al.Bath(), al.Cutoff(), al.Mirroring(),
2703  al.Population(), al.Normalization(), al.LineShapeType(),
2704  al.T0(), al.CutoffFreqValue(), al.LinemixingLimit(),
2705  al.QuantumIdentity(), al.LocalQuanta(), al.BroadeningSpecies());
2706 }
2707 
2708 
2710 {
2711  return mlines[i];
2712 }
2713 
2714 
2716 {
2717  return mlines[i];
2718 }
2719 
2721 {
2722  std::reverse(mlines.begin(), mlines.end());
2723 }
2724 
2726 {
2727  return global_data::species_data[Species()].Isotopologue()[Isotopologue()].Mass();
2728 }
2729 
2731  const ArrayOfArrayOfSpeciesTag& atm_spec) const
2732 {
2733  return LineShape::vmrs(atm_vmrs, atm_spec, QuantumIdentity(),
2734  BroadeningSpecies(), Self(), Bath(), LineShapeType());
2735 }
2736 
2738  const ArrayOfArrayOfSpeciesTag& atm_spec) const
2739 {
2740  if (atm_vmrs.nelem() not_eq atm_spec.nelem())
2741  throw std::runtime_error("Bad species and vmr lists");
2742 
2743  for (Index i=0; i<atm_spec.nelem(); i++)
2744  if (atm_spec[i].nelem() and atm_spec[i][0].Species() == Species())
2745  return atm_vmrs[i];
2746  return 0;
2747 }
2748 
2749 bool Absorption::line_in_id(const Absorption::Lines& band, const QuantumIdentifier& id, size_t line_index)
2750 {
2751  if (band.Species() != id.Species())
2752  return false;
2753  else if (band.Isotopologue() != id.Isotopologue())
2754  return false;
2755  else if (id.Type() == QuantumIdentifier::NONE)
2756  return false;
2757  else if (id.Type() == QuantumIdentifier::ALL)
2758  return true;
2759  else if (id.Type() == QuantumIdentifier::ENERGY_LEVEL)
2760  throw std::runtime_error("Cannot match energy level to line");
2761  else {
2762  for (Index iq=0; iq<Index(QuantumNumberType::FINAL_ENTRY); iq++) {
2763  auto qn_line = band.LowerQuantumNumber(line_index, QuantumNumberType(iq));
2764  auto qn_id = id.LowerQuantumNumber(QuantumNumberType(iq));
2765 
2766  if ((qn_id.isUndefined() and qn_line.isDefined()) or
2767  (qn_line.isDefined() and qn_line not_eq qn_id)) {
2768  return false;
2769  }
2770  }
2771 
2772  for (Index iq=0; iq<Index(QuantumNumberType::FINAL_ENTRY); iq++) {
2773  auto qn_line = band.UpperQuantumNumber(line_index, QuantumNumberType(iq));
2774  auto qn_id = id.UpperQuantumNumber(QuantumNumberType(iq));
2775 
2776  if ((qn_id.isUndefined() and qn_line.isDefined()) or
2777  (qn_line.isDefined() and qn_line not_eq qn_id)) {
2778  return false;
2779  }
2780  }
2781  }
2782 
2783  return true;
2784 }
2785 
2786 bool Absorption::line_upper_in_id(const Absorption::Lines& band, const QuantumIdentifier& id, size_t line_index)
2787 {
2788  if (band.Species() != id.Species())
2789  return false;
2790  else if (band.Isotopologue() != id.Isotopologue())
2791  return false;
2792  else if (id.Type() == QuantumIdentifier::NONE)
2793  return false;
2794  else if (id.Type() == QuantumIdentifier::ALL)
2795  return true;
2796  else if (id.Type() == QuantumIdentifier::TRANSITION)
2797  throw std::runtime_error("Cannot match transition level to energy level");
2798  else {
2799  for (Index iq=0; iq<Index(QuantumNumberType::FINAL_ENTRY); iq++) {
2800  auto qn_line = band.UpperQuantumNumber(line_index, QuantumNumberType(iq));
2801  auto qn_id = id.EnergyLevelQuantumNumber(QuantumNumberType(iq));
2802 
2803  if ((qn_id.isUndefined() and qn_line.isDefined()) or
2804  (qn_line.isDefined() and qn_line not_eq qn_id)) {
2805  return false;
2806  }
2807  }
2808  }
2809 
2810  return true;
2811 }
2812 
2813 bool Absorption::line_lower_in_id(const Absorption::Lines& band, const QuantumIdentifier& id, size_t line_index)
2814 {
2815  if (band.Species() != id.Species())
2816  return false;
2817  else if (band.Isotopologue() != id.Isotopologue())
2818  return false;
2819  else if (id.Type() == QuantumIdentifier::NONE)
2820  return false;
2821  else if (id.Type() == QuantumIdentifier::ALL)
2822  return true;
2823  else if (id.Type() == QuantumIdentifier::TRANSITION)
2824  throw std::runtime_error("Cannot match transition level to energy level");
2825  else {
2826  for (Index iq=0; iq<Index(QuantumNumberType::FINAL_ENTRY); iq++) {
2827  auto qn_line = band.LowerQuantumNumber(line_index, QuantumNumberType(iq));
2828  auto qn_id = id.EnergyLevelQuantumNumber(QuantumNumberType(iq));
2829 
2830  if ((qn_id.isUndefined() and qn_line.isDefined()) or
2831  (qn_line.isDefined() and qn_line not_eq qn_id)) {
2832  return false;
2833  }
2834  }
2835  }
2836 
2837  return true;
2838 }
2839 
2840 bool Absorption::id_in_line(const Absorption::Lines& band, const QuantumIdentifier& id, size_t line_index)
2841 {
2842  if (band.Species() != id.Species())
2843  return false;
2844  else if (band.Isotopologue() != id.Isotopologue())
2845  return false;
2846  else if (id.Type() == QuantumIdentifier::NONE)
2847  return false;
2848  else if (id.Type() == QuantumIdentifier::ALL)
2849  return true;
2850  else if (id.Type() == QuantumIdentifier::ENERGY_LEVEL)
2851  throw std::runtime_error("Cannot match energy level to line");
2852  else {
2853  for (Index iq=0; iq<Index(QuantumNumberType::FINAL_ENTRY); iq++) {
2854  auto qn_line = band.LowerQuantumNumber(line_index, QuantumNumberType(iq));
2855  auto qn_id = id.LowerQuantumNumber(QuantumNumberType(iq));
2856 
2857  if ((qn_line.isUndefined() and qn_id.isDefined()) or
2858  (qn_id.isDefined() and qn_id not_eq qn_line)) {
2859  return false;
2860  }
2861  }
2862 
2863  for (Index iq=0; iq<Index(QuantumNumberType::FINAL_ENTRY); iq++) {
2864  auto qn_line = band.UpperQuantumNumber(line_index, QuantumNumberType(iq));
2865  auto qn_id = id.UpperQuantumNumber(QuantumNumberType(iq));
2866 
2867  if ((qn_line.isUndefined() and qn_id.isDefined()) or
2868  (qn_id.isDefined() and qn_id not_eq qn_line)) {
2869  return false;
2870  }
2871  }
2872  }
2873 
2874  return true;
2875 }
2876 
2877 bool Absorption::id_in_line_upper(const Absorption::Lines& band, const QuantumIdentifier& id, size_t line_index)
2878 {
2879  if (band.Species() != id.Species())
2880  return false;
2881  else if (band.Isotopologue() != id.Isotopologue())
2882  return false;
2883  else if (id.Type() == QuantumIdentifier::NONE)
2884  return false;
2885  else if (id.Type() == QuantumIdentifier::ALL)
2886  return true;
2887  else if (id.Type() == QuantumIdentifier::TRANSITION)
2888  throw std::runtime_error("Cannot match transition level to energy level");
2889  else {
2890  for (Index iq=0; iq<Index(QuantumNumberType::FINAL_ENTRY); iq++) {
2891  auto qn_line = band.UpperQuantumNumber(line_index, QuantumNumberType(iq));
2892  auto qn_id = id.EnergyLevelQuantumNumber(QuantumNumberType(iq));
2893 
2894  if ((qn_line.isUndefined() and qn_id.isDefined()) or
2895  (qn_id.isDefined() and qn_id not_eq qn_line)) {
2896  return false;
2897  }
2898  }
2899  }
2900 
2901  return true;
2902 }
2903 
2904 bool Absorption::id_in_line_lower(const Absorption::Lines& band, const QuantumIdentifier& id, size_t line_index)
2905 {
2906  if (band.Species() != id.Species())
2907  return false;
2908  else if (band.Isotopologue() != id.Isotopologue())
2909  return false;
2910  else if (id.Type() == QuantumIdentifier::NONE)
2911  return false;
2912  else if (id.Type() == QuantumIdentifier::ALL)
2913  return true;
2914  else if (id.Type() == QuantumIdentifier::TRANSITION)
2915  throw std::runtime_error("Cannot match transition level to energy level");
2916  else {
2917  for (Index iq=0; iq<Index(QuantumNumberType::FINAL_ENTRY); iq++) {
2918  auto qn_line = band.LowerQuantumNumber(line_index, QuantumNumberType(iq));
2919  auto qn_id = id.EnergyLevelQuantumNumber(QuantumNumberType(iq));
2920 
2921  if ((qn_line.isUndefined() and qn_id.isDefined()) or
2922  (qn_id.isDefined() and qn_id not_eq qn_line)) {
2923  return false;
2924  }
2925  }
2926  }
2927 
2928  return true;
2929 }
2930 
2931 bool Absorption::line_is_id(const Absorption::Lines& band, const QuantumIdentifier& id, size_t line_index)
2932 {
2933  if (line_in_id(band, id, line_index) and id_in_line(band, id, line_index))
2934  return true;
2935  else
2936  return false;
2937 }
2938 
2940  if (not even(Jf + lf + 1))
2941  return - sqrt(2 * Jf + 1) * wigner3j(Jf, k, Ji, li, lf - li, -lf);
2942  else
2943  return + sqrt(2 * Jf + 1) * wigner3j(Jf, k, Ji, li, lf - li, -lf);
2944 }
2945 
2947  if (not even(Jf + N))
2948  return - sqrt(6 * (2 * Jf + 1) * (2 * Ji + 1)) * wigner6j(1_rat, 1_rat, 1_rat, Ji, Jf, N);
2949  else
2950  return + sqrt(6 * (2 * Jf + 1) * (2 * Ji + 1)) * wigner6j(1_rat, 1_rat, 1_rat, Ji, Jf, N);
2951 }
2952 
2954 {
2955  // Default data and values for this type
2957  data.selfbroadening = true;
2958  data.bathbroadening = true;
2959  data.lineshapetype = LineShape::Type::VP;
2960  data.species.resize(2);
2961 
2962  // Global species lookup data:
2964 
2965  // This value is used to flag missing data both in species and
2966  // isotopologue lists. Could be any number, it just has to be made sure
2967  // that it is neither the index of a species nor of an isotopologue.
2968  const Index missing = species_data.nelem() + 100;
2969 
2970  // We need a species index sorted by MYTRAN tag. Keep this in a
2971  // static variable, so that we have to do this only once. The ARTS
2972  // species index is hind[<MYTRAN tag>]. The value of
2973  // missing means that we don't have this species.
2974  //
2975  // Allow for up to 100 species in MYTRAN in the future.
2976  static Array<Index> hspec(100, missing);
2977 
2978  // This is an array of arrays for each mytran tag. It contains the
2979  // ARTS indices of the MYTRAN isotopologues.
2980  static Array<ArrayOfIndex> hiso(100);
2981 
2982  // Remember if this stuff has already been initialized:
2983  static bool hinit = false;
2984 
2985  // Remember, about which missing species we have already issued a
2986  // warning:
2987  static ArrayOfIndex warned_missing;
2988 
2989  if (!hinit) {
2990  for (Index i = 0; i < species_data.nelem(); ++i) {
2991  const SpeciesRecord& sr = species_data[i];
2992 
2993  // We have to be careful and check for the case that all
2994  // MYTRAN isotopologue tags are -1 (this species is missing in MYTRAN).
2995 
2996  if (0 < sr.Isotopologue()[0].MytranTag()) {
2997  // The MYTRAN tags are stored as species plus isotopologue tags
2998  // (MO and ISO)
2999  // in the Isotopologue() part of the species record.
3000  // We can extract the MO part from any of the isotopologue tags,
3001  // so we use the first one. We do this by taking an integer
3002  // division by 10.
3003 
3004  Index mo = sr.Isotopologue()[0].MytranTag() / 10;
3005  // cout << "mo = " << mo << endl;
3006  hspec[mo] = i;
3007 
3008  // Get a nicer to handle array of MYTRAN iso tags:
3009  Index n_iso = sr.Isotopologue().nelem();
3010  ArrayOfIndex iso_tags;
3011  iso_tags.resize(n_iso);
3012  for (Index j = 0; j < n_iso; ++j) {
3013  iso_tags[j] = sr.Isotopologue()[j].MytranTag();
3014  }
3015 
3016  // Reserve elements for the isotopologue tags. How much do we
3017  // need? This depends on the largest MYTRAN tag that we know
3018  // about!
3019  // Also initialize the tags to missing.
3020  // cout << "iso_tags = " << iso_tags << endl;
3021  // cout << "static_cast<Index>(max(iso_tags))%10 + 1 = "
3022  // << static_cast<Index>(max(iso_tags))%10 + 1 << endl;
3023  hiso[mo].resize(max(iso_tags) % 10 + 1);
3024  hiso[mo] = missing; // Matpack can set all elements like this.
3025 
3026  // Set the isotopologue tags:
3027  for (Index j = 0; j < n_iso; ++j) {
3028  if (0 < iso_tags[j]) // ignore -1 elements
3029  {
3030  // To get the iso tags from MytranTag() we also have to take
3031  // modulo 10 to get rid of mo.
3032  // cout << "iso_tags[j] % 10 = " << iso_tags[j] % 10 << endl;
3033  hiso[mo][iso_tags[j] % 10] = j;
3034  }
3035  }
3036  }
3037  }
3038 
3039  hinit = true;
3040  }
3041 
3042  // This contains the rest of the line to parse. At the beginning the
3043  // entire line. Line gets shorter and shorter as we continue to
3044  // extract stuff from the beginning.
3045  String line;
3046 
3047  // The first item is the molecule number:
3048  Index mo;
3049 
3050  // Look for more comments?
3051  bool comment = true;
3052 
3053  while (comment) {
3054  // Return true if eof is reached:
3055  if (is.eof()) return data;
3056 
3057  // Throw runtime_error if stream is bad:
3058  if (!is) throw runtime_error("Stream bad.");
3059 
3060  // Read line from file into linebuffer:
3061  getline(is, line);
3062 
3063  // It is possible that we were exactly at the end of the file before
3064  // calling getline. In that case the previous eof() was still false
3065  // because eof() evaluates only to true if one tries to read after the
3066  // end of the file. The following check catches this.
3067  if (line.nelem() == 0 && is.eof()) return data;
3068 
3069  // Because of the fixed FORTRAN format, we need to break up the line
3070  // explicitly in apropriate pieces. Not elegant, but works!
3071 
3072  // Extract molecule number:
3073  mo = 0;
3074  // Initialization of mo is important, because mo stays the same
3075  // if line is empty.
3076  extract(mo, line, 2);
3077  // cout << "mo = " << mo << endl;
3078 
3079  // If mo == 0 this is just a comment line:
3080  if (0 != mo) {
3081  // See if we know this species. We will give an error if a
3082  // species is not known.
3083  if (missing != hspec[mo])
3084  comment = false;
3085  else {
3086  // See if this is already in warned_missing, use
3087  // std::count for that:
3088  if (0 == std::count(warned_missing.begin(), warned_missing.end(), mo)) {
3089  warned_missing.push_back(mo);
3090  }
3091  }
3092  }
3093  }
3094 
3095  // Ok, we seem to have a valid species here.
3096 
3097  // Set mspecies from my cool index table:
3098  data.quantumidentity.Species(hspec[mo]);
3099 
3100  // Extract isotopologue:
3101  Index iso;
3102  extract(iso, line, 1);
3103  // cout << "iso = " << iso << endl;
3104 
3105  // Set misotopologue from the other cool index table.
3106  // We have to be careful to issue an error for unknown iso tags. Iso
3107  // could be either larger than the size of hiso[mo], or set
3108  // explicitly to missing. Unfortunately we have to test both cases.
3109  data.quantumidentity.Isotopologue(missing);
3110  if (iso < hiso[mo].nelem())
3111  if (missing != hiso[mo][iso]) data.quantumidentity.Isotopologue(hiso[mo][iso]);
3112 
3113  // Issue error message if misotopologue is still missing:
3114  if (missing == data.quantumidentity.Isotopologue()) {
3115  ostringstream os;
3116  os << "Species: " << species_data[data.quantumidentity.Species()].Name()
3117  << ", isotopologue iso = " << iso << " is unknown.";
3118  throw runtime_error(os.str());
3119  }
3120 
3121  // Position.
3122  {
3123  // MYTRAN position in MHz:
3124  Numeric v;
3125 
3126  // Extract MYTRAN postion:
3127  extract(v, line, 13);
3128 
3129  // ARTS position in Hz:
3130  data.line.F0() = v * 1E6;
3131  // cout << "mf = " << mf << endl;
3132  }
3133 
3134  // Accuracy for line position
3135  {
3136  // Extract MYTRAN postion accuracy:
3137  Numeric df;
3138  extract(df, line, 8);
3139  }
3140 
3141  // Intensity.
3142  {
3143  constexpr Numeric SPEED_OF_LIGHT = Constant::c; // in [m/s]
3144 
3145  // MYTRAN2 intensity is in cm-1/(molec * cm-2) at 296 Kelvin.
3146  // (just like HITRAN, only isotopologue ratio is not included)
3147  // The first cm-1 is the frequency unit (it cancels with the
3148  // 1/frequency unit of the line shape function).
3149  //
3150  // We need to do the following:
3151  // 1. Convert frequency from wavenumber to Hz (factor 1e2 * c)
3152  // 2. Convert [molec * cm-2] to [molec * m-2] (factor 1e-4)
3153 
3154  const Numeric hi2arts = 1e-2 * SPEED_OF_LIGHT;
3155 
3156  Numeric s;
3157 
3158  // Extract HITRAN intensity:
3159  extract(s, line, 10);
3160 
3161  // Convert to ARTS units (Hz / (molec * m-2) ), or shorter: Hz*m^2
3162  data.line.I0() = s * hi2arts;
3163  }
3164 
3165  // Air broadening parameters.
3166  Numeric agam, sgam;
3167  {
3168  // MYTRAN parameter is in MHz/Torr at reference temperature
3169  // All parameters are HWHM
3170  Numeric gam;
3171  // External constant from constants.cc: Converts torr to
3172  // Pa. Multiply value in torr by this number to get value in Pa.
3173  constexpr Numeric TORR2PA = Conversion::TORR2PA;
3174 
3175  // Extract HITRAN AGAM value:
3176  extract(gam, line, 5);
3177 
3178  // ARTS parameter in Hz/Pa:
3179  agam = gam * 1E6 / TORR2PA;
3180 
3181  // Extract MYTRAN SGAM value:
3182  extract(gam, line, 5);
3183 
3184  // ARTS parameter in Hz/Pa:
3185  sgam = gam * 1E6 / TORR2PA;
3186 
3187  // cout << "agam, sgam = " << magam << ", " << msgam << endl;
3188  }
3189 
3190  // Lower state energy.
3191  {
3192  // MYTRAN parameter is in wavenumbers (cm^-1).
3193  // We have to convert this to the ARTS unit Joule.
3194 
3195  // Extract from Catalogue line
3196  extract(data.line.E0(), line, 10);
3197 
3198  // Convert to Joule:
3199  data.line.E0() = wavenumber_to_joule(data.line.E0());
3200  }
3201 
3202  // Temperature coefficient of broadening parameters.
3203  Numeric nself, nair;
3204  {
3205  // This is dimensionless, we can also extract directly.
3206  extract(nair, line, 4);
3207 
3208  // Extract the self broadening parameter:
3209  extract(nself, line, 4);
3210  // cout << "mnair = " << mnair << endl;
3211  }
3212 
3213  // Reference temperature for broadening parameter in K:
3214  Numeric tgam;
3215  {
3216  // correct units, extract directly
3217  extract(tgam, line, 7);
3218  }
3219 
3220  // Pressure shift.
3221  Numeric psf;
3222  {
3223  // MYTRAN value in MHz/Torr
3224  Numeric d;
3225  // External constant from constants.cc: Converts torr to
3226  // Pa. Multiply value in torr by this number to get value in Pa.
3227  constexpr Numeric TORR2PA = Conversion::TORR2PA;
3228 
3229  // Extract MYTRAN value:
3230  extract(d, line, 9);
3231 
3232  // ARTS value in Hz/Pa
3233  psf = d * 1E6 / TORR2PA;
3234  }
3235  // Set the accuracies using the definition of MYTRAN accuracy
3236  // indices. If some are missing, they are set to -1.
3237 
3238  //Skip upper state global quanta index
3239  {
3240  Index eu;
3241  extract(eu, line, 3);
3242  }
3243 
3244  //Skip lower state global quanta index
3245  {
3246  Index el;
3247  extract(el, line, 3);
3248  }
3249 
3250  //Skip upper state local quanta
3251  {
3252  Index eul;
3253  extract(eul, line, 9);
3254  }
3255 
3256  //Skip lower state local quanta
3257  {
3258  Index ell;
3259  extract(ell, line, 9);
3260  }
3261  // Accuracy index for intensity
3262  {
3263  Index di0;
3264  // Extract MYTRAN value:
3265  extract(di0, line, 1);
3266  }
3267 
3268  // Accuracy index for AGAM
3269  {
3270  Index dgam;
3271  // Extract MYTRAN value:
3272  extract(dgam, line, 1);
3273  }
3274 
3275  // Accuracy index for NAIR
3276  {
3277  Index dair;
3278  // Extract MYTRAN value:
3279  extract(dair, line, 1);
3280  }
3281 
3282  // These were all the parameters that we can extract from
3283  // MYTRAN. However, we still have to set the reference temperatures
3284  // to the appropriate value:
3285 
3286  // Reference temperature for Intensity in K.
3287  // (This is fix for MYTRAN2)
3288  data.T0 = 296.0;
3289 
3290  // It is important that you intialize here all the new parameters that
3291  // you added to the line record. (This applies to all the reading
3292  // functions, also for ARTS, JPL, and HITRAN format.) Parameters
3293  // should be either set from the catalogue, or set to -1.)
3294 
3295  // Convert to correct temperature if tgam != ti0
3296  if (tgam != data.T0) {
3297  agam *= pow(tgam / data.T0, nair);
3298  sgam *= pow(tgam / data.T0, nself);
3299  psf *= pow(tgam / data.T0, (Numeric).25 + (Numeric)1.5 * nair);
3300  }
3301 
3302  // Set line shape computer
3303  data.line.LineShape() = LineShape::Model(sgam, nself, agam, nair, psf);
3304 
3305  // That's it!
3306  data.bad = false;
3307  return data;
3308 }
3309 
3311 {
3312  // Default data and values for this type
3314  data.selfbroadening = true;
3315  data.bathbroadening = true;
3316  data.lineshapetype = LineShape::Type::VP;
3317  data.species.resize(2);
3318 
3319  // Global species lookup data:
3321 
3322  // We need a species index sorted by JPL tag. Keep this in a
3323  // static variable, so that we have to do this only once. The ARTS
3324  // species index is JplMap[<JPL tag>]. We need Index in this map,
3325  // because the tag array within the species data is an Index array.
3326  static map<Index, SpecIsoMap> JplMap;
3327 
3328  // Remember if this stuff has already been initialized:
3329  static bool hinit = false;
3330 
3331  if (!hinit) {
3332  for (Index i = 0; i < species_data.nelem(); ++i) {
3333  const SpeciesRecord& sr = species_data[i];
3334 
3335  for (Index j = 0; j < sr.Isotopologue().nelem(); ++j) {
3336  for (Index k = 0; k < sr.Isotopologue()[j].JplTags().nelem(); ++k) {
3337  SpecIsoMap indicies(i, j);
3338 
3339  JplMap[sr.Isotopologue()[j].JplTags()[k]] = indicies;
3340  }
3341  }
3342  }
3343  hinit = true;
3344  }
3345 
3346  // This contains the rest of the line to parse. At the beginning the
3347  // entire line. Line gets shorter and shorter as we continue to
3348  // extract stuff from the beginning.
3349  String line;
3350 
3351  // Look for more comments?
3352  bool comment = true;
3353 
3354  while (comment) {
3355  // Return true if eof is reached:
3356  if (is.eof()) return data;
3357 
3358  // Throw runtime_error if stream is bad:
3359  if (!is) throw runtime_error("Stream bad.");
3360 
3361  // Read line from file into linebuffer:
3362  getline(is, line);
3363 
3364  // It is possible that we were exactly at the end of the file before
3365  // calling getline. In that case the previous eof() was still false
3366  // because eof() evaluates only to true if one tries to read after the
3367  // end of the file. The following check catches this.
3368  if (line.nelem() == 0 && is.eof()) return data;
3369 
3370  // Because of the fixed FORTRAN format, we need to break up the line
3371  // explicitly in apropriate pieces. Not elegant, but works!
3372 
3373  // Extract center frequency:
3374  // Initialization of v is important, because v stays the same
3375  // if line is empty.
3376  // JPL position in MHz:
3377  Numeric v = 0.0;
3378 
3379  // Extract JPL position:
3380  extract(v, line, 13);
3381 
3382  // check for empty line
3383  if (v != 0.0) {
3384  // ARTS position in Hz:
3385  data.line.F0() = v * 1E6;
3386 
3387  comment = false;
3388  }
3389  }
3390 
3391  // Accuracy for line position
3392  {
3393  Numeric df;
3394  extract(df, line, 8);
3395  }
3396 
3397  // Intensity.
3398  {
3399  // JPL has log (10) of intensity in nm2 MHz at 300 Kelvin.
3400  //
3401  // We need to do the following:
3402  // 1. take 10^intensity
3403  // 2. convert to cm-1/(molecule * cm-2): devide by c * 1e10
3404  // 3. Convert frequency from wavenumber to Hz (factor 1e2 * c)
3405  // 4. Convert [molec * cm-2] to [molec * m-2] (factor 1e-4)
3406 
3407  Numeric s;
3408 
3409  // Extract JPL intensity:
3410  extract(s, line, 8);
3411 
3412  // remove log
3413  s = pow((Numeric)10., s);
3414 
3415  // Convert to ARTS units (Hz / (molec * m-2) ), or shorter: Hz*m^2
3416  data.line.I0() = s / 1E12;
3417  }
3418 
3419  // Degrees of freedom
3420  {
3421  Index dr;
3422 
3423  // Extract degrees of freedom
3424  extract(dr, line, 2);
3425  }
3426 
3427  // Lower state energy.
3428  {
3429  // JPL parameter is in wavenumbers (cm^-1).
3430  // We have to convert this to the ARTS unit Joule.
3431 
3432  // Extract from Catalogue line
3433  extract(data.line.E0(), line, 10);
3434 
3435  // Convert to Joule:
3436  data.line.E0() = wavenumber_to_joule(data.line.E0());
3437  }
3438 
3439  // Upper state degeneracy
3440  {
3441  Index gup;
3442 
3443  // Extract upper state degeneracy
3444  extract(gup, line, 3);
3445  }
3446 
3447  // Tag number
3448  Index tag;
3449  {
3450  // Extract Tag number
3451  extract(tag, line, 7);
3452 
3453  // make sure tag is not negative (damned jpl cat):
3454  tag = tag > 0 ? tag : -tag;
3455  }
3456 
3457  // ok, now for the cool index map:
3458 
3459  // is this tag valid?
3460  const map<Index, SpecIsoMap>::const_iterator i = JplMap.find(tag);
3461  if (i == JplMap.end()) {
3462  ostringstream os;
3463  os << "JPL Tag: " << tag << " is unknown.";
3464  throw runtime_error(os.str());
3465  }
3466 
3467  SpecIsoMap id = i->second;
3468 
3469  // Set line ID
3470  data.quantumidentity.Species(id.Speciesindex());
3471  data.quantumidentity.Isotopologue(id.Isotopologueindex());
3472 
3473  // Air broadening parameters: unknown to jpl, use old iup forward
3474  // model default values, which is mostly set to 0.0025 GHz/hPa, even
3475  // though for some lines the pressure broadening is given explicitly
3476  // in the program code. The explicitly given values are ignored and
3477  // only the default value is set. Self broadening was in general not
3478  // considered in the old forward model.
3479  Numeric agam, sgam;
3480  {
3481  // ARTS parameter in Hz/Pa:
3482  agam = 2.5E4;
3483 
3484  // ARTS parameter in Hz/Pa:
3485  sgam = agam;
3486  }
3487 
3488  // Temperature coefficient of broadening parameters. Was set to 0.75
3489  // in old forward model, even though for some lines the parameter is
3490  // given explicitly in the program code. The explicitly given values
3491  // are ignored and only the default value is set. Self broadening
3492  // not considered.
3493  Numeric nair, nself;
3494  {
3495  nair = 0.75;
3496  nself = 0.0;
3497  }
3498 
3499  // Reference temperature for broadening parameter in K, was
3500  // generally set to 300 K in old forward model, with the exceptions
3501  // as already mentioned above: //DEPRECEATED but is same as for mti0 so moving on
3502  // {
3503  // mtgam = 300.0;
3504  // }
3505 
3506  // Pressure shift: not given in JPL, set to 0
3507  Numeric psf;
3508  { psf = 0.0; }
3509 
3510  // These were all the parameters that we can extract from
3511  // JPL. However, we still have to set the reference temperatures
3512  // to the appropriate value:
3513 
3514  // Reference temperature for Intensity in K.
3515  data.T0 = 300.0;
3516 
3517  // Set line shape computer
3518  data.line.LineShape() = LineShape::Model(sgam, nself, agam, nair, psf);
3519 
3520  // That's it!
3521  data.bad = false;
3522  return data;
3523 }
3524 
3525 
3526 
3527 bool Absorption::Lines::OK() const noexcept
3528 {
3529  const Index nq = mlocalquanta.size();
3530  const Index nb = mbroadeningspecies.nelem();
3531 
3532  // Test that self and bath is covered by the range if set positive
3533  if (nb < (Index(mselfbroadening) + Index(mbathbroadening)))
3534  return false;
3535 
3536  // Test that the temperature is physical
3537  if (mT0 <= 0)
3538  return false;
3539 
3540  // Test that all lines have the correct sized line shape model
3541  if (std::any_of(mlines.cbegin(), mlines.cend(), [nb](auto& line){return line.LineShapeElems() != nb;}))
3542  return false;
3543 
3544  // Test that all lines have the correct sized local quantum numbers
3545  if (std::any_of(mlines.cbegin(), mlines.cend(), [nq](auto& line){return line.LowerQuantumElems() != nq or line.UpperQuantumElems() != nq;}))
3546  return false;
3547 
3548  // Otherwise everything is fine!
3549  return true;
3550 }
Self
Self
Definition: arts_api_classes.h:211
transform
void transform(VectorView y, double(&my_func)(double), ConstVectorView x)
A generic transform function for vectors, which can be used to implement mathematical functions opera...
Definition: matpackI.cc:1476
Absorption::Lines::LowerQuantumNumber
Rational LowerQuantumNumber(size_t k, QuantumNumberType qnt) const noexcept
Quantum number lower level.
Definition: absorptionlines.cc:40
LineShape::from_linefunctiondata
std::istream & from_linefunctiondata(std::istream &data, Type &type, bool &self, bool &bath, Model &m, ArrayOfSpeciesTag &species)
Definition: lineshapemodel.cc:158
QuantumNumberType::N
@ N
QuantumIdentifier::ENERGY_LEVEL
@ ENERGY_LEVEL
Definition: quantum.h:393
Absorption::SingleLine::A
Numeric A() const noexcept
Einstein spontaneous emission.
Definition: absorptionlines.h:351
Absorption::Lines::AllLines
const std::vector< SingleLine > & AllLines() const noexcept
Lines.
Definition: absorptionlines.h:855
QuantumIdentifier
Class to identify and match lines by their quantum numbers.
Definition: quantum.h:390
Absorption::SingleLine::F0
Numeric F0() const noexcept
Central frequency.
Definition: absorptionlines.h:342
SpecIsoMap
Definition: absorption.h:338
Absorption::SingleLine::g_upp
Numeric g_upp() const noexcept
Upper level statistical weight.
Definition: absorptionlines.h:357
QuantumParserHITRAN2004
Parser for quantum number strings from HITRAN 2004 and later.
Definition: quantum_parser_hitran.h:31
Absorption::Lines::BroadeningSpeciesVMR
Vector BroadeningSpeciesVMR(const ConstVectorView, const ArrayOfArrayOfSpeciesTag &) const
Returns the VMRs of the broadening species.
Definition: absorptionlines.cc:2730
absorption.h
Declarations required for the calculation of absorption coefficients.
iso
void iso(Array< IsotopologueRecord >::iterator &ii, String name, const ArrayOfNumeric &coeff, const ArrayOfNumeric &temp_range, const Index &coefftype)
Initialize isotopologue and move iterator to next one.
Definition: partition_function_data.cc:1732
Absorption::SingleLine::I0
Numeric I0() const noexcept
Reference line strength.
Definition: absorptionlines.h:348
wigner6j
Numeric wigner6j(const Rational j1, const Rational j2, const Rational j3, const Rational l1, const Rational l2, const Rational l3)
Wigner 6J symbol.
Definition: wigner_functions.cc:65
QuantumNumberType::F
@ F
Absorption::ReadFromJplStream
SingleLineExternal ReadFromJplStream(istream &is)
Read from JPL.
Definition: absorptionlines.cc:3310
Absorption::id_in_line
bool id_in_line(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line's ID.
Definition: absorptionlines.cc:2840
absorptionlines.h
Contains the absorption namespace.
Absorption::operator<<
std::ostream & operator<<(std::ostream &, const SingleLine &)
Definition: absorptionlines.cc:2497
Absorption::Lines::SelfVMR
Numeric SelfVMR(const ConstVectorView, const ArrayOfArrayOfSpeciesTag &) const
Returns the VMR of the species.
Definition: absorptionlines.cc:2737
QuantumNumberType
QuantumNumberType
Enum for Quantum Numbers used for indexing.
Definition: quantum.h:48
LineShape::shapetype2metadatastring
String shapetype2metadatastring(Type type) noexcept
Turns selected Type into a human readable string.
Definition: lineshapemodel.h:830
Absorption::Lines::UpperQuantumNumbers
String UpperQuantumNumbers() const noexcept
Upper quantum numbers string.
Definition: absorptionlines.cc:2560
Absorption::reduced_rovibrational_dipole
Numeric reduced_rovibrational_dipole(Rational Jf, Rational Ji, Rational lf, Rational li, Rational k=Rational(1))
Compute the reduced rovibrational dipole moment.
Definition: absorptionlines.cc:2939
Absorption::SingleLine::UpperQuantumNumbers
const std::vector< Rational > & UpperQuantumNumbers() const noexcept
Upper level quantum numbers.
Definition: absorptionlines.h:369
operator<<
std::ostream & operator<<(std::ostream &os, const ArrayOfAbsorptionLines &aol)
Definition: absorptionlines.cc:2533
data
G0 G2 FVC Y DV Numeric Numeric Numeric Zeeman LowerQuantumNumbers void * data
Definition: arts_api_classes.cc:232
Species
QuantumIdentifier::QType Index LowerQuantumNumbers Species
Definition: arts_api_classes.cc:255
Absorption::id_in_line_lower
bool id_in_line_lower(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line's ID.
Definition: absorptionlines.cc:2904
global_data::species_data
const Array< SpeciesRecord > species_data
Species Data.
Definition: partition_function_data.cc:42
LineShape::from_artscat4
std::istream & from_artscat4(std::istream &is, Type &type, bool &self, bool &bath, Model &m, ArrayOfSpeciesTag &species, const QuantumIdentifier &qid)
Definition: lineshapemodel.cc:95
Absorption::SingleLine
Computations and data for a single absorption line.
Definition: absorptionlines.h:246
Absorption::Lines::ReverseLines
void ReverseLines() noexcept
Reverses the order of the internal lines.
Definition: absorptionlines.cc:2720
QuantumNumberType::v1
@ v1
Absorption::id_in_line_upper
bool id_in_line_upper(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line's ID.
Definition: absorptionlines.cc:2877
wigner3j
Numeric wigner3j(const Rational j1, const Rational j2, const Rational j3, const Rational m1, const Rational m2, const Rational m3)
Wigner 3J symbol.
Definition: wigner_functions.cc:40
Absorption::ReadFromArtscat5Stream
SingleLineExternal ReadFromArtscat5Stream(istream &is)
Read from ARTSCAT-5.
Definition: absorptionlines.cc:513
Absorption::Lines::ShapeParameter_dInternal
Numeric ShapeParameter_dInternal(size_t k, Numeric T, Numeric P, const Vector &vmrs, const RetrievalQuantity &derivative) const noexcept
Line shape parameter internal derivative.
Definition: absorptionlines.cc:131
QuantumIdentifier::ALL
@ ALL
Definition: quantum.h:393
sqrt
Numeric sqrt(const Rational r)
Square root.
Definition: rational.h:620
extract
void extract(T &x, String &line, Index n)
Extract something from the beginning of a string.
Definition: mystring.h:297
Absorption::Lines::SpeciesMass
Numeric SpeciesMass() const noexcept
Mass of the molecule.
Definition: absorptionlines.cc:2725
Absorption::Lines::RemoveLocalQuantum
void RemoveLocalQuantum(size_t)
Remove quantum numbers at the given position from all lines.
Definition: absorptionlines.cc:2669
Array
This can be used to make arrays out of anything.
Definition: array.h:108
pow
Numeric pow(const Rational base, Numeric exp)
Power of.
Definition: rational.h:628
SpeciesTag
A tag group can consist of the sum of several of these.
Definition: abs_species_tags.h:44
QuantumIdentifier::NONE
@ NONE
Definition: quantum.h:393
Absorption::split_list_of_external_lines
std::vector< Lines > split_list_of_external_lines(std::vector< SingleLineExternal > &external_lines, const std::vector< QuantumNumberType > &localquantas={}, const std::vector< QuantumNumberType > &globalquantas={})
Splits a list of lines into proper Lines.
Definition: absorptionlines.cc:2435
Absorption::nelem
Index nelem(const Lines &l)
Number of lines.
Definition: absorptionlines.h:1820
LineShape::Model
Main line shape model class.
Definition: lineshapemodel.h:972
IsValidQuantumNumberName
bool IsValidQuantumNumberName(String name)
Check for valid quantum number name.
Definition: quantum.cc:164
Absorption::ReadFromArtscat3Stream
SingleLineExternal ReadFromArtscat3Stream(istream &is)
Read from ARTSCAT-3.
Definition: absorptionlines.cc:155
Absorption::ReadFromLBLRTMStream
SingleLineExternal ReadFromLBLRTMStream(istream &is)
Read from LBLRTM.
Definition: absorptionlines.cc:1922
TORR2PA
const Numeric TORR2PA
Global constant, converts torr to Pa.
my_basic_string< char >
Absorption::ReadFromHitranOnlineStream
SingleLineExternal ReadFromHitranOnlineStream(istream &is)
Read from HITRAN online.
Definition: absorptionlines.cc:1171
Absorption::mirroringtype2metadatastring
String mirroringtype2metadatastring(MirroringType in)
Definition: absorptionlines.h:81
quantum_parser_hitran.h
Parser for quantum numbers from HITRAN 2004 and later.
even
constexpr bool even(const Rational r)
Returns true if even integer.
Definition: rational.h:762
quantumnumbertype2string
String quantumnumbertype2string(QuantumNumberType s)
Definition: quantum.h:162
Absorption::Lines::ShapeParameters_dVMR
LineShape::Output ShapeParameters_dVMR(size_t k, Numeric T, Numeric P, const QuantumIdentifier &vmr_qid) const noexcept
Line shape parameters vmr derivative.
Definition: absorptionlines.cc:102
ConstVectorView::nelem
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:51
double_imanip
Input manipulator class for doubles to enable nan and inf parsing.
Definition: file.h:117
Absorption::SingleLine::E0
Numeric E0() const noexcept
Lower level energy.
Definition: absorptionlines.h:345
ARTS::Group::Rational
Rational Rational
Definition: autoarts.h:96
Absorption::Lines::ShapeParameters
LineShape::Output ShapeParameters(size_t k, Numeric T, Numeric P, const Vector &vmrs) const noexcept
Line shape parameters.
Definition: absorptionlines.cc:68
split_quantum_numbers_from_hitran_online
std::vector< std::array< String, 2 > > split_quantum_numbers_from_hitran_online(String &qns)
Definition: absorptionlines.cc:1149
Numeric
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
Absorption::line_upper_in_id
bool line_upper_in_id(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line's ID.
Definition: absorptionlines.cc:2786
linalg::var
void var(VectorView var, const Vector &y, const ArrayOfVector &ys, const Index start=0, const Index end=-1)
Compute the variance of the ranged ys.
Definition: raw.cc:49
global_data.h
Absorption::normalizationtype2metadatastring
String normalizationtype2metadatastring(NormalizationType in)
Definition: absorptionlines.h:129
Absorption::cutofftype2metadatastring
String cutofftype2metadatastring(CutoffType in, Numeric cutoff)
Definition: absorptionlines.h:234
Absorption::Lines
Definition: absorptionlines.h:547
my_basic_string::trim
void trim()
Trim leading and trailing whitespace.
Definition: mystring.h:225
Absorption::Lines::Species
Index Species() const noexcept
Species Index.
Definition: absorptionlines.h:846
Absorption::Lines::OK
bool OK() const noexcept
Definition: absorptionlines.cc:3527
LineShape::from_pressurebroadeningdata
std::istream & from_pressurebroadeningdata(std::istream &data, LineShape::Type &type, bool &self, bool &bath, Model &m, ArrayOfSpeciesTag &species, const QuantumIdentifier &qid)
Legacy reading of old deprecated PressureBroadeningData class.
Definition: lineshapemodel.cc:275
LineShape::from_linemixingdata
std::istream & from_linemixingdata(std::istream &data, Model &lsc)
Legacy reading of old deprecated LineMixingData class.
Definition: lineshapemodel.cc:306
QuantumIdentifier::TRANSITION
@ TRANSITION
Definition: quantum.h:393
Absorption::SingleLine::SameQuantumNumbers
bool SameQuantumNumbers(const SingleLine &sl) const noexcept
Checks if the quantum numbers are the same of the two lines.
Definition: absorptionlines.cc:2679
max
#define max(a, b)
Definition: legacy_continua.cc:20629
Absorption::operator>>
std::istream & operator>>(std::istream &, SingleLine &)
Definition: absorptionlines.cc:2514
Absorption::Lines::SpeciesName
String SpeciesName() const noexcept
Species Name.
Definition: absorptionlines.cc:2547
LineShape::Type
Type
Type of line shape to compute.
Definition: lineshapemodel.h:788
SpeciesRecord
Contains the lookup data for one species.
Definition: absorption.h:144
my_basic_string::nelem
Index nelem() const
Number of elements.
Definition: mystring.h:246
update_id
void update_id(QuantumIdentifier &qid, const std::vector< std::array< String, 2 > > &upper_list, const std::vector< std::array< String, 2 > > &lower_list)
Updates the quantum identifier based on a lists of strings.
Definition: quantum.cc:435
Absorption::SingleLine::LowerQuantumNumbers
const std::vector< Rational > & LowerQuantumNumbers() const noexcept
Lower level quantum numbers.
Definition: absorptionlines.h:366
LineShape::Output
Main output of Model.
Definition: lineshapemodel.h:875
Absorption::Lines::ShapeParameters_dT
LineShape::Output ShapeParameters_dT(size_t k, Numeric T, Numeric P, const Vector &vmrs) const noexcept
Line shape parameters temperature derivatives.
Definition: absorptionlines.cc:76
Absorption::Lines::Isotopologue
Index Isotopologue() const noexcept
Isotopologue Index.
Definition: absorptionlines.h:849
Absorption::Lines::Line
SingleLine & Line(Index) noexcept
Returns a single line.
Definition: absorptionlines.cc:2709
N
#define N
Definition: rng.cc:164
Absorption::SingleLine::g_low
Numeric g_low() const noexcept
Lower level statistical weight.
Definition: absorptionlines.h:354
Absorption::SingleLine::LineShape
const LineShape::Model & LineShape() const noexcept
Line shape model.
Definition: absorptionlines.h:363
Isotopologue
QuantumIdentifier::QType Isotopologue
Definition: arts_api_classes.cc:242
LineShape::ModelMetaDataArray
ArrayOfString ModelMetaDataArray(const Model &m, const bool self, const bool bath, const ArrayOfSpeciesTag &sts, const Numeric T0)
Definition: lineshapemodel.cc:641
SpeciesRecord::Isotopologue
const Array< IsotopologueRecord > & Isotopologue() const
Definition: absorption.h:199
Absorption::Lines::LineShapePos
Index LineShapePos(const Index &spec) const noexcept
Position among broadening species or -1.
Definition: absorptionlines.cc:84
Absorption::Lines::PopLine
SingleLine PopLine(Index) noexcept
Pops a single line.
Definition: absorptionlines.cc:2693
Absorption::ReadFromArtscat4Stream
SingleLineExternal ReadFromArtscat4Stream(istream &is)
Read from ARTSCAT-4.
Definition: absorptionlines.cc:317
q
#define q
Definition: legacy_continua.cc:21712
wavenumber_to_joule
Numeric wavenumber_to_joule(Numeric e)
A little helper function to convert energy from units of wavenumber (cm^-1) to Joule (J).
Definition: absorption.cc:500
Absorption::ReadFromHitran2001Stream
SingleLineExternal ReadFromHitran2001Stream(istream &is)
Read from HITRAN before 2004.
Definition: absorptionlines.cc:1560
constants.h
Constants of physical expressions as constexpr.
Absorption::string2normalizationtype
NormalizationType string2normalizationtype(const String &in)
Definition: absorptionlines.h:104
LineShape::Model::Data
const std::vector< SingleSpeciesModel > & Data() const noexcept
The line shape model data.
Definition: lineshapemodel.h:1345
Absorption::ReadFromMytran2Stream
SingleLineExternal ReadFromMytran2Stream(istream &is)
Read from Mytran2 The MYTRAN2 format is as follows (directly taken from the abs_my....
Definition: absorptionlines.cc:2953
SpeciesRecord::Name
const String & Name() const
Definition: absorption.h:197
Absorption::populationtype2metadatastring
String populationtype2metadatastring(PopulationType in)
Definition: absorptionlines.h:186
Absorption::Lines::LowerQuantumNumbers
String LowerQuantumNumbers() const noexcept
Lower quantum numbers string.
Definition: absorptionlines.cc:2570
QuantumParserHITRAN2004::Parse
void Parse(QuantumIdentifier &qid, const String &quantum_string) const
Parse quantum numbers from string.
Definition: quantum_parser_hitran.cc:410
Absorption::line_in_id
bool line_in_id(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line's ID.
Definition: absorptionlines.cc:2749
ThrowIfQuantumNumberNameInvalid
void ThrowIfQuantumNumberNameInvalid(String name)
Check for valid quantum number name and throws if it is invalid.
Definition: quantum.cc:168
SPEED_OF_LIGHT
const Numeric SPEED_OF_LIGHT
file.h
This file contains basic functions to handle ASCII files.
Absorption::createEmptyCopy
Lines createEmptyCopy(const Lines &al) noexcept
Creates a copy of the input lines structure.
Definition: absorptionlines.cc:2701
Absorption::Lines::RemoveLine
void RemoveLine(Index) noexcept
Removes a single line.
Definition: absorptionlines.cc:2687
ARTS::Var::x
Vector x(Workspace &ws) noexcept
Definition: autoarts.h:7346
Index
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
RetrievalQuantity
Deals with internal derivatives, Jacobian definition, and OEM calculations.
Definition: jacobian.h:120
Absorption::Lines::MetaData
String MetaData() const
Returns a printable statement about the lines.
Definition: absorptionlines.cc:2580
Absorption::line_lower_in_id
bool line_lower_in_id(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line's ID.
Definition: absorptionlines.cc:2813
LineShape::differenceOutput
constexpr Output differenceOutput(Output y, Output x) noexcept
Diff of two output.
Definition: lineshapemodel.h:919
Absorption::Lines::RemoveUnusedLocalQuantums
void RemoveUnusedLocalQuantums()
Remove quantum numbers that are not used by even a single line.
Definition: absorptionlines.cc:2643
Absorption::reduced_magnetic_quadrapole
Numeric reduced_magnetic_quadrapole(Rational Jf, Rational Ji, Rational N)
Compute the reduced magnetic quadrapole moment.
Definition: absorptionlines.cc:2946
Absorption::Lines::UpperQuantumNumber
Rational UpperQuantumNumber(size_t k, QuantumNumberType qnt) const noexcept
Quantum number upper level.
Definition: absorptionlines.cc:47
Absorption::SingleLineExternal
Single line reading output.
Definition: absorptionlines.h:530
Vector
The Vector class.
Definition: matpackI.h:860
ConstVectorView
A constant view of a Vector.
Definition: matpackI.h:476
Absorption::ReadFromHitran2004Stream
SingleLineExternal ReadFromHitran2004Stream(istream &is)
Read from newer HITRAN.
Definition: absorptionlines.cc:752
SpeciesTag::Species
Index Species() const
Molecular species index.
Definition: abs_species_tags.h:64
Array::nelem
Index nelem() const
Number of elements.
Definition: array.h:195
Absorption::line_is_id
bool line_is_id(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier is equal to a line's identifier.
Definition: absorptionlines.cc:2931
Rational
Implements rational numbers to work with other ARTS types.
Definition: rational.h:54
Absorption::string2mirroringtype
MirroringType string2mirroringtype(const String &in)
Definition: absorptionlines.h:56
Absorption::SingleLine::Zeeman
Zeeman::Model Zeeman() const noexcept
Zeeman model.
Definition: absorptionlines.h:360
LineShape::vmrs
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's main calculations.
Definition: lineshapemodel.cc:474
QuantumNumberType::J
@ J