Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update InflowWind_c_bindings interface #2582

Merged
merged 3 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 31 additions & 15 deletions modules/inflowwind/python-lib/inflowwind_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,20 @@ class InflowWindLib(CDLL):
# 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

def __init__(self, library_path):
super().__init__(library_path)
self.library_path = library_path

self._initialize_routines()
self.ended = False # For error handling at end

# Input file handling
self.IfWinputPass = 1 # Assume passing of input file as a string

# Create buffers for class data
self.abort_error_level = 4
self.error_status_c = c_int(0)
Expand All @@ -84,18 +91,27 @@ def __init__(self, library_path):

self.numChannels = 0 # Number of channels returned

# flags
self.debuglevel = 0 # 0-4 levels

# OutRootName
# If HD writes a file (echo, summary, or other), use this for the
# root of the file name.
self.outRootName = "Output_ifwlib_default"


def _initialize_routines(self):
"""
Initialize the Python handles to necessary routines in the InflowWind library.
"""
self.IfW_C_Init.argtypes = [
POINTER(c_int), # IfW input file passed as string
POINTER(c_char_p), # input file string
POINTER(c_int), # input file string length
POINTER(c_char_p), # uniform file string
POINTER(c_int), # uniform file string length
POINTER(c_char), # OutRootName
POINTER(c_int), # numWindPts
POINTER(c_double), # dt
POINTER(c_int), # debuglevel
POINTER(c_int), # number of channels
POINTER(c_char), # output channel names
POINTER(c_char), # output channel units
Expand All @@ -121,29 +137,29 @@ def _initialize_routines(self):
self.IfW_C_End.restype = c_int


def ifw_init(self, input_string_array, uniform_string_array):
def ifw_init(self, IfW_input_string_array):
"""
Call the initialization routine in the InflowWind library.
"""

# Set up inputs: Pass single NULL joined string
input_string = '\x00'.join(input_string_array)
input_string = input_string.encode('utf-8')
input_string_length = len(input_string)

uniform_string = '\x00'.join(uniform_string_array)
uniform_string = uniform_string.encode('utf-8')
uniform_string_length = len(uniform_string)

IfW_input_string = '\x00'.join(IfW_input_string_array)
IfW_input_string = IfW_input_string.encode('utf-8')
IfW_input_string_length = len(IfW_input_string)

# Rootname for ADI output files (echo etc).
_outRootName_c = create_string_buffer((self.outRootName.ljust(self.default_str_c_len)).encode('utf-8'))

self._numChannels_c = c_int(0)

self.IfW_C_Init(
c_char_p(input_string), # IN: input file string
byref(c_int(input_string_length)), # IN: input file string length
c_char_p(uniform_string), # IN: uniform file string
byref(c_int(uniform_string_length)), # IN: uniform file string length
byref(c_int(self.IfWinputPass)), # IN: IfW input file is passed
c_char_p(IfW_input_string), # IN: input file string
byref(c_int(IfW_input_string_length)), # IN: input file string length
_outRootName_c, # IN: rootname for ADI file writing
byref(c_int(self.numWindPts)), # IN: number of wind points
byref(c_double(self.dt)), # IN: time step (dt)
byref(c_int(self.debuglevel)), # IN: debuglevel
byref(self._numChannels_c), # OUT: number of channels
self._channel_names_c, # OUT: output channel names as c_char
self._channel_units_c, # OUT: output channel units as c_char
Expand Down
194 changes: 137 additions & 57 deletions modules/inflowwind/src/IfW_C_Binding.f90
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,39 @@ MODULE InflowWind_C_BINDING
PUBLIC :: IfW_C_CalcOutput
PUBLIC :: IfW_C_End

!------------------------------------------------------------------------------------
! Version info for display
type(ProgDesc), parameter :: version = ProgDesc( 'InflowWind library', '', '' )

! Accessible to all routines inside module
TYPE(InflowWind_InputType) , SAVE :: InputData !< Inputs to InflowWind
TYPE(InflowWind_InitInputType) , SAVE :: InitInp
TYPE(InflowWind_InitOutputType) , SAVE :: InitOutData !< Initial output data -- Names, units, and version info.
TYPE(InflowWind_ParameterType) , SAVE :: p !< Parameters
TYPE(InflowWind_ContinuousStateType) , SAVE :: ContStates !< Initial continuous states
TYPE(InflowWind_DiscreteStateType) , SAVE :: DiscStates !< Initial discrete states
TYPE(InflowWind_ConstraintStateType) , SAVE :: ConstrStates !< Constraint states at Time
TYPE(InflowWind_OtherStateType) , SAVE :: OtherStates !< Initial other/optimization states
TYPE(InflowWind_OutputType) , SAVE :: y !< Initial output (outputs are not calculated; only the output mesh is initialized)
TYPE(InflowWind_MiscVarType) , SAVE :: m !< Misc variables for optimization (not copied in glue code)

! This must exactly match the value in the Python interface. We are not using the variable 'ErrMsgLen'
! so that we avoid issues if ErrMsgLen changes in the NWTC Library. If the value of ErrMsgLen does change
! in the NWTC Library, ErrMsgLen_C (and the equivalent value in the Python interface) can be updated
! to be equivalent to ErrMsgLen + 1, but the logic exists to correctly handle different lengths of the strings
integer(IntKi), parameter :: ErrMsgLen_C=1025 ! Numerical equivalent of ErrMsgLen + 1
!------------------------------------------------------------------------------------
! Debugging: DebugLevel -- passed at PreInit
! 0 - none
! 1 - some summary info
! 2 - above + all position/orientation info
! 3 - above + input files (if direct passed)
! 4 - above + meshes
integer(IntKi) :: DebugLevel = 0

!------------------------------------------------------------------------------------
! Primary IfW data derived types
type(InflowWind_InputType) :: InputData !< Inputs to InflowWind
type(InflowWind_InitInputType) :: InitInp
type(InflowWind_InitOutputType) :: InitOutData !< Initial output data -- Names, units, and version info.
type(InflowWind_ParameterType) :: p !< Parameters
type(InflowWind_ContinuousStateType) :: ContStates !< Initial continuous states
type(InflowWind_DiscreteStateType) :: DiscStates !< Initial discrete states
type(InflowWind_ConstraintStateType) :: ConstrStates !< Constraint states at Time
type(InflowWind_OtherStateType) :: OtherStates !< Initial other/optimization states
type(InflowWind_OutputType) :: y !< Initial output (outputs are not calculated; only the output mesh is initialized)
type(InflowWind_MiscVarType) :: m !< Misc variables for optimization (not copied in glue code)

!------------------------------------------------------------------------------------
! Error handling
! This must exactly match the value in the python-lib. If ErrMsgLen changes at
! some point in the nwtc-library, this should be updated, but the logic exists
! to correctly handle different lengths of the strings
integer(IntKi), parameter :: ErrMsgLen_C = 1025
integer(IntKi), parameter :: IntfStrLen = 1025 ! length of other strings through the C interface



Expand All @@ -78,35 +91,39 @@ end subroutine SetErr
!===============================================================================================================
!--------------------------------------------- IFW INIT --------------------------------------------------------
!===============================================================================================================
SUBROUTINE IfW_C_Init(InputFileString_C, InputFileStringLength_C, InputUniformString_C, InputUniformStringLength_C, NumWindPts_C, DT_C, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='IfW_C_Init')
SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStringLength_C, OutRootName_C, &
NumWindPts_C, DT_C, DebugLevel_in, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, &
ErrStat_C, ErrMsg_C) BIND (C, NAME='IfW_C_Init')
IMPLICIT NONE
#ifndef IMPLICIT_DLLEXPORT
!DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_Init
!GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_Init
#endif
TYPE(C_PTR) , INTENT(IN ) :: InputFileString_C
INTEGER(C_INT) , INTENT(IN ) :: InputFileStringLength_C
TYPE(C_PTR) , INTENT(IN ) :: InputUniformString_C
INTEGER(C_INT) , INTENT(IN ) :: InputUniformStringLength_C
INTEGER(C_INT) , INTENT(IN ) :: NumWindPts_C
REAL(C_DOUBLE) , INTENT(IN ) :: DT_C
INTEGER(C_INT) , INTENT( OUT) :: NumChannels_C
CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelNames_C(ChanLen*MaxOutPts+1)
CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelUnits_C(ChanLen*MaxOutPts+1)
INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C
CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C)

