|
16 | 16 |
|
17 | 17 |
|
18 | 18 | def check_besselj_zeros(nu, z): |
| 19 | + """ |
| 20 | + Sanity-check a sequence of estimated zeros of the Bessel function with order `nu`. |
| 21 | + :param nu: The real number order of the Bessel function. |
| 22 | + :param z: (Array-like) A sequence of postulated zeros. |
| 23 | + :return result: True or False. |
| 24 | + """ |
| 25 | + # Compute first and second order differences of the sequence of zeros |
19 | 26 | dz = np.diff(z) |
20 | 27 | ddz = np.diff(dz) |
21 | 28 |
|
| 29 | + # Check criteria for acceptable zeros |
22 | 30 | result = True |
| 31 | + # Real roots |
23 | 32 | result = result and all(np.isreal(z)) |
| 33 | + # All roots should be > 0, check first of increasing sequence |
24 | 34 | result = result and z[0] > 0 |
| 35 | + # Spacing between zeros is greater than 3 |
25 | 36 | result = result and all(dz > 3) |
26 | 37 |
|
| 38 | + # Second order differences should be zero or just barely increasing to |
| 39 | + # within 16x machine precision. |
27 | 40 | if nu >= 0.5: |
28 | 41 | result = result and all(ddz < 16 * np.spacing(z[1:-1])) |
| 42 | + # For nu < 0.5 the spacing will be slightly decreasing, so flip the sign |
29 | 43 | else: |
30 | 44 | result = result and all(ddz > -16 * np.spacing(z[1:-1])) |
31 | 45 |
|
32 | 46 | return result |
33 | 47 |
|
34 | 48 |
|
35 | 49 | def besselj_newton(nu, z0, max_iter=10): |
| 50 | + """ |
| 51 | + Uses the Newton-Raphson method to compute the zero(s) of the |
| 52 | + Bessel function with order `nu` with initial guess(es) `z0`. |
| 53 | +
|
| 54 | + :param nu: The real number order of the Bessel function. |
| 55 | + :param z0: (Array-like) The initial guess(es) for the root-finding algorithm. |
| 56 | + :param max_iter: Maximum number of iterations for Newton-Raphson |
| 57 | + (default: 10). |
| 58 | + :return z: (Array-like) The estimated root(s). |
| 59 | + """ |
36 | 60 | z = z0 |
37 | 61 |
|
38 | 62 | # Factor worse than machine precision |
@@ -157,6 +181,14 @@ def real_sph_harmonic(j, m, theta, phi): |
157 | 181 |
|
158 | 182 |
|
159 | 183 | def besselj_zeros(nu, k): |
| 184 | + """ |
| 185 | + Finds the first `k` zeros of the Bessel function of order `nu`, i.e. J_nu. |
| 186 | + Adapted from "zerobess.m" by Jonas Lundgren <[email protected]> |
| 187 | +
|
| 188 | + :param nu: The real number order of the Bessel function (must be positive and <1e7). |
| 189 | + :param k: The number of zeros to return (must be >= 3). |
| 190 | + :return z: A 1D NumPy array of the first `k` zeros. |
| 191 | + """ |
160 | 192 | assert k >= 3, "k must be >= 3" |
161 | 193 | assert 0 <= nu <= 1e7, "nu must be between 0 and 1e7" |
162 | 194 |
|
@@ -216,12 +248,24 @@ def besselj_zeros(nu, k): |
216 | 248 |
|
217 | 249 |
|
218 | 250 | def num_besselj_zeros(ell, r): |
| 251 | + """ |
| 252 | + Compute the zeros of the order `ell` Bessel function which are less than `r`. |
| 253 | +
|
| 254 | + :param ell: The real number order of the Bessel function. |
| 255 | + :param r: The upper bound for zeros returned. |
| 256 | + :return n, r0: The number of zeros and the zeros themselves |
| 257 | + as a NumPy array. |
| 258 | + """ |
219 | 259 | k = 4 |
| 260 | + # get the first 4 zeros |
220 | 261 | r0 = besselj_zeros(ell, k) |
221 | 262 | while all(r0 < r): |
| 263 | + # increase the number of zeros sought |
| 264 | + # until one of the zeros is greater than `r` |
222 | 265 | k *= 2 |
223 | 266 | r0 = besselj_zeros(ell, k) |
224 | 267 | r0 = r0[r0 < r] |
| 268 | + # return the number of zeros and the zeros themselves |
225 | 269 | return len(r0), r0 |
226 | 270 |
|
227 | 271 |
|
|
0 commit comments