FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
0018 — Example.xml: a Door output at t=0 depends on process memory layout
ID 0018
Class BUG
Severity 2
Status ready
Models Trawl/TrawlDoor
Found 2026-09-15 fix pass (new finding)
Decision needed

Evidence

Running FhSim (playpen build 3.0.0-743778a9, libfhsim_fisheryVis.so, headless) on a scratch copy of examples/input/Example.xml (TEnd 0.05; the Environment/Basic block replaced by a loadable Environment block from fhsim_environment/examples/input/, see 0019) with -o CSV output, twice with the same input file, gives different first data rows when only the process environment differs (an extra 3000-byte environment variable, or -c 0 versus -c 1): column 82 — the first Door column, AngleOfAttack under the observer's ASCII port order — is -8.090807e-03 in one run and 9.597028e-03 in the other at t = 0, and column 2 (CLControl output) differs with it. Two runs with an identical environment are byte-identical, so it is not a random source; the value depends on memory layout, which is the signature of an uninitialized read. The difference propagates: every later row differs in the low digits of the door and cable columns.

AngleOfAttack returns ma_dAlpha (src/trawl/ToTrawlDoor.cpp:62-65), computed by TD_HydroForces from ma_adRelVel_d (src/trawl/TrawlDoorBase.cpp:345), which is the door velocity minus the water velocity obtained from m_environment->GetParticleVelocity (:270-275). ma_dAlpha is zeroed in the constructor (:38), so the garbage enters either through the environment's particle velocity at t = 0 (the substituted Environment object, in fhsim_environment), through the door states as seen by the observer's first sample, or through an engine-side ordering of the first output evaluation. Not localized further.

Effect

The shipped roll-control example is not reproducible run to run; regression baselines for it (0001) cannot be byte-compared until the source is found. The magnitude at t = 0 is small (1e-2 rad) but it is fed into the PID loop.

Possible fix

Run the scratch scenario under valgrind or a -fsanitize=memory build of fhsim, fhsim_environment and this library; the first uninitialized-read report names the site. If it is in this repository, initialize the member; if it is in fhsim_environment or fhsim, file it there.

Owner question: none about the fix shape, but the scenario needs the Environment/Basic replacement from 0019 to run at all.

Test that would prove it

The sanitizer run is the proof. A cheaper guard once 0001 exists: the Example.xml regression case run twice with different environment sizes must produce identical CSVs.

Risk

None until the site is known.

Second data point (2026-09-17, from FISH-0029)

FISH-0029 recorded a second, independent manifestation of what is almost certainly the same upstream read. Trawl/TrawlDoor's AngleOfAttack port at t=0 in the trimmed regression case (tests/in/TrawlDoor/) reads 3.490653e-01 where the analytic value for the recorded state is 0.349065850399 (3.490659e-01) — a 5.5e-7 rad gap. That gap was proven to be independent of the observer's sampling mechanism: rewriting OutAngleOfAttack to compute directly from (dT, adX) (no cache) left the number unchanged, and reconstructing the port math with GetParticleVelocity assumed to return exactly the case current (2.5,0,0) reproduces the analytic value to 12 digits. The residual therefore lives entirely in GetParticleVelocity's actual return at Pos=(0,0,20) — the same marenv/fhsim_environment call implicated here. It is two orders of magnitude smaller than this item's 1e-2 rad anomaly but points at the same site, and the same sanitizer run resolves both. See the resolved FISH-0029 for the full derivation.

Why not solved (fix pass 2026-09-15)

New finding recorded during the fix pass, not fixed. A Door output column (AngleOfAttack) at t=0 in Example.xml differs between two runs that differ only in the size of the process environment, while identical environments give identical output. That points to an uninitialised read somewhere on the door, environment or engine path, and it is not localised. Fixing it needs a valgrind or MemorySanitizer run to find the read, which was outside the scope of a documentation-and-consistency pass. Full details are in item 0018 of the codebase's REVIEW_ISSUES.md.