ARTS  2.0.49
ppath.cc
Go to the documentation of this file.
1 /* Copyright (C) 2002-2008 Patrick Eriksson <patrick.eriksson@chalmers.se>
2 
3  This program is free software; you can redistribute it and/or modify it
4  under the terms of the GNU General Public License as published by the
5  Free Software Foundation; either version 2, or (at your option) any
6  later version.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16  USA. */
17 
18 
19 
20 /*===========================================================================
21  === File description
22  ===========================================================================*/
23 
39 /*===========================================================================
40  === External declarations
41  ===========================================================================*/
42 
43 #include <cmath>
44 #include <stdexcept>
45 #include "agenda_class.h"
46 #include "array.h"
47 #include "arts_omp.h"
48 #include "auto_md.h"
49 #include "check_input.h"
50 #include "math_funcs.h"
51 #include "messages.h"
52 #include "mystring.h"
53 #include "logic.h"
54 #include "poly_roots.h"
55 #include "ppath.h"
56 #include "refraction.h"
57 #include "special_interp.h"
58 
59 extern const Numeric DEG2RAD;
60 extern const Numeric RAD2DEG;
61 
62 
63 
64 /*===========================================================================
65  === Common precision variables
66  ===========================================================================*/
67 
68 // This variable defines the maximum allowed error tolerance for radius.
69 // The variable is, for example, used to check that a given a radius is
70 // consistent with the specified grid cell.
71 //
72 #ifdef USE_DOUBLE
73 const double RTOL = 1e-2;
74 #else
75 const double RTOL = 10;
76 #endif
77 
78 
79 // As RTOL but for latitudes and longitudes.
80 //
81 #ifdef USE_DOUBLE
82 const double LATLONTOL = 1e-11;
83 #else
84 const double LATLONTOL = 1e-6;
85 #endif
86 
87 
88 // This variable defines how much zenith and azimuth angles can
89 // deviate from 0, 90 and 180 degrees, but still be treated to be 0,
90 // 90 or 180. For example, an azimuth angle of 180-ANGTOL/2 will
91 // be treated as a strictly southward observation. However, the
92 // angles are not allowed to go outside their defined range. This
93 // means, for example, that values above 180 are never allowed.
94 //
95 #ifdef USE_DOUBLE
96 const double ANGTOL = 1e-7;
97 #else
98 const double ANGTOL = 1e-4;
99 #endif
100 
101 
102 // Latitudes with an absolute value > POLELAT are considered to be on
103 // the south or north pole for 3D.
104 //
105 const double POLELAT = 89.9999;
106 
107 
108 // Maximum tilt of pressure levels, in degrees
109 //
110 const double PTILTMAX = 5;
111 
112 
113 
114 /*===========================================================================
115  === Functions related to geometrical propagation paths
116  ===========================================================================*/
117 
119 
131 double geometrical_ppc( const double& r, const double& za )
132 {
133  assert( r > 0 );
134  assert( abs(za) <= 180 );
135 
136  return r * sin( DEG2RAD * abs(za) );
137 }
138 
139 
140 
142 
161  const double& ppc,
162  const double& a_za,
163  const double& r )
164 {
165  assert( ppc >= 0 );
166  assert( abs(a_za) <= 180 );
167  assert( r >= ppc - RTOL );
168 
169  if( r > ppc )
170  {
171  double za = RAD2DEG * asin( ppc / r );
172  if( abs(a_za) > 90 )
173  { za = 180 - za; }
174  if( a_za < 0 )
175  { za = -za; }
176  return za;
177  }
178  else
179  {
180  return 90;
181  }
182 }
183 
184 
185 
187 
201  const double& ppc,
202  const double& za )
203 {
204  assert( ppc >= 0 );
205  assert( abs(za) <= 180 );
206 
207  return ppc / sin( DEG2RAD * abs(za) );
208 }
209 
210 
211 
213 
229  const double& za0,
230  const double& lat0,
231  const double& za )
232 {
233  assert( abs(za0) <= 180 );
234  assert( abs(za) <= 180 );
235  assert( ( za0 >= 0 && za >= 0 ) || ( za0 < 0 && za < 0 ) );
236 
237  return lat0 + za0 - za;
238 }
239 
240 
241 
243 
257  const double& ppc,
258  const double& r )
259 {
260  assert( ppc >= 0 );
261  assert( r >= ppc - RTOL );
262 
263  if( r > ppc )
264  { return sqrt( r*r - ppc*ppc ); }
265  else
266  { return 0; }
267 }
268 
269 
270 
272 
286  const double& ppc,
287  const double& l )
288 {
289  assert( ppc >= 0 );
290  assert( l >= 0 );
291 
292  return sqrt( l*l + ppc*ppc );
293 }
294 
295 
296 
298 
311  const double& ppc,
312  const double& lat0,
313  const double& za0,
314  const double& lat )
315 {
316  assert( ppc >= 0 );
317  assert( abs(za0) <= 180 );
318  assert( ( za0 >= 0 && lat >= lat0 ) || ( za0 <= 0 && lat <= lat0 ) );
319 
320  // Zenith angle at the new latitude
321  const double za = za0 + lat0 -lat;
322 
323  return geompath_r_at_za( ppc, za );
324 }
325 
326 
327 
329 
351  Vector& r,
352  Vector& lat,
353  Vector& za,
354  double& lstep,
355  const double& ppc,
356  const double& r1,
357  const double& lat1,
358  const double& za1,
359  const double& r2,
360  const double& lmax )
361 {
362  // Calculate length along the path for point 1 and 2.
363  const double l1 = geompath_l_at_r( ppc, r1 );
364  const double l2 = geompath_l_at_r( ppc, r2 );
365 
366  // Calculate needed number of steps, considering a possible length criterion
367  Index n;
368  if( lmax > 0 )
369  {
370  // The absolute value of the length distance is needed here
371  n = Index( ceil( abs( l2 - l1 ) / lmax ) );
372 
373  // We can't accept n=0, which is the case if l1 = l2
374  if( n < 1 )
375  { n = 1; }
376  }
377  else
378  { n = 1; }
379 
380  // Length of path steps (note that lstep here can be negative)
381  lstep = ( l2 - l1 ) / (double)n;
382 
383  // Allocate vectors and put in point 1
384  r.resize(n+1);
385  lat.resize(n+1);
386  za.resize(n+1);
387  r[0] = r1;
388  lat[0] = lat1;
389  za[0] = za1;
390 
391  // Loop steps (beside last) and calculate radius and zenith angle
392  for( Index i=1; i<n; i++ )
393  {
394  r[i] = geompath_r_at_l( ppc, l1 + lstep * (double)i );
395  za[i] = geompath_za_at_r( ppc, za1, r[i] );
396  }
397 
398  // For maximum accuracy, set last radius to be exactly r2.
399  r[n] = r2;
400  za[n] = geompath_za_at_r( ppc, za1, r2 ); // Don't use r[n] here
401 
402  // Ensure that zenith and nadir observations keep their zenith angle
403  if( abs(za1) < ANGTOL || abs(za1) > 180-ANGTOL )
404  { za = za1; }
405 
406  // Calculate latitudes
407  for( Index i=1; i<=n; i++ )
408  { lat[i] = geompath_lat_at_za( za1, lat1, za[i] ); }
409 
410  // Take absolute value of lstep
411  lstep = abs( lstep );
412 }
413 
414 
415 
417 
434  const double& r1,
435  const double& lat1,
436  const double& r2,
437  const double& lat2 )
438 {
439  if( lat2 == lat1 )
440  {
441  if( r1 <= r2 )
442  { return 0; }
443  else
444  { return 180; }
445  }
446  else
447  {
448  // Absolute value of the latitude difference
449  const double dlat = abs( lat2 - lat1 );
450 
451  // The zenith angle is obtained by a combination of the lawes of sine
452  // and cosine.
453  double za = dlat + RAD2DEG * asin( r1 * sin( DEG2RAD * dlat ) /
454  sqrt( r1*r1 + r2*r2 - 2 * r1 * r2 * cos( DEG2RAD * dlat ) ) );
455 
456  // Consider viewing direction
457  if( lat2 < lat1 )
458  { za = -za; }
459 
460  return za;
461  }
462 }
463 
464 
465 
466 
467 
468 /*===========================================================================
469  === Basic (true) 3D function
470  ===========================================================================*/
471 
473 
490 void sph2cart(
491  double& x,
492  double& y,
493  double& z,
494  const double& r,
495  const double& lat,
496  const double& lon )
497 {
498  assert( r > 0 );
499  assert( abs( lat ) <= 90 );
500  assert( abs( lon ) <= 360 );
501 
502  const double latrad = DEG2RAD * lat;
503  const double lonrad = DEG2RAD * lon;
504 
505  x = r * cos( latrad ); // Common term for x and z
506  z = x * sin( lonrad );
507  x = x * cos( lonrad );
508  y = r * sin( latrad );
509 }
510 
511 
512 
514 
527 void cart2sph(
528  double& r,
529  double& lat,
530  double& lon,
531  const double& x,
532  const double& y,
533  const double& z )
534 {
535  r = sqrt( x*x + y*y + z*z );
536  lat = RAD2DEG * asin( y / r );
537  lon = RAD2DEG * atan2( z, x );
538 }
539 
540 
541 
543 
568  double& x,
569  double& y,
570  double& z,
571  double& dx,
572  double& dy,
573  double& dz,
574  const double& r,
575  const double& lat,
576  const double& lon,
577  const double& za,
578  const double& aa )
579 {
580  // lat = +-90
581  // For lat = +- 90 the azimuth angle gives the longitude along which the
582  // LOS goes
583  if( abs( lat ) > POLELAT )
584  {
585  const double s = sign( lat );
586 
587  x = 0;
588  z = 0;
589  y = s * r;
590 
591  dy = s * cos( DEG2RAD * za );
592  dx = sin( DEG2RAD * za );
593  dz = dx * sin( DEG2RAD * aa );
594  dx = dx * cos( DEG2RAD * aa );
595  }
596 
597  else
598  {
599  const double latrad = DEG2RAD * lat;
600  const double lonrad = DEG2RAD * lon;
601  const double zarad = DEG2RAD * za;
602  const double aarad = DEG2RAD * aa;
603 
604  sph2cart( x, y, z, r, lat, lon );
605 
606  const double coslat = cos( latrad );
607  const double sinlat = sin( latrad );
608  const double coslon = cos( lonrad );
609  const double sinlon = sin( lonrad );
610  const double cosza = cos( zarad );
611  const double sinza = sin( zarad );
612  const double cosaa = cos( aarad );
613  const double sinaa = sin( aarad );
614 
615  const double dr = cosza;
616  const double dlat = sinza * cosaa; // r-terms cancel out below
617  const double dlon = sinza * sinaa / coslat;
618 
619  dx = coslat*coslon * dr - sinlat*coslon * dlat - coslat*sinlon * dlon;
620  dy = sinlat * dr + coslat * dlat;
621  dz = coslat*sinlon * dr - sinlat*sinlon * dlat + coslat*coslon * dlon;
622  }
623 }
624 
625 
626 
628 
653  double& r,
654  double& lat,
655  double& lon,
656  double& za,
657  double& aa,
658  const double& x,
659  const double& y,
660  const double& z,
661  const double& dx,
662  const double& dy,
663  const double& dz )
664 {
665  // Assert that LOS vector is normalised
666  assert( abs( sqrt( dx*dx + dy*dy + dz*dz ) - 1 ) < 1e-6 );
667 
668  // Spherical coordinates
669  cart2sph( r, lat, lon, x, y, z );
670 
671  // Spherical derivatives
672  const double coslat = cos( DEG2RAD * lat );
673  const double sinlat = sin( DEG2RAD * lat );
674  const double coslon = cos( DEG2RAD * lon );
675  const double sinlon = sin( DEG2RAD * lon );
676  const double dr = coslat*coslon*dx + sinlat*dy + coslat*sinlon*dz;
677  const double dlat = -sinlat*coslon/r*dx + coslat/r*dy -sinlat*sinlon/r*dz;
678  const double dlon = -sinlon/coslat/r*dx + coslon/coslat/r*dz;
679 
680  // LOS angles
681  za = RAD2DEG * acos( dr );
682  //
683  if( za < ANGTOL || za > 180-ANGTOL )
684  { aa = 0; }
685 
686  else if( abs( lat ) <= POLELAT )
687  {
688  aa = RAD2DEG * acos( r * dlat / sin( DEG2RAD * za ) );
689 
690  if( isnan( aa ) )
691  {
692  if( dlat >= 0 )
693  { aa = 0; }
694  else
695  { aa = 180; }
696  }
697  else if( dlon < 0 )
698  { aa = -aa; }
699  }
700 
701  // For lat = +- 90 the azimuth angle gives the longitude along which the
702  // LOS goes
703  else
704  { aa = RAD2DEG * atan2( dz, dx ); }
705 }
706 
707 
708 
710 
736  double& lon,
737  const double& lon5,
738  const double& lon6 )
739 {
740  assert( lon6 >= lon5 );
741 
742  if( lon < lon5 && lon+180 <= lon6 )
743  { lon += 360; }
744  else if( lon > lon6 && lon-180 >= lon5 )
745  { lon -= 360; }
746 }
747 #ifndef USE_DOUBLE
749  float& lon,
750  const double& lon5,
751  const double& lon6 )
752 {
753  assert( lon6 >= lon5 );
754 
755  if( lon < lon5 && lon+180 <= lon6 )
756  { lon += 360; }
757  else if( lon > lon6 && lon-180 >= lon5 )
758  { lon -= 360; }
759 }
760 #endif
761 
762 
763 
765 
785  double& r_tan,
786  double& lat_tan,
787  double& lon_tan,
788  double& l_tan,
789  const double& r,
790  const double& lat,
791  const double& lon,
792  const double& za,
793  const double& aa,
794  const double& ppc )
795 {
796  assert( za >= 90 );
797  assert( r >= ppc );
798 
799  double x, y, z, dx, dy, dz;
800 
801  poslos2cart( x, y, z, dx, dy, dz, r, lat, lon, za, aa );
802 
803  l_tan = sqrt( r*r - ppc*ppc );
804 
805  cart2sph( r_tan, lat_tan, lon_tan, x+dx*l_tan, y+dy*l_tan, z+dz*l_tan );
806 }
807 
808 
809 
811 
835  double& dx,
836  double& dy,
837  double& dz,
838  const double& za,
839  const double& aa )
840 {
841  const double zarad = DEG2RAD * za;
842  const double aarad = DEG2RAD * aa;
843 
844  dz = cos( zarad );
845  dx = sin( zarad );
846  dy = sin( aarad ) * dx;
847  dx = cos( aarad ) * dx;
848 }
849 
850 
851 
853 
877  double& za,
878  double& aa,
879  const double& dx,
880  const double& dy,
881  const double& dz )
882 {
883  const double r = sqrt( dx*dx + dy*dy + dz*dz );
884 
885  assert( r > 0 );
886 
887  za = RAD2DEG * acos( dz / r );
888  aa = RAD2DEG * atan2( dy, dx );
889 }
890 
891 
892 
894 
912  Matrix& R,
913  ConstVectorView vrot,
914  const Numeric& a )
915 {
916  assert( R.ncols() == 3 );
917  assert( R.nrows() == 3 );
918  assert( vrot.nelem() == 3 );
919 
920  const double u = vrot[0];
921  const double v = vrot[1];
922  const double w = vrot[2];
923 
924  const double u2 = u * u;
925  const double v2 = v * v;
926  const double w2 = w * w;
927 
928  assert( sqrt( u2 + v2 + w2 ) );
929 
930  const double c = cos( DEG2RAD * a );
931  const double s = sin( DEG2RAD * a );
932 
933  // Fill R
934  R(0,0) = u2 + (v2 + w2)*c;
935  R(0,1) = u*v*(1-c) - w*s;
936  R(0,2) = u*w*(1-c) + v*s;
937  R(1,0) = u*v*(1-c) + w*s;
938  R(1,1) = v2 + (u2+w2)*c;
939  R(1,2) = v*w*(1-c) - u*s;
940  R(2,0) = u*w*(1-c) - v*s;
941  R(2,1) = v*w*(1-c)+u*s;
942  R(2,2) = w2 + (u2+v2)*c;
943 }
944 
945 
946 
967 void map_daa(
968  double& za,
969  double& aa,
970  const double& za0,
971  const double& aa0,
972  const double& aa_grid )
973 {
974  assert( abs( aa_grid ) <= 5 );
975 
976  Vector xyz(3);
977  Vector vrot(3);
978  Vector u(3);
979 
980  // Unit vector towards aa0 at za=90
981  //
982  zaaa2cart( xyz[0], xyz[1], xyz[2], 90, aa0 );
983 
984  // Find vector around which rotation shall be performed
985  //
986  // We can write this as cross([0 0 1],xyz). It turns out that the result
987  // of this operation is just [-y,x,0].
988  //
989  vrot[0] = -xyz[1];
990  vrot[1] = xyz[0];
991  vrot[2] = 0;
992 
993  // Unit vector towards aa0+aa at za=90
994  //
995  zaaa2cart( xyz[0], xyz[1], xyz[2], 90, aa0+aa_grid );
996 
997  // Apply rotation
998  //
999  Matrix R(3,3);
1000  rotationmat3D( R, vrot, za0-90 );
1001  mult( u, R, xyz );
1002 
1003  // Calculate za and aa for rotated u
1004  //
1005  cart2zaaa( za, aa, u[0], u[1], u[2] );
1006 }
1007 
1008 
1009 
1010 
1011 
1012 
1013 /*===========================================================================
1014  === Functions related to slope and tilt of the surface and pressure levels
1015  ===========================================================================*/
1016 
1018 
1044  ConstVectorView lat_grid,
1045  ConstVectorView r_geoid,
1046  ConstVectorView z_surf,
1047  const GridPos& gp,
1048  const double& za )
1049 {
1050  Index i1 = gridpos2gridrange( gp, abs( za ) >= 0 );
1051  const double r1 = r_geoid[i1] + z_surf[i1];
1052  const double r2 = r_geoid[i1+1] + z_surf[i1+1];
1053  return ( r2 - r1 ) / ( lat_grid[i1+1] - lat_grid[i1] );
1054 }
1055 
1056 
1057 
1059 
1077  const double& lat1,
1078  const double& lat2,
1079  const double& r1,
1080  const double& r2 )
1081 {
1082  return ( r2 - r1 ) / ( lat2 -lat1 );
1083 }
1084 
1085 
1086 
1088 
1103  const double& lat1,
1104  const double& lat3,
1105  const double& r1,
1106  const double& r3,
1107  const double& lat )
1108 {
1109  return r1 + ( lat - lat1 ) * ( r3 - r1 ) / ( lat3 - lat1 );
1110 }
1111 
1112 
1114 
1134  const double& lat1,
1135  const double& lat3,
1136  const double& lon5,
1137  const double& lon6,
1138  const double& r15,
1139  const double& r35,
1140  const double& r36,
1141  const double& r16,
1142  const double& lat,
1143  const double& lon )
1144 {
1145  // We can't have any assert of *lat* and *lon* here as we can go outside
1146  // the ranges when called from *plevel_slope_3d*.
1147 
1148  if( lat == lat1 )
1149  { return r15 + ( lon - lon5 ) * ( r16 - r15 ) / ( lon6 -lon5 ); }
1150  else if( lat == lat3 )
1151  { return r35 + ( lon - lon5 ) * ( r36 - r35 ) / ( lon6 -lon5 ); }
1152  else if( lon == lon5 )
1153  { return r15 + ( lat - lat1 ) * ( r35 - r15 ) / ( lat3 -lat1 ); }
1154  else if( lon == lon6 )
1155  { return r16 + ( lat - lat1 ) * ( r36 - r16 ) / ( lat3 -lat1 ); }
1156  else
1157  {
1158  const double fdlat = ( lat - lat1 ) / ( lat3 - lat1 );
1159  const double fdlon = ( lon - lon5 ) / ( lon6 - lon5 );
1160  return (1-fdlat)*(1-fdlon)*r15 + fdlat*(1-fdlon)*r35 +
1161  (1-fdlat)*fdlon*r16 + fdlat*fdlon*r36;
1162  }
1163 }
1164 
1165 
1166 
1168 
1196  const double& lat1,
1197  const double& lat3,
1198  const double& lon5,
1199  const double& lon6,
1200  const double& r15,
1201  const double& r35,
1202  const double& r36,
1203  const double& r16,
1204  const double& lat,
1205  const double& lon,
1206  const double& aa )
1207 {
1208  // Size of test angular distance. Unit is degrees.
1209  const double dang = 1e-5;
1210 
1211  // Radius at point of interest
1212  const double r0 = rsurf_at_latlon( lat1, lat3, lon5, lon6,
1213  r15, r35, r36, r16, lat, lon );
1214 
1215  // Convert position and an imaginary LOS to cartesian coordinates
1216  // double is hard-coded to match declaration of poslos2cart and for
1217  // maximum precision
1218  double x, y, z, dx, dy, dz;
1219  poslos2cart( x, y, z, dx, dy, dz, r0, lat, lon, 90, aa );
1220 
1221  // Calculate the distance corresponding to *dang*
1222  const double l = r0 * DEG2RAD * dang;
1223 
1224  // Calculate radius of the pressure level at the lat/lon, the distance
1225  // *l* away.
1226  double r2, lat2, lon2, za2, aa2;
1227  cart2poslos( r2, lat2, lon2, za2, aa2, x+dx*l, y+dy*l, z+dz*l, dx, dy, dz );
1228  resolve_lon( lon2, lon5, lon6 );
1229  r2 = rsurf_at_latlon( lat1, lat3, lon5, lon6, r15, r35, r36, r16, lat2,lon2);
1230 
1231  // Return slope
1232  return ( r2 - r0 ) / dang;
1233 }
1234 
1235 
1236 
1238 
1268  ConstVectorView lat_grid,
1269  ConstVectorView lon_grid,
1270  ConstMatrixView r_geoid,
1271  ConstMatrixView z_surf,
1272  const GridPos& gp_lat,
1273  const GridPos& gp_lon,
1274  const double& aa )
1275 {
1276  Index ilat = gridpos2gridrange( gp_lat, abs( aa ) >= 0 );
1277  Index ilon = gridpos2gridrange( gp_lon, aa >= 0 );
1278 
1279  // Restore latitude and longitude values
1280  Vector itw(2);
1281  double lat, lon;
1282  interpweights( itw, gp_lat );
1283  lat = interp( itw, lat_grid, gp_lat );
1284  interpweights( itw, gp_lon );
1285  lon = interp( itw, lon_grid, gp_lon );
1286 
1287  // Extract values that defines the grid cell
1288  const double lat1 = lat_grid[ilat];
1289  const double lat3 = lat_grid[ilat+1];
1290  const double lon5 = lon_grid[ilon];
1291  const double lon6 = lon_grid[ilon+1];
1292  const double r15 = r_geoid(ilat,ilon) + z_surf(ilat,ilon);
1293  const double r35 = r_geoid(ilat+1,ilon) + z_surf(ilat+1,ilon);
1294  const double r36 = r_geoid(ilat+1,ilon+1) + z_surf(ilat+1,ilon+1);
1295  const double r16 = r_geoid(ilat,ilon+1) + z_surf(ilat,ilon+1);
1296 
1297  return plevel_slope_3d( lat1, lat3, lon5, lon6, r15, r35, r36, r16,
1298  lat, lon, aa );
1299 }
1300 
1301 
1302 
1304 
1318  const double& r,
1319  const double& c )
1320 {
1321  // The tilt (in radians) is c/r if c is converted to m/radian. So we get
1322  // conversion RAD2DEG twice
1323  return RAD2DEG * RAD2DEG * c / r;
1324 }
1325 
1326 
1327 
1329 
1349  const Index& atmosphere_dim,
1350  ConstVectorView lat_grid,
1351  ConstVectorView lon_grid,
1352  ConstMatrixView r_geoid,
1353  ConstMatrixView z_surface,
1354  const GridPos& gp_lat,
1355  const GridPos& gp_lon,
1356  ConstVectorView los )
1357 {
1358  if( atmosphere_dim == 1 )
1359  { return 0.0; }
1360  else if( atmosphere_dim == 2 )
1361  {
1362  const double rslope = plevel_slope_2d( lat_grid, r_geoid(joker,0),
1363  z_surface(joker,0), gp_lat, los[0] );
1364  Vector itw(2); interpweights( itw, gp_lat );
1365  const Numeric rv_surface = interp( itw, r_geoid(joker,0), gp_lat ) +
1366  interp( itw, z_surface(joker,0), gp_lat );
1367  return plevel_angletilt( rv_surface, rslope);
1368  }
1369  else
1370  {
1371  const double rslope = plevel_slope_3d( lat_grid, lon_grid, r_geoid,
1372  z_surface, gp_lat, gp_lon, los[1] );
1373  Vector itw(4); interpweights( itw, gp_lat, gp_lon );
1374  const Numeric rv_surface = interp( itw, r_geoid, gp_lat, gp_lon ) +
1375  interp( itw, z_surface, gp_lat, gp_lon );
1376  return plevel_angletilt( rv_surface, rslope);
1377  }
1378 }
1379 
1380 
1381 
1383 
1403  const double& za,
1404  const double& tilt )
1405 {
1406  assert( abs(za) <= 180 );
1407 
1408  // Yes, it shall be -tilt in both cases, if you wonder.
1409  if( za > (90-tilt) || za < (-90-tilt) )
1410  { return true; }
1411  else
1412  { return false; }
1413 }
1414 
1415 
1416 
1418 
1463  const double& rp,
1464  const double& za,
1465  const double& r0,
1466  double c )
1467 {
1468  assert( abs(za) <= 180 );
1469 
1470  const double no_crossing = 999;
1471 
1472  // Handle the cases of za=0 and za=180.
1473  if( abs(za) < ANGTOL )
1474  {
1475  if( rp < r0 )
1476  { return 0; }
1477  else
1478  { return no_crossing; }
1479  }
1480  if( abs(za) > 180-ANGTOL )
1481  {
1482  if( rp > r0 )
1483  { return 0; }
1484  else
1485  { return no_crossing; }
1486  }
1487 
1488  // If rp>=r0, check if the given LOS goes in the direction towards
1489  // the pressure level. If not, return 999.
1490  //
1491  if( rp >= r0 && !is_los_downwards( za, plevel_angletilt( r0, c ) ) )
1492  { return no_crossing; }
1493 
1494  // The case with c=0 can be handled analytically
1495  if( c == 0 )
1496  {
1497  double ppc = geometrical_ppc( rp, za );
1498  if( ( rp < r0 && abs(za) <= 90 ) ||
1499  ( rp > r0 && abs(za) > 90 && r0 >= ppc ) )
1500  { return geompath_lat_at_za( za, 0, geompath_za_at_r( ppc, za, r0 ) );}
1501  else
1502  { return no_crossing; }
1503  }
1504 
1505 
1506  // Approximative solution:
1507  else
1508  {
1509  // If r0=rp, numerical inaccuracy can give a false solution, very close
1510  // to 0, that we must throw away.
1511  double dmin = 0;
1512  if( r0 == rp )
1513  { dmin = 1e-12; }
1514 
1515  // The nadir angle in radians, and cosine and sine of that angle
1516  const double beta = DEG2RAD * ( 180 - abs(za) );
1517  const double cv = cos( beta );
1518  const double sv = sin( beta );
1519 
1520  // Convert slope to m/radian and consider viewing direction
1521  c *= RAD2DEG;
1522  if( za < 0 )
1523  { c = -c; }
1524 
1525  // The vector of polynomial coefficients
1526  Vector p(5);
1527  //
1528  p[0] = ( r0 - rp ) * sv;
1529  p[1] = r0 * cv + c * sv;
1530  p[2] = -r0 * sv / 2 + c * cv;
1531  p[3] = -r0 * cv / 6 - c * sv / 2;
1532  p[4] = -c * cv / 6;
1533 
1534  // Calculate roots of the polynomial
1535  Matrix roots(4,2);
1536  poly_root_solve( roots, p );
1537 
1538  // Find the smallest root with imaginary part = 0, and real part > 0.
1539  //
1540  double dlat = no_crossing / RAD2DEG;
1541  //
1542  for( Index i=0; i<4; i++ )
1543  {
1544  if( roots(i,1) == 0 && roots(i,0) > dmin && roots(i,0) < dlat )
1545  { dlat = roots(i,0); }
1546  }
1547 
1548  // Change sign if zenith angle is negative
1549  if( dlat < no_crossing && za < 0 )
1550  { dlat = -dlat; }
1551 
1552  return RAD2DEG * dlat;
1553  }
1554 }
1555 
1556 
1557 
1559 
1600  double& r,
1601  double& lat,
1602  double& lon,
1603  double& l,
1604  const double r_surf,
1605  const double r_start, // No & to avoid problems if these
1606  const double lat_start, // variables are the same as the
1607  const double lon_start, // output ones.
1608  const double za_start,
1609  const double& x,
1610  const double& y,
1611  const double& z,
1612  const double& dx,
1613  const double& dy,
1614  const double& dz )
1615 {
1616  assert( za_start >= 0 );
1617  assert( za_start <= 180 );
1618 
1619  // Set values for no crossing found
1620  r = -1;
1621  lat = 999;
1622  lon = 999;
1623  l = -1;
1624 
1625  // Handle the cases of za=0 and za=180.
1626  if( ( za_start < ANGTOL && r_start < r_surf ) ||
1627  ( za_start > 180-ANGTOL && r_start > r_surf ) )
1628  {
1629  r = r_surf;
1630  lat = lat_start;
1631  lon = lon_start;
1632  l = abs( r_surf - r_start );
1633  }
1634 
1635  else
1636  {
1637  const double p = x*dx + y*dy + z*dz;
1638  const double pp = p * p;
1639  const double q = x*x + y*y + z*z - r_surf*r_surf;
1640 
1641  const double l1 = -p + sqrt( pp - q );
1642  const double l2 = -p - sqrt( pp - q );
1643 
1644  if( l1 < 0 && l2 > 0 )
1645  { l = l2; }
1646  else if( l1 > 0 && l2 < 0 )
1647  { l = l1; }
1648  else if( l1 < l2 )
1649  { l = l1; }
1650  else
1651  { l = l2; }
1652 
1653  if( l > 0 )
1654  {
1655  r = r_surf;
1656  lat = RAD2DEG * asin( ( y+dy*l ) / r );
1657  lon = RAD2DEG * atan2( z+dz*l, x+dx*l );
1658  }
1659 
1660  }
1661 }
1662 
1663 
1664 
1665 
1666 /*===========================================================================
1667  === Path through grid cells and 1D pressure range
1668  ===========================================================================*/
1669 
1671 
1698  Vector& r_v,
1699  Vector& lat_v,
1700  Vector& za_v,
1701  double& lstep,
1702  Index& endface,
1703  Index& tanpoint,
1704  const double& r_start0,
1705  const double& lat_start,
1706  const double& za_start,
1707  const double& ppc,
1708  const double& lmax,
1709  const double& ra,
1710  const double& rb,
1711  const double& rsurface )
1712 {
1713  double r_start = r_start0;
1714 
1715  assert( rb > ra );
1716  assert( r_start >= ra - RTOL );
1717  assert( r_start <= rb + RTOL );
1718 
1719  // Shift radius if outside
1720  if( r_start < ra )
1721  { r_start = ra; }
1722  else if( r_start > rb )
1723  { r_start = rb; }
1724 
1725  // Get end radius of the path step (r_end). If looking downwards, it must
1726  // be checked if:
1727  // a tangent point is passed
1728  // there is an intersection with the surface
1729  //
1730  double r_end;
1731  //
1732  endface = 0;
1733  tanpoint = 0;
1734  //
1735  if( za_start <= 90 )
1736  {
1737  r_end = rb;
1738  endface = 3;
1739  }
1740  else
1741  {
1742  // The tangent radius equals here ppc.
1743 
1744  if( ( ra > rsurface ) && ( ra > ppc ) )
1745  {
1746  r_end = ra;
1747  endface = 1;
1748  }
1749  else if( ppc >= rsurface )
1750  {
1751  r_end = ppc;
1752  tanpoint = 1;
1753  if( ppc != ra )
1754  { endface = 0; }
1755  }
1756  else
1757  {
1758  r_end = rsurface;
1759  endface = 7;
1760  }
1761  }
1762 
1763  // Calculate basic variables from r_start to r_end.
1764  //
1765  geompath_from_r1_to_r2( r_v, lat_v, za_v, lstep, ppc, r_start, lat_start,
1766  za_start, r_end, lmax );
1767 
1768  // Force end zenith angle to be exact when we know the correct value
1769  if( tanpoint )
1770  { za_v[za_v.nelem()-1] = 90; }
1771 }
1772 
1773 
1774 
1776 
1826  Vector& r_v,
1827  Vector& lat_v,
1828  Vector& za_v,
1829  double& lstep,
1830  Index& endface,
1831  Index& tanpoint,
1832  const double& r_start0,
1833  const double& lat_start,
1834  const double& za_start,
1835  const double& ppc,
1836  const double& lmax,
1837  const double& lat1,
1838  const double& lat3,
1839  const double& r1a,
1840  const double& r3a,
1841  const double& r3b,
1842  const double& r1b,
1843  const double& rsurface1,
1844  const double& rsurface3 )
1845 {
1846  double r_start = r_start0;
1847 
1848  // Assert latitudes
1849  assert( lat_start >= lat1 );
1850  assert( lat_start <= lat3 );
1851 
1852  // Slopes of pressure levels
1853  const double c2 = plevel_slope_2d( lat1, lat3, r1a, r3a );
1854  const double c4 = plevel_slope_2d( lat1, lat3, r1b, r3b );
1855  const double csurface = plevel_slope_2d( lat1, lat3, rsurface1, rsurface3 );
1856 
1857  // Latitude distance between start point and left grid cell boundary
1858  const double dlat_left = lat_start - lat1;
1859 
1860  // Radius of lower and upper pressure level at *lat_start*.
1861  const double rlow = r1a + c2*dlat_left;
1862  const double rupp = r1b + c4*dlat_left;
1863 
1864  // Latitude distance to latitude end face in the viewing direction
1865  double dlat_endface;
1866  if( za_start >= 0 )
1867  { dlat_endface = lat3 - lat_start; }
1868  else
1869  { dlat_endface = -dlat_left; }
1870 
1871  // Assert radius (some extra tolerance is needed for radius)
1872  assert( r_start >= rlow - RTOL );
1873  assert( r_start <= rupp + RTOL );
1874 
1875  // Shift radius if outside
1876  if( r_start < rlow )
1877  { r_start = rlow; }
1878  else if( r_start > rupp )
1879  { r_start = rupp; }
1880 
1881  // The end point is most easily determined by the latitude difference to
1882  // the start point. For some cases there exist several crossings and we want
1883  // the one closest in latitude to the start point. The latitude distance
1884  // for the crossing shall not exceed dlat2end.
1885  //
1886  double dlat2end = 999;
1887  double abs_za_start = abs( za_start );
1888  endface = 0;
1889 
1890 
1891  // --- Lower face
1892  //
1893  // We don't need to consider the face if we are standing
1894  // on the pressure level.
1895  //
1896  if( r_start > rlow && abs(za_start) > ANGTOL )
1897  {
1898  dlat2end = plevel_crossing_2d( r_start, za_start, rlow, c2 );
1899  endface = 2; // This variable will be re-set if there was no crossing
1900  }
1901 
1902 
1903  // --- The surface.
1904  //
1905  // Check shall be done only if the surface is, at least partly, inside
1906  // the grid cell.
1907  //
1908  if( rsurface1 >= r1a || rsurface3 >= r3a )
1909  {
1910  double r_surface = rsurface1 + csurface * dlat_left;
1911 
1912  assert( r_start >= r_surface );
1913 
1914  double dlat2surface = plevel_crossing_2d( r_start, za_start, r_surface,
1915  csurface );
1916  if( abs(dlat2surface) <= abs(dlat2end) )
1917  {
1918  dlat2end = dlat2surface;
1919  endface = 7;
1920  }
1921  }
1922 
1923 
1924  // If dlat2end <= dlat_endface we are ready. Otherwise we have to check
1925  // remaining cell faces. The same applies after testing upper face.
1926 
1927 
1928  // --- Upper face (pressure level ip+1).
1929  //
1930  if( abs(dlat2end) > abs(dlat_endface) && abs_za_start < 180-ANGTOL )
1931  {
1932  // For cases when the tangent point is in-between *r_start* and
1933  // the pressure level, 999 is returned. This case will anyhow
1934  // be handled correctly.
1935 
1936  double dlattry = plevel_crossing_2d( r_start, za_start, rupp, c4 );
1937 
1938  if( abs(dlattry) < abs(dlat2end) )
1939  {
1940  dlat2end = dlattry;
1941  endface = 4;
1942  }
1943  }
1944 
1945  // Left or right end face
1946  if( abs(dlat2end) > abs(dlat_endface) )
1947  {
1948  dlat2end = dlat_endface;
1949  if( za_start >= 0 )
1950  { endface = 3; }
1951  else
1952  { endface = 1; }
1953  }
1954 
1955  assert( endface );
1956 
1957  // Check there is a tangent point inside the grid cell
1958  if( abs_za_start > 90 && ( abs_za_start - abs(dlat2end) ) <= 90 )
1959  {
1960  tanpoint = 1;
1961 
1962  // Check if the tangent point is closer than the end point
1963  if( ( abs_za_start - 90 ) < abs( dlat2end ) )
1964  {
1965  endface = 0;
1966  dlat2end = sign(za_start) * ( abs_za_start - 90 );
1967  }
1968  }
1969  else
1970  { tanpoint = 0; }
1971 
1972 
1973  // Calculate radius for end point.
1974  // To obtain best possible accuracy it is calculated to match found end face,
1975  // and not based on dlat2end.
1976  //
1977  double r_end = -1;
1978  //
1979  if( tanpoint )
1980  { r_end = geompath_r_at_za( ppc, sign(za_start) * 90 ); }
1981  else if( endface == 1 )
1982  { r_end = geompath_r_at_lat( ppc, lat_start, za_start, lat1 ); }
1983  else if( endface == 2 )
1984  { r_end = r1a + c2 * ( dlat_left + dlat2end ); }
1985  else if( endface == 3 )
1986  { r_end = geompath_r_at_lat( ppc, lat_start, za_start, lat3 ); }
1987  else if( endface == 4 )
1988  { r_end = r1b + c4 * ( dlat_left + dlat2end ); }
1989  else if( endface == 7 )
1990  { r_end = rsurface1 + csurface * ( dlat_left + dlat2end ); }
1991 
1992  // Fill the return vectors
1993  //
1994  geompath_from_r1_to_r2( r_v, lat_v, za_v, lstep, ppc, r_start, lat_start,
1995  za_start, r_end, lmax );
1996 
1997  // Force end latitude and zenith angle to be exact as possible
1998  if( tanpoint )
1999  {
2000  if( za_start >= 0 )
2001  { za_v[za_v.nelem()-1] = 90; }
2002  else
2003  { za_v[za_v.nelem()-1] = -90; }
2004  }
2005  //
2006  if( endface == 1 )
2007  { lat_v[lat_v.nelem()-1] = lat1; }
2008  else if( endface == 3 )
2009  { lat_v[lat_v.nelem()-1] = lat3; }
2010  else
2011  { lat_v[lat_v.nelem()-1] = lat_start + dlat2end; }
2012 }
2013 
2014 
2015 
2017 
2070  Vector& r_v,
2071  Vector& lat_v,
2072  Vector& lon_v,
2073  Vector& za_v,
2074  Vector& aa_v,
2075  double& lstep,
2076  Index& endface,
2077  Index& tanpoint,
2078  const double& r_start0,
2079  const double& lat_start0,
2080  const double& lon_start0,
2081  const double& za_start,
2082  const double& aa_start,
2083  const double& ppc,
2084  const double& lmax,
2085  const double& lat1,
2086  const double& lat3,
2087  const double& lon5,
2088  const double& lon6,
2089  const double& r15a,
2090  const double& r35a,
2091  const double& r36a,
2092  const double& r16a,
2093  const double& r15b,
2094  const double& r35b,
2095  const double& r36b,
2096  const double& r16b,
2097  const double& rsurface15,
2098  const double& rsurface35,
2099  const double& rsurface36,
2100  const double& rsurface16 )
2101 {
2102  double r_start = r_start0;
2103  double lat_start = lat_start0;
2104  double lon_start = lon_start0;
2105 
2106  // Assert latitude and longitude
2107  assert( lat_start >= lat1 - LATLONTOL );
2108  assert( lat_start <= lat3 + LATLONTOL );
2109  assert( !( abs( lat_start) < 90 && lon_start < lon5 - LATLONTOL ) );
2110  assert( !( abs( lat_start) < 90 && lon_start > lon6 + LATLONTOL ) );
2111 
2112  // Shift latitude and longitude if outside
2113  if( lat_start < lat1 )
2114  { lat_start = lat1; }
2115  else if( lat_start > lat3 )
2116  { lat_start = lat3; }
2117  if( lon_start < lon5 )
2118  { lon_start = lon5; }
2119  else if( lon_start > lon6 )
2120  { lon_start = lon6; }
2121 
2122  // Radius of lower and upper pressure level at the start position
2123  double rlow = rsurf_at_latlon( lat1, lat3, lon5, lon6,
2124  r15a, r35a, r36a, r16a, lat_start, lon_start );
2125  double rupp = rsurf_at_latlon( lat1, lat3, lon5, lon6,
2126  r15b, r35b, r36b, r16b, lat_start, lon_start );
2127 
2128  // Assert radius (some extra tolerance is needed for radius)
2129  assert( r_start >= rlow - RTOL );
2130  assert( r_start <= rupp + RTOL );
2131 
2132  // Shift radius if outside
2133  if( r_start < rlow )
2134  { r_start = rlow; }
2135  else if( r_start > rupp )
2136  { r_start = rupp; }
2137 
2138  // Position and LOS in cartesian coordinates
2139  double x, y, z, dx, dy, dz;
2140  poslos2cart( x, y, z, dx, dy, dz, r_start, lat_start, lon_start,
2141  za_start, aa_start );
2142 
2143  // Determine the position of the end point
2144  //
2145  endface = 0;
2146  tanpoint = 0;
2147  //
2148  double r_end, lat_end, lon_end, l_end;
2149 
2150 
2151  // Zenith and nadir looking are handled as special cases
2152 
2153  // Zenith looking
2154  if( za_start < ANGTOL )
2155  {
2156  r_end = rupp;
2157  lat_end = lat_start;
2158  lon_end = lon_start;
2159  l_end = rupp - r_start;
2160  endface = 4;
2161  }
2162 
2163  // Nadir looking
2164  else if( za_start > 180-ANGTOL )
2165  {
2166  const double rsurface = rsurf_at_latlon( lat1, lat3, lon5, lon6,
2167  rsurface15, rsurface35, rsurface36, rsurface16,
2168  lat_start, lon_start );
2169 
2170  if( rlow > rsurface )
2171  {
2172  r_end = rlow;
2173  endface = 2;
2174  }
2175  else
2176  {
2177  r_end = rsurface;
2178  endface = 7;
2179  }
2180  lat_end = lat_start;
2181  lon_end = lon_start;
2182  l_end = r_start - r_end;
2183  }
2184 
2185  // Check faces in number order
2186  else
2187  {
2188  // Calculate correction terms for the position to compensate for
2189  // numerical inaccuracy.
2190  //
2191  double r_corr, lat_corr, lon_corr;
2192  //
2193  cart2sph( r_corr, lat_corr, lon_corr, x, y, z );
2194  //
2195  r_corr -= r_start;
2196  lat_corr -= lat_start;
2197  lon_corr -= lon_start;
2198 
2199  // The end point is found by testing different step lengths until the
2200  // step length has been determined by a precision of *l_acc*.
2201  //
2202  // The first step is to found a length that goes outside the grid cell,
2203  // to find an upper limit. The lower limit is set to 0. The upper
2204  // limit is found be testing lengths of 100, 1000 ... m.
2205  // The search algorith is bisection, the next length to test is the
2206  // mean value of the minimum and maximum length limits.
2207 
2208  l_end = 100;
2209 
2210  double l_acc = 1e-3;
2211  double l_in = 0, l_out = l_end;
2212  bool ready = false, startup = true;
2213  double abs_aa = abs( aa_start );
2214 
2215  double l_tan = 99e6;
2216  if( za_start > 90 )
2217  { l_tan = sqrt( r_start*r_start - ppc*ppc ); }
2218 
2219  bool do_surface = false;
2220  if( rsurface15+RTOL >= r15a || rsurface35+RTOL >= r35a ||
2221  rsurface36+RTOL >= r36a || rsurface16+RTOL >= r16a )
2222  { do_surface = true; }
2223 
2224 
2225  while( !ready )
2226  {
2227  cart2sph( r_end, lat_end, lon_end,
2228  x+dx*l_end, y+dy*l_end, z+dz*l_end );
2229  r_end -= r_corr;
2230  lat_end -= lat_corr;
2231  lon_end -= lon_corr;
2232  resolve_lon( lon_end, lon5, lon6 );
2233 
2234  // Special fixes for north-south observations
2235  if( abs( lat_start ) < 90 && ( abs(aa_start) < ANGTOL ||
2236  abs(aa_start) > 180-ANGTOL ) )
2237  { lon_end = lon_start; }
2238 
2239  // Special fixes for west-east observations
2240  if( abs( abs_aa - 90 ) < ANGTOL )
2241  {
2242  if( lat_start == 0 )
2243  { lat_end = 0; }
2244  else if( lat_start > 0 && lat_end > lat_start )
2245  { lat_end = lat_start; }
2246  else if( lat_start < 0 && lat_end < lat_start )
2247  { lat_end = lat_start; }
2248  }
2249 
2250  bool inside = true;
2251 
2252  rlow = rsurf_at_latlon( lat1, lat3, lon5, lon6,
2253  r15a, r35a, r36a, r16a, lat_end, lon_end );
2254  rupp = rsurf_at_latlon( lat1, lat3, lon5, lon6,
2255  r15b, r35b, r36b, r16b, lat_end, lon_end );
2256 
2257  if( do_surface )
2258  {
2259  const double r_surface =
2260  rsurf_at_latlon( lat1, lat3, lon5, lon6,
2261  rsurface15, rsurface35, rsurface36, rsurface16,
2262  lat_end, lon_end );
2263 
2264  if( r_surface+RTOL >= rlow && r_end <= r_surface+RTOL )
2265  { inside = false; endface = 7; }
2266  }
2267 
2268  if( inside )
2269  {
2270  if( lat_end < lat1 )
2271  { inside = false; endface = 1; }
2272  else if( lat_end > lat3 )
2273  { inside = false; endface = 3; }
2274  else if( lon_end < lon5 )
2275  { inside = false; endface = 5; }
2276  else if( lon_end > lon6 )
2277  { inside = false; endface = 6; }
2278  else if( r_end < rlow )
2279  { inside = false; endface = 2; }
2280  else if( r_end > rupp )
2281  { inside = false; endface = 4; }
2282  }
2283 
2284  if( startup )
2285  {
2286  if( inside )
2287  {
2288  if( l_end < l_tan && l_end*10 > l_tan )
2289  { l_end = l_tan; }
2290  else
2291  { l_end *= 10; }
2292  }
2293  else
2294  {
2295  l_out = l_end;
2296  l_end = ( l_out + l_in ) / 2;
2297  startup = false;
2298  }
2299  }
2300  else
2301  {
2302  if( inside )
2303  { l_in = l_end; }
2304  else
2305  { l_out = l_end; }
2306 
2307  if( ( l_out - l_in ) < l_acc )
2308  { ready = true; }
2309  else
2310  { l_end = ( l_out + l_in ) / 2; }
2311  }
2312  }
2313 
2314  // Now when we are ready, we remove the correction terms. Otherwise
2315  // we can end up in an infinite loop if the step length is smaller
2316  // than the correction.
2317  r_end += r_corr;
2318  lat_end += lat_corr;
2319  lon_end += lon_corr;
2320  resolve_lon( lon_end, lon5, lon6 );
2321 
2322  // Set the relevant coordinate to be consistent with found endface.
2323  //
2324  if( endface == 1 )
2325  { lat_end = lat1; }
2326  else if( endface == 2 )
2327  { r_end = rsurf_at_latlon( lat1, lat3, lon5, lon6, r15a, r35a, r36a,
2328  r16a, lat_end, lon_end ); }
2329  else if( endface == 3 )
2330  { lat_end = lat3; }
2331  else if( endface == 4 )
2332  { r_end = rsurf_at_latlon( lat1, lat3, lon5, lon6, r15b, r35b, r36b,
2333  r16b, lat_end, lon_end ); }
2334  else if( endface == 5 )
2335  { lon_end = lon5; }
2336  else if( endface == 6 )
2337  { lon_end = lon6; }
2338  else if( endface == 7 )
2339  { r_end = rsurf_at_latlon( lat1, lat3, lon5, lon6, rsurface15,
2340  rsurface35, rsurface36, rsurface16, lat_end, lon_end ); }
2341 
2342  //--- Tangent point?
2343  //
2344  if( za_start > 90 )
2345  {
2346  if( l_tan < l_end )
2347  {
2348  geompath_tanpos_3d( r_end, lat_end, lon_end, l_end,
2349  r_start, lat_start, lon_start,
2350  za_start, aa_start, ppc );
2351  resolve_lon( lon_end, lon5, lon6 );
2352  endface = 0;
2353  tanpoint = 1;
2354  }
2355  else if( l_tan == l_end )
2356  { tanpoint = 1; }
2357  }
2358  }
2359 
2360  //--- Create return vectors
2361  //
2362  Index n = 1;
2363  //
2364  if( lmax > 0 )
2365  {
2366  n = Index( ceil( abs( l_end / lmax ) ) );
2367  if( n < 1 )
2368  { n = 1; }
2369  }
2370  //
2371  r_v.resize( n+1 );
2372  lat_v.resize( n+1 );
2373  lon_v.resize( n+1 );
2374  za_v.resize( n+1 );
2375  aa_v.resize( n+1 );
2376  //
2377  r_v[0] = r_start;
2378  lat_v[0] = lat_start;
2379  lon_v[0] = lon_start;
2380  za_v[0] = za_start;
2381  aa_v[0] = aa_start;
2382  //
2383  double ldouble = l_end / (double)n;
2384  lstep = ldouble;
2385  //
2386  for( Index j=1; j<=n; j++ )
2387  {
2388  const double l = ldouble * (double)j;
2389 
2390  // We seperate here float/double to avoid unnecessary copying for
2391  // Numeric=double. A solution like this is needed to avoid compiler
2392  // warnings.
2393 #ifdef USE_DOUBLE
2394  cart2poslos( r_v[j], lat_v[j], lon_v[j], za_v[j], aa_v[j],
2395  x+dx*l, y+dy*l, z+dz*l, dx, dy, dz );
2396 #else
2397  double rtmp, lattmp, lontmp, zatmp, aatmp;
2398  cart2poslos( rtmp, lattmp, lontmp, zatmp, aatmp,
2399  x+dx*l, y+dy*l, z+dz*l, dx, dy, dz );
2400  r_v[j] = rtmp;
2401  lat_v[j] = lattmp;
2402  lon_v[j] = lontmp;
2403  za_v[j] = zatmp;
2404  aa_v[j] = aatmp;
2405 #endif
2406  }
2407 
2408 
2409  //--- Set last point especially, which should improve the accuracy
2410  r_v[n] = r_end;
2411  lat_v[n] = lat_end;
2412  lon_v[n] = lon_end;
2413 
2414  //--- Set last zenith angle to be as accurate as possible
2415  if( za_start < ANGTOL || za_start > 180-ANGTOL )
2416  { za_v[n] = za_start; }
2417  else if( tanpoint )
2418  { za_v[n] = 90; }
2419  else
2420  { za_v[n] = geompath_za_at_r( ppc, za_start, r_v[n] ); }
2421 
2422  //--- Set last azimuth angle to be as accurate as possible for
2423  // zenith and nadir observations
2424  if( abs( lat_start ) < 90 &&
2425  ( abs(aa_start) < ANGTOL || abs( aa_start) > 180-ANGTOL ) )
2426  {
2427  aa_v[n] = aa_start;
2428  lon_v[n] = lon_start;
2429  }
2430 
2431  // Shall lon values be shifted (value 0 and n+1 are already OK)?
2432  for( Index j=1; j<n; j++ )
2433  { resolve_lon( lon_v[j], lon5, lon6 ); }
2434 }
2435 
2436 
2437 
2438 
2439 
2440 /*===========================================================================
2441  === Functions operating on the Ppath structure
2442  ===========================================================================*/
2443 
2445 
2462  Ppath& ppath,
2463  const Index& atmosphere_dim,
2464  const Index& np )
2465 {
2466  assert( atmosphere_dim >= 1 );
2467  assert( atmosphere_dim <= 3 );
2468 
2469  ppath.dim = atmosphere_dim;
2470  ppath.np = np;
2471  ppath.constant = -1;
2472  if( atmosphere_dim < 3 )
2473  {
2474  ppath.pos.resize( np, 2 );
2475  ppath.los.resize( np, 1 );
2476  }
2477  else
2478  {
2479  ppath.pos.resize( np, atmosphere_dim );
2480  ppath.los.resize( np, 2 );
2481  ppath.gp_lon.resize( np );
2482  }
2483  ppath.gp_p.resize( np );
2484  if( atmosphere_dim >= 2 )
2485  { ppath.gp_lat.resize( np ); }
2486  ppath.z.resize( np );
2487  if( np > 0 )
2488  { ppath.l_step.resize( np-1 ); }
2489  else
2490  { ppath.l_step.resize( 0 ); }
2491  ppath_set_background( ppath, 0 );
2492  ppath.tan_pos.resize(0);
2493  ppath.geom_tan_pos.resize(0);
2494 }
2495 
2496 
2497 
2498 
2500 
2520  Ppath& ppath,
2521  const Index& case_nr )
2522 {
2523  switch ( case_nr )
2524  {
2525  case 0:
2526  ppath.background = "";
2527  break;
2528  case 1:
2529  ppath.background = "space";
2530  break;
2531  case 2:
2532  ppath.background = "surface";
2533  break;
2534  case 3:
2535  ppath.background = "cloud box level";
2536  break;
2537  case 4:
2538  ppath.background = "cloud box interior";
2539  break;
2540  default:
2541  ostringstream os;
2542  os << "Case number " << case_nr << " is not defined.";
2543  throw runtime_error(os.str());
2544  }
2545 }
2546 
2547 
2548 
2550 
2562 {
2563  if( ppath.background == "" )
2564  { return 0; }
2565  else if( ppath.background == "space" )
2566  { return 1; }
2567  else if( ppath.background == "surface" )
2568  { return 2; }
2569  else if( ppath.background == "cloud box level" )
2570  { return 3; }
2571  else if( ppath.background == "cloud box interior" )
2572  { return 4; }
2573  else
2574  {
2575  ostringstream os;
2576  os << "The string " << ppath.background
2577  << " is not a valid background case.";
2578  throw runtime_error(os.str());
2579  }
2580 }
2581 
2582 
2583 
2585 
2599  Ppath& ppath1,
2600  const Ppath& ppath2 )
2601 {
2602  assert( ppath1.np >= ppath2.np );
2603 
2604  // The field np shall not be copied !
2605 
2606  ppath1.dim = ppath2.dim;
2607  ppath1.constant = ppath2.constant;
2608  ppath1.background = ppath2.background;
2609 
2610  ppath1.pos(Range(0,ppath2.np),joker) = ppath2.pos;
2611  ppath1.los(Range(0,ppath2.np),joker) = ppath2.los;
2612  ppath1.z[Range(0,ppath2.np)] = ppath2.z;
2613  ppath1.l_step[Range(0,ppath2.np-1)] = ppath2.l_step;
2614 
2615  for( Index i=0; i<ppath2.np; i++ )
2616  {
2617  gridpos_copy( ppath1.gp_p[i], ppath2.gp_p[i] );
2618 
2619  if( ppath1.dim >= 2 )
2620  { gridpos_copy( ppath1.gp_lat[i], ppath2.gp_lat[i] ); }
2621 
2622  if( ppath1.dim == 3 )
2623  { gridpos_copy( ppath1.gp_lon[i], ppath2.gp_lon[i] ); }
2624  }
2625 
2626  if( ppath2.tan_pos.nelem() )
2627  { ppath1.tan_pos = ppath2.tan_pos; }
2628  if( ppath2.geom_tan_pos.nelem() )
2629  { ppath1.geom_tan_pos = ppath2.geom_tan_pos; }
2630 }
2631 
2632 
2633 
2635 
2653  Ppath& ppath1,
2654  const Ppath& ppath2 )
2655 {
2656  const Index n1 = ppath1.np;
2657  const Index n2 = ppath2.np;
2658 
2659  Ppath ppath;
2660  ppath_init_structure( ppath, ppath1.dim, n1 );
2661  ppath_copy( ppath, ppath1 );
2662 
2663  ppath_init_structure( ppath1, ppath1.dim, n1 + n2 - 1 );
2664  ppath_copy( ppath1, ppath );
2665 
2666  // Append data from ppath2
2667  Index i1;
2668  for( Index i=1; i<n2; i++ )
2669  {
2670  i1 = n1 + i - 1;
2671 
2672  ppath1.pos(i1,0) = ppath2.pos(i,0);
2673  ppath1.pos(i1,1) = ppath2.pos(i,1);
2674  ppath1.los(i1,0) = ppath2.los(i,0);
2675  ppath1.z[i1] = ppath2.z[i];
2676  gridpos_copy( ppath1.gp_p[i1], ppath2.gp_p[i] );
2677 
2678  if( ppath1.dim >= 2 )
2679  { gridpos_copy( ppath1.gp_lat[i1], ppath2.gp_lat[i] ); }
2680 
2681  if( ppath1.dim == 3 )
2682  {
2683  ppath1.pos(i1,2) = ppath2.pos(i,2);
2684  ppath1.los(i1,1) = ppath2.los(i,1);
2685  gridpos_copy( ppath1.gp_lon[i1], ppath2.gp_lon[i] );
2686  }
2687 
2688  ppath1.l_step[i1-1] = ppath2.l_step[i-1];
2689  }
2690 
2691  if( ppath_what_background( ppath2 ) )
2692  { ppath1.background = ppath2.background; }
2693 }
2694 
2695 
2696 
2698 
2723  Ppath& ppath,
2724  ConstVectorView r,
2725  ConstVectorView lat,
2726  ConstVectorView za,
2727  ConstVectorView lstep,
2728  const double& r_geoid,
2729  ConstVectorView z_field,
2730  const Index& ip )
2731 {
2732  // Help variables that are common for all points.
2733  const double r1 = r_geoid + z_field[ip];
2734  const double dr = z_field[ip+1] - z_field[ip];
2735 
2736  for( Index i=0; i<r.nelem(); i++ )
2737  {
2738  ppath.pos(i,0) = r[i];
2739  ppath.pos(i,1) = lat[i];
2740  ppath.los(i,0) = za[i];
2741 
2742  ppath.gp_p[i].idx = ip;
2743  ppath.gp_p[i].fd[0] = ( r[i] - r1 ) / dr;
2744  ppath.gp_p[i].fd[1] = 1 - ppath.gp_p[i].fd[0];
2745  gridpos_check_fd( ppath.gp_p[i] );
2746 
2747  ppath.z[i] = r[i] - r_geoid;
2748 
2749  if( i > 0 )
2750  { ppath.l_step[i-1] = lstep[i-1]; }
2751  }
2752 }
2753 
2754 
2755 
2757 
2783  Ppath& ppath,
2784  ConstVectorView r,
2785  ConstVectorView lat,
2786  ConstVectorView za,
2787  const double& lstep,
2788  ConstVectorView r_geoid,
2789  ConstMatrixView z_field,
2790  ConstVectorView lat_grid,
2791  const Index& ip,
2792  const Index& ilat )
2793 {
2794  // Help variables that are common for all points.
2795  const double dlat = lat_grid[ilat+1] - lat_grid[ilat];
2796  const double r1low = r_geoid[ilat] + z_field(ip,ilat);
2797  const double drlow = r_geoid[ilat+1] + z_field(ip,ilat+1) -r1low;
2798  const double r1upp = r_geoid[ilat] + z_field(ip+1,ilat);
2799  const double drupp = r_geoid[ilat+1] + z_field(ip+1,ilat+1) - r1upp;
2800  const double z1low = z_field(ip,ilat);
2801  const double dzlow = z_field(ip,ilat+1) -z1low;
2802  const double z1upp = z_field(ip+1,ilat);
2803  const double dzupp = z_field(ip+1,ilat+1) - z1upp;
2804 
2805  for( Index i=0; i<r.nelem(); i++ )
2806  {
2807  ppath.pos(i,0) = r[i];
2808  ppath.pos(i,1) = lat[i];
2809  ppath.los(i,0) = za[i];
2810 
2811  // Weigt in the latitude direction
2812  double w = ( lat[i] - lat_grid[ilat] ) / dlat;
2813 
2814  // Radius of lower and upper face at present latitude
2815  const double rlow = r1low + w * drlow;
2816  const double rupp = r1upp + w * drupp;
2817 
2818  // Geometrical altitude of lower and upper face at present latitude
2819  const double zlow = z1low + w * dzlow;
2820  const double zupp = z1upp + w * dzupp;
2821 
2822  ppath.gp_p[i].idx = ip;
2823  ppath.gp_p[i].fd[0] = ( r[i] - rlow ) / ( rupp - rlow );
2824  ppath.gp_p[i].fd[1] = 1 - ppath.gp_p[i].fd[0];
2825  gridpos_check_fd( ppath.gp_p[i] );
2826 
2827  ppath.z[i] = zlow + ppath.gp_p[i].fd[0] * ( zupp -zlow );
2828 
2829  ppath.gp_lat[i].idx = ilat;
2830  ppath.gp_lat[i].fd[0] = ( lat[i] - lat_grid[ilat] ) / dlat;
2831  ppath.gp_lat[i].fd[1] = 1 - ppath.gp_lat[i].fd[0];
2832  gridpos_check_fd( ppath.gp_lat[i] );
2833 
2834  if( i > 0 )
2835  { ppath.l_step[i-1] = lstep; }
2836  }
2837 }
2838 
2839 
2840 
2842 
2872  Ppath& ppath,
2873  ConstVectorView r,
2874  ConstVectorView lat,
2875  ConstVectorView lon,
2876  ConstVectorView za,
2877  ConstVectorView aa,
2878  const double& lstep,
2879  ConstMatrixView r_geoid,
2880  ConstTensor3View z_field,
2881  ConstVectorView lat_grid,
2882  ConstVectorView lon_grid,
2883  const Index& ip,
2884  const Index& ilat,
2885  const Index& ilon )
2886 {
2887  // Help variables that are common for all points.
2888  const double lat1 = lat_grid[ilat];
2889  const double lat3 = lat_grid[ilat+1];
2890  const double lon5 = lon_grid[ilon];
2891  const double lon6 = lon_grid[ilon+1];
2892  const double r15a = r_geoid(ilat,ilon) + z_field(ip,ilat,ilon);
2893  const double r35a = r_geoid(ilat+1,ilon) + z_field(ip,ilat+1,ilon);
2894  const double r36a = r_geoid(ilat+1,ilon+1) + z_field(ip,ilat+1,ilon+1);
2895  const double r16a = r_geoid(ilat,ilon+1) + z_field(ip,ilat,ilon+1);
2896  const double r15b = r_geoid(ilat,ilon) + z_field(ip+1,ilat,ilon);
2897  const double r35b = r_geoid(ilat+1,ilon) + z_field(ip+1,ilat+1,ilon);
2898  const double r36b = r_geoid(ilat+1,ilon+1) + z_field(ip+1,ilat+1,ilon+1);
2899  const double r16b = r_geoid(ilat,ilon+1) + z_field(ip+1,ilat,ilon+1);
2900  const double rg15 = r_geoid(ilat,ilon);
2901  const double rg35 = r_geoid(ilat+1,ilon);
2902  const double rg36 = r_geoid(ilat+1,ilon+1);
2903  const double rg16 = r_geoid(ilat,ilon+1);
2904  const double dlat = lat3 - lat1;
2905  const double dlon = lon6 - lon5;
2906 
2907  for( Index i=0; i<r.nelem(); i++ )
2908  {
2909  // Radius of pressure levels at present lat and lon
2910  const double rlow = rsurf_at_latlon( lat1, lat3, lon5, lon6,
2911  r15a, r35a, r36a, r16a, lat[i], lon[i] );
2912  const double rupp = rsurf_at_latlon( lat1, lat3, lon5, lon6,
2913  r15b, r35b, r36b, r16b, lat[i], lon[i] );
2914 
2915  // Position and LOS.
2916  ppath.pos(i,0) = r[i];
2917  ppath.pos(i,1) = lat[i];
2918  ppath.pos(i,2) = lon[i];
2919  ppath.los(i,0) = za[i];
2920  ppath.los(i,1) = aa[i];
2921 
2922  // Pressure grid index
2923  ppath.gp_p[i].idx = ip;
2924  ppath.gp_p[i].fd[0] = ( ppath.pos(i,0) - rlow ) / ( rupp - rlow );
2925  ppath.gp_p[i].fd[1] = 1 - ppath.gp_p[i].fd[0];
2926  gridpos_check_fd( ppath.gp_p[i] );
2927 
2928  // Geometrical altitude
2929  const double rgeoid = rsurf_at_latlon( lat1, lat3, lon5, lon6,
2930  rg15, rg35, rg36, rg16, lat[i], lon[i] );
2931  const double zlow = rlow - rgeoid;
2932  const double zupp = rupp - rgeoid;
2933  //
2934  ppath.z[i] = zlow + ppath.gp_p[i].fd[0] * ( zupp -zlow );
2935 
2936  // Latitude grid index
2937  ppath.gp_lat[i].idx = ilat;
2938  ppath.gp_lat[i].fd[0] = ( lat[i] - lat1 ) / dlat;
2939  ppath.gp_lat[i].fd[1] = 1 - ppath.gp_lat[i].fd[0];
2940  gridpos_check_fd( ppath.gp_lat[i] );
2941 
2942  // Longitude grid index
2943  //
2944  // The longitude is undefined at the poles. The grid index is set to
2945  // the start point.
2946  //
2947  if( abs( lat[i] ) < 90 )
2948  {
2949  ppath.gp_lon[i].idx = ilon;
2950  ppath.gp_lon[i].fd[0] = ( lon[i] - lon5 ) / dlon;
2951  ppath.gp_lon[i].fd[1] = 1 - ppath.gp_lon[i].fd[0];
2952  gridpos_check_fd( ppath.gp_lon[i] );
2953  }
2954  else
2955  {
2956  ppath.gp_lon[i].idx = 0;
2957  ppath.gp_lon[i].fd[0] = 0;
2958  ppath.gp_lon[i].fd[1] = 1;
2959  }
2960 
2961  if( i > 0 )
2962  { ppath.l_step[i-1] = lstep; }
2963  }
2964 }
2965 
2966 
2967 
2968 
2969 
2970 /*===========================================================================
2971  === Functions related to propagation paths with refraction
2972  ===========================================================================*/
2973 
2975 
2989  const double& r,
2990  const double& za,
2991  const double& refr_index )
2992 {
2993  assert( r > 0 );
2994  assert( abs(za) <= 180 );
2995 
2996  return r * refr_index * sin( DEG2RAD * abs(za) );
2997 }
2998 
2999 
3000 
3001 
3002 
3003 /*===========================================================================
3004  === Help functions for the *ppath_step* functions found below
3005  === These functions are mainly pieces of code that are common for at least
3006  === two functions (or two places in some function) and for this reason
3007  === the headers are not complete.
3008  ===========================================================================*/
3009 
3010 
3012 
3024  double& r_start,
3025  double& lat_start,
3026  double& za_start,
3027  Index& ip,
3028  const Ppath& ppath )
3029 {
3030  // Number of points in the incoming ppath
3031  const Index imax = ppath.np - 1;
3032 
3033  // Extract starting radius, zenith angle and latitude
3034  r_start = ppath.pos(imax,0);
3035  lat_start = ppath.pos(imax,1);
3036  za_start = ppath.los(imax,0);
3037 
3038  // Determine index of the pressure level being the lower limit for the
3039  // grid range of interest.
3040  //
3041  ip = gridpos2gridrange( ppath.gp_p[imax], za_start<=90 );
3042 }
3043 
3044 
3045 
3047 
3059  Ppath& ppath,
3060  ConstVectorView r_v,
3061  ConstVectorView lat_v,
3062  ConstVectorView za_v,
3063  const double& lstep,
3064  ConstVectorView z_field,
3065  const double& r_geoid,
3066  const Index& ip,
3067  const Index& endface,
3068  const Index& tanpoint,
3069  const double& ppc )
3070 {
3071  // Number of path points
3072  const Index np = r_v.nelem();
3073 
3074  // Re-allocate ppath for return results and fill the structure
3075  //
3076  ppath_init_structure( ppath, 1, np );
3077  //
3078  ppath.constant = ppc;
3079  //
3080  ppath_fill_1d( ppath, r_v, lat_v, za_v, Vector(np-1,lstep), r_geoid,
3081  z_field, ip );
3082 
3083  gridpos_check_fd( ppath.gp_p[np-1] );
3084 
3085  //--- End point is the surface
3086  if( endface == 7 )
3087  { ppath_set_background( ppath, 2 ); }
3088 
3089  //--- End point is a tangent point
3090  else if( tanpoint )
3091  {
3092  ppath.tan_pos.resize(2);
3093  ppath.tan_pos[0] = r_v[np-1];
3094  ppath.tan_pos[1] = lat_v[np-1];
3095  }
3096 
3097  //--- End point is on top of a pressure level
3098  else
3099  {
3100  gridpos_force_end_fd( ppath.gp_p[np-1], z_field.nelem() );
3101  }
3102 }
3103 
3104 
3105 
3107 
3119  double& r_start,
3120  double& lat_start,
3121  double& za_start,
3122  Index& ip,
3123  Index& ilat,
3124  double& lat1,
3125  double& lat3,
3126  double& r1a,
3127  double& r3a,
3128  double& r3b,
3129  double& r1b,
3130  double& rsurface1,
3131  double& rsurface3,
3132  Ppath& ppath,
3133  ConstVectorView lat_grid,
3134  ConstMatrixView z_field,
3135  ConstVectorView r_geoid,
3136  ConstVectorView z_surface
3137  )
3138 {
3139  // Number of points in the incoming ppath
3140  const Index imax = ppath.np - 1;
3141 
3142  // Extract starting radius, zenith angle and latitude
3143  r_start = ppath.pos(imax,0);
3144  lat_start = ppath.pos(imax,1);
3145  za_start = ppath.los(imax,0);
3146 
3147  // Determine interesting latitude grid range and latitude end points of
3148  // the range.
3149  //
3150  ilat = gridpos2gridrange( ppath.gp_lat[imax], za_start >= 0 );
3151  //
3152  lat1 = lat_grid[ilat];
3153  lat3 = lat_grid[ilat+1];
3154 
3155  // Determine interesting pressure grid range. Do this first assuming that
3156  // the pressure levels are not tilted (that is, abs(za_start<=90) always
3157  // mean upward observation).
3158  // Set radius for the corners of the grid cell and the radial slope of
3159  // pressure level limits of the grid cell to match the found ip.
3160  //
3161  ip = gridpos2gridrange( ppath.gp_p[imax], abs(za_start) <= 90);
3162  //
3163  r1a = r_geoid[ilat] + z_field(ip,ilat); // lower-left
3164  r3a = r_geoid[ilat+1] + z_field(ip,ilat+1); // lower-right
3165  r3b = r_geoid[ilat+1] + z_field(ip+1,ilat+1); // upper-right
3166  r1b = r_geoid[ilat] + z_field(ip+1,ilat); // upper-left
3167 
3168  // This part is a fix to catch start postions on top of a pressure level
3169  // that does not have an end fractional distance for the first step.
3170  {
3171  // Radius of lower and upper pressure level at the start position
3172  const double rlow = rsurf_at_lat( lat1, lat3, r1a, r3a, lat_start );
3173  const double rupp = rsurf_at_lat( lat1, lat3, r1b, r3b, lat_start );
3174  if( abs(r_start-rlow) < RTOL || abs(r_start-rupp) < RTOL )
3175  { gridpos_force_end_fd( ppath.gp_p[imax], z_field.nrows() ); }
3176  }
3177 
3178  // Slopes of pressure levels
3179  double c2 = plevel_slope_2d( lat1, lat3, r1a, r3a );
3180  double c4 = plevel_slope_2d( lat1, lat3, r1b, r3b );
3181 
3182  // Check if the LOS zenith angle happen to be between 90 and the zenith angle
3183  // of the pressure level (that is, 90 + tilt of pressure level), and in
3184  // that case if ip must be changed. This check is only needed when the
3185  // start point is on a pressure level.
3186  //
3187  if( is_gridpos_at_index_i( ppath.gp_p[imax], ip ) )
3188  {
3189  double tilt = plevel_angletilt( r_start, c2 );
3190 
3191  if( is_los_downwards( za_start, tilt ) )
3192  {
3193  ip--;
3194  r1b = r1a; r3b = r3a; c4 = c2;
3195  r1a = r_geoid[ilat] + z_field(ip,ilat);
3196  r3a = r_geoid[ilat+1] + z_field(ip,ilat+1);
3197  c2 = plevel_slope_2d( lat1, lat3, r1a, r3a );
3198  }
3199  }
3200  else if( is_gridpos_at_index_i( ppath.gp_p[imax], ip+1 ) )
3201  {
3202  double tilt = plevel_angletilt( r_start, c4 );
3203 
3204  if( !is_los_downwards( za_start, tilt ) )
3205  {
3206  ip++;
3207  r1a = r1b; r3a = r3b; c2 = c4;
3208  r3b = r_geoid[ilat+1] + z_field(ip+1,ilat+1);
3209  r1b = r_geoid[ilat] + z_field(ip+1,ilat);
3210  c4 = plevel_slope_2d( lat1, lat3, r1b, r3b );
3211  }
3212  }
3213 
3214  // Surface radius at latitude end points
3215  rsurface1 = r_geoid[ilat] + z_surface[ilat];
3216  rsurface3 = r_geoid[ilat+1] + z_surface[ilat+1];
3217 }
3218 
3219 
3220 
3222 
3234  Ppath& ppath,
3235  ConstVectorView r_v,
3236  ConstVectorView lat_v,
3237  ConstVectorView za_v,
3238  const double& lstep,
3239  ConstVectorView lat_grid,
3240  ConstMatrixView z_field,
3241  ConstVectorView r_geoid,
3242  const Index& ip,
3243  const Index& ilat,
3244  const Index& endface,
3245  const Index& tanpoint,
3246  const double& ppc )
3247 {
3248  // Number of path points
3249  const Index np = r_v.nelem();
3250  const Index imax = np-1;
3251 
3252  // Re-allocate ppath for return results and fill the structure
3253  //
3254  ppath_init_structure( ppath, 2, np );
3255  //
3256  ppath.constant = ppc;
3257  //
3258  ppath_fill_2d( ppath, r_v, lat_v, za_v, lstep, r_geoid, z_field, lat_grid,
3259  ip, ilat );
3260 
3261  gridpos_check_fd( ppath.gp_p[imax] );
3262  gridpos_check_fd( ppath.gp_lat[imax] );
3263 
3264  // Do end-face specific tasks
3265  if( endface == 7 )
3266  { ppath_set_background( ppath, 2 ); }
3267  else if( tanpoint )
3268  {
3269  ppath.tan_pos.resize(2);
3270  ppath.tan_pos[0] = r_v[np-1];
3271  ppath.tan_pos[1] = lat_v[np-1];
3272  }
3273 
3274  // Set fractional distance for end point
3275  //
3276  if( endface == 1 || endface == 3 )
3277  { gridpos_force_end_fd( ppath.gp_lat[np-1], lat_grid.nelem() ); }
3278  else if( endface == 2 || endface == 4 )
3279  { gridpos_force_end_fd( ppath.gp_p[np-1], z_field.nrows() ); }
3280 
3281  // Handle cases where exactly a corner is hit, or when slipping outside of
3282  // the grid box due to numerical inaccuarcy
3283  if( ppath.gp_p[imax].fd[0] < 0 || ppath.gp_p[imax].fd[1] < 0 )
3284  {
3285  gridpos_force_end_fd( ppath.gp_p[imax], z_field.nrows() );
3286  }
3287  if( ppath.gp_lat[imax].fd[0] < 0 || ppath.gp_lat[imax].fd[1] < 0 )
3288  {
3289  gridpos_force_end_fd( ppath.gp_lat[imax], lat_grid.nelem() );
3290  }
3291 }
3292 
3293 
3294 
3296 
3308  double& r_start,
3309  double& lat_start,
3310  double& lon_start,
3311  double& za_start,
3312  double& aa_start,
3313  Index& ip,
3314  Index& ilat,
3315  Index& ilon,
3316  double& lat1,
3317  double& lat3,
3318  double& lon5,
3319  double& lon6,
3320  double& r15a,
3321  double& r35a,
3322  double& r36a,
3323  double& r16a,
3324  double& r15b,
3325  double& r35b,
3326  double& r36b,
3327  double& r16b,
3328  double& rsurface15,
3329  double& rsurface35,
3330  double& rsurface36,
3331  double& rsurface16,
3332  Ppath& ppath,
3333  ConstVectorView lat_grid,
3334  ConstVectorView lon_grid,
3335  ConstTensor3View z_field,
3336  ConstMatrixView r_geoid,
3337  ConstMatrixView z_surface )
3338 {
3339  // Index of last point in the incoming ppath
3340  const Index imax = ppath.np - 1;
3341 
3342  // Extract starting radius, zenith angle and latitude
3343  r_start = ppath.pos(imax,0);
3344  lat_start = ppath.pos(imax,1);
3345  lon_start = ppath.pos(imax,2);
3346  za_start = ppath.los(imax,0);
3347  aa_start = ppath.los(imax,1);
3348 
3349  // Number of lat/lon
3350  const Index nlat = lat_grid.nelem();
3351  const Index nlon = lon_grid.nelem();
3352 
3353  // Lower index of lat and lon ranges of interest
3354  //
3355  // The longitude is undefined at the poles and as the azimuth angle
3356  // is defined in other way at the poles.
3357  //
3358  if( lat_start == 90 )
3359  {
3360  ilat = nlat - 2;
3361  GridPos gp_tmp;
3362  gridpos( gp_tmp, lon_grid, aa_start );
3363  if( aa_start < 180 )
3364  { ilon = gridpos2gridrange( gp_tmp, 1 ); }
3365  else
3366  { ilon = gridpos2gridrange( gp_tmp, 0 ); }
3367  }
3368  else if( lat_start == -90 )
3369  {
3370  ilat = 0;
3371  GridPos gp_tmp;
3372  gridpos( gp_tmp, lon_grid, aa_start );
3373  if( aa_start < 180 )
3374  { ilon = gridpos2gridrange( gp_tmp, 1 ); }
3375  else
3376  { ilon = gridpos2gridrange( gp_tmp, 0 ); }
3377  }
3378  else
3379  {
3380  if( lat_start > 0 )
3381  ilat = gridpos2gridrange( ppath.gp_lat[imax], abs( aa_start ) < 90 );
3382  else
3383  ilat = gridpos2gridrange( ppath.gp_lat[imax], abs( aa_start ) <= 90 );
3384  if( lon_start < lon_grid[nlon-1] )
3385  { ilon = gridpos2gridrange( ppath.gp_lon[imax], aa_start >= 0 ); }
3386  else
3387  { ilon = nlon - 2; }
3388  }
3389  //
3390  lat1 = lat_grid[ilat];
3391  lat3 = lat_grid[ilat+1];
3392  lon5 = lon_grid[ilon];
3393  lon6 = lon_grid[ilon+1];
3394 
3395  // Determine interesting pressure grid range. Do this first assuming that
3396  // the pressure levels are not tilted (that is, abs(za_start<=90) always
3397  // mean upward observation).
3398  // Set radius for the corners of the grid cell and the radial slope of
3399  // pressure level limits of the grid cell to match the found ip.
3400  //
3401  ip = gridpos2gridrange( ppath.gp_p[imax], za_start <= 90 );
3402  //
3403  r15a = r_geoid(ilat,ilon) + z_field(ip,ilat,ilon);
3404  r35a = r_geoid(ilat+1,ilon) + z_field(ip,ilat+1,ilon);
3405  r36a = r_geoid(ilat+1,ilon+1) + z_field(ip,ilat+1,ilon+1);
3406  r16a = r_geoid(ilat,ilon+1) + z_field(ip,ilat,ilon+1);
3407  r15b = r_geoid(ilat,ilon) + z_field(ip+1,ilat,ilon);
3408  r35b = r_geoid(ilat+1,ilon) + z_field(ip+1,ilat+1,ilon);
3409  r36b = r_geoid(ilat+1,ilon+1) + z_field(ip+1,ilat+1,ilon+1);
3410  r16b = r_geoid(ilat,ilon+1) + z_field(ip+1,ilat,ilon+1);
3411 
3412  // Check if the LOS zenith angle happen to be between 90 and the zenith angle
3413  // of the pressure level (that is, 90 + tilt of pressure level), and in
3414  // that case if ip must be changed. This check is only needed when the
3415  // start point is on a pressure level.
3416  //
3417  if( fabs(za_start-90) <= PTILTMAX )
3418  {
3419  if( is_gridpos_at_index_i( ppath.gp_p[imax], ip ) )
3420  {
3421  // Slope and angular tilt of lower pressure level
3422  double c2 = plevel_slope_3d( lat1, lat3, lon5, lon6,
3423  r15a, r35a, r36a, r16a, lat_start, lon_start, aa_start );
3424  double tilt = plevel_angletilt( r_start, c2 );
3425  // when tilt < 1sec assume this is a numeric problem and reset tilt to 0)
3426  if( tilt < 2.e-4 )
3427  {
3428  tilt=0.;
3429  }
3430 
3431  if( is_los_downwards( za_start, tilt ) )
3432  {
3433  ip--;
3434  r15b = r15a; r35b = r35a; r36b = r36a; r16b = r16a;
3435  r15a = r_geoid(ilat,ilon) + z_field(ip,ilat,ilon);
3436  r35a = r_geoid(ilat+1,ilon) + z_field(ip,ilat+1,ilon);
3437  r36a = r_geoid(ilat+1,ilon+1) + z_field(ip,ilat+1,ilon+1);
3438  r16a = r_geoid(ilat,ilon+1) + z_field(ip,ilat,ilon+1);
3439  }
3440  }
3441  else if( is_gridpos_at_index_i( ppath.gp_p[imax], ip+1 ) )
3442  {
3443  // Slope and angular tilt of lower pressure level
3444  double c4 = plevel_slope_3d( lat1, lat3 ,lon5, lon6,
3445  r15b, r35b, r36b, r16b, lat_start, lon_start, aa_start );
3446  double tilt = plevel_angletilt( r_start, c4 );
3447 
3448  if( !is_los_downwards( za_start, tilt ) )
3449  {
3450  ip++;
3451  r15a = r15b; r35a = r35b; r36a = r36b; r16a = r16b;
3452  r15b = r_geoid(ilat,ilon) + z_field(ip+1,ilat,ilon);
3453  r35b = r_geoid(ilat+1,ilon) + z_field(ip+1,ilat+1,ilon);
3454  r36b = r_geoid(ilat+1,ilon+1) + z_field(ip+1,ilat+1,ilon+1);
3455  r16b = r_geoid(ilat,ilon+1) + z_field(ip+1,ilat,ilon+1);
3456  }
3457  }
3458  }
3459 
3460  // Surface radius at latitude/longitude corner points
3461  rsurface15 = r_geoid(ilat,ilon) + z_surface(ilat,ilon);
3462  rsurface35 = r_geoid(ilat+1,ilon) + z_surface(ilat+1,ilon);
3463  rsurface36 = r_geoid(ilat+1,ilon+1) + z_surface(ilat+1,ilon+1);
3464  rsurface16 = r_geoid(ilat,ilon+1) + z_surface(ilat,ilon+1);
3465 }
3466 
3467 
3468 
3470 
3482  Ppath& ppath,
3483  ConstVectorView r_v,
3484  ConstVectorView lat_v,
3485  ConstVectorView lon_v,
3486  ConstVectorView za_v,
3487  ConstVectorView aa_v,
3488  const double& lstep,
3489  ConstVectorView lat_grid,
3490  ConstVectorView lon_grid,
3491  ConstTensor3View z_field,
3492  ConstMatrixView r_geoid,
3493  const Index& ip,
3494  const Index& ilat,
3495  const Index& ilon,
3496  const Index& endface,
3497  const Index& tanpoint,
3498  const double& ppc )
3499 {
3500  // Number of path points
3501  const Index np = r_v.nelem();
3502  const Index imax = np-1;
3503 
3504  // Re-allocate ppath for return results and fill the structure
3505  //
3506  ppath_init_structure( ppath, 3, np );
3507  //
3508  ppath.constant = ppc;
3509  //
3510  ppath_fill_3d( ppath, r_v, lat_v, lon_v, za_v, aa_v, lstep,
3511  r_geoid, z_field, lat_grid, lon_grid, ip, ilat, ilon );
3512 
3513  // Remove these? !!!
3514  assert( ppath.gp_p[imax].fd[0] > -0.01 );
3515  assert( ppath.gp_p[imax].fd[0] < 1.01 );
3516  assert( ppath.gp_lat[imax].fd[0] > -0.01 );
3517  assert( ppath.gp_lat[imax].fd[0] < 1.01 );
3518  assert( ppath.gp_lon[imax].fd[0] > -0.01 );
3519  assert( ppath.gp_lon[imax].fd[0] < 1.01 );
3520 
3521  // Do end-face specific tasks
3522  if( endface == 7 )
3523  { ppath_set_background( ppath, 2 ); }
3524  if( tanpoint )
3525  {
3526  ppath.tan_pos.resize(3);
3527  ppath.tan_pos[0] = r_v[imax];
3528  ppath.tan_pos[1] = lat_v[imax];
3529  ppath.tan_pos[2] = lon_v[imax];
3530  }
3531 
3532  // Set fractional distance for end point
3533  //
3534  if( endface == 1 || endface == 3 )
3535  { gridpos_force_end_fd( ppath.gp_lat[imax], lat_grid.nelem() ); }
3536  else if( endface == 2 || endface == 4 )
3537  { gridpos_force_end_fd( ppath.gp_p[imax], z_field.npages() ); }
3538  else if( endface == 5 || endface == 6 )
3539  { gridpos_force_end_fd( ppath.gp_lon[imax], lon_grid.nelem() ); }
3540 
3541  // Handle cases where exactly a corner is hit, or when slipping outside of
3542  // the grid box due to numerical inaccuarcy
3543  if( ppath.gp_p[imax].fd[0] < 0 || ppath.gp_p[imax].fd[1] < 0 )
3544  {
3545  gridpos_force_end_fd( ppath.gp_p[imax], z_field.npages() );
3546  }
3547  if( ppath.gp_lat[imax].fd[0] < 0 || ppath.gp_lat[imax].fd[1] < 0 )
3548  {
3549  gridpos_force_end_fd( ppath.gp_lat[imax], lat_grid.nelem() );
3550  }
3551  if( ppath.gp_lon[imax].fd[0] < 0 || ppath.gp_lon[imax].fd[1] < 0 )
3552  {
3553  gridpos_force_end_fd( ppath.gp_lon[imax], lon_grid.nelem() );
3554  }
3555 }
3556 
3557 
3558 
3560 
3573  Vector& r_v,
3574  Vector& lat_v,
3575  Vector& lon_v,
3576  Vector& za_v,
3577  Vector& aa_v,
3578  double& lstep,
3579  ConstVectorView r_rt,
3580  ConstVectorView lat_rt,
3581  ConstVectorView lon_rt,
3582  ConstVectorView za_rt,
3583  ConstVectorView aa_rt,
3584  ConstVectorView l_rt,
3585  const double& lmax )
3586 {
3587  // Interpolate the radii, zenith angles and latitudes to a set of points
3588  // linearly spaced along the path. If *lmax* <= 0, then only the end points
3589  // shall be kept.
3590  //
3591  const Index nrt = r_rt.nelem();
3592  Index n = 2;
3593  double ltotsum = l_rt.sum();
3594  // Ensure that there are at least two points
3595  if( lmax > 0 )
3596  { n = max (Index( ceil( ltotsum / lmax ) ) + 1, Index(2)); }
3597  //
3598  r_v.resize(n);
3599  lat_v.resize(n);
3600  za_v.resize(n);
3601  //
3602  if( n == 2 )
3603  {
3604  r_v[0] = r_rt[0]; r_v[1] = r_rt[nrt-1];
3605  za_v[0] = za_rt[0]; za_v[1] = za_rt[nrt-1];
3606  lat_v[0] = lat_rt[0]; lat_v[1] = lat_rt[nrt-1];
3607  lstep = ltotsum;
3608  if( lon_rt.nelem() > 0 )
3609  {
3610  lon_v.resize(n); lon_v[0] = lon_rt[0]; lon_v[1] = lon_rt[nrt-1];
3611  aa_v.resize(n); aa_v[0] = aa_rt[0]; aa_v[1] = aa_rt[nrt-1];
3612  }
3613  }
3614  else
3615  {
3616  Vector lsum(nrt), llin(n);
3617  ArrayOfGridPos gp(n);
3618  Matrix itw(n,2);
3619 
3620  lsum[0] = 0;
3621  for( Index i=1; i<nrt; i++ )
3622  { lsum[i] = lsum[i-1] + l_rt[i-1]; }
3623 
3624  nlinspace( llin, 0, ltotsum, n );
3625 
3626  gridpos( gp, lsum, llin );
3627 
3628  interpweights( itw, gp );
3629 
3630  interp( r_v, itw, r_rt, gp );
3631  interp( za_v, itw, za_rt, gp );
3632  interp( lat_v, itw, lat_rt, gp );
3633  lstep = llin[1] - llin[0];
3634 
3635  if( lon_rt.nelem() > 0 )
3636  {
3637  lon_v.resize(n); interp( lon_v, itw, lon_rt, gp );
3638  aa_v.resize(n); interp( aa_v, itw, aa_rt, gp );
3639  }
3640  }
3641 }
3642 
3643 
3644 
3646 
3656  Vector& r_v,
3657  Vector& lat_v,
3658  Vector& za_v,
3659  double& lstep,
3660  const Array<double>& r_array,
3661  const Array<double>& lat_array,
3662  const Array<double>& za_array,
3663  const Array<double>& l_array,
3664  const Index& reversed,
3665  const double& lmax )
3666 {
3667  // Copy arrays to vectors for later interpolation
3668  //
3669  // Number of ray tracing points
3670  const Index nrt=r_array.nelem();
3671  //
3672  Vector r_rt(nrt), lat_rt(nrt), za_rt(nrt), l_rt(nrt-1);
3673  //
3674  if( !reversed )
3675  {
3676  for( Index i=0; i<nrt; i++ )
3677  {
3678  r_rt[i] = r_array[i];
3679  lat_rt[i] = lat_array[i];
3680  za_rt[i] = za_array[i];
3681  if( i < (nrt-1) )
3682  { l_rt[i] = l_array[i]; }
3683  }
3684  }
3685  else
3686  {
3687  for( Index i=0; i<nrt; i++ )
3688  {
3689  const Index i1 = nrt - 1 - i;
3690  r_rt[i] = r_array[i1];
3691  lat_rt[i] = lat_array[0]+ lat_array[nrt-1] - lat_array[i1];
3692  za_rt[i] = 180 - za_array[i1];
3693  if( i < (nrt-1) )
3694  { l_rt[i] = l_array[i1-1]; }
3695  }
3696  }
3697 
3698  // Interpolate the radii, zenith angles and latitudes to a set of points
3699  // linearly spaced along the path. If *lmax* <= 0, then only the end points
3700  //
3701  Vector dummy; // Dummy vector for output variables not used
3702  //
3703  interpolate_raytracing_points( r_v, lat_v, dummy, za_v, dummy, lstep,
3704  r_rt, lat_rt, Vector(0), za_rt, Vector(0), l_rt, lmax );
3705 }
3706 
3707 
3708 
3710 
3718  Vector& r_v,
3719  Vector& lat_v,
3720  Vector& lon_v,
3721  Vector& za_v,
3722  Vector& aa_v,
3723  double& lstep,
3724  const Array<double>& r_array,
3725  const Array<double>& lat_array,
3726  const Array<double>& lon_array,
3727  const Array<double>& za_array,
3728  const Array<double>& aa_array,
3729  const Array<double>& l_array,
3730  const double& lmax )
3731 {
3732  // Copy arrays to vectors for later interpolation
3733  //
3734  // Number of ray tracing points
3735  const Index nrt=r_array.nelem();
3736  //
3737  Vector r_rt(nrt), lat_rt(nrt), lon_rt(nrt);
3738  Vector za_rt(nrt), aa_rt(nrt),l_rt(nrt-1);
3739  //
3740  for( Index i=0; i<nrt; i++ )
3741  {
3742  r_rt[i] = r_array[i];
3743  lat_rt[i] = lat_array[i];
3744  lon_rt[i] = lon_array[i];
3745  za_rt[i] = za_array[i];
3746  aa_rt[i] = aa_array[i];
3747  if( i < (nrt-1) )
3748  { l_rt[i] = l_array[i]; }
3749  }
3750 
3751  // Interpolate the radii, zenith angles and latitudes to a set of points
3752  // linearly spaced along the path. If *lmax* <= 0, then only the end points
3753  //
3754  interpolate_raytracing_points( r_v, lat_v, lon_v, za_v, aa_v, lstep,
3755  r_rt, lat_rt, lon_rt, za_rt, aa_rt, l_rt, lmax );
3756 }
3757 
3758 
3759 
3760 /*===========================================================================
3761  === Core functions for geometrical *ppath_step* functions
3762  ===========================================================================*/
3763 
3765 
3792  Ppath& ppath,
3793  ConstVectorView p_grid,
3794  ConstVectorView z_field,
3795  const double& r_geoid,
3796  const double& z_surface,
3797  const double& lmax )
3798 {
3799  // Starting radius, zenith angle and latitude
3800  double r_start, lat_start, za_start;
3801 
3802  // Index of the pressure level being the lower limit for the
3803  // grid range of interest.
3804  Index ip;
3805 
3806  // Determine the variables defined above, and make asserts of input
3807  ppath_start_1d( r_start, lat_start, za_start, ip, ppath );
3808 
3809  // If the field "constant" is negative, this is the first call of the
3810  // function and the path constant shall be calculated.
3811  double ppc;
3812  if( ppath.constant < 0 )
3813  { ppc = geometrical_ppc( r_start, za_start ); }
3814  else
3815  { ppc = ppath.constant; }
3816 
3817 
3818  // The path is determined by another function. Determine some variables
3819  // needed bý that function and call the function.
3820  //
3821  // Vars to hold found path points, path step length and coding for end face
3822  Vector r_v, lat_v, za_v;
3823  double lstep;
3824  Index endface, tanpoint;
3825  //
3826  do_gridrange_1d( r_v, lat_v, za_v, lstep, endface, tanpoint,
3827  r_start, lat_start, za_start, ppc, lmax,
3828  r_geoid+z_field[ip], r_geoid+z_field[ip+1], r_geoid+z_surface );
3829 
3830 
3831  // Fill *ppath*
3832  //
3833  ppath_end_1d( ppath, r_v, lat_v, za_v, lstep, z_field, r_geoid, ip, endface,
3834  tanpoint, ppc );
3835 
3836 
3837  // Make part from a tangent point and up to the starting pressure level.
3838  if( endface == 0 && tanpoint )
3839  {
3840  Ppath ppath2;
3841  ppath_init_structure( ppath2, ppath.dim, ppath.np );
3842  ppath_copy( ppath2, ppath );
3843 
3844  ppath_step_geom_1d( ppath2, p_grid, z_field, r_geoid, z_surface, lmax );
3845 
3846  // Combine ppath and ppath2
3847  ppath_append( ppath, ppath2 );
3848  }
3849 }
3850 
3851 
3852 
3854 
3872  Ppath& ppath,
3873  ConstVectorView p_grid,
3874  ConstVectorView lat_grid,
3875  ConstMatrixView z_field,
3876  ConstVectorView r_geoid,
3877  ConstVectorView z_surface,
3878  const double& lmax )
3879 {
3880  // Radius, zenith angle and latitude of start point.
3881  double r_start, lat_start, za_start;
3882 
3883  // Lower grid index for the grid cell of interest.
3884  Index ip, ilat;
3885 
3886  // Radii and latitudes set by *ppath_start_2d*.
3887  double lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3;
3888 
3889  // Determine the variables defined above and make all possible asserts
3890  ppath_start_2d( r_start, lat_start, za_start, ip, ilat,
3891  lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3,
3892  ppath, lat_grid, z_field, r_geoid, z_surface );
3893 
3894  // If the field "constant" is negative, this is the first call of the
3895  // function and the path constant shall be calculated.
3896  double ppc;
3897  if( ppath.constant < 0 )
3898  { ppc = geometrical_ppc( r_start, za_start ); }
3899  else
3900  { ppc = ppath.constant; }
3901 
3902 
3903  // Vars to hold found path points, path step length and coding for end face
3904  Vector r_v, lat_v, za_v;
3905  double lstep;
3906  Index endface, tanpoint;
3907 
3908  do_gridcell_2d( r_v, lat_v, za_v, lstep, endface, tanpoint,
3909  r_start, lat_start, za_start, ppc, lmax, lat1, lat3,
3910  r1a, r3a, r3b, r1b, rsurface1, rsurface3 );
3911 
3912  // Fill *ppath*
3913  //
3914  ppath_end_2d( ppath, r_v, lat_v, za_v, lstep, lat_grid, z_field, r_geoid,
3915  ip, ilat, endface, tanpoint, ppc );
3916 
3917 
3918  // Make part after a tangent point.
3919  //
3920  if( endface == 0 && tanpoint )
3921  {
3922  Ppath ppath2;
3923  ppath_init_structure( ppath2, ppath.dim, ppath.np );
3924  ppath_copy( ppath2, ppath );
3925 
3926  // Call this function recursively
3927  ppath_step_geom_2d( ppath2, p_grid, lat_grid, z_field,
3928  r_geoid, z_surface, lmax );
3929 
3930  // Combine ppath and ppath2
3931  ppath_append( ppath, ppath2 );
3932  }
3933 }
3934 
3935 
3936 
3938 
3957  Ppath& ppath,
3958  ConstVectorView p_grid,
3959  ConstVectorView lat_grid,
3960  ConstVectorView lon_grid,
3961  ConstTensor3View z_field,
3962  ConstMatrixView r_geoid,
3963  ConstMatrixView z_surface,
3964  const double& lmax )
3965 {
3966  // Radius, zenith angle and latitude of start point.
3967  double r_start, lat_start, lon_start, za_start, aa_start;
3968 
3969  // Lower grid index for the grid cell of interest.
3970  Index ip, ilat, ilon;
3971 
3972  // Radius for corner points, latitude and longitude of the grid cell
3973  //
3974  double lat1, lat3, lon5, lon6;
3975  double r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b;
3976  double rsurface15, rsurface35, rsurface36, rsurface16;
3977 
3978  // Determine the variables defined above and make all possible asserts
3979  ppath_start_3d( r_start, lat_start, lon_start, za_start, aa_start,
3980  ip, ilat, ilon, lat1, lat3, lon5, lon6,
3981  r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b,
3982  rsurface15, rsurface35, rsurface36, rsurface16,
3983  ppath, lat_grid, lon_grid, z_field, r_geoid, z_surface );
3984 
3985 
3986  // If the field "constant" is negative, this is the first call of the
3987  // function and the path constant shall be calculated.
3988  double ppc;
3989  if( ppath.constant < 0 )
3990  { ppc = geometrical_ppc( r_start, za_start ); }
3991  else
3992  { ppc = ppath.constant; }
3993 
3994 
3995  // Vars to hold found path points, path step length and coding for end face
3996  Vector r_v, lat_v, lon_v, za_v, aa_v;
3997  double lstep;
3998  Index endface, tanpoint;
3999 
4000  do_gridcell_3d( r_v, lat_v, lon_v, za_v, aa_v, lstep, endface, tanpoint,
4001  r_start, lat_start, lon_start, za_start, aa_start, ppc, lmax,
4002  lat1, lat3, lon5, lon6,
4003  r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b,
4004  rsurface15, rsurface35, rsurface36, rsurface16 );
4005 
4006  // Fill *ppath*
4007  //
4008  ppath_end_3d( ppath, r_v, lat_v, lon_v, za_v, aa_v, lstep, lat_grid,
4009  lon_grid, z_field, r_geoid, ip, ilat, ilon, endface, tanpoint, ppc );
4010 
4011 
4012  // Make part after a tangent point.
4013  //
4014  if( endface == 0 && tanpoint )
4015  {
4016  Ppath ppath2;
4017  ppath_init_structure( ppath2, ppath.dim, ppath.np );
4018  ppath_copy( ppath2, ppath );
4019 
4020  // Call this function recursively
4021  ppath_step_geom_3d( ppath2, p_grid, lat_grid, lon_grid, z_field,
4022  r_geoid, z_surface, lmax );
4023 
4024  // Combine ppath and ppath2
4025  ppath_append( ppath, ppath2 );
4026  }
4027 }
4028 
4029 
4030 
4031 
4032 
4033 /*===========================================================================
4034  === Ray tracing functions
4035  ===========================================================================*/
4036 
4038 
4091  Workspace& ws,
4092  Array<double>& r_array,
4093  Array<double>& lat_array,
4094  Array<double>& za_array,
4095  Array<double>& l_array,
4096  Index& endface,
4097  Index& tanpoint,
4098  double r,
4099  double lat,
4100  double za,
4101  Numeric& rte_pressure,
4102  Numeric& rte_temperature,
4103  Vector& rte_vmr_list,
4104  Numeric& refr_index,
4105  const Agenda& refr_index_agenda,
4106  const double& ppc,
4107  const double& lraytrace,
4108  const double& r1,
4109  const double& r3,
4110  const double& r_surface,
4111  const double& r_geoid,
4112  ConstVectorView p_grid,
4113  ConstVectorView z_field,
4114  ConstVectorView t_field,
4115  ConstMatrixView vmr_field )
4116 {
4117  // Loop boolean
4118  bool ready = false;
4119 
4120  // Variables for output from do_gridrange_1d
4121  Vector r_v, lat_v, za_v;
4122  double lstep, dlat = 9999;
4123 
4124  while( !ready )
4125  {
4126  // Constant for the geometrical step to make
4127  const double ppc_step = geometrical_ppc( r, za );
4128 
4129  // Where will a geometric path exit the grid cell?
4130  do_gridrange_1d( r_v, lat_v, za_v, lstep, endface, tanpoint, r, lat, za,
4131  ppc_step, -1, r1, r3, r_surface );
4132 
4133  assert( r_v.nelem() == 2 );
4134 
4135  // If *lstep* is < *lraytrace*, extract the found end point and
4136  // we are ready.
4137  // Otherwise, we make a geometrical step with length *lraytrace*.
4138  // To avoid that the refrcation calculations end up outside the grid
4139  // cell when *lstep* is just below *lraytrace*, we allow *lstep*
4140  // to be somewhat larger than *lraytrace*.
4141 
4142  if( lstep <= 1.01 * lraytrace )
4143  {
4144  r = r_v[1];
4145  dlat = lat_v[1] - lat;
4146  lat = lat_v[1];
4147  ready = true;
4148  }
4149  else
4150  {
4151  if( abs(za) <= 90 )
4152  { lstep = lraytrace; }
4153  else
4154  { lstep = -lraytrace; }
4155 
4156  const double r_new = geompath_r_at_l( ppc_step,
4157  geompath_l_at_r(ppc_step,r) + lstep );
4158 
4159  dlat = RAD2DEG * acos( ( r_new*r_new + r*r -
4160  lstep*lstep ) / ( 2 * r_new*r ) );
4161  r = r_new;
4162  lat = lat + dlat;
4163  lstep = abs( lstep );
4164  }
4165 
4166  // Calculate LOS zenith angle at found point.
4167  //
4168  // Refractive index at *r*
4169  get_refr_index_1d( ws, refr_index, rte_pressure, rte_temperature,
4170  rte_vmr_list, refr_index_agenda,
4171  p_grid, r_geoid, z_field, t_field, vmr_field, r );
4172 
4173  const double ppc_local = ppc / refr_index;
4174 
4175  if( ready && tanpoint )
4176  { za = 90; }
4177  else if( r >= ppc_local )
4178  { za = geompath_za_at_r( ppc_local, za, r ); }
4179  else
4180  {
4181  r = ppc_local;
4182  if( za > 90 )
4183  {
4184  ready = true;
4185  tanpoint = 1;
4186  if( r > r1 )
4187  { endface = 0; }
4188  }
4189  za = 90;
4190  }
4191 
4192  // Store found point
4193  r_array.push_back( r );
4194  lat_array.push_back( lat );
4195  za_array.push_back( za );
4196  l_array.push_back( lstep );
4197  }
4198 }
4199 
4200 
4201 
4203 
4258  Workspace& ws,
4259  Array<double>& r_array,
4260  Array<double>& lat_array,
4261  Array<double>& za_array,
4262  Array<double>& l_array,
4263  Index& endface,
4264  Index& tanpoint,
4265  double r,
4266  double lat,
4267  double za,
4268  Numeric& rte_pressure,
4269  Numeric& rte_temperature,
4270  Vector& rte_vmr_list,
4271  Numeric& refr_index,
4272  const Agenda& refr_index_agenda,
4273  const double& lraytrace,
4274  const double& lat1,
4275  const double& lat3,
4276  const double& r1a,
4277  const double& r3a,
4278  const double& r3b,
4279  const double& r1b,
4280  const double& rsurface1,
4281  const double& rsurface3,
4282  ConstVectorView p_grid,
4283  ConstVectorView lat_grid,
4284  ConstVectorView r_geoid,
4285  ConstMatrixView z_field,
4286  ConstMatrixView t_field,
4287  ConstTensor3View vmr_field )
4288 {
4289  // Loop boolean
4290  bool ready = false;
4291 
4292  // Variables for output from do_gridcell_2d
4293  Vector r_v, lat_v, za_v;
4294  double lstep, dlat = 9999, r_new, lat_new;
4295 
4296  while( !ready )
4297  {
4298  // Constant for the geometrical step to make
4299  const double ppc_step = geometrical_ppc( r, za );
4300 
4301  // Where will a geometric path exit the grid cell?
4302  do_gridcell_2d( r_v, lat_v, za_v, lstep, endface, tanpoint,
4303  r, lat, za, ppc_step, -1, lat1, lat3, r1a, r3a, r3b, r1b,
4304  rsurface1, rsurface3 );
4305 
4306  assert( r_v.nelem() == 2 );
4307 
4308  // If *lstep* is < *lraytrace*, extract the found end point and
4309  // we are ready.
4310  // Otherwise, we make a geometrical step with length *lraytrace*.
4311  // To avoid that the refrcation calculations end up outside the grid
4312  // cell when *lstep* is just below *lraytrace*, we allow *lstep*
4313  // to be somewhat larger than *lraytrace*.
4314 
4315  if( lstep <= 1.01 * lraytrace )
4316  {
4317  r_new = r_v[1];
4318  dlat = lat_v[1] - lat;
4319  lat_new = lat_v[1];
4320  ready = true;
4321  }
4322  else
4323  {
4324  if( abs(za) <= 90 )
4325  { lstep = lraytrace; }
4326  else
4327  { lstep = -lraytrace; }
4328 
4329  r_new = geompath_r_at_l( ppc_step,
4330  geompath_l_at_r(ppc_step,r) + lstep );
4331  dlat = RAD2DEG * acos( ( r_new*r_new + r*r -
4332  lstep*lstep ) / ( 2 * r_new*r ) );
4333  if( za < 0 )
4334  { dlat = -dlat; }
4335 
4336  lat_new = lat + dlat;
4337  lstep = abs( lstep );
4338 
4339  // For paths along the latitude end faces we can end up outside the
4340  // grid cell. We simply look for points outisde the grid cell.
4341  if( lat_new < lat1 )
4342  { lat_new = lat1; }
4343  else if( lat_new > lat3 )
4344  { lat_new = lat3; }
4345  }
4346 
4347  // Calculate LOS zenith angle at found point.
4348  if( ready && tanpoint )
4349  {
4350  // It is not totally correct to set *za* to 90 here. We neglect
4351  // then the curvature of this ray tracing step, but we don't care
4352  // about this for simplicity. This point has been flagged as the
4353  // the tangent point and we treat it then it that way.
4354  za = sign(za) * 90;
4355  }
4356  else
4357  {
4358  Numeric dndr, dndlat;
4359  const double za_rad = DEG2RAD * za;
4360 
4361  refr_gradients_2d( ws, refr_index, dndr, dndlat, rte_pressure,
4362  rte_temperature, rte_vmr_list, refr_index_agenda,
4363  p_grid, lat_grid, r_geoid, z_field, t_field,
4364  vmr_field, r, lat );
4365 
4366  za += -dlat + RAD2DEG * lstep / refr_index * ( -sin(za_rad) * dndr +
4367  cos(za_rad) * dndlat );
4368 
4369  // Make sure that obtained *za* is inside valid range
4370  if( za < -180 )
4371  { za += 360; }
4372  else if( za > 180 )
4373  { za -= 360; }
4374  }
4375 
4376  r = r_new;
4377  lat = lat_new;
4378 
4379  // If the path is zenith/nadir along a latitude end face, we must check
4380  // that the path does not exit with new *za*.
4381  if( lat == lat1 && za < 0 )
4382  { endface = 1; ready = 1; }
4383  else if( lat == lat3 && za > 0 )
4384  { endface = 3; ready = 1; }
4385 
4386  // Store found point
4387  r_array.push_back( r );
4388  lat_array.push_back( lat );
4389  za_array.push_back( za );
4390  l_array.push_back( lstep );
4391  }
4392 }
4393 
4394 
4395 
4397 
4466  Workspace& ws,
4467  Array<double>& r_array,
4468  Array<double>& lat_array,
4469  Array<double>& lon_array,
4470  Array<double>& za_array,
4471  Array<double>& aa_array,
4472  Array<double>& l_array,
4473  Index& endface,
4474  Index& tanpoint,
4475  double r,
4476  double lat,
4477  double lon,
4478  double za,
4479  double aa,
4480  Numeric& rte_pressure,
4481  Numeric& rte_temperature,
4482  Vector& rte_vmr_list,
4483  Numeric& refr_index,
4484  const Agenda& refr_index_agenda,
4485  const double& lraytrace,
4486  const double& lat1,
4487  const double& lat3,
4488  const double& lon5,
4489  const double& lon6,
4490  const double& r15a,
4491  const double& r35a,
4492  const double& r36a,
4493  const double& r16a,
4494  const double& r15b,
4495  const double& r35b,
4496  const double& r36b,
4497  const double& r16b,
4498  const double& rsurface15,
4499  const double& rsurface35,
4500  const double& rsurface36,
4501  const double& rsurface16,
4502  ConstVectorView p_grid,
4503  ConstVectorView lat_grid,
4504  ConstVectorView lon_grid,
4505  ConstMatrixView r_geoid,
4506  ConstTensor3View z_field,
4507  ConstTensor3View t_field,
4508  ConstTensor4View vmr_field )
4509 {
4510  // Loop boolean
4511  bool ready = false;
4512 
4513  // Variables for output from do_gridcell_2d
4514  Vector r_v, lat_v, lon_v, za_v, aa_v;
4515  double r_new, lat_new, lon_new, za_new, aa_new;
4516  double lstep;
4517 
4518  while( !ready )
4519  {
4520  // Constant for the geometrical step to make
4521  const double ppc_step = geometrical_ppc( r, za );
4522 
4523  // Where will a geometric path exit the grid cell?
4524  do_gridcell_3d( r_v, lat_v, lon_v, za_v, aa_v, lstep, endface, tanpoint,
4525  r, lat, lon, za, aa, ppc_step, -1, lat1, lat3, lon5, lon6,
4526  r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b,
4527  rsurface15, rsurface35, rsurface36, rsurface16 );
4528 
4529  assert( r_v.nelem() == 2 );
4530 
4531  // If *lstep* is < *lraytrace*, extract the found end point and
4532  // we are ready.
4533  // Otherwise, we make a geometrical step with length *lraytrace*.
4534  // To avoid that the refrcation calculations end up outside the grid
4535  // cell when *lstep* is just below *lraytrace*, we allow *lstep*
4536  // to be somewhat larger than *lraytrace*.
4537 
4538  if( lstep <= 1.01 * lraytrace )
4539  {
4540  r_new = r_v[1];
4541  lat_new = lat_v[1];
4542  lon_new = lon_v[1];
4543  za_new = za_v[1];
4544  aa_new = aa_v[1];
4545  ready = true;
4546  }
4547  else
4548  {
4549  // Sensor pos and LOS in cartesian coordinates
4550  double x, y, z, dx, dy, dz;
4551  poslos2cart( x, y, z, dx, dy, dz, r, lat, lon, za, aa );
4552 
4553  lstep = lraytrace;
4554 
4555  cart2poslos( r_new, lat_new, lon_new, za_new, aa_new,
4556  x+dx*lstep, y+dy*lstep, z+dz*lstep, dx, dy, dz );
4557 
4558  // For paths along some end face we can end up outside the
4559  // grid cell. We simply look for points outisde the grid cell.
4560  if( lat_new < lat1 )
4561  { lat_new = lat1; }
4562  else if( lat_new > lat3 )
4563  { lat_new = lat3; }
4564  if( lon_new < lon5 )
4565  { lon_new = lon5; }
4566  else if( lon_new > lon6 )
4567  { lon_new = lon6; }
4568 
4569  // Checks to improve the accuracy for speciel cases.
4570  // The checks are only needed for values cacluated locally as
4571  // the same checks are made inside *do_gridcell_3d*.
4572 
4573  //--- Set zenith angle to be as accurate as possible
4574  if( za < ANGTOL || za > 180-ANGTOL )
4575  { za_new = za; }
4576  else
4577  { za_new = geompath_za_at_r( ppc_step, za, r_new ); }
4578 
4579  //--- Set azimuth angle and lon. to be as accurate as possible for
4580  // north-south observations
4581  if( abs( lat ) < 90 &&
4582  ( abs(aa) < ANGTOL || abs( aa ) > 180-ANGTOL ) )
4583  {
4584  aa_new = aa;
4585  lon_new = lon;
4586  }
4587 
4588  // Shall lon values be shifted?
4589  resolve_lon( lon_new, lon5, lon6 );
4590  }
4591 
4592  // Calculate LOS zenith angle at found point.
4593  if( ready && tanpoint )
4594  {
4595  // It is not totally correct to set *za* to 90 here. We neglect
4596  // then the curvature of this ray tracing step, but we don't care
4597  // about this for simplicity. This point has been flagged as the
4598  // the tangent point and we treat it then it that way.
4599  za_new = 90;
4600  }
4601  else
4602  {
4603  Numeric dndr, dndlat, dndlon;
4604 
4605  refr_gradients_3d( ws,
4606  refr_index, dndr, dndlat, dndlon, rte_pressure,
4607  rte_temperature, rte_vmr_list, refr_index_agenda,
4608  p_grid, lat_grid, lon_grid, r_geoid, z_field,
4609  t_field, vmr_field, r, lat, lon );
4610 
4611  const double aterm = RAD2DEG * lstep / refr_index;
4612  const double za_rad = DEG2RAD * za;
4613  const double aa_rad = DEG2RAD * aa;
4614  const double sinza = sin( za_rad );
4615  const double sinaa = sin( aa_rad );
4616  const double cosaa = cos( aa_rad );
4617 
4618 
4619  if( za < ANGTOL || za > 180-ANGTOL )
4620  {
4621  za_new += aterm * ( cos(za_rad) *
4622  ( cosaa * dndlat + sinaa * dndlon ) );
4623  aa_new = RAD2DEG * atan2( dndlon, dndlat);
4624  }
4625  else
4626  {
4627  za_new += aterm * ( -sinza * dndr + cos(za_rad) *
4628  ( cosaa * dndlat + sinaa * dndlon ) );
4629  aa_new += aterm * sinza * ( cosaa * dndlon - sinaa * dndlat );
4630  }
4631 
4632  // Make sure that obtained angles are inside valid ranges
4633  if( za_new > 180 )
4634  { za_new = 180 - za_new; }
4635  else if( za_new < 0 )
4636  { za_new = -za_new; }
4637  //
4638  if( aa_new > 180 )
4639  { aa_new -= 360; }
4640  else if( aa_new < -180 )
4641  { aa_new += 360; }
4642  }
4643 
4644  r = r_new;
4645  lat = lat_new;
4646  lon = lon_new;
4647  za = za_new;
4648  aa = aa_new;
4649 
4650  // For some cases where the path goes along an end face,
4651  // it could be the case that the refraction bends the path out
4652  // of the grid cell.
4653  if( lon == lon5 && aa < 0 )
4654  { endface = 5; ready = 1; }
4655  else if( lon == lon6 && aa > 0 )
4656  { endface = 6; ready = 1; }
4657  else if( za > 0 && za < 180 )
4658  {
4659  if( lat == lat1 && lat != -90 && abs( aa ) > 90 )
4660  { endface = 1; ready = 1; }
4661  else if( lat == lat3 && lat != 90 && abs( aa ) < 90 )
4662  { endface = 3; ready = 1; }
4663  }
4664 
4665  // Store found point
4666  r_array.push_back( r );
4667  lat_array.push_back( lat );
4668  lon_array.push_back( lon );
4669  za_array.push_back( za );
4670  aa_array.push_back( aa );
4671  l_array.push_back( lstep );
4672  }
4673 }
4674 
4675 
4676 
4677 
4678 
4679 /*===========================================================================
4680  === Core functions for refraction *ppath_step* functions
4681  ===========================================================================*/
4682 
4684 
4715  Workspace& ws,
4716  Ppath& ppath,
4717  Numeric& rte_pressure,
4718  Numeric& rte_temperature,
4719  Vector& rte_vmr_list,
4720  Numeric& refr_index,
4721  const Agenda& refr_index_agenda,
4722  ConstVectorView p_grid,
4723  ConstVectorView z_field,
4724  ConstVectorView t_field,
4725  ConstMatrixView vmr_field,
4726  const double& r_geoid,
4727  const double& z_surface,
4728  const String& rtrace_method,
4729  const double& lraytrace,
4730  const double& lmax )
4731 {
4732  // Starting radius, zenith angle and latitude
4733  double r_start, lat_start, za_start;
4734 
4735  // Index of the pressure level being the lower limit for the
4736  // grid range of interest.
4737  Index ip;
4738 
4739  // Determine the variables defined above, and make asserts of input
4740  ppath_start_1d( r_start, lat_start, za_start, ip, ppath );
4741 
4742  // If the field "constant" is negative, this is the first call of the
4743  // function and the path constant shall be calculated.
4744  // If the sensor is placed outside the atmosphere, the constant is
4745  // already set.
4746  double ppc;
4747  if( ppath.constant < 0 )
4748  {
4749  get_refr_index_1d( ws, refr_index, rte_pressure, rte_temperature,
4750  rte_vmr_list,
4751  refr_index_agenda, p_grid, r_geoid, z_field,
4752  t_field, vmr_field, r_start );
4753  ppc = refraction_ppc( r_start, za_start, refr_index );
4754  }
4755  else
4756  { ppc = ppath.constant; }
4757 
4758 
4759  // Perform the ray tracing
4760  //
4761  // Arrays to store found ray tracing points
4762  // (Vectors don't work here as we don't know how many points there will be)
4763  Array<double> r_array, lat_array, za_array, l_array;
4764  //
4765  // Store the start point
4766  r_array.push_back( r_start );
4767  lat_array.push_back( lat_start );
4768  za_array.push_back( za_start );
4769  //
4770  // Number coding for end face
4771  Index endface, tanpoint;
4772  //
4773  if( rtrace_method == "linear_euler" )
4774  {
4776  r_array, lat_array, za_array, l_array, endface,
4777  tanpoint, r_start, lat_start, za_start, rte_pressure, rte_temperature,
4778  rte_vmr_list, refr_index, refr_index_agenda, ppc, lraytrace,
4779  r_geoid+z_field[ip], r_geoid+z_field[ip+1], r_geoid + z_surface,
4780  r_geoid, p_grid, z_field, t_field, vmr_field );
4781  }
4782 #ifndef NDEBUG
4783  else
4784  {
4785  bool known_ray_trace_method = false;
4786  assert( known_ray_trace_method );
4787  }
4788 #endif
4789 
4790 
4791  // Interpolate the radii, zenith angles and latitudes to a set of points
4792  // linearly spaced along the path.
4793  //
4794  Vector r_v, lat_v, za_v;
4795  double lstep;
4796  //
4797  from_raytracingarrays_to_ppath_vectors_1d_and_2d( r_v, lat_v, za_v, lstep,
4798  r_array, lat_array, za_array, l_array, 0, lmax );
4799 
4800 
4801  // Fill *ppath*
4802  ppath_end_1d( ppath, r_v, lat_v, za_v, lstep, z_field, r_geoid, ip, endface,
4803  tanpoint, ppc );
4804 
4805 
4806  //--- End point is a tangent point
4807  if( endface == 0 && tanpoint )
4808  {
4809  // Make part from tangent point and up to the starting pressure level.
4810  //
4811  Ppath ppath2;
4812  ppath_init_structure( ppath2, ppath.dim, ppath.np );
4813  ppath_copy( ppath2, ppath );
4814 
4815  ppath_step_refr_1d( ws,
4816  ppath2, rte_pressure, rte_temperature, rte_vmr_list,
4817  refr_index, refr_index_agenda, p_grid, z_field, t_field,
4818  vmr_field, r_geoid, z_surface, rtrace_method, lraytrace, lmax );
4819 
4820  // Combine ppath and ppath2
4821  ppath_append( ppath, ppath2 );
4822  }
4823 }
4824 
4825 
4826 
4828 
4858  Workspace& ws,
4859  Ppath& ppath,
4860  Numeric& rte_pressure,
4861  Numeric& rte_temperature,
4862  Vector& rte_vmr_list,
4863  Numeric& refr_index,
4864  const Agenda& refr_index_agenda,
4865  ConstVectorView p_grid,
4866  ConstVectorView lat_grid,
4867  ConstMatrixView z_field,
4868  ConstMatrixView t_field,
4869  ConstTensor3View vmr_field,
4870  ConstVectorView r_geoid,
4871  ConstVectorView z_surface,
4872  const String& rtrace_method,
4873  const double& lraytrace,
4874  const double& lmax )
4875 {
4876  // Radius, zenith angle and latitude of start point.
4877  double r_start, lat_start, za_start;
4878 
4879  // Lower grid index for the grid cell of interest.
4880  Index ip, ilat;
4881 
4882  // Radii and latitudes set by *ppath_start_2d*.
4883  double lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3;
4884 
4885  // Determine the variables defined above and make all possible asserts
4886  ppath_start_2d( r_start, lat_start, za_start, ip, ilat,
4887  lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3,
4888  ppath, lat_grid, z_field, r_geoid, z_surface );
4889 
4890  // Perform the ray tracing
4891  //
4892  // No constant for the path is valid here.
4893  //
4894  // Arrays to store found ray tracing points
4895  // (Vectors don't work here as we don't know how many points there will be)
4896  Array<double> r_array, lat_array, za_array, l_array;
4897  //
4898  // Store the start point
4899  r_array.push_back( r_start );
4900  lat_array.push_back( lat_start );
4901  za_array.push_back( za_start );
4902  //
4903  // Number coding for end face
4904  Index endface, tanpoint;
4905  //
4906  if( rtrace_method == "linear_euler" )
4907  {
4909  r_array, lat_array, za_array, l_array, endface,
4910  tanpoint, r_start, lat_start, za_start, rte_pressure, rte_temperature,
4911  rte_vmr_list, refr_index, refr_index_agenda, lraytrace,
4912  lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3,
4913  p_grid, lat_grid, r_geoid, z_field, t_field, vmr_field );
4914  }
4915 #ifndef NDEBUG
4916  else
4917  {
4918  bool known_ray_trace_method = false;
4919  assert( known_ray_trace_method );
4920  }
4921 #endif
4922 
4923 
4924  // Interpolate the radii, zenith angles and latitudes to a set of points
4925  // linearly spaced along the path.
4926  //
4927  Vector r_v, lat_v, za_v;
4928  double lstep;
4929  //
4930  from_raytracingarrays_to_ppath_vectors_1d_and_2d( r_v, lat_v, za_v, lstep,
4931  r_array, lat_array, za_array, l_array, 0, lmax );
4932 
4933 
4934  // Fill *ppath*
4935  ppath_end_2d( ppath, r_v, lat_v, za_v, lstep, lat_grid, z_field, r_geoid,
4936  ip, ilat, endface, tanpoint, -1 );
4937 
4938 
4939  // Make part after a tangent point.
4940  //
4941  if( endface == 0 && tanpoint )
4942  {
4943  Ppath ppath2;
4944  ppath_init_structure( ppath2, ppath.dim, ppath.np );
4945  ppath_copy( ppath2, ppath );
4946 
4947  // Call this function recursively
4948  ppath_step_refr_2d( ws,
4949  ppath2, rte_pressure, rte_temperature, rte_vmr_list,
4950  refr_index, refr_index_agenda, p_grid, lat_grid, z_field,
4951  t_field, vmr_field,
4952  r_geoid, z_surface, rtrace_method, lraytrace, lmax );
4953 
4954  // Combine ppath and ppath2
4955  ppath_append( ppath, ppath2 );
4956  }
4957 }
4958 
4959 
4960 
4962 
4993  Workspace& ws,
4994  Ppath& ppath,
4995  Numeric& rte_pressure,
4996  Numeric& rte_temperature,
4997  Vector& rte_vmr_list,
4998  Numeric& refr_index,
4999  const Agenda& refr_index_agenda,
5000  ConstVectorView p_grid,
5001  ConstVectorView lat_grid,
5002  ConstVectorView lon_grid,
5003  ConstTensor3View z_field,
5004  ConstTensor3View t_field,
5005  ConstTensor4View vmr_field,
5006  ConstMatrixView r_geoid,
5007  ConstMatrixView z_surface,
5008  const String& rtrace_method,
5009  const double& lraytrace,
5010  const double& lmax )
5011 {
5012  // Radius, zenith angle and latitude of start point.
5013  double r_start, lat_start, lon_start, za_start, aa_start;
5014 
5015  // Lower grid index for the grid cell of interest.
5016  Index ip, ilat, ilon;
5017 
5018  // Radius for corner points, latitude and longitude of the grid cell
5019  //
5020  double lat1, lat3, lon5, lon6;
5021  double r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b;
5022  double rsurface15, rsurface35, rsurface36, rsurface16;
5023 
5024  // Determine the variables defined above and make all possible asserts
5025  ppath_start_3d( r_start, lat_start, lon_start, za_start, aa_start,
5026  ip, ilat, ilon, lat1, lat3, lon5, lon6,
5027  r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b,
5028  rsurface15, rsurface35, rsurface36, rsurface16,
5029  ppath, lat_grid, lon_grid, z_field, r_geoid, z_surface );
5030 
5031  // Perform the ray tracing
5032  //
5033  // No constant for the path is valid here.
5034  //
5035  // Arrays to store found ray tracing points
5036  // (Vectors don't work here as we don't know how many points there will be)
5037  Array<double> r_array, lat_array, lon_array, za_array, aa_array, l_array;
5038  //
5039  // Store the start point
5040  r_array.push_back( r_start );
5041  lat_array.push_back( lat_start );
5042  lon_array.push_back( lon_start );
5043  za_array.push_back( za_start );
5044  aa_array.push_back( aa_start );
5045  //
5046  // Number coding for end face
5047  Index endface, tanpoint;
5048  //
5049  if( rtrace_method == "linear_euler" )
5050  {
5051  raytrace_3d_linear_euler( ws, r_array, lat_array, lon_array, za_array,
5052  aa_array, l_array, endface, tanpoint, r_start,
5053  lat_start, lon_start, za_start, aa_start,
5054  rte_pressure, rte_temperature, rte_vmr_list,
5055  refr_index, refr_index_agenda, lraytrace,
5056  lat1, lat3, lon5, lon6,
5057  r15a, r35a, r36a, r15a, r15b, r35b, r36b, r15b,
5058  rsurface15, rsurface35, rsurface36, rsurface16,
5059  p_grid, lat_grid, lon_grid,
5060  r_geoid, z_field, t_field, vmr_field );
5061  }
5062 #ifndef NDEBUG
5063  else
5064  {
5065  bool known_ray_trace_method = false;
5066  assert( known_ray_trace_method );
5067  }
5068 #endif
5069 
5070 
5071  // Interpolate the radii, zenith angles and latitudes to a set of points
5072  // linearly spaced along the path.
5073  //
5074  Vector r_v, lat_v, lon_v, za_v, aa_v;
5075  double lstep;
5076  //
5077  from_raytracingarrays_to_ppath_vectors_3d( r_v, lat_v, lon_v, za_v, aa_v,
5078  lstep, r_array, lat_array, lon_array,
5079  za_array, aa_array, l_array, lmax );
5080 
5081 
5082  // Fill *ppath*
5083  ppath_end_3d( ppath, r_v, lat_v, lon_v, za_v, aa_v, lstep, lat_grid,
5084  lon_grid, z_field, r_geoid, ip, ilat, ilon, endface, tanpoint, -1 );
5085 
5086 
5087  // Make part after a tangent point.
5088  //
5089  if( endface == 0 && tanpoint )
5090  {
5091  Ppath ppath2;
5092  ppath_init_structure( ppath2, ppath.dim, ppath.np );
5093  ppath_copy( ppath2, ppath );
5094 
5095  // Call this function recursively
5096  ppath_step_refr_3d( ws,
5097  ppath2, rte_pressure, rte_temperature, rte_vmr_list,
5098  refr_index, refr_index_agenda, p_grid, lat_grid,
5099  lon_grid, z_field, t_field, vmr_field,
5100  r_geoid, z_surface, rtrace_method, lraytrace, lmax );
5101 
5102  // Combine ppath and ppath2
5103  ppath_append( ppath, ppath2 );
5104  }
5105 }
5106 
5107 
5108 
5109 
5110 /*===========================================================================
5111  === Core of ppathCalc and help function(s)
5112  ===========================================================================*/
5113 
5115 
5165  const Index& atmosphere_dim,
5166  ConstVectorView p_grid,
5167  ConstVectorView lat_grid,
5168  ConstVectorView lon_grid,
5169  ConstTensor3View z_field,
5170  ConstMatrixView r_geoid,
5171  ConstMatrixView z_surface,
5172  const Index& cloudbox_on,
5173  const ArrayOfIndex& cloudbox_limits,
5174  const bool& outside_cloudbox,
5175  ConstVectorView rte_pos,
5176  ConstVectorView rte_los,
5177  const Verbosity& verbosity)
5178 {
5179  CREATE_OUT1
5180  CREATE_OUT2
5181 
5182  // This function contains no checks or asserts as it is only a sub-function
5183  // to ppathCalc where the input is checked carefully.
5184 
5185  // Allocate the ppath structure
5186  ppath_init_structure( ppath, atmosphere_dim, 1 );
5187 
5188  // Number of pressure levels
5189  const Index np = p_grid.nelem();
5190 
5191  // The different atmospheric dimensionalities are handled seperately
5192 
5193  //-- 1D ---------------------------------------------------------------------
5194  if( atmosphere_dim == 1 )
5195  {
5196  // Radius for the surface
5197  const double r_surface = r_geoid(0,0) + z_surface(0,0);
5198 
5199  // Radius for the top of the atmosphere
5200  const double r_top = r_geoid(0,0) + z_field(np-1,0,0);
5201 
5202  // The only forbidden case here is that the sensor is below the surface
5203  if( (rte_pos[0] + RTOL) < r_surface )
5204  {
5205  ostringstream os;
5206  os << "The ppath starting point is placed "
5207  << (r_surface - rte_pos[0])/1e3 << " km below surface level.\n"
5208  << "This point must be above the surface.";
5209  throw runtime_error(os.str());
5210  }
5211 
5212  out2 << " sensor altitude : " << (rte_pos[0]-r_geoid(0,0))/1e3
5213  << " km\n";
5214 
5215  // If downwards, calculate geometrical tangent position
5216  Vector geom_tan_pos(0);
5217  if( rte_los[0] > 90 )
5218  {
5219  geom_tan_pos.resize(2);
5220  geom_tan_pos[0] = geometrical_ppc( rte_pos[0], rte_los[0] );
5221  geom_tan_pos[1] = geompath_lat_at_za( rte_los[0], 0, 90 );
5222  out2 << " geom. tangent radius : " << geom_tan_pos[0]/1e3
5223  <<" km\n";
5224  out2 << " geom. tangent latitude : " << geom_tan_pos[1] << "\n";
5225  out2 << " geom. tangent altitude : "
5226  << (geom_tan_pos[0]-r_geoid(0,0))/1e3 << " km\n";
5227  }
5228 
5229  // Put sensor position and LOS in ppath as first guess
5230  ppath.pos(0,0) = rte_pos[0];
5231  ppath.los(0,0) = rte_los[0];
5232  ppath.pos(0,1) = 0;
5233  ppath.z[0] = ppath.pos(0,0) - r_geoid(0,0);
5234 
5235 
5236  // The sensor is inside the model atmosphere, 1D ------------------------
5237  if( rte_pos[0] < r_top )
5238  {
5239  // Use below the values in ppath (instead of rte_pos and rte_los) as
5240  // they can be modified on the way.
5241 
5242  // Is the sensor on the surface looking down?
5243  // If yes and the sensor is inside the cloudbox, the background will
5244  // be changed below.
5245  if( ppath.pos(0,0) == r_surface && ppath.los(0,0) > 90 )
5246  { ppath_set_background( ppath, 2 ); }
5247 
5248  // Check sensor position with respect to cloud box.
5249  if( cloudbox_on )
5250  {
5251  if( outside_cloudbox )
5252  {
5253  // Is the sensor inside the cloud box?
5254  if( ppath.z[0] > z_field(cloudbox_limits[0],0,0) &&
5255  ppath.z[0] < z_field(cloudbox_limits[1],0,0) )
5256  { ppath_set_background( ppath, 4 ); }
5257 
5258  else if( ( ppath.z[0] == z_field(cloudbox_limits[0],0,0) &&
5259  ppath.los(0,0) <= 90 )
5260  ||
5261  ( ppath.z[0] == z_field(cloudbox_limits[1],0,0) &&
5262  ppath.los(0,0) > 90 ) )
5263  {
5264  ppath_set_background( ppath, 3 );
5265  }
5266  }
5267  }
5268  }
5269 
5270  // The sensor is outside the model atmosphere, 1D -----------------------
5271  else
5272  {
5273  // Upward:
5274  if( rte_los[0] <= 90 )
5275  {
5276  ppath_set_background( ppath, 1 );
5277  out1 << " --- WARNING ---, path is totally outside of the "
5278  << "model atmosphere\n";
5279  }
5280  // Downward:
5281  else
5282  {
5283  ppath.constant = geom_tan_pos[0];
5284 
5285  // Path is above the atmosphere
5286  if( ppath.constant >= r_top )
5287  {
5288  ppath_set_background( ppath, 1 );
5289  out1 << " --- WARNING ---, path is totally outside of the "
5290  << "model atmosphere\n";
5291  }
5292 
5293  // Path enters the atmosphere
5294  else
5295  {
5296  ppath.z[0] = z_field(np-1,0,0);
5297  ppath.pos(0,0) = r_top;
5298  ppath.los(0,0) = geompath_za_at_r( ppath.constant, rte_los[0],
5299  r_top );
5300  ppath.pos(0,1) = geompath_lat_at_za( rte_los[0], 0,
5301  ppath.los(0,0) );
5302  // Path directly enters at cloudbox (reaching up to top of atmosphere)
5303  if( cloudbox_on )
5304  {
5305  if( ppath.z[0] == z_field(cloudbox_limits[1],0,0) )
5306  {
5307  ppath_set_background( ppath, 3 );
5308  }
5309  }
5310  }
5311  }
5312  }
5313 
5314  // Get grid position for the end point, if it is inside the atmosphere.
5315  if( ppath.z[0] <= z_field(np-1,0,0) )
5316  {
5317  gridpos( ppath.gp_p, z_field(joker,0,0), ppath.z );
5318  gridpos_check_fd( ppath.gp_p[0] );
5319  }
5320 
5321  // Set geometrical tangent point position
5322  if( geom_tan_pos.nelem() == 2 )
5323  {
5324  ppath.geom_tan_pos.resize(2);
5325  ppath.geom_tan_pos = geom_tan_pos;
5326  }
5327 
5328  } // End 1D
5329 
5330 
5331  //-- 2D ---------------------------------------------------------------------
5332  else if( atmosphere_dim == 2 )
5333  {
5334  // Number of points in the latitude grids
5335  const Index nlat = lat_grid.nelem();
5336 
5337  // Is the sensor inside the latitude range of the model atmosphere,
5338  // and below the top of the atmosphere? If yes, is_inside = true.
5339  // Store geoid and surface radii, grid position and interpolation weights
5340  // for later use.
5341  //
5342  bool is_inside = false;
5343  double rv_geoid=-1, rv_surface=-1; // -1 to avoid compiler warnings
5344  GridPos gp_lat;
5345  Vector itw(2);
5346  //
5347  if( rte_pos[1] >= lat_grid[0] && rte_pos[1] <= lat_grid[nlat-1] )
5348  {
5349  gridpos( gp_lat, lat_grid, rte_pos[1] );
5350  interpweights( itw, gp_lat );
5351 
5352  rv_geoid = interp( itw, r_geoid(joker,0), gp_lat );
5353  rv_surface = rv_geoid + interp( itw, z_surface(joker,0), gp_lat );
5354 
5355  out2 << " sensor altitude : " << (rte_pos[0]-rv_geoid)/1e3
5356  << " km\n";
5357 
5358  if( rte_pos[0] < ( rv_geoid +
5359  interp( itw, z_field(np-1,joker,0), gp_lat ) ) )
5360  { is_inside = true; }
5361  }
5362 
5363  // If downwards, calculate geometrical tangent position. If the tangent
5364  // point is inside the covered latitude range, calculate also the
5365  // geometrical altitude of the tangent point and the top of atmosphere.
5366  //
5367  Vector geom_tan_pos(0);
5368  double geom_tan_z=-2, geom_tan_atmtop=-1; // OK values if the variables
5369  // are not set below
5370  if( abs(rte_los[0]) > 90 )
5371  {
5372  geom_tan_pos.resize(2);
5373  geom_tan_pos[0] = geometrical_ppc( rte_pos[0], rte_los[0] );
5374  if( rte_los[0] > 0 )
5375  { geom_tan_pos[1] =
5376  geompath_lat_at_za( rte_los[0], rte_pos[1], 90 ); }
5377  else
5378  { geom_tan_pos[1] =
5379  geompath_lat_at_za( rte_los[0], rte_pos[1], -90 ); }
5380  out2 << " geom. tangent radius : " << geom_tan_pos[0] / 1e3
5381  <<" km\n";
5382  out2 << " geom. tangent latitude : " << geom_tan_pos[1]
5383  << "\n";
5384  //
5385  if( geom_tan_pos[1] >= lat_grid[0] &&
5386  geom_tan_pos[1] <= lat_grid[nlat-1] )
5387  {
5388  GridPos gp_tan;
5389  Vector itw_tan(2);
5390  gridpos( gp_tan, lat_grid, geom_tan_pos[1] );
5391  interpweights( itw_tan, gp_tan );
5392  geom_tan_z = geom_tan_pos[0] -
5393  interp( itw_tan, r_geoid(joker,0), gp_tan );
5394  geom_tan_atmtop =
5395  interp( itw_tan, z_field(np-1,joker,0), gp_tan );
5396  out2 << " geom. tangent altitude : " << geom_tan_z/1e3
5397  << " km\n";
5398  }
5399  }
5400 
5401  // Put sensor position and LOS in ppath as first guess
5402  ppath.pos(0,0) = rte_pos[0];
5403  ppath.pos(0,1) = rte_pos[1];
5404  ppath.los(0,0) = rte_los[0];
5405 
5406  // The sensor is inside the model atmosphere, 2D ------------------------
5407  if( is_inside )
5408  {
5409  // Check that the sensor is above the surface
5410  if( (rte_pos[0] + RTOL) < rv_surface )
5411  {
5412  ostringstream os;
5413  os << "The ppath starting point is placed "
5414  << (rv_surface - rte_pos[0])/1e3 << " km below surface level.\n"
5415  << "This point must be above the surface.";
5416  throw runtime_error(os.str());
5417  }
5418 
5419  // Check that not at latitude end point and looks out
5420  if( ( rte_pos[1] == lat_grid[0] && rte_los[0] < 0 ) )
5421  throw runtime_error( "The sensor is at the lower latitude end "
5422  "point and the zenith angle < 0." );
5423  if( rte_pos[1] == lat_grid[nlat-1] && rte_los[0] >= 0 )
5424  throw runtime_error( "The sensor is at the upper latitude end "
5425  "point and the zenith angle >= 0." );
5426 
5427  // Geometrical altitude
5428  ppath.z[0] = ppath.pos(0,0) - rv_geoid;
5429 
5430  // Use below the values in ppath (instead of rte_pos and rte_los) as
5431  // they can be modified on the way.
5432 
5433  // Grid positions
5434  ppath.gp_lat[0].idx = gp_lat.idx;
5435  ppath.gp_lat[0].fd[0] = gp_lat.fd[0];
5436  ppath.gp_lat[0].fd[1] = gp_lat.fd[1];
5437 
5438  // Create a vector with the geometrical altitude of the pressure
5439  // levels for the sensor latitude and use it to get ppath.gp_p.
5440  Vector z_grid(np);
5441  z_at_lat_2d( z_grid, p_grid, lat_grid,
5442  z_field(joker,joker,0), gp_lat );
5443  gridpos( ppath.gp_p, z_grid, ppath.z );
5444 
5445  // Is the sensor on the surface looking down?
5446  if( ppath.pos(0,0) == rv_surface )
5447  {
5448  // Calculate radial slope of the surface
5449  const double rslope = plevel_slope_2d( lat_grid,
5450  r_geoid(joker,0), z_surface(joker,0),
5451  gp_lat, ppath.los(0,0) );
5452 
5453  // Calculate angular tilt of the surface
5454  const double atilt = plevel_angletilt( rv_surface, rslope);
5455 
5456  // Are we looking down into the surface?
5457  // If yes and the sensor is inside the cloudbox, the background
5458  // will be changed below.
5459  if( is_los_downwards( ppath.los(0,0), atilt ) )
5460  { ppath_set_background( ppath, 2 ); }
5461  }
5462 
5463  // Handle possible numerical problems for grid positions
5464  gridpos_check_fd( ppath.gp_p[0] );
5465  gridpos_check_fd( ppath.gp_lat[0] );
5466  }
5467 
5468  // The sensor is outside the model atmosphere, 2D -----------------------
5469  else
5470  {
5471  // Handle cases when the sensor appears to look the wrong way
5472  if( ( rte_pos[1] <= lat_grid[0] && rte_los[0] <= 0 ) ||
5473  ( rte_pos[1] >= lat_grid[nlat-1] && rte_los[0] >= 0 ) )
5474  {
5475  ostringstream os;
5476  os << "The sensor is outside (or at the limit) of the model "
5477  << "atmosphere but\nlooks in the wrong direction (wrong sign "
5478  << "for the zenith angle?).\nThis case includes nadir "
5479  << "looking exactly at the latitude end points.";
5480  throw runtime_error( os.str() );
5481  }
5482 
5483  // Upward:
5484  if( abs(rte_los[0]) <= 90 )
5485  {
5486  ppath_set_background( ppath, 1 );
5487  out1 << " ------- WARNING -------: path is totally outside of "
5488  << "the model atmosphere\n";
5489  }
5490 
5491  // Downward:
5492  else
5493  {
5494  // We can here set the path constant, that equals the radius of
5495  // the geometrical tangent point.
5496  ppath.constant = geom_tan_pos[0];
5497 
5498  // If the sensor is outside the latitude range, check that path
5499  // is above the closest corner of the model atmosphere
5500  if( rte_pos[1] < lat_grid[0] || rte_pos[1] > lat_grid[nlat-1] )
5501  {
5502  Index ic = 0;
5503  String sc = "lower";
5504  if( rte_pos[1] > lat_grid[0] )
5505  { ic = nlat - 1; sc = "upper"; }
5506  const double rv = geompath_r_at_lat( ppath.constant,
5507  rte_pos[1], rte_los[0], lat_grid[ic] );
5508  if( rv < ( r_geoid(ic,0) + z_field(np-1,ic,0) ) )
5509  {
5510  ostringstream os;
5511  os << "The sensor is outside of the model atmosphere and "
5512  << "looks in the\n" << sc << " latitude end face.\n"
5513  << "The geometrical altitude of the corner point is "
5514  << z_field(np-1,ic,0)/1e3 << " km.\n"
5515  << "The geometrical altitude of the entrance point is "
5516  << (rv-r_geoid(ic,0))/1e3 << " km.";
5517  throw runtime_error( os.str() );
5518  }
5519  }
5520 
5521  // If the tangent point is inside covered latitude range,
5522  // everything is OK. If not, the path must be below the corner of
5523  // the model atmosphere.
5524  if( ( geom_tan_pos[1] < lat_grid[0] ||
5525  geom_tan_pos[1] > lat_grid[nlat-1] ) )
5526  {
5527  Index ic = 0;
5528  String sc = "lower";
5529  if( rte_los[0] >= 0 )
5530  { ic = nlat - 1; sc = "upper"; }
5531  const double rv = geompath_r_at_lat( ppath.constant,
5532  rte_pos[1], rte_los[0], lat_grid[ic] );
5533  if( rv >= ( r_geoid(ic,0) + z_field(np-1,ic,0) ) )
5534  {
5535  ostringstream os;
5536  os << "The combination of sensor position and line-of-"
5537  << "sight gives a\npropagation path that goes above "
5538  << "the model atmosphere, with\na tangent point "
5539  << "outside the covered latitude range.\nThe latitude "
5540  << "of the tangent point is " << geom_tan_pos[1]
5541  << " degrees.";
5542  throw runtime_error( os.str() );
5543  }
5544  }
5545 
5546  // That should be all needed checks. We know now that the path is
5547  // either totally outside the atmosphere, with a tangent point
5548  // inside lat_grid, or enters the atmosphere from the top
5549  // somewhere inside lat_grid. In the latter case we need to
5550  // determine the latitude of the entrance point.
5551 
5552  // Path is above the atmosphere:
5553  // Requieres that tangent point is inside lat_grid and above the
5554  // top of the atmosphere.
5555  if( geom_tan_pos[1] >= lat_grid[0] &&
5556  geom_tan_pos[1] <= lat_grid[nlat-1] &&
5557  geom_tan_z >= geom_tan_atmtop )
5558  {
5559  ppath_set_background( ppath, 1 );
5560  out1 << " ------- WARNING -------: "
5561  << "path is totally outside of the model atmosphere\n";
5562  }
5563 
5564  // The path enters the atmosphere
5565  else
5566  {
5567  // Find the latitude where the path passes top of the
5568  // atmosphere.
5569 
5570  // We are handling this in a rather dumb way. A test is
5571  // performed for each latitude range using
5572  // plevel_crossing_2d. A bit smarter algorithm was considered
5573  // but that made the code more messy. The case when the
5574  // sensor is placed inside lat_grid must be hanled
5575  // seperetaly.
5576 
5577  // Determine first latitude range of interest, search
5578  // direction and first test latitude.
5579  double lat0;
5580  Index ilat0, istep;
5581  //
5582  if( rte_pos[1] <= lat_grid[0] )
5583  {
5584  lat0 = lat_grid[0];
5585  ilat0 = 0;
5586  istep = 1;
5587  }
5588  else if( rte_pos[1] >= lat_grid[nlat-1] )
5589  {
5590  lat0 = lat_grid[nlat-1];
5591  ilat0 = nlat-1;
5592  istep = -1;
5593  }
5594  else
5595  {
5596  lat0 = rte_pos[1];
5597  if( rte_los[0] >= 0 )
5598  {
5599  ilat0 = gridpos2gridrange( gp_lat, 1 );
5600  istep = 1;
5601  }
5602  else
5603  {
5604  ilat0 = gridpos2gridrange( gp_lat, 0 ) + 1;
5605  istep = -1;
5606  }
5607  }
5608 
5609 
5610  // Loop until entrance point is found
5611  Index ready = 0;
5612  while( !ready )
5613  {
5614  // The slope c is here calculated for the viewing
5615  // direction and the zenith angle to apply shall then be
5616  // positive.
5617 
5618  // Calculate radius and zenith angle of path at lat0
5619  double r0 = geompath_r_at_lat( ppath.constant,
5620  rte_pos[1], rte_los[0], lat0 );
5621  double za0 = abs( geompath_za_at_r( ppath.constant,
5622  rte_los[0], r0 ) );
5623 
5624  // Calculate radius and slope to use in plevel_crossing_2d
5625  double rv1 = r_geoid(ilat0,0) + z_field(np-1,ilat0,0);
5626  double rv2 = r_geoid(ilat0+istep,0) +
5627  z_field(np-1,ilat0+istep,0);
5628  double latstep = abs( lat_grid[ilat0+istep] -
5629  lat_grid[ilat0] );
5630  double c = ( rv2 - rv1 ) / latstep;
5631 
5632  if( lat0 != lat_grid[ilat0] )
5633  { rv1 = rv1 + c * abs( lat0 - lat_grid[ilat0] ); }
5634 
5635  double dlat = plevel_crossing_2d( r0, za0, rv1, c );
5636 
5637  if( dlat <= latstep )
5638  {
5639  ready = 1;
5640  ppath.pos(0,1) = lat0 + (double)istep*dlat;
5641  ppath.pos(0,0) = rv1 + c * dlat;
5642  ppath.los(0,0) = geompath_za_at_r( ppath.constant,
5643  rte_los[0], ppath.pos(0,0) );
5644  // Re-use some variables from above
5645  rv1 = r_geoid(ilat0,0);
5646  rv2 = r_geoid(ilat0+istep,0);
5647  c = ( rv2 - rv1 ) / latstep;
5648  ppath.z[0] = ppath.pos(0,0) - ( rv1 + c *
5649  abs( ppath.pos(0,1) - lat_grid[ilat0] ) );
5650  ppath.gp_p[0].idx = np - 2;
5651  ppath.gp_p[0].fd[0] = 1;
5652  ppath.gp_p[0].fd[1] = 0;
5653  gridpos( ppath.gp_lat[0], lat_grid, ppath.pos(0,1) );
5654  }
5655  else
5656  {
5657  ilat0 += istep;
5658  lat0 = lat_grid[ilat0];
5659  }
5660  }
5661 
5662  // Handle possible numerical problems for grid positions
5663  gridpos_check_fd( ppath.gp_p[0] );
5664  gridpos_check_fd( ppath.gp_lat[0] );
5665  }
5666  }
5667  }
5668 
5669  // Set geometrical tangent point position
5670  if( geom_tan_pos.nelem() == 2 )
5671  {
5672  ppath.geom_tan_pos.resize(2);
5673  ppath.geom_tan_pos = geom_tan_pos;
5674  }
5675  } // End 2D
5676 
5677 
5678  //-- 3D ---------------------------------------------------------------------
5679  else
5680  {
5681  // Number of points in the latitude and longitude grids
5682  const Index nlat = lat_grid.nelem();
5683  const Index nlon = lon_grid.nelem();
5684 
5685  // Is the sensor inside the latitude and longitude ranges of the
5686  // model atmosphere, and below the top of the atmosphere? If
5687  // yes, is_inside = true. Store geoid and surface radii, grid
5688  // position and interpolation weights for later use.
5689  //
5690  bool is_inside = false;
5691  double rv_geoid=-1, rv_surface=-1; // -1 to avoid compiler warnings
5692  GridPos gp_lat, gp_lon;
5693  Vector itw(4);
5694  //
5695  if( rte_pos[1] >= lat_grid[0] && rte_pos[1] <= lat_grid[nlat-1] &&
5696  rte_pos[2] >= lon_grid[0] && rte_pos[2] <= lon_grid[nlon-1] )
5697  {
5698  gridpos( gp_lat, lat_grid, rte_pos[1] );
5699  gridpos( gp_lon, lon_grid, rte_pos[2] );
5700  interpweights( itw, gp_lat, gp_lon );
5701 
5702  rv_geoid = interp( itw, r_geoid, gp_lat, gp_lon );
5703  rv_surface = rv_geoid + interp( itw, z_surface, gp_lat, gp_lon );
5704 
5705  out2 << " sensor altitude : " << (rte_pos[0]-rv_geoid)/1e3
5706  << " km\n";
5707 
5708  if( rte_pos[0] < ( rv_geoid + interp( itw,
5709  z_field(np-1,joker,joker), gp_lat, gp_lon ) ) )
5710  { is_inside = true; }
5711  }
5712 
5713  // If downwards, calculate geometrical tangent position. If the tangent
5714  // point is inside the covered latitude range, calculate also the
5715  // geometrical altitude of the tangent point and the top of atmosphere.
5716  //
5717  Array<double> geom_tan_pos(0);
5718  double geom_tan_z=-2, geom_tan_atmtop=-1; // OK values if the variables
5719  // are not set below
5720  if( rte_los[0] > 90 )
5721  {
5722  geom_tan_pos.resize(3);
5723  double dummy;
5724  geompath_tanpos_3d( geom_tan_pos[0], geom_tan_pos[1],
5725  geom_tan_pos[2], dummy, rte_pos[0], rte_pos[1], rte_pos[2],
5726  rte_los[0], rte_los[1],
5727  geometrical_ppc( rte_pos[0], rte_los[0] ) );
5728  resolve_lon( geom_tan_pos[2], lon_grid[0], lon_grid[nlon-1] );
5729  out2 << " geom. tangent radius : " << geom_tan_pos[0] / 1e3
5730  <<" km\n";
5731  out2 << " geom. tangent latitude : " << geom_tan_pos[1]
5732  << "\n";
5733  out2 << " geom. tangent longitude: " << geom_tan_pos[2]
5734  << "\n";
5735  //
5736 
5737  if( geom_tan_pos[1] >= lat_grid[0] &&
5738  geom_tan_pos[1] <= lat_grid[nlat-1] &&
5739  geom_tan_pos[2] >= lon_grid[0] &&
5740  geom_tan_pos[2] <= lon_grid[nlon-1] )
5741  {
5742  GridPos gp_tanlat, gp_tanlon;
5743  Vector itw_tan(4);
5744  gridpos( gp_tanlat, lat_grid, geom_tan_pos[1] );
5745  gridpos( gp_tanlon, lon_grid, geom_tan_pos[2] );
5746  interpweights( itw_tan, gp_tanlat, gp_tanlon );
5747  geom_tan_z = geom_tan_pos[0] -
5748  interp( itw_tan, r_geoid, gp_tanlat, gp_tanlon );
5749  geom_tan_atmtop = interp( itw_tan,
5750  z_field(np-1,joker,joker), gp_tanlat, gp_tanlon );
5751  out2 << " geom. tangent altitude : " << geom_tan_z/1e3
5752  << " km\n";
5753  }
5754  }
5755 
5756  // Put sensor position and LOS in ppath as first guess
5757  ppath.pos(0,0) = rte_pos[0];
5758  ppath.pos(0,1) = rte_pos[1];
5759  ppath.pos(0,2) = rte_pos[2];
5760  ppath.los(0,0) = rte_los[0];
5761  ppath.los(0,1) = rte_los[1];
5762 
5763 
5764  // The sensor is inside the model atmosphere, 3D ------------------------
5765  if( is_inside )
5766  {
5767  // Check that the sensor is above the surface
5768  if( (rte_pos[0] + RTOL) < rv_surface )
5769  {
5770  ostringstream os;
5771  os << "The ppath starting point is placed "
5772  << (rv_surface - rte_pos[0])/1e3 << " km below surface level.\n"
5773  << "This point must be above the surface.";
5774  throw runtime_error(os.str());
5775  }
5776 
5777  // Check that not at latitude end point and looks out
5778  if( rte_pos[1] > -90 && rte_pos[1] == lat_grid[0] &&
5779  abs( rte_los[1] > 90 ) )
5780  throw runtime_error( "The sensor is at the lower latitude end "
5781  "point and the absolute value of the azimuth angle > 90." );
5782  if( rte_pos[1] < 90 && rte_pos[1] == lat_grid[nlat-1] &&
5783  abs( rte_los[1] ) <= 90 )
5784  throw runtime_error( "The sensor is at the upper latitude end "
5785  "point and the absolute value of the azimuth angle <= 90." );
5786 
5787  // Check that not at longitude end point and looks out
5788  if( rte_pos[2] == lon_grid[0] && rte_los[1] < 0 )
5789  throw runtime_error( "The sensor is at the lower longitude end "
5790  "point and the azimuth angle < 0." );
5791  if( rte_pos[2] == lon_grid[nlon-1] && rte_los[1] > 0 )
5792  throw runtime_error( "The sensor is at the upper longitude end "
5793  "point and the azimuth angle > 0." );
5794 
5795  // Geometrical altitude
5796  ppath.z[0] = ppath.pos(0,0) - rv_geoid;
5797 
5798  // Use below the values in ppath (instead of rte_pos and rte_los) as
5799  // they can be modified on the way.
5800 
5801  // Grid positions
5802  ppath.gp_lat[0].idx = gp_lat.idx;
5803  ppath.gp_lat[0].fd[0] = gp_lat.fd[0];
5804  ppath.gp_lat[0].fd[1] = gp_lat.fd[1];
5805  ppath.gp_lon[0].idx = gp_lon.idx;
5806  ppath.gp_lon[0].fd[0] = gp_lon.fd[0];
5807  ppath.gp_lon[0].fd[1] = gp_lon.fd[1];
5808 
5809  // Create a vector with the geometrical altitude of the pressure
5810  // levels for the sensor latitude and use it to get ppath.gp_p.
5811  Vector z_grid(np);
5812  z_at_latlon( z_grid, p_grid, lat_grid, lon_grid, z_field,
5813  gp_lat, gp_lon );
5814  gridpos( ppath.gp_p, z_grid, ppath.z );
5815 
5816  // Is the sensor on the surface looking down?
5817  if( ppath.pos(0,0) == rv_surface )
5818  {
5819  // Calculate radial slope of the surface
5820  const double rslope = plevel_slope_3d( lat_grid, lon_grid,
5821  r_geoid, z_surface, gp_lat, gp_lon, ppath.los(0,1) );
5822 
5823  // Calculate angular tilt of the surface
5824  const double atilt = plevel_angletilt( rv_surface, rslope);
5825 
5826  // Are we looking down into the surface?
5827  // If yes and the sensor is inside the cloudbox, the background
5828  // will be changed below.
5829  if( is_los_downwards( ppath.los(0,0), atilt ) )
5830  { ppath_set_background( ppath, 2 ); }
5831  }
5832 
5833  // Check sensor position with respect to cloud box.
5834  if( cloudbox_on )
5835  {
5836  // To check all possible cases here when the sensor is at the
5837  // level and can either look into or out from the box needs
5838  // a lot of coding.
5839  // So we are instead sloppy and set all cases when the sensor
5840  // is inside or at the level to be inside the box.
5841  // The neglected cases should be very unlikely in for real
5842  // situations.
5843 
5844  if( ppath.pos(0,1) >= lat_grid[cloudbox_limits[2]] &&
5845  ppath.pos(0,1) <= lat_grid[cloudbox_limits[3]] &&
5846  ppath.pos(0,2) >= lon_grid[cloudbox_limits[4]] &&
5847  ppath.pos(0,2) <= lon_grid[cloudbox_limits[5]] )
5848  {
5849  // Calculate the lower and upper altitude radius limit for
5850  // the cloud box at the latitude of the sensor
5851 
5852  const double rv_low = rv_geoid + interp( itw,
5853  z_field(cloudbox_limits[0],joker,joker), gp_lat, gp_lon );
5854  const double rv_upp = rv_geoid + interp( itw,
5855  z_field(cloudbox_limits[1],joker,joker), gp_lat, gp_lon );
5856 
5857  if( ppath.pos(0,0) >= rv_low && ppath.pos(0,0) <= rv_upp )
5858  {
5859  if( outside_cloudbox )
5860  { ppath_set_background( ppath, 4 ); }
5861  }
5862  else if( ppath.pos(0,0) <= rv_low-RTOL &&
5863  ppath.pos(0,0) >= rv_upp+RTOL )
5864  { assert( outside_cloudbox ); }
5865  }
5866  else
5867  { assert( outside_cloudbox ); }
5868  }
5869 
5870  // Handle possible numerical problems for grid positions
5871  gridpos_check_fd( ppath.gp_p[0] );
5872  gridpos_check_fd( ppath.gp_lat[0] );
5873  gridpos_check_fd( ppath.gp_lon[0] );
5874  }
5875 
5876  // The sensor is outside the model atmosphere, 3D -----------------------
5877  else
5878  {
5879  // Handle cases when the sensor appears to look the wrong way in
5880  // the north-south direction
5881  if( ( rte_pos[1] <= lat_grid[0] && abs( rte_los[1] ) >= 90 ) ||
5882  ( rte_pos[1] >= lat_grid[nlat-1] && abs( rte_los[1] ) <= 90 ) )
5883  {
5884  ostringstream os;
5885  os << "The sensor is north or south (or at the limit) of the "
5886  << "model atmosphere but\nlooks in the wrong direction.\n";
5887  throw runtime_error( os.str() );
5888  }
5889 
5890  // Handle cases when the sensor appears to look the wrong way in
5891  // the west-east direction. We demand that the sensor is inside the
5892  // range of lon_grid even if all longitudes are covered.
5893  if( ( rte_pos[2] <= lon_grid[0] && rte_los[1] < 0 ) ||
5894  ( rte_pos[2] >= lon_grid[nlon-1] && rte_los[1] > 0 ) )
5895  {
5896  ostringstream os;
5897  os << "The sensor is east or west (or at the limit) of the "
5898  << "model atmosphere but\nlooks in the wrong direction.\n";
5899  throw runtime_error( os.str() );
5900  }
5901 
5902  // Upward:
5903  if( abs(rte_los[0]) <= 90 )
5904  {
5905  ppath_set_background( ppath, 1 );
5906  out1 << " ------- WARNING -------: path is totally outside of "
5907  << "the model atmosphere\n";
5908  }
5909 
5910  // Downward:
5911  else
5912  {
5913 
5914  // We can here set the path constant, that equals the radius of
5915  // the geometrical tangent point.
5916  ppath.constant = geom_tan_pos[0];
5917 
5918 
5919  // We either checks that:
5920  // 1. the tangent point is above the atmosphere, inside covered
5921  // lat and lon ranges
5922  // 2. We try to determine the entrance point, and issues an error
5923  // if it is not at the top of the atmosphere.
5924 
5925  // Is tangent point above the model atmosphere
5926  if( geom_tan_pos[1] >= lat_grid[0] &&
5927  geom_tan_pos[1] <= lat_grid[nlat-1] &&
5928  geom_tan_pos[2] >= lon_grid[0] &&
5929  geom_tan_pos[2] <= lon_grid[nlon-1] &&
5930  geom_tan_z >= geom_tan_atmtop )
5931  {
5932  ppath_set_background( ppath, 1 );
5933  out1 << " ------- WARNING -------: path is totally "
5934  << "outside of the model atmosphere\n";
5935  }
5936 
5937  // The path: does it enter the atmosphere, and then where?
5938  else
5939  {
5940  // Create a matrix for top of the atmosphere radii
5941  Matrix r_atmtop(nlat,nlon);
5942  //
5943  for( Index ilat=0; ilat<nlat; ilat++ )
5944  {
5945  for( Index ilon=0; ilon<nlon; ilon++ )
5946  { r_atmtop(ilat,ilon) = r_geoid(ilat,ilon) +
5947  z_field(np-1,ilat,ilon); }
5948  }
5949 
5950  // Handle the case when the sensor radius is obviously too low
5951  double rtopmin = min( r_atmtop );
5952  if( rte_pos[0] <= rtopmin )
5953  {
5954  ostringstream os;
5955  os << "The sensor radius is smaller than the minimum "
5956  << "radius of the top of\nthe atmosphere. This gives "
5957  << "no possible OK entrance point for the path.";
5958  throw runtime_error( os.str() );
5959  }
5960 
5961  // Handle the case when the path clearly passes above the
5962  // model atmosphere
5963  double rtopmax = max( r_atmtop );
5964  if( geom_tan_pos[0] >= rtopmax )
5965  {
5966  ostringstream os;
5967  os << "The combination of sensor position and line-of-"
5968  << "sight gives a\npropagation path that goes above "
5969  << "the model atmosphere, with\na tangent point "
5970  << "outside the covered latitude and longitude "
5971  << "ranges.\nThe position of the "
5972  << "tangent point is:\n lat : " << geom_tan_pos[1]
5973  << "\n lon : " << geom_tan_pos[2];
5974  throw runtime_error( os.str() );
5975  }
5976 
5977  // Sensor pos and LOS in cartesian coordinates
5978  double x, y, z, dx, dy, dz;
5979  poslos2cart( x, y, z, dx, dy, dz, rte_pos[0],
5980  rte_pos[1], rte_pos[2], rte_los[0], rte_los[1] );
5981 
5982  // Boolean for that entrance point CANNOT be found
5983  bool failed = false;
5984 
5985  // Determine the entrance point for the minimum of *r_atmtop*
5986  double r_top, lat_top, lon_top, l_top;
5987  plevel_crossing_3d( r_top, lat_top, lon_top, l_top,
5988  rtopmin, rte_pos[0], rte_pos[1], rte_pos[2],
5989  rte_los[0], x, y, z, dx, dy, dz );
5990  resolve_lon( lon_top, lon_grid[0], lon_grid[nlon-1] );
5991 
5992  // If no crossing was found for min radius, or it is outside
5993  // covered lat and lon ranges, try max radius and make the
5994  // same check. If check not succesful, there is no OK
5995  // entrance point.
5996  if( lat_top < lat_grid[0] || lat_top > lat_grid[nlat-1] ||
5997  lon_top < lon_grid[0] || lon_top > lon_grid[nlon-1] )
5998  {
5999  plevel_crossing_3d( r_top, lat_top, lon_top, l_top,
6000  rtopmax, rte_pos[0], rte_pos[1], rte_pos[2],
6001  rte_los[0], x, y, z, dx, dy, dz );
6002  resolve_lon( lon_top, lon_grid[0], lon_grid[nlon-1] );
6003 
6004  if( lat_top < lat_grid[0] || lat_top > lat_grid[nlat-1] ||
6005  lon_top < lon_grid[0] || lon_top > lon_grid[nlon-1] )
6006  { failed = true; }
6007  }
6008 
6009  // Search iteratively for the entrance point until
6010  // convergence or moving out from covered lat and lon ranges
6011  //
6012  bool ready = false;
6013  //
6014  while( !failed && !ready )
6015  {
6016  // Determine radius at found lat and lon
6017  GridPos gp_lattop, gp_lontop;
6018  double lat1, lat3, lon5, lon6, r15, r35, r36, r16;
6019  Index ilat, ilon;
6020  gridpos( gp_lattop, lat_grid, lat_top );
6021  ilat = gridpos2gridrange( gp_lattop, abs(rte_los[1])>=90);
6022  gridpos( gp_lontop, lon_grid, lon_top );
6023  ilon = gridpos2gridrange( gp_lontop, rte_los[1] > 0 );
6024  r15 = r_geoid(ilat,ilon) + z_field(np-1,ilat,ilon);
6025  r35 = r_geoid(ilat+1,ilon) + z_field(np-1,ilat+1,ilon);
6026  r36 = r_geoid(ilat+1,ilon+1) + z_field(np-1,ilat+1,ilon+1);
6027  r16 = r_geoid(ilat,ilon+1) + z_field(np-1,ilat,ilon+1);
6028  lat1 = lat_grid[ilat];
6029  lat3 = lat_grid[ilat+1];
6030  lon5 = lon_grid[ilon];
6031  lon6 = lon_grid[ilon+1];
6032  r_top = rsurf_at_latlon( lat1, lat3, lon5, lon6,
6033  r15, r35, r36, r16, lat_top, lon_top );
6034 
6035  // Determine new entrance position for latest estimated
6036  // entrance radius
6037  double lat_top2, lon_top2;
6038  plevel_crossing_3d( r_top, lat_top2, lon_top2, l_top,
6039  r_top, rte_pos[0], rte_pos[1], rte_pos[2],
6040  rte_los[0], x, y, z, dx, dy, dz );
6041  resolve_lon( lon_top2, lon_grid[0], lon_grid[nlon-1] );
6042 
6043  // Check if new position is sufficiently close to old
6044  if( abs( lat_top2 - lat_top ) < 1e-6 &&
6045  abs( lon_top2 - lon_top ) < 1e-6 )
6046  { ready = true; }
6047 
6048  // Have we moved outside covered lat or lon range?
6049  else if( lat_top < lat_grid[0] ||
6050  lat_top > lat_grid[nlat-1] ||
6051  lon_top < lon_grid[0] ||
6052  lon_top > lon_grid[nlon-1] )
6053  { failed = true; }
6054 
6055  lat_top = lat_top2;
6056  lon_top = lon_top2;
6057  }
6058 
6059  if( failed )
6060  {
6061  ostringstream os;
6062  os << "The path does not enter the model atmosphere at "
6063  << "the top.\nThe path reaches the top of the "
6064  << "atmosphere altitude\napproximately at the "
6065  << "position:\n lat : " << lat_top << "\n lon : "
6066  << lon_top;
6067  throw runtime_error( os.str() );
6068  }
6069 
6070  // Correct found lat and lon for some special cases
6071  //
6072  if( rte_los[0] == 180 )
6073  {
6074  lat_top = rte_pos[1];
6075  lon_top = rte_pos[2];
6076  }
6077  else if( abs( rte_pos[1] ) < 90 && ( abs(rte_los[1]) < ANGTOL
6078  || abs( rte_los[1] ) > 180-ANGTOL ) )
6079  { lon_top = rte_pos[2]; }
6080 
6081  // Move found values to *ppath*
6082  //
6083  // Position
6084  ppath.pos(0,0) = r_top;
6085  ppath.pos(0,1) = lat_top;
6086  ppath.pos(0,2) = lon_top;
6087  //
6088  // Grid position
6089  ppath.gp_p[0].idx = np - 2;
6090  ppath.gp_p[0].fd[0] = 1;
6091  ppath.gp_p[0].fd[1] = 0;
6092  gridpos( ppath.gp_lat[0], lat_grid, lat_top );
6093  gridpos( ppath.gp_lon[0], lon_grid, lon_top );
6094  //
6095  // Geometrical altitude
6096  Vector itw2(4);
6097  interpweights( itw2, ppath.gp_lat[0], ppath.gp_lon[0] );
6098  ppath.z[0] = ppath.pos(0,0) - interp(itw2, r_geoid,
6099  ppath.gp_lat[0], ppath.gp_lon[0] );
6100  //
6101  // LOS
6102  double zatmp, aatmp;
6103  cart2poslos( r_top, lat_top, lon_top, zatmp, aatmp,
6104  x+dx*l_top, y+dy*l_top, z+dz*l_top, dx, dy, dz );
6105  ppath.los(0,0) = zatmp;
6106  ppath.los(0,1) = aatmp;
6107  //
6108  // Correct found LOS for some special cases
6109  if( rte_los[0] == 180 )
6110  { ppath.los(0,0) = 180; }
6111  if( abs( rte_pos[1] ) < 90 && ( abs( rte_los[1] ) < ANGTOL
6112  || abs( rte_los[1] ) > 180-ANGTOL ) )
6113  { ppath.los(0,1) = rte_los[1]; }
6114  }
6115 
6116  // Handle possible numerical problems for grid positions
6117  gridpos_check_fd( ppath.gp_p[0] );
6118  gridpos_check_fd( ppath.gp_lat[0] );
6119  gridpos_check_fd( ppath.gp_lon[0] );
6120  } // else
6121 
6122  }
6123 
6124  // Set geometrical tangent point position
6125  if( geom_tan_pos.nelem() == 3 )
6126  {
6127  ppath.geom_tan_pos.resize(3);
6128  ppath.geom_tan_pos[0] = geom_tan_pos[0];
6129  ppath.geom_tan_pos[1] = geom_tan_pos[1];
6130  ppath.geom_tan_pos[2] = geom_tan_pos[2];
6131  }
6132  } // End 3D
6133 }
6134 
6135 
6136 
6137 
6138 
6140 
6172  // WS Output:
6173  Ppath& ppath,
6174  // WS Input:
6175  const Agenda& ppath_step_agenda,
6176  const Index& atmosphere_dim,
6177  const Vector& p_grid,
6178  const Vector& lat_grid,
6179  const Vector& lon_grid,
6180  const Tensor3& z_field,
6181  const Matrix& r_geoid,
6182  const Matrix& z_surface,
6183  const Index& cloudbox_on,
6184  const ArrayOfIndex& cloudbox_limits,
6185  const Vector& rte_pos,
6186  const Vector& rte_los,
6187  const bool& outside_cloudbox,
6188  const Verbosity& verbosity)
6189 {
6190  CREATE_OUT2
6191 
6192  // This function is a WSM but it is normally only called from RteCalc.
6193  // For that reason, this function does not repeat input checks that are
6194  // performed in RteCalc, it only performs checks regarding the sensor
6195  // position and LOS.
6196 
6197  //--- Check input -----------------------------------------------------------
6198 
6199  // Sensor position and LOS
6200  //
6201  chk_vector_length( "rte_pos", rte_pos, atmosphere_dim );
6202  if( atmosphere_dim == 1 )
6203  {
6204  chk_vector_length( "rte_los", rte_los, 1 );
6205  chk_if_in_range( "sensor zenith angle", rte_los[0], 0., 180. );
6206  }
6207  else if( atmosphere_dim == 2 )
6208  {
6209  chk_vector_length( "rte_los", rte_los, 1 );
6210  chk_if_in_range( "sensor zenith angle", rte_los[0], -180., 180. );
6211  }
6212  else
6213  {
6214  chk_if_in_range( "sensor latitude", rte_pos[1], -90., 90. );
6215  chk_if_in_range( "sensor longitude", rte_pos[2], -360., 360. );
6216  chk_vector_length( "rte_los", rte_los, 2 );
6217  chk_if_in_range( "sensor zenith angle", rte_los[0], 0., 180. );
6218  chk_if_in_range( "sensor azimuth angle", rte_los[1], -180., 180. );
6219  }
6220  assert( outside_cloudbox || cloudbox_on );
6221 
6222  //--- End: Check input ------------------------------------------------------
6223 
6224 
6225  // Some messages
6226  out2 << " -------------------------------------\n";
6227  out2 << " sensor radius : " << rte_pos[0]/1e3 << " km\n";
6228  if( atmosphere_dim >= 2 )
6229  out2 << " sensor latitude : " << rte_pos[1] << "\n";
6230  if( atmosphere_dim == 3 )
6231  out2 << " sensor longitude : " << rte_pos[2] << "\n";
6232  out2 << " sensor zenith angle : " << rte_los[0] << "\n";
6233  if( atmosphere_dim == 3 )
6234  out2 << " sensor azimuth angle : " << rte_los[1] << "\n";
6235 
6236 
6237  // Initiate the partial Ppath structure.
6238  // The function doing the work sets ppath_step to the point of the path
6239  // inside the atmosphere closest to the sensor, if the path is at all inside
6240  // the atmosphere.
6241  // If the background field is set by the function this flags that there is no
6242  // path to follow (for example when the sensor is inside the cloud box).
6243  // The function checks also that the sensor position and LOS give an
6244  // allowed path.
6245  //
6246  Ppath ppath_step;
6247  //
6248  ppath_start_stepping( ppath_step, atmosphere_dim, p_grid, lat_grid,
6249  lon_grid, z_field, r_geoid, z_surface,
6250  cloudbox_on, cloudbox_limits, outside_cloudbox, rte_pos, rte_los,
6251  verbosity );
6252 
6253  out2 << " -------------------------------------\n";
6254 
6255  // Perform propagation path steps until the starting point is found, which
6256  // is flagged by ppath_step by setting the background field.
6257  //
6258  // The results of each step, returned by ppath_step_agenda as a new
6259  // ppath_step, are stored as an array of Ppath structures.
6260  //
6261  Array<Ppath> ppath_array;
6262  ppath_array.push_back( ppath_step );
6263  //
6264  Index np = ppath_step.np; // Counter for number of points of the path
6265  Index istep = 0; // Counter for number of steps
6266  //
6267  const Index imax_p = p_grid.nelem() - 1;
6268  const Index imax_lat = lat_grid.nelem() - 1;
6269  const Index imax_lon = lon_grid.nelem() - 1;
6270 
6271  while( !ppath_what_background( ppath_step ) )
6272  {
6273  // Call ppath_step agenda.
6274  // The new path step is added to *ppath_array* last in the while block
6275  //
6276  istep++;
6277  //
6278  ppath_step_agendaExecute( ws, ppath_step, atmosphere_dim, p_grid,
6279  lat_grid, lon_grid, z_field, r_geoid, z_surface,
6280  ppath_step_agenda );
6281 
6282  // Number of points in returned path step
6283  const Index n = ppath_step.np;
6284 
6285  // Increase the total number
6286  np += n - 1;
6287 
6288  if( istep > 10e3 )
6289  throw runtime_error(
6290  "10 000 path points have been reached. Is this an infinite loop?" );
6291 
6292 
6293  //----------------------------------------------------------------------
6294  //--- Check if some boundary is reached
6295  //----------------------------------------------------------------------
6296 
6297  //--- Outside cloud box ------------------------------------------------
6298  if( outside_cloudbox )
6299  {
6300 
6301  // Check if the top of the atmosphere is reached
6302  if( is_gridpos_at_index_i( ppath_step.gp_p[n-1], imax_p ) )
6303  {
6304  ppath_set_background( ppath_step, 1 );
6305  }
6306 
6307  // Check that path does not exit at a latitude or longitude end face
6308  if( atmosphere_dim == 2 )
6309  {
6310  // Latitude
6311  if( is_gridpos_at_index_i( ppath_step.gp_lat[n-1], 0 ) )
6312  {
6313  ostringstream os;
6314  os << "The path exits the atmosphere through the lower "
6315  << "latitude end face.\nThe exit point is at an altitude"
6316  << "of " << ppath_step.z[n-1]/1e3 << " km.";
6317  throw runtime_error( os.str() );
6318  }
6319  if( is_gridpos_at_index_i( ppath_step.gp_lat[n-1], imax_lat ) )
6320  {
6321  ostringstream os;
6322  os << "The path exits the atmosphere through the upper "
6323  << "latitude end face.\nThe exit point is at an altitude"
6324  << " of " << ppath_step.z[n-1]/1e3 << " km.";
6325  throw runtime_error( os.str() );
6326  }
6327  }
6328  if( atmosphere_dim == 3 )
6329  {
6330  // Latitude
6331  if( lat_grid[0] > -90 &&
6332  is_gridpos_at_index_i( ppath_step.gp_lat[n-1], 0 ) )
6333  {
6334  ostringstream os;
6335  os << "The path exits the atmosphere through the lower "
6336  << "latitude end face.\nThe exit point is at an altitude"
6337  << " of " << ppath_step.z[n-1]/1e3 << " km.";
6338  throw runtime_error( os.str() );
6339  }
6340  if( lat_grid[imax_lat] < 90 &&
6341  is_gridpos_at_index_i( ppath_step.gp_lat[n-1], imax_lat ) )
6342  {
6343  ostringstream os;
6344  os << "The path exits the atmosphere through the upper"
6345  << "latitude end face.\nThe exit point is at an altitude"
6346  << " of " << ppath_step.z[n-1]/1e3 << " km.";
6347  throw runtime_error( os.str() );
6348  }
6349 
6350  // Longitude
6351  // Note that it must be if and else if here. Otherwise e.g. -180
6352  // will be shifted to 180 and then later back to -180.
6353  if( is_gridpos_at_index_i( ppath_step.gp_lon[n-1], 0 ) &&
6354  ppath_step.los(n-1,1) < 0 &&
6355  abs( ppath_step.pos(n-1,1) ) < 90 )
6356  {
6357  // Check if the longitude point can be shifted +360 degrees
6358  if( lon_grid[imax_lon] - lon_grid[0] >= 360 )
6359  {
6360  ppath_step.pos(n-1,2) = ppath_step.pos(n-1,2) + 360;
6361  gridpos( ppath_step.gp_lon[n-1], lon_grid,
6362  ppath_step.pos(n-1,2) );
6363  }
6364  else
6365  {
6366  ostringstream os;
6367  os << "The path exits the atmosphere through the lower "
6368  << "longitude end face.\nThe exit point is at an "
6369  << "altitude of " << ppath_step.z[n-1]/1e3 << " km.";
6370  throw runtime_error( os.str() );
6371  }
6372  }
6373  else if(
6374  is_gridpos_at_index_i( ppath_step.gp_lon[n-1], imax_lon ) &&
6375  ppath_step.los(n-1,1) > 0 &&
6376  abs( ppath_step.pos(n-1,1) ) < 90 )
6377  {
6378  // Check if the longitude point can be shifted -360 degrees
6379  if( lon_grid[imax_lon] - lon_grid[0] >= 360 )
6380  {
6381  ppath_step.pos(n-1,2) = ppath_step.pos(n-1,2) - 360;
6382  gridpos( ppath_step.gp_lon[n-1], lon_grid,
6383  ppath_step.pos(n-1,2) );
6384  }
6385  else
6386  {
6387  ostringstream os;
6388  os << "The path exits the atmosphere through the upper "
6389  << "longitude end face.\nThe exit point is at an "
6390  << "altitude of " << ppath_step.z[n-1]/1e3 << " km.";
6391  throw runtime_error( os.str() );
6392  }
6393  }
6394  }
6395 
6396 
6397  // Check if there is an intersection with an active cloud box
6398  if( cloudbox_on )
6399  {
6400  double ipos = fractional_gp( ppath_step.gp_p[n-1] );
6401 
6402  if( ipos >= double( cloudbox_limits[0] ) &&
6403  ipos <= double( cloudbox_limits[1] ) )
6404  {
6405  if( atmosphere_dim == 1 )
6406  { ppath_set_background( ppath_step, 3 ); }
6407  else
6408  {
6409  ipos = fractional_gp( ppath_step.gp_lat[n-1] );
6410 
6411  if( ipos >= double( cloudbox_limits[2] ) &&
6412  ipos <= double( cloudbox_limits[3] ) )
6413  {
6414  if( atmosphere_dim == 2 )
6415  { ppath_set_background( ppath_step, 3 ); }
6416  else
6417  {
6418 
6419  ipos = fractional_gp( ppath_step.gp_lon[n-1] );
6420 
6421  if( ipos >= double( cloudbox_limits[4] ) &&
6422  ipos <= double( cloudbox_limits[5] ) )
6423  { ppath_set_background( ppath_step, 3 ); }
6424  }
6425  }
6426  }
6427  }
6428  }
6429  }
6430 
6431  //--- Inside cloud box -------------------------------------------------
6432  else
6433  {
6434  // A first version just checked if point was at or outside any
6435  // boundary but numerical problems could cause that the start point
6436  // was taken as the exit point. So check of ppath direction had to be
6437  // added. Fractional distances used for this.
6438 
6439  // Pressure dimension
6440  double ipos1 = fractional_gp( ppath_step.gp_p[n-1] );
6441  double ipos2 = fractional_gp( ppath_step.gp_p[n-2] );
6442  if( ipos1 <= double( cloudbox_limits[0] ) && ipos1 < ipos2 )
6443  { ppath_set_background( ppath_step, 3 ); }
6444 
6445  else if( ipos1 >= double( cloudbox_limits[1] ) && ipos1 > ipos2 )
6446  { ppath_set_background( ppath_step, 3 ); }
6447 
6448  else if( atmosphere_dim > 1 )
6449  {
6450  // Latitude dimension
6451  ipos1 = fractional_gp( ppath_step.gp_lat[n-1] );
6452  ipos2 = fractional_gp( ppath_step.gp_lat[n-2] );
6453  if( ipos1 <= double( cloudbox_limits[2] ) && ipos1 < ipos2 )
6454  { ppath_set_background( ppath_step, 3 ); }
6455 
6456  else if( ipos1 >= double( cloudbox_limits[3] ) && ipos1 > ipos2 )
6457  { ppath_set_background( ppath_step, 3 ); }
6458 
6459  else if ( atmosphere_dim > 2 )
6460  {
6461  // Longitude dimension
6462  ipos1 = fractional_gp( ppath_step.gp_lon[n-1] );
6463  ipos2 = fractional_gp( ppath_step.gp_lon[n-2] );
6464  if( ipos1 <= double( cloudbox_limits[4] ) && ipos1 < ipos2 )
6465  { ppath_set_background( ppath_step, 3 ); }
6466 
6467  else if( ipos1 >= double( cloudbox_limits[5] ) &&
6468  ipos1 > ipos2 )
6469  { ppath_set_background( ppath_step, 3 ); }
6470  }
6471  }
6472  }
6473  //----------------------------------------------------------------------
6474  //--- End of boundary check
6475  //----------------------------------------------------------------------
6476 
6477 
6478  // Put new ppath_step in ppath_array
6479  ppath_array.push_back( ppath_step );
6480 
6481  } // End path steps
6482 
6483  // Combine all structures in ppath_array to form the return Ppath structure.
6484  //
6485  ppath_init_structure( ppath, atmosphere_dim, np );
6486  //
6487  np = 0; // Now used as counter for points moved to ppath
6488  //
6489  for( Index i=0; i<ppath_array.nelem(); i++ )
6490  {
6491  // For the first structure, the first point shall be included, but the
6492  // first structure can also be empty.
6493  // For later structures, the first point shall not be included, but
6494  // there will always be at least two points.
6495  // Only the first structure can be empty.
6496 
6497  Index n = ppath_array[i].np;
6498 
6499  if( n )
6500  {
6501  // First index to include
6502  Index i1 = 1;
6503  if( i == 0 )
6504  { i1 = 0; }
6505  else
6506  { assert( n > 1 ); }
6507 
6508  // Vectors and matrices that can be handled by ranges.
6509  ppath.z[ Range(np,n-i1) ] = ppath_array[i].z[ Range(i1,n-i1) ];
6510  ppath.pos( Range(np,n-i1), joker ) =
6511  ppath_array[i].pos( Range(i1,n-i1), joker );
6512  ppath.los( Range(np,n-i1), joker ) =
6513  ppath_array[i].los( Range(i1,n-i1), joker );
6514 
6515  // For i==1, there is no defined l_step. For higher i, all
6516  // values in l_step shall be copied.
6517  if( i > 0 )
6518  { ppath.l_step[ Range(np-1,n-1) ] = ppath_array[i].l_step; }
6519 
6520  // Grid positions must be handled by a loop
6521  for( Index j=i1; j<n; j++ )
6522  { ppath.gp_p[np+j-i1] = ppath_array[i].gp_p[j]; }
6523  if( atmosphere_dim >= 2 )
6524  {
6525  for( Index j=i1; j<n; j++ )
6526  { ppath.gp_lat[np+j-i1] = ppath_array[i].gp_lat[j]; }
6527  }
6528  if( atmosphere_dim == 3 )
6529  {
6530  for( Index j=i1; j<n; j++ )
6531  { ppath.gp_lon[np+j-i1] = ppath_array[i].gp_lon[j]; }
6532  }
6533 
6534  // Fields just set once
6535  if( ppath_array[i].tan_pos.nelem() )
6536  {
6537  ppath.tan_pos.resize( ppath_array[i].tan_pos.nelem() );
6538  ppath.tan_pos = ppath_array[i].tan_pos;
6539  }
6540  if( ppath_array[i].geom_tan_pos.nelem() )
6541  {
6542  ppath.geom_tan_pos.resize( ppath_array[i].tan_pos.nelem() );
6543  ppath.geom_tan_pos = ppath_array[i].geom_tan_pos;
6544  }
6545 
6546  // Increase number of points done
6547  np += n - i1;
6548 
6549  }
6550  }
6551  ppath.background = ppath_step.background;
6552 }
geompath_r_at_lat
double geompath_r_at_lat(const double &ppc, const double &lat0, const double &za0, const double &lat)
geompath_r_at_lat
Definition: ppath.cc:310
Matrix
The Matrix class.
Definition: matpackI.h:767
ppath_calc
void ppath_calc(Workspace &ws, Ppath &ppath, const Agenda &ppath_step_agenda, const Index &atmosphere_dim, const Vector &p_grid, const Vector &lat_grid, const Vector &lon_grid, const Tensor3 &z_field, const Matrix &r_geoid, const Matrix &z_surface, const Index &cloudbox_on, const ArrayOfIndex &cloudbox_limits, const Vector &rte_pos, const Vector &rte_los, const bool &outside_cloudbox, const Verbosity &verbosity)
ppath_calc
Definition: ppath.cc:6171
plevel_crossing_3d
void plevel_crossing_3d(double &r, double &lat, double &lon, double &l, const double r_surf, const double r_start, const double lat_start, const double lon_start, const double za_start, const double &x, const double &y, const double &z, const double &dx, const double &dy, const double &dz)
plevel_crossing_3d
Definition: ppath.cc:1599
ppath_init_structure
void ppath_init_structure(Ppath &ppath, const Index &atmosphere_dim, const Index &np)
ppath_init_structure
Definition: ppath.cc:2461
ppath_step_agendaExecute
void ppath_step_agendaExecute(Workspace &ws, Ppath &ppath_step, const Index atmosphere_dim, const Vector &p_grid, const Vector &lat_grid, const Vector &lon_grid, const Tensor3 &z_field, const Matrix &r_geoid, const Matrix &z_surface, const Agenda &input_agenda)
Definition: auto_md.cc:10292
ppath_start_stepping
void ppath_start_stepping(Ppath &ppath, const Index &atmosphere_dim, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstMatrixView r_geoid, ConstMatrixView z_surface, const Index &cloudbox_on, const ArrayOfIndex &cloudbox_limits, const bool &outside_cloudbox, ConstVectorView rte_pos, ConstVectorView rte_los, const Verbosity &verbosity)
ppath_start_stepping
Definition: ppath.cc:5164
auto_md.h
gridpos_copy
void gridpos_copy(GridPos &gp_new, const GridPos &gp_old)
gridpos_copy
Definition: interpolation.cc:482
geompath_tanpos_3d
void geompath_tanpos_3d(double &r_tan, double &lat_tan, double &lon_tan, double &l_tan, const double &r, const double &lat, const double &lon, const double &za, const double &aa, const double &ppc)
geompath_tanpos_3d
Definition: ppath.cc:784
gridpos
void gridpos(ArrayOfGridPos &gp, ConstVectorView old_grid, ConstVectorView new_grid, const Numeric &extpolfac)
Set up a grid position Array.
Definition: interpolation.cc:167
is_gridpos_at_index_i
bool is_gridpos_at_index_i(const GridPos &gp, const Index &i)
is_gridpos_at_index_i
Definition: interpolation.cc:665
Tensor3
The Tensor3 class.
Definition: matpackIII.h:340
zaaa2cart
void zaaa2cart(double &dx, double &dy, double &dz, const double &za, const double &aa)
zaaa2cart
Definition: ppath.cc:834
gridpos_check_fd
void gridpos_check_fd(GridPos &gp)
gridpos_check_fd
Definition: interpolation.cc:524
ppath_end_2d
void ppath_end_2d(Ppath &ppath, ConstVectorView r_v, ConstVectorView lat_v, ConstVectorView za_v, const double &lstep, ConstVectorView lat_grid, ConstMatrixView z_field, ConstVectorView r_geoid, const Index &ip, const Index &ilat, const Index &endface, const Index &tanpoint, const double &ppc)
ppath_end_2d
Definition: ppath.cc:3233
sign
Numeric sign(const Numeric &x)
sign
Definition: math_funcs.cc:473
ppath_append
void ppath_append(Ppath &ppath1, const Ppath &ppath2)
ppath_append
Definition: ppath.cc:2652
ppath_fill_2d
void ppath_fill_2d(Ppath &ppath, ConstVectorView r, ConstVectorView lat, ConstVectorView za, const double &lstep, ConstVectorView r_geoid, ConstMatrixView z_field, ConstVectorView lat_grid, const Index &ip, const Index &ilat)
ppath_fill_2d
Definition: ppath.cc:2782
PTILTMAX
const double PTILTMAX
Definition: ppath.cc:110
raytrace_3d_linear_euler
void raytrace_3d_linear_euler(Workspace &ws, Array< double > &r_array, Array< double > &lat_array, Array< double > &lon_array, Array< double > &za_array, Array< double > &aa_array, Array< double > &l_array, Index &endface, Index &tanpoint, double r, double lat, double lon, double za, double aa, Numeric &rte_pressure, Numeric &rte_temperature, Vector &rte_vmr_list, Numeric &refr_index, const Agenda &refr_index_agenda, const double &lraytrace, const double &lat1, const double &lat3, const double &lon5, const double &lon6, const double &r15a, const double &r35a, const double &r36a, const double &r16a, const double &r15b, const double &r35b, const double &r36b, const double &r16b, const double &rsurface15, const double &rsurface35, const double &rsurface36, const double &rsurface16, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstMatrixView r_geoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field)
raytrace_3d_linear_euler
Definition: ppath.cc:4465
joker
const Joker joker
plevel_crossing_2d
double plevel_crossing_2d(const double &rp, const double &za, const double &r0, double c)
plevel_crossing_2d
Definition: ppath.cc:1462
interp
Numeric interp(ConstVectorView itw, ConstVectorView a, const GridPos &tc)
Red 1D Interpolate.
Definition: interpolation.cc:1000
beta
#define beta
Definition: continua.cc:13828
poslos2cart
void poslos2cart(double &x, double &y, double &z, double &dx, double &dy, double &dz, const double &r, const double &lat, const double &lon, const double &za, const double &aa)
poslos2cart
Definition: ppath.cc:567
geompath_l_at_r
double geompath_l_at_r(const double &ppc, const double &r)
geompath_l_at_r
Definition: ppath.cc:256
interpolate_raytracing_points
void interpolate_raytracing_points(Vector &r_v, Vector &lat_v, Vector &lon_v, Vector &za_v, Vector &aa_v, double &lstep, ConstVectorView r_rt, ConstVectorView lat_rt, ConstVectorView lon_rt, ConstVectorView za_rt, ConstVectorView aa_rt, ConstVectorView l_rt, const double &lmax)
interpolate_raytracing_points
Definition: ppath.cc:3572
z_at_latlon
void z_at_latlon(VectorView z, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, const GridPos &gp_lat, const GridPos &gp_lon)
z_at_latlon
Definition: special_interp.cc:934
Ppath::background
String background
Definition: ppath.h:70
ppath_step_refr_1d
void ppath_step_refr_1d(Workspace &ws, Ppath &ppath, Numeric &rte_pressure, Numeric &rte_temperature, Vector &rte_vmr_list, Numeric &refr_index, const Agenda &refr_index_agenda, ConstVectorView p_grid, ConstVectorView z_field, ConstVectorView t_field, ConstMatrixView vmr_field, const double &r_geoid, const double &z_surface, const String &rtrace_method, const double &lraytrace, const double &lmax)
ppath_step_refr_1d
Definition: ppath.cc:4714
ppath_start_2d
void ppath_start_2d(double &r_start, double &lat_start, double &za_start, Index &ip, Index &ilat, double &lat1, double &lat3, double &r1a, double &r3a, double &r3b, double &r1b, double &rsurface1, double &rsurface3, Ppath &ppath, ConstVectorView lat_grid, ConstMatrixView z_field, ConstVectorView r_geoid, ConstVectorView z_surface)
ppath_start_2d
Definition: ppath.cc:3118
refr_gradients_3d
void refr_gradients_3d(Workspace &ws, Numeric &refr_index, Numeric &dndr, Numeric &dndlat, Numeric &dndlon, Numeric &a_pressure, Numeric &a_temperature, Vector &a_vmr_list, const Agenda &refr_index_agenda, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstMatrixView r_geoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, const Numeric &r, const Numeric &lat, const Numeric &lon)
refr_gradients_3d
Definition: refraction.cc:511
ppath_end_3d
void ppath_end_3d(Ppath &ppath, ConstVectorView r_v, ConstVectorView lat_v, ConstVectorView lon_v, ConstVectorView za_v, ConstVectorView aa_v, const double &lstep, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstMatrixView r_geoid, const Index &ip, const Index &ilat, const Index &ilon, const Index &endface, const Index &tanpoint, const double &ppc)
ppath_end_3d
Definition: ppath.cc:3481
poly_roots.h
Contains the code to determine roots of polynomials.
Vector::resize
void resize(Index n)
Resize function.
Definition: matpackI.cc:771
ppath_end_1d
void ppath_end_1d(Ppath &ppath, ConstVectorView r_v, ConstVectorView lat_v, ConstVectorView za_v, const double &lstep, ConstVectorView z_field, const double &r_geoid, const Index &ip, const Index &endface, const Index &tanpoint, const double &ppc)
ppath_end_1d
Definition: ppath.cc:3058
fractional_gp
Numeric fractional_gp(const GridPos &gp)
fractional_gp
Definition: interpolation.cc:504
ConstMatrixView::nrows
Index nrows() const
Returns the number of rows.
Definition: matpackI.cc:796
resolve_lon
void resolve_lon(double &lon, const double &lon5, const double &lon6)
resolve_lon
Definition: ppath.cc:735
geompath_za_at_r
double geompath_za_at_r(const double &ppc, const double &a_za, const double &r)
geompath_za_at_r
Definition: ppath.cc:160
is_los_downwards
bool is_los_downwards(const double &za, const double &tilt)
is_los_downwards
Definition: ppath.cc:1402
CREATE_OUT2
#define CREATE_OUT2
Definition: messages.h:207
z_at_lat_2d
void z_at_lat_2d(VectorView z, ConstVectorView p_grid, ConstVectorView lat_grid, ConstMatrixView z_field, const GridPos &gp_lat)
z_at_lat_2d
Definition: special_interp.cc:882
GridPos::fd
Numeric fd[2]
Definition: interpolation.h:76
array.h
This file contains the definition of Array.
q
#define q
Definition: continua.cc:14103
Ppath
The structure to describe a propagation path and releated quantities.
Definition: ppath.h:59
Ppath::gp_p
ArrayOfGridPos gp_p
Definition: ppath.h:66
CREATE_OUT1
#define CREATE_OUT1
Definition: messages.h:206
Agenda
The Agenda class.
Definition: agenda_class.h:44
sph2cart
void sph2cart(double &x, double &y, double &z, const double &r, const double &lat, const double &lon)
sph2cart
Definition: ppath.cc:490
ConstTensor3View::npages
Index npages() const
Returns the number of pages.
Definition: matpackIII.h:151
dx
#define dx
Definition: continua.cc:14561
ConstTensor4View
A constant view of a Tensor4.
Definition: matpackIV.h:149
dmin
#define dmin(a, b)
Definition: continua.cc:13098
Array< GridPos >
refraction_ppc
double refraction_ppc(const double &r, const double &za, const double &refr_index)
refraction_ppc
Definition: ppath.cc:2988
geometrical_ppc
double geometrical_ppc(const double &r, const double &za)
geometrical_ppc
Definition: ppath.cc:131
geompath_r_at_l
double geompath_r_at_l(const double &ppc, const double &l)
geompath_r_at_l
Definition: ppath.cc:285
agenda_class.h
Declarations for agendas.
mult
void mult(VectorView y, const ConstMatrixView &M, const ConstVectorView &x)
Matrix Vector multiplication.
Definition: matpackI.cc:1607
plevel_slope_2d
double plevel_slope_2d(ConstVectorView lat_grid, ConstVectorView r_geoid, ConstVectorView z_surf, const GridPos &gp, const double &za)
plevel_slope_2d
Definition: ppath.cc:1043
Ppath::gp_lat
ArrayOfGridPos gp_lat
Definition: ppath.h:67
ANGTOL
const double ANGTOL
Definition: ppath.cc:98
messages.h
Declarations having to do with the four output streams.
ppath_step_geom_1d
void ppath_step_geom_1d(Ppath &ppath, ConstVectorView p_grid, ConstVectorView z_field, const double &r_geoid, const double &z_surface, const double &lmax)
ppath_step_geom_1d
Definition: ppath.cc:3791
cart2sph
void cart2sph(double &r, double &lat, double &lon, const double &x, const double &y, const double &z)
cart2sph
Definition: ppath.cc:527
ppath_start_1d
void ppath_start_1d(double &r_start, double &lat_start, double &za_start, Index &ip, const Ppath &ppath)
ppath_start_1d
Definition: ppath.cc:3023
ConstMatrixView::ncols
Index ncols() const
Returns the number of columns.
Definition: matpackI.cc:802
my_basic_string
The implementation for String, the ARTS string class.
Definition: mystring.h:62
raytrace_1d_linear_euler
void raytrace_1d_linear_euler(Workspace &ws, Array< double > &r_array, Array< double > &lat_array, Array< double > &za_array, Array< double > &l_array, Index &endface, Index &tanpoint, double r, double lat, double za, Numeric &rte_pressure, Numeric &rte_temperature, Vector &rte_vmr_list, Numeric &refr_index, const Agenda &refr_index_agenda, const double &ppc, const double &lraytrace, const double &r1, const double &r3, const double &r_surface, const double &r_geoid, ConstVectorView p_grid, ConstVectorView z_field, ConstVectorView t_field, ConstMatrixView vmr_field)
raytrace_1d_linear_euler
Definition: ppath.cc:4090
raytrace_2d_linear_euler
void raytrace_2d_linear_euler(Workspace &ws, Array< double > &r_array, Array< double > &lat_array, Array< double > &za_array, Array< double > &l_array, Index &endface, Index &tanpoint, double r, double lat, double za, Numeric &rte_pressure, Numeric &rte_temperature, Vector &rte_vmr_list, Numeric &refr_index, const Agenda &refr_index_agenda, const double &lraytrace, const double &lat1, const double &lat3, const double &r1a, const double &r3a, const double &r3b, const double &r1b, const double &rsurface1, const double &rsurface3, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView r_geoid, ConstMatrixView z_field, ConstMatrixView t_field, ConstTensor3View vmr_field)
raytrace_2d_linear_euler
Definition: ppath.cc:4257
abs
#define abs(x)
Definition: continua.cc:13094
Ppath::los
Matrix los
Definition: ppath.h:69
Ppath::gp_lon
ArrayOfGridPos gp_lon
Definition: ppath.h:68
Ppath::np
Index np
Definition: ppath.h:61
ConstVectorView::nelem
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:175
RTOL
const double RTOL
Definition: ppath.cc:75
POLELAT
const double POLELAT
Definition: ppath.cc:105
Ppath::z
Vector z
Definition: ppath.h:64
ConstVectorView::sum
Numeric sum() const
The sum of all elements of a Vector.
Definition: matpackI.cc:181
ppath_step_geom_2d
void ppath_step_geom_2d(Ppath &ppath, ConstVectorView p_grid, ConstVectorView lat_grid, ConstMatrixView z_field, ConstVectorView r_geoid, ConstVectorView z_surface, const double &lmax)
ppath_step_geom_2d
Definition: ppath.cc:3871
Ppath::tan_pos
Vector tan_pos
Definition: ppath.h:71
refr_gradients_2d
void refr_gradients_2d(Workspace &ws, Numeric &refr_index, Numeric &dndr, Numeric &dndlat, Numeric &a_pressure, Numeric &a_temperature, Vector &a_vmr_list, const Agenda &refr_index_agenda, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView r_geoid, ConstMatrixView z_field, ConstMatrixView t_field, ConstTensor3View vmr_field, const Numeric &r, const Numeric &lat)
refr_gradients_2d
Definition: refraction.cc:430
ppath_step_geom_3d
void ppath_step_geom_3d(Ppath &ppath, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstMatrixView r_geoid, ConstMatrixView z_surface, const double &lmax)
ppath_step_geom_3d
Definition: ppath.cc:3956
Numeric
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
Verbosity
Definition: messages.h:50
cart2zaaa
void cart2zaaa(double &za, double &aa, const double &dx, const double &dy, const double &dz)
cart2zaaa
Definition: ppath.cc:876
do_gridrange_1d
void do_gridrange_1d(Vector &r_v, Vector &lat_v, Vector &za_v, double &lstep, Index &endface, Index &tanpoint, const double &r_start0, const double &lat_start, const double &za_start, const double &ppc, const double &lmax, const double &ra, const double &rb, const double &rsurface)
do_gridrange_1d
Definition: ppath.cc:1697
Ppath::l_step
Vector l_step
Definition: ppath.h:65
poly_root_solve
int poly_root_solve(Matrix &roots, Vector &coeffs)
Definition: poly_roots.cc:100
cart2poslos
void cart2poslos(double &r, double &lat, double &lon, double &za, double &aa, const double &x, const double &y, const double &z, const double &dx, const double &dy, const double &dz)
cart2poslos
Definition: ppath.cc:652
map_daa
void map_daa(double &za, double &aa, const double &za0, const double &aa0, const double &aa_grid)
Definition: ppath.cc:967
math_funcs.h
rsurf_at_latlon
double rsurf_at_latlon(const double &lat1, const double &lat3, const double &lon5, const double &lon6, const double &r15, const double &r35, const double &r36, const double &r16, const double &lat, const double &lon)
rsurf_at_latlon
Definition: ppath.cc:1133
geompath_lat_at_za
double geompath_lat_at_za(const double &za0, const double &lat0, const double &za)
geompath_lat_at_za
Definition: ppath.cc:228
from_raytracingarrays_to_ppath_vectors_3d
void from_raytracingarrays_to_ppath_vectors_3d(Vector &r_v, Vector &lat_v, Vector &lon_v, Vector &za_v, Vector &aa_v, double &lstep, const Array< double > &r_array, const Array< double > &lat_array, const Array< double > &lon_array, const Array< double > &za_array, const Array< double > &aa_array, const Array< double > &l_array, const double &lmax)
from_raytracingarrays_to_ppath_vectors_3d
Definition: ppath.cc:3717
get_refr_index_1d
void get_refr_index_1d(Workspace &ws, Numeric &refr_index, Numeric &a_pressure, Numeric &a_temperature, Vector &a_vmr_list, const Agenda &refr_index_agenda, ConstVectorView p_grid, const Numeric &r_geoid, ConstVectorView z_field, ConstVectorView t_field, ConstMatrixView vmr_field, const Numeric &r)
get_refr_index_1d
Definition: refraction.cc:82
geompath_r_at_za
double geompath_r_at_za(const double &ppc, const double &za)
geompath_r_at_za
Definition: ppath.cc:200
Matrix::resize
void resize(Index r, Index c)
Resize function.
Definition: matpackI.cc:1549
ppath_step_refr_2d
void ppath_step_refr_2d(Workspace &ws, Ppath &ppath, Numeric &rte_pressure, Numeric &rte_temperature, Vector &rte_vmr_list, Numeric &refr_index, const Agenda &refr_index_agenda, ConstVectorView p_grid, ConstVectorView lat_grid, ConstMatrixView z_field, ConstMatrixView t_field, ConstTensor3View vmr_field, ConstVectorView r_geoid, ConstVectorView z_surface, const String &rtrace_method, const double &lraytrace, const double &lmax)
ppath_step_refr_2d
Definition: ppath.cc:4857
max
#define max(a, b)
Definition: continua.cc:13097
gridpos_force_end_fd
void gridpos_force_end_fd(GridPos &gp, const Index &n)
gridpos_force_end_fd
Definition: interpolation.cc:567
nlinspace
void nlinspace(Vector &x, const Numeric start, const Numeric stop, const Index n)
nlinspace
Definition: math_funcs.cc:261
ConstMatrixView
A constant view of a Matrix.
Definition: matpackI.h:591
ppath_copy
void ppath_copy(Ppath &ppath1, const Ppath &ppath2)
ppath_copy
Definition: ppath.cc:2598
do_gridcell_3d
void do_gridcell_3d(Vector &r_v, Vector &lat_v, Vector &lon_v, Vector &za_v, Vector &aa_v, double &lstep, Index &endface, Index &tanpoint, const double &r_start0, const double &lat_start0, const double &lon_start0, const double &za_start, const double &aa_start, const double &ppc, const double &lmax, const double &lat1, const double &lat3, const double &lon5, const double &lon6, const double &r15a, const double &r35a, const double &r36a, const double &r16a, const double &r15b, const double &r35b, const double &r36b, const double &r16b, const double &rsurface15, const double &rsurface35, const double &rsurface36, const double &rsurface16)
do_gridcell_3d
Definition: ppath.cc:2069
rotationmat3D
void rotationmat3D(Matrix &R, ConstVectorView vrot, const Numeric &a)
rotationmat3D
Definition: ppath.cc:911
plevel_slope_3d
double plevel_slope_3d(const double &lat1, const double &lat3, const double &lon5, const double &lon6, const double &r15, const double &r35, const double &r36, const double &r16, const double &lat, const double &lon, const double &aa)
plevel_slope_3d
Definition: ppath.cc:1195
Range
The range class.
Definition: matpackI.h:148
GridPos
Structure to store a grid position.
Definition: interpolation.h:74
ppath.h
Propagation path structure and functions.
DEG2RAD
const Numeric DEG2RAD
Ppath::constant
Numeric constant
Definition: ppath.h:62
gridpos2gridrange
Index gridpos2gridrange(const GridPos &gp, const bool &upwards)
gridpos2gridrange
Definition: interpolation.cc:701
logic.h
Header file for logic.cc.
chk_vector_length
void chk_vector_length(const String &x_name, ConstVectorView x, const Index &l)
chk_vector_length
Definition: check_input.cc:222
LATLONTOL
const double LATLONTOL
Definition: ppath.cc:84
refraction.h
Refraction functions.
min
#define min(a, b)
Definition: continua.cc:13096
za_geom2other_point
double za_geom2other_point(const double &r1, const double &lat1, const double &r2, const double &lat2)
za_geom2other_point
Definition: ppath.cc:433
Workspace
Workspace class.
Definition: workspace_ng.h:47
ConstTensor3View
A constant view of a Tensor3.
Definition: matpackIII.h:147
RAD2DEG
const Numeric RAD2DEG
special_interp.h
Header file for special_interp.cc.
ppath_fill_1d
void ppath_fill_1d(Ppath &ppath, ConstVectorView r, ConstVectorView lat, ConstVectorView za, ConstVectorView lstep, const double &r_geoid, ConstVectorView z_field, const Index &ip)
ppath_fill_1d
Definition: ppath.cc:2722
ppath_step_refr_3d
void ppath_step_refr_3d(Workspace &ws, Ppath &ppath, Numeric &rte_pressure, Numeric &rte_temperature, Vector &rte_vmr_list, Numeric &refr_index, const Agenda &refr_index_agenda, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstMatrixView r_geoid, ConstMatrixView z_surface, const String &rtrace_method, const double &lraytrace, const double &lmax)
ppath_step_refr_3d
Definition: ppath.cc:4992
geompath_from_r1_to_r2
void geompath_from_r1_to_r2(Vector &r, Vector &lat, Vector &za, double &lstep, const double &ppc, const double &r1, const double &lat1, const double &za1, const double &r2, const double &lmax)
geompath_from_r1_to_r2
Definition: ppath.cc:350
ppath_fill_3d
void ppath_fill_3d(Ppath &ppath, ConstVectorView r, ConstVectorView lat, ConstVectorView lon, ConstVectorView za, ConstVectorView aa, const double &lstep, ConstMatrixView r_geoid, ConstTensor3View z_field, ConstVectorView lat_grid, ConstVectorView lon_grid, const Index &ip, const Index &ilat, const Index &ilon)
ppath_fill_3d
Definition: ppath.cc:2871
Ppath::geom_tan_pos
Vector geom_tan_pos
Definition: ppath.h:72
GridPos::idx
Index idx
Definition: interpolation.h:75
from_raytracingarrays_to_ppath_vectors_1d_and_2d
void from_raytracingarrays_to_ppath_vectors_1d_and_2d(Vector &r_v, Vector &lat_v, Vector &za_v, double &lstep, const Array< double > &r_array, const Array< double > &lat_array, const Array< double > &za_array, const Array< double > &l_array, const Index &reversed, const double &lmax)
from_raytracingarrays_to_ppath_vectors_1d_and_2d
Definition: ppath.cc:3655
ppath_set_background
void ppath_set_background(Ppath &ppath, const Index &case_nr)
ppath_set_background
Definition: ppath.cc:2519
Index
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
chk_if_in_range
void chk_if_in_range(const String &x_name, const Index &x, const Index &x_low, const Index &x_high)
chk_if_in_range
Definition: check_input.cc:95
do_gridcell_2d
void do_gridcell_2d(Vector &r_v, Vector &lat_v, Vector &za_v, double &lstep, Index &endface, Index &tanpoint, const double &r_start0, const double &lat_start, const double &za_start, const double &ppc, const double &lmax, const double &lat1, const double &lat3, const double &r1a, const double &r3a, const double &r3b, const double &r1b, const double &rsurface1, const double &rsurface3)
do_gridcell_2d
Definition: ppath.cc:1825
plevel_angletilt
double plevel_angletilt(const double &r, const double &c)
plevel_angletilt
Definition: ppath.cc:1317
check_input.h
Ppath::dim
Index dim
Definition: ppath.h:60
ppath_what_background
Index ppath_what_background(const Ppath &ppath)
ppath_what_background
Definition: ppath.cc:2561
Vector
The Vector class.
Definition: matpackI.h:555
arts_omp.h
Header file for helper functions for OpenMP.
ConstVectorView
A constant view of a Vector.
Definition: matpackI.h:300
Ppath::pos
Matrix pos
Definition: ppath.h:63
Array::nelem
Index nelem() const
Number of elements.
Definition: array.h:172
interpweights
void interpweights(VectorView itw, const GridPos &tc)
Red 1D interpolation weights.
Definition: interpolation.cc:756
ppath_start_3d
void ppath_start_3d(double &r_start, double &lat_start, double &lon_start, double &za_start, double &aa_start, Index &ip, Index &ilat, Index &ilon, double &lat1, double &lat3, double &lon5, double &lon6, double &r15a, double &r35a, double &r36a, double &r16a, double &r15b, double &r35b, double &r36b, double &r16b, double &rsurface15, double &rsurface35, double &rsurface36, double &rsurface16, Ppath &ppath, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstMatrixView r_geoid, ConstMatrixView z_surface)
ppath_start_3d
Definition: ppath.cc:3307
mystring.h
This file contains the definition of String, the ARTS string class.
rsurf_at_lat
double rsurf_at_lat(const double &lat1, const double &lat3, const double &r1, const double &r3, const double &lat)
rsurf_at_lat
Definition: ppath.cc:1102
surfacetilt
double surfacetilt(const Index &atmosphere_dim, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstMatrixView r_geoid, ConstMatrixView z_surface, const GridPos &gp_lat, const GridPos &gp_lon, ConstVectorView los)
surfacetilt
Definition: ppath.cc:1348