Python Solving 1D wave equation and animating - python

I adapted a code for solving 1d wave equation. My problem is animating the time dependent results. I tried it with the following code, any idea what I made wrong? I used the animation package, but somehiw it always says that more than one element is ambigous...
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
import matplotlib.animation as animation
#Set Helvetica Font
rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})
## for Palatino and other serif fonts use:
#rc('font',**{'family':'serif','serif':['Palatino']})
rc('text', usetex=True)
class wave1d(object):
def __init__(self,width,T,nx,nt,c):
self.x = np.linspace(-0.5*width,0.5*width,nx)
self.t = np.linspace(0,T,nt+1)
self.dx = self.x[1]-self.x[0]
self.dt = self.t[1]-self.t[0]
self.xx = self.x
# Gamma_x squared
self.gx2 = c*self.dt/self.dx
# 2*(1-gamma_x^2-gamma_y^2)
self.gamma = 2*(1 - self.gx2)
def solve(self,ffun,gfun):
f = ffun(self.xx)
g = gfun(self.xx)
u = np.zeros((nx,nt+1))
# Set initial condition
u[:,0] = f
""" Compute first time step """
u[:,1] = 0.5*self.gamma*f+self.dt*g
u[1:-1,1] += 0.5*self.gx2*(f[2:]+f[:-2])
for k in range(1,nt):
# Every point contains these terms
u[:,k+1] = self.gamma*u[:,k] - u[:,k-1]
# Interior points
u[1:-1,k+1] += self.gx2*(u[2:,k]+u[:-2,k])
# Right boundary
u[-1,k+1] += 2*self.gx2*u[-2,k]
# Left boundary
u[0,k+1] += 2*self.gx2*u[1,k]
return u
if __name__ == '__main__':
# Final time
T = 2
# Domain dimensions
width = 8
# Wave speed
c = 1
# Number of time steps
nt = 400
# Grid points in x direction
nx = 500
wave_eq = wave1d(width,T,nx,nt,c)
# Initial value functions
f = lambda x: np.sin(x-width)
g = lambda x: 0
u = wave_eq.solve(f,g)
x = wave_eq.x
frames = []
fig = plt.figure(1,(16,8))
for k in range(nt+1):
frame = plt.show(u[:,k])
frames.append([frame])
ani = animation.ArtistAnimation(fig,frames,interval=50, blit=True,repeat_delay=1000)
# ani.save('wave1d.mp4',dpi=300)
plt.show()

Related

plot decision boundary in python

I did a logistic regression on my data and now I find the best Theta array to find the class of a new data.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def h_theta(x,theta):
return np.dot(x,np.transpose(theta))
def g_z(x,theta):
return 1/(1+pow(np.e,-h_theta(x,theta)))
def cost_function(x,y,theta):
cost = 0
for i in range(len(y)):
l = np.log(g_z(x[i],theta))
cost += -y[i]*l -(1-y[i])*np.log((1-(g_z(x[i],theta))))
return cost/(2*len(y))
def updata_theta(x,y,theta,alpha):
for i in range(6):
u = 0
for j in range(len(y)):
u += (h_theta(x[j],theta)-y[j])*x[j,i]
theta[0,i] -= alpha*u/(len(y))
data = pd.read_csv("D:\REZA\programming\machine learning-andrew ng\coding\machine-learning-ex2\ex2\ex2data2.csv")
y = np.array(data["1"])
s = np.array(data.drop("1",axis=1))
x1T2 = np.zeros((117,1))
x2T2 = np.zeros((117,1))
x1x2 = np.zeros((117,1))
one = np.ones((117,1))
m = len(y)
for i in range(m):
x1T2[i] = s[i,0]*s[i,0]
x2T2[i] = s[i,1]*s[i,1]
x1x2[i] = s[i,0]*s[i,1]
x = np.append(one,s,axis=1)
f = np.append(x1T2,x2T2,axis=1)
f = np.append(f,x1x2,axis=1)
x = np.append(x,f,axis=1)
x = np.array(x,dtype=np.float)
theta = np.zeros((1,6),dtype=float)
n=0
alpha = 0.003
while(n<100 and cost_function(x,y,theta)>0.01):
updata_theta(x,y,theta,alpha)
n+=1
I can plot my data with plt.scatter
plt.scatter(x[:,1],x[:,2],c=y)
plt.show()
scatter plot output
Now I want to plot decision boundary using this theta array, but I don't know how to do it.

