Skip to content

Commit d6b4649

Browse files
committed
Merge pull request #62 from danielru/feature/disp_plot
Function to plot semi-discrete dispersion relation
2 parents 324d1c6 + d44e817 commit d6b4649

File tree

2 files changed

+149
-1
lines changed

2 files changed

+149
-1
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
from pySDC import Level as lvl
2+
from pySDC import Hooks as hookclass
3+
from pySDC import CollocationClasses as collclass
4+
from pySDC import Step as stepclass
5+
6+
from pySDC.datatype_classes.complex_mesh import mesh, rhs_imex_mesh
7+
from pySDC.sweeper_classes.imex_1st_order import imex_1st_order as imex
8+
from standard_integrators import dirk
9+
# for simplicity, import the scalar problem to generate Q matrices
10+
from examples.fwsw.ProblemClass import swfw_scalar
11+
import numpy as np
12+
import scipy.linalg as la
13+
import scipy.optimize as opt
14+
import sympy
15+
16+
from pylab import rcParams
17+
import matplotlib.pyplot as plt
18+
from subprocess import call
19+
20+
def findomega(stab_fh):
21+
assert np.array_equal(np.shape(stab_fh),[2,2]), 'Not 2x2 matrix...'
22+
omega = sympy.Symbol('omega')
23+
func = (sympy.exp(-1j*omega)-stab_fh[0,0])*(sympy.exp(-1j*omega)-stab_fh[1,1])-stab_fh[0,1]*stab_fh[1,0]
24+
solsym = sympy.solve(func, omega)
25+
sol0 = complex(solsym[0])
26+
sol1 = complex(solsym[1])
27+
if sol0.real>=0:
28+
sol = sol0
29+
elif sol1.real>=0:
30+
sol = sol1
31+
else:
32+
print "Two roots with real part of same sign..."
33+
return sol
34+
35+
if __name__ == "__main__":
36+
37+
pparams = {}
38+
# the following are not used in the computation
39+
pparams['lambda_s'] = np.array([0.0])
40+
pparams['lambda_f'] = np.array([0.0])
41+
pparams['u0'] = 1.0
42+
swparams = {}
43+
swparams['collocation_class'] = collclass.CollGaussLegendre
44+
swparams['num_nodes'] = 2
45+
K = 2
46+
dirk_order = K
47+
48+
c_speed = 1.0
49+
U_speed = 0.05
50+
51+
#
52+
# ...this is functionality copied from test_imexsweeper. Ideally, it should be available in one place.
53+
#
54+
step = stepclass.step(params={})
55+
L = lvl.level(problem_class=swfw_scalar, problem_params=pparams, dtype_u=mesh, dtype_f=rhs_imex_mesh, sweeper_class=imex, sweeper_params=swparams, level_params={}, hook_class=hookclass.hooks, id="stability")
56+
step.register_level(L)
57+
step.status.dt = 1.0 # can't use different value
58+
step.status.time = 0.0
59+
u0 = step.levels[0].prob.u_exact(step.status.time)
60+
step.init_step(u0)
61+
nnodes = step.levels[0].sweep.coll.num_nodes
62+
level = step.levels[0]
63+
problem = level.prob
64+
65+
QE = level.sweep.QE[1:,1:]
66+
QI = level.sweep.QI[1:,1:]
67+
Q = level.sweep.coll.Qmat[1:,1:]
68+
Nsamples = 30
69+
k_vec = np.linspace(0, np.pi, Nsamples+1, endpoint=False)
70+
k_vec = k_vec[1:]
71+
phase = np.zeros((2,Nsamples))
72+
amp_factor = np.zeros((2,Nsamples))
73+
for i in range(0,np.size(k_vec)):
74+
Cs = -1j*k_vec[i]*np.array([[0.0, c_speed],[c_speed, 0.0]], dtype='complex')
75+
Uadv = -1j*k_vec[i]*np.array([[U_speed, 0.0], [0.0, U_speed]], dtype='complex')
76+
77+
LHS = np.eye(2*nnodes) - step.status.dt*( np.kron(QI,Cs) + np.kron(QE,Uadv) )
78+
RHS = step.status.dt*( np.kron(Q, Uadv+Cs) - np.kron(QI,Cs) - np.kron(QE,Uadv) )
79+
80+
LHSinv = np.linalg.inv(LHS)
81+
Mat_sweep = np.linalg.matrix_power(LHSinv.dot(RHS), K)
82+
for k in range(0,K):
83+
Mat_sweep = Mat_sweep + np.linalg.matrix_power(LHSinv.dot(RHS),k).dot(LHSinv)
84+
##
85+
# ---> The update formula for this case need verification!!
86+
update = step.status.dt*np.kron( level.sweep.coll.weights, Uadv+Cs )
87+
88+
y1 = np.array([1,0])
89+
y2 = np.array([0,1])
90+
e1 = np.kron( np.ones(nnodes), y1 )
91+
stab_fh_1 = y1 + update.dot( Mat_sweep.dot(e1) )
92+
e2 = np.kron( np.ones(nnodes), y2 )
93+
stab_fh_2 = y2 + update.dot(Mat_sweep.dot(e2))
94+
stab_sdc = np.column_stack((stab_fh_1, stab_fh_2))
95+
96+
# Stability function of backward Euler is 1/(1-z); system is y' = (Cs+Uadv)*y
97+
#stab_ie = np.linalg.inv( np.eye(2) - step.status.dt*(Cs+Uadv) )
98+
99+
# For testing, insert exact stability function exp(-dt*i*k*(Cs+Uadv)
100+
#stab_fh = la.expm(Cs+Uadv)
101+
102+
dirkts = dirk(Cs+Uadv, dirk_order)
103+
stab_fh1 = dirkts.timestep(y1, 1.0)
104+
stab_fh2 = dirkts.timestep(y2, 1.0)
105+
stab_dirk = np.column_stack((stab_fh1, stab_fh2))
106+
107+
sol_sdc = findomega(stab_sdc)
108+
sol_dirk = findomega(stab_dirk)
109+
110+
# Now solve for discrete phase
111+
phase[0,i] = sol_sdc.real
112+
amp_factor[0,i] = np.exp(sol_sdc.imag)
113+
phase[1,i] = sol_dirk.real
114+
amp_factor[1,i] = np.exp(sol_dirk.imag)
115+
###
116+
rcParams['figure.figsize'] = 2.5, 2.5
117+
fs = 8
118+
fig = plt.figure()
119+
plt.plot(k_vec, k_vec*(U_speed+c_speed), '--', color='k', linewidth=1.5, label='Exact')
120+
plt.plot(k_vec, phase[0,:], '-', color='b', linewidth=1.5, label='SDC('+str(K)+')')
121+
plt.plot(k_vec, phase[1,:], '-', color='g', linewidth=1.5, label='DIRK('+str(dirkts.order)+')')
122+
plt.xlabel('Wave number', fontsize=fs)
123+
plt.ylabel('Phase speed', fontsize=fs)
124+
plt.xlim([k_vec[0], k_vec[-1:]])
125+
plt.ylim([k_vec[0], k_vec[-1:]])
126+
fig.gca().tick_params(axis='both', labelsize=fs)
127+
plt.legend(loc='upper left', fontsize=fs, prop={'size':fs})
128+
#plt.show()
129+
filename = 'sdc-fwsw-disprel-phase-K'+str(K)+'-M'+str(swparams['num_nodes'])+'.pdf'
130+
plt.gcf().savefig(filename, bbox_inches='tight')
131+
call(["pdfcrop", filename, filename])
132+
133+
fig = plt.figure()
134+
plt.plot(k_vec, 1.0+np.zeros(np.size(k_vec)), '--', color='k', linewidth=1.5, label='Exact')
135+
plt.plot(k_vec, amp_factor[0,:], '-', color='b', linewidth=1.5, label='SDC('+str(K)+')')
136+
plt.plot(k_vec, amp_factor[1,:], '-', color='g', linewidth=1.5, label='DIRK('+str(dirkts.order)+')')
137+
plt.xlabel('Wave number', fontsize=fs)
138+
plt.ylabel('Amplification factor', fontsize=fs)
139+
fig.gca().tick_params(axis='both', labelsize=fs)
140+
plt.xlim([k_vec[0], k_vec[-1:]])
141+
plt.ylim([k_vec[0], k_vec[-1:]])
142+
plt.legend(loc='lower left', fontsize=fs, prop={'size':fs})
143+
plt.gca().set_ylim([0.0, 1.1])
144+
#plt.show()
145+
filename = 'sdc-fwsw-disprel-ampfac-K'+str(K)+'-M'+str(swparams['num_nodes'])+'.pdf'
146+
plt.gcf().savefig(filename, bbox_inches='tight')
147+
call(["pdfcrop", filename, filename])
148+

examples/acoustic_1d_imex/standard_integrators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def __init__(self, M, order):
112112
self.b[1] = 1.0 - 1.0/(3.0*alpha*alpha)
113113
self.b[2] = 1.0/(6.0*alpha*alpha)
114114

115-
self.stages = np.zeros((self.nstages,self.Ndof))
115+
self.stages = np.zeros((self.nstages,self.Ndof), dtype='complex')
116116

117117
def timestep(self, u0, dt):
118118

0 commit comments

Comments
 (0)