Calculating length between 3D points - python

I created a spline in Abaqus and now I would like to calculate the length of that spline.
The spline consists of 19 points [CoGX_Init, CoGY_Init, CoGZ_Init].
I want to determine the distance between each point with the following formula; (sqrt((x2-x1)**2+(y2-y1)**2+(z2-z1)**2)) and then count these points to find the complete length of the spline.
This is my code;
N = np.zeros((1, len(CoGZ_Init)))
for j in range(0, len(CoGZ_Init)-1):
x1 = CoGX_Init[j]
x2 = CoGX_Init[j+1]
y1 = CoGY_Init[j]
y2 = CoGY_Init[j+1]
z1 = CoGZ_Init[j]
z2 = CoGZ_Init[j+1]
N[j] = sqrt((x2-x1)**2+(y2-y1)**2+(z2-z1)**2)
print(sum[N])
When I run this, I receive the error the following error for line N[j]:
index 1 is out of bounds for axis 0 with size 1.

Your array N is an array like [[0., 0., 0., ... 0.]] -- so you need to change
N = np.zeros((1, len(CoGZ_Init)))
to
N = np.zeros(len(CoGZ_Init))
OR change
N[j] = sqrt((x2-x1)**2+(y2-y1)**2+(z2-z1)**2)
to
N[0][j] = sqrt((x2-x1)**2+(y2-y1)**2+(z2-z1)**2)

Related

Manual RK4 method for solving IVP (data formatting problem)

Currently I'm attempting to solve 4 coupled ODE's to stabilize an inverted pendulum on a cart. I have no problem doing it with ODEINT from Scipy, however, I can't make it work with a manual implementation. Most likely this is due to some weird data formatting done in the 'model' function in the code.
I have tried multiple things to no avail, thus I won't post my error codes, since they range from the size not fitting when adding all the calculated steps in the RK4 method.
My current code with ODEINT working is down below. What I'm asking is whether someone can help me, so that the function 'model' is properly made, so that I can implement the RK4 solver (which I can do for other ODE's without any problem).
import numpy as np
from scipy.integrate import solve_ivp
from scipy import signal
g = 9.82
l = 0.281
mc = 6.28
alpha = 0.4
mp = 0.175
t_start = 0.
t_end = 12.
tol = 10**(-1)
# Define A and B and the poles we want
A = np.array([[0., 1., 0., 0.], [(mc+mp)*g/(l*mc), 0., 0., (-alpha)/(l*mc)], [0., 0., 0., 1.], [(g*mp)/mc, 0., 0., (-alpha)/mc]])
B = np.array([[0.], [1./(l*mc)], [0.], [1./mc]])
Poles = np.array([complex(-1.,2.), complex(-1.,-2.), complex(-2.,1.), complex(-2.,-1.)])
# Determine K
signal = signal.place_poles(A, B, Poles)
K = signal.gain_matrix
# print(signal.computed_poles) # To verify if the computes poles are correct
# Define the model
def model(t,x):
x1, x2, x3, x4 = x
u = -np.matmul(K,x)
dx1dt = x2
dx2dt = (np.cos(x1.astype(float))*(u-alpha*x4-mp*l*x2**2*np.sin(x1.astype(float)))+(mc+mp)*g*np.sin(x1.astype(float)))/(l*(mc+mp*(1-np.cos(x1.astype(float))**2)))
dx3dt = x4
dx4dt = (u-alpha*x4-mp*l*x2**2*np.sin(x1.astype(float))+mp*g*np.sin(x1.astype(float))*np.cos(x1.astype(float)))/(mc+mp*(1-np.cos(x1.astype(float))**2))
return np.array([dx1dt, dx2dt, dx3dt, dx4dt])
# Solve the system
N = 10000 # Number of steps
t = np.linspace(t_start, t_end, N)
t_span = (t_start, t_end)
x0 = np.array([0.2, 0., 0., 0.])
sol = solve_ivp(model,t_span,x0, t_eval=t, method='RK45')
index = np.argmin(sol.y[2,:]) # Max displacement from the origin
print(f' The biggest deviation from the origin is: {abs(sol.y[2, index])} meters.')
#This doesn't work
def RK4(fcn,a ,b ,y0 ,N):
h = (b-a)/N
x = a + np.arange(N+1)*h
y = np.zeros((x.size,y0.size))
y[0,:] = y0
for k in range(N):
k1 = fcn(x[k], y[k,:])
k2 = fcn(x[k] + h/2, y[k,:] + h*k1/2)
k3 = fcn(x[k] + h/2, y[k,:] + h*k2/2)
k4 = fcn(x[k] + h, y[k,:] + h*k3)
y[k+1,:] = y[k,:] + h/6*(k1 + 2*(k2 + k3) + k4)
return x,y
a,b = RK4(model, 0, 12, x0, 1000)
Which yields the following error:
runcell(0, 'C:/Users/Nikolai Lund Kühne/OneDrive - Aalborg Universitet/Uni/3. semester/P3 - Dynamiske Systemer/manualRK4.py')
The biggest deviation from the origin is: 0.48256054833140316 meters.
Traceback (most recent call last):
File "C:\Users\Nikolai Lund Kühne\OneDrive - Aalborg Universitet\Uni\3. semester\P3 - Dynamiske Systemer\manualRK4.py", line 57, in <module>
a,b = RK4(model, 0, 12, x0, 1000)
File "C:\Users\Nikolai Lund Kühne\OneDrive - Aalborg Universitet\Uni\3. semester\P3 - Dynamiske Systemer\manualRK4.py", line 53, in RK4
y[k+1,:] = y[k,:] + h/6*(k1 + 2*(k2 + k3) + k4)
ValueError: could not broadcast input array from shape (4,4,4) into shape (4)
Edit 2: Attempt to implement RK4 manually results in some weird errors.
Edit 1: Based on a comment the code is now implemented with solve_ivp.
I did not completely debug this, and you could also reduce the data to a state where the expected happens. So some speculation.
Numpy is halping in the style of Matlab. The constructed format of K is an array in the shape of a row vector, [[K1,K2,K3,K4]]. Now the matrix-vector multiplication in any form, K#x, has a one-dimensional result. Mathematically, one would expect either a scalar or a 1x1 matrix [[u1]]. Following the Matlab philosophy it is neither, it is a simple array u=[u1]. Any further scalar operation that has u inside will also result in 1-element arrays. Putting the derivatives together, this has the effect of producing a column vector. Now further operations with arrays have the potential to broadcast that to a 4x4 matrix-shaped array. How the 4x4x4 shaped tensor occurs I did not follow-up on, but it seems quite possible.

