ARTS 2.5.11 (git: 725533f0)
species_tags.cc
Go to the documentation of this file.
1#include "species_tags.h"
2
3#include <algorithm>
4#include <cfloat>
5#include <fast_float/fast_float.h>
6#include <iomanip>
7#include <iterator>
8#include <string_view>
9#include <system_error>
10
11#include "debug.h"
12#include "isotopologues.h"
13#include "nonstd.h"
14#include "partfun.h"
15#include "species.h"
16
17namespace Species {
18namespace detail {
19constexpr void trim(std::string_view &text) {
20 while (text.size() and nonstd::isspace(text.front()))
21 text.remove_prefix(1);
22 while (text.size() and nonstd::isspace(text.back()))
23 text.remove_suffix(1);
24}
25
26constexpr std::string_view next(std::string_view &text) {
27 std::string_view next = text.substr(
28 0, text.find_first_of('-', text.size() > 0 and text.front() == '-'));
29 text.remove_prefix(std::min(text.size(), next.size()+1));
30 trim(next);
31 trim(text);
32 return next;
33}
34
35constexpr std::string_view next_tag(std::string_view &text) {
36 std::string_view next = text.substr(0, text.find_first_of(','));
37 text.remove_prefix(std::min(text.size(), next.size()+1));
38 trim(next);
39 trim(text);
40 return next;
41}
42
43constexpr Species spec(std::string_view part,
44 std::string_view orig [[maybe_unused]]) {
45 Species s = fromShortName(part);
46 ARTS_USER_ERROR_IF(not good_enum(s), "The species extraction from ",
47 std::quoted(part),
48 " is not good. "
49 "The original tag reads ",
50 std::quoted(orig))
51 return s;
52}
53
54constexpr Index isot(Species species, std::string_view isot,
55 std::string_view orig [[maybe_unused]]) {
56 Index spec_ind = find_species_index(species, isot);
57 ARTS_USER_ERROR_IF(spec_ind < 0, "Bad isotopologue: ", std::quoted(isot),
58 "\nValid options are:\n",
59 isotopologues_names(species),
60 "\nThe original tag reads ", std::quoted(orig))
61 return spec_ind;
62}
63
64constexpr Numeric freq(std::string_view part,
65 std::string_view orig [[maybe_unused]]) {
66 Numeric f;
67 if (part.size() == 1 and part.front() == '*')
68 f = -1;
69 else {
70 auto err =
71 fast_float::from_chars(part.data(), part.data() + part.size(), f);
72 ARTS_USER_ERROR_IF(err.ec == std::errc::invalid_argument,
73 "Invalid argument: ", std::quoted(part),
74 "\nThe original tag reads ", std::quoted(orig));
75 ARTS_USER_ERROR_IF(err.ec == std::errc::result_out_of_range,
76 "Out-of-range: ", std::quoted(part),
77 "\nThe original tag reads ", std::quoted(orig));
78 }
79 return f;
80}
81
82constexpr void check(std::string_view text, std::string_view orig) {
83 ARTS_USER_ERROR_IF(text.size(), "Parsing error. The text ",
84 std::quoted(text),
85 " remains to be parsed at end of a complete species tag\n"
86 "The original tag reads ", std::quoted(orig))
87}
88} // namespace detail
89
90SpeciesTag parse_tag(std::string_view text) {
91 using namespace detail;
92 trim(text);
93
94 const std::string_view orig = text;
95 SpeciesTag tag;
96
97 // The first part is a species --- we do not know what to do with it yet
98 const Species species = spec(next(text), orig);
99
100 // Check if species name contains the special tag for
101 // Faraday Rotation
102 if (species == Species::free_electrons) {
103 constexpr Index ind =
104 find_species_index(IsotopeRecord(Species::free_electrons));
105 tag.spec_ind = ind;
106 tag.type = TagType::FreeElectrons;
107 }
108
109 // Check if species name contains the special tag for
110 // Particles
111 if (species == Species::particles) {
112 constexpr Index ind = find_species_index(IsotopeRecord(Species::particles));
113 tag.spec_ind = ind;
114 tag.type = TagType::Particles;
115 }
116
117 // If there is no text remaining after the previous next(), then we are a
118 // wild-tag species. Otherwise we have to process the tag a bit more
119 if (text.size() == 0) {
120 if (tag.type == TagType::FINAL) {
121 tag.spec_ind = isot(species, Joker, orig);
122 tag.type = TagType::Plain;
123 }
124 check(text, orig);
125 } else {
126 if (const std::string_view tag_key = next(text); tag_key.front() == 'Z') {
127 tag.spec_ind = isot(species, next(text), orig);
128 tag.type = TagType::Zeeman;
130 is_predefined_model(Isotopologues[tag.spec_ind]),
131 "Bad Zeeman tag using predefined model in tag: ", std::quoted(orig))
132 } else if (tag_key == "CIA") {
133 tag.spec_ind = isot(species, Joker, orig);
134 tag.cia_2nd_species = spec(next(text), orig);
135 tag.type = TagType::Cia;
136 check(text, orig);
137 } else if (tag_key == "XFIT") {
138 tag.spec_ind = isot(species, Joker, orig);
139 tag.type = TagType::XsecFit;
140 check(text, orig);
141 } else {
142 tag.spec_ind = isot(species, tag_key, orig);
143 tag.type = is_predefined_model(Isotopologues[tag.spec_ind])
144 ? TagType::Predefined
145 : TagType::Plain;
146 }
147 }
148
149 if (text.size()) {
150 tag.lower_freq = freq(next(text), orig);
151 tag.upper_freq = freq(next(text), orig);
152 ARTS_USER_ERROR_IF(tag.upper_freq >= 0 and tag.lower_freq >= 0 and
153 tag.upper_freq <= tag.lower_freq,
154 "Invalid frequency range [", tag.lower_freq, ", ",
155 tag.upper_freq, "]\nOriginal tag: ", std::quoted(orig))
156 check(text, orig);
157 }
158
159 return tag;
160}
161
162Array<Tag> parse_tags(std::string_view text) {
163 using namespace detail;
164 trim(text);
165
166 Array<Tag> tags;
167 while (text.size()) {
168 tags.emplace_back(parse_tag(next_tag(text)));
169 }
170
171 return tags;
172}
173
174Numeric Tag::Q(Numeric T) const {
175 return PartitionFunctions::Q(T, Isotopologue());
176}
177
178Numeric Tag::dQdT(Numeric T) const {
179 return PartitionFunctions::dQdT(T, Isotopologue());
180}
181
182Tag::Tag(std::string_view text) : Tag(parse_tag(text)) {}
183
185
197String Tag::Name() const {
198 std::ostringstream os;
199
200 // First the species name:
201 os << toShortName(Isotopologue().spec) << "-";
202
203 // Is this a CIA tag?
204 if (type == TagType::Cia) {
205 os << "CIA-" << toShortName(cia_2nd_species);
206
207 } else if (type == TagType::FreeElectrons || type == TagType::Particles) {
208 os << toShortName(Isotopologue().spec);
209 }
210 // Hitran Xsec flag.
211 else if (type == TagType::XsecFit) {
212 os << "XFIT";
213 } else {
214 // Zeeman flag.
215 if (type == TagType::Zeeman) os << "Z-";
216
217 // Now the isotopologue. Can be a single isotopologue or ALL.
218 os << Isotopologue().isotname << '-';
219
220 // Now the frequency limits, if there are any. For this we first
221 // need to determine the floating point precision.
222
223 // Determine the precision, depending on whether Numeric is double
224 // or float:
225 constexpr int precision = std::same_as<Numeric, double> ? DBL_DIG : FLT_DIG;
226
227 if (0 > lower_freq) {
228 // lower_freq < 0 means no lower limit.
229 os << Joker << '-';
230 } else {
231 os << std::setprecision(precision);
232 os << lower_freq << "-";
233 }
234
235 if (0 > upper_freq) {
236 // upper_freq < 0 means no upper limit.
237 os << Joker;
238 } else {
239 os << std::setprecision(precision);
240 os << upper_freq;
241 }
242 }
243 return os.str();
244}
245} // namespace Species
246
248 : ArrayOfSpeciesTag(Species::parse_tags(text)) {
250 size() and
251 std::any_of(begin(), end(),
252 [front_species = front().Spec()](const SpeciesTag &tag) {
253 return tag.Spec() not_eq front_species;
254 }),
255 "All species in a group of tags must be the same\n"
256 "Your list of tags have been parsed as: ",
257 *this, "\nThe original tags-list read ", std::quoted(text))
258
260 size() and
261 std::any_of(
262 begin(), end(),
263 [front_is_zeeman = front().type == Species::TagType::Zeeman](
264 const SpeciesTag &tag) {
265 return front_is_zeeman ? tag.type != Species::TagType::Zeeman
266 : tag.type == Species::TagType::Zeeman;
267 }),
268 "All species in a group of tags must be Zeeman-tagged if any one is\n"
269 "Your list of tags have been parsed as: ",
270 *this, "\nThe original tags-list read ", std::quoted(text)) {}
271}
272
274 Species::Species spec,
275 Index i) noexcept {
276 const Index n = specs.nelem();
277 for (; i < n; i++)
278 if (specs[i].Species() == spec) return i;
279 return -1;
280}
281
283 Species::Species spec) noexcept {
284 return find_next_species(specs, spec, 0);
285}
286
287std::pair<Index, Index> find_first_species_tag(
288 const ArrayOfArrayOfSpeciesTag& specs, const SpeciesTag& tag) noexcept {
289 for (Index i = 0; i < specs.nelem(); i++) {
290 if (auto ptr = std::find(specs[i].cbegin(), specs[i].cend(), tag);
291 ptr not_eq specs[i].cend())
292 return {i, std::distance(specs[i].cbegin(), ptr)};
293 }
294 return {-1, -1};
295}
296
297std::pair<Index, Index> find_first_isotologue(
298 const ArrayOfArrayOfSpeciesTag& specs,
299 const SpeciesIsotopeRecord& isot) noexcept {
300 for (Index i = 0; i < specs.nelem(); i++) {
301 if (auto ptr =
302 std::find_if(specs[i].cbegin(),
303 specs[i].cend(),
304 [&](auto& tag) { return tag.Isotopologue() == isot; });
305 ptr not_eq specs[i].cend())
306 return {i, std::distance(specs[i].cbegin(), ptr)};
307 }
308 return {-1, -1};
309}
310
312 Index num_free_electrons = 0;
313 for (Index i = 0; i < abs_species.nelem(); ++i) {
314 bool has_free_electrons = false;
315 bool has_particles = false;
316 bool has_hitran_xsec = false;
317 for (Index s = 0; s < abs_species[i].nelem(); ++s) {
318 if (abs_species[i][s].Type() == Species::TagType::FreeElectrons) {
319 num_free_electrons++;
320 has_free_electrons = true;
321 }
322
323 if (abs_species[i][s].Type() == Species::TagType::Particles) {
324 has_particles = true;
325 }
326
327 if (abs_species[i][s].Type() == Species::TagType::XsecFit) {
328 has_hitran_xsec = true;
329 }
330 }
331
332 ARTS_USER_ERROR_IF(abs_species[i].nelem() > 1 && has_free_electrons,
333 "'free_electrons' must not be combined "
334 "with other tags in the same group.");
335 ARTS_USER_ERROR_IF(num_free_electrons > 1,
336 "'free_electrons' must not be defined "
337 "more than once.");
338
339 ARTS_USER_ERROR_IF(abs_species[i].nelem() > 1 && has_particles,
340 "'particles' must not be combined "
341 "with other tags in the same group.");
342
343 ARTS_USER_ERROR_IF(abs_species[i].nelem() > 1 && has_hitran_xsec,
344 "'hitran_xsec' must not be combined "
345 "with other tags in the same group.");
346 }
347}
348
349String ArrayOfSpeciesTag::Name() const {
350 String out = "";
351 bool first = true;
352 for (auto& x : *this) {
353 if (not first) out += ", ";
354 out += x.Name();
355 first = false;
356 }
357 return out;
358}
359
360std::set<Species::Species> lbl_species(
361 const ArrayOfArrayOfSpeciesTag& abs_species) noexcept {
362 std::set<Species::Species> unique_species;
363 for (auto& specs : abs_species) {
364 if (specs.RequireLines()) unique_species.insert(specs.front().Spec());
365 }
366 return unique_species;
367}
368
370 const Vector& rtp_vmr,
371 const Species spec) ARTS_NOEXCEPT {
372 ARTS_ASSERT(abs_species.nelem() == rtp_vmr.nelem())
373
374 auto pos =
375 std::find_if(abs_species.begin(),
376 abs_species.end(),
377 [spec](const ArrayOfSpeciesTag& tag_group) {
378 return tag_group.nelem() and tag_group.Species() == spec;
379 });
380 return pos == abs_species.end()
381 ? 0
382 : rtp_vmr[std::distance(abs_species.begin(), pos)];
383}
384
386 for (auto& species_list : abs_species) {
387 for (auto& tag : species_list) {
388 switch (tag.type) {
389 case Species::TagType::Plain:
390 Plain = true;
391 break;
392 case Species::TagType::Zeeman:
393 Zeeman = true;
394 break;
395 case Species::TagType::Predefined:
396 Predefined = true;
397 break;
398 case Species::TagType::Cia:
399 Cia = true;
400 break;
401 case Species::TagType::FreeElectrons:
402 FreeElectrons = true;
403 break;
404 case Species::TagType::Particles:
405 Particles = true;
406 break;
407 case Species::TagType::XsecFit:
408 XsecFit = true;
409 break;
410 case Species::TagType::FINAL: { /* leave last */
411 }
412 }
413 }
414 }
415}
416
417std::ostream& operator<<(std::ostream& os, SpeciesTagTypeStatus val) {
418 Species::TagType x{Species::TagType::FINAL};
419 switch (x) {
420 case Species::TagType::FINAL:
421 os << "Species tag types:\n";
422 [[fallthrough]];
423 case Species::TagType::Plain:
424 os << " Plain: " << val.Plain << '\n';
425 [[fallthrough]];
426 case Species::TagType::Zeeman:
427 os << " Zeeman: " << val.Zeeman << '\n';
428 [[fallthrough]];
429 case Species::TagType::Predefined:
430 os << " Predefined: " << val.Predefined << '\n';
431 [[fallthrough]];
432 case Species::TagType::Cia:
433 os << " Cia: " << val.Cia << '\n';
434 [[fallthrough]];
435 case Species::TagType::FreeElectrons:
436 os << " FreeElectrons: " << val.FreeElectrons << '\n';
437 [[fallthrough]];
438 case Species::TagType::Particles:
439 os << " Particles: " << val.Particles << '\n';
440 [[fallthrough]];
441 case Species::TagType::XsecFit:
442 os << " XsecFit: " << val.XsecFit << '\n';
443 }
444 return os;
445}
ArrayOfSpeciesTag() noexcept
This can be used to make arrays out of anything.
Definition array.h:31
Index nelem() const ARTS_NOEXCEPT
Definition array.h:75
Helper macros for debugging.
#define ARTS_NOEXCEPT
Definition debug.h:83
#define ARTS_ASSERT(condition,...)
Definition debug.h:86
#define ARTS_USER_ERROR_IF(condition,...)
Definition debug.h:137
constexpr bool good_enum(EnumType x) noexcept
Checks if the enum number is good.
Definition enums.h:21
Numeric dQdT(Numeric T, const Species::IsotopeRecord &ir)
Definition partfun.cc:18
Numeric Q(Numeric T, const Species::IsotopeRecord &ir)
Definition partfun.cc:14
constexpr Index isot(Species species, std::string_view isot, std::string_view orig)
constexpr void check(std::string_view text, std::string_view orig)
constexpr std::string_view next_tag(std::string_view &text)
constexpr void trim(std::string_view &text)
constexpr std::string_view next(std::string_view &text)
constexpr Numeric freq(std::string_view part, std::string_view orig)
constexpr Species spec(std::string_view part, std::string_view orig)
String isotopologues_names(Species spec)
Array< Tag > parse_tags(std::string_view text)
Parse a list of species tags into an Array<Tag>
constexpr bool is_predefined_model(const IsotopeRecord &ir) noexcept
constexpr std::array Isotopologues
A list of all ARTS isotopologues, note how the species enum class input HAS to be sorted.
Numeric first_vmr(const ArrayOfArrayOfSpeciesTag &abs_species, const Vector &rtp_vmr, const Species spec) ARTS_NOEXCEPT
constexpr Index find_species_index(const Species spec, const std::string_view isot) noexcept
constexpr std::string_view Joker
SpeciesTag parse_tag(std::string_view text)
Implements Zeeman modeling.
constexpr int isspace(int ch) noexcept
Returns 1 if x is a standard space-character.
Definition nonstd.h:39
Index find_first_species(const ArrayOfArrayOfSpeciesTag &specs, Species::Species spec) noexcept
Index find_next_species(const ArrayOfArrayOfSpeciesTag &specs, Species::Species spec, Index i) noexcept
std::pair< Index, Index > find_first_isotologue(const ArrayOfArrayOfSpeciesTag &specs, const SpeciesIsotopeRecord &isot) noexcept
std::set< Species::Species > lbl_species(const ArrayOfArrayOfSpeciesTag &abs_species) noexcept
void check_abs_species(const ArrayOfArrayOfSpeciesTag &abs_species)
std::pair< Index, Index > find_first_species_tag(const ArrayOfArrayOfSpeciesTag &specs, const SpeciesTag &tag) noexcept
std::ostream & operator<<(std::ostream &os, SpeciesTagTypeStatus val)
Species::Tag SpeciesTag
Struct to test of an ArrayOfArrayOfSpeciesTag contains a tagtype.
SpeciesTagTypeStatus(const ArrayOfArrayOfSpeciesTag &abs_species)
Struct containing all information needed about one isotope.