예제 #1
0
def compute_eigenvector(U, U0, direction):
    rho = U[:, 0]
    u = U[:, 1] / rho
    v = U[:, 2] / rho
    E = U[:, 3] / rho

    P = P_from_Ev(E, rho, u, v)

    nunk = U.shape[1]
    nelem = U.shape[0]

    a = np.sqrt(GAM * P / rho)
    q = 1 / 2 * (u**2 + v**2)  # Dynamic pressure
    h = a**2 / (GAM - 1) + q  # Enthalpy

    rho0 = U0[:, 0]
    u0 = U0[:, 1] / rho0
    v0 = U0[:, 2] / rho0
    E0 = U0[:, 3] / rho0

    P0 = P_from_Ev(E0, rho0, u0, v0)

    nunk = U0.shape[1]
    nelem = U0.shape[0]

    a0 = np.sqrt(GAM * P0 / rho0)
    q0 = 1 / 2 * (u0**2 + v0**2)  # Dynamic pressure
    h0 = a0**2 / (GAM - 1) + q0  # Enthalpy

    Rjlist = []
    Ljlist = []

    if direction == 'dx':
        Rlhs0, Llhs0 = eigenvector_x(u0[0], v0[0], a0[0], q0[0], h0[0], nunk)
        for idx in range(nelem):
            Rj, Lj = eigenvector_x(u[idx], v[idx], a[idx], q[idx], h[idx],
                                   nunk)
            Rjlist.append(Rj)
            Ljlist.append(Lj)
        Rlhs0pre = None
        Llhs0pre = None

    elif direction == 'dy':
        # For the y-direction, the bottom boundary can either be pre or post-shock
        Rlhs0, Llhs0 = eigenvector_y(u0[0], v0[0], a0[0], q0[0], h0[0], nunk)
        Rlhs0pre, Llhs0pre = eigenvector_y(u0[-1], v0[-1], a0[-1], q0[-1],
                                           h0[-1], nunk)
        for idx in range(nelem):
            Rj, Lj = eigenvector_y(u[idx], v[idx], a[idx], q[idx], h[idx],
                                   nunk)
            Rjlist.append(Rj)
            Ljlist.append(Lj)

    Rj = Rjlist
    Lj = Ljlist

    return Rj, Lj, Rlhs0, Llhs0, Rlhs0pre, Llhs0pre
def flux_pressure(UL, UR, ah, MaL, MaR):
    #    rhoL = UL[:,0]
    #    rhoR = UR[:,0]
    #
    #    vL = UL[:,1]/rhoL
    #    vR = UR[:,1]/rhoR
    #
    #    EL = UL[:,2]/rhoL
    #    ER = UR[:,2]/rhoR
    #
    #    PL = P_from_Ev(EL,rhoL,vL)
    #    PR = P_from_Ev(ER,rhoR,vR)
    #    aL = np.sqrt(GAM*PL/rhoL)
    #    aR = np.sqrt(GAM*PR/rhoR)
    #
    #    MaLloc = vL/aL
    #    MaRloc = vR/aR
    #
    #    qL = np.zeros(UL.shape)
    #    qR = np.zeros(UR.shape)
    #
    #    qL[:,1] = PL * MaLloc
    #    qL[:,2] = PL * aL
    #
    #    qR[:,1] = PR * MaRloc
    #    qR[:,2] = PR * aR
    #
    #    return 1/2 * (qR-qL)

    # Get the pressure from primitive variables
    rhoL = UL[:, 0]
    rhoR = UR[:, 0]

    vL = UL[:, 1] / rhoL
    vR = UR[:, 1] / rhoR

    EL = UL[:, 2] / rhoL
    ER = UR[:, 2] / rhoR

    PL = P_from_Ev(EL, rhoL, vL)
    PR = P_from_Ev(ER, rhoR, vR)

    Pp, Pm = compute_Ppm(MaL, MaR)

    Fp = np.zeros(UL.shape)
    Fp[:, 1] = Pp * PL + Pm * PR
    Fp[:, 2] = 1 / 2 * (PL * (vL + ah) + PR * (vR - ah))

    return Fp
