Skip to content

Clean-up, refactor, streamlining, and upgrades for HydroDyn strip-theory implementation - #3487

Open
luwang00 wants to merge 14 commits into
OpenFAST:devfrom
luwang00:Morison_Refactor
Open

luwang00 wants to merge 14 commits into
OpenFAST:devfrom
luwang00:Morison_Refactor

Conversation

@luwang00

@luwang00 luwang00 commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

This PR is ready to be merged.

Feature or improvement description
This PR includes various improvements to the strip-theory code within HydroDyn. The changes include simple clean-up and refactoring, streamlining the formulation and internal logic, and upgrades for unrestricted strip-theory member rotation. These changes were made to prepare HydroDyn for planned upcoming upgrades and new features. Key changes are listed below.

Strip-theory member displacement (WaveDisp)
Strip-theory member displacements are controlled by the WaveDisp setting. Previously:

  • Setting WaveDisp=0 held the strip-theory members at the fixed reference position. The only displacement was due to a static floater yaw orientation offset given by PtfmRefY with PtfmYMod=0. Because the strip-theory members were fixed in place, WaveDisp=0 could not be used with a dynamic reference yaw position (PtfmYMod=1). OpenFAST would error out with this input combination.
  • When WaveDisp=1, a mixed approach was used: the strip-theory member nodes and joints were allowed to move following the instantaneous motion of the structure, except that any vertical displacement was ignored when wave stretching was disabled (WaveStMod=0). The wave kinematics and dynamic pressure were computed at the displaced nodal/joint positions. However, the member and joint orientations were only updated based on the reference platform low-frequency yaw offset PtfmRefY (static or dynamic) when computing the strip-theory loads. This implementation was adopted to maintain some geometric consistency with the potential-flow bodies, which only have XY-plane drift and low-frequency yaw corrections.

The previous formulation was somewhat convoluted, opaque, and not fully consistent. For example, it used instantaneous nodal displacements for wave kinematics but corrected member/joint orientations only for low-frequency yaw drift. With the present upgrade, the strip-theory member displacement is reformulated and the WaveDisp switch reinterpreted in a more intuitive fashion.

  • WaveDisp=0 now positions and orients strip-theory members and joints in a way that is strictly geometrically consistent with the potential-flow bodies. Both the nodal positions (at which wave kinematics and dynamic pressure are computed) and the member/joint orientations are updated only according to the platform reference yaw (PtfmRefY) and platform XY-drift following the same ExctnDisp and ExctnCutoff settings used by the potential-flow bodies. This implies only 3-DOF horizontal-plane displacements (surge, sway, and yaw).
  • WaveDisp=1 now positions strip-theory members/joints based on the full, exact instantaneous displacement of the structure. Both nodal positions and joint/member orientations are updated based on the 6-DOF motion of the structure with nonlinear rotation. This means a strip-theory-only model no longer places any constraint on body rotation, and HydroDyn can be used to model, e.g., the upending of a spar where the structure rolls/pitches by 90 deg. Furthermore, the full displacement in all three directions is now included regardless of WaveStMod. Load smoothing is enabled when either WaveStMod>0 or WaveDisp=1 for suitable surface-piercing members, since either option can cause nodes to enter/exit the water. Additionally, with WaveDisp=1, HydroDyn no longer assumes rigid/straight members, which can be useful when coupled with other structural solvers.

Added mass (AMMod)
Previously, an AMMod switch was included in the primary HydroDyn input file. Setting AMMod=0 forced HydroDyn to always compute the distributed added-mass force up to the still-water level while neglecting structure Z-displacement. This was added to address numerical stability issues with some hydroelastic models.

With the general improvement to core solver stability since OpenFAST v5, this is deemed no longer necessary, and it is hardly ever used based on user feedback. Therefore, the AMMod input option is removed here to further streamline the internal logic. Added mass is now computed with the same wetted-section logic as the drag/inertia components (up to the instantaneous free surface when WaveStMod>0, SWL otherwise), eliminating the separate AMMod code paths.

Code cleanup

  • Consistent unit-vector formulation for member inclination and orientation. Removed legacy trigonometry-based calculation.
  • Precompute/reuse the member reference DCM (CMatrix).
  • Converted section-geometry if/else-if chains to SELECT CASE.
  • Streamlined/fixed error handling.
  • Consolidated end-plate buoyancy into an AddEndBuoyancy helper subroutine.
  • Removed unused subroutines. Net ~330 fewer lines in Morison.f90.

Interface / input changes
Removed AMMod from the HydroDyn primary input file and modified WaveDisp input comment (see api_change.rst and updated HydroDyn user docs). openfast_io reader and writer are also updated accordingly.

Documentation
Updated HydroDyn input_files.rst, appendix.rst, and api_change.rst: rewrote WaveDisp semantics, removed the obsolete vertical-displacement explanation, and documented the AMMod removal.

