Commit d8545bae authored by famen's avatar famen
Browse files

First commit for the development of the wrapper for hawc2 to call a BLADED style controller dll

parent ee92e08e
Pipeline #22433 passed with stage
in 3 minutes and 52 seconds
......@@ -9,6 +9,7 @@ set(MODSRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/modules)
set(IPCSRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/individual_pitch_controller)
set(DTUWEC_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/dtu_we_controller)
set(BLADED_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/dtu_we_controller_bladed)
set(BLADED_HAWC2_INTERFACE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/bladed_controller_hawc2)
set(FLAP_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/flap_controller_individual_aep_u_f)
set(FLAPCYC_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/flap_controller_cyclic)
set(BUILD_TYPE SHARED)
......@@ -18,9 +19,12 @@ set(LIB static)
message("Configuring DTU WEC source files: " ${DTUWEC_SRC_DIR})
add_subdirectory(${DTUWEC_SRC_DIR})
message("Configuring Bladed Interface source files: " ${BLADED_SRC_DIR})
message("Configuring Interface to DTUWEC for Bladed source files: " ${BLADED_SRC_DIR})
add_subdirectory(${BLADED_SRC_DIR})
message("Configuring Interface to Bladed controller for HAWC2 source files: " ${BLADED_HAWC2_INTERFACE_SRC_DIR})
add_subdirectory(${BLADED_HAWC2_INTERFACE_SRC_DIR})
message("Configuring Individual Pitch Controller source files: " ${IPCSRC_DIR})
add_subdirectory(${IPCSRC_DIR})
......
# Set the project name
project(bladed_hawc2_interface LANGUAGES Fortran)
message("Configuring Subproject: " ${PROJECT_NAME})
message("Root Directory is: " ${ROOT_DIR})
# set source code
set(BLADED_HAWC2_INTERFACE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
message("Source directory of the subproject is: " ${BLADED_HAWC2_INTERFACE_SRC_DIR})
file(GLOB_RECURSE MODSRC "${ROOT_DIR}/src/modules/global_constants.f90"
"${ROOT_DIR}/src/modules/global_variables.f90"
"${ROOT_DIR}/src/modules/misc_mod.f90"
"${ROOT_DIR}/src/modules/user_defined_types.f90"
)
file(GLOB_RECURSE SRC "${BLADED_HAWC2_INTERFACE_SRC_DIR}/bladed_controller_hawc2.f90"
# "${DTUWEC_SRC_DIR}/dtu_we_controller.f90"
# "${DTUWEC_SRC_DIR}/dtu_we_controller_fcns.f90"
# "${DTUWEC_SRC_DIR}/turbine_controller.f90"
# "${DTUWEC_SRC_DIR}/safety_system.f90"
# "${DTUWEC_SRC_DIR}/floating_controller.f90"
)
list (APPEND SRC ${MODSRC})
message("Including the utils: " "${ROOT_DIR}/utils/cmake/CMakeLists.txt")
include(${ROOT_DIR}/utils/cmake/CMakeLists.txt)
module bladed_controller_hawc2
!
! Interface for HAWC2 to use the Bladed Style Controller
!
use BuildInfo
use dll_utils
use iso_c_binding
implicit none
! real(8) dump_array(50)
! real(8) time_old
! logical repeated
! DLL data type
Type Tdiscon_dll
integer (C_INTPTR_T) :: p_dll ! integer pointer to DLL
#if defined __GFORTRAN__
type(C_FUNPTR) :: p_func ! function pointer to DISCON subroutine in DLL for gfortran compiler
#elif defined __INTEL_COMPILER
integer (C_INTPTR_T) :: p_func ! integer pointer to DISCON subroutine in DLL for Intel Fortran Compiler
#endif
character*256 :: filename=''
character*256 :: infile =''
character*256 :: outfile=''
character*256 :: func_name=''
real(4),dimension(100) :: avrSWAP=0.0 ! The swap array, used to pass data to, and receive data from, the DLL controller.
integer(c_int) :: is_shutdown
integer(c_int) :: hawc2_status
integer(c_int) :: max_num_of_value_logging, start_elem_record_logging,max_str_length_logging
integer :: istep_hawc2
integer :: istep_controller !
real*8 :: aero_power
real*8 :: gear_ratio
real*8 :: efficiency
real*8 :: time_step = 0.d0 ! time step used in simulation [s]
end Type Tdiscon_dll
! declare global variables
Type(Tdiscon_dll), save :: discon_dll
! variables for debugging purpose
character(1) str
logical, save :: DEBUG_Flag = .true.
! declare the DISCON() function in the interface
Interface
subroutine DISCON( avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG ) bind(c,name='DISCON')
use, intrinsic :: iso_c_binding
REAL(4), INTENT(INOUT) :: avrSWAP (*) ! The swap array, used to pass data to, and receive data from, the DLL controller.
INTEGER(4), INTENT( OUT) :: aviFAIL ! A flag used to indicate the success of this DLL call set as follows
! aviFAIL = 0 if the DLL call was successful;
! aviFAIL > 0 if the DLL call was successful but cMessage should be issued as a warning messsage;
! aviFAIL < 0 if the DLL call was unsuccessful or for any other reason the simulation is to be stopped at this point with cMessage as the error message.
INTEGER(1), INTENT(IN ) :: accINFILE (*) ! The address of the first record of an array of 1-byte CHARACTERs giving the name of the parameter input file, 'DISCON.IN'.
INTEGER(1), INTENT( OUT) :: avcMSG (*) ! The address of the first record of an array of 1-byte CHARACTERS giving the message contained in cMessage, which will be displayed by the calling program if aviFAIL <> 0.
INTEGER(1), INTENT(IN ) :: avcOUTNAME(*) ! The address of the first record of an array of 1-byte CHARACTERS giving the simulation run name without extension.
end subroutine DISCON
end interface
procedure(DISCON), bind(c), pointer :: fp_discon
contains
!**************************************************************************************************
subroutine initstring(istring) bind(c,name='initstring')
! implicit none
!DEC$ IF .NOT. DEFINED(__LINUX__)
!DEC$ ATTRIBUTES DLLEXPORT :: initstring
!GCC$ ATTRIBUTES DLLEXPORT :: initstring
!DEC$ END IF
! Declare the subroutine input arguments
integer(1) :: istring(*)
! Declare the local variables
integer(1) :: istring256(256)
character*256 :: cstring
character(len=7) :: ext(4) = (/'.dll','.txt','.dat','.so '/) ! all extentions
character(kind=C_CHAR,len=256) :: name, tmp, tmp1, tmp2 ! C-String
integer :: i_ext,n_idx
equivalence(istring256,cstring)
istring256(1:256)=istring(1:256)
! Read in the initstring given in the type2_dll block in HAWC2 main htc file
tmp = trim(adjustl(cstring))
! Remove the last null character, if there
if ( tmp(len_trim(tmp):len_trim(tmp)) == C_NULL_CHAR ) then
tmp1 = tmp(1:len_trim(tmp)-1)
else
tmp1 = trim(tmp)
end if
! Split the input string if exist ':' to get the controller input file name
n_idx = index(tmp1,':')
if (n_idx /= 0) then
name = tmp1(1:n_idx-1) ! dll name
discon_dll%infile = tmp1(n_idx+1:len_trim(tmp1)) ! external control input file name
else
name = tmp1
end if
! Get extension from file name
do i_ext = 1, size(ext)
if (name(len_trim(name)-len_trim(ext(i_ext))+1:len_trim(name))==trim(ext(1))) then
discon_dll%filename = trim(adjustl(name))
write(6,'(A)') 'HAWC2 found a controller dll: "'//trim(discon_dll%filename)//'" from third party.'
! TODO: set a flag here
exit
end if
enddo
end subroutine initstring
!**************************************************************************************************
!**************************************************************************************************
subroutine init_discon_dll(array1, array2) bind(c, name='init_discon_dll')
! Call this subroution in HAWC2 type2_dll block for init purpose
! when using controller dlls from the 3rd party
!DEC$ IF .NOT. DEFINED(__LINUX__)
!DEC$ ATTRIBUTES DLLEXPORT :: init_discon_dll
!GCC$ ATTRIBUTES DLLEXPORT :: init_discon_dll
!DEC$ END IF
! I/O parameters
real(c_double), dimension(10), intent(inout) :: array1
real(c_double), dimension(1), intent(inout) :: array2
! Local variables
! integer(4) :: stepno
! Required init parameters from users to initialize the controller :
! constant 1: time step [s]
! constant 2: total drivetrain efficiency [-]
! constant 3: gearbox ratio [-]
! constant 4: Max. number of values which can be returned for loggingreserved init parameters [-]
! constant 5: Record number for start of logging output [-]
! constant 6: Max. No. of characters which can be returned in "OUTNAME" [-]
discon_dll%time_step = array1(1)
discon_dll%efficiency = array1(2)
discon_dll%gear_ratio = array1(3)
discon_dll%max_num_of_value_logging = nint(array1(4))
discon_dll%start_elem_record_logging = nint(array1(5))
discon_dll%max_str_length_logging = nint(array1(6))
discon_dll%func_name = 'DISCON'
! Load the 3rd party dll into memory
write(6,'(A)') 'The external 3rd party DLL "'//trim(discon_dll%filename)//'" is attempted to open.'
discon_dll%p_dll=loaddll(discon_dll%filename,0)
if (discon_dll%p_dll==0) then
write(0,'(A)') '*** ERROR *** External 3rd party DLL "'//trim(discon_dll%filename)//'" could not be loaded!'
array2(1)=0.0
else
write(6,'(A)') 'Successfully opening the external 3rd party DLL "'//trim(discon_dll%filename)//'"'
array2(1)=1.0
endif
! Load function or subroutine from this 3rd party dll
discon_dll%p_func = loadsymbol(discon_dll%p_dll,trim(adjustl(discon_dll%func_name)),0)
if (transfer(discon_dll%p_func,C_INTPTR_T)==0) then
write(0,'(A)') '*** ERROR *** The function "'//trim(adjustl(discon_dll%func_name))//'" could not be loaded in the external 3rd party DLL.'
array2(1)=0.0
else
write(6,'(A)') 'Successfully loading function "'//trim(adjustl(discon_dll%func_name))//'" from the external 3rd party DLL.'
array2(1)=1.0
endif
! Now it works for Intel Fortran Compiler in VS 2017/2019 and GNU Fortran on Windows
! TODO:
! Remember to test this implementation on Linux
call c_f_procpointer(transfer(discon_dll%p_func,c_null_funptr),fp_discon)
!stepno = 0
!time_old = 0.0
end subroutine init_discon_dll
subroutine update_discon_dll(array1, array2) bind(c, name='update_discon_dll')
! Call this subroution in HAWC2 type2_dll block at each time step
! when using the controller dlls from the 3rd party
!DEC$ IF .NOT. DEFINED(__LINUX__)
!DEC$ ATTRIBUTES DLLEXPORT :: update_discon_dll
!GCC$ ATTRIBUTES DLLEXPORT :: update_discon_dll
!DEC$ END IF
! Global variables
real(c_double), dimension(40), intent(inout) :: array1
real(c_double), dimension(20), intent(inout) :: array2
! Define local Variables
integer, save :: i_step = 0
real(c_double) time
integer(4) :: i, iostat, callno = 0
integer(c_int) :: iStatus
INTEGER(4) :: aviFail
INTEGER(1) :: accInFile(256) ! CHARACTER string giving the name of the parameter input file stored as a 1-byte array.
INTEGER(1) :: avcMSG(256) = 20 ! CHARACTER string giving a message that will be displayed by the calling program if aviFAIL <> 0 stored as a 1-byte array.
INTEGER(1), SAVE :: avcOutName(1024) ! CHARACTER string giving the simulation output name stored as a 1-byte array.
CHARACTER( 256) :: cInFile ! CHARACTER string giving the name of the parameter input file, 'DISCON.IN'
CHARACTER( 256) :: cMessage ! CHARACTER string giving a message that will be displayed by the calling program if aviFAIL <> 0.
CHARACTER(1024), SAVE :: cOutName ! CHARACTER string giving the simulation run name without extension.
equivalence (accInFile , cInFile )
equivalence (avcMSG, cMessage)
equivalence (avcOutName, cOutName)
iStatus = nint(array1(1))
aviFail = 0
! Convert c character arrays to Fortran CHARACTER strings:
!cOutName = transfer(avcOUTNAME(1:len(cOutName)),cOutName)
!i = index(cOutName, C_NULL_CHAR) - 1 ! Find the c NULL character at the end of cOutName, if it has then remove it
!if (i>0) cOutName = cOutName(1:i)
!
!cInFile = transfer(avcINFILE(1:len(cInFile)),cInFile)
!i = index(cInFile, C_NULL_CHAR) - 1 ! Find the c NULL character at the end of cInFile, if it has then remove it
!if (i>0) cInFile = cInFile(1:i)
! If iStatus = 0 means OpenFAST/Bladed running at time = 0.00,
! The DTUWEC starts to run at the second time step, eg., time = delta_t
! Therefore, the iStatus should be bigger than 0
!--------------------------------------------------------------------------------------------
! Required inputs:
! 1: status
! 2: time[s]
! 3: rotor speed [rad/s]
! 4: blade 1 pitch angle [rad]
! 5: blade 2 pitch angle [rad]
! 6: blade 3 pitch angle [rad]
! 7: Shaft moment [kNm]
! 8: flap moment, blade 1 [kNm]
! 9: flap moment, blade 2 [kNm]
! 10: flap moment, blade 3 [kNm]
! 11: rotor azimuth angle [rad]
! 12: tower top fore-aft acceleration [m/s^2]
! 13: nacelle nodding acceleration [rad/s^2]
!--------------------------------------------------------------------------------------------
! Provided outputs:
! 1: generator torque [Nm]
! 2: pitch1 demand [rad]
! 3: pitch2 demand [rad]
! 4: pitch3 demand [rad]
! 5: Mechanical power [kW]
! 6: Electrical power [kW]
!--------------------------------------------------------------------------------------------
! Required avrSWAP channels 1-2-3-4-15-20-21-33-34-49-50-51-60-62-63-64
discon_dll%avrSWAP(1) = iStatus !status
discon_dll%avrSWAP(2) = array1(2) !time[s]
discon_dll%avrSWAP(3) = discon_dll%time_step !time step in simulation [s]
discon_dll%avrSWAP(4) = array1(4) !blade 1 pitch angle [rad]
! discon_dll%avrSWAP(10)=0.d0 ! 0=pitch position actuator, 1=pitch rate actuator
! discon_dll%avrSWAP(14) = array1(8) * 1000.d0 * array1(3) ! measured shaft power [W]
discon_dll%avrSWAP(15) = array1(7) * 1000.d0 * array1(3) * discon_dll%efficiency! measured electrical power [W]
discon_dll%avrSWAP(20) = array1(3) * discon_dll%gear_ratio !generator speed [rad/s]
discon_dll%avrSWAP(21) = array1(3) !rotor speed [rad/s]
! discon_dll%avrSWAP(23)=array1(8) * 1000.d0 * discon_dll%gearratio ! measured generator torque [Nm]
! not active discon_dll%avrSWAP(24) ! measured yaw error [rad]
! discon_dll%avrSWAP(27)=array1(7) !Hub wind speed [m/s]
! discon_dll%avrSWAP(28)=0.d0 ! 0=collective pitch 1=individual pitch
! discon_dll%avrSWAP(30)=array1( 9)*1000.d0 ! flap moment, blade 1 [Nm]
! discon_dll%avrSWAP(31)=array1(10)*1000.d0 ! flap moment, blade 2 [Nm]
! discon_dll%avrSWAP(32)=array1(11)*1000.d0 ! flap moment, blade 3 [Nm]
discon_dll%avrSWAP(33) = array1(5) ! blade 2 pitch angle [rad]
discon_dll%avrSWAP(34) = array1(6) ! blade 3 pitch angle [rad]
discon_dll%avrSWAP(49) = size(avcMSG)-1
discon_dll%avrSWAP(50) = len(trim(discon_dll%infile))
if(iStatus == 0) then
cInFile=trim(discon_dll%Infile)
call fp_discon(discon_dll%avrSWAP,aviFail,accInFile,avcOutName,avcMSG)
endif
discon_dll%avrSWAP(51) = len(trim(discon_dll%outfile))
if(iStatus == 0) then
cOutName=trim(discon_dll%Outfile)
endif
! discon_dll%avrSWAP(53)=array1(13) ! tower top fore-aft acceleration [m/s^2]
discon_dll%avrSWAP(60) = array1(12) ! rotor azimuth angle [rad]
discon_dll%avrSWAP(62) = discon_dll%max_num_of_value_logging ! Max. number of values which can be returned for logging
discon_dll%avrSWAP(63) = discon_dll%start_elem_record_logging ! Record number for start of logging output
discon_dll%avrSWAP(64) = discon_dll%max_str_length_logging ! Max. no. of characters which can be returned in "OUTNAME"
! discon_dll%avrSWAP(83)=array1(14) ! nacelle nodding acceleration [rad/s^2]
! ------------------------------------------------------------------------------------
! Floating turbine part
! ------------------------------------------------------------------------------------
! floater motion
discon_dll%avrSWAP(66)=0.d0 ! PtfmTDxi
discon_dll%avrSWAP(67)=0.d0 ! PtfmTDyi
discon_dll%avrSWAP(68)=0.d0 ! PtfmTDzi
discon_dll%avrSWAP(69)=0.d0 ! PtfmRDxi
discon_dll%avrSWAP(70)=0.d0 ! PtfmRDyi
discon_dll%avrSWAP(71)=0.d0 ! PtfmRDzi
! floater velocity
discon_dll%avrSWAP(72)=0.d0 ! PtfmTVxi
discon_dll%avrSWAP(73)=0.d0 ! PtfmTVyi
discon_dll%avrSWAP(74)=0.d0 ! PtfmTVzi
discon_dll%avrSWAP(75)=0.d0 ! PtfmRVxi
discon_dll%avrSWAP(76)=0.d0 ! PtfmRVyi
discon_dll%avrSWAP(77)=0.d0 ! PtfmRVzi
! floater acceleration
discon_dll%avrSWAP(78)=0.d0 ! PtfmTAxi
discon_dll%avrSWAP(79)=0.d0 ! PtfmTAyi
discon_dll%avrSWAP(80)=0.d0 ! PtfmTAzi
discon_dll%avrSWAP(81)=0.d0 ! PtfmRAxi
discon_dll%avrSWAP(82)=0.d0 ! PtfmRAyi
discon_dll%avrSWAP(83)=0.d0 ! PtfmRAzi
if (aviFail == 0) then
continue ! call was succesfull
elseif (aviFAIL > 0) then
write (0,*) '*** WARNING *** In external dll. Warning message from discon:'
write (0,*) trim(cMessage)
else
write (0,*) '*** ERROR *** In external dll. Error message from discon:'
write (0,*) trim(cMessage)
stop
endif
if (( iStatus == 0 ) .and. ( aviFail >= 0 ) ) then
write (0,*) '*** INFO *** In external dll: Calling to DISCON dll at FIRST timestep is successful!'
stop
endif
return
end subroutine update_discon_dll
end module bladed_controller_hawc2
\ No newline at end of file
#define FILE_NAME "bladed_hawc2_interface.dll"
#define FILE_DESCRIPTION "Bladed Style Controller - HAWC2 Interface"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment