download
raw
10 kB
import numpy as np
#import matplotlib.pyplot as plt
import scitools.std as plt
def solver(I, V, m, b, s, F, dt, T, damping='linear'):
"""
Solve m*u'' + f(u') + s(u) = F(t) for t in (0,T],
u(0)=I and u'(0)=V,
by a central finite difference method with time step dt.
If damping is 'linear', f(u')=b*u, while if damping is
'quadratic', f(u')=b*u'*abs(u').
F(t) and s(u) are Python functions.
"""
dt = float(dt); b = float(b); m = float(m) # avoid integer div.
Nt = int(round(T/dt))
u = np.zeros(Nt+1)
t = np.linspace(0, Nt*dt, Nt+1)
u[0] = I
if damping == 'linear':
u[1] = u[0] + dt*V + dt**2/(2*m)*(-b*V - s(u[0]) + F(t[0]))
elif damping == 'quadratic':
u[1] = u[0] + dt*V + \
dt**2/(2*m)*(-b*V*abs(V) - s(u[0]) + F(t[0]))
for n in range(1, Nt):
if damping == 'linear':
u[n+1] = (2*m*u[n] + (b*dt/2 - m)*u[n-1] +
dt**2*(F(t[n]) - s(u[n])))/(m + b*dt/2)
elif damping == 'quadratic':
u[n+1] = (2*m*u[n] - m*u[n-1] + b*u[n]*abs(u[n] - u[n-1])
+ dt**2*(F(t[n]) - s(u[n])))/\
(m + b*abs(u[n] - u[n-1]))
return u, t
def visualize(u, t, title='', filename='tmp'):
plt.plot(t, u, 'b-')
plt.xlabel('t')
plt.ylabel('u')
dt = t[1] - t[0]
plt.title('dt=%g' % dt)
umin = 1.2*u.min(); umax = 1.2*u.max()
plt.axis([t[0], t[-1], umin, umax])
plt.title(title)
plt.savefig(filename + '.png')
plt.savefig(filename + '.pdf')
plt.show()
import sympy as sym
def test_constant():
"""Verify a constant solution."""
u_exact = lambda t: I
I = 1.2; V = 0; m = 2; b = 0.9
w = 1.5
s = lambda u: w**2*u
F = lambda t: w**2*u_exact(t)
dt = 0.2
T = 2
u, t = solver(I, V, m, b, s, F, dt, T, 'linear')
difference = np.abs(u_exact(t) - u).max()
tol = 1E-13
assert difference < tol
u, t = solver(I, V, m, b, s, F, dt, T, 'quadratic')
difference = np.abs(u_exact(t) - u).max()
assert difference < tol
def lhs_eq(t, m, b, s, u, damping='linear'):
"""Return lhs of differential equation as sympy expression."""
v = sym.diff(u, t)
if damping == 'linear':
return m*sym.diff(u, t, t) + b*v + s(u)
else:
return m*sym.diff(u, t, t) + b*v*sym.Abs(v) + s(u)
def test_quadratic():
"""Verify a quadratic solution."""
I = 1.2; V = 3; m = 2; b = 0.9
s = lambda u: 4*u
t = sym.Symbol('t')
dt = 0.2
T = 2
q = 2 # arbitrary constant
u_exact = I + V*t + q*t**2
F = sym.lambdify(t, lhs_eq(t, m, b, s, u_exact, 'linear'))
u_exact = sym.lambdify(t, u_exact, modules='numpy')
u1, t1 = solver(I, V, m, b, s, F, dt, T, 'linear')
diff = np.abs(u_exact(t1) - u1).max()
tol = 1E-13
assert diff < tol
# In the quadratic damping case, u_exact must be linear
# in order exactly recover this solution
u_exact = I + V*t
F = sym.lambdify(t, lhs_eq(t, m, b, s, u_exact, 'quadratic'))
u_exact = sym.lambdify(t, u_exact, modules='numpy')
u2, t2 = solver(I, V, m, b, s, F, dt, T, 'quadratic')
diff = np.abs(u_exact(t2) - u2).max()
assert diff < tol
def test_sinusoidal():
"""Verify a numerically exact sinusoidal solution when b=F=0."""
from math import asin
def u_exact(t):
w_numerical = 2/dt*np.arcsin(w*dt/2)
return I*np.cos(w_numerical*t)
I = 1.2; V = 0; m = 2; b = 0
w = 1.5 # fix the frequency
s = lambda u: m*w**2*u
F = lambda t: 0
dt = 0.2
T = 6
u, t = solver(I, V, m, b, s, F, dt, T, 'linear')
diff = np.abs(u_exact(t) - u).max()
tol = 1E-14
assert diff < tol
u, t = solver(I, V, m, b, s, F, dt, T, 'quadratic')
diff = np.abs(u_exact(t) - u).max()
assert diff < tol
def test_mms():
"""Use method of manufactured solutions."""
m = 4.; b = 1
w = 1.5
t = sym.Symbol('t')
u_exact = 3*sym.exp(-0.2*t)*sym.cos(1.2*t)
I = u_exact.subs(t, 0).evalf()
V = sym.diff(u_exact, t).subs(t, 0).evalf()
u_exact_py = sym.lambdify(t, u_exact, modules='numpy')
s = lambda u: u**3
dt = 0.2
T = 6
errors_linear = []
errors_quadratic = []
# Run grid refinements and compute exact error
for i in range(5):
F_formula = lhs_eq(t, m, b, s, u_exact, 'linear')
F = sym.lambdify(t, F_formula)
u1, t1 = solver(I, V, m, b, s, F, dt, T, 'linear')
error = np.sqrt(np.sum((u_exact_py(t1) - u1)**2)*dt)
errors_linear.append((dt, error))
F_formula = lhs_eq(t, m, b, s, u_exact, 'quadratic')
#print sym.latex(F_formula, mode='plain')
F = sym.lambdify(t, F_formula)
u2, t2 = solver(I, V, m, b, s, F, dt, T, 'quadratic')
error = np.sqrt(np.sum((u_exact_py(t2) - u2)**2)*dt)
errors_quadratic.append((dt, error))
dt /= 2
# Estimate convergence rates
tol = 0.05
for errors in errors_linear, errors_quadratic:
for i in range(1, len(errors)):
dt, error = errors[i]
dt_1, error_1 = errors[i-1]
r = np.log(error/error_1)/np.log(dt/dt_1)
assert abs(r - 2.0) < tol
def main():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--I', type=float, default=1.0)
parser.add_argument('--V', type=float, default=0.0)
parser.add_argument('--m', type=float, default=1.0)
parser.add_argument('--b', type=float, default=0.0)
parser.add_argument('--s', type=str, default='u')
parser.add_argument('--F', type=str, default='0')
parser.add_argument('--dt', type=float, default=0.05)
parser.add_argument('--T', type=float, default=10)
parser.add_argument('--window_width', type=float, default=30.,
help='Number of periods in a window')
parser.add_argument('--damping', type=str, default='linear')
parser.add_argument('--savefig', action='store_true')
# Hack to allow --SCITOOLS options (scitools.std reads this argument
# at import)
parser.add_argument('--SCITOOLS_easyviz_backend', default='matplotlib')
a = parser.parse_args()
from scitools.std import StringFunction
s = StringFunction(a.s, independent_variable='u')
F = StringFunction(a.F, independent_variable='t')
I, V, m, b, dt, T, window_width, savefig, damping = \
a.I, a.V, a.m, a.b, a.dt, a.T, a.window_width, a.savefig, \
a.damping
u, t = solver(I, V, m, b, s, F, dt, T, damping)
num_periods = plot_empirical_freq_and_amplitude(u, t)
num_periods = 4
if num_periods <= 40:
figure()
visualize(u, t)
else:
visualize_front(u, t, window_width, savefig)
visualize_front_ascii(u, t)
show()
def plot_empirical_freq_and_amplitude(u, t):
minima, maxima = minmax(t, u)
p = periods(maxima)
a = amplitudes(minima, maxima)
plt.figure()
from math import pi
w = 2*pi/p
plt.plot(range(len(p)), w, 'r-')
plt.hold('on')
plt.plot(range(len(a)), a, 'b-')
ymax = 1.1*max(w.max(), a.max())
ymin = 0.9*min(w.min(), a.min())
plt.axis([0, max(len(p), len(a)), ymin, ymax])
plt.legend(['estimated frequency', 'estimated amplitude'],
loc='upper right')
return len(maxima)
def visualize_front(u, t, window_width, savefig=False):
"""
Visualize u and the exact solution vs t, using a
moving plot window and continuous drawing of the
curves as they evolve in time.
Makes it easy to plot very long time series.
P is the approximate duration of one period.
"""
import scitools.std as st
from scitools.MovingPlotWindow import MovingPlotWindow
umin = 1.2*u.min(); umax = -umin
plot_manager = MovingPlotWindow(
window_width=window_width,
dt=t[1]-t[0],
yaxis=[umin, umax],
mode='continuous drawing')
for n in range(1,len(u)):
if plot_manager.plot(n):
s = plot_manager.first_index_in_plot
st.plot(t[s:n+1], u[s:n+1], 'r-1',
title='t=%6.3f' % t[n],
axis=plot_manager.axis(),
show=not savefig) # drop window if savefig
if savefig:
print 't=%g' % t[n]
st.savefig('tmp_vib%04d.png' % n)
plot_manager.update(n)
def visualize_front_ascii(u, t, fps=10):
"""
Plot u and the exact solution vs t line by line in a
terminal window (only using ascii characters).
Makes it easy to plot very long time series.
"""
from scitools.avplotter import Plotter
import time
umin = 1.2*u.min(); umax = -umin
p = Plotter(ymin=umin, ymax=umax, width=60, symbols='+o')
for n in range(len(u)):
print p.plot(t[n], u[n]), '%.2f' % (t[n])
time.sleep(1/float(fps))
def minmax(t, u):
"""
Compute all local minima and maxima of the function u(t),
represented by discrete points in the arrays u and t.
Return lists minima and maxima of (t[i],u[i]) extreme points.
"""
minima = []; maxima = []
for n in range(1, len(u)-1, 1):
if u[n-1] > u[n] < u[n+1]:
minima.append((t[n], u[n]))
if u[n-1] < u[n] > u[n+1]:
maxima.append((t[n], u[n]))
return minima, maxima
def periods(extrema):
"""
Given a list of (t,u) points of the maxima or minima,
return an array of the corresponding local periods.
"""
p = [extrema[n][0] - extrema[n-1][0]
for n in range(1, len(extrema))]
return array(p)
def amplitudes(minima, maxima):
"""
Given a list of (t,u) points of the minima and maxima of
u, return an array of the corresponding local amplitudes.
"""
# Compare first maxima with first minima and so on
a = [(abs(maxima[n][1] - minima[n][1]))/2.0
for n in range(min(len(minima),len(maxima)))]
return array(a)
if __name__ == '__main__':
main()
#test_constant()
#test_sinusoidal()
#test_mms()
#test_quadratic()

Xet Storage Details

Size:
10 kB
·
Xet hash:
2f269e3b1c1cc080774e1a901a479541ac1fc0d6d478318d91a5de3f79e622c2

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.