Impacted areas of the software
HydroDyn, openfast_io, docs, r-test

Generative AI usage
Co-authored-by: Anthropic Claude claude@anthropic.com
Assisted by: GitHub Copilot support@github.com

Test results, if applicable
Due to the changes to WaveDisp=1 behavior, the results of the following three tests changed slightly:

  • hd_5MW_ITIBarge_DLL_WTurb_WavesIrr
  • 5MW_ITIBarge_DLL_WTurb_WavesIrr
  • 5MW_MRSemi_DLL_WSt_WavesIrr

The changes are expected and small (differences relative to the previous baseline are of the same order of magnitude as the tolerance threshold).

  • r-test branch merging required

luwang00 and others added 14 commits October 5, 2026 15:59
…-vector helper

Morison_CalcOutput only consumed the axial unit vector k_hat from GetOrientationAngles; its tilt/heading trig outputs were unused. Replace that routine with a slim GetElementAxialVec(p1,p2,k_hat,...) that returns only k_hat and keeps the zero-length-element fatal guard (do not assume rigid structure). Convert the marine-growth weight moments F_WMG(4)/F_WMG(5) from sinPhi*sinBeta / sinPhi*cosBeta to the exact identities k_hat(2)/k_hat(1), update all four call sites, and drop the now-dead trig locals.

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>
…helper

Collapse the three duplicated external end-plate buoyancy blocks into a single contained AddEndBuoyancy subroutine, replace the brittle EqualRealNos partial-wetting arc test with the secStat classification returned by GetSectionFreeSurfaceIntersects_Cyl, drop the dead SetErrStat after that call, and convert the section-geometry branch to SELECT CASE.

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Convert bare SetErrStat propagation sites to 'if (Failed()) return' so fatal errors from sub-calls abort instead of continuing with bad data. Fixes two lost-error cases: paired GetTotalWaveElev calls that shared one SetErrStat (first status overwritten), and the VisMesh Transfer_Point_to_Line2 call that passed ErrStat/ErrMsg directly and clobbered accumulated errors.

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>
…tput

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Store each member's undisplaced section direction-cosine matrix as member%CMatrix at initialization instead of recomputing it every step via Morison_DirCosMtrx inside Morison_CalcOutput and getMemBallastHiPt.

The reference DCM is constant per (straight) member, so the per-step calls built from the static u%Mesh%Position were redundant. CMatrix is computed once in SetMemberProperties_Cyl (spin 0, axisymmetric) and SetMemberProperties_Rec (spin = MSpinOrient), then combined with the instantaneous u%Mesh%Orientation at each use site, preserving results bit-for-bit.

- Add CMatrix(3,3) to Morison_MemberType (registry + regenerated types).
- YawMember now yaw-rotates x_hat/y_hat for all section geometries, and deliberately leaves CMatrix in the undisplaced frame since it is composed with the full nodal orientation downstream.
- Remove dead Morison_DirCosMtrx calls, unused CMatrix/CMatrix0 locals, and the redundant MSecGeom checks in SetMemberProperties_Rec and YawMember.

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Delete dead code with no callers: Morison_DirCosMtrx_noSpin (generic interface collapsed to Morison_DirCosMtrx), ElementCentroid, ElementVolume, FindInterpFactor, InterpWrappedStpInt, InterpWrappedStpLogical, DistributeElementLoads, and AddEndLoad, plus stale commented references.

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Replace the trig-based incline angle (phi/sinPhi/cosPhi via acos/sin/cos) in member initialization with direct axial-unit-vector expressions, remove the now-redundant cosPhi_ref member field (consumers use k(3)), and guard the inclination-threshold checks with ABS(cosPhi) so a reversed member is still treated as vertical.

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Replace the yaw-only YawMember/YawJoint helpers with RotateMemberNode and RotateJoint, which rotate reference-configuration member and joint properties to either the reference-yaw orientation (WaveDisp=0) or the full instantaneous node orientation (WaveDisp=1). Rotate per node in all three hydrodynamic loops (smoothing, no-smoothing, UpdateDiscState) and at the free-surface-crossing element, reading from the pristine parameter copy to avoid compounding. Keep hydrostatic loads on exact orientation. Remove the input constraint forbidding PtfmYMod=1 with WaveDisp=0 and clean up error handling in Morison_UpdateDiscState.

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Add a PtfmRefXY pipeline so strip-theory (Morison) members translate with the low-frequency / instantaneous horizontal drift of the HydroDyn origin when WaveDisp=0, mirroring the existing PtfmRefY reference-yaw treatment and the WAMIT ExctnDisp filter.

