Skip to content

Commit 238bd39

Browse files
author
sveinlin
committed
added trunc dir
1 parent f5ac90c commit 238bd39

3 files changed

Lines changed: 233 additions & 0 deletions

File tree

src/trunc/trunc_decay_FE.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
Empirical estimation of the truncation error in a scheme.
3+
Examples on the Forward Euler scheme for the decay ODE u'=-au.
4+
"""
5+
import numpy as np
6+
import trunc_empir
7+
8+
def decay_FE(dt, N):
9+
dt = float(dt)
10+
t = np.linspace(0, N*dt, N+1)
11+
u_e = I*np.exp(-a*t) # exact solution, I and a are global
12+
u = u_e # naming convention when writing up the scheme
13+
R = np.zeros(N)
14+
15+
for n in range(0, N):
16+
R[n] = (u[n+1] - u[n])/dt + a*u[n]
17+
18+
# Theoretical expression for the trunction error
19+
R_a = 0.5*I*(-a)**2*np.exp(-a*t)*dt
20+
21+
return R, t[:-1], R_a[:-1]
22+
23+
if __name__ == '__main__':
24+
I = 1; a = 2 # global variables needed in decay_FE
25+
trunc_empir.estimate(decay_FE, T=2.5, N_0=6, m=4, makeplot=True)

src/trunc/trunc_empir.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
Empirical estimation of the truncation error in a scheme
3+
for a problem with one independent variable.
4+
"""
5+
import numpy as np
6+
import scitools.std as plt
7+
8+
def estimate(truncation_error, T, N_0, m, makeplot=True):
9+
"""
10+
Compute the truncation error in a problem with one independent
11+
variable, using m meshes, and estimate the convergence
12+
rate of the truncation error.
13+
14+
The user-supplied function truncation_error(dt, N) computes
15+
the truncation error on a uniform mesh with N intervals of
16+
length dt::
17+
18+
R, t, R_a = truncation_error(dt, N)
19+
20+
where R holds the truncation error at points in the array t,
21+
and R_a are the corresponding theoretical truncation error
22+
values (None if not available).
23+
24+
The truncation_error function is run on a series of meshes
25+
with 2**i*N_0 intervals, i=0,1,...,m-1.
26+
The values of R and R_a are restricted to the coarsest mesh.
27+
and based on these data, the convergence rate of R (pointwise)
28+
and time-integrated R can be estimated empirically.
29+
"""
30+
N = [2**i*N_0 for i in range(m)]
31+
32+
R_I = np.zeros(m) # time-integrated R values on various meshes
33+
R = [None]*m # time series of R restricted to coarsest mesh
34+
R_a = [None]*m # time series of R_a restricted to coarsest mesh
35+
dt = np.zeros(m)
36+
legends_R = []; legends_R_a = [] # all legends of curves
37+
38+
for i in range(m):
39+
dt[i] = T/float(N[i])
40+
R[i], t, R_a[i] = truncation_error(dt[i], N[i])
41+
42+
R_I[i] = np.sqrt(dt[i]*np.sum(R[i]**2))
43+
44+
if i == 0:
45+
t_coarse = t # the coarsest mesh
46+
47+
stride = N[i]/N_0
48+
R[i] = R[i][::stride] # restrict to coarsest mesh
49+
R_a[i] = R_a[i][::stride]
50+
51+
if makeplot:
52+
plt.figure(1)
53+
plt.plot(t_coarse, R[i], log='y')
54+
legends_R.append('N=%d' % N[i])
55+
plt.hold('on')
56+
57+
plt.figure(2)
58+
plt.plot(t_coarse, R_a[i] - R[i], log='y')
59+
plt.hold('on')
60+
legends_R_a.append('N=%d' % N[i])
61+
62+
if makeplot:
63+
plt.figure(1)
64+
plt.xlabel('time')
65+
plt.ylabel('pointwise truncation error')
66+
plt.legend(legends_R)
67+
plt.savefig('R_series.png')
68+
plt.savefig('R_series.pdf')
69+
plt.figure(2)
70+
plt.xlabel('time')
71+
plt.ylabel('pointwise error in estimated truncation error')
72+
plt.legend(legends_R_a)
73+
plt.savefig('R_error.png')
74+
plt.savefig('R_error.pdf')
75+
76+
# Convergence rates
77+
r_R_I = convergence_rates(dt, R_I)
78+
print 'R integrated in time; r:',
79+
print ' '.join(['%.1f' % r for r in r_R_I])
80+
R = np.array(R) # two-dim. numpy array
81+
r_R = [convergence_rates(dt, R[:,n])[-1]
82+
for n in range(len(t_coarse))]
83+
84+
# Plot convergence rates
85+
if makeplot:
86+
plt.figure()
87+
plt.plot(t_coarse, r_R)
88+
plt.xlabel('time')
89+
plt.ylabel('r')
90+
plt.axis([t_coarse[0], t_coarse[-1], 0, 2.5])
91+
plt.title('Pointwise rate $r$ in truncation error $\sim\Delta t^r$')
92+
plt.savefig('R_rate_series.png')
93+
plt.savefig('R_rate_series.pdf')
94+
95+
def convergence_rates(h, E):
96+
"""
97+
Given a sequence of discretization parameters in the list h,
98+
and corresponding errors in the list E,
99+
compute the convergence rate of two successive (h[i], E[i])
100+
and (h[i+1],E[i+1]) experiments, assuming the model E=C*h^r
101+
(for small enough h).
102+
"""
103+
from math import log
104+
r = [log(E[i]/E[i-1])/log(h[i]/h[i-1])
105+
for i in range(1, len(h))]
106+
return r

