I am attempting to model a 1D wave created by a Gaussian point source using the finite difference approximation method. Below is my code.
import matplotlib.pyplot as plt
import numpy as np
########Pre-Defining Values########
# spacial extent
lox = -1000
upx = 1000
# space sampling interval (km)
dx = 2.0
dx2inv = 1/(dx*dx)
# temporal extent
lot = 0
upt = 60
# time sampling interval (s)
dt = 0.5
dt2 = dt*dt
x = np.arange(lox,upx,dx)
t = np.arange(lot,upt,dt)
# pressure source location
psx = 0
# velocity (km/s)
v = 2.0
v2 = v*v
# density change location
pcl = 500
# density
p1 = 1
p1inv = 1/p1
p2 = 0.2
p2inv = 1/p2
pinv = np.zeros_like(x)
p = np.zeros_like(x)
for i in range(0,(int)((upx+pcl)/dx),1):
pinv[i] = p1inv
p[i] = p1
for i in range((int)((upx+pcl)/dx),len(pinv),1):
pinv[i] = p2inv
p[i] = p2
# waveform
f = np.zeros((len(t),len(x)))
# source
amp = 20
mu = 0
sig = 10/dx
s = np.zeros_like(f)
s[0] = 1/(sig*np.sqrt(2*np.pi)) * np.exp(-(x-mu)*(x-mu)/2/sig/sig)
maxinv = 1/np.amax(s[0])
for i in range(1,len(s[0])):
s[0][i] *= amp*maxinv
########Calculating Waveform########
h = np.zeros_like(f)
n1 = len(f)
n2 = len(f[0])
def fdx(i1):
for i2 in range(1,n2-1):
gi = f[i1][i2 ]
gi -= f[i1][i2-1]
gi *= pinv[i2]
h[i1][i2-1] -= gi
h[i1][i2 ] = gi
#f[0] = s[0]
fdx(0)
for i2 in range(0,n2):
f[1][i2] = 2*f[0][i2] + (s[0][i2] - h[0][i2] * dx2inv) * p[i2] * v2 * dt2
for i1 in range(1,n1-1):
fdx(i1)
for i2 in range(0,n2):
f[i1+1][i2] = 2*f[i1][i2] - f[i1-1][i2] + (s[i1][i2] - h[i1][i2] * dx2inv) * p[i2] * v2 * dt2
########Plotting########
plt.plot(x,f[50])
maxf = 1.5*amp
minf = -1.5*amp
plt.axis([lox,upx,minf,maxf])
plt.xlabel('x')
plt.ylabel('f(x,t)')
# vertical colored bars representing density
plt.axvspan(lox, pcl, facecolor='g', alpha=0.1)
plt.axvspan(pcl, upx, facecolor='g', alpha=0.2)
# text with density values
plt.text(pcl-0.2*upx,0.8*maxf,r'$\rho = $%s'%(p1),fontsize=15)
plt.text(pcl+0.05*upx,0.8*maxf,r'$\rho = $%s'%(p2),fontsize=15)
plt.show()
Unfortunately this code does not produce the correct result (two Gaussian pulses traveling left and right away from x=0). It instead produces one Gaussian pulse that grows with time. Does anyone know what error I am making?
Thank you very much.
It has been some time since you posted this, but if it is of any help, here is a code to generate Gaussian pulses. I am not good at programming so i am sorry if this code is obfuscating. I have used 1D FDTD wave propagation equation for an EM wave (unitless) :
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
#defining dimensions
xdim=720
time_tot = 500
xsource = xdim/2
#stability factor
S=1
#Speed of light
c=1
epsilon0=1
mu0=1
delta =1
deltat = S*delta/c
Ez = np.zeros(xdim)
Hy = np.zeros(xdim)
epsilon = epsilon0*np.ones(xdim)
mu = mu0*np.ones(xdim)
fig , axis = plt.subplots(1,1)
axis.set_xlim(len(Ez))
axis.set_ylim(-3,3)
axis.set_title("E Field")
line, = axis.plot([],[])
def init():
line.set_data([],[])
return line,
def animate(n, *args, **kwargs):
Hy[0:xdim-1] = Hy[0:xdim-1]+(delta/(delta*mu[0:xdim-1]))*(Ez[1:xdim]-Ez[0:xdim-1])
Ez[1:xdim]= Ez[1:xdim]+(delta/(delta*epsilon[1:xdim]))*(Hy[1:xdim]-Hy[0:xdim-1])
Ez[xsource] = Ez[xsource] + 30.0*(1/np.sqrt(2*np.pi))*np.exp(-(n-80.0)**2/(100))
ylims = axis.get_ylim()
if (abs(np.amax(Ez))>ylims[1]):
axis.set_ylim(-(np.amax(Ez)+2),np.amax(Ez)+2)
line.set_data(np.arange(len(Ez)),Ez)
return line,
ani = animation.FuncAnimation(fig, animate, init_func=init, frames=(time_tot), interval=10, blit=False, repeat =False)
fig.show()
I hope it helps. :)
Related
I have been trying to replicate Figures 1 and 2 from O'Dwyer's paper, "Electronic and thermal transport in hot carrier solar cells with low-dimensional contacts" (Link to O'Dwyer Paper), with Python on Spyder.
Figures to replicate
Figure 1: w = 1e-5
Figure 1
Figure 2 = w = 1e-2
Figure 2
Method
To find the absorber temperature, T_H, one needs to equate the net incoming energy flow due to radiation, Qrad, and the net heat current flowing out of the hot absorber reservoir, Qabs. Their equations are as follows:
Equations for Qrad and Qabs
The bold lined plots from Figures 1 and 2 refer to Wurfel's solutions that are given by the following equations:
Wurfel's Solutions
I am having some success replicating Figure 2 where w=1e-2 (my results shown below) but have no success getting Figure 1 where w=1e-5 (points and num_T below refer to the number of plotting points and the number of temperatures to iterate through respectively).
My attempt at Figure 2 when w=1e-2, points = 21, num_T = 300
My attempt at Figure 2
I think I am currently having trouble with the "overflow encountered in exp" warning trying to get Figure 1 with w=1e-5 to work. When I try to calculate Qabs (refer to code below in 'Parameters' function), it gives absurd values with orders of magnitude ~1e-70. However, when I run the same equations in WolframAlpha, I get a more reasonable result.
For example, the T_H value when W = 1e-5, N = 1e12 and Voltage = 0 V is ~T_H = 1448K (refer to Figure 1, top left graph).Using WolframAlpha, I get 4.54986×10^22 for Qrad and 4.83602×10^22 for Qabs (WolframAlpha solution for Qrad at w=1e-5,N=1e12,V=0) and WolframAlpha solution for Qabs at w=1e-5,N=1e12,V=0)) which are the results I want in Python. Find below all my code.
All Code
import os
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
import matplotlib.ticker as ticker
from scipy.integrate import quad
from scipy.special import expit
import time
from sympy import symbols, Eq, solve
# import warnings
# warnings.filterwarnings("ignore")
t0= time.perf_counter()
directory = r'C:\Users\gyanj\Documents\GADGET BACKUP\University\5th Year\Thesis\Python Simul\ODwyer\Plots'
os.chdir(directory)
c = 3e8 #speed of light, m/s
q = 1.602e-19 # charge of electron, C
h = 6.626e-34/q #Planck's Constant, eVs
k = 8.617e-5 # Boltzmann's Constant, eVK^-1
stefan = 5.67e-8 #Stefan-Boltzmann's Constant, Wm^-2K^-4
T_C = 300 #Cold Reservoir Temperature, K
T_S = 6000 #Sun Temperature, K
Omega = np.pi #Absorption/Emission Solid Angle, sr
A = 1e-4 #Absorber Area, m^2
points = 21 # Number of plotting points
num_T = 300 #Number of temperatures to iterate through
Temperatures = np.linspace(T_C,T_S,num_T) # array of temperatures
E_u = 1 #Average electrochemical potential of system, eV
V = np.linspace(0,1,points) #V applied symetrically across device
max_lim = np.inf# integral upper limit
W = [1e-2] #Transmission function width
N = [1e9,1e10,1e12] #Number of contacts
#Following block used for progress bar (not relevant to calculations)
global total
total = len(W)*len(N)*(points)*len(Temperatures)
progress = 0
counter = 0
full_time = 0
#Object containing all relevant parameters
class param:
def __init__(self, TH, I, P, n, Qrad, Qabs):
self.TH = TH #Hot reservoir/Absorber Temperature, K
self.I = I # Current, A/m^2
self.P = P #Power, W/m^2
self.n = n #Efficiency
self.Qrad = Qrad #net incoming energy flow due to radiation
self.Qabs = Qabs #net heat current flowing out of the hot absorber reservoir
Data = np.empty([len(W),len(N),points], dtype = object) #Contain all param objects
datafile = 'ODwyer.dat'
fout = open(datafile,'w')
fout.write('')
fout.close()
for i in range(len(W)):
for j in range(len(N)):
for x in range(points):
Data[i][j][x] = param(0,0,0,0,0,0)
# Function Paramaters calculates Qrad,Qabs and I for a given T_H,u_H,u_C,N_contact,w,voltage
def Parameters (T_H, u_H, u_C, N_contact, w, voltage):
eqn1 = lambda E: ((E)**3/(np.exp(E/(k*T_S))-1)-(E)**3/(np.exp(E/(k*T_H))-1))
Qrad = ((2*Omega*A*q)/((h**3)*(c**2)))*quad(eqn1,0,max_lim)[0]
eqn2 = lambda E:(E-u_H)*(expit(-(E-u_H)/(k*T_H))-expit(-(E-u_C)/(k*T_C)))*(np.exp(-(E-E_u/2)**2/(w)))
Qabs = ((4*N_contact*q)/h)*quad(eqn2,0,max_lim)[0]
if Qabs < 0:
Qabs = np.inf
error = abs(Qrad-Qabs)
eqn3 = lambda E:(expit(-(E-u_H)/(k*T_H))-expit(-(E-u_C)/(k*T_C)))*(np.exp(-(E-E_u/2)**2/(w)))
I = -((2*N_contact*q)/h)*quad(eqn3,0,max_lim)[0]/A
fout = open(datafile,'a')
fout.write('%.2e\t%.2e\t%.1f\t%.2f\t%.2e\t%.2e\n'%(w,N_contact,T_H,voltage,Qrad,Qabs))
fout.close()
return error, I, Qrad, Qabs
#Progress bar for simulation time (not relevant for calculations)
def progressbar(progress):
if (progress >= 0.01):
t1 = time.perf_counter() - t0
full_time = t1*1/progress*100
timeleft = full_time-t1
if timeleft >= 3600:
timelefthrs = int(round(timeleft/3600,0))
timeleftmins = int((timeleft-timelefthrs*3600)%60)
print('\rSimulation Progress: %.2f%%\t Estimated Time Left: %dh %dm '%(progress,timelefthrs, timeleftmins), end='')
elif timeleft >= 60 and timeleft <3600: # in mins
timeleftmins = int(round(timeleft/60,0))
timeleftsecs = int((timeleft-timeleftmins*60)%60)
print('\rSimulation Progress: %.2f%%\t Estimated Time Left: %dm %ds '%(progress,timeleftmins, timeleftsecs), end='')
else:
print('\rSimulation Progress: %.2f%%\t Estimated Time Left: %ds '%(progress,timeleft), end='')
else:
print('\rSimulation Progress: %.2f%%'%(progress), end='')
def Odwyer(index, counter):
for j in range(len(N)):
for i in range(points): #per V
u_H = E_u+V[i]/2 #Hot absorber electrochemical potential, eV
u_C = E_u-V[i]/2 #Cold Reservoir electrochemical potential, eV
error = np.inf #initialise error between Qrad and Qabs as inf
for x in range(len(Temperatures)):
temperature = Temperatures[x]
diff, I, Qrad, Qabs= Parameters(Temperatures[x], u_H, u_C, N[j], W[index], V[i])
if diff <= error: #if difference between Qabs and Qrad is smaller than previous error, use this Temperature[x]
Data[index][j][i].TH = temperature
Data[index][j][i].Qrad = Qrad
Data[index][j][i].Qabs = Qabs
Data[index][j][i].I = I
Data[index][j][i].P = I*V[i]
Data[index][j][i].n = I*V[i]/(stefan*(T_S**4))
error = abs(diff)
counter += 1
progress = counter/total*100
progressbar(progress)
#Plotting
fig, axs= plt.subplots(2,2, constrained_layout=True)
ax1 = axs[0,0]
ax2 = axs[0,1]
ax3 = axs[1,0]
ax4 = axs[1,1]
for i in range(2):
for j in range(2):
axs[i,j].set_xlim(0,1)
axs[i,j].xaxis.set_major_locator(ticker.MultipleLocator(0.5))
axs[i,j].set_xlabel("Voltage (V)")
ax1.set_ylim(0,T_S)
ax1.set_ylabel("TH (K)")
ax1.yaxis.set_major_locator(ticker.MultipleLocator(2000))
ax2.set_ylim(0,1e8)
ax2.set_ylabel("I (A/m^2)")
ax2.yaxis.set_major_locator(ticker.MultipleLocator(2e7))
ax3.set_ylim(0,1e8)
ax3.set_ylabel("Power (W/m^2)")
ax3.yaxis.set_major_locator(ticker.MultipleLocator(2e7))
ax4.set_ylim(0,1)
ax4.set_ylabel("Efficiency")
ax4.yaxis.set_major_locator(ticker.MultipleLocator(0.2))
TH = np.empty([len(N),points])
I = np.empty([len(N),points])
P = np.empty([len(N),points])
n = np.empty([len(N),points])
for j in range(len(N)):
for x in range(points):
TH[j][x] = Data[index][j][x].TH
I[j][x] = Data[index][j][x].I
P[j][x] = Data[index][j][x].P
n[j][x] = Data[index][j][x].n
#Wurfel's Solution
TH_W = []
I_W = []
P_W = []
n_W = []
for x in range(points):
if V[x] == E_u:
TH_wurfel = 1e20
else:
TH_wurfel = T_C/(1-V[x]/E_u)
TH_W.append(TH_wurfel)
Iwurfel = (stefan)/(E_u)*(T_S**4-TH_wurfel**4)
Pwurfel = stefan*(T_S**4-TH_wurfel**4)*(1-T_C/TH_wurfel)
nwurfel = (T_S**4-TH_wurfel**4)/(T_S**4)*(1-T_C/TH_wurfel)
I_W.append(Iwurfel)
P_W.append(Pwurfel)
n_W.append(nwurfel)
linestyles = ['--','-','-.']
for j in range(len(N)):
for x in range(points):
if TH[j][x] == T_S:
TH[j][x] = 1e8
for i in range(len(N)):
ax1.plot(V,TH[i], label='N = %.0e'%N[i], color = 'black', linestyle = linestyles[i], linewidth = 1)
ax2.plot(V,I[i], label='N = %.0e'%N[i], color = 'black', linestyle = linestyles[i], linewidth = 1)
ax3.plot(V,P[i], label='N = %.0e'%N[i], color = 'black', linestyle = linestyles[i], linewidth = 1)
ax4.plot(V,n[i], label='N = %.0e'%N[i], color = 'black', linestyle = linestyles[i], linewidth = 1)
ax1.plot(V,TH_W, color = 'black', label='Wurfel', linewidth = 3)
ax2.plot(V,I_W, color = 'black', label='Wurfel', linewidth = 3)
ax3.plot(V,P_W, color = 'black', label='Wurfel', linewidth = 3)
ax4.plot(V,n_W, color = 'black', label='Wurfel', linewidth = 3)
fig.suptitle('w = %.0e eV' % W[index])
ax1.legend(loc='upper right', fontsize = 8)
ax2.legend(loc='upper right', fontsize = 8)
ax3.legend(loc='upper right', fontsize = 8)
ax4.legend(loc='upper right', fontsize = 8)
#Saving figure
fig.savefig('w = %.0e eV, pp = %d, num_T = %d.jpg' %(W[index],points,num_T), dpi=800)
return counter
for x in range(len(W)):
counter = Odwyer(x, counter)
# Printing out object values
for x in range(len(W)):
for j in range(len(N)):
print('Parameters for W = %0.e, N = %.0e'%(W[x],N[j]))
for i in range(points):
print('w = %.0e\tV = %.2f\tTH = %.0f\tQrad = %.2e\tQabs = %.2e\tI = %.2e'%(W[x],V[i],Data[x][j][i].TH,Data[x][j][i].Qrad,Data[x][j][i].Qabs,Data[x][j][i].I))
print('\nComplete!')
What I've tried
I have tried changing the upper limit of the integrals from inf to lower values and although it removed the overflow warning for values ~<15, it made Qabs = 0.00e00. I also tried changing arguments for 'limit' and 'epsabs' in the 'quad' function but couldn't get that to work either. Changing the variables 'points' and 'num_T' did not improve the accuracy of my values either. I have also read and tried solutions from relevant posts regarding overflows such as Overflow Post but to no avail. This is my first post so if you require any further information from me to fix my problem, feel free to let me know!
Here's a quick and dirty stdlib (no numpy) script that got something close to the WolframAlpha answer:
from math import exp, pi
C1 = 8.617e-5 * 6000
C2 = 8.617e-5 * 1448
def f(x):
denom1 = exp(x / C1)
denom2 = exp(x / C2)
# did some algebra
difference = (denom2 - denom1) / (denom1 - 1) / (denom2 - 1)
return x ** 3 * difference
bins = 10_000
endpoint = 10
total = 0.0
for i in range(1, bins+1):
x = i * endpoint / bins
total += f(x)
# account for widths
total *= (endpoint / bins)
scaled = float(total) * 2 * pi * 1e-4 / (4.14e-15)**3 / (3e8)**2
print(scaled)
# 4.549838698077388e+22
Part of the problem (I'm guessing, not sure) would be that 1/(a-1) - 1/(b-1) will be wildly imprecise for a and b close to 1, so you can do some algebra to try and fix that, and make it (b-a)/(a-1)/(b-1).
I am plotting 2D images of energy and density distribution. There is always a slight misalignment in the mapping where the very first "columns" seem to go to the last columns during the plot.
I have attach link to for data test file.
Data files
Here is the plot :
Is there anything to prevent this ?
The partial code in plotting is as follows:
import numpy as np
import matplotlib.pyplot as plt
import pylab as pyl
import scipy.stats as ss
import matplotlib.ticker as ticker
import matplotlib.transforms as tr
#%matplotlib inline
pi = 3.1415
n = 5e24 # density plasma
m = 9.109e-31
eps = 8.85e-12
e = 1.6021725e-19
c = 3e8
wp=np.sqrt(n*e*e/(m*eps))
kp = np.sqrt(n*e*e/(m*eps))/c #plasma wavenumber
case=400
## decide on the target range of analysis for multiples
start= 20500
end = 21500
gap = 1000
## Multiples plots
def target_range (start, end, gap):
while start<= end:
yield start
start += gap
for step in target_range(start, end, gap):
fdata =np.genfromtxt('./beam_{}'.format(step)).reshape(-1,6)
## dimension, dt, and superpaticle
xBoxsize = 50e-6 #window size
yBoxsize = 80e-6 #window size
xbind = 10
ybind = 1
dx = 4e-8 #cell size
dy = 4e-7 #cell size
dz = 1e-6 #assume to be same as dy
dt = 1.3209965456e-16
sptcl = 1.6e10
xsub = 0e-6
xmax = dt*step*c
xmin = xmax - xBoxsize
ysub = 1e-7
ymin = ysub #to make our view window
ymax = yBoxsize - ysub
xbins = int((xmax - xmin)/(dx*xbind))
ybins = int((ymax - ymin)/(dy*ybind))
#zbins = int((zmax - zmin)/dz) #option for 3D
# To make or define "data_arr" as a matrix with 2D array size 'xbins x ybins'
data_arr = np.zeros((2,xbins,ybins), dtype=np.float)
for line in fdata:
x = int((line[0]-xmin)/(dx*xbind))
y = int((line[1]-ymin)/(dy*ybind))
#z = int((line[2]-zmin)/dz)
if x >= xbins: x = xbins - 1
if y >= ybins: y = ybins - 1
#if z >= zbins: z = zbins - 1
data_arr[0, x, y] = data_arr[0,x, y] + 1 #cummulative adding up the number of particles
energy_total = np.sqrt(1+ line[2]*line[2]/(c*c)+line[3]*line[3]/(c*c))/0.511
data_arr[1, x, y] += energy_total
#array 1 tells us the energy while array 0 tells us the particles
## make average energy , total energy/particle number
np.errstate(divide='ignore',invalid='ignore')
en_arr = np.true_divide(data_arr[1],data_arr[0]) # total energy/number of particles
en_arr[en_arr == np.inf] = 0
en_arr = np.nan_to_num(en_arr)
en_arr = en_arr.T
## This part is real density of the distribution
data_arr[0]= data_arr[0] * sptcl/dx/dy #in m-3
d = data_arr[0].T
## Plot and save density and energy distribution figures
den_dist=plt.figure(1)
plt.imshow(d,origin='lower', aspect = 'auto',cmap =plt.get_cmap('gnuplot'),extent =(xmin/1e-3,xmax/1e-3,ymin/1e-6,ymax/1e-6))
plt.title('Density_dist [m-3]_{}'.format(step))
plt.xlabel('distance[mm]')
plt.ylabel('y [um]')
plt.colorbar()
plt.show()
den_dist.savefig("./Qen_distribution_{}.png".format(step),format ='png')
#note:cmap: rainbow, hot,jet,gnuplot,plasma
energy_dist=plt.figure(2)
plt.imshow(en_arr, origin ='lower',aspect = 'auto', cmap =plt.get_cmap('jet'),extent =(xmin/1e-3,xmax/1e-3,ymin/1e-6,ymax/1e-6))
plt.title ('Energy_dist [MeV]_{} '.format(step))
plt.xlabel('distance[mm]')
plt.ylabel('y [um]')
plt.colorbar()
plt.show()
energy_dist.savefig("./Qenergy_distribution_{}.png".format(step),format ='png')
I am trying to plot the phase potrait for the equation as defined in my sh2 function in the code below. I know the expected phase plot should be [[expected phase plot][1]][1] [1]: https://i.stack.imgur.com/y1T9Y.png.
However this is my result: [[result][1]: https://i.stack.imgur.com/EuSOm.png.
I am using integrate.odeint can anyone suggest what I could change in the could below or if there would be best to use another algorithm that would give me a closer result to th expected.
Please find my code :
import matplotlib.pyplot as plt
import numpy as np
from numpy import sin
import scipy.integrate as integrate
from math import *
g = 9.81
l = 1.6
l_big = 2.0
l_small = 1.6
m = 0.01
alpha = l_big-l_small
k = 10*(10**40)
def sh2(r1,t):
theta1,omega1 = r1
sh2_theta1 = omega1
sh2_omega1 = -g*(l + ((1/2)*alpha*(1-np.tanh(theta1*omega1*k))))*sin(theta1)
return np.array([sh2_theta1, sh2_omega1],float)
init_state = np.radians([30.0,0])
dt = 1/10.0
time = np.arange(0,10.0,dt)
timexo = np.arange(0,10.0,dt)
state2 = integrate.odeint(sh2,init_state,time)
print(len(state2),len(timexo))
state2_plot = np.transpose(state2[0:2500])
plt.plot(timexo[0:2500],state2_plot[1], '--m', label = r'$\theta = \frac{\pi}{6}$')
plt.xlabel('Time t (s) ')
plt.ylabel('Angular Velocity' ' ' r'$\dot{\theta}$')
plt.show()
#code for phase plot
# initial values
x_0 = 0.0 # intial angular position
v_0 = 1.0 # initial angular momentum
t_0 = 0 # initial time
# initial y-vector from initial position and momentum
y0 = np.array([x_0,v_0])
# max value of time and points in time to integrate to
t_max = 10
N_spacing_in_t = 10000
# create vector of time points you want to evaluate
t = np.linspace(t_0,t_max,N_spacing_in_t)
# create vector of positions for those times
y_result = integrate.odeint(sh2, init_state, t)
# get angle and angular momentum
angle = y_result[:,0]
angular_velocity = y_result[:,1]
# plot result
fig = plt.figure()
plt.plot(angle, angular_velocity,'--k',lw=1)
plt.xlabel('Angle' ' ' r'$\theta$')
plt.ylabel(r'Angular Velocity' r' $\dot{\theta}$')
plt.gcf().savefig('pumping.png',dpi=300)
plt.show()
Thank you for tour time
I'm going through Strogatz's Nonlinear Dynamics and Chaos and I've hit a snag in chapter 2 Exercise 2.8.1. (Educator flag: I've graduated so this isn't for a class, I'm just trying to get back into the numerical solving of differential equations) It's a pretty simple differential equation and I can plot individual solution curves given different initial conditions but I'm trying to use quiver or streamplot to superimpose individual solutions on top of the vector field.
My problem is in understanding how to translate the vector field plots for similar problems in the dy/dx form found here over to the dx/dt form that's primarily tackled in Strogatz's book.
Given that the x vector that's defined in the logistic function is only one dimensional I'm having a hard time reasoning out how express the u and v flows in quiver or streamplot since the problem only seems to have a u flow. It's probably super easy and is being over-thought but any guidance or assistance would be much appreciated!
So far I have the following:
# 2.8.1
# Plot the vector field and some trajectories for xdot = x(1-x) given
# some different initial conditions for the logistic equation with carrying
# capacity K = 1
# dx/dt = x(1-x)
# Imports:
from __future__ import division
from scipy import *
import numpy as np
import pylab
import matplotlib as mp
from matplotlib import pyplot as plt
import sys
import math as mt
def logistic(x,t):
return np.array([x[0]*(1-x[0])])
def RK4(t0 = 0, x0 = np.array([1]), t1 = 5 , dt = 0.01, ng = None):
tsp = np.arange(t0, t1, dt)
Nsize = np.size(tsp)
X = np.empty((Nsize, np.size(x0)))
X[0] = x0
for i in range(1, Nsize):
k1 = ng(X[i-1],tsp[i-1])
k2 = ng(X[i-1] + dt/2*k1, tsp[i-1] + dt/2)
k3 = ng(X[i-1] + dt/2*k2, tsp[i-1] + dt/2)
k4 = ng(X[i-1] + dt*k3, tsp[i-1] + dt)
X[i] = X[i-1] + dt/6*(k1 + 2*k2 + 2*k3 + k4)
return X
def tplot():
t0 = 0
t1 = 10
dt = 0.02
tsp = np.arange(t0,t1,dt)
X = RK4(x0 = np.array([2]), t1 = 10,dt = 0.02, ng = logistic)
Y = RK4(x0 = np.array([0.01]), t1 = 10,dt = 0.02, ng = logistic)
Z = RK4(x0 = np.array([0.5]), t1 = 10,dt = 0.02, ng = logistic)
P = RK4(x0 = np.array([3]), t1 = 10,dt = 0.02, ng = logistic)
Q = RK4(x0 = np.array([0.1]), t1 = 10,dt = 0.02, ng = logistic)
R = RK4(x0 = np.array([1.5]), t1 = 10,dt = 0.02, ng = logistic)
O = RK4(x0 = np.array([1]), t1 = 10,dt = 0.02, ng = logistic)
pylab.figure()
pylab.plot(tsp,X)
pylab.plot(tsp,Y)
pylab.plot(tsp,Z)
pylab.plot(tsp,P)
pylab.plot(tsp,Q)
pylab.plot(tsp,R)
pylab.plot(tsp,O)
pylab.title('Logistic Equation - K=1')
pylab.xlabel('Time')
pylab.ylabel('Xdot')
pylab.show()
print tplot()
image here
To graph a slope from a derivative (like, dx/dt), you can first find dx/dt, and then use a fixed dt to calculate dx. Then, at each (t, x) of interest, plot the little line segment from (t,x) to (t+dt, x+dx).
Here's an example for your equation dx/dt = x(1-x). (The Strogatz picture doesn't have arrowheads so I removed them too.)
import numpy as np
import matplotlib.pyplot as plt
times = np.linspace(0, 10, 20)
x = np.linspace(0 ,2, 20)
T, X = np.meshgrid(times, x) # make a grid that roughly matches the Strogatz grid
dxdt = X*(1-X) # the equation of interest
dt = .5*np.ones(X.shape) # a constant value (.5 is just so segments don't run into each other -- given spacing of times array
dx = dxdt * dt # given dt, now calc dx for the line segment
plt.quiver(T, X, dt, dx, headwidth=0., angles='xy', scale=15.)
plt.show()
wonkybadonk: For the difference in slope of the plotted trajectories and the plotted vector field seem to be due to the fact that your vector field are not steep enough. Make sure that
dx = dxdt*dt; (point by point multiplication, not a dot product)
and that you added "angles='xy'" as a quiver argument. (see tom10 post).
Main Problem: How can the scipy.signal.cwt() function be inversed.
I have seen where Matlab has an inverse continuous wavelet transform function which will return the original form of the data by inputting the wavelet transform, although you can filter out the slices you don't want.
MATALAB inverse cwt funciton
Since scipy doesn't appear to have the same function, I have been trying to figure out how to get the data back in the same form, while removing the noise and background.
How do I do this?
I tried squaring it to remove negative values, but this gives me values way to large and not quite right.
Here is what I have been trying:
# Compute the wavelet transform
widths = range(1,11)
cwtmatr = signal.cwt(xy['y'], signal.ricker, widths)
# Maybe we multiple by the original data? and square?
WT_to_original_data = (xy['y'] * cwtmatr)**2
And here is a fully compilable short script to show you the type of data I am trying to get and what I have etc.:
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
# Make some random data with peaks and noise
def make_peaks(x):
bkg_peaks = np.array(np.zeros(len(x)))
desired_peaks = np.array(np.zeros(len(x)))
# Make peaks which contain the data desired
# (Mid range/frequency peaks)
for i in range(0,10):
center = x[-1] * np.random.random() - x[0]
amp = 60 * np.random.random() + 10
width = 10 * np.random.random() + 5
desired_peaks += amp * np.e**(-(x-center)**2/(2*width**2))
# Also make background peaks (not desired)
for i in range(0,3):
center = x[-1] * np.random.random() - x[0]
amp = 40 * np.random.random() + 10
width = 100 * np.random.random() + 100
bkg_peaks += amp * np.e**(-(x-center)**2/(2*width**2))
return bkg_peaks, desired_peaks
x = np.array(range(0, 1000))
bkg_peaks, desired_peaks = make_peaks(x)
y_noise = np.random.normal(loc=30, scale=10, size=len(x))
y = bkg_peaks + desired_peaks + y_noise
xy = np.array( zip(x,y), dtype=[('x',float), ('y',float)])
# Compute the wavelet transform
# I can't figure out what the width is or does?
widths = range(1,11)
# Ricker is 2nd derivative of Gaussian
# (*close* to what *most* of the features are in my data)
# (They're actually Lorentzians and Breit-Wigner-Fano lines)
cwtmatr = signal.cwt(xy['y'], signal.ricker, widths)
# Maybe we multiple by the original data? and square?
WT = (xy['y'] * cwtmatr)**2
# plot the data and results
fig = plt.figure()
ax_raw_data = fig.add_subplot(4,3,1)
ax = {}
for i in range(0, 11):
ax[i] = fig.add_subplot(4,3, i+2)
ax_desired_transformed_data = fig.add_subplot(4,3,12)
ax_raw_data.plot(xy['x'], xy['y'], 'g-')
for i in range(0,10):
ax[i].plot(xy['x'], WT[i])
ax_desired_transformed_data.plot(xy['x'], desired_peaks, 'k-')
fig.tight_layout()
plt.show()
This script will output this image:
Where the first plot is the raw data, the middle plots are the wavelet transforms and the last plot is what I want to get out as the processed (background and noise removed) data.
Does anyone have any suggestions? Thank you so much for the help.
I ended up finding a package which provides an inverse wavelet transform function called mlpy. The function is mlpy.wavelet.uwt. This is the compilable script I ended up with which may interest people if they are trying to do noise or background removal:
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
import mlpy.wavelet as wave
# Make some random data with peaks and noise
############################################################
def gen_data():
def make_peaks(x):
bkg_peaks = np.array(np.zeros(len(x)))
desired_peaks = np.array(np.zeros(len(x)))
# Make peaks which contain the data desired
# (Mid range/frequency peaks)
for i in range(0,10):
center = x[-1] * np.random.random() - x[0]
amp = 100 * np.random.random() + 10
width = 10 * np.random.random() + 5
desired_peaks += amp * np.e**(-(x-center)**2/(2*width**2))
# Also make background peaks (not desired)
for i in range(0,3):
center = x[-1] * np.random.random() - x[0]
amp = 80 * np.random.random() + 10
width = 100 * np.random.random() + 100
bkg_peaks += amp * np.e**(-(x-center)**2/(2*width**2))
return bkg_peaks, desired_peaks
# make x axis
x = np.array(range(0, 1000))
bkg_peaks, desired_peaks = make_peaks(x)
avg_noise_level = 30
std_dev_noise = 10
size = len(x)
scattering_noise_amp = 100
scat_center = 100
scat_width = 15
scat_std_dev_noise = 100
y_scattering_noise = np.random.normal(scattering_noise_amp, scat_std_dev_noise, size) * np.e**(-(x-scat_center)**2/(2*scat_width**2))
y_noise = np.random.normal(avg_noise_level, std_dev_noise, size) + y_scattering_noise
y = bkg_peaks + desired_peaks + y_noise
xy = np.array( zip(x,y), dtype=[('x',float), ('y',float)])
return xy
# Random data Generated
#############################################################
xy = gen_data()
# Make 2**n amount of data
new_y, bool_y = wave.pad(xy['y'])
orig_mask = np.where(bool_y==True)
# wavelet transform parameters
levels = 8
wf = 'h'
k = 2
# Remove Noise first
# Wave transform
wt = wave.uwt(new_y, wf, k, levels)
# Matrix of the difference between each wavelet level and the original data
diff_array = np.array([(wave.iuwt(wt[i:i+1], wf, k)-new_y) for i in range(len(wt))])
# Index of the level which is most similar to original data (to obtain smoothed data)
indx = np.argmin(np.sum(diff_array**2, axis=1))
# Use the wavelet levels around this region
noise_wt = wt[indx:indx+1]
# smoothed data in 2^n length
new_y = wave.iuwt(noise_wt, wf, k)
# Background Removal
error = 10000
errdiff = 100
i = -1
iter_y_dict = {0:np.copy(new_y)}
bkg_approx_dict = {0:np.array([])}
while abs(errdiff)>=1*10**-24:
i += 1
# Wave transform
wt = wave.uwt(iter_y_dict[i], wf, k, levels)
# Assume last slice is lowest frequency (background approximation)
bkg_wt = wt[-3:-1]
bkg_approx_dict[i] = wave.iuwt(bkg_wt, wf, k)
# Get the error
errdiff = error - sum(iter_y_dict[i] - bkg_approx_dict[i])**2
error = sum(iter_y_dict[i] - bkg_approx_dict[i])**2
# Make every peak higher than bkg_wt
diff = (new_y - bkg_approx_dict[i])
peak_idxs_to_remove = np.where(diff>0.)[0]
iter_y_dict[i+1] = np.copy(new_y)
iter_y_dict[i+1][peak_idxs_to_remove] = np.copy(bkg_approx_dict[i])[peak_idxs_to_remove]
# new data without noise and background
new_y = new_y[orig_mask]
bkg_approx = bkg_approx_dict[len(bkg_approx_dict.keys())-1][orig_mask]
new_data = diff[orig_mask]
##############################################################
# plot the data and results
fig = plt.figure()
ax_raw_data = fig.add_subplot(121)
ax_WT = fig.add_subplot(122)
ax_raw_data.plot(xy['x'], xy['y'], 'g')
for bkg in bkg_approx_dict.values():
ax_raw_data.plot(xy['x'], bkg[orig_mask], 'k')
ax_WT.plot(xy['x'], new_data, 'y')
fig.tight_layout()
plt.show()
And here is the output I am getting now:
As you can see, there is still a problem with the background removal (it shifts to the right after each iteration), but it is a different question which I will address here.