Skip to content

Commit eab6ed1

Browse files
committed
IfW_c: update interface
- Add debuglevel (only partially completed) - update regression test - remove the passing of the uniform wind file (might be able to remove some stuff from IfW) - other updates to more closely match structure of ADI_c - some linting
1 parent 40202bb commit eab6ed1

File tree

4 files changed

+170
-74
lines changed

4 files changed

+170
-74
lines changed

modules/inflowwind/python-lib/inflowwind_library.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,20 @@ class InflowWindLib(CDLL):
5252
# here.
5353
error_msg_c_len = 1025
5454

55+
# NOTE: the length of the name used for any output file written by the
56+
# IfW Fortran code is 1025.
57+
default_str_c_len = 1025
58+
5559
def __init__(self, library_path):
5660
super().__init__(library_path)
5761
self.library_path = library_path
5862

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

66+
# Input file handling
67+
self.IfWinputPass = 1 # Assume passing of input file as a string
68+
6269
# Create buffers for class data
6370
self.abort_error_level = 4
6471
self.error_status_c = c_int(0)
@@ -84,18 +91,27 @@ def __init__(self, library_path):
8491

8592
self.numChannels = 0 # Number of channels returned
8693

94+
# flags
95+
self.debuglevel = 0 # 0-4 levels
96+
97+
# OutRootName
98+
# If HD writes a file (echo, summary, or other), use this for the
99+
# root of the file name.
100+
self.outRootName = "Output_ifwlib_default"
101+
87102

