In [1]:
%matplotlib inline
%load_ext autoreload
%autoreload 2
import scipy as sp
import numpy as np
import matplotlib.pyplot as plt
import mousai as ms
from scipy import pi, sin
In [3]:
# Test that all is working.
# f_tol adjusts accuracy. This is smaller than reasonable, but illustrative of usage.
t, x, e, amps, phases = ms.hb_time(ms.duff_osc, sp.array([[0,1,-1]]), .7, f_tol = 1e-12)
print('Equation errors (should be zero): ',e)
print('Constant term of FFT of signal should be zero: ', ms.fftp.fft(x)[0,0])
In [3]:
# Using more harmonics.
t, x, e, amps, phases = ms.hb_time(ms.duff_osc, x0 = sp.array([[0,1,-1]]), omega = .7, num_harmonics= 7)
print('Equation errors (should be zero): ', e)
print('Constant term of FFT of signal should be zero: ', ms.fftp.fft(x)[0,0])
Sometimes we can improve just by restarting from the prior end point. Sometimes, we just think it's improved.
In [4]:
t, x, e, amps, phases = ms.hb_time(ms.duff_osc, x0 = x, omega = .7, num_harmonics= 7)
print('Errors: ', e)
print('Constant term of FFT of signal should be zero: ', ms.fftp.fft(x)[0,0])
In [5]:
# Let's get a smoother response
time, xc = ms.time_history(t,x)
plt.plot(time,xc.T,t,x.T,'*')
plt.grid(True)
print('The average for this problem is known to be zero, we got', sp.average(x))
In [26]:
def duff_osc2(x, v, params):
omega = params['omega']
t = params['cur_time']
return np.array([[-x-.1*x**3-.1*v+1*sin(omega*t)]])
In [27]:
t, x, e, amps, phases = ms.hb_time(duff_osc2, sp.array([[0,1,-1]]), .7, num_harmonics=5)
print(x,e)
print('Constant term of FFT of signal should be zero: ', ms.fftp.fft(x)[0,0])
time, xc = ms.time_history(t,x)
plt.plot(time, xc.T, t, x.T, '*')
plt.grid(True)
In [28]:
omega = sp.linspace(0.1,3,200)+1/200
amp = sp.zeros_like(omega)
x = sp.array([[0,-1,1,0,0]])
for i, freq in enumerate(omega):
#print(i,freq,x)
try:
t, x, e, amps, phases = ms.hb_time(duff_osc2, x, freq)#, f_tol = 1e-10)#, callback = resid)
amp[i]=amps[0]
except:
amp[i] = sp.nan
plt.plot(omega, amp)
Out[28]:
The break is an indicative of a break in the branch and is actually a result of the solution being unstable. Not the system, but the solution. By that we mean that while this is considered a solution, it isn't one that will actually continue in a real situation and another solution will necessarily be found.
A simple solution is to change the starting guess to be away from the solution and see if it finds another one. Indeed that happens.
In [29]:
omega = sp.linspace(0.1,3,90)+1/200
amp = sp.zeros_like(omega)
x = sp.array([[0,-1,1]])
for i, freq in enumerate(omega):
#print(i,freq,x)
#print(sp.average(x))
x = x-sp.average(x)
try:
t, x, e, amps, phases = ms.hb_time(duff_osc2, x, freq, num_harmonics=4, verbose = False, f_tol = 1e-6)#, callback = resid)
amp[i]=amps[0]
except:
amp[i] = sp.nan
plt.plot(omega, amp)
Out[29]:
In [30]:
omegal = sp.arange(3,.03,-1/200)+1/200
ampl = sp.zeros_like(omegal)
x = sp.array([[0,-1,1]])
for i, freq in enumerate(omegal):
# Here we try to obtain solutions, but if they don't work,
# we ignore them by inserting `np.nan` values.
x = x-sp.average(x)
try:
t, x, e, amps, phases = ms.hb_time(duff_osc2, x, freq, num_harmonics=4, f_tol = 1e-6)#, callback = resid)
ampl[i]=amps[0]
except:
ampl[i] = sp.nan
plt.plot(omegal, ampl)
Out[30]:
In [31]:
plt.plot(omegal,ampl)
plt.plot(omega,amp)
#plt.axis([0,3, 0, 10.5])
Out[31]:
In [32]:
from scipy.optimize import newton_krylov
In [33]:
def duff_amp_resid(a):
return (mu**2+(sigma-3/8*alpha/omega_0*a**2)**2)*a**2-(k**2)/4/omega_0**2
In [34]:
mu = 0.05 # damping
k = 1 # excitation amplitude
sigma = -0.9 #detuning
omega_0 = 1 # driving frequency
alpha = 0.1 # cubic coefficient
In [35]:
newton_krylov(duff_amp_resid,-.1)
Out[35]:
In [36]:
sigmas = sp.linspace(-1,3,200)
amplitudes = sp.zeros_like(sigmas)
x = newton_krylov(duff_amp_resid,1)
for i, sigma in enumerate(sigmas):
try:
amplitudes[i] = newton_krylov(duff_amp_resid,x)
x = amplitudes[i]
except:
amplitudes[i] = newton_krylov(duff_amp_resid,0)
x = amplitudes[i]
plt.plot(sigmas,amplitudes)
Out[36]:
In [37]:
sigmas = sp.linspace(-1,3,200)
sigmasr = sigmas[::-1]
amplitudesr = sp.zeros_like(sigmas)
x = newton_krylov(duff_amp_resid,3)
for i, sigma in enumerate(sigmasr):
try:
amplitudesr[i] = newton_krylov(duff_amp_resid,x)
x = amplitudesr[i]
except:
amplitudesr[i] = sp.nan#newton_krylov(duff_amp_resid,0)
x = amplitudesr[i]
plt.plot(sigmasr,amplitudesr)
Out[37]:
In [38]:
plt.plot(sigmasr,amplitudesr)
plt.plot(sigmas,amplitudes)
Out[38]:
In [14]:
def duff_osc2(x, v, params):
omega = params['omega']
t = params['cur_time']
return np.array([[-x-.1*x**3-.1*v+1*sin(omega*t)]])
_,_,_,a,_ = ms.hb_time(duff_osc2, sp.array([[0,1,-1]]), 0.7, num_harmonics=1)
print(a)
In [20]:
_,_,_,a,_ = ms.hb_time(lambda x,v, params:np.array([[-x-.1*x**3-.1*v+1*sin(0.7*params['cur_time'])]]), sp.array([[0,1,-1]]), .7, num_harmonics=1)
a
Out[20]:
Two things to note:
n by 1 array.