Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
79e942e
pyOpenFAST: minor changes to interface_abc
andrew-platt Sep 17, 2025
92ca19d
adi c-bind: move vtk from _Init to _PreInit
andrew-platt Sep 17, 2025
f2676cb
ifw c-bind: update interface mem handling, new routines
andrew-platt Sep 23, 2025
a55c8bc
adi c-bind: add pointer get/set FlowField
andrew-platt Sep 23, 2025
4513b10
ADI c-bind: remove redundant loop on setting rotor motion
andrew-platt Oct 8, 2025
82104bd
ADI c-bind: allow setting external IfW instance with pointer to data …
andrew-platt Nov 12, 2025
7de30a2
adi: fix bug in previous commit
andrew-platt Nov 12, 2025
c650b7a
ifw c-bind: comment out IfW_C_SetFlowFieldPointer
andrew-platt Nov 12, 2025
8c9c297
ifw c-bind: reneable set pointer routine
andrew-platt Nov 13, 2025
9928df3
ss c-bind: add routine to get min/max elev
andrew-platt Nov 13, 2025
ea0e9d3
Merge remote-tracking branch 'origin/wavetank3' into f/c-bind_updates…
andrew-platt Nov 19, 2025
e275c5e
ADI c-bind: bug introduced in merge
andrew-platt Nov 19, 2025
ea016c5
nwtc_c_bind: specify integer type on errstat
andrew-platt Nov 19, 2025
33f1c4d
ss c-bind: initialize returned vals from init (in case of early abort)
andrew-platt Nov 19, 2025
91a0110
SS c-bind: add routines to get env vars
andrew-platt Nov 20, 2025
054d478
HD c-bind: correct error message about multiple nodes
andrew-platt Nov 20, 2025
d527a23
Merge remote-tracking branch 'origin/wavetank3' into f/c-bind_updates…
andrew-platt Nov 21, 2025
cc473e8
TempQuickFix: disable wavetank since it won't compile
andrew-platt Nov 21, 2025
380a6e9
Merge remote-tracking branch 'origin/wavetank3' into f/c-bind_updates…
andrew-platt Dec 31, 2025
2c2a46e
Merge remote-tracking branch 'OpenFAST/dev' into f/c-bind_updates_ADI
andrew-platt Dec 31, 2025
4518300
update wavetank interface after merge
andrew-platt Jan 6, 2026
a709863
Initial plan
Copilot Jan 6, 2026
e345faf
Fix get_elevMinMax function signature and docstring
Copilot Jan 6, 2026
5945d8f
Merge pull request #28 from andrew-platt/copilot/sub-pr-27
andrew-platt Jan 6, 2026
c3caf84
update py_SS reg test after merge
andrew-platt Jan 6, 2026
d4627d5
HD c-bind: add clarifying comment after co-pilot confusion
andrew-platt Jan 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions glue-codes/labview/src/WaveTank.f90
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,13 @@ subroutine WaveTank_Init( &
SimSettings%Env%WtrDpth, &
SimSettings%Env%MSL2SWL, &
SimSettings%Sim%MHK, &
0_c_int, & ! externFlowfield_in
WrVTK_Dir_C, & ! vtk directory to use
SimSettings%Viz%WrVTK, & ! VTK visualization data output: (switch) {0=none; 1=initialization data only; 2=animation; 3=mode shapes}
SimSettings%Viz%WrVTK_Type, & ! Type of VTK visualization data: (switch) {1=surfaces; 2=basic meshes (lines/points); 3=all meshes (debug)} [unused if WrVTK=0]
SimSettings%Viz%WrVTK_DT, & ! timestep of VTK writing
SimSettings%Viz%VTKNacDim, & ! Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m)
SimSettings%TrbCfg%HubRad, & ! Hub radius for VTK surface rendering
DebugLevelMod, &
ErrStat_C2, ErrMsg_C2 &
)
Expand Down Expand Up @@ -351,16 +358,10 @@ subroutine WaveTank_Init( &
c_loc(IfW_InputFile_C(1)), & ! IfWinputFileString_C; Input file as a single string with lines delineated by C_NULL_CHAR
IntfStrLen, & ! IfWinputFileStringLength_C; length of the input file string
OutRootName_C, & ! Root name to use for echo files and other
WrVTK_Dir_C, & ! vtk directory to use
SimSettings%Sim%InterpOrd, & ! interpolation order for extrap/interp
SimSettings%Sim%DT, & ! DT for simulation (used in checks only)
SimSettings%Sim%TMax, & ! Max time for simulation (not used here)
0_c_int, & ! storeHHVel - Store hub height time series from IfW -- set to false since not used here
SimSettings%Viz%WrVTK, & ! VTK visualization data output: (switch) {0=none; 1=initialization data only; 2=animation; 3=mode shapes}
SimSettings%Viz%WrVTK_Type, & ! Type of VTK visualization data: (switch) {1=surfaces; 2=basic meshes (lines/points); 3=all meshes (debug)} [unused if WrVTK=0]
SimSettings%Viz%WrVTK_DT, & ! timestep of VTK writing
SimSettings%Viz%VTKNacDim, & ! Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m)
SimSettings%TrbCfg%HubRad, & ! Hub radius for VTK surface rendering
1_c_int, & ! wrOuts_C -- Write ADI output file -- hard code to true for now
SimSettings%Sim%DT, & ! Timestep to write output file from ADI
ADI_NumChannels_C, ADI_WriteOutputHdr_C, ADI_WriteOutputUnt_C, &
Expand Down
109 changes: 46 additions & 63 deletions glue-codes/python/pyOpenFAST/aerodyn_inflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
import numpy as np
import numpy.typing as npt

