Limit the output of ODEINT Python - python

I'm trying to define 'hard' limits to the return values of Scipy's odeint function but am unsure if function allows for such. I've modified this example from Scipy Cookbook so that the coupled two bodied system could collide.
Image Link
Graph Collision
Imaginary Stop
Specifically, the spring constant and mass have been changed to be weaker and lighter. You'll also notice the 'if' statement used inside the function trying to limit the travel of 'm1' no further than the initial input of 'x2'. Sorry, I don't have enough reputation points to post the graphs, but you'll clearly see the two masses can occupy the same space during portions of the solution.
# Use ODEINT to solve the differential equations defined by the vector field
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import numpy as np
def vectorfield(w, t, p):
"""
Defines the differential equations for the coupled spring-mass system.
Arguments:
w : vector of the state variables:
w = [x1,y1,x2,y2]
t : time
p : vector of the parameters:
p = [m1,m2,k1,k2,L1,L2,b1,b2]
"""
x1, y1, x2, y2 = w
m1, m2, k1, k2, L1, L2, b1, b2 = p
# Create f = (x1',y1',x2',y2'):
f = [y1,
(-b1 * y1 - k1 * (x1 - L1) + k2 * (x2 - x1 - L2)) / m1,
y2,
(-b2 * y2 - k2 * (x2 - x1 - L2)) / m2]
if y1 > x2:
y1 == x2
else:
y1 == y1
return f
# Parameter values
# Masses:
m1 = 0.5
m2 = 1.5
# Spring constants
k1 = 0.1
k2 = 40.0
# Natural lengths
L1 = 0.5
L2 = 1.0
# Friction coefficients
b1 = 0.8
b2 = 0.5
# Initial conditions
# x1 and x2 are the initial displacements; y1 and y2 are the initial velocities
x1 = 0.5
y1 = 0.0
x2 = 4.25
y2 = 0.0
# ODE solver parameters
abserr = 1.0e-8
relerr = 1.0e-6
stoptime = 5.0
numpoints = 2500
# Create the time samples for the output of the ODE solver.
# I use a large number of points, only because I want to make
# a plot of the solution that looks nice.
t = [stoptime * float(i) / (numpoints - 1) for i in range(numpoints)]
# Pack up the parameters and initial conditions:
p = [m1, m2, k1, k2, L1, L2, b1, b2]
w0 = [x1, y1, x2, y2]
# Call the ODE solver.
wsol = odeint(vectorfield, w0, t, args=(p,),
atol=abserr, rtol=relerr)
plt.plot(t, wsol[:, 0], 'b', label='theta(t)')
plt.plot(t, wsol[:, 2], 'g', label='omega(t)')
plt.show()
I'm unsure if my variables are not being set up correctly, or if the odeint function cannot accept these types of 'limits' I'm trying to add. Ideally, it would be nice if an 'upper boundary' could be set, if the displacement of the mass were beyond that value, it could then be set equal to a maximum limit of sorts.
Thank you in advance for the help.

Related

solve_ivp "ValueError: setting an array element with a sequence" issue

I am trying to solve a system of ODE's related to a coupled mass and spring system using solve_ivp. I am getting a problem stating that "ValueError: setting an array element with a sequence". See below for the code I am trying to use.
from scipy.integrate import solve_ivp
import numpy as np
func(t, w, p):
#unpack the variables and parameters"
x1, y1, x2, y2 = w
m1, m2, k1, k2, L1, L2, b1, b2 = p
# Create the functions
y1 = (-b1 * y1 - k1 * (x1 - L1) + k2 * (x2 - x1 - L2)) / m1,
y2 = (-b2 * y2 - k2 * (x2 - x1 - L2)) / m2
return y1, y2
# Parameter values
# Masses:
m1 = 1.0
m2 = 1.5
# Spring constants
k1 = 8.0
k2 = 40.0
# Natural lengths
L1 = 0.5
L2 = 1.0
# Friction coefficients
b1 = 0.8
b2 = 0.5
# Initial conditions
# x1 and x2 are the initial displacements; y1 and y2 are the initial velocities
x1 = 0.5
y1 = 0.0
x2 = 2.25
y2 = 0.0
# Pack up the parameters and initial conditions:
p = [m1, m2, k1, k2, L1, L2, b1, b2]
w0 = [x1, y1, x2, y2]
t_span = np.linspace(0, 15, 1000)
Sol = solve_ivp(lambda t, w: func(t, w, p), [t_span[0], t_span[-1]], y0=w0, t_eval=t_span)
I am not really sure where I am going wrong because the error doesn't specify which line of code the problem is with. Has anyone experienced this before?
If x is the position and y the velocity, then name z the acceleration and return a vector of the derivatives of the inputs
def func(t, w, p):
#unpack the variables and parameters"
x1, y1, x2, y2 = w
m1, m2, k1, k2, L1, L2, b1, b2 = p
# Create the functions
z1 = (-b1 * y1 - k1 * (x1 - L1) + k2 * (x2 - x1 - L2)) / m1,
z2 = (-b2 * y2 - k2 * (x2 - x1 - L2)) / m2
return y1, z1, y2, z2
Everything else looks like it should work.

