feat(obs): unified zero_obs control and time-normalised readback
- Replace split zero_force_segment / zero_sensor_segment with unified zero_obs_async() — a single memset covers all three obs segments (force, torque, sensor), resetting the step accumulator. - Add _obs_accum_steps counter so read_*(normalize=True) returns the physically meaningful per-step average for all telemetry fields. - Sensor now always applies area-normalisation internally; the normalize parameter only controls the additional time-normalisation step. - run() gains zero_obs=True parameter (default) to control reset-on-step. - 7 new integration tests covering accumulation, zeroing, and normalise. - Fix bug in test_sensor_accuracy.py (undefined loop variable i). - Bump version to 0.4.0 for the API change. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -314,7 +314,7 @@ def _run_one(
|
||||
stream.synchronize()
|
||||
sim.bodies.download_obs_full_async(stream)
|
||||
stream.synchronize()
|
||||
force = sim.bodies.read_force(0)
|
||||
force = sim.bodies.read_force(0, normalize=False)
|
||||
fx = float(force[0])
|
||||
fy = float(force[1])
|
||||
if not np.isfinite(fx) or not np.isfinite(fy):
|
||||
|
||||
@@ -321,7 +321,7 @@ def run_one_simulation(
|
||||
stream.synchronize()
|
||||
sim.bodies.download_obs_full_async(stream)
|
||||
stream.synchronize()
|
||||
fvec = sim.bodies.read_force(0)
|
||||
fvec = sim.bodies.read_force(0, normalize=False)
|
||||
lift = float(fvec[1])
|
||||
drag = float(fvec[0])
|
||||
if not np.isfinite(lift) or not np.isfinite(drag):
|
||||
|
||||
@@ -65,10 +65,8 @@ def test_sensor_accuracy() -> dict:
|
||||
# Get macroscopic field after one more step (with sensor accumulation)
|
||||
import pycuda.driver as cuda
|
||||
stream = cuda.Stream()
|
||||
sim.bodies.zero_sensor_segment_async(stream)
|
||||
sim.stepper.step(1, action_gpu=sim.bodies.action_gpu,
|
||||
obs_gpu=sim.bodies.obs_gpu, stream=stream)
|
||||
stream.synchronize()
|
||||
sim.run(1, zero_obs=True, upload_act=False, sync_obs=True, stream=stream)
|
||||
# stream.synchronize() is called inside run()
|
||||
|
||||
macro = sim.get_macroscopic()
|
||||
ux = macro["ux"]
|
||||
@@ -76,7 +74,8 @@ def test_sensor_accuracy() -> dict:
|
||||
|
||||
results = {}
|
||||
all_pass = True
|
||||
for sid in sensor_ids:
|
||||
for idx, sid in enumerate(sensor_ids):
|
||||
pos = positions[idx]
|
||||
cells_arr, _ = sim.bodies.get(sid).get_sensor_list(
|
||||
sim.lbm_cfg.nx, sim.lbm_cfg.ny
|
||||
)
|
||||
@@ -97,7 +96,7 @@ def test_sensor_accuracy() -> dict:
|
||||
if not passed:
|
||||
all_pass = False
|
||||
|
||||
results[f"sensor_{sid}_pos{positions[i]}"] = {
|
||||
results[f"sensor_{sid}_pos{pos}"] = {
|
||||
"sensor_reading": [sensor_reading_x, sensor_reading_y],
|
||||
"manual_average": [sensor_ux_mean, sensor_uy_mean],
|
||||
"diff": [float(diff_ux), float(diff_uy)],
|
||||
@@ -106,7 +105,7 @@ def test_sensor_accuracy() -> dict:
|
||||
}
|
||||
status = "PASS" if passed else "FAIL"
|
||||
print(
|
||||
f" Sensor {sid} @ {positions[sid]}: "
|
||||
f" Sensor {sid} @ {pos}: "
|
||||
f"reading=({sensor_reading_x:.8f},{sensor_reading_y:.8f}) "
|
||||
f"manual=({sensor_ux_mean:.8f},{sensor_uy_mean:.8f}) "
|
||||
f"diff=({diff_ux:.2e},{diff_uy:.2e}) "
|
||||
|
||||
Reference in New Issue
Block a user