00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006, 2007, 2008, Andrew W. Steiner 00005 00006 This file is part of O2scl. 00007 00008 O2scl is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 3 of the License, or 00011 (at your option) any later version. 00012 00013 O2scl is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with O2scl. If not, see <http://www.gnu.org/licenses/>. 00020 00021 ------------------------------------------------------------------- 00022 */ 00023 #ifndef O2SCL_INTERP_H 00024 #define O2SCL_INTERP_H 00025 00026 #include <iostream> 00027 #include <string> 00028 00029 // for gsl_linalg_solve_symm_tridiag for cspline_interp 00030 #include <gsl/gsl_linalg.h> 00031 00032 #include <o2scl/err_hnd.h> 00033 #include <o2scl/collection.h> 00034 #include <o2scl/ovector_tlate.h> 00035 #include <o2scl/search_vec.h> 00036 00037 #ifndef DOXYGENP 00038 namespace o2scl { 00039 #endif 00040 00041 /** 00042 \brief Base low-level interpolation class 00043 00044 The descendants of this class are intended to be fast 00045 interpolation routines for increasing functions, leaving the 00046 some error handling, user-friendliness, and other more 00047 complicated improvements for other classes. 00048 00049 For any pair of vectors x and y into which you would like to 00050 interpolate, you need to call allocate() and init() first, and then 00051 the interpolation functions, and then free(). If the next pair 00052 of vectors has the same size, then you need only to call init() 00053 before the next call to an interpolation function. If the vectors 00054 do not change, then you may call the interpolation functions in 00055 succession. 00056 00057 All of the descendants are based on the GSL interpolation 00058 routines and give identical results. 00059 00060 \future These might work for decreasing functions by just 00061 replacing calls to search_vec::bsearch_inc() with 00062 search_vec::bsearch_dec(). If this is the case, then this should 00063 be rewritten accordingly. (I think I might have removed the 00064 acceleration) 00065 */ 00066 template<class vec_t> class base_interp { 00067 00068 #ifndef DOXYGEN_INTERNAL 00069 00070 protected: 00071 00072 /// The binary search object 00073 search_vec<vec_t> sv; 00074 00075 /** 00076 \brief An internal function to assist in computing the 00077 integral for both the cspline and Akima types 00078 */ 00079 double integ_eval(double ai, double bi, double ci, double di, double xi, 00080 double a, double b) { 00081 const double r1 = a - xi; 00082 const double r2 = b - xi; 00083 const double r12 = r1 + r2; 00084 const double bterm = 0.5 * bi * r12; 00085 const double cterm = (1.0 / 3.0) * ci * (r1 * r1 + r2 * r2 + r1 * r2); 00086 const double dterm = 0.25 * di * r12 * (r1 * r1 + r2 * r2); 00087 00088 return (b - a) * (ai + bterm + cterm + dterm); 00089 } 00090 00091 #endif 00092 00093 public: 00094 00095 virtual ~base_interp() {} 00096 00097 /** 00098 \brief The minimum size of the vectors to interpolate between 00099 00100 This needs to be set in the constructor of the children for 00101 access by the class user 00102 */ 00103 size_t min_size; 00104 00105 /// Allocate memory, assuming x and y have size \c size 00106 virtual int allocate(size_t size) { return 0; } 00107 00108 /// Free allocated memory 00109 virtual int free() { return 0; } 00110 00111 /// Initialize interpolation routine 00112 virtual int init(const vec_t &x, const vec_t &y, size_t size) { 00113 return 0; 00114 } 00115 00116 /// Give the value of the function \f$ y(x=x_0) \f$ . 00117 virtual int interp(const vec_t &x, const vec_t &y, size_t size, 00118 double x0, double &y0) { 00119 return 0; 00120 } 00121 00122 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 00123 virtual int deriv(const vec_t &x, const vec_t &y, size_t size, 00124 double x0, double &dydx) { 00125 return 0; 00126 } 00127 00128 /** \brief Give the value of the second derivative 00129 \f$ y^{\prime \prime}(x=x_0) \f$ . 00130 */ 00131 virtual int deriv2(const vec_t &x, const vec_t &y, size_t size, 00132 double x0, double &d2ydx2) { 00133 return 0; 00134 } 00135 00136 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 00137 virtual int integ(const vec_t &x, const vec_t &y, size_t size, 00138 double a, double b, double &result) { 00139 return 0; 00140 } 00141 }; 00142 00143 /** 00144 \brief Linear interpolation (GSL) 00145 00146 Linear interpolation requires no calls to allocate(), free() or 00147 init(), as there is no internal storage required. 00148 */ 00149 template<class vec_t> class linear_interp : public base_interp<vec_t> { 00150 00151 public: 00152 00153 linear_interp() { 00154 this->min_size=2; 00155 } 00156 00157 /// Give the value of the function \f$ y(x=x_0) \f$ . 00158 virtual int interp(const vec_t &x_array, const vec_t &y_array, 00159 size_t size, double x, double &y) { 00160 double x_lo, x_hi; 00161 double y_lo, y_hi; 00162 double dx; 00163 size_t index; 00164 00165 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00166 00167 /* evaluate */ 00168 x_lo = x_array[index]; 00169 x_hi = x_array[index + 1]; 00170 y_lo = y_array[index]; 00171 y_hi = y_array[index + 1]; 00172 dx = x_hi - x_lo; 00173 if (dx > 0.0) 00174 { 00175 y = y_lo + (x - x_lo) / dx * (y_hi - y_lo); 00176 return gsl_success; 00177 } 00178 else 00179 { 00180 y = 0.0; 00181 return GSL_EINVAL; 00182 } 00183 } 00184 00185 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 00186 virtual int deriv(const vec_t &x_array, const vec_t &y_array, 00187 size_t size, 00188 double x, double &dydx) { 00189 00190 double x_lo, x_hi; 00191 double y_lo, y_hi; 00192 double dx; 00193 double dy; 00194 size_t index; 00195 00196 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00197 00198 /* evaluate */ 00199 x_lo = x_array[index]; 00200 x_hi = x_array[index + 1]; 00201 y_lo = y_array[index]; 00202 y_hi = y_array[index + 1]; 00203 dx = x_hi - x_lo; 00204 dy = y_hi - y_lo; 00205 if (dx > 0.0) 00206 { 00207 dydx = dy / dx;; 00208 return gsl_success; 00209 } 00210 else 00211 { 00212 dydx = 0.0; 00213 return GSL_EINVAL; 00214 } 00215 } 00216 00217 /** \brief Give the value of the second derivative 00218 \f$ y^{\prime \prime}(x=x_0) \f$ . 00219 */ 00220 virtual int deriv2(const vec_t &x, const vec_t &y, size_t size, 00221 double x0, double &d2ydx2) { 00222 d2ydx2=0.0; 00223 return gsl_success; 00224 } 00225 00226 00227 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 00228 virtual int integ(const vec_t &x_array, const vec_t &y_array, 00229 size_t size, double a, double b, 00230 double &result) { 00231 size_t i, index_a, index_b; 00232 00233 index_a=this->sv.bsearch_inc(a,x_array,0,size-1); 00234 index_b=this->sv.bsearch_inc(b,x_array,0,size-1); 00235 00236 /* endpoints span more than one interval */ 00237 00238 result = 0.0; 00239 00240 /* interior intervals */ 00241 for(i=index_a; i<=index_b; i++) { 00242 const double x_hi = x_array[i + 1]; 00243 const double x_lo = x_array[i]; 00244 const double y_lo = y_array[i]; 00245 const double y_hi = y_array[i + 1]; 00246 const double dx = x_hi - x_lo; 00247 00248 if(dx != 0.0) { 00249 if (i == index_a || i == index_b) 00250 { 00251 double x1 = (i == index_a) ? a : x_lo; 00252 double x2 = (i == index_b) ? b : x_hi; 00253 const double D = (y_hi-y_lo)/dx; 00254 result += (x2-x1) * (y_lo + 0.5*D*((x2-x_lo)+(x1-x_lo))); 00255 } 00256 else 00257 { 00258 result += 0.5 * dx * (y_lo + y_hi); 00259 } 00260 } 00261 } 00262 00263 return gsl_success; 00264 } 00265 }; 00266 00267 /** 00268 \brief Cubic spline interpolation (GSL) 00269 */ 00270 template<class vec_t> class cspline_interp : public base_interp<vec_t> { 00271 00272 #ifndef DOXYGEN_INTERNAL 00273 00274 protected: 00275 00276 /// \name Storage for cubic spline interpolation 00277 //@{ 00278 double *c; 00279 double *g; 00280 double *diag; 00281 double *offdiag; 00282 //@} 00283 00284 /// True for periodic boundary conditions 00285 bool peri; 00286 00287 /// Compute coefficients for cubic spline interpolation 00288 void coeff_calc(const double c_array[], double dy, double dx, 00289 size_t index, double * b, double * c2, double * d) { 00290 const double c_i = c_array[index]; 00291 const double c_ip1 = c_array[index + 1]; 00292 *b = (dy / dx) - dx * (c_ip1 + 2.0 * c_i) / 3.0; 00293 *c2 = c_i; 00294 *d = (c_ip1 - c_i) / (3.0 * dx); 00295 } 00296 00297 #endif 00298 00299 public: 00300 00301 /** \brief Create a base interpolation object with natural or 00302 periodic boundary conditions 00303 */ 00304 cspline_interp(bool periodic=false) { 00305 00306 peri=periodic; 00307 if (peri) this->min_size=2; 00308 else this->min_size=3; 00309 00310 } 00311 00312 virtual ~cspline_interp() {} 00313 00314 /// Allocate memory, assuming x and y have size \c size 00315 virtual int allocate(size_t size) { 00316 00317 c = (double *) malloc (size * sizeof (double)); 00318 00319 if ( c == NULL) 00320 { 00321 GSL_ERROR_NULL("failed to allocate space for c", GSL_ENOMEM); 00322 } 00323 00324 g = (double *) malloc (size * sizeof (double)); 00325 00326 if ( g == NULL) 00327 { 00328 std::free(c); 00329 GSL_ERROR_NULL("failed to allocate space for g", GSL_ENOMEM); 00330 } 00331 00332 diag = (double *) malloc (size * sizeof (double)); 00333 00334 if ( diag == NULL) 00335 { 00336 std::free(g); 00337 std::free(c); 00338 GSL_ERROR_NULL("failed to allocate space for diag", GSL_ENOMEM); 00339 } 00340 00341 offdiag = (double *) malloc (size * sizeof (double)); 00342 00343 if ( offdiag == NULL) 00344 { 00345 std::free(diag); 00346 std::free(g); 00347 std::free(c); 00348 GSL_ERROR_NULL("failed to allocate space for offdiag", GSL_ENOMEM); 00349 } 00350 00351 return 0; 00352 } 00353 00354 /// Initialize interpolation routine 00355 virtual int init(const vec_t &xa, const vec_t &ya, size_t size) { 00356 00357 if (peri) { 00358 00359 /// Periodic boundary conditions 00360 00361 size_t i; 00362 size_t num_points = size; 00363 /* Engeln-Mullges + Uhlig "n" */ 00364 size_t max_index = num_points - 1; 00365 /* linear system is sys_size x sys_size */ 00366 size_t sys_size = max_index; 00367 00368 if (sys_size == 2) { 00369 /* solve 2x2 system */ 00370 00371 const double h0 = xa[1] - xa[0]; 00372 const double h1 = xa[2] - xa[1]; 00373 const double h2 = xa[3] - xa[2]; 00374 const double A = 2.0*(h0 + h1); 00375 const double B = h0 + h1; 00376 double gx[2]; 00377 double det; 00378 00379 gx[0] = 3.0 * ((ya[2] - ya[1]) / h1 - (ya[1] - ya[0]) / h0); 00380 gx[1] = 3.0 * ((ya[1] - ya[2]) / h2 - (ya[2] - ya[1]) / h1); 00381 00382 det = 3.0 * (h0 + h1) * (h0 + h1); 00383 c[1] = ( A * gx[0] - B * gx[1])/det; 00384 c[2] = (-B * gx[0] + A * gx[1])/det; 00385 c[0] = c[2]; 00386 00387 return gsl_success; 00388 00389 } else { 00390 00391 for (i = 0; i < sys_size-1; i++) { 00392 const double h_i = xa[i + 1] - xa[i]; 00393 const double h_ip1 = xa[i + 2] - xa[i + 1]; 00394 const double ydiff_i = ya[i + 1] - ya[i]; 00395 const double ydiff_ip1 = ya[i + 2] - ya[i + 1]; 00396 const double g_i = (h_i != 0.0) ? 1.0 / h_i : 0.0; 00397 const double g_ip1 = (h_ip1 != 0.0) ? 1.0 / h_ip1 : 0.0; 00398 offdiag[i] = h_ip1; 00399 diag[i] = 2.0 * (h_ip1 + h_i); 00400 g[i] = 3.0 * (ydiff_ip1 * g_ip1 - ydiff_i * g_i); 00401 } 00402 00403 i = sys_size - 1; 00404 { 00405 const double h_i = xa[i + 1] - xa[i]; 00406 const double h_ip1 = xa[1] - xa[0]; 00407 const double ydiff_i = ya[i + 1] - ya[i]; 00408 const double ydiff_ip1 = ya[1] - ya[0]; 00409 const double g_i = (h_i != 0.0) ? 1.0 / h_i : 0.0; 00410 const double g_ip1 = (h_ip1 != 0.0) ? 1.0 / h_ip1 : 0.0; 00411 offdiag[i] = h_ip1; 00412 diag[i] = 2.0 * (h_ip1 + h_i); 00413 g[i] = 3.0 * (ydiff_ip1 * g_ip1 - ydiff_i * g_i); 00414 } 00415 00416 { 00417 gsl_vector_view g_vec = gsl_vector_view_array( g, sys_size); 00418 gsl_vector_view diag_vec = 00419 gsl_vector_view_array( diag,sys_size); 00420 gsl_vector_view offdiag_vec = 00421 gsl_vector_view_array( offdiag, sys_size); 00422 gsl_vector_view solution_vec = 00423 gsl_vector_view_array (( c) + 1, sys_size); 00424 int status = 00425 gsl_linalg_solve_symm_cyc_tridiag(&diag_vec.vector, 00426 &offdiag_vec.vector, 00427 &g_vec.vector, 00428 &solution_vec.vector); 00429 c[0] = c[max_index]; 00430 00431 return status; 00432 } 00433 } 00434 00435 } else { 00436 00437 /// Natural boundary conditions 00438 00439 size_t i; 00440 size_t num_points = size; 00441 size_t max_index = num_points - 1; 00442 size_t sys_size = max_index - 1; 00443 00444 c[0] = 0.0; 00445 c[max_index] = 0.0; 00446 00447 for (i = 0; i < sys_size; i++) 00448 { 00449 const double h_i = xa[i + 1] - xa[i]; 00450 const double h_ip1 = xa[i + 2] - xa[i + 1]; 00451 const double ydiff_i = ya[i + 1] - ya[i]; 00452 const double ydiff_ip1 = ya[i + 2] - ya[i + 1]; 00453 const double g_i = (h_i != 0.0) ? 1.0 / h_i : 0.0; 00454 const double g_ip1 = (h_ip1 != 0.0) ? 1.0 / h_ip1 : 0.0; 00455 offdiag[i] = h_ip1; 00456 diag[i] = 2.0 * (h_ip1 + h_i); 00457 g[i] = 3.0 * (ydiff_ip1 * g_ip1 - ydiff_i * g_i); 00458 } 00459 00460 if (sys_size == 1) 00461 { 00462 c[1] = g[0] / diag[0]; 00463 return gsl_success; 00464 } 00465 else 00466 { 00467 gsl_vector_view g_vec = gsl_vector_view_array( g, sys_size); 00468 gsl_vector_view diag_vec = gsl_vector_view_array( diag, sys_size); 00469 gsl_vector_view offdiag_vec = 00470 gsl_vector_view_array( offdiag, sys_size - 1); 00471 gsl_vector_view solution_vec = 00472 gsl_vector_view_array (( c) + 1, sys_size); 00473 00474 int status = gsl_linalg_solve_symm_tridiag(&diag_vec.vector, 00475 &offdiag_vec.vector, 00476 &g_vec.vector, 00477 &solution_vec.vector); 00478 return status; 00479 } 00480 } 00481 } 00482 00483 00484 /// Free allocated memory 00485 virtual int free() { 00486 std::free(c); 00487 std::free(g); 00488 std::free(diag); 00489 std::free(offdiag); 00490 00491 return 0; 00492 } 00493 00494 /// Give the value of the function \f$ y(x=x_0) \f$ . 00495 virtual int interp(const vec_t &x_array, const vec_t &y_array, 00496 size_t size, double x, double &y) { 00497 00498 double x_lo, x_hi; 00499 double dx; 00500 size_t index; 00501 00502 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00503 00504 /* evaluate */ 00505 x_hi = x_array[index + 1]; 00506 x_lo = x_array[index]; 00507 dx = x_hi - x_lo; 00508 if (dx > 0.0) 00509 { 00510 const double y_lo = y_array[index]; 00511 const double y_hi = y_array[index + 1]; 00512 const double dy = y_hi - y_lo; 00513 double delx = x - x_lo; 00514 double b_i, c_i, d_i; 00515 coeff_calc( c, dy, dx, index, &b_i, &c_i, &d_i); 00516 y = y_lo + delx * (b_i + delx * (c_i + delx * d_i)); 00517 return gsl_success; 00518 } 00519 else 00520 { 00521 y = 0.0; 00522 return GSL_EINVAL; 00523 } 00524 } 00525 00526 00527 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 00528 virtual int deriv(const vec_t &x_array, const vec_t &y_array, 00529 size_t size, double x, double &dydx) { 00530 00531 double x_lo, x_hi; 00532 double dx; 00533 size_t index; 00534 00535 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00536 00537 /* evaluate */ 00538 x_hi = x_array[index + 1]; 00539 x_lo = x_array[index]; 00540 dx = x_hi - x_lo; 00541 if (dx > 0.0) 00542 { 00543 const double y_lo = y_array[index]; 00544 const double y_hi = y_array[index + 1]; 00545 const double dy = y_hi - y_lo; 00546 double delx = x - x_lo; 00547 double b_i, c_i, d_i; 00548 coeff_calc( c, dy, dx, index, &b_i, &c_i, &d_i); 00549 dydx = b_i + delx * (2.0 * c_i + 3.0 * d_i * delx); 00550 return gsl_success; 00551 } 00552 else 00553 { 00554 dydx = 0.0; 00555 return GSL_FAILURE; 00556 } 00557 } 00558 00559 /** \brief Give the value of the second derivative 00560 \f$ y^{\prime \prime}(x=x_0) \f$ . 00561 */ 00562 virtual int deriv2(const vec_t &x_array, const vec_t &y_array, 00563 size_t size, double x, double &d2ydx2) { 00564 00565 double x_lo, x_hi; 00566 double dx; 00567 size_t index; 00568 00569 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00570 00571 /* evaluate */ 00572 x_hi = x_array[index + 1]; 00573 x_lo = x_array[index]; 00574 dx = x_hi - x_lo; 00575 if (dx > 0.0) 00576 { 00577 const double y_lo = y_array[index]; 00578 const double y_hi = y_array[index + 1]; 00579 const double dy = y_hi - y_lo; 00580 double delx = x - x_lo; 00581 double b_i, c_i, d_i; 00582 coeff_calc( c, dy, dx, index, &b_i, &c_i, &d_i); 00583 d2ydx2 = 2.0 * c_i + 6.0 * d_i * delx; 00584 return gsl_success; 00585 } 00586 else 00587 { 00588 d2ydx2 = 0.0; 00589 return GSL_FAILURE; 00590 } 00591 } 00592 00593 public: 00594 00595 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 00596 virtual int integ(const vec_t &x_array, const vec_t &y_array, 00597 size_t size, double a, double b, 00598 double &result) { 00599 00600 size_t i, index_a, index_b; 00601 00602 index_a=this->sv.bsearch_inc(a,x_array,0,size-1); 00603 index_b=this->sv.bsearch_inc(b,x_array,0,size-1); 00604 00605 result = 0.0; 00606 00607 /* interior intervals */ 00608 for(i=index_a; i<=index_b; i++) { 00609 const double x_hi = x_array[i + 1]; 00610 const double x_lo = x_array[i]; 00611 const double y_lo = y_array[i]; 00612 const double y_hi = y_array[i + 1]; 00613 const double dx = x_hi - x_lo; 00614 const double dy = y_hi - y_lo; 00615 if(dx != 0.0) { 00616 double b_i, c_i, d_i; 00617 coeff_calc( c, dy, dx, i, &b_i, &c_i, &d_i); 00618 00619 if (i == index_a || i == index_b) 00620 { 00621 double x1 = (i == index_a) ? a : x_lo; 00622 double x2 = (i == index_b) ? b : x_hi; 00623 result += this->integ_eval(y_lo, b_i, c_i, d_i, x_lo, x1, x2); 00624 } 00625 else 00626 { 00627 result += dx * (y_lo + dx*(0.5*b_i + 00628 dx*(c_i/3.0 + 0.25*d_i*dx))); 00629 } 00630 } 00631 else { 00632 result = 0.0; 00633 return GSL_FAILURE; 00634 } 00635 } 00636 00637 return gsl_success; 00638 } 00639 00640 }; 00641 00642 /** 00643 \brief Cubic spline interpolation with periodic 00644 boundary conditions (GSL) 00645 00646 This is convenient to allow interpolation objects to 00647 be supplied as template parameters 00648 */ 00649 template<class vec_t> class cspline_peri_interp : 00650 public cspline_interp<vec_t> { 00651 00652 public: 00653 cspline_peri_interp() : cspline_interp<vec_t>(true) { 00654 } 00655 00656 }; 00657 00658 /** 00659 \brief Akima spline interpolation (GSL) 00660 00661 \future It appears that the interp() function below searches 00662 for indices slightly differently than the original GSL 00663 eval() function. This should be checked, as it might be 00664 slightly non-optimal in terms of speed (shouldn't matter for 00665 the accuracy). 00666 */ 00667 template<class vec_t> class akima_interp : public base_interp<vec_t> { 00668 00669 #ifndef DOXYGEN_INTERNAL 00670 00671 protected: 00672 00673 /// \name Storage for Akima spline interpolation 00674 //@{ 00675 double *b; 00676 double *c; 00677 double *d; 00678 double *um; 00679 //@} 00680 00681 /// True for periodic boundary conditions 00682 bool peri; 00683 00684 /// For initializing the interpolation 00685 void akima_calc(const vec_t &x_array, size_t size, double m[]) { 00686 size_t i; 00687 00688 for (i = 0; i < (size - 1); i++) { 00689 00690 const double NE = fabs (m[i + 1] - m[i]) + 00691 fabs (m[i - 1] - m[i - 2]); 00692 00693 if (NE == 0.0) { 00694 b[i] = m[i]; 00695 c[i] = 0.0; 00696 d[i] = 0.0; 00697 } else { 00698 const double h_i = x_array[i + 1] - x_array[i]; 00699 const double NE_next = fabs (m[i + 2] - m[i + 1]) + 00700 fabs (m[i] - m[i - 1]); 00701 const double alpha_i = fabs (m[i - 1] - m[i - 2]) / NE; 00702 double alpha_ip1; 00703 double tL_ip1; 00704 if (NE_next == 0.0) { 00705 tL_ip1 = m[i]; 00706 } else { 00707 alpha_ip1 = fabs (m[i] - m[i - 1]) / NE_next; 00708 tL_ip1 = (1.0 - alpha_ip1) * m[i] + alpha_ip1 * m[i + 1]; 00709 } 00710 b[i] = (1.0 - alpha_i) * m[i - 1] + alpha_i * m[i]; 00711 c[i] = (3.0 * m[i] - 2.0 * b[i] - tL_ip1) / h_i; 00712 d[i] = (b[i] + tL_ip1 - 2.0 * m[i]) / (h_i * h_i); 00713 } 00714 } 00715 } 00716 00717 #endif 00718 00719 public: 00720 00721 /** \brief Create a base interpolation object with or without 00722 periodic boundary conditions 00723 */ 00724 akima_interp(bool periodic=false) { 00725 peri=periodic; 00726 this->min_size=5; 00727 } 00728 00729 virtual ~akima_interp() {} 00730 00731 /// Allocate memory, assuming x and y have size \c size 00732 virtual int allocate(size_t size) { 00733 00734 b = (double *) malloc (size * sizeof (double)); 00735 00736 if (b==NULL) { 00737 GSL_ERROR_NULL("failed to allocate space for c", GSL_ENOMEM); 00738 } 00739 00740 c = (double *) malloc (size * sizeof (double)); 00741 00742 if (c==NULL) { 00743 std::free(b); 00744 GSL_ERROR_NULL("failed to allocate space for g", GSL_ENOMEM); 00745 } 00746 00747 d = (double *) malloc (size * sizeof (double)); 00748 00749 if (d==NULL) { 00750 std::free(b); 00751 std::free(c); 00752 GSL_ERROR_NULL("failed to allocate space for diag", GSL_ENOMEM); 00753 } 00754 00755 um = (double *) malloc ((size+4) * sizeof (double)); 00756 00757 if (um==NULL) { 00758 std::free(b); 00759 std::free(c); 00760 std::free(d); 00761 GSL_ERROR_NULL("failed to allocate space for offdiag", GSL_ENOMEM); 00762 } 00763 00764 return 0; 00765 } 00766 00767 /// Initialize interpolation routine 00768 virtual int init(const vec_t &xa, const vec_t &ya, size_t size) { 00769 00770 if (peri) { 00771 00772 /// Periodic boundary conditions 00773 00774 double *m=um+2; 00775 size_t i; 00776 for (i = 0; i <= size - 2; i++) 00777 { 00778 m[i] = (ya[i + 1] - ya[i]) / (xa[i + 1] - xa[i]); 00779 } 00780 00781 /* periodic boundary conditions */ 00782 m[-2] = m[size - 1 - 2]; 00783 m[-1] = m[size - 1 - 1]; 00784 m[size - 1] = m[0]; 00785 m[size] = m[1]; 00786 00787 akima_calc (xa,size, m); 00788 00789 } else { 00790 00791 /// Non-periodic boundary conditions 00792 00793 double *m=um+2; 00794 size_t i; 00795 for (i = 0; i <= size - 2; i++) { 00796 m[i] = (ya[i + 1] - ya[i]) / (xa[i + 1] - xa[i]); 00797 } 00798 00799 /* non-periodic boundary conditions */ 00800 m[-2] = 3.0 * m[0] - 2.0 * m[1]; 00801 m[-1] = 2.0 * m[0] - m[1]; 00802 m[size - 1] = 2.0 * m[size - 2] - m[size - 3]; 00803 m[size] = 3.0 * m[size - 2] - 2.0 * m[size - 3]; 00804 00805 akima_calc (xa, size, m); 00806 00807 } 00808 return 0; 00809 } 00810 00811 00812 /// Free allocated memory 00813 virtual int free() { 00814 std::free(b); 00815 std::free(c); 00816 std::free(d); 00817 std::free(um); 00818 00819 return 0; 00820 } 00821 00822 /// Give the value of the function \f$ y(x=x_0) \f$ . 00823 virtual int interp(const vec_t &x_array, const vec_t &y_array, 00824 size_t size, double x, double &y) { 00825 00826 size_t index; 00827 00828 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00829 00830 const double x_lo = x_array[index]; 00831 const double delx = x - x_lo; 00832 const double bb = b[index]; 00833 const double cc = c[index]; 00834 const double dd = d[index]; 00835 y = y_array[index] + delx * (bb + delx * (cc + dd * delx)); 00836 return gsl_success; 00837 } 00838 00839 00840 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 00841 virtual int deriv(const vec_t &x_array, const vec_t &y_array, 00842 size_t size, double x, double &dydx) { 00843 00844 size_t index; 00845 00846 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00847 double x_lo = x_array[index]; 00848 double delx = x - x_lo; 00849 double bb = b[index]; 00850 double cc = c[index]; 00851 double dd = d[index]; 00852 dydx = bb + delx * (2.0 * cc + 3.0 * dd * delx); 00853 return gsl_success; 00854 00855 } 00856 00857 /** \brief Give the value of the second derivative 00858 \f$ y^{\prime \prime}(x=x_0) \f$ . 00859 */ 00860 virtual int deriv2(const vec_t &x_array, const vec_t &y_array, 00861 size_t size, double x, double &d2ydx2) { 00862 00863 size_t index; 00864 00865 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00866 00867 const double x_lo = x_array[index]; 00868 const double delx = x - x_lo; 00869 const double cc = c[index]; 00870 const double dd = d[index]; 00871 d2ydx2 = 2.0 * cc + 6.0 * dd * delx; 00872 return gsl_success; 00873 00874 } 00875 00876 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 00877 virtual int integ(const vec_t &x_array, const vec_t &y_array, 00878 size_t size, double aa, double bb, 00879 double &result) { 00880 00881 size_t i, index_a, index_b; 00882 00883 index_a=this->sv.bsearch_inc(aa,x_array,0,size-1); 00884 index_b=this->sv.bsearch_inc(bb,x_array,0,size-1); 00885 00886 result = 0.0; 00887 00888 /* interior intervals */ 00889 00890 for(i=index_a; i<=index_b; i++) { 00891 00892 const double x_hi = x_array[i + 1]; 00893 const double x_lo = x_array[i]; 00894 const double y_lo = y_array[i]; 00895 const double dx = x_hi - x_lo; 00896 00897 if(dx != 0.0) { 00898 00899 if (i==index_a || i==index_b) { 00900 double x1 = (i==index_a) ? aa : x_lo; 00901 double x2 = (i==index_b) ? bb : x_hi; 00902 result += this->integ_eval(y_lo,b[i],c[i],d[i],x_lo,x1,x2); 00903 } else { 00904 result+=dx*(y_lo+dx*(0.5*b[i]+dx*(c[i]/3.0+0.25*d[i]*dx))); 00905 } 00906 00907 } else { 00908 result = 0.0; 00909 return GSL_FAILURE; 00910 } 00911 } 00912 00913 return gsl_success; 00914 } 00915 00916 }; 00917 00918 /** 00919 \brief Akima spline interpolation with periodic 00920 boundary conditions (GSL) 00921 00922 This is convenient to allow interpolation objects to 00923 be supplied as template parameters 00924 */ 00925 template<class vec_t> class akima_peri_interp : 00926 public akima_interp<vec_t> { 00927 00928 public: 00929 akima_peri_interp() : akima_interp<vec_t>(true) { 00930 } 00931 00932 }; 00933 00934 /** 00935 \brief Interpolation class 00936 00937 This interpolation class is intended to be sufficiently general 00938 to handle any vector type. Interpolation of \ref ovector like 00939 objects is performed with the default template parameters, and 00940 \ref array_interp is provided for simple interpolation on 00941 C-style arrays. 00942 00943 The type of interpolation to be performed can be specified using 00944 the set_type() function or in the constructor. The default is 00945 cubic splines with natural boundary conditions. 00946 00947 The class automatically handles decreasing arrays by converting 00948 from an object of type \c vec_t to an object ov type \c rvec_t. 00949 00950 While \c vec_t may be any vector type which allows indexing via 00951 \c [], \c rvec_t must be a vector type which allows indexing 00952 and has a constructor with one of the two forms 00953 \verbatim 00954 rvec_t::rvec_t(vec_t &v); 00955 rvec_t::rvec_t(vec_t v); 00956 \endverbatim 00957 so that o2scl_interp can automatically "reverse" a vector if 00958 necessary. 00959 00960 It is important that different instances of o2scl_interp_vec 00961 and o2scl_interp not be given the same interpolation objects, 00962 as they will clash. 00963 00964 */ 00965 template<class vec_t=ovector_view, class rvec_t=ovector_const_reverse> 00966 class o2scl_interp { 00967 00968 #ifndef DOXYGEN_INTERNAL 00969 00970 protected: 00971 00972 /// Pointer to base interpolation object 00973 base_interp<vec_t> *itp; 00974 00975 /// Pointer to base interpolation object for reversed vectors 00976 base_interp<rvec_t> *ritp; 00977 00978 #endif 00979 00980 public: 00981 00982 /// Create with base interpolation objects \c it and \c rit 00983 o2scl_interp(base_interp<vec_t> &it, base_interp<rvec_t> &rit) { 00984 itp=⁢ 00985 ritp=&rit; 00986 } 00987 00988 /** \brief Create with base interpolation object \c it and use 00989 \ref def_ritp for reverse interpolation if necessary 00990 */ 00991 o2scl_interp(base_interp<vec_t> &it) { 00992 itp=⁢ 00993 ritp=&def_ritp; 00994 } 00995 00996 /** \brief Create an interpolator using \ref def_itp and 00997 \ref def_ritp 00998 */ 00999 o2scl_interp() { 01000 itp=&def_itp; 01001 ritp=&def_ritp; 01002 } 01003 01004 virtual ~o2scl_interp() {} 01005 01006 /// Give the value of the function \f$ y(x=x_0) \f$ . 01007 virtual double interp(const double x0, size_t n, const vec_t &x, 01008 const vec_t &y) 01009 { 01010 01011 double ret=0.0; 01012 01013 err_hnd->reset(); 01014 01015 if (n<itp->min_size) { 01016 set_err("Vector size too small in interp().",gsl_edom); 01017 } 01018 01019 if (x[0]>x[n-1]) { 01020 01021 // Decreasing case 01022 01023 rvec_t ax(x); 01024 rvec_t ay(y); 01025 01026 int r1=ritp->allocate(n); 01027 if (r1==0) { 01028 int r2=ritp->init(ax,ay,n); 01029 if (r2==0) { 01030 ritp->interp(ax,ay,n,x0,ret); 01031 ritp->free(); 01032 } 01033 } 01034 01035 } else { 01036 01037 // Increasing case 01038 01039 int r1=itp->allocate(n); 01040 if (r1==0) { 01041 int r2=itp->init(x,y,n); 01042 if (r2==0) { 01043 itp->interp(x,y,n,x0,ret); 01044 itp->free(); 01045 } 01046 } 01047 01048 } 01049 01050 return ret; 01051 } 01052 01053 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 01054 virtual double deriv(const double x0, size_t n, const vec_t &x, 01055 const vec_t &y) 01056 { 01057 01058 double ret=0.0; 01059 01060 err_hnd->reset(); 01061 01062 if (n<itp->min_size) { 01063 set_err("Vector size too small in interp().",gsl_edom); 01064 } 01065 01066 if (x[0]>x[n-1]) { 01067 01068 rvec_t ax(x); 01069 rvec_t ay(y); 01070 01071 int r1=ritp->allocate(n); 01072 if (r1==0) { 01073 int r2=ritp->init(ax,ay,n); 01074 if (r2==0) { 01075 ritp->deriv(ax,ay,n,x0,ret); 01076 ritp->free(); 01077 } 01078 } 01079 01080 } else { 01081 01082 // Increasing case 01083 01084 int r1=itp->allocate(n); 01085 if (r1==0) { 01086 int r2=itp->init(x,y,n); 01087 if (r2==0) { 01088 itp->deriv(x,y,n,x0,ret); 01089 itp->free(); 01090 } 01091 } 01092 } 01093 01094 return ret; 01095 } 01096 01097 /** \brief Give the value of the second derivative 01098 \f$ y^{\prime \prime}(x=x_0) \f$ . 01099 */ 01100 virtual double deriv2(const double x0, size_t n, const vec_t &x, 01101 const vec_t &y) 01102 { 01103 01104 double ret=0.0; 01105 01106 err_hnd->reset(); 01107 01108 if (n<itp->min_size) { 01109 01110 set_err("Vector size too small in deriv2().",gsl_edom); 01111 01112 } 01113 01114 if (x[0]>x[n-1]) { 01115 01116 // Decreasing case 01117 01118 rvec_t ax(x); 01119 rvec_t ay(y); 01120 01121 int r1=ritp->allocate(n); 01122 if (r1==0) { 01123 int r2=ritp->init(ax,ay,n); 01124 if (r2==0) { 01125 ritp->deriv2(ax,ay,n,x0,ret); 01126 ritp->free(); 01127 } 01128 } 01129 01130 } else { 01131 01132 // Increasing case 01133 01134 int r1=itp->allocate(n); 01135 if (r1==0) { 01136 int r2=itp->init(x,y,n); 01137 if (r2==0) { 01138 itp->deriv2(x,y,n,x0,ret); 01139 itp->free(); 01140 } 01141 } 01142 01143 } 01144 01145 return ret; 01146 } 01147 01148 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 01149 virtual double integ(const double x1, const double x2, size_t n, 01150 const vec_t &x, const vec_t &y) 01151 { 01152 01153 double ret=0.0; 01154 01155 err_hnd->reset(); 01156 01157 if (n<itp->min_size) { 01158 01159 set_err("Vector size too small in integ().",gsl_edom); 01160 01161 } 01162 01163 if (x[0]>x[n-1]) { 01164 01165 // Decreasing case 01166 01167 rvec_t ax(x); 01168 rvec_t ay(y); 01169 01170 int r1=ritp->allocate(n); 01171 if (r1==0) { 01172 int r2=ritp->init(ax,ay,n); 01173 if (r2==0) { 01174 ritp->integ(ax,ay,n,x1,x2,ret); 01175 ritp->free(); 01176 } 01177 } 01178 01179 } else { 01180 01181 // Increasing case 01182 01183 int r1=itp->allocate(n); 01184 if (r1==0) { 01185 int r2=itp->init(x,y,n); 01186 if (r2==0) { 01187 itp->integ(x,y,n,x1,x2,ret); 01188 itp->free(); 01189 } 01190 } 01191 01192 } 01193 01194 return ret; 01195 } 01196 01197 /// Set base interpolation object 01198 int set_type(base_interp<vec_t> &it, base_interp<rvec_t> &rit) { 01199 itp=⁢ 01200 ritp=&rit; 01201 return 0; 01202 } 01203 01204 /** \brief Default base interpolation object (cubic spline with natural 01205 boundary conditions) 01206 */ 01207 cspline_interp<vec_t> def_itp; 01208 01209 /** \brief Default base interpolation object for reversed vectors 01210 (cubic spline with natural boundary conditions) 01211 */ 01212 cspline_interp<rvec_t> def_ritp; 01213 01214 }; 01215 01216 /** 01217 \brief Interpolation class for pre-specified vector 01218 01219 This interpolation class is intended to be sufficiently general 01220 to handle any vector type. Interpolation of \ref ovector like 01221 objects is performed with the default template parameters, and 01222 \ref array_interp_vec is provided for simple interpolation on 01223 C-style arrays. 01224 01225 The class automatically handles decreasing arrays by copying the 01226 old array to a reversed version. For several interpolations on 01227 the same data, copying the initial array can be faster than 01228 overloading operator[]. 01229 01230 The type of interpolation to be performed can be specified 01231 using the set_type() function. The default is cubic splines 01232 with natural boundary conditions. 01233 01234 It is important that different instances of o2scl_interp_vec 01235 and o2scl_interp not be given the same interpolation objects, 01236 as they will clash. 01237 01238 \todo Need to fix constructor to behave properly if init() fails. 01239 It should free the memory and set \c ln to zero. 01240 */ 01241 template<class vec_t=ovector_view, class alloc_vec_t=ovector, 01242 class alloc_t=ovector_alloc> class o2scl_interp_vec { 01243 01244 #ifndef DOXYGEN_INTERNAL 01245 01246 protected: 01247 01248 /// Memory allocator for objects of type \c alloc_vec_t 01249 alloc_t ao; 01250 01251 /// Pointer to base interpolation object 01252 base_interp<vec_t> *itp; 01253 01254 /// True if the original array was increasing 01255 bool inc; 01256 01257 /// Pointer to the user-specified x vector 01258 const vec_t *lx; 01259 01260 /// Pointer to the user-specified x vector 01261 const vec_t *ly; 01262 01263 /// Reversed version of x 01264 alloc_vec_t lrx; 01265 01266 /// Reversed version of y 01267 alloc_vec_t lry; 01268 01269 /// Size of user-specified vectors 01270 size_t ln; 01271 01272 #endif 01273 01274 public: 01275 01276 /// Create with base interpolation object \c it 01277 o2scl_interp_vec(base_interp<vec_t> &it, 01278 size_t n, const vec_t &x, const vec_t &y) { 01279 itp=⁢ 01280 ln=0; 01281 01282 if (n<itp->min_size) { 01283 set_err("Vector size too small in o2scl_interp_vec().",gsl_edom); 01284 } else { 01285 size_t iend=n-1; 01286 01287 if (x[0]>x[iend]) { 01288 01289 ao.allocate(lrx,n); 01290 ao.allocate(lry,n); 01291 01292 for(size_t i=0;i<n;i++) { 01293 lrx[iend-i]=x[i]; 01294 lry[iend-i]=y[i]; 01295 } 01296 01297 int r1=itp->allocate(n); 01298 if (r1==0) { 01299 itp->init(lrx,lry,n); 01300 ln=n; 01301 inc=false; 01302 } 01303 01304 } else { 01305 01306 int r1=itp->allocate(n); 01307 if (r1==0) { 01308 itp->init(x,y,n); 01309 ln=n; 01310 lx=&x; 01311 ly=&y; 01312 inc=true; 01313 } 01314 01315 } 01316 01317 } 01318 01319 } 01320 01321 virtual ~o2scl_interp_vec() { 01322 if (ln>0) { 01323 itp->free(); 01324 } 01325 if (inc==false) { 01326 ao.free(lrx); 01327 ao.free(lry); 01328 } 01329 } 01330 01331 /// Give the value of the function \f$ y(x=x_0) \f$ . 01332 virtual double interp(const double x0) { 01333 double ret=0.0; 01334 if (ln>0) { 01335 if (inc) itp->interp(*lx,*ly,ln,x0,ret); 01336 else itp->interp(lrx,lry,ln,x0,ret); 01337 } 01338 return ret; 01339 } 01340 01341 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 01342 virtual double deriv(const double x0) { 01343 double ret=0.0; 01344 if (ln>0) { 01345 if (inc) itp->deriv(*lx,*ly,ln,x0,ret); 01346 else itp->deriv(lrx,lry,ln,x0,ret); 01347 } 01348 return ret; 01349 } 01350 01351 /** \brief Give the value of the second derivative 01352 \f$ y^{\prime \prime}(x=x_0) \f$ . 01353 */ 01354 virtual double deriv2(const double x0) { 01355 double ret=0.0; 01356 if (ln>0) { 01357 if (inc) itp->deriv2(*lx,*ly,ln,x0,ret); 01358 else itp->deriv2(lrx,lry,ln,x0,ret); 01359 } 01360 return ret; 01361 } 01362 01363 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 01364 virtual double integ(const double x1, const double x2) { 01365 double ret=0.0; 01366 if (ln>0) { 01367 if (inc) itp->integ(*lx,*ly,ln,x1,x2,ret); 01368 else itp->integ(lrx,lry,ln,x1,x2,ret); 01369 } 01370 return ret; 01371 } 01372 01373 /// Set base interpolation object 01374 int set_type(base_interp<vec_t> &it) { 01375 itp=⁢ 01376 return 0; 01377 } 01378 01379 /** \brief Default base interpolation object (cubic spline with natural 01380 boundary conditions) 01381 */ 01382 cspline_interp<vec_t> def_itp; 01383 01384 }; 01385 01386 /** \brief A specialization of o2scl_interp for C-style double arrays 01387 */ 01388 template<size_t n> class array_interp : 01389 public o2scl_interp<double[n],array_const_reverse<n> > { 01390 01391 public: 01392 01393 /// Create with base interpolation objects \c it and \c rit 01394 array_interp(base_interp<double[n]> &it, 01395 base_interp<array_const_reverse<n> > &rit) 01396 : o2scl_interp<double[n],array_const_reverse<n> >(it,rit) { 01397 } 01398 01399 /** \brief Create with base interpolation object \c it and use 01400 the default base interpolation object for reversed arrays 01401 */ 01402 array_interp(base_interp<double[n]> &it) : 01403 o2scl_interp<double[n],array_const_reverse<n> >(it) { 01404 } 01405 01406 /** \brief Create an interpolator using the default base 01407 interpolation objects 01408 */ 01409 array_interp() : 01410 o2scl_interp<double[n],array_const_reverse<n> >() { 01411 } 01412 01413 }; 01414 01415 /** 01416 \brief A specialization of o2scl_interp_vec for C-style arrays 01417 */ 01418 template<class arr_t> class array_interp_vec : 01419 public o2scl_interp_vec<arr_t,arr_t,array_alloc<arr_t> > { 01420 01421 public: 01422 01423 /// Create with base interpolation object \c it 01424 array_interp_vec(base_interp<arr_t> &it, 01425 size_t nv, const arr_t &x, const arr_t &y) : 01426 o2scl_interp_vec<arr_t,arr_t,array_alloc<arr_t> >(it,nv,x,y) { 01427 } 01428 01429 }; 01430 01431 #ifndef DOXYGENP 01432 } 01433 #endif 01434 01435 #endif
Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.
Project hosting provided by
,
O2scl Sourceforge Project Page