def compute_right_eigenvector(U):
    rho = U[:,0]
    v = U[:,1] / rho
    E = U[:,2] / rho
    
    P = P_from_Ev(E,rho,v)
    
    a = np.sqrt(GAM*P/rho)
    Rjlist = []
    Rjinvlist = []
    
    for idx in np.arange(0,len(rho),1):
        Rj = np.zeros((3,3))
        Rj[0,:] = np.ones((1,3))
        
        Rj[1,0] = v[idx]
        Rj[1,1] = v[idx] - a[idx]
        Rj[1,2] = v[idx] + a[idx]
        
        h = v[idx]**2/2 + a[idx]**2/(GAM-1)        
        Rj[2,0] = v[idx]**2/2
        Rj[2,1] = h - v[idx]*a[idx]
        Rj[2,2] = h + v[idx]*a[idx]
        
        Rjlist.append(Rj)
        Rjinvlist.append(np.linalg.inv(Rj))
        
    Rj = Rjlist
    Rjinv = Rjinvlist

    return Rj,Rjinv     
def compute_alphaR(UL, UR):
    rhoL = UL[:, 0]
    rhoR = UR[:, 0]

    vL = UL[:, 1] / rhoL
    vR = UR[:, 1] / rhoR

    EL = UL[:, 2] / rhoL
    ER = UR[:, 2] / rhoR

    PL = P_from_Ev(EL, rhoL, vL)
    PR = P_from_Ev(ER, rhoR, vR)

    num = 2 * (PR / rhoR)
    den = (PL / rhoL) + (PR / rhoR)

    return num / den
예제 #5
0
def compute_euler_flux(U):
    rho = U[:, 0]
    v = U[:, 1] / rho
    E = U[:, 2] / rho

    P = P_from_Ev(E, rho, v)

    flx = np.zeros(U.shape)

    flx[:, 0] = rho * v
    flx[:, 1] = rho * v**2 + P
    flx[:, 2] = rho * E * v + P * v

    return flx
예제 #6
0
def compute_eigenvector(U):
    rho = U[:, 0]
    v = U[:, 1] / rho
    E = U[:, 2] / rho

    P = P_from_Ev(E, rho, v)

    a = np.sqrt(GAM * P / rho)
    Rjlist = []
    Rjinvlist = []

    for idx in np.arange(0, len(rho), 1):
        Rj = np.zeros((3, 3))
        Rjinv = np.zeros((3, 3))
        # Right eigenvector
        Rj[0, :] = np.ones((1, 3))

        Rj[1, 0] = v[idx] - a[idx]
        Rj[1, 1] = v[idx]
        Rj[1, 2] = v[idx] + a[idx]

        h = v[idx]**2 / 2 + a[idx]**2 / (GAM - 1)
        Rj[2, 0] = h - v[idx] * a[idx]
        Rj[2, 1] = v[idx]**2 / 2
        Rj[2, 2] = h + v[idx] * a[idx]

        # Left eigenvector
        voa = v[idx] / a[idx]  # v over a
        a2 = a[idx]**2
        Rjinv[0, 0] = 1 / 2 * (1 / 2 * (GAM - 1) * voa**2 + voa)
        Rjinv[0, 1] = -1 / (2 * a[idx]) * ((GAM - 1) * voa + 1)
        Rjinv[0, 2] = (GAM - 1) / (2 * a2)

        Rjinv[1, 0] = 1 - 1 / 2 * (GAM - 1) * voa**2
        Rjinv[1, 1] = (GAM - 1) * v[idx] / a2
        Rjinv[1, 2] = -(GAM - 1) / a2

        Rjinv[2, 0] = 1 / 2 * (1 / 2 * (GAM - 1) * voa**2 - voa)
        Rjinv[2, 1] = -1 / (2 * a[idx]) * ((GAM - 1) * voa - 1)
        Rjinv[2, 2] = (GAM - 1) / (2 * a2)

        Rjlist.append(Rj)
        Rjinvlist.append(Rjinv)

    Rj = Rjlist
    Rjinv = Rjinvlist

    return Rj, Rjinv
