Skip to content

Implementation of generalized support structures in AeroDyn - #3311

Merged
andrew-platt merged 36 commits into
OpenFAST:devfrom
luwang00:f/AD_GS
Aug 29, 2026
Merged

Implementation of generalized support structures in AeroDyn#3311
andrew-platt merged 36 commits into
OpenFAST:devfrom
luwang00:f/AD_GS

Conversation

@luwang00

@luwang00 luwang00 commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

This PR is ready to merge.

Feature or improvement description
This PR adds generalized support structures in AeroDyn, allowing the flow-induced loads on the support structures and the influence on the rotors to be modeled (effect on OLAF wake will be implemented in the future). The motion and loads mapping for the generalized support structures is between AeroDyn and SubDyn (instead of ElastoDyn).

This PR is still a work-in-progress:

  • Update AeroDyn input file format and parsing with new sections for generalized support structures
  • Construct AeroDyn motion input and loads output meshes for general support structures (at the moment, these are point meshes instead of line meshes to match SubDyn)
  • Add mesh-mapping between AeroDyn and SubDyn in the glue-code
  • Add mesh-mapping between AeroDyn and ElastoDyn platform point mesh (only if no SubDyn and a single ElastoDyn)
  • Update AeroDyn Jacobian subroutines to include the new general support structure mesh motion input (could use more checking and optimization)
  • Implement drag force on the general support structure in AeroDyn
  • Implement support structure influence on rotors and OLAF wake
  • Implement support structure wake effects on rotors and OLAF wake
  • Add mesh and load VTK visualization for the AeroDyn generalized support structures (glue-code VTK_type=3 only for now pending line mesh visualization; consistent with SubDyn)
  • Update AeroDyn summary file
  • Update openfast_io
  • Update r-tests
  • Update documentation
  • Add new AeroDyn output channels (deferred)

Two new input sections for the generalized support structures have been added to the AeroDyn input file just before the Outputs section. The first section allows the user to specify the coordinates of a list of NumGSJoints joints. The second section defines a list of NumGSMembers support members.

For each member in this second table, the user needs to specify the starting and ending joints of each member by referencing the GSJointID in the previous joint table. The user also needs to specify the diameter, drag coefficient, and turbulence intensity (for the Eames wake deficit model) at the starting and ending joints. If different values are provided, AeroDyn assumes a linear variation of each parameter along the member. Finally, the user needs to specify the maximum element length GSMDiv for the discretization of each member. AeroDyn will discretize each member into elements of equal length not greater than GSMDiv.

The format of the new input sections are provided below. Note that the generalized support structure affects all rotors and does not need to be defined for each rotor separately.

======  General support structure joints  ===========================================================
??                    NumGSJoints
GSJointID      GSJointxi      GSJointyi      GSJointzi
(-)            (m)            (m)            (m)
...            ...            ...            ...
======  General support structure members  ==========================================================
??                    NumGSMembers
GSMemberID  GSMJointID1  GSMJointID2     GSMDia1       GSMDia2     GSMCd1        GSMCd2        GSMTI1       GSMTI2       GSMDiv
(-)            (-)          (-)           (m)           (m)          (-)           (-)          (-)          (-)           (m)
...            ...          ...           ...           ...          ...           ...          ...          ...           ...

Implementation details and theory can be found in the updated AeroDyn user and theory documentation. Note that the previously incomplete documentation on the tower influence models was also expanded to provide a foundation for the generalized support structure influence model.

Based on VTK visualization, motion and load mesh mapping between AeroDyn generalized support structure and SubDyn is working correctly. Both mesh node positions and force vectors are in alignment.

Impacted areas of the software
AeroDyn, Glue-code, openfast_io, docs

Generative AI usage
Co-authored-by: Microsoft Copilot [copilot@microsoft.com]
Co-authored-by: Anthropic Claude [claude@anthropic.com]

Test results, if applicable
The changes made to the AeroDyn input file require updating the r-tests.

  • r-test branch merging required

