Correct implementation of SI, SIS, SIR models (python) - 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()

Related

How could I make a line which is inverted to the original

At the moment this is my code:
import scipy
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import EventCollection
import math
from scipy import integrate
from scipy import constants
K_b = 1.380649e-23 # Boltzmanns Constant
a_n = 6.022e23 # Avogrados Number
T = 10 # Kelvin
mass = 28
a = 10e-5
def mantleEvolution(tMax, dt) :
N = int(tMax/dt) + 1
timeArray = np.zeros(N)
numberArray = np.zeros(N)
t = 0
n_m = 1 #per cubic centimeter
n_s = 0
i = 0
while i < N:
m_m = float(mass) / a_n
V_m = math.sqrt((8.0 * K_b * float(T)) / (math.pi * float(m_m)))
R = math.pi * pow(float(a), 2) * n_m * V_m
ρ_d = 3 # g/cm^3
μ_g = 2 * 1.67e-23
m_d = (4 / 3) * math.pi * pow(float(a), 3)
n_g = 10e4
d_g = 0.01 # 1% mass density of gas
n_d = (d_g * n_g * μ_g) / (m_d * float(ρ_d))
n_s = n_s + R * dt
n_m = n_m - R * n_d * dt
timeArray[i] = t
numberArray[i] = n_m
t = t + dt
i = i + 1
return [timeArray, numberArray, n_s]
timeArray, numberArray, n_s = mantleEvolution(5.0 * 10**15, 5.0 * 10**9)
fig = plt.figure(figsize=(6.4, 6.4))
ax1 = plt.subplot(111)
ax1.plot(timeArray, numberArray)
ax1.set_xlabel('time, seconds', fontsize=20)
ax1.set_ylabel('number', fontsize=20)
plt.setp(ax1.get_xticklabels(), fontsize=16)
plt.setp(ax1.get_yticklabels(), fontsize=16)
fig.subplots_adjust(left=.18)
plt.savefig('mantleEvolution.pdf')
This is the graph I recieve from graphing my code:
What I wish to find out is how I could plot a second line which behaves the exact opposite where it grows at the same rate the other declines
So for example, something that ressembles this:
If you look at the graph you'll see that the value on the y axis is for both graphs the same for two opposite x values at the x axis. That means that all you want to do is invert the x axis. That is fairly easy done using the formula:
new_x = -1 * (old_x - 2.5) + 2.5
By the way, I see some interesting comments in your code. What is it that you are trying to do here if I may ask?

Example python nfft fourier transform - Issues with signal reconstruction normalization

