TECA
The Toolkit for Extreme Climate Analysis
teca_coordinate_util.h
Go to the documentation of this file.
1 #ifndef teca_cartesian_mesh_util_h
2 #define teca_cartesian_mesh_util_h
3 
4 /// @file
5 
6 #include "teca_cartesian_mesh.h"
7 #include "teca_variant_array.h"
8 #include "teca_metadata.h"
9 #include "teca_array_attributes.h"
10 
11 #include <vector>
12 #include <cmath>
13 #include <type_traits>
14 #include <typeinfo>
15 #include <iomanip>
16 
17 /// For printing data as ASCII with the maximum supported numerical precision
18 #define max_prec(T) \
19  std::setprecision(std::numeric_limits<T>::digits10 + 1)
20 
21 /// Codes dealing with operations on coordinate systems
23 {
24 /** @brief
25  * traits classes used to get default tolerances for comparing numbers
26  * of a given precision.
27  *
28  * @details
29  * A relative tolerance is used for comparing large
30  * numbers and an absolute tolerance is used for comparing small numbers.
31  * these defaults are not universal and will not work well in all situations.
32  *
33  * see also:
34  * https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
35  */
36 template <typename n_t>
37 struct equal_tt {};
38 
39 #define declare_equal_tt(cpp_t, atol, rtol) \
40 /** Specialization for cpp_t with default absTol and relTol */ \
41 template <> \
42 struct equal_tt<cpp_t> \
43 { \
44  static cpp_t absTol() { return atol; } \
45  static cpp_t relTol() { return rtol; } \
46 };
47 
48 declare_equal_tt(float, 10.0f*std::numeric_limits<float>::epsilon(),
49  std::numeric_limits<float>::epsilon())
50 
51 declare_equal_tt(double, 10.0*std::numeric_limits<double>::epsilon(),
52  std::numeric_limits<float>::epsilon())
53 
54 declare_equal_tt(long double, std::numeric_limits<double>::epsilon(),
55  std::numeric_limits<double>::epsilon())
56 
57 /** Compare two floating point numbers. absTol handles comparing numbers very
58  * close to zero. relTol handles comparing larger values.
59  */
60 template <typename T>
61 bool equal(T a, T b,
62  T relTol = equal_tt<T>::relTol(), T absTol = equal_tt<T>::absTol(),
63  typename std::enable_if<std::is_floating_point<T>::value>::type* = 0)
64 {
65  // for numbers close to zero
66  T diff = std::abs(a - b);
67  if (diff <= absTol)
68  return true;
69  // realtive difference for larger values
70  a = std::abs(a);
71  b = std::abs(b);
72  b = (b > a) ? b : a;
73  b *= relTol;
74  if (diff <= b)
75  return true;
76  return false;
77 }
78 
79 /// Compare two integral numbers.
80 template <typename T>
81 bool equal(T a, T b, T relTol = 0, T absTol = 0,
82  typename std::enable_if<std::is_integral<T>::value>::type* = 0)
83 {
84  (void)relTol;
85  (void)absTol;
86  return a == b;
87 }
88 
89 /** Compare two floating point numbers. This overload may be used in regression
90  * tests or other contexts where a diagnostic error message should be reported
91  * if the numbers are not equal.
92  */
93 template <typename T>
94 bool equal(T a, T b, std::string &diagnostic,
95  T relTol = equal_tt<T>::relTol(), T absTol = equal_tt<T>::absTol(),
96  typename std::enable_if<std::is_floating_point<T>::value>::type* = 0)
97 {
98  // for numbers close to zero
99  T diff = std::abs(a - b);
100  if (diff <= absTol)
101  return true;
102  // realtive difference for larger values
103  T aa = std::abs(a);
104  T bb = std::abs(b);
105  bb = (bb > aa) ? bb : aa;
106  T cc = bb*relTol;
107  if (diff <= cc)
108  return true;
109  // a and b are not equal format the diagnostic
110  T eps = std::numeric_limits<T>::epsilon();
111  std::ostringstream os;
112  os << max_prec(T) << a << " != " << max_prec(T) << b
113  << " relTol=" << max_prec(T) << relTol
114  << " absTol=" << max_prec(T) << absTol
115  << " |a-b|=" << max_prec(T) << diff
116  << " |a-b|/eps=" << max_prec(T) << diff/eps
117  << " max(|a|,|b|)*relTol=" << max_prec(T) << cc
118  << " |a-b|/max(|a|,|b|)=" << max_prec(T) << diff/bb
119  << " eps(" << typeid(a).name() << sizeof(T) << ")="
120  << max_prec(T) << eps;
121  diagnostic = os.str();
122  return false;
123 }
124 
125 /** Compare two integral numbers. This overload may be used in regression
126  * tests or other contexts where a diagnostic error message should be reported
127  * if the numbers are not equal.
128  */
129 template <typename T>
130 bool equal(T a, T b, std::string &diagnostic, T relTol = 0, T absTol = 0,
131  typename std::enable_if<std::is_integral<T>::value>::type* = 0)
132 {
133  (void)relTol;
134  (void)absTol;
135  if (a == b)
136  return true;
137  // a and b are not equal format the diagnostic
138  std::ostringstream os;
139  os << typeid(a).name() << sizeof(T) << " " << a << " != " << b;
140  diagnostic = os.str();
141  return false;
142 }
143 
144 /** Error codes returned by bool equal(const const_p_teca_variant_array &array1,
145  const const_p_teca_variant_array &array2,
146  double absTol, double relTol, int errorNo,
147  std::string &errorStr);
148  * | Code | Meaning |
149  * |------------------|---------|
150  * | no_error | no error |
151  * | length_missmatch | length missmatch |
152  * | type_missmatch | type missmatch |
153  * | invalid_value | invalid value detected (NaN, inf) |
154  * | value_missmatch | a value failed to compare within the tolerance |
155  */
157 {
158  enum {no_error = 0,
159  invalid_value = 1,
160  length_missmatch = 2,
161  type_missmatch = 3,
162  value_missmatch = 4,
163  unsupported_type = 5
164  };
165 };
166 
167 /** Compare two variant arrays elementwise for equality. If the arrays fail to
168  * compare within the specified tolerance errorNo will contain one of the
169  * equal_error enumerations and errorStr will conatin a diagnostic message
170  * describing the failure.
171  */
172 bool equal(const const_p_teca_variant_array &array1,
173  const const_p_teca_variant_array &array2,
174  double absTol, double relTol, int &errorNo,
175  std::string &errorStr);
176 
177 /// Less than or equal to predicate
178 template<typename data_t>
179 struct leq
180 { static bool eval(const data_t &l, const data_t &r) { return l <= r; } };
181 
182 /// Greater than or equal to predicate
183 template<typename data_t>
184 struct geq
185 { static bool eval(const data_t &l, const data_t &r) { return l >= r; } };
186 
187 /// Less than predicate
188 template<typename data_t>
189 struct lt
190 { static bool eval(const data_t &l, const data_t &r) { return l < r; } };
191 
192 /// Greater than predicate
193 template<typename data_t>
194 struct gt
195 { static bool eval(const data_t &l, const data_t &r) { return l > r; } };
196 
197 /// comparator implementing bracket for ascending input arrays
198 template<typename data_t>
200 {
201  // m_0 is an index into the data, m_1 = m_0 + 1
202  // comparitors defining the bracket orientation. for data in
203  // ascending order: val >= data[m_0] && val <= data[m_1]
204  using comp0_t = geq<data_t>;
205  using comp1_t = leq<data_t>;
206 
207  // m_0 is an index into the data, m_1 = m_0 + 1
208  // get the id of the smaller value (lower == true)
209  // or the larger value (lower == false)
210  static unsigned long get_id(bool lower,
211  unsigned long m_0, unsigned long m_1)
212  {
213  if (lower)
214  return m_0;
215  return m_1;
216  }
217 };
218 
219 /// comparator implementing bracket for descending input arrays
220 template<typename data_t>
222 {
223  // m_0 is an index into the data, m_1 = m_0 + 1
224  // comparitors defining the bracket orientation. for data in
225  // descending order: val <= data[m_0] && val >= data[m_1]
226  using comp0_t = leq<data_t>;
227  using comp1_t = geq<data_t>;
228 
229  // m_0 is an index into the data, m_1 = m_0 + 1
230  // get the id of the smaller value (lower == true)
231  // or the larger value (lower == false)
232  static unsigned long get_id(bool lower,
233  unsigned long m_0, unsigned long m_1)
234  {
235  if (lower)
236  return m_1;
237  return m_0;
238  }
239 };
240 
241 /** binary search that will locate index bounding the value above or below
242  * such that data[i] <= val or val <= data[i+1] depending on the value of
243  * lower. return 0 if the value is found. the comp0 and comp1 template
244  * parameters let us operate on both ascending and descending input. defaults
245  * are set for ascending inputs.
246  */
247 template <typename data_t, typename bracket_t = ascend_bracket<data_t>>
248 int index_of(const data_t *data, unsigned long l, unsigned long r,
249  data_t val, bool lower, unsigned long &id)
250 {
251  unsigned long m_0 = (r + l)/2;
252  unsigned long m_1 = m_0 + 1;
253 
254  if (m_0 == r)
255  {
256  if (equal(val, data[m_0]))
257  {
258  id = m_0;
259  return 0;
260  }
261  // not found
262  return -1;
263  }
264  else
265  if (bracket_t::comp0_t::eval(val, data[m_0]) &&
266  bracket_t::comp1_t::eval(val, data[m_1]))
267  {
268  // found a bracket around the value
269  if (equal(val, data[m_0]))
270  id = m_0;
271  else
272  if (equal(val, data[m_1]))
273  id = m_1;
274  else
275  id = bracket_t::get_id(lower, m_0, m_1);
276  return 0;
277  }
278  else
279  if (bracket_t::comp1_t::eval(val, data[m_0]))
280  {
281  // split range to the left
282  return teca_coordinate_util::index_of<data_t, bracket_t>(
283  data, l, m_0, val, lower, id);
284  }
285  else
286  {
287  // split the range to the right
288  return teca_coordinate_util::index_of<data_t, bracket_t>(
289  data, m_1, r, val, lower, id);
290  }
291 
292  // not found
293  return -1;
294 }
295 
296 /** binary search that will locate index of the given value. return 0 if the
297  * value is found.
298  */
299 template <typename T>
300 int index_of(const T *data, size_t l, size_t r, T val, unsigned long &id)
301 {
302  unsigned long m_0 = (r + l)/2;
303  unsigned long m_1 = m_0 + 1;
304 
305  if (m_0 == r)
306  {
307  // need this test when len of data is 1
308  if (equal(val, data[m_0], T(8)*std::numeric_limits<T>::epsilon()))
309  {
310  id = m_0;
311  return 0;
312  }
313  // not found
314  return -1;
315  }
316  else
317  if (equal(val, data[m_0], T(8)*std::numeric_limits<T>::epsilon()))
318  {
319  id = m_0;
320  return 0;
321  }
322  else
323  if (equal(val, data[m_1], T(8)*std::numeric_limits<T>::epsilon()))
324  {
325  id = m_1;
326  return 0;
327  }
328  else
329  if (val < data[m_0])
330  {
331  // split range to the left
332  return teca_coordinate_util::index_of(data, l, m_0, val, id);
333  }
334  else
335  {
336  // split the range to the right
337  return teca_coordinate_util::index_of(data, m_1, r, val, id);
338  }
339 
340  // not found
341  return -1;
342 }
343 
344 /** Convert bounds to extents. return non-zero if the requested bounds are
345  * not in the given coordinate arrays. coordinate arrays must not be empty.
346  */
347 int bounds_to_extent(const double *bounds,
349  const const_p_teca_variant_array &z, unsigned long *extent);
350 
351 int bounds_to_extent(const double *bounds,
352  const const_p_teca_variant_array &x, unsigned long *extent);
353 
354 int bounds_to_extent(const double *bounds, const teca_metadata &md,
355  unsigned long *extent);
356 
357 /** Get the i,j,k cell index of point x,y,z in the given mesh. return 0 if
358  * successful.
359  */
360 template<typename T>
361 int index_of(const const_p_teca_cartesian_mesh &mesh, T x, T y, T z,
362  unsigned long &i, unsigned long &j, unsigned long &k)
363 {
364  const_p_teca_variant_array xc = mesh->get_x_coordinates();
365  const_p_teca_variant_array yc = mesh->get_y_coordinates();
366  const_p_teca_variant_array zc = mesh->get_z_coordinates();
367 
370  xc.get(),
371 
372  const NT *p_xc = std::dynamic_pointer_cast<TT>(xc)->get();
373  const NT *p_yc = std::dynamic_pointer_cast<TT>(yc)->get();
374  const NT *p_zc = std::dynamic_pointer_cast<TT>(zc)->get();
375 
376  unsigned long nx = xc->size();
377  unsigned long ny = yc->size();
378  unsigned long nz = zc->size();
379 
380  if (teca_coordinate_util::index_of(p_xc, 0, nx-1, x, true, i)
381  || teca_coordinate_util::index_of(p_yc, 0, ny-1, y, true, j)
382  || teca_coordinate_util::index_of(p_zc, 0, nz-1, z, true, k))
383  {
384  // out of bounds
385  return -1;
386  }
387 
388  // success
389  return 0;
390  )
391 
392  // coords are not a floating point type
393  return -1;
394 }
395 
396 /** given a human readable date string in YYYY-MM-DD hh:mm:ss format and a
397  * list of floating point offset times in the specified calendar and units find
398  * the closest time step. return 0 if successful see index_of for a description
399  * of lower, if clamp is true then when the date falls outside of the time
400  * values either the first or last time step is returned.
401  */
403  bool lower, bool clamp, const std::string &calendar,
404  const std::string &units, const std::string &date,
405  unsigned long &step);
406 
407 /** given a time value (val), associated time units (units), and calendar
408  * (calendar), return a human-readable rendering of the date (date) in a
409  * strftime-format (format). return 0 if successful.
410  */
411 int time_to_string(double val, const std::string &calendar,
412  const std::string &units, const std::string &format, std::string &date);
413 
414 /** build random access data structures for an indexed table. the index column
415  * gives each entity a unique id. the index is used to identify rows that
416  * belong in the entity. it is assumed that an entity occupies consecutive rows.
417  * the returns are: n_entities, the number of entities found; counts, the
418  * number of rows used by each entity; offsets, the starting row of each
419  * entity; ids, a new set of ids for the entities starting from 0
420  */
421 template <typename int_t>
422 void get_table_offsets(const int_t *index, unsigned long n_rows,
423  unsigned long &n_entities, std::vector<unsigned long> &counts,
424  std::vector<unsigned long> &offsets, std::vector<unsigned long> &ids)
425 {
426  // count unique number of steps and compute an array index
427  // from each time step.
428  n_entities = 1;
429  ids.resize(n_rows);
430  unsigned long n_m1 = n_rows - 1;
431  for (unsigned long i = 0; i < n_m1; ++i)
432  {
433  ids[i] = n_entities - 1;
434  if (index[i] != index[i+1])
435  ++n_entities;
436  }
437  ids[n_m1] = n_entities - 1;
438 
439  // compute num storms in each step
440  counts.resize(n_entities);
441  unsigned long q = 0;
442  for (unsigned long i = 0; i < n_entities; ++i)
443  {
444  counts[i] = 1;
445  while ((q < n_m1) && (index[q] == index[q+1]))
446  {
447  ++counts[i];
448  ++q;
449  }
450  ++q;
451  }
452 
453  // compute the offset to the first storm in each step
454  offsets.resize(n_entities);
455  offsets[0] = 0;
456  for (unsigned long i = 1; i < n_entities; ++i)
457  offsets[i] = offsets[i-1] + counts[i-1];
458 }
459 
460 /** 0th order (nearest neighbor) interpolation for nodal data on a stretched
461  * Cartesian mesh. This overload implements the general 3D case.
462  * cx, cy, cz is the location to interpolate to
463  * p_x, p_y, p_z array arrays containing the source coordinates with extents
464  * [0, ihi, 0, jhi, 0, khi]
465  * p_data is the field to interpolate from
466  * val is the result
467  * returns 0 if successful, an error occurs if cx, cy, cz is outside of the
468  * source coordinate system
469  */
470 template<typename CT, typename DT>
471 int interpolate_nearest(CT cx, CT cy, CT cz,
472  const CT *p_x, const CT *p_y, const CT *p_z,
473  const DT *p_data, unsigned long ihi, unsigned long jhi,
474  unsigned long khi, unsigned long nx, unsigned long nxy,
475  DT &val)
476 {
477  // get i,j of node less than cx,cy
478  unsigned long i = 0;
479  unsigned long j = 0;
480  unsigned long k = 0;
481 
482  if ((ihi && teca_coordinate_util::index_of(p_x, 0, ihi, cx, true, i))
483  || (jhi && teca_coordinate_util::index_of(p_y, 0, jhi, cy, true, j))
484  || (khi && teca_coordinate_util::index_of(p_z, 0, khi, cz, true, k)))
485  {
486  // cx,cy,cz is outside the coordinate axes
487  return -1;
488  }
489 
490  // get i,j of node greater than cx,cy
491  unsigned long ii = std::min(i + 1, ihi);
492  unsigned long jj = std::min(j + 1, jhi);
493  unsigned long kk = std::min(k + 1, khi);
494 
495  // get index of nearest node
496  unsigned long p = (cx - p_x[i]) <= (p_x[ii] - cx) ? i : ii;
497  unsigned long q = (cy - p_y[j]) <= (p_y[jj] - cy) ? j : jj;
498  unsigned long r = (cz - p_z[k]) <= (p_z[kk] - cz) ? k : kk;
499 
500  // assign value from nearest node
501  val = p_data[p + nx*q + nxy*r];
502  return 0;
503 }
504 
505 /** 0th order (nearest neighbor) interpolation for nodal data on a stretched
506  * Cartesian mesh. This overload implements the special case where both source
507  * and target mesh data are in a 2D x-y plane using fewer operations than the
508  * general 3D implementation.
509  * cx, cy, cz is the location to interpolate to
510  * p_x, p_y, p_z array arrays containing the source coordinates with extents
511  * [0, ihi, 0, jhi, 0, khi]
512  * p_data is the field to interpolate from
513  * val is the result
514  * returns 0 if successful, an error occurs if cx, cy, cz is outside of the
515  * source coordinate system
516  */
517 template<typename coord_t, typename data_t>
518 int interpolate_nearest(coord_t cx, coord_t cy, const coord_t *p_x,
519  const coord_t *p_y, const data_t *p_data, unsigned long ihi,
520  unsigned long jhi, unsigned long nx, data_t &val)
521 {
522  // get i,j of node less than cx,cy
523  unsigned long i = 0;
524  unsigned long j = 0;
525 
526  if ((ihi && teca_coordinate_util::index_of(p_x, 0, ihi, cx, true, i))
527  || (jhi && teca_coordinate_util::index_of(p_y, 0, jhi, cy, true, j)))
528  {
529  // cx,cy is outside the coordinate axes
530  return -1;
531  }
532 
533  // get i,j of node greater than cx,cy
534  unsigned long ii = std::min(i + 1, ihi);
535  unsigned long jj = std::min(j + 1, jhi);
536 
537  // get index of nearest node
538  unsigned long p = (cx - p_x[i]) <= (p_x[ii] - cx) ? i : ii;
539  unsigned long q = (cy - p_y[j]) <= (p_y[jj] - cy) ? j : jj;
540 
541  // assign value from nearest node
542  val = p_data[p + nx*q];
543 
544  return 0;
545 }
546 
547 /** 1st order (linear) interpolation for nodal data on stretched Cartesian
548  * mesh. This overload implements the general 3D case.
549  * cx, cy, cz is the location to interpolate to
550  * p_x, p_y, p_z array arrays containing the source coordinates with extents
551  * [0, ihi, 0, jhi, 0, khi]
552  * p_data is the field to interpolate from
553  * val is the result
554  * returns 0 if successful, an error occurs if cx, cy, cz is outside of the
555  * source coordinate system
556  */
557 template<typename CT, typename DT>
558 int interpolate_linear(CT cx, CT cy, CT cz,
559  const CT *p_x, const CT *p_y, const CT *p_z,
560  const DT *p_data, unsigned long ihi, unsigned long jhi,
561  unsigned long khi, unsigned long nx, unsigned long nxy,
562  DT &val)
563 {
564  // get i,j of node less than cx,cy
565  unsigned long i = 0;
566  unsigned long j = 0;
567  unsigned long k = 0;
568 
569  if ((ihi && teca_coordinate_util::index_of(p_x, 0, ihi, cx, true, i))
570  || (jhi && teca_coordinate_util::index_of(p_y, 0, jhi, cy, true, j))
571  || (khi && teca_coordinate_util::index_of(p_z, 0, khi, cz, true, k)))
572  {
573  // cx,cy,cz is outside the coordinate axes
574  return -1;
575  }
576 
577  // get i,j of node greater than cx,cy,cz
578  unsigned long ii = std::min(i + 1, ihi);
579  unsigned long jj = std::min(j + 1, jhi);
580  unsigned long kk = std::min(k + 1, khi);
581 
582  // compute weights
583  CT wx = ii == i ? 0 : (cx - p_x[i])/(p_x[ii] - p_x[i]);
584  CT wy = jj == j ? 0 : (cy - p_y[j])/(p_y[jj] - p_y[j]);
585  CT wz = kk == k ? 0 : (cz - p_z[k])/(p_z[kk] - p_z[k]);
586 
587  CT vx = CT(1) - wx;
588  CT vy = CT(1) - wy;
589  CT vz = CT(1) - wz;
590 
591  // interpolate
592  val = vx*vy*vz*p_data[ i + j*nx + k*nxy]
593  + wx*vy*vz*p_data[ii + j*nx + k*nxy]
594  + wx*wy*vz*p_data[ii + jj*nx + k*nxy]
595  + vx*wy*vz*p_data[ i + jj*nx + k*nxy]
596  + vx*vy*wz*p_data[ i + j*nx + kk*nxy]
597  + wx*vy*wz*p_data[ii + j*nx + kk*nxy]
598  + wx*wy*wz*p_data[ii + jj*nx + kk*nxy]
599  + vx*wy*wz*p_data[ i + jj*nx + kk*nxy];
600 
601  return 0;
602 }
603 
604 /** 1st order (linear) interpolation for nodal data on stretched Cartesian mesh.
605  * This overload implements the special case where both source and target data
606  * are in a 2D x-y plane using fewer operations than the general 3D
607  * implementation.
608  * cx, cy, cz is the location to interpolate to
609  * p_x, p_y, p_z array arrays containing the source coordinates with extents
610  * [0, ihi, 0, jhi, 0, khi]
611  * p_data is the field to interpolate from
612  * val is the result
613  * returns 0 if successful, an error occurs if cx, cy, cz is outside of the
614  * source coordinate system
615  */
616 template<typename CT, typename DT>
617 int interpolate_linear(CT cx, CT cy, const CT *p_x, const CT *p_y,
618  const DT *p_data, unsigned long ihi, unsigned long jhi,
619  unsigned long nx, DT &val)
620 {
621  // get i,j of node less than cx,cy
622  unsigned long i = 0;
623  unsigned long j = 0;
624 
625  if ((ihi && teca_coordinate_util::index_of(p_x, 0, ihi, cx, true, i))
626  || (jhi && teca_coordinate_util::index_of(p_y, 0, jhi, cy, true, j)))
627  {
628  // cx,cy is outside the coordinate axes
629  return -1;
630  }
631 
632  // get i,j of node greater than cx,cy
633  unsigned long ii = std::min(i + 1, ihi);
634  unsigned long jj = std::min(j + 1, jhi);
635 
636  // compute weights
637  CT wx = ii == i ? 0 : (cx - p_x[i])/(p_x[ii] - p_x[i]);
638  CT wy = jj == j ? 0 : (cy - p_y[j])/(p_y[jj] - p_y[j]);
639 
640  CT vx = CT(1) - wx;
641  CT vy = CT(1) - wy;
642 
643  // interpolate
644  val = vx*vy*p_data[ i + j*nx]
645  + wx*vy*p_data[ii + j*nx]
646  + wx*wy*p_data[ii + jj*nx]
647  + vx*wy*p_data[ i + jj*nx];
648 
649  return 0;
650 }
651 
652 /// A functor templated on order of accuracy for above Cartesian mesh interpolants
653 template<int> struct interpolate_t;
654 
655 /// Zero'th order interpolant specialization
656 template<> struct interpolate_t<0>
657 {
658  // 3D
659  template<typename CT, typename DT>
660  int operator()(CT tx, CT ty, CT tz, const CT *sx, const CT *sy,
661  const CT *sz, const DT *sa, unsigned long ihi, unsigned long jhi,
662  unsigned long khi, unsigned long nx, unsigned long nxy, DT &ta)
663  {
665  sx,sy,sz,sa, ihi,jhi,khi, nx,nxy, ta);
666  }
667 
668  // 2D x-y plane
669  template<typename CT, typename DT>
670  int operator()(CT tx, CT ty, const CT *sx, const CT *sy,
671  const DT *sa, unsigned long ihi, unsigned long jhi,
672  unsigned long nx, DT &ta)
673  {
675  sx,sy,sa, ihi,jhi, nx, ta);
676  }
677 };
678 
679 /// First order interpolant specialization
680 template<> struct interpolate_t<1>
681 {
682  // 3D
683  template<typename CT, typename DT>
684  int operator()(CT tx, CT ty, CT tz, const CT *sx, const CT *sy,
685  const CT *sz, const DT *sa, unsigned long ihi, unsigned long jhi,
686  unsigned long khi, unsigned long nx,unsigned long nxy, DT &ta)
687  {
689  sx,sy,sz,sa, ihi,jhi,khi, nx,nxy, ta);
690  }
691 
692  // 2D x-y plane
693  template<typename CT, typename DT>
694  int operator()(CT tx, CT ty, const CT *sx, const CT *sy,
695  const DT *sa, unsigned long ihi, unsigned long jhi,
696  unsigned long nx, DT &ta)
697  {
699  sx,sy,sa, ihi,jhi, nx, ta);
700  }
701 };
702 
703 /// return 0 if the centering is one of the values defined in teca_array_attributes
704 int validate_centering(int centering);
705 
706 /// convert from a cell extent to a face, edge or point centered extent
707 template <typename num_t>
708 int convert_cell_extent(num_t *extent, int centering)
709 {
710  switch (centering)
711  {
712  case teca_array_attributes::invalid_value:
713  TECA_ERROR("detected invalid_value in centering")
714  return -1;
715  break;
716  case teca_array_attributes::cell_centering:
717  break;
718  case teca_array_attributes::x_face_centering:
719  extent[1] += 1;
720  break;
721  case teca_array_attributes::y_face_centering:
722  extent[3] += 1;
723  break;
724  case teca_array_attributes::z_face_centering:
725  extent[5] += 1;
726  break;
727  case teca_array_attributes::x_edge_centering:
728  extent[3] += 1;
729  extent[5] += 1;
730  break;
731  case teca_array_attributes::y_edge_centering:
732  extent[1] += 1;
733  extent[5] += 1;
734  break;
735  case teca_array_attributes::z_edge_centering:
736  extent[1] += 1;
737  extent[3] += 1;
738  break;
739  case teca_array_attributes::point_centering:
740  extent[1] += 1;
741  extent[3] += 1;
742  extent[5] += 1;
743  break;
744  case teca_array_attributes::no_centering:
745  break;
746  default:
747  TECA_ERROR("this centering is undefined " << centering)
748  return -1;
749  }
750  return 0;
751 }
752 
753 /** Given Cartesian mesh metadata extract whole_extent and bounds
754  * if bounds metadata is not already present then it is initialized
755  * from coordinate arrays. It's an error if whole_extent or coordinate
756  * arrays are not present. return zero if successful.
757  */
759  unsigned long *whole_extent, double *bounds);
760 
761 /// get the mesh's bounds from the coordinate axis arrays
764  double *bounds);
765 
766 /** Check that one Cartesian region covers the other coordinates must be in
767  * ascending order. assumes that both regions are specified in ascending order.
768  */
769 template <typename num_t>
770 int covers_ascending(const num_t *whole, const num_t *part)
771 {
772  if ((part[0] >= whole[0]) && (part[0] <= whole[1]) &&
773  (part[1] >= whole[0]) && (part[1] <= whole[1]) &&
774  (part[2] >= whole[2]) && (part[2] <= whole[3]) &&
775  (part[3] >= whole[2]) && (part[3] <= whole[3]) &&
776  (part[4] >= whole[4]) && (part[4] <= whole[5]) &&
777  (part[5] >= whole[4]) && (part[5] <= whole[5]))
778  return 1;
779  return 0;
780 }
781 
782 /** Check that one Cartesian region covers the other, taking into account the
783  * order of the coordinates. assumes that the regions are specified in the same
784  * orientation.
785  */
786 template <typename num_t>
787 int covers(const num_t *whole, const num_t *part)
788 {
789  bool x_ascend = whole[0] <= whole[1];
790  bool y_ascend = whole[2] <= whole[3];
791  bool z_ascend = whole[4] <= whole[5];
792  if (((x_ascend &&
793  (part[0] >= whole[0]) && (part[0] <= whole[1]) &&
794  (part[1] >= whole[0]) && (part[1] <= whole[1])) ||
795  (!x_ascend &&
796  (part[0] <= whole[0]) && (part[0] >= whole[1]) &&
797  (part[1] <= whole[0]) && (part[1] >= whole[1]))) &&
798  ((y_ascend &&
799  (part[2] >= whole[2]) && (part[2] <= whole[3]) &&
800  (part[3] >= whole[2]) && (part[3] <= whole[3])) ||
801  (!y_ascend &&
802  (part[2] <= whole[2]) && (part[2] >= whole[3]) &&
803  (part[3] <= whole[2]) && (part[3] >= whole[3]))) &&
804  ((z_ascend &&
805  (part[4] >= whole[4]) && (part[4] <= whole[5]) &&
806  (part[5] >= whole[4]) && (part[5] <= whole[5])) ||
807  (!z_ascend &&
808  (part[4] <= whole[4]) && (part[4] >= whole[5]) &&
809  (part[5] <= whole[4]) && (part[5] >= whole[5]))))
810  return 1;
811  return 0;
812 }
813 
814 /** check that two Cartesian regions have the same orientation ie they are
815  * either both specified in ascending or descending order.
816  */
817 template <typename num_t>
818 int same_orientation(const num_t *whole, const num_t *part)
819 {
820  if ((((whole[0] <= whole[1]) && (part[0] <= part[1])) ||
821  ((whole[0] >= whole[1]) && (part[0] >= part[1]))) &&
822  (((whole[2] <= whole[3]) && (part[2] <= part[3])) ||
823  ((whole[2] >= whole[3]) && (part[2] >= part[3]))) &&
824  (((whole[4] <= whole[5]) && (part[4] <= part[5])) ||
825  ((whole[4] >= whole[5]) && (part[4] >= part[5]))))
826  return 1;
827  return 0;
828 }
829 
830 /** where array dimensions specified by nx_max, ny_max, and nz_max are 1, and
831  * the extent would be out of bounds, set the extent to [0, 0]. If verbose is
832  * set, a warning is reported when the extent was clamped in one or more
833  * directions. The return is non zero if any direction was clamped and 0
834  * otherwise.
835  */
836 int clamp_dimensions_of_one(unsigned long nx_max, unsigned long ny_max,
837  unsigned long nz_max, unsigned long *extent, bool verbose);
838 
839 /** Return 0 if the passed extent does not exceed array dimensions specified in
840  * nx_max, ny_max, and nz_max. If verbose is set, an error is reported via
841  * TECA_ERROR when the extent would be out of bounds.
842  */
843 int validate_extent(unsigned long nx_max, unsigned long ny_max,
844  unsigned long nz_max, unsigned long *extent, bool verbose);
845 
846 
847 /// compares a set of arrays against a reference array
849 {
850 public:
851  /// set the array to which others will be compared to
852  void set_reference_array(const std::string &a_source,
853  const std::string &a_name, const std::string a_units,
854  const const_p_teca_variant_array &a_array);
855 
856  /// add an array to check against the reference
857  void append_array(const std::string &a_source,
858  const std::string &a_name, const std::string &a_units,
859  const const_p_teca_variant_array &a_array);
860 
861  /// error codes potentially returned from ::validate
862  enum {no_error = 0,
863  invalid_value = 1,
864  length_missmatch = 2,
865  type_missmatch = 3,
866  value_missmatch = 4,
867  unsupported_type = 5,
868  units_missmatch = 6
869  };
870 
871  /** Compare all the arrays in the collection against the reference returns
872  * 0 if all arrays in the collection are equal to the reference. When an
873  * array does not compare equal to the reference array a descritpion
874  * explaining why is returned in errorStr.
875  */
876  int validate(const std::string &a_descriptor,
877  double a_abs_tol, double a_rel_tol,
878  std::string &errorStr);
879 
880 private:
881  const_p_teca_variant_array m_reference;
882  std::string m_reference_source;
883  std::string m_reference_name;
884  std::string m_reference_units;
885 
886  std::vector<const_p_teca_variant_array> m_arrays;
887  std::vector<std::string> m_array_sources;
888  std::vector<std::string> m_array_names;
889  std::vector<std::string> m_array_units;
890 };
891 
892 /// Check that cooridnate arrays from different sources match a refrence array
893 /** Compares names, units, and values of coordinate axis arrays.
894  */
896 {
897 public:
899  m_absolute_tolerance(equal_tt<float>::absTol()),
900  m_relative_tolerance(equal_tt<float>::relTol())
901  {}
902 
903  /** Adds a time axis to validate. if provides_time is true then the axis
904  * becomes the reference to which all others are compared. returns 0 if
905  * necessary metadata was present and non zero if the necessary information
906  * was not present.
907  */
908  int add_time_axis(const std::string &source,
909  const teca_metadata &coords, const teca_metadata &atts,
910  bool provides_time);
911 
912  /** Adds anx-coordinate axis to validate. if provides_geometry is true then
913  * the axis becomes the reference to which all others are compared. returns
914  * 0 if necessary metadata was present and non zero if the necessary
915  * information was not present.
916  */
917  int add_x_coordinate_axis(const std::string &source,
918  const teca_metadata &coords, const teca_metadata &atts,
919  bool provides_geometry);
920 
921  /** Adds anx-coordinate axis to validate. if provides_geometry is true then
922  * the axis becomes the reference to which all others are compared. returns
923  * 0 if necessary metadata was present and non zero if the necessary
924  * information was not present.
925  */
926  int add_y_coordinate_axis(const std::string &source,
927  const teca_metadata &coords, const teca_metadata &atts,
928  bool provides_geometry);
929 
930  /** Adds anx-coordinate axis to validate. if provides_geometry is true then
931  * the axis becomes the reference to which all others are compared. returns
932  * 0 if necessary metadata was present and non zero if the necessary
933  * information was not present.
934  */
935  int add_z_coordinate_axis(const std::string &source,
936  const teca_metadata &coords, const teca_metadata &atts,
937  bool provides_geometry);
938 
939  /** runs the validation. returns 0 if all of the stored coordinate axes are
940  * equal to the reference axes. When an array does not compare equal to the
941  * reference array a descritpion explaining why is returned in errorStr.
942  */
943  int validate_spatial_coordinate_axes(std::string &errorStr);
944 
945  /** runs the validation. returns 0 if all of the stored time axes are
946  * equal to the reference axis. When an array does not compare equal to the
947  * reference array a descritpion explaining why is returned in errorStr.
948  */
949  int validate_time_axis(std::string &errorStr);
950 
951 private:
952  double m_absolute_tolerance;
953  double m_relative_tolerance;
958 };
959 
960 };
961 #endif
teca_coordinate_util::teca_validate_arrays
compares a set of arrays against a reference array
Definition: teca_coordinate_util.h:848
teca_variant_array.h
teca_coordinate_util::ascend_bracket
comparator implementing bracket for ascending input arrays
Definition: teca_coordinate_util.h:199
teca_coordinate_util::teca_coordinate_axis_validator::add_x_coordinate_axis
int add_x_coordinate_axis(const std::string &source, const teca_metadata &coords, const teca_metadata &atts, bool provides_geometry)
teca_coordinate_util::interpolate_t
A functor templated on order of accuracy for above Cartesian mesh interpolants.
Definition: teca_coordinate_util.h:653
teca_metadata
A generic container for meta data in the form of name=value pairs.
Definition: teca_metadata.h:18
teca_coordinate_util::teca_validate_arrays::validate
int validate(const std::string &a_descriptor, double a_abs_tol, double a_rel_tol, std::string &errorStr)
teca_coordinate_util::covers
int covers(const num_t *whole, const num_t *part)
Definition: teca_coordinate_util.h:787
teca_variant_array_impl
The concrete implementation of our type agnostic container for contiguous arrays.
Definition: teca_variant_array.h:23
teca_coordinate_util::convert_cell_extent
int convert_cell_extent(num_t *extent, int centering)
convert from a cell extent to a face, edge or point centered extent
Definition: teca_coordinate_util.h:708
teca_coordinate_util
Codes dealing with operations on coordinate systems.
Definition: teca_coordinate_util.h:22
teca_coordinate_util::teca_coordinate_axis_validator::validate_spatial_coordinate_axes
int validate_spatial_coordinate_axes(std::string &errorStr)
teca_coordinate_util::validate_centering
int validate_centering(int centering)
return 0 if the centering is one of the values defined in teca_array_attributes
teca_coordinate_util::time_to_string
int time_to_string(double val, const std::string &calendar, const std::string &units, const std::string &format, std::string &date)
teca_calcalcs::date
int date(double val, int *year, int *month, int *day, int *hour, int *minute, double *second, const char *dataunits, const char *calendar_name)
teca_coordinate_util::equal
bool equal(T a, T b, T relTol=equal_tt< T >::relTol(), T absTol=equal_tt< T >::absTol(), typename std::enable_if< std::is_floating_point< T >::value >::type *=0)
Definition: teca_coordinate_util.h:61
teca_coordinate_util::index_of
int index_of(const data_t *data, unsigned long l, unsigned long r, data_t val, bool lower, unsigned long &id)
Definition: teca_coordinate_util.h:248
teca_variant_array_impl::get
T & get(unsigned long i)
Get the ith value.
Definition: teca_variant_array.h:565
teca_coordinate_util::interpolate_nearest
int interpolate_nearest(CT cx, CT cy, CT cz, const CT *p_x, const CT *p_y, const CT *p_z, const DT *p_data, unsigned long ihi, unsigned long jhi, unsigned long khi, unsigned long nx, unsigned long nxy, DT &val)
Definition: teca_coordinate_util.h:471
teca_coordinate_util::time_step_of
int time_step_of(const const_p_teca_variant_array &time, bool lower, bool clamp, const std::string &calendar, const std::string &units, const std::string &date, unsigned long &step)
const_p_teca_variant_array
std::shared_ptr< const teca_variant_array > const_p_teca_variant_array
Definition: teca_variant_array.h:22
teca_coordinate_util::equal_tt
traits classes used to get default tolerances for comparing numbers of a given precision.
Definition: teca_coordinate_util.h:37
teca_coordinate_util::gt
Greater than predicate.
Definition: teca_coordinate_util.h:194
teca_coordinate_util::bounds_to_extent
int bounds_to_extent(const double *bounds, const const_p_teca_variant_array &x, const const_p_teca_variant_array &y, const const_p_teca_variant_array &z, unsigned long *extent)
TEMPLATE_DISPATCH_FP
#define TEMPLATE_DISPATCH_FP(t, p, body)
Executes the code in body if p is a t<nt> where nt is a floating point type.
Definition: teca_variant_array.h:193
teca_coordinate_util::interpolate_linear
int interpolate_linear(CT cx, CT cy, CT cz, const CT *p_x, const CT *p_y, const CT *p_z, const DT *p_data, unsigned long ihi, unsigned long jhi, unsigned long khi, unsigned long nx, unsigned long nxy, DT &val)
Definition: teca_coordinate_util.h:558
teca_coordinate_util::get_cartesian_mesh_bounds
int get_cartesian_mesh_bounds(const const_p_teca_variant_array x, const const_p_teca_variant_array y, const const_p_teca_variant_array z, double *bounds)
get the mesh's bounds from the coordinate axis arrays
teca_coordinate_util::get_cartesian_mesh_extent
int get_cartesian_mesh_extent(const teca_metadata &md, unsigned long *whole_extent, double *bounds)
teca_coordinate_util::equal_error
Definition: teca_coordinate_util.h:156
teca_coordinate_util::teca_validate_arrays::set_reference_array
void set_reference_array(const std::string &a_source, const std::string &a_name, const std::string a_units, const const_p_teca_variant_array &a_array)
set the array to which others will be compared to
teca_coordinate_util::teca_coordinate_axis_validator::add_z_coordinate_axis
int add_z_coordinate_axis(const std::string &source, const teca_metadata &coords, const teca_metadata &atts, bool provides_geometry)
teca_coordinate_util::clamp_dimensions_of_one
int clamp_dimensions_of_one(unsigned long nx_max, unsigned long ny_max, unsigned long nz_max, unsigned long *extent, bool verbose)
teca_coordinate_util::teca_validate_arrays::append_array
void append_array(const std::string &a_source, const std::string &a_name, const std::string &a_units, const const_p_teca_variant_array &a_array)
add an array to check against the reference
teca_coordinate_util::get_table_offsets
void get_table_offsets(const int_t *index, unsigned long n_rows, unsigned long &n_entities, std::vector< unsigned long > &counts, std::vector< unsigned long > &offsets, std::vector< unsigned long > &ids)
Definition: teca_coordinate_util.h:422
teca_coordinate_util::descend_bracket
comparator implementing bracket for descending input arrays
Definition: teca_coordinate_util.h:221
teca_coordinate_util::teca_coordinate_axis_validator::validate_time_axis
int validate_time_axis(std::string &errorStr)
teca_coordinate_util::covers_ascending
int covers_ascending(const num_t *whole, const num_t *part)
Definition: teca_coordinate_util.h:770
max_prec
#define max_prec(T)
For printing data as ASCII with the maximum supported numerical precision.
Definition: teca_coordinate_util.h:18
teca_coordinate_util::same_orientation
int same_orientation(const num_t *whole, const num_t *part)
Definition: teca_coordinate_util.h:818
teca_coordinate_util::teca_coordinate_axis_validator::add_y_coordinate_axis
int add_y_coordinate_axis(const std::string &source, const teca_metadata &coords, const teca_metadata &atts, bool provides_geometry)
teca_coordinate_util::teca_coordinate_axis_validator
Check that cooridnate arrays from different sources match a refrence array.
Definition: teca_coordinate_util.h:895
teca_coordinate_util::geq
Greater than or equal to predicate.
Definition: teca_coordinate_util.h:184
teca_coordinate_util::validate_extent
int validate_extent(unsigned long nx_max, unsigned long ny_max, unsigned long nz_max, unsigned long *extent, bool verbose)
teca_coordinate_util::teca_coordinate_axis_validator::add_time_axis
int add_time_axis(const std::string &source, const teca_metadata &coords, const teca_metadata &atts, bool provides_time)
teca_coordinate_util::lt
Less than predicate.
Definition: teca_coordinate_util.h:189
TECA_ERROR
#define TECA_ERROR(_msg)
Constructs an error message and sends it to the stderr stream.
Definition: teca_common.h:138
teca_coordinate_util::leq
Less than or equal to predicate.
Definition: teca_coordinate_util.h:179