FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
0107 — A source-set body can see its own private source after overtaking its frozen wake (D3)
ID 0107
Class KNOWN-LIMITATION
Severity 2
Status blocked

Models: —

Found: 2026-10-12, feature wake-casting-trawl (trawl doors and clump weights cast as an IndexedWakeCastingObject). Documented the same day by owner ruling (2026-10-12): "document the D3 own-source limitation now, file the fix as a follow-up".

Decision needed: which fix (direct-only exclusion with new marenv API, or an env re-arm guard), and, for the first, the marenv version bump (minor; the owner decides).

Evidence

  • D3 ("a caster never sees its own wake") holds only for the registered (lumped) field. src/environment/WakeCaster.cpp:381-387: WakeCaster::Query() sets query.exclude = m_lumpedField and query.own = m_privateField, the whole private field. include/fhsim_environment/BodyWake.h:46-47 and :260-261 (Query()) state the same.
  • marenv include/marenv/wake/WakeRegistry.h:23-27: WakeQuery has only exclude (a registered field) and own (an unregistered field to add); there is no way to leave out one source of a field.
  • marenv src/wake/SourceSetWake.cpp:186-215: SourceSetWake::Snapshot::Sample (Direct) composes every source and image, skipping none.
  • marenv include/marenv/wake/GriddedWake.h:25: GriddedSnapshot::logRatio holds only the summed L = Σ log(1 − d_i) at the nodes (src/wake/WakeBuilder.cpp:168); one source's share cannot be taken out.
  • include/fhsim_environment/WakeCaster.h:130-137 already names the mechanism: the fields translate at the source velocity of the last rebuild; a source whose velocity then changes by Δv moves by about Δv·T against its frozen wake and, past x_start (half a source diameter by default), enters its own wake.
  • Freeze re-arms on the area-weighted mean carrier position (src/environment/WakeCaster.cpp:416, src/environment/WakeSchedule.cpp:175-188) once it is WakeRearmDistance (0.1) × √(W·H) away from the frozen frame (WakeSchedule.cpp:260, the frontal rectangle of the whole object, WakeCaster.cpp:552). For a rig about 100 m wide that is several metres, far more than a door's x_start of about 1.4 m; bodies that oscillate about a fixed mean do not move the mean at all.

Effect

Affects every source set: net panels (PanelSourceSet), the bodies of an IndexedWakeCastingObject (fhsim_marine_elements' trawl doors and clump weights, TestWakeCluster) and WakeGroup members. A body that has moved downstream of its frozen elementary source by more than x_start feels its own deficit: a drag drop with no physical cause, which feeds the next rebuild. Not checked: no test samples a source set's own body after overtaking. Documented in BodyWake.h, WakeSourceSet.h (IndexedWakeCastingObject), doc/user/wakes.md ("Who sees the wake") and CONTEXT.md (Private Field).

Possible fix

  1. Direct-only exclusion (recommended). Tag the sources in marenv's ElementarySource (an id), and let a query skip source i at sample point i (a new WakeQuery member, or a Sample overload taking the skipped id). The caster supplies a point-to-source map: it publishes sources in flow order and skips the non-casting ones (src/environment/WakeCaster.cpp:531-533 sort, :701-716 the skip and push_back), so the published index is not the set's source index; the map must be built in MarchPrivateSources. For a net a sample point is a node and a source a panel, so "own" means the panels that carry the node; this needs a ruling too. New marenv API: a minor bump, the owner decides.
  2. Env re-arm guard. Re-arm Freeze (or rebuild earlier under Periodic) when any source, not only the mean position, has moved more than x_start relative to the frozen frame. No marenv change; cheap for a few bodies, one check per source per step for a net. It narrows the window but does not close it between rebuilds (Periodic, WakeBlendTime), and could make an oscillating rig rebuild very often (see ENV-0109).

Not advised: exclusion under Grid. The grid stores only the summed log ratio, so excluding a source would need either one grid per source or subtracting a re-evaluated kernel from interpolated nodes, which does not cancel the source's own interpolated deposit (see ENV-0108).

Test that would prove it

A TestWakeCluster (or a two-body IndexedWakeCastingObject) with WakeUpdate="Off", WakeEvaluation="Direct", built at rest in a current; then move body 0 downstream by 2 x_start against the frame and take: its WaterVelocity must equal the one with its own source removed (Direct, fix 1), or the caster must have rebuilt (fix 2). Today the first fails.

Risk

Fix 1 changes results of every source set whose bodies drift against their frozen frame, nets included; existing net baselines may move. Fix 2 adds rebuilds (cost, blends) and log lines; it interacts with ENV-0109.