1+ import numpy as np
2+ from scipy .sparse import linalg
3+ from pylab import rcParams
4+ import matplotlib .pyplot as plt
5+ from matplotlib .patches import Polygon
6+ from subprocess import call
7+ import sympy
8+ import warnings
9+
10+ def selectomega (omega ):
11+ assert np .size (omega )== 2 , "Should have 2 entries..."
12+ return omega [1 ]
13+
14+ def findomegasystem (Z ):
15+ assert np .array_equal (np .shape (Z ), [2 ,2 ]), "Must be 2x2 matrix..."
16+ omega = sympy .Symbol ('omega' )
17+ expf = sympy .exp (- 1j * omega )
18+ func = (expf - Z [0 ,0 ])* (expf - Z [1 ,1 ]) - Z [1 ,0 ]* Z [0 ,1 ]
19+ solsym = sympy .solve (func , omega )
20+ sols = np .array ([complex (solsym [0 ]), complex (solsym [1 ])], dtype = 'complex' )
21+ return sols
22+
23+ def findomega (Z ):
24+ assert np .size (Z )== 2 , 'Not a vector of length 2...'
25+ omega = sympy .Symbol ('omega' )
26+ expf = sympy .exp (- 1j * omega )
27+ func = (expf - Z [0 ])* (expf - Z [1 ])
28+ solsym = sympy .solve (func , omega )
29+ sols = np .array ([complex (solsym [0 ]), complex (solsym [1 ])], dtype = 'complex' )
30+ return sols
31+
32+ def findroots (R , n ):
33+ assert abs (n - float (int (n )))< 1e-14 , "n must be an integer or a float equal to an integer"
34+ p = np .zeros (n + 1 , dtype = 'complex' )
35+ p [- 1 ] = - R
36+ p [0 ] = 1.0
37+ return np .roots (p )
38+
39+ def normalise (R , T , targets , verbose = False , exact = None ):
40+ roots = findroots (R , T )
41+ if verbose :
42+ print ""
43+ print "roots:"
44+ print roots
45+ for x in roots :
46+ assert abs (x ** T - R )< 1e-10 , ("Element in roots not a proper root: err=%5.3e" % abs (x ** T - R ))
47+
48+ resi = np .zeros ((np .size (roots ),np .size (targets )))
49+ for i in range (0 ,np .size (targets )):
50+ for j in range (0 ,np .size (roots )):
51+ resi [j ,i ] = abs ( roots [j ] - targets [i ] )
52+
53+ # Select root that generates smallest residual across all targets:
54+ # find row number of smallest element in resi
55+ minind = np .argmin (resi ) / np .size (targets )
56+ if verbose :
57+ print ("Target values: %s" % targets )
58+ print ("Residuals: %s" % resi )
59+ print ("Selected row: %s" % minind )
60+ print ("Selected value: %s" % roots [minind ])
61+ if not exact == None :
62+ print ("Exact values: %s" % exact )
63+ return roots [minind ]
64+
65+
66+ Tend = 4.0
67+ nslices = int (Tend )
68+ Nsamples = 80
69+ k_vec = np .linspace (0.0 , np .pi , Nsamples + 1 , endpoint = False )
70+ k_vec = k_vec [1 :]
71+
72+ Uadv = 0.1
73+ cspeed = 1.0
74+
75+ phase = np .zeros ((2 ,Nsamples ))
76+ amp_factor = np .zeros ((2 ,Nsamples ))
77+ targets = np .zeros ((2 ,Nsamples ), dtype = 'complex' )
78+
79+ imax = 21
80+ #imax = Nsamples
81+ for i in range (17 ,imax ):
82+ print ("---- i = %2i ---- " % i )
83+ Lmat = - 1j * k_vec [i ]* np .array ([ [Uadv , cspeed ], [cspeed , Uadv ] ], dtype = 'complex' )
84+
85+ stab = linalg .expm (Lmat * Tend )
86+
87+ # if i==0, compute targets from contiuous stability function
88+ stab_unit = linalg .expm (Lmat )
89+
90+ # analytic frequencies match frequencies computed from unit interval system
91+ omegas_unit = findomegasystem (stab_unit )
92+ phase [0 ,i ] = Uadv + cspeed
93+ phase [1 ,i ] = selectomega (omegas_unit ).real / k_vec [i ]
94+
95+ # diagonalise unit system and verify that frequencies do not change
96+ Dunit , Vunit = np .linalg .eig (stab_unit )
97+ #Dunit = np.sort(Dunit)
98+ if i == 0 :
99+ targets [:,i ] = Dunit
100+
101+ omegas_diag_unit = findomega (Dunit )
102+
103+ err_omega = np .linalg .norm (omegas_unit - omegas_diag_unit , np .inf )
104+ print ("Defect between unit and unit-diagonalised frequencies: %5.3E" % err_omega )
105+
106+ # normalise stab function
107+ D , V = np .linalg .eig (stab )
108+ omegas_diag_full = findomega (D )
109+
110+ err_omega = np .linalg .norm (omegas_diag_full / Tend - omegas_diag_unit , np .inf )
111+ print ""
112+ print omegas_diag_unit
113+ print omegas_diag_full / Tend
114+ print (">>>> Defect between omegas from diagonalised unit system and diagonalised full system: %5.3E" % err_omega )
115+
116+ D = np .sort (D )
117+ Dtilde = np .zeros (2 , dtype = 'complex' )
118+ for j in range (0 ,2 ):
119+ Dtilde [j ] = normalise (D [j ], Tend , targets = targets [:,i ], verbose = True , exact = Dunit )
120+ #Dtilde[j] = normalise(D[j], Tend, targets=Dunit, verbose=False)
121+ #Dtilde = np.sort(Dtilde)
122+ err_eigv = np .linalg .norm (Dtilde - Dunit , np .inf )
123+ print ("Defect between normalised and unit stability function eigenvalues: %5.3E" % err_eigv )
124+
125+ if (i < imax - 1 ):
126+ targets [:,i + 1 ] = Dtilde
127+
128+ # normalised stability function matches unit interval stability function
129+ stab_unit_sorted = V .dot (np .diag (Dunit ).dot (np .linalg .inv (V )))
130+ stab_normalised = V .dot (np .diag (Dtilde ).dot (np .linalg .inv (V )))
131+ err_stab = np .linalg .norm (stab_normalised - stab_unit_sorted , np .infty )
132+ print ("Defect between unit interval and normalised stability matrix: %5.3E" % err_stab )
133+ ### NOTE: The defects here are caused by different ordering of eigenvalues!
134+
135+ # frequencies of unit interval system match frequencies of normalised system
136+ omegas_diag_normalised = findomega (Dtilde )
137+ err_omega = np .linalg .norm (omegas_diag_unit - omegas_diag_normalised , np .inf )
138+ print ("Defect between frequencies from diagonalised normalised system and diagonalised unit intervall: %5.3E" % err_omega )
139+ # end of loop body over k_vec
140+
141+ print "------"
142+
143+ #rcParams['figure.figsize'] = 1.5, 1.5
144+ if True :
145+ fs = 14
146+ fig = plt .figure ()
147+ plt .plot (k_vec , phase [0 ,:], '--' , color = 'k' , linewidth = 1.5 , label = 'Exact' )
148+ plt .plot (k_vec , phase [1 ,:], '-' , color = 'g' , linewidth = 1.5 , label = 'Solved' )
149+ plt .xlabel ('Wave number' , fontsize = fs , labelpad = 0.25 )
150+ plt .ylabel ('Phase speed' , fontsize = fs , labelpad = 0.5 )
151+ plt .xlim ([k_vec [0 ], k_vec [- 1 :]])
152+ plt .ylim ([0.0 , 1.1 * np .max (phase )])
153+ fig .gca ().tick_params (axis = 'both' , labelsize = fs )
154+ plt .legend (loc = 'lower left' , fontsize = fs , prop = {'size' :fs - 2 })
155+
156+ fig = plt .figure ()
157+ plt .plot (k_vec , amp_factor [0 ,:], '--' , color = 'k' , linewidth = 1.5 , label = 'Exact' )
158+ plt .plot (k_vec , amp_factor [1 ,:], '-' , color = 'g' , linewidth = 1.5 , label = 'Solved' )
159+ plt .xlabel ('Wave number' , fontsize = fs , labelpad = 0.25 )
160+ plt .ylabel ('Amplification factor' , fontsize = fs , labelpad = 0.5 )
161+ fig .gca ().tick_params (axis = 'both' , labelsize = fs )
162+ plt .xlim ([k_vec [0 ], k_vec [- 1 :]])
163+ # plt.ylim([k_vec[0], k_vec[-1:]])
164+ plt .legend (loc = 'lower left' , fontsize = fs , prop = {'size' :fs - 2 })
165+ plt .gca ().set_ylim ([0.0 , 1.1 ])
166+ #plt.xticks([0, 1, 2, 3], fontsize=fs)
167+ plt .show ()
0 commit comments