Python: double integration on an infinite domain - python

I am trying to integrate my function over u and xx and then store the values in a matrix so I can plot them with imshow or pcolormesh. The bounds on the integration are 0 < u < inf and -inf < xx < inf. At the moment, I am only taking the bounds to be 10 until I can figure this out.
import numpy as np
import pylab as pl
from scipy.integrate import dblquad
b = 50.0
x = np.linspace(-10, 10, 1000)
y = np.linspace(0, 10, 1000)
T = pl.zeros([len(x), len(y)])
for xi in enumerate(x):
for yi in enumerate(y):
def f(xi, yi, u, xx):
return ((np.exp(u * (b - yi)) - np.exp(-u * (b - yi))) /
(np.exp(u * b) - np.exp(-u * b)) * np.cos(u * (xx - xi)))
def fint(u, xx):
return T + dblquad(f, -10, 10, 0.1, 10, args = (u, xx))[0]
This is the code I have so far but I know it isn't working properly; unfortunately, I don't know what the problem is. Maybe I can't have the two for loops in my definition of f or my my fint is wrong.

It's not completely clear from your question what you're trying to do. But here's how I interpreted it: You have a double integral over two variables u and xx, which also takes two parameters xi and yi. You want to evaluate the integral over xx and u at many different values of xi and yj, and store these values in T. Assuming this is what you want to do (and correct me if I'm wrong), here's how I would do it.
import numpy as np
from scipy.integrate import dblquad
b = 50.0
x = np.linspace(-10, 10, 1000)
y = np.linspace(0, 10, 1000)
def f(xx, u, xi, yj):
return ((np.exp(u * (b - yj)) - np.exp(-u * (b - yj))) /
(np.exp(u * b) - np.exp(-u * b)) * np.cos(u * (xx - xi)))
T = np.zeros([len(x), len(y)])
for i, xi in enumerate(x):
for j, yj in enumerate(y):
T[i, j] += dblquad(
f, -10, 10, lambda x: 0.1, lambda x: 10, args=(xi, yj))[0]

fint is the only thing that calls f. You are not calling fint, which means that f is not being used at all, just defined about a million times. I would consider defining the function just once and calling it a million times instead.

Related

Projectile motion 3d odes

I am trying to solve this set of differential equations with solve_ivp in Python. Winds are horizontal two dimensional vectors, so I need differential equation solved in y dimension.
This is the error I get:
ValueError: not enough values to unpack (expected 6, got 4)
Is there any other way of solving it? Here is my code:(just projectile motion in x-z plane, distance as function of angle). For the example of two such initial angles (with which we hit approximately the same point) I am trying to calculate the dispersion of hits in the wind according to the point hit in no wind.
Thanks for any help or pointers
from random import randrange
from statistics import stdev
from turtle import title
import numpy as np
import scipy as sp
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
m = 30
v0 = 301
g = 9.81
ro = 1.28
A = 0.0026
c = 1
k = 0.5 * c * ro * A
B = k / m
V = v0
c_max = 10
c_windx = 0
c_windy = 0
def dSdt(t, S, B, c_windx, c_windy):
x, vx, y, vy, z, vz = S
speed = np.hypot(np.hypot((vx + c_windx),(vy + c_windy)), vz)
return [vx, -B * speed * (vx + c_windx), vy, -B * speed * (vy + c_windy), vz, -B * speed * vz - g]
def distance(angle, B, c_windx, V=v0, t=600):
v0x = V * np.cos(np.radians(angle))
v0y = 0
v0z = V * np.sin(np.radians(angle))
sol = solve_ivp(dSdt, [0, t], y0 =[0,v0x,0,v0y,0,v0z], t_eval=np.linspace(0,t,10000), args=(B, c_windx,c_windy), atol=1e-7, rtol=1e-4)
justabove = np.where(np.diff(np.sign(sol.y[2])) < 0)[0][0]
justunder = justabove + 1
xloc = (sol.y[0][justabove] + sol.y[0][justunder])/2
return xloc
angles = np.linspace(0, 90, 200)
xlocs = np.vectorize(distance)(angles, B=B, c_windx = c_windx)
plt.plot(angles, xlocs)
plt.xlabel('angles []')
plt.ylabel('distance [m]')
plt.axvline(angles[np.argmax(xlocs)], ls='--', color='r')
plt.text(angles[np.argmax(xlocs)] + 0.2 ,0, round(angles[np.argmax(xlocs)], 2), rotation=90, color='r')
plt.title('Distance(angle)')
plt.show()
alfa = 15 #its optional
def same_distance_alfa(B,c_windx, V=v0, t=600):
for a in np.arange(alfa + 5, 90, 0.1):
if abs(distance(alfa, B, c_windx, V=v0, t=600) - distance(a, B, c_windx, V=v0, t=600) < 10):
print((distance(15, B, c_windx, V=v0, t=600) - distance(a, B, c_windx, V=v0, t=600)))
return a
beta = same_distance_alfa(B)

