plotting beta against maximum I in an SIR model - python

im trying to run an SIR model in python. I want to plot changing beta values against the maximum of I for each beta value. I have the beta's figured out but i dont know how i would plot the I against it?.
here's my code so far:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
import math
def SIR(y, t):
S, I, R = y
mu = 0.1
N = S + I + R
Sdot = -beta * S * I
Idot = beta * S * I - mu * I
Rdot = mu * I
return Sdot, Idot, Rdot
tf = 100
Nsteps = 1000
t = np.linspace(0, tf, Nsteps+1)
S0 = 10**4 - 3
I0 = 3
R0 = 0
y0 = np.array([S0, I0, R0])
y_sol = odeint(SIR, y0, t)
S = y_sol[:,0]
I = y_sol[:,1]
R = y_sol[:,2]
Imax = max(I)
tmax = t[I.argmax()]
Smax = S[I.argmax()]
beta_vals = np.linspace(0.2, 0.6, 5)
max_I =[]
for beta in beta_vals:
max_I.append(max(I))
plt.plot(beta_vals, max_I)
plt.xlabel('beta')
plt.ylabel('Maximum value of I')
plt.title('Effect of beta on the maximum value of I')
plt.show()
I thought that using append would give me the corresponding values but i end up with a straight line

You are almost there.
Try this updated code :
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
import math
def SIR(y, t, beta):
S, I, R = y
mu = 0.1
N = S + I + R
Sdot = -beta * S * I
Idot = beta * S * I - mu * I
Rdot = mu * I
return Sdot, Idot, Rdot
tf = 100
Nsteps = 1000
t = np.linspace(0, tf, Nsteps+1)
S0 = 10**4 - 3
I0 = 3
R0 = 0
y0 = np.array([S0, I0, R0])
beta_vals = np.linspace(0.2, 0.6, 5)
max_I = []
for beta in beta_vals:
y_sol = odeint(SIR, y0, t, args=(beta,))
S = y_sol[:,0]
I = y_sol[:,1]
max_I.append(max(I))
plt.plot(beta_vals, max_I)
plt.xlabel('beta')
plt.ylabel('Maximum value of I')
plt.title('Effect of beta on the maximum value of I')
plt.show()

Related

Runge Kutta 4th order Python