Solve over-determined system of linear equations

I have a rather simple system of equations of the form:
1*A + 0*B + x2*C + y2*D = x1
0*A + 1*B + y2*C + x2*D = y1
where the pairs (x1,y1) and (x2,y2) are known floats of length N (the system is over-determined), and I need to solve for the A, B, C, D parameters.
I've been playing around with numpy.linalg.lstsq but I can't seem to get the shapes of the matrices right. This is what I have
import numpy as np
N = 10000
x1, y1 = np.random.uniform(0., 5000., (2, N))
x2, y2 = np.random.uniform(0., 5000., (2, N))
# 1*A + 0*B + x2*C + y2*D = x1
# 0*A + 1*B + y2*C + x2*D = y1
l1 = np.array([np.ones(N), np.zeros(N), x2, y2])
l2 = np.array([np.zeros(N), np.ones(N), y2, x2])
M1 = np.array([l1, l2])
M2 = np.array([x1, y1])
ABCD = np.linalg.lstsq(M1, M2)[0]
print(ABCD)
which fails with:
numpy.linalg.linalg.LinAlgError: 3-dimensional array given. Array must be two-dimensional
What am I doing wrong?
Keeping everything else fixed, changing M1 and M2 to
M1 = np.vstack([l1.T, l2.T])
M2 = np.concatenate([x1, y1])
should do the job.
Your concatenation is an issue and also the parameters for lstsq() have to be transposed.
M1 = np.hstack((l1,l2))
M2 = np.hstack((x1,x2))
ABCD = np.linalg.lstsq(M1.T,M2.T)[0]

Find steady-state of a set of differential equations