I wrote a full working example for both nfft, and scipy.fft.
In both cases I start with a simple 1D sinusoidal signal with a little noise, take the fourier transform, and then go backwards and reconstruct the original signal.
Here is my code as clean and readable as I could manage:
import numpy
import nfft
import scipy
import scipy.fft
import matplotlib.pyplot as plt
if True: #<--- Ensure non-global namespace
#Define signal:
x = -0.5 + numpy.random.rand(1000)
#x = numpy.linspace(-.5, .5, 1000) #--> in case we want to run uniform time domain
f = numpy.sin(10 * 2 * numpy.pi * x) + .1*numpy.random.randn( 1000 ) #Add some 'y' randomness to the sample
#prepare wavenumbers for transform:
N = len(x)
k = - N // 2 + numpy.arange(N)
#print ('k', k) #---> Uniform Steps [-500, -499, ...0..., 499,500]
f_k = nfft.nfft_adjoint(x, f, len(k), truncated=False )
#plot transform
plt.figure()
plt.plot(k, f_k.real, label='real')
plt.plot(k, f_k.imag, label='imag')
plt.legend()
#Reconstruct the original signal with nfft
f_recon = nfft.nfft( x, f_k ) / 2000
#Plot original vs reconstructed
plt.figure()
plt.title('nfft')
plt.scatter(x, f, label='f(x)')
plt.scatter(x, f_recon, label='f_recon(x)', marker='+')
plt.legend()
if True: #<--- Ensure non-global namespace
#Define signal:
x = numpy.linspace(-.5, .5, 1000)
f = numpy.sin(10 * 2 * numpy.pi * x) + .1*numpy.random.randn( 1000 ) #Add some 'y' randomness to the sample
#prepare wavenumbers for transform:
N = len(x)
TimeSpacing = x[1] - x[0]
k = scipy.fft.fftfreq(N, TimeSpacing)
#print ('k', k) #---> Confusing steps: [0,1,...500,-500,-499,...-1]
f_k = scipy.fft.fft(f)
#plot transform
plt.figure()
plt.plot(k, f_k.real, label='real')
plt.plot(k, f_k.imag, label='imag')
plt.legend()
#Reconstruct the original signal with scipy.fft
f_recon = scipy.fft.ifft(f_k)
#Plot original vs reconstructed
plt.figure()
plt.title('scipy.fft')
plt.scatter(x, f, label='f(x)')
plt.scatter(x, f_recon, label='f_recon(x)', marker='+')
plt.legend()
plt.show()
Here are the relevant generated plots:
The nfft reconstruction seems to fail at normalizing.
I arbitrarily divided the magnitudes by 2000 just to get them to plot well.
What is the correct normalization constant?
The nfft also seems to not reproduce the original points.
Even if I got hte normalization constant correct, there is no way I would get the original points back here.
What did I do wrong, and how do I fix it?
The above mentioned package does not implement a inverse nfft
The ndft is f_hat # np.exp(-2j * np.pi * x * k[:, None])
The ndft_adjoint is f # np.exp(2j * np.pi * k * x[:, None])
Let k = -N//2 + np.arange(N), and A = np.exp(-2j * np.pi * k * k[:, None])
A # np.conj(A) = N * np.eye(N) (checked numerically)
Thus, for random x the adjoint transformation is equals to the inverse transform. The given reference paper provides a few options, I implemented Algorithm 1 CGNE, from page 9
import numpy as np # I have the habit to use np
def nfft_inverse(x, y, N, w = 1, L=100):
f = np.zeros(N, dtype=np.complex128);
r = y - nfft.nfft(x, f);
p = nfft.nfft_adjoint(x, r, N);
r_norm = np.sum(abs(r)**2 * w)
for l in range(L):
p_norm = np.sum(abs(p)**2 * w);
alpha = r_norm / p_norm
f += alpha * w * p;
r = y - nfft.nfft(x, f)
r_norm_2 = np.sum(abs(r)**2 * w)
beta = r_norm_2 / r_norm
p = beta * p + nfft.nfft_adjoint(x, w * r, N)
r_norm = r_norm_2;
#print(l, r_norm)
return f;
The algorithm converges slowly and poorly
plt.figure(figsize=(14, 7))
plt.title('inverse nfft error histogram')
#plt.scatter(x, f_hat, label='f(x)')
h_hat = nfft_inverse(x, f, N, L = 1)
plt.hist(f_hat - numpy.real(h_hat), bins=30, label='1 iteration')
h_hat = nfft_inverse(x, f, N, L = 10)
plt.hist(f_hat - numpy.real(h_hat), bins=30, label='10 iterations')
h_hat = nfft_inverse(x, f, N, L = 1000)
plt.hist(f_hat - numpy.real(h_hat), bins=30, label='1000 iterations')
plt.xlabel('error')
plt.ylabel('occurrencies')
plt.legend()
I tried also to use scipy minimization, to minimize the residual ||nfft(x, f) - y||**2 explicitly
import numpy as np # the habit
import scipy.optimize
def nfft_gradient_descent(x, y, N, L=10, tol=1e-8, method='CG'):
'''
compute $min || A # f - y ||**2 via gradient descent
the gradient is
`A^H # (A # f - y)`
Multiply by A using nfft.nfft
'''
def cost(fpack):
f = fpack[0::2] + 1j * fpack[1::2]
u = np.sum(np.abs(nfft.nfft(x, f) - y)**2)
return u
def grad(fpack):
f = fpack[0::2] + 1j * fpack[1::2]
r = nfft.nfft(x, f) - y
u = nfft.nfft_adjoint(x, r, N)
return np.stack([np.real(u), np.imag(u)], axis=1).reshape(-1)
x0 = np.zeros([N, 2])
result = scipy.optimize.minimize(cost, x0=x0, jac=grad, tol=tol, method=method, options={'maxiter': L, 'disp': True})
return result.x[0::2] + 1j * result.x[1::2];
The results look similar, you can try by different methods or parameters by your self if you want. But I believe the transformation is ill conditioned because transformed residual is considerably reduced but the residual on the reconstructed values is large.
Edit 1
Is it basically true that you found that the there is not a true inverse to the algorithm then? I cannot obtain my original points? x != nfft(nfft_adjoint(x))
Please check the section 2.3 of the reference paper
Numerical comparison
Cris Luengo answer metioned another possibility, that is, instead of reconstructing f at the points x, you may reconstruct a resampled version at equidistant points using the ifft. So you already have three options, and I will do a quick comparison. Bear in mind that the plot shown there is based on a NFFT computed in 16k samples, while here I am using 1k samples.
Since the FFT method uses different points we cannot compare to the original signal, what I will do is to compare with the harmonic function without the noise. The variance of the noise is 0.01, so an exact reconstruction would lead to this mean squared error.
N = 1024
x = -0.5 + numpy.random.rand(N)
f_hat = numpy.sin(10 * 2 * numpy.pi * x) + .1*numpy.random.randn( N ) #Add some 'y' randomness to the sample
k = - N // 2 + numpy.arange(N)
f = nfft.nfft(x, f_hat)
print('nfft_inverse')
h_hat = nfft_inverse(x, f, len(x), L = 10)
print('10 iterations: ', np.mean((numpy.sin(10 * 2 * numpy.pi * x) - numpy.real(h_hat))**2))
h_hat = nfft_inverse(x, f, len(x), L = 100)
print('100 iterations: ', np.mean((numpy.sin(10 * 2 * numpy.pi * x) - numpy.real(h_hat))**2))
h_hat = nfft_inverse(x, f, len(x), L = 1000)
print('1000 iterations: ', np.mean((numpy.sin(10 * 2 * numpy.pi * x) - numpy.real(h_hat))**2))
print('nfft_gradient_descent')
h_hat = nfft_gradient_descent(x, f, len(x), L = 10)
print('10 iterations: ', np.mean((numpy.sin(10 * 2 * numpy.pi * x) - numpy.real(h_hat))**2))
h_hat = nfft_gradient_descent(x, f, len(x), L = 100)
print('100 iterations: ', np.mean((numpy.sin(10 * 2 * numpy.pi * x) - numpy.real(h_hat))**2))
h_hat = nfft_gradient_descent(x, f, len(x), L = 1000)
print('1000 iterations: ', np.mean((numpy.sin(10 * 2 * numpy.pi * x) - numpy.real(h_hat))**2))
#Reconstruct the original at a spaced grid based on nfft result using ifft
f_recon = - numpy.fft.fftshift(numpy.fft.ifft(numpy.fft.ifftshift(f_k))) / (N / N2)
x_recon = k / N;
print('using IFFT: ', np.mean((numpy.sin(10 * 2 * numpy.pi * x_recon) - numpy.real(f_recon))**2))
Results:
nfft_inverse
10 iterations: 0.0798988590351581
100 iterations: 0.05136853850272318
1000 iterations: 0.037316315280700896
nfft_gradient_descent
10 iterations: 0.08832834348902704
100 iterations: 0.05901599049633016
1000 iterations: 0.043921864589484
using IFFT: 0.49044932854606377
Another way to view is
plt.plot(numpy.sin(10 * 2 * numpy.pi * x_recon), numpy.real(f_recon), '.', label='ifft')
plt.plot(numpy.sin(10 * 2 * numpy.pi * x), numpy.real(nfft_gradient_descent(x, f, len(x), L = 5)), '.', label='gradient descent L=5')
plt.plot(numpy.sin(10 * 2 * numpy.pi * x), numpy.real(nfft_inverse(x, f, len(x), L = 5)), '.', label='nfft_inverse L=5')
plt.plot(numpy.sin(10 * 2 * numpy.pi * x), np.real(f_hat), '.', label='original')
plt.legend()
Even though the IFFT matrix is better conditioned, it gives results in a worse reconstruction of the signal. Also from the last plot it becomes more noticeable that there is a slight attenuation. Probably due to a systematic energy leak to the imaginary part (an error in my code??). Just a quick test, multiplying it by 1.3 gives better results
Bob has already posted an excellent answer, this is just to complement with some details I hope are instructive.
First, compare the two plots for the computed frequency components. Note how the one for the NFFT is much more noisy than the one for the regular FFT. You are estimating these frequency components for a sampled signal from noisy samples, in one case the samples are regularly spaced, in the other they are randomly spaced. It is a known result that regular sampling is more efficient than random sampling (efficient meaning that you need fewer samples to get the same amount of information). Thus, it is expected that the random sampling yield the more noisy result.
We can compute the "normal" inverse FFT from the frequency components estimated by the NFFT:
f_recon = numpy.fft.fftshift(numpy.fft.ifft(numpy.fft.ifftshift(f_k)))
x_recon = numpy.linspace(-.5, .5, N)
I used ifftshift because NFFT defines the k to go from -N/2 to N/2-1, whereas the FFT defines it to go from 0 to N-1. ifftshift swaps the two halves of the signal to turn the first into the second (k from N/2 to N-1 is equal to -N/2 to -1). I also used fftshift on the result of the IFFT, because the same thing applies to the time axis, it shifts the origin from the first sample to the middle of the sequence.
Note how noisy f_recon is. This is explained by the poor estimates of f_k we could make with the non-uniformed sampled signal. There is also a sign error, we could already observe this error when we compared the two estimates of f_k. This comes from the adjoint NFFT having the same sign in the exponent as the inverse DFT, which actually means that f_recon is flipped w.r.t. x.
If we increase the number of random samples we can obtain a better estimate:
import numpy
import nfft
import matplotlib.pyplot as plt
#Define signal:
N = 1024 * 16 # power of two for speed
x = -0.5 + numpy.random.rand(N)
f = numpy.sin(10 * 2 * numpy.pi * x) + .1 * numpy.random.randn(N) # Add some 'y' randomness to the sample
#prepare wavenumbers for transform:
k = - N // 2 + numpy.arange(N)
N2 = 1024
f_k = nfft.nfft_adjoint(x, f, N2, truncated=False)
#Reconstruct the original signal with nfft
# (note the minus sign to flip the signal, in reality we should flip x)
f_recon = - numpy.fft.fftshift(numpy.fft.ifft(numpy.fft.ifftshift(f_k))) / (N / N2)
x_recon = numpy.linspace(-.5, .5, N2, endpoint=False)
#Plot original vs reconstructed
plt.figure()
plt.title('nfft')
plt.scatter(x[:N2], f[:N2], label='f(x)') # don't plot all samples, there's too many
plt.scatter(x_recon, f_recon, label='f_recon(x)')
plt.legend()
plt.show()