Big numbers problem while "odeint" numerical integration

I'm having some computational problems with the following code:
import numpy as np
from numpy import arange
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from scipy.integrate import quad
import matplotlib as mpl
mpl.rcParams['agg.path.chunksize'] = 10000
# parameters
Ms = 100 #GeV Singlet Mass
Me = 0.511e-3 #Gev Electron Mass
Mp = 1.22e19 #GeV Planck Mass
gs = 106.75 # Entropy dof
H0 = 2.133*(0.7)*1e-42 # GeV Hubble parameter (unused)
gx = 2 # WIMP's dof
g = 100 # total dof
sigmav=[1e-25,1e-11,1e-12] # cross section's order of magnitude
xi=1e-2
xe=1e2
npts=int(1e5)
x = np.linspace(xi, xe, npts)
def fMB(p,x,m):
return np.exp(-x*np.sqrt(1+p*p/(m*m)))*p*p
def neq(x,m):
return (gx/(2*np.pi*np.pi))*quad(fMB, 0, np.inf, args=(x,m))[0]
def neq_nr(x,m):
return 2*(m**2/(2*np.pi*x))**(3/2)*np.exp(-x)
def stot(x):
return (2*np.pi*np.pi/45)*gs*Ms*Ms*Ms/(x*x*x)
def Yeq(x,m):
return neq(x,m)/stot(x)
Yeq2=np.vectorize(Yeq)
def Yeq_nr(x):
return 0.145*(gx/gs)*(x)**(3/2)*np.exp(-x)
def Yeq_r(x):
return 0.278*(3*gx/4)/gs
def Ytot(x):
if np.any(x<=1):
return Yeq_r(x)
else:
return Yeq_nr(x)
def eqd(yl,x,Ms,σv):
'''
Ms [GeV] : Singlet Mass
σv: [1/GeV^2] : ⟨σv⟩
'''
H = 1.67*g**(1/2)*Ms**2/Mp
dyl = -neq(x,Ms)*σv*(yl**2-Yeq(x,Ms)**2)/(x**(-2)*H*x*Yeq(x,Ms)) #occorre ancora dividere per Yeq_nr(x) oppure Yeq(x)
return dyl
y0=1e-15
yl0 = odeint( eqd, y0, x,args=(Ms,sigmav[0]), full_output=True)
yl1 = odeint( eqd, y0, x,args=(Ms,sigmav[1]), full_output=True)
yl2 = odeint( eqd, y0, x,args=(Ms,sigmav[2]), full_output=True)
fig = plt.figure(figsize=(11,8))
plt.loglog(x,yl0[0], label = r'$\langle σ v\rangle = %s {\rm GeV}^{-2}$'%(sigmav[0]))
plt.loglog(x,yl1[0], label = r'$\langle σ v\rangle = %s {\rm GeV}^{-2}$'%(sigmav[1]))
plt.loglog(x,yl2[0], label = r'$\langle σ v\rangle = %s {\rm GeV}^{-2}$'%(sigmav[2]))
plt.loglog(x,Yeq_nr(x), '--', label = '$Y_{EQ}^{nr}$')
plt.loglog(x,Yeq2(x,Ms), '--', label = '$Y_{EQ}$')
plt.ylim(ymax=0.1,ymin=y0)
plt.xlim(xmax=xe,xmin=xi)
plt.xlabel('$x = m_χ/T$', size= 15)
plt.ylabel('$Y$', size= 15)
plt.title('$m_χ = %s$ GeV'%(Ms), size= 15)
plt.legend(loc='best',fontsize=12)
plt.grid(True)
plt.savefig('abundance.jpg',bbox_inches='tight', dpi=150)
In particular, as soon as I use little values of sigmav (ranging from 10^-12 to 10^-25) the solution is well displayed, but making use of bigger values (starting from 10^-11) I obtain problems and I guess is a order of magnitudes problem, but I don't know how to handle it!
Thanks to everyone!
Edit 1:
I'm uploading a plot making use of three different values of sigmav and as you may see the bigger one (1e-10) is showing (I guess) precision problems plot_1

