ARTS  2.3.1285(git:92a29ea9-dirty)
jacobian.cc
Go to the documentation of this file.
1 /* Copyright (C) 2004-2012 Mattias Ekstrom <ekstrom@rss.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 
26 #include "jacobian.h"
27 #include "arts.h"
28 #include "auto_md.h"
29 #include "check_input.h"
30 #include "global_data.h"
31 #include "lin_alg.h"
32 #include "physics_funcs.h"
33 #include "rte.h"
34 #include "special_interp.h"
35 
36 extern const Numeric NAT_LOG_TEN;
37 extern const Numeric PI;
38 
39 extern const String ABSSPECIES_MAINTAG;
40 extern const String SCATSPECIES_MAINTAG;
41 extern const String TEMPERATURE_MAINTAG;
42 extern const String WIND_MAINTAG;
43 extern const String MAGFIELD_MAINTAG;
44 extern const String FLUX_MAINTAG;
45 extern const String PROPMAT_SUBSUBTAG;
46 extern const String ELECTRONS_MAINTAG;
47 extern const String PARTICULATES_MAINTAG;
48 extern const String POLYFIT_MAINTAG;
49 extern const String SINEFIT_MAINTAG;
50 
51 ostream& operator<<(ostream& os, const RetrievalQuantity& ot) {
52  return os << "\n Main tag = " << ot.MainTag()
53  << "\n Sub tag = " << ot.Subtag()
54  << "\n Mode = " << ot.Mode()
55  << "\n Analytical = " << ot.Analytical();
56 }
57 
59  bool& any_affine,
60  const ArrayOfRetrievalQuantity& jqs,
61  const bool& before_affine) {
62  jis.resize(jqs.nelem());
63 
64  any_affine = false;
65 
66  // Indices before affine transformation
67  if (before_affine) {
68  for (Index i = 0; i < jqs.nelem(); ++i) {
69  jis[i] = ArrayOfIndex(2);
70  if (i > 0) {
71  jis[i][0] = jis[i - 1][1] + 1;
72  } else {
73  jis[i][0] = 0;
74  }
75  const RetrievalQuantity& jq = jqs[i];
76  jis[i][1] = jis[i][0] + jq.nelem() - 1;
77  if (jq.HasAffine()) {
78  any_affine = true;
79  }
80  }
81  }
82 
83  // After affine transformation
84  else {
85  for (Index i = 0; i < jqs.nelem(); ++i) {
86  jis[i] = ArrayOfIndex(2);
87  if (i > 0) {
88  jis[i][0] = jis[i - 1][1] + 1;
89  } else {
90  jis[i][0] = 0;
91  }
92  const RetrievalQuantity& jq = jqs[i];
93  if (jq.HasAffine()) {
94  jis[i][1] = jis[i][0] + jq.TransformationMatrix().ncols() - 1;
95  any_affine = true;
96  } else {
97  jis[i][1] = jis[i][0] + jq.nelem() - 1;
98  }
99  }
100  }
101 }
102 
103 void transform_jacobian(Matrix& jacobian,
104  const Vector x,
105  const ArrayOfRetrievalQuantity& jqs) {
106  // Range indices without affine trans
108  bool any_affine;
109  //
110  jac_ranges_indices(jis, any_affine, jqs, true);
111 
112  Vector x_t(x);
113  transform_x_back(x_t, jqs, false);
114 
115  // Apply functional transformations
116  for (Index i = 0; i < jqs.nelem(); ++i) {
117  const RetrievalQuantity& jq = jqs[i];
118  const String tfun = jq.TransformationFunc();
119  // Remember to add new functions also to transform_jacobian and transform_x_back
120  if (tfun == "") {
121  // Nothing to do
122  } else if (tfun == "log") {
123  for (Index c = jis[i][0]; c <= jis[i][1]; ++c) {
124  jacobian(joker, c) *= exp(x_t[c]);
125  }
126  } else if (tfun == "log10") {
127  for (Index c = jis[i][0]; c <= jis[i][1]; ++c) {
128  jacobian(joker, c) *= NAT_LOG_TEN * pow(10.0, x_t[c]);
129  }
130  } else if (tfun == "atanh") {
131  const Vector& pars = jq.TFuncParameters();
132  for (Index c = jis[i][0]; c <= jis[i][1]; ++c) {
133  jacobian(joker, c) *=
134  2 * (pars[1] - pars[0]) / pow(exp(-x_t[c]) + exp(x_t[c]), 2.0);
135  }
136  } else {
137  assert(0);
138  }
139  }
140 
141  // Apply affine transformations
142  if (any_affine) {
143  ArrayOfArrayOfIndex jis_t;
144  jac_ranges_indices(jis_t, any_affine, jqs);
145 
146  Matrix jacobian_t(jacobian.nrows(), jis_t.back()[1] + 1);
147 
148  for (Index i = 0; i < jqs.nelem(); ++i) {
149  const RetrievalQuantity& jq = jqs[i];
150  Index col_start = jis[i][0];
151  Index col_extent = jis[i][1] - jis[i][0] + 1;
152  Range col_range(col_start, col_extent);
153  Index col_start_t = jis_t[i][0];
154  Index col_extent_t = jis_t[i][1] - jis_t[i][0] + 1;
155  Range col_range_t(col_start_t, col_extent_t);
156  if (jq.HasAffine()) {
157  mult(jacobian_t(joker, col_range_t),
158  jacobian(joker, col_range),
159  jq.TransformationMatrix());
160  } else {
161  jacobian_t(joker, col_range_t) = jacobian(joker, col_range);
162  }
163  }
164  swap(jacobian_t, jacobian);
165  }
166 }
167 
169  // Range indices without affine trans
171  bool any_affine;
172  //
173  jac_ranges_indices(jis, any_affine, jqs, true);
174 
175  // Apply functional transformations
176  for (Index i = 0; i < jqs.nelem(); ++i) {
177  const RetrievalQuantity& jq = jqs[i];
178  const String tfun = jq.TransformationFunc();
179  // Remember to add new functions also to transform_jacobian and transform_x_back
180  if (tfun == "") {
181  // Nothing to do
182  } else if (tfun == "log") {
183  const Vector& pars = jq.TFuncParameters();
184  for (Index r = jis[i][0]; r <= jis[i][1]; ++r) {
185  if (x[r] <= pars[0]) {
186  ostringstream os;
187  os << "log-transformation selected for retrieval quantity with\n"
188  << "index " << i << " (0-based), but at least one value <= z_min\n"
189  << "found for this quantity. This is not allowed.";
190  throw std::runtime_error(os.str());
191  }
192  x[r] = log(x[r] - pars[0]);
193  }
194  } else if (tfun == "log10") {
195  const Vector& pars = jq.TFuncParameters();
196  for (Index r = jis[i][0]; r <= jis[i][1]; ++r) {
197  if (x[r] <= 0) {
198  ostringstream os;
199  os << "log10-transformation selected for retrieval quantity with\n"
200  << "index " << i << " (0-based), but at least one value <= z_min\n"
201  << "found for this quantity. This is not allowed.";
202  throw std::runtime_error(os.str());
203  }
204  x[r] = log10(x[r] - pars[0]);
205  }
206  } else if (tfun == "atanh") {
207  const Vector& pars = jq.TFuncParameters();
208  for (Index r = jis[i][0]; r <= jis[i][1]; ++r) {
209  if (x[r] <= pars[0]) {
210  ostringstream os;
211  os << "atanh-transformation selected for retrieval quantity with\n"
212  << "index " << i << " (0-based), but at least one value <= z_min\n"
213  << "found for this quantity. This is not allowed.";
214  throw std::runtime_error(os.str());
215  }
216  if (x[r] >= pars[1]) {
217  ostringstream os;
218  os << "atanh-transformation selected for retrieval quantity with\n"
219  << "index " << i << " (0-based), but at least one value is\n"
220  << ">= z_max. This is not allowed.";
221  throw std::runtime_error(os.str());
222  }
223  x[r] = atanh(2 * (x[r] - pars[0]) / (pars[1] - pars[0]) - 1);
224  }
225  } else {
226  assert(0);
227  }
228  }
229 
230  // Apply affine transformations
231  if (any_affine) {
232  ArrayOfArrayOfIndex jis_t;
233  jac_ranges_indices(jis_t, any_affine, jqs);
234 
235  Vector x_t(jis_t.back()[1] + 1);
236 
237  for (Index i = 0; i < jqs.nelem(); ++i) {
238  const RetrievalQuantity& jq = jqs[i];
239  Index col_start = jis[i][0];
240  Index col_extent = jis[i][1] - jis[i][0] + 1;
241  Range col_range(col_start, col_extent);
242  Index col_start_t = jis_t[i][0];
243  Index col_extent_t = jis_t[i][1] - jis_t[i][0] + 1;
244  Range col_range_t(col_start_t, col_extent_t);
245  if (jq.HasAffine()) {
246  Vector t(x[col_range]);
247  t -= jq.OffsetVector();
248  mult(x_t[col_range_t], transpose(jq.TransformationMatrix()), t);
249  } else {
250  x_t[col_range_t] = x[col_range];
251  }
252  }
253  swap(x, x_t);
254  }
255 }
256 
258  const ArrayOfRetrievalQuantity& jqs,
259  bool revert_functional_transforms) {
260  // Range indices without affine trans
262  bool any_affine;
263  //
264  jac_ranges_indices(jis, any_affine, jqs, true);
265 
266  // Revert affine transformations
267  // Apply affine transformations
268  if (any_affine) {
269  ArrayOfArrayOfIndex jis_t;
270  jac_ranges_indices(jis_t, any_affine, jqs);
271 
272  Vector x(jis.back()[1] + 1);
273 
274  for (Index i = 0; i < jqs.nelem(); ++i) {
275  const RetrievalQuantity& jq = jqs[i];
276  Index col_start = jis[i][0];
277  Index col_extent = jis[i][1] - jis[i][0] + 1;
278  Range col_range(col_start, col_extent);
279  Index col_start_t = jis_t[i][0];
280  Index col_extent_t = jis_t[i][1] - jis_t[i][0] + 1;
281  Range col_range_t(col_start_t, col_extent_t);
282  if (jq.HasAffine()) {
283  mult(x[col_range], jq.TransformationMatrix(), x_t[col_range_t]);
284  x[col_range] += jq.OffsetVector();
285  } else {
286  x[col_range] = x_t[col_range_t];
287  }
288  }
289  swap(x_t, x);
290  }
291 
292  if (revert_functional_transforms) {
293  // Revert functional transformations
294  for (Index i = 0; i < jqs.nelem(); ++i) {
295  const RetrievalQuantity& jq = jqs[i];
296  const String tfun = jq.TransformationFunc();
297  // Remember to add new functions also to transform_jacobian and transform_x_back
298  if (tfun == "") {
299  // Nothing to do
300  } else if (tfun == "log") {
301  const Vector& pars = jq.TFuncParameters();
302  for (Index r = jis[i][0]; r <= jis[i][1]; ++r) {
303  x_t[r] = pars[0] + exp(x_t[r]);
304  }
305  } else if (tfun == "log10") {
306  const Vector& pars = jq.TFuncParameters();
307  for (Index r = jis[i][0]; r <= jis[i][1]; ++r) {
308  x_t[r] = pars[0] + pow(10.0, x_t[r]);
309  }
310  } else if (tfun == "atanh") {
311  const Vector& pars = jq.TFuncParameters();
312  for (Index r = jis[i][0]; r <= jis[i][1]; ++r) {
313  x_t[r] = pars[0] + ((pars[1] - pars[0]) / 2) * (1 + tanh(x_t[r]));
314  }
315  } else {
316  assert(0);
317  }
318  }
319  }
320 }
321 
322 /*===========================================================================
323  === Help sub-functions to handle analytical jacobians (in alphabetical order)
324  ===========================================================================*/
325 
326 // Small help function, to make the code below cleaner
328  ConstMatrixView diy_dq,
329  const Numeric& w) {
330  for (Index irow = 0; irow < diy_dx.nrows(); irow++) {
331  for (Index icol = 0; icol < diy_dx.ncols(); icol++) {
332  diy_dx(irow, icol) += w * diy_dq(irow, icol);
333  }
334  }
335 }
336 
338  const RetrievalQuantity& jacobian_quantity,
339  ConstTensor3View diy_dpath,
340  const Index& atmosphere_dim,
341  const Ppath& ppath,
342  ConstVectorView ppath_p) {
343  // If this is an integration target then diy_dx is just the sum of all in diy_dpath
344  if (jacobian_quantity.Integration()) {
345  diy_dx(0, joker, joker) = diy_dpath(0, joker, joker);
346  for (Index i = 1; i < diy_dpath.npages(); i++)
347  diy_dx(0, joker, joker) += diy_dpath(i, joker, joker);
348  return;
349  }
350 
351  assert(jacobian_quantity.Grids().nelem() == atmosphere_dim);
352 
353  // We want here an extrapolation to infinity ->
354  // extremly high extrapolation factor
355  const Numeric extpolfac = 1.0e99;
356 
357  if (ppath.np > 1) // Otherwise nothing to do here
358  {
359  // Pressure
360  Index nr1 = jacobian_quantity.Grids()[0].nelem();
361  ArrayOfGridPos gp_p(ppath.np);
362  if (nr1 > 1) {
363  p2gridpos(gp_p, jacobian_quantity.Grids()[0], ppath_p, extpolfac);
365  } else {
366  gp4length1grid(gp_p);
367  }
368 
369  // Latitude
370  Index nr2 = 1;
371  ArrayOfGridPos gp_lat;
372  if (atmosphere_dim > 1) {
373  gp_lat.resize(ppath.np);
374  nr2 = jacobian_quantity.Grids()[1].nelem();
375  if (nr2 > 1) {
376  gridpos(gp_lat,
377  jacobian_quantity.Grids()[1],
378  ppath.pos(joker, 1),
379  extpolfac);
380  jacobian_type_extrapol(gp_lat);
381  } else {
382  gp4length1grid(gp_lat);
383  }
384  }
385 
386  // Longitude
387  ArrayOfGridPos gp_lon;
388  if (atmosphere_dim > 2) {
389  gp_lon.resize(ppath.np);
390  if (jacobian_quantity.Grids()[2].nelem() > 1) {
391  gridpos(gp_lon,
392  jacobian_quantity.Grids()[2],
393  ppath.pos(joker, 2),
394  extpolfac);
395  jacobian_type_extrapol(gp_lon);
396  } else {
397  gp4length1grid(gp_lon);
398  }
399  }
400 
401  //- 1D
402  if (atmosphere_dim == 1) {
403  for (Index ip = 0; ip < ppath.np; ip++) {
404  if (gp_p[ip].fd[1] > 0) {
405  from_dpath_to_dx(diy_dx(gp_p[ip].idx, joker, joker),
406  diy_dpath(ip, joker, joker),
407  gp_p[ip].fd[1]);
408  }
409  if (gp_p[ip].fd[0] > 0) {
410  from_dpath_to_dx(diy_dx(gp_p[ip].idx + 1, joker, joker),
411  diy_dpath(ip, joker, joker),
412  gp_p[ip].fd[0]);
413  }
414  }
415  }
416 
417  //- 2D
418  else if (atmosphere_dim == 2) {
419  for (Index ip = 0; ip < ppath.np; ip++) {
420  Index ix = nr1 * gp_lat[ip].idx + gp_p[ip].idx;
421  // Low lat, low p
422  if (gp_lat[ip].fd[1] > 0 && gp_p[ip].fd[1] > 0)
423  from_dpath_to_dx(diy_dx(ix, joker, joker),
424  diy_dpath(ip, joker, joker),
425  gp_lat[ip].fd[1] * gp_p[ip].fd[1]);
426  // Low lat, high p
427  if (gp_lat[ip].fd[1] > 0 && gp_p[ip].fd[0] > 0)
428  from_dpath_to_dx(diy_dx(ix + 1, joker, joker),
429  diy_dpath(ip, joker, joker),
430  gp_lat[ip].fd[1] * gp_p[ip].fd[0]);
431  // High lat, low p
432  if (gp_lat[ip].fd[0] > 0 && gp_p[ip].fd[1] > 0)
433  from_dpath_to_dx(diy_dx(ix + nr1, joker, joker),
434  diy_dpath(ip, joker, joker),
435  gp_lat[ip].fd[0] * gp_p[ip].fd[1]);
436  // High lat, high p
437  if (gp_lat[ip].fd[0] > 0 && gp_p[ip].fd[0] > 0)
438  from_dpath_to_dx(diy_dx(ix + nr1 + 1, joker, joker),
439  diy_dpath(ip, joker, joker),
440  gp_lat[ip].fd[0] * gp_p[ip].fd[0]);
441  }
442  }
443 
444  //- 3D
445  else if (atmosphere_dim == 3) {
446  for (Index ip = 0; ip < ppath.np; ip++) {
447  Index ix =
448  nr2 * nr1 * gp_lon[ip].idx + nr1 * gp_lat[ip].idx + gp_p[ip].idx;
449  // Low lon, low lat, low p
450  if (gp_lon[ip].fd[1] > 0 && gp_lat[ip].fd[1] > 0 && gp_p[ip].fd[1] > 0)
452  diy_dx(ix, joker, joker),
453  diy_dpath(ip, joker, joker),
454  gp_lon[ip].fd[1] * gp_lat[ip].fd[1] * gp_p[ip].fd[1]);
455  // Low lon, low lat, high p
456  if (gp_lon[ip].fd[1] > 0 && gp_lat[ip].fd[1] > 0 && gp_p[ip].fd[0] > 0)
458  diy_dx(ix + 1, joker, joker),
459  diy_dpath(ip, joker, joker),
460  gp_lon[ip].fd[1] * gp_lat[ip].fd[1] * gp_p[ip].fd[0]);
461  // Low lon, high lat, low p
462  if (gp_lon[ip].fd[1] > 0 && gp_lat[ip].fd[0] > 0 && gp_p[ip].fd[1] > 0)
464  diy_dx(ix + nr1, joker, joker),
465  diy_dpath(ip, joker, joker),
466  gp_lon[ip].fd[1] * gp_lat[ip].fd[0] * gp_p[ip].fd[1]);
467  // Low lon, high lat, high p
468  if (gp_lon[ip].fd[1] > 0 && gp_lat[ip].fd[0] > 0 && gp_p[ip].fd[0] > 0)
470  diy_dx(ix + nr1 + 1, joker, joker),
471  diy_dpath(ip, joker, joker),
472  gp_lon[ip].fd[1] * gp_lat[ip].fd[0] * gp_p[ip].fd[0]);
473 
474  // Increase *ix* (to be valid for high lon level)
475  ix += nr2 * nr1;
476 
477  // High lon, low lat, low p
478  if (gp_lon[ip].fd[0] > 0 && gp_lat[ip].fd[1] > 0 && gp_p[ip].fd[1] > 0)
480  diy_dx(ix, joker, joker),
481  diy_dpath(ip, joker, joker),
482  gp_lon[ip].fd[0] * gp_lat[ip].fd[1] * gp_p[ip].fd[1]);
483  // High lon, low lat, high p
484  if (gp_lon[ip].fd[0] > 0 && gp_lat[ip].fd[1] > 0 && gp_p[ip].fd[0] > 0)
486  diy_dx(ix + 1, joker, joker),
487  diy_dpath(ip, joker, joker),
488  gp_lon[ip].fd[0] * gp_lat[ip].fd[1] * gp_p[ip].fd[0]);
489  // High lon, high lat, low p
490  if (gp_lon[ip].fd[0] > 0 && gp_lat[ip].fd[0] > 0 && gp_p[ip].fd[1] > 0)
492  diy_dx(ix + nr1, joker, joker),
493  diy_dpath(ip, joker, joker),
494  gp_lon[ip].fd[0] * gp_lat[ip].fd[0] * gp_p[ip].fd[1]);
495  // High lon, high lat, high p
496  if (gp_lon[ip].fd[0] > 0 && gp_lat[ip].fd[0] > 0 && gp_p[ip].fd[0] > 0)
498  diy_dx(ix + nr1 + 1, joker, joker),
499  diy_dpath(ip, joker, joker),
500  gp_lon[ip].fd[0] * gp_lat[ip].fd[0] * gp_p[ip].fd[0]);
501  }
502  }
503  }
504 }
505 
507  const RetrievalQuantity& jacobian_quantity,
508  ConstMatrixView diy_dpos,
509  const Index& atmosphere_dim,
510  ConstVectorView rtp_pos) {
511  assert(jacobian_quantity.Grids().nelem() ==
512  max(atmosphere_dim - 1, Index(1)));
513  assert(rtp_pos.nelem() == atmosphere_dim);
514 
515  // We want here an extrapolation to infinity ->
516  // extremly high extrapolation factor
517  const Numeric extpolfac = 1.0e99;
518 
519  // Handle 1D separately
520  if (atmosphere_dim == 1) {
521  diy_dx(0, joker, joker) = diy_dpos;
522  return;
523  }
524 
525  // Latitude
526  Index nr1 = 1;
527  ArrayOfGridPos gp_lat;
528  {
529  gp_lat.resize(1);
530  nr1 = jacobian_quantity.Grids()[0].nelem();
531  if (nr1 > 1) {
532  gridpos(gp_lat,
533  jacobian_quantity.Grids()[0],
534  Vector(1, rtp_pos[1]),
535  extpolfac);
536  jacobian_type_extrapol(gp_lat);
537  } else {
538  gp4length1grid(gp_lat);
539  }
540  }
541 
542  // Longitude
543  ArrayOfGridPos gp_lon;
544  if (atmosphere_dim > 2) {
545  gp_lon.resize(1);
546  if (jacobian_quantity.Grids()[1].nelem() > 1) {
547  gridpos(gp_lon,
548  jacobian_quantity.Grids()[1],
549  Vector(1, rtp_pos[2]),
550  extpolfac);
551  jacobian_type_extrapol(gp_lon);
552  } else {
553  gp4length1grid(gp_lon);
554  }
555  }
556 
557  //- 2D
558  if (atmosphere_dim == 2) {
559  if (gp_lat[0].fd[1] > 0) {
560  from_dpath_to_dx(diy_dx(gp_lat[0].idx, joker, joker),
561  diy_dpos(joker, joker),
562  gp_lat[0].fd[1]);
563  }
564  if (gp_lat[0].fd[0] > 0) {
565  from_dpath_to_dx(diy_dx(gp_lat[0].idx + 1, joker, joker),
566  diy_dpos(joker, joker),
567  gp_lat[0].fd[0]);
568  }
569  }
570  //- 3D
571  else {
572  Index ix = nr1 * gp_lon[0].idx + gp_lat[0].idx;
573  // Low lon, low lat
574  if (gp_lon[0].fd[1] > 0 && gp_lat[0].fd[1] > 0)
575  from_dpath_to_dx(diy_dx(ix, joker, joker),
576  diy_dpos(joker, joker),
577  gp_lon[0].fd[1] * gp_lat[0].fd[1]);
578  // Low lon, high lat
579  if (gp_lon[0].fd[1] > 0 && gp_lat[0].fd[0] > 0)
580  from_dpath_to_dx(diy_dx(ix + 1, joker, joker),
581  diy_dpos(joker, joker),
582  gp_lon[0].fd[1] * gp_lat[0].fd[0]);
583  // High lon, low lat
584  if (gp_lon[0].fd[0] > 0 && gp_lat[0].fd[1] > 0)
585  from_dpath_to_dx(diy_dx(ix + nr1, joker, joker),
586  diy_dpos(joker, joker),
587  gp_lon[0].fd[0] * gp_lat[0].fd[1]);
588  // High lon, high lat
589  if (gp_lon[0].fd[0] > 0 && gp_lat[0].fd[0] > 0)
590  from_dpath_to_dx(diy_dx(ix + nr1 + 1, joker, joker),
591  diy_dpos(joker, joker),
592  gp_lon[0].fd[0] * gp_lat[0].fd[0]);
593  }
594 }
595 
597  ArrayOfIndex& abs_species_i,
598  ArrayOfIndex& scat_species_i,
599  ArrayOfIndex& is_t,
600  ArrayOfIndex& wind_i,
601  ArrayOfIndex& magfield_i,
602  const ArrayOfRetrievalQuantity& jacobian_quantities,
603  const ArrayOfArrayOfSpeciesTag& abs_species,
604  const Index& cloudbox_on,
605  const ArrayOfString& scat_species) {
607  //
608  if (jacobian_quantities[iq].MainTag() == TEMPERATURE_MAINTAG &&
609  jacobian_quantities[iq].SubSubtag() == PROPMAT_SUBSUBTAG) {
610  is_t[iq] = Index(JacobianType::Temperature);
611  } else { is_t[iq] = Index(JacobianType::None); }
612  //
613  if (jacobian_quantities[iq].MainTag() == ABSSPECIES_MAINTAG) {
614  if (jacobian_quantities[iq].SubSubtag() == PROPMAT_SUBSUBTAG) {
615  bool test_available = false;
616  for (Index ii = 0; ii < abs_species.nelem(); ii++) {
617  if (abs_species[ii][0].Species() ==
618  SpeciesTag(jacobian_quantities[iq].Subtag()).Species()) {
619  test_available = true;
620  abs_species_i[iq] = ii;
621  break;
622  }
623  }
624  if (!test_available) {
625  ostringstream os;
626  os << "Could not find " << jacobian_quantities[iq].Subtag()
627  << " in species of abs_species.\n";
628  throw std::runtime_error(os.str());
629  }
630  } else {
631  ArrayOfSpeciesTag atag;
632  array_species_tag_from_string(atag, jacobian_quantities[iq].Subtag());
633  abs_species_i[iq] = chk_contains("abs_species", abs_species, atag);
634  }
635  } else if (jacobian_quantities[iq].MainTag() == PARTICULATES_MAINTAG ||
636  jacobian_quantities[iq].MainTag() == ELECTRONS_MAINTAG) {
637  abs_species_i[iq] = -9999;
638  } else { abs_species_i[iq] = -1; }
639  //
640  if (cloudbox_on &&
641  jacobian_quantities[iq].MainTag() == SCATSPECIES_MAINTAG) {
642  scat_species_i[iq] =
643  find_first(scat_species, jacobian_quantities[iq].Subtag());
644  if (scat_species_i[iq] < 0) {
645  ostringstream os;
646  os << "Jacobian quantity with index " << iq << " refers to\n"
647  << " " << jacobian_quantities[iq].Subtag()
648  << "\nbut this species could not be found in *scat_species*.";
649  throw runtime_error(os.str());
650  }
651  } else { scat_species_i[iq] = -1; }
652  //
653  if (jacobian_quantities[iq].MainTag() == WIND_MAINTAG &&
654  jacobian_quantities[iq].SubSubtag() == PROPMAT_SUBSUBTAG) {
655  // Map u, v and w to 1, 2 and 3, respectively
656  char c = jacobian_quantities[iq].Subtag()[0];
657  const Index test = Index(c) - 116;
658  if (test == 1)
659  wind_i[iq] = Index(JacobianType::WindFieldU);
660  else if (test == 2)
661  wind_i[iq] = Index(JacobianType::WindFieldV);
662  else if (test == 3)
663  wind_i[iq] = Index(JacobianType::WindFieldW);
664  else if (test == (Index('s') - 116))
665  wind_i[iq] = Index(JacobianType::AbsWind);
666  } else { wind_i[iq] = Index(JacobianType::None); }
667  //
668  if (jacobian_quantities[iq].MainTag() == MAGFIELD_MAINTAG &&
669  jacobian_quantities[iq].SubSubtag() == PROPMAT_SUBSUBTAG) {
670  // Map u, v and w to 1, 2 and 3, respectively
671  char c = jacobian_quantities[iq].Subtag()[0];
672  const Index test = Index(c) - 116;
673  if (test == 1)
674  magfield_i[iq] = Index(JacobianType::MagFieldU);
675  else if (test == 2)
676  magfield_i[iq] = Index(JacobianType::MagFieldV);
677  else if (test == 3)
678  magfield_i[iq] = Index(JacobianType::MagFieldW);
679  else if (test == (Index('s') - 116))
680  magfield_i[iq] = Index(JacobianType::AbsMag);
681  } else { magfield_i[iq] = Index(JacobianType::None); }
682 
683  )
684 }
685 
686 /*===========================================================================
687  === Other functions, in alphabetical order
688  ===========================================================================*/
689 
691  ostringstream& os,
692  const Vector& p_grid,
693  const Vector& lat_grid,
694  const Vector& lon_grid,
695  const Vector& p_retr,
696  const Vector& lat_retr,
697  const Vector& lon_retr,
698  const String& p_retr_name,
699  const String& lat_retr_name,
700  const String& lon_retr_name,
701  const Index& dim) {
702  assert(grids.nelem() == dim);
703 
704  if (p_retr.nelem() == 0) {
705  os << "The grid vector *" << p_retr_name << "* is empty,"
706  << " at least one pressure level\n"
707  << "should be specified.";
708  return false;
709  } else if (!is_decreasing(p_retr)) {
710  os << "The pressure grid vector *" << p_retr_name << "* is not a\n"
711  << "strictly decreasing vector, which is required.";
712  return false;
713  } else if (p_grid.nelem() == 1 and p_grid.nelem() == p_retr.nelem()) {
714  if (p_grid[0] not_eq p_retr[0]) {
715  os << "Mismatching 1-long grids for " << p_retr_name;
716  return false;
717  }
718 
719  // Necessary repeat but grids are OK
720  grids[0] = p_retr;
721  } else if (log(p_retr[0]) > 1.5 * log(p_grid[0]) - 0.5 * log(p_grid[1]) ||
722  log(p_retr[p_retr.nelem() - 1]) <
723  1.5 * log(p_grid[p_grid.nelem() - 1]) -
724  0.5 * log(p_grid[p_grid.nelem() - 2])) {
725  os << "The grid vector *" << p_retr_name << "* is not covered by the\n"
726  << "corresponding atmospheric grid.";
727  return false;
728  } else {
729  // Pressure grid ok, add it to grids
730  grids[0] = p_retr;
731  }
732 
733  if (dim >= 2) {
734  // If 2D and 3D atmosphere, check latitude grid
735  if (lat_retr.nelem() == 0) {
736  os << "The grid vector *" << lat_retr_name << "* is empty,"
737  << " at least one latitude\n"
738  << "should be specified for a 2D/3D atmosphere.";
739  return false;
740  } else if (!is_increasing(lat_retr)) {
741  os << "The latitude grid vector *" << lat_retr_name << "* is not a\n"
742  << "strictly increasing vector, which is required.";
743  return false;
744  } else if (lat_grid.nelem() == 1 and lat_grid.nelem() == lat_retr.nelem()) {
745  if (lat_grid[0] not_eq lat_retr[0]) {
746  os << "Mismatching 1-long grids for " << lat_retr_name;
747  return false;
748  }
749 
750  // Necessary repeat but grids are OK
751  grids[1] = lat_retr;
752  } else if (lat_retr[0] < 1.5 * lat_grid[0] - 0.5 * lat_grid[1] ||
753  lat_retr[lat_retr.nelem() - 1] >
754  1.5 * lat_grid[lat_grid.nelem() - 1] -
755  0.5 * lat_grid[lat_grid.nelem() - 2]) {
756  os << "The grid vector *" << lat_retr_name << "* is not covered by the\n"
757  << "corresponding atmospheric grid.";
758  return false;
759  } else {
760  // Latitude grid ok, add it to grids
761  grids[1] = lat_retr;
762  }
763  if (dim == 3) {
764  // For 3D atmosphere check longitude grid
765  if (lon_retr.nelem() == 0) {
766  os << "The grid vector *" << lon_retr_name << "* is empty,"
767  << " at least one longitude\n"
768  << "should be specified for a 3D atmosphere.";
769  return false;
770  } else if (!is_increasing(lon_retr)) {
771  os << "The longitude grid vector *" << lon_retr_name << "* is not a\n"
772  << "strictly increasing vector, which is required.";
773  return false;
774  } else if (lon_grid.nelem() == 1 and
775  lon_grid.nelem() == lon_retr.nelem()) {
776  if (lon_grid[0] not_eq lon_retr[0]) {
777  os << "Mismatching 1-long grids for " << lon_retr_name;
778  return false;
779  }
780 
781  // Necessary repeat but grids are OK
782  grids[2] = lon_retr;
783  } else if (lon_retr[0] < 1.5 * lon_grid[0] - 0.5 * lon_grid[1] ||
784  lon_retr[lon_retr.nelem() - 1] >
785  1.5 * lon_grid[lon_grid.nelem() - 1] -
786  0.5 * lon_grid[lon_grid.nelem() - 2]) {
787  os << "The grid vector *" << lon_retr_name
788  << "* is not covered by the\n"
789  << "corresponding atmospheric grid.";
790  return false;
791  } else {
792  // Longitude grid ok, add it to grids
793  grids[2] = lon_retr;
794  }
795  }
796  }
797  return true;
798 }
799 
801  ostringstream& os,
802  const Vector& lat_grid,
803  const Vector& lon_grid,
804  const Vector& lat_retr,
805  const Vector& lon_retr,
806  const String& lat_retr_name,
807  const String& lon_retr_name,
808  const Index& dim) {
809  assert(grids.nelem() == max(dim - 1, Index(1)));
810 
811  if (dim == 1) {
812  // Here we only need to create a length 1 dummy grid
813  grids[0].resize(1);
814  grids[0][0] = 0;
815  }
816 
817  if (dim >= 2) {
818  // If 2D and 3D atmosphere, check latitude grid
819  if (lat_retr.nelem() == 0) {
820  os << "The grid vector *" << lat_retr_name << "* is empty,"
821  << " at least one latitude\n"
822  << "should be specified for a 2D/3D atmosphere.";
823  return false;
824  } else if (!is_increasing(lat_retr)) {
825  os << "The latitude grid vector *" << lat_retr_name << "* is not a\n"
826  << "strictly increasing vector, which is required.";
827  return false;
828  } else if (lat_grid.nelem() == 1 and lat_grid.nelem() == lat_retr.nelem()) {
829  if (lat_grid[0] not_eq lat_retr[0]) {
830  os << "Mismatching 1-long grids for " << lat_retr_name;
831  return false;
832  }
833 
834  // Necessary repeat but grids are OK
835  grids[0] = lat_retr;
836  } else if (lat_retr[0] < 1.5 * lat_grid[0] - 0.5 * lat_grid[1] ||
837  lat_retr[lat_retr.nelem() - 1] >
838  1.5 * lat_grid[lat_grid.nelem() - 1] -
839  0.5 * lat_grid[lat_grid.nelem() - 2]) {
840  os << "The grid vector *" << lat_retr_name << "* is not covered by the\n"
841  << "corresponding atmospheric grid.";
842  return false;
843  } else {
844  // Latitude grid ok, add it to grids
845  grids[0] = lat_retr;
846  }
847 
848  if (dim == 3) {
849  // For 3D atmosphere check longitude grid
850  if (lon_retr.nelem() == 0) {
851  os << "The grid vector *" << lon_retr_name << "* is empty,"
852  << " at least one longitude\n"
853  << "should be specified for a 3D atmosphere.";
854  return false;
855  } else if (!is_increasing(lon_retr)) {
856  os << "The longitude grid vector *" << lon_retr_name << "* is not a\n"
857  << "strictly increasing vector, which is required.";
858  return false;
859  } else if (lon_grid.nelem() == 1 and
860  lon_grid.nelem() == lon_retr.nelem()) {
861  if (lon_grid[0] not_eq lon_retr[0]) {
862  os << "Mismatching 1-long grids for " << lon_retr_name;
863  return false;
864  }
865 
866  // Necessary repeat but grids are OK
867  grids[1] = lon_retr;
868  } else if (lon_retr[0] < 1.5 * lon_grid[0] - 0.5 * lon_grid[1] ||
869  lon_retr[lon_retr.nelem() - 1] >
870  1.5 * lon_grid[lon_grid.nelem() - 1] -
871  0.5 * lon_grid[lon_grid.nelem() - 2]) {
872  os << "The grid vector *" << lon_retr_name
873  << "* is not covered by the\n"
874  << "corresponding atmospheric grid.";
875  return false;
876  } else {
877  // Longitude grid ok, add it to grids
878  grids[1] = lon_retr;
879  }
880  }
881  }
882  return true;
883 }
884 
886  for (Index i = 0; i < gp.nelem(); i++) {
887  if (gp[i].fd[0] < 0) {
888  gp[i].fd[0] = 0;
889  gp[i].fd[1] = 1;
890  } else if (gp[i].fd[0] > 1) {
891  gp[i].fd[0] = 1;
892  gp[i].fd[1] = 0;
893  }
894  }
895 }
896 
898  const Vector& x,
899  const Index& poly_coeff) {
900  const Index l = x.nelem();
901 
902  assert(l > poly_coeff);
903 
904  if (b.nelem() != l) b.resize(l);
905 
906  if (poly_coeff == 0) {
907  b = 1.0;
908  } else {
909  const Numeric xmin = min(x);
910  const Numeric dx = 0.5 * (max(x) - xmin);
911  //
912  for (Index i = 0; i < l; i++) {
913  b[i] = (x[i] - xmin) / dx - 1.0;
914  b[i] = pow(b[i], int(poly_coeff));
915  }
916  //
917  b -= mean(b);
918  }
919 }
920 
921 void calcBaselineFit(Vector& y_baseline,
922  const Vector& x,
923  const Index& mblock_index,
924  const Sparse& sensor_response,
925  const ArrayOfIndex& sensor_response_pol_grid,
926  const Vector& sensor_response_f_grid,
927  const Matrix& sensor_response_dlos_grid,
928  const RetrievalQuantity& rq,
929  const Index rq_index,
930  const ArrayOfArrayOfIndex& jacobian_indices) {
931  bool is_sine_fit = false;
932  if (rq.MainTag() == POLYFIT_MAINTAG) {
933  is_sine_fit = false;
934  } else if (rq.MainTag() == SINEFIT_MAINTAG) {
935  is_sine_fit = true;
936  } else {
937  throw runtime_error(
938  "Retrieval quantity is neither a polynomial or a sine "
939  " baseline fit.");
940  }
941 
942  // Size and check of sensor_response
943  //
944  const Index nf = sensor_response_f_grid.nelem();
945  const Index npol = sensor_response_pol_grid.nelem();
946  const Index nlos = sensor_response_dlos_grid.nrows();
947 
948  // Evaluate basis functions for fits.
949  Vector w, s, c;
950  if (is_sine_fit) {
951  s.resize(nf);
952  c.resize(nf);
953  Numeric period = rq.Grids()[0][0];
954  for (Index f = 0; f < nf; f++) {
955  Numeric a = (sensor_response_f_grid[f] - sensor_response_f_grid[0]) * 2 *
956  PI / period;
957  s[f] = sin(a);
958  c[f] = cos(a);
959  }
960  } else {
961  Numeric poly_coeff = rq.Grids()[0][0];
963  w, sensor_response_f_grid, static_cast<Index>(poly_coeff));
964  }
965 
966  // Compute baseline
967  ArrayOfVector jg = rq.Grids();
968  const Index n1 = jg[1].nelem();
969  const Index n2 = jg[2].nelem();
970  const Index n3 = jg[3].nelem();
971  const Range rowind = get_rowindex_for_mblock(sensor_response, mblock_index);
972  const Index row4 = rowind.get_start();
973  Index col4 = jacobian_indices[rq_index][0];
974 
975  if (n3 > 1) {
976  col4 += mblock_index * n2 * n1;
977  }
978 
979  for (Index l = 0; l < nlos; l++) {
980  const Index row3 = row4 + l * nf * npol;
981  const Index col3 = col4 + l * n1 * (is_sine_fit ? 2 : 1);
982 
983  for (Index f = 0; f < nf; f++) {
984  const Index row2 = row3 + f * npol;
985 
986  for (Index p = 0; p < npol; p++) {
987  Index col1 = col3;
988  if (n1 > 1) {
989  col1 += p;
990  }
991  if (is_sine_fit) {
992  y_baseline[row2 + p] += x[col1] * s[f] + x[col1 + 1] * c[f];
993  } else {
994  y_baseline[row2 + p] += w[f] * x[col1];
995  }
996  }
997  }
998  }
999 }
1000 
1002  const String& unit,
1003  const Numeric& vmr,
1004  const Numeric& p,
1005  const Numeric& t) {
1006  if (unit == "rel" || unit == "logrel") {
1007  x = 1;
1008  } else if (unit == "vmr") {
1009  if (vmr == 0) {
1010  x = 0;
1011  return;
1012  }
1013  x = 1 / vmr;
1014  } else if (unit == "nd") {
1015  if (vmr == 0) {
1016  x = 0;
1017  return;
1018  }
1019  x = 1 / (vmr * number_density(p, t));
1020  } else {
1021  ostringstream os;
1022  os << "Allowed options for gas species jacobians are "
1023  "\"rel\", \"vmr\" and \"nd\".\nYou have selected: "
1024  << unit << std::endl;
1025  throw std::runtime_error(os.str());
1026  }
1027 }
1028 
1030  const String& unit,
1031  const Numeric& vmr,
1032  const Numeric& p,
1033  const Numeric& t) {
1034  if (unit == "rel" || unit == "logrel") {
1035  x = vmr;
1036  } else if (unit == "vmr") {
1037  x = 1;
1038  } else if (unit == "nd") {
1039  x = 1 / number_density(p, t);
1040  } else {
1041  ostringstream os;
1042  os << "Allowed options for gas species jacobians are "
1043  "\"rel\", \"vmr\" and \"nd\".\nYou have selected: "
1044  << unit << std::endl;
1045  throw std::runtime_error(os.str());
1046  }
1047 }
1048 
1050  VectorView diy2,
1051  ConstMatrixView ImT,
1053  ConstMatrixView dT1,
1054  ConstMatrixView dT2,
1055  ConstVectorView iYmJ,
1056  ConstVectorView dJ1,
1057  ConstVectorView dJ2,
1058  const Index stokes_dim,
1059  const bool transmission_only) {
1060  /*
1061  * Solves
1062  *
1063  * diy1 = PiT [ dT1 iYmJ + (1-T) dJ1 ],
1064  *
1065  * and
1066  *
1067  * diy2 += PiT [ dT2 iYmJ + (1-T) dJ2 ],
1068  *
1069  * where diy2 is diy1 from a prior layer
1070  *
1071  * FIXME: Needs HSE
1072  */
1073 
1074  // Computation vectors
1075  Vector a(stokes_dim), b(stokes_dim);
1076 
1077  // The first time a level is involved in a layer
1078  mult(a, dT1, iYmJ);
1079  if (not transmission_only) {
1080  mult(b, ImT, dJ1);
1081  a += b;
1082  }
1083  mult(diy1, cumulative_transmission, a);
1084 
1085  // The second time a level is involved in a layer
1086  mult(a, dT2, iYmJ);
1087  if (not transmission_only) {
1088  mult(b, ImT, dJ2);
1089  a += b;
1090  }
1091  mult(b, cumulative_transmission, a);
1092  diy2 += b;
1093 }
1094 
1095 //======================================================================
1096 // Propmat partials descriptions
1097 //======================================================================
1098 
1100  ArrayOfIndex pos;
1101  pos.reserve(js.nelem());
1102  for (Index i = 0; i < js.nelem(); i++)
1103  if (js[i] not_eq JacPropMatType::NotPropagationMatrixType) pos.push_back(i);
1104  return pos;
1105 }
1106 
1108  const Index i) noexcept {
1109  Index j = -1;
1110  for (Index k = 0; k <= i; k++)
1111  if (js[k] not_eq JacPropMatType::NotPropagationMatrixType) j++;
1112  return j;
1113 }
1114 
1115 bool is_wind_parameter(const RetrievalQuantity& t) noexcept {
1118 }
1119 
1121  return is_wind_parameter(t) or t == JacPropMatType::Frequency;
1122 }
1123 
1126 }
1127 
1128 bool is_magnetic_parameter(const RetrievalQuantity& t) noexcept {
1131 }
1132 
1133 bool is_nlte_parameter(const RetrievalQuantity& t) noexcept {
1134  return t == JacPropMatType::NLTE;
1135 }
1136 
1137 #define ISLINESHAPETYPE(X) \
1138  bool is_pressure_broadening_##X(const RetrievalQuantity& t) noexcept { \
1139  return t == JacPropMatType::LineShape##X##X0 or \
1140  t == JacPropMatType::LineShape##X##X1 or \
1141  t == JacPropMatType::LineShape##X##X2; \
1142  }
1143 ISLINESHAPETYPE(G0)
1144 ISLINESHAPETYPE(D0)
1145 ISLINESHAPETYPE(G2)
1146 ISLINESHAPETYPE(D2)
1147 ISLINESHAPETYPE(FVC)
1148 ISLINESHAPETYPE(ETA)
1149 ISLINESHAPETYPE(Y)
1150 ISLINESHAPETYPE(G)
1151 ISLINESHAPETYPE(DV)
1152 #undef ISLINESHAPETYPE
1153 
1154 #define VARISLINESHAPEPARAM(X, Y) (t == JacPropMatType::LineShape##X##Y)
1156  return VARISLINESHAPEPARAM(G0, X0) or VARISLINESHAPEPARAM(D0, X0) or
1157  VARISLINESHAPEPARAM(G2, X0) or VARISLINESHAPEPARAM(D2, X0) or
1158  VARISLINESHAPEPARAM(FVC, X0) or VARISLINESHAPEPARAM(ETA, X0) or
1159  VARISLINESHAPEPARAM(Y, X0) or VARISLINESHAPEPARAM(G, X0) or
1160  VARISLINESHAPEPARAM(DV, X0);
1161 }
1162 
1164  return VARISLINESHAPEPARAM(G0, X1) or VARISLINESHAPEPARAM(D0, X1) or
1165  VARISLINESHAPEPARAM(G2, X1) or VARISLINESHAPEPARAM(D2, X1) or
1166  VARISLINESHAPEPARAM(FVC, X1) or VARISLINESHAPEPARAM(ETA, X1) or
1167  VARISLINESHAPEPARAM(Y, X1) or VARISLINESHAPEPARAM(G, X1) or
1168  VARISLINESHAPEPARAM(DV, X1);
1169 }
1170 
1172  return VARISLINESHAPEPARAM(G0, X2) or VARISLINESHAPEPARAM(D0, X2) or
1173  VARISLINESHAPEPARAM(G2, X2) or VARISLINESHAPEPARAM(D2, X2) or
1174  VARISLINESHAPEPARAM(FVC, X2) or VARISLINESHAPEPARAM(ETA, X2) or
1175  VARISLINESHAPEPARAM(Y, X2) or VARISLINESHAPEPARAM(G, X2) or
1176  VARISLINESHAPEPARAM(DV, X2);
1177 }
1178 #undef VARISLINESHAPEPARAM
1179 
1181  const RetrievalQuantity& t) noexcept {
1185 }
1186 
1193 }
1194 
1195 bool is_line_parameter(const RetrievalQuantity& t) noexcept {
1198 }
1199 
1201  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return (j == JacPropMatType::Temperature or j == JacPropMatType::VMR or is_frequency_parameter(j));});
1202 }
1203 
1205  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return (j == JacPropMatType::Temperature or j == JacPropMatType::VMR or is_frequency_parameter(j));});
1206 }
1207 
1209  if (std::any_of(js.cbegin(), js.cend(), [](auto& j){return is_line_parameter(j);}))
1210  throw std::runtime_error("Line specific parameters are not supported while using continuum tags.\nWe do not track what lines are in the continuum.\n");
1211  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return (j == JacPropMatType::Temperature or j == JacPropMatType::VMR or is_frequency_parameter(j));});
1212 }
1213 
1215  return not std::any_of(js.cbegin(), js.cend(), [](auto& j){return j not_eq JacPropMatType::VMR or j not_eq JacPropMatType::LineStrength or not is_nlte_parameter(j);});
1216 }
1217 
1219  if (std::any_of(js.cbegin(), js.cend(), [](auto& j){return is_line_parameter(j);}))
1220  throw std::runtime_error("Line specific parameters are not supported while\n using the relaxation matrix line mixing routine.\n We do not yet track individual lines in the relaxation matrix calculations.\n");
1221  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return (j == JacPropMatType::Temperature or is_frequency_parameter(j));});
1222 }
1223 
1225  if (std::any_of(js.cbegin(), js.cend(), [](auto& j){return is_line_parameter(j);}))
1226  throw std::runtime_error("Line specific parameters are not supported while using Lookup table.\nWe do not track lines in the Lookup.\n");
1227  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return (j == JacPropMatType::Temperature or j == JacPropMatType::VMR or is_frequency_parameter(j));});
1228 }
1229 
1231  if (std::any_of(js.cbegin(), js.cend(), [](auto& j){return is_derived_magnetic_parameter(j);}))
1232  throw std::runtime_error("This method does not yet support Zeeman-style magnetic Jacobian calculations.\n Please use u, v, and w Jacobians instead.\n");
1233  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return j == JacPropMatType::MagneticU or j == JacPropMatType::MagneticV or j == JacPropMatType::MagneticW or j == JacPropMatType::Electrons or is_frequency_parameter(j);});
1234 }
1235 
1237  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return j not_eq JacPropMatType::NotPropagationMatrixType;});
1238 }
1239 
1241  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return j not_eq JacPropMatType::NotPropagationMatrixType;});
1242 }
1243 
1245  if (rq.QuantumIdentity().Type() == QuantumIdentifier::ALL) { // Single tag
1246  for (const auto& s : ast) {
1247  if (rq.QuantumIdentity().Species() not_eq s.Species())
1248  return false; // Species must match
1249  if (rq.QuantumIdentity().Isotopologue() >= 0 and s.Isotopologue() >= 0 and
1250  rq.QuantumIdentity().Isotopologue() not_eq s.Isotopologue())
1251  return false; // Isotopologue must match or be undefined
1252  }
1253  } else {
1254  ArrayOfSpeciesTag test;
1256  if (ast not_eq test)
1257  return false; // Match single tag perfectly or throw out
1258  }
1259  return true;
1260 }
1261 
1262 bool species_match(const RetrievalQuantity& rq, const Index species) {
1263  if (rq == JacPropMatType::VMR)
1264  if (rq.QuantumIdentity().Species() == species) return true;
1265  return false;
1266 }
1267 
1269  const Index species,
1270  const Index iso) {
1271  const SpeciesRecord& spr = global_data::species_data[species];
1272  if (species_match(rq, species))
1273  if (rq.QuantumIdentity().Isotopologue() == iso or
1274  spr.Isotopologue().nelem() == iso or iso < 0)
1275  return true;
1276  return false;
1277 }
1278 
1280  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return j == JacPropMatType::Temperature;});
1281 }
1282 
1284  const QuantumIdentifier& line_qid) noexcept {
1285  auto p = std::find_if(js.cbegin(), js.cend(), [&line_qid](auto& j){return j == JacPropMatType::VMR and j.QuantumIdentity().In(line_qid);});
1286  if (p not_eq js.cend())
1287  return {true, p -> QuantumIdentity()};
1288  else
1289  return {false, line_qid};
1290 }
1291 
1293  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return j == JacPropMatType::LineCenter;});
1294 }
1295 
1297  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return is_frequency_parameter(j);});
1298 }
1299 
1301  return std::any_of(js.cbegin(), js.cend(), [](auto& j){return is_magnetic_parameter(j);});
1302 }
1303 
1305  auto p = std::find_if(js.cbegin(), js.cend(), [](auto& j){return j == JacPropMatType::Temperature;});
1306  if (p not_eq js.cend())
1307  return p -> Perturbation();
1308  else
1309  return 0.0;
1310 }
1311 
1313  auto p = std::find_if(js.cbegin(), js.cend(), [](auto& j){return is_frequency_parameter(j);});
1314  if (p not_eq js.cend())
1315  return p -> Perturbation();
1316  else
1317  return 0.0;
1318 }
1319 
1321  auto p = std::find_if(js.cbegin(), js.cend(), [](auto& j){return is_magnetic_parameter(j);});
1322  if (p not_eq js.cend())
1323  return p -> Perturbation();
1324  else
1325  return 0.0;
1326 }
1327 
1329 #define lineshapevariable(X1, X2) \
1330  JacPropMatType::LineShape##X1##X2 : return "Line-Shape: " #X1 " " #X2
1331  switch (rq.PropMatType()) {
1332  case JacPropMatType::VMR:
1333  return "VMR";
1335  return "Electrons-VMR";
1337  return "Particulate-VMR";
1339  return "Temperature";
1341  return "Magnetic-Strength";
1343  return "Magnetic-u";
1345  return "Magnetic-v";
1347  return "Magnetic-w";
1349  return "Wind-Strength";
1350  case JacPropMatType::WindU:
1351  return "Wind-u";
1352  case JacPropMatType::WindV:
1353  return "Wind-v";
1354  case JacPropMatType::WindW:
1355  return "Wind-w";
1357  return "Frequency";
1359  return "Line-Strength";
1361  return "Line-Center";
1363  return "Line-Special-Parameter-1";
1364  case JacPropMatType::NLTE:
1365  return "NLTE-Level";
1366  case lineshapevariable(G0, X0);
1367  case lineshapevariable(G0, X1);
1368  case lineshapevariable(G0, X2);
1369  case lineshapevariable(G0, X3);
1370  case lineshapevariable(D0, X0);
1371  case lineshapevariable(D0, X1);
1372  case lineshapevariable(D0, X2);
1373  case lineshapevariable(D0, X3);
1374  case lineshapevariable(G2, X0);
1375  case lineshapevariable(G2, X1);
1376  case lineshapevariable(G2, X2);
1377  case lineshapevariable(G2, X3);
1378  case lineshapevariable(D2, X0);
1379  case lineshapevariable(D2, X1);
1380  case lineshapevariable(D2, X2);
1381  case lineshapevariable(D2, X3);
1382  case lineshapevariable(ETA, X0);
1383  case lineshapevariable(ETA, X1);
1384  case lineshapevariable(ETA, X2);
1385  case lineshapevariable(ETA, X3);
1386  case lineshapevariable(FVC, X0);
1387  case lineshapevariable(FVC, X1);
1388  case lineshapevariable(FVC, X2);
1389  case lineshapevariable(FVC, X3);
1390  case lineshapevariable(Y, X0);
1391  case lineshapevariable(Y, X1);
1392  case lineshapevariable(Y, X2);
1393  case lineshapevariable(Y, X3);
1394  case lineshapevariable(G, X0);
1395  case lineshapevariable(G, X1);
1396  case lineshapevariable(G, X2);
1397  case lineshapevariable(G, X3);
1398  case lineshapevariable(DV, X0);
1399  case lineshapevariable(DV, X1);
1400  case lineshapevariable(DV, X2);
1401  case lineshapevariable(DV, X3);
1403  return "Not-A-Prop-Mat-Variable";
1404  }
1405 #undef lineshapevariable
1406 
1407  return "UNDEFINED-CHECK-IF-CASE-LIST-IS-COMPLETE";
1408 }
void iso(Array< IsotopologueRecord >::iterator &ii, String name, const ArrayOfNumeric &coeff, const ArrayOfNumeric &temp_range, const Index &coefftype)
Initialize isotopologue and move iterator to next one.
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
bool is_pressure_broadening_G0(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a G0 derivative.
bool is_pressure_broadening_Y(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a Y derivative.
ArrayOfIndex equivalent_propmattype_indexes(const ArrayOfRetrievalQuantity &js)
Returns a list of positions for the derivatives in Propagation Matrix calculations.
Definition: jacobian.cc:1099
const String SINEFIT_MAINTAG
void diy_from_pos_to_rgrids(Tensor3View diy_dx, const RetrievalQuantity &jacobian_quantity, ConstMatrixView diy_dpos, const Index &atmosphere_dim, ConstVectorView rtp_pos)
diy_from_pos_to_rgrids
Definition: jacobian.cc:506
Index find_first(const Array< base > &x, const base &w)
Find first occurance.
Definition: array.h:292
bool supports_LBL_without_phase(const ArrayOfRetrievalQuantity &js)
Returns if the array supports line-by-line derivatives without requiring the phase.
Definition: jacobian.cc:1214
JacPropMatType PropMatType() const
Returns the propagation matrix derivative type.
Definition: jacobian.h:267
The VectorView class.
Definition: matpackI.h:610
const ArrayOfVector & Grids() const
Returns the grids of the retrieval.
Definition: jacobian.h:255
const String TEMPERATURE_MAINTAG
const String FLUX_MAINTAG
Index nelem() const
Number of elements.
Definition: array.h:195
void Isotopologue(Index iso)
Set the Isotopologue.
Definition: quantum.h:487
bool is_pressure_broadening_D0(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a D0 derivative.
const QuantumIdentifier & QuantumIdentity() const
Returns the identity of this Jacobian.
Definition: jacobian.h:311
const Index & Analytical() const
Returns the analytical tag.
Definition: jacobian.h:227
const String POLYFIT_MAINTAG
QuantumIdentifier::QType Index LowerQuantumNumbers Species
bool do_magnetic_jacobian(const ArrayOfRetrievalQuantity &js) noexcept
Returns if the array wants a magnetic derivative.
Definition: jacobian.cc:1300
Routines for setting up the jacobian.
The Vector class.
Definition: matpackI.h:860
Index Species() const
Molecular species index.
bool is_line_parameter(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is related to the absorption line.
Definition: jacobian.cc:1195
The MatrixView class.
Definition: matpackI.h:1093
Numeric frequency_perturbation(const ArrayOfRetrievalQuantity &js) noexcept
Returns the frequency perturbation if it exists.
Definition: jacobian.cc:1312
bool is_increasing(ConstVectorView x)
Checks if a vector is sorted and strictly increasing.
Definition: logic.cc:215
The Sparse class.
Definition: matpackII.h:60
bool supports_relaxation_matrix(const ArrayOfRetrievalQuantity &js)
Returns if the array supports relaxation matrix derivatives.
Definition: jacobian.cc:1218
void from_dpath_to_dx(MatrixView diy_dx, ConstMatrixView diy_dq, const Numeric &w)
Definition: jacobian.cc:327
The range class.
Definition: matpackI.h:160
const String PARTICULATES_MAINTAG
const String ABSSPECIES_MAINTAG
Linear algebra functions.
const Vector & OffsetVector() const
Definition: jacobian.h:347
bool do_line_center_jacobian(const ArrayOfRetrievalQuantity &js) noexcept
Returns if the array wants a line center derivative.
Definition: jacobian.cc:1292
invlib::Vector< ArtsVector > Vector
invlib wrapper type for ARTS vectors.
Definition: oem.h:32
#define ISLINESHAPETYPE(X)
Definition: jacobian.cc:1137
cmplx FADDEEVA() w(cmplx z, double relerr)
Definition: Faddeeva.cc:680
bool check_retrieval_grids(ArrayOfVector &grids, ostringstream &os, const Vector &p_grid, const Vector &lat_grid, const Vector &lon_grid, const Vector &p_retr, const Vector &lat_retr, const Vector &lon_retr, const String &p_retr_name, const String &lat_retr_name, const String &lon_retr_name, const Index &dim)
Check that the retrieval grids are defined for each atmosphere dim.
Definition: jacobian.cc:690
Matrix pos
The distance between start pos and the last position in pos.
Definition: ppath.h:64
void transform_jacobian(Matrix &jacobian, const Vector x, const ArrayOfRetrievalQuantity &jqs)
Applies both functional and affine transformations.
Definition: jacobian.cc:103
void p2gridpos(ArrayOfGridPos &gp, ConstVectorView old_pgrid, ConstVectorView new_pgrid, const Numeric &extpolfac)
Calculates grid positions for pressure values.
const String WIND_MAINTAG
void get_pointers_for_analytical_jacobians(ArrayOfIndex &abs_species_i, ArrayOfIndex &scat_species_i, ArrayOfIndex &is_t, ArrayOfIndex &wind_i, ArrayOfIndex &magfield_i, const ArrayOfRetrievalQuantity &jacobian_quantities, const ArrayOfArrayOfSpeciesTag &abs_species, const Index &cloudbox_on, const ArrayOfString &scat_species)
Help function for analytical jacobian calculations.
Definition: jacobian.cc:596
#define min(a, b)
constexpr Index get_start() const
Returns the start index of the range.
Definition: matpackI.h:327
#define FOR_ANALYTICAL_JACOBIANS_DO(what_to_do)
Definition: jacobian.h:405
Numeric number_density(const Numeric &p, const Numeric &t)
number_density
bool supports_continuum(const ArrayOfRetrievalQuantity &js)
Returns if the array supports continuum derivatives.
Definition: jacobian.cc:1208
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:51
Index equivalent_propmattype_index(const ArrayOfRetrievalQuantity &js, const Index i) noexcept
Returns a list of positions for the derivatives in Propagation Matrix calculations.
Definition: jacobian.cc:1107
const Array< SpeciesRecord > species_data
Species Data.
Array< Index > ArrayOfIndex
An array of Index.
Definition: array.h:40
bool is_pressure_broadening_FVC(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a FVC derivative.
void calcBaselineFit(Vector &y_baseline, const Vector &x, const Index &mblock_index, const Sparse &sensor_response, const ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_f_grid, const Matrix &sensor_response_dlos_grid, const RetrievalQuantity &rq, const Index rq_index, const ArrayOfArrayOfIndex &jacobian_indices)
Calculate baseline fit.
Definition: jacobian.cc:921
Deals with internal derivatives, Jacobian definition, and OEM calculations.
Definition: jacobian.h:120
Numeric magnetic_field_perturbation(const ArrayOfRetrievalQuantity &js) noexcept
Returns the magnetic field perturbation if it exists.
Definition: jacobian.cc:1320
bool is_frequency_parameter(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a frequency parameter in propagation matrix calculations.
Definition: jacobian.cc:1120
const Array< IsotopologueRecord > & Isotopologue() const
Definition: absorption.h:199
void jac_ranges_indices(ArrayOfArrayOfIndex &jis, bool &any_affine, const ArrayOfRetrievalQuantity &jqs, const bool &before_affine)
Determines the index range inside x and the Jacobian for each retrieval quantity. ...
Definition: jacobian.cc:58
bool supports_CIA(const ArrayOfRetrievalQuantity &js)
Returns if the array supports CIA derivatives.
Definition: jacobian.cc:1200
Index ncols() const
Returns the number of columns.
Definition: matpackI.cc:432
const String PROPMAT_SUBSUBTAG
bool supports_propmat_clearsky(const ArrayOfRetrievalQuantity &js)
Returns if the array supports propagation matrix derivatives.
Definition: jacobian.cc:1240
void swap(ComplexVector &v1, ComplexVector &v2)
Swaps two objects.
Definition: complex.cc:731
The global header file for ARTS.
bool supports_hitran_xsec(const ArrayOfRetrievalQuantity &js)
Returns if the array supports HITRAN cross-section derivatives.
Definition: jacobian.cc:1204
bool is_derived_magnetic_parameter(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a derived magnetic parameter.
Definition: jacobian.cc:1124
ostream & operator<<(ostream &os, const RetrievalQuantity &ot)
Output operator for RetrievalQuantity.
Definition: jacobian.cc:51
bool is_pressure_broadening_D2(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a D2 derivative.
_CS_string_type str() const
Definition: sstream.h:491
ArrayOfTransmissionMatrix cumulative_transmission(const ArrayOfTransmissionMatrix &T, const CumulativeTransmission type)
Accumulate the transmission matrix over all layers.
String propmattype_string(const RetrievalQuantity &rq)
Returns a string of the retrieval quantity propagation matrix type.
Definition: jacobian.cc:1328
void vmrunitscf(Numeric &x, const String &unit, const Numeric &vmr, const Numeric &p, const Numeric &t)
Scale factor for conversion between gas species units.
Definition: jacobian.cc:1001
Index chk_contains(const String &x_name, const Array< T > &x, const T &what)
Check if an array contains a value.
Definition: check_input.h:149
const Matrix & TransformationMatrix() const
Definition: jacobian.h:346
const Numeric PI
const String & Mode() const
Returns the mode.
Definition: jacobian.h:213
The Tensor3View class.
Definition: matpackIII.h:239
void Species(Index sp)
Set the Species.
Definition: quantum.h:481
Contains the lookup data for one species.
Definition: absorption.h:144
const String MAGFIELD_MAINTAG
A tag group can consist of the sum of several of these.
bool Integration() const
Do integration?
Definition: jacobian.h:326
const String & MainTag() const
Returns the main tag.
Definition: jacobian.h:170
void gridpos(ArrayOfGridPos &gp, ConstVectorView old_grid, ConstVectorView new_grid, const Numeric &extpolfac)
Set up a grid position Array.
bool is_nlte_parameter(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a NLTE parameter.
Definition: jacobian.cc:1133
Class to identify and match lines by their quantum numbers.
Definition: quantum.h:390
jacobianVMRcheck do_vmr_jacobian(const ArrayOfRetrievalQuantity &js, const QuantumIdentifier &line_qid) noexcept
Returns the required info for VMR Jacobian.
Definition: jacobian.cc:1283
bool HasAffine() const
Definition: jacobian.h:343
bool is_lineshape_parameter(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a G0, D0, G2, D2, ETA, FVC, Y, G, DV derivative.
Definition: jacobian.cc:1187
Numeric temperature_perturbation(const ArrayOfRetrievalQuantity &js) noexcept
Returns the temperature perturbation if it exists.
Definition: jacobian.cc:1304
const Joker joker
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
The Matrix class.
Definition: matpackI.h:1193
void jacobian_type_extrapol(ArrayOfGridPos &gp)
Adopts grid positions to extrapolation used for jacobians.
Definition: jacobian.cc:885
bool is_pressure_broadening_G(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a G derivative.
void get_diydx(VectorView diy1, VectorView diy2, ConstMatrixView ImT, ConstMatrixView cumulative_transmission, ConstMatrixView dT1, ConstMatrixView dT2, ConstVectorView iYmJ, ConstVectorView dJ1, ConstVectorView dJ2, const Index stokes_dim, const bool transmission_only)
Definition: jacobian.cc:1049
#define dx
bool do_temperature_jacobian(const ArrayOfRetrievalQuantity &js) noexcept
Returns if the array wants the temperature derivative.
Definition: jacobian.cc:1279
bool is_lineshape_parameter_X0(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a X0 derivative.
Definition: jacobian.cc:1155
Header file for special_interp.cc.
Numeric pow(const Rational base, Numeric exp)
Power of.
Definition: rational.h:628
void mult(ComplexVectorView y, const ConstComplexMatrixView &M, const ConstComplexVectorView &x)
Matrix-Vector Multiplication.
Definition: complex.cc:1579
const String & TransformationFunc() const
Definition: jacobian.h:344
bool supports_lookup(const ArrayOfRetrievalQuantity &js)
Returns if the array supports lookup table derivatives.
Definition: jacobian.cc:1224
bool species_match(const RetrievalQuantity &rq, const ArrayOfSpeciesTag &ast)
Returns if the Retrieval quantity is VMR derivative for all the species in the species tags...
Definition: jacobian.cc:1244
bool is_magnetic_parameter(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a magnetic parameter.
Definition: jacobian.cc:1128
void dxdvmrscf(Numeric &x, const String &unit, const Numeric &vmr, const Numeric &p, const Numeric &t)
Scale factor for conversion of derivatives with respect to VMR.
Definition: jacobian.cc:1029
void transform_x(Vector &x, const ArrayOfRetrievalQuantity &jqs)
Handles transformations of the state vector.
Definition: jacobian.cc:168
Index npages() const
Returns the number of pages.
Definition: matpackIII.h:144
constexpr QType Type() const
Definition: quantum.h:526
const Vector & TFuncParameters() const
Definition: jacobian.h:345
ConstComplexMatrixView transpose(ConstComplexMatrixView m)
Const version of transpose.
Definition: complex.cc:1509
#define lineshapevariable(X1, X2)
bool is_lineshape_parameter_X1(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a X1 derivative.
Definition: jacobian.cc:1163
void resize(Index n)
Resize function.
Definition: matpackI.cc:404
bool is_lineshape_parameter_X2(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a X2 derivative.
Definition: jacobian.cc:1171
#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
const String SCATSPECIES_MAINTAG
void diy_from_path_to_rgrids(Tensor3View diy_dx, const RetrievalQuantity &jacobian_quantity, ConstTensor3View diy_dpath, const Index &atmosphere_dim, const Ppath &ppath, ConstVectorView ppath_p)
Maps jacobian data for points along the propagation path, to jacobian retrieval grid data...
Definition: jacobian.cc:337
Range get_rowindex_for_mblock(const Sparse &sensor_response, const Index &mblock_index)
Returns the "range" of y corresponding to a measurement block.
Definition: rte.cc:1301
Numeric mean(const ConstVectorView &x)
Mean function, vector version.
Definition: matpackI.cc:1589
const String ELECTRONS_MAINTAG
#define VARISLINESHAPEPARAM(X, Y)
Definition: jacobian.cc:1154
void polynomial_basis_func(Vector &b, const Vector &x, const Index &poly_coeff)
Calculates polynomial basis functions.
Definition: jacobian.cc:897
A constant view of a Matrix.
Definition: matpackI.h:982
bool supports_faraday(const ArrayOfRetrievalQuantity &js)
Returns if the array supports Faraday derivatives.
Definition: jacobian.cc:1230
bool is_decreasing(ConstVectorView x)
Checks if a vector is sorted in reversed order and is strictly decreasing.
Definition: logic.cc:252
bool species_iso_match(const RetrievalQuantity &rq, const Index species, const Index iso)
Returns if the Retrieval quantity is VMR derivative for all the species in the species tags...
Definition: jacobian.cc:1268
bool is_lineshape_parameter_bar_linemixing(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a G0, D0, G2, D2, ETA, FVC derivative.
Definition: jacobian.cc:1180
bool is_wind_parameter(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a wind parameter.
Definition: jacobian.cc:1115
Deals with whether or not we should do a VMR derivative.
Definition: jacobian.h:1160
The structure to describe a propagation path and releated quantities.
Definition: ppath.h:48
Index nelem() const
Number of elements in the grids.
Definition: jacobian.h:297
const String & Subtag() const
Returns the sub-tag.
Definition: jacobian.h:184
const Numeric NAT_LOG_TEN
bool supports_particles(const ArrayOfRetrievalQuantity &js)
Returns if the array supports particulate derivatives.
Definition: jacobian.cc:1236
void transform_x_back(Vector &x_t, const ArrayOfRetrievalQuantity &jqs, bool revert_functional_transforms)
Handles back-transformations of the state vector.
Definition: jacobian.cc:257
bool is_pressure_broadening_G2(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a G0 derivative.
void array_species_tag_from_string(ArrayOfSpeciesTag &tags, const String &names)
Converts a String to ArrayOfSpeciesTag.
bool is_pressure_broadening_ETA(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a ETA derivative.
bool do_frequency_jacobian(const ArrayOfRetrievalQuantity &js) noexcept
Returns if the array wants a frequency derivative.
Definition: jacobian.cc:1296
This file contains declerations of functions of physical character.
Index nrows() const
Returns the number of rows.
Definition: matpackI.cc:429
bool is_pressure_broadening_DV(const RetrievalQuantity &t) noexcept
Returns if the Retrieval quantity is a DV derivative.
Declaration of functions in rte.cc.
void gp4length1grid(ArrayOfGridPos &gp)
Grid position matching a grid of length 1.