I am trying to solve this equation using Runge Kutta 4th order:
applying d2Q/dt2=F(y,x,v) and dQ/dt=u Q=y in my program.
I try to run the code but i get this error:
Traceback (most recent call last):
File "C:\Users\Egw\Desktop\Analysh\Askhsh1\asdasda.py", line 28, in <module>
k1 = F(y, u, x) #(x, v, t)
File "C:\Users\Egw\Desktop\Analysh\Askhsh1\asdasda.py", line 13, in F
return ((Vo/L -(R0/L)*u -(R1/L)*u**3 - y*(1/L*C)))
OverflowError: (34, 'Result too large')
I tried using the decimal library but I still couldnt make it work properly.I might have not used it properly tho.
My code is this one:
import numpy as np
from math import pi
from numpy import arange
from matplotlib.pyplot import plot, show
#parameters
R0 = 200
R1 = 250
L = 15
h = 0.002
Vo=1000
C=4.2*10**(-6)
t=0.93
def F(y, u, x):
return ((Vo/L -(R0/L)*u -(R1/L)*u**3 - y*(1/L*C)))
xpoints = arange(0,t,h)
ypoints = []
upoints = []
y = 0.0
u = Vo/L
for x in xpoints:
ypoints.append(y)
upoints.append(u)
m1 = u
k1 = F(y, u, x) #(x, v, t)
m2 = h*(u + 0.5*k1)
k2 = (h*F(y+0.5*m1, u+0.5*k1, x+0.5*h))
m3 = h*(u + 0.5*k2)
k3 = h*F(y+0.5*m2, u+0.5*k2, x+0.5*h)
m4 = h*(u + k3)
k4 = h*F(y+m3, u+k3, x+h)
y += (m1 + 2*m2 + 2*m3 + m4)/6
u += (k1 + 2*k2 + 2*k3 + k4)/6
plot(xpoints, upoints)
show()
plot(xpoints, ypoints)
show()
I expected to get the plots of u and y against t.
Turns out I messed up with the equations I was using for Runge Kutta
The correct code is the following:
import numpy as np
from math import pi
from numpy import arange
from matplotlib.pyplot import plot, show
#parameters
R0 = 200
R1 = 250
L = 15
h = 0.002
Vo=1000
C=4.2*10**(-6)
t0=0
#dz/dz
def G(x,y,z):
return Vo/L -(R0/L)*z -(R1/L)*z**3 - y/(L*C)
#dy/dx
def F(x,y,z):
return z
t = np.arange(t0, 0.93, h)
x = np.zeros(len(t))
y = np.zeros(len(t))
z = np.zeros(len(t))
y[0] = 0.0
z[0] = 0
for i in range(1, len(t)):
k0=h*F(x[i-1],y[i-1],z[i-1])
l0=h*G(x[i-1],y[i-1],z[i-1])
k1=h*F(x[i-1]+h*0.5,y[i-1]+k0*0.5,z[i-1]+l0*0.5)
l1=h*G(x[i-1]+h*0.5,y[i-1]+k0*0.5,z[i-1]+l0*0.5)
k2=h*F(x[i-1]+h*0.5,y[i-1]+k1*0.5,z[i-1]+l1*0.5)
l2=h*G(x[i-1]+h*0.5,y[i-1]+k1*0.5,z[i-1]+l1*0.5)
k3=h*F(x[i-1]+h,y[i-1]+k2,z[i-1]+l2)
l3 = h * G(x[i - 1] + h, y[i - 1] + k2, z[i - 1] + l2)
y[i]=y[i-1]+(k0+2*k1+2*k2+k3)/6
z[i] = z[i - 1] + (l0 + 2 * l1 + 2 * l2 + l3) / 6
Q=y
I=z
plot(t, Q)
show()
plot(t, I)
show()
If I may draw your attention to these 4 lines
m1 = u
k1 = F(y, u, x) #(x, v, t)
m2 = h*(u + 0.5*k1)
k2 = (h*F(y+0.5*m1, u+0.5*k1, x+0.5*h))
You should note a fundamental structural difference between the first two lines and the second pair of lines.
You need to multiply with the step size h also in the first pair.
The next problem is the step size and the cubic term. It contributes a term of size 3*(R1/L)*u^2 ~ 50*u^2 to the Lipschitz constant. In the original IVP per the question with u=Vo/L ~ 70 this term is of size 2.5e+5. To compensate only that term to stay in the stability region of the method, the step size has to be smaller 1e-5.
In the corrected initial conditions with u=0 at the start the velocity u remains below 0.001 so the cubic term does not determine stability, this is now governed by the last term contributing a Lipschitz term of 1/sqrt(L*C) ~ 125. The step size for stability is now 0.02, with 0.002 one can expect quantitatively useful results.
You can use decimal libary for more precision (handle more digits), but it's kind of annoying every value should be the same class (decimal.Decimal).
For example:
import numpy as np
from math import pi
from numpy import arange
from matplotlib.pyplot import plot, show
# Import decimal.Decimal as D
import decimal
from decimal import Decimal as D
# Precision
decimal.getcontext().prec = 10_000_000
#parameters
# Every value should be D class (decimal.Decimal class)
R0 = D(200)
R1 = D(250)
L = D(15)
h = D(0.002)
Vo = D(1000)
C = D(4.2*10**(-6))
t = D(0.93)
def F(y, u, x):
# Decomposed for use D
a = D(Vo/L)
b = D(-(R0/L)*u)
c = D(-(R1/L)*u**D(3))
d = D(-y*(D(1)/L*C))
return ((a + b + c + d ))
xpoints = arange(0,t,h)
ypoints = []
upoints = []
y = D(0.0)
u = D(Vo/L)
for x in xpoints:
ypoints.append(y)
upoints.append(u)
m1 = u
k1 = F(y, u, x) #(x, v, t)
m2 = (h*(u + D(0.5)*k1))
k2 = (h*F(y+D(0.5)*m1, u+D(0.5)*k1, x+D(0.5)*h))
m3 = h*(u + D(0.5)*k2)
k3 = h*F(y+D(0.5)*m2, u+D(0.5)*k2, x+D(0.5)*h)
m4 = h*(u + k3)
k4 = h*F(y+m3, u+k3, x+h)
y += (m1 + D(2)*m2 + D(2)*m3 + m4)/D(6)
u += (k1 + D(2)*k2 + D(2)*k3 + k4)/D(6)
plot(xpoints, upoints)
show()
plot(xpoints, ypoints)
show()
But even with ten million of precision I still get an overflow error. Check the components of the formula, their values are way too high. You can increase precision for handle them, but you'll notice it takes time to calculate them.
Problem implementation using scipy.integrate.odeint and scipy.integrate.solve_ivp.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint, solve_ivp
# Input data initial conditions
ti = 0.0
tf = 0.5
N = 100000
h = (tf-ti)/N
# Initial conditions
u0 = 0.0
Q0 = 0.0
t_span = np.linspace(ti,tf,N)
r0 = np.array([Q0,u0])
# Parameters
R0 = 200
R1 = 250
L = 15
C = 4.2*10**(-6)
V0 = 1000
# Systems of First Order Equations
# This function is used with odeint, as specified in the documentation for scipy.integrate.odeint
def f(r,t,R0,R1,L,C,V0):
Q,u = r
ode1 = u
ode2 = -((R0/L)*u)-((R1/L)*u**3)-((1/(L*C))*Q)+(V0/L)
return np.array([ode1,ode2])
# This function is used in our 4Order Runge-Kutta implementation and in scipy.integrate.solve_ivp
def F(t,r,R0,R1,L,C,V0):
Q,u = r
ode1 = u
ode2 = -((R0/L)*u)-((R1/L)*u**3)-((1/(L*C))*Q)+(V0/L)
return np.array([ode1,ode2])
# Resolution with oedint
sol_1 = odeint(f,r0,t_span,args=(R0,R1,L,C,V0))
sol_2 = solve_ivp(fun=F,t_span=(ti,tf), y0=r0, method='LSODA',args=(R0,R1,L,C,V0))
Q_odeint, u_odeint = sol_1[:,0], sol_1[:,1]
Q_solve_ivp, u_solve_ivp = sol_2.y[0,:], sol_2.y[1,:]
# Figures
plt.figure(figsize=[30.0,10.0])
plt.subplot(3,1,1)
plt.grid(color = 'red',linestyle='--',linewidth=0.4)
plt.plot(t_span,Q_odeint,'r',t_span,u_odeint,'b')
plt.xlabel('t(s)')
plt.ylabel('Q(t), u(t)')
plt.subplot(3,1,2)
plt.plot(sol_2.t,Q_solve_ivp,'g',sol_2.t,u_solve_ivp,'y')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('t(s)')
plt.ylabel('Q(t), u(t)')
plt.subplot(3,1,3)
plt.plot(Q_solve_ivp,u_solve_ivp,'green')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('Q(t)')
plt.ylabel('u(t)')
plt.show()
Runge-Kutta 4th
# Code development of Runge-Kutta 4 Order
# Parameters
R0 = 200
R1 = 250
L = 15
C = 4.2*10**(-6)
V0 = 1000
# Input data initial conditions #
ti = 0.0
tf = 0.5
N = 100000
h = (tf-ti)/N
# Initial conditions
u0 = 0.0
Q0 = 0.0
# First order ordinary differential equations
def f1(t,Q,u):
return u
def f2(t,Q,u):
return -((R0/L)*u)-((R1/L)*u**3)-((1/(L*C))*Q)+(V0/L)
t = np.zeros(N); Q = np.zeros(N); u = np.zeros(N)
t[0] = ti
Q[0] = Q0
u[0] = u0
for i in range(0,N-1,1):
k1 = h*f1(t[i],Q[i],u[i])
l1 = h*f2(t[i],Q[i],u[i])
k2 = h*f1(t[i]+(h/2),Q[i]+(k1/2),u[i]+(l1/2))
l2 = h*f2(t[i]+(h/2),Q[i]+(k1/2),u[i]+(l1/2))
k3 = h*f1(t[i]+(h/2),Q[i]+(k2/2),u[i]+(l2/2))
l3 = h*f2(t[i]+(h/2),Q[i]+(k2/2),u[i]+(l2/2))
k4 = h*f1(t[i]+h,Q[i]+k3,u[i]+l3)
l4 = h*f2(t[i]+h,Q[i]+k3,u[i]+l3)
Q[i+1] = Q[i] + ((k1+2*k2+2*k3+k4)/6)
u[i+1] = u[i] + ((l1+2*l2+2*l3+l4)/6)
t[i+1] = t[i] + h
plt.figure(figsize=[20.0,10.0])
plt.subplot(1,2,1)
plt.plot(t,Q_solve_ivp,'r',t,Q_odeint,'y',t,Q,'b')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('t(s)')
plt.ylabel(r'$Q(t)_{Odeint}$, $Q(t)_{RK4}$')
plt.subplot(1,2,2)
plt.plot(t,Q_solve_ivp,'g',t,Q_odeint,'y',t,Q,'b')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('t(s)')
plt.ylabel(r'$Q(t)_{solve_ivp}$, $Q(t)_{RK4}$')