luwang00 and others added 19 commits April 17, 2026 14:42
…continuities) and blend member fields

Several fixes to the generalized support structure (GS) influence models
in AeroDyn, developed and validated today against the QuadRotor case:

1. Separate potential-flow and shadow contributions. CalculateGSInfluence
   now returns v_pot and v_shad independently instead of a single merged
   vector. This fixes (a) double-counting when both models are active
   (a single member gave v + |v|), and (b) a coordinate-dependent
   root-sum-square that destroyed the sign of the streamwise deficit and
   scrambled the transverse direction (dip appeared as a speed-up at the
   wrong azimuth). Shadow is now RSS of the signed local-frame deficit
   magnitudes applied along the vector-sum direction (frame independent).

2. Fix a spurious deep (~68%) deficit on the upwind rotor. In
   GSInfl_NearestPoint the axial offset was forced to zbar=0 at interior
   junctions and the axial direction used the point-mesh identity instead
   of the member axis, so the wide center column members injected a full
   doublet deficit ~190 m past their ends. The true zbar and member-axis
   z_hat are now used, so the abs(zbar)<1 guard correctly suppresses
   members the blade node is axially past.

3. Remove step-like discontinuities in the induced velocity. Apply the
   same cosine-squared taper as TwrInfl_NearestPoint at every GS member
   end (junction or free end), so each finite member's field decays
   smoothly (C1: zero value and slope) to zero at |zbar|=1 instead of
   switching on/off.

4. Replace the linear superposition of per-member potential-flow
   contributions with an influence-weighted partition-of-unity blend,
   V = sum_i w_i v_i / sum_i w_i, w_i = |v_i|^p (p = GSBlendExp = 6).
   Summing potential-flow solutions over-counts the induced velocity where
   members meet, producing a sharp junction notch. The blend is a convex
   combination, so it never exceeds the strongest single member (no
   double-counting), reduces to the single-member result in isolation, and
   requires no structure-topology logic. p=6 is the smallest even exponent
   that makes the weight (v.v)^(p/2) a polynomial (C-infinity) while
   keeping the worst-case collinear-joint dip below 5%.

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Move GSPotent, GSShadow, and GSAero from AD_InputFile into the GSInputFile registry type and read them directly in Init_GSParam, replacing the hardcoded baseline/Powles/noVIV defaults that made option 2 (Bak/Eames), GSShadow_none, and disabling GSAero unreachable. Update the input parser, SetParameters, CompAeroMaps override, and input validation to reference InputFileData%GS%*, and regenerate AeroDyn_Types.

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>

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

Document that the generalized support structure (GS) influence is not applied to the sector-averaged inflow (SetSectAvgInflow) or the OLAF/FVW wake wind (SetInputsForFVW), since there is no array/scalar GSInflArray analogous to TwrInflArray. Also clarify the GSInfl_NearestPoint option-2 nearest-node comment for curved/deflected members. Comments only; no behavior change.

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>

Co-authored-by: Claude <noreply@anthropic.com>
The blade-node clearance output (BNClrnc) previously overwrote the tower clearance with the GS clearance when both influence models were active. Combine them so the output reports the minimum clearance across whichever models are active, keyed off array allocation (each clearance array is allocated only when its model is on).

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>

Co-authored-by: Claude <noreply@anthropic.com>
- Store the nearest-member GS clearance in m%GSClrnc so the blade-node Clrnc output is populated; previously GSInfl passed a local scalar that was never written to m%GSClrnc and only held the last member's value

- Reduce clearance as the minimum over all GS members (independent of DisturbInflow), matching the AeroDyn_IO reducer that reports the smallest tower/GS clearance

- Remove dead commented-out outer member loops and dedent the bodies of GSInfl_NearestLine2Element and GSInfl_NearestPoint (both are called one member at a time)

- Drop redundant iMem_min and merge the two associate blocks in GSInfl_NearestPoint; fix its defensive fallback to reference the passed-in member

