// CelerisLab – boundary/inlet/zou_he_local.cuh
// Local on-site west velocity inlet closures.
//
// D2Q9 and D3Q19 variants recover rho from local mass closure and reconstruct
// unknown incoming populations from local target velocity constraints.
//
// In the current ghost-source architecture these reconstructed states are later
// used as pull sources. Therefore this method requires post-BC ghost collision
// in the step kernel.
// ============================================================================

#ifndef CELERIS_BOUNDARY_INLET_ZOU_HE_LOCAL_CUH
#define CELERIS_BOUNDARY_INLET_ZOU_HE_LOCAL_CUH

#if LATTICE_MODEL == LATTICE_D2Q9

// Free-slip y-walls: at inlet rows y=1 and y=NY-2, pull can source wall nodes for
// some known directions. Copy those from stored DDF at (x=1, same y) only.
//
// NOTE: This helper is not Zou-He-specific.  All west inlet schemes that use
// west_velocity_rho_closure_d2q9() need clean known-direction values.  The
// free-slip wall interferes with these at the top/bottom inlet corners.
// The legacy name is kept for now (not renamed yet).
__device__ inline void repair_zou_he_west_knowns_d2q9(
    float* __restrict__ f,
    const fpxx* __restrict__ fi_in,
    unsigned int x,
    unsigned int y)
{
    if (x != 0u) return;

    const unsigned long k_int = linear_index(x + 1u, y);

    if (y == 1u) {
        f[3] = load_ddf(fi_in, index_f(k_int, 3u));
        f[8] = load_ddf(fi_in, index_f(k_int, 8u));
    } else if (y == (unsigned int)(NY - 2)) {
        f[4] = load_ddf(fi_in, index_f(k_int, 4u));
        f[6] = load_ddf(fi_in, index_f(k_int, 6u));
    }
}

__device__ inline void apply_zou_he_left_velocity_inlet_d2q9(
    float* __restrict__ f,
    float ux_target,
    float uy_target)
{
    const float rho = west_velocity_rho_closure_d2q9(f, ux_target);

    f[1] = f[2] + (2.0f / 3.0f) * rho * ux_target;

    f[5] = f[6]
         + 0.5f * (f[4] - f[3])
         + (1.0f / 6.0f) * rho * ux_target
         + 0.5f * rho * uy_target;

    f[7] = f[8]
         + 0.5f * (f[3] - f[4])
         + (1.0f / 6.0f) * rho * ux_target
         - 0.5f * rho * uy_target;
}

#endif // LATTICE_MODEL == LATTICE_D2Q9

#if LATTICE_MODEL == LATTICE_D3Q19

// Hecht-Harting style D3Q19 on-site west velocity closure adapted to the
// codebase paired ordering:
//   7  = (+x,+y),  8  = (-x,-y)
//   13 = (+x,-y), 14 = (-x,+y)
//   9  = (+x,+z), 10 = (-x,-z)
//   15 = (+x,-z), 16 = (-x,+z)
__device__ inline void apply_zou_he_left_velocity_inlet_d3q19(
    float* __restrict__ f,
    float ux_target,
    float uy_target,
    float uz_target)
{
    const float rho = west_velocity_rho_closure_d3q19(f, ux_target);

    const float Nyx = 0.5f * (f[3] + f[11] + f[17] - (f[4] + f[12] + f[18]))
                    - (1.0f / 3.0f) * rho * uy_target;
    const float Nzx = 0.5f * (f[5] + f[11] + f[18] - (f[6] + f[17] + f[12]))
                    - (1.0f / 3.0f) * rho * uz_target;

    f[1]  = f[2]  + (1.0f / 3.0f) * rho * ux_target;
    f[7]  = f[8]  + (1.0f / 6.0f) * rho * (ux_target + uy_target) - Nyx;
    f[13] = f[14] + (1.0f / 6.0f) * rho * (ux_target - uy_target) + Nyx;
    f[9]  = f[10] + (1.0f / 6.0f) * rho * (ux_target + uz_target) - Nzx;
    f[15] = f[16] + (1.0f / 6.0f) * rho * (ux_target - uz_target) + Nzx;
}

#endif // LATTICE_MODEL == LATTICE_D3Q19

#endif // CELERIS_BOUNDARY_INLET_ZOU_HE_LOCAL_CUH
