First, a bit of background:
I am using spherical harmonics as an example of a function on the surface of a sphere like the front spheres in this image:
I produced one of these spheres, coloured according to the value of the harmonic function at points on its surface. I do this first for a very large number of points, so my function is very accurate. I've called this my fine
sphere.
Now that I have my fine
sphere, I take a relatively small number of points on the sphere. These are the points I wish to interpolate from, the training data, and I call them interp
points. Here are my interp
points, coloured to their values, plotted on my fine
sphere.
Now, the goal of the project is to use these interp
points to train a SciPy Radial Basis Function to interpolate my function on the sphere. I was able to do this using:
# Train the interpolation using interp coordinates
rbf = Rbf(interp.phi, interp.theta, harmonic13_coarse)
# The result of the interpolation on fine coordinates
interp_values = rbf(fine.phi, fine.theta)
Which produced this interpolation, plotted on the sphere:
Hopefully, through this last image, you can see my problem. Notice the line running through the interpolation? This is because the interpolation data has a boundary. The boundary is because I trained the radial basis function using spherical coordinates (boundaries at [0,pi] and [0,2pi]).
rbf = Rbf(interp.phi, interp.theta, harmonic13_coarse)
My goal, and why I'm posting this problem, is to interpolate my function on the surface of the sphere using the x,y,z Cartesian coordinates of the data on the sphere. This way, since spheres don't have boundaries, I won't have this boundary error like I do in spherical coordinates. However, I just can't figure out how to do this.
I've tried simply giving the Rbf function the x,y,z coordinates and the value of the function.
rbf=Rbf(interp.x, interp.y, interp.z, harmonic13_coarse)
interp_values=rbf(fine.x,fine.y,fine.z)
But NumPy throws me a Singular Matrix Error
numpy.linalg.linalg.LinAlgError: singular matrix
Is there any way for me to give Rbf my data sites in Cartesian coordinates, with the function values at each site and have it behave like it does with spherical coordinates but without that boundaries? From the Rbf documentation, there is the attribute norm
for defining a different distance norm, could I have to use a spherical distance to get this to work?
I'm pretty much stumped on this. Let me know if you have any ideas for interpolating my function on a sphere without the boundaries of spherical coordinates.
Here is my code in full:
import matplotlib.pyplot as plt
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from scipy import special
from scipy.interpolate import Rbf
from collections import namedtuple
from mayavi import mlab
# Nice aliases
pi = np.pi
cos = np.cos
sin = np.sin
# Creating a sphere in Cartesian and Sphereical
# Saves coordinates as named tuples
def coordinates(r, n):
phi, theta = np.mgrid[0:pi:n, 0:2 * pi:n]
Coor = namedtuple('Coor', 'r phi theta x y z')
r = r
x = r * sin(phi) * cos(theta)
y = r * sin(phi) * sin(theta)
z = r * cos(phi)
return Coor(r, phi, theta, x, y, z)
# Creating a sphere
# fine is coordinates on a fine grid
# interp is coordinates on coarse grid for training interpolation
fine = coordinates(1, 100j)
interp = coordinates(1, 5j)
# Defining finection to colour sphere
# Here we are using a spherical harmonic
def harmonic(m, n, theta, phi):
return special.sph_harm(m, n, theta, phi).real
norm = colors.Normalize()
# One example of the harmonic function, for testing
harmonic13_fine = harmonic(1, 3, fine.theta, fine.phi)
harmonic13_coarse = harmonic(1, 3, interp.theta, interp.phi)
# Train the interpolation using interp coordinates
rbf = Rbf(interp.phi, interp.theta, harmonic13_coarse)
# The result of the interpolation on fine coordinates
interp_values = rbf(fine.phi, fine.theta)
rbf=Rbf(interp.x, interp.y, interp.z, harmonic13_coarse)
interp_values=rbf(fine.x,fine.y,fine.z)
#Figure of harmoinc function on sphere in fine cordinates
#Points3d showing interpolation training points coloured to their value
mlab.figure()
vmax, vmin = np.max(harmonic13_fine), np.min(harmonic13_fine)
mlab.mesh(fine.x, fine.y, fine.z, scalars=harmonic13_fine, vmax=vmax, vmin=vmin)
mlab.points3d(interp.x, interp.y, interp.z, harmonic13_coarse,
scale_factor=0.1, scale_mode='none', vmax=vmax, vmin=vmin)
#Figure showing results of rbf interpolation
mlab.figure()
vmax, vmin = np.max(harmonic13_fine), np.min(harmonic13_fine)
mlab.mesh(fine.x, fine.y, fine.z, scalars=interp_values)
# mlab.points3d(interp.x, interp.y, interp.z, scalars, scale_factor=0.1, scale_mode='none',vmax=vmax, vmin=vmin)
mlab.show()
The boundary you see is because you are mapping a closed surface (S2) to an open one (R2). One way or another, you will have boundaries. The local properties of the manifolds are compatible, so it works for most of the sphere, but not the global, you get a line.
The way around it is to use an atlas instead of a single chart. An atlas is a collection of overlapping charts. In the overlapping region, you need to define weights, a smooth function that goes from 0 to 1 on each chart. (Sorry, probably differential geometry was not what you were expecting to hear).
If you don't want to go all the way here, you can notice that your original sphere has an equator where the variance is minimal. You can then rotate your fine sphere and make it coincide with the line. It doesn't solve your problem, but it can certainly mitigate it.
You can change the standard distance:
def euclidean_norm(x1, x2):
return np.sqrt( ((x1 - x2)**2).sum(axis=0) )
by the sphere distance (see, for instance, this question Haversine Formula in Python (Bearing and Distance between two GPS points)).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With