Solving a boundary value problem DE in python

I am trying to solve the following set of DE's:
dx' = cos(a)
dy' = sin(a)
dF' = - b * x * cos(a) + sin(a)
da' = (b * x * sin(a) + cos(a)) / F
with the conditions:
x(0) = y(0) = x(1) = 0
y(1) = 0.6
F(0) = 0.38
a(0) = -0.5
I tried following a similar problem, but I just can't get it to work. Is it possible, that my F(0) and a(0) are completely off, I am not even sure about them.
import numpy as np
from scipy.integrate import solve_bvp
import matplotlib.pyplot as plt
beta = 5
def fun(x, y):
x, dx, y, dy, F, dF, a, da, = y;
dxds=np.cos(a)
dyds=np.sin(a)
dFds=-beta * x * np.cos(a) + np.sin(a)
dads=(beta * x * np.sin(a) + np.cos(a) ) / F
return dx, dxds, dy, dyds, dF, dFds, da, dads
def bc(ya, yb):
return ya[0], yb[0], ya[2], yb[2] + 0.6, ya[4] + 1, yb[4] + 1, ya[6], yb[6]
x = np.linspace(0, 0.5, 10)
y = np.zeros((8, x.size))
y[4] = 0.38
y[6] = 2.5
res = solve_bvp(fun, bc, x, y)
print(res.message)
x_plot = np.linspace(0, 0.5, 200)
plt.plot(x_plot, res.sol(x_plot)[0])
I think that you have foremost a physics problem, translating the physical situation into an ODE system.
x(s) and y(s) are the coordinates of the rope where s is the length along the rope. Consequently, (x'(s),y'(s)) is a unit vector that is uniquely characterized by its angle a(s), giving
x'(s) = cos(a(s))
y'(s) = sin(a(s))
To get the shape, one now has to consider the mechanics. The assumption seems to be that the rope rotates without spiraling around the rotation axis, staying in one plane. Additionally, from the equilibrium of forces you also get that the other two equations are indeed first order, not second order equations. So your state only has 4 components and the ODE system function thus has to be
def fun(s, u):
x, y, F, a = u;
dxds=np.cos(a)
dyds=np.sin(a)
dFds=-beta * x * np.cos(a) + np.sin(a)
dads=(beta * x * np.sin(a) + np.cos(a) ) / F
return dxds, dyds, dFds, dads
Now there are only 4 boundary condition slots available, which are the coordinates of the start and end of the rope.
def bc(ua, ub):
return ua[0], ub[0], ua[1], ub[1] - 0.6
Additionally, the interval length for s is also the rope length, so a value of 0.5 is impossible for the given coordinates on the pole, try 1.0. There is some experimentation needed to get an initial guess that does not lead to a singular Jacobian in the BVP solver. In the end I get the solution in the x-y plane
with the components

Is there a faster way of repeating a chunk of code x times and taking an average?

