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 if (c == 0) { 00319 set_err2_ret("Failed to allocate space for c", 00320 " in cspline_interp::allocate().",gsl_enomem); 00321 } 00322 00323 g = (double *) malloc (size * sizeof (double)); 00324 if (g == 0) { 00325 std::free(c); 00326 set_err2_ret("Failed to allocate space for g", 00327 " in cspline_interp::allocate().",gsl_enomem); 00328 } 00329 00330 diag = (double *) malloc (size * sizeof (double)); 00331 if (diag == 0) { 00332 std::free(g); 00333 std::free(c); 00334 set_err2_ret("Failed to allocate space for diag", 00335 " in cspline_interp::allocate().",gsl_enomem); 00336 } 00337 00338 offdiag = (double *) malloc (size * sizeof (double)); 00339 if (offdiag == 0) { 00340 std::free(diag); 00341 std::free(g); 00342 std::free(c); 00343 set_err2_ret("Failed to allocate space for offdiag", 00344 " in cspline_interp::allocate().",gsl_enomem); 00345 } 00346 00347 return 0; 00348 } 00349 00350 /// Initialize interpolation routine 00351 virtual int init(const vec_t &xa, const vec_t &ya, size_t size) { 00352 00353 if (peri) { 00354 00355 /// Periodic boundary conditions 00356 00357 size_t i; 00358 size_t num_points = size; 00359 /* Engeln-Mullges + Uhlig "n" */ 00360 size_t max_index = num_points - 1; 00361 /* linear system is sys_size x sys_size */ 00362 size_t sys_size = max_index; 00363 00364 if (sys_size == 2) { 00365 /* solve 2x2 system */ 00366 00367 const double h0 = xa[1] - xa[0]; 00368 const double h1 = xa[2] - xa[1]; 00369 const double h2 = xa[3] - xa[2]; 00370 const double A = 2.0*(h0 + h1); 00371 const double B = h0 + h1; 00372 double gx[2]; 00373 double det; 00374 00375 gx[0] = 3.0 * ((ya[2] - ya[1]) / h1 - (ya[1] - ya[0]) / h0); 00376 gx[1] = 3.0 * ((ya[1] - ya[2]) / h2 - (ya[2] - ya[1]) / h1); 00377 00378 det = 3.0 * (h0 + h1) * (h0 + h1); 00379 c[1] = ( A * gx[0] - B * gx[1])/det; 00380 c[2] = (-B * gx[0] + A * gx[1])/det; 00381 c[0] = c[2]; 00382 00383 return gsl_success; 00384 00385 } else { 00386 00387 for (i = 0; i < sys_size-1; i++) { 00388 const double h_i = xa[i + 1] - xa[i]; 00389 const double h_ip1 = xa[i + 2] - xa[i + 1]; 00390 const double ydiff_i = ya[i + 1] - ya[i]; 00391 const double ydiff_ip1 = ya[i + 2] - ya[i + 1]; 00392 const double g_i = (h_i != 0.0) ? 1.0 / h_i : 0.0; 00393 const double g_ip1 = (h_ip1 != 0.0) ? 1.0 / h_ip1 : 0.0; 00394 offdiag[i] = h_ip1; 00395 diag[i] = 2.0 * (h_ip1 + h_i); 00396 g[i] = 3.0 * (ydiff_ip1 * g_ip1 - ydiff_i * g_i); 00397 } 00398 00399 i = sys_size - 1; 00400 { 00401 const double h_i = xa[i + 1] - xa[i]; 00402 const double h_ip1 = xa[1] - xa[0]; 00403 const double ydiff_i = ya[i + 1] - ya[i]; 00404 const double ydiff_ip1 = ya[1] - ya[0]; 00405 const double g_i = (h_i != 0.0) ? 1.0 / h_i : 0.0; 00406 const double g_ip1 = (h_ip1 != 0.0) ? 1.0 / h_ip1 : 0.0; 00407 offdiag[i] = h_ip1; 00408 diag[i] = 2.0 * (h_ip1 + h_i); 00409 g[i] = 3.0 * (ydiff_ip1 * g_ip1 - ydiff_i * g_i); 00410 } 00411 00412 { 00413 gsl_vector_view g_vec = gsl_vector_view_array( g, sys_size); 00414 gsl_vector_view diag_vec = 00415 gsl_vector_view_array( diag,sys_size); 00416 gsl_vector_view offdiag_vec = 00417 gsl_vector_view_array( offdiag, sys_size); 00418 gsl_vector_view solution_vec = 00419 gsl_vector_view_array (( c) + 1, sys_size); 00420 int status = 00421 gsl_linalg_solve_symm_cyc_tridiag(&diag_vec.vector, 00422 &offdiag_vec.vector, 00423 &g_vec.vector, 00424 &solution_vec.vector); 00425 c[0] = c[max_index]; 00426 00427 return status; 00428 } 00429 } 00430 00431 } else { 00432 00433 /// Natural boundary conditions 00434 00435 size_t i; 00436 size_t num_points = size; 00437 size_t max_index = num_points - 1; 00438 size_t sys_size = max_index - 1; 00439 00440 c[0] = 0.0; 00441 c[max_index] = 0.0; 00442 00443 for (i = 0; i < sys_size; i++) 00444 { 00445 const double h_i = xa[i + 1] - xa[i]; 00446 const double h_ip1 = xa[i + 2] - xa[i + 1]; 00447 const double ydiff_i = ya[i + 1] - ya[i]; 00448 const double ydiff_ip1 = ya[i + 2] - ya[i + 1]; 00449 const double g_i = (h_i != 0.0) ? 1.0 / h_i : 0.0; 00450 const double g_ip1 = (h_ip1 != 0.0) ? 1.0 / h_ip1 : 0.0; 00451 offdiag[i] = h_ip1; 00452 diag[i] = 2.0 * (h_ip1 + h_i); 00453 g[i] = 3.0 * (ydiff_ip1 * g_ip1 - ydiff_i * g_i); 00454 } 00455 00456 if (sys_size == 1) 00457 { 00458 c[1] = g[0] / diag[0]; 00459 return gsl_success; 00460 } 00461 else 00462 { 00463 gsl_vector_view g_vec = gsl_vector_view_array( g, sys_size); 00464 gsl_vector_view diag_vec = gsl_vector_view_array( diag, sys_size); 00465 gsl_vector_view offdiag_vec = 00466 gsl_vector_view_array( offdiag, sys_size - 1); 00467 gsl_vector_view solution_vec = 00468 gsl_vector_view_array (( c) + 1, sys_size); 00469 00470 int status = gsl_linalg_solve_symm_tridiag(&diag_vec.vector, 00471 &offdiag_vec.vector, 00472 &g_vec.vector, 00473 &solution_vec.vector); 00474 return status; 00475 } 00476 } 00477 } 00478 00479 00480 /// Free allocated memory 00481 virtual int free() { 00482 std::free(c); 00483 std::free(g); 00484 std::free(diag); 00485 std::free(offdiag); 00486 00487 return 0; 00488 } 00489 00490 /// Give the value of the function \f$ y(x=x_0) \f$ . 00491 virtual int interp(const vec_t &x_array, const vec_t &y_array, 00492 size_t size, double x, double &y) { 00493 00494 double x_lo, x_hi; 00495 double dx; 00496 size_t index; 00497 00498 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00499 00500 /* evaluate */ 00501 x_hi = x_array[index + 1]; 00502 x_lo = x_array[index]; 00503 dx = x_hi - x_lo; 00504 if (dx > 0.0) 00505 { 00506 const double y_lo = y_array[index]; 00507 const double y_hi = y_array[index + 1]; 00508 const double dy = y_hi - y_lo; 00509 double delx = x - x_lo; 00510 double b_i, c_i, d_i; 00511 coeff_calc( c, dy, dx, index, &b_i, &c_i, &d_i); 00512 y = y_lo + delx * (b_i + delx * (c_i + delx * d_i)); 00513 return gsl_success; 00514 } 00515 else 00516 { 00517 y = 0.0; 00518 return GSL_EINVAL; 00519 } 00520 } 00521 00522 00523 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 00524 virtual int deriv(const vec_t &x_array, const vec_t &y_array, 00525 size_t size, double x, double &dydx) { 00526 00527 double x_lo, x_hi; 00528 double dx; 00529 size_t index; 00530 00531 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00532 00533 /* evaluate */ 00534 x_hi = x_array[index + 1]; 00535 x_lo = x_array[index]; 00536 dx = x_hi - x_lo; 00537 if (dx > 0.0) 00538 { 00539 const double y_lo = y_array[index]; 00540 const double y_hi = y_array[index + 1]; 00541 const double dy = y_hi - y_lo; 00542 double delx = x - x_lo; 00543 double b_i, c_i, d_i; 00544 coeff_calc( c, dy, dx, index, &b_i, &c_i, &d_i); 00545 dydx = b_i + delx * (2.0 * c_i + 3.0 * d_i * delx); 00546 return gsl_success; 00547 } 00548 else 00549 { 00550 dydx = 0.0; 00551 return GSL_FAILURE; 00552 } 00553 } 00554 00555 /** \brief Give the value of the second derivative 00556 \f$ y^{\prime \prime}(x=x_0) \f$ . 00557 */ 00558 virtual int deriv2(const vec_t &x_array, const vec_t &y_array, 00559 size_t size, double x, double &d2ydx2) { 00560 00561 double x_lo, x_hi; 00562 double dx; 00563 size_t index; 00564 00565 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00566 00567 /* evaluate */ 00568 x_hi = x_array[index + 1]; 00569 x_lo = x_array[index]; 00570 dx = x_hi - x_lo; 00571 if (dx > 0.0) 00572 { 00573 const double y_lo = y_array[index]; 00574 const double y_hi = y_array[index + 1]; 00575 const double dy = y_hi - y_lo; 00576 double delx = x - x_lo; 00577 double b_i, c_i, d_i; 00578 coeff_calc( c, dy, dx, index, &b_i, &c_i, &d_i); 00579 d2ydx2 = 2.0 * c_i + 6.0 * d_i * delx; 00580 return gsl_success; 00581 } 00582 else 00583 { 00584 d2ydx2 = 0.0; 00585 return GSL_FAILURE; 00586 } 00587 } 00588 00589 public: 00590 00591 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 00592 virtual int integ(const vec_t &x_array, const vec_t &y_array, 00593 size_t size, double a, double b, 00594 double &result) { 00595 00596 size_t i, index_a, index_b; 00597 00598 index_a=this->sv.bsearch_inc(a,x_array,0,size-1); 00599 index_b=this->sv.bsearch_inc(b,x_array,0,size-1); 00600 00601 result = 0.0; 00602 00603 /* interior intervals */ 00604 for(i=index_a; i<=index_b; i++) { 00605 const double x_hi = x_array[i + 1]; 00606 const double x_lo = x_array[i]; 00607 const double y_lo = y_array[i]; 00608 const double y_hi = y_array[i + 1]; 00609 const double dx = x_hi - x_lo; 00610 const double dy = y_hi - y_lo; 00611 if(dx != 0.0) { 00612 double b_i, c_i, d_i; 00613 coeff_calc( c, dy, dx, i, &b_i, &c_i, &d_i); 00614 00615 if (i == index_a || i == index_b) 00616 { 00617 double x1 = (i == index_a) ? a : x_lo; 00618 double x2 = (i == index_b) ? b : x_hi; 00619 result += this->integ_eval(y_lo, b_i, c_i, d_i, x_lo, x1, x2); 00620 } 00621 else 00622 { 00623 result += dx * (y_lo + dx*(0.5*b_i + 00624 dx*(c_i/3.0 + 0.25*d_i*dx))); 00625 } 00626 } 00627 else { 00628 result = 0.0; 00629 return GSL_FAILURE; 00630 } 00631 } 00632 00633 return gsl_success; 00634 } 00635 00636 }; 00637 00638 /** 00639 \brief Cubic spline interpolation with periodic 00640 boundary conditions (GSL) 00641 00642 This is convenient to allow interpolation objects to 00643 be supplied as template parameters 00644 */ 00645 template<class vec_t> class cspline_peri_interp : 00646 public cspline_interp<vec_t> { 00647 00648 public: 00649 cspline_peri_interp() : cspline_interp<vec_t>(true) { 00650 } 00651 00652 }; 00653 00654 /** 00655 \brief Akima spline interpolation (GSL) 00656 00657 \future It appears that the interp() function below searches 00658 for indices slightly differently than the original GSL 00659 eval() function. This should be checked, as it might be 00660 slightly non-optimal in terms of speed (shouldn't matter for 00661 the accuracy). 00662 */ 00663 template<class vec_t> class akima_interp : public base_interp<vec_t> { 00664 00665 #ifndef DOXYGEN_INTERNAL 00666 00667 protected: 00668 00669 /// \name Storage for Akima spline interpolation 00670 //@{ 00671 double *b; 00672 double *c; 00673 double *d; 00674 double *um; 00675 //@} 00676 00677 /// True for periodic boundary conditions 00678 bool peri; 00679 00680 /// For initializing the interpolation 00681 void akima_calc(const vec_t &x_array, size_t size, double m[]) { 00682 size_t i; 00683 00684 for (i = 0; i < (size - 1); i++) { 00685 00686 const double NE = fabs (m[i + 1] - m[i]) + 00687 fabs (m[i - 1] - m[i - 2]); 00688 00689 if (NE == 0.0) { 00690 b[i] = m[i]; 00691 c[i] = 0.0; 00692 d[i] = 0.0; 00693 } else { 00694 const double h_i = x_array[i + 1] - x_array[i]; 00695 const double NE_next = fabs (m[i + 2] - m[i + 1]) + 00696 fabs (m[i] - m[i - 1]); 00697 const double alpha_i = fabs (m[i - 1] - m[i - 2]) / NE; 00698 double alpha_ip1; 00699 double tL_ip1; 00700 if (NE_next == 0.0) { 00701 tL_ip1 = m[i]; 00702 } else { 00703 alpha_ip1 = fabs (m[i] - m[i - 1]) / NE_next; 00704 tL_ip1 = (1.0 - alpha_ip1) * m[i] + alpha_ip1 * m[i + 1]; 00705 } 00706 b[i] = (1.0 - alpha_i) * m[i - 1] + alpha_i * m[i]; 00707 c[i] = (3.0 * m[i] - 2.0 * b[i] - tL_ip1) / h_i; 00708 d[i] = (b[i] + tL_ip1 - 2.0 * m[i]) / (h_i * h_i); 00709 } 00710 } 00711 } 00712 00713 #endif 00714 00715 public: 00716 00717 /** \brief Create a base interpolation object with or without 00718 periodic boundary conditions 00719 */ 00720 akima_interp(bool periodic=false) { 00721 peri=periodic; 00722 this->min_size=5; 00723 } 00724 00725 virtual ~akima_interp() {} 00726 00727 /// Allocate memory, assuming x and y have size \c size 00728 virtual int allocate(size_t size) { 00729 00730 b = (double *) malloc (size * sizeof (double)); 00731 if (b==0) { 00732 set_err2_ret("Failed to allocate space for c", 00733 " in akima_interp::allocate().",gsl_enomem); 00734 } 00735 00736 c = (double *) malloc (size * sizeof (double)); 00737 if (c==0) { 00738 std::free(b); 00739 set_err2_ret("Failed to allocate space for g", 00740 " in akima_interp::allocate().",gsl_enomem); 00741 } 00742 00743 d = (double *) malloc (size * sizeof (double)); 00744 if (d==0) { 00745 std::free(b); 00746 std::free(c); 00747 set_err2_ret("Failed to allocate space for diag", 00748 " in akima_interp::allocate().",gsl_enomem); 00749 } 00750 00751 um = (double *) malloc ((size+4) * sizeof (double)); 00752 if (um==0) { 00753 std::free(b); 00754 std::free(c); 00755 std::free(d); 00756 set_err2_ret("Failed to allocate space for offdiag", 00757 " in akima_interp::allocate().",gsl_enomem); 00758 } 00759 00760 return 0; 00761 } 00762 00763 /// Initialize interpolation routine 00764 virtual int init(const vec_t &xa, const vec_t &ya, size_t size) { 00765 00766 if (peri) { 00767 00768 /// Periodic boundary conditions 00769 00770 double *m=um+2; 00771 size_t i; 00772 for (i = 0; i <= size - 2; i++) 00773 { 00774 m[i] = (ya[i + 1] - ya[i]) / (xa[i + 1] - xa[i]); 00775 } 00776 00777 /* periodic boundary conditions */ 00778 m[-2] = m[size - 1 - 2]; 00779 m[-1] = m[size - 1 - 1]; 00780 m[size - 1] = m[0]; 00781 m[size] = m[1]; 00782 00783 akima_calc (xa,size, m); 00784 00785 } else { 00786 00787 /// Non-periodic boundary conditions 00788 00789 double *m=um+2; 00790 size_t i; 00791 for (i = 0; i <= size - 2; i++) { 00792 m[i] = (ya[i + 1] - ya[i]) / (xa[i + 1] - xa[i]); 00793 } 00794 00795 /* non-periodic boundary conditions */ 00796 m[-2] = 3.0 * m[0] - 2.0 * m[1]; 00797 m[-1] = 2.0 * m[0] - m[1]; 00798 m[size - 1] = 2.0 * m[size - 2] - m[size - 3]; 00799 m[size] = 3.0 * m[size - 2] - 2.0 * m[size - 3]; 00800 00801 akima_calc (xa, size, m); 00802 00803 } 00804 return 0; 00805 } 00806 00807 00808 /// Free allocated memory 00809 virtual int free() { 00810 std::free(b); 00811 std::free(c); 00812 std::free(d); 00813 std::free(um); 00814 00815 return 0; 00816 } 00817 00818 /// Give the value of the function \f$ y(x=x_0) \f$ . 00819 virtual int interp(const vec_t &x_array, const vec_t &y_array, 00820 size_t size, double x, double &y) { 00821 00822 size_t index; 00823 00824 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00825 00826 const double x_lo = x_array[index]; 00827 const double delx = x - x_lo; 00828 const double bb = b[index]; 00829 const double cc = c[index]; 00830 const double dd = d[index]; 00831 y = y_array[index] + delx * (bb + delx * (cc + dd * delx)); 00832 return gsl_success; 00833 } 00834 00835 00836 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 00837 virtual int deriv(const vec_t &x_array, const vec_t &y_array, 00838 size_t size, double x, double &dydx) { 00839 00840 size_t index; 00841 00842 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00843 double x_lo = x_array[index]; 00844 double delx = x - x_lo; 00845 double bb = b[index]; 00846 double cc = c[index]; 00847 double dd = d[index]; 00848 dydx = bb + delx * (2.0 * cc + 3.0 * dd * delx); 00849 return gsl_success; 00850 00851 } 00852 00853 /** \brief Give the value of the second derivative 00854 \f$ y^{\prime \prime}(x=x_0) \f$ . 00855 */ 00856 virtual int deriv2(const vec_t &x_array, const vec_t &y_array, 00857 size_t size, double x, double &d2ydx2) { 00858 00859 size_t index; 00860 00861 index=this->sv.bsearch_inc(x,x_array,0,size-1); 00862 00863 const double x_lo = x_array[index]; 00864 const double delx = x - x_lo; 00865 const double cc = c[index]; 00866 const double dd = d[index]; 00867 d2ydx2 = 2.0 * cc + 6.0 * dd * delx; 00868 return gsl_success; 00869 00870 } 00871 00872 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 00873 virtual int integ(const vec_t &x_array, const vec_t &y_array, 00874 size_t size, double aa, double bb, 00875 double &result) { 00876 00877 size_t i, index_a, index_b; 00878 00879 index_a=this->sv.bsearch_inc(aa,x_array,0,size-1); 00880 index_b=this->sv.bsearch_inc(bb,x_array,0,size-1); 00881 00882 result = 0.0; 00883 00884 /* interior intervals */ 00885 00886 for(i=index_a; i<=index_b; i++) { 00887 00888 const double x_hi = x_array[i + 1]; 00889 const double x_lo = x_array[i]; 00890 const double y_lo = y_array[i]; 00891 const double dx = x_hi - x_lo; 00892 00893 if(dx != 0.0) { 00894 00895 if (i==index_a || i==index_b) { 00896 double x1 = (i==index_a) ? aa : x_lo; 00897 double x2 = (i==index_b) ? bb : x_hi; 00898 result += this->integ_eval(y_lo,b[i],c[i],d[i],x_lo,x1,x2); 00899 } else { 00900 result+=dx*(y_lo+dx*(0.5*b[i]+dx*(c[i]/3.0+0.25*d[i]*dx))); 00901 } 00902 00903 } else { 00904 result = 0.0; 00905 return GSL_FAILURE; 00906 } 00907 } 00908 00909 return gsl_success; 00910 } 00911 00912 }; 00913 00914 /** 00915 \brief Akima spline interpolation with periodic 00916 boundary conditions (GSL) 00917 00918 This is convenient to allow interpolation objects to 00919 be supplied as template parameters 00920 */ 00921 template<class vec_t> class akima_peri_interp : 00922 public akima_interp<vec_t> { 00923 00924 public: 00925 akima_peri_interp() : akima_interp<vec_t>(true) { 00926 } 00927 00928 }; 00929 00930 /** 00931 \brief Interpolation class 00932 00933 This interpolation class is intended to be sufficiently general 00934 to handle any vector type. Interpolation of \ref ovector like 00935 objects is performed with the default template parameters, and 00936 \ref array_interp is provided for simple interpolation on 00937 C-style arrays. 00938 00939 The type of interpolation to be performed can be specified using 00940 the set_type() function or in the constructor. The default is 00941 cubic splines with natural boundary conditions. 00942 00943 The class automatically handles decreasing arrays by converting 00944 from an object of type \c vec_t to an object ov type \c rvec_t. 00945 00946 While \c vec_t may be any vector type which allows indexing via 00947 \c [], \c rvec_t must be a vector type which allows indexing 00948 and has a constructor with one of the two forms 00949 \verbatim 00950 rvec_t::rvec_t(vec_t &v); 00951 rvec_t::rvec_t(vec_t v); 00952 \endverbatim 00953 so that o2scl_interp can automatically "reverse" a vector if 00954 necessary. 00955 00956 It is important that different instances of o2scl_interp_vec 00957 and o2scl_interp not be given the same interpolation objects, 00958 as they will clash. 00959 00960 */ 00961 template<class vec_t=ovector_view, class rvec_t=ovector_const_reverse> 00962 class o2scl_interp { 00963 00964 #ifndef DOXYGEN_INTERNAL 00965 00966 protected: 00967 00968 /// Pointer to base interpolation object 00969 base_interp<vec_t> *itp; 00970 00971 /// Pointer to base interpolation object for reversed vectors 00972 base_interp<rvec_t> *ritp; 00973 00974 #endif 00975 00976 public: 00977 00978 /// Create with base interpolation objects \c it and \c rit 00979 o2scl_interp(base_interp<vec_t> &it, base_interp<rvec_t> &rit) { 00980 itp=⁢ 00981 ritp=&rit; 00982 } 00983 00984 /** \brief Create with base interpolation object \c it and use 00985 \ref def_ritp for reverse interpolation if necessary 00986 */ 00987 o2scl_interp(base_interp<vec_t> &it) { 00988 itp=⁢ 00989 ritp=&def_ritp; 00990 } 00991 00992 /** \brief Create an interpolator using \ref def_itp and 00993 \ref def_ritp 00994 */ 00995 o2scl_interp() { 00996 itp=&def_itp; 00997 ritp=&def_ritp; 00998 } 00999 01000 virtual ~o2scl_interp() {} 01001 01002 /// Give the value of the function \f$ y(x=x_0) \f$ . 01003 virtual double interp(const double x0, size_t n, const vec_t &x, 01004 const vec_t &y) 01005 { 01006 01007 double ret=0.0; 01008 01009 err_hnd->reset(); 01010 01011 if (n<itp->min_size) { 01012 set_err("Vector size too small in interp().",gsl_edom); 01013 } 01014 01015 if (x[0]>x[n-1]) { 01016 01017 // Decreasing case 01018 01019 rvec_t ax(x); 01020 rvec_t ay(y); 01021 01022 int r1=ritp->allocate(n); 01023 if (r1==0) { 01024 int r2=ritp->init(ax,ay,n); 01025 if (r2==0) { 01026 ritp->interp(ax,ay,n,x0,ret); 01027 ritp->free(); 01028 } 01029 } 01030 01031 } else { 01032 01033 // Increasing case 01034 01035 int r1=itp->allocate(n); 01036 if (r1==0) { 01037 int r2=itp->init(x,y,n); 01038 if (r2==0) { 01039 itp->interp(x,y,n,x0,ret); 01040 itp->free(); 01041 } 01042 } 01043 01044 } 01045 01046 return ret; 01047 } 01048 01049 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 01050 virtual double deriv(const double x0, size_t n, const vec_t &x, 01051 const vec_t &y) 01052 { 01053 01054 double ret=0.0; 01055 01056 err_hnd->reset(); 01057 01058 if (n<itp->min_size) { 01059 set_err("Vector size too small in interp().",gsl_edom); 01060 } 01061 01062 if (x[0]>x[n-1]) { 01063 01064 rvec_t ax(x); 01065 rvec_t ay(y); 01066 01067 int r1=ritp->allocate(n); 01068 if (r1==0) { 01069 int r2=ritp->init(ax,ay,n); 01070 if (r2==0) { 01071 ritp->deriv(ax,ay,n,x0,ret); 01072 ritp->free(); 01073 } 01074 } 01075 01076 } else { 01077 01078 // Increasing case 01079 01080 int r1=itp->allocate(n); 01081 if (r1==0) { 01082 int r2=itp->init(x,y,n); 01083 if (r2==0) { 01084 itp->deriv(x,y,n,x0,ret); 01085 itp->free(); 01086 } 01087 } 01088 } 01089 01090 return ret; 01091 } 01092 01093 /** \brief Give the value of the second derivative 01094 \f$ y^{\prime \prime}(x=x_0) \f$ . 01095 */ 01096 virtual double deriv2(const double x0, size_t n, const vec_t &x, 01097 const vec_t &y) 01098 { 01099 01100 double ret=0.0; 01101 01102 err_hnd->reset(); 01103 01104 if (n<itp->min_size) { 01105 01106 set_err("Vector size too small in deriv2().",gsl_edom); 01107 01108 } 01109 01110 if (x[0]>x[n-1]) { 01111 01112 // Decreasing case 01113 01114 rvec_t ax(x); 01115 rvec_t ay(y); 01116 01117 int r1=ritp->allocate(n); 01118 if (r1==0) { 01119 int r2=ritp->init(ax,ay,n); 01120 if (r2==0) { 01121 ritp->deriv2(ax,ay,n,x0,ret); 01122 ritp->free(); 01123 } 01124 } 01125 01126 } else { 01127 01128 // Increasing case 01129 01130 int r1=itp->allocate(n); 01131 if (r1==0) { 01132 int r2=itp->init(x,y,n); 01133 if (r2==0) { 01134 itp->deriv2(x,y,n,x0,ret); 01135 itp->free(); 01136 } 01137 } 01138 01139 } 01140 01141 return ret; 01142 } 01143 01144 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 01145 virtual double integ(const double x1, const double x2, size_t n, 01146 const vec_t &x, const vec_t &y) 01147 { 01148 01149 double ret=0.0; 01150 01151 err_hnd->reset(); 01152 01153 if (n<itp->min_size) { 01154 01155 set_err("Vector size too small in integ().",gsl_edom); 01156 01157 } 01158 01159 if (x[0]>x[n-1]) { 01160 01161 // Decreasing case 01162 01163 rvec_t ax(x); 01164 rvec_t ay(y); 01165 01166 int r1=ritp->allocate(n); 01167 if (r1==0) { 01168 int r2=ritp->init(ax,ay,n); 01169 if (r2==0) { 01170 ritp->integ(ax,ay,n,x1,x2,ret); 01171 ritp->free(); 01172 } 01173 } 01174 01175 } else { 01176 01177 // Increasing case 01178 01179 int r1=itp->allocate(n); 01180 if (r1==0) { 01181 int r2=itp->init(x,y,n); 01182 if (r2==0) { 01183 itp->integ(x,y,n,x1,x2,ret); 01184 itp->free(); 01185 } 01186 } 01187 01188 } 01189 01190 return ret; 01191 } 01192 01193 /// Set base interpolation object 01194 int set_type(base_interp<vec_t> &it, base_interp<rvec_t> &rit) { 01195 itp=⁢ 01196 ritp=&rit; 01197 return 0; 01198 } 01199 01200 /** \brief Default base interpolation object (cubic spline with natural 01201 boundary conditions) 01202 */ 01203 cspline_interp<vec_t> def_itp; 01204 01205 /** \brief Default base interpolation object for reversed vectors 01206 (cubic spline with natural boundary conditions) 01207 */ 01208 cspline_interp<rvec_t> def_ritp; 01209 01210 }; 01211 01212 /** 01213 \brief Interpolation class for pre-specified vector 01214 01215 This interpolation class is intended to be sufficiently general 01216 to handle any vector type. Interpolation of \ref ovector like 01217 objects is performed with the default template parameters, and 01218 \ref array_interp_vec is provided for simple interpolation on 01219 C-style arrays. 01220 01221 The class automatically handles decreasing arrays by copying the 01222 old array to a reversed version. For several interpolations on 01223 the same data, copying the initial array can be faster than 01224 overloading operator[]. 01225 01226 The type of interpolation to be performed can be specified 01227 using the set_type() function. The default is cubic splines 01228 with natural boundary conditions. 01229 01230 It is important that different instances of o2scl_interp_vec 01231 and o2scl_interp not be given the same interpolation objects, 01232 as they will clash. 01233 01234 \todo Need to fix constructor to behave properly if init() fails. 01235 It should free the memory and set \c ln to zero. 01236 */ 01237 template<class vec_t=ovector_view, class alloc_vec_t=ovector, 01238 class alloc_t=ovector_alloc> class o2scl_interp_vec { 01239 01240 #ifndef DOXYGEN_INTERNAL 01241 01242 protected: 01243 01244 /// Memory allocator for objects of type \c alloc_vec_t 01245 alloc_t ao; 01246 01247 /// Pointer to base interpolation object 01248 base_interp<vec_t> *itp; 01249 01250 /// True if the original array was increasing 01251 bool inc; 01252 01253 /// Pointer to the user-specified x vector 01254 const vec_t *lx; 01255 01256 /// Pointer to the user-specified x vector 01257 const vec_t *ly; 01258 01259 /// Reversed version of x 01260 alloc_vec_t lrx; 01261 01262 /// Reversed version of y 01263 alloc_vec_t lry; 01264 01265 /// Size of user-specified vectors 01266 size_t ln; 01267 01268 #endif 01269 01270 public: 01271 01272 /// Create with base interpolation object \c it 01273 o2scl_interp_vec(base_interp<vec_t> &it, 01274 size_t n, const vec_t &x, const vec_t &y) { 01275 itp=⁢ 01276 ln=0; 01277 01278 if (n<itp->min_size) { 01279 set_err("Vector size too small in o2scl_interp_vec().",gsl_edom); 01280 } else { 01281 size_t iend=n-1; 01282 01283 if (x[0]>x[iend]) { 01284 01285 ao.allocate(lrx,n); 01286 ao.allocate(lry,n); 01287 01288 for(size_t i=0;i<n;i++) { 01289 lrx[iend-i]=x[i]; 01290 lry[iend-i]=y[i]; 01291 } 01292 01293 int r1=itp->allocate(n); 01294 if (r1==0) { 01295 itp->init(lrx,lry,n); 01296 ln=n; 01297 inc=false; 01298 } 01299 01300 } else { 01301 01302 int r1=itp->allocate(n); 01303 if (r1==0) { 01304 itp->init(x,y,n); 01305 ln=n; 01306 lx=&x; 01307 ly=&y; 01308 inc=true; 01309 } 01310 01311 } 01312 01313 } 01314 01315 } 01316 01317 virtual ~o2scl_interp_vec() { 01318 if (ln>0) { 01319 itp->free(); 01320 } 01321 if (inc==false) { 01322 ao.free(lrx); 01323 ao.free(lry); 01324 } 01325 } 01326 01327 /// Give the value of the function \f$ y(x=x_0) \f$ . 01328 virtual double interp(const double x0) { 01329 double ret=0.0; 01330 if (ln>0) { 01331 if (inc) itp->interp(*lx,*ly,ln,x0,ret); 01332 else itp->interp(lrx,lry,ln,x0,ret); 01333 } 01334 return ret; 01335 } 01336 01337 /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ . 01338 virtual double deriv(const double x0) { 01339 double ret=0.0; 01340 if (ln>0) { 01341 if (inc) itp->deriv(*lx,*ly,ln,x0,ret); 01342 else itp->deriv(lrx,lry,ln,x0,ret); 01343 } 01344 return ret; 01345 } 01346 01347 /** \brief Give the value of the second derivative 01348 \f$ y^{\prime \prime}(x=x_0) \f$ . 01349 */ 01350 virtual double deriv2(const double x0) { 01351 double ret=0.0; 01352 if (ln>0) { 01353 if (inc) itp->deriv2(*lx,*ly,ln,x0,ret); 01354 else itp->deriv2(lrx,lry,ln,x0,ret); 01355 } 01356 return ret; 01357 } 01358 01359 /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ . 01360 virtual double integ(const double x1, const double x2) { 01361 double ret=0.0; 01362 if (ln>0) { 01363 if (inc) itp->integ(*lx,*ly,ln,x1,x2,ret); 01364 else itp->integ(lrx,lry,ln,x1,x2,ret); 01365 } 01366 return ret; 01367 } 01368 01369 /// Set base interpolation object 01370 int set_type(base_interp<vec_t> &it) { 01371 itp=⁢ 01372 return 0; 01373 } 01374 01375 /** \brief Default base interpolation object (cubic spline with natural 01376 boundary conditions) 01377 */ 01378 cspline_interp<vec_t> def_itp; 01379 01380 }; 01381 01382 /** \brief A specialization of o2scl_interp for C-style double arrays 01383 */ 01384 template<size_t n> class array_interp : 01385 public o2scl_interp<double[n],array_const_reverse<n> > { 01386 01387 public: 01388 01389 /// Create with base interpolation objects \c it and \c rit 01390 array_interp(base_interp<double[n]> &it, 01391 base_interp<array_const_reverse<n> > &rit) 01392 : o2scl_interp<double[n],array_const_reverse<n> >(it,rit) { 01393 } 01394 01395 /** \brief Create with base interpolation object \c it and use 01396 the default base interpolation object for reversed arrays 01397 */ 01398 array_interp(base_interp<double[n]> &it) : 01399 o2scl_interp<double[n],array_const_reverse<n> >(it) { 01400 } 01401 01402 /** \brief Create an interpolator using the default base 01403 interpolation objects 01404 */ 01405 array_interp() : 01406 o2scl_interp<double[n],array_const_reverse<n> >() { 01407 } 01408 01409 }; 01410 01411 /** 01412 \brief A specialization of o2scl_interp_vec for C-style arrays 01413 */ 01414 template<class arr_t> class array_interp_vec : 01415 public o2scl_interp_vec<arr_t,arr_t,array_alloc<arr_t> > { 01416 01417 public: 01418 01419 /// Create with base interpolation object \c it 01420 array_interp_vec(base_interp<arr_t> &it, 01421 size_t nv, const arr_t &x, const arr_t &y) : 01422 o2scl_interp_vec<arr_t,arr_t,array_alloc<arr_t> >(it,nv,x,y) { 01423 } 01424 01425 }; 01426 01427 #ifndef DOXYGENP 01428 } 01429 #endif 01430 01431 #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