1D FitzHugh Nagumo model - python

I am going to solve 1D FitzHugh Nagoma with homogeneous Neumann boundary conditions.
How to plot U and V separately. Here a1=2, a0=-0.03 , ep= 0.01 Du= 1, Dv=4 I was confused by plotting the figure
U_t=Du U_xx +U -U^3 - V
V_t=Dv V_xx + ep(U-a1V - a0)
import numpy as np
import matplotlib.pyplot as plt
#matplotlib inline
Du = 0.001
Dv = 0.005
tau = 1
k = -.00
ep = 0.01
a1 = 2
a0 = -0.03
L = 2
N= 10
x = np.linspace(0, L, N+1)
dx = x[1]-x[0]
T = 45 # total time
dt = .01 # time step
size = N
n = int(T / dt) # number of iterations
U = np.random.rand(size)
V = np.random.rand(size)
def laplacian(Z):
Ztop = Z[0:-2]
Zbottom = Z[2:]
Zcenter = Z[1:-1]
return (Ztop + Zbottom -
2 * Zcenter) / dx**2
def show_patterns(U, ax=None):
ax.imshow(U, cmap=plt.cm.copper,
interpolation='bilinear',
extent=[-1, 1])
ax.set_axis_off()
fig, axes = plt.subplots(3, 3, figsize=(16, 16))
step_plot = n // 9
# We simulate the PDE with the finite difference
# method.
for i in range(n):
# We compute the Laplacian of u and v.
deltaU = laplacian(U)
deltaV = laplacian(V)
# We take the values of u and v inside the grid.
Uc = U[1:-1]
Vc = V[1:-1]
# We update the variables.
U[1:-1], V[1:-1] = \
Uc + dt * (Du * deltaU + Uc - Uc**3 - Vc),\
Vc + dt * (Dv * deltaV + ep*(Uc - a1*Vc - a0)) / tau
# Neumann conditions: derivatives at the edges
# are null.
for Z in (U, V):
Z[0] = Z[1]
Z[-1] = Z[-2]
# Z[:, 0] = Z[:, 1]
# Z[:, -1] = Z[:, -2]
# We plot the state of the system at
# 9 different times.
fig, ax = plt.subplots(1, 1, figsize=(8, 8))
show_patterns(U,ax=None)
I got an error 'NoneType' object has no attribute 'imshow'
and could not solve it

This line
show_patterns(U,ax=None)
passed in a None to the ax parameter.
I don't know what ax should be but it needs to be properly initialised.

Related

stackoverflow error in 2d fipy PDE solver in python