예제 #7
0
def compute_euler_flux(U, direction):
    rho = U[:, 0]
    u = U[:, 1] / rho
    v = U[:, 2] / rho
    E = U[:, 3] / rho

    P = P_from_Ev(E, rho, u, v)

    flx = np.zeros(U.shape)

    if direction == 'dx':
        flx[:, 0] = rho * u
        flx[:, 1] = rho * u**2 + P
        flx[:, 2] = rho * u * v
        flx[:, 3] = rho * E * u + P * u

    elif direction == 'dy':
        flx[:, 0] = rho * v
        flx[:, 1] = rho * u * v
        flx[:, 2] = rho * v**2 + P
        flx[:, 3] = rho * E * v + P * v

    return flx
예제 #8
0
def compute_lfc_flux(u, U0, dz, order):
    ### Indices
    nelem = u.shape[0]  # Number of elements
    nunk = u.shape[1]  # Number of unknowns

    # What's r that corresponds to our WENO order?
    rweno = int((order - 1) / 2)

    # u_{i+1}, u_{i-1}
    up1 = np.roll(u, -1, axis=0)
    um1 = np.roll(u, 1, axis=0)

    ### Roe average at u^{i+-1/2}
    up1h, um1h = roe_average(u, U0)
    #    up1h = (u+up1)*1/2

    ### LF Flux splitting: f = f^+ + f^- where
    ### f^{+-} = 1/2*(F[U] + alpha * U)
    ### We do reconstruction on the FLUX

    ### Calculate the LF split flux in all cells
    rho = u[:, 0]
    v = u[:, 1] / rho
    E = u[:, 2] / rho

    P = P_from_Ev(E, rho, v)

    a = np.sqrt(GAM * P / rho)

    # We want the max eigenvalue
    l1 = np.abs(v - a)
    l2 = np.abs(v)
    l3 = np.abs(v + a)

    eig = np.vstack((l1, l2, l3))
    alpha = np.max(eig, axis=1)  # GLOBAL MAX

    ### Characteristics
    # Eigenvectors: Compute the block matrix at each right eigenvector
    Rjp1h, Rjinvp1h = compute_eigenvector(up1h)

    ### Compute the flux
    flx = compute_euler_flux(u)
    flx0 = compute_euler_flux(U0)

    ######
    # I + 1/2
    ######
    ### Transform into the characteristic domain
    v, g, vlf = to_characteristics(u, flx, U0, flx0, order, Rjinvp1h, alpha)

    ### Compute f+, f- at i+1/2 for all elements on the stencil
    FLXp = np.zeros((nelem, order, nunk))
    FLXm = np.zeros((nelem, order, nunk))

    FLXp = 1 / 2 * (g[:, 0:order, :] + vlf[:, 0:order, :])
    FLXm = 1 / 2 * (g[:, 1:order + 1, :] - vlf[:, 1:order + 1, :])

    #    if order == 5:
    #        FLXp = 1/2*(g[:,0:5,:] + vlf[:,0:5,:])
    #        FLXm = 1/2*(g[:,1:6,:] - vlf[:,1:6,:])
    #    elif order == 3:
    #        FLXp = 1/2*(g[:,0:3,:] + vlf[:,0:3,:])
    #        FLXm = 1/2*(g[:,1:6,:] - vlf[:,1:6,:])
    #    FLXm = 1/2*(g - vlf)

    ### Reconstruct WENO in the characteristics domain
    # Reconstruct the data on the stencil
    fp1hL, fm1hR = compute_lr(FLXp, order)
    fp3hL, fp1hR = compute_lr(FLXm, order)

    # Compute the flux
    FLXp1h = fp1hL + fp1hR
    FLXm1h = np.roll(FLXp1h, 1, axis=0)

    #    t1 = np.copy(FLXp1h)
    #    t2 = np.copy(FLXm1h)

    ### Go back into the normal domain
    for idx in range(nelem):
        FLXp1h[idx] = np.matmul(Rjp1h[idx], FLXp1h[idx])
        if idx > 0:
            FLXm1h[idx] = np.matmul(Rjp1h[idx - 1], FLXm1h[idx])
        else:
            FLXm1h[idx] = np.matmul(Rjp1h[0], FLXm1h[idx])

    return -1 / dz * (FLXp1h - FLXm1h)
