Gate action upload on Jet stationarity; document setter device-error handling

ObjectManager._upload_action_async is the single entry for every action
path (setters, direct edits of the public action array, run()). It now
also rejects nonzero vx/vy/omega slots of Jet-driven bodies via
validate_jet_motion, before any device copy. Regression: each motion
slot edited directly is rejected with no device write; zero motion
uploads. Simulation.set_jet_amplitude documents that a device error
requires close/recreate, as for other CUDA exceptions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-15 08:58:17 +00:00
1 parent 063508d1a3
commit 19bd3088ef
3 files changed
+33 -3

No files matched your search

+10 -2
View File
@@ -622,11 +622,19 @@ class ObjectManager:
cuda.memcpy_dtoh_async(self.obs_pinned, self.obs_gpu, stream)
def _upload_action_async(self, stream: cuda.Stream) -> None:
"""Async H2D of host action array to action_gpu."""
"""Async H2D of host action array to action_gpu.
Single gate for every action path (setters or direct ``action`` edits):
surface-actuated and Jet-driven bodies must keep zero motion slots.
"""
slot = 3*self.cfg.dim
for obj in self.objects:
obj.validate_surface_actuation(self.cfg)
if obj.surface_actuation is not None and self.action[(obj.obj_id+1)*3*self.cfg.dim-1] != 0:
base = obj.obj_id*slot
if obj.surface_actuation is not None and self.action[base+slot-1] != 0:
raise ValueError("Surface actuation cannot share the rotation action")
self.validate_jet_motion(obj.obj_id, {"vx": self.action[base], "vy": self.action[base+1],
"omega": self.action[base+slot-1]})
if self.action_gpu is None or self.action.size == 0:
return
if self._action_pinned is None or self._action_pinned.size != self.action.size:
+6 -1
View File
@@ -173,7 +173,12 @@ class Simulation:
@context_bound
def set_jet_amplitude(self, index: int, amplitude: float) -> None:
"""Change one jet's signed amplitude; re-uploads per-link velocity only."""
"""Change one jet's signed amplitude; re-uploads per-link velocity only.
Invalid input changes nothing. A device error leaves the host amplitude
unchanged but the device column undefined: treat it like any other CUDA
exception here and close/recreate the Simulation before stepping again.
"""
jet = self.bodies.jets[index].with_amplitude(amplitude)
if self._initialized:
self.field.curved.set_jet_amplitude(index, amplitude, self.stream)