diff --git a/.github/workflows/automated-dev-tests.yml b/.github/workflows/automated-dev-tests.yml index 52ed9f8cb2..4920d86cea 100644 --- a/.github/workflows/automated-dev-tests.yml +++ b/.github/workflows/automated-dev-tests.yml @@ -368,6 +368,7 @@ jobs: path: | ${{github.workspace}}/build/reg_tests/glue-codes/openfast-cpp ${{github.workspace}}/build/reg_tests/glue-codes/python + ${{github.workspace}}/build/reg_tests/glue-codes/other ${{github.workspace}}/build/reg_tests/modules/aerodyn ${{github.workspace}}/build/reg_tests/modules/moordyn ${{github.workspace}}/build/reg_tests/modules/inflowwind diff --git a/docs/source/user/index.rst b/docs/source/user/index.rst index f2d22f797d..f06e595df3 100644 --- a/docs/source/user/index.rst +++ b/docs/source/user/index.rst @@ -32,6 +32,7 @@ This section contains documentation for the OpenFAST module-coupling environment TurbSim FAST.Farm C++ API + WaveTank Additional module documentation diff --git a/docs/source/user/other/index.rst b/docs/source/user/other/index.rst new file mode 100644 index 0000000000..4aba57b5bd --- /dev/null +++ b/docs/source/user/other/index.rst @@ -0,0 +1,40 @@ +.. _WaveTank: + +WaveTank +======== + +The WaveTank glue-code is an experimental code for coupling hardware-in-the-loop +MHK models in a wavetank to software simulating the MHK turbine loads that +cannot be physically modeled in the wave tank. The *OpenFAST* modules +*SeaState*, *AeroDyn*, *MoorDyn*, and *InflowWind* are statically linked into a +single dynamic library (``cmake`` target ``wavetanktesting_c_binding``) with a +c-binding based interface. This library can be called from *LabView* or another +code. + +Inputs to the library include the time and motions, including the velocities and +accelerations, located at a single reference poitn at each time step. The +resulting forces and moments are returned to the calling code. + +Restrictions +~~~~~~~~~~~~ +The current setup WaveTank library has several restrictions: + +- rigid structure including platform, tower, and nacelle +- no yaw DOF +- rigid rotor +- constant rotor RPM for entire simulation +- no option for controller interfacing at present +- visualization limitted to *AeroDyn* and *SeaState* +- Current implementation only supports floating MHK turbines (``MHK = 2``). Other modes are present but not fully implemented. + + + + +Input File +~~~~~~~~~~ + + +.. toctree:: + :maxdepth: 2 + + wavetank_input.rst diff --git a/docs/source/user/other/wavetank_input.rst b/docs/source/user/other/wavetank_input.rst new file mode 100644 index 0000000000..5ef7490064 --- /dev/null +++ b/docs/source/user/other/wavetank_input.rst @@ -0,0 +1,242 @@ +.. _WaveTank-Input: + +Input File +---------- + +This document describes the WaveTank configuration input file (``wavetankconfig_input.txt``) used to set up and run the WaveTank model for marine hydrokinetic (MHK) turbine testing. + +- The file is read by the WaveTank library during initialization. + +Conventions and Units +--------------------- + +- SI units are used throughout: m, s, kg, N, Pa. +- Angles are in degrees unless otherwise specified. +- Rotational speed is in rpm where noted. +- Positions and heights are referenced to Mean Sea Level (MSL) unless otherwise noted. +- Input files for modules may be relative or absolute paths. + +File and Simulation Control +--------------------------- + +OutRootName (string) + Root name used when writing summary or other files. + Example: ``FRM1Q_Floating_tank_test``. + +DT (s) + Nominal timestep for WaveTank internal scheduling. Currently unused/reserved. + +TMax (s) + Maximum simulation time for WaveTank internal scheduling. Currently unused/reserved. + +MHK (switch) + MHK turbine type switch: + + - 0: Not an MHK turbine + - 1: Fixed MHK turbine + - 2: Floating MHK turbine + + Only the floating option (2) is supported at present. + +InterpOrd (-) + Interpolation order for internal data interpolation. Currently unused/reserved. + +DebugLevel (switch) + Controls logging and visualization detail: + + - 0: none + - 1: I/O summary + - 2: + positions/orientations passed + - 3: + input file + - 4: + all meshes + +.. note:: + Parameters marked “unused” are reserved for future development and are currently ignored by the code path. + +Froude Scaling (disabled) +------------------------- + +The following parameters may appear but are typically commented out. Froude scaling is not complete in the current code. Do not use unless explicitly enabled. + +ScaleFact (-) + Froude scaling factor λ = (full-size dimension) / (model-size dimension). Expected > 1 for scale-model testing. + +DensFact (-) + Density ratio ρ_full / ρ_model, used with Froude scaling of forces/moments. + +Environment +----------- + +Gravity (m/s^2) + Gravitational acceleration. + +WtrDens (kg/m^3) + Water (working fluid) density. + +WtrVisc (m^2/s) + Kinematic viscosity of the working fluid. + +SpdSound (m/s) + Speed of sound in the working fluid. + +Patm (Pa) + Atmospheric pressure. Used for cavitation checks. + +Pvap (Pa) + Vapor pressure of the working fluid. Used for cavitation checks. + +WtrDpth (m) + Water depth. + +MSL2SWL (m) + Offset between still-water level (SWL) and mean sea level (MSL); positive upward. + +Sea State +--------- + +SS_InputFile (string) + Path to SeaState input file defining wave conditions. Ensure path is valid relative to the run directory or use an absolute path. + +WaveTimeShift (s) + Time shift applied to the SeaState wave time series to adjust phase and match tank conditions. + +MoorDyn +------- + +MD_InputFile (string) + Path to MoorDyn input file defining mooring system properties and connections. + +AeroDyn and InflowWind +---------------------- + +AD_InputFile (string) + Path to AeroDyn input file defining aerodynamic model configuration (used for hydro/aero coupling as applicable in MHK context). + +IfW_InputFile (string) + Path to InflowWind input file defining inflow conditions for the rotor (e.g., currents or wind, depending on model setup). + +Turbine Geometry and Reference Frames +------------------------------------- + +NumBl (-) + Number of blades on the rotor. + +HubRad (m) + Distance from the rotor apex to the blade root. + +PreCone (deg) + Blade cone angle. + +OverHang (m) + Distance from the yaw axis (tower centerline) to the rotor apex. Negative values indicate rotor apex aft of the yaw axis under the model’s convention. + +ShftTilt (deg) + Rotor shaft tilt angle. + +Twr2Shft (m) + Vertical distance from tower-top to the rotor shaft center (nacelle center). Negative values are below tower-top. + +TowerHt (m) + Height of the tower relative to MSL. Tower is vertically aligned with ``TowerBsPt`` (sloped towers not supported). + +TowerBsPt (m, m, m) + Tower base location relative to the platform reference position in x and y, and relative to MSL in z: + + - x: along surge axis + - y: along sway axis + - z: height relative to MSL + +PtfmRefPos (m, m, m) + Platform reference point position relative to MSL. All platform motions and loads connect at this point. + +PtfmRefOrient (deg, deg, deg) + Platform reference orientation given as Euler angles [roll, pitch, yaw]. + +Turbine Initial Conditions +-------------------------- + +RotSpeed (rpm) + Initial rotational speed of the rotor (in rotor coordinates). + +NacYaw (deg) + Initial or fixed nacelle yaw angle. + +BldPitch (deg) + Initial blade 1 pitch angle. If a multi-blade model is used, blade pitch control typically applies per blade in other modules; here this initializes blade 1. + +Azimuth (deg) + Initial rotor azimuth angle. + +Wave Buoy +--------- + +WaveBuoyLoc (m, m) + Location of the wave elevation measurement buoy in the tank coordinate frame. SeaState data is returned at each timestep at this location. + +Output +------ + +SendScreenToFile (flag) + If true, send screen output to a file named ``.screen.log``. + +OutFile (switch) + Controls tabular output of channels: + + - 0: no output file of channels + - 1: output file in text format (written at default DT) + +OutFmt (string) + Format specifier for text tabular output channels (excluding the time channel). Uses a Fortran-like format string. + Example: ``ES20.6E2``. + +VTK Visualization Output +------------------------ + +WrVTK_Dir (string) + Output directory for VTK visualization files. + +WrVTK (switch) + VTK visualization data output: + + - 0: none + - 1: initialization data only + - 2: animation + - 3: mode shapes + +WrVTK_type (switch) + Type of VTK visualization data: + + - 1: surfaces + - 2: basic meshes (lines/points) + - 3: all meshes (debug) + +.. note:: + Only lines/points may be supported in some builds. If surfaces are not + supported, use ``WrVTK_type = 2`` to visualize line/point data. + +WrVTK_DT (s) + Timestep for writing VTK files. + +VTKNacDim (m, m, m, m, m, m) + Nacelle dimensions for VTK surface rendering in the format ``[x0, y0, z0, Lx, Ly, Lz]``: + + - ``x0, y0, z0``: nacelle origin offsets + - ``Lx, Ly, Lz``: nacelle extents along x, y, z + +Implementation Notes and Best Practices +--------------------------------------- + +- Only floating MHK (``MHK = 2``) is currently supported; other MHK modes will + not perform as expected. +- Ensure external file paths (*SeaState*, *MoorDyn*, *AeroDyn*, *InflowWind*) + are valid relative to the working directory or specify absolute paths. +- Coordinate conventions: + + - Positions and heights are referenced to MSL unless otherwise noted. + - The platform reference point (``PtfmRefPos``) is the coupling point for + motions and loads. + - The tower base is defined relative to ``PtfmRefPos`` in x and y, and to MSL + in z. + +- Choose ``OutFmt`` to balance precision and file size. The example ``ES20.6E2`` + is suitable for scientific notation with fixed width. diff --git a/glue-codes/labview/CMakeLists.txt b/glue-codes/labview/CMakeLists.txt index b14ef7ae4b..490a014a8e 100644 --- a/glue-codes/labview/CMakeLists.txt +++ b/glue-codes/labview/CMakeLists.txt @@ -14,15 +14,29 @@ # limitations under the License. # -add_library(wavetanktestinglib SHARED +if (GENERATE_TYPES) + generate_f90_types(src/WaveTank_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/WaveTank_Types.f90 -noextrap) +endif() + +add_library(wavetanktesting_c_binding SHARED + src/WaveTank_Types.f90 + src/WaveTank_IO.f90 + src/WaveTank_Struct.f90 src/WaveTank.f90 ) -target_link_libraries(wavetanktestinglib aerodyn_inflow_c_binding moordyn_c_binding seastate_c_binding nwtclibs versioninfolib) +target_link_libraries( + wavetanktesting_c_binding + aerodyn_inflow_c_bind_static + moordyn_c_bind_static + seastate_c_bind_static + nwtclibs + versioninfolib +) if(APPLE OR UNIX) - target_compile_definitions(wavetanktestinglib PRIVATE IMPLICIT_DLLEXPORT) + target_compile_definitions(wavetanktesting_c_binding PRIVATE IMPLICIT_DLLEXPORT) endif() -install(TARGETS wavetanktestinglib +install(TARGETS wavetanktesting_c_binding EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/glue-codes/labview/examples/WaveTank.vi b/glue-codes/labview/examples/WaveTank.vi new file mode 100644 index 0000000000..dbfcd8ded2 Binary files /dev/null and b/glue-codes/labview/examples/WaveTank.vi differ diff --git a/glue-codes/labview/src/OPENFAST_RT_DLL.f90 b/glue-codes/labview/src/OPENFAST_RT_DLL.f90 deleted file mode 100644 index 50cee2773f..0000000000 --- a/glue-codes/labview/src/OPENFAST_RT_DLL.f90 +++ /dev/null @@ -1,125 +0,0 @@ -! OPENFAST_RT_DLL.f90 -! (c) 2009, 2012 National Renewable Energy Laboratory -! Paul Fleming, National Wind Technology Center, September 2009, 2012 -! Bonnie Jonkman, National Wind Technology Center, October 2012 -! -! Modification of OPENFAST for Labview RT -! Also includes code from OPENFAST_Simulink Adaptation -!==================================================================================== - -subroutine OPENFAST_RT_DLL_INIT (FileName_RT_Byte, FLen) - - ! Expose subroutine OPENFAST_RT_DLL_INIT to users of this DLL - ! - !DEC$ ATTRIBUTES DLLEXPORT::FAST_RT_DLL_INIT - -USE NWTC_Library -USE General, ONLY : PriFile, Cmpl4LV - -USE OPENFAST_IO_Subs ! OPENFAST_Input(), OPENFAST_Begin() -USE OPENFASTSubs ! OPENFAST_Initialize() - - ! This sub-routine is called by RT to initialize all internal variables - -IMPLICIT NONE - -INTEGER, PARAMETER :: MaxFileNameLen = 100 -INTEGER(B1Ki) :: FileName_RT_Byte(MaxFileNameLen) ! FileName_RT_Byte - -CHARACTER(MaxFileNameLen) :: FileName_RT_Char ! FileName_RT_Byte converted to ASCII characters -INTEGER :: FLen ! trim length of FileName_RT_Byte -INTEGER :: I ! temporary loop counter - - -IF ( FLen > MaxFileNameLen ) CALL ProgAbort('File name is too long in OPENFAST_RT_DLL_INIT.') -DO I=1,FLen - FileName_RT_Char(I:I) = ACHAR(FileName_RT_Byte(I)) -END DO -!EQUIVALENCE(FileName_RT_Byte2,FileName_RT_Char) !Make the character filename equivalent to incoming filename byte array - -!FileName_RT_Byte2(:) = FileName_RT_Byte(:) - - - -!Assign PriFile based on passed in string -PriFile = FileName_RT_Char(1:FLen) - - - ! Open and read input files, initialize global parameters. -CALL OPENFAST_Begin( PriFile, RootName, DirRoot ) - - -!Set compiler flag for Simulink -Cmpl4LV = .TRUE. - -CALL OPENFAST_Input() - - ! Set up initial values for all degrees of freedom. -CALL OPENFAST_Initialize(p,x,y,OtherState) - - -end subroutine OPENFAST_RT_DLL_INIT - - - - -!==================================================================================== -subroutine OPENFAST_RT_DLL_SIM (BlPitchCom_RT, YawPosCom_RT, YawRateCom_RT, ElecPwr_RT, GenTrq_RT, OutData_RT, Time_RT, HSSBrFrac_RT) - - - ! Expose subroutine OPENFAST_RT_DLL_SIM to users of this DLL - ! - !DEC$ ATTRIBUTES DLLEXPORT::FAST_RT_DLL_SIM - - -USE SimCont !ZTime - -! These are needed for FirstTime = .FALSE. -USE DriveTrain ! GenTrq and now also HSSBrFrac -USE TurbCont ! BlPitch -USE TurbConf ! NumBl -USE Blades ! TipNode -USE Precision ! ReKi -USE Features ! CompAero -USE Output ! for WrOutHdr - -USE OPENFASTSubs ! TimeMarch() - -IMPLICIT NONE - - ! This sub-routine implements n-iterations of time step and returns outputs to Labview RT - - ! Variables -REAL(ReKi), INTENT(IN) :: GenTrq_RT ! Mechanical generator torque. -REAL(ReKi), INTENT(IN) :: ElecPwr_RT ! Electrical power -REAL(ReKi), INTENT(IN) :: YawPosCom_RT ! Yaw position -REAL(ReKi), INTENT(IN) :: YawRateCom_RT ! Yaw rate -REAL(ReKi), INTENT(IN) :: BlPitchCom_RT (*) -REAL(ReKi), INTENT(OUT) :: OutData_RT (*) -REAL(ReKi), INTENT(OUT) :: Time_RT -REAL(ReKi), INTENT(IN) :: HSSBrFrac_RT ! Brake Fraction - - !Copy in inputs from RT - BlPitchCom = BlPitchCom_RT(1:NumBl) - YawPosCom = YawPosCom_RT - YawRateCom = YawRateCom_RT - ElecPwr = ElecPwr_RT - GenTrq= GenTrq_RT - HSSBrFrac = HSSBrFrac_RT - - ! Set the command pitch angles to the actual pitch angles since we have no - ! built-in pitch actuator: - BlPitch = BlPitchCom - - -!Run simulation -Call TimeMarch( p_StrD, x_StrD, OtherSt_StrD, y_StrD, ErrStat, ErrMsg ) - - -!Copy outputs -OutData_RT(1:p_StrD%NumOuts) = OutData(1:p_StrD%NumOuts) -Time_RT = ZTime; -OutData_RT(p_StrD%NumOuts+1) = TMax; -OutData_RT(p_StrD%NumOuts+2) = Time_RT; - -end subroutine OPENFAST_RT_DLL_SIM diff --git a/glue-codes/labview/src/README.txt b/glue-codes/labview/src/README.txt new file mode 100644 index 0000000000..1952663266 --- /dev/null +++ b/glue-codes/labview/src/README.txt @@ -0,0 +1,17 @@ +2025.11.10 + +This is a work in progress. Some things are not complete or functional yet: + + +Froude scaling is not complete, nor is it tested!!!! + +At present, only some inputs are scaled, but equations +have not been verified yet. This has been disabled by +removing the reading of the `*Fact` input lines in the +input file parsing and input file. + +TODO: + - verify equations in FroudeScaling* functions + - scale resulting forces / moments + - add scaled time, pos, vel, acc, frc, mom to output + channels and add subscripting to differentiate diff --git a/glue-codes/labview/src/WaveTank.f90 b/glue-codes/labview/src/WaveTank.f90 index cca86d6d7b..90bf7dcd62 100644 --- a/glue-codes/labview/src/WaveTank.f90 +++ b/glue-codes/labview/src/WaveTank.f90 @@ -1,210 +1,830 @@ - +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2025 National Renewable Energy Laboratory +! +! This file is a module specific to an experimental wave tank at NREL. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +!********************************************************************************************************************************** +! +! This code is designed to connect with LabView for a specific wave tank test case and likely will not work for other purposes. +! +! For this test, a physical platform is deployed in a wave tank with cable acutators that are controlled through LabView. This +! module is called to provide some loads that are not present in the physical tank setup. These include the following: +! - rotor loading from a fixed RPM MHK rotor from AeroDyn. This is calculated from either steady current provided by SeaState +! - Mooring loads from MoorDyn +! +! +!********************************************************************************************************************************** MODULE WaveTankTesting - USE ISO_C_BINDING - USE NWTC_Library - ! USE Precision - USE MoorDyn_C - USE SeaState_C_Binding - USE NWTC_C_Binding, ONLY: IntfStrLen, SetErrStat_C - - IMPLICIT NONE - SAVE - - PUBLIC :: WaveTank_Init - - REAL(C_DOUBLE) :: dt_c = 0.01_C_DOUBLE ! 100 hertz - REAL(C_FLOAT) :: g_c = 9.8065_C_FLOAT - REAL(C_FLOAT) :: rho_c = 1025.0_C_FLOAT - REAL(C_FLOAT) :: depth_c = 200.0_C_FLOAT - REAL(C_FLOAT), DIMENSION(6) :: ptfminit_c = 0.0_C_FLOAT - INTEGER(C_INT) :: interporder_c = 2 ! 1: linear (uses two time steps) or 2: quadratic (uses three time steps) - - INTEGER(C_INT) :: N_CAMERA_POINTS - - INTEGER(C_INT) :: load_period = 20 ! seconds - -CONTAINS - - - -SUBROUTINE WaveTank_Init( & - MD_InputFile_c, & - SS_InputFile_c, & - AD_InputFile_c, & - IfW_InputFile_c, & - n_camera_points_c, & - ErrStat_c, & - ErrMsg_c & -) BIND (C, NAME='WaveTank_Init') + use ISO_C_BINDING + use NWTC_Library + use SeaState_C_Binding, ONLY: SeaSt_C_PreInit, SeaSt_C_Init, SeaSt_C_CalcOutput, SeaSt_C_End, MaxOutPts, SeaSt_C_GetWaveFieldPointer, SeaSt_C_GetSurfElev + use SeaSt_WaveField_Types, ONLY: SeaSt_WaveFieldType + use AeroDyn_Inflow_C_BINDING, ONLY: ADI_C_PreInit, ADI_C_SetupRotor, ADI_C_Init, ADI_C_End, MaxADIOutputs, ADI_C_SetRotorMotion, ADI_C_UpdateStates, ADI_C_CalcOutput, ADI_C_GetRotorLoads + use MoorDyn_C, ONLY: MD_C_Init, MD_C_End, MD_C_SetWaveFieldData, MD_C_UpdateStates, MD_C_CalcOutput + use NWTC_C_Binding, ONLY: IntfStrLen, SetErrStat_C, SetErrStat_F2C, ErrMsgLen_C, StringConvert_F2C, FileNameFromCString, AbortErrLev_C + use WaveTank_Types + use WaveTank_IO + use WaveTank_Struct + + implicit none + save + + public :: WaveTank_Init + public :: WaveTank_CalcStep + public :: WaveTank_End + + ! output to screen or to file (LabView doesn't capture console output nicely) + integer(IntKi) :: ScreenLogOutput_Un = -1 + character(1024) :: ScreenLogOutput_File + + ! Simulation data storage + type(SimSettingsType), target :: SimSettings + + ! IO data storage for CalcStep + type(CalcStepIOdataType) :: CalcStepIO + real(c_double) :: TimePrev_c + + ! Output file writing: headers, units, data, filename, fileunit etc. + type(WrOutputDataType) :: WrOutputData + + ! Structural model data storage + type(MeshesMotionType), target :: MeshMotions ! motion meshes (inputs) + type(MeshesLoadsType ), target :: MeshLoads ! load meshes (output) + type(MeshesMapsType ) :: MeshMaps ! mappings + type(StructTmpType ) :: StructTmp ! temporary data - avoids reallocation + + ! time stuff + integer(IntKi) :: VTKn_Global ! global timestep for VTK + integer(IntKi) :: VTKn_last ! last global timestep for VTK + + +!TODO: +! - add echo file +! - add summary file +! - add scaling +! - Input for scaling already in place +! - add info into summary file on scaling +! - add unscaled interface IO outputs to file as well as the regular IO currently in there +! - add pre and post scaling routines for time, pos, vel, acc, force/moment + + +contains + +subroutine WaveTank_Init( & + WT_InputFile_C, & + RootName_C, & + VTKdir_C, & + ErrStat_C, & + ErrMsg_C & +) bind (C, name='WaveTank_Init') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_Init !GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_Init #endif -CHARACTER(KIND=C_CHAR), INTENT(IN ), TARGET :: MD_InputFile_c(IntfStrLen) -CHARACTER(KIND=C_CHAR), INTENT(IN ), TARGET :: SS_InputFile_c(IntfStrLen) -CHARACTER(KIND=C_CHAR), INTENT(IN ), TARGET :: AD_InputFile_c(IntfStrLen) -CHARACTER(KIND=C_CHAR), INTENT(IN ), TARGET :: IfW_InputFile_c(IntfStrLen) -INTEGER(C_INT), INTENT(IN ) :: n_camera_points_c -INTEGER(C_INT), INTENT( OUT) :: ErrStat_C -CHARACTER(KIND=C_CHAR), INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) - -! Local variables -integer(c_int) :: numchannels_c -character(kind=c_char) :: outputchannelnames_c(100000) -character(kind=c_char) :: outputchannelunits_c(100000) -integer(c_int) :: input_file_passed = 0 ! We're passing paths to input files rather than input strings for all modules -! character(kind=c_char), pointer :: filestring_c(IntfStrLen) ! Point to input file path input argument - -print *, MD_InputFile_c -print *, SS_InputFile_c -print *, AD_InputFile_c -print *, IfW_InputFile_c - -N_CAMERA_POINTS = n_camera_points_c - -! filestring_c => MD_InputFile_c -! call MD_C_Init( & -! input_file_passed, & -! filestring_c, & -! IntfStrLen, & -! dt_c, & -! g_c, & -! rho_c, & -! depth_c, & -! ptfminit_c, & -! interporder_c, & -! numchannels_c, & -! outputchannelnames_c, & -! outputchannelunits_c, & -! ErrStat_C, ErrMsg_C & -! ) - -! call ADI_C_Init( & -! ADinputFilePassed, & ! integer(c_int), intent(in ) :: ADinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation] -! ADinputFileString_C, & ! type(c_ptr), intent(in ) :: ADinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR -! ADinputFileStringLength_C, & ! integer(c_int), intent(in ) :: ADinputFileStringLength_C !< lenght of the input file string -! IfWinputFilePassed, & ! integer(c_int), intent(in ) :: IfWinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation] -! IfWinputFileString_C, & ! type(c_ptr), intent(in ) :: IfWinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR -! IfWinputFileStringLength_C, & ! integer(c_int), intent(in ) :: IfWinputFileStringLength_C !< lenght of the input file string -! OutRootName_C, & ! character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) !< Root name to use for echo files and other -! OutVTKDir_C, & ! character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output -! gravity_C, & ! real(c_float), intent(in ) :: gravity_C !< Gravitational acceleration (m/s^2) -! defFldDens_C, & ! real(c_float), intent(in ) :: defFldDens_C !< Air density (kg/m^3) -! defKinVisc_C, & ! real(c_float), intent(in ) :: defKinVisc_C !< Kinematic viscosity of working fluid (m^2/s) -! defSpdSound_C, & ! real(c_float), intent(in ) :: defSpdSound_C !< Speed of sound in working fluid (m/s) -! defPatm_C, & ! real(c_float), intent(in ) :: defPatm_C !< Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] -! defPvap_C, & ! real(c_float), intent(in ) :: defPvap_C !< Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] -! WtrDpth_C, & ! real(c_float), intent(in ) :: WtrDpth_C !< Water depth (m) -! MSL2SWL_C, & ! real(c_float), intent(in ) :: MSL2SWL_C !< Offset between still-water level and mean sea level (m) [positive upward] -! InterpOrder_C, & ! integer(c_int), intent(in ) :: InterpOrder_C !< Interpolation order to use (must be 1 or 2) -! DT_C, & ! real(c_double), intent(in ) :: DT_C !< Timestep used with AD for stepping forward from t to t+dt. Must be constant. -! TMax_C, & ! real(c_double), intent(in ) :: TMax_C !< Maximum time for simulation -! storeHHVel, & ! integer(c_int), intent(in ) :: storeHHVel !< Store hub height time series from IfW -! WrVTK_in, & ! integer(c_int), intent(in ) :: WrVTK_in !< Write VTK outputs [0: none, 1: init only, 2: animation] -! WrVTK_inType, & ! integer(c_int), intent(in ) :: WrVTK_inType !< Write VTK outputs as [1: surface, 2: lines, 3: both] -! WrVTK_inDT, & ! real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes -! VTKNacDim_in, & ! real(c_float), intent(in ) :: VTKNacDim_in(6) !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) -! VTKHubRad_in, & ! real(c_float), intent(in ) :: VTKHubrad_in !< Hub radius for VTK surface rendering -! wrOuts_C, & -! DT_Outs_C, & -! NumChannels_C, & -! OutputChannelNames_C, & -! OutputChannelUnits_C, & -! ErrStat_C, ErrMsg_C & -! ) - - -! ! Input file info -! integer(c_int), intent(in ) :: ADinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation] -! type(c_ptr), intent(in ) :: ADinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR -! integer(c_int), intent(in ) :: ADinputFileStringLength_C !< lenght of the input file string -! integer(c_int), intent(in ) :: IfWinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation] -! type(c_ptr), intent(in ) :: IfWinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR -! integer(c_int), intent(in ) :: IfWinputFileStringLength_C !< lenght of the input file string -! character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) !< Root name to use for echo files and other -! character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output -! ! Environmental -! real(c_float), intent(in ) :: gravity_C !< Gravitational acceleration (m/s^2) -! real(c_float), intent(in ) :: defFldDens_C !< Air density (kg/m^3) -! real(c_float), intent(in ) :: defKinVisc_C !< Kinematic viscosity of working fluid (m^2/s) -! real(c_float), intent(in ) :: defSpdSound_C !< Speed of sound in working fluid (m/s) -! real(c_float), intent(in ) :: defPatm_C !< Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] -! real(c_float), intent(in ) :: defPvap_C !< Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] -! real(c_float), intent(in ) :: WtrDpth_C !< Water depth (m) -! real(c_float), intent(in ) :: MSL2SWL_C !< Offset between still-water level and mean sea level (m) [positive upward] -! ! Interpolation -! integer(c_int), intent(in ) :: InterpOrder_C !< Interpolation order to use (must be 1 or 2) -! ! Time -! real(c_double), intent(in ) :: DT_C !< Timestep used with AD for stepping forward from t to t+dt. Must be constant. -! 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 -! integer(c_int), intent( out) :: NumChannels_C !< Number of output channels requested from the input file -! character(kind=c_char), intent( out) :: OutputChannelNames_C(ChanLen*MaxADIOutputs+1) !< NOTE: if MaxADIOutputs is sufficiently large, we may overrun the buffer on the Python side. -! character(kind=c_char), intent( out) :: OutputChannelUnits_C(ChanLen*MaxADIOutputs+1) -! integer(c_int), intent( out) :: ErrStat_C !< Error status -! character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) !< Error message (C_NULL_CHAR terminated) - -! Set compiler flag for Labview -! Cmpl4LV = .TRUE. - -END SUBROUTINE WaveTank_Init - -! delta_time, & -SUBROUTINE WaveTank_CalcOutput( & - frame_number, & - positions_x, & - positions_y, & - positions_z, & - rotation_matrix, & - loads, & - ErrStat_c, & - ErrMsg_c & -) BIND (C, NAME='WaveTank_CalcOutput') + character(c_char), intent(in ) :: WT_InputFile_C(IntfStrLen) + character(kind=c_char), intent( out) :: RootName_C(IntfStrLen) + character(kind=c_char), intent( out) :: VTKdir_C(IntfStrLen) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + integer(c_int) :: ErrStat_C2 + character(kind=c_char, len=ErrMsgLen_C) :: ErrMsg_C2 + integer(IntKi) :: ErrStat_F2 + character(ErrMsgLen) :: ErrMsg_F2 + character(1024) :: InputFile + integer(IntKi) :: i,k + integer(c_int), allocatable :: tmpMeshPtToBladeNum(:) + type(FileInfoType) :: FileInfo_In !< The derived type for holding the full input file for parsing -- we may pass this in the future + + ! debug level for passing to modules. Backing down the level by 1 for modules + integer(IntKi) :: DebugLevelMod + + ! local C variables for transferring names + character(kind=c_char) :: WrVTK_Dir_C(IntfStrLen) + character(kind=c_char) :: OutRootName_C(IntfStrLen) + + ! The length of these arrays much match what is set in the corresponding C binding modules, or be larger + character(kind=c_char) :: SS_WriteOutputHdr_C(ChanLen*MaxOutPts+1) + character(kind=c_char) :: SS_WriteOutputUnt_C(ChanLen*MaxOutPts+1) + character(kind=c_char) :: MD_WriteOutputHdr_C(ChanLen*1000) ! probably oversized + character(kind=c_char) :: MD_WriteOutputUnt_C(ChanLen*1000) ! probably oversized + character(kind=c_char) :: ADI_WriteOutputHdr_C(ChanLen*MaxADIOutputs+1) + character(kind=c_char) :: ADI_WriteOutputUnt_C(ChanLen*MaxADIOutputs+1) + + ! Filename conversions -- read in as fortran strings, but sent to other modules as c_char arrays + character(kind=c_char) :: SS_InputFile_C(IntfStrLen) + character(kind=c_char), target :: MD_InputFile_C(IntfStrLen) + character(kind=c_char), target :: AD_InputFile_C(IntfStrLen) + character(kind=c_char), target :: IfW_InputFile_C(IntfStrLen) + + ! temporary storage of number of output channels + integer(c_int) :: SS_NumChannels_C + integer(c_int) :: MD_NumChannels_C + integer(c_int) :: ADI_NumChannels_C + + ! set constants + call NWTC_Init() + + ! Initialize error handling + ErrStat_C = ErrID_None + ErrMsg_C = " "//C_NULL_CHAR + + InputFile = transfer(WT_InputFile_C, InputFile) + i = index(InputFile, char(0)) + InputFile = InputFile(1:i) + call ProcessComFile(InputFile, FileInfo_In, ErrStat_F2, ErrMsg_F2); if (Failed()) return + call ParseInputFile(FileInfo_In, SimSettings, ErrStat_F2, ErrMsg_F2); if (Failed()) return + + ! return rootname + RootName_C = c_null_char + RootName_C = transfer(trim(SimSettings%Sim%OutRootName),RootName_C) + + ! If SendScreenToFile - send to file .screen.log if true + if (SimSettings%Outs%SendScreenToFile) then + call GetNewUnit(ScreenLogOutput_Un, ErrStat_F2, ErrMsg_F2); if (Failed()) return + ScreenLogOutput_File = trim(SimSettings%Sim%OutRootName)//'.screen.log' + call OpenFOutFile(ScreenLogOutput_Un, ScreenLogOutput_File, ErrStat_F2, ErrMsg_F2); if (Failed()) return + call SetConsoleUnit(ScreenLogOutput_Un) ! this will redirect all screen output to a file instead + endif + + ! validate the settings now that the screen can be written to file + call ValidateInputFile(SimSettings, ErrStat_F2, ErrMsg_F2); if (Failed()) return + + ! debugging + if (SimSettings%Sim%DebugLevel > 0_c_int) call ShowPassedData() + if (SimSettings%Sim%DebugLevel > 2_c_int) call Print_FileInfo_Struct(CU,FileInfo_In) + + ! set debug level for modules (backing off by 1 to allow just checking wavetank io) + DebugLevelMod = max( 0_IntKi, SimSettings%Sim%DebugLevel-1_IntKi) + + ! VTK directory + WrVTK_Dir_C = c_null_char + WrVTK_Dir_C = transfer( trim(SimSettings%Viz%WrVTK_Dir), WrVTK_Dir_C ) + ! return VTKdir + VTKdir_C = c_null_char + if (SimSettings%Viz%WrVTK > 0_c_int) VTKdir_C = WrVTK_Dir_C + + ! Set a previous time (used in calcstep) + TimePrev_c = -SimSettings%Sim%DT ! we need this at T=0 + + !------------------------------ + ! Allocate temp storage + !------------------------------ + call AllocTmpStorage(ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + + !------------------------------ + ! Build struct model + !------------------------------ + call StructCreate(SimSettings, MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + ! output VTK for struct model (if requested) + if (SimSettings%Viz%WrVTK > 0_c_int) then + ! create directory if doesn't exist + call MKDIR( trim(SimSettings%Viz%WrVTK_Dir) ) + ! write mesh refs + call WrVTK_Struct_Ref(SimSettings, MeshMotions, MeshLoads, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + endif + + ! map the structural meshes (write vtk first in case of issues) + call StructCreateMeshMaps(SimSettings, MeshMotions, MeshLoads, MeshMaps, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + + !------------------------------ + ! Setup and initialize SeaState + !------------------------------ + call SeaSt_C_PreInit( & + SimSettings%Env%Gravity, & + SimSettings%Env%WtrDens, & + SimSettings%Env%WtrDpth, & + SimSettings%Env%MSL2SWL, & + DebugLevelMod, & + WrVTK_Dir_C, & + SimSettings%Viz%WrVTK, & + SimSettings%Viz%WrVTK_DT, & + ErrStat_C2, ErrMsg_C2 ) + if (Failed_c('SeaSt_C_PreInit')) return + + SS_InputFile_C = c_null_char + SS_InputFile_C = transfer(trim(SimSettings%ModSettings%SS_InputFile ), SS_InputFile_C ) + OutRootName_C = transfer(trim(SimSettings%Sim%OutRootName)//'.SeaSt'//c_null_char, OutRootName_C) + call SeaSt_C_Init( & + SS_InputFile_C, & + OutRootName_C, & + SimSettings%Sim%TMax, & + SimSettings%Sim%DT, & + SimSettings%ModSettings%WaveTimeShift, & + SS_NumChannels_C, & + SS_WriteOutputHdr_C, & + SS_WriteOutputUnt_C, & + ErrStat_C2, ErrMsg_C2 ) + if (Failed_c('SeaSt_C_Init')) return + + ! store channel info + WrOutputData%NumChans_SS = int(SS_NumChannels_c,IntKi) + call TransferOutChanNamesUnits(WrOutputData%NumChans_SS, 'WriteOutputHdr_SS', SS_WriteOutputHdr_c, WrOutputData%WriteOutputHdr_SS,ErrStat_F2,ErrMsg_F2); if (Failed()) return + call TransferOutChanNamesUnits(WrOutputData%NumChans_SS, 'WriteOutputUnt_SS', SS_WriteOutputUnt_c, WrOutputData%WriteOutputUnt_SS,ErrStat_F2,ErrMsg_F2); if (Failed()) return + + + !------------------------------ + ! Set the SeaState Wave Field pointer onto MoorDyn + !------------------------------ + call WaveTank_SetWaveFieldPointer(ErrStat_C2, ErrMsg_C2) + if (Failed_c('WaveTank_SetWaveFieldPointer')) return + + + !------------------------------ + ! Setup and initialize MoorDyn + !------------------------------ + ! set the platform position/orientation + call SetMDTmpMotion() +!FIXME: this interface will change!!! -- Split with PreInit +!FIXME: add WrVTK_Dir_C, SimSettings%Viz%WrVTK, SimSettings%Viz%WrVTK_DT + MD_InputFile_C = c_null_char + MD_InputFile_C = transfer(trim(SimSettings%ModSettings%MD_InputFile ), MD_InputFile_C ) + OutRootName_C = transfer(trim(SimSettings%Sim%OutRootName)//'.MD'//c_null_char, OutRootName_C) + call MD_C_Init( & + 0_c_int, & !< InputFilePassed: 0 for file, 1 for string + c_loc(MD_InputFile_C(1)), & + int(IntfStrLen,c_int), & !< InputFileStringLength_C + SimSettings%Sim%DT, & + SimSettings%Env%Gravity, & + SimSettings%Env%WtrDens, & + SimSettings%Env%WtrDpth, & + StructTmp%PtfmPosAng_c, & + SimSettings%Sim%InterpOrd, & + MD_NumChannels_C, & + MD_WriteOutputHdr_C, & + MD_WriteOutputUnt_C, & + ErrStat_C2, ErrMsg_C2 & + ) +!FIXME: add this when updating MD interface +! DebugLevelMod, & + if (Failed_c('MD_C_Init')) return + + ! store channel info + WrOutputData%NumChans_MD = int(MD_NumChannels_c,IntKi) + call TransferOutChanNamesUnits(WrOutputData%NumChans_MD, 'WriteOutputHdr_MD', MD_WriteOutputHdr_c, WrOutputData%WriteOutputHdr_MD,ErrStat_F2,ErrMsg_F2); if (Failed()) return + call TransferOutChanNamesUnits(WrOutputData%NumChans_MD, 'WriteOutputUnt_MD', MD_WriteOutputUnt_c, WrOutputData%WriteOutputUnt_MD,ErrStat_F2,ErrMsg_F2); if (Failed()) return + + !------------------------------ + ! Setup and initialize AeroDyn+Inflow + !------------------------------ + call ADI_C_PreInit( & + 1_c_int, & ! only one turbine + 0_c_int, & ! transpose DCM inside ADI (0=false) + 1_c_int, & ! PointLoadOutput - use line to point load mapping -- necessary for mapping to blade root without an actual blade structure + SimSettings%Env%Gravity, & + SimSettings%Env%WtrDens, & + SimSettings%Env%WtrVisc, & + SimSettings%Env%SpdSound, & + SimSettings%Env%Patm, & + SimSettings%Env%Pvap, & + SimSettings%Env%WtrDpth, & + SimSettings%Env%MSL2SWL, & + SimSettings%Sim%MHK, & + DebugLevelMod, & + ErrStat_C2, ErrMsg_C2 & + ) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'ADI_C_PreInit') + if (ErrStat_C >= AbortErrLev_C) then + call CleanUp() + return + endif + + ! Nacelle motion + call SetADITmpNacMotion() + + ! Hub motion + call SetADITmpHubMotion() + + ! Blade motion + call SetADITmpBldMotion() + + ! Mapping - one mesh point for each blade + call AllocAry(tmpMeshPtToBladeNum, 2, "tmpMeshPtToBladeNum", ErrStat_F2, ErrMsg_F2); if (Failed()) return + do k=1,SimSettings%TrbCfg%NumBl + tmpMeshPtToBladeNum(k) = k + enddo + + ! Setup the rotor + call ADI_C_SetupRotor(1_c_int, 1_c_int, & ! iWT -- turbine number, IsHAWT=True + StructTmp%PtfmPosAng_c(1:3), & ! Only x,y,z location, no orientation + StructTmp%HubPos_c, StructTmp%HubDCM_c, & ! HubPos, Hub orientation DCM, + StructTmp%NacPos_c, StructTmp%NacDCM_c, & ! NacPos, Nac orientation DCM, + int(SimSettings%TrbCfg%NumBl,c_int), & ! NumBlades + StructTmp%BldPos_c, StructTmp%BldDCM_c, & ! Blade root positions, blade root orientation DCM (flattened, concatenated) + int(SimSettings%TrbCfg%NumBl,c_int), & ! Num mesh points (only one per blade) + StructTmp%BldPos_c, StructTmp%BldDCM_c, & ! Blade root positions, blade root orientation DCM (flattened, concatenated) + tmpMeshPtToBladeNum, & ! MeshPtToBladeNum + ErrStat_C2, ErrMsg_C2 ) + if (Failed_c('ADI_C_SetupRotor')) return + + AD_InputFile_C = c_null_char + AD_InputFile_C = transfer(trim(SimSettings%ModSettings%AD_InputFile ), AD_InputFile_C ) + IfW_InputFile_C = c_null_char + IfW_InputFile_C = transfer(trim(SimSettings%ModSettings%IfW_InputFile), IfW_InputFile_C) + OutRootName_C = transfer(trim(SimSettings%Sim%OutRootName)//'.ADI'//c_null_char, OutRootName_C) + call ADI_C_Init( & + 0, & ! ADinputFilePassed; 0 for file, 1 for string + c_loc(AD_InputFile_C(1)), & ! ADinputFileString_C; Input file as a single string with lines delineated by C_NULL_CHAR + IntfStrLen, & ! ADinputFileStringLength_C; length of the input file string + 0, & ! IfWinputFilePassed; 0 for file, 1 for string + 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, & + ErrStat_C2, ErrMsg_C2) + if (Failed_c('ADI_C_Init')) return + + ! store channel info + WrOutputData%NumChans_ADI = int(ADI_NumChannels_c,IntKi) + call TransferOutChanNamesUnits(WrOutputData%NumChans_ADI, 'WriteOutputHdr_ADI', ADI_WriteOutputHdr_c, WrOutputData%WriteOutputHdr_ADI, ErrStat_F2, ErrMsg_F2); if (Failed()) return + call TransferOutChanNamesUnits(WrOutputData%NumChans_ADI, 'WriteOutputUnt_ADI', ADI_WriteOutputUnt_c, WrOutputData%WriteOutputUnt_ADI, ErrStat_F2, ErrMsg_F2); if (Failed()) return + + + !------------------------------ + ! Assemble data for output file + !------------------------------ + if (SimSettings%Outs%OutFile > 0_IntKi) then + WrOutputData%OutName = trim(SimSettings%Sim%OutRootName)//'.out' + call InitOutputFile(WrOutputData,ErrStat_F2,ErrMsg_F2); if (Failed()) return + + ! allocate storage for output channels from each of the modules, and c_float versions + call AllocAry(WrOutputData%OutData_SS, WrOutputData%Numchans_SS, 'OutData_SS', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_SS_c, WrOutputData%Numchans_SS, 'OutData_SS_c', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_MD, WrOutputData%Numchans_MD, 'OutData_MD', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_MD_c, WrOutputData%Numchans_MD, 'OutData_MD_c', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_ADI, WrOutputData%Numchans_ADI, 'OutData_ADI', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_ADI_c, WrOutputData%Numchans_ADI, 'OutData_ADI_c', ErrStat_F2, ErrMsg_F2); if (Failed()) return + endif + + !------------------------------ + ! Final cleanup + !------------------------------ + ! Initialize time counting for VTK + VTKn_Global = 0_IntKi + VTKn_last = -1_IntKi + call ShowReturnData() + +contains + logical function Failed_c(txt) + character(*), intent(in) :: txt + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, txt) + Failed_c = ErrStat_C >= AbortErrLev_C + if (Failed_c) call CleanUp() + end function Failed_c + logical function Failed() + call SetErrStat_F2C(ErrStat_F2, ErrMsg_F2, ErrStat_C, ErrMsg_C) + Failed = ErrStat_C >= AbortErrLev_C + if (Failed) call Cleanup() + end function Failed + subroutine Cleanup() + call NWTC_Library_DestroyFileInfoType(FileInfo_In, ErrStat_F2, ErrMsg_F2) ! ignore error from this + if (ScreenLogOutput_Un > 0) close(ScreenLogOutput_Un) + if (allocated(tmpMeshPtToBladeNum)) deallocate(tmpMeshPtToBladeNum) + end subroutine Cleanup + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: WaveTank_Init input values") + call WrScr(" --------------------------------------------------------") + call WrScr(" WT_InputFile_C -> "//trim(InputFile)) + call WrScr(" --------------------------------------------------------") + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: WaveTank_Init returned values") + call WrScr(" --------------------------------------------------------") + call WrScr(" RootName_C <- "//trim(SimSettings%Sim%OutRootName)) + call WrScr(" WrVTK_Dir_C <- "//trim(SimSettings%Viz%WrVTK_Dir)) + call WrScr("-----------------------------------------------------------") + end subroutine + subroutine AllocTmpStorage(ErrStat3,ErrMsg3) + integer(IntKi), intent(out) :: ErrStat3 + character(ErrMsgLen), intent(out) :: ErrMsg3 + call AllocAry(CalcStepIO%FrcMom_ADI_c, 6*SimSettings%TrbCfg%NumBl, 'FrcMom_ADI_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + call AllocAry(StructTmp%BldPos_c, 3*SimSettings%TrbCfg%NumBl, 'TmpBldPos_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + call AllocAry(StructTmp%BldDCM_c, 9*SimSettings%TrbCfg%NumBl, 'TmpBldDCM_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + call AllocAry(StructTmp%BldVel_c, 6*SimSettings%TrbCfg%NumBl, 'TmpBldVel_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + call AllocAry(StructTmp%BldAcc_c, 6*SimSettings%TrbCfg%NumBl, 'TmpBldAcc_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + end subroutine +end subroutine WaveTank_Init + +!subroutine DeallocEverything() +!end subroutine DeallocEverything + + +!> Step from T-dt to T and output values at T +subroutine WaveTank_CalcStep( & + time_c, & + pos_c, & + vel_c, & + acc_c, & + loads_c, & + buoyWaveElev_c, & + ErrStat_C, & + ErrMsg_C & +) BIND (C, NAME='WaveTank_CalcStep') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_CalcStep +!GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_CalcStep +#endif + real(c_double), intent(in ) :: time_c + real(c_float), intent(in ) :: pos_c(6) ! [x,y,z,roll,pitch,yaw] + real(c_float), intent(in ) :: vel_c(6) ! [x_dot,y_dot,z_dot,roll_dot,pitch_dot,yaw_dot] + real(c_float), intent(in ) :: acc_c(6) ! [x_ddot,y_ddot,z_ddot,roll_ddot,pitch_ddot,yaw_ddot] + real(c_float), intent( out) :: loads_c(6) ! [Fx,Fy,Fz,Mx,My,Mz] + real(c_float), intent( out) :: buoyWaveElev_c ! wave elevation at buoy + integer(c_int), intent( out) :: ErrStat_C + character(c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer(c_int) :: ErrStat_C2 + character(c_char) :: ErrMsg_C2(ErrMsgLen_C) + integer(IntKi) :: ErrStat_F2 + character(ErrMsgLen) :: ErrMsg_F2 + integer(IntKi) :: i + + ! Initialize error handling + ErrStat_C = ErrID_None + ErrMsg_C = " "//C_NULL_CHAR + + + ! debugging + if (SimSettings%Sim%DebugLevel > 0_c_int) call ShowPassedData() + + ! zero loads in case of error + loads_c = 0.0_c_float + + ! Transfer CalcStepIO data (storing for output to file) + CalcStepIO%Time_c = time_C + CalcStepIO%PosAng_c = pos_c + CalcStepIO%Vel_c = vel_c + CalcStepIO%Acc_c = acc_c + + + !-------------------------------------- + ! Update motion meshes + !-------------------------------------- + call StructMotionUpdate(SimSettings, CalcStepIO, MeshMotions, MeshMaps, StructTmp, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + + !-------------------------------------- + ! Wave elevation at buoy, update buoy + !-------------------------------------- + StructTmp%BuoyPos_c(1:2) = real(SimSettings%WaveBuoy%XYLoc, c_float) + call SeaSt_C_GetSurfElev(Time_C, StructTmp%BuoyPos_c(1:2), buoyWaveElev_c, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::SeaSt_C_GetSurfElev') + if (ErrStat_C >= AbortErrLev_C) return + MeshMotions%WaveBuoyMotion%TranslationDisp(:,1) = (/ 0.0_ReKi, 0.0_ReKi, real(buoyWaveElev_c, ReKi) /) + + + !-------------------------------------- + ! Write VTK if requested + ! Do this here in case failed calcs + !-------------------------------------- + if (SimSettings%Viz%WrVTK > 0_c_int) then + ! only write on desired time interval (same logic used in c-binding modules) + VTKn_Global = nint(Time_C / SimSettings%Viz%WrVTK_DT) + if (VTKn_Global /= VTKn_last) then ! already wrote this one + VTKn_last = VTKn_Global ! store the current number to make sure we don't write it twice + call WrVTK_Struct(VTKn_Global, SimSettings, MeshMotions, MeshLoads, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + endif + endif + + + !-------------------------------------- + ! call SeaState_Calc (writes vis) + !-------------------------------------- + call SeaSt_C_CalcOutput(Time_C, WrOutputData%OutData_SS_c, ErrStat_C, ErrMsg_C) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::SeaSt_C_CalcOutput') + if (ErrStat_C >= AbortErrLev_C) return + ! transfer data for writing out + WrOutputData%OutData_SS = real(WrOutputData%OutData_SS_c, ReKi) + + + !-------------------------------------- + ! MD calculations + !-------------------------------------- + ! Platform positions at T + call SetMDTmpMotion() + + ! Update to T+DT + call MD_C_UpdateStates(TimePrev_c, Time_c, StructTmp%PtfmPosAng_c, StructTmp%PtfmVel_c, StructTmp%PtfmAcc_c, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::MD_C_UpdateStates') + if (ErrStat_C >= AbortErrLev_C) return + + ! get loads at T+DT + call MD_C_CalcOutput(Time_c, StructTmp%PtfmPosAng_c, StructTmp%PtfmVel_c, StructTmp%PtfmAcc_c, CalcStepIO%FrcMom_MD_c, WrOutputData%OutData_MD, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::MD_C_CalcOutput') + if (ErrStat_C >= AbortErrLev_C) return + + ! Put mooring loads onto mesh + call SetMDMeshLoads() + + + !-------------------------------------- + ! ADI calculations + !-------------------------------------- + ! Nacelle motion + call SetADITmpNacMotion() + + ! Hub motion + call SetADITmpHubMotion() + + ! Blade motion + call SetADITmpBldMotion() + + ! Set the rotor motion (assumed single rotor) + ! NOTE: ADI handles blade root and mesh seaparately. For our purposes, + ! the blade root and the first mesh node of the blade (using only + ! one point for a rigid blade) are identical. + call ADI_C_SetRotorMotion( 1_IntKi, & ! rotor number + StructTmp%HubPos_c, StructTmp%HubDCM_c, StructTmp%HubVel_c, StructTmp%HubAcc_c, & + StructTmp%NacPos_c, StructTmp%NacDCM_c, StructTmp%NacVel_c, StructTmp%NacAcc_c, & + StructTmp%BldPos_c, StructTmp%BldDCM_c, StructTmp%BldVel_c, StructTmp%BldAcc_c, & + int(SimSettings%TrbCfg%NumBl, c_int), & ! Number of mesh points (number of blade roots) + StructTmp%BldPos_c, StructTmp%BldDCM_c, StructTmp%BldVel_c, StructTmp%BldAcc_c, & + ErrStat_C2, ErrMsg_C2 ) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::ADI_C_SetRotorMotion') + if (ErrStat_C >= AbortErrLev_C) return + + ! Update ADI states to next time + call ADI_C_UpdateStates(TimePrev_c, Time_c, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::ADI_C_UpdateStates') + if (ErrStat_C >= AbortErrLev_C) return + + ! calculate outputs from ADI + call ADI_C_CalcOutput(Time_c, WrOutputData%OutData_ADI, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::ADI_C_CalcOutput') + if (ErrStat_C >= AbortErrLev_C) return + + ! get loads from rotor (assumed single rotor) + call ADI_C_GetRotorLoads( 1_IntKi, & ! rotor number + int(SimSettings%TrbCfg%NumBl, c_int), & ! Number of mesh points (number of blade roots) + CalcStepIO%FrcMom_ADI_c, & ! 6xNumMeshPts_C array [Fx,Fy,Fz,Mx,My,Mz] -- forces and moments (global) + CalcStepIO%HubVel_ADI_c, & ! Wind speed array [Vx,Vy,Vz] -- (m/s) (global) + ErrStat_C2, ErrMsg_C2 ) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::ADI_C_GetRotorLoads') + if (ErrStat_C >= AbortErrLev_C) return + + ! Set load on blade root mesh + call SetADIMeshLoads() + + + !-------------------------------------- + ! Transfer mesh loads back to platform + !-------------------------------------- + call StructLoadsMeshTransfer(SimSettings, CalcStepIO, MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + ! set output loads at platform + CalcStepIO%FrcMom_C(1:3) = real(MeshLoads%PtfmPtLoads%Force(1:3,1), c_float) + CalcStepIO%FrcMom_C(4:6) = real(MeshLoads%PtfmPtLoads%Moment(1:3,1), c_float) + loads_c = CalcStepIO%FrcMom_C + + ! debugging + if (SimSettings%Sim%DebugLevel > 0_c_int) call ShowReturnData() + + ! Transfer outputs and write to file + if (SimSettings%Outs%OutFile > 0_IntKi) then + ! output to file + call WriteOutputLine(SimSettings%Outs%OutFmt, CalcStepIO, StructTmp, WrOutputData, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + endif + + ! keep track of the time + if (Time_c > TimePrev_c) TimePrev_c = Time_c + + +contains + logical function Failed() + call SetErrStat_F2C(ErrStat_F2, ErrMsg_F2, ErrStat_C, ErrMsg_C) + Failed = ErrStat_C >= AbortErrLev_C + !if (Failed) call Cleanup() + end function Failed + subroutine ShowPassedData() + character(120) :: TmpStr + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: WaveTank_CalcStep input values") + call WrScr(" --------------------------------------------------------") + call WrScr(" time_c -> "//trim(Num2LStr(time_c))) + write(TmpStr,'("(", *(f10.5, :, ","))') pos_c; TmpStr=trim(TmpStr)//" )" + call WrScr(" pos_c -> "//trim(TmpStr)) + write(TmpStr,'("(", *(f10.5, :, ","))') vel_c; TmpStr=trim(TmpStr)//" )" + call WrScr(" vel_c -> "//trim(TmpStr)) + write(TmpStr,'("(", *(f10.5, :, ","))') acc_c; TmpStr=trim(TmpStr)//" )" + call WrScr(" acc_c -> "//trim(TmpStr)) + call WrScr(" --------------------------------------------------------") + end subroutine ShowPassedData + subroutine ShowReturnData() + character(120) :: TmpStr + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: WaveTank_CalcStep returned values") + call WrScr(" --------------------------------------------------------") + write(TmpStr,'("(", *(f14.2, :, ","))') loads_c; TmpStr=trim(TmpStr)//" )" + call WrScr(" loads_c <- "//trim(TmpStr)) + call WrScr(" buoyWaveElev_c <- "//trim(Num2LStr(buoyWaveElev_c))) + call WrScr("-----------------------------------------------------------") + end subroutine +end subroutine + + + + + +!--------------------------------- +! routines to copy mesh info to temporary vars for transfer +subroutine SetMDTmpMotion() + type(MeshType), pointer :: Ptfm + Ptfm => MeshMotions%PtfmPtMotion + StructTmp%PtfmPosAng_c(1:3) = real(Ptfm%Position(1:3,1), c_float) + real(Ptfm%TranslationDisp(1:3,1), c_float) + StructTmp%PtfmPosAng_c(4:6) = real(CalcStepIO%PosAng_c(4:6), c_float) ! Euler angle set -- used to set Orientation + StructTmp%PtfmVel_c(1:3) = real(Ptfm%TranslationVel(1:3,1), c_float) + StructTmp%PtfmVel_c(4:6) = real(Ptfm%RotationVel(1:3,1), c_float) + StructTmp%PtfmAcc_c(1:3) = real(Ptfm%TranslationAcc(1:3,1), c_float) + StructTmp%PtfmAcc_c(4:6) = real(Ptfm%RotationAcc(1:3,1), c_float) +end subroutine + +subroutine SetMDMeshLoads() + type(MeshType), pointer :: MoorLd + integer(IntKi) :: i1,i2 + MoorLd => MeshLoads%MooringLoads + MoorLd%Force(1:3,1) = real(CalcStepIO%FrcMom_MD_c(1:3), ReKi) + MoorLd%Moment(1:3,1) = real(CalcStepIO%FrcMom_MD_c(4:6), ReKi) +end subroutine + +subroutine SetADITmpNacMotion() +! NOTE: we are treating the nacelle as the tower top and simply using that location +! NOTE: the nacelle drag isn't getting returned anyhow + type(MeshType), pointer :: Twr + Twr => MeshMotions%TowerMotion + StructTmp%NacPos_c(1:3) = real(Twr%Position(1:3,2), c_float) + real(Twr%TranslationDisp(1:3,2), c_float) + StructTmp%NacDCM_c(1:9) = real(reshape(Twr%Orientation(1:3,1:3,2), (/9/)), c_float) + StructTmp%NacVel_c(1:3) = real(Twr%TranslationVel(1:3,2), c_float) + StructTmp%NacVel_c(4:6) = real(Twr%RotationVel(1:3,2), c_float) + StructTmp%NacAcc_c(1:3) = real(Twr%TranslationAcc(1:3,2), c_float) + StructTmp%NacAcc_c(4:6) = real(Twr%RotationAcc(1:3,2), c_float) +end subroutine + +subroutine SetADITmpHubMotion() + type(MeshType), pointer :: Hub + Hub => MeshMotions%HubMotion + StructTmp%HubPos_c(1:3) = real(Hub%Position(1:3,1), c_float) + real(Hub%TranslationDisp(1:3,1), c_float) + StructTmp%HubDCM_c(1:9) = real(reshape(Hub%Orientation(1:3,1:3,1), (/9/)), c_float) + StructTmp%HubVel_c(1:3) = real(Hub%TranslationVel(1:3,1), c_float) + StructTmp%HubVel_c(4:6) = real(Hub%RotationVel(1:3,1), c_float) + StructTmp%HubAcc_c(1:3) = real(Hub%TranslationAcc(1:3,1), c_float) + StructTmp%HubAcc_c(4:6) = real(Hub%RotationAcc(1:3,1), c_float) +end subroutine + +subroutine SetADITmpBldMotion() + type(MeshType), pointer :: Root + integer(IntKi) :: k,i1,i2 + do k=1,SimSettings%TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + ! position -- x,y,z + i1=(k-1)*3+1; i2=i1+2 + StructTmp%BldPos_c(i1:i2) = real(Root%Position(1:3,1), c_float) + real(Root%TranslationDisp(1:3,1), c_float) + ! orientation DCM unpacked flat -- 9 elements + i1=(k-1)*9+1; i2=i1+8 + StructTmp%BldDCM_c(i1:i2) = real(reshape(Root%Orientation(1:3,1:3,1),(/9/)), c_float) + ! Translation Vel / Accel -- TVx,TVy,TVz / TAx, TAy, TAz + i1=(k-1)*6+1; i2=i1+2 + StructTmp%BldVel_c(i1:i2) = real(Root%TranslationVel(1:3,1), c_float) + StructTmp%BldAcc_c(i1:i2) = real(Root%TranslationAcc(1:3,1), c_float) + ! Rotation Vel / Accel -- RVx,RVy,RVz / RAx, RAy, RAz + i1=(k-1)*6+4; i2=i1+2 + StructTmp%BldVel_c(i1:i2) = real(Root%RotationVel(1:3,1), c_float) + StructTmp%BldAcc_c(i1:i2) = real(Root%RotationAcc(1:3,1), c_float) + enddo +end subroutine + +subroutine SetADIMeshLoads() + type(MeshType), pointer :: RootLd + integer(IntKi) :: k,i1,i2 + do k=1,SimSettings%TrbCfg%NumBl + RootLd => MeshLoads%BladeRootLoads(k) + ! Forces + i1=(k-1)*6+1; i2=i1+2 + RootLd%Force(1:3,1) = real(CalcStepIO%FrcMom_ADI_c(i1:i2), ReKi) + ! Momment + i1=(k-1)*6+4; i2=i1+2 + RootLd%Moment(1:3,1) = real(CalcStepIO%FrcMom_ADI_c(i1:i2), ReKi) + enddo +end subroutine + + + +subroutine WaveTank_End(ErrStat_C, ErrMsg_C) bind (C, NAME="WaveTank_End") #ifndef IMPLICIT_DLLEXPORT -!DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_CalcOutput -!GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_CalcOutput +!DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_End +!GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_End #endif -! INTEGER(C_INT) :: delta_time -INTEGER(C_INT) :: frame_number -REAL(C_FLOAT), INTENT(IN ) :: positions_x(N_CAMERA_POINTS) -REAL(C_FLOAT), INTENT(IN ) :: positions_y(N_CAMERA_POINTS) -REAL(C_FLOAT), INTENT(IN ) :: positions_z(N_CAMERA_POINTS) -REAL(C_FLOAT), INTENT(IN ) :: rotation_matrix(9) -REAL(C_FLOAT), INTENT( OUT) :: loads(N_CAMERA_POINTS) -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 :: i + ! Local variables + integer(c_int) :: ErrStat_C2 + character(kind=c_char, len=ErrMsgLen_C) :: ErrMsg_C2 + integer(IntKi) :: ErrStat_F2 + character(ErrMsgLen) :: ErrMsg_F2 -IF ( MOD(frame_number / load_period, 2) == 0 ) THEN - loads = -1.0 -ELSE - loads = 1.0 -ENDIF + ErrStat_C = ErrID_None + ErrMsg_C = " "//C_NULL_CHAR -END SUBROUTINE + ! destroy mesh info + call StructDestroy(MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat_F2, ErrMsg_F2) + call SetErrStat_F2C(ErrStat_F2, ErrMsg_F2, ErrStat_C, ErrMsg_C) -SUBROUTINE WaveTank_End() bind (C, NAME="WaveTank_End") + ! in case we were writing to a file instead of the screen + if (ScreenLogOutput_Un > 0) close(ScreenLogOutput_Un) + call MD_C_END(ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'MD_C_END') -IMPLICIT NONE + call SeaSt_C_END(ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'SeaSt_C_END') + call ADI_C_END(ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'ADI_C_END') + ! close output file + if (WrOutputData%OutUn > 0) then + close(WrOutputData%OutUn, iostat=ErrStat_F2) + if (ErrStat_F2 /= 0_IntKi) call SetErrStat_C(int(ErrID_Fatal,c_int), 'could no close output file '//trim(WrOutputData%OutName), ErrStat_C, ErrMsg_C, 'ADI_C_END') + WrOutputData%OutUn = -1 ! mark as closed - prevents faults + endif +end subroutine -END SUBROUTINE -end module WaveTankTesting +subroutine WaveTank_SetWaveFieldPointer(ErrStat_C, ErrMsg_C) bind (C, NAME="WaveTank_SetWaveFieldPointer") +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_SetWaveFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_SetWaveFieldPointer +#endif + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer(c_int) :: ErrStat_C2 + character(kind=c_char, len=ErrMsgLen_C) :: ErrMsg_C2 + + ! Set the SeaState FlowField pointer onto MoorDyn + type(c_ptr) :: WaveFieldPointer_C + type(SeaSt_WaveFieldType), pointer :: WaveFieldPointer_F => NULL() ! used only in sanity check + + ErrStat_C = ErrID_None + ErrMsg_C = " "//C_NULL_CHAR + + call SeaSt_C_GetWaveFieldPointer(WaveFieldPointer_C, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_SetWaveFieldPointer') + if (ErrStat_C >= AbortErrLev_C) return + + call C_F_POINTER(WaveFieldPointer_C, WaveFieldPointer_F) + ! Verify that the data in the WaveField pointer has been set + if (WaveFieldPointer_F%WtrDpth == 0) then + ErrStat_C2 = ErrID_Fatal + ErrMsg_C2 = "SeaState WaveFieldPointer is WtrDpth is 0.0, so it it probably not initialized." + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_SetWaveFieldPointer') + return + endif + + ! There isn't a good way to check for an error here. Will get caught at init + call MD_C_SetWaveFieldData(WaveFieldPointer_C) + + ! Probably doesn't matter, but clear the fortran pointer just in case + WaveFieldPointer_F => NULL() +end subroutine + +END MODULE WaveTankTesting diff --git a/glue-codes/labview/src/WaveTank_IO.f90 b/glue-codes/labview/src/WaveTank_IO.f90 new file mode 100644 index 0000000000..ab7cd5f84c --- /dev/null +++ b/glue-codes/labview/src/WaveTank_IO.f90 @@ -0,0 +1,398 @@ +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2025 National Renewable Energy Laboratory +! +! This file is a module specific to an experimental wave tank at NREL. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +!********************************************************************************************************************************** +! +! This code is designed to connect with LabView for a specific wave tank test case and likely will not work for other purposes. +! +! For this test, a physical platform is deployed in a wave tank with cable acutators that are controlled through LabView. This +! module is called to provide some loads that are not present in the physical tank setup. These include the following: +! - rotor loading from a fixed RPM MHK rotor from AeroDyn. This is calculated from either steady current provided by SeaState +! - Mooring loads from MoorDyn +! +! +!********************************************************************************************************************************** +MODULE WaveTank_IO + use ISO_C_BINDING + use NWTC_Library + use NWTC_IO + use WaveTank_Types + + implicit none + private + + public :: ParseInputFile + public :: ValidateInputFile + public :: TransferOutChanNamesUnits + public :: InitOutputFile + public :: WriteOutputLine + + ! These channels are output by default + integer(IntKi), parameter :: NumDefChans = 42 + character(OutStrLenM1), parameter :: DefChanNames(NumDefChans) = (/ "Time ", & + "Ptfm_x ","Ptfm_y ","Ptfm_z ", & ! position (absolute global) + "Ptfm_Rx ","Ptfm_Ry ","Ptfm_Rz ", & ! Euler angles phi,theta,psi + "Ptfm_Vx ","Ptfm_Vy ","Ptfm_Vz ", & ! translation vel + "Ptfm_RVx ","Ptfm_RVy ","Ptfm_RVz ", & ! rotation vel + "Ptfm_Ax ","Ptfm_Ay ","Ptfm_Az ", & ! translation acc + "Ptfm_RAx ","Ptfm_RAy ","Ptfm_RAz ", & ! rotation acc + "Ptfm_Fx ","Ptfm_Fy ","Ptfm_Fz ", & ! Forces total + "Ptfm_Mx ","Ptfm_My ","Ptfm_Mz ", & ! Moments total + "Ptfm_MD_Fx ","Ptfm_MD_Fy ","Ptfm_MD_Fz ", & ! Forces from MD + "Ptfm_MD_Mx ","Ptfm_MD_My ","Ptfm_MD_Mz ", & ! Moments from MD + "Ptfm_ADI_Fx ","Ptfm_ADI_Fy ","Ptfm_ADI_Fz ", & ! Forces from ADI + "Ptfm_ADI_Mx ","Ptfm_ADI_My ","Ptfm_ADI_Mz ", & ! Moments from ADI + "Azimuth ","RotSpeed ","BlPitch ", & + "NacYaw ","BuoyElev " & + /) + character(OutStrLenM1), parameter :: DefChanUnits(NumDefChans) = (/ "(s) ", & + "(m) ","(m) ","(m) ", & + "(rad) ","(rad) ","(rad) ", & + "(m/s) ","(m/s) ","(m/s) ", & + "(rad/s) ","(rad/s) ","(rad/s) ", & + "(m/s^2) ","(m/s^2) ","(m/s^2) ", & + "(rad/s^2) ","(rad/s^2) ","(rad/s^2) ", & + "(N) ","(N) ","(N) ", & ! Forces total + "(N-m) ","(N-m) ","(N-m) ", & ! Moments total + "(N) ","(N) ","(N) ", & ! Forces from MD + "(N-m) ","(N-m) ","(N-m) ", & ! Moments from MD + "(N) ","(N) ","(N) ", & ! Forces from ADI + "(N-m) ","(N-m) ","(N-m) ", & ! Moments from ADI + "(deg) ","(RPM) ","(deg) ", & + "(deg) ","(m) " & + /) + +contains + +subroutine ParseInputFile(FileInfo_In, SimSettings, ErrStat, ErrMsg) + type(FileInfoType), intent(in ) :: FileInfo_In !< The derived type for holding the file information. + type(SimSettingsType), intent( out) :: SimSettings + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + + ! Local variables + integer :: CurLine + character(1024), target :: TmpPath + character(1024) :: FileName + integer(IntKi) :: ErrStat2 ! local status of error message + character(ErrMsgLen) :: ErrMsg2 ! local error message if errStat /= ErrID_None + character(*), parameter :: RoutineName = 'WaveTankTesting.ParseInputFile' + + ErrStat = ErrID_None + ErrMsg = " " + + CurLine = 1 + ! Separator/header lines skipped automatically + ! ----- Simulation control ------------- + call ParseVar( FileInfo_In, CurLine, 'DT', SimSettings%Sim%DT, ErrStat2, ErrMsg2); if(Failed()) return; ! timestep (unused) + call ParseVar( FileInfo_In, CurLine, 'TMax', SimSettings%Sim%TMax, ErrStat2, ErrMsg2); if(Failed()) return; ! Max sim time (used only with SeaState wavemod 5) + call ParseVar( FileInfo_In, CurLine, 'MHK', SimSettings%Sim%MHK, ErrStat2, ErrMsg2); if(Failed()) return; ! MHK turbine type (switch) {0=Not an MHK turbine; 1=Fixed MHK turbine; 2=Floating MHK turbine} + call ParseVar( FileInfo_In, CurLine, 'InterpOrd', SimSettings%Sim%InterpOrd, ErrStat2, ErrMsg2); if(Failed()) return; ! Interpolation order [unused] +!TODO: These are placeholders for later use. Some of the logic is incomplete which is why this has been commented out. +! call ParseVar( FileInfo_In, CurLine, 'ScaleFact', SimSettings%Sim%ScaleFact, ErrStat2, ErrMsg2); if(Failed()) return; ! scaling factor for scaling full size model to wavetank scale results (Froude scaling: lambda = full_dimension / scale_dimension) [>1 expected] (-) +! call ParseVar( FileInfo_In, CurLine, 'DensFact', SimSettings%Sim%DensFact, ErrStat2, ErrMsg2); if(Failed()) return; ! ratio of density - Density_full/Density_model (rho_F/rho_M). Used with Froude scaling of forces/moments" (-) + call ParseVar( FileInfo_In, CurLine, 'DebugLevel', SimSettings%Sim%DebugLevel, ErrStat2, ErrMsg2); if(Failed()) return; ! 0: none, 1: I/O summary, 2: +positions/orientations passed, 3:, 4: +all meshes + call ParseVar( FileInfo_In, CurLine, 'OutRootName', SimSettings%Sim%OutRootName, ErrStat2, ErrMsg2); if(Failed()) return; ! Root name for any summary or other files + ! -------- Environment ---------------- + call ParseVar( FileInfo_In, CurLine, 'Gravity', SimSettings%Env%Gravity, ErrStat2, ErrMsg2); if(Failed()) return; ! Gravitational acceleration (m/s^2) + call ParseVar( FileInfo_In, CurLine, 'WtrDens', SimSettings%Env%WtrDens, ErrStat2, ErrMsg2); if(Failed()) return; ! Water density (kg/m^3) + call ParseVar( FileInfo_In, CurLine, 'WtrVisc', SimSettings%Env%WtrVisc, ErrStat2, ErrMsg2); if(Failed()) return; ! Kinematic viscosity of working fluid (m^2/s) + call ParseVar( FileInfo_In, CurLine, 'SpdSound', SimSettings%Env%SpdSound, ErrStat2, ErrMsg2); if(Failed()) return; ! Speed of sound in working fluid (m/s) + call ParseVar( FileInfo_In, CurLine, 'Patm', SimSettings%Env%Patm, ErrStat2, ErrMsg2); if(Failed()) return; ! Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] + call ParseVar( FileInfo_In, CurLine, 'Pvap', SimSettings%Env%Pvap, ErrStat2, ErrMsg2); if(Failed()) return; ! Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] + call ParseVar( FileInfo_In, CurLine, 'WtrDpth', SimSettings%Env%WtrDpth, ErrStat2, ErrMsg2); if(Failed()) return; ! Water depth (m) + call ParseVar( FileInfo_In, CurLine, 'MSL2SWL', SimSettings%Env%MSL2SWL, ErrStat2, ErrMsg2); if(Failed()) return; ! Offset between still-water level and mean sea level (m) [positive upward] + ! -------- SeaState ------------------- + call ParseVar( FileInfo_In, CurLine, 'SS_InputFile', SimSettings%ModSettings%SS_InputFile, ErrStat2, ErrMsg2); if(Failed()) return; ! SeaState input file + call ParseVar( FileInfo_In, CurLine, 'WaveTimeShift', SimSettings%ModSettings%WaveTimeShift, ErrStat2, ErrMsg2); if(Failed()) return; ! Shift the SeaState wavetime by this amount (for phase shifting waves to match tank) + ! -------- MoorDyn -------------------- + call ParseVar( FileInfo_In, CurLine, 'MD_InputFile', SimSettings%ModSettings%MD_InputFile, ErrStat2, ErrMsg2); if(Failed()) return; ! MoorDyn input file + ! -------- AeroDyn + InflowWind ------- + call ParseVar( FileInfo_In, CurLine, 'AD_InputFile', SimSettings%ModSettings%AD_InputFile, ErrStat2, ErrMsg2); if(Failed()) return; ! AeroDyn input file + call ParseVar( FileInfo_In, CurLine, 'IfW_InputFile', SimSettings%ModSettings%IfW_InputFile, ErrStat2, ErrMsg2); if(Failed()) return; ! InflowWind input file + ! -------- Turbine Configuration ------ + call ParseVar( FileInfo_In, CurLine, 'NumBl', SimSettings%TrbCfg%NumBl, ErrStat2, ErrMsg2); if(Failed()) return; ! Number of blades (-) + call ParseVar( FileInfo_In, CurLine, 'HubRad', SimSettings%TrbCfg%HubRad, ErrStat2, ErrMsg2); if(Failed()) return; ! The distance from the rotor apex to the blade root (meters) + call ParseVar( FileInfo_In, CurLine, 'PreCone', SimSettings%TrbCfg%PreCone, ErrStat2, ErrMsg2); if(Failed()) return; ! Blade cone angle (degrees) + SimSettings%TrbCfg%PreCone = D2R * SimSettings%TrbCfg%PreCone + call ParseVar( FileInfo_In, CurLine, 'OverHang', SimSettings%TrbCfg%OverHang, ErrStat2, ErrMsg2); if(Failed()) return; ! Distance from yaw axis to rotor apex [3 blades] or teeter pin [2 blades] (meters) + call ParseVar( FileInfo_In, CurLine, 'ShftTilt', SimSettings%TrbCfg%ShftTilt, ErrStat2, ErrMsg2); if(Failed()) return; ! Rotor shaft tilt angle (degrees) + SimSettings%TrbCfg%ShftTilt = D2R * SimSettings%TrbCfg%ShftTilt + call ParseVar( FileInfo_In, CurLine, 'Twr2Shft', SimSettings%TrbCfg%Twr2Shft, ErrStat2, ErrMsg2); if(Failed()) return; ! Vertical distance from the tower-top to the rotor shaft, center of nacelle (meters) + call ParseVar( FileInfo_In, CurLine, 'TowerHt', SimSettings%TrbCfg%TowerHt, ErrStat2, ErrMsg2); if(Failed()) return; ! Height of tower relative MSL + call ParseAry( FileInfo_In, CurLine, 'TowerBsPt', SimSettings%TrbCfg%TowerBsPt, 3, ErrStat2, ErrMsg2); if(Failed()) return; ! Height of tower base relative to PtfmRefPos in x,y, and water surface in z (meters) + call ParseAry( FileInfo_In, CurLine, 'PtfmRefPos', SimSettings%TrbCfg%PtfmRefPos, 3, ErrStat2, ErrMsg2); if(Failed()) return; ! Location of platform reference point, relative to MSL. Motions and loads all connect to this point + call ParseAry( FileInfo_In, CurLine, 'PtfmRefOrient', SimSettings%TrbCfg%PtfmRefOrient, 3, ErrStat2, ErrMsg2); if(Failed()) return; ! Orientation of platform reference point, Euler angle set of roll,pitch,yaw" (deg) + SimSettings%TrbCfg%PtfmRefOrient = D2R * SimSettings%TrbCfg%PtfmRefOrient + ! -------- Turbine Operating Point ---- + call ParseVar( FileInfo_In, CurLine, 'RotSpeed', SimSettings%TrbInit%RotSpeed, ErrStat2, ErrMsg2); if(Failed()) return; ! Rotational speed of rotor in rotor coordinates (rpm) + call ParseVar( FileInfo_In, CurLine, 'NacYaw', SimSettings%TrbInit%NacYaw, ErrStat2, ErrMsg2); if(Failed()) return; ! Initial or fixed nacelle-yaw angle (deg read) + call ParseVar( FileInfo_In, CurLine, 'BldPitch', SimSettings%TrbInit%BldPitch, ErrStat2, ErrMsg2); if(Failed()) return; ! Blade 1 pitch (deg read) + call ParseVar( FileInfo_In, CurLine, 'Azimuth', SimSettings%TrbInit%Azimuth, ErrStat2, ErrMsg2); if(Failed()) return; ! Initial azimuth (deg read) + ! angles are read as degrees, store as radians internally + SimSettings%TrbInit%NacYaw = D2R * SimSettings%TrbInit%NacYaw + SimSettings%TrbInit%BldPitch = D2R * SimSettings%TrbInit%BldPitch + SimSettings%TrbInit%Azimuth = D2R * SimSettings%TrbInit%Azimuth + ! -------- Wave Buoy------------------ + call ParseAry( FileInfo_In, CurLine, 'WaveBuoyLoc', SimSettings%WaveBuoy%XYLoc, 2, ErrStat2, ErrMsg2); if(Failed()) return; ! Location of the wave elevation measurement buoy. SeaState data returned at every timestep at this location (m) + ! -------- Output --------------------- + call ParseVar( FileInfo_In, CurLine, 'SendScreenToFile', SimSettings%Outs%SendScreenToFile, ErrStat2, ErrMsg2); if(Failed()) return; ! send to file .screen.log if true + call ParseVar( FileInfo_In, CurLine, 'OutFile', SimSettings%Outs%OutFile, ErrStat2, ErrMsg2); if(Failed()) return; ! 0: no output file of channels, 1: output file in text format (at default DT) + call ParseVar( FileInfo_In, CurLine, 'OutFmt', SimSettings%Outs%OutFmt, ErrStat2, ErrMsg2); if(Failed()) return; ! Format used for text tabular output, excluding the time channel. (quoted string) + ! -------- VTK output ----------------- + call ParseVar( FileInfo_In, CurLine, 'WrVTK_Dir', SimSettings%Viz%WrVTK_Dir, ErrStat2, ErrMsg2); if(Failed()) return; ! output directory for visualization + call ParseVar( FileInfo_In, CurLine, 'WrVTK', SimSettings%Viz%WrVTK, ErrStat2, ErrMsg2); if(Failed()) return; ! VTK visualization data output: (switch) {0=none; 1=initialization data only; 2=animation; 3=mode shapes} + call ParseVar( FileInfo_In, CurLine, 'WrVTK_type', SimSettings%Viz%WrVTK_type, ErrStat2, ErrMsg2); if(Failed()) return; ! Type of VTK visualization data: (switch) {1=surfaces; 2=basic meshes (lines/points); 3=all meshes (debug)} [unused if WrVTK=0] + call ParseVar( FileInfo_In, CurLine, 'WrVTK_DT', SimSettings%Viz%WrVTK_DT, ErrStat2, ErrMsg2); if(Failed()) return; ! DT for writing VTK files + call ParseAry( FileInfo_In, CurLine, 'VTKNacDim', SimSettings%Viz%VTKNacDim, 6, ErrStat2, ErrMsg2); if(Failed()) return; ! Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) + +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + end function Failed +end subroutine + + + +subroutine ValidateInputFile(SimSettings, ErrStat, ErrMsg) + type(SimSettingsType), intent(inout) :: SimSettings + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTankTesting.ValidateInputFile' + logical :: file_exists + + ErrStat = ErrID_None + ErrMsg = "" + + !------------------------ + ! Sim Control + !------------------------ + if (SimSettings%Sim%MHK /= 2_c_int) call SetErrStat(ErrID_Fatal, "WaveTank module only works for floating MHK turbines at present (MHK=2).",ErrStat,ErrMsg,RoutineName) + if (SimSettings%Sim%ScaleFact < 1.0_c_float) call SetErrStat(ErrID_Fatal, "ScaleFact should be > 1", ErrStat,ErrMsg,RoutineName) + if (SimSettings%Sim%ScaleFact > 1.0_c_float) then + call SetErrStat(ErrID_Warn, "ScaleFact should be == 1 for now. Scaling is untested and incomplete!!!!", ErrStat,ErrMsg,RoutineName) + call WrScr("/---------------------------------------------------------\") + call WrScr("|--- WARNING ---- WARNING ---- WARNING ---- WARNING ---|") + call WrScr("|---------------------------------------------------------|") + call WrScr("| |") + call WrScr("| Froude scaling is not complete, nor is it tested!!!! |") + call WrScr("| |") + call WrScr("| At present, only some inputs are scaled, but equations |") + call WrScr("| have not been verified yet. This is useful just for |") + call WrScr("| observing motions are occuring, but will corrupt your |") + call WrScr("| simulation. |") + call WrScr("| |") + call WrScr("| Set ScaleFact=1.0 in your input file. |") + call WrScr("| |") + call WrScr("| TODO: |") + call WrScr("| - verify equations in FroudeScaling* functions |") + call WrScr("| - scale resulting forces / moments |") + call WrScr("| - add scaled time, pos, vel, acc, frc, mom to output |") + call WrScr("| channels and add subscripting to differentiate |") + call WrScr("| |") + call WrScr("\---------------------------------------------------------/") + endif + + + !------------------------ + ! Environment + !------------------------ + + !------------------------ + ! Turbine Config + !------------------------ + + !------------------------ + ! Input files + !------------------------ + inquire(file=SimSettings%ModSettings%SS_InputFile, exist=file_exists); if (.not. file_exists) call SetErrStat(ErrID_Fatal,"Cannot find SeaState input file " //trim(SimSettings%ModSettings%SS_InputFile ),ErrStat,ErrMsg,RoutineName) + inquire(file=SimSettings%ModSettings%MD_InputFile, exist=file_exists); if (.not. file_exists) call SetErrStat(ErrID_Fatal,"Cannot find MoorDyn input file " //trim(SimSettings%ModSettings%MD_InputFile ),ErrStat,ErrMsg,RoutineName) + inquire(file=SimSettings%ModSettings%AD_InputFile, exist=file_exists); if (.not. file_exists) call SetErrStat(ErrID_Fatal,"Cannot find AeroDyn input file " //trim(SimSettings%ModSettings%AD_InputFile ),ErrStat,ErrMsg,RoutineName) + inquire(file=SimSettings%ModSettings%IfW_InputFile, exist=file_exists); if (.not. file_exists) call SetErrStat(ErrID_Fatal,"Cannot find InflowWind input file "//trim(SimSettings%ModSettings%IfW_InputFile),ErrStat,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return + + ! Wave time shift must be positive + if (SimSettings%ModSettings%WaveTimeShift < 0.0_DbKi) call SetErrStat(ErrID_Fatal, "WaveTimeShift must be >= 0",Errstat,ErrMsg,RoutineName) + + !------------------------ + ! Turbine Operating point + !------------------------ + + !------------------------ + ! Output + !------------------------ + + !------------------------ + ! VTK + !------------------------ + +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + end function Failed +end subroutine + + +!> Transfer names or units from c character arrays into fortran character arrays for output file writing +subroutine TransferOutChanNamesUnits(NumChans, name, NamesUnits_C, NamesUnits, ErrStat, ErrMsg) + integer(IntKi), intent(in ) :: NumChans + character(*), intent(in ) :: name + character(c_char), intent(in ) :: NamesUnits_C(:) + character(ChanLen), allocatable, intent( out) :: NamesUnits(:) + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: i,idxStart,idxEnd + call AllocAry(NamesUnits, NumChans, name, ErrStat, ErrMsg) + if (ErrStat >= AbortErrLev) return + do i=1,NumChans + idxStart = (i-1)*ChanLen + 1 + idxEnd = i*ChanLen + NamesUnits(i) = transfer(NamesUnits_C(idxStart:idxEnd),NamesUnits(i)) + enddo +end subroutine + + +!> open the output file and populate the header +subroutine InitOutputFile(WrOutputData, ErrStat, ErrMsg) + type(WrOutputDataType), intent(inout) :: WrOutputData + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: i + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'InitOutputFile' + if (WrOutputData%OutUn > 0) then + ErrStat = ErrID_Warn + ErrMsg = "Output file "//trim(WrOutputData%OutName)//" already open" + return + endif + + call GetNewUnit(WrOutputData%OutUn,ErrStat2,ErrMsg2) + call OpenFOutFile (WrOutputData%OutUn, trim(WrOutputData%OutName), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + +!FIXME: add version info here + ! write header line + write (WrOutputData%OutUn,'(/,A)') 'Simulation run on '//CurDate()//' at '//CurTime()//' using the WaveTank c-binding interface' + write (WrOutputData%OutUn,'()') + write (WrOutputData%OutUn,'()') + write (WrOutputData%OutUn,'()') + write (WrOutputData%OutUn,'()') + + !...................................................... + ! Write the names of the output parameters on one line: + !...................................................... + ! default outputs + call WrFileNR(WrOutputData%OutUn, DefChanNames(1)) ! time channel + do i=2,NumDefChans + call WrFileNR(WrOutputData%OutUn, tab//DefChanNames(i)) + enddo + ! SS + do i=1,WrOutputData%NumChans_SS + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputHdr_SS(i)) + enddo + ! MD + do i=1,WrOutputData%NumChans_MD + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputHdr_MD(i)) + enddo + ! ADI + do i=1,WrOutputData%NumChans_ADI + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputHdr_ADI(i)) + enddo + write (WrOutputData%OutUn,'()') + + !...................................................... + ! Write the units of the output parameters on one line: + !...................................................... + ! default outputs + call WrFileNR(WrOutputData%OutUn, DefChanUnits(1)) ! time channel + do i=2,NumDefChans + call WrFileNR(WrOutputData%OutUn, tab//DefChanUnits(i)) + enddo + ! SS + do i=1,WrOutputData%NumChans_SS + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputUnt_SS(i)) + enddo + ! MD + do i=1,WrOutputData%NumChans_MD + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputUnt_MD(i)) + enddo + ! ADI + do i=1,WrOutputData%NumChans_ADI + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputUnt_ADI(i)) + enddo + write (WrOutputData%OutUn,'()') + +end subroutine + + +subroutine WriteOutputLine(OutFmt, CalcStepIO, StructTmp, WrOutputData, ErrStat, ErrMsg) + character(*), intent(in ) :: OutFmt + type(CalcStepIOdataType), intent(in ) :: CalcStepIO + type(StructTmpType), intent(in ) :: StructTmp ! operating states are in here + type(WrOutputDataType), intent(in ) :: WrOutputData + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: OutUnit + integer(IntKi) :: errStat2 ! Status of error message (we're going to ignore errors in writing to the file) + character(ErrMsgLen) :: errMsg2 ! Error message if ErrStat /= ErrID_None + character(200) :: frmt ! A string to hold a format specifier + character(15) :: tmpStr ! temporary string to print the time output as text + real(ReKi) :: TmpAry5(5) ! temporary array for RotSpeed, BlPitch, NacYaw, Azimuth, BuoyWaveElev + + ErrStat = ErrID_None + ErrMSg = "" + + frmt = '"'//tab//'"'//trim(OutFmt) ! format for array elements from individual modules + OutUnit = WrOutputData%OutUn + if (OutUnit <= 0_IntKi) then + ErrStat = ErrID_Severe + ErrMSg = 'Cannot write to output file '//trim(WrOutputData%OutName) + return + endif + + ! time + write( tmpStr, '(F15.6)' ) CalcStepIO%Time_c + call WrFileNR( OutUnit, tmpStr ) + ! position / orientation euler angles, velocity, accel, resulting force/moment + call WrNumAryFileNR(OutUnit, CalcStepIO%PosAng_c, frmt, errStat2, errMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, CalcStepIO%Vel_c, frmt, errStat2, errMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, CalcStepIO%Acc_c, frmt, errStat2, errMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, CalcStepIO%FrcMom_c, frmt, errStat2, errMsg2); if (Failed()) return ! total + call WrNumAryFileNR(OutUnit, StructTmp%FrcMom_MD_at_Ptfm, frmt, errStat2, errMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, StructTmp%FrcMom_ADI_at_Ptfm, frmt, errStat2, errMsg2); if (Failed()) return + TmpAry5 = (/ R2D*StructTmp%Azimuth, StructTmp%RotSpeed, R2D*StructTmp%BldPitch, R2D*StructTmp%NacYaw, CalcStepIO%BuoyWaveElev /) + call WrNumAryFileNR(OutUnit, TmpAry5, frmt, errStat2, errMsg2); if (Failed()) return + ! channels from modules + call WrNumAryFileNR(OutUnit, WrOutputData%OutData_SS, frmt, errStat2, ErrMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, WrOutputData%OutData_MD, frmt, errStat2, ErrMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, WrOutputData%OutData_ADI, frmt, errStat2, ErrMsg2); if (Failed()) return + ! write a new line (advance to the next line) + write (OutUnit,'()') +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, 'WriteOutputLine') + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + +END MODULE WaveTank_IO diff --git a/glue-codes/labview/src/WaveTank_Registry.txt b/glue-codes/labview/src/WaveTank_Registry.txt new file mode 100644 index 0000000000..f3072b5aca --- /dev/null +++ b/glue-codes/labview/src/WaveTank_Registry.txt @@ -0,0 +1,160 @@ +################################################################################################################################### +# Registry for WaveTank module +# This Registry file is used to create WaveTank_Types.f90 +# It also contains copy, destroy, pack, and unpack routines associated with each defined data types. +# See the NWTC Programmer's Handbook for further information on the format/contents of this file. +# +# Entries are of the form +# +# +# Use ^ as a shortcut for the value in the same column from the previous line. +################################################################################################################################### +# ...... Include files (definitions from NWTC Library) ............................................................................ +include Registry_NWTC_Library.txt +# + +typedef WaveTank/WT SimType c_double DT - - - "timestep" - +typedef ^ ^ c_double TMax - - - "Max sim time" - +typedef ^ ^ c_int MHK - - - "MHK turbine type (switch) {0=Not an MHK turbine; 1=Fixed MHK turbine; 2=Floating MHK turbine}" (-) +typedef ^ ^ c_int InterpOrd - 1 - "Interpolation order" - +typedef ^ ^ c_float ScaleFact - 1 - "scaling factor for scaling full size model to wavetank scale results (Froude scaling: lambda = full_dimension / scale_dimension) (>1 expected) (-) +typedef ^ ^ c_float DensFact - 1 - "ratio of density - Density_full/Density_model (rho_F/rho_M). Used with Froude scaling of forces/moments" (-) +typedef ^ ^ c_int DebugLevel - - - "Debug level for outputs" - +typedef ^ ^ character(1024) OutRootName - - - "Rootname for outputs" - + +typedef ^ EnvType c_float Gravity - - - "gravitational constant (positive for down)" (m/s^2) +typedef ^ ^ ^ WtrDens - - - "Water density" (kg/m^3) +typedef ^ ^ ^ WtrVisc - - - "fluid viscosity" (m^2/s) +typedef ^ ^ ^ SpdSound - - - "Speed of sound in working fluid" (m/s) +typedef ^ ^ ^ Patm - - - "Atmospheric pressure [used only for an MHK turbine cavitation check]" (Pa) +typedef ^ ^ ^ Pvap - - - "Vapour pressure of working fluid [used only for an MHK turbine cavitation check]" (Pa) +typedef ^ ^ ^ WtrDpth - - - "Water depth" (m) +typedef ^ ^ ^ MSL2SWL - - - "Mean sea level to still water level" (m) + +typedef ^ TurbConfigType IntKi NumBl - - - "Number of blades" (-) +typedef ^ ^ SiKi HubRad - - - "The distance from the rotor apex to the blade root" (m) +typedef ^ ^ ^ PreCone - - - "Blade cone angle" (deg) +typedef ^ ^ ^ OverHang - - - "Distance from yaw axis to rotor apex [3 blades] or teeter pin [2 blades]" (m) +typedef ^ ^ ^ ShftTilt - - - "Rotor shaft tilt angle" (deg) +typedef ^ ^ ^ Twr2Shft - - - "Vertical distance from the tower-top to the rotor shaft" (m) +typedef ^ ^ ^ TowerHt - - - "Height of tower relative MSL" (m) +typedef ^ ^ ^ TowerBsPt 3 - - "Tower base location relative to MSL. Consider absolute difference to PtfmRef [floating MHK]" (m) +typedef ^ ^ ^ PtfmRefPos 3 - - "Location of platform reference point, relative to MSL. Motions and loads all connect to this point" (m) +typedef ^ ^ ^ PtfmRefOrient 3 - - "Orientation of platform reference point, Euler angle set of roll,pitch,yaw" (rad) + +typedef ^ TurbInitCondType ReKi RotSpeed - - - "Rotor speed" (RPM) +typedef ^ ^ ^ NacYaw - - - "Initial or fixed nacelle-yaw angle - read as deg, convert to rad" (rad) +typedef ^ ^ ^ BldPitch - - - "Fixed blade pitch for full simulation - read as deg, convert to rad" (rad) +typedef ^ ^ ^ Azimuth - 0 - "Initial azimuth (actual azimuth calculated and not stored) - read as deg, convert to rad" (rad) + +typedef ^ WaveBuoyType ReKi XYLoc 2 - - "Location of the wave elevation measurement buoy. SeaState data returned at every timestep at this location" (m) + +typedef ^ OutFilesType logical SendScreenToFile - - - "send to file .screen.log if true" (-) +typedef ^ ^ c_int OutFile - - - "0: no output file of channels, 1: output file in text format (at DT)" (-) +typedef ^ ^ character(20) OutFmt - - - "Format used for text tabular output, excluding the time channel. (quoted string)" (-) + +typedef ^ VizType c_int WrVTK - - - "Write VTK?" - +typedef ^ ^ ^ WrVTK_type - - - "Write VTK outputs as [1: surface, 2: lines, 3: both]" - +typedef ^ ^ c_double WrVTK_DT - - - "Time step between VTK writes" - +typedef ^ ^ character(1024) WrVTK_dir - - - "Directory for VTK writing" - +typedef ^ ^ c_float VTKNacDim 6 - - "Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz]" (m) +typedef ^ ^ IntKi Twidth - 6 - "Time width -- hard coded for now" (-) + +typedef ^ ModSettings character(1024) SS_InputFile - - - "SeaState input file" (-) +typedef ^ ^ DbKi WaveTimeShift - - - "Shift the SeaState wavetime by this amount (for phase shifting waves to match tank)" (s) +typedef ^ ^ character(1024) MD_InputFile - - - "MoorDyn input file" (-) +typedef ^ ^ ^ AD_InputFile - - - "AeroDyn input file" (-) +typedef ^ ^ ^ IfW_InputFile - - - "InflowWind input file" (-) + + +typedef ^ SimSettingsType SimType Sim - - - "Simulation settings" - +typedef ^ ^ EnvType Env - - - "Environment settings" - +typedef ^ ^ TurbConfigType TrbCfg - - - "Turbine configuration" - +typedef ^ ^ TurbInitCondType TrbInit - - - "Turbine initial operating point" - +typedef ^ ^ WaveBuoyType WaveBuoy - - - "Wave elevation buoy locat (x-y)" (m) +typedef ^ ^ OutFilesType Outs - - - "Output settings" - +typedef ^ ^ VizType Viz - - - "Vizualization settings" - +typedef ^ ^ ModSettings ModSettings - - - "Input files for each module" - + + +# Storage for IO to CalcStep +typedef ^ CalcStepIOdataType c_double Time_c - - - "IN: time" "(s)" +typedef ^ ^ c_float PosAng_c 6 - - "IN: Position + Euler Ang [x,y,z,phi,theta,psi]" "[(m) (rad)]" +typedef ^ ^ ^ Vel_c 6 - - "IN: Velocity [Vx,Vy,Vz,RVx,RVy,RVz]" "[(m/s) (rad/s)]" +typedef ^ ^ ^ Acc_c 6 - - "IN: Acceleration [Ax,Ay,Az,RAx,RAy,RAz]" "[(m/s^2) (rad/s^2)]" +typedef ^ ^ ^ FrcMom_c 6 - - "OUT: Acceleration [Fx,Fy,Fz,Mx,My,Mz]" "[(N) (N-m)]" +typedef ^ ^ ^ FrcMom_MD_c 6 - - "calculated forces/moments from MD" - +typedef ^ ^ ^ FrcMom_ADI_c : - - "calculated forces/moments from ADI" - +typedef ^ ^ ^ HubVel_ADI_c 3 - - "hub height wind vel from ADI" - +typedef ^ ^ ReKi BuoyWaveElev - - - "calculated wave elevation at buoy" - + + +# storage for output file data (num chans, WriteOutputHdr, out arrays per module, etc) +typedef ^ WrOutputDataType IntKi NumChans_cbind - 0 - "Number of output channels from c-bind" - +typedef ^ ^ ^ NumChans_SS - 0 - "Number of output channels from SS" - +typedef ^ ^ ^ NumChans_MD - 0 - "Number of output channels from MD" - +typedef ^ ^ ^ NumChans_ADI - 0 - "Number of output channels from ADI" - +typedef ^ ^ ^ NumChans_all - 0 - "Total number of channels (sum of above)" - +typedef ^ ^ character(ChanLen) WriteOutputHdr_SS : - - "output file header names from SS" - +typedef ^ ^ ^ WriteOutputUnt_SS : - - "output file header units from SS" - +typedef ^ ^ ^ WriteOutputHdr_MD : - - "output file header names from MD" - +typedef ^ ^ ^ WriteOutputUnt_MD : - - "output file header units from MD" - +typedef ^ ^ ^ WriteOutputHdr_ADI : - - "output file header names from ADI" - +typedef ^ ^ ^ WriteOutputUnt_ADI : - - "output file header units from ADI" - +typedef ^ ^ c_float OutData_SS_c : - - "output data from SS as passed c_float" - +typedef ^ ^ ^ OutData_MD_c : - - "output data from MD as passed c_float" - +typedef ^ ^ ^ OutData_ADI_c : - - "output data from ADI as passed c_float" - +typedef ^ ^ SiKi OutData_SS : - - "output data from SS" - +typedef ^ ^ ^ OutData_MD : - - "output data from MD" - +typedef ^ ^ ^ OutData_ADI : - - "output data from ADI" - +typedef ^ ^ character(1024) OutName - - - "Output file name" - +typedef ^ ^ IntKi OutUn - -1 - "Output unit" - + + +# Mesh structures and mappings +typedef ^ MeshesMotionType MeshType PtfmPtMotion - - - "Platform principle ref point. Also serves as tower base" - +typedef ^ ^ ^ TowerMotion - - - "Tower mesh (used only for vis)" - +typedef ^ ^ ^ HubMotion - - - "Hub mesh (for mappings, no loadings)" - +typedef ^ ^ ^ BladeRootMotion : - - "Blade root motions" - +typedef ^ ^ ^ WaveBuoyMotion - - - "wave measurement buoy motion (sensor only)" - + +typedef ^ MeshesLoadsType MeshType PtfmPtLoads - - - "Platform principle ref point loads output" - +typedef ^ ^ ^ PtfmPtLoadsTmp - - - "Platform principle ref point loads output - temp var for load summation" - +typedef ^ ^ ^ MooringLoads - - - "Mooring loads (always at PtfmPt, but separated for simplicity)" - +typedef ^ ^ ^ TowerLoads - - - "Tower mesh (unused)" - +typedef ^ ^ ^ HubLoads - - - "Hub mesh (for mappings, intermediate loads)" - +typedef ^ ^ ^ BladeRootLoads : - - "Blade root loads" - + +# NOTE: rigid geometry, no yaw, static pitch +typedef ^ MeshesMapsType MeshMapType Motion_PRP_2_Twr - - - "PRP to tower motion" - +typedef ^ ^ ^ Motion_PRP_2_Hub - - - "Twrtop to nacelle - add rotation afterwards" - +typedef ^ ^ ^ Motion_Hub_2_BldRoot : - - "Hub to blade root motion transfer" - +typedef ^ ^ ^ Load_BldRoot_2_Hub : - - "Blade root loads to hub" - +typedef ^ ^ ^ Load_Hub_2_PRP - - - "Hub to nacelle load transfer" - +typedef ^ ^ ^ Load_Twr_2_PRP - - - "Tower loads to PRP (unused)" - +typedef ^ ^ ^ Load_Moor_2_PRP - - - "Mooring loads to PRP" - + +# temporary data storage for structural model +typedef ^ StructTmpType ReKi Azimuth - 0 - "Current Azimuth" (rad) +typedef ^ ^ ^ RotSpeed - - - "Rotor speed" (RPM) +typedef ^ ^ ^ BldPitch - - - "Blade pitch" (rad) +typedef ^ ^ ^ NacYaw - - - "Nacelle-yaw angle" (rad) +typedef ^ ^ ^ FrcMom_ADI_at_Ptfm 6 - - "Total aero loading summed to Ptfm point" (N, N-m) +typedef ^ ^ ^ FrcMom_MD_at_Ptfm 6 - - "MoorDyn loading at Ptfm point" (N, N-m) +typedef ^ ^ c_float BuoyPos_c 2 - - "Buoy XY position" (m) +typedef ^ ^ ^ PtfmPosAng_c 6 - - "Temp position and euler angle" (m, rad) +typedef ^ ^ ^ PtfmVel_c 6 - - "Temp velocity " (m/s, rad/s) +typedef ^ ^ ^ PtfmAcc_c 6 - - "Temp acceleration " (m/s^2, rad/s^2) +typedef ^ ^ ^ NacPos_c 3 - - "Temp nacelle position " (m, rad) +typedef ^ ^ c_double NacDCM_c 9 - - "Temp nacelle orientation DCM" (-) +typedef ^ ^ c_float NacVel_c 6 - - "Temp nacelle velocity " (m/s, rad/s) +typedef ^ ^ ^ NacAcc_c 6 - - "Temp nacelle acceleration " (m/s^2, rad/s^2) +typedef ^ ^ ^ HubPos_c 3 - - "Temp hub position " (m, rad) +typedef ^ ^ c_double HubDCM_c 9 - - "Temp hub orientation DCM" (-) +typedef ^ ^ c_float HubVel_c 6 - - "Temp hub velocity " (m/s, rad/s) +typedef ^ ^ ^ HubAcc_c 6 - - "Temp hub acceleration " (m/s^2, rad/s^2) +typedef ^ ^ ^ BldPos_c : - - "Temp blade position -- sequential by blade" (m) +typedef ^ ^ c_double BldDCM_c : - - "Temp blade orientation DCM -- flat sequential by blade" (-) +typedef ^ ^ c_float BldVel_c : - - "Temp blade velocity -- sequential by blade" (m/s, rad/s) +typedef ^ ^ ^ BldAcc_c : - - "Temp blade acceleration -- sequential by blade" (m/s^2, rad/s^2) + diff --git a/glue-codes/labview/src/WaveTank_Struct.f90 b/glue-codes/labview/src/WaveTank_Struct.f90 new file mode 100644 index 0000000000..7c096b8e0b --- /dev/null +++ b/glue-codes/labview/src/WaveTank_Struct.f90 @@ -0,0 +1,666 @@ +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2025 National Renewable Energy Laboratory +! +! This file is a module specific to an experimental wave tank at NREL. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +!********************************************************************************************************************************** +! +! This module provides structural model for the wavetank interface +! +!********************************************************************************************************************************** +module WaveTank_Struct + use ISO_C_BINDING + use NWTC_Library + use WaveTank_Types + + implicit none + private + + save + + public :: StructCreate + public :: StructCreateMeshMaps + public :: StructDestroy + public :: StructMotionUpdate + public :: StructLoadsMeshTransfer + public :: WrVTK_Struct_Ref + public :: WrVTK_Struct + public :: FroudeScaleM2F_Disp + public :: FroudeScaleM2F_TVel + public :: FroudeScaleM2F_RVel + public :: FroudeScaleM2F_TAcc + public :: FroudeScaleM2F_RAcc + public :: FroudeScaleM2F_Time + public :: FroudeScaleF2M_Frc + public :: FroudeScaleF2M_Mom + +contains + + +!> create the structural model, allocate temp data storage, setup mesh mappings +subroutine StructCreate(SimSettings, MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat, ErrMsg) + type(SimSettingsType), target, intent(in ) :: SimSettings + type(MeshesMotionType), target, intent(inout) :: MeshMotions + type(MeshesLoadsType ), target, intent(inout) :: MeshLoads + type(MeshesMapsType ), intent(inout) :: MeshMaps + type(StructTmpType ), intent(inout) :: StructTmp + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructCreate' + real(ReKi) :: TmpPos(3) + real(DbKi) :: AzBlade ! temporary var for calculating blade mounting azimuth + real(DbKi) :: TmpAng(3) ! temporary euler angle + real(DbKi) :: Orient(3,3) ! temporary orientation + type(TurbConfigType), pointer :: TrbCfg ! to shorten notation + type(TurbInitCondType), pointer :: TrbInit ! to shorten notation + type(MeshType), pointer :: Ptfm, PtfmLd ! to shorten notation + type(MeshType), pointer :: Twr, TwrLd ! to shorten notation + type(MeshType), pointer :: Hub, HubLd ! to shorten notation + type(MeshType), pointer :: Root, RootLd ! to shorten notation + type(MeshType), pointer :: MoorLd ! to shorten notation + integer(IntKi) :: k ! blade counter + ErrStat = ErrID_None + ErrMsg = '' + + TrbCfg => SimSettings%TrbCfg + TrbInit => SimSettings%TrbInit + + ! Set some state information + StructTmp%RotSpeed = TrbInit%RotSpeed + StructTmp%BldPitch = TrbInit%BldPitch + StructTmp%NacYaw = TrbInit%NacYaw + StructTmp%Azimuth = TrbInit%Azimuth + + + !------------------------------- + ! Wave measurement buoy + !------------------------------- + TmpPos = 0.0_ReKi + TmpPos(1:2) = SimSettings%WaveBuoy%XYLoc(1:2) + call Eye(Orient, ErrStat2, ErrMsg2); if (Failed()) return + call CreateInputPointMesh(MeshMotions%WaveBuoyMotion, TmpPos, Orient, ErrStat2, ErrMsg2, hasMotion=.true., hasLoads=.false.); if (Failed()) return + + + !------------------------------- + ! create PRP platform mesh point + !------------------------------- + Ptfm => MeshMotions%PtfmPtMotion + TmpPos = real(TrbCfg%PtfmRefPos, ReKi) + Orient=WT_EulerToDCM_fromInput(TrbCfg%PtfmRefOrient) + call CreateInputPointMesh(Ptfm, TmpPos, Orient, ErrStat2, ErrMsg2, hasMotion=.true., hasLoads=.false.); if (Failed()) return + Ptfm%RemapFlag = .false. + + ! create platform load mesh + PtfmLd => MeshLoads%PtfmPtLoads + call MeshCopy( SrcMesh=Ptfm, DestMesh=PtfmLd, CtrlCode=MESH_SIBLING, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + PtfmLd%RemapFlag = .false. + + ! create a temporary load mesh + call MeshCopy( PtfmLd, MeshLoads%PtfmPtLoadsTmp, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + if (Failed()) return + PtfmLd%RemapFlag = .false. + + ! create a mooring mesh point + MoorLd => MeshLoads%MooringLoads + call MeshCopy( SrcMesh=Ptfm, DestMesh=MoorLd, CtrlCode=MESH_COUSIN, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + PtfmLd%RemapFlag = .false. + + + !------------------------------- + ! create 2 point tower mesh + !------------------------------- + Twr => MeshMotions%TowerMotion + call MeshCreate ( BlankMesh = Twr, IOS=COMPONENT_INPUT, Nnodes=2, ErrStat=ErrStat2, ErrMess=ErrMsg2, & + Orientation = .true., TranslationDisp = .true., TranslationVel = .true., RotationVel = .true., TranslationAcc = .TRUE., RotationAcc = .true.) + if (Failed()) return + + ! Tower bottom + TmpPos(1:2) = real(TrbCfg%TowerBsPt(1:2) + TrbCfg%PtfmRefPos(1:2), ReKi) ! relative to PtfmRefPos in (x,y) + TmpPos(3) = real(TrbCfg%TowerBsPt(3), ReKi) ! relative to MSL in (z) + call MeshPositionNode(Twr, 1, TmpPos, errStat2, errMsg2) ! orientation is identity by default + if (Failed()) return + + ! Tower top -- assumes vertical tower + TmpPos(3) = real(TrbCfg%TowerHt,ReKi) ! c_float to ReKi + call MeshPositionNode(Twr, 2, TmpPos, errStat2, errMsg2) ! orientation is identity by default + if (Failed()) return + + ! create line element + call MeshConstructElement( Twr, ELEMENT_LINE2, errStat2, errMsg2, p1=1, p2=2 ) + if (Failed()) return + + ! commit mesh + call MeshCommit(Twr, errStat2, errMsg2 ) + + ! initialize location + Twr%Orientation = Twr%RefOrientation + Twr%TranslationDisp = 0.0_R8Ki + Twr%TranslationVel = 0.0_ReKi + Twr%RemapFlag = .false. + + ! create tower load mesh + TwrLd => MeshLoads%TowerLoads + call MeshCopy( SrcMesh=Twr, DestMesh=TwrLd, CtrlCode=MESH_SIBLING, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + TwrLd%RemapFlag = .false. + TwrLd%Force = 0.0_ReKi + TwrLd%Moment = 0.0_ReKi + + + !------------------------------- + ! create hub mesh + !------------------------------- + ! NOTE: for a reference mesh position, nacelle yaw should be zero. since NacYaw is static in this + ! we are setting it once here. If it needs to be dynamic, zero it here and update the yaw + ! in the StructMotionUpdate routine below + Hub => MeshMotions%HubMotion + TmpPos(1:3) = Twr%Position(1:3,2) ! Tower top + TmpPos(1) = TmpPos(1) + cos(TrbInit%NacYaw) * TrbCfg%OverHang ! X, nacelle yaw, and overhang + TmpPos(2) = TmpPos(2) + sin(TrbInit%NacYaw) * TrbCfg%OverHang ! Y, nacelle yaw, and overhang + TmpPos(3) = TmpPos(3) + TrbCfg%Twr2Shft - abs(TrbCfg%OverHang) * tan(TrbCfg%ShftTilt) ! Z, shaft height above tower top, and shaft tilt + + TmpAng = (/ 0.0_DbKi, -real(TrbCfg%ShftTilt,DbKi), real(TrbInit%NacYaw,DbKi) /) ! Hub/rotor azimuth is zero for reference. Hub axis on upwind points towards nacelle. + Orient = EulerConstruct(TmpAng) + call CreateInputPointMesh(Hub, TmpPos, Orient, ErrStat2, ErrMsg2, hasMotion=.true., hasLoads=.false.); if (Failed()) return + Hub%RemapFlag = .false. + + ! create tower load mesh + HubLd => MeshLoads%HubLoads + call MeshCopy( SrcMesh=Hub, DestMesh=HubLd, CtrlCode=MESH_SIBLING, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + HubLd%RemapFlag = .false. + HubLd%Force = 0.0_ReKi + HubLd%Moment = 0.0_ReKi + + + !------------------------------- + ! create blade root mesh + !------------------------------- + ! NOTE: for a reference mesh position, blade pitch should be zero. since BldPitch is static in this + ! we are setting it once here. If it needs to be dynamic, zero it here and update the yaw + ! in the StructMotionUpdate routine below + allocate(MeshMotions%BladeRootMotion(TrbCfg%NumBl),STAT=ErrStat2) + if (ErrStat2 /= 0) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = "Could not allocate BladeRootMotion mesh" + if (Failed()) return + endif + do k=1,TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + AzBlade = TwoPi_D * real((k-1),DbKi)/real(TrbCfg%NumBl,DbKi) + TmpAng = (/ AzBlade, real(TrbCfg%PreCone,DbKi), real(-TrbInit%BldPitch,DbKi) /) ! Blade pitch does not follow RHR + Orient = EulerConstruct(TmpAng) + Orient = matmul(Orient,Hub%Orientation(1:3,1:3,1)) + TmpPos = Hub%Position(1:3,1) + TrbCfg%HubRad * real(Orient(3,1:3),ReKi) + call CreateInputPointMesh(Root, TmpPos, Orient, ErrStat2, ErrMsg2, hasMotion=.true., hasLoads=.false.); if (Failed()) return + Root%RemapFlag = .false. + enddo + + ! create blade root load mesh + allocate(MeshLoads%BladeRootLoads(TrbCfg%NumBl),STAT=ErrStat2) + if (ErrStat2 /= 0) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = "Could not allocate BladeRootLoads mesh" + if (Failed()) return + endif + do k=1,TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + RootLd => MeshLoads%BladeRootLoads(k) + call MeshCopy( SrcMesh=Root, DestMesh=RootLd, CtrlCode=MESH_SIBLING, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + RootLd%RemapFlag = .false. + RootLd%Force = 0.0_ReKi + RootLd%Moment = 0.0_ReKi + enddo + +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!> create mesh mappings +subroutine StructCreateMeshMaps(SimSettings, MeshMotions, MeshLoads, MeshMaps, ErrStat, ErrMsg) + type(SimSettingsType), intent(in ) :: SimSettings + type(MeshesMotionType), intent(inout) :: MeshMotions + type(MeshesLoadsType ), intent(inout) :: MeshLoads + type(MeshesMapsType ), intent(inout) :: MeshMaps + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructCreateMeshMaps' + integer(IntKi) :: k + + ErrStat = ErrID_None + ErrMsg = '' + + !------------------------------- + ! Mapping arrays + allocate(MeshMaps%Motion_Hub_2_BldRoot(SimSettings%TrbCfg%NumBl),STAT=ErrStat2) + if (ErrStat2 /= 0) then + ErrStat = ErrID_Fatal + ErrMsg = "Could not allocate Motion_Hub_2_BldRoot mesh mapping" + return + endif + allocate(MeshMaps%Load_BldRoot_2_Hub(SimSettings%TrbCfg%NumBl),STAT=ErrStat2) + if (ErrStat2 /= 0) then + ErrStat = ErrID_Fatal + ErrMsg = "Could not allocate Load_BldRoot_2_Hub mesh mapping" + return + endif + + !------------------------------- + ! Mesh motion mappings + call MeshMapCreate(MeshMotions%PtfmPtMotion, MeshMotions%TowerMotion, MeshMaps%Motion_PRP_2_Twr, errStat2, errMsg2); if(Failed())return + call MeshMapCreate(MeshMotions%PtfmPtMotion, MeshMotions%HubMotion, MeshMaps%Motion_PRP_2_Hub, errStat2, errMsg2); if(Failed())return + do k=1,SimSettings%TrbCfg%NumBl + call MeshMapCreate(MeshMotions%HubMotion, MeshMotions%BladeRootMotion(k), MeshMaps%Motion_Hub_2_BldRoot(k), errStat2, errMsg2); if(Failed())return + enddo + + !------------------------------- + ! Mesh load mappings + call MeshMapCreate(MeshLoads%TowerLoads, MeshLoads%PtfmPtLoads, MeshMaps%Load_Twr_2_PRP, errStat2, ErrMsg2); if(Failed()) return + call MeshMapCreate(MeshLoads%HubLoads, MeshLoads%PtfmPtLoads, MeshMaps%Load_Hub_2_PRP, errStat2, ErrMsg2); if(Failed()) return + do k=1,SimSettings%TrbCfg%NumBl + call MeshMapCreate(MeshLoads%BladeRootLoads(k), MeshLoads%HubLoads, MeshMaps%Load_BldRoot_2_Hub(k), errStat2, errMsg2); if(Failed())return + enddo + call MeshMapCreate(MeshLoads%MooringLoads, MeshLoads%PtfmPtLoads, MeshMaps%Load_Moor_2_PRP, errStat2, ErrMsg2); if(Failed()) return + +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!> updates the structural meshes +subroutine StructMotionUpdate(SimSettings, CalcStepIO, MeshMotions, MeshMaps, StructTmp, ErrStat, ErrMsg) + type(SimSettingsType), target, intent(in ) :: SimSettings + type(CalcStepIOdataType), intent(in ) :: CalcStepIO + type(MeshesMotionType), target, intent(inout) :: MeshMotions + type(MeshesMapsType ), intent(inout) :: MeshMaps + type(StructTmpType ), intent(inout) :: StructTmp + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructMotionUpdate' + real(R8Ki) :: TmpTransDisp(3) + real(DbKi) :: TmpAng(3) ! temporary euler angle + real(R8Ki) :: Orient(3,3) ! temporary orientation + type(TurbConfigType), pointer :: TrbCfg ! to shorten notation + type(TurbInitCondType), pointer :: TrbInit ! to shorten notation + type(MeshType), pointer :: Ptfm ! to shorten notation + type(MeshType), pointer :: Twr ! to shorten notation + type(MeshType), pointer :: Hub ! to shorten notation + type(MeshType), pointer :: Root ! to shorten notation + real(c_float) :: ScaleFact ! to shorten notation + integer(IntKi) :: k + + ErrStat = ErrID_None + ErrMsg = '' + + TrbCfg => SimSettings%TrbCfg + TrbInit => SimSettings%TrbInit + + ! scaling factor + ScaleFact = SimSettings%Sim%ScaleFact + + ! update PtfmPtMotion + Ptfm => MeshMotions%PtfmPtMotion + Ptfm%TranslationDisp(1:3,1) = FroudeScaleM2F_Disp(ScaleFact, CalcStepIO%PosAng_c(1:3), Ptfm%Position(1:3,1)) + Ptfm%Orientation(1:3,1:3,1) = WT_EulerToDCM_fromInput(CalcStepIO%PosAng_c(4:6)) ! angles don't scale + Ptfm%TranslationVel(1:3,1) = FroudeScaleM2F_TVel(ScaleFact, CalcStepIO%Vel_c(1:3)) + Ptfm%RotationVel(1:3,1) = FroudeScaleM2F_RVel(ScaleFact, CalcStepIO%Vel_c(4:6)) + Ptfm%TranslationAcc(1:3,1) = FroudeScaleM2F_TAcc(ScaleFact, CalcStepIO%Acc_c(1:3)) + Ptfm%RotationAcc(1:3,1) = FroudeScaleM2F_RAcc(ScaleFact, CalcStepIO%Acc_c(4:6)) + + !-------------------------------------- + ! transfer Ptfm to Tower + Twr => MeshMotions%TowerMotion + call Transfer_Point_to_Line2( Ptfm, Twr, MeshMaps%Motion_PRP_2_Twr, ErrStat2, ErrMsg2 ); if (Failed()) return; + + !-------------------------------------- + ! transfer Ptfm to hub (tower is rigid) + Hub => MeshMotions%HubMotion + call Transfer_Point_to_Point( Ptfm, Hub, MeshMaps%Motion_PRP_2_Hub, ErrStat2, ErrMsg2 ); if (Failed()) return; + + ! rotor azimuth + StructTmp%Azimuth = modulo(real(CalcStepIO%Time_c,ReKi)*StructTmp%RotSpeed + TrbInit%Azimuth, TwoPi ) + + ! update hub azimuth -- include initial azimuth + TmpAng = (/ real(StructTmp%Azimuth,DbKi), 0.0_DbKi, 0.0_DbKi /) + Orient = EulerConstruct(TmpAng) + Hub%Orientation(1:3,1:3,1) = matmul(Orient,Hub%Orientation(1:3,1:3,1)) + + !-------------------------------------- + ! hub to blades + do k=1,TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + call Transfer_Point_to_Point( Hub, Root, MeshMaps%Motion_Hub_2_BldRoot(k), ErrStat2, ErrMsg2 ); if (Failed()) return; + enddo + +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!> updates the structural load meshes and populates aggregated loads +subroutine StructLoadsMeshTransfer(SimSettings, CalcStepIO, MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat, ErrMsg) + type(SimSettingsType), target, intent(in ) :: SimSettings + type(CalcStepIOdataType), intent(in ) :: CalcStepIO + type(MeshesMotionType), target, intent(inout) :: MeshMotions + type(MeshesLoadsType), target, intent(inout) :: MeshLoads + type(MeshesMapsType ), intent(inout) :: MeshMaps + type(StructTmpType ), intent(inout) :: StructTmp + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructMotionUpdate' + real(R8Ki) :: TmpTransDisp(3) + real(DbKi) :: TmpAng(3) ! temporary euler angle + real(R8Ki) :: Orient(3,3) ! temporary orientation + type(TurbConfigType), pointer :: TrbCfg ! to shorten notation + type(TurbInitCondType), pointer :: TrbInit ! to shorten notation + type(MeshType), pointer :: Ptfm, PtfmLd, PtfmLdTmp ! to shorten notation + type(MeshType), pointer :: Twr, TwrLd ! to shorten notation + type(MeshType), pointer :: Hub, HubLd ! to shorten notation + type(MeshType), pointer :: Root, RootLd ! to shorten notation + type(MeshType), pointer :: MoorLd ! to shorten notation + real(c_float) :: ScaleFact ! to shorten notation + integer(IntKi) :: k + + ErrStat = ErrID_None + ErrMsg = '' + + ! shorthand pointers + TrbCfg => SimSettings%TrbCfg + TrbInit => SimSettings%TrbInit + Hub => MeshMotions%HubMotion + HubLd => MeshLoads%HubLoads + Twr => MeshMotions%TowerMotion + TwrLd => MeshLoads%TowerLoads + Ptfm => MeshMotions%PtfmPtMotion + PtfmLd => MeshLoads%PtfmPtLoads + PtfmLdTmp=> MeshLoads%PtfmPtLoadsTmp + MoorLd => MeshLoads%MooringLoads + + !----------------------------------------- + ! Aero loading + !----------------------------------------- + ! Transfer blade root loads to hub + do k=1,TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + RootLd => MeshLoads%BladeRootLoads(k) + call Transfer_Point_To_Point( RootLd, HubLd, MeshMaps%Load_BldRoot_2_Hub(k), ErrStat2, ErrMsg2, Root, Hub ) + if (Failed()) return + enddo + + ! Transfer hub to platform + call Transfer_Point_To_Point( HubLd, PtfmLd, MeshMaps%Load_Hub_2_PRP, ErrStat2, ErrMsg2, Hub, Ptfm ) + if (Failed()) return + + + !----------------------------------------- + ! Transfer tower to platform + ! NOTE: no tower loads at present from ADI + !FIXME: add tower loads output transfer to mesh here + !call Transfer_Line2_To_Point( TwrLd, PtfmLdTmp, MeshMaps%Load_Twr_2_PRP, ErrStat2, ErrMsg2, Twr, Ptfm ) + !PtfmLd%Force(1:3,1) = PtfmLd%Force(1:3,1) + PtfmLdTmp%Force(1:3,1) + !PtfmLd%Moment(1:3,1) = PtfmLd%Moment(1:3,1) + PtfmLdTmp%Moment(1:3,1) + + ! Store the ADI summed foreces and moments for output + StructTmp%FrcMom_ADI_at_Ptfm(1:3) = PtfmLd%Force(1:3,1) + StructTmp%FrcMom_ADI_at_Ptfm(4:6) = PtfmLd%Moment(1:3,1) + + + !----------------------------------------- + ! Mooring loading + !----------------------------------------- + ! Transfer mooring load + call Transfer_Point_To_Point( MoorLd, PtfmLdTmp, MeshMaps%Load_Moor_2_PRP, ErrStat2, ErrMsg2, Ptfm, Ptfm ) + if (Failed()) return + PtfmLd%Force(1:3,1) = PtfmLd%Force(1:3,1) + PtfmLdTmp%Force(1:3,1) + PtfmLd%Moment(1:3,1) = PtfmLd%Moment(1:3,1) + PtfmLdTmp%Moment(1:3,1) + + ! Store the MD summed foreces and moments for output + StructTmp%FrcMom_MD_at_Ptfm(1:3) = PtfmLdTmp%Force(1:3,1) + StructTmp%FrcMom_MD_at_Ptfm(4:6) = PtfmLdTmp%Moment(1:3,1) + +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!> destroy all structural model related info +subroutine StructDestroy(MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat,ErrMsg) ! We are actually ignoring all errors from here + type(MeshesMotionType), intent(inout) :: MeshMotions + type(MeshesLoadsType ), intent(inout) :: MeshLoads + type(MeshesMapsType ), intent(inout) :: MeshMaps + type(StructTmpType ), intent(inout) :: StructTmp + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructDestroy' + ErrStat = ErrID_None + ErrMsg = '' + call WT_DestroyMeshesMotionType(MeshMotions, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyMeshesLoadsType(MeshLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyMeshesMapsType(MeshMaps, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyStructTmpType(StructTmp, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + + + +!> Convert an Euler angle set of Roll, Pitch, Yaw ordering to a DCM. +!! this routine exists for two reasons +!! 1. ordering may be different +!! 2. incoming Euler angle is c_float instead of R8Ki +!! NOTE: no Euler angles are exported, so we stick with the OF convention +!! for all internal conversions +function WT_EulerToDCM_fromInput(Ang) result(DCM) + real(c_float), intent(in ) :: Ang(3) + real(R8Ki) :: DCM(3,3) + !>>> Select one of the two following orders + ! 3-2-1 intrinsic rotation sequence of the 3 Tait-Bryan angles (1-2-3 extrinsic rotation) + DCM = EulerConstruct(real(Ang, DbKi)) + !! 1-2-3 intrinsic rotation sequence of the 3 Tait-Bryan angles (3-2-1 extrinsic rotation) + !DCM = EulerConstructZYX(real(Ang, DbKi)) +end function + + + +subroutine WrVTK_Struct_Ref(SimSettings, MeshMotions, MeshLoads, ErrStat, ErrMsg) + type(SimSettingsType), target, intent(in ) :: SimSettings + type(MeshesMotionType), intent(in ) :: MeshMotions + type(MeshesLoadsType ), intent(in ) :: MeshLoads + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::WrVTK_Struct_Ref' + character(1024) :: DirRootName + real(SiKi) :: RefPt(3) + integer(IntKi) :: k + ErrStat = ErrID_None + ErrMsg = '' + RefPt = (/ 0.0_SiKi, 0.0_SiKi, 0.0_SiKi /) + DirRootName = trim(SimSettings%Viz%WrVTK_dir)//PathSep//trim(SimSettings%Sim%OutRootName) + ! Wave elevation measurement buoy + call MeshWrVTKreference(RefPt, MeshMotions%WaveBuoyMotion, trim(DirRootName)//'.WaveBuoyMotion', ErrStat2, ErrMsg2); if (Failed()) return + ! Platform point + call MeshWrVTKreference(RefPt, MeshMotions%PtfmPtMotion, trim(DirRootName)//'.Struct'//'.PtfmPtMotion', ErrStat2, ErrMsg2); if (Failed()) return + ! Tower + call MeshWrVTKreference(RefPt, MeshMotions%TowerMotion, trim(DirRootName)//'.Struct'//'.TowerMotion', ErrStat2, ErrMsg2); if (Failed()) return + ! hub point + call MeshWrVTKreference(RefPt, MeshMotions%HubMotion, trim(DirRootName)//'.Struct'//'.HubMotion', ErrStat2, ErrMsg2); if (Failed()) return + ! RootMotion points + do k=1,SimSettings%TrbCfg%NumBl + call MeshWrVTKreference(RefPt, MeshMotions%BladeRootMotion(k), trim(DirRootName)//'.Struct'//'.RootMotion'//trim(Num2LStr(k)), ErrStat2, ErrMsg2); if (Failed()) return + enddo +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + +subroutine WrVTK_Struct(n_Global, SimSettings, MeshMotions, MeshLoads, ErrStat, ErrMsg) + integer(IntKi), intent(in ) :: n_Global + type(SimSettingsType), target, intent(in ) :: SimSettings + type(MeshesMotionType), intent(in ) :: MeshMotions + type(MeshesLoadsType ), intent(in ) :: MeshLoads + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::WrVTK_Struct' + character(1024) :: DirRootName + real(SiKi) :: RefPt(3) + integer(IntKi) :: k + ErrStat = ErrID_None + ErrMsg = '' + RefPt = (/ 0.0_SiKi, 0.0_SiKi, 0.0_SiKi /) + DirRootName = trim(SimSettings%Viz%WrVTK_dir)//PathSep//trim(SimSettings%Sim%OutRootName) + ! Wave elevation measurement buoy + call MeshWrVTK(RefPt, MeshMotions%WaveBuoyMotion, trim(DirRootName)//'.WaveBuoyMotion', n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + ! Platform point + call MeshWrVTK(RefPt, MeshMotions%PtfmPtMotion, trim(DirRootName)//'.Struct'//'.PtfmPtMotion', n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + ! Tower + call MeshWrVTK(RefPt, MeshMotions%TowerMotion, trim(DirRootName)//'.Struct'//'.TowerMotion', n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + ! Hub point + call MeshWrVTK(RefPt, MeshMotions%HubMotion, trim(DirRootName)//'.Struct'//'.HubMotion', n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + ! RootMotion points + do k=1,SimSettings%TrbCfg%NumBl + call MeshWrVTK(RefPt, MeshMotions%BladeRootMotion(k), trim(DirRootName)//'.Struct'//'.RootMotion'//trim(Num2LStr(k)), n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + enddo +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!----------------------------------------------- +! Froude scaling from here: https://home.hvl.no/ansatte/gste/ftp/MarinLab_files/Litteratur/NTNU_Scaling_Laws.pdf, page 21 +! notation below: +! model scale: _m +! full scale: _f +! ScaleFact: length_f/length_m = lambda +! DensFact: rho_f/rho_m + +!> scale model displacements to full scale +!! length_full = length_model * lambda +function FroudeScaleM2F_Disp(ScaleFact, Pos_m, refPos_f) result(transDisp_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: Pos_m(3) + real(ReKi), intent(in ) :: refPos_f(3) + real(R8Ki) :: transdisp_f(3) + transDisp_f = real(ScaleFact*Pos_m,R8Ki) - real(refPos_f,R8Ki) +end function + +!> scale model translational velocity to full scale +!! TVel_full = TVel_model * sqrt( lambda ) TODO: check this!!!! +function FroudeScaleM2F_TVel(ScaleFact, TVel_m) result(TVel_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: TVel_m(3) + real(ReKi) :: TVel_f(3) + TVel_f = sqrt(real(ScaleFact,ReKi)) * real(TVel_m,ReKi) +end function + +!> scale model rotational velocity to full scale +!! RVel_full = RVel_model * sqrt(lambda) TODO: check this!!!! +function FroudeScaleM2F_RVel(ScaleFact, RVel_m) result(RVel_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: RVel_m(3) + real(ReKi) :: RVel_f(3) + RVel_f = real(RVel_m,ReKi) / sqrt(real(ScaleFact,ReKi)) +end function + +!> scale model translational acceleration to full scale +!! TAcc_full = TAcc_model ---> no scaling applied +function FroudeScaleM2F_TAcc(ScaleFact, TAcc_m) result(TAcc_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: TAcc_m(3) + real(ReKi) :: TAcc_f(3) + TAcc_f = real(TAcc_m,ReKi) +end function + +!> scale model rotational acceleration to full scale +!! RAcc_full = RAcc_model / lambda TODO: check this!!!! +function FroudeScaleM2F_RAcc(ScaleFact, RAcc_m) result(RAcc_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: RAcc_m(3) + real(ReKi) :: RAcc_f(3) + RAcc_f = real(RAcc_m,ReKi) / real(ScaleFact,ReKi) +end function + +!> scale model time to full scale +!! sqrt(lambda) = sqrt(Length_full/ Length_model) +function FroudeScaleM2F_Time(ScaleFact, Time_m) result(Time_f) + real(c_float), intent(in ) :: ScaleFact + real(c_double),intent(in ) :: Time_m + real(R8Ki) :: Time_f + Time_f = sqrt(real(ScaleFact,R8Ki)) * real(Time_m,R8Ki) +end function + +!> scale full scale force to model +!! lambda^3 * DensFact * Frc_model = Frc_full +function FroudeScaleF2M_Frc(ScaleFact, DensFact, Frc_f) result(Frc_m) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: DensFact + real(ReKi), intent(in ) :: Frc_f(3) + real(c_float) :: Frc_m(3) + Frc_m = real(Frc_f, c_float) / (ScaleFact**3 * DensFact) +end function + +!> scale full scale moment to model +!! lambda^4 * DensFact * Mom_model = Mom_full +function FroudeScaleF2M_Mom(ScaleFact, DensFact, Mom_f) result(Mom_m) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: DensFact + real(ReKi), intent(in ) :: Mom_f(3) + real(c_float) :: Mom_m(3) + Mom_m = real(Mom_f, c_float) / (ScaleFact**4 * DensFact) +end function + + +end module diff --git a/glue-codes/labview/src/WaveTank_Types.f90 b/glue-codes/labview/src/WaveTank_Types.f90 new file mode 100644 index 0000000000..c134bcaa1e --- /dev/null +++ b/glue-codes/labview/src/WaveTank_Types.f90 @@ -0,0 +1,1671 @@ +!STARTOFREGISTRYGENERATEDFILE 'WaveTank_Types.f90' +! +! WARNING This file is generated automatically by the FAST registry. +! Do not edit. Your changes to this file will be lost. +! +! FAST Registry +!********************************************************************************************************************************* +! WaveTank_Types +!................................................................................................................................. +! This file is part of WaveTank. +! +! Copyright (C) 2012-2016 National Renewable Energy Laboratory +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +! +! +! W A R N I N G : This file was automatically generated from the FAST registry. Changes made to this file may be lost. +! +!********************************************************************************************************************************* +!> This module contains the user-defined types needed in WaveTank. It also contains copy, destroy, pack, and +!! unpack routines associated with each defined data type. This code is automatically generated by the FAST Registry. +MODULE WaveTank_Types +!--------------------------------------------------------------------------------------------------------------------------------- +USE ISO_C_BINDING +USE NWTC_Library +IMPLICIT NONE +! ========= SimType ======= + TYPE, PUBLIC :: SimType + REAL(c_double) :: DT = 0.0_R8Ki !< timestep [-] + REAL(c_double) :: TMax = 0.0_R8Ki !< Max sim time [-] + INTEGER(c_int) :: MHK = 0_IntKi !< MHK turbine type (switch) {0=Not an MHK turbine; 1=Fixed MHK turbine; 2=Floating MHK turbine} [(-)] + INTEGER(c_int) :: InterpOrd = 1 !< Interpolation order [-] + REAL(c_float) :: ScaleFact = 1 + REAL(c_float) :: DensFact = 1 !< ratio of density - Density_full/Density_model (rho_F/rho_M). Used with Froude scaling of forces/moments [(-)] + INTEGER(c_int) :: DebugLevel = 0_IntKi !< Debug level for outputs [-] + character(1024) :: OutRootName !< Rootname for outputs [-] + END TYPE SimType +! ======================= +! ========= EnvType ======= + TYPE, PUBLIC :: EnvType + REAL(c_float) :: Gravity = 0.0_R4Ki !< gravitational constant (positive for down) [(m/s^2)] + REAL(c_float) :: WtrDens = 0.0_R4Ki !< Water density [(kg/m^3)] + REAL(c_float) :: WtrVisc = 0.0_R4Ki !< fluid viscosity [(m^2/s)] + REAL(c_float) :: SpdSound = 0.0_R4Ki !< Speed of sound in working fluid [(m/s)] + REAL(c_float) :: Patm = 0.0_R4Ki !< Atmospheric pressure [used only for an MHK turbine cavitation check] [(Pa)] + REAL(c_float) :: Pvap = 0.0_R4Ki !< Vapour pressure of working fluid [used only for an MHK turbine cavitation check] [(Pa)] + REAL(c_float) :: WtrDpth = 0.0_R4Ki !< Water depth [(m)] + REAL(c_float) :: MSL2SWL = 0.0_R4Ki !< Mean sea level to still water level [(m)] + END TYPE EnvType +! ======================= +! ========= TurbConfigType ======= + TYPE, PUBLIC :: TurbConfigType + INTEGER(IntKi) :: NumBl = 0_IntKi !< Number of blades [(-)] + REAL(SiKi) :: HubRad = 0.0_R4Ki !< The distance from the rotor apex to the blade root [(m)] + REAL(SiKi) :: PreCone = 0.0_R4Ki !< Blade cone angle [(deg)] + REAL(SiKi) :: OverHang = 0.0_R4Ki !< Distance from yaw axis to rotor apex [3 blades] or teeter pin [2 blades] [(m)] + REAL(SiKi) :: ShftTilt = 0.0_R4Ki !< Rotor shaft tilt angle [(deg)] + REAL(SiKi) :: Twr2Shft = 0.0_R4Ki !< Vertical distance from the tower-top to the rotor shaft [(m)] + REAL(SiKi) :: TowerHt = 0.0_R4Ki !< Height of tower relative MSL [(m)] + REAL(SiKi) , DIMENSION(1:3) :: TowerBsPt = 0.0_R4Ki !< Tower base location relative to MSL. Consider absolute difference to PtfmRef [floating MHK] [(m)] + REAL(SiKi) , DIMENSION(1:3) :: PtfmRefPos = 0.0_R4Ki !< Location of platform reference point, relative to MSL. Motions and loads all connect to this point [(m)] + REAL(SiKi) , DIMENSION(1:3) :: PtfmRefOrient = 0.0_R4Ki !< Orientation of platform reference point, Euler angle set of roll,pitch,yaw [(rad)] + END TYPE TurbConfigType +! ======================= +! ========= TurbInitCondType ======= + TYPE, PUBLIC :: TurbInitCondType + REAL(ReKi) :: RotSpeed = 0.0_ReKi !< Rotor speed [(RPM)] + REAL(ReKi) :: NacYaw = 0.0_ReKi !< Initial or fixed nacelle-yaw angle - read as deg, convert to rad [(rad)] + REAL(ReKi) :: BldPitch = 0.0_ReKi !< Fixed blade pitch for full simulation - read as deg, convert to rad [(rad)] + REAL(ReKi) :: Azimuth = 0 !< Initial azimuth (actual azimuth calculated and not stored) - read as deg, convert to rad [(rad)] + END TYPE TurbInitCondType +! ======================= +! ========= WaveBuoyType ======= + TYPE, PUBLIC :: WaveBuoyType + REAL(ReKi) , DIMENSION(1:2) :: XYLoc = 0.0_ReKi !< Location of the wave elevation measurement buoy. SeaState data returned at every timestep at this location [(m)] + END TYPE WaveBuoyType +! ======================= +! ========= OutFilesType ======= + TYPE, PUBLIC :: OutFilesType + LOGICAL :: SendScreenToFile = .false. !< send to file .screen.log if true [(-)] + INTEGER(c_int) :: OutFile = 0_IntKi !< 0: no output file of channels, 1: output file in text format (at DT) [(-)] + character(20) :: OutFmt !< Format used for text tabular output, excluding the time channel. (quoted string) [(-)] + END TYPE OutFilesType +! ======================= +! ========= VizType ======= + TYPE, PUBLIC :: VizType + INTEGER(c_int) :: WrVTK = 0_IntKi !< Write VTK? [-] + INTEGER(c_int) :: WrVTK_type = 0_IntKi !< Write VTK outputs as [1: surface, 2: lines, 3: both] [-] + REAL(c_double) :: WrVTK_DT = 0.0_R8Ki !< Time step between VTK writes [-] + character(1024) :: WrVTK_dir !< Directory for VTK writing [-] + REAL(c_float) , DIMENSION(1:6) :: VTKNacDim = 0.0_R4Ki !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] [(m)] + INTEGER(IntKi) :: Twidth = 6 !< Time width -- hard coded for now [(-)] + END TYPE VizType +! ======================= +! ========= ModSettings ======= + TYPE, PUBLIC :: ModSettings + character(1024) :: SS_InputFile !< SeaState input file [(-)] + REAL(DbKi) :: WaveTimeShift = 0.0_R8Ki !< Shift the SeaState wavetime by this amount (for phase shifting waves to match tank) [(s)] + character(1024) :: MD_InputFile !< MoorDyn input file [(-)] + character(1024) :: AD_InputFile !< AeroDyn input file [(-)] + character(1024) :: IfW_InputFile !< InflowWind input file [(-)] + END TYPE ModSettings +! ======================= +! ========= SimSettingsType ======= + TYPE, PUBLIC :: SimSettingsType + TYPE(SimType) :: Sim !< Simulation settings [-] + TYPE(EnvType) :: Env !< Environment settings [-] + TYPE(TurbConfigType) :: TrbCfg !< Turbine configuration [-] + TYPE(TurbInitCondType) :: TrbInit !< Turbine initial operating point [-] + TYPE(WaveBuoyType) :: WaveBuoy !< Wave elevation buoy locat (x-y) [(m)] + TYPE(OutFilesType) :: Outs !< Output settings [-] + TYPE(VizType) :: Viz !< Vizualization settings [-] + TYPE(ModSettings) :: ModSettings !< Input files for each module [-] + END TYPE SimSettingsType +! ======================= +! ========= CalcStepIOdataType ======= + TYPE, PUBLIC :: CalcStepIOdataType + REAL(c_double) :: Time_c = 0.0_R8Ki !< IN: time [(s)] + REAL(c_float) , DIMENSION(1:6) :: PosAng_c = 0.0_R4Ki !< IN: Position + Euler Ang [x,y,z,phi,theta,psi] [[(m) (rad)]] + REAL(c_float) , DIMENSION(1:6) :: Vel_c = 0.0_R4Ki !< IN: Velocity [Vx,Vy,Vz,RVx,RVy,RVz] [[(m/s) (rad/s)]] + REAL(c_float) , DIMENSION(1:6) :: Acc_c = 0.0_R4Ki !< IN: Acceleration [Ax,Ay,Az,RAx,RAy,RAz] [[(m/s^2) (rad/s^2)]] + REAL(c_float) , DIMENSION(1:6) :: FrcMom_c = 0.0_R4Ki !< OUT: Acceleration [Fx,Fy,Fz,Mx,My,Mz] [[(N) (N-m)]] + REAL(c_float) , DIMENSION(1:6) :: FrcMom_MD_c = 0.0_R4Ki !< calculated forces/moments from MD [-] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: FrcMom_ADI_c !< calculated forces/moments from ADI [-] + REAL(c_float) , DIMENSION(1:3) :: HubVel_ADI_c = 0.0_R4Ki !< hub height wind vel from ADI [-] + REAL(ReKi) :: BuoyWaveElev = 0.0_ReKi !< calculated wave elevation at buoy [-] + END TYPE CalcStepIOdataType +! ======================= +! ========= WrOutputDataType ======= + TYPE, PUBLIC :: WrOutputDataType + INTEGER(IntKi) :: NumChans_cbind = 0 !< Number of output channels from c-bind [-] + INTEGER(IntKi) :: NumChans_SS = 0 !< Number of output channels from SS [-] + INTEGER(IntKi) :: NumChans_MD = 0 !< Number of output channels from MD [-] + INTEGER(IntKi) :: NumChans_ADI = 0 !< Number of output channels from ADI [-] + INTEGER(IntKi) :: NumChans_all = 0 !< Total number of channels (sum of above) [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputHdr_SS !< output file header names from SS [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputUnt_SS !< output file header units from SS [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputHdr_MD !< output file header names from MD [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputUnt_MD !< output file header units from MD [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputHdr_ADI !< output file header names from ADI [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputUnt_ADI !< output file header units from ADI [-] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: OutData_SS_c !< output data from SS as passed c_float [-] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: OutData_MD_c !< output data from MD as passed c_float [-] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: OutData_ADI_c !< output data from ADI as passed c_float [-] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: OutData_SS !< output data from SS [-] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: OutData_MD !< output data from MD [-] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: OutData_ADI !< output data from ADI [-] + character(1024) :: OutName !< Output file name [-] + INTEGER(IntKi) :: OutUn = -1 !< Output unit [-] + END TYPE WrOutputDataType +! ======================= +! ========= MeshesMotionType ======= + TYPE, PUBLIC :: MeshesMotionType + TYPE(MeshType) :: PtfmPtMotion !< Platform principle ref point. Also serves as tower base [-] + TYPE(MeshType) :: TowerMotion !< Tower mesh (used only for vis) [-] + TYPE(MeshType) :: HubMotion !< Hub mesh (for mappings, no loadings) [-] + TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: BladeRootMotion !< Blade root motions [-] + TYPE(MeshType) :: WaveBuoyMotion !< wave measurement buoy motion (sensor only) [-] + END TYPE MeshesMotionType +! ======================= +! ========= MeshesLoadsType ======= + TYPE, PUBLIC :: MeshesLoadsType + TYPE(MeshType) :: PtfmPtLoads !< Platform principle ref point loads output [-] + TYPE(MeshType) :: PtfmPtLoadsTmp !< Platform principle ref point loads output - temp var for load summation [-] + TYPE(MeshType) :: MooringLoads !< Mooring loads (always at PtfmPt, but separated for simplicity) [-] + TYPE(MeshType) :: TowerLoads !< Tower mesh (unused) [-] + TYPE(MeshType) :: HubLoads !< Hub mesh (for mappings, intermediate loads) [-] + TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: BladeRootLoads !< Blade root loads [-] + END TYPE MeshesLoadsType +! ======================= +! ========= MeshesMapsType ======= + TYPE, PUBLIC :: MeshesMapsType + TYPE(MeshMapType) :: Motion_PRP_2_Twr !< PRP to tower motion [-] + TYPE(MeshMapType) :: Motion_PRP_2_Hub !< Twrtop to nacelle - add rotation afterwards [-] + TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: Motion_Hub_2_BldRoot !< Hub to blade root motion transfer [-] + TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: Load_BldRoot_2_Hub !< Blade root loads to hub [-] + TYPE(MeshMapType) :: Load_Hub_2_PRP !< Hub to nacelle load transfer [-] + TYPE(MeshMapType) :: Load_Twr_2_PRP !< Tower loads to PRP (unused) [-] + TYPE(MeshMapType) :: Load_Moor_2_PRP !< Mooring loads to PRP [-] + END TYPE MeshesMapsType +! ======================= +! ========= StructTmpType ======= + TYPE, PUBLIC :: StructTmpType + REAL(ReKi) :: Azimuth = 0 !< Current Azimuth [(rad)] + REAL(ReKi) :: RotSpeed = 0.0_ReKi !< Rotor speed [(RPM)] + REAL(ReKi) :: BldPitch = 0.0_ReKi !< Blade pitch [(rad)] + REAL(ReKi) :: NacYaw = 0.0_ReKi !< Nacelle-yaw angle [(rad)] + REAL(ReKi) , DIMENSION(1:6) :: FrcMom_ADI_at_Ptfm = 0.0_ReKi !< Total aero loading summed to Ptfm point [(N,] + REAL(ReKi) , DIMENSION(1:6) :: FrcMom_MD_at_Ptfm = 0.0_ReKi !< MoorDyn loading at Ptfm point [(N,] + REAL(c_float) , DIMENSION(1:2) :: BuoyPos_c = 0.0_R4Ki !< Buoy XY position [(m)] + REAL(c_float) , DIMENSION(1:6) :: PtfmPosAng_c = 0.0_R4Ki !< Temp position and euler angle [(m,] + REAL(c_float) , DIMENSION(1:6) :: PtfmVel_c = 0.0_R4Ki !< Temp velocity [(m/s,] + REAL(c_float) , DIMENSION(1:6) :: PtfmAcc_c = 0.0_R4Ki !< Temp acceleration [(m/s^2,] + REAL(c_float) , DIMENSION(1:3) :: NacPos_c = 0.0_R4Ki !< Temp nacelle position [(m,] + REAL(c_double) , DIMENSION(1:9) :: NacDCM_c = 0.0_R8Ki !< Temp nacelle orientation DCM [(-)] + REAL(c_float) , DIMENSION(1:6) :: NacVel_c = 0.0_R4Ki !< Temp nacelle velocity [(m/s,] + REAL(c_float) , DIMENSION(1:6) :: NacAcc_c = 0.0_R4Ki !< Temp nacelle acceleration [(m/s^2,] + REAL(c_float) , DIMENSION(1:3) :: HubPos_c = 0.0_R4Ki !< Temp hub position [(m,] + REAL(c_double) , DIMENSION(1:9) :: HubDCM_c = 0.0_R8Ki !< Temp hub orientation DCM [(-)] + REAL(c_float) , DIMENSION(1:6) :: HubVel_c = 0.0_R4Ki !< Temp hub velocity [(m/s,] + REAL(c_float) , DIMENSION(1:6) :: HubAcc_c = 0.0_R4Ki !< Temp hub acceleration [(m/s^2,] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: BldPos_c !< Temp blade position -- sequential by blade [(m)] + REAL(c_double) , DIMENSION(:), ALLOCATABLE :: BldDCM_c !< Temp blade orientation DCM -- flat sequential by blade [(-)] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: BldVel_c !< Temp blade velocity -- sequential by blade [(m/s,] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: BldAcc_c !< Temp blade acceleration -- sequential by blade [(m/s^2,] + END TYPE StructTmpType +! ======================= +CONTAINS + +subroutine WT_CopySimType(SrcSimTypeData, DstSimTypeData, CtrlCode, ErrStat, ErrMsg) + type(SimType), intent(in) :: SrcSimTypeData + type(SimType), intent(inout) :: DstSimTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopySimType' + ErrStat = ErrID_None + ErrMsg = '' + DstSimTypeData%DT = SrcSimTypeData%DT + DstSimTypeData%TMax = SrcSimTypeData%TMax + DstSimTypeData%MHK = SrcSimTypeData%MHK + DstSimTypeData%InterpOrd = SrcSimTypeData%InterpOrd + DstSimTypeData%ScaleFact = SrcSimTypeData%ScaleFact + DstSimTypeData%DensFact = SrcSimTypeData%DensFact + DstSimTypeData%DebugLevel = SrcSimTypeData%DebugLevel + DstSimTypeData%OutRootName = SrcSimTypeData%OutRootName +end subroutine + +subroutine WT_DestroySimType(SimTypeData, ErrStat, ErrMsg) + type(SimType), intent(inout) :: SimTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroySimType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackSimType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SimType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackSimType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%DT) + call RegPack(RF, InData%TMax) + call RegPack(RF, InData%MHK) + call RegPack(RF, InData%InterpOrd) + call RegPack(RF, InData%ScaleFact) + call RegPack(RF, InData%DensFact) + call RegPack(RF, InData%DebugLevel) + call RegPack(RF, InData%OutRootName) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackSimType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SimType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackSimType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%DT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TMax); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MHK); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%InterpOrd); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%ScaleFact); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DensFact); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DebugLevel); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutRootName); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyEnvType(SrcEnvTypeData, DstEnvTypeData, CtrlCode, ErrStat, ErrMsg) + type(EnvType), intent(in) :: SrcEnvTypeData + type(EnvType), intent(inout) :: DstEnvTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyEnvType' + ErrStat = ErrID_None + ErrMsg = '' + DstEnvTypeData%Gravity = SrcEnvTypeData%Gravity + DstEnvTypeData%WtrDens = SrcEnvTypeData%WtrDens + DstEnvTypeData%WtrVisc = SrcEnvTypeData%WtrVisc + DstEnvTypeData%SpdSound = SrcEnvTypeData%SpdSound + DstEnvTypeData%Patm = SrcEnvTypeData%Patm + DstEnvTypeData%Pvap = SrcEnvTypeData%Pvap + DstEnvTypeData%WtrDpth = SrcEnvTypeData%WtrDpth + DstEnvTypeData%MSL2SWL = SrcEnvTypeData%MSL2SWL +end subroutine + +subroutine WT_DestroyEnvType(EnvTypeData, ErrStat, ErrMsg) + type(EnvType), intent(inout) :: EnvTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyEnvType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackEnvType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(EnvType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackEnvType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%Gravity) + call RegPack(RF, InData%WtrDens) + call RegPack(RF, InData%WtrVisc) + call RegPack(RF, InData%SpdSound) + call RegPack(RF, InData%Patm) + call RegPack(RF, InData%Pvap) + call RegPack(RF, InData%WtrDpth) + call RegPack(RF, InData%MSL2SWL) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackEnvType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(EnvType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackEnvType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%Gravity); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WtrDens); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WtrVisc); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%SpdSound); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Patm); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Pvap); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WtrDpth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MSL2SWL); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyTurbConfigType(SrcTurbConfigTypeData, DstTurbConfigTypeData, CtrlCode, ErrStat, ErrMsg) + type(TurbConfigType), intent(in) :: SrcTurbConfigTypeData + type(TurbConfigType), intent(inout) :: DstTurbConfigTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyTurbConfigType' + ErrStat = ErrID_None + ErrMsg = '' + DstTurbConfigTypeData%NumBl = SrcTurbConfigTypeData%NumBl + DstTurbConfigTypeData%HubRad = SrcTurbConfigTypeData%HubRad + DstTurbConfigTypeData%PreCone = SrcTurbConfigTypeData%PreCone + DstTurbConfigTypeData%OverHang = SrcTurbConfigTypeData%OverHang + DstTurbConfigTypeData%ShftTilt = SrcTurbConfigTypeData%ShftTilt + DstTurbConfigTypeData%Twr2Shft = SrcTurbConfigTypeData%Twr2Shft + DstTurbConfigTypeData%TowerHt = SrcTurbConfigTypeData%TowerHt + DstTurbConfigTypeData%TowerBsPt = SrcTurbConfigTypeData%TowerBsPt + DstTurbConfigTypeData%PtfmRefPos = SrcTurbConfigTypeData%PtfmRefPos + DstTurbConfigTypeData%PtfmRefOrient = SrcTurbConfigTypeData%PtfmRefOrient +end subroutine + +subroutine WT_DestroyTurbConfigType(TurbConfigTypeData, ErrStat, ErrMsg) + type(TurbConfigType), intent(inout) :: TurbConfigTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyTurbConfigType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackTurbConfigType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(TurbConfigType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackTurbConfigType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%NumBl) + call RegPack(RF, InData%HubRad) + call RegPack(RF, InData%PreCone) + call RegPack(RF, InData%OverHang) + call RegPack(RF, InData%ShftTilt) + call RegPack(RF, InData%Twr2Shft) + call RegPack(RF, InData%TowerHt) + call RegPack(RF, InData%TowerBsPt) + call RegPack(RF, InData%PtfmRefPos) + call RegPack(RF, InData%PtfmRefOrient) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackTurbConfigType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(TurbConfigType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackTurbConfigType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%NumBl); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PreCone); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OverHang); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%ShftTilt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Twr2Shft); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TowerHt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TowerBsPt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmRefPos); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmRefOrient); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyTurbInitCondType(SrcTurbInitCondTypeData, DstTurbInitCondTypeData, CtrlCode, ErrStat, ErrMsg) + type(TurbInitCondType), intent(in) :: SrcTurbInitCondTypeData + type(TurbInitCondType), intent(inout) :: DstTurbInitCondTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyTurbInitCondType' + ErrStat = ErrID_None + ErrMsg = '' + DstTurbInitCondTypeData%RotSpeed = SrcTurbInitCondTypeData%RotSpeed + DstTurbInitCondTypeData%NacYaw = SrcTurbInitCondTypeData%NacYaw + DstTurbInitCondTypeData%BldPitch = SrcTurbInitCondTypeData%BldPitch + DstTurbInitCondTypeData%Azimuth = SrcTurbInitCondTypeData%Azimuth +end subroutine + +subroutine WT_DestroyTurbInitCondType(TurbInitCondTypeData, ErrStat, ErrMsg) + type(TurbInitCondType), intent(inout) :: TurbInitCondTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyTurbInitCondType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackTurbInitCondType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(TurbInitCondType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackTurbInitCondType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%RotSpeed) + call RegPack(RF, InData%NacYaw) + call RegPack(RF, InData%BldPitch) + call RegPack(RF, InData%Azimuth) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackTurbInitCondType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(TurbInitCondType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackTurbInitCondType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%RotSpeed); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacYaw); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BldPitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Azimuth); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyWaveBuoyType(SrcWaveBuoyTypeData, DstWaveBuoyTypeData, CtrlCode, ErrStat, ErrMsg) + type(WaveBuoyType), intent(in) :: SrcWaveBuoyTypeData + type(WaveBuoyType), intent(inout) :: DstWaveBuoyTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyWaveBuoyType' + ErrStat = ErrID_None + ErrMsg = '' + DstWaveBuoyTypeData%XYLoc = SrcWaveBuoyTypeData%XYLoc +end subroutine + +subroutine WT_DestroyWaveBuoyType(WaveBuoyTypeData, ErrStat, ErrMsg) + type(WaveBuoyType), intent(inout) :: WaveBuoyTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyWaveBuoyType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackWaveBuoyType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(WaveBuoyType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackWaveBuoyType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%XYLoc) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackWaveBuoyType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(WaveBuoyType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackWaveBuoyType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%XYLoc); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyOutFilesType(SrcOutFilesTypeData, DstOutFilesTypeData, CtrlCode, ErrStat, ErrMsg) + type(OutFilesType), intent(in) :: SrcOutFilesTypeData + type(OutFilesType), intent(inout) :: DstOutFilesTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyOutFilesType' + ErrStat = ErrID_None + ErrMsg = '' + DstOutFilesTypeData%SendScreenToFile = SrcOutFilesTypeData%SendScreenToFile + DstOutFilesTypeData%OutFile = SrcOutFilesTypeData%OutFile + DstOutFilesTypeData%OutFmt = SrcOutFilesTypeData%OutFmt +end subroutine + +subroutine WT_DestroyOutFilesType(OutFilesTypeData, ErrStat, ErrMsg) + type(OutFilesType), intent(inout) :: OutFilesTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyOutFilesType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackOutFilesType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(OutFilesType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackOutFilesType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%SendScreenToFile) + call RegPack(RF, InData%OutFile) + call RegPack(RF, InData%OutFmt) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackOutFilesType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(OutFilesType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackOutFilesType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%SendScreenToFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutFmt); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyVizType(SrcVizTypeData, DstVizTypeData, CtrlCode, ErrStat, ErrMsg) + type(VizType), intent(in) :: SrcVizTypeData + type(VizType), intent(inout) :: DstVizTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyVizType' + ErrStat = ErrID_None + ErrMsg = '' + DstVizTypeData%WrVTK = SrcVizTypeData%WrVTK + DstVizTypeData%WrVTK_type = SrcVizTypeData%WrVTK_type + DstVizTypeData%WrVTK_DT = SrcVizTypeData%WrVTK_DT + DstVizTypeData%WrVTK_dir = SrcVizTypeData%WrVTK_dir + DstVizTypeData%VTKNacDim = SrcVizTypeData%VTKNacDim + DstVizTypeData%Twidth = SrcVizTypeData%Twidth +end subroutine + +subroutine WT_DestroyVizType(VizTypeData, ErrStat, ErrMsg) + type(VizType), intent(inout) :: VizTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyVizType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackVizType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(VizType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackVizType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%WrVTK) + call RegPack(RF, InData%WrVTK_type) + call RegPack(RF, InData%WrVTK_DT) + call RegPack(RF, InData%WrVTK_dir) + call RegPack(RF, InData%VTKNacDim) + call RegPack(RF, InData%Twidth) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackVizType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(VizType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackVizType' + if (RF%ErrStat /= ErrID_None) 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%WrVTK_DT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WrVTK_dir); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%VTKNacDim); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Twidth); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyModSettings(SrcModSettingsData, DstModSettingsData, CtrlCode, ErrStat, ErrMsg) + type(ModSettings), intent(in) :: SrcModSettingsData + type(ModSettings), intent(inout) :: DstModSettingsData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyModSettings' + ErrStat = ErrID_None + ErrMsg = '' + DstModSettingsData%SS_InputFile = SrcModSettingsData%SS_InputFile + DstModSettingsData%WaveTimeShift = SrcModSettingsData%WaveTimeShift + DstModSettingsData%MD_InputFile = SrcModSettingsData%MD_InputFile + DstModSettingsData%AD_InputFile = SrcModSettingsData%AD_InputFile + DstModSettingsData%IfW_InputFile = SrcModSettingsData%IfW_InputFile +end subroutine + +subroutine WT_DestroyModSettings(ModSettingsData, ErrStat, ErrMsg) + type(ModSettings), intent(inout) :: ModSettingsData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyModSettings' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackModSettings(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ModSettings), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackModSettings' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%SS_InputFile) + call RegPack(RF, InData%WaveTimeShift) + call RegPack(RF, InData%MD_InputFile) + call RegPack(RF, InData%AD_InputFile) + call RegPack(RF, InData%IfW_InputFile) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackModSettings(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ModSettings), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackModSettings' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%SS_InputFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WaveTimeShift); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MD_InputFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%AD_InputFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%IfW_InputFile); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopySimSettingsType(SrcSimSettingsTypeData, DstSimSettingsTypeData, CtrlCode, ErrStat, ErrMsg) + type(SimSettingsType), intent(in) :: SrcSimSettingsTypeData + type(SimSettingsType), intent(inout) :: DstSimSettingsTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_CopySimSettingsType' + ErrStat = ErrID_None + ErrMsg = '' + call WT_CopySimType(SrcSimSettingsTypeData%Sim, DstSimSettingsTypeData%Sim, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyEnvType(SrcSimSettingsTypeData%Env, DstSimSettingsTypeData%Env, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyTurbConfigType(SrcSimSettingsTypeData%TrbCfg, DstSimSettingsTypeData%TrbCfg, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyTurbInitCondType(SrcSimSettingsTypeData%TrbInit, DstSimSettingsTypeData%TrbInit, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyWaveBuoyType(SrcSimSettingsTypeData%WaveBuoy, DstSimSettingsTypeData%WaveBuoy, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyOutFilesType(SrcSimSettingsTypeData%Outs, DstSimSettingsTypeData%Outs, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyVizType(SrcSimSettingsTypeData%Viz, DstSimSettingsTypeData%Viz, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyModSettings(SrcSimSettingsTypeData%ModSettings, DstSimSettingsTypeData%ModSettings, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return +end subroutine + +subroutine WT_DestroySimSettingsType(SimSettingsTypeData, ErrStat, ErrMsg) + type(SimSettingsType), intent(inout) :: SimSettingsTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_DestroySimSettingsType' + ErrStat = ErrID_None + ErrMsg = '' + call WT_DestroySimType(SimSettingsTypeData%Sim, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyEnvType(SimSettingsTypeData%Env, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyTurbConfigType(SimSettingsTypeData%TrbCfg, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyTurbInitCondType(SimSettingsTypeData%TrbInit, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyWaveBuoyType(SimSettingsTypeData%WaveBuoy, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyOutFilesType(SimSettingsTypeData%Outs, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyVizType(SimSettingsTypeData%Viz, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyModSettings(SimSettingsTypeData%ModSettings, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine WT_PackSimSettingsType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SimSettingsType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackSimSettingsType' + if (RF%ErrStat >= AbortErrLev) return + call WT_PackSimType(RF, InData%Sim) + call WT_PackEnvType(RF, InData%Env) + call WT_PackTurbConfigType(RF, InData%TrbCfg) + call WT_PackTurbInitCondType(RF, InData%TrbInit) + call WT_PackWaveBuoyType(RF, InData%WaveBuoy) + call WT_PackOutFilesType(RF, InData%Outs) + call WT_PackVizType(RF, InData%Viz) + call WT_PackModSettings(RF, InData%ModSettings) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackSimSettingsType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SimSettingsType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackSimSettingsType' + if (RF%ErrStat /= ErrID_None) return + call WT_UnpackSimType(RF, OutData%Sim) ! Sim + call WT_UnpackEnvType(RF, OutData%Env) ! Env + call WT_UnpackTurbConfigType(RF, OutData%TrbCfg) ! TrbCfg + call WT_UnpackTurbInitCondType(RF, OutData%TrbInit) ! TrbInit + call WT_UnpackWaveBuoyType(RF, OutData%WaveBuoy) ! WaveBuoy + call WT_UnpackOutFilesType(RF, OutData%Outs) ! Outs + call WT_UnpackVizType(RF, OutData%Viz) ! Viz + call WT_UnpackModSettings(RF, OutData%ModSettings) ! ModSettings +end subroutine + +subroutine WT_CopyCalcStepIOdataType(SrcCalcStepIOdataTypeData, DstCalcStepIOdataTypeData, CtrlCode, ErrStat, ErrMsg) + type(CalcStepIOdataType), intent(in) :: SrcCalcStepIOdataTypeData + type(CalcStepIOdataType), intent(inout) :: DstCalcStepIOdataTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'WT_CopyCalcStepIOdataType' + ErrStat = ErrID_None + ErrMsg = '' + DstCalcStepIOdataTypeData%Time_c = SrcCalcStepIOdataTypeData%Time_c + DstCalcStepIOdataTypeData%PosAng_c = SrcCalcStepIOdataTypeData%PosAng_c + DstCalcStepIOdataTypeData%Vel_c = SrcCalcStepIOdataTypeData%Vel_c + DstCalcStepIOdataTypeData%Acc_c = SrcCalcStepIOdataTypeData%Acc_c + DstCalcStepIOdataTypeData%FrcMom_c = SrcCalcStepIOdataTypeData%FrcMom_c + DstCalcStepIOdataTypeData%FrcMom_MD_c = SrcCalcStepIOdataTypeData%FrcMom_MD_c + if (allocated(SrcCalcStepIOdataTypeData%FrcMom_ADI_c)) then + LB(1:1) = lbound(SrcCalcStepIOdataTypeData%FrcMom_ADI_c) + UB(1:1) = ubound(SrcCalcStepIOdataTypeData%FrcMom_ADI_c) + if (.not. allocated(DstCalcStepIOdataTypeData%FrcMom_ADI_c)) then + allocate(DstCalcStepIOdataTypeData%FrcMom_ADI_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstCalcStepIOdataTypeData%FrcMom_ADI_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstCalcStepIOdataTypeData%FrcMom_ADI_c = SrcCalcStepIOdataTypeData%FrcMom_ADI_c + end if + DstCalcStepIOdataTypeData%HubVel_ADI_c = SrcCalcStepIOdataTypeData%HubVel_ADI_c + DstCalcStepIOdataTypeData%BuoyWaveElev = SrcCalcStepIOdataTypeData%BuoyWaveElev +end subroutine + +subroutine WT_DestroyCalcStepIOdataType(CalcStepIOdataTypeData, ErrStat, ErrMsg) + type(CalcStepIOdataType), intent(inout) :: CalcStepIOdataTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyCalcStepIOdataType' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(CalcStepIOdataTypeData%FrcMom_ADI_c)) then + deallocate(CalcStepIOdataTypeData%FrcMom_ADI_c) + end if +end subroutine + +subroutine WT_PackCalcStepIOdataType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(CalcStepIOdataType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackCalcStepIOdataType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%Time_c) + call RegPack(RF, InData%PosAng_c) + call RegPack(RF, InData%Vel_c) + call RegPack(RF, InData%Acc_c) + call RegPack(RF, InData%FrcMom_c) + call RegPack(RF, InData%FrcMom_MD_c) + call RegPackAlloc(RF, InData%FrcMom_ADI_c) + call RegPack(RF, InData%HubVel_ADI_c) + call RegPack(RF, InData%BuoyWaveElev) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackCalcStepIOdataType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(CalcStepIOdataType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackCalcStepIOdataType' + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%Time_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PosAng_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Vel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Acc_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%FrcMom_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%FrcMom_MD_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%FrcMom_ADI_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubVel_ADI_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BuoyWaveElev); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyWrOutputDataType(SrcWrOutputDataTypeData, DstWrOutputDataTypeData, CtrlCode, ErrStat, ErrMsg) + type(WrOutputDataType), intent(in) :: SrcWrOutputDataTypeData + type(WrOutputDataType), intent(inout) :: DstWrOutputDataTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'WT_CopyWrOutputDataType' + ErrStat = ErrID_None + ErrMsg = '' + DstWrOutputDataTypeData%NumChans_cbind = SrcWrOutputDataTypeData%NumChans_cbind + DstWrOutputDataTypeData%NumChans_SS = SrcWrOutputDataTypeData%NumChans_SS + DstWrOutputDataTypeData%NumChans_MD = SrcWrOutputDataTypeData%NumChans_MD + DstWrOutputDataTypeData%NumChans_ADI = SrcWrOutputDataTypeData%NumChans_ADI + DstWrOutputDataTypeData%NumChans_all = SrcWrOutputDataTypeData%NumChans_all + if (allocated(SrcWrOutputDataTypeData%WriteOutputHdr_SS)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputHdr_SS) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputHdr_SS) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputHdr_SS)) then + allocate(DstWrOutputDataTypeData%WriteOutputHdr_SS(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputHdr_SS.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputHdr_SS = SrcWrOutputDataTypeData%WriteOutputHdr_SS + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputUnt_SS)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputUnt_SS) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputUnt_SS) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputUnt_SS)) then + allocate(DstWrOutputDataTypeData%WriteOutputUnt_SS(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputUnt_SS.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputUnt_SS = SrcWrOutputDataTypeData%WriteOutputUnt_SS + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputHdr_MD)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputHdr_MD) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputHdr_MD) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputHdr_MD)) then + allocate(DstWrOutputDataTypeData%WriteOutputHdr_MD(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputHdr_MD.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputHdr_MD = SrcWrOutputDataTypeData%WriteOutputHdr_MD + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputUnt_MD)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputUnt_MD) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputUnt_MD) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputUnt_MD)) then + allocate(DstWrOutputDataTypeData%WriteOutputUnt_MD(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputUnt_MD.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputUnt_MD = SrcWrOutputDataTypeData%WriteOutputUnt_MD + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputHdr_ADI)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputHdr_ADI) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputHdr_ADI) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputHdr_ADI)) then + allocate(DstWrOutputDataTypeData%WriteOutputHdr_ADI(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputHdr_ADI.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputHdr_ADI = SrcWrOutputDataTypeData%WriteOutputHdr_ADI + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputUnt_ADI)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputUnt_ADI) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputUnt_ADI) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputUnt_ADI)) then + allocate(DstWrOutputDataTypeData%WriteOutputUnt_ADI(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputUnt_ADI.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputUnt_ADI = SrcWrOutputDataTypeData%WriteOutputUnt_ADI + end if + if (allocated(SrcWrOutputDataTypeData%OutData_SS_c)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_SS_c) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_SS_c) + if (.not. allocated(DstWrOutputDataTypeData%OutData_SS_c)) then + allocate(DstWrOutputDataTypeData%OutData_SS_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_SS_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_SS_c = SrcWrOutputDataTypeData%OutData_SS_c + end if + if (allocated(SrcWrOutputDataTypeData%OutData_MD_c)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_MD_c) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_MD_c) + if (.not. allocated(DstWrOutputDataTypeData%OutData_MD_c)) then + allocate(DstWrOutputDataTypeData%OutData_MD_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_MD_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_MD_c = SrcWrOutputDataTypeData%OutData_MD_c + end if + if (allocated(SrcWrOutputDataTypeData%OutData_ADI_c)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_ADI_c) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_ADI_c) + if (.not. allocated(DstWrOutputDataTypeData%OutData_ADI_c)) then + allocate(DstWrOutputDataTypeData%OutData_ADI_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_ADI_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_ADI_c = SrcWrOutputDataTypeData%OutData_ADI_c + end if + if (allocated(SrcWrOutputDataTypeData%OutData_SS)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_SS) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_SS) + if (.not. allocated(DstWrOutputDataTypeData%OutData_SS)) then + allocate(DstWrOutputDataTypeData%OutData_SS(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_SS.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_SS = SrcWrOutputDataTypeData%OutData_SS + end if + if (allocated(SrcWrOutputDataTypeData%OutData_MD)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_MD) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_MD) + if (.not. allocated(DstWrOutputDataTypeData%OutData_MD)) then + allocate(DstWrOutputDataTypeData%OutData_MD(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_MD.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_MD = SrcWrOutputDataTypeData%OutData_MD + end if + if (allocated(SrcWrOutputDataTypeData%OutData_ADI)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_ADI) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_ADI) + if (.not. allocated(DstWrOutputDataTypeData%OutData_ADI)) then + allocate(DstWrOutputDataTypeData%OutData_ADI(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_ADI.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_ADI = SrcWrOutputDataTypeData%OutData_ADI + end if + DstWrOutputDataTypeData%OutName = SrcWrOutputDataTypeData%OutName + DstWrOutputDataTypeData%OutUn = SrcWrOutputDataTypeData%OutUn +end subroutine + +subroutine WT_DestroyWrOutputDataType(WrOutputDataTypeData, ErrStat, ErrMsg) + type(WrOutputDataType), intent(inout) :: WrOutputDataTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyWrOutputDataType' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(WrOutputDataTypeData%WriteOutputHdr_SS)) then + deallocate(WrOutputDataTypeData%WriteOutputHdr_SS) + end if + if (allocated(WrOutputDataTypeData%WriteOutputUnt_SS)) then + deallocate(WrOutputDataTypeData%WriteOutputUnt_SS) + end if + if (allocated(WrOutputDataTypeData%WriteOutputHdr_MD)) then + deallocate(WrOutputDataTypeData%WriteOutputHdr_MD) + end if + if (allocated(WrOutputDataTypeData%WriteOutputUnt_MD)) then + deallocate(WrOutputDataTypeData%WriteOutputUnt_MD) + end if + if (allocated(WrOutputDataTypeData%WriteOutputHdr_ADI)) then + deallocate(WrOutputDataTypeData%WriteOutputHdr_ADI) + end if + if (allocated(WrOutputDataTypeData%WriteOutputUnt_ADI)) then + deallocate(WrOutputDataTypeData%WriteOutputUnt_ADI) + end if + if (allocated(WrOutputDataTypeData%OutData_SS_c)) then + deallocate(WrOutputDataTypeData%OutData_SS_c) + end if + if (allocated(WrOutputDataTypeData%OutData_MD_c)) then + deallocate(WrOutputDataTypeData%OutData_MD_c) + end if + if (allocated(WrOutputDataTypeData%OutData_ADI_c)) then + deallocate(WrOutputDataTypeData%OutData_ADI_c) + end if + if (allocated(WrOutputDataTypeData%OutData_SS)) then + deallocate(WrOutputDataTypeData%OutData_SS) + end if + if (allocated(WrOutputDataTypeData%OutData_MD)) then + deallocate(WrOutputDataTypeData%OutData_MD) + end if + if (allocated(WrOutputDataTypeData%OutData_ADI)) then + deallocate(WrOutputDataTypeData%OutData_ADI) + end if +end subroutine + +subroutine WT_PackWrOutputDataType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(WrOutputDataType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackWrOutputDataType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%NumChans_cbind) + call RegPack(RF, InData%NumChans_SS) + call RegPack(RF, InData%NumChans_MD) + call RegPack(RF, InData%NumChans_ADI) + call RegPack(RF, InData%NumChans_all) + call RegPackAlloc(RF, InData%WriteOutputHdr_SS) + call RegPackAlloc(RF, InData%WriteOutputUnt_SS) + call RegPackAlloc(RF, InData%WriteOutputHdr_MD) + call RegPackAlloc(RF, InData%WriteOutputUnt_MD) + call RegPackAlloc(RF, InData%WriteOutputHdr_ADI) + call RegPackAlloc(RF, InData%WriteOutputUnt_ADI) + call RegPackAlloc(RF, InData%OutData_SS_c) + call RegPackAlloc(RF, InData%OutData_MD_c) + call RegPackAlloc(RF, InData%OutData_ADI_c) + call RegPackAlloc(RF, InData%OutData_SS) + call RegPackAlloc(RF, InData%OutData_MD) + call RegPackAlloc(RF, InData%OutData_ADI) + call RegPack(RF, InData%OutName) + call RegPack(RF, InData%OutUn) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackWrOutputDataType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(WrOutputDataType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackWrOutputDataType' + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%NumChans_cbind); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumChans_SS); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumChans_MD); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumChans_ADI); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumChans_all); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputHdr_SS); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputUnt_SS); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputHdr_MD); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputUnt_MD); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputHdr_ADI); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputUnt_ADI); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_SS_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_MD_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_ADI_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_SS); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_MD); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_ADI); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutName); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutUn); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyMeshesMotionType(SrcMeshesMotionTypeData, DstMeshesMotionTypeData, CtrlCode, ErrStat, ErrMsg) + type(MeshesMotionType), intent(inout) :: SrcMeshesMotionTypeData + type(MeshesMotionType), intent(inout) :: DstMeshesMotionTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_CopyMeshesMotionType' + ErrStat = ErrID_None + ErrMsg = '' + call MeshCopy(SrcMeshesMotionTypeData%PtfmPtMotion, DstMeshesMotionTypeData%PtfmPtMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesMotionTypeData%TowerMotion, DstMeshesMotionTypeData%TowerMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesMotionTypeData%HubMotion, DstMeshesMotionTypeData%HubMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcMeshesMotionTypeData%BladeRootMotion)) then + LB(1:1) = lbound(SrcMeshesMotionTypeData%BladeRootMotion) + UB(1:1) = ubound(SrcMeshesMotionTypeData%BladeRootMotion) + if (.not. allocated(DstMeshesMotionTypeData%BladeRootMotion)) then + allocate(DstMeshesMotionTypeData%BladeRootMotion(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMeshesMotionTypeData%BladeRootMotion.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call MeshCopy(SrcMeshesMotionTypeData%BladeRootMotion(i1), DstMeshesMotionTypeData%BladeRootMotion(i1), CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + call MeshCopy(SrcMeshesMotionTypeData%WaveBuoyMotion, DstMeshesMotionTypeData%WaveBuoyMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return +end subroutine + +subroutine WT_DestroyMeshesMotionType(MeshesMotionTypeData, ErrStat, ErrMsg) + type(MeshesMotionType), intent(inout) :: MeshesMotionTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_DestroyMeshesMotionType' + ErrStat = ErrID_None + ErrMsg = '' + call MeshDestroy( MeshesMotionTypeData%PtfmPtMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesMotionTypeData%TowerMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesMotionTypeData%HubMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(MeshesMotionTypeData%BladeRootMotion)) then + LB(1:1) = lbound(MeshesMotionTypeData%BladeRootMotion) + UB(1:1) = ubound(MeshesMotionTypeData%BladeRootMotion) + do i1 = LB(1), UB(1) + call MeshDestroy( MeshesMotionTypeData%BladeRootMotion(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(MeshesMotionTypeData%BladeRootMotion) + end if + call MeshDestroy( MeshesMotionTypeData%WaveBuoyMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine WT_PackMeshesMotionType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(MeshesMotionType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackMeshesMotionType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call MeshPack(RF, InData%PtfmPtMotion) + call MeshPack(RF, InData%TowerMotion) + call MeshPack(RF, InData%HubMotion) + call RegPack(RF, allocated(InData%BladeRootMotion)) + if (allocated(InData%BladeRootMotion)) then + call RegPackBounds(RF, 1, lbound(InData%BladeRootMotion), ubound(InData%BladeRootMotion)) + LB(1:1) = lbound(InData%BladeRootMotion) + UB(1:1) = ubound(InData%BladeRootMotion) + do i1 = LB(1), UB(1) + call MeshPack(RF, InData%BladeRootMotion(i1)) + end do + end if + call MeshPack(RF, InData%WaveBuoyMotion) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackMeshesMotionType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(MeshesMotionType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackMeshesMotionType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call MeshUnpack(RF, OutData%PtfmPtMotion) ! PtfmPtMotion + call MeshUnpack(RF, OutData%TowerMotion) ! TowerMotion + call MeshUnpack(RF, OutData%HubMotion) ! HubMotion + if (allocated(OutData%BladeRootMotion)) deallocate(OutData%BladeRootMotion) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%BladeRootMotion(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%BladeRootMotion.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call MeshUnpack(RF, OutData%BladeRootMotion(i1)) ! BladeRootMotion + end do + end if + call MeshUnpack(RF, OutData%WaveBuoyMotion) ! WaveBuoyMotion +end subroutine + +subroutine WT_CopyMeshesLoadsType(SrcMeshesLoadsTypeData, DstMeshesLoadsTypeData, CtrlCode, ErrStat, ErrMsg) + type(MeshesLoadsType), intent(inout) :: SrcMeshesLoadsTypeData + type(MeshesLoadsType), intent(inout) :: DstMeshesLoadsTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_CopyMeshesLoadsType' + ErrStat = ErrID_None + ErrMsg = '' + call MeshCopy(SrcMeshesLoadsTypeData%PtfmPtLoads, DstMeshesLoadsTypeData%PtfmPtLoads, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesLoadsTypeData%PtfmPtLoadsTmp, DstMeshesLoadsTypeData%PtfmPtLoadsTmp, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesLoadsTypeData%MooringLoads, DstMeshesLoadsTypeData%MooringLoads, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesLoadsTypeData%TowerLoads, DstMeshesLoadsTypeData%TowerLoads, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesLoadsTypeData%HubLoads, DstMeshesLoadsTypeData%HubLoads, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcMeshesLoadsTypeData%BladeRootLoads)) then + LB(1:1) = lbound(SrcMeshesLoadsTypeData%BladeRootLoads) + UB(1:1) = ubound(SrcMeshesLoadsTypeData%BladeRootLoads) + if (.not. allocated(DstMeshesLoadsTypeData%BladeRootLoads)) then + allocate(DstMeshesLoadsTypeData%BladeRootLoads(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMeshesLoadsTypeData%BladeRootLoads.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call MeshCopy(SrcMeshesLoadsTypeData%BladeRootLoads(i1), DstMeshesLoadsTypeData%BladeRootLoads(i1), CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if +end subroutine + +subroutine WT_DestroyMeshesLoadsType(MeshesLoadsTypeData, ErrStat, ErrMsg) + type(MeshesLoadsType), intent(inout) :: MeshesLoadsTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_DestroyMeshesLoadsType' + ErrStat = ErrID_None + ErrMsg = '' + call MeshDestroy( MeshesLoadsTypeData%PtfmPtLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesLoadsTypeData%PtfmPtLoadsTmp, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesLoadsTypeData%MooringLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesLoadsTypeData%TowerLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesLoadsTypeData%HubLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(MeshesLoadsTypeData%BladeRootLoads)) then + LB(1:1) = lbound(MeshesLoadsTypeData%BladeRootLoads) + UB(1:1) = ubound(MeshesLoadsTypeData%BladeRootLoads) + do i1 = LB(1), UB(1) + call MeshDestroy( MeshesLoadsTypeData%BladeRootLoads(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(MeshesLoadsTypeData%BladeRootLoads) + end if +end subroutine + +subroutine WT_PackMeshesLoadsType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(MeshesLoadsType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackMeshesLoadsType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call MeshPack(RF, InData%PtfmPtLoads) + call MeshPack(RF, InData%PtfmPtLoadsTmp) + call MeshPack(RF, InData%MooringLoads) + call MeshPack(RF, InData%TowerLoads) + call MeshPack(RF, InData%HubLoads) + call RegPack(RF, allocated(InData%BladeRootLoads)) + if (allocated(InData%BladeRootLoads)) then + call RegPackBounds(RF, 1, lbound(InData%BladeRootLoads), ubound(InData%BladeRootLoads)) + LB(1:1) = lbound(InData%BladeRootLoads) + UB(1:1) = ubound(InData%BladeRootLoads) + do i1 = LB(1), UB(1) + call MeshPack(RF, InData%BladeRootLoads(i1)) + end do + end if + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackMeshesLoadsType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(MeshesLoadsType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackMeshesLoadsType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call MeshUnpack(RF, OutData%PtfmPtLoads) ! PtfmPtLoads + call MeshUnpack(RF, OutData%PtfmPtLoadsTmp) ! PtfmPtLoadsTmp + call MeshUnpack(RF, OutData%MooringLoads) ! MooringLoads + call MeshUnpack(RF, OutData%TowerLoads) ! TowerLoads + call MeshUnpack(RF, OutData%HubLoads) ! HubLoads + if (allocated(OutData%BladeRootLoads)) deallocate(OutData%BladeRootLoads) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%BladeRootLoads(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%BladeRootLoads.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call MeshUnpack(RF, OutData%BladeRootLoads(i1)) ! BladeRootLoads + end do + end if +end subroutine + +subroutine WT_CopyMeshesMapsType(SrcMeshesMapsTypeData, DstMeshesMapsTypeData, CtrlCode, ErrStat, ErrMsg) + type(MeshesMapsType), intent(inout) :: SrcMeshesMapsTypeData + type(MeshesMapsType), intent(inout) :: DstMeshesMapsTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_CopyMeshesMapsType' + ErrStat = ErrID_None + ErrMsg = '' + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Motion_PRP_2_Twr, DstMeshesMapsTypeData%Motion_PRP_2_Twr, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Motion_PRP_2_Hub, DstMeshesMapsTypeData%Motion_PRP_2_Hub, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcMeshesMapsTypeData%Motion_Hub_2_BldRoot)) then + LB(1:1) = lbound(SrcMeshesMapsTypeData%Motion_Hub_2_BldRoot) + UB(1:1) = ubound(SrcMeshesMapsTypeData%Motion_Hub_2_BldRoot) + if (.not. allocated(DstMeshesMapsTypeData%Motion_Hub_2_BldRoot)) then + allocate(DstMeshesMapsTypeData%Motion_Hub_2_BldRoot(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMeshesMapsTypeData%Motion_Hub_2_BldRoot.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Motion_Hub_2_BldRoot(i1), DstMeshesMapsTypeData%Motion_Hub_2_BldRoot(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + if (allocated(SrcMeshesMapsTypeData%Load_BldRoot_2_Hub)) then + LB(1:1) = lbound(SrcMeshesMapsTypeData%Load_BldRoot_2_Hub) + UB(1:1) = ubound(SrcMeshesMapsTypeData%Load_BldRoot_2_Hub) + if (.not. allocated(DstMeshesMapsTypeData%Load_BldRoot_2_Hub)) then + allocate(DstMeshesMapsTypeData%Load_BldRoot_2_Hub(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMeshesMapsTypeData%Load_BldRoot_2_Hub.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Load_BldRoot_2_Hub(i1), DstMeshesMapsTypeData%Load_BldRoot_2_Hub(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Load_Hub_2_PRP, DstMeshesMapsTypeData%Load_Hub_2_PRP, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Load_Twr_2_PRP, DstMeshesMapsTypeData%Load_Twr_2_PRP, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Load_Moor_2_PRP, DstMeshesMapsTypeData%Load_Moor_2_PRP, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return +end subroutine + +subroutine WT_DestroyMeshesMapsType(MeshesMapsTypeData, ErrStat, ErrMsg) + type(MeshesMapsType), intent(inout) :: MeshesMapsTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_DestroyMeshesMapsType' + ErrStat = ErrID_None + ErrMsg = '' + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Motion_PRP_2_Twr, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Motion_PRP_2_Hub, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(MeshesMapsTypeData%Motion_Hub_2_BldRoot)) then + LB(1:1) = lbound(MeshesMapsTypeData%Motion_Hub_2_BldRoot) + UB(1:1) = ubound(MeshesMapsTypeData%Motion_Hub_2_BldRoot) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Motion_Hub_2_BldRoot(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(MeshesMapsTypeData%Motion_Hub_2_BldRoot) + end if + if (allocated(MeshesMapsTypeData%Load_BldRoot_2_Hub)) then + LB(1:1) = lbound(MeshesMapsTypeData%Load_BldRoot_2_Hub) + UB(1:1) = ubound(MeshesMapsTypeData%Load_BldRoot_2_Hub) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Load_BldRoot_2_Hub(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(MeshesMapsTypeData%Load_BldRoot_2_Hub) + end if + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Load_Hub_2_PRP, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Load_Twr_2_PRP, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Load_Moor_2_PRP, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine WT_PackMeshesMapsType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(MeshesMapsType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackMeshesMapsType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call NWTC_Library_PackMeshMapType(RF, InData%Motion_PRP_2_Twr) + call NWTC_Library_PackMeshMapType(RF, InData%Motion_PRP_2_Hub) + call RegPack(RF, allocated(InData%Motion_Hub_2_BldRoot)) + if (allocated(InData%Motion_Hub_2_BldRoot)) then + call RegPackBounds(RF, 1, lbound(InData%Motion_Hub_2_BldRoot), ubound(InData%Motion_Hub_2_BldRoot)) + LB(1:1) = lbound(InData%Motion_Hub_2_BldRoot) + UB(1:1) = ubound(InData%Motion_Hub_2_BldRoot) + do i1 = LB(1), UB(1) + call NWTC_Library_PackMeshMapType(RF, InData%Motion_Hub_2_BldRoot(i1)) + end do + end if + call RegPack(RF, allocated(InData%Load_BldRoot_2_Hub)) + if (allocated(InData%Load_BldRoot_2_Hub)) then + call RegPackBounds(RF, 1, lbound(InData%Load_BldRoot_2_Hub), ubound(InData%Load_BldRoot_2_Hub)) + LB(1:1) = lbound(InData%Load_BldRoot_2_Hub) + UB(1:1) = ubound(InData%Load_BldRoot_2_Hub) + do i1 = LB(1), UB(1) + call NWTC_Library_PackMeshMapType(RF, InData%Load_BldRoot_2_Hub(i1)) + end do + end if + call NWTC_Library_PackMeshMapType(RF, InData%Load_Hub_2_PRP) + call NWTC_Library_PackMeshMapType(RF, InData%Load_Twr_2_PRP) + call NWTC_Library_PackMeshMapType(RF, InData%Load_Moor_2_PRP) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackMeshesMapsType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(MeshesMapsType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackMeshesMapsType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call NWTC_Library_UnpackMeshMapType(RF, OutData%Motion_PRP_2_Twr) ! Motion_PRP_2_Twr + call NWTC_Library_UnpackMeshMapType(RF, OutData%Motion_PRP_2_Hub) ! Motion_PRP_2_Hub + if (allocated(OutData%Motion_Hub_2_BldRoot)) deallocate(OutData%Motion_Hub_2_BldRoot) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%Motion_Hub_2_BldRoot(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%Motion_Hub_2_BldRoot.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackMeshMapType(RF, OutData%Motion_Hub_2_BldRoot(i1)) ! Motion_Hub_2_BldRoot + end do + end if + if (allocated(OutData%Load_BldRoot_2_Hub)) deallocate(OutData%Load_BldRoot_2_Hub) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%Load_BldRoot_2_Hub(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%Load_BldRoot_2_Hub.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackMeshMapType(RF, OutData%Load_BldRoot_2_Hub(i1)) ! Load_BldRoot_2_Hub + end do + end if + call NWTC_Library_UnpackMeshMapType(RF, OutData%Load_Hub_2_PRP) ! Load_Hub_2_PRP + call NWTC_Library_UnpackMeshMapType(RF, OutData%Load_Twr_2_PRP) ! Load_Twr_2_PRP + call NWTC_Library_UnpackMeshMapType(RF, OutData%Load_Moor_2_PRP) ! Load_Moor_2_PRP +end subroutine + +subroutine WT_CopyStructTmpType(SrcStructTmpTypeData, DstStructTmpTypeData, CtrlCode, ErrStat, ErrMsg) + type(StructTmpType), intent(in) :: SrcStructTmpTypeData + type(StructTmpType), intent(inout) :: DstStructTmpTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'WT_CopyStructTmpType' + ErrStat = ErrID_None + ErrMsg = '' + DstStructTmpTypeData%Azimuth = SrcStructTmpTypeData%Azimuth + DstStructTmpTypeData%RotSpeed = SrcStructTmpTypeData%RotSpeed + DstStructTmpTypeData%BldPitch = SrcStructTmpTypeData%BldPitch + DstStructTmpTypeData%NacYaw = SrcStructTmpTypeData%NacYaw + DstStructTmpTypeData%FrcMom_ADI_at_Ptfm = SrcStructTmpTypeData%FrcMom_ADI_at_Ptfm + DstStructTmpTypeData%FrcMom_MD_at_Ptfm = SrcStructTmpTypeData%FrcMom_MD_at_Ptfm + DstStructTmpTypeData%BuoyPos_c = SrcStructTmpTypeData%BuoyPos_c + DstStructTmpTypeData%PtfmPosAng_c = SrcStructTmpTypeData%PtfmPosAng_c + DstStructTmpTypeData%PtfmVel_c = SrcStructTmpTypeData%PtfmVel_c + DstStructTmpTypeData%PtfmAcc_c = SrcStructTmpTypeData%PtfmAcc_c + DstStructTmpTypeData%NacPos_c = SrcStructTmpTypeData%NacPos_c + DstStructTmpTypeData%NacDCM_c = SrcStructTmpTypeData%NacDCM_c + DstStructTmpTypeData%NacVel_c = SrcStructTmpTypeData%NacVel_c + DstStructTmpTypeData%NacAcc_c = SrcStructTmpTypeData%NacAcc_c + DstStructTmpTypeData%HubPos_c = SrcStructTmpTypeData%HubPos_c + DstStructTmpTypeData%HubDCM_c = SrcStructTmpTypeData%HubDCM_c + DstStructTmpTypeData%HubVel_c = SrcStructTmpTypeData%HubVel_c + DstStructTmpTypeData%HubAcc_c = SrcStructTmpTypeData%HubAcc_c + if (allocated(SrcStructTmpTypeData%BldPos_c)) then + LB(1:1) = lbound(SrcStructTmpTypeData%BldPos_c) + UB(1:1) = ubound(SrcStructTmpTypeData%BldPos_c) + if (.not. allocated(DstStructTmpTypeData%BldPos_c)) then + allocate(DstStructTmpTypeData%BldPos_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstStructTmpTypeData%BldPos_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstStructTmpTypeData%BldPos_c = SrcStructTmpTypeData%BldPos_c + end if + if (allocated(SrcStructTmpTypeData%BldDCM_c)) then + LB(1:1) = lbound(SrcStructTmpTypeData%BldDCM_c) + UB(1:1) = ubound(SrcStructTmpTypeData%BldDCM_c) + if (.not. allocated(DstStructTmpTypeData%BldDCM_c)) then + allocate(DstStructTmpTypeData%BldDCM_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstStructTmpTypeData%BldDCM_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstStructTmpTypeData%BldDCM_c = SrcStructTmpTypeData%BldDCM_c + end if + if (allocated(SrcStructTmpTypeData%BldVel_c)) then + LB(1:1) = lbound(SrcStructTmpTypeData%BldVel_c) + UB(1:1) = ubound(SrcStructTmpTypeData%BldVel_c) + if (.not. allocated(DstStructTmpTypeData%BldVel_c)) then + allocate(DstStructTmpTypeData%BldVel_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstStructTmpTypeData%BldVel_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstStructTmpTypeData%BldVel_c = SrcStructTmpTypeData%BldVel_c + end if + if (allocated(SrcStructTmpTypeData%BldAcc_c)) then + LB(1:1) = lbound(SrcStructTmpTypeData%BldAcc_c) + UB(1:1) = ubound(SrcStructTmpTypeData%BldAcc_c) + if (.not. allocated(DstStructTmpTypeData%BldAcc_c)) then + allocate(DstStructTmpTypeData%BldAcc_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstStructTmpTypeData%BldAcc_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstStructTmpTypeData%BldAcc_c = SrcStructTmpTypeData%BldAcc_c + end if +end subroutine + +subroutine WT_DestroyStructTmpType(StructTmpTypeData, ErrStat, ErrMsg) + type(StructTmpType), intent(inout) :: StructTmpTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyStructTmpType' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(StructTmpTypeData%BldPos_c)) then + deallocate(StructTmpTypeData%BldPos_c) + end if + if (allocated(StructTmpTypeData%BldDCM_c)) then + deallocate(StructTmpTypeData%BldDCM_c) + end if + if (allocated(StructTmpTypeData%BldVel_c)) then + deallocate(StructTmpTypeData%BldVel_c) + end if + if (allocated(StructTmpTypeData%BldAcc_c)) then + deallocate(StructTmpTypeData%BldAcc_c) + end if +end subroutine + +subroutine WT_PackStructTmpType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(StructTmpType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackStructTmpType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%Azimuth) + call RegPack(RF, InData%RotSpeed) + call RegPack(RF, InData%BldPitch) + call RegPack(RF, InData%NacYaw) + call RegPack(RF, InData%FrcMom_ADI_at_Ptfm) + call RegPack(RF, InData%FrcMom_MD_at_Ptfm) + call RegPack(RF, InData%BuoyPos_c) + call RegPack(RF, InData%PtfmPosAng_c) + call RegPack(RF, InData%PtfmVel_c) + call RegPack(RF, InData%PtfmAcc_c) + call RegPack(RF, InData%NacPos_c) + call RegPack(RF, InData%NacDCM_c) + call RegPack(RF, InData%NacVel_c) + call RegPack(RF, InData%NacAcc_c) + call RegPack(RF, InData%HubPos_c) + call RegPack(RF, InData%HubDCM_c) + call RegPack(RF, InData%HubVel_c) + call RegPack(RF, InData%HubAcc_c) + call RegPackAlloc(RF, InData%BldPos_c) + call RegPackAlloc(RF, InData%BldDCM_c) + call RegPackAlloc(RF, InData%BldVel_c) + call RegPackAlloc(RF, InData%BldAcc_c) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackStructTmpType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(StructTmpType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackStructTmpType' + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%Azimuth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotSpeed); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BldPitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacYaw); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%FrcMom_ADI_at_Ptfm); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%FrcMom_MD_at_Ptfm); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BuoyPos_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmPosAng_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmVel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmAcc_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacPos_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacDCM_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacVel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacAcc_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubPos_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubDCM_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubVel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubAcc_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BldPos_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BldDCM_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BldVel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BldAcc_c); if (RegCheckErr(RF, RoutineName)) return +end subroutine +END MODULE WaveTank_Types +!ENDOFREGISTRYGENERATEDFILE diff --git a/glue-codes/labview/src/libwavetanktestinglib.h b/glue-codes/labview/src/libwavetanktestinglib.h new file mode 100644 index 0000000000..00ce5f13a6 --- /dev/null +++ b/glue-codes/labview/src/libwavetanktestinglib.h @@ -0,0 +1,14 @@ +#ifndef WAVETANKTESTING_H +#define WAVETANKTESTING_H + +#ifdef __cplusplus +extern "C" { +#endif + +void WaveTank_NoOp(); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/glue-codes/openfast/CMakeLists.txt b/glue-codes/openfast/CMakeLists.txt index a742b5972a..08fde158b7 100644 --- a/glue-codes/openfast/CMakeLists.txt +++ b/glue-codes/openfast/CMakeLists.txt @@ -40,5 +40,3 @@ if(BUILD_OPENFAST_LIB_DRIVER) install(TARGETS openfast_lib_driver RUNTIME DESTINATION bin) endif() - - diff --git a/glue-codes/python/examples/OpenFAST.py b/glue-codes/python/examples/OpenFAST.py index 6a953d70c8..d7b7e25784 100644 --- a/glue-codes/python/examples/OpenFAST.py +++ b/glue-codes/python/examples/OpenFAST.py @@ -1,4 +1,6 @@ - +# NOTE: this file is not complete, but can serve as a starting point for +# calling OpenFAST through the library interface. Modification will be +# necessary from pyOpenFAST import fast project_root = '/Users/rmudafor/Development/openfast' diff --git a/glue-codes/python/examples/WaveTankDriver.py b/glue-codes/python/examples/WaveTankDriver.py deleted file mode 100644 index 3a8ec07db8..0000000000 --- a/glue-codes/python/examples/WaveTankDriver.py +++ /dev/null @@ -1,198 +0,0 @@ - -from ctypes import ( - CDLL, - POINTER, - create_string_buffer, - byref, - c_byte, - c_int, - c_double, - c_float, - c_char, - c_char_p, - c_bool -) -import numpy as np -from pathlib import Path - -from OpynFAST.interface_abc import OpenFASTInterfaceType - -project_root = '/Users/rmudafor/Development/openfast' -library_path = project_root + '/build/glue-codes/labview/libwavetanktestinglib.dylib' - -class WaveTankLib(OpenFASTInterfaceType): - - def __init__(self, library_path: str, input_file_names: dict): - """ - _summary_ - - Args: - library_path (str): Path to the compile wavetank interface shared library - input_file_names (dict): Map of file names for each included module: - - MD_InputFile - - SS_InputFile - - AD_InputFile - - IfW_InputFile - """ - super().__init__(library_path) - - self.input_file_names = { - k: create_string_buffer(str(Path(v).absolute() ).encode('utf-8')) - for k,v in input_file_names.items() - } - - self._initialize_routines() - - # Create buffers for class data - self.ended = False # For error handling at end - - # This buffer for the channel names and units is set arbitrarily large - # to start. Channel name and unit lengths are currently hard - # coded to 20 (this must match ChanLen in NWTC_Base.f90). - # self._channel_names_c = create_string_buffer(20 * 4000 + 1) - # self._channel_units_c = create_string_buffer(20 * 4000 + 1) - - self.dt = c_double(0) - self.total_time = c_double(0) - self.numTimeSteps = c_int(0) - - def _initialize_routines(self): - self.WaveTank_Init.argtypes = [ - POINTER(c_char), # intent(in ) :: MD_InputFile_c(IntfStrLen) - POINTER(c_char), # intent(in ) :: SS_InputFile_c(IntfStrLen) - POINTER(c_char), # intent(in ) :: AD_InputFile_c(IntfStrLen) - POINTER(c_char), # intent(in ) :: IfW_InputFile_c(IntfStrLen) - POINTER(c_int), # intent(in ) :: IfW_InputFile_c(IntfStrLen) - POINTER(c_int), # intent(in ) :: n_camera_points_c - POINTER(c_char), # intent( out) :: ErrMsg_C(ErrMsgLen_C) - ] - self.WaveTank_Init.restype = c_int - - self.WaveTank_CalcOutput.argtypes = [ - POINTER(c_int), # integer(c_int) :: frame_number - POINTER(c_float), # real(c_float), intent(in ) :: positions_x(N_CAMERA_POINTS) - POINTER(c_float), # real(c_float), intent(in ) :: positions_y(N_CAMERA_POINTS) - POINTER(c_float), # real(c_float), intent(in ) :: positions_z(N_CAMERA_POINTS) - POINTER(c_float), # real(c_float), intent(in ) :: rotation_matrix(9) - POINTER(c_float), # real(c_float), intent( out) :: loads(N_CAMERA_POINTS) - POINTER(c_int), # integer(c_int), intent( out) :: ErrStat_C - POINTER(c_char), # character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) - ] - self.WaveTank_CalcOutput.restype = c_int - - - def init(self, n_camera_points): - _error_status = c_int(0) - _error_message = create_string_buffer(self.ERROR_MSG_C_LEN) - - # Convert the string into a c_char byte array - # input_string = '\x00'.join(input_string_array) - # input_string = input_string.encode('utf-8') - # input_string_length = len(input_string) - - # # Convert the initial positions array into c_float array - # init_positions_c = (c_float * 6)(0.0, ) - # for i, p in enumerate(platform_init_pos): - # init_positions_c[i] = c_float(p) - - # self._numChannels = c_int(0) - - # gravity = c_float(9.80665) - # water_density = c_float(1025) - # water_depth = c_float(200) - # msl2swl = c_float(0) - # outrootname = "./seastate.SeaSt".encode('utf-8') - # wave_kinematics_mode = c_int(0) - # n_steps = c_int(801) - # time_interval = c_float(0.125) - # wave_elevation_series_flag = c_int(0) - self.WaveTank_Init( - self.input_file_names["MoorDyn"], - self.input_file_names["SeaState"], - self.input_file_names["AeroDyn"], - self.input_file_names["InflowWind"], - byref(c_int(n_camera_points)), - # create_string_buffer(outrootname), - # byref(gravity), - # byref(water_density), - # byref(water_depth), - # byref(msl2swl), - # byref(n_steps), - # byref(time_interval), - # byref(wave_elevation_series_flag), - # byref(wave_kinematics_mode), - byref(_error_status), - _error_message, - ) - if self.fatal_error(_error_status): - raise RuntimeError(f"Error {_error_status.value}: {_error_message.value}") - - def calc_output( - self, - frame_number: int, - positions_x: np.ndarray, - positions_y: np.ndarray, - positions_z: np.ndarray, - rotation_matrix: np.ndarray, - loads: np.ndarray, - ): - _error_status = c_int(0) - _error_message = create_string_buffer(self.ERROR_MSG_C_LEN) - - self.WaveTank_CalcOutput( - byref(c_int(frame_number)), - positions_x.ctypes.data_as(POINTER(c_float)), - positions_y.ctypes.data_as(POINTER(c_float)), - positions_z.ctypes.data_as(POINTER(c_float)), - rotation_matrix.ctypes.data_as(POINTER(c_float)), - loads.ctypes.data_as(POINTER(c_float)), - byref(_error_status), - _error_message, - ) - if self.fatal_error(_error_status): - raise RuntimeError(f"Error {_error_status.value}: {_error_message.value}") - - @property - def output_channel_names(self): - if len(self._channel_names.value.split()) == 0: - return [] - output_channel_names = self._channel_names.value.split() - output_channel_names = [n.decode('UTF-8') for n in output_channel_names] - return output_channel_names - - @property - def output_channel_units(self): - if len(self._channel_units.value.split()) == 0: - return [] - output_channel_units = self._channel_units.value.split() - output_channel_units = [n.decode('UTF-8') for n in output_channel_units] - return output_channel_units - - -if __name__=="__main__": - wavetanklib = WaveTankLib( - library_path, - { - "MoorDyn": "/Users/rmudafor/Development/openfast/reg_tests/r-test/modules/moordyn/py_md_5MW_OC4Semi/md_primary.inp", - "SeaState": "/Users/rmudafor/Development/openfast/reg_tests/r-test/modules/seastate/seastate_1/NRELOffshrBsline5MW_OC4DeepCwindSemi_SeaState.dat", - "AeroDyn": "/Users/rmudafor/Development/openfast/reg_tests/r-test/modules/aerodyn/ad_MHK_RM1_Floating/MHK_RM1_Floating_AeroDyn.dat", - "InflowWind": "/Users/rmudafor/Development/openfast/reg_tests/r-test/modules/inflowwind/py_ifw_turbsimff/ifw_primary.inp", - }, - ) - wavetanklib.init(n_camera_points=3) - - positions_x = np.zeros(1, dtype=np.float32) - positions_y = np.zeros(1, dtype=np.float32) - positions_z = np.zeros(1, dtype=np.float32) - rotation_matrix = np.zeros(9, dtype=np.float32) - loads = np.zeros(6, dtype=np.float32) - - for i in range(50): - wavetanklib.calc_output( - frame_number=i, - positions_x=positions_x, - positions_y=positions_y, - positions_z=positions_z, - rotation_matrix=rotation_matrix, - loads=loads, - ) diff --git a/glue-codes/python/pyOpenFAST/fast.py b/glue-codes/python/pyOpenFAST/fast.py index 14fb2271f1..de29fd23e6 100644 --- a/glue-codes/python/pyOpenFAST/fast.py +++ b/glue-codes/python/pyOpenFAST/fast.py @@ -17,7 +17,6 @@ from .interface_abc import OpenFASTInterfaceType -IntfStrLen = 1025 # FAST_Library global NumFixedInputs = 51 # FAST_Library global @@ -127,7 +126,7 @@ def _initialize_routines(self) -> None: def init(self) -> None: _error_status = c_int(0) - _error_message = create_string_buffer(IntfStrLen) + _error_message = create_string_buffer(self.IntfStrLen) self.FAST_AllocateTurbines( byref(self.n_turbines), @@ -172,7 +171,7 @@ def init(self) -> None: def sim(self) -> None: _error_status = c_int(0) - _error_message = create_string_buffer(IntfStrLen) + _error_message = create_string_buffer(self.IntfStrLen) self.FAST_Start( byref(self.i_turb), @@ -213,7 +212,7 @@ def sim(self) -> None: def deinit(self) -> None: _error_status = c_int(0) - _error_message = create_string_buffer(IntfStrLen) + _error_message = create_string_buffer(self.IntfStrLen) if not self.ended: self.ended = True @@ -265,7 +264,7 @@ def total_output_steps(self) -> int: def get_hub_position(self) -> Tuple: _error_status = c_int(0) - _error_message = create_string_buffer(IntfStrLen) + _error_message = create_string_buffer(self.IntfStrLen) # Data buffers absolute_position = (c_float * 3)(0.0, ) diff --git a/glue-codes/python/pyOpenFAST/interface_abc.py b/glue-codes/python/pyOpenFAST/interface_abc.py index d3bf9d737e..5b9c3a5f22 100644 --- a/glue-codes/python/pyOpenFAST/interface_abc.py +++ b/glue-codes/python/pyOpenFAST/interface_abc.py @@ -26,6 +26,9 @@ class OpenFASTInterfaceType(CDLL): 4: "Fatal Error" } + # NWTC Library sets the length of file names passed through the interfaces + IntfStrLen = 1025 + # 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 diff --git a/glue-codes/python/pyOpenFAST/seastate.py b/glue-codes/python/pyOpenFAST/seastate.py index 4ae6ef0420..1f41facc03 100644 --- a/glue-codes/python/pyOpenFAST/seastate.py +++ b/glue-codes/python/pyOpenFAST/seastate.py @@ -87,6 +87,8 @@ def __init__(self, library_path): # request information from # non-CalcOutput routines. + self.WaveTimeShift = 0 # shift wave time (positive only) + self.numChannels = 0 # Number of channels returned # flags @@ -115,10 +117,11 @@ def _initialize_routines(self): self.SeaSt_C_PreInit.restype = None self.SeaSt_C_Init.argtypes = [ - POINTER(c_char_p), # intent(in ) :: InputFile_c(IntfStrLen) - POINTER(c_char_p), # intent(in ) :: OutRootName_c(IntfStrLen) - POINTER(c_int), # intent(in ) :: NSteps_c - POINTER(c_float), # intent(in ) :: TimeInterval_c + POINTER(c_char), # intent(in ) :: InputFile_c(IntfStrLen) + POINTER(c_char), # intent(in ) :: OutRootName_c(IntfStrLen) + POINTER(c_double), # intent(in ) :: TimeInterval_c + POINTER(c_double), # intent(in ) :: TMax_c + POINTER(c_double), # intent(in ) :: WaveTimeShift (positive only) POINTER(c_int), # intent( out) :: NumChannels_c POINTER(c_char), # intent( out) :: OutputChannelNames_C POINTER(c_char), # intent( out) :: OutputChannelUnits_C @@ -258,10 +261,17 @@ def seastate_init( self, primary_ss_file, outrootname: str = "./seastate.SeaSt", - n_steps: int = 801, + time_max: float = 60, time_interval: float = 0.125, ): + ss_file_c = create_string_buffer( + primary_ss_file.ljust(self.default_str_c_len).encode('utf-8') + ) + outrootname_c = create_string_buffer( + outrootname.ljust(self.default_str_c_len).encode('utf-8') + ) + # This buffer for the channel names and units is set arbitrarily large # to start. Channel name and unit lengths are currently hard # coded to 20 (this must match ChanLen in NWTC_Base.f90). @@ -271,10 +281,11 @@ def seastate_init( self.SeaSt_C_Init( - c_char_p(primary_ss_file.encode('utf-8')), #FIXME: this might overrun the buffer!!!!! - c_char_p(outrootname.encode('utf-8')), - byref(c_int(n_steps)), - byref(c_float(time_interval)), + ss_file_c, + outrootname_c, + byref(c_double(time_interval)), + byref(c_double(time_max)), + byref(c_double(self.WaveTimeShift)), byref(self._numChannels), _channel_names, _channel_units, @@ -389,16 +400,18 @@ def get_fluidVelAcc(self, pos[2] = position[2] vel = np.zeros( 3, dtype=c_float ) acc = np.zeros( 3, dtype=c_float ) + nodeInWater_c = c_int(0) self.SeaSt_C_GetFluidVelAcc( byref(c_double(time)), # IN -> current simulation time pos.ctypes.data_as(POINTER(c_float)), # IN -> position (3 vector) vel.ctypes.data_as(POINTER(c_float)), # OUT <- velocity (3 vector) acc.ctypes.data_as(POINTER(c_float)), # OUT <- acceleration (3 vector) - nodeInWater.ctypes.data_as(POINTER(c_int)), # OUT <- node is in water (0=false, 1=true) + nodeInWater_c, # OUT <- node is in water (0=false, 1=true) byref(self.error_status_c), # OUT <- error status self.error_message_c # OUT <- error message ) self.check_error() + nodeInWater = nodeInWater_c.value return vel,acc,nodeInWater diff --git a/glue-codes/python/pyOpenFAST/tdmslib.py b/glue-codes/python/pyOpenFAST/tdmslib.py new file mode 100644 index 0000000000..b7f6d6a063 --- /dev/null +++ b/glue-codes/python/pyOpenFAST/tdmslib.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Jun 5 11:26:52 2024 + +@author: schamot + +Description: + Code takes a .tdms file path as input and outputs the data as a dictionary. + + Parameters + ---------- + path : str + path to the .tdms file. + + Returns + ------- + pyDict : dict + Python dictionary of the tdms file. + +""" + +import nptdms + +def main(): + fileName = "Oscilloscope Data/UF HASEL tests/UF2down_3kVcycleForce.tdms" + output = TdmsToDict(fileName) + print(output) + +def TdmsToDict(path): + ''' + Code takes a .tdms file path as input and outputs the data as a dictionary. + + Parameters + ---------- + path : str + path to the .tdms file. + + Returns + ------- + pyDict : dict + Python dictionary of the tdms file. + + ''' + # Get the files requested + tdmsFile = path + + # open/read the tdms file + tdms_file = nptdms.TdmsFile.read(tdmsFile) + + # Set up python dictionary + pyDict = {} + # Get group names + for group in tdms_file.groups(): + #print(f'''Group: {group.name}''') + # Create group dictionary key + pyDict[group.name] = {} + # Get channel names per group + for channel in group.channels(): + #print(f'''\tChannel: {channel.name}''') + pyDict[group.name][channel.name] = channel[:] + # if the data has properties add them + if channel.properties != {}: + pyDict[group.name][channel.name + "_properties"] = channel.properties + + return pyDict + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/glue-codes/python/pyOpenFAST/wavetanktesting.py b/glue-codes/python/pyOpenFAST/wavetanktesting.py new file mode 100644 index 0000000000..625d25c732 --- /dev/null +++ b/glue-codes/python/pyOpenFAST/wavetanktesting.py @@ -0,0 +1,445 @@ +# 2025.12.23 +# This is a work in progress. It is used for testing of the +# wavetanktestinglib that can be coupled to labview. It is not complete at +# this point +from ctypes import ( + CDLL, + POINTER, + create_string_buffer, + byref, + c_byte, + c_int, + c_double, + c_float, + c_char, + c_char_p, + c_bool +) + +import numpy as np +import numpy.typing as npt +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from typing import Any,Dict, List, Optional, Tuple, Union + +from pyOpenFAST.interface_abc import OpenFASTInterfaceType + +def to_c_array(array: npt.NDArray, c_type: Any = c_float) -> Any: + """Converts numpy array to C array of specified type. + + Args: + array: Input numpy array + c_type: C type to convert to (default: c_float) + + Returns: + C-compatible array of the specified type + """ + try: + if isinstance(array, np.ndarray): + flat_array = array.flatten() + return (c_type * len(flat_array))(*flat_array) + # If list/tuple, convert directly to C array + return (c_type * len(array))(*array) + except Exception as e: + raise TypeError(f"Failed to convert to C array: {e}") + +def to_c_string(input_array: List[str]) -> Tuple[bytes, int]: + """Converts input string array into a null-separated byte string for use in C. + + Args: + input_array: List of strings to join with null characters + + Returns: + Tuple containing: + - The encoded byte string + - Length of the encoded string + """ + encoded_string = '\x00'.join(input_array).encode('utf-8') + return encoded_string, len(encoded_string) + + +@dataclass +class MotionData: + """POD-style container for motion-related data i.e. state of a node.""" + pos: npt.NDArray[np.float32] # [x,y,z,roll,pitch,yaw] + vel: npt.NDArray[np.float32] # [x_dot,y_dot,z_dot,roll_dot,pitch_dot,yaw_dot] + acc: npt.NDArray[np.float32] # [x_ddot,y_ddot,z_ddot,roll_ddot,pitch_ddot,yaw_ddot] + +@dataclass +class LoadsData: + """POD-style container for motion-related data i.e. state of a node.""" + loads: npt.NDArray[np.float32] # [Fx,Fy,Fz,Mx,My,Mz] + +#------------------------------------------------------------------------------- +# Generate a debug file +#------------------------------------------------------------------------------- +class DriverDbg: + """ + A helper class for debugging the wavetankinterface. This class writes out all the + input positions/orientations, velocities, accelerations, and the resulting + forces and moments at the platform mesh point. If functioning correctly, this + will be identical to the corresponding values in the wavetank output + channels. + + NOTE: This may not output everything in the interface as updates have been made + since writing this, but this routine was not updated accordingly. + """ + + def __init__(self, filename: str) -> None: + """Initializes the debugging class and open the output file.""" + self.filename = filename + self.opened = True + + with open(filename, 'wt') as self.debug_file: + self._write_header() + + self.debug_file = open(filename, 'at') # switch to append mode + + def _write_header(self) -> None: + """Writes the header information to the debug file.""" + # Build header components + timestamp = datetime.now().strftime('%b-%d-%Y %H:%M:%S') + header_lines = [ + f"## This file was generated by wavetank_c_lib on {timestamp}", + f"## This file contains the resulting forces/moments at the referenc mesh point passed into the adi_c_lib", + "#", + "#", + "#", + "#" + ] + + # Write column headers + column_names = ["Time"] + column_units = ["(s)"] + # Position columns + for suffix in ["x", "y", "z"]: + column_names.append(f"{suffix}") + column_units.append("(m)") + # orientation columns + for suffix in ["phi", "theta", "psi"]: + column_names.append(f"{suffix}") + column_units.append("(rad)") + # Velocity columns + for suffix in ["Vx", "Vy", "Vz"]: + column_names.append(f"{suffix}") + column_units.append("(m/s)") + # Angular velocity columns + for suffix in ["RVx", "RVy", "RVz"]: + column_names.append(f"{suffix}") + column_units.append("(rad/s)") + # Acceleration columns + for suffix in ["Ax", "Ay", "Az"]: + column_names.append(f"{suffix}") + column_units.append("(m/s^2)") + # Angular acceleration columns + for suffix in ["RAx", "RAy", "RAz"]: + column_names.append(f"{suffix}") + column_units.append("(rad/s^2)") + # Force columns + for suffix in ["Fx", "Fy", "Fz"]: + column_names.append(f"{suffix}") + column_units.append("(N)") + # Moment columns + for suffix in ["Mx", "My", "Mz"]: + column_names.append(f"{suffix}") + column_units.append("(N-m)") + + f_string = "{:^25s}" + header_lines.append("".join([f_string.format(name) for name in column_names])) + header_lines.append("".join([f_string.format(unit) for name, unit in zip(column_names, column_units)])) + + self.debug_file.write("\n".join(header_lines) + "\n") + + def write( + self, + t: float, + body_motion: MotionData, + body_loads: LoadsData, + ) -> None: + """Writes the current state to the debug file.""" + row_data = [f"{t:10.4f}"] + + row_data.extend([f"{val:25.7e}" for val in body_motion.pos[:]]) + row_data.extend([f"{val:25.7e}" for val in body_motion.vel[:]]) + row_data.extend([f"{val:25.7e}" for val in body_motion.acc[:]]) + row_data.extend([f"{val:25.7e}" for val in body_loads.loads[:]]) + + self.debug_file.write("".join(row_data) + "\n") + self.debug_file.flush() + + def end(self) -> None: + """Closes the debug file.""" + if self.opened: + self.debug_file.close() + self.opened = False + + + +class WaveTankLib(OpenFASTInterfaceType): + + #-------------------------------------- + # Error levels + #-------------------------------------- + error_levels: Dict[int, str] = { + 0: "None", + 1: "Info", + 2: "Warning", + 3: "Severe Error", + 4: "Fatal Error" + } + + # Debug output file: When coupled into another code, an array of position/orientation, + # velocities, and accelerations are passed in, and an array of Forces + Moments is + # returned. For debugging, it may be useful to dump all off this to a file. + debug_output_file: str = "DbgOutputs.out" + debug_outputs: int = 1 # For checking the interface, set this to 1 + + #-------------------------------------- + # 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 + + def __init__(self, library_path: str): + """ + + Args: + library_path (str): Path to the compile wavetank interface shared library + input_file_names (dict): Map of file names for each included module: + - WT_InputFile + """ + super().__init__(library_path) + + self._initialize_routines() + + self.ended = False # For error handling at end + self.print_error_level = 1 + + + # 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) + + # returned values + self.rootname_c = create_string_buffer(self.IntfStrLen) + self.vtkdir_c = create_string_buffer(self.IntfStrLen) + self.buoyWaveElev_c = c_float(0) # wave elevation at buoy + + def _initialize_routines(self): + self.WaveTank_Init.argtypes = [ + POINTER(c_char), # intent(in ) :: WT_InputFile_c(IntfStrLen) + POINTER(c_char), # intent( out) :: RootName_C(IntfStrLen) + POINTER(c_char), # intent( out) :: VTKdir_C(IntfStrLen) + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char), # intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.WaveTank_Init.restype = None + + self.WaveTank_CalcStep.argtypes = [ + POINTER(c_double), # real(c_double) :: time + POINTER(c_float), # intent(in ) :: pos(6) + POINTER(c_float), # intent(in ) :: vel(6) + POINTER(c_float), # intent(in ) :: acc(6) + POINTER(c_float), # intent( out) :: FrcMom(6) + POINTER(c_float), # intent( out) :: buoyWaveElev + POINTER(c_int), # integer(c_int), intent( out) :: ErrStat_C + POINTER(c_char), # character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.WaveTank_CalcStep.restype = None + + self.WaveTank_End.argtypes = [ + POINTER(c_int), # integer(c_int), intent( out) :: ErrStat_C + POINTER(c_char), # character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.WaveTank_End.restype = c_int + + self.WaveTank_SetWaveFieldPointer.argtypes = [ + POINTER(c_int), # integer(c_int), intent( out) :: ErrStat_C + POINTER(c_char), # character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.WaveTank_SetWaveFieldPointer.restype = None + + + def check_error(self) -> None: + """Checks for and handles any errors from the Fortran library. + + Raises: + RuntimeError: If a fatal error occurs in the Fortran code + """ + # If the error status is 0, return + if self.error_status_c.value == 0: + return + + # Get the error level and error message + error_level = self.error_levels.get( + self.error_status_c.value, + f"Unknown Error Level: {self.error_status_c.value}" + ) + error_msg = self.error_message_c.raw.decode('utf-8').strip() + message = f"WaveTank library {error_level}: {error_msg}" + # If the error level is fatal, call WaveTank_End() and raise an error + if self.error_status_c.value >= self.abort_error_level: + try: + self.WaveTank_End( + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + if self.error_status_c.value == 4: + error_msg = self.error_message_c.raw.decode('utf-8').strip() + print(f'WaveTank_End error: {error_msg}') + except Exception as e: + message += f"\nAdditional error during cleanup: {e}" + raise RuntimeError(message) + else: + print(message) + + + def _validate_loads_data( + self, + loads: LoadsData, + name: str, + ) -> None: + """Validates motion data dimensions. + + Args: + motion: Motion data to validate + name: Name of the component for error messages + + Raises: + ValueError: If dimensions are incorrect + """ + expected_shape = 6 + + if loads.loads.shape[0] != expected_shape: + raise ValueError( + f"{name} loads must have shape {expected_shape}, " + f"got {loads.loads.shape}" + ) + + + def _validate_motion_data( + self, + motion: MotionData, + name: str, + ) -> None: + """Validates motion data dimensions. + + Args: + motion: Motion data to validate + name: Name of the component for error messages + + Raises: + ValueError: If dimensions are incorrect + """ + expected_shape = 6 + + if motion.pos.shape[0] != expected_shape: + raise ValueError( + f"{name} position must have shape {expected_shape}, " + f"got {motion.pos.shape}" + ) + + if motion.vel.shape[0] != expected_shape: + raise ValueError( + f"{name} velocity must have shape {expected_shape}, " + f"got {motion.vel.shape}" + ) + + if motion.acc.shape[0] != expected_shape: + raise ValueError( + f"{name} acceleration must have shape {expected_shape}, " + f"got {motion.acc.shape}" + ) + + def _prepare_motion_arrays( + self, + body: MotionData, + ) -> Dict[str, Any]: + """Prepares C-compatible arrays for motion data. + + Args: + body: body motion data + + Returns: + Dictionary containing all prepared C arrays + """ + return { + # body data + 'body_pos_c': to_c_array(body.pos, c_float), + 'body_vel_c': to_c_array(body.vel, c_float), + 'body_acc_c': to_c_array(body.acc, c_float), + } + + + + def init(self, input_file_names: dict): + _error_message = create_string_buffer(self.ERROR_MSG_C_LEN) + + # Create C-compatible string buffers for input file names + self.input_file_names = { + k: create_string_buffer(str(Path(v).absolute()).encode('utf-8'), self.IntfStrLen) + for k,v in input_file_names.items() + } + + # # Convert the initial positions array into c_float array + # init_positions_c = (c_float * 6)(0.0, ) + # for i, p in enumerate(platform_init_pos): + # init_positions_c[i] = c_float(p) + + self.WaveTank_Init( + self.input_file_names["WaveTankConfig"], + self.rootname_c, # OUT <- rootname of output files + self.vtkdir_c, # OUT <- directory for vtk output + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + self.check_error() + # tmp = self.rootname_c.raw.decode('utf-8').strip() + # print(f'RootName_c: {tmp}') + # tmp = self.vtkdir_c.raw.decode('utf-8').strip() + # print(f'VTKdir_c: {tmp}') + + def calc_step( + self, + time: float, + body_motion: MotionData, + body_loads: LoadsData, + ): + self._validate_motion_data(body_motion, "body") + self._validate_loads_data(body_loads, "body") + + # loads storage + #tmp_loads_c=np.array([0, 0, 0, 0, 0, 0], dtype=c_float) + tmp_loads_c = (c_float * (6))(0.) + + # Convert data to C arrays + motion_arrays = self._prepare_motion_arrays(body_motion) + + self.WaveTank_CalcStep( + byref(c_double(time)), + motion_arrays['body_pos_c'], # IN -> body pos [x,y,z,roll,pitch,yaw] + motion_arrays['body_vel_c'], # IN -> body vel [TVx, TVy, TVz, RVx, RVy, RVz] + motion_arrays['body_acc_c'], # IN -> body acc [TAx, TAy, TAz, RAx, RAy, RAz] + tmp_loads_c, # OUT <- body forces and moments [Fx,Fy,Fz,Mx,My,Mz] + byref(self.buoyWaveElev_c), # OUT <- buoy wave elevation + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + self.check_error() + + # Copy over loads + body_loads.loads = np.ctypeslib.as_array(tmp_loads_c).astype(np.float32).copy() + #print(f"body_loads.loads {body_loads.loads}") + + + def end(self) -> None: + self.WaveTank_End( + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + self.check_error() diff --git a/glue-codes/python/pyproject.toml b/glue-codes/python/pyproject.toml index 8c98abaef0..5d43e8b265 100644 --- a/glue-codes/python/pyproject.toml +++ b/glue-codes/python/pyproject.toml @@ -10,6 +10,7 @@ readme = "README.md" requires-python = ">=3.9" authors = [ { name = "Rafael Mudafort", email = "Rafael.Mudafort@nrel.gov" }, + { name = "Andy Platt", email = "Andy.Platt@nrel.gov" }, ] license = { file = "LICENSE.txt" } keywords = ["openfast"] diff --git a/modules/aerodyn/CMakeLists.txt b/modules/aerodyn/CMakeLists.txt index 89863f109e..38a204b0c6 100644 --- a/modules/aerodyn/CMakeLists.txt +++ b/modules/aerodyn/CMakeLists.txt @@ -98,17 +98,36 @@ add_executable(unsteadyaero_driver ) target_link_libraries(unsteadyaero_driver basicaerolib lindynlib versioninfolib) + # AeroDyn-InflowWind c-bindings interface library -add_library(aerodyn_inflow_c_binding SHARED +# create object instead of directly linking into shared and static -- causes issues in parallel builds +# This is only required because we are static linking the library for wavetank +# NOTE: target linking at the object, static, and shared libraries. Different CMake versions handle this +# slightly differently with unpredictable results if I don't. +add_library(aerodyn_inflow_c_binding_object OBJECT src/AeroDyn_Inflow_C_Binding_Types.f90 src/AeroDyn_Inflow_C_Binding.f90 ) -target_link_libraries(aerodyn_inflow_c_binding aerodyn_driver_subs versioninfolib) +target_link_libraries(aerodyn_inflow_c_binding_object adilib aerodyn_driver_subs nwtclibs versioninfolib) +set_property(TARGET aerodyn_inflow_c_binding_object PROPERTY POSITION_INDEPENDENT_CODE 1) # required for shared libs + +# shared +add_library(aerodyn_inflow_c_binding SHARED $) +target_link_libraries(aerodyn_inflow_c_binding adilib aerodyn_driver_subs nwtclibs versioninfolib) if(APPLE OR UNIX) target_compile_definitions(aerodyn_inflow_c_binding PRIVATE IMPLICIT_DLLEXPORT) endif() -install(TARGETS aerodynlib basicaerolib aerodyn_driver_subs aerodyn_driver unsteadyaero_driver aerodyn_inflow_c_binding adilib +# C-bindings non-shared interface +# This is a workaround for building wavetank into a single DLL (also allows setting CU globaly for sending screen to file for labview integration) +add_library(aerodyn_inflow_c_bind_static STATIC $) +target_link_libraries(aerodyn_inflow_c_bind_static adilib aerodyn_driver_subs nwtclibs versioninfolib) +if(APPLE OR UNIX) + target_compile_definitions(aerodyn_inflow_c_bind_static PRIVATE IMPLICIT_DLLEXPORT) +endif() + + +install(TARGETS aerodynlib basicaerolib aerodyn_driver_subs aerodyn_driver unsteadyaero_driver aerodyn_inflow_c_binding adilib aerodyn_inflow_c_bind_static EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/modules/aerodyn/src/AeroDyn_Inflow.f90 b/modules/aerodyn/src/AeroDyn_Inflow.f90 index bc3ec06a8e..fc3363757a 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow.f90 @@ -387,6 +387,8 @@ subroutine ADI_InitInflowWind(Root, i_IW, u_AD, o_AD, IW, dt, InitOutData, errSt endif InitInData%RootName = trim(Root)//'.IfW' InitInData%MHK = i_IW%MHK + InitInData%WtrDpth = i_IW%WtrDpth + InitInData%MSL2SWL = i_IW%MSL2SWL ! OLAF might be used in AD, in which case we need to allow out of bounds for some calcs. To do that ! the average values for the entire wind profile must be calculated and stored (we don't know if OLAF ! is used until after AD_Init below). diff --git a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 index 0047035c3c..4be71dd8f9 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 @@ -34,7 +34,6 @@ MODULE AeroDyn_Inflow_C_BINDING SAVE PUBLIC :: ADI_C_Init - !PUBLIC :: ADI_C_ReInit PUBLIC :: ADI_C_CalcOutput PUBLIC :: ADI_C_UpdateStates PUBLIC :: ADI_C_End @@ -126,7 +125,8 @@ 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) :: n_VTK ! VTK timestep + integer(IntKi) :: VTKn_Global ! global timestep for VTK + integer(IntKi) :: VTKn_last ! last global timestep for VTK 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) @@ -317,7 +317,9 @@ subroutine ADI_C_PreInit( & ! Set whether these are MHK turbines InitInp%AD%MHK = MHK_in InitInp%IW_InitInp%MHK = MHK_in - InitInp%IW_InitInp%OutputAccel = MHK_in /= MHK_None + InitInp%IW_InitInp%OutputAccel = MHK_in /= MHK_None + InitInp%IW_InitInp%WtrDpth = REAL(WtrDpth_in, ReKi) + InitInp%IW_InitInp%MSL2SWL = REAL(MSL2SWL_in, ReKi) ! Offset the origin to account for water depth for MHK turbines do iWT=1,Sim%NumTurbines @@ -495,16 +497,16 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString 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 @@ -554,7 +556,8 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString Sim%root = trim(OutRootName) ! Timekeeping n_Global = 0_IntKi ! Assume we are on timestep 0 at start - n_VTK = -1_IntKi ! counter advance just before writing + VTKn_Global = 0_IntKi + VTKn_last = -1_IntKi ! Interpolation order InterpOrder = int(InterpOrder_C, IntKi) @@ -566,7 +569,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString WrOutputsData%VTKHubrad = real(VTKHubrad_in, SiKi) WrOutputsData%VTKRefPoint = (/ 0.0_ReKi, 0.0_ReKi, 0.0_ReKi /) !TODO: should this be an input? WrOutputsData%root = trim(OutRootName) - WrOutputsData%n_VTKTime = 1 ! output every timestep ! Write outputs to file WrOutputsData%fileFmt = int(wrOuts_C, IntKi) @@ -856,6 +858,7 @@ end subroutine SetupFileOutputs subroutine ShowPassedData() character(1) :: TmpFlag integer :: i,j + character(IntfStrLen) :: tmpPath call WrSCr("") call WrScr("-----------------------------------------------------------") call WrScr("Interface debugging: Variables passed in through interface") @@ -866,10 +869,18 @@ subroutine ShowPassedData() call WrScr(" ADinputFilePassed_C "//TmpFlag ) call WrScr(" ADinputFileString_C (ptr addr) "//trim(Num2LStr(LOC(ADinputFileString_C))) ) call WrScr(" ADinputFileStringLength_C "//trim(Num2LStr( ADinputFileStringLength_C )) ) + if (ADinputFilePassed==0_c_int) then + i = index(ADinputFileString, char(0)) ! skip anything after c_null_char + call WrScr(" ADinputFileString_C "//ADinputFileString(1:i)) + endif TmpFlag="F"; if (IfWinputFilePassed==1_c_int) TmpFlag="T" call WrScr(" IfWinputFilePassed_C "//TmpFlag ) call WrScr(" IfWinputFileString_C (ptr addr)"//trim(Num2LStr(LOC(IfWinputFileString_C))) ) call WrScr(" IfWinputFileStringLength_C "//trim(Num2LStr( IfWinputFileStringLength_C )) ) + if (IfWinputFilePassed==0_c_int) then + i = index(IfWinputFileString, char(0)) ! skip anything after c_null_char + call WrScr(" IfWinputFileString_C "//trim(IfWinputFileString(1:i))) + endif call WrScr(" OutRootName "//trim(OutRootName) ) call WrScr(" OutVTKDir "//trim(OutVTKDir) ) call WrScr(" Interpolation") @@ -1045,13 +1056,12 @@ SUBROUTINE ADI_C_CalcOutput(Time_C, & !------------------------------------------------------- ! Write VTK if requested (animation=2) if (WrOutputsData%WrVTK > 1_IntKi) then - ! Check if writing this step (note this may overwrite if we rerun a step in a correction loop) - if ( mod( n_Global, WrOutputsData%n_VTKTime ) == 0 ) THEN - ! increment the current VTK output number if not a correction step, otherwise overwrite previous - if (.not. EqualRealNos( real(Time,DbKi), InputTimePrev_Calc ) ) then - n_VTK = n_VTK + 1_IntKi ! Increment for this write - endif + ! only write on desired time interval (same logic used in c-binding modules) + VTKn_Global = nint(Time_C / WrOutputsData%VTK_dt ) + if (VTKn_Global /= VTKn_last) then ! already wrote this one + VTKn_last = VTKn_Global ! store the current number to make sure we don't write it twice call WrVTK_Meshes(ADI%u(1)%AD%rotors(:),(/0.0_SiKi,0.0_SiKi,0.0_SiKi/),ErrStat_F2,ErrMsg_F2) + if (Failed()) return endif endif @@ -1285,19 +1295,7 @@ SUBROUTINE ADI_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='ADI_C_End') if (allocated(ADI%u)) deallocate(ADI%u) endif - ! Destroy any other copies of states (rerun on (STATE_CURR) is ok) - call ADI_DestroyContState( ADI%x( STATE_LAST), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyContState( ADI%x( STATE_CURR), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyContState( ADI%x( STATE_PRED), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyDiscState( ADI%xd( STATE_LAST), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyDiscState( ADI%xd( STATE_CURR), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyDiscState( ADI%xd( STATE_PRED), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyConstrState( ADI%z( STATE_LAST), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyConstrState( ADI%z( STATE_CURR), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyConstrState( ADI%z( STATE_PRED), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyOtherState( ADI%OtherState(STATE_LAST), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyOtherState( ADI%OtherState(STATE_CURR), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyOtherState( ADI%OtherState(STATE_PRED), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) + call ADI_DestroyData(ADI, ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) ! if deallocate other items now !if (allocated(InputTimes)) deallocate(InputTimes) @@ -2369,7 +2367,7 @@ subroutine WrVTK_Points(ErrStat3,ErrMsg3) ! Blade point motion (structural mesh from driver) do iBlade=1,Sim%WT(iWT)%NumBlades - call MeshWrVTK(RefPoint, BldStrMotionMesh(iWT)%BldMesh(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BldStrMotionMesh'//trim(Num2LStr(iBlade)), n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + call MeshWrVTK(RefPoint, BldStrMotionMesh(iWT)%BldMesh(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BldStrMotionMesh'//trim(Num2LStr(iBlade)), VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return enddo @@ -2377,20 +2375,20 @@ subroutine WrVTK_Points(ErrStat3,ErrMsg3) if (allocated(rot_u(iWT)%BladeRootMotion)) then do iBlade=1,Sim%WT(iWT)%NumBlades if (rot_u(iWT)%BladeRootMotion(iBlade)%Committed) then - call MeshWrVTK(RefPoint, rot_u(iWT)%BladeRootMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BladeRootMotion'//trim(num2lstr(iBlade)), n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + call MeshWrVTK(RefPoint, rot_u(iWT)%BladeRootMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BladeRootMotion'//trim(num2lstr(iBlade)), VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return endif enddo endif ! Nacelle (structural point input - if ( rot_u(iWT)%NacelleMotion%Committed ) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleMotion', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if ( rot_u(iWT)%NacelleMotion%Committed ) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleMotion', VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Free wake if (allocated(ADI%m%AD%FVW_u) .and. iWT==1) then if (allocated(ADI%m%AD%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(STATE_CURR)%AD%FVW, ADI%z(STATE_CURR)%AD%FVW, ADI%m%AD%FVW, trim(WrOutputsData%VTK_OutFileRoot)//'.FVW', n_VTK, WrOutputsData%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords + call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(STATE_CURR)%AD%FVW, ADI%z(STATE_CURR)%AD%FVW, ADI%m%AD%FVW, trim(WrOutputsData%VTK_OutFileRoot)//'.FVW', VTKn_Global, WrOutputsData%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords endif end if end subroutine WrVTK_Points @@ -2406,11 +2404,11 @@ subroutine WrVTK_Surfaces(ErrStat3,ErrMsg3) ErrMsg3 = '' ! TODO: use this routine when it is moved out of the driver and into ADI - ! call AD_WrVTK_Surfaces(ADI%u(1)%AD, ADI%y%AD, RefPoint, ADI%m%VTK_Surfaces, n_VTK, WrOutputsData%Root, WrOutputsData%VTK_tWidth, 25, WrOutputsData%VTKHubRad) + ! call AD_WrVTK_Surfaces(ADI%u(1)%AD, ADI%y%AD, RefPoint, ADI%m%VTK_Surfaces, VTKn_Global, WrOutputsData%Root, WrOutputsData%VTK_tWidth, 25, WrOutputsData%VTKHubRad) ! Nacelle if ( rot_u(iWT)%NacelleMotion%Committed ) then - call MeshWrVTK_PointSurface (RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleSurface', n_VTK, & + call MeshWrVTK_PointSurface (RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleSurface', VTKn_Global, & OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, verts=WrOutputsData%VTK_Surface(iWT)%NacelleBox) if (ErrStat3 >= AbortErrLev) return endif @@ -2418,14 +2416,14 @@ subroutine WrVTK_Surfaces(ErrStat3,ErrMsg3) ! Tower if (rot_u(iWT)%TowerMotion%Committed) then call MeshWrVTK_Ln2Surface (RefPoint, rot_u(iWT)%TowerMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.TowerSurface', & - n_VTK, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, numSectors, ADI%m%VTK_Surfaces(iWT)%TowerRad ) + VTKn_Global, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, numSectors, ADI%m%VTK_Surfaces(iWT)%TowerRad ) if (ErrStat3 >= AbortErrLev) return endif ! Hub if (rot_u(iWT)%HubMotion%Committed) then call MeshWrVTK_PointSurface (RefPoint, rot_u(iWT)%HubMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.HubSurface', & - n_VTK, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, & + VTKn_Global, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, & NumSegments=numSectors, radius=WrOutputsData%VTKHubRad) if (ErrStat3 >= AbortErrLev) return endif @@ -2435,7 +2433,7 @@ subroutine WrVTK_Surfaces(ErrStat3,ErrMsg3) do iBlade=1,Sim%WT(iWT)%NumBlades if (rot_u(iWT)%BladeMotion(iBlade)%Committed) then call MeshWrVTK_Ln2Surface (RefPoint, rot_u(iWT)%BladeMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Blade'//trim(num2lstr(iBlade))//'Surface', & - n_VTK, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth , verts=ADI%m%VTK_Surfaces(iWT)%BladeShape(iBlade)%AirfoilCoords, & + VTKn_Global, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth , verts=ADI%m%VTK_Surfaces(iWT)%BladeShape(iBlade)%AirfoilCoords, & Sib=ADI%y%AD%rotors(iWT)%BladeLoad(iBlade) ) if (ErrStat3 >= AbortErrLev) return endif @@ -2451,22 +2449,22 @@ subroutine WrVTK_Lines(ErrStat3,ErrMsg3) ErrMsg3 = '' ! Tower - if (rot_u(iWT)%TowerMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%TowerMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Tower', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if (rot_u(iWT)%TowerMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%TowerMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Tower', VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Nacelle meshes - if (rot_u(iWT)%NacelleMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Nacelle', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if (rot_u(iWT)%NacelleMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Nacelle', VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Hub - if (rot_u(iWT)%HubMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%HubMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Hub', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if (rot_u(iWT)%HubMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%HubMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Hub', VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Blades if (allocated(rot_u(iWT)%BladeMotion)) then do iBlade=1,Sim%WT(iWT)%NumBlades if (rot_u(iWT)%BladeMotion(iBlade)%Committed) then - call MeshWrVTK(RefPoint, rot_u(iWT)%BladeMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Blade'//trim(num2lstr(iBlade)), n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + call MeshWrVTK(RefPoint, rot_u(iWT)%BladeMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Blade'//trim(num2lstr(iBlade)), VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return endif enddo diff --git a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding_Registry.txt b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding_Registry.txt index 11c880895c..81d602087d 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding_Registry.txt @@ -11,8 +11,7 @@ ################################################################################################################################### # ...... Include files (definitions from NWTC Library) ............................................................................ include Registry_NWTC_Library.txt -# -# ..... Table of combined cases to run ....................................................................................................... +# ADI c-bind types param AeroDyn_Inflow_C_Binding/ADI_cbind - IntKi NumPtsDiskAvg - 144 - "Number of points for disk average velocity calculations" - typedef ^ DiskAvgVelData ReKi DiskWindPosRel {3}{NumPtsDiskAvg} - - "Position points for disk average sampling, relative to hub" - typedef ^ ^ ReKi DiskWindPosAbs {3}{NumPtsDiskAvg} - - "Position points for disk average sampling, absolute/global/interial" - diff --git a/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt b/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt index 7a9485a152..eb44d2e5e0 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt @@ -37,6 +37,8 @@ typedef ^ ^ ReKi HWindSpe typedef ^ ^ ReKi RefHt - - - "RefHeight" typedef ^ ^ ReKi PLExp - - - "PLExp" typedef ^ ^ IntKi MHK - - - "MHK turbine type switch" - +typedef ^ ^ ReKi WtrDpth - - - "Water depth" m +typedef ^ ^ ReKi MSL2SWL - - - "Offset between still-water level and mean sea level" m typedef ^ ^ IntKi FilePassingMethod - 0 - "Should we read everthing from an input file (0), passed in as a FileInfoType structure (1), or passed as the IfW_InputFile structure (2)" - typedef ^ ^ FileInfoType PassedFileInfo - - - "If we don't use the input file, pass everything through this as a FileInfo structure" - typedef ^ ^ InflowWind_InputFile PassedFileData - - - "If we don't use the input file, pass everything through this as an IfW InputFile structure" - @@ -52,7 +54,7 @@ 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 + # ..... InitOut ................................................................................................................... typedef ^ InitOutputType ProgDesc Ver - - - "This module's name, version, and date" - diff --git a/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 b/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 index 9f5450c906..e5ddb83400 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 @@ -59,6 +59,8 @@ MODULE AeroDyn_Inflow_Types REAL(ReKi) :: RefHt = 0.0_ReKi !< RefHeight [-] REAL(ReKi) :: PLExp = 0.0_ReKi !< PLExp [-] INTEGER(IntKi) :: MHK = 0_IntKi !< MHK turbine type switch [-] + REAL(ReKi) :: WtrDpth = 0.0_ReKi !< Water depth [m] + REAL(ReKi) :: MSL2SWL = 0.0_ReKi !< Offset between still-water level and mean sea level [m] INTEGER(IntKi) :: FilePassingMethod = 0 !< Should we read everthing from an input file (0), passed in as a FileInfoType structure (1), or passed as the IfW_InputFile structure (2) [-] TYPE(FileInfoType) :: PassedFileInfo !< If we don't use the input file, pass everything through this as a FileInfo structure [-] TYPE(InflowWind_InputFile) :: PassedFileData !< If we don't use the input file, pass everything through this as an IfW InputFile structure [-] @@ -74,7 +76,6 @@ 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] END TYPE ADI_InitInputType ! ======================= ! ========= ADI_InitOutputType ======= @@ -302,6 +303,8 @@ subroutine ADI_CopyIW_InputData(SrcIW_InputDataData, DstIW_InputDataData, CtrlCo DstIW_InputDataData%RefHt = SrcIW_InputDataData%RefHt DstIW_InputDataData%PLExp = SrcIW_InputDataData%PLExp DstIW_InputDataData%MHK = SrcIW_InputDataData%MHK + DstIW_InputDataData%WtrDpth = SrcIW_InputDataData%WtrDpth + DstIW_InputDataData%MSL2SWL = SrcIW_InputDataData%MSL2SWL DstIW_InputDataData%FilePassingMethod = SrcIW_InputDataData%FilePassingMethod call NWTC_Library_CopyFileInfoType(SrcIW_InputDataData%PassedFileInfo, DstIW_InputDataData%PassedFileInfo, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -339,6 +342,8 @@ subroutine ADI_PackIW_InputData(RF, Indata) call RegPack(RF, InData%RefHt) call RegPack(RF, InData%PLExp) call RegPack(RF, InData%MHK) + call RegPack(RF, InData%WtrDpth) + call RegPack(RF, InData%MSL2SWL) call RegPack(RF, InData%FilePassingMethod) call NWTC_Library_PackFileInfoType(RF, InData%PassedFileInfo) call InflowWind_PackInputFile(RF, InData%PassedFileData) @@ -358,6 +363,8 @@ subroutine ADI_UnPackIW_InputData(RF, OutData) call RegUnpack(RF, OutData%RefHt); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%PLExp); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MHK); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WtrDpth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MSL2SWL); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%FilePassingMethod); if (RegCheckErr(RF, RoutineName)) return call NWTC_Library_UnpackFileInfoType(RF, OutData%PassedFileInfo) ! PassedFileInfo call InflowWind_UnpackInputFile(RF, OutData%PassedFileData) ! PassedFileData @@ -386,7 +393,6 @@ 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 end subroutine subroutine ADI_DestroyInitInput(InitInputData, ErrStat, ErrMsg) @@ -415,7 +421,6 @@ 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) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -430,7 +435,6 @@ 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 end subroutine subroutine ADI_CopyInitOutput(SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg) diff --git a/modules/hydrodyn/src/HydroDyn.f90 b/modules/hydrodyn/src/HydroDyn.f90 index 25afdd70e9..2412cacfa5 100644 --- a/modules/hydrodyn/src/HydroDyn.f90 +++ b/modules/hydrodyn/src/HydroDyn.f90 @@ -1228,8 +1228,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, REAL(ReKi), PARAMETER :: LrgAngle = 0.261799387799149 ! Threshold for platform roll and pitch rotation (15 deg). This is consistent with the ElastoDyn check. LOGICAL, SAVE :: FrstWarn_LrgY = .TRUE. - ! Initialize ErrStat - + ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -1240,7 +1239,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, IF ( (p%OutSwtch == 1 .OR. p%OutSwtch == 3) .AND. ( Time > m%LastOutTime ) ) THEN CALL HDOut_WriteOutputs( m%LastOutTime, y, p, m%Decimate, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return END IF m%LastOutTime = Time ! time associated with the next values of y%WriteOutput @@ -1260,8 +1259,8 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, IF ( (ABS( WrapToPi(PRPRotation(3)-PtfmRefY) ) > LrgAngle) .AND. FrstWarn_LrgY ) THEN ErrStat2 = ErrID_Severe ErrMsg2 = 'Yaw angle at PRP relative to the reference yaw position (PtfmRefY) violated the small angle assumption. The solution might be inaccurate. Consider using PtfmYMod=1 and adjust PtfmYCutoff. Simulation continuing, but future warnings will be suppressed.' - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) FrstWarn_LrgY = .FALSE. + if (Failed()) return END IF !------------------------------------------------------------------- @@ -1270,16 +1269,15 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, !------------------------------------------------------------------- - if ( p%PotMod == 1 ) then ! Transformation matrices between global and PRP frame - ALLOCATE(RRb2g(6*p%NBody,6*p%NBody),STAT=ErrStat2) - ALLOCATE(RRg2b(6*p%NBody,6*p%NBody),STAT=ErrStat2) + ALLOCATE(RRb2g(6*p%NBody,6*p%NBody),STAT=ErrStat2); if (Failed0("RRb2g")) return; + ALLOCATE(RRg2b(6*p%NBody,6*p%NBody),STAT=ErrStat2); if (Failed0("RRg2b")) return; RRg2b(:,:) = 0.0_ReKi do iBody = 1, p%NBody ! Determine the rotational angles from the direction-cosine matrix ! rotdisp = GetRotAngs ( u%PtfmRefY, u%WAMITMesh%Orientation(:,:,iBody), ErrStat2, ErrMsg2 ) - ! CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ! if (Failed()) return rotdisp = EulerExtractZYX(u%WAMITMesh%Orientation(:,:,iBody)) indxStart = (iBody-1)*6+1 indxEnd = indxStart+5 @@ -1294,9 +1292,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, end do RRb2g = transpose(RRg2b) - !FIXME: Error handling appears to be broken here. if ( p%NBodyMod == 1 ) then - ! Compute the load contirbution from user-supplied added stiffness and damping m%F_PtfmAdd = p%AddF0(:,1) - matmul(p%AddCLin(:,:,1), q) & - matmul( matmul(RRb2g,p%AddBLin(:,:,1) ), matmul(RRg2b,qdot) ) & @@ -1334,14 +1330,13 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, if ( p%NBodyMod == 1 .or. p%NBody == 1 ) then ! Copy the inputs from the HD mesh into the WAMIT mesh call MeshCopy( u%WAMITMesh, m%u_WAMIT(1)%Mesh, MESH_UPDATECOPY, ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - if ( ErrStat >= AbortErrLev ) return + if (Failed()) return ! m%u_WAMIT(1)%PtfmRefY = u%PtfmRefY m%u_WAMIT(1)%PtfmRefY = PtfmRefY call WAMIT_CalcOutput( Time, m%u_WAMIT(1), p%WAMIT(1), x%WAMIT(1), xd%WAMIT(1), & z%WAMIT, OtherState%WAMIT(1), y%WAMIT(1), m%WAMIT(1), ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return do iBody=1,p%NBody y%WAMITMesh%Force (:,iBody) = y%WAMITMesh%Force (:,iBody) + y%WAMIT(1)%Mesh%Force (:,iBody) y%WAMITMesh%Moment(:,iBody) = y%WAMITMesh%Moment(:,iBody) + y%WAMIT(1)%Mesh%Moment(:,iBody) @@ -1363,7 +1358,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, m%u_WAMIT(iBody)%PtfmRefY = PtfmRefY call WAMIT_CalcOutput( Time, m%u_WAMIT(iBody), p%WAMIT(iBody), x%WAMIT(iBody), xd%WAMIT(iBody), & z%WAMIT, OtherState%WAMIT(iBody), y%WAMIT(iBody), m%WAMIT(iBody), ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return y%WAMITMesh%Force (:,iBody) = y%WAMITMesh%Force (:,iBody) + y%WAMIT(iBody)%Mesh%Force (:,1) y%WAMITMesh%Moment(:,iBody) = y%WAMITMesh%Moment(:,iBody) + y%WAMIT(iBody)%Mesh%Moment(:,1) @@ -1383,7 +1378,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, if ( p%NBodyMod == 1 .or. p%NBody == 1 ) then call WAMIT2_CalcOutput( Time, PtfmRefY, p%WaveField, p%WAMIT2(1), y%WAMIT2(1), m%WAMIT2(1), ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return do iBody=1,p%NBody y%WAMITMesh%Force (:,iBody) = y%WAMITMesh%Force (:,iBody) + y%WAMIT2(1)%Mesh%Force (:,iBody) y%WAMITMesh%Moment(:,iBody) = y%WAMITMesh%Moment(:,iBody) + y%WAMIT2(1)%Mesh%Moment(:,iBody) @@ -1394,7 +1389,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, do iBody=1,p%NBody call WAMIT2_CalcOutput( Time, PtfmRefY, p%WaveField, p%WAMIT2(iBody), y%WAMIT2(iBody), m%WAMIT2(iBody), ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return y%WAMITMesh%Force (:,iBody) = y%WAMITMesh%Force (:,iBody) + y%WAMIT2(iBody)%Mesh%Force (:,1) y%WAMITMesh%Moment(:,iBody) = y%WAMITMesh%Moment(:,iBody) + y%WAMIT2(iBody)%Mesh%Moment(:,1) @@ -1414,6 +1409,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, Inputs_FIT%si_t(:) = u%WAMITMesh%TranslationDisp(:,1) Inputs_FIT%vel_t(:) = u%WAMITMesh%TranslationVel (:,1) CALL FIT_CalcOutput( Time, Inputs_FIT, p%FIT, FIT_x, xd%FIT, FIT_z, OtherState%FIT, y%FIT, ErrStat2, ErrMsg2 ) + if (Failed()) return ! Add FIT forces to the HydroDyn output mesh y%WAMITMesh%Force (:,1) = y%WAMITMesh%Force (:,1) + y%FIT%F(:) @@ -1428,16 +1424,16 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, u%Morison%PtfmRefY = PtfmRefY CALL Morison_CalcOutput( Time, u%Morison, p%Morison, x%Morison, xd%Morison, & z%Morison, OtherState%Morison, y%Morison, m%Morison, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return END IF ! Integrate all the mesh loads onto the platfrom reference Point (PRP) at (0,0,0) m%F_Hydro = CalcLoadsAtWRP( y, u, m%AllHdroOrigin, m%HD_MeshMap, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return ! Map calculated results into the first p%NumOuts values of the y%WriteOutput Array CALL HDOut_MapOutputs( p, y, m%WAMIT, m%WAMIT2, m%F_PtfmAdd, m%F_Waves, m%F_Hydro, u%PRPMesh, PtfmRefY, q, qdot, qdotdot, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return ! Aggregate the sub-module outputs IF (p%Morison%NumOuts > 0) THEN @@ -1453,6 +1449,21 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, IF (ALLOCATED(RRb2g)) DEALLOCATE(RRb2g) IF (ALLOCATED(RRg2b)) DEALLOCATE(RRg2b) +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed + ! check for failed where /= 0 is fatal + logical function Failed0(txt) + character(*), intent(in) :: txt + if (ErrStat2 /= 0) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = "Could not allocate "//trim(txt) + call SetErrStat(ErrStat2, ErrMsg2, errStat, errMsg, RoutineName) + endif + Failed0 = ErrStat >= AbortErrLev + end function Failed0 END SUBROUTINE HydroDyn_CalcOutput @@ -1567,6 +1578,9 @@ function CalcLoadsAtWRP( y, u, AllHdroOrigin, MeshMapData, ErrStat, ErrMsg ) integer(IntKi) :: ErrStat2 ! temporary Error status of the operation character(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None + ErrStat = ErrID_None + ErrMsg = "" + CalcLoadsAtWRP = 0.0_ReKi if ( y%WAMITMesh%Committed ) then diff --git a/modules/moordyn/CMakeLists.txt b/modules/moordyn/CMakeLists.txt index 5ec97ef21c..ffaff4fd8d 100644 --- a/modules/moordyn/CMakeLists.txt +++ b/modules/moordyn/CMakeLists.txt @@ -37,15 +37,32 @@ add_executable(moordyn_driver target_link_libraries(moordyn_driver moordynlib versioninfolib) # C-bindings interface library -add_library(moordyn_c_binding SHARED - src/MoorDyn_C_Binding.f90 -) -target_link_libraries(moordyn_c_binding moordynlib seastlib versioninfolib) +# create object instead of directly linking into shared and static -- causes issues in parallel builds +# This is only required because we are static linking the library for wavetank +# NOTE: target linking at the object, static, and shared libraries. Different CMake versions handle this +# slightly differently with unpredictable results if I don't. +add_library(moordyn_c_binding_object OBJECT src/MoorDyn_C_Binding.f90) +target_link_libraries(moordyn_c_binding_object moordynlib seastlib nwtclibs versioninfolib) +set_property(TARGET moordyn_c_binding_object PROPERTY POSITION_INDEPENDENT_CODE 1) # required for shared libs + +# shared +add_library(moordyn_c_binding SHARED $) +target_link_libraries(moordyn_c_binding moordynlib seastlib nwtclibs versioninfolib) if(APPLE OR UNIX) target_compile_definitions(moordyn_c_binding PRIVATE IMPLICIT_DLLEXPORT) endif() -install(TARGETS moordynlib moordyn_driver moordyn_c_binding +# C-bindings non-shared interface +# This is a workaround for building wavetank into a single DLL (also allows setting CU globaly for sending screen to file for labview integration) +add_library(moordyn_c_bind_static SHARED $) +target_link_libraries(moordyn_c_bind_static moordynlib seastlib nwtclibs versioninfolib) +if(APPLE OR UNIX) + target_compile_definitions(moordyn_c_bind_static PRIVATE IMPLICIT_DLLEXPORT) +endif() + + + +install(TARGETS moordynlib moordyn_driver moordyn_c_binding moordyn_c_bind_static EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin ARCHIVE DESTINATION lib diff --git a/modules/moordyn/src/MoorDyn.f90 b/modules/moordyn/src/MoorDyn.f90 index 034d683ba0..1ba16c8ccd 100644 --- a/modules/moordyn/src/MoorDyn.f90 +++ b/modules/moordyn/src/MoorDyn.f90 @@ -3922,15 +3922,10 @@ SUBROUTINE MD_End(u, p, x, xd, z, other, y, m, ErrStat , ErrMsg) CALL MD_DestroyMisc(m, ErrStat2, ErrMsg2) CALL CheckError( ErrStat2, ErrMsg2 ) - IF (p%UnLog > 0_IntKi) CLOSE( p%UnLog ) ! close log file if it's open - !TODO: any need to specifically deallocate things like m%xTemp%states in the above? <<<< - - ! IF ( ErrStat==ErrID_None) THEN - ! CALL WrScr('MoorDyn closed without errors') - ! ELSE - ! CALL WrScr('MoorDyn closed with errors') - ! END IF - + IF (p%UnLog > 0_IntKi) then + CLOSE( p%UnLog ) ! close log file if it's open + p%UnLog = -1 ! in case we call end a second time for whatever reason + endif CONTAINS diff --git a/modules/moordyn/src/MoorDyn_C_Binding.f90 b/modules/moordyn/src/MoorDyn_C_Binding.f90 index b2d136c46f..ef70233506 100644 --- a/modules/moordyn/src/MoorDyn_C_Binding.f90 +++ b/modules/moordyn/src/MoorDyn_C_Binding.f90 @@ -33,7 +33,9 @@ MODULE MoorDyn_C PUBLIC :: MD_C_UpdateStates PUBLIC :: MD_C_CalcOutput PUBLIC :: MD_C_End +PUBLIC :: MD_C_SetWaveFieldData +PRIVATE !------------------------------------------------------------------------------------ ! Version info for display @@ -129,6 +131,7 @@ MODULE MoorDyn_C !=============================================================================================================== !---------------------------------------------- MD INIT -------------------------------------------------------- !=============================================================================================================== +!FIXME: add ShowPassed and DebugLevel SUBROUTINE MD_C_Init( & InputFilePassed, InputFileString_C, InputFileStringLength_C, & DT_C, G_C, RHO_C, DEPTH_C, PtfmInit_C, & @@ -147,11 +150,12 @@ SUBROUTINE MD_C_Init( & REAL(C_FLOAT) , INTENT(IN ) :: G_C REAL(C_FLOAT) , INTENT(IN ) :: RHO_C REAL(C_FLOAT) , INTENT(IN ) :: DEPTH_C +!FIXME: PtfmInit_C should be resized for N nodes (6xN position), but can stay as euler angle for angles REAL(C_FLOAT) , INTENT(IN ) :: PtfmInit_C(6) ! TODO: make this more flexible, can we not have 6 DOF only coupling? INTEGER(C_INT) , INTENT(IN ) :: InterpOrder_C INTEGER(C_INT) , INTENT( OUT) :: NumChannels_C - CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelNames_C(100000) - CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelUnits_C(100000) + CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelNames_C(ChanLen*1000) ! The size of these arrays was chosen as a "big number", it isn't set by MoorDyn. Watch out, it might be bigger than this! + CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelUnits_C(ChanLen*1000) INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) @@ -171,7 +175,28 @@ SUBROUTINE MD_C_Init( & CALL DispCopyrightLicense( version%Name ) CALL DispCompileRuntimeInfo( version%Name ) - + ! Destroy global memory + if (allocated(u)) then + do i = 1, size(u) + call MD_DestroyInput(u(i), ErrStat_F, ErrMsg_F) + end do + deallocate(u) + end if + call MD_DestroyParam(p, ErrStat_F, ErrMsg_F) + do i = 0, 2 + call MD_DestroyContState(x(i), ErrStat_F, ErrMsg_F) + end do + do i = 0, 2 + call MD_DestroyDiscState(xd(i), ErrStat_F, ErrMsg_F) + end do + do i = 0, 2 + call MD_DestroyConstrState(z(i), ErrStat_F, ErrMsg_F) + end do + do i = 0, 2 + call MD_DestroyOtherState(other(i), ErrStat_F, ErrMsg_F) + end do + call MD_DestroyOutput(y, ErrStat_F, ErrMsg_F) + call MD_DestroyMisc(m, ErrStat_F, ErrMsg_F) ! Convert the MD input file to FileInfoType !---------------------------------------------------------------------------------------------------------------------------------------------- @@ -530,8 +555,13 @@ SUBROUTINE MD_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='MD_C_End') ErrMsg_F = '' ! Call the main subroutine MD_End - CALL MD_End(u(1), p, x(1), xd(1), z(1), other(1), y, m, ErrStat_F2, ErrMsg_F2) - call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) + ! If u is not allocated, then we didn't get far at all in initialization, + ! or AD_C_End got called before Init. We don't want a segfault, so check + ! for allocation. + if (allocated(u)) then + CALL MD_End(u(1), p, x(1), xd(1), z(1), other(1), y, m, ErrStat_F2, ErrMsg_F2) + call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) + endif ! NOTE: MoorDyn_End only takes 1 instance of u, not the array. So extra ! logic is required here (this isn't necessary in the fortran driver @@ -585,7 +615,6 @@ END SUBROUTINE MD_C_End !----------------------------------------------- MD SetWaveFieldData ------------------------------------------- !=============================================================================================================== !> Set the wave field data pointer from an external source such as SeaState -!! Assumes that MD_C_Init has been run since it uses those values to check that there's a valid pointer SUBROUTINE MD_C_SetWaveFieldData(WaveFieldData_C) BIND (C, NAME='MD_C_SetWaveFieldData') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: MD_C_SetWaveFieldData diff --git a/modules/moordyn/src/MoorDyn_IO.f90 b/modules/moordyn/src/MoorDyn_IO.f90 index be4ef63a78..3061b350f9 100644 --- a/modules/moordyn/src/MoorDyn_IO.f90 +++ b/modules/moordyn/src/MoorDyn_IO.f90 @@ -1317,22 +1317,20 @@ SUBROUTINE MDIO_CloseOutput ( p, m, ErrStat, ErrMsg ) INTEGER, INTENT( OUT ) :: ErrStat ! a non-zero value indicates an error occurred CHARACTER(*), INTENT( OUT ) :: ErrMsg ! Error message if ErrStat /= ErrID_None - INTEGER(IntKi) :: I ! generic counter - + INTEGER(IntKi) :: I ! generic counter + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'MDIO_CloseOutput' ErrStat = 0 ErrMsg = "" - -!FIXME: make sure thes are actually open before trying to close them. Segfault will occur otherwise!!!! -! This bug can be triggered by an early failure of the parsing routines, before these files were ever opened -! which returns MD to OpenFAST as ErrID_Fatal, then OpenFAST calls MD_End, which calls this. - ! close main MoorDyn output file if (p%MDUnOut > 0) then - CLOSE( p%MDUnOut, IOSTAT = ErrStat ) - IF ( ErrStat /= 0 ) THEN - ErrMsg = 'Error closing output file' + CLOSE( p%MDUnOut, IOSTAT = ErrStat2 ) + p%MDUnOut = -1 + IF ( ErrStat2 /= 0 ) THEN + call SetErrStat(ErrID_Severe,'Error closing output file',ErrStat,ErrMsg,RoutineName) END IF end if @@ -1340,9 +1338,10 @@ SUBROUTINE MDIO_CloseOutput ( p, m, ErrStat, ErrMsg ) DO I=1,p%NRods if (allocated(m%RodList)) then if (m%RodList(I)%RodUnOut > 0) then - CLOSE( m%RodList(I)%RodUnOut, IOSTAT = ErrStat ) - IF ( ErrStat /= 0 ) THEN - ErrMsg = 'Error closing rod output file' + CLOSE( m%RodList(I)%RodUnOut, IOSTAT = ErrStat2 ) + m%RodList(I)%RodUnOut = -1 + IF ( ErrStat2 /= 0 ) THEN + call SetErrStat(ErrID_Severe,'Error closing rod output file',ErrStat,ErrMsg,RoutineName) END IF end if end if @@ -1352,9 +1351,10 @@ SUBROUTINE MDIO_CloseOutput ( p, m, ErrStat, ErrMsg ) DO I=1,p%NLines if (allocated(m%LineList)) then if (m%LineList(I)%LineUnOut > 0) then - CLOSE( m%LineList(I)%LineUnOut, IOSTAT = ErrStat ) - IF ( ErrStat /= 0 ) THEN - ErrMsg = 'Error closing line output file' + CLOSE( m%LineList(I)%LineUnOut, IOSTAT = ErrStat2 ) + m%LineList(I)%LineUnOut = -1 + IF ( ErrStat2 /= 0 ) THEN + call SetErrStat(ErrID_Severe,'Error closing line output file',ErrStat,ErrMsg,RoutineName) END IF end if end if diff --git a/modules/nwtc-library/src/NWTC_C_Binding.f90 b/modules/nwtc-library/src/NWTC_C_Binding.f90 index ca3a574710..fae7dfd2d5 100644 --- a/modules/nwtc-library/src/NWTC_C_Binding.f90 +++ b/modules/nwtc-library/src/NWTC_C_Binding.f90 @@ -32,6 +32,7 @@ MODULE NWTC_C_Binding ! to correctly handle different lengths of the strings INTEGER(IntKi), PARAMETER :: ErrMsgLen_C = ErrMsgLen + 1 ! Currently, this is 8197 INTEGER(IntKi), PARAMETER :: IntfStrLen = 1025 ! length of other strings through the C interface such as file paths +integer(c_int), PARAMETER :: AbortErrLev_C = 4_c_int CONTAINS diff --git a/modules/nwtc-library/src/SysFlangLinux.f90 b/modules/nwtc-library/src/SysFlangLinux.f90 index 42f04c2d60..621e405528 100644 --- a/modules/nwtc-library/src/SysFlangLinux.f90 +++ b/modules/nwtc-library/src/SysFlangLinux.f90 @@ -55,7 +55,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. Unit 6 causes ADAMS to crash. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console. Unit 6 causes ADAMS to crash. INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -107,7 +107,15 @@ FUNCTION dlClose(handle) BIND(C,NAME="dlclose") CONTAINS - + !======================================================================= + SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + + INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. + CU = Unit + + END SUBROUTINE SetConsoleUnit + !======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. @@ -458,4 +466,4 @@ SUBROUTINE FreeDynamicLib ( DLL, ErrStat, ErrMsg ) END SUBROUTINE FreeDynamicLib !======================================================================= END MODULE SysSubs - \ No newline at end of file + diff --git a/modules/nwtc-library/src/SysGnuLinux.f90 b/modules/nwtc-library/src/SysGnuLinux.f90 index d45d88b3c4..0fb51921b3 100644 --- a/modules/nwtc-library/src/SysGnuLinux.f90 +++ b/modules/nwtc-library/src/SysGnuLinux.f90 @@ -56,7 +56,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -89,6 +89,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysGnuWin.f90 b/modules/nwtc-library/src/SysGnuWin.f90 index 0905792bbc..95794cf195 100644 --- a/modules/nwtc-library/src/SysGnuWin.f90 +++ b/modules/nwtc-library/src/SysGnuWin.f90 @@ -56,7 +56,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -89,6 +89,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysIFL.f90 b/modules/nwtc-library/src/SysIFL.f90 index c6c439e890..f2ccbe1cde 100644 --- a/modules/nwtc-library/src/SysIFL.f90 +++ b/modules/nwtc-library/src/SysIFL.f90 @@ -56,7 +56,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -91,6 +91,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysIVF.f90 b/modules/nwtc-library/src/SysIVF.f90 index 2a6304dbdb..d0e7227657 100644 --- a/modules/nwtc-library/src/SysIVF.f90 +++ b/modules/nwtc-library/src/SysIVF.f90 @@ -56,7 +56,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 7 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 7 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}]; Note: NewLine change to ACHAR(10) here on Windows to fix issues with C/Fortran interoperability using WrScr @@ -90,6 +90,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a NaN (not-a-number) value. diff --git a/modules/nwtc-library/src/SysIVF_Labview.f90 b/modules/nwtc-library/src/SysIVF_Labview.f90 index 529ed52f32..ac42539000 100644 --- a/modules/nwtc-library/src/SysIVF_Labview.f90 +++ b/modules/nwtc-library/src/SysIVF_Labview.f90 @@ -73,7 +73,7 @@ MODULE SysSubs INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 7 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 7 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .FALSE. ! A flag to tell the program that keyboard input is allowed in the environment. @@ -125,6 +125,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) +!======================================================================= + SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + + INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. + CU = Unit + + END SUBROUTINE SetConsoleUnit !======================================================================= SUBROUTINE FlushOut ( Unit ) diff --git a/modules/nwtc-library/src/SysMatlabLinuxGnu.f90 b/modules/nwtc-library/src/SysMatlabLinuxGnu.f90 index d69f227433..6b2a847d24 100644 --- a/modules/nwtc-library/src/SysMatlabLinuxGnu.f90 +++ b/modules/nwtc-library/src/SysMatlabLinuxGnu.f90 @@ -59,7 +59,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .FALSE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -92,6 +92,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysMatlabLinuxIntel.f90 b/modules/nwtc-library/src/SysMatlabLinuxIntel.f90 index c521e449f0..a31dc15cad 100644 --- a/modules/nwtc-library/src/SysMatlabLinuxIntel.f90 +++ b/modules/nwtc-library/src/SysMatlabLinuxIntel.f90 @@ -59,7 +59,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .FALSE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -94,6 +94,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysMatlabWindows.f90 b/modules/nwtc-library/src/SysMatlabWindows.f90 index 09c931aff2..c462d92df9 100644 --- a/modules/nwtc-library/src/SysMatlabWindows.f90 +++ b/modules/nwtc-library/src/SysMatlabWindows.f90 @@ -47,7 +47,7 @@ MODULE SysSubs !======================================================================= - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .FALSE. ! A flag to tell the program that keyboard input is allowed in the environment. @@ -82,6 +82,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) +!======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit !======================================================================= SUBROUTINE FlushOut ( Unit ) diff --git a/modules/nwtc-library/tests/NWTC_Library_test_tools.F90 b/modules/nwtc-library/tests/NWTC_Library_test_tools.F90 index 01df14383a..685cefcff1 100644 --- a/modules/nwtc-library/tests/NWTC_Library_test_tools.F90 +++ b/modules/nwtc-library/tests/NWTC_Library_test_tools.F90 @@ -12,16 +12,14 @@ module nwtc_library_test_tools character(11), parameter :: terminal="/dev/stdout" #endif -integer, parameter :: stdout=CU - contains subroutine hide_terminal_output() - open(unit=stdout, file=trim(nullfile)) + open(unit=CU, file=trim(nullfile)) end subroutine subroutine show_terminal_output() - open(unit=stdout, file=terminal, status="old") + open(unit=CU, file=terminal, status="old") end subroutine end module diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index aea181b73e..8ab3f1f1ac 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -586,6 +586,7 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, SED, BD, S Init%InData_SeaSt%hasIce = p_FAST%CompIce /= Module_None Init%InData_SeaSt%InputFile = p_FAST%SeaStFile Init%InData_SeaSt%OutRootName = TRIM(p_FAST%OutFileRoot)//'.'//TRIM(y_FAST%Module_Abrev(Module_SeaSt)) + Init%InData_SeaSt%WaveTimeShift = 0.0_DbKi ! for phase shifting wave field in time (positive value only) ! these values support wave field handling Init%InData_SeaSt%WaveFieldMod = p_FAST%WaveFieldMod diff --git a/modules/seastate/CMakeLists.txt b/modules/seastate/CMakeLists.txt index 9900facf14..2b834f0e8d 100644 --- a/modules/seastate/CMakeLists.txt +++ b/modules/seastate/CMakeLists.txt @@ -39,22 +39,39 @@ add_library(seastlib STATIC ) target_link_libraries(seastlib nwtclibs versioninfolib) + # C-bindings interface library -add_library(seastate_c_binding SHARED - src/SeaState_C_Binding.f90 -) -target_link_libraries(seastate_c_binding seastlib versioninfolib) +# create object instead of directly linking into shared and static -- causes issues in parallel builds +# This is only required because we are static linking the library for wavetank +# NOTE: target linking at the object, static, and shared libraries. Different CMake versions handle this +# slightly differently with unpredictable results if I don't. +add_library(seastate_c_binding_object OBJECT src/SeaState_C_Binding.f90) +target_link_libraries(seastate_c_binding_object seastlib nwtclibs versioninfolib) +set_property(TARGET seastate_c_binding_object PROPERTY POSITION_INDEPENDENT_CODE 1) # required for shared libs + +# shared +add_library(seastate_c_binding SHARED $) +target_link_libraries(seastate_c_binding seastlib nwtclibs versioninfolib) if(APPLE OR UNIX) target_compile_definitions(seastate_c_binding PRIVATE IMPLICIT_DLLEXPORT) endif() +# C-bindings static interface +# This is a workaround for building wavetank into a single DLL (also allows setting CU globaly for sending screen to file for labview integration) +add_library(seastate_c_bind_static STATIC $) +target_link_libraries(seastate_c_bind_static seastlib nwtclibs versioninfolib) +if(APPLE OR UNIX) + target_compile_definitions(seastate_c_bind_static PRIVATE IMPLICIT_DLLEXPORT) +endif() + + # Driver add_executable(seastate_driver src/SeaState_DriverCode.f90 ) target_link_libraries(seastate_driver seastlib) -install(TARGETS seastate_driver seastlib seastate_c_binding +install(TARGETS seastate_driver seastlib seastate_c_binding seastate_c_bind_static EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/modules/seastate/src/SeaSt_WaveField.f90 b/modules/seastate/src/SeaSt_WaveField.f90 index fd5029ef5d..43be7527a9 100644 --- a/modules/seastate/src/SeaSt_WaveField.f90 +++ b/modules/seastate/src/SeaSt_WaveField.f90 @@ -38,7 +38,7 @@ function WaveField_GetNodeWaveElev1( WaveField, WaveField_m, Time, pos, ErrStat, ErrMsg = "" IF (ALLOCATED(WaveField%WaveElev1)) THEN - CALL WaveField_Interp_Setup3D( Time, pos, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ) + CALL WaveField_Interp_Setup3D( Time+WaveField%WaveTimeShift, pos, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) Zeta = GridInterp3D(WaveField%WaveElev1,WaveField_m) ELSE @@ -68,7 +68,7 @@ function WaveField_GetNodeWaveElev2( WaveField, WaveField_m, Time, pos, ErrStat, ErrMsg = "" IF (ALLOCATED(WaveField%WaveElev2)) THEN - CALL WaveField_Interp_Setup3D( Time, pos, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ) + CALL WaveField_Interp_Setup3D( Time+WaveField%WaveTimeShift, pos, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) Zeta = GridInterp3D(WaveField%WaveElev2,WaveField_m) ELSE @@ -126,7 +126,7 @@ SUBROUTINE WaveField_GetNodeWaveNormal( WaveField, WaveField_m, Time, pos, n, Er ErrStat = ErrID_None ErrMsg = "" - call GridInterpSetupN( (/Real(Time,ReKi),pos(1),pos(2)/), WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ) + call GridInterpSetupN( (/Real(Time+WaveField%WaveTimeShift,ReKi),pos(1),pos(2)/), WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ) slope = GridInterpS( WaveField%WaveElev1, WaveField%SrfGridParams, WaveField_m ) if (ALLOCATED(WaveField%WaveElev2)) then slope = slope + GridInterpS( WaveField%WaveElev2, WaveField%SrfGridParams, WaveField_m ) @@ -183,7 +183,7 @@ SUBROUTINE WaveField_GetNodeWaveKin( WaveField, WaveField_m, Time, pos, forceNod IF ( pos(3) <= 0.0_ReKi) THEN ! Node is at or below the SWL nodeInWater = 1_IntKi ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) FDynP = GridInterp4D ( WaveField%WaveDynP, WaveField_m ) @@ -209,7 +209,7 @@ SUBROUTINE WaveField_GetNodeWaveKin( WaveField, WaveField_m, Time, pos, forceNod IF ( pos(3) <= 0.0_SiKi) THEN ! Node is below the SWL - evaluate wave dynamics as usual ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) FDynP = GridInterp4D ( WaveField%WaveDynP, WaveField_m ) @@ -220,7 +220,7 @@ SUBROUTINE WaveField_GetNodeWaveKin( WaveField, WaveField_m, Time, pos, forceNod ELSE ! Node is above SWL - need wave stretching ! Vertical wave stretching - CALL WaveField_Interp_Setup4D( Time, posXY0, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, posXY0, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) FDynP = GridInterp4D ( WaveField%WaveDynP, WaveField_m ) @@ -230,7 +230,7 @@ SUBROUTINE WaveField_GetNodeWaveKin( WaveField, WaveField_m, Time, pos, forceNod ! Extrapoled wave stretching IF (WaveField%WaveStMod == 2) THEN - CALL WaveField_Interp_Setup3D( Time, posXY, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_Interp_Setup3D( Time+WaveField%WaveTimeShift, posXY, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:) = FV(:) + GridInterp3DVec( WaveField%PWaveVel0, WaveField_m ) * pos(3) FA(:) = FA(:) + GridInterp3DVec( WaveField%PWaveAcc0, WaveField_m ) * pos(3) FDynP = FDynP + GridInterp3D ( WaveField%PWaveDynP0, WaveField_m ) * pos(3) @@ -249,7 +249,7 @@ SUBROUTINE WaveField_GetNodeWaveKin( WaveField, WaveField_m, Time, pos, forceNod posPrime(3) = MIN( posPrime(3), 0.0_ReKi) ! Clamp z-position to zero. Needed when forceNodeInWater=.TRUE. ! Obtain the wave-field variables by interpolation with the mapped position. - CALL WaveField_Interp_Setup4D( Time, posPrime, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, posPrime, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) FDynP = GridInterp4D ( WaveField%WaveDynP, WaveField_m ) @@ -311,7 +311,7 @@ SUBROUTINE WaveField_GetNodeWaveVelAcc( WaveField, WaveField_m, Time, pos, force IF ( pos(3) <= 0.0_ReKi) THEN ! Node is at or below the SWL nodeInWater = 1_IntKi ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) ELSE ! Node is above the SWL @@ -331,7 +331,7 @@ SUBROUTINE WaveField_GetNodeWaveVelAcc( WaveField, WaveField_m, Time, pos, force IF ( pos(3) <= 0.0_SiKi) THEN ! Node is below the SWL - evaluate wave dynamics as usual ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) @@ -344,7 +344,7 @@ SUBROUTINE WaveField_GetNodeWaveVelAcc( WaveField, WaveField_m, Time, pos, force ! Extrapoled wave stretching IF (WaveField%WaveStMod == 2) THEN - CALL WaveField_Interp_Setup3D( Time, posXY, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_Interp_Setup3D( Time+WaveField%WaveTimeShift, posXY, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:) = FV(:) + GridInterp3DVec( WaveField%PWaveVel0, WaveField_m ) * pos(3) FA(:) = FA(:) + GridInterp3DVec( WaveField%PWaveAcc0, WaveField_m ) * pos(3) END IF @@ -359,7 +359,7 @@ SUBROUTINE WaveField_GetNodeWaveVelAcc( WaveField, WaveField_m, Time, pos, force posPrime(3) = MIN( posPrime(3), 0.0_ReKi) ! Clamp z-position to zero. Needed when forceNodeInWater=.TRUE. ! Obtain the wave-field variables by interpolation with the mapped position. - CALL WaveField_Interp_Setup4D( Time, posPrime, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, posPrime, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) diff --git a/modules/seastate/src/SeaSt_WaveField.txt b/modules/seastate/src/SeaSt_WaveField.txt index 380de0f028..d7ab572f09 100644 --- a/modules/seastate/src/SeaSt_WaveField.txt +++ b/modules/seastate/src/SeaSt_WaveField.txt @@ -24,11 +24,6 @@ param SeaSt_WaveField - INTEGER ConstWaveMod #--------------------------------------------------------------------------------------------------------------------------------------------------------- # #--------------------------------------------------------------------------------------------------------------------------------------------------------- -typedef ^ ParameterType IntKi n 4 - - "number of evenly-spaced grid points in the t, x, y, and z directions" - -typedef ^ ParameterType ReKi delta 4 - - "size between 2 consecutive grid points in each grid direction" "s,m,m,m" -typedef ^ ParameterType ReKi pZero 4 - - "fixed position of the XYZ grid (i.e., XYZ coordinates of m%V(:,1,1,1,:))" "m" -typedef ^ ParameterType ReKi Z_Depth - - - "grid depth" m - typedef ^ MiscVarType SiKi N3D {8} - - "this is the weighting function for 3-d velocity field" - typedef ^ MiscVarType SiKi N4D {16} - - "this is the weighting function for 4-d velocity field" - typedef ^ MiscVarType integer Indx_Lo 4 - - "this is the index into the 4-d velocity field for each wave component" - @@ -77,4 +72,5 @@ typedef ^ ^ INTEGER WaveMod typedef ^ ^ INTEGER NStepWave - - - "Total number of frequency components = total number of time steps in the incident wave" - typedef ^ ^ INTEGER NStepWave2 - - - "NStepWave / 2" - typedef ^ ^ SiKi GridDepth - - - "Depth (>0) of wave grid below SWL" m +typedef ^ ^ DbKi WaveTimeShift - 0 - "Add this to the time to effectively phase shift the wave (useful for hybrid tank testing). Positive value only (advance time)" (s) typedef ^ ^ Current_InitInputType Current_InitInput - - - "InitInputs in the Current Module. For coupling with MD." - diff --git a/modules/seastate/src/SeaSt_WaveField_Types.f90 b/modules/seastate/src/SeaSt_WaveField_Types.f90 index de8385a0bc..a7493d67e6 100644 --- a/modules/seastate/src/SeaSt_WaveField_Types.f90 +++ b/modules/seastate/src/SeaSt_WaveField_Types.f90 @@ -49,14 +49,6 @@ MODULE SeaSt_WaveField_Types INTEGER(IntKi), PUBLIC, PARAMETER :: ConstWaveMod_None = 0 ! ConstWaveMod = 0 [Constrained wave model: No constrained waves] [-] INTEGER(IntKi), PUBLIC, PARAMETER :: ConstWaveMod_CrestElev = 1 ! ConstWaveMod = 1 [Constrained wave model: Constrained wave with specified crest elevation, alpha] [-] INTEGER(IntKi), PUBLIC, PARAMETER :: ConstWaveMod_Peak2Trough = 2 ! ConstWaveMod = 2 [Constrained wave model: Constrained wave with guaranteed peak-to-trough crest height, HCrest] [-] -! ========= SeaSt_WaveField_ParameterType ======= - TYPE, PUBLIC :: SeaSt_WaveField_ParameterType - INTEGER(IntKi) , DIMENSION(1:4) :: n = 0_IntKi !< number of evenly-spaced grid points in the t, x, y, and z directions [-] - REAL(ReKi) , DIMENSION(1:4) :: delta = 0.0_ReKi !< size between 2 consecutive grid points in each grid direction [s,m,m,m] - REAL(ReKi) , DIMENSION(1:4) :: pZero = 0.0_ReKi !< fixed position of the XYZ grid (i.e., XYZ coordinates of m%V(:,1,1,1,:)) [m] - REAL(ReKi) :: Z_Depth = 0.0_ReKi !< grid depth [m] - END TYPE SeaSt_WaveField_ParameterType -! ======================= ! ========= SeaSt_WaveField_MiscVarType ======= TYPE, PUBLIC :: SeaSt_WaveField_MiscVarType REAL(SiKi) , DIMENSION(1:8) :: N3D = 0.0_R4Ki !< this is the weighting function for 3-d velocity field [-] @@ -107,58 +99,12 @@ MODULE SeaSt_WaveField_Types INTEGER(IntKi) :: NStepWave = 0_IntKi !< Total number of frequency components = total number of time steps in the incident wave [-] INTEGER(IntKi) :: NStepWave2 = 0_IntKi !< NStepWave / 2 [-] REAL(SiKi) :: GridDepth = 0.0_R4Ki !< Depth (>0) of wave grid below SWL [m] + REAL(DbKi) :: WaveTimeShift = 0 !< Add this to the time to effectively phase shift the wave (useful for hybrid tank testing). Positive value only (advance time) [(s)] TYPE(Current_InitInputType) :: Current_InitInput !< InitInputs in the Current Module. For coupling with MD. [-] END TYPE SeaSt_WaveFieldType ! ======================= CONTAINS -subroutine SeaSt_WaveField_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) - type(SeaSt_WaveField_ParameterType), intent(in) :: SrcParamData - type(SeaSt_WaveField_ParameterType), intent(inout) :: DstParamData - integer(IntKi), intent(in ) :: CtrlCode - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - character(*), parameter :: RoutineName = 'SeaSt_WaveField_CopyParam' - ErrStat = ErrID_None - ErrMsg = '' - DstParamData%n = SrcParamData%n - DstParamData%delta = SrcParamData%delta - DstParamData%pZero = SrcParamData%pZero - DstParamData%Z_Depth = SrcParamData%Z_Depth -end subroutine - -subroutine SeaSt_WaveField_DestroyParam(ParamData, ErrStat, ErrMsg) - type(SeaSt_WaveField_ParameterType), intent(inout) :: ParamData - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - character(*), parameter :: RoutineName = 'SeaSt_WaveField_DestroyParam' - ErrStat = ErrID_None - ErrMsg = '' -end subroutine - -subroutine SeaSt_WaveField_PackParam(RF, Indata) - type(RegFile), intent(inout) :: RF - type(SeaSt_WaveField_ParameterType), intent(in) :: InData - character(*), parameter :: RoutineName = 'SeaSt_WaveField_PackParam' - if (RF%ErrStat >= AbortErrLev) return - call RegPack(RF, InData%n) - call RegPack(RF, InData%delta) - call RegPack(RF, InData%pZero) - call RegPack(RF, InData%Z_Depth) - if (RegCheckErr(RF, RoutineName)) return -end subroutine - -subroutine SeaSt_WaveField_UnPackParam(RF, OutData) - type(RegFile), intent(inout) :: RF - type(SeaSt_WaveField_ParameterType), intent(inout) :: OutData - character(*), parameter :: RoutineName = 'SeaSt_WaveField_UnPackParam' - if (RF%ErrStat /= ErrID_None) return - call RegUnpack(RF, OutData%n); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%delta); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%pZero); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%Z_Depth); if (RegCheckErr(RF, RoutineName)) return -end subroutine - subroutine SeaSt_WaveField_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) type(SeaSt_WaveField_MiscVarType), intent(in) :: SrcMiscData type(SeaSt_WaveField_MiscVarType), intent(inout) :: DstMiscData @@ -429,6 +375,7 @@ subroutine SeaSt_WaveField_CopySeaSt_WaveFieldType(SrcSeaSt_WaveFieldTypeData, D DstSeaSt_WaveFieldTypeData%NStepWave = SrcSeaSt_WaveFieldTypeData%NStepWave DstSeaSt_WaveFieldTypeData%NStepWave2 = SrcSeaSt_WaveFieldTypeData%NStepWave2 DstSeaSt_WaveFieldTypeData%GridDepth = SrcSeaSt_WaveFieldTypeData%GridDepth + DstSeaSt_WaveFieldTypeData%WaveTimeShift = SrcSeaSt_WaveFieldTypeData%WaveTimeShift call Current_CopyInitInput(SrcSeaSt_WaveFieldTypeData%Current_InitInput, DstSeaSt_WaveFieldTypeData%Current_InitInput, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -540,6 +487,7 @@ subroutine SeaSt_WaveField_PackSeaSt_WaveFieldType(RF, Indata) call RegPack(RF, InData%NStepWave) call RegPack(RF, InData%NStepWave2) call RegPack(RF, InData%GridDepth) + call RegPack(RF, InData%WaveTimeShift) call Current_PackInitInput(RF, InData%Current_InitInput) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -591,6 +539,7 @@ subroutine SeaSt_WaveField_UnPackSeaSt_WaveFieldType(RF, OutData) call RegUnpack(RF, OutData%NStepWave); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%NStepWave2); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%GridDepth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WaveTimeShift); if (RegCheckErr(RF, RoutineName)) return call Current_UnpackInitInput(RF, OutData%Current_InitInput) ! Current_InitInput end subroutine END MODULE SeaSt_WaveField_Types diff --git a/modules/seastate/src/SeaState.f90 b/modules/seastate/src/SeaState.f90 index f3dae11151..6bce2063b9 100644 --- a/modules/seastate/src/SeaState.f90 +++ b/modules/seastate/src/SeaState.f90 @@ -171,6 +171,13 @@ SUBROUTINE SeaSt_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, Init ! Initialize Waves module (Note that this may change InputFileData%Waves%WaveDT) CALL Waves_Init(InputFileData%Waves, Waves_InitOut, p%WaveField, ErrStat2, ErrMsg2 ); if(Failed()) return; + ! Store the WaveTimeShift + p%WaveField%WaveTimeShift = InitInp%WaveTimeShift + if (p%WaveField%WaveTimeShift < 0.0_DbKi) then + call SetErrStat(ErrID_Fatal, 'WaveTimeShift from driver code cannot be negative', ErrStat, ErrMsg, RoutineName) + return + endif + ! Copy Waves initialization output into the initialization input type for the WAMIT module p%WaveDT = InputFileData%Waves%WaveDT diff --git a/modules/seastate/src/SeaState.txt b/modules/seastate/src/SeaState.txt index 8b2635bfed..67d328588a 100644 --- a/modules/seastate/src/SeaState.txt +++ b/modules/seastate/src/SeaState.txt @@ -72,6 +72,7 @@ typedef ^ ^ ReKi def typedef ^ ^ ReKi defWtrDpth - - - "Default water depth from the driver; may be overwritten " "m" typedef ^ ^ ReKi defMSL2SWL - - - "Default mean sea level to still water level from the driver; may be overwritten" "m" typedef ^ ^ DbKi TMax - - - "Supplied by Driver: The total simulation time" "(sec)" +typedef ^ ^ DbKi WaveTimeShift - 0 - "Add this to the time to effectively phase shift the wave (useful for hybrid tank testing). Positive value only (advance time)" (s) typedef ^ ^ INTEGER WaveFieldMod - - - "Wave field handling (-) (switch) 0: use individual SeaState inputs without adjustment, 1: adjust wave phases based on turbine offsets from farm origin" - typedef ^ ^ ReKi PtfmLocationX - - - "Supplied by Driver: X coordinate of platform location in the wave field" "m" typedef ^ ^ ReKi PtfmLocationY - - - "Supplied by Driver: Y coordinate of platform location in the wave field" "m" diff --git a/modules/seastate/src/SeaState_C_Binding.f90 b/modules/seastate/src/SeaState_C_Binding.f90 index d70d2125e9..640740dbeb 100644 --- a/modules/seastate/src/SeaState_C_Binding.f90 +++ b/modules/seastate/src/SeaState_C_Binding.f90 @@ -48,7 +48,7 @@ MODULE SeaState_C_Binding ! 2 - above + all position/orientation info ! 3 - above + input files (if direct passed) ! 4 - above + meshes - integer(IntKi) :: DebugLevel = 4 + integer(IntKi) :: DebugLevel logical :: PreInitDone = .false. !------------------------------------------------------------------------------------ @@ -84,7 +84,7 @@ MODULE SeaState_C_Binding !> Set environment variables -subroutine SeaSt_C_PreInit(Gravity_C, WtrDens_C, WtrDpth_C, MSL2SWL_C, DebugLevel_In, OutVTKDir_C, WrVTK_in, WrVTK_inDT, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_PreInit') +subroutine SeaSt_C_PreInit(Gravity_C, WtrDens_C, WtrDpth_C, MSL2SWL_C, DebugLevel_C, OutVTKDir_C, WrVTK_in, WrVTK_inDT, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_PreInit') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_PreInit !GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_PreInit @@ -93,14 +93,13 @@ subroutine SeaSt_C_PreInit(Gravity_C, WtrDens_C, WtrDpth_C, MSL2SWL_C, DebugLeve real(c_float), intent(in ) :: WtrDens_C real(c_float), intent(in ) :: WtrDpth_C real(c_float), intent(in ) :: MSL2SWL_C - integer(c_int), intent(in ) :: DebugLevel_In + integer(c_int), intent(in ) :: DebugLevel_C 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] real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes integer(c_int), intent( out) :: ErrStat_C character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) - character(kind=C_CHAR, len=IntfStrLen), pointer :: InputString !< Input string as a single string with NULL chracter separating lines integer :: ErrStat, ErrStat2 character(ErrMsgLen) :: ErrMsg, ErrMsg2 integer :: i,j,k @@ -114,8 +113,13 @@ subroutine SeaSt_C_PreInit(Gravity_C, WtrDens_C, WtrDpth_C, MSL2SWL_C, DebugLeve call DispCopyrightLicense( SeaSt_ProgDesc%Name ) call DispCompileRuntimeInfo( SeaSt_ProgDesc%Name ) + ! Store the out root dir - do this before ShowPassedData call + vtk%outdir = TRANSFER( OutVTKDir_C, vtk%outdir ) + i = INDEX(vtk%outdir,C_NULL_CHAR) - 1 ! if this has a c null character at the end... + if ( i > 0 ) vtk%outdir = vtk%outdir(1:I) ! remove it + ! interface debugging - DebugLevel = int(DebugLevel_in,IntKi) + DebugLevel = int(DebugLevel_C,IntKi) ! check valid debug level, show passed data if >0 if (DebugLevel < 0_IntKi) then @@ -153,10 +157,6 @@ subroutine SeaSt_C_PreInit(Gravity_C, WtrDens_C, WtrDpth_C, MSL2SWL_C, DebugLeve endif if (vtk%write > 0_IntKi) then - ! Store the out root dir - vtk%outdir = TRANSFER( OutVTKDir_C, vtk%outdir ) - i = INDEX(vtk%outdir,C_NULL_CHAR) - 1 ! if this has a c null character at the end... - if ( i > 0 ) vtk%outdir = vtk%outdir(1:I) ! remove it ! Tell SeaState to generate the visualization using default grid InitInp%SurfaceVis = .true. InitInp%SurfaceVisNx = 0 ! use the WaveField grid resolution @@ -179,8 +179,6 @@ 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() - ! character(1) :: TmpFlag - ! integer :: i,j call WrScr("-----------------------------------------------------------") call WrScr("Interface debugging: SeaSt_C_PreInit") call WrScr(" --------------------------------------------------------") @@ -188,8 +186,8 @@ subroutine ShowPassedData() call WrScr(" WtrDens_C -> "//trim(Num2LStr(WtrDens_C))) call WrScr(" WtrDpth_C -> "//trim(Num2LStr(WtrDpth_C))) call WrScr(" MSL2SWL_C -> "//trim(Num2LStr(MSL2SWL_C))) - call WrScr(" DebugLevel_In -> "//trim(Num2LStr(DebugLevel_In))) - call WrScr(" OutVTKDir_C (ptr addr) -> "//trim(Num2LStr(LOC(OutVTKDir_C)))) + call WrScr(" DebugLevel_C -> "//trim(Num2LStr(DebugLevel_C))) + call WrScr(" OutVTKDir_C -> "//trim(vtk%outdir)) call WrScr(" WrVTK_in -> "//trim(Num2LStr(WrVTK_in))) call WrScr(" WrVTK_inDT -> "//trim(Num2LStr(WrVTK_inDT))) call WrScr("-----------------------------------------------------------") @@ -198,15 +196,16 @@ end subroutine SeaSt_C_PreInit !> Initialize the library (PreInit must be called first) -subroutine SeaSt_C_Init(InputFile_C, OutRootName_C, NSteps_C, TimeInterval_C, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_Init') +subroutine SeaSt_C_Init(InputFile_C, OutRootName_C, TimeInterval_C, TMax_C, WaveTimeShift_C, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_Init') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_Init !GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_Init #endif - type(c_ptr), intent(in ) :: InputFile_C - type(c_ptr), intent(in ) :: OutRootName_C - integer(c_int), intent(in ) :: NSteps_C - real(c_float), intent(in ) :: TimeInterval_C + character(kind=c_char), intent(in ) :: InputFile_C(IntfStrLen) + character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) + real(c_double), intent(in ) :: TimeInterval_C + real(c_double), intent(in ) :: TMax_c + real(c_double), intent(in ) :: WaveTimeShift_C integer(c_int), intent( out) :: NumChannels_C character(kind=c_char), intent( out) :: OutputChannelNames_C(ChanLen*MaxOutPts+1) character(kind=c_char), intent( out) :: OutputChannelUnits_C(ChanLen*MaxOutPts+1) @@ -214,11 +213,7 @@ subroutine SeaSt_C_Init(InputFile_C, OutRootName_C, NSteps_C, TimeInterval_C, Nu character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) ! Local variables - character(kind=C_CHAR, len=IntfStrLen), pointer :: InputFileString !< Input file as a single string with NULL chracter separating lines - character(kind=C_CHAR, len=IntfStrLen), pointer :: OutputFileString !< Input file as a single string with NULL chracter separating lines - character(IntfStrLen) :: InputFileName character(IntfStrLen) :: OutRootName - character(1024) :: vtkroot real(DbKi) :: Interval !< DT for calling integer :: ErrStat, ErrStat2 character(ErrMsgLen) :: ErrMsg, ErrMsg2 @@ -239,20 +234,24 @@ subroutine SeaSt_C_Init(InputFile_C, OutRootName_C, NSteps_C, TimeInterval_C, Nu call DispCopyrightLicense( SeaSt_ProgDesc%Name ) call DispCompileRuntimeInfo( SeaSt_ProgDesc%Name ) - if (DebugLevel > 0_IntKi) call ShowPassedData() - ! Input files - call C_F_POINTER(InputFile_C, InputFileString) ! Get a pointer to the input file string - InputFileName = FileNameFromCString(InputFileString, IntfStrLen) ! convert the input file name from c_char to fortran character + ! Input file + InitInp%InputFile = TRANSFER( InputFile_C, InitInp%InputFile ) + i = INDEX(InitInp%InputFile,C_NULL_CHAR) - 1 ! if this has a c null character at the end... + if ( i > 0 ) InitInp%InputFile = InitInp%InputFile(1:I) ! remove it - call C_F_POINTER(OutRootName_C, OutputFileString) ! Get a pointer to the input file string - OutRootName = FileNameFromCString(OutputFileString, IntfStrLen) ! convert the input file name from c_char to fortran character + ! OutRootName - this should be relative to current location + InitInp%OutRootName = TRANSFER( OutRootName_C, InitInp%OutRootName ) + i = INDEX(InitInp%OutRootName,C_NULL_CHAR) - 1 ! if this has a c null character at the end... + if ( i > 0 ) InitInp%OutRootName = InitInp%OutRootName(1:I) ! remove it + vtk%OutRootName = InitInp%OutRootName ! store for vtk (will modify below) + + ! Debugging interface + if (DebugLevel > 0_IntKi) call ShowPassedData() ! Set other inputs for calling SeaSt_Init - InitInp%InputFile = InputFileName - InitInp%UseInputFile = .TRUE. - InitInp%OutRootName = OutRootName - InitInp%TMax = (NSteps_C - 1) * TimeInterval_C ! Using this to match the SeaState driver; could otherwise get TMax directly + InitInp%UseInputFile = .TRUE. ! don't allow passing of full file contents as a string + InitInp%TMax = real(TMax_c, DbKi) InitInp%WaveFieldMod = 0_IntKi InitInp%WrWvKinMod = 0_IntKi InitInp%Linearize = .false. @@ -260,6 +259,7 @@ subroutine SeaSt_C_Init(InputFile_C, OutRootName_C, NSteps_C, TimeInterval_C, Nu InitInp%WaveFieldMod = 0 ! does not currently support moving platform. Not really necessary though since can directly get data in absolute coords InitInp%PtfmLocationX = 0.0_ReKi InitInp%PtfmLocationY = 0.0_ReKi + InitInp%WaveTimeShift = real(WaveTimeShift_C,DbKi) call SeaSt_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOutData, ErrStat2, ErrMsg2 ) if (Failed()) return @@ -282,30 +282,7 @@ subroutine SeaSt_C_Init(InputFile_C, OutRootName_C, NSteps_C, TimeInterval_C, Nu OutputChannelUnits_C(k) = C_NULL_CHAR if (vtk%write > 0_IntKi) then - ! check dt (can't check against Interval since that is never set, so just make sure it is positive) - if (vtk%dt <= 0.0) vtk%dt = 0.25 - if (allocated(InitOutData%WaveElevVisGrid)) then - vtk%NWaveElevPts(1) = size(InitOutData%WaveElevVisX) - vtk%NWaveElevPts(2) = size(InitOutData%WaveElevVisY) - call move_alloc(InitOutData%WaveElevVisX, vtk%WaveElevVisX) - call move_alloc(InitOutData%WaveElevVisY, vtk%WaveElevVisY) - call move_alloc(InitOutData%WaveElevVisGrid,vtk%WaveElevVisGrid ) - else - vtk%NWaveElevPts = 0 - vtk%write = 0 ! FIXME throw warning if we do this - endif - ! get the name of the output directory for vtk files (in a subdirectory called "vtk" of the output directory), and - ! create the VTK directory if it does not exist - call GetPath ( OutRootName, vtk%OutRootName, vtkroot ) ! the returned vtk%OutRootName includes a file separator character at the end - if (PathIsRelative(trim(vtk%OutRootName))) then - vtk%OutRootName = trim(vtk%OutRootName) // trim(vtk%outdir) - else - vtk%OutRootName = trim(vtk%outdir) - endif - call MKDIR( trim(vtk%OutRootName) ) - vtk%OutRootName = trim( vtk%OutRootName ) // PathSep // trim(vtkroot) - call WrVTK_WaveElevVisGrid (0.0_DbKi, vtk, ErrStat2, ErrMsg2) - if (Failed()) return + call VTKsetup() endif @@ -322,17 +299,38 @@ 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() - character(1) :: TmpFlag - ! integer :: i,j call WrScr("-----------------------------------------------------------") call WrScr("Interface debugging: SeaSt_C_Init") call WrScr(" --------------------------------------------------------") - call WrScr(" InputFile_C (ptr addr) -> "//trim(Num2LStr(LOC(InputFile_C)))) - call WrScr(" OutRootName_C (ptr addr) -> "//trim(Num2LStr(LOC(OutRootName_C)))) - call WrScr(" NSteps_C - > "//trim(Num2LStr(NSteps_C))) + call WrScr(" InputFile_C -> "//trim(InitInp%InputFile)) + call WrScr(" OutRootName_C -> "//trim(InitInp%OutRootName)) + call WrScr(" TMax_C -> "//trim(Num2LStr(TMax_C))) call WrScr(" TimeInterval_C -> "//trim(Num2LStr(TimeInterval_C))) + call WrScr(" WaveTimeShift_C -> "//trim(Num2LStr(WaveTimeShift_C))) call WrScr("-----------------------------------------------------------") end subroutine ShowPassedData + + subroutine VTKsetup() + ! check dt (can't check against Interval since that is never set, so just make sure it is positive) + if (vtk%dt <= 0.0) vtk%dt = 0.25 + ! move data + if (allocated(InitOutData%WaveElevVisGrid)) then + vtk%NWaveElevPts(1) = size(InitOutData%WaveElevVisX) + vtk%NWaveElevPts(2) = size(InitOutData%WaveElevVisY) + call move_alloc(InitOutData%WaveElevVisX, vtk%WaveElevVisX) + call move_alloc(InitOutData%WaveElevVisY, vtk%WaveElevVisY) + call move_alloc(InitOutData%WaveElevVisGrid,vtk%WaveElevVisGrid ) + else + vtk%NWaveElevPts = 0 + vtk%write = 0 ! FIXME throw warning if we do this + endif + ! get the name of the output directory for vtk files (in a subdirectory called "vtk" of the output directory), and + ! create the VTK directory if it does not exist + call MKDIR( trim(vtk%outdir) ) + vtk%OutRootName = trim(vtk%outdir) // PathSep //trim( vtk%OutRootName ) + call WrVTK_WaveElevVisGrid (0.0_DbKi, vtk, ErrStat2, ErrMsg2) + if (Failed()) return + end subroutine VTKsetup end subroutine SeaSt_C_Init subroutine SeaSt_C_CalcOutput(Time_C, OutputChannelValues_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_CalcOutput') diff --git a/modules/seastate/src/SeaState_DriverCode.f90 b/modules/seastate/src/SeaState_DriverCode.f90 index 53e33165f3..aec84e8ba3 100644 --- a/modules/seastate/src/SeaState_DriverCode.f90 +++ b/modules/seastate/src/SeaState_DriverCode.f90 @@ -161,6 +161,7 @@ program SeaStateDriver InitInData%OutRootName = drvrInitInp%OutRootName InitInData%TMax = (drvrInitInp%NSteps-1) * drvrInitInp%TimeInterval ! Starting time is always t = 0.0 InitInData%HasIce = .false. + InitInData%WaveTimeShift = 0.0_DbKi ! for phase shifting wave field in time (positive value only) ! Get the current time call date_and_time ( Values=StrtTime ) ! Let's time the whole simulation diff --git a/modules/seastate/src/SeaState_Output.f90 b/modules/seastate/src/SeaState_Output.f90 index 4f56ea9339..64d9f60677 100644 --- a/modules/seastate/src/SeaState_Output.f90 +++ b/modules/seastate/src/SeaState_Output.f90 @@ -1038,6 +1038,7 @@ SUBROUTINE SeaStOut_WrSummaryFile(InitInp, InputFileData, p, ErrStat, ErrMsg ) p%WaveField%EffWtrDpth, '(m) relative to SWL' WRITE( UnSum, '(1X,A15,F8.2,A20,F8.2,A19/)' ) 'Grid Z_Depth : ', InputFileData%Z_Depth - p%WaveField%MSL2SWL, '(m) relative to MSL; ', & InputFileData%Z_Depth, '(m) relative to SWL' + WRITE( UnSum, '(1X,A50,F10.5,A4)' ) 'WaveTimeShift: (applied at WaveField data access) ', p%WaveField%WaveTimeShift,' (s)' end if Frmt = '(1X,ES18.4e2,2x,ES18.4e2,2x,ES18.4e2,2x,ES18.4e2)' diff --git a/modules/seastate/src/SeaState_Types.f90 b/modules/seastate/src/SeaState_Types.f90 index 03017a6f11..7d19e1e2bf 100644 --- a/modules/seastate/src/SeaState_Types.f90 +++ b/modules/seastate/src/SeaState_Types.f90 @@ -93,6 +93,7 @@ MODULE SeaState_Types REAL(ReKi) :: defWtrDpth = 0.0_ReKi !< Default water depth from the driver; may be overwritten [m] REAL(ReKi) :: defMSL2SWL = 0.0_ReKi !< Default mean sea level to still water level from the driver; may be overwritten [m] REAL(DbKi) :: TMax = 0.0_R8Ki !< Supplied by Driver: The total simulation time [(sec)] + REAL(DbKi) :: WaveTimeShift = 0 !< Add this to the time to effectively phase shift the wave (useful for hybrid tank testing). Positive value only (advance time) [(s)] INTEGER(IntKi) :: WaveFieldMod = 0_IntKi !< Wave field handling (-) (switch) 0: use individual SeaState inputs without adjustment, 1: adjust wave phases based on turbine offsets from farm origin [-] REAL(ReKi) :: PtfmLocationX = 0.0_ReKi !< Supplied by Driver: X coordinate of platform location in the wave field [m] REAL(ReKi) :: PtfmLocationY = 0.0_ReKi !< Supplied by Driver: Y coordinate of platform location in the wave field [m] @@ -492,6 +493,7 @@ subroutine SeaSt_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, Err DstInitInputData%defWtrDpth = SrcInitInputData%defWtrDpth DstInitInputData%defMSL2SWL = SrcInitInputData%defMSL2SWL DstInitInputData%TMax = SrcInitInputData%TMax + DstInitInputData%WaveTimeShift = SrcInitInputData%WaveTimeShift DstInitInputData%WaveFieldMod = SrcInitInputData%WaveFieldMod DstInitInputData%PtfmLocationX = SrcInitInputData%PtfmLocationX DstInitInputData%PtfmLocationY = SrcInitInputData%PtfmLocationY @@ -530,6 +532,7 @@ subroutine SeaSt_PackInitInput(RF, Indata) call RegPack(RF, InData%defWtrDpth) call RegPack(RF, InData%defMSL2SWL) call RegPack(RF, InData%TMax) + call RegPack(RF, InData%WaveTimeShift) call RegPack(RF, InData%WaveFieldMod) call RegPack(RF, InData%PtfmLocationX) call RegPack(RF, InData%PtfmLocationY) @@ -556,6 +559,7 @@ subroutine SeaSt_UnPackInitInput(RF, OutData) call RegUnpack(RF, OutData%defWtrDpth); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%defMSL2SWL); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%TMax); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WaveTimeShift); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%WaveFieldMod); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%PtfmLocationX); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%PtfmLocationY); if (RegCheckErr(RF, RoutineName)) return diff --git a/modules/servodyn/src/ServoDyn.f90 b/modules/servodyn/src/ServoDyn.f90 index ad9c588d50..f8a5a0a0df 100644 --- a/modules/servodyn/src/ServoDyn.f90 +++ b/modules/servodyn/src/ServoDyn.f90 @@ -1037,7 +1037,7 @@ subroutine CheckInfo() do i=1,size(InitOut%LinNames_y) Flag='F' if (InitOut%RotFrame_y(i)) Flag='T' - call WrFileNR(CU,' '//Num2LStr(i)//Flag//' '//InitOut%LinNames_y(i)//NewLine) + call WrScr(' '//Num2LStr(i)//Flag//' '//InitOut%LinNames_y(i)) enddo endif if (allocated(InitOut%LinNames_x)) then @@ -1059,13 +1059,13 @@ subroutine CheckInfo() do i=1,size(InitOut%LinNames_x) Flag='F' if (InitOut%RotFrame_x(i)) Flag='T' - call WrFileNR(CU,' '//Num2LStr(i)//Flag//' '//trim(Num2LStr(InitOut%DerivOrder_x(i)))//' '//InitOut%LinNames_x(i)//NewLine) + call WrScr(' '//Num2LStr(i)//Flag//' '//trim(Num2LStr(InitOut%DerivOrder_x(i)))//' '//InitOut%LinNames_x(i)) enddo endif if (allocated(InitOut%LinNames_u)) then call WrScr('Perturb Size u') do i=1,size(p%du) - call WrFileNR(CU,' '//trim(Num2LStr(i))//' '//trim(Num2LStr(p%du(i)))//NewLine) + call WrScr(' '//trim(Num2LStr(i))//' '//trim(Num2LStr(p%du(i)))) enddo call WrScr('LinNames_u') do j=1,p%NumBStC @@ -1087,9 +1087,9 @@ subroutine CheckInfo() FlagLoad='F' if (InitOut%RotFrame_u(i)) Flag='T' if (InitOut%IsLoad_u(i)) FlagLoad='T' - call WrFileNR(CU,' '//Num2LStr(i)//Flag//' '//FlagLoad//' ('// & + call WrScr(' '//Num2LStr(i)//Flag//' '//FlagLoad//' ('// & trim(Num2LStr(p%Jac_u_indx(i,1)))//','//trim(Num2LStr(p%Jac_u_indx(i,2)))//','//trim(Num2LStr(p%Jac_u_indx(i,3)))// & - ') '//InitOut%LinNames_u(i)//NewLine) + ') '//InitOut%LinNames_u(i)) enddo endif end subroutine CheckInfo diff --git a/modules/version/tests/VersionInfo_test_tools.F90 b/modules/version/tests/VersionInfo_test_tools.F90 index 30e67ae0f7..de6a95e11b 100644 --- a/modules/version/tests/VersionInfo_test_tools.F90 +++ b/modules/version/tests/VersionInfo_test_tools.F90 @@ -12,16 +12,14 @@ module versioninfo_test_tools character(11), parameter :: terminal="/dev/stdout" #endif -integer, parameter :: stdout=CU - contains subroutine hide_terminal_output() - open(unit=stdout, file=trim(nullfile)) + open(unit=CU, file=trim(nullfile)) end subroutine subroutine show_terminal_output() - open(unit=stdout, file=terminal, status="old") + open(unit=CU, file=terminal, status="old") end subroutine end module diff --git a/reg_tests/CTestList.cmake b/reg_tests/CTestList.cmake index b4725e8ddc..90e2d03dbe 100644 --- a/reg_tests/CTestList.cmake +++ b/reg_tests/CTestList.cmake @@ -316,6 +316,15 @@ function(py_openfast_io_library_pytest TESTNAME LABEL) endfunction(py_openfast_io_library_pytest) +# py_wavetank +function(py_wavetank_regression TESTNAME LABEL) + set(TEST_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/executeWavetankPyRegressionCase.py") + set(SEASTATE_EXECUTABLE "${Python_EXECUTABLE}") + set(SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/..") + set(BUILD_DIRECTORY "${CTEST_BINARY_DIR}/glue-codes/other") + regression(${TEST_SCRIPT} ${SEASTATE_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} " " ${TESTNAME} "${LABEL}" " ") +endfunction(py_wavetank_regression) + #=============================================================================== # Regression tests #=============================================================================== @@ -356,6 +365,7 @@ of_regression("StC_test_OC4Semi" "openfast;servodyn;hydrod of_regression("MHK_RM1_Fixed" "openfast;elastodyn;aerodyn;mhk;offshore") of_regression("MHK_RM1_Floating" "openfast;elastodyn;aerodyn;hydrodyn;moordyn;mhk;offshore") of_regression("MHK_RM1_Floating_wNacDrag" "openfast;elastodyn;aerodyn;hydrodyn;moordyn;mhk;offshore") +of_regression("MHK_RM1_Floating_Tank-scaled" "openfast;elastodyn;aerodyn;hydrodyn;moordyn;mhk;offshore;scaled") of_regression("Tailfin_FreeYaw1DOF_PolarBased" "openfast;elastodyn;aerodyn") of_regression("Tailfin_FreeYaw1DOF_Unsteady" "openfast;elastodyn;aerodyn") of_regression("5MW_Land_DLL_WTurb_ADsk" "openfast;elastodyn;aerodisk") @@ -550,3 +560,6 @@ adsk_regression("adsk_timeseries_shutdown" "aerodisk") # SimplifiedElastoDyn regression tests sed_regression("sed_test_HSSbrk" "simple-elastodyn") sed_regression("sed_test_freewheel" "simple-elastodyn") + +# Wavetank library interface (MD + SS + AD) +py_wavetank_regression("py_wavetank_test1" "wavetank;aerodyn;moordyn;seastate;python;scaled") diff --git a/reg_tests/executeWavetankPyRegressionCase.py b/reg_tests/executeWavetankPyRegressionCase.py new file mode 100644 index 0000000000..506702716f --- /dev/null +++ b/reg_tests/executeWavetankPyRegressionCase.py @@ -0,0 +1,149 @@ +# +# Copyright 2025 National Renewable Energy Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" + This program executes the WaveTank library interface through python interface + for a single test case. The test data is contained in a git submodule, r-test, + which must be initialized prior to running. See the r-test README or OpenFAST + documentation for more info. + + Get usage with: `executeWavetankPyRegressionCase.py -h` +""" + +import os +import sys +basepath = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.sep.join([basepath, "lib"])) +import argparse +import numpy as np +import shutil +import glob +import subprocess +import rtestlib as rtl +import openfastDrivers +import pass_fail +from errorPlotting import exportCaseSummary + +##### Helper functions +excludeExt=['.out','.outb','.ech','.yaml','.sum','.log'] + +##### Main program + +### Store the python executable for future python calls +pythonCommand = sys.executable + +### Verify input arguments +parser = argparse.ArgumentParser(description="Executes wavetank c-bindings library interface with a regression test for a single test case.") +parser.add_argument("caseName", metavar="Case-Name", type=str, nargs=1, help="The name of the test case.") +parser.add_argument("executable", metavar="WaveTank-Python", type=str, nargs=1, help="The path to the wavetank python driver case.") +parser.add_argument("sourceDirectory", metavar="path/to/openfast_repo", type=str, nargs=1, help="The path to the OpenFAST repository.") +parser.add_argument("buildDirectory", metavar="path/to/openfast_repo/build", type=str, nargs=1, help="The path to the OpenFAST repository build directory.") +parser.add_argument("rtol", metavar="Relative-Tolerance", type=float, nargs=1, help="Relative tolerance to allow the solution to deviate; expressed as order of magnitudes less than baseline.") +parser.add_argument("atol", metavar="Absolute-Tolerance", type=float, nargs=1, help="Absolute tolerance to allow small values to pass; expressed as order of magnitudes less than baseline.") +parser.add_argument("-p", "-plot", dest="plot", action='store_true', help="bool to include plots in failed cases") +parser.add_argument("-n", "-no-exec", dest="noExec", action='store_true', help="bool to prevent execution of the test cases") +parser.add_argument("-v", "-verbose", dest="verbose", action='store_true', help="bool to include verbose system output") + +args = parser.parse_args() + +caseName = args.caseName[0] +executable = args.executable[0] +sourceDirectory = args.sourceDirectory[0] +buildDirectory = args.buildDirectory[0] +rtol = args.rtol[0] +atol = args.atol[0] +plotError = args.plot if args.plot is False else True +noExec = args.noExec if args.noExec is False else True +verbose = args.verbose if args.verbose is False else True + +# validate inputs +rtl.validateExeOrExit(executable) +rtl.validateDirOrExit(sourceDirectory) +if not os.path.isdir(buildDirectory): + os.makedirs(buildDirectory, exist_ok=True) + +### Build the filesystem navigation variables for running the test case +regtests = os.path.join(sourceDirectory, "reg_tests") +lib = os.path.join(regtests, "lib") +rtest = os.path.join(regtests, "r-test") +moduleDirectory = os.path.join(rtest, "glue-codes", "other") +inputsDirectory = os.path.join(moduleDirectory, caseName) +targetOutputDirectory = os.path.join(inputsDirectory) +testBuildDirectory = os.path.join(buildDirectory, caseName) + +dependsDir = os.path.join("..", "..", "openfast", "MHK_RM1_Floating_Tank-scaled") + +# verify all the required directories exist +if not os.path.isdir(rtest): + rtl.exitWithError("The test data directory, {}, does not exist. If you haven't already, run `git submodule update --init --recursive`".format(rtest)) +if not os.path.isdir(targetOutputDirectory): + rtl.exitWithError("The test data outputs directory, {}, does not exist. Try running `git submodule update`".format(targetOutputDirectory)) +if not os.path.isdir(inputsDirectory): + rtl.exitWithError("The test data inputs directory, {}, does not exist. Verify your local repository is up to date.".format(inputsDirectory)) + +# create the local output directory if it does not already exist +# and initialize it with input files for all test cases +if not os.path.isdir(testBuildDirectory): + rtl.copyTree(inputsDirectory, testBuildDirectory, excludeExt) + +# Dependency +src = os.path.join(inputsDirectory, dependsDir) +dst = os.path.join(testBuildDirectory, dependsDir) +if not os.path.isdir(dst): + rtl.copyTree(src, dst, excludeExt) + +### Run inflowwind on the test case +if not noExec: + caseInputFile = os.path.join(testBuildDirectory, "py_wavetank_driver.py") + returnCode = openfastDrivers.runInflowwindDriverCase(caseInputFile, executable) + if returnCode != 0: + sys.exit(returnCode*10) + +### Build the filesystem navigation variables for running the regression test. +# the "Points.Results.dat" file is from calls to routines other than CalcOutput +localOutFile = os.path.join(testBuildDirectory, "FRM1Q_Floating_tank_test.out") +baselineOutFile = os.path.join(targetOutputDirectory, "FRM1Q_Floating_tank_test.out") +rtl.validateFileOrExit(localOutFile) +rtl.validateFileOrExit(baselineOutFile) + +testData, testInfo, _ = pass_fail.readFASTOut(localOutFile) +baselineData, baselineInfo, _ = pass_fail.readFASTOut(baselineOutFile) + +passing_channels = pass_fail.passing_channels(testData.T, baselineData.T, rtol, atol) +passing_channels = passing_channels.T + +norms = pass_fail.calculateNorms(testData, baselineData) + +# export all case summaries +channel_names = testInfo["attribute_names"] +exportCaseSummary(testBuildDirectory, caseName, channel_names, passing_channels, norms) + +# passing case +if np.all(passing_channels): + sys.exit(0) + +# failing case +if plotError: + from errorPlotting import finalizePlotDirectory, plotOpenfastError + for channel in testInfo["attribute_names"]: + try: + plotOpenfastError(localOutFile, baselineOutFile, channel, rtol, atol) + except: + error = sys.exc_info()[1] + print("Error generating plots: {}".format(error)) + finalizePlotDirectory(localOutFile, testInfo["attribute_names"], caseName) + +sys.exit(1) diff --git a/reg_tests/r-test b/reg_tests/r-test index ecdfa25bfe..b5145a9acd 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit ecdfa25bfe21ccbe3987133f3fdbc35458aaae1a +Subproject commit b5145a9acd1a366b883cd4cd2455a68e2d318e20 diff --git a/requirements.txt b/requirements.txt index ad406e0555..1006eeb363 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ numpy vtk Bokeh>=2.4,!=3.0.0,!=3.0.1,!=3.0.2,!=3.0.3 -pytest \ No newline at end of file +pytest +nptdms