How to change Function by without Changing its Parameters

I am new to python and in learning stages. I wanted to implement Particle Swarm Optimization(PSO) algorithm which I did by taking help from on-line materials and python tutorials. In PSO, a simple calculus problem is inferred i-e 100 * ((y - (x2))2) + ((1 - (x2))2). This problem is defined in a fitness function.
def fitness(x, y):
return 100 * ((y - (x**2))**2) + ((1 - (x**2))**2)
Now, I want to replace this simple calculus problem by simple first order Ordinary Differential Equation(ODE) by without changing existing function parameters (x,y) and want to return the value of dy_dx,y0 and t for further process.
# Define a function which calculates the derivative
def dy_dx(y, x):
return x - y
t = np.linspace(0,5,100)
y0 = 1.0 # the initial condition
ys = odeint(dy_dx, y0, t)`
In python odeint function is used for ODE which requires three essential parameters i-e func/model, y0( Initial condition on y (can be a vector) and t(A sequence of time points for which to solve for y) Example of odeint parameters.
I don't want to change its parameters because it will be difficult for me to make changes in algorithm.
For simplicity I pasted the full code below and my question is open to anyone if wants to modify the code with further parameters in General Best, Personal Best and r[i].
import numpy as np
from scipy.integrate import odeint
import random as rand
from scipy.integrate import odeint
from numpy import array
import matplotlib.pyplot as plt
def main():
#Variables
n = 40
num_variables = 2
a = np.empty((num_variables, n))
v = np.empty((num_variables, n))
Pbest = np.empty((num_variables, n))
Gbest = np.empty((1, 2))
r = np.empty((n))
for i in range(0, num_variables):
for j in range(0, n):
Pbest[i][j] = rand.randint(-20, 20)
a[i][j] = Pbest[i][j]
v[i][j] = 0
for i in range(0, n):
r[i] = fitness(a[0][i], a[1][i])
#Sort elements of Pbest
Order(Pbest, r, n)
Gbest[0][0] = Pbest[0][0]
Gbest[0][1] = Pbest[1][0]
generation = 0
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.grid(True)
while(generation < 1000):
for i in range(n):
#Get Personal Best
if(fitness(a[0][i], a[1][i]) < fitness(Pbest[0][i], Pbest[1][i])):
Pbest[0][i] = a[0][i]
Pbest[1][i] = a[1][i]
#Get General Best
if(fitness(Pbest[0][i], Pbest[1][i]) < fitness(Gbest[0][0], Gbest[0][1])):
Gbest[0][0] = Pbest[0][i]
Gbest[0][1] = Pbest[1][i]
#Calculate Velocity
Vector_Velocidad(n, a, Pbest, Gbest, v)
generation = generation + 1
print 'Generacion: ' + str(generation) + ' - - - Gbest: ' +str(Gbest)
line1 = ax.plot(a[0], a[1], 'r+')
line2 = ax.plot(Gbest[0][0], Gbest[0][1], 'g*')
ax.set_xlim(-10, 10)
ax.set_ylim(-10, 10)
fig.canvas.draw()
ax.clear()
ax.grid(True)
print 'Gbest: '
print Gbest
def Vector_Velocidad(n, a, Pbest, Gbest, v):
for i in range(n):
#Velocity in X
v[0][i] = 0.7 * v[0][i] + (Pbest[0][i] - a[0][i]) * rand.random() * 1.47 + (Gbest[0][0] - a[0][i]) * rand.random() * 1.47
a[0][i] = a[0][i] + v[0][i]
v[1][i] = 0.7 * v[1][i] + (Pbest[1][i] - a[1][i]) * rand.random() * 1.47 + (Gbest[0][1] - a[1][i]) * rand.random() * 1.47
a[1][i] = a[1][i] + v[1][i]
def fitness(x, y):
return 100 * ((y - (x**2))**2) + ((1 - (x**2))**2)
def Order(Pbest, r, n):
for i in range(1, n):
for j in range(0, n - 1):
if r[j] > r[j + 1]:
#Order the fitness
tempRes = r[j]
r[j] = r[j + 1]
r[j + 1] = tempRes
#Order las X, Y
tempX = Pbest[0][j]
Pbest[0][j] = Pbest[0][j + 1]
Pbest[0][j + 1] = tempX
tempY = Pbest[1][j]
Pbest[1][j] = Pbest[1][j + 1]
Pbest[1][j + 1] = tempY
if '__main__' == main():
main()

Offset for SciPy.optimize least squares is not correct

I have a script which generates a noisy curve composed of 3 different sine curves. Using LombScargle I find the periods which are dominant because of these curves and detrend the original curve.
This involves phase folding, using SciPy optimize to fit a sine curve and then extrapolating the fitted curve out of phase space and onto the original multi-sine-wave-curve, and then subtracting this fit to detrend the data.
I iterate this process so that eventually when the signal on the LombScargle is less than 3 times some pre-generated noise height, the iteration stops. (equivalent to signal-to-noise ratio becoming too small).
But after the 1st iteration the phase of the fitted curve is way off! I can't see why this happens though. Can anyone help?
import matplotlib.pyplot as plt
import numpy as np
import math
from scipy.optimize import leastsq
from astropy.stats import LombScargle
t = np.linspace(15,30,1000)
y = 5 * np.sin(2*np.pi * 1./2. * (t + 1)) + 30
y1 = 11 * np.sin(2*np.pi * 1./3. * (t + 1))
y4 = 2 * np.sin(2*np.pi * 1./7. * (t + 1))
sampl = np.random.uniform(low = 0, high = 2.5, size = len(t))
y2 = y+y1+y4+sampl
y2 = y2/np.nanmedian(y2)
freq = np.linspace(1./150.,1./0.5,10000)
power2 = LombScargle(t,sampl).power(freq)
for _ in range(5):
plt.figure()
plt.subplot(221)
plt.plot(t,y2,'-')
plt.grid()
power = LombScargle(t,y2).power(freq)
p = 1./freq[np.argmax(power)]
plt.title(p)
plt.subplot(222)
plt.plot(1./freq,power)
plt.grid()
# print p
if np.max(power) <= np.max(power2)*3:
print 'done'
break
else:
foldtimes = (t - t[0])/p
FoldTimes = foldtimes % 1
p_0 = np.min(FoldTimes)
dp = 0.01
its = math.ceil((np.max(FoldTimes)-p_0)/dp)
n = np.linspace(0,its,its+1)
binned_flux = []
binned_phase = []
for i in range(len(n)):
indices = np.where(((p_0 + (n[i]*dp)) <= FoldTimes) & (FoldTimes <= (p_0 + (n[i]*dp)+dp)))[0]
if len(indices)>1:
binned_flux.append( np.nanmedian(y2[indices])) ##continuously adds the averaged fluxes to list
binned_phase.append (np.mean(FoldTimes[indices])) #same for hjs averages
binned_flux = np.array(binned_flux)
binned_phase = np.array(binned_phase)
plt.subplot(223)
plt.grid()
plt.plot(binned_phase,binned_flux,'.',linestyle='None')
plt.ylabel('Relative Flux')
plt.xlabel('Phase')
guess_mean = np.mean(y2)
guess_A = (np.max(y2) - np.min(y2))/2.
guess_phase = 0
optimize_func = lambda x: ((x[0] * np.sin(2*np.pi*(np.sort(binned_phase) + x[1]))) + x[2]) - binned_flux
est_A, est_phase, est_mean = leastsq(optimize_func, [guess_A,guess_phase,guess_mean])[0]
print est_phase
fit = (est_A*np.sin(2*np.pi*(1./p)*(t+ (est_phase*p)))) + est_mean
fit_p = (est_A*np.sin(2*np.pi*(1.)*(np.sort(binned_phase)+(est_phase)))) + est_mean
plt.plot(binned_phase,fit_p)
plt.subplot(224)
plt.plot(t,y2,'-')
plt.plot(t,fit)
plt.grid()
y2 = (y2 - fit) + est_mean #rewrites the original curve minus the fit

How can I modify my graph so it displays the proper information for the axes?

I have written a program to solve the Heat Equation (u_t = k * u_xx) numerically by method of Finite Differences.
For my problem, u is function of x and t, where 0 < x < L and t > 0. I have specified L = 1 (the length of the rod) and the terminal time T = 10 seconds for my problem, so I would like for the graph to be displayed on the domain (x,t) \in {(0,1) x (0, 10)}. However, my axes just don't make sense. It is plotting the x-axis from values of 0 - 40 and the t-axis is showing -0.25 - 0.00.
How can I edit my code so that when I plot u which depends on x, t the graph will display for values of x ranging from 0 - 1 and t ranging from 0 - 10 seconds??
Thanks in advance for any and all help. it is very greatly appreciated. Here is the code I am working with:
## This program is to implement a Finite Difference method approximation
## to solve the Heat Equation, u_t = k * u_xx,
## in 1D w/out sources & on a finite interval 0 < x < L. The PDE
## is subject to B.C: u(0,t) = u(L,t) = 0,
## and the I.C: u(x,0) = f(x).
import numpy as np
import matplotlib.pyplot as plt
# Parameters
L = 1 # length of the rod
T = 10 # terminal time
N = 40 # spatial values
M = 1600 # time values/hops; (M ~ N^2)
s = 0.25 # s := k * ( (dt) / (dx)^2 )
# uniform mesh
x_init = 0
x_end = L
dx = float(x_end - x_init) / N
x = np.arange(x_init, x_end, dx)
x[0] = x_init
# time discretization
t_init = 0
t_end = T
dt = float(t_end - t_init) / M
t = np.arange(t_init, t_end, dt)
t[0] = t_init
# time-vector
for m in xrange(0, M):
t[m] = m * dt
# spatial-vector
for j in xrange(0, N):
x[j] = j * dx
# definition of the solution u(x,t) to u_t = k * u_xx
u = np.zeros((N, M+1)) # array to store values of the solution
# Finite Difference Scheme:
u[:,0] = x * (x - 1) #initial condition
for m in xrange(0, M):
for j in xrange(1, N-1):
if j == 1:
u[j-1,m] = 0 # Boundary condition
elif j == N-1:
u[j+1,m] = 0 # Boundary Condition
else:
u[j,m+1] = u[j,m] + s * ( u[j+1,m] -
2 * u[j,m] + u[j-1,m] )
# for graph
print u, x, t
plt.plot(u)
plt.title('Finite Difference Approx. to Heat Equation')
plt.xlabel('x-axis')
plt.ylabel('time (seconds)')
plt.axis()
plt.show()
It appears that whatever displays for the x-axis reflects the number of step sizes in space that I take (N = 40) for my code. I thought np.arange(x_init, x_end, dx) would return evenly spaced values within the interval (x_init, x_end) with step size dx? So what am I doing wrong? Thanks again.
You have some issues with your code as your u turns out to be 40x1601 and not 40x1600. However, I think the plot you may be after (after correcting u) is
corrected_u = u[:,:-1:]
plt.pcolor(t, x, corrected_u)

Categories

Resources