test_ellip_harm.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. #
  2. # Tests for the Ellipsoidal Harmonic Function,
  3. # Distributed under the same license as SciPy itself.
  4. #
  5. from __future__ import division, print_function, absolute_import
  6. import numpy as np
  7. from numpy.testing import (assert_equal, assert_almost_equal, assert_allclose,
  8. assert_)
  9. from scipy._lib._numpy_compat import suppress_warnings
  10. from scipy.special._testutils import assert_func_equal
  11. from scipy.special import ellip_harm, ellip_harm_2, ellip_normal
  12. from scipy.integrate import IntegrationWarning
  13. from numpy import sqrt, pi
  14. def test_ellip_potential():
  15. def change_coefficient(lambda1, mu, nu, h2, k2):
  16. x = sqrt(lambda1**2*mu**2*nu**2/(h2*k2))
  17. y = sqrt((lambda1**2 - h2)*(mu**2 - h2)*(h2 - nu**2)/(h2*(k2 - h2)))
  18. z = sqrt((lambda1**2 - k2)*(k2 - mu**2)*(k2 - nu**2)/(k2*(k2 - h2)))
  19. return x, y, z
  20. def solid_int_ellip(lambda1, mu, nu, n, p, h2, k2):
  21. return (ellip_harm(h2, k2, n, p, lambda1)*ellip_harm(h2, k2, n, p, mu)
  22. * ellip_harm(h2, k2, n, p, nu))
  23. def solid_int_ellip2(lambda1, mu, nu, n, p, h2, k2):
  24. return (ellip_harm_2(h2, k2, n, p, lambda1)
  25. * ellip_harm(h2, k2, n, p, mu)*ellip_harm(h2, k2, n, p, nu))
  26. def summation(lambda1, mu1, nu1, lambda2, mu2, nu2, h2, k2):
  27. tol = 1e-8
  28. sum1 = 0
  29. for n in range(20):
  30. xsum = 0
  31. for p in range(1, 2*n+2):
  32. xsum += (4*pi*(solid_int_ellip(lambda2, mu2, nu2, n, p, h2, k2)
  33. * solid_int_ellip2(lambda1, mu1, nu1, n, p, h2, k2)) /
  34. (ellip_normal(h2, k2, n, p)*(2*n + 1)))
  35. if abs(xsum) < 0.1*tol*abs(sum1):
  36. break
  37. sum1 += xsum
  38. return sum1, xsum
  39. def potential(lambda1, mu1, nu1, lambda2, mu2, nu2, h2, k2):
  40. x1, y1, z1 = change_coefficient(lambda1, mu1, nu1, h2, k2)
  41. x2, y2, z2 = change_coefficient(lambda2, mu2, nu2, h2, k2)
  42. res = sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2)
  43. return 1/res
  44. pts = [
  45. (120, sqrt(19), 2, 41, sqrt(17), 2, 15, 25),
  46. (120, sqrt(16), 3.2, 21, sqrt(11), 2.9, 11, 20),
  47. ]
  48. with suppress_warnings() as sup:
  49. sup.filter(IntegrationWarning, "The occurrence of roundoff error")
  50. sup.filter(IntegrationWarning, "The maximum number of subdivisions")
  51. for p in pts:
  52. err_msg = repr(p)
  53. exact = potential(*p)
  54. result, last_term = summation(*p)
  55. assert_allclose(exact, result, atol=0, rtol=1e-8, err_msg=err_msg)
  56. assert_(abs(result - exact) < 10*abs(last_term), err_msg)
  57. def test_ellip_norm():
  58. def G01(h2, k2):
  59. return 4*pi
  60. def G11(h2, k2):
  61. return 4*pi*h2*k2/3
  62. def G12(h2, k2):
  63. return 4*pi*h2*(k2 - h2)/3
  64. def G13(h2, k2):
  65. return 4*pi*k2*(k2 - h2)/3
  66. def G22(h2, k2):
  67. res = (2*(h2**4 + k2**4) - 4*h2*k2*(h2**2 + k2**2) + 6*h2**2*k2**2 +
  68. sqrt(h2**2 + k2**2 - h2*k2)*(-2*(h2**3 + k2**3) + 3*h2*k2*(h2 + k2)))
  69. return 16*pi/405*res
  70. def G21(h2, k2):
  71. res = (2*(h2**4 + k2**4) - 4*h2*k2*(h2**2 + k2**2) + 6*h2**2*k2**2
  72. + sqrt(h2**2 + k2**2 - h2*k2)*(2*(h2**3 + k2**3) - 3*h2*k2*(h2 + k2)))
  73. return 16*pi/405*res
  74. def G23(h2, k2):
  75. return 4*pi*h2**2*k2*(k2 - h2)/15
  76. def G24(h2, k2):
  77. return 4*pi*h2*k2**2*(k2 - h2)/15
  78. def G25(h2, k2):
  79. return 4*pi*h2*k2*(k2 - h2)**2/15
  80. def G32(h2, k2):
  81. res = (16*(h2**4 + k2**4) - 36*h2*k2*(h2**2 + k2**2) + 46*h2**2*k2**2
  82. + sqrt(4*(h2**2 + k2**2) - 7*h2*k2)*(-8*(h2**3 + k2**3) +
  83. 11*h2*k2*(h2 + k2)))
  84. return 16*pi/13125*k2*h2*res
  85. def G31(h2, k2):
  86. res = (16*(h2**4 + k2**4) - 36*h2*k2*(h2**2 + k2**2) + 46*h2**2*k2**2
  87. + sqrt(4*(h2**2 + k2**2) - 7*h2*k2)*(8*(h2**3 + k2**3) -
  88. 11*h2*k2*(h2 + k2)))
  89. return 16*pi/13125*h2*k2*res
  90. def G34(h2, k2):
  91. res = (6*h2**4 + 16*k2**4 - 12*h2**3*k2 - 28*h2*k2**3 + 34*h2**2*k2**2
  92. + sqrt(h2**2 + 4*k2**2 - h2*k2)*(-6*h2**3 - 8*k2**3 + 9*h2**2*k2 +
  93. 13*h2*k2**2))
  94. return 16*pi/13125*h2*(k2 - h2)*res
  95. def G33(h2, k2):
  96. res = (6*h2**4 + 16*k2**4 - 12*h2**3*k2 - 28*h2*k2**3 + 34*h2**2*k2**2
  97. + sqrt(h2**2 + 4*k2**2 - h2*k2)*(6*h2**3 + 8*k2**3 - 9*h2**2*k2 -
  98. 13*h2*k2**2))
  99. return 16*pi/13125*h2*(k2 - h2)*res
  100. def G36(h2, k2):
  101. res = (16*h2**4 + 6*k2**4 - 28*h2**3*k2 - 12*h2*k2**3 + 34*h2**2*k2**2
  102. + sqrt(4*h2**2 + k2**2 - h2*k2)*(-8*h2**3 - 6*k2**3 + 13*h2**2*k2 +
  103. 9*h2*k2**2))
  104. return 16*pi/13125*k2*(k2 - h2)*res
  105. def G35(h2, k2):
  106. res = (16*h2**4 + 6*k2**4 - 28*h2**3*k2 - 12*h2*k2**3 + 34*h2**2*k2**2
  107. + sqrt(4*h2**2 + k2**2 - h2*k2)*(8*h2**3 + 6*k2**3 - 13*h2**2*k2 -
  108. 9*h2*k2**2))
  109. return 16*pi/13125*k2*(k2 - h2)*res
  110. def G37(h2, k2):
  111. return 4*pi*h2**2*k2**2*(k2 - h2)**2/105
  112. known_funcs = {(0, 1): G01, (1, 1): G11, (1, 2): G12, (1, 3): G13,
  113. (2, 1): G21, (2, 2): G22, (2, 3): G23, (2, 4): G24,
  114. (2, 5): G25, (3, 1): G31, (3, 2): G32, (3, 3): G33,
  115. (3, 4): G34, (3, 5): G35, (3, 6): G36, (3, 7): G37}
  116. def _ellip_norm(n, p, h2, k2):
  117. func = known_funcs[n, p]
  118. return func(h2, k2)
  119. _ellip_norm = np.vectorize(_ellip_norm)
  120. def ellip_normal_known(h2, k2, n, p):
  121. return _ellip_norm(n, p, h2, k2)
  122. # generate both large and small h2 < k2 pairs
  123. np.random.seed(1234)
  124. h2 = np.random.pareto(0.5, size=1)
  125. k2 = h2 * (1 + np.random.pareto(0.5, size=h2.size))
  126. points = []
  127. for n in range(4):
  128. for p in range(1, 2*n+2):
  129. points.append((h2, k2, n*np.ones(h2.size), p*np.ones(h2.size)))
  130. points = np.array(points)
  131. with suppress_warnings() as sup:
  132. sup.filter(IntegrationWarning, "The occurrence of roundoff error")
  133. assert_func_equal(ellip_normal, ellip_normal_known, points, rtol=1e-12)
  134. def test_ellip_harm_2():
  135. def I1(h2, k2, s):
  136. res = (ellip_harm_2(h2, k2, 1, 1, s)/(3 * ellip_harm(h2, k2, 1, 1, s))
  137. + ellip_harm_2(h2, k2, 1, 2, s)/(3 * ellip_harm(h2, k2, 1, 2, s)) +
  138. ellip_harm_2(h2, k2, 1, 3, s)/(3 * ellip_harm(h2, k2, 1, 3, s)))
  139. return res
  140. with suppress_warnings() as sup:
  141. sup.filter(IntegrationWarning, "The occurrence of roundoff error")
  142. assert_almost_equal(I1(5, 8, 10), 1/(10*sqrt((100-5)*(100-8))))
  143. # Values produced by code from arXiv:1204.0267
  144. assert_almost_equal(ellip_harm_2(5, 8, 2, 1, 10), 0.00108056853382)
  145. assert_almost_equal(ellip_harm_2(5, 8, 2, 2, 10), 0.00105820513809)
  146. assert_almost_equal(ellip_harm_2(5, 8, 2, 3, 10), 0.00106058384743)
  147. assert_almost_equal(ellip_harm_2(5, 8, 2, 4, 10), 0.00106774492306)
  148. assert_almost_equal(ellip_harm_2(5, 8, 2, 5, 10), 0.00107976356454)
  149. def test_ellip_harm():
  150. def E01(h2, k2, s):
  151. return 1
  152. def E11(h2, k2, s):
  153. return s
  154. def E12(h2, k2, s):
  155. return sqrt(abs(s*s - h2))
  156. def E13(h2, k2, s):
  157. return sqrt(abs(s*s - k2))
  158. def E21(h2, k2, s):
  159. return s*s - 1/3*((h2 + k2) + sqrt(abs((h2 + k2)*(h2 + k2)-3*h2*k2)))
  160. def E22(h2, k2, s):
  161. return s*s - 1/3*((h2 + k2) - sqrt(abs((h2 + k2)*(h2 + k2)-3*h2*k2)))
  162. def E23(h2, k2, s):
  163. return s * sqrt(abs(s*s - h2))
  164. def E24(h2, k2, s):
  165. return s * sqrt(abs(s*s - k2))
  166. def E25(h2, k2, s):
  167. return sqrt(abs((s*s - h2)*(s*s - k2)))
  168. def E31(h2, k2, s):
  169. return s*s*s - (s/5)*(2*(h2 + k2) + sqrt(4*(h2 + k2)*(h2 + k2) -
  170. 15*h2*k2))
  171. def E32(h2, k2, s):
  172. return s*s*s - (s/5)*(2*(h2 + k2) - sqrt(4*(h2 + k2)*(h2 + k2) -
  173. 15*h2*k2))
  174. def E33(h2, k2, s):
  175. return sqrt(abs(s*s - h2))*(s*s - 1/5*((h2 + 2*k2) + sqrt(abs((h2 +
  176. 2*k2)*(h2 + 2*k2) - 5*h2*k2))))
  177. def E34(h2, k2, s):
  178. return sqrt(abs(s*s - h2))*(s*s - 1/5*((h2 + 2*k2) - sqrt(abs((h2 +
  179. 2*k2)*(h2 + 2*k2) - 5*h2*k2))))
  180. def E35(h2, k2, s):
  181. return sqrt(abs(s*s - k2))*(s*s - 1/5*((2*h2 + k2) + sqrt(abs((2*h2
  182. + k2)*(2*h2 + k2) - 5*h2*k2))))
  183. def E36(h2, k2, s):
  184. return sqrt(abs(s*s - k2))*(s*s - 1/5*((2*h2 + k2) - sqrt(abs((2*h2
  185. + k2)*(2*h2 + k2) - 5*h2*k2))))
  186. def E37(h2, k2, s):
  187. return s * sqrt(abs((s*s - h2)*(s*s - k2)))
  188. assert_equal(ellip_harm(5, 8, 1, 2, 2.5, 1, 1),
  189. ellip_harm(5, 8, 1, 2, 2.5))
  190. known_funcs = {(0, 1): E01, (1, 1): E11, (1, 2): E12, (1, 3): E13,
  191. (2, 1): E21, (2, 2): E22, (2, 3): E23, (2, 4): E24,
  192. (2, 5): E25, (3, 1): E31, (3, 2): E32, (3, 3): E33,
  193. (3, 4): E34, (3, 5): E35, (3, 6): E36, (3, 7): E37}
  194. point_ref = []
  195. def ellip_harm_known(h2, k2, n, p, s):
  196. for i in range(h2.size):
  197. func = known_funcs[(int(n[i]), int(p[i]))]
  198. point_ref.append(func(h2[i], k2[i], s[i]))
  199. return point_ref
  200. np.random.seed(1234)
  201. h2 = np.random.pareto(0.5, size=30)
  202. k2 = h2*(1 + np.random.pareto(0.5, size=h2.size))
  203. s = np.random.pareto(0.5, size=h2.size)
  204. points = []
  205. for i in range(h2.size):
  206. for n in range(4):
  207. for p in range(1, 2*n+2):
  208. points.append((h2[i], k2[i], n, p, s[i]))
  209. points = np.array(points)
  210. assert_func_equal(ellip_harm, ellip_harm_known, points, rtol=1e-12)
  211. def test_ellip_harm_invalid_p():
  212. # Regression test. This should return nan.
  213. n = 4
  214. # Make p > 2*n + 1.
  215. p = 2*n + 2
  216. result = ellip_harm(0.5, 2.0, n, p, 0.2)
  217. assert np.isnan(result)