Having problems with scipy.integrate.solve_ivp

I am trying to use scipy.integrate.solve_ivp to calculate the solutions to newton's gravitation equation for my n body simulation, however I am confused how the function is passed into solve_ivp. I have the following code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
G = 6.67408e-11
m_sun = 1988500e24
m_jupiter = 1898.13e24
m_earth = 5.97219e24
au = 149597870.700e3
v_factor = 1731460
year = 31557600.e0
init_s = np.array([-6.534087946884256E-03*au, 6.100454846284101E-03*au, 1.019968145073305E-04*au, -6.938967653087248E-06*v_factor, -5.599052606952444E-06*v_factor, 2.173251724105919E-07*v_factor])
init_j = np.array([2.932487231769548E+00*au, -4.163444383137574E+00*au, -4.833604407653648E-02*au, 6.076788230491844E-03*v_factor, 4.702729516645153E-03*v_factor, -1.554436340872727E-04*v_factor])
variables_s = init_s
variables_j = init_j
N = 2
tStart = 0e0
t_End = 25*year
Nt = 2000
dt = t_End/Nt
temp_end = dt
t=tStart
domain = (t, temp_end)
planetsinit = np.vstack((init_s, init_j))
planetspos = planetsinit[:,0:3]
mass = np.vstack((1988500e24, 1898.13e24))
def weird_division(n, d):
return n / d if d else 0
variables_save = np.zeros((N,6,Nt))
variables_save[:,:,0] = planetsinit
pos_s = planetspos[0]
pos_j = planetspos[1]
while t < t_End:
t_index = int(weird_division(t, dt))
for index in range(len(planetspos)):
for otherindex in range(len(planetspos)):
if index != otherindex:
x1_p1, x2_p1, x3_p1 = planetsinit[index, 0:3]
x1_p2, x2_p2, x3_p2 = planetsinit[otherindex, 0:3]
m = mass[otherindex]
def f_grav(t, y):
x1_p1, x2_p1, x3_p1, v1_p1, v2_p1, v3_p1 = y
x1_diff = x1_p1 - x1_p2
x2_diff = x2_p1 - x2_p2
x3_diff = x3_p1 - x3_p2
dydt = [v1_p1,
v2_p1,
v3_p1,
-(x1_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2),
-(x2_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2),
-(x3_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2)]
return dydt
solution = solve_ivp(fun=f_grav, t_span=domain, y0=planetsinit[index])
planetsinit[index] = solution['y'][0:6, -1]
variables_save[index,:,t_index] = solution['y'][0:6, -1]
planetspos[index] = planetsinit[index][0:3]
t += dt
temp_end += dt
domain = (t,temp_end)
pos_s = variables_save[0,0:3,:]
pos_j = variables_save[1,0:3,:]
plt.plot(variables_save[0,0:3,:][0], variables_save[0,0:3,:][1])
plt.plot(variables_save[1,0:3,:][0], variables_save[1,0:3,:][1])
The code above works very nicely and produces a stable orbit. However when I calculate the acceleration outside the function and feed that through into the f_grav function, something goes wrong and produces an orbit which is no longer stable. However I am perplexed as I don't know why the data is different as to be it seems like that I have passed through the exactly same inputs. Which leads me to think that maybe its the way the the function f_grav is interpolated by the solve_ivp integrator? To calculate the acceleration outside all I do is change the following code in the loop to:
x1_p1, x2_p1, x3_p1 = planetsinit[index, 0:3]
x1_p2, x2_p2, x3_p2 = planetsinit[otherindex, 0:3]
m = mass[otherindex]
x1_diff = x1_p1 - x1_p2
x2_diff = x2_p1 - x2_p2
x3_diff = x3_p1 - x3_p2
ax = -(x1_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2)
ay = -(x2_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2)
az = -(x3_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2)
def f_grav(t, y):
x1_p1, x2_p1, x3_p1, v1_p1, v2_p1, v3_p1 = y
dydt = [v1_p1,
v2_p1,
v3_p1,
ax,
ay,
az]
return dydt
solution = solve_ivp(fun=f_grav, t_span=domain, y0=planetsinit[index])
planetsinit[index] = solution['y'][0:6, -1]
variables_save[index,:,t_index] = solution['y'][0:6, -1]
planetspos[index] = planetsinit[index][0:3]
As I said I don't know why different orbits are produces which are shown below and any hints as to why or how to solve it would me much appreciated. To clarify why I can't use the working code as it is, as when more bodies are involved I aim to sum the accelerations contribution of all the other planets which isn't possible this way where the acceleration is calculated in the function itself.
Sorry for the large coding chunks but I did feel it was appropriate as then it could be run and the problem itself is clearer.
Both have the same time period, dt, however the orbit on the left is stable and the one on the right is not