Latin hypercube sampling

LHS method provides sampling values between zero to 1. If I want to set bounds, for example, for one dimension value should be -0 to 15? How can I do that in pyDOE python?
from pyDOE import *
n = 2
samples = 50
d = lhs(n, samples, criterion='center')
x1 = d[:,0]
x2 = d[:,1]
My x1 values should be between -10 to 10, and x2 should be 1 to 20.
Multiple each data point in x1 (or x2) by the range of your bounds e.g. 10 - (-10) ie. 20, and add it to the lower bound.
x1_new = [None for i in range(len(x1))]
for i,j in enumerate(x1):
x1_new[i] = -10 + 20*j
... I think?
I figured it out
import pyDOE as pyd
bounds = np.array([[-10,10],[1,20]]) #[Bounds]
# print(xbounds)
X = pyd.lhs(2, 100, criterion='centermaximin')
X[:,0] = (X[:,0]*(bounds[0,1]-bounds[0,0])+bounds[0,0])
X[:,1] = (X[:,1]*(bounds[1,1]-bounds[1,0])+bounds[1,0])

How to find a vector from a list of vectors that is nearest to all other vectors using Python?

I have a list of vectors as a numpy array.
[[ 1., 0., 0.],
[ 0., 1., 2.] ...]
They all have the same dimension. How do I find out that in the vector space which vector is the closest to all the other vectors in the array? Is there scipy or sklearn function that calculates this?
Update:
By "closest", I meant the cosine and the Euclidean distance.
Update 2:
Let's say I have 4 vectors (a,b,c,d), and the Cosine distance between the vectors are:
a,b = 0.2
a,c = 0.9
a,d = 0.7
b,c = 0.5
b,d = 0.75
c,d = 0.8
So for each, vector a,b,c,d I get :
{
'a': [1,0.2,0.9,0.7],
'b': [0.2,1,0.5,0.75],
'c' : [0.9,0.5,1,0.75],
'd' : [0.7,0.75,0.8,1]
}
Is there a way of saying let's say vector d is the one that is the most similar to a,b,c ?
You could brute force it something like this. Note that this is O(n^2), and will get slow for large n.
import numpy as np
def cost_function(v1, v2):
"""Returns the square of the distance between vectors v1 and v2."""
diff = np.subtract(v1, v2)
# You may want to take the square root here
return np.dot(diff, diff)
n_vectors = 5
vectors = np.random.rand(n_vectors,3)
min_i = -1
min_cost = 0
for i in range (0, n_vectors):
sum_cost = 0.0
for j in range(0, n_vectors):
sum_cost = sum_cost + cost_function(vectors[i,:],vectors[j,:])
if min_i < 0 or min_cost > sum_cost:
min_i = i
min_cost = sum_cost
print('{} at {}: {:.3f}'.format(i, vectors[i,:], sum_cost))
print('Lowest cost point is {} at {}: {:.3f}'.format(min_i, vectors[min_i,:], min_cost))