- Update the GSClrnc registry/type comment to reference the nearest GS member instead of the tower

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Document the GSPotent/GSShadow/GSAero options and the GENERAL SUPPORT STRUCTURE joint/member input tables, including the MHK vertical-coordinate (MSL) datum convention and coupling behavior with SubDyn and the ElastoDyn platform.

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

Co-authored-by: Claude <noreply@anthropic.com>
Document the tower potential-flow (TwrPotent baseline and Bak) and shadow (Powles, Eames) models, their superposition, and the tower-end handling (clearance-based exclusion capsule and cosine-squared axial taper). Clarify that the coordinate-scaling taper is exact cos^2 only for potential flow and note that the tower-shadow end treatment will be improved in the future.

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Document the GS influence models as an extension of the tower models: per-member potential-flow and shadow evaluation reusing the tower expressions and end handling. Explain the rationale for the influence-weighted partition-of-unity combination of per-member potential-flow contributions (avoids joint double-counting, topology-free, reduces to the single-member/tower result) and the root-sum-square combination of shadow deficits. Note that the wake model may be unsuitable for strongly raked members and is expected to be improved to align the wake with the incident wind direction in the future.

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>

Co-authored-by: Claude <noreply@anthropic.com>
Write the GS point mesh (with GSLoad drag force vectors as point data) in WrVTK_AllMeshes (VTK_Type=3). A single copy is written for the shared GS structure to avoid duplication in multirotor cases. Deferred from VTK_Type=1/2 and from driver VTK output until proper line-mesh visualization is added, to align with SubDyn.

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

Co-authored-by: Claude <noreply@anthropic.com>
Add theory-manual sections for the tower cross-flow drag load (TwrAero) and the generalized support-structure drag load (GSAero), including the drag-force formulation, the point-mesh lumping used for the GS load, and a note that the GS provides drag only (buoyancy, added mass, and fluid inertia belong in HydroDyn) with a caution against double-counting drag between AeroDyn and HydroDyn.

Co-authored-by: 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.

Pull request overview

This PR introduces a generalized support-structure (GS) capability in AeroDyn, enabling support-member drag loads and support-member influence/shadow effects on rotor inflow, with coupling/mapping handled between AeroDyn and SubDyn (or ED platform point when SubDyn is absent). It also extends OpenFAST glue-code VTK output and updates AeroDyn documentation to describe the new GS inputs and theory.

Changes:

  • Add GS data types, parameters, input parsing, initialization, inflow evaluation, influence models, and GS drag load calculation in AeroDyn.
  • Add glue-code mesh/load mappings for GS motion and loads between AeroDyn↔SubDyn and AeroDyn↔ED platform point mesh, plus VTK output for GS meshes.
  • Update AeroDyn user/theory docs to document the new GS input sections and modeling approach.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
modules/subdyn/src/SubDyn_Types.f90 Minor type/comment/line-number adjustment related to member node bookkeeping.
modules/openfast-library/src/FAST_Subs.f90 Write a single GS VTK output (avoids duplicates in multirotor cases).
modules/openfast-library/src/FAST_Mapping.f90 Add GS motion/load mappings between AD and SD / ED platform point mesh.
modules/aerodyn/src/AeroDyn.f90 Core GS implementation: init, inflow, influence, drag loads, and Jacobian plumbing.
modules/aerodyn/src/AeroDyn_Types.f90 Add GS-related types/fields and serialization support; update AD data-loc indices.
modules/aerodyn/src/AeroDyn_Registry.txt Register GS constants and new GS typedefs/fields for code generation/registry.
modules/aerodyn/src/AeroDyn_IO.f90 Parse new GS input sections; adjust clearance output behavior; add GS summary file writer.
modules/aerodyn/src/AeroDyn_Inflow_Types.f90 Add ADI registry plumbing for new GSMotion/GSLoad meshes.
docs/source/user/aerodyn/theory.rst Add/expand theory documentation for tower influence and new generalized support structure models.
docs/source/user/aerodyn/input.rst Document new GS input sections, coupling behavior, and summary/clearance outputs.

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

