Fitting unorganized data to a sphere using scipy.interpolate.SmoothSphereBivariateSpline - python

I have 974 data points located on the surface of the unit sphere. The points are not ordered in any particular way. I wish to read in these data points, along with their corresponding polar coordinates (phi, theta). I then wish to interpolate onto a regular latitude-longitude grid using scipy.interpolate.SmoothSphereBivariateSpline. So far I have the following code:
import numpy as np
from scipy.interpolate import SmoothSphereBivariateSpline
#Read in the unorganized grid points
# and also shift so that phi in [0,pi] and theta in [0,2*pi)
leb = np.genfromtxt('grid.txt')
u, v = np.hsplit(leb, 2)
phi, theta = u[:,0], v[:,0]
theta += np.pi
#Read in the unorganized data values
data1 = np.genfromtxt('0p0_97p03.txt')
#Create the interpolator object
lut = SmoothSphereBivariateSpline(phi, theta, data1, s=350)
#Generate a regular lat-long grid to interpolate onto
N = 100
lat = np.linspace(0.0, np.pi, N)
lon = np.linspace(0.0, 2.0*np.pi, N)
lat, lon = np.meshgrid(lat, lon)
#Now interpolate onto the regular grid
data_inerp = lut(lat, lon)
In the above code, the arrays phi, theta and data1 are the unorganized data points, with each of these three arrays having a shape (974,). When i run this code I get the error message:
Traceback (most recent call last):
File "spherebiv.py", line 25, in <module>
data_inerp = lut(lat, lon)
File "/usr/lib/python2.7/dist-packages/scipy/interpolate/fitpack2.py", line 958, in __call__
raise ValueError("Error code returned by bispev: %s" % ier)
ValueError: Error code returned by bispev: 10
Now I don't understand this error code - it seems to be something to do with the underlying Fortran routine. Is there anything obvious I'm doing wrong here?

Related

Calculating vorticity for multiple vertical levels in MetPy

