00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00026 #include <cmath>
00027 #include "gas_abs_lookup.h"
00028 #include "interpolation.h"
00029 #include "interpolation_poly.h"
00030 #include "make_vector.h"
00031 #include "logic.h"
00032 #include "check_input.h"
00033 #include "messages.h"
00034 #include "physics_funcs.h"
00035
00037
00047 void find_new_grid_in_old_grid( ArrayOfIndex& pos,
00048 ConstVectorView old_grid,
00049 ConstVectorView new_grid )
00050 {
00051 const Index n_new_grid = new_grid.nelem();
00052 const Index n_old_grid = old_grid.nelem();
00053
00054
00055 assert( n_new_grid == pos.nelem() );
00056
00057
00058
00059 const Numeric tolerance = 1;
00060
00061
00062 Index j = 0;
00063
00064
00065 for ( Index i=0; i<n_new_grid; ++i )
00066 {
00067
00068
00069
00070
00071 while ( abs(new_grid[i]-old_grid[j]) > tolerance )
00072 {
00073 ++j;
00074 if ( j>=n_old_grid )
00075 {
00076 ostringstream os;
00077 os << "Cannot find new frequency " << i
00078 << " (" << new_grid[i] << "Hz) in the lookup table frequency grid.";
00079 throw runtime_error( os.str() );
00080 }
00081 }
00082
00083 pos[i] = j;
00084 out3 << " " << new_grid[i] << " found, index = " << pos[i] << ".\n";
00085 }
00086 }
00087
00089
00117 void GasAbsLookup::Adapt( const ArrayOfArrayOfSpeciesTag& current_species,
00118 ConstVectorView current_f_grid )
00119 {
00120
00121 const Index n_current_species = current_species.nelem();
00122 const Index n_current_f_grid = current_f_grid.nelem();
00123
00124 const Index n_species = species.nelem();
00125 const Index n_nls = nonlinear_species.nelem();
00126 const Index n_nls_pert = nls_pert.nelem();
00127 const Index n_f_grid = f_grid.nelem();
00128 const Index n_p_grid = p_grid.nelem();
00129
00130 out2 << " Original table: " << n_species << " species, "
00131 << n_f_grid << " frequencies.\n"
00132 << " Adapt to: " << n_current_species << " species, "
00133 << n_current_f_grid << " frequencies.\n";
00134
00135 if ( 0 == n_nls )
00136 {
00137 out2 << " Table contains no nonlinear species.\n";
00138 }
00139
00140
00141 ArrayOfIndex non_linear(n_species,0);
00142 for ( Index s=0; s<n_nls; ++s )
00143 {
00144 non_linear[nonlinear_species[s]] = 1;
00145 }
00146
00147 if ( 0 == t_pert.nelem() )
00148 {
00149 out2 << " Table contains no temperature perturbations.\n";
00150 }
00151
00152
00153
00154
00155
00156 GasAbsLookup new_table;
00157
00158
00159
00160
00161 if ( 0 == n_species )
00162 {
00163 ostringstream os;
00164 os << "The lookup table should have at least one species.";
00165 throw runtime_error( os.str() );
00166 }
00167
00168
00169
00170 if ( !is_unique(nonlinear_species) )
00171 {
00172 ostringstream os;
00173 os << "The table must not have duplicate nonlinear species.\n"
00174 << "Value of *nonlinear_species*: " << nonlinear_species;
00175 throw runtime_error( os.str() );
00176 }
00177
00178
00179 for ( Index i=0; i<n_nls; ++i )
00180 {
00181 ostringstream os;
00182 os << "nonlinear_species[" << i << "]";
00183 chk_if_in_range( os.str(),
00184 nonlinear_species[i],
00185 0,
00186 n_species-1 );
00187 }
00188
00189
00190 chk_if_increasing( "f_grid", f_grid );
00191
00192
00193 chk_if_decreasing( "p_grid", p_grid );
00194
00195
00196 chk_matrix_nrows( "vmrs_ref", vmrs_ref, n_species );
00197 chk_matrix_ncols( "vmrs_ref", vmrs_ref, n_p_grid );
00198
00199
00200 chk_vector_length( "t_ref", t_ref, n_p_grid );
00201
00202
00203
00204
00205
00206
00207
00208 if ( 0 == n_nls )
00209 {
00210 chk_vector_length( "nls_pert", nls_pert, 0 );
00211 }
00212 else
00213 {
00214 if ( 0 == n_nls_pert )
00215 {
00216 ostringstream os;
00217 os << "The vector nls_pert should contain the perturbations\n"
00218 << "for the nonlinear species, but it is empty.";
00219 throw runtime_error( os.str() );
00220 }
00221 }
00222
00223
00224
00225
00226
00227
00228
00229
00230 if ( 0==n_nls )
00231 {
00232 if ( 0==t_pert.nelem() )
00233 {
00234
00235
00236
00237
00238
00239
00240 chk_size( "xsec", xsec,
00241 1,
00242 n_species,
00243 n_f_grid,
00244 n_p_grid );
00245 }
00246 else
00247 {
00248
00249
00250
00251
00252
00253
00254 chk_size( "xsec", xsec,
00255 t_pert.nelem(),
00256 n_species,
00257 n_f_grid,
00258 n_p_grid );
00259 }
00260 }
00261 else
00262 {
00263
00264
00265
00266
00267
00268
00269 Index a = t_pert.nelem();
00270 Index b = n_species
00271 + n_nls * ( n_nls_pert - 1 );
00272 Index c = n_f_grid;
00273 Index d = n_p_grid;
00274
00275 chk_size( "xsec", xsec, a, b, c, d );
00276 }
00277
00278
00279
00280
00281 ArrayOfIndex original_spec_pos_in_xsec(n_species);
00282 for (Index i=0,sp=0; i<n_species; ++i)
00283 {
00284 original_spec_pos_in_xsec[i] = sp;
00285 if (non_linear[i]) sp += n_nls_pert;
00286 else sp += 1;
00287 }
00288
00289
00290
00291
00292
00293
00294 if ( 0==n_current_species )
00295 {
00296 ostringstream os;
00297 os << "The list of current species should not be empty.";
00298 throw runtime_error( os.str() );
00299 }
00300
00301
00302 chk_if_increasing( "current_f_grid", current_f_grid );
00303
00304
00305
00306
00307
00308 ArrayOfIndex i_current_species(n_current_species);
00309 out3 << " Looking for species in lookup table:\n";
00310 for ( Index i=0; i<n_current_species; ++i )
00311 {
00312 out3 << " " << get_tag_group_name( current_species[i] ) << ": ";
00313
00314
00315
00316 i_current_species[i] =
00317 chk_contains( "species", species, current_species[i] );
00318 out3 << "found.\n";
00319 }
00320
00321
00322 Index n_current_nonlinear_species = 0;
00323 ArrayOfIndex current_non_linear(n_current_species,0);
00324
00325
00326
00327
00328 out3 << " Finding out which of the current species are nonlinear:\n";
00329 for ( Index i=0; i<n_current_species; ++i )
00330 {
00331
00332 if (non_linear[i_current_species[i]])
00333 {
00334 out3 << " " << get_tag_group_name( current_species[i] ) << "\n";
00335
00336 current_non_linear[i] = 1;
00337 ++n_current_nonlinear_species;
00338 }
00339 }
00340
00341
00342
00343
00344
00345
00346
00347
00348 ArrayOfIndex i_current_f_grid(n_current_f_grid);
00349 out3 << " Looking for Frequencies in lookup table:\n";
00350
00351
00352
00353
00354 find_new_grid_in_old_grid( i_current_f_grid,
00355 f_grid,
00356 current_f_grid );
00357
00358
00359
00360
00361
00362
00363 new_table.species.resize( n_current_species );
00364 for ( Index i=0; i<n_current_species; ++i )
00365 {
00366 new_table.species[i] = species[i_current_species[i]];
00367
00368
00369 if (current_non_linear[i])
00370 new_table.nonlinear_species.push_back( i );
00371 }
00372
00373
00374 new_table.f_grid.resize( n_current_f_grid );
00375 for ( Index i=0; i<n_current_f_grid; ++i )
00376 {
00377 new_table.f_grid[i] = f_grid[i_current_f_grid[i]];
00378 }
00379
00380
00381
00382 new_table.p_grid = p_grid;
00383
00384
00385 new_table.vmrs_ref.resize( n_current_species,
00386 n_p_grid );
00387 for ( Index i=0; i<n_current_species; ++i )
00388 {
00389 new_table.vmrs_ref( i,
00390 Range(joker) )
00391 = vmrs_ref( i_current_species[i],
00392 Range(joker) );
00393 }
00394
00395
00396
00397 new_table.t_ref = t_ref;
00398
00399
00400
00401 new_table.t_pert = t_pert;
00402
00403
00404
00405 if ( 0 != new_table.nonlinear_species.nelem() )
00406 {
00407
00408 new_table.nls_pert = nls_pert;
00409 }
00410
00411
00412 new_table.xsec.resize( xsec.nbooks(),
00413 n_current_species+n_current_nonlinear_species*(n_nls_pert-1),
00414 n_current_f_grid,
00415 xsec.ncols() );
00416
00417
00418
00419
00420
00421
00422 for ( Index i_s=0,sp=0; i_s<n_current_species; ++i_s )
00423 {
00424
00425 Index n_v;
00426 if (current_non_linear[i_s])
00427 n_v = n_nls_pert;
00428 else
00429 n_v = 1;
00430
00431
00432
00433
00434
00435 for ( Index i_f=0; i_f<n_current_f_grid; ++i_f )
00436 {
00437 new_table.xsec( Range(joker),
00438 Range(sp,n_v),
00439 i_f,
00440 Range(joker) )
00441 =
00442 xsec( Range(joker),
00443 Range(original_spec_pos_in_xsec[i_current_species[i_s]],n_v),
00444 i_current_f_grid[i_f],
00445 Range(joker) );
00446
00447
00448
00449
00450
00451
00452 }
00453
00454 sp += n_v;
00455 }
00456
00457
00458
00459 *this = new_table;
00460
00461
00462 log_p_grid.resize( n_p_grid );
00463 transform( log_p_grid,
00464 log,
00465 p_grid );
00466 }
00467
00469
00509 void GasAbsLookup::Extract( Matrix& sga,
00510 const Index& p_interp_order,
00511 const Index& t_interp_order,
00512 const Index& h2o_interp_order,
00513 const Index& f_index,
00514 const Numeric& p,
00515 const Numeric& T,
00516 ConstVectorView abs_vmrs ) const
00517 {
00518
00519
00520
00521 const Index n_species = species.nelem();
00522
00523
00524 const Index n_nls = nonlinear_species.nelem();
00525
00526
00527 const Index n_f_grid = f_grid.nelem();
00528
00529
00530 const Index n_p_grid = p_grid.nelem();
00531
00532
00533 const Index n_t_pert = t_pert.nelem();
00534
00535
00536 const Index n_nls_pert = nls_pert.nelem();
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548 Index h2o_index = -1;
00549 if (n_nls>0)
00550 {
00551 h2o_index = find_first_species_tg( species,
00552 species_index_from_species_name("H2O") );
00553
00554
00555
00556
00557
00558 if ( h2o_index == -1 )
00559 {
00560 ostringstream os;
00561 os << "With nonlinear species, at least one species must be a H2O species.";
00562 throw runtime_error( os.str() );
00563 }
00564 }
00565
00566
00567 assert( is_size( vmrs_ref, n_species, n_p_grid) );
00568
00569
00570 assert( is_size( t_ref, n_p_grid) );
00571
00572
00573 DEBUG_ONLY({
00574 Index a,b,c,d;
00575 if ( 0 == n_t_pert ) a = 1;
00576 else a = n_t_pert;
00577 b = n_species + n_nls * ( n_nls_pert - 1 );
00578 c = n_f_grid;
00579 d = n_p_grid;
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590 assert( is_size( xsec, a, b, c, d ) );
00591 })
00592
00593
00594 if (log_p_grid.nelem() != n_p_grid)
00595 {
00596 ostringstream os;
00597 os << "The lookup table internal variable log_p_grid is not initialized.\n"
00598 << "Use the abs_lookupAdapt method!";
00599 throw runtime_error( os.str() );
00600 }
00601
00602
00603
00604
00605
00606
00607
00608 if ( (n_p_grid < p_interp_order+1) )
00609 {
00610 ostringstream os;
00611 os << "The number of pressure grid points in the table ("
00612 << n_p_grid << ") is not enough for the desired order of interpolation ("
00613 << p_interp_order << ").";
00614 throw runtime_error( os.str() );
00615 }
00616
00617 if ( (n_nls_pert != 0) && (n_nls_pert < h2o_interp_order+1) )
00618 {
00619 ostringstream os;
00620 os << "The number of humidity perturbation grid points in the table ("
00621 << n_nls_pert << ") is not enough for the desired order of interpolation ("
00622 << h2o_interp_order << ").";
00623 throw runtime_error( os.str() );
00624 }
00625
00626 if ( (n_t_pert != 0) && (n_t_pert < t_interp_order+1) )
00627 {
00628 ostringstream os;
00629 os << "The number of temperature perturbation grid points in the table ("
00630 << n_t_pert << ") is not enough for the desired order of interpolation ("
00631 << t_interp_order << ").";
00632 throw runtime_error( os.str() );
00633 }
00634
00635
00636
00637
00638
00639 if ( !is_size( abs_vmrs, n_species ) )
00640 {
00641 ostringstream os;
00642 os << "Number of species in lookup table does not match number\n"
00643 << "of species for which you want to extract absorption.\n"
00644 << "Have you used abs_lookupAdapt?";
00645 throw runtime_error( os.str() );
00646 }
00647
00648
00649
00650
00651
00652 Index f_start, f_extent;
00653 if ( f_index < 0 )
00654 {
00655
00656
00657 f_start = 0;
00658 f_extent = n_f_grid;
00659 }
00660 else
00661 {
00662
00663
00664
00665 if ( f_index >= n_f_grid )
00666 {
00667 ostringstream os;
00668 os << "Problem with gas absorption lookup table.\n"
00669 << "Frequency index f_index is too high, you have " << f_index
00670 << ", the largest allowed value is " << n_f_grid-1 << ".";
00671 throw runtime_error( os.str() );
00672 }
00673
00674 f_start = f_index;
00675 f_extent = 1;
00676 }
00677 const Range f_range(f_start,f_extent);
00678
00679
00680
00681 ArrayOfIndex non_linear(n_species,0);
00682 for ( Index s=0; s<n_nls; ++s )
00683 {
00684 non_linear[nonlinear_species[s]] = 1;
00685 }
00686
00687
00688
00689
00690
00691 const Numeric n = number_density( p, T );
00692
00693
00694
00695
00696
00697 {
00698 const Numeric p_max = p_grid[0] + 0.5*(p_grid[0]-p_grid[1]);
00699 const Numeric p_min = p_grid[n_p_grid-1] - 0.5*(p_grid[n_p_grid-2]-p_grid[n_p_grid-1]);
00700 if ( ( p > p_max ) ||
00701 ( p < p_min ) )
00702 {
00703 ostringstream os;
00704 os << "Problem with gas absorption lookup table.\n"
00705 << "Pressure p is outside the range covered by the lookup table.\n"
00706 << "Your p value is " << p << " Pa.\n"
00707 << "The allowed range is " << p_min << " to " << p_max << ".\n"
00708 << "The pressure grid range in the table is " << p_grid[n_p_grid-1]
00709 << " to " << p_grid[0] << ".\n"
00710 << "We allow a bit of extrapolation, but NOT SO MUCH!";
00711 throw runtime_error( os.str() );
00712 }
00713 }
00714
00715
00716
00717
00718
00719 GridPosPoly pgp;
00720 gridpos_poly( pgp,
00721 log_p_grid,
00722 log(p),
00723 p_interp_order );
00724
00725
00726 Vector pitw(p_interp_order+1);
00727 interpweights(pitw,pgp);
00728
00729
00730
00731
00732
00733
00734
00735
00736 Tensor3 xsec_pre_interpolated( p_interp_order+1, f_extent, n_species );
00737
00738 for ( Index pi=0; pi<p_interp_order+1; ++pi )
00739 {
00740
00741 const Index this_p_grid_index = pgp.idx[pi];
00742
00743
00744
00745 const Index do_T = n_t_pert;
00746
00747
00748
00749
00750 GridPosPoly tgp;
00751 if (do_T)
00752 {
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780 const Numeric effective_T_ref = t_ref[this_p_grid_index];
00781
00782
00783 const Numeric T_offset = T - effective_T_ref;
00784
00785
00786
00787
00788 {
00789 const Numeric t_min = t_pert[0] - 0.5*(t_pert[1]-t_pert[0]);
00790 const Numeric t_max = t_pert[n_t_pert-1] + 0.5*(t_pert[n_t_pert-1]-t_pert[n_t_pert-2]);
00791 if ( ( T_offset > t_max ) ||
00792 ( T_offset < t_min ) )
00793 {
00794 ostringstream os;
00795 os << "Problem with gas absorption lookup table.\n"
00796 << "Temperature T is outside the range covered by the lookup table.\n"
00797 << "Your temperature was " << T
00798 << " K at a pressure of " << p << " Pa.\n"
00799 << "The temperature offset value is " << T_offset << ".\n"
00800 << "The allowed range is " << t_min << " to " << t_max << ".\n"
00801 << "The temperature perturbation grid range in the table is "
00802 << t_pert[0] << " to " << t_pert[n_t_pert-1] << ".\n"
00803 << "We allow a bit of extrapolation, but NOT SO MUCH!";
00804 throw runtime_error( os.str() );
00805 }
00806 }
00807
00808 gridpos_poly( tgp, t_pert, T_offset, t_interp_order );
00809 }
00810
00811
00812
00813
00814
00815 GridPosPoly vgp;
00816 if (n_nls>0)
00817 {
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836 const Numeric effective_vmr_ref = vmrs_ref(h2o_index, this_p_grid_index);
00837
00838
00839
00840 const Numeric VMR_frac = abs_vmrs[h2o_index] / effective_vmr_ref;
00841
00842
00843 {
00844
00845 const Numeric x_min = nls_pert[0] - 0.5*(nls_pert[1]-nls_pert[0]);
00846 const Numeric x_max = nls_pert[n_nls_pert-1]
00847 + 0.5*(nls_pert[n_nls_pert-1]-nls_pert[n_nls_pert-2]);
00848
00849 if ( ( VMR_frac > x_max ) ||
00850 ( VMR_frac < x_min ) )
00851 {
00852 ostringstream os;
00853 os << "Problem with gas absorption lookup table.\n"
00854 << "VMR for H2O (species " << h2o_index
00855 << ") is outside the range covered by the lookup table.\n"
00856 << "Your VMR was " << abs_vmrs[h2o_index]
00857 << " at a pressure of " << p << " Pa.\n"
00858 << "The reference VMR value there is " << effective_vmr_ref << "\n"
00859 << "The fractional VMR relative to the reference value is "
00860 << VMR_frac << ".\n"
00861 << "The allowed range is " << x_min << " to " << x_max << ".\n"
00862 << "The fractional VMR perturbation grid range in the table is "
00863 << nls_pert[0] << " to " << nls_pert[n_nls_pert-1] << ".\n"
00864 << "We allow a bit of extrapolation, but NOT SO MUCH!";
00865 throw runtime_error( os.str() );
00866 }
00867 }
00868
00869
00870 gridpos_poly( vgp, nls_pert, VMR_frac, h2o_interp_order );
00871 }
00872
00873
00874 Index fpi=0;
00875 for ( Index si=0; si<n_species; ++si )
00876 {
00877
00878
00879 const Index do_VMR = non_linear[si];
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891 VectorView res(xsec_pre_interpolated(pi,Range(joker),si));
00892
00893 if (do_T)
00894 if (do_VMR)
00895 {
00896
00897
00898
00899
00900 Vector itw( (t_interp_order+1)*
00901 (h2o_interp_order+1) );
00902
00903 interpweights(itw,tgp,vgp);
00904
00905
00906 ConstTensor3View this_xsec
00907 = xsec( Range(joker),
00908 Range(fpi,n_nls_pert),
00909 f_range,
00910 this_p_grid_index );
00911
00912
00913
00914
00915
00916
00917
00918
00919 res = 0;
00920 Vector res_dummy(f_extent);
00921 Index iti = 0;
00922 for ( Index r=0; r<t_interp_order+1; ++r )
00923 for ( Index c=0; c<h2o_interp_order+1; ++c )
00924 {
00925 res_dummy = this_xsec( tgp.idx[r], vgp.idx[c], Range(joker) );
00926 res_dummy *= itw[iti];
00927
00928 res += res_dummy;
00929 ++iti;
00930 }
00931 }
00932 else
00933 {
00934
00935
00936
00937
00938 Vector itw(t_interp_order+1);
00939
00940 interpweights(itw,tgp);
00941
00942
00943 ConstMatrixView this_xsec
00944 = xsec( Range(joker),
00945 fpi,
00946 f_range,
00947 this_p_grid_index );
00948
00949
00950
00951
00952
00953
00954
00955
00956 res = 0;
00957 Vector res_dummy(f_extent);
00958 Index iti = 0;
00959 for ( Index r=0; r<t_interp_order+1; ++r )
00960 {
00961 res_dummy = this_xsec( tgp.idx[r], Range(joker) );
00962 res_dummy *= itw[iti];
00963
00964 res += res_dummy;
00965 ++iti;
00966 }
00967 }
00968 else
00969 if (do_VMR)
00970 {
00971
00972
00973
00974
00975 Vector itw(h2o_interp_order+1);
00976
00977 interpweights(itw,vgp);
00978
00979
00980 ConstMatrixView this_xsec
00981 = xsec( 0,
00982 Range(fpi,n_nls_pert),
00983 f_range,
00984 this_p_grid_index );
00985
00986
00987
00988
00989
00990
00991
00992
00993 res = 0;
00994 Vector res_dummy(f_extent);
00995 Index iti = 0;
00996 for ( Index c=0; c<h2o_interp_order+1; ++c )
00997 {
00998 res_dummy = this_xsec( vgp.idx[c], Range(joker) );
00999 res_dummy *= itw[iti];
01000
01001 res += res_dummy;
01002 ++iti;
01003 }
01004 }
01005 else
01006 {
01007
01008
01009
01010
01011
01012 res = xsec(0,fpi,f_range,this_p_grid_index);
01013 }
01014
01015
01016
01017
01018 if (do_VMR)
01019 fpi += n_nls_pert;
01020 else
01021 fpi++;
01022
01023 }
01024
01025
01026
01027 assert( fpi==xsec.npages() );
01028
01029 }
01030
01031
01032
01033
01034
01035
01036
01037 sga.resize(f_extent, n_species);
01038 sga = 0;
01039 for ( Index pi=0; pi<p_interp_order+1; ++pi )
01040 {
01041
01042 xsec_pre_interpolated(pi,Range(joker),Range(joker)) *= pitw[pi];
01043
01044
01045 sga += xsec_pre_interpolated(pi,Range(joker),Range(joker));
01046 }
01047
01048
01049
01050
01051
01052 for ( Index si=0; si<n_species; ++si )
01053 sga(Range(joker),si) *= ( n * abs_vmrs[si] );
01054
01055
01056 }
01057
01058
01059 void GasAbsLookup::GetFgrid( Vector& f ) const
01060 {
01061 f.resize( f_grid.nelem() );
01062 f = f_grid;
01063 }
01064
01065
01066 void GasAbsLookup::GetPgrid( Vector& p ) const
01067 {
01068 p.resize( p_grid.nelem() );
01069 p = p_grid;
01070 }
01071
01072
01074 ostream& operator<< (ostream& os, const GasAbsLookup& )
01075 {
01076 os << "GasAbsLookup: Output operator not implemented";
01077 return os;
01078 }
01079
01080