# flow single-rank Kokkos kernel unit tests (44 ctests). Built by the module's CMake under
# `-DPECLET_FLOW_BUILD_TESTS=ON` (one tree per backend; suite/docs/QUALITY_PLAN.md §3.D), which
# supplies Kokkos and the core include path already resolved for the module; the directory also
# still configures STANDALONE (Kokkos only), which is how the older three-trees-per-backend recipe
# and the GPU probe trees drive it:
#   cmake -S tests/kokkos -B build_kokkos -DCMAKE_PREFIX_PATH=<suite>/extern/install/<backend>
#   cmake --build build_kokkos -j && ctest --test-dir build_kokkos --output-on-failure
cmake_minimum_required(VERSION 3.24)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
  # ---- standalone project -----------------------------------------------------------------------
  project(peclet_flow_kernel_tests LANGUAGES CXX)

  set(CMAKE_CXX_STANDARD 20)
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
  if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
  endif()

  find_package(Kokkos CONFIG REQUIRED)
  message(STATUS "flow/tests/kokkos: Kokkos ${Kokkos_VERSION} (${Kokkos_DEVICES})")

  # core headers (field registry / GraphAMG bottom / halo types included by the solver headers):
  # the sibling checkout in the suite, or FetchContent at the release pin on a standalone CI checkout.
  list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake")
  include(PecletDeps)
  peclet_sibling_include(peclet-core "${PECLET_CORE_TAG}" "../../../core" PECLET_CORE_INCLUDE)
  enable_testing()
elseif(NOT PECLET_CORE_INCLUDE)
  message(FATAL_ERROR "flow/tests/kokkos: add_subdirectory() needs PECLET_CORE_INCLUDE from the module's CMake")
endif()
# WO-W0: the container-free VoF kernels moved to core (peclet::core::vof) and flow's src/vof/*.hpp
# are thin includes of them, so ANY target that compiles a src/ header needs the core include path,
# not only the ones that reach for the field registry. Directory-wide (the headers are header-only).
include_directories("${PECLET_CORE_INCLUDE}")

# The ONE compiled instantiation of Solver<Grid> (QUALITY_PLAN G.8), which the eighteen tests here
# that include flow_ibm.hpp link instead of instantiating the class template themselves. The module
# defines it for us when this directory is a subdirectory; standalone we define it ourselves. These
# are the SINGLE-RANK tests, so it is the variant WITHOUT PECLET_FLOW_MPI.
include(PecletFlowSolver)
peclet_flow_add_solver_library(peclet_flow_solver)

add_executable(test_reductions test_reductions.cpp)
target_include_directories(test_reductions PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_reductions PRIVATE Kokkos::kokkos)
add_test(NAME reductions COMMAND test_reductions)

# Named field registry on the Solver (includes flow_ibm.hpp -> needs the core headers).
add_executable(test_field_registry test_field_registry.cpp)
target_include_directories(test_field_registry PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_field_registry PRIVATE Kokkos::kokkos)
add_test(NAME field_registry COMMAND test_field_registry)

# Scalar transport (advection-diffusion): 2nd-order diffusion operator + advection conservation.
add_executable(test_scalar_transport test_scalar_transport.cpp)
target_include_directories(test_scalar_transport PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_scalar_transport PRIVATE Kokkos::kokkos)
add_test(NAME scalar_transport COMMAND test_scalar_transport)

# Property closures + per-cell body force (LinearMix / Boussinesq / Arrhenius vs host oracle).
add_executable(test_closures test_closures.cpp)
target_include_directories(test_closures PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_closures PRIVATE Kokkos::kokkos)
add_test(NAME closures COMMAND test_closures)

# Variable-viscosity diffusion stencil (ibmBuildDiffusionVar + FaceProps means vs oracle).
add_executable(test_variable_mu test_variable_mu.cpp)
target_include_directories(test_variable_mu PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_variable_mu PRIVATE Kokkos::kokkos)
add_test(NAME variable_mu COMMAND test_variable_mu)