I am trying to solve a system of three coupled PDEs and two ODEs (coupled) in 2D. The problem tries to solve for a case of a particle moving in a medium. The medium is given by the fields- velocity components vx, vy, and density m and there is one particle moving in this medium with position Rx, Ry.
It runs fine for a while but then throws up errors:
"Fatal Python error: Cannot recover from stack overflow.
Current thread 0x00007fe155915700 (most recent call first):"
Here is the code:
"""
"""
from fipy import (CellVariable, PeriodicGrid2D, Viewer, TransientTerm, DiffusionTerm,
UniformNoiseVariable, LinearLUSolver, numerix,
ImplicitSourceTerm, ExponentialConvectionTerm, VanLeerConvectionTerm,
PowerLawConvectionTerm, Variable)
import sys
import inspect
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage
from scipy.optimize import curve_fit
from scipy.signal import correlate
from scipy.stats import kurtosis
from scipy.interpolate import interp1d
numerix.random.seed(2)
def run_simulation(f0, Total_time):
# Define mesh size and number of points
nx = 50
ny = nx
L = 50
dx = L / nx
dy = dx
mesh = PeriodicGrid2D(dx, dy, nx, ny)
# Variables to use
vx = CellVariable(name='vx', mesh=mesh, hasOld=1)
vy = CellVariable(name='vy', mesh=mesh, hasOld=1)
m = CellVariable(name='m', mesh=mesh, hasOld=1)
# Initial condition
m.setValue(UniformNoiseVariable(mesh=mesh, minimum=0.6215, maximum=0.6225))
vx.setValue(UniformNoiseVariable(mesh=mesh, minimum=0, maximum=0.00001))
vy.setValue(UniformNoiseVariable(mesh=mesh, minimum=0, maximum=0.00001))
#particle position
x0=10.0
y0=25.0
# create grids for grad function
xgrid=np.unique(mesh.x.value)+dx/2
ygrid=np.unique(mesh.y.value)+dy/2
# parameters ------------------------------
B=4.0
Gamma=1.0
gamma=1.0
Dm=0.005
C=100.0
## save the initial positions in Rx,Ry
Rx=Variable(value=x0)
Ry=Variable(value=y0)
theta=Variable(value=0.0) # n-hat = cos(theta) x-hat + sin(theta) y-hat
sigma = 1
dt = 0.05
#-----------------------------------------
x_hat = [1.0, 0.0]
y_hat = [0.0, 1.0]
#------------- dirac delta function --------------
# https://stackoverflow.com/questions/58041222/dirac-delta-source-term-in-fipy
def delta_func(x, y, epsilon):
return ((x < epsilon) & (x > -epsilon) & (y < epsilon) & (y > -epsilon)) * \
(1 + numerix.cos(numerix.pi * x / epsilon) * numerix.cos(numerix.pi * y / epsilon)) / 2 / epsilon
############## equations #############
# renormalized parameters by Gamma
# fields : velocity vector, density scalar
# Gamma * v = -B rho(grad(rho)) + f* n-cap* delta(r-R), B>0, f>0, Gamma>0
# dot(rho) + del.(v rho) = 0
# particle
# dot(R) = (f/gamma)*(n-cap) - (C/gamma) * rho(grad(rho)) C>0
# Gamma=gamma=1, B' = B/Gamma, C'=C/gamma, f'=f/Gamma
######################################
eq_m = (TransientTerm(var=m) + ExponentialConvectionTerm(coeff=x_hat * vx + y_hat * vy, var=m) == DiffusionTerm(coeff=Dm, var=m) )
eq_vx = (ImplicitSourceTerm(coeff=1., var=vx) == -(B/Gamma)*m.grad.dot(x_hat)*(m) + (f0/Gamma)*numerix.cos(theta)* delta_func(mesh.x-Rx,mesh.y-Ry,sigma) )
eq_vy = (ImplicitSourceTerm(coeff=1., var=vy) == -(B/Gamma)*m.grad.dot(y_hat)*(m) + (f0/Gamma)*numerix.sin(theta)* delta_func(mesh.x-Rx,mesh.y-Ry,sigma) )
eq = eq_m & eq_vx & eq_vy
viewer = Viewer(vars=(m))
elapsed = 0.0
ms = []
vxs = []
vys = []
xs = []
ys = []
while elapsed < Total_time:
# Old values are used for sweeping when solving nonlinear values
vx.updateOld()
vy.updateOld()
m.updateOld()
print(elapsed, Rx, Ry)
mgrid=np.reshape(m.value,(nx,ny))
# gradient cal, dydx[0][x,y], dydx[1][x,y] -> x derivative, y derivative at x,y
dydx=np.gradient(mgrid,dx,dy,edge_order=2)
# impose periodic boundary on gradient
dydx[0][nx-1,:]=(mgrid[0,:]-mgrid[nx-2,:])/(2.0*dx)
dydx[0][0,:]=(mgrid[1,:]-mgrid[nx-1,:])/(2.0*dx)
dydx[1][:,ny-1]=(mgrid[:,0]-mgrid[:,ny-2])/(2.0*dy)
dydx[1][:,0]=(mgrid[:,1]-mgrid[:,ny-1])/(2.0*dy)
# solve ode
idx = np.argmin(np.abs(xgrid - Rx))
idy = np.argmin(np.abs(ygrid - Ry))
x0=x0+ ((f0/gamma)*np.cos(theta) - C*mgrid[idx,idy]*dydx[0][idx,idy])*dt
y0=y0+ ((f0/gamma)*np.sin(theta) - C*mgrid[idx,idy]*dydx[1][idx,idy])*dt
if(x0>L):
x0=x0-L
if(x0<0):
x0=x0+L
if(y0>L):
y0=y0-L
if(y0<0):
y0=y0+L
Rx.setValue(x0) # element-wise assignment did not work
Ry.setValue(y0)
elapsed += dt
res = 1e5
old_res = res * 2
step = 0
while res > 1e-5 and step < 5 and old_res / res > 1.01:
old_res = res
res = eq.sweep(dt=dt)
step += 1
# The variable values are just numpy arrays so easy to use!
# save velocity & density
vxs.append(vx.value.copy())
vys.append(vy.value.copy())
ms.append(m.value.copy())
viewer.plot()
# save x and y positions
xs.append(mesh.x.value.copy())
ys.append(mesh.y.value.copy())
return ms, vxs, vys, xs, ys
if __name__ == '__main__':
path = 'result/'
Total_time= 50 #40
f0 = 2
ms, vxs, vys, xs, ys = run_simulation(f0,Total_time)
name = 'f0_{:.4f}'.format(f0)
y = np.array([ms, vxs, vys])
xx = np.reshape(xs,(50,50))
yy = np.reshape(ys,(50,50))
vx = np.reshape(vxs[800][:],(50,50))
vy = np.reshape(vys[800][:],(50,50))
print(np.shape(xx), np.shape(xs), np.shape(vx))
#np.save(path + name, y)
plt.imshow(np.reshape(ms[800][:],(50,50)), aspect='auto', interpolation='bicubic', cmap='jet', extent=[0, 50, 50, 0])
plt.colorbar(label='density m')
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'rho_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
#---------------------------------------------
plt.imshow(np.reshape(vxs[800][:],(50,50)), aspect='auto', interpolation='bicubic', cmap='jet', extent=[0, 50, 50, 0])
plt.colorbar(label='velocity vx')
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'vel_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
#---------------------------------------------
plt.quiver(xx,yy,vx,vy,scale=3)
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'v_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
What can cause this error? I am not defining the equation inside the loop (this caused a similar problem before). Thank you in advance for your help.
UPDATE
I changed the function calling for x.mesh as
delta_func(xp,yp,sigma) where xp and yp are xp=Variable(value=mesh.x-Rx) and yp=Variable(value=mesh.y-Ry). Directly calling the x.mesh might have caused the problem according to an answer to my old question. But that did not help, I am still getting the overflow error. Here is the new version of the code:
"""
"""
from fipy import (CellVariable, PeriodicGrid2D, Viewer, TransientTerm, DiffusionTerm,
UniformNoiseVariable, LinearLUSolver, numerix,
ImplicitSourceTerm, ExponentialConvectionTerm, VanLeerConvectionTerm,
PowerLawConvectionTerm, Variable)
import sys
import inspect
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage
from scipy.optimize import curve_fit
from scipy.signal import correlate
from scipy.stats import kurtosis
from scipy.interpolate import interp1d
numerix.random.seed(2)
def run_simulation(f0, Total_time):
# Define mesh size and number of points
nx = 50
ny = nx
L = 50
dx = L / nx
dy = dx
mesh = PeriodicGrid2D(dx, dy, nx, ny)
# Variables to use
vx = CellVariable(name='vx', mesh=mesh, hasOld=1)
vy = CellVariable(name='vy', mesh=mesh, hasOld=1)
m = CellVariable(name='m', mesh=mesh, hasOld=1)
# Initial condition
m.setValue(UniformNoiseVariable(mesh=mesh, minimum=0.6215, maximum=0.6225))
vx.setValue(UniformNoiseVariable(mesh=mesh, minimum=0, maximum=0.00001))
vy.setValue(UniformNoiseVariable(mesh=mesh, minimum=0, maximum=0.00001))
#particle position
x0=10.0
y0=25.0
# create grids for grad function
xgrid=np.unique(mesh.x.value)+dx/2
ygrid=np.unique(mesh.y.value)+dy/2
# parameters ------------------------------
B=4.0
Gamma=1.0
gamma=1.0
Dm=0.005
C=100.0
## save the initial positions in Rx,Ry
Rx=Variable(value=x0)
Ry=Variable(value=y0)
theta=Variable(value=0.0) # n-hat = cos(theta) x-hat + sin(theta) y-hat
sigma = 1
dt = 0.05
#-----------------------------------------
xp=Variable(value=mesh.x-Rx)
yp=Variable(value=mesh.y-Ry)
x_hat = [1.0, 0.0]
y_hat = [0.0, 1.0]
#------------- dirac delta function --------------
# https://stackoverflow.com/questions/58041222/dirac-delta-source-term-in-fipy
def delta_func(x, y, epsilon):
return ((x < epsilon) & (x > -epsilon) & (y < epsilon) & (y > -epsilon)) * \
(1 + numerix.cos(numerix.pi * x / epsilon) * numerix.cos(numerix.pi * y / epsilon)) / 2 / epsilon
############## equations #############
# renormalized parameters by Gamma
# fields : velocity vector, density scalar
# Gamma * v = -B rho(grad(rho)) + f* n-cap* delta(r-R), B>0, f>0, Gamma>0
# dot(rho) + del.(v rho) = 0
# particle
# dot(R) = (f/gamma)*(n-cap) - (C/gamma) * rho(grad(rho)) C>0
# Gamma=gamma=1, B' = B/Gamma, C'=C/gamma, f'=f/Gamma
######################################
eq_m = (TransientTerm(var=m) + ExponentialConvectionTerm(coeff=x_hat * vx + y_hat * vy, var=m) == DiffusionTerm(coeff=Dm, var=m) )
eq_vx = (ImplicitSourceTerm(coeff=1., var=vx) == -(B/Gamma)*m.grad.dot(x_hat)*(m) + (f0/Gamma)*numerix.cos(theta)* delta_func(xp,yp,sigma) )
eq_vy = (ImplicitSourceTerm(coeff=1., var=vy) == -(B/Gamma)*m.grad.dot(y_hat)*(m) + (f0/Gamma)*numerix.sin(theta)* delta_func(xp,yp,sigma) )
eq = eq_m & eq_vx & eq_vy
viewer = Viewer(vars=(m))
elapsed = 0.0
ms = []
vxs = []
vys = []
xs = []
ys = []
while elapsed < Total_time:
# Old values are used for sweeping when solving nonlinear values
vx.updateOld()
vy.updateOld()
m.updateOld()
print(elapsed, Rx, Ry)
mgrid=np.reshape(m.value,(nx,ny))
# gradient cal, dydx[0][x,y], dydx[1][x,y] -> x derivative, y derivative at x,y
dydx=np.gradient(mgrid,dx,dy,edge_order=2)
# impose periodic boundary on gradient
dydx[0][nx-1,:]=(mgrid[0,:]-mgrid[nx-2,:])/(2.0*dx)
dydx[0][0,:]=(mgrid[1,:]-mgrid[nx-1,:])/(2.0*dx)
dydx[1][:,ny-1]=(mgrid[:,0]-mgrid[:,ny-2])/(2.0*dy)
dydx[1][:,0]=(mgrid[:,1]-mgrid[:,ny-1])/(2.0*dy)
# solve ode
idx = np.argmin(np.abs(xgrid - Rx))
idy = np.argmin(np.abs(ygrid - Ry))
x0=x0+ ((f0/gamma)*np.cos(theta) - C*mgrid[idx,idy]*dydx[0][idx,idy])*dt
y0=y0+ ((f0/gamma)*np.sin(theta) - C*mgrid[idx,idy]*dydx[1][idx,idy])*dt
if(x0>L):
x0=x0-L
if(x0<0):
x0=x0+L
if(y0>L):
y0=y0-L
if(y0<0):
y0=y0+L
Rx.setValue(x0) # element-wise assignment did not work
Ry.setValue(y0)
elapsed += dt
res = 1e5
old_res = res * 2
step = 0
while res > 1e-5 and step < 5 and old_res / res > 1.01:
old_res = res
res = eq.sweep(dt=dt)
step += 1
# The variable values are just numpy arrays so easy to use!
# save velocity & density
vxs.append(vx.value.copy())
vys.append(vy.value.copy())
ms.append(m.value.copy())
viewer.plot()
# save x and y positions
xs.append(mesh.x.value.copy())
ys.append(mesh.y.value.copy())
return ms, vxs, vys, xs, ys
if __name__ == '__main__':
path = 'result/'
Total_time= 100 #40
f0 = 2
ms, vxs, vys, xs, ys = run_simulation(f0,Total_time)
name = 'f0_{:.4f}'.format(f0)
y = np.array([ms, vxs, vys])
xx = np.reshape(xs,(50,50))
yy = np.reshape(ys,(50,50))
vx = np.reshape(vxs[380][:],(50,50))
vy = np.reshape(vys[380][:],(50,50))
print(np.shape(xx), np.shape(xs), np.shape(vx))
#np.save(path + name, y)
plt.imshow(np.reshape(ms[380][:],(50,50)), aspect='auto', interpolation='bicubic', cmap='jet', extent=[0, 50, 50, 0])
plt.colorbar(label='density m')
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'rho_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
#---------------------------------------------
plt.imshow(np.reshape(vxs[380][:],(50,50)), aspect='auto', interpolation='bicubic', cmap='jet', extent=[0, 50, 50, 0])
plt.colorbar(label='velocity vx')
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'vel_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
#---------------------------------------------
plt.quiver(xx,yy,vx,vy,scale=3)
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'v_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
I must confess that I ran out of patience before getting the stack overflow error, but I was able to identify the problem.
It's a similar (although more subtle) issue to what you reported before. Because theta is declared as a Variable,
x0=x0+ ((f0/gamma)*np.cos(theta) - C*mgrid[idx,idy]*dydx[0][idx,idy])*dt
y0=y0+ ((f0/gamma)*np.sin(theta) - C*mgrid[idx,idy]*dydx[1][idx,idy])*dt
result in longer and longer Variable expressions (and longer and longer step times). I.e., x0 = (((x0_0 + dx0_1) + dx0_2) + dx0_3) + dx0_4 + ...
Changing these to
x0=x0+ (((f0/gamma)*np.cos(theta) - C*mgrid[idx,idy]*dydx[0][idx,idy])*dt).value
y0=y0+ (((f0/gamma)*np.sin(theta) - C*mgrid[idx,idy]*dydx[1][idx,idy])*dt).value
resolves this issue.
Addressing the warning following the question edit
The warning in the comments about "...has been cast to a constant CellVariable" is due to:
xp=Variable(value=mesh.x-Rx)
yp=Variable(value=mesh.y-Ry)
This is definitely not recommended. This takes the MeshVariable objects mesh.x and mesh.y and then throws away the mesh. When the result is later associated with a mesh, e.g., by multiplying by another MeshVariable or using as a SourceTerm, FiPy warns because it looks like it could fit on the mesh, but it's not on a mesh. Just use
xp=mesh.x-Rx
yp=mesh.y-Ry

