ARTS  2.0.49
gas_abs_lookup.cc
Go to the documentation of this file.
1 /* Copyright (C) 2002-2008 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 <cmath>
27 #include "gas_abs_lookup.h"
28 #include "interpolation.h"
29 #include "interpolation_poly.h"
30 #include "make_vector.h"
31 #include "logic.h"
32 #include "check_input.h"
33 #include "messages.h"
34 #include "physics_funcs.h"
35 
37 
48  ConstVectorView old_grid,
49  ConstVectorView new_grid,
50  const Verbosity& verbosity )
51 {
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  // The frequency difference in Hz that we are willing to tolerate. 1
61  // Hz seems to be on the safe side.
62  const Numeric tolerance = 1;
63 
64  // Old grid position:
65  Index j = 0;
66 
67  // Loop the new frequencies:
68  for ( Index i=0; i<n_new_grid; ++i )
69  {
70  // We have done runtime checks that both the new and the old
71  // frequency gris are sorted in GasAbsLookup::Adapt, so we can
72  // use the fact here.
73 
74  while ( abs(new_grid[i]-old_grid[j]) > tolerance )
75  {
76  ++j;
77  if ( j>=n_old_grid )
78  {
79  ostringstream os;
80  os << "Cannot find new frequency " << i
81  << " (" << new_grid[i] << "Hz) in the lookup table frequency grid.";
82  throw runtime_error( os.str() );
83  }
84  }
85 
86  pos[i] = j;
87  out3 << " " << new_grid[i] << " found, index = " << pos[i] << ".\n";
88  }
89 }
90 
92 
120 void GasAbsLookup::Adapt( const ArrayOfArrayOfSpeciesTag& current_species,
121  ConstVectorView current_f_grid,
122  const Verbosity& verbosity )
123 {
126 
127  // Some constants we will need:
128  const Index n_current_species = current_species.nelem();
129  const Index n_current_f_grid = current_f_grid.nelem();
130 
131  const Index n_species = species.nelem();
132  const Index n_nls = nonlinear_species.nelem();
133  const Index n_nls_pert = nls_pert.nelem();
134  const Index n_f_grid = f_grid.nelem();
135  const Index n_p_grid = p_grid.nelem();
136 
137  out2 << " Original table: " << n_species << " species, "
138  << n_f_grid << " frequencies.\n"
139  << " Adapt to: " << n_current_species << " species, "
140  << n_current_f_grid << " frequencies.\n";
141 
142  if ( 0 == n_nls )
143  {
144  out2 << " Table contains no nonlinear species.\n";
145  }
146 
147  // Set up a logical array for the nonlinear species
148  ArrayOfIndex non_linear(n_species,0);
149  for ( Index s=0; s<n_nls; ++s )
150  {
151  non_linear[nonlinear_species[s]] = 1;
152  }
153 
154  if ( 0 == t_pert.nelem() )
155  {
156  out2 << " Table contains no temperature perturbations.\n";
157  }
158 
159  // We are constructing a new lookup table, containing just the
160  // species and frequencies that are necessary for the current
161  // calculation. We will build it in this local variable, then copy
162  // it back to *this in the end.
163  GasAbsLookup new_table;
164 
165  // First some checks on the lookup table itself:
166 
167  // Species:
168  if ( 0 == n_species )
169  {
170  ostringstream os;
171  os << "The lookup table should have at least one species.";
172  throw runtime_error( os.str() );
173  }
174 
175  // Nonlinear species:
176  // They should be unique ...
178  {
179  ostringstream os;
180  os << "The table must not have duplicate nonlinear species.\n"
181  << "Value of *nonlinear_species*: " << nonlinear_species;
182  throw runtime_error( os.str() );
183  }
184 
185  // ... and pointing at valid species.
186  for ( Index i=0; i<n_nls; ++i )
187  {
188  ostringstream os;
189  os << "nonlinear_species[" << i << "]";
190  chk_if_in_range( os.str(),
192  0,
193  n_species-1 );
194  }
195 
196  // Frequency grid:
197  chk_if_increasing( "f_grid", f_grid );
198 
199  // Pressure grid:
200  chk_if_decreasing( "p_grid", p_grid );
201 
202  // Reference VMRs:
203  chk_matrix_nrows( "vmrs_ref", vmrs_ref, n_species );
204  chk_matrix_ncols( "vmrs_ref", vmrs_ref, n_p_grid );
205 
206  // Reference temperatur:
207  chk_vector_length( "t_ref", t_ref, n_p_grid );
208 
209  // Temperature perturbations:
210  // Nothing to check for t_pert, it seems.
211 
212  // Perturbations for nonlinear species:
213  // Check that nls_pert is empty if and only if nonlinear_species is
214  // empty:
215  if ( 0 == n_nls )
216  {
217  chk_vector_length( "nls_pert", nls_pert, 0 );
218  }
219  else
220  {
221  if ( 0 == n_nls_pert )
222  {
223  ostringstream os;
224  os << "The vector nls_pert should contain the perturbations\n"
225  << "for the nonlinear species, but it is empty.";
226  throw runtime_error( os.str() );
227  }
228  }
229 
230  // The table itself, xsec:
231  //
232  // We have to separtely consider the 3 cases described in the
233  // documentation of GasAbsLookup.
234  //
235  // Dimension: [ a, b, c, d ]
236  //
237  if ( 0==n_nls )
238  {
239  if ( 0==t_pert.nelem() )
240  {
241  // Simplest case (no temperature perturbations,
242  // no vmr perturbations):
243  // a = 1
244  // b = n_species
245  // c = n_f_grid
246  // d = n_p_grid
247  chk_size( "xsec", xsec,
248  1,
249  n_species,
250  n_f_grid,
251  n_p_grid );
252  }
253  else
254  {
255  // Standard case (temperature perturbations,
256  // but no vmr perturbations):
257  // a = n_t_pert
258  // b = n_species
259  // c = n_f_grid
260  // d = n_p_grid
261  chk_size( "xsec", xsec,
262  t_pert.nelem(),
263  n_species,
264  n_f_grid,
265  n_p_grid );
266  }
267  }
268  else
269  {
270  // Full case (with temperature perturbations and
271  // vmr perturbations):
272  // a = n_t_pert
273  // b = n_species + n_nonlinear_species * ( n_nls_pert - 1 )
274  // c = n_f_grid
275  // d = n_p_grid
276  Index a = t_pert.nelem();
277  Index b = n_species
278  + n_nls * ( n_nls_pert - 1 );
279  Index c = n_f_grid;
280  Index d = n_p_grid;
281 
282  chk_size( "xsec", xsec, a, b, c, d );
283  }
284 
285  // We also need indices to the positions of the original species
286  // data in xsec. Nonlinear species take more space, therefor the
287  // position in xsec is not the same as the position in species.
288  ArrayOfIndex original_spec_pos_in_xsec(n_species);
289  for (Index i=0,sp=0; i<n_species; ++i)
290  {
291  original_spec_pos_in_xsec[i] = sp;
292  if (non_linear[i]) sp += n_nls_pert;
293  else sp += 1;
294  }
295 
296 
297 
298  // Now some checks on the input data:
299 
300  // The list of current species should not be empty:
301  if ( 0==n_current_species )
302  {
303  ostringstream os;
304  os << "The list of current species should not be empty.";
305  throw runtime_error( os.str() );
306  }
307 
308  // The grid of current frequencies should be monotonically sorted:
309  chk_if_increasing( "current_f_grid", current_f_grid );
310 
311 
312  // 1. Find and remember the indices of the current species in the
313  // lookup table. At the same time verify that each species is
314  // included in the table exactly once.
315  ArrayOfIndex i_current_species(n_current_species);
316  out3 << " Looking for species in lookup table:\n";
317  for ( Index i=0; i<n_current_species; ++i )
318  {
319  out3 << " " << get_tag_group_name( current_species[i] ) << ": ";
320  // We need no error checking for the next statement, since the
321  // chk_contains function throws a runtime error if the species
322  // is not found exactly once.
323  i_current_species[i] =
324  chk_contains( "species", species, current_species[i] );
325  out3 << "found.\n";
326  }
327 
328  // 1a. Find out which of the current species are nonlinear species:
329  Index n_current_nonlinear_species = 0; // Number of current nonlinear species
330  ArrayOfIndex current_non_linear(n_current_species,0); // A logical array to
331  // flag which of the
332  // current species are
333  // nonlinear.
334 
335  out3 << " Finding out which of the current species are nonlinear:\n";
336  for ( Index i=0; i<n_current_species; ++i )
337  {
338  // Check if this is a nonlinear species:
339  if (non_linear[i_current_species[i]])
340  {
341  out3 << " " << get_tag_group_name( current_species[i] ) << "\n";
342 
343  current_non_linear[i] = 1;
344  ++n_current_nonlinear_species;
345  }
346  }
347 
348  // 2. Find and remember the frequencies of the current calculation in
349  // the lookup table. At the same time verify that all frequencies are
350  // included and that no frequency occurs twice.
351 
352  // FIXME: This is a bit tricky, because we are comparing
353  // Numerics. Let's see how well this works in practice.
354 
355  ArrayOfIndex i_current_f_grid(n_current_f_grid);
356  out3 << " Looking for Frequencies in lookup table:\n";
357 
358  // We need no error checking for the next statement, since the
359  // function called throws a runtime error if a frequency
360  // is not found, or if the grids are not ok.
361  find_new_grid_in_old_grid( i_current_f_grid,
362  f_grid,
363  current_f_grid,
364  verbosity );
365 
366 
367  // 3. Use the species and frequency index lists to build the new lookup
368  // table.
369 
370  // Species:
371  new_table.species.resize( n_current_species );
372  for ( Index i=0; i<n_current_species; ++i )
373  {
374  new_table.species[i] = species[i_current_species[i]];
375 
376  // Is this a nonlinear species?
377  if (current_non_linear[i])
378  new_table.nonlinear_species.push_back( i );
379  }
380 
381  // Frequency grid:
382  new_table.f_grid.resize( n_current_f_grid );
383  for ( Index i=0; i<n_current_f_grid; ++i )
384  {
385  new_table.f_grid[i] = f_grid[i_current_f_grid[i]];
386  }
387 
388  // Pressure grid:
389  // new_table.p_grid.resize( n_p_grid );
390  new_table.p_grid = p_grid;
391 
392  // Reference VMR profiles:
393  new_table.vmrs_ref.resize( n_current_species,
394  n_p_grid );
395  for ( Index i=0; i<n_current_species; ++i )
396  {
397  new_table.vmrs_ref( i,
398  Range(joker) )
399  = vmrs_ref( i_current_species[i],
400  Range(joker) );
401  }
402 
403  // Reference temperature profile:
404  // new_table.t_ref.resize( t_ref.nelem() );
405  new_table.t_ref = t_ref;
406 
407  // Vector of temperature perturbations:
408  // new_table.t_pert.resize( t_pert.nelem() );
409  new_table.t_pert = t_pert;
410 
411  // Vector of perturbations for the VMRs of the nonlinear species:
412  // (Should stay empty if we have no nonlinear species)
413  if ( 0 != new_table.nonlinear_species.nelem() )
414  {
415  // new_table.nls_pert.resize( n_nls_pert );
416  new_table.nls_pert = nls_pert;
417  }
418 
419  // Absorption coefficients:
420  new_table.xsec.resize( xsec.nbooks(),
421  n_current_species+n_current_nonlinear_species*(n_nls_pert-1),
422  n_current_f_grid,
423  xsec.ncols() );
424 
425  // We have to copy the right species and frequencies from the old to
426  // the new table. Temperature perturbations and pressure grid remain
427  // the same.
428 
429  // Do species:
430  for ( Index i_s=0,sp=0; i_s<n_current_species; ++i_s )
431  {
432  // n_v is the number of VMR perturbations
433  Index n_v;
434  if (current_non_linear[i_s])
435  n_v = n_nls_pert;
436  else
437  n_v = 1;
438 
439 // cout << "i_s / sp / n_v = " << i_s << " / " << sp << " / " << n_v << endl;
440 // cout << "orig_pos = " << original_spec_pos_in_xsec[i_current_species[i_s]] << endl;
441 
442  // Do frequencies:
443  for ( Index i_f=0; i_f<n_current_f_grid; ++i_f )
444  {
445  new_table.xsec( Range(joker),
446  Range(sp,n_v),
447  i_f,
448  Range(joker) )
449  =
450  xsec( Range(joker),
451  Range(original_spec_pos_in_xsec[i_current_species[i_s]],n_v),
452  i_current_f_grid[i_f],
453  Range(joker) );
454 
455 // cout << "result: " << xsec( Range(joker),
456 // Range(original_spec_pos_in_xsec[i_current_species[i_s]],n_v),
457 // i_current_f_grid[i_f],
458 // Range(joker) ) << endl;
459 
460  }
461 
462  sp += n_v;
463  }
464 
465 
466  // 4. Replace original table by the new one.
467  *this = new_table;
468 
469  // 5. Initialize log_p_grid.
470  log_p_grid.resize( n_p_grid );
472  log,
473  p_grid );
474 }
475 
477 
518  const Index& p_interp_order,
519  const Index& t_interp_order,
520  const Index& h2o_interp_order,
521  const Index& f_index,
522  const Numeric& p,
523  const Numeric& T,
524  ConstVectorView abs_vmrs ) const
525 {
526  // 1. Obtain some properties of the lookup table:
527 
528  // Number of gas species in the table:
529  const Index n_species = species.nelem();
530 
531  // Number of nonlinear species:
532  const Index n_nls = nonlinear_species.nelem();
533 
534  // Number of frequencies in the table:
535  const Index n_f_grid = f_grid.nelem();
536 
537  // Number of pressure grid points in the table:
538  const Index n_p_grid = p_grid.nelem();
539 
540  // Number of temperature perturbations:
541  const Index n_t_pert = t_pert.nelem();
542 
543  // Number of nonlinear species perturbations:
544  const Index n_nls_pert = nls_pert.nelem();
545 
546 
547  // 2. First some checks on the lookup table itself:
548 
549  // Most checks here are asserts, because they check the internal
550  // consistency of the lookup table. They should never fail if the
551  // table has been created with ARTS.
552 
553  // If there are nonlinear species, then at least one species must be
554  // H2O. We will use that to perturb in the case of nonlinear
555  // species.
556  Index h2o_index = -1;
557  if (n_nls>0)
558  {
559  h2o_index = find_first_species_tg( species,
561 
562  // This is a runtime error, even though it would be more logical
563  // for it to be an assertion, since it is an internal check on
564  // the table. The reason is that it is somewhat awkward to check
565  // for this in other places.
566  if ( h2o_index == -1 )
567  {
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 ) a = 1;
584  else a = n_t_pert;
585  b = n_species + n_nls * ( n_nls_pert - 1 );
586  c = n_f_grid;
587  d = n_p_grid;
588 // cout << "xsec: "
589 // << xsec.nbooks() << ", "
590 // << xsec.npages() << ", "
591 // << xsec.nrows() << ", "
592 // << xsec.ncols() << "\n";
593 // cout << "a b c d: "
594 // << a << ", "
595 // << b << ", "
596 // << c << ", "
597 // << d << "\n";
598  assert( is_size( xsec, a, b, c, d ) );
599  })
600 
601  // Make sure that log_p_grid is initialized:
602  if (log_p_grid.nelem() != n_p_grid)
603  {
604  ostringstream os;
605  os << "The lookup table internal variable log_p_grid is not initialized.\n"
606  << "Use the abs_lookupAdapt method!";
607  throw runtime_error( os.str() );
608  }
609 
610  // Verify that we have enough pressure, temperature and humdity grid points
611  // for the desired interpolation orders. This check is not only
612  // table internal, since abs_nls_interp_order and abs_t_interp_order
613  // are separate WSVs that could have been modified. Hence, these are
614  // runtime errors.
615 
616  if ( (n_p_grid < p_interp_order+1) )
617  {
618  ostringstream os;
619  os << "The number of pressure grid points in the table ("
620  << n_p_grid << ") 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  {
627  ostringstream os;
628  os << "The number of humidity perturbation grid points in the table ("
629  << n_nls_pert << ") 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  {
636  ostringstream os;
637  os << "The number of temperature perturbation grid points in the table ("
638  << n_t_pert << ") is not enough for the desired order of interpolation ("
639  << t_interp_order << ").";
640  throw runtime_error( os.str() );
641  }
642 
643 
644  // 3. Checks on the input variables:
645 
646  // Assert that abs_vmrs has the right dimension:
647  if ( !is_size( abs_vmrs, n_species ) )
648  {
649  ostringstream os;
650  os << "Number of species in lookup table does not match number\n"
651  << "of species for which you want to extract absorption.\n"
652  << "Have you used abs_lookupAdapt?";
653  throw runtime_error( os.str() );
654  }
655 
656 
657  // 4. Set up some things we will need later on:
658 
659  // Set the start and extent for the frequency loop:
660  Index f_start, f_extent;
661  if ( f_index < 0 )
662  {
663  // This means we should extract for all frequencies.
664 
665  f_start = 0;
666  f_extent = n_f_grid;
667  }
668  else
669  {
670  // This means we should extract only for one frequency.
671 
672  // Check that f_index is inside f_grid:
673  if ( f_index >= n_f_grid )
674  {
675  ostringstream os;
676  os << "Problem with gas absorption lookup table.\n"
677  << "Frequency index f_index is too high, you have " << f_index
678  << ", the largest allowed value is " << n_f_grid-1 << ".";
679  throw runtime_error( os.str() );
680  }
681 
682  f_start = f_index;
683  f_extent = 1;
684  }
685  const Range f_range(f_start,f_extent);
686 
687 
688  // Set up a logical array for the nonlinear species
689  ArrayOfIndex non_linear(n_species,0);
690  for ( Index s=0; s<n_nls; ++s )
691  {
692  non_linear[nonlinear_species[s]] = 1;
693  }
694 
695 
696  // Calculate the number density for the given pressure and
697  // temperature:
698  // n = n0*T0/p0 * p/T or n = p/kB/t, ideal gas law
699  const Numeric n = number_density( p, T );
700 
701 
702  // 5. Determine pressure grid position and interpolation weights:
703 
704  // Check that p is inside the grid. (p_grid is sorted in decreasing order.)
705  {
706  const Numeric p_max = p_grid[0] + 0.5*(p_grid[0]-p_grid[1]);
707  const Numeric p_min = p_grid[n_p_grid-1] - 0.5*(p_grid[n_p_grid-2]-p_grid[n_p_grid-1]);
708  if ( ( p > p_max ) ||
709  ( p < p_min ) )
710  {
711  ostringstream os;
712  os << "Problem with gas absorption lookup table.\n"
713  << "Pressure p is outside the range covered by the lookup table.\n"
714  << "Your p value is " << p << " Pa.\n"
715  << "The allowed range is " << p_min << " to " << p_max << ".\n"
716  << "The pressure grid range in the table is " << p_grid[n_p_grid-1]
717  << " to " << p_grid[0] << ".\n"
718  << "We allow a bit of extrapolation, but NOT SO MUCH!";
719  throw runtime_error( os.str() );
720  }
721  }
722 
723 
724  // For sure, we need to store the pressure grid position.
725  // We do the interpolation in log(p). Test have shown that this
726  // gives slightly better accuracy than interpolating in p directly.
727  ArrayOfGridPosPoly pgp(1);
728  gridpos_poly( pgp,
729  log_p_grid,
730  log(p),
731  p_interp_order );
732 
733  // Pressure interpolation weights:
734  Vector pitw;
735  pitw.resize(p_interp_order+1);
736  interpweights(pitw,pgp[0]);
737 
738 
739  // 6. We do the T and VMR interpolation for the pressure levels
740  // that are used in the pressure interpolation. (How many depends on
741  // p_interp_order.)
742 
743  // To store the interpolated result for the p_interp_order+1
744  // pressure levels:
745  Tensor3 xsec_pre_interpolated;
746  xsec_pre_interpolated.resize( p_interp_order+1, f_extent, n_species );
747 
748  for ( Index pi=0; pi<p_interp_order+1; ++pi )
749  {
750  // Index into p_grid:
751  const Index this_p_grid_index = pgp[0].idx[pi];
752 
753  // Flag for temperature interpolation, if this is not 0 we want
754  // to do T interpolation:
755  const Index do_T = n_t_pert;
756 
757  // Determine temperature grid position. This is only done if we
758  // want temperature interpolation, but the variable tgp has to
759  // be visible also outside for later use:
760  ArrayOfGridPosPoly tgp(1); // only a scalar
761  if (do_T)
762  {
763 
764  // Temperature in the atmosphere is altitude
765  // dependent. When we do the interpolation for the pressure level
766  // below and above our point, we should correct the target value of
767  // the interpolation to the altitude (pressure) difference. This
768  // ensures that there is for example no T interpolation if the
769  // desired T is right on the reference profile curve.
770  //
771  // I explicitly compared this with the old option to calculate
772  // the temperature offset relative to the temperature at
773  // this level. The performance in both cases is very
774  // similar. The reason, why I decided to keep this new
775  // version, is that it avoids the problem of needing
776  // oversized temperature perturbations if the pressure
777  // grid is coarse.
778  //
779  // No! The above approach leads to problems when combined with
780  // higher order pressure interpolation. The problem is that
781  // the reference T and VMR profiles may be very
782  // irregular. (For example the H2O profile often has a big
783  // jump near the bottom.) That sometimes leads to negative
784  // effective reference values when the reference profile is
785  // interpolated. I therefore reverted back to the original
786  // version of using the real temperature and humidity, not
787  // the interpolated one.
788 
789  // const Numeric effective_T_ref = interp(pitw,t_ref,pgp);
790  const Numeric effective_T_ref = t_ref[this_p_grid_index];
791 
792  // Convert temperature to offset from t_ref:
793  const Numeric T_offset = T - effective_T_ref;
794 
795  // cout << "T_offset = " << T_offset << endl;
796 
797  // Check that temperature offset is inside the allowed range.
798  {
799  const Numeric t_min = t_pert[0] - 0.5*(t_pert[1]-t_pert[0]);
800  const Numeric t_max = t_pert[n_t_pert-1] + 0.5*(t_pert[n_t_pert-1]-t_pert[n_t_pert-2]);
801  if ( ( T_offset > t_max ) ||
802  ( T_offset < t_min ) )
803  {
804  ostringstream os;
805  os << "Problem with gas absorption lookup table.\n"
806  << "Temperature T is outside the range covered by the lookup table.\n"
807  << "Your temperature was " << T
808  << " K at a pressure of " << p << " Pa.\n"
809  << "The temperature offset value is " << T_offset << ".\n"
810  << "The allowed range is " << t_min << " to " << t_max << ".\n"
811  << "The temperature perturbation grid range in the table is "
812  << t_pert[0] << " to " << t_pert[n_t_pert-1] << ".\n"
813  << "We allow a bit of extrapolation, but NOT SO MUCH!";
814  throw runtime_error( os.str() );
815  }
816  }
817 
818  gridpos_poly( tgp, t_pert, T_offset, t_interp_order );
819  }
820 
821  // Determine the H2O VMR grid position. We need to do this only
822  // once, since the only species who's VMR is interpolated is
823  // H2O. We do this only if there are nonlinear species, but the
824  // variable has to be visible later.
825  ArrayOfGridPosPoly vgp(1); // only a scalar
826  if (n_nls>0)
827  {
828  // Similar to the T case, we first interpolate the reference
829  // VMR to the pressure of extraction, then compare with
830  // the extraction VMR to determine the offset/fractional
831  // difference for the VMR interpolation.
832  //
833  // No! The above approach leads to problems when combined with
834  // higher order pressure interpolation. The problem is that
835  // the reference T and VMR profiles may be very
836  // irregular. (For example the H2O profile often has a big
837  // jump near the bottom.) That sometimes leads to negative
838  // effective reference values when the reference profile is
839  // interpolated. I therefore reverted back to the original
840  // version of using the real temperature and humidity, not
841  // the interpolated one.
842 
843 // const Numeric effective_vmr_ref = interp(pitw,
844 // vmrs_ref(h2o_index, Range(joker)),
845 // pgp);
846  const Numeric effective_vmr_ref = vmrs_ref(h2o_index, this_p_grid_index);
847 
848 
849  // Fractional VMR:
850  const Numeric VMR_frac = abs_vmrs[h2o_index] / effective_vmr_ref;
851 
852  // Check that VMR_frac is inside the allowed range.
853  {
854  // FIXME: This check depends on how I interpolate VMR.
855  const Numeric x_min = nls_pert[0] - 0.5*(nls_pert[1]-nls_pert[0]);
856  const Numeric x_max = nls_pert[n_nls_pert-1]
857  + 0.5*(nls_pert[n_nls_pert-1]-nls_pert[n_nls_pert-2]);
858 
859  if ( ( VMR_frac > x_max ) ||
860  ( VMR_frac < x_min ) )
861  {
862  ostringstream os;
863  os << "Problem with gas absorption lookup table.\n"
864  << "VMR for H2O (species " << h2o_index
865  << ") is outside the range covered by the lookup table.\n"
866  << "Your VMR was " << abs_vmrs[h2o_index]
867  << " at a pressure of " << p << " Pa.\n"
868  << "The reference VMR value there is " << effective_vmr_ref << "\n"
869  << "The fractional VMR relative to the reference value is "
870  << VMR_frac << ".\n"
871  << "The allowed range is " << x_min << " to " << x_max << ".\n"
872  << "The fractional VMR perturbation grid range in the table is "
873  << nls_pert[0] << " to " << nls_pert[n_nls_pert-1] << ".\n"
874  << "We allow a bit of extrapolation, but NOT SO MUCH!";
875  throw runtime_error( os.str() );
876  }
877  }
878 
879  // For now, do linear interpolation in the fractional VMR.
880  gridpos_poly( vgp, nls_pert, VMR_frac, h2o_interp_order );
881  }
882 
883  // 7. Loop species:
884  Index fpi=0;
885  for ( Index si=0; si<n_species; ++si )
886  {
887  // Flag for VMR interpolation, if this is not 0 we want to
888  // do VMR interpolation:
889  const Index do_VMR = non_linear[si];
890 
891  // We now have everything that we need to handle all
892  // different interpolation cases.
893 
894  // 8. Do the interpolation in T and/or VMR. Here are
895  // handlers for 4 different cases (all possible
896  // combinations).
897 
898  // For interpolation result.
899  // Fixed pressure level and species.
900  // Free dimension is frequency.
901  VectorView res(xsec_pre_interpolated(pi,Range(joker),si));
902 
903  if (do_T)
904  if (do_VMR)
905  {
906  // With T and VMR
907 
908  // This is a "red" 2D interpolation case.
909 
910  Vector itw;
911  itw.resize( (t_interp_order+1)*
912  (h2o_interp_order+1) );
913 
914  interpweights(itw,tgp[0],vgp[0]);
915 
916  // Get the right view on xsec:
917  ConstTensor3View this_xsec
918  = xsec( Range(joker), // Temperature range
919  Range(fpi,n_nls_pert), // VMR profile range
920  f_range, // Frequency range
921  this_p_grid_index ); // Pressure index
922 
923  // We must do the same interpolation for all
924  // frequencies. Instead of playing with interp and
925  // Views and looping the whole thing over frequency, I
926  // choose to do this explicitly here, but directly for
927  // all frequencies. This should be much more efficient.
928 
929  // Initialize result to zero:
930  res = 0;
931  Index iti = 0;
932  for ( Index r=0; r<t_interp_order+1; ++r )
933  for ( Index c=0; c<h2o_interp_order+1; ++c )
934  {
935  for ( Index f=0; f<f_extent; ++f )
936  res[f] += itw[iti] * this_xsec( tgp[0].idx[r], vgp[0].idx[c], f );
937 
938  ++iti;
939  }
940  }
941  else
942  {
943  // With T no VMR
944 
945  // This is a "red" 1D interpolation case.
946 
947  Vector itw;
948  itw.resize(t_interp_order+1);
949 
950  interpweights(itw,tgp[0]);
951 
952  // Get the right view on xsec:
953  ConstMatrixView this_xsec
954  = xsec( Range(joker), // Temperature range
955  fpi, // the matching species
956  f_range, // Frequency range
957  this_p_grid_index ); // Pressure index
958 
959  // We must do the same interpolation for all
960  // frequencies. Instead of playing with interp and
961  // Views and looping the whole thing over frequency, I
962  // choose to do this explicitly here, but directly for
963  // all frequencies. This should be much more efficient.
964 
965  // Initialize result to zero:
966  res = 0;
967  Index iti = 0;
968  for ( Index r=0; r<t_interp_order+1; ++r )
969  {
970  for ( Index f=0; f<f_extent; ++f )
971  res[f] += itw[iti] * this_xsec( tgp[0].idx[r], f );
972 
973  ++iti;
974  }
975  }
976  else
977  if (do_VMR)
978  {
979  // No T with VMR
980 
981  // This is a "red" 1D interpolation case.
982 
983  Vector itw;
984  itw.resize(h2o_interp_order+1);
985 
986  interpweights(itw,vgp[0]);
987 
988  // Get the right view on xsec:
989  ConstMatrixView this_xsec
990  = xsec( 0, // no T variations
991  Range(fpi,n_nls_pert), // VMR profile range
992  f_range, // Frequency range
993  this_p_grid_index ); // Pressure index
994 
995  // We must do the same interpolation for all
996  // frequencies. Instead of playing with interp and
997  // Views and looping the whole thing over frequency, I
998  // choose to do this explicitly here, but directly for
999  // all frequencies. This should be much more efficient.
1000 
1001  // Initialize result to zero:
1002  res = 0;
1003  Index iti = 0;
1004  for ( Index c=0; c<h2o_interp_order+1; ++c )
1005  {
1006  for ( Index f=0; f<f_extent; ++f )
1007  res[f] += itw[iti] * this_xsec( vgp[0].idx[c], f );
1008 
1009  ++iti;
1010  }
1011  }
1012  else
1013  {
1014  // No T no VMR
1015 
1016  // No need to interpolate anything here, actually.
1017  // We can copy all frequencies simultaneously, using
1018  // the range variable f_range.
1019  res = xsec(0,fpi,f_range,this_p_grid_index);
1020  }
1021 
1022  // Increase fpi. fpi marks the position of the first profile
1023  // of the current species in xsec. This is needed to find
1024  // the right subsection of xsec in the presence of nonlinear species.
1025  if (do_VMR)
1026  fpi += n_nls_pert;
1027  else
1028  fpi++;
1029 
1030  } // End of species loop
1031 
1032  // fpi should have reached the end of that dimension of xsec. Check
1033  // this with an assertion:
1034  assert( fpi==xsec.npages() );
1035 
1036  } // End of pressure index loop (below and above gp)
1037 
1038  // Now we have to interpolate between the p_interp_order+1 pressure levels
1039 
1040  // It is a "red" 1D interpolation case we are talking about here.
1041  // (But for a matrix in frequency and species.) Doing a loop over
1042  // frequency and species with an interp call inside would be
1043  // unefficient, so we do this by hand here.
1044  sga.resize(f_extent, n_species);
1045  sga = 0;
1046  for ( Index pi=0; pi<p_interp_order+1; ++pi )
1047  {
1048  // Multiply pre interpolated quantities with pressure interpolation weights:
1049  xsec_pre_interpolated(pi,Range(joker),Range(joker)) *= pitw[pi];
1050 
1051  // Add up in sga:
1052  sga += xsec_pre_interpolated(pi,Range(joker),Range(joker));
1053  }
1054 
1055  // Watch out, this is not yet the final result, we
1056  // need to multiply with the number density of the species, i.e.,
1057  // with the total number density n, times the VMR of the
1058  // species:
1059  for ( Index si=0; si<n_species; ++si )
1060  sga(Range(joker),si) *= ( n * abs_vmrs[si] );
1061 
1062  // That's it, we're done!
1063 }
1064 
1065 
1067 {
1068  return f_grid;
1069 }
1070 
1071 
1073 {
1074  return p_grid;
1075 }
1076 
1077 
1079 ostream& operator<< (ostream& os, const GasAbsLookup& /* gal */)
1080 {
1081  os << "GasAbsLookup: Output operator not implemented";
1082  return os;
1083 }
1084 
1085 
Matrix
The Matrix class.
Definition: matpackI.h:767
GasAbsLookup::GetPgrid
const Vector & GetPgrid() const
Definition: gas_abs_lookup.cc:1072
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:1728
species_index_from_species_name
Index species_index_from_species_name(String name)
Return species index for given species name.
Definition: abs_species_tags.cc:452
Tensor4::resize
void resize(Index b, Index p, Index r, Index c)
Resize function.
Definition: matpackIV.cc:1404
GasAbsLookup::GetFgrid
const Vector & GetFgrid() const
Definition: gas_abs_lookup.cc:1066
GasAbsLookup::nonlinear_species
ArrayOfIndex nonlinear_species
The species tags with non-linear treatment.
Definition: gas_abs_lookup.h:167
chk_if_increasing
void chk_if_increasing(const String &x_name, const ArrayOfIndex &x)
chk_if_increasing
Definition: check_input.cc:126
Tensor3
The Tensor3 class.
Definition: matpackIII.h:340
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:47
joker
const Joker joker
interpolation.h
Header file for interpolation.cc.
Vector::resize
void resize(Index n)
Resize function.
Definition: matpackI.cc:771
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:314
CREATE_OUT2
#define CREATE_OUT2
Definition: messages.h:207
is_size
bool is_size(ConstVectorView x, const Index &n)
Verifies that the size of x is l.
Definition: logic.cc:90
Tensor3::resize
void resize(Index p, Index r, Index c)
Resize function.
Definition: matpackIII.cc:863
GasAbsLookup
An absorption lookup table.
Definition: gas_abs_lookup.h:44
DEBUG_ONLY
#define DEBUG_ONLY(...)
Definition: arts.h:146
Array< Index >
number_density
Numeric number_density(const Numeric &p, const Numeric &t)
number_density
Definition: physics_funcs.cc:195
CREATE_OUT3
#define CREATE_OUT3
Definition: messages.h:208
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:539
abs
#define abs(x)
Definition: continua.cc:13094
ConstVectorView::nelem
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:175
GasAbsLookup::t_ref
Vector t_ref
The reference temperature profile [K].
Definition: gas_abs_lookup.h:198
VectorView
The VectorView class.
Definition: matpackI.h:373
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:393
physics_funcs.h
ConstTensor4View::ncols
Index ncols() const
Returns the number of columns.
Definition: matpackIV.cc:78
GasAbsLookup::f_grid
Vector f_grid
The frequency grid [Hz].
Definition: gas_abs_lookup.h:171
GasAbsLookup::p_grid
Vector p_grid
The pressure grid for the table [Pa].
Definition: gas_abs_lookup.h:175
Numeric
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
Verbosity
Definition: messages.h:50
ConstTensor4View::npages
Index npages() const
Returns the number of pages.
Definition: matpackIV.cc:66
ConstTensor4View::nbooks
Index nbooks() const
Returns the number of books.
Definition: matpackIV.cc:60
GasAbsLookup::nls_pert
Vector nls_pert
The vector of perturbations for the VMRs of the nonlinear species.
Definition: gas_abs_lookup.h:227
make_vector.h
The class MakeVector is a special kind of Vector that can be initialized explicitly from one or more ...
GasAbsLookup::xsec
Tensor4 xsec
Absorption cross sections.
Definition: gas_abs_lookup.h:261
Matrix::resize
void resize(Index r, Index c)
Resize function.
Definition: matpackI.cc:1549
interpolation_poly.h
Header file for interpolation_poly.cc.
ConstMatrixView
A constant view of a Matrix.
Definition: matpackI.h:591
chk_matrix_nrows
void chk_matrix_nrows(const String &x_name, ConstMatrixView x, const Index &l)
chk_matrix_nrows
Definition: check_input.cc:568
GasAbsLookup::species
ArrayOfArrayOfSpeciesTag species
The species tags for which the table is valid.
Definition: gas_abs_lookup.h:158
Range
The range class.
Definition: matpackI.h:148
operator<<
ostream & operator<<(ostream &os, const GasAbsLookup &)
Output operatior for GasAbsLookup.
Definition: gas_abs_lookup.cc:1079
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:222
GasAbsLookup::Extract
void Extract(Matrix &sga, const Index &p_interp_order, const Index &t_interp_order, const Index &h2o_interp_order, const Index &f_index, const Numeric &p, const Numeric &T, ConstVectorView abs_vmrs) const
Extract scalar gas absorption coefficients from the lookup table.
Definition: gas_abs_lookup.cc:517
GasAbsLookup::log_p_grid
Vector log_p_grid
The natural log of the pressure grid.
Definition: gas_abs_lookup.h:183
ConstTensor3View
A constant view of a Tensor3.
Definition: matpackIII.h:147
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:120
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:193
chk_size
void chk_size(const String &x_name, ConstVectorView x, const Index &c)
Runtime check for size of Vector.
Definition: check_input.cc:911
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:95
check_input.h
gridpos_poly
void gridpos_poly(ArrayOfGridPosPoly &gp, ConstVectorView old_grid, ConstVectorView new_grid, const Index order, const Numeric &extpolfac)
Set up grid positions for higher order interpolation.
Definition: interpolation_poly.cc:124
GasAbsLookup::vmrs_ref
Matrix vmrs_ref
The reference VMR profiles.
Definition: gas_abs_lookup.h:193
is_unique
bool is_unique(const ArrayOfIndex &x)
Checks if an ArrayOfIndex is unique, i.e., has no duplicate values.
Definition: logic.cc:330
GasAbsLookup::t_pert
Vector t_pert
The vector of temperature perturbations [K].
Definition: gas_abs_lookup.h:212
Vector
The Vector class.
Definition: matpackI.h:555
ConstVectorView
A constant view of a Vector.
Definition: matpackI.h:300
Array::nelem
Index nelem() const
Number of elements.
Definition: array.h:172
chk_if_decreasing
void chk_if_decreasing(const String &x_name, ConstVectorView x)
chk_if_decreasing
Definition: check_input.cc:316
interpweights
void interpweights(VectorView itw, const GridPos &tc)
Red 1D interpolation weights.
Definition: interpolation.cc:756
gas_abs_lookup.h
Declarations for the gas absorption lookup table.