struve_convergence.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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. from __future__ import absolute_import, division, print_function
  24. import numpy as np
  25. import matplotlib.pyplot as plt
  26. import mpmath
  27. def err_metric(a, b, atol=1e-290):
  28. m = abs(a - b) / (atol + abs(b))
  29. m[np.isinf(b) & (a == b)] = 0
  30. return m
  31. def do_plot(is_h=True):
  32. from scipy.special._ufuncs import (_struve_power_series,
  33. _struve_asymp_large_z,
  34. _struve_bessel_series)
  35. vs = np.linspace(-1000, 1000, 91)
  36. zs = np.sort(np.r_[1e-5, 1.0, np.linspace(0, 700, 91)[1:]])
  37. rp = _struve_power_series(vs[:,None], zs[None,:], is_h)
  38. ra = _struve_asymp_large_z(vs[:,None], zs[None,:], is_h)
  39. rb = _struve_bessel_series(vs[:,None], zs[None,:], is_h)
  40. mpmath.mp.dps = 50
  41. if is_h:
  42. sh = lambda v, z: float(mpmath.struveh(mpmath.mpf(v), mpmath.mpf(z)))
  43. else:
  44. sh = lambda v, z: float(mpmath.struvel(mpmath.mpf(v), mpmath.mpf(z)))
  45. ex = np.vectorize(sh, otypes='d')(vs[:,None], zs[None,:])
  46. err_a = err_metric(ra[0], ex) + 1e-300
  47. err_p = err_metric(rp[0], ex) + 1e-300
  48. err_b = err_metric(rb[0], ex) + 1e-300
  49. err_est_a = abs(ra[1]/ra[0])
  50. err_est_p = abs(rp[1]/rp[0])
  51. err_est_b = abs(rb[1]/rb[0])
  52. z_cutoff = 0.7*abs(vs) + 12
  53. levels = [-1000, -12]
  54. plt.cla()
  55. plt.hold(1)
  56. plt.contourf(vs, zs, np.log10(err_p).T, levels=levels, colors=['r', 'r'], alpha=0.1)
  57. plt.contourf(vs, zs, np.log10(err_a).T, levels=levels, colors=['b', 'b'], alpha=0.1)
  58. plt.contourf(vs, zs, np.log10(err_b).T, levels=levels, colors=['g', 'g'], alpha=0.1)
  59. plt.contour(vs, zs, np.log10(err_p).T, levels=levels, colors=['r', 'r'], linestyles=[':', ':'])
  60. plt.contour(vs, zs, np.log10(err_a).T, levels=levels, colors=['b', 'b'], linestyles=[':', ':'])
  61. plt.contour(vs, zs, np.log10(err_b).T, levels=levels, colors=['g', 'g'], linestyles=[':', ':'])
  62. lp = plt.contour(vs, zs, np.log10(err_est_p).T, levels=levels, colors=['r', 'r'], linestyles=['-', '-'])
  63. la = plt.contour(vs, zs, np.log10(err_est_a).T, levels=levels, colors=['b', 'b'], linestyles=['-', '-'])
  64. lb = plt.contour(vs, zs, np.log10(err_est_b).T, levels=levels, colors=['g', 'g'], linestyles=['-', '-'])
  65. plt.clabel(lp, fmt={-1000: 'P', -12: 'P'})
  66. plt.clabel(la, fmt={-1000: 'A', -12: 'A'})
  67. plt.clabel(lb, fmt={-1000: 'B', -12: 'B'})
  68. plt.plot(vs, z_cutoff, 'k--')
  69. plt.xlim(vs.min(), vs.max())
  70. plt.ylim(zs.min(), zs.max())
  71. plt.xlabel('v')
  72. plt.ylabel('z')
  73. def main():
  74. plt.clf()
  75. plt.subplot(121)
  76. do_plot(True)
  77. plt.title('Struve H')
  78. plt.subplot(122)
  79. do_plot(False)
  80. plt.title('Struve L')
  81. plt.savefig('struve_convergence.png')
  82. plt.show()
  83. if __name__ == "__main__":
  84. main()