ARTS  2.3.1285(git:92a29ea9-dirty)
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,
522  ConstVectorView abs_vmrs,
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 }
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
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.
Index nelem() const
Number of elements.
Definition: array.h:195
Vector nls_pert
The vector of perturbations for the VMRs of the nonlinear species.
void resize(Index s, Index b, Index p, Index r, Index c)
Resize function.
Definition: matpackV.cc:1743
void chk_if_decreasing(const String &x_name, ConstVectorView x)
chk_if_decreasing
Definition: check_input.cc:358
Vector t_pert
The vector of temperature perturbations [K].
Declarations having to do with the four output streams.
void chk_if_increasing(const String &x_name, const ArrayOfIndex &x)
chk_if_increasing
Definition: check_input.cc:117
The Vector class.
Definition: matpackI.h:860
#define abs(x)
Numeric interp(ConstVectorView itw, ConstVectorView a, const GridPos &tc)
Red 1D Interpolate.
ostream & operator<<(ostream &os, const GasAbsLookup &)
Output operatior for GasAbsLookup.
The Tensor4 class.
Definition: matpackIV.h:421
The range class.
Definition: matpackI.h:160
String get_tag_group_name(const ArrayOfSpeciesTag &tg)
Return the name of a tag group as a string.
Index npages() const
Returns the number of pages.
Definition: matpackIV.cc:60
bool is_zeeman(const ArrayOfSpeciesTag &tg)
Is this a Zeeman tag group?
ArrayOfIndex nonlinear_species
The species tags with non-linear treatment.
Header file for interpolation.cc.
void Adapt(const ArrayOfArrayOfSpeciesTag &current_species, ConstVectorView current_f_grid, const Verbosity &verbosity)
Adapt lookup table to current calculation.
Vector log_p_grid
The natural log of the pressure grid.
void chk_matrix_ncols(const String &x_name, ConstMatrixView x, const Index &l)
chk_matrix_ncols
Definition: check_input.cc:419
Numeric number_density(const Numeric &p, const Numeric &t)
number_density
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:51
bool is_unique(const ArrayOfIndex &x)
Checks if an ArrayOfIndex is unique, i.e., has no duplicate values.
Definition: logic.cc:274
Tensor4 xsec
Absorption cross sections.
#define DEBUG_ONLY(...)
Definition: debug.h:36
Matrix vmrs_ref
The reference VMR profiles.
Vector f_grid
The frequency grid [Hz].
_CS_string_type str() const
Definition: sstream.h:491
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
The Tensor3View class.
Definition: matpackIII.h:239
Index find_first_species_tg(const ArrayOfArrayOfSpeciesTag &tgs, const Index &spec)
Find first occurrence of species in tag groups.
An absorption lookup table.
const Vector & GetFgrid() const
const Joker joker
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
The Matrix class.
Definition: matpackI.h:1193
Declarations for the gas absorption lookup table.
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.
Index species_index_from_species_name(String name)
Return species index for given species name.
Definition: absorption.cc:531
Vector t_ref
The reference temperature profile [K].
Header file for logic.cc.
Vector p_grid
The pressure grid for the table [Pa].
This can be used to make arrays out of anything.
Definition: array.h:40
Header file for interpolation_poly.cc.
void chk_size(const String &x_name, ConstVectorView x, const Index &c)
Runtime check for size of Vector.
Definition: check_input.cc:466
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.
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
void resize(Index n)
Resize function.
Definition: matpackI.cc:404
#define max(a, b)
A constant view of a Tensor3.
Definition: matpackIII.h:132
A constant view of a Vector.
Definition: matpackI.h:476
void chk_vector_length(const String &x_name, ConstVectorView x, const Index &l)
chk_vector_length
Definition: check_input.cc:281
const Vector & GetPgrid() const
void chk_matrix_nrows(const String &x_name, ConstMatrixView x, const Index &l)
chk_matrix_nrows
Definition: check_input.cc:441
Index nbooks() const
Returns the number of books.
Definition: matpackIV.cc:57
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
ArrayOfGridPosPoly fgp_default
Frequency grid positions.
Subclasses of runtime_error.
Definition: check_input.h:108
#define CREATE_OUT3
Definition: messages.h:207
bool is_size(ConstVectorView x, const Index &n)
Verifies that the size of x is l.
Definition: logic.cc:79
Index ncols() const
Returns the number of columns.
Definition: matpackIV.cc:66
void interpweights(VectorView itw, const GridPos &tc)
Red 1D interpolation weights.
#define CREATE_OUT2
Definition: messages.h:206
ArrayOfArrayOfSpeciesTag species
The species tags for which the table is valid.
This file contains declerations of functions of physical character.
void resize(Index b, Index p, Index r, Index c)
Resize function.
Definition: matpackIV.cc:1064
The Tensor5 class.
Definition: matpackV.h:506
void resize(Index r, Index c)
Resize function.
Definition: matpackI.cc:1056