def spherical_besselj_root(ell, nmax, only=True, derivative=False): r"""Find positive zero(s) :math:`u_{\ell n}` of the spherical Bessel function :math:`j_{\ell}` of the first kind of order :math:`\ell`, or its derivative :math:`j'_{\ell}`, up to a maximum number :math:`n_\textrm{max}`. Solving for roots of the spherical Bessel function relies on the identity :math:`j_\ell(x) = \sqrt{\pi/(2x)} J_{\ell + 1/2}(x)`, where :math:`J_\ell(x)` is the Bessel funcion of the first kind. Solving for roots of the derivative function employs the interval bisection method, with the interval ansatz :math:`\ell + 1 \leqslant x \leqslant n_\textrm{max} \operatorname{max}\{4, \ell\}`. Parameters ---------- ell : int Order of the spherical Bessel function, ``ell >= 0``. nmax : int Maximum number of positive zeros to be found, ``nmax >= 1``. only : bool, optional If `True` (default), return the maximal root only. derivative : bool, optional If `True` (default is `False`), compute the zero(s) of the derivative function instead. Returns ------- u_ell : float, array_like Positive zero(s) for order `ell` (in ascending order). """ if not derivative: # `mpmath` returns a ``mpf`` float which needs conversion. if only: u_ell = float(besseljzero(ell + 0.5, nmax, derivative=0)) else: u_ell = np.asarray([ float(besseljzero(ell + 0.5, n, derivative=0)) for n in range(1, nmax + 1) ]) else: u_elln_list = binary_search( lambda x: spherical_besselj(ell, x, derivative=True), ell + 1, nmax * max(4, ell), maxnum=nmax ) u_ell = u_elln_list[-1] if only else u_elln_list return u_ell
def jn_zeros(n, k, method="sympy"): """ Zeros of the spherical Bessel function of the first kind. This returns an array of zeros of jn up to the k-th zero. method = "sympy": uses the SymPy's jn and findroot to find all roots method = "scipy": uses the SciPy's sph_jn and newton to find all roots, which if faster than method="sympy", but it requires SciPy and only works with low precision floating point numbers Examples: >>> from sympy.mpmath import nprint >>> from sympy import jn_zeros >>> nprint(jn_zeros(2, 4)) [5.76345919689, 9.09501133048, 12.3229409706, 15.5146030109] """ if method == "sympy": from sympy.mpmath import findroot f = lambda x: jn(n, x).n() elif method == "scipy": from scipy.special import sph_jn from scipy.optimize import newton f = lambda x: sph_jn(n, x)[0][-1] elif method == 'mpmath': # this needs a recent version of mpmath, newer than in sympy from mpmath import besseljzero return [besseljzero(n + 0.5, k) for k in xrange(1, k + 1)] else: raise NotImplementedError("Unknown method.") def solver(f, x): if method == "sympy": # findroot(solver="newton") or findroot(solver="secant") can't find # the root within the given tolerance. So we use solver="muller", # which converges towards complex roots (even for real starting # points), and so we need to chop all complex parts (that are small # anyway). Also we need to set the tolerance, as it sometimes fail # without it. def f_real(x): return f(complex(x).real) root = findroot(f_real, x, solver="muller", tol=1e-9) root = complex(root).real elif method == "scipy": root = newton(f, x) else: raise NotImplementedError("Unknown method.") return root # we need to approximate the position of the first root: root = n+pi # determine the first root exactly: root = solver(f, root) roots = [root] for i in range(k-1): # estimate the position of the next root using the last root + pi: root = solver(f, root+pi) roots.append(root) return roots
def test_bessel_dirichlet(m, N_coordinates, N_basis_functions = None): N_basis_functions = N_basis_functions or N_coordinates S_guess = bessel_dirichlet_S_guess(m, max(N_basis_functions, N_coordinates)) bessel_j_zeros = numpy.double([mpmath.besseljzero(m, i) for i in xrange(1, N_basis_functions+1)]) bessel_j_values = numpy.sqrt(2.0) / numpy.abs(scipy.special.jn(m + 1, bessel_j_zeros)) return test_bessel(m, S_guess, N_coordinates, bessel_j_zeros, bessel_j_values, bessel_initial_guess)
def bessel_initial_guess(m, N, S_guess): bessel_j_zeros = [mpmath.besseljzero(m, i) for i in xrange(1, N+1)] abscissas = [zero / S_guess for zero in bessel_j_zeros] weights = [2.0 / (S_guess * mpmath.besselj(m + 1, bessel_j_zeros[i]))**2 for i in xrange(N)] weights = numpy.reshape(numpy.double(weights), (N,)) abscissas = numpy.reshape(numpy.double(abscissas), (N,)) return weights, abscissas
def besselJZeros(m, a, b): require_mpmath() if not hasattr(mpmath, 'besseljzero'): besseljn = lambda x: mpmath.besselj(m, x) results = [mpmath.findroot(besseljn, mpmath.pi*(kp - 1./4 + 0.5*m)) for kp in range(a, b+1)] else: results = [mpmath.besseljzero(m, i) for i in xrange(a, b+1)] # Check that we haven't found double roots or missed a root. All roots should be separated by approximately pi assert all([0.6*mpmath.pi < (b - a) < 1.4*mpmath.pi for a, b in zip(results[:-1], results[1:])]), "Separation of Bessel zeros was incorrect." return results
def test_bessel_neumann(m, N_coordinates, N_basis_functions = None): N_basis_functions = N_basis_functions or N_coordinates S_guess = bessel_neumann_S_guess(m, max(N_basis_functions, N_coordinates)) bessel_j_zeros = numpy.double([mpmath.besseljzero(m, i, derivative=True) for i in xrange(1, N_basis_functions+1)]) bessel_j_values = numpy.sqrt(2.0) / numpy.abs(scipy.special.jn(m, bessel_j_zeros)) if m > 0: bessel_j_values /= numpy.sqrt(1.0 - (m / bessel_j_zeros)**2) return test_bessel(m, S_guess, N_coordinates, bessel_j_zeros, bessel_j_values, bessel_initial_guess)
def bessel_neumann_kspace_initial_guess(m, N, S_guess): bessel_j_zeros = [mpmath.besseljzero(m, i, derivative=True) for i in xrange(1, N+1)] abscissas = [zero / S_guess for zero in bessel_j_zeros] additional_factor = (lambda i: 1.0) if m == 0 else (lambda i: 1.0 - (m/bessel_j_zeros[i])**2) weights = [2.0 / (S_guess * additional_factor(i) * mpmath.besselj(m, bessel_j_zeros[i]))**2 for i in xrange(N)] weights = numpy.reshape(numpy.double(weights), (N,)) abscissas = numpy.reshape(numpy.double(abscissas), (N,)) return weights, abscissas
def jn_zeros(n, k, method="diofant", dps=15): """ Zeros of the spherical Bessel function of the first kind. This returns an array of zeros of jn up to the k-th zero. * method = "diofant": uses mpmath's function ``besseljzero`` * method = "scipy": uses the `SciPy's sph_jn <http://docs.scipy.org/doc/scipy/reference/generated/scipy.special.jn_zeros.html>`_ and `newton <http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html>`_ to find all roots, which is faster than computing the zeros using a general numerical solver, but it requires SciPy and only works with low precision floating point numbers. [The function used with method="diofant" is a recent addition to mpmath, before that a general solver was used.] Examples ======== >>> from diofant import jn_zeros >>> jn_zeros(2, 4, dps=5) [5.7635, 9.095, 12.323, 15.515] See Also ======== jn, yn, besselj, besselk, bessely """ from math import pi if method == "diofant": prec = dps_to_prec(dps) return [ Expr._from_mpmath( besseljzero(sympify(n + 0.5)._to_mpmath(prec), int(l)), prec) for l in range(1, k + 1) ] elif method == "scipy": from scipy.optimize import newton try: from scipy.special import spherical_jn except ImportError: # pragma: no cover from scipy.special import sph_jn def spherical_jn(n, x): return sph_jn(n, x)[0][-1] def f(x): return spherical_jn(n, x) else: raise NotImplementedError("Unknown method.") def solver(f, x): if method == "scipy": root = newton(f, x) else: raise NotImplementedError("Unknown method.") return root # we need to approximate the position of the first root: root = n + pi # determine the first root exactly: root = solver(f, root) roots = [root] for i in range(k - 1): # estimate the position of the next root using the last root + pi: root = solver(f, root + pi) roots.append(root) return roots
def jn_zeros(n, k, method="sympy", dps=15): """ Zeros of the spherical Bessel function of the first kind. This returns an array of zeros of jn up to the k-th zero. * method = "sympy": uses :func:`mpmath.besseljzero` * method = "scipy": uses the `SciPy's sph_jn <http://docs.scipy.org/doc/scipy/reference/generated/scipy.special.jn_zeros.html>`_ and `newton <http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html>`_ to find all roots, which is faster than computing the zeros using a general numerical solver, but it requires SciPy and only works with low precision floating point numbers. [The function used with method="sympy" is a recent addition to mpmath, before that a general solver was used.] Examples ======== >>> from sympy import jn_zeros >>> jn_zeros(2, 4, dps=5) [5.7635, 9.095, 12.323, 15.515] See Also ======== jn, yn, besselj, besselk, bessely """ from math import pi if method == "sympy": from mpmath import besseljzero from mpmath.libmp.libmpf import dps_to_prec from sympy import Expr prec = dps_to_prec(dps) return [Expr._from_mpmath(besseljzero(S(n + 0.5)._to_mpmath(prec), int(l)), prec) for l in range(1, k + 1)] elif method == "scipy": from scipy.special import sph_jn from scipy.optimize import newton f = lambda x: sph_jn(n, x)[0][-1] else: raise NotImplementedError("Unknown method.") def solver(f, x): if method == "scipy": root = newton(f, x) else: raise NotImplementedError("Unknown method.") return root # we need to approximate the position of the first root: root = n + pi # determine the first root exactly: root = solver(f, root) roots = [root] for i in range(k - 1): # estimate the position of the next root using the last root + pi: root = solver(f, root + pi) roots.append(root) return roots
def besselJPrimeZeros(m, a, b): require_mpmath() results = [mpmath.besseljzero(m, i, derivative=1) for i in xrange(a, b+1)] return results
def jn_zeros(n, k, method="diofant", dps=15): """ Zeros of the spherical Bessel function of the first kind. This returns an array of zeros of jn up to the k-th zero. * method = "diofant": uses mpmath's function ``besseljzero`` * method = "scipy": uses :func:`scipy.special.jn_zeros`. and :func:`scipy.optimize.newton` to find all roots, which is faster than computing the zeros using a general numerical solver, but it requires SciPy and only works with low precision floating point numbers. [The function used with method="diofant" is a recent addition to mpmath, before that a general solver was used.] Examples ======== >>> jn_zeros(2, 4, dps=5) [5.7635, 9.095, 12.323, 15.515] See Also ======== jn, yn, besselj, besselk, bessely """ from math import pi if method == "diofant": prec = dps_to_prec(dps) return [Expr._from_mpmath(besseljzero(sympify(n + 0.5)._to_mpmath(prec), int(l)), prec) for l in range(1, k + 1)] elif method == "scipy": from scipy.optimize import newton try: from scipy.special import spherical_jn except ImportError: # pragma: no cover from scipy.special import sph_jn def spherical_jn(n, x): return sph_jn(n, x)[0][-1] def f(x): return spherical_jn(n, x) else: raise NotImplementedError("Unknown method.") def solver(f, x): if method == "scipy": root = newton(f, x) else: raise NotImplementedError("Unknown method.") return root # we need to approximate the position of the first root: root = n + pi # determine the first root exactly: root = solver(f, root) roots = [root] for i in range(k - 1): # estimate the position of the next root using the last root + pi: root = solver(f, root + pi) roots.append(root) return roots
from sympy.abc import x,n from sympy.utilities.lambdify import lambdify from scipy.special import j1,j0,jn_zeros import numpy as np import matplotlib.pyplot as plt from sympy import * import math from mpltools.style import use as uuu from mpmath import besseljzero uuu('ggplot') a=10.0 nu=1.0 nmax=100 Omega = 1.0 l = [float(besseljzero(1,n+1)) for n in range(nmax + 1)] def u(r,t): summ=0 for n in xrange(nmax): summ+=j1(r/a * l[n]) / (l[n] * j0(l[n])) * math.exp((- l[n]**2 * t * nu / a**2)) return - 2 * Omega * summ fig = plt.figure(num=None, figsize=(7.5,5), dpi=300) ax = fig.add_subplot(1,1,1) box = ax.get_position() ax.set_position([box.x0, box.y0, box.width, box.height]) ax.axis([0,a*100,0,Omega])
def jn_zeros(n, k, method="sympy"): """ Zeros of the spherical Bessel function of the first kind. This returns an array of zeros of jn up to the k-th zero. method = "sympy": uses the SymPy's jn and findroot to find all roots method = "scipy": uses the SciPy's sph_jn and newton to find all roots, which if faster than method="sympy", but it requires SciPy and only works with low precision floating point numbers Examples: >>> from sympy.mpmath import nprint >>> from sympy import jn_zeros >>> nprint(jn_zeros(2, 4)) [5.76345919689, 9.09501133048, 12.3229409706, 15.5146030109] """ if method == "sympy": from sympy.mpmath import findroot f = lambda x: jn(n, x).n() elif method == "scipy": from scipy.special import sph_jn from scipy.optimize import newton f = lambda x: sph_jn(n, x)[0][-1] elif method == 'mpmath': # this needs a recent version of mpmath, newer than in sympy from mpmath import besseljzero return [besseljzero(n + 0.5, k) for k in xrange(1, k + 1)] else: raise NotImplementedError("Unknown method.") def solver(f, x): if method == "sympy": # findroot(solver="newton") or findroot(solver="secant") can't find # the root within the given tolerance. So we use solver="muller", # which converges towards complex roots (even for real starting # points), and so we need to chop all complex parts (that are small # anyway). Also we need to set the tolerance, as it sometimes fail # without it. def f_real(x): return f(complex(x).real) root = findroot(f_real, x, solver="muller", tol=1e-9) root = complex(root).real elif method == "scipy": root = newton(f, x) else: raise NotImplementedError("Unknown method.") return root # we need to approximate the position of the first root: root = n + pi # determine the first root exactly: root = solver(f, root) roots = [root] for i in range(k - 1): # estimate the position of the next root using the last root + pi: root = solver(f, root + pi) roots.append(root) return roots
def bessel_dirichlet_S_guess(m, N): return numpy.double(mpmath.besseljzero(m, N+1))
def bessel_neumann_S_guess(m, N): return numpy.double(mpmath.besseljzero(m, N+1, derivative=True))