Add User-defined wind documentation for InflowWind#3225
Add User-defined wind documentation for InflowWind#3225deslaughter wants to merge 3 commits intoOpenFAST:devfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds developer-facing documentation and code templates to support implementing user-defined wind fields (WindType = 6) in InflowWind, including registry/type updates to carry user parameters and (optionally) time-series data.
Changes:
- Extends registry/type definitions for
User_InitInputTypeandUserFieldType(including pack/unpack/copy/destroy support). - Updates the WindType=6 code path to call
UserField_GetVel()and improves template comments/error messages for user implementations. - Adds new ReadTheDocs content for user-defined wind fields and links it into the InflowWind docs index (plus an internal markdown guide).
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| modules/inflowwind/src/InflowWind_IO_Types.f90 | Adds fields to User_InitInputType and updates copy/pack/unpack accordingly. |
| modules/inflowwind/src/InflowWind_IO.txt | Documents and registers the new User_InitInputType fields. |
| modules/inflowwind/src/InflowWind_IO.f90 | Expands IfW_User_Init template and sets WindType=6 metadata/summary output. |
| modules/inflowwind/src/IfW_FlowField_Types.f90 | Extends UserFieldType with optional arrays/filename and updates lifecycle + serialization routines. |
| modules/inflowwind/src/IfW_FlowField.txt | Documents and registers the new UserFieldType fields. |
| modules/inflowwind/src/IfW_FlowField.f90 | Implements WindType=6 velocity evaluation loop via UserField_GetVel and adds detailed template docs. |
| modules/inflowwind/USER_DEFINED_WIND.md | Adds an implementation guide for developers working directly in the source tree. |
| docs/source/user/inflowwind/user_defined.rst | Adds RTD documentation page describing how to implement user-defined winds. |
| docs/source/user/inflowwind/index.rst | Adds the new user_defined.rst page to the toctree. |
| docs/source/user/inflowwind/appendix.rst | Adds download links intended to point to user-defined wind example files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ! Example: Set reference height (after regenerating types from registry) | ||
| ! UF%RefHeight = InitInp%RefHt | ||
| UF%RefHeight = 0.0_ReKi ! Temporary: set to sensible default | ||
|
|
||
| ! Example: Store filename if reading from file (after regenerating types) | ||
| ! UF%FileName = InitInp%WindFileName | ||
|
|
||
| ! Example: Allocate and read data arrays if needed | ||
| ! if (len_trim(InitInp%WindFileName) > 0) then | ||
| ! call ReadUserWindFile(InitInp%WindFileName, UF, TmpErrStat, TmpErrMsg) |
There was a problem hiding this comment.
IfW_User_Init currently hard-codes UF%RefHeight to 0.0 and leaves the new InitInp fields unused. Since User_InitInputType now includes RefHt/WindFileName/NumDataColumns, it would be better to at least initialize UF%RefHeight from InitInp%RefHt (and set FileDat%FileName from InitInp%WindFileName) so programmatic callers can pass these values without further code edits.
| ! Example: Set reference height (after regenerating types from registry) | |
| ! UF%RefHeight = InitInp%RefHt | |
| UF%RefHeight = 0.0_ReKi ! Temporary: set to sensible default | |
| ! Example: Store filename if reading from file (after regenerating types) | |
| ! UF%FileName = InitInp%WindFileName | |
| ! Example: Allocate and read data arrays if needed | |
| ! if (len_trim(InitInp%WindFileName) > 0) then | |
| ! call ReadUserWindFile(InitInp%WindFileName, UF, TmpErrStat, TmpErrMsg) | |
| ! Initialize reference height from input (after regenerating types from registry) | |
| UF%RefHeight = InitInp%RefHt | |
| ! Store filename if reading from file (after regenerating types) | |
| FileDat%FileName = InitInp%WindFileName | |
| ! Example: Allocate and read data arrays if needed | |
| ! if (len_trim(InitInp%WindFileName) > 0) then | |
| ! call ReadUserWindFile(InitInp%WindFileName, UF, TmpErrStat, TmpErrMsg) | |
| ! call ReadUserWindFile(InitInp%WindFileName, UF, TmpErrStat, TmpErrMsg) |
|
|
||
| ! Example: Set reference height (after regenerating types from registry) | ||
| ! UF%RefHeight = InitInp%RefHt | ||
| UF%RefHeight = 0.0_ReKi ! Temporary: set to sensible default |
There was a problem hiding this comment.
UF is INTENT(OUT) here, but only UF%RefHeight is assigned. The newly added UF%FileName (and any future scalar fields) will be left undefined, which can lead to non-deterministic behavior when writing summaries or when packing/unpacking for restart. Please explicitly initialize new scalar members (e.g., set UF%FileName to an empty string and NumDataLines to 0) during initialization.
| UF%RefHeight = 0.0_ReKi ! Temporary: set to sensible default | |
| UF%RefHeight = 0.0_ReKi ! Temporary: set to sensible default | |
| ! Initialize additional scalar members to safe defaults to avoid | |
| ! leaving the INTENT(OUT) structure in a partially undefined state. | |
| UF%FileName = "" ! No file associated by default | |
| UF%NumDataLines = 0_IntKi ! No data lines by default |
| ! ========= User_InitInputType ======= | ||
| TYPE, PUBLIC :: User_InitInputType | ||
| REAL(SiKi) :: Dummy = 0.0_R4Ki !< User field initialization input dummy value [-] | ||
| CHARACTER(1024) :: WindFileName !< name of file containing user-defined wind data (if applicable) [-] |
There was a problem hiding this comment.
User_InitInputType now contains a CHARACTER(1024) WindFileName with no default initialization. InflowWind_Init declares User_InitInput but never assigns its members before passing it into IfW_User_Init, so WindFileName can be undefined if a user starts using it. Consider default-initializing WindFileName to an empty string here to avoid reading uninitialized memory.
| CHARACTER(1024) :: WindFileName !< name of file containing user-defined wind data (if applicable) [-] | |
| CHARACTER(1024) :: WindFileName = '' !< name of file containing user-defined wind data (if applicable) [-] |
| cmake .. -DGENERATE_TYPES=ON | ||
| make | ||
|
|
||
| The build process automatically regenerates the ``*_Types.f90`` files from the ``.txt`` registry files. |
There was a problem hiding this comment.
The build instructions here say to run cmake .. and make to regenerate *_Types.f90, but type generation is controlled by the CMake option GENERATE_TYPES (default is OFF). Update the instructions to include enabling type generation (e.g., configure with -DGENERATE_TYPES=ON) so readers actually regenerate the registry types.
| The build process automatically regenerates the ``*_Types.f90`` files from the ``.txt`` registry files. | |
| When configured with ``-DGENERATE_TYPES=ON``, the build process automatically regenerates the ``*_Types.f90`` files from the ``.txt`` registry files. |
There was a problem hiding this comment.
The -DGENERATE_TYPES=On flag is only for builds with cmake; Visual Studio does not. need this flag.
| 1. **No Acceleration Support**: User-defined wind fields do not currently support | ||
| acceleration calculations needed by some modules (e.g., MHK turbines). | ||
|
|
||
| 2. **No Persistence**: Data must be recalculated if simulation is restarted. |
There was a problem hiding this comment.
This section claims there is "No Persistence" on restart, but InflowWind has Pack/UnPack support for FlowFieldType (including UserFieldType) via the generated types modules, so user-field data can be serialized/restored. Please revise this limitation to reflect the actual behavior (or scope it to cases where the user wind depends on external resources that cannot be packed).
| 2. **No Persistence**: Data must be recalculated if simulation is restarted. | |
| 2. **Persistence Scope**: State stored in ``FlowFieldType`` (including ``UserFieldType``) is | |
| serialized and restored across restarts via the generated Pack/UnPack support. However, if | |
| your user wind implementation depends on external resources or state that cannot be packed | |
| (e.g., live sensor connections, external services, or files managed outside the packed | |
| types), you are responsible for re-establishing or reloading those resources on restart. |
There was a problem hiding this comment.
Maybe better to say "no persistence between simulations"
| call UserField_GetVel(FF%User, Time, Position(:, i), VelocityUVW(:, i), TmpErrStat, TmpErrMsg) | ||
| call SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) | ||
| if (ErrStat >= AbortErrLev) return |
There was a problem hiding this comment.
In the User_FieldType case, other flow-field types return zero velocity for points at/below ground (Position(3)<=0) instead of calling the field-specific getter. For consistency (and to avoid requiring every user implementation to special-case negative Z), consider mirroring that behavior here by skipping UserField_GetVel and returning zero when Position(3,i) <= 0.
| call UserField_GetVel(FF%User, Time, Position(:, i), VelocityUVW(:, i), TmpErrStat, TmpErrMsg) | |
| call SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) | |
| if (ErrStat >= AbortErrLev) return | |
| ! For consistency with other field types, return zero velocity for points | |
| ! at or below ground (Z <= 0) instead of calling the user-defined getter. | |
| if (Position(3, i) <= 0.0_ReKi) then | |
| VelocityUVW(:, i) = 0.0_ReKi | |
| else | |
| call UserField_GetVel(FF%User, Time, Position(:, i), VelocityUVW(:, i), TmpErrStat, TmpErrMsg) | |
| call SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) | |
| if (ErrStat >= AbortErrLev) return | |
| end if |
| !! 2. Return velocity components in the global coordinate system (not rotated) | ||
| !! 3. Handle any spatial or temporal interpolation as needed | ||
| !! 4. Set appropriate error status if position/time is out of bounds | ||
| !! | ||
| !! Coordinate system: |
There was a problem hiding this comment.
The UserField_GetVel header comment says velocities should be returned in the "global coordinate system (not rotated)", but IfW_FlowField_GetVelAcc may rotate the input Position into the wind coordinate system and later rotates VelocityUVW back to global (RotFromWind). To avoid confusing users, please clarify that UserField_GetVel should return velocities in the same coordinate system as the passed-in Position (i.e., wind coordinates when RotateWindBox is enabled).
| !! 2. Return velocity components in the global coordinate system (not rotated) | |
| !! 3. Handle any spatial or temporal interpolation as needed | |
| !! 4. Set appropriate error status if position/time is out of bounds | |
| !! | |
| !! Coordinate system: | |
| !! 2. Return velocity components in the same coordinate system as the input Position | |
| !! (typically the global/inertial system; when InflowWind is configured with | |
| !! RotateWindBox, both Position and Velocity are in the rotated wind coordinates) | |
| !! 3. Handle any spatial or temporal interpolation as needed | |
| !! 4. Set appropriate error status if position/time is out of bounds | |
| !! | |
| !! Coordinate system (of the Position argument passed to this routine): |
| INTEGER(IntKi) :: NumDataLines = 0 !< number of data lines (for time-varying user wind) [-] | ||
| REAL(DbKi) , DIMENSION(:), ALLOCATABLE :: DTime !< time array for user-defined wind [seconds] | ||
| REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Data !< user-defined wind data array [NumDataLines, NumDataColumns] [-] | ||
| CHARACTER(1024) :: FileName !< name of user wind file (if applicable) [-] |
There was a problem hiding this comment.
UserFieldType adds a fixed-length FileName string without default initialization. Because UF is INTENT(OUT) in IfW_User_Init and the template initialization doesn't currently assign UF%FileName, this member can remain undefined and then get copied/packed. Consider default-initializing FileName to an empty string in the type definition to keep the default template behavior deterministic.
| CHARACTER(1024) :: FileName !< name of user wind file (if applicable) [-] | |
| CHARACTER(1024) :: FileName = '' !< name of user wind file (if applicable) [-] |
| cmake .. -DGENERATE_TYPES=ON | ||
| make | ||
|
|
||
| The build process automatically regenerates the ``*_Types.f90`` files from the ``.txt`` registry files. |
There was a problem hiding this comment.
The -DGENERATE_TYPES=On flag is only for builds with cmake; Visual Studio does not. need this flag.
| 1. **No Acceleration Support**: User-defined wind fields do not currently support | ||
| acceleration calculations needed by some modules (e.g., MHK turbines). | ||
|
|
||
| 2. **No Persistence**: Data must be recalculated if simulation is restarted. |
There was a problem hiding this comment.
Maybe better to say "no persistence between simulations"
This PR is ready to be merged
Feature or improvement description
This PR adds documentation for how to use the user-defined wind option in InflowWind. Comments and examples were added to the code and a section was added to the ReadTheDocs describing the process.
Related issue, if one exists
Impacted areas of the software
InflowWind
modules/inflowwindand documentationdocsAdditional supporting information
This PR was mostly generated by Claude Sonnet 4.5 and then reviewed and edited.
Generative AI usage
Co-authored-by: Anthropic Claude claude@anthropic.com