ARTS  2.4.0(git:4fb77825)
gas_abs_lookup.cc
Go to the documentation of this file.
1 /* Copyright (C) 2002-2012 Stefan Buehler <sbuehler@ltu.se>
2 
3  This program is free software; you can redistribute it and/or modify it
4  under the terms of the GNU General Public License as published by the
5  Free Software Foundation; either version 2, or (at your option) any
6  later version.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16  USA. */
17 
26 #include "gas_abs_lookup.h"
27 #include <cfloat>
28 #include <cmath>
29 #include "check_input.h"
30 #include "interpolation.h"
31 #include "interpolation_poly.h"
32 #include "logic.h"
33 #include "messages.h"
34 #include "physics_funcs.h"
35 
37 
49  ConstVectorView old_grid,
50  ConstVectorView new_grid,
51  const Verbosity& verbosity) {
53 
54  const Index n_new_grid = new_grid.nelem();
55  const Index n_old_grid = old_grid.nelem();
56 
57  // Make sure that pos has the right size:
58  assert(n_new_grid == pos.nelem());
59 
60  // Old grid position:
61  Index j = 0;
62 
63  // Loop the new frequencies:
64  for (Index i = 0; i < n_new_grid; ++i) {
65  // We have done runtime checks that both the new and the old
66  // frequency grids are sorted in GasAbsLookup::Adapt, so we can
67  // use the fact here.
68 
69  while (abs(new_grid[i] - old_grid[j]) >
70  max(abs(new_grid[i]), abs(old_grid[j])) * DBL_EPSILON) {
71  ++j;
72  if (j >= n_old_grid) {
73  ostringstream os;
74  os << "Cannot find new frequency " << i << " (" << new_grid[i]
75  << "Hz) in the lookup table frequency grid.";
76  throw runtime_error(os.str());
77  }
78  }
79 
80  pos[i] = j;
81  out3 << " " << new_grid[i] << " found, index = " << pos[i] << ".\n";
82  }
83 }
84 
86 
118 void GasAbsLookup::Adapt(const ArrayOfArrayOfSpeciesTag& current_species,
119  ConstVectorView current_f_grid,
120  const Verbosity& verbosity) {
121  CREATE_OUT2;
122  CREATE_OUT3;
123 
124  // Some constants we will need:
125  const Index n_current_species = current_species.nelem();
126  const Index n_current_f_grid = current_f_grid.nelem();
127 
128  const Index n_species = species.nelem();
129  const Index n_nls = nonlinear_species.nelem();
130  const Index n_nls_pert = nls_pert.nelem();
131  const Index n_f_grid = f_grid.nelem();
132  const Index n_p_grid = p_grid.nelem();
133 
134  out2 << " Original table: " << n_species << " species, " << n_f_grid
135  << " frequencies.\n"
136  << " Adapt to: " << n_current_species << " species, "
137  << n_current_f_grid << " frequencies.\n";
138 
139  if (0 == n_nls) {
140  out2 << " Table contains no nonlinear species.\n";
141  }
142 
143  // Set up a logical array for the nonlinear species
144  ArrayOfIndex non_linear(n_species, 0);
145  for (Index s = 0; s < n_nls; ++s) {
146  non_linear[nonlinear_species[s]] = 1;
147  }
148 
149  if (0 == t_pert.nelem()) {
150  out2 << " Table contains no temperature perturbations.\n";
151  }
152 
153  // We are constructing a new lookup table, containing just the
154  // species and frequencies that are necessary for the current
155  // calculation. We will build it in this local variable, then copy
156  // it back to *this in the end.
157  GasAbsLookup new_table;
158 
159  // First some checks on the lookup table itself:
160 
161  // Species:
162  if (0 == n_species) {
163  ostringstream os;
164  os << "The lookup table should have at least one species.";
165  throw runtime_error(os.str());
166  }
167 
168  // Nonlinear species:
169  // They should be unique ...
171  ostringstream os;
172  os << "The table must not have duplicate nonlinear species.\n"
173  << "Value of *nonlinear_species*: " << nonlinear_species;
174  throw runtime_error(os.str());
175  }
176 
177  // ... and pointing at valid species.
178  for (Index i = 0; i < n_nls; ++i) {
179  ostringstream os;
180  os << "nonlinear_species[" << i << "]";
181  chk_if_in_range(os.str(), nonlinear_species[i], 0, n_species - 1);
182  }
183 
184  // Frequency grid:
185  chk_if_increasing("f_grid", f_grid);
186 
187  // Pressure grid:
188  chk_if_decreasing("p_grid", p_grid);
189 
190  // Reference VMRs:
191  chk_matrix_nrows("vmrs_ref", vmrs_ref, n_species);
192  chk_matrix_ncols("vmrs_ref", vmrs_ref, n_p_grid);
193 
194  // Reference temperatur:
195  chk_vector_length("t_ref", t_ref, n_p_grid);
196 
197  // Temperature perturbations:
198  // Nothing to check for t_pert, it seems.
199 
200  // Perturbations for nonlinear species:
201  // Check that nls_pert is empty if and only if nonlinear_species is
202  // empty:
203  if (0 == n_nls) {
204  chk_vector_length("nls_pert", nls_pert, 0);
205  } else {
206  if (0 == n_nls_pert) {
207  ostringstream os;
208  os << "The vector nls_pert should contain the perturbations\n"
209  << "for the nonlinear species, but it is empty.";
210  throw runtime_error(os.str());
211  }
212  }
213 
214  // The table itself, xsec:
215  //
216  // We have to separtely consider the 3 cases described in the
217  // documentation of GasAbsLookup.
218  //
219  // Dimension: [ a, b, c, d ]
220  //
221  if (0 == n_nls) {
222  if (0 == t_pert.nelem()) {
223  // Simplest case (no temperature perturbations,
224  // no vmr perturbations):
225  // a = 1
226  // b = n_species
227  // c = n_f_grid
228  // d = n_p_grid
229  chk_size("xsec", xsec, 1, n_species, n_f_grid, n_p_grid);
230  } else {
231  // Standard case (temperature perturbations,
232  // but no vmr perturbations):
233  // a = n_t_pert
234  // b = n_species
235  // c = n_f_grid
236  // d = n_p_grid
237  chk_size("xsec", xsec, t_pert.nelem(), n_species, n_f_grid, n_p_grid);
238  }
239  } else {
240  // Full case (with temperature perturbations and
241  // vmr perturbations):
242  // a = n_t_pert
243  // b = n_species + n_nonlinear_species * ( n_nls_pert - 1 )
244  // c = n_f_grid
245  // d = n_p_grid
246  Index a = t_pert.nelem();
247  Index b = n_species + n_nls * (n_nls_pert - 1);
248  Index c = n_f_grid;
249  Index d = n_p_grid;
250 
251  chk_size("xsec", xsec, a, b, c, d);
252  }
253 
254  // We also need indices to the positions of the original species
255  // data in xsec. Nonlinear species take more space, therefor the
256  // position in xsec is not the same as the position in species.
257  ArrayOfIndex original_spec_pos_in_xsec(n_species);
258  for (Index i = 0, sp = 0; i < n_species; ++i) {
259  original_spec_pos_in_xsec[i] = sp;
260  if (non_linear[i])
261  sp += n_nls_pert;
262  else
263  sp += 1;
264  }
265 
266  // Now some checks on the input data:
267 
268  // The list of current species should not be empty:
269  if (0 == n_current_species) {
270  ostringstream os;
271  os << "The list of current species should not be empty.";
272  throw runtime_error(os.str());
273  }
274 
275  // The grid of current frequencies should be monotonically sorted:
276  chk_if_increasing("current_f_grid", current_f_grid);
277 
278  // 1. Find and remember the indices of the current species in the
279  // lookup table. At the same time verify that each species is
280  // included in the table exactly once.
281  ArrayOfIndex i_current_species(n_current_species);
282  out3 << " Looking for species in lookup table:\n";
283  for (Index i = 0; i < n_current_species; ++i) {
284  out3 << " " << get_tag_group_name(current_species[i]) << ": ";
285 
286  try {
287  i_current_species[i] =
288  chk_contains("abs_species", species, current_species[i]);
289 
290  } catch (const runtime_error_not_found&) {
291  // Is this one of the trivial species?
292  const Index spec_type = current_species[i][0].Type();
293  if (spec_type == SpeciesTag::TYPE_ZEEMAN ||
294  spec_type == SpeciesTag::TYPE_FREE_ELECTRONS ||
295  spec_type == SpeciesTag::TYPE_PARTICLES) {
296  // Set to -1 to flag that this needs special handling later on.
297  i_current_species[i] = -1;
298  } else {
299  ostringstream os;
300  os << "Species " << get_tag_group_name(current_species[i])
301  << " is missing in absorption lookup table.";
302  throw runtime_error(os.str());
303  }
304  }
305 
306  out3 << "found (or trivial).\n";
307  }
308 
309  // 1a. Find out which of the current species are nonlinear species:
310  Index n_current_nonlinear_species = 0; // Number of current nonlinear species
311  ArrayOfIndex current_non_linear(n_current_species, 0); // A logical array to
312  // flag which of the
313  // current species are
314  // nonlinear.
315 
316  out3 << " Finding out which of the current species are nonlinear:\n";
317  for (Index i = 0; i < n_current_species; ++i) {
318  if (i_current_species[i] >= 0) // Jump over trivial species here.
319  {
320  // Check if this is a nonlinear species:
321  if (non_linear[i_current_species[i]]) {
322  out3 << " " << get_tag_group_name(current_species[i]) << "\n";
323 
324  current_non_linear[i] = 1;
325  ++n_current_nonlinear_species;
326  }
327  }
328  }
329 
330  // 2. Find and remember the frequencies of the current calculation in
331  // the lookup table. At the same time verify that all frequencies are
332  // included and that no frequency occurs twice.
333 
334  // FIXME: This is a bit tricky, because we are comparing
335  // Numerics. Let's see how well this works in practice.
336 
337  ArrayOfIndex i_current_f_grid(n_current_f_grid);
338  out3 << " Looking for Frequencies in lookup table:\n";
339 
340  // We need no error checking for the next statement, since the
341  // function called throws a runtime error if a frequency
342  // is not found, or if the grids are not ok.
344  i_current_f_grid, f_grid, current_f_grid, verbosity);
345 
346  // 3. Use the species and frequency index lists to build the new lookup
347  // table.
348 
349  // Species:
350  new_table.species.resize(n_current_species);
351  for (Index i = 0; i < n_current_species; ++i) {
352  if (i_current_species[i] >= 0) {
353  new_table.species[i] = species[i_current_species[i]];
354 
355  // Is this a nonlinear species?
356  if (current_non_linear[i]) new_table.nonlinear_species.push_back(i);
357  } else {
358  // Here we handle the case of the trivial species, for which we simply
359  // copy the name:
360 
361  new_table.species[i] = current_species[i];
362  }
363  }
364 
365  // Frequency grid:
366  new_table.f_grid.resize(n_current_f_grid);
367  for (Index i = 0; i < n_current_f_grid; ++i) {
368  new_table.f_grid[i] = f_grid[i_current_f_grid[i]];
369  }
370 
371  // Pressure grid:
372  // new_table.p_grid.resize( n_p_grid );
373  new_table.p_grid = p_grid;
374 
375  // Reference VMR profiles:
376  new_table.vmrs_ref.resize(n_current_species, n_p_grid);
377  for (Index i = 0; i < n_current_species; ++i) {
378  if (i_current_species[i] >= 0) {
379  new_table.vmrs_ref(i, Range(joker)) =
380  vmrs_ref(i_current_species[i], Range(joker));
381  } else {
382  // Here we handle the case of the trivial species, for which we set
383  // the reference VMR to NAN.
384  new_table.vmrs_ref(i, Range(joker)) = NAN;
385  }
386  }
387 
388  // Reference temperature profile:
389  // new_table.t_ref.resize( t_ref.nelem() );
390  new_table.t_ref = t_ref;
391 
392  // Vector of temperature perturbations:
393  // new_table.t_pert.resize( t_pert.nelem() );
394  new_table.t_pert = t_pert;
395 
396  // Vector of perturbations for the VMRs of the nonlinear species:
397  // (Should stay empty if we have no nonlinear species)
398  if (0 != new_table.nonlinear_species.nelem()) {
399  // new_table.nls_pert.resize( n_nls_pert );
400  new_table.nls_pert = nls_pert;
401  }
402 
403  // Absorption coefficients:
404  new_table.xsec.resize(
405  xsec.nbooks(),
406  n_current_species + n_current_nonlinear_species * (n_nls_pert - 1),
407  n_current_f_grid,
408  xsec.ncols());
409 
410  // We have to copy the right species and frequencies from the old to
411  // the new table. Temperature perturbations and pressure grid remain
412  // the same.
413 
414  // Do species:
415  for (Index i_s = 0, sp = 0; i_s < n_current_species; ++i_s) {
416  // n_v is the number of VMR perturbations
417  Index n_v;
418  if (current_non_linear[i_s])
419  n_v = n_nls_pert;
420  else
421  n_v = 1;
422 
423  // cout << "i_s / sp / n_v = " << i_s << " / " << sp << " / " << n_v << endl;
424  // cout << "orig_pos = " << original_spec_pos_in_xsec[i_current_species[i_s]] << endl;
425 
426  // Do frequencies:
427  for (Index i_f = 0; i_f < n_current_f_grid; ++i_f) {
428  if (i_current_species[i_s] >= 0) {
429  new_table.xsec(Range(joker), Range(sp, n_v), i_f, Range(joker)) =
430  xsec(Range(joker),
431  Range(original_spec_pos_in_xsec[i_current_species[i_s]], n_v),
432  i_current_f_grid[i_f],
433  Range(joker));
434  } else {
435  // Here we handle the case of the trivial species, which we simply
436  // set to NAN:
437  new_table.xsec(Range(joker), Range(sp, n_v), i_f, Range(joker)) = NAN;
438  }
439 
440  // cout << "result: " << xsec( Range(joker),
441  // Range(original_spec_pos_in_xsec[i_current_species[i_s]],n_v),
442  // i_current_f_grid[i_f],
443  // Range(joker) ) << endl;
444  }
445 
446  sp += n_v;
447  }
448 
449  // 4. Replace original table by the new one.
450  *this = new_table;
451 
452  // 5. Initialize log_p_grid.
453  log_p_grid.resize(n_p_grid);
454  transform(log_p_grid, log, p_grid);
455 
456  // 6. Initialize fgp_default.
457  fgp_default.resize(f_grid.nelem());
459 }
460 
462 
516  const Index& p_interp_order,
517  const Index& t_interp_order,
518  const Index& h2o_interp_order,
519  const Index& f_interp_order,
520  const Numeric& p,
521  const Numeric& T,
523  ConstVectorView new_f_grid,
524  const Numeric& extpolfac) const {
525  // 1. Obtain some properties of the lookup table:
526 
527  // Number of gas species in the table:
528  const Index n_species = species.nelem();
529 
530  // Number of nonlinear species:
531  const Index n_nls = nonlinear_species.nelem();
532 
533  // Number of frequencies in the table:
534  const Index n_f_grid = f_grid.nelem();
535 
536  // Number of pressure grid points in the table:
537  const Index n_p_grid = p_grid.nelem();
538 
539  // Number of temperature perturbations:
540  const Index n_t_pert = t_pert.nelem();
541 
542  // Number of nonlinear species perturbations:
543  const Index n_nls_pert = nls_pert.nelem();
544 
545  // Number of frequencies in new_f_grid, the frequency grid for which we
546  // want to extract.
547  const Index n_new_f_grid = new_f_grid.nelem();
548 
549  // 2. First some checks on the lookup table itself:
550 
551  // Most checks here are asserts, because they check the internal
552  // consistency of the lookup table. They should never fail if the
553  // table has been created with ARTS.
554 
555  // If there are nonlinear species, then at least one species must be
556  // H2O. We will use that to perturb in the case of nonlinear
557  // species.
558  Index h2o_index = -1;
559  if (n_nls > 0) {
560  h2o_index =
562 
563  // This is a runtime error, even though it would be more logical
564  // for it to be an assertion, since it is an internal check on
565  // the table. The reason is that it is somewhat awkward to check
566  // for this in other places.
567  if (h2o_index == -1) {
568  ostringstream os;
569  os << "With nonlinear species, at least one species must be a H2O species.";
570  throw runtime_error(os.str());
571  }
572  }
573 
574  // Check that the dimension of vmrs_ref is consistent with species and p_grid:
575  assert(is_size(vmrs_ref, n_species, n_p_grid));
576 
577  // Check dimension of t_ref:
578  assert(is_size(t_ref, n_p_grid));
579 
580  // Check dimension of xsec:
581  DEBUG_ONLY({
582  Index a, b, c, d;
583  if (0 == n_t_pert)
584  a = 1;
585  else
586  a = n_t_pert;
587  b = n_species + n_nls * (n_nls_pert - 1);
588  c = n_f_grid;
589  d = n_p_grid;
590  // cout << "xsec: "
591  // << xsec.nbooks() << ", "
592  // << xsec.npages() << ", "
593  // << xsec.nrows() << ", "
594  // << xsec.ncols() << "\n";
595  // cout << "a b c d: "
596  // << a << ", "
597  // << b << ", "
598  // << c << ", "
599  // << d << "\n";
600  assert(is_size(xsec, a, b, c, d));
601  })
602 
603  // Make sure that log_p_grid is initialized:
604  if (log_p_grid.nelem() != n_p_grid) {
605  ostringstream os;
606  os << "The lookup table internal variable log_p_grid is not initialized.\n"
607  << "Use the abs_lookupAdapt method!";
608  throw runtime_error(os.str());
609  }
610 
611  // Verify that we have enough pressure, temperature,humdity, and frequency grid points
612  // for the desired interpolation orders. This check is not only
613  // table internal, since abs_nls_interp_order and abs_t_interp_order
614  // are separate WSVs that could have been modified. Hence, these are
615  // runtime errors.
616 
617  if ((n_p_grid < p_interp_order + 1)) {
618  ostringstream os;
619  os << "The number of pressure grid points in the table (" << n_p_grid
620  << ") is not enough for the desired order of interpolation ("
621  << p_interp_order << ").";
622  throw runtime_error(os.str());
623  }
624 
625  if ((n_nls_pert != 0) && (n_nls_pert < h2o_interp_order + 1)) {
626  ostringstream os;
627  os << "The number of humidity perturbation grid points in the table ("
628  << n_nls_pert
629  << ") is not enough for the desired order of interpolation ("
630  << h2o_interp_order << ").";
631  throw runtime_error(os.str());
632  }
633 
634  if ((n_t_pert != 0) && (n_t_pert < t_interp_order + 1)) {
635  ostringstream os;
636  os << "The number of temperature perturbation grid points in the table ("
637  << n_t_pert << ") is not enough for the desired order of interpolation ("
638  << t_interp_order << ").";
639  throw runtime_error(os.str());
640  }
641 
642  if ((n_f_grid < f_interp_order + 1)) {
643  ostringstream os;
644  os << "The number of frequency grid points in the table (" << n_f_grid
645  << ") is not enough for the desired order of interpolation ("
646  << f_interp_order << ").";
647  throw runtime_error(os.str());
648  }
649 
650  // 3. Checks on the input variables:
651 
652  // Check that abs_vmrs has the right dimension:
653  if (!is_size(abs_vmrs, n_species)) {
654  ostringstream os;
655  os << "Number of species in lookup table does not match number\n"
656  << "of species for which you want to extract absorption.\n"
657  << "Have you used abs_lookupAdapt? Or did you miss to add\n"
658  << "some VRM fields (e.g. for free electrons or particles)?\n";
659  throw runtime_error(os.str());
660  }
661 
662  // 4. Set up some things we will need later on:
663 
664  // 4.a Frequency grid positions
665 
666  // Frequency grid positions. The pointer is used to save copying of the
667  // default from the lookup table.
668  const ArrayOfGridPosPoly* fgp;
669  ArrayOfGridPosPoly fgp_local;
670 
671  // With f_interp_order 0 the frequency grid has to have the same size as in the
672  // lookup table, or exactly one element. If it matches the lookup table, we
673  // do no frequency interpolation at all. (We set the frequency grid positions
674  // to the predefined ones that come with the lookup table.)
675  if (f_interp_order == 0) {
676 
677  // We do some superficial checks below, to make sure that the
678  // frequency grid is the same as in the lookup table (only first
679  // and last element of f_grid are tested). As margin for
680  // agreement, we pick a value that is just slightly smaller than
681  // the perturbation that is used by the wind jacobian, which is 0.1.
682  const Numeric allowed_f_margin = 0.09;
683 
684  if (n_new_f_grid == n_f_grid) {
685  // Use the default fgp that is stored in the lookup table itself
686  // (which effectively means no frequency interpolation)
687  fgp = &fgp_default;
688 
689  // Check first f_grid element:
690  if (abs(f_grid[0] - new_f_grid[0]) > allowed_f_margin)
691  {
692  ostringstream os;
693  os << "First frequency in f_grid inconsistent with lookup table.\n"
694  << "f_grid[0] = " << f_grid[0] << "\n"
695  << "new_f_grid[0] = " << new_f_grid[0] << ".";
696  throw runtime_error(os.str());
697  }
698 
699  // Check last f_grid element:
700  if (abs(f_grid[n_f_grid - 1] - new_f_grid[n_new_f_grid - 1]) > allowed_f_margin)
701  {
702  ostringstream os;
703  os << "Last frequency in f_grid inconsistent with lookup table.\n"
704  << "f_grid[n_f_grid-1] = " << f_grid[n_f_grid - 1]
705  << "\n"
706  << "new_f_grid[n_new_f_grid-1] = " << new_f_grid[n_new_f_grid - 1]
707  << ".";
708  throw runtime_error(os.str());
709  }
710  } else if (n_new_f_grid == 1) {
711  fgp = &fgp_local;
712  fgp_local.resize(1);
713  gridpos_poly(fgp_local, f_grid, new_f_grid, 0);
714 
715  // Check that we really are on a frequency grid point, for safety's sake.
716  if (abs(f_grid[fgp_local[0].idx[0]] - new_f_grid[0]) > allowed_f_margin)
717  {
718  ostringstream os;
719  os << "Cannot find a matching lookup table frequency for frequency "
720  << new_f_grid[0] << ".\n"
721  << "(This check has not been properly tested, so perhaps this is\n"
722  << "a false alarm. Check for this in file gas_abs_lookup.cc.)";
723  throw runtime_error(os.str());
724  }
725  } else {
726  ostringstream os;
727  os << "With f_interp_order 0 the frequency grid has to have the same\n"
728  << "size as in the lookup table, or exactly one element.";
729  throw runtime_error(os.str());
730  }
731  } else {
732  const Numeric f_min = f_grid[0] - 0.5 * (f_grid[1] - f_grid[0]);
733  const Numeric f_max = f_grid[n_f_grid - 1] +
734  0.5 * (f_grid[n_f_grid - 1] - f_grid[n_f_grid - 2]);
735  if (new_f_grid[0] < f_min) {
736  ostringstream os;
737  os << "Problem with gas absorption lookup table.\n"
738  << "At least one frequency is outside the range covered by the lookup table.\n"
739  << "Your new frequency value is " << new_f_grid[0] << " Hz.\n"
740  << "The allowed range is " << f_min << " to " << f_max << " Hz.";
741  throw runtime_error(os.str());
742  }
743  if (new_f_grid[n_new_f_grid - 1] > f_max) {
744  ostringstream os;
745  os << "Problem with gas absorption lookup table.\n"
746  << "At least one frequency is outside the range covered by the lookup table.\n"
747  << "Your new frequency value is " << new_f_grid[n_new_f_grid - 1]
748  << " Hz.\n"
749  << "The allowed range is " << f_min << " to " << f_max << " Hz.";
750  throw runtime_error(os.str());
751  }
752 
753  // We do have real frequency interpolation (f_interp_order!=0).
754  fgp = &fgp_local;
755  fgp_local.resize(n_new_f_grid);
756  gridpos_poly(fgp_local, f_grid, new_f_grid, f_interp_order);
757  }
758 
759  // 4.b Other stuff
760 
761  // Flag for temperature interpolation, if this is not 0 we want
762  // to do T interpolation:
763  const Index do_T = n_t_pert;
764 
765  // Set up a logical array for the nonlinear species
766  ArrayOfIndex non_linear(n_species, 0);
767  for (Index s = 0; s < n_nls; ++s) {
768  non_linear[nonlinear_species[s]] = 1;
769  }
770 
771  // Calculate the number density for the given pressure and
772  // temperature:
773  // n = n0*T0/p0 * p/T or n = p/kB/t, ideal gas law
774  const Numeric n = number_density(p, T);
775 
776  // 5. Determine pressure grid position and interpolation weights:
777 
778  // Check that p is inside the grid. (p_grid is sorted in decreasing order.)
779  {
780  const Numeric p_max = p_grid[0] + 0.5 * (p_grid[0] - p_grid[1]);
781  const Numeric p_min = p_grid[n_p_grid - 1] -
782  0.5 * (p_grid[n_p_grid - 2] - p_grid[n_p_grid - 1]);
783  if ((p > p_max) || (p < p_min)) {
784  ostringstream os;
785  os << "Problem with gas absorption lookup table.\n"
786  << "Pressure p is outside the range covered by the lookup table.\n"
787  << "Your p value is " << p << " Pa.\n"
788  << "The allowed range is " << p_min << " to " << p_max << ".\n"
789  << "The pressure grid range in the table is " << p_grid[n_p_grid - 1]
790  << " to " << p_grid[0] << ".\n"
791  << "We allow a bit of extrapolation, but NOT SO MUCH!";
792  throw runtime_error(os.str());
793  }
794  }
795 
796  // For sure, we need to store the pressure grid position.
797  // We do the interpolation in log(p). Test have shown that this
798  // gives slightly better accuracy than interpolating in p directly.
799  ArrayOfGridPosPoly pgp(1);
800  gridpos_poly(pgp, log_p_grid, log(p), p_interp_order);
801 
802  // Pressure interpolation weights:
803  Vector pitw;
804  pitw.resize(p_interp_order + 1);
805  interpweights(pitw, pgp[0]);
806 
807  // Define also other grid positions and interpolation weights here, so that
808  // we do not have to allocate them over and over in the loops below.
809 
810  // Define the ArrayOfGridPosPoly that corresponds to "no interpolation at all".
811  ArrayOfGridPosPoly gp_trivial(1);
812  gp_trivial[0].idx.resize(1);
813  gp_trivial[0].w.resize(1);
814  gp_trivial[0].idx[0] = 0;
815  gp_trivial[0].w[0] = 1;
816 
817  // Temperature grid positions.
818  ArrayOfGridPosPoly tgp_withT(1); // Only a scalar.
819  ArrayOfGridPosPoly* tgp; // Pointer to either tgp_withT or gp_trivial.
820 
821  // Set this_t_interp_order, depending on whether we do T interpolation or not.
822  Index this_t_interp_order; // Local T interpolation order
823  if (do_T) {
824  this_t_interp_order = t_interp_order;
825  tgp = &tgp_withT;
826  } else {
827  // For the !do_T case we simply take the single
828  // temperature that is there, so we point tgp accordingly.
829  this_t_interp_order = 0;
830  tgp = &gp_trivial;
831  }
832 
833  // H2O(VMR) grid positions. vgp is what will be used in the interpolation.
834  // Depending on species, it is either pointed to gp_trivial, or to vgp_h2o.
835  ArrayOfGridPosPoly* vgp;
836  ArrayOfGridPosPoly vgp_h2o(1); // only a scalar
837 
838  // 6. We do the T and VMR interpolation for the pressure levels
839  // that are used in the pressure interpolation. (How many depends on
840  // p_interp_order.)
841 
842  // To store the interpolated result for the p_interp_order+1
843  // pressure levels:
844  // xsec dimensions are:
845  // Temperature
846  // H2O
847  // Frequency
848  // Pressure
849  // Dimensions of pre_interpolated are:
850  // Pressure (interpolation points)
851  // Species
852  // Temperature (always 1)
853  // H2O (always 1)
854  // Frequency
855 
856  Tensor5 xsec_pre_interpolated;
857  xsec_pre_interpolated.resize(
858  p_interp_order + 1, n_species, 1, 1, n_new_f_grid);
859 
860  // Define variables for interpolation weights outside the loops.
861  // We will make itw point to either the weights with H2O interpolation, or
862  // the ones without.
863  Tensor4 itw_withH2O, itw_noH2O, *itw;
864 
865  for (Index pi = 0; pi < p_interp_order + 1; ++pi) {
866  // Throw a runtime error if one of the reference VMR profiles is zero, but
867  // abs_vmrs is not. (This means that the lookup table was calculated with a
868  // reference profile of zero for that gas.)
869  // for (Index si=0; si<n_species; ++si)
870  // if ( (vmrs_ref(si,pi) == 0) &&
871  // (abs_vmrs[si] != 0) )
872  // {
873  // ostringstream os;
874  // os << "Reference VMR profile is zero, you cannot extract\n"
875  // << "Absorption for this species.\n"
876  // << "Species: " << si
877  // << " (" << get_species_name(species[si]) << ")\n"
878  // << "Lookup table pressure level: " << pi
879  // << " (" << p_grid[pi] << " Pa).";
880  // throw runtime_error( os.str() );
881  // }
882 
883  // Index into p_grid:
884  const Index this_p_grid_index = pgp[0].idx[pi];
885 
886  // Determine temperature grid position. This is only done if we
887  // want temperature interpolation, but the variable tgp has to
888  // be visible also outside for later use:
889  if (do_T) {
890  // Temperature in the atmosphere is altitude
891  // dependent. When we do the interpolation for the pressure level
892  // below and above our point, we should correct the target value of
893  // the interpolation to the altitude (pressure) difference. This
894  // ensures that there is for example no T interpolation if the
895  // desired T is right on the reference profile curve.
896  //
897  // I explicitly compared this with the old option to calculate
898  // the temperature offset relative to the temperature at
899  // this level. The performance in both cases is very
900  // similar. The reason, why I decided to keep this new
901  // version, is that it avoids the problem of needing
902  // oversized temperature perturbations if the pressure
903  // grid is coarse.
904  //
905  // No! The above approach leads to problems when combined with
906  // higher order pressure interpolation. The problem is that
907  // the reference T and VMR profiles may be very
908  // irregular. (For example the H2O profile often has a big
909  // jump near the bottom.) That sometimes leads to negative
910  // effective reference values when the reference profile is
911  // interpolated. I therefore reverted back to the original
912  // version of using the real temperature and humidity, not
913  // the interpolated one.
914 
915  // const Numeric effective_T_ref = interp(pitw,t_ref,pgp);
916  const Numeric effective_T_ref = t_ref[this_p_grid_index];
917 
918  // Convert temperature to offset from t_ref:
919  const Numeric T_offset = T - effective_T_ref;
920 
921  // cout << "T_offset = " << T_offset << endl;
922 
923  // Check that temperature offset is inside the allowed range.
924  {
925  const Numeric t_min = t_pert[0] - extpolfac * (t_pert[1] - t_pert[0]);
926  const Numeric t_max =
927  t_pert[n_t_pert - 1] +
928  extpolfac * (t_pert[n_t_pert - 1] - t_pert[n_t_pert - 2]);
929  if ((T_offset > t_max) || (T_offset < t_min)) {
930  ostringstream os;
931  os << "Problem with gas absorption lookup table.\n"
932  << "Temperature T is outside the range covered by the lookup table.\n"
933  << "Your temperature was " << T << " K at a pressure of " << p
934  << " Pa.\n"
935  << "The temperature offset value is " << T_offset << ".\n"
936  << "The allowed range is " << t_min << " to " << t_max << ".\n"
937  << "The temperature perturbation grid range in the table is "
938  << t_pert[0] << " to " << t_pert[n_t_pert - 1] << ".\n"
939  << "We allow a bit of extrapolation, but NOT SO MUCH!";
940  throw runtime_error(os.str());
941  }
942  }
943 
944  gridpos_poly(tgp_withT, t_pert, T_offset, t_interp_order, extpolfac);
945  }
946 
947  // Determine the H2O VMR grid position. We need to do this only
948  // once, since the only species who's VMR is interpolated is
949  // H2O. We do this only if there are nonlinear species, but the
950  // variable has to be visible later.
951  if (n_nls > 0) {
952  // Similar to the T case, we first interpolate the reference
953  // VMR to the pressure of extraction, then compare with
954  // the extraction VMR to determine the offset/fractional
955  // difference for the VMR interpolation.
956  //
957  // No! The above approach leads to problems when combined with
958  // higher order pressure interpolation. The problem is that
959  // the reference T and VMR profiles may be very
960  // irregular. (For example the H2O profile often has a big
961  // jump near the bottom.) That sometimes leads to negative
962  // effective reference values when the reference profile is
963  // interpolated. I therefore reverted back to the original
964  // version of using the real temperature and humidity, not
965  // the interpolated one.
966 
967  // const Numeric effective_vmr_ref = interp(pitw,
968  // vmrs_ref(h2o_index, Range(joker)),
969  // pgp);
970  const Numeric effective_vmr_ref = vmrs_ref(h2o_index, this_p_grid_index);
971 
972  // Fractional VMR:
973  const Numeric VMR_frac = abs_vmrs[h2o_index] / effective_vmr_ref;
974 
975  // Check that VMR_frac is inside the allowed range.
976  {
977  // FIXME: This check depends on how I interpolate VMR.
978  const Numeric x_min =
979  nls_pert[0] - extpolfac * (nls_pert[1] - nls_pert[0]);
980  const Numeric x_max =
981  nls_pert[n_nls_pert - 1] +
982  extpolfac * (nls_pert[n_nls_pert - 1] - nls_pert[n_nls_pert - 2]);
983 
984  if ((VMR_frac > x_max) || (VMR_frac < x_min)) {
985  ostringstream os;
986  os << "Problem with gas absorption lookup table.\n"
987  << "VMR for H2O (species " << h2o_index
988  << ") is outside the range covered by the lookup table.\n"
989  << "Your VMR was " << abs_vmrs[h2o_index] << " at a pressure of "
990  << p << " Pa.\n"
991  << "The reference VMR value there is " << effective_vmr_ref << "\n"
992  << "The fractional VMR relative to the reference value is "
993  << VMR_frac << ".\n"
994  << "The allowed range is " << x_min << " to " << x_max << ".\n"
995  << "The fractional VMR perturbation grid range in the table is "
996  << nls_pert[0] << " to " << nls_pert[n_nls_pert - 1] << ".\n"
997  << "We allow a bit of extrapolation, but NOT SO MUCH!";
998  throw runtime_error(os.str());
999  }
1000  }
1001 
1002  // For now, do linear interpolation in the fractional VMR.
1003  gridpos_poly(vgp_h2o, nls_pert, VMR_frac, h2o_interp_order, extpolfac);
1004  }
1005 
1006  // Precalculate interpolation weights.
1007  if (n_nls < n_species) {
1008  // Precalculate weights without H2O interpolation if there are less
1009  // nonlinear species than total species. (So at least one species
1010  // without H2O interpolation.)
1011  itw_noH2O.resize(1,
1012  1,
1013  n_new_f_grid,
1014  (this_t_interp_order + 1) * (1) * // H2O dimension
1015  (f_interp_order + 1));
1016  interpweights(itw_noH2O, *tgp, gp_trivial, *fgp);
1017  }
1018  if (n_nls > 0) {
1019  // Precalculate weights with H2O interpolation if there is at least
1020  // one nonlinear species.
1021  itw_withH2O.resize(1,
1022  1,
1023  n_new_f_grid,
1024  (this_t_interp_order + 1) * (h2o_interp_order + 1) *
1025  (f_interp_order + 1));
1026  interpweights(itw_withH2O, *tgp, vgp_h2o, *fgp);
1027  }
1028 
1029  // 7. Loop species:
1030  Index fpi = 0;
1031  for (Index si = 0; si < n_species; ++si) {
1032  // Flag for VMR interpolation, if this is not 0 we want to
1033  // do VMR interpolation:
1034  const Index do_VMR = non_linear[si];
1035 
1036  // For interpolation result.
1037  // Fixed pressure level and species.
1038  // Free dimension is T, H2O, and frequency.
1039  Tensor3View res(xsec_pre_interpolated(
1040  pi, si, Range(joker), Range(joker), Range(joker)));
1041 
1042  // Ignore species such as Zeeman and free_electrons which are not
1043  // stored in the lookup table. For those the result is set to 0.
1044  if (is_zeeman(species[si]) ||
1046  species[si][0].Type() == SpeciesTag::TYPE_PARTICLES) {
1047  if (do_VMR) {
1048  ostringstream os;
1049  os << "Problem with gas absorption lookup table.\n"
1050  << "VMR interpolation is not allowed for species \""
1051  << species[si][0].Name() << "\"";
1052  throw runtime_error(os.str());
1053  }
1054  res = 0.;
1055  fpi++;
1056  continue;
1057  }
1058 
1059  // Set h2o related interpolation parameters:
1060  Index this_h2o_extent; // Range of H2O interpolation
1061  if (do_VMR) {
1062  vgp = &vgp_h2o;
1063  this_h2o_extent = n_nls_pert;
1064  itw = &itw_withH2O;
1065  } else {
1066  vgp = &gp_trivial;
1067  this_h2o_extent = 1;
1068  itw = &itw_noH2O;
1069  }
1070 
1071  // Get the right view on xsec.
1072  ConstTensor3View this_xsec =
1073  xsec(Range(joker), // Temperature range
1074  Range(fpi, this_h2o_extent), // VMR profile range
1075  Range(joker), // Frequency range
1076  this_p_grid_index); // Pressure index
1077 
1078  // Do interpolation.
1079  interp(res, // result
1080  *itw, // weights
1081  this_xsec, // input
1082  *tgp,
1083  *vgp,
1084  *fgp); // grid positions
1085 
1086  // Increase fpi. fpi marks the position of the first profile
1087  // of the current species in xsec. This is needed to find
1088  // the right subsection of xsec in the presence of nonlinear species.
1089  if (do_VMR)
1090  fpi += n_nls_pert;
1091  else
1092  fpi++;
1093 
1094  } // End of species loop
1095 
1096  // fpi should have reached the end of that dimension of xsec. Check
1097  // this with an assertion:
1098  assert(fpi == xsec.npages());
1099 
1100  } // End of pressure index loop (below and above gp)
1101 
1102  // Now we have to interpolate between the p_interp_order+1 pressure levels
1103 
1104  // It is a "red" 1D interpolation case we are talking about here.
1105  // (But for a matrix in frequency and species.) Doing a loop over
1106  // frequency and species with an interp call inside would be
1107  // unefficient, so we do this by hand here.
1108  sga.resize(n_species, n_new_f_grid);
1109  sga = 0;
1110  for (Index pi = 0; pi < p_interp_order + 1; ++pi) {
1111  // Multiply pre interpolated quantities with pressure interpolation weights.
1112  // Dimensions of pre_interpolated are:
1113  // Pressure (interpolation points)
1114  // Species
1115  // Temperature (always 1)
1116  // H2O (always 1)
1117  // Frequency
1118  xsec_pre_interpolated(
1119  pi, Range(joker), Range(joker), Range(joker), Range(joker)) *= pitw[pi];
1120 
1121  // Add up in sga.
1122  // Dimensions of sga are (species, frequency)
1123  sga += xsec_pre_interpolated(pi, Range(joker), 0, 0, Range(joker));
1124  }
1125 
1126  // Watch out, this is not yet the final result, we
1127  // need to multiply with the number density of the species, i.e.,
1128  // with the total number density n, times the VMR of the
1129  // species:
1130  for (Index si = 0; si < n_species; ++si)
1131  sga(si, Range(joker)) *= (n * abs_vmrs[si]);
1132 
1133  // That's it, we're done!
1134 }
1135 
1136 const Vector& GasAbsLookup::GetFgrid() const { return f_grid; }
1137 
1138 const Vector& GasAbsLookup::GetPgrid() const { return p_grid; }
1139 
1141 ostream& operator<<(ostream& os, const GasAbsLookup& /* gal */) {
1142  os << "GasAbsLookup: Output operator not implemented";
1143  return os;
1144 }
Matrix
The Matrix class.
Definition: matpackI.h:1193
GasAbsLookup::GetPgrid
const Vector & GetPgrid() const
Definition: gas_abs_lookup.cc:1138
transform
void transform(VectorView y, double(&my_func)(double), ConstVectorView x)
A generic transform function for vectors, which can be used to implement mathematical functions opera...
Definition: matpackI.cc:1476
Tensor4::resize
void resize(Index b, Index p, Index r, Index c)
Resize function.
Definition: matpackIV.cc:1064
GasAbsLookup::GetFgrid
const Vector & GetFgrid() const
Definition: gas_abs_lookup.cc:1136
GasAbsLookup::nonlinear_species
ArrayOfIndex nonlinear_species
The species tags with non-linear treatment.
Definition: gas_abs_lookup.h:205
chk_if_increasing
void chk_if_increasing(const String &x_name, const ArrayOfIndex &x)
chk_if_increasing
Definition: check_input.cc:117
find_new_grid_in_old_grid
void find_new_grid_in_old_grid(ArrayOfIndex &pos, ConstVectorView old_grid, ConstVectorView new_grid, const Verbosity &verbosity)
Find positions of new grid points in old grid.
Definition: gas_abs_lookup.cc:48
joker
const Joker joker
interpolation.h
Header file for interpolation.cc.
interp
Numeric interp(ConstVectorView itw, ConstVectorView a, const GridPos &tc)
Red 1D Interpolate.
Definition: interpolation.cc:970
abs
#define abs(x)
Definition: legacy_continua.cc:20626
ARTS::Var::verbosity
Verbosity verbosity(Workspace &ws) noexcept
Definition: autoarts.h:7112
GasAbsLookup::Extract
void Extract(Matrix &sga, const Index &p_interp_order, const Index &t_interp_order, const Index &h2o_interp_order, const Index &f_interp_order, const Numeric &p, const Numeric &T, ConstVectorView abs_vmrs, ConstVectorView new_f_grid, const Numeric &extpolfac) const
Extract scalar gas absorption coefficients from the lookup table.
Definition: gas_abs_lookup.cc:515
DEBUG_ONLY
#define DEBUG_ONLY(...)
Definition: debug.h:36
Vector::resize
void resize(Index n)
Resize function.
Definition: matpackI.cc:404
get_tag_group_name
String get_tag_group_name(const ArrayOfSpeciesTag &tg)
Return the name of a tag group as a string.
Definition: abs_species_tags.cc:464
CREATE_OUT2
#define CREATE_OUT2
Definition: messages.h:206
Tensor4
The Tensor4 class.
Definition: matpackIV.h:421
is_size
bool is_size(ConstVectorView x, const Index &n)
Verifies that the size of x is l.
Definition: logic.cc:79
GasAbsLookup
An absorption lookup table.
Definition: gas_abs_lookup.h:45
ARTS::Var::abs_vmrs
Matrix abs_vmrs(Workspace &ws) noexcept
Definition: autoarts.h:2267
Tensor3View
The Tensor3View class.
Definition: matpackIII.h:239
Array
This can be used to make arrays out of anything.
Definition: array.h:108
number_density
Numeric number_density(const Numeric &p, const Numeric &t)
number_density
Definition: physics_funcs.cc:219
species_index_from_species_name
Index species_index_from_species_name(String name)
Return species index for given species name.
Definition: absorption.cc:531
is_zeeman
bool is_zeeman(const ArrayOfSpeciesTag &tg)
Is this a Zeeman tag group?
Definition: abs_species_tags.cc:702
CREATE_OUT3
#define CREATE_OUT3
Definition: messages.h:207
Tensor5::resize
void resize(Index s, Index b, Index p, Index r, Index c)
Resize function.
Definition: matpackV.cc:1743
messages.h
Declarations having to do with the four output streams.
chk_matrix_ncols
void chk_matrix_ncols(const String &x_name, ConstMatrixView x, const Index &l)
chk_matrix_ncols
Definition: check_input.cc:419
ConstVectorView::nelem
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:51
GasAbsLookup::t_ref
Vector t_ref
The reference temperature profile [K].
Definition: gas_abs_lookup.h:246
find_first_species_tg
Index find_first_species_tg(const ArrayOfArrayOfSpeciesTag &tgs, const Index &spec)
Find first occurrence of species in tag groups.
Definition: abs_species_tags.cc:532
physics_funcs.h
This file contains declerations of functions of physical character.
ConstTensor4View::ncols
Index ncols() const
Returns the number of columns.
Definition: matpackIV.cc:66
GasAbsLookup::f_grid
Vector f_grid
The frequency grid [Hz].
Definition: gas_abs_lookup.h:209
GasAbsLookup::p_grid
Vector p_grid
The pressure grid for the table [Pa].
Definition: gas_abs_lookup.h:223
SpeciesTag::TYPE_FREE_ELECTRONS
@ TYPE_FREE_ELECTRONS
Definition: abs_species_tags.h:152
Numeric
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
Verbosity
Definition: messages.h:49
ConstTensor4View::npages
Index npages() const
Returns the number of pages.
Definition: matpackIV.cc:60
GasAbsLookup::fgp_default
ArrayOfGridPosPoly fgp_default
Frequency grid positions.
Definition: gas_abs_lookup.h:219
SpeciesTag::TYPE_ZEEMAN
@ TYPE_ZEEMAN
Definition: abs_species_tags.h:149
ConstTensor4View::nbooks
Index nbooks() const
Returns the number of books.
Definition: matpackIV.cc:57
GasAbsLookup::nls_pert
Vector nls_pert
The vector of perturbations for the VMRs of the nonlinear species.
Definition: gas_abs_lookup.h:275
GasAbsLookup::xsec
Tensor4 xsec
Absorption cross sections.
Definition: gas_abs_lookup.h:309
runtime_error_not_found
Subclasses of runtime_error.
Definition: check_input.h:108
max
#define max(a, b)
Definition: legacy_continua.cc:20629
Matrix::resize
void resize(Index r, Index c)
Resize function.
Definition: matpackI.cc:1056
LineShape::Type
Type
Type of line shape to compute.
Definition: lineshapemodel.h:788
interpolation_poly.h
Header file for interpolation_poly.cc.
chk_matrix_nrows
void chk_matrix_nrows(const String &x_name, ConstMatrixView x, const Index &l)
chk_matrix_nrows
Definition: check_input.cc:441
Tensor5
The Tensor5 class.
Definition: matpackV.h:506
GasAbsLookup::species
ArrayOfArrayOfSpeciesTag species
The species tags for which the table is valid.
Definition: gas_abs_lookup.h:196
Range
The range class.
Definition: matpackI.h:160
operator<<
ostream & operator<<(ostream &os, const GasAbsLookup &)
Output operatior for GasAbsLookup.
Definition: gas_abs_lookup.cc:1141
logic.h
Header file for logic.cc.
chk_vector_length
void chk_vector_length(const String &x_name, ConstVectorView x, const Index &l)
chk_vector_length
Definition: check_input.cc:281
GasAbsLookup::log_p_grid
Vector log_p_grid
The natural log of the pressure grid.
Definition: gas_abs_lookup.h:231
ConstTensor3View
A constant view of a Tensor3.
Definition: matpackIII.h:132
GasAbsLookup::Adapt
void Adapt(const ArrayOfArrayOfSpeciesTag &current_species, ConstVectorView current_f_grid, const Verbosity &verbosity)
Adapt lookup table to current calculation.
Definition: gas_abs_lookup.cc:118
chk_contains
Index chk_contains(const String &x_name, const Array< T > &x, const T &what)
Check if an array contains a value.
Definition: check_input.h:149
chk_size
void chk_size(const String &x_name, ConstVectorView x, const Index &c)
Runtime check for size of Vector.
Definition: check_input.cc:466
Index
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
chk_if_in_range
void chk_if_in_range(const String &x_name, const Index &x, const Index &x_low, const Index &x_high)
chk_if_in_range
Definition: check_input.cc:89
check_input.h
gridpos_poly
void gridpos_poly(ArrayOfGridPosPoly &gp, ConstVectorView old_grid, ConstVectorView new_grid, const Index order, const Numeric &extpolfac)
The maximum difference from 1 that we allow for a sum check.
Definition: interpolation_poly.cc:120
GasAbsLookup::vmrs_ref
Matrix vmrs_ref
The reference VMR profiles.
Definition: gas_abs_lookup.h:241
is_unique
bool is_unique(const ArrayOfIndex &x)
Checks if an ArrayOfIndex is unique, i.e., has no duplicate values.
Definition: logic.cc:274
GasAbsLookup::t_pert
Vector t_pert
The vector of temperature perturbations [K].
Definition: gas_abs_lookup.h:260
Vector
The Vector class.
Definition: matpackI.h:860
ConstVectorView
A constant view of a Vector.
Definition: matpackI.h:476
SpeciesTag::TYPE_PARTICLES
@ TYPE_PARTICLES
Definition: abs_species_tags.h:153
Array::nelem
Index nelem() const
Number of elements.
Definition: array.h:195
chk_if_decreasing
void chk_if_decreasing(const String &x_name, ConstVectorView x)
chk_if_decreasing
Definition: check_input.cc:358
interpweights
void interpweights(VectorView itw, const GridPos &tc)
Red 1D interpolation weights.
Definition: interpolation.cc:741
gas_abs_lookup.h
Declarations for the gas absorption lookup table.