from .interface_abc import OpenFASTInterfaceType

#-------------------------------------------------------------------------------
# Helper functions and classes
#-------------------------------------------------------------------------------
Expand Down Expand Up @@ -144,36 +146,14 @@ class MotionData:
#-------------------------------------------------------------------------------
# C-interface library class for AeroDyn x InflowWind
#-------------------------------------------------------------------------------
class AeroDynInflowLib(CDLL):
class AeroDynInflowLib(OpenFASTInterfaceType):
"""A Python interface to the AeroDyn/InflowWind library.

This class provides a modern Python interface for calling and running AeroDyn
and InflowWind together. It handles initialization, runtime operations, and cleanup
of the underlying Fortran library.
"""

#--------------------------------------
# Error levels (from IfW)
#--------------------------------------
error_levels: Dict[int, str] = {
0: "None",
1: "Info",
2: "Warning",
3: "Severe Error",
4: "Fatal Error"
}

#--------------------------------------
# Constants
#--------------------------------------
# NOTE: The length of the error message in Fortran is determined by the
# ErrMsgLen variable in the NWTC_Base.f90 file. If ErrMsgLen is modified,
# the corresponding size here must also be updated to match.
ERROR_MESSAGE_LENGTH: int = 8197
DEFAULT_STRING_LENGTH: int = 1025
CHANNEL_NAME_LENGTH: int = 20
MAX_CHANNELS: int = 8000

def __init__(self, library_path: Union[str, Path]) -> None:
"""Initializes the AeroDyn/InflowWind interface.

Expand All @@ -199,11 +179,6 @@ def __init__(self, library_path: Union[str, Path]) -> None:
self.aerodyn_inputs_passed_as_string: bool = True # Pass input file as string
self.inflow_inputs_passed_as_string: bool = True # Pass input file as string

# Error handling setup
self.abort_error_level = 4
self.error_status_c = c_int(0)
self.error_message_c = create_string_buffer(self.ERROR_MESSAGE_LENGTH)

# Channel information buffers
self._channel_names_c = create_string_buffer(
self.CHANNEL_NAME_LENGTH * self.MAX_CHANNELS
Expand All @@ -223,6 +198,10 @@ def __init__(self, library_path: Union[str, Path]) -> None:
# MHK flag: 0->not MHK, 1->fixed bottom, 2->floating
self.mhk = 0

# External IfW data: 0->internal, 1->external IfW instance
# NOTE: if external, must call set pointer routine
self.externIfW = 0

# 0->None, 1->Info, 2->Warning, 3->Severe Error, 4->Fatal Error
self.debug_level = 0

Expand Down Expand Up @@ -316,7 +295,7 @@ def check_error(self) -> None:
message = f"AeroDyn/InflowWind {error_level}: {error_msg}"

# If the error level is fatal, call adi_end() and raise an error
if self.error_status_c.value >= self.abort_error_level:
if self.error_status_c.value >= self.abort_error_level.value:
try:
self.adi_end()
except Exception as e:
Expand All @@ -333,22 +312,37 @@ def adi_preinit(self) -> None:
Raises:
RuntimeError: If pre-initialization fails
"""
# Prepare output file paths
vtk_output_dir_c = create_string_buffer(
self.output_vtk_dir.ljust(self.default_str_c_len).encode('utf-8')
)

