How to make a condition to terminate appending? - python

I am writing a code to plot several projectile trajectories of various theta values in Python.
theta = np.arange(np.pi/6, np.pi/3)
t = np.linspace(0,2,num=100)
while y0>=0:
for i in theta:
x = []
y = []
for k in t:
x0= v_0*np.cos(i)*k
y0= v_0*np.sin(i)*k - 1/2*g*(k**2)
x.append(x0)
x.append(y0)
After forming the arrays and putting in the necessary conditions for projectile, I have used a while loop to put the terminating instruction in the program. I think, I am missing a crucial point. Thanks!

I think you want your terminating condition inside your inner-most loop. See below, where I also defined a couple of missing constants (v_0, g) and fixed one x to y. also printing the results
theta = np.arange(np.pi/6, np.pi/3)
t = np.linspace(0,2,num=100)
v_0 = 1
g=10
for i in theta:
x = []
y = []
for k in t:
x0= v_0*np.cos(i)*k
y0= v_0*np.sin(i)*k - 1/2*g*(k**2)
x.append(x0)
y.append(y0)
if y0 < 0: # the main change here. Stop looping when y_0 below zero
break
print(f'theta:{i}')
print(f'x:{x}')
print(f'y:{y}')
produces
theta:0.5235987755982988
x:[0.0, 0.017495462702715934, 0.03499092540543187, 0.052486388108147805, 0.06998185081086374, 0.08747731351357968]
y:[0.0, 0.008060401999795939, 0.012039587797163551, 0.011937557392102841, 0.007754310784613805, -0.0005101520253035577]
Plotting it (y vs x), looks reasonable
It is also worth noting that your definition of theta = np.arange(np.pi/6, np.pi/3) looks rather strange, what are you trying to achieve here?

Related

Calculating volume of a 2 variable definite integral using the midpoint rule of Riemann Summs

I am trying to approximate the volume of a 2 variable definite integral sin^2(x)-cos^2(y) using while and for loops. I've changed the code quite often and with the most recent change, it broke. I am very new to python so I'm still figuring out how to work with arrays properly.
This is what I have untill now (EDIT: With alani's comment I managed to fix the error, but now I'm not receiving an answer when running the code)
import numpy as np
import scipy.integrate
def f(x,y):
return np.sin(x)**2-np.cos(y)**2
print(scipy.integrate.dblquad(f,0,1,0,2))
def Riemann(x0,xn,y0,yn,N):
e = 1;
while e > 1e-3:
x = np.linspace(0,1,N)
y = np.linspace(0,2,N)
dx = (x0-xn)/N
dy = (y0-yn)/N
for i in range(N):
V = (dx*dy)*(f(x,y))
np.sum(V)
e = abs(1-V)
print(Riemann(0,1,0,2,1000))
When running this code I receive:
(-0.2654480895858587, 9.090239973208559e-15)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-9-c654507b2f73> in <module>
19 np.sum(V)
20 e = abs(1-V)
---> 21 print(Riemann(0,1,0,2,10))
22
23
<ipython-input-9-c654507b2f73> in Riemann(x0, xn, y0, yn, N)
10 def Riemann(x0,xn,y0,yn,N):
11 e = 1;
---> 12 while e > 1e-3:
13 x = np.linspace(0,1,N)
14 y = np.linspace(0,2,N)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Your code has multiple issues, I'll address them so that you can improve. First off all, the formatting is pretty terrible. Put spaces after commas, separate things more with white-space, etc. You can see the difference in my code, and I'm by no means an expert at code formatting.
Second, your method is not doing what you think it's doing. Every time you iterate i, you create an entire array of values and you assign it to V, since x and y are both arrays. Neither x nor y are being updated here. The loop does the same thing every time, and V get re-assigned the same value every time. np.sum(V) never gets assigned anywhere, so the only thing getting updated at all in that loop is e. Of course, that bit is incorrect since you cannot subtract a vector from a scalar, since, as I wrote above, V is a vector.
Your function didn't use x0, y0, etc. for your bounds of integration, since your linspaces were hardcoded.
Now we come to the solution. There are two approaches to this problem. There's the "slow" pure Python way, where we just loop over our y's and x's and take function values multiplied by the base dx * dy. That version looks like this:
# This a naive version, using two for loops. It's very slow.
def Riemann(x0, xn, y0, yn, N):
xs = np.linspace(x0, xn, N)
ys = np.linspace(y0, yn, N)
dx = (x0 - xn) / N
dy = (y0 - yn) / N
V = 0
for y in ys:
for x in xs:
V += f(x, y)
return dx * dy * V
Note I moved the multiplication outside to save some on performance.
The other way is to use numpy, that version looks like this:
def Riemann(x0, xn, y0, yn, N):
points = itertools.product(np.linspace(x0, xn, N), np.linspace(y0, yn, N))
points = np.array(list(points))
xs = points[:, 0]
ys = points[:, 1]
dx = (x0 - xn) / N
dy = (y0 - yn) / N
return dx * dy * np.sum(f(xs, ys))
Here we avoid the double for-loop. Note that you must include import itertools for this to work. Here we use the Cartesian product to create all points we wish to evaluate, and then give those points to your function f which is designed to work with numpy arrays. We get a vector back from f of all the function values at each point, and we simply just sum all the elements, just like we did in the for-loop. Then we can multiply by the common base dx * dy and return that.
The only thing I do not understand about your code is what you want e to do, and how it relates to N. I'm guessing it's some sort of error tolerance, but why you were trying to subtract the total volume so far (even if your code did nothing of the sort) from 1 I can't understand.

Bifurcation diagram python - no image?

I'm trying to make a bifurcation diagram for the following iterated map:
x_n+1 = x_n * e^(r(1-x_n)).
First I defined the map:
def newmap(x,r):
return x*math.exp(r*(1-x))
Then I tried this:
def bifurcation_diagram(rmin=0, rmax=4, r_N=2000, N_min=4000, N = 1000):
rspace = np.linspace(rmin, rmax, r_N)
x = 0
rset = []
xset = []
for r in rspace:
for i in range(N_min + N):
x = newmap(x,r)
if i > N_min:
rset.append(r)
xset.append(x)
plt.figure(figsize=(16,7))
plt.xlim((rmin,rmax))
plt.ylim((0,5))
plt.scatter(rset,xset,s=0.3,c='C0',linewidth=0)
plt.xlabel(r'r', fontsize=20)
plt.ylabel(r'$x_{end}$', fontsize=29, rotation=0)
plt.show()
When I try bifurcation_diagram() I get a blank plot.
I'm not sure where I'm going wrong here.
The problem is that x=0 is a fixed point and so is x=1. If you switch x=0 to x=0.1 but otherwise leave it where it is, the first r value drives x to (what is for those values) the attracting fixed point 1. You need to put x=0.1 inside of the main loop:
for r in rspace:
x = 0.01
(with everything else as before).

Is there a way to easily integrate a set of differential equations over a full grid of points?

