Skip to content

Commit 7967cc2

Browse files
committed
Merge pull request #76 from danielru/fix/matrix_functions
Matrix functions in sweeper
2 parents ea319ec + 833ad5a commit 7967cc2

File tree

3 files changed

+63
-49
lines changed

3 files changed

+63
-49
lines changed

examples/fwsw/plot_stability.py

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
N_s = 100
1919
N_f = 400
2020

21-
lam_s_max = 12.0
22-
lam_f_max = 24.0
21+
lam_s_max = 3.0
22+
lam_f_max = 12.0
2323
lambda_s = 1j*np.linspace(0.0, lam_s_max, N_s)
2424
lambda_f = 1j*np.linspace(0.0, lam_f_max, N_f)
2525

@@ -31,7 +31,7 @@
3131
swparams = {}
3232
swparams['collocation_class'] = collclass.CollGaussLegendre
3333
swparams['num_nodes'] = 3
34-
K = 0
34+
K = 3
3535
do_coll_update = True
3636

3737
#
@@ -47,10 +47,6 @@
4747
nnodes = step.levels[0].sweep.coll.num_nodes
4848
level = step.levels[0]
4949
problem = level.prob
50-
51-
QE = level.sweep.QE[1:,1:]
52-
QI = level.sweep.QI[1:,1:]
53-
Q = level.sweep.coll.Qmat[1:,1:]
5450

5551
stab = np.zeros((N_f, N_s), dtype='complex')
5652

@@ -59,15 +55,9 @@
5955
lambda_fast = lambda_f[j]
6056
lambda_slow = lambda_s[i]
6157
if K is not 0:
62-
63-
LHS = np.eye(nnodes) - step.status.dt*( lambda_fast*QI + lambda_slow*QE )
64-
RHS = step.status.dt*( (lambda_fast+lambda_slow)*Q - (lambda_fast*QI + lambda_slow*QE) )
65-
66-
Pinv = np.linalg.inv(LHS)
67-
Mat_sweep = np.linalg.matrix_power(Pinv.dot(RHS), K)
68-
for k in range(0,K):
69-
Mat_sweep = Mat_sweep + np.linalg.matrix_power(Pinv.dot(RHS),k).dot(Pinv)
70-
58+
lambdas = [ lambda_fast, lambda_slow ]
59+
LHS, RHS = level.sweep.get_scalar_problems_sweeper_mats( lambdas = lambdas )
60+
Mat_sweep = level.sweep.get_scalar_problems_manysweep_mat( nsweeps = K, lambdas = lambdas )
7161
else:
7262
# Compute stability function of collocation solution
7363
Mat_sweep = np.linalg.inv(np.eye(nnodes)-step.status.dt*(lambda_fast + lambda_slow)*Q)

pySDC/sweeper_classes/imex_1st_order.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ def update_nodes(self):
129129

130130
return None
131131

