diff --git a/glue-codes/labview/src/WaveTank.f90 b/glue-codes/labview/src/WaveTank.f90 index 90bf7dcd6..08f272a63 100644 --- a/glue-codes/labview/src/WaveTank.f90 +++ b/glue-codes/labview/src/WaveTank.f90 @@ -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 & ) @@ -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, & diff --git a/glue-codes/python/pyOpenFAST/aerodyn_inflow.py b/glue-codes/python/pyOpenFAST/aerodyn_inflow.py index 5dd46cc79..62b332137 100644 --- a/glue-codes/python/pyOpenFAST/aerodyn_inflow.py +++ b/glue-codes/python/pyOpenFAST/aerodyn_inflow.py @@ -60,6 +60,8 @@ import numpy as np import numpy.typing as npt +from .interface_abc import OpenFASTInterfaceType + #------------------------------------------------------------------------------- # Helper functions and classes #------------------------------------------------------------------------------- @@ -144,7 +146,7 @@ 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 @@ -152,28 +154,6 @@ class AeroDynInflowLib(CDLL): 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. @@ -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 @@ -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 @@ -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: @@ -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() @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/glue-codes/python/pyOpenFAST/hydrodyn.py b/glue-codes/python/pyOpenFAST/hydrodyn.py index df9da2334..507554a9f 100644 --- a/glue-codes/python/pyOpenFAST/hydrodyn.py +++ b/glue-codes/python/pyOpenFAST/hydrodyn.py @@ -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) @@ -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. diff --git a/glue-codes/python/pyOpenFAST/inflowwind.py b/glue-codes/python/pyOpenFAST/inflowwind.py index bc741443e..f2bcd9400 100644 --- a/glue-codes/python/pyOpenFAST/inflowwind.py +++ b/glue-codes/python/pyOpenFAST/inflowwind.py @@ -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 @@ -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 diff --git a/glue-codes/python/pyOpenFAST/interface_abc.py b/glue-codes/python/pyOpenFAST/interface_abc.py index 5b9c3a5f2..ad69e8b18 100644 --- a/glue-codes/python/pyOpenFAST/interface_abc.py +++ b/glue-codes/python/pyOpenFAST/interface_abc.py @@ -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", @@ -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) diff --git a/glue-codes/python/pyOpenFAST/moordyn.py b/glue-codes/python/pyOpenFAST/moordyn.py index 01272a2d4..63136970d 100644 --- a/glue-codes/python/pyOpenFAST/moordyn.py +++ b/glue-codes/python/pyOpenFAST/moordyn.py @@ -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) diff --git a/glue-codes/python/pyOpenFAST/seastate.py b/glue-codes/python/pyOpenFAST/seastate.py index 1f41facc0..944fd79cb 100644 --- a/glue-codes/python/pyOpenFAST/seastate.py +++ b/glue-codes/python/pyOpenFAST/seastate.py @@ -46,10 +46,6 @@ class MotionData: position information for SeaState """ position: npt.NDArray[np.float32] - #orientation: npt.NDArray[np.float64] - #velocity: npt.NDArray[np.float32] - #acceleration: npt.NDArray[np.float32] - class SeaStateLib(OpenFASTInterfaceType): @@ -72,7 +68,6 @@ def __init__(self, library_path): self.ended = False # For error handling at end # 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) @@ -189,6 +184,16 @@ def _initialize_routines(self): ] self.SeaSt_C_GetSurfNorm.restype = None + self.SeaSt_C_GetElevMinMaxEstimate.argtypes = [ + POINTER(c_float), # intent( out) :: elevMin_c + POINTER(c_float), # intent( out) :: elevMax_c + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char) # intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.SeaSt_C_GetElevMinMaxEstimate.restype = None + + + def check_error(self) -> None: """Checks for and handles any errors from the Fortran library. @@ -475,8 +480,32 @@ def get_surfNorm(self, self.check_error() return norm - - + def get_elevMinMax(self) -> tuple[float, float]: + """ + Get estimate of the min and max total wave elevation. Will over + estimate range when 2nd order waves used + + Returns: + tuple[float, float]: A tuple containing (elevMin, elevMax) where: + - elevMin: minimum elevation estimate in meters + - elevMax: maximum elevation estimate in meters + + Raises: + RuntimeError: If calculation fails + """ + elevMin_c = c_float(0.0) + elevMax_c = c_float(0.0) + print("Calling SeaSt_C_GetElevMinMaxEstimate") + self.SeaSt_C_GetElevMinMaxEstimate( + elevMin_c, # out <- min elev + elevMax_c, # out <- max elev + byref(self.error_status_c), # OUT <- error status + self.error_message_c # OUT <- error message + ) + self.check_error() + elevMin = elevMin_c.value + elevMax = elevMax_c.value + return elevMin,elevMax @property diff --git a/modules/aerodyn/src/AeroDyn_Inflow.f90 b/modules/aerodyn/src/AeroDyn_Inflow.f90 index fc3363757..d730dfa2e 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow.f90 @@ -73,14 +73,25 @@ subroutine ADI_Init(InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut p%MHK = InitInp%AD%MHK p%WtrDpth = InitInp%AD%WtrDpth - ! --- Initialize Inflow Wind + ! --- Initialize Inflow Wind call ADI_InitInflowWind(InitInp%RootName, InitInp%IW_InitInp, u%AD, OtherState%AD, m%IW, Interval, InitOut_IW, errStat2, errMsg2); if (Failed()) return ! Concatenate AD outputs to IW outputs call concatOutputHeaders(InitOut%WriteOutputHdr, InitOut%WriteOutputUnt, InitOut_IW%WriteOutputHdr, InitOut_IW%WriteOutputUnt, errStat2, errMsg2); if(Failed()) return ! --- Initialize AeroDyn ! Link InflowWind's FlowField to AeroDyn's FlowField - InitInp%AD%FlowField => InitOut_IW%FlowField + select case (m%IW%CompInflow) + case (0) ! steady wind - data stored as a flowfield dataset + InitInp%AD%FlowField => InitOut_IW%FlowField + case (1) ! IfW is used directly in ADI + InitInp%AD%FlowField => InitOut_IW%FlowField + case (2) ! FlowField pointer is passed in + InitInp%AD%FlowField => InitInp%FlowField + case default + ErrStat2 = ErrID_Fatal + ErrMsg2 = 'Invalid value for CompInflow' + if (Failed()) return + end select call AD_Init(InitInp%AD, u%AD, p%AD, x%AD, xd%AD, z%AD, OtherState%AD, y%AD, m%AD, Interval, InitOut_AD, errStat2, errMsg2); if (Failed()) return InitOut%Ver = InitOut_AD%ver @@ -251,7 +262,6 @@ end subroutine ADI_UpdateStates !---------------------------------------------------------------------------------------------------------------------------------- !> Routine for computing outputs, used in both loose and tight coupling. subroutine ADI_CalcOutput(t, u, p, x, xd, z, OtherState, y, m, errStat, errMsg) - use IfW_FlowField, only: IfW_FlowField_GetVelAcc real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(ADI_InputType), intent(inout) :: u !< Inputs at Time t ! NOTE: set as in-out since "Inflow" needs to be set type(ADI_ParameterType), intent(in ) :: p !< Parameters @@ -273,7 +283,9 @@ subroutine ADI_CalcOutput(t, u, p, x, xd, z, OtherState, y, m, errStat, errMsg) errMsg = "" !---------------------------------------------------------------------------- - ! Calculate InflowWind outputs if module was initialized + ! Calculate InflowWind outputs if module is directly used within ADI. + ! NOTE: if IfW is external and only the FlowField pointer is used, we don't do + ! any IfW calcoutput here !---------------------------------------------------------------------------- if (m%IW%CompInflow == 1) then @@ -374,7 +386,7 @@ subroutine ADI_InitInflowWind(Root, i_IW, u_AD, o_AD, IW, dt, InitOutData, errSt if(Failed()) return IW%p%FlowField%AccFieldValid = .true. end if - else + elseif (i_IW%CompInflow == 1) then ! InflowWind loaded here ! Initialze InflowWind module InitInData%InputFileName = i_IW%InputFile InitInData%Linearize = i_IW%Linearize @@ -403,6 +415,7 @@ subroutine ADI_InitInflowWind(Root, i_IW, u_AD, o_AD, IW, dt, InitOutData, errSt IW%x, IW%xd, IW%z, IW%OtherSt, & IW%y, IW%m, dt, InitOutData, errStat2, errMsg2 ) if(Failed()) return + !elseif (i_IW%CompInflow == 2) then ! InflowWind is external, using FlowField pointer, no init call required endif ! --- Store main init input data (data that don't use InfloWind directly) diff --git a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 index 4be71dd8f..22ecc7d1b 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 @@ -37,16 +37,25 @@ MODULE AeroDyn_Inflow_C_BINDING PUBLIC :: ADI_C_CalcOutput PUBLIC :: ADI_C_UpdateStates PUBLIC :: ADI_C_End - PUBLIC :: ADI_C_PreInit ! Initial call to setup number of turbines - PUBLIC :: ADI_C_SetupRotor ! Initial node positions etc for a rotor - PUBLIC :: ADI_C_SetRotorMotion ! Set motions for a given rotor - PUBLIC :: ADI_C_GetRotorLoads ! Retrieve loads for a given rotor - PUBLIC :: ADI_C_GetDiskAvgVel ! Get the disk average velocity for the rotor + PUBLIC :: ADI_C_PreInit ! Initial call to setup number of turbines + PUBLIC :: ADI_C_SetupRotor ! Initial node positions etc for a rotor + PUBLIC :: ADI_C_SetRotorMotion ! Set motions for a given rotor + PUBLIC :: ADI_C_GetRotorLoads ! Retrieve loads for a given rotor + PUBLIC :: ADI_C_GetDiskAvgVel ! Get the disk average velocity for the rotor + PUBLIC :: ADI_C_SetFlowFieldPointer ! Set the pointer to the flowfield data (from external IfW instance) + PUBLIC :: ADI_C_GetFlowFieldPointer ! Get the pointer to the flowfield data (to pass to external IfW instance) !------------------------------------------------------------------------------------ ! Version info for display type(ProgDesc), parameter :: version = ProgDesc( 'AeroDyn-Inflow library', '', '' ) + !------------------------------------------------------------------------------------ + ! Wind + ! externFlowField - FlowField is handled externally to ADI, pointer must be set manually + ! FlowFieldPtrSet - is the FlowField pointer set from external? + logical :: externFlowField = .false. + logical :: FlowFieldPtrSet = .false. + !------------------------------------------------------------------------------------ ! Debugging: DebugLevel -- passed at PreInit ! 0 - none @@ -125,8 +134,9 @@ MODULE AeroDyn_Inflow_C_BINDING ! interface and therefore must store it here analogously to how it is handled ! in the OpenFAST glue code. integer(IntKi) :: n_Global ! global timestep - integer(IntKi) :: VTKn_Global ! global timestep for VTK - integer(IntKi) :: VTKn_last ! last global timestep for VTK + integer(IntKi) :: VTKn_Global ! global timestep for VTK + integer(IntKi) :: VTKn_last ! last global timestep for VTK + character(IntfStrLen) :: OutVTKDir !< Output directory for files (relative to current location) real(DbKi) :: InputTimePrev ! input time of last UpdateStates call real(DbKi) :: InputTimePrev_Calc ! input time of last CalcOutput call ! Note that we are including the previous state info here (not done in OF this way) @@ -187,6 +197,10 @@ subroutine ADI_C_PreInit( & defSpdSound_in, defPatm_in, defPvap_in, & WtrDpth_in, MSL2SWL_in, & MHK_in, & + externFlowField_in, & + OutVTKDir_C, & + WrVTK_in, WrVTK_inType, WrVTK_inDT, & + VTKNacDim_in, VTKHubRad_in, & DebugLevel_in, ErrStat_C, ErrMsg_C & ) BIND (C, NAME='ADI_C_PreInit') implicit none @@ -194,24 +208,34 @@ subroutine ADI_C_PreInit( & !DEC$ ATTRIBUTES DLLEXPORT :: ADI_C_PreInit !GCC$ ATTRIBUTES DLLEXPORT :: ADI_C_PreInit #endif - integer(c_int), intent(in ) :: NumTurbines_C - integer(c_int), intent(in ) :: TransposeDCM_in !< Transpose DCMs as they are passed i - integer(c_int), intent(in ) :: PointLoadOutput_in - real(c_float), intent(in ) :: gravity_in !< Gravitational acceleration (m/s^2) - real(c_float), intent(in ) :: defFldDens_in !< Air density (kg/m^3) - real(c_float), intent(in ) :: defKinVisc_in !< Kinematic viscosity of working fluid (m^2/s) - real(c_float), intent(in ) :: defSpdSound_in !< Speed of sound in working fluid (m/s) - real(c_float), intent(in ) :: defPatm_in !< Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] - real(c_float), intent(in ) :: defPvap_in !< Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] - real(c_float), intent(in ) :: WtrDpth_in !< Water depth (m) [used only for an MHK turbine] - real(c_float), intent(in ) :: MSL2SWL_in !< Offset between still-water level and mean sea level (m) [positive upward, used only for an MHK turbine] - integer(c_int), intent(in ) :: MHK_in !< Marine hydrokinetic turbine [0: none; 1: fixed bottom MHK; 2: Floating MHK] - integer(c_int), intent(in ) :: DebugLevel_in - integer(c_int), intent( out) :: ErrStat_C - character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer(c_int), intent(in ) :: NumTurbines_C + integer(c_int), intent(in ) :: TransposeDCM_in !< Transpose DCMs as they are passed in + integer(c_int), intent(in ) :: PointLoadOutput_in + real(c_float), intent(in ) :: gravity_in !< Gravitational acceleration (m/s^2) + real(c_float), intent(in ) :: defFldDens_in !< Air density (kg/m^3) + real(c_float), intent(in ) :: defKinVisc_in !< Kinematic viscosity of working fluid (m^2/s) + real(c_float), intent(in ) :: defSpdSound_in !< Speed of sound in working fluid (m/s) + real(c_float), intent(in ) :: defPatm_in !< Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] + real(c_float), intent(in ) :: defPvap_in !< Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] + real(c_float), intent(in ) :: WtrDpth_in !< Water depth (m) [used only for an MHK turbine] + real(c_float), intent(in ) :: MSL2SWL_in !< Offset between still-water level and mean sea level (m) [positive upward, used only for an MHK turbine] + integer(c_int), intent(in ) :: MHK_in !< Marine hydrokinetic turbine [0: none; 1: fixed bottom MHK; 2: Floating MHK] + ! inflow + integer(c_int), intent(in ) :: externFlowField_in !< skip IfW setup and use external module with pointer + ! VTK + character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output + integer(c_int), intent(in ) :: WrVTK_in !< Write VTK outputs [0: none, 1: init only, 2: animation] + integer(c_int), intent(in ) :: WrVTK_inType !< Write VTK outputs as [1: surface, 2: lines, 3: both] + real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes + real(c_float), intent(in ) :: VTKNacDim_in(6) !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) + real(c_float), intent(in ) :: VTKHubrad_in !< Hub radius for VTK surface rendering + integer(c_int), intent(in ) :: DebugLevel_in + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) ! Local variables integer(IntKi) :: iWT !< current turbine + integer(IntKi) :: i !< generic index variables integer :: ErrStat_F !< aggregated error status character(ErrMsgLen) :: ErrMsg_F !< aggregated error message integer :: ErrStat_F2 !< temporary error status from a call @@ -226,6 +250,9 @@ subroutine ADI_C_PreInit( & CALL DispCopyrightLicense( version%Name ) CALL DispCompileRuntimeInfo( version%Name ) + ! clear out any memory that may be leftover from previous use of library without unloading + call ClearTmpStorage() + ! Save flag for outputting point or distributed loads PointLoadOutput = PointLoadOutput_in /= 0 @@ -328,6 +355,31 @@ subroutine ADI_C_PreInit( & end if enddo + + !---------------------------------------------------- + ! ADI settings + !---------------------------------------------------- + ! FlowField - internal to ADI library, or external + ! if externally set, must call the ADI_C_SetFlowFieldPointer routine + if (externFlowField_in==1_c_int) then + externFlowField = .true. + endif + + ! Setup VTK + ! OutVTKDir -- output directory + OutVTKDir = TRANSFER( OutVTKDir_C, OutVTKDir ) + i = INDEX(OutVTKDir,C_NULL_CHAR) - 1 ! if this has a c null character at the end... + if ( i > 0 ) OutVTKDir = OutVTKDir(1:I) ! remove it + + ! VTK writing + WrOutputsData%WrVTK = int(WrVTK_in, IntKi) + WrOutputsData%WrVTK_Type = int(WrVTK_inType, IntKi) + WrOutputsData%VTK_dt = real(WrVTK_inDT, DbKi) + WrOutputsData%VTKNacDim = real(VTKNacDim_in, SiKi) + WrOutputsData%VTKHubrad = real(VTKHubrad_in, SiKi) + WrOutputsData%VTKRefPoint = (/ 0.0_ReKi, 0.0_ReKi, 0.0_ReKi /) !TODO: should this be an input? + WrOutputsData%n_VTKTime = 1 ! output every timestep + call SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C) contains @@ -377,6 +429,14 @@ subroutine ShowPassedData() call WrScr(" defPvap_C "//trim(Num2LStr( defPvap_in )) ) call WrScr(" WtrDpth_C "//trim(Num2LStr( WtrDpth_in )) ) call WrScr(" MSL2SWL_C "//trim(Num2LStr( MSL2SWL_in )) ) + call WrScr(" Wind from external IfW instance") + TmpFlag="F"; if (externFlowField_in==1_c_int) TmpFlag="T" + call WrScr(" externFlowField_in "//TmpFlag ) + call WrScr(" VTK visualization variables") + call WrScr(" OutVTKDir "//trim(OutVTKDir) ) + call WrScr(" WrVTK_in "//trim(Num2LStr( WrVTK_in )) ) + call WrScr(" WrVTK_inType "//trim(Num2LStr( WrVTK_inType )) ) + call WrScr(" WrVTK_inDT "//trim(Num2LStr( WrVTK_inDT )) ) call WrScr("-----------------------------------------------------------") end subroutine ShowPassedData @@ -387,11 +447,8 @@ end subroutine ADI_C_PreInit !=============================================================================================================== SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileStringLength_C, & IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStringLength_C, OutRootName_C, & - OutVTKDir_C, & InterpOrder_C, DT_C, TMax_C, & storeHHVel, & - WrVTK_in, WrVTK_inType, WrVTK_inDT, & - VTKNacDim_in, VTKHubRad_in, & wrOuts_C, DT_Outs_C, & NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, & ErrStat_C, ErrMsg_C) BIND (C, NAME='ADI_C_Init') @@ -408,7 +465,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString type(c_ptr), intent(in ) :: IfWinputFileString_C !< Input file as a single string with lines delineated by C_NULL_CHAR integer(c_int), intent(in ) :: IfWinputFileStringLength_C !< length of the input file string character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) !< Root name to use for echo files and other - character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output ! Interpolation integer(c_int), intent(in ) :: InterpOrder_C !< Interpolation order to use (must be 1 or 2) ! Time @@ -416,12 +472,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString real(c_double), intent(in ) :: TMax_C !< Maximum time for simulation ! Flags integer(c_int), intent(in ) :: storeHHVel !< Store hub height time series from IfW - ! VTK - integer(c_int), intent(in ) :: WrVTK_in !< Write VTK outputs [0: none, 1: init only, 2: animation] - integer(c_int), intent(in ) :: WrVTK_inType !< Write VTK outputs as [1: surface, 2: lines, 3: both] - real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes - real(c_float), intent(in ) :: VTKNacDim_in(6) !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) - real(c_float), intent(in ) :: VTKHubrad_in !< Hub radius for VTK surface rendering integer(c_int), intent(in ) :: wrOuts_C !< Write ADI output file real(c_double), intent(in ) :: DT_Outs_C !< Timestep to write output file from ADI ! Output @@ -441,7 +491,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString character(ErrMsgLen) :: ErrMsg_F !< aggregated error message integer(IntKi) :: ErrStat_F2 !< temporary error status from a call character(ErrMsgLen) :: ErrMsg_F2 !< temporary error message from a call - character(IntfStrLen) :: OutVTKDir !< Output directory for files (relative to current location) integer(IntKi) :: i,j,k !< generic index variables integer(IntKi) :: iWT !< current turbine number (iterate through during setup for ADI_Init call) integer(IntKi) :: AeroProjMod !< for checking that all turbines use the same AeroProjMod @@ -464,6 +513,24 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString if (Failed()) return endif + ! if using an external IfW, check that the pointer was actually set and is valid + if (externFlowField) then + if (.not. FlowFieldPtrSet) then + ErrStat_F2 = ErrID_Fatal + ErrMsg_F2 = "FlowField data from an external InflowWind instance declared, but pointer to data not set. Call ADI_C_SetFlowFieldPointer after ADI_C_PreInit, but before ADI_C_Init" + if (Failed()) return + endif + if (associated(InitInp%FlowField)) then + ! basic sanity check + if (InitInp%FlowField%FieldType <= 0_IntKi) then + ErrStat_F2 = ErrID_Fatal + ErrMsg_F2 = "Invalid FlowField pointer passed in, or external InflowWind FlowField not initialized. Call ADI_C_SetFlowFieldPointer after ADI_C_PreInit, but before ADI_C_Init" + if (Failed()) return + endif + endif + endif + + do iWT=1,Sim%NumTurbines if (Sim%WT(iWT)%NumBlades < 0) call SetErrStat(ErrID_Fatal,"Rotor "//trim(Num2LStr(iWT))//" not initialized. Call ADI_C_SetupRotor prior to calling ADI_C_Init",ErrStat_F2,ErrMsg_F2,RoutineName) enddo @@ -480,9 +547,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString endif enddo - ! Setup temporary storage arrays for simpler transfers - call SetTempStorage(ErrStat_F2,ErrMsg_F2); if (Failed()) return - !-------------------------- ! Input files @@ -492,21 +556,16 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString 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 - ! OutVTKDir -- output directory - OutVTKDir = TRANSFER( OutVTKDir_C, OutVTKDir ) - i = INDEX(OutVTKDir,C_NULL_CHAR) - 1 ! if this has a c null character at the end... - if ( i > 0 ) OutVTKDir = OutVTKDir(1:I) ! remove it - - ! Get fortran pointer to C_NULL_CHAR deliniated input files as a string - call C_F_pointer(ADinputFileString_C, ADinputFileString) - call C_F_pointer(IfWinputFileString_C, IfWinputFileString) - ! For debugging the interface: if (DebugLevel > 0) then call ShowPassedData() endif + ! Get fortran pointer to C_NULL_CHAR deliniated input files as a string + call C_F_pointer(ADinputFileString_C, ADinputFileString) + call C_F_pointer(IfWinputFileString_C, IfWinputFileString) + ! Format AD input file contents InitInp%AD%RootName = OutRootName if (ADinputFilePassed==1_c_int) then @@ -561,13 +620,7 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString ! Interpolation order InterpOrder = int(InterpOrder_C, IntKi) - ! VTK outputs - WrOutputsData%WrVTK = int(WrVTK_in, IntKi) - WrOutputsData%WrVTK_Type = int(WrVTK_inType, IntKi) - WrOutputsData%VTK_dt = real(WrVTK_inDT, DbKi) - WrOutputsData%VTKNacDim = real(VTKNacDim_in, SiKi) - WrOutputsData%VTKHubrad = real(VTKHubrad_in, SiKi) - WrOutputsData%VTKRefPoint = (/ 0.0_ReKi, 0.0_ReKi, 0.0_ReKi /) !TODO: should this be an input? + ! VTK output file WrOutputsData%root = trim(OutRootName) ! Write outputs to file @@ -576,6 +629,7 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString ! Validate and set some inputs (moved to subroutine to make cleaner to read call ValidateSetInputs(ErrStat_F2,ErrMsg_F2); if(Failed()) return + call ValidateSetVTK(ErrStat_F2,ErrMsg_F2); if(Failed()) return ! Linearization ! for now, set linearization to false. Pass this in later when interface supports it @@ -591,7 +645,11 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString InitInp%storeHHVel = storeHHVel==1_c_int InitInp%WrVTK = WrOutputsData%WrVTK InitInp%WrVTK_Type = WrOutputsData%WrVTK_Type - InitInp%IW_InitInp%CompInflow = 1 ! Use InflowWind + if (externFlowField) then + InitInp%IW_InitInp%CompInflow = 2 ! Use external instance of InflowWind + else + InitInp%IW_InitInp%CompInflow = 1 ! Use InflowWind + endif !---------------------------------------------------- ! Allocate input array u and corresponding InputTimes @@ -630,7 +688,7 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString ! Set the interface meshes for motion inputs and loads output !------------------------------------------------------------- call SetupMotionLoadsInterfaceMeshes(); if (Failed()) return - ! setup meshes + ! setup VTK params if (WrOutputsData%WrVTK > 0_IntKi) then if (len_trim(OutVTKDir) <= 0) then OutVTKDir = 'vtk-ADI' @@ -777,23 +835,6 @@ subroutine ValidateSetInputs(ErrStat3,ErrMsg3) return endif - ! VTK outputs - if ( WrOutputsData%WrVTK < 0_IntKi .or. WrOutputsData%WrVTK > 2_IntKi ) then - call SetErrStat(ErrID_Fatal,"WrVTK option for writing VTK visualization files must be [0: none, 1: init only, 2: animation]",ErrStat3,ErrMsg3,RoutineName) - return - endif - if ( WrOutputsData%WrVTK_Type > 0_IntKi ) then - if ( WrOutputsData%WrVTK_Type < 1_IntKi .or. WrOutputsData%WrVTK_Type > 3_IntKi ) then - call SetErrStat(ErrID_Fatal,"WrVTK_Type option for writing VTK visualization files must be [1: surface, 2: lines, 3: both]",ErrStat3,ErrMsg3,RoutineName) - return - endif - if (WrOutputsData%VTKHubRad < 0.0_SiKi) then - call SetErrStat(ErrID_Warn,"VTKHubRad for surface visualization of hub less than zero. Setting to zero.",ErrStat3,ErrMsg3,RoutineName) - WrOutputsData%VTKHubRad = 0.0_SiKi - endif - endif - - ! check fileFmt if ( WrOutputsData%fileFmt /= idFmtNone .and. WrOutputsData%fileFmt /= idFmtAscii .and. & WrOutputsData%fileFmt /= idFmtBinary .and. WrOutputsData%fileFmt /= idFmtBoth) then @@ -813,6 +854,31 @@ subroutine ValidateSetInputs(ErrStat3,ErrMsg3) call SetErrStat(ErrID_Warn,"Requested DT_Outs is not an integer multiple of DT. Changing DT_Outs to "//trim(Num2LStr(WrOutputsData%DT_Outs))//".",ErrStat3,ErrMsg3,RoutineName) endif endif + end subroutine ValidateSetInputs + !> Validate and set some of the outputs (values must be stored before here as some might be changed) + subroutine ValidateSetVTK(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 !< temporary error status + character(ErrMsgLen), intent( out) :: ErrMsg3 !< temporary error message + + ErrStat3 = ErrID_None + ErrMsg3 = "" + + ! VTK outputs + if ( WrOutputsData%WrVTK < 0_IntKi .or. WrOutputsData%WrVTK > 2_IntKi ) then + call SetErrStat(ErrID_Fatal,"WrVTK option for writing VTK visualization files must be [0: none, 1: init only, 2: animation]",ErrStat3,ErrMsg3,RoutineName) + return + endif + if ( WrOutputsData%WrVTK_Type > 0_IntKi ) then + if ( WrOutputsData%WrVTK_Type < 1_IntKi .or. WrOutputsData%WrVTK_Type > 3_IntKi ) then + call SetErrStat(ErrID_Fatal,"WrVTK_Type option for writing VTK visualization files must be [1: surface, 2: lines, 3: both]",ErrStat3,ErrMsg3,RoutineName) + return + endif + if (WrOutputsData%VTKHubRad < 0.0_SiKi) then + call SetErrStat(ErrID_Warn,"VTKHubRad for surface visualization of hub less than zero. Setting to zero.",ErrStat3,ErrMsg3,RoutineName) + WrOutputsData%VTKHubRad = 0.0_SiKi + endif + endif + if (WrOutputsData%WrVTK > 1_IntKi) then ! only if writing during simulation is requested (ignore init or no outputs) ! If a smaller timestep between outputs is requested than the simulation runs at, change to DT if (WrOutputsData%VTK_DT < Sim%dT) then @@ -826,7 +892,7 @@ subroutine ValidateSetInputs(ErrStat3,ErrMsg3) call SetErrStat(ErrID_Warn,"Requested VTK_DT is not an integer multiple of DT. Changing VTK_DT to "//trim(Num2LStr(WrOutputsData%VTK_DT))//".",ErrStat3,ErrMsg3,RoutineName) endif endif - end subroutine ValidateSetInputs + end subroutine ValidateSetVTK !> allocate data storage for file outputs subroutine SetupFileOutputs() @@ -882,7 +948,6 @@ subroutine ShowPassedData() call WrScr(" IfWinputFileString_C "//trim(IfWinputFileString(1:i))) endif call WrScr(" OutRootName "//trim(OutRootName) ) - call WrScr(" OutVTKDir "//trim(OutVTKDir) ) call WrScr(" Interpolation") call WrScr(" InterpOrder_C "//trim(Num2LStr( InterpOrder_C )) ) call WrScr(" Time variables") @@ -894,11 +959,10 @@ subroutine ShowPassedData() call WrScr(" Flags") TmpFlag="F"; if (storeHHVel==1_c_int) TmpFlag="T" call WrScr(" storeHHVel "//TmpFlag ) - call WrScr(" WrVTK_in "//trim(Num2LStr( WrVTK_in )) ) - call WrScr(" WrVTK_inType "//trim(Num2LStr( WrVTK_inType )) ) - call WrScr(" WrVTK_inDT "//trim(Num2LStr( WrVTK_inDT )) ) call WrScr("-----------------------------------------------------------") end subroutine ShowPassedData +!FIXME: add a ShowReturnData here! + !> This subroutine sets the interface meshes to map to the input motions to the AD !! meshes @@ -1085,6 +1149,7 @@ logical function Failed() call SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C) endif end function Failed +!FIXME: add a showpassed/return routine END SUBROUTINE ADI_C_CalcOutput !=============================================================================================================== @@ -1751,15 +1816,13 @@ subroutine ADI_C_SetRotorMotion( iWT_c, & enddo ! Transfer motions to input meshes - do iWT=1,Sim%NumTurbines - call Set_MotionMesh(iWT, ErrStat_F2, ErrMsg_F2); if (Failed()) return - call AD_SetInputMotion( iWT, ADI_u, & - HubPos_C, HubOri_C, HubVel_C, HubAcc_C, & - NacPos_C, NacOri_C, NacVel_C, NacAcc_C, & - BldRootPos_C, BldRootOri_C, BldRootVel_C, BldRootAcc_C, & - ErrStat_F2, ErrMsg_F2 ) ! transfer input motion mesh to u(1) meshes - if (Failed()) return - enddo + call Set_MotionMesh(iWT, ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AD_SetInputMotion( iWT, ADI_u, & + HubPos_C, HubOri_C, HubVel_C, HubAcc_C, & + NacPos_C, NacOri_C, NacVel_C, NacAcc_C, & + BldRootPos_C, BldRootOri_C, BldRootVel_C, BldRootAcc_C, & + ErrStat_F2, ErrMsg_F2 ) ! transfer input motion mesh to u(1) meshes + if (Failed()) return ! Set error status call SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C) @@ -2517,34 +2580,6 @@ subroutine WrVTK_Ground (RefPoint, HalfLengths, FileRootName, errStat, errMsg) end subroutine WrVTK_Ground -!-------------------------------------------------------------------- -!> Set some temporary data storage arrays to simplify data conversion -subroutine SetTempStorage(ErrStat,ErrMsg) - INTEGER(IntKi), intent(out) :: errStat !< Indicates whether an error occurred (see NWTC_Library) - character(*), intent(out) :: errMsg !< Error message associated with the errStat - INTEGER(IntKi) :: ErrStat_F2 - CHARACTER(ErrMsgLen) :: ErrMsg_F2 - character(*), parameter :: RoutineName = 'SetTempStorage' !< for error handling - ErrStat = ErrID_None - ErrMsg = "" - if (.not. allocated(NumMeshPts)) then - ErrStat = ErrID_Fatal - ErrMSg = "Pre-Init has not been called yet" - return - endif - if (minval(NumMeshPts) < 0) then - ErrStat = ErrID_Fatal - ErrMSg = "ADI_C_SetupRotor haven't been called for all rotors" - return - endif - -contains - logical function Failed() - CALL SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat, ErrMsg, RoutineName ) - Failed = ErrStat >= AbortErrLev - end function Failed -end subroutine SetTempStorage - !-------------------------------------------------------------------- !> Don't leave junk in memory. So destroy meshes and mappings. subroutine ClearTmpStorage() @@ -2570,7 +2605,6 @@ subroutine ClearMeshArr1(MeshName) enddo deallocate(MeshName) end subroutine ClearMeshArr1 - subroutine ClearMeshMapArr2(MapName) type(MeshMapType), allocatable :: MapName(:,:) integer :: i,j @@ -2581,8 +2615,85 @@ subroutine ClearMeshMapArr2(MapName) enddo deallocate(MapName) end subroutine ClearMeshMapArr2 - end subroutine ClearTmpStorage +!> return the pointer to the WaveField data +subroutine ADI_C_GetFlowFieldPointer(FlowFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='ADI_C_GetFlowFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: ADI_C_GetFlowFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: ADI_C_GetFlowFieldPointer +#endif + type(c_ptr), intent( out) :: FlowFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'ADI_C_GetFlowFieldPointer' + ErrStat = ErrID_None + ErrMSg = "" + if (associated(ADI%m%IW%p%FlowField)) then + FlowFieldPointer_C = C_LOC(ADI%m%IW%p%FlowField) + else + FlowFieldPointer_C = C_NULL_PTR + call SetErrStat(ErrID_Fatal,"Pointer to FlowField data not valid: data not initialized",ErrStat,ErrMsg,RoutineName) + endif + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: ADI_C_GetFlowFieldPointer") + call WrScr(" --------------------------------------------------------") + call WrScr(" FlowFieldPointer_C -> "//trim(Num2LStr(loc(ADI%m%IW%p%FlowField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine + + +!> set the pointer to the FlowField data +subroutine ADI_C_SetFlowFieldPointer(FlowFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='ADI_C_SetFlowFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: ADI_C_SetFlowFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: ADI_C_SetFlowFieldPointer +#endif + type(c_ptr), intent(in ) :: FlowFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'ADI_C_SetFlowFieldPointer' + ErrStat = ErrID_None + ErrMSg = "" + ! check if externFlowField expected + if (.not. externFlowField) then + call SetErrStat(ErrID_Severe,"External flowfield not expected. Set externFlowField_in=1 in call to ADI_C_PreInit prior to calling ADI_C_SetFlowFieldPointer.",ErrStat,ErrMsg,RoutineName) + endif + ! set pointer + call C_F_POINTER(FlowFieldPointer_C, InitInp%FlowField) + if (associated(InitInp%FlowField)) then + ! basic sanity check + if (InitInp%FlowField%FieldType <= 0_IntKi) then + call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or FlowField not initialized",ErrStat,ErrMsg,RoutineName) + else + FlowFieldPtrSet = .true. + endif + else + call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or FlowField not initialized.",ErrStat,ErrMsg,RoutineName) + endif + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: ADI_C_SetFlowFieldPointer") + call WrScr(" --------------------------------------------------------") + call WrScr(" FlowFieldPointer_C <- "//trim(Num2LStr(loc(InitInp%FlowField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine + + END MODULE AeroDyn_Inflow_C_BINDING diff --git a/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt b/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt index eb44d2e5e..ffd1e56cb 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt @@ -26,13 +26,13 @@ typedef ^ ^ InflowWind_ParameterType p typedef ^ ^ InflowWind_MiscVarType m - - - "Misc/optimization variables" typedef ^ ^ InflowWind_InputType u - - - "Array of inputs associated with InputTimes" typedef ^ ^ InflowWind_OutputType y - - - "System outputs" -typedef ^ ^ IntKi CompInflow - - - "0=Steady Wind, 1=InflowWind" "-" +typedef ^ ^ IntKi CompInflow - - - "0=Steady Wind, 1=InflowWind, 2=External IfW (ADI c-bind only)" "-" typedef ^ ^ ReKi HWindSpeed - - - "RefHeight Wind speed" typedef ^ ^ ReKi RefHt - - - "RefHeight" typedef ^ ^ ReKi PLExp - - - "PLExp" # ..... InflowWind Input data ..................................................................................................... typedef ^ ADI_IW_InputData Character(1024) InputFile - - - "Name of InfloWind input file" - -typedef ^ ^ IntKi CompInflow - - - "0=Steady Wind, 1=InflowWind" "-" +typedef ^ ^ IntKi CompInflow - - - "0=Steady Wind, 1=InflowWind, 2=External IfW (ADI c-bind only)" "-" typedef ^ ^ ReKi HWindSpeed - - - "RefHeight Wind speed" typedef ^ ^ ReKi RefHt - - - "RefHeight" typedef ^ ^ ReKi PLExp - - - "PLExp" @@ -54,6 +54,8 @@ typedef ^ ^ Character(1024) RootName typedef ^ ^ Logical storeHHVel - .false. - "If True, hub height velocity will be computed by infow wind" - typedef ^ ^ IntKi WrVTK - 0 - "0= no vtk, 1=init only, 2=animation" "-" typedef ^ ^ IntKi WrVTK_Type - 1 - "Flag for VTK output type (1=surface, 2=line, 3=both)" - +typedef ^ ^ ReKi WtrDpth - - - "Water depth" m +typedef ^ ^ FlowFieldType *FlowField - - - "Pointer of InflowWinds flow field data type (from external IfW instance -- used with c-binding)" - # ..... InitOut ................................................................................................................... diff --git a/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 b/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 index e5ddb8340..c50a2238e 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 @@ -45,7 +45,7 @@ MODULE AeroDyn_Inflow_Types TYPE(InflowWind_MiscVarType) :: m !< Misc/optimization variables [-] TYPE(InflowWind_InputType) :: u !< Array of inputs associated with InputTimes [-] TYPE(InflowWind_OutputType) :: y !< System outputs [-] - INTEGER(IntKi) :: CompInflow = 0_IntKi !< 0=Steady Wind, 1=InflowWind [-] + INTEGER(IntKi) :: CompInflow = 0_IntKi !< 0=Steady Wind, 1=InflowWind, 2=External IfW (ADI c-bind only) [-] REAL(ReKi) :: HWindSpeed = 0.0_ReKi !< RefHeight Wind speed [-] REAL(ReKi) :: RefHt = 0.0_ReKi !< RefHeight [-] REAL(ReKi) :: PLExp = 0.0_ReKi !< PLExp [-] @@ -54,7 +54,7 @@ MODULE AeroDyn_Inflow_Types ! ========= ADI_IW_InputData ======= TYPE, PUBLIC :: ADI_IW_InputData Character(1024) :: InputFile !< Name of InfloWind input file [-] - INTEGER(IntKi) :: CompInflow = 0_IntKi !< 0=Steady Wind, 1=InflowWind [-] + INTEGER(IntKi) :: CompInflow = 0_IntKi !< 0=Steady Wind, 1=InflowWind, 2=External IfW (ADI c-bind only) [-] REAL(ReKi) :: HWindSpeed = 0.0_ReKi !< RefHeight Wind speed [-] REAL(ReKi) :: RefHt = 0.0_ReKi !< RefHeight [-] REAL(ReKi) :: PLExp = 0.0_ReKi !< PLExp [-] @@ -76,6 +76,8 @@ MODULE AeroDyn_Inflow_Types LOGICAL :: storeHHVel = .false. !< If True, hub height velocity will be computed by infow wind [-] INTEGER(IntKi) :: WrVTK = 0 !< 0= no vtk, 1=init only, 2=animation [-] INTEGER(IntKi) :: WrVTK_Type = 1 !< Flag for VTK output type (1=surface, 2=line, 3=both) [-] + REAL(ReKi) :: WtrDpth = 0.0_ReKi !< Water depth [m] + TYPE(FlowFieldType) , POINTER :: FlowField => NULL() !< Pointer of InflowWinds flow field data type (from external IfW instance -- used with c-binding) [-] END TYPE ADI_InitInputType ! ======================= ! ========= ADI_InitOutputType ======= @@ -378,6 +380,7 @@ subroutine ADI_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, ErrSt integer(IntKi), intent(in ) :: CtrlCode integer(IntKi), intent( out) :: ErrStat character(*), intent( out) :: ErrMsg + integer(B4Ki) :: LB(0), UB(0) integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'ADI_CopyInitInput' @@ -393,6 +396,8 @@ subroutine ADI_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, ErrSt DstInitInputData%storeHHVel = SrcInitInputData%storeHHVel DstInitInputData%WrVTK = SrcInitInputData%WrVTK DstInitInputData%WrVTK_Type = SrcInitInputData%WrVTK_Type + DstInitInputData%WtrDpth = SrcInitInputData%WtrDpth + DstInitInputData%FlowField => SrcInitInputData%FlowField end subroutine subroutine ADI_DestroyInitInput(InitInputData, ErrStat, ErrMsg) @@ -408,12 +413,14 @@ subroutine ADI_DestroyInitInput(InitInputData, ErrStat, ErrMsg) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call ADI_DestroyIW_InputData(InitInputData%IW_InitInp, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + nullify(InitInputData%FlowField) end subroutine subroutine ADI_PackInitInput(RF, Indata) type(RegFile), intent(inout) :: RF type(ADI_InitInputType), intent(in) :: InData character(*), parameter :: RoutineName = 'ADI_PackInitInput' + logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return call AD_PackInitInput(RF, InData%AD) call ADI_PackIW_InputData(RF, InData%IW_InitInp) @@ -421,6 +428,14 @@ subroutine ADI_PackInitInput(RF, Indata) call RegPack(RF, InData%storeHHVel) call RegPack(RF, InData%WrVTK) call RegPack(RF, InData%WrVTK_Type) + call RegPack(RF, InData%WtrDpth) + call RegPack(RF, associated(InData%FlowField)) + if (associated(InData%FlowField)) then + call RegPackPointer(RF, c_loc(InData%FlowField), PtrInIndex) + if (.not. PtrInIndex) then + call IfW_FlowField_PackFlowFieldType(RF, InData%FlowField) + end if + end if if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -428,6 +443,11 @@ subroutine ADI_UnPackInitInput(RF, OutData) type(RegFile), intent(inout) :: RF type(ADI_InitInputType), intent(inout) :: OutData character(*), parameter :: RoutineName = 'ADI_UnPackInitInput' + integer(B4Ki) :: LB(0), UB(0) + integer(IntKi) :: stat + logical :: IsAllocAssoc + integer(B8Ki) :: PtrIdx + type(c_ptr) :: Ptr if (RF%ErrStat /= ErrID_None) return call AD_UnpackInitInput(RF, OutData%AD) ! AD call ADI_UnpackIW_InputData(RF, OutData%IW_InitInp) ! IW_InitInp @@ -435,6 +455,25 @@ subroutine ADI_UnPackInitInput(RF, OutData) call RegUnpack(RF, OutData%storeHHVel); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%WrVTK); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%WrVTK_Type); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WtrDpth); if (RegCheckErr(RF, RoutineName)) return + if (associated(OutData%FlowField)) deallocate(OutData%FlowField) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackPointer(RF, Ptr, PtrIdx); if (RegCheckErr(RF, RoutineName)) return + if (c_associated(Ptr)) then + call c_f_pointer(Ptr, OutData%FlowField) + else + allocate(OutData%FlowField,stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%FlowField.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + RF%Pointers(PtrIdx) = c_loc(OutData%FlowField) + call IfW_FlowField_UnpackFlowFieldType(RF, OutData%FlowField) ! FlowField + end if + else + OutData%FlowField => null() + end if end subroutine subroutine ADI_CopyInitOutput(SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg) diff --git a/modules/hydrodyn/src/HydroDyn_C_Binding.f90 b/modules/hydrodyn/src/HydroDyn_C_Binding.f90 index e3f753faf..efa64402d 100644 --- a/modules/hydrodyn/src/HydroDyn_C_Binding.f90 +++ b/modules/hydrodyn/src/HydroDyn_C_Binding.f90 @@ -691,6 +691,11 @@ end subroutine SetMotionLoadsInterfaceMeshes !! If more than one input node was passed in, but only a single HD node !! exits (single Morison or single WAMIT), then give error that too many !! nodes passed. + !! More than one node is passed in (NumNodePts>1) indicates that the structure + !! is modeled as flexible. This requires more than one destination node on + !! either the Morison or WAMIT meshes. Note that some nodes may be + !! co-located, so checking that the total number of nodes is the same does + !! not work. subroutine CheckNodes(ErrStat3,ErrMsg3) integer(IntKi), intent( out) :: ErrStat3 !< temporary error status character(ErrMsgLen), intent( out) :: ErrMsg3 !< temporary error message @@ -698,19 +703,19 @@ subroutine CheckNodes(ErrStat3,ErrMsg3) ErrMsg3 = "" if ( NumNodePts > 1 ) then if ( HD%u(1)%Morison%Mesh%Committed .and. HD%u(1)%WAMITMesh%Committed ) then - if ( (HD%u(1)%Morison%Mesh%Nnodes + HD%u(1)%WAMITMesh%Nnodes) < NumNodePts ) then + if ( (HD%u(1)%Morison%Mesh%Nnodes + HD%u(1)%WAMITMesh%Nnodes) < 2_IntKi) then ErrStat3 = ErrID_Fatal - ErrMsg3 = "More nodes passed into library than exist in HydroDyn model" + ErrMsg3 = "More than one node passed into library, but only one HydroDyn node exists." endif elseif ( HD%u(1)%Morison%Mesh%Committed ) then ! No WAMIT - if ( HD%u(1)%Morison%Mesh%Nnodes < NumNodePts ) then + if ( HD%u(1)%Morison%Mesh%Nnodes < 2_IntKi ) then ErrStat3 = ErrID_Fatal - ErrMsg3 = "More nodes passed into library than exist in HydroDyn model Morison mesh" + ErrMsg3 = "More than one node passed into library, but only one HydroDyn node exists on Morison mesh." endif elseif ( HD%u(1)%WAMITMesh%Committed ) then ! No Morison - if ( HD%u(1)%WAMITMesh%Nnodes < NumNodePts ) then + if ( HD%u(1)%WAMITMesh%Nnodes < 2_IntKi ) then ErrStat3 = ErrID_Fatal - ErrMsg3 = "More nodes passed into library than exist in HydroDyn model WAMIT mesh" + ErrMsg3 = "More than one node passed into library, but only one HydroDyn node exists on the WAMIT mesh." endif endif endif diff --git a/modules/inflowwind/src/IfW_C_Binding.f90 b/modules/inflowwind/src/IfW_C_Binding.f90 index b3bc732f6..79df59b10 100644 --- a/modules/inflowwind/src/IfW_C_Binding.f90 +++ b/modules/inflowwind/src/IfW_C_Binding.f90 @@ -19,18 +19,23 @@ !********************************************************************************************************************************** MODULE InflowWind_C_BINDING - USE ISO_C_BINDING - USE InflowWind - USE InflowWind_Subs, only: MaxOutPts - USE InflowWind_Types - USE NWTC_Library - USE VersionInfo + use ISO_C_BINDING + use IfW_FlowField, only: IfW_FlowField_GetVelAcc + use InflowWind + use InflowWind_Subs, only: MaxOutPts + use InflowWind_Types + use NWTC_Library + use VersionInfo + use NWTC_C_Binding, only: ErrMsgLen_C, IntfStrLen, SetErrStat_F2C IMPLICIT NONE PUBLIC :: IfW_C_Init PUBLIC :: IfW_C_CalcOutput PUBLIC :: IfW_C_End + PUBLIC :: IfW_C_GetFlowFieldPointer + PUBLIC :: IfW_C_SetFlowFieldPointer + PUBLIC :: IfW_C_GetWindVel !------------------------------------------------------------------------------------ ! Version info for display @@ -58,43 +63,14 @@ MODULE InflowWind_C_BINDING 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 - - - CONTAINS -!> This routine sets the error status in C_CHAR for export to calling code. -!! Make absolutely certain that we do not overrun the end of ErrMsg_C. That is hard coded to 1025, -!! but ErrMsgLen is set in the nwtc_library, and could change without updates here. We don't want an -!! inadvertant buffer overrun -- that can lead to bad things. -subroutine SetErr(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) - integer, intent(in ) :: ErrStat !< aggregated error message (fortran type) - character(ErrMsgLen), intent(in ) :: ErrMsg !< aggregated error message (fortran type) - integer(c_int), intent( out) :: ErrStat_C - character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) - ErrStat_C = ErrStat ! We will send back the same error status that is used in OpenFAST - if (ErrMsgLen > ErrMsgLen_C-1) then ! If ErrMsgLen is > the space in ErrMsg_C, do not copy everything over - ErrMsg_C = TRANSFER( trim(ErrMsg(1:ErrMsgLen_C-1))//C_NULL_CHAR, ErrMsg_C ) - else - ErrMsg_C = TRANSFER( trim(ErrMsg)//C_NULL_CHAR, ErrMsg_C ) - endif -end subroutine SetErr - - !=============================================================================================================== !--------------------------------------------- IFW INIT -------------------------------------------------------- !=============================================================================================================== -SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStringLength_C, OutRootName_C, & - NumWindPts_C, DT_C, DebugLevel_in, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, & +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 @@ -129,6 +105,9 @@ SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStri ErrStat = ErrID_None ErrMsg = "" + ! clear out any leftover memory that might be allocated from a previous call + call MemClear(ErrStat2, ErrMsg2); if (Failed()) return + CALL NWTC_Init( ProgNameIn=version%Name ) CALL DispCopyrightLicense( version%Name ) CALL DispCompileRuntimeInfo( version%Name ) @@ -160,9 +139,7 @@ SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStri endif ! For debugging the interface: - if (DebugLevel > 0) then - call ShowPassedData() - endif + if (DebugLevel > 0) call ShowPassedData() ! Get fortran pointer to C_NULL_CHAR deliniated input file as a string CALL C_F_pointer(IfWinputFileString_C, IfWinputFileString) @@ -217,7 +194,8 @@ SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStri call Cleanup() - call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + CONTAINS logical function Failed() @@ -225,7 +203,7 @@ logical function Failed() Failed = ErrStat >= AbortErrLev if (Failed) then call Cleanup() - call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) endif end function Failed subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here @@ -238,8 +216,7 @@ subroutine ShowPassedData() integer :: i,j call WrSCr("") call WrScr("-----------------------------------------------------------") - call WrScr("Interface debugging: Variables passed in through interface") - call WrScr(" IfW_C_Init") + call WrScr("Interface debugging: IfW_C_Init") call WrScr(" --------------------------------------------------------") call WrScr(" FileInfo") TmpFlag="F"; if (IfWinputFilePassed==1_c_int) TmpFlag="T" @@ -260,15 +237,14 @@ END SUBROUTINE IfW_C_Init !--------------------------------------------- IFW CALCOUTPUT -------------------------------------------------- !=============================================================================================================== -SUBROUTINE IfW_C_CalcOutput(Time_C,Positions_C,Velocities_C,OutputChannelValues_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_CalcOutput') - IMPLICIT NONE +SUBROUTINE IfW_C_CalcOutput(Time_C,Pos_C,Vel_C,OutputChannelValues_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_CalcOutput') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_CalcOutput !GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_CalcOutput #endif REAL(C_DOUBLE) , INTENT(IN ) :: Time_C - REAL(C_FLOAT) , INTENT(IN ) :: Positions_C(3*InitInp%NumWindPoints) - REAL(C_FLOAT) , INTENT( OUT) :: Velocities_C(3*InitInp%NumWindPoints) + REAL(C_FLOAT) , INTENT(IN ) :: Pos_C(3*InitInp%NumWindPoints) + REAL(C_FLOAT) , INTENT( OUT) :: Vel_C(3*InitInp%NumWindPoints) REAL(C_FLOAT) , INTENT( OUT) :: OutputChannelValues_C(p%NumOuts) INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) @@ -285,52 +261,238 @@ SUBROUTINE IfW_C_CalcOutput(Time_C,Positions_C,Velocities_C,OutputChannelValues_ ErrStat = ErrID_None ErrMsg = "" + ! Interface debugging + if (DebugLevel > 0) call ShowPassedData() + ! Convert the inputs from C to Fortran Time = REAL(Time_C,DbKi) - InputData%PositionXYZ = reshape( real(Positions_C,ReKi), (/3, InitInp%NumWindPoints/) ) + InputData%PositionXYZ = reshape( real(Pos_C,ReKi), (/3, InitInp%NumWindPoints/) ) ! Call the main subroutine InflowWind_CalcOutput to get the velocities CALL InflowWind_CalcOutput( Time, InputData, p, ContStates, DiscStates, ConstrStates, OtherStates, y, m, ErrStat2, ErrMsg2 ) if (Failed()) return ! Get velocities out of y and flatten them (still in same spot in memory) - Velocities_C = reshape( REAL(y%VelocityUVW, C_FLOAT), (/3*InitInp%NumWindPoints/) ) ! VelocityUVW is 2D array of ReKi (might need reshape or make into pointer); size [3,N] + Vel_C = reshape( REAL(y%VelocityUVW, C_FLOAT), (/3*InitInp%NumWindPoints/) ) ! VelocityUVW is 2D array of ReKi (might need reshape or make into pointer); size [3,N] + + ! Interface debugging + if (DebugLevel > 0) call ShowReturnData() ! Get the output channel info out of y OutputChannelValues_C = REAL(y%WriteOutput, C_FLOAT) - call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) CONTAINS logical function Failed() CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) Failed = ErrStat >= AbortErrLev - if (Failed) call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + if (Failed) call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) end function Failed + !> This subroutine prints out all the variables that are passed in. Use this only + !! for debugging the interface on the Fortran side. + subroutine ShowPassedData() + integer(IntKi) :: i + character(4) :: TmpCh + call WrSCr("") + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: IfW_C_CalcOutput") + call WrScr(" --------------------------------------------------------") + call WrScr(" Time_C -> "//trim(Num2LStr(Time_C))) + do i=1,InitInp%NumWindPoints + write(TmpCh, '(i4)') i + call WrScr(" Pos_C("//TmpCh//") -> ("//trim(Num2LStr(Pos_C((i-1)*3+1)))//","//trim(Num2LStr(Pos_C((i-1)*3+2)))//","//trim(Num2LStr(Pos_C((i-1)*3+3)))//")") + enddo + end subroutine ShowPassedData + subroutine ShowReturnData() + integer(IntKi) :: i + character(4) :: TmpCh + do i=1,InitInp%NumWindPoints + call WrScr(" Vel_C("//TmpCh//") <- ("//trim(Num2LStr(Vel_C((i-1)*3+1)))//","//trim(Num2LStr(Vel_C((i-1)*3+2)))//","//trim(Num2LStr(Vel_C((i-1)*3+3)))//")") + enddo + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData END SUBROUTINE IfW_C_CalcOutput !=============================================================================================================== !--------------------------------------------------- IFW END --------------------------------------------------- !=============================================================================================================== - -SUBROUTINE IfW_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_End') - IMPLICIT NONE +subroutine IfW_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_End') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_End !GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_End #endif - INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C - CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat,ErrStat2 + character(ErrMsgLen) :: ErrMsg,ErrMsg2 + character(*), parameter :: RoutineName = 'IfW_C_End' - ! Local variables - INTEGER :: ErrStat - CHARACTER(ErrMsgLen) :: ErrMsg + ErrStat = ErrID_None + ErrMsg = "" ! Call the main subroutine InflowWind_End - CALL InflowWind_End( InputData, p, ContStates, DiscStates, ConstrStates, OtherStates, y, m, ErrStat, ErrMsg ) + call InflowWind_End( InputData, p, ContStates, DiscStates, ConstrStates, OtherStates, y, m, ErrStat2, ErrMsg2 ) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + ! Clear extra memory within library + call MemClear(ErrStat2, ErrMsg2) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) +end subroutine IfW_C_End + + +!> basic routine to get the wind velocity at a single point in time and space +subroutine IfW_C_GetWindVel(Time_C,Pos_C,Vel_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_GetWindVel') + real(c_double), intent(in ) :: Time_C + real(c_float), intent(in ) :: Pos_C(3) + real(c_float), intent( out) :: Vel_C(3) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + real(dbki) :: Time + integer :: ErrStat, ErrStat2 + character(ErrMsgLen) :: ErrMsg, ErrMsg2 + character(*), parameter :: RoutineName = 'IfW_C_GetWindVel' + integer(intKi) :: StartNode + real(ReKi) :: Pos(3,1), Vel(3,1), PosOffset(3) + real(ReKi), allocatable :: NoAcc(:,:) + + ErrStat = ErrID_None + ErrMsg = "" + + ! Interface debugging + if (DebugLevel > 0) call ShowPassedData() + + if (.not. associated(p%FlowField)) then + ErrStat = ErrID_Fatal + ErrMsg = "Invalid pointer to FlowField data. Is the data initialized?" + Vel_C = 0.0_c_float + endif + + ! Initialize node. Since this is standalone, set to 1. + StartNode = 1 + ! no offset + PosOffset = 0.0_ReKi + + ! Convert the inputs from C to Fortran + Time = REAL(Time_C,DbKi) + Pos(1:3,1) = real(Pos_C,ReKi) + + ! call wind routine to get single point velocity + call IfW_FlowField_GetVelAcc(p%FlowField, StartNode, Time, Pos, Vel, NoAcc, ErrStat2, ErrMsg2) + if (Failed()) return + Vel_C = real(Vel(1:3,1), c_float) + + ! Interface debugging + if (DebugLevel > 0) call ShowReturnData() + + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + if (Failed) call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end function Failed + !> This subroutine prints out all the variables that are passed in. Use this only + !! for debugging the interface on the Fortran side. + subroutine ShowPassedData() + call WrSCr("") + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: IfW_C_GetWindVel") + call WrScr(" --------------------------------------------------------") + call WrScr(" Time_C -> "//trim(Num2LStr(Time_C))) + call WrScr(" Pos_C -> ("//trim(Num2LStr(Pos_C(1)))//","//trim(Num2LStr(Pos_C(2)))//","//trim(Num2LStr(Pos_C(3)))//")") + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr(" Vel_C <- ("//trim(Num2LStr(Vel_C(1)))//","//trim(Num2LStr(Vel_C(2)))//","//trim(Num2LStr(Vel_C(3)))//")") + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData +end subroutine IfW_C_GetWindVel + + +!> clear local memory that isn't stored in `_End` routine +subroutine MemClear(ErrStat,ErrMsg) + integer, intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + call InflowWind_DestroyInitInput( InitInp, ErrStat, ErrMsg) + call InflowWind_DestroyInitOutput(InitOutData, ErrStat, ErrMsg) +end subroutine MemClear + + + +!> return the pointer to the WaveField data +subroutine IfW_C_GetFlowFieldPointer(FlowFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_GetFlowFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_GetFlowFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_GetFlowFieldPointer +#endif + type(c_ptr), intent( out) :: FlowFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'IfW_C_GetFlowFieldPointer' + ErrStat = ErrID_None + ErrMSg = "" + if (associated(p%FlowField)) then + FlowFieldPointer_C = C_LOC(p%FlowField) + else + FlowFieldPointer_C = C_NULL_PTR + call SetErrStat(ErrID_Fatal,"Pointer to FlowField data not valid: data not initialized",ErrStat,ErrMsg,RoutineName) + endif + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: IfW_C_GetFlowFieldPointer") + call WrScr(" --------------------------------------------------------") + call WrScr(" FlowFieldPointer_C -> "//trim(Num2LStr(loc(p%FlowField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine + + +!FIXME: this will require changes to IfW_C_Init to instantiate an empty IfW instance +! so before exposing this publicly, the initialization should be updated. +!> set the pointer to the FlowField data +subroutine IfW_C_SetFlowFieldPointer(FlowFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_SetFlowFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_SetFlowFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_SetFlowFieldPointer +#endif + type(c_ptr), intent(in ) :: FlowFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'IfW_C_SetFlowFieldPointer' + ErrStat = ErrID_None + ErrMSg = "" + call C_F_POINTER(FlowFieldPointer_C, p%FlowField) + if (associated(p%FlowField)) then + ! basic sanity check + if (p%FlowField%FieldType <= 0_IntKi) then + call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or FlowField not initialized",ErrStat,ErrMsg,RoutineName) + endif + else + call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or FlowField not initialized",ErrStat,ErrMsg,RoutineName) + endif + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: IfW_C_SetFlowFieldPointer") + call WrScr(" --------------------------------------------------------") + call WrScr(" FlowFieldPointer_C <- "//trim(Num2LStr(loc(p%FlowField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine - call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) -END SUBROUTINE IfW_C_End END MODULE diff --git a/modules/nwtc-library/src/NWTC_C_Binding.f90 b/modules/nwtc-library/src/NWTC_C_Binding.f90 index fae7dfd2d..fa95c356c 100644 --- a/modules/nwtc-library/src/NWTC_C_Binding.f90 +++ b/modules/nwtc-library/src/NWTC_C_Binding.f90 @@ -38,12 +38,12 @@ MODULE NWTC_C_Binding !> This routine sets the error status in C_CHAR for export to calling code. SUBROUTINE SetErrStat_F2C(ErrStat_F, ErrMsg_F, ErrStat_C, ErrMsg_C) - INTEGER, INTENT(IN ) :: ErrStat_F !< aggregated error status (fortran type) + INTEGER(IntKi), INTENT(IN ) :: ErrStat_F !< aggregated error status (fortran type) CHARACTER(ErrMsgLen), INTENT(IN ) :: ErrMsg_F !< aggregated error message (fortran type) INTEGER(C_INT), INTENT( OUT) :: ErrStat_C CHARACTER(KIND=C_CHAR), INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) - ErrStat_C = ErrStat_F ! We will send back the same error status that is used in OpenFAST + ErrStat_C = int(ErrStat_F,c_int) ! We will send back the same error status that is used in OpenFAST if (ErrMsgLen > ErrMsgLen_C-1) then ! If ErrMsgLen is > the space in ErrMsg_C, do not copy everything over ErrMsg_C = TRANSFER( TRIM(ErrMsg_F(1:ErrMsgLen_C-1))//C_NULL_CHAR, ErrMsg_C ) else diff --git a/modules/seastate/src/SeaSt_WaveField.f90 b/modules/seastate/src/SeaSt_WaveField.f90 index 43be7527a..04d66270d 100644 --- a/modules/seastate/src/SeaSt_WaveField.f90 +++ b/modules/seastate/src/SeaSt_WaveField.f90 @@ -12,6 +12,7 @@ MODULE SeaSt_WaveField PUBLIC WaveField_GetNodeWaveElev1 PUBLIC WaveField_GetNodeWaveElev2 PUBLIC WaveField_GetNodeTotalWaveElev +PUBLIC WaveField_GetMinMaxWaveElevEstimate PUBLIC WaveField_GetNodeWaveNormal PUBLIC WaveField_GetNodeWaveKin PUBLIC WaveField_GetNodeWaveVelAcc @@ -109,6 +110,37 @@ logical function Failed() END FUNCTION WaveField_GetNodeTotalWaveElev +!> Gives an estimate of the min and max wave elevation. It will overshoot for second order +subroutine WaveField_GetMinMaxWaveElevEstimate( WaveField, MinElev, MaxElev, ErrStat, ErrMsg ) + type(SeaSt_WaveFieldType), pointer, intent(in ) :: WaveField + real(SiKi), intent( out) :: MinElev + real(SiKi), intent( out) :: MaxElev + integer(IntKi), intent( out) :: ErrStat ! Error status of the operation + character(*), intent( out) :: ErrMsg ! Error message if errStat /= ErrID_None + character(*), parameter :: RoutineName = 'WaveField_GetMinMaxWaveElevEstimate' + + ErrStat = ErrID_None + ErrMsg = "" + MinElev = 0.0_SiKi + MaxElev = 0.0_SiKi + + ! Check that data exists + if (.not. associated(WaveField)) then + ErrStat = ErrID_Fatal + ErrMsg = trim(RoutineName)//": WaveField data does not exist." + return + endif + + if (allocated(WaveField%WaveElev1)) then + MinElev = minval(WaveField%WaveElev1) + MaxElev = maxval(WaveField%WaveElev1) + endif + if (allocated(WaveField%WaveElev2)) then + MinElev = MinElev + minval(WaveField%WaveElev2) + MaxElev = MaxElev + maxval(WaveField%WaveElev2) + endif +end subroutine WaveField_GetMinMaxWaveElevEstimate + SUBROUTINE WaveField_GetNodeWaveNormal( WaveField, WaveField_m, Time, pos, n, ErrStat, ErrMsg ) type(SeaSt_WaveFieldType), intent(in ) :: WaveField type(GridInterp_MiscVarType), intent(inout) :: WaveField_m diff --git a/modules/seastate/src/SeaState_C_Binding.f90 b/modules/seastate/src/SeaState_C_Binding.f90 index 640740dbe..1a7a6d81d 100644 --- a/modules/seastate/src/SeaState_C_Binding.f90 +++ b/modules/seastate/src/SeaState_C_Binding.f90 @@ -40,6 +40,10 @@ MODULE SeaState_C_Binding PUBLIC :: SeaSt_C_GetFluidVelAcc PUBLIC :: SeaSt_C_GetSurfElev PUBLIC :: SeaSt_C_GetSurfNorm + PUBLIC :: SeaSt_C_GetElevMinMaxEstimate + PUBLIC :: SeaSt_C_GetDens + PUBLIC :: SeaSt_C_GetDpth + PUBLIC :: SeaSt_C_GetMSL2SWL !------------------------------------------------------------------------------------ ! Debugging: DebugLevel -- passed at PreInit @@ -229,6 +233,13 @@ subroutine SeaSt_C_Init(InputFile_C, OutRootName_C, TimeInterval_C, TMax_C, Wave ! Initialize error handling ErrStat = ErrID_None ErrMsg = "" + ErrStat_C = ErrID_None + ErrMsg_C = c_null_char + + ! Initialize vars in case of early return + NumChannels_C = 0_IntKi + OutputChannelNames_C = c_null_char + OutputChannelUnits_C = c_null_char call NWTC_Init( ProgNameIn= SeaSt_ProgDesc%Name ) call DispCopyrightLicense( SeaSt_ProgDesc%Name ) @@ -438,23 +449,21 @@ subroutine SeaSt_C_GetWaveFieldPointer(WaveFieldPointer_C,ErrStat_C,ErrMsg_C) BI integer :: ErrStat character(ErrMsgLen) :: ErrMsg character(*), parameter :: RoutineName = 'SeaSt_C_GetWaveFieldPointer' + logical :: valid ErrStat = ErrID_None ErrMSg = "" - if (associated(p%WaveField)) then - WaveFieldPointer_C = C_LOC(p%WaveField) - else - WaveFieldPointer_C = C_NULL_PTR - call SetErrStat(ErrID_Fatal,"Pointer to WaveField data not valid: data not initialized",ErrStat,ErrMsg,RoutineName) - endif + WaveFieldPointer_C = C_NULL_PTR + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (valid) WaveFieldPointer_C = C_LOC(p%WaveField) call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) if (DebugLevel > 1) call ShowPassedData() return contains subroutine ShowPassedData() call WrScr("-----------------------------------------------------------") - call WrScr("Interface debugging: SeaSt_C_GetWaveFieldPointer") + call WrScr("Interface debugging: SeaSt_C_GetWaveFieldPointer returns") call WrScr(" --------------------------------------------------------") - call WrScr(" WaveFieldPointer_C -> "//trim(Num2LStr(loc(p%WaveField)))) + call WrScr(" WaveFieldPointer_C <- "//trim(Num2LStr(loc(p%WaveField)))) call WrScr("-----------------------------------------------------------") end subroutine ShowPassedData end subroutine @@ -472,26 +481,20 @@ subroutine SeaSt_C_SetWaveFieldPointer(WaveFieldPointer_C,ErrStat_C,ErrMsg_C) BI integer :: ErrStat character(ErrMsgLen) :: ErrMsg character(*), parameter :: RoutineName = 'SeaSt_C_SetWaveFieldPointer' + logical :: valid ErrStat = ErrID_None ErrMSg = "" call C_F_POINTER(WaveFieldPointer_C, p%WaveField) - if (associated(p%WaveField)) then - ! basic sanity check - if (.not. allocated(p%WaveField%WaveTime)) then - call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or WaveField not initialized",ErrStat,ErrMsg,RoutineName) - endif - else - call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or WaveField not initialized",ErrStat,ErrMsg,RoutineName) - endif + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) if (DebugLevel > 1) call ShowPassedData() return contains subroutine ShowPassedData() call WrScr("-----------------------------------------------------------") - call WrScr("Interface debugging: SeaSt_C_SetWaveFieldPointer") + call WrScr("Interface debugging: SeaSt_C_SetWaveFieldPointer inputs") call WrScr(" --------------------------------------------------------") - call WrScr(" WaveFieldPointer_C <- "//trim(Num2LStr(loc(p%WaveField)))) + call WrScr(" WaveFieldPointer_C -> "//trim(Num2LStr(loc(p%WaveField)))) call WrScr("-----------------------------------------------------------") end subroutine ShowPassedData end subroutine @@ -522,11 +525,14 @@ subroutine SeaSt_C_GetFluidVelAcc(Time_C, Pos_C, Vel_C, Acc_C, NodeInWater_C, Er integer :: ErrStat, ErrStat2 character(ErrMsgLen) :: ErrMsg, ErrMsg2 character(*), parameter :: RoutineName = 'SeaSt_C_GetFluidVelAcc' + logical :: valid - ! Initialize error handling + ! Initialize ErrStat = ErrID_None ErrMsg = "" - + Vel_c = 0.0_c_float + Acc_c = 0.0_c_float + forceNodeInWater = .false. if (DebugLevel > 1) call ShowPassedData() @@ -535,6 +541,13 @@ subroutine SeaSt_C_GetFluidVelAcc(Time_C, Pos_C, Vel_C, Acc_C, NodeInWater_C, Er Time = real(Time_C, DbKi) Pos = real(Pos_C, ReKi) + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call SetErrStat_F2C(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) + return + endif + ! get wave field velocity and acceleration (current is included in this) ! Notes: ! - if node is out of water, velocity and acceleration are zero @@ -594,6 +607,7 @@ subroutine SeaSt_C_GetSurfElev(Time_C, Pos_C, Elev_C, ErrStat_C,ErrMsg_C) BIND ( integer :: ErrStat character(ErrMsgLen) :: ErrMsg character(*), parameter :: RoutineName = 'SeaSt_C_GetSurfElev' + logical :: valid if (DebugLevel > 1) call ShowPassedData() @@ -601,6 +615,13 @@ subroutine SeaSt_C_GetSurfElev(Time_C, Pos_C, Elev_C, ErrStat_C,ErrMsg_C) BIND ( Time = real(Time_C, DbKi) Pos = real(Pos_C(1:2), ReKi) + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call Cleanup() + return + endif + ! get wave elevation (total combined first and second order) ! Notes: ! - if position is outside the wave field boundary, it will simply return boundary edge value @@ -650,9 +671,17 @@ subroutine SeaSt_C_GetSurfNorm(Time_C, Pos_C, NormVec_C, ErrStat_C,ErrMsg_C) BIN integer :: ErrStat character(ErrMsgLen) :: ErrMsg character(*), parameter :: RoutineName = 'SeaSt_C_GetSurfNorm' + logical :: valid if (DebugLevel > 1) call ShowPassedData() + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call Cleanup() + return + endif + ! convert position and time to fortran types Time = real(Time_C, DbKi) Pos = real(Pos_C(1:2), ReKi) @@ -684,6 +713,189 @@ end subroutine ShowReturnData end subroutine SeaSt_C_GetSurfNorm +!> return the min and max levels across entire wavefield. This only needs to be called once at the +!! start if desired +subroutine SeaSt_C_GetElevMinMaxEstimate(Min_C, Max_C, ErrStat_C,ErrMsg_C) BIND (C, NAME='SeaSt_C_GetElevMinMaxEstimate') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetElevMinMaxEstimate +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetElevMinMaxEstimate +#endif + real(c_float), intent( out) :: Min_C + real(c_float), intent( out) :: Max_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + real(SiKi) :: MinElev + real(SiKi) :: MaxElev + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetElevMinMaxEstimate' + logical :: valid + + if (DebugLevel > 1) call ShowPassedData() + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call Cleanup() + return + endif + + ! Measure directly from the data set (this is not ideal and will break if the layout changes) + call WaveField_GetMinMaxWaveElevEstimate( p%WaveField, MinElev, MaxElev, ErrStat, ErrMsg) + Min_C = real(MinElev, c_float) + Max_C = real(MaxElev, c_float) + + if (DebugLevel > 1) call ShowReturnData() + call Cleanup() + return +contains + subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here + CALL SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end subroutine Cleanup + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetElevMinMaxEstimate") + call WrScr(" --------------------------------------------------------") + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr(" Min_C <- "//trim(Num2LStr(Min_C))) + call WrScr(" Max_C <- "//trim(Num2LStr(Max_C))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData +end subroutine SeaSt_C_GetElevMinMaxEstimate + + +!---------------------------------------------------------------------------------------------------------------------------------- +! Routines to return environment vars +!---------------------------------------------------------------------------------------------------------------------------------- +!> retrieve the water density +subroutine SeaSt_C_GetDens(Dens_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_GetDens') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetDens +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetDens +#endif + real(c_float), intent( out) :: Dens_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetDens' + logical :: valid + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call SetErrStat_F2C(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) + Dens_C = 0.0_c_float + return + endif + + Dens_C = real(p%WaveField%WtrDens, c_float) + if (DebugLevel > 1) call ShowReturnData() +contains + subroutine ShowReturnData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetDens returns") + call WrScr(" --------------------------------------------------------") + call WrScr(" Dens_C <- "//trim(Num2LStr(Dens_C))) + call WrScr(" --------------------------------------------------------") + end subroutine ShowReturnData +end subroutine + + +!> retrieve the water depth +subroutine SeaSt_C_GetDpth(Dpth_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_GetDpth') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetDpth +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetDpth +#endif + real(c_float), intent( out) :: Dpth_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetDpth' + logical :: valid + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call SetErrStat_F2C(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) + Dpth_C = 0.0_c_float + return + endif + + Dpth_C = real(p%WaveField%WtrDpth, c_float) + if (DebugLevel > 1) call ShowReturnData() +contains + subroutine ShowReturnData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetDpth returns") + call WrScr(" --------------------------------------------------------") + call WrScr(" Dpth_C <- "//trim(Num2LStr(Dpth_C))) + call WrScr(" --------------------------------------------------------") + end subroutine ShowReturnData +end subroutine + + +!> retrieve MSL to SWL distance +subroutine SeaSt_C_GetMSL2SWL(MSL2SWL_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_GetMSL2SWL') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetMSL2SWL +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetMSL2SWL +#endif + real(c_float), intent( out) :: MSL2SWL_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetMSL2SWL' + logical :: valid + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call SetErrStat_F2C(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) + MSL2SWL_C = 0.0_c_float + return + endif + + MSL2SWL_C = real(p%WaveField%MSL2SWL, c_float) + if (DebugLevel > 1) call ShowReturnData() +contains + subroutine ShowReturnData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetMSL2SWL returns") + call WrScr(" --------------------------------------------------------") + call WrScr(" MSL2SWL_C <- "//trim(Num2LStr(MSL2SWL_C))) + call WrScr(" --------------------------------------------------------") + end subroutine ShowReturnData +end subroutine + + +!> routine to check if the WaveField pointer is valid. ErrStat==ErrID_None is a valid pointer +subroutine CheckWaveFieldPtr(callingRoutine,valid,ErrStat,ErrMsg) + character(*), intent(in ) :: callingRoutine + logical, intent( out) :: valid + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + ErrStat = ErrID_None + ErrMsg = "" + valid = .true. + if (associated(p%WaveField)) then + ! basic sanity check + if (.not. allocated(p%WaveField%WaveTime)) then + ErrStat = ErrID_Fatal + ErrMsg = trim(callingRoutine)//":: Invalid pointer passed in, or WaveField not initialized" + valid = .false. + endif + else + ErrStat = ErrID_Fatal + ErrMsg = trim(callingRoutine)//":: Invalid pointer passed in, or WaveField not initialized" + valid = .false. + endif +end subroutine !FIXME: the following visualization writer should be merged into the library vtk.f90 diff --git a/reg_tests/r-test b/reg_tests/r-test index b5145a9ac..28de0e697 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit b5145a9acd1a366b883cd4cd2455a68e2d318e20 +Subproject commit 28de0e697b68ca08cce8590fc00f935e27c22265