|
FhSim
3.1.0
Marine systems simulation
|
| ID | 0006 |
| Class | BUG |
| Severity | 2 |
| Status | blocked |
| Models | Trawl/TrawlDoorAddedFoil |
| Found | 2026-09-15 audit (a7d080d) |
| Decision needed | Confirm the count-versus-last-index convention of Interp1D and land the corrected length together with FISH-0004 and the first foil test. |
src/trawl/TrawlDoorAddedFoil.cpp:15-26 builds three arrays of length m_numFoilHydroPoints + 2, writing the user's table into [1 .. n] and putting clamping sentinels at [0] and [n+1]:
src/trawl/TrawlDoorAddedFoil.cpp:75-76 then interpolates with the unpadded count:
The padded arrays hold n + 2 samples, so Interp1D sees only the first n: the lower sentinel plus the first n - 1 user points. The last user point and the upper sentinel are never visible to it.
For any foil whose polar is tabulated (once FISH-0004 makes the table live at all): the last row of FoilHydro_Aoa/CL/CD is silently ignored, so a user who tabulates up to stall loses the stall point; and there is no upper clamp, so an angle of attack above the second-to-last tabulated angle is extrapolated or saturated against whatever Interp1D does past its last sample, instead of being clamped to zero lift by the +FLT_MAX sentinel the constructor installed. Lift on the wrong side of stall is precisely where a control study needs the clamp.
Pass the padded length at src/trawl/TrawlDoorAddedFoil.cpp:75-76:
Confirm sfh::math::Interp1D's last parameter is a sample count and not a last-index before committing; if it is a last-index the correct argument is m_numFoilHydroPoints + 1. That is a signature lookup in the fhsim package, not a judgement call.
No open semantics: the intent is unambiguous from the sentinels — they exist to clamp out-of-range angles, so the interpolator must see them. Blocked on FISH-0004 only: until the zeroing at TrawlDoorAddedFoil.cpp:93-94 is removed, neither foilCL nor foilCD reaches the simulation, so this fix is unobservable and untestable.
Depends on FISH-0001 and FISH-0004. In tests/in/SimObj-TrawlDoorAddedFoil-Stall/, tabulate a three-point polar whose last point is the only non-zero CL and command an angle of attack that lands on it: before the fix the lift is zero (the point is invisible to the interpolator), after it non-zero. A second case commands an angle well beyond the table and asserts the lift is zero, proving the upper sentinel is in play.
Confined to Trawl/TrawlDoorAddedFoil, which no shipped input file uses. It changes foil forces for tabulated polars, so it belongs in the same release as FISH-0004.
Re-confirmed at HEAD: Interp1D(..., m_numFoilHydroPoints) is called on arrays allocated with n+2 entries. The fix is a single constant, but it changes the foil forces and is unobservable until FISH-0004 lands, and the count-versus-last-index convention of Interp1D was not verified during the fix pass, so even the direction of the correction is not certain. Blocked on FISH-0004; land the two together with the first foil test.