예제 #9
0
def compute_lf_flux(u, U0, dz, order):
    ### Calculate the max eigenvalue of all cells
    rho = u[:, 0]
    v = u[:, 1] / rho
    E = u[:, 2] / rho

    P = P_from_Ev(E, rho, v)

    a = np.sqrt(GAM * P / rho)

    # We want the max eigenvalue
    l1 = np.abs(v - a)
    l2 = np.abs(v)
    l3 = np.abs(v + a)

    eig = np.vstack((l1, l2, l3))
    alpha = np.max(eig, axis=1)  # GLOBAL MAX

    ### LF Flux splitting: f = f^+ + f^- where
    ### f^{+-} = 1/2*(F[U] + alpha * U)
    ### We do reconstruction on the FLUX
    flx = compute_euler_flux(u)
    vlf = np.matmul(np.diag(alpha), u.T).T

    ### Characteristics
    # Eigenvectors: Compute the block matrix at each right eigenvector
    up1h = u
    Rjp1h, Rjinvp1h = compute_eigenvector(up1h)

    ### Compute the flux
    flx = compute_euler_flux(u)
    flx0 = compute_euler_flux(U0)

    nelem = u.shape[0]
    nunk = u.shape[1]

    ######
    # I + 1/2
    ######
    ### Transform into the characteristic domain
    v, g, vlf = to_characteristics(u, flx, U0, flx0, order, Rjinvp1h, alpha)

    ### Compute f+, f- at i+1/2 for all elements on the stencil
    FLXp = np.zeros((nelem, order, nunk))
    FLXm = np.zeros((nelem, order, nunk))

    FLXp = 1 / 2 * (g[:, 0:order, :] + vlf[:, 0:order, :])
    FLXm = 1 / 2 * (g[:, 1:order + 1, :] - vlf[:, 1:order + 1, :])

    ### Reconstruct WENO in the characteristics domain
    # Reconstruct the data on the stencil
    fp1hL, fm1hR = compute_lr(FLXp, order)
    fp3hL, fp1hR = compute_lr(FLXm, order)

    # Compute the flux
    FLXp1h = fp1hL + fp1hR
    FLXm1h = np.roll(FLXp1h, 1, axis=0)

    ### Go back into the normal domain
    for idx in range(nelem):
        FLXp1h[idx] = np.matmul(Rjp1h[idx], FLXp1h[idx])
        if idx > 0:
            FLXm1h[idx] = np.matmul(Rjp1h[idx - 1], FLXm1h[idx])
        else:
            FLXm1h[idx] = np.matmul(Rjp1h[0], FLXm1h[idx])

    return -1 / dz * (FLXp1h - FLXm1h)