Matplotlib FuncAnimation not displaying any frames until animation is complete

I'm trying to animate a plot using matplotlib's FuncAnimation, however no frames of the animation are visible until the animation reaches the final frame. If I set repeat = True nothing is ever displayed. When I first run the code a matplotlib icon appears but nothing displays when I click on it until it shows me the final frame:
If I save the animation I see the animation display correctly so this leads me to think that my code is mostly correct so I hope this is a simple fix that I'm just missing.
Apologies if I'm dumping too much code but I'm not sure if there's anything that's not needed for the minimum reproducible example.
Here's the main code
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from quantum_custom.constants import spin_down, spin_up, H00, H11, H
import quantum_custom.walk as walk
class QuantumState:
def __init__(self, state):
self.state = state
#"coin flips"
max_N = 100 #this will be the final number of coin flips
positions = 2*max_N + 1
#initial conditions
initial_spin = spin_down
initial_position = np.zeros(positions)
initial_position[max_N] = 1
initial_state = np.kron(np.matmul(H, initial_spin), initial_position) #initial state is Hadamard acting on intial state, tensor product with the initial position
quantum_state = QuantumState(initial_state)
#plot the graph
fig, ax = plt.subplots()
plt.title("N = 0")
x = np.arange(positions)
line, = ax.plot([],[])
loc = range(0, positions, positions // 10)
plt.xticks(loc)
plt.xlim(0, positions)
plt.ylim((0, 1))
ax.set_xticklabels(range(-max_N, max_N + 1, positions // 10))
ax.set_xlabel("x")
ax.set_ylabel("Probability")
def init():
line.set_data([],[])
return line,
def update(N):
next_state = walk.flip_once(quantum_state.state, max_N)
probs = walk.get_prob(next_state, max_N)
quantum_state.state = next_state
start_index = N % 2 + 1
cleaned_probs = probs[start_index::2]
cleaned_x = x[start_index::2]
line.set_data(cleaned_x, cleaned_probs)
if cleaned_probs.max() != 0:
plt.ylim((0, cleaned_probs.max()))
plt.title(f"N = {N}")
return line,
anim = animation.FuncAnimation(
fig,
update,
frames = max_N + 1,
init_func = init,
interval = 20,
repeat = False,
blit = True,
)
anim.save("animated.gif", writer = "ffmpeg", fps = 15)
plt.show()
Here's my quantum_custom.constants module.
#define spin up and spin down vectors as standard basis
spin_up = np.array([1,0])
spin_down = np.array([0,1])
#define our Hadamard operator, H, in terms of ith, jth entries, Hij
H00 = np.outer(spin_up, spin_up)
H01 = np.outer(spin_up, spin_down)
H10 = np.outer(spin_down, spin_up)
H11 = np.outer(spin_down, spin_down)
H = (H00 + H01 + H10 - H11)/np.sqrt(2.0) #matrix representation of Hadamard gate in standard basis
Here's my quantum_custom.walk module.
import numpy as np
from quantum_custom.constants import H00, H11, H
#define walk operators
def walk_operator(max_N):
position_count = 2 * max_N + 1
shift_plus = np.roll(np.eye(position_count), 1, axis = 0)
shift_minus = np.roll(np.eye(position_count), -1, axis = 0)
step_operator = np.kron(H00, shift_plus) + np.kron(H11, shift_minus)
return step_operator.dot(np.kron(H, np.eye(position_count)))
def flip_once(state, max_N):
"""
Flips the Hadamard coin once and acts on the given state appropriately.
Returns the state after the Hadamard coin flip.
"""
walk_op = walk_operator(max_N)
next_state = walk_op.dot(state)
return next_state
def get_prob(state, max_N):
"""
For the given state, calculates the probability of being in any possible position.
Returns an array of probabilities.
"""
position_count = 2 * max_N + 1
prob = np.empty(position_count)
for k in range(position_count):
posn = np.zeros(position_count)
posn[k] = 1
posn_outer = np.outer(posn, posn)
alt_measurement_k = eye_kron(2, posn_outer)
proj = alt_measurement_k.dot(state)
prob[k] = proj.dot(proj.conjugate()).real
return prob
def eye_kron(eye_dim, mat):
"""
Speeds up the calculation of the tensor product of an identity matrix of dimension eye_dim with a given matrix.
This exploits the fact that majority of values in the resulting matrix will be zeroes apart from on the leading diagonal where we simply have copies of the given matrix.
Returns a matrix.
"""
mat_dim = len(mat)
result_dim = eye_dim * mat_dim #dimension of the resulting matrix
result = np.zeros((result_dim, result_dim))
result[0:mat_dim, 0:mat_dim] = mat
result[mat_dim:result_dim, mat_dim:result_dim] = mat
return result
I know that saving the animation is a solution but I'd really like to have the plot display just from running the code as opposed to having to save it. Thanks!
As per Sameeresque's suggestion I tried using different backends for matplot lib. This was done by altering by import statements as follows.
import matplotlib
matplotlib.use("tkagg")
import matplotlib.pyplot as plt
Note that it's important to add the two additional lines before import matplotlib.pyplot as plt otherwise it won't do anything.

Python: Numerov's method plotting error

I'm trying to solve the Schrödinger equation with the Numerov's method. Here is my code:
from pylab import *
from scipy.optimize import brentq
import numpy as np
l = float(input("Angular momentum l:"))
L = float(input("Width of the potential:"))
Vo = float(input("Value of the potential:"))
N = int(input("Number of steps (~10000):"))
h = float(3*L/N)
psi = np.zeros(N) #wave function
psi[0] = 0
psi[1] = h
def V(x,E):
"""
Effective potential function.
"""
if x > L:
return -2*E+l*(l+1)/x**2
else:
return -2*(Vo+E)+l*(l+1)/x**2
def Wavefunction(energy):
"""
Calculates wave function psi for the given value
of energy E and returns value at point xmax
"""
global psi
global E
E=energy
for i in range(2,N):
psi[i]=(2*(1+5*(h**2)*V(i*h,E)/12)*psi[i-1]-(1-(h**2)*V((i-1)*h,E)/12)*psi[i-2])/(1-(h**2)*V((i+1)*h,E)/12)
return psi[-1]
def find_energy_levels(x,y):
"""
Gives all zeroes in y = psi_max, x=en
"""
zeroes = []
s = np.sign(y)
for i in range(len(y)-1):
if s[i]+s[i+1] == 0: #sign change
zero = brentq(Wavefunction, x[i], x[i+1])
zeroes.append(zero)
return zeroes
def main():
energies = np.linspace(-Vo,0,int(10*Vo)) # vector of energies where we look for the stable states
psi_max = [] # vector of wave function at x = 3L for all of the energies in energies
for energy in energies:
psi_max.append(Wavefunction(energy)) # for each energy find the the psi_max at xmax
E_levels = find_energy_levels(energies,psi_max) # now find the energies where psi_max = 0
print ("Energies for the bound states are: ")
for E in E_levels:
print ("%.2f" %E)
# Plot the wavefunctions for first 4 eigenstates
x = np.linspace(0, 3*L, N)
figure()
for E in E_levels:
Wavefunction(E)
plot(x, psi, label="E = %.2f"%E)
legend(loc="upper right")
xlabel('r')
ylabel('$u(r)$', fontsize = 10)
grid()
savefig('numerov.pdf', bbox_inches='tight')
if __name__ == "__main__":
main()
Everything was working really well, this is a plot for Vo=35, l=1, but when I try whit a value of Vo=85, l=0 (is the same for Vo>50), the plot is not what I expected (the end of the plot blows up). For l=1, the error vanish. I am a novice in Python, so I do not know what would be the error. Thanks for the help.

Categories