struve_convergence.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. """
  2. Convergence regions of the expansions used in ``struve.c``
  3. Note that for v >> z both functions tend rapidly to 0,
  4. and for v << -z, they tend to infinity.
  5. The floating-point functions over/underflow in the lower left and right
  6. corners of the figure.
  7. Figure legend
  8. =============
  9. Red region
  10. Power series is close (1e-12) to the mpmath result
  11. Blue region
  12. Asymptotic series is close to the mpmath result
  13. Green region
  14. Bessel series is close to the mpmath result
  15. Dotted colored lines
  16. Boundaries of the regions
  17. Solid colored lines
  18. Boundaries estimated by the routine itself. These will be used
  19. for determining which of the results to use.
  20. Black dashed line
  21. The line z = 0.7*|v| + 12
  22. """
  23. import numpy as np
  24. import matplotlib.pyplot as plt
  25. import mpmath
  26. def err_metric(a, b, atol=1e-290):
  27. m = abs(a - b) / (atol + abs(b))
  28. m[np.isinf(b) & (a == b)] = 0
  29. return m
  30. def do_plot(is_h=True):
  31. from scipy.special._ufuncs import (_struve_power_series,
  32. _struve_asymp_large_z,
  33. _struve_bessel_series)
  34. vs = np.linspace(-1000, 1000, 91)
  35. zs = np.sort(np.r_[1e-5, 1.0, np.linspace(0, 700, 91)[1:]])
  36. rp = _struve_power_series(vs[:,None], zs[None,:], is_h)
  37. ra = _struve_asymp_large_z(vs[:,None], zs[None,:], is_h)
  38. rb = _struve_bessel_series(vs[:,None], zs[None,:], is_h)
  39. mpmath.mp.dps = 50
  40. if is_h:
  41. sh = lambda v, z: float(mpmath.struveh(mpmath.mpf(v), mpmath.mpf(z)))
  42. else:
  43. sh = lambda v, z: float(mpmath.struvel(mpmath.mpf(v), mpmath.mpf(z)))
  44. ex = np.vectorize(sh, otypes='d')(vs[:,None], zs[None,:])
  45. err_a = err_metric(ra[0], ex) + 1e-300
  46. err_p = err_metric(rp[0], ex) + 1e-300
  47. err_b = err_metric(rb[0], ex) + 1e-300
  48. err_est_a = abs(ra[1]/ra[0])
  49. err_est_p = abs(rp[1]/rp[0])
  50. err_est_b = abs(rb[1]/rb[0])
  51. z_cutoff = 0.7*abs(vs) + 12
  52. levels = [-1000, -12]
  53. plt.cla()
  54. plt.hold(1)
  55. plt.contourf(vs, zs, np.log10(err_p).T, levels=levels, colors=['r', 'r'], alpha=0.1)
  56. plt.contourf(vs, zs, np.log10(err_a).T, levels=levels, colors=['b', 'b'], alpha=0.1)
  57. plt.contourf(vs, zs, np.log10(err_b).T, levels=levels, colors=['g', 'g'], alpha=0.1)
  58. plt.contour(vs, zs, np.log10(err_p).T, levels=levels, colors=['r', 'r'], linestyles=[':', ':'])
  59. plt.contour(vs, zs, np.log10(err_a).T, levels=levels, colors=['b', 'b'], linestyles=[':', ':'])
  60. plt.contour(vs, zs, np.log10(err_b).T, levels=levels, colors=['g', 'g'], linestyles=[':', ':'])
  61. lp = plt.contour(vs, zs, np.log10(err_est_p).T, levels=levels, colors=['r', 'r'], linestyles=['-', '-'])
  62. la = plt.contour(vs, zs, np.log10(err_est_a).T, levels=levels, colors=['b', 'b'], linestyles=['-', '-'])
  63. lb = plt.contour(vs, zs, np.log10(err_est_b).T, levels=levels, colors=['g', 'g'], linestyles=['-', '-'])
  64. plt.clabel(lp, fmt={-1000: 'P', -12: 'P'})
  65. plt.clabel(la, fmt={-1000: 'A', -12: 'A'})
  66. plt.clabel(lb, fmt={-1000: 'B', -12: 'B'})
  67. plt.plot(vs, z_cutoff, 'k--')
  68. plt.xlim(vs.min(), vs.max())
  69. plt.ylim(zs.min(), zs.max())
  70. plt.xlabel('v')
  71. plt.ylabel('z')
  72. def main():
  73. plt.clf()
  74. plt.subplot(121)
  75. do_plot(True)
  76. plt.title('Struve H')
  77. plt.subplot(122)
  78. do_plot(False)
  79. plt.title('Struve L')
  80. plt.savefig('struve_convergence.png')
  81. plt.show()
  82. if __name__ == "__main__":
  83. main()