Let us assume I have a set of differential equations to be integrated with scipy odeint. Now my goal is to find the steady-state (I chose initial conditions such that this state exists). At the moment I have implemented something like
cond = True
while cond:
x = integrate(interval = [0,t], steps = 200)
if var(x[-22::]) < maxvar:
cond = False
return mean(x)
else:
t*= 2
Do you have a more efficient approach?
If you are using odeint, then you already have your differential equations written as a function f(x, t) (or possibly f(x, t, *args)). If your system is autonomous (i.e. f does not actually depend on t), you can find an equilibrium by solving f(x, 0) == 0 for x. You can use, for example, scipy.optimize.fsolve to solve for the equilibrium.
The following is an example. It uses the "Coupled Spring Mass System" example from the scipy cookbook. scipy.optimize.fsolve is used to find the equilibrium solution x1 = 0.5, y1 = 0, x2 = 1.5, y2 = 0.
from scipy.optimize import fsolve
def vectorfield(w, t, p):
"""
Defines the differential equations for the coupled spring-mass system.
Arguments:
w : vector of the state variables:
w = [x1, y1, x2, y2]
t : time
p : vector of the parameters:
p = [m1, m2, k1, k2, L1, L2, b1, b2]
"""
x1, y1, x2, y2 = w
m1, m2, k1, k2, L1, L2, b1, b2 = p
# Create f = (x1', y1', x2', y2'):
f = [y1,
(-b1 * y1 - k1 * (x1 - L1) + k2 * (x2 - x1 - L2)) / m1,
y2,
(-b2 * y2 - k2 * (x2 - x1 - L2)) / m2]
return f
if __name__ == "__main__":
# Parameter values
# Masses:
m1 = 1.0
m2 = 1.5
# Spring constants
k1 = 8.0
k2 = 40.0
# Natural lengths
L1 = 0.5
L2 = 1.0
# Friction coefficients
b1 = 0.8
b2 = 0.5
# Pack up the parameters and initial conditions:
p = [m1, m2, k1, k2, L1, L2, b1, b2]
# Initial guess to pass to fsolve. The second and fourth components
# are the velocities of the masses, and we know they will be 0 at
# equilibrium. For the positions x1 and x2, we'll try 1 for both.
# A better guess could be obtained by solving the ODEs for some time
# interval, and using the last point of that solution.
w0 = [1.0, 0, 1.0, 0]
# Find the equilibrium
eq = fsolve(vectorfield, w0, args=(0, p))
print "Equilibrium: x1 = {0:.1f} y1 = {1:.1f} x2 = {2:.1f} y2 = {3:.1f}".format(*eq)
The output is:
Equilibrium: x1 = 0.5 y1 = 0.0 x2 = 1.5 y2 = 0.0
Referring to your comments, I don't see any better way:
In this case where you know approximately where the system will settle.
(This is fairly easy to predict in some systems, like a pendulum, or a charging capacitor), and that it will settle, the fastest way I know is to check if
(p[0] * x[i] + p[1] * x[i-1] ... + p[n] * x[i-n] - mean(x[i-0:n]) )< epsilon)
The difficulty is determining the size of epsilon, the parameters p[0:n] to tune this detection.
Clearly you are already using this window method:
epsilon = varmax
p[0:n] = 1
n = 22
and have optimized it by removing the parameters for the filter, and simply using the variance.
For many systems (where settling looks like 1 order differential equations) the filter parameters turn out to look like this:
p[0] = n/2
p[n] = n/2
p[1:n-1] = 0
meaning you can make stuff go faster if you replace the calculation of state variance with this simple test:
if( abs(x[-22] - x[0]) > epsilon)
This will not detect correctly if small disturbances are still present, since we don't know your system, that's difficult to talk about...

Estimating parameter values using optimize.curve.fit

I am attempting to estimate the parameters of the non-linear equation:
y(x1, x2) = x1 / A + Bx1 + Cx2
using the method outlined in the answer to this question, but can find no documentation on how to pass multiple independent variables to the curve_fit function appropriately.
Specifically, I am attempting to estimate plant biomass (y) on the basis of the density of the plant (x1), and the density of a competitor (x2). I have three exponential equations (of the form y = a[1-exp(-b*x1)]) for the the relationship between plant density and plant biomass, with different parameter values for three competitor densities:
For x2 == 146: y = 1697 * [1 - exp(-0.010 * x1)]
For x2 == 112: y = 1994 * [1 - exp(-0.023 * x1)]
For x2 == 127: y = 1022 * [1 - exp(-0.008 * x1)]
I would therefore like to write code along the lines of:
def model_func(self, x_vals, A, B, C):
return x_vals[0] / (A + B * x_vals[0] + C * x_vals[1])
def fit_nonlinear(self, d, y):
opt_parms, parm_cov = sp.optimize.curve_fit(self.model_func, [x1, x2], y, p0 = (0.2, 0.004, 0.007), maxfev=10000)
A, B, C = opt_parms
return A, B, C
However I have not found any documentation on how to format the argument y (passed to fit_nonlinear) to capture to two-dimensional nature of the x_vals (the documentation for curve_fit states y should be an N-length sequence). Is what I am attempting possible with curve_fit?
Based on your comment above, you want to think of using the flat versions of the matrices. If you take the same element from both the X1 and X2 matrices, that pair of values has a corresponding y-value. Here's a minimal example
import numpy as np
import scipy as sp
import scipy.optimize
x1 = np.linspace(-1, 1)
x2 = np.linspace(-1, 1)
X1, X2 = np.meshgrid(x1, x2)
def func(X, A, B, C):
X1, X2 = X
return X1 / (A + B * X1 + C * X2)
# generate some noisy points corresponding to a set of parameter values
p_ref = [0.15, 0.001, 0.05]
Yref = func([X1, X2], *p_ref)
std = Yref.std()
Y = Yref + np.random.normal(scale=0.1 * std, size=Yref.shape)
# fit a curve to the noisy points
p0 = (0.2, 0.004, 0.007)
p, cov = sp.optimize.curve_fit(func, [X1.flat, X2.flat], Y.flat, p0=p0)
# if the parameters from the fit are close to the ones used
# to generate the noisy points, we succeeded
print p_ref
print p