88103
def _initialize_routines(self):
89104
"""
90105
Initialize the Python handles to necessary routines in the InflowWind library.
91106
"""
92107
self.IfW_C_Init.argtypes = [
108+
POINTER(c_int), # IfW input file passed as string
93109
POINTER(c_char_p), # input file string
94110
POINTER(c_int), # input file string length
95-
POINTER(c_char_p), # uniform file string
96-
POINTER(c_int), # uniform file string length
111+
POINTER(c_char), # OutRootName
97112
POINTER(c_int), # numWindPts
98113
POINTER(c_double), # dt
114+
POINTER(c_int), # debuglevel
99115
POINTER(c_int), # number of channels
100116
POINTER(c_char), # output channel names
101117
POINTER(c_char), # output channel units
@@ -121,29 +137,29 @@ def _initialize_routines(self):
121137
self.IfW_C_End.restype = c_int
122138

123139

124-
def ifw_init(self, input_string_array, uniform_string_array):
140+
def ifw_init(self, IfW_input_string_array):
125141
"""
126142
Call the initialization routine in the InflowWind library.
127143
"""
128144

129145
# Set up inputs: Pass single NULL joined string
130-
input_string = '\x00'.join(input_string_array)
131-
input_string = input_string.encode('utf-8')
132-
input_string_length = len(input_string)
133-
134-
uniform_string = '\x00'.join(uniform_string_array)
135-
uniform_string = uniform_string.encode('utf-8')
136-
uniform_string_length = len(uniform_string)
137-
146+
IfW_input_string = '\x00'.join(IfW_input_string_array)
147+
IfW_input_string = IfW_input_string.encode('utf-8')
148+
IfW_input_string_length = len(IfW_input_string)
149+
150+
# Rootname for ADI output files (echo etc).
151+
_outRootName_c = create_string_buffer((self.outRootName.ljust(self.default_str_c_len)).encode('utf-8'))
152+
138153
self._numChannels_c = c_int(0)
139154

140155
self.IfW_C_Init(
141-
c_char_p(input_string), # IN: input file string
142-
byref(c_int(input_string_length)), # IN: input file string length
143-
c_char_p(uniform_string), # IN: uniform file string
144-
byref(c_int(uniform_string_length)), # IN: uniform file string length
156+
byref(c_int(self.IfWinputPass)), # IN: IfW input file is passed
157+
c_char_p(IfW_input_string), # IN: input file string
158+
byref(c_int(IfW_input_string_length)), # IN: input file string length
159+
_outRootName_c, # IN: rootname for ADI file writing
145160
byref(c_int(self.numWindPts)), # IN: number of wind points
146161
byref(c_double(self.dt)), # IN: time step (dt)
162+
byref(c_int(self.debuglevel)), # IN: debuglevel
147163
byref(self._numChannels_c), # OUT: number of channels
148164
self._channel_names_c, # OUT: output channel names as c_char
149165
self._channel_units_c, # OUT: output channel units as c_char

modules/inflowwind/src/IfW_C_Binding.f90

Lines changed: 137 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,39 @@ MODULE InflowWind_C_BINDING
3232
PUBLIC :: IfW_C_CalcOutput
3333
PUBLIC :: IfW_C_End
3434

35+
!------------------------------------------------------------------------------------
3536
! Version info for display
3637
type(ProgDesc), parameter :: version = ProgDesc( 'InflowWind library', '', '' )
3738

38-
! Accessible to all routines inside module
39-
TYPE(InflowWind_InputType) , SAVE :: InputData !< Inputs to InflowWind
40-
TYPE(InflowWind_InitInputType) , SAVE :: InitInp
41-
TYPE(InflowWind_InitOutputType) , SAVE :: InitOutData !< Initial output data -- Names, units, and version info.
42-
TYPE(InflowWind_ParameterType) , SAVE :: p !< Parameters
43-
TYPE(InflowWind_ContinuousStateType) , SAVE :: ContStates !< Initial continuous states
44-
TYPE(InflowWind_DiscreteStateType) , SAVE :: DiscStates !< Initial discrete states
45-
TYPE(InflowWind_ConstraintStateType) , SAVE :: ConstrStates !< Constraint states at Time
46-
TYPE(InflowWind_OtherStateType) , SAVE :: OtherStates !< Initial other/optimization states
47-
TYPE(InflowWind_OutputType) , SAVE :: y !< Initial output (outputs are not calculated; only the output mesh is initialized)
48-
TYPE(InflowWind_MiscVarType) , SAVE :: m !< Misc variables for optimization (not copied in glue code)
49-
50-
! This must exactly match the value in the Python interface. We are not using the variable 'ErrMsgLen'
51-
! so that we avoid issues if ErrMsgLen changes in the NWTC Library. If the value of ErrMsgLen does change
52-
! in the NWTC Library, ErrMsgLen_C (and the equivalent value in the Python interface) can be updated
53-
! to be equivalent to ErrMsgLen + 1, but the logic exists to correctly handle different lengths of the strings
54-
integer(IntKi), parameter :: ErrMsgLen_C=1025 ! Numerical equivalent of ErrMsgLen + 1
39+
!------------------------------------------------------------------------------------
40+
! Debugging: DebugLevel -- passed at PreInit
41+
! 0 - none
42+
! 1 - some summary info
43+
! 2 - above + all position/orientation info
44+
! 3 - above + input files (if direct passed)
45+
! 4 - above + meshes
46+
integer(IntKi) :: DebugLevel = 0
47+
48+
!------------------------------------------------------------------------------------
49+
! Primary IfW data derived types
50+
type(InflowWind_InputType) :: InputData !< Inputs to InflowWind
51+
type(InflowWind_InitInputType) :: InitInp
52+
type(InflowWind_InitOutputType) :: InitOutData !< Initial output data -- Names, units, and version info.
53+
type(InflowWind_ParameterType) :: p !< Parameters
54+
type(InflowWind_ContinuousStateType) :: ContStates !< Initial continuous states
55+
type(InflowWind_DiscreteStateType) :: DiscStates !< Initial discrete states
56+
type(InflowWind_ConstraintStateType) :: ConstrStates !< Constraint states at Time
57+
type(InflowWind_OtherStateType) :: OtherStates !< Initial other/optimization states
58+
type(InflowWind_OutputType) :: y !< Initial output (outputs are not calculated; only the output mesh is initialized)
59+
type(InflowWind_MiscVarType) :: m !< Misc variables for optimization (not copied in glue code)
60+
61+
!------------------------------------------------------------------------------------
62+
! Error handling
63+
! This must exactly match the value in the python-lib. If ErrMsgLen changes at
64+
! some point in the nwtc-library, this should be updated, but the logic exists
65+
! to correctly handle different lengths of the strings
66+
integer(IntKi), parameter :: ErrMsgLen_C = 1025
67+
integer(IntKi), parameter :: IntfStrLen = 1025 ! length of other strings through the C interface
5568

5669

5770

@@ -78,35 +91,39 @@ end subroutine SetErr
7891
!===============================================================================================================
7992
!--------------------------------------------- IFW INIT --------------------------------------------------------
8093
!===============================================================================================================
81-
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')
94+
SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStringLength_C, OutRootName_C, &
95+
NumWindPts_C, DT_C, DebugLevel_in, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, &
96+
ErrStat_C, ErrMsg_C) BIND (C, NAME='IfW_C_Init')
8297
IMPLICIT NONE
8398
#ifndef IMPLICIT_DLLEXPORT
8499
!DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_Init
85100
!GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_Init
86101
#endif
87-
TYPE(C_PTR) , INTENT(IN ) :: InputFileString_C
88-
INTEGER(C_INT) , INTENT(IN ) :: InputFileStringLength_C
89-
TYPE(C_PTR) , INTENT(IN ) :: InputUniformString_C
90-
INTEGER(C_INT) , INTENT(IN ) :: InputUniformStringLength_C
91-
INTEGER(C_INT) , INTENT(IN ) :: NumWindPts_C
92-
REAL(C_DOUBLE) , INTENT(IN ) :: DT_C
93-
INTEGER(C_INT) , INTENT( OUT) :: NumChannels_C
94-
CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelNames_C(ChanLen*MaxOutPts+1)
95-
CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelUnits_C(ChanLen*MaxOutPts+1)
96-
INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C
97-
CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C)
98-
99-
! Local Variables
100-
CHARACTER(kind=C_char, len=InputFileStringLength_C), POINTER :: InputFileString !< Input file as a single string with NULL chracter separating lines
101-
CHARACTER(kind=C_char, len=InputUniformStringLength_C), POINTER :: UniformFileString !< Input file as a single string with NULL chracter separating lines -- Uniform wind file
102-
103-
REAL(DbKi) :: TimeInterval
104-
INTEGER :: ErrStat !< aggregated error message
105-
CHARACTER(ErrMsgLen) :: ErrMsg !< aggregated error message
106-
INTEGER :: ErrStat2 !< temporary error status from a call
107-
CHARACTER(ErrMsgLen) :: ErrMsg2 !< temporary error message from a call
108-
INTEGER :: i,j,k
109-
character(*), parameter :: RoutineName = 'IfW_C_Init' !< for error handling
102+
integer(c_int), intent(in ) :: IfWinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation]
103+
type(c_ptr), intent(in ) :: IfWinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR
104+
integer(c_int), intent(in ) :: IfWinputFileStringLength_C !< lenght of the input file string
105+
character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) !< Root name to use for echo files and other
106+
integer(c_int), intent(in ) :: NumWindPts_C
107+
real(c_double), intent(in ) :: DT_C
108+
integer(c_int), intent(in ) :: DebugLevel_in
109+
integer(c_int), intent( out) :: NumChannels_C
110+
character(kind=c_char), intent( out) :: OutputChannelNames_C(ChanLen*MaxOutPts+1)
111+
character(kind=c_char), intent( out) :: OutputChannelUnits_C(ChanLen*MaxOutPts+1)
112+
integer(c_int), intent( out) :: ErrStat_C
113+
character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C)
114+
115+
! local variables
116+
character(IntfStrLen) :: OutRootName !< Root name to use for echo files and other
117+
character(IntfStrLen) :: TmpFileName !< Temporary file name if not passing AD or IfW input file contents directly
118+
character(kind=c_char, len=IfWinputFileStringLength_C), pointer :: IfWinputFileString !< Input file as a single string with NULL chracter separating lines
119+
120+
real(DbKi) :: TimeInterval
121+
integer :: ErrStat !< aggregated error message
122+
character(ErrMsgLen) :: ErrMsg !< aggregated error message
123+
integer :: ErrStat2 !< temporary error status from a call
124+
character(ErrMsgLen) :: ErrMsg2 !< temporary error message from a call
125+
integer :: i,j,k
126+
character(*), parameter :: RoutineName = 'IfW_C_Init' !< for error handling
110127

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

