Skip to content
Snippets Groups Projects
reference_implementation.py 5.14 KiB
Newer Older
import numpy as np
import numpy.linalg as la  # noqa: F401
import pyopencl as cl  # noqa: F401
import pyopencl.array  # noqa
import pyopencl.tools  # noqa
import pyopencl.clrandom  # noqa
import loopy as lp  # noqa

# FIXME: add new file ideal_gas.py that has flux, pressure, etc. (any
# computation that depends on EOS

def pressure(state):
    # FIXME: for ideal gas stuff, make nvars, ndim, gamma, etc module level
    # constants
    # FIXME: also split out velocity, rho, energy into separate routines
    gamma = 1.4
    ndim = state.size - 2

    rho = state[0]
    energy = state[ndim+1]

    ke = 0.0
    for i in range(ndim):
        ke += (state[i+1]/rho)**2
    ke = 0.5*rho*ke

    return (gamma - 1)*(energy - ke)


def sound_speed(state):
    gamma = 1.4
    p = pressure(state)
    rho = state[0]

    return np.sqrt(gamma*p/rho)


def velocity(state):
    rho = state[0]
    return state[1:4]/rho


def flux(state):
    nvars = state.size
    ndim = nvars - 2
    result = u.empty_array(nvars, ndim)
    vel = state[1:ndim+1]/rho
    energy = state[ndim+1]
    p = pressure(state)

    for i in range(ndim):
        result[:,i] = vel[i]*state
        result[ndim+1,i] += vel[i]*p

    return result


def pointwise_fluxes(states):
    nvars, npoints = states.shape
    ndim = 3
    result = u.empty_array(nvars, ndim, npoints)

    for k in range(npoints):
        result[:,:,k] = flux(states[:,k])

    return result


def roe_eigensystem(state_pair, frozen_metrics, direction):
    nvars = state_pair.shape[0]
    ndim = frozen_metrics.shape[0]

    prg = u.get_weno_program_with_root_kernel("roe_eigensystem")
    queue = u.get_queue(cl._csc)

    R_dev = u.empty_array_on_device(queue, nvars, nvars)
    R_inv_dev = u.empty_array_on_device(queue, nvars, nvars)
    lam_dev = u.empty_array_on_device(queue, nvars)

    prg(queue, nvars=nvars, ndim=ndim, d=direction,
            states=state_pair, metrics_frozen=frozen_metrics,
            R=R_dev, R_inv=R_inv_dev, lambda_roe=lam_dev)

    return R_dev.get(), R_inv_dev.get(), lam_dev.get()


def lambda_pointwise(states, metrics, direction):
    def lam(state, metric_norm, direction):
        c = sound_speed(state)
        c_norm = c*metric_norm[direction]
        vel = velocity(state)[direction]
        result = np.repeat(vel, state.size)
        result[-2] += c_norm
        result[-1] -= c_norm

        return result

    metric_norm = np.sqrt((metrics**2).sum(axis=1))

    return np.array([lam(s, m, direction-1)
        for s, m in zip(states.T, metric_norm)]).T.copy(order="F")


def split_char_fluxes(states, wavespeeds, frozen_metrics, frozen_jacobian, R_inv):
    nvars, npoints = states.shape
    ndim = frozen_metrics.size

    fluxes = pointwise_fluxes(states)

    char_fluxes_pos = u.empty_array(nvars, npoints)
    char_fluxes_neg = u.empty_array(nvars, npoints)
        generalized_fluxes = u.zero_array(nvars)
        for i in range(ndim):
            generalized_fluxes += frozen_metrics[i]*fluxes[:,i,k]

        generalized_states = states[:,k]/frozen_jacobian

        combination_pos = u.empty_array(nvars, nvars)
        combination_neg = u.empty_array(nvars, nvars)
        for i in range(nvars):
            char_fluxes_pos[i,k] = 0.5*np.dot(R_inv[i,:],
                    (generalized_fluxes + wavespeeds[i]*generalized_states))
            char_fluxes_neg[i,k] = 0.5*np.dot(R_inv[i,:],
                    (generalized_fluxes - wavespeeds[i]*generalized_states))

    return char_fluxes_pos, char_fluxes_neg


    sum1 = u.empty_array(fluxes.shape[0], 3)
    sum2 = u.empty_array(fluxes.shape[0], 3)

    sum1[:,0] = fluxes[:,0] - 4*fluxes[:,1] + 3*fluxes[:,2]
    sum1[:,1] = -fluxes[:,1] + fluxes[:,3]
    sum1[:,2] = -3*fluxes[:,2] + 4*fluxes[:,3] - fluxes[:,4]

    for i in range(3):
        sum2[:,i] = fluxes[:,i] - 2*fluxes[:,i+1] + fluxes[:,i+2]

    result = (1.0/4)*(sum1**2) + (13.0/12)*(sum2**2)

    return result


def weno_weights(oscillation, frozen_metric):
    linear = np.array([0.1, 0.6, 0.3])
    eps = 1e-6*frozen_metric

    raw_weights = u.empty_array(5,3)
    for i in range(5):
        for j in range(3):
            raw_weights[i,j] = linear[j]/(oscillation[i,j] + eps)**2

    weight_sum = raw_weights.sum(axis=1)
    weights = u.empty_array(5,3)
    for i in range(5):
        for j in range(3):
            weights[i,j] = raw_weights[i,j]/weight_sum[i]

    return weights


def flux_differences(f):
    w = np.array([-1, 3, -3, 1])
    res = u.empty_array(5, 3)
    for j in range(res.shape[1]):
        res[:,j] = f[:,j:j+4]@w
    return res


def combination_weighting(w):
    return np.array(
            [20*w[:,0] - 1, -10*(w[:,0] + w[:,1]) + 5, np.ones(w.shape[0])]
            ).T


def combine_fluxes(w, f):
    cw = combination_weighting(w)
    return np.sum(np.multiply(cw, f), axis=1)


def dissipation_part(R, char_fluxes, w, sign):
    flux_diff = flux_differences(char_fluxes)[:,::sign]
    flux_comb = combine_fluxes(w, flux_diff)

    return -sign*R@flux_comb/60


def weno_flux(consistent, dissipation_pos, dissipation_neg):
    return consistent + dissipation_pos + dissipation_neg