Efficient boolean disk packing mask

I need to make a mask of hexagonal packed disks. The code below does the job, but I don't feel like its efficient. I'm learning python as well so I'd love to get some expert advice on how to do this more computationally efficient.
r = 0.01
X, Y = np.mgrid[0:1:1000j, 0:1:1000j]
mask = np.full(X.shape, False)
px, py = np.mgrid[r : 1 : 2 * r * np.sqrt(3), r : 1 + r + np.finfo(float).eps: 2 * r]
px = np.vstack((px, px + r * np.sqrt(3)))
py = np.vstack((py, py - r))
fig, ax = plt.subplots(figsize= (12, 12), dpi=50)
img = ax.imshow(mask * 1, cmap = 'gray', vmin = 0, vmax = 1, extent = [0, 1, 0, 1])
for i, _ in np.ndenumerate(px): #is this loop dumb and inefficient?
C = (X - px[i]) ** 2 + (Y - py[i]) ** 2
mask = mask | (C < r ** 2)
img.set_data(mask * 1)
ax.set_aspect(1)
In particular, is there a way to vectorize the for loop?
Thanks
It may be efficient to create a single tile of the pattern, and then repeat it horizontally and vertically as needed:
Create a tile:
import numpy as np
import matplotlib.pyplot as plt
r = 0.01
sq3 = np.sqrt(3)
samples = 1000
X, Y = np.mgrid[1:(1 + 2 * sq3):int(sq3 * samples) * 1j, 0:2:samples * 1j]
XY = np.c_[X.flatten(), Y.flatten()]
# coordinates of centers of disks; suffices to take disks of radius 1 here
p = np.array([[1, 1], [(1 + sq3), 0], [(1 + sq3), 2], [(1 + 2 * sq3), 1]])
# compute the distance from each point of XY to each disk center
dist = (XY**2).sum(axis=1).reshape(-1, 1) + (p**2).sum(axis=1) - 2 * (XY # p.T)
# mask points outside the disks
tile = (np.min(dist, axis=1) < 1).reshape(X.shape)
fig, ax = plt.subplots(figsize=(5, 5))
ax.set_aspect(1)
plt.imshow(tile, extent=[0, 2 * r, r, (1 + 2 * sq3) * r]);
It gives:
Repeat the tile:
# the number of times to repeat the tile in the horizontal and vertical directions
h, w = 20, 30
# donwsample the tile as needed and replicate
sample_rate = 10
mask = np.tile(tile[::sample_rate, ::sample_rate], (h, w))
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_aspect(1)
ax.imshow(mask, extent=[0, 2 * r * w, 0, 2 * sq3 * r * h]);
This gives:

How to use if statement in a differential equation (SciPy)?

I am trying to solve a differential equation with Python.
In this two system differential equation if the value of first variable (v) is more than a threshold (30) it should be reset to another value (-65). Below I put my code. The problem is that the value of first variable after reaching 30 remains constant and won't reset to -65. These equations describe the dynamics of a single neuron. The equations are taken from this website and this PDF file.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FormatStrFormatter
from scipy.integrate import odeint
plt.close('all')
a = 0.02
b = 0.2
c = -65
d = 8
i = 0
p = [a,b,c,d,i]
def fun(u,tspan,*p):
du = [0,0]
if u[0] < 30: #Checking if the threshold has been reached
du[0] = (0.04*u[0] + 5)*u[0] + 150 - u[1] - p[4]
du[1] = p[0]*(p[1]*u[0]-u[1])
else:
u[0] = p[2] #reset to -65
u[1] = u[1] + p[3]
return du
p = tuple(p)
y0 = [0,0]
tspan = np.linspace(0,100,1000)
sol = odeint(fun, y0, tspan, args=p)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
plt.plot(tspan,sol[:,0],'k',linewidth = 5)
plt.plot(tspan,sol[:,1],'r',linewidth = 5)
myleg = plt.legend(['v','u'],\
loc='upper right',prop = {'size':28,'weight':'bold'}, bbox_to_anchor=(1,0.9))
The solution looks like:
Here is the correct solution by Julia, here u1 represent v:
This is the Julia code:
using DifferentialEquations
using Plots
a = 0.02
b = 0.2
c = -65
d = 8
i = 0
p = [a,b,c,d,i]
function fun(du,u,p,t)
if u[1] <30
du[1] = (0.04*u[1] + 5)*u[1] + 150 - u[2] - p[5]
du[2] = p[1]*(p[2]*u[1]-u[2])
else
u[1] = p[3]
u[2] = u[2] + p[4]
end
end
u0 = [0.0;0.0]
tspan = (0.0,100)
prob = ODEProblem(fun,u0,tspan,p)
tic()
sol = solve(prob,reltol = 1e-8)
toc()
plot(sol)
Recommended solution
This uses events and integrates separately after each discontinuity.
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import solve_ivp
a = 0.02
b = 0.2
c = -65
d = 8
i = 0
p = [a,b,c,d,i]
# Define event function and make it a terminal event
def event(t, u):
return u[0] - 30
event.terminal = True
# Define differential equation
def fun(t, u):
du = [(0.04*u[0] + 5)*u[0] + 150 - u[1] - p[4],
p[0]*(p[1]*u[0]-u[1])]
return du
u = [0,0]
ts = []
ys = []
t = 0
tend = 100
while True:
sol = solve_ivp(fun, (t, tend), u, events=event)
ts.append(sol.t)
ys.append(sol.y)
if sol.status == 1: # Event was hit
# New start time for integration
t = sol.t[-1]
# Reset initial state
u = sol.y[:, -1].copy()
u[0] = p[2] #reset to -65
u[1] = u[1] + p[3]
else:
break
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# We have to stitch together the separate simulation results for plotting
ax.plot(np.concatenate(ts), np.concatenate(ys, axis=1).T)
myleg = plt.legend(['v','u'])
Minimum change "solution"
It appears as though your approach works just fine with solve_ivp.
Warning I think in both Julia and solve_ivp, the correct way to handle this kind of thing is to use events. I believe the approach below relies on an implementation detail, which is that the state vector passed to the function is the same object as the internal state vector, which allows us to modify it in place. If it were a copy, this approach wouldn't work. In addition, there is no guarantee in this approach that the solver is taking small enough steps that the correct point where the limit is reached will be stepped on. Using events will make this more correct and generalisable to other differential equations which perhaps have lower gradients before the discontinuity.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import FormatStrFormatter
from scipy.integrate import solve_ivp
plt.close('all')
a = 0.02
b = 0.2
c = -65
d = 8
i = 0
p = [a,b,c,d,i]
def fun(t, u):
du = [0,0]
if u[0] < 30: #Checking if the threshold has been reached
du[0] = (0.04*u[0] + 5)*u[0] + 150 - u[1] - p[4]
du[1] = p[0]*(p[1]*u[0]-u[1])
else:
u[0] = p[2] #reset to -65
u[1] = u[1] + p[3]
return du
y0 = [0,0]
tspan = (0,100)
sol = solve_ivp(fun, tspan, y0)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
plt.plot(sol.t,sol.y[0, :],'k',linewidth = 5)
plt.plot(sol.t,sol.y[1, :],'r',linewidth = 5)
myleg = plt.legend(['v','u'],loc='upper right',prop = {'size':28,'weight':'bold'}, bbox_to_anchor=(1,0.9))
Result

Correct implementation of SI, SIS, SIR models (python)

I have created some very basic implementations of the mentioned models. However, although graphs seem to look right, the numbers don't add up to a constant. That is for the sum of susceptible/infected/recovered people in each compartment should add up to N (which is total number of people), but it doesn't, for some reason it adds up to some bizarre decimal numbers, and I really don't know how to fix it, after looking at it for 3 days now.
The SI Model:
import matplotlib.pyplot as plt
N = 1000000
S = N - 1
I = 1
beta = 0.6
sus = [] # infected compartment
inf = [] # susceptible compartment
prob = [] # probability of infection at time t
def infection(S, I, N):
t = 0
while (t < 100):
S = S - beta * ((S * I / N))
I = I + beta * ((S * I) / N)
p = beta * (I / N)
sus.append(S)
inf.append(I)
prob.append(p)
t = t + 1
infection(S, I, N)
figure = plt.figure()
figure.canvas.set_window_title('SI model')
figure.add_subplot(211)
inf_line, =plt.plot(inf, label='I(t)')
sus_line, = plt.plot(sus, label='S(t)')
plt.legend(handles=[inf_line, sus_line])
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) # use scientific notation
ax = figure.add_subplot(212)
prob_line = plt.plot(prob, label='p(t)')
plt.legend(handles=prob_line)
type(ax) # matplotlib.axes._subplots.AxesSubplot
# manipulate
vals = ax.get_yticks()
ax.set_yticklabels(['{:3.2f}%'.format(x*100) for x in vals])
plt.xlabel('T')
plt.ylabel('p')
plt.show()
SIS Model:
import matplotlib.pylab as plt
N = 1000000
S = N - 1
I = 1
beta = 0.3
gamma = 0.1
sus = \[\]
inf = \[\]
def infection(S, I, N):
for t in range (0, 1000):
S = S - (beta*S*I/N) + gamma * I
I = I + (beta*S*I/N) - gamma * I
sus.append(S)
inf.append(I)
infection(S, I, N)
figure = plt.figure()
figure.canvas.set_window_title('SIS model')
inf_line, =plt.plot(inf, label='I(t)')
sus_line, = plt.plot(sus, label='S(t)')
plt.legend(handles=\[inf_line, sus_line\])
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.xlabel('T')
plt.ylabel('N')
plt.show()
SIR Model:
import matplotlib.pylab as plt
N = 1000000
S = N - 1
I = 1
R = 0
beta = 0.5
mu = 0.1
sus = []
inf = []
rec = []
def infection(S, I, R, N):
for t in range (1, 100):
S = S -(beta * S * I)/N
I = I + ((beta * S * I)/N) - R
R = mu * I
sus.append(S)
inf.append(I)
rec.append(R)
infection(S, I, R, N)
figure = plt.figure()
figure.canvas.set_window_title('SIR model')
inf_line, =plt.plot(inf, label='I(t)')
sus_line, = plt.plot(sus, label='S(t)')
rec_line, = plt.plot(rec, label='R(t)')
plt.legend(handles=[inf_line, sus_line, rec_line])
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.xlabel('T')
plt.ylabel('N')
plt.show()
I'll look only at the SI model.
Your two key variables are S and I. (You may have reversed the meanings of these two variables, though that does not affect what I write here.) You initialize them so their sum is N which is the constant 1000000.
You update your two key variables in the lines
S = S - beta * ((S * I / N))
I = I + beta * ((S * I) / N)
You apparently intend to add to I and subtract from S the same value, so the sum of S and I is unchanged. However, you actually first change S then use that new value to change I, so the values added and subtracted are not actually the same, and the sum of the variables has not remained constant.
You can fix this by using Python's ability to update multiple variables in one line. Replace those two lines with
S, I = S - beta * ((S * I / N)), I + beta * ((S * I) / N)
This calculates both of the new values before updating the variables, so the same value actually added and subtracted from the two variables. (There are other ways to get the same effect, such as temporary variables for the updated values, or one temporary variable to store the amount to add and subtract, but since you use Python you may as well use its capabilities.)
When I now run the program, I get these graphs:
which I think is what you want.
So the solution above worked for the SIS model as well.
As for the SIR model I had to solve differential equations using odeint, here is a simple solution to the SIR model:
import matplotlib.pylab as plt
from scipy.integrate import odeint
import numpy as np
N = 1000
S = N - 1
I = 1
R = 0
beta = 0.6 # infection rate
gamma = 0.2 # recovery rate
# differential equatinons
def diff(sir, t):
# sir[0] - S, sir[1] - I, sir[2] - R
dsdt = - (beta * sir[0] * sir[1])/N
didt = (beta * sir[0] * sir[1])/N - gamma * sir[1]
drdt = gamma * sir[1]
print (dsdt + didt + drdt)
dsirdt = [dsdt, didt, drdt]
return dsirdt
# initial conditions
sir0 = (S, I, R)
# time points
t = np.linspace(0, 100)
# solve ODE
# the parameters are, the equations, initial conditions,
# and time steps (between 0 and 100)
sir = odeint(diff, sir0, t)
plt.plot(t, sir[:, 0], label='S(t)')
plt.plot(t, sir[:, 1], label='I(t)')
plt.plot(t, sir[:, 2], label='R(t)')
plt.legend()
plt.xlabel('T')
plt.ylabel('N')
# use scientific notation
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.show()