I don't know why I get the multiple graphs of W versus PT using the attached python code

I want to get three graphs (W vs. X, W vs. y, W vs PT). But I can get two proper graphs (W vs. X and W vs. y). Unfortunately, I finally got multiple graphs of W vs PT (green lines). I don't know how to handle it. Anything you could do for me would be highly appreciated.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def PBR(X, W):
a = 9.8*10**(-5)
y = (1-a*W)**0.5
PH2 = PT0*(1.5-X)*y
PB = PT0 * X * y
PT = PT0 * (1 - X)*y
r = -k * PT * PH2 / (1 + KB*PB + KT *PT)
dXdW = -r/FT0
return dXdW
W = np.linspace(0, 10000)
KT = 1.038
KB = 1.39
FT0 = 50
k = 0.00087
PT0 = 12
X0 = 0
a = 9.8*10**(-5)
y = (1-a*W)**0.5
PT = PT0 * (1 - X)*y
X = odeint(PBR, X0, W)
plt.plot(W, PT, 'g', linewidth=0.5)
plt.plot(W, X,'r', linewidth=3.0)
plt.plot(W, y,'b', linewidth=5.0)
enter image description here
Are you looking for output like this. If yes - small issue in your script. Look at line marked as #**. You want to multiply with first dimension of X array.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def PBR(X, W):
a = 9.8*10**(-5)
y = (1-a*W)**0.5
PH2 = PT0*(1.5-X)*y
PB = PT0 * X * y
PT = PT0 * (1 - X)*y
r = -k * PT * PH2 / (1 + KB*PB + KT *PT)
dXdW = -r/FT0
return dXdW
W = np.linspace(0, 10000)
KT = 1.038
KB = 1.39
FT0 = 50
k = 0.00087
PT0 = 12
X0 = 0
a = 9.8*10**(-5)
y = (1-a*W)**0.5
X = odeint(PBR, X0, W)
PT = PT0 * np.multiply(1 - X[:,0],y) #**
print(PT)
plt.plot(W, PT, 'g', linewidth=0.5)
plt.plot(W, X,'r', linewidth=3.0)
plt.plot(W, y,'b', linewidth=5.0)