Comment thread modules/aerodyn/src/AeroDyn.f90
Comment thread modules/aerodyn/src/AeroDyn_IO.f90 Outdated
Comment thread modules/aerodyn/src/AeroDyn_Registry.txt
Comment thread modules/aerodyn/src/AeroDyn_Types.f90

@jjonkman jjonkman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm excited to see this feature incorporated into AeroDyn. Please find a few minor comments on the documentation below.

Comment thread docs/source/user/aerodyn/input.rst Outdated
Comment thread docs/source/user/aerodyn/input.rst Outdated
Comment thread docs/source/user/aerodyn/input.rst Outdated
Comment thread docs/source/user/aerodyn/input.rst Outdated
Comment thread docs/source/user/aerodyn/input.rst Outdated
Comment thread docs/source/user/aerodyn/theory.rst Outdated
Comment thread docs/source/user/aerodyn/theory.rst Outdated
Comment thread docs/source/user/aerodyn/theory.rst Outdated
Comment thread docs/source/user/aerodyn/theory.rst Outdated
Comment thread docs/source/user/aerodyn/theory.rst
luwang00 and others added 2 commits August 27, 2026 18:57
…puts

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

Co-authored-by: Claude <noreply@anthropic.com>
@luwang00
luwang00 marked this pull request as ready for review August 28, 2026 16:44
luwang00 and others added 2 commits August 28, 2026 11:42
…d inflow

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

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

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

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

@jjonkman jjonkman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you for addressing my review comments!

luwang00 and others added 6 commits August 28, 2026 12:47
Previously the generalized support-structure (GS) inflow influence was applied
only in RotCalcOutput. The state-derivative, constraint-residual, and
linearization paths gated GS behind an absent optional argument, so GS influence
was silently dropped from BEMT/DBEMT/UA state marching, the induction (phi)
constraint solve, and the input/state/constraint Jacobians -- while the tower
influence was applied unconditionally.

Thread GSInflow into RotCalcConstrStateResidual and RotCalcContStateDeriv, and
forward it from AD_UpdateStates, AD_CalcConstrStateResidual, and the
dYdu/dXdu/dXdx/dZdz Jacobian blocks. Also thread it into the remaining
operating-point setup/init calls (frozen-wake induction, BEMT_InitStates state
initialization, and the UpdatePhi/ValidPhi guess) so those match the
GS-inclusive nonlinear model. In dXdu, recompute GSInflow_perturb via
AD_CalcWind_GS for parity with the dYdu block, and hoist the RotInflow/GS
perturbation copies above the dYdu block so both loops have allocated
perturbation copies (fixes a latent RotInflow_perturb allocation gap when dXdu
is requested without dYdu).

Now that every call site supplies it, make GSInflow a mandatory argument in
SetInputs, SetDisturbedInflow, RotCalcConstrStateResidual, and
RotCalcContStateDeriv (removing the now-redundant present() guard), and position
it immediately after RotInflow to match RotCalcOutput and the RotJacobianP*
routines. Any future path that omits GS inflow now fails to compile rather than
silently dropping it.

Also apply GS influence to the OLAF/FVW lifting-line disturbed inflow
(SetInputsForFVW), so unsteady airfoil aerodynamics see the GS-disturbed inflow
regardless of Wake_Mod. Document the remaining limitation (GS not applied to the
OLAF wake-convection wind) in the theory manual.

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

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

Update the documented AeroDyn primary example and the API change log for the
new generalized support-structure (GS) influence model.

- ad_primary_example.dat: add GSPotent/GSShadow/GSAero switches after TwrAero
  and the required "General support structure joints" and "General support
  structure members" sections (with 0 counts and their header/units lines) so
  the documented example parses with the current reader.
- api_change.rst (v5.1.0): document the new GS switches and the two new
  sections in the "Added in OpenFAST 5.1.0" table, mirroring the multi-line
  table layout used for other module sections.

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

Add input validation for the generalized support-structure (GS) model in
ValidateInputData:

- Range-check GSPotent (0/1/2) and GSShadow (0/1/2), mirroring the existing
  GSAero and tower switch checks.