I'm trying to calculate vorticity in MetPy for multiple (consecutive) vertical levels. When I try to calculate it for a single level, everything works fine.
Here's the code; I've used the example for cross sections from https://unidata.github.io/MetPy/latest/examples/cross_section.html#sphx-glr-examples-cross-section-py.
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
import metpy.calc as mpcalc
from metpy.cbook import get_test_data
from metpy.interpolate import cross_section
from metpy.units import units
data = xr.open_dataset(get_test_data('narr_example.nc', False))
data = data.metpy.parse_cf().squeeze()
data_crs = data['Temperature'].metpy.cartopy_crs
lat = data['lat']
lon = data['lon']
f = mpcalc.coriolis_parameter(lat)
dx, dy = mpcalc.lat_lon_grid_deltas(lon, lat, initstring=data_crs.proj4_init)
Then the calculation of vorticity is performed.
vort = mpcalc.vorticity(data['u_wind'], data['v_wind'], dx, dy)
The traceback:
Traceback (most recent call last):
File "E:\Временные файлы\cross_section (1).py", line 63, in <module>
vort = mpcalc.vorticity(data['u_wind'], data['v_wind'], dx, dy)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\xarray.py", line 436, in wrapper
return func(*args, **kwargs)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\calc\kinematics.py", line 60, in wrapper
ret = func(*args, **kwargs)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\calc\kinematics.py", line 121, in vorticity
dudy = first_derivative(u, delta=dy, axis=-2)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\calc\tools.py", line 920, in wrapper
return preprocess_xarray(func)(f, **kwargs)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\xarray.py", line 436, in wrapper
return func(*args, **kwargs)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\calc\tools.py", line 1014, in first_derivative
combined_delta = delta[tuple(delta_slice0)] + delta[tuple(delta_slice1)]
File "C:\ProgramData\Miniconda3\lib\site-packages\pint\quantity.py", line 1400, in __getitem__
value = self._magnitude[key]
IndexError: too many indices for array
I'm absolutely stuck. Searching "metpy multiple levels calculations" (no actual quotes) gives no relevant results.
The doc says:
metpy.calc.vorticity(u, v, dx, dy)[source]
Calculate the vertical vorticity of the horizontal wind.
Parameters:
u ((M, N) ndarray) – x component of the wind
v ((M, N) ndarray) – y component of the wind
dx (float or ndarray) – The grid spacing(s) in the x-direction. If an array, there should be one item less than the size of u along the applicable axis.
dy (float or ndarray) – The grid spacing(s) in the y-direction. If an array, there should be one item less than the size of u along the applicable axis.
dim_order (str or None, optional) – The ordering of dimensions in passed in arrays. Can be one of None, 'xy', or 'yx'. 'xy' indicates that the dimension corresponding to x is the leading dimension, followed by y. 'yx' indicates that x is the last dimension, preceded by y. None indicates that the default ordering should be assumed, which is ‘yx’. Can only be passed as a keyword argument, i.e. func(…, dim_order=’xy’).
Returns:
(M, N) ndarray – vertical vorticity
I conclude that the input can have more than 2 dimensions, but 3-dimensional input (as it is in my case) gives errors. What can be done to fix them?
I'm absolutely new to Python, so I could've made a stupid mistake.
Unfortunately, the error message that comes up isn't that helpful in this case if you don't know what to look for!
The problem with the vorticity function call in your example is that the dimensionality of your input variables do not match. data['u_wind'] and data['v_wind'] are 3D arrays with shape (29, 118, 292), but dx and dy, since they were computed from lat_lon_grid_deltas, are 2D arrays with shapes (118, 291) and (117, 292) respectively. And so, we need to obtain arrays that broadcast appropriately...there are many different ways that you could do this, but here are two options that I would recommend:
Option 1: Manual Broadcasting
Since the "extra" dimension that dx and dy are missing is the first dimension (in the vertical), we can just make dx and dy into properly aligned 3D arrays by inserting a size-one leading dimension:
dx, dy = mpcalc.lat_lon_grid_deltas(lon, lat, initstring=data_crs.proj4_init)
dx = dx[None, :]
dy = dy[None, :]
vort = mpcalc.vorticity(data['u_wind'], data['v_wind'], dx, dy)
Option 2: Use the grid_deltas_from_dataarray() helper function
MetPy also has a helper function to make pulling the grid deltas from an xarray DataArray easy. It also ensures that the broadcasting occurs properly, so you don't have to do it yourself. Using it in your example, it would be:
dx, dy = mpcalc.grid_deltas_from_dataarray(data['u_wind'])
vort = mpcalc.vorticity(data['u_wind'], data['v_wind'], dx, dy)

Error when trying to interpolate using SmoothSphereBivariateSpline(): "ValueError: Error code returned by bispev: 10"

I want to interpolate data, which is randomly scattered on the surface of a sphere, onto a regular longitude/latitude grid. I tried to do this with SmoothSphereBivariateSpline() from the scipy.interpolate package (see the code below).
import numpy as np
from scipy.interpolate import SmoothSphereBivariateSpline
#Define the input data and the original sampling points
NSamp = 2000
Theta = np.random.uniform(0,np.pi,NSamp)
Phi = np.random.uniform(0,2*np.pi, NSamp)
Data = np.ones(NSamp)
Interpolator = SmoothSphereBivariateSpline(Theta, Phi, Data, s=3.5)
#Prepare the grid to which the input shall be interpolated
NLon = 64
NLat = 32
GridPosLons = np.arange(NLon)/NLon * 2 * np.pi
GridPosLats = np.arange(NLat)/NLat * np.pi
LatsGrid, LonsGrid = np.meshgrid(GridPosLats, GridPosLons)
Lats = LatsGrid.ravel()
Lons = LonsGrid.ravel()
#Interpolate
Interpolator(Lats, Lons)
However, when I execute this code it gives me the following error:
ValueError: Error code returned by bispev: 10
Does anyone know what the problem is and how to fix it? Is this a bug or am I doing something wrong?
In the documentation of __call__ method of SmoothSphereBivariateSpline, note the grid flag (some other interpolators have it too). If True, it's understood that you are entering one-dimensional arrays from which a grid is to be formed. This is the default value. But you already made a meshgrid from your one-dimensional arrays, so this default behavior doesn't work for your input.
Solution: use either
Interpolator(Lats, Lons, grid=False)
or, which is simpler and better:
Interpolator(GridPosLats, GridPosLons)
The latter will return the data in grid form (2D array), which makes more sense than the flattened data you would get with the first version.