# Convert VTK nacelle dimensions to C array
vtk_nac_dimension_c = to_c_array(self.vtk_nacelle_dimension, c_float)

self.ADI_C_PreInit(
byref(c_int(self.num_turbines)), # IN -> number of turbines
byref(c_int(self.transpose_dcm)), # IN -> transpose_dcm flag (0=false, 1=true)
byref(c_int(self.point_load_output)), # IN -> point_load_output flag (0=false, 1=true)
byref(c_float(self.gravity)), # IN -> gravity
byref(c_float(self.fluid_density)), # IN -> fluid density
byref(c_float(self.kinematic_viscosity)), # IN -> kinematic viscosity
byref(c_float(self.sound_speed)), # IN -> speed of sound
byref(c_float(self.atmospheric_pressure)), # IN -> atmospheric pressure
byref(c_float(self.vapor_pressure)), # IN -> vapor pressure
byref(c_float(self.water_depth)), # IN -> water depth
byref(c_float(self.mean_sea_level_offset)), # IN -> MSL to SWL offset
byref(c_int(self.mhk)), # IN -> mhk flag (0=not MHK, 1=fixed bottom, 2=floating)
byref(c_int(self.debug_level)), # IN -> debug level (0=None to 4=all meshes)
byref(self.error_status_c), # OUT <- error status code
self.error_message_c # OUT <- error message buffer
byref(c_int(self.num_turbines)), # IN -> number of turbines
byref(c_int(self.transpose_dcm)), # IN -> transpose_dcm flag (0=false, 1=true)
byref(c_int(self.point_load_output)), # IN -> point_load_output flag (0=false, 1=true)
byref(c_float(self.gravity)), # IN -> gravity
byref(c_float(self.fluid_density)), # IN -> fluid density
byref(c_float(self.kinematic_viscosity)), # IN -> kinematic viscosity
byref(c_float(self.sound_speed)), # IN -> speed of sound
byref(c_float(self.atmospheric_pressure)), # IN -> atmospheric pressure
byref(c_float(self.vapor_pressure)), # IN -> vapor pressure
byref(c_float(self.water_depth)), # IN -> water depth
byref(c_float(self.mean_sea_level_offset)), # IN -> MSL to SWL offset
byref(c_int(self.mhk)), # IN -> mhk flag (0=not MHK, 1=fixed bottom, 2=floating)
byref(c_int(self.externIfW)), # IN -> external IfW instance (0=internal IfW, 1=external IfW with pointer to data (setpointer call required))
vtk_output_dir_c, # IN -> directory for vtk output files
byref(c_int(self.write_vtk)), # IN -> write VTK flag
byref(c_int(self.vtk_type)), # IN -> VTK write type
byref(c_double(self.vtk_dt)), # IN -> VTK output time step
vtk_nac_dimension_c, # IN -> VTK nacelle dimensions
byref(c_float(self.vtk_hub_radius)), # IN -> VTK hub radius
byref(c_int(self.debug_level)), # IN -> debug level (0=None to 4=all meshes)
byref(self.error_status_c), # OUT <- error status code
self.error_message_c # OUT <- error message buffer
)
self.check_error()