src/trunc/truncation_errors.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import sympy as sym
2+
3+
class TaylorSeries:
4+
"""Class for symbolic Taylor series."""
5+
def __init__(self, f, num_terms=4):
6+
self.f = f
7+
self.N = num_terms
8+
# Introduce symbols for the derivatives
9+
self.df = [f]
10+
for i in range(1, self.N+1):
11+
self.df.append(sym.Symbol('D%d%s' % (i, f.name)))
12+
13+
def __call__(self, h):
14+
"""Return the truncated Taylor series at x+h."""
15+
terms = self.f
16+
for i in range(1, self.N+1):
17+
terms += sym.Rational(1, sym.factorial(i))*self.df[i]*h**i
18+
return terms
19+
20+
21+
class DiffOp:
22+
"""Class for discrete difference operators."""
23+
def __init__(self, f, independent_variable='x',
24+
num_terms_Taylor_series=4):
25+
self.Taylor = TaylorSeries(f, num_terms_Taylor_series)
26+
self.f = self.Taylor.f
27+
self.h = sym.Symbol('d%s' % independent_variable)
28+
29+
# Finite difference operators
30+
h, f, f_T = self.h, self.f, self.Taylor # short names
31+
theta = sym.Symbol('theta')
32+
self.diffops = {
33+
'Dtp': (f_T(h) - f)/h,
34+
'Dtm': (f - f_T(-h))/h,
35+
'Dt': (f_T(h/2) - f_T(-h/2))/h,
36+
'D2t': (f_T(h) - f_T(-h))/(2*h),
37+
'DtDt': (f_T(h) - 2*f + f_T(-h))/h**2,
38+
'barDt': (f_T((1-theta)*h) - f_T(-theta*h))/h,
39+
}
40+
self.diffops = {diffop: sym.simplify(self.diffops[diffop])
41+
for diffop in self.diffops}
42+
43+
self.diffops['weighted_arithmetic_mean'] = \
44+
self._weighted_arithmetic_mean()
45+
self.diffops['geometric_mean'] = self._geometric_mean()
46+
self.diffops['harmonic_mean'] = self._harmonic_mean()
47+
48+
def _weighted_arithmetic_mean(self):
49+
# The expansion is around n*h + theta*h
50+
h, f, f_T = self.h, self.f, self.Taylor
51+
theta = sym.Symbol('theta')
52+
f_n = f_T(-h*theta)
53+
f_np1 = f_T((1-theta)*h)
54+
a_mean = theta*f_np1 + (1-theta)*f_n
55+
return sym.expand(a_mean)
56+
57+
def _geometric_mean(self):
58+
h, f, f_T = self.h, self.f, self.Taylor
59+
f_nmhalf = f_T(-h/2)
60+
f_nphalf = f_T(h/2)
61+
g_mean = f_nmhalf*f_nphalf
62+
return sym.expand(g_mean)
63+
64+
def _harmonic_mean(self):
65+
h, f, f_T = self.h, self.f, self.Taylor
66+
f_nmhalf = f_T(-h/2)
67+
f_nphalf = f_T(h/2)
68+
h_mean = 2/(1/f_nmhalf + 1/f_nphalf)
69+
return sym.expand(h_mean)
70+
71+
def D(self, i):
72+
"""Return the symbol for the i-th derivative."""
73+
return self.Taylor.df[i]
74+
75+
def __getitem__(self, operator_name):
76+
return self.diffops.get(operator_name, None)
77+
78+
def operator_names(self):
79+
"""Return all names for the operators."""
80+
return list(self.diffops.keys())
81+
82+
def truncation_errors():
83+
# Make a table
84+
u, theta = sym.symbols('u theta')
85+
diffop = DiffOp(u, independent_variable='t',
86+
num_terms_Taylor_series=5)
87+
D1u = diffop.D(1) # symbol for du/dt
88+
D2u = diffop.D(2) # symbol for d^2u/dt^2
89+
print 'R Dt:', diffop['Dt'] - D1u
90+
print 'R Dtm:', diffop['Dtm'] - D1u
91+
print 'R Dtp:', diffop['Dtp'] - D1u
92+
print 'R barDt:', diffop['barDt'] - D1u
93+
print 'R DtDt:', diffop['DtDt'] - D2u
94+
print 'R weighted arithmetic mean:', diffop['weighted_arithmetic_mean'] - u
95+
print 'R arithmetic mean:', diffop['weighted_arithmetic_mean'].subs(theta, sym.Rational(1,2)) - u
96+
print 'R geometric mean:', diffop['geometric_mean'] - u
97+
dt = diffop.h
98+
print 'R harmonic mean:', (diffop['harmonic_mean'] - u).\
99+
series(dt, 0, 3).as_leading_term(dt)
100+
101+
if __name__ == '__main__':
102+
truncation_errors()

0 commit comments

Comments
 (0)