Producing 2D perlin noise with numpy

I'm trying to produce 2D perlin noise using numpy, but instead of something smooth I get this :
my broken perlin noise, with ugly squares everywhere
For sure, I'm mixing up my dimensions somewhere, probably when I combine the four gradients ... But I can't find it and my brain is melting right now. Anyone can help me pinpoint the problem ?
Anyway, here is the code:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
def perlin(x,y,seed=0):
# permutation table
np.random.seed(seed)
p = np.arange(256,dtype=int)
np.random.shuffle(p)
p = np.stack([p,p]).flatten()
# coordinates of the first corner
xi = x.astype(int)
yi = y.astype(int)
# internal coordinates
xf = x - xi
yf = y - yi
# fade factors
u = fade(xf)
v = fade(yf)
# noise components
n00 = gradient(p[p[xi]+yi],xf,yf)
n01 = gradient(p[p[xi]+yi+1],xf,yf-1)
n11 = gradient(p[p[xi+1]+yi+1],xf-1,yf-1)
n10 = gradient(p[p[xi+1]+yi],xf-1,yf)
# combine noises
x1 = lerp(n00,n10,u)
x2 = lerp(n10,n11,u)
return lerp(x2,x1,v)
def lerp(a,b,x):
"linear interpolation"
return a + x * (b-a)
def fade(t):
"6t^5 - 15t^4 + 10t^3"
return 6 * t**5 - 15 * t**4 + 10 * t**3
def gradient(h,x,y):
"grad converts h to the right gradient vector and return the dot product with (x,y)"
vectors = np.array([[0,1],[0,-1],[1,0],[-1,0]])
g = vectors[h%4]
return g[:,:,0] * x + g[:,:,1] * y
lin = np.linspace(0,5,100,endpoint=False)
y,x = np.meshgrid(lin,lin)
plt.imshow(perlin(x,y,seed=0))
Thanks to Paul Panzer and a good night of sleep it works now ...
import numpy as np
import matplotlib.pyplot as plt
def perlin(x, y, seed=0):
# permutation table
np.random.seed(seed)
p = np.arange(256, dtype=int)
np.random.shuffle(p)
p = np.stack([p, p]).flatten()
# coordinates of the top-left
xi, yi = x.astype(int), y.astype(int)
# internal coordinates
xf, yf = x - xi, y - yi
# fade factors
u, v = fade(xf), fade(yf)
# noise components
n00 = gradient(p[p[xi] + yi], xf, yf)
n01 = gradient(p[p[xi] + yi + 1], xf, yf - 1)
n11 = gradient(p[p[xi + 1] + yi + 1], xf - 1, yf - 1)
n10 = gradient(p[p[xi + 1] + yi], xf - 1, yf)
# combine noises
x1 = lerp(n00, n10, u)
x2 = lerp(n01, n11, u) # FIX1: I was using n10 instead of n01
return lerp(x1, x2, v) # FIX2: I also had to reverse x1 and x2 here
def lerp(a, b, x):
"linear interpolation"
return a + x * (b - a)
def fade(t):
"6t^5 - 15t^4 + 10t^3"
return 6 * t**5 - 15 * t**4 + 10 * t**3
def gradient(h, x, y):
"grad converts h to the right gradient vector and return the dot product with (x,y)"
vectors = np.array([[0, 1], [0, -1], [1, 0], [-1, 0]])
g = vectors[h % 4]
return g[:, :, 0] * x + g[:, :, 1] * y
lin = np.linspace(0, 5, 100, endpoint=False)
x, y = np.meshgrid(lin, lin) # FIX3: I thought I had to invert x and y here but it was a mistake
plt.imshow(perlin(x, y, seed=2), origin='upper')

Categories

Resources