Expand Down Expand Up @@ -423,14 +417,8 @@ def adi_init(

# Prepare output file paths
output_file_root_name_c = create_string_buffer(
self.output_root_name.ljust(self.DEFAULT_STRING_LENGTH).encode('utf-8')
self.output_root_name.ljust(self.default_str_c_len).encode('utf-8')
)
vtk_output_dir_c = create_string_buffer(
self.output_vtk_dir.ljust(self.DEFAULT_STRING_LENGTH).encode('utf-8')
)

# Convert VTK nacelle dimensions to C array
vtk_nac_dimension_c = to_c_array(self.vtk_nacelle_dimension, c_float)

self.ADI_C_Init(
byref(c_int(self.aerodyn_inputs_passed_as_string)), # IN -> AD input file is passed as string
Expand All @@ -440,16 +428,10 @@ def adi_init(
c_char_p(ifw_input_string), # IN -> IfW input file as string
byref(c_int(ifw_input_string_length)), # IN -> IfW input file string length
output_file_root_name_c, # IN -> rootname for ADI file writing
vtk_output_dir_c, # IN -> directory for vtk output files
byref(c_int(self.interpolation_order)), # IN -> interpolation order (1: linear, 2: quadratic)
byref(c_double(self.dt)), # IN -> time step
byref(c_double(self.t_max)), # IN -> maximum simulation time
byref(c_int(self.store_hub_height_velocity)), # IN -> store hub height velocity flag
byref(c_int(self.write_vtk)), # IN -> write VTK flag
byref(c_int(self.vtk_type)), # IN -> VTK write type
byref(c_double(self.vtk_dt)), # IN -> VTK output time step
vtk_nac_dimension_c, # IN -> VTK nacelle dimensions
byref(c_float(self.vtk_hub_radius)), # IN -> VTK hub radius
byref(c_int(self.write_outputs)), # IN -> write outputs flag
byref(c_double(self.output_timestep)), # IN -> output time step
byref(self._num_channels_c), # OUT <- number of channels
Expand Down Expand Up @@ -708,6 +690,13 @@ def _initialize_routines(self) -> None:
POINTER(c_float), # WtrDpth
POINTER(c_float), # MSL2SWL
POINTER(c_int), # MHK
POINTER(c_int), # externIfW
POINTER(c_char), # OutVTKdir
POINTER(c_int), # WrVTK
POINTER(c_int), # WrVTK_Type
POINTER(c_double), # WrVTK_DT -- 0 or negative to do every step
POINTER(c_float), # VTKNacDim
POINTER(c_float), # VTKHubRad
POINTER(c_int), # debuglevel
POINTER(c_int), # ErrStat_C
POINTER(c_char) # ErrMsg_C
Expand Down Expand Up @@ -748,16 +737,10 @@ def _initialize_routines(self) -> None:
POINTER(c_char_p), # IfW input file as string
POINTER(c_int), # IfW input file string length
POINTER(c_char), # OutRootName
POINTER(c_char), # OutVTKdir
POINTER(c_int), # InterpOrder
POINTER(c_double), # dt
POINTER(c_double), # tmax
POINTER(c_int), # storeHHVel
POINTER(c_int), # WrVTK
POINTER(c_int), # WrVTK_Type
POINTER(c_double), # WrVTK_DT -- 0 or negative to do every step
POINTER(c_float), # VTKNacDim
POINTER(c_float), # VTKHubRad
POINTER(c_int), # wrOuts -- file format for writing outputs
POINTER(c_double), # DT_Outs -- timestep for outputs to file
POINTER(c_int), # number of channels
Expand Down
27 changes: 3 additions & 24 deletions glue-codes/python/pyOpenFAST/hydrodyn.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,25 +76,9 @@
import numpy as np
import datetime

class HydroDynLib(CDLL):
# Human readable error levels from IfW.
error_levels = {
0: "None",
1: "Info",
2: "Warning",
3: "Severe Error",
4: "Fatal Error"
}

# NOTE: the error message length in Fortran is controlled by the
# ErrMsgLen variable in the NWTC_Base.f90 file. If that ever
# changes, it may be necessary to update the corresponding size
# here.
error_msg_c_len = 1025

# NOTE: the length of the name used for any output file written by the
# HD Fortran code is 1025.
default_str_c_len = 1025
from .interface_abc import OpenFASTInterfaceType

class HydroDynLib(OpenFASTInterfaceType):

def __init__(self, library_path):
super().__init__(library_path)
Expand All @@ -107,11 +91,6 @@ def __init__(self, library_path):
self.seastate_inputs_passed_as_string: bool = True # Pass input file as string
self.hydrodyn_inputs_passed_as_string: bool = True # Pass input file as string

# Create buffers for class data
self.abort_error_level = 4
self.error_status_c = c_int(0)
self.error_message_c = create_string_buffer(self.error_msg_c_len)

# This is not sufficient for HD
#FIXME: ChanLen may not always be 20 -- could be as much as 256
# Possible fix is to pass this length over to Fortran side.
Expand Down
24 changes: 3 additions & 21 deletions glue-codes/python/pyOpenFAST/inflowwind.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,9 @@
import datetime
import os

from .interface_abc import OpenFASTInterfaceType

class InflowWindLib(CDLL):
# Human readable error levels from IfW.
error_levels = {
0: "None",
1: "Info",
2: "Warning",
3: "Severe Error",
4: "Fatal Error"
}

# NOTE: the error message length in Fortran is controlled by the
# ErrMsgLen variable in the NWTC_Base.f90 file. If that ever
# changes, it may be necessary to update the corresponding size
# here.
error_msg_c_len = 1025

# NOTE: the length of the name used for any output file written by the
# IfW Fortran code is 1025.
default_str_c_len = 1025

class InflowWindLib(OpenFASTInterfaceType):
def __init__(self, library_path):
super().__init__(library_path)
self.library_path = library_path
Expand All @@ -69,7 +51,7 @@ def __init__(self, library_path):
# Create buffers for class data
self.abort_error_level = 4
self.error_status_c = c_int(0)
self.error_message_c = create_string_buffer(self.error_msg_c_len)
self.error_message_c = create_string_buffer(self.ERROR_MESSAGE_LENGTH)

# This buffer for the channel names and units is set arbitrarily large
# to start. InflowWind only has a maximum of 9 outputs at present, but
Expand Down
16 changes: 16 additions & 0 deletions glue-codes/python/pyOpenFAST/interface_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@

class OpenFASTInterfaceType(CDLL):

#--------------------------------------
# Constants
#--------------------------------------
# NOTE: The length of the error message in Fortran is determined by the
# ErrMsgLen variable in the NWTC_Base.f90 file. If ErrMsgLen is modified,
# the corresponding size here must also be updated to match.
ERROR_MESSAGE_LENGTH: int = 8197
DEFAULT_STRING_LENGTH: int = 1025
CHANNEL_NAME_LENGTH: int = 20
MAX_CHANNELS: int = 8000

# Human readable error levels
error_levels = {
0: "None",
Expand All @@ -39,7 +50,12 @@ class OpenFASTInterfaceType(CDLL):
# Fortran code is 1025.
default_str_c_len = 1025

# error handling
abort_error_level = c_int(4)
error_status_c = c_int(0)
error_message_c = create_string_buffer(ERROR_MESSAGE_LENGTH)



def __init__(self, library_path: str):
super().__init__(library_path)
Expand Down
5 changes: 0 additions & 5 deletions glue-codes/python/pyOpenFAST/moordyn.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,6 @@ def __init__(self, library_path):
super().__init__(library_path)

self._initialize_routines()

# Create buffers for class data
self.error_status_c = c_int(0)
self.error_message_c = create_string_buffer(self.ERROR_MSG_C_LEN)
self.error_message = create_string_buffer(1025)
self.ended = False # For error handling at end

self._channel_names = create_string_buffer(256*1000)
Expand Down
Loading