spherical_harmonics1.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. """Expand and reconstruct any surface
  2. (here a simple box) into spherical harmonics"""
  3. # Expand an arbitrary closed shape into spherical harmonics
  4. # using SHTOOLS (https://shtools.github.io/SHTOOLS)
  5. # and then truncate the expansion to a specific lmax and
  6. # reconstruct the projected points on a finer grid.
  7. import pyshtools
  8. import numpy as np
  9. from scipy.interpolate import griddata
  10. from vedo import spher2cart, mag, Box, Point, Points, Plotter
  11. ###########################################################################
  12. lmax = 8 # maximum degree of the spherical harm. expansion
  13. N = 50 # number of grid intervals on the unit sphere
  14. rmax = 500 # line length
  15. x0 = [250, 250, 250] # set SPH sphere at this position
  16. ###########################################################################
  17. x0 = np.array(x0)
  18. surface = Box(pos=x0+[10,20,30], size=(300,150,100))
  19. surface.color('grey').alpha(0.2)
  20. ############################################################
  21. # cast rays from the sphere center and find intersections
  22. agrid, pts = [], []
  23. for th in np.linspace(0, np.pi, N, endpoint=True):
  24. longs = []
  25. for ph in np.linspace(0, 2*np.pi, N, endpoint=False):
  26. p = spher2cart(rmax, th, ph)
  27. intersections = surface.intersect_with_line(x0, x0+p)
  28. if len(intersections):
  29. value = mag(intersections[0]-x0)
  30. longs.append(value)
  31. pts.append(intersections[0])
  32. else:
  33. print('No hit for theta, phi =', th, ph, c='r')
  34. longs.append(rmax)
  35. pts.append(p)
  36. agrid.append(longs)
  37. agrid = np.array(agrid)
  38. hits = Points(pts)
  39. hits.cmap('jet', agrid.ravel()).add_scalarbar3d('scalar distance to x_0')
  40. hits.scalarbar = hits.scalarbar.clone2d(size=0.12)
  41. #############################################################
  42. grid = pyshtools.SHGrid.from_array(agrid)
  43. clm = grid.expand()
  44. grid_reco = clm.expand(lmax=lmax).to_array() # cut "high frequency" components
  45. #############################################################
  46. # interpolate to a finer grid
  47. ll = []
  48. for i, long in enumerate(np.linspace(0, 360, num=grid_reco.shape[1], endpoint=False)):
  49. for j, lat in enumerate(np.linspace(90, -90, num=grid_reco.shape[0], endpoint=True)):
  50. th = np.deg2rad(90 - lat)
  51. ph = np.deg2rad(long)
  52. p = spher2cart(grid_reco[j][i], th, ph)
  53. ll.append((lat, long))
  54. radii = grid_reco.T.ravel()
  55. n = 200j
  56. lnmin, lnmax = np.array(ll).min(axis=0), np.array(ll).max(axis=0)
  57. grid = np.mgrid[lnmax[0]:lnmin[0]:n, lnmin[1]:lnmax[1]:n]
  58. grid_x, grid_y = grid
  59. grid_reco_finer = griddata(ll, radii, (grid_x, grid_y), method='cubic')
  60. pts2 = []
  61. for i, long in enumerate(np.linspace(0, 360, num=grid_reco_finer.shape[1], endpoint=False)):
  62. for j, lat in enumerate(np.linspace(90, -90, num=grid_reco_finer.shape[0], endpoint=True)):
  63. th = np.deg2rad(90 - lat)
  64. ph = np.deg2rad(long)
  65. p = spher2cart(grid_reco_finer[j][i], th, ph)
  66. pts2.append(p+x0)
  67. plt = Plotter(N=2, axes=1)
  68. plt.at(0).show(surface, hits, Point(x0), __doc__)
  69. plt.at(1).show(
  70. f'Spherical harmonics expansion of order {lmax}',
  71. Points(pts2).c("red5").alpha(0.5),
  72. surface,
  73. )
  74. plt.interactive().close()