00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00026 #include "arts.h"
00027 #include "jacobian.h"
00028 #include "special_interp.h"
00029 #include "physics_funcs.h"
00030
00031 ostream& operator << (ostream& os, const RetrievalQuantity& ot)
00032 {
00033 return os << "\n Main tag = " << ot.MainTag()
00034 << "\n Sub tag = " << ot.Subtag();
00035 }
00036
00037
00038
00039
00040
00042
00053 void calc_nd_field( Tensor3View& nd,
00054 const VectorView& p,
00055 const Tensor3View& t)
00056 {
00057 assert( nd.npages()==t.npages() );
00058 assert( nd.nrows()==t.nrows() );
00059 assert( nd.ncols()==t.ncols() );
00060 assert( nd.npages()==p.nelem() );
00061
00062 for (Index p_it=0; p_it<nd.npages(); p_it++)
00063 {
00064 for (Index lat_it=0; lat_it<nd.nrows(); lat_it++)
00065 {
00066 for (Index lon_it=0; lon_it<nd.ncols(); lon_it++)
00067 {
00068 nd(p_it,lat_it,lon_it) = number_density( p[p_it], t(p_it,lat_it,lon_it));
00069 }
00070 }
00071 }
00072 }
00073
00074
00075
00077
00102 bool check_retrieval_grids( ArrayOfVector& grids,
00103 ostringstream& os,
00104 const Vector& p_grid,
00105 const Vector& lat_grid,
00106 const Vector& lon_grid,
00107 const Vector& p_retr,
00108 const Vector& lat_retr,
00109 const Vector& lon_retr,
00110 const String& p_retr_name,
00111 const String& lat_retr_name,
00112 const String& lon_retr_name,
00113 const Index& dim)
00114 {
00115 if ( p_retr.nelem()==0 )
00116 {
00117 os << "The grid vector *" << p_retr_name << "* is empty,"
00118 << " at least one pressure level\n"
00119 << "should be specified.";
00120 return false;
00121 }
00122 else if( !is_decreasing( p_retr ) )
00123 {
00124 os << "The pressure grid vector *" << p_retr_name << "* is not a\n"
00125 << "strictly decreasing vector, which is required.";
00126 return false;
00127 }
00128 else if ( p_retr[0]>p_grid[0] ||
00129 p_retr[p_retr.nelem()-1]<p_grid[p_grid.nelem()-1] )
00130 {
00131 os << "The grid vector *" << p_retr_name << "* is not covered by the\n"
00132 << "corresponding atmospheric grid.";
00133 return false;
00134 }
00135 else
00136 {
00137
00138 grids[0]=p_retr;
00139 }
00140
00141 if (dim>=2)
00142 {
00143
00144 if ( lat_retr.nelem()==0 )
00145 {
00146 os << "The grid vector *" << lat_retr_name << "* is empty,"
00147 << " at least one latitude\n"
00148 << "should be specified for a 2D/3D atmosphere.";
00149 return false;
00150 }
00151 else if( !is_increasing( lat_retr ) )
00152 {
00153 os << "The latitude grid vector *" << lat_retr_name << "* is not a\n"
00154 << "strictly increasing vector, which is required.";
00155 return false;
00156 }
00157 else if ( lat_retr[0]<lat_grid[0] ||
00158 lat_retr[lat_retr.nelem()-1]>lat_grid[lat_grid.nelem()-1] )
00159 {
00160 os << "The grid vector *" << lat_retr_name << "* is not covered by the\n"
00161 << "corresponding atmospheric grid.";
00162 return false;
00163 }
00164 else
00165 {
00166
00167 grids[1]=lat_retr;
00168 }
00169 if (dim==3)
00170 {
00171
00172 if ( lon_retr.nelem()==0 )
00173 {
00174 os << "The grid vector *" << lon_retr_name << "* is empty,"
00175 << " at least one longitude\n"
00176 << "should be specified for a 3D atmosphere.";
00177 return false;
00178 }
00179 else if( !is_increasing( lon_retr ) )
00180 {
00181 os << "The longitude grid vector *" << lon_retr_name << "* is not a\n"
00182 << "strictly increasing vector, which is required.";
00183 return false;
00184 }
00185 else if ( lon_retr[0]<lon_grid[0] ||
00186 lon_retr[lon_retr.nelem()-1]>lon_grid[lon_grid.nelem()-1] )
00187 {
00188 os << "The grid vector *" << lon_retr_name << "* is not covered by the\n"
00189 << "corresponding atmospheric grid.";
00190 return false;
00191 }
00192 else
00193 {
00194
00195 grids[2]=lon_retr;
00196 }
00197 }
00198 }
00199 return true;
00200 }
00201
00202
00203
00205
00224 void get_perturbation_gridpos( ArrayOfGridPos& gp,
00225 const Vector& atm_grid,
00226 const Vector& jac_grid,
00227 const bool& is_pressure)
00228 {
00229 Index nj = jac_grid.nelem();
00230 Index na = atm_grid.nelem();
00231 Vector pert(nj+2);
00232
00233
00234 if ( is_pressure )
00235 {
00236 pert[0] = atm_grid[0]*10.0;
00237 pert[nj+1] = atm_grid[na-1]*0.1;
00238 }
00239 else
00240 {
00241 pert[0] = atm_grid[0]-1.0;
00242 pert[nj+1] = atm_grid[na-1]+1.0;
00243 }
00244 pert[Range(1,nj)] = jac_grid;
00245
00246
00247 gp.resize(na);
00248 if( is_pressure ){
00249 p2gridpos( gp, pert, atm_grid);
00250 }
00251 else
00252 {
00253 gridpos( gp, pert, atm_grid);
00254 }
00255 }
00256
00257
00258
00260
00283 void get_perturbation_limit( ArrayOfIndex& limit,
00284 const Vector& pert_grid,
00285 const Vector& atm_limit)
00286 {
00287 limit.resize(2);
00288
00289 Index na = atm_limit.nelem()-1;
00290
00291
00292
00293 Numeric inc = 1;
00294 if (is_decreasing(pert_grid))
00295 inc = -1;
00296
00297
00298
00299
00300
00301
00302 limit[0]=0;
00303 while (inc*pert_grid[limit[0]+1] < inc*atm_limit[0])
00304 {
00305 limit[0]++;
00306 }
00307
00308
00309 limit[1]=pert_grid.nelem();
00310 while (inc*pert_grid[limit[1]-1] > inc*atm_limit[na])
00311 {
00312 limit[1]--;
00313 }
00314
00315 assert(limit[1]>limit[0]);
00316
00317 }
00318
00319
00320
00322
00335 void get_perturbation_range( Range& range,
00336 const Index& index,
00337 const Index& length)
00338 {
00339 if (index==0)
00340 range = Range(index,2);
00341 else if (index==length-1)
00342 range = Range(index+1,2);
00343 else
00344 range = Range(index+1,1);
00345
00346 }
00347
00348
00349
00351
00366
00367
00368
00369 void from_dq_to_dx(
00370 MatrixView diy_dx,
00371 ConstMatrixView diy_dq,
00372 const Numeric& w )
00373 {
00374 for( Index iv=0; iv<diy_dx.nrows(); iv++ )
00375 {
00376 for( Index is=0; is<diy_dx.ncols(); is++ )
00377 { diy_dx(iv,is) += w * diy_dq(iv,is); }
00378 }
00379 }
00380
00381 void jacobian_from_path_to_rgrids(
00382 MatrixView ib_q_jacs,
00383 const Index& nbdone,
00384 const ArrayOfTensor4& diy_dq,
00385 const Index& iq,
00386 const Index& atmosphere_dim,
00387 const ArrayOfPpath& ppath_array,
00388 const RetrievalQuantity& jacobian_quantity )
00389 {
00390 assert( diy_dq.nelem() );
00391 assert( ppath_array.nelem() == diy_dq.nelem() );
00392
00393 const Index stokes_dim = diy_dq[0].ncols();
00394 const Index nf = diy_dq[0].nrows();
00395
00396
00397
00398 const Numeric extpolfac = 1.0e99;
00399
00400
00401 Vector r_grid;
00402
00403
00404
00405 Tensor3 diy_dx;
00406 bool diydx_unset = true;
00407
00408
00409 for( Index ia=0; ia<ppath_array.nelem(); ia++ )
00410 {
00411 if( ppath_array[ia].np > 1 )
00412 {
00413
00414 r_grid = jacobian_quantity.Grids()[0];
00415 Index nr1 = r_grid.nelem();
00416 ArrayOfGridPos gp_p(ppath_array[ia].np);
00417 p2gridpos( gp_p, r_grid, ppath_array[ia].p, extpolfac );
00418
00419
00420 Index nr2 = 1;
00421 ArrayOfGridPos gp_lat;
00422 if( atmosphere_dim > 1 )
00423 {
00424 gp_lat.resize(ppath_array[ia].np);
00425 r_grid = jacobian_quantity.Grids()[1];
00426 nr2 = r_grid.nelem();
00427 gridpos( gp_lat, r_grid, ppath_array[ia].pos(joker,1),
00428 extpolfac );
00429 }
00430
00431
00432 Index nr3 = 1;
00433 ArrayOfGridPos gp_lon;
00434 if( atmosphere_dim > 2 )
00435 {
00436 gp_lon.resize(ppath_array[ia].np);
00437 r_grid = jacobian_quantity.Grids()[2];
00438 nr3 = r_grid.nelem();
00439 gridpos( gp_lon, r_grid, ppath_array[ia].pos(joker,2),
00440 extpolfac );
00441 }
00442
00443
00444 if( diydx_unset )
00445 {
00446 diy_dx.resize(nr1*nr2*nr3,nf,stokes_dim);
00447 diy_dx = 0.0;
00448 diydx_unset = false;
00449 }
00450
00451
00452 if( atmosphere_dim == 1 )
00453 {
00454 for( Index ip=0; ip<ppath_array[ia].np; ip++ )
00455 {
00456 if( gp_p[ip].fd[0] < 1 )
00457 {
00458 from_dq_to_dx( diy_dx(gp_p[ip].idx,joker,joker),
00459 diy_dq[ia](iq,ip,joker,joker),
00460 gp_p[ip].fd[1] );
00461 }
00462 if( gp_p[ip].fd[0] > 0 )
00463 {
00464 from_dq_to_dx( diy_dx(gp_p[ip].idx+1,joker,joker),
00465 diy_dq[ia](iq,ip,joker,joker),
00466 gp_p[ip].fd[0] );
00467 }
00468 }
00469 }
00470
00471
00472 else if( atmosphere_dim == 2 )
00473 {
00474 for( Index ip=0; ip<ppath_array[ia].np; ip++ )
00475 {
00476 Index ix = nr1*gp_lat[ip].idx + gp_p[ip].idx;
00477
00478 from_dq_to_dx( diy_dx(ix,joker,joker),
00479 diy_dq[ia](iq,ip,joker,joker),
00480 gp_lat[ip].fd[1]*gp_p[ip].fd[1] );
00481
00482 from_dq_to_dx( diy_dx(ix+1,joker,joker),
00483 diy_dq[ia](iq,ip,joker,joker),
00484 gp_lat[ip].fd[1]*gp_p[ip].fd[0] );
00485
00486 from_dq_to_dx( diy_dx(ix+nr1,joker,joker),
00487 diy_dq[ia](iq,ip,joker,joker),
00488 gp_lat[ip].fd[0]*gp_p[ip].fd[1] );
00489
00490 from_dq_to_dx( diy_dx(ix+nr1+1,joker,joker),
00491 diy_dq[ia](iq,ip,joker,joker),
00492 gp_lat[ip].fd[0]*gp_p[ip].fd[0] );
00493 }
00494 }
00495
00496
00497 else if( atmosphere_dim == 3 )
00498 {
00499 for( Index ip=0; ip<ppath_array[ia].np; ip++ )
00500 {
00501 Index ix = nr2*nr1*gp_lon[ip].idx +
00502 nr1*gp_lat[ip].idx + gp_p[ip].idx;
00503
00504 from_dq_to_dx( diy_dx(ix,joker,joker),
00505 diy_dq[ia](iq,ip,joker,joker),
00506 gp_lon[ip].fd[1]*gp_lat[ip].fd[1]*gp_p[ip].fd[1]);
00507
00508 from_dq_to_dx( diy_dx(ix+1,joker,joker),
00509 diy_dq[ia](iq,ip,joker,joker),
00510 gp_lon[ip].fd[1]*gp_lat[ip].fd[1]*gp_p[ip].fd[0]);
00511
00512 from_dq_to_dx( diy_dx(ix+nr1,joker,joker),
00513 diy_dq[ia](iq,ip,joker,joker),
00514 gp_lon[ip].fd[1]*gp_lat[ip].fd[0]*gp_p[ip].fd[1]);
00515
00516 from_dq_to_dx( diy_dx(ix+nr1+1,joker,joker),
00517 diy_dq[ia](iq,ip,joker,joker),
00518 gp_lon[ip].fd[1]*gp_lat[ip].fd[0]*gp_p[ip].fd[0]);
00519
00520
00521 ix += nr2*nr1;
00522
00523
00524 from_dq_to_dx( diy_dx(ix,joker,joker),
00525 diy_dq[ia](iq,ip,joker,joker),
00526 gp_lon[ip].fd[0]*gp_lat[ip].fd[1]*gp_p[ip].fd[1]);
00527
00528 from_dq_to_dx( diy_dx(ix+1,joker,joker),
00529 diy_dq[ia](iq,ip,joker,joker),
00530 gp_lon[ip].fd[0]*gp_lat[ip].fd[1]*gp_p[ip].fd[0]);
00531
00532 from_dq_to_dx( diy_dx(ix+nr1,joker,joker),
00533 diy_dq[ia](iq,ip,joker,joker),
00534 gp_lon[ip].fd[0]*gp_lat[ip].fd[0]*gp_p[ip].fd[1]);
00535
00536 from_dq_to_dx( diy_dx(ix+nr1+1,joker,joker),
00537 diy_dq[ia](iq,ip,joker,joker),
00538 gp_lon[ip].fd[0]*gp_lat[ip].fd[0]*gp_p[ip].fd[0]);
00539 }
00540 }
00541 }
00542 }
00543
00544
00545 if( diydx_unset )
00546 {
00547 for( Index iv=0; iv<nf; iv++ )
00548 {
00549 const Index i0 = nbdone + iv*stokes_dim;
00550 for( Index is=0; is<stokes_dim; is++ )
00551 { ib_q_jacs(i0+is,joker) = 0.0; }
00552 }
00553 }
00554 else
00555 {
00556 for( Index iv=0; iv<nf; iv++ )
00557 {
00558 const Index i0 = nbdone + iv*stokes_dim;
00559 for( Index is=0; is<stokes_dim; is++ )
00560 { ib_q_jacs(i0+is,joker) = diy_dx(joker,iv,is); }
00561 }
00562 }
00563 }
00564
00565
00566
00568
00582 void perturbation_field_1d( VectorView field,
00583 const ArrayOfGridPos& p_gp,
00584 const Index& p_pert_n,
00585 const Range& p_range,
00586 const Numeric& size,
00587 const Index& method)
00588 {
00589
00590 Vector pert(field.nelem());
00591 Matrix itw(p_gp.nelem(),2);
00592 interpweights(itw,p_gp);
00593
00594 Vector pert_field(p_pert_n,1.0-(Numeric)method);
00595 pert_field[p_range] += size;
00596 interp( pert, itw, pert_field, p_gp);
00597 if (method==0)
00598 {
00599 field *= pert;
00600 }
00601 else
00602 {
00603 field += pert;
00604 }
00605 }
00606
00607
00608
00610
00627 void perturbation_field_2d( MatrixView field,
00628 const ArrayOfGridPos& p_gp,
00629 const ArrayOfGridPos& lat_gp,
00630 const Index& p_pert_n,
00631 const Index& lat_pert_n,
00632 const Range& p_range,
00633 const Range& lat_range,
00634 const Numeric& size,
00635 const Index& method)
00636 {
00637
00638 Matrix pert(field.nrows(),field.ncols());
00639 Tensor3 itw(p_gp.nelem(),lat_gp.nelem(),4);
00640 interpweights(itw,p_gp,lat_gp);
00641
00642 Matrix pert_field(p_pert_n,lat_pert_n,1.0-(Numeric)method);
00643 pert_field(p_range,lat_range) += size;
00644 interp( pert, itw, pert_field, p_gp, lat_gp);
00645 if (method==0)
00646 {
00647 field *= pert;
00648 }
00649 else
00650 {
00651 field += pert;
00652 }
00653 }
00654
00655
00656
00658
00678 void perturbation_field_3d( Tensor3View field,
00679 const ArrayOfGridPos& p_gp,
00680 const ArrayOfGridPos& lat_gp,
00681 const ArrayOfGridPos& lon_gp,
00682 const Index& p_pert_n,
00683 const Index& lat_pert_n,
00684 const Index& lon_pert_n,
00685 const Range& p_range,
00686 const Range& lat_range,
00687 const Range& lon_range,
00688 const Numeric& size,
00689 const Index& method)
00690 {
00691
00692 Tensor3 pert(field.npages(),field.nrows(),field.ncols());
00693 Tensor4 itw(p_gp.nelem(),lat_gp.nelem(),lon_gp.nelem(),8);
00694 interpweights(itw,p_gp,lat_gp,lon_gp);
00695
00696 Tensor3 pert_field(p_pert_n,lat_pert_n,lon_pert_n,1.0-(Numeric)method);
00697 pert_field(p_range,lat_range,lon_range) += size;
00698 interp( pert, itw, pert_field, p_gp, lat_gp, lon_gp);
00699 if (method==0)
00700 {
00701 field *= pert;
00702 }
00703 else
00704 {
00705 field += pert;
00706 }
00707 }
00708
00709
00710
00712
00725 void polynomial_basis_func(
00726 Vector& b,
00727 const Vector& x,
00728 const Index& poly_coeff )
00729 {
00730 const Index l = x.nelem();
00731
00732 assert( l > poly_coeff );
00733
00734 if( b.nelem() != l )
00735 b.resize( l );
00736
00737 if( poly_coeff == 0 )
00738 {
00739 for( Index i=0; i<l; i++ )
00740 {
00741 b[i] = 1.0;
00742 }
00743 }
00744 else
00745 {
00746 const Numeric xmin = min( x );
00747 const Numeric dx = 0.5 * ( max( x ) - xmin );
00748
00749 for( Index i=0; i<l; i++ )
00750 {
00751 b[i] = ( x[i] - xmin ) / dx - 1.0;
00752 b[i] = pow( b[i], int(poly_coeff) );
00753 }
00754
00755 b -= mean( b );
00756 }
00757 }
00758
00759