# Round 3: FP16S and ddf_shifting compatibility

Date: 2026-06-03
Duration: 8+ hours of code + testing

## Summary of work

This round added and verified FP16S store precision and ddf_shifting mode compatibility across the body module, curved boundary, and diagnostics pipeline.

## Primary achievements

### 1. MRT shifting fix (critical)

**Problem discovered:** D2Q9 MRT is NOT shift-invariant in moment space because M(w) produces non-zero entries in m[1] (energy) and m[2] (energy^2).  Previous assumption that "f - feq cancels weights pairwise" only holds for SRT/TRT (per-direction collision), not for MRT (moment-space collision).

**Fix applied:** When `USE_DDF_SHIFTING=1`, `collide_mrt` now:
1. Unshifts `g[i] += w[i]` at entry (physical space)
2. Performs full MRT collision in physical space
3. Reshifts `g[i] -= w[i]` at exit

**Verified:** `MRT + ddf_shifting + zou_he_local` produces K2 results with `amp_CL` recovering from 0.007 to 0.476.

**Note on D3Q19:** Has a `compute_feq` inconsistency (shifted feq applied to unshifted g) that needs fixing before D3Q19 shifting is usable.

### 2. ddf_shifting code fixes

| Fix | File | Status |
|-----|------|--------|
| Inlet west_velocity_rho_closure +5/6 correction | `inlet/common.cuh` | Verified, both D2Q9 and D3Q19 |
| Curved force/torque +2*w_i correction | `curved_boundary.cuh`, `aux_kernels.cu` | Verified |
| Host upload_ddf() asymmetry (subtract weights before upload) | `field.py` | Verified |
| Checkpoint streaming and ddf_shifting match check | `checkpoint.py` | Implemented |

### 3. Inlet knowns repair generalization (partial)

The `repair_zou_he_west_knowns_d2q9` function was moved from Zou-He-only to all west inlet schemes in `inlet_outlet.cuh`.  This fixes free-slip y-wall contamination of ghost-node populations.  However, this alone was insufficient to fix the regularized+shifting issue.

### 4. EsoPull curved boundary (from Round 2, documented here for posterity)

Verified against Kan99b K2: **bit-identical metrics** between double_buffer and esopull streaming.

Key architectural decisions:
- `streaming/esopull_semantic_helpers.cuh` -- single source of truth for physical-value semantics
- `MacroscopicEsoPullKernel` -- GPU-side diagnostics (not host raw decode)
- `Prepare + Apply` two-phase curved boundary

## Current compatibility matrix

### Verified (passes K2/Sah04 benchmarks)

| Config | Details |
|--------|---------|
| FP32 + MRT + double_buffer | Full validation (all Kan99b, Sah04) |
| FP32 + MRT + esopull | K2 bit-identical to double_buffer |
| FP16S + MRT + double_buffer | Sah04 S2 passes |
| ddf_shifting + MRT + zou_he_local | K2 amp_CL recovered, full validation pending |

### Known limitations

| Issue | Severity | Status |
|-------|----------|--------|
| FP16S K2 St deviation (0.170 vs 0.142) | Medium -- quantization noise in curved region | Documented, acceptable for low-Re research |
| FP16S S4 divergence at step 71420 | Medium -- high-blockage quantization + wall-gap | Use FP32 for high-blockage |
| ddf_shifting + regularized inlet + MRT | High -- suppressed shedding even after inlet repair | Regularized in shifting needs root cause. Use zou_he_local for now. |
| D3Q19 MRT shifting patch incomplete | Medium -- shifted feq applied to unshifted g | Not in 2D scope, TODO noted in code |
| ddf_shifting + esopull combined | Not verified | Both paths validated separately but not together |

## Key lessons recorded

1. **SRT/TRT are shift-invariant; MRT is NOT.**  The weight vector w has non-zero moment space projection.  Always shift/unshift around MRT.

2. **Repair_zou_he_west_knowns is NOT Zou-He-specific.**  Every west inlet scheme using `west_velocity_rho_closure_d2q9()` needs it under free-slip y-walls.

3. **FP16S failure modes are specific:** Low-blockage (S2) is fine; rotating cylinder (K2) shows quantization sensitivity; high-blockage (S4) can diverge.  Test all three regimes before claiming FP16S works.

4. **`compute_feq` in shifting mode returns `feq - w`, not `feq`.**  This is correct for SRT/TRT (NEQ terms cancel w), but causes a bias when paired with an unshifted `g` (D3Q19 MRT case).

5. **Host DDF path is not symmetric under shifting.**  `download_ddf()` adds back w; `upload_ddf()` (before fix) did not subtract w.  This breaks host-side initialization, snapshot/restore, and add_vortex under shifting.