예제 #10
0
def compute_lfc_flux_1D(U, U0, dx, dy, Nx, Ny, order, direction, options, tc,
                        lambda_calc_char):
    nelem = U.shape[0]
    nunk = U.shape[1]

    ### Compute the average at the border
    Up1h = compute_average(U, U0, dx, dy, Nx, Ny, direction)

    ### Calculate the LF split flux in all cells
    rho = U[:, 0]
    u = U[:, 1] / rho
    v = U[:, 2] / rho
    E = U[:, 3] / rho

    P = P_from_Ev(E, rho, u, v)
    a = np.sqrt(GAM * P / rho)

    # We want the max eigenvalue
    if direction == 'dx':
        l1 = np.abs(u - a)
        l2 = np.abs(u)
        l3 = np.abs(u + a)
        l4 = np.abs(u)

    elif direction == 'dy':
        l1 = np.abs(v - a)
        l2 = np.abs(v)
        l3 = np.abs(v + a)
        l4 = np.abs(v)

    eig = np.vstack((l1, l2, l3, l4))
    alpha = np.max(eig, axis=1)  # GLOBAL MAX

    ### Characteristics
    # Eigenvectors: Compute the block matrix at each right eigenvector
    if direction == 'dx':
        Rh, Lh, Rlhs0, Llhs0, _, _ = compute_eigenvector(Up1h, U0, direction)
    elif direction == 'dy':
        Rh, Lh, Rlhs0, Llhs0, Rlhs0pre, Llhs0pre = compute_eigenvector(
            Up1h, U0, direction)

    ### Compute the flux
    flx = compute_euler_flux(U, direction)
    flx0 = compute_euler_flux(U0, direction)

    ### Transform into the characteristic domain
    # V = R^-1 U, H = R^-1 * F or R^-1 * G, VLF = alpha * V
    #    V,H,VLF = to_characteristics(U,flx,U0,flx0,order,Lh,alpha,Nx,Ny,direction)
    V, H, VLF = to_characteristics(U, flx, U0, flx0, order, Lh, alpha, Nx, Ny,
                                   direction, options, tc, lambda_calc_char)

    ### Compute f+, f- at i+1/2 for all elements on the stencil
    FLXp = np.zeros((nelem, order, nunk))
    FLXm = np.zeros((nelem, order, nunk))

    FLXp = 1 / 2 * (H[:, 0:order, :] + VLF[:, 0:order, :])
    FLXm = 1 / 2 * (H[:, 1:order + 1, :] - VLF[:, 1:order + 1, :])

    fp1hL, fm1hR = compute_lr(FLXp, order)
    fp3hL, fp1hR = compute_lr(FLXm, order)

    # Compute the flux
    FLXp1h = fp1hL + fp1hR

    if direction == 'dx':
        FLXm1h = np.roll(FLXp1h, 1, axis=0)
    elif direction == 'dy':
        FLXm1h = np.roll(FLXp1h, Nx, axis=0)

    ### Go back into the normal domain
    if direction == 'dx':
        for idx in range(nelem):
            FLXp1h[idx] = np.matmul(Rh[idx], FLXp1h[idx])
            if idx % Nx != 0:
                FLXm1h[idx] = np.matmul(Rh[idx - 1], FLXm1h[idx])
            else:
                FLXm1h[idx] = np.matmul(Rlhs0, FLXm1h[idx])
    elif direction == 'dy':
        for idx in range(nelem):
            FLXp1h[idx] = np.matmul(Rh[idx], FLXp1h[idx])
            if idx > Nx - 1:  # Any cell with an index higher than Nx is NOT on the bottom boundary
                FLXm1h[idx] = np.matmul(Rh[idx - Nx], FLXm1h[idx])
            else:
                FLXm1h[idx] = np.matmul(
                    Rlhs0, FLXm1h[idx]
                )  # We don't care about what's at the bottom boundary: it will be overwritten

    if direction == 'dx':
        dz = dx
    elif direction == 'dy':
        dz = dy

    return -1 / dz * (FLXp1h - FLXm1h)
예제 #11
0
order = 5
flux_type = 'LFC'

# Data holders
# [rho,rho*u,E]
U = np.zeros([len(zvec), 3])

# Case definition
caseNum = 7
left, right, cfl, tmax = defineCase(caseNum)
#tmax = 0.02
f_0(U)
U0 = np.copy(U)

# Time
P0 = P_from_Ev(U[:, 2] / U[:, 0], U[:, 0], U[:, 1] / U[:, 0])
a0 = np.sqrt(GAM * P0 / U[:, 0])
v0 = U[:, 1] / U[:, 0]
lam0 = np.max(v0 + a0)

