feat(body): add force_region, fix sensor API, reorganize docs

- Add force_region object type: local Guo forcing via sparse compact list
- ForceRegionSoA container, ForceRegionKernel, stepper dispatch
- add_body("force_region", ...) + set_force(id, fx, fy) API
- Fix read_sensor(normalize=...) not being passed from Simulation layer
- Fix force_region incorrectly entering curved cut-link path (P0 blocker)
- Clean up module boundaries: body/__init__ no longer imports from lbm
- Circluar import fix: common/streakline <-> pathline
- Package data globs fixed for recursive kernel files
- Version unified to 0.3.0
- Performance analysis: pycuda launch overhead vs GPU compute at various grid sizes
- Nsight Systems + Nsight Compute profiling data and report
- Documentation reorganized under docs/ (audit, validation_specs)
- README overhaul: multi-body examples, validated benchmarks, force_region docs

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Frank14fandCursor committed 2026-06-01 18:07:55 +08:00
1 parent 2e052480c2
commit b110591433
16 files changed
+2076 -18

No files matched your search

+24
View File
@@ -123,6 +123,30 @@ Future geometry types (polygon, mesh) will use the same `add_body()` function wi
| `sim.read_force(id)` -> ndarray | Force vector [fx, fy] (2D) |
| `sim.read_torque(id)` -> ndarray | Torque [tz] (2D) |
| `sim.read_sensor(id)` -> ndarray | Area-averaged velocity via GPU sensor kernel |
| `sim.set_force(id, fx=..., fy=...)` | Set force density on a force_region object (notice: see persistence note below) |
### force_region usage
```python
# Create a circular force application region
fr_id = sim.add_body("force_region", center=(50, 50), radius=15)
# Set force density (lattice units, implicit GPU upload)
sim.set_force(fr_id, fx=0.001, fy=0.0)
# The region applies Guo forcing on each step. Zero force = no-op.
sim.set_force(fr_id, fx=0.0, fy=0.0) # disable force
```
**Persistence note:** `set_force()` writes the action buffer directly but does not update the object's state record. If `sync_to_gpu()` is called afterward, the force will be reset to zero. For the common usage pattern (initialize -> set_force -> run -> set_force -> run ...), this is not an issue. A future update will add proper force storage in the object state.
### Comparison: body types
| Type | Flag overlay | Produces cut-links | Readback | Runtime control |
|------|-------------|-------------------|----------|-----------------|
| `"circle"` | OBSTACLE + BC_CURVED | Yes (Bouzidi) | force/torque | `set_body(id, omega=...)` |
| `"sensor"` | FLUID + SENSOR_FLAG | No | area-averaged velocity | None needed |
| `"force_region"` | None (zero mask) | **No** | None | `set_force(id, fx=..., fy=...)` |
#### Data access