BER-SNR curve in Rayleigh channel using BPSK

I am trying to generate BER-SNR curve in Rayleigh channel using BPSK modulation. Can anyone help me to find what is wrong with the python code? The theoretical BER differs from the simulated BER. The red dotted line is theoretical and the blue one is simulated.
import math as mt
import numpy as np
import matplotlib.pyplot as plt
import numpy.random as nr
from scipy.linalg import toeplitz
# ------------Simulation parameters------------------------
N = 500 #No.of symbols
H_n = 10 #No.of channel elements
h_ray = np.zeros((H_n, 1), dtype = complex)
# ---------------------------------------------------------
EbN0dB = np.array(range(0, 11))
itr = len(EbN0dB)
ber = [None] * itr
theoreticalBER = [None] * itr
# -----------------------------Transmitter----------------------------
s = 2 * (nr.rand(N, 1) >= 0.5) - 1 # BPSK
# ------------------------------Channel-------------------------------
h_ray = (1/mt.sqrt(2)) * (nr.randn(H_n, 1) + 1j * nr.randn(H_n, 1))
# Rayleigh fading channel
z = np.zeros((N-1,1))
c = np.concatenate((h_ray, z), axis=0)
r = np.concatenate(([h_ray[0]], np.zeros((1, N-1))), axis=1)
h_ray_y=toeplitz(c, r) # Toeplitz matrix instead of convolution
y_ray = np.dot(h_ray_y, s)
for n in range(itr):
EbN0dB_n = EbN0dB[n]
EbN0 = 10.0 ** (-EbN0dB_n/ 20.0)
noise = 1/mt.sqrt(2) * (nr.randn(N+H_n-1) + 1j * nr.randn(N+H_n-1))
r_ray = y_ray + EbN0 * noise
# --------------------------Receiver------------------------------
r_t = np.dot(np.dot(np.linalg.pinv(np.dot(h_ray_y.T, h_ray_y)), h_ray_y.T), r_ray) # (H.T * H)^(-1)*H.T*received
r_d = 2 * (np.real(r_t) >= 0) - 1
errors = (s != r_d).sum()
ber[n] = 1.0 * errors / N
EbN0_th = 10.0 ** (EbN0dB_n / 10.0)
theoreticalBER[n] = 0.5 *(1 - mt.sqrt(EbN0_th/ (1 + EbN0_th)))
ber=np.asanyarray(ber)
#-------------------------------------------------------------------------
plt.plot(EbN0dB, np.log10(ber), '--bo')
plt.plot(EbN0dB, np.log10(theoreticalBER), 'ro')
plt.xlabel('EbN0(dB)')
plt.ylabel('BER')
plt.grid(True)
plt.title('BPSK - Rayleigh')
plt.show()