The problem is that I would like to be able to integrate the differential equations starting for each point of the grid at once instead of having to loop over the scipy integrator for each coordinate. (I'm sure there's an easy way)
As background for the code I'm trying to solve the trajectories of a Couette flux alternating the direction of the velocity each certain period, that is a well known dynamical system that produces chaos. I don't think the rest of the code really matters as the part of the integration with scipy and my usage of the meshgrid function of numpy.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, writers
from scipy.integrate import solve_ivp
start_T = 100
L = 1
V = 1
total_run_time = 10*3
grid_points = 10
T_list = np.arange(start_T, 1, -1)
x = np.linspace(0, L, grid_points)
y = np.linspace(0, L, grid_points)
X, Y = np.meshgrid(x, y)
condition = True
totals = np.zeros((start_T, total_run_time, 2))
alphas = np.zeros(start_T)
i = 0
for T in T_list:
alphas[i] = L / (V * T)
solution = np.array([X, Y])
for steps in range(int(total_run_time/T)):
t = steps*T
if condition:
def eq(t, x):
return V * np.sin(2 * np.pi * x[1] / L), 0.0
condition = False
else:
def eq(t, x):
return 0.0, V * np.sin(2 * np.pi * x[1] / L)
condition = True
time_steps = np.arange(t, t + T)
xt = solve_ivp(eq, time_steps, solution)
solution = np.array([xt.y[0], xt.y[1]])
totals[i][t: t + T][0] = solution[0]
totals[i][t: t + T][1] = solution[1]
i += 1
np.save('alphas.npy', alphas)
np.save('totals.npy', totals)
The error given is :
ValueError: y0 must be 1-dimensional.
And it comes from the 'solve_ivp' function of scipy because it doesn't accept the format of the numpy function meshgrid. I know I could run some loops and get over it but I'm assuming there must be a 'good' way to do it using numpy and scipy. I accept advice for the rest of the code too.
Yes, you can do that, in several variants. The question remains if it is advisable.
To implement a generally usable ODE integrator, it needs to be abstracted from the models. Most implementations do that by having the state space a flat-array vector space, some allow a vector space engine to be passed as parameter, so that structured vector spaces can be used. The scipy integrators are not of this type.
So you need to translate the states to flat vectors for the integrator, and back to the structured state for the model.
def encode(X,Y): return np.concatenate([X.flatten(),Y.flatten()])
def decode(U): return U.reshape([2,grid_points,grid_points])
Then you can implement the ODE function as
def eq(t,U):
X,Y = decode(U)
Vec = V * np.sin(2 * np.pi * x[1] / L)
if int(t/T)%2==0:
return encode(Vec, np.zeros(Vec.shape))
else:
return encode(np.zeros(Vec.shape), Vec)
with initial value
U0 = encode(X,Y)
Then this can be directly integrated over the whole time span.
Why this might be not such a good idea: Thinking of each grid point and its trajectory separately, each trajectory has its own sequence of adapted time steps for the given error level. In integrating all simultaneously, the adapted step size is the minimum over all trajectories at the given time. Thus while the individual trajectories might have only short intervals with very small step sizes amid long intervals with sparse time steps, these can overlap in the ensemble to result in very small step sizes everywhere.
If you go beyond the testing stage, switch to a more compiled solver implementation, odeint is a Fortran code with wrappers, so half a solution. JITcode translates to C code and links with the compiled solver behind odeint. Leaving python you get sundials, the diffeq module of julia-lang, or boost::odeint.
TL;DR
I don't think you can "integrate the differential equations starting for each point of the grid at once".
MWE
Please try to provide a MWE to reproduce your problem, like you said : "I don't think the rest of the code really matters", and it makes it harder for people to understand your problem.
Understanding how to talk to the solver
Before answering your question, there are several things that seem to be misunderstood :
by defining time_steps = np.arange(t, t + T) and then calling solve_ivp(eq, time_steps, solution) : the second argument of solve_ivp is the time span you want the solution for, ie, the "start" and "stop" time as a 2-uple. Here your time_steps is 30-long (for the first loop), so I would probably replace it by (t, t+T). Look for t_span in the doc.
from what I understand, it seems like you want to control each iteration of the numerical resolution : that's not how solve_ivp works. More over, I think you want to switch the function "eq" at each iteration. Since you have to pass the "the right hand side" of the equation, you need to wrap this behavior inside a function. It would not work (see right after) but in terms of concept something like this:
def RHS(t, x):
# unwrap your variables, condition is like an additional variable of your problem,
# with a very simple differential equation
x0, x1, condition = x
# compute new results for x0 and x1
if condition:
x0_out, x1_out = V * np.sin(2 * np.pi * x[1] / L), 0.0
else:
x0_out, x1_out = 0.0, V * np.sin(2 * np.pi * x[1] / L)
# compute new result for condition
condition_out = not(condition)
return [x0_out, x1_out, condition_out]
This would not work because the evolution of condition doesn't satisfy some mathematical properties of derivation/continuity. So condition is like a boolean switch that parametrizes the model, we can use global to control the state of this boolean :
condition = True
def RHS_eq(t, y):
global condition
x0, x1 = y
# compute new results for x0 and x1
if condition:
x0_out, x1_out = V * np.sin(2 * np.pi * x1 / L), 0.0
else:
x0_out, x1_out = 0.0, V * np.sin(2 * np.pi * x1 / L)
# update condition
condition = 0 if condition==1 else 1
return [x0_out, x1_out]
finaly, and this is the ValueError you mentionned in your post : you define solution = np.array([X, Y]) which actually is initial condition and supposed to be "y0: array_like, shape (n,)" where n is the number of variable of the problem (in the case of [x0_out, x1_out] that would be 2)
A MWE for a single initial condition
All that being said, lets start with a simple MWE for a single starting point (0.5,0.5), so we have a clear view of how to use the solver :
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# initial conditions for x0, x1, and condition
initial = [0.5, 0.5]
condition = True
# time span
t_span = (0, 100)
# constants
V = 1
L = 1
# define the "model", ie the set of equations of t
def RHS_eq(t, y):
global condition
x0, x1 = y
# compute new results for x0 and x1
if condition:
x0_out, x1_out = V * np.sin(2 * np.pi * x1 / L), 0.0
else:
x0_out, x1_out = 0.0, V * np.sin(2 * np.pi * x1 / L)
# update condition
condition = 0 if condition==1 else 1
return [x0_out, x1_out]
solution = solve_ivp(RHS_eq, # Right Hand Side of the equation(s)
t_span, # time span, a 2-uple
initial, # initial conditions
)
fig, ax = plt.subplots()
ax.plot(solution.t,
solution.y[0],
label="x0")
ax.plot(solution.t,
solution.y[1],
label="x1")
ax.legend()
Final answer
Now, what we want is to do the exact same thing but for various initial conditions, and from what I understand, we can't : again, quoting the doc
y0 : array_like, shape (n,) : Initial state. . The solver's initial condition only allows one starting point vector.
So to answer the initial question : I don't think you can "integrate the differential equations starting for each point of the grid at once".

