feat(esopull): runtime body sync for EsoPull streaming mode

- New esopull_sync.cu: DecodeCellsToPhysical + EncodePhysicalToCells
  (compact-list mode, ddf_shifting-aware, encode applies collision).
- sync_bodies() now branches for double_buffer vs esopull: decode
  backing layout to physical DDF on GPU -> host patch -> collide +
  encode back to backing layout. No temp_gpu, no full-grid copy.
- 4 new integration tests covering esopull add/remove/cycle/roundtrip.
- ddf_shifting + esopull + sync_bodies jointly verified (1300 steps
  stable after add/remove).
- Bump version to 0.5.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Frank14fandCursor committed 2026-06-21 22:31:02 +08:00
1 parent 04c2bc75ea
commit 00b957f904
10 files changed
+343 -75

No files matched your search

@@ -0,0 +1,81 @@
// CelerisLab -- step/esopull_sync.cu
// Compact-list EsoPull decode/encode for runtime body topology sync.
//
// Decodes a subset of cells from EsoPull backing layout to physical DDF,
// or encodes physical DDF back to EsoPull backing layout.
// Each thread handles one cell from a compact index list.
//
// Included by kernel_v2.cu. No standalone compilation.
// ============================================================================
#ifndef CELERIS_STEP_ESOPULL_SYNC_CU
#define CELERIS_STEP_ESOPULL_SYNC_CU
// ---------------------------------------------------------------------------
// DecodeCellsToPhysical: backing layout -> physical DDF (compact list)
//
// Uses ``t`` as the step count for EsoPull semantic parity decoding.
// ---------------------------------------------------------------------------
__global__ void DecodeCellsToPhysical(
const fpxx* fi,
float* fi_phys,
const unsigned int* cell_idx,
unsigned int n_cells,
unsigned long t)
{
unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
if (tid >= n_cells) return;
unsigned long k = (unsigned long)cell_idx[tid];
unsigned int x, y; coordinates(k, x, y);
unsigned long j[NQ]; compute_neighbors(x, y, j);
float f[NQ];
load_physical_node_esopull(k, fi, j, t, f);
// DDF-shifting: backing layout stores f-w, unshift to physical for host patch
#if USE_DDF_SHIFTING
#pragma unroll
for (int i = 0; i < NQ; i++)
f[i] += (float)d_w[i];
#endif
for (int i = 0; i < NQ; i++)
fi_phys[tid * NQ + i] = f[i];
}
// ---------------------------------------------------------------------------
// EncodePhysicalToCells: physical DDF -> backing layout (compact list)
//
// Reads physical (pre-collision) DDF from *fi_phys*, applies collision
// to produce post-collision DDF, then encodes into the EsoPull backing
// layout on *fi*. The backing layout stores post-collision values,
// matching what store_f_esopull would produce at step *t*.
// ---------------------------------------------------------------------------
__global__ void EncodePhysicalToCells(
fpxx* fi,
const float* fi_phys,
const unsigned int* cell_idx,
unsigned int n_cells,
unsigned long t)
{
unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
if (tid >= n_cells) return;
unsigned long k = (unsigned long)cell_idx[tid];
unsigned int x, y; coordinates(k, x, y);
unsigned long j[NQ]; compute_neighbors(x, y, j);
float f[NQ];
for (int i = 0; i < NQ; i++)
f[i] = fi_phys[tid * NQ + i];
// Collide: physical (pre-collision) -> post-collision
float rho_n, ux, uy;
compute_rho_u(f, rho_n, ux, uy);
collide_dispatch(f, rho_n, ux, uy);
store_physical_node_esopull(k, f, fi, j, t);
}
#endif // CELERIS_STEP_ESOPULL_SYNC_CU