Hitting a specified target region on a sphere

I am attempting to create a program that will count the number of hits to a specific rectangular area on the surface of a sphere. How the programs is supposed to work, is random lines are generated and if one of those line hits in the target area the count goes up one. My problem is I do not think I am generating the lines correctly and I really have know idea how to correctly set the count parameters. This is the code I have so far and how I think the lines should be generated and what the count parameter might be.
import numpy as np
import random as rand
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect("equal")
#rough model of the earth
theta, phi = np.mgrid[0:2*np.pi : 20j ,0:np.pi : 20j]
r = 6.3
x = r * np.cos(phi)*np.sin(theta)
y = r * np.sin(phi)*np.sin(theta)
z = r * np.cos(theta)
ax.plot_wireframe(x,y,z, color = "k")
#target area
lat1x = 46.49913179 * (2*np.pi/360)
lat2x = 46.4423682 * (2*np.pi/360)
long1y = -119.4049072 * (2*np.pi/360)
long2y = -119.5048141 * (2*np.pi/360)
lat3x = 46.3973998 * (2*np.pi/360)
lat4x = 46.4532495 * (2*np.pi/360)
long3y = -119.4495392 * (2*np.pi/360)
long4y = -119.3492884 * (2*np.pi/360)
def to_cartesian(lat,lon):
x = r * np.cos(lon)*np.cos(lat)
y = r * np.sin(lon)*np.cos(lat)
z = r * np.sin(lat)
return [x,y,z]
p1 = to_cartesian(lat1x,long1y)
p2 = to_cartesian(lat2x,long2y)
p3 = to_cartesian(lat3x,long3y)
p4 = to_cartesian(lat4x,long4y)
X = np.array([p1,p2,p3,p4])
ax.scatter(X[:,0],X[:,1],X[:,2], color = "r")
#random line path
n = 500
x0 = np.zeros(n)
y0 = np.zeros(n)
z0 = np.zeros(n)
x1 = np.zeros(n)
y1 = np.zeros(n)
z1 = np.zeros(n)
for k in range (n):
theta = rand.uniform(0.0, np.pi)
phi = rand.uniform(0, (2 * np.pi))
x0[k] = 100 * np.sin(phi) * np.cos(theta)
y0[k] = 100 * np.sin(phi) * np.sin(theta)
z0[k] = 100 * np.cos(theta)
for j in range (n):
theta = rand.uniform(0.0, np.pi)
phi = rand.uniform(0, (2 * np.pi))
x1[j] = 100 * np.sin(phi) * np.cos(theta)
y1[j] = 100 * np.sin(phi) * np.sin(theta)
z1[j] = 100 * np.cos(theta)
#ax.plot_wireframe([x0[k],x1[j]],[y0[k],y1[j]],[z0[k],z1[j]], color="g")
# count if hit target area
count = 0
for i in range (n):
if np.any([x1[i]<=X[:0]<=x0[i]]) and np.any([y1[i]<=X[:1]<=y0[i]]) and
np.any([z1[i]<=X[:2]<=z0[i]]):
count =+1
print (count)
plt.show()