# Variable-density projection: hydrostatic acid test (ratio 3 + 1e3) + uniform-rho reduction.
add_executable(test_vardensity_projection test_vardensity_projection.cpp)
target_include_directories(test_vardensity_projection PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vardensity_projection PRIVATE Kokkos::kokkos)
add_test(NAME vardensity_projection COMMAND test_vardensity_projection)

# WO-H: 3-D wall-bounded (nz >= 8) pressure convergence for all three Krylov drivers, including the
# stratified hydrostatic column and the small-coefficient-at-an-inflow-face configurations.
# --no-coarse-ghost reinstates the pre-repair coarse ghost and makes every PCG row fail.
add_executable(test_pressure_wallbounded test_pressure_wallbounded.cpp)
target_include_directories(test_pressure_wallbounded PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_pressure_wallbounded PRIVATE Kokkos::kokkos)
add_test(NAME pressure_wallbounded COMMAND test_pressure_wallbounded)

# VoF rung V0: PLIC toolbox (SZ/Lehmann-Gekle plane<->volume, MYC/Youngs normals, slab flux).
add_executable(test_vof_plic test_vof_plic.cpp)
target_include_directories(test_vof_plic PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_vof_plic PRIVATE Kokkos::kokkos)
add_test(NAME vof_plic COMMAND test_vof_plic)

# VoF Part II rungs P0/P1 (WO-P01): the PLIC polygon area, the one-sided gradient fit, planar
# regression, the divergence source at ratio 100, and the 1-D Stefan problem.
add_executable(test_vof_phase_change test_vof_phase_change.cpp)
target_include_directories(test_vof_phase_change PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_phase_change PRIVATE Kokkos::kokkos)
add_test(NAME vof_phase_change COMMAND test_vof_phase_change)