Find two zeros of a function with Python

I have a function f(x) which I know has two zeros within an interval and I need to compute both x values for wich the function cross 0.
I usually use
import scipy.optimize as opt
opt.brentq(f, xmin, xmax)
But the problem is this method is working if the function has one 0 in the interval, and it is not very simple to know where to divide in two parts.
The function is also time costly to evaluate...
I think a good approach would be to pre-process the search of the zeros by sampling f before searching for the zeros. During that pre-process, you evaluate f to detect if the sign of the function has changed.
def preprocess(f,xmin,xmax,step):
first_sign = f(xmin) > 0 # True if f(xmin) > 0, otherwise False
x = xmin + step
while x <= xmax: # This loop detects when the function changes its sign
fstep = f(x)
if first_sign and fstep < 0:
return x
elif not(first_sign) and fstep > 0:
return x
x += step
return x # If you ever reach here, that means that there isn't a zero in the function !!!
With this function , you can separate your initial interval in several smaller intervals. For example :
import scipy.optimize as opt
step = ...
xmid = preprocess(f,xmin,max,step)
z0 = opt.brentq(f,xmin,xmid)
z1 = opt.brentq(f,xmid,xmax)
Depending of the functions f you use, you may need to separate your interval in more than two sub-intervals. Just iterates through [xmin,xmax] like this :
x_list = []
x = x_min
while x < xmax: # This discovers when f changes its sign
x_list.append(x)
x = preprocess(f,x,xmax,step)
x_list.append(xmax)
z_list = []
for i in range(len(x_list) - 1):
z_list.append(opt.brentq(f,x_list[i],x_list[i + 1]))
In the end, z_list contains all the zeros in the given interval [xmin,xmax].
Keep in mind that this algorithm is time-consuming but will do the job.

ODEs with infinite initlal condition in python

I have a second order differential equation that I want to solve it in python. The problem is that for one of the variables I don't have the initial condition in 0 but only the value at infinity. Can one tell me what parameters I should provide for scipy.integrate.odeint ? Can it be solved?
Equation:
Theta needs to be found in terms of time. Its first derivative is equal to zero at t=0. theta is not known at t=0 but it goes to zero at sufficiently large time. all the rest is known. As an approximate I can be set to zero, thus removing the second order derivative which should make the problem easier.
This is far from being a full answer, but is posted here on the OP's request.
The method I described in the comment is what is known as a shooting method, that allows converting a boundary value problem into an initial value problem. For convenience, I am going to rename your function theta as y. To solve your equation numerically, you would first turn it into a first order system, using two auxiliary function, z1 = y and z2 = y', and so your current equation
I y'' + g y' + k y = f(y, t)
would be rewitten as the system
z1' = z2
z2' = f(z1, t) - g z2 - k z1
and your boundary conditions are
z1(inf) = 0
z2(0) = 0
So first we set up the function to compute the derivative of your new vectorial function:
def deriv(z, t) :
return np.array([z[1],
f(z[0], t) - g * z[1] - k * z[0]])
If we had a condition z1[0] = a we could solve this numerically between t = 0 and t = 1000, and get the value of y at the last time as something like
def y_at_inf(a) :
return scipy.integrate.odeint(deriv, np.array([a, 0]),
np.linspace(0, 1000, 10000))[0][-1, 0]
So now all we need to know is what value of a makes y = 0 at t = 1000, our poor man's infinity, with
a = scipy.optimize.root(y_at_inf, [1])

Categories

Resources