Skip to content

Commit 16903cd

Browse files
author
Daniel Ruprecht
committed
adds a trapezoidal rule integrator and corresponding tests
1 parent 3163889 commit 16903cd

File tree

4 files changed

+103
-3
lines changed

4 files changed

+103
-3
lines changed

scripts/adv_disp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import numpy as np
2-
from scipy.sparse import linalg
32
from pylab import rcParams
43
import matplotlib.pyplot as plt
54
from matplotlib.patches import Polygon
65
from subprocess import call
76
import sympy
87
import warnings
8+
from scipy.sparse import linalg
99

1010
def findomega(Z):
1111
omega = sympy.Symbol('omega')

scripts/plot_dispersion.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ def normalise(R, T, target):
6565
u0 = solution_linear(u0_val, np.array([[symb]],dtype='complex'))
6666
ucoarse = solution_linear(u0_val, np.array([[symb_coarse]],dtype='complex'))
6767
para = parareal(0.0, Tend, nslices, intexact, impeuler, nfine, ncoarse, 0.0, niter_v[0], u0)
68-
69-
68+
7069
# get update matrix for imp Euler over one slice
7170
stab_fine = para.timemesh.slices[0].get_fine_update_matrix(u0)
7271
stab_fine = stab_fine

src/trapezoidal.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from integrator import integrator
2+
from solution import solution
3+
import copy
4+
5+
# Used to compute update matrices
6+
from solution_linear import solution_linear
7+
from scipy import sparse
8+
from scipy import linalg
9+
import numpy as np
10+
11+
class trapezoidal(integrator):
12+
13+
def __init__(self, tstart, tend, nsteps):
14+
super(trapezoidal, self).__init__(tstart, tend, nsteps)
15+
self.order = 2
16+
17+
def run(self, u0):
18+
assert isinstance(u0, solution), "Initial value u0 must be an object of type solution"
19+
for i in range(0,self.nsteps):
20+
utemp = copy.deepcopy(u0)
21+
utemp.f()
22+
u0.applyM()
23+
u0.axpy(0.5*self.dt, utemp)
24+
u0.solve(0.5*self.dt)
25+
26+
#
27+
# For linear problems My' = A', a backward Euler update corresponds
28+
# to
29+
# u_n+1 = (M - dt*A)^(-1)*M*u_n
30+
# so that a full update is [(M-dt*A)^(-1)*M]^nsteps
31+
def get_update_matrix(self, sol):
32+
assert isinstance(sol, solution_linear), "Update function can only be computed for solutions of type solution_linear"
33+
# Fetch matrices from solution and make sure they are sparse
34+
M = sparse.csc_matrix(sol.M)
35+
A = sparse.csc_matrix(sol.A)
36+
Rmat = sparse.linalg.inv(M - 0.5*self.dt*A)
37+
# this is call is necessary because if Rmat has only 1 entry, it gets converted to a dense array here
38+
Rmat = sparse.csc_matrix(Rmat)
39+
Rmat = Rmat.dot(M+0.5*self.dt*A)
40+
Rmat = Rmat**self.nsteps
41+
return Rmat

tests/test_trapezoidal.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import sys
2+
sys.path.append('../src')
3+
4+
import numpy as np
5+
from scipy import sparse
6+
from scipy import linalg
7+
from integrator import integrator
8+
from trapezoidal import trapezoidal
9+
from solution_linear import solution_linear
10+
from nose.tools import *
11+
import unittest
12+
13+
class TestTrapezoidal(unittest.TestCase):
14+
15+
def setUp(self):
16+
self.ndof = np.random.randint(255)
17+
self.A = sparse.spdiags([ np.ones(self.ndof), -2.0*np.ones(self.ndof), np.ones(self.ndof)], [-1,0,1], self.ndof, self.ndof, format="csc")
18+
self.M = sparse.spdiags([ np.random.rand(self.ndof) ], [0], self.ndof, self.ndof, format="csc")
19+
self.sol = solution_linear(np.ones(self.ndof), self.A, self.M)
20+
21+
# Can instantiate object
22+
def test_caninstantiate(self):
23+
tp = trapezoidal(0.0, 1.0, 10)
24+
25+
# Throws if tend < tstart
26+
def test_tendbeforetstartthrows(self):
27+
with self.assertRaises(AssertionError):
28+
tp = trapezoidal(1.0, 0.0, 10)
29+
30+
# See if run function can be called
31+
def test_cancallrun(self):
32+
tp = trapezoidal(0.0, 1.0, 10)
33+
tp.run(self.sol)
34+
35+
# See if run does the same as the update matrix for a scalar problem
36+
def test_callcorrectscalar(self):
37+
eig = -1.0
38+
u0 = solution_linear(np.array([1.0]), np.array([[eig]]))
39+
nsteps = 50
40+
tp = trapezoidal(0.0, 1.0, nsteps)
41+
tp.run(u0)
42+
assert abs(u0.y - np.exp(-1.0))<5e-3, ("Very wrong solution. Error: %5.2e" % abs(u0.y - np.exp(-1.0)))
43+
Rmat = tp.get_update_matrix(u0)
44+
Rmat_ie = Rmat[0,0]
45+
Rmat_ex = ((1.0 + 0.5*tp.dt*eig)/(1.0 - 0.5*tp.dt*eig))**nsteps
46+
assert abs(Rmat_ie - Rmat_ex)<1e-14, ("Update function generated by trapezoidal rule for scalar case does not match exact value. Error: %5.3e" % abs(Rmat_ie - Rmat_ex))
47+
48+
# See if run does the same as the update matrix
49+
def test_callcorrect(self):
50+
u0 = solution_linear(np.ones(self.ndof), self.A, self.M)
51+
nsteps = 13
52+
tp = trapezoidal(0.0, 1.0, nsteps)
53+
tp.run(u0)
54+
# Compute output through update matrix and compare
55+
Rmat = tp.get_update_matrix(u0)
56+
yend = Rmat.dot(np.ones((self.ndof,1)))
57+
sol_end = solution_linear( yend, self.A, self.M )
58+
sol_end.axpy(-1.0, u0)
59+
assert sol_end.norm()<2e-12, ("Output from trapezoidal rule integrator differs from result computed with power of update matrix -- norm of difference: %5.3e" % sol_end.norm())
60+

0 commit comments

Comments
 (0)