// CelerisLab – boundary/outlet/pressure_neq.cuh
// Pressure outlet and zero-gradient outlet closures.
// ============================================================================

#ifndef CELERIS_BOUNDARY_OUTLET_PRESSURE_NEQ_CUH
#define CELERIS_BOUNDARY_OUTLET_PRESSURE_NEQ_CUH

#ifndef OUTLET_MODE
#define OUTLET_MODE 0
#endif

#ifndef OUTLET_BACKFLOW_CLAMP
#define OUTLET_BACKFLOW_CLAMP 1
#endif

#ifndef OUTLET_BLEND_ALPHA
#define OUTLET_BLEND_ALPHA 0.70f
#endif

#ifndef OUTLET_SRT_NEQ_DAMP
#define OUTLET_SRT_NEQ_DAMP 0.50f
#endif

#if LATTICE_MODEL == LATTICE_D2Q9
__device__ inline void apply_pressure_outlet_d2q9(
    float* __restrict__ f,
    const float* __restrict__ f_neb)
{
#if OUTLET_MODE == 1
    f[2] = f_neb[2];
    f[8] = f_neb[8];
    f[6] = f_neb[6];
#else
    float rho_neb, u_neb, v_neb;
    compute_rho_u(f_neb, rho_neb, u_neb, v_neb);
#if OUTLET_BACKFLOW_CLAMP
    u_neb = fmaxf(u_neb, 0.0f);
#endif
    const float rho_out = RHO;

    float feq_tar[9], feq_neb[9];
    compute_feq(rho_out, u_neb, v_neb, feq_tar);
    compute_feq(rho_neb, u_neb, v_neb, feq_neb);

#if COLLISION_MODEL == 0 || COLLISION_MODEL == 1
    const float beta = OUTLET_SRT_NEQ_DAMP;
    #pragma unroll
    for (int i = 0; i < 9; i++) {
        const float fneq = f_neb[i] - feq_neb[i];
        f[i] = feq_tar[i] + beta * fneq;
    }
#elif OUTLET_MODE == 2
    const float a = OUTLET_BLEND_ALPHA;
    f[2] = a * (f_neb[2] - feq_neb[2] + feq_tar[2]) + (1.0f - a) * f_neb[2];
    f[8] = a * (f_neb[8] - feq_neb[8] + feq_tar[8]) + (1.0f - a) * f_neb[8];
    f[6] = a * (f_neb[6] - feq_neb[6] + feq_tar[6]) + (1.0f - a) * f_neb[6];
#else
    f[2] = f_neb[2] - feq_neb[2] + feq_tar[2];
    f[8] = f_neb[8] - feq_neb[8] + feq_tar[8];
    f[6] = f_neb[6] - feq_neb[6] + feq_tar[6];
#endif
#endif
}
#endif

#if LATTICE_MODEL == LATTICE_D3Q19
__device__ inline void apply_pressure_outlet_d3q19(
    float* __restrict__ f,
    const float* __restrict__ f_neb)
{
#if OUTLET_MODE == 1
    f[2]  = f_neb[2];
    f[8]  = f_neb[8];
    f[10] = f_neb[10];
    f[14] = f_neb[14];
    f[16] = f_neb[16];
#else
    float rho_neb, un, vn, wn;
    compute_rho_u(f_neb, rho_neb, un, vn, wn);
#if OUTLET_BACKFLOW_CLAMP
    un = fmaxf(un, 0.0f);
#endif
    const float rho_out = RHO;

    float feq_tar[19], feq_neb[19];
    compute_feq(rho_out, un, vn, wn, feq_tar);
    compute_feq(rho_neb, un, vn, wn, feq_neb);

#if COLLISION_MODEL == 0 || COLLISION_MODEL == 1
    const float beta = OUTLET_SRT_NEQ_DAMP;
    #pragma unroll
    for (int i = 0; i < 19; i++) {
        const float fneq = f_neb[i] - feq_neb[i];
        f[i] = feq_tar[i] + beta * fneq;
    }
#elif OUTLET_MODE == 2
    const float a = OUTLET_BLEND_ALPHA;
    f[2]  = a * (f_neb[2]  - feq_neb[2]  + feq_tar[2])  + (1.0f - a) * f_neb[2];
    f[8]  = a * (f_neb[8]  - feq_neb[8]  + feq_tar[8])  + (1.0f - a) * f_neb[8];
    f[10] = a * (f_neb[10] - feq_neb[10] + feq_tar[10]) + (1.0f - a) * f_neb[10];
    f[14] = a * (f_neb[14] - feq_neb[14] + feq_tar[14]) + (1.0f - a) * f_neb[14];
    f[16] = a * (f_neb[16] - feq_neb[16] + feq_tar[16]) + (1.0f - a) * f_neb[16];
#else
    f[2]  = f_neb[2]  - feq_neb[2]  + feq_tar[2];
    f[8]  = f_neb[8]  - feq_neb[8]  + feq_tar[8];
    f[10] = f_neb[10] - feq_neb[10] + feq_tar[10];
    f[14] = f_neb[14] - feq_neb[14] + feq_tar[14];
    f[16] = f_neb[16] - feq_neb[16] + feq_tar[16];
#endif
#endif
}
#endif

#endif // CELERIS_BOUNDARY_OUTLET_PRESSURE_NEQ_CUH