Plane equation for 3D vectors

I want to find a 3D plane equation given 3 points. I have got the normal calculated after applying the cross product. But the equation of a plane is known to be the normal multiply by another vector which what I am taught to be as P.OP. I substitute my main reference point as OP and i want P to be in (x, y, z) form. So that I can get something like e.g,
OP = (1, 2, 3)
I want to get something like that:
(x-1)
(y-2)
(z-3)
May I know how?
Below is my reference code.(Note: plane_point_1_x(), plane_point_1_y(), plane_point_1_z() are all functions asking for the user input of the respective points)
"""
I used Point P as my reference point so I will make use of it in this section
"""
vector_pop_x = int('x') - int(plane_point_1_x())
vector_pop_y = int('y') - int(plane_point_1_y())
vector_pop_z = int('z') - int(plane_point_1_z())
print vector_pop_x, vector_pop_y, vector_pop_z
All the above is what i did, but for some reason it did not work. I think the problem lies in the x, y , z part.
Say you have three known points, each with (x, y, z). For example:
p1 = (1, 2, 3)
p2 = (4, 6, 9)
p3 = (12, 11, 9)
Make them into symbols that are easier to look at for further processing:
x1, y1, z1 = p1
x2, y2, z2 = p2
x3, y3, z3 = p3
Determine two vectors from the points:
v1 = [x3 - x1, y3 - y1, z3 - z1]
v2 = [x2 - x1, y2 - y1, z2 - z1]
Determine the cross product of the two vectors:
cp = [v1[1] * v2[2] - v1[2] * v2[1],
v1[2] * v2[0] - v1[0] * v2[2],
v1[0] * v2[1] - v1[1] * v2[0]]
A plane can be described using a simple equation ax + by + cz = d. The three coefficients from the cross product are a, b and c, and d can be solved by substituting a known point, for example the first:
a, b, c = cp
d = a * x1 + b * y1 + c * z1
Now do something useful, like determine the z value at x=4, y=5. Re-arrange the simple equation, and solve for z:
x = 4
y = 5
z = (d - a * x - b * y) / float(c) # z = 6.176470588235294
If I am not mistaken, one good solution here contains mistypes
vector1 = [x2 - x1, y2 - y1, z2 - z1]
vector2 = [x3 - x1, y3 - y1, z3 - z1]
cross_product = [vector1[1] * vector2[2] - vector1[2] * vector2[1], -1 * (vector1[0] * vector2[2] - vector1[2] * vector2[0]), vector1[0] * vector2[1] - vector1[1] * vector2[0]]
a = cross_product[0]
b = cross_product[1]
c = cross_product[2]
d = - (cross_product[0] * x1 + cross_product[1] * y1 + cross_product[2] * z1)
Tried previous (author's) version, but had to check it. With couple more minuses in formulas seems correct now.
One good way is:
| x1 y1 z2 1 |
| x2 y2 z2 1 |
| x3 y3 z3 1 | = 0
| x y z 1 |
Where the vertical pipes mean the determinant of the matrix, and (x1 y1 z1), (x2 y2 z2), and (x3 y3 z3) are your given points.
Plane implicit Eqn:
All points P = (x, y, z) satisfying
<n, QP> = 0
where
n is the plane normal vector,
Q is some point on the plane (any will do)
QP is the vector from Q to P
<a, b> is the scalar (dot) product operator.
(Remember that QP can be computed as P - Q)
I wish this answer already existed. Coded from http://www.had2know.com/academics/equation-plane-through-3-points.html
Supposing 3 points p1, p2, p3 - consisting of [x1, y1, z1], etc.
vector1 = [x2 - x1, y2 - y1, z2 - z1]
vector2 = [x3 - x1, y3 - y1, z3 - z1]
cross_product = [vector1[1] * vector2[2] - vector1[2] * vector2[1], -1 * vector1[0] * v2[2] - vector1[2] * vector2[0], vector1[0] * vector2[1] - vector1[1] * vector2[0]]
d = cross_product[0] * x1 - cross_product[1] * y1 + cross_product[2] * z1
a = cross_product[0]
b = cross_product[1]
c = cross_product[2]
d = d

Categories