Find neighbors with cuts efficiently and return index

I have many points in the x,y plane, with length around 10000, each point (x,y) has an intrinsic radius r. This small data set is only one tiny corner of my entire data set. I have an interested point (x1,y1), I want to find nearby point around (x1,y1) within 1 and meet the criteria that the distance between (x,y) and (x1,y1) is less than r. I want to return the index of those good points, not the good points themselves.
import numpy as np
np.random.seed(2000)
x = 20.*np.random.rand(10000)
y = 20.*np.random.rand(10000)
r = 0.3*np.random.rand(10000)
x1 = 10. ### (x1,y1) is an interest point
y1 = 12.
def index_finder(x,y,r,x1,y1):
idx = (abs(x - x1) < 1.) & (abs(y - y1) < 1.) ### This cut will probably cut 90% of the data
x_temp = x[idx] ### but if I do like this, then I lose the track of the original index
y_temp = y[idx]
dis_square = (x_temp - x1)*(x_temp - x1) + (y_temp - y1)*(y_temp - y1)
idx1 = dis_square < r*r ### after this cut, there are only a few left
x_good = x_temp[idx1]
y_good = y_temp[idx1]
In this function, I can find the good points around (x1,y1), but not the index of those good points. HOWEVER, I need the ORIGINAL index because the ORIGINAL index are used to extract other data associated with the coordinate (x,y). As I mentioned, the sample data set is only a tiny corner of my entire data set, I will call the above function around 1,000,000 times for my entire data set, therefore the efficiency of the above index_finder function is also a consideration.
Any thoughts on such task?
Approach #1
We could simply index into the first mask with its own mask for selecting the True places masked values from the second stage, like so -
idx[idx] = idx1
Thus, idx would have the final valid masked values/ good valued places corresponding to original array x and y, i.e. -
x_good = x[idx]
y_good = y[idx]
This mask could then be used to index into other arrays as mentioned in the question.
Approach #2
As another approach, we could use two conditional statements , thus creating two masks with them. Finally, combine them with AND-ing to get the combined mask, which could be indexed into x and y arrays for the final outputs. We won't need to get the actual indices that way, so that's one more benefit with it.
Hence, the implementation -
X = x-x1
Y = y-y1
mask1 = (np.abs(X) < 1.) & (np.abs(Y) < 1.)
mask2 = X**2 + Y*2 < r**2
comb_mask = mask1 & mask2
x_good = x[comb_mask]
y_good = y[comb_mask]
If for some reason, you still need the corresponding indices, just do -
comb_idx = np.flatnonzero(comb_mask)
If you are doing these operations for different x1 and y1 pairs for the same x and y dataset, I would suggest using broadcasting to vectorize it through all those x1, y1 paired datasets, as shown in this post.
numpy.where seems made for finding the indices
the vectorized norm calc + np.where() could be faster than a loop
sq_norm = (x - x1)**2 + (y - y1)**2 # no need to take 10000 sqrt
idcs = np.where(sq_norm < 1.)
len(idcs[0])
Out[193]: 69
np.stack((idcs[0], x[idcs], y[idcs]), axis=1)[:5]
Out[194]:
array([[ 38. , 9.47165956, 11.94250173],
[ 39. , 9.6966941 , 11.67505453],
[ 276. , 10.68835317, 12.11589316],
[ 288. , 9.93632584, 11.07624915],
[ 344. , 9.48644057, 12.04911857]])
the norm calc can include the r array too, the 2nd step?
r_sq_norm = (x[idcs] - x1)**2 + (y[idcs] - y1)**2 - r[idcs]**2
r_idcs = np.where(r_sq_norm < 0.)
idcs[0][r_idcs]
Out[11]: array([1575, 3476, 3709], dtype=int64)
you might want to time the 2 step test vs including r in the 1st vectorized norm calc?
sq_norm = (x - x1)**2 + (y - y1)**2 - r**2
idcs = np.where(sq_norm < 0.)
idcs[0]
Out[13]: array([1575, 3476, 3709], dtype=int64)
You can take a mask of your indices, like so:
def index_finder(x,y,r,x1,y1):
idx = np.nonzero((abs(x - x1) < 1.) & (abs(y - y1) < 1.)) #numerical, not boolean
mask = (x[idx] - x1)*(x[idx] - x1) + (y[idx] - y1)*(y[idx] - y1) < r*r
idx1 = [i[mask] for i in idx]
x_good = x_temp[idx1]
y_good = y_temp[idx1]
now idx1 is the indices you want to extract.
Faster way in general to do this is to use scipy.spatial.KDTree
from scipy.spatial import KDTree
xy = np.stack((x,y))
kdt = KDTree(xy)
kdt.query_ball_point([x1, y1], r)
If you have many points to query against the same dataset, this will be much faster than sequentially calling your index_finder app.
x1y1 = np.stack((x1, y1)) #`x1` and `y1` are arrays of coordinates.
kdt.query_ball_point(x1y1, r)
ALSO WRONG: if you have different distances for each point, you can do:
def query_variable_ball(kdtree, x, y, r):
out = []
for x_, y_, r_ in zip(x, y, r):
out.append(kdt.query_ball_point([x_, y_], r_)
return out
xy = np.stack((x,y))
kdt = KDTree(xy)
query_variable_ball(kdt, x1, y1, r)
EDIT 2: This should work with different r values for each point
from scipy.spatial import KDTree
def index_finder_kd(x, y, r, x1, y1): # all arrays
xy = np.stack((x,y), axis = -1)
x1y1 = np.stack((x1, y1), axis = -1)
xytree = KDTree(xy)
d, i = xytree.query(x1y1, k = None, distance_upper_bound = 1.)
good_idx = np.zeros(x.size, dtype = bool)
for idx, dist in zip(i, d):
good_idx[idx] |= r[idx] > dist
x_good = x[good_idx]
y_good = y[good_idx]
return x_good, y_good, np.flatnonzero(good_idx)
This is very slow for only one (x1, y1) pair as the KDTree takes a while to populate. But if you have millions of pairs, this will be much faster.
(I've assumed you want the union of all good points in the (x, y) data for all (x1, y1), if you want them separately it's also possible using a similar method, removing elements of i[j] based on whether d[j] < r[i[j]])

Numpy: Linear system with specific conditions. No negative solutions

I'm writing a Python code using numpy. In my code I use "linalg.solve" to solve a linear system of n equations in n variables. Of course the solutions could be either positive or negative. What I need to do is to have always positive solutions or at least equal to 0. To do so I first want the software to solve my linear system of equations in this form
x=np.linalg.solve(A,b)
in which x is an array with n variables in a specific order (x1, x2, x3.....xn),
A is a n dimensional square matrix and b is a n-dimensional array.
Now I thought to do this:
-solve the system of equations
-check if every x is positive
-if not, every negative x I'll want them to be =0 (for example x2=-2 ---->x2=0)
-with a generic xn=0 want to eliminate the n-row and the n-coloumn in the n dimensional square matrix A (I'll obtain another square matrix A1) and eliminate the n element in b obtaining b1.
-solve the system again with the matrix A1 and b1
-re-iterate untill every x is positive or zero
-at last build a final array of n elements in which I'll put the last iteration solutions and every variable which was equal to zero ( I NEED THEM IN ORDER AS IT WOULD HAVE BEEN NO ITERATIONS so if during the iterations it was x2=0 -----> xfinal=[x1, 0 , x3,.....,xn]
Think it 'll work but don't know how to do it in python.
Hope I was clear. Can't really figure it out!
You have a minimization problem, i.e.
min ||Ax - b||
s.t. x_i >= 0 for all i in [0, n-1]
You can use the Optimize module from Scipy
import numpy as np
from scipy.optimize import minimize
A = np.array([[1., 2., 3.],[4., 5., 6.],[7., 8., 10.]], order='C')
b = np.array([6., 12., 21.])
n = len(b)
# Ax = b --> x = [1., -2., 3.]
fun = lambda x: np.linalg.norm(np.dot(A,x)-b)
# xo = np.linalg.solve(A,b)
# sol = minimize(fun, xo, method='SLSQP', constraints={'type': 'ineq', 'fun': lambda x: x})
sol = minimize(fun, np.zeros(n), method='L-BFGS-B', bounds=[(0.,None) for x in xrange(n)])
x = sol['x'] # [2.79149722e-01, 1.02818379e-15, 1.88222298e+00]
With your method I get x = [ 0.27272727, 0., 1.90909091].
In the case you still want to use your algorithm, it is below
n = len(b)
x = np.linalg.solve(A,b)
pos = np.where(x>=0.)[0]
while len(pos) < n:
Ap = A[pos][:,pos]
bp = b[pos]
xp = np.linalg.solve(Ap, bp)
x = np.zeros(len(b))
x[pos] = xp
pos = np.where(x>=0.)[0]
But I don't recommend you to use it, you should use the minimize option.
Even faster and more reliable also using minimization is the method scipy.optimize.lsq_linear that is specially dedicated for linear optimization.
The bounds are transposed and use np.inf instead of None for the upper bound.
Working example:
from scipy.optimize import lsq_linear
n = A.shape[1]
res = lsq_linear(A, b, bounds=np.array([(0.,np.inf) for i in range(n)]).T, lsmr_tol='auto', verbose=1)
y = res.x
You provide a matrix A and a vector b that have the same number of rows (= the dimension of the range of A).

Categories