! Local Variables
CHARACTER(kind=C_char, len=InputFileStringLength_C), POINTER :: InputFileString !< Input file as a single string with NULL chracter separating lines
CHARACTER(kind=C_char, len=InputUniformStringLength_C), POINTER :: UniformFileString !< Input file as a single string with NULL chracter separating lines -- Uniform wind file

REAL(DbKi) :: TimeInterval
INTEGER :: ErrStat !< aggregated error message
CHARACTER(ErrMsgLen) :: ErrMsg !< aggregated error message
INTEGER :: ErrStat2 !< temporary error status from a call
CHARACTER(ErrMsgLen) :: ErrMsg2 !< temporary error message from a call
INTEGER :: i,j,k
character(*), parameter :: RoutineName = 'IfW_C_Init' !< for error handling
integer(c_int), intent(in ) :: IfWinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation]
type(c_ptr), intent(in ) :: IfWinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR
integer(c_int), intent(in ) :: IfWinputFileStringLength_C !< lenght of the input file string
character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) !< Root name to use for echo files and other
integer(c_int), intent(in ) :: NumWindPts_C
real(c_double), intent(in ) :: DT_C
integer(c_int), intent(in ) :: DebugLevel_in
integer(c_int), intent( out) :: NumChannels_C
character(kind=c_char), intent( out) :: OutputChannelNames_C(ChanLen*MaxOutPts+1)
character(kind=c_char), intent( out) :: OutputChannelUnits_C(ChanLen*MaxOutPts+1)
integer(c_int), intent( out) :: ErrStat_C
character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C)

