In [1]:
from sympy import init_session
init_session()
The Euler equations in primitive variable form, $q = (\rho, u, p)^\intercal$ appear as:
$$q_t + A(q) q_x = 0$$with the matrix $A(q)$:
$$A(q) = \left ( \begin{array}{ccc} u & \rho & 0 \\ 0 & u & 1/\rho \\ 0 & \gamma p & u \end{array} \right ) $$The sound speed is related to the adiabatic index, $\gamma$, as $c^2 = \gamma p /\rho$.
We can represent this matrix symbolically in SymPy and explore its eigensystem.
In [2]:
from sympy.abc import rho
rho, u, c = symbols('rho u c')
A = Matrix([[u, rho, 0], [0, u, rho**-1], [0, c**2 * rho, u]])
A
Out[2]:
The eigenvalues are the speeds at which information propagates with. SymPy returns them as a dictionary, giving the multiplicity for each eigenvalue.
In [3]:
A.eigenvals()
Out[3]:
The right eigenvectors are what SymPy gives natively. For a given eigenvalue, $\lambda$, these satisfy:
$$A r = \lambda r$$
In [4]:
R = A.eigenvects() # this returns a tuple for each eigenvector with multiplicity -- unpack it
r = []
lam = []
for (ev, _, rtmp) in R:
r.append(rtmp[0])
lam.append(ev)
# we can normalize them anyway we want, so let's make the first entry 1
for n in range(len(r)):
v = r[n]
r[n] = v/v[0]
In [5]:
r[0]
Out[5]:
this corresponds to the eigenvalue
In [6]:
lam[0]
Out[6]:
In [7]:
r[1]
Out[7]:
this corresponds to the eigenvalue
In [8]:
lam[1]
Out[8]:
In [9]:
r[2]
Out[9]:
this corresponds to the eigenvalue
In [10]:
lam[2]
Out[10]:
Here they are as a matrix, $R$, in order from smallest to largest eigenvalue
In [11]:
R = zeros(3,3)
R[:,0] = r[1]
R[:,1] = r[0]
R[:,2] = r[2]
R
Out[11]:
The left eigenvectors satisfy:
$$l A = \lambda l$$SymPy doesn't have a method to get left eigenvectors directly, so we take the transpose of this expression:
$$(l A)^\intercal = A^\intercal l^\intercal = \lambda l^\intercal$$Therefore, the transpose of the left eigenvectors, $l^\intercal$, are the right eigenvectors of transpose of $A$
In [12]:
B = A.transpose()
B
Out[12]:
In [13]:
L = B.eigenvects()
l = []
laml = []
for (ev, _, ltmp) in L:
l.append(ltmp[0].transpose())
laml.append(ev)
Traditionally, we normalize these such that $l^{(\mu)} \cdot r^{(\nu)} = \delta_{\mu\nu}$
In [14]:
for n in range(len(l)):
if lam[n] == laml[n]:
ltmp = l[n]
p = ltmp.dot(r[n])
l[n] = ltmp/p
In [15]:
l[0]
Out[15]:
In [16]:
l[1]
Out[16]:
In [17]:
l[2]
Out[17]:
In [ ]:
In [18]:
ps = symbols('p_s')
As = Matrix([[u, rho, 0], [c**2/rho, u, ps/rho], [0, 0, u]])
As
Out[18]:
In [19]:
As.eigenvals()
Out[19]:
In [20]:
R = As.eigenvects() # this returns a tuple for each eigenvector with multiplicity -- unpack it
r = []
lam = []
for (ev, _, rtmp) in R:
r.append(rtmp[0])
lam.append(ev)
# we can normalize them anyway we want, so let's make the first entry 1
for n in range(len(r)):
v = r[n]
r[n] = v/v[0]
In [21]:
r[0], lam[0]
Out[21]:
In [22]:
r[1], lam[1]
Out[22]:
In [23]:
r[2], lam[2]
Out[23]:
In [24]:
Bs = As.transpose()
L = B.eigenvects()
l = []
laml = []
for (ev, _, ltmp) in L:
l.append(ltmp[0].transpose())
laml.append(ev)
normalization
In [25]:
for n in range(len(l)):
if lam[n] == laml[n]:
ltmp = l[n]
p = ltmp.dot(r[n])
l[n] = ltmp/p
In [26]:
simplify(l[0])
Out[26]:
In [27]:
l[1]
Out[27]:
In [28]:
l[2]
Out[28]:
In [29]:
rho, u, v, c = symbols('rho u v c')
A = Matrix([[u, rho, 0, 0], [0, u, 0, rho**-1], [0,0, u, 0], [0, c**2 * rho, 0, u]])
A
Out[29]:
In [30]:
A.eigenvals()
Out[30]:
In [31]:
R = A.eigenvects() # this returns a tuple for each eigenvector with multiplicity -- unpack it
r = []
lam = []
for (ev, _, rtmp) in R:
for rv in rtmp:
r.append(rv)
lam.append(ev)
# we can normalize them anyway we want, so let's make the first entry 1
for n in range(len(r)):
v = r[n]
if not v[0] == 0:
r[n] = v/v[0]
In [32]:
r[0], lam[0]
Out[32]:
In [33]:
r[1], lam[1]
Out[33]:
In [34]:
r[2], lam[2]
Out[34]:
In [35]:
r[3], lam[3]
Out[35]:
In [ ]: