121 lines
3.4 KiB
Python
121 lines
3.4 KiB
Python
"""
|
|
Convergence regions of the expansions used in ``struve.c``
|
|
|
|
Note that for v >> z both functions tend rapidly to 0,
|
|
and for v << -z, they tend to infinity.
|
|
|
|
The floating-point functions over/underflow in the lower left and right
|
|
corners of the figure.
|
|
|
|
|
|
Figure legend
|
|
=============
|
|
|
|
Red region
|
|
Power series is close (1e-12) to the mpmath result
|
|
|
|
Blue region
|
|
Asymptotic series is close to the mpmath result
|
|
|
|
Green region
|
|
Bessel series is close to the mpmath result
|
|
|
|
Dotted colored lines
|
|
Boundaries of the regions
|
|
|
|
Solid colored lines
|
|
Boundaries estimated by the routine itself. These will be used
|
|
for determining which of the results to use.
|
|
|
|
Black dashed line
|
|
The line z = 0.7*|v| + 12
|
|
|
|
"""
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt # type: ignore[import]
|
|
|
|
import mpmath # type: ignore[import]
|
|
|
|
|
|
def err_metric(a, b, atol=1e-290):
|
|
m = abs(a - b) / (atol + abs(b))
|
|
m[np.isinf(b) & (a == b)] = 0
|
|
return m
|
|
|
|
|
|
def do_plot(is_h=True):
|
|
from scipy.special._ufuncs import (_struve_power_series,
|
|
_struve_asymp_large_z,
|
|
_struve_bessel_series)
|
|
|
|
vs = np.linspace(-1000, 1000, 91)
|
|
zs = np.sort(np.r_[1e-5, 1.0, np.linspace(0, 700, 91)[1:]])
|
|
|
|
rp = _struve_power_series(vs[:,None], zs[None,:], is_h)
|
|
ra = _struve_asymp_large_z(vs[:,None], zs[None,:], is_h)
|
|
rb = _struve_bessel_series(vs[:,None], zs[None,:], is_h)
|
|
|
|
mpmath.mp.dps = 50
|
|
if is_h:
|
|
sh = lambda v, z: float(mpmath.struveh(mpmath.mpf(v), mpmath.mpf(z)))
|
|
else:
|
|
sh = lambda v, z: float(mpmath.struvel(mpmath.mpf(v), mpmath.mpf(z)))
|
|
ex = np.vectorize(sh, otypes='d')(vs[:,None], zs[None,:])
|
|
|
|
err_a = err_metric(ra[0], ex) + 1e-300
|
|
err_p = err_metric(rp[0], ex) + 1e-300
|
|
err_b = err_metric(rb[0], ex) + 1e-300
|
|
|
|
err_est_a = abs(ra[1]/ra[0])
|
|
err_est_p = abs(rp[1]/rp[0])
|
|
err_est_b = abs(rb[1]/rb[0])
|
|
|
|
z_cutoff = 0.7*abs(vs) + 12
|
|
|
|
levels = [-1000, -12]
|
|
|
|
plt.cla()
|
|
|
|
plt.hold(1)
|
|
plt.contourf(vs, zs, np.log10(err_p).T, levels=levels, colors=['r', 'r'], alpha=0.1)
|
|
plt.contourf(vs, zs, np.log10(err_a).T, levels=levels, colors=['b', 'b'], alpha=0.1)
|
|
plt.contourf(vs, zs, np.log10(err_b).T, levels=levels, colors=['g', 'g'], alpha=0.1)
|
|
|
|
plt.contour(vs, zs, np.log10(err_p).T, levels=levels, colors=['r', 'r'], linestyles=[':', ':'])
|
|
plt.contour(vs, zs, np.log10(err_a).T, levels=levels, colors=['b', 'b'], linestyles=[':', ':'])
|
|
plt.contour(vs, zs, np.log10(err_b).T, levels=levels, colors=['g', 'g'], linestyles=[':', ':'])
|
|
|
|
lp = plt.contour(vs, zs, np.log10(err_est_p).T, levels=levels, colors=['r', 'r'], linestyles=['-', '-'])
|
|
la = plt.contour(vs, zs, np.log10(err_est_a).T, levels=levels, colors=['b', 'b'], linestyles=['-', '-'])
|
|
lb = plt.contour(vs, zs, np.log10(err_est_b).T, levels=levels, colors=['g', 'g'], linestyles=['-', '-'])
|
|
|
|
plt.clabel(lp, fmt={-1000: 'P', -12: 'P'})
|
|
plt.clabel(la, fmt={-1000: 'A', -12: 'A'})
|
|
plt.clabel(lb, fmt={-1000: 'B', -12: 'B'})
|
|
|
|
plt.plot(vs, z_cutoff, 'k--')
|
|
|
|
plt.xlim(vs.min(), vs.max())
|
|
plt.ylim(zs.min(), zs.max())
|
|
|
|
plt.xlabel('v')
|
|
plt.ylabel('z')
|
|
|
|
|
|
def main():
|
|
plt.clf()
|
|
plt.subplot(121)
|
|
do_plot(True)
|
|
plt.title('Struve H')
|
|
|
|
plt.subplot(122)
|
|
do_plot(False)
|
|
plt.title('Struve L')
|
|
|
|
plt.savefig('struve_convergence.png')
|
|
plt.show()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|