dt = dz / lam0 * cfl
tc = 0

# Plots
f, axarr = plt.subplots(3, 2)
axarr[0, 0].set_title("Density")
axarr[0, 1].set_title("Pressure")
axarr[1, 0].set_title("Temperature")
axarr[1, 1].set_title("Velocity")
axarr[2, 0].set_title("Mach number")
예제 #12
0
def compute_ecusp_flux(u,U0,dz,order):
    # u_{i+1}, u_{i-1}
    up1 = np.roll(u,-1,axis=0)
    um1 = np.roll(u,1,axis=0)  
    
    ### Reconstruct the data on the stencil
    up1hL, um1hR = compute_lr(up1,u,um1,order)  
    up1hR = np.roll(um1hR,-1,axis=0)
                
    # Compute the RHS flux
    um1hL = np.roll(up1hL,1,axis=0) # This will contain u_{i-1/2}^L
       
    ### i + 1/2
    # Calculate the convective flux
    rhoL = up1hL[:,0]
    rhoR = up1hR[:,0]

    
    vL = up1hL[:,1]/rhoL
    vR = up1hR[:,1]/rhoR
    
    EL = up1hL[:,2]/rhoL
    ER = up1hR[:,2]/rhoR
    
    PL = P_from_Ev(EL,rhoL,vL)
    PR = P_from_Ev(ER,rhoR,vR)
    
    ap1hL = np.sqrt(GAM*PL/rhoL)
    ap1hR = np.sqrt(GAM*PR/rhoR)
    
    ah = 1/2*(ap1hL+ap1hR)
 
    MaL = vL/ah
    MaR = vR/ah

    
    fp1hc = flux_convective(up1hL,up1hR,ah,MaL,MaR)    
    fp1hp = flux_pressure(up1hL,up1hR,ah,MaL,MaR)
    
    ### i - 1/2
    rhoL = um1hL[:,0]
    rhoR = um1hR[:,0]
    
    vL = um1hL[:,1]/rhoL
    vR = um1hR[:,1]/rhoR
    
    EL = um1hL[:,2]/rhoL
    ER = um1hR[:,2]/rhoR
    
    PL = P_from_Ev(EL,rhoL,vL)
    PR = P_from_Ev(ER,rhoR,vR)
    am1hL = np.sqrt(GAM*PL/rhoL)
    am1hR = np.sqrt(GAM*PR/rhoR)
    
    ah = 1/2*(am1hL+am1hR)

    MaL = vL/ah
    MaR = vR/ah
    
    fm1hc = flux_convective(um1hL,um1hR,ah,MaL,MaR)
    fm1hp = flux_pressure(um1hL,um1hR,ah,MaL,MaR)


    ### Calculate all types of fluxes
    # Subsonic
    fRsub = (fp1hc + fp1hp)
    fLsub = (fm1hc + fm1hp)
    
    
    # Supersonic traveling right: reconstruct flux on boundary
    flx = compute_euler_flux(u)
    
    # u_{i+1}, u_{i-1}
    flxp1 = np.roll(flx,-1,axis=0)
    flxm1 = np.roll(flx,1,axis=0)   
    
    fp1hL, fm1hR = compute_lr(flxp1,flx,flxm1,order) 
    fm1hL = np.roll(fp1hL,1,axis=0)
    fRsupr = fp1hL
    fLsupr = fm1hL
