NASA Logo
Ocean Color Science Software

ocssw V2022
spline.h
Go to the documentation of this file.
1 /*
2  * spline.h
3  *
4  * simple cubic spline interpolation library without external
5  * dependencies
6  *
7  * ---------------------------------------------------------------------
8  * Copyright (C) 2011, 2014 Tino Kluge (ttk448 at gmail.com)
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  * ---------------------------------------------------------------------
23  *
24  */
25 
26 #ifndef TK_SPLINE_H
27 #define TK_SPLINE_H
28 
29 #include <cstdio>
30 #include <cassert>
31 #include <vector>
32 #include <algorithm>
33 
34 // unnamed namespace only because the implementation is in this
35 // header file and we don't want to export symbols to the obj files
36 namespace {
37 
38 namespace tk {
39 
40 // band matrix solver
41 class band_matrix {
42  private:
43  std::vector<std::vector<double> > m_upper; // upper band
44  std::vector<std::vector<double> > m_lower; // lower band
45  public:
46  band_matrix(){}; // constructor
47  band_matrix(int dim, int n_u, int n_l); // constructor
48  ~band_matrix(){}; // destructor
49  void resize(int dim, int n_u, int n_l); // init with dim,n_u,n_l
50  int dim() const; // matrix dimension
51  int num_upper() const {
52  return m_upper.size() - 1;
53  }
54  int num_lower() const {
55  return m_lower.size() - 1;
56  }
57  // access operator
58  double& operator()(int i, int j); // write
59  double operator()(int i, int j) const; // read
60  // we can store an additional diogonal (in m_lower)
61  double& saved_diag(int i);
62  double saved_diag(int i) const;
63  void lu_decompose();
64  std::vector<double> r_solve(const std::vector<double>& b) const;
65  std::vector<double> l_solve(const std::vector<double>& b) const;
66  std::vector<double> lu_solve(const std::vector<double>& b, bool is_lu_decomposed = false);
67 };
68 
69 // spline interpolation
70 class spline {
71  public:
72  enum bd_type { first_deriv = 1, second_deriv = 2 };
73 
74  private:
75  std::vector<double> m_x, m_y; // x,y coordinates of points
76  // interpolation parameters
77  // f(x) = a*(x-x_i)^3 + b*(x-x_i)^2 + c*(x-x_i) + y_i
78  std::vector<double> m_a, m_b, m_c; // spline coefficients
79  double m_b0, m_c0; // for left extrapol
80  bd_type m_left, m_right;
81  double m_left_value, m_right_value;
82  bool m_force_linear_extrapolation;
83 
84  public:
85  // set default boundary condition to be zero curvature at both ends
86  spline()
87  : m_left(second_deriv),
88  m_right(second_deriv),
89  m_left_value(0.0),
90  m_right_value(0.0),
91  m_force_linear_extrapolation(false) {
92  ;
93  }
94 
95  // optional, but if called it has to come be before set_points()
96  void set_boundary(bd_type left, double left_value, bd_type right, double right_value,
97  bool force_linear_extrapolation = false);
98  void set_points(const std::vector<double>& x, const std::vector<double>& y, bool cubic_spline = true);
99  double operator()(double x) const;
100 };
101 
102 // ---------------------------------------------------------------------
103 // implementation part, which could be separated into a cpp file
104 // ---------------------------------------------------------------------
105 
106 // band_matrix implementation
107 // -------------------------
108 
109 band_matrix::band_matrix(int dim, int n_u, int n_l) {
110  resize(dim, n_u, n_l);
111 }
112 void band_matrix::resize(int dim, int n_u, int n_l) {
113  assert(dim > 0);
114  assert(n_u >= 0);
115  assert(n_l >= 0);
116  m_upper.resize(n_u + 1);
117  m_lower.resize(n_l + 1);
118  for (size_t i = 0; i < m_upper.size(); i++) {
119  m_upper[i].resize(dim);
120  }
121  for (size_t i = 0; i < m_lower.size(); i++) {
122  m_lower[i].resize(dim);
123  }
124 }
125 int band_matrix::dim() const {
126  if (m_upper.size() > 0) {
127  return m_upper[0].size();
128  } else {
129  return 0;
130  }
131 }
132 
133 // defines the new operator (), so that we can access the elements
134 // by A(i,j), index going from i=0,...,dim()-1
135 double& band_matrix::operator()(int i, int j) {
136  int k = j - i; // what band is the entry
137  assert((i >= 0) && (i < dim()) && (j >= 0) && (j < dim()));
138  assert((-num_lower() <= k) && (k <= num_upper()));
139  // k=0 -> diogonal, k<0 lower left part, k>0 upper right part
140  if (k >= 0)
141  return m_upper[k][i];
142  else
143  return m_lower[-k][i];
144 }
145 double band_matrix::operator()(int i, int j) const {
146  int k = j - i; // what band is the entry
147  assert((i >= 0) && (i < dim()) && (j >= 0) && (j < dim()));
148  assert((-num_lower() <= k) && (k <= num_upper()));
149  // k=0 -> diogonal, k<0 lower left part, k>0 upper right part
150  if (k >= 0)
151  return m_upper[k][i];
152  else
153  return m_lower[-k][i];
154 }
155 // second diag (used in LU decomposition), saved in m_lower
156 double band_matrix::saved_diag(int i) const {
157  assert((i >= 0) && (i < dim()));
158  return m_lower[0][i];
159 }
160 double& band_matrix::saved_diag(int i) {
161  assert((i >= 0) && (i < dim()));
162  return m_lower[0][i];
163 }
164 
165 // LR-Decomposition of a band matrix
166 void band_matrix::lu_decompose() {
167  int i_max, j_max;
168  int j_min;
169  double x;
170 
171  // preconditioning
172  // normalize column i so that a_ii=1
173  for (int i = 0; i < this->dim(); i++) {
174  assert(this->operator()(i, i) != 0.0);
175  this->saved_diag(i) = 1.0 / this->operator()(i, i);
176  j_min = std::max(0, i - this->num_lower());
177  j_max = std::min(this->dim() - 1, i + this->num_upper());
178  for (int j = j_min; j <= j_max; j++) {
179  this->operator()(i, j) *= this->saved_diag(i);
180  }
181  this->operator()(i, i) = 1.0; // prevents rounding errors
182  }
183 
184  // Gauss LR-Decomposition
185  for (int k = 0; k < this->dim(); k++) {
186  i_max = std::min(this->dim() - 1, k + this->num_lower()); // num_lower not a mistake!
187  for (int i = k + 1; i <= i_max; i++) {
188  assert(this->operator()(k, k) != 0.0);
189  x = -this->operator()(i, k) / this->operator()(k, k);
190  this->operator()(i, k) = -x; // assembly part of L
191  j_max = std::min(this->dim() - 1, k + this->num_upper());
192  for (int j = k + 1; j <= j_max; j++) {
193  // assembly part of R
194  this->operator()(i, j) = this->operator()(i, j) + x * this->operator()(k, j);
195  }
196  }
197  }
198 }
199 // solves Ly=b
200 std::vector<double> band_matrix::l_solve(const std::vector<double>& b) const {
201  assert(this->dim() == (int)b.size());
202  std::vector<double> x(this->dim());
203  int j_start;
204  double sum;
205  for (int i = 0; i < this->dim(); i++) {
206  sum = 0;
207  j_start = std::max(0, i - this->num_lower());
208  for (int j = j_start; j < i; j++)
209  sum += this->operator()(i, j) * x[j];
210  x[i] = (b[i] * this->saved_diag(i)) - sum;
211  }
212  return x;
213 }
214 // solves Rx=y
215 std::vector<double> band_matrix::r_solve(const std::vector<double>& b) const {
216  assert(this->dim() == (int)b.size());
217  std::vector<double> x(this->dim());
218  int j_stop;
219  double sum;
220  for (int i = this->dim() - 1; i >= 0; i--) {
221  sum = 0;
222  j_stop = std::min(this->dim() - 1, i + this->num_upper());
223  for (int j = i + 1; j <= j_stop; j++)
224  sum += this->operator()(i, j) * x[j];
225  x[i] = (b[i] - sum) / this->operator()(i, i);
226  }
227  return x;
228 }
229 
230 std::vector<double> band_matrix::lu_solve(const std::vector<double>& b, bool is_lu_decomposed) {
231  assert(this->dim() == (int)b.size());
232  std::vector<double> x, y;
233  if (is_lu_decomposed == false) {
234  this->lu_decompose();
235  }
236  y = this->l_solve(b);
237  x = this->r_solve(y);
238  return x;
239 }
240 
241 // spline implementation
242 // -----------------------
243 
244 void spline::set_boundary(spline::bd_type left, double left_value, spline::bd_type right, double right_value,
245  bool force_linear_extrapolation) {
246  assert(m_x.size() == 0); // set_points() must not have happened yet
247  m_left = left;
248  m_right = right;
249  m_left_value = left_value;
250  m_right_value = right_value;
251  m_force_linear_extrapolation = force_linear_extrapolation;
252 }
253 
254 void spline::set_points(const std::vector<double>& x, const std::vector<double>& y, bool cubic_spline) {
255  assert(x.size() == y.size());
256  assert(x.size() > 2);
257  m_x = x;
258  m_y = y;
259  int n = x.size();
260  // TODO: maybe sort x and y, rather than returning an error
261  for (int i = 0; i < n - 1; i++) {
262  assert(m_x[i] < m_x[i + 1]);
263  }
264 
265  if (cubic_spline == true) { // cubic spline interpolation
266  // setting up the matrix and right hand side of the equation system
267  // for the parameters b[]
268  band_matrix A(n, 1, 1);
269  std::vector<double> rhs(n);
270  for (int i = 1; i < n - 1; i++) {
271  A(i, i - 1) = 1.0 / 3.0 * (x[i] - x[i - 1]);
272  A(i, i) = 2.0 / 3.0 * (x[i + 1] - x[i - 1]);
273  A(i, i + 1) = 1.0 / 3.0 * (x[i + 1] - x[i]);
274  rhs[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
275  }
276  // boundary conditions
277  if (m_left == spline::second_deriv) {
278  // 2*b[0] = f''
279  A(0, 0) = 2.0;
280  A(0, 1) = 0.0;
281  rhs[0] = m_left_value;
282  } else if (m_left == spline::first_deriv) {
283  // c[0] = f', needs to be re-expressed in terms of b:
284  // (2b[0]+b[1])(x[1]-x[0]) = 3 ((y[1]-y[0])/(x[1]-x[0]) - f')
285  A(0, 0) = 2.0 * (x[1] - x[0]);
286  A(0, 1) = 1.0 * (x[1] - x[0]);
287  rhs[0] = 3.0 * ((y[1] - y[0]) / (x[1] - x[0]) - m_left_value);
288  } else {
289  assert(false);
290  }
291  if (m_right == spline::second_deriv) {
292  // 2*b[n-1] = f''
293  A(n - 1, n - 1) = 2.0;
294  A(n - 1, n - 2) = 0.0;
295  rhs[n - 1] = m_right_value;
296  } else if (m_right == spline::first_deriv) {
297  // c[n-1] = f', needs to be re-expressed in terms of b:
298  // (b[n-2]+2b[n-1])(x[n-1]-x[n-2])
299  // = 3 (f' - (y[n-1]-y[n-2])/(x[n-1]-x[n-2]))
300  A(n - 1, n - 1) = 2.0 * (x[n - 1] - x[n - 2]);
301  A(n - 1, n - 2) = 1.0 * (x[n - 1] - x[n - 2]);
302  rhs[n - 1] = 3.0 * (m_right_value - (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]));
303  } else {
304  assert(false);
305  }
306 
307  // solve the equation system to obtain the parameters b[]
308  m_b = A.lu_solve(rhs);
309 
310  // calculate parameters a[] and c[] based on b[]
311  m_a.resize(n);
312  m_c.resize(n);
313  for (int i = 0; i < n - 1; i++) {
314  m_a[i] = 1.0 / 3.0 * (m_b[i + 1] - m_b[i]) / (x[i + 1] - x[i]);
315  m_c[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) -
316  1.0 / 3.0 * (2.0 * m_b[i] + m_b[i + 1]) * (x[i + 1] - x[i]);
317  }
318  } else { // linear interpolation
319  m_a.resize(n);
320  m_b.resize(n);
321  m_c.resize(n);
322  for (int i = 0; i < n - 1; i++) {
323  m_a[i] = 0.0;
324  m_b[i] = 0.0;
325  m_c[i] = (m_y[i + 1] - m_y[i]) / (m_x[i + 1] - m_x[i]);
326  }
327  }
328 
329  // for left extrapolation coefficients
330  m_b0 = (m_force_linear_extrapolation == false) ? m_b[0] : 0.0;
331  m_c0 = m_c[0];
332 
333  // for the right extrapolation coefficients
334  // f_{n-1}(x) = b*(x-x_{n-1})^2 + c*(x-x_{n-1}) + y_{n-1}
335  double h = x[n - 1] - x[n - 2];
336  // m_b[n-1] is determined by the boundary condition
337  m_a[n - 1] = 0.0;
338  m_c[n - 1] = 3.0 * m_a[n - 2] * h * h + 2.0 * m_b[n - 2] * h + m_c[n - 2]; // = f'_{n-2}(x_{n-1})
339  if (m_force_linear_extrapolation == true)
340  m_b[n - 1] = 0.0;
341 }
342 
343 double spline::operator()(double x) const {
344  size_t n = m_x.size();
345  // find the closest point m_x[idx] < x, idx=0 even if x<m_x[0]
346  std::vector<double>::const_iterator it;
347  it = std::lower_bound(m_x.begin(), m_x.end(), x);
348  int idx = std::max(int(it - m_x.begin()) - 1, 0);
349 
350  double h = x - m_x[idx];
351  double interpol;
352  if (x < m_x[0]) {
353  // extrapolation to the left
354  interpol = (m_b0 * h + m_c0) * h + m_y[0];
355  } else if (x > m_x[n - 1]) {
356  // extrapolation to the right
357  interpol = (m_b[n - 1] * h + m_c[n - 1]) * h + m_y[n - 1];
358  } else {
359  // interpolation
360  interpol = ((m_a[idx] * h + m_b[idx]) * h + m_c[idx]) * h + m_y[idx];
361  }
362  return interpol;
363 }
364 
365 } // namespace tk
366 
367 } // namespace
368 
369 #endif /* TK_SPLINE_H */
int j
Definition: decode_rs.h:73
These are used to scale the SD before writing it to the HDF4 file The default is and which means the product is not scaled at all Since the product is usually stored as a float inside of this is a way to write the float out as a integer l2prod min
subroutine spline(s, x, y, n, in, t, il, iu, vl, vu, e, u)
Definition: phs.f:1348
float h[MODELMAX]
Definition: atrem_corl1.h:131
Definition: spline.h:38
const float A
Definition: calc_par.c:100
data_t b[NROOTS+1]
Definition: decode_rs.h:77
float first_deriv(float x[], float y[], int n)
Definition: aerosol.c:289
int i
Definition: decode_rs.h:71
int k
Definition: decode_rs.h:73
l2prod max