The drift follows the user-specified ExctnDisp/ExctnCutOff regardless of whether potential-flow bodies are present: the raw ExctnDisp is captured into p%PtfmXYMod before HydroDynInput_ProcessInitData may force it to 0 for the WAMIT grid, and the ExctnCutOff>0 validation is checked beforehand and broadened to ExctnDisp=2.

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Update the WaveDisp input comment and documentation to reflect the clearer
interpretation: WaveDisp=0 places strip-theory members/joints at a
potential-flow-consistent reference configuration (reference yaw plus
horizontal x-y drift governed by ExctnDisp/ExctnCutOff), and WaveDisp=1 uses
the exact instantaneous displaced position and orientation. Updates
input_files.rst, appendix.rst, api_change.rst, and the FAST_writer.py comment
string.

Co-authored-by: GitHub Copilot <copilot@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
…heory

Remove the hidden logic that dropped the vertical (Z) displacement of
strip-theory member nodes when wave stretching was disabled. The member
submergence status now checks every node (members are no longer assumed
rigid/straight), and load smoothing is applied for right-side-up members
crossing the free surface once whenever wave stretching is enabled or
WaveDisp=1, since both can drive nodes in and out of the water. Smoothing is
skipped only when WaveStMod=0 and WaveDisp=0. Update input_files.rst to drop
the obsolete Z-displacement explanation.

Co-authored-by: GitHub Copilot <copilot@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
…d loads

The AMMod switch selected whether the distributed strip-theory added-mass
force was evaluated only up to the SWL (0) or followed the instantaneous
wetted section (1). With the improved solver stability in 5.1.0 the SWL-only
option is no longer needed, so AMMod is removed and the added-mass force is
now computed the same way as the other distributed loads: up to the
instantaneous free surface when wave stretching is enabled (WaveStMod > 0)
and up to the SWL otherwise.

Removes AMMod from the Morison registry/types, HydroDyn input parsing and
validation, the Python reader/writer, and the documentation (with an
api_change.rst entry). Bumps the r-test pointer to the input files with the
AMMod line removed (required because HydroDyn uses fixed-form line-position
input parsing).

Co-authored-by: GitHub Copilot <copilot@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
…thing

Use the SWL (z=0) as the free surface when wave stretching is disabled in the smoothing loop, matching how nodeInWater/MemSubStat are determined. Previously the instantaneous wave elevation was used to locate the crossing element while in-water status used the SWL, so the WaveStMod=0 + WaveDisp=1 combination could leave FSElem unset (-1) and segfault when indexing arrays. Also add a fatal-error guard if no free-surface-crossing element is found.

Co-authored-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Input validation and documentation still conflict with the newly introduced WaveDisp and ExctnDisp behavior.

3 open findings
What changed in this PR

Refactors HydroDyn strip theory to support consistent reference or full instantaneous member motion while simplifying added-mass handling.

Changes:

  • Redefines WaveDisp for full position/orientation handling.
  • Removes AMMod and consolidates Morison load logic.
  • Updates serialization, Python I/O, and documentation.
File Description
openfast_io/​openfast_io/​FAST_writer.py Writes the revised strip-theory format.
openfast_io/​openfast_io/​FAST_reader.py Reads HydroDyn files without AMMod.
modules/​hydrodyn/​src/​Morison.txt Updates Morison Registry types.
modules/​hydrodyn/​src/​Morison.f90 Refactors displacement, rotation, and load calculations.
modules/​hydrodyn/​src/​Morison_Types.f90 Updates generated Morison types and serialization.
modules/​hydrodyn/​src/​HydroDyn.txt Adds horizontal reference-drift state definitions.
modules/​hydrodyn/​src/​HydroDyn.f90 Computes and propagates reference XY drift.
modules/​hydrodyn/​src/​HydroDyn_Types.f90 Adds drift states and parameters.
modules/​hydrodyn/​src/​HydroDyn_Input.f90 Removes AMMod parsing and revises validation.
docs/​source/​user/​hydrodyn/​input_files.rst Documents revised strip-theory behavior.
docs/​source/​user/​hydrodyn/​appendix.rst Updates the example HydroDyn input.
docs/​source/​user/​api_change.rst Records the input/API changes.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1507 to +1509
if ( InputFileData%WAMIT%ExctnDisp == 2 .and. InputFileData%WAMIT%ExctnCutOff <= 0.0 ) then
CALL SetErrStat( ErrID_Fatal,'ExctnCutOff must be greater than zero.',ErrStat,ErrMsg,RoutineName)
end if
Comment on lines +324 to +325
Note that **PtfmYMod** and **PtfmRefY** also affect the strip-theory
hydrodynamic load when **WaveDisp** = 0. In that mode, the displacement
#
typedef ^ InputType MeshType Mesh - - - "Kinematics of each node input mesh" -
typedef ^ ^ ReKi PtfmRefY - - - "Reference platform yaw offset" (rad)
typedef ^ ^ ReKi PtfmRefXY {2} - - "x,y drift of the HydroDyn origin (PRP), consistent with potential-flow body ExctnDisp. Used to displace the Morison members when WaveDisp=0" (m)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants