1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
// // A rust binding for the GSL library by Guillaume Gomez (guillaume1.gomez@gmail.com) // /*! #Discrete Hankel Transforms This chapter describes functions for performing Discrete Hankel Transforms (DHTs). ##Definitions The discrete Hankel transform acts on a vector of sampled data, where the samples are assumed to have been taken at points related to the zeroes of a Bessel function of fixed order; compare this to the case of the discrete Fourier transform, where samples are taken at points related to the zeroes of the sine or cosine function. Specifically, let f(t) be a function on the unit interval and j_(\nu,m) the m-th zero of the Bessel function J_\nu(x). Then the finite \nu-Hankel transform of f(t) is defined to be the set of numbers g_m given by, g_m = \int_0^1 t dt J_\nu(j_(\nu,m)t) f(t), so that, f(t) = \sum_{m=1}^\infty (2 J_\nu(j_(\nu,m)t) / J_(\nu+1)(j_(\nu,m))^2) g_m. Suppose that f is band-limited in the sense that g_m=0 for m > M. Then we have the following fundamental sampling theorem. g_m = (2 / j_(\nu,M)^2) \sum_{k=1}^{M-1} f(j_(\nu,k)/j_(\nu,M)) (J_\nu(j_(\nu,m) j_(\nu,k) / j_(\nu,M)) / J_(\nu+1)(j_(\nu,k))^2). It is this discrete expression which defines the discrete Hankel transform. The kernel in the summation above defines the matrix of the \nu-Hankel transform of size M-1. The coefficients of this matrix, being dependent on \nu and M, must be precomputed and stored; the gsl_dht object encapsulates this data. The allocation function gsl_dht_alloc returns a gsl_dht object which must be properly initialized with gsl_dht_init before it can be used to perform transforms on data sample vectors, for fixed \nu and M, using the gsl_dht_apply function. The implementation allows a scaling of the fundamental interval, for convenience, so that one can assume the function is defined on the interval [0,X], rather than the unit interval. Notice that by assumption f(t) vanishes at the endpoints of the interval, consistent with the inversion formula and the sampling formula given above. Therefore, this transform corresponds to an orthogonal expansion in eigenfunctions of the Dirichlet problem for the Bessel differential equation. ##References and Further Reading The algorithms used by these functions are described in the following papers, H. Fisk Johnson, Comp. Phys. Comm. 43, 181 (1987). D. Lemoine, J. Chem. Phys. 101, 3936 (1994). !*/ use ffi; use enums; pub struct DiscreteHankel { t: *mut ffi::gsl_dht } impl DiscreteHankel { /// This function allocates a Discrete Hankel transform object of size size. pub fn new(size: usize) -> Option<DiscreteHankel> { let tmp = unsafe { ffi::gsl_dht_alloc(size) }; if tmp.is_null() { None } else { Some(DiscreteHankel { t: tmp, }) } } /// This function allocates a Discrete Hankel transform object of size size and initializes it for the given values of nu and xmax. pub fn new_with_init(size: usize, nu: f64, xmax: f64) -> Option<DiscreteHankel> { let tmp = unsafe { ffi::gsl_dht_new(size, nu, xmax) }; if tmp.is_null() { None } else { Some(DiscreteHankel { t: tmp, }) } } /// This function initializes the transform self for the given values of nu and xmax. pub fn init(&self, nu: f64, xmax: f64) -> enums::Value { unsafe { ffi::gsl_dht_init(self.t, nu, xmax) } } /// This function applies the transform t to the array f_in whose size is equal to the size of the transform. The result is stored in the array /// f_out which must be of the same length. /// /// Applying this function to its output gives the original data multiplied by (1/j_(\nu,M))^2, up to numerical errors. pub fn apply(&self, f_in: &mut [f64], f_out: &mut [f64]) -> enums::Value { unsafe { ffi::gsl_dht_apply(self.t, f_in.as_mut_ptr(), f_out.as_mut_ptr()) } } /// This function returns the value of the n-th sample point in the unit interval, (j_{\nu,n+1}/j_{\nu,M}) X. These are the points where the /// function f(t) is assumed to be sampled. pub fn x_sample(&self, n: i32) -> f64 { unsafe { ffi::gsl_dht_x_sample(self.t, n) } } /// This function returns the value of the n-th sample point in “k-space”, j_{\nu,n+1}/X. pub fn k_sample(&self, n: i32) -> f64 { unsafe { ffi::gsl_dht_k_sample(self.t, n) } } } impl Drop for DiscreteHankel { fn drop(&mut self) { unsafe { ffi::gsl_dht_free(self.t) }; self.t = ::std::ptr::null_mut(); } } impl ffi::FFI<ffi::gsl_dht> for DiscreteHankel { fn wrap(t: *mut ffi::gsl_dht) -> DiscreteHankel { DiscreteHankel { t: t } } fn unwrap(t: &DiscreteHankel) -> *mut ffi::gsl_dht { t.t } }