python - different array length along interpolation axis?

I am trying to use the Python interpolation function to get the value y for a given x but I am getting the error "raise ValueError("x and y arrays must be equal in length along along interpolation axis" even though my arrays have both equal size and shape (according to what I get when I use .shape in my code). I am quite new to programming so I don't know how to check what else could be different in my arrays. Here is my code:
s = []
def slowroll(y, t):
phi, dphi, a = y
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
da = h*a
ddphi = -3.*h*dphi - phi
return [dphi,ddphi,da]
phi_ini = 18.
dphi_ini = -0.1
init_y = [phi_ini,dphi_ini,1.]
h_ini =np.sqrt(1/3. * (1/2. * dphi_ini**2. + 1/2.*phi_ini**2.))
t=np.linspace(0.,20.,100.)
from scipy.integrate import odeint
sol = odeint(slowroll, init_y, t)
phi = sol[:,0]
dphi = sol[:,1]
a=sol[:,2]
n=np.log(a)
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
s.extend(a*h)
x = np.asarray(s)
y = np.asarray(t)
F = interp1d(y, x, kind='cubic')
print F(7.34858263)
After adding in the required imports, I've been unable to duplicate your error with version 2.7.12. What python version are you running?
import numpy as np
from scipy.interpolate import interp1d
s = []
def slowroll(y, t):
phi, dphi, a = y
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
da = h*a
ddphi = -3.*h*dphi - phi
return [dphi,ddphi,da]
phi_ini = 18.
dphi_ini = -0.1
init_y = [phi_ini,dphi_ini,1.]
h_ini =np.sqrt(1/3. * (1/2. * dphi_ini**2. + 1/2.*phi_ini**2.))
t=np.linspace(0.,20.,100.)
from scipy.integrate import odeint
sol = odeint(slowroll, init_y, t)
phi = sol[:,0]
dphi = sol[:,1]
a=sol[:,2]
n=np.log(a)
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
s.extend(a*h)
x = np.asarray(s)
y = np.asarray(t)
F = interp1d(y, x, kind='cubic')
print F(7.34858263)
Output:
2.11688518961e+20

Categories