Length-1 Arrays and Python Scalars Via plt.text

I'm trying to use plt.text to plot temperature values at their associated lat/lon points on a plot.
After reviewing the plt.text documentation, it appears that the plotted value (third arg) has to be a number and that the number has to be a whole number, NOT a number with decimals.
Below is the code that I'm trying to work with and the associated traceback error that I'm receiving:
Script Code:
data = np.loadtxt('/.../.../.../tmax_day0', delimiter=',', skiprows=1)
grid_x, grid_y = np.mgrid[-85:64:dx, 34:49:dx]
temp = data[:,2]
#print temp
grid_z = griddata((data[:,1],data[:,0]), data[:,2], (grid_x,grid_y), method='linear')
x,y = m(data[:,1], data[:,0]) # flip lat/lon
grid_x,grid_y = m(grid_x,grid_y)
#m.plot(x,y, 'ko', markersize=2)
def str_to_float(str):
try:
number = float(str)
except ValueError:
number = 0.0
return number
fmt = str_to_float(temp)
#annotate point temperature on plot
plt.text(grid_x, grid_y, fmt, fontdict=None)
Traceback Error:
Traceback (most recent call last):
File "plotpoints.py", line 56, in <module>
fmt = str_to_float(temp)
File "plotpoints.py", line 51, in str_to_float
number = float(str)
TypeError: only length-1 arrays can be converted to Python scalars
Data sample from text file tmax_day0:
latitude,longitude,value
36.65408,-83.21783,90
41.00928,-74.73628,92.02
43.77714,-71.75598,90
44.41944,-72.01944,88.8
39.5803,-79.3394,79
38.3154,-76.5501,86
38.91444,-82.09833,94
40.64985,-75.44771,92.6
41.25389,-70.05972,81.2
39.45202,-74.56699,90.88
I was able to achieve plotting data values only by using the following code:
for i in range(len(temp)):
plt.text(x[i], y[i], temp[i], va="top", family="monospace")
Result:
You aren't using a "proper" array, and are instead using a numpy array. Numpy arrays don't play well with non-numpy functions.
Going from your comment, this has been edited.
You would first need to fix the string so it's a proper array.
fmt = fmt[0].split()
I think should work to create a new (normal) array of strings. And then this to map that to an array of floats:
list_of_floats = np.array(map(float, fmt))

Interpolating on a 2D grid python