#    fRsupr = compute_euler_flux(up1hL)
#    fLsupr = compute_euler_flux(um1hL)
    
    # Supersonic travelight left
    fRsupl = compute_euler_flux(up1hR)
    fLsupl = compute_euler_flux(um1hR)    
    
    
    ### Make sure we use the applicable formula, which depends on the local speed
    # What's the local speed of sound?
    vloc = u[:,1]/u[:,0]
    Eloc = u[:,2]/u[:,0]
    rholoc = u[:,0]
    
    Ploc = P_from_Ev(Eloc,rholoc,vloc)
    aloc = np.sqrt(GAM*Ploc/rholoc)

    b1 = (np.abs(vloc) <= aloc)
    b2 = vloc > aloc
    b3 = vloc < -aloc
    
    fR = np.zeros(u.shape)
    fL = np.zeros(u.shape)
        
    for idx in np.arange(0,3,1):
        fR[:,idx] = fRsub[:,idx] * b1 + fRsupr[:,idx] * b2 + fRsupl[:,idx] * b3
        fL[:,idx] = fLsub[:,idx] * b1 + fLsupr[:,idx] * b2 + fLsupl[:,idx] * b3

    return -1/dz * (fR-fL)
예제 #13
0
# dataZ = griddata(grid, U0[:,1], (xv, yv), method='nearest')

# Plots
f, axarr = plt.subplots(3, 2)
axarr[0, 0].set_title("Density")
axarr[0, 1].set_title("Pressure")
axarr[1, 0].set_title("Temperature")
axarr[1, 1].set_title("x-Velocity")
axarr[2, 0].set_title("Mach number")
axarr[2, 1].set_title("Energy")

rho = U0[:, 0]
u = U0[:, 1] / U0[:, 0]
v = U0[:, 2] / U0[:, 0]
E = U0[:, 3] / U0[:, 0]
P = P_from_Ev(E, rho, u, v)
asos = np.sqrt(GAM * P / rho)
lam = np.max(np.abs(v) + asos)

rhoZ = griddata(grid, rho, (xv, yv), method='nearest')
uZ = griddata(grid, u, (xv, yv), method='nearest')
vZ = griddata(grid, v, (xv, yv), method='nearest')
EZ = griddata(grid, E, (xv, yv), method='nearest')
PZ = griddata(grid, P, (xv, yv), method='nearest')
TZ = PZ / rhoZ
aZ = np.sqrt(GAM * PZ / rhoZ)
MZ = np.sqrt(uZ**2 + vZ**2) / aZ