136+
137+
! interface debugging
138+
DebugLevel = int(DebugLevel_in,IntKi)
139+
140+
! Input files
141+
OutRootName = TRANSFER( OutRootName_C, OutRootName )
142+
i = INDEX(OutRootName,C_NULL_CHAR) - 1 ! if this has a c null character at the end...
143+
if ( i > 0 ) OutRootName = OutRootName(1:I) ! remove it
144+
145+
! if non-zero, show all passed data here. Then check valid values
146+
if (DebugLevel /= 0_IntKi) then
147+
call WrScr(" Interface debugging level "//trim(Num2Lstr(DebugLevel))//" requested.")
148+
call ShowPassedData()
149+
endif
150+
! check valid debug level
151+
if (DebugLevel < 0_IntKi) then
152+
ErrStat2 = ErrID_Fatal
153+
ErrMsg2 = "Interface debug level must be 0 or greater"//NewLine// &
154+
" 0 - none"//NewLine// &
155+
" 1 - some summary info and variables passed through interface"//NewLine// &
156+
" 2 - above + all position/orientation info"//NewLine// &
157+
" 3 - above + input files (if direct passed)"//NewLine// &
158+
" 4 - above + meshes"
159+
if (Failed()) return;
160+
endif
161+
162+
! For debugging the interface:
163+
if (DebugLevel > 0) then
164+
call ShowPassedData()
165+
endif
166+
119167
! Get fortran pointer to C_NULL_CHAR deliniated input file as a string
120-
CALL C_F_pointer(InputFileString_C, InputFileString)
121-
CALL C_F_pointer(InputUniformString_C, UniformFileString)
122-
123-
! Store string-inputs as type FileInfoType within InflowWind_InitInputType
124-
CALL InitFileInfo(InputFileString, InitInp%PassedFileInfo, ErrStat2, ErrMsg2); if (Failed()) return
125-
InitInp%FilePassingMethod = 1_IntKi ! read file and pass as FileInfoType structure
126-
127-
! store Uniform File strings if they are non-zero sized
128-
if (len(UniformFileString) > 1) then
129-
CALL InitFileInfo(UniformFileString, InitInp%WindType2Info, ErrStat2, ErrMsg2); if (Failed()) return
130-
InitInp%WindType2UseInputFile = .FALSE.
131-
else ! Default to reading from disk
132-
InitInp%WindType2UseInputFile = .TRUE.
168+
CALL C_F_pointer(IfWinputFileString_C, IfWinputFileString)
169+
170+
! Format IfW input file contents
171+
if (IfWinputFilePassed==1_c_int) then
172+
InitInp%FilePassingMethod = 1_IntKi ! Don't try to read an input -- use passed data instead (blades and AF tables not passed) using FileInfoType
173+
InitInp%InputFileName = "passed_ifw_file" ! not actually used
174+
call InitFileInfo(IfWinputFileString, InitInp%PassedFileInfo, ErrStat2, ErrMsg2); if (Failed()) return
175+
else
176+
InitInp%FilePassingMethod = 0_IntKi ! Read input info from a primary input file
177+
i = min(IntfStrLen,IfWinputFileStringLength_C)
178+
TmpFileName = ''
179+
TmpFileName(1:i) = IfWinputFileString(1:i)
180+
i = INDEX(TmpFileName,C_NULL_CHAR) - 1 ! if this has a c null character at the end...
181+
if ( i > 0 ) TmpFileName = TmpFileName(1:I) ! remove it
182+
InitInp%InputFileName = TmpFileName
183+
endif
184+
185+
! For diagnostic purposes, the following can be used to display the contents
186+
! of the InFileInfo data structure.
187+
! CU is the screen -- system dependent.
188+
if (DebugLevel >= 3) then
189+
if (IfWinputFilePassed==1_c_int) call Print_FileInfo_Struct( CU, InitInp%PassedFileInfo )
133190
endif
134191

135192
! Set other inputs for calling InflowWind_Init
136-
InitInp%NumWindPoints = NumWindPts_C
137-
InitInp%InputFileName = "passed_ifw_file" ! dummy
138-
InitInp%RootName = "ifwRoot" ! used for making echo files
193+
InitInp%NumWindPoints = int(NumWindPts_C, IntKi)
194+
InitInp%RootName = OutRootName ! used for making echo files
139195
TimeInterval = REAL(DT_C, DbKi)
140196

141197
! Call the main subroutine InflowWind_Init - only need InitInp and TimeInterval as inputs, the rest are set by InflowWind_Init
@@ -174,6 +230,30 @@ logical function Failed()
174230
end function Failed
175231
subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here
176232
end subroutine Cleanup
233+
234+
!> This subroutine prints out all the variables that are passed in. Use this only
235+
!! for debugging the interface on the Fortran side.
236+
subroutine ShowPassedData()
237+
character(1) :: TmpFlag
238+
integer :: i,j
239+
call WrSCr("")
240+
call WrScr("-----------------------------------------------------------")
241+
call WrScr("Interface debugging: Variables passed in through interface")
242+
call WrScr(" ADI_C_Init")
243+
call WrScr(" --------------------------------------------------------")
244+
call WrScr(" FileInfo")
245+
TmpFlag="F"; if (IfWinputFilePassed==1_c_int) TmpFlag="T"
246+
call WrScr(" IfWinputFilePassed_C "//TmpFlag )
247+
call WrScr(" IfWinputFileString_C (ptr addr)"//trim(Num2LStr(LOC(IfWinputFileString_C))) )
248+
call WrScr(" IfWinputFileStringLength_C "//trim(Num2LStr( IfWinputFileStringLength_C )) )
249+
call WrScr(" OutRootName "//trim(OutRootName) )
250+
call WrScr(" Input variables")
251+
call WrScr(" NumWindPts_C "//trim(Num2LStr( NumWindPts_C)) )
252+
call WrScr(" Time variables")
253+
call WrScr(" DT_C "//trim(Num2LStr( DT_C )) )
254+
call WrScr("-----------------------------------------------------------")
255+
end subroutine ShowPassedData
256+
177257
END SUBROUTINE IfW_C_Init
178258

179259
!===============================================================================================================

0 commit comments

Comments
 (0)