I'm having small troubles to understand how to implement a cubic interpolation on a grid.
I have 25 magnitude values stored on a 1D array. Each values represents the number inside a cell. Thus, the values are being showed on a 5x5 imshow image where each of the 25 resulting cells represents my stored values.
I have successfully used built-in interpolation parameters on imshow but I would really like to return the result of the interpolation.
data = 1D numpy array with 25 values
imshow(data .reshape(5, 5), origin='upper', interpolation='lanczos', cmap=cm.jet)
I was trying to go with this built-in scipy interpolation function, however I do not know it it is really the best way.
from scipy.interpolate import griddata
grid_x, grid_y = np.mgrid[0:4:100j, 0:4:200j]
grid_z2 = griddata(np.array([arange(5), arange(5)]).T, data, (grid_x, grid_y), method='cubic')
I'm receiving a ValueError with different number of values and points. If this is not really the best way I'm able to use other approaches.
Traceback (most recent call last):
File "file.py", line 100, in <module>
grid_z2 = griddata(np.array([arange(5), arange(5)]).T, data, (grid_x, grid_y), method='cubic')
File "C:\Program Files\Anaconda\lib\site-packages\scipy\interpolate\ndgriddata.py", line 212, in griddata
rescale=rescale)
File "scipy\interpolate\interpnd.pyx", line 840, in scipy.interpolate.interpnd.CloughTocher2DInterpolator.__init__ (scipy\interpolate\interpnd.c:9953)
File "scipy\interpolate\interpnd.pyx", line 78, in scipy.interpolate.interpnd.NDInterpolatorBase.__init__ (scipy\interpolate\interpnd.c:2342)
File "scipy\interpolate\interpnd.pyx", line 121, in scipy.interpolate.interpnd.NDInterpolatorBase._check_init_shape (scipy\interpolate\interpnd.c:3085)
ValueError: different number of values and points
I think I manage to use interpolate.RectBivariateSpline to satisfy my ends.
Here's the full complete code:
from scipy import interpolate
x = arange(5)
y = arange(5)
sp_x = interpolate.RectBivariateSpline(x, y, data)
nx = linspace(0, 4, 50)
ny = linspace(0, 4, 50)
n_data = sp_x(nx, ny)

How to back-calculate latitude and longitude from scipy.interpolate.RectSphereBivariateSpline?

Using THIS example from the RectSphereBivariateSpline function in the scipy.interpolate sub-package, I would like to back calculate arrays with latitude and longitude in degrees, and an array with the interpolated data value for each coordinate on the grid.
The interpolated data object RectSphereBivariateSpline creates appears to be the u and v components of the data values, with one value for each degree change of the grid (Latitude = 180 and Longitude = 360 for this example).
Is that right?
How might I back-calculate latitude, longitude, and their respective data values for plotting?
import numpy as np
from scipy.interpolate import RectSphereBivariateSpline
def geo_interp(lats,lons,data,grid_size_deg):
'''We want to interpolate it to a global one-degree grid'''
deg2rad = np.pi/180.
new_lats = np.linspace(grid_size_deg, 180, 180) * deg2rad
new_lons = np.linspace(grid_size_deg, 360, 360) * deg2rad
new_lats, new_lons = np.meshgrid(new_lats, new_lons)
'''We need to set up the interpolator object'''
lut = RectSphereBivariateSpline(lats*deg2rad, lons*deg2rad, data)
'''Finally we interpolate the data. The RectSphereBivariateSpline
object only takes 1-D arrays as input, therefore we need to do some reshaping.'''
data_interp = lut.ev(new_lats.ravel(),
new_lons.ravel()).reshape((360, 180)).T
return data_interp
if __name__ == '__main__':
import matplotlib.pyplot as plt
'''Suppose we have global data on a coarse grid'''
lats = np.linspace(10, 170, 9) # in degrees
lons = np.linspace(0, 350, 18) # in degrees
data = np.dot(np.atleast_2d(90. - np.linspace(-80., 80., 18)).T,
np.atleast_2d(180. - np.abs(np.linspace(0., 350., 9)))).T
'''Interpolate data to 1 degree grid'''
data_interp = geo_interp(lats,lons,data,1)
'''Looking at the original and the interpolated data,
one can see that the interpolant reproduces the original data very well'''
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax1.imshow(data, interpolation='nearest')
ax2 = fig.add_subplot(212)
ax2.imshow(data_interp, interpolation='nearest')
plt.show()
I thought I might use vector addition (i.e. Pythagorean theorem), but this doesn't work as I only have one value for each degree change, not point
pow((pow(data_interp[0,:],2.0)+pow(data_interp[:,0],2.0)),1/2.0)
It seems that the latitude and longitude vectors that can be used for plotting are generated in our "geo_interp" function ("net_lats" and "new_lons"). If you require these in your main program, you should either declare these vectors outside of your function, or you should have the function return these generated vectors to be used in the main program.
Hope this helps.

Categories