p0 = axarr[0, 0].contour(xv, yv, rhoZ)
p1 = axarr[0, 1].contour(xv, yv, PZ)
p2 = axarr[1, 0].contour(xv, yv, TZ)
def compute_lf_flux(u,U0,dz,order):
    # u_{i+1}, u_{i-1}
    up1 = np.roll(u,-1,axis=0)
    um1 = np.roll(u,1,axis=0)  

    ### Roe average at u^{i+-1/2}
    up1h, um1h = roe_average(u,U0)

    ### LF Flux splitting: f = f^+ + f^- where
    ### f^{+-} = 1/2*(F[U] + alpha * U)
    ### We do reconstruction on the FLUX
    
    ### Calculate the LF split flux in all cells
    rho = u[:,0]
    v = u[:,1]/rho
    E = u[:,2]/rho
     
    P = P_from_Ev(E,rho,v)
    
    a = np.sqrt(GAM*P/rho)
    
    # We want the max eigenvalue  
    l1 = np.abs(v-a)   
    l2 = np.abs(v)
    l3 = np.abs(v+a)
    
    eig = np.vstack((l1,l2,l3))
    eig = np.transpose(eig)
    alpha = np.max(eig) # GLOBAL MAX

    ### Characteristics
    # Eigenvectors: Compute the block matrix at each right eigenvector
    Rjp1h,Rjinvp1h = compute_right_eigenvector(up1h);
    Rjm1h,Rjinvm1h = compute_right_eigenvector(um1h);

    # Transform into the characteristic domain
    v = np.copy(u)
    g = np.copy(u) 
    
    flx = compute_euler_flux(u)
    
    ######
    # I + 1/2
    ######
    nelem = u.shape[0]
    for idx in np.arange(0,nelem,1):
        v[idx,:] = np.matmul(Rjinvp1h[idx],u[idx,:])
        g[idx,:] = np.matmul(Rjinvp1h[idx],flx[idx,:])

    ### Reconstruct WENO in the characteristics domain
    FLXp = np.zeros(u.shape)
    FLXm = np.zeros(u.shape)
    for idx in np.arange(0,3,1):
        FLXp[:,idx] = 1/2*(g[:,idx] + alpha*v[:,idx])
        FLXm[:,idx] = 1/2*(g[:,idx] - alpha*v[:,idx])
    
    ### i + 1/2 ^+
    fp1 = np.roll(FLXp,-1,axis=0)
    fm1 = np.roll(FLXp,1,axis=0)
    
    # Reconstruct the data on the stencil
    fp1hL, fm1hR = compute_lr(fp1,FLXp,fm1,order)
    fp1hR = np.roll(fm1hR,-1,axis=0)
    #up1hL, um1hR = compute_lr(up1,u,um1,order)  
    #up1hR = np.roll(um1hR,-1,axis=0)
                
    # Compute the RHS flux
    fiph1_P = fp1hL
    
    ### i+1/2 ^-
    fp1 = np.roll(FLXm,-1,axis=0)
    fm1 = np.roll(FLXm,1,axis=0)
    
    # Reconstruct the data on the stencil
    fp1hL, fm1hR = compute_lr(fp1,FLXm,fm1,order)
    fp1hR = np.roll(fm1hR,-1,axis=0)
    fiph1_M = fp1hR
    
    ### Go back to component domain
    for idx in np.arange(0,nelem,1):
        fiph1_P[idx,:] = np.matmul(Rjp1h[idx],fiph1_P[idx,:])
        fiph1_M[idx,:] = np.matmul(Rjp1h[idx],fiph1_M[idx,:])

    FLXp1h = fiph1_P + fiph1_M
    
    ######
    # I - 1/2
    ######
    nelem = u.shape[0]
    for idx in np.arange(0,nelem,1):
        v[idx,:] = np.matmul(Rjinvm1h[idx],u[idx,:])
        g[idx,:] = np.matmul(Rjinvm1h[idx],flx[idx,:])

    ### Reconstruct WENO in the characteristics domain
    FLXp = np.zeros(u.shape)
    FLXm = np.zeros(u.shape)
    for idx in np.arange(0,3,1):
        FLXp[:,idx] = 1/2*(g[:,idx] + alpha*v[:,idx])
        FLXm[:,idx] = 1/2*(g[:,idx] - alpha*v[:,idx])
    
    ### i - 1/2 ^+
    fp1 = np.roll(FLXp,-1,axis=0)
    fm1 = np.roll(FLXp,1,axis=0)
    
    # Reconstruct the data on the stencil
    fp1hL, fm1hR = compute_lr(fp1,FLXp,fm1,order)
    fp1hR = np.roll(fm1hR,-1,axis=0)
    #up1hL, um1hR = compute_lr(up1,u,um1,order)  
    #up1hR = np.roll(um1hR,-1,axis=0)
                
    # Compute the RHS flux
    fiph1_M = fm1hR
    
    ### i-1/2 ^-
    fp1 = np.roll(FLXm,-1,axis=0)
    fm1 = np.roll(FLXm,1,axis=0)
    
    # Reconstruct the data on the stencil
    fp1hL, fm1hR = compute_lr(fp1,FLXm,fm1,order)
#    fp1hR = np.roll(fm1hR,-1,axis=0)
    fm1hL = np.roll(fp1hL,1,axis=0)
    fiph1_P = fm1hL
    
    ### Go back to component domain
    for idx in np.arange(0,nelem,1):
        fiph1_P[idx,:] = np.matmul(Rjm1h[idx],fiph1_P[idx,:])
        fiph1_M[idx,:] = np.matmul(Rjm1h[idx],fiph1_M[idx,:])    
        
    FLXm1h = fiph1_P + fiph1_M

    return -1/dz*(FLXp1h - FLXm1h)