ARTS  2.3.1285(git:92a29ea9-dirty)
ppath.cc
Go to the documentation of this file.
1 /* Copyright (C) 2002-2012 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 
31 /*===========================================================================
32  === External declarations
33  ===========================================================================*/
34 
35 #include "ppath.h"
36 #include <cmath>
37 #include <stdexcept>
38 #include "agenda_class.h"
39 #include "array.h"
40 #include "arts_omp.h"
41 #include "auto_md.h"
42 #include "check_input.h"
43 #include "geodetic.h"
44 #include "logic.h"
45 #include "math_funcs.h"
46 #include "messages.h"
47 #include "mystring.h"
48 #include "poly_roots.h"
49 #include "refraction.h"
50 #include "rte.h"
51 #include "special_interp.h"
52 
53 extern const Numeric DEG2RAD;
54 extern const Numeric RAD2DEG;
55 
56 /*===========================================================================
57  === Precision variables
58  ===========================================================================*/
59 
60 // This variable defines the maximum allowed error tolerance for radius.
61 // The variable is, for example, used to check that a given a radius is
62 // consistent with the specified grid cell.
63 //
64 const Numeric RTOL = 1e-3;
65 
66 // As RTOL but for latitudes and longitudes.
67 //
68 DEBUG_ONLY(const Numeric LATLONTOL = 1e-8;)
69 
70 // Accuarcy for length comparisons.
71 //
72 const Numeric LACC = 1e-5;
73 
74 // Values to apply if some calculation does not provide a solution
75 const Numeric R_NOT_FOUND = -1; // A value below zero
76 const Numeric L_NOT_FOUND = 99e99; // Some very large value for l/lat/lon
77 const Numeric LAT_NOT_FOUND = 99e99;
78 const Numeric LON_NOT_FOUND = 99e99;
79 
80 /*===========================================================================
81  === Functions related to geometrical propagation paths
82  ===========================================================================*/
83 
96 Numeric geometrical_ppc(const Numeric& r, const Numeric& za) {
97  assert(r > 0);
98  assert(abs(za) <= 180);
99 
100  return r * sin(DEG2RAD * abs(za));
101 }
102 
104  const Numeric& a_za,
105  const Numeric& r) {
106  assert(ppc >= 0);
107  assert(abs(a_za) <= 180);
108  assert(r >= ppc - RTOL);
109 
110  if (r > ppc) {
111  Numeric za = RAD2DEG * asin(ppc / r);
112  if (abs(a_za) > 90) {
113  za = 180 - za;
114  }
115  if (a_za < 0) {
116  za = -za;
117  }
118  return za;
119  } else {
120  if (a_za > 0) {
121  return 90;
122  } else {
123  return -90;
124  }
125  }
126 }
127 
141 Numeric geompath_r_at_za(const Numeric& ppc, const Numeric& za) {
142  assert(ppc >= 0);
143  assert(abs(za) <= 180);
144 
145  return ppc / sin(DEG2RAD * abs(za));
146 }
147 
149  const Numeric& lat0,
150  const Numeric& za) {
151  assert(abs(za0) <= 180);
152  assert(abs(za) <= 180);
153  assert((za0 >= 0 && za >= 0) || (za0 < 0 && za < 0));
154 
155  return lat0 + za0 - za;
156 }
157 
158 Numeric geompath_l_at_r(const Numeric& ppc, const Numeric& r) {
159  assert(ppc >= 0);
160  assert(r >= ppc - RTOL);
161 
162  if (r > ppc) {
163  return sqrt(r * r - ppc * ppc);
164  } else {
165  return 0;
166  }
167 }
168 
182 Numeric geompath_r_at_l(const Numeric& ppc, const Numeric& l) {
183  assert(ppc >= 0);
184 
185  return sqrt(l * l + ppc * ppc);
186 }
187 
201  const Numeric& lat0,
202  const Numeric& za0,
203  const Numeric& lat) {
204  assert(ppc >= 0);
205  assert(abs(za0) <= 180);
206  assert((za0 >= 0 && lat >= lat0) || (za0 <= 0 && lat <= lat0));
207 
208  // Zenith angle at the new latitude
209  const Numeric za = za0 + lat0 - lat;
210 
211  return geompath_r_at_za(ppc, za);
212 }
213 
237  Vector& lat,
238  Vector& za,
239  Numeric& lstep,
240  const Numeric& ppc,
241  const Numeric& r1,
242  const Numeric& lat1,
243  const Numeric& za1,
244  const Numeric& r2,
245  const bool& tanpoint,
246  const Numeric& lmax) {
247  // Calculate length from tangent point, along the path for point 1 and 2.
248  // Length defined in the viewing direction, ie. negative at end of sensor
249  Numeric l1 = geompath_l_at_r(ppc, r1);
250  if (abs(za1) > 90) {
251  l1 *= -1;
252  }
253  Numeric l2 = geompath_l_at_r(ppc, r2);
254  if (l1 < 0) {
255  l2 *= -1;
256  }
257  if (tanpoint) {
258  l2 *= -1;
259  }
260 
261  // Calculate needed number of steps, considering a possible length criterion
262  Index n;
263  if (lmax > 0) {
264  // The absolute value of the length distance is needed here
265  // We can't accept n=0, which is the case if l1 = l2
266  n = max(Index(1), Index(ceil(abs(l2 - l1) / lmax)));
267  } else {
268  n = 1;
269  }
270 
271  // Length of path steps (note that lstep here can become negative)
272  lstep = (l2 - l1) / (Numeric)n;
273 
274  // Allocate vectors and put in point 1
275  r.resize(n + 1);
276  lat.resize(n + 1);
277  za.resize(n + 1);
278  r[0] = r1;
279  lat[0] = lat1;
280  za[0] = za1;
281 
282  // Loop steps (beside last) and calculate radius and zenith angle
283  for (Index i = 1; i < n; i++) {
284  const Numeric l = l1 + lstep * (Numeric)i;
285  r[i] = geompath_r_at_l(ppc, l); // Sign of l does not matter here
286  // Set a zenith angle to 80 or 100 depending on sign of l
287  za[i] = geompath_za_at_r(ppc, sign(za1) * (90 - sign(l) * 10), r[i]);
288  }
289 
290  // For maximum accuracy, set last radius to be exactly r2.
291  r[n] = r2; // Don't use r[n] below
292  za[n] = geompath_za_at_r(ppc, sign(za1) * (90 - sign(l2) * 10), r2);
293 
294  // Ensure that zenith and nadir observations keep their zenith angle
295  if (abs(za1) < ANGTOL || abs(za1) > 180 - ANGTOL) {
296  za = za1;
297  }
298 
299  // Calculate latitudes
300  for (Index i = 1; i <= n; i++) {
301  lat[i] = geompath_lat_at_za(za1, lat1, za[i]);
302  }
303 
304  // Take absolute value of lstep
305  lstep = abs(lstep);
306 }
307 
308 /*===========================================================================
309  === Functions focusing on zenith and azimuth angles
310  ===========================================================================*/
311 
313  Numeric& aa,
314  const Numeric& dx,
315  const Numeric& dy,
316  const Numeric& dz) {
317  const Numeric r = sqrt(dx * dx + dy * dy + dz * dz);
318 
319  assert(r > 0);
320 
321  za = RAD2DEG * acos(dz / r);
322  aa = RAD2DEG * atan2(dy, dx);
323 }
324 
348  Numeric& dy,
349  Numeric& dz,
350  const Numeric& za,
351  const Numeric& aa) {
352  const Numeric zarad = DEG2RAD * za;
353  const Numeric aarad = DEG2RAD * aa;
354 
355  dz = cos(zarad);
356  dx = sin(zarad);
357  dy = sin(aarad) * dx;
358  dx = cos(aarad) * dx;
359 }
360 
377 void rotationmat3D(Matrix& R, ConstVectorView vrot, const Numeric& a) {
378  assert(R.ncols() == 3);
379  assert(R.nrows() == 3);
380  assert(vrot.nelem() == 3);
381 
382  const Numeric l =
383  sqrt(vrot[0] * vrot[0] + vrot[1] * vrot[1] + vrot[2] * vrot[2]);
384  assert(l > 1 - 9);
385  const Numeric u = vrot[0] / l;
386  const Numeric v = vrot[1] / l;
387  const Numeric w = vrot[2] / l;
388  const Numeric u2 = u * u;
389  const Numeric v2 = v * v;
390  const Numeric w2 = w * w;
391  const Numeric c = cos(DEG2RAD * a);
392  const Numeric s = sin(DEG2RAD * a);
393 
394  // Fill R
395  R(0, 0) = u2 + (v2 + w2) * c;
396  R(0, 1) = u * v * (1 - c) - w * s;
397  R(0, 2) = u * w * (1 - c) + v * s;
398  R(1, 0) = u * v * (1 - c) + w * s;
399  R(1, 1) = v2 + (u2 + w2) * c;
400  R(1, 2) = v * w * (1 - c) - u * s;
401  R(2, 0) = u * w * (1 - c) - v * s;
402  R(2, 1) = v * w * (1 - c) + u * s;
403  R(2, 2) = w2 + (u2 + v2) * c;
404 }
405 
407  Numeric& aa,
408  const Numeric& za0,
409  const Numeric& aa0,
410  const Numeric& dza,
411  const Numeric& daa) {
412  Vector xyz(3);
413  Vector vrot(3);
414  Vector u(3);
415 
416  // Unit vector towards aa0 at za=90
417  //
418  zaaa2cart(xyz[0], xyz[1], xyz[2], 90, aa0);
419 
420  // Find vector around which rotation shall be performed
421  //
422  // We can write this as cross([0 0 1],xyz). It turns out that the result
423  // of this operation is just [-y,x,0].
424  //
425  vrot[0] = -xyz[1];
426  vrot[1] = xyz[0];
427  vrot[2] = 0;
428 
429  // Unit vector towards aa0+aa at za=90
430  //
431  zaaa2cart(xyz[0], xyz[1], xyz[2], 90 + dza, aa0 + daa);
432 
433  // Apply rotation
434  //
435  Matrix R(3, 3);
436  rotationmat3D(R, vrot, za0 - 90);
437  mult(u, R, xyz);
438 
439  // Calculate za and aa for rotated u
440  //
441  cart2zaaa(za, aa, u[0], u[1], u[2]);
442 }
443 
444 void diff_za_aa(Numeric& dza,
445  Numeric& daa,
446  const Numeric& za0,
447  const Numeric& aa0,
448  const Numeric& za,
449  const Numeric& aa) {
450  Vector xyz(3);
451  Vector vrot(3);
452  Vector u(3);
453 
454  assert(za != 0 && za != 180);
455 
456  // Unit vector towards aa0 at za=90
457  //
458  zaaa2cart(xyz[0], xyz[1], xyz[2], za0, aa0);
459 
460  // Find vector around which rotation shall be performed
461  //
462  // We can write this as cross([0 0 1],xyz). It turns out that the result
463  // of this operation is just [-y,x,0].
464  //
465  vrot[0] = -xyz[1];
466  vrot[1] = xyz[0];
467  vrot[2] = 0;
468 
469  // Unit vector towards aa0+aa at za=90
470  //
471  zaaa2cart(xyz[0], xyz[1], xyz[2], za, aa);
472 
473  // Apply rotation
474  //
475  Matrix R(3, 3);
476  rotationmat3D(R, vrot, -(za0 - 90));
477  mult(u, R, xyz);
478 
479  // Calculate za and aa for rotated u
480  //
481  Numeric za_tmp, aa_tmp;
482  cart2zaaa(za_tmp, aa_tmp, u[0], u[1], u[2]);
483 
484  // Calculate dza and daa
485  dza = za_tmp - 90;
486  daa = aa_tmp - aa0;
487 }
488 
489 /*===========================================================================
490  === Various functions
491  ===========================================================================*/
492 
507  const Numeric& za,
508  const Numeric& refr_index_air) {
509  assert(r > 0);
510  assert(abs(za) <= 180);
511 
512  return r * refr_index_air * sin(DEG2RAD * abs(za));
513 }
514 
515 void resolve_lon(Numeric& lon, const Numeric& lon5, const Numeric& lon6) {
516  assert(lon6 >= lon5);
517 
518  if (lon < lon5 && lon + 180 <= lon6) {
519  lon += 360;
520  } else if (lon > lon6 && lon - 180 >= lon5) {
521  lon -= 360;
522  }
523 }
524 
525 void find_tanpoint(Index& it, const Ppath& ppath) {
526  Numeric zmin = 99e99;
527  it = -1;
528  while (it < ppath.np - 1 && ppath.pos(it + 1, 0) < zmin) {
529  it++;
530  zmin = ppath.pos(it, 0);
531  }
532  if (it == 0 || it == ppath.np - 1) {
533  it = -1;
534  }
535 }
536 
538  // Checker flags
539  bool below = false, above = false;
540 
541  // Check first pos before altitude
542  for (Index i = 0; i < p.np; i++) {
543  if (p.pos(i, 0) < alt) {
544  if (above) return i - 1;
545  below = true;
546  } else {
547  if (below) return i - 1;
548  above = true;
549  }
550  }
551 
552  return -1;
553 }
554 
555 void error_if_limb_ppath(const Ppath& ppath) {
556  if (ppath.np > 2) {
557  Numeric signfac = sign(ppath.pos(1, 0) - ppath.pos(0, 0));
558  for (Index i = 2; i < ppath.np; i++) {
559  if (signfac * (ppath.pos(i, 0) - ppath.pos(i - 1, 0)) < 0)
560  throw runtime_error(
561  "A propagation path of limb character found. Such "
562  "viewing geometries are not supported by this method. "
563  "Propagation paths must result in strictly "
564  "increasing or decreasing altitudes.");
565  }
566  }
567 }
568 
569 /*===========================================================================
570  = 2D functions for surface and pressure level slope and tilt
571  ===========================================================================*/
572 
588  const Numeric& lat3,
589  const Numeric& r1,
590  const Numeric& r3,
591  const Numeric& lat) {
592  return r1 + (lat - lat1) * (r3 - r1) / (lat3 - lat1);
593 }
594 
596  ConstVectorView lat_grid,
597  ConstVectorView refellipsoid,
598  ConstVectorView z_surf,
599  const GridPos& gp,
600  const Numeric& za) {
601  Index i1 = gridpos2gridrange(gp, za >= 0);
602  const Numeric r1 = refell2r(refellipsoid, lat_grid[i1]) + z_surf[i1];
603  const Numeric r2 = refell2r(refellipsoid, lat_grid[i1 + 1]) + z_surf[i1 + 1];
604  //
605  c1 = (r2 - r1) / (lat_grid[i1 + 1] - lat_grid[i1]);
606 }
607 
625  const Numeric& lat1,
626  const Numeric& lat2,
627  const Numeric& r1,
628  const Numeric& r2) {
629  c1 = (r2 - r1) / (lat2 - lat1);
630 }
631 
632 Numeric plevel_angletilt(const Numeric& r, const Numeric& c1) {
633  // The tilt (in radians) is c1/r if c1 is converted to m/radian. So we get
634  // conversion RAD2DEG twice
635  return RAD2DEG * RAD2DEG * c1 / r;
636 }
637 
638 bool is_los_downwards(const Numeric& za, const Numeric& tilt) {
639  assert(abs(za) <= 180);
640 
641  // Yes, it shall be -tilt in both cases, if you wonder.
642  if (za > (90 - tilt) || za < (-90 - tilt)) {
643  return true;
644  } else {
645  return false;
646  }
647 }
648 
672  Numeric& l,
673  const Numeric& r_hit,
674  const Numeric& r_start,
675  const Numeric& lat_start,
676  const Numeric& za_start,
677  const Numeric& ppc) {
678  assert(abs(za_start) <= 180);
679  assert(r_start >= ppc);
680 
681  const Numeric absza = abs(za_start);
682 
683  // If above and looking upwards or r_hit below tangent point,
684  // we have no crossing:
685  if ((r_start >= r_hit && absza <= 90) || ppc > r_hit) {
686  lat = LAT_NOT_FOUND;
687  l = L_NOT_FOUND;
688  }
689 
690  else {
691  // Passages of tangent point
692  if (absza > 90 && r_start <= r_hit) {
693  Numeric za = geompath_za_at_r(ppc, sign(za_start) * 89, r_hit);
694  lat = geompath_lat_at_za(za_start, lat_start, za);
695  l = geompath_l_at_r(ppc, r_start) + geompath_l_at_r(ppc, r_hit);
696  }
697 
698  else {
699  Numeric za = geompath_za_at_r(ppc, za_start, r_hit);
700  lat = geompath_lat_at_za(za_start, lat_start, za);
701  l = abs(geompath_l_at_r(ppc, r_start) - geompath_l_at_r(ppc, r_hit));
702  assert(l > 0);
703  }
704  }
705 }
706 
743  const Numeric& za,
744  const Numeric& r0,
745  Numeric c1) {
746  const Numeric zaabs = abs(za);
747 
748  assert(za != 0);
749  assert(zaabs != 180);
750  assert(abs(c1) > 0); // c1=0 should work, but unnecessary to use this func.
751 
752  // Convert slope to m/radian and consider viewing direction
753  c1 *= RAD2DEG;
754  if (za < 0) {
755  c1 = -c1;
756  }
757 
758  // The nadir angle in radians, and cosine and sine of that angle
759  const Numeric beta = DEG2RAD * (180 - zaabs);
760  const Numeric cv = cos(beta);
761  const Numeric sv = sin(beta);
762 
763  // Some repeated terms
764  const Numeric r0s = r0 * sv;
765  const Numeric r0c = r0 * cv;
766  const Numeric cs = c1 * sv;
767  const Numeric cc = c1 * cv;
768 
769  // The vector of polynomial coefficients
770  //
771  Index n = 6;
772  Vector p0(n + 1);
773  //
774  p0[0] = r0s - rp * sv;
775  p0[1] = r0c + cs;
776  p0[2] = -r0s / 2 + cc;
777  p0[3] = -r0c / 6 - cs / 2;
778  p0[4] = r0s / 24 - cc / 6;
779  p0[5] = r0c / 120 + cs / 24;
780  p0[6] = -r0s / 720 + cc / 120;
781  //
782  // The accuracy when solving the polynomial equation gets worse when
783  // approaching 0 and 180 deg. The solution is to let the start polynomial
784  // order decrease when approaching these angles. The values below based on
785  // practical experience, don't change without making extremely careful tests.
786  //
787  if (abs(90 - zaabs) > 89.9) {
788  n = 1;
789  } else if (abs(90 - zaabs) > 75) {
790  n = 4;
791  }
792 
793  // Calculate roots of the polynomial
794  Matrix roots;
795  int solutionfailure = 1;
796  //
797  while (solutionfailure) {
798  roots.resize(n, 2);
799  Vector p;
800  p = p0[Range(0, n + 1)];
801  solutionfailure = poly_root_solve(roots, p);
802  if (solutionfailure) {
803  n -= 1;
804  assert(n > 0);
805  }
806  }
807 
808  // If r0=rp, numerical inaccuracy can give a false solution, very close
809  // to 0, that we must throw away.
810  Numeric dmin = 0;
811  if (abs(r0 - rp) < 1e-9) // 1 nm set based on practical experience.
812  {
813  dmin = 5e-12;
814  }
815 
816  // Find the smallest root with imaginary part = 0, and real part > 0.
817  Numeric dlat = 1.571; // Not interested in solutions above 90 deg!
818  //
819  for (Index i = 0; i < n; i++) {
820  if (roots(i, 1) == 0 && roots(i, 0) > dmin && roots(i, 0) < dlat) {
821  dlat = roots(i, 0);
822  }
823  }
824 
825  if (dlat < 1.57) // A somewhat smaller value than start one
826  {
827  // Convert back to degrees
828  dlat = RAD2DEG * dlat;
829 
830  // Change sign if zenith angle is negative
831  if (za < 0) {
832  dlat = -dlat;
833  }
834  } else {
835  dlat = LAT_NOT_FOUND;
836  }
837 
838  return dlat;
839 }
840 
872  Numeric& lat,
873  Numeric& l,
874  const Numeric& r_start0,
875  const Numeric& lat_start,
876  const Numeric& za_start,
877  const Numeric& ppc,
878  const Numeric& lat1,
879  const Numeric& lat3,
880  const Numeric& r1,
881  const Numeric& r3,
882  const bool& above) {
883  const Numeric absza = abs(za_start);
884 
885  assert(absza <= 180);
886  assert(lat_start >= lat1 && lat_start <= lat3);
887 
888  // Zenith case
889  if (absza < ANGTOL) {
890  if (above) {
891  r = R_NOT_FOUND;
892  lat = LAT_NOT_FOUND;
893  l = L_NOT_FOUND;
894  } else {
895  lat = lat_start;
896  r = rsurf_at_lat(lat1, lat3, r1, r3, lat);
897  l = max(1e-9, r - r_start0); // Max to ensure a small positive
898  } // step, to handle numerical issues
899  }
900 
901  // Nadir case
902  else if (absza > 180 - ANGTOL) {
903  if (above) {
904  lat = lat_start;
905  r = rsurf_at_lat(lat1, lat3, r1, r3, lat);
906  l = max(1e-9, r_start0 - r); // As above
907  } else {
908  r = R_NOT_FOUND;
909  lat = LAT_NOT_FOUND;
910  l = L_NOT_FOUND;
911  }
912  }
913 
914  // The general case
915  else {
916  const Numeric rmin = min(r1, r3);
917  const Numeric rmax = max(r1, r3);
918 
919  // The case of negligible slope
920  if (rmax - rmin < 1e-6) {
921  // Set r_start and r, considering impact of numerical problems
922  Numeric r_start = r_start0;
923  r = r1;
924  if (above) {
925  if (r_start < rmax) {
926  r_start = r = rmax;
927  }
928  } else {
929  if (r_start > rmin) {
930  r_start = r = rmin;
931  }
932  }
933 
934  r_crossing_2d(lat, l, r, r_start, lat_start, za_start, ppc);
935 
936  // Check if inside [lat1,lat3]
937  if (lat > lat3 || lat < lat1) {
938  r = R_NOT_FOUND;
939  lat = LAT_NOT_FOUND;
940  }
941  }
942 
943  // With slope
944  else {
945  // Set r_start, considering impact of numerical problems
946  Numeric r_start = r_start0;
947  if (above) {
948  if (r_start < rmin) {
949  r_start = rmin;
950  }
951  } else {
952  if (r_start > rmax) {
953  r_start = rmax;
954  }
955  }
956 
957  Numeric za = 999;
958 
959  // Calculate crossing with closest radius
960  if (r_start > rmax) {
961  r = rmax;
962  r_crossing_2d(lat, l, r, r_start, lat_start, za_start, ppc);
963  } else if (r_start < rmin) {
964  r = rmin;
965  r_crossing_2d(lat, l, r, r_start, lat_start, za_start, ppc);
966  } else {
967  r = r_start;
968  lat = lat_start;
969  l = 0;
970  za = za_start;
971  }
972 
973  // lat must be inside [lat1,lat3] if relevant to continue
974  if (lat < lat1 || lat > lat3) {
975  r = R_NOT_FOUND;
976  } // lat already set by r_crossing_2d
977 
978  // Otherwise continue from found point, considering the level slope
979  else {
980  // Level slope and radius at lat
981  Numeric cpl;
982  plevel_slope_2d(cpl, lat1, lat3, r1, r3);
983  const Numeric rpl = r1 + cpl * (lat - lat1);
984 
985  // Make adjustment if numerical problems
986  if (above) {
987  if (r < rpl) {
988  r = rpl;
989  }
990  } else {
991  if (r > rpl) {
992  r = rpl;
993  }
994  }
995 
996  // Calculate zenith angle at (r,lat) (if <180, already set above)
997  if (za > 180) // lat+za preserved (also with negative za)
998  {
999  za = lat_start + za_start - lat;
1000  };
1001 
1002  // Latitude distance from present point to actual crossing
1003  const Numeric dlat = rslope_crossing2d(r, za, rpl, cpl);
1004 
1005  // Update lat and check if still inside [lat1,lat3].
1006  // If yes, determine r and l
1007  lat += dlat;
1008  if (lat < lat1 || lat > lat3) {
1009  r = R_NOT_FOUND;
1010  lat = LAT_NOT_FOUND;
1011  l = L_NOT_FOUND;
1012  } else {
1013  // It was tested to calculate r from geompath functions, but
1014  // appeared to give poorer accuracy around zenith/nadir
1015  r = rpl + cpl * dlat;
1016 
1017  // Zenith angle needed to check tangent point
1018  za = lat_start + za_start - lat;
1019 
1020  // Passage of tangent point requires special attention
1021  if (absza > 90 && abs(za) < 90) {
1022  l = geompath_l_at_r(ppc, r_start) + geompath_l_at_r(ppc, r);
1023  } else {
1024  l = abs(geompath_l_at_r(ppc, r_start) - geompath_l_at_r(ppc, r));
1025  }
1026  }
1027  }
1028  }
1029  }
1030 }
1031 
1032 /*===========================================================================
1033  = 3D functions for level slope and tilt, and lat/lon crossings
1034  ===========================================================================*/
1035 
1056  const Numeric& lat3,
1057  const Numeric& lon5,
1058  const Numeric& lon6,
1059  const Numeric& r15,
1060  const Numeric& r35,
1061  const Numeric& r36,
1062  const Numeric& r16,
1063  const Numeric& lat,
1064  const Numeric& lon) {
1065  // We can't have any assert of *lat* and *lon* here as we can go outside
1066  // the ranges when called from *plevel_slope_3d*.
1067 
1068  if (lat == lat1) {
1069  return r15 + (lon - lon5) * (r16 - r15) / (lon6 - lon5);
1070  } else if (lat == lat3) {
1071  return r35 + (lon - lon5) * (r36 - r35) / (lon6 - lon5);
1072  } else if (lon == lon5) {
1073  return r15 + (lat - lat1) * (r35 - r15) / (lat3 - lat1);
1074  } else if (lon == lon6) {
1075  return r16 + (lat - lat1) * (r36 - r16) / (lat3 - lat1);
1076  } else {
1077  const Numeric fdlat = (lat - lat1) / (lat3 - lat1);
1078  const Numeric fdlon = (lon - lon5) / (lon6 - lon5);
1079  return (1 - fdlat) * (1 - fdlon) * r15 + fdlat * (1 - fdlon) * r35 +
1080  (1 - fdlat) * fdlon * r16 + fdlat * fdlon * r36;
1081  }
1082 }
1083 
1085  Numeric& c2,
1086  const Numeric& lat1,
1087  const Numeric& lat3,
1088  const Numeric& lon5,
1089  const Numeric& lon6,
1090  const Numeric& r15,
1091  const Numeric& r35,
1092  const Numeric& r36,
1093  const Numeric& r16,
1094  const Numeric& lat,
1095  const Numeric& lon,
1096  const Numeric& aa) {
1097  // Save time and avoid numerical problems if all r are equal
1098  if (r15 == r35 && r15 == r36 && r15 == r16 && r35 == r36 && r35 == r16 &&
1099  r36 == r16) {
1100  c1 = 0;
1101  c2 = 0;
1102  }
1103 
1104  else {
1105  // Radius at point of interest
1106  const Numeric r0 =
1107  rsurf_at_latlon(lat1, lat3, lon5, lon6, r15, r35, r36, r16, lat, lon);
1108 
1109  // Size of test angular distance. Unit is degrees.
1110  // dang = 1e-4 corresponds to about 10 m shift horisontally.
1111  // Smaller values should probably be avoided. For example, 1e-5 gave
1112  // significant c2 when it should be zero.
1113  const Numeric dang = 1e-4;
1114 
1115  Numeric lat2, lon2;
1116  latlon_at_aa(lat2, lon2, lat, lon, aa, dang);
1117  resolve_lon(lon2, lon5, lon6);
1118  const Numeric dr1 =
1120  lat1, lat3, lon5, lon6, r15, r35, r36, r16, lat2, lon2) -
1121  r0;
1122 
1123  latlon_at_aa(lat2, lon2, lat, lon, aa, 2 * dang);
1124  resolve_lon(lon2, lon5, lon6);
1125  const Numeric dr2 =
1127  lat1, lat3, lon5, lon6, r15, r35, r36, r16, lat2, lon2) -
1128  r0;
1129 
1130  // Derive linear and quadratic coefficient
1131  c1 = 0.5 * (4 * dr1 - dr2);
1132  c2 = (dr1 - c1) / (dang * dang);
1133  c1 /= dang;
1134  }
1135 }
1136 
1163  Numeric& c2,
1164  ConstVectorView lat_grid,
1165  ConstVectorView lon_grid,
1166  ConstVectorView refellipsoid,
1167  ConstMatrixView z_surf,
1168  const GridPos& gp_lat,
1169  const GridPos& gp_lon,
1170  const Numeric& aa) {
1171  Index ilat = gridpos2gridrange(gp_lat, abs(aa) >= 0);
1172  Index ilon = gridpos2gridrange(gp_lon, aa >= 0);
1173 
1174  // Allow that we are at the upper edge of the grids only for special cases:
1175  const Index llat = lat_grid.nelem() - 1;
1176  // At North pole:
1177  if (ilat >= llat) {
1178  if (lat_grid[llat] > POLELAT) {
1179  ilat = llat - 1;
1180  } else {
1181  throw runtime_error(
1182  "The upper latitude end of the atmosphere "
1183  "reached, that is not allowed.");
1184  }
1185  }
1186  // Complete 360deg lon coverage:
1187  if (ilon >= lon_grid.nelem() - 1) {
1188  if (is_lon_cyclic(lon_grid)) {
1189  ilon = 0;
1190  } else {
1191  throw runtime_error(
1192  "The upper longitude end of the atmosphere "
1193  "reached, that is not allowed.");
1194  }
1195  }
1196 
1197  // Restore latitude and longitude values
1198  Vector itw(2);
1199  Numeric lat, lon;
1200  interpweights(itw, gp_lat);
1201  lat = interp(itw, lat_grid, gp_lat);
1202  interpweights(itw, gp_lon);
1203  lon = interp(itw, lon_grid, gp_lon);
1204 
1205  // Extract values that defines the grid cell
1206  const Numeric lat1 = lat_grid[ilat];
1207  const Numeric lat3 = lat_grid[ilat + 1];
1208  const Numeric lon5 = lon_grid[ilon];
1209  const Numeric lon6 = lon_grid[ilon + 1];
1210  const Numeric re1 = refell2r(refellipsoid, lat1);
1211  const Numeric re3 = refell2r(refellipsoid, lat3);
1212  const Numeric r15 = re1 + z_surf(ilat, ilon);
1213  const Numeric r35 = re3 + z_surf(ilat + 1, ilon);
1214  const Numeric r36 = re3 + z_surf(ilat + 1, ilon + 1);
1215  const Numeric r16 = re1 + z_surf(ilat, ilon + 1);
1216 
1218  c1, c2, lat1, lat3, lon5, lon6, r15, r35, r36, r16, lat, lon, aa);
1219 }
1220 
1237  const Numeric& za,
1238  const Numeric& r0,
1239  Numeric c1,
1240  Numeric c2) {
1241  // Even if the four corner radii of the grid cell differ, both c1 and c2 can
1242  // turn up to be 0. So in contrast to the 2D version, here we accept zero c.
1243 
1244  // Convert c1 and c2 from degrees to radians
1245  c1 *= RAD2DEG;
1246  c2 *= RAD2DEG * RAD2DEG;
1247 
1248  // The nadir angle in radians, and cosine and sine of that angle
1249  const Numeric beta = DEG2RAD * (180 - za);
1250  const Numeric cv = cos(beta);
1251  const Numeric sv = sin(beta);
1252 
1253  // Some repeated terms
1254  const Numeric r0s = r0 * sv;
1255  const Numeric r0c = r0 * cv;
1256  const Numeric c1s = c1 * sv;
1257  const Numeric c1c = c1 * cv;
1258  const Numeric c2s = c2 * sv;
1259  const Numeric c2c = c2 * cv;
1260 
1261  // The vector of polynomial coefficients
1262  //
1263  Index n = 6;
1264  Vector p0(n + 1);
1265  //
1266  p0[0] = r0s - rp * sv;
1267  p0[1] = r0c + c1s;
1268  p0[2] = -r0s / 2 + c1c + c2s;
1269  p0[3] = -r0c / 6 - c1s / 2 + c2c;
1270  p0[4] = r0s / 24 - c1c / 6 - c2s / 2;
1271  p0[5] = r0c / 120 + c1s / 24 - c2c / 6;
1272  p0[6] = -r0s / 720 + c1c / 120 + c2s / 24;
1273  //
1274  // The accuracy when solving the polynomial equation gets worse when
1275  // approaching 0 and 180 deg. The solution is to let the start polynomial
1276  // order decrease when approaching these angles. The values below based
1277  // on practical experience, don't change without making extremly careful
1278  // tests.
1279  //
1280  if (abs(90 - za) > 89.9) {
1281  n = 1;
1282  } else if (abs(90 - za) > 75) {
1283  n = 4;
1284  }
1285 
1286  // Calculate roots of the polynomial
1287  Matrix roots;
1288  int solutionfailure = 1;
1289  //
1290  while (solutionfailure) {
1291  roots.resize(n, 2);
1292  Vector p;
1293  p = p0[Range(0, n + 1)];
1294  solutionfailure = poly_root_solve(roots, p);
1295  if (solutionfailure) {
1296  n -= 1;
1297  assert(n > 0);
1298  }
1299  }
1300 
1301  // If r0=rp, numerical inaccuracy can give a false solution, very close
1302  // to 0, that we must throw away.
1303  Numeric dmin = 0;
1304  if (r0 == rp) {
1305  dmin = 1e-6;
1306  }
1307 
1308  // Find the smallest root with imaginary part = 0, and real part > 0.
1309  //
1310  Numeric dlat = 1.571; // Not interested in solutions above 90 deg!
1311  //
1312  for (Index i = 0; i < n; i++) {
1313  if (roots(i, 1) == 0 && roots(i, 0) > dmin && roots(i, 0) < dlat) {
1314  dlat = roots(i, 0);
1315  }
1316  }
1317 
1318  if (dlat < 1.57) // A somewhat smaller value than start one
1319  {
1320  // Convert back to degrees
1321  dlat = RAD2DEG * dlat;
1322  } else {
1323  dlat = LAT_NOT_FOUND;
1324  }
1325 
1326  return dlat;
1327 }
1328 
1361  Numeric& lon,
1362  Numeric& l,
1363  const Numeric& r_hit,
1364  const Numeric& r_start,
1365  const Numeric& lat_start,
1366  const Numeric& lon_start,
1367  const Numeric& za_start,
1368  const Numeric& ppc,
1369  const Numeric& x,
1370  const Numeric& y,
1371  const Numeric& z,
1372  const Numeric& dx,
1373  const Numeric& dy,
1374  const Numeric& dz) {
1375  assert(za_start >= 0);
1376  assert(za_start <= 180);
1377 
1378  // If above and looking upwards or r_hit below tangent point,
1379  // we have no crossing:
1380  if ((r_start >= r_hit && za_start <= 90) || ppc > r_hit) {
1381  lat = LAT_NOT_FOUND;
1382  lon = LON_NOT_FOUND;
1383  l = L_NOT_FOUND;
1384  }
1385 
1386  else {
1387  // Zenith/nadir
1388  if (za_start < ANGTOL || za_start > 180 - ANGTOL) {
1389  l = abs(r_hit - r_start);
1390  lat = lat_start;
1391  lon = lon_start;
1392  }
1393 
1394  else {
1395  const Numeric p = x * dx + y * dy + z * dz;
1396  const Numeric pp = p * p;
1397  const Numeric q = x * x + y * y + z * z - r_hit * r_hit;
1398  const Numeric sq = sqrt(pp - q);
1399  const Numeric l1 = -p + sq;
1400  const Numeric l2 = -p - sq;
1401 
1402  const Numeric lmin = min(l1, l2);
1403  const Numeric lmax = max(l1, l2);
1404 
1405  // If r_start equals r_hit solutions just above zero can appear (that
1406  // shall be rejected). So we pick the biggest solution if lmin is
1407  // negative or just above zero.
1408  // (Tried to use "if( r_start != r_hit )", but failed occasionally)
1409  if (lmin < 1e-6) {
1410  l = lmax;
1411  } else {
1412  l = lmin;
1413  }
1414  assert(l > 0);
1415 
1416  lat = RAD2DEG * asin((z + dz * l) / r_hit);
1417  lon = RAD2DEG * atan2(y + dy * l, x + dx * l);
1418  }
1419  }
1420 }
1421 
1422 /*===========================================================================
1423  === Basic functions for the Ppath structure
1424  ===========================================================================*/
1425 
1427  const Index& atmosphere_dim,
1428  const Index& np) {
1429  assert(np > 0);
1430  assert(atmosphere_dim >= 1);
1431  assert(atmosphere_dim <= 3);
1432 
1433  ppath.dim = atmosphere_dim;
1434  ppath.np = np;
1435  ppath.constant = -1;
1436 
1437  const Index npos = max(Index(2), atmosphere_dim);
1438  const Index nlos = max(Index(1), atmosphere_dim - 1);
1439 
1440  ppath.start_pos.resize(npos);
1441  ppath.start_pos = -999;
1442  ppath.start_los.resize(nlos);
1443  ppath.start_los = -999;
1444  ppath.start_lstep = 0;
1445  ppath.end_pos.resize(npos);
1446  ppath.end_los.resize(nlos);
1447  ppath.end_lstep = 0;
1448 
1449  ppath.pos.resize(np, npos);
1450  ppath.los.resize(np, nlos);
1451  ppath.r.resize(np);
1452  ppath.lstep.resize(np - 1);
1453 
1454  ppath.gp_p.resize(np);
1455  if (atmosphere_dim >= 2) {
1456  ppath.gp_lat.resize(np);
1457  if (atmosphere_dim == 3) {
1458  ppath.gp_lon.resize(np);
1459  }
1460  }
1461 
1462  ppath_set_background(ppath, 0);
1463  ppath.nreal.resize(np);
1464  ppath.ngroup.resize(np);
1465 }
1466 
1467 void ppath_set_background(Ppath& ppath, const Index& case_nr) {
1468  switch (case_nr) {
1469  case 0:
1470  ppath.background = "unvalid";
1471  break;
1472  case 1:
1473  ppath.background = "space";
1474  break;
1475  case 2:
1476  ppath.background = "surface";
1477  break;
1478  case 3:
1479  ppath.background = "cloud box level";
1480  break;
1481  case 4:
1482  ppath.background = "cloud box interior";
1483  break;
1484  case 9:
1485  ppath.background = "transmitter";
1486  break;
1487  default:
1488  ostringstream os;
1489  os << "Case number " << case_nr << " is not defined.";
1490  throw runtime_error(os.str());
1491  }
1492 }
1493 
1495  if (ppath.background == "unvalid") {
1496  return 0;
1497  } else if (ppath.background == "space") {
1498  return 1;
1499  } else if (ppath.background == "surface") {
1500  return 2;
1501  } else if (ppath.background == "cloud box level") {
1502  return 3;
1503  } else if (ppath.background == "cloud box interior") {
1504  return 4;
1505  } else if (ppath.background == "transmitter") {
1506  return 9;
1507  } else {
1508  ostringstream os;
1509  os << "The string " << ppath.background
1510  << " is not a valid background case.";
1511  throw runtime_error(os.str());
1512  }
1513 }
1514 
1515 void ppath_copy(Ppath& ppath1, const Ppath& ppath2, const Index& ncopy) {
1516  Index n;
1517  if (ncopy < 0) {
1518  n = ppath2.np;
1519  } else {
1520  n = ncopy;
1521  }
1522 
1523  assert(ppath1.np >= n);
1524 
1525  // The field np shall not be copied !
1526 
1527  ppath1.dim = ppath2.dim;
1528  ppath1.constant = ppath2.constant;
1529  ppath1.background = ppath2.background;
1530 
1531  // As index 0 always is included in the copying, the end point is covered
1532  ppath1.end_pos = ppath2.end_pos;
1533  ppath1.end_los = ppath2.end_los;
1534  ppath1.end_lstep = ppath2.end_lstep;
1535 
1536  // Copy start point only if copying fills up ppath1
1537  if (n == ppath1.np) {
1538  ppath1.start_pos = ppath2.start_pos;
1539  ppath1.start_los = ppath2.start_los;
1540  ppath1.start_lstep = ppath2.start_lstep;
1541  }
1542 
1543  ppath1.pos(Range(0, n), joker) = ppath2.pos(Range(0, n), joker);
1544  ppath1.los(Range(0, n), joker) = ppath2.los(Range(0, n), joker);
1545  ppath1.r[Range(0, n)] = ppath2.r[Range(0, n)];
1546  ppath1.nreal[Range(0, n)] = ppath2.nreal[Range(0, n)];
1547  ppath1.ngroup[Range(0, n)] = ppath2.ngroup[Range(0, n)];
1548  if (n > 1) {
1549  ppath1.lstep[Range(0, n - 1)] = ppath2.lstep[Range(0, n - 1)];
1550  }
1551 
1552  for (Index i = 0; i < n; i++) {
1553  gridpos_copy(ppath1.gp_p[i], ppath2.gp_p[i]);
1554 
1555  if (ppath1.dim >= 2) {
1556  gridpos_copy(ppath1.gp_lat[i], ppath2.gp_lat[i]);
1557  }
1558 
1559  if (ppath1.dim == 3) {
1560  gridpos_copy(ppath1.gp_lon[i], ppath2.gp_lon[i]);
1561  }
1562  }
1563 }
1564 
1581 void ppath_append(Ppath& ppath1, const Ppath& ppath2) {
1582  const Index n1 = ppath1.np;
1583  const Index n2 = ppath2.np;
1584 
1585  Ppath ppath;
1586  ppath_init_structure(ppath, ppath1.dim, n1);
1587  ppath_copy(ppath, ppath1, -1);
1588 
1589  ppath_init_structure(ppath1, ppath1.dim, n1 + n2 - 1);
1590  ppath_copy(ppath1, ppath, -1);
1591 
1592  // Append data from ppath2
1593  Index i1;
1594  for (Index i = 1; i < n2; i++) {
1595  i1 = n1 + i - 1;
1596 
1597  ppath1.pos(i1, 0) = ppath2.pos(i, 0);
1598  ppath1.pos(i1, 1) = ppath2.pos(i, 1);
1599  ppath1.los(i1, 0) = ppath2.los(i, 0);
1600  ppath1.r[i1] = ppath2.r[i];
1601  ppath1.nreal[i1] = ppath2.nreal[i];
1602  ppath1.ngroup[i1] = ppath2.ngroup[i];
1603  gridpos_copy(ppath1.gp_p[i1], ppath2.gp_p[i]);
1604 
1605  if (ppath1.dim >= 2) {
1606  gridpos_copy(ppath1.gp_lat[i1], ppath2.gp_lat[i]);
1607  }
1608 
1609  if (ppath1.dim == 3) {
1610  ppath1.pos(i1, 2) = ppath2.pos(i, 2);
1611  ppath1.los(i1, 1) = ppath2.los(i, 1);
1612  gridpos_copy(ppath1.gp_lon[i1], ppath2.gp_lon[i]);
1613  }
1614 
1615  ppath1.lstep[i1 - 1] = ppath2.lstep[i - 1];
1616  }
1617 
1618  if (ppath_what_background(ppath2)) {
1619  ppath1.background = ppath2.background;
1620  }
1621 
1622  ppath.start_pos = ppath2.start_pos;
1623  ppath.start_los = ppath2.start_los;
1624  ppath.start_lstep = ppath2.start_lstep;
1625 }
1626 
1627 /*===========================================================================
1628  === 1D/2D/3D start and end ppath functions
1629  ===========================================================================*/
1630 
1641 void ppath_start_1d(Numeric& r_start,
1642  Numeric& lat_start,
1643  Numeric& za_start,
1644  Index& ip,
1645  const Ppath& ppath) {
1646  // Number of points in the incoming ppath
1647  const Index imax = ppath.np - 1;
1648 
1649  // Extract starting radius, zenith angle and latitude
1650  r_start = ppath.r[imax];
1651  lat_start = ppath.pos(imax, 1);
1652  za_start = ppath.los(imax, 0);
1653 
1654  // Determine index of the pressure level being the lower limit for the
1655  // grid range of interest.
1656  //
1657  ip = gridpos2gridrange(ppath.gp_p[imax], za_start <= 90);
1658 }
1659 
1670 void ppath_end_1d(Ppath& ppath,
1671  ConstVectorView r_v,
1672  ConstVectorView lat_v,
1673  ConstVectorView za_v,
1674  ConstVectorView lstep,
1675  ConstVectorView n_v,
1676  ConstVectorView ng_v,
1677  ConstVectorView z_field,
1678  ConstVectorView refellipsoid,
1679  const Index& ip,
1680  const Index& endface,
1681  const Numeric& ppc) {
1682  // Number of path points
1683  const Index np = r_v.nelem();
1684 
1685  // Re-allocate ppath for return results and fill the structure
1686  ppath_init_structure(ppath, 1, np);
1687 
1688  ppath.constant = ppc;
1689 
1690  // Help variables that are common for all points.
1691  const Numeric r1 = refellipsoid[0] + z_field[ip];
1692  const Numeric dr = z_field[ip + 1] - z_field[ip];
1693 
1694  for (Index i = 0; i < np; i++) {
1695  ppath.r[i] = r_v[i];
1696  ppath.pos(i, 0) = r_v[i] - refellipsoid[0];
1697  ppath.pos(i, 1) = lat_v[i];
1698  ppath.los(i, 0) = za_v[i];
1699  ppath.nreal[i] = n_v[i];
1700  ppath.ngroup[i] = ng_v[i];
1701 
1702  ppath.gp_p[i].idx = ip;
1703  ppath.gp_p[i].fd[0] = (r_v[i] - r1) / dr;
1704  ppath.gp_p[i].fd[1] = 1 - ppath.gp_p[i].fd[0];
1705  gridpos_check_fd(ppath.gp_p[i]);
1706 
1707  if (i > 0) {
1708  ppath.lstep[i - 1] = lstep[i - 1];
1709  }
1710  }
1711  gridpos_check_fd(ppath.gp_p[np - 1]);
1712 
1713  //--- End point is the surface
1714  if (endface == 7) {
1715  ppath_set_background(ppath, 2);
1716  }
1717 
1718  //--- End point is on top of a pressure level
1719  else if (endface <= 4) {
1720  gridpos_force_end_fd(ppath.gp_p[np - 1], z_field.nelem());
1721  }
1722 }
1723 
1734 void ppath_start_2d(Numeric& r_start,
1735  Numeric& lat_start,
1736  Numeric& za_start,
1737  Index& ip,
1738  Index& ilat,
1739  Numeric& lat1,
1740  Numeric& lat3,
1741  Numeric& r1a,
1742  Numeric& r3a,
1743  Numeric& r3b,
1744  Numeric& r1b,
1745  Numeric& rsurface1,
1746  Numeric& rsurface3,
1747  Ppath& ppath,
1748  ConstVectorView lat_grid,
1749  ConstMatrixView z_field,
1750  ConstVectorView refellipsoid,
1751  ConstVectorView z_surface) {
1752  // Number of points in the incoming ppath
1753  const Index imax = ppath.np - 1;
1754 
1755  // Extract starting radius, zenith angle and latitude
1756  r_start = ppath.r[imax];
1757  lat_start = ppath.pos(imax, 1);
1758  za_start = ppath.los(imax, 0);
1759 
1760  // Determine interesting latitude grid range and latitude end points of
1761  // the range.
1762  //
1763  ilat = gridpos2gridrange(ppath.gp_lat[imax], za_start >= 0);
1764  //
1765  lat1 = lat_grid[ilat];
1766  lat3 = lat_grid[ilat + 1];
1767 
1768  // Determine interesting pressure grid range. Do this first assuming that
1769  // the pressure levels are not tilted (that is, abs(za_start<=90) always
1770  // mean upward observation).
1771  // Set radius for the corners of the grid cell and the radial slope of
1772  // pressure level limits of the grid cell to match the found ip.
1773  //
1774  ip = gridpos2gridrange(ppath.gp_p[imax], abs(za_start) <= 90);
1775  //
1776  const Numeric re1 = refell2r(refellipsoid, lat_grid[ilat]);
1777  const Numeric re3 = refell2r(refellipsoid, lat_grid[ilat + 1]);
1778  //
1779  r1a = re1 + z_field(ip, ilat); // lower-left
1780  r3a = re3 + z_field(ip, ilat + 1); // lower-right
1781  r3b = re3 + z_field(ip + 1, ilat + 1); // upper-right
1782  r1b = re1 + z_field(ip + 1, ilat); // upper-left
1783 
1784  // This part is a fix to catch start postions on top of a pressure level
1785  // that does not have an end fractional distance for the first step.
1786  {
1787  // Radius of lower and upper pressure level at the start position
1788  const Numeric rlow = rsurf_at_lat(lat1, lat3, r1a, r3a, lat_start);
1789  const Numeric rupp = rsurf_at_lat(lat1, lat3, r1b, r3b, lat_start);
1790  if (abs(r_start - rlow) < RTOL || abs(r_start - rupp) < RTOL) {
1791  gridpos_force_end_fd(ppath.gp_p[imax], z_field.nrows());
1792  }
1793  }
1794 
1795  // Slopes of pressure levels
1796  Numeric c2, c4;
1797  plevel_slope_2d(c2, lat1, lat3, r1a, r3a);
1798  plevel_slope_2d(c4, lat1, lat3, r1b, r3b);
1799 
1800  // Check if the LOS zenith angle happen to be between 90 and the zenith angle
1801  // of the pressure level (that is, 90 + tilt of pressure level), and in
1802  // that case if ip must be changed. This check is only needed when the
1803  // start point is on a pressure level.
1804  //
1805  if (is_gridpos_at_index_i(ppath.gp_p[imax], ip)) {
1806  Numeric tilt = plevel_angletilt(r_start, c2);
1807 
1808  if (is_los_downwards(za_start, tilt)) {
1809  ip--;
1810  r1b = r1a;
1811  r3b = r3a;
1812  r1a = re1 + z_field(ip, ilat);
1813  r3a = re3 + z_field(ip, ilat + 1);
1814  plevel_slope_2d(c2, lat1, lat3, r1a, r3a);
1815  }
1816  } else if (is_gridpos_at_index_i(ppath.gp_p[imax], ip + 1)) {
1817  Numeric tilt = plevel_angletilt(r_start, c4);
1818 
1819  if (!is_los_downwards(za_start, tilt)) {
1820  ip++;
1821  r1a = r1b;
1822  r3a = r3b;
1823  r3b = re3 + z_field(ip + 1, ilat + 1);
1824  r1b = re1 + z_field(ip + 1, ilat);
1825  plevel_slope_2d(c4, lat1, lat3, r1b, r3b);
1826  }
1827  }
1828 
1829  // Surface radius at latitude end points
1830  rsurface1 = re1 + z_surface[ilat];
1831  rsurface3 = re3 + z_surface[ilat + 1];
1832 }
1833 
1844 void ppath_end_2d(Ppath& ppath,
1845  ConstVectorView r_v,
1846  ConstVectorView lat_v,
1847  ConstVectorView za_v,
1848  ConstVectorView lstep,
1849  ConstVectorView n_v,
1850  ConstVectorView ng_v,
1851  ConstVectorView lat_grid,
1852  ConstMatrixView z_field,
1853  ConstVectorView refellipsoid,
1854  const Index& ip,
1855  const Index& ilat,
1856  const Index& endface,
1857  const Numeric& ppc) {
1858  // Number of path points
1859  const Index np = r_v.nelem();
1860  const Index imax = np - 1;
1861 
1862  // Re-allocate ppath for return results and fill the structure
1863  //
1864  ppath_init_structure(ppath, 2, np);
1865 
1866  ppath.constant = ppc;
1867 
1868  // Help variables that are common for all points.
1869  const Numeric dlat = lat_grid[ilat + 1] - lat_grid[ilat];
1870  const Numeric z1low = z_field(ip, ilat);
1871  const Numeric z1upp = z_field(ip + 1, ilat);
1872  const Numeric dzlow = z_field(ip, ilat + 1) - z1low;
1873  const Numeric dzupp = z_field(ip + 1, ilat + 1) - z1upp;
1874  Numeric re = refell2r(refellipsoid, lat_grid[ilat]);
1875  const Numeric r1low = re + z1low;
1876  const Numeric r1upp = re + z1upp;
1877  re = refell2r(refellipsoid, lat_grid[ilat + 1]);
1878  const Numeric drlow = re + z_field(ip, ilat + 1) - r1low;
1879  const Numeric drupp = re + z_field(ip + 1, ilat + 1) - r1upp;
1880 
1881  for (Index i = 0; i < np; i++) {
1882  ppath.r[i] = r_v[i];
1883  ppath.pos(i, 1) = lat_v[i];
1884  ppath.los(i, 0) = za_v[i];
1885  ppath.nreal[i] = n_v[i];
1886  ppath.ngroup[i] = ng_v[i];
1887 
1888  // Weight in the latitude direction
1889  Numeric w = (lat_v[i] - lat_grid[ilat]) / dlat;
1890 
1891  // Radius of lower and upper face at present latitude
1892  const Numeric rlow = r1low + w * drlow;
1893  const Numeric rupp = r1upp + w * drupp;
1894 
1895  // Geometrical altitude of lower and upper face at present latitude
1896  const Numeric zlow = z1low + w * dzlow;
1897  const Numeric zupp = z1upp + w * dzupp;
1898 
1899  ppath.gp_p[i].idx = ip;
1900  ppath.gp_p[i].fd[0] = (r_v[i] - rlow) / (rupp - rlow);
1901  ppath.gp_p[i].fd[1] = 1 - ppath.gp_p[i].fd[0];
1902  gridpos_check_fd(ppath.gp_p[i]);
1903 
1904  ppath.pos(i, 0) = zlow + ppath.gp_p[i].fd[0] * (zupp - zlow);
1905 
1906  ppath.gp_lat[i].idx = ilat;
1907  ppath.gp_lat[i].fd[0] = (lat_v[i] - lat_grid[ilat]) / dlat;
1908  ppath.gp_lat[i].fd[1] = 1 - ppath.gp_lat[i].fd[0];
1909  gridpos_check_fd(ppath.gp_lat[i]);
1910 
1911  if (i > 0) {
1912  ppath.lstep[i - 1] = lstep[i - 1];
1913  }
1914  }
1915  gridpos_check_fd(ppath.gp_p[imax]);
1916  gridpos_check_fd(ppath.gp_lat[imax]);
1917 
1918  // Do end-face specific tasks
1919  if (endface == 7) {
1920  ppath_set_background(ppath, 2);
1921  }
1922 
1923  // Set fractional distance for end point
1924  //
1925  if (endface == 1 || endface == 3) {
1926  gridpos_force_end_fd(ppath.gp_lat[np - 1], lat_grid.nelem());
1927  } else if (endface == 2 || endface == 4) {
1928  gridpos_force_end_fd(ppath.gp_p[np - 1], z_field.nrows());
1929  }
1930 
1931  // Handle cases where exactly a corner is hit, or when slipping outside of
1932  // the grid box due to numerical inaccuarcy
1933  if (ppath.gp_p[imax].fd[0] < 0 || ppath.gp_p[imax].fd[1] < 0) {
1934  gridpos_force_end_fd(ppath.gp_p[imax], z_field.nrows());
1935  }
1936  if (ppath.gp_lat[imax].fd[0] < 0 || ppath.gp_lat[imax].fd[1] < 0) {
1937  gridpos_force_end_fd(ppath.gp_lat[imax], lat_grid.nelem());
1938  }
1939 }
1940 
1951 void ppath_start_3d(Numeric& r_start,
1952  Numeric& lat_start,
1953  Numeric& lon_start,
1954  Numeric& za_start,
1955  Numeric& aa_start,
1956  Index& ip,
1957  Index& ilat,
1958  Index& ilon,
1959  Numeric& lat1,
1960  Numeric& lat3,
1961  Numeric& lon5,
1962  Numeric& lon6,
1963  Numeric& r15a,
1964  Numeric& r35a,
1965  Numeric& r36a,
1966  Numeric& r16a,
1967  Numeric& r15b,
1968  Numeric& r35b,
1969  Numeric& r36b,
1970  Numeric& r16b,
1971  Numeric& rsurface15,
1972  Numeric& rsurface35,
1973  Numeric& rsurface36,
1974  Numeric& rsurface16,
1975  Ppath& ppath,
1976  ConstVectorView lat_grid,
1977  ConstVectorView lon_grid,
1978  ConstTensor3View z_field,
1979  ConstVectorView refellipsoid,
1980  ConstMatrixView z_surface) {
1981  // Index of last point in the incoming ppath
1982  const Index imax = ppath.np - 1;
1983 
1984  // Extract starting radius, zenith angle and latitude
1985  r_start = ppath.r[imax];
1986  lat_start = ppath.pos(imax, 1);
1987  lon_start = ppath.pos(imax, 2);
1988  za_start = ppath.los(imax, 0);
1989  aa_start = ppath.los(imax, 1);
1990 
1991  // Number of lat/lon
1992  const Index nlat = lat_grid.nelem();
1993  const Index nlon = lon_grid.nelem();
1994 
1995  // Lower index of lat and lon ranges of interest
1996  //
1997  // The longitude is undefined at the poles and as the azimuth angle
1998  // is defined in other way at the poles.
1999  //
2000  if (lat_start == 90) {
2001  ilat = nlat - 2;
2002  GridPos gp_tmp;
2003  gridpos(gp_tmp, lon_grid, aa_start);
2004  if (aa_start < 180) {
2005  ilon = gridpos2gridrange(gp_tmp, 1);
2006  } else {
2007  ilon = gridpos2gridrange(gp_tmp, 0);
2008  }
2009  } else if (lat_start == -90) {
2010  ilat = 0;
2011  GridPos gp_tmp;
2012  gridpos(gp_tmp, lon_grid, aa_start);
2013  if (aa_start < 180) {
2014  ilon = gridpos2gridrange(gp_tmp, 1);
2015  } else {
2016  ilon = gridpos2gridrange(gp_tmp, 0);
2017  }
2018  } else {
2019  if (lat_start > 0) {
2020  ilat = gridpos2gridrange(ppath.gp_lat[imax], abs(aa_start) < 90);
2021  } else {
2022  ilat = gridpos2gridrange(ppath.gp_lat[imax], abs(aa_start) <= 90);
2023  }
2024  if (lon_start < lon_grid[nlon - 1]) {
2025  ilon = gridpos2gridrange(ppath.gp_lon[imax], aa_start >= 0);
2026  } else {
2027  ilon = nlon - 2;
2028  }
2029  }
2030  //
2031  lat1 = lat_grid[ilat];
2032  lat3 = lat_grid[ilat + 1];
2033  lon5 = lon_grid[ilon];
2034  lon6 = lon_grid[ilon + 1];
2035 
2036  // Determine interesting pressure grid range. Do this first assuming that
2037  // the pressure levels are not tilted (that is, abs(za_start<=90) always
2038  // mean upward observation).
2039  // Set radius for the corners of the grid cell and the radial slope of
2040  // pressure level limits of the grid cell to match the found ip.
2041  //
2042  ip = gridpos2gridrange(ppath.gp_p[imax], za_start <= 90);
2043  //
2044  const Numeric re1 = refell2r(refellipsoid, lat_grid[ilat]);
2045  const Numeric re3 = refell2r(refellipsoid, lat_grid[ilat + 1]);
2046  //
2047  r15a = re1 + z_field(ip, ilat, ilon);
2048  r35a = re3 + z_field(ip, ilat + 1, ilon);
2049  r36a = re3 + z_field(ip, ilat + 1, ilon + 1);
2050  r16a = re1 + z_field(ip, ilat, ilon + 1);
2051  r15b = re1 + z_field(ip + 1, ilat, ilon);
2052  r35b = re3 + z_field(ip + 1, ilat + 1, ilon);
2053  r36b = re3 + z_field(ip + 1, ilat + 1, ilon + 1);
2054  r16b = re1 + z_field(ip + 1, ilat, ilon + 1);
2055 
2056  // Check if the LOS zenith angle happen to be between 90 and the zenith angle
2057  // of the pressure level (that is, 90 + tilt of pressure level), and in
2058  // that case if ip must be changed. This check is only needed when the
2059  // start point is on a pressure level.
2060  //
2061  if (fabs(za_start - 90) <= 10) // To save time. Ie. max tilt assumed =10 deg
2062  {
2063  if (is_gridpos_at_index_i(ppath.gp_p[imax], ip)) {
2064  // Slope and angular tilt of lower pressure level
2065  Numeric c2a, c2b;
2066  plevel_slope_3d(c2a,
2067  c2b,
2068  lat1,
2069  lat3,
2070  lon5,
2071  lon6,
2072  r15a,
2073  r35a,
2074  r36a,
2075  r16a,
2076  lat_start,
2077  lon_start,
2078  aa_start);
2079  Numeric tilt = plevel_angletilt(r_start, c2a);
2080  // Negelect very small tilts, likely caused by numerical problems
2081  if (abs(tilt) > 1e-4 && is_los_downwards(za_start, tilt)) {
2082  ip--;
2083  r15b = r15a;
2084  r35b = r35a;
2085  r36b = r36a;
2086  r16b = r16a;
2087  r15a = re1 + z_field(ip, ilat, ilon);
2088  r35a = re3 + z_field(ip, ilat + 1, ilon);
2089  r36a = re3 + z_field(ip, ilat + 1, ilon + 1);
2090  r16a = re1 + z_field(ip, ilat, ilon + 1);
2091  }
2092  } else if (is_gridpos_at_index_i(ppath.gp_p[imax], ip + 1)) {
2093  // Slope and angular tilt of upper pressure level
2094  Numeric c4a, c4b;
2095  plevel_slope_3d(c4a,
2096  c4b,
2097  lat1,
2098  lat3,
2099  lon5,
2100  lon6,
2101  r15b,
2102  r35b,
2103  r36b,
2104  r16b,
2105  lat_start,
2106  lon_start,
2107  aa_start);
2108  Numeric tilt = plevel_angletilt(r_start, c4a);
2109  //
2110  if (!is_los_downwards(za_start, tilt)) {
2111  ip++;
2112  r15a = r15b;
2113  r35a = r35b;
2114  r36a = r36b;
2115  r16a = r16b;
2116  r15b = re1 + z_field(ip + 1, ilat, ilon);
2117  r35b = re3 + z_field(ip + 1, ilat + 1, ilon);
2118  r36b = re3 + z_field(ip + 1, ilat + 1, ilon + 1);
2119  r16b = re1 + z_field(ip + 1, ilat, ilon + 1);
2120  }
2121  }
2122  }
2123 
2124  // Surface radius at latitude/longitude corner points
2125  rsurface15 = re1 + z_surface(ilat, ilon);
2126  rsurface35 = re3 + z_surface(ilat + 1, ilon);
2127  rsurface36 = re3 + z_surface(ilat + 1, ilon + 1);
2128  rsurface16 = re1 + z_surface(ilat, ilon + 1);
2129 }
2130 
2141 void ppath_end_3d(Ppath& ppath,
2142  ConstVectorView r_v,
2143  ConstVectorView lat_v,
2144  ConstVectorView lon_v,
2145  ConstVectorView za_v,
2146  ConstVectorView aa_v,
2147  ConstVectorView lstep,
2148  ConstVectorView n_v,
2149  ConstVectorView ng_v,
2150  ConstVectorView lat_grid,
2151  ConstVectorView lon_grid,
2152  ConstTensor3View z_field,
2153  ConstVectorView refellipsoid,
2154  const Index& ip,
2155  const Index& ilat,
2156  const Index& ilon,
2157  const Index& endface,
2158  const Numeric& ppc) {
2159  // Number of path points
2160  const Index np = r_v.nelem();
2161  const Index imax = np - 1;
2162 
2163  // Re-allocate ppath for return results and fill the structure
2164  //
2165  ppath_init_structure(ppath, 3, np);
2166  //
2167  ppath.constant = ppc;
2168 
2169  // Help variables that are common for all points.
2170  const Numeric lat1 = lat_grid[ilat];
2171  const Numeric lat3 = lat_grid[ilat + 1];
2172  const Numeric lon5 = lon_grid[ilon];
2173  const Numeric lon6 = lon_grid[ilon + 1];
2174  const Numeric re1 = refell2r(refellipsoid, lat_grid[ilat]);
2175  const Numeric re3 = refell2r(refellipsoid, lat_grid[ilat + 1]);
2176  const Numeric r15a = re1 + z_field(ip, ilat, ilon);
2177  const Numeric r35a = re3 + z_field(ip, ilat + 1, ilon);
2178  const Numeric r36a = re3 + z_field(ip, ilat + 1, ilon + 1);
2179  const Numeric r16a = re1 + z_field(ip, ilat, ilon + 1);
2180  const Numeric r15b = re1 + z_field(ip + 1, ilat, ilon);
2181  const Numeric r35b = re3 + z_field(ip + 1, ilat + 1, ilon);
2182  const Numeric r36b = re3 + z_field(ip + 1, ilat + 1, ilon + 1);
2183  const Numeric r16b = re1 + z_field(ip + 1, ilat, ilon + 1);
2184  const Numeric dlat = lat3 - lat1;
2185  const Numeric dlon = lon6 - lon5;
2186 
2187  for (Index i = 0; i < np; i++) {
2188  // Radius of pressure levels at present lat and lon
2189  const Numeric rlow = rsurf_at_latlon(
2190  lat1, lat3, lon5, lon6, r15a, r35a, r36a, r16a, lat_v[i], lon_v[i]);
2191  const Numeric rupp = rsurf_at_latlon(
2192  lat1, lat3, lon5, lon6, r15b, r35b, r36b, r16b, lat_v[i], lon_v[i]);
2193 
2194  // Position and LOS
2195  ppath.r[i] = r_v[i];
2196  ppath.pos(i, 1) = lat_v[i];
2197  ppath.pos(i, 2) = lon_v[i];
2198  ppath.los(i, 0) = za_v[i];
2199  ppath.los(i, 1) = aa_v[i];
2200  ppath.nreal[i] = n_v[i];
2201  ppath.ngroup[i] = ng_v[i];
2202 
2203  // Pressure grid index
2204  ppath.gp_p[i].idx = ip;
2205  ppath.gp_p[i].fd[0] = (r_v[i] - rlow) / (rupp - rlow);
2206  ppath.gp_p[i].fd[1] = 1 - ppath.gp_p[i].fd[0];
2207  gridpos_check_fd(ppath.gp_p[i]);
2208 
2209  // Geometrical altitude
2210  const Numeric re = rsurf_at_latlon(
2211  lat1, lat3, lon5, lon6, re1, re3, re3, re1, lat_v[i], lon_v[i]);
2212  const Numeric zlow = rlow - re;
2213  const Numeric zupp = rupp - re;
2214  //
2215  ppath.pos(i, 0) = zlow + ppath.gp_p[i].fd[0] * (zupp - zlow);
2216 
2217  // Latitude grid index
2218  ppath.gp_lat[i].idx = ilat;
2219  ppath.gp_lat[i].fd[0] = (lat_v[i] - lat1) / dlat;
2220  ppath.gp_lat[i].fd[1] = 1 - ppath.gp_lat[i].fd[0];
2221  gridpos_check_fd(ppath.gp_lat[i]);
2222 
2223  // Longitude grid index
2224  //
2225  // The longitude is undefined at the poles. The grid index is set to
2226  // the start point.
2227  //
2228  if (abs(lat_v[i]) < POLELAT) {
2229  ppath.gp_lon[i].idx = ilon;
2230  ppath.gp_lon[i].fd[0] = (lon_v[i] - lon5) / dlon;
2231  ppath.gp_lon[i].fd[1] = 1 - ppath.gp_lon[i].fd[0];
2232  gridpos_check_fd(ppath.gp_lon[i]);
2233  } else {
2234  ppath.gp_lon[i].idx = 0;
2235  ppath.gp_lon[i].fd[0] = 0;
2236  ppath.gp_lon[i].fd[1] = 1;
2237  }
2238 
2239  if (i > 0) {
2240  ppath.lstep[i - 1] = lstep[i - 1];
2241  }
2242  }
2243 
2244  // Do end-face specific tasks
2245  if (endface == 7) {
2246  ppath_set_background(ppath, 2);
2247  }
2248 
2249  // Set fractional distance for end point
2250  //
2251  if (endface == 1 || endface == 3) {
2252  gridpos_force_end_fd(ppath.gp_lat[imax], lat_grid.nelem());
2253  } else if (endface == 2 || endface == 4) {
2254  gridpos_force_end_fd(ppath.gp_p[imax], z_field.npages());
2255  } else if (endface == 5 || endface == 6) {
2256  gridpos_force_end_fd(ppath.gp_lon[imax], lon_grid.nelem());
2257  }
2258 
2259  // Handle cases where exactly a corner is hit, or when slipping outside of
2260  // the grid box due to numerical inaccuarcy
2261  if (ppath.gp_p[imax].fd[0] < 0 || ppath.gp_p[imax].fd[1] < 0) {
2262  gridpos_force_end_fd(ppath.gp_p[imax], z_field.npages());
2263  }
2264  if (ppath.gp_lat[imax].fd[0] < 0 || ppath.gp_lat[imax].fd[1] < 0) {
2265  gridpos_force_end_fd(ppath.gp_lat[imax], lat_grid.nelem());
2266  }
2267  if (ppath.gp_lon[imax].fd[0] < 0 || ppath.gp_lon[imax].fd[1] < 0) {
2268  gridpos_force_end_fd(ppath.gp_lon[imax], lon_grid.nelem());
2269  }
2270 }
2271 
2272 /*===========================================================================
2273  === Core functions for geometrical ppath_step calculations
2274  ===========================================================================*/
2275 
2301  Vector& lat_v,
2302  Vector& za_v,
2303  Numeric& lstep,
2304  Index& endface,
2305  const Numeric& r_start0,
2306  const Numeric& lat_start,
2307  const Numeric& za_start,
2308  const Numeric& ppc,
2309  const Numeric& lmax,
2310  const Numeric& ra,
2311  const Numeric& rb,
2312  const Numeric& rsurface) {
2313  Numeric r_start = r_start0;
2314 
2315  assert(rb > ra);
2316  assert(r_start >= ra - RTOL);
2317  assert(r_start <= rb + RTOL);
2318 
2319  // Shift radius if outside
2320  if (r_start < ra) {
2321  r_start = ra;
2322  } else if (r_start > rb) {
2323  r_start = rb;
2324  }
2325 
2326  Numeric r_end;
2327  bool tanpoint = false;
2328  endface = -1;
2329 
2330  // If upward, end point radius is always rb
2331  if (za_start <= 90) {
2332  endface = 4;
2333  r_end = rb;
2334  }
2335 
2336  else {
2337  // Path reaches ra:
2338  if (ra > rsurface && ra > ppc) {
2339  endface = 2;
2340  r_end = ra;
2341  }
2342 
2343  // Path reaches the surface:
2344  else if (rsurface > ppc) {
2345  endface = 7;
2346  r_end = rsurface;
2347  }
2348 
2349  // The remaining option is a tangent point and back to rb
2350  else {
2351  endface = 4;
2352  r_end = rb;
2353  tanpoint = true;
2354  }
2355  }
2356 
2357  assert(endface > 0);
2358 
2360  lat_v,
2361  za_v,
2362  lstep,
2363  ppc,
2364  r_start,
2365  lat_start,
2366  za_start,
2367  r_end,
2368  tanpoint,
2369  lmax);
2370 }
2371 
2373  ConstVectorView z_field,
2374  ConstVectorView refellipsoid,
2375  const Numeric& z_surface,
2376  const Numeric& lmax) {
2377  // Starting radius, zenith angle and latitude
2378  Numeric r_start, lat_start, za_start;
2379 
2380  // Index of the pressure level being the lower limit for the
2381  // grid range of interest.
2382  Index ip;
2383 
2384  // Determine the variables defined above, and make asserts of input
2385  ppath_start_1d(r_start, lat_start, za_start, ip, ppath);
2386 
2387  // If the field "constant" is negative, this is the first call of the
2388  // function and the path constant shall be calculated.
2389  Numeric ppc;
2390  if (ppath.constant < 0) {
2391  ppc = geometrical_ppc(r_start, za_start);
2392  } else {
2393  ppc = ppath.constant;
2394  }
2395 
2396  // The path is determined by another function. Determine some variables
2397  // needed bý that function and call the function.
2398  //
2399  // Vars to hold found path points, path step length and coding for end face
2400  Vector r_v, lat_v, za_v;
2401  Numeric lstep;
2402  Index endface;
2403  //
2404  do_gridrange_1d(r_v,
2405  lat_v,
2406  za_v,
2407  lstep,
2408  endface,
2409  r_start,
2410  lat_start,
2411  za_start,
2412  ppc,
2413  lmax,
2414  refellipsoid[0] + z_field[ip],
2415  refellipsoid[0] + z_field[ip + 1],
2416  refellipsoid[0] + z_surface);
2417 
2418  // Fill *ppath*
2419  const Index np = r_v.nelem();
2420  ppath_end_1d(ppath,
2421  r_v,
2422  lat_v,
2423  za_v,
2424  Vector(np - 1, lstep),
2425  Vector(np, 1),
2426  Vector(np, 1),
2427  z_field,
2428  refellipsoid,
2429  ip,
2430  endface,
2431  ppc);
2432 }
2433 
2440  Vector& lat_v,
2441  Vector& za_v,
2442  Numeric& lstep,
2443  Index& endface,
2444  const Numeric& r_start0,
2445  const Numeric& lat_start0,
2446  const Numeric& za_start,
2447  const Numeric& l_start,
2448  const Index& icall,
2449  const Numeric& ppc,
2450  const Numeric& lmax,
2451  const Numeric& lat1,
2452  const Numeric& lat3,
2453  const Numeric& r1a,
2454  const Numeric& r3a,
2455  const Numeric& r3b,
2456  const Numeric& r1b,
2457  const Numeric& rsurface1,
2458  const Numeric& rsurface3) {
2459  Numeric r_start = r_start0;
2460  Numeric lat_start = lat_start0;
2461 
2462  assert(icall < 10);
2463 
2464  // Assert latitude and longitude
2465  assert(lat_start >= lat1 - LATLONTOL);
2466  assert(lat_start <= lat3 + LATLONTOL);
2467 
2468  // Shift latitude and longitude if outside
2469  if (lat_start < lat1) {
2470  lat_start = lat1;
2471  } else if (lat_start > lat3) {
2472  lat_start = lat3;
2473  }
2474 
2475  // Radius of lower and upper pressure level at the start position
2476  Numeric rlow = rsurf_at_lat(lat1, lat3, r1a, r3a, lat_start);
2477  Numeric rupp = rsurf_at_lat(lat1, lat3, r1b, r3b, lat_start);
2478 
2479  // Assert radius (some extra tolerance is needed for radius)
2480  assert(r_start >= rlow - RTOL);
2481  assert(r_start <= rupp + RTOL);
2482 
2483  // Shift radius if outside
2484  if (r_start < rlow) {
2485  r_start = rlow;
2486  } else if (r_start > rupp) {
2487  r_start = rupp;
2488  }
2489 
2490  // Position and LOS in cartesian coordinates
2491  Numeric x, z, dx, dz;
2492  poslos2cart(x, z, dx, dz, r_start, lat_start, za_start);
2493 
2494  // Some booleans to check for recursive call
2495  bool unsafe = false;
2496  bool do_surface = false;
2497 
2498  // Determine the position of the end point
2499  //
2500  endface = 0;
2501  //
2502  Numeric r_end, lat_end, l_end;
2503 
2504  // Zenith and nadir looking are handled as special cases
2505  const Numeric absza = abs(za_start);
2506 
2507  // Zenith looking
2508  if (absza < ANGTOL) {
2509  l_end = rupp - r_start;
2510  endface = 4;
2511  }
2512 
2513  // Nadir looking
2514  else if (absza > 180 - ANGTOL) {
2515  const Numeric rsurface =
2516  rsurf_at_lat(lat1, lat3, rsurface1, rsurface3, lat_start);
2517 
2518  if (rlow > rsurface) {
2519  l_end = r_start - rlow;
2520  endface = 2;
2521  } else {
2522  l_end = r_start - rsurface;
2523  endface = 7;
2524  }
2525  }
2526 
2527  else {
2528  unsafe = true;
2529 
2530  // Calculate correction terms for the position to compensate for
2531  // numerical inaccuracy.
2532  //
2533  Numeric r_corr, lat_corr;
2534  //
2535  cart2pol(r_corr, lat_corr, x, z, lat_start, za_start);
2536  //
2537  r_corr -= r_start;
2538  lat_corr -= lat_start;
2539 
2540  // The end point is found by testing different step lengths until the
2541  // step length has been determined by a precision of *l_acc*.
2542  //
2543  // The first step is to found a length that goes outside the grid cell,
2544  // to find an upper limit. The lower limit is set to 0. The upper
2545  // limit is either set to l_start or it is estimated from the size of
2546  // the grid cell.
2547  // The search algorith is bisection, the next length to test is the
2548  // mean value of the minimum and maximum length limits.
2549  //
2550  if (l_start > 0) {
2551  l_end = l_start;
2552  } else {
2553  l_end = 2 * (rupp - rlow);
2554  }
2555  //
2556  Numeric l_in = 0, l_out = l_end;
2557  bool ready = false, startup = true;
2558 
2559  if (rsurface1 + RTOL >= r1a || rsurface3 + RTOL >= r3a) {
2560  do_surface = true;
2561  }
2562 
2563  while (!ready) {
2564  cart2pol(
2565  r_end, lat_end, x + dx * l_end, z + dz * l_end, lat_start, za_start);
2566  r_end -= r_corr;
2567  lat_end -= lat_corr;
2568 
2569  bool inside = true;
2570 
2571  rlow = rsurf_at_lat(lat1, lat3, r1a, r3a, lat_end);
2572 
2573  if (do_surface) {
2574  const Numeric r_surface =
2575  rsurf_at_lat(lat1, lat3, rsurface1, rsurface3, lat_end);
2576  if (r_surface >= rlow && r_end <= r_surface) {
2577  inside = false;
2578  endface = 7;
2579  }
2580  }
2581 
2582  if (inside) {
2583  if (lat_end < lat1) {
2584  inside = false;
2585  endface = 1;
2586  } else if (lat_end > lat3) {
2587  inside = false;
2588  endface = 3;
2589  } else if (r_end < rlow) {
2590  inside = false;
2591  endface = 2;
2592  } else {
2593  rupp = rsurf_at_lat(lat1, lat3, r1b, r3b, lat_end);
2594  if (r_end > rupp) {
2595  inside = false;
2596  endface = 4;
2597  }
2598  }
2599  }
2600 
2601  if (startup) {
2602  if (inside) {
2603  l_in = l_end;
2604  l_end *= 5;
2605  } else {
2606  l_out = l_end;
2607  l_end = (l_out + l_in) / 2;
2608  startup = false;
2609  }
2610  } else {
2611  if (inside) {
2612  l_in = l_end;
2613  } else {
2614  l_out = l_end;
2615  }
2616 
2617  if ((l_out - l_in) < LACC) {
2618  ready = true;
2619  } else {
2620  l_end = (l_out + l_in) / 2;
2621  }
2622  }
2623  }
2624  }
2625 
2626  //--- Create return vectors
2627  //
2628  Index n = 1;
2629  //
2630  if (lmax > 0) {
2631  n = Index(ceil(abs(l_end / lmax)));
2632  if (n < 1) {
2633  n = 1;
2634  }
2635  }
2636  //
2637  r_v.resize(n + 1);
2638  lat_v.resize(n + 1);
2639  za_v.resize(n + 1);
2640  //
2641  r_v[0] = r_start;
2642  lat_v[0] = lat_start;
2643  za_v[0] = za_start;
2644  //
2645  lstep = l_end / (Numeric)n;
2646  Numeric l;
2647  bool ready = true;
2648  //
2649  for (Index j = 1; j <= n; j++) {
2650  l = lstep * (Numeric)j;
2651  cart2poslos(r_v[j],
2652  lat_v[j],
2653  za_v[j],
2654  x + dx * l,
2655  z + dz * l,
2656  dx,
2657  dz,
2658  ppc,
2659  lat_start,
2660  za_start);
2661 
2662  if (j < n) {
2663  if (unsafe) {
2664  // Check that r_v[j] is above lower pressure level and the
2665  // surface. This can fail around tangent points. For p-levels
2666  // with constant r this is easy to handle analytically, but the
2667  // problem is tricky in the general case with a non-spherical
2668  // geometry, and this crude solution is used instead. Not the
2669  // most elegant solution, but it works! Added later the same
2670  // check for upper level, after getting assert in that direction.
2671  // The z_field was crazy, but still formerly correct.
2672  rlow = rsurf_at_lat(lat1, lat3, r1a, r3a, lat_v[j]);
2673  if (do_surface) {
2674  const Numeric r_surface =
2675  rsurf_at_lat(lat1, lat3, rsurface1, rsurface3, lat_v[j]);
2676  const Numeric r_test = max(r_surface, rlow);
2677  if (r_v[j] < r_test) {
2678  ready = false;
2679  break;
2680  }
2681  } else if (r_v[j] < rlow) {
2682  ready = false;
2683  break;
2684  }
2685 
2686  rupp = rsurf_at_lat(lat1, lat3, r1b, r3b, lat_v[j]);
2687  if (r_v[j] > rupp) {
2688  ready = false;
2689  break;
2690  }
2691  }
2692  } else // j==n
2693  {
2694  if (unsafe) {
2695  // Set end point to be consistent with found endface.
2696  //
2697  if (endface == 1) {
2698  lat_v[n] = lat1;
2699  } else if (endface == 2) {
2700  r_v[n] = rsurf_at_lat(lat1, lat3, r1a, r3a, lat_v[n]);
2701  } else if (endface == 3) {
2702  lat_v[n] = lat3;
2703  } else if (endface == 4) {
2704  r_v[n] = rsurf_at_lat(lat1, lat3, r1b, r3b, lat_v[n]);
2705  } else if (endface == 7) {
2706  r_v[n] = rsurf_at_lat(lat1, lat3, rsurface1, rsurface3, lat_v[n]);
2707  }
2708  }
2709  }
2710  }
2711 
2712  if (!ready) { // If an "outside" point found, restart with l as start search length
2714  lat_v,
2715  za_v,
2716  lstep,
2717  endface,
2718  r_start,
2719  lat_start,
2720  za_start,
2721  l,
2722  icall + 1,
2723  ppc,
2724  lmax,
2725  lat1,
2726  lat3,
2727  r1a,
2728  r3a,
2729  r3b,
2730  r1b,
2731  rsurface1,
2732  rsurface3);
2733  }
2734 }
2735 
2737  ConstVectorView lat_grid,
2738  ConstMatrixView z_field,
2739  ConstVectorView refellipsoid,
2740  ConstVectorView z_surface,
2741  const Numeric& lmax) {
2742  // Radius, zenith angle and latitude of start point.
2743  Numeric r_start, lat_start, za_start;
2744 
2745  // Lower grid index for the grid cell of interest.
2746  Index ip, ilat;
2747 
2748  // Radii and latitudes set by *ppath_start_2d*.
2749  Numeric lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3;
2750 
2751  // Determine the variables defined above and make all possible asserts
2752  ppath_start_2d(r_start,
2753  lat_start,
2754  za_start,
2755  ip,
2756  ilat,
2757  lat1,
2758  lat3,
2759  r1a,
2760  r3a,
2761  r3b,
2762  r1b,
2763  rsurface1,
2764  rsurface3,
2765  ppath,
2766  lat_grid,
2767  z_field,
2768  refellipsoid,
2769  z_surface);
2770 
2771  // If the field "constant" is negative, this is the first call of the
2772  // function and the path constant shall be calculated.
2773  Numeric ppc;
2774  if (ppath.constant < 0) {
2775  ppc = geometrical_ppc(r_start, za_start);
2776  } else {
2777  ppc = ppath.constant;
2778  }
2779 
2780  // Vars to hold found path points, path step length and coding for end face
2781  Vector r_v, lat_v, za_v;
2782  Numeric lstep;
2783  Index endface;
2784 
2786  lat_v,
2787  za_v,
2788  lstep,
2789  endface,
2790  r_start,
2791  lat_start,
2792  za_start,
2793  -1,
2794  0,
2795  ppc,
2796  lmax,
2797  lat1,
2798  lat3,
2799  r1a,
2800  r3a,
2801  r3b,
2802  r1b,
2803  rsurface1,
2804  rsurface3);
2805  // Fill *ppath*
2806  const Index np = r_v.nelem();
2807  ppath_end_2d(ppath,
2808  r_v,
2809  lat_v,
2810  za_v,
2811  Vector(np - 1, lstep),
2812  Vector(np, 1),
2813  Vector(np, 1),
2814  lat_grid,
2815  z_field,
2816  refellipsoid,
2817  ip,
2818  ilat,
2819  endface,
2820  ppc);
2821 }
2822 
2829  Vector& lat_v,
2830  Vector& lon_v,
2831  Vector& za_v,
2832  Vector& aa_v,
2833  Numeric& lstep,
2834  Index& endface,
2835  const Numeric& r_start0,
2836  const Numeric& lat_start0,
2837  const Numeric& lon_start0,
2838  const Numeric& za_start,
2839  const Numeric& aa_start,
2840  const Numeric& l_start,
2841  const Index& icall,
2842  const Numeric& ppc,
2843  const Numeric& lmax,
2844  const Numeric& lat1,
2845  const Numeric& lat3,
2846  const Numeric& lon5,
2847  const Numeric& lon6,
2848  const Numeric& r15a,
2849  const Numeric& r35a,
2850  const Numeric& r36a,
2851  const Numeric& r16a,
2852  const Numeric& r15b,
2853  const Numeric& r35b,
2854  const Numeric& r36b,
2855  const Numeric& r16b,
2856  const Numeric& rsurface15,
2857  const Numeric& rsurface35,
2858  const Numeric& rsurface36,
2859  const Numeric& rsurface16) {
2860  Numeric r_start = r_start0;
2861  Numeric lat_start = lat_start0;
2862  Numeric lon_start = lon_start0;
2863 
2864  assert(icall < 10);
2865 
2866  // Assert latitude and longitude
2867  assert(lat_start >= lat1 - LATLONTOL);
2868  assert(lat_start <= lat3 + LATLONTOL);
2869  assert(!(abs(lat_start) < POLELAT && lon_start < lon5 - LATLONTOL));
2870  assert(!(abs(lat_start) < POLELAT && lon_start > lon6 + LATLONTOL));
2871 
2872  // Assertthat ppc and path data are consustent
2873  assert(abs(ppc - r_start0 * sin(DEG2RAD * za_start)) < 0.1);
2874 
2875  // Shift latitude and longitude if outside
2876  if (lat_start < lat1) {
2877  lat_start = lat1;
2878  } else if (lat_start > lat3) {
2879  lat_start = lat3;
2880  }
2881  if (lon_start < lon5) {
2882  lon_start = lon5;
2883  } else if (lon_start > lon6) {
2884  lon_start = lon6;
2885  }
2886 
2887  // Radius of lower and upper pressure level at the start position
2888  Numeric rlow = rsurf_at_latlon(
2889  lat1, lat3, lon5, lon6, r15a, r35a, r36a, r16a, lat_start, lon_start);
2890  Numeric rupp = rsurf_at_latlon(
2891  lat1, lat3, lon5, lon6, r15b, r35b, r36b, r16b, lat_start, lon_start);
2892 
2893  // Assert radius (some extra tolerance is needed for radius)
2894  assert(r_start >= rlow - RTOL);
2895  assert(r_start <= rupp + RTOL);
2896 
2897  // Shift radius if outside
2898  if (r_start < rlow) {
2899  r_start = rlow;
2900  } else if (r_start > rupp) {
2901  r_start = rupp;
2902  }
2903 
2904  // Position and LOS in cartesian coordinates
2905  Numeric x, y, z, dx, dy, dz;
2906  poslos2cart(
2907  x, y, z, dx, dy, dz, r_start, lat_start, lon_start, za_start, aa_start);
2908 
2909  // Some booleans to check for recursive call
2910  bool unsafe = false;
2911  bool do_surface = false;
2912 
2913  // Determine the position of the end point
2914  //
2915  endface = 0;
2916  //
2917  Numeric r_end, lat_end, lon_end, l_end;
2918 
2919  // Zenith and nadir looking are handled as special cases
2920 
2921  // Zenith looking
2922  if (za_start < ANGTOL) {
2923  l_end = rupp - r_start;
2924  endface = 4;
2925  }
2926 
2927  // Nadir looking
2928  else if (za_start > 180 - ANGTOL) {
2929  const Numeric rsurface = rsurf_at_latlon(lat1,
2930  lat3,
2931  lon5,
2932  lon6,
2933  rsurface15,
2934  rsurface35,
2935  rsurface36,
2936  rsurface16,
2937  lat_start,
2938  lon_start);
2939 
2940  if (rlow > rsurface) {
2941  l_end = r_start - rlow;
2942  endface = 2;
2943  } else {
2944  l_end = r_start - rsurface;
2945  endface = 7;
2946  }
2947  }
2948 
2949  else {
2950  unsafe = true;
2951 
2952  // Calculate correction terms for the position to compensate for
2953  // numerical inaccuracy.
2954  //
2955  Numeric r_corr, lat_corr, lon_corr;
2956  //
2957  cart2sph(r_corr,
2958  lat_corr,
2959  lon_corr,
2960  x,
2961  y,
2962  z,
2963  lat_start,
2964  lon_start,
2965  za_start,
2966  aa_start);
2967  //
2968  r_corr -= r_start;
2969  lat_corr -= lat_start;
2970  lon_corr -= lon_start;
2971 
2972  // The end point is found by testing different step lengths until the
2973  // step length has been determined by a precision of *l_acc*.
2974  //
2975  // The first step is to found a length that goes outside the grid cell,
2976  // to find an upper limit. The lower limit is set to 0. The upper
2977  // limit is either set to l_start or it is estimated from the size of
2978  // the grid cell.
2979  // The search algorith is bisection, the next length to test is the
2980  // mean value of the minimum and maximum length limits.
2981  //
2982  if (l_start > 0) {
2983  l_end = l_start;
2984  } else {
2985  l_end = 2 * (rupp - rlow);
2986  }
2987  //
2988  Numeric l_in = 0, l_out = l_end;
2989  bool ready = false, startup = true;
2990 
2991  if (rsurface15 + RTOL >= r15a || rsurface35 + RTOL >= r35a ||
2992  rsurface36 + RTOL >= r36a || rsurface16 + RTOL >= r16a) {
2993  do_surface = true;
2994  }
2995 
2996  while (!ready) {
2997  cart2sph(r_end,
2998  lat_end,
2999  lon_end,
3000  x + dx * l_end,
3001  y + dy * l_end,
3002  z + dz * l_end,
3003  lat_start,
3004  lon_start,
3005  za_start,
3006  aa_start);
3007  r_end -= r_corr;
3008  lat_end -= lat_corr;
3009  lon_end -= lon_corr;
3010  resolve_lon(lon_end, lon5, lon6);
3011 
3012  // Special fix for north-south observations
3013  if (abs(lat_start) < POLELAT && abs(lat_end) < POLELAT &&
3014  (abs(aa_start) < ANGTOL || abs(aa_start) > 180 - ANGTOL)) {
3015  lon_end = lon_start;
3016  }
3017 
3018  bool inside = true;
3019 
3020  rlow = rsurf_at_latlon(
3021  lat1, lat3, lon5, lon6, r15a, r35a, r36a, r16a, lat_end, lon_end);
3022 
3023  if (do_surface) {
3024  const Numeric r_surface = rsurf_at_latlon(lat1,
3025  lat3,
3026  lon5,
3027  lon6,
3028  rsurface15,
3029  rsurface35,
3030  rsurface36,
3031  rsurface16,
3032  lat_end,
3033  lon_end);
3034  if (r_surface >= rlow && r_end <= r_surface) {
3035  inside = false;
3036  endface = 7;
3037  }
3038  }
3039 
3040  if (inside) {
3041  if (lat_end < lat1) {
3042  inside = false;
3043  endface = 1;
3044  } else if (lat_end > lat3) {
3045  inside = false;
3046  endface = 3;
3047  } else if (lon_end < lon5) {
3048  inside = false;
3049  endface = 5;
3050  } else if (lon_end > lon6) {
3051  inside = false;
3052  endface = 6;
3053  } else if (r_end < rlow) {
3054  inside = false;
3055  endface = 2;
3056  } else {
3057  rupp = rsurf_at_latlon(
3058  lat1, lat3, lon5, lon6, r15b, r35b, r36b, r16b, lat_end, lon_end);
3059  if (r_end > rupp) {
3060  inside = false;
3061  endface = 4;
3062  }
3063  }
3064  }
3065 
3066  if (startup) {
3067  if (inside) {
3068  l_in = l_end;
3069  l_end *= 5;
3070  } else {
3071  l_out = l_end;
3072  l_end = (l_out + l_in) / 2;
3073  startup = false;
3074  }
3075  } else {
3076  if (inside) {
3077  l_in = l_end;
3078  } else {
3079  l_out = l_end;
3080  }
3081 
3082  if ((l_out - l_in) < LACC) {
3083  ready = true;
3084  } else {
3085  l_end = (l_out + l_in) / 2;
3086  }
3087  }
3088  }
3089  }
3090 
3091  //--- Create return vectors
3092  //
3093  Index n = 1;
3094  //
3095  if (lmax > 0) {
3096  n = Index(ceil(abs(l_end / lmax)));
3097  if (n < 1) {
3098  n = 1;
3099  }
3100  }
3101  //
3102  r_v.resize(n + 1);
3103  lat_v.resize(n + 1);
3104  lon_v.resize(n + 1);
3105  za_v.resize(n + 1);
3106  aa_v.resize(n + 1);
3107  //
3108  r_v[0] = r_start;
3109  lat_v[0] = lat_start;
3110  lon_v[0] = lon_start;
3111  za_v[0] = za_start;
3112  aa_v[0] = aa_start;
3113  //
3114  lstep = l_end / (Numeric)n;
3115  Numeric l;
3116  bool ready = true;
3117  //
3118  for (Index j = 1; j <= n; j++) {
3119  l = lstep * (Numeric)j;
3120  cart2poslos(r_v[j],
3121  lat_v[j],
3122  lon_v[j],
3123  za_v[j],
3124  aa_v[j],
3125  x + dx * l,
3126  y + dy * l,
3127  z + dz * l,
3128  dx,
3129  dy,
3130  dz,
3131  ppc,
3132  x,
3133  y,
3134  z,
3135  lat_start,
3136  lon_start,
3137  za_start,
3138  aa_start);
3139 
3140  // Shall lon values be shifted (value 0 is already OK)?
3141  resolve_lon(lon_v[j], lon5, lon6);
3142 
3143  if (j < n) {
3144  if (unsafe) {
3145  // Check that r_v[j] is above lower pressure level and the
3146  // surface. This can fail around tangent points. For p-levels
3147  // with constant r this is easy to handle analytically, but the
3148  // problem is tricky in the general case with a non-spherical
3149  // geometry, and this crude solution is used instead. Not the
3150  // most elegant solution, but it works! Added later the same
3151  // check for upper level, after getting assert in that direction.
3152  // The z_field was crazy, but still formerly correct.
3153  rlow = rsurf_at_latlon(
3154  lat1, lat3, lon5, lon6, r15a, r35a, r36a, r16a, lat_v[j], lon_v[j]);
3155  if (do_surface) {
3156  const Numeric r_surface = rsurf_at_latlon(lat1,
3157  lat3,
3158  lon5,
3159  lon6,
3160  rsurface15,
3161  rsurface35,
3162  rsurface36,
3163  rsurface16,
3164  lat_v[j],
3165  lon_v[j]);
3166  const Numeric r_test = max(r_surface, rlow);
3167  if (r_v[j] < r_test) {
3168  ready = false;
3169  break;
3170  }
3171  } else if (r_v[j] < rlow) {
3172  ready = false;
3173  break;
3174  }
3175 
3176  rupp = rsurf_at_latlon(
3177  lat1, lat3, lon5, lon6, r15b, r35b, r36b, r16b, lat_v[j], lon_v[j]);
3178  if (r_v[j] > rupp) {
3179  ready = false;
3180  break;
3181  }
3182  }
3183  } else // j==n
3184  {
3185  if (unsafe) {
3186  // Set end point to be consistent with found endface.
3187  //
3188  if (endface == 1) {
3189  lat_v[n] = lat1;
3190  } else if (endface == 2) {
3191  r_v[n] = rsurf_at_latlon(lat1,
3192  lat3,
3193  lon5,
3194  lon6,
3195  r15a,
3196  r35a,
3197  r36a,
3198  r16a,
3199  lat_v[n],
3200  lon_v[n]);
3201  } else if (endface == 3) {
3202  lat_v[n] = lat3;
3203  } else if (endface == 4) {
3204  r_v[n] = rsurf_at_latlon(lat1,
3205  lat3,
3206  lon5,
3207  lon6,
3208  r15b,
3209  r35b,
3210  r36b,
3211  r16b,
3212  lat_v[n],
3213  lon_v[n]);
3214  } else if (endface == 5) {
3215  lon_v[n] = lon5;
3216  } else if (endface == 6) {
3217  lon_v[n] = lon6;
3218  } else if (endface == 7) {
3219  r_v[n] = rsurf_at_latlon(lat1,
3220  lat3,
3221  lon5,
3222  lon6,
3223  rsurface15,
3224  rsurface35,
3225  rsurface36,
3226  rsurface16,
3227  lat_v[n],
3228  lon_v[n]);
3229  }
3230  }
3231  }
3232  }
3233 
3234  if (!ready) { // If an "outside" point found, restart with l as start search length
3236  lat_v,
3237  lon_v,
3238  za_v,
3239  aa_v,
3240  lstep,
3241  endface,
3242  r_start,
3243  lat_start,
3244  lon_start,
3245  za_start,
3246  aa_start,
3247  l,
3248  icall + 1,
3249  ppc,
3250  lmax,
3251  lat1,
3252  lat3,
3253  lon5,
3254  lon6,
3255  r15a,
3256  r35a,
3257  r36a,
3258  r16a,
3259  r15b,
3260  r35b,
3261  r36b,
3262  r16b,
3263  rsurface15,
3264  rsurface35,
3265  rsurface36,
3266  rsurface16);
3267  }
3268 }
3269 
3271  ConstVectorView lat_grid,
3272  ConstVectorView lon_grid,
3273  ConstTensor3View z_field,
3274  ConstVectorView refellipsoid,
3275  ConstMatrixView z_surface,
3276  const Numeric& lmax) {
3277  // Radius, zenith angle and latitude of start point.
3278  Numeric r_start, lat_start, lon_start, za_start, aa_start;
3279 
3280  // Lower grid index for the grid cell of interest.
3281  Index ip, ilat, ilon;
3282 
3283  // Radius for corner points, latitude and longitude of the grid cell
3284  //
3285  Numeric lat1, lat3, lon5, lon6;
3286  Numeric r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b;
3287  Numeric rsurface15, rsurface35, rsurface36, rsurface16;
3288 
3289  // Determine the variables defined above and make all possible asserts
3290  ppath_start_3d(r_start,
3291  lat_start,
3292  lon_start,
3293  za_start,
3294  aa_start,
3295  ip,
3296  ilat,
3297  ilon,
3298  lat1,
3299  lat3,
3300  lon5,
3301  lon6,
3302  r15a,
3303  r35a,
3304  r36a,
3305  r16a,
3306  r15b,
3307  r35b,
3308  r36b,
3309  r16b,
3310  rsurface15,
3311  rsurface35,
3312  rsurface36,
3313  rsurface16,
3314  ppath,
3315  lat_grid,
3316  lon_grid,
3317  z_field,
3318  refellipsoid,
3319  z_surface);
3320 
3321  // If the field "constant" is negative, this is the first call of the
3322  // function and the path constant shall be calculated.
3323  Numeric ppc;
3324  if (ppath.constant < 0) {
3325  ppc = geometrical_ppc(r_start, za_start);
3326  } else {
3327  ppc = ppath.constant;
3328  }
3329 
3330  // Vars to hold found path points, path step length and coding for end face
3331  Vector r_v, lat_v, lon_v, za_v, aa_v;
3332  Numeric lstep;
3333  Index endface;
3334 
3336  lat_v,
3337  lon_v,
3338  za_v,
3339  aa_v,
3340  lstep,
3341  endface,
3342  r_start,
3343  lat_start,
3344  lon_start,
3345  za_start,
3346  aa_start,
3347  -1,
3348  0,
3349  ppc,
3350  lmax,
3351  lat1,
3352  lat3,
3353  lon5,
3354  lon6,
3355  r15a,
3356  r35a,
3357  r36a,
3358  r16a,
3359  r15b,
3360  r35b,
3361  r36b,
3362  r16b,
3363  rsurface15,
3364  rsurface35,
3365  rsurface36,
3366  rsurface16);
3367 
3368  // Fill *ppath*
3369  const Index np = r_v.nelem();
3370  ppath_end_3d(ppath,
3371  r_v,
3372  lat_v,
3373  lon_v,
3374  za_v,
3375  aa_v,
3376  Vector(np - 1, lstep),
3377  Vector(np, 1),
3378  Vector(np, 1),
3379  lat_grid,
3380  lon_grid,
3381  z_field,
3382  refellipsoid,
3383  ip,
3384  ilat,
3385  ilon,
3386  endface,
3387  ppc);
3388 }
3389 
3390 /*===========================================================================
3391  === Core functions for refraction *ppath_step* functions
3392  ===========================================================================*/
3393 
3433  Array<Numeric>& r_array,
3434  Array<Numeric>& lat_array,
3435  Array<Numeric>& za_array,
3436  Array<Numeric>& l_array,
3437  Array<Numeric>& n_array,
3438  Array<Numeric>& ng_array,
3439  Index& endface,
3440  ConstVectorView p_grid,
3441  ConstVectorView refellipsoid,
3442  ConstTensor3View z_field,
3443  ConstTensor3View t_field,
3444  ConstTensor4View vmr_field,
3445  ConstVectorView f_grid,
3446  const Numeric& lmax,
3447  const Agenda& refr_index_air_agenda,
3448  const Numeric& lraytrace,
3449  const Numeric& rsurface,
3450  const Numeric& r1,
3451  const Numeric& r3,
3452  Numeric r,
3453  Numeric lat,
3454  Numeric za) {
3455  // Loop boolean
3456  bool ready = false;
3457 
3458  // Store first point
3459  Numeric refr_index_air, refr_index_air_group;
3460  get_refr_index_1d(ws,
3461  refr_index_air,
3462  refr_index_air_group,
3463  refr_index_air_agenda,
3464  p_grid,
3465  refellipsoid,
3466  z_field,
3467  t_field,
3468  vmr_field,
3469  f_grid,
3470  r);
3471  r_array.push_back(r);
3472  lat_array.push_back(lat);
3473  za_array.push_back(za);
3474  n_array.push_back(refr_index_air);
3475  ng_array.push_back(refr_index_air_group);
3476 
3477  // Variables for output from do_gridcell_2d
3478  Vector r_v, lat_v, za_v;
3479  Numeric lstep, lcum = 0, dlat;
3480 
3481  while (!ready) {
3482  // Constant for the geometrical step to make
3483  const Numeric ppc_step = geometrical_ppc(r, za);
3484 
3485  // Where will a geometric path exit the grid cell?
3486  do_gridrange_1d(r_v,
3487  lat_v,
3488  za_v,
3489  lstep,
3490  endface,
3491  r,
3492  lat,
3493  za,
3494  ppc_step,
3495  -1,
3496  r1,
3497  r3,
3498  rsurface);
3499  assert(r_v.nelem() == 2);
3500 
3501  // If *lstep* is <= *lraytrace*, extract the found end point (if not
3502  // a tangent point, we are ready).
3503  // Otherwise, we make a geometrical step with length *lraytrace*.
3504 
3505  Numeric za_flagside = za;
3506 
3507  if (lstep <= lraytrace) {
3508  r = r_v[1];
3509  dlat = lat_v[1] - lat;
3510  lat = lat_v[1];
3511  lcum += lstep;
3512  ready = true;
3513  } else {
3514  Numeric l;
3515  if (za <= 90) {
3516  l = geompath_l_at_r(ppc_step, r) + lraytrace;
3517  } else {
3518  l = geompath_l_at_r(ppc_step, r) - lraytrace;
3519  if (l < 0) {
3520  za_flagside = 180 - za_flagside;
3521  } // Tangent point passed!
3522  }
3523 
3524  r = geompath_r_at_l(ppc_step, l);
3525 
3526  const Numeric lat_new = geompath_lat_at_za(
3527  za, lat, geompath_za_at_r(ppc_step, za_flagside, r));
3528  dlat = lat_new - lat;
3529  lat = lat_new;
3530  lstep = lraytrace;
3531  lcum += lraytrace;
3532  }
3533 
3534  // Refractive index at new point
3535  Numeric dndr;
3536  refr_gradients_1d(ws,
3537  refr_index_air,
3538  refr_index_air_group,
3539  dndr,
3540  refr_index_air_agenda,
3541  p_grid,
3542  refellipsoid,
3543  z_field,
3544  t_field,
3545  vmr_field,
3546  f_grid,
3547  r);
3548 
3549  // Calculate LOS zenith angle at found point.
3550  const Numeric za_rad = DEG2RAD * za;
3551  //
3552  za += -dlat; // Pure geometrical effect
3553  //
3554  za += (RAD2DEG * lstep / refr_index_air) * (-sin(za_rad) * dndr);
3555 
3556  // Make sure that obtained *za* is inside valid range
3557  if (za < 0) {
3558  za = -za;
3559  } else if (za > 180) {
3560  za -= 360;
3561  }
3562 
3563  // Store found point?
3564  if (ready || (lmax > 0 && lcum + lraytrace > lmax)) {
3565  r_array.push_back(r);
3566  lat_array.push_back(lat);
3567  za_array.push_back(za);
3568  n_array.push_back(refr_index_air);
3569  ng_array.push_back(refr_index_air_group);
3570  l_array.push_back(lcum);
3571  lcum = 0;
3572  }
3573  }
3574 }
3575 
3577  Ppath& ppath,
3578  ConstVectorView p_grid,
3579  ConstTensor3View z_field,
3580  ConstTensor3View t_field,
3581  ConstTensor4View vmr_field,
3582  ConstVectorView f_grid,
3583  ConstVectorView refellipsoid,
3584  const Numeric& z_surface,
3585  const Numeric& lmax,
3586  const Agenda& refr_index_air_agenda,
3587  const String& rtrace_method,
3588  const Numeric& lraytrace) {
3589  // Starting radius, zenith angle and latitude
3590  Numeric r_start, lat_start, za_start;
3591 
3592  // Index of the pressure level being the lower limit for the
3593  // grid range of interest.
3594  Index ip;
3595 
3596  // Determine the variables defined above, and make asserts of input
3597  ppath_start_1d(r_start, lat_start, za_start, ip, ppath);
3598 
3599  // If the field "constant" is negative, this is the first call of the
3600  // function and the path constant shall be calculated.
3601  // If the sensor is placed outside the atmosphere, the constant is
3602  // already set.
3603  Numeric ppc;
3604  if (ppath.constant < 0) {
3605  Numeric refr_index_air, refr_index_air_group;
3606  get_refr_index_1d(ws,
3607  refr_index_air,
3608  refr_index_air_group,
3609  refr_index_air_agenda,
3610  p_grid,
3611  refellipsoid,
3612  z_field,
3613  t_field,
3614  vmr_field,
3615  f_grid,
3616  r_start);
3617  ppc = refraction_ppc(r_start, za_start, refr_index_air);
3618  } else {
3619  ppc = ppath.constant;
3620  }
3621 
3622  // Perform the ray tracing
3623  //
3624  // Arrays to store found ray tracing points
3625  // (Vectors don't work here as we don't know how many points there will be)
3626  Array<Numeric> r_array, lat_array, za_array, l_array, n_array, ng_array;
3627  Index endface;
3628  //
3629  if (rtrace_method == "linear_basic") {
3630  /*
3631  raytrace_1d_linear_basic( ws, r_array, lat_array, za_array, l_array,
3632  n_array, ng_array, endface, refellipsoid, p_grid, z_field, t_field,
3633  vmr_field, f_grid, lmax, refr_index_air_agenda,
3634  lraytrace, ppc, refellipsoid[0] + z_surface,
3635  refellipsoid[0]+z_field[ip], refellipsoid[0]+z_field[ip+1],
3636  r_start, lat_start, za_start );
3637  */
3639  r_array,
3640  lat_array,
3641  za_array,
3642  l_array,
3643  n_array,
3644  ng_array,
3645  endface,
3646  p_grid,
3647  refellipsoid,
3648  z_field,
3649  t_field,
3650  vmr_field,
3651  f_grid,
3652  lmax,
3653  refr_index_air_agenda,
3654  lraytrace,
3655  refellipsoid[0] + z_surface,
3656  refellipsoid[0] + z_field(ip, 0, 0),
3657  refellipsoid[0] + z_field(ip + 1, 0, 0),
3658  r_start,
3659  lat_start,
3660  za_start);
3661  } else {
3662  // Make sure we fail if called with an invalid rtrace_method.
3663  assert(false);
3664  }
3665 
3666  // Fill *ppath*
3667  //
3668  const Index np = r_array.nelem();
3669  Vector r_v(np), lat_v(np), za_v(np), l_v(np - 1), n_v(np), ng_v(np);
3670  for (Index i = 0; i < np; i++) {
3671  r_v[i] = r_array[i];
3672  lat_v[i] = lat_array[i];
3673  za_v[i] = za_array[i];
3674  n_v[i] = n_array[i];
3675  ng_v[i] = ng_array[i];
3676  if (i < np - 1) {
3677  l_v[i] = l_array[i];
3678  }
3679  }
3680  //
3681  ppath_end_1d(ppath,
3682  r_v,
3683  lat_v,
3684  za_v,
3685  l_v,
3686  n_v,
3687  ng_v,
3688  z_field(joker, 0, 0),
3689  refellipsoid,
3690  ip,
3691  endface,
3692  ppc);
3693 }
3694 
3739  Array<Numeric>& r_array,
3740  Array<Numeric>& lat_array,
3741  Array<Numeric>& za_array,
3742  Array<Numeric>& l_array,
3743  Array<Numeric>& n_array,
3744  Array<Numeric>& ng_array,
3745  Index& endface,
3746  ConstVectorView p_grid,
3747  ConstVectorView lat_grid,
3748  ConstVectorView refellipsoid,
3749  ConstTensor3View z_field,
3750  ConstTensor3View t_field,
3751  ConstTensor4View vmr_field,
3752  ConstVectorView f_grid,
3753  const Numeric& lmax,
3754  const Agenda& refr_index_air_agenda,
3755  const Numeric& lraytrace,
3756  const Numeric& lat1,
3757  const Numeric& lat3,
3758  const Numeric& rsurface1,
3759  const Numeric& rsurface3,
3760  const Numeric& r1a,
3761  const Numeric& r3a,
3762  const Numeric& r3b,
3763  const Numeric& r1b,
3764  Numeric r,
3765  Numeric lat,
3766  Numeric za) {
3767  // Loop boolean
3768  bool ready = false;
3769 
3770  // Store first point
3771  Numeric refr_index_air, refr_index_air_group;
3772  get_refr_index_2d(ws,
3773  refr_index_air,
3774  refr_index_air_group,
3775  refr_index_air_agenda,
3776  p_grid,
3777  lat_grid,
3778  refellipsoid,
3779  z_field,
3780  t_field,
3781  vmr_field,
3782  f_grid,
3783  r,
3784  lat);
3785  r_array.push_back(r);
3786  lat_array.push_back(lat);
3787  za_array.push_back(za);
3788  n_array.push_back(refr_index_air);
3789  ng_array.push_back(refr_index_air_group);
3790 
3791  // Variables for output from do_gridcell_2d
3792  Vector r_v, lat_v, za_v;
3793  Numeric lstep, lcum = 0, dlat;
3794 
3795  while (!ready) {
3796  // Constant for the geometrical step to make
3797  const Numeric ppc_step = geometrical_ppc(r, za);
3798 
3799  // Where will a geometric path exit the grid cell?
3801  lat_v,
3802  za_v,
3803  lstep,
3804  endface,
3805  r,
3806  lat,
3807  za,
3808  lraytrace,
3809  0,
3810  ppc_step,
3811  -1,
3812  lat1,
3813  lat3,
3814  r1a,
3815  r3a,
3816  r3b,
3817  r1b,
3818  rsurface1,
3819  rsurface3);
3820  assert(r_v.nelem() == 2);
3821 
3822  // If *lstep* is <= *lraytrace*, extract the found end point (if not
3823  // a tangent point, we are ready).
3824  // Otherwise, we make a geometrical step with length *lraytrace*.
3825 
3826  Numeric za_flagside = za;
3827 
3828  if (lstep <= lraytrace) {
3829  r = r_v[1];
3830  dlat = lat_v[1] - lat;
3831  lat = lat_v[1];
3832  lcum += lstep;
3833  ready = true;
3834  } else {
3835  Numeric l;
3836  if (abs(za) <= 90) {
3837  l = geompath_l_at_r(ppc_step, r) + lraytrace;
3838  } else {
3839  l = geompath_l_at_r(ppc_step, r) - lraytrace;
3840  if (l < 0) // Tangent point passed!
3841  {
3842  za_flagside = sign(za) * 180 - za_flagside;
3843  }
3844  }
3845 
3846  r = geompath_r_at_l(ppc_step, l);
3847 
3848  const Numeric lat_new = geompath_lat_at_za(
3849  za, lat, geompath_za_at_r(ppc_step, za_flagside, r));
3850  dlat = lat_new - lat;
3851  lat = lat_new;
3852  lstep = lraytrace;
3853  lcum += lraytrace;
3854 
3855  // For paths along the latitude end faces we can end up outside the
3856  // grid cell. We simply look for points outisde the grid cell.
3857  if (lat < lat1) {
3858  lat = lat1;
3859  } else if (lat > lat3) {
3860  lat = lat3;
3861  }
3862  }
3863 
3864  // Refractive index at new point
3865  Numeric dndr, dndlat;
3866  refr_gradients_2d(ws,
3867  refr_index_air,
3868  refr_index_air_group,
3869  dndr,
3870  dndlat,
3871  refr_index_air_agenda,
3872  p_grid,
3873  lat_grid,
3874  refellipsoid,
3875  z_field,
3876  t_field,
3877  vmr_field,
3878  f_grid,
3879  r,
3880  lat);
3881 
3882  // Calculate LOS zenith angle at found point.
3883  const Numeric za_rad = DEG2RAD * za;
3884  //
3885  za += -dlat; // Pure geometrical effect
3886  //
3887  za += (RAD2DEG * lstep / refr_index_air) *
3888  (-sin(za_rad) * dndr + cos(za_rad) * dndlat);
3889 
3890  // Make sure that obtained *za* is inside valid range
3891  if (za < -180) {
3892  za += 360;
3893  } else if (za > 180) {
3894  za -= 360;
3895  }
3896 
3897  // If the path is zenith/nadir along a latitude end face, we must check
3898  // that the path does not exit with new *za*.
3899  if (lat == lat1 && za < 0) {
3900  endface = 1;
3901  ready = 1;
3902  } else if (lat == lat3 && za > 0) {
3903  endface = 3;
3904  ready = 1;
3905  }
3906 
3907  // Store found point?
3908  if (ready || (lmax > 0 && lcum + lraytrace > lmax)) {
3909  r_array.push_back(r);
3910  lat_array.push_back(lat);
3911  za_array.push_back(za);
3912  n_array.push_back(refr_index_air);
3913  ng_array.push_back(refr_index_air_group);
3914  l_array.push_back(lcum);
3915  lcum = 0;
3916  }
3917  }
3918 }
3919 
3921  Ppath& ppath,
3922  ConstVectorView p_grid,
3923  ConstVectorView lat_grid,
3924  ConstTensor3View z_field,
3925  ConstTensor3View t_field,
3926  ConstTensor4View vmr_field,
3927  ConstVectorView f_grid,
3928  ConstVectorView refellipsoid,
3929  ConstVectorView z_surface,
3930  const Numeric& lmax,
3931  const Agenda& refr_index_air_agenda,
3932  const String& rtrace_method,
3933  const Numeric& lraytrace) {
3934  // Radius, zenith angle and latitude of start point.
3935  Numeric r_start, lat_start, za_start;
3936 
3937  // Lower grid index for the grid cell of interest.
3938  Index ip, ilat;
3939 
3940  // Radii and latitudes set by *ppath_start_2d*.
3941  Numeric lat1, lat3, r1a, r3a, r3b, r1b, rsurface1, rsurface3;
3942 
3943  // Determine the variables defined above and make all possible asserts
3944  ppath_start_2d(r_start,
3945  lat_start,
3946  za_start,
3947  ip,
3948  ilat,
3949  lat1,
3950  lat3,
3951  r1a,
3952  r3a,
3953  r3b,
3954  r1b,
3955  rsurface1,
3956  rsurface3,
3957  ppath,
3958  lat_grid,
3959  z_field(joker, joker, 0),
3960  refellipsoid,
3961  z_surface);
3962 
3963  // Perform the ray tracing
3964  //
3965  // No constant for the path is valid here.
3966  //
3967  // Arrays to store found ray tracing points
3968  // (Vectors don't work here as we don't know how many points there will be)
3969  Array<Numeric> r_array, lat_array, za_array, l_array, n_array, ng_array;
3970  Index endface;
3971  //
3972  if (rtrace_method == "linear_basic") {
3974  r_array,
3975  lat_array,
3976  za_array,
3977  l_array,
3978  n_array,
3979  ng_array,
3980  endface,
3981  p_grid,
3982  lat_grid,
3983  refellipsoid,
3984  z_field,
3985  t_field,
3986  vmr_field,
3987  f_grid,
3988  lmax,
3989  refr_index_air_agenda,
3990  lraytrace,
3991  lat1,
3992  lat3,
3993  rsurface1,
3994  rsurface3,
3995  r1a,
3996  r3a,
3997  r3b,
3998  r1b,
3999  r_start,
4000  lat_start,
4001  za_start);
4002  } else {
4003  // Make sure we fail if called with an invalid rtrace_method.
4004  assert(false);
4005  }
4006 
4007  // Fill *ppath*
4008  //
4009  const Index np = r_array.nelem();
4010  Vector r_v(np), lat_v(np), za_v(np), l_v(np - 1), n_v(np), ng_v(np);
4011  for (Index i = 0; i < np; i++) {
4012  r_v[i] = r_array[i];
4013  lat_v[i] = lat_array[i];
4014  za_v[i] = za_array[i];
4015  n_v[i] = n_array[i];
4016  ng_v[i] = ng_array[i];
4017  if (i < np - 1) {
4018  l_v[i] = l_array[i];
4019  }
4020  }
4021  //
4022  ppath_end_2d(ppath,
4023  r_v,
4024  lat_v,
4025  za_v,
4026  l_v,
4027  n_v,
4028  ng_v,
4029  lat_grid,
4030  z_field(joker, joker, 0),
4031  refellipsoid,
4032  ip,
4033  ilat,
4034  endface,
4035  -1);
4036 }
4037 
4096  Array<Numeric>& r_array,
4097  Array<Numeric>& lat_array,
4098  Array<Numeric>& lon_array,
4099  Array<Numeric>& za_array,
4100  Array<Numeric>& aa_array,
4101  Array<Numeric>& l_array,
4102  Array<Numeric>& n_array,
4103  Array<Numeric>& ng_array,
4104  Index& endface,
4105  ConstVectorView refellipsoid,
4106  ConstVectorView p_grid,
4107  ConstVectorView lat_grid,
4108  ConstVectorView lon_grid,
4109  ConstTensor3View z_field,
4110  ConstTensor3View t_field,
4111  ConstTensor4View vmr_field,
4112  ConstVectorView f_grid,
4113  const Numeric& lmax,
4114  const Agenda& refr_index_air_agenda,
4115  const Numeric& lraytrace,
4116  const Numeric& lat1,
4117  const Numeric& lat3,
4118  const Numeric& lon5,
4119  const Numeric& lon6,
4120  const Numeric& rsurface15,
4121  const Numeric& rsurface35,
4122  const Numeric& rsurface36,
4123  const Numeric& rsurface16,
4124  const Numeric& r15a,
4125  const Numeric& r35a,
4126  const Numeric& r36a,
4127  const Numeric& r16a,
4128  const Numeric& r15b,
4129  const Numeric& r35b,
4130  const Numeric& r36b,
4131  const Numeric& r16b,
4132  Numeric r,
4133  Numeric lat,
4134  Numeric lon,
4135  Numeric za,
4136  Numeric aa) {
4137  // Loop boolean
4138  bool ready = false;
4139 
4140  // Store first point
4141  Numeric refr_index_air, refr_index_air_group;
4142  get_refr_index_3d(ws,
4143  refr_index_air,
4144  refr_index_air_group,
4145  refr_index_air_agenda,
4146  p_grid,
4147  lat_grid,
4148  lon_grid,
4149  refellipsoid,
4150  z_field,
4151  t_field,
4152  vmr_field,
4153  f_grid,
4154  r,
4155  lat,
4156  lon);
4157  r_array.push_back(r);
4158  lat_array.push_back(lat);
4159  lon_array.push_back(lon);
4160  za_array.push_back(za);
4161  aa_array.push_back(aa);
4162  n_array.push_back(refr_index_air);
4163  ng_array.push_back(refr_index_air_group);
4164 
4165  // Variables for output from do_gridcell_3d
4166  Vector r_v, lat_v, lon_v, za_v, aa_v;
4167  Numeric lstep, lcum = 0;
4168  Numeric za_new, aa_new;
4169 
4170  while (!ready) {
4171  // Constant for the geometrical step to make
4172  const Numeric ppc_step = geometrical_ppc(r, za);
4173 
4174  // Where will a geometric path exit the grid cell?
4176  lat_v,
4177  lon_v,
4178  za_v,
4179  aa_v,
4180  lstep,
4181  endface,
4182  r,
4183  lat,
4184  lon,
4185  za,
4186  aa,
4187  lraytrace,
4188  0,
4189  ppc_step,
4190  -1,
4191  lat1,
4192  lat3,
4193  lon5,
4194  lon6,
4195  r15a,
4196  r35a,
4197  r36a,
4198  r16a,
4199  r15b,
4200  r35b,
4201  r36b,
4202  r16b,
4203  rsurface15,
4204  rsurface35,
4205  rsurface36,
4206  rsurface16);
4207  assert(r_v.nelem() == 2);
4208 
4209  // If *lstep* is <= *lraytrace*, extract the found end point.
4210  // Otherwise, we make a geometrical step with length *lraytrace*.
4211 
4212  if (lstep <= lraytrace) {
4213  r = r_v[1];
4214  lat = lat_v[1];
4215  lon = lon_v[1];
4216  za_new = za_v[1];
4217  aa_new = aa_v[1];
4218  lcum += lstep;
4219  ready = true;
4220  } else {
4221  // Sensor pos and LOS in cartesian coordinates
4222  Numeric x, y, z, dx, dy, dz, lat_new, lon_new;
4223  //
4224  poslos2cart(x, y, z, dx, dy, dz, r, lat, lon, za, aa);
4225  lstep = lraytrace;
4226  cart2poslos(r,
4227  lat_new,
4228  lon_new,
4229  za_new,
4230  aa_new,
4231  x + dx * lstep,
4232  y + dy * lstep,
4233  z + dz * lstep,
4234  dx,
4235  dy,
4236  dz,
4237  ppc_step,
4238  x,
4239  y,
4240  z,
4241  lat,
4242  lon,
4243  za,
4244  aa);
4245  lcum += lstep;
4246 
4247  // Shall lon values be shifted?
4248  resolve_lon(lon_new, lon5, lon6);
4249 
4250  lat = lat_new;
4251  lon = lon_new;
4252  }
4253 
4254  // Refractive index at new point
4255  Numeric dndr, dndlat, dndlon;
4256  refr_gradients_3d(ws,
4257  refr_index_air,
4258  refr_index_air_group,
4259  dndr,
4260  dndlat,
4261  dndlon,
4262  refr_index_air_agenda,
4263  p_grid,
4264  lat_grid,
4265  lon_grid,
4266  refellipsoid,
4267  z_field,
4268  t_field,
4269  vmr_field,
4270  f_grid,
4271  r,
4272  lat,
4273  lon);
4274 
4275  // Calculate LOS zenith angle at found point.
4276  const Numeric aterm = RAD2DEG * lstep / refr_index_air;
4277  const Numeric za_rad = DEG2RAD * za;
4278  const Numeric aa_rad = DEG2RAD * aa;
4279  const Numeric sinza = sin(za_rad);
4280  const Numeric sinaa = sin(aa_rad);
4281  const Numeric cosaa = cos(aa_rad);
4282  //
4283  Vector los(2);
4284  los[0] = za_new;
4285  los[1] = aa_new;
4286  //
4287  if (za < ANGTOL || za > 180 - ANGTOL) {
4288  los[0] += aterm * (cos(za_rad) * (cosaa * dndlat + sinaa * dndlon));
4289  los[1] = RAD2DEG * atan2(dndlon, dndlat);
4290  } else {
4291  los[0] += aterm * (-sinza * dndr +
4292  cos(za_rad) * (cosaa * dndlat + sinaa * dndlon));
4293  los[1] += aterm * sinza * (cosaa * dndlon - sinaa * dndlat);
4294  }
4295  //
4296  adjust_los(los, 3);
4297  //
4298  za = los[0];
4299  aa = los[1];
4300 
4301  // For some cases where the path goes along an end face,
4302  // it could be the case that the refraction bends the path out
4303  // of the grid cell.
4304  if (za > 0 && za < 180) {
4305  if (lon == lon5 && aa < 0) {
4306  endface = 5;
4307  ready = 1;
4308  } else if (lon == lon6 && aa > 0) {
4309  endface = 6;
4310  ready = 1;
4311  } else if (lat == lat1 && lat != -90 && abs(aa) > 90) {
4312  endface = 1;
4313  ready = 1;
4314  } else if (lat == lat3 && lat != 90 && abs(aa) < 90) {
4315  endface = 3;
4316  ready = 1;
4317  }
4318  }
4319 
4320  // Store found point?
4321  if (ready || (lmax > 0 && lcum + lraytrace > lmax)) {
4322  r_array.push_back(r);
4323  lat_array.push_back(lat);
4324  lon_array.push_back(lon);
4325  za_array.push_back(za);
4326  aa_array.push_back(aa);
4327  n_array.push_back(refr_index_air);
4328  ng_array.push_back(refr_index_air_group);
4329  l_array.push_back(lcum);
4330  lcum = 0;
4331  }
4332  }
4333 }
4334 
4336  Ppath& ppath,
4337  ConstVectorView p_grid,
4338  ConstVectorView lat_grid,
4339  ConstVectorView lon_grid,
4340  ConstTensor3View z_field,
4341  ConstTensor3View t_field,
4342  ConstTensor4View vmr_field,
4343  ConstVectorView f_grid,
4344  ConstVectorView refellipsoid,
4345  ConstMatrixView z_surface,
4346  const Numeric& lmax,
4347  const Agenda& refr_index_air_agenda,
4348  const String& rtrace_method,
4349  const Numeric& lraytrace) {
4350  // Radius, zenith angle and latitude of start point.
4351  Numeric r_start, lat_start, lon_start, za_start, aa_start;
4352 
4353  // Lower grid index for the grid cell of interest.
4354  Index ip, ilat, ilon;
4355 
4356  // Radius for corner points, latitude and longitude of the grid cell
4357  //
4358  Numeric lat1, lat3, lon5, lon6;
4359  Numeric r15a, r35a, r36a, r16a, r15b, r35b, r36b, r16b;
4360  Numeric rsurface15, rsurface35, rsurface36, rsurface16;
4361 
4362  // Determine the variables defined above and make all possible asserts
4363  ppath_start_3d(r_start,
4364  lat_start,
4365  lon_start,
4366  za_start,
4367  aa_start,
4368  ip,
4369  ilat,
4370  ilon,
4371  lat1,
4372  lat3,
4373  lon5,
4374  lon6,
4375  r15a,
4376  r35a,
4377  r36a,
4378  r16a,
4379  r15b,
4380  r35b,
4381  r36b,
4382  r16b,
4383  rsurface15,
4384  rsurface35,
4385  rsurface36,
4386  rsurface16,
4387  ppath,
4388  lat_grid,
4389  lon_grid,
4390  z_field,
4391  refellipsoid,
4392  z_surface);
4393 
4394  // Perform the ray tracing
4395  //
4396  // No constant for the path is valid here.
4397  //
4398  // Arrays to store found ray tracing points
4399  // (Vectors don't work here as we don't know how many points there will be)
4400  Array<Numeric> r_array, lat_array, lon_array, za_array, aa_array;
4401  Array<Numeric> l_array, n_array, ng_array;
4402  Index endface;
4403  //
4404  if (rtrace_method == "linear_basic") {
4406  r_array,
4407  lat_array,
4408  lon_array,
4409  za_array,
4410  aa_array,
4411  l_array,
4412  n_array,
4413  ng_array,
4414  endface,
4415  refellipsoid,
4416  p_grid,
4417  lat_grid,
4418  lon_grid,
4419  z_field,
4420  t_field,
4421  vmr_field,
4422  f_grid,
4423  lmax,
4424  refr_index_air_agenda,
4425  lraytrace,
4426  lat1,
4427  lat3,
4428  lon5,
4429  lon6,
4430  rsurface15,
4431  rsurface35,
4432  rsurface36,
4433  rsurface16,
4434  r15a,
4435  r35a,
4436  r36a,
4437  r16a,
4438  r15b,
4439  r35b,
4440  r36b,
4441  r16b,
4442  r_start,
4443  lat_start,
4444  lon_start,
4445  za_start,
4446  aa_start);
4447  } else {
4448  // Make sure we fail if called with an invalid rtrace_method.
4449  assert(false);
4450  }
4451 
4452  // Fill *ppath*
4453  //
4454  const Index np = r_array.nelem();
4455  Vector r_v(np), lat_v(np), lon_v(np), za_v(np), aa_v(np), l_v(np - 1);
4456  Vector n_v(np), ng_v(np);
4457  for (Index i = 0; i < np; i++) {
4458  r_v[i] = r_array[i];
4459  lat_v[i] = lat_array[i];
4460  lon_v[i] = lon_array[i];
4461  za_v[i] = za_array[i];
4462  aa_v[i] = aa_array[i];
4463  n_v[i] = n_array[i];
4464  ng_v[i] = ng_array[i];
4465  if (i < np - 1) {
4466  l_v[i] = l_array[i];
4467  }
4468  }
4469  //
4470  // Fill *ppath*
4471  ppath_end_3d(ppath,
4472  r_v,
4473  lat_v,
4474  lon_v,
4475  za_v,
4476  aa_v,
4477  l_v,
4478  n_v,
4479  ng_v,
4480  lat_grid,
4481  lon_grid,
4482  z_field,
4483  refellipsoid,
4484  ip,
4485  ilat,
4486  ilon,
4487  endface,
4488  -1);
4489 }
4490 
4491 /*===========================================================================
4492  === Main functions
4493  ===========================================================================*/
4494 
4496  const Index& atmosphere_dim,
4497  ConstVectorView p_grid,
4498  ConstVectorView lat_grid,
4499  ConstVectorView lon_grid,
4500  ConstTensor3View z_field,
4501  ConstVectorView refellipsoid,
4502  ConstMatrixView z_surface,
4503  const Index& cloudbox_on,
4504  const ArrayOfIndex& cloudbox_limits,
4505  const bool& ppath_inside_cloudbox_do,
4506  ConstVectorView rte_pos,
4507  ConstVectorView rte_los,
4508  const Verbosity& verbosity) {
4509  CREATE_OUT1;
4510 
4511  // This function contains no checks or asserts as it is only a sub-function.
4512 
4513  // Allocate the ppath structure
4514  ppath_init_structure(ppath, atmosphere_dim, 1);
4515 
4516  // Index of last pressure level
4517  const Index lp = p_grid.nelem() - 1;
4518 
4519  // The different atmospheric dimensionalities are handled seperately
4520 
4521  //-- 1D ---------------------------------------------------------------------
4522  if (atmosphere_dim == 1) {
4523  // End position and LOS
4524  ppath.end_pos[0] = rte_pos[0];
4525  ppath.end_pos[1] = 0;
4526  ppath.end_los[0] = rte_los[0];
4527 
4528  // Sensor is inside the model atmosphere:
4529  if (rte_pos[0] < z_field(lp, 0, 0)) {
4530  // Check that the sensor is above the surface
4531  if ((rte_pos[0] + RTOL) < z_surface(0, 0)) {
4532  ostringstream os;
4533  os << "The ppath starting point is placed "
4534  << (z_surface(0, 0) - rte_pos[0]) / 1e3 << " km below the surface.";
4535  throw runtime_error(os.str());
4536  }
4537 
4538  // Set ppath
4539  ppath.pos(0, joker) = ppath.end_pos;
4540  ppath.r[0] = refellipsoid[0] + rte_pos[0];
4541  ppath.los(0, joker) = ppath.end_los;
4542  //
4543  gridpos(ppath.gp_p, z_field(joker, 0, 0), ppath.pos(0, 0));
4544  gridpos_check_fd(ppath.gp_p[0]);
4545 
4546  // Is the sensor on the surface looking down?
4547  // If yes and the sensor is inside the cloudbox, the background will
4548  // be changed below.
4549  if (ppath.pos(0, 0) <= z_surface(0, 0) && ppath.los(0, 0) > 90) {
4550  ppath_set_background(ppath, 2);
4551  }
4552 
4553  // Outside cloudbox:
4554  // Check sensor position with respect to cloud box.
4555  if (cloudbox_on && !ppath_inside_cloudbox_do) {
4556  const Numeric fgp = fractional_gp(ppath.gp_p[0]);
4557  if (fgp >= (Numeric)cloudbox_limits[0] &&
4558  fgp <= (Numeric)cloudbox_limits[1]) {
4559  ppath_set_background(ppath, 4);
4560  }
4561  }
4562 
4563  // Inside cloudbox:
4564  DEBUG_ONLY(if (ppath_inside_cloudbox_do) {
4565  const Numeric fgp = fractional_gp(ppath.gp_p[0]);
4566  assert(fgp >= (Numeric)cloudbox_limits[0] &&
4567  fgp <= (Numeric)cloudbox_limits[1]);
4568  })
4569  }
4570 
4571  // Sensor is outside the model atmosphere:
4572  else {
4573  // We can here set ppc and n as we are outside the atmosphere
4574  ppath.nreal = 1.0;
4575  ppath.ngroup = 1.0;
4576  ppath.constant =
4577  geometrical_ppc(refellipsoid[0] + rte_pos[0], rte_los[0]);
4578 
4579  // Totally outside
4580  if (rte_los[0] <= 90 ||
4581  ppath.constant >= refellipsoid[0] + z_field(lp, 0, 0)) {
4582  ppath.pos(0, 0) = rte_pos[0];
4583  ppath.pos(0, 1) = 0;
4584  ppath.r[0] = refellipsoid[0] + rte_pos[0];
4585  ppath.los(0, 0) = rte_los[0];
4586  //
4587  ppath_set_background(ppath, 1);
4588  out1 << " --- WARNING ---, path is totally outside of the "
4589  << "model atmosphere\n";
4590  }
4591 
4592  // Path enters the atmosphere.
4593  else {
4594  ppath.r[0] = refellipsoid[0] + z_field(lp, 0, 0);
4595  ppath.pos(0, 0) = z_field(lp, 0, 0);
4596  ppath.los(0, 0) =
4597  geompath_za_at_r(ppath.constant, rte_los[0], ppath.r[0]);
4598  ppath.pos(0, 1) = geompath_lat_at_za(rte_los[0], 0, ppath.los(0, 0));
4599  ppath.end_lstep =
4600  geompath_l_at_r(ppath.constant, refellipsoid[0] + rte_pos[0]) -
4601  geompath_l_at_r(ppath.constant, ppath.r[0]);
4602 
4603  // Here we know the grid position exactly
4604  ppath.gp_p[0].idx = lp - 1;
4605  ppath.gp_p[0].fd[0] = 1;
4606  ppath.gp_p[0].fd[1] = 0;
4607 
4608  // If cloud box reaching TOA, we have also found the background
4609  if (cloudbox_on && cloudbox_limits[1] == lp) {
4610  ppath_set_background(ppath, 3);
4611  }
4612  }
4613  }
4614  } // End 1D
4615 
4616  //-- 2D ---------------------------------------------------------------------
4617  else if (atmosphere_dim == 2) {
4618  // End position and LOS
4619  ppath.end_pos[0] = rte_pos[0];
4620  ppath.end_pos[1] = rte_pos[1];
4621  ppath.end_los[0] = rte_los[0];
4622 
4623  // Index of last latitude
4624  const Index llat = lat_grid.nelem() - 1;
4625 
4626  // Is sensor inside range of lat_grid?
4627  // If yes, determine TOA altitude at sensor position
4628  GridPos gp_lat;
4629  Vector itw(2);
4630  bool islatin = false;
4631  Numeric r_e; // Ellipsoid radius at sensor position
4632  Numeric z_toa = -99e99;
4633  if (rte_pos[1] > lat_grid[0] && rte_pos[1] < lat_grid[llat]) {
4634  islatin = true;
4635  gridpos(gp_lat, lat_grid, rte_pos[1]);
4636  interpweights(itw, gp_lat);
4637  z_toa = interp(itw, z_field(lp, joker, 0), gp_lat);
4638  r_e = refell2d(refellipsoid, lat_grid, gp_lat);
4639  } else {
4640  r_e = refell2r(refellipsoid, rte_pos[1]);
4641  }
4642 
4643  // Sensor is inside the model atmosphere:
4644  if (islatin && rte_pos[0] < z_toa) {
4645  const Numeric z_s = interp(itw, z_surface(joker, 0), gp_lat);
4646 
4647  // Check that the sensor is above the surface
4648  if ((rte_pos[0] + RTOL) < z_s) {
4649  ostringstream os;
4650  os << "The ppath starting point is placed " << (z_s - rte_pos[0]) / 1e3
4651  << " km below the surface.";
4652  throw runtime_error(os.str());
4653  }
4654 
4655  // Set ppath
4656  ppath.pos(0, joker) = ppath.end_pos;
4657  ppath.r[0] = r_e + rte_pos[0];
4658  ppath.los(0, joker) = ppath.end_los;
4659 
4660  // Determine gp_p (gp_lat is recalculated, but ...)
4661  GridPos gp_lon_dummy;
4662  rte_pos2gridpos(ppath.gp_p[0],
4663  ppath.gp_lat[0],
4664  gp_lon_dummy,
4665  atmosphere_dim,
4666  p_grid,
4667  lat_grid,
4668  lon_grid,
4669  z_field,
4670  rte_pos);
4671  gridpos_check_fd(ppath.gp_p[0]);
4672  gridpos_check_fd(ppath.gp_lat[0]);
4673 
4674  // Is the sensor on the surface looking down?
4675  // If yes and the sensor is inside the cloudbox, the background will
4676  // be changed below.
4677  if (ppath.pos(0, 0) <= z_s) {
4678  // Calculate radial slope of the surface
4679  Numeric rslope;
4680  plevel_slope_2d(rslope,
4681  lat_grid,
4682  refellipsoid,
4683  z_surface(joker, 0),
4684  gp_lat,
4685  ppath.los(0, 0));
4686 
4687  // Calculate angular tilt of the surface
4688  const Numeric atilt = plevel_angletilt(r_e + z_s, rslope);
4689 
4690  // Are we looking down into the surface?
4691  // If yes and the sensor is inside the cloudbox, the background
4692  // will be changed below.
4693  if (is_los_downwards(ppath.los(0, 0), atilt)) {
4694  ppath_set_background(ppath, 2);
4695  }
4696  }
4697 
4698  // Outside cloudbox:
4699  // Check sensor position with respect to cloud box.
4700  if (cloudbox_on && !ppath_inside_cloudbox_do) {
4701  const Numeric fgp = fractional_gp(ppath.gp_p[0]);
4702  const Numeric fgl = fractional_gp(ppath.gp_lat[0]);
4703  if (fgp >= (Numeric)cloudbox_limits[0] &&
4704  fgp <= (Numeric)cloudbox_limits[1] &&
4705  fgl >= (Numeric)cloudbox_limits[2] &&
4706  fgl <= (Numeric)cloudbox_limits[3]) {
4707  ppath_set_background(ppath, 4);
4708  }
4709  }
4710 
4711  // Inside cloudbox:
4712  DEBUG_ONLY(if (ppath_inside_cloudbox_do) {
4713  const Numeric fgp = fractional_gp(ppath.gp_p[0]);
4714  const Numeric fgl = fractional_gp(ppath.gp_lat[0]);
4715  assert(fgp >= (Numeric)cloudbox_limits[0] &&
4716  fgp <= (Numeric)cloudbox_limits[1] &&
4717  fgl >= (Numeric)cloudbox_limits[2] &&
4718  fgl <= (Numeric)cloudbox_limits[3]);
4719  })
4720  }
4721 
4722  // Sensor is outside the model atmosphere:
4723  else {
4724  // Handle cases when the sensor looks in the wrong way
4725  if ((rte_pos[1] <= lat_grid[0] && rte_los[0] <= 0) ||
4726  (rte_pos[1] >= lat_grid[llat] && rte_los[0] >= 0)) {
4727  ostringstream os;
4728  os << "The sensor is outside (or at the limit) of the model "
4729  << "atmosphere but\nlooks in the wrong direction (wrong sign "
4730  << "for the zenith angle?).\nThis case includes nadir "
4731  << "looking exactly at the latitude end points.";
4732  throw runtime_error(os.str());
4733  }
4734 
4735  // We can here set ppc and n as we are outside the atmosphere
4736  ppath.nreal = 1.0;
4737  ppath.ngroup = 1.0;
4738  const Numeric r_p = r_e + rte_pos[0];
4739  ppath.constant = geometrical_ppc(r_p, rte_los[0]);
4740 
4741  // Determine TOA radii, and min and max value
4742  Vector r_toa(llat + 1);
4743  Numeric r_toa_min = 99e99, r_toa_max = -1;
4744  for (Index ilat = 0; ilat <= llat; ilat++) {
4745  r_toa[ilat] =
4746  refell2r(refellipsoid, lat_grid[ilat]) + z_field(lp, ilat, 0);
4747  if (r_toa[ilat] < r_toa_min) {
4748  r_toa_min = r_toa[ilat];
4749  }
4750  if (r_toa[ilat] > r_toa_max) {
4751  r_toa_max = r_toa[ilat];
4752  }
4753  }
4754  if (r_p <= r_toa_max) {
4755  ostringstream os;
4756  os << "The sensor is horizontally outside (or at the limit) of "
4757  << "the model\natmosphere, but is at a radius smaller than "
4758  << "the maximum value of\nthe top-of-the-atmosphere radii. "
4759  << "This is not allowed. Make the\nmodel atmosphere larger "
4760  << "to also cover the sensor position?";
4761  throw runtime_error(os.str());
4762  }
4763 
4764  // Upward:
4765  if (abs(rte_los[0]) <= 90) {
4766  ppath.pos(0, 0) = rte_pos[0];
4767  ppath.pos(0, 1) = rte_pos[1];
4768  ppath.r[0] = r_e + rte_pos[0];
4769  ppath.los(0, 0) = rte_los[0];
4770  //
4771  ppath_set_background(ppath, 1);
4772  out1 << " ------- WARNING -------: path is totally outside of "
4773  << "the model atmosphere\n";
4774  }
4775 
4776  // Downward:
4777  else {
4778  bool above = false, ready = false, failed = false;
4779  Numeric rt = -1, latt, lt, lt_old = L_NOT_FOUND;
4780  GridPos gp_latt;
4781  Vector itwt(2);
4782 
4783  // Check if clearly above the model atmosphere
4784  if (ppath.constant >= r_toa_max) {
4785  above = true;
4786  ready = true;
4787  } else { // Otherwise pick out a suitable first test radius
4788  if (islatin || ppath.constant > r_toa_min) {
4789  rt = r_toa_max;
4790  } else {
4791  rt = r_toa_min;
4792  }
4793  }
4794 
4795  // Iterate until solution found or moving out from model atm.
4796  //
4797  while (!ready && !failed) {
4798  // If rt < ppath.constant, ppath above atmosphere
4799  if (rt < ppath.constant) {
4800  above = true;
4801  ready = true;
4802  }
4803 
4804  else {
4805  // Calculate length to entrance point at rt
4806  r_crossing_2d(
4807  latt, lt, rt, r_p, rte_pos[1], rte_los[0], ppath.constant);
4808  assert(lt < 9e9);
4809 
4810  // Entrance outside range of lat_grid = fail
4811  if (latt < lat_grid[0] || latt > lat_grid[llat]) {
4812  failed = true;
4813  }
4814 
4815  // OK iteration
4816  else {
4817  // Converged?
4818  if (abs(lt - lt_old) < LACC) {
4819  ready = true;
4820  }
4821 
4822  // Update rt
4823  lt_old = lt;
4824  gridpos(gp_latt, lat_grid, latt);
4825  interpweights(itwt, gp_latt);
4826  rt = interp(itwt, r_toa, gp_latt);
4827  }
4828  }
4829  } // while
4830 
4831  if (failed) {
4832  ostringstream os;
4833  os << "The path does not enter the model atmosphere. It "
4834  << "reaches the\ntop of the atmosphere "
4835  << "altitude around latitude " << latt << " deg.";
4836  throw runtime_error(os.str());
4837  } else if (above) {
4838  ppath.pos(0, 0) = rte_pos[0];
4839  ppath.pos(0, 1) = rte_pos[1];
4840  ppath.r[0] = r_e + rte_pos[0];
4841  ppath.los(0, 0) = rte_los[0];
4842  //
4843  ppath_set_background(ppath, 1);
4844  out1 << " ------- WARNING -------: path is totally outside "
4845  << "of the model atmosphere\n";
4846  } else {
4847  ppath.r[0] = rt;
4848  ppath.pos(0, 0) = interp(itwt, z_field(lp, joker, 0), gp_latt);
4849  // Calculate za first and use to determine lat
4850  ppath.los(0, 0) = geompath_za_at_r(ppath.constant, rte_los[0], rt);
4851  ppath.pos(0, 1) =
4852  geompath_lat_at_za(rte_los[0], rte_pos[1], ppath.los(0, 0));
4853  ppath.end_lstep = lt;
4854 
4855  // Here we know the pressure grid position exactly
4856  ppath.gp_p[0].idx = lp - 1;
4857  ppath.gp_p[0].fd[0] = 1;
4858  ppath.gp_p[0].fd[1] = 0;
4859 
4860  // Latitude grid position already calculated
4861  gridpos_copy(ppath.gp_lat[0], gp_latt);
4862 
4863  // Hit with cloudbox reaching TOA?
4864  if (cloudbox_on && cloudbox_limits[1] == lp) {
4865  Numeric fgp = fractional_gp(gp_latt);
4866  if (fgp >= (Numeric)cloudbox_limits[2] &&
4867  fgp <= (Numeric)cloudbox_limits[3]) {
4868  ppath_set_background(ppath, 3);
4869  }
4870  }
4871  }
4872  } // Downward
4873  } // Outside
4874  } // End 2D
4875 
4876  //-- 3D ---------------------------------------------------------------------
4877  else {
4878  // Index of last latitude and longitude
4879  const Index llat = lat_grid.nelem() - 1;
4880  const Index llon = lon_grid.nelem() - 1;
4881 
4882  // Adjust longitude of rte_pos to range used in lon_grid
4883  Numeric lon2use = rte_pos[2];
4884  resolve_lon(lon2use, lon_grid[0], lon_grid[llon]);
4885 
4886  // End position and LOS
4887  ppath.end_pos[0] = rte_pos[0];
4888  ppath.end_pos[1] = rte_pos[1];
4889  ppath.end_pos[2] = lon2use;
4890  ppath.end_los[0] = rte_los[0];
4891  ppath.end_los[1] = rte_los[1];
4892 
4893  // Is sensor inside range of lat_grid and lon_grid?
4894  // If yes, determine TOA altitude at sensor position
4895  GridPos gp_lat, gp_lon;
4896  Vector itw(4);
4897  bool islatlonin = false;
4898  Numeric r_e; // Ellipsoid radius at sensor position
4899  Numeric z_toa = -99e99;
4900 
4901  if (rte_pos[1] >= lat_grid[0] && rte_pos[1] <= lat_grid[llat] &&
4902  lon2use >= lon_grid[0] && lon2use <= lon_grid[llon]) {
4903  islatlonin = true;
4904  gridpos(gp_lat, lat_grid, rte_pos[1]);
4905  gridpos(gp_lon, lon_grid, lon2use);
4906  interpweights(itw, gp_lat, gp_lon);
4907  z_toa = interp(itw, z_field(lp, joker, joker), gp_lat, gp_lon);
4908  r_e = refell2d(refellipsoid, lat_grid, gp_lat);
4909  } else {
4910  r_e = refell2r(refellipsoid, rte_pos[1]);
4911  }
4912 
4913  // Sensor is inside the model atmosphere:
4914  if (islatlonin && rte_pos[0] < z_toa) {
4915  const Numeric z_s = interp(itw, z_surface, gp_lat, gp_lon);
4916 
4917  // Check that the sensor is above the surface
4918  if ((rte_pos[0] + RTOL) < z_s) {
4919  ostringstream os;
4920  os << "The ppath starting point is placed " << (z_s - rte_pos[0]) / 1e3
4921  << " km below the surface.";
4922  throw runtime_error(os.str());
4923  }
4924 
4925  // Set ppath
4926  ppath.pos(0, joker) = ppath.end_pos;
4927  ppath.r[0] = r_e + rte_pos[0];
4928  ppath.los(0, joker) = ppath.end_los;
4929 
4930  // Determine gp_p (gp_lat and gp_lon are recalculated, but ...)
4931  rte_pos2gridpos(ppath.gp_p[0],
4932  ppath.gp_lat[0],
4933  ppath.gp_lon[0],
4934  atmosphere_dim,
4935  p_grid,
4936  lat_grid,
4937  lon_grid,
4938  z_field,
4939  rte_pos);
4940  gridpos_check_fd(ppath.gp_p[0]);
4941  gridpos_check_fd(ppath.gp_lat[0]);
4942  gridpos_check_fd(ppath.gp_lon[0]);
4943 
4944  // Is the sensor on the surface looking down?
4945  // If yes and the sensor is inside the cloudbox, the background will
4946  // be changed below.
4947  if (ppath.pos(0, 0) <= z_s) {
4948  // Calculate radial slope of the surface
4949  Numeric c1, c2;
4950  plevel_slope_3d(c1,
4951  c2,
4952  lat_grid,
4953  lon_grid,
4954  refellipsoid,
4955  z_surface,
4956  gp_lat,
4957  gp_lon,
4958  ppath.los(0, 1));
4959 
4960  // Calculate angular tilt of the surface
4961  const Numeric atilt = plevel_angletilt(r_e + z_s, c1);
4962 
4963  // Are we looking down into the surface?
4964  // If yes and the sensor is inside the cloudbox, the background
4965  // will be changed below.
4966  if (is_los_downwards(ppath.los(0, 0), atilt)) {
4967  ppath_set_background(ppath, 2);
4968  }
4969  }
4970 
4971  // Outside cloudbox:
4972  // Check sensor position with respect to cloud box.
4973  if (cloudbox_on && !ppath_inside_cloudbox_do) {
4974  const Numeric fgp = fractional_gp(ppath.gp_p[0]);
4975  const Numeric fgl = fractional_gp(ppath.gp_lat[0]);
4976  const Numeric fgo = fractional_gp(ppath.gp_lon[0]);
4977  if (fgp >= (Numeric)cloudbox_limits[0] &&
4978  fgp <= (Numeric)cloudbox_limits[1] &&
4979  fgl >= (Numeric)cloudbox_limits[2] &&
4980  fgl <= (Numeric)cloudbox_limits[3] &&
4981  fgo >= (Numeric)cloudbox_limits[4] &&
4982  fgo <= (Numeric)cloudbox_limits[5]) {
4983  ppath_set_background(ppath, 4);
4984  }
4985  }
4986 
4987  // Inside cloudbox:
4988  DEBUG_ONLY(if (ppath_inside_cloudbox_do) {
4989  const Numeric fgp = fractional_gp(ppath.gp_p[0]);
4990  const Numeric fgl = fractional_gp(ppath.gp_lat[0]);
4991  const Numeric fgo = fractional_gp(ppath.gp_lon[0]);
4992  assert(fgp >= (Numeric)cloudbox_limits[0] &&
4993  fgp <= (Numeric)cloudbox_limits[1] &&
4994  fgl >= (Numeric)cloudbox_limits[2] &&
4995  fgl <= (Numeric)cloudbox_limits[3] &&
4996  fgo >= (Numeric)cloudbox_limits[4] &&
4997  fgo <= (Numeric)cloudbox_limits[5]);
4998  })
4999  }
5000 
5001  // Sensor is outside the model atmosphere:
5002  else {
5003  // We can here set ppc and n as we are outside the atmosphere
5004  ppath.nreal = 1.0;
5005  ppath.ngroup = 1.0;
5006  const Numeric r_p = r_e + rte_pos[0];
5007  ppath.constant = geometrical_ppc(r_p, rte_los[0]);
5008 
5009  // Determine TOA radii, and min and max value
5010  Matrix r_toa(llat + 1, llon + 1);
5011  Numeric r_toa_min = 99e99, r_toa_max = -1;
5012  for (Index ilat = 0; ilat <= llat; ilat++) {
5013  const Numeric r_lat = refell2r(refellipsoid, lat_grid[ilat]);
5014  for (Index ilon = 0; ilon <= llon; ilon++) {
5015  r_toa(ilat, ilon) = r_lat + z_field(lp, ilat, ilon);
5016  if (r_toa(ilat, ilon) < r_toa_min) {
5017  r_toa_min = r_toa(ilat, ilon);
5018  }
5019  if (r_toa(ilat, ilon) > r_toa_max) {
5020  r_toa_max = r_toa(ilat, ilon);
5021  }
5022  }
5023  }
5024 
5025  if (r_p <= r_toa_max) {
5026  ostringstream os;
5027  os << "The sensor is horizontally outside (or at the limit) of "
5028  << "the model\natmosphere, but is at a radius smaller than "
5029  << "the maximum value of\nthe top-of-the-atmosphere radii. "
5030  << "This is not allowed. Make the\nmodel atmosphere larger "
5031  << "to also cover the sensor position?";
5032  throw runtime_error(os.str());
5033  }
5034 
5035  // Upward:
5036  if (rte_los[0] <= 90) {
5037  ppath.pos(0, 0) = rte_pos[0];
5038  ppath.pos(0, 1) = rte_pos[1];
5039  ppath.pos(0, 2) = rte_pos[2];
5040  ppath.r[0] = r_e + rte_pos[0];
5041  ppath.los(0, 0) = rte_los[0];
5042  ppath.los(0, 1) = rte_los[1];
5043  //
5044  ppath_set_background(ppath, 1);
5045  out1 << " ------- WARNING -------: path is totally outside of "
5046  << "the model atmosphere\n";
5047  }
5048 
5049  // Downward:
5050  else {
5051  bool above = false, ready = false, failed = false;
5052  Numeric rt = -1, latt, lont, lt, lt_old = L_NOT_FOUND;
5053  GridPos gp_latt, gp_lont;
5054  Vector itwt(4);
5055 
5056  // Check if clearly above the model atmosphere
5057  if (ppath.constant >= r_toa_max) {
5058  above = true;
5059  ready = true;
5060  } else { // Otherwise pick out a suitable first test radius
5061  if (islatlonin || ppath.constant > r_toa_min) {
5062  rt = r_toa_max;
5063  } else {
5064  rt = r_toa_min;
5065  }
5066  }
5067 
5068  // Sensor pos and LOS in cartesian coordinates
5069  Numeric x, y, z, dx, dy, dz;
5070  poslos2cart(x,
5071  y,
5072  z,
5073  dx,
5074  dy,
5075  dz,
5076  r_p,
5077  rte_pos[1],
5078  lon2use,
5079  rte_los[0],
5080  rte_los[1]);
5081 
5082  // Iterate until solution found or moving out from model atm.
5083  //
5084  while (!ready && !failed) {
5085  // If rt < ppath.constant, ppath above atmosphere
5086  if (rt < ppath.constant) {
5087  above = true;
5088  ready = true;
5089  }
5090 
5091  else {
5092  // Calculate lat and lon for entrance point at rt
5093  r_crossing_3d(latt,
5094  lont,
5095  lt,
5096  rt,
5097  r_p,
5098  rte_pos[1],
5099  lon2use,
5100  rte_los[0],
5101  ppath.constant,
5102  x,
5103  y,
5104  z,
5105  dx,
5106  dy,
5107  dz);
5108  resolve_lon(lont, lon_grid[0], lon_grid[llon]);
5109 
5110  // Entrance outside range of lat/lon_grids = fail
5111  if (latt < lat_grid[0] || latt > lat_grid[llat] ||
5112  lont < lon_grid[0] || lont > lon_grid[llon]) {
5113  failed = true;
5114  }
5115 
5116  // OK iteration
5117  else {
5118  // Converged?
5119  if (abs(lt - lt_old) < LACC) {
5120  ready = true;
5121  }
5122 
5123  // Update rt
5124  lt_old = lt;
5125  gridpos(gp_latt, lat_grid, latt);
5126  gridpos(gp_lont, lon_grid, lont);
5127  interpweights(itwt, gp_latt, gp_lont);
5128  rt = interp(itwt, r_toa, gp_latt, gp_lont);
5129  }
5130  }
5131  } // while
5132 
5133  if (failed) {
5134  ostringstream os;
5135  os << "The path does not enter the model atmosphere. It\n"
5136  << "reaches the top of the atmosphere altitude around:\n"
5137  << " lat: " << latt << " deg.\n lon: " << lont << " deg.";
5138  throw runtime_error(os.str());
5139  } else if (above) {
5140  ppath.pos(0, 0) = rte_pos[0];
5141  ppath.pos(0, 1) = rte_pos[1];
5142 // ppath.pos(0, 1) = lon2use;
5143  ppath.r[0] = r_e + rte_pos[0];
5144  ppath.los(0, 0) = rte_los[0];
5145  ppath.los(0, 1) = rte_los[1];
5146  //
5147  ppath_set_background(ppath, 1);
5148  out1 << " ------- WARNING -------: path is totally outside "
5149  << "of the model atmosphere\n";
5150  } else {
5151  // Calculate lt for last rt, and use it to determine pos/los
5152  lt = geompath_l_at_r(ppath.constant, r_p) -
5153  geompath_l_at_r(ppath.constant, rt);
5154  cart2poslos(ppath.r[0],
5155  ppath.pos(0, 1),
5156  ppath.pos(0, 2),
5157  ppath.los(0, 0),
5158  ppath.los(0, 1),
5159  x + dx * lt,
5160  y + dy * lt,
5161  z + dz * lt,
5162  dx,
5163  dy,
5164  dz,
5165  ppath.constant,
5166  x,
5167  y,
5168  z,
5169  rte_pos[1],
5170  lon2use,
5171  rte_los[0],
5172  rte_los[1]);
5173  assert(abs(ppath.r[0] - rt) < RTOL);
5174  resolve_lon(ppath.pos(0, 2), lon_grid[0], lon_grid[llon]);
5175  //
5176  ppath.pos(0, 0) =
5177  interp(itwt, z_field(lp, joker, joker), gp_latt, gp_lont);
5178  ppath.end_lstep = lt;
5179 
5180  // Here we know the pressure grid position exactly
5181  ppath.gp_p[0].idx = lp - 1;
5182  ppath.gp_p[0].fd[0] = 1;
5183  ppath.gp_p[0].fd[1] = 0;
5184 
5185  // Lat and lon grid position already calculated
5186  gridpos_copy(ppath.gp_lat[0], gp_latt);
5187  gridpos_copy(ppath.gp_lon[0], gp_lont);
5188 
5189  // Hit with cloudbox reaching TOA?
5190  if (cloudbox_on && cloudbox_limits[1] == lp) {
5191  Numeric fgp1 = fractional_gp(gp_latt);
5192  Numeric fgp2 = fractional_gp(gp_lont);
5193  if (fgp1 >= (Numeric)cloudbox_limits[2] &&
5194  fgp1 <= (Numeric)cloudbox_limits[3] &&
5195  fgp2 >= (Numeric)cloudbox_limits[4] &&
5196  fgp2 <= (Numeric)cloudbox_limits[5]) {
5197  ppath_set_background(ppath, 3);
5198  }
5199  }
5200  }
5201  } // Downward
5202  } // Outside
5203  } // End 3D
5204 }
5205 
5207  Ppath& ppath,
5208  const Agenda& ppath_step_agenda,
5209  const Index& atmosphere_dim,
5210  const Vector& p_grid,
5211  const Vector& lat_grid,
5212  const Vector& lon_grid,
5213  const Tensor3& z_field,
5214  const Vector& f_grid,
5215  const Vector& refellipsoid,
5216  const Matrix& z_surface,
5217  const Index& cloudbox_on,
5218  const ArrayOfIndex& cloudbox_limits,
5219  const Vector& rte_pos,
5220  const Vector& rte_los,
5221  const Numeric& ppath_lmax,
5222  const Numeric& ppath_lraytrace,
5223  const bool& ppath_inside_cloudbox_do,
5224  const Verbosity& verbosity) {
5225  // This function is a WSM but it is normally only called from yCalc.
5226  // For that reason, this function does not repeat input checks that are
5227  // performed in yCalc, it only performs checks regarding the sensor
5228  // position and LOS.
5229 
5230  //--- Check input -----------------------------------------------------------
5231  chk_rte_pos(atmosphere_dim, rte_pos);
5232  chk_rte_los(atmosphere_dim, rte_los);
5233  if (ppath_inside_cloudbox_do && !cloudbox_on)
5234  throw runtime_error(
5235  "The WSV *ppath_inside_cloudbox_do* can only be set "
5236  "to 1 if also *cloudbox_on* is 1.");
5237  //--- End: Check input ------------------------------------------------------
5238 
5239  // Initiate the partial Ppath structure.
5240  // The function doing the work sets ppath_step to the point of the path
5241  // inside the atmosphere closest to the sensor, if the path is at all inside
5242  // the atmosphere.
5243  // If the background field is set by the function this flags that there is no
5244  // path to follow (for example when the sensor is inside the cloud box).
5245  // The function checks also that the sensor position and LOS give an
5246  // allowed path.
5247  //
5248  Ppath ppath_step;
5249  //
5250  ppath_start_stepping(ppath_step,
5251  atmosphere_dim,
5252  p_grid,
5253  lat_grid,
5254  lon_grid,
5255  z_field,
5256  refellipsoid,
5257  z_surface,
5258  cloudbox_on,
5259  cloudbox_limits,
5260  ppath_inside_cloudbox_do,
5261  rte_pos,
5262  rte_los,
5263  verbosity);
5264  // For debugging:
5265  //Print( ppath_step, 0, verbosity );
5266 
5267  // The only data we need to store from this initial ppath_step is
5268  // end_pos/los/lstep
5269  const Numeric end_lstep = ppath_step.end_lstep;
5270  const Vector end_pos = ppath_step.end_pos;
5271  const Vector end_los = ppath_step.end_los;
5272 
5273  // Perform propagation path steps until the starting point is found, which
5274  // is flagged by ppath_step by setting the background field.
5275  //
5276  // The results of each step, returned by ppath_step_agenda as a new
5277  // ppath_step, are stored as an array of Ppath structures.
5278  //
5279  Array<Ppath> ppath_array(0);
5280  Index np = 1; // Counter for number of points of the path
5281  Index istep = 0; // Counter for number of steps
5282  //
5283  const Index imax_p = p_grid.nelem() - 1;
5284  const Index imax_lat = lat_grid.nelem() - 1;
5285  const Index imax_lon = lon_grid.nelem() - 1;
5286  //
5287  bool ready = ppath_what_background(ppath_step);
5288  //
5289  while (!ready) {
5290  // Call ppath_step agenda.
5291  // The new path step is added to *ppath_array* last in the while block
5292  //
5293  istep++;
5294  //
5296  ppath_step,
5297  ppath_lmax,
5298  ppath_lraytrace,
5299  f_grid,
5300  ppath_step_agenda);
5301  // For debugging:
5302  //Print( ppath_step, 0, verbosity );
5303 
5304  // Number of points in returned path step
5305  const Index n = ppath_step.np;
5306 
5307  // Increase the total number
5308  np += n - 1;
5309 
5310  if (istep > (Index)1e4)
5311  throw runtime_error(
5312  "10 000 path points have been reached. Is this an infinite loop?");
5313 
5314  //----------------------------------------------------------------------
5315  //--- Check if some boundary is reached
5316  //----------------------------------------------------------------------
5317 
5318  //--- Outside cloud box ------------------------------------------------
5319  if (!ppath_inside_cloudbox_do) {
5320  // Check if the top of the atmosphere is reached
5321  // (Perform first a strict check, and if upward propagation also a
5322  // non-strict check. The later to avoid fatal "fixes" at TOA.)
5323  if (is_gridpos_at_index_i(ppath_step.gp_p[n - 1], imax_p) ||
5324  (abs(ppath_step.los(n - 1, 0)) < 90 &&
5325  is_gridpos_at_index_i(ppath_step.gp_p[n - 1], imax_p, false))) {
5326  ppath_set_background(ppath_step, 1);
5327  }
5328 
5329  // Check that path does not exit at a latitude or longitude end face
5330  if (atmosphere_dim == 2) {
5331  // Latitude
5332  if (is_gridpos_at_index_i(ppath_step.gp_lat[n - 1], 0)) {
5333  ostringstream os;
5334  os << "The path exits the atmosphere through the lower "
5335  << "latitude end face.\nThe exit point is at an altitude"
5336  << " of " << ppath_step.pos(n - 1, 0) / 1e3 << " km.";
5337  throw runtime_error(os.str());
5338  }
5339  if (is_gridpos_at_index_i(ppath_step.gp_lat[n - 1], imax_lat)) {
5340  ostringstream os;
5341  os << "The path exits the atmosphere through the upper "
5342  << "latitude end face.\nThe exit point is at an altitude"
5343  << " of " << ppath_step.pos(n - 1, 0) / 1e3 << " km.";
5344  throw runtime_error(os.str());
5345  }
5346  }
5347  if (atmosphere_dim == 3) {
5348  // Latitude
5349  if (lat_grid[0] > -90 &&
5350  is_gridpos_at_index_i(ppath_step.gp_lat[n - 1], 0)) {
5351  ostringstream os;
5352  os << "The path exits the atmosphere through the lower "
5353  << "latitude end face.\nThe exit point is at an altitude"
5354  << " of " << ppath_step.pos(n - 1, 0) / 1e3 << " km.";
5355  throw runtime_error(os.str());
5356  }
5357  if (lat_grid[imax_lat] < 90 &&
5358  is_gridpos_at_index_i(ppath_step.gp_lat[n - 1], imax_lat)) {
5359  ostringstream os;
5360  os << "The path exits the atmosphere through the upper "
5361  << "latitude end face.\nThe exit point is at an altitude"
5362  << " of " << ppath_step.pos(n - 1, 0) / 1e3 << " km.";
5363  throw runtime_error(os.str());
5364  }
5365 
5366  // Longitude
5367  // Note that it must be if and else if here. Otherwise e.g. -180
5368  // will be shifted to 180 and then later back to -180.
5369  if (is_gridpos_at_index_i(ppath_step.gp_lon[n - 1], 0) &&
5370  ppath_step.los(n - 1, 1) < 0 &&
5371  abs(ppath_step.pos(n - 1, 1)) < 90) {
5372  // Check if the longitude point can be shifted +360 degrees
5373  if (lon_grid[imax_lon] - lon_grid[0] >= 360) {
5374  ppath_step.pos(n - 1, 2) = ppath_step.pos(n - 1, 2) + 360;
5375  gridpos(
5376  ppath_step.gp_lon[n - 1], lon_grid, ppath_step.pos(n - 1, 2));
5377  } else {
5378  ostringstream os;
5379  os << "The path exits the atmosphere through the lower "
5380  << "longitude end face.\nThe exit point is at an "
5381  << "altitude of " << ppath_step.pos(n - 1, 0) / 1e3 << " km.";
5382  throw runtime_error(os.str());
5383  }
5384  } else if (is_gridpos_at_index_i(ppath_step.gp_lon[n - 1], imax_lon) &&
5385  ppath_step.los(n - 1, 1) > 0 &&
5386  abs(ppath_step.pos(n - 1, 1)) < 90) {
5387  // Check if the longitude point can be shifted -360 degrees
5388  if (lon_grid[imax_lon] - lon_grid[0] >= 360) {
5389  ppath_step.pos(n - 1, 2) = ppath_step.pos(n - 1, 2) - 360;
5390  gridpos(
5391  ppath_step.gp_lon[n - 1], lon_grid, ppath_step.pos(n - 1, 2));
5392  } else {
5393  ostringstream os;
5394  os << "The path exits the atmosphere through the upper "
5395  << "longitude end face.\nThe exit point is at an "
5396  << "altitude of " << ppath_step.pos(n - 1, 0) / 1e3 << " km.";
5397  throw runtime_error(os.str());
5398  }
5399  }
5400  }
5401 
5402  // Check if there is an intersection with an active cloud box
5403  if (cloudbox_on) {
5404  Numeric ipos = fractional_gp(ppath_step.gp_p[n - 1]);
5405 
5406  if (ipos >= Numeric(cloudbox_limits[0]) &&
5407  ipos <= Numeric(cloudbox_limits[1])) {
5408  if (atmosphere_dim == 1) {
5409  ppath_set_background(ppath_step, 3);
5410  } else {
5411  ipos = fractional_gp(ppath_step.gp_lat[n - 1]);
5412 
5413  if (ipos >= Numeric(cloudbox_limits[2]) &&
5414  ipos <= Numeric(cloudbox_limits[3])) {
5415  if (atmosphere_dim == 2) {
5416  ppath_set_background(ppath_step, 3);
5417  } else {
5418  ipos = fractional_gp(ppath_step.gp_lon[n - 1]);
5419 
5420  if (ipos >= Numeric(cloudbox_limits[4]) &&
5421  ipos <= Numeric(cloudbox_limits[5])) {
5422  ppath_set_background(ppath_step, 3);
5423  }
5424  }
5425  }
5426  }
5427  }
5428  }
5429  }
5430 
5431  //--- Inside cloud box -------------------------------------------------
5432  else {
5433  // A first version just checked if point was at or outside any
5434  // boundary but numerical problems could cause that the start point
5435  // was taken as the exit point. So check of ppath direction had to be
5436  // added. Fractional distances used for this.
5437 
5438  // Pressure dimension
5439  Numeric ipos1 = fractional_gp(ppath_step.gp_p[n - 1]);
5440  Numeric ipos2 = fractional_gp(ppath_step.gp_p[n - 2]);
5441  assert(ipos1 >= (Numeric)cloudbox_limits[0]);
5442  assert(ipos1 <= (Numeric)cloudbox_limits[1]);
5443  if (ipos1 <= (Numeric)cloudbox_limits[0] && ipos1 < ipos2) {
5444  ppath_set_background(ppath_step, 3);
5445  }
5446 
5447  else if (ipos1 >= Numeric(cloudbox_limits[1]) && ipos1 > ipos2) {
5448  ppath_set_background(ppath_step, 3);
5449  }
5450 
5451  else if (atmosphere_dim > 1) {
5452  // Latitude dimension
5453  ipos1 = fractional_gp(ppath_step.gp_lat[n - 1]);
5454  ipos2 = fractional_gp(ppath_step.gp_lat[n - 2]);
5455  assert(ipos1 >= (Numeric)cloudbox_limits[2]);
5456  assert(ipos1 <= (Numeric)cloudbox_limits[3]);
5457  if (ipos1 <= Numeric(cloudbox_limits[2]) && ipos1 < ipos2) {
5458  ppath_set_background(ppath_step, 3);
5459  }
5460 
5461  else if (ipos1 >= Numeric(cloudbox_limits[3]) && ipos1 > ipos2) {
5462  ppath_set_background(ppath_step, 3);
5463  }
5464 
5465  else if (atmosphere_dim > 2) {
5466  // Longitude dimension
5467  ipos1 = fractional_gp(ppath_step.gp_lon[n - 1]);
5468  ipos2 = fractional_gp(ppath_step.gp_lon[n - 2]);
5469  assert(ipos1 >= (Numeric)cloudbox_limits[4]);
5470  assert(ipos1 <= (Numeric)cloudbox_limits[5]);
5471  if (ipos1 <= Numeric(cloudbox_limits[4]) && ipos1 < ipos2) {
5472  ppath_set_background(ppath_step, 3);
5473  }
5474 
5475  else if (ipos1 >= Numeric(cloudbox_limits[5]) && ipos1 > ipos2) {
5476  ppath_set_background(ppath_step, 3);
5477  }
5478  }
5479  }
5480  }
5481  //----------------------------------------------------------------------
5482  //--- End of boundary check
5483  //----------------------------------------------------------------------
5484 
5485  // Ready?
5486  if (ppath_what_background(ppath_step)) {
5487  ppath_step.start_pos = ppath_step.pos(n - 1, joker);
5488  ppath_step.start_los = ppath_step.los(n - 1, joker);
5489  ready = true;
5490  }
5491 
5492  // Put new ppath_step in ppath_array
5493  ppath_array.push_back(ppath_step);
5494 
5495  } // End path steps
5496 
5497  // Combine all structures in ppath_array to form the return Ppath structure.
5498  //
5499  ppath_init_structure(ppath, atmosphere_dim, np);
5500  //
5501  const Index na = ppath_array.nelem();
5502  //
5503  if (na == 0) // No path, just the starting point
5504  {
5505  ppath_copy(ppath, ppath_step, 1);
5506  // To set n for positions inside the atmosphere, ppath_step_agenda
5507  // must be called once. The later is not always the case. A fix to handle
5508  // those cases:
5509  if (ppath_what_background(ppath_step) > 1) {
5510  //Print( ppath_step, 0, verbosity );
5512  ppath_step,
5513  ppath_lmax,
5514  ppath_lraytrace,
5515  f_grid,
5516  ppath_step_agenda);
5517  ppath.nreal[0] = ppath_step.nreal[0];
5518  ppath.ngroup[0] = ppath_step.ngroup[0];
5519  }
5520  }
5521 
5522  else // Otherwise, merge the array elelments
5523  {
5524  np = 0; // Now used as counter for points moved to ppath
5525  //
5526  for (Index i = 0; i < na; i++) {
5527  // For the first structure, the first point shall be included.
5528  // For later structures, the first point shall not be included, but
5529  // there will always be at least two points.
5530 
5531  Index n = ppath_array[i].np;
5532 
5533  // First index to include
5534  Index i1 = 1;
5535  if (i == 0) {
5536  i1 = 0;
5537  }
5538 
5539  // Vectors and matrices that can be handled by ranges.
5540  ppath.r[Range(np, n - i1)] = ppath_array[i].r[Range(i1, n - i1)];
5541  ppath.pos(Range(np, n - i1), joker) =
5542  ppath_array[i].pos(Range(i1, n - i1), joker);
5543  ppath.los(Range(np, n - i1), joker) =
5544  ppath_array[i].los(Range(i1, n - i1), joker);
5545  ppath.nreal[Range(np, n - i1)] = ppath_array[i].nreal[Range(i1, n - i1)];
5546  ppath.ngroup[Range(np, n - i1)] =
5547  ppath_array[i].ngroup[Range(i1, n - i1)];
5548  ppath.lstep[Range(np - i1, n - 1)] = ppath_array[i].lstep;
5549 
5550  // Grid positions must be handled by a loop
5551  for (Index j = i1; j < n; j++) {
5552  ppath.gp_p[np + j - i1] = ppath_array[i].gp_p[j];
5553  }
5554  if (atmosphere_dim >= 2) {
5555  for (Index j = i1; j < n; j++) {
5556  ppath.gp_lat[np + j - i1] = ppath_array[i].gp_lat[j];
5557  }
5558  }
5559  if (atmosphere_dim == 3) {
5560  for (Index j = i1; j < n; j++) {
5561  ppath.gp_lon[np + j - i1] = ppath_array[i].gp_lon[j];
5562  }
5563  }
5564 
5565  // Increase number of points done
5566  np += n - i1;
5567  }
5568 
5569  // Field just included once:
5570  // end_pos/los/lspace from first path_step (extracted above):
5571  ppath.end_lstep = end_lstep;
5572  ppath.end_pos = end_pos;
5573  ppath.end_los = end_los;
5574  // Constant, background and start_pos/los from last path_step:
5575  ppath.constant = ppath_step.constant;
5576  ppath.background = ppath_step.background;
5577  ppath.start_pos = ppath_step.start_pos;
5578  ppath.start_los = ppath_step.start_los;
5579  ppath.start_lstep = ppath_step.start_lstep;
5580  }
5581 }
5582 
5583 
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
void latlon_at_aa(Numeric &lat2, Numeric &lon2, const Numeric &lat1, const Numeric &lon1, const Numeric &aa, const Numeric &ddeg)
latlon_at_aa
Definition: geodetic.cc:883
void get_refr_index_2d(Workspace &ws, Numeric &refr_index_air, Numeric &refr_index_air_group, const Agenda &refr_index_air_agenda, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView refellipsoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, const Numeric &r, const Numeric &lat)
get_refr_index_2d
Definition: refraction.cc:264
ArrayOfGridPos gp_lat
Index position with respect to the latitude grid.
Definition: ppath.h:84
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 Vector &f_grid, const Vector &refellipsoid, const Matrix &z_surface, const Index &cloudbox_on, const ArrayOfIndex &cloudbox_limits, const Vector &rte_pos, const Vector &rte_los, const Numeric &ppath_lmax, const Numeric &ppath_lraytrace, const bool &ppath_inside_cloudbox_do, const Verbosity &verbosity)
This is the core for the WSM ppathStepByStep.
Definition: ppath.cc:5206
bool is_lon_cyclic(ConstVectorView grid, const Numeric &epsilon)
Check if the given longitude grid is cyclic.
Definition: logic.cc:369
void get_refr_index_3d(Workspace &ws, Numeric &refr_index_air, Numeric &refr_index_air_group, const Agenda &refr_index_air_agenda, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstVectorView refellipsoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, const Numeric &r, const Numeric &lat, const Numeric &lon)
Definition: refraction.cc:357
Numeric geompath_za_at_r(const Numeric &ppc, const Numeric &a_za, const Numeric &r)
Calculates the zenith angle for a given radius along a geometrical propagation path.
Definition: ppath.cc:103
const Numeric LON_NOT_FOUND
Definition: ppath.cc:78
Numeric constant
The propagation path constant (only used for 1D)
Definition: ppath.h:54
#define dmin(a, b)
The Agenda class.
Definition: agenda_class.h:44
Index nelem() const
Number of elements.
Definition: array.h:195
Declarations having to do with the four output streams.
void zaaa2cart(Numeric &dx, Numeric &dy, Numeric &dz, const Numeric &za, const Numeric &aa)
Converts zenith and azimuth angles to a cartesian unit vector.
Definition: ppath.cc:347
Contains the code to determine roots of polynomials.
Numeric refraction_ppc(const Numeric &r, const Numeric &za, const Numeric &refr_index_air)
Calculates the propagation path constant for cases with refraction.
Definition: ppath.cc:506
void ppath_copy(Ppath &ppath1, const Ppath &ppath2, const Index &ncopy)
Copy the content in ppath2 to ppath1.
Definition: ppath.cc:1515
Matrix los
Line-of-sight at each ppath point.
Definition: ppath.h:66
void ppath_start_stepping(Ppath &ppath, const Index &atmosphere_dim, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstVectorView refellipsoid, ConstMatrixView z_surface, const Index &cloudbox_on, const ArrayOfIndex &cloudbox_limits, const bool &ppath_inside_cloudbox_do, ConstVectorView rte_pos, ConstVectorView rte_los, const Verbosity &verbosity)
Initiates a Ppath structure for calculation of a path with ppath_step.
Definition: ppath.cc:4495
void raytrace_3d_linear_basic(Workspace &ws, Array< Numeric > &r_array, Array< Numeric > &lat_array, Array< Numeric > &lon_array, Array< Numeric > &za_array, Array< Numeric > &aa_array, Array< Numeric > &l_array, Array< Numeric > &n_array, Array< Numeric > &ng_array, Index &endface, ConstVectorView refellipsoid, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, const Numeric &lmax, const Agenda &refr_index_air_agenda, const Numeric &lraytrace, const Numeric &lat1, const Numeric &lat3, const Numeric &lon5, const Numeric &lon6, const Numeric &rsurface15, const Numeric &rsurface35, const Numeric &rsurface36, const Numeric &rsurface16, const Numeric &r15a, const Numeric &r35a, const Numeric &r36a, const Numeric &r16a, const Numeric &r15b, const Numeric &r35b, const Numeric &r36b, const Numeric &r16b, Numeric r, Numeric lat, Numeric lon, Numeric za, Numeric aa)
Performs ray tracing for 3D with linear steps.
Definition: ppath.cc:4095
The Vector class.
Definition: matpackI.h:860
#define abs(x)
Numeric geompath_r_at_l(const Numeric &ppc, const Numeric &l)
Calculates the radius for a distance from the tangent point.
Definition: ppath.cc:182
void do_gridcell_2d_byltest(Vector &r_v, Vector &lat_v, Vector &za_v, Numeric &lstep, Index &endface, const Numeric &r_start0, const Numeric &lat_start0, const Numeric &za_start, const Numeric &l_start, const Index &icall, const Numeric &ppc, const Numeric &lmax, const Numeric &lat1, const Numeric &lat3, const Numeric &r1a, const Numeric &r3a, const Numeric &r3b, const Numeric &r1b, const Numeric &rsurface1, const Numeric &rsurface3)
Works as do_gridcell_3d_byltest, but downscaled to 2D.
Definition: ppath.cc:2439
Numeric interp(ConstVectorView itw, ConstVectorView a, const GridPos &tc)
Red 1D Interpolate.
Vector end_pos
End position.
Definition: ppath.h:72
void raytrace_2d_linear_basic(Workspace &ws, Array< Numeric > &r_array, Array< Numeric > &lat_array, Array< Numeric > &za_array, Array< Numeric > &l_array, Array< Numeric > &n_array, Array< Numeric > &ng_array, Index &endface, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView refellipsoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, const Numeric &lmax, const Agenda &refr_index_air_agenda, const Numeric &lraytrace, const Numeric &lat1, const Numeric &lat3, const Numeric &rsurface1, const Numeric &rsurface3, const Numeric &r1a, const Numeric &r3a, const Numeric &r3b, const Numeric &r1b, Numeric r, Numeric lat, Numeric za)
Performs ray tracing for 2D with linear steps.
Definition: ppath.cc:3738
Index dim
Atmospheric dimensionality.
Definition: ppath.h:50
const Numeric DEG2RAD
void find_tanpoint(Index &it, const Ppath &ppath)
Identifies the tangent point of a propagation path.
Definition: ppath.cc:525
void ppath_step_refr_2d(Workspace &ws, Ppath &ppath, ConstVectorView p_grid, ConstVectorView lat_grid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, ConstVectorView refellipsoid, ConstVectorView z_surface, const Numeric &lmax, const Agenda &refr_index_air_agenda, const String &rtrace_method, const Numeric &lraytrace)
Calculates 2D propagation path steps, with refraction, using a simple and fast ray tracing scheme...
Definition: ppath.cc:3920
void ppath_step_agendaExecute(Workspace &ws, Ppath &ppath_step, const Numeric ppath_lmax, const Numeric ppath_lraytrace, const Vector &f_grid, const Agenda &input_agenda)
Definition: auto_md.cc:24814
The range class.
Definition: matpackI.h:160
Vector lstep
The length between ppath points.
Definition: ppath.h:70
void rotationmat3D(Matrix &R, ConstVectorView vrot, const Numeric &a)
Creates a 3D rotation matrix.
Definition: ppath.cc:377
void ppath_step_geom_3d(Ppath &ppath, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstVectorView refellipsoid, ConstMatrixView z_surface, const Numeric &lmax)
Calculates 3D geometrical propagation path steps.
Definition: ppath.cc:3270
void cart2zaaa(Numeric &za, Numeric &aa, const Numeric &dx, const Numeric &dy, const Numeric &dz)
Converts a cartesian directional vector to zenith and azimuth.
Definition: ppath.cc:312
void ppath_end_2d(Ppath &ppath, ConstVectorView r_v, ConstVectorView lat_v, ConstVectorView za_v, ConstVectorView lstep, ConstVectorView n_v, ConstVectorView ng_v, ConstVectorView lat_grid, ConstMatrixView z_field, ConstVectorView refellipsoid, const Index &ip, const Index &ilat, const Index &endface, const Numeric &ppc)
Internal help function for 2D path calculations.
Definition: ppath.cc:1844
invlib::Vector< ArtsVector > Vector
invlib wrapper type for ARTS vectors.
Definition: oem.h:32
Numeric fractional_gp(const GridPos &gp)
fractional_gp
const Numeric RAD2DEG
void chk_rte_los(const Index &atmosphere_dim, ConstVectorView rte_los)
chk_rte_los
cmplx FADDEEVA() w(cmplx z, double relerr)
Definition: Faddeeva.cc:680
Matrix pos
The distance between start pos and the last position in pos.
Definition: ppath.h:64
void plevel_slope_3d(Numeric &c1, Numeric &c2, const Numeric &lat1, const Numeric &lat3, const Numeric &lon5, const Numeric &lon6, const Numeric &r15, const Numeric &r35, const Numeric &r36, const Numeric &r16, const Numeric &lat, const Numeric &lon, const Numeric &aa)
Definition: ppath.cc:1084
Vector ngroup
The group index of refraction.
Definition: ppath.h:80
void raytrace_1d_linear_basic(Workspace &ws, Array< Numeric > &r_array, Array< Numeric > &lat_array, Array< Numeric > &za_array, Array< Numeric > &l_array, Array< Numeric > &n_array, Array< Numeric > &ng_array, Index &endface, ConstVectorView p_grid, ConstVectorView refellipsoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, const Numeric &lmax, const Agenda &refr_index_air_agenda, const Numeric &lraytrace, const Numeric &rsurface, const Numeric &r1, const Numeric &r3, Numeric r, Numeric lat, Numeric za)
Performs ray tracing for 1D with linear steps.
Definition: ppath.cc:3432
Numeric refell2d(ConstVectorView refellipsoid, ConstVectorView lat_grid, const GridPos gp)
refell2d
Definition: geodetic.cc:1174
#define min(a, b)
Index gridpos2gridrange(const GridPos &gp, const bool &upwards)
gridpos2gridrange
Vector start_pos
Start position.
Definition: ppath.h:58
Vector r
Radius of each ppath point.
Definition: ppath.h:68
Numeric rslope_crossing3d(const Numeric &rp, const Numeric &za, const Numeric &r0, Numeric c1, Numeric c2)
3D version of rslope_crossing2d.
Definition: ppath.cc:1236
const Numeric LAT_NOT_FOUND
Definition: ppath.cc:77
void get_refr_index_1d(Workspace &ws, Numeric &refr_index_air, Numeric &refr_index_air_group, const Agenda &refr_index_air_agenda, ConstVectorView p_grid, ConstVectorView refellipsoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, const Numeric &r)
get_refr_index_1d
Definition: refraction.cc:184
Numeric geompath_lat_at_za(const Numeric &za0, const Numeric &lat0, const Numeric &za)
Calculates the latitude for a given zenith angle along a geometrical propagation path.
Definition: ppath.cc:148
bool is_gridpos_at_index_i(const GridPos &gp, const Index &i, const bool &strict)
is_gridpos_at_index_i
void resolve_lon(Numeric &lon, const Numeric &lon5, const Numeric &lon6)
Resolves which longitude angle that shall be used.
Definition: ppath.cc:515
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:51
Numeric geometrical_ppc(const Numeric &r, const Numeric &za)
Calculates the propagation path constant for pure geometrical calculations.
Definition: ppath.cc:96
Structure to store a grid position.
Definition: interpolation.h:73
A constant view of a Tensor4.
Definition: matpackIV.h:133
Index ppath_what_background(const Ppath &ppath)
Returns the case number for the radiative background.
Definition: ppath.cc:1494
Numeric end_lstep
The distance between end pos and the first position in pos.
Definition: ppath.h:76
Numeric sign(const Numeric &x)
sign
Definition: math_funcs.cc:423
const Numeric L_NOT_FOUND
Definition: ppath.cc:76
This file contains the definition of Array.
void gridpos_check_fd(GridPos &gp)
gridpos_check_fd
void plevel_slope_2d(Numeric &c1, ConstVectorView lat_grid, ConstVectorView refellipsoid, ConstVectorView z_surf, const GridPos &gp, const Numeric &za)
Calculates the radial slope of the surface or a pressure level for 2D.
Definition: ppath.cc:595
Vector end_los
End line-of-sight.
Definition: ppath.h:74
void diff_za_aa(Numeric &dza, Numeric &daa, const Numeric &za0, const Numeric &aa0, const Numeric &za, const Numeric &aa)
Takes the difference of zenith and azimuth angles.
Definition: ppath.cc:444
Index ncols() const
Returns the number of columns.
Definition: matpackI.cc:432
void ppath_set_background(Ppath &ppath, const Index &case_nr)
Sets the background field of a Ppath structure.
Definition: ppath.cc:1467
The Tensor3 class.
Definition: matpackIII.h:339
String background
Radiative background.
Definition: ppath.h:56
Numeric rslope_crossing2d(const Numeric &rp, const Numeric &za, const Numeric &r0, Numeric c1)
Calculates the angular distance to a crossing with a level having a radial slope. ...
Definition: ppath.cc:742
void do_gridrange_1d(Vector &r_v, Vector &lat_v, Vector &za_v, Numeric &lstep, Index &endface, const Numeric &r_start0, const Numeric &lat_start, const Numeric &za_start, const Numeric &ppc, const Numeric &lmax, const Numeric &ra, const Numeric &rb, const Numeric &rsurface)
Calculates the geometrical path through a 1D grid range.
Definition: ppath.cc:2300
#define DEBUG_ONLY(...)
Definition: debug.h:36
void geompath_from_r1_to_r2(Vector &r, Vector &lat, Vector &za, Numeric &lstep, const Numeric &ppc, const Numeric &r1, const Numeric &lat1, const Numeric &za1, const Numeric &r2, const bool &tanpoint, const Numeric &lmax)
Determines radii, latitudes and zenith angles between two points of a propagation path...
Definition: ppath.cc:236
_CS_string_type str() const
Definition: sstream.h:491
void ppath_end_1d(Ppath &ppath, ConstVectorView r_v, ConstVectorView lat_v, ConstVectorView za_v, ConstVectorView lstep, ConstVectorView n_v, ConstVectorView ng_v, ConstVectorView z_field, ConstVectorView refellipsoid, const Index &ip, const Index &endface, const Numeric &ppc)
Internal help function for 1D path calculations.
Definition: ppath.cc:1670
const Numeric ANGTOL
Width of zenith and nadir directions.
Definition: ppath.h:108
void ppath_end_3d(Ppath &ppath, ConstVectorView r_v, ConstVectorView lat_v, ConstVectorView lon_v, ConstVectorView za_v, ConstVectorView aa_v, ConstVectorView lstep, ConstVectorView n_v, ConstVectorView ng_v, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstVectorView refellipsoid, const Index &ip, const Index &ilat, const Index &ilon, const Index &endface, const Numeric &ppc)
Internal help function for 3D path calculations.
Definition: ppath.cc:2141
void chk_rte_pos(const Index &atmosphere_dim, ConstVectorView rte_pos, const bool &is_rte_pos2)
chk_rte_pos
void do_gridcell_3d_byltest(Vector &r_v, Vector &lat_v, Vector &lon_v, Vector &za_v, Vector &aa_v, Numeric &lstep, Index &endface, const Numeric &r_start0, const Numeric &lat_start0, const Numeric &lon_start0, const Numeric &za_start, const Numeric &aa_start, const Numeric &l_start, const Index &icall, const Numeric &ppc, const Numeric &lmax, const Numeric &lat1, const Numeric &lat3, const Numeric &lon5, const Numeric &lon6, const Numeric &r15a, const Numeric &r35a, const Numeric &r36a, const Numeric &r16a, const Numeric &r15b, const Numeric &r35b, const Numeric &r36b, const Numeric &r16b, const Numeric &rsurface15, const Numeric &rsurface35, const Numeric &rsurface36, const Numeric &rsurface16)
See ATD for a description of the algorithm.
Definition: ppath.cc:2828
Declarations for agendas.
void ppath_init_structure(Ppath &ppath, const Index &atmosphere_dim, const Index &np)
Initiates a Ppath structure to hold the given number of points.
Definition: ppath.cc:1426
void cart2poslos(Numeric &r, Numeric &lat, Numeric &za, const Numeric &x, const Numeric &z, const Numeric &dx, const Numeric &dz, const Numeric &ppc, const Numeric &lat0, const Numeric &za0)
cart2poslos
Definition: geodetic.cc:113
Numeric start_lstep
Length between sensor and atmospheric boundary.
Definition: ppath.h:62
#define beta
#define CREATE_OUT1
Definition: messages.h:205
void add_za_aa(Numeric &za, Numeric &aa, const Numeric &za0, const Numeric &aa0, const Numeric &dza, const Numeric &daa)
Adds up zenith and azimuth angles.
Definition: ppath.cc:406
void ppath_append(Ppath &ppath1, const Ppath &ppath2)
Combines two Ppath structures.
Definition: ppath.cc:1581
void gridpos(ArrayOfGridPos &gp, ConstVectorView old_grid, ConstVectorView new_grid, const Numeric &extpolfac)
Set up a grid position Array.
Index first_pos_before_altitude(const Ppath &p, const Numeric &alt)
Determines ppath position just below an altitude.
Definition: ppath.cc:537
void ppath_start_1d(Numeric &r_start, Numeric &lat_start, Numeric &za_start, Index &ip, const Ppath &ppath)
Internal help function for 1D path calculations.
Definition: ppath.cc:1641
const Joker joker
const Numeric R_NOT_FOUND
Definition: ppath.cc:75
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
The Matrix class.
Definition: matpackI.h:1193
Vector nreal
The real part of the refractive index at each path position.
Definition: ppath.h:78
Numeric plevel_angletilt(const Numeric &r, const Numeric &c1)
Calculates the angular tilt of the surface or a pressure level.
Definition: ppath.cc:632
const Numeric RTOL
Definition: ppath.cc:64
void refr_gradients_3d(Workspace &ws, Numeric &refr_index_air, Numeric &refr_index_air_group, Numeric &dndr, Numeric &dndlat, Numeric &dndlon, const Agenda &refr_index_air_agenda, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstVectorView refellipsoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, const Numeric &r, const Numeric &lat, const Numeric &lon)
refr_gradients_3d
Definition: refraction.cc:637
Numeric refell2r(ConstVectorView refellipsoid, const Numeric &lat)
refell2r
Definition: geodetic.cc:1135
void error_if_limb_ppath(const Ppath &ppath)
Throws an error if ppath altitudes not are strictly increasing or decreasing.
Definition: ppath.cc:555
void gridpos_copy(GridPos &gp_new, const GridPos &gp_old)
gridpos_copy
bool is_los_downwards(const Numeric &za, const Numeric &tilt)
Determines if a line-of-sight is downwards compared to the angular tilt of the surface or a pressure ...
Definition: ppath.cc:638
#define dx
Header file for special_interp.cc.
Propagation path structure and functions.
void ppath_step_geom_2d(Ppath &ppath, ConstVectorView lat_grid, ConstMatrixView z_field, ConstVectorView refellipsoid, ConstVectorView z_surface, const Numeric &lmax)
Calculates 2D geometrical propagation path steps.
Definition: ppath.cc:2736
void ppath_step_geom_1d(Ppath &ppath, ConstVectorView z_field, ConstVectorView refellipsoid, const Numeric &z_surface, const Numeric &lmax)
Calculates 1D geometrical propagation path steps.
Definition: ppath.cc:2372
void mult(ComplexVectorView y, const ConstComplexMatrixView &M, const ConstComplexVectorView &x)
Matrix-Vector Multiplication.
Definition: complex.cc:1579
void ppath_start_3d(Numeric &r_start, Numeric &lat_start, Numeric &lon_start, Numeric &za_start, Numeric &aa_start, Index &ip, Index &ilat, Index &ilon, Numeric &lat1, Numeric &lat3, Numeric &lon5, Numeric &lon6, Numeric &r15a, Numeric &r35a, Numeric &r36a, Numeric &r16a, Numeric &r15b, Numeric &r35b, Numeric &r36b, Numeric &r16b, Numeric &rsurface15, Numeric &rsurface35, Numeric &rsurface36, Numeric &rsurface16, Ppath &ppath, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstVectorView refellipsoid, ConstMatrixView z_surface)
Internal help function for 3D path calculations.
Definition: ppath.cc:1951
void r_crossing_3d(Numeric &lat, Numeric &lon, Numeric &l, const Numeric &r_hit, const Numeric &r_start, const Numeric &lat_start, const Numeric &lon_start, const Numeric &za_start, const Numeric &ppc, const Numeric &x, const Numeric &y, const Numeric &z, const Numeric &dx, const Numeric &dy, const Numeric &dz)
Calculates where a 3D LOS crosses the specified radius.
Definition: ppath.cc:1360
void r_crossing_2d(Numeric &lat, Numeric &l, const Numeric &r_hit, const Numeric &r_start, const Numeric &lat_start, const Numeric &za_start, const Numeric &ppc)
Calculates where a 2D LOS crosses the specified radius.
Definition: ppath.cc:671
Header file for logic.cc.
Numeric rsurf_at_latlon(const Numeric &lat1, const Numeric &lat3, const Numeric &lon5, const Numeric &lon6, const Numeric &r15, const Numeric &r35, const Numeric &r36, const Numeric &r16, const Numeric &lat, const Numeric &lon)
Determines the radius of a pressure level or the surface given the radius at the corners of a 3D grid...
Definition: ppath.cc:1055
This can be used to make arrays out of anything.
Definition: array.h:40
void adjust_los(VectorView los, const Index &atmosphere_dim)
Ensures that the zenith and azimuth angles of a line-of-sight vector are inside defined ranges...
Definition: rte.cc:140
Numeric geompath_r_at_lat(const Numeric &ppc, const Numeric &lat0, const Numeric &za0, const Numeric &lat)
Calculates the radius for a given latitude.
Definition: ppath.cc:200
void ppath_step_refr_1d(Workspace &ws, Ppath &ppath, ConstVectorView p_grid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, ConstVectorView refellipsoid, const Numeric &z_surface, const Numeric &lmax, const Agenda &refr_index_air_agenda, const String &rtrace_method, const Numeric &lraytrace)
Calculates 1D propagation path steps including effects of refraction.
Definition: ppath.cc:3576
void rte_pos2gridpos(GridPos &gp_p, GridPos &gp_lat, GridPos &gp_lon, const Index &atmosphere_dim, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstVectorView rte_pos)
Converts a geographical position (rte_pos) to grid positions for p, lat and lon.
void cart2pol(Numeric &r, Numeric &lat, const Numeric &x, const Numeric &z, const Numeric &lat0, const Numeric &za0)
cart2pol
Definition: geodetic.cc:74
void resize(Index n)
Resize function.
Definition: matpackI.cc:404
#define max(a, b)
A constant view of a Tensor3.
Definition: matpackIII.h:132
A constant view of a Vector.
Definition: matpackI.h:476
Index np
Number of points describing the ppath.
Definition: ppath.h:52
ArrayOfGridPos gp_lon
Index position with respect to the longitude grid.
Definition: ppath.h:86
void ppath_start_2d(Numeric &r_start, Numeric &lat_start, Numeric &za_start, Index &ip, Index &ilat, Numeric &lat1, Numeric &lat3, Numeric &r1a, Numeric &r3a, Numeric &r3b, Numeric &r1b, Numeric &rsurface1, Numeric &rsurface3, Ppath &ppath, ConstVectorView lat_grid, ConstMatrixView z_field, ConstVectorView refellipsoid, ConstVectorView z_surface)
Internal help function for 2D path calculations.
Definition: ppath.cc:1734
void refr_gradients_1d(Workspace &ws, Numeric &refr_index_air, Numeric &refr_index_air_group, Numeric &dndr, const Agenda &refr_index_air_agenda, ConstVectorView p_grid, ConstVectorView refellipsoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, const Numeric &r)
refr_gradients_1d
Definition: refraction.cc:454
A constant view of a Matrix.
Definition: matpackI.h:982
void refr_gradients_2d(Workspace &ws, Numeric &refr_index_air, Numeric &refr_index_air_group, Numeric &dndr, Numeric &dndlat, const Agenda &refr_index_air_agenda, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView refellipsoid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, const Numeric &r, const Numeric &lat)
refr_gradients_2d
Definition: refraction.cc:531
Vector start_los
Start line-of-sight.
Definition: ppath.h:60
Workspace class.
Definition: workspace_ng.h:40
void cart2sph(Numeric &r, Numeric &lat, Numeric &lon, const Numeric &x, const Numeric &y, const Numeric &z, const Numeric &lat0, const Numeric &lon0, const Numeric &za0, const Numeric &aa0)
cart2sph
Definition: geodetic.cc:563
#define q
Refraction functions.
Numeric geompath_r_at_za(const Numeric &ppc, const Numeric &za)
Calculates the zenith angle for a given radius along a geometrical propagation path.
Definition: ppath.cc:141
Numeric rsurf_at_lat(const Numeric &lat1, const Numeric &lat3, const Numeric &r1, const Numeric &r3, const Numeric &lat)
Determines the radius of a pressure level or the surface given the radius at the corners of a 2D grid...
Definition: ppath.cc:587
The structure to describe a propagation path and releated quantities.
Definition: ppath.h:48
Header file for helper functions for OpenMP.
void ppath_step_refr_3d(Workspace &ws, Ppath &ppath, ConstVectorView p_grid, ConstVectorView lat_grid, ConstVectorView lon_grid, ConstTensor3View z_field, ConstTensor3View t_field, ConstTensor4View vmr_field, ConstVectorView f_grid, ConstVectorView refellipsoid, ConstMatrixView z_surface, const Numeric &lmax, const Agenda &refr_index_air_agenda, const String &rtrace_method, const Numeric &lraytrace)
Calculates 3D propagation path steps, with refraction, using a simple and fast ray tracing scheme...
Definition: ppath.cc:4335
void plevel_crossing_2d(Numeric &r, Numeric &lat, Numeric &l, const Numeric &r_start0, const Numeric &lat_start, const Numeric &za_start, const Numeric &ppc, const Numeric &lat1, const Numeric &lat3, const Numeric &r1, const Numeric &r3, const bool &above)
Handles the crossing with a geometric ppaths step and a atmospheric grid box level for 2D...
Definition: ppath.cc:871
const Numeric POLELAT
Size of north and south poles.
Definition: ppath.h:97
int poly_root_solve(Matrix &roots, Vector &coeffs)
Definition: poly_roots.cc:90
void interpweights(VectorView itw, const GridPos &tc)
Red 1D interpolation weights.
const Numeric LACC
Definition: ppath.cc:72
void poslos2cart(Numeric &x, Numeric &z, Numeric &dx, Numeric &dz, const Numeric &r, const Numeric &lat, const Numeric &za)
poslos2cart
Definition: geodetic.cc:355
Index nrows() const
Returns the number of rows.
Definition: matpackI.cc:429
ArrayOfGridPos gp_p
Index position with respect to the pressure grid.
Definition: ppath.h:82
Numeric geompath_l_at_r(const Numeric &ppc, const Numeric &r)
Calculates the length from the tangent point for the given radius.
Definition: ppath.cc:158
void gridpos_force_end_fd(GridPos &gp, const Index &n)
gridpos_force_end_fd
This file contains the definition of String, the ARTS string class.
Declaration of functions in rte.cc.
Numeric sqrt(const Rational r)
Square root.
Definition: rational.h:620
void resize(Index r, Index c)
Resize function.
Definition: matpackI.cc:1056