! local variables
character(IntfStrLen) :: OutRootName !< Root name to use for echo files and other
character(IntfStrLen) :: TmpFileName !< Temporary file name if not passing AD or IfW input file contents directly
character(kind=c_char, len=IfWinputFileStringLength_C), pointer :: IfWinputFileString !< Input file as a single string with NULL chracter separating lines

real(DbKi) :: TimeInterval
integer :: ErrStat !< aggregated error message
character(ErrMsgLen) :: ErrMsg !< aggregated error message
integer :: ErrStat2 !< temporary error status from a call
character(ErrMsgLen) :: ErrMsg2 !< temporary error message from a call
integer :: i,j,k
character(*), parameter :: RoutineName = 'IfW_C_Init' !< for error handling

! Initialize error handling
ErrStat = ErrID_None
Expand All @@ -116,26 +133,65 @@ SUBROUTINE IfW_C_Init(InputFileString_C, InputFileStringLength_C, InputUniformSt
CALL DispCopyrightLicense( version%Name )
CALL DispCompileRuntimeInfo( version%Name )


! interface debugging
DebugLevel = int(DebugLevel_in,IntKi)

! Input files
OutRootName = TRANSFER( OutRootName_C, OutRootName )
i = INDEX(OutRootName,C_NULL_CHAR) - 1 ! if this has a c null character at the end...
if ( i > 0 ) OutRootName = OutRootName(1:I) ! remove it

! if non-zero, show all passed data here. Then check valid values
if (DebugLevel /= 0_IntKi) then
call WrScr(" Interface debugging level "//trim(Num2Lstr(DebugLevel))//" requested.")
call ShowPassedData()
endif
! check valid debug level
if (DebugLevel < 0_IntKi) then
ErrStat2 = ErrID_Fatal
ErrMsg2 = "Interface debug level must be 0 or greater"//NewLine// &
" 0 - none"//NewLine// &
" 1 - some summary info and variables passed through interface"//NewLine// &
" 2 - above + all position/orientation info"//NewLine// &
" 3 - above + input files (if direct passed)"//NewLine// &
" 4 - above + meshes"
if (Failed()) return;
endif

! For debugging the interface:
if (DebugLevel > 0) then
call ShowPassedData()
endif

! Get fortran pointer to C_NULL_CHAR deliniated input file as a string
CALL C_F_pointer(InputFileString_C, InputFileString)
CALL C_F_pointer(InputUniformString_C, UniformFileString)

! Store string-inputs as type FileInfoType within InflowWind_InitInputType
CALL InitFileInfo(InputFileString, InitInp%PassedFileInfo, ErrStat2, ErrMsg2); if (Failed()) return
InitInp%FilePassingMethod = 1_IntKi ! read file and pass as FileInfoType structure

! store Uniform File strings if they are non-zero sized
if (len(UniformFileString) > 1) then
CALL InitFileInfo(UniformFileString, InitInp%WindType2Info, ErrStat2, ErrMsg2); if (Failed()) return
InitInp%WindType2UseInputFile = .FALSE.
else ! Default to reading from disk
InitInp%WindType2UseInputFile = .TRUE.
CALL C_F_pointer(IfWinputFileString_C, IfWinputFileString)

! Format IfW input file contents
if (IfWinputFilePassed==1_c_int) then
InitInp%FilePassingMethod = 1_IntKi ! Don't try to read an input -- use passed data instead (blades and AF tables not passed) using FileInfoType
InitInp%InputFileName = "passed_ifw_file" ! not actually used
call InitFileInfo(IfWinputFileString, InitInp%PassedFileInfo, ErrStat2, ErrMsg2); if (Failed()) return
else
InitInp%FilePassingMethod = 0_IntKi ! Read input info from a primary input file
i = min(IntfStrLen,IfWinputFileStringLength_C)
TmpFileName = ''
TmpFileName(1:i) = IfWinputFileString(1:i)
i = INDEX(TmpFileName,C_NULL_CHAR) - 1 ! if this has a c null character at the end...
if ( i > 0 ) TmpFileName = TmpFileName(1:I) ! remove it
InitInp%InputFileName = TmpFileName
endif

! For diagnostic purposes, the following can be used to display the contents
! of the InFileInfo data structure.
! CU is the screen -- system dependent.
if (DebugLevel >= 3) then
if (IfWinputFilePassed==1_c_int) call Print_FileInfo_Struct( CU, InitInp%PassedFileInfo )
endif

! Set other inputs for calling InflowWind_Init
InitInp%NumWindPoints = NumWindPts_C
InitInp%InputFileName = "passed_ifw_file" ! dummy
InitInp%RootName = "ifwRoot" ! used for making echo files
InitInp%NumWindPoints = int(NumWindPts_C, IntKi)
InitInp%RootName = OutRootName ! used for making echo files
TimeInterval = REAL(DT_C, DbKi)

! Call the main subroutine InflowWind_Init - only need InitInp and TimeInterval as inputs, the rest are set by InflowWind_Init
Expand Down Expand Up @@ -174,6 +230,30 @@ logical function Failed()
end function Failed
subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here
end subroutine Cleanup

!> This subroutine prints out all the variables that are passed in. Use this only
!! for debugging the interface on the Fortran side.
subroutine ShowPassedData()
character(1) :: TmpFlag
integer :: i,j
call WrSCr("")
call WrScr("-----------------------------------------------------------")
call WrScr("Interface debugging: Variables passed in through interface")
call WrScr(" ADI_C_Init")
call WrScr(" --------------------------------------------------------")
call WrScr(" FileInfo")
TmpFlag="F"; if (IfWinputFilePassed==1_c_int) TmpFlag="T"
call WrScr(" IfWinputFilePassed_C "//TmpFlag )
call WrScr(" IfWinputFileString_C (ptr addr)"//trim(Num2LStr(LOC(IfWinputFileString_C))) )
call WrScr(" IfWinputFileStringLength_C "//trim(Num2LStr( IfWinputFileStringLength_C )) )
call WrScr(" OutRootName "//trim(OutRootName) )
call WrScr(" Input variables")
call WrScr(" NumWindPts_C "//trim(Num2LStr( NumWindPts_C)) )
call WrScr(" Time variables")
call WrScr(" DT_C "//trim(Num2LStr( DT_C )) )
call WrScr("-----------------------------------------------------------")
end subroutine ShowPassedData

END SUBROUTINE IfW_C_Init

!===============================================================================================================
Expand Down
Loading