132-
133132
def compute_end_point(self):
134133
"""
135134
Compute u at the right point of the interval
@@ -155,3 +154,45 @@ def compute_end_point(self):
155154
L.uend += L.tau[-1]
156155

157156
return None
157+
158+
def get_sweeper_mats(self):
159+
"""
160+
Returns the three matrices Q, QI, QE which define the sweeper. The first row and column, corresponding to the left starting value, are removed to correspond to the notation
161+
introduced in Ruprecht & Speck, ``Spectral deferred corrections with fast-wave slow-wave splitting'', 2016
162+
"""
163+
QE = self.QE[1:,1:]
164+
QI = self.QI[1:,1:]
165+
Q = self.coll.Qmat[1:,1:]
166+
return QE, QI, Q
167+
168+
def get_scalar_problems_sweeper_mats(self, lambdas=[None, None]):
169+
"""
170+
For a scalar problem, an IMEX-SDC sweep can be written in matrix formulation. This function returns the corresponding matrices.
171+
See Ruprecht & Speck, ``Spectral deferred corrections with fast-wave slow-wave splitting'', 2016 for the derivation.
172+
173+
The first entry in lambdas is lambda_fast, the second is lambda_slow.
174+
"""
175+
QE, QI, Q = self.get_sweeper_mats()
176+
if lambdas==[None,None]:
177+
pass
178+
# should use lambdas from attached problem and make sure it is a scalar IMEX
179+
raise NotImplementedError("At the moment, the values for lambda have to be provided")
180+
else:
181+
lambda_fast = lambdas[0]
182+
lambda_slow = lambdas[1]
183+
nnodes = self.coll.num_nodes
184+
dt = self.level.dt
185+
LHS = np.eye(nnodes) - dt*( lambda_fast*QI + lambda_slow*QE )
186+
RHS = dt*( (lambda_fast+lambda_slow)*Q - (lambda_fast*QI + lambda_slow*QE) )
187+
return LHS, RHS
188+
189+
def get_scalar_problems_manysweep_mat(self, nsweeps, lambdas=[None,None]):
190+
"""
191+
For a scalar problem, K sweeps of IMEX-SDC can be written in matrix form.
192+
"""
193+
LHS, RHS = self.get_scalar_problems_sweeper_mats(lambdas = lambdas)
194+
Pinv = np.linalg.inv(LHS)
195+
Mat_sweep = np.linalg.matrix_power(Pinv.dot(RHS), nsweeps)
196+
for k in range(0,nsweeps):
197+
Mat_sweep += np.linalg.matrix_power(Pinv.dot(RHS), k).dot(Pinv)
198+
return Mat_sweep

tests/test_imexsweeper.py

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,6 @@ def setupLevelStepProblem(self):
3232
problem = level.prob
3333
return step, level, problem, nnodes
3434

35-
def setupQMatrices(self, level):
36-
QE = level.sweep.QE[1:,1:]
37-
QI = level.sweep.QI[1:,1:]
38-
Q = level.sweep.coll.Qmat[1:,1:]
39-
return QE, QI, Q
40-
41-
def setupSweeperMatrices(self, step, level, problem):
42-
nnodes = step.levels[0].sweep.coll.num_nodes
43-
# Build SDC sweep matrix
44-
QE, QI, Q = self.setupQMatrices(level)
45-
dt = step.status.dt
46-
LHS = np.eye(nnodes) - step.status.dt*( problem.lambda_f[0]*QI + problem.lambda_s[0]*QE )
47-
RHS = step.status.dt*( (problem.lambda_f[0]+problem.lambda_s[0])*Q - (problem.lambda_f[0]*QI + problem.lambda_s[0]*QE) )
48-
return LHS, RHS
49-
5035
#
5136
# General setUp function used by all tests
5237
#
@@ -118,7 +103,8 @@ def test_sweepequalmatrix(self):
118103
# Perform node-to-node SDC sweep
119104
level.sweep.update_nodes()
120105

121-
LHS, RHS = self.setupSweeperMatrices(step, level, problem)
106+
lambdas = [ problem.lambda_f[0] , problem.lambda_s[0] ]
107+
LHS, RHS = level.sweep.get_scalar_problems_sweeper_mats( lambdas = lambdas )
122108

123109
unew = np.linalg.inv(LHS).dot( u0full + RHS.dot(u0full) )
124110
usweep = np.array([ level.u[l].values.flatten() for l in range(1,nnodes+1) ])
@@ -137,14 +123,14 @@ def test_updateformula(self):
137123
# Perform update step in sweeper
138124
level.sweep.update_nodes()
139125
ustages = np.array([ level.u[l].values.flatten() for l in range(1,nnodes+1) ])
140-
141126
# Compute end value through provided function
142127
level.sweep.compute_end_point()
143128
uend_sweep = level.uend.values
144129
# Compute end value from matrix formulation
145130
uend_mat = self.pparams['u0'] + step.status.dt*level.sweep.coll.weights.dot(ustages*(problem.lambda_s[0] + problem.lambda_f[0]))
146131
assert np.linalg.norm(uend_sweep - uend_mat, np.infty)<1e-14, "Update formula in sweeper gives different result than matrix update formula"
147132

133+
148134
#
149135
# Compute the exact collocation solution by matrix inversion and make sure it is a fixed point
150136
#
@@ -155,7 +141,7 @@ def test_collocationinvariant(self):
155141
level.sweep.predict()
156142
u0full = np.array([ level.u[l].values.flatten() for l in range(1,nnodes+1) ])
157143

158-
QE, QI, Q = self.setupQMatrices(level)
144+
QE, QI, Q = level.sweep.get_sweeper_mats()
159145

160146
# Build collocation matrix
161147
Mcoll = np.eye(nnodes) - step.status.dt*Q*(problem.lambda_s[0] + problem.lambda_f[0])
@@ -172,9 +158,9 @@ def test_collocationinvariant(self):
172158
# Perform node-to-node SDC sweep
173159
level.sweep.update_nodes()
174160

175-
# Build matrices for matrix formulation of sweep
176-
LHS = np.eye(nnodes) - step.status.dt*( problem.lambda_f[0]*QI + problem.lambda_s[0]*QE )
177-
RHS = step.status.dt*( (problem.lambda_f[0]+problem.lambda_s[0])*Q - (problem.lambda_f[0]*QI + problem.lambda_s[0]*QE) )
161+
lambdas = [ problem.lambda_f[0] , problem.lambda_s[0] ]
162+
LHS, RHS = level.sweep.get_scalar_problems_sweeper_mats( lambdas = lambdas )
163+
178164
# Make sure both matrix and node-to-node sweep leave collocation unaltered
179165
unew = np.linalg.inv(LHS).dot( u0full + RHS.dot(ucoll) )
180166
assert np.linalg.norm( unew - ucoll, np.infty )<1e-14, "Collocation solution not invariant under matrix SDC sweep"
@@ -198,18 +184,16 @@ def test_manysweepsequalmatrix(self):
198184
level.sweep.update_nodes()
199185
usweep = np.array([ level.u[l].values.flatten() for l in range(1,nnodes+1) ])
200186

201-
LHS, RHS = self.setupSweeperMatrices(step, level, problem)
187+
lambdas = [ problem.lambda_f[0] , problem.lambda_s[0] ]
188+
LHS, RHS = level.sweep.get_scalar_problems_sweeper_mats( lambdas = lambdas )
189+
202190
unew = u0full
203191
for i in range(0,K):
204192
unew = np.linalg.inv(LHS).dot( u0full + RHS.dot(unew) )
205193

206194
assert np.linalg.norm(unew - usweep, np.infty)<1e-14, "Doing multiple node-to-node sweeps yields different result than same number of matrix-form sweeps"
207195

208-
# Build single matrix representing K sweeps
209-
Pinv = np.linalg.inv(LHS)
210-
Mat_sweep = np.linalg.matrix_power(Pinv.dot(RHS), K)
211-
for i in range(0,K):
212-
Mat_sweep = Mat_sweep + np.linalg.matrix_power(Pinv.dot(RHS),i).dot(Pinv)
196+
Mat_sweep = level.sweep.get_scalar_problems_manysweep_mat( nsweeps = K, lambdas = lambdas )
213197
usweep_onematrix = Mat_sweep.dot(u0full)
214198
assert np.linalg.norm( usweep_onematrix - usweep, np.infty )<1e-14, "Single-matrix multiple sweep formulation yields different result than multiple sweeps in node-to-node or matrix form form"
215199

@@ -231,12 +215,11 @@ def test_manysweepupdate(self):
231215
level.sweep.compute_end_point()
232216
uend_sweep = level.uend.values
233217

234-
LHS, RHS = self.setupSweeperMatrices(step, level, problem)
218+
lambdas = [ problem.lambda_f[0] , problem.lambda_s[0] ]
219+
LHS, RHS = level.sweep.get_scalar_problems_sweeper_mats( lambdas = lambdas )
220+
235221
# Build single matrix representing K sweeps
236-
Pinv = np.linalg.inv(LHS)
237-
Mat_sweep = np.linalg.matrix_power(Pinv.dot(RHS), K)
238-
for i in range(0,K):
239-
Mat_sweep = Mat_sweep + np.linalg.matrix_power(Pinv.dot(RHS),i).dot(Pinv)
222+
Mat_sweep = level.sweep.get_scalar_problems_manysweep_mat( nsweeps = K, lambdas = lambdas )
240223
# Now build update function
241224
update = 1.0 + (problem.lambda_s[0] + problem.lambda_f[0])*level.sweep.coll.weights.dot(Mat_sweep.dot(np.ones(nnodes)))
242225
# Multiply u0 by value of update function to get end value directly

0 commit comments

Comments
 (0)