00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00040
00041
00042
00043
00044 #include <cmath>
00045 #include "arts.h"
00046 #include "auto_md.h"
00047 #include "check_input.h"
00048 #include "math_funcs.h"
00049 #include "messages.h"
00050 #include "ppath.h"
00051 #include "special_interp.h"
00052 #include "xml_io.h"
00053 #include "refraction.h"
00054 #include "m_general.h"
00055
00056 extern const Numeric RAD2DEG;
00057 extern const Numeric DEG2RAD;
00058 extern const Numeric EARTH_GRAV_CONST;
00059
00060
00061
00062
00063
00064
00065 void rte_losSet(
00066
00067 Vector& rte_los,
00068
00069 const Index& atmosphere_dim,
00070
00071 const Numeric& za,
00072 const Numeric& aa )
00073 {
00074
00075 chk_if_in_range( "atmosphere_dim", atmosphere_dim, 1, 3 );
00076
00077 if( atmosphere_dim == 1 )
00078 { rte_los.resize(1); }
00079 else
00080 {
00081 rte_los.resize(2);
00082 rte_los[1] = aa;
00083 }
00084 rte_los[0] = za;
00085 }
00086
00087
00088
00089 void rte_posAddGeoidWGS84(
00090
00091 Vector& rte_pos,
00092
00093 const Index& atmosphere_dim,
00094 const Numeric& latitude_1d,
00095 const Numeric& meridian_angle_1d )
00096 {
00097
00098 chk_if_in_range( "atmosphere_dim", atmosphere_dim, 1, 3 );
00099 chk_vector_length( "rte_pos", rte_pos, atmosphere_dim );
00100
00101
00102 Matrix m(1,rte_pos.nelem());
00103 m(0,joker) = rte_pos;
00104 sensor_posAddGeoidWGS84( m, atmosphere_dim, latitude_1d, meridian_angle_1d);
00105 rte_pos[0] = m(0,0);
00106 }
00107
00108
00109
00110 void rte_posAddRgeoid(
00111
00112 Vector& rte_pos,
00113
00114 const Index& atmosphere_dim,
00115 const Vector& lat_grid,
00116 const Vector& lon_grid,
00117 const Matrix& r_geoid )
00118 {
00119
00120 chk_if_in_range( "atmosphere_dim", atmosphere_dim, 1, 3 );
00121 chk_vector_length( "rte_pos", rte_pos, atmosphere_dim );
00122
00123
00124 Matrix m(1,rte_pos.nelem());
00125 m(0,joker) = rte_pos;
00126 sensor_posAddRgeoid( m, atmosphere_dim, lat_grid, lon_grid, r_geoid );
00127 rte_pos[0] = m(0,0);
00128 }
00129
00130
00131 void rte_posSet(
00132
00133 Vector& rte_pos,
00134
00135 const Index& atmosphere_dim,
00136
00137 const Numeric& r_or_z,
00138 const Numeric& lat,
00139 const Numeric& lon )
00140 {
00141
00142 chk_if_in_range( "atmosphere_dim", atmosphere_dim, 1, 3 );
00143
00144 rte_pos.resize(atmosphere_dim);
00145 rte_pos[0] = r_or_z;
00146 if( atmosphere_dim >= 2 )
00147 { rte_pos[1] = lat; }
00148 if( atmosphere_dim == 3 )
00149 { rte_pos[2] = lon; }
00150 }
00151
00152
00153
00154 void rte_pos_and_losFromTangentPressure(
00155 Workspace& ws,
00156
00157 Vector& rte_pos,
00158 Vector& rte_los,
00159 Ppath& ppath,
00160
00161 const Index& atmosphere_dim,
00162 const Vector& p_grid,
00163 const Tensor3& z_field,
00164 const Vector & lat_grid,
00165 const Vector & lon_grid,
00166 const Agenda & ppath_step_agenda,
00167 const Matrix & r_geoid,
00168 const Matrix & z_surface,
00169
00170 const Numeric& tan_p
00171 )
00172 {
00173
00174 if (atmosphere_dim > 1)
00175 throw runtime_error(
00176 "Sorry, this function currently only works for atmosphere_dim==1");
00177
00178
00179 Index np=p_grid.nelem();
00180 Vector log_p_grid(np);
00181 Numeric log_p=log10(tan_p);
00182 GridPos gp;
00183 Vector itw(2);
00184 Numeric z;
00185 rte_pos.resize(atmosphere_dim);
00186 rte_los.resize(atmosphere_dim);
00187
00188
00189 for (Index i=0;i<np;i++){log_p_grid[i]=log10(p_grid[i]);}
00190
00191 gridpos(gp,log_p_grid,log_p);
00192 out1 << "gp.index = " << gp.idx << "\n";
00193 interpweights(itw,gp);
00194 z = interp(itw,z_field(Range(joker),0,0),gp);
00195 out1 << " Tangent pressure corresponds to an altitude of " << z << "m.\n";
00196
00197
00198 rte_pos[0]=z+r_geoid(0,0);
00199 rte_los[0]=90;
00200
00201 Index cloudbox_on=0;
00202 ArrayOfIndex cloudbox_limits(2);
00203 bool outside_cloudbox=true;
00204 ppath_calc(ws, ppath,ppath_step_agenda,atmosphere_dim,p_grid,lat_grid,
00205 lon_grid,z_field, r_geoid, z_surface,cloudbox_on, cloudbox_limits,
00206 rte_pos, rte_los, outside_cloudbox);
00207 rte_pos = ppath.pos(ppath.np-1,Range(0,atmosphere_dim));
00208 rte_los = ppath.los(ppath.np-1,joker);
00209 rte_los[0]=180.0-rte_los[0];
00210 }
00211
00212
00213
00214 void ppathCalc(
00215 Workspace& ws,
00216
00217 Ppath& ppath,
00218
00219 const Agenda& ppath_step_agenda,
00220 const Index& atmosphere_dim,
00221 const Vector& p_grid,
00222 const Vector& lat_grid,
00223 const Vector& lon_grid,
00224 const Tensor3& z_field,
00225 const Matrix& r_geoid,
00226 const Matrix& z_surface,
00227 const Index& cloudbox_on,
00228 const ArrayOfIndex& cloudbox_limits,
00229 const Vector& rte_pos,
00230 const Vector& rte_los )
00231 {
00232 ppath_calc( ws, ppath, ppath_step_agenda, atmosphere_dim,
00233 p_grid, lat_grid, lon_grid, z_field, r_geoid, z_surface,
00234 cloudbox_on, cloudbox_limits, rte_pos, rte_los, 1 );
00235 }
00236
00237
00238
00239 void ppath_stepGeometric(
00240
00241 Ppath& ppath_step,
00242
00243 const Index& atmosphere_dim,
00244 const Vector& p_grid,
00245 const Vector& lat_grid,
00246 const Vector& lon_grid,
00247 const Tensor3& z_field,
00248 const Matrix& r_geoid,
00249 const Matrix& z_surface,
00250 const Numeric& ppath_lmax )
00251 {
00252
00253
00254
00255
00256 if( atmosphere_dim == 1 )
00257 { ppath_step_geom_1d( ppath_step, p_grid, z_field(joker,0,0),
00258 r_geoid(0,0), z_surface(0,0), ppath_lmax ); }
00259
00260 else if( atmosphere_dim == 2 )
00261 { ppath_step_geom_2d( ppath_step, p_grid, lat_grid,
00262 z_field(joker,joker,0), r_geoid(joker,0),
00263 z_surface(joker,0), ppath_lmax ); }
00264
00265
00266 else if( atmosphere_dim == 3 )
00267 { ppath_step_geom_3d( ppath_step, p_grid, lat_grid, lon_grid,
00268 z_field, r_geoid, z_surface, ppath_lmax ); }
00269
00270 else
00271 { throw runtime_error( "The atmospheric dimensionality must be 1-3." ); }
00272 }
00273
00274
00275
00276 void ppath_stepRefractionEuler(
00277 Workspace& ws,
00278
00279 Ppath& ppath_step,
00280 Numeric& rte_pressure,
00281 Numeric& rte_temperature,
00282 Vector& rte_vmr_list,
00283 Numeric& refr_index,
00284
00285 const Agenda& refr_index_agenda,
00286 const Index& atmosphere_dim,
00287 const Vector& p_grid,
00288 const Vector& lat_grid,
00289 const Vector& lon_grid,
00290 const Tensor3& z_field,
00291 const Tensor3& t_field,
00292 const Tensor4& vmr_field,
00293 const Matrix& r_geoid,
00294 const Matrix& z_surface,
00295 const Numeric& ppath_lmax,
00296 const Numeric& ppath_lraytrace )
00297 {
00298
00299
00300
00301
00302 assert( ppath_lraytrace > 0 );
00303
00304 if( atmosphere_dim == 1 )
00305 { ppath_step_refr_1d( ws, ppath_step, rte_pressure, rte_temperature,
00306 rte_vmr_list, refr_index, refr_index_agenda,
00307 p_grid, z_field(joker,0,0), t_field(joker,0,0),
00308 vmr_field(joker,joker,0,0), r_geoid(0,0), z_surface(0,0),
00309 "linear_euler", ppath_lraytrace, ppath_lmax ); }
00310
00311 else if( atmosphere_dim == 2 )
00312 { ppath_step_refr_2d( ws, ppath_step, rte_pressure, rte_temperature,
00313 rte_vmr_list, refr_index, refr_index_agenda,
00314 p_grid, lat_grid, z_field(joker,joker,0),
00315 t_field(joker,joker,0), vmr_field(joker, joker,joker,0),
00316 r_geoid(joker,0), z_surface(joker,0),
00317 "linear_euler", ppath_lraytrace, ppath_lmax ); }
00318
00319 else if( atmosphere_dim == 3 )
00320 { ppath_step_refr_3d( ws, ppath_step, rte_pressure, rte_temperature,
00321 rte_vmr_list, refr_index, refr_index_agenda,
00322 p_grid, lat_grid, lon_grid, z_field,
00323 t_field, vmr_field, r_geoid, z_surface,
00324 "linear_euler", ppath_lraytrace, ppath_lmax ); }
00325
00326 else
00327 { throw runtime_error( "The atmospheric dimensionality must be 1-3." ); }
00328 }
00329
00330
00331
00332 void sensor_posAddGeoidWGS84(
00333
00334 Matrix& sensor_pos,
00335
00336 const Index& atmosphere_dim,
00337 const Numeric& latitude_1d,
00338 const Numeric& meridian_angle_1d )
00339 {
00340
00341 chk_if_in_range( "atmosphere_dim", atmosphere_dim, 1, 3 );
00342 chk_matrix_ncols( "sensor_pos", sensor_pos, atmosphere_dim );
00343
00344
00345 const Index npos = sensor_pos.nrows();
00346 if( npos == 0 )
00347 throw runtime_error("The number of positions is 0, must be at least 1.");
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357 if( atmosphere_dim == 1 )
00358 {
00359 Vector lats(0);
00360
00361
00362 Matrix r;
00363 r_geoidWGS84( r, 1, lats, Vector(0), latitude_1d, meridian_angle_1d );
00364
00365
00366 sensor_pos(joker,0) += r(0,0);
00367 }
00368
00369 else
00370 {
00371 for( Index i=0; i<npos; i++ )
00372 {
00373 Vector lats(2);
00374 Index pos_used;
00375
00376 if( sensor_pos(i,1) < 90 )
00377 {
00378 lats[0] = sensor_pos(i,1);
00379 lats[1] = 90;
00380 pos_used = 0;
00381 }
00382 else
00383 {
00384 lats[0] = -90;
00385 lats[1] = sensor_pos(i,1);
00386 pos_used = 1;
00387 }
00388
00389
00390 Matrix r;
00391 r_geoidWGS84( r, 2, lats, Vector(0), -999, -999 );
00392
00393 sensor_pos(i,0) += r(pos_used,0);
00394 }
00395 }
00396 }
00397
00398
00399
00400 void sensor_posAddRgeoid(
00401
00402 Matrix& sensor_pos,
00403
00404 const Index& atmosphere_dim,
00405 const Vector& lat_grid,
00406 const Vector& lon_grid,
00407 const Matrix& r_geoid )
00408 {
00409
00410 chk_if_in_range( "atmosphere_dim", atmosphere_dim, 1, 3 );
00411 chk_matrix_ncols( "sensor_pos", sensor_pos, atmosphere_dim );
00412 chk_atm_surface( "r_geoid", r_geoid, atmosphere_dim, lat_grid, lon_grid );
00413
00414
00415 const Index npos = sensor_pos.nrows();
00416
00417 if( npos == 0 )
00418 throw runtime_error("The number of positions is 0, must be at least 1.");
00419
00420 if( atmosphere_dim == 1 )
00421 { sensor_pos(joker,0) += r_geoid(0,0); }
00422
00423 else
00424 {
00425
00426 if( min(sensor_pos(joker,1)) < lat_grid[0] ||
00427 max(sensor_pos(joker,1)) > last(lat_grid) )
00428 throw runtime_error(
00429 "You have given a position with a latitude outside *lat_grid*." );
00430 if( atmosphere_dim == 3 )
00431 {
00432 if( min(sensor_pos(joker,2)) < lon_grid[0] ||
00433 max(sensor_pos(joker,2)) > last(lon_grid) )
00434 throw runtime_error(
00435 "You have given a position with a longitude outside *lon_grid*." );
00436 }
00437
00438 if( atmosphere_dim == 2 )
00439 {
00440 ArrayOfGridPos gp(npos);
00441 Matrix itw(npos,2);
00442 gridpos( gp, lat_grid, sensor_pos(joker,1) );
00443 interpweights( itw, gp );
00444 Vector v_rgeoid(npos);
00445 interp( v_rgeoid, itw, r_geoid(joker,0), gp );
00446 for( Index i=0; i<npos; i++ )
00447 { sensor_pos(i,0) += v_rgeoid[i]; }
00448 }
00449 else
00450 {
00451 ArrayOfGridPos gp_lat(npos), gp_lon(npos);
00452 Matrix itw(npos,4);
00453 gridpos( gp_lat, lat_grid, sensor_pos(joker,1) );
00454 gridpos( gp_lon, lon_grid, sensor_pos(joker,2) );
00455 interpweights( itw, gp_lat, gp_lon );
00456 Vector v_rgeoid(npos);
00457 interp( v_rgeoid, itw, r_geoid, gp_lat, gp_lon );
00458 for( Index i=0; i<npos; i++ )
00459 { sensor_pos(i,0) += v_rgeoid[i]; }
00460 }
00461 }
00462 }
00463
00464
00465
00466 void VectorZtanToZa1D(
00467
00468 Vector& za_vector,
00469
00470 const Matrix& sensor_pos,
00471 const Matrix& r_geoid,
00472 const Index& atmosphere_dim,
00473
00474 const Vector& ztan_vector)
00475 {
00476 if( atmosphere_dim != 1 ) {
00477 throw runtime_error( "The function can only be used for 1D atmospheres." );
00478 }
00479
00480 const Index npos = sensor_pos.nrows();
00481
00482 if( ztan_vector.nelem() != npos )
00483 {
00484 ostringstream os;
00485 os << "The number of altitudes in the geometric tangent altitude vector must\n"
00486 << "match the number of positions in *sensor_pos*.";
00487 throw runtime_error( os.str() );
00488 }
00489
00490 za_vector.resize(npos);
00491
00492 for( Index i=0; i<npos; i++ )
00493 { za_vector[i] = geompath_za_at_r( r_geoid(0,0) + ztan_vector[i], 100,
00494 sensor_pos(i,0) ); }
00495 }
00496
00497
00498
00499 void VectorZtanToZaRefr1D(
00500 Workspace& ws,
00501
00502 Numeric& refr_index,
00503 Numeric& rte_pressure,
00504 Numeric& rte_temperature,
00505 Vector& rte_vmr_list,
00506
00507 Vector& za_vector,
00508
00509 const Agenda& refr_index_agenda,
00510 const Matrix& sensor_pos,
00511 const Vector& p_grid,
00512 const Tensor3& t_field,
00513 const Tensor3& z_field,
00514 const Tensor4& vmr_field,
00515 const Matrix& r_geoid,
00516 const Index& atmosphere_dim,
00517
00518 const Vector& ztan_vector)
00519 {
00520 if( atmosphere_dim != 1 ) {
00521 throw runtime_error( "The function can only be used for 1D atmospheres." );
00522 }
00523
00524 if( ztan_vector.nelem() != sensor_pos.nrows() ) {
00525 ostringstream os;
00526 os << "The number of altitudes in true tangent altitude vector must\n"
00527 << "match the number of positions in *sensor_pos*.";
00528 throw runtime_error( os.str() );
00529 }
00530
00531
00532 za_vector.resize( ztan_vector.nelem() );
00533
00534
00535 for( Index i=0; i<ztan_vector.nelem(); i++ ) {
00536 get_refr_index_1d( ws,
00537 refr_index, rte_pressure, rte_temperature, rte_vmr_list,
00538 refr_index_agenda, p_grid, r_geoid(0,0),
00539 z_field(joker,0,0), t_field(joker,0,0), vmr_field(joker,joker,0,0),
00540 ztan_vector[i]+r_geoid(0,0) );
00541
00542
00543 za_vector[i] = 180-asin(refr_index*(ztan_vector[i]+r_geoid(0,0)) /
00544 sensor_pos(i,0))*RAD2DEG;
00545 }
00546
00547 }
00548
00549
00550
00551 void ZaSatOccultation(
00552 Workspace& ws,
00553
00554 Vector& za_out,
00555
00556 const Agenda& ppath_step_agenda,
00557 const Index& atmosphere_dim,
00558 const Vector& p_grid,
00559 const Vector& lat_grid,
00560 const Vector& lon_grid,
00561 const Tensor3& z_field,
00562 const Matrix& r_geoid,
00563 const Matrix& z_surface,
00564
00565 const Numeric& z_recieve,
00566 const Numeric& z_send,
00567 const Numeric& t_sample,
00568 const Numeric& z_scan_low,
00569 const Numeric& z_scan_high )
00570 {
00571
00572 assert( atmosphere_dim ==1 );
00573 if ( z_scan_low >= z_scan_high ) {
00574 ostringstream os;
00575 os << "The lowest scan altitude *z_scan_low*number is higher or equal\n"
00576 << "to the highest scan altitude *z_scan_high*.";
00577 throw runtime_error( os.str() );
00578 }
00579 chk_atm_grids( atmosphere_dim, p_grid, lat_grid, lon_grid );
00580 chk_atm_field( "z_field", z_field, atmosphere_dim, p_grid, lat_grid,
00581 lon_grid );
00582 chk_atm_surface( "r_geoid", r_geoid, atmosphere_dim, lat_grid, lon_grid );
00583 chk_atm_surface( "z_surface", z_surface, atmosphere_dim, lat_grid, lon_grid );
00584
00585
00586 const Numeric r_recieve = r_geoid(0,0) + z_recieve;
00587 const Numeric r_send = r_geoid(0,0) + z_send;
00588
00589
00590
00591 const Numeric d = 3e3;
00592 const Numeric r_scan_max = z_scan_high + d + r_geoid(0,0);
00593 const Numeric r_scan_min = z_scan_low - d + r_geoid(0,0);
00594
00595
00596 const Numeric za_ref_max = geompath_za_at_r( r_scan_min, 100, r_recieve );
00597 const Numeric za_ref_min = geompath_za_at_r( r_scan_max, 100, r_recieve );
00598
00599
00600 const Numeric za_step = 600.0;
00601 Vector za_ref;
00602 linspace( za_ref, za_ref_min, za_ref_max, 1/za_step );
00603
00604
00605 Vector lat_ref( za_ref.nelem() );
00606 Vector z_ref( za_ref.nelem() );
00607 Index i;
00608 for ( i=0; i<za_ref.nelem(); i++ ) {
00609 Ppath ppath;
00610 ppath_calc( ws, ppath, ppath_step_agenda, atmosphere_dim,
00611 p_grid, lat_grid, lon_grid, z_field, r_geoid,
00612 z_surface, 0, ArrayOfIndex(0), Vector(1,r_recieve),
00613 Vector(1,za_ref[i]), 1 );
00614 if ( ppath.tan_pos.nelem() != 0 ) {
00615
00616
00617
00618 lat_ref[i] = geompath_lat_at_za( ppath.los(ppath.np-1,0),
00619 ppath.pos(ppath.np-1,1), geompath_za_at_r(
00620 ppath.pos(ppath.np-1,0)*sin(DEG2RAD*ppath.los(ppath.np-1,0)),
00621 10, r_send ));
00622 z_ref[i] = ppath.tan_pos[0] - r_geoid(0,0);
00623 } else {
00624
00625
00626 lat_ref[i] = 2*lat_ref[i-1] - lat_ref[i-2];
00627 z_ref[i] = 2*z_ref[i-1] - z_ref[i-2];
00628 break;
00629 }
00630 }
00631
00632
00633 Vector za_ref_above = za_ref[Range(0,i)];
00634 Vector z_ref_above = z_ref[Range(0,i)];
00635 Vector lat_ref_above = lat_ref[Range(0,i)];
00636
00637
00638
00639 GridPos gp_high, gp_low;
00640 gridpos( gp_high, z_ref_above, z_scan_high );
00641 gridpos( gp_low, z_ref_above, z_scan_low );
00642
00643 Vector itw_high(2), itw_low(2);
00644 interpweights( itw_high, gp_high );
00645 interpweights( itw_low, gp_low );
00646
00647 Numeric lat_min = interp( itw_high, lat_ref_above, gp_high );
00648 Numeric lat_max = interp( itw_low, lat_ref_above, gp_low );
00649
00650
00651
00652
00653 Numeric w_send, w_recieve, lat_dot, lat_sample;
00654 w_send = sqrt(EARTH_GRAV_CONST/r_send) / r_send;
00655 w_recieve = sqrt(EARTH_GRAV_CONST/r_recieve) / r_recieve;
00656 lat_dot = (w_send + w_recieve) * RAD2DEG;
00657 lat_sample = t_sample * lat_dot;
00658
00659 Vector lat_out;
00660 linspace( lat_out, lat_min, lat_max, lat_sample );
00661
00662
00663 ArrayOfGridPos gp(lat_out.nelem());
00664 gridpos( gp, lat_ref_above, lat_out );
00665 Matrix itw(lat_out.nelem(), 2);
00666 interpweights( itw, gp );
00667 za_out.resize( lat_out.nelem() );
00668 interp( za_out, itw, za_ref_above, gp );
00669
00670 }