- Require strictly positive GS member diameters (GSMDia1/GSMDia2) when GSPotent
  or GSShadow is enabled, preventing a 2.0/GSDiam division by zero in the GS
  potential-flow/shadow influence routines.
- Enforce 0.05 < GSMTI1/GSMTI2 < 1 (fatal) with a >0.4 unphysical warning when
  the Eames GS shadow model is selected, preventing a GSTI division by zero
  (mirrors the Eames tower-shadow limits).
- Warn when a GS model is enabled (GSPotent, GSShadow, or GSAero) but no GS
  members are defined (NumGSMembers=0), so the GS model being disabled is no
  longer silent.

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Addresses the error-handling issues raised in the PR review (section 6.7):

- AD_PrintSum_GS call in AD_Init passed the accumulated ErrStat/ErrMsg as the
  routine's INTENT(OUT) args, clobbering prior warnings and testing stale error
  state; now uses ErrStat2/ErrMsg2.
- AD_JacobianPInput, AD_JacobianPContState and AD_JacobianPConstrState called
  AD_CalcWind_GS immediately after AD_CalcWind_Rotor with a single status check;
  AD_CalcWind_GS resets ErrStat to none, silently dropping a rotor wind error.
  Added an intermediate check between the two calls.
- AD_JacobianPInput leaked RotInflow_perturb and GSInflow_perturb (created with
  MESH_NEWCOPY, never destroyed); both are now destroyed in cleanup().
- AD_PrintSum_GS initializes its INTENT(OUT) ErrStat/ErrMsg before the early
  return on NMembers<=0.
- Removed the unused y argument and unused YAML import from AD_PrintSum_GS.

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

The continuous-state-derivative operating-point path in FAST_GetOP called
RotCalcContStateDeriv without the now-mandatory GSInflow argument, so the BEMT
state derivative used an inflow that omitted the generalized support structure
disturbance (and would fail to compile once GSInflow was made mandatory).

- FAST_GetOP: compute GS inflow via AD_CalcWind_GS (chaining StartNode after the
  rotor wind calc) and pass m%Inflow(iInput)%GSInflow to RotCalcContStateDeriv.
- AeroDyn: make AD_CalcWind_GS public so the glue code can call it, mirroring the
  already-public AD_CalcWind_Rotor.

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
luwang00 and others added 2 commits August 28, 2026 17:42
Two review follow-ups for the generalized support structure (GS) feature:

- ExternalInflow (§6.5b): Init_ExtInfw sized nNodesVel from hub/blades/tower/
  nacelle/tailfin only, so the GS nodes that AD_CalcWind_GS appends to the shared
  wind-point sequence were uncounted. Under CompInflow=2 the point-field query
  silently returns zero inflow on the GS nodes (and shifts any OLAF wake-point
  indices). ExternalInflow does not export GS node positions to CFD nor map
  velocities back, so raise a fatal error when GS is active with ExternalInflow
  instead of running with bad inflow.

- OLAF wake BoxExceedAllow (§6.6): add an in-code comment explaining that
  BoxExceedAllow=.true. on the MHK+SeaState WaveField wake-point query matches the
  existing InflowWind branch, so far-wake OLAF particles that drift out of the
  current/wave grid extrapolate instead of aborting the run.

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

GSMotion no longer registers the Orientation field in the mesh or in the
linearization variables; the GS influence and aero models use only
translational position and velocity. Also documents that the shared GS
structure is sampled once from rotor 1 and reused for all rotors.

Bumps the r-test pointer so the MHK_RM1_Floating_Tank-scaled fixture disables
the tower model (TwrPotent=0, TwrShadow=0, TwrAero=False). The AeroDyn
C-binding rejects tower/GS options, so py_wavetank_test1 could not initialize;
disabling tower matches both the wavetank and full OpenFAST baselines.

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
@andrew-platt
andrew-platt merged commit 83d329e into OpenFAST:dev Aug 29, 2026
13 checks passed
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.

5 participants