import numpy as np
from numba import njit
[docs]
@njit(cache=True)
def states(idir, ng, dx, dt,
irho, iu, iv, ip, ix, nspec,
gamma, qv, dqv):
r"""
predict the cell-centered state to the edges in one-dimension
using the reconstructed, limited slopes.
We follow the convection here that ``V_l[i]`` is the left state at the
i-1/2 interface and ``V_l[i+1]`` is the left state at the i+1/2
interface.
We need the left and right eigenvectors and the eigenvalues for
the system projected along the x-direction.
Taking our state vector as :math:`Q = (\rho, u, v, p)^T`, the eigenvalues
are :math:`u - c`, :math:`u`, :math:`u + c`.
We look at the equations of hydrodynamics in a split fashion --
i.e., we only consider one dimension at a time.
Considering advection in the x-direction, the Jacobian matrix for
the primitive variable formulation of the Euler equations
projected in the x-direction is::
/ u r 0 0 \
| 0 u 0 1/r |
A = | 0 0 u 0 |
\ 0 rc^2 0 u /
The right eigenvectors are::
/ 1 \ / 1 \ / 0 \ / 1 \
|-c/r | | 0 | | 0 | | c/r |
r1 = | 0 | r2 = | 0 | r3 = | 1 | r4 = | 0 |
\ c^2 / \ 0 / \ 0 / \ c^2 /
In particular, we see from r3 that the transverse velocity (v in
this case) is simply advected at a speed u in the x-direction.
The left eigenvectors are::
l1 = ( 0, -r/(2c), 0, 1/(2c^2) )
l2 = ( 1, 0, 0, -1/c^2 )
l3 = ( 0, 0, 1, 0 )
l4 = ( 0, r/(2c), 0, 1/(2c^2) )
The fluxes are going to be defined on the left edge of the
computational zones::
| | | |
| | | |
-+------+------+------+------+------+------+--
| i-1 | i | i+1 |
^ ^ ^
q_l,i q_r,i q_l,i+1
q_r,i and q_l,i+1 are computed using the information in zone i,j.
Parameters
----------
idir : int
Are we predicting to the edges in the x-direction (1) or y-direction (2)?
ng : int
The number of ghost cells
dx : ndarray
The cell spacing
dt : float
The timestep
irho, iu, iv, ip, ix : int
Indices of the density, x-velocity, y-velocity, pressure and species in the
state vector
nspec : int
The number of species
gamma : float
Adiabatic index
qv : ndarray
The primitive state vector
dqv : ndarray
Spatial derivative of the state vector
Returns
-------
out : ndarray, ndarray
State vector predicted to the left and right edges
"""
qx, qy, nvar = qv.shape
q_l = np.zeros_like(qv)
q_r = np.zeros_like(qv)
nx = qx - 2 * ng
ny = qy - 2 * ng
ilo = ng
ihi = ng + nx
jlo = ng
jhi = ng + ny
ns = nvar - nspec
dtdx = dt / dx
dtdx4 = 0.25 * dtdx
lvec = np.zeros((nvar, nvar))
rvec = np.zeros((nvar, nvar))
e_val = np.zeros(nvar)
betal = np.zeros(nvar)
betar = np.zeros(nvar)
# this is the loop over zones. For zone i, we see q_l[i+1] and q_r[i]
for i in range(ilo - 2, ihi + 2):
for j in range(jlo - 2, jhi + 2):
dq = dqv[i, j, :]
q = qv[i, j, :]
cs = np.sqrt(gamma * q[ip] / q[irho])
lvec[:, :] = 0.0
rvec[:, :] = 0.0
e_val[:] = 0.0
# compute the eigenvalues and eigenvectors
if idir == 1:
e_val[:] = np.array([q[iu] - cs, q[iu], q[iu], q[iu] + cs])
lvec[0, :ns] = [0.0, -0.5 *
q[irho] / cs, 0.0, 0.5 / (cs * cs)]
lvec[1, :ns] = [1.0, 0.0,
0.0, -1.0 / (cs * cs)]
lvec[2, :ns] = [0.0, 0.0, 1.0, 0.0]
lvec[3, :ns] = [0.0, 0.5 *
q[irho] / cs, 0.0, 0.5 / (cs * cs)]
rvec[0, :ns] = [1.0, -cs / q[irho], 0.0, cs * cs]
rvec[1, :ns] = [1.0, 0.0, 0.0, 0.0]
rvec[2, :ns] = [0.0, 0.0, 1.0, 0.0]
rvec[3, :ns] = [1.0, cs / q[irho], 0.0, cs * cs]
# now the species -- they only have a 1 in their corresponding slot
e_val[ns:] = q[iu]
for n in range(ix, ix + nspec):
lvec[n, n] = 1.0
rvec[n, n] = 1.0
else:
e_val[:] = np.array([q[iv] - cs, q[iv], q[iv], q[iv] + cs])
lvec[0, :ns] = [0.0, 0.0, -0.5 *
q[irho] / cs, 0.5 / (cs * cs)]
lvec[1, :ns] = [1.0, 0.0,
0.0, -1.0 / (cs * cs)]
lvec[2, :ns] = [0.0, 1.0, 0.0, 0.0]
lvec[3, :ns] = [0.0, 0.0, 0.5 *
q[irho] / cs, 0.5 / (cs * cs)]
rvec[0, :ns] = [1.0, 0.0, -cs / q[irho], cs * cs]
rvec[1, :ns] = [1.0, 0.0, 0.0, 0.0]
rvec[2, :ns] = [0.0, 1.0, 0.0, 0.0]
rvec[3, :ns] = [1.0, 0.0, cs / q[irho], cs * cs]
# now the species -- they only have a 1 in their corresponding slot
e_val[ns:] = q[iv]
for n in range(ix, ix + nspec):
lvec[n, n] = 1.0
rvec[n, n] = 1.0
# define the reference states
if idir == 1:
# this is one the right face of the current zone,
# so the fastest moving eigenvalue is e_val[3] = u + c
factor = 0.5 * (1.0 - dtdx[i, j] * max(e_val[3], 0.0))
q_l[i + 1, j, :] = q + factor * dq
# left face of the current zone, so the fastest moving
# eigenvalue is e_val[3] = u - c
factor = 0.5 * (1.0 + dtdx[i, j] * min(e_val[0], 0.0))
q_r[i, j, :] = q - factor * dq
else:
factor = 0.5 * (1.0 - dtdx[i, j] * max(e_val[3], 0.0))
q_l[i, j + 1, :] = q + factor * dq
factor = 0.5 * (1.0 + dtdx[i, j] * min(e_val[0], 0.0))
q_r[i, j, :] = q - factor * dq
# compute the Vhat functions
for m in range(nvar):
asum = np.dot(lvec[m, :], dq)
# Should we change to max(e_val[3], 0.0) and min(e_val[0], 0.0)?
betal[m] = dtdx4[i, j] * (e_val[3] - e_val[m]) * \
(np.copysign(1.0, e_val[m]) + 1.0) * asum
betar[m] = dtdx4[i, j] * (e_val[0] - e_val[m]) * \
(1.0 - np.copysign(1.0, e_val[m])) * asum
# construct the states
for m in range(nvar):
sum_l = np.dot(betal, np.ascontiguousarray(rvec[:, m]))
sum_r = np.dot(betar, np.ascontiguousarray(rvec[:, m]))
if idir == 1:
q_l[i + 1, j, m] = q_l[i + 1, j, m] + sum_l
q_r[i, j, m] = q_r[i, j, m] + sum_r
else:
q_l[i, j + 1, m] = q_l[i, j + 1, m] + sum_l
q_r[i, j, m] = q_r[i, j, m] + sum_r
return q_l, q_r
[docs]
@njit(cache=True)
def artificial_viscosity(ng, dx, dy, Lx, Ly,
xmin, ymin, coord_type,
cvisc, u, v):
r"""
Compute the artificial viscosity. Here, we compute edge-centered
approximations to the divergence of the velocity. This follows
directly Colella \ Woodward (1984) Eq. 4.5
data locations::
j+3/2--+---------+---------+---------+
| | | |
j+1 + | | |
| | | |
j+1/2--+---------+---------+---------+
| | | |
j + X | |
| | | |
j-1/2--+---------+----Y----+---------+
| | | |
j-1 + | | |
| | | |
j-3/2--+---------+---------+---------+
| | | | | | |
i-1 i i+1
i-3/2 i-1/2 i+1/2 i+3/2
``X`` is the location of ``avisco_x[i,j]``
``Y`` is the location of ``avisco_y[i,j]``
Parameters
----------
ng : int
The number of ghost cells
dx, dy : float
Cell spacings
xmin, ymin : float
Min physical x, y boundary
Lx, Ly: ndarray
Cell size in x, y direction
cvisc : float
viscosity parameter
u, v : ndarray
x- and y-velocities
Returns
-------
out : ndarray, ndarray
Artificial viscosity in the x- and y-directions
"""
qx, qy = u.shape
avisco_x = np.zeros((qx, qy))
avisco_y = np.zeros((qx, qy))
divU = np.zeros((qx, qy))
nx = qx - 2 * ng
ny = qy - 2 * ng
ilo = ng
ihi = ng + nx
jlo = ng
jhi = ng + ny
# Let's first compute divergence at the vertex
# First compute the left and right x-velocities by
# averaging over the y-interface.
# As well as the top and bottom y-velocities by
# averaging over the x-interface.
# Then a simple difference is done between the right and left,
# and top and bottom to get the divergence at the vertex.
for i in range(ilo - 1, ihi + 1):
for j in range(jlo - 1, jhi + 1):
# For Cartesian2d:
if coord_type == 0:
# Find the average right and left u velocity
ur = 0.5 * (u[i, j] + u[i, j - 1])
ul = 0.5 * (u[i - 1, j] + u[i - 1, j - 1])
# Find the average top and bottom v velocity
vt = 0.5 * (v[i, j] + v[i - 1, j])
vb = 0.5 * (v[i, j - 1] + v[i - 1, j - 1])
# Finite difference to get ux and vy
ux = (ur - ul) / dx
vy = (vt - vb) / dy
# Find div(U)_{i-1/2, j-1/2}
divU[i, j] = ux + vy
# For SphericalPolar:
else:
# cell-centered r-coord of right, left cell and node-centered r
rr = (i + 0.5 - ng) * dx + xmin
rl = (i - 0.5 - ng) * dx + xmin
rc = (i - ng) * dx + xmin
# Find the average right and left u velocity
ur = 0.5 * (u[i, j] + u[i, j - 1])
ul = 0.5 * (u[i - 1, j] + u[i - 1, j - 1])
# Finite difference to get ux and vy
ux = (ur * rr * rr - ul * rl * rl) / (rc * rc * dx)
# cell-centered sin(theta) of top, bot cell and node-centered sin(theta)
sint = np.sin((j + 0.5 - ng) * dy + ymin)
sinb = np.sin((j - 0.5 - ng) * dy + ymin)
sinc = np.sin((j - ng) * dy + ymin)
# if sinc = 0, i.e. theta= {0, pi}, then vy goes inf
# but due to phi-symmetry, numerator cancel, so zero.
if sinc == 0.0:
vy = 0.0
else:
# Find the average top and bottom v velocity
vt = 0.5 * (v[i, j] + v[i - 1, j])
vb = 0.5 * (v[i, j - 1] + v[i - 1, j - 1])
vy = (sint * vt - sinb * vb) / (rc * sinc * dy)
# Find div(U)_{i-1/2, j-1/2}
divU[i, j] = ux + vy
# Compute divergence at the face by averaging over divergence at vertex
for i in range(ilo, ihi):
for j in range(jlo, jhi):
# div(U)_{i-1/2, j} = 0.5 (div(U)_{i-1/2, j-1/2} + div(U)_{i-1/2, j+1/2})
divU_x = 0.5 * (divU[i, j] + divU[i, j + 1])
# div(U)_{i, j-1/2} = 0.5 (div(U)_{i-1/2, j-1/2} + div(U)_{i+1/2, j-1/2})
divU_y = 0.5 * (divU[i, j] + divU[i + 1, j])
avisco_x[i, j] = cvisc * max(-divU_x * Lx[i, j], 0.0)
avisco_y[i, j] = cvisc * max(-divU_y * Ly[i, j], 0.0)
return avisco_x, avisco_y