Starting with:
a,b=np.ogrid[0:n+1:1,0:n+1:1]
B=np.exp(1j*(np.pi/3)*np.abs(a-b))
B[z,b] = np.exp(1j * (np.pi/3) * np.abs(z - b +x))
B[a,z] = np.exp(1j * (np.pi/3) * np.abs(a - z +x))
B[diag,diag]=1-1j/np.sqrt(3)
this produces an n*n grid that acts as a matrix.
n is just a number chosen to represent the indices, i.e. an a*b matrix where a and b both go up to n.
Where z is a constant I choose to replace a row and column with the B[z,b] and B[a,z] formulas. (Essentially the same formula but with a small number added to the np.abs(a-b))
The diagonal of the matrix is given by the bottom line:
B[diag,diag]=1-1j/np.sqrt(3)
where,
diag=np.arange(n+1)
I would like to repeat this code 50 times where the only thing that changes is x so I will end up with 50 versions of the B np.ogrid. x is a randomly generated number between -0.8 and 0.8 each time.
x=np.random.uniform(-0.8,0.8)
I want to generate 50 versions of B with random values of x each time and take a geometric average of the 50 versions of B using the definition:
def geo_mean(y):
y = np.asarray(y)
return np.prod(y ** (1.0 / y.shape[0]), axis=-1)
I have tried to set B as a function of some index and then use a for _ in range(): loop, this doesn't work. Aside from copy and pasting the block 50 times and denoting each one as B1, B2, B3 etc; I can't think of another way of working this out.
EDIT:
I'm now using part of a given solution in order to show clearly what I am looking for:
#A matrix with 50 random values between -0.8 and 0.8 to be used in the loop
X=np.random.uniform(-0.8,0.8, (50,1))
#constructing the base array before modification by random x values in position z
a,b = np.ogrid[0:n+1:1,0:n+1:1]
B = np.exp(1j * ( np.pi / 3) * np.abs( a - b ))
B[diag,diag] = 1 - 1j / np.sqrt(3)
#list to store all modified arrays
randomarrays = []
for i in range( 0,50 ):
#copy array and modify it
Bnew = np.copy( B )
Bnew[z, b] = np.exp( 1j * ( np.pi / 3 ) * np.abs(z - b + X[i]))
Bnew[a, z] = np.exp( 1j * ( np.pi / 3 ) * np.abs(a - z + X[i]))
randomarrays.append(Bnew)
Bstack = np.dstack(randomarrays)
#calculate the geometric mean value along the axis that was the row in 2D arrays
B0 = geo_mean(Bstack)
From this example, every iteration of i uses the same value of X, I can't seem to get a way to get each new loop of i to use the next value in the matrix X. I am unsure of the ++ action in python, I know it does not work in python, I just don't know how to use the python equivalent. I want a loop to use a value of X, then the next loop to use the next value and so on and so forth so I can dstack all the matrices at the end and find a geo_mean for each element in the stacked matrices.
One pedestrian way would be to use a list comprehension or generator expression:
>>> def f(n, z, x):
... diag = np.arange(n+1)
... a,b=np.ogrid[0:n+1:1,0:n+1:1]
... B=np.exp(1j*(np.pi/3)*np.abs(a-b))
... B[z,b] = np.exp(1j * (np.pi/3) * np.abs(z - b +x))
... B[a,z] = np.exp(1j * (np.pi/3) * np.abs(a - z +x))
... B[diag,diag]=1-1j/np.sqrt(3)
... return B
...
>>> X = np.random.uniform(-0.8, 0.8, (10,))
>>> np.prod((*map(np.power, map(f, 10*(4,), 10*(2,), X), 10 * (1/10,)),), axis=0)
But in your concrete example we can do much better than that;
using the identity exp(a) x exp(b) = exp(a + b) we can convert the geometric mean after exponentiation to an arithmetic mean before exponentition. A bit of care is required because of the multivaluedness of the complex n-th root which occurs in the geometric mean. In the code below we normalize the angles occurring to range -pi, pi so as to always hit the same branch as the n-th root.
Please also note that the geo_mean function you provide is definitely wrong. It fails the basic sanity check that taking the average of copies of the same thing should return the same thing. I've provided a better version. It is still not perfect, but I think there actually is no perfect solution, because of the nonuniqueness of the complex root.
Because of this I recommend taking the average before exponentiating. As long as your random spread is less than pi this allows a well-defined averaging procedure with an average that is actually close to the samples
import numpy as np
def f(n, z, X, do_it_pps_way=True):
X = np.asanyarray(X)
diag = np.arange(n+1)
a,b=np.ogrid[0:n+1:1,0:n+1:1]
B=np.exp(1j*(np.pi/3)*np.abs(a-b))
X = X.reshape(-1,1,1)
if do_it_pps_way:
zbx = np.mean(np.abs(z-b+X), axis=0)
azx = np.mean(np.abs(a-z+X), axis=0)
else:
zbx = np.mean((np.abs(z-b+X)+3) % 6 - 3, axis=0)
azx = np.mean((np.abs(a-z+X)+3) % 6 - 3, axis=0)
B[z,b] = np.exp(1j * (np.pi/3) * zbx)
B[a,z] = np.exp(1j * (np.pi/3) * azx)
B[diag,diag]=1-1j/np.sqrt(3)
return B
def geo_mean(y):
y = np.asarray(y)
dim = len(y.shape)
y = np.atleast_2d(y)
v = np.prod(y, axis=0) ** (1.0 / y.shape[0])
return v[0] if dim == 1 else v
def geo_mean_correct(y):
y = np.asarray(y)
return np.prod(y ** (1.0 / y.shape[0]), axis=0)
# demo that orig geo_mean is wrong
B = np.exp(1j * np.random.random((5, 5)))
# the mean of four times the same thing should be the same thing:
if not np.allclose(B, geo_mean([B, B, B, B])):
print('geo_mean failed')
if np.allclose(B, geo_mean_correct([B, B, B, B])):
print('but geo_mean_correct works')
n, z, m = 10, 3, 50
X = np.random.uniform(-0.8, 0.8, (m,))
B0 = f(n, z, X, do_it_pps_way=False)
B1 = np.prod((*map(np.power, map(f, m*(n,), m*(z,), X), m * (1/m,)),), axis=0)
B2 = geo_mean_correct([f(n, z, x) for x in X])
# This is the recommended way:
B_recommended = f(n, z, X, do_it_pps_way=True)
print()
print(np.allclose(B1, B0))
print(np.allclose(B2, B1))
I think you should rely more on numpy functionality, when approaching your problem. Not a numpy expert myself, so there is surely room for improvement:
from scipy.stats import gmean
n = 2
z = 1
a = np.arange(n + 1).reshape(1, n + 1)
#constructing the base array before modification by random x values in position z
B = np.exp(1j * (np.pi / 3) * np.abs(a - a.T))
B[a, a] = 1 - 1j / np.sqrt(3)
#list to store all modified arrays
random_arrays = []
for _ in range(50):
#generate random x value
x=np.random.uniform(-0.8, 0.8)
#copy array and modify it
B_new = np.copy(B)
B_new[z, a] = np.exp(1j * (np.pi / 3) * np.abs(z - a + x))
B_new[a, z] = np.exp(1j * (np.pi / 3) * np.abs(a - z + x))
random_arrays.append(B_new)
#store all B arrays as a 3D array
B_stack = np.stack(random_arrays)
#calculate the geometric mean value along the axis that was the row in 2D arrays
geom_mean_for_rows = gmean(B_stack, axis = 2)
It uses the geometric mean function from scipy.stats module to have a vectorised approach for this calculation.

Calculate the Fourier series with the trigonometry approach

I try to implement the Fourier series function according to the following formulas:
...where...
...and...
Here is my approach to the problem:
import numpy as np
import pylab as py
# Define "x" range.
x = np.linspace(0, 10, 1000)
# Define "T", i.e functions' period.
T = 2
L = T / 2
# "f(x)" function definition.
def f(x):
return np.sin(np.pi * 1000 * x)
# "a" coefficient calculation.
def a(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for i in np.linspace(a, b, accuracy):
x = a + i * dx
integration += f(x) * np.cos((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# "b" coefficient calculation.
def b(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for i in np.linspace(a, b, accuracy):
x = a + i * dx
integration += f(x) * np.sin((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# Fourier series.
def Sf(x, L, n = 10):
a0 = a(0, L)
sum = 0
for i in np.arange(1, n + 1):
sum += ((a(i, L) * np.cos(n * np.pi * x)) + (b(i, L) * np.sin(n * np.pi * x)))
return (a0 / 2) + sum
# x axis.
py.plot(x, np.zeros(np.size(x)), color = 'black')
# y axis.
py.plot(np.zeros(np.size(x)), x, color = 'black')
# Original signal.
py.plot(x, f(x), linewidth = 1.5, label = 'Signal')
# Approximation signal (Fourier series coefficients).
py.plot(x, Sf(x, L), color = 'red', linewidth = 1.5, label = 'Fourier series')
# Specify x and y axes limits.
py.xlim([0, 10])
py.ylim([-2, 2])
py.legend(loc = 'upper right', fontsize = '10')
py.show()
...and here is what I get after plotting the result:
I've read the How to calculate a Fourier series in Numpy? and I've implemented this approach already. It works great, but it use the expotential method, where I want to focus on trigonometry functions and the rectangular method in case of calculating the integraions for a_{n} and b_{n} coefficients.
Thank you in advance.
UPDATE (SOLVED)
Finally, here is a working example of the code. However, I'll spend more time on it, so if there is anything that can be improved, it will be done.
from __future__ import division
import numpy as np
import pylab as py
# Define "x" range.
x = np.linspace(0, 10, 1000)
# Define "T", i.e functions' period.
T = 2
L = T / 2
# "f(x)" function definition.
def f(x):
return np.sin((np.pi) * x) + np.sin((2 * np.pi) * x) + np.sin((5 * np.pi) * x)
# "a" coefficient calculation.
def a(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.cos((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# "b" coefficient calculation.
def b(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.sin((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# Fourier series.
def Sf(x, L, n = 10):
a0 = a(0, L)
sum = np.zeros(np.size(x))
for i in np.arange(1, n + 1):
sum += ((a(i, L) * np.cos((i * np.pi * x) / L)) + (b(i, L) * np.sin((i * np.pi * x) / L)))
return (a0 / 2) + sum
# x axis.
py.plot(x, np.zeros(np.size(x)), color = 'black')
# y axis.
py.plot(np.zeros(np.size(x)), x, color = 'black')
# Original signal.
py.plot(x, f(x), linewidth = 1.5, label = 'Signal')
# Approximation signal (Fourier series coefficients).
py.plot(x, Sf(x, L), '.', color = 'red', linewidth = 1.5, label = 'Fourier series')
# Specify x and y axes limits.
py.xlim([0, 5])
py.ylim([-2.2, 2.2])
py.legend(loc = 'upper right', fontsize = '10')
py.show()
Consider developing your code in a different way, block by block. You should be surprised if a code like this would work at the first try. Debugging is one option, as #tom10 said. The other option is rapid prototyping the code step by step in the interpreter, even better with ipython.
Above, you are expecting that b_1000 is non-zero, since the input f(x) is a sinusoid with a 1000 in it. You're also expecting that all other coefficients are zero right?
Then you should focus on the function b(n, L, accuracy = 1000) only. Looking at it, 3 things are going wrong. Here are some hints.
the multiplication of dx is within the loop. Sure about that?
in the loop, i is supposed to be an integer right? Is it really an integer? by prototyping or debugging you would discover this
be careful whenever you write (1/L) or a similar expression. If you're using python2.7, you're doing likely wrong. If not, at least use a from __future__ import division at the top of your source. Read this PEP if you don't know what I am talking about.
If you address these 3 points, b() will work. Then think of a in a similar fashion.

Python: Plotting Bessel functions of the first kind with a float argument

The equation I am working with is
$$
E = M_e + \sum_{n = 1}^N\frac{2}{n}\mathcal{J}_n(ne)\sin(nM_e)
$$
where $\mathcal{J}_n(x)$ is the nth Bessel function of the first kind.
As a test, I plotted the first 6 Bessel functions and everything worked out correctly. When I enter the argument of $n * e$, the plot isn't what I anticipated it to be.
import numpy as np
import pylab as py
import scipy.special as sp
x = np.linspace(0, 15, 500000)
for v in range(0, 6):
py.plot(x, sp.jv(v, x))
py.xlim((0, 15))
py.ylim((-0.5, 1.1))
py.legend(('$\mathcal{J}_0(x)$', '$\mathcal{J}_1(x)$', '$\mathcal{J}_2(x)$',
'$\mathcal{J}_3(x)$', '$\mathcal{J}_4(x)$', '$\mathcal{J}_5(x)$'),
loc = 0)
py.xlabel('$x$')
py.ylabel('$\mathcal{J}_n(x)$')
#py.title('Plots of the first six Bessel Functions')
py.grid(True)
#py.savefig('besseln0to6.eps', format = 'eps')
py.show()
e = 0.99
def E(M):
return (M + sum(2.0 / n * sp.jv(n * e, M) * np.sin(n * M)
for n in range(1, 3, 1)))
M = np.linspace(0, 2 * np.pi, 500000)
fig2 = py.figure()
ax2 = fig2.add_subplot(111, aspect = 'equal')
ax2.plot(E(M), M, 'b')
def E2(M):
return (M + sum(2.0 / n * sp.jv(n * e, M) * np.sin(n * M)
for n in range(1, 11, 1)))
ax2.plot(E2(M), M, 'r')
py.xlim((0, 2 * np.pi))
py.ylim((0, 2 * np.pi))
py.xlabel('Eccentric anomaly, $E$')
py.ylabel('Mean anomaly, $M_e$')
py.show()
The plot is supposed to look like for n = 10
The problem is the use of the Bessel function sp.jv(n * e, M) whereas it should be order, argument. That in turn leads to sp.jv(n , n * e) which generates the correct plot.

Categories

Resources