# VoF rung V1: Weymouth-Yue split geometric advection on prescribed velocity fields.
add_executable(test_vof_advect test_vof_advect.cpp)
target_include_directories(test_vof_advect PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(test_vof_advect PRIVATE Kokkos::kokkos)
add_test(NAME vof_advect COMMAND test_vof_advect)

add_executable(test_advection test_advection.cpp)
target_include_directories(test_advection PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_advection PRIVATE Kokkos::kokkos)
add_test(NAME advection COMMAND test_advection)

add_executable(test_stencils test_stencils.cpp)
target_include_directories(test_stencils PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_stencils PRIVATE Kokkos::kokkos)
add_test(NAME stencils COMMAND test_stencils)

add_executable(test_transfer test_transfer.cpp)
target_include_directories(test_transfer PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_transfer PRIVATE Kokkos::kokkos)
add_test(NAME transfer COMMAND test_transfer)

add_executable(test_cutcell test_cutcell.cpp)
# PECLET_CORE_INCLUDE: the P1 exact-apply block includes mac_cutcell_mg.hpp for MReal/FPV (the operator
# storage type it measures against), which pulls in the core GraphAMG/halo headers.
target_include_directories(test_cutcell PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_cutcell PRIVATE Kokkos::kokkos)
add_test(NAME cutcell COMMAND test_cutcell)

add_executable(test_bc test_bc.cpp)
target_include_directories(test_bc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_bc PRIVATE Kokkos::kokkos)
add_test(NAME bc COMMAND test_bc)

add_executable(test_ibm test_ibm.cpp)
target_include_directories(test_ibm PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_ibm PRIVATE Kokkos::kokkos)
add_test(NAME ibm COMMAND test_ibm)

add_executable(test_ibm_overlay test_ibm_overlay.cpp)
target_include_directories(test_ibm_overlay PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_ibm_overlay PRIVATE Kokkos::kokkos)
add_test(NAME ibm_overlay COMMAND test_ibm_overlay)

add_executable(test_ibm_apply test_ibm_apply.cpp)
target_include_directories(test_ibm_apply PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_ibm_apply PRIVATE Kokkos::kokkos)
add_test(NAME ibm_apply COMMAND test_ibm_apply)

add_executable(test_sdflow_tg test_sdflow_tg.cpp)
target_include_directories(test_sdflow_tg PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_sdflow_tg PRIVATE Kokkos::kokkos)
add_test(NAME sdflow_tg COMMAND test_sdflow_tg)

# Red-black Gauss-Seidel throughput probe (N K on the command line). Registered under the `bench`
# label so `ctest -N` lists it and `ctest -LE bench` (CI) leaves it out -- nothing is hidden.
add_executable(bench_rbgs bench_rbgs.cpp)
target_include_directories(bench_rbgs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(bench_rbgs PRIVATE Kokkos::kokkos)
add_test(NAME bench_rbgs COMMAND bench_rbgs 64 20)
set_tests_properties(bench_rbgs PROPERTIES LABELS bench)

add_executable(test_mg test_mg.cpp)
target_include_directories(test_mg PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_mg PRIVATE Kokkos::kokkos)
add_test(NAME mg COMMAND test_mg)

add_executable(test_fou_operator test_fou_operator.cpp)
target_include_directories(test_fou_operator PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_fou_operator PRIVATE Kokkos::kokkos)
add_test(NAME fou_operator COMMAND test_fou_operator)

add_executable(test_poiseuille test_poiseuille.cpp)
target_include_directories(test_poiseuille PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_poiseuille PRIVATE Kokkos::kokkos)
add_test(NAME poiseuille COMMAND test_poiseuille)

# Free-slip / symmetry domain BC (set_domain_bc type 4): half Poiseuille channel == full channel
# pointwise (both grids, both sides), uniform flow along slip faces, sphere in a slip box == its
# mirror-periodic twin (the pressure solve).
add_executable(test_freeslip test_freeslip.cpp)
target_include_directories(test_freeslip PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_freeslip PRIVATE Kokkos::kokkos)
add_test(NAME freeslip COMMAND test_freeslip)

# Outflow reversal census (outflow_backflow) -- the detector of the conditionally-stable
# zero-gradient-outflow regime (reversed outlet with the backflow stabilization off).
add_executable(test_outflow_backflow test_outflow_backflow.cpp)
target_include_directories(test_outflow_backflow PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_outflow_backflow PRIVATE Kokkos::kokkos)
add_test(NAME outflow_backflow COMMAND test_outflow_backflow)

# VoF rung V2a (WO-J): the colour field wired into the NS solver — bridge, hydrostatic acid test
# through C, conservation, C==const reductions, harmonic rho_f ablation, interface-local CFL.
# PECLET_VOF_TWOPHASE_LONG=1 runs the conservation gate for 1000 steps instead of 200.
add_executable(test_vof_twophase test_vof_twophase.cpp)
target_include_directories(test_vof_twophase PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_twophase PRIVATE Kokkos::kokkos)
add_test(NAME vof_twophase COMMAND test_vof_twophase)

# VoF rung V2b (WO-K): momentum-consistent transport of rho^c u_c on the half-shifted MAC CVs.
add_executable(test_vof_momentum test_vof_momentum.cpp)
target_include_directories(test_vof_momentum PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_momentum PRIVATE Kokkos::kokkos)
add_test(NAME vof_momentum COMMAND test_vof_momentum)

# VoF rung V3 (WO-O): the Popinet height-function curvature cascade + the Jibben/Han PLIC-volumetric
# paraboloid fallback (src/vof/curvature.hpp, src/vof/curvature_field.hpp). Standalone — no Solver.
add_executable(test_vof_curvature test_vof_curvature.cpp)
target_include_directories(test_vof_curvature PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_vof_curvature PRIVATE Kokkos::kokkos)
add_test(NAME vof_curvature COMMAND test_vof_curvature)

# VoF rung V4 (WO-P): balanced-force CSF (src/vof/surface_tension.hpp) + the Brackbill capillary dt.
# The loud gate is P1: a stationary droplet with a constant curvature must stay at machine zero.
add_executable(test_vof_surface_tension test_vof_surface_tension.cpp)
target_include_directories(test_vof_surface_tension PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_surface_tension PRIVATE Kokkos::kokkos)
add_test(NAME vof_surface_tension COMMAND test_vof_surface_tension)

# VoF rung V5a (WO-Q): the colour field transported THROUGH an immersed solid — the openness
# embed (G3), conservation of sum eps_eff*C through a sphere packing (G2), and the neutral 90-degree
# solid-band fill measured as a contact angle (G5). PECLET_VOF_CUTCELL_LONG=0 runs the full sizes.
# WO-V9: the profiling instrument is inert (timers on/off and worklist on/off bitwise).
add_executable(test_vof_timing test_vof_timing.cpp)
target_include_directories(test_vof_timing PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_timing PRIVATE Kokkos::kokkos)
add_test(NAME vof_timing COMMAND test_vof_timing)
# A timing instrument, not a correctness gate: LABELS bench, excluded by `ctest -LE bench` (CI).
set_tests_properties(vof_timing PROPERTIES LABELS bench)

add_executable(test_vof_cutcell test_vof_cutcell.cpp)
target_include_directories(test_vof_cutcell PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${CMAKE_CURRENT_SOURCE_DIR} ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_cutcell PRIVATE Kokkos::kokkos)
add_test(NAME vof_cutcell COMMAND test_vof_cutcell)

# VoF rung V5b (WO-S): the theta-consistent solid-band fill as a PURE KERNEL test (G0) — the
# flat-wall idempotence that makes the prescribed contact angle a fixed point of the fill.
add_executable(test_vof_wetting test_vof_wetting.cpp)
target_include_directories(test_vof_wetting PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_wetting PRIVATE Kokkos::kokkos)
add_test(NAME vof_wetting COMMAND test_vof_wetting)

# VoF rung V6 (WO-V6): the DYNAMIC contact angle (Cox-Voinov with an explicit slip length) and
# advancing/receding hysteresis — the model as arithmetic, the U_cl sign convention as geometry,
# and the two through the solver on a kinematic advancing/receding pair.
add_executable(test_vof_wetting_dynamic test_vof_wetting_dynamic.cpp)
target_include_directories(test_vof_wetting_dynamic PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_wetting_dynamic PRIVATE Kokkos::kokkos)
add_test(NAME vof_wetting_dynamic COMMAND test_vof_wetting_dynamic)

# VoF rung V-BC (WO-R): two-phase open boundaries — the out-of-domain flux rule, the inflow/
# backflow colour ghost, the inflow property ghost, the exact colour budget and the
# variable-density outflow correction. PECLET_VOF_BC_QUICK=1 shortens the budget run.
add_executable(test_vof_bc test_vof_bc.cpp)
target_include_directories(test_vof_bc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_bc PRIVATE Kokkos::kokkos)
add_test(NAME vof_bc COMMAND test_vof_bc)

add_executable(test_poiseuille_ibm test_poiseuille_ibm.cpp)
target_include_directories(test_poiseuille_ibm PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
target_link_libraries(test_poiseuille_ibm PRIVATE Kokkos::kokkos)
add_test(NAME poiseuille_ibm COMMAND test_poiseuille_ibm)

# VoF rung V8 (WO-T): the COLLOCATED path — variable density in the ABC approximate projection,
# forces as face accelerations with the averaged cell counterpart, colour advection from the
# projected face field. The gated quantity on this grid is the FACE field (the cell field carries
# the approximate projection's invisible checkerboard, measured here against its constant-density
# control).
# VoF Part III rung W0 (WO-W0): the per-bubble BLOCK container (block_container.hpp +
# block_exchange.hpp). Standalone -- its own advectors and its own serial exchange, no Solver.
add_executable(test_vof_blocks test_vof_blocks.cpp)
target_include_directories(test_vof_blocks PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_blocks PRIVATE Kokkos::kokkos)
add_test(NAME vof_blocks COMMAND test_vof_blocks)

add_executable(test_vof_collocated test_vof_collocated.cpp)
target_include_directories(test_vof_collocated PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${CMAKE_CURRENT_SOURCE_DIR} ${PECLET_CORE_INCLUDE})
target_link_libraries(test_vof_collocated PRIVATE Kokkos::kokkos)
add_test(NAME vof_collocated COMMAND test_vof_collocated)

# ANISOTROPIC CELLS, the pressure operator alone (doc/anisotropic_metric.md §8.5, the ORDER part
# of gate G4; Phase 2 commit C2). CutcellMG driven directly on a periodic all-fluid box with the
# per-axis weights w = (1, 4, 1/4) of a (N, 2N, N/2) grid: the manufactured cos*cos*cos solution
# must converge at the cubic rate. The level-table and V-cycle-rate parts of G4 are commit C3.
add_executable(test_cutcellmg_aniso test_cutcellmg_aniso.cpp)
target_include_directories(test_cutcellmg_aniso PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src
                           ${PECLET_CORE_INCLUDE})
target_link_libraries(test_cutcellmg_aniso PRIVATE Kokkos::kokkos)
add_test(NAME cutcellmg_aniso COMMAND test_cutcellmg_aniso)

# PHYSICAL DOMAINS (suite/docs/PHYSICAL_UNITS_PLAN.md Phase 1). Two gates off one binary:
#  units_identity        — the physical constructor at extent == cells reproduces the cell-unit
#                          solver (accessors, and BITWISE fields at rho = dt = 1);
#  units_scale_invariance — the same physical problem in two unit systems 1000x apart agrees to
#                          1e-13 relative, on a sampled SDF and on an analytic scene.
add_executable(test_units test_units.cpp)
target_include_directories(test_units PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src ${PECLET_CORE_INCLUDE})
target_link_libraries(test_units PRIVATE Kokkos::kokkos)
add_test(NAME units_identity COMMAND test_units identity)
add_test(NAME units_scale_invariance COMMAND test_units scale)
add_test(NAME units_vof_sigma COMMAND test_units vof)
#  units_anisotropic_poiseuille — Phase 2 gate G1 (doc/anisotropic_metric.md §8.2): the exact
#                          Poiseuille parabola on cells (16, 40, 8) over the box (16, 12, 16),
#                          i.e. spacing (1, 0.3, 2), against its isotropic control.
add_test(NAME units_anisotropic_poiseuille COMMAND test_units aniso)
#  units_anisotropic_sphere — Phase 2 gate G2 (doc/anisotropic_metric.md §8.3): the regression's
#                          Zick & Homsy sphere on cells (N, 2N, N/2) over the cube, N = 16/24/32,
#                          against the cubic control and the recorded baseline; plus the §4.4
#                          periodic-Stokes hydro-force identity on both grids.
add_test(NAME units_anisotropic_sphere COMMAND test_units sphere)
#  units_anisotropic_tgv   — Phase 2 gate G3 (doc/anisotropic_metric.md §8.4): Stokes Taylor-Green
#                          on cells (N, 2N, 4) with the DISCRETELY divergence-free initial field,
#                          against the exact anisotropic backward-Euler amplitude ratio.
add_test(NAME units_anisotropic_tgv COMMAND test_units tgv)
# PHASE 3 (flow/doc/anisotropic_vof.md §11 S2): the balanced-force CSF on BOX cells — the gate the
# Phase 2 merge unlocked, since `enable_vof` refused an anisotropic domain until Phase 3 landed.
add_test(NAME units_vof_aniso COMMAND test_units vofaniso)

# G.8: link every target in this directory against the one compiled instantiation. The library is
# STATIC, so this is inert for the kernel unit tests that never name `Solver` -- the linker pulls no
# member in -- and it means a test added later gets the fast build without a separate edit here.
get_property(_flow_kernel_test_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
foreach(_t IN LISTS _flow_kernel_test_targets)
  if(NOT "${_t}" STREQUAL "peclet_flow_solver")   # standalone: the library is defined HERE
    target_link_libraries(${_t} PRIVATE peclet_flow_solver)
  endif()
endforeach()
