I just have place holders for the initial conditions. But it seems the u function is the main issue. In removing it and replacing it as a constant I get a float int error. I feel like I'm almost there and am just making a small mistake.
enter image description here
code:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc('text', usetex = True)
mpl.rc('font', family = 'serif', size=16)
from scipy.integrate import solve_ivp
%matplotlib inline
def EulerSys(F, y0, ti, tf, h):
t = np.arange(ti, tf+h, h)
n = len(t)
neq = len(y0)
x = np.zeros((neq, n))
x[:,0] = y0
for i in range(t.size - 1):
# [new value] = [old value] + [slope x step size]
x[:,i+1] = x[:,i] + h*F( t[i], x[:,i] )
return x
m1=55
m2=400
m3=100
k1=230000
k2=30000
k3=50000
k4=0
b2=1500
b3=4000
b4=700
L0=5
v=15
A=0.03
h = .05
ti =0
tf =3.2
t = np.arange(ti, tf+h, h)
# the initial conditions:
x0 = np.array([ 2.0, 0.0 , 1, 1,1,1])
n = len(t)
neq = len(x0)
# initialize the vector to store the solutions:
# the rows are the solutions, the columns are the time instances:
x = np.zeros((neq, n))
# store the initial conditions:
x[:,0] = x0
print(x)
def myFun(t,x):
u = lambda t: (A/2)*(1-np.cos(2*(3.14*(v*t/L0)))
n = len(x)
dx = np.zeros((n))
dx[0] = x[1]
dx[1] = (-(b2 + b4)*x[1]+b2*x[3]+b4*x[5]-[k1+k2+k4]*x[0]+k2*x[2]+k4*x[4]+k1*u)/m1
dx[2] = x[3]
dx[3] = (b2*x[1]-(b2+b3)*x[3]+b3*x[5]+k2*x[0]-(k2+k3)*x[2]+k3*x[4])/m2
dx[4] = x[5]
dx[5] = (b4*x[1]+b3*x[3]-(b3+b4)*x[5]+k4*x[0]+k3*x[2]-(k3+k4)*x(4))/m3
return dx
xsol= EulerSys(myFun, x0, ti, tf, h)
you are simply missing one bracket at the end.
u = lambda t: (A/2)*(1-np.cos(2*(3.14*(v*t/L0)))
should be:
u = lambda t: (A/2)*(1-np.cos(2*(3.14*(v*t/L0)))) # one more bracket
here is the test code that is stripped from the original question:
import numpy as np
# define variables needed
A = 1
v = 1
L0 = 1
u = lambda t: (A/2)*(1-np.cos(2*(3.14*(v*t/L0))))
print(u(1))
the result (with test numbers) is this:
2.536543312392503e-06
Suppose I have equations x = z + 2 and y = x + 1 and I wish to substitute the first one into the second one, to eliminate x and get y = z + 3. In SymPy, I can create the first two equations as:
x = sympy.symbols('x')
y = sympy.symbols('y')
z = sympy.symbols('z')
equation_one = sympy.Eq(x, z + 2)
equation_two = sympy.Eq(y, x + 1)
What is the correct way to now substitute equation_one into equation_two? The output should be a new equation.
An approach that works in this case is to use the attributes lhs/rhs ("left hand side" and "right hand side").
import sympy as sp
x = sp.symbols('x')
y = sp.symbols('y')
z = sp.symbols('z')
equation_one = sp.Eq(x, z + 2)
equation_two = sp.Eq(y, x + 1)
print(equation_two.subs(equation_one.lhs,equation_one.rhs))
Result:
Eq(y, z + 3)
I have a curve as shown below:
The x coordinates and the y coordinates for this plot are:
path_x= (4.0, 5.638304088577984, 6.785456961280076, 5.638304088577984, 4.0)
path_y =(0.0, 1.147152872702092, 2.7854569612800755, 4.423761049858059, 3.2766081771559668)
And I obtained the above picture by:
x_min =min(path_x)-1
x_max =max(path_x)+1
y_min =min(path_y)-1
y_max =max(path_y)+1
num_pts = len(path_x)
fig = plt.figure(figsize=(8,8))
#fig = plt.figure()
plt.suptitle("Curve and the boundary")
ax = fig.add_subplot(1,1,1)
ax.set_xlim([min(x_min,y_min),max(x_max,y_max)])
ax.set_ylim([min(x_min,y_min),max(x_max,y_max)])
ax.plot(path_x,path_y)
Now my intention is to draw a smooth curve using cubic splines. But looks like for cubic splines you need the x coordinates to be on ascending order. whereas in this case, neither x values nor y values are in the ascending order.
Also this is not a function. That is an x value is mapped with more than one element in the range.
I also went over this post. But I couldn't figure out a proper method to solve my problem.
I really appreciate your help in this regard
As suggested in the comments, you can always parameterize any curve/surface with an arbitrary (and linear!) parameter.
For example, define t as a parameter such that you get x=x(t) and y=y(t). Since t is arbitrary, you can define it such that at t=0, you get your first path_x[0],path_y[0], and at t=1, you get your last pair of coordinates, path_x[-1],path_y[-1].
Here is a code using scipy.interpolate
import numpy
import scipy.interpolate
import matplotlib.pyplot as plt
path_x = numpy.asarray((4.0, 5.638304088577984, 6.785456961280076, 5.638304088577984, 4.0),dtype=float)
path_y = numpy.asarray((0.0, 1.147152872702092, 2.7854569612800755, 4.423761049858059, 3.2766081771559668),dtype=float)
# defining arbitrary parameter to parameterize the curve
path_t = numpy.linspace(0,1,path_x.size)
# this is the position vector with
# x coord (1st row) given by path_x, and
# y coord (2nd row) given by path_y
r = numpy.vstack((path_x.reshape((1,path_x.size)),path_y.reshape((1,path_y.size))))
# creating the spline object
spline = scipy.interpolate.interp1d(path_t,r,kind='cubic')
# defining values of the arbitrary parameter over which
# you want to interpolate x and y
# it MUST be within 0 and 1, since you defined
# the spline between path_t=0 and path_t=1
t = numpy.linspace(numpy.min(path_t),numpy.max(path_t),100)
# interpolating along t
# r[0,:] -> interpolated x coordinates
# r[1,:] -> interpolated y coordinates
r = spline(t)
plt.plot(path_x,path_y,'or')
plt.plot(r[0,:],r[1,:],'-k')
plt.xlabel('x')
plt.ylabel('y')
plt.show()
With output
For non-ascending x splines can be easily computed if you make both x and y functions of another parameter t: x(t), y(t).
In your case you have 5 points so t should be just enumeration of these points, i.e. t = 0, 1, 2, 3, 4 for 5 points.
So if x = [5, 2, 7, 3, 6] then x(t) = x(0) = 5, x(1) = 2, x(2) = 7, x(3) = 3, x(4) = 6. Same for y.
Then compute spline function for both x(t) and y(t). Afterwards compute values of splines in all many intermediate t points. Lastly just use all calculated values x(t) and y(t) as a function y(x).
Once before I implemented cubic spline computation from scratch using Numpy, so I use this code in my example below if you don't mind (it could be useful for you to learn about spline math), replace with your library functions. Also in my code you can see numba lines commented out, if you want you can use these Numba annotations to speed up computation.
You have to look at main() function at the bottom of code, it shows how to compute and use x(t) and y(t).
Try it online!
import numpy as np, matplotlib.pyplot as plt
# Solves linear system given by Tridiagonal Matrix
# Helper for calculating cubic splines
##numba.njit(cache = True, fastmath = True, inline = 'always')
def tri_diag_solve(A, B, C, F):
n = B.size
assert A.ndim == B.ndim == C.ndim == F.ndim == 1 and (
A.size == B.size == C.size == F.size == n
) #, (A.shape, B.shape, C.shape, F.shape)
Bs, Fs = np.zeros_like(B), np.zeros_like(F)
Bs[0], Fs[0] = B[0], F[0]
for i in range(1, n):
Bs[i] = B[i] - A[i] / Bs[i - 1] * C[i - 1]
Fs[i] = F[i] - A[i] / Bs[i - 1] * Fs[i - 1]
x = np.zeros_like(B)
x[-1] = Fs[-1] / Bs[-1]
for i in range(n - 2, -1, -1):
x[i] = (Fs[i] - C[i] * x[i + 1]) / Bs[i]
return x
# Calculate cubic spline params
##numba.njit(cache = True, fastmath = True, inline = 'always')
def calc_spline_params(x, y):
a = y
h = np.diff(x)
c = np.concatenate((np.zeros((1,), dtype = y.dtype),
np.append(tri_diag_solve(h[:-1], (h[:-1] + h[1:]) * 2, h[1:],
((a[2:] - a[1:-1]) / h[1:] - (a[1:-1] - a[:-2]) / h[:-1]) * 3), 0)))
d = np.diff(c) / (3 * h)
b = (a[1:] - a[:-1]) / h + (2 * c[1:] + c[:-1]) / 3 * h
return a[1:], b, c[1:], d
# Spline value calculating function, given params and "x"
##numba.njit(cache = True, fastmath = True, inline = 'always')
def func_spline(x, ix, x0, a, b, c, d):
dx = x - x0[1:][ix]
return a[ix] + (b[ix] + (c[ix] + d[ix] * dx) * dx) * dx
# Compute piece-wise spline function for "x" out of sorted "x0" points
##numba.njit([f'f{ii}[:](f{ii}[:], f{ii}[:], f{ii}[:], f{ii}[:], f{ii}[:], f{ii}[:])' for ii in (4, 8)],
# cache = True, fastmath = True, inline = 'always')
def piece_wise_spline(x, x0, a, b, c, d):
xsh = x.shape
x = x.ravel()
ix = np.searchsorted(x0[1 : -1], x)
y = func_spline(x, ix, x0, a, b, c, d)
y = y.reshape(xsh)
return y
def main():
x0 = np.array([4.0, 5.638304088577984, 6.785456961280076, 5.638304088577984, 4.0])
y0 = np.array([0.0, 1.147152872702092, 2.7854569612800755, 4.423761049858059, 3.2766081771559668])
t0 = np.arange(len(x0)).astype(np.float64)
plt.plot(x0, y0)
vs = []
for e in (x0, y0):
a, b, c, d = calc_spline_params(t0, e)
x = np.linspace(0, t0[-1], 100)
vs.append(piece_wise_spline(x, t0, a, b, c, d))
plt.plot(vs[0], vs[1])
plt.show()
if __name__ == '__main__':
main()
Output:
I am trying to create y as an array to create a function iterating through zeta which is dependent upon E all using a for loop. However the values are not being added to the list.
I have also tried defining the variables and the mathematical function as two different coding functions
screenshot
import cmath
import matplotlib.pyplot as plt
a = 2*10**-15
Vo = 83*10**6
m = 1.6726*10**(-27)
pi = cmath.pi
E = []
E.append(-83*10**6)
hbar = 6.62607015*10**(-34)/ pi
K = 16.032280*10**6
y = []
y.append(51311.18131)
def variables(y, E):
for i in range(1, 83, 1):
alpha = cmath.sqrt(2*m*(E[i-1]+Vo)/(hbar**2))
zeta = alpha*a
eta = cmath.sqrt(k - zeta**2)
y[i] = zeta*cmath.tan(zeta) - eta
E[i] = E[i-1] + 1
return y, E
print('E = ', E, 'Y = ', y)
plt.plot(E, y)
The program as of now should graph y values as a function of Zeta which is changing with energy.
You don't need the loop to be in a function, just put it at the top-level of the script. And use y.append() and E.append() to add to those lists.
for i in range(1, 83):
alpha = cmath.sqrt(2*m*(E[i-1]+Vo)/(hbar**2))
zeta = alpha*a
eta = cmath.sqrt(k - zeta**2)
y.append(zeta*cmath.tan(zeta) - eta)
E.append(E[i-1] + 1)
In addition to #Barmar's answer your k variable needs to be K (upper case).
import cmath
import matplotlib.pyplot as plt
a = 2*10**-15
Vo = 83*10**6
m = 1.6726*10**(-27)
pi = cmath.pi
E = [0] * 83
E.append(-83*10**6)
hbar = 6.62607015*10**(-34)/ pi
K = 16.032280*10**6
y = [0] * 83
y.append(51311.18131)
for i in range(1, 83, 1):
alpha = cmath.sqrt(2*m*(E[i-1]+Vo)/(hbar**2))
zeta = alpha*a
eta = cmath.sqrt(K - zeta**2)
y[i] = zeta*cmath.tan(zeta) - eta
E[i] = E[i-1] + 1
print('E = ', E, 'Y = ', y)
plt.plot(E, y)
Also it is not required to use append as sometimes append doesn't always work well with calculated index lookups. It might be better for you to initialize the y and E lists to be the length of your loop first.
I'm trying to solve the following problem of coupled ODEs using odeint() from scipy. The system looks like this:
X'_k = mean(Y_k) + F
Y'_{k,j} = X_k - Y_{k,j}
This is a system with 3 X variables, and for each X variable, there are other 3 Y variables.
From what I read from the documentation, and the examples here and here, I can pass the system of equations as a list. And that is what I tried in the following example:
import numpy as np
from scipy.integrate import odeint
def dZdt(Z, t):
X = Z[0]
Y = Z[1]
F = 4
d_x = np.zeros(3)
d_y = np.zeros(3*3).reshape(3,3)
# Compute the Y values
for k in range(3):
for j in range(3):
d_y[k][j] = X[k] - Y[k][j]
# X values
d_x[k] = Y[k].mean() + F
d = [d_x, d_y]
return d
# Initial conditions
X0 = np.random.uniform(size=3)
Y0 = np.random.uniform(size = 3*3).reshape(3,3)
Z0 = [X0, Y0]
t = range(20)
Z = odeint(dZdt, Z0, t)
Where k, j = (1,2,3) and Z = [X,Y]
But I'm afraid I'm getting the following error:
ValueError: could not broadcast input array from shape (3,3) into shape (3)
My real problem is more complex, because j, and k, can be bigger than 3 (they go from 1 to j_max, and K_max, respectively) so I cannot write the 12 variables one by one.
My guessing is that somewhere in the code, Y variables are tried to fill in X shape... but no clue about where.
Any idea of what I'm doing wrong?
You are trying to represent an unknown function by two arrays inside of a list. It must be a one-dimensional array. So, instead of 3 X-variables and 9 Y-variables it must be a flat list of 12 variables. Like this:
def dZdt(Z, t):
X = Z[:3]
Y = Z[3:].reshape(3, 3)
F = 4
d_x = np.zeros(3)
d_y = np.zeros((3, 3))
# Compute the Y values
for k in range(3):
for j in range(3):
d_y[k, j] = X[k] - Y[k, j]
# X values
d_x[k] = Y[k].mean() + F
d = np.concatenate((d_x.ravel(), d_y.ravel()))
return d
# Initial conditions
X0 = np.random.uniform(size=3)
Y0 = np.random.uniform(size=(3, 3))
Z0 = np.concatenate((X0.ravel(), Y0.ravel()))
t = range(20)
Z = odeint(dZdt, Z0, t)
NumPy arrays are indexed as Y[k, j], not Y[k][j]. And there are ample vectorization opportunities that would eliminate the loops in the computation of dZdt. Like this:
def dZdt(Z, t):
X = Z[:3]
Y = Z[3:].reshape(3, 3)
F = 4
d_y = X[:, None] - Y
d_x = Y.mean(axis=1) + F
d = np.concatenate((d_x.ravel(), d_y.ravel()))
return d