00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00039
00040
00041
00042
00043 #include <cmath>
00044 #include <stdexcept>
00045 #include "agenda_class.h"
00046 #include "array.h"
00047 #include "arts_omp.h"
00048 #include "auto_md.h"
00049 #include "check_input.h"
00050 #include "math_funcs.h"
00051 #include "messages.h"
00052 #include "mystring.h"
00053 #include "logic.h"
00054 #include "poly_roots.h"
00055 #include "ppath.h"
00056 #include "refraction.h"
00057 #include "special_interp.h"
00058
00059 extern const Numeric DEG2RAD;
00060 extern const Numeric RAD2DEG;
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072 #ifdef USE_DOUBLE
00073 const double RTOL = 1e-2;
00074 #else
00075 const double RTOL = 10;
00076 #endif
00077
00078
00079
00080
00081 #ifdef USE_DOUBLE
00082 const double LATLONTOL = 1e-11;
00083 #else
00084 const double LATLONTOL = 1e-6;
00085 #endif
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095 #ifdef USE_DOUBLE
00096 const double ANGTOL = 1e-7;
00097 #else
00098 const double ANGTOL = 1e-4;
00099 #endif
00100
00101
00102
00103
00104
00105 const double POLELAT = 89.9999;
00106
00107
00108
00109
00110
00111
00112
00114
00126 double geometrical_ppc( const double& r, const double& za )
00127 {
00128 assert( r > 0 );
00129 assert( abs(za) <= 180 );
00130
00131 return r * sin( DEG2RAD * abs(za) );
00132 }
00133
00134
00135
00137
00155 double geompath_za_at_r(
00156 const double& ppc,
00157 const double& a_za,
00158 const double& r )
00159 {
00160 assert( ppc >= 0 );
00161 assert( abs(a_za) <= 180 );
00162 assert( r >= ppc - RTOL );
00163
00164 if( r > ppc )
00165 {
00166 double za = RAD2DEG * asin( ppc / r );
00167 if( abs(a_za) > 90 )
00168 { za = 180 - za; }
00169 if( a_za < 0 )
00170 { za = -za; }
00171 return za;
00172 }
00173 else
00174 {
00175 return 90;
00176 }
00177 }
00178
00179
00180
00182
00195 double geompath_r_at_za(
00196 const double& ppc,
00197 const double& za )
00198 {
00199 assert( ppc >= 0 );
00200 assert( abs(za) <= 180 );
00201
00202 return ppc / sin( DEG2RAD * abs(za) );
00203 }
00204
00205
00206
00208
00223 double geompath_lat_at_za(
00224 const double& za0,
00225 const double& lat0,
00226 const double& za )
00227 {
00228 assert( abs(za0) <= 180 );
00229 assert( abs(za) <= 180 );
00230 assert( ( za0 >= 0 && za >= 0 ) || ( za0 < 0 && za < 0 ) );
00231
00232 return lat0 + za0 - za;
00233 }
00234
00235
00236
00238
00251 double geompath_l_at_r(
00252 const double& ppc,
00253 const double& r )
00254 {
00255 assert( ppc >= 0 );
00256 assert( r >= ppc - RTOL );
00257
00258 if( r > ppc )
00259 { return sqrt( r*r - ppc*ppc ); }
00260 else
00261 { return 0; }
00262 }
00263
00264
00265
00267
00280 double geompath_r_at_l(
00281 const double& ppc,
00282 const double& l )
00283 {
00284 assert( ppc >= 0 );
00285 assert( l >= 0 );
00286
00287 return sqrt( l*l + ppc*ppc );
00288 }
00289
00290
00291
00293
00305 double geompath_r_at_lat(
00306 const double& ppc,
00307 const double& lat0,
00308 const double& za0,
00309 const double& lat )
00310 {
00311 assert( ppc >= 0 );
00312 assert( abs(za0) <= 180 );
00313 assert( ( za0 >= 0 && lat >= lat0 ) || ( za0 <= 0 && lat <= lat0 ) );
00314
00315
00316 const double za = za0 + lat0 -lat;
00317
00318 return geompath_r_at_za( ppc, za );
00319 }
00320
00321
00322
00324
00345 void geompath_from_r1_to_r2(
00346 Vector& r,
00347 Vector& lat,
00348 Vector& za,
00349 double& lstep,
00350 const double& ppc,
00351 const double& r1,
00352 const double& lat1,
00353 const double& za1,
00354 const double& r2,
00355 const double& lmax )
00356 {
00357
00358 const double l1 = geompath_l_at_r( ppc, r1 );
00359 const double l2 = geompath_l_at_r( ppc, r2 );
00360
00361
00362 Index n;
00363 if( lmax > 0 )
00364 {
00365
00366 n = Index( ceil( abs( l2 - l1 ) / lmax ) );
00367
00368
00369 if( n < 1 )
00370 { n = 1; }
00371 }
00372 else
00373 { n = 1; }
00374
00375
00376 lstep = ( l2 - l1 ) / (double)n;
00377
00378
00379 r.resize(n+1);
00380 lat.resize(n+1);
00381 za.resize(n+1);
00382 r[0] = r1;
00383 lat[0] = lat1;
00384 za[0] = za1;
00385
00386
00387 for( Index i=1; i<n; i++ )
00388 {
00389 r[i] = geompath_r_at_l( ppc, l1 + lstep * (double)i );
00390 za[i] = geompath_za_at_r( ppc, za1, r[i] );
00391 }
00392
00393
00394 r[n] = r2;
00395 za[n] = geompath_za_at_r( ppc, za1, r2 );
00396
00397
00398 if( abs(za1) < ANGTOL || abs(za1) > 180-ANGTOL )
00399 { za = za1; }
00400
00401
00402 for( Index i=1; i<=n; i++ )
00403 { lat[i] = geompath_lat_at_za( za1, lat1, za[i] ); }
00404
00405
00406 lstep = abs( lstep );
00407 }
00408
00409
00410
00412
00428 double za_geom2other_point(
00429 const double& r1,
00430 const double& lat1,
00431 const double& r2,
00432 const double& lat2 )
00433 {
00434 if( lat2 == lat1 )
00435 {
00436 if( r1 <= r2 )
00437 { return 0; }
00438 else
00439 { return 180; }
00440 }
00441 else
00442 {
00443
00444 const double dlat = abs( lat2 - lat1 );
00445
00446
00447
00448 double za = dlat + RAD2DEG * asin( r1 * sin( DEG2RAD * dlat ) /
00449 sqrt( r1*r1 + r2*r2 - 2 * r1 * r2 * cos( DEG2RAD * dlat ) ) );
00450
00451
00452 if( lat2 < lat1 )
00453 { za = -za; }
00454
00455 return za;
00456 }
00457 }
00458
00459
00460
00461
00462
00463
00464
00465
00466
00468
00485 void sph2cart(
00486 double& x,
00487 double& y,
00488 double& z,
00489 const double& r,
00490 const double& lat,
00491 const double& lon )
00492 {
00493 assert( r > 0 );
00494 assert( abs( lat ) <= 90 );
00495 assert( abs( lon ) <= 360 );
00496
00497 const double latrad = DEG2RAD * lat;
00498 const double lonrad = DEG2RAD * lon;
00499
00500 x = r * cos( latrad );
00501 z = x * sin( lonrad );
00502 x = x * cos( lonrad );
00503 y = r * sin( latrad );
00504 }
00505
00506
00507
00509
00522 void cart2sph(
00523 double& r,
00524 double& lat,
00525 double& lon,
00526 const double& x,
00527 const double& y,
00528 const double& z )
00529 {
00530 r = sqrt( x*x + y*y + z*z );
00531 lat = RAD2DEG * asin( y / r );
00532 lon = RAD2DEG * atan2( z, x );
00533 }
00534
00535
00536
00538
00562 void poslos2cart(
00563 double& x,
00564 double& y,
00565 double& z,
00566 double& dx,
00567 double& dy,
00568 double& dz,
00569 const double& r,
00570 const double& lat,
00571 const double& lon,
00572 const double& za,
00573 const double& aa )
00574 {
00575
00576
00577
00578 if( abs( lat ) > POLELAT )
00579 {
00580 const double s = sign( lat );
00581
00582 x = 0;
00583 z = 0;
00584 y = s * r;
00585
00586 dy = s * cos( DEG2RAD * za );
00587 dx = sin( DEG2RAD * za );
00588 dz = dx * sin( DEG2RAD * aa );
00589 dx = dx * cos( DEG2RAD * aa );
00590 }
00591
00592 else
00593 {
00594 const double latrad = DEG2RAD * lat;
00595 const double lonrad = DEG2RAD * lon;
00596 const double zarad = DEG2RAD * za;
00597 const double aarad = DEG2RAD * aa;
00598
00599 sph2cart( x, y, z, r, lat, lon );
00600
00601 const double coslat = cos( latrad );
00602 const double sinlat = sin( latrad );
00603 const double coslon = cos( lonrad );
00604 const double sinlon = sin( lonrad );
00605 const double cosza = cos( zarad );
00606 const double sinza = sin( zarad );
00607 const double cosaa = cos( aarad );
00608 const double sinaa = sin( aarad );
00609
00610 const double dr = cosza;
00611 const double dlat = sinza * cosaa;
00612 const double dlon = sinza * sinaa / coslat;
00613
00614 dx = coslat*coslon * dr - sinlat*coslon * dlat - coslat*sinlon * dlon;
00615 dy = sinlat * dr + coslat * dlat;
00616 dz = coslat*sinlon * dr - sinlat*sinlon * dlat + coslat*coslon * dlon;
00617 }
00618 }
00619
00620
00621
00623
00647 void cart2poslos(
00648 double& r,
00649 double& lat,
00650 double& lon,
00651 double& za,
00652 double& aa,
00653 const double& x,
00654 const double& y,
00655 const double& z,
00656 const double& dx,
00657 const double& dy,
00658 const double& dz )
00659 {
00660
00661 assert( abs( sqrt( dx*dx + dy*dy + dz*dz ) - 1 ) < 1e-6 );
00662
00663
00664 cart2sph( r, lat, lon, x, y, z );
00665
00666
00667 const double coslat = cos( DEG2RAD * lat );
00668 const double sinlat = sin( DEG2RAD * lat );
00669 const double coslon = cos( DEG2RAD * lon );
00670 const double sinlon = sin( DEG2RAD * lon );
00671 const double dr = coslat*coslon*dx + sinlat*dy + coslat*sinlon*dz;
00672 const double dlat = -sinlat*coslon/r*dx + coslat/r*dy -sinlat*sinlon/r*dz;
00673 const double dlon = -sinlon/coslat/r*dx + coslon/coslat/r*dz;
00674
00675
00676 za = RAD2DEG * acos( dr );
00677
00678 if( za < ANGTOL || za > 180-ANGTOL )
00679 { aa = 0; }
00680
00681 else if( abs( lat ) <= POLELAT )
00682 {
00683 aa = RAD2DEG * acos( r * dlat / sin( DEG2RAD * za ) );
00684
00685 if( isnan( aa ) )
00686 {
00687 if( dlat >= 0 )
00688 { aa = 0; }
00689 else
00690 { aa = 180; }
00691 }
00692 else if( dlon < 0 )
00693 { aa = -aa; }
00694 }
00695
00696
00697
00698 else
00699 { aa = RAD2DEG * atan2( dz, dx ); }
00700 }
00701
00702
00703
00705
00730 void resolve_lon(
00731 double& lon,
00732 const double& lon5,
00733 const double& lon6 )
00734 {
00735 assert( lon6 >= lon5 );
00736 assert( ( lon6 - lon5 ) < 360 );
00737
00738 if( lon < lon5 || lon > lon6 )
00739 {
00740 const double meanlon = ( lon5 + lon6 ) / 2;
00741 const double diff0 = abs( meanlon - lon );
00742
00743 if( abs( lon + 360 - meanlon ) < diff0 )
00744 { lon += 360; }
00745 else if( abs( lon - 360 - meanlon ) < diff0 )
00746 { lon -= 360; }
00747 }
00748 }
00749 #ifndef USE_DOUBLE
00750 void resolve_lon(
00751 float& lon,
00752 const double& lon5,
00753 const double& lon6 )
00754 {
00755 assert( lon6 >= lon5 );
00756 assert( ( lon6 - lon5 ) < 360 );
00757
00758 if( lon < lon5 || lon > lon6 )
00759 {
00760 const double meanlon = ( lon5 + lon6 ) / 2;
00761 const double diff0 = abs( meanlon - lon );
00762
00763 if( abs( lon + 360 - meanlon ) < diff0 )
00764 { lon += 360; }
00765 else if( abs( lon - 360 - meanlon ) < diff0 )
00766 { lon -= 360; }
00767 }
00768 }
00769 #endif
00770
00771
00772
00774
00793 void geompath_tanpos_3d(
00794 double& r_tan,
00795 double& lat_tan,
00796 double& lon_tan,
00797 double& l_tan,
00798 const double& r,
00799 const double& lat,
00800 const double& lon,
00801 const double& za,
00802 const double& aa,
00803 const double& ppc )
00804 {
00805 assert( za >= 90 );
00806 assert( r >= ppc );
00807
00808 double x, y, z, dx, dy, dz;
00809
00810 poslos2cart( x, y, z, dx, dy, dz, r, lat, lon, za, aa );
00811
00812 l_tan = sqrt( r*r - ppc*ppc );
00813
00814 cart2sph( r_tan, lat_tan, lon_tan, x+dx*l_tan, y+dy*l_tan, z+dz*l_tan );
00815 }
00816
00817
00818
00819
00820
00821
00822
00823
00824
00826
00851 double plevel_slope_2d(
00852 ConstVectorView lat_grid,
00853 ConstVectorView r_geoid,
00854 ConstVectorView z_surf,
00855 const GridPos& gp,
00856 const double& za )
00857 {
00858 Index i1 = gridpos2gridrange( gp, abs( za ) >= 0 );
00859 const double r1 = r_geoid[i1] + z_surf[i1];
00860 const double r2 = r_geoid[i1+1] + z_surf[i1+1];
00861 return ( r2 - r1 ) / ( lat_grid[i1+1] - lat_grid[i1] );
00862 }
00863
00864
00865
00867
00884 double plevel_slope_2d(
00885 const double& lat1,
00886 const double& lat2,
00887 const double& r1,
00888 const double& r2 )
00889 {
00890 return ( r2 - r1 ) / ( lat2 -lat1 );
00891 }
00892
00893
00894
00896
00915 double rsurf_at_latlon(
00916 const double& lat1,
00917 const double& lat3,
00918 const double& lon5,
00919 const double& lon6,
00920 const double& r15,
00921 const double& r35,
00922 const double& r36,
00923 const double& r16,
00924 const double& lat,
00925 const double& lon )
00926 {
00927
00928
00929
00930 if( lat == lat1 )
00931 { return r15 + ( lon - lon5 ) * ( r16 - r15 ) / ( lon6 -lon5 ); }
00932 else if( lat == lat3 )
00933 { return r35 + ( lon - lon5 ) * ( r36 - r35 ) / ( lon6 -lon5 ); }
00934 else if( lon == lon5 )
00935 { return r15 + ( lat - lat1 ) * ( r35 - r15 ) / ( lat3 -lat1 ); }
00936 else if( lon == lon6 )
00937 { return r16 + ( lat - lat1 ) * ( r36 - r16 ) / ( lat3 -lat1 ); }
00938 else
00939 {
00940 const double fdlat = ( lat - lat1 ) / ( lat3 - lat1 );
00941 const double fdlon = ( lon - lon5 ) / ( lon6 - lon5 );
00942 return (1-fdlat)*(1-fdlon)*r15 + fdlat*(1-fdlon)*r35 +
00943 (1-fdlat)*fdlon*r16 + fdlat*fdlon*r36;
00944 }
00945 }
00946
00947
00948
00950
00977 double plevel_slope_3d(
00978 const double& lat1,
00979 const double& lat3,
00980 const double& lon5,
00981 const double& lon6,
00982 const double& r15,
00983 const double& r35,
00984 const double& r36,
00985 const double& r16,
00986 const double& lat,
00987 const double& lon,
00988 const double& aa )
00989 {
00990
00991 const double dang = 1e-4;
00992
00993
00994 const double r0 = rsurf_at_latlon( lat1, lat3, lon5, lon6,
00995 r15, r35, r36, r16, lat, lon );
00996
00997
00998
00999
01000 double x, y, z, dx, dy, dz;
01001 poslos2cart( x, y, z, dx, dy, dz, r0, lat, lon, 90, aa );
01002
01003
01004 const double l = r0 * DEG2RAD * dang;
01005
01006
01007
01008 double r2, lat2, lon2, za2, aa2;
01009 cart2poslos( r2, lat2, lon2, za2, aa2, x+dx*l, y+dy*l, z+dz*l, dx, dy, dz );
01010 r2 = rsurf_at_latlon( lat1, lat3, lon5, lon6, r15, r35, r36, r16, lat2,lon2);
01011
01012
01013 return ( r2 - r0 ) / dang;
01014 }
01015
01016
01017
01019
01048 double plevel_slope_3d(
01049 ConstVectorView lat_grid,
01050 ConstVectorView lon_grid,
01051 ConstMatrixView r_geoid,
01052 ConstMatrixView z_surf,
01053 const GridPos& gp_lat,
01054 const GridPos& gp_lon,
01055 const double& aa )
01056 {
01057 Index ilat = gridpos2gridrange( gp_lat, abs( aa ) >= 0 );
01058 Index ilon = gridpos2gridrange( gp_lon, aa >= 0 );
01059
01060
01061 Vector itw(2);
01062 double lat, lon;
01063 interpweights( itw, gp_lat );
01064 lat = interp( itw, lat_grid, gp_lat );
01065 interpweights( itw, gp_lon );
01066 lon = interp( itw, lon_grid, gp_lon );
01067
01068
01069 const double lat1 = lat_grid[ilat];
01070 const double lat3 = lat_grid[ilat+1];
01071 const double lon5 = lon_grid[ilon];
01072 const double lon6 = lon_grid[ilon+1];
01073 const double r15 = r_geoid(ilat,ilon) + z_surf(ilat,ilon);
01074 const double r35 = r_geoid(ilat+1,ilon) + z_surf(ilat+1,ilon);
01075 const double r36 = r_geoid(ilat+1,ilon+1) + z_surf(ilat+1,ilon+1);
01076 const double r16 = r_geoid(ilat,ilon+1) + z_surf(ilat,ilon+1);
01077
01078 return plevel_slope_3d( lat1, lat3, lon5, lon6, r15, r35, r36, r16,
01079 lat, lon, aa );
01080 }
01081
01082
01083
01085
01098 double plevel_angletilt(
01099 const double& r,
01100 const double& c )
01101 {
01102
01103
01104 return RAD2DEG * RAD2DEG * c / r;
01105 }
01106
01107
01108
01110
01129 bool is_los_downwards(
01130 const double& za,
01131 const double& tilt )
01132 {
01133 assert( abs(za) <= 180 );
01134
01135
01136 if( za > (90-tilt) || za < (-90-tilt) )
01137 { return true; }
01138 else
01139 { return false; }
01140 }
01141
01142
01143
01145
01189 double plevel_crossing_2d(
01190 const double& rp,
01191 const double& za,
01192 const double& r0,
01193 double c )
01194 {
01195 assert( abs(za) <= 180 );
01196
01197 const double no_crossing = 999;
01198
01199
01200 if( abs(za) < ANGTOL )
01201 {
01202 if( rp < r0 )
01203 { return 0; }
01204 else
01205 { return no_crossing; }
01206 }
01207 if( abs(za) > 180-ANGTOL )
01208 {
01209 if( rp > r0 )
01210 { return 0; }
01211 else
01212 { return no_crossing; }
01213 }
01214
01215
01216
01217
01218 if( rp >= r0 && !is_los_downwards( za, plevel_angletilt( r0, c ) ) )
01219 { return no_crossing; }
01220
01221
01222 if( c == 0 )
01223 {
01224 double ppc = geometrical_ppc( rp, za );
01225 if( ( rp < r0 && abs(za) <= 90 ) ||
01226 ( rp > r0 && abs(za) > 90 && r0 >= ppc ) )
01227 { return geompath_lat_at_za( za, 0, geompath_za_at_r( ppc, za, r0 ) );}
01228 else
01229 { return no_crossing; }
01230 }
01231
01232
01233
01234 else
01235 {
01236
01237
01238 double dmin = 0;
01239 if( r0 == rp )
01240 { dmin = 1e-12; }
01241
01242
01243 const double beta = DEG2RAD * ( 180 - abs(za) );
01244 const double cv = cos( beta );
01245 const double sv = sin( beta );
01246
01247
01248 c *= RAD2DEG;
01249 if( za < 0 )
01250 { c = -c; }
01251
01252
01253 Vector p(5);
01254
01255 p[0] = ( r0 - rp ) * sv;
01256 p[1] = r0 * cv + c * sv;
01257 p[2] = -r0 * sv / 2 + c * cv;
01258 p[3] = -r0 * cv / 6 - c * sv / 2;
01259 p[4] = -c * cv / 6;
01260
01261
01262 Matrix roots(4,2);
01263 poly_root_solve( roots, p );
01264
01265
01266
01267 double dlat = no_crossing / RAD2DEG;
01268
01269 for( Index i=0; i<4; i++ )
01270 {
01271 if( roots(i,1) == 0 && roots(i,0) > dmin && roots(i,0) < dlat )
01272 { dlat = roots(i,0); }
01273 }
01274
01275
01276 if( dlat < no_crossing && za < 0 )
01277 { dlat = -dlat; }
01278
01279 return RAD2DEG * dlat;
01280 }
01281 }
01282
01283
01284
01286
01326 void plevel_crossing_3d(
01327 double& r,
01328 double& lat,
01329 double& lon,
01330 double& l,
01331 const double r_surf,
01332 const double r_start,
01333 const double lat_start,
01334 const double lon_start,
01335 const double za_start,
01336 const double& x,
01337 const double& y,
01338 const double& z,
01339 const double& dx,
01340 const double& dy,
01341 const double& dz )
01342 {
01343 assert( za_start >= 0 );
01344 assert( za_start <= 180 );
01345
01346
01347 r = -1;
01348 lat = 999;
01349 lon = 999;
01350 l = -1;
01351
01352
01353 if( ( za_start < ANGTOL && r_start < r_surf ) ||
01354 ( za_start > 180-ANGTOL && r_start > r_surf ) )
01355 {
01356 r = r_surf;
01357 lat = lat_start;
01358 lon = lon_start;
01359 l = abs( r_surf - r_start );
01360 }
01361
01362 else
01363 {
01364 const double p = x*dx + y*dy + z*dz;
01365 const double pp = p * p;
01366 const double q = x*x + y*y + z*z - r_surf*r_surf;
01367
01368 const double l1 = -p + sqrt( pp - q );
01369 const double l2 = -p - sqrt( pp - q );
01370
01371 if( l1 < 0 && l2 > 0 )
01372 { l = l2; }
01373 else if( l1 > 0 && l2 < 0 )
01374 { l = l1; }
01375 else if( l1 < l2 )
01376 { l = l1; }
01377 else
01378 { l = l2; }
01379
01380 if( l > 0 )
01381 {
01382 r = r_surf;
01383 lat = RAD2DEG * asin( ( y+dy*l ) / r );
01384 lon = RAD2DEG * atan2( z+dz*l, x+dx*l );
01385 }
01386
01387 }
01388 }
01389
01390
01391
01392
01393
01394
01395
01396
01398
01424 void do_gridrange_1d(
01425 Vector& r_v,
01426 Vector& lat_v,
01427 Vector& za_v,
01428 double& lstep,
01429 Index& endface,
01430 Index& tanpoint,
01431 const double& r_start0,
01432 const double& lat_start,
01433 const double& za_start,
01434 const double& ppc,
01435 const double& lmax,
01436 const double& ra,
01437 const double& rb,
01438 const double& rsurface )
01439 {
01440 double r_start = r_start0;
01441
01442 assert( rb > ra );
01443 assert( r_start >= ra - RTOL );
01444 assert( r_start <= rb + RTOL );
01445
01446
01447 if( r_start < ra )
01448 { r_start = ra; }
01449 else if( r_start > rb )
01450 { r_start = rb; }
01451
01452
01453
01454
01455
01456
01457 double r_end;
01458
01459 endface = 0;
01460 tanpoint = 0;
01461
01462 if( za_start <= 90 )
01463 {
01464 r_end = rb;
01465 endface = 3;
01466 }
01467 else
01468 {
01469
01470
01471 if( ( ra > rsurface ) && ( ra > ppc ) )
01472 {
01473 r_end = ra;
01474 endface = 1;
01475 }
01476 else if( ppc >= rsurface )
01477 {
01478 r_end = ppc;
01479 tanpoint = 1;
01480 if( ppc != ra )
01481 { endface = 0; }
01482 }
01483 else
01484 {
01485 r_end = rsurface;
01486 endface = 7;
01487 }
01488 }
01489
01490
01491
01492 geompath_from_r1_to_r2( r_v, lat_v, za_v, lstep, ppc, r_start, lat_start,
01493 za_start, r_end, lmax );
01494
01495
01496 if( tanpoint )
01497 { za_v[za_v.nelem()-1] = 90; }
01498 }
01499
01500
01501
01503
01552 void do_gridcell_2d(
01553 Vector& r_v,
01554 Vector& lat_v,
01555 Vector& za_v,
01556 double& lstep,
01557 Index& endface,
01558 Index& tanpoint,
01559 const double& r_start0,
01560 const double& lat_start,
01561 const double& za_start,
01562 const double& ppc,
01563 const double& lmax,
01564 const double& lat1,
01565 const double& lat3,
01566 const double& r1a,
01567 const double& r3a,
01568 const double& r3b,
01569 const double& r1b,
01570 const double& rsurface1,
01571 const double& rsurface3 )
01572 {
01573 double r_start = r_start0;
01574
01575
01576 assert( lat_start >= lat1 );
01577 assert( lat_start <= lat3 );
01578
01579
01580 const double c2 = plevel_slope_2d( lat1, lat3, r1a, r3a );
01581 const double c4 = plevel_slope_2d( lat1, lat3, r1b, r3b );
01582 const double csurface = plevel_slope_2d( lat1, lat3, rsurface1, rsurface3 );
01583
01584
01585 const double dlat_left = lat_start - lat1;
01586
01587
01588 const double rlow = r1a + c2*dlat_left;
01589 const double rupp = r1b + c4*dlat_left;
01590
01591
01592 double dlat_endface;
01593 if( za_start >= 0 )
01594 { dlat_endface = lat3 - lat_start; }
01595 else
01596 { dlat_endface = -dlat_left; }
01597
01598
01599 assert( r_start >= rlow - RTOL );
01600 assert( r_start <= rupp + RTOL );
01601
01602
01603 if( r_start < rlow )
01604 { r_start = rlow; }
01605 else if( r_start > rupp )
01606 { r_start = rupp; }
01607
01608
01609
01610
01611
01612
01613 double dlat2end = 999;
01614 double abs_za_start = abs( za_start );
01615 endface = 0;
01616
01617
01618
01619
01620
01621
01622
01623 if( r_start > rlow && abs(za_start) > ANGTOL )
01624 {
01625 dlat2end = plevel_crossing_2d( r_start, za_start, rlow, c2 );
01626 endface = 2;
01627 }
01628
01629
01630
01631
01632
01633
01634
01635 if( rsurface1 >= r1a || rsurface3 >= r3a )
01636 {
01637 double r_surface = rsurface1 + csurface * dlat_left;
01638
01639 assert( r_start >= r_surface );
01640
01641 double dlat2surface = plevel_crossing_2d( r_start, za_start, r_surface,
01642 csurface );
01643 if( abs(dlat2surface) <= abs(dlat2end) )
01644 {
01645 dlat2end = dlat2surface;
01646 endface = 7;
01647 }
01648 }
01649
01650
01651
01652
01653
01654
01655
01656
01657 if( abs(dlat2end) > abs(dlat_endface) && abs_za_start < 180-ANGTOL )
01658 {
01659
01660
01661
01662
01663 double dlattry = plevel_crossing_2d( r_start, za_start, rupp, c4 );
01664
01665 if( abs(dlattry) < abs(dlat2end) )
01666 {
01667 dlat2end = dlattry;
01668 endface = 4;
01669 }
01670 }
01671
01672
01673
01674 if( abs(dlat2end) > abs(dlat_endface) )
01675 {
01676 dlat2end = dlat_endface;
01677 if( za_start >= 0 )
01678 { endface = 3; }
01679 else
01680 { endface = 1; }
01681 }
01682
01683 assert( endface );
01684
01685
01686 if( abs_za_start > 90 && ( abs_za_start - abs(dlat2end) ) <= 90 )
01687 {
01688 tanpoint = 1;
01689
01690
01691 if( ( abs_za_start - 90 ) < abs( dlat2end ) )
01692 {
01693 endface = 0;
01694 dlat2end = sign(za_start) * ( abs_za_start - 90 );
01695 }
01696 }
01697 else
01698 { tanpoint = 0; }
01699
01700
01701
01702
01703
01704
01705 double r_end = -1;
01706
01707 if( tanpoint )
01708 { r_end = geompath_r_at_za( ppc, sign(za_start) * 90 ); }
01709 else if( endface == 1 )
01710 { r_end = geompath_r_at_lat( ppc, lat_start, za_start, lat1 ); }
01711 else if( endface == 2 )
01712 { r_end = r1a + c2 * ( dlat_left + dlat2end ); }
01713 else if( endface == 3 )
01714 { r_end = geompath_r_at_lat( ppc, lat_start, za_start, lat3 ); }
01715 else if( endface == 4 )
01716 { r_end = r1b + c4 * ( dlat_left + dlat2end ); }
01717 else if( endface == 7 )
01718 { r_end = rsurface1 + csurface * ( dlat_left + dlat2end ); }
01719
01720
01721
01722 geompath_from_r1_to_r2( r_v, lat_v, za_v, lstep, ppc, r_start, lat_start,
01723 za_start, r_end, lmax );
01724
01725
01726 if( tanpoint )
01727 {
01728 if( za_start >= 0 )
01729 { za_v[za_v.nelem()-1] = 90; }
01730 else
01731 { za_v[za_v.nelem()-1] = -90; }
01732 }
01733
01734 if( endface == 1 )
01735 { lat_v[lat_v.nelem()-1] = lat1; }
01736 else if( endface == 3 )
01737 { lat_v[lat_v.nelem()-1] = lat3; }
01738 else
01739 { lat_v[lat_v.nelem()-1] = lat_start + dlat2end; }
01740 }
01741
01742
01743
01745
01797 void do_gridcell_3d(
01798 Vector& r_v,
01799 Vector& lat_v,
01800 Vector& lon_v,
01801 Vector& za_v,
01802 Vector& aa_v,
01803 double& lstep,
01804 Index& endface,
01805 Index& tanpoint,
01806 const double& r_start0,
01807 const double& lat_start0,
01808 const double& lon_start0,
01809 const double& za_start,
01810 const double& aa_start,
01811 const double& ppc,
01812 const double& lmax,
01813 const double& lat1,
01814 const double& lat3,
01815 const double& lon5,
01816 const double& lon6,
01817 const double& r15a,
01818 const double& r35a,
01819 const double& r36a,
01820 const double& r16a,
01821 const double& r15b,
01822 const double& r35b,
01823 const double& r36b,
01824 const double& r16b,
01825 const double& rsurface15,
01826 const double& rsurface35,
01827 const double& rsurface36,
01828 const double& rsurface16 )
01829 {
01830 double r_start = r_start0;
01831 double lat_start = lat_start0;
01832 double lon_start = lon_start0;
01833
01834
01835 assert( lat_start >= lat1 - LATLONTOL );
01836 assert( lat_start <= lat3 + LATLONTOL );
01837 assert( !( abs( lat_start) < 90 && lon_start < lon5 - LATLONTOL ) );
01838 assert( !( abs( lat_start) < 90 && lon_start > lon6 + LATLONTOL ) );
01839
01840
01841 if( lat_start < lat1 )
01842 { lat_start = lat1; }
01843 else if( lat_start > lat3 )
01844 { lat_start = lat3; }
01845 if( lon_start < lon5 )
01846 { lon_start = lon5; }
01847 else if( lon_start > lon6 )
01848 { lon_start = lon6; }
01849
01850
01851 double rlow = rsurf_at_latlon( lat1, lat3, lon5, lon6,
01852 r15a, r35a, r36a, r16a, lat_start, lon_start );
01853 double rupp = rsurf_at_latlon( lat1, lat3, lon5, lon6,
01854 r15b, r35b, r36b, r16b, lat_start, lon_start );
01855
01856
01857 assert( r_start >= rlow - RTOL );
01858 assert( r_start <= rupp + RTOL );
01859
01860
01861
01862 if( r_start < rlow )
01863 { r_start = rlow; }
01864 else if( r_start > rupp )
01865 { r_start = rupp; }
01866
01867
01868
01869
01870
01871 assert( !( lat_start==lat1 && lat_start!=-90 && abs( aa_start ) > 90 ) );
01872 assert( !( lat_start==lat3 && lat_start!= 90 && abs( aa_start ) < 90 ) );
01873 assert( !( lon_start==lon5 && abs(lat_start)!=90 && aa_start < 0 ) );
01874 assert( !( lon_start==lon6 && abs(lat_start)!=90 && aa_start > 0 ) );
01875
01876 if( r_start == rlow )
01877 {
01878 DEBUG_ONLY (const double c = plevel_slope_3d( lat1, lat3, lon5, lon6,
01879 r15a, r35a, r36a, r16a,
01880 lat_start, lon_start,
01881 aa_start ));
01882 DEBUG_ONLY (const double tilt = plevel_angletilt( r_start, c ));
01883
01884 assert( !is_los_downwards( za_start, tilt ) );
01885 }
01886 else if( r_start == rupp )
01887 {
01888 DEBUG_ONLY (const double c = plevel_slope_3d( lat1, lat3, lon5, lon6,
01889 r15b, r35b, r36b, r16b,
01890 lat_start, lon_start,
01891 aa_start ));
01892 DEBUG_ONLY (const double tilt = plevel_angletilt( r_start, c ));
01893
01894 assert( is_los_downwards( za_start, tilt ) );
01895 }
01896
01897
01898
01899 double x, y, z, dx, dy, dz;
01900 poslos2cart( x, y, z, dx, dy, dz, r_start, lat_start, lon_start,
01901 za_start, aa_start );
01902
01903
01904
01905
01906 endface = 0;
01907 tanpoint = 0;
01908
01909 double r_end, lat_end, lon_end, l_end;
01910
01911
01912
01913
01914
01915 if( za_start < ANGTOL )
01916 {
01917 r_end = rupp;
01918 lat_end = lat_start;
01919 lon_end = lon_start;
01920 l_end = rupp - r_start;
01921 endface = 4;
01922 }
01923
01924
01925 else if( za_start > 180-ANGTOL )
01926 {
01927 const double rsurface = rsurf_at_latlon( lat1, lat3, lon5, lon6,
01928 rsurface15, rsurface35, rsurface36, rsurface16,
01929 lat_start, lon_start );
01930
01931 if( rlow > rsurface )
01932 {
01933 r_end = rlow;
01934 endface = 2;
01935 }
01936 else
01937 {
01938 r_end = rsurface;
01939 endface = 7;
01940 }
01941 lat_end = lat_start;
01942 lon_end = lon_start;
01943 l_end = r_start - r_end;
01944 }
01945
01946
01947 else
01948 {
01949
01950
01951
01952 double r_corr, lat_corr, lon_corr;
01953
01954 cart2sph( r_corr, lat_corr, lon_corr, x, y, z );
01955
01956 r_corr -= r_start;
01957 lat_corr -= lat_start;
01958 lon_corr -= lon_start;
01959
01960
01961
01962
01963
01964
01965
01966
01967
01968
01969 l_end = 1;
01970
01971 double l_acc = 1e-3;
01972 double l_in = 0, l_out = l_end;
01973 bool ready = false, startup = true;
01974 double abs_aa = abs( aa_start );
01975
01976 double l_tan = 99e6;
01977 if( za_start > 90 )
01978 { l_tan = sqrt( r_start*r_start - ppc*ppc ); }
01979
01980 bool do_surface = false;
01981 if( rsurface15 >= r15a || rsurface35 >= r35a ||
01982 rsurface36 >= r36a || rsurface16 >= r16a )
01983 { do_surface = true; }
01984
01985
01986 while( !ready )
01987 {
01988 cart2sph( r_end, lat_end, lon_end,
01989 x+dx*l_end, y+dy*l_end, z+dz*l_end );
01990 r_end -= r_corr;
01991 lat_end -= lat_corr;
01992 lon_end -= lon_corr;
01993
01994
01995 if( abs( lat_start ) < 90 && ( abs(aa_start) < ANGTOL ||
01996 abs(aa_start) > 180-ANGTOL ) )
01997 { lon_end = lon_start; }
01998 else
01999 { resolve_lon( lon_end, lon5, lon6 ); }
02000
02001
02002 if( abs( abs_aa - 90 ) < ANGTOL )
02003 {
02004 if( lat_start == 0 )
02005 { lat_end = 0; }
02006 else if( lat_start > 0 && lat_end > lat_start )
02007 { lat_end = lat_start; }
02008 else if( lat_start < 0 && lat_end < lat_start )
02009 { lat_end = lat_start; }
02010 }
02011
02012 bool inside = true;
02013
02014 if( lat_end < lat1 )
02015 { inside = false; endface = 1; }
02016 else if( lat_end > lat3 )
02017 { inside = false; endface = 3; }
02018 else if( lon_end < lon5 )
02019 { inside = false; endface = 5; }
02020 else if( lon_end > lon6 )
02021 { inside = false; endface = 6; }
02022
02023 if( inside )
02024 {
02025 rlow = rsurf_at_latlon( lat1, lat3, lon5, lon6,
02026 r15a, r35a, r36a, r16a, lat_end, lon_end );
02027 rupp = rsurf_at_latlon( lat1, lat3, lon5, lon6,
02028 r15b, r35b, r36b, r16b, lat_end, lon_end );
02029
02030 if( r_end < rlow )
02031 { inside = false; endface = 2; }
02032 else if( r_end > rupp )
02033 { inside = false; endface = 4; }
02034
02035 if( do_surface )
02036 {
02037 const double r_surface =
02038 rsurf_at_latlon( lat1, lat3, lon5, lon6,
02039 rsurface15, rsurface35, rsurface36, rsurface16,
02040 lat_end, lon_end );
02041
02042 if( r_surface+RTOL >= rlow && r_end <= r_surface+RTOL )
02043 { inside = false; endface = 7; }
02044 }
02045 }
02046
02047 if( startup )
02048 {
02049 if( inside )
02050 {
02051 if( l_end < l_tan && l_end*10 > l_tan )
02052 { l_end = l_tan; }
02053 else
02054 { l_end *= 10; }
02055 }
02056 else
02057 {
02058 l_out = l_end;
02059 l_end = ( l_out + l_in ) / 2;
02060 startup = false;
02061 }
02062 }
02063 else
02064 {
02065 if( inside )
02066 { l_in = l_end; }
02067 else
02068 { l_out = l_end; }
02069
02070 if( ( l_out - l_in ) < l_acc )
02071 { ready = true; }
02072 else
02073 { l_end = ( l_out + l_in ) / 2; }
02074 }
02075 }
02076
02077
02078
02079
02080 r_end += r_corr;
02081 lat_end += lat_corr;
02082 lon_end += lon_corr;
02083
02084
02085
02086 if( endface == 1 )
02087 { lat_end = lat1; }
02088 else if( endface == 2 )
02089 { r_end = rsurf_at_latlon( lat1, lat3, lon5, lon6, r15a, r35a, r36a,
02090 r16a, lat_end, lon_end ); }
02091 else if( endface == 3 )
02092 { lat_end = lat3; }
02093 else if( endface == 4 )
02094 { r_end = rsurf_at_latlon( lat1, lat3, lon5, lon6, r15b, r35b, r36b,
02095 r16b, lat_end, lon_end ); }
02096 else if( endface == 5 )
02097 { lon_end = lon5; }
02098 else if( endface == 6 )
02099 { lon_end = lon6; }
02100 else if( endface == 7 )
02101 { r_end = rsurf_at_latlon( lat1, lat3, lon5, lon6, rsurface15,
02102 rsurface35, rsurface36, rsurface16, lat_end, lon_end ); }
02103
02104
02105
02106 if( za_start > 90 )
02107 {
02108 if( l_tan < l_end )
02109 {
02110 geompath_tanpos_3d( r_end, lat_end, lon_end, l_end,
02111 r_start, lat_start, lon_start,
02112 za_start, aa_start, ppc );
02113 endface = 0;
02114 tanpoint = 1;
02115 }
02116 else if( l_tan == l_end )
02117 { tanpoint = 1; }
02118 }
02119 }
02120
02121
02122
02123
02124 Index n = 1;
02125
02126 if( lmax > 0 )
02127 {
02128 n = Index( ceil( abs( l_end / lmax ) ) );
02129 if( n < 1 )
02130 { n = 1; }
02131 }
02132
02133 r_v.resize( n+1 );
02134 lat_v.resize( n+1 );
02135 lon_v.resize( n+1 );
02136 za_v.resize( n+1 );
02137 aa_v.resize( n+1 );
02138
02139 r_v[0] = r_start;
02140 lat_v[0] = lat_start;
02141 lon_v[0] = lon_start;
02142 za_v[0] = za_start;
02143 aa_v[0] = aa_start;
02144
02145 double ldouble = l_end / (double)n;
02146 lstep = ldouble;
02147
02148 for( Index j=1; j<=n; j++ )
02149 {
02150 const double l = ldouble * (double)j;
02151
02152
02153
02154
02155 #ifdef USE_DOUBLE
02156 cart2poslos( r_v[j], lat_v[j], lon_v[j], za_v[j], aa_v[j],
02157 x+dx*l, y+dy*l, z+dz*l, dx, dy, dz );
02158 #else
02159 double rtmp, lattmp, lontmp, zatmp, aatmp;
02160 cart2poslos( rtmp, lattmp, lontmp, zatmp, aatmp,
02161 x+dx*l, y+dy*l, z+dz*l, dx, dy, dz );
02162 r_v[j] = rtmp;
02163 lat_v[j] = lattmp;
02164 lon_v[j] = lontmp;
02165 za_v[j] = zatmp;
02166 aa_v[j] = aatmp;
02167 #endif
02168 }
02169
02170
02171
02172 r_v[n] = r_end;
02173 lat_v[n] = lat_end;
02174 lon_v[n] = lon_end;
02175
02176
02177 if( za_start < ANGTOL || za_start > 180-ANGTOL )
02178 { za_v[n] = za_start; }
02179 else if( tanpoint )
02180 { za_v[n] = 90; }
02181 else
02182 { za_v[n] = geompath_za_at_r( ppc, za_start, r_v[n] ); }
02183
02184
02185
02186 if( abs( lat_start ) < 90 &&
02187 ( abs(aa_start) < ANGTOL || abs( aa_start) > 180-ANGTOL ) )
02188 {
02189 aa_v[n] = aa_start;
02190 lon_v[n] = lon_start;
02191 }
02192
02193
02194 for( Index j=1; j<n; j++ )
02195 { resolve_lon( lon_v[j], lon5, lon6 ); }
02196 }
02197
02198
02199
02200
02201
02202
02203
02204
02205
02207
02223 void ppath_init_structure(
02224 Ppath& ppath,
02225 const Index& atmosphere_dim,
02226 const Index& np )
02227 {
02228 assert( atmosphere_dim >= 1 );
02229 assert( atmosphere_dim <= 3 );
02230
02231 ppath.dim = atmosphere_dim;
02232 ppath.np = np;
02233 ppath.refraction = 0;
02234 ppath.method = "-";
02235 ppath.constant = -1;
02236 if( atmosphere_dim < 3 )
02237 {
02238 ppath.pos.resize( np, 2 );
02239 ppath.los.resize( np, 1 );
02240 }
02241 else
02242 {
02243 ppath.pos.resize( np, atmosphere_dim );
02244 ppath.los.resize( np, 2 );
02245 ppath.gp_lon.resize( np );
02246 }
02247 ppath.gp_p.resize( np );
02248 if( atmosphere_dim >= 2 )
02249 { ppath.gp_lat.resize( np ); }
02250 ppath.z.resize( np );
02251 if( np > 0 )
02252 { ppath.l_step.resize( np-1 ); }
02253 else
02254 { ppath.l_step.resize( 0 ); }
02255 ppath_set_background( ppath, 0 );
02256 ppath.tan_pos.resize(0);
02257 ppath.geom_tan_pos.resize(0);
02258 ppath.p.resize(0);
02259 ppath.t.resize(0);
02260 ppath.vmr.resize(0,0);
02261 ppath.next_parts.resize(0);
02262 }
02263
02264
02265
02266
02268
02287 void ppath_set_background(
02288 Ppath& ppath,
02289 const Index& case_nr )
02290 {
02291 switch ( case_nr )
02292 {
02293 case 0:
02294 ppath.background = "";
02295 break;
02296 case 1:
02297 ppath.background = "space";
02298 break;
02299 case 2:
02300 ppath.background = "surface";
02301 break;
02302 case 3:
02303 ppath.background = "cloud box level";
02304 break;
02305 case 4:
02306 ppath.background = "cloud box interior";
02307 break;
02308 default:
02309 ostringstream os;
02310 os << "Case number " << case_nr << " is not defined.";
02311 throw runtime_error(os.str());
02312 }
02313 }
02314
02315
02316
02318
02329 Index ppath_what_background( const Ppath& ppath )
02330 {
02331 if( ppath.background == "" )
02332 { return 0; }
02333 else if( ppath.background == "space" )
02334 { return 1; }
02335 else if( ppath.background == "surface" )
02336 { return 2; }
02337 else if( ppath.background == "cloud box level" )
02338 { return 3; }
02339 else if( ppath.background == "cloud box interior" )
02340 { return 4; }
02341 else
02342 {
02343 ostringstream os;
02344 os << "The string " << ppath.background
02345 << " is not a valid background case.";
02346 throw runtime_error(os.str());
02347 }
02348 }
02349
02350
02351
02353
02366 void ppath_copy(
02367 Ppath& ppath1,
02368 const Ppath& ppath2 )
02369 {
02370 assert( ppath1.np >= ppath2.np );
02371
02372
02373
02374 ppath1.dim = ppath2.dim;
02375 ppath1.refraction = ppath2.refraction;
02376 ppath1.method = ppath2.method;
02377 ppath1.constant = ppath2.constant;
02378 ppath1.background = ppath2.background;
02379
02380 ppath1.pos(Range(0,ppath2.np),joker) = ppath2.pos;
02381 ppath1.los(Range(0,ppath2.np),joker) = ppath2.los;
02382 ppath1.z[Range(0,ppath2.np)] = ppath2.z;
02383 for( Index i=0; i<ppath2.np; i++ )
02384 {
02385 if( i > 0 )
02386 { ppath1.l_step[i-1] = ppath2.l_step[i-1]; }
02387 }
02388
02389
02390 for( Index i=0; i<ppath2.np; i++ )
02391 {
02392 gridpos_copy( ppath1.gp_p[i], ppath2.gp_p[i] );
02393
02394 if( ppath1.dim >= 2 )
02395 { gridpos_copy( ppath1.gp_lat[i], ppath2.gp_lat[i] ); }
02396
02397 if( ppath1.dim == 3 )
02398 { gridpos_copy( ppath1.gp_lon[i], ppath2.gp_lon[i] ); }
02399 }
02400
02401 if( ppath2.tan_pos.nelem() )
02402 { ppath1.tan_pos = ppath2.tan_pos; }
02403 if( ppath2.geom_tan_pos.nelem() )
02404 { ppath1.geom_tan_pos = ppath2.geom_tan_pos; }
02405
02406 if( ppath2.p.nelem() )
02407 {
02408 ppath1.p.resize(ppath1.np);
02409 ppath1.p[Range(0,ppath2.np)] = ppath2.p;
02410 }
02411 if( ppath2.t.nelem() )
02412 {
02413 ppath1.t.resize(ppath1.np);
02414 ppath1.t[Range(0,ppath2.np)] = ppath2.t;
02415 }
02416 if( ppath2.vmr.nrows() )
02417 {
02418 ppath1.vmr.resize(ppath2.vmr.nrows(),ppath1.np);
02419 ppath1.vmr(joker,Range(0,ppath2.np)) = ppath2.vmr;
02420 }
02421 if( ppath2.next_parts.nelem() )
02422 { ppath1.next_parts = ppath2.next_parts; }
02423 }
02424
02425
02426
02428
02445 void ppath_append(
02446 Ppath& ppath1,
02447 const Ppath& ppath2 )
02448 {
02449 const Index n1 = ppath1.np;
02450 const Index n2 = ppath2.np;
02451
02452 Ppath ppath;
02453 ppath_init_structure( ppath, ppath1.dim, n1 );
02454 ppath_copy( ppath, ppath1 );
02455
02456 ppath_init_structure( ppath1, ppath1.dim, n1 + n2 - 1 );
02457 ppath_copy( ppath1, ppath );
02458
02459
02460 Index i1;
02461 for( Index i=1; i<n2; i++ )
02462 {
02463 i1 = n1 + i - 1;
02464
02465 ppath1.pos(i1,0) = ppath2.pos(i,0);
02466 ppath1.pos(i1,1) = ppath2.pos(i,1);
02467 ppath1.los(i1,0) = ppath2.los(i,0);
02468 ppath1.z[i1] = ppath2.z[i];
02469 gridpos_copy( ppath1.gp_p[i1], ppath2.gp_p[i] );
02470
02471 if( ppath1.dim >= 2 )
02472 { gridpos_copy( ppath1.gp_lat[i1], ppath2.gp_lat[i] ); }
02473
02474 if( ppath1.dim == 3 )
02475 {
02476 ppath1.pos(i1,2) = ppath2.pos(i,2);
02477 ppath1.los(i1,1) = ppath2.los(i,1);
02478 gridpos_copy( ppath1.gp_lon[i1], ppath2.gp_lon[i] );
02479 }
02480
02481 ppath1.l_step[i1-1] = ppath2.l_step[i-1];
02482
02483
02484 if( ppath1.p.nelem() )
02485 {
02486 ppath1.p[i1] = ppath2.p[i];
02487 ppath1.t[i1] = ppath2.t[i];
02488 ppath1.vmr(joker,i1) = ppath2.vmr(joker,i);
02489
02490 }
02491 }
02492
02493 if( ppath_what_background( ppath2 ) )
02494 { ppath1.background = ppath2.background; }
02495
02496
02497 if( ppath2.next_parts.nelem() )
02498 { ppath1.next_parts = ppath2.next_parts; }
02499 }
02500
02501
02502
02504
02528 void ppath_fill_1d(
02529 Ppath& ppath,
02530 ConstVectorView r,
02531 ConstVectorView lat,
02532 ConstVectorView za,
02533 ConstVectorView lstep,
02534 const double& r_geoid,
02535 ConstVectorView z_field,
02536 const Index& ip )
02537 {
02538
02539 const double r1 = r_geoid + z_field[ip];
02540 const double dr = z_field[ip+1] - z_field[ip];
02541
02542 for( Index i=0; i<r.nelem(); i++ )
02543 {
02544 ppath.pos(i,0) = r[i];
02545 ppath.pos(i,1) = lat[i];
02546 ppath.los(i,0) = za[i];
02547
02548 ppath.gp_p[i].idx = ip;
02549 ppath.gp_p[i].fd[0] = ( r[i] - r1 ) / dr;
02550 ppath.gp_p[i].fd[1] = 1 - ppath.gp_p[i].fd[0];
02551 gridpos_check_fd( ppath.gp_p[i] );
02552
02553 ppath.z[i] = r[i] - r_geoid;
02554
02555 if( i > 0 )
02556 { ppath.l_step[i-1] = lstep[i-1]; }
02557 }
02558 }
02559
02560
02561
02563
02588 void ppath_fill_2d(
02589 Ppath& ppath,
02590 ConstVectorView r,
02591 ConstVectorView lat,
02592 ConstVectorView za,
02593 const double& lstep,
02594 ConstVectorView r_geoid,
02595 ConstMatrixView z_field,
02596 ConstVectorView lat_grid,
02597 const Index& ip,
02598 const Index& ilat )
02599 {
02600
02601 const double dlat = lat_grid[ilat+1] - lat_grid[ilat];
02602 const double r1low = r_geoid[ilat] + z_field(ip,ilat);
02603 const double drlow = r_geoid[ilat+1] + z_field(ip,ilat+1) -r1low;
02604 const double r1upp = r_geoid[ilat] + z_field(ip+1,ilat);
02605 const double drupp = r_geoid[ilat+1] + z_field(ip+1,ilat+1) - r1upp;
02606 const double z1low = z_field(ip,ilat);
02607 const double dzlow = z_field(ip,ilat+1) -z1low;
02608 const double z1upp = z_field(ip+1,ilat);
02609 const double dzupp = z_field(ip+1,ilat+1) - z1upp;
02610
02611 for( Index i=0; i<r.nelem(); i++ )
02612 {
02613 ppath.pos(i,0) = r[i];
02614 ppath.pos(i,1) = lat[i];
02615 ppath.los(i,0) = za[i];
02616
02617
02618 double w = ( lat[i] - lat_grid[ilat] ) / dlat;
02619
02620
02621 const double rlow = r1low + w * drlow;
02622 const double rupp = r1upp + w * drupp;
02623
02624
02625 const double zlow = z1low + w * dzlow;
02626 const double zupp = z1upp + w * dzupp;
02627
02628 ppath.gp_p[i].idx = ip;
02629 ppath.gp_p[i].fd[0] = ( r[i] - rlow ) / ( rupp - rlow );
02630 ppath.gp_p[i].fd[1] = 1 - ppath.gp_p[i].fd[0];
02631 gridpos_check_fd( ppath.gp_p[i] );
02632
02633 ppath.z[i] = zlow + ppath.gp_p[i].fd[0] * ( zupp -zlow );
02634
02635 ppath.gp_lat[i].idx = ilat;
02636 ppath.gp_lat[i].fd[0] = ( lat[i] - lat_grid[ilat] ) / dlat;
02637 ppath.gp_lat[i].fd[1] = 1 - ppath.gp_lat[i].fd[0];
02638 gridpos_check_fd( ppath.gp_lat[i] );
02639
02640 if( i > 0 )
02641 { ppath.l_step[i-1] = lstep; }
02642 }
02643 }
02644
02645
02646
02648
02677 void ppath_fill_3d(
02678 Ppath& ppath,
02679 ConstVectorView r,
02680 ConstVectorView lat,
02681 ConstVectorView lon,
02682 ConstVectorView za,
02683 ConstVectorView aa,
02684 const double& lstep,
02685 ConstMatrixView r_geoid,
02686 ConstTensor3View z_field,
02687 ConstVectorView lat_grid,
02688 ConstVectorView lon_grid,
02689 const Index& ip,
02690 const Index& ilat,
02691 const Index& ilon )
02692 {
02693
02694 const double lat1 = lat_grid[ilat];
02695 const double lat3 = lat_grid[ilat+1];
02696 const double lon5 = lon_grid[ilon];
02697 const double lon6 = lon_grid[ilon+1];
02698 const double r15a = r_geoid(ilat,ilon) + z_field(ip,ilat,ilon);
02699 const double r35a = r_geoid(ilat+1,ilon) + z_field(ip,ilat+1,ilon);
02700 const double r36a = r_geoid(ilat+1,ilon+1) + z_field(ip,ilat+1,ilon+1);
02701 const double r16a = r_geoid(ilat,ilon+1) + z_field(ip,ilat,ilon+1);
02702 const double r15b = r_geoid(ilat,ilon) + z_field(ip+1,ilat,ilon);
02703 const double r35b = r_geoid(ilat+1,ilon) + z_field(ip+1,ilat+1,ilon);
02704 const double r36b = r_geoid(ilat+1,ilon+1) + z_field(ip+1,ilat+1,ilon+1);
02705 const double r16b = r_geoid(ilat,ilon+1) + z_field(ip+1,ilat,ilon+1);
02706 const double rg15 = r_geoid(ilat,ilon);
02707 const double rg35 = r_geoid(ilat+1,ilon);
02708 const double rg36 = r_geoid(ilat+1,ilon+1);
02709 const double rg16 = r_geoid(ilat,ilon+1);
02710 const double dlat = lat3 - lat1;
02711 const double dlon = lon6 - lon5;
02712
02713 for( Index i=0; i<r.nelem(); i++ )
02714 {
02715
02716 const double rlow = rsurf_at_latlon( lat1, lat3, lon5, lon6,
02717 r15a, r35a, r36a, r16a, lat[i], lon[i] );
02718 const double rupp = rsurf_at_latlon( lat1, lat3, lon5, lon6,
02719 r15b, r35b, r36b, r16b, lat[i], lon[i] );
02720
02721
02722 ppath.pos(i,0) = r[i];
02723 ppath.pos(i,1) = lat[i];
02724 ppath.pos(i,2) = lon[i];
02725 ppath.los(i,0) = za[i];
02726 ppath.los(i,1) = aa[i];
02727
02728
02729 ppath.gp_p[i].idx = ip;
02730 ppath.gp_p[i].fd[0] = ( ppath.pos(i,0) - rlow ) / ( rupp - rlow );
02731 ppath.gp_p[i].fd[1] = 1 - ppath.gp_p[i].fd[0];
02732 gridpos_check_fd( ppath.gp_p[i] );
02733
02734
02735 const double rgeoid = rsurf_at_latlon( lat1, lat3, lon5, lon6,
02736 rg15, rg35, rg36, rg16, lat[i], lon[i] );
02737 const double zlow = rlow - rgeoid;
02738 const double zupp = rupp - rgeoid;
02739
02740 ppath.z[i] = zlow + ppath.gp_p[i].fd[0] * ( zupp -zlow );
02741
02742
02743 ppath.gp_lat[i].idx = ilat;
02744 ppath.gp_lat[i].fd[0] = ( lat[i] - lat1 ) / dlat;
02745 ppath.gp_lat[i].fd[1] = 1 - ppath.gp_lat[i].fd[0];
02746 gridpos_check_fd( ppath.gp_lat[i] );
02747
02748
02749
02750
02751
02752
02753 if( abs( lat[i] ) < 90 )
02754 {
02755 ppath.gp_lon[i].idx = ilon;
02756 ppath.gp_lon[i].fd[0] = ( lon[i] - lon5 ) / dlon;
02757 ppath.gp_lon[i].fd[1] = 1 - ppath.gp_lon[i].fd[0];
02758 gridpos_check_fd( ppath.gp_lon[i] );
02759 }
02760 else
02761 {
02762 ppath.gp_lon[i].idx = 0;
02763 ppath.gp_lon[i].fd[0] = 0;
02764 ppath.gp_lon[i].fd[1] = 1;
02765 }
02766
02767 if( i > 0 )
02768 { ppath.l_step[i-1] = lstep; }
02769 }
02770 }
02771
02772
02773
02774
02775
02776
02777
02778
02779
02781
02794 double refraction_ppc(
02795 const double& r,
02796 const double& za,
02797 const double& refr_index )
02798 {
02799 assert( r > 0 );
02800 assert( abs(za) <= 180 );
02801
02802 return r * refr_index * sin( DEG2RAD * abs(za) );
02803 }
02804
02805
02806
02807
02808
02809
02810
02811
02812
02813
02814
02815
02816
02818
02829 void ppath_start_1d(
02830 double& r_start,
02831 double& lat_start,
02832 double& za_start,
02833 Index& ip,
02834 const Ppath& ppath,
02835 ConstVectorView DEBUG_ONLY (p_grid),
02836 ConstVectorView DEBUG_ONLY (z_field),
02837 const double& DEBUG_ONLY (r_geoid),
02838 const double& DEBUG_ONLY (z_surface)
02839 )
02840 {
02841
02842 const Index imax = ppath.np - 1;
02843
02844
02845
02846 DEBUG_ONLY (const Index npl = p_grid.nelem());
02847
02848
02849 r_start = ppath.pos(imax,0);
02850 lat_start = ppath.pos(imax,1);
02851 za_start = ppath.los(imax,0);
02852
02853
02854 assert( npl >= 2 );
02855 assert( is_decreasing( p_grid ) );
02856 assert( is_size( z_field, npl ) );
02857 assert( is_increasing( z_field ) );
02858 assert( r_geoid > 0 );
02859
02860 assert( ppath.dim == 1 );
02861 assert( ppath.np >= 1 );
02862 assert( ppath.gp_p[imax].idx >= 0 );
02863 assert( ppath.gp_p[imax].idx <= ( npl - 2 ) );
02864 assert( ppath.gp_p[imax].fd[0] >= 0 );
02865 assert( ppath.gp_p[imax].fd[0] <= 1 );
02866
02867 assert( r_start >= r_geoid + z_surface - 1e-6 );
02868 assert( za_start >= 0 && za_start <= 180 );
02869
02870
02871
02872
02873
02874 ip = gridpos2gridrange( ppath.gp_p[imax], za_start<=90 );
02875
02876 out3 << " pressure grid range : " << ip << "\n";
02877 }
02878
02879
02880
02882
02893 void ppath_end_1d(
02894 Ppath& ppath,
02895 ConstVectorView r_v,
02896 ConstVectorView lat_v,
02897 ConstVectorView za_v,
02898 const double& lstep,
02899 ConstVectorView z_field,
02900 const double& r_geoid,
02901 const Index& ip,
02902 const Index& endface,
02903 const Index& tanpoint,
02904 const String& method,
02905 const Index& refraction,
02906 const double& ppc )
02907 {
02908 out3 << " end face number code : " << endface << "\n";
02909
02910
02911 const Index np = r_v.nelem();
02912
02913
02914
02915 ppath_init_structure( ppath, 1, np );
02916
02917 ppath.method = method;
02918 ppath.refraction = refraction;
02919 ppath.constant = ppc;
02920
02921 ppath_fill_1d( ppath, r_v, lat_v, za_v, Vector(np-1,lstep), r_geoid,
02922 z_field, ip );
02923
02924
02925
02926
02927
02928 if( endface == 7 )
02929 { ppath_set_background( ppath, 2 ); }
02930
02931
02932 else if( tanpoint )
02933 {
02934 ppath.tan_pos.resize(2);
02935 ppath.tan_pos[0] = r_v[np-1];
02936 ppath.tan_pos[1] = lat_v[np-1];
02937 }
02938
02939
02940 else
02941 {
02942 gridpos_force_end_fd( ppath.gp_p[np-1] );
02943 }
02944 }
02945
02946
02947
02949
02960 void ppath_start_2d(
02961 double& r_start,
02962 double& lat_start,
02963 double& za_start,
02964 Index& ip,
02965 Index& ilat,
02966 double& lat1,
02967 double& lat3,
02968 double& r1a,
02969 double& r3a,
02970 double& r3b,
02971 double& r1b,
02972 double& rsurface1,
02973 double& rsurface3,
02974 const Ppath& ppath,
02975 ConstVectorView DEBUG_ONLY (p_grid),
02976 ConstVectorView lat_grid,
02977 ConstMatrixView z_field,
02978 ConstVectorView r_geoid,
02979 ConstVectorView z_surface
02980 )
02981 {
02982
02983 const Index imax = ppath.np - 1;
02984
02985
02986 DEBUG_ONLY (const Index npl = p_grid.nelem());
02987 DEBUG_ONLY (const Index nlat = lat_grid.nelem());
02988
02989
02990 r_start = ppath.pos(imax,0);
02991 lat_start = ppath.pos(imax,1);
02992 za_start = ppath.los(imax,0);
02993
02994
02995 assert( npl >= 2 );
02996 assert( is_decreasing( p_grid ) );
02997 assert( nlat >= 2 );
02998 assert( is_increasing( lat_grid ) );
02999 assert( is_size( z_field, npl, nlat ) );
03000 assert( is_size( r_geoid, nlat ) );
03001 assert( is_size( z_surface, nlat ) );
03002
03003 assert( ppath.dim == 2 );
03004 assert( ppath.np >= 1 );
03005 assert( ppath.gp_p[imax].idx >= 0 );
03006 assert( ppath.gp_p[imax].idx <= ( npl - 2 ) );
03007 assert( ppath.gp_p[imax].fd[0] >= 0 );
03008 assert( ppath.gp_p[imax].fd[0] <= 1 );
03009 assert( ppath.gp_lat[imax].idx >= 0 );
03010 assert( ppath.gp_lat[imax].idx <= ( nlat - 2 ) );
03011 assert( ppath.gp_lat[imax].fd[0] >= 0 );
03012 assert( ppath.gp_lat[imax].fd[0] <= 1 );
03013
03014 assert( za_start >= -180 );
03015 assert( za_start <= 180 );
03016
03017
03018
03019
03020
03021
03022
03023 ilat = gridpos2gridrange( ppath.gp_lat[imax], za_start >= 0 );
03024
03025 lat1 = lat_grid[ilat];
03026 lat3 = lat_grid[ilat+1];
03027
03028
03029
03030
03031
03032
03033
03034 ip = gridpos2gridrange( ppath.gp_p[imax], abs(za_start) <= 90);
03035
03036 r1a = r_geoid[ilat] + z_field(ip,ilat);
03037 r3a = r_geoid[ilat+1] + z_field(ip,ilat+1);
03038 r3b = r_geoid[ilat+1] + z_field(ip+1,ilat+1);
03039 r1b = r_geoid[ilat] + z_field(ip+1,ilat);
03040
03041
03042 double c2 = plevel_slope_2d( lat1, lat3, r1a, r3a );
03043 double c4 = plevel_slope_2d( lat1, lat3, r1b, r3b );
03044
03045
03046
03047
03048
03049
03050 if( is_gridpos_at_index_i( ppath.gp_p[imax], ip ) )
03051 {
03052 double tilt = plevel_angletilt( r_start, c2 );
03053
03054 if( is_los_downwards( za_start, tilt ) )
03055 {
03056 ip--;
03057 r1b = r1a; r3b = r3a; c4 = c2;
03058 r1a = r_geoid[ilat] + z_field(ip,ilat);
03059 r3a = r_geoid[ilat+1] + z_field(ip,ilat+1);
03060 c2 = plevel_slope_2d( lat1, lat3, r1a, r3a );
03061 }
03062 }
03063 else if( is_gridpos_at_index_i( ppath.gp_p[imax], ip+1 ) )
03064 {
03065 double tilt = plevel_angletilt( r_start, c4 );
03066
03067 if( !is_los_downwards( za_start, tilt ) )
03068 {
03069 ip++;
03070 r1a = r1b; r3a = r3b; c2 = c4;
03071 r3b = r_geoid[ilat+1] + z_field(ip+1,ilat+1);
03072 r1b = r_geoid[ilat] + z_field(ip+1,ilat);
03073 c4 = plevel_slope_2d( lat1, lat3, r1b, r3b );
03074 }
03075 }
03076
03077 out3 << " pressure grid range : " << ip << "\n";
03078 out3 << " latitude grid range : " << ilat << "\n";
03079
03080
03081 rsurface1 = r_geoid[ilat] + z_surface[ilat];
03082 rsurface3 = r_geoid[ilat+1] + z_surface[ilat+1];
03083 }
03084
03085
03086
03088
03099 void ppath_end_2d(
03100 Ppath& ppath,
03101 ConstVectorView r_v,
03102 ConstVectorView lat_v,
03103 ConstVectorView za_v,
03104 const double& lstep,
03105 ConstVectorView lat_grid,
03106 ConstMatrixView z_field,
03107 ConstVectorView r_geoid,
03108 const Index& ip,
03109 const Index& ilat,
03110 const Index& endface,
03111 const Index& tanpoint,
03112 const String& method,
03113 const Index& refraction,
03114 const double& ppc )
03115 {
03116
03117 const Index np = r_v.nelem();
03118
03119 out3 << " end face number code : " << endface << "\n";
03120
03121
03122
03123 ppath_init_structure( ppath, 2, np );
03124
03125 ppath.method = method;
03126 ppath.refraction = refraction;
03127 ppath.constant = ppc;
03128
03129 ppath_fill_2d( ppath, r_v, lat_v, za_v, lstep, r_geoid, z_field, lat_grid,
03130 ip, ilat );
03131
03132
03133 if( endface == 7 )
03134 { ppath_set_background( ppath, 2 ); }
03135 else if( tanpoint )
03136 {
03137 ppath.tan_pos.resize(2);
03138 ppath.tan_pos[0] = r_v[np-1];
03139 ppath.tan_pos[1] = lat_v[np-1];
03140 }
03141
03142
03143
03144 if( endface == 1 || endface == 3 )
03145 { gridpos_force_end_fd( ppath.gp_lat[np-1] ); }
03146 else if( endface == 2 || endface == 4 )
03147 { gridpos_force_end_fd( ppath.gp_p[np-1] ); }
03148 }
03149
03150
03151
03153
03164 void ppath_start_3d(
03165 double& r_start,
03166 double& lat_start,
03167 double& lon_start,
03168 double& za_start,
03169 double& aa_start,
03170 Index& ip,
03171 Index& ilat,
03172 Index& ilon,
03173 double& lat1,
03174 double& lat3,
03175 double& lon5,
03176 double& lon6,
03177 double& r15a,
03178 double& r35a,
03179 double& r36a,
03180 double& r16a,
03181 double& r15b,
03182 double& r35b,
03183 double& r36b,
03184 double& r16b,
03185 double& rsurface15,
03186 double& rsurface35,
03187 double& rsurface36,
03188 double& rsurface16,
03189 Ppath& ppath,
03190 ConstVectorView DEBUG_ONLY (p_grid),
03191 ConstVectorView lat_grid,
03192 ConstVectorView lon_grid,
03193 ConstTensor3View z_field,
03194 ConstMatrixView r_geoid,
03195 ConstMatrixView z_surface )
03196 {
03197
03198 const Index imax = ppath.np - 1;
03199
03200
03201 DEBUG_ONLY (const Index npl = p_grid.nelem());
03202 const Index nlat = lat_grid.nelem();
03203 const Index nlon = lon_grid.nelem();
03204
03205
03206 r_start = ppath.pos(imax,0);
03207 lat_start = ppath.pos(imax,1);
03208 lon_start = ppath.pos(imax,2);
03209 za_start = ppath.los(imax,0);
03210 aa_start = ppath.los(imax,1);
03211
03212
03213 assert( npl >= 2 );
03214 assert( is_decreasing( p_grid ) );
03215 assert( nlat >= 2 );
03216 assert( nlon >= 2 );
03217 assert( is_increasing( lat_grid ) );
03218 assert( is_increasing( lon_grid ) );
03219 assert( is_size( z_field, npl, nlat, nlon ) );
03220 assert( is_size( r_geoid, nlat, nlon ) );
03221 assert( is_size( z_surface, nlat, nlon ) );
03222
03223 assert( ppath.dim == 3 );
03224 assert( ppath.np >= 1 );
03225 assert( ppath.gp_p[imax].idx >= 0 );
03226 assert( ppath.gp_p[imax].idx <= ( npl - 2 ) );
03227 assert( ppath.gp_p[imax].fd[0] >= 0 );
03228 assert( ppath.gp_p[imax].fd[0] <= 1 );
03229 assert( ppath.gp_lat[imax].idx >= 0 );
03230 assert( ppath.gp_lat[imax].idx <= ( nlat - 2 ) );
03231 assert( ppath.gp_lat[imax].fd[0] >= 0 );
03232 assert( ppath.gp_lat[imax].fd[0] <= 1 );
03233 assert( ppath.gp_lon[imax].idx >= 0 );
03234 assert( ppath.gp_lon[imax].idx <= ( nlon - 2 ) );
03235 assert( ppath.gp_lon[imax].fd[0] >= 0 );
03236 assert( ppath.gp_lon[imax].fd[0] <= 1 );
03237
03238 assert( za_start >= 0 );
03239 assert( za_start <= 180 );
03240 assert( aa_start >= -180 );
03241 assert( aa_start <= 180 );
03242
03243
03244
03245
03246
03247
03248
03249 if( lat_start == 90 )
03250 {
03251 ilat = nlat - 2;
03252 GridPos gp_tmp;
03253 gridpos( gp_tmp, lon_grid, aa_start );
03254 if( aa_start < 180 )
03255 { ilon = gridpos2gridrange( gp_tmp, 1 ); }
03256 else
03257 { ilon = gridpos2gridrange( gp_tmp, 0 ); }
03258 }
03259 else if( lat_start == -90 )
03260 {
03261 ilat = 0;
03262 GridPos gp_tmp;
03263 gridpos( gp_tmp, lon_grid, aa_start );
03264 if( aa_start < 180 )
03265 { ilon = gridpos2gridrange( gp_tmp, 1 ); }
03266 else
03267 { ilon = gridpos2gridrange( gp_tmp, 0 ); }
03268 }
03269 else
03270 {
03271 if( lat_start > 0 )
03272 ilat = gridpos2gridrange( ppath.gp_lat[imax], abs( aa_start ) < 90 );
03273 else
03274 ilat = gridpos2gridrange( ppath.gp_lat[imax], abs( aa_start ) <= 90 );
03275 if( lon_start < lon_grid[nlon-1] )
03276 { ilon = gridpos2gridrange( ppath.gp_lon[imax], aa_start >= 0 ); }
03277 else
03278 { ilon = nlon - 2; }
03279 }
03280
03281 lat1 = lat_grid[ilat];
03282 lat3 = lat_grid[ilat+1];
03283 lon5 = lon_grid[ilon];
03284 lon6 = lon_grid[ilon+1];
03285
03286
03287
03288
03289
03290
03291
03292 ip = gridpos2gridrange( ppath.gp_p[imax], za_start <= 90 );
03293
03294 r15a = r_geoid(ilat,ilon) + z_field(ip,ilat,ilon);
03295 r35a = r_geoid(ilat+1,ilon) + z_field(ip,ilat+1,ilon);
03296 r36a = r_geoid(ilat+1,ilon+1) + z_field(ip,ilat+1,ilon+1);
03297 r16a = r_geoid(ilat,ilon+1) + z_field(ip,ilat,ilon+1);
03298 r15b = r_geoid(ilat,ilon) + z_field(ip+1,ilat,ilon);
03299 r35b = r_geoid(ilat+1,ilon) + z_field(ip+1,ilat+1,ilon);
03300 r36b = r_geoid(ilat+1,ilon+1) + z_field(ip+1,ilat+1,ilon+1);
03301 r16b = r_geoid(ilat,ilon+1) + z_field(ip+1,ilat,ilon+1);
03302
03303
03304
03305 {
03306
03307 const double rlow = rsurf_at_latlon( lat1, lat3, lon5, lon6,
03308 r15a, r35a, r36a, r16a, lat_start, lon_start );
03309 const double rupp = rsurf_at_latlon( lat1, lat3, lon5, lon6,
03310 r15b, r35b, r36b, r16b, lat_start, lon_start );
03311 if (abs(r_start-rlow) < RTOL || abs(r_start-rupp) < RTOL)
03312 { gridpos_force_end_fd( ppath.gp_p[imax] ); }
03313 }
03314
03315
03316
03317
03318
03319
03320 if( is_gridpos_at_index_i( ppath.gp_p[imax], ip ) )
03321 {
03322
03323 double c2 = plevel_slope_3d( lat1, lat3, lon5, lon6,
03324 r15a, r35a, r36a, r16a, lat_start, lon_start, aa_start );
03325 double tilt = plevel_angletilt( r_start, c2 );
03326
03327 if( is_los_downwards( za_start, tilt ) )
03328 {
03329 ip--;
03330 r15b = r15a; r35b = r35a; r36b = r36a; r16b = r16a;
03331 r15a = r_geoid(ilat,ilon) + z_field(ip,ilat,ilon);
03332 r35a = r_geoid(ilat+1,ilon) + z_field(ip,ilat+1,ilon);
03333 r36a = r_geoid(ilat+1,ilon+1) + z_field(ip,ilat+1,ilon+1);
03334 r16a = r_geoid(ilat,ilon+1) + z_field(ip,ilat,ilon+1);
03335 }
03336 }
03337 else if( is_gridpos_at_index_i( ppath.gp_p[imax], ip+1 ) )
03338 {
03339
03340 double c4 = plevel_slope_3d( lat1, lat3 ,lon5, lon6,
03341 r15b, r35b, r36b, r16b, lat_start, lon_start, aa_start );
03342 double tilt = plevel_angletilt( r_start, c4 );
03343
03344 if( !is_los_downwards( za_start, tilt ) )
03345 {
03346 ip++;
03347 r15a = r15b; r35a = r35b; r36a = r36b; r16a = r16b;
03348 r15b = r_geoid(ilat,ilon) + z_field(ip+1,ilat,ilon);
03349 r35b = r_geoid(ilat+1,ilon) + z_field(ip+1,ilat+1,ilon);
03350 r36b = r_geoid(ilat+1,ilon+1) + z_field(ip+1,ilat+1,ilon+1);
03351 r16b = r_geoid(ilat,ilon+1) + z_field(ip+1,ilat,ilon+1);
03352 }
03353 }
03354
03355 out3 << " pressure grid range : " << ip << "\n";
03356 out3 << " latitude grid range : " << ilat << "\n";
03357 out3 << " longitude grid range : " << ilon << "\n";
03358
03359
03360 rsurface15 = r_geoid(ilat,ilon) + z_surface(ilat,ilon);
03361 rsurface35 = r_geoid(ilat+1,ilon) + z_surface(ilat+1,ilon);
03362 rsurface36 = r_geoid(ilat+1,ilon+1) + z_surface(ilat+1,ilon+1);
03363 rsurface16 = r_geoid(ilat,ilon+1) + z_surface(ilat,ilon+1);
03364 }
03365
03366
03367
03369
03380 void ppath_end_3d(
03381 Ppath& ppath,
03382 ConstVectorView r_v,
03383 ConstVectorView lat_v,
03384 ConstVectorView lon_v,
03385 ConstVectorView za_v,
03386 ConstVectorView aa_v,
03387 const double& lstep,
03388 ConstVectorView lat_grid,
03389 ConstVectorView lon_grid,
03390 ConstTensor3View z_field,
03391 ConstMatrixView r_geoid,
03392 const Index& ip,
03393 const Index& ilat,
03394 const Index& ilon,
03395 const Index& endface,
03396 const Index& tanpoint,
03397 const String& method,
03398 const Index& refraction,
03399 const double& ppc )
03400 {
03401
03402 const Index np = r_v.nelem();
03403
03404 out3 << " end face number code : " << endface << "\n";
03405
03406
03407
03408 ppath_init_structure( ppath, 3, np );
03409
03410 ppath.method = method;
03411 ppath.refraction = refraction;
03412 ppath.constant = ppc;
03413
03414 ppath_fill_3d( ppath, r_v, lat_v, lon_v, za_v, aa_v, lstep,
03415 r_geoid, z_field, lat_grid, lon_grid, ip, ilat, ilon );
03416
03417
03418 if( endface == 7 )
03419 { ppath_set_background( ppath, 2 ); }
03420 if( tanpoint )
03421 {
03422 ppath.tan_pos.resize(3);
03423 ppath.tan_pos[0] = r_v[np-1];
03424 ppath.tan_pos[1] = lat_v[np-1];
03425 ppath.tan_pos[2] = lon_v[np-1];
03426 }
03427
03428
03429
03430 if( endface == 1 || endface == 3 )
03431 { gridpos_force_end_fd( ppath.gp_lat[np-1] ); }
03432 else if( endface == 2 || endface == 4 )
03433 { gridpos_force_end_fd( ppath.gp_p[np-1] ); }
03434 else if( endface == 5 || endface == 6 )
03435 { gridpos_force_end_fd( ppath.gp_lon[np-1] ); }
03436 }
03437
03438
03439
03441
03453 void interpolate_raytracing_points(
03454 Vector& r_v,
03455 Vector& lat_v,
03456 Vector& lon_v,
03457 Vector& za_v,
03458 Vector& aa_v,
03459 double& lstep,
03460 ConstVectorView r_rt,
03461 ConstVectorView lat_rt,
03462 ConstVectorView lon_rt,
03463 ConstVectorView za_rt,
03464 ConstVectorView aa_rt,
03465 ConstVectorView l_rt,
03466 const double& lmax )
03467 {
03468
03469
03470
03471
03472 const Index nrt = r_rt.nelem();
03473 Index n = 2;
03474 double ltotsum = l_rt.sum();
03475
03476 if( lmax > 0 )
03477 { n = max (Index( ceil( ltotsum / lmax ) ) + 1, Index(2)); }
03478
03479 r_v.resize(n);
03480 lat_v.resize(n);
03481 za_v.resize(n);
03482
03483 if( n == 2 )
03484 {
03485 r_v[0] = r_rt[0]; r_v[1] = r_rt[nrt-1];
03486 za_v[0] = za_rt[0]; za_v[1] = za_rt[nrt-1];
03487 lat_v[0] = lat_rt[0]; lat_v[1] = lat_rt[nrt-1];
03488 lstep = ltotsum;
03489 if( lon_rt.nelem() > 0 )
03490 {
03491 lon_v.resize(n); lon_v[0] = lon_rt[0]; lon_v[1] = lon_rt[nrt-1];
03492 aa_v.resize(n); aa_v[0] = aa_rt[0]; aa_v[1] = aa_rt[nrt-1];
03493 }
03494 }
03495 else
03496 {
03497 Vector lsum(nrt), llin(n);
03498 ArrayOfGridPos gp(n);
03499 Matrix itw(n,2);
03500
03501 lsum[0] = 0;
03502 for( Index i=1; i<nrt; i++ )
03503 { lsum[i] = lsum[i-1] + l_rt[i-1]; }
03504
03505 nlinspace( llin, 0, ltotsum, n );
03506
03507 gridpos( gp, lsum, llin );
03508 gridpos_force_end_fd( gp[0] );
03509 gridpos_force_end_fd( gp[n-1] );
03510
03511 interpweights( itw, gp );
03512
03513 interp( r_v, itw, r_rt, gp );
03514 interp( za_v, itw, za_rt, gp );
03515 interp( lat_v, itw, lat_rt, gp );
03516 lstep = llin[1] - llin[0];
03517
03518 if( lon_rt.nelem() > 0 )
03519 {
03520 lon_v.resize(n); interp( lon_v, itw, lon_rt, gp );
03521 aa_v.resize(n); interp( aa_v, itw, aa_rt, gp );
03522 }
03523 }
03524 }
03525
03526
03527
03529
03538 void from_raytracingarrays_to_ppath_vectors_1d_and_2d(
03539 Vector& r_v,
03540 Vector& lat_v,
03541 Vector& za_v,
03542 double& lstep,
03543 const Array<double>& r_array,
03544 const Array<double>& lat_array,
03545 const Array<double>& za_array,
03546 const Array<double>& l_array,
03547 const Index& reversed,
03548 const double& lmax )
03549 {
03550
03551
03552
03553 const Index nrt=r_array.nelem();
03554
03555 Vector r_rt(nrt), lat_rt(nrt), za_rt(nrt), l_rt(nrt-1);
03556
03557 if( !reversed )
03558 {
03559 for( Index i=0; i<nrt; i++ )
03560 {
03561 r_rt[i] = r_array[i];
03562 lat_rt[i] = lat_array[i];
03563 za_rt[i] = za_array[i];
03564 if( i < (nrt-1) )
03565 { l_rt[i] = l_array[i]; }
03566 }
03567 }
03568 else
03569 {
03570 for( Index i=0; i<nrt; i++ )
03571 {
03572 const Index i1 = nrt - 1 - i;
03573 r_rt[i] = r_array[i1];
03574 lat_rt[i] = lat_array[0]+ lat_array[nrt-1] - lat_array[i1];
03575 za_rt[i] = 180 - za_array[i1];
03576 if( i < (nrt-1) )
03577 { l_rt[i] = l_array[i1-1]; }
03578 }
03579 }
03580
03581
03582
03583
03584 Vector dummy;
03585
03586 interpolate_raytracing_points( r_v, lat_v, dummy, za_v, dummy, lstep,
03587 r_rt, lat_rt, Vector(0), za_rt, Vector(0), l_rt, lmax );
03588 }
03589
03590
03591
03593
03600 void from_raytracingarrays_to_ppath_vectors_3d(
03601 Vector& r_v,
03602 Vector& lat_v,
03603 Vector& lon_v,
03604 Vector& za_v,
03605 Vector& aa_v,
03606 double& lstep,
03607 const Array<double>& r_array,
03608 const Array<double>& lat_array,
03609 const Array<double>& lon_array,
03610 const Array<double>& za_array,
03611 const Array<double>& aa_array,
03612 const Array<double>& l_array,
03613 const double& lmax )
03614 {
03615
03616
03617
03618 const Index nrt=r_array.nelem();
03619
03620 Vector r_rt(nrt), lat_rt(nrt), lon_rt(nrt);
03621 Vector za_rt(nrt), aa_rt(nrt),l_rt(nrt-1);
03622
03623 for( Index i=0; i<nrt; i++ )
03624 {
03625 r_rt[i] = r_array[i];
03626 lat_rt[i] = lat_array[i];
03627 lon_rt[i] = lon_array[i];
03628 za_rt[i] = za_array[i];
03629 aa_rt[i] = aa_array[i];
03630 if( i < (nrt-1) )
03631 { l_rt[i] = l_array[i]; }
03632 }
03633
03634
03635
03636
03637 interpolate_raytracing_points( r_v, lat_v, lon_v, za_v, aa_v, lstep,
03638 r_rt, lat_rt, lon_rt, za_rt, aa_rt, l_rt, lmax );
03639 }
03640
03641
03642
03643
03644
03645
03646
03648
03674 void ppath_step_geom_1d(
03675 Ppath& ppath,
03676 ConstVectorView p_grid,
03677 ConstVectorView z_field,
03678 const double& r_geoid,
03679 const double& z_surface,
03680 const double& lmax )
03681 {
03682
03683 double r_start, lat_start, za_start;
03684
03685
03686
03687 Index ip;
03688
03689
03690 ppath_start_1d( r_start, lat_start, za_start, ip,
03691 ppath, p_grid, z_field, r_geoid, z_surface );
03692
03693
03694
03695
03696 double ppc;
03697 if( ppath.constant < 0 )
03698 { ppc = geometrical_ppc( r_start, za_start ); }
03699 else
03700 { ppc = ppath.constant; }
03701
03702
03703
03704
03705
03706
03707 Vector r_v, lat_v, za_v;
03708 double lstep;
03709 Index endface, tanpoint;
03710
03711 do_gridrange_1d( r_v, lat_v, za_v, lstep, endface, tanpoint,
03712 r_start, lat_start, za_start, ppc, lmax,
03713 r_geoid+z_field[ip], r_geoid+z_field[ip+1], r_geoid+z_surface );
03714
03715
03716
03717
03718 String method;
03719 if( lmax < 0 )
03720 { method = "1D geometrical"; }
03721 else
03722 { method = "1D geometrical with length criterion"; }
03723
03724 ppath_end_1d( ppath, r_v, lat_v, za_v, lstep, z_field, r_geoid, ip, endface,
03725 tanpoint, method, 0, ppc );
03726
03727
03728
03729 if( endface == 0 && tanpoint )
03730 {
03731 Ppath ppath2;
03732 ppath_init_structure( ppath2, ppath.dim, ppath.np );
03733 ppath_copy( ppath2, ppath );
03734
03735 out3 << " --- Recursive step to include tangent point --------\n";
03736
03737 ppath_step_geom_1d( ppath2, p_grid, z_field, r_geoid, z_surface, lmax );
03738
03739 out3 << " ----------------------------------------------------\n";
03740
03741
03742 ppath_append( ppath, ppath2 );
03743 }
03744 }
03745
03746
03747
03749
03766 void ppath_step_geom_2d(
03767 Ppath& ppath,
03768 ConstVectorView p_grid,
03769 ConstVectorView lat_grid,
03770 ConstMatrixView z_field,
03771 ConstVectorView r_geoid,
03772 ConstVectorView z_surface,
03773 const double& lmax )
03774 {
03775
03776 double r_start, lat_start, za_start;
03777
03778
03779 Index ip, ilat;
03780
03781
03782 double lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3;
03783
03784
03785 ppath_start_2d( r_start, lat_start, za_start, ip, ilat,
03786 lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3,
03787 ppath, p_grid, lat_grid, z_field, r_geoid, z_surface );
03788
03789
03790
03791
03792 double ppc;
03793 if( ppath.constant < 0 )
03794 { ppc = geometrical_ppc( r_start, za_start ); }
03795 else
03796 { ppc = ppath.constant; }
03797
03798
03799
03800 Vector r_v, lat_v, za_v;
03801 double lstep;
03802 Index endface, tanpoint;
03803
03804 do_gridcell_2d( r_v, lat_v, za_v, lstep, endface, tanpoint,
03805 r_start, lat_start, za_start, ppc, lmax, lat1, lat3,
03806 r1a, r3a, r3b, r1b, rsurface1, rsurface3 );
03807
03808
03809
03810
03811 String method;
03812 if( lmax < 0 )
03813 { method = "2D geometrical"; }
03814 else
03815 { method = "2D geometrical with length criterion"; }
03816
03817 ppath_end_2d( ppath, r_v, lat_v, za_v, lstep, lat_grid, z_field, r_geoid,
03818 ip, ilat, endface, tanpoint, method, 0, ppc );
03819
03820
03821
03822
03823 if( endface == 0 && tanpoint )
03824 {
03825 Ppath ppath2;
03826 ppath_init_structure( ppath2, ppath.dim, ppath.np );
03827 ppath_copy( ppath2, ppath );
03828
03829 out3 << " --- Recursive step to include tangent point --------\n";
03830
03831
03832 ppath_step_geom_2d( ppath2, p_grid, lat_grid, z_field,
03833 r_geoid, z_surface, lmax );
03834
03835 out3 << " ----------------------------------------------------\n";
03836
03837
03838 ppath_append( ppath, ppath2 );
03839 }
03840 }
03841
03842
03843
03845
03863 void ppath_step_geom_3d(
03864 Ppath& ppath,
03865 ConstVectorView p_grid,
03866 ConstVectorView lat_grid,
03867 ConstVectorView lon_grid,
03868 ConstTensor3View z_field,
03869 ConstMatrixView r_geoid,
03870 ConstMatrixView z_surface,
03871 const double& lmax )
03872 {
03873
03874 double r_start, lat_start, lon_start, za_start, aa_start;
03875
03876
03877 Index ip, ilat, ilon;
03878
03879
03880
03881 double lat1, lat3, lon5, lon6;
03882 double r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b;
03883 double rsurface15, rsurface35, rsurface36, rsurface16;
03884
03885
03886 ppath_start_3d( r_start, lat_start, lon_start, za_start, aa_start,
03887 ip, ilat, ilon, lat1, lat3, lon5, lon6,
03888 r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b,
03889 rsurface15, rsurface35, rsurface36, rsurface16,
03890 ppath, p_grid, lat_grid, lon_grid, z_field, r_geoid, z_surface );
03891
03892
03893
03894
03895 double ppc;
03896 if( ppath.constant < 0 )
03897 { ppc = geometrical_ppc( r_start, za_start ); }
03898 else
03899 { ppc = ppath.constant; }
03900
03901
03902
03903 Vector r_v, lat_v, lon_v, za_v, aa_v;
03904 double lstep;
03905 Index endface, tanpoint;
03906
03907 do_gridcell_3d( r_v, lat_v, lon_v, za_v, aa_v, lstep, endface, tanpoint,
03908 r_start, lat_start, lon_start, za_start, aa_start, ppc, lmax,
03909 lat1, lat3, lon5, lon6,
03910 r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b,
03911 rsurface15, rsurface35, rsurface36, rsurface16 );
03912
03913
03914
03915 String method;
03916 if( lmax < 0 )
03917 { method = "3D geometrical"; }
03918 else
03919 { method = "3D geometrical with length criterion"; }
03920
03921 ppath_end_3d( ppath, r_v, lat_v, lon_v, za_v, aa_v, lstep, lat_grid,
03922 lon_grid, z_field, r_geoid, ip, ilat, ilon, endface, tanpoint,
03923 method, 0, ppc );
03924
03925
03926
03927
03928 if( endface == 0 && tanpoint )
03929 {
03930 Ppath ppath2;
03931 ppath_init_structure( ppath2, ppath.dim, ppath.np );
03932 ppath_copy( ppath2, ppath );
03933
03934 out3 << " --- Recursive step to include tangent point --------\n";
03935
03936
03937 ppath_step_geom_3d( ppath2, p_grid, lat_grid, lon_grid, z_field,
03938 r_geoid, z_surface, lmax );
03939
03940 out3 << " ----------------------------------------------------\n";
03941
03942
03943 ppath_append( ppath, ppath2 );
03944 }
03945 }
03946
03947
03948
03949
03950
03951
03952
03953
03954
03956
04008 void raytrace_1d_linear_euler(
04009 Workspace& ws,
04010 Array<double>& r_array,
04011 Array<double>& lat_array,
04012 Array<double>& za_array,
04013 Array<double>& l_array,
04014 Index& endface,
04015 Index& tanpoint,
04016 double r,
04017 double lat,
04018 double za,
04019 Numeric& rte_pressure,
04020 Numeric& rte_temperature,
04021 Vector& rte_vmr_list,
04022 Numeric& refr_index,
04023 const Agenda& refr_index_agenda,
04024 const double& ppc,
04025 const double& lraytrace,
04026 const double& r1,
04027 const double& r3,
04028 const double& r_surface,
04029 const double& r_geoid,
04030 ConstVectorView p_grid,
04031 ConstVectorView z_field,
04032 ConstVectorView t_field,
04033 ConstMatrixView vmr_field )
04034 {
04035
04036 bool ready = false;
04037
04038
04039 Vector r_v, lat_v, za_v;
04040 double lstep, dlat = 9999;
04041
04042 while( !ready )
04043 {
04044
04045 const double ppc_step = geometrical_ppc( r, za );
04046
04047
04048 do_gridrange_1d( r_v, lat_v, za_v, lstep, endface, tanpoint, r, lat, za,
04049 ppc_step, -1, r1, r3, r_surface );
04050
04051 assert( r_v.nelem() == 2 );
04052
04053
04054
04055
04056
04057
04058
04059
04060 if( lstep <= 1.01 * lraytrace )
04061 {
04062 r = r_v[1];
04063 dlat = lat_v[1] - lat;
04064 lat = lat_v[1];
04065 ready = true;
04066 }
04067 else
04068 {
04069 if( abs(za) <= 90 )
04070 { lstep = lraytrace; }
04071 else
04072 { lstep = -lraytrace; }
04073
04074 const double r_new = geompath_r_at_l( ppc_step,
04075 geompath_l_at_r(ppc_step,r) + lstep );
04076
04077 dlat = RAD2DEG * acos( ( r_new*r_new + r*r -
04078 lstep*lstep ) / ( 2 * r_new*r ) );
04079 r = r_new;
04080 lat = lat + dlat;
04081 lstep = abs( lstep );
04082 }
04083
04084
04085
04086
04087 get_refr_index_1d( ws, refr_index, rte_pressure, rte_temperature,
04088 rte_vmr_list, refr_index_agenda,
04089 p_grid, r_geoid, z_field, t_field, vmr_field, r );
04090
04091 const double ppc_local = ppc / refr_index;
04092
04093 if( ready && tanpoint )
04094 { za = 90; }
04095 else if( r >= ppc_local )
04096 { za = geompath_za_at_r( ppc_local, za, r ); }
04097 else
04098 {
04099 r = ppc_local;
04100 if( za > 90 )
04101 {
04102 ready = true;
04103 tanpoint = 1;
04104 if( r > r1 )
04105 { endface = 0; }
04106 }
04107 za = 90;
04108 }
04109
04110
04111 r_array.push_back( r );
04112 lat_array.push_back( lat );
04113 za_array.push_back( za );
04114 l_array.push_back( lstep );
04115 }
04116 }
04117
04118
04119
04121
04175 void raytrace_2d_linear_euler(
04176 Workspace& ws,
04177 Array<double>& r_array,
04178 Array<double>& lat_array,
04179 Array<double>& za_array,
04180 Array<double>& l_array,
04181 Index& endface,
04182 Index& tanpoint,
04183 double r,
04184 double lat,
04185 double za,
04186 Numeric& rte_pressure,
04187 Numeric& rte_temperature,
04188 Vector& rte_vmr_list,
04189 Numeric& refr_index,
04190 const Agenda& refr_index_agenda,
04191 const double& lraytrace,
04192 const double& lat1,
04193 const double& lat3,
04194 const double& r1a,
04195 const double& r3a,
04196 const double& r3b,
04197 const double& r1b,
04198 const double& rsurface1,
04199 const double& rsurface3,
04200 ConstVectorView p_grid,
04201 ConstVectorView lat_grid,
04202 ConstVectorView r_geoid,
04203 ConstMatrixView z_field,
04204 ConstMatrixView t_field,
04205 ConstTensor3View vmr_field )
04206 {
04207
04208 bool ready = false;
04209
04210
04211 Vector r_v, lat_v, za_v;
04212 double lstep, dlat = 9999, r_new, lat_new;
04213
04214 while( !ready )
04215 {
04216
04217 const double ppc_step = geometrical_ppc( r, za );
04218
04219
04220 do_gridcell_2d( r_v, lat_v, za_v, lstep, endface, tanpoint,
04221 r, lat, za, ppc_step, -1, lat1, lat3, r1a, r3a, r3b, r1b,
04222 rsurface1, rsurface3 );
04223 assert( r_v.nelem() == 2 );
04224
04225
04226
04227
04228
04229
04230
04231
04232 if( lstep <= 1.01 * lraytrace )
04233 {
04234 r_new = r_v[1];
04235 dlat = lat_v[1] - lat;
04236 lat_new = lat_v[1];
04237 ready = true;
04238 }
04239 else
04240 {
04241 if( abs(za) <= 90 )
04242 { lstep = lraytrace; }
04243 else
04244 { lstep = -lraytrace; }
04245
04246 r_new = geompath_r_at_l( ppc_step,
04247 geompath_l_at_r(ppc_step,r) + lstep );
04248 dlat = RAD2DEG * acos( ( r_new*r_new + r*r -
04249 lstep*lstep ) / ( 2 * r_new*r ) );
04250 if( za < 0 )
04251 { dlat = -dlat; }
04252
04253 lat_new = lat + dlat;
04254 lstep = abs( lstep );
04255
04256
04257
04258 if( lat_new < lat1 )
04259 { lat_new = lat1; }
04260 else if( lat_new > lat3 )
04261 { lat_new = lat3; }
04262 }
04263
04264
04265 if( ready && tanpoint )
04266 {
04267
04268
04269
04270
04271 za = sign(za) * 90;
04272 }
04273 else
04274 {
04275 Numeric dndr, dndlat;
04276 const double za_rad = DEG2RAD * za;
04277
04278 refr_gradients_2d( ws, refr_index, dndr, dndlat, rte_pressure,
04279 rte_temperature, rte_vmr_list, refr_index_agenda,
04280 p_grid, lat_grid, r_geoid, z_field, t_field,
04281 vmr_field, r, lat );
04282
04283 za += -dlat + RAD2DEG * lstep / refr_index * ( -sin(za_rad) * dndr +
04284 cos(za_rad) * dndlat );
04285
04286
04287 if( za < -180 )
04288 { za += 360; }
04289 else if( za > 180 )
04290 { za -= 360; }
04291 }
04292
04293 r = r_new;
04294 lat = lat_new;
04295
04296
04297
04298 if( lat == lat1 && za < 0 )
04299 { endface = 1; ready = 1; }
04300 else if( lat == lat3 && za > 0 )
04301 { endface = 3; ready = 1; }
04302
04303
04304 r_array.push_back( r );
04305 lat_array.push_back( lat );
04306 za_array.push_back( za );
04307 l_array.push_back( lstep );
04308 }
04309 }
04310
04311
04312
04314
04382 void raytrace_3d_linear_euler(
04383 Workspace& ws,
04384 Array<double>& r_array,
04385 Array<double>& lat_array,
04386 Array<double>& lon_array,
04387 Array<double>& za_array,
04388 Array<double>& aa_array,
04389 Array<double>& l_array,
04390 Index& endface,
04391 Index& tanpoint,
04392 double r,
04393 double lat,
04394 double lon,
04395 double za,
04396 double aa,
04397 Numeric& rte_pressure,
04398 Numeric& rte_temperature,
04399 Vector& rte_vmr_list,
04400 Numeric& refr_index,
04401 const Agenda& refr_index_agenda,
04402 const double& lraytrace,
04403 const double& lat1,
04404 const double& lat3,
04405 const double& lon5,
04406 const double& lon6,
04407 const double& r15a,
04408 const double& r35a,
04409 const double& r36a,
04410 const double& r16a,
04411 const double& r15b,
04412 const double& r35b,
04413 const double& r36b,
04414 const double& r16b,
04415 const double& rsurface15,
04416 const double& rsurface35,
04417 const double& rsurface36,
04418 const double& rsurface16,
04419 ConstVectorView p_grid,
04420 ConstVectorView lat_grid,
04421 ConstVectorView lon_grid,
04422 ConstMatrixView r_geoid,
04423 ConstTensor3View z_field,
04424 ConstTensor3View t_field,
04425 ConstTensor4View vmr_field )
04426 {
04427
04428 bool ready = false;
04429
04430
04431 Vector r_v, lat_v, lon_v, za_v, aa_v;
04432 double r_new, lat_new, lon_new, za_new, aa_new;
04433 double lstep;
04434
04435 while( !ready )
04436 {
04437
04438 const double ppc_step = geometrical_ppc( r, za );
04439
04440
04441 do_gridcell_3d( r_v, lat_v, lon_v, za_v, aa_v, lstep, endface, tanpoint,
04442 r, lat, lon, za, aa, ppc_step, -1, lat1, lat3, lon5, lon6,
04443 r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b,
04444 rsurface15, rsurface35, rsurface36, rsurface16 );
04445
04446 assert( r_v.nelem() == 2 );
04447
04448
04449
04450
04451
04452
04453
04454
04455
04456 if( lstep <= 1.01 * lraytrace )
04457 {
04458 r_new = r_v[1];
04459 lat_new = lat_v[1];
04460 lon_new = lon_v[1];
04461 za_new = za_v[1];
04462 aa_new = aa_v[1];
04463 ready = true;
04464 }
04465 else
04466 {
04467
04468 double x, y, z, dx, dy, dz;
04469 poslos2cart( x, y, z, dx, dy, dz, r, lat, lon, za, aa );
04470
04471 lstep = lraytrace;
04472
04473 cart2poslos( r_new, lat_new, lon_new, za_new, aa_new,
04474 x+dx*lstep, y+dy*lstep, z+dz*lstep, dx, dy, dz );
04475
04476
04477
04478 if( lat_new < lat1 )
04479 { lat_new = lat1; }
04480 else if( lat_new > lat3 )
04481 { lat_new = lat3; }
04482 if( lon_new < lon5 )
04483 { lon_new = lon5; }
04484 else if( lon_new > lon6 )
04485 { lon_new = lon6; }
04486
04487
04488
04489
04490
04491
04492 if( za < ANGTOL || za > 180-ANGTOL )
04493 { za_new = za; }
04494 else
04495 { za_new = geompath_za_at_r( ppc_step, za, r_new ); }
04496
04497
04498
04499 if( abs( lat ) < 90 &&
04500 ( abs(aa) < ANGTOL || abs( aa ) > 180-ANGTOL ) )
04501 {
04502 aa_new = aa;
04503 lon_new = lon;
04504 }
04505
04506
04507 resolve_lon( lon_new, lon5, lon6 );
04508 }
04509
04510
04511 if( ready && tanpoint )
04512 {
04513
04514
04515
04516
04517 za_new = 90;
04518 }
04519 else
04520 {
04521 Numeric dndr, dndlat, dndlon;
04522
04523 refr_gradients_3d( ws,
04524 refr_index, dndr, dndlat, dndlon, rte_pressure,
04525 rte_temperature, rte_vmr_list, refr_index_agenda,
04526 p_grid, lat_grid, lon_grid, r_geoid, z_field,
04527 t_field, vmr_field, r, lat, lon );
04528
04529 const double aterm = RAD2DEG * lstep / refr_index;
04530 const double za_rad = DEG2RAD * za;
04531 const double aa_rad = DEG2RAD * aa;
04532 const double sinza = sin( za_rad );
04533 const double sinaa = sin( aa_rad );
04534 const double cosaa = cos( aa_rad );
04535
04536
04537 if( za < ANGTOL || za > 180-ANGTOL )
04538 {
04539 za_new += aterm * ( cos(za_rad) *
04540 ( cosaa * dndlat + sinaa * dndlon ) );
04541 aa_new = RAD2DEG * atan2( dndlon, dndlat);
04542 }
04543 else
04544 {
04545 za_new += aterm * ( -sinza * dndr + cos(za_rad) *
04546 ( cosaa * dndlat + sinaa * dndlon ) );
04547 aa_new += aterm * sinza * ( cosaa * dndlon - sinaa * dndlat );
04548 }
04549
04550
04551 if( za_new > 180 )
04552 { za_new = 180 - za_new; }
04553 else if( za_new < 0 )
04554 { za_new = -za_new; }
04555
04556 if( aa_new > 180 )
04557 { aa_new -= 360; }
04558 else if( aa_new < -180 )
04559 { aa_new += 360; }
04560 }
04561
04562 r = r_new;
04563 lat = lat_new;
04564 lon = lon_new;
04565 za = za_new;
04566 aa = aa_new;
04567
04568
04569
04570
04571 if( lon == lon5 && aa < 0 )
04572 { endface = 5; ready = 1; }
04573 else if( lon == lon6 && aa > 0 )
04574 { endface = 6; ready = 1; }
04575 else if( za > 0 && za < 180 )
04576 {
04577 if( lat == lat1 && lat != -90 && abs( aa ) > 90 )
04578 { endface = 1; ready = 1; }
04579 else if( lat == lat3 && lat != 90 && abs( aa ) < 90 )
04580 { endface = 3; ready = 1; }
04581 }
04582
04583
04584 r_array.push_back( r );
04585 lat_array.push_back( lat );
04586 lon_array.push_back( lon );
04587 za_array.push_back( za );
04588 aa_array.push_back( aa );
04589 l_array.push_back( lstep );
04590 }
04591 }
04592
04593
04594
04595
04596
04597
04598
04599
04600
04602
04632 void ppath_step_refr_1d(
04633 Workspace& ws,
04634 Ppath& ppath,
04635 Numeric& rte_pressure,
04636 Numeric& rte_temperature,
04637 Vector& rte_vmr_list,
04638 Numeric& refr_index,
04639 const Agenda& refr_index_agenda,
04640 ConstVectorView p_grid,
04641 ConstVectorView z_field,
04642 ConstVectorView t_field,
04643 ConstMatrixView vmr_field,
04644 const double& r_geoid,
04645 const double& z_surface,
04646 const String& rtrace_method,
04647 const double& lraytrace,
04648 const double& lmax )
04649 {
04650
04651 double r_start, lat_start, za_start;
04652
04653
04654
04655 Index ip;
04656
04657
04658 ppath_start_1d( r_start, lat_start, za_start, ip,
04659 ppath, p_grid, z_field, r_geoid, z_surface );
04660
04661
04662 assert( t_field.nelem() == p_grid.nelem() );
04663 assert( vmr_field.ncols() == p_grid.nelem() );
04664
04665
04666
04667
04668
04669
04670 double ppc;
04671 if( ppath.constant < 0 )
04672 {
04673 get_refr_index_1d( ws, refr_index, rte_pressure, rte_temperature,
04674 rte_vmr_list,
04675 refr_index_agenda, p_grid, r_geoid, z_field,
04676 t_field, vmr_field, r_start );
04677 ppc = refraction_ppc( r_start, za_start, refr_index );
04678 }
04679 else
04680 { ppc = ppath.constant; }
04681
04682
04683
04684
04685
04686
04687 Array<double> r_array, lat_array, za_array, l_array;
04688
04689
04690 r_array.push_back( r_start );
04691 lat_array.push_back( lat_start );
04692 za_array.push_back( za_start );
04693
04694
04695 String method;
04696
04697
04698 Index endface, tanpoint;
04699
04700 if( rtrace_method == "linear_euler" )
04701 {
04702 if( lmax < 0 )
04703 { method = "1D linear Euler"; }
04704 else
04705 { method = "1D linear Euler, with length criterion"; }
04706
04707 raytrace_1d_linear_euler( ws,
04708 r_array, lat_array, za_array, l_array, endface,
04709 tanpoint, r_start, lat_start, za_start, rte_pressure, rte_temperature,
04710 rte_vmr_list, refr_index, refr_index_agenda, ppc, lraytrace,
04711 r_geoid+z_field[ip], r_geoid+z_field[ip+1], r_geoid + z_surface,
04712 r_geoid, p_grid, z_field, t_field, vmr_field );
04713 }
04714 #ifndef NDEBUG
04715 else
04716 {
04717 bool known_ray_trace_method = false;
04718 assert( known_ray_trace_method );
04719 }
04720 #endif
04721
04722
04723
04724
04725
04726 Vector r_v, lat_v, za_v;
04727 double lstep;
04728
04729 from_raytracingarrays_to_ppath_vectors_1d_and_2d( r_v, lat_v, za_v, lstep,
04730 r_array, lat_array, za_array, l_array, 0, lmax );
04731
04732
04733
04734 ppath_end_1d( ppath, r_v, lat_v, za_v, lstep, z_field, r_geoid, ip, endface,
04735 tanpoint, method, 1, ppc );
04736
04737
04738
04739 if( endface == 0 && tanpoint )
04740 {
04741
04742
04743 Ppath ppath2;
04744 ppath_init_structure( ppath2, ppath.dim, ppath.np );
04745 ppath_copy( ppath2, ppath );
04746
04747 out3 << " --- Recursive step to include tangent point --------\n";
04748
04749 ppath_step_refr_1d( ws,
04750 ppath2, rte_pressure, rte_temperature, rte_vmr_list,
04751 refr_index, refr_index_agenda, p_grid, z_field, t_field,
04752 vmr_field, r_geoid, z_surface, rtrace_method, lraytrace, lmax );
04753
04754 out3 << " ----------------------------------------------------\n";
04755
04756
04757 ppath_append( ppath, ppath2 );
04758 }
04759 }
04760
04761
04762
04764
04793 void ppath_step_refr_2d(
04794 Workspace& ws,
04795 Ppath& ppath,
04796 Numeric& rte_pressure,
04797 Numeric& rte_temperature,
04798 Vector& rte_vmr_list,
04799 Numeric& refr_index,
04800 const Agenda& refr_index_agenda,
04801 ConstVectorView p_grid,
04802 ConstVectorView lat_grid,
04803 ConstMatrixView z_field,
04804 ConstMatrixView t_field,
04805 ConstTensor3View vmr_field,
04806 ConstVectorView r_geoid,
04807 ConstVectorView z_surface,
04808 const String& rtrace_method,
04809 const double& lraytrace,
04810 const double& lmax )
04811 {
04812
04813 double r_start, lat_start, za_start;
04814
04815
04816 Index ip, ilat;
04817
04818
04819 double lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3;
04820
04821
04822 ppath_start_2d( r_start, lat_start, za_start, ip, ilat,
04823 lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3,
04824 ppath, p_grid, lat_grid, z_field, r_geoid, z_surface );
04825
04826
04827 assert( t_field.nrows() == p_grid.nelem() );
04828 assert( t_field.ncols() == lat_grid.nelem() );
04829 assert( vmr_field.nrows() == p_grid.nelem() );
04830 assert( vmr_field.ncols() == lat_grid.nelem() );
04831
04832
04833
04834
04835
04836
04837
04838
04839 Array<double> r_array, lat_array, za_array, l_array;
04840
04841
04842 r_array.push_back( r_start );
04843 lat_array.push_back( lat_start );
04844 za_array.push_back( za_start );
04845
04846
04847 String method;
04848
04849
04850 Index endface, tanpoint;
04851
04852 if( rtrace_method == "linear_euler" )
04853 {
04854 if( lmax < 0 )
04855 { method = "2D linear Euler"; }
04856 else
04857 { method = "2D linear Euler, with length criterion"; }
04858
04859 raytrace_2d_linear_euler( ws,
04860 r_array, lat_array, za_array, l_array, endface,
04861 tanpoint, r_start, lat_start, za_start, rte_pressure, rte_temperature,
04862 rte_vmr_list, refr_index, refr_index_agenda, lraytrace,
04863 lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3,
04864 p_grid, lat_grid, r_geoid, z_field, t_field, vmr_field );
04865 }
04866 #ifndef NDEBUG
04867 else
04868 {
04869 bool known_ray_trace_method = false;
04870 assert( known_ray_trace_method );
04871 }
04872 #endif
04873
04874
04875
04876
04877
04878 Vector r_v, lat_v, za_v;
04879 double lstep;
04880
04881 from_raytracingarrays_to_ppath_vectors_1d_and_2d( r_v, lat_v, za_v, lstep,
04882 r_array, lat_array, za_array, l_array, 0, lmax );
04883
04884
04885
04886 ppath_end_2d( ppath, r_v, lat_v, za_v, lstep, lat_grid, z_field, r_geoid,
04887 ip, ilat, endface, tanpoint, method, 1, -1 );
04888
04889
04890
04891
04892 if( endface == 0 && tanpoint )
04893 {
04894 Ppath ppath2;
04895 ppath_init_structure( ppath2, ppath.dim, ppath.np );
04896 ppath_copy( ppath2, ppath );
04897
04898 out3 << " --- Recursive step to include tangent point --------\n";
04899
04900
04901 ppath_step_refr_2d( ws,
04902 ppath2, rte_pressure, rte_temperature, rte_vmr_list,
04903 refr_index, refr_index_agenda, p_grid, lat_grid, z_field,
04904 t_field, vmr_field,
04905 r_geoid, z_surface, rtrace_method, lraytrace, lmax );
04906
04907 out3 << " ----------------------------------------------------\n";
04908
04909
04910 ppath_append( ppath, ppath2 );
04911 }
04912 }
04913
04914
04915
04917
04947 void ppath_step_refr_3d(
04948 Workspace& ws,
04949 Ppath& ppath,
04950 Numeric& rte_pressure,
04951 Numeric& rte_temperature,
04952 Vector& rte_vmr_list,
04953 Numeric& refr_index,
04954 const Agenda& refr_index_agenda,
04955 ConstVectorView p_grid,
04956 ConstVectorView lat_grid,
04957 ConstVectorView lon_grid,
04958 ConstTensor3View z_field,
04959 ConstTensor3View t_field,
04960 ConstTensor4View vmr_field,
04961 ConstMatrixView r_geoid,
04962 ConstMatrixView z_surface,
04963 const String& rtrace_method,
04964 const double& lraytrace,
04965 const double& lmax )
04966 {
04967
04968 double r_start, lat_start, lon_start, za_start, aa_start;
04969
04970
04971 Index ip, ilat, ilon;
04972
04973
04974
04975 double lat1, lat3, lon5, lon6;
04976 double r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b;
04977 double rsurface15, rsurface35, rsurface36, rsurface16;
04978
04979
04980 ppath_start_3d( r_start, lat_start, lon_start, za_start, aa_start,
04981 ip, ilat, ilon, lat1, lat3, lon5, lon6,
04982 r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b,
04983 rsurface15, rsurface35, rsurface36, rsurface16,
04984 ppath, p_grid, lat_grid, lon_grid, z_field, r_geoid, z_surface );
04985
04986
04987 assert( t_field.npages() == p_grid.nelem() );
04988 assert( t_field.nrows() == lat_grid.nelem() );
04989 assert( t_field.ncols() == lon_grid.nelem() );
04990 assert( vmr_field.npages() == p_grid.nelem() );
04991 assert( vmr_field.nrows() == lat_grid.nelem() );
04992 assert( vmr_field.ncols() == lon_grid.nelem() );
04993
04994
04995
04996
04997
04998
04999
05000
05001 Array<double> r_array, lat_array, lon_array, za_array, aa_array, l_array;
05002
05003
05004 r_array.push_back( r_start );
05005 lat_array.push_back( lat_start );
05006 lon_array.push_back( lon_start );
05007 za_array.push_back( za_start );
05008 aa_array.push_back( aa_start );
05009
05010
05011 String method;
05012
05013
05014 Index endface, tanpoint;
05015
05016 if( rtrace_method == "linear_euler" )
05017 {
05018 if( lmax < 0 )
05019 { method = "3D linear Euler"; }
05020 else
05021 { method = "3D linear Euler, with length criterion"; }
05022
05023 raytrace_3d_linear_euler( ws, r_array, lat_array, lon_array, za_array,
05024 aa_array, l_array, endface, tanpoint, r_start,
05025 lat_start, lon_start, za_start, aa_start,
05026 rte_pressure, rte_temperature, rte_vmr_list,
05027 refr_index, refr_index_agenda, lraytrace,
05028 lat1, lat3, lon5, lon6,
05029 r15a, r35a, r36a, r15a, r15b, r35b, r36b, r15b,
05030 rsurface15, rsurface35, rsurface36, rsurface16,
05031 p_grid, lat_grid, lon_grid,
05032 r_geoid, z_field, t_field, vmr_field );
05033 }
05034 #ifndef NDEBUG
05035 else
05036 {
05037 bool known_ray_trace_method = false;
05038 assert( known_ray_trace_method );
05039 }
05040 #endif
05041
05042
05043
05044
05045
05046 Vector r_v, lat_v, lon_v, za_v, aa_v;
05047 double lstep;
05048
05049 from_raytracingarrays_to_ppath_vectors_3d( r_v, lat_v, lon_v, za_v, aa_v,
05050 lstep, r_array, lat_array, lon_array,
05051 za_array, aa_array, l_array, lmax );
05052
05053
05054
05055 ppath_end_3d( ppath, r_v, lat_v, lon_v, za_v, aa_v, lstep, lat_grid,
05056 lon_grid, z_field, r_geoid, ip, ilat, ilon, endface, tanpoint,
05057 method, 1, -1 );
05058
05059
05060
05061
05062 if( endface == 0 && tanpoint )
05063 {
05064 Ppath ppath2;
05065 ppath_init_structure( ppath2, ppath.dim, ppath.np );
05066 ppath_copy( ppath2, ppath );
05067
05068 out3 << " --- Recursive step to include tangent point --------\n";
05069
05070
05071 ppath_step_refr_3d( ws,
05072 ppath2, rte_pressure, rte_temperature, rte_vmr_list,
05073 refr_index, refr_index_agenda, p_grid, lat_grid,
05074 lon_grid, z_field, t_field, vmr_field,
05075 r_geoid, z_surface, rtrace_method, lraytrace, lmax );
05076
05077 out3 << " ----------------------------------------------------\n";
05078
05079
05080 ppath_append( ppath, ppath2 );
05081 }
05082 }
05083
05084
05085
05086
05087
05088
05089
05090
05092
05141 void ppath_start_stepping(
05142 Ppath& ppath,
05143 const Index& atmosphere_dim,
05144 ConstVectorView p_grid,
05145 ConstVectorView lat_grid,
05146 ConstVectorView lon_grid,
05147 ConstTensor3View z_field,
05148 ConstMatrixView r_geoid,
05149 ConstMatrixView z_surface,
05150 const Index& cloudbox_on,
05151 const ArrayOfIndex& cloudbox_limits,
05152 const bool& outside_cloudbox,
05153 ConstVectorView rte_pos,
05154 ConstVectorView rte_los )
05155 {
05156
05157
05158
05159
05160 ppath_init_structure( ppath, atmosphere_dim, 1 );
05161
05162
05163 const Index np = p_grid.nelem();
05164
05165
05166
05167
05168 if( atmosphere_dim == 1 )
05169 {
05170
05171 const double r_surface = r_geoid(0,0) + z_surface(0,0);
05172
05173
05174 const double r_top = r_geoid(0,0) + z_field(np-1,0,0);
05175
05176
05177 if( (rte_pos[0] + RTOL) < r_surface )
05178 {
05179 ostringstream os;
05180 os << "The ppath starting point is placed "
05181 << (r_surface - rte_pos[0])/1e3 << " km below surface level.\n"
05182 << "This point must be above the surface.";
05183 throw runtime_error(os.str());
05184 }
05185
05186 out2 << " sensor altitude : " << (rte_pos[0]-r_geoid(0,0))/1e3
05187 << " km\n";
05188
05189
05190 Vector geom_tan_pos(0);
05191 if( rte_los[0] > 90 )
05192 {
05193 geom_tan_pos.resize(2);
05194 geom_tan_pos[0] = geometrical_ppc( rte_pos[0], rte_los[0] );
05195 geom_tan_pos[1] = geompath_lat_at_za( rte_los[0], 0, 90 );
05196 out2 << " geom. tangent radius : " << geom_tan_pos[0]/1e3
05197 <<" km\n";
05198 out2 << " geom. tangent latitude : " << geom_tan_pos[1] << "\n";
05199 out2 << " geom. tangent altitude : "
05200 << (geom_tan_pos[0]-r_geoid(0,0))/1e3 << " km\n";
05201 }
05202
05203
05204 ppath.pos(0,0) = rte_pos[0];
05205 ppath.los(0,0) = rte_los[0];
05206 ppath.pos(0,1) = 0;
05207 ppath.z[0] = ppath.pos(0,0) - r_geoid(0,0);
05208
05209
05210
05211 if( rte_pos[0] < r_top )
05212 {
05213
05214
05215
05216
05217
05218
05219 if( ppath.pos(0,0) == r_surface && ppath.los(0,0) > 90 )
05220 { ppath_set_background( ppath, 2 ); }
05221
05222
05223 if( cloudbox_on )
05224 {
05225 if( outside_cloudbox )
05226 {
05227
05228 if( ppath.z[0] > z_field(cloudbox_limits[0],0,0) &&
05229 ppath.z[0] < z_field(cloudbox_limits[1],0,0) )
05230 { ppath_set_background( ppath, 4 ); }
05231
05232 else if( ( ppath.z[0] == z_field(cloudbox_limits[0],0,0) &&
05233 ppath.los(0,0) <= 90 )
05234 ||
05235 ( ppath.z[0] == z_field(cloudbox_limits[1],0,0) &&
05236 ppath.los(0,0) > 90 ) )
05237 {
05238 ppath_set_background( ppath, 3 );
05239 }
05240 }
05241 else
05242 {
05243 assert( ppath.z[0] >= z_field(cloudbox_limits[0],0,0) &&
05244 ppath.z[0] <= z_field(cloudbox_limits[1],0,0) );
05245
05246 }
05247 }
05248 }
05249
05250
05251 else
05252 {
05253
05254 if( rte_los[0] <= 90 )
05255 throw runtime_error("When the sensor is placed outside the model"
05256 " atmosphere, upward observations are not allowed." );
05257
05258
05259
05260 ppath.constant = geom_tan_pos[0];
05261
05262
05263 if( ppath.constant >= r_top )
05264 {
05265 ppath_set_background( ppath, 1 );
05266 out1 << " --- WARNING ---, path is totally outside of the "
05267 << "model atmosphere\n";
05268 }
05269
05270
05271 else
05272 {
05273 ppath.z[0] = z_field(np-1,0,0);
05274 ppath.pos(0,0) = r_top;
05275 ppath.los(0,0) = geompath_za_at_r( ppath.constant, rte_los[0],
05276 r_top );
05277 ppath.pos(0,1) = geompath_lat_at_za( rte_los[0], 0,
05278 ppath.los(0,0) );
05279 }
05280 }
05281
05282
05283 if( ppath.z[0] <= z_field(np-1,0,0) )
05284 { gridpos( ppath.gp_p, z_field(joker,0,0), ppath.z ); }
05285
05286
05287 gridpos_check_fd( ppath.gp_p[0] );
05288
05289
05290 if( geom_tan_pos.nelem() == 2 )
05291 {
05292 ppath.geom_tan_pos.resize(2);
05293 ppath.geom_tan_pos = geom_tan_pos;
05294 }
05295
05296 }
05297
05298
05299
05300 else if( atmosphere_dim == 2 )
05301 {
05302
05303 const Index nlat = lat_grid.nelem();
05304
05305
05306
05307
05308
05309
05310 bool is_inside = false;
05311 double rv_geoid=-1, rv_surface=-1;
05312 GridPos gp_lat;
05313 Vector itw(2);
05314
05315 if( rte_pos[1] >= lat_grid[0] && rte_pos[1] <= lat_grid[nlat-1] )
05316 {
05317 gridpos( gp_lat, lat_grid, rte_pos[1] );
05318 interpweights( itw, gp_lat );
05319
05320 rv_geoid = interp( itw, r_geoid(joker,0), gp_lat );
05321 rv_surface = rv_geoid + interp( itw, z_surface(joker,0), gp_lat );
05322
05323 out2 << " sensor altitude : " << (rte_pos[0]-rv_geoid)/1e3
05324 << " km\n";
05325
05326 if( rte_pos[0] < ( rv_geoid +
05327 interp( itw, z_field(np-1,joker,0), gp_lat ) ) )
05328 { is_inside = true; }
05329 }
05330
05331
05332
05333
05334
05335 Vector geom_tan_pos(0);
05336 double geom_tan_z=-2, geom_tan_atmtop=-1;
05337
05338 if( abs(rte_los[0]) > 90 )
05339 {
05340 geom_tan_pos.resize(2);
05341 geom_tan_pos[0] = geometrical_ppc( rte_pos[0], rte_los[0] );
05342 if( rte_los[0] > 0 )
05343 { geom_tan_pos[1] =
05344 geompath_lat_at_za( rte_los[0], rte_pos[1], 90 ); }
05345 else
05346 { geom_tan_pos[1] =
05347 geompath_lat_at_za( rte_los[0], rte_pos[1], -90 ); }
05348 out2 << " geom. tangent radius : " << geom_tan_pos[0] / 1e3
05349 <<" km\n";
05350 out2 << " geom. tangent latitude : " << geom_tan_pos[1]
05351 << "\n";
05352
05353 if( geom_tan_pos[1] >= lat_grid[0] &&
05354 geom_tan_pos[1] <= lat_grid[nlat-1] )
05355 {
05356 GridPos gp_tan;
05357 Vector itw_tan(2);
05358 gridpos( gp_tan, lat_grid, geom_tan_pos[1] );
05359 interpweights( itw_tan, gp_tan );
05360 geom_tan_z = geom_tan_pos[0] -
05361 interp( itw_tan, r_geoid(joker,0), gp_tan );
05362 geom_tan_atmtop =
05363 interp( itw_tan, z_field(np-1,joker,0), gp_tan );
05364 out2 << " geom. tangent altitude : " << geom_tan_z/1e3
05365 << " km\n";
05366 }
05367 }
05368
05369
05370 ppath.pos(0,0) = rte_pos[0];
05371 ppath.pos(0,1) = rte_pos[1];
05372 ppath.los(0,0) = rte_los[0];
05373
05374
05375 if( is_inside )
05376 {
05377
05378 if( (rte_pos[0] + RTOL) < rv_surface )
05379 {
05380 ostringstream os;
05381 os << "The ppath starting point is placed "
05382 << (rv_surface - rte_pos[0])/1e3 << " km below surface level.\n"
05383 << "This point must be above the surface.";
05384 throw runtime_error(os.str());
05385 }
05386
05387
05388 if( ( rte_pos[1] == lat_grid[0] && rte_los[0] < 0 ) )
05389 throw runtime_error( "The sensor is at the lower latitude end "
05390 "point and the zenith angle < 0." );
05391 if( rte_pos[1] == lat_grid[nlat-1] && rte_los[0] >= 0 )
05392 throw runtime_error( "The sensor is at the upper latitude end "
05393 "point and the zenith angle >= 0." );
05394
05395
05396 ppath.z[0] = ppath.pos(0,0) - rv_geoid;
05397
05398
05399
05400
05401
05402 ppath.gp_lat[0].idx = gp_lat.idx;
05403 ppath.gp_lat[0].fd[0] = gp_lat.fd[0];
05404 ppath.gp_lat[0].fd[1] = gp_lat.fd[1];
05405
05406
05407
05408 Vector z_grid(np);
05409 z_at_lat_2d( z_grid, p_grid, lat_grid,
05410 z_field(joker,joker,0), gp_lat );
05411 gridpos( ppath.gp_p, z_grid, ppath.z );
05412
05413
05414 if( ppath.pos(0,0) == rv_surface )
05415 {
05416
05417 const double rslope = plevel_slope_2d( lat_grid,
05418 r_geoid(joker,0), z_surface(joker,0),
05419 gp_lat, ppath.los(0,0) );
05420
05421
05422 const double atilt = plevel_angletilt( rv_surface, rslope);
05423
05424
05425
05426
05427 if( is_los_downwards( ppath.los(0,0), atilt ) )
05428 { ppath_set_background( ppath, 2 ); }
05429 }
05430 }
05431
05432
05433 else
05434 {
05435
05436 if( abs(rte_los[0]) <= 90 )
05437 {
05438 ostringstream os;
05439 os << "When the sensor is placed outside the model atmosphere,\n"
05440 << "upward observations are not allowed.";
05441 throw runtime_error( os.str() );
05442 }
05443
05444
05445
05446 ppath.constant = geom_tan_pos[0];
05447
05448
05449 if( ( rte_pos[1] <= lat_grid[0] && rte_los[0] <= 0 ) ||
05450 ( rte_pos[1] >= lat_grid[nlat-1] && rte_los[0] >= 0 ) )
05451 {
05452 ostringstream os;
05453 os << "The sensor is outside (or at the limit) of the model "
05454 << "atmosphere but\nlooks in the wrong direction (wrong sign "
05455 << "for the zenith angle?).\nThis case includes nadir "
05456 << "looking exactly at the latitude end points.";
05457 throw runtime_error( os.str() );
05458 }
05459
05460
05461
05462 if( rte_pos[1] < lat_grid[0] || rte_pos[1] > lat_grid[nlat-1] )
05463 {
05464 Index ic = 0;
05465 String sc = "lower";
05466 if( rte_pos[1] > lat_grid[0] )
05467 { ic = nlat - 1; sc = "upper"; }
05468 const double rv = geompath_r_at_lat( ppath.constant, rte_pos[1],
05469 rte_los[0], lat_grid[ic] );
05470 if( rv < ( r_geoid(ic,0) + z_field(np-1,ic,0) ) )
05471 {
05472 ostringstream os;
05473 os << "The sensor is outside of the model atmosphere and "
05474 << "looks in the\n" << sc << " latitude end face.\n"
05475 << "The geometrical altitude of the corner point is "
05476 << z_field(np-1,ic,0)/1e3 << " km.\n"
05477 << "The geometrical altitude of the entrance point is "
05478 << (rv-r_geoid(ic,0))/1e3 << " km.";
05479 throw runtime_error( os.str() );
05480 }
05481 }
05482
05483
05484
05485
05486 if( ( geom_tan_pos[1] < lat_grid[0] ||
05487 geom_tan_pos[1] > lat_grid[nlat-1] ) )
05488 {
05489 Index ic = 0;
05490 String sc = "lower";
05491 if( rte_los[0] >= 0 )
05492 { ic = nlat - 1; sc = "upper"; }
05493 const double rv = geompath_r_at_lat( ppath.constant, rte_pos[1],
05494 rte_los[0], lat_grid[ic] );
05495 if( rv >= ( r_geoid(ic,0) + z_field(np-1,ic,0) ) )
05496 {
05497 ostringstream os;
05498 os << "The combination of sensor position and line-of-sight "
05499 << "gives a\npropagation path that goes above the model "
05500 << "atmosphere, with\na tangent point outside the covered"
05501 << " latitude range.\nThe latitude of the tangent point "
05502 << "is " << geom_tan_pos[1] << " degrees.";
05503 throw runtime_error( os.str() );
05504 }
05505 }
05506
05507
05508
05509
05510
05511
05512
05513
05514
05515
05516 if( geom_tan_pos[1] >= lat_grid[0] &&
05517 geom_tan_pos[1] <= lat_grid[nlat-1] &&
05518 geom_tan_z >= geom_tan_atmtop )
05519 {
05520 ppath_set_background( ppath, 1 );
05521 out1 << " ------- WARNING -------: path is totally outside of "
05522 << "the model atmosphere\n";
05523 }
05524
05525
05526 else
05527 {
05528
05529
05530
05531
05532
05533
05534
05535
05536
05537
05538
05539 double lat0;
05540 Index ilat0, istep;
05541
05542 if( rte_pos[1] <= lat_grid[0] )
05543 {
05544 lat0 = lat_grid[0];
05545 ilat0 = 0;
05546 istep = 1;
05547 }
05548 else if( rte_pos[1] >= lat_grid[nlat-1] )
05549 {
05550 lat0 = lat_grid[nlat-1];
05551 ilat0 = nlat-1;
05552 istep = -1;
05553 }
05554 else
05555 {
05556 lat0 = rte_pos[1];
05557 if( rte_los[0] >= 0 )
05558 {
05559 ilat0 = gridpos2gridrange( gp_lat, 1 );
05560 istep = 1;
05561 }
05562 else
05563 {
05564 ilat0 = gridpos2gridrange( gp_lat, 0 ) + 1;
05565 istep = -1;
05566 }
05567 }
05568
05569
05570
05571 Index ready = 0;
05572 while( !ready )
05573 {
05574
05575
05576
05577
05578 double r0 = geompath_r_at_lat( ppath.constant, rte_pos[1],
05579 rte_los[0], lat0 );
05580 double za0 = abs( geompath_za_at_r( ppath.constant,
05581 rte_los[0], r0 ) );
05582
05583
05584 double rv1 = r_geoid(ilat0,0) + z_field(np-1,ilat0,0);
05585 double rv2 = r_geoid(ilat0+istep,0) +
05586 z_field(np-1,ilat0+istep,0);
05587 double latstep = abs( lat_grid[ilat0+istep] -
05588 lat_grid[ilat0] );
05589 double c = ( rv2 - rv1 ) / latstep;
05590
05591 if( lat0 != lat_grid[ilat0] )
05592 { rv1 = rv1 + c * abs( lat0 - lat_grid[ilat0] ); }
05593
05594 double dlat = plevel_crossing_2d( r0, za0, rv1, c );
05595
05596 if( dlat <= latstep )
05597 {
05598 ready = 1;
05599 ppath.pos(0,1) = lat0 + (double)istep*dlat;
05600 ppath.pos(0,0) = rv1 + c * dlat;
05601 ppath.los(0,0) = geompath_za_at_r( ppath.constant,
05602 rte_los[0], ppath.pos(0,0) );
05603
05604 rv1 = r_geoid(ilat0,0);
05605 rv2 = r_geoid(ilat0+istep,0);
05606 c = ( rv2 - rv1 ) / latstep;
05607 ppath.z[0] = ppath.pos(0,0) - ( rv1 + c *
05608 abs( ppath.pos(0,1) - lat_grid[ilat0] ) );
05609 ppath.gp_p[0].idx = np - 2;
05610 ppath.gp_p[0].fd[0] = 1;
05611 ppath.gp_p[0].fd[1] = 0;
05612 gridpos( ppath.gp_lat[0], lat_grid, ppath.pos(0,1) );
05613 }
05614 else
05615 {
05616 ilat0 += istep;
05617 lat0 = lat_grid[ilat0];
05618 }
05619 }
05620 }
05621 }
05622
05623
05624 gridpos_check_fd( ppath.gp_p[0] );
05625 gridpos_check_fd( ppath.gp_lat[0] );
05626
05627
05628 if( geom_tan_pos.nelem() == 2 )
05629 {
05630 ppath.geom_tan_pos.resize(2);
05631 ppath.geom_tan_pos = geom_tan_pos;
05632 }
05633
05634 }
05635
05636
05637
05638 else
05639 {
05640
05641 const Index nlat = lat_grid.nelem();
05642 const Index nlon = lon_grid.nelem();
05643
05644
05645
05646
05647
05648
05649 bool is_inside = false;
05650 double rv_geoid=-1, rv_surface=-1;
05651 GridPos gp_lat, gp_lon;
05652 Vector itw(4);
05653
05654 if( rte_pos[1] >= lat_grid[0] && rte_pos[1] <= lat_grid[nlat-1] &&
05655 rte_pos[2] >= lon_grid[0] && rte_pos[2] <= lon_grid[nlon-1] )
05656 {
05657 gridpos( gp_lat, lat_grid, rte_pos[1] );
05658 gridpos( gp_lon, lon_grid, rte_pos[2] );
05659 interpweights( itw, gp_lat, gp_lon );
05660
05661 rv_geoid = interp( itw, r_geoid, gp_lat, gp_lon );
05662 rv_surface = rv_geoid + interp( itw, z_surface, gp_lat, gp_lon );
05663
05664 out2 << " sensor altitude : " << (rte_pos[0]-rv_geoid)/1e3
05665 << " km\n";
05666
05667 if( rte_pos[0] < ( rv_geoid + interp( itw,
05668 z_field(np-1,joker,joker), gp_lat, gp_lon ) ) )
05669 { is_inside = true; }
05670 }
05671
05672
05673
05674
05675
05676 Array<double> geom_tan_pos(0);
05677 double geom_tan_z=-2, geom_tan_atmtop=-1;
05678
05679 if( rte_los[0] > 90 )
05680 {
05681 geom_tan_pos.resize(3);
05682 double dummy;
05683 geompath_tanpos_3d( geom_tan_pos[0], geom_tan_pos[1],
05684 geom_tan_pos[2], dummy, rte_pos[0], rte_pos[1], rte_pos[2],
05685 rte_los[0], rte_los[1],
05686 geometrical_ppc( rte_pos[0], rte_los[0] ) );
05687 out2 << " geom. tangent radius : " << geom_tan_pos[0] / 1e3
05688 <<" km\n";
05689 out2 << " geom. tangent latitude : " << geom_tan_pos[1]
05690 << "\n";
05691 out2 << " geom. tangent longitude: " << geom_tan_pos[2]
05692 << "\n";
05693
05694
05695 if( geom_tan_pos[1] >= lat_grid[0] &&
05696 geom_tan_pos[1] <= lat_grid[nlat-1] &&
05697 geom_tan_pos[2] >= lon_grid[0] &&
05698 geom_tan_pos[2] <= lon_grid[nlon-1] )
05699 {
05700 GridPos gp_tanlat, gp_tanlon;
05701 Vector itw_tan(4);
05702 gridpos( gp_tanlat, lat_grid, geom_tan_pos[1] );
05703 gridpos( gp_tanlon, lon_grid, geom_tan_pos[2] );
05704 interpweights( itw_tan, gp_tanlat, gp_tanlon );
05705 geom_tan_z = geom_tan_pos[0] -
05706 interp( itw_tan, r_geoid, gp_tanlat, gp_tanlon );
05707 geom_tan_atmtop = interp( itw_tan,
05708 z_field(np-1,joker,joker), gp_tanlat, gp_tanlon );
05709 out2 << " geom. tangent altitude : " << geom_tan_z/1e3
05710 << " km\n";
05711 }
05712 }
05713
05714
05715 ppath.pos(0,0) = rte_pos[0];
05716 ppath.pos(0,1) = rte_pos[1];
05717 ppath.pos(0,2) = rte_pos[2];
05718 ppath.los(0,0) = rte_los[0];
05719 ppath.los(0,1) = rte_los[1];
05720
05721
05722
05723 if( is_inside )
05724 {
05725
05726 if( (rte_pos[0] + RTOL) < rv_surface )
05727 {
05728 ostringstream os;
05729 os << "The ppath starting point is placed "
05730 << (rv_surface - rte_pos[0])/1e3 << " km below surface level.\n"
05731 << "This point must be above the surface.";
05732 throw runtime_error(os.str());
05733 }
05734
05735
05736 if( rte_pos[1] > -90 && rte_pos[1] == lat_grid[0] &&
05737 abs( rte_los[1] > 90 ) )
05738 throw runtime_error( "The sensor is at the lower latitude end "
05739 "point and the absolute value of the azimuth angle > 90." );
05740 if( rte_pos[1] < 90 && rte_pos[1] == lat_grid[nlat-1] &&
05741 abs( rte_los[1] ) <= 90 )
05742 throw runtime_error( "The sensor is at the upper latitude end "
05743 "point and the absolute value of the azimuth angle <= 90." );
05744
05745
05746 if( rte_pos[2] == lon_grid[0] && rte_los[1] < 0 )
05747 throw runtime_error( "The sensor is at the lower longitude end "
05748 "point and the azimuth angle < 0." );
05749 if( rte_pos[2] == lon_grid[nlon-1] && rte_los[1] > 0 )
05750 throw runtime_error( "The sensor is at the upper longitude end "
05751 "point and the azimuth angle > 0." );
05752
05753
05754 ppath.z[0] = ppath.pos(0,0) - rv_geoid;
05755
05756
05757
05758
05759
05760 ppath.gp_lat[0].idx = gp_lat.idx;
05761 ppath.gp_lat[0].fd[0] = gp_lat.fd[0];
05762 ppath.gp_lat[0].fd[1] = gp_lat.fd[1];
05763 ppath.gp_lon[0].idx = gp_lon.idx;
05764 ppath.gp_lon[0].fd[0] = gp_lon.fd[0];
05765 ppath.gp_lon[0].fd[1] = gp_lon.fd[1];
05766
05767
05768
05769 Vector z_grid(np);
05770 z_at_latlon( z_grid, p_grid, lat_grid, lon_grid, z_field,
05771 gp_lat, gp_lon );
05772 gridpos( ppath.gp_p, z_grid, ppath.z );
05773
05774
05775 if( ppath.pos(0,0) == rv_surface )
05776 {
05777
05778 const double rslope = plevel_slope_3d( lat_grid, lon_grid,
05779 r_geoid, z_surface, gp_lat, gp_lon, ppath.los(0,1) );
05780
05781
05782 const double atilt = plevel_angletilt( rv_surface, rslope);
05783
05784
05785
05786
05787 if( is_los_downwards( ppath.los(0,0), atilt ) )
05788 { ppath_set_background( ppath, 2 ); }
05789 }
05790
05791
05792 if( cloudbox_on )
05793 {
05794
05795
05796
05797
05798
05799
05800
05801
05802 if( ppath.pos(0,1) >= lat_grid[cloudbox_limits[2]] &&
05803 ppath.pos(0,1) <= lat_grid[cloudbox_limits[3]] &&
05804 ppath.pos(0,2) >= lon_grid[cloudbox_limits[4]] &&
05805 ppath.pos(0,2) <= lon_grid[cloudbox_limits[5]] )
05806 {
05807
05808
05809
05810 const double rv_low = rv_geoid + interp( itw,
05811 z_field(cloudbox_limits[0],joker,joker), gp_lat, gp_lon );
05812 const double rv_upp = rv_geoid + interp( itw,
05813 z_field(cloudbox_limits[1],joker,joker), gp_lat, gp_lon );
05814
05815 if( ppath.pos(0,0) >= rv_low && ppath.pos(0,0) <= rv_upp )
05816 {
05817 if( outside_cloudbox )
05818 { ppath_set_background( ppath, 4 ); }
05819 }
05820 else if( ppath.pos(0,0) <= rv_low-RTOL && ppath.pos(0,0) >= rv_upp+RTOL )
05821 { assert( outside_cloudbox ); }
05822 }
05823 else
05824 { assert( outside_cloudbox ); }
05825 }
05826 }
05827
05828
05829 else
05830 {
05831
05832 if( abs(rte_los[0]) <= 90 )
05833 {
05834 ostringstream os;
05835 os << "When the sensor is placed outside the model atmosphere,\n"
05836 << "upward observations are not allowed.";
05837 throw runtime_error( os.str() );
05838 }
05839
05840
05841
05842 ppath.constant = geom_tan_pos[0];
05843
05844
05845
05846 if( ( rte_pos[1] <= lat_grid[0] && abs( rte_los[1] ) >= 90 ) ||
05847 ( rte_pos[1] >= lat_grid[nlat-1] && abs( rte_los[1] ) <= 90 ) )
05848 {
05849 ostringstream os;
05850 os << "The sensor is north or south (or at the limit) of the "
05851 << "model atmosphere but\nlooks in the wrong direction.\n";
05852 throw runtime_error( os.str() );
05853 }
05854
05855
05856
05857
05858 if( ( rte_pos[2] <= lon_grid[0] && rte_los[1] < 0 ) ||
05859 ( rte_pos[2] >= lon_grid[nlon-1] && rte_los[1] > 0 ) )
05860 {
05861 ostringstream os;
05862 os << "The sensor is east or west (or at the limit) of the "
05863 << "model atmosphere but\nlooks in the wrong direction.\n";
05864 throw runtime_error( os.str() );
05865 }
05866
05867
05868
05869
05870
05871
05872
05873
05874 if( geom_tan_pos[1] >= lat_grid[0] &&
05875 geom_tan_pos[1] <= lat_grid[nlat-1] &&
05876 geom_tan_pos[2] >= lon_grid[0] &&
05877 geom_tan_pos[2] <= lon_grid[nlon-1] &&
05878 geom_tan_z >= geom_tan_atmtop )
05879 {
05880 ppath_set_background( ppath, 1 );
05881 out1 << " ------- WARNING -------: path is totally outside of "
05882 << "the model atmosphere\n";
05883 }
05884
05885
05886 else
05887 {
05888
05889 Matrix r_atmtop(nlat,nlon);
05890
05891 for( Index ilat=0; ilat<nlat; ilat++ )
05892 {
05893 for( Index ilon=0; ilon<nlon; ilon++ )
05894 { r_atmtop(ilat,ilon) = r_geoid(ilat,ilon) +
05895 z_field(np-1,ilat,ilon); }
05896 }
05897
05898
05899 double rtopmin = min( r_atmtop );
05900 if( rte_pos[0] <= rtopmin )
05901 {
05902 ostringstream os;
05903 os << "The sensor radius is smaller than the minimum radius "
05904 << "of the top of\nthe atmosphere. This gives no possible"
05905 << " OK entrance point for the path.";
05906 throw runtime_error( os.str() );
05907 }
05908
05909
05910
05911 double rtopmax = max( r_atmtop );
05912 if( geom_tan_pos[0] >= rtopmax )
05913 {
05914 ostringstream os;
05915 os << "The combination of sensor position and line-of-sight "
05916 << "gives a\npropagation path that goes above the model "
05917 << "atmosphere, with\na tangent point outside the covered"
05918 << " latitude and longitude ranges.\nThe position of the "
05919 << "tangent point is:\n lat : " << geom_tan_pos[1]
05920 << "\n lon : " << geom_tan_pos[2];
05921 throw runtime_error( os.str() );
05922 }
05923
05924
05925 double x, y, z, dx, dy, dz;
05926 poslos2cart( x, y, z, dx, dy, dz, rte_pos[0],
05927 rte_pos[1], rte_pos[2], rte_los[0], rte_los[1] );
05928
05929
05930 bool failed = false;
05931
05932
05933 double r_top, lat_top, lon_top, l_top;
05934 plevel_crossing_3d( r_top, lat_top, lon_top, l_top,
05935 rtopmin, rte_pos[0], rte_pos[1], rte_pos[2],
05936 rte_los[0], x, y, z, dx, dy, dz );
05937
05938
05939
05940
05941 if( lat_top < lat_grid[0] || lat_top > lat_grid[nlat-1] ||
05942 lon_top < lat_grid[0] || lon_top > lat_grid[nlat-1] )
05943 {
05944 plevel_crossing_3d( r_top, lat_top, lon_top, l_top,
05945 rtopmax, rte_pos[0], rte_pos[1], rte_pos[2],
05946 rte_los[0], x, y, z, dx, dy, dz );
05947
05948 if( lat_top < lat_grid[0] || lat_top > lat_grid[nlat-1] ||
05949 lon_top < lon_grid[0] || lon_top > lon_grid[nlon-1] )
05950 { failed = true; }
05951 }
05952
05953
05954
05955
05956 bool ready = false;
05957
05958 while( !failed && !ready )
05959 {
05960
05961 GridPos gp_lattop, gp_lontop;
05962 double lat1, lat3, lon5, lon6, r15, r35, r36, r16;
05963 Index ilat, ilon;
05964 gridpos( gp_lattop, lat_grid, lat_top );
05965 ilat = gridpos2gridrange( gp_lattop, abs(rte_los[1]) >= 90 );
05966 gridpos( gp_lontop, lon_grid, lon_top );
05967 ilon = gridpos2gridrange( gp_lontop, rte_los[1] > 0 );
05968 r15 = r_geoid(ilat,ilon) + z_field(np-1,ilat,ilon);
05969 r35 = r_geoid(ilat+1,ilon) + z_field(np-1,ilat+1,ilon);
05970 r36 = r_geoid(ilat+1,ilon+1) + z_field(np-1,ilat+1,ilon+1);
05971 r16 = r_geoid(ilat,ilon+1) + z_field(np-1,ilat,ilon+1);
05972 lat1 = lat_grid[ilat];
05973 lat3 = lat_grid[ilat+1];
05974 lon5 = lon_grid[ilon];
05975 lon6 = lon_grid[ilon+1];
05976 r_top = rsurf_at_latlon( lat1, lat3, lon5, lon6,
05977 r15, r35, r36, r16, lat_top, lon_top );
05978
05979
05980
05981 double lat_top2, lon_top2;
05982 plevel_crossing_3d( r_top, lat_top2, lon_top2, l_top,
05983 r_top, rte_pos[0], rte_pos[1], rte_pos[2],
05984 rte_los[0], x, y, z, dx, dy, dz );
05985
05986
05987 if( abs( lat_top2 - lat_top ) < 1e-6 &&
05988 abs( lon_top2 - lon_top ) < 1e-6 )
05989 { ready = true; }
05990
05991
05992 else if( lat_top < lat_grid[0] ||
05993 lat_top > lat_grid[nlat-1] ||
05994 lon_top < lon_grid[0] ||
05995 lon_top > lon_grid[nlon-1] )
05996 { failed = true; }
05997
05998 lat_top = lat_top2;
05999 lon_top = lon_top2;
06000 }
06001
06002 if( failed )
06003 {
06004 ostringstream os;
06005 os << "The path does not enter the model atmosphere at the "
06006 << "top.\nThe path reaches the top of the atmosphere "
06007 << "altitude\napproximately at the position:\n"
06008 << " lat : " << lat_top << "\n lon : " << lon_top;
06009 throw runtime_error( os.str() );
06010 }
06011
06012
06013
06014 if( rte_los[0] == 180 )
06015 {
06016 lat_top = rte_pos[1];
06017 lon_top = rte_pos[2];
06018 }
06019 else if( abs( rte_pos[1] ) < 90 && ( abs(rte_los[1]) < ANGTOL
06020 || abs( rte_los[1] ) > 180-ANGTOL ) )
06021 { lon_top = rte_pos[2]; }
06022
06023
06024
06025
06026 ppath.pos(0,0) = r_top;
06027 ppath.pos(0,1) = lat_top;
06028 ppath.pos(0,2) = lon_top;
06029
06030
06031 ppath.gp_p[0].idx = np - 2;
06032 ppath.gp_p[0].fd[0] = 1;
06033 ppath.gp_p[0].fd[1] = 0;
06034 gridpos( ppath.gp_lat[0], lat_grid, lat_top );
06035 gridpos( ppath.gp_lon[0], lon_grid, lon_top );
06036
06037
06038 Vector itw2(4);
06039 interpweights( itw2, ppath.gp_lat[0], ppath.gp_lon[0] );
06040 ppath.z[0] = ppath.pos(0,0) - interp(itw2, r_geoid,
06041 ppath.gp_lat[0], ppath.gp_lon[0] );
06042
06043
06044 double zatmp, aatmp;
06045 cart2poslos( r_top, lat_top, lon_top, zatmp, aatmp,
06046 x+dx*l_top, y+dy*l_top, z+dz*l_top, dx, dy, dz );
06047 ppath.los(0,0) = zatmp;
06048 ppath.los(0,1) = aatmp;
06049
06050
06051 if( rte_los[0] == 180 )
06052 { ppath.los(0,0) = 180; }
06053 if( abs( rte_pos[1] ) < 90 && ( abs( rte_los[1] ) < ANGTOL
06054 || abs( rte_los[1] ) > 180-ANGTOL ) )
06055 { ppath.los(0,1) = rte_los[1]; }
06056 }
06057
06058 }
06059
06060
06061 gridpos_check_fd( ppath.gp_p[0] );
06062 gridpos_check_fd( ppath.gp_lat[0] );
06063 gridpos_check_fd( ppath.gp_lon[0] );
06064
06065
06066 if( geom_tan_pos.nelem() == 3 )
06067 {
06068 ppath.geom_tan_pos.resize(3);
06069 ppath.geom_tan_pos[0] = geom_tan_pos[0];
06070 ppath.geom_tan_pos[1] = geom_tan_pos[1];
06071 ppath.geom_tan_pos[2] = geom_tan_pos[2];
06072 }
06073 }
06074 }
06075
06076
06077
06078
06079
06081
06112 void ppath_calc(
06113 Workspace& ws,
06114
06115 Ppath& ppath,
06116
06117 const Agenda& ppath_step_agenda,
06118 const Index& atmosphere_dim,
06119 const Vector& p_grid,
06120 const Vector& lat_grid,
06121 const Vector& lon_grid,
06122 const Tensor3& z_field,
06123 const Matrix& r_geoid,
06124 const Matrix& z_surface,
06125 const Index& cloudbox_on,
06126 const ArrayOfIndex& cloudbox_limits,
06127 const Vector& rte_pos,
06128 const Vector& rte_los,
06129 const bool& outside_cloudbox )
06130 {
06131
06132
06133
06134
06135
06136
06137
06138
06139
06140 chk_vector_length( "rte_pos", rte_pos, atmosphere_dim );
06141 chk_if_over_0( "sensor radius", rte_pos[0] );
06142 if( atmosphere_dim == 1 )
06143 {
06144 chk_vector_length( "rte_los", rte_los, 1 );
06145 chk_if_in_range( "sensor zenith angle", rte_los[0], 0., 180. );
06146 }
06147 else if( atmosphere_dim == 2 )
06148 {
06149 chk_vector_length( "rte_los", rte_los, 1 );
06150 chk_if_in_range( "sensor zenith angle", rte_los[0], -180., 180. );
06151 if( cloudbox_on )
06152 { throw runtime_error( "The cloud box is not defined for 2D." ); }
06153 }
06154 else
06155 {
06156 chk_if_in_range( "sensor latitude", rte_pos[1], -90., 90. );
06157 chk_if_in_range( "sensor longitude", rte_pos[2], -360., 360. );
06158 chk_vector_length( "rte_los", rte_los, 2 );
06159 chk_if_in_range( "sensor zenith angle", rte_los[0], 0., 180. );
06160 chk_if_in_range( "sensor azimuth angle", rte_los[1], -180., 180. );
06161 }
06162 assert( outside_cloudbox || cloudbox_on );
06163
06164
06165
06166
06167
06168 out2 << " -------------------------------------\n";
06169 out2 << " sensor radius : " << rte_pos[0]/1e3 << " km\n";
06170 if( atmosphere_dim >= 2 )
06171 out2 << " sensor latitude : " << rte_pos[1] << "\n";
06172 if( atmosphere_dim == 3 )
06173 out2 << " sensor longitude : " << rte_pos[2] << "\n";
06174 out2 << " sensor zenith angle : " << rte_los[0] << "\n";
06175 if( atmosphere_dim == 3 )
06176 out2 << " sensor azimuth angle : " << rte_los[1] << "\n";
06177
06178
06179
06180
06181
06182
06183
06184
06185
06186
06187
06188 Ppath ppath_step;
06189
06190 ppath_start_stepping( ppath_step, atmosphere_dim, p_grid, lat_grid,
06191 lon_grid, z_field, r_geoid, z_surface,
06192 cloudbox_on, cloudbox_limits, outside_cloudbox, rte_pos, rte_los );
06193
06194 out2 << " -------------------------------------\n";
06195
06196
06197
06198
06199
06200
06201
06202 Array<Ppath> ppath_array;
06203 ppath_array.push_back( ppath_step );
06204
06205 Index np = ppath_step.np;
06206 Index istep = 0;
06207
06208 const Index imax_p = p_grid.nelem() - 1;
06209 const Index imax_lat = lat_grid.nelem() - 1;
06210 const Index imax_lon = lon_grid.nelem() - 1;
06211
06212 while( !ppath_what_background( ppath_step ) )
06213 {
06214
06215
06216
06217 istep++;
06218
06219 ppath_step_agendaExecute( ws, ppath_step, atmosphere_dim, p_grid,
06220 lat_grid, lon_grid, z_field, r_geoid, z_surface,
06221 ppath_step_agenda );
06222
06223
06224 const Index n = ppath_step.np;
06225
06226
06227 np += n - 1;
06228
06229
06230
06231
06232
06233
06234 if( outside_cloudbox )
06235 {
06236
06237
06238 if( is_gridpos_at_index_i( ppath_step.gp_p[n-1], imax_p ) )
06239 { ppath_set_background( ppath_step, 1 ); }
06240
06241
06242 if( atmosphere_dim == 2 )
06243 {
06244
06245 if( is_gridpos_at_index_i( ppath_step.gp_lat[n-1], 0 ) )
06246 {
06247 ostringstream os;
06248 os << "The path exits the atmosphere through the lower "
06249 << "latitude end face.\nThe exit point is at an altitude"
06250 << "of " << ppath_step.z[n-1]/1e3 << " km.";
06251 throw runtime_error( os.str() );
06252 }
06253 if( is_gridpos_at_index_i( ppath_step.gp_lat[n-1], imax_lat ) )
06254 {
06255 ostringstream os;
06256 os << "The path exits the atmosphere through the upper "
06257 << "latitude end face.\nThe exit point is at an altitude"
06258 << " of " << ppath_step.z[n-1]/1e3 << " km.";
06259 throw runtime_error( os.str() );
06260 }
06261 }
06262 if( atmosphere_dim == 3 )
06263 {
06264
06265 if( lat_grid[0] > -90 &&
06266 is_gridpos_at_index_i( ppath_step.gp_lat[n-1], 0 ) )
06267 {
06268 ostringstream os;
06269 os << "The path exits the atmosphere through the lower "
06270 << "latitude end face.\nThe exit point is at an altitude"
06271 << " of " << ppath_step.z[n-1]/1e3 << " km.";
06272 throw runtime_error( os.str() );
06273 }
06274 if( lat_grid[imax_lat] < 90 &&
06275 is_gridpos_at_index_i( ppath_step.gp_lat[n-1], imax_lat ) )
06276 {
06277 ostringstream os;
06278 os << "The path exits the atmosphere through the upper"
06279 << "latitude end face.\nThe exit point is at an altitude"
06280 << " of " << ppath_step.z[n-1]/1e3 << " km.";
06281 throw runtime_error( os.str() );
06282 }
06283
06284
06285
06286
06287 if( is_gridpos_at_index_i( ppath_step.gp_lon[n-1], 0 ) &&
06288 ppath_step.los(n-1,1) < 0 &&
06289 abs( ppath_step.pos(n-1,1) ) < 90 )
06290 {
06291
06292 if( lon_grid[imax_lon] - lon_grid[0] >= 360 )
06293 {
06294 ppath_step.pos(n-1,2) = ppath_step.pos(n-1,2) + 360;
06295 gridpos( ppath_step.gp_lon[n-1], lon_grid,
06296 ppath_step.pos(n-1,2) );
06297 }
06298 else
06299 {
06300 ostringstream os;
06301 os << "The path exits the atmosphere through the lower "
06302 << "longitude end face.\nThe exit point is at an "
06303 << "altitude of " << ppath_step.z[n-1]/1e3 << " km.";
06304 throw runtime_error( os.str() );
06305 }
06306 }
06307 else if(
06308 is_gridpos_at_index_i( ppath_step.gp_lon[n-1], imax_lon ) &&
06309 ppath_step.los(n-1,1) > 0 &&
06310 abs( ppath_step.pos(n-1,1) ) < 90 )
06311 {
06312
06313 if( lon_grid[imax_lon] - lon_grid[0] >= 360 )
06314 {
06315 ppath_step.pos(n-1,2) = ppath_step.pos(n-1,2) - 360;
06316 gridpos( ppath_step.gp_lon[n-1], lon_grid,
06317 ppath_step.pos(n-1,2) );
06318 }
06319 else
06320 {
06321 ostringstream os;
06322 os << "The path exits the atmosphere through the upper "
06323 << "longitude end face.\nThe exit point is at an "
06324 << "altitude of " << ppath_step.z[n-1]/1e3 << " km.";
06325 throw runtime_error( os.str() );
06326 }
06327 }
06328 }
06329
06330
06331
06332 if( cloudbox_on )
06333 {
06334 double ipos = fractional_gp( ppath_step.gp_p[n-1] );
06335
06336
06337 if( ipos >= double( cloudbox_limits[0] ) &&
06338 ipos <= double( cloudbox_limits[1] ) )
06339 {
06340 if( atmosphere_dim == 1 )
06341 { ppath_set_background( ppath_step, 3 ); }
06342 else
06343 {
06344 ipos = fractional_gp( ppath_step.gp_lat[n-1] );
06345
06346
06347 if( ipos >= double( cloudbox_limits[2] ) &&
06348 ipos <= double( cloudbox_limits[3] ) )
06349 {
06350 ipos = fractional_gp( ppath_step.gp_lon[n-1] );
06351
06352
06353 if( ipos >= double( cloudbox_limits[4] ) &&
06354 ipos <= double( cloudbox_limits[5] ) )
06355 { ppath_set_background( ppath_step, 3 ); }
06356 }
06357 }
06358 }
06359 }
06360 }
06361
06362
06363 else
06364 {
06365
06366
06367
06368
06369
06370 double ipos = fractional_gp( ppath_step.gp_p[n-1] );
06371 if( ipos <= double( cloudbox_limits[0] ) ||
06372 ipos >= double( cloudbox_limits[1] ) )
06373 { ppath_set_background( ppath_step, 3 ); }
06374
06375 else if( atmosphere_dim > 1 )
06376 {
06377
06378 ipos = fractional_gp( ppath_step.gp_lat[n-1] );
06379 if( ipos <= double( cloudbox_limits[2] ) ||
06380 ipos >= double( cloudbox_limits[3] ) )
06381 { ppath_set_background( ppath_step, 3 ); }
06382
06383 else
06384 {
06385
06386 ipos = fractional_gp( ppath_step.gp_lon[n-1] );
06387 if( ipos <= double( cloudbox_limits[4] ) ||
06388 ipos >= double( cloudbox_limits[5] ) )
06389 { ppath_set_background( ppath_step, 3 ); }
06390 }
06391 }
06392 }
06393
06394
06395
06396
06397
06398
06399 ppath_array.push_back( ppath_step );
06400
06401 }
06402
06403
06404
06405 ppath_init_structure( ppath, atmosphere_dim, np );
06406
06407 np = 0;
06408
06409 for( Index i=0; i<ppath_array.nelem(); i++ )
06410 {
06411
06412
06413
06414
06415
06416
06417 Index n = ppath_array[i].np;
06418
06419 if( n )
06420 {
06421
06422 Index i1 = 1;
06423 if( i == 0 )
06424 { i1 = 0; }
06425 else
06426 { assert( n > 1 ); }
06427
06428
06429 ppath.z[ Range(np,n-i1) ] = ppath_array[i].z[ Range(i1,n-i1) ];
06430 ppath.pos( Range(np,n-i1), joker ) =
06431 ppath_array[i].pos( Range(i1,n-i1), joker );
06432 ppath.los( Range(np,n-i1), joker ) =
06433 ppath_array[i].los( Range(i1,n-i1), joker );
06434
06435
06436
06437 if( i > 0 )
06438 { ppath.l_step[ Range(np-1,n-1) ] = ppath_array[i].l_step; }
06439
06440
06441 for( Index j=i1; j<n; j++ )
06442 { ppath.gp_p[np+j-i1] = ppath_array[i].gp_p[j]; }
06443 if( atmosphere_dim >= 2 )
06444 {
06445 for( Index j=i1; j<n; j++ )
06446 { ppath.gp_lat[np+j-i1] = ppath_array[i].gp_lat[j]; }
06447 }
06448 if( atmosphere_dim == 3 )
06449 {
06450 for( Index j=i1; j<n; j++ )
06451 { ppath.gp_lon[np+j-i1] = ppath_array[i].gp_lon[j]; }
06452 }
06453
06454
06455 if( ppath_array[i].tan_pos.nelem() )
06456 {
06457 ppath.tan_pos.resize( ppath_array[i].tan_pos.nelem() );
06458 ppath.tan_pos = ppath_array[i].tan_pos;
06459 }
06460 if( ppath_array[i].geom_tan_pos.nelem() )
06461 {
06462 ppath.geom_tan_pos.resize( ppath_array[i].tan_pos.nelem() );
06463 ppath.geom_tan_pos = ppath_array[i].geom_tan_pos;
06464 }
06465
06466
06467 np += n - i1;
06468
06469 }
06470 }
06471 ppath.method = ppath_step.method;
06472 ppath.refraction = ppath_step.refraction;
06473 ppath.constant = ppath_step.constant;
06474 ppath.background = ppath_step.background;
06475
06476 out3 << " number of path steps : " << istep << "\n";
06477 out3 << " number of path points : " << ppath.z.nelem() << "\n";
06478
06479
06480
06481
06482 if( ppath.refraction && min( z_field(z_field.npages()-1,0,0) ) < 60e3 )
06483 {
06484 out2 << " *** WARNING****\n"
06485 << " The calculated propagation path can be inexact as the "
06486 << "atmosphere\n only extends to "
06487 << min( z_field(z_field.npages()-1,0,0) ) << " km. \n"
06488 << " The importance of this depends on the observation "
06489 << "geometry.\n It is recommended that the top of the atmosphere "
06490 << "is not below 60 km.\n";
06491 }
06492 }