OB.DAAC Logo
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 
27 #ifndef TK_SPLINE_H
28 #define TK_SPLINE_H
29 
30 #include <cstdio>
31 #include <cassert>
32 #include <vector>
33 #include <algorithm>
34 
35 
36 // unnamed namespace only because the implementation is in this
37 // header file and we don't want to export symbols to the obj files
38 namespace
39 {
40 
41 namespace tk
42 {
43 
44 // band matrix solver
45 class band_matrix
46 {
47 private:
48  std::vector< std::vector<double> > m_upper; // upper band
49  std::vector< std::vector<double> > m_lower; // lower band
50 public:
51  band_matrix() {}; // constructor
52  band_matrix(int dim, int n_u, int n_l); // constructor
53  ~band_matrix() {}; // destructor
54  void resize(int dim, int n_u, int n_l); // init with dim,n_u,n_l
55  int dim() const; // matrix dimension
56  int num_upper() const
57  {
58  return m_upper.size()-1;
59  }
60  int num_lower() const
61  {
62  return m_lower.size()-1;
63  }
64  // access operator
65  double & operator () (int i, int j); // write
66  double operator () (int i, int j) const; // read
67  // we can store an additional diogonal (in m_lower)
68  double& saved_diag(int i);
69  double saved_diag(int i) const;
70  void lu_decompose();
71  std::vector<double> r_solve(const std::vector<double>& b) const;
72  std::vector<double> l_solve(const std::vector<double>& b) const;
73  std::vector<double> lu_solve(const std::vector<double>& b,
74  bool is_lu_decomposed=false);
75 
76 };
77 
78 
79 // spline interpolation
80 class spline
81 {
82 public:
83  enum bd_type {
84  first_deriv = 1,
85  second_deriv = 2
86  };
87 
88 private:
89  std::vector<double> m_x,m_y; // x,y coordinates of points
90  // interpolation parameters
91  // f(x) = a*(x-x_i)^3 + b*(x-x_i)^2 + c*(x-x_i) + y_i
92  std::vector<double> m_a,m_b,m_c; // spline coefficients
93  double m_b0, m_c0; // for left extrapol
94  bd_type m_left, m_right;
95  double m_left_value, m_right_value;
96  bool m_force_linear_extrapolation;
97 
98 public:
99  // set default boundary condition to be zero curvature at both ends
100  spline(): m_left(second_deriv), m_right(second_deriv),
101  m_left_value(0.0), m_right_value(0.0),
102  m_force_linear_extrapolation(false)
103  {
104  ;
105  }
106 
107  // optional, but if called it has to come be before set_points()
108  void set_boundary(bd_type left, double left_value,
109  bd_type right, double right_value,
110  bool force_linear_extrapolation=false);
111  void set_points(const std::vector<double>& x,
112  const std::vector<double>& y, bool cubic_spline=true);
113  double operator() (double x) const;
114 };
115 
116 
117 
118 // ---------------------------------------------------------------------
119 // implementation part, which could be separated into a cpp file
120 // ---------------------------------------------------------------------
121 
122 
123 // band_matrix implementation
124 // -------------------------
125 
126 band_matrix::band_matrix(int dim, int n_u, int n_l)
127 {
128  resize(dim, n_u, n_l);
129 }
130 void band_matrix::resize(int dim, int n_u, int n_l)
131 {
132  assert(dim>0);
133  assert(n_u>=0);
134  assert(n_l>=0);
135  m_upper.resize(n_u+1);
136  m_lower.resize(n_l+1);
137  for(size_t i=0; i<m_upper.size(); i++) {
138  m_upper[i].resize(dim);
139  }
140  for(size_t i=0; i<m_lower.size(); i++) {
141  m_lower[i].resize(dim);
142  }
143 }
144 int band_matrix::dim() const
145 {
146  if(m_upper.size()>0) {
147  return m_upper[0].size();
148  } else {
149  return 0;
150  }
151 }
152 
153 
154 // defines the new operator (), so that we can access the elements
155 // by A(i,j), index going from i=0,...,dim()-1
156 double & band_matrix::operator () (int i, int j)
157 {
158  int k=j-i; // what band is the entry
159  assert( (i>=0) && (i<dim()) && (j>=0) && (j<dim()) );
160  assert( (-num_lower()<=k) && (k<=num_upper()) );
161  // k=0 -> diogonal, k<0 lower left part, k>0 upper right part
162  if(k>=0) return m_upper[k][i];
163  else return m_lower[-k][i];
164 }
165 double band_matrix::operator () (int i, int j) const
166 {
167  int k=j-i; // what band is the entry
168  assert( (i>=0) && (i<dim()) && (j>=0) && (j<dim()) );
169  assert( (-num_lower()<=k) && (k<=num_upper()) );
170  // k=0 -> diogonal, k<0 lower left part, k>0 upper right part
171  if(k>=0) return m_upper[k][i];
172  else return m_lower[-k][i];
173 }
174 // second diag (used in LU decomposition), saved in m_lower
175 double band_matrix::saved_diag(int i) const
176 {
177  assert( (i>=0) && (i<dim()) );
178  return m_lower[0][i];
179 }
180 double & band_matrix::saved_diag(int i)
181 {
182  assert( (i>=0) && (i<dim()) );
183  return m_lower[0][i];
184 }
185 
186 // LR-Decomposition of a band matrix
187 void band_matrix::lu_decompose()
188 {
189  int i_max,j_max;
190  int j_min;
191  double x;
192 
193  // preconditioning
194  // normalize column i so that a_ii=1
195  for(int i=0; i<this->dim(); i++) {
196  assert(this->operator()(i,i)!=0.0);
197  this->saved_diag(i)=1.0/this->operator()(i,i);
198  j_min=std::max(0,i-this->num_lower());
199  j_max=std::min(this->dim()-1,i+this->num_upper());
200  for(int j=j_min; j<=j_max; j++) {
201  this->operator()(i,j) *= this->saved_diag(i);
202  }
203  this->operator()(i,i)=1.0; // prevents rounding errors
204  }
205 
206  // Gauss LR-Decomposition
207  for(int k=0; k<this->dim(); k++) {
208  i_max=std::min(this->dim()-1,k+this->num_lower()); // num_lower not a mistake!
209  for(int i=k+1; i<=i_max; i++) {
210  assert(this->operator()(k,k)!=0.0);
211  x=-this->operator()(i,k)/this->operator()(k,k);
212  this->operator()(i,k)=-x; // assembly part of L
213  j_max=std::min(this->dim()-1,k+this->num_upper());
214  for(int j=k+1; j<=j_max; j++) {
215  // assembly part of R
216  this->operator()(i,j)=this->operator()(i,j)+x*this->operator()(k,j);
217  }
218  }
219  }
220 }
221 // solves Ly=b
222 std::vector<double> band_matrix::l_solve(const std::vector<double>& b) const
223 {
224  assert( this->dim()==(int)b.size() );
225  std::vector<double> x(this->dim());
226  int j_start;
227  double sum;
228  for(int i=0; i<this->dim(); i++) {
229  sum=0;
230  j_start=std::max(0,i-this->num_lower());
231  for(int j=j_start; j<i; j++) sum += this->operator()(i,j)*x[j];
232  x[i]=(b[i]*this->saved_diag(i)) - sum;
233  }
234  return x;
235 }
236 // solves Rx=y
237 std::vector<double> band_matrix::r_solve(const std::vector<double>& b) const
238 {
239  assert( this->dim()==(int)b.size() );
240  std::vector<double> x(this->dim());
241  int j_stop;
242  double sum;
243  for(int i=this->dim()-1; i>=0; i--) {
244  sum=0;
245  j_stop=std::min(this->dim()-1,i+this->num_upper());
246  for(int j=i+1; j<=j_stop; j++) sum += this->operator()(i,j)*x[j];
247  x[i]=( b[i] - sum ) / this->operator()(i,i);
248  }
249  return x;
250 }
251 
252 std::vector<double> band_matrix::lu_solve(const std::vector<double>& b,
253  bool is_lu_decomposed)
254 {
255  assert( this->dim()==(int)b.size() );
256  std::vector<double> x,y;
257  if(is_lu_decomposed==false) {
258  this->lu_decompose();
259  }
260  y=this->l_solve(b);
261  x=this->r_solve(y);
262  return x;
263 }
264 
265 
266 
267 
268 // spline implementation
269 // -----------------------
270 
271 void spline::set_boundary(spline::bd_type left, double left_value,
272  spline::bd_type right, double right_value,
273  bool force_linear_extrapolation)
274 {
275  assert(m_x.size()==0); // set_points() must not have happened yet
276  m_left=left;
277  m_right=right;
278  m_left_value=left_value;
279  m_right_value=right_value;
280  m_force_linear_extrapolation=force_linear_extrapolation;
281 }
282 
283 
284 void spline::set_points(const std::vector<double>& x,
285  const std::vector<double>& y, bool cubic_spline)
286 {
287  assert(x.size()==y.size());
288  assert(x.size()>2);
289  m_x=x;
290  m_y=y;
291  int n=x.size();
292  // TODO: maybe sort x and y, rather than returning an error
293  for(int i=0; i<n-1; i++) {
294  assert(m_x[i]<m_x[i+1]);
295  }
296 
297  if(cubic_spline==true) { // cubic spline interpolation
298  // setting up the matrix and right hand side of the equation system
299  // for the parameters b[]
300  band_matrix A(n,1,1);
301  std::vector<double> rhs(n);
302  for(int i=1; i<n-1; i++) {
303  A(i,i-1)=1.0/3.0*(x[i]-x[i-1]);
304  A(i,i)=2.0/3.0*(x[i+1]-x[i-1]);
305  A(i,i+1)=1.0/3.0*(x[i+1]-x[i]);
306  rhs[i]=(y[i+1]-y[i])/(x[i+1]-x[i]) - (y[i]-y[i-1])/(x[i]-x[i-1]);
307  }
308  // boundary conditions
309  if(m_left == spline::second_deriv) {
310  // 2*b[0] = f''
311  A(0,0)=2.0;
312  A(0,1)=0.0;
313  rhs[0]=m_left_value;
314  } else if(m_left == spline::first_deriv) {
315  // c[0] = f', needs to be re-expressed in terms of b:
316  // (2b[0]+b[1])(x[1]-x[0]) = 3 ((y[1]-y[0])/(x[1]-x[0]) - f')
317  A(0,0)=2.0*(x[1]-x[0]);
318  A(0,1)=1.0*(x[1]-x[0]);
319  rhs[0]=3.0*((y[1]-y[0])/(x[1]-x[0])-m_left_value);
320  } else {
321  assert(false);
322  }
323  if(m_right == spline::second_deriv) {
324  // 2*b[n-1] = f''
325  A(n-1,n-1)=2.0;
326  A(n-1,n-2)=0.0;
327  rhs[n-1]=m_right_value;
328  } else if(m_right == spline::first_deriv) {
329  // c[n-1] = f', needs to be re-expressed in terms of b:
330  // (b[n-2]+2b[n-1])(x[n-1]-x[n-2])
331  // = 3 (f' - (y[n-1]-y[n-2])/(x[n-1]-x[n-2]))
332  A(n-1,n-1)=2.0*(x[n-1]-x[n-2]);
333  A(n-1,n-2)=1.0*(x[n-1]-x[n-2]);
334  rhs[n-1]=3.0*(m_right_value-(y[n-1]-y[n-2])/(x[n-1]-x[n-2]));
335  } else {
336  assert(false);
337  }
338 
339  // solve the equation system to obtain the parameters b[]
340  m_b=A.lu_solve(rhs);
341 
342  // calculate parameters a[] and c[] based on b[]
343  m_a.resize(n);
344  m_c.resize(n);
345  for(int i=0; i<n-1; i++) {
346  m_a[i]=1.0/3.0*(m_b[i+1]-m_b[i])/(x[i+1]-x[i]);
347  m_c[i]=(y[i+1]-y[i])/(x[i+1]-x[i])
348  - 1.0/3.0*(2.0*m_b[i]+m_b[i+1])*(x[i+1]-x[i]);
349  }
350  } else { // linear interpolation
351  m_a.resize(n);
352  m_b.resize(n);
353  m_c.resize(n);
354  for(int i=0; i<n-1; i++) {
355  m_a[i]=0.0;
356  m_b[i]=0.0;
357  m_c[i]=(m_y[i+1]-m_y[i])/(m_x[i+1]-m_x[i]);
358  }
359  }
360 
361  // for left extrapolation coefficients
362  m_b0 = (m_force_linear_extrapolation==false) ? m_b[0] : 0.0;
363  m_c0 = m_c[0];
364 
365  // for the right extrapolation coefficients
366  // f_{n-1}(x) = b*(x-x_{n-1})^2 + c*(x-x_{n-1}) + y_{n-1}
367  double h=x[n-1]-x[n-2];
368  // m_b[n-1] is determined by the boundary condition
369  m_a[n-1]=0.0;
370  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})
371  if(m_force_linear_extrapolation==true)
372  m_b[n-1]=0.0;
373 }
374 
375 double spline::operator() (double x) const
376 {
377  size_t n=m_x.size();
378  // find the closest point m_x[idx] < x, idx=0 even if x<m_x[0]
379  std::vector<double>::const_iterator it;
380  it=std::lower_bound(m_x.begin(),m_x.end(),x);
381  int idx=std::max( int(it-m_x.begin())-1, 0);
382 
383  double h=x-m_x[idx];
384  double interpol;
385  if(x<m_x[0]) {
386  // extrapolation to the left
387  interpol=(m_b0*h + m_c0)*h + m_y[0];
388  } else if(x>m_x[n-1]) {
389  // extrapolation to the right
390  interpol=(m_b[n-1]*h + m_c[n-1])*h + m_y[n-1];
391  } else {
392  // interpolation
393  interpol=((m_a[idx]*h + m_b[idx])*h + m_c[idx])*h + m_y[idx];
394  }
395  return interpol;
396 }
397 
398 
399 } // namespace tk
400 
401 
402 } // namespace
403 
404 #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:41
data_t b[NROOTS+1]
Definition: decode_rs.h:77
float first_deriv(float x[], float y[], int n)
Definition: aerosol.c:246
int i
Definition: decode_rs.h:71
int k
Definition: decode_rs.h:73
l2prod max