def setup(self, s, k, orders, bias=0, lowringing=False, direction='forward'): self.logger.debug('Initialization...') self.logsc, self.dlogs, self.ns = self.__class__.calc_log_binning(s) logkc = self.__class__.calc_log_binning(k, self.ns)[0] kcsc = 10**(self.logsc + logkc) self.logger.info( 'Proposed central k * central s = {:.4g}; ns = {:d}, s-range = {:.4g} - {:.4g}.' .format(kcsc, self.ns, self.s[0], self.s[-1])) self.preset, self.kcsc = [], [] for order in orders: nkcsc, save, ok = fftlog.fhti(self.ns, order, self.dlogs * scipy.log(10.), bias, kcsc, int(lowringing)) self.kcsc += [nkcsc] txt = 'For order {:.3g}, new central k * central s = {:.4g}, ok = {}.'.format( order, nkcsc, ok == True) self.logger.debug(txt) if not ok: raise ValueError(txt) self.kcsc = scipy.mean(self.kcsc) self.logger.info( 'New central k * central s = {:.4g}; nk = {:d}, k-range = {:.4g} - {:.4g}.' .format(self.kcsc, self.nk, self.k[0], self.k[-1])) if not lowringing: assert self.kcsc == kcsc, 'New k/s is not the same as given in input.' for order in orders: save, ok = fftlog.fhti(self.ns, order, self.dlogs * scipy.log(10.), bias, self.kcsc, 0)[1:] self.preset += [save] if not ok: raise ValueError( 'FFTlog fails to compute order {:.3g}.'.format(order))
def test_fftlog(): # This is the example provided in `fftlogtest.f` of the original code. It # is the same test that is shown as an example in the gallery. # See the example for more details. # Parameters. logrmin = -4 logrmax = 4 n = 64 mu = 0 q = 0 kr = 1 kropt = 1 tdir = 1 logrc = (logrmin + logrmax)/2 nc = (n + 1)/2.0 dlogr = (logrmax - logrmin)/n dlnr = dlogr*np.log(10.0) # Calculate input function: $r^{\mu+1}\exp\left(-\frac{r^2}{2}\right)$. r = 10**(logrc + (np.arange(1, n+1) - nc)*dlogr) ar = r**(mu + 1)*np.exp(-r**2/2.0) # Initialize FFTLog transform - note fhti resets `kr` kr, xsave, _ = fhti(n, mu, dlnr, q, kr, kropt) assert_allclose(kr, 0.953538967579) logkc = np.log10(kr) - logrc # rk = 10**(logrc - logkc) # Transform # ak = fftl(ar.copy(), xsave, rk, tdir) ak = fht(ar.copy(), xsave, tdir) # Calculate Output function: $k^{\mu+1}\exp\left(-\frac{k^2}{2}\right)$ k = 10**(logkc + (np.arange(1, n+1) - nc)*dlogr) theo = k**(mu + 1)*np.exp(-k**2/2.0) # Check values assert_allclose(theo[theo > 1e-3], ak[theo > 1e-3], rtol=1e-8, atol=5e-5)
def __init__(self, logrmin=-4, logrmax=4, n=64, mu=0, q=0, kr=1, kropt=1): self.logrmin = logrmin # Range of periodic interval self.logrmax = logrmax self.n = n # Number of points (Max 4096) self.mu = mu # Order mu of Bessel function self.q = q # Bias exponent: q = 0 is unbiased self.kr = kr # Sensible approximate choice of k_c r_c self.kropt = kropt # Tell fhti to change kr to low-ringing value # WARNING: kropt = 3 will fail, as interaction is not supported # Forward transform (changed from dir to tdir, as dir is a python fct) self.tdir = 1 # Log-spacing of points dlogr = (logrmax - logrmin) / n dlnr = dlogr * np.log(10.0) kr, wsave, ok = fftlog.fhti(self.n, self.mu, dlnr, self.q, self.kr, self.kropt) print('fftlog.fhti: ok =', bool(ok), '; New kr = ', kr)
def iHankel(k, f, N=4096, order=0.5): """ This routine returns the inverse Hankel transform of order :math:`\\nu` of a log-spaced function. The computation is .. math :: f(r) = \int_0^\infty \ dk \ k \ f(k) \ J_\\nu(kr) .. warning:: Because of log-spacing, an extra `k` factor has been already added in the code. .. warning:: If ``order = 0.5`` it is similar to the 3D Fourier transform of a spherically symmetric function. Parameters ---------- `k`: array Abscissae of function, log-spaced. `f`: array ordinates of function. `N`: int, default = 4096 Number of output points `order`: float, default = 0.5 Order of the transform (Bessel polynomial). Returns ---------- rr: array Frequencies. Fr: array Transformed array. """ # FFT specifics mu = order # Order mu of Bessel function q = 0 # Bias exponent: q = 0 is unbiased kr = 1 # Sensible approximate choice of k_c r_c kropt = 1 # Tell fhti to change kr to low-ringing value tdir = -1 # Backward transform logkc = (np.max(np.log10(k)) + np.min(np.log10(k))) / 2. dlogk = (np.max(np.log10(k)) - np.min(np.log10(k))) / N dlnk = dlogk * np.log(10.) if N % 2 == 0: nc = N / 2 else: nc = (N + 1) / 2 k_t = 10.**(logkc + (np.arange(1, N + 1) - nc) * dlogk) # Initialise function (add k_t extra factor because of log-spacing) funct = si.interp1d(k, f, kind="cubic", bounds_error=False, fill_value=0.) ak = funct(k_t) * k_t # Initialization of transform #kr, xsave, ok = fftlog.fhti(N, mu, dlnk, q, kr, kropt) fft_obj = fftlog.fhti(N, mu, dlnk, q, kr, kropt) kr, xsave = fft_obj[0], fft_obj[1] logrc = np.log10(kr) - logkc # Transform (dividing by a rr extra factor because of log-spacing) Fr = fftlog.fht(ak.copy(), xsave, tdir) if N % 2 == 0: rr = 10.**(logrc + (np.arange(N) - nc) * dlogk) else: rr = 10.**(logrc + (np.arange(1, N + 1) - nc) * dlogk) Fr /= rr return rr, Fr
def iHankel_3D_order(k, f, N=4096, order=0): """ This routine is similar to the Fourier transform in 3D but it can return any order of the Bessel function .. math :: f_\ell(r) = \int_0^\infty \\frac{dk \ k^2}{2\pi^2} \ f(k) \ j_\ell(kr) Parameters ---------- `k`: array Abscissae of function, log-spaced. `f`: array ordinates of function. `N`: int, default = 4096 Number of output points `order`: float, default = 0 Order of the Bessel spherical polynomial. Returns ---------- rr: array Frequencies. Fr: array Transformed array. """ # FFT specifics mu = order + 0.5 # Order mu of Bessel function q = 0 # Bias exponent: q = 0 is unbiased kr = 1 # Sensible approximate choice of k_c r_c kropt = 1 # Tell fhti to change kr to low-ringing value tdir = -1 # Backward transform logkc = (np.max(np.log10(k)) + np.min(np.log10(k))) / 2. dlogk = (np.max(np.log10(k)) - np.min(np.log10(k))) / N dlnk = dlogk * np.log(10.) if N % 2 == 0: nc = N / 2 else: nc = (N + 1) / 2 k_t = 10.**(logkc + (np.arange(1, N + 1) - nc) * dlogk) # Initialise function funct = si.interp1d(k, f, kind="cubic", bounds_error=False, fill_value=0.) ak = funct(k_t) * k_t**1.5 # Initialization of transform #kr, xsave, ok = fftlog.fhti(N, mu, dlnk, q, kr, kropt) fft_obj = fftlog.fhti(N, mu, dlnk, q, kr, kropt) kr, xsave = fft_obj[0], fft_obj[1] logrc = np.log10(kr) - logkc # Transform ar = fftlog.fht(ak.copy(), xsave, tdir) if N % 2 == 0: rr = 10.**(logrc + (np.arange(N) - nc) * dlogk) else: rr = 10.**(logrc + (np.arange(1, N + 1) - nc) * dlogk) Fr = ar / (2. * np.pi * rr)**1.5 return rr, Fr
def Hankel_3D_order(r, f, N=4096, order=0): """ This routine is analogous to the Fourier transform in 3D but it can return any order of the Bessel function .. math :: f_\ell(k) = \int_0^\infty dr \ 4\pi r^2 \ f(r) \ j_\ell(kr) Parameters ---------- `r`: array Abscissae of function, log-spaced. `f`: array ordinates of function. `N`: int, default = 4096 Number of output points `order`: float, default = 0 Order of the Bessel spherical polynomial. Returns ---------- kk: array Frequencies. Fk: array Transformed array. """ mu = order + 0.5 # Order mu of Bessel function q = 0 # Bias exponent: q = 0 is unbiased kr = 1 # Sensible approximate choice of k_c r_c kropt = 1 # Tell fhti to change kr to low-ringing value tdir = 1 # Forward transform logrc = (np.max(np.log10(r)) + np.min(np.log10(r))) / 2. dlogr = (np.max(np.log10(r)) - np.min(np.log10(r))) / N dlnr = dlogr * np.log(10.) if N % 2 == 0: nc = N / 2 else: nc = (N + 1) / 2 r_t = 10.**(logrc + (np.arange(1, N + 1) - nc) * dlogr) # Initialise function funct = si.interp1d(r, f, kind="cubic", bounds_error=False, fill_value=0.) ar = funct(r_t) * (2. * np.pi * r_t)**1.5 # Initialization of transform #kr, xsave, ok = fftlog.fhti(N, mu, dlnr, q, kr, kropt) fft_obj = fftlog.fhti(N, mu, dlnr, q, kr, kropt) kr, xsave = fft_obj[0], fft_obj[1] logkc = np.log10(kr) - logrc # Transform ak = fftlog.fht(ar.copy(), xsave, tdir) if N % 2 == 0: kk = 10.**(logkc + (np.arange(N) - nc) * dlogr) else: kk = 10.**(logkc + (np.arange(1, N + 1) - nc) * dlogr) Fk = ak / kk**1.5 return kk, Fk
def iFFT_iso_3D(k, f, N=4096): """ This routine returns the inverse FFT of a radially symmetric function. It employs the ``FFTlog`` module, which in turn makes use of the Hankel transform: therefore the function that will be actually transformed is :math:`f(k) \ k^{1.5}/(2\pi r)^{1.5}`. N.B. Since the integral is performed in log-space, the exponent of `r` is 1.5 instead of 0.5. The computation is .. math :: f(r) = \int_0^\infty \\frac{dk \ k^2}{2\pi^2} \ f(k) \ j_0(kr) Parameters ---------- `k`: array Abscissae of function, log-spaced. `f`: array ordinates of function. `N`: int, default = 4096 Number of output points Returns ---------- rr: array Frequencies. Fr: array Transformed array. """ # FFT specifics mu = 0.5 # Order mu of Bessel function q = 0 # Bias exponent: q = 0 is unbiased kr = 1 # Sensible approximate choice of k_c r_c kropt = 1 # Tell fhti to change kr to low-ringing value tdir = -1 # Backward transform logkc = (np.max(np.log10(k)) + np.min(np.log10(k))) / 2. dlogk = (np.max(np.log10(k)) - np.min(np.log10(k))) / N dlnk = dlogk * np.log(10.) if N % 2 == 0: nc = N / 2 else: nc = (N + 1) / 2 k_t = 10.**(logkc + (np.arange(1, N + 1) - nc) * dlogk) # Initialise function funct = si.interp1d(k, f, kind="cubic", bounds_error=False, fill_value=0.) ak = funct(k_t) * k_t**1.5 # Initialization of transform #kr, xsave, ok = fftlog.fhti(N, mu, dlnk, q, kr, kropt) fft_obj = fftlog.fhti(N, mu, dlnk, q, kr, kropt) kr, xsave = fft_obj[0], fft_obj[1] logrc = np.log10(kr) - logkc # Transform ar = fftlog.fht(ak.copy(), xsave, tdir) if N % 2 == 0: rr = 10.**(logrc + (np.arange(N) - nc) * dlogk) else: rr = 10.**(logrc + (np.arange(1, N + 1) - nc) * dlogk) Fr = ar / (2 * np.pi * rr)**1.5 return rr, Fr
def FFT_iso_3D(r, f, N=4096): """ This routine returns the FFT of a radially symmetric function. It employs the ``FFTlog`` module, which in turn makes use of the Hankel transform: therefore the function that will be actually transformed is :math:`f(r) \ (2\pi r)^{1.5}/k^{1.5}`. N.B. Since the integral is performed in log-space, the exponent of `r` is 1.5 instead of 0.5. The computation is .. math :: f(k) = \int_0^\infty dr \ 4\pi r^2 \ f(r) \ j_0(kr) Parameters ---------- `r`: array Abscissae of function, log-spaced. `f`: array ordinates of function. `N`: int, default = 4096 Number of output points Returns ---------- kk: array Frequencies. Fk: array Transformed array. """ mu = 0.5 # Order mu of Bessel function q = 0 # Bias exponent: q = 0 is unbiased kr = 1 # Sensible approximate choice of k_c r_c kropt = 1 # Tell fhti to change kr to low-ringing value tdir = 1 # Forward transform logrc = (np.max(np.log10(r)) + np.min(np.log10(r))) / 2. dlogr = (np.max(np.log10(r)) - np.min(np.log10(r))) / N dlnr = dlogr * np.log(10.) if N % 2 == 0: nc = N / 2 else: nc = (N + 1) / 2 r_t = 10.**(logrc + (np.arange(1, N + 1) - nc) * dlogr) # Initialise function funct = si.interp1d(r, f, kind="cubic", bounds_error=False, fill_value=0.) ar = funct(r_t) * (2. * np.pi * r_t)**1.5 # Initialization of transform #kr, xsave, ok = fftlog.fhti(N, mu, dlnr, q, kr, kropt) fft_obj = fftlog.fhti(N, mu, dlnr, q, kr, kropt) kr, xsave = fft_obj[0], fft_obj[1] logkc = np.log10(kr) - logrc # Transform ak = fftlog.fht(ar.copy(), xsave, tdir) if N % 2 == 0: kk = 10.**(logkc + (np.arange(N) - nc) * dlogr) else: kk = 10.**(logkc + (np.arange(1, N + 1) - nc) * dlogr) Fk = ak / kk**1.5 return kk, Fk
# Log-spacing of points dlogr = (logrmax - logrmin) / n dlnr = dlogr * np.log(10.0) # ### Calculate input function: $r^{\mu+1}\exp\left(-\frac{r^2}{2}\right)$ # In[4]: r = 10**(logrc + (np.arange(1, n + 1) - nc) * dlogr) ar = r**(mu + 1) * np.exp(-r**2 / 2.0) # ### Initialize FFTLog transform - note fhti resets `kr` # In[5]: kr, wsave, ok = fftlog.fhti(n, mu, dlnr, q, kr, kropt) print('fftlog.fhti: ok =', bool(ok), '; New kr = ', kr) # ### Call `fftlog.fht` (or `fftlog.fhtl`) # In[6]: logkc = np.log10(kr) - logrc print('Central point in k-space at log10(k_c) = ', logkc) # rk = r_c/k_c rk = 10**(logrc - logkc) # Transform #ak = fftlog.fftl(ar.copy(), wsave, rk, tdir) ak = fftlog.fht(ar.copy(), wsave, tdir)