Jets: freeze origins, guard set_body, fine-boundary hit helper

Sector/Tube/Radial store origin as a validated float 2-tuple, so caller
lists/arrays cannot change a registered definition. Simulation.set_body
rejects nonzero motion for bodies with Jet-driven links once initialized
(before initialize the sync-time stationary check applies).
prepare_fine_boundary accepts jets in the same coarse cell-edge frame as
fine objects, records read-only FP64 edge-frame hits (FineBoundary2D
.curved_hits, default None) and rejects empty or overlapping selections
before CUDA. FixedPatchStepper wiring is not included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-15 08:27:35 +00:00
1 parent da8bfb0d4a
commit 62c525e735
4 files changed
+82 -5

No files matched your search

+14 -3
View File
@@ -32,6 +32,15 @@ def _finite(*values):
raise ValueError("Jet parameters must be finite")
def _freeze_origin(spec):
"""Store ``origin`` as a validated float 2-tuple (no caller-owned list/array)."""
origin = tuple(float(v) for v in spec.origin)
if len(origin) != 2:
raise ValueError("Jet origin must be a 2D point")
_finite(*origin)
object.__setattr__(spec, "origin", origin)
@dataclass(frozen=True)
class Sector:
"""Annular sector about ``origin``: |angle offset| < half_width, r_min <= r <= r_max."""
@@ -42,7 +51,8 @@ class Sector:
r_min: float = 0.0
def __post_init__(self):
_finite(*self.origin, self.angle, self.half_width, self.r_max, self.r_min)
_freeze_origin(self)
_finite(self.angle, self.half_width, self.r_max, self.r_min)
if not (0 < self.half_width <= math.pi and 0 <= self.r_min <= self.r_max):
raise ValueError("Sector requires 0 < half_width <= pi and 0 <= r_min <= r_max")
@@ -63,7 +73,8 @@ class Tube:
half_width: float
def __post_init__(self):
_finite(*self.origin, self.axis_angle, self.length, self.half_width)
_freeze_origin(self)
_finite(self.axis_angle, self.length, self.half_width)
if self.length <= 0 or self.half_width <= 0:
raise ValueError("Tube requires positive length and half_width")
@@ -82,7 +93,7 @@ class Radial:
inward: bool = True
def __post_init__(self):
_finite(*self.origin)
_freeze_origin(self)
def direction(self, hits):
d = hits - np.asarray(self.origin, np.float64)