# Pressure Poisson with NekRS + Ginkgo Replaces NekRS's built-in Preconditioned Conjugate Gradient (PCG) with Ginkgo's CG solver for the pressure Poisson equation. NekRS's matrix-free spectral element operator is wrapped as a Ginkgo `LinOp`, keeping all data on the device with zero-copy pointer sharing. Compatible with NekRS v26+. ## Integration Overview NekRS does **not** assemble a sparse matrix. The Laplacian is applied matrix-free via tensor-product spectral element operations. To use Ginkgo's Krylov solvers, we wrap NekRS's `elliptic` operator as a custom `gko::LinOp` subclass that calls NekRS's `op()` method internally. Key points: - **No sparse matrix** — Ginkgo's solver works with the `LinOp` interface, not CSR - **Zero-copy** — OCCA memory objects and Ginkgo arrays share the same pointers - **UDF plugin** — examples compile as shared libraries loaded by NekRS at runtime - **Shared wrapper** — the `NekRSOperator` class lives in `nekrs/nekrs_ginkgo.hpp` and is shared between both NekRS examples ## Step-by-Step ### Wrapping the Operator NekRS's `elliptic::op()` applies $A \cdot x$ matrix-free. We wrap it as a `gko::LinOp` (defined in `nekrs/nekrs_ginkgo.hpp`): ```{literalinclude} ../nekrs_ginkgo.hpp :language: cpp :start-after: [nekrs-operator-wrapper] :end-before: [/nekrs-operator-wrapper] ``` ### The Ginkgo Solve Function Builds a Ginkgo CG solver from the wrapped operator: ```{literalinclude} ../nekrs_ginkgo.hpp :language: cpp :start-after: [nekrs-ginkgo-solve] :end-before: [/nekrs-ginkgo-solve] ``` ### NekRS UDF Integration The solve function is called from NekRS's per-timestep hook. NekRS automatically injects the `nrs` variable into UDF functions. ```{literalinclude} poisson.cpp :language: cpp :start-after: [udf-execute-step] :end-before: [/udf-execute-step] ``` ## Building NekRS UDFs compile as shared libraries (plugins), not standalone executables: ```bash cmake -S . -B build --preset default \ -DNEKRS_DIR=/path/to/nekrs \ -DGinkgo_DIR=/path/to/ginkgo cmake --build build ``` This produces `libpoisson.so`. ## Running **Note:** Running has not been tested end-to-end. The code compiles against the NekRS v26 API, validating the integration pattern, but actually executing it requires a full NekRS case setup. To run, you would need a NekRS case directory with `.par` and `.re2` files (e.g., the `ethier` example bundled with NekRS). Copy the built shared library into the case directory and configure the `.par` to load it: ```bash # Copy a NekRS example case cp -r /path/to/nekrs/examples/ethier /tmp/test-case # Copy the UDF plugin cp build/libpoisson.so /tmp/test-case/ # Run cd /tmp/test-case && mpirun -np 1 nekrs --setup ethier ``` The `UDF_ExecuteStep` hook intercepts the pressure solve at every time step. Note that NekRS still calls its own pressure solver internally — a production integration would need to disable the built-in solve and route it through the Ginkgo path exclusively. ## Expected Output (approximate) ```text [Ginkgo] Pressure DOFs: 262144 [Ginkgo] Pressure solve at step 100, time = 0.2 [Ginkgo] Pressure solve at step 200, time = 0.4 ```