ARTS  2.3.1285(git:92a29ea9-dirty)
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;
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;
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.
448  Rational(atoi(mquantum_numbers_str.substr(0, 3).c_str())));
451  Rational(atoi(mquantum_numbers_str.substr(6, 3).c_str())));
454  Rational(atoi(mquantum_numbers_str.substr(3, 3).c_str())));
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) {
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)
494  if (q[1] > -1)
496  if (q[2] > -1)
498  if (q[3] > -1)
500  if (q[4] > -1)
502  if (q[5] > -1)
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") {
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 
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;
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;
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;
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;
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 
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';
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(),
2630  mbathbroadening,
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(),
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;
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;
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 }
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.
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
std::vector< SingleLine > mlines
A list of individual lines.
#define N
Definition: rng.cc:164
std::ostream & operator<<(std::ostream &os, const ArrayOfAbsorptionLines &aol)
const QuantumNumbers & UpperQuantumNumbers() const noexcept
Return the upper quantum numbers by const reference.
Definition: quantum.h:587
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
Computations and data for a single absorption line.
Vector vmrs(const ConstVectorView &atmospheric_vmrs, const ArrayOfArrayOfSpeciesTag &atmospheric_species, const QuantumIdentifier &self, const ArrayOfSpeciesTag &lineshape_species, bool self_in_list, bool bath_in_list, Type type)
Returns a VMR vector for this model&#39;s main calculations.
std::istream & from_linefunctiondata(std::istream &data, Model &lsc)
LineShape::Type mlineshapetype
Type of line shape.
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
SingleLineExternal ReadFromHitran2001Stream(istream &is)
Read from HITRAN before 2004.
const ArrayOfSpeciesTag & BroadeningSpecies() const noexcept
Returns the broadening species.
String LowerQuantumNumbers() const noexcept
Lower quantum numbers string.
Index nelem() const
Number of elements.
Definition: array.h:195
void Isotopologue(Index iso)
Set the Isotopologue.
Definition: quantum.h:487
QuantumIdentifier::QType Index LowerQuantumNumbers Species
SingleLineExternal ReadFromArtscat3Stream(istream &is)
Read from ARTSCAT-3.
Main output of Model.
bool SameQuantumNumbers(const SingleLine &sl) const noexcept
Checks if the quantum numbers are the same of the two lines.
Numeric F0() const noexcept
Central frequency.
MirroringType mmirroring
Mirroring type.
SingleLineExternal ReadFromMytran2Stream(istream &is)
Read from Mytran2 The MYTRAN2 format is as follows (directly taken from the abs_my.c documentation):
Rational LowerQuantumNumber(size_t k, QuantumNumberType qnt) const noexcept
Quantum number lower level.
SingleLineExternal ReadFromArtscat4Stream(istream &is)
Read from ARTSCAT-4.
QuantumIdentifier mquantumidentity
Catalog ID.
The Vector class.
Definition: matpackI.h:860
void ReverseLines() noexcept
Reverses the order of the internal lines.
Index Isotopologue() const noexcept
Isotopologue Index.
String UpperQuantumNumbers() const noexcept
Upper quantum numbers string.
bool line_lower_in_id(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line&#39;s ID.
Numeric reduced_magnetic_quadrapole(Rational Jf, Rational Ji, Rational N)
Compute the reduced magnetic quadrapole moment.
Constants of physical expressions as constexpr.
Main line shape model class.
MirroringType string2mirroringtype(const String &in)
Numeric SpeciesMass() const noexcept
Mass of the molecule.
void RemoveLocalQuantum(size_t)
Remove quantum numbers at the given position from all lines.
Index NumLines() const noexcept
Number of lines.
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&#39;s identifier.
This file contains basic functions to handle ASCII files.
Contains the absorption namespace.
String shapetype2metadatastring(Type type) noexcept
Turns selected Type into a human readable string.
G0 G2 FVC Y DV Numeric Numeric Numeric Zeeman LowerQuantumNumbers void * data
const Numeric TORR2PA
Global constant, converts torr to Pa.
const std::vector< SingleSpeciesModel > & Data() const noexcept
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:51
QuantumNumberType
Enum for Quantum Numbers used for indexing.
Definition: quantum.h:48
basic_stringstream< char, string_char_traits< char >, alloc > stringstream
Definition: sstream.h:205
bool Self() const noexcept
Returns self broadening status.
PopulationType mpopulation
Line population distribution.
const Array< SpeciesRecord > species_data
Species Data.
SingleLine & Line(Index) noexcept
Returns a single line.
SingleLineExternal ReadFromArtscat5Stream(istream &is)
Read from ARTSCAT-5.
void RemoveLine(Index) noexcept
Removes a single line.
String quantumnumbertype2string(QuantumNumberType s)
Definition: quantum.h:162
Deals with internal derivatives, Jacobian definition, and OEM calculations.
Definition: jacobian.h:120
LineShape::Output ShapeParameters_dT(size_t k, Numeric T, Numeric P, const Vector &vmrs) const noexcept
Line shape parameters temperature derivatives.
Rational UpperQuantumNumber(size_t k, QuantumNumberType qnt) const noexcept
Quantum number upper level.
const Array< IsotopologueRecord > & Isotopologue() const
Definition: absorption.h:199
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.
std::istream & operator>>(std::istream &, SingleLine &)
bool line_in_id(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line&#39;s ID.
std::istream & from_artscat4(std::istream &is, Model &lsc, const QuantumIdentifier &qid)
Parser for quantum numbers from HITRAN 2004 and later.
Numeric mcutofffreq
cutoff frequency
bool line_upper_in_id(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line&#39;s ID.
void ThrowIfQuantumNumberNameInvalid(String name)
Check for valid quantum number name and throws if it is invalid.
Definition: quantum.cc:168
_CS_string_type str() const
Definition: sstream.h:491
Numeric reduced_rovibrational_dipole(Rational Jf, Rational Ji, Rational lf, Rational li, Rational k=Rational(1))
Compute the reduced rovibrational dipole moment.
SingleLineExternal ReadFromHitranOnlineStream(istream &is)
Read from HITRAN online.
Numeric wigner6j(const Rational j1, const Rational j2, const Rational j3, const Rational l1, const Rational l2, const Rational l3)
Wigner 6J symbol.
void Species(Index sp)
Set the Species.
Definition: quantum.h:481
CutoffType mcutoff
cutoff type, by band or by line
Implements rational numbers to work with other ARTS types.
Definition: rational.h:54
bool Bath() const noexcept
Returns bath broadening status.
bool id_in_line_upper(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line&#39;s ID.
Contains the lookup data for one species.
Definition: absorption.h:144
QuantumIdentifier::QType Isotopologue
Index Species() const noexcept
Species Index.
A tag group can consist of the sum of several of these.
SingleLineExternal ReadFromLBLRTMStream(istream &is)
Read from LBLRTM.
Vector BroadeningSpeciesVMR(const ConstVectorView, const ArrayOfArrayOfSpeciesTag &) const
Returns the VMRs of the broadening species.
Numeric g_low() const noexcept
Lower level statistical weight.
SingleLineExternal ReadFromJplStream(istream &is)
Read from JPL.
void extract(T &x, String &line, Index n)
Extract something from the beginning of a string.
Definition: mystring.h:297
Class to identify and match lines by their quantum numbers.
Definition: quantum.h:390
void RemoveUnusedLocalQuantums()
Remove quantum numbers that are not used by even a single line.
Numeric E0() const noexcept
Lower level energy.
bool mbathbroadening
Does the line broadening have bath broadening.
void SetLineMixingModel(SingleSpeciesModel x)
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
ArrayOfSpeciesTag mbroadeningspecies
A list of broadening species.
Numeric A() const noexcept
Einstein spontaneous emission.
Index nelem() const
Number of elements.
Definition: mystring.h:246
const String & Name() const
Definition: absorption.h:197
Declarations required for the calculation of absorption coefficients.
Numeric mlinemixinglimit
linemixing limit
ArrayOfString ModelMetaDataArray(const Model &m, const bool self, const bool bath, const ArrayOfSpeciesTag &sts, const Numeric T0)
Numeric pow(const Rational base, Numeric exp)
Power of.
Definition: rational.h:628
std::ostream & operator<<(std::ostream &, const SingleLine &)
Numeric mT0
Reference temperature for all parameters of the lines.
std::istream & from_linemixingdata(std::istream &data, Model &lsc)
Legacy reading of old deprecated LineMixingData class.
LineShape::Output ShapeParameters(size_t k, Numeric T, Numeric P, const Vector &vmrs) const noexcept
Line shape parameters.
bool OK() const noexcept
void Set(Index qn, Rational r)
Set quantum number at position.
Definition: quantum.h:310
basic_ostringstream< char, string_char_traits< char >, alloc > ostringstream
Definition: sstream.h:204
This can be used to make arrays out of anything.
Definition: array.h:40
String cutofftype2metadatastring(CutoffType in, Numeric cutoff)
bool mselfbroadening
Does the line broadening have self broadening.
const QuantumNumbers & LowerQuantumNumbers() const noexcept
Return the lower quantum numbers by const reference.
Definition: quantum.h:592
constexpr Output differenceOutput(Output y, Output x) noexcept
Diff of two output.
NormalizationType string2normalizationtype(const String &in)
Parser for quantum number strings from HITRAN 2004 and later.
bool id_in_line(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line&#39;s ID.
void Parse(QuantumIdentifier &qid, const String &quantum_string) const
Parse quantum numbers from string.
#define max(a, b)
const std::vector< Rational > & UpperQuantumNumbers() const noexcept
Upper level quantum numbers.
Numeric wigner3j(const Rational j1, const Rational j2, const Rational j3, const Rational m1, const Rational m2, const Rational m3)
Wigner 3J symbol.
std::vector< QuantumNumberType > mlocalquanta
List of local quantum numbers, these must be defined.
A constant view of a Vector.
Definition: matpackI.h:476
const std::vector< Rational > & LowerQuantumNumbers() const noexcept
Lower level quantum numbers.
SingleLine PopLine(Index) noexcept
Pops a single line.
std::vector< std::array< String, 2 > > split_quantum_numbers_from_hitran_online(String &qns)
std::istream & from_pressurebroadeningdata(std::istream &data, Model &lsc, const QuantumIdentifier &qid)
Index nelem(const Lines &l)
Number of lines.
Lines createEmptyCopy(const Lines &al) noexcept
Creates a copy of the input lines structure.
const LineShape::Model & LineShape() const noexcept
Line shape model.
LineShape::Output ShapeParameters_dVMR(size_t k, Numeric T, Numeric P, const QuantumIdentifier &vmr_qid) const noexcept
Line shape parameters vmr derivative.
Index LineShapePos(const Index &spec) const noexcept
Position among broadening species or -1.
Single line reading output.
bool IsValidQuantumNumberName(String name)
Check for valid quantum number name.
Definition: quantum.cc:164
#define q
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
Numeric ShapeParameter_dInternal(size_t k, Numeric T, Numeric P, const Vector &vmrs, const RetrievalQuantity &derivative) const noexcept
Line shape parameter internal derivative.
const QuantumIdentifier & QuantumIdentity() const noexcept
Returns identity status.
Zeeman::Model Zeeman() const noexcept
Zeeman model.
String populationtype2metadatastring(PopulationType in)
String normalizationtype2metadatastring(NormalizationType in)
SingleLineExternal ReadFromHitran2004Stream(istream &is)
Read from newer HITRAN.
constexpr bool even(const Rational r)
Returns true if even integer.
Definition: rational.h:762
Numeric SelfVMR(const ConstVectorView, const ArrayOfArrayOfSpeciesTag &) const
Returns the VMR of the species.
const Numeric SPEED_OF_LIGHT
String SpeciesName() const noexcept
Species Name.
Numeric g_upp() const noexcept
Upper level statistical weight.
LineShape::Type LineShapeType() const noexcept
Returns lineshapetype style.
String mirroringtype2metadatastring(MirroringType in)
Input manipulator class for doubles to enable nan and inf parsing.
Definition: file.h:117
NormalizationType mnormalization
Line normalization type.
Numeric sqrt(const Rational r)
Square root.
Definition: rational.h:620
Numeric I0() const noexcept
Reference line strength.
bool id_in_line_lower(const Lines &band, const QuantumIdentifier &id, size_t line_index)
Checks if the external quantum identifier match a line&#39;s ID.
String MetaData() const
Returns a printable statement about the lines.
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
const std::vector< SingleLine > & AllLines() const noexcept
Lines.