From e3d6fe2e0897eeb0061763eaa28816900255b859 Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Wed, 22 Apr 2020 14:23:53 -0600 Subject: [PATCH 01/16] InflowWind synchronization InflowWind driver: ---- - functionality: - added option to convert file formats - input file changes: - added section for file conversion options - moved dt after timeStart (before summary stuff) - removed inflowWind setup section header - skip reading GridCtrCoord, GridD{x|y|z}, and GridN{x|y|z} lines if they aren't going to be used (and don't print them in summary file) InflowWind: --- - added code to include vertical flow angle (to rotate FF wind boxes vertically, too) - added base FF wind module so TurbSim, Bladed, and HAWC2 formats can use common code (all three formats use the same base code for interpolation, mean wind speed, etc) - added LHR rule for Bladed-generated winds - option for native Bladed file formats - return 0 wind speed at and below the ground height (z=0) instead of producing an error --- modules/aerodyn14/src/AeroDyn14_Types.f90 | 1 + modules/aerodyn14/src/DWM_Types.f90 | 1 + modules/inflowwind/CMakeLists.txt | 3 + modules/inflowwind/src/IfW_BladedFFWind.f90 | 1132 ++++++++-------- modules/inflowwind/src/IfW_BladedFFWind.txt | 37 +- .../inflowwind/src/IfW_BladedFFWind_Types.f90 | 452 ++----- modules/inflowwind/src/IfW_FFWind_Base.f90 | 1163 +++++++++++++++++ modules/inflowwind/src/IfW_FFWind_Base.txt | 53 + .../inflowwind/src/IfW_FFWind_Base_Types.f90 | 711 ++++++++++ modules/inflowwind/src/IfW_HAWCWind.f90 | 535 +------- modules/inflowwind/src/IfW_HAWCWind.txt | 46 +- modules/inflowwind/src/IfW_HAWCWind_Types.f90 | 454 +++---- modules/inflowwind/src/IfW_TSFFWind.f90 | 536 ++------ modules/inflowwind/src/IfW_TSFFWind.txt | 27 +- modules/inflowwind/src/IfW_TSFFWind_Types.f90 | 433 ++---- modules/inflowwind/src/IfW_UniformWind.f90 | 155 ++- modules/inflowwind/src/IfW_UserWind.f90 | 4 +- modules/inflowwind/src/InflowWind.f90 | 410 ++++-- modules/inflowwind/src/InflowWind.txt | 46 +- modules/inflowwind/src/InflowWind_Driver.f90 | 73 +- .../inflowwind/src/InflowWind_Driver_Subs.f90 | 428 +++--- .../src/InflowWind_Driver_Types.f90 | 4 + modules/inflowwind/src/InflowWind_Subs.f90 | 280 ++-- modules/inflowwind/src/InflowWind_Types.f90 | 222 ++-- modules/openfast-library/src/FAST_Types.f90 | 1 + vs-build/FASTlib/FASTlib.vfproj | 51 +- vs-build/RunRegistry.bat | 1 + 27 files changed, 4042 insertions(+), 3217 deletions(-) create mode 100644 modules/inflowwind/src/IfW_FFWind_Base.f90 create mode 100644 modules/inflowwind/src/IfW_FFWind_Base.txt create mode 100644 modules/inflowwind/src/IfW_FFWind_Base_Types.f90 diff --git a/modules/aerodyn14/src/AeroDyn14_Types.f90 b/modules/aerodyn14/src/AeroDyn14_Types.f90 index ad4321d6bf..ac7ba02436 100644 --- a/modules/aerodyn14/src/AeroDyn14_Types.f90 +++ b/modules/aerodyn14/src/AeroDyn14_Types.f90 @@ -32,6 +32,7 @@ MODULE AeroDyn14_Types !--------------------------------------------------------------------------------------------------------------------------------- USE IfW_UniformWind_Types +USE IfW_FFWind_Base_Types USE IfW_TSFFWind_Types USE IfW_BladedFFWind_Types USE IfW_HAWCWind_Types diff --git a/modules/aerodyn14/src/DWM_Types.f90 b/modules/aerodyn14/src/DWM_Types.f90 index 0678240e6c..414686d853 100644 --- a/modules/aerodyn14/src/DWM_Types.f90 +++ b/modules/aerodyn14/src/DWM_Types.f90 @@ -32,6 +32,7 @@ MODULE DWM_Types !--------------------------------------------------------------------------------------------------------------------------------- USE IfW_UniformWind_Types +USE IfW_FFWind_Base_Types USE IfW_TSFFWind_Types USE IfW_BladedFFWind_Types USE IfW_HAWCWind_Types diff --git a/modules/inflowwind/CMakeLists.txt b/modules/inflowwind/CMakeLists.txt index b590760485..8604785199 100644 --- a/modules/inflowwind/CMakeLists.txt +++ b/modules/inflowwind/CMakeLists.txt @@ -23,6 +23,7 @@ if (GENERATE_TYPES) generate_f90_types(src/IfW_TSFFWind.txt ${CMAKE_CURRENT_LIST_DIR}/src/IfW_TSFFWind_Types.f90 -noextrap) generate_f90_types(src/IfW_UniformWind.txt ${CMAKE_CURRENT_LIST_DIR}/src/IfW_UniformWind_Types.f90 -noextrap) generate_f90_types(src/IfW_UserWind.txt ${CMAKE_CURRENT_LIST_DIR}/src/IfW_UserWind_Types.f90 -noextrap) + generate_f90_types(src/IfW_FFWind_Base.txt ${CMAKE_CURRENT_LIST_DIR}/src/IfW_FFWind_Base_Types.f90 -noextrap) endif() set(IFW_SOURCES @@ -35,6 +36,8 @@ set(IFW_SOURCES src/InflowWind_Subs.f90 src/InflowWind.f90 src/Lidar.f90 + src/IfW_FFWind_Base.f90 + src/IfW_FFWind_Base_Types.f90 src/IfW_BladedFFWind_Types.f90 src/IfW_4Dext_Types.f90 src/IfW_HAWCWind_Types.f90 diff --git a/modules/inflowwind/src/IfW_BladedFFWind.f90 b/modules/inflowwind/src/IfW_BladedFFWind.f90 index dac44e47c8..620d387016 100644 --- a/modules/inflowwind/src/IfW_BladedFFWind.f90 +++ b/modules/inflowwind/src/IfW_BladedFFWind.f90 @@ -36,6 +36,7 @@ MODULE IfW_BladedFFWind USE NWTC_Library USE IfW_BladedFFWind_Types + USE IfW_FFWind_Base IMPLICIT NONE PRIVATE @@ -55,18 +56,155 @@ MODULE IfW_BladedFFWind !! 09/25/1997 - Created by M. Buhl from GETFILES in ViewWind. !! 09/23/2009 - modified by B. Jonkman: this subroutine was split into several subroutines (was ReadFF) !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 -SUBROUTINE IfW_BladedFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOutData, ErrStat, ErrMsg) - IMPLICIT NONE +SUBROUTINE IfW_BladedFFWind_Init(InitInp, ParamData, MiscVars, InitOutData, ErrStat, ErrMsg) CHARACTER(*), PARAMETER :: RoutineName="IfW_BladedFFWind_Init" ! Passed Variables - TYPE(IfW_BladedFFWind_InitInputType), INTENT(IN ) :: InitData !< Initialization data passed to the module + TYPE(IfW_BladedFFWind_InitInputType), INTENT(IN ) :: InitInp !< Initialization data passed to the module TYPE(IfW_BladedFFWind_ParameterType), INTENT( OUT) :: ParamData !< Parameters TYPE(IfW_BladedFFWind_MiscVarType), INTENT( OUT) :: MiscVars !< misc/optimization data (storage for the main data) TYPE(IfW_BladedFFWind_InitOutputType), INTENT( OUT) :: InitOutData !< Initial output - REAL(DbKi), INTENT(IN ) :: Interval !< Time Interval to use (passed through here) + + ! Error Handling + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< determines if an error has been encountered + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about errors + + + ! Temporary variables for error handling + REAL(ReKi) :: TI(3) + REAL(ReKi) :: ScaleFactors(3) + INTEGER(IntKi) :: TmpErrStat ! temporary error status + CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message + TYPE(IfW_FFWind_InitInputType) :: FF_InitInp ! Initialization input data for FF scaling + + + + ErrMsg = '' + ErrStat = ErrID_None + + + CALL ReadFiles(InitInp, FF_InitInp, InitOutData, ParamData, TI, TmpErrStat, TmpErrMsg) + CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) + IF ( ErrStat >= AbortErrLev ) RETURN + + !------------------------------------------------------------------------------------------------- + ! If the wind file has zero-mean and unit standard deviation (native Bladed format), scale the data: + !------------------------------------------------------------------------------------------------- + ParamData%FF%AddMeanAfterInterp = .false. + ParamData%FF%WindProfileType = FF_InitInp%WindProfileType + ParamData%FF%Z0 = FF_InitInp%Z0 + ParamData%FF%PLExp = FF_InitInp%PLExp + + if (InitInp%NativeBladedFmt) then + ParamData%FF%InterpTower = .true. + + ! Validate scaling data if we've got native-Bladed format + CALL FFWind_ValidateInput(FF_InitInp, ParamData%FF%NFFComp, TmpErrStat, TmpErrMsg) + CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) + IF ( ErrStat >= AbortErrLev ) RETURN + + ! scale to requested TI (or use requested scale factors) + call ScaleTurbulence(FF_InitInp, ParamData%FF%FFData(:,:,:,1:ParamData%FF%NFFSteps), ScaleFactors, TmpErrStat, TmpErrMsg) + CALL SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + ! Add the mean wind speed to the u component. + call AddMeanVelocity(FF_InitInp, ParamData%FF%GridBase, 1.0_ReKi/ParamData%FF%InvFFZD, ParamData%FF%FFData) + else + ParamData%FF%InterpTower = .false. + end if + + + IF (ParamData%FF%Periodic) THEN + ParamData%FF%InitXPosition = 0 ! start at the hub + ParamData%FF%TotalTime = ParamData%FF%NFFSteps*ParamData%FF%FFDTime + ELSE + ParamData%FF%InitXPosition = ParamData%FF%FFYHWid ! start half the grid width ahead of the turbine + ParamData%FF%TotalTime = (ParamData%FF%NFFSteps-1)*ParamData%FF%FFDTime + ENDIF + + ! overwrite the offset + IF (InitInp%NativeBladedFmt) THEN + ParamData%FF%InitXPosition = FF_InitInp%XOffset + END IF + + + !------------------------------------------------------------------------------------------------- + ! Set the InitOutput information + !------------------------------------------------------------------------------------------------- + + InitOutdata%Ver = IfW_BladedFFWind_Ver + InitOutdata%TI = TI + + + + !------------------------------------------------------------------------------------------------- + ! Write to the summary file + !------------------------------------------------------------------------------------------------- + + IF ( InitInp%SumFileUnit > 0 ) THEN + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) 'Bladed-style wind type. Read by InflowWind sub-module '// & + TRIM(IfW_BladedFFWind_Ver%Name)//' '//TRIM(IfW_BladedFFWind_Ver%Ver) + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) TRIM(TmpErrMsg) + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' FileName: '//TRIM(InitInp%WindFileName) + WRITE(InitInp%SumFileUnit,'(A34,I3)', IOSTAT=TmpErrStat) ' Binary file format id: ',ParamData%FF%WindFileFormat + WRITE(InitInp%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Reference height (m): ',ParamData%FF%RefHt + WRITE(InitInp%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Timestep (s): ',ParamData%FF%FFDTime + WRITE(InitInp%SumFileUnit,'(A34,I12)', IOSTAT=TmpErrStat) ' Number of timesteps: ',ParamData%FF%NFFSteps + WRITE(InitInp%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Mean windspeed (m/s): ',ParamData%FF%MeanFFWS + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Characteristic TI: [ '// & + TRIM(Num2LStr(TI(1)))//', '//TRIM(Num2LStr(TI(2)))//', '//TRIM(Num2LStr(TI(3)))//' ] ' + WRITE(InitInp%SumFileUnit,'(A34,L1)', IOSTAT=TmpErrStat) ' Windfile is periodic: ',ParamData%FF%Periodic + WRITE(InitInp%SumFileUnit,'(A34,L1)', IOSTAT=TmpErrStat) ' Windfile includes tower: ',ParamData%FF%NTGrids > 0 + + IF ( ParamData%FF%Periodic ) THEN + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Time range (s): [ '// & + TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr(ParamData%FF%TotalTime))//' ]' + ELSE ! Shift the time range to compensate for the shifting of the wind grid + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Time range (s): [ '// & + TRIM(Num2LStr(-ParamData%FF%InitXPosition*ParamData%FF%InvMFFWS))//' : '// & + TRIM(Num2LStr(ParamData%FF%TotalTime-ParamData%FF%InitXPosition*ParamData%FF%InvMFFWS))//' ]' + ENDIF + + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Y range (m): [ '// & + TRIM(Num2LStr(-ParamData%FF%FFYHWid))//' : '//TRIM(Num2LStr(ParamData%FF%FFYHWid))//' ]' + + IF ( ParamData%FF%NTGrids > 0 ) THEN + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Z range (m): [ '// & + TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr(ParamData%FF%RefHt + ParamData%FF%FFZHWid))//' ]' + ELSE + WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Z range (m): [ '// & + TRIM(Num2LStr(ParamData%FF%RefHt - ParamData%FF%FFZHWid))//' : '//TRIM(Num2LStr(ParamData%FF%RefHt + ParamData%FF%FFZHWid))//' ]' + ENDIF + + + ! We are assuming that if the last line was written ok, then all of them were. + IF (TmpErrStat /= 0_IntKi) THEN + CALL SetErrStat(ErrID_Fatal,'Error writing to summary file.',ErrStat,ErrMsg,RoutineName) + RETURN + ENDIF + ENDIF + + + + + RETURN + +END SUBROUTINE IfW_BladedFFWind_Init +!======================================================================================================== +SUBROUTINE ReadFiles(InitInp, FF_InitInp, InitOut, ParamData, TI, ErrStat, ErrMsg) + + CHARACTER(*), PARAMETER :: RoutineName="ReadFiles" + + ! Passed Variables + TYPE(IfW_BladedFFWind_InitInputType), INTENT(IN ) :: InitInp !< Initialization data passed to the module + TYPE(IfW_FFWind_InitInputType), INTENT( OUT) :: FF_InitInp !< Initialization data for scaling + TYPE(IfW_BladedFFWind_InitOutputType), INTENT(INOUT) :: InitOut !< Initial output + TYPE(IfW_BladedFFWind_ParameterType), INTENT( OUT) :: ParamData !< Parameters + REAL(ReKi) , INTENT( OUT) :: TI (3) !< turbulence intensities of the wind components as defined in the FF file, not necessarially the actual TI ! Error Handling @@ -81,51 +219,80 @@ SUBROUTINE IfW_BladedFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOu ! Local Variables: - REAL(ReKi) :: TI (3) ! turbulence intensities of the wind components as defined in the FF file, not necessarially the actual TI REAL(ReKi) :: BinTI (3) ! turbulence intensities of the wind components as defined in the FF binary file, not necessarially the actual TI + REAL(ReKi) :: NatTI (3) ! turbulence intensities of the wind components as defined in the native FF summary file REAL(ReKi) :: UBar REAL(ReKi) :: ZCenter - INTEGER(IntKi) :: UnitWind ! Unit number for the InflowWind input file + INTEGER(IntKi) :: UnitWind ! Unit number for the InflowWind input file INTEGER(B2Ki) :: Dum_Int2 INTEGER(IntKi) :: I LOGICAL :: CWise + LOGICAL :: LHR ! Left-hand rule for Bladed files (is the v component aligned along *negative* Y?) + LOGICAL :: Exists CHARACTER( 1028 ) :: SumFile ! length is LEN(ParamData%WindFileName) + the 4-character extension. CHARACTER( 1028 ) :: TwrFile ! length is LEN(ParamData%WindFileName) + the 4-character extension. + CHARACTER(1024) :: BinFileName + CHARACTER(1024) :: PriPath - - !------------------------------------------------------------------------------------------------- - ! Initialize temporary variables - !------------------------------------------------------------------------------------------------- - + ErrMsg = '' ErrStat = ErrID_None - - TmpErrMsg = '' - TmpErrStat = ErrID_None - - + + + if (InitInp%NativeBladedFmt) then + call Read_NativeBladedSummary(InitInp%WindFileName, FF_InitInp%PLExp, NatTI, ParamData%FF%MeanFFWS, ParamData%FF%RefHt, InitOut%PropagationDir, InitOut%VFlowAngle, BinFileName, FF_InitInp%XOffset, TmpErrStat, TmpErrMsg) + CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) + IF ( ErrStat >= AbortErrLev ) RETURN + + + if (pathIsRelative(BinFileName)) then + CALL GetPath( InitInp%WindFileName, PriPath ) ! Binary file will be relative to the path where the primary input file is located. + BinFileName = TRIM(PriPath)//TRIM(BinFileName) + end if + + + ! default values for Bladed Format + CWise = .false. + ZCenter = ParamData%FF%RefHt + ParamData%FF%Periodic = .true. + + FF_InitInp%ScaleMethod = ScaleMethod_StdDev + FF_InitInp%SigmaF = NatTI * ParamData%FF%MeanFFWS + FF_InitInp%sf = FF_InitInp%SigmaF +! FF_InitInp%ScaleMethod = ScaleMethod_Direct ! Bladed files should have std of 1, so we'll just multiply (closer to what Bladed does) + + FF_InitInp%RefHt = ParamData%FF%RefHt + FF_InitInp%URef = ParamData%FF%MeanFFWS + FF_InitInp%WindProfileType = WindProfileType_PL ! it could also have logarithmic, but I'm going to leave that off for now + + TI = 100.0_ReKi + UBar = 0.0_ReKi + LHR = .true. + + else + InitOut%PropagationDir = 0.0_ReKi + InitOut%VFlowAngle = 0.0_ReKi + BinFileName = InitInp%WindFileName + end if + + ! Get a unit number to use CALL GetNewUnit(UnitWind, TmpErrStat, TmpErrMsg) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, 'IfW_BladedFFWind') + CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN - !------------------------------------------------------------------------------------------------- - ! Copy things from the InitData to the ParamData - !------------------------------------------------------------------------------------------------- - - !---------------------------------------------------------------------------------------------- ! Open the binary file, read its "header" (first 2-byte integer) to determine what format ! binary file it is, and close it. !---------------------------------------------------------------------------------------------- - CALL OpenBInpFile (UnitWind, TRIM(InitData%WindFileName), TmpErrStat, TmpErrMsg) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, 'IfW_BladedFFWind') + CALL OpenBInpFile (UnitWind, TRIM(BinFileName), TmpErrStat, TmpErrMsg) + CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN ! Read the first binary integer from the file to get info on the type. @@ -134,8 +301,8 @@ SUBROUTINE IfW_BladedFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOu CLOSE( UnitWind ) IF (TmpErrStat /= 0) THEN - CALL SetErrStat( ErrID_Fatal, ' Error reading first binary integer from file "'//TRIM(InitData%WindFileName)//'."', & - ErrStat, ErrMsg, 'IfW_BladedFFWind') + CALL SetErrStat(ErrID_Fatal,' Error reading first binary integer from file "'//TRIM(BinFileName)//'."', & + ErrStat,ErrMsg,RoutineName) RETURN ENDIF @@ -146,43 +313,45 @@ SUBROUTINE IfW_BladedFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOu ! Store the binary format information so the InflowWind code can use it. ! Also changes to IntKi from INT(2) to compare in the SELECT below - ParamData%WindFileFormat = Dum_Int2 + ParamData%FF%WindFileFormat = Dum_Int2 - SELECT CASE (ParamData%WindFileFormat) + SELECT CASE (ParamData%FF%WindFileFormat) CASE ( -1, -2, -3, -99 ) ! Bladed-style binary format - !........................................................................................... - ! Create full-field summary file name from binary file root name. Also get tower file - ! name. - !........................................................................................... + IF (.not. InitInp%NativeBladedFmt) THEN + + !........................................................................................... + ! Create full-field summary file name from binary file root name. Also get tower file + ! name. + !........................................................................................... - CALL GetRoot(InitData%WindFileName, SumFile) + CALL GetRoot(BinFileName, SumFile) TwrFile = TRIM(SumFile)//'.twr' SumFile = TRIM(SumFile)//'.sum' - !........................................................................................... - ! Read the summary file to get necessary scaling information - !........................................................................................... - - CALL Read_Summary_FF (UnitWind, TRIM(SumFile), CWise, ZCenter, TI, TmpErrStat, TmpErrMsg ) + !........................................................................................... + ! Read the summary file to get necessary scaling information + !........................................................................................... + + CALL Read_Summary_FF (UnitWind, TRIM(SumFile), CWise, ZCenter, TI, UBar, ParamData%FF%RefHt, ParamData%FF%Periodic, LHR, TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CLOSE ( UnitWind ) RETURN - END IF - - UBar = ParamData%MeanFFWS ! temporary storage .... this is our only check to see if the summary and binary files "match" - + END IF + + END IF + !........................................................................................... ! Open the binary file and read its header !........................................................................................... - CALL OpenBInpFile (UnitWind, TRIM(InitData%WindFileName), TmpErrStat, TmpErrMsg ) + CALL OpenBInpFile (UnitWind, TRIM(BinFileName), TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CLOSE ( UnitWind ) @@ -190,7 +359,7 @@ SUBROUTINE IfW_BladedFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOu END IF IF ( Dum_Int2 == -99 ) THEN ! Newer-style BLADED format - CALL Read_Bladed_FF_Header1 (UnitWind, BinTI, TmpErrStat, TmpErrMsg) + CALL Read_Bladed_FF_Header1 (UnitWind, BinTI, ParamData%FF, InitInp%NativeBladedFmt, TmpErrStat, TmpErrMsg) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CLOSE ( UnitWind ) @@ -200,12 +369,15 @@ SUBROUTINE IfW_BladedFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOu ! If the TIs are also in the binary file (BinTI > 0), ! use those numbers instead of ones from the summary file - DO I =1,ParamData%NFFComp - IF ( BinTI(I) > 0 ) TI(I) = BinTI(I) - ENDDO + if (.not. InitInp%NativeBladedFmt) then + DO I =1,ParamData%FF%NFFComp + IF ( BinTI(I) > 0 ) TI(I) = BinTI(I) + ENDDO + end if + ELSE - CALL Read_Bladed_FF_Header0 (UnitWind, TmpErrStat, TmpErrMsg) ! Older-style BLADED format + CALL Read_Bladed_FF_Header0 (UnitWind, ParamData%FF, InitInp%NativeBladedFmt, TmpErrStat, TmpErrMsg) ! Older-style BLADED format CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CLOSE ( UnitWind ) @@ -220,47 +392,55 @@ SUBROUTINE IfW_BladedFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOu ! Let's see if the summary and binary FF wind files go together before continuing. !........................................................................................... - IF ( ABS( UBar - ParamData%MeanFFWS ) > 0.1 ) THEN + IF (.not. InitInp%NativeBladedFmt) THEN + IF ( ABS( UBar - ParamData%FF%MeanFFWS ) > 0.1 ) THEN CALL SetErrStat( ErrID_Fatal, ' Error: Incompatible mean hub-height wind speeds in FF wind files. '//& - '(Check that the .sum and .wnd files were generated together.)', ErrStat, ErrMsg, RoutineName ) - RETURN - ENDIF - + '(Check that the .sum and .wnd files were generated together.)', ErrStat, ErrMsg, RoutineName ) + CLOSE ( UnitWind ) + RETURN + ENDIF + END IF + !........................................................................................... ! Calculate the height of the bottom of the grid !........................................................................................... - ParamData%GridBase = ZCenter - ParamData%FFZHWid ! the location, in meters, of the bottom of the grid - + ParamData%FF%GridBase = ZCenter - ParamData%FF%FFZHWid ! the location, in meters, of the bottom of the grid + IF ( ParamData%FF%GridBase < 0.0_ReKi ) THEN + call SetErrStat( ErrID_Severe, 'WARNING: The bottom of the grid is located at a height of '//& + TRIM( Num2LStr(ParamData%FF%GridBase) )//' meters, which is below the ground.'//& + ' Winds below the ground will be set to 0.', ErrStat,ErrMsg, RoutineName) + END IF !........................................................................................... ! Read the binary grids (converted to m/s) and close the file !........................................................................................... - CALL Read_Bladed_Grids( MiscVars, CWise, TI, TmpErrStat, TmpErrMsg) + CALL Read_Bladed_Grids( UnitWind, InitInp%NativeBladedFmt, CWise, LHR, TI, ParamData%FF, TmpErrStat, TmpErrMsg) + CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) + CLOSE ( UnitWind ) + if (InitInp%NativeBladedFmt) TI = NatTI*100.0_ReKi ! report these TI for the native Bladed format in percent - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) - IF ( ErrStat >= AbortErrLev ) RETURN + IF ( ErrStat >= AbortErrLev ) RETURN !........................................................................................... ! Read the tower points file !........................................................................................... - IF ( InitData%TowerFileExist ) THEN ! If we specified a tower file + IF ( InitInp%TowerFileExist .AND. .NOT. InitInp%NativeBladedFmt) THEN ! If we specified a tower file INQUIRE ( FILE=TRIM(TwrFile) , EXIST=Exists ) ! Double check that the tower file exists and read it. If it was requested but doesn't exist, ! throw fatal error and exit. IF ( Exists ) THEN - CALL Read_FF_Tower( MiscVars, TmpErrStat, TmpErrMsg ) + CALL Read_FF_Tower( UnitWind, ParamData%FF, TwrFile, TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CLOSE ( UnitWind ) RETURN END IF - ParamData%TowerDataExist = .TRUE. ELSE CALL SetErrStat( ErrID_Fatal, ' Tower file '//TRIM(TwrFile)//' specified for Bladed full-field '// & 'wind files does not exist.', ErrStat, ErrMsg, RoutineName) @@ -268,101 +448,26 @@ SUBROUTINE IfW_BladedFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOu RETURN ENDIF ELSE - ParamData%NTGrids = 0_IntKi - ParamData%TowerDataExist = .FALSE. + ParamData%FF%NTGrids = 0_IntKi ENDIF CASE DEFAULT CALL SetErrStat( ErrID_Fatal, ' This is not a bladed-style binary wind file (binary format identifier: '// & - TRIM(Num2LStr(ParamData%WindFileFormat))//'. This might be a TurbSim binary wind file.', & + TRIM(Num2LStr(ParamData%FF%WindFileFormat))//'. This might be a TurbSim binary wind file.', & ErrStat, ErrMsg, RoutineName ) RETURN - END SELECT - - - IF (ParamData%Periodic) THEN - ParamData%InitXPosition = 0 ! start at the hub - ParamData%TotalTime = ParamData%NFFSteps*ParamData%FFDTime - ELSE - ParamData%InitXPosition = ParamData%FFYHWid ! start half the grid width ahead of the turbine - ParamData%TotalTime = (ParamData%NFFSteps-1)*ParamData%FFDTime - ENDIF - - - - !------------------------------------------------------------------------------------------------- - ! Set the InitOutput information - !------------------------------------------------------------------------------------------------- - - InitOutdata%Ver = IfW_BladedFFWind_Ver - InitOutdata%TI = TI - - - - !------------------------------------------------------------------------------------------------- - ! Write to the summary file - !------------------------------------------------------------------------------------------------- - - IF ( InitData%SumFileUnit > 0 ) THEN - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) 'Bladed-style wind type. Read by InflowWind sub-module '// & - TRIM(IfW_BladedFFWind_Ver%Name)//' '//TRIM(IfW_BladedFFWind_Ver%Ver) - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) TRIM(TmpErrMsg) - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' FileName: '//TRIM(InitData%WindFileName) - WRITE(InitData%SumFileUnit,'(A34,I3)', IOSTAT=TmpErrStat) ' Binary file format id: ',ParamData%WindFileFormat - WRITE(InitData%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Reference height (m): ',ParamData%RefHt - WRITE(InitData%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Timestep (s): ',ParamData%FFDTime - WRITE(InitData%SumFileUnit,'(A34,I12)', IOSTAT=TmpErrStat) ' Number of timesteps: ',ParamData%NFFSteps - WRITE(InitData%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Mean windspeed (m/s): ',ParamData%MeanFFWS - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Characteristic TI: [ '// & - TRIM(Num2LStr(TI(1)))//', '//TRIM(Num2LStr(TI(2)))//', '//TRIM(Num2LStr(TI(3)))//' ] ' - WRITE(InitData%SumFileUnit,'(A34,L1)', IOSTAT=TmpErrStat) ' Windfile is periodic: ',ParamData%Periodic - WRITE(InitData%SumFileUnit,'(A34,L1)', IOSTAT=TmpErrStat) ' Windfile includes tower: ',ParamData%TowerDataExist - - IF ( ParamData%Periodic ) THEN - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Time range (s): [ '// & - TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr(ParamData%TotalTime))//' ]' - ELSE ! Shift the time range to compensate for the shifting of the wind grid - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Time range (s): [ '// & - TRIM(Num2LStr(-ParamData%InitXPosition*ParamData%InvMFFWS))//' : '// & - TRIM(Num2LStr(ParamData%TotalTime-ParamData%InitXPosition*ParamData%InvMFFWS))//' ]' - ENDIF - - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Y range (m): [ '// & - TRIM(Num2LStr(-ParamData%FFYHWid))//' : '//TRIM(Num2LStr(ParamData%FFYHWid))//' ]' - - IF ( ParamData%TowerDataExist ) THEN - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Z range (m): [ '// & - TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr(ParamData%RefHt + ParamData%FFZHWid))//' ]' - ELSE - WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Z range (m): [ '// & - TRIM(Num2LStr(ParamData%RefHt - ParamData%FFZHWid))//' : '//TRIM(Num2LStr(ParamData%RefHt + ParamData%FFZHWid))//' ]' - ENDIF - - - ! We are assuming that if the last line was written ok, then all of them were. - IF (TmpErrStat /= 0_IntKi) THEN - CALL SetErrStat(ErrID_Fatal,'Error writing to summary file.',ErrStat,ErrMsg,RoutineName) - RETURN - ENDIF - ENDIF - - - - - RETURN - - - CONTAINS + END SELECT + +END SUBROUTINE ReadFiles !==================================================================================================== !> This subroutine reads the text summary file to get normalizing parameters, the location of the !! grid, and the direction the grid was written to the binary file !! !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 - SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, ErrMsg ) + SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, UBar, RefHt, Periodic, LHR, ErrStat, ErrMsg ) IMPLICIT NONE @@ -375,13 +480,17 @@ SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, Er LOGICAL, INTENT( OUT) :: CWise !< rotation (for reading the order of the binary data) REAL(ReKi), INTENT( OUT) :: ZCenter !< the height at the center of the grid REAL(ReKi), INTENT( OUT) :: TI (3) !< turbulence intensities of the wind components as defined in the FF file, not necessarially the actual TI + REAL(ReKi), INTENT( OUT) :: UBar !< mean (advection) wind speed + REAL(ReKi), INTENT( OUT) :: RefHt !< Reference height + LOGICAL, INTENT( OUT) :: Periodic !< rotation (for reading the order of the binary data) + LOGICAL, INTENT( OUT) :: LHR !< Left-hand rule for Bladed files (is the v component aligned along *negative* Y?) INTEGER(IntKi), INTENT( OUT) :: ErrStat !< returns 0 if no error encountered in the subroutine CHARACTER(*), INTENT( OUT) :: ErrMsg !< holds the error messages ! Local variables REAL(ReKi) :: ZGOffset ! The vertical offset of the turbine on rectangular grid (allows turbulence not centered on turbine hub) - INTEGER, PARAMETER :: NumStrings = 6 ! number of strings to be looking for in the file + INTEGER, PARAMETER :: NumStrings = 7 ! number of strings to be looking for in the file INTEGER(IntKi) :: FirstIndx ! The first character of a line where data is located INTEGER(IntKi) :: I ! A loop counter @@ -406,8 +515,9 @@ SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, Er LineCount = 0 StrNeeded(:) = .TRUE. ZGOffset = 0.0 - ParamData%RefHt = 0.0 - ParamData%Periodic = .FALSE. + RefHt = 0.0 + Periodic = .FALSE. + LHR = .FALSE. !---------------------------------------------------------------------------------------------- ! Open summary file. @@ -429,6 +539,7 @@ SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, Er ! 4) 'UBAR' ! 5) 'HEIGHT OFFSET' (optional) ! 6) 'PERIODIC' (optional) + ! 7) 'BLADED LEFT-HAND RULE' (optional) DO WHILE ( ( ErrStat == ErrID_None ) .AND. StrNeeded(NumStrings) ) @@ -438,7 +549,7 @@ SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, Er READ ( UnitWind, '(A)', IOSTAT=TmpErrStat ) LINE IF ( TmpErrStat /= 0 ) THEN - ! the "HEIGHT OFFSET" and "PERIODIC" parameters are not necessary. We'll assume they are zero/false if we didn't find it. + ! the "HEIGHT OFFSET", "PERIODIC", and "BLADED LEFT-HAND RULE" parameters are not necessary. We'll assume they are zero/false if we didn't find it. IF ( StrNeeded(1) .OR. StrNeeded(2) .OR. StrNeeded(4) ) THEN CALL SetErrStat( ErrID_Fatal, ' Error reading line #'//TRIM(Num2LStr(LineCount))//' of the summary file, "'// & TRIM(FileName)//'". Could not find all of the required parameters.', ErrStat, ErrMsg, RoutineName ) @@ -489,7 +600,7 @@ SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, Er IF ( INDEX( LINE, 'HUB HEIGHT' ) > 0 .OR. INDEX( LINE, 'ZHUB' ) > 0 ) THEN - READ (LINE, *, IOSTAT = TmpErrStat) ParamData%RefHt + READ (LINE, *, IOSTAT = TmpErrStat) RefHt IF ( TmpErrStat /= 0 ) THEN CALL SetErrStat( ErrID_Fatal, ' Error reading hub height from FF summary file.', ErrStat, ErrMsg, RoutineName ) @@ -517,7 +628,7 @@ SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, Er FirstIndx = INDEX( LINE, '=' ) + 1 ! Look for the equal siqn to find the number we're looking for - READ ( LINE( FirstIndx:LEN(LINE) ), *, IOSTAT=TmpErrStat ) ParamData%MeanFFWS + READ ( LINE( FirstIndx:LEN(LINE) ), *, IOSTAT=TmpErrStat ) UBar IF ( TmpErrStat /= 0 ) THEN CALL SetErrStat( ErrID_Fatal, ' Error reading UBar binary data normalizing parameter from FF summary file.', ErrStat, ErrMsg, RoutineName ) @@ -557,7 +668,7 @@ SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, Er !------------------------------------------------------------------------------------------- ! #5: Get the grid "HEIGHT OFFSET", if it exists (in TurbSim). Otherwise, assume it's zero - ! ZGOffset = HH - GridBase - ParamData%FFZHWid + ! ZGOffset = HH - GridBase - ParamData%FF%FFZHWid !------------------------------------------------------------------------------------------- IF ( INDEX( LINE, 'HEIGHT OFFSET' ) > 0 ) THEN @@ -574,25 +685,35 @@ SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, Er ENDIF !INDEX for "HEIGHT OFFSET" - ELSEIF ( StrNeeded(6) ) THEN + ELSE + + IF ( StrNeeded(6) ) THEN + + !------------------------------------------------------------------------------------------- + ! #6: Get the grid "PERIODIC", if it exists (in TurbSim). Otherwise, assume it's + ! not a periodic file (would only show up if the HEIGHT OFFSET is in the file) + !------------------------------------------------------------------------------------------- + IF ( INDEX( LINE, 'PERIODIC' ) > 0 ) THEN + + Periodic = .TRUE. + StrNeeded(6) = .FALSE. + CYCLE + ENDIF !INDEX for "PERIODIC" + END IF - !------------------------------------------------------------------------------------------- - ! #5: Get the grid "PERIODIC", if it exists (in TurbSim). Otherwise, assume it's - ! not a periodic file - !------------------------------------------------------------------------------------------- - IF ( INDEX( LINE, 'PERIODIC' ) > 0 ) THEN + IF ( StrNeeded(7) ) THEN - ParamData%Periodic = .TRUE. - StrNeeded(6) = .FALSE. + IF ( INDEX( LINE, 'BLADED LEFT-HAND RULE') > 0 ) THEN + LHR = .TRUE. + StrNeeded(7) = .FALSE. + END IF ! INDEX for "BLADED LEFT-HAND RULE" - ENDIF !INDEX for "PERIODIC" + END IF ENDIF ! StrNeeded - ENDDO !WHILE - !------------------------------------------------------------------------------------------------- ! Close the summary file !------------------------------------------------------------------------------------------------- @@ -604,17 +725,17 @@ SUBROUTINE Read_Summary_FF ( UnitWind, FileName, CWise, ZCenter, TI, ErrStat, Er ! Calculate the height of the grid center !------------------------------------------------------------------------------------------------- - ZCenter = ParamData%RefHt - ZGOffset + ZCenter = RefHt - ZGOffset END SUBROUTINE Read_Summary_FF !==================================================================================================== !> Reads the binary headers from the turbulence files of the old Bladed variety. Note that - !! because of the normalization, neither ParamData%NZGrids or ParamData%NYGrids are larger than 32 points. + !! because of the normalization, neither ParamData%FF%NZGrids or ParamData%FF%NYGrids are larger than 32 points. !! 21-Sep-2009 - B. Jonkman, NREL/NWTC. !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 - SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) + SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, p, NativeBladedFmt, ErrStat, ErrMsg) IMPLICIT NONE @@ -623,6 +744,8 @@ SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) ! Passed Variables: INTEGER(IntKi), INTENT(IN ) :: UnitWind !< unit number of already-opened wind file + TYPE(IfW_FFWind_ParameterType), INTENT(INOUT) :: p !< Parameters + LOGICAL, INTENT(IN ) :: NativeBladedFmt !< Whether this should ignore the advection speed in the binary file INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status CHARACTER(*), INTENT( OUT) :: ErrMsg !< error message @@ -638,7 +761,6 @@ SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) ! Temporary Error Handling INTEGER(IntKi) :: TmpErrStat ! for checking the IOSTAT from a READ or Open statement - CHARACTER(ErrMsgLen) :: TmpErrMsg ! Temporary ErrMsg !------------------------------------------------------------------------------------------------- @@ -660,7 +782,7 @@ SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading number of wind components from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%NFFComp = -1*Dum_Int2 + p%NFFComp = -1*Dum_Int2 ! Read 2-byte integer. Can't use library routines for this. @@ -671,7 +793,7 @@ SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) RETURN ENDIF FFZDelt = 0.001*Dum_Int2 - ParamData%InvFFZD = 1.0/FFZDelt + p%InvFFZD = 1.0/FFZDelt ! Read 2-byte integer. Can't use library routines for this. @@ -682,7 +804,7 @@ SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) RETURN ENDIF FFYDelt = 0.001*Dum_Int2 - ParamData%InvFFYD = 1.0/FFYDelt + p%InvFFYD = 1.0/FFYDelt ! Read 2-byte integer. Can't use library routines for this. @@ -702,7 +824,7 @@ SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading number of time steps from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%NFFSteps = 2*Dum_Int2 + p%NFFSteps = 2*Dum_Int2 ! Read 2-byte integer. Can't use library routines for this. @@ -712,10 +834,10 @@ SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading mean full-field wind speed from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%MeanFFWS = 0.1*Dum_Int2 - ParamData%InvMFFWS = 1.0/ParamData%MeanFFWS - ParamData%FFDTime = FFXDelt/ParamData%MeanFFWS - ParamData%FFRate = 1.0/ParamData%FFDTime + if (.not. NativeBladedFmt) p%MeanFFWS = 0.1*Dum_Int2 + p%InvMFFWS = 1.0/p%MeanFFWS + p%FFDTime = FFXDelt/p%MeanFFWS + p%FFRate = 1.0/p%FFDTime DO I = 1,5 @@ -738,8 +860,8 @@ SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading nz from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%NZGrids = Dum_Int2/1000 - ParamData%FFZHWid = 0.5*FFZDelt*( ParamData%NZGrids - 1 ) ! half the vertical size of the grid + p%NZGrids = Dum_Int2/1000 + p%FFZHWid = 0.5*FFZDelt*( p%NZGrids - 1 ) ! half the vertical size of the grid ! Read 2-byte integer. Can't use library routines for this. @@ -749,11 +871,11 @@ SUBROUTINE Read_Bladed_FF_Header0 (UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading ny from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%NYGrids = Dum_Int2/1000 - ParamData%FFYHWid = 0.5*FFYDelt*( ParamData%NYGrids - 1 ) + p%NYGrids = Dum_Int2/1000 + p%FFYHWid = 0.5*FFYDelt*( p%NYGrids - 1 ) - IF (ParamData%NFFComp == 3) THEN + IF (p%NFFComp == 3) THEN DO I=1,6 @@ -778,7 +900,7 @@ END SUBROUTINE Read_Bladed_FF_Header0 !! 16-May-2002 - Windward Engineering. !! 21-Sep-2009 - B. Jonkman, NREL. updated to trap errors and add extra parameters for MANN model !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 - SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) + SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, p, NativeBladedFmt, ErrStat, ErrMsg) IMPLICIT NONE @@ -789,6 +911,8 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) INTEGER(IntKi), INTENT(IN ) :: UnitWind !< unit number of already-opened wind file REAL(ReKi), INTENT( OUT) :: TI(3) !< turbulence intensity contained in file header + TYPE(IfW_FFWind_ParameterType), INTENT(INOUT) :: p !< Parameters + LOGICAL, INTENT(IN ) :: NativeBladedFmt !< Whether this should ignore the advection speed in the binary file INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status CHARACTER(*), INTENT( OUT) :: ErrMsg !< error message @@ -809,7 +933,6 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) ! Temporary Error Handling INTEGER(IntKi) :: TmpErrStat - CHARACTER(ErrMsgLen) :: TmpErrMsg !------------------------------------------------------------------------------------------------- @@ -849,14 +972,14 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) !---------------------------------------- !1-component Von Karman (1) or Kaimal (2) !---------------------------------------- - ParamData%NFFComp = 1 + p%NFFComp = 1 CASE(3, 5) !---------------------------------------- !3-component Von Karman (3) or IEC-2 ! Kaimal (5) !---------------------------------------- - ParamData%NFFComp = 3 + p%NFFComp = 3 CASE(4) !---------------------------------------- @@ -870,7 +993,7 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading number of components from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%NFFComp = Dum_Int4 + p%NFFComp = Dum_Int4 ! Read 4-byte real. Can't use library routines for this. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Real4 ! Latitude (deg) @@ -930,7 +1053,7 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading number of data from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%NFFComp = Dum_Int4 + p%NFFComp = Dum_Int4 CASE DEFAULT @@ -950,7 +1073,7 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) RETURN ENDIF FFZDelt = Dum_Real4 - ParamData%InvFFZD = 1.0/FFZDelt + p%InvFFZD = 1.0/FFZDelt ! Read 4-byte real. Can't use library routines for this. @@ -961,7 +1084,7 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) RETURN ENDIF FFYDelt = Dum_Real4 - ParamData%InvFFYD = 1.0/FFYDelt + p%InvFFYD = 1.0/FFYDelt ! Read 4-byte real. Can't use library routines for this. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Real4 ! delta x (m) @@ -980,7 +1103,7 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading number of time steps from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%NFFSteps = 2*Dum_Int4 + p%NFFSteps = 2*Dum_Int4 ! Read 4-byte real. Can't use library routines for this. @@ -990,10 +1113,10 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading mean full-field wind speed from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%MeanFFWS = Dum_Real4 - ParamData%InvMFFWS = 1.0/ParamData%MeanFFWS - ParamData%FFDTime = FFXDelt/ParamData%MeanFFWS - ParamData%FFRate = 1.0/ParamData%FFDTime + if (.not. NativeBladedFmt) p%MeanFFWS = Dum_Real4 + p%InvMFFWS = 1.0/p%MeanFFWS + p%FFDTime = FFXDelt/p%MeanFFWS + p%FFRate = 1.0/p%FFDTime DO I = 1,3 @@ -1029,8 +1152,8 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading nz from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%NZGrids = Dum_Int4 - ParamData%FFZHWid = 0.5*FFZDelt*( ParamData%NZGrids - 1 ) ! half the vertical size of the grid + p%NZGrids = Dum_Int4 + p%FFZHWid = 0.5*FFZDelt*( p%NZGrids - 1 ) ! half the vertical size of the grid ! Read 4-integer real. Can't use library routines for this. @@ -1040,11 +1163,11 @@ SUBROUTINE Read_Bladed_FF_Header1 (UnitWind, TI, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading ny from binary FF file.', ErrStat, ErrMsg, RoutineName) RETURN ENDIF - ParamData%NYGrids = Dum_Int4 - ParamData%FFYHWid = 0.5*FFYDelt*( ParamData%NYGrids - 1 ) + p%NYGrids = Dum_Int4 + p%FFYHWid = 0.5*FFYDelt*( p%NYGrids - 1 ) - IF (ParamData%NFFComp == 3) THEN + IF (p%NFFComp == 3) THEN DO I=1,6 @@ -1165,20 +1288,24 @@ END SUBROUTINE Read_Bladed_FF_Header1 !> This subroutine continues reading UnitWind, starting after the headers have been read. !! It reads the Grids and converts the data to un-normalized wind speeds in m/s. !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 - SUBROUTINE Read_Bladed_Grids ( MiscVars, CWise, TI, ErrStat, ErrMsg ) + SUBROUTINE Read_Bladed_Grids ( UnitWind, NativeBladedFmt, CWise, LHR, TI, p, ErrStat, ErrMsg ) IMPLICIT NONE CHARACTER(*), PARAMETER :: RoutineName="Read_Bladed_Grids" ! Passed variables - TYPE(IfW_BladedFFWind_MiscVarType), INTENT(INOUT) :: MiscVars !< misc/optimization data (storage for the main data) - LOGICAL, INTENT(IN ) :: CWise !< clockwise flag (determines if y is increasing or decreasing in file) - REAL(ReKi), INTENT(IN ) :: TI (3) !< turbulence intensities of the wind components as defined in the FF file, not necessarially the actual TI - INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status - CHARACTER(*), INTENT( OUT) :: ErrMsg !< error message + INTEGER(IntKi), INTENT(IN ) :: UnitWind !< unit number of already-opened wind file + LOGICAL, INTENT(IN ) :: NativeBladedFmt !< whether this data is in native Bladed format (scale to zero mean and unit standard deviation) + LOGICAL, INTENT(IN ) :: CWise !< clockwise flag (determines if y is increasing or decreasing in file) + LOGICAL, INTENT(IN ) :: LHR !< Left-hand rule for Bladed files (is the v component aligned along *negative* Y?) + REAL(ReKi), INTENT(IN ) :: TI (3) !< turbulence intensities of the wind components as defined in the FF file, not necessarially the actual TI + TYPE(IfW_FFWind_ParameterType), INTENT(INOUT) :: p !< Parameters + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status + CHARACTER(*), INTENT( OUT) :: ErrMsg !< error message - REAL(ReKi), PARAMETER :: FF_Offset(3) = (/ 1.0, 0.0, 0.0 /) ! used for "un-normalizing" the data + REAL(ReKi) :: FF_Scale(3) !< used for "un-normalizing" the data + REAL(ReKi) :: FF_Offset(3) !< used for "un-normalizing" the data INTEGER(IntKi) :: CFirst INTEGER(IntKi) :: CLast @@ -1196,17 +1323,30 @@ SUBROUTINE Read_Bladed_Grids ( MiscVars, CWise, TI, ErrStat, ErrMsg ) INTEGER(IntKi) :: TmpErrStat ! for checking the result of IOSTAT on READ or Open statements CHARACTER(ErrMsgLen) :: TmpErrMsg + IF (NativeBladedFmt) THEN + FF_Scale = 0.001_ReKi + FF_Offset = 0.0_ReKi + ELSE + FF_Scale = 0.001_ReKi*p%MeanFFWS*TI/100.0_ReKi + FF_Offset= (/ p%MeanFFWS, 0.0_ReKi, 0.0_ReKi /) ! used for "un-normalizing" the data + END IF + ! Bladed convention has positive V pointed along negative Y + IF (LHR) THEN ! left-hand rule + FF_Scale(2) = -FF_Scale(2) + END IF + + !------------------------------------------------------------------------------------------------- ! Generate an informative message. Initialize the ErrStat. !------------------------------------------------------------------------------------------------- ! This could take a while, so we'll write a message to tell users what's going on: - CALL WrScr( NewLine//' Reading a '//TRIM( Num2LStr(ParamData%NYGrids) )//'x'//TRIM( Num2LStr(ParamData%NZGrids) )// & - ' grid ('//TRIM( Num2LStr(ParamData%FFYHWid*2) )//' m wide, '// & - TRIM( Num2LStr(ParamData%GridBase) )//' m to '// & - TRIM( Num2LStr(ParamData%GridBase+ParamData%FFZHWid*2) )//& - ' m above ground) with a characteristic wind speed of '//TRIM( Num2LStr(ParamData%MeanFFWS) )//' m/s. ' ) + CALL WrScr( NewLine//' Reading a '//TRIM( Num2LStr(p%NYGrids) )//'x'//TRIM( Num2LStr(p%NZGrids) )// & + ' grid ('//TRIM( Num2LStr(p%FFYHWid*2) )//' m wide, '// & + TRIM( Num2LStr(p%GridBase) )//' m to '// & + TRIM( Num2LStr(p%GridBase+p%FFZHWid*2) )//& + ' m above ground) with a characteristic wind speed of '//TRIM( Num2LStr(p%MeanFFWS) )//' m/s. ' ) ErrMsg = "" ErrStat = ErrID_None @@ -1215,25 +1355,25 @@ SUBROUTINE Read_Bladed_Grids ( MiscVars, CWise, TI, ErrStat, ErrMsg ) ! Allocate space for the FF array !------------------------------------------------------------------------------------------------- - TmpNumSteps = ParamData%NFFSteps + 1 ! add another step, just in case there is an odd number of steps. + TmpNumSteps = p%NFFSteps + 1 ! add another step, just in case there is an odd number of steps. !bjj: should we reorganize this FFData array so we access the data faster? - IF ( .NOT. ALLOCATED( ParamData%FFData ) ) THEN - CALL AllocAry( ParamData%FFData, ParamData%NZGrids,ParamData%NYGrids,ParamData%NFFComp,TmpNumSteps, & + IF ( .NOT. ALLOCATED( p%FFData ) ) THEN + CALL AllocAry( p%FFData, p%NZGrids,p%NYGrids,p%NFFComp,TmpNumSteps, & 'Full-field wind data array.', TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN ELSE - IF (SIZE(ParamData%FFData,1) /= ParamData%NZGrids .OR. SIZE(ParamData%FFData,2) /= ParamData%NYGrids .OR. & - SIZE(ParamData%FFData,3) /= ParamData%NFFComp .OR. SIZE(ParamData%FFData,3) /= TmpNumSteps ) THEN + IF (SIZE(p%FFData,1) /= p%NZGrids .OR. SIZE(p%FFData,2) /= p%NYGrids .OR. & + SIZE(p%FFData,3) /= p%NFFComp .OR. SIZE(p%FFData,3) /= TmpNumSteps ) THEN ! Let's make the array the correct size (we should never get here, but you never know) - DEALLOCATE( ParamData%FFData ) + DEALLOCATE( p%FFData ) - CALL AllocAry( ParamData%FFData, ParamData%NZGrids,ParamData%NYGrids,ParamData%NFFComp,TmpNumSteps, & + CALL AllocAry( p%FFData, p%NZGrids,p%NYGrids,p%NFFComp,TmpNumSteps, & 'Full-field wind data array.', TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN @@ -1245,15 +1385,15 @@ SUBROUTINE Read_Bladed_Grids ( MiscVars, CWise, TI, ErrStat, ErrMsg ) ! Initialize the data and set column indexing to account for direction of turbine rotation (CWise) !------------------------------------------------------------------------------------------------- - ParamData%FFData(:,:,:,:) = 0.0 ! we may have only one component + p%FFData(:,:,:,:) = 0.0 ! we may have only one component IF ( CWise ) THEN - CFirst = ParamData%NYGrids + CFirst = p%NYGrids CLast = 1 CStep = -1 ELSE CFirst = 1 - CLast = ParamData%NYGrids + CLast = p%NYGrids CStep = 1 ENDIF @@ -1263,22 +1403,22 @@ SUBROUTINE Read_Bladed_Grids ( MiscVars, CWise, TI, ErrStat, ErrMsg ) !------------------------------------------------------------------------------------------------- !bjj: should we reorganize this FFData array so we access the data faster? - ParamData%NFFSteps = TmpNumSteps + p%NFFSteps = TmpNumSteps TIME_LOOP: DO IT=1,TmpNumSteps ! time (add 1 to see if there is an odd number of grids) - DO IR=1,ParamData%NZGrids ! the rows (vertical) + DO IR=1,p%NZGrids ! the rows (vertical) DO IC=CFirst,CLast,CStep ! the columns (lateral) - DO I=1,ParamData%NFFComp ! wind components (U, V, W) + DO I=1,p%NFFComp ! wind components (U, V, W) ! Get the next integer from the file. ! This is a 2-byte integer, so we can't use the library read routines. READ (UnitWind,IOStat=TmpErrStat) Dum_Int2 IF (TmpErrStat /= 0) THEN IF ( IT == TmpNumSteps ) THEN ! There really were an even number of steps - ParamData%NFFSteps = TmpNumSteps - 1 + p%NFFSteps = TmpNumSteps - 1 ErrStat = 0 EXIT TIME_LOOP ELSE @@ -1286,11 +1426,11 @@ SUBROUTINE Read_Bladed_Grids ( MiscVars, CWise, TI, ErrStat, ErrMsg ) 'ic = '//TRIM(Num2LStr(ic))// & ', ir = '//TRIM(Num2LStr(ir))// & ', it = '//TRIM(Num2LStr(it))// & - ', nffsteps = '//TRIM(Num2LStr(ParamData%NFFSteps)), ErrStat, ErrMsg, RoutineName) + ', nffsteps = '//TRIM(Num2LStr(p%NFFSteps)), ErrStat, ErrMsg, RoutineName) RETURN ENDIF ELSE - ParamData%FFData(IR,IC,I,IT) = ParamData%MeanFFWS*(FF_Offset(I)+0.00001*TI(I)*Dum_Int2) + p%FFData(IR,IC,I,IT) = FF_Offset(I)+FF_Scale(I)*Dum_Int2 ENDIF END DO !I @@ -1301,15 +1441,15 @@ SUBROUTINE Read_Bladed_Grids ( MiscVars, CWise, TI, ErrStat, ErrMsg ) END DO TIME_LOOP !IT - IF ( ParamData%Periodic ) THEN - TmpErrMsg = ' Processed '//TRIM( Num2LStr( ParamData%NFFSteps ) )//' time steps of '// & - TRIM( Num2LStr ( ParamData%FFRate ) )//'-Hz full-field data (period of '// & - TRIM( Num2LStr( ParamData%FFDTime*ParamData%NFFSteps ) )//' seconds).' + IF ( p%Periodic ) THEN + TmpErrMsg = ' Processed '//TRIM( Num2LStr( p%NFFSteps ) )//' time steps of '// & + TRIM( Num2LStr ( p%FFRate ) )//'-Hz full-field data (period of '// & + TRIM( Num2LStr( p%FFDTime*p%NFFSteps ) )//' seconds).' ELSE - TmpErrMsg= ' Processed '//TRIM( Num2LStr( ParamData%NFFSteps ) )//' time steps of '// & - TRIM( Num2LStr ( ParamData%FFRate ) )//'-Hz full-field data ('// & - TRIM( Num2LStr( ParamData%FFDTime*( ParamData%NFFSteps - 1 ) ) )//' seconds).' + TmpErrMsg= ' Processed '//TRIM( Num2LStr( p%NFFSteps ) )//' time steps of '// & + TRIM( Num2LStr ( p%FFRate ) )//'-Hz full-field data ('// & + TRIM( Num2LStr( p%FFDTime*( p%NFFSteps - 1 ) ) )//' seconds).' ENDIF CALL WrScr( NewLine//TRIM(TmpErrMsg) ) !CALL SetErrStat( ErrID_Info, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) @@ -1322,15 +1462,16 @@ END SUBROUTINE Read_Bladed_Grids !! The FF grid must be read before this subroutine is called! (many checks are made to ensure the !! files belong together) !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 - SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) + SUBROUTINE Read_FF_Tower( UnitWind, p, TwrFileName, ErrStat, ErrMsg ) IMPLICIT NONE CHARACTER(*), PARAMETER :: RoutineName="Read_FF_Tower" ! Passed Variables: - - TYPE(IfW_BladedFFWind_MiscVarType), INTENT(INOUT) :: MiscVars !< misc/optimization data (storage for the main data) + INTEGER(IntKi) :: UnitWind !< unit number of wind file to be opened + TYPE(IfW_FFWind_ParameterType), INTENT(INOUT) :: p !< Parameters + CHARACTER(*), INTENT(IN ) :: TwrFileName INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status return value (0=no error; non-zero is error) CHARACTER(*), INTENT( OUT) :: ErrMsg !< a message for errors that occur @@ -1362,9 +1503,9 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ErrMsg = '' ErrStat = ErrID_None - ParamData%NTGrids = 0 + p%NTGrids = 0 - IF ( ParamData%NFFComp /= 3 ) THEN + IF ( p%NFFComp /= 3 ) THEN CALL SetErrStat( ErrID_Fatal, ' Error: Tower binary files require 3 wind components.', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF @@ -1373,7 +1514,7 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ! Open the file !------------------------------------------------------------------------------------------------- - CALL OpenBInpFile (UnitWind, TRIM(TwrFile), TmpErrStat, TmpErrMsg) + CALL OpenBInpFile (UnitWind, TRIM(TwrFileName), TmpErrStat, TmpErrMsg) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) RETURN @@ -1384,11 +1525,11 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ! This is a 4-byte real, so we can't use the library read routines. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Real4 ! dz, in meters [4-byte REAL] IF ( TmpErrStat /= 0 ) THEN - CALL SetErrStat( ErrID_Fatal, ' Error reading dz in the binary tower file "'//TRIM( TwrFile )//'."', ErrStat, ErrMsg, RoutineName ) + CALL SetErrStat( ErrID_Fatal, ' Error reading dz in the binary tower file "'//TRIM( TwrFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - IF ( ABS(Dum_Real4*ParamData%InvFFZD-1) > TOL ) THEN + IF ( ABS(Dum_Real4*p%InvFFZD-1) > TOL ) THEN CALL SetErrStat( ErrID_Fatal, ' Resolution in the FF binary file does not match the tower file.', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF @@ -1397,11 +1538,11 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ! This is a 4-byte real, so we can't use the library read routines. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Real4 ! dx, in meters [4-byte REAL] IF ( TmpErrStat /= 0 ) THEN - CALL SetErrStat( ErrID_Fatal, ' Error reading dx in the binary tower file "'//TRIM( TwrFile )//'."', ErrStat, ErrMsg, RoutineName ) + CALL SetErrStat( ErrID_Fatal, ' Error reading dx in the binary tower file "'//TRIM( TwrFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - IF ( ABS(Dum_Real4*ParamData%InvMFFWS/ParamData%FFDTime-1) > TOL ) THEN + IF ( ABS(Dum_Real4*p%InvMFFWS/p%FFDTime-1) > TOL ) THEN CALL SetErrStat( ErrID_Fatal, ' Time resolution in the FF binary file does not match the tower file.', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF @@ -1410,12 +1551,12 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ! This is a 4-byte real, so we can't use the library read routines. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Real4 ! Zmax, in meters [4-byte REAL] IF ( TmpErrStat /= 0 ) THEN - CALL SetErrStat( ErrID_Fatal, ' Error reading Zmax in the binary tower file "'//TRIM( TwrFile )//'."', ErrStat, ErrMsg, RoutineName ) + CALL SetErrStat( ErrID_Fatal, ' Error reading Zmax in the binary tower file "'//TRIM( TwrFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - IF ( ABS(Dum_Real4/ParamData%GridBase-1) > TOL ) THEN - CALL SetErrStat( ErrID_Fatal, ' Height in the FF binary file does not match the tower file "'//TRIM( TwrFile )//'."', ErrStat, ErrMsg, RoutineName ) + IF ( ABS(Dum_Real4/p%GridBase-1) > TOL ) THEN + CALL SetErrStat( ErrID_Fatal, ' Height in the FF binary file does not match the tower file "'//TRIM( TwrFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF @@ -1423,11 +1564,11 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ! This is a 4-byte integer, so we can't use the library read routines. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Int4 ! NumOutSteps [4-byte INTEGER] IF ( TmpErrStat /= 0 ) THEN - CALL SetErrStat( ErrID_Fatal, ' Error reading NumOutSteps in the binary tower file "'//TRIM( TwrFile )//'."', ErrStat, ErrMsg, RoutineName ) + CALL SetErrStat( ErrID_Fatal, ' Error reading NumOutSteps in the binary tower file "'//TRIM( TwrFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - IF ( Dum_Int4 /= ParamData%NFFSteps ) THEN + IF ( Dum_Int4 /= p%NFFSteps ) THEN CALL SetErrStat( ErrID_Fatal, ' Number of time steps in the FF binary file does not match the tower file.', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF @@ -1436,22 +1577,22 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ! This is a 4-byte integer, so we can't use the library read routines. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Int4 ! NumZ [4-byte INTEGER] IF ( TmpErrStat /= 0 ) THEN - CALL SetErrStat( ErrID_Fatal, ' Error reading NumZ in the binary tower file "'//TRIM( TwrFile )//'."', ErrStat, ErrMsg, RoutineName ) + CALL SetErrStat( ErrID_Fatal, ' Error reading NumZ in the binary tower file "'//TRIM( TwrFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%NTGrids = Dum_Int4 + p%NTGrids = Dum_Int4 ! This is a 4-byte real, so we can't use the library read routines. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Real4 ! UHub [4-byte REAL] IF ( TmpErrStat /= 0 ) THEN - CALL SetErrStat( ErrID_Fatal, ' Error reading UHub in the binary tower file "'//TRIM( TwrFile )//'."', ErrStat, ErrMsg, RoutineName ) + CALL SetErrStat( ErrID_Fatal, ' Error reading UHub in the binary tower file "'//TRIM( TwrFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - IF ( ABS(Dum_Real4*ParamData%InvMFFWS - 1) > TOL ) THEN + IF ( ABS(Dum_Real4*p%InvMFFWS - 1) > TOL ) THEN CALL SetErrStat( ErrID_Fatal, ' Mean wind speed in the FF binary file does not match the tower file.', ErrStat, ErrMsg, RoutineName ) - ParamData%NTGrids = 0 + p%NTGrids = 0 RETURN ENDIF @@ -1460,9 +1601,9 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ! Read the TI values fromthe tower file: 4-byte reals. !bjj: not sure you can call this routine to read from a binary file... - !CALL ReadVar( UnitWind, TRIM(TwrFile), TI(IC), 'TI('//TRIM(Num2LStr(IC))//')', 'TI value for u,v, or w', TmpErrStat, TmpErrMsg ) + !CALL ReadVar( UnitWind, TRIM(InitInp%WindFileName), TI(IC), 'TI('//TRIM(Num2LStr(IC))//')', 'TI value for u,v, or w', TmpErrStat, TmpErrMsg ) !IF (TmpErrStat /= ErrID_None) THEN - ! ParamData%NTGrids = 0 + ! p%NTGrids = 0 ! CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) ! IF (ErrStat >= AbortErrLev) RETURN !ENDIF @@ -1470,9 +1611,9 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) READ (UnitWind, IOSTAT=TmpErrStat) TI(IC) ! TI(u), TI(v), TI(w) [4-byte REAL] IF (TmpErrStat /= 0) THEN - ParamData%NTGrids = 0 + p%NTGrids = 0 CALL SetErrStat( ErrID_Fatal, ' Error reading TI('//TRIM(Num2LStr(IC))//') in the binary tower file "' & - //TRIM( TwrFile )//'."', ErrStat, ErrMsg, RoutineName ) + //TRIM( TwrFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF @@ -1482,10 +1623,10 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ! Allocate arrays for the tower points !---------------------------------------------------------------------------------------------- - IF ( ParamData%NTGrids > 0 ) THEN + IF ( p%NTGrids > 0 ) THEN - IF ( .NOT. ALLOCATED( ParamData%FFTower ) ) THEN - CALL AllocAry( ParamData%FFTower, ParamData%NFFComp, ParamData%NTGrids, ParamData%NFFSteps, & + IF ( .NOT. ALLOCATED( p%FFTower ) ) THEN + CALL AllocAry( p%FFTower, p%NFFComp, p%NTGrids, p%NFFSteps, & 'Tower wind data array.', TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) RETURN @@ -1502,25 +1643,25 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) ! Loop through time. - DO IT=1,ParamData%NFFSteps + DO IT=1,p%NFFSteps - DO IZ=1,ParamData%NTGrids ! If NTGrids<1, there are no tower points & FFTower is not allocated + DO IZ=1,p%NTGrids ! If NTGrids<1, there are no tower points & FFTower is not allocated ! Ytower = 0 ! Lateral location of the tower data point, in m relative to tower centerline ! Ztower(IZ) = Z1 - (IZ-1)*dz ! Vertical location of tower data point, in m relative to ground - DO IC=1,ParamData%NFFComp ! number of wind components + DO IC=1,p%NFFComp ! number of wind components ! Read in the 2-byte integer. Can't use library read routines for this. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Int2 ! normalized wind-component, INT(2) IF ( TmpErrStat /= 0 ) THEN CALL SetErrStat( ErrID_Fatal, ' Error reading binary tower data file. it = '//TRIM(Num2LStr(it))// & - ', nffsteps = '//TRIM(Num2LStr(ParamData%NFFSteps)), ErrStat, ErrMsg, RoutineName ) - ParamData%NTGrids = 0 + ', nffsteps = '//TRIM(Num2LStr(p%NFFSteps)), ErrStat, ErrMsg, RoutineName ) + p%NTGrids = 0 RETURN ENDIF - ParamData%FFTower(IC,IZ,IT) = ParamData%MeanFFWS*(FF_Offset(IC)+0.00001*TI(IC)*Dum_Int2) ! wind-component scaled to m/s + p%FFTower(IC,IZ,IT) = p%MeanFFWS*(FF_Offset(IC)+0.00001*TI(IC)*Dum_Int2) ! wind-component scaled to m/s ENDDO !IC @@ -1534,8 +1675,8 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) !------------------------------------------------------------------------------------------------- CLOSE ( UnitWind ) - TmpErrMsg = ' Processed '//TRIM( Num2LStr(ParamData%NFFSteps) )//' time steps of '// & - TRIM( Num2LStr(ParamData%NTGrids) )//'x1 tower data grids.' + TmpErrMsg = ' Processed '//TRIM( Num2LStr(p%NFFSteps) )//' time steps of '// & + TRIM( Num2LStr(p%NTGrids) )//'x1 tower data grids.' !CALL SetErrStat( ErrID_Info, ErrMsgLcl, ErrStat, ErrMsg, RoutineName ) CALL WrScr( NewLine//TRIM(TmpErrMsg) ) @@ -1543,7 +1684,116 @@ SUBROUTINE Read_FF_Tower( MiscVars, ErrStat, ErrMsg ) RETURN END SUBROUTINE Read_FF_Tower -END SUBROUTINE IfW_BladedFFWind_Init +!==================================================================================================== +!> This subroutine reads the text summary file to get normalizing parameters, the location of the +!! grid, and the direction the grid was written to the binary file +SUBROUTINE Read_NativeBladedSummary ( FileName, PLExp, TI, UBar, RefHt, PropagationDir, VFlowAngle, BinFileName, XOffset, ErrStat, ErrMsg ) + + IMPLICIT NONE + + CHARACTER(*), PARAMETER :: RoutineName="Read_NativeBladedSummary" + + + ! Passed variables + CHARACTER(*), INTENT(IN ) :: FileName !< name of the summary file + REAL(ReKi), INTENT( OUT) :: PLExp !< the power-law exponent for vertical wind shear + REAL(ReKi), INTENT( OUT) :: TI (3) !< turbulence intensities of the wind components as defined in the FF file, not necessarially the actual TI + REAL(ReKi), INTENT( OUT) :: UBar !< mean (advection) wind speed + REAL(ReKi), INTENT( OUT) :: RefHt !< Reference height + REAL(ReKi), INTENT( OUT) :: PropagationDir !< propagation direction + REAL(ReKi), INTENT( OUT) :: VFlowAngle !< vertical flow angle + CHARACTER(*), INTENT( OUT) :: BinFileName !< name of the binary file containing wind data + REAL(ReKi), INTENT( OUT) :: XOffset !< distance offset for start of wind files + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< returns 0 if no error encountered in the subroutine + CHARACTER(*), INTENT( OUT) :: ErrMsg !< holds the error messages + + ! Local variables + INTEGER(IntKi), PARAMETER :: UnEc= -1 ! echo file unit number (set to something else > 0 for debugging) + INTEGER(IntKi) :: CurLine ! Current line to parse in FileInfo data structure + INTEGER(IntKi) :: ErrStat2 ! temporary error status + CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary error message + + TYPE (FileInfoType) :: FileInfo ! The derived type for holding the file information. + + + !---------------------------------------------------------------------------------------------- + ! Initialize some variables + !---------------------------------------------------------------------------------------------- + + ErrStat = ErrID_None + ErrMsg = '' + + !---------------------------------------------------------------------------------------------- + ! Open and read the summary file; store data in FileInfo structure. + !---------------------------------------------------------------------------------------------- + + CALL ProcessComFile ( FileName, FileInfo, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF (ErrStat >= AbortErrLev) THEN + CALL Cleanup() + RETURN + END IF + + !------------------------------------------------------------------------------------------------- + ! Process the lines stored in FileInfo + !------------------------------------------------------------------------------------------------- + + CurLine = 1 + + CALL ParseVar ( FileInfo, CurLine, 'UBAR', UBar, ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + CALL ParseVar ( FileInfo, CurLine, 'REFHT', RefHt, ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + CALL ParseVar ( FileInfo, CurLine, 'TI', TI(1), ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + CALL ParseVar ( FileInfo, CurLine, 'TI_V', TI(2), ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + CALL ParseVar ( FileInfo, CurLine, 'TI_W', TI(3), ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + CALL ParseVar ( FileInfo, CurLine, 'WDIR', PropagationDir, ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + PropagationDir = R2D*PropagationDir + + CALL ParseVar ( FileInfo, CurLine, 'FLINC', VFlowAngle, ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + VFlowAngle = R2D*VFlowAngle ! convert to degrees + + CALL ParseVar ( FileInfo, CurLine, 'WINDF', BinFileName, ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + CALL ParseVar ( FileInfo, CurLine, 'WSHEAR', PLExp, ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + IF (ErrStat >= AbortErrLev) THEN + CALL Cleanup() + RETURN + END IF + + CALL ParseVar ( FileInfo, CurLine, 'XOffset', XOffset, ErrStat2, ErrMsg2, UnEc ) + if (ErrStat2/=ErrID_None) then + XOffset = 0.0_ReKi ! this will be the default if offset is not in the file + end if + + + !------------------------------------------------------------------------------------------------- + ! Get rid of the FileInfo data structure (including pointers and allocatable array): + !------------------------------------------------------------------------------------------------- + + call Cleanup ( ) + + +CONTAINS + + SUBROUTINE Cleanup () + CALL NWTC_Library_DestroyFileInfoType (FileInfo, ErrStat2, ErrMsg2) + END SUBROUTINE Cleanup + +END SUBROUTINE Read_NativeBladedSummary !==================================================================================================== @@ -1573,362 +1823,14 @@ SUBROUTINE IfW_BladedFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, D INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status CHARACTER(*), INTENT( OUT) :: ErrMsg !< The error message - ! local variables - INTEGER(IntKi) :: NumPoints ! Number of points specified by the PositionXYZ array - ! local counters - INTEGER(IntKi) :: PointNum ! a loop counter for the current point - ! temporary variables - INTEGER(IntKi) :: TmpErrStat ! temporary error status - CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message - - - !------------------------------------------------------------------------------------------------- - ! Check that the module has been initialized. - !------------------------------------------------------------------------------------------------- - - ErrStat = ErrID_None - ErrMsg = '' - - !------------------------------------------------------------------------------------------------- - ! Initialize some things - !------------------------------------------------------------------------------------------------- - - - ! The array is transposed so that the number of points is the second index, x/y/z is the first. - ! This is just in case we only have a single point, the SIZE command returns the correct number of points. - NumPoints = SIZE(PositionXYZ,2) - - - ! Step through all the positions and get the velocities - DO PointNum = 1, NumPoints - - ! Calculate the velocity for the position - Velocity(:,PointNum) = FF_Interp(Time,PositionXYZ(:,PointNum),ParamData,MiscVars,TmpErrStat,TmpErrMsg) - - ! Error handling - IF (TmpErrStat /= ErrID_None) THEN ! adding this so we don't have to convert numbers to strings every time - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, "IfW_BladedFFWind:CalcOutput [position=("// & - TRIM(Num2LStr(PositionXYZ(1,PointNum)))//", "// & - TRIM(Num2LStr(PositionXYZ(2,PointNum)))//", "// & - TRIM(Num2LStr(PositionXYZ(3,PointNum)))//")]" ) - IF (ErrStat >= AbortErrLev) RETURN - END IF - - ENDDO - - - - !REMOVE THIS for AeroDyn 15 - ! Return the average disk velocity values needed by AeroDyn 14. This is the WindInf_ADhack_diskVel routine. - DiskVel(1) = ParamData%MeanFFWS - DiskVel(2:3) = 0.0_ReKi + CALL IfW_FFWind_CalcOutput(Time, PositionXYZ, ParamData%FF, Velocity, DiskVel, ErrStat, ErrMsg) RETURN END SUBROUTINE IfW_BladedFFWind_CalcOutput - !+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - !> This function is used to interpolate into the full-field wind array or tower array if it has - !! been defined and is necessary for the given inputs. It receives X, Y, Z and - !! TIME from the calling routine. It then computes a time shift due to a nonzero X based upon - !! the average windspeed. The modified time is used to decide which pair of time slices to interpolate - !! within and between. After finding the two time slices, it decides which four grid points bound the - !! (Y,Z) pair. It does a bilinear interpolation for each time slice. Linear interpolation is then used - !! to interpolate between time slices. This routine assumes that X is downwind, Y is to the left when - !! looking downwind and Z is up. It also assumes that no extrapolation will be needed. - !! - !! If tower points are used, it assumes the velocity at the ground is 0. It interpolates between - !! heights and between time slices, but ignores the Y input. - !! - !! 11/07/1994 - Created by M. Buhl from the original TURBINT. - !! 09/25/1997 - Modified by M. Buhl to use f90 constructs and new variable names. Renamed to FF_Interp. - !! 09/23/2009 - Modified by B. Jonkman to use arguments instead of modules to determine time and position. - !! Height is now relative to the ground - !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 - FUNCTION FF_Interp(Time, Position, ParamData, MiscVars, ErrStat, ErrMsg) - - IMPLICIT NONE - - CHARACTER(*), PARAMETER :: RoutineName="FF_Interp" - - REAL(DbKi), INTENT(IN ) :: Time !< time (s) - REAL(ReKi), INTENT(IN ) :: Position(3) !< takes the place of XGrnd, YGrnd, ZGrnd - TYPE(IfW_BladedFFWind_ParameterType), INTENT(IN ) :: ParamData !< Parameters - TYPE(IfW_BladedFFWind_MiscVarType), INTENT(INOUT) :: MiscVars !< misc/optimization data (storage for the main data) - REAL(ReKi) :: FF_Interp(3) !< The U, V, W velocities - - INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status - CHARACTER(*), INTENT( OUT) :: ErrMsg !< error message - - ! Local Variables: - - REAL(ReKi) :: TimeShifted - REAL(ReKi),PARAMETER :: Tol = 1.0E-3 ! a tolerance for determining if two reals are the same (for extrapolation) - REAL(ReKi) :: T - REAL(ReKi) :: TGRID - REAL(ReKi) :: Y - REAL(ReKi) :: YGRID - REAL(ReKi) :: Z - REAL(ReKi) :: ZGRID - REAL(ReKi) :: N(8) ! array for holding scaling factors for the interpolation algorithm - REAL(ReKi) :: u(8) ! array for holding the corner values for the interpolation algorithm across a cubic volume - REAL(ReKi) :: M(4) ! array for holding scaling factors for the interpolation algorithm - REAL(ReKi) :: v(4) ! array for holding the corner values for the interpolation algorithm across an area - - INTEGER(IntKi) :: IDIM - INTEGER(IntKi) :: ITHI - INTEGER(IntKi) :: ITLO - INTEGER(IntKi) :: IYHI - INTEGER(IntKi) :: IYLO - INTEGER(IntKi) :: IZHI - INTEGER(IntKi) :: IZLO - - LOGICAL :: OnGrid - - !------------------------------------------------------------------------------------------------- - ! Initialize variables - !------------------------------------------------------------------------------------------------- - - FF_Interp(:) = 0.0_ReKi ! the output velocities (in case ParamData%NFFComp /= 3) - - ErrStat = ErrID_None - ErrMsg = "" - - !------------------------------------------------------------------------------------------------- - ! Find the bounding time slices. - !------------------------------------------------------------------------------------------------- - - ! Perform the time shift. At time=0, a point half the grid width downstream (ParamData%FFYHWid) will index into the zero time slice. - ! If we did not do this, any point downstream of the tower at the beginning of the run would index outside of the array. - ! This all assumes the grid width is at least as large as the rotor. If it isn't, then the interpolation will not work. - - - TimeShifted = TIME + ( ParamData%InitXPosition - Position(1) )*ParamData%InvMFFWS ! in distance, X: InputInfo%Position(1) - ParamData%InitXPosition - TIME*ParamData%MeanFFWS - - - IF ( ParamData%Periodic ) THEN ! translate TimeShifted to ( 0 <= TimeShifted < ParamData%TotalTime ) - - TimeShifted = MODULO( TimeShifted, ParamData%TotalTime ) - ! If TimeShifted is a very small negative number, modulo returns the incorrect value due to internal rounding errors. - ! See bug report #471 - IF (TimeShifted == ParamData%TotalTime) TimeShifted = 0.0_ReKi - - TGRID = TimeShifted*ParamData%FFRate - ITLO = INT( TGRID ) ! convert REAL to INTEGER (add 1 later because our grids start at 1, not 0) - T = 2.0_ReKi * ( TGRID - REAL(ITLO, ReKi) ) - 1.0_ReKi ! a value between -1 and 1 that indicates a relative position between ITLO and ITHI - - ITLO = ITLO + 1 - IF ( ITLO == ParamData%NFFSteps ) THEN - ITHI = 1 - ELSE - ITHI = ITLO + 1 - ENDIF - - - ELSE - - TGRID = TimeShifted*ParamData%FFRate - ITLO = INT( TGRID ) ! convert REAL to INTEGER (add 1 later because our grids start at 1, not 0) - T = 2.0_ReKi * ( TGRID - REAL(ITLO, ReKi) ) - 1.0_ReKi ! a value between -1 and 1 that indicates a relative position between ITLO and ITHI - - ITLO = ITLO + 1 ! add one since our grids start at 1, not 0 - ITHI = ITLO + 1 - - IF ( ITLO >= ParamData%NFFSteps .OR. ITLO < 1 ) THEN - IF ( ITLO == ParamData%NFFSteps ) THEN - ITHI = ITLO - IF ( T <= TOL ) THEN ! we're on the last point - T = -1.0_ReKi - ELSE ! We'll extrapolate one dt past the last value in the file - ITLO = ITHI - 1 - ENDIF - ELSE - ErrMsg = ' Error: FF wind array was exhausted at '//TRIM( Num2LStr( REAL( TIME, ReKi ) ) )// & - ' seconds (trying to access data at '//TRIM( Num2LStr( REAL( TimeShifted, ReKi ) ) )//' seconds).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - ENDIF - - ENDIF - - - !------------------------------------------------------------------------------------------------- - ! Find the bounding rows for the Z position. [The lower-left corner is (1,1) when looking upwind.] - !------------------------------------------------------------------------------------------------- - - ZGRID = ( Position(3) - ParamData%GridBase )*ParamData%InvFFZD - - IF (ZGRID > -1*TOL) THEN - OnGrid = .TRUE. - - ! Index for start and end slices - IZLO = INT( ZGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 - IZHI = IZLO + 1 - - ! Set Z as a value between -1 and 1 for the relative location between IZLO and IZHI. - ! Subtract 1_IntKi from Z since the indices are starting at 1, not 0 - Z = 2.0_ReKi * (ZGRID - REAL(IZLO - 1_IntKi, ReKi)) - 1.0_ReKi - - IF ( IZLO < 1 ) THEN - IF ( IZLO == 0 .AND. Z >= 1.0-TOL ) THEN - Z = -1.0_ReKi - IZLO = 1 - ELSE - ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction (Z='//& - TRIM(Num2LStr(Position(3)))//' m is below the grid).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - ELSEIF ( IZLO >= ParamData%NZGrids ) THEN - IF ( IZLO == ParamData%NZGrids .AND. Z <= TOL ) THEN - Z = -1.0_ReKi - IZHI = IZLO ! We're right on the last point, which is still okay - ELSE - ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction (Z='//& - TRIM(Num2LStr(Position(3)))//' m is above the grid).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - ENDIF - - ELSE - - OnGrid = .FALSE. ! this is on the tower - - IF ( ParamData%NTGrids < 1 ) THEN - ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction '// & - '(height (Z='//TRIM(Num2LStr(Position(3)))//' m) is below the grid and no tower points are defined).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - - IZLO = INT( -1.0*ZGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 - - - IF ( IZLO >= ParamData%NTGrids ) THEN !our dz is the difference between the bottom tower point and the ground - IZLO = ParamData%NTGrids - - ! Check that this isn't zero. Value between -1 and 1 corresponding to the relative position. - Z = 1.0_ReKi - 2.0_ReKi * (Position(3) / (ParamData%GridBase - REAL(IZLO - 1_IntKi, ReKi)/ParamData%InvFFZD)) - - ELSE - - ! Set Z as a value between -1 and 1 for the relative location between IZLO and IZHI. Used in the interpolation. - Z = 2.0_ReKi * (ABS(ZGRID) - REAL(IZLO - 1_IntKi, ReKi)) - 1.0_ReKi - - ENDIF - IZHI = IZLO + 1 - - ENDIF - - - IF ( OnGrid ) THEN ! The tower points don't use this - - !------------------------------------------------------------------------------------------------- - ! Find the bounding columns for the Y position. [The lower-left corner is (1,1) when looking upwind.] - !------------------------------------------------------------------------------------------------- - - YGRID = ( Position(2) + ParamData%FFYHWid )*ParamData%InvFFYD ! really, it's (Position(2) - -1.0*ParamData%FFYHWid) - - IYLO = INT( YGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 - IYHI = IYLO + 1 - - ! Set Y as a value between -1 and 1 for the relative location between IYLO and IYHI. Used in the interpolation. - ! Subtract 1_IntKi from IYLO since grids start at index 1, not 0 - Y = 2.0_ReKi * (YGRID - REAL(IYLO - 1_IntKi, ReKi)) - 1.0_ReKi - - IF ( IYLO >= ParamData%NYGrids .OR. IYLO < 1 ) THEN - IF ( IYLO == 0 .AND. Y >= 1.0-TOL ) THEN - Y = -1.0_ReKi - IYLO = 1 - ELSE IF ( IYLO == ParamData%NYGrids .AND. Y <= TOL ) THEN - Y = -1.0_ReKi - IYHI = IYLO ! We're right on the last point, which is still okay - ELSE - ErrMsg = ' FF wind array boundaries violated: Grid too small in Y direction. Y='// & - TRIM(Num2LStr(Position(2)))//'; Y boundaries = ['//TRIM(Num2LStr(-1.0*ParamData%FFYHWid))// & - ', '//TRIM(Num2LStr(ParamData%FFYHWid))//']' - ErrStat = ErrID_Fatal ! we don't return anything - RETURN - ENDIF - ENDIF - - !------------------------------------------------------------------------------------------------- - ! Interpolate on the grid - !------------------------------------------------------------------------------------------------- - - DO IDIM=1,ParamData%NFFComp ! all the components - - -!New Algorithm here - N(1) = ( 1.0_ReKi + Z )*( 1.0_ReKi - Y )*( 1.0_ReKi - T ) - N(2) = ( 1.0_ReKi + Z )*( 1.0_ReKi + Y )*( 1.0_ReKi - T ) - N(3) = ( 1.0_ReKi - Z )*( 1.0_ReKi + Y )*( 1.0_ReKi - T ) - N(4) = ( 1.0_ReKi - Z )*( 1.0_ReKi - Y )*( 1.0_ReKi - T ) - N(5) = ( 1.0_ReKi + Z )*( 1.0_ReKi - Y )*( 1.0_ReKi + T ) - N(6) = ( 1.0_ReKi + Z )*( 1.0_ReKi + Y )*( 1.0_ReKi + T ) - N(7) = ( 1.0_ReKi - Z )*( 1.0_ReKi + Y )*( 1.0_ReKi + T ) - N(8) = ( 1.0_ReKi - Z )*( 1.0_ReKi - Y )*( 1.0_ReKi + T ) - N = N / REAL( SIZE(N), ReKi ) ! normalize - - - u(1) = ParamData%FFData( IZHI, IYLO, IDIM, ITLO ) - u(2) = ParamData%FFData( IZHI, IYHI, IDIM, ITLO ) - u(3) = ParamData%FFData( IZLO, IYHI, IDIM, ITLO ) - u(4) = ParamData%FFData( IZLO, IYLO, IDIM, ITLO ) - u(5) = ParamData%FFData( IZHI, IYLO, IDIM, ITHI ) - u(6) = ParamData%FFData( IZHI, IYHI, IDIM, ITHI ) - u(7) = ParamData%FFData( IZLO, IYHI, IDIM, ITHI ) - u(8) = ParamData%FFData( IZLO, IYLO, IDIM, ITHI ) - - FF_Interp(IDIM) = SUM ( N * u ) - - - END DO !IDIM - - ELSE - - !------------------------------------------------------------------------------------------------- - ! Interpolate on the tower array - !------------------------------------------------------------------------------------------------- - - DO IDIM=1,ParamData%NFFComp ! all the components - - !---------------------------------------------------------------------------------------------- - ! Interpolate between the two times using an area interpolation. - !---------------------------------------------------------------------------------------------- - - ! Setup the scaling factors. Set the unused portion of the array to zero - M(1) = ( 1.0_ReKi + Z )*( 1.0_ReKi - T ) - M(2) = ( 1.0_ReKi + Z )*( 1.0_ReKi + T ) - M(3) = ( 1.0_ReKi - Z )*( 1.0_ReKi - T ) - M(4) = ( 1.0_ReKi - Z )*( 1.0_ReKi + T ) - M = M / 4.0_ReKi ! normalize - - IF (IZHI > ParamData%NTGrids) THEN - v(1) = 0.0_ReKi ! on the ground - v(2) = 0.0_ReKi ! on the ground - ELSE - v(1) = ParamData%FFTower( IDIM, IZHI, ITLO ) - v(2) = ParamData%FFTower( IDIM, IZHI, ITHI ) - END IF - - v(3) = ParamData%FFTower( IDIM, IZLO, ITLO ) - v(4) = ParamData%FFTower( IDIM, IZLO, ITHI ) - - FF_Interp(IDIM) = SUM ( M * v ) - - - END DO !IDIM - - ENDIF ! OnGrid - RETURN - - END FUNCTION FF_Interp - !==================================================================================================== !! This subroutine cleans up any data that is still allocated. The (possibly) open files are diff --git a/modules/inflowwind/src/IfW_BladedFFWind.txt b/modules/inflowwind/src/IfW_BladedFFWind.txt index 3c393a355f..0893b0527c 100644 --- a/modules/inflowwind/src/IfW_BladedFFWind.txt +++ b/modules/inflowwind/src/IfW_BladedFFWind.txt @@ -8,49 +8,32 @@ ################################################################################################################################### include Registry_NWTC_Library.txt +usefrom IfW_FFWind_Base.txt ######################### -typedef IfW_BladedFFWind/IfW_BladedFFWind InitInputType CHARACTER(1024) WindFileName - - - "Name of the wind file to use" - -typedef ^ ^ Logical TowerFileExist - - - "Tower file exists" - -typedef ^ ^ IntKi SumFileUnit - - - "Unit number for the summary file (-1 for none). Provided by IfW." - +typedef IfW_BladedFFWind/IfW_BladedFFWind InitInputType CHARACTER(1024) WindFileName - - - "Name of the wind file to use" - +typedef ^ ^ Logical TowerFileExist - - - "Tower file exists" - +typedef ^ ^ IntKi SumFileUnit - - - "Unit number for the summary file (-1 for none). Provided by IfW." - +typedef ^ ^ Logical NativeBladedFmt - - - "Whether this is native Bladed (needs wind profile and TI scaling) or not" - +#typedef ^ ^ IfW_FFWind_InitInputType FF - - - "scaling data (provided for native Bladed format)" - # Init Output typedef ^ InitOutputType ProgDesc Ver - - - "Version information off FFWind submodule" - typedef ^ ^ ReKi TI {3} - - "Turbulence intensity given in the file" - +typedef ^ InitOutputType ReKi PropagationDir - - - "Propogation direction from native Bladed format" degrees +typedef ^ InitOutputType ReKi VFlowAngle - - - "Vertical flow angle from native Bladed format" degrees # ..... Misc/Optimization variables................................................................................................. # Define any data that are used only for efficiency purposes (these variables are not associated with time): # e.g. indices for searching in an array, large arrays that are local variables in any routine called multiple times, etc. -typedef ^ MiscVarType IntKi TimeIndex - 0 - "An Index into the TData array" - +typedef ^ MiscVarType IntKi dummy - 0 - "An Index into the TData array" - # ..... Parameters ................................................................................................................ # Define parameters here: # Time step for integration of continuous states (if a fixed-step integrator is used) and update of discrete states: -typedef ^ ParameterType Logical Periodic - .FALSE. - "Flag to indicate if the wind file is periodic" - -typedef ^ ^ Logical TowerDataExist - .FALSE. - "If true, we specified a tower file" - -typedef ^ ^ DbKi DT - - - "Time step for cont. state integration & disc. state update" seconds -typedef ^ ^ SiKi FFData :::: - - "Array of FF data" - -typedef ^ ^ SiKi FFTower ::: - - "Array of data along tower, below FF array" - -typedef ^ ^ ReKi FFDTime - 0 - "Delta time" seconds -typedef ^ ^ ReKi FFRate - 0 - "Data rate (1/FFTime)" Hertz -typedef ^ ^ ReKi FFYHWid - 0 - "Half the grid width" meters -typedef ^ ^ ReKi FFZHWid - 0 - "Half the grid height" meters -typedef ^ ^ ReKi RefHt - 0 - "Reference (hub) height of the grid" meters -typedef ^ ^ ReKi GridBase - 0 - "the height of the bottom of the grid" meters -typedef ^ ^ ReKi InitXPosition - 0 - "the initial x position of grid (distance in FF is offset)" meters -typedef ^ ^ ReKi InvFFYD - 0 - "reciprocal of delta y" 1/meters -typedef ^ ^ ReKi InvFFZD - 0 - "reciprocal of delta z" 1/meters -typedef ^ ^ ReKi InvMFFWS - 0 - "reciprocal of mean wind speed (MeanFFWS)" seconds/meter -typedef ^ ^ ReKi MeanFFWS - 0 - "Mean wind speed (as defined in FF file), not necessarily of the portion used" meters/second -typedef ^ ^ ReKi TotalTime - 0 - "The total time of the simulation" seconds -typedef ^ ^ IntKi NFFComp - 3 - "Number of wind components" - -typedef ^ ^ IntKi NFFSteps - 0 - "Number of time steps in the FF array" - -typedef ^ ^ IntKi NYGrids - 0 - "Number of points in the lateral (y) direction of the grids" - -typedef ^ ^ IntKi NZGrids - 0 - "Number of points in the vertical (z) direction of the grids" - -typedef ^ ^ IntKi NTGrids - 0 - "Number of points in the vertical (z) direction on the tower (below the grids)" - -typedef ^ ^ IntKi WindFileFormat - - - "Binary file format description number" - +typedef ^ ParameterType IfW_FFWind_ParameterType FF - - - "Parameters used in all full-field wind types" - diff --git a/modules/inflowwind/src/IfW_BladedFFWind_Types.f90 b/modules/inflowwind/src/IfW_BladedFFWind_Types.f90 index fd15ba6f7d..6c38d1b100 100644 --- a/modules/inflowwind/src/IfW_BladedFFWind_Types.f90 +++ b/modules/inflowwind/src/IfW_BladedFFWind_Types.f90 @@ -31,6 +31,7 @@ !! unpack routines associated with each defined data type. This code is automatically generated by the FAST Registry. MODULE IfW_BladedFFWind_Types !--------------------------------------------------------------------------------------------------------------------------------- +USE IfW_FFWind_Base_Types USE NWTC_Library IMPLICIT NONE ! ========= IfW_BladedFFWind_InitInputType ======= @@ -38,44 +39,25 @@ MODULE IfW_BladedFFWind_Types CHARACTER(1024) :: WindFileName !< Name of the wind file to use [-] LOGICAL :: TowerFileExist !< Tower file exists [-] INTEGER(IntKi) :: SumFileUnit !< Unit number for the summary file (-1 for none). Provided by IfW. [-] + LOGICAL :: NativeBladedFmt !< Whether this is native Bladed (needs wind profile and TI scaling) or not [-] END TYPE IfW_BladedFFWind_InitInputType ! ======================= ! ========= IfW_BladedFFWind_InitOutputType ======= TYPE, PUBLIC :: IfW_BladedFFWind_InitOutputType TYPE(ProgDesc) :: Ver !< Version information off FFWind submodule [-] REAL(ReKi) , DIMENSION(1:3) :: TI !< Turbulence intensity given in the file [-] + REAL(ReKi) :: PropagationDir !< Propogation direction from native Bladed format [degrees] + REAL(ReKi) :: VFlowAngle !< Vertical flow angle from native Bladed format [degrees] END TYPE IfW_BladedFFWind_InitOutputType ! ======================= ! ========= IfW_BladedFFWind_MiscVarType ======= TYPE, PUBLIC :: IfW_BladedFFWind_MiscVarType - INTEGER(IntKi) :: TimeIndex = 0 !< An Index into the TData array [-] + INTEGER(IntKi) :: dummy = 0 !< An Index into the TData array [-] END TYPE IfW_BladedFFWind_MiscVarType ! ======================= ! ========= IfW_BladedFFWind_ParameterType ======= TYPE, PUBLIC :: IfW_BladedFFWind_ParameterType - LOGICAL :: Periodic = .FALSE. !< Flag to indicate if the wind file is periodic [-] - LOGICAL :: TowerDataExist = .FALSE. !< If true, we specified a tower file [-] - REAL(DbKi) :: DT !< Time step for cont. state integration & disc. state update [seconds] - REAL(SiKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: FFData !< Array of FF data [-] - REAL(SiKi) , DIMENSION(:,:,:), ALLOCATABLE :: FFTower !< Array of data along tower, below FF array [-] - REAL(ReKi) :: FFDTime = 0 !< Delta time [seconds] - REAL(ReKi) :: FFRate = 0 !< Data rate (1/FFTime) [Hertz] - REAL(ReKi) :: FFYHWid = 0 !< Half the grid width [meters] - REAL(ReKi) :: FFZHWid = 0 !< Half the grid height [meters] - REAL(ReKi) :: RefHt = 0 !< Reference (hub) height of the grid [meters] - REAL(ReKi) :: GridBase = 0 !< the height of the bottom of the grid [meters] - REAL(ReKi) :: InitXPosition = 0 !< the initial x position of grid (distance in FF is offset) [meters] - REAL(ReKi) :: InvFFYD = 0 !< reciprocal of delta y [1/meters] - REAL(ReKi) :: InvFFZD = 0 !< reciprocal of delta z [1/meters] - REAL(ReKi) :: InvMFFWS = 0 !< reciprocal of mean wind speed (MeanFFWS) [seconds/meter] - REAL(ReKi) :: MeanFFWS = 0 !< Mean wind speed (as defined in FF file), not necessarily of the portion used [meters/second] - REAL(ReKi) :: TotalTime = 0 !< The total time of the simulation [seconds] - INTEGER(IntKi) :: NFFComp = 3 !< Number of wind components [-] - INTEGER(IntKi) :: NFFSteps = 0 !< Number of time steps in the FF array [-] - INTEGER(IntKi) :: NYGrids = 0 !< Number of points in the lateral (y) direction of the grids [-] - INTEGER(IntKi) :: NZGrids = 0 !< Number of points in the vertical (z) direction of the grids [-] - INTEGER(IntKi) :: NTGrids = 0 !< Number of points in the vertical (z) direction on the tower (below the grids) [-] - INTEGER(IntKi) :: WindFileFormat !< Binary file format description number [-] + TYPE(IfW_FFWind_ParameterType) :: FF !< Parameters used in all full-field wind types [-] END TYPE IfW_BladedFFWind_ParameterType ! ======================= CONTAINS @@ -88,9 +70,6 @@ SUBROUTINE IfW_BladedFFWind_CopyInitInput( SrcInitInputData, DstInitInputData, C ! Local INTEGER(IntKi) :: i,j,k INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_BladedFFWind_CopyInitInput' @@ -100,6 +79,7 @@ SUBROUTINE IfW_BladedFFWind_CopyInitInput( SrcInitInputData, DstInitInputData, C DstInitInputData%WindFileName = SrcInitInputData%WindFileName DstInitInputData%TowerFileExist = SrcInitInputData%TowerFileExist DstInitInputData%SumFileUnit = SrcInitInputData%SumFileUnit + DstInitInputData%NativeBladedFmt = SrcInitInputData%NativeBladedFmt END SUBROUTINE IfW_BladedFFWind_CopyInitInput SUBROUTINE IfW_BladedFFWind_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -151,6 +131,7 @@ SUBROUTINE IfW_BladedFFWind_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, E Int_BufSz = Int_BufSz + 1*LEN(InData%WindFileName) ! WindFileName Int_BufSz = Int_BufSz + 1 ! TowerFileExist Int_BufSz = Int_BufSz + 1 ! SumFileUnit + Int_BufSz = Int_BufSz + 1 ! NativeBladedFmt IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -186,6 +167,8 @@ SUBROUTINE IfW_BladedFFWind_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, E Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = InData%SumFileUnit Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%NativeBladedFmt, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE IfW_BladedFFWind_PackInitInput SUBROUTINE IfW_BladedFFWind_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -202,9 +185,6 @@ SUBROUTINE IfW_BladedFFWind_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata INTEGER(IntKi) :: Int_Xferred INTEGER(IntKi) :: i INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_BladedFFWind_UnPackInitInput' @@ -226,6 +206,8 @@ SUBROUTINE IfW_BladedFFWind_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata Int_Xferred = Int_Xferred + 1 OutData%SumFileUnit = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 + OutData%NativeBladedFmt = TRANSFER(IntKiBuf(Int_Xferred), OutData%NativeBladedFmt) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE IfW_BladedFFWind_UnPackInitInput SUBROUTINE IfW_BladedFFWind_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -247,6 +229,8 @@ SUBROUTINE IfW_BladedFFWind_CopyInitOutput( SrcInitOutputData, DstInitOutputData CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN DstInitOutputData%TI = SrcInitOutputData%TI + DstInitOutputData%PropagationDir = SrcInitOutputData%PropagationDir + DstInitOutputData%VFlowAngle = SrcInitOutputData%VFlowAngle END SUBROUTINE IfW_BladedFFWind_CopyInitOutput SUBROUTINE IfW_BladedFFWind_DestroyInitOutput( InitOutputData, ErrStat, ErrMsg ) @@ -315,6 +299,8 @@ SUBROUTINE IfW_BladedFFWind_PackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, DEALLOCATE(Int_Buf) END IF Re_BufSz = Re_BufSz + SIZE(InData%TI) ! TI + Re_BufSz = Re_BufSz + 1 ! PropagationDir + Re_BufSz = Re_BufSz + 1 ! VFlowAngle IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -374,6 +360,10 @@ SUBROUTINE IfW_BladedFFWind_PackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ReKiBuf(Re_Xferred) = InData%TI(i1) Re_Xferred = Re_Xferred + 1 END DO + ReKiBuf(Re_Xferred) = InData%PropagationDir + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%VFlowAngle + Re_Xferred = Re_Xferred + 1 END SUBROUTINE IfW_BladedFFWind_PackInitOutput SUBROUTINE IfW_BladedFFWind_UnPackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -449,6 +439,10 @@ SUBROUTINE IfW_BladedFFWind_UnPackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdat OutData%TI(i1) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO + OutData%PropagationDir = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%VFlowAngle = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE IfW_BladedFFWind_UnPackInitOutput SUBROUTINE IfW_BladedFFWind_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) @@ -465,7 +459,7 @@ SUBROUTINE IfW_BladedFFWind_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrSta ! ErrStat = ErrID_None ErrMsg = "" - DstMiscData%TimeIndex = SrcMiscData%TimeIndex + DstMiscData%dummy = SrcMiscData%dummy END SUBROUTINE IfW_BladedFFWind_CopyMisc SUBROUTINE IfW_BladedFFWind_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -514,7 +508,7 @@ SUBROUTINE IfW_BladedFFWind_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSta Re_BufSz = 0 Db_BufSz = 0 Int_BufSz = 0 - Int_BufSz = Int_BufSz + 1 ! TimeIndex + Int_BufSz = Int_BufSz + 1 ! dummy IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -542,7 +536,7 @@ SUBROUTINE IfW_BladedFFWind_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSta Db_Xferred = 1 Int_Xferred = 1 - IntKiBuf(Int_Xferred) = InData%TimeIndex + IntKiBuf(Int_Xferred) = InData%dummy Int_Xferred = Int_Xferred + 1 END SUBROUTINE IfW_BladedFFWind_PackMisc @@ -572,7 +566,7 @@ SUBROUTINE IfW_BladedFFWind_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, Err Re_Xferred = 1 Db_Xferred = 1 Int_Xferred = 1 - OutData%TimeIndex = IntKiBuf(Int_Xferred) + OutData%dummy = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 END SUBROUTINE IfW_BladedFFWind_UnPackMisc @@ -584,71 +578,15 @@ SUBROUTINE IfW_BladedFFWind_CopyParam( SrcParamData, DstParamData, CtrlCode, Err CHARACTER(*), INTENT( OUT) :: ErrMsg ! Local INTEGER(IntKi) :: i,j,k - INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_BladedFFWind_CopyParam' ! ErrStat = ErrID_None ErrMsg = "" - DstParamData%Periodic = SrcParamData%Periodic - DstParamData%TowerDataExist = SrcParamData%TowerDataExist - DstParamData%DT = SrcParamData%DT -IF (ALLOCATED(SrcParamData%FFData)) THEN - i1_l = LBOUND(SrcParamData%FFData,1) - i1_u = UBOUND(SrcParamData%FFData,1) - i2_l = LBOUND(SrcParamData%FFData,2) - i2_u = UBOUND(SrcParamData%FFData,2) - i3_l = LBOUND(SrcParamData%FFData,3) - i3_u = UBOUND(SrcParamData%FFData,3) - i4_l = LBOUND(SrcParamData%FFData,4) - i4_u = UBOUND(SrcParamData%FFData,4) - IF (.NOT. ALLOCATED(DstParamData%FFData)) THEN - ALLOCATE(DstParamData%FFData(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u,i4_l:i4_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%FFData.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstParamData%FFData = SrcParamData%FFData -ENDIF -IF (ALLOCATED(SrcParamData%FFTower)) THEN - i1_l = LBOUND(SrcParamData%FFTower,1) - i1_u = UBOUND(SrcParamData%FFTower,1) - i2_l = LBOUND(SrcParamData%FFTower,2) - i2_u = UBOUND(SrcParamData%FFTower,2) - i3_l = LBOUND(SrcParamData%FFTower,3) - i3_u = UBOUND(SrcParamData%FFTower,3) - IF (.NOT. ALLOCATED(DstParamData%FFTower)) THEN - ALLOCATE(DstParamData%FFTower(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%FFTower.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstParamData%FFTower = SrcParamData%FFTower -ENDIF - DstParamData%FFDTime = SrcParamData%FFDTime - DstParamData%FFRate = SrcParamData%FFRate - DstParamData%FFYHWid = SrcParamData%FFYHWid - DstParamData%FFZHWid = SrcParamData%FFZHWid - DstParamData%RefHt = SrcParamData%RefHt - DstParamData%GridBase = SrcParamData%GridBase - DstParamData%InitXPosition = SrcParamData%InitXPosition - DstParamData%InvFFYD = SrcParamData%InvFFYD - DstParamData%InvFFZD = SrcParamData%InvFFZD - DstParamData%InvMFFWS = SrcParamData%InvMFFWS - DstParamData%MeanFFWS = SrcParamData%MeanFFWS - DstParamData%TotalTime = SrcParamData%TotalTime - DstParamData%NFFComp = SrcParamData%NFFComp - DstParamData%NFFSteps = SrcParamData%NFFSteps - DstParamData%NYGrids = SrcParamData%NYGrids - DstParamData%NZGrids = SrcParamData%NZGrids - DstParamData%NTGrids = SrcParamData%NTGrids - DstParamData%WindFileFormat = SrcParamData%WindFileFormat + CALL IfW_FFWind_CopyParam( SrcParamData%FF, DstParamData%FF, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE IfW_BladedFFWind_CopyParam SUBROUTINE IfW_BladedFFWind_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -660,12 +598,7 @@ SUBROUTINE IfW_BladedFFWind_DestroyParam( ParamData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" -IF (ALLOCATED(ParamData%FFData)) THEN - DEALLOCATE(ParamData%FFData) -ENDIF -IF (ALLOCATED(ParamData%FFTower)) THEN - DEALLOCATE(ParamData%FFTower) -ENDIF + CALL IfW_FFWind_DestroyParam( ParamData%FF, ErrStat, ErrMsg ) END SUBROUTINE IfW_BladedFFWind_DestroyParam SUBROUTINE IfW_BladedFFWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -703,37 +636,24 @@ SUBROUTINE IfW_BladedFFWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSt Re_BufSz = 0 Db_BufSz = 0 Int_BufSz = 0 - Int_BufSz = Int_BufSz + 1 ! Periodic - Int_BufSz = Int_BufSz + 1 ! TowerDataExist - Db_BufSz = Db_BufSz + 1 ! DT - Int_BufSz = Int_BufSz + 1 ! FFData allocated yes/no - IF ( ALLOCATED(InData%FFData) ) THEN - Int_BufSz = Int_BufSz + 2*4 ! FFData upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%FFData) ! FFData - END IF - Int_BufSz = Int_BufSz + 1 ! FFTower allocated yes/no - IF ( ALLOCATED(InData%FFTower) ) THEN - Int_BufSz = Int_BufSz + 2*3 ! FFTower upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%FFTower) ! FFTower - END IF - Re_BufSz = Re_BufSz + 1 ! FFDTime - Re_BufSz = Re_BufSz + 1 ! FFRate - Re_BufSz = Re_BufSz + 1 ! FFYHWid - Re_BufSz = Re_BufSz + 1 ! FFZHWid - Re_BufSz = Re_BufSz + 1 ! RefHt - Re_BufSz = Re_BufSz + 1 ! GridBase - Re_BufSz = Re_BufSz + 1 ! InitXPosition - Re_BufSz = Re_BufSz + 1 ! InvFFYD - Re_BufSz = Re_BufSz + 1 ! InvFFZD - Re_BufSz = Re_BufSz + 1 ! InvMFFWS - Re_BufSz = Re_BufSz + 1 ! MeanFFWS - Re_BufSz = Re_BufSz + 1 ! TotalTime - Int_BufSz = Int_BufSz + 1 ! NFFComp - Int_BufSz = Int_BufSz + 1 ! NFFSteps - Int_BufSz = Int_BufSz + 1 ! NYGrids - Int_BufSz = Int_BufSz + 1 ! NZGrids - Int_BufSz = Int_BufSz + 1 ! NTGrids - Int_BufSz = Int_BufSz + 1 ! WindFileFormat + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + Int_BufSz = Int_BufSz + 3 ! FF: size of buffers for each call to pack subtype + CALL IfW_FFWind_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, .TRUE. ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FF + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FF + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FF + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -761,103 +681,34 @@ SUBROUTINE IfW_BladedFFWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSt Db_Xferred = 1 Int_Xferred = 1 - IntKiBuf(Int_Xferred) = TRANSFER(InData%Periodic, IntKiBuf(1)) - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = TRANSFER(InData%TowerDataExist, IntKiBuf(1)) - Int_Xferred = Int_Xferred + 1 - DbKiBuf(Db_Xferred) = InData%DT - Db_Xferred = Db_Xferred + 1 - IF ( .NOT. ALLOCATED(InData%FFData) ) THEN - IntKiBuf( Int_Xferred ) = 0 - Int_Xferred = Int_Xferred + 1 - ELSE - IntKiBuf( Int_Xferred ) = 1 - Int_Xferred = Int_Xferred + 1 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,2) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,3) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,3) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,4) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,4) - Int_Xferred = Int_Xferred + 2 - - DO i4 = LBOUND(InData%FFData,4), UBOUND(InData%FFData,4) - DO i3 = LBOUND(InData%FFData,3), UBOUND(InData%FFData,3) - DO i2 = LBOUND(InData%FFData,2), UBOUND(InData%FFData,2) - DO i1 = LBOUND(InData%FFData,1), UBOUND(InData%FFData,1) - ReKiBuf(Re_Xferred) = InData%FFData(i1,i2,i3,i4) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END DO - END IF - IF ( .NOT. ALLOCATED(InData%FFTower) ) THEN - IntKiBuf( Int_Xferred ) = 0 - Int_Xferred = Int_Xferred + 1 - ELSE - IntKiBuf( Int_Xferred ) = 1 - Int_Xferred = Int_Xferred + 1 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFTower,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFTower,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFTower,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFTower,2) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFTower,3) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFTower,3) - Int_Xferred = Int_Xferred + 2 - - DO i3 = LBOUND(InData%FFTower,3), UBOUND(InData%FFTower,3) - DO i2 = LBOUND(InData%FFTower,2), UBOUND(InData%FFTower,2) - DO i1 = LBOUND(InData%FFTower,1), UBOUND(InData%FFTower,1) - ReKiBuf(Re_Xferred) = InData%FFTower(i1,i2,i3) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END IF - ReKiBuf(Re_Xferred) = InData%FFDTime - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%FFRate - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%FFYHWid - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%FFZHWid - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%RefHt - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%GridBase - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%InitXPosition - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%InvFFYD - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%InvFFZD - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%InvMFFWS - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%MeanFFWS - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%TotalTime - Re_Xferred = Re_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NFFComp - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NFFSteps - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NYGrids - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NZGrids - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NTGrids - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%WindFileFormat - Int_Xferred = Int_Xferred + 1 + CALL IfW_FFWind_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, OnlySize ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Re_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Re_Buf) > 0) ReKiBuf( Re_Xferred:Re_Xferred+SIZE(Re_Buf)-1 ) = Re_Buf + Re_Xferred = Re_Xferred + SIZE(Re_Buf) + DEALLOCATE(Re_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Db_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Db_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Db_Buf) > 0) DbKiBuf( Db_Xferred:Db_Xferred+SIZE(Db_Buf)-1 ) = Db_Buf + Db_Xferred = Db_Xferred + SIZE(Db_Buf) + DEALLOCATE(Db_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Int_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Int_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Int_Buf) > 0) IntKiBuf( Int_Xferred:Int_Xferred+SIZE(Int_Buf)-1 ) = Int_Buf + Int_Xferred = Int_Xferred + SIZE(Int_Buf) + DEALLOCATE(Int_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF END SUBROUTINE IfW_BladedFFWind_PackParam SUBROUTINE IfW_BladedFFWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -873,10 +724,6 @@ SUBROUTINE IfW_BladedFFWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, Er INTEGER(IntKi) :: Db_Xferred INTEGER(IntKi) :: Int_Xferred INTEGER(IntKi) :: i - INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_BladedFFWind_UnPackParam' @@ -890,109 +737,46 @@ SUBROUTINE IfW_BladedFFWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, Er Re_Xferred = 1 Db_Xferred = 1 Int_Xferred = 1 - OutData%Periodic = TRANSFER(IntKiBuf(Int_Xferred), OutData%Periodic) - Int_Xferred = Int_Xferred + 1 - OutData%TowerDataExist = TRANSFER(IntKiBuf(Int_Xferred), OutData%TowerDataExist) - Int_Xferred = Int_Xferred + 1 - OutData%DT = DbKiBuf(Db_Xferred) - Db_Xferred = Db_Xferred + 1 - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FFData not allocated - Int_Xferred = Int_Xferred + 1 - ELSE - Int_Xferred = Int_Xferred + 1 - i1_l = IntKiBuf( Int_Xferred ) - i1_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i2_l = IntKiBuf( Int_Xferred ) - i2_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i3_l = IntKiBuf( Int_Xferred ) - i3_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i4_l = IntKiBuf( Int_Xferred ) - i4_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - IF (ALLOCATED(OutData%FFData)) DEALLOCATE(OutData%FFData) - ALLOCATE(OutData%FFData(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u,i4_l:i4_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FFData.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - DO i4 = LBOUND(OutData%FFData,4), UBOUND(OutData%FFData,4) - DO i3 = LBOUND(OutData%FFData,3), UBOUND(OutData%FFData,3) - DO i2 = LBOUND(OutData%FFData,2), UBOUND(OutData%FFData,2) - DO i1 = LBOUND(OutData%FFData,1), UBOUND(OutData%FFData,1) - OutData%FFData(i1,i2,i3,i4) = REAL(ReKiBuf(Re_Xferred), SiKi) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END DO - END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FFTower not allocated - Int_Xferred = Int_Xferred + 1 - ELSE - Int_Xferred = Int_Xferred + 1 - i1_l = IntKiBuf( Int_Xferred ) - i1_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i2_l = IntKiBuf( Int_Xferred ) - i2_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i3_l = IntKiBuf( Int_Xferred ) - i3_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - IF (ALLOCATED(OutData%FFTower)) DEALLOCATE(OutData%FFTower) - ALLOCATE(OutData%FFTower(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FFTower.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - DO i3 = LBOUND(OutData%FFTower,3), UBOUND(OutData%FFTower,3) - DO i2 = LBOUND(OutData%FFTower,2), UBOUND(OutData%FFTower,2) - DO i1 = LBOUND(OutData%FFTower,1), UBOUND(OutData%FFTower,1) - OutData%FFTower(i1,i2,i3) = REAL(ReKiBuf(Re_Xferred), SiKi) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END IF - OutData%FFDTime = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%FFRate = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%FFYHWid = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%FFZHWid = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%RefHt = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%GridBase = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%InitXPosition = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%InvFFYD = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%InvFFZD = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%InvMFFWS = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%MeanFFWS = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%TotalTime = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%NFFComp = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%NFFSteps = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%NYGrids = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%NZGrids = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%NTGrids = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%WindFileFormat = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Re_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Re_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Re_Buf = ReKiBuf( Re_Xferred:Re_Xferred+Buf_size-1 ) + Re_Xferred = Re_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Db_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Db_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Db_Buf = DbKiBuf( Db_Xferred:Db_Xferred+Buf_size-1 ) + Db_Xferred = Db_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Int_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Int_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) + Int_Xferred = Int_Xferred + Buf_size + END IF + CALL IfW_FFWind_UnpackParam( Re_Buf, Db_Buf, Int_Buf, OutData%FF, ErrStat2, ErrMsg2 ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) + IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) + IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) END SUBROUTINE IfW_BladedFFWind_UnPackParam END MODULE IfW_BladedFFWind_Types diff --git a/modules/inflowwind/src/IfW_FFWind_Base.f90 b/modules/inflowwind/src/IfW_FFWind_Base.f90 new file mode 100644 index 0000000000..ecddece01a --- /dev/null +++ b/modules/inflowwind/src/IfW_FFWind_Base.f90 @@ -0,0 +1,1163 @@ +!> This module uses full-field binary wind files to determine the wind inflow. +!! This module assumes that the origin, (0,0,0), is located at the tower centerline at ground level, +!! and that all units are specified in the metric system (using meters and seconds). +!! Data is shifted by half the grid width to account for turbine yaw (so that data in the X +!! direction actually starts at -1*p%FFYHWid meters). +MODULE IfW_FFWind_Base +!! +!! Created 25-Sep-2009 by B. Jonkman, National Renewable Energy Laboratory +!! using subroutines and modules from AeroDyn v12.58 +!! +!!---------------------------------------------------------------------------------------------------- +!! Feb 2013 v2.00.00 A. Platt +!! -- updated to the new framework +!! -- Modified to use NWTC_Library v. 2.0 +!! -- Note: Jacobians are not included in this version. +!! +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2015-2106 National Renewable Energy Laboratory +! +! This file is part of InflowWind. +! +! 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. +! +!********************************************************************************************************************************** + + USE NWTC_Library + USE IfW_FFWind_Base_Types + + IMPLICIT NONE + + + INTEGER(IntKi), PARAMETER :: WindProfileType_None = -1 !< don't add wind profile; already included in input + INTEGER(IntKi), PARAMETER :: WindProfileType_Constant = 0 !< constant wind + INTEGER(IntKi), PARAMETER :: WindProfileType_Log = 1 !< logarithmic + INTEGER(IntKi), PARAMETER :: WindProfileType_PL = 2 !< power law + + INTEGER(IntKi), PARAMETER :: ScaleMethod_None = 0 !< no scaling + INTEGER(IntKi), PARAMETER :: ScaleMethod_Direct = 1 !< direct scaling factors + INTEGER(IntKi), PARAMETER :: ScaleMethod_StdDev = 2 !< requested standard deviation + + +CONTAINS +!==================================================================================================== + +!==================================================================================================== +!> This routine acts as a wrapper for the GetWindSpeed routine. It steps through the array of input +!! positions and calls the GetWindSpeed routine to calculate the velocities at each point. +SUBROUTINE IfW_FFWind_CalcOutput(Time, PositionXYZ, p, Velocity, DiskVel, ErrStat, ErrMsg) + + IMPLICIT NONE + + + ! Passed Variables + REAL(DbKi), INTENT(IN ) :: Time !< time from the start of the simulation + REAL(ReKi), INTENT(IN ) :: PositionXYZ(:,:) !< Array of XYZ coordinates, 3xN + TYPE(IfW_FFWind_ParameterType), INTENT(IN ) :: p !< Parameters + REAL(ReKi), INTENT(INOUT) :: Velocity(:,:) !< Velocity output at Time (Set to INOUT so that array does not get deallocated) + REAL(ReKi), INTENT( OUT) :: DiskVel(3) !< HACK for AD14: disk velocity output at Time + + ! Error handling + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status + CHARACTER(*), INTENT( OUT) :: ErrMsg !< The error message + + ! local variables + INTEGER(IntKi) :: NumPoints ! Number of points specified by the PositionXYZ array + + ! local counters + INTEGER(IntKi) :: PointNum ! a loop counter for the current point + + ! temporary variables + INTEGER(IntKi) :: TmpErrStat ! temporary error status + CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message + CHARACTER(*), PARAMETER :: RoutineName = 'IfW_FFWind_CalcOutput' + + + !------------------------------------------------------------------------------------------------- + ! Check that the module has been initialized. + !------------------------------------------------------------------------------------------------- + + ErrStat = ErrID_None + ErrMsg = '' + + !------------------------------------------------------------------------------------------------- + ! Initialize some things + !------------------------------------------------------------------------------------------------- + + + ! The array is transposed so that the number of points is the second index, x/y/z is the first. + ! This is just in case we only have a single point, the SIZE command returns the correct number of points. + NumPoints = SIZE(PositionXYZ,2) + + + ! Step through all the positions and get the velocities + DO PointNum = 1, NumPoints + + ! Calculate the velocity for the position + Velocity(:,PointNum) = FFWind_Interp(Time,PositionXYZ(:,PointNum),p,TmpErrStat,TmpErrMsg) + + ! Error handling + IF (TmpErrStat /= ErrID_None) THEN ! adding this so we don't have to convert numbers to strings every time + CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName//" [position=("// & + TRIM(Num2LStr(PositionXYZ(1,PointNum)))//", "// & + TRIM(Num2LStr(PositionXYZ(2,PointNum)))//", "// & + TRIM(Num2LStr(PositionXYZ(3,PointNum)))//") in wind-file coordinates]" ) + IF (ErrStat >= AbortErrLev) RETURN + END IF + + ENDDO + + IF (p%AddMeanAfterInterp) THEN + DO PointNum = 1, NumPoints + Velocity(1,PointNum) = Velocity(1,PointNum) + CalculateMeanVelocity(p,PositionXYZ(3,PointNum)) + ENDDO + END IF + + + !REMOVE THIS for AeroDyn 15 + ! Return the average disk velocity values needed by AeroDyn 14. This is the WindInf_ADhack_diskVel routine. + DiskVel(1) = p%MeanFFWS + DiskVel(2:3) = 0.0_ReKi + + + RETURN + +END SUBROUTINE IfW_FFWind_CalcOutput +!+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +!> This function is used to interpolate into the full-field wind array or tower array if it has +!! been defined and is necessary for the given inputs. It receives X, Y, Z and +!! TIME from the calling routine. It then computes a time shift due to a nonzero X based upon +!! the average windspeed. The modified time is used to decide which pair of time slices to interpolate +!! within and between. After finding the two time slices, it decides which four grid points bound the +!! (Y,Z) pair. It does a bilinear interpolation for each time slice. Linear interpolation is then used +!! to interpolate between time slices. This routine assumes that X is downwind, Y is to the left when +!! looking downwind and Z is up. It also assumes that no extrapolation will be needed. +!! +!! If tower points are used, it assumes the velocity at the ground is 0. It interpolates between +!! heights and between time slices, but ignores the Y input. +!! +!! 11/07/1994 - Created by M. Buhl from the original TURBINT. +!! 09/25/1997 - Modified by M. Buhl to use f90 constructs and new variable names. Renamed to FF_Interp. +!! 09/23/2009 - Modified by B. Jonkman to use arguments instead of modules to determine time and position. +!! Height is now relative to the ground +!! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 +FUNCTION FFWind_Interp(Time, Position, p, ErrStat, ErrMsg) + + IMPLICIT NONE + + CHARACTER(*), PARAMETER :: RoutineName="FFWind_Interp" + + REAL(DbKi), INTENT(IN ) :: Time !< time (s) + REAL(ReKi), INTENT(IN ) :: Position(3) !< takes the place of XGrnd, YGrnd, ZGrnd + TYPE(IfW_FFWind_ParameterType), INTENT(IN ) :: p !< Parameters + REAL(ReKi) :: FFWind_Interp(3) !< The U, V, W velocities + + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status + CHARACTER(*), INTENT( OUT) :: ErrMsg !< error message + + ! Local Variables: + + REAL(ReKi) :: TimeShifted + REAL(ReKi),PARAMETER :: Tol = 1.0E-3 ! a tolerance for determining if two reals are the same (for extrapolation) + REAL(ReKi) :: T + REAL(ReKi) :: TGRID + REAL(ReKi) :: Y + REAL(ReKi) :: YGRID + REAL(ReKi) :: Z + REAL(ReKi) :: ZGRID + REAL(ReKi) :: N(8) ! array for holding scaling factors for the interpolation algorithm + REAL(ReKi) :: u(8) ! array for holding the corner values for the interpolation algorithm across a cubic volume + REAL(ReKi) :: M(4) ! array for holding scaling factors for the interpolation algorithm + REAL(ReKi) :: v(4) ! array for holding the corner values for the interpolation algorithm across an area + + INTEGER(IntKi) :: IDIM + INTEGER(IntKi) :: ITHI + INTEGER(IntKi) :: ITLO + INTEGER(IntKi) :: IYHI + INTEGER(IntKi) :: IYLO + INTEGER(IntKi) :: IZHI + INTEGER(IntKi) :: IZLO + + LOGICAL :: OnGrid + + !------------------------------------------------------------------------------------------------- + ! Initialize variables + !------------------------------------------------------------------------------------------------- + + FFWind_Interp(:) = 0.0_ReKi ! the output velocities (in case p%NFFComp /= 3) + + ErrStat = ErrID_None + ErrMsg = "" + + + !------------------------------------------------------------------------------------------------- + ! By definition, wind below the ground is always zero (no turbulence, either). + !------------------------------------------------------------------------------------------------- + IF ( Position(3) <= 0.0_ReKi ) THEN + FFWind_Interp = 0.0_ReKi + RETURN + END IF + + + !------------------------------------------------------------------------------------------------- + ! Find the bounding time slices. + !------------------------------------------------------------------------------------------------- + + ! Perform the time shift. At time=0, a point half the grid width downstream (p%FFYHWid) will index into the zero time slice. + ! If we did not do this, any point downstream of the tower at the beginning of the run would index outside of the array. + ! This all assumes the grid width is at least as large as the rotor. If it isn't, then the interpolation will not work. + + + TimeShifted = TIME + ( p%InitXPosition - Position(1) )*p%InvMFFWS ! in distance, X: InputInfo%Position(1) - p%InitXPosition - TIME*p%MeanFFWS + + + IF ( p%Periodic ) THEN ! translate TimeShifted to ( 0 <= TimeShifted < p%TotalTime ) + + TimeShifted = MODULO( TimeShifted, p%TotalTime ) + ! If TimeShifted is a very small negative number, modulo returns the incorrect value due to internal rounding errors. + ! See bug report #471 + IF (TimeShifted == p%TotalTime) TimeShifted = 0.0_ReKi + + TGRID = TimeShifted*p%FFRate + ITLO = INT( TGRID ) ! convert REAL to INTEGER (add 1 later because our grids start at 1, not 0) + T = 2.0_ReKi * ( TGRID - REAL(ITLO, ReKi) ) - 1.0_ReKi ! a value between -1 and 1 that indicates a relative position between ITLO and ITHI + + ITLO = ITLO + 1 + IF ( ITLO == p%NFFSteps ) THEN + ITHI = 1 + ELSE + ITHI = ITLO + 1 + ENDIF + + + ELSE + + TGRID = TimeShifted*p%FFRate + ITLO = INT( TGRID ) ! convert REAL to INTEGER (add 1 later because our grids start at 1, not 0) + T = 2.0_ReKi * ( TGRID - REAL(ITLO, ReKi) ) - 1.0_ReKi ! a value between -1 and 1 that indicates a relative position between ITLO and ITHI + + ITLO = ITLO + 1 ! add one since our grids start at 1, not 0 + ITHI = ITLO + 1 + + IF ( ITLO >= p%NFFSteps .OR. ITLO < 1 ) THEN + IF ( ITLO == p%NFFSteps ) THEN + ITHI = ITLO + IF ( T <= TOL ) THEN ! we're on the last point + T = -1.0_ReKi + ELSE ! We'll extrapolate one dt past the last value in the file + ITLO = ITHI - 1 + ENDIF + ELSE + ErrMsg = ' Error: FF wind array was exhausted at '//TRIM( Num2LStr( REAL( TIME, ReKi ) ) )// & + ' seconds (trying to access data at '//TRIM( Num2LStr( REAL( TimeShifted, ReKi ) ) )//' seconds).' + ErrStat = ErrID_Fatal + RETURN + ENDIF + ENDIF + + ENDIF + + + !------------------------------------------------------------------------------------------------- + ! Find the bounding rows for the Z position. [The lower-left corner is (1,1) when looking upwind.] + !------------------------------------------------------------------------------------------------- + + ZGRID = ( Position(3) - p%GridBase )*p%InvFFZD + + IF (ZGRID > -1*TOL) THEN + OnGrid = .TRUE. + + ! Index for start and end slices + IZLO = INT( ZGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 + IZHI = IZLO + 1 + + ! Set Z as a value between -1 and 1 for the relative location between IZLO and IZHI. + ! Subtract 1_IntKi from Z since the indices are starting at 1, not 0 + Z = 2.0_ReKi * (ZGRID - REAL(IZLO - 1_IntKi, ReKi)) - 1.0_ReKi + + IF ( IZLO < 1 ) THEN + IF ( IZLO == 0 .AND. Z >= 1.0-TOL ) THEN + Z = -1.0_ReKi + IZLO = 1 + ELSE + ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction (Z='//& + TRIM(Num2LStr(Position(3)))//' m is below the grid).' + ErrStat = ErrID_Fatal + RETURN + ENDIF + ELSEIF ( IZLO >= p%NZGrids ) THEN + IF ( IZLO == p%NZGrids .AND. Z <= TOL ) THEN + Z = -1.0_ReKi + IZHI = IZLO ! We're right on the last point, which is still okay + ELSE + ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction (Z='//& + TRIM(Num2LStr(Position(3)))//' m is above the grid).' + ErrStat = ErrID_Fatal + RETURN + ENDIF + ENDIF + + ELSE + + OnGrid = .FALSE. ! this is on the tower + + IF (p%InterpTower) then + + ! get Z between ground and bottom of grid + ZGRID = Position(3)/p%GridBase + Z = 2.0_ReKi * ZGRID - 1.0_ReKi + IZHI = 1 + IZLO = 0 + + IF ( ZGRID < 0.0_ReKi ) THEN + ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction '// & + '(height (Z='//TRIM(Num2LStr(Position(3)))//' m) is below the ground).' + ErrStat = ErrID_Fatal + RETURN + ENDIF + + ELSE + + IF ( p%NTGrids < 1) THEN + ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction '// & + '(height (Z='//TRIM(Num2LStr(Position(3)))//' m) is below the grid and no tower points are defined).' + ErrStat = ErrID_Fatal + RETURN + ENDIF + + IZLO = INT( -1.0*ZGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 + + + IF ( IZLO >= p%NTGrids ) THEN !our dz is the difference between the bottom tower point and the ground + IZLO = p%NTGrids + + ! Check that this isn't zero. Value between -1 and 1 corresponding to the relative position. + Z = 1.0_ReKi - 2.0_ReKi * (Position(3) / (p%GridBase - REAL(IZLO - 1_IntKi, ReKi)/p%InvFFZD)) + + ELSE + + ! Set Z as a value between -1 and 1 for the relative location between IZLO and IZHI. Used in the interpolation. + Z = 2.0_ReKi * (ABS(ZGRID) - REAL(IZLO - 1_IntKi, ReKi)) - 1.0_ReKi + + ENDIF + IZHI = IZLO + 1 + + ENDIF + + END IF + + IF ( OnGrid ) THEN ! The tower points don't use this + + CALL GetInterpValues() + + !------------------------------------------------------------------------------------------------- + ! Interpolate on the grid + !------------------------------------------------------------------------------------------------- + + DO IDIM=1,p%NFFComp ! all the components + + u(1) = p%FFData( IZHI, IYLO, IDIM, ITLO ) + u(2) = p%FFData( IZHI, IYHI, IDIM, ITLO ) + u(3) = p%FFData( IZLO, IYHI, IDIM, ITLO ) + u(4) = p%FFData( IZLO, IYLO, IDIM, ITLO ) + u(5) = p%FFData( IZHI, IYLO, IDIM, ITHI ) + u(6) = p%FFData( IZHI, IYHI, IDIM, ITHI ) + u(7) = p%FFData( IZLO, IYHI, IDIM, ITHI ) + u(8) = p%FFData( IZLO, IYLO, IDIM, ITHI ) + + FFWind_Interp(IDIM) = SUM ( N * u ) + + END DO !IDIM + + ELSE + + IF (p%InterpTower) THEN + + CALL GetInterpValues() + + !------------------------------------------------------------------------------------------------- + ! Interpolate on the bottom of the grid to the ground + !------------------------------------------------------------------------------------------------- + + DO IDIM=1,p%NFFComp ! all the components + + u(1) = p%FFData( IZHI, IYLO, IDIM, ITLO ) + u(2) = p%FFData( IZHI, IYHI, IDIM, ITLO ) + u(3) = 0.0_ReKi !p%FFData( IZLO, IYHI, IDIM, ITLO ) + u(4) = 0.0_ReKi !p%FFData( IZLO, IYLO, IDIM, ITLO ) + u(5) = p%FFData( IZHI, IYLO, IDIM, ITHI ) + u(6) = p%FFData( IZHI, IYHI, IDIM, ITHI ) + u(7) = 0.0_ReKi !p%FFData( IZLO, IYHI, IDIM, ITHI ) + u(8) = 0.0_ReKi !p%FFData( IZLO, IYLO, IDIM, ITHI ) + + FFWind_Interp(IDIM) = SUM ( N * u ) + + END DO !IDIM + + ELSE + + !------------------------------------------------------------------------------------------------- + ! Interpolate on the tower array + !------------------------------------------------------------------------------------------------- + ! Setup the scaling factors. Set the unused portion of the array to zero + M(1) = ( 1.0_ReKi + Z )*( 1.0_ReKi - T ) + M(2) = ( 1.0_ReKi + Z )*( 1.0_ReKi + T ) + M(3) = ( 1.0_ReKi - Z )*( 1.0_ReKi - T ) + M(4) = ( 1.0_ReKi - Z )*( 1.0_ReKi + T ) + M = M / 4.0_ReKi ! normalize + + + DO IDIM=1,p%NFFComp ! all the components + + !---------------------------------------------------------------------------------------------- + ! Interpolate between the two times using an area interpolation. + !---------------------------------------------------------------------------------------------- + + IF (IZHI > p%NTGrids) THEN + v(1) = 0.0_ReKi ! on the ground + v(2) = 0.0_ReKi ! on the ground + ELSE + v(1) = p%FFTower( IDIM, IZHI, ITLO ) + v(2) = p%FFTower( IDIM, IZHI, ITHI ) + END IF + + v(3) = p%FFTower( IDIM, IZLO, ITLO ) + v(4) = p%FFTower( IDIM, IZLO, ITHI ) + + FFWind_Interp(IDIM) = SUM ( M * v ) + + + END DO !IDIM + + END IF ! Interpolate below the grid + + ENDIF ! OnGrid + RETURN + +CONTAINS + SUBROUTINE GetInterpValues() + + !------------------------------------------------------------------------------------------------- + ! Find the bounding columns for the Y position. [The lower-left corner is (1,1) when looking upwind.] + !------------------------------------------------------------------------------------------------- + + YGRID = ( Position(2) + p%FFYHWid )*p%InvFFYD ! really, it's (Position(2) - -1.0*p%FFYHWid) + + IYLO = INT( YGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 + IYHI = IYLO + 1 + + ! Set Y as a value between -1 and 1 for the relative location between IYLO and IYHI. Used in the interpolation. + ! Subtract 1_IntKi from IYLO since grids start at index 1, not 0 + Y = 2.0_ReKi * (YGRID - REAL(IYLO - 1_IntKi, ReKi)) - 1.0_ReKi + + IF ( IYLO >= p%NYGrids .OR. IYLO < 1 ) THEN + IF ( IYLO == 0 .AND. Y >= 1.0-TOL ) THEN + Y = -1.0_ReKi + IYLO = 1 + ELSE IF ( IYLO == p%NYGrids .AND. Y <= TOL ) THEN + Y = -1.0_ReKi + IYHI = IYLO ! We're right on the last point, which is still okay + ELSE + ErrMsg = ' FF wind array boundaries violated: Grid too small in Y direction. Y='// & + TRIM(Num2LStr(Position(2)))//'; Y boundaries = ['//TRIM(Num2LStr(-1.0*p%FFYHWid))// & + ', '//TRIM(Num2LStr(p%FFYHWid))//']' + ErrStat = ErrID_Fatal ! we don't return anything + RETURN + ENDIF + ENDIF + + !------------------------------------------------------------------------------------------------- + ! Get normalization values for 3d-linear interpolation on the grid + !------------------------------------------------------------------------------------------------- + +!New Algorithm here + N(1) = ( 1.0_ReKi + Z )*( 1.0_ReKi - Y )*( 1.0_ReKi - T ) + N(2) = ( 1.0_ReKi + Z )*( 1.0_ReKi + Y )*( 1.0_ReKi - T ) + N(3) = ( 1.0_ReKi - Z )*( 1.0_ReKi + Y )*( 1.0_ReKi - T ) + N(4) = ( 1.0_ReKi - Z )*( 1.0_ReKi - Y )*( 1.0_ReKi - T ) + N(5) = ( 1.0_ReKi + Z )*( 1.0_ReKi - Y )*( 1.0_ReKi + T ) + N(6) = ( 1.0_ReKi + Z )*( 1.0_ReKi + Y )*( 1.0_ReKi + T ) + N(7) = ( 1.0_ReKi - Z )*( 1.0_ReKi + Y )*( 1.0_ReKi + T ) + N(8) = ( 1.0_ReKi - Z )*( 1.0_ReKi - Y )*( 1.0_ReKi + T ) + N = N / REAL( SIZE(N), ReKi ) ! normalize + + END SUBROUTINE GetInterpValues +END FUNCTION FFWind_Interp +!==================================================================================================== +!> This routine is used read scale the full-field turbulence data stored in HAWC format. +SUBROUTINE ScaleTurbulence(InitInp, FFData, ScaleFactors, ErrStat, ErrMsg) + + ! Passed Variables + TYPE(IfW_FFWind_InitInputType), INTENT(IN ) :: InitInp !< Initialization input data passed to the module + REAL(SiKi), INTENT(INOUT) :: FFData(:,:,:,:) !< full-field wind inflow data + REAL(ReKi), INTENT( OUT) :: ScaleFactors(3) !< scaling factors that were used + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< determines if an error has been encountered + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about errors + + ! Local Variables: + ! note that the variables used to compute statistics use double precision: + REAL(DbKi) :: v(3) ! instanteanous wind speed at target position + REAL(DbKi) :: vMean(3) ! average wind speeds over time at target position + REAL(DbKi) :: vSum(3) ! sum over time of wind speeds at target position + REAL(DbKi) :: vSum2(3) ! sum of wind speeds squared + REAL(ReKi) :: ActualSigma(3) ! computed standard deviation + + INTEGER :: ic ! Loop counter for wind component + INTEGER :: ix ! Loop counter for x or t + INTEGER :: iy ! Loop counter for y + INTEGER :: iz ! Loop counter for z + + INTEGER :: nc ! number of FF wind components + INTEGER :: nx ! size of x (or t) dimension of turbulence box + INTEGER :: ny ! size of y dimension of turbulence box + INTEGER :: nz ! size of z dimension of turbulence box + + CHARACTER(*), PARAMETER :: RoutineName = 'ScaleTurbulence' + + + + ErrStat = ErrID_None + ErrMsg = "" + + if ( InitInp%ScaleMethod == ScaleMethod_None ) then + + ! don't scale FFWind: + ScaleFactors = 1.0_ReKi + + else ! ScaleMethod_Direct or ScaleMethod_StdDev + + !.............................. + ! determine the scaling factors: + !.............................. + + if ( InitInp%ScaleMethod == ScaleMethod_Direct ) then + ! Use the scaling factors specified in the input file: + ScaleFactors = InitInp%sf + + else !if ( InitInp%ScaleMethod == ScaleMethod_StdDev ) then + ! compute the scale factor to get requested sigma: + + nz = size(FFData,1) + ny = size(FFData,2) + nc = size(FFData,3) + nx = size(FFData,4) + + ! find the center point of the grid (if we don't have an odd number of grid points, we'll pick the point closest to the center) + iz = (nz + 1) / 2 ! integer division + iy = (ny + 1) / 2 ! integer division + + ! compute the actual sigma at the point specified by (iy,iz). (This sigma should be close to 1.) + v = 0.0_ReKi + vSum = 0.0_ReKi + vSum2 = 0.0_ReKi + DO ix=1,nx + v(1:nc) = FFData(iz,iy,:,ix) + + vSum = vSum + v + vSum2 = vSum2 + v**2 + ENDDO ! IX + + vMean = vSum/nx + ActualSigma = SQRT( ABS( (vSum2/nx) - vMean**2 ) ) + + ! check that the ActualSigma isn't 0 + !InitOut%sf = InitInp%SigmaF / ActualSigma ! factor = Target / actual + do ic=1,nc + if ( EqualRealNos( ActualSigma(ic), 0.0_ReKi ) ) then + ScaleFactors(ic) = 0.0_ReKi + if ( .not. EqualRealNos( InitInp%SigmaF(ic), 0.0_ReKi ) ) then + call SetErrStat( ErrID_Fatal,"Computed standard deviation is zero; cannot scale to achieve target non-zero standard deviation.", ErrStat, ErrMsg, RoutineName ) + end if + else + ScaleFactors(ic) = InitInp%SigmaF(ic) / ActualSigma(ic) + end if + end do + + end if + + !.............................. + ! scale the data using our scaling factors: + !.............................. + + do ix=1,nx + do ic = 1,nc + FFData( :, :, ic, ix ) = ScaleFactors(ic) * FFData( :, :, ic, ix ) + end do !IC + end do + + end if + +END SUBROUTINE ScaleTurbulence +!==================================================================================================== +!> This routine is used to add a mean wind profile to the HAWC format turbulence data. +SUBROUTINE AddMeanVelocity(InitInp, GridBase, dz, FFData) + + ! Passed Variables + TYPE(IfW_FFWind_InitInputType), INTENT(IN ) :: InitInp !< Initialization input data passed to the module + REAL(ReKi), INTENT(IN ) :: GridBase !< height of the lowest point on the grid + REAL(ReKi), INTENT(IN ) :: dz !< distance between two zertically consectutive grid points + REAL(SiKi), INTENT(INOUT) :: FFData(:,:,:,:) !< FF wind-inflow data + + ! Local Variables: + REAL(ReKi) :: Z ! height + REAL(ReKi) :: U ! mean wind speed + INTEGER(IntKi) :: iz ! loop counter + INTEGER(IntKi) :: nz ! number of points in the z direction + + + nz = size(FFData,1) + + DO iz = 1,nz + + Z = GridBase + ( iz - 1 )*dz + if (Z <= 0.0_ReKi) cycle + + SELECT CASE ( InitInp%WindProfileType ) + + CASE ( WindProfileType_PL ) + + U = InitInp%URef*( Z / InitInp%RefHt )**InitInp%PLExp ! [IEC 61400-1 6.3.1.2 (10)] + + CASE ( WindProfileType_Log ) + + IF ( .not. EqualRealNos( InitInp%RefHt, InitInp%Z0 ) .and. Z > 0.0_ReKi ) THEN + U = InitInp%URef*( LOG( Z / InitInp%Z0 ) )/( LOG( InitInp%RefHt / InitInp%Z0 ) ) + ELSE + U = 0.0_ReKi + ENDIF + + CASE ( WindProfileType_Constant ) + + U = InitInp%URef + + CASE DEFAULT ! WindProfileType_None + + U = 0.0_ReKi + + END SELECT + + FFData( iz, :, 1, : ) = FFData( iz, :, 1, : ) + U + + END DO ! iz + + +END SUBROUTINE AddMeanVelocity +!==================================================================================================== +FUNCTION CalculateMeanVelocity(p,z) RESULT(u) + + TYPE(IfW_FFWind_ParameterType), INTENT(IN ) :: p !< Parameters + REAL(ReKi) , INTENT(IN ) :: Z ! height + REAL(ReKi) :: u ! mean wind speed at height z + + SELECT CASE ( p%WindProfileType ) + + CASE ( WindProfileType_PL ) + + U = p%MeanFFWS*( Z / p%RefHt )**p%PLExp ! [IEC 61400-1 6.3.1.2 (10)] + + CASE ( WindProfileType_Log ) + + IF ( .not. EqualRealNos( p%RefHt, p%Z0 ) .and. Z > 0.0_ReKi ) THEN + U = p%MeanFFWS*( LOG( Z / p%Z0 ) )/( LOG( p%RefHt / p%Z0 ) ) + ELSE + U = 0.0_ReKi + ENDIF + + CASE ( WindProfileType_Constant ) + + U = p%MeanFFWS + + CASE DEFAULT + + U = 0.0_ReKi + + END SELECT + +END FUNCTION CalculateMeanVelocity +!==================================================================================================== +!> This routine is used to add a subtract the mean wind speed from turbulence data (so that the added mean can be added later). +!! Note that this does NOT scale using the length of the wind simulation, so there may be differences with the HAWC implementation. +SUBROUTINE SubtractMeanVelocity(FFData) + + ! Passed Variables + REAL(SiKi), INTENT(INOUT) :: FFData(:,:,:,:) !< FF wind-inflow data + + ! Local Variables: + REAL(ReKi) :: MeanVal ! computed mean wind speed + INTEGER(IntKi) :: ic ! loop counter + INTEGER(IntKi) :: iy ! loop counter + INTEGER(IntKi) :: iz ! loop counter + INTEGER(IntKi) :: nt ! number of points in the x (time) direction + + + nt = size(FFData,4) + + DO ic = 1,1 !size(FFData,3) + DO iy = 1,size(FFData,2) + DO iz = 1,size(FFData,1) + meanVal = sum(FFData(iz,iy,ic,:)) / nt + + FFData( iz,iy,ic,: ) = FFData( iz,iy,ic,: ) - meanVal + END DO ! iz + END DO ! iy + END DO ! ic + + +END SUBROUTINE SubtractMeanVelocity +!==================================================================================================== +!> This routine is used to make sure the initInp data is valid. +SUBROUTINE FFWind_ValidateInput(InitInp, nffc, ErrStat, ErrMsg) + + ! Passed Variables + TYPE(IfW_FFWind_InitInputType), INTENT(IN ) :: InitInp !< Initialization input data passed to the module + INTEGER(IntKi), INTENT(IN ) :: nffc !< number of full-field wind components (normally 3) + + ! Error Handling + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< determines if an error has been encountered + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about errors + character(*), parameter :: RoutineName = 'FFWind_ValidateInput' + + integer(intki) :: ic ! loop counter + + ErrStat = ErrID_None + ErrMsg = "" + + IF ( InitInp%RefHt < 0.0_ReKi .or. EqualRealNos( InitInp%RefHt, 0.0_ReKi ) ) call SetErrStat( ErrID_Fatal, 'The grid reference height must be larger than 0.', ErrStat, ErrMsg, RoutineName ) + + if ( InitInp%ScaleMethod == ScaleMethod_Direct) then + do ic=1,nffc + if ( InitInp%sf(ic) < 0.0_ReKi ) CALL SetErrStat( ErrID_Fatal, 'Turbulence scaling factors must not be negative.', ErrStat, ErrMsg, RoutineName ) + end do + elseif ( InitInp%ScaleMethod == ScaleMethod_StdDev ) then + do ic=1,nffc + if ( InitInp%sigmaf(ic) < 0.0_ReKi ) CALL SetErrStat( ErrID_Fatal, 'Turbulence standard deviations must not be negative.', ErrStat, ErrMsg, RoutineName ) + end do +#ifdef UNUSED_INPUTFILE_LINES + if ( InitInp%TStart < 0.0_ReKi ) CALL SetErrStat( ErrID_Fatal, 'TStart for turbulence standard deviation calculations must not be negative.', ErrStat, ErrMsg, RoutineName ) + if ( InitInp%TEnd <= InitInp%TStart ) CALL SetErrStat( ErrID_Fatal, 'TEnd for turbulence standard deviation calculations must be after TStart.', ErrStat, ErrMsg, RoutineName ) +#endif + elseif ( InitInp%ScaleMethod /= ScaleMethod_None ) then + CALL SetErrStat( ErrID_Fatal, 'Turbulence scaling method must be 0 (none), 1 (direct scaling factors), or 2 (target standard deviation).', ErrStat, ErrMsg, RoutineName ) + end if + + + if (InitInp%WindProfileType == WindProfileType_Log) then + if ( InitInp%z0 < 0.0_ReKi .or. EqualRealNos( InitInp%z0, 0.0_ReKi ) ) & + call SetErrStat( ErrID_Fatal, 'The surface roughness length, Z0, must be greater than zero', ErrStat, ErrMsg, RoutineName ) + elseif ( InitInp%WindProfileType < WindProfileType_None .or. InitInp%WindProfileType > WindProfileType_PL) then + call SetErrStat( ErrID_Fatal, 'The WindProfile type must be 0 (constant), 1 (logarithmic) or 2 (power law).', ErrStat, ErrMsg, RoutineName ) + end if + + IF ( InitInp%URef < 0.0_ReKi ) call SetErrStat( ErrID_Fatal, 'The reference wind speed must not be negative.', ErrStat, ErrMsg, RoutineName ) + + +END SUBROUTINE FFWind_ValidateInput +!==================================================================================================== +SUBROUTINE ConvertFFWind_to_HAWC2(FileRootName, p, ErrStat, ErrMsg) + CHARACTER(*), INTENT(IN ) :: FileRootName !< RootName for output files + TYPE(IfW_FFWind_ParameterType), INTENT(IN ) :: p !< Parameters + + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + + ! Local variables + REAL(SiKi) :: delta(3) + + delta(1) = p%MeanFFWS * p%FFDTime + delta(2) = 1.0_SiKi / p%InvFFYD + delta(3) = 1.0_SiKi / p%InvFFZD + + CALL WrBinHAWC(FileRootName, p%FFData(:,:,:,1:p%NFFSteps), delta, ErrStat, ErrMsg) + + IF (.NOT. p%Periodic) THEN + call SetErrStat( ErrID_Severe, 'File converted to HAWC format is not periodic. Jumps may occur in resulting simulation.', & + ErrStat, ErrMsg, 'ConvertFFWind_to_HAWC2') + END IF + +END SUBROUTINE ConvertFFWind_to_HAWC2 +!==================================================================================================== +SUBROUTINE ConvertFFWind_to_Bladed(FileRootName, p, ErrStat, ErrMsg) + CHARACTER(*), INTENT(IN ) :: FileRootName !< RootName for output files + TYPE(IfW_FFWind_ParameterType), INTENT(IN ) :: p !< Parameters + + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + + ! Local variables + REAL(SiKi) :: delta(3) + + delta(1) = p%MeanFFWS * p%FFDTime + delta(2) = 1.0_SiKi / p%InvFFYD + delta(3) = 1.0_SiKi / p%InvFFZD + + CALL WrBinBladed(FileRootName, p%FFData(:,:,:,1:p%NFFSteps), delta, p%MeanFFWS, p%RefHt, p%GridBase, p%Periodic, p%AddMeanAfterInterp, ErrStat, ErrMsg) + +END SUBROUTINE ConvertFFWind_to_Bladed +!================================================================================================================================== + SUBROUTINE WrBinHAWC(FileRootName, FFWind, delta, ErrStat, ErrMsg) + CHARACTER(*), INTENT(IN) :: FileRootName !< Name of the file to write the output in + REAL(SiKi), INTENT(IN) :: FFWind(:,:,:,:) !< 4D wind speeds: index 1=z (height), 2=y (lateral), 3=dimension(u,v,w), 4=time or x + REAL(SiKi), INTENT(IN) :: delta(3) !< array containing dx, dy, dz in meters + INTEGER(IntKi), INTENT(OUT):: ErrStat !< Indicates whether an error occurred (see NWTC_Library) + CHARACTER(*), INTENT(OUT):: ErrMsg !< Error message associated with the ErrStat + + ! local variables + CHARACTER(*), PARAMETER :: Comp(3) = (/'u','v','w'/) + INTEGER(IntKi), PARAMETER :: AryDim(3) = (/4, 2, 1/) ! x,y,z dimensions of FFWind array + INTEGER(IntKi) :: nc + INTEGER(IntKi) :: IC, IX, IY, IZ + INTEGER(IntKi) :: UnWind + !REAL(SiKi) :: MeanVal(size(FFWind,1),size(FFWind,2)) + REAL(SiKi) :: MeanVal(size(FFWind,1)) + + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'WrBinHAWC' + CHARACTER(1024) :: RootWithoutPathName + + ErrStat = ErrID_None + ErrMsg = "" + + CALL GetNewUnit( UnWind, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + nc = size(FFWind,3) ! check that nc == 3 ???? + + ! need to remove time-average value from DIM=1 + MeanVal = 0.0_SiKi + DO IX = 1,size(FFWind,4) + MeanVal = MeanVal + FFWind(:,1,1,ix) + END DO + MeanVal = MeanVal / size(FFWind,4) + + + ! write the summary file + CALL OpenFOutFile ( UnWind, trim(FileRootName)//'-HAWC.sum', ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + WRITE( UnWind, '(A)' ) '; Wind file converted to HAWC format on '//CurDate()//' at '//CurTime() + + WRITE( UnWind, '()' ) + DO IZ = size(FFWind,AryDim(3)),1,-1 + WRITE( UnWind, '(A,I3,A,F15.5)' ) '; mean removed at z(', iz, ') = ', MeanVal(iz) + END DO + + WRITE( UnWind, '(A)' ) 'turb_format 1 ;' +! WRITE( UnWind, '(A)' ) 'center_pos0 0.0 0.0 '//trim(num2lstr( ';' + + WRITE( UnWind, '()' ) + WRITE( UnWind, '(A)' ) 'begin mann;' + + ic = INDEX( FileRootName, '\', BACK=.TRUE. ) + ic = MAX( ic, INDEX( FileRootName, '/', BACK=.TRUE. ) ) + RootWithoutPathName = FileRootName((ic+1):) + + + DO IC = 1,nc + WRITE( UnWind, '(2x,A, T30, A, " ;")' ) 'filename_'//Comp(IC), trim(RootWithoutPathName)//'-HAWC-'//Comp(IC)//'.bin' + END DO + DO IC = 1,nc + WRITE( UnWind, '(2x,A, T30, I8, 1x, F15.5, " ;")' ) 'box_dim_'//Comp(IC), size( FFWind, AryDim(ic) ), delta(ic) + END DO + WRITE( UnWind, '(2x,A)' ) 'dont_scale 1; converter did not rescale turbulence to unit standard deviation' + WRITE( UnWind, '(A)' ) 'end mann;' + CLOSE ( UnWind ) + + + ! write the binary files for each component + + DO IC = 1,nc + + CALL OpenBOutFile ( UnWind, trim(FileRootName)//'-HAWC-'//Comp(ic)//'.bin', ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + DO IX = 1,size(FFWind,AryDim(1)) + DO IY = size(FFWind,AryDim(2)),1,-1 + WRITE( UnWind, IOSTAT=ErrStat2 ) FFWind(:,iy,ic,ix) - MeanVal(:) ! note that FFWind is SiKi (4-byte reals, not default kinds) + END DO + END DO + + CLOSE ( UnWind ) + + MeanVal = 0.0_SiKi + + END DO + + END SUBROUTINE WrBinHAWC +!================================================================================================================================== + SUBROUTINE WrBinBladed(FileRootName, FFWind, delta, MeanFFWS, HubHt, GridBase, Periodic, AddMeanAfterInterp, ErrStat, ErrMsg) + CHARACTER(*), INTENT(IN) :: FileRootName !< Name of the file to write the output in + REAL(SiKi), INTENT(IN) :: FFWind(:,:,:,:) !< 4D wind speeds: index 1=z (height), 2=y (lateral), 3=dimension(u,v,w), 4=time or x + REAL(SiKi), INTENT(IN) :: delta(3) !< array containing dx, dy, dz in meters + REAL(ReKi), INTENT(IN) :: MeanFFWS !< advection speed (mean wind speed at hub) + REAL(ReKi), INTENT(IN) :: HubHt !< hub height + REAL(ReKi), INTENT(IN) :: GridBase !< height of lowest grid point + LOGICAL, INTENT(IN) :: Periodic !< whether this wind file is periodic + LOGICAL, INTENT(IN) :: AddMeanAfterInterp !< whether this wind file contains a mean longditudinal wind speed + INTEGER(IntKi), INTENT(OUT):: ErrStat !< Indicates whether an error occurred (see NWTC_Library) + CHARACTER(*), INTENT(OUT):: ErrMsg !< Error message associated with the ErrStat + + ! local variables + INTEGER(IntKi), PARAMETER :: AryDim(3) = (/4, 2, 1/) ! x,y,z dimensions of FFWind array + INTEGER(IntKi) :: ic, it, iy, iz + INTEGER(IntKi) :: UnWind + REAL(SiKi) :: MeanVal(size(FFWind,1),size(FFWind,2)) + REAL(SiKi) :: SigmaGrid( size(FFWind,1),size(FFWind,2)) + REAL(SiKi) :: TI(3) !< array containing turbulence intensity (for scaling factors) + REAL(SiKi) :: Sigma(3) !< array containing standard deviations (for scaling factors) + REAL(SiKi) :: Scl(3) !< array containing scaling factors + REAL(SiKi) :: Off(3) !< array containing offsets + REAL(SiKi) :: Tmp + REAL(ReKi) :: MeanFFWS_nonZero !< advection speed (mean wind speed at hub) + + + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'WrBinBladed' + + REAL(SiKi), PARAMETER :: Tolerance = 0.0001 ! The largest difference between two numbers that are assumed to be equal + + + ErrStat = ErrID_None + ErrMsg = "" + + !----------------------------------------------------- + ! Calculate the stats + !----------------------------------------------------- + + do ic=3,1,-1 + + ! mean values: + MeanVal = 0.0_SiKi + DO it = 1,size(FFWind,AryDim(1)) + MeanVal = MeanVal + FFWind(:,:,ic,it) + END DO + MeanVal = MeanVal / real( size(FFWind,AryDim(1)), SiKi) + + ! standard deviations (with 1/N scaling factor): + SigmaGrid = 0.0_SiKi + DO it = 1,size(FFWind,4) + SigmaGrid = SigmaGrid + FFWind(:,:,ic,it)**2 + END DO + SigmaGrid = SigmaGrid / size(FFWind,AryDim(1)) + SigmaGrid = SQRT( MAX( SigmaGrid - MeanVal**2, 0.0_SiKi ) ) + + ! now get the average standard deviation for each component: + Sigma(ic) = sum(SigmaGrid)/size(SigmaGrid) ! get the average sigma over the grid + Sigma(ic) = MAX(100.0_SiKi*Tolerance, Sigma(ic)) ! make sure this scaling isn't too small + + end do + + ! We need to take into account the shear across the grid in the sigma calculations for scaling the data, + ! and ensure that 32.767*sigma_u >= |V-UHub| so that we don't get values out of the range of our scaling values + ! in this BLADED-style binary output. Tmp is |V-UHub| + Tmp = MAX( ABS(MAXVAL(FFWind(:,:,1,:))-MeanFFWS), ABS(MINVAL(FFWind(:,:,1,:))-MeanFFWS) ) !Get the range of wind speed values for scaling in BLADED-format .wnd files + Sigma(1) = MAX(Sigma(1),0.05_SiKi*Tmp) + do ic=2,3 + Sigma(ic) = MAX( Sigma(ic), 0.05_SiKi*ABS(MAXVAL(FFWind(:,:,ic,:))), 0.05_SiKi*ABS(MINVAL(FFWind(:,:,ic,:))) ) ! put the abs() after the maxval() and minval() to avoid stack-overflow issues with large wind files + end do + + ! Put normalizing factors into the summary file. The user can use them to + ! tell a simulation program how to rescale the data. + + if ( abs(MeanFFWS) < 0.1_ReKi ) then + MeanFFWS_nonZero = sign( 0.1, MeanFFWS ) + else + MeanFFWS_nonZero = MeanFFWS + end if + + TI = Sigma / MeanFFWS_nonZero + + !----------------------------------------------------- + ! The summary file + !----------------------------------------------------- + CALL GetNewUnit( UnWind, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + CALL OpenFOutFile ( UnWind, trim(FileRootName)//'-Bladed.sum', ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + !The string "TurbSim" needs to be in the 2nd line of the summary file if AeroDyn will read this. + WRITE( UnWind,"( / 'TurbSim - This summary file was generated by ', A, ' on ' , A , ' at ' , A , '.' / )") "NWTC_Library", CurDate(), CurTime() + WRITE( UnWind, '(/)' ) + WRITE( UnWind, '(/)' ) + WRITE( UnWind, '( L10, 2X, "Clockwise rotation when looking downwind?")' ) .FALSE. + WRITE( UnWind, '( F10.3, 2X, "Hub height [m]")' ) HubHt + WRITE( UnWind, '( F10.3, 2X, "Grid height [m]")' ) delta(3)*(size(FFWind,AryDim(3)) - 1) + WRITE( UnWind, '( F10.3, 2X, "Grid width [m]")' ) delta(2)*(size(FFWind,AryDim(2)) - 1) + WRITE( UnWind, '(/"BLADED-style binary scaling parameters:"/)' ) + WRITE( UnWind, '( 2X, "UBar = ", F9.4, " m/s")' ) MeanFFWS_nonZero + WRITE( UnWind, '( 2X, "TI(u) = ", F9.4, " %")' ) 100.0*TI(1) + WRITE( UnWind, '( 2X, "TI(v) = ", F9.4, " %")' ) 100.0*TI(2) + WRITE( UnWind, '( 2X, "TI(w) = ", F9.4, " %")' ) 100.0*TI(3) + WRITE( UnWind, '(/)' ) + WRITE( UnWind, '( 2X, "Height offset = ", F9.4, " m" )' ) HubHt - 0.5*delta(3)*(size(FFWind,AryDim(3)) - 1) - GridBase ! This will be zero for square grids + ! ZGOffset = ( HubHt - delta(3)*(size(FFWind,1) - 1) / 2.0 - Zbottom ) + WRITE( UnWind, '( 2X, "Grid Base = ", F9.4, " m" )' ) GridBase + if (Periodic) then + WRITE (UnWind,'()' ) + WRITE (UnWind,'( A)' ) 'Creating a PERIODIC output file.' + end if + WRITE (UnWind,'( A)' ) 'Creating a BLADED LEFT-HAND RULE output file.' + + + CLOSE (UnWind) + + !----------------------------------------------------- + ! The BINARY file + !----------------------------------------------------- + CALL OpenBOutFile ( UnWind, TRIM(FileRootName)//'-Bladed.wnd', ErrStat, ErrMsg ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + WRITE (UnWind) INT( -99 , B2Ki ) ! -99 = New Bladed format + WRITE (UnWind) INT( 4 , B2Ki ) ! 4 = improved von karman (not used, but needed for next 7 inputs) + WRITE (UnWind) INT( size(FFWind,3) , B4Ki ) ! size(FFWind,3) = 3 = number of wind components + WRITE (UnWind) REAL( 45.0_SiKi , SiKi ) ! Latitude (degrees) (informational, not used in FAST) + WRITE (UnWind) REAL( 0.03_SiKi , SiKi ) ! Roughness length (m) (informational, not used in FAST) + WRITE (UnWind) REAL( HubHt , SiKi ) ! Reference Height (m) (informational, not used in FAST) + WRITE (UnWind) REAL( 100.0*TI(1) , SiKi ) ! Longitudinal turbulence intensity (%) + WRITE (UnWind) REAL( 100.0*TI(2) , SiKi ) ! Lateral turbulence intensity (%) + WRITE (UnWind) REAL( 100.0*TI(3) , SiKi ) ! Vertical turbulence intensity (%) + + WRITE (UnWind) REAL( delta(3) , SiKi ) ! grid spacing in vertical direction, in m + WRITE (UnWind) REAL( delta(2) , SiKi ) ! grid spacing in lateral direction, in m + WRITE (UnWind) REAL( delta(1) , SiKi ) ! grid spacing in longitudinal direciton, in m + WRITE (UnWind) INT( size(FFWind,AryDim(1))/2 , B4Ki ) ! half the number of points in alongwind direction + WRITE (UnWind) REAL( MeanFFWS_nonZero , SiKi ) ! the mean wind speed in m/s + WRITE (UnWind) REAL( 0 , SiKi ) ! the vertical length scale of the longitudinal component in m + WRITE (UnWind) REAL( 0 , SiKi ) ! the lateral length scale of the longitudinal component in m + WRITE (UnWind) REAL( 0 , SiKi ) ! the longitudinal length scale of the longitudinal component in m + WRITE (UnWind) INT( 0 , B4Ki ) ! an unused integer + WRITE (UnWind) INT( 0 , B4Ki ) ! the random number seed + WRITE (UnWind) INT( size(FFWind,AryDim(3)) , B4Ki ) ! the number of grid points vertically + WRITE (UnWind) INT( size(FFWind,AryDim(2)) , B4Ki ) ! the number of grid points laterally + WRITE (UnWind) INT( 0 , B4Ki ) ! the vertical length scale of the lateral component, not used + WRITE (UnWind) INT( 0 , B4Ki ) ! the lateral length scale of the lateral component, not used + WRITE (UnWind) INT( 0 , B4Ki ) ! the longitudinal length scale of the lateral component, not used + WRITE (UnWind) INT( 0 , B4Ki ) ! the vertical length scale of the vertical component, not used + WRITE (UnWind) INT( 0 , B4Ki ) ! the lateral length scale of the vertical component, not used + WRITE (UnWind) INT( 0 , B4Ki ) ! the longitudinal length scale of the vertical component, not used + + ! Scaling value to convert wind speeds to 16-bit integers + do ic = 1,3 + if (.not. EqualRealNos( Sigma(ic), 0.0_SiKi ) ) then + Scl(ic) = 1000.0/( Sigma(ic) ) + else + Scl(ic) = 1.0_SiKi + end if + end do + Scl(2) = -Scl(2) ! Bladed convention is positive V is pointed along negative Y (IEC turbine coordinate) + + ! Offset value to convert wind speeds to 16-bit integers + IF (AddMeanAfterInterp) THEN ! Note that this will not take into account any shear!!! + Off(1) = 0.0 + ELSE + Off(1) = MeanFFWS * Scl(1) + END IF + Off(2) = 0.0 + Off(3) = 0.0 + + DO it=1,size(FFWind,AryDim(1)) + DO iz=1,size(FFWind,AryDim(3)) ! 1=bottom of grid + DO iy=1,size(FFWind,AryDim(2)) ! 1=left of grid, i.e. y(1) = -GridWidth/2 + + ! Scale velocity for 16-bit integers: + WRITE ( UnWind ) NINT( FFWind(iz,iy,:,it) * Scl - Off , B2Ki ) ! scale to int16 + + ENDDO !IY + ENDDO !IZ + ENDDO !IT + + CLOSE( UnWind ) + + + END SUBROUTINE WrBinBladed +!==================================================================================================== +SUBROUTINE ConvertFFWind_toVTK(FileRootName, p, ErrStat, ErrMsg) + CHARACTER(*), INTENT(IN ) :: FileRootName !< RootName for output files + TYPE(IfW_FFWind_ParameterType), INTENT(IN ) :: p !< Parameters + + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + + ! Local variables + CHARACTER(1024) :: RootPathName + CHARACTER(1024) :: FileName + INTEGER :: UnWind + INTEGER :: i + INTEGER :: iy + INTEGER :: iz + + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'ConvertFFWind_toVTK' + + + CALL GetPath ( FileRootName, RootPathName ) + CALL GetNewUnit( UnWind, ErrStat, ErrMsg ) + + do i = 1,p%NFFSteps + + ! Create the output vtk file with naming /vtk/DisYZ.t.vtk + + RootPathName = trim(RootPathName)//PathSep//"vtk" + call MkDir( trim(RootPathName) ) ! make this directory if it doesn't already exist + + !FileName = trim(RootPathName)//PathSep//"vtk"//PathSep//"DisYZ.t"//trim(num2lstr(i))//".vtp" + FileName = trim(RootPathName)//PathSep//"DisYZ.t"//trim(num2lstr(i))//".vtp" + + ! see WrVTK_SP_header + CALL OpenFOutFile ( UnWind, TRIM(FileName), ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return + + WRITE(UnWind,'(A)') '# vtk DataFile Version 3.0' + WRITE(UnWind,'(A)') "InflowWind YZ Slice at T= "//trim(num2lstr((i-1)*p%FFDTime))//" s" + WRITE(UnWind,'(A)') 'ASCII' + WRITE(UnWind,'(A)') 'DATASET STRUCTURED_POINTS' + + ! Note: gridVals must be stored such that the left-most dimension is X and the right-most dimension is Z + ! see WrVTK_SP_vectors3D() + WRITE(UnWind,'(A,3(i5,1X))') 'DIMENSIONS ', 1, p%NYGrids, p%NZGrids + WRITE(UnWind,'(A,3(f10.2,1X))') 'ORIGIN ' , p%InitXPosition, -p%FFYHWid, p%GridBase + WRITE(UnWind,'(A,3(f10.2,1X))') 'SPACING ' , 0.0_ReKi, 1.0_SiKi / p%InvFFYD, 1.0_SiKi / p%InvFFZD + WRITE(UnWind,'(A,i5)') 'POINT_DATA ', p%NYGrids*p%NZGrids + WRITE(UnWind,'(A)') 'VECTORS DisYZ float' + + DO iz=1,p%NZGrids + DO iy=1,p%NYGrids + WRITE(UnWind,'(3(f10.2,1X))') p%FFData(iz,iy,:,i) + END DO + END DO + + CLOSE(UnWind) + + end do + + +END SUBROUTINE ConvertFFWind_toVTK + + +!==================================================================================================== + +END MODULE IfW_FFWind_Base diff --git a/modules/inflowwind/src/IfW_FFWind_Base.txt b/modules/inflowwind/src/IfW_FFWind_Base.txt new file mode 100644 index 0000000000..a1f78d1232 --- /dev/null +++ b/modules/inflowwind/src/IfW_FFWind_Base.txt @@ -0,0 +1,53 @@ +################################################################################################################################### +# Registry for IfW_BladedFFWind, creates MODULE IfW_BladedFFWind_Types +# Module IfW_BladedFFWind_Types contains all of the user-defined types needed in IfW_BladedFFWind. It also contains copy, destroy, pack, and +# unpack routines associated with each defined data types. +################################################################################################################################### +# Entries are of the form +# keyword +################################################################################################################################### + + +typedef IfW_FFWind_Base/IfW_FFWind InitInputType IntKi ScaleMethod - 0 - "Turbulence scaling method [0=none, 1=direct scaling, 2= calculate scaling factor based on a desired standard deviation]" - +typedef ^ ^ ReKi SF 3 0 - "Turbulence scaling factor for each direction [ScaleMethod=1]" - +typedef ^ ^ ReKi SigmaF 3 0 - "Turbulence standard deviation to calculate scaling from in each direction [ScaleMethod=2]" - +#typedef ^ ^ ReKi TStart - 0 - "" - +#typedef ^ ^ ReKi TEnd - 0 - "" - +typedef ^ ^ IntKi WindProfileType - -1 - "Wind profile type (0=constant;1=logarithmic;2=power law)" - +typedef ^ ^ ReKi RefHt - 0 - "Reference (hub) height of the grid" meters +typedef ^ ^ ReKi URef - 0 - "Mean u-component wind speed at the reference height" meters +typedef ^ ^ ReKi PLExp - 0 - "Power law exponent (used for PL wind profile type only)" - +typedef ^ ^ ReKi Z0 - 0 - "Surface roughness length (used for LOG wind profile type only)" - +typedef ^ ^ ReKi XOffset - 0 - "distance offset for FF wind files" m + + +# ..... Parameters ................................................................................................................ +# Define parameters here: +# Time step for integration of continuous states (if a fixed-step integrator is used) and update of discrete states: +typedef ^ ParameterType Logical Periodic - .FALSE. - "Flag to indicate if the wind file is periodic" - +typedef ^ ParameterType Logical InterpTower - .FALSE. - "Flag to indicate if we should interpolate wind speeds below the tower" - +typedef ^ ^ SiKi FFData :::: - - "Array of FF data" - +typedef ^ ^ SiKi FFTower ::: - - "Array of data along tower, below FF array" - +typedef ^ ^ ReKi FFDTime - 0 - "Delta time" seconds +typedef ^ ^ ReKi FFRate - 0 - "Data rate (1/FFDTime)" Hertz +typedef ^ ^ ReKi FFYHWid - 0 - "Half the grid width" meters +typedef ^ ^ ReKi FFZHWid - 0 - "Half the grid height" meters +typedef ^ ^ ReKi RefHt - 0 - "Reference (hub) height of the grid" meters +typedef ^ ^ ReKi GridBase - 0 - "the height of the bottom of the grid" meters +typedef ^ ^ ReKi InitXPosition - 0 - "the initial x position of grid (distance in FF is offset)" meters +typedef ^ ^ ReKi InvFFYD - 0 - "reciprocal of delta y" 1/meters +typedef ^ ^ ReKi InvFFZD - 0 - "reciprocal of delta z" 1/meters +typedef ^ ^ ReKi InvMFFWS - 0 - "reciprocal of mean wind speed (MeanFFWS)" seconds/meter +typedef ^ ^ ReKi MeanFFWS - 0 - "Mean wind speed (as defined in FF file), not necessarily of the portion used" meters/second +typedef ^ ^ ReKi TotalTime - 0 - "The total time of the simulation" seconds +typedef ^ ^ IntKi NFFComp - 3 - "Number of wind components" - +typedef ^ ^ IntKi NFFSteps - 0 - "Number of time steps in the FF array" - +typedef ^ ^ IntKi NYGrids - 0 - "Number of points in the lateral (y) direction of the grids" - +typedef ^ ^ IntKi NZGrids - 0 - "Number of points in the vertical (z) direction of the grids" - +typedef ^ ^ IntKi NTGrids - 0 - "Number of points in the vertical (z) direction on the tower (below the grids)" - +typedef ^ ^ IntKi WindFileFormat - - - "Binary file format description number" - +typedef ^ ^ Logical AddMeanAfterInterp - .FALSE. - "Add the mean wind speed after interpolating at a given height?" - +typedef ^ ^ IntKi WindProfileType - -1 - "Wind profile type (0=constant;1=logarithmic;2=power law)" - +typedef ^ ^ ReKi PLExp - 0 - "Power law exponent (used for PL wind profile type only)" - +typedef ^ ^ ReKi Z0 - 0 - "Surface roughness length (used for LOG wind profile type only)" - + diff --git a/modules/inflowwind/src/IfW_FFWind_Base_Types.f90 b/modules/inflowwind/src/IfW_FFWind_Base_Types.f90 new file mode 100644 index 0000000000..ffc3ddafd7 --- /dev/null +++ b/modules/inflowwind/src/IfW_FFWind_Base_Types.f90 @@ -0,0 +1,711 @@ +!STARTOFREGISTRYGENERATEDFILE 'IfW_FFWind_Base_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 +!********************************************************************************************************************************* +! IfW_FFWind_Base_Types +!................................................................................................................................. +! This file is part of IfW_FFWind_Base. +! +! 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 IfW_FFWind_Base. 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 IfW_FFWind_Base_Types +!--------------------------------------------------------------------------------------------------------------------------------- +USE NWTC_Library +IMPLICIT NONE +! ========= IfW_FFWind_InitInputType ======= + TYPE, PUBLIC :: IfW_FFWind_InitInputType + INTEGER(IntKi) :: ScaleMethod = 0 !< Turbulence scaling method [0=none, 1=direct scaling, 2= calculate scaling factor based on a desired standard deviation] [-] + REAL(ReKi) , DIMENSION(1:3) :: SF !< Turbulence scaling factor for each direction [ScaleMethod=1] [-] + REAL(ReKi) , DIMENSION(1:3) :: SigmaF !< Turbulence standard deviation to calculate scaling from in each direction [ScaleMethod=2] [-] + INTEGER(IntKi) :: WindProfileType = -1 !< Wind profile type (0=constant;1=logarithmic;2=power law) [-] + REAL(ReKi) :: RefHt = 0 !< Reference (hub) height of the grid [meters] + REAL(ReKi) :: URef = 0 !< Mean u-component wind speed at the reference height [meters] + REAL(ReKi) :: PLExp = 0 !< Power law exponent (used for PL wind profile type only) [-] + REAL(ReKi) :: Z0 = 0 !< Surface roughness length (used for LOG wind profile type only) [-] + REAL(ReKi) :: XOffset = 0 !< distance offset for FF wind files [m] + END TYPE IfW_FFWind_InitInputType +! ======================= +! ========= IfW_FFWind_ParameterType ======= + TYPE, PUBLIC :: IfW_FFWind_ParameterType + LOGICAL :: Periodic = .FALSE. !< Flag to indicate if the wind file is periodic [-] + LOGICAL :: InterpTower = .FALSE. !< Flag to indicate if we should interpolate wind speeds below the tower [-] + REAL(SiKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: FFData !< Array of FF data [-] + REAL(SiKi) , DIMENSION(:,:,:), ALLOCATABLE :: FFTower !< Array of data along tower, below FF array [-] + REAL(ReKi) :: FFDTime = 0 !< Delta time [seconds] + REAL(ReKi) :: FFRate = 0 !< Data rate (1/FFDTime) [Hertz] + REAL(ReKi) :: FFYHWid = 0 !< Half the grid width [meters] + REAL(ReKi) :: FFZHWid = 0 !< Half the grid height [meters] + REAL(ReKi) :: RefHt = 0 !< Reference (hub) height of the grid [meters] + REAL(ReKi) :: GridBase = 0 !< the height of the bottom of the grid [meters] + REAL(ReKi) :: InitXPosition = 0 !< the initial x position of grid (distance in FF is offset) [meters] + REAL(ReKi) :: InvFFYD = 0 !< reciprocal of delta y [1/meters] + REAL(ReKi) :: InvFFZD = 0 !< reciprocal of delta z [1/meters] + REAL(ReKi) :: InvMFFWS = 0 !< reciprocal of mean wind speed (MeanFFWS) [seconds/meter] + REAL(ReKi) :: MeanFFWS = 0 !< Mean wind speed (as defined in FF file), not necessarily of the portion used [meters/second] + REAL(ReKi) :: TotalTime = 0 !< The total time of the simulation [seconds] + INTEGER(IntKi) :: NFFComp = 3 !< Number of wind components [-] + INTEGER(IntKi) :: NFFSteps = 0 !< Number of time steps in the FF array [-] + INTEGER(IntKi) :: NYGrids = 0 !< Number of points in the lateral (y) direction of the grids [-] + INTEGER(IntKi) :: NZGrids = 0 !< Number of points in the vertical (z) direction of the grids [-] + INTEGER(IntKi) :: NTGrids = 0 !< Number of points in the vertical (z) direction on the tower (below the grids) [-] + INTEGER(IntKi) :: WindFileFormat !< Binary file format description number [-] + LOGICAL :: AddMeanAfterInterp = .FALSE. !< Add the mean wind speed after interpolating at a given height? [-] + INTEGER(IntKi) :: WindProfileType = -1 !< Wind profile type (0=constant;1=logarithmic;2=power law) [-] + REAL(ReKi) :: PLExp = 0 !< Power law exponent (used for PL wind profile type only) [-] + REAL(ReKi) :: Z0 = 0 !< Surface roughness length (used for LOG wind profile type only) [-] + END TYPE IfW_FFWind_ParameterType +! ======================= +CONTAINS + SUBROUTINE IfW_FFWind_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrStat, ErrMsg ) + TYPE(IfW_FFWind_InitInputType), INTENT(IN) :: SrcInitInputData + TYPE(IfW_FFWind_InitInputType), INTENT(INOUT) :: DstInitInputData + INTEGER(IntKi), INTENT(IN ) :: CtrlCode + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg +! Local + INTEGER(IntKi) :: i,j,k + INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 + INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 + INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 + INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'IfW_FFWind_CopyInitInput' +! + ErrStat = ErrID_None + ErrMsg = "" + DstInitInputData%ScaleMethod = SrcInitInputData%ScaleMethod + DstInitInputData%SF = SrcInitInputData%SF + DstInitInputData%SigmaF = SrcInitInputData%SigmaF + DstInitInputData%WindProfileType = SrcInitInputData%WindProfileType + DstInitInputData%RefHt = SrcInitInputData%RefHt + DstInitInputData%URef = SrcInitInputData%URef + DstInitInputData%PLExp = SrcInitInputData%PLExp + DstInitInputData%Z0 = SrcInitInputData%Z0 + DstInitInputData%XOffset = SrcInitInputData%XOffset + END SUBROUTINE IfW_FFWind_CopyInitInput + + SUBROUTINE IfW_FFWind_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) + TYPE(IfW_FFWind_InitInputType), INTENT(INOUT) :: InitInputData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'IfW_FFWind_DestroyInitInput' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE IfW_FFWind_DestroyInitInput + + SUBROUTINE IfW_FFWind_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) + REAL(ReKi), ALLOCATABLE, INTENT( OUT) :: ReKiBuf(:) + REAL(DbKi), ALLOCATABLE, INTENT( OUT) :: DbKiBuf(:) + INTEGER(IntKi), ALLOCATABLE, INTENT( OUT) :: IntKiBuf(:) + TYPE(IfW_FFWind_InitInputType), INTENT(IN) :: InData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + LOGICAL,OPTIONAL, INTENT(IN ) :: SizeOnly + ! Local variables + INTEGER(IntKi) :: Re_BufSz + INTEGER(IntKi) :: Re_Xferred + INTEGER(IntKi) :: Db_BufSz + INTEGER(IntKi) :: Db_Xferred + INTEGER(IntKi) :: Int_BufSz + INTEGER(IntKi) :: Int_Xferred + INTEGER(IntKi) :: i,i1,i2,i3,i4,i5 + LOGICAL :: OnlySize ! if present and true, do not pack, just allocate buffers + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'IfW_FFWind_PackInitInput' + ! buffers to store subtypes, if any + REAL(ReKi), ALLOCATABLE :: Re_Buf(:) + REAL(DbKi), ALLOCATABLE :: Db_Buf(:) + INTEGER(IntKi), ALLOCATABLE :: Int_Buf(:) + + OnlySize = .FALSE. + IF ( PRESENT(SizeOnly) ) THEN + OnlySize = SizeOnly + ENDIF + ! + ErrStat = ErrID_None + ErrMsg = "" + Re_BufSz = 0 + Db_BufSz = 0 + Int_BufSz = 0 + Int_BufSz = Int_BufSz + 1 ! ScaleMethod + Re_BufSz = Re_BufSz + SIZE(InData%SF) ! SF + Re_BufSz = Re_BufSz + SIZE(InData%SigmaF) ! SigmaF + Int_BufSz = Int_BufSz + 1 ! WindProfileType + Re_BufSz = Re_BufSz + 1 ! RefHt + Re_BufSz = Re_BufSz + 1 ! URef + Re_BufSz = Re_BufSz + 1 ! PLExp + Re_BufSz = Re_BufSz + 1 ! Z0 + Re_BufSz = Re_BufSz + 1 ! XOffset + IF ( Re_BufSz .GT. 0 ) THEN + ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating ReKiBuf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + IF ( Db_BufSz .GT. 0 ) THEN + ALLOCATE( DbKiBuf( Db_BufSz ), STAT=ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DbKiBuf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + IF ( Int_BufSz .GT. 0 ) THEN + ALLOCATE( IntKiBuf( Int_BufSz ), STAT=ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating IntKiBuf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + IF(OnlySize) RETURN ! return early if only trying to allocate buffers (not pack them) + + Re_Xferred = 1 + Db_Xferred = 1 + Int_Xferred = 1 + + IntKiBuf(Int_Xferred) = InData%ScaleMethod + Int_Xferred = Int_Xferred + 1 + DO i1 = LBOUND(InData%SF,1), UBOUND(InData%SF,1) + ReKiBuf(Re_Xferred) = InData%SF(i1) + Re_Xferred = Re_Xferred + 1 + END DO + DO i1 = LBOUND(InData%SigmaF,1), UBOUND(InData%SigmaF,1) + ReKiBuf(Re_Xferred) = InData%SigmaF(i1) + Re_Xferred = Re_Xferred + 1 + END DO + IntKiBuf(Int_Xferred) = InData%WindProfileType + Int_Xferred = Int_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%RefHt + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%URef + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%PLExp + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%Z0 + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%XOffset + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE IfW_FFWind_PackInitInput + + SUBROUTINE IfW_FFWind_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) + REAL(ReKi), ALLOCATABLE, INTENT(IN ) :: ReKiBuf(:) + REAL(DbKi), ALLOCATABLE, INTENT(IN ) :: DbKiBuf(:) + INTEGER(IntKi), ALLOCATABLE, INTENT(IN ) :: IntKiBuf(:) + TYPE(IfW_FFWind_InitInputType), INTENT(INOUT) :: OutData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + ! Local variables + INTEGER(IntKi) :: Buf_size + INTEGER(IntKi) :: Re_Xferred + INTEGER(IntKi) :: Db_Xferred + INTEGER(IntKi) :: Int_Xferred + INTEGER(IntKi) :: i + INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 + INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 + INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 + INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'IfW_FFWind_UnPackInitInput' + ! buffers to store meshes, if any + REAL(ReKi), ALLOCATABLE :: Re_Buf(:) + REAL(DbKi), ALLOCATABLE :: Db_Buf(:) + INTEGER(IntKi), ALLOCATABLE :: Int_Buf(:) + ! + ErrStat = ErrID_None + ErrMsg = "" + Re_Xferred = 1 + Db_Xferred = 1 + Int_Xferred = 1 + OutData%ScaleMethod = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + i1_l = LBOUND(OutData%SF,1) + i1_u = UBOUND(OutData%SF,1) + DO i1 = LBOUND(OutData%SF,1), UBOUND(OutData%SF,1) + OutData%SF(i1) = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END DO + i1_l = LBOUND(OutData%SigmaF,1) + i1_u = UBOUND(OutData%SigmaF,1) + DO i1 = LBOUND(OutData%SigmaF,1), UBOUND(OutData%SigmaF,1) + OutData%SigmaF(i1) = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END DO + OutData%WindProfileType = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + OutData%RefHt = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%URef = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%PLExp = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%Z0 = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%XOffset = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE IfW_FFWind_UnPackInitInput + + SUBROUTINE IfW_FFWind_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg ) + TYPE(IfW_FFWind_ParameterType), INTENT(IN) :: SrcParamData + TYPE(IfW_FFWind_ParameterType), INTENT(INOUT) :: DstParamData + INTEGER(IntKi), INTENT(IN ) :: CtrlCode + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg +! Local + INTEGER(IntKi) :: i,j,k + INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 + INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 + INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 + INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'IfW_FFWind_CopyParam' +! + ErrStat = ErrID_None + ErrMsg = "" + DstParamData%Periodic = SrcParamData%Periodic + DstParamData%InterpTower = SrcParamData%InterpTower +IF (ALLOCATED(SrcParamData%FFData)) THEN + i1_l = LBOUND(SrcParamData%FFData,1) + i1_u = UBOUND(SrcParamData%FFData,1) + i2_l = LBOUND(SrcParamData%FFData,2) + i2_u = UBOUND(SrcParamData%FFData,2) + i3_l = LBOUND(SrcParamData%FFData,3) + i3_u = UBOUND(SrcParamData%FFData,3) + i4_l = LBOUND(SrcParamData%FFData,4) + i4_u = UBOUND(SrcParamData%FFData,4) + IF (.NOT. ALLOCATED(DstParamData%FFData)) THEN + ALLOCATE(DstParamData%FFData(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u,i4_l:i4_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%FFData.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%FFData = SrcParamData%FFData +ENDIF +IF (ALLOCATED(SrcParamData%FFTower)) THEN + i1_l = LBOUND(SrcParamData%FFTower,1) + i1_u = UBOUND(SrcParamData%FFTower,1) + i2_l = LBOUND(SrcParamData%FFTower,2) + i2_u = UBOUND(SrcParamData%FFTower,2) + i3_l = LBOUND(SrcParamData%FFTower,3) + i3_u = UBOUND(SrcParamData%FFTower,3) + IF (.NOT. ALLOCATED(DstParamData%FFTower)) THEN + ALLOCATE(DstParamData%FFTower(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%FFTower.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%FFTower = SrcParamData%FFTower +ENDIF + DstParamData%FFDTime = SrcParamData%FFDTime + DstParamData%FFRate = SrcParamData%FFRate + DstParamData%FFYHWid = SrcParamData%FFYHWid + DstParamData%FFZHWid = SrcParamData%FFZHWid + DstParamData%RefHt = SrcParamData%RefHt + DstParamData%GridBase = SrcParamData%GridBase + DstParamData%InitXPosition = SrcParamData%InitXPosition + DstParamData%InvFFYD = SrcParamData%InvFFYD + DstParamData%InvFFZD = SrcParamData%InvFFZD + DstParamData%InvMFFWS = SrcParamData%InvMFFWS + DstParamData%MeanFFWS = SrcParamData%MeanFFWS + DstParamData%TotalTime = SrcParamData%TotalTime + DstParamData%NFFComp = SrcParamData%NFFComp + DstParamData%NFFSteps = SrcParamData%NFFSteps + DstParamData%NYGrids = SrcParamData%NYGrids + DstParamData%NZGrids = SrcParamData%NZGrids + DstParamData%NTGrids = SrcParamData%NTGrids + DstParamData%WindFileFormat = SrcParamData%WindFileFormat + DstParamData%AddMeanAfterInterp = SrcParamData%AddMeanAfterInterp + DstParamData%WindProfileType = SrcParamData%WindProfileType + DstParamData%PLExp = SrcParamData%PLExp + DstParamData%Z0 = SrcParamData%Z0 + END SUBROUTINE IfW_FFWind_CopyParam + + SUBROUTINE IfW_FFWind_DestroyParam( ParamData, ErrStat, ErrMsg ) + TYPE(IfW_FFWind_ParameterType), INTENT(INOUT) :: ParamData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'IfW_FFWind_DestroyParam' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(ParamData%FFData)) THEN + DEALLOCATE(ParamData%FFData) +ENDIF +IF (ALLOCATED(ParamData%FFTower)) THEN + DEALLOCATE(ParamData%FFTower) +ENDIF + END SUBROUTINE IfW_FFWind_DestroyParam + + SUBROUTINE IfW_FFWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) + REAL(ReKi), ALLOCATABLE, INTENT( OUT) :: ReKiBuf(:) + REAL(DbKi), ALLOCATABLE, INTENT( OUT) :: DbKiBuf(:) + INTEGER(IntKi), ALLOCATABLE, INTENT( OUT) :: IntKiBuf(:) + TYPE(IfW_FFWind_ParameterType), INTENT(IN) :: InData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + LOGICAL,OPTIONAL, INTENT(IN ) :: SizeOnly + ! Local variables + INTEGER(IntKi) :: Re_BufSz + INTEGER(IntKi) :: Re_Xferred + INTEGER(IntKi) :: Db_BufSz + INTEGER(IntKi) :: Db_Xferred + INTEGER(IntKi) :: Int_BufSz + INTEGER(IntKi) :: Int_Xferred + INTEGER(IntKi) :: i,i1,i2,i3,i4,i5 + LOGICAL :: OnlySize ! if present and true, do not pack, just allocate buffers + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'IfW_FFWind_PackParam' + ! buffers to store subtypes, if any + REAL(ReKi), ALLOCATABLE :: Re_Buf(:) + REAL(DbKi), ALLOCATABLE :: Db_Buf(:) + INTEGER(IntKi), ALLOCATABLE :: Int_Buf(:) + + OnlySize = .FALSE. + IF ( PRESENT(SizeOnly) ) THEN + OnlySize = SizeOnly + ENDIF + ! + ErrStat = ErrID_None + ErrMsg = "" + Re_BufSz = 0 + Db_BufSz = 0 + Int_BufSz = 0 + Int_BufSz = Int_BufSz + 1 ! Periodic + Int_BufSz = Int_BufSz + 1 ! InterpTower + Int_BufSz = Int_BufSz + 1 ! FFData allocated yes/no + IF ( ALLOCATED(InData%FFData) ) THEN + Int_BufSz = Int_BufSz + 2*4 ! FFData upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%FFData) ! FFData + END IF + Int_BufSz = Int_BufSz + 1 ! FFTower allocated yes/no + IF ( ALLOCATED(InData%FFTower) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! FFTower upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%FFTower) ! FFTower + END IF + Re_BufSz = Re_BufSz + 1 ! FFDTime + Re_BufSz = Re_BufSz + 1 ! FFRate + Re_BufSz = Re_BufSz + 1 ! FFYHWid + Re_BufSz = Re_BufSz + 1 ! FFZHWid + Re_BufSz = Re_BufSz + 1 ! RefHt + Re_BufSz = Re_BufSz + 1 ! GridBase + Re_BufSz = Re_BufSz + 1 ! InitXPosition + Re_BufSz = Re_BufSz + 1 ! InvFFYD + Re_BufSz = Re_BufSz + 1 ! InvFFZD + Re_BufSz = Re_BufSz + 1 ! InvMFFWS + Re_BufSz = Re_BufSz + 1 ! MeanFFWS + Re_BufSz = Re_BufSz + 1 ! TotalTime + Int_BufSz = Int_BufSz + 1 ! NFFComp + Int_BufSz = Int_BufSz + 1 ! NFFSteps + Int_BufSz = Int_BufSz + 1 ! NYGrids + Int_BufSz = Int_BufSz + 1 ! NZGrids + Int_BufSz = Int_BufSz + 1 ! NTGrids + Int_BufSz = Int_BufSz + 1 ! WindFileFormat + Int_BufSz = Int_BufSz + 1 ! AddMeanAfterInterp + Int_BufSz = Int_BufSz + 1 ! WindProfileType + Re_BufSz = Re_BufSz + 1 ! PLExp + Re_BufSz = Re_BufSz + 1 ! Z0 + IF ( Re_BufSz .GT. 0 ) THEN + ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating ReKiBuf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + IF ( Db_BufSz .GT. 0 ) THEN + ALLOCATE( DbKiBuf( Db_BufSz ), STAT=ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DbKiBuf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + IF ( Int_BufSz .GT. 0 ) THEN + ALLOCATE( IntKiBuf( Int_BufSz ), STAT=ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating IntKiBuf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + IF(OnlySize) RETURN ! return early if only trying to allocate buffers (not pack them) + + Re_Xferred = 1 + Db_Xferred = 1 + Int_Xferred = 1 + + IntKiBuf(Int_Xferred) = TRANSFER(InData%Periodic, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%InterpTower, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%FFData) ) THEN + IntKiBuf( Int_Xferred ) = 0 + Int_Xferred = Int_Xferred + 1 + ELSE + IntKiBuf( Int_Xferred ) = 1 + Int_Xferred = Int_Xferred + 1 + IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,4) + Int_Xferred = Int_Xferred + 2 + + DO i4 = LBOUND(InData%FFData,4), UBOUND(InData%FFData,4) + DO i3 = LBOUND(InData%FFData,3), UBOUND(InData%FFData,3) + DO i2 = LBOUND(InData%FFData,2), UBOUND(InData%FFData,2) + DO i1 = LBOUND(InData%FFData,1), UBOUND(InData%FFData,1) + ReKiBuf(Re_Xferred) = InData%FFData(i1,i2,i3,i4) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END DO + END DO + END IF + IF ( .NOT. ALLOCATED(InData%FFTower) ) THEN + IntKiBuf( Int_Xferred ) = 0 + Int_Xferred = Int_Xferred + 1 + ELSE + IntKiBuf( Int_Xferred ) = 1 + Int_Xferred = Int_Xferred + 1 + IntKiBuf( Int_Xferred ) = LBOUND(InData%FFTower,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFTower,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%FFTower,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFTower,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%FFTower,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFTower,3) + Int_Xferred = Int_Xferred + 2 + + DO i3 = LBOUND(InData%FFTower,3), UBOUND(InData%FFTower,3) + DO i2 = LBOUND(InData%FFTower,2), UBOUND(InData%FFTower,2) + DO i1 = LBOUND(InData%FFTower,1), UBOUND(InData%FFTower,1) + ReKiBuf(Re_Xferred) = InData%FFTower(i1,i2,i3) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END DO + END IF + ReKiBuf(Re_Xferred) = InData%FFDTime + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%FFRate + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%FFYHWid + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%FFZHWid + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%RefHt + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%GridBase + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%InitXPosition + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%InvFFYD + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%InvFFZD + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%InvMFFWS + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%MeanFFWS + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%TotalTime + Re_Xferred = Re_Xferred + 1 + IntKiBuf(Int_Xferred) = InData%NFFComp + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = InData%NFFSteps + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = InData%NYGrids + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = InData%NZGrids + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = InData%NTGrids + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = InData%WindFileFormat + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%AddMeanAfterInterp, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = InData%WindProfileType + Int_Xferred = Int_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%PLExp + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%Z0 + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE IfW_FFWind_PackParam + + SUBROUTINE IfW_FFWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) + REAL(ReKi), ALLOCATABLE, INTENT(IN ) :: ReKiBuf(:) + REAL(DbKi), ALLOCATABLE, INTENT(IN ) :: DbKiBuf(:) + INTEGER(IntKi), ALLOCATABLE, INTENT(IN ) :: IntKiBuf(:) + TYPE(IfW_FFWind_ParameterType), INTENT(INOUT) :: OutData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + ! Local variables + INTEGER(IntKi) :: Buf_size + INTEGER(IntKi) :: Re_Xferred + INTEGER(IntKi) :: Db_Xferred + INTEGER(IntKi) :: Int_Xferred + INTEGER(IntKi) :: i + INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 + INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 + INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 + INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'IfW_FFWind_UnPackParam' + ! buffers to store meshes, if any + REAL(ReKi), ALLOCATABLE :: Re_Buf(:) + REAL(DbKi), ALLOCATABLE :: Db_Buf(:) + INTEGER(IntKi), ALLOCATABLE :: Int_Buf(:) + ! + ErrStat = ErrID_None + ErrMsg = "" + Re_Xferred = 1 + Db_Xferred = 1 + Int_Xferred = 1 + OutData%Periodic = TRANSFER(IntKiBuf(Int_Xferred), OutData%Periodic) + Int_Xferred = Int_Xferred + 1 + OutData%InterpTower = TRANSFER(IntKiBuf(Int_Xferred), OutData%InterpTower) + Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FFData not allocated + Int_Xferred = Int_Xferred + 1 + ELSE + Int_Xferred = Int_Xferred + 1 + i1_l = IntKiBuf( Int_Xferred ) + i1_u = IntKiBuf( Int_Xferred + 1) + Int_Xferred = Int_Xferred + 2 + i2_l = IntKiBuf( Int_Xferred ) + i2_u = IntKiBuf( Int_Xferred + 1) + Int_Xferred = Int_Xferred + 2 + i3_l = IntKiBuf( Int_Xferred ) + i3_u = IntKiBuf( Int_Xferred + 1) + Int_Xferred = Int_Xferred + 2 + i4_l = IntKiBuf( Int_Xferred ) + i4_u = IntKiBuf( Int_Xferred + 1) + Int_Xferred = Int_Xferred + 2 + IF (ALLOCATED(OutData%FFData)) DEALLOCATE(OutData%FFData) + ALLOCATE(OutData%FFData(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u,i4_l:i4_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FFData.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i4 = LBOUND(OutData%FFData,4), UBOUND(OutData%FFData,4) + DO i3 = LBOUND(OutData%FFData,3), UBOUND(OutData%FFData,3) + DO i2 = LBOUND(OutData%FFData,2), UBOUND(OutData%FFData,2) + DO i1 = LBOUND(OutData%FFData,1), UBOUND(OutData%FFData,1) + OutData%FFData(i1,i2,i3,i4) = REAL(ReKiBuf(Re_Xferred), SiKi) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END DO + END DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FFTower not allocated + Int_Xferred = Int_Xferred + 1 + ELSE + Int_Xferred = Int_Xferred + 1 + i1_l = IntKiBuf( Int_Xferred ) + i1_u = IntKiBuf( Int_Xferred + 1) + Int_Xferred = Int_Xferred + 2 + i2_l = IntKiBuf( Int_Xferred ) + i2_u = IntKiBuf( Int_Xferred + 1) + Int_Xferred = Int_Xferred + 2 + i3_l = IntKiBuf( Int_Xferred ) + i3_u = IntKiBuf( Int_Xferred + 1) + Int_Xferred = Int_Xferred + 2 + IF (ALLOCATED(OutData%FFTower)) DEALLOCATE(OutData%FFTower) + ALLOCATE(OutData%FFTower(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FFTower.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i3 = LBOUND(OutData%FFTower,3), UBOUND(OutData%FFTower,3) + DO i2 = LBOUND(OutData%FFTower,2), UBOUND(OutData%FFTower,2) + DO i1 = LBOUND(OutData%FFTower,1), UBOUND(OutData%FFTower,1) + OutData%FFTower(i1,i2,i3) = REAL(ReKiBuf(Re_Xferred), SiKi) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END DO + END IF + OutData%FFDTime = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%FFRate = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%FFYHWid = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%FFZHWid = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%RefHt = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%GridBase = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%InitXPosition = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%InvFFYD = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%InvFFZD = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%InvMFFWS = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%MeanFFWS = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%TotalTime = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%NFFComp = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + OutData%NFFSteps = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + OutData%NYGrids = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + OutData%NZGrids = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + OutData%NTGrids = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + OutData%WindFileFormat = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + OutData%AddMeanAfterInterp = TRANSFER(IntKiBuf(Int_Xferred), OutData%AddMeanAfterInterp) + Int_Xferred = Int_Xferred + 1 + OutData%WindProfileType = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + OutData%PLExp = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%Z0 = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE IfW_FFWind_UnPackParam + +END MODULE IfW_FFWind_Base_Types +!ENDOFREGISTRYGENERATEDFILE diff --git a/modules/inflowwind/src/IfW_HAWCWind.f90 b/modules/inflowwind/src/IfW_HAWCWind.f90 index 66e8748e9f..62b9365f6a 100644 --- a/modules/inflowwind/src/IfW_HAWCWind.f90 +++ b/modules/inflowwind/src/IfW_HAWCWind.f90 @@ -31,6 +31,7 @@ MODULE IfW_HAWCWind !********************************************************************************************************************************** USE NWTC_Library USE IfW_HAWCWind_Types + USE IfW_FFWind_Base IMPLICIT NONE PRIVATE @@ -42,14 +43,6 @@ MODULE IfW_HAWCWind PUBLIC :: IfW_HAWCWind_CalcOutput INTEGER(IntKi), PARAMETER :: nc = 3 !< number of wind components - INTEGER(IntKi), PARAMETER :: WindProfileType_None = -1 !< don't add wind profile; already included in input data - INTEGER(IntKi), PARAMETER :: WindProfileType_Constant = 0 !< constant wind - INTEGER(IntKi), PARAMETER :: WindProfileType_Log = 1 !< logarithmic - INTEGER(IntKi), PARAMETER :: WindProfileType_PL = 2 !< power law - - INTEGER(IntKi), PARAMETER :: ScaleMethod_None = 0 !< no scaling - INTEGER(IntKi), PARAMETER :: ScaleMethod_Direct = 1 !< direct scaling factors - INTEGER(IntKi), PARAMETER :: ScaleMethod_StdDev = 2 !< requested standard deviation CONTAINS !==================================================================================================== @@ -91,31 +84,43 @@ SUBROUTINE IfW_HAWCWind_Init(InitInp, p, MiscVars, Interval, InitOut, ErrStat, E !------------------------------------------------------------------------------------------------- ! Set some internal module parameters based on input file values !------------------------------------------------------------------------------------------------- - p%nx = InitInp%nx - p%ny = InitInp%ny - p%nz = InitInp%nz - p%RefHt = InitInp%RefHt - p%URef = InitInp%URef - p%InitPosition = InitInp%InitPosition - if (EqualRealNos(InitInp%InitPosition(1), 0.0_ReKi)) p%InitPosition(1) = InitInp%dx ! This is the old behaviour - - - p%deltaXInv = 1.0 / InitInp%dx - p%deltaYInv = 1.0 / InitInp%dy - p%deltaZInv = 1.0 / InitInp%dz - + p%FF%WindFileFormat = 0 + p%FF%Periodic = .true. + p%FF%InterpTower = .true. - p%LengthX = InitInp%dx * p%nx !(nx-1) !because the turbulence box is periodic in the X direction, we need to consider the length between point 1 and the next point 1 (instead of between points 1 and nx) - p%LengthYHalf = 0.5*InitInp%dy * (p%ny-1) - p%GridBase = p%RefHt - 0.5*(p%nz-1)*InitInp%dz - - IF ( p%GridBase < 0.0_ReKi ) THEN - call SetErrStat( ErrID_Fatal, 'The bottom of the grid is located at a height of '//& - TRIM( Num2LStr(p%GridBase) )//' meters, which is below the ground.', ErrStat,ErrMsg, RoutineName) - RETURN - END IF + p%FF%NFFComp = 3 + p%FF%NFFSteps = InitInp%nx + p%FF%NYGrids = InitInp%ny + p%FF%NZGrids = InitInp%nz + p%FF%NTGrids = 0 + + p%FF%MeanFFWS = InitInp%FF%URef + p%FF%FFDTime = InitInp%dx / InitInp%FF%URef + p%FF%FFRate = 1.0 / p%FF%FFDTime + p%FF%InvFFYD = 1.0 / InitInp%dy + p%FF%InvFFZD = 1.0 / InitInp%dz + p%FF%InvMFFWS = 1.0 / p%FF%MeanFFWS + p%FF%TotalTime = InitInp%nx * InitInp%dx / InitInp%FF%URef + p%FF%FFYHWid = 0.5 * InitInp%dy * (InitInp%ny-1) + p%FF%FFZHWid = 0.5 * InitInp%dz * (InitInp%nz-1) + p%FF%GridBase = InitInp%FF%RefHt - p%FF%FFZHWid + p%FF%RefHt = InitInp%FF%RefHt + + p%FF%WindProfileType = InitInp%FF%WindProfileType + p%FF%Z0 = InitInp%FF%Z0 + p%FF%PLExp = InitInp%FF%PLExp + p%FF%AddMeanAfterInterp = .true. + + p%FF%InitXPosition = InitInp%FF%XOffset + + IF ( p%FF%GridBase < 0.0_ReKi ) THEN + call SetErrStat( ErrID_Severe, 'WARNING: The bottom of the grid is located at a height of '//& + TRIM( Num2LStr(p%FF%GridBase) )//' meters, which is below the ground.'//& + ' Winds below the ground will be set to 0.', ErrStat,ErrMsg, RoutineName) + END IF + !------------------------------------------------------------------------------------------------- ! Read data files: !------------------------------------------------------------------------------------------------- @@ -126,7 +131,7 @@ SUBROUTINE IfW_HAWCWind_Init(InitInp, p, MiscVars, Interval, InitOut, ErrStat, E !------------------------------------------------------------------------------------------------- ! scale to requested TI (or use requested scale factors) !------------------------------------------------------------------------------------------------- - call ScaleTurbulence( p, InitInp, Interval, InitOut, MiscVars, TmpErrStat, TmpErrMsg) + call ScaleTurbulence(InitInp%FF, p%FF%FFData, InitOut%sf, TmpErrStat, TmpErrMsg) CALL SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -134,10 +139,9 @@ SUBROUTINE IfW_HAWCWind_Init(InitInp, p, MiscVars, Interval, InitOut, ErrStat, E !------------------------------------------------------------------------------------------------- ! Add the mean wind speed to the u component. !------------------------------------------------------------------------------------------------- - call AddMeanVelocity(p, InitInp, TmpErrStat, TmpErrMsg) - CALL SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - + if (InitInp%FF%ScaleMethod /= ScaleMethod_None) call SubtractMeanVelocity(p%FF%FFData) + if (.not. p%FF%AddMeanAfterInterp) call AddMeanVelocity(InitInp%FF, p%FF%GridBase, 1.0_ReKi/p%FF%InvFFZD, p%FF%FFData) + !------------------------------------------------------------------------------------------------- ! write info to summary file, if necessary !------------------------------------------------------------------------------------------------- @@ -146,24 +150,26 @@ SUBROUTINE IfW_HAWCWind_Init(InitInp, p, MiscVars, Interval, InitOut, ErrStat, E WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) 'HAWC wind type. Read by InflowWind sub-module '//TRIM(GetNVD(IfW_HAWCWind_Ver)) - WRITE(InitInp%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Reference height (m): ',p%RefHt - WRITE(InitInp%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Timestep (s): ',1.0_ReKi / (p%deltaXInv * p%URef) - WRITE(InitInp%SumFileUnit,'(A34,I12)', IOSTAT=TmpErrStat) ' Number of timesteps: ',p%nx - WRITE(InitInp%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Mean windspeed (m/s): ',p%URef + WRITE(InitInp%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Reference height (m): ',InitInp%FF%RefHt + WRITE(InitInp%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Timestep (s): ',p%FF%FFDTime + WRITE(InitInp%SumFileUnit,'(A34,I12)', IOSTAT=TmpErrStat) ' Number of timesteps: ',p%FF%NFFSteps + WRITE(InitInp%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Mean windspeed (m/s): ',p%FF%MeanFFWS WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Time range (s): [ '// & - TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr( p%LengthX / p%URef ))//' ]' + TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr( p%FF%TotalTime ))//' ]' WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' X range (m): [ '// & - TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr( p%LengthX ))//' ]' + TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr( p%FF%TotalTime * p%FF%MeanFFWS ))//' ]' WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Y range (m): [ '// & - TRIM(Num2LStr(-p%LengthYHalf))//' : '//TRIM(Num2LStr(p%LengthYHalf))//' ]' + TRIM(Num2LStr(-p%FF%FFYHWid))//' : '//TRIM(Num2LStr(p%FF%FFYHWid))//' ]' WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Z range (m): [ '// & - TRIM(Num2LStr(p%GridBase))//' : '//TRIM(Num2LStr(p%GridBase + p%nz / p%deltaZInv))//' ]' + TRIM(Num2LStr(p%FF%GridBase))//' : '//TRIM(Num2LStr(p%FF%GridBase + p%FF%FFZHWid*2.0))//' ]' WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) 'Scaling factors used:' WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' u v w ' WRITE(InitInp%SumFileUnit,'(A)', IOSTAT=TmpErrStat) '---------- ---------- ----------' WRITE(InitInp%SumFileUnit,'(F10.3,2x,F10.3,2x,F10.3)',IOSTAT=TmpErrStat) InitOut%sf ENDIF + + MiscVars%DummyMiscVar = 0 RETURN @@ -180,11 +186,13 @@ SUBROUTINE ValidateInput(InitInp, ErrStat, ErrMsg) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about errors character(*), parameter :: RoutineName = 'ValidateInput' - integer(intki) :: ic ! loop counter ErrStat = ErrID_None ErrMsg = "" + ! validate the inputs for scaling turbulence: + CALL FFWind_ValidateInput(InitInp%FF, nc, ErrStat, ErrMsg) + IF ( InitInp%nx < 1 ) CALL SetErrStat( ErrID_Fatal, 'Number of grid points in the X direction must be at least 1.', ErrStat, ErrMsg, RoutineName ) IF ( InitInp%ny < 1 ) CALL SetErrStat( ErrID_Fatal, 'Number of grid points in the Y direction must be at least 1.', ErrStat, ErrMsg, RoutineName ) IF ( InitInp%nz < 1 ) CALL SetErrStat( ErrID_Fatal, 'Number of grid points in the Z direction must be at least 1.', ErrStat, ErrMsg, RoutineName ) @@ -192,35 +200,7 @@ SUBROUTINE ValidateInput(InitInp, ErrStat, ErrMsg) IF ( InitInp%dx < 0.0_ReKi .or. EqualRealNos( InitInp%dx, 0.0_ReKi ) ) CALL SetErrStat( ErrID_Fatal, 'The grid spacing in the X direction must be larger than 0.', ErrStat, ErrMsg, RoutineName ) IF ( InitInp%dy < 0.0_ReKi .or. EqualRealNos( InitInp%dy, 0.0_ReKi ) ) CALL SetErrStat( ErrID_Fatal, 'The grid spacing in the Y direction must be larger than 0.', ErrStat, ErrMsg, RoutineName ) IF ( InitInp%dz < 0.0_ReKi .or. EqualRealNos( InitInp%dz, 0.0_ReKi ) ) CALL SetErrStat( ErrID_Fatal, 'The grid spacing in the Z direction must be larger than 0.', ErrStat, ErrMsg, RoutineName ) - - IF ( InitInp%RefHt < 0.0_ReKi .or. EqualRealNos( InitInp%RefHt, 0.0_ReKi ) ) call SetErrStat( ErrID_Fatal, 'The grid reference height must be larger than 0.', ErrStat, ErrMsg, RoutineName ) - - if ( InitInp%ScaleMethod == ScaleMethod_Direct) then - do ic=1,nc - if ( InitInp%sf(ic) < 0.0_ReKi ) CALL SetErrStat( ErrID_Fatal, 'Turbulence scaling factors must not be negative.', ErrStat, ErrMsg, RoutineName ) - end do - elseif ( InitInp%ScaleMethod == ScaleMethod_StdDev ) then - do ic=1,nc - if ( InitInp%sigmaf(ic) < 0.0_ReKi ) CALL SetErrStat( ErrID_Fatal, 'Turbulence standard deviations must not be negative.', ErrStat, ErrMsg, RoutineName ) - end do -#ifdef UNUSED_INPUTFILE_LINES - if ( InitInp%TStart < 0.0_ReKi ) CALL SetErrStat( ErrID_Fatal, 'TStart for turbulence standard deviation calculations must not be negative.', ErrStat, ErrMsg, RoutineName ) - if ( InitInp%TEnd <= InitInp%TStart ) CALL SetErrStat( ErrID_Fatal, 'TEnd for turbulence standard deviation calculations must be after TStart.', ErrStat, ErrMsg, RoutineName ) -#endif - elseif ( InitInp%ScaleMethod /= ScaleMethod_None ) then - CALL SetErrStat( ErrID_Fatal, 'HAWC scaling method must be 0 (none), 1 (direct scaling factors), or 2 (target standard deviation).', ErrStat, ErrMsg, RoutineName ) - end if - - - if (InitInp%WindProfileType == WindProfileType_Log) then - if ( InitInp%z0 < 0.0_ReKi .or. EqualRealNos( InitInp%z0, 0.0_ReKi ) ) & - call SetErrStat( ErrID_Fatal, 'The surface roughness length, Z0, must be greater than zero', ErrStat, ErrMsg, RoutineName ) - elseif ( InitInp%WindProfileType < WindProfileType_None .or. InitInp%WindProfileType > WindProfileType_PL) then - call SetErrStat( ErrID_Fatal, 'The WindProfile type must be 0 (constant), 1 (logarithmic) or 2 (power law).', ErrStat, ErrMsg, RoutineName ) - end if - - IF ( InitInp%URef < 0.0_ReKi ) call SetErrStat( ErrID_Fatal, 'The reference wind speed must not be negative.', ErrStat, ErrMsg, RoutineName ) - + END SUBROUTINE ValidateInput !==================================================================================================== @@ -234,10 +214,10 @@ SUBROUTINE ReadTurbulenceData(p, InitInp, ErrStat, ErrMsg) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about errors ! Local Variables: - INTEGER :: IC ! Loop counter for the number of wind components - INTEGER :: IX ! Loop counter for the number of grid points in the X direction - INTEGER :: IY ! Loop counter for the number of grid points in the Y direction - INTEGER :: unWind ! unit number for reading binary files + INTEGER :: IC ! Loop counter for the number of wind components + INTEGER :: IX ! Loop counter for the number of grid points in the X direction + INTEGER :: IY ! Loop counter for the number of grid points in the Y direction + INTEGER :: unWind ! unit number for reading binary files INTEGER(IntKi) :: TmpErrStat ! temporary error status CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message @@ -252,7 +232,7 @@ SUBROUTINE ReadTurbulenceData(p, InitInp, ErrStat, ErrMsg) ! Allocate space for the wind array. !------------------------------------------------------------------------------------------------- - CALL AllocAry( p%HAWCData, p%nz, p%ny, p%nx, nc, 'p%HAWCData', TmpErrStat, TmpErrMsg ) + CALL AllocAry( p%FF%FFData, p%FF%NZGrids,p%FF%NYGrids,p%FF%NFFComp, p%FF%NFFSteps, 'p%FF%FFData', TmpErrStat, TmpErrMsg ) CALL SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -267,7 +247,10 @@ SUBROUTINE ReadTurbulenceData(p, InitInp, ErrStat, ErrMsg) ! this could take a while, so we'll write a message indicating what's going on: CALL WrScr( NewLine//' Reading HAWC wind files with grids of '//& - TRIM( Num2LStr(p%nx) )//' x '//TRIM( Num2LStr(p%ny) )//' x '//TRIM( Num2LStr(p%nz) )//' points.' ) + TRIM( Num2LStr(p%FF%NFFSteps) )//' x '//TRIM( Num2LStr(p%FF%NYGrids) )//' x '//TRIM( Num2LStr(p%FF%NZGrids) )//' points'// & + ' ('//TRIM( Num2LStr(p%FF%FFYHWid*2) )//' m wide, '// TRIM( Num2LStr(p%FF%GridBase) )//' m to '// & + TRIM( Num2LStr(p%FF%GridBase+p%FF%FFZHWid*2) )//& + ' m above ground) with a characteristic wind speed of '//TRIM( Num2LStr(p%FF%MeanFFWS) )//' m/s. ' ) CALL GetNewUnit( UnWind, TmpErrStat, TmpErrMsg ) @@ -283,11 +266,11 @@ SUBROUTINE ReadTurbulenceData(p, InitInp, ErrStat, ErrMsg) CALL SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN - DO IX = p%nx,1,-1 ! Time is the opposite of X .... - DO IY = p%ny,1,-1 - !DO IZ = 1,p%nz + DO IX = 1,p%FF%NFFSteps + DO IY = p%FF%NYGrids,1,-1 + !DO IZ = 1,p%FF%NZGrids - READ( UnWind, IOSTAT=TmpErrStat ) p%HAWCData(:,iy,ix,ic) ! note that HAWCData is SiKi (4-byte reals, not default kinds) + READ( UnWind, IOSTAT=TmpErrStat ) p%FF%FFData(:,iy,ic,ix) ! note that FFData is SiKi (4-byte reals, not default kinds) IF (TmpErrStat /= 0) THEN TmpErrMsg = ' Error reading binary data from "'//TRIM(InitInp%WindFileName(IC))//'". I/O error ' & @@ -308,178 +291,6 @@ SUBROUTINE ReadTurbulenceData(p, InitInp, ErrStat, ErrMsg) END SUBROUTINE ReadTurbulenceData !==================================================================================================== -!> This routine is used read scale the full-field turbulence data stored in HAWC format. -SUBROUTINE ScaleTurbulence(p, InitInp, Interval, InitOut, MiscVars, ErrStat, ErrMsg) - - ! Passed Variables - TYPE(IfW_HAWCWind_ParameterType), INTENT(INOUT) :: p !< Parameters - TYPE(IfW_HAWCWind_InitInputType), INTENT(IN ) :: InitInp !< Initialization input data passed to the module - TYPE(IfW_HAWCWind_InitOutputType), INTENT(INOUT) :: InitOut !< Initialization output data passed from the module - REAL(DbKi), INTENT(IN ) :: Interval !< Time Interval to use (passed through here) - TYPE(IfW_HAWCWind_MiscVarType), INTENT(INOUT) :: MiscVars !< Misc variables for optimization (not copied in glue code) - INTEGER(IntKi), INTENT( OUT) :: ErrStat !< determines if an error has been encountered - CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about errors - - ! Local Variables: - REAL(DbKi) :: v(3) ! instanteanous wind speed at target position - REAL(DbKi) :: vMean(3) ! average wind speeds over time at target position - REAL(DbKi) :: vSum(3) ! sum over time of wind speeds at target position - REAL(DbKi) :: vSum2(3) ! sum of wind speeds squared - REAL(ReKi) :: ActualSigma(3) ! computed standard deviation - - INTEGER :: ic ! Loop counter for wind component - INTEGER :: ix ! Loop counter for x - INTEGER :: iy ! Loop counter for y - INTEGER :: iz ! Loop counter for z - - INTEGER(IntKi) :: TmpErrStat ! temporary error status - CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message - CHARACTER(*), PARAMETER :: RoutineName = 'CalcScaleFactors' - - - - ErrStat = ErrID_None - ErrMsg = "" - - if ( InitInp%ScaleMethod == ScaleMethod_None ) then - InitOut%sf = 1.0_ReKi - else ! ScaleMethod_Direct or ScaleMethod_StdDev - if ( InitInp%ScaleMethod == ScaleMethod_Direct ) then - InitOut%sf = InitInp%sf - else !if ( InitInp%ScaleMethod == ScaleMethod_StdDev ) then - -#ifdef UNUSED_INPUTFILE_LINES - ! calculate actual standard deviations, then compute scaling factor based on ratio of target to actual - position(1) = 0.0_ReKi - position(2) = 0.0_ReKi - position(3) = p%RefHt - - vSum = 0.0 - vSum2 = 0.0 - - n = nint( ( InitInp%TEnd - InitInp%TStart ) / Interval ) - DO it=1,n - t = InitInp%TStart + it*Interval - v = FF_Interp(t,position,p,OtherStates,TmpErrStat,TmpErrMsg) - CALL SetErrStat( TmpErrStat,TmpErrMsg, ErrStat, ErrMsg, RoutineName ) - - vSum = vSum + v - vSum2 = vSum2 + v**2 - ENDDO ! IT - - vMean = vSum/n - ActualSigma = SQRT( ABS( (vSum2/n) - vMean**2 ) ) - - !InitOut%sf = InitInp%SigmaF / ActualSigma ! factor = Target / actual - - do ic=1,nc - if ( EqualRealNos( ActualSigma(ic), 0.0_ReKi ) ) then - InitOut%sf(ic) = 0.0_ReKi - if ( .not. EqualRealNos( InitInp%SigmaF(ic), 0.0_ReKi ) ) then - call SetErrStat( ErrID_Fatal,"Computed standard deviation is zero; cannot scale to achieve target standard deviation.", ErrStat, ErrMsg, RoutineName ) - end if - else - InitOut%sf(ic) = InitInp%SigmaF(ic) / ActualSigma(ic) - end if - end do -#else - - ! roughly the point in the center of the grid - iz = (p%nz + 1) / 2 ! integer division - iy = (p%ny + 1) / 2 ! integer division - - DO ix=1,p%nx - v = p%HAWCData(iz,iy,ix,:) - - vSum = vSum + v - vSum2 = vSum2 + v**2 - ENDDO ! IT - - vMean = vSum/p%nx - ActualSigma = SQRT( ABS( (vSum2/p%nx) - vMean**2 ) ) - - !InitOut%sf = InitInp%SigmaF / ActualSigma ! factor = Target / actual - do ic=1,nc - if ( EqualRealNos( ActualSigma(ic), 0.0_ReKi ) ) then - InitOut%sf(ic) = 0.0_ReKi - if ( .not. EqualRealNos( InitInp%SigmaF(ic), 0.0_ReKi ) ) then - call SetErrStat( ErrID_Fatal,"Computed standard deviation is zero; cannot scale to achieve target standard deviation.", ErrStat, ErrMsg, RoutineName ) - end if - else - InitOut%sf(ic) = InitInp%SigmaF(ic) / ActualSigma(ic) - end if - end do -#endif - end if - - ! scale using our scaling factors: - do ic = 1,nc - p%HAWCData( :, :, :, ic ) = InitOut%sf(IC)*p%HAWCData( :, :, :, ic ) - end do !IC - - end if - -END SUBROUTINE ScaleTurbulence -!==================================================================================================== -!> This routine is used to add a mean wind profile to the HAWC format turbulence data. -SUBROUTINE AddMeanVelocity(p, InitInp, ErrStat, ErrMsg) - - ! Passed Variables - TYPE(IfW_HAWCWind_ParameterType), INTENT(INOUT) :: p !< Parameters - TYPE(IfW_HAWCWind_InitInputType), INTENT(IN ) :: InitInp !< Initialization input data passed to the module - INTEGER(IntKi), INTENT( OUT) :: ErrStat !< determines if an error has been encountered - CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about errors - - ! Local Variables: - REAL(ReKi) :: Z ! height - REAL(ReKi) :: U ! mean wind speed - INTEGER(IntKi) :: iz ! loop counter - INTEGER(IntKi) :: TmpErrStat ! temporary error status - CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message - CHARACTER(*), PARAMETER :: RoutineName = 'AddMeanVelocity' - - - - ErrStat = ErrID_None - ErrMsg = "" - - - DO IZ = 1,p%NZ - - Z = p%GridBase + ( IZ - 1 )*InitInp%dz - - SELECT CASE ( InitInp%WindProfileType ) - - CASE ( WindProfileType_PL ) - - U = p%URef*( Z / p%RefHt )**InitInp%PLExp ! [IEC 61400-1 6.3.1.2 (10)] - - CASE ( WindProfileType_Log ) - - IF ( .not. EqualRealNos( p%RefHt, InitInp%Z0 ) .and. z > 0.0_ReKi ) THEN - U = p%URef*( LOG( Z / InitInp%Z0 ) )/( LOG( p%RefHt / InitInp%Z0 ) ) - ELSE - U = 0.0_ReKi - ENDIF - - CASE ( WindProfileType_Constant ) - - U = p%URef - - CASE DEFAULT - - U = 0.0_ReKi - - END SELECT - - p%HAWCData( IZ, :, :, 1 ) = p%HAWCData( IZ, :, :, 1 ) + U - - - END DO ! IZ - - -END SUBROUTINE AddMeanVelocity -!==================================================================================================== !> This routine acts as a wrapper for the GetWindSpeed routine. It steps through the array of input !! positions and calls the GetWindSpeed routine to calculate the velocities at each point. !! @@ -526,222 +337,10 @@ SUBROUTINE IfW_HAWCWind_CalcOutput(Time, PositionXYZ, p, Velocity, DiskVel, Misc ! Initialize some things !------------------------------------------------------------------------------------------------- - - ! The array is transposed so that the number of points is the second index, x/y/z is the first. - ! This is just in case we only have a single point, the SIZE command returns the correct number of points. - NumPoints = SIZE(PositionXYZ,2) - - ! Step through all the positions and get the velocities - DO PointNum = 1, NumPoints - - ! Calculate the velocity for the position - Velocity(:,PointNum) = FF_Interp(Time,PositionXYZ(:,PointNum),p,MiscVars,TmpErrStat,TmpErrMsg) - - - ! Error handling - IF (TmpErrStat /= ErrID_None) THEN ! adding this so we don't have to convert numbers to strings every time - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName//" [position=("// & - TRIM(Num2LStr(PositionXYZ(1,PointNum)))//", "// & - TRIM(Num2LStr(PositionXYZ(2,PointNum)))//", "// & - TRIM(Num2LStr(PositionXYZ(3,PointNum)))//") in wind-file coordinates]" ) - IF (ErrStat >= AbortErrLev) RETURN - END IF - - ENDDO - - - - !REMOVE THIS for AeroDyn 15 - ! Return the average disk velocity values needed by AeroDyn 14. This is the WindInf_ADhack_diskVel routine. - DiskVel(1) = p%URef - DiskVel(2:3) = 0.0_ReKi - - + CALL IfW_FFWind_CalcOutput(Time, PositionXYZ, p%FF, Velocity, DiskVel, ErrStat, ErrMsg) RETURN END SUBROUTINE IfW_HAWCWind_CalcOutput -!==================================================================================================== - !+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - !> This function is used to interpolate into the full-field wind array. It receives X, Y, Z and - !! TIME from the calling routine. It then computes a time shift due to a nonzero X based upon - !! the average windspeed. The modified time is used to decide which pair of time slices to interpolate - !! within and between. After finding the two time slices, it decides which four grid points bound the - !! (Y,Z) pair. It does a 3-d linear interpolation. This routine assumes that X is downwind, Y is to the left when - !! looking downwind and Z is up. It also assumes that no extrapolation will be needed. - FUNCTION FF_Interp(Time, Position, p, MiscVars, ErrStat, ErrMsg) - - REAL(DbKi), INTENT(IN ) :: Time !< Time at which to find wind speed - REAL(ReKi), INTENT(IN ) :: Position(3) !< takes the place of XGrnd, YGrnd, ZGrnd - TYPE(IfW_HAWCWind_ParameterType), INTENT(IN ) :: p !< Parameters - TYPE(IfW_HAWCWind_MiscVarType), INTENT(INOUT) :: MiscVars !< Misc variables for optimization (not copied in glue code) - REAL(ReKi) :: FF_Interp(3) !< The U, V, W velocities - - INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status - CHARACTER(*), INTENT( OUT) :: ErrMsg !< error message - - ! Local Variables: - - CHARACTER(*), PARAMETER :: RoutineName="FF_Interp" - - - REAL(ReKi) :: ShiftedXPosition - REAL(ReKi),PARAMETER :: Tol = 1.0E-3 ! a tolerance for determining if two reals are the same (for extrapolation) - REAL(ReKi) :: X ! value between -1 and 1 (where we are between 2 x grid points) - REAL(ReKi) :: XGRID - REAL(ReKi) :: Y - REAL(ReKi) :: YGRID - REAL(ReKi) :: Z - REAL(ReKi) :: ZGRID - REAL(ReKi) :: N(8) ! array for holding scaling factors for the interpolation algorithm - REAL(ReKi) :: u(8) ! array for holding the corner values for the interpolation algorithm across a cubic volume - - INTEGER(IntKi) :: IDIM - INTEGER(IntKi) :: IXHI - INTEGER(IntKi) :: IXLO - INTEGER(IntKi) :: IYHI - INTEGER(IntKi) :: IYLO - INTEGER(IntKi) :: IZHI - INTEGER(IntKi) :: IZLO - - - !------------------------------------------------------------------------------------------------- - ! Initialize variables - !------------------------------------------------------------------------------------------------- - - FF_Interp(:) = 0.0_ReKi ! the output velocities (in case p%NFFComp /= 3) - - ErrStat = ErrID_None - ErrMsg = "" - - !------------------------------------------------------------------------------------------------- - ! Find the bounding time slices. - !------------------------------------------------------------------------------------------------- - - ! bjj: should we shift by MIN(YHalfWid,FFZHWid)? - - ! Assume Taylor's Frozen Turbulence Hypothesis applies: u(X,Y,Z,t) = u( X-U*t, Y, Z, 0) - - ShiftedXPosition = Position(1) - TIME*p%URef - p%InitPosition(1) !this puts the first X grid point at the undeflected tower centerline (or p%InitXPosition) - - ! The wind file is periodic so we'll translate this position to ( 0 <= ShiftedXPosition < p%LengthX ) - - ShiftedXPosition = MODULO( ShiftedXPosition, p%LengthX ) - ! If ShiftedXPosition is a very small negative number, modulo returns the incorrect value due to internal rounding errors. - ! See bug report #471 - IF (ShiftedXPosition == p%LengthX) ShiftedXPosition = 0.0_ReKi - - XGrid = ShiftedXPosition * p%deltaXInv - - IXLO = INT( XGrid ) ! convert REAL to INTEGER - X = 2.0_ReKi * ( XGrid - REAL(IXLO, ReKi) ) - 1.0_ReKi ! a value between -1 and 1 that indicates a relative position between IXLO and IXHI - IXLO = IXLO + 1 ! Add 1 because our grids start at 1, not zero - - IF ( IXLO == p%NX ) THEN - IXHI = 1 - ELSE - IXHI = IXLO + 1 - ENDIF - - !------------------------------------------------------------------------------------------------- - ! Find the bounding rows for the Z position. [The lower-left corner is (1,1) when looking upwind.] - !------------------------------------------------------------------------------------------------- - - ZGRID = ( Position(3) - p%GridBase - p%InitPosition(3) )*p%deltaZInv - - ! Index for start and end slices - IZLO = INT( ZGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 - IZHI = IZLO + 1 - - ! Set Z as a value between -1 and 1 for the relative location between IZLO and IZHI. - ! Subtract 1_IntKi from Z since the indices are starting at 1, not 0 - Z = 2.0_ReKi * (ZGRID - REAL(IZLO - 1_IntKi, ReKi)) - 1.0_ReKi - - IF ( IZLO < 1 ) THEN - IF ( IZLO == 0 .AND. Z >= 1.0-TOL ) THEN - Z = -1.0_ReKi - IZLO = 1 - ELSE - ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction (Z='//& - TRIM(Num2LStr(Position(3)))//' m is below the grid).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - ELSEIF ( IZLO >= p%nz ) THEN - IF ( IZLO == p%nz .AND. Z <= TOL ) THEN - Z = -1.0_ReKi - IZHI = IZLO ! We're right on the last point, which is still okay - ELSE - ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction (Z='//& - TRIM(Num2LStr(Position(3)))//' m is above the grid).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - ENDIF - - - !------------------------------------------------------------------------------------------------- - ! Find the bounding columns for the Y position. [The lower-left corner is (1,1) when looking upwind.] - !------------------------------------------------------------------------------------------------- - YGRID = ( Position(2) + p%LengthYHalf - p%InitPosition(2) )*p%deltaYInv ! really, it's (Position(2) - -1.0*p%LengthYHalf) - - IYLO = INT( YGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 - IYHI = IYLO + 1 - - ! Set Y as a value between -1 and 1 for the relative location between IYLO and IYHI. Used in the interpolation. - ! Subtract 1_IntKi from IYLO since grids start at index 1, not 0 - Y = 2.0_ReKi * (YGRID - REAL(IYLO - 1_IntKi, ReKi)) - 1.0_ReKi - - IF ( IYLO >= p%ny .OR. IYLO < 1 ) THEN - IF ( IYLO == 0 .AND. Y >= 1.0-TOL ) THEN - Y = -1.0_ReKi - IYLO = 1 - ELSE IF ( IYLO == p%ny .AND. Y <= TOL ) THEN - Y = -1.0_ReKi - IYHI = IYLO ! We're right on the last point, which is still okay - ELSE - ErrMsg = ' FF wind array boundaries violated: Grid too small in Y direction. Y='// & - TRIM(Num2LStr(Position(2)))//'; Y boundaries = ['//TRIM(Num2LStr(-1.0*p%LengthYHalf))// & - ', '//TRIM(Num2LStr(p%LengthYHalf))//']' - ErrStat = ErrID_Fatal ! we don't return anything - RETURN - ENDIF - ENDIF - - !------------------------------------------------------------------------------------------------- - ! Interpolate on the grid - !------------------------------------------------------------------------------------------------- - - DO IDIM=1,nc ! all the components - - - N(1) = ( 1.0_ReKi + Z )*( 1.0_ReKi - Y )*( 1.0_ReKi - X ) - N(2) = ( 1.0_ReKi + Z )*( 1.0_ReKi + Y )*( 1.0_ReKi - X ) - N(3) = ( 1.0_ReKi - Z )*( 1.0_ReKi + Y )*( 1.0_ReKi - X ) - N(4) = ( 1.0_ReKi - Z )*( 1.0_ReKi - Y )*( 1.0_ReKi - X ) - N(5) = ( 1.0_ReKi + Z )*( 1.0_ReKi - Y )*( 1.0_ReKi + X ) - N(6) = ( 1.0_ReKi + Z )*( 1.0_ReKi + Y )*( 1.0_ReKi + X ) - N(7) = ( 1.0_ReKi - Z )*( 1.0_ReKi + Y )*( 1.0_ReKi + X ) - N(8) = ( 1.0_ReKi - Z )*( 1.0_ReKi - Y )*( 1.0_ReKi + X ) - N = N / REAL( SIZE(N), ReKi ) ! normalize - - - u(1) = p%HAWCData( IZHI, IYLO, IXLO, IDIM ) - u(2) = p%HAWCData( IZHI, IYHI, IXLO, IDIM ) - u(3) = p%HAWCData( IZLO, IYHI, IXLO, IDIM ) - u(4) = p%HAWCData( IZLO, IYLO, IXLO, IDIM ) - u(5) = p%HAWCData( IZHI, IYLO, IXHI, IDIM ) - u(6) = p%HAWCData( IZHI, IYHI, IXHI, IDIM ) - u(7) = p%HAWCData( IZLO, IYHI, IXHI, IDIM ) - u(8) = p%HAWCData( IZLO, IYLO, IXHI, IDIM ) - - FF_Interp(IDIM) = SUM ( N * u ) - - - END DO !IDIM - - RETURN - - END FUNCTION FF_Interp !==================================================================================================== !> This subroutine cleans up any data that is still allocated. The (possibly) open files are !! closed in InflowWindMod. diff --git a/modules/inflowwind/src/IfW_HAWCWind.txt b/modules/inflowwind/src/IfW_HAWCWind.txt index 4d38bd5672..1d45291817 100644 --- a/modules/inflowwind/src/IfW_HAWCWind.txt +++ b/modules/inflowwind/src/IfW_HAWCWind.txt @@ -8,30 +8,21 @@ ################################################################################################################################### include Registry_NWTC_Library.txt +usefrom IfW_FFWind_Base.txt ######################### # Init Input -typedef IfW_HAWCWind/IfW_HAWCWind InitInputType CHARACTER(1024) WindFileName {3} - - "Name of the wind file to use" - -typedef ^ ^ IntKi SumFileUnit - - - "Unit number for the summary file (-1 for none). Provided by IfW." - -typedef ^ ^ IntKi nx - 0 - "Number of grids in the x direction (in the 3 files above)" - -typedef ^ ^ IntKi ny - 0 - "Number of grids in the y direction (in the 3 files above)" - -typedef ^ ^ IntKi nz - 0 - "Number of grids in the z direction (in the 3 files above)" - -typedef ^ ^ IntKi ScaleMethod - 0 - "Turbulence scaling method [0=none, 1=direct scaling, 2= calculate scaling factor based on a desired standard deviation]" - -typedef ^ ^ ReKi SF 3 0 - "Turbulence scaling factor for each direction [ScaleMethod=1]" - -typedef ^ ^ ReKi SigmaF 3 0 - "Turbulence standard deviation to calculate scaling from in each direction [ScaleMethod=2]" - -#typedef ^ ^ ReKi TStart - 0 - "" - -#typedef ^ ^ ReKi TEnd - 0 - "" - -typedef ^ ^ ReKi dx - 0 - "size of grids in the x direction (in the 3 files above)" - -typedef ^ ^ ReKi dy - 0 - "size of grids in the y direction (in the 3 files above)" - -typedef ^ ^ ReKi dz - 0 - "size of grids in the z direction (in the 3 files above)" - -typedef ^ ^ IntKi WindProfileType - - - "Wind profile type (0=constant;1=logarithmic;2=power law)" - -typedef ^ ^ ReKi RefHt - 0 - "Reference (hub) height of the grid" meters -typedef ^ ^ ReKi URef - 0 - "Mean u-component wind speed at the reference height" meters -typedef ^ ^ ReKi PLExp - 0 - "Power law exponent (used for PL wind profile type only)" - -typedef ^ ^ ReKi Z0 - 0 - "Surface roughness length (used for LOG wind profile type only)" - -typedef ^ ^ ReKi InitPosition 3 0 - "the initial position of grid (distance in FF is offset)" meters +typedef IfW_HAWCWind/IfW_HAWCWind InitInputType CHARACTER(1024) WindFileName {3} - - "Name of the wind file to use" - +typedef ^ ^ IntKi SumFileUnit - - - "Unit number for the summary file (-1 for none). Provided by IfW." - +typedef ^ ^ IntKi nx - 0 - "Number of grids in the x direction (in the 3 files above)" - +typedef ^ ^ IntKi ny - 0 - "Number of grids in the y direction (in the 3 files above)" - +typedef ^ ^ IntKi nz - 0 - "Number of grids in the z direction (in the 3 files above)" - +typedef ^ ^ ReKi dx - 0 - "size of grids in the x direction (in the 3 files above)" - +typedef ^ ^ ReKi dy - 0 - "size of grids in the y direction (in the 3 files above)" - +typedef ^ ^ ReKi dz - 0 - "size of grids in the z direction (in the 3 files above)" - +typedef ^ ^ IfW_FFWind_InitInputType FF - - - "scaling data" - # Init Output @@ -53,22 +44,7 @@ typedef ^ MiscVarType ReKi DummyMiscVar - - - "Rem # ..... Parameters ................................................................................................................ # Define parameters here: # Time step for integration of continuous states (if a fixed-step integrator is used) and update of discrete states: -typedef ^ ParameterType IntKi nx - 0 - "Number of grids in the x direction (in the 3 files above)" - -typedef ^ ^ IntKi ny - 0 - "Number of grids in the y direction (in the 3 files above)" - -typedef ^ ^ IntKi nz - 0 - "Number of grids in the z direction (in the 3 files above)" - -typedef ^ ^ ReKi RefHt - 0 - "Reference (hub) height of the grid" meters -typedef ^ ^ SiKi HAWCData :::: - - "Array of HAWC data" - -typedef ^ ^ ReKi InitPosition 3 0 - "the initial position of grid (distance in FF is offset)" meters - -typedef ^ ^ ReKi GridBase - 0 - "the height of the bottom of the grid" meters -typedef ^ ^ ReKi LengthX - - - "the grid length in the X direction (distance between point 1 and the next point 1 [because it is periodic])" meters -typedef ^ ^ ReKi LengthYHalf - - - "half the grid width" meters -typedef ^ ^ ReKi deltaXInv - - - "multiplicative inverse of delta X" "1/m" -typedef ^ ^ ReKi deltaYInv - - - "multiplicative inverse of delta Y" "1/m" -typedef ^ ^ ReKi deltaZInv - - - "multiplicative inverse of delta Z" "1/m" - -typedef ^ ^ ReKi URef - 0 - "Mean u-component wind speed at the reference height" meters - +typedef ^ ParameterType IfW_FFWind_ParameterType FF - - - "Parameters used in all full-field wind types" - # ..... Inputs .................................................................................................................... # Define inputs that are not on this mesh here: diff --git a/modules/inflowwind/src/IfW_HAWCWind_Types.f90 b/modules/inflowwind/src/IfW_HAWCWind_Types.f90 index 63058dc0d0..8fce841f50 100644 --- a/modules/inflowwind/src/IfW_HAWCWind_Types.f90 +++ b/modules/inflowwind/src/IfW_HAWCWind_Types.f90 @@ -31,6 +31,7 @@ !! unpack routines associated with each defined data type. This code is automatically generated by the FAST Registry. MODULE IfW_HAWCWind_Types !--------------------------------------------------------------------------------------------------------------------------------- +USE IfW_FFWind_Base_Types USE NWTC_Library IMPLICIT NONE ! ========= IfW_HAWCWind_InitInputType ======= @@ -40,18 +41,10 @@ MODULE IfW_HAWCWind_Types INTEGER(IntKi) :: nx = 0 !< Number of grids in the x direction (in the 3 files above) [-] INTEGER(IntKi) :: ny = 0 !< Number of grids in the y direction (in the 3 files above) [-] INTEGER(IntKi) :: nz = 0 !< Number of grids in the z direction (in the 3 files above) [-] - INTEGER(IntKi) :: ScaleMethod = 0 !< Turbulence scaling method [0=none, 1=direct scaling, 2= calculate scaling factor based on a desired standard deviation] [-] - REAL(ReKi) , DIMENSION(1:3) :: SF !< Turbulence scaling factor for each direction [ScaleMethod=1] [-] - REAL(ReKi) , DIMENSION(1:3) :: SigmaF !< Turbulence standard deviation to calculate scaling from in each direction [ScaleMethod=2] [-] REAL(ReKi) :: dx = 0 !< size of grids in the x direction (in the 3 files above) [-] REAL(ReKi) :: dy = 0 !< size of grids in the y direction (in the 3 files above) [-] REAL(ReKi) :: dz = 0 !< size of grids in the z direction (in the 3 files above) [-] - INTEGER(IntKi) :: WindProfileType !< Wind profile type (0=constant;1=logarithmic;2=power law) [-] - REAL(ReKi) :: RefHt = 0 !< Reference (hub) height of the grid [meters] - REAL(ReKi) :: URef = 0 !< Mean u-component wind speed at the reference height [meters] - REAL(ReKi) :: PLExp = 0 !< Power law exponent (used for PL wind profile type only) [-] - REAL(ReKi) :: Z0 = 0 !< Surface roughness length (used for LOG wind profile type only) [-] - REAL(ReKi) , DIMENSION(1:3) :: InitPosition !< the initial position of grid (distance in FF is offset) [meters] + TYPE(IfW_FFWind_InitInputType) :: FF !< scaling data [-] END TYPE IfW_HAWCWind_InitInputType ! ======================= ! ========= IfW_HAWCWind_InitOutputType ======= @@ -87,19 +80,7 @@ MODULE IfW_HAWCWind_Types ! ======================= ! ========= IfW_HAWCWind_ParameterType ======= TYPE, PUBLIC :: IfW_HAWCWind_ParameterType - INTEGER(IntKi) :: nx = 0 !< Number of grids in the x direction (in the 3 files above) [-] - INTEGER(IntKi) :: ny = 0 !< Number of grids in the y direction (in the 3 files above) [-] - INTEGER(IntKi) :: nz = 0 !< Number of grids in the z direction (in the 3 files above) [-] - REAL(ReKi) :: RefHt = 0 !< Reference (hub) height of the grid [meters] - REAL(SiKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: HAWCData !< Array of HAWC data [-] - REAL(ReKi) , DIMENSION(1:3) :: InitPosition !< the initial position of grid (distance in FF is offset) [meters] - REAL(ReKi) :: GridBase = 0 !< the height of the bottom of the grid [meters] - REAL(ReKi) :: LengthX !< the grid length in the X direction (distance between point 1 and the next point 1 [because it is periodic]) [meters] - REAL(ReKi) :: LengthYHalf !< half the grid width [meters] - REAL(ReKi) :: deltaXInv !< multiplicative inverse of delta X [1/m] - REAL(ReKi) :: deltaYInv !< multiplicative inverse of delta Y [1/m] - REAL(ReKi) :: deltaZInv !< multiplicative inverse of delta Z [1/m] - REAL(ReKi) :: URef = 0 !< Mean u-component wind speed at the reference height [meters] + TYPE(IfW_FFWind_ParameterType) :: FF !< Parameters used in all full-field wind types [-] END TYPE IfW_HAWCWind_ParameterType ! ======================= ! ========= IfW_HAWCWind_InputType ======= @@ -118,8 +99,6 @@ SUBROUTINE IfW_HAWCWind_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlC INTEGER(IntKi) :: i,j,k INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_HAWCWind_CopyInitInput' @@ -131,18 +110,12 @@ SUBROUTINE IfW_HAWCWind_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlC DstInitInputData%nx = SrcInitInputData%nx DstInitInputData%ny = SrcInitInputData%ny DstInitInputData%nz = SrcInitInputData%nz - DstInitInputData%ScaleMethod = SrcInitInputData%ScaleMethod - DstInitInputData%SF = SrcInitInputData%SF - DstInitInputData%SigmaF = SrcInitInputData%SigmaF DstInitInputData%dx = SrcInitInputData%dx DstInitInputData%dy = SrcInitInputData%dy DstInitInputData%dz = SrcInitInputData%dz - DstInitInputData%WindProfileType = SrcInitInputData%WindProfileType - DstInitInputData%RefHt = SrcInitInputData%RefHt - DstInitInputData%URef = SrcInitInputData%URef - DstInitInputData%PLExp = SrcInitInputData%PLExp - DstInitInputData%Z0 = SrcInitInputData%Z0 - DstInitInputData%InitPosition = SrcInitInputData%InitPosition + CALL IfW_FFWind_CopyInitInput( SrcInitInputData%FF, DstInitInputData%FF, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE IfW_HAWCWind_CopyInitInput SUBROUTINE IfW_HAWCWind_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -154,6 +127,7 @@ SUBROUTINE IfW_HAWCWind_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" + CALL IfW_FFWind_DestroyInitInput( InitInputData%FF, ErrStat, ErrMsg ) END SUBROUTINE IfW_HAWCWind_DestroyInitInput SUBROUTINE IfW_HAWCWind_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -196,18 +170,27 @@ SUBROUTINE IfW_HAWCWind_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSt Int_BufSz = Int_BufSz + 1 ! nx Int_BufSz = Int_BufSz + 1 ! ny Int_BufSz = Int_BufSz + 1 ! nz - Int_BufSz = Int_BufSz + 1 ! ScaleMethod - Re_BufSz = Re_BufSz + SIZE(InData%SF) ! SF - Re_BufSz = Re_BufSz + SIZE(InData%SigmaF) ! SigmaF Re_BufSz = Re_BufSz + 1 ! dx Re_BufSz = Re_BufSz + 1 ! dy Re_BufSz = Re_BufSz + 1 ! dz - Int_BufSz = Int_BufSz + 1 ! WindProfileType - Re_BufSz = Re_BufSz + 1 ! RefHt - Re_BufSz = Re_BufSz + 1 ! URef - Re_BufSz = Re_BufSz + 1 ! PLExp - Re_BufSz = Re_BufSz + 1 ! Z0 - Re_BufSz = Re_BufSz + SIZE(InData%InitPosition) ! InitPosition + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + Int_BufSz = Int_BufSz + 3 ! FF: size of buffers for each call to pack subtype + CALL IfW_FFWind_PackInitInput( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, .TRUE. ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FF + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FF + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FF + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -249,36 +232,40 @@ SUBROUTINE IfW_HAWCWind_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSt Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = InData%nz Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%ScaleMethod - Int_Xferred = Int_Xferred + 1 - DO i1 = LBOUND(InData%SF,1), UBOUND(InData%SF,1) - ReKiBuf(Re_Xferred) = InData%SF(i1) - Re_Xferred = Re_Xferred + 1 - END DO - DO i1 = LBOUND(InData%SigmaF,1), UBOUND(InData%SigmaF,1) - ReKiBuf(Re_Xferred) = InData%SigmaF(i1) - Re_Xferred = Re_Xferred + 1 - END DO ReKiBuf(Re_Xferred) = InData%dx Re_Xferred = Re_Xferred + 1 ReKiBuf(Re_Xferred) = InData%dy Re_Xferred = Re_Xferred + 1 ReKiBuf(Re_Xferred) = InData%dz Re_Xferred = Re_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%WindProfileType - Int_Xferred = Int_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%RefHt - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%URef - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%PLExp - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%Z0 - Re_Xferred = Re_Xferred + 1 - DO i1 = LBOUND(InData%InitPosition,1), UBOUND(InData%InitPosition,1) - ReKiBuf(Re_Xferred) = InData%InitPosition(i1) - Re_Xferred = Re_Xferred + 1 - END DO + CALL IfW_FFWind_PackInitInput( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, OnlySize ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Re_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Re_Buf) > 0) ReKiBuf( Re_Xferred:Re_Xferred+SIZE(Re_Buf)-1 ) = Re_Buf + Re_Xferred = Re_Xferred + SIZE(Re_Buf) + DEALLOCATE(Re_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Db_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Db_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Db_Buf) > 0) DbKiBuf( Db_Xferred:Db_Xferred+SIZE(Db_Buf)-1 ) = Db_Buf + Db_Xferred = Db_Xferred + SIZE(Db_Buf) + DEALLOCATE(Db_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Int_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Int_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Int_Buf) > 0) IntKiBuf( Int_Xferred:Int_Xferred+SIZE(Int_Buf)-1 ) = Int_Buf + Int_Xferred = Int_Xferred + SIZE(Int_Buf) + DEALLOCATE(Int_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF END SUBROUTINE IfW_HAWCWind_PackInitInput SUBROUTINE IfW_HAWCWind_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -296,8 +283,6 @@ SUBROUTINE IfW_HAWCWind_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, Er INTEGER(IntKi) :: i INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_HAWCWind_UnPackInitInput' @@ -327,42 +312,52 @@ SUBROUTINE IfW_HAWCWind_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, Er Int_Xferred = Int_Xferred + 1 OutData%nz = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 - OutData%ScaleMethod = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - i1_l = LBOUND(OutData%SF,1) - i1_u = UBOUND(OutData%SF,1) - DO i1 = LBOUND(OutData%SF,1), UBOUND(OutData%SF,1) - OutData%SF(i1) = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - END DO - i1_l = LBOUND(OutData%SigmaF,1) - i1_u = UBOUND(OutData%SigmaF,1) - DO i1 = LBOUND(OutData%SigmaF,1), UBOUND(OutData%SigmaF,1) - OutData%SigmaF(i1) = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - END DO OutData%dx = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 OutData%dy = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 OutData%dz = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 - OutData%WindProfileType = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%RefHt = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%URef = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%PLExp = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%Z0 = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - i1_l = LBOUND(OutData%InitPosition,1) - i1_u = UBOUND(OutData%InitPosition,1) - DO i1 = LBOUND(OutData%InitPosition,1), UBOUND(OutData%InitPosition,1) - OutData%InitPosition(i1) = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - END DO + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Re_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Re_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Re_Buf = ReKiBuf( Re_Xferred:Re_Xferred+Buf_size-1 ) + Re_Xferred = Re_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Db_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Db_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Db_Buf = DbKiBuf( Db_Xferred:Db_Xferred+Buf_size-1 ) + Db_Xferred = Db_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Int_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Int_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) + Int_Xferred = Int_Xferred + Buf_size + END IF + CALL IfW_FFWind_UnpackInitInput( Re_Buf, Db_Buf, Int_Buf, OutData%FF, ErrStat2, ErrMsg2 ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) + IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) + IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) END SUBROUTINE IfW_HAWCWind_UnPackInitInput SUBROUTINE IfW_HAWCWind_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -1221,46 +1216,15 @@ SUBROUTINE IfW_HAWCWind_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat CHARACTER(*), INTENT( OUT) :: ErrMsg ! Local INTEGER(IntKi) :: i,j,k - INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_HAWCWind_CopyParam' ! ErrStat = ErrID_None ErrMsg = "" - DstParamData%nx = SrcParamData%nx - DstParamData%ny = SrcParamData%ny - DstParamData%nz = SrcParamData%nz - DstParamData%RefHt = SrcParamData%RefHt -IF (ALLOCATED(SrcParamData%HAWCData)) THEN - i1_l = LBOUND(SrcParamData%HAWCData,1) - i1_u = UBOUND(SrcParamData%HAWCData,1) - i2_l = LBOUND(SrcParamData%HAWCData,2) - i2_u = UBOUND(SrcParamData%HAWCData,2) - i3_l = LBOUND(SrcParamData%HAWCData,3) - i3_u = UBOUND(SrcParamData%HAWCData,3) - i4_l = LBOUND(SrcParamData%HAWCData,4) - i4_u = UBOUND(SrcParamData%HAWCData,4) - IF (.NOT. ALLOCATED(DstParamData%HAWCData)) THEN - ALLOCATE(DstParamData%HAWCData(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u,i4_l:i4_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%HAWCData.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstParamData%HAWCData = SrcParamData%HAWCData -ENDIF - DstParamData%InitPosition = SrcParamData%InitPosition - DstParamData%GridBase = SrcParamData%GridBase - DstParamData%LengthX = SrcParamData%LengthX - DstParamData%LengthYHalf = SrcParamData%LengthYHalf - DstParamData%deltaXInv = SrcParamData%deltaXInv - DstParamData%deltaYInv = SrcParamData%deltaYInv - DstParamData%deltaZInv = SrcParamData%deltaZInv - DstParamData%URef = SrcParamData%URef + CALL IfW_FFWind_CopyParam( SrcParamData%FF, DstParamData%FF, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE IfW_HAWCWind_CopyParam SUBROUTINE IfW_HAWCWind_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -1272,9 +1236,7 @@ SUBROUTINE IfW_HAWCWind_DestroyParam( ParamData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" -IF (ALLOCATED(ParamData%HAWCData)) THEN - DEALLOCATE(ParamData%HAWCData) -ENDIF + CALL IfW_FFWind_DestroyParam( ParamData%FF, ErrStat, ErrMsg ) END SUBROUTINE IfW_HAWCWind_DestroyParam SUBROUTINE IfW_HAWCWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -1312,23 +1274,24 @@ SUBROUTINE IfW_HAWCWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Re_BufSz = 0 Db_BufSz = 0 Int_BufSz = 0 - Int_BufSz = Int_BufSz + 1 ! nx - Int_BufSz = Int_BufSz + 1 ! ny - Int_BufSz = Int_BufSz + 1 ! nz - Re_BufSz = Re_BufSz + 1 ! RefHt - Int_BufSz = Int_BufSz + 1 ! HAWCData allocated yes/no - IF ( ALLOCATED(InData%HAWCData) ) THEN - Int_BufSz = Int_BufSz + 2*4 ! HAWCData upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%HAWCData) ! HAWCData - END IF - Re_BufSz = Re_BufSz + SIZE(InData%InitPosition) ! InitPosition - Re_BufSz = Re_BufSz + 1 ! GridBase - Re_BufSz = Re_BufSz + 1 ! LengthX - Re_BufSz = Re_BufSz + 1 ! LengthYHalf - Re_BufSz = Re_BufSz + 1 ! deltaXInv - Re_BufSz = Re_BufSz + 1 ! deltaYInv - Re_BufSz = Re_BufSz + 1 ! deltaZInv - Re_BufSz = Re_BufSz + 1 ! URef + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + Int_BufSz = Int_BufSz + 3 ! FF: size of buffers for each call to pack subtype + CALL IfW_FFWind_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, .TRUE. ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FF + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FF + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FF + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -1356,62 +1319,34 @@ SUBROUTINE IfW_HAWCWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Db_Xferred = 1 Int_Xferred = 1 - IntKiBuf(Int_Xferred) = InData%nx - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%ny - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%nz - Int_Xferred = Int_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%RefHt - Re_Xferred = Re_Xferred + 1 - IF ( .NOT. ALLOCATED(InData%HAWCData) ) THEN - IntKiBuf( Int_Xferred ) = 0 - Int_Xferred = Int_Xferred + 1 - ELSE - IntKiBuf( Int_Xferred ) = 1 - Int_Xferred = Int_Xferred + 1 - IntKiBuf( Int_Xferred ) = LBOUND(InData%HAWCData,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%HAWCData,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%HAWCData,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%HAWCData,2) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%HAWCData,3) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%HAWCData,3) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%HAWCData,4) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%HAWCData,4) - Int_Xferred = Int_Xferred + 2 + CALL IfW_FFWind_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, OnlySize ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN - DO i4 = LBOUND(InData%HAWCData,4), UBOUND(InData%HAWCData,4) - DO i3 = LBOUND(InData%HAWCData,3), UBOUND(InData%HAWCData,3) - DO i2 = LBOUND(InData%HAWCData,2), UBOUND(InData%HAWCData,2) - DO i1 = LBOUND(InData%HAWCData,1), UBOUND(InData%HAWCData,1) - ReKiBuf(Re_Xferred) = InData%HAWCData(i1,i2,i3,i4) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END DO - END IF - DO i1 = LBOUND(InData%InitPosition,1), UBOUND(InData%InitPosition,1) - ReKiBuf(Re_Xferred) = InData%InitPosition(i1) - Re_Xferred = Re_Xferred + 1 - END DO - ReKiBuf(Re_Xferred) = InData%GridBase - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%LengthX - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%LengthYHalf - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%deltaXInv - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%deltaYInv - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%deltaZInv - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%URef - Re_Xferred = Re_Xferred + 1 + IF(ALLOCATED(Re_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Re_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Re_Buf) > 0) ReKiBuf( Re_Xferred:Re_Xferred+SIZE(Re_Buf)-1 ) = Re_Buf + Re_Xferred = Re_Xferred + SIZE(Re_Buf) + DEALLOCATE(Re_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Db_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Db_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Db_Buf) > 0) DbKiBuf( Db_Xferred:Db_Xferred+SIZE(Db_Buf)-1 ) = Db_Buf + Db_Xferred = Db_Xferred + SIZE(Db_Buf) + DEALLOCATE(Db_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Int_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Int_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Int_Buf) > 0) IntKiBuf( Int_Xferred:Int_Xferred+SIZE(Int_Buf)-1 ) = Int_Buf + Int_Xferred = Int_Xferred + SIZE(Int_Buf) + DEALLOCATE(Int_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF END SUBROUTINE IfW_HAWCWind_PackParam SUBROUTINE IfW_HAWCWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -1427,10 +1362,6 @@ SUBROUTINE IfW_HAWCWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSta INTEGER(IntKi) :: Db_Xferred INTEGER(IntKi) :: Int_Xferred INTEGER(IntKi) :: i - INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_HAWCWind_UnPackParam' @@ -1444,67 +1375,46 @@ SUBROUTINE IfW_HAWCWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSta Re_Xferred = 1 Db_Xferred = 1 Int_Xferred = 1 - OutData%nx = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%ny = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%nz = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%RefHt = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! HAWCData not allocated - Int_Xferred = Int_Xferred + 1 - ELSE - Int_Xferred = Int_Xferred + 1 - i1_l = IntKiBuf( Int_Xferred ) - i1_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i2_l = IntKiBuf( Int_Xferred ) - i2_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i3_l = IntKiBuf( Int_Xferred ) - i3_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i4_l = IntKiBuf( Int_Xferred ) - i4_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - IF (ALLOCATED(OutData%HAWCData)) DEALLOCATE(OutData%HAWCData) - ALLOCATE(OutData%HAWCData(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u,i4_l:i4_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%HAWCData.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - DO i4 = LBOUND(OutData%HAWCData,4), UBOUND(OutData%HAWCData,4) - DO i3 = LBOUND(OutData%HAWCData,3), UBOUND(OutData%HAWCData,3) - DO i2 = LBOUND(OutData%HAWCData,2), UBOUND(OutData%HAWCData,2) - DO i1 = LBOUND(OutData%HAWCData,1), UBOUND(OutData%HAWCData,1) - OutData%HAWCData(i1,i2,i3,i4) = REAL(ReKiBuf(Re_Xferred), SiKi) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END DO - END IF - i1_l = LBOUND(OutData%InitPosition,1) - i1_u = UBOUND(OutData%InitPosition,1) - DO i1 = LBOUND(OutData%InitPosition,1), UBOUND(OutData%InitPosition,1) - OutData%InitPosition(i1) = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - END DO - OutData%GridBase = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%LengthX = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%LengthYHalf = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%deltaXInv = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%deltaYInv = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%deltaZInv = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%URef = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Re_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Re_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Re_Buf = ReKiBuf( Re_Xferred:Re_Xferred+Buf_size-1 ) + Re_Xferred = Re_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Db_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Db_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Db_Buf = DbKiBuf( Db_Xferred:Db_Xferred+Buf_size-1 ) + Db_Xferred = Db_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Int_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Int_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) + Int_Xferred = Int_Xferred + Buf_size + END IF + CALL IfW_FFWind_UnpackParam( Re_Buf, Db_Buf, Int_Buf, OutData%FF, ErrStat2, ErrMsg2 ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) + IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) + IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) END SUBROUTINE IfW_HAWCWind_UnPackParam SUBROUTINE IfW_HAWCWind_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/inflowwind/src/IfW_TSFFWind.f90 b/modules/inflowwind/src/IfW_TSFFWind.f90 index 56872d883c..6e400323d7 100644 --- a/modules/inflowwind/src/IfW_TSFFWind.f90 +++ b/modules/inflowwind/src/IfW_TSFFWind.f90 @@ -2,7 +2,7 @@ !! This module assumes that the origin, (0,0,0), is located at the tower centerline at ground level, !! and that all units are specified in the metric system (using meters and seconds). !! Data is shifted by half the grid width to account for turbine yaw (so that data in the X -!! direction actually starts at -1*ParamData%FFYHWid meters). +!! direction actually starts at -1*ParamData%FF%FFYHWid meters). MODULE IfW_TSFFWind !! !! Created 25-Sep-2009 by B. Jonkman, National Renewable Energy Laboratory @@ -36,6 +36,7 @@ MODULE IfW_TSFFWind USE NWTC_Library USE IfW_TSFFWind_Types + USE IfW_FFWind_Base IMPLICIT NONE PRIVATE @@ -55,7 +56,7 @@ MODULE IfW_TSFFWind !! 09/25/1997 - Created by M. Buhl from GETFILES in ViewWind. !! 09/23/2009 - modified by B. Jonkman: this subroutine was split into several subroutines (was ReadFF) !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 -SUBROUTINE IfW_TSFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOutData, ErrStat, ErrMsg) +SUBROUTINE IfW_TSFFWind_Init(InitData, ParamData, MiscVars, InitOutData, ErrStat, ErrMsg) IMPLICIT NONE @@ -68,8 +69,6 @@ SUBROUTINE IfW_TSFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOutDat TYPE(IfW_TSFFWind_MiscVarType), INTENT( OUT) :: MiscVars !< Misc variables for optimization (not copied in glue code) TYPE(IfW_TSFFWind_InitOutputType), INTENT( OUT) :: InitOutData !< Initial output - REAL(DbKi), INTENT(IN ) :: Interval !< Time Interval to use (passed through here) - ! Error Handling INTEGER(IntKi), INTENT( OUT) :: ErrStat !< determines if an error has been encountered @@ -93,9 +92,8 @@ SUBROUTINE IfW_TSFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOutDat ErrMsg = '' ErrStat = ErrID_None - TmpErrMsg = '' - TmpErrStat = ErrID_None - + ParamData%FF%InterpTower = .false. + ParamData%FF%AddMeanAfterInterp = .false. ! Get a unit number to use @@ -109,7 +107,6 @@ SUBROUTINE IfW_TSFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOutDat ! Copy things from the InitData to the ParamData !------------------------------------------------------------------------------------------------- - !---------------------------------------------------------------------------------------------- ! Open the binary file, read its "header" (first 2-byte integer) to determine what format ! binary file it is, and close it. @@ -134,11 +131,12 @@ SUBROUTINE IfW_TSFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOutDat !---------------------------------------------------------------------------------------------- ! Read the files to get the required FF data. !---------------------------------------------------------------------------------------------- + ! Store the binary format information so the InflowWind code can use it. ! Also changes to IntKi from INT(2) to compare in the SELECT below - ParamData%WindFileFormat = Dum_Int2 + ParamData%FF%WindFileFormat = Dum_Int2 - SELECT CASE (ParamData%WindFileFormat) + SELECT CASE (ParamData%FF%WindFileFormat) CASE ( 7, 8 ) ! TurbSim binary format @@ -152,19 +150,19 @@ SUBROUTINE IfW_TSFFWind_Init(InitData, ParamData, MiscVars, Interval, InitOutDat CASE DEFAULT CALL SetErrStat( ErrID_Fatal, ' Error: This is not a TurbSim binary wind file type (binary format identifier: '// & - TRIM(Num2LStr(ParamData%WindFileFormat))//'. This might be a Bladed style binary wind file.', & + TRIM(Num2LStr(ParamData%FF%WindFileFormat))//'. This might be a Bladed style binary wind file.', & ErrStat, ErrMsg, RoutineName ) RETURN END SELECT - IF (ParamData%Periodic) THEN - ParamData%InitXPosition = 0 ! start at the hub - ParamData%TotalTime = ParamData%NFFSteps*ParamData%FFDTime + IF (ParamData%FF%Periodic) THEN + ParamData%FF%InitXPosition = 0 ! start at the hub + ParamData%FF%TotalTime = ParamData%FF%NFFSteps*ParamData%FF%FFDTime ELSE - ParamData%InitXPosition = ParamData%FFYHWid ! start half the grid with ahead of the turbine - ParamData%TotalTime = (ParamData%NFFSteps-1)*ParamData%FFDTime + ParamData%FF%InitXPosition = ParamData%FF%FFYHWid ! start half the grid width ahead of the turbine + ParamData%FF%TotalTime = (ParamData%FF%NFFSteps-1)*ParamData%FF%FFDTime ENDIF @@ -223,7 +221,7 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message - ParamData%NFFComp = 3 ! this file contains 3 wind components + ParamData%FF%NFFComp = 3 ! this file contains 3 wind components ErrStat = ErrID_None ErrMsg = "" @@ -244,7 +242,7 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading the file identifier in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%Periodic = Dum_Int2 == INT( 8, B2Ki) ! the number 7 is used for non-periodic wind files; 8 is periodic wind + ParamData%FF%Periodic = Dum_Int2 == INT( 8, B2Ki) ! the number 7 is used for non-periodic wind files; 8 is periodic wind ! Read in the 4-byte integer. Can't use library read routines for this. @@ -253,7 +251,7 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading the number of z grid points in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%NZGrids = Dum_Int4 + ParamData%FF%NZGrids = Dum_Int4 ! Read in the 4-byte integer. Can't use library read routines for this. @@ -262,7 +260,7 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading the number of y grid points in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%NYGrids = Dum_Int4 + ParamData%FF%NYGrids = Dum_Int4 ! Read in the 4-byte integer. Can't use library read routines for this. @@ -271,7 +269,7 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading the number of tower points in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%NTGrids = Dum_Int4 + ParamData%FF%NTGrids = Dum_Int4 ! Read in the 4-byte integer. Can't use library read routines for this. @@ -280,7 +278,7 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading the number of time steps in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%NFFSteps = Dum_Int4 + ParamData%FF%NFFSteps = Dum_Int4 ! Read in the 4-byte real. Can't use library read routines for this. @@ -289,8 +287,8 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading dz in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%InvFFZD = 1.0/Dum_Real4 ! 1/dz - ParamData%FFZHWid = 0.5*(ParamData%NZGrids-1)*Dum_Real4 ! half the grid height + ParamData%FF%InvFFZD = 1.0/Dum_Real4 ! 1/dz + ParamData%FF%FFZHWid = 0.5*(ParamData%FF%NZGrids-1)*Dum_Real4 ! half the grid height ! Read in the 4-byte real. Can't use library read routines for this. @@ -299,8 +297,8 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading dy in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%InvFFYD = 1.0 / Dum_Real4 ! 1/dy - ParamData%FFYHWid = 0.5*(ParamData%NYGrids-1)*Dum_Real4 ! half grid grid width + ParamData%FF%InvFFYD = 1.0 / Dum_Real4 ! 1/dy + ParamData%FF%FFYHWid = 0.5*(ParamData%FF%NYGrids-1)*Dum_Real4 ! half grid grid width ! Read in the 4-byte real. Can't use library read routines for this. @@ -309,8 +307,8 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading dt in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%FFDTime = Dum_Real4 - ParamData%FFRate = 1.0/ParamData%FFDTime + ParamData%FF%FFDTime = Dum_Real4 + ParamData%FF%FFRate = 1.0/ParamData%FF%FFDTime ! Read in the 4-byte real. Can't use library read routines for this. @@ -319,8 +317,8 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading mean wind speed in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%MeanFFWS = Dum_Real4 - ParamData%InvMFFWS = 1.0 / ParamData%MeanFFWS + ParamData%FF%MeanFFWS = Dum_Real4 + ParamData%FF%InvMFFWS = 1.0 / ParamData%FF%MeanFFWS ! Read in the 4-byte real. Can't use library read routines for this. @@ -329,7 +327,7 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading zHub in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%RefHt = Dum_Real4 + ParamData%FF%RefHt = Dum_Real4 ! Read in the 4-byte real. Can't use library read routines for this. @@ -338,16 +336,16 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CALL SetErrStat( ErrID_Fatal, ' Error reading GridBase in the FF binary file "'//TRIM( InitData%WindFileName )//'."', ErrStat, ErrMsg, RoutineName ) RETURN ENDIF - ParamData%GridBase = Dum_Real4 + ParamData%FF%GridBase = Dum_Real4 - ! ZGOffset = ParamData%RefHt - ParamData%GridBase - ParamData%FFZHWid + ! ZGOffset = ParamData%FF%RefHt - ParamData%FF%GridBase - ParamData%FF%FFZHWid !---------------------------------------------------------------------------------------------- ! Read the binary scaling factors !---------------------------------------------------------------------------------------------- - DO IC = 1,ParamData%NFFComp + DO IC = 1,ParamData%FF%NFFComp ! Read in the 4-byte real. Can't use library read routines for this. READ (UnitWind, IOSTAT=TmpErrStat) Vslope(IC) ! the IC-component slope for scaling, REAL(4) IF ( TmpErrStat /= 0 ) THEN @@ -406,32 +404,30 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) ! this could take a while, so we'll write a message indicating what's going on: - CALL WrScr( NewLine//' Reading a '//TRIM( Num2LStr(ParamData%NYGrids) )//'x'//TRIM( Num2LStr(ParamData%NZGrids) )// & - ' grid ('//TRIM( Num2LStr(ParamData%FFYHWid*2) )//' m wide, '// & - TRIM( Num2LStr(ParamData%GridBase) )//' m to '// & - TRIM( Num2LStr(ParamData%GridBase+ParamData%FFZHWid*2) )//& + CALL WrScr( NewLine//' Reading a '//TRIM( Num2LStr(ParamData%FF%NYGrids) )//'x'//TRIM( Num2LStr(ParamData%FF%NZGrids) )// & + ' grid ('//TRIM( Num2LStr(ParamData%FF%FFYHWid*2) )//' m wide, '// & + TRIM( Num2LStr(ParamData%FF%GridBase) )//' m to '// & + TRIM( Num2LStr(ParamData%FF%GridBase+ParamData%FF%FFZHWid*2) )//& ' m above ground) with a characteristic wind speed of '// & - TRIM( Num2LStr(ParamData%MeanFFWS) )//' m/s. '//TRIM(DescStr) ) + TRIM( Num2LStr(ParamData%FF%MeanFFWS) )//' m/s. '//TRIM(DescStr) ) !---------------------------------------------------------------------------------------------- ! Allocate arrays for the FF grid as well as the tower points, if they exist !---------------------------------------------------------------------------------------------- - IF ( .NOT. ALLOCATED( ParamData%FFData ) ) THEN - CALL AllocAry( ParamData%FFData, ParamData%NZGrids, ParamData%NYGrids, ParamData%NFFComp, ParamData%NFFSteps, & + IF ( .NOT. ALLOCATED( ParamData%FF%FFData ) ) THEN + CALL AllocAry( ParamData%FF%FFData, ParamData%FF%NZGrids, ParamData%FF%NYGrids, ParamData%FF%NFFComp, ParamData%FF%NFFSteps, & 'Full-field wind data array.', TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) RETURN ENDIF - IF ( ParamData%NTGrids > 0 ) THEN - - ParamData%TowerDataExist = .TRUE. + IF ( ParamData%FF%NTGrids > 0 ) THEN - IF ( .NOT. ALLOCATED( ParamData%FFTower ) ) THEN - CALL AllocAry( ParamData%FFTower, ParamData%NFFComp, ParamData%NTGrids, ParamData%NFFSteps, & + IF ( .NOT. ALLOCATED( ParamData%FF%FFTower ) ) THEN + CALL AllocAry( ParamData%FF%FFTower, ParamData%FF%NFFComp, ParamData%FF%NTGrids, ParamData%FF%NFFSteps, & 'Tower wind file data array.', TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) RETURN @@ -445,19 +441,19 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) ! Loop through time. - DO IT=1,ParamData%NFFSteps + DO IT=1,ParamData%FF%NFFSteps !........................................................................................... ! Read grid data at this time step. !........................................................................................... - DO IZ=1,ParamData%NZGrids + DO IZ=1,ParamData%FF%NZGrids ! Zgrid(IZ) = Z1 + (IZ-1)*dz ! Vertical location of grid data point, in m relative to ground - DO IY=1,ParamData%NYGrids + DO IY=1,ParamData%FF%NYGrids ! Ygrid(IY) = -0.5*(ny-1)*dy + (IY-1)*dy ! Horizontal location of grid data point, in m relative to tower centerline - DO IC=1,ParamData%NFFComp ! number of wind components (U, V, W) + DO IC=1,ParamData%FF%NFFComp ! number of wind components (U, V, W) ! Read in the 2-byte integer. Can't use library read routines for this. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Int2 ! normalized wind-component, INT(2) @@ -467,7 +463,7 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) RETURN ENDIF - ParamData%FFData(IZ,IY,IC,IT) = ( Dum_Int2 - Voffset(IC) ) / VSlope(IC) + ParamData%FF%FFData(IZ,IY,IC,IT) = ( Dum_Int2 - Voffset(IC) ) / VSlope(IC) ENDDO !IC @@ -480,12 +476,12 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) ! Read the tower data at this time step. !........................................................................................... - DO IZ=1,ParamData%NTGrids ! If NTGrids<1, there are no tower points & FFTower is not allocated + DO IZ=1,ParamData%FF%NTGrids ! If NTGrids<1, there are no tower points & FFTower is not allocated ! Ytower = 0 ! Lateral location of the tower data point, in m relative to tower centerline ! Ztower(IZ) = Z1 - (IZ-1)*dz ! Vertical location of tower data point, in m relative to ground - DO IC=1,ParamData%NFFComp ! number of wind components + DO IC=1,ParamData%FF%NFFComp ! number of wind components ! Read in a 2-byte integer. Can't use library routines for this. READ (UnitWind, IOSTAT=TmpErrStat) Dum_Int2 ! normalized wind-component, INT(2) @@ -495,7 +491,7 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) RETURN ENDIF - ParamData%FFTower(IC,IZ,IT) = ( Dum_Int2 - Voffset(IC) ) / VSlope(IC) ! wind-component scaled to m/s + ParamData%FF%FFTower(IC,IZ,IT) = ( Dum_Int2 - Voffset(IC) ) / VSlope(IC) ! wind-component scaled to m/s ENDDO !IC @@ -510,14 +506,14 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) CLOSE ( UnitWind ) - IF ( ParamData%Periodic ) THEN - TmpErrMsg = ' Processed '//TRIM( Num2LStr( ParamData%NFFSteps ) )//' time steps of '// & - TRIM( Num2LStr ( ParamData%FFRate ) )//'-Hz full-field data (period of '// & - TRIM( Num2LStr( ParamData%FFDTime*( ParamData%NFFSteps ) ) )//' seconds).' + IF ( ParamData%FF%Periodic ) THEN + TmpErrMsg = ' Processed '//TRIM( Num2LStr( ParamData%FF%NFFSteps ) )//' time steps of '// & + TRIM( Num2LStr ( ParamData%FF%FFRate ) )//'-Hz full-field data (period of '// & + TRIM( Num2LStr( ParamData%FF%FFDTime*( ParamData%FF%NFFSteps ) ) )//' seconds).' ELSE - TmpErrMsg = ' Processed '//TRIM( Num2LStr( ParamData%NFFSteps ) )//' time steps of '// & - TRIM( Num2LStr ( ParamData%FFRate ) )//'-Hz full-field data ('// & - TRIM( Num2LStr( ParamData%FFDTime*( ParamData%NFFSteps - 1 ) ) )//' seconds).' + TmpErrMsg = ' Processed '//TRIM( Num2LStr( ParamData%FF%NFFSteps ) )//' time steps of '// & + TRIM( Num2LStr ( ParamData%FF%FFRate ) )//'-Hz full-field data ('// & + TRIM( Num2LStr( ParamData%FF%FFDTime*( ParamData%FF%NFFSteps - 1 ) ) )//' seconds).' ENDIF CALL WrScr( NewLine//TRIM(TmpErrMsg) ) ! Note: the TmpErrMsg gets used below for the summary file @@ -534,32 +530,32 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) ' '//TRIM(IfW_TSFFWind_Ver%Ver) WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) TRIM(TmpErrMsg) WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' FileName: '//TRIM(InitData%WindFileName) - WRITE(InitData%SumFileUnit,'(A34,I3)', IOSTAT=TmpErrStat) ' Binary file format id: ',ParamData%WindFileFormat - WRITE(InitData%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Reference height (m): ',ParamData%RefHt - WRITE(InitData%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Timestep (s): ',ParamData%FFDTime - WRITE(InitData%SumFileUnit,'(A34,I12)', IOSTAT=TmpErrStat) ' Number of timesteps: ',ParamData%NFFSteps - WRITE(InitData%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Mean windspeed (m/s): ',ParamData%MeanFFWS - WRITE(InitData%SumFileUnit,'(A34,L1)', IOSTAT=TmpErrStat) ' Windfile is periodic: ',ParamData%Periodic - WRITE(InitData%SumFileUnit,'(A34,L1)', IOSTAT=TmpErrStat) ' Windfile includes tower: ',ParamData%TowerDataExist - - IF ( ParamData%Periodic ) THEN + WRITE(InitData%SumFileUnit,'(A34,I3)', IOSTAT=TmpErrStat) ' Binary file format id: ',ParamData%FF%WindFileFormat + WRITE(InitData%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Reference height (m): ',ParamData%FF%RefHt + WRITE(InitData%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Timestep (s): ',ParamData%FF%FFDTime + WRITE(InitData%SumFileUnit,'(A34,I12)', IOSTAT=TmpErrStat) ' Number of timesteps: ',ParamData%FF%NFFSteps + WRITE(InitData%SumFileUnit,'(A34,G12.4)',IOSTAT=TmpErrStat) ' Mean windspeed (m/s): ',ParamData%FF%MeanFFWS + WRITE(InitData%SumFileUnit,'(A34,L1)', IOSTAT=TmpErrStat) ' Windfile is periodic: ',ParamData%FF%Periodic + WRITE(InitData%SumFileUnit,'(A34,L1)', IOSTAT=TmpErrStat) ' Windfile includes tower: ',ParamData%FF%NTGrids > 0 + + IF ( ParamData%FF%Periodic ) THEN WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Time range (s): [ '// & - TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr(ParamData%TotalTime))//' ]' + TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr(ParamData%FF%TotalTime))//' ]' ELSE ! Shift the time range to compensate for the shifting of the wind grid WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Time range (s): [ '// & - TRIM(Num2LStr(-ParamData%InitXPosition*ParamData%InvMFFWS))//' : '// & - TRIM(Num2LStr(ParamData%TotalTime-ParamData%InitXPosition*ParamData%InvMFFWS))//' ]' + TRIM(Num2LStr(-ParamData%FF%InitXPosition*ParamData%FF%InvMFFWS))//' : '// & + TRIM(Num2LStr(ParamData%FF%TotalTime-ParamData%FF%InitXPosition*ParamData%FF%InvMFFWS))//' ]' ENDIF WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Y range (m): [ '// & - TRIM(Num2LStr(-ParamData%FFYHWid))//' : '//TRIM(Num2LStr(ParamData%FFYHWid))//' ]' + TRIM(Num2LStr(-ParamData%FF%FFYHWid))//' : '//TRIM(Num2LStr(ParamData%FF%FFYHWid))//' ]' - IF ( ParamData%TowerDataExist ) THEN + IF ( ParamData%FF%NTGrids > 0 ) THEN WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Z range (m): [ '// & - TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr(ParamData%RefHt + ParamData%FFZHWid))//' ]' + TRIM(Num2LStr(0.0_ReKi))//' : '//TRIM(Num2LStr(ParamData%FF%RefHt + ParamData%FF%FFZHWid))//' ]' ELSE WRITE(InitData%SumFileUnit,'(A)', IOSTAT=TmpErrStat) ' Z range (m): [ '// & - TRIM(Num2LStr(ParamData%RefHt - ParamData%FFZHWid))//' : '//TRIM(Num2LStr(ParamData%RefHt + ParamData%FFZHWid))//' ]' + TRIM(Num2LStr(ParamData%FF%RefHt - ParamData%FF%FFZHWid))//' : '//TRIM(Num2LStr(ParamData%FF%RefHt + ParamData%FF%FFZHWid))//' ]' ENDIF @@ -571,26 +567,17 @@ SUBROUTINE Read_TurbSim_FF(UnitWind, ErrStat, ErrMsg) ENDIF - - - - RETURN + RETURN END SUBROUTINE READ_TurbSim_FF -END SUBROUTINE IfW_TSFFWind_Init + END SUBROUTINE IfW_TSFFWind_Init !==================================================================================================== !==================================================================================================== -!> This routine acts as a wrapper for the GetWindSpeed routine. It steps through the array of input -!! positions and calls the GetWindSpeed routine to calculate the velocities at each point. -!! -!! There are inefficiencies in how this set of routines is coded, but that is a problem for another -!! day. For now, it merely needs to be functional. It can be fixed up and made all pretty later. -!! -!! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 -SUBROUTINE IfW_TSFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, DiskVel, MiscVars, ErrStat, ErrMsg) +!> This routine computes the wind speed at each of the PositionXYZ points. +SUBROUTINE IfW_TSFFWind_CalcOutput(Time, PositionXYZ, p, Velocity, DiskVel, MiscVars, ErrStat, ErrMsg) IMPLICIT NONE @@ -601,7 +588,7 @@ SUBROUTINE IfW_TSFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, Disk ! Passed Variables REAL(DbKi), INTENT(IN ) :: Time !< time from the start of the simulation REAL(ReKi), INTENT(IN ) :: PositionXYZ(:,:) !< Array of XYZ coordinates, 3xN - TYPE(IfW_TSFFWind_ParameterType), INTENT(IN ) :: ParamData !< Parameters + TYPE(IfW_TSFFWind_ParameterType), INTENT(IN ) :: p !< Parameters REAL(ReKi), INTENT(INOUT) :: Velocity(:,:) !< Velocity output at Time (Set to INOUT so that array does not get deallocated) REAL(ReKi), INTENT( OUT) :: DiskVel(3) !< HACK for AD14: disk velocity output at Time TYPE(IfW_TSFFWind_MiscVarType), INTENT(INOUT) :: MiscVars !< Misc variables for optimization (not copied in glue code) @@ -610,371 +597,20 @@ SUBROUTINE IfW_TSFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, Disk INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status CHARACTER(*), INTENT( OUT) :: ErrMsg !< The error message - ! local variables - INTEGER(IntKi) :: NumPoints ! Number of points specified by the PositionXYZ array - - ! local counters - INTEGER(IntKi) :: PointNum ! a loop counter for the current point - - ! temporary variables - INTEGER(IntKi) :: TmpErrStat ! temporary error status - CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message - - - - !------------------------------------------------------------------------------------------------- - ! Check that the module has been initialized. - !------------------------------------------------------------------------------------------------- - - ErrStat = ErrID_None - ErrMsg = '' - - !------------------------------------------------------------------------------------------------- - ! Initialize some things - !------------------------------------------------------------------------------------------------- - - - ! The array is transposed so that the number of points is the second index, x/y/z is the first. - ! This is just in case we only have a single point, the SIZE command returns the correct number of points. - NumPoints = SIZE(PositionXYZ,2) - ! Step through all the positions and get the velocities - DO PointNum = 1, NumPoints - - ! Calculate the velocity for the position - Velocity(:,PointNum) = FF_Interp(Time,PositionXYZ(:,PointNum),ParamData,MiscVars,TmpErrStat,TmpErrMsg) - - - ! Error handling - IF (TmpErrStat /= ErrID_None) THEN ! adding this so we don't have to convert numbers to strings every time - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName//" [position=("// & - TRIM(Num2LStr(PositionXYZ(1,PointNum)))//", "// & - TRIM(Num2LStr(PositionXYZ(2,PointNum)))//", "// & - TRIM(Num2LStr(PositionXYZ(3,PointNum)))//") in wind-file coordinates]" ) - IF (ErrStat >= AbortErrLev) RETURN - END IF - - ENDDO - - - - !REMOVE THIS for AeroDyn 15 - ! Return the average disk velocity values needed by AeroDyn 14. This is the WindInf_ADhack_diskVel routine. - DiskVel(1) = ParamData%MeanFFWS - DiskVel(2:3) = 0.0_ReKi + CALL IfW_FFWind_CalcOutput(Time, PositionXYZ, p%FF, Velocity, DiskVel, ErrStat, ErrMsg) RETURN END SUBROUTINE IfW_TSFFWind_CalcOutput - !+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - !> This function is used to interpolate into the full-field wind array or tower array if it has - !! been defined and is necessary for the given inputs. It receives X, Y, Z and - !! TIME from the calling routine. It then computes a time shift due to a nonzero X based upon - !! the average windspeed. The modified time is used to decide which pair of time slices to interpolate - !! within and between. After finding the two time slices, it decides which four grid points bound the - !! (Y,Z) pair. It does a bilinear interpolation for each time slice. Linear interpolation is then used - !! to interpolate between time slices. This routine assumes that X is downwind, Y is to the left when - !! looking downwind and Z is up. It also assumes that no extrapolation will be needed. - !! - !! If tower points are used, it assumes the velocity at the ground is 0. It interpolates between - !! heights and between time slices, but ignores the Y input. - !! - !! 11/07/1994 - Created by M. Buhl from the original TURBINT. - !! 09/25/1997 - Modified by M. Buhl to use f90 constructs and new variable names. Renamed to FF_Interp. - !! 09/23/2009 - Modified by B. Jonkman to use arguments instead of modules to determine time and position. - !! Height is now relative to the ground - !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 - FUNCTION FF_Interp(t_in, Position, ParamData, MiscVars, ErrStat, ErrMsg) - !---------------------------------------------------------------------------------------------------- - - IMPLICIT NONE - - CHARACTER(*), PARAMETER :: RoutineName="FF_Interp" - - REAL(DbKi), INTENT(IN ) :: t_in !< input time - REAL(ReKi), INTENT(IN ) :: Position(3) !< takes the place of XGrnd, YGrnd, ZGrnd - TYPE(IfW_TSFFWind_ParameterType), INTENT(IN ) :: ParamData !< Parameters - TYPE(IfW_TSFFWind_MiscVarType), INTENT(INOUT) :: MiscVars !< Misc variables for optimization (not copied in glue code) - REAL(ReKi) :: FF_Interp(3) !< The U, V, W velocities - - INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status - CHARACTER(*), INTENT( OUT) :: ErrMsg !< error message - - ! Local Variables: - - REAL(ReKi) :: TimeShifted - REAL(ReKi),PARAMETER :: Tol = 1.0E-3 ! a tolerance for determining if two reals are the same (for extrapolation) - REAL(ReKi) :: T - REAL(ReKi) :: TGRID - REAL(ReKi) :: Y - REAL(ReKi) :: YGRID - REAL(ReKi) :: Z - REAL(ReKi) :: ZGRID - REAL(ReKi) :: N(8) ! array for holding scaling factors for the interpolation algorithm - REAL(ReKi) :: u(8) ! array for holding the corner values for the interpolation algorithm across a cubic volume - REAL(ReKi) :: M(4) ! array for holding scaling factors for the interpolation algorithm - REAL(ReKi) :: v(4) ! array for holding the corner values for the interpolation algorithm across an area - - INTEGER(IntKi) :: IDIM - INTEGER(IntKi) :: ITHI - INTEGER(IntKi) :: ITLO - INTEGER(IntKi) :: IYHI - INTEGER(IntKi) :: IYLO - INTEGER(IntKi) :: IZHI - INTEGER(IntKi) :: IZLO - - LOGICAL :: OnGrid - - !------------------------------------------------------------------------------------------------- - ! Initialize variables - !------------------------------------------------------------------------------------------------- - - FF_Interp(:) = 0.0_ReKi ! the output velocities (in case ParamData%NFFComp /= 3) - - ErrStat = ErrID_None - ErrMsg = "" - - !------------------------------------------------------------------------------------------------- - ! Find the bounding time slices. - !------------------------------------------------------------------------------------------------- - - ! Perform the time shift. At t_in=0, a point half the grid width downstream (ParamData%FFYHWid) will index into the zero time slice. - ! If we did not do this, any point downstream of the tower at the beginning of the run would index outside of the array. - ! This all assumes the grid width is at least as large as the rotor. If it isn't, then the interpolation will not work. - - - TimeShifted = t_in + ( ParamData%InitXPosition - Position(1) )*ParamData%InvMFFWS ! in distance, X: InputInfo%Position(1) - ParamData%InitXPosition - t*ParamData%MeanFFWS - - - IF ( ParamData%Periodic ) THEN ! translate TimeShifted to ( 0 <= TimeShifted < ParamData%TotalTime ) - - TimeShifted = MODULO( TimeShifted, ParamData%TotalTime ) - ! If TimeShifted is a very small negative number, modulo returns the incorrect value due to internal rounding errors. - ! See bug report #471 - IF (TimeShifted == ParamData%TotalTime) TimeShifted = 0.0_ReKi - - TGRID = TimeShifted*ParamData%FFRate - ITLO = INT( TGRID ) ! convert REAL to INTEGER (add 1 later because our grids start at 1, not 0) - T = 2.0_ReKi * ( TGRID - REAL(ITLO, ReKi) ) - 1.0_ReKi ! a value between -1 and 1 that indicates a relative position between ITLO and ITHI - - ITLO = ITLO + 1 - IF ( ITLO == ParamData%NFFSteps ) THEN - ITHI = 1 - ELSE - ITHI = ITLO + 1 - ENDIF - - - ELSE - - TGRID = TimeShifted*ParamData%FFRate - ITLO = INT( TGRID ) ! convert REAL to INTEGER (add 1 later because our grids start at 1, not 0) - T = 2.0_ReKi * ( TGRID - REAL(ITLO, ReKi) ) - 1.0_ReKi ! a value between -1 and 1 that indicates a relative position between ITLO and ITHI - - ITLO = ITLO + 1 ! add one since our grids start at 1, not 0 - ITHI = ITLO + 1 - - IF ( ITLO >= ParamData%NFFSteps .OR. ITLO < 1 ) THEN - IF ( ITLO == ParamData%NFFSteps ) THEN - ITHI = ITLO - IF ( T <= TOL ) THEN ! we're on the last point - T = -1.0_ReKi - ELSE ! We'll extrapolate one dt past the last value in the file - ITLO = ITHI - 1 - ENDIF - ELSE - ErrMsg = ' Error: FF wind array was exhausted at '//TRIM( Num2LStr( REAL( t_in, ReKi ) ) )// & - ' seconds (trying to access data at '//TRIM( Num2LStr( REAL( TimeShifted, ReKi ) ) )//' seconds).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - ENDIF - - ENDIF - - - !------------------------------------------------------------------------------------------------- - ! Find the bounding rows for the Z position. [The lower-left corner is (1,1) when looking upwind.] - !------------------------------------------------------------------------------------------------- - - ZGRID = ( Position(3) - ParamData%GridBase )*ParamData%InvFFZD - - IF (ZGRID > -1*TOL) THEN - OnGrid = .TRUE. - - ! Index for start and end slices - IZLO = INT( ZGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 - IZHI = IZLO + 1 - - ! Set Z as a value between -1 and 1 for the relative location between IZLO and IZHI. - ! Subtract 1_IntKi from Z since the indices are starting at 1, not 0 - Z = 2.0_ReKi * (ZGRID - REAL(IZLO - 1_IntKi, ReKi)) - 1.0_ReKi - - IF ( IZLO < 1 ) THEN - IF ( IZLO == 0 .AND. Z >= 1.0-TOL ) THEN - Z = -1.0_ReKi - IZLO = 1 - ELSE - ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction (Z='//& - TRIM(Num2LStr(Position(3)))//' m is below the grid).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - ELSEIF ( IZLO >= ParamData%NZGrids ) THEN - IF ( IZLO == ParamData%NZGrids .AND. Z <= TOL ) THEN - Z = -1.0_ReKi - IZHI = IZLO ! We're right on the last point, which is still okay - ELSE - ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction (Z='//& - TRIM(Num2LStr(Position(3)))//' m is above the grid).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - ENDIF - - ELSE - - OnGrid = .FALSE. ! this is on the tower - - IF ( ParamData%NTGrids < 1 ) THEN - ErrMsg = ' FF wind array boundaries violated. Grid too small in Z direction '// & - '(height (Z='//TRIM(Num2LStr(Position(3)))//' m) is below the grid and no tower points are defined).' - ErrStat = ErrID_Fatal - RETURN - ENDIF - - IZLO = INT( -1.0*ZGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 - - - IF ( IZLO >= ParamData%NTGrids ) THEN !our dz is the difference between the bottom tower point and the ground - IZLO = ParamData%NTGrids - - ! Check that this isn't zero. Value between -1 and 1 corresponding to the relative position. - Z = 1.0_ReKi - 2.0_ReKi * (Position(3) / (ParamData%GridBase - REAL(IZLO - 1_IntKi, ReKi)/ParamData%InvFFZD)) - - ELSE - - ! Set Z as a value between -1 and 1 for the relative location between IZLO and IZHI. Used in the interpolation. - Z = 2.0_ReKi * (ABS(ZGRID) - REAL(IZLO - 1_IntKi, ReKi)) - 1.0_ReKi - - ENDIF - IZHI = IZLO + 1 - - ENDIF - - - IF ( OnGrid ) THEN ! The tower points don't use this - - !------------------------------------------------------------------------------------------------- - ! Find the bounding columns for the Y position. [The lower-left corner is (1,1) when looking upwind.] - !------------------------------------------------------------------------------------------------- - - YGRID = ( Position(2) + ParamData%FFYHWid )*ParamData%InvFFYD ! really, it's (Position(2) - -1.0*ParamData%FFYHWid) - - IYLO = INT( YGRID ) + 1 ! convert REAL to INTEGER, then add one since our grids start at 1, not 0 - IYHI = IYLO + 1 - - ! Set Y as a value between -1 and 1 for the relative location between IYLO and IYHI. Used in the interpolation. - ! Subtract 1_IntKi from IYLO since grids start at index 1, not 0 - Y = 2.0_ReKi * (YGRID - REAL(IYLO - 1_IntKi, ReKi)) - 1.0_ReKi - - IF ( IYLO >= ParamData%NYGrids .OR. IYLO < 1 ) THEN - IF ( IYLO == 0 .AND. Y >= 1.0-TOL ) THEN - Y = -1.0_ReKi - IYLO = 1 - ELSE IF ( IYLO == ParamData%NYGrids .AND. Y <= TOL ) THEN - Y = -1.0_ReKi - IYHI = IYLO ! We're right on the last point, which is still okay - ELSE - ErrMsg = ' FF wind array boundaries violated: Grid too small in Y direction. Y='// & - TRIM(Num2LStr(Position(2)))//'; Y boundaries = ['//TRIM(Num2LStr(-1.0*ParamData%FFYHWid))// & - ', '//TRIM(Num2LStr(ParamData%FFYHWid))//']' - ErrStat = ErrID_Fatal ! we don't return anything - RETURN - ENDIF - ENDIF - - !------------------------------------------------------------------------------------------------- - ! Interpolate on the grid - !------------------------------------------------------------------------------------------------- - - DO IDIM=1,ParamData%NFFComp ! all the components - - - N(1) = ( 1.0_ReKi + Z )*( 1.0_ReKi - Y )*( 1.0_ReKi - T ) - N(2) = ( 1.0_ReKi + Z )*( 1.0_ReKi + Y )*( 1.0_ReKi - T ) - N(3) = ( 1.0_ReKi - Z )*( 1.0_ReKi + Y )*( 1.0_ReKi - T ) - N(4) = ( 1.0_ReKi - Z )*( 1.0_ReKi - Y )*( 1.0_ReKi - T ) - N(5) = ( 1.0_ReKi + Z )*( 1.0_ReKi - Y )*( 1.0_ReKi + T ) - N(6) = ( 1.0_ReKi + Z )*( 1.0_ReKi + Y )*( 1.0_ReKi + T ) - N(7) = ( 1.0_ReKi - Z )*( 1.0_ReKi + Y )*( 1.0_ReKi + T ) - N(8) = ( 1.0_ReKi - Z )*( 1.0_ReKi - Y )*( 1.0_ReKi + T ) - N = N / REAL( SIZE(N), ReKi ) ! normalize - - - u(1) = ParamData%FFData( IZHI, IYLO, IDIM, ITLO ) - u(2) = ParamData%FFData( IZHI, IYHI, IDIM, ITLO ) - u(3) = ParamData%FFData( IZLO, IYHI, IDIM, ITLO ) - u(4) = ParamData%FFData( IZLO, IYLO, IDIM, ITLO ) - u(5) = ParamData%FFData( IZHI, IYLO, IDIM, ITHI ) - u(6) = ParamData%FFData( IZHI, IYHI, IDIM, ITHI ) - u(7) = ParamData%FFData( IZLO, IYHI, IDIM, ITHI ) - u(8) = ParamData%FFData( IZLO, IYLO, IDIM, ITHI ) - - FF_Interp(IDIM) = SUM ( N * u ) - - - END DO !IDIM - - ELSE - - !------------------------------------------------------------------------------------------------- - ! Interpolate on the tower array - !------------------------------------------------------------------------------------------------- - - DO IDIM=1,ParamData%NFFComp ! all the components - - !---------------------------------------------------------------------------------------------- - ! Interpolate between the two times using an area interpolation. - !---------------------------------------------------------------------------------------------- - - ! Setup the scaling factors. Set the unused portion of the array to zero - M(1) = ( 1.0_ReKi + Z )*( 1.0_ReKi - T ) - M(2) = ( 1.0_ReKi + Z )*( 1.0_ReKi + T ) - M(3) = ( 1.0_ReKi - Z )*( 1.0_ReKi - T ) - M(4) = ( 1.0_ReKi - Z )*( 1.0_ReKi + T ) - M = M / 4.0_ReKi ! normalize - - IF (IZHI > ParamData%NTGrids) THEN - v(1) = 0.0_ReKi ! on the ground - v(2) = 0.0_ReKi ! on the ground - ELSE - v(1) = ParamData%FFTower( IDIM, IZHI, ITLO ) - v(2) = ParamData%FFTower( IDIM, IZHI, ITHI ) - END IF - - v(3) = ParamData%FFTower( IDIM, IZLO, ITLO ) - v(4) = ParamData%FFTower( IDIM, IZLO, ITHI ) - - FF_Interp(IDIM) = SUM ( M * v ) - - - END DO !IDIM - - ENDIF ! OnGrid - RETURN - - END FUNCTION FF_Interp - - !==================================================================================================== !> This subroutine cleans up any data that is still allocated. The (possibly) open files are !! closed in InflowWindMod. !! !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 -SUBROUTINE IfW_TSFFWind_End( ParamData, MiscVars, ErrStat, ErrMsg) +SUBROUTINE IfW_TSFFWind_End( p, m, ErrStat, ErrMsg) IMPLICIT NONE @@ -983,13 +619,13 @@ SUBROUTINE IfW_TSFFWind_End( ParamData, MiscVars, ErrStat, ErrMsg) ! Passed Variables - TYPE(IfW_TSFFWind_ParameterType), INTENT(INOUT) :: ParamData !< Parameters - TYPE(IfW_TSFFWind_MiscVarType), INTENT(INOUT) :: MiscVars !< Misc variables for optimization (not copied in glue code) + TYPE(IfW_TSFFWind_ParameterType), INTENT(INOUT) :: p !< Parameters + TYPE(IfW_TSFFWind_MiscVarType), INTENT(INOUT) :: m !< Misc variables for optimization (not copied in glue code) ! Error Handling - INTEGER(IntKi), INTENT( OUT) :: ErrStat !< determines if an error has been encountered - CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about errors + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< determines if an error has been encountered + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about errors ! Local Variables @@ -1006,13 +642,13 @@ SUBROUTINE IfW_TSFFWind_End( ParamData, MiscVars, ErrStat, ErrMsg) ! Destroy parameter data - CALL IfW_TSFFWind_DestroyParam( ParamData, TmpErrStat, TmpErrMsg ) + CALL IfW_TSFFWind_DestroyParam( p, TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) ! Destroy the misc data - CALL IfW_TSFFWind_DestroyMisc( MiscVars, TmpErrStat, TmpErrMsg ) + CALL IfW_TSFFWind_DestroyMisc( m, TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) diff --git a/modules/inflowwind/src/IfW_TSFFWind.txt b/modules/inflowwind/src/IfW_TSFFWind.txt index 85289483ac..71b9e9c753 100644 --- a/modules/inflowwind/src/IfW_TSFFWind.txt +++ b/modules/inflowwind/src/IfW_TSFFWind.txt @@ -8,6 +8,7 @@ ################################################################################################################################### include Registry_NWTC_Library.txt +usefrom IfW_FFWind_Base.txt ######################### @@ -23,32 +24,10 @@ typedef ^ InitOutputType ProgDesc Ver # ..... Misc/Optimization variables................................................................................................. # Define any data that are used only for efficiency purposes (these variables are not associated with time): # e.g. indices for searching in an array, large arrays that are local variables in any routine called multiple times, etc. -typedef ^ MiscVarType IntKi TimeIndex - 0 - "An Index into the TData array" - +typedef ^ MiscVarType IntKi dummy - 0 - "An Index into the TData array" - # ..... Parameters ................................................................................................................ # Define parameters here: # Time step for integration of continuous states (if a fixed-step integrator is used) and update of discrete states: -typedef ^ ParameterType DbKi DT - - - "Time step for cont. state integration & disc. state update" seconds -typedef ^ ^ Logical TowerDataExist - .FALSE. - "Data exists for the tower" - -typedef ^ ^ Logical Periodic - .FALSE. - "Flag to indicate if the wind file is periodic" - -typedef ^ ^ SiKi FFData :::: - - "Array of FF data" - -typedef ^ ^ SiKi FFTower ::: - - "Array of data along tower, below FF array" - -typedef ^ ^ ReKi FFDTime - 0 - "Delta time" seconds -typedef ^ ^ ReKi FFRate - 0 - "Data rate (1/FFTime)" Hertz -typedef ^ ^ ReKi FFYHWid - 0 - "Half the grid width" meters -typedef ^ ^ ReKi FFZHWid - 0 - "Half the grid height" meters -typedef ^ ^ ReKi RefHt - 0 - "Reference (hub) height of the grid" meters -typedef ^ ^ ReKi GridBase - 0 - "the height of the bottom of the grid" meters -typedef ^ ^ ReKi InitXPosition - 0 - "the initial x position of grid (distance in FF is offset)" meters -typedef ^ ^ ReKi InvFFYD - 0 - "reciprocal of delta y" 1/meters -typedef ^ ^ ReKi InvFFZD - 0 - "reciprocal of delta z" 1/meters -typedef ^ ^ ReKi InvMFFWS - 0 - "reciprocal of mean wind speed (MeanFFWS)" seconds/meter -typedef ^ ^ ReKi MeanFFWS - 0 - "Mean wind speed (as defined in FF file), not necessarily of the portion used" meters/second -typedef ^ ^ ReKi TotalTime - 0 - "The total time of the simulation" seconds -typedef ^ ^ IntKi NFFComp - 3 - "Number of wind components" - -typedef ^ ^ IntKi NFFSteps - 0 - "Number of time steps in the FF array" - -typedef ^ ^ IntKi NYGrids - 0 - "Number of points in the lateral (y) direction of the grids" - -typedef ^ ^ IntKi NZGrids - 0 - "Number of points in the vertical (z) direction of the grids" - -typedef ^ ^ IntKi NTGrids - 0 - "Number of points in the vertical (z) direction on the tower (below the grids)" - -typedef ^ ^ IntKi WindFileFormat - - - "Binary file format description number" - +typedef ^ ParameterType IfW_FFWind_ParameterType FF - - - "Parameters used in all full-field wind types" - diff --git a/modules/inflowwind/src/IfW_TSFFWind_Types.f90 b/modules/inflowwind/src/IfW_TSFFWind_Types.f90 index 687f7eb742..e3fe8d9185 100644 --- a/modules/inflowwind/src/IfW_TSFFWind_Types.f90 +++ b/modules/inflowwind/src/IfW_TSFFWind_Types.f90 @@ -31,6 +31,7 @@ !! unpack routines associated with each defined data type. This code is automatically generated by the FAST Registry. MODULE IfW_TSFFWind_Types !--------------------------------------------------------------------------------------------------------------------------------- +USE IfW_FFWind_Base_Types USE NWTC_Library IMPLICIT NONE ! ========= IfW_TSFFWind_InitInputType ======= @@ -46,34 +47,12 @@ MODULE IfW_TSFFWind_Types ! ======================= ! ========= IfW_TSFFWind_MiscVarType ======= TYPE, PUBLIC :: IfW_TSFFWind_MiscVarType - INTEGER(IntKi) :: TimeIndex = 0 !< An Index into the TData array [-] + INTEGER(IntKi) :: dummy = 0 !< An Index into the TData array [-] END TYPE IfW_TSFFWind_MiscVarType ! ======================= ! ========= IfW_TSFFWind_ParameterType ======= TYPE, PUBLIC :: IfW_TSFFWind_ParameterType - REAL(DbKi) :: DT !< Time step for cont. state integration & disc. state update [seconds] - LOGICAL :: TowerDataExist = .FALSE. !< Data exists for the tower [-] - LOGICAL :: Periodic = .FALSE. !< Flag to indicate if the wind file is periodic [-] - REAL(SiKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: FFData !< Array of FF data [-] - REAL(SiKi) , DIMENSION(:,:,:), ALLOCATABLE :: FFTower !< Array of data along tower, below FF array [-] - REAL(ReKi) :: FFDTime = 0 !< Delta time [seconds] - REAL(ReKi) :: FFRate = 0 !< Data rate (1/FFTime) [Hertz] - REAL(ReKi) :: FFYHWid = 0 !< Half the grid width [meters] - REAL(ReKi) :: FFZHWid = 0 !< Half the grid height [meters] - REAL(ReKi) :: RefHt = 0 !< Reference (hub) height of the grid [meters] - REAL(ReKi) :: GridBase = 0 !< the height of the bottom of the grid [meters] - REAL(ReKi) :: InitXPosition = 0 !< the initial x position of grid (distance in FF is offset) [meters] - REAL(ReKi) :: InvFFYD = 0 !< reciprocal of delta y [1/meters] - REAL(ReKi) :: InvFFZD = 0 !< reciprocal of delta z [1/meters] - REAL(ReKi) :: InvMFFWS = 0 !< reciprocal of mean wind speed (MeanFFWS) [seconds/meter] - REAL(ReKi) :: MeanFFWS = 0 !< Mean wind speed (as defined in FF file), not necessarily of the portion used [meters/second] - REAL(ReKi) :: TotalTime = 0 !< The total time of the simulation [seconds] - INTEGER(IntKi) :: NFFComp = 3 !< Number of wind components [-] - INTEGER(IntKi) :: NFFSteps = 0 !< Number of time steps in the FF array [-] - INTEGER(IntKi) :: NYGrids = 0 !< Number of points in the lateral (y) direction of the grids [-] - INTEGER(IntKi) :: NZGrids = 0 !< Number of points in the vertical (z) direction of the grids [-] - INTEGER(IntKi) :: NTGrids = 0 !< Number of points in the vertical (z) direction on the tower (below the grids) [-] - INTEGER(IntKi) :: WindFileFormat !< Binary file format description number [-] + TYPE(IfW_FFWind_ParameterType) :: FF !< Parameters used in all full-field wind types [-] END TYPE IfW_TSFFWind_ParameterType ! ======================= CONTAINS @@ -85,10 +64,6 @@ SUBROUTINE IfW_TSFFWind_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlC CHARACTER(*), INTENT( OUT) :: ErrMsg ! Local INTEGER(IntKi) :: i,j,k - INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_TSFFWind_CopyInitInput' @@ -195,10 +170,6 @@ SUBROUTINE IfW_TSFFWind_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, Er INTEGER(IntKi) :: Db_Xferred INTEGER(IntKi) :: Int_Xferred INTEGER(IntKi) :: i - INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_TSFFWind_UnPackInitInput' @@ -443,7 +414,7 @@ SUBROUTINE IfW_TSFFWind_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, E ! ErrStat = ErrID_None ErrMsg = "" - DstMiscData%TimeIndex = SrcMiscData%TimeIndex + DstMiscData%dummy = SrcMiscData%dummy END SUBROUTINE IfW_TSFFWind_CopyMisc SUBROUTINE IfW_TSFFWind_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -492,7 +463,7 @@ SUBROUTINE IfW_TSFFWind_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, E Re_BufSz = 0 Db_BufSz = 0 Int_BufSz = 0 - Int_BufSz = Int_BufSz + 1 ! TimeIndex + Int_BufSz = Int_BufSz + 1 ! dummy IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -520,7 +491,7 @@ SUBROUTINE IfW_TSFFWind_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, E Db_Xferred = 1 Int_Xferred = 1 - IntKiBuf(Int_Xferred) = InData%TimeIndex + IntKiBuf(Int_Xferred) = InData%dummy Int_Xferred = Int_Xferred + 1 END SUBROUTINE IfW_TSFFWind_PackMisc @@ -550,7 +521,7 @@ SUBROUTINE IfW_TSFFWind_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat Re_Xferred = 1 Db_Xferred = 1 Int_Xferred = 1 - OutData%TimeIndex = IntKiBuf(Int_Xferred) + OutData%dummy = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 END SUBROUTINE IfW_TSFFWind_UnPackMisc @@ -562,71 +533,15 @@ SUBROUTINE IfW_TSFFWind_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat CHARACTER(*), INTENT( OUT) :: ErrMsg ! Local INTEGER(IntKi) :: i,j,k - INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_TSFFWind_CopyParam' ! ErrStat = ErrID_None ErrMsg = "" - DstParamData%DT = SrcParamData%DT - DstParamData%TowerDataExist = SrcParamData%TowerDataExist - DstParamData%Periodic = SrcParamData%Periodic -IF (ALLOCATED(SrcParamData%FFData)) THEN - i1_l = LBOUND(SrcParamData%FFData,1) - i1_u = UBOUND(SrcParamData%FFData,1) - i2_l = LBOUND(SrcParamData%FFData,2) - i2_u = UBOUND(SrcParamData%FFData,2) - i3_l = LBOUND(SrcParamData%FFData,3) - i3_u = UBOUND(SrcParamData%FFData,3) - i4_l = LBOUND(SrcParamData%FFData,4) - i4_u = UBOUND(SrcParamData%FFData,4) - IF (.NOT. ALLOCATED(DstParamData%FFData)) THEN - ALLOCATE(DstParamData%FFData(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u,i4_l:i4_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%FFData.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstParamData%FFData = SrcParamData%FFData -ENDIF -IF (ALLOCATED(SrcParamData%FFTower)) THEN - i1_l = LBOUND(SrcParamData%FFTower,1) - i1_u = UBOUND(SrcParamData%FFTower,1) - i2_l = LBOUND(SrcParamData%FFTower,2) - i2_u = UBOUND(SrcParamData%FFTower,2) - i3_l = LBOUND(SrcParamData%FFTower,3) - i3_u = UBOUND(SrcParamData%FFTower,3) - IF (.NOT. ALLOCATED(DstParamData%FFTower)) THEN - ALLOCATE(DstParamData%FFTower(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%FFTower.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstParamData%FFTower = SrcParamData%FFTower -ENDIF - DstParamData%FFDTime = SrcParamData%FFDTime - DstParamData%FFRate = SrcParamData%FFRate - DstParamData%FFYHWid = SrcParamData%FFYHWid - DstParamData%FFZHWid = SrcParamData%FFZHWid - DstParamData%RefHt = SrcParamData%RefHt - DstParamData%GridBase = SrcParamData%GridBase - DstParamData%InitXPosition = SrcParamData%InitXPosition - DstParamData%InvFFYD = SrcParamData%InvFFYD - DstParamData%InvFFZD = SrcParamData%InvFFZD - DstParamData%InvMFFWS = SrcParamData%InvMFFWS - DstParamData%MeanFFWS = SrcParamData%MeanFFWS - DstParamData%TotalTime = SrcParamData%TotalTime - DstParamData%NFFComp = SrcParamData%NFFComp - DstParamData%NFFSteps = SrcParamData%NFFSteps - DstParamData%NYGrids = SrcParamData%NYGrids - DstParamData%NZGrids = SrcParamData%NZGrids - DstParamData%NTGrids = SrcParamData%NTGrids - DstParamData%WindFileFormat = SrcParamData%WindFileFormat + CALL IfW_FFWind_CopyParam( SrcParamData%FF, DstParamData%FF, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE IfW_TSFFWind_CopyParam SUBROUTINE IfW_TSFFWind_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -638,12 +553,7 @@ SUBROUTINE IfW_TSFFWind_DestroyParam( ParamData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" -IF (ALLOCATED(ParamData%FFData)) THEN - DEALLOCATE(ParamData%FFData) -ENDIF -IF (ALLOCATED(ParamData%FFTower)) THEN - DEALLOCATE(ParamData%FFTower) -ENDIF + CALL IfW_FFWind_DestroyParam( ParamData%FF, ErrStat, ErrMsg ) END SUBROUTINE IfW_TSFFWind_DestroyParam SUBROUTINE IfW_TSFFWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -681,37 +591,24 @@ SUBROUTINE IfW_TSFFWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Re_BufSz = 0 Db_BufSz = 0 Int_BufSz = 0 - Db_BufSz = Db_BufSz + 1 ! DT - Int_BufSz = Int_BufSz + 1 ! TowerDataExist - Int_BufSz = Int_BufSz + 1 ! Periodic - Int_BufSz = Int_BufSz + 1 ! FFData allocated yes/no - IF ( ALLOCATED(InData%FFData) ) THEN - Int_BufSz = Int_BufSz + 2*4 ! FFData upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%FFData) ! FFData - END IF - Int_BufSz = Int_BufSz + 1 ! FFTower allocated yes/no - IF ( ALLOCATED(InData%FFTower) ) THEN - Int_BufSz = Int_BufSz + 2*3 ! FFTower upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%FFTower) ! FFTower - END IF - Re_BufSz = Re_BufSz + 1 ! FFDTime - Re_BufSz = Re_BufSz + 1 ! FFRate - Re_BufSz = Re_BufSz + 1 ! FFYHWid - Re_BufSz = Re_BufSz + 1 ! FFZHWid - Re_BufSz = Re_BufSz + 1 ! RefHt - Re_BufSz = Re_BufSz + 1 ! GridBase - Re_BufSz = Re_BufSz + 1 ! InitXPosition - Re_BufSz = Re_BufSz + 1 ! InvFFYD - Re_BufSz = Re_BufSz + 1 ! InvFFZD - Re_BufSz = Re_BufSz + 1 ! InvMFFWS - Re_BufSz = Re_BufSz + 1 ! MeanFFWS - Re_BufSz = Re_BufSz + 1 ! TotalTime - Int_BufSz = Int_BufSz + 1 ! NFFComp - Int_BufSz = Int_BufSz + 1 ! NFFSteps - Int_BufSz = Int_BufSz + 1 ! NYGrids - Int_BufSz = Int_BufSz + 1 ! NZGrids - Int_BufSz = Int_BufSz + 1 ! NTGrids - Int_BufSz = Int_BufSz + 1 ! WindFileFormat + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + Int_BufSz = Int_BufSz + 3 ! FF: size of buffers for each call to pack subtype + CALL IfW_FFWind_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, .TRUE. ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FF + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FF + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FF + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -739,103 +636,34 @@ SUBROUTINE IfW_TSFFWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Db_Xferred = 1 Int_Xferred = 1 - DbKiBuf(Db_Xferred) = InData%DT - Db_Xferred = Db_Xferred + 1 - IntKiBuf(Int_Xferred) = TRANSFER(InData%TowerDataExist, IntKiBuf(1)) - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = TRANSFER(InData%Periodic, IntKiBuf(1)) - Int_Xferred = Int_Xferred + 1 - IF ( .NOT. ALLOCATED(InData%FFData) ) THEN - IntKiBuf( Int_Xferred ) = 0 - Int_Xferred = Int_Xferred + 1 - ELSE - IntKiBuf( Int_Xferred ) = 1 - Int_Xferred = Int_Xferred + 1 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,2) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,3) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,3) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFData,4) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFData,4) - Int_Xferred = Int_Xferred + 2 - - DO i4 = LBOUND(InData%FFData,4), UBOUND(InData%FFData,4) - DO i3 = LBOUND(InData%FFData,3), UBOUND(InData%FFData,3) - DO i2 = LBOUND(InData%FFData,2), UBOUND(InData%FFData,2) - DO i1 = LBOUND(InData%FFData,1), UBOUND(InData%FFData,1) - ReKiBuf(Re_Xferred) = InData%FFData(i1,i2,i3,i4) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END DO - END IF - IF ( .NOT. ALLOCATED(InData%FFTower) ) THEN - IntKiBuf( Int_Xferred ) = 0 - Int_Xferred = Int_Xferred + 1 - ELSE - IntKiBuf( Int_Xferred ) = 1 - Int_Xferred = Int_Xferred + 1 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFTower,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFTower,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFTower,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFTower,2) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%FFTower,3) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FFTower,3) - Int_Xferred = Int_Xferred + 2 - - DO i3 = LBOUND(InData%FFTower,3), UBOUND(InData%FFTower,3) - DO i2 = LBOUND(InData%FFTower,2), UBOUND(InData%FFTower,2) - DO i1 = LBOUND(InData%FFTower,1), UBOUND(InData%FFTower,1) - ReKiBuf(Re_Xferred) = InData%FFTower(i1,i2,i3) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END IF - ReKiBuf(Re_Xferred) = InData%FFDTime - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%FFRate - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%FFYHWid - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%FFZHWid - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%RefHt - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%GridBase - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%InitXPosition - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%InvFFYD - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%InvFFZD - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%InvMFFWS - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%MeanFFWS - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%TotalTime - Re_Xferred = Re_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NFFComp - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NFFSteps - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NYGrids - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NZGrids - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%NTGrids - Int_Xferred = Int_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%WindFileFormat - Int_Xferred = Int_Xferred + 1 + CALL IfW_FFWind_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, OnlySize ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Re_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Re_Buf) > 0) ReKiBuf( Re_Xferred:Re_Xferred+SIZE(Re_Buf)-1 ) = Re_Buf + Re_Xferred = Re_Xferred + SIZE(Re_Buf) + DEALLOCATE(Re_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Db_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Db_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Db_Buf) > 0) DbKiBuf( Db_Xferred:Db_Xferred+SIZE(Db_Buf)-1 ) = Db_Buf + Db_Xferred = Db_Xferred + SIZE(Db_Buf) + DEALLOCATE(Db_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Int_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Int_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Int_Buf) > 0) IntKiBuf( Int_Xferred:Int_Xferred+SIZE(Int_Buf)-1 ) = Int_Buf + Int_Xferred = Int_Xferred + SIZE(Int_Buf) + DEALLOCATE(Int_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF END SUBROUTINE IfW_TSFFWind_PackParam SUBROUTINE IfW_TSFFWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -851,10 +679,6 @@ SUBROUTINE IfW_TSFFWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSta INTEGER(IntKi) :: Db_Xferred INTEGER(IntKi) :: Int_Xferred INTEGER(IntKi) :: i - INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: i2, i2_l, i2_u ! bounds (upper/lower) for an array dimension 2 - INTEGER(IntKi) :: i3, i3_l, i3_u ! bounds (upper/lower) for an array dimension 3 - INTEGER(IntKi) :: i4, i4_l, i4_u ! bounds (upper/lower) for an array dimension 4 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'IfW_TSFFWind_UnPackParam' @@ -868,109 +692,46 @@ SUBROUTINE IfW_TSFFWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSta Re_Xferred = 1 Db_Xferred = 1 Int_Xferred = 1 - OutData%DT = DbKiBuf(Db_Xferred) - Db_Xferred = Db_Xferred + 1 - OutData%TowerDataExist = TRANSFER(IntKiBuf(Int_Xferred), OutData%TowerDataExist) - Int_Xferred = Int_Xferred + 1 - OutData%Periodic = TRANSFER(IntKiBuf(Int_Xferred), OutData%Periodic) - Int_Xferred = Int_Xferred + 1 - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FFData not allocated - Int_Xferred = Int_Xferred + 1 - ELSE - Int_Xferred = Int_Xferred + 1 - i1_l = IntKiBuf( Int_Xferred ) - i1_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i2_l = IntKiBuf( Int_Xferred ) - i2_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i3_l = IntKiBuf( Int_Xferred ) - i3_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i4_l = IntKiBuf( Int_Xferred ) - i4_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - IF (ALLOCATED(OutData%FFData)) DEALLOCATE(OutData%FFData) - ALLOCATE(OutData%FFData(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u,i4_l:i4_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FFData.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - DO i4 = LBOUND(OutData%FFData,4), UBOUND(OutData%FFData,4) - DO i3 = LBOUND(OutData%FFData,3), UBOUND(OutData%FFData,3) - DO i2 = LBOUND(OutData%FFData,2), UBOUND(OutData%FFData,2) - DO i1 = LBOUND(OutData%FFData,1), UBOUND(OutData%FFData,1) - OutData%FFData(i1,i2,i3,i4) = REAL(ReKiBuf(Re_Xferred), SiKi) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END DO - END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FFTower not allocated - Int_Xferred = Int_Xferred + 1 - ELSE - Int_Xferred = Int_Xferred + 1 - i1_l = IntKiBuf( Int_Xferred ) - i1_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i2_l = IntKiBuf( Int_Xferred ) - i2_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - i3_l = IntKiBuf( Int_Xferred ) - i3_u = IntKiBuf( Int_Xferred + 1) - Int_Xferred = Int_Xferred + 2 - IF (ALLOCATED(OutData%FFTower)) DEALLOCATE(OutData%FFTower) - ALLOCATE(OutData%FFTower(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FFTower.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - DO i3 = LBOUND(OutData%FFTower,3), UBOUND(OutData%FFTower,3) - DO i2 = LBOUND(OutData%FFTower,2), UBOUND(OutData%FFTower,2) - DO i1 = LBOUND(OutData%FFTower,1), UBOUND(OutData%FFTower,1) - OutData%FFTower(i1,i2,i3) = REAL(ReKiBuf(Re_Xferred), SiKi) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO - END IF - OutData%FFDTime = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%FFRate = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%FFYHWid = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%FFZHWid = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%RefHt = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%GridBase = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%InitXPosition = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%InvFFYD = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%InvFFZD = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%InvMFFWS = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%MeanFFWS = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%TotalTime = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%NFFComp = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%NFFSteps = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%NYGrids = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%NZGrids = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%NTGrids = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%WindFileFormat = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Re_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Re_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Re_Buf = ReKiBuf( Re_Xferred:Re_Xferred+Buf_size-1 ) + Re_Xferred = Re_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Db_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Db_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Db_Buf = DbKiBuf( Db_Xferred:Db_Xferred+Buf_size-1 ) + Db_Xferred = Db_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Int_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Int_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) + Int_Xferred = Int_Xferred + Buf_size + END IF + CALL IfW_FFWind_UnpackParam( Re_Buf, Db_Buf, Int_Buf, OutData%FF, ErrStat2, ErrMsg2 ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) + IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) + IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) END SUBROUTINE IfW_TSFFWind_UnPackParam END MODULE IfW_TSFFWind_Types diff --git a/modules/inflowwind/src/IfW_UniformWind.f90 b/modules/inflowwind/src/IfW_UniformWind.f90 index c40b00a27a..ed64ef9854 100644 --- a/modules/inflowwind/src/IfW_UniformWind.f90 +++ b/modules/inflowwind/src/IfW_UniformWind.f90 @@ -68,6 +68,8 @@ MODULE IfW_UniformWind PUBLIC :: IfW_UniformWind_JacobianPInput PUBLIC :: IfW_UniformWind_GetOP + PUBLIC :: Uniform_to_FF + CONTAINS !==================================================================================================== @@ -82,7 +84,7 @@ MODULE IfW_UniformWind !! the PositionXYZ array. !! @date 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 !---------------------------------------------------------------------------------------------------- -SUBROUTINE IfW_UniformWind_Init(InitData, ParamData, MiscVars, Interval, InitOutData, ErrStat, ErrMsg) +SUBROUTINE IfW_UniformWind_Init(InitData, ParamData, MiscVars, InitOutData, ErrStat, ErrMsg) IMPLICIT NONE @@ -96,8 +98,6 @@ SUBROUTINE IfW_UniformWind_Init(InitData, ParamData, MiscVars, Interval, InitOut TYPE(IfW_UniformWind_MiscVarType), INTENT( OUT) :: MiscVars !< Misc variables for optimization (not copied in glue code) TYPE(IfW_UniformWind_InitOutputType), INTENT( OUT) :: InitOutData !< Initial output - REAL(DbKi), INTENT(IN ) :: Interval !< We don't change this. - ! Error handling @@ -538,7 +538,7 @@ SUBROUTINE IfW_UniformWind_CalcOutput(Time, PositionXYZ, p, Velocity, DiskVel, m DO PointNum = 1, NumPoints ! Calculate the velocity for the position - call GetWindSpeed(PositionXYZ(:,PointNum), p, m, op, Velocity(:,PointNum), TmpErrStat, TmpErrMsg) + call GetWindSpeed(PositionXYZ(:,PointNum), p, op, Velocity(:,PointNum), TmpErrStat, TmpErrMsg) ! Error handling CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) @@ -645,12 +645,11 @@ END SUBROUTINE InterpParams !! in space represented by InputPosition. !! !! 16-Apr-2013 - A. Platt, NREL. Converted to modular framework. Modified for NWTC_Library 2.0 -SUBROUTINE GetWindSpeed(InputPosition, p, m, op, WindSpeed, ErrStat, ErrMsg) +SUBROUTINE GetWindSpeed(InputPosition, p, op, WindSpeed, ErrStat, ErrMsg) ! Passed Variables REAL(ReKi), INTENT(IN ) :: InputPosition(3) !< input information: positions X,Y,Z TYPE(IfW_UniformWind_ParameterType), INTENT(IN ) :: p !< Parameters - TYPE(IfW_UniformWind_MiscVarType), INTENT(INOUT) :: m !< Misc variables (stores last index into array time for efficiency) TYPE(IfW_UniformWind_Intrp), INTENT(IN ) :: op !< operating point values; interpolated UniformWind parameters for this time (for glue-code linearization operating point) INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status @@ -675,8 +674,10 @@ SUBROUTINE GetWindSpeed(InputPosition, p, m, op, WindSpeed, ErrStat, ErrMsg) !> 2. Calculate the wind speed at this time (if z<0, return an error): !------------------------------------------------------------------------------------------------- - if ( InputPosition(3) < 0.0_ReKi ) then - call SetErrStat(ErrID_Fatal,'Height must not be negative.',ErrStat,ErrMsg,'GetWindSpeed') + if ( InputPosition(3) <= 0.0_ReKi ) then + if (.not. EqualRealNos(InputPosition(3), 0.0_ReKi) ) call SetErrStat(ErrID_Severe,'Height must not be negative.',ErrStat,ErrMsg,'GetWindSpeed') + WindSpeed = 0.0 + return end if !> Let \f{eqnarray}{ V_h & = & V \, \left( \frac{Z}{Z_{ref}} \right) ^ {V_{shr}} & \mbox{power-law wind shear} \\ @@ -978,5 +979,143 @@ SUBROUTINE IfW_UniformWind_GetOP( t, p, m, OP_out ) END SUBROUTINE IfW_UniformWind_GetOP +!==================================================================================================== +SUBROUTINE Uniform_to_FF(p, m, p_ff, ErrStat, ErrMsg) + + USE IfW_FFWind_Base + + TYPE(IfW_UniformWind_ParameterType), INTENT(IN ) :: p !< UniformWind Parameters + TYPE(IfW_UniformWind_MiscVarType), INTENT(INOUT) :: m !< Misc variables for optimization (not copied in glue code) + TYPE(IfW_FFWind_ParameterType), INTENT( OUT) :: p_ff !< FF Parameters + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status + CHARACTER(*), INTENT( OUT) :: ErrMsg !< error message + + ! local variables + REAL(DbKi) :: Time !< time from the start of the simulation + REAL(ReKi) :: PositionXYZ(3,1) !< Array of XYZ coordinates, 3xN + REAL(ReKi) :: Velocity(3,1) !< Velocity output at Time (Set to INOUT so that array does not get deallocated) + REAL(ReKi) :: DiskVel(3) !< HACK for AD14: disk velocity output at Time + REAL(ReKi) :: n + + INTEGER(ReKi) , parameter :: dz = 5.0 + INTEGER(ReKi) , parameter :: dy = 5.0 + INTEGER(ReKi) :: i + INTEGER(ReKi) :: it + INTEGER(ReKi) :: iy + INTEGER(ReKi) :: iz + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), parameter :: RoutineName = 'Uniform_to_FF' + + ErrStat = ErrID_None + ErrMsg = "" + + p_ff%WindFileFormat = -1 ! "Binary file format description number" - + p_ff%NFFComp = 3 ! "Number of wind components" - + p_ff%Periodic = .false. + p_ff%InterpTower = .true. + p_ff%RefHt = p%RefHt + p_ff%NTGrids = 0 + p_ff%InvFFYD = 1.0_ReKi / dy ! "reciprocal of delta y" 1/meters + p_ff%InvFFZD = 1.0_ReKi / dz ! "reciprocal of delta z" 1/meters + + ! add roughly 10% to the width + n = NINT( p%RefLength*1.1_ReKi*0.5_ReKi / dy ) + p_ff%NYGrids = n*2+1 ! "Number of points in the lateral (y) direction of the grids" - + p_ff%FFYHWid = 0.5_ReKi * dy * (p_ff%NYGrids-1) ! "Half the grid width" meters + + n = NINT( p%RefLength*1.1_ReKi*0.5_ReKi / dz ) + p_ff%NZGrids = INT( p_ff%RefHt / dy ) + n + 1 ! "Number of points in the vertical (z) direction of the grids" - + + p_ff%FFZHWid = 0.5_ReKi * dz * (p_ff%NZGrids -1) ! "Half the grid height" meters + p_ff%GridBase = p_ff%RefHt + n*dz - p_ff%FFZHWid*2.0_ReKi ! "the height of the bottom of the grid" meters + + p_ff%InitXPosition = 0.0_ReKi ! "the initial x position of grid (distance in FF is offset)" meters + + + ! time will be the smallest delta t in this Uniform wind file + if (p%NumDataLines < 2) then + p_ff%FFDTime = 600.0_ReKi ! doesn't matter what the time step is + else + p_ff%FFDTime = HUGE(p_ff%FFDTime) ! "Delta time" seconds + do i=2,p%NumDataLines + p_ff%FFDTime = min(p_ff%FFDTime, p%TData(i) - p%TData(i-1)) + end do + + if (p_ff%FFDTime < 0.0001) then + call SetErrStat( ErrID_Fatal, "Smallest time step in uniform wind file is less that 0.0001 seconds. Increase the time step "//& + " to convert to a FF file.", ErrStat, ErrMsg, RoutineName ) + return + end if + + end if + + p_ff%FFRate = 1.0_ReKi / p_ff%FFDTime ! "Data rate (1/FFDTime)" Hertz + + + p_ff%AddMeanAfterInterp = .FALSE. ! "Add the mean wind speed after interpolating at a given height?" - + p_ff%WindProfileType = WindProfileType_PL ! "Wind profile type (0=constant;1=logarithmic;2=power law)" - + p_ff%PLExp = GetAverageVal(p%VSHR) ! "Power law exponent (used for PL wind profile type only)" - + p_ff%Z0 = 0.0_ReKi ! "Surface roughness length (used for LOG wind profile type only)" - + + if (p%NumDataLines < 2) then + p_ff%NFFSteps = 2 ! "Number of time steps in the FF array" - + else + p_ff%NFFSteps = NINT(p%TData(p%NumDataLines) / p_ff%FFDTime) + 1 + end if + + p_ff%TotalTime = (p_ff%NFFSteps-1) * p_ff%FFDTime ! "The total time of the simulation" seconds + + + call AllocAry( p_ff%FFData, p_ff%NZGrids,p_ff%NYGrids,p_ff%NFFComp, p_ff%NFFSteps, 'p%FF%FFData', ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return + + PositionXYZ = 0.0_ReKi + do it = 1,p_ff%NFFSteps + Time = (it-1)*p_ff%FFDTime + + do iy = 1,p_ff%NYGrids + PositionXYZ(2,1) = (iy-1)*dy - p_ff%FFYHWid + + do iz=1,p_ff%NZGrids + PositionXYZ(3,1) = (iz-1)*dz + p_ff%GridBase + + call IfW_UniformWind_CalcOutput(Time, PositionXYZ, p, Velocity, DiskVel, m, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + p_ff%FFData(iz,iy,:,it) = Velocity(:,1) + + end do ! iz + end do ! iy + end do ! it + + ! compute some averages for this simulation + p_ff%MeanFFWS = GetAverageVal(p%V) ! "Mean wind speed (advection speed)" + p_ff%InvMFFWS = 1.0_ReKi / p_ff%MeanFFWS + + RETURN + +CONTAINS + + FUNCTION GetAverageVal(Ary) RESULT(Avg) + REAL(ReKi), intent(in) :: Ary(:) + REAL(ReKi) :: Avg + + if (p%NumDataLines < 2) then + Avg = Ary(1) + else + Avg = p%TData(1) * Ary(1) ! in case tData(1)/=0 + do i=2,p%NumDataLines + Avg = Avg + (p%TData(i)-p%TData(i-1)) * (Ary(i)+Ary(i-1))/2.0_ReKi + end do + Avg = Avg / (p%TData(p%NumDataLines)-p%TData(1)) + end if + + END FUNCTION GetAverageVal + +END SUBROUTINE Uniform_to_FF +!==================================================================================================== + !==================================================================================================== END MODULE IfW_UniformWind diff --git a/modules/inflowwind/src/IfW_UserWind.f90 b/modules/inflowwind/src/IfW_UserWind.f90 index 7321734bdb..cf52a33860 100644 --- a/modules/inflowwind/src/IfW_UserWind.f90 +++ b/modules/inflowwind/src/IfW_UserWind.f90 @@ -176,8 +176,8 @@ SUBROUTINE IfW_UserWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, DiskV INTEGER(IntKi) :: NumPoints ! Number of points passed in ! temporary variables - INTEGER(IntKi) :: TmpErrStat ! temporary error status - CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message + !INTEGER(IntKi) :: TmpErrStat ! temporary error status + !CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message diff --git a/modules/inflowwind/src/InflowWind.f90 b/modules/inflowwind/src/InflowWind.f90 index 6c12d390f9..c9c5991e2c 100644 --- a/modules/inflowwind/src/InflowWind.f90 +++ b/modules/inflowwind/src/InflowWind.f90 @@ -21,6 +21,7 @@ !********************************************************************************************************************************** ! LICENSING ! Copyright (C) 2015-2016 National Renewable Energy Laboratory +! Copyright (C) 2017 Envision Energy ! ! This file is part of InflowWind. ! @@ -43,7 +44,8 @@ MODULE InflowWind USE InflowWind_Types USE NWTC_Library USE InflowWind_Subs - USE Lidar + + USE Lidar ! module for obtaining sensor data IMPLICIT NONE PRIVATE @@ -52,12 +54,17 @@ MODULE InflowWind + ! ..... Public Subroutines ................................................................................................... PUBLIC :: InflowWind_Init !< Initialization routine PUBLIC :: InflowWind_CalcOutput !< Calculate the wind velocities PUBLIC :: InflowWind_End !< Ending routine (includes clean up) + PUBLIC :: InflowWind_Convert2HAWC !< An extension of the FAST framework, this routine converts an InflowWind data structure to HAWC format wind files + PUBLIC :: InflowWind_Convert2Bladed !< An extension of the FAST framework, this routine converts an InflowWind data structure to Bladed format wind files (with shear already included) + PUBLIC :: InflowWind_Convert2VTK !< An extension of the FAST framework, this routine converts an InflowWind data structure to VTK format wind files + ! These routines satisfy the framework, but do nothing at present. PUBLIC :: InflowWind_UpdateStates !< Loose coupling routine for solving for constraint states, integrating continuous states, and updating discrete states @@ -230,22 +237,12 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, ENDIF - ! Set the p and OtherStates for InflowWind using the input file information. - - CALL InflowWind_SetParameters( InitInp, InputFileData, p, m, TmpErrStat, TmpErrMsg ) - CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) - IF ( ErrStat>= AbortErrLev ) THEN - CALL Cleanup() - RETURN - ENDIF - - ! If a summary file was requested, open it. IF ( InputFileData%SumPrint ) THEN ! Open the summary file and write some preliminary info to it - CALL InflowWind_OpenSumFile( SumFileUnit, SumFileName, IfW_Ver, p%WindType, TmpErrStat, TmpErrMsg ) + CALL InflowWind_OpenSumFile( SumFileUnit, SumFileName, IfW_Ver, InputFileData%WindType, TmpErrStat, TmpErrMsg ) CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) IF (ErrStat >= AbortErrLev) THEN CALL Cleanup() @@ -279,8 +276,7 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, InitOutData%WindFileInfo%MWS = HUGE(InitOutData%WindFileInfo%MWS) - InitOutData%WindFileInfo%WindType = p%WindType - SELECT CASE ( p%WindType ) + SELECT CASE ( InputFileData%WindType ) CASE ( Steady_WindNumber ) @@ -345,6 +341,7 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, ! Store wind file metadata InitOutData%WindFileInfo%FileName = "" + InitOutData%WindFileInfo%WindType = Steady_WindNumber InitOutData%WindFileInfo%RefHt = InputFileData%Steady_RefHt InitOutData%WindFileInfo%RefHt_Set = .FALSE. ! The wind file does not set this InitOutData%WindFileInfo%DT = 0.0_ReKi @@ -389,7 +386,7 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, ! Initialize the UniformWind module CALL IfW_UniformWind_Init(Uniform_InitData, p%UniformWind, & - m%UniformWind, TimeInterval, Uniform_InitOutData, TmpErrStat, TmpErrMsg) + m%UniformWind, Uniform_InitOutData, TmpErrStat, TmpErrMsg) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, ' IfW_Init' ) IF ( ErrStat >= AbortErrLev ) RETURN @@ -397,6 +394,7 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, ! Store wind file metadata InitOutData%WindFileInfo%FileName = InputFileData%Uniform_FileName + InitOutData%WindFileInfo%WindType = Uniform_WindNumber InitOutData%WindFileInfo%RefHt = p%UniformWind%RefHt InitOutData%WindFileInfo%RefHt_Set = .FALSE. ! The wind file does not set this InitOutData%WindFileInfo%DT = Uniform_InitOutData%WindFileDT @@ -447,89 +445,53 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, ! Initialize the TSFFWind module CALL IfW_TSFFWind_Init(TSFF_InitData, p%TSFFWind, & - m%TSFFWind, TimeInterval, TSFF_InitOutData, TmpErrStat, TmpErrMsg) + m%TSFFWind, TSFF_InitOutData, TmpErrStat, TmpErrMsg) CALL SetErrSTat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN ! Store wind file metadata InitOutData%WindFileInfo%FileName = InputFileData%TSFF_FileName - InitOutData%WindFileInfo%RefHt = p%TSFFWind%RefHt - InitOutData%WindFileInfo%RefHt_Set = .TRUE. - InitOutData%WindFileInfo%DT = p%TSFFWind%FFDTime - InitOutData%WindFileInfo%NumTSteps = p%TSFFWind%NFFSteps - InitOutData%WindFileInfo%ConstantDT = .TRUE. - IF ( p%TSFFWind%Periodic ) THEN - InitOutData%WindFileInfo%TRange = (/ 0.0_ReKi, p%TSFFWind%TotalTime /) - InitOutData%WindFileInfo%TRange_Limited = .FALSE. - ELSE ! Shift the time range to compensate for the shifting of the wind grid - InitOutData%WindFileInfo%TRange = (/ 0.0_ReKi, p%TSFFWind%TotalTime /) & - - p%TSFFWind%InitXPosition*p%TSFFWind%InvMFFWS - InitOutData%WindFileInfo%TRange_Limited = .TRUE. - ENDIF - InitOutData%WindFileInfo%YRange = (/ -p%TSFFWind%FFYHWid, p%TSFFWind%FFYHWid /) - InitOutData%WindFileInfo%YRange_Limited = .TRUE. ! Hard boundaries enforced in y-direction - IF ( p%TSFFWind%TowerDataExist ) THEN ! have tower data - InitOutData%WindFileInfo%ZRange = (/ 0.0_Reki, & - p%TSFFWind%RefHt + p%TSFFWind%FFZHWid /) - ELSE - InitOutData%WindFileInfo%ZRange = (/ p%TSFFWind%RefHt - p%TSFFWind%FFZHWid, & - p%TSFFWind%RefHt + p%TSFFWind%FFZHWid /) - ENDIF - InitOutData%WindFileInfo%ZRange_Limited = .TRUE. - InitOutData%WindFileInfo%BinaryFormat = p%TSFFWind%WindFileFormat - InitOutData%WindFileInfo%IsBinary = .TRUE. - InitOutData%WindFileInfo%TI = 0.0_ReKi - InitOutData%WindFileInfo%TI_listed = .FALSE. - InitOutData%WindFileInfo%MWS = p%TSFFWind%MeanFFWS - + + CALL SetFFInitOutData(p%TSFFWind%FF) - CASE ( BladedFF_WindNumber ) + CASE ( BladedFF_WindNumber, BladedFF_Shr_WindNumber ) ! Set InitInp information - BladedFF_InitData%WindFileName = InputFileData%BladedFF_FileName - BladedFF_InitData%TowerFileExist = InputFileData%BladedFF_TowerFile BladedFF_InitData%SumFileUnit = SumFileUnit - + + if (InputFileData%WindType /= BladedFF_Shr_WindNumber) then + BladedFF_InitData%WindFileName = TRIM(InputFileData%BladedFF_FileName)//'.wnd' + BladedFF_InitData%TowerFileExist = InputFileData%BladedFF_TowerFile + BladedFF_InitData%NativeBladedFmt = .false. + else + BladedFF_InitData%WindFileName = InputFileData%BladedFF_FileName + BladedFF_InitData%TowerFileExist = .false. + BladedFF_InitData%NativeBladedFmt = .true. + !call IfW_FFWind_CopyInitInput( InputFileData%FF, BladedFF_InitData%FF, MESH_NEWCOPY, TmpErrStat, TmpErrMsg) + end if + ! Initialize the BladedFFWind module CALL IfW_BladedFFWind_Init(BladedFF_InitData, p%BladedFFWind, m%BladedFFWind, & - TimeInterval, BladedFF_InitOutData, TmpErrStat, TmpErrMsg) + BladedFF_InitOutData, TmpErrStat, TmpErrMsg) CALL SetErrSTat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN ! Store wind file metadata InitOutData%WindFileInfo%FileName = InputFileData%BladedFF_FileName - InitOutData%WindFileInfo%RefHt = p%BladedFFWind%RefHt - InitOutData%WindFileInfo%RefHt_Set = .TRUE. - InitOutData%WindFileInfo%DT = p%BladedFFWind%FFDTime - InitOutData%WindFileInfo%NumTSteps = p%BladedFFWind%NFFSteps - InitOutData%WindFileInfo%ConstantDT = .TRUE. - IF ( p%BladedFFWind%Periodic ) THEN - InitOutData%WindFileInfo%TRange = (/ 0.0_ReKi, p%BladedFFWind%TotalTime /) - InitOutData%WindFileInfo%TRange_Limited = .FALSE. - ELSE ! Shift the time range to compensate for the shifting of the wind grid - InitOutData%WindFileInfo%TRange = (/ 0.0_ReKi, p%BladedFFWind%TotalTime /) & - - p%BladedFFWind%InitXPosition*p%BladedFFWind%InvMFFWS - InitOutData%WindFileInfo%TRange_Limited = .TRUE. - ENDIF - InitOutData%WindFileInfo%YRange = (/ -p%BladedFFWind%FFYHWid, p%BladedFFWind%FFYHWid /) - InitOutData%WindFileInfo%YRange_Limited = .TRUE. ! Hard boundaries enforced in y-direction - IF ( p%BladedFFWind%TowerDataExist ) THEN ! have tower data - InitOutData%WindFileInfo%ZRange = (/ 0.0_Reki, & - p%BladedFFWind%RefHt + p%BladedFFWind%FFZHWid /) - ELSE - InitOutData%WindFileInfo%ZRange = (/ p%BladedFFWind%RefHt - p%BladedFFWind%FFZHWid, & - p%BladedFFWind%RefHt + p%BladedFFWind%FFZHWid /) - ENDIF - InitOutData%WindFileInfo%ZRange_Limited = .TRUE. - InitOutData%WindFileInfo%BinaryFormat = p%BladedFFWind%WindFileFormat - InitOutData%WindFileInfo%IsBinary = .TRUE. - InitOutData%WindFileInfo%TI = BladedFF_InitOutData%TI - InitOutData%WindFileInfo%TI_listed = .TRUE. ! This must be listed in the file someplace - InitOutData%WindFileInfo%MWS = p%BladedFFWind%MeanFFWS - - + + CALL SetFFInitOutData(p%BladedFFWind%FF) + InitOutData%WindFileInfo%TI = BladedFF_InitOutData%TI + InitOutData%WindFileInfo%TI_listed = .TRUE. ! This must be listed in the file someplace + + if (InputFileData%WindType == BladedFF_Shr_WindNumber) then + InputFileData%WindType = BladedFF_WindNumber + InputFileData%PropagationDir = BladedFF_InitOutData%PropagationDir + InputFileData%VFlowAngle = BladedFF_InitOutData%VFlowAngle + end if + + CASE ( HAWC_WindNumber ) ! Set InitInp information @@ -540,54 +502,25 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, HAWC_InitData%nx = InputFileData%HAWC_nx HAWC_InitData%ny = InputFileData%HAWC_ny HAWC_InitData%nz = InputFileData%HAWC_nz - HAWC_InitData%ScaleMethod = InputFileData%HAWC_ScaleMethod - HAWC_InitData%SF(1) = InputFileData%HAWC_SFx - HAWC_InitData%SF(2) = InputFileData%HAWC_SFy - HAWC_InitData%SF(3) = InputFileData%HAWC_SFz - HAWC_InitData%SigmaF(1) = InputFileData%HAWC_SigmaFx - HAWC_InitData%SigmaF(2) = InputFileData%HAWC_SigmaFy - HAWC_InitData%SigmaF(3) = InputFileData%HAWC_SigmaFz - !HAWC_InitData%TStart = InputFileData%HAWC_TStart - !HAWC_InitData%TEnd = InputFileData%HAWC_TEnd + HAWC_InitData%dx = InputFileData%HAWC_dx HAWC_InitData%dy = InputFileData%HAWC_dy HAWC_InitData%dz = InputFileData%HAWC_dz - HAWC_InitData%WindProfileType = InputFileData%HAWC_ProfileType - HAWC_InitData%RefHt = InputFileData%HAWC_RefHt - HAWC_InitData%URef = InputFileData%HAWC_URef - HAWC_InitData%PLExp = InputFileData%HAWC_PLExp - HAWC_InitData%Z0 = InputFileData%HAWC_Z0 - HAWC_InitData%InitPosition = InputFileData%HAWC_InitPosition + + call IfW_FFWind_CopyInitInput( InputFileData%FF, HAWC_InitData%FF, MESH_NEWCOPY, TmpErrStat, TmpErrMsg) ! Initialize the HAWCWind module CALL IfW_HAWCWind_Init(HAWC_InitData, p%HAWCWind, m%HAWCWind, & TimeInterval, HAWC_InitOutData, TmpErrStat, TmpErrMsg) - CALL SetErrSTat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) + CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN ! Store wind file metadata + CALL SetFFInitOutData(p%HAWCWind%FF) InitOutData%WindFileInfo%FileName = InputFileData%HAWC_FileName_u - InitOutData%WindFileInfo%RefHt = p%HAWCWind%RefHt - InitOutData%WindFileInfo%RefHt_Set = .TRUE. - InitOutData%WindFileInfo%DT = p%HAWCWind%deltaXInv / p%HAWCWind%URef - InitOutData%WindFileInfo%NumTSteps = p%HAWCWind%nx - InitOutData%WindFileInfo%ConstantDT = .TRUE. - InitOutData%WindFileInfo%TRange = p%HAWCWind%LengthX / p%HAWCWind%URef - InitOutData%WindFileInfo%TRange_Limited = .FALSE. - InitOutData%WindFileInfo%YRange = (/ -p%HAWCWind%LengthYHalf, p%HAWCWind%LengthYHalf /) - InitOutData%WindFileInfo%YRange_Limited = .TRUE. ! Hard boundaries enforced in y-direction - InitOutData%WindFileInfo%ZRange = (/ p%HAWCWind%GridBase, & - p%HAWCWind%GridBase + p%HAWCWind%nz / p%HAWCWind%deltaZInv /) - InitOutData%WindFileInfo%ZRange_Limited = .TRUE. - InitOutData%WindFileInfo%BinaryFormat = -1_IntKi - InitOutData%WindFileInfo%IsBinary = .TRUE. - InitOutData%WindFileInfo%TI = BladedFF_InitOutData%TI - InitOutData%WindFileInfo%TI_listed = .FALSE. ! This must be listed in the file someplace - InitOutData%WindFileInfo%MWS = p%HAWCWind%URef - - + CASE (User_WindNumber) @@ -597,7 +530,7 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN - CASE (FDext_WindNumber) + CASE ( FDext_WindNumber ) ! Initialize the UserWind module CALL IfW_4Dext_Init(InitInp%FDext, p%FDext, m%FDext, TimeInterval, FDext_InitOutData, TmpErrStat, TmpErrMsg) @@ -611,14 +544,27 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, - END SELECT + END SELECT + + !IF ( InputFileData%CTTS_Flag ) THEN + ! ! Initialize the CTTS_Wind module + !ENDIF - IF ( p%CTTS_Flag ) THEN - ! Initialize the CTTS_Wind module - ENDIF + !............................................ + ! Set the p and OtherStates for InflowWind using the input file information. + ! (set this after initializing modules so that we can use propagationDir and VFlowAng from native-Bladed files + !............................................ + + CALL InflowWind_SetParameters( InitInp, InputFileData, p, m, TmpErrStat, TmpErrMsg ) + CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) + IF ( ErrStat>= AbortErrLev ) THEN + CALL Cleanup() + RETURN + ENDIF + !!! !---------------------------------------------------------------------------------------------- !!! ! Check for coherent turbulence file (KH superimposed on a background wind file) @@ -752,9 +698,43 @@ SUBROUTINE CleanUp() END SUBROUTINE CleanUp + SUBROUTINE SetFFInitOutData(FFp) + + TYPE(IfW_FFWind_ParameterType), INTENT(IN ) :: FFp !< Parameters -END SUBROUTINE InflowWind_Init + + InitOutData%WindFileInfo%WindType = p%WindType + InitOutData%WindFileInfo%RefHt = FFp%RefHt + InitOutData%WindFileInfo%RefHt_Set = .TRUE. + InitOutData%WindFileInfo%DT = FFp%FFDTime + InitOutData%WindFileInfo%NumTSteps = FFp%NFFSteps + InitOutData%WindFileInfo%ConstantDT = .TRUE. + IF ( FFp%Periodic ) THEN + InitOutData%WindFileInfo%TRange = (/ 0.0_ReKi, FFp%TotalTime /) + InitOutData%WindFileInfo%TRange_Limited = .FALSE. + ELSE ! Shift the time range to compensate for the shifting of the wind grid + InitOutData%WindFileInfo%TRange = (/ 0.0_ReKi, FFp%TotalTime /) - FFp%InitXPosition*FFp%InvMFFWS + InitOutData%WindFileInfo%TRange_Limited = .TRUE. + ENDIF + InitOutData%WindFileInfo%YRange = (/ -FFp%FFYHWid, FFp%FFYHWid /) + InitOutData%WindFileInfo%YRange_Limited = .TRUE. ! Hard boundaries enforced in y-direction + IF ( p%TSFFWind%FF%NTGrids > 0 ) THEN ! have tower data + InitOutData%WindFileInfo%ZRange = (/ 0.0_Reki, FFp%RefHt + FFp%FFZHWid /) + ELSE + InitOutData%WindFileInfo%ZRange = (/ FFp%GridBase, & + FFp%GridBase + FFp%FFZHWid*2.0 /) + ENDIF + InitOutData%WindFileInfo%ZRange_Limited = .TRUE. + InitOutData%WindFileInfo%BinaryFormat = FFp%WindFileFormat + InitOutData%WindFileInfo%IsBinary = .TRUE. + InitOutData%WindFileInfo%MWS = FFp%MeanFFWS + InitOutData%WindFileInfo%TI = 0.0_ReKi + InitOutData%WindFileInfo%TI_listed = .FALSE. + + END SUBROUTINE SetFFInitOutData + +END SUBROUTINE InflowWind_Init !==================================================================================================== @@ -1173,7 +1153,7 @@ SUBROUTINE InflowWind_JacobianPInput( t, u, p, x, xd, z, OtherState, y, m, ErrSt local_dYdu = 0.0_R8Ki end if - dYdu(3*n+i, 3*n+1:) = p%OutParam(i)%SignM * local_dYdu( comp , 4:6) + dYdu(3*n+i, 3*n+1:) = p%OutParam(i)%SignM * local_dYdu( comp , 4:6) end do CASE DEFAULT @@ -1514,5 +1494,191 @@ SUBROUTINE InflowWind_GetOP( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMs END SUBROUTINE InflowWind_GetOP !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +!==================================================================================================== +SUBROUTINE InflowWind_Convert2HAWC( FileRootName, p, m, ErrStat, ErrMsg ) + + USE IfW_FFWind_Base + IMPLICIT NONE + + CHARACTER(*), PARAMETER :: RoutineName="InflowWind_Convert2HAWC" + + ! Subroutine arguments + + TYPE(InflowWind_ParameterType), INTENT(IN ) :: p !< Parameters + TYPE(InflowWind_MiscVarType), INTENT(INOUT) :: m !< Misc/optimization variables + CHARACTER(*), INTENT(IN ) :: FileRootName !< RootName for output files + + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + + ! Local variables + TYPE(IfW_FFWind_ParameterType) :: p_ff !< FF Parameters + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + + + ! Compute the wind velocities by stepping through all the data points and calling the appropriate GetWindSpeed routine + SELECT CASE ( p%WindType ) + + CASE (Steady_WindNumber, Uniform_WindNumber) + + CALL Uniform_to_FF(p%UniformWind, m%UniformWind, p_ff, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + IF (ErrStat < AbortErrLev) THEN + CALL ConvertFFWind_to_HAWC2(FileRootName, p_ff, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + END IF + + CALL IfW_FFWind_DestroyParam(p_ff,ErrStat2,ErrMsg2) + + CASE (TSFF_WindNumber) + + CALL ConvertFFWind_to_HAWC2(FileRootName, p%TSFFWind%FF, ErrStat, ErrMsg) + + CASE (BladedFF_WindNumber) + + CALL ConvertFFWind_to_HAWC2(FileRootName, p%BladedFFWind%FF, ErrStat, ErrMsg) + + CASE ( HAWC_WindNumber ) + + CALL ConvertFFWind_to_HAWC2(FileRootName, p%HAWCWind%FF, ErrStat, ErrMsg) + + CASE DEFAULT ! User_WindNumber + + ErrStat = ErrID_Warn + ErrMsg = 'Wind type '//TRIM(Num2LStr(p%WindType))//' cannot be converted to HAWC format.' + + END SELECT + +END SUBROUTINE InflowWind_Convert2HAWC + +!==================================================================================================== +SUBROUTINE InflowWind_Convert2Bladed( FileRootName, p, m, ErrStat, ErrMsg ) + + USE IfW_FFWind_Base + IMPLICIT NONE + + CHARACTER(*), PARAMETER :: RoutineName="InflowWind_Convert2Bladed" + + ! Subroutine arguments + + TYPE(InflowWind_ParameterType), INTENT(IN ) :: p !< Parameters + TYPE(InflowWind_MiscVarType), INTENT(INOUT) :: m !< Misc/optimization variables + CHARACTER(*), INTENT(IN ) :: FileRootName !< RootName for output files + + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! Local variables + TYPE(IfW_FFWind_ParameterType) :: p_ff !< FF Parameters + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + + ErrStat = ErrID_None + ErrMsg = "" + + ! Local variables + + ! Compute the wind velocities by stepping through all the data points and calling the appropriate GetWindSpeed routine + SELECT CASE ( p%WindType ) + + CASE (Steady_WindNumber, Uniform_WindNumber) + + CALL Uniform_to_FF(p%UniformWind, m%UniformWind, p_ff, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + IF (ErrStat < AbortErrLev) THEN + CALL ConvertFFWind_to_Bladed(FileRootName, p_ff, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + END IF + + CALL IfW_FFWind_DestroyParam(p_ff,ErrStat2,ErrMsg2) + + CASE (TSFF_WindNumber) + + CALL ConvertFFWind_to_Bladed(FileRootName, p%TSFFWind%FF, ErrStat, ErrMsg) + + CASE (BladedFF_WindNumber) + + CALL ConvertFFWind_to_Bladed(FileRootName, p%BladedFFWind%FF, ErrStat, ErrMsg) + + CASE ( HAWC_WindNumber ) + + CALL ConvertFFWind_to_Bladed(FileRootName, p%HAWCWind%FF, ErrStat, ErrMsg) + + CASE DEFAULT ! User_WindNumber + + ErrStat = ErrID_Warn + ErrMsg = 'Wind type '//TRIM(Num2LStr(p%WindType))//' cannot be converted to Bladed format.' + + END SELECT + +END SUBROUTINE InflowWind_Convert2Bladed + +!==================================================================================================== +SUBROUTINE InflowWind_Convert2VTK( FileRootName, p, m, ErrStat, ErrMsg ) + + USE IfW_FFWind_Base + IMPLICIT NONE + + CHARACTER(*), PARAMETER :: RoutineName="InflowWind_Convert2VTK" + + ! Subroutine arguments + + TYPE(InflowWind_ParameterType), INTENT(IN ) :: p !< Parameters + TYPE(InflowWind_MiscVarType), INTENT(INOUT) :: m !< Misc/optimization variables + CHARACTER(*), INTENT(IN ) :: FileRootName !< RootName for output files + + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! Local variables + TYPE(IfW_FFWind_ParameterType) :: p_ff !< FF Parameters + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + + ErrStat = ErrID_None + ErrMsg = "" + + ! Local variables + + ! Compute the wind velocities by stepping through all the data points and calling the appropriate GetWindSpeed routine + SELECT CASE ( p%WindType ) + + CASE (Steady_WindNumber, Uniform_WindNumber) + + CALL Uniform_to_FF(p%UniformWind, m%UniformWind, p_ff, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + IF (ErrStat < AbortErrLev) THEN + CALL ConvertFFWind_toVTK(FileRootName, p_ff, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + END IF + + CALL IfW_FFWind_DestroyParam(p_ff,ErrStat2,ErrMsg2) + + CASE (TSFF_WindNumber) + + CALL ConvertFFWind_toVTK(FileRootName, p%TSFFWind%FF, ErrStat, ErrMsg) + + CASE (BladedFF_WindNumber) + + CALL ConvertFFWind_toVTK(FileRootName, p%BladedFFWind%FF, ErrStat, ErrMsg) + + CASE ( HAWC_WindNumber ) + + CALL ConvertFFWind_toVTK(FileRootName, p%HAWCWind%FF, ErrStat, ErrMsg) + + CASE DEFAULT ! User_WindNumber + + ErrStat = ErrID_Warn + ErrMsg = 'Wind type '//TRIM(Num2LStr(p%WindType))//' cannot be converted to VTK format.' + + END SELECT + +END SUBROUTINE InflowWind_Convert2VTK !==================================================================================================== END MODULE InflowWind diff --git a/modules/inflowwind/src/InflowWind.txt b/modules/inflowwind/src/InflowWind.txt index d634f2db00..363e186295 100644 --- a/modules/inflowwind/src/InflowWind.txt +++ b/modules/inflowwind/src/InflowWind.txt @@ -9,6 +9,7 @@ usefrom IfW_UniformWind.txt usefrom IfW_TSFFWind.txt +usefrom IfW_FFWind_Base.txt usefrom IfW_BladedFFWind.txt usefrom IfW_HAWCWind.txt usefrom IfW_UserWind.txt @@ -25,8 +26,9 @@ param ^ - IntKi TSFF_WindNu param ^ - IntKi BladedFF_WindNumber - 4 - "Bladed style binary full-field file. Includes native bladed format" - param ^ - IntKi HAWC_WindNumber - 5 - "HAWC wind file." - param ^ - IntKi User_WindNumber - 6 - "User defined wind." - -param ^ - IntKi FDext_WindNumber - 7 - "4D wind from external souce (i.e., FAST.Farm)." - -param ^ - IntKi Highest_WindNumber - 7 - "Highest wind number supported." - +param ^ - IntKi BladedFF_Shr_WindNumber - 7 - "Native Bladed binary full-field file." - +param ^ - IntKi FDext_WindNumber - 8 - "4D wind from external souce (i.e., FAST.Farm)." - +param ^ - IntKi Highest_WindNumber - 8 - "Highest wind number supported." - ######################### @@ -60,6 +62,7 @@ typedef ^ ^ ReKi MWS typedef InflowWind/InflowWind InflowWind_InputFile LOGICAL EchoFlag - - - "Echo the input file" - typedef ^ ^ IntKi WindType - 0 - "Type of windfile" - typedef ^ ^ ReKi PropagationDir - - - "Direction of wind propagation (meteorological direction)" (degrees) +typedef ^ ^ ReKi VFlowAngle - - - "Vertical (upflow) angle" degrees typedef ^ ^ IntKi NWindVel - - - "Number of points to output the wind velocity (0 to 9)" typedef ^ ^ ReKi WindVxiList : - - "List of X coordinates for wind velocity measurements" meters typedef ^ ^ ReKi WindVyiList : - - "List of Y coordinates for wind velocity measurements" meters @@ -85,28 +88,14 @@ typedef ^ ^ IntKi HAWC_nz typedef ^ ^ ReKi HAWC_dx - - - "HAWC -- distance between points in x direction" meters typedef ^ ^ ReKi HAWC_dy - - - "HAWC -- distance between points in y direction" meters typedef ^ ^ ReKi HAWC_dz - - - "HAWC -- distance between points in z direction" meters -typedef ^ ^ ReKi HAWC_RefHt - - - "HAWC -- reference height" meters -typedef ^ ^ IntKi HAWC_ScaleMethod - - - "HAWC -- scale method" - -typedef ^ ^ ReKi HAWC_SFx - - - "HAWC -- turbulence scaling factor x direction" - -typedef ^ ^ ReKi HAWC_SFy - - - "HAWC -- turbulence scaling factor y direction" - -typedef ^ ^ ReKi HAWC_SFz - - - "HAWC -- turbulence scaling factor z direction" - -typedef ^ ^ ReKi HAWC_SigmaFx - - - "HAWC -- turbulence standard deviation x direction" - -typedef ^ ^ ReKi HAWC_SigmaFy - - - "HAWC -- turbulence standard deviation y direction" - -typedef ^ ^ ReKi HAWC_SigmaFz - - - "HAWC -- turbulence standard deviation z direction" - -typedef ^ ^ ReKi HAWC_TStart - - - "HAWC -- start time for turbulence scaling" seconds -typedef ^ ^ ReKi HAWC_TEnd - - - "HAWC -- end time for turbulence scaling" seconds -typedef ^ ^ ReKi HAWC_URef - - - "HAWC -- Mean u-component wind speed at the reference height" meters -typedef ^ ^ IntKi HAWC_ProfileType - - - "HAWC -- Wind profile type (0=constant;1=logarithmic;2=power law)" - -typedef ^ ^ ReKi HAWC_PLExp - - - "HAWC -- Power law exponent (used for PL wind profile type only)" - -typedef ^ ^ ReKi HAWC_Z0 - - - "HAWC -- Surface roughness length (used for LOG wind profile type only)" - -typedef ^ ^ ReKi HAWC_InitPosition 3 - - "HAWC -- initial position (offset for wind file box)" meters typedef ^ ^ LOGICAL SumPrint - - - "Write summary info to a file .IfW.Sum" - typedef ^ ^ IntKi NumOuts - - - "Number of parameters in the output list (number of outputs requested)" - -typedef ^ ^ CHARACTER(ChanLen) OutList : - - "List of user-requested output channels" - +typedef ^ ^ CHARACTER(ChanLen) OutList : - - "List of user-requested output channels" - typedef ^ ^ IntKi SensorType - SensorType_None - "Sensor type (for lidar/sensor module)" - typedef ^ ^ IntKi NumPulseGate - - - "the number of range gates to return wind speeds at" - typedef ^ ^ ReKi RotorApexOffsetPos 3 - - "position of the lidar unit relative to the rotor apex of rotation" m typedef ^ ^ LOGICAL LidRadialVel - - - "TRUE => return radial component, FALSE => return 'x' direction estimate" - +typedef ^ ^ IfW_FFWind_InitInputType FF - - - "scaling data" - @@ -115,22 +104,22 @@ typedef ^ ^ LOGICAL Linearize typedef ^ ^ LOGICAL Use4Dext - .FALSE. - "Flag that tells this module if an external module will pass it 4-D velocity grids." - typedef ^ ^ IntKi NumWindPoints - - - "Number of wind velocity points expected" - typedef ^ ^ LOGICAL UseInputFile - .TRUE. - "Should we read everthing from an input file, or do we get it some other way" - -typedef ^ ^ CHARACTER(1024) RootName - - - "RootName for writing output files" +typedef ^ ^ CHARACTER(1024) RootName - - - "RootName for writing output files" typedef ^ ^ InflowWind_InputFile PassedFileData - - - "If we don't use the input file, pass everything through this" - typedef ^ ^ Lidar_InitInputType lidar - - - "InitInput for lidar data" - typedef ^ ^ IfW_4Dext_InitInputType FDext - - - "InitInput for lidar data" - # Init Output -typedef ^ InitOutputType CHARACTER(ChanLen) WriteOutputHdr : - - "Names of output-to-file channels" - -typedef ^ ^ CHARACTER(ChanLen) WriteOutputUnt : - - "Units of output-to-file channels" - -typedef ^ ^ ProgDesc Ver - - - "Version information of InflowWind module" - -typedef ^ ^ WindFileMetaData WindFileInfo - - - "Meta data from the wind file" - -typedef ^ InitOutputType CHARACTER(LinChanLen) LinNames_y {:} - - "Names of the outputs used in linearization" - -typedef ^ InitOutputType CHARACTER(LinChanLen) LinNames_u {:} - - "Names of the inputs used in linearization" - -typedef ^ InitOutputType LOGICAL RotFrame_y {:} - - "Flag that tells FAST/MBC3 if the outputs used in linearization are in the rotating frame" - -typedef ^ InitOutputType LOGICAL RotFrame_u {:} - - "Flag that tells FAST/MBC3 if the inputs used in linearization are in the rotating frame" - -typedef ^ InitOutputType LOGICAL IsLoad_u {:} - - "Flag that tells FAST if the inputs used in linearization are loads (for preconditioning matrix)" - +typedef ^ InitOutputType CHARACTER(ChanLen) WriteOutputHdr : - - "Names of output-to-file channels" - +typedef ^ ^ CHARACTER(ChanLen) WriteOutputUnt : - - "Units of output-to-file channels" - +typedef ^ ^ ProgDesc Ver - - - "Version information of InflowWind module" - +typedef ^ ^ WindFileMetaData WindFileInfo - - - "Meta data from the wind file" - +typedef ^ InitOutputType CHARACTER(LinChanLen) LinNames_y {:} - - "Names of the outputs used in linearization" - +typedef ^ InitOutputType CHARACTER(LinChanLen) LinNames_u {:} - - "Names of the inputs used in linearization" - +typedef ^ InitOutputType LOGICAL RotFrame_y {:} - - "Flag that tells FAST/MBC3 if the outputs used in linearization are in the rotating frame" - +typedef ^ InitOutputType LOGICAL RotFrame_u {:} - - "Flag that tells FAST/MBC3 if the inputs used in linearization are in the rotating frame" - +typedef ^ InitOutputType LOGICAL IsLoad_u {:} - - "Flag that tells FAST if the inputs used in linearization are loads (for preconditioning matrix)" - # ..... Misc/Optimization variables................................................................................................. @@ -154,6 +143,7 @@ typedef ^ ParameterType CHARACTER(1024) RootFileNam typedef ^ ^ LOGICAL CTTS_Flag - .FALSE. - "determines if coherent turbulence is used" - typedef ^ ^ DbKi DT - - - "Time step for cont. state integration & disc. state update" seconds typedef ^ ^ ReKi PropagationDir - - - "Direction of wind propagation" radians +typedef ^ ^ ReKi VFlowAngle - - - "Vertical (upflow) angle" radians typedef ^ ^ ReKi RotToWind {3}{3} - - "Rotation matrix for rotating from the global XYZ coordinate system to the wind coordinate system (wind along X')" - typedef ^ ^ ReKi RotFromWind {3}{3} - - "Rotation matrix for rotating from the wind coordinate system (wind along X') back to the global XYZ coordinate system. Equal to TRANSPOSE(RotToWind)" - diff --git a/modules/inflowwind/src/InflowWind_Driver.f90 b/modules/inflowwind/src/InflowWind_Driver.f90 index edc7074f0d..99145795e8 100644 --- a/modules/inflowwind/src/InflowWind_Driver.f90 +++ b/modules/inflowwind/src/InflowWind_Driver.f90 @@ -167,7 +167,9 @@ PROGRAM InflowWind_Driver CLSettingsFlags%PointsOutputInit = .FALSE. ! Points output file not started CLSettingsFlags%Verbose = .FALSE. ! Turn on verbose error reporting? CLSettingsFlags%VVerbose = .FALSE. ! Turn on very verbose error reporting? - + CLSettingsFlags%WrHAWC = .FALSE. ! don't convert to HAWC format + CLSettingsFlags%WrBladed = .FALSE. ! don't convert to Bladed format + CLSettingsFlags%WrVTK = .FALSE. ! don't convert to VTK format ! Initialize the driver settings to their default values (same as the CL -- command line -- values) Settings = CLSettings @@ -234,7 +236,7 @@ PROGRAM InflowWind_Driver CALL ReadDvrIptFile( CLSettings%DvrIptFileName, SettingsFlags, Settings, ProgInfo, ErrStat, ErrMsg ) IF ( ErrStat >= AbortErrLev ) THEN CALL ProgAbort( ErrMsg ) - ELSEIF ( ErrStat /= 0 ) THEN + ELSEIF ( ErrStat /= ErrID_None ) THEN CALL WrScr( NewLine//ErrMsg ) ErrStat = ErrID_None ENDIF @@ -273,6 +275,9 @@ PROGRAM InflowWind_Driver ELSE + SettingsFlags%WrHAWC = .FALSE. + SettingsFlags%WrBladed = .FALSE. + SettingsFlags%WrVTK = .FALSE. ! VVerbose error reporting IF ( IfWDriver_Verbose >= 10_IntKi ) CALL WrScr('No driver input file used. Updating driver settings with command line arguments') @@ -449,10 +454,11 @@ PROGRAM InflowWind_Driver IF ( SettingsFlags%DvrIptFile ) THEN CALL GetRoot( Settings%DvrIptFileName, InflowWind_InitInp%RootName ) ELSE - InflowWind_InitInp%RootName = "" + CALL GetRoot( InflowWind_InitInp%InputFileName, InflowWind_InitInp%RootName ) + !InflowWind_InitInp%RootName = "" END IF - !CALL GetRoot( InflowWind_InitInp%InputFileName, InflowWind_InitInp%RootName ) - + InflowWind_InitInp%RootName = trim(InflowWind_InitInp%RootName)//'.IfW' + IF ( IfWDriver_Verbose >= 5_IntKi ) CALL WrScr('Calling InflowWind_Init...') @@ -478,12 +484,67 @@ PROGRAM InflowWind_Driver IF ( IfWDriver_Verbose >= 5_IntKi ) CALL WrScr(NewLine//'InflowWind_Init CALL returned without errors.'//NewLine) + ! Convert InflowWind file to HAWC format + IF (SettingsFlags%WrHAWC) THEN + + CALL InflowWind_Convert2HAWC( InflowWind_InitInp%RootName, InflowWind_p, InflowWind_MiscVars, ErrStat, ErrMsg ) + + IF (ErrStat > ErrID_None) THEN + CALL WrScr( TRIM(ErrMsg) ) + IF ( ErrStat >= AbortErrLev ) THEN + CALL DriverCleanup() + CALL ProgAbort( ErrMsg ) + ELSEIF ( IfWDriver_Verbose >= 7_IntKi ) THEN + CALL WrScr(NewLine//' InflowWind_Convert2HAWC returned: ErrStat: '//TRIM(Num2LStr(ErrStat))) + END IF + ELSE + IF ( IfWDriver_Verbose >= 5_IntKi ) CALL WrScr(NewLine//'InflowWind_Convert2HAWC CALL returned without errors.'//NewLine) + END IF + + END IF ! Write HAWC2 files + + + ! Convert InflowWind file to Native Bladed format + IF (SettingsFlags%WrBladed) THEN + CALL InflowWind_Convert2Bladed( InflowWind_InitInp%RootName, InflowWind_p, InflowWind_MiscVars, ErrStat, ErrMsg ) + + IF (ErrStat > ErrID_None) THEN + CALL WrScr( TRIM(ErrMsg) ) + IF ( ErrStat >= AbortErrLev ) THEN + CALL DriverCleanup() + CALL ProgAbort( ErrMsg ) + ELSEIF ( IfWDriver_Verbose >= 7_IntKi ) THEN + CALL WrScr(NewLine//' InflowWind_Convert2Bladed returned: ErrStat: '//TRIM(Num2LStr(ErrStat))) + END IF + ELSE + IF ( IfWDriver_Verbose >= 5_IntKi ) CALL WrScr(NewLine//'InflowWind_Convert2Bladed CALL returned without errors.'//NewLine) + END IF + END IF + + IF (SettingsFlags%WrVTK) THEN + CALL InflowWind_Convert2VTK( InflowWind_InitInp%RootName, InflowWind_p, InflowWind_MiscVars, ErrStat, ErrMsg ) + + IF (ErrStat > ErrID_None) THEN + CALL WrScr( TRIM(ErrMsg) ) + IF ( ErrStat >= AbortErrLev ) THEN + CALL DriverCleanup() + CALL ProgAbort( ErrMsg ) + ELSEIF ( IfWDriver_Verbose >= 7_IntKi ) THEN + CALL WrScr(NewLine//' InflowWind_Convert2VTK returned: ErrStat: '//TRIM(Num2LStr(ErrStat))) + END IF + ELSE + IF ( IfWDriver_Verbose >= 5_IntKi ) CALL WrScr(NewLine//'InflowWind_Convert2VTK CALL returned without errors.'//NewLine) + END IF + + END IF + !-------------------------------------------------------------------------------------------------------------------------------- !-=-=- Other Setup -=-=- !-------------------------------------------------------------------------------------------------------------------------------- ! Setup any additional things +if (SettingsFlags%WindGrid .or. SettingsFlags%PointsFile .or. SettingsFlags%FFTcalc) then ! we can skip all of this if we haven't asked for any output ! Timestep -- The timestep for the calling InflowWind_CalcOutput may need to be changed to what is in the file if the ! DT = DEFAULT option was used in the driver input file. This does not need to be changed in the InflowWind_Parameters @@ -796,7 +857,7 @@ PROGRAM InflowWind_Driver ENDIF - +end if !-------------------------------------------------------------------------------------------------------------------------------- diff --git a/modules/inflowwind/src/InflowWind_Driver_Subs.f90 b/modules/inflowwind/src/InflowWind_Driver_Subs.f90 index 8f1b045d5c..3d09ce625f 100644 --- a/modules/inflowwind/src/InflowWind_Driver_Subs.f90 +++ b/modules/inflowwind/src/InflowWind_Driver_Subs.f90 @@ -6,6 +6,7 @@ !********************************************************************************************************************************** ! LICENSING ! Copyright (C) 2015-2016 National Renewable Energy Laboratory +! Copyright (C) 2017 Envision Energy USA ! ! This file is part of InflowWind. ! @@ -73,6 +74,9 @@ SUBROUTINE DispHelpText( ErrStat, ErrMsg ) CALL WrScr(" white space delimited FILE") CALL WrScr(" "//SwChar//"v -- verbose output ") CALL WrScr(" "//SwChar//"vv -- very verbose output ") + CALL WrScr(" "//SwChar//"HAWC -- convert contents of to HAWC format ") + CALL WrScr(" "//SwChar//"Bladed -- convert contents of to Bladed format ") + CALL WrScr(" "//SwChar//"vtk -- convert contents of to vtk format ") CALL WrScr(" "//SwChar//"help -- print this help menu and exit") CALL WrScr("") CALL WrScr(" Notes:") @@ -246,7 +250,7 @@ SUBROUTINE ParseArg( CLSettings, CLFlags, ThisArgUC, ThisArg, ifwFlagSet, ErrSta IMPLICIT NONE ! Storing the arguments - TYPE( IfWDriver_Flags ), INTENT(INOUT) :: CLFlags ! Flags indicating which arguments were specified + TYPE( IfWDriver_Flags ), INTENT(INOUT) :: CLFlags ! Flags indicating which arguments were specified TYPE( IfWDriver_Settings ), INTENT(INOUT) :: CLSettings ! Arguments passed in CHARACTER(*), INTENT(IN ) :: ThisArgUC ! The current argument (upper case for testing) @@ -299,18 +303,27 @@ SUBROUTINE ParseArg( CLSettings, CLFlags, ThisArgUC, ThisArg, ifwFlagSet, ErrSta ! If no delimeters were given, than this option is simply a flag IF ( Delim1 == 0_IntKi ) THEN ! check to see if the filename is the name of the IfW input file - IF ( ThisArgUC(1:3) == "IFW" ) THEN + IF ( TRIM(ThisArgUC) == "IFW" ) THEN ifwFlagSet = .TRUE. ! More logic in the routine that calls this one to set things. RETURN - ELSEIF ( ThisArgUC(1:3) == "SUM" ) THEN + ELSEIF ( TRIM(ThisArgUC) == "SUM" ) THEN CLFlags%Summary = .TRUE. RETURN - ELSEIF ( ThisArgUC(1:2) == "VV" ) THEN + ELSEIF ( TRIM(ThisArgUC) == "VV" ) THEN CLFlags%VVerbose = .TRUE. RETURN - ELSEIF ( ThisArgUC(1:1) == "V" ) THEN + ELSEIF ( TRIM(ThisArgUC) == "V" ) THEN CLFlags%Verbose = .TRUE. RETURN + ELSEIF ( TRIM(ThisArgUC) == "HAWC" ) THEN + CLFlags%WrHAWC = .TRUE. + RETURN + ELSEIF ( TRIM(ThisArgUC) == "BLADED" ) THEN + CLFlags%WrBladed = .TRUE. + RETURN + ELSEIF ( TRIM(ThisArgUC) == "VTK" ) THEN + CLFlags%WrVTK = .TRUE. + RETURN ELSE CALL SetErrStat( ErrID_Warn," Unrecognized option '"//SwChar//TRIM(ThisArg)//"'. Ignoring. Use option "//SwChar//"help for list of options.", & ErrStat,ErrMsg,'ParseArg') @@ -703,7 +716,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat INTEGER(IntKi) :: ios !< I/O status INTEGER(IntKi) :: ErrStatTmp !< Temporary error status for calls CHARACTER(1024) :: ErrMsgTmp !< Temporary error messages for calls - + CHARACTER(*), PARAMETER :: RoutineName = 'ReadDvrIptFile' ! Initialize the echo file unit to -1 which is the default to prevent echoing, we will alter this based on user input UnEchoLocal = -1 @@ -714,7 +727,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL OpenFInpFile( UnIn, FileName, ErrStatTmp, ErrMsgTmp ) IF ( ErrStatTmp /= ErrID_None ) THEN CALL SetErrStat(ErrID_Fatal,' Failed to open InflowWind Driver input file: '//FileName, & - ErrStat,ErrMsg,'ReadDvrIptFile') + ErrStat,ErrMsg,RoutineName) CLOSE( UnIn ) RETURN ENDIF @@ -729,7 +742,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadCom( UnIn, FileName,' InflowWind Driver input file header line 1', ErrStatTmp, ErrMsgTmp ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CLOSE( UnIn ) RETURN ENDIF @@ -737,7 +750,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadCom( UnIn, FileName, 'InflowWind Driver input file header line 2', ErrStatTmp, ErrMsgTmp ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CLOSE( UnIn ) RETURN ENDIF @@ -746,7 +759,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat ! Echo Input Files. CALL ReadVar ( UnIn, FileName, EchoFileContents, 'Echo', 'Echo Input', ErrStatTmp, ErrMsgTmp ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CLOSE( UnIn ) RETURN ENDIF @@ -762,7 +775,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL GetNewUnit( UnEchoLocal ) CALL OpenEcho ( UnEchoLocal, EchoFileName, ErrStatTmp, ErrMsgTmp, ProgInfo ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CLOSE( UnIn ) RETURN ENDIF @@ -774,7 +787,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat ! Reread and echo CALL ReadCom( UnIn, FileName,' InflowWind Driver input file header line 1', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal, ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -783,7 +796,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadCom( UnIn, FileName, 'InflowWind Driver input file header line 2', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -793,7 +806,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat ! Echo Input Files. CALL ReadVar ( UnIn, FileName, EchoFileContents, 'Echo', 'Echo Input', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -811,18 +824,18 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat ! Header CALL ReadCom( UnIn, FileName,' Driver setup section, comment line', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN ENDIF - ! InflowWind input file + ! Name of InflowWind input file CALL ReadVar( UnIn, FileName,DvrSettings%IfWIptFileName,'IfWIptFileName',' InflowWind input filename', & ErrStatTmp,ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -830,12 +843,45 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat DvrFlags%IfWIptFile = .TRUE. ENDIF + !------------------------------------------------------------------------------------------------- + ! File Conversion Options section + !------------------------------------------------------------------------------------------------- + + ! Header + CALL ReadCom( UnIn, FileName,'File Conversion Options Section Header', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) + CALL SetErrStat(ErrStatTmp, ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + + ! WrHAWC + CALL ReadVar( UnIn, FileName, DvrFlags%WrHAWC, 'WrHAWC', 'Convert wind data to HAWC2 format?', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) + CALL SetErrStat(ErrStatTmp, ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + + ! WrBladed + CALL ReadVar( UnIn, FileName, DvrFlags%WrBladed, 'WrBladed', 'Convert wind data to Bladed format?', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) + CALL SetErrStat(ErrStatTmp, ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + + ! WrVTK + CALL ReadVar( UnIn, FileName, DvrFlags%WrVTK, 'WrVTK', 'Convert wind data to VTK format?', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) + CALL SetErrStat(ErrStatTmp, ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + + IF ( ErrStat >= AbortErrLev ) THEN + CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) + CLOSE( UnIn ) + RETURN + ENDIF + + !------------------------------------------------------------------------------------------------- + ! Tests of Interpolation Options section + !------------------------------------------------------------------------------------------------- + CALL ReadCom( UnIn, FileName,'Tests of Interpolation Options Section Header', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) + CALL SetErrStat(ErrStatTmp, ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + + ! Number of timesteps CALL ReadVar( UnIn, FileName,NumTimeStepsChr,'NumTimeStepsChr',' Character string for number of timesteps to read.', & ErrStatTmp,ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -864,7 +910,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadVar( UnIn, FileName,DvrSettings%TStart,'TStart',' Time in wind file to start parsing.', & ErrStatTmp,ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -873,51 +919,11 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat ENDIF - ! Summarize the extents in the windfile - CALL ReadVar( UnIn, FileName,DvrFlags%Summary,'Summary',' Summarize data extents in the windfile', & - ErrStatTmp,ErrMsgTmp, UnEchoLocal ) - IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') - CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) - CLOSE( UnIn ) - RETURN -! ELSE -! DvrFlags%Summary = .TRUE. - ENDIF - - - ! Summarize everything in a summary file/ - CALL ReadVar( UnIn, FileName,DvrFlags%SummaryFile,'SummaryFile',' Summarize the results in a .sum file', & - ErrStatTmp,ErrMsgTmp, UnEchoLocal ) - IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') - CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) - CLOSE( UnIn ) - RETURN -! ELSE -! DvrFlags%SummaryFile = .TRUE. - ENDIF - - - !------------------------------------------------------------------------------------------------- - ! InflowWind setup section - !------------------------------------------------------------------------------------------------- - - ! Header - CALL ReadCom( UnIn, FileName,' InflowWind setup section, comment line', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) - IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') - CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) - CLOSE( UnIn ) - RETURN - ENDIF - - ! DT -- Timestep size for the driver to take (or DEFAULT for what the file contains) CALL ReadVar( UnIn, FileName,DTChr,'DTChr',' Character string for Timestep size for the driver to take (or DEFAULT for what the file contains).', & ErrStatTmp,ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -940,6 +946,33 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat DvrFlags%DTDefault = .FALSE. ENDIF ENDIF + + + ! Summarize the extents in the windfile + CALL ReadVar( UnIn, FileName,DvrFlags%Summary,'Summary',' Summarize data extents in the windfile', & + ErrStatTmp,ErrMsgTmp, UnEchoLocal ) + IF ( ErrStatTmp /= ErrID_None ) THEN + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) + CLOSE( UnIn ) + RETURN +! ELSE +! DvrFlags%Summary = .TRUE. + ENDIF + + + ! Summarize everything in a summary file/ + CALL ReadVar( UnIn, FileName,DvrFlags%SummaryFile,'SummaryFile',' Summarize the results in a .sum file', & + ErrStatTmp,ErrMsgTmp, UnEchoLocal ) + IF ( ErrStatTmp /= ErrID_None ) THEN + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) + CLOSE( UnIn ) + RETURN +! ELSE +! DvrFlags%SummaryFile = .TRUE. + ENDIF + #ifdef UNUSED_INPUTFILE_LINES @@ -950,7 +983,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat ! Header CALL ReadCom( UnIn, FileName,' FFT calculations, comment line', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -961,7 +994,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadVar( UnIn, FileName,DvrFlags%FFTcalc,'FFTcalc',' Perform an FFT?', & ErrStatTmp,ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -974,7 +1007,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadAry ( UnIn, FileName, DvrSettings%FFTcoord, 3, 'FFTcoord', & 'FFT coordinate', ErrStatTmp, ErrMsgTmp, UnEchoLocal) IF ( ErrStat /= ErrID_None ) THEN - CALL SetErrStat( ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat( ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -982,7 +1015,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat ELSE CALL ReadCom( UnIn, FileName,' Skipping the FFT coordinate since not doint an FFT.', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -998,7 +1031,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat ! Header line CALL ReadCom( UnIn, FileName,' Points file input, comment line', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -1009,36 +1042,26 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadVar( UnIn, FileName,DvrFlags%PointsFile,'PointsFile',' Read a points file?', & ErrStatTmp,ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN ENDIF - IF ( DvrFlags%PointsFile ) THEN - ! Points input file - CALL ReadVar( UnIn, FileName,DvrSettings%PointsFileName,'PointsFileName',' Points file input filename', & - ErrStatTmp,ErrMsgTmp, UnEchoLocal ) - IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') - CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) - CLOSE( UnIn ) - RETURN - ENDIF - ELSE - ! Skip the next entry points file section. - CALL ReadCom( UnIn, FileName,' Skipping the points filename since not using it.', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) - IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') - CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) - CLOSE( UnIn ) - RETURN - ENDIF + ! Points input file (unused if .not. DvrFlags%PointsFile) + CALL ReadVar( UnIn, FileName,DvrSettings%PointsFileName,'PointsFileName',' Points file input filename', & + ErrStatTmp,ErrMsgTmp, UnEchoLocal ) + IF ( ErrStatTmp /= ErrID_None ) THEN + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) + CLOSE( UnIn ) + RETURN ENDIF + !------------------------------------------------------------------------------------------------- ! gridded data output !------------------------------------------------------------------------------------------------- @@ -1046,7 +1069,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat ! Header CALL ReadCom( UnIn, FileName,' Gridded data output, comment line', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -1057,7 +1080,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadVar( UnIn, FileName,DvrFlags%WindGrid,'WindGrid',' Output a grid of data?', & ErrStatTmp,ErrMsgTmp, UnEchoLocal ) IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -1071,7 +1094,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadAry ( UnIn, FileName, GridCtrCoord, 3, 'GridCtrCoord', & 'Coordinate of the center of the gridded data', ErrStatTmp, ErrMsgTmp, UnEchoLocal) IF ( ErrStat /= ErrID_None ) THEN - CALL SetErrStat( ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat( ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -1081,7 +1104,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadAry ( UnIn, FileName, TmpRealAr3, 3, 'GridDX, GridDY, GridDZ', & 'GridDX, GridDY, GridDZ', ErrStatTmp, ErrMsgTmp, UnEchoLocal) IF ( ErrStat /= ErrID_None ) THEN - CALL SetErrStat( ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat( ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -1100,7 +1123,7 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat CALL ReadAry ( UnIn, FileName, TmpIntAr3, 3, 'GridNx, GridNY, GridNZ', & 'GridNx, GridNY, GridNZ', ErrStatTmp, ErrMsgTmp, UnEchoLocal) IF ( ErrStat /= ErrID_None ) THEN - CALL SetErrStat( ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') + CALL SetErrStat( ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) CLOSE( UnIn ) RETURN @@ -1114,120 +1137,135 @@ SUBROUTINE ReadDvrIptFile( DvrFileName, DvrFlags, DvrSettings, ProgInfo, ErrStat DvrFlags%YRange = .TRUE. ! read in value for the Y direction gridding DvrFlags%ZRange = .TRUE. ! read in value for the Z direction gridding - ELSE - ! Skip the next three entries of the gridded data section. - CALL ReadCom( UnIn, FileName,' Skipping the gridded data section since not calculating it.', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) - IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') - CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) - CLOSE( UnIn ) - RETURN - ENDIF - CALL ReadCom( UnIn, FileName,' Skipping the gridded data section since not calculating it.', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) - IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') - CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) - CLOSE( UnIn ) - RETURN + + + ! Check that valid values of Dx, Dy, and Dz were read in. + ! Check GridDx + IF ( EqualRealNos(DvrSettings%GridDelta(1), 0.0_ReKi) ) THEN + DvrFlags%Dx = .FALSE. + DvrFlags%XRange = .FALSE. + DvrSettings%GridDelta(1) = 0.0_ReKi + CALL SetErrStat(ErrID_Warn,' Grid spacing in X direction is 0. Ignoring.',ErrStat,ErrMsg,RoutineName) ENDIF - CALL ReadCom( UnIn, FileName,' Skipping the gridded data section since not calculating it.', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) - IF ( ErrStatTmp /= ErrID_None ) THEN - CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,'ReadDvrIptFile') - CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) - CLOSE( UnIn ) - RETURN - ENDIF - ENDIF - ! Check that valid values of Dx, Dy, and Dz were read in. - ! Check GridDx - IF ( EqualRealNos(DvrSettings%GridDelta(1), 0.0_ReKi) ) THEN - DvrFlags%Dx = .FALSE. - DvrFlags%XRange = .FALSE. - DvrSettings%GridDelta(1) = 0.0_ReKi - CALL SetErrStat(ErrID_Warn,' Grid spacing in X direction is 0. Ignoring.',ErrStat,ErrMsg,'ReadDvrIptFile') - ENDIF - - ! Check GridDy - IF ( EqualRealNos(DvrSettings%GridDelta(2), 0.0_ReKi) ) THEN - DvrFlags%Dy = .FALSE. - DvrFlags%YRange = .FALSE. - DvrSettings%GridDelta(2) = 0.0_ReKi - CALL SetErrStat(ErrID_Warn,' Grid spacing in Y direction is 0. Ignoring.',ErrStat,ErrMsg,'ReadDvrIptFile') - ENDIF - - ! Check GridDz - IF ( EqualRealNos(DvrSettings%GridDelta(3), 0.0_ReKi) ) THEN - DvrFlags%Dz = .FALSE. - DvrFlags%ZRange = .FALSE. - DvrSettings%GridDelta(3) = 0.0_ReKi - CALL SetErrStat(ErrID_Warn,' Grid spacing in Z direction is 0. Ignoring.',ErrStat,ErrMsg,'ReadDvrIptFile') - ENDIF + ! Check GridDy + IF ( EqualRealNos(DvrSettings%GridDelta(2), 0.0_ReKi) ) THEN + DvrFlags%Dy = .FALSE. + DvrFlags%YRange = .FALSE. + DvrSettings%GridDelta(2) = 0.0_ReKi + CALL SetErrStat(ErrID_Warn,' Grid spacing in Y direction is 0. Ignoring.',ErrStat,ErrMsg,RoutineName) + ENDIF + ! Check GridDz + IF ( EqualRealNos(DvrSettings%GridDelta(3), 0.0_ReKi) ) THEN + DvrFlags%Dz = .FALSE. + DvrFlags%ZRange = .FALSE. + DvrSettings%GridDelta(3) = 0.0_ReKi + CALL SetErrStat(ErrID_Warn,' Grid spacing in Z direction is 0. Ignoring.',ErrStat,ErrMsg,RoutineName) + ENDIF + + + ! Now need to set the XRange, YRange, and ZRange values based on what we read in + ! For XRange, check that we have an actual value for the number of points + IF ( (DvrSettings%GridN(1) <= 0) .OR. (.NOT. DvrFlags%XRange) ) THEN + DvrSettings%XRange = GridCtrCoord(1) + DvrFlags%Dx = .FALSE. + DvrFlags%XRange = .FALSE. + + IF ( DvrSettings%GridN(1) < 0 ) THEN + CALL SetErrStat(ErrID_Warn,' Negative number for number of grid points along X direction. Ignoring.',ErrStat,ErrMsg, RoutineName) + ELSE + CALL SetErrStat(ErrID_Warn,' No points along X direction. Ignoring.',ErrStat,ErrMsg, RoutineName) + ENDIF - ! Now need to set the XRange, YRange, and ZRange values based on what we read in - ! For XRange, check that we have an actual value for the number of points - IF ( (DvrSettings%GridN(1) <= 0) .OR. (.NOT. DvrFlags%XRange) ) THEN - DvrSettings%XRange = GridCtrCoord(1) - DvrFlags%Dx = .FALSE. - DvrFlags%XRange = .FALSE. + DvrSettings%GridN(1) = 1_IntKi ! Set to 1 for easier indexing. - IF ( DvrSettings%GridN(1) < 0 ) THEN - CALL SetErrStat(ErrID_Warn,' Negative number for number of grid points along X direction. Ignoring.',ErrStat,ErrMsg, 'ReadDvrIptFile') ELSE - CALL SetErrStat(ErrID_Warn,' No points along X direction. Ignoring.',ErrStat,ErrMsg, 'ReadDvrIptFile') + ! Set the XRange values + DvrSettings%XRange(1) = GridCtrCoord(1) - (REAL(DvrSettings%GridN(1) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(1) + DvrSettings%XRange(2) = GridCtrCoord(1) + (REAL(DvrSettings%GridN(1) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(1) + DvrFlags%XRange = .TRUE. ENDIF - DvrSettings%GridN(1) = 1_IntKi ! Set to 1 for easier indexing. - ELSE - ! Set the XRange values - DvrSettings%XRange(1) = GridCtrCoord(1) - (REAL(DvrSettings%GridN(1) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(1) - DvrSettings%XRange(2) = GridCtrCoord(1) + (REAL(DvrSettings%GridN(1) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(1) - DvrFlags%XRange = .TRUE. - ENDIF + ! For YRange, check that we have an actual value for the number of points + IF ( (DvrSettings%GridN(2) <= 0) .OR. (.NOT. DvrFlags%YRange) ) THEN + DvrSettings%YRange = GridCtrCoord(2) + DvrFlags%Dy = .FALSE. + DvrFlags%YRange = .FALSE. + IF ( DvrSettings%GridN(2) < 0 ) THEN + CALL SetErrStat(ErrID_Warn,' Negative number for number of grid points along Y direction. Ignoring.',ErrStat,ErrMsg, RoutineName) + ELSE + CALL SetErrStat(ErrID_Warn,' No points along Y direction. Ignoring.',ErrStat,ErrMsg, RoutineName) + ENDIF - ! For YRange, check that we have an actual value for the number of points - IF ( (DvrSettings%GridN(2) <= 0) .OR. (.NOT. DvrFlags%YRange) ) THEN - DvrSettings%YRange = GridCtrCoord(2) - DvrFlags%Dy = .FALSE. - DvrFlags%YRange = .FALSE. + DvrSettings%GridN(2) = 1_IntKi ! Set to 1 for easier indexing. - IF ( DvrSettings%GridN(2) < 0 ) THEN - CALL SetErrStat(ErrID_Warn,' Negative number for number of grid points along Y direction. Ignoring.',ErrStat,ErrMsg, 'ReadDvrIptFile') ELSE - CALL SetErrStat(ErrID_Warn,' No points along Y direction. Ignoring.',ErrStat,ErrMsg, 'ReadDvrIptFile') + ! Set the YRange values + DvrSettings%YRange(1) = GridCtrCoord(2) - (REAL(DvrSettings%GridN(2) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(2) + DvrSettings%YRange(2) = GridCtrCoord(2) + (REAL(DvrSettings%GridN(2) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(2) + DvrFlags%YRange = .TRUE. ENDIF - DvrSettings%GridN(2) = 1_IntKi ! Set to 1 for easier indexing. + ! For ZRange, check that we have an actual value for the number of points, set to ctr point if negative or zero. + IF ( (DvrSettings%GridN(3) <= 0) .OR. (.NOT. DvrFlags%ZRange) ) THEN + DvrSettings%ZRange = abs(GridCtrCoord(3)) ! shouldn't have a negative value anyhow + DvrFlags%Dz = .FALSE. + DvrFlags%ZRange = .FALSE. - ELSE - ! Set the YRange values - DvrSettings%YRange(1) = GridCtrCoord(2) - (REAL(DvrSettings%GridN(2) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(2) - DvrSettings%YRange(2) = GridCtrCoord(2) + (REAL(DvrSettings%GridN(2) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(2) - DvrFlags%YRange = .TRUE. - ENDIF + IF ( DvrSettings%GridN(3) < 0 ) THEN + CALL SetErrStat(ErrID_Warn,' Negative number for number of grid points along Z direction. Ignoring.',ErrStat,ErrMsg, RoutineName) + ELSE + CALL SetErrStat(ErrID_Warn,' No points along Z direction. Ignoring.',ErrStat,ErrMsg, 'ReadDvrIptFile') + ENDIF - ! For ZRange, check that we have an actual value for the number of points, set to ctr point if negative or zero. - IF ( (DvrSettings%GridN(3) <= 0) .OR. (.NOT. DvrFlags%ZRange) ) THEN - DvrSettings%ZRange = abs(GridCtrCoord(3)) ! shouldn't have a negative value anyhow - DvrFlags%Dz = .FALSE. - DvrFlags%ZRange = .FALSE. + DvrSettings%GridN(3) = 1_IntKi ! Set to 1 for easier indexing. - IF ( DvrSettings%GridN(3) < 0 ) THEN - CALL SetErrStat(ErrID_Warn,' Negative number for number of grid points along Z direction. Ignoring.',ErrStat,ErrMsg, 'ReadDvrIptFile') ELSE - CALL SetErrStat(ErrID_Warn,' No points along Z direction. Ignoring.',ErrStat,ErrMsg, 'ReadDvrIptFile') + ! Set the ZRange values + DvrSettings%ZRange(1) = GridCtrCoord(3) - (REAL(DvrSettings%GridN(3) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(3) + DvrSettings%ZRange(2) = GridCtrCoord(3) + (REAL(DvrSettings%GridN(3) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(3) + DvrFlags%ZRange = .TRUE. + ENDIF + + + ELSE ! read these lines as comments (actually, we don't need to read them) + + + DvrSettings%GridDelta = 0.0_ReKi + DvrFlags%Dx = .FALSE. + DvrFlags%Dy = .FALSE. + DvrFlags%Dz = .FALSE. + + DvrSettings%GridN = 0.0_ReKi + DvrFlags%XRange = .FALSE. + DvrFlags%YRange = .FALSE. + DvrFlags%ZRange = .FALSE. + + ! Skip the next three entries of the gridded data section. + CALL ReadCom( UnIn, FileName,' Skipping the gridded data section since not calculating it.', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) + IF ( ErrStatTmp /= ErrID_None ) THEN + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) + CLOSE( UnIn ) + RETURN + ENDIF + CALL ReadCom( UnIn, FileName,' Skipping the gridded data section since not calculating it.', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) + IF ( ErrStatTmp /= ErrID_None ) THEN + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) + CLOSE( UnIn ) + RETURN + ENDIF + CALL ReadCom( UnIn, FileName,' Skipping the gridded data section since not calculating it.', ErrStatTmp, ErrMsgTmp, UnEchoLocal ) + IF ( ErrStatTmp /= ErrID_None ) THEN + CALL SetErrStat(ErrID_Fatal,ErrMsgTmp,ErrStat,ErrMsg,RoutineName) + CALL CleanupEchoFile( EchoFileContents, UnEchoLocal ) + CLOSE( UnIn ) + RETURN ENDIF - - DvrSettings%GridN(3) = 1_IntKi ! Set to 1 for easier indexing. - - ELSE - ! Set the ZRange values - DvrSettings%ZRange(1) = GridCtrCoord(3) - (REAL(DvrSettings%GridN(3) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(3) - DvrSettings%ZRange(2) = GridCtrCoord(3) + (REAL(DvrSettings%GridN(3) - 1_IntKi ) / 2.0_ReKi ) * DvrSettings%GridDelta(3) - DvrFlags%ZRange = .TRUE. ENDIF @@ -1285,6 +1323,10 @@ SUBROUTINE UpdateSettingsWithCL( DvrFlags, DvrSettings, CLFlags, CLSettings, DVR ErrMsgTmp = '' + DvrFlags%WrHAWC = DvrFlags%WrHAWC .or. CLFlags%WrHAWC ! create file if specified in either place + DvrFlags%WrBladed = DvrFlags%WrBladed .or. CLFlags%WrBladed ! create file if specified in either place + DvrFlags%WrVTK = DvrFlags%WrVTK .or. CLFlags%WrVTK ! create file if specified in either place + ! ! Due to the complexity, we are handling overwriting driver input file settings with ! ! command line settings and the instance where no driver input file is read separately. ! IF ( DVRIPT .AND. DvrFlags%WindGrid ) THEN @@ -2227,7 +2269,7 @@ SUBROUTINE WindGridVel_OutputWrite (FileUnit, FileName, Initialized, Settings, G INTEGER(IntKi) :: I !< generic counter - WindVelFmt = "(F14.7,3x,F14.7,3x,F14.7,3x,F14.7,3x,F14.7,3x,F14.7)" + WindVelFmt = "(3(F14.7,3x),3(F10.3,3x))" ErrMsg = '' ErrStat = ErrID_None @@ -2367,6 +2409,14 @@ SUBROUTINE printSettings( DvrFlags, DvrSettings ) ELSE CALL WrScr(' NumTimeSteps: '//FLAG(DvrFlags%NumTimeSteps)// ' '//TRIM(Num2LStr(DvrSettings%NumTimeSteps))) ENDIF + CALL WrScr(' FFTcalc: '//FLAG(DvrFlags%FFTcalc)// ' ['//TRIM(Num2LStr(DvrSettings%FFTcoord(1)))//', '& + //TRIM(Num2LStr(DvrSettings%FFTcoord(2)))//', '& + //TRIM(Num2LStr(DvrSettings%FFTcoord(3)))//']') + CALL WrScr(' WindGrid: '//FLAG(DvrFlags%WindGrid)) +if (DvrFlags%WindGrid) then + CALL WrScr(' GridN: ' //TRIM(Num2LStr(DvrSettings%GridN(1)))//' x ' & + //TRIM(Num2LStr(DvrSettings%GridN(2)))//' x ' & + //TRIM(Num2LStr(DvrSettings%GridN(3)))) CALL WrScr(' XRange: '//FLAG(DvrFlags%XRange)// ' '//TRIM(Num2LStr(DvrSettings%XRange(1)))//' -- ' & //TRIM(Num2LStr(DvrSettings%XRange(2)))) CALL WrScr(' YRange: '//FLAG(DvrFlags%YRange)// ' '//TRIM(Num2LStr(DvrSettings%YRange(1)))//' -- ' & @@ -2376,14 +2426,8 @@ SUBROUTINE printSettings( DvrFlags, DvrSettings ) CALL WrScr(' Dx: '//FLAG(DvrFlags%Dx)// ' '//TRIM(Num2LStr(DvrSettings%GridDelta(1)))) CALL WrScr(' Dy: '//FLAG(DvrFlags%Dy)// ' '//TRIM(Num2LStr(DvrSettings%GridDelta(2)))) CALL WrScr(' Dz: '//FLAG(DvrFlags%Dz)// ' '//TRIM(Num2LStr(DvrSettings%GridDelta(3)))) - CALL WrScr(' FFTcalc: '//FLAG(DvrFlags%FFTcalc)// ' ['//TRIM(Num2LStr(DvrSettings%FFTcoord(1)))//', '& - //TRIM(Num2LStr(DvrSettings%FFTcoord(2)))//', '& - //TRIM(Num2LStr(DvrSettings%FFTcoord(3)))//']') - CALL WrScr(' WindGrid: '//FLAG(DvrFlags%WindGrid)) - CALL WrScr(' GridN: ' //TRIM(Num2LStr(DvrSettings%GridN(1)))//' x ' & - //TRIM(Num2LStr(DvrSettings%GridN(2)))//' x ' & - //TRIM(Num2LStr(DvrSettings%GridN(3)))) CALL WrScr(' WindGridOutputInit: '//FLAG(DvrFlags%WindGridOutputInit)//' Unit #: '//TRIM(Num2LStr(DvrSettings%WindGridOutputUnit))) +end if CALL WrScr(' FFTOutputInit: '//FLAG(DvrFlags%FFTOutputInit)// ' Unit #: '//TRIM(Num2LStr(DvrSettings%FFTOutputUnit))) CALL WrScr(' PointsOutputInit: '//FLAG(DvrFlags%PointsOutputInit)// ' Unit #: '//TRIM(Num2LStr(DvrSettings%PointsOutputUnit))) RETURN diff --git a/modules/inflowwind/src/InflowWind_Driver_Types.f90 b/modules/inflowwind/src/InflowWind_Driver_Types.f90 index a06ab3e3e2..26bad3c3d7 100644 --- a/modules/inflowwind/src/InflowWind_Driver_Types.f90 +++ b/modules/inflowwind/src/InflowWind_Driver_Types.f90 @@ -66,6 +66,10 @@ MODULE InflowWind_Driver_Types LOGICAL :: FFTOutputInit = .FALSE. !< Is the FFT output file initialized LOGICAL :: Verbose = .FALSE. !< Verbose error reporting LOGICAL :: VVerbose = .FALSE. !< Very Verbose error reporting + + LOGICAL :: WrHAWC = .FALSE. !< Requested file conversion to HAWC2 format? + LOGICAL :: WrBladed = .FALSE. !< Requested file conversion to Bladed format? + LOGICAL :: WrVTK = .FALSE. !< Requested file output as VTK? END TYPE IfWDriver_Flags diff --git a/modules/inflowwind/src/InflowWind_Subs.f90 b/modules/inflowwind/src/InflowWind_Subs.f90 index 8a6f457e7a..9b3dbef73b 100644 --- a/modules/inflowwind/src/InflowWind_Subs.f90 +++ b/modules/inflowwind/src/InflowWind_Subs.f90 @@ -143,8 +143,6 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err ! Local variables INTEGER(IntKi) :: UnitInput !< Unit number for the input file INTEGER(IntKi) :: UnitEcho !< The local unit number for this module's echo file - CHARACTER(1024) :: TmpPath !< Temporary storage for relative path name - CHARACTER(1024) :: TmpFmt !< Temporary storage for format statement CHARACTER(35) :: Frmt !< Output format for logical parameters. (matches NWTC Subroutine Library format) @@ -154,6 +152,8 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err CHARACTER(1024) :: PriPath ! Path name of the primary file + InputFileData%VFlowAngle = 0.0 ! default vertical flow angle + ! Initialize local data UnitEcho = -1 @@ -272,7 +272,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err ! Read WindType CALL ReadVar( UnitInput, InputFileName, InputFileData%WindType, 'WindType', & 'switch for wind file type (1=steady; 2=uniform; 3=binary TurbSim FF; '//& - '4=binary Bladed-style FF; 5=HAWC format; 6=User defined)', & + '4=binary Bladed-style FF; 5=HAWC format; 6=User defined; 7=native Bladed FF)', & TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) THEN @@ -476,7 +476,6 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err RETURN ENDIF IF ( PathIsRelative( InputFileData%BladedFF_FileName ) ) InputFileData%BladedFF_FileName = TRIM(PriPath)//TRIM(InputFileData%BladedFF_FileName) - InputFileData%BladedFF_FileName = TRIM(InputFileData%BladedFF_FileName)//'.wnd' ! Read TowerFileFlag CALL ReadVar( UnitInput, InputFileName, InputFileData%BladedFF_TowerFile, 'TowerFileFlag', & @@ -631,7 +630,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_RefHt - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_RefHt, 'HAWC_RefHt', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%RefHt, 'HAWC_RefHt', & 'Reference (hub) height of the grid', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -654,7 +653,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_ScaleMethod - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_ScaleMethod, 'HAWC_ScaleMethod', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%ScaleMethod, 'HAWC_ScaleMethod', & 'Turbulence scaling method [0=none, 1=direct scaling, 2= calculate scaling '// & 'factor based on a desired standard deviation]', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) @@ -664,7 +663,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_SFx - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SFx, 'HAWC_SFx', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%SF(1), 'HAWC_SFx', & 'Turbulence scaling factor for the x direction [ScaleMethod=1]', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -673,7 +672,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_SFy - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SFy, 'HAWC_SFy', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%SF(2), 'HAWC_SFy', & 'Turbulence scaling factor for the y direction [ScaleMethod=1]', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -682,7 +681,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_SFz - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SFz, 'HAWC_SFz', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%SF(3), 'HAWC_SFz', & 'Turbulence scaling factor for the z direction [ScaleMethod=1]', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -691,7 +690,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_SigmaFx - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SigmaFx, 'HAWC_SigmaFx', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%SigmaF(1), 'HAWC_SigmaFx', & 'Turbulence standard deviation to calculate scaling from in x direction [ScaleMethod=2]', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -700,7 +699,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_SigmaFy - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SigmaFy, 'HAWC_SigmaFy', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%SigmaF(2), 'HAWC_SigmaFy', & 'Turbulence standard deviation to calculate scaling from in y direction [ScaleMethod=2]', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -709,7 +708,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_SigmaFz - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SigmaFz, 'HAWC_SigmaFz', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%SigmaF(3), 'HAWC_SigmaFz', & 'Turbulence standard deviation to calculate scaling from in z direction [ScaleMethod=2]', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -721,7 +720,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err !FIXME: TStart has no comment ! Read HAWC_TStart - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_TStart, 'HAWC_TStart', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%TStart, 'HAWC_TStart', & '', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -731,7 +730,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err !FIXME: TEnd has no comment ! Read HAWC_TEnd - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_TEnd, 'HAWC_TEnd', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%TEnd, 'HAWC_TEnd', & '', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -754,7 +753,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_URef - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_URef, 'HAWC_URef', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%URef, 'HAWC_URef', & 'Mean u-component wind speed at the reference height', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -763,7 +762,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_ProfileType - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_ProfileType, 'HAWC_ProfileType', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%WindProfileType, 'HAWC_ProfileType', & 'Wind profile type (0=constant;1=logarithmic;2=power law)', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -772,7 +771,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_PLExp - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_PLExp, 'HAWC_PLExp', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%PLExp, 'HAWC_PLExp', & 'Power law exponent (used for PL wind profile type only)', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -781,7 +780,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_Z0 - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_Z0, 'HAWC_Z0', & + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%Z0, 'HAWC_Z0', & 'Surface roughness length (used for LOG wind profile type only)', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -790,9 +789,8 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err END IF ! Read HAWC_InitPosition (Shift of wind box) NOTE: This an optional input!!!! - InputFileData%HAWC_InitPosition(2:3) = 0.0_ReKi ! We are only using X, so only read in one. The data can handle 3 coords - CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_InitPosition(1), 'HAWC_Position', & - 'Initial position of the HAWC wind file (shift along X usually)', TmpErrStat, TmpErrMsg, UnitEcho ) + CALL ReadVar( UnitInput, InputFileName, InputFileData%FF%XOffset, 'HAWC_Position', & + 'Initial position of the wind file (shift along X)', TmpErrStat, TmpErrMsg, UnitEcho ) if (TmpErrStat == ErrID_None) then !---------------------- OUTPUT -------------------------------------------------- CALL ReadCom( UnitInput, InputFileName, 'Section Header: Output', TmpErrStat, TmpErrMsg, UnitEcho ) @@ -802,7 +800,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err RETURN END IF else - InputFileData%HAWC_InitPosition = 0.0_ReKi + InputFileData%FF%XOffset = 0.0_ReKi TmpErrStat = ErrID_None ! reset TmpErrMsg = "" ! NOTE: since we read in a line that wasn't a number, what we actually read was the header. @@ -890,10 +888,6 @@ SUBROUTINE InflowWind_ValidateInput( InitInp, InputFileData, ErrStat, ErrMsg ) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message from this subroutine - ! Temporary variables - INTEGER(IntKi) :: TmpErrStat !< Temporary error status for subroutine and function calls - CHARACTER(ErrMsgLen) :: TmpErrMsg !< Temporary error message for subroutine and function calls - ! Local variables INTEGER(IntKi) :: I !< Generic counter @@ -911,12 +905,13 @@ SUBROUTINE InflowWind_ValidateInput( InitInp, InputFileData, ErrStat, ErrMsg ) ! WindType IF ( InputFileData%WindType <= Undef_WindNumber .OR. InputFileData%WindType > Highest_WindNumber ) THEN CALL SetErrStat(ErrID_Fatal,' Invalid WindType specified. Only values of '// & - TRIM(Num2LStr( Steady_WindNumber ))//','// & - TRIM(Num2LStr( Uniform_WindNumber ))//','// & - TRIM(Num2LStr( TSFF_WindNumber ))//','// & - TRIM(Num2LStr( BladedFF_WindNumber ))//','// & - TRIM(Num2LStr( HAWC_WindNumber ))//', or '// & - TRIM(Num2LStr( User_WindNumber ))//' are supported.', & + TRIM(Num2LStr( Steady_WindNumber ))//','// & + TRIM(Num2LStr( Uniform_WindNumber ))//','// & + TRIM(Num2LStr( TSFF_WindNumber ))//','// & + TRIM(Num2LStr( BladedFF_WindNumber ))//','// & + TRIM(Num2LStr( BladedFF_Shr_WindNumber ))//','// & + TRIM(Num2LStr( HAWC_WindNumber ))//', or '// & + TRIM(Num2LStr( User_WindNumber ))//' are supported.', & ErrStat,ErrMsg,RoutineName) RETURN ENDIF @@ -967,7 +962,7 @@ SUBROUTINE InflowWind_ValidateInput( InitInp, InputFileData, ErrStat, ErrMsg ) CASE ( TSFF_WindNumber ) CALL TSFF_ValidateInput() - CASE ( BladedFF_WindNumber ) + CASE ( BladedFF_WindNumber, BladedFF_Shr_WindNumber ) CALL BladedFF_ValidateInput() CASE ( HAWC_WindNumber ) @@ -976,7 +971,7 @@ SUBROUTINE InflowWind_ValidateInput( InitInp, InputFileData, ErrStat, ErrMsg ) CASE ( User_WindNumber ) CALL User_ValidateInput() - CASE (FDext_WindNumber) + CASE ( FDext_WindNumber ) IF ( .not. InitInp%Use4Dext) call SetErrStat(ErrID_Fatal,'4D external wind file is valid only with FAST.Farm',ErrStat,ErrMsg,RoutineName) CASE DEFAULT ! keep this check to make sure that all new wind types have been accounted for CALL SetErrStat(ErrID_Fatal,' Undefined wind type.',ErrStat,ErrMsg,RoutineName) @@ -1089,12 +1084,12 @@ SUBROUTINE BladedFF_ValidateInput() ! Local variables LOGICAL :: TmpFileExist - ! Check that the filename requested actually exists - INQUIRE( file=InputFileData%BladedFF_FileName, exist=TmpFileExist ) - IF ( .NOT. TmpFileExist ) THEN - CALL SetErrStat( ErrID_Fatal," Cannot find Bladed-style full-field wind input file: '"//TRIM(InputFileData%BladedFF_FileName)//"'", & - ErrStat,ErrMsg,RoutineName) - ENDIF + ! ! Check that the filename requested actually exists (this happens in the BladedFF submodule...) + !INQUIRE( file=InputFileData%BladedFF_FileName, exist=TmpFileExist ) + !IF ( .NOT. TmpFileExist ) THEN + ! CALL SetErrStat( ErrID_Fatal," Cannot find Bladed-style full-field wind input file: '"//TRIM(InputFileData%BladedFF_FileName)//"'", & + ! ErrStat,ErrMsg,RoutineName) + !ENDIF RETURN END SUBROUTINE BladedFF_ValidateInput @@ -1167,122 +1162,7 @@ SUBROUTINE HAWC_ValidateInput() ENDIF - ! Check that the number of grids make some sense - IF ( InputFileData%HAWC_nx <= 0_IntKi ) THEN - CALL SetErrStat( ErrID_Fatal,' Number of grids in the x-direction of HAWC wind files (nx) must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - IF ( InputFileData%HAWC_ny <= 0_IntKi ) THEN - CALL SetErrStat( ErrID_Fatal,' Number of grids in the y-direction of HAWC wind files (ny) must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - IF ( InputFileData%HAWC_nz <= 0_IntKi ) THEN - CALL SetErrStat( ErrID_Fatal,' Number of grids in the z-direction of HAWC wind files (nz) must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - - ! Check that the distance between points is positive - IF ( InputFileData%HAWC_dx <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' Distance between points in the x-direction of HAWC wind files (dx) must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - IF ( InputFileData%HAWC_dy <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' Distance between points in the y-direction of HAWC wind files (dy) must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - IF ( InputFileData%HAWC_dz <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' Distance between points in the z-direction of HAWC wind files (dz) must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - - ! Check that RefHt is positive - IF ( InputFileData%HAWC_RefHt <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' Reference height (RefHt) for HAWC winds must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - - !---------------------- - ! Scaling method info - !---------------------- - - IF ( ( InputFileData%HAWC_ScaleMethod < 0_IntKi ) .OR. ( InputFileData%HAWC_ScaleMethod > 3_IntKi ) ) THEN - CALL SetErrStat( ErrID_Fatal,' The scaling method (ScaleMethod) for HAWC winds can only be 0, 1, or 2.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - ! Check the other scaling parameters - SELECT CASE( InputFileData%HAWC_ScaleMethod ) - - CASE ( 1 ) - - IF ( InputFileData%HAWC_SFx <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SFx must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - IF ( InputFileData%HAWC_SFy <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SFy must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - IF ( InputFileData%HAWC_SFz <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SFz must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - CASE ( 2 ) - - IF ( InputFileData%HAWC_SigmaFx <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SigmaFx must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - IF ( InputFileData%HAWC_SigmaFy <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SigmaFy must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - IF ( InputFileData%HAWC_SigmaFz <= 0.0_ReKi ) THEN - CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SigmaFz must be greater than zero.', & - ErrStat,ErrMsg,RoutineName) - ENDIF - - END SELECT - - - ! Start and end times of the scaling, but only if ScaleMethod is set. - IF ( ( InputFileData%HAWC_ScaleMethod == 1_IntKi ) .OR. (InputFileData%HAWC_ScaleMethod == 2_IntKi ) ) THEN - ! Check that the start time is >= 0. NOTE: this may be an invalid test. - !IF ( InputFileData%HAWC_TStart < 0.0_ReKi ) THEN - ! CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling TStart must be zero or positive.', & - ! ErrStat,ErrMsg,RoutineName) - !ENDIF - - !IF ( InputFileData%HAWC_TStart < 0.0_ReKi ) THEN - ! CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling TStart must be zero or positive.', & - ! ErrStat,ErrMsg,RoutineName) - !ENDIF - - !IF ( InputFileData%HAWC_TStart > InputFileData%HAWC_TEnd ) THEN - ! CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling start time must occur before the end time.', & - ! ErrStat,ErrMsg,RoutineName) - !ENDIF - -!FIXME: How do we want to handle having the start and end times both being exactly zero? Is that a do not apply, or apply for all time? -!FIXME: What about TStart == TEnd ? - !IF ( EqualRealNos( InputFileData%HAWC_TStart, InputFileData%HAWC_TEnd ) ) THEN - ! CALL SetErrStat( ErrID_Severe,' Start and end time for HAWC wind scaling are identical. No time elapses.', & - ! ErrStat,ErrMsg,RoutineName) - !ENDIF - - ENDIF + ! we'll check the rest of these in the HAWC_Init() routine RETURN @@ -1345,7 +1225,8 @@ SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrM !----------------------------------------------------------------- ! Copy over the general information that applies to everything !----------------------------------------------------------------- - + p%CTTS_Flag = .FALSE. + ! Copy the WindType over. p%WindType = InputFileData%WindType @@ -1356,6 +1237,7 @@ SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrM p%PropagationDir = D2R * InputFileData%PropagationDir CALL MPi2Pi( p%PropagationDir ) ! Shift if necessary so that the value is between -pi and pi + p%VFlowAngle = D2R * InputFileData%VFlowAngle ! Copy over the list of wind coordinates. Move the arrays to the new one. p%NWindVel = InputFileData%NWindVel @@ -1411,11 +1293,58 @@ SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrM !! was converted from degrees to radians already). !! @note The PropagationDir is given in Meteorological \f$\Delta\phi\f$, so this is the negative of the \f$\Delta\phi\f$ !! for polar coordinates + !! + !! An upflow angle rotation is also included in the direction cosine matrix. + !! + !! The rotation matrix _RotToWind_, (\f$ M \f$), rotates the spatial points (\f$ [ X~ Y~ Z ] \f$) into the coordinate system + !! the wind (\f$ [ X'~ Y'~ Z' ] \f$). This is a rotation about the point \f$ [ 0~ 0~ H ] \f$ (H = hub reference height), first + !! about the \f$ Y\f$-axis (in the negative direction), followed by a rotation about \f$Z\f$-axis (also in the negative direction + !! due to the meteorological convention used). In the implimentation, the rotation is about the coordinate \f$ [ 0~ 0~ H ] \f$ + !! where \f$ H \f$ is the reference height of the turbine (hub height). + !! + !! \f$ [X'~ Y'~ Z'] = M \left( [X~ Y~ Z] - [ 0~ 0~ H] \right) + [ 0~ 0~ H ] \f$ + !! + !! where + !! + !! \f{eqnarray*}{ + !! M & = & R(-\theta_y) R(-\theta_z) + !! & = & \begin{bmatrix} \cos(-\theta_y) & 0 & -\sin(-\theta_y) \\ + !! 0 & 1 & 0 \\ + !! \sin(-\theta_y) & 0 & \cos(-\theta_y) \end{bmatrix} + !! \begin{bmatrix} \cos(-\theta_z) & \sin(-\theta_z) & 0 \\ + !! -\sin(-\theta_z) & \cos(-\theta_z) & 0 \\ + !! 0 & 0 & 1 \end{bmatrix} + !! & = & \begin{bmatrix} C_y & 0 & -S_y \\ + !! 0 & 1 & 0 \\ + !! S_y & 0 & C_y \end{bmatrix} + !! \begin{bmatrix} C_z & S_z & 0 \\ + !! -S_z & C_z & 0 \\ + !! 0 & 0 & 1 \end{bmatrix} \\ + !! & & + !! & = & \begin{bmatrix} + !! \cos(-\theta_y) \cos(-\theta_z) & \cos(-\theta_y) \sin(-\theta_z) & - \sin(-\theta_y) \\ + !! - \sin(-\theta_z) & \cos(-\theta_z) & 0 \\ + !! \sin(-\theta_y) \cos(-\theta_z) & \sin(-\theta_y) \sin(-\theta_z) & \cos(-\theta_y) \\ + !! \end{bmatrix} + !! & = & \begin{bmatrix} + !! C_y C_z & C_y S_z & - S_y \\ + !! - S_z & C_z & 0 \\ + !! S_y C_z & S_y S_z & C_y \\ + !! \end{bmatrix} \\ + !! \f} + !! + !! where \f$ \theta_z \f$ is the rotation about the Z direction (_PropagationDir_) and \f$ \theta_y \f$ is + !! the upflow angle (_VFlowAngle_). + + + + ! Create the rotation matrices -- rotate from XYZ to X'Y'Z' (wind aligned along X) coordinates - p%RotToWind(1,:) = (/ COS(-p%PropagationDir), SIN(-p%PropagationDir), 0.0_ReKi /) - p%RotToWind(2,:) = (/ -SIN(-p%PropagationDir), COS(-p%PropagationDir), 0.0_ReKi /) - p%RotToWind(3,:) = (/ 0.0_ReKi, 0.0_ReKi, 1.0_ReKi /) + ! Included in this rotation is the wind upflow (inclination) angle (rotation about Y axis) + p%RotToWind(1,:) = (/ COS(-p%VFlowAngle) * COS(-p%PropagationDir), COS(-p%VFlowAngle) * SIN(-p%PropagationDir), -SIN(-p%VFlowAngle) /) + p%RotToWind(2,:) = (/ -SIN(-p%PropagationDir), COS(-p%PropagationDir), 0.0_ReKi /) + p%RotToWind(3,:) = (/ SIN(-p%VFlowAngle) * COS(-p%PropagationDir), SIN(-p%VFlowAngle) * SIN(-p%PropagationDir), COS(-p%VFlowAngle) /) ! Create the rotation matrices -- rotate from X'Y'Z' (wind aligned along X) to global XYZ coordinates p%RotFromWind = TRANSPOSE(p%RotToWind) @@ -1428,8 +1357,9 @@ SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrM IF ( ErrStat>= AbortErrLev ) RETURN p%WindViXYZprime = 0.0_ReKi + ! set the output points. Note rotation is about the hub height at [0 0 H]. See InflowWind_SetParameters for details. DO I = 1,p%NWindVel - p%WindViXYZprime(:,I) = MATMUL( p%RotToWind, p%WindViXYZ(:,I) ) + p%WindViXYZprime(:,I) = MATMUL( p%RotToWind, (p%WindViXYZ(:,I) - (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) )) + (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) ENDDO END SUBROUTINE InflowWind_SetParameters @@ -1579,15 +1509,16 @@ SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) IF ( Indx > 0 ) THEN ! we found the channel name - p%OutParam(I)%Indx = ParamIndxAry(Indx) IF ( InvalidOutput( ParamIndxAry(Indx) ) ) THEN ! but, it isn't valid for these settings + p%OutParam(I)%Indx = 0 ! pick any valid channel (I just picked "Time=0" here because it's universal) p%OutParam(I)%Units = "INVALID" p%OutParam(I)%SignM = 0 ELSE + p%OutParam(I)%Indx = ParamIndxAry(Indx) p%OutParam(I)%Units = ParamUnitsAry(Indx) ! it's a valid output END IF ELSE ! this channel isn't valid - p%OutParam(I)%Indx = Time ! pick any valid channel (I just picked "Time" here because it's universal) + p%OutParam(I)%Indx = 0 ! pick any valid channel (I just picked "Time=0" here because it's universal) p%OutParam(I)%Units = "INVALID" p%OutParam(I)%SignM = 0 ! multiply all results by zero @@ -1629,8 +1560,8 @@ SUBROUTINE SetOutParamLin( p, ErrStat, ErrMsg ) call AllocAry(p%OutParamLinIndx, 2, p%NumOuts, 'OutParamLinIndx', ErrStat2, ErrMsg2) call setErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) if (ErrStat >= AbortErrLev) return - - p%OutParamLinIndx = 0 + + p%OutParamLinIndx = 0 ! initialize to 0 do i = 1,p%NumOuts if (p%OutParam(i)%SignM /= 0 ) then @@ -1684,7 +1615,7 @@ SUBROUTINE SetAllOuts( p, y, m, ErrStat, ErrMsg ) m%AllOuts( WindVelX(I) ) = m%WindViUVW(1,I) m%AllOuts( WindVelY(I) ) = m%WindViUVW(2,I) - m%AllOuts( WindVelZ(I) ) = m%WindViUVW(3,I) + m%AllOuts( WindVelZ(I) ) = m%WindViUVW(3,I) END DO @@ -1842,11 +1773,12 @@ SUBROUTINE CalculateOutput( Time, InputData, p, x, xd, z, OtherStates, y, m, Fil ! Apply the coordinate transformation to the PositionXYZ coordinates to get the PositionXYZprime coordinate list ! If the PropagationDir is zero, we don't need to apply this and will simply copy the data. Repeat for the WindViXYZ. - IF ( EqualRealNos (p%PropagationDir, 0.0_ReKi) ) THEN + IF ( EqualRealNos (p%PropagationDir, 0.0_ReKi) .AND. EqualRealNos (p%VFlowAngle, 0.0_ReKi) ) THEN PositionXYZprime = InputData%PositionXYZ ELSE + ! NOTE: rotations are about the hub at [ 0 0 H ]. See InflowWind_SetParameters for details. DO I = 1,SIZE(InputData%PositionXYZ,DIM=2) - PositionXYZprime(:,I) = MATMUL( p%RotToWind, InputData%PositionXYZ(:,I) ) + PositionXYZprime(:,I) = MATMUL( p%RotToWind, (InputData%PositionXYZ(:,I) - (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /)) ) + (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) ENDDO ENDIF @@ -1971,7 +1903,7 @@ SUBROUTINE CalculateOutput( Time, InputData, p, x, xd, z, OtherStates, y, m, Fil ENDIF - CASE (FDext_WindNumber) + CASE ( FDext_WindNumber ) CALL IfW_4Dext_CalcOutput(Time, PositionXYZprime, p%FDext, y%VelocityUVW, m%FDext, TmpErrStat, TmpErrMsg) DiskVel = 0.0_ReKi ! this is only for AD14, which we frankly don't care about in 4Dext wind @@ -2046,16 +1978,18 @@ SUBROUTINE CalculateOutput( Time, InputData, p, x, xd, z, OtherStates, y, m, Fil ! coordinate frame, but only if PropagationDir is not zero. This is only a rotation of the returned wind field, so ! UVW contains the direction components of the wind at XYZ after translation from the U'V'W' wind velocity components ! in the X'Y'Z' (wind file) coordinate frame. - IF ( .NOT. EqualRealNos (p%PropagationDir, 0.0_ReKi) ) THEN + ! NOTE: rotations are about the hub at [ 0 0 H ]. See InflowWind_SetParameters for details. + IF ( .NOT. (EqualRealNos (p%PropagationDir, 0.0_ReKi) .AND. EqualRealNos (p%VFlowAngle, 0.0_ReKi) )) THEN DO I = 1,SIZE(y%VelocityUVW,DIM=2) - y%VelocityUVW(:,I) = MATMUL( p%RotFromWind, y%VelocityUVW(:,I) ) + y%VelocityUVW(:,I) = MATMUL( p%RotFromWind, (y%VelocityUVW(:,I) - (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /)) ) + (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) ENDDO ENDIF ! We also need to rotate the reference frame for the WindViUVW array - IF ( .NOT. EqualRealNos (p%PropagationDir, 0.0_ReKi) .AND. FillWrOut ) THEN + ! NOTE: rotations are about the hub at [ 0 0 H ]. See InflowWind_SetParameters for details. + IF ( .NOT. (EqualRealNos (p%PropagationDir, 0.0_ReKi) .AND. EqualRealNos (p%VFlowAngle, 0.0_ReKi) ) .AND. FillWrOut ) THEN DO I = 1,SIZE(m%WindViUVW,DIM=2) - m%WindViUVW(:,I) = MATMUL( p%RotFromWind, m%WindViUVW(:,I) ) + m%WindViUVW(:,I) = MATMUL( p%RotFromWind, (m%WindViUVW(:,I) - (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /)) ) + (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) ENDDO ENDIF diff --git a/modules/inflowwind/src/InflowWind_Types.f90 b/modules/inflowwind/src/InflowWind_Types.f90 index d8f2890b35..66fcb0998a 100644 --- a/modules/inflowwind/src/InflowWind_Types.f90 +++ b/modules/inflowwind/src/InflowWind_Types.f90 @@ -32,6 +32,7 @@ MODULE InflowWind_Types !--------------------------------------------------------------------------------------------------------------------------------- USE IfW_UniformWind_Types +USE IfW_FFWind_Base_Types USE IfW_TSFFWind_Types USE IfW_BladedFFWind_Types USE IfW_HAWCWind_Types @@ -47,8 +48,9 @@ MODULE InflowWind_Types INTEGER(IntKi), PUBLIC, PARAMETER :: BladedFF_WindNumber = 4 ! Bladed style binary full-field file. Includes native bladed format [-] INTEGER(IntKi), PUBLIC, PARAMETER :: HAWC_WindNumber = 5 ! HAWC wind file. [-] INTEGER(IntKi), PUBLIC, PARAMETER :: User_WindNumber = 6 ! User defined wind. [-] - INTEGER(IntKi), PUBLIC, PARAMETER :: FDext_WindNumber = 7 ! 4D wind from external souce (i.e., FAST.Farm). [-] - INTEGER(IntKi), PUBLIC, PARAMETER :: Highest_WindNumber = 7 ! Highest wind number supported. [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: BladedFF_Shr_WindNumber = 7 ! Native Bladed binary full-field file. [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: FDext_WindNumber = 8 ! 4D wind from external souce (i.e., FAST.Farm). [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: Highest_WindNumber = 8 ! Highest wind number supported. [-] ! ========= WindFileMetaData ======= TYPE, PUBLIC :: WindFileMetaData CHARACTER(1024) :: FileName !< Name of the windfile retrieved [-] @@ -76,6 +78,7 @@ MODULE InflowWind_Types LOGICAL :: EchoFlag !< Echo the input file [-] INTEGER(IntKi) :: WindType = 0 !< Type of windfile [-] REAL(ReKi) :: PropagationDir !< Direction of wind propagation (meteorological direction) [(degrees)] + REAL(ReKi) :: VFlowAngle !< Vertical (upflow) angle [degrees] INTEGER(IntKi) :: NWindVel !< Number of points to output the wind velocity (0 to 9) [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: WindVxiList !< List of X coordinates for wind velocity measurements [meters] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: WindVyiList !< List of Y coordinates for wind velocity measurements [meters] @@ -101,21 +104,6 @@ MODULE InflowWind_Types REAL(ReKi) :: HAWC_dx !< HAWC -- distance between points in x direction [meters] REAL(ReKi) :: HAWC_dy !< HAWC -- distance between points in y direction [meters] REAL(ReKi) :: HAWC_dz !< HAWC -- distance between points in z direction [meters] - REAL(ReKi) :: HAWC_RefHt !< HAWC -- reference height [meters] - INTEGER(IntKi) :: HAWC_ScaleMethod !< HAWC -- scale method [-] - REAL(ReKi) :: HAWC_SFx !< HAWC -- turbulence scaling factor x direction [-] - REAL(ReKi) :: HAWC_SFy !< HAWC -- turbulence scaling factor y direction [-] - REAL(ReKi) :: HAWC_SFz !< HAWC -- turbulence scaling factor z direction [-] - REAL(ReKi) :: HAWC_SigmaFx !< HAWC -- turbulence standard deviation x direction [-] - REAL(ReKi) :: HAWC_SigmaFy !< HAWC -- turbulence standard deviation y direction [-] - REAL(ReKi) :: HAWC_SigmaFz !< HAWC -- turbulence standard deviation z direction [-] - REAL(ReKi) :: HAWC_TStart !< HAWC -- start time for turbulence scaling [seconds] - REAL(ReKi) :: HAWC_TEnd !< HAWC -- end time for turbulence scaling [seconds] - REAL(ReKi) :: HAWC_URef !< HAWC -- Mean u-component wind speed at the reference height [meters] - INTEGER(IntKi) :: HAWC_ProfileType !< HAWC -- Wind profile type (0=constant;1=logarithmic;2=power law) [-] - REAL(ReKi) :: HAWC_PLExp !< HAWC -- Power law exponent (used for PL wind profile type only) [-] - REAL(ReKi) :: HAWC_Z0 !< HAWC -- Surface roughness length (used for LOG wind profile type only) [-] - REAL(ReKi) , DIMENSION(1:3) :: HAWC_InitPosition !< HAWC -- initial position (offset for wind file box) [meters] LOGICAL :: SumPrint !< Write summary info to a file .IfW.Sum [-] INTEGER(IntKi) :: NumOuts !< Number of parameters in the output list (number of outputs requested) [-] CHARACTER(ChanLen) , DIMENSION(:), ALLOCATABLE :: OutList !< List of user-requested output channels [-] @@ -123,6 +111,7 @@ MODULE InflowWind_Types INTEGER(IntKi) :: NumPulseGate !< the number of range gates to return wind speeds at [-] REAL(ReKi) , DIMENSION(1:3) :: RotorApexOffsetPos !< position of the lidar unit relative to the rotor apex of rotation [m] LOGICAL :: LidRadialVel !< TRUE => return radial component, FALSE => return 'x' direction estimate [-] + TYPE(IfW_FFWind_InitInputType) :: FF !< scaling data [-] END TYPE InflowWind_InputFile ! ======================= ! ========= InflowWind_InitInputType ======= @@ -170,6 +159,7 @@ MODULE InflowWind_Types LOGICAL :: CTTS_Flag = .FALSE. !< determines if coherent turbulence is used [-] REAL(DbKi) :: DT !< Time step for cont. state integration & disc. state update [seconds] REAL(ReKi) :: PropagationDir !< Direction of wind propagation [radians] + REAL(ReKi) :: VFlowAngle !< Vertical (upflow) angle [radians] REAL(ReKi) , DIMENSION(1:3,1:3) :: RotToWind !< Rotation matrix for rotating from the global XYZ coordinate system to the wind coordinate system (wind along X') [-] REAL(ReKi) , DIMENSION(1:3,1:3) :: RotFromWind !< Rotation matrix for rotating from the wind coordinate system (wind along X') back to the global XYZ coordinate system. Equal to TRANSPOSE(RotToWind) [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WindViXYZprime !< List of XYZ coordinates for velocity measurements, translated to the wind coordinate system (prime coordinates). This equals MATMUL( RotToWind, ParamData%WindViXYZ ) [meters/second] @@ -501,6 +491,7 @@ SUBROUTINE InflowWind_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCod DstInputFileData%EchoFlag = SrcInputFileData%EchoFlag DstInputFileData%WindType = SrcInputFileData%WindType DstInputFileData%PropagationDir = SrcInputFileData%PropagationDir + DstInputFileData%VFlowAngle = SrcInputFileData%VFlowAngle DstInputFileData%NWindVel = SrcInputFileData%NWindVel IF (ALLOCATED(SrcInputFileData%WindVxiList)) THEN i1_l = LBOUND(SrcInputFileData%WindVxiList,1) @@ -559,21 +550,6 @@ SUBROUTINE InflowWind_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCod DstInputFileData%HAWC_dx = SrcInputFileData%HAWC_dx DstInputFileData%HAWC_dy = SrcInputFileData%HAWC_dy DstInputFileData%HAWC_dz = SrcInputFileData%HAWC_dz - DstInputFileData%HAWC_RefHt = SrcInputFileData%HAWC_RefHt - DstInputFileData%HAWC_ScaleMethod = SrcInputFileData%HAWC_ScaleMethod - DstInputFileData%HAWC_SFx = SrcInputFileData%HAWC_SFx - DstInputFileData%HAWC_SFy = SrcInputFileData%HAWC_SFy - DstInputFileData%HAWC_SFz = SrcInputFileData%HAWC_SFz - DstInputFileData%HAWC_SigmaFx = SrcInputFileData%HAWC_SigmaFx - DstInputFileData%HAWC_SigmaFy = SrcInputFileData%HAWC_SigmaFy - DstInputFileData%HAWC_SigmaFz = SrcInputFileData%HAWC_SigmaFz - DstInputFileData%HAWC_TStart = SrcInputFileData%HAWC_TStart - DstInputFileData%HAWC_TEnd = SrcInputFileData%HAWC_TEnd - DstInputFileData%HAWC_URef = SrcInputFileData%HAWC_URef - DstInputFileData%HAWC_ProfileType = SrcInputFileData%HAWC_ProfileType - DstInputFileData%HAWC_PLExp = SrcInputFileData%HAWC_PLExp - DstInputFileData%HAWC_Z0 = SrcInputFileData%HAWC_Z0 - DstInputFileData%HAWC_InitPosition = SrcInputFileData%HAWC_InitPosition DstInputFileData%SumPrint = SrcInputFileData%SumPrint DstInputFileData%NumOuts = SrcInputFileData%NumOuts IF (ALLOCATED(SrcInputFileData%OutList)) THEN @@ -592,6 +568,9 @@ SUBROUTINE InflowWind_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCod DstInputFileData%NumPulseGate = SrcInputFileData%NumPulseGate DstInputFileData%RotorApexOffsetPos = SrcInputFileData%RotorApexOffsetPos DstInputFileData%LidRadialVel = SrcInputFileData%LidRadialVel + CALL IfW_FFWind_CopyInitInput( SrcInputFileData%FF, DstInputFileData%FF, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE InflowWind_CopyInputFile SUBROUTINE InflowWind_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) @@ -615,6 +594,7 @@ SUBROUTINE InflowWind_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) IF (ALLOCATED(InputFileData%OutList)) THEN DEALLOCATE(InputFileData%OutList) ENDIF + CALL IfW_FFWind_DestroyInitInput( InputFileData%FF, ErrStat, ErrMsg ) END SUBROUTINE InflowWind_DestroyInputFile SUBROUTINE InflowWind_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -655,6 +635,7 @@ SUBROUTINE InflowWind_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat Int_BufSz = Int_BufSz + 1 ! EchoFlag Int_BufSz = Int_BufSz + 1 ! WindType Re_BufSz = Re_BufSz + 1 ! PropagationDir + Re_BufSz = Re_BufSz + 1 ! VFlowAngle Int_BufSz = Int_BufSz + 1 ! NWindVel Int_BufSz = Int_BufSz + 1 ! WindVxiList allocated yes/no IF ( ALLOCATED(InData%WindVxiList) ) THEN @@ -692,21 +673,6 @@ SUBROUTINE InflowWind_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat Re_BufSz = Re_BufSz + 1 ! HAWC_dx Re_BufSz = Re_BufSz + 1 ! HAWC_dy Re_BufSz = Re_BufSz + 1 ! HAWC_dz - Re_BufSz = Re_BufSz + 1 ! HAWC_RefHt - Int_BufSz = Int_BufSz + 1 ! HAWC_ScaleMethod - Re_BufSz = Re_BufSz + 1 ! HAWC_SFx - Re_BufSz = Re_BufSz + 1 ! HAWC_SFy - Re_BufSz = Re_BufSz + 1 ! HAWC_SFz - Re_BufSz = Re_BufSz + 1 ! HAWC_SigmaFx - Re_BufSz = Re_BufSz + 1 ! HAWC_SigmaFy - Re_BufSz = Re_BufSz + 1 ! HAWC_SigmaFz - Re_BufSz = Re_BufSz + 1 ! HAWC_TStart - Re_BufSz = Re_BufSz + 1 ! HAWC_TEnd - Re_BufSz = Re_BufSz + 1 ! HAWC_URef - Int_BufSz = Int_BufSz + 1 ! HAWC_ProfileType - Re_BufSz = Re_BufSz + 1 ! HAWC_PLExp - Re_BufSz = Re_BufSz + 1 ! HAWC_Z0 - Re_BufSz = Re_BufSz + SIZE(InData%HAWC_InitPosition) ! HAWC_InitPosition Int_BufSz = Int_BufSz + 1 ! SumPrint Int_BufSz = Int_BufSz + 1 ! NumOuts Int_BufSz = Int_BufSz + 1 ! OutList allocated yes/no @@ -718,6 +684,24 @@ SUBROUTINE InflowWind_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat Int_BufSz = Int_BufSz + 1 ! NumPulseGate Re_BufSz = Re_BufSz + SIZE(InData%RotorApexOffsetPos) ! RotorApexOffsetPos Int_BufSz = Int_BufSz + 1 ! LidRadialVel + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + Int_BufSz = Int_BufSz + 3 ! FF: size of buffers for each call to pack subtype + CALL IfW_FFWind_PackInitInput( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, .TRUE. ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FF + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FF + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FF + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -751,6 +735,8 @@ SUBROUTINE InflowWind_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat Int_Xferred = Int_Xferred + 1 ReKiBuf(Re_Xferred) = InData%PropagationDir Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%VFlowAngle + Re_Xferred = Re_Xferred + 1 IntKiBuf(Int_Xferred) = InData%NWindVel Int_Xferred = Int_Xferred + 1 IF ( .NOT. ALLOCATED(InData%WindVxiList) ) THEN @@ -856,38 +842,6 @@ SUBROUTINE InflowWind_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat Re_Xferred = Re_Xferred + 1 ReKiBuf(Re_Xferred) = InData%HAWC_dz Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_RefHt - Re_Xferred = Re_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%HAWC_ScaleMethod - Int_Xferred = Int_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_SFx - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_SFy - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_SFz - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_SigmaFx - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_SigmaFy - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_SigmaFz - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_TStart - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_TEnd - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_URef - Re_Xferred = Re_Xferred + 1 - IntKiBuf(Int_Xferred) = InData%HAWC_ProfileType - Int_Xferred = Int_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_PLExp - Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%HAWC_Z0 - Re_Xferred = Re_Xferred + 1 - DO i1 = LBOUND(InData%HAWC_InitPosition,1), UBOUND(InData%HAWC_InitPosition,1) - ReKiBuf(Re_Xferred) = InData%HAWC_InitPosition(i1) - Re_Xferred = Re_Xferred + 1 - END DO IntKiBuf(Int_Xferred) = TRANSFER(InData%SumPrint, IntKiBuf(1)) Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = InData%NumOuts @@ -919,6 +873,34 @@ SUBROUTINE InflowWind_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat END DO IntKiBuf(Int_Xferred) = TRANSFER(InData%LidRadialVel, IntKiBuf(1)) Int_Xferred = Int_Xferred + 1 + CALL IfW_FFWind_PackInitInput( Re_Buf, Db_Buf, Int_Buf, InData%FF, ErrStat2, ErrMsg2, OnlySize ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Re_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Re_Buf) > 0) ReKiBuf( Re_Xferred:Re_Xferred+SIZE(Re_Buf)-1 ) = Re_Buf + Re_Xferred = Re_Xferred + SIZE(Re_Buf) + DEALLOCATE(Re_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Db_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Db_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Db_Buf) > 0) DbKiBuf( Db_Xferred:Db_Xferred+SIZE(Db_Buf)-1 ) = Db_Buf + Db_Xferred = Db_Xferred + SIZE(Db_Buf) + DEALLOCATE(Db_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF + IF(ALLOCATED(Int_Buf)) THEN + IntKiBuf( Int_Xferred ) = SIZE(Int_Buf); Int_Xferred = Int_Xferred + 1 + IF (SIZE(Int_Buf) > 0) IntKiBuf( Int_Xferred:Int_Xferred+SIZE(Int_Buf)-1 ) = Int_Buf + Int_Xferred = Int_Xferred + SIZE(Int_Buf) + DEALLOCATE(Int_Buf) + ELSE + IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 + ENDIF END SUBROUTINE InflowWind_PackInputFile SUBROUTINE InflowWind_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -954,6 +936,8 @@ SUBROUTINE InflowWind_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrS Int_Xferred = Int_Xferred + 1 OutData%PropagationDir = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 + OutData%VFlowAngle = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 OutData%NWindVel = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! WindVxiList not allocated @@ -1068,40 +1052,6 @@ SUBROUTINE InflowWind_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrS Re_Xferred = Re_Xferred + 1 OutData%HAWC_dz = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 - OutData%HAWC_RefHt = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_ScaleMethod = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%HAWC_SFx = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_SFy = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_SFz = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_SigmaFx = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_SigmaFy = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_SigmaFz = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_TStart = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_TEnd = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_URef = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_ProfileType = IntKiBuf(Int_Xferred) - Int_Xferred = Int_Xferred + 1 - OutData%HAWC_PLExp = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - OutData%HAWC_Z0 = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - i1_l = LBOUND(OutData%HAWC_InitPosition,1) - i1_u = UBOUND(OutData%HAWC_InitPosition,1) - DO i1 = LBOUND(OutData%HAWC_InitPosition,1), UBOUND(OutData%HAWC_InitPosition,1) - OutData%HAWC_InitPosition(i1) = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - END DO OutData%SumPrint = TRANSFER(IntKiBuf(Int_Xferred), OutData%SumPrint) Int_Xferred = Int_Xferred + 1 OutData%NumOuts = IntKiBuf(Int_Xferred) @@ -1138,6 +1088,46 @@ SUBROUTINE InflowWind_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrS END DO OutData%LidRadialVel = TRANSFER(IntKiBuf(Int_Xferred), OutData%LidRadialVel) Int_Xferred = Int_Xferred + 1 + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Re_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Re_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Re_Buf = ReKiBuf( Re_Xferred:Re_Xferred+Buf_size-1 ) + Re_Xferred = Re_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Db_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Db_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Db_Buf = DbKiBuf( Db_Xferred:Db_Xferred+Buf_size-1 ) + Db_Xferred = Db_Xferred + Buf_size + END IF + Buf_size=IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF(Buf_size > 0) THEN + ALLOCATE(Int_Buf(Buf_size),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating Int_Buf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) + Int_Xferred = Int_Xferred + Buf_size + END IF + CALL IfW_FFWind_UnpackInitInput( Re_Buf, Db_Buf, Int_Buf, OutData%FF, ErrStat2, ErrMsg2 ) ! FF + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) + IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) + IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) END SUBROUTINE InflowWind_UnPackInputFile SUBROUTINE InflowWind_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrStat, ErrMsg ) @@ -3060,6 +3050,7 @@ SUBROUTINE InflowWind_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, DstParamData%CTTS_Flag = SrcParamData%CTTS_Flag DstParamData%DT = SrcParamData%DT DstParamData%PropagationDir = SrcParamData%PropagationDir + DstParamData%VFlowAngle = SrcParamData%VFlowAngle DstParamData%RotToWind = SrcParamData%RotToWind DstParamData%RotFromWind = SrcParamData%RotFromWind IF (ALLOCATED(SrcParamData%WindViXYZprime)) THEN @@ -3219,6 +3210,7 @@ SUBROUTINE InflowWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er Int_BufSz = Int_BufSz + 1 ! CTTS_Flag Db_BufSz = Db_BufSz + 1 ! DT Re_BufSz = Re_BufSz + 1 ! PropagationDir + Re_BufSz = Re_BufSz + 1 ! VFlowAngle Re_BufSz = Re_BufSz + SIZE(InData%RotToWind) ! RotToWind Re_BufSz = Re_BufSz + SIZE(InData%RotFromWind) ! RotFromWind Int_BufSz = Int_BufSz + 1 ! WindViXYZprime allocated yes/no @@ -3420,6 +3412,8 @@ SUBROUTINE InflowWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er Db_Xferred = Db_Xferred + 1 ReKiBuf(Re_Xferred) = InData%PropagationDir Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%VFlowAngle + Re_Xferred = Re_Xferred + 1 DO i2 = LBOUND(InData%RotToWind,2), UBOUND(InData%RotToWind,2) DO i1 = LBOUND(InData%RotToWind,1), UBOUND(InData%RotToWind,1) ReKiBuf(Re_Xferred) = InData%RotToWind(i1,i2) @@ -3777,6 +3771,8 @@ SUBROUTINE InflowWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Db_Xferred = Db_Xferred + 1 OutData%PropagationDir = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 + OutData%VFlowAngle = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 i1_l = LBOUND(OutData%RotToWind,1) i1_u = UBOUND(OutData%RotToWind,1) i2_l = LBOUND(OutData%RotToWind,2) diff --git a/modules/openfast-library/src/FAST_Types.f90 b/modules/openfast-library/src/FAST_Types.f90 index b67b32bb2d..144c6320df 100644 --- a/modules/openfast-library/src/FAST_Types.f90 +++ b/modules/openfast-library/src/FAST_Types.f90 @@ -36,6 +36,7 @@ MODULE FAST_Types USE TMD_Types USE ServoDyn_Types USE IfW_UniformWind_Types +USE IfW_FFWind_Base_Types USE IfW_TSFFWind_Types USE IfW_BladedFFWind_Types USE IfW_HAWCWind_Types diff --git a/vs-build/FASTlib/FASTlib.vfproj b/vs-build/FASTlib/FASTlib.vfproj index 2d069cb191..3e862a9dfe 100644 --- a/vs-build/FASTlib/FASTlib.vfproj +++ b/vs-build/FASTlib/FASTlib.vfproj @@ -1052,31 +1052,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1258,6 +1284,7 @@ + diff --git a/vs-build/RunRegistry.bat b/vs-build/RunRegistry.bat index 3719c14711..184f39e0a3 100644 --- a/vs-build/RunRegistry.bat +++ b/vs-build/RunRegistry.bat @@ -106,6 +106,7 @@ GOTO checkError :IfW_BladedFFWind :IfW_UserWind :IfW_4Dext +:IfW_FFWind_Base :IfW_UniformWind SET CURR_LOC=%IfW_Loc% SET Output_Loc=%CURR_LOC% From 6a022de3a796726fb81744a48a964638c923518b Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Wed, 2 Sep 2020 13:48:21 -0600 Subject: [PATCH 02/16] Add InflowWind driver files for Visual Studio build --- vs-build/InflowWind/InflowWind_driver.sln | 61 ++++ vs-build/InflowWind/InflowWind_driver.vfproj | 357 +++++++++++++++++++ 2 files changed, 418 insertions(+) create mode 100644 vs-build/InflowWind/InflowWind_driver.sln create mode 100644 vs-build/InflowWind/InflowWind_driver.vfproj diff --git a/vs-build/InflowWind/InflowWind_driver.sln b/vs-build/InflowWind/InflowWind_driver.sln new file mode 100644 index 0000000000..bb6d88cdff --- /dev/null +++ b/vs-build/InflowWind/InflowWind_driver.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{6989167D-11E4-40FE-8C1A-2192A86A7E90}") = "InflowWind_driver", "InflowWind_driver.vfproj", "{3BBE2741-5B28-47BC-9E7F-3E1D172838FB}" + ProjectSection(ProjectDependencies) = postProject + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} = {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FAST_Registry", "..\Registry\FAST_Registry.vcxproj", "{DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_Double|Win32 = Debug_Double|Win32 + Debug_Double|x64 = Debug_Double|x64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release_Double|Win32 = Release_Double|Win32 + Release_Double|x64 = Release_Double|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Debug_Double|Win32.ActiveCfg = Debug_Double|Win32 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Debug_Double|Win32.Build.0 = Debug_Double|Win32 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Debug_Double|x64.ActiveCfg = Debug_Double|x64 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Debug_Double|x64.Build.0 = Debug_Double|x64 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Debug|Win32.ActiveCfg = Debug|Win32 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Debug|Win32.Build.0 = Debug|Win32 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Debug|x64.ActiveCfg = Debug|x64 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Debug|x64.Build.0 = Debug|x64 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Release_Double|Win32.ActiveCfg = Release_Double|Win32 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Release_Double|Win32.Build.0 = Release_Double|Win32 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Release_Double|x64.ActiveCfg = Release_Double|x64 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Release_Double|x64.Build.0 = Release_Double|x64 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Release|Win32.ActiveCfg = Release|Win32 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Release|Win32.Build.0 = Release|Win32 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Release|x64.ActiveCfg = Release|x64 + {3BBE2741-5B28-47BC-9E7F-3E1D172838FB}.Release|x64.Build.0 = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vs-build/InflowWind/InflowWind_driver.vfproj b/vs-build/InflowWind/InflowWind_driver.vfproj new file mode 100644 index 0000000000..d07e7d29ae --- /dev/null +++ b/vs-build/InflowWind/InflowWind_driver.vfproj @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 04a09d0a0dfd4a71e109d583b4c8e7f73f76aecb Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Wed, 2 Sep 2020 13:58:25 -0600 Subject: [PATCH 03/16] fix visual studio issue in last commit --- vs-build/InflowWind/InflowWind_driver.vfproj | 154 +++++++++---------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/vs-build/InflowWind/InflowWind_driver.vfproj b/vs-build/InflowWind/InflowWind_driver.vfproj index d07e7d29ae..c7179bd934 100644 --- a/vs-build/InflowWind/InflowWind_driver.vfproj +++ b/vs-build/InflowWind/InflowWind_driver.vfproj @@ -91,164 +91,164 @@ - - - + - + - - + + + + - - - + - + - - + + + + - - - - - - + + + + + + - - - + - + - - + + + + - - - + - + - - + + + + - - - + - + - - + + + + - - - + - + - - + + + + - - - + - + - - + + + + - - - + - + - - + + + + @@ -258,89 +258,89 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -352,6 +352,6 @@ - - + + From ab20d7e76c6b848d0e11b98ac33aedbe31ec7a00 Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Wed, 2 Sep 2020 22:16:12 -0600 Subject: [PATCH 04/16] updated documentation for new InflowWind features --- docs/source/user/api_change.rst | 5 + docs/source/user/index.rst | 1 + docs/source/user/inflowwind/appendix.rst | 38 ++++++ docs/source/user/inflowwind/driver.rst | 115 ++++++++++++++++++ .../inflowwind_bladedscaling_example.dat | 10 ++ .../examples/inflowwind_driver_example.inp | 24 ++++ .../examples/inflowwind_example.dat | 56 +++++++++ docs/source/user/inflowwind/index.rst | 22 ++++ docs/source/user/inflowwind/input.rst | 83 +++++++++++++ modules/inflowwind/src/InflowWind_Subs.f90 | 2 +- 10 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 docs/source/user/inflowwind/appendix.rst create mode 100644 docs/source/user/inflowwind/driver.rst create mode 100644 docs/source/user/inflowwind/examples/inflowwind_bladedscaling_example.dat create mode 100644 docs/source/user/inflowwind/examples/inflowwind_driver_example.inp create mode 100644 docs/source/user/inflowwind/examples/inflowwind_example.dat create mode 100644 docs/source/user/inflowwind/index.rst create mode 100644 docs/source/user/inflowwind/input.rst diff --git a/docs/source/user/api_change.rst b/docs/source/user/api_change.rst index 2a37dd7f3f..16e7cf9591 100644 --- a/docs/source/user/api_change.rst +++ b/docs/source/user/api_change.rst @@ -30,6 +30,11 @@ AeroDyn 14 AA_InputFile "unused" AA_InputFile AeroDyn 35 [separator line] ====== OLAF -- cOnvecting LAgrangian Filaments (Free Vortex Wake) Theory Options ================== [used only when WakeMod=3] AeroDyn 36 OLAFInputFileName "Elliptic_OLAF.dat" OLAFInputFileName - Input file for OLAF [used only when WakeMod=3] AirFoilTables 4\* BL_file "unused" BL_file - The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. +IfW driver 6 [separator line] ===================== File Conversion Options ================================= +IfW driver 7 WrHAWC false WrHAWC - Convert all data to HAWC2 format? (flag) +IfW driver 8 WrBladed false WrBladed - Convert all data to Bladed format? (flag) +IfW driver 9 WrVTK false WrVTK - Convert all data to VTK format? (flag) + ============== ==== ================== ============================================================================================================================================================================= diff --git a/docs/source/user/index.rst b/docs/source/user/index.rst index 0cf94cb104..8a94dcaf93 100644 --- a/docs/source/user/index.rst +++ b/docs/source/user/index.rst @@ -18,6 +18,7 @@ Details on the transition from FAST v8 to OpenFAST may be found in :numref:`fast aerodyn-aeroacoustics/index.rst beamdyn/index.rst elastodyn/index.rst + inflowwind/index.rst fast_to_openfast.rst cppapi/index.rst diff --git a/docs/source/user/inflowwind/appendix.rst b/docs/source/user/inflowwind/appendix.rst new file mode 100644 index 0000000000..63d8a66a67 --- /dev/null +++ b/docs/source/user/inflowwind/appendix.rst @@ -0,0 +1,38 @@ +.. _ifw_appendix: + +Appendix +======== + +.. _ifw_input_files: + +InflowWind Input Files +---------------------- + +In this appendix we describe the InflowWind input-file structure and provide examples. + +1) InflowWind Driver Input File +:download:`(driver input file example) `: + +The driver input file is needed only for the standalone version of InflowWind. It contains inputs regarding the InflowWind file, interpolation parameters, and the desired output files. +The InflowWind driver can also be run without this file by using command-line arguments instead. + +2) InflowWind Primary Input File +:download:`(primary input file example) `: + +The primary InflowWind input file defines the inflow that is generated or read from other files. The InflowWind file contains sections for each type of wind-file format. + +3) Native Bladed Scaling File +:download:`(primary input file example) `: + +This file includes lines that determine how to scale the non-dimensional full-field +turbulence files from Bladed. + +.. _ifw_output_channels: + +InflowWind List of Output Channels +---------------------------------- + +This is a list of all possible output parameters for the InflowWind module. +See the InflowWind tab of the +:download:`(OutListParameters.xlsx file) <../../../OtherSupporting/OutListParameters.xlsx>`: + diff --git a/docs/source/user/inflowwind/driver.rst b/docs/source/user/inflowwind/driver.rst new file mode 100644 index 0000000000..b0ef7a0170 --- /dev/null +++ b/docs/source/user/inflowwind/driver.rst @@ -0,0 +1,115 @@ +InflowWind Driver +================= +Example input files are +included in :numref:`ifw_appendix`. + +Command-line syntax for InflowWind driver: + +:: + + InlowWind_Driver [options] + + where: -- Name of driver input file to use + options: /ifw -- treat as name of InflowWind input file (no driver input file) + + The following options will override values in the driver input file: + /DT[#] -- timestep + /TStart[#] -- start time + /TSteps[#] -- number of timesteps + /xrange[#:#] -- range of x (#'s are reals) + /yrange[#:#] -- range of y + /zrange[#:#] -- range in z (ground = 0.0) + /Dx[#] -- spacing in x + /Dy[#] -- spacing in y + /Dz[#] -- spacing in z + /points[FILE] -- calculates at x,y,z coordinates specified in a white space delimited FILE + /v -- verbose output + /vv -- very verbose output + /hawc -- convert wind file specified in InflowWind to HAWC format + /bladed -- convert wind file specified in InflowWind to Bladed format + /vtk -- convert wind file specified in InflowWind to VTK format + /help -- print this help menu and exit + +:: + + Notes: + - Unspecified ranges and resolutions default to what is in the file. + - If no XRange is specified, assumed to be only at X=0 + - Options are not case sensitive. + +The `InflowWind Manual `__ +contains a description of file formats that it can read. + +Specifying the InflowWind Input File +------------------------------------ + +The InflowWind driver input file requires that an InflowWind input file +be specified within it. See an example InflowWind input +file in :numref:`ifw_appendix`. + +Within the InflowWind input file, if the wind file being specified is +Bladed native format (``WindType = 7``), please also see +:numref:`ifw_native_bladed`. + +Wind-file output formats +------------------------ + +The InflowWind driver is capable of writing the wind data read from the +input wind file into wind files of various formats. + +HAWC2 +~~~~~ + +This format generates the following files: + +- three binary files, one for each component: + ``-HAWC.u``, ``-HAWC.v``, and ``-HAWC.w`` + +- a text summary file in the style of HAWC2 input files: + ``-HAWC.sum`` + +In the conversion script, the u component will have the (approximate) +mean removed at each height. The mean value that was removed is +displayed as comments in the text summary file. The turbulence is not +scaled, so it will have the same scaling as the original file. + +Bladed +~~~~~~ + +This format generates a packed binary file and a text summary file. + +This output format is in the Bladed-style format that TurbSim generates. That +means that **the shear is included** in the file. + +VTK +~~~ + +This format creates files in a subdirectory called ``vtk``. There is one +vtk file for each time in the full-field data structure, and the entire +Y-Z grid is printed in each file. This format can be used to visualize +the wind field using a viewer such as ParaView. + +Converting uniform wind to full-field wind format +------------------------------------------------- + +When converting from a uniform wind file to a full-field wind format, +the following assumptions are used: - The advection speed is the +time-averaged horizontal wind speed in the uniform wind file (it does +not include the gust speed). - The constant time-step used in the output +file is the smallest difference between any two entries in the +hub-height file. - The maximum time in the uniform wind file will be +used as the maximum time in the FF binary file. - The grid is generated +with 5-m resolution in the lateral (Y) and horizontal (Z) directions. - +The size of the grid is based on the ``RefLength`` parameter in the +InflowWind input file. The converter adds approximately 10% to the grid +width, with the exact size determined by achieving the desired grid +resolution. The grid is centered in the lateral direction; it extends +vertically above ``RefHt`` by the same distance as the grid width, and +extends below ``RefHt`` to the ground (or within one grid point of the +ground). + +Note that there is a potential time shift between the uniform and +full-field wind files, equal to the time it takes to travel the distance +of half the grid width. When using the resulting full-field files, care +must be taken that the aeroelastic code does not treat it as periodic. + diff --git a/docs/source/user/inflowwind/examples/inflowwind_bladedscaling_example.dat b/docs/source/user/inflowwind/examples/inflowwind_bladedscaling_example.dat new file mode 100644 index 0000000000..77c407d3e9 --- /dev/null +++ b/docs/source/user/inflowwind/examples/inflowwind_bladedscaling_example.dat @@ -0,0 +1,10 @@ +UBAR 12 +REFHT 90 +TI 0.033333 +TI_V 0.026667 +TI_W 0.016667 +WDIR 0 +FLINC .139626222222222 +WINDF "../tw06_80hh_s200.wnd" +WSHEAR .2 +XOFFSET 0 \ No newline at end of file diff --git a/docs/source/user/inflowwind/examples/inflowwind_driver_example.inp b/docs/source/user/inflowwind/examples/inflowwind_driver_example.inp new file mode 100644 index 0000000000..36b82cec00 --- /dev/null +++ b/docs/source/user/inflowwind/examples/inflowwind_driver_example.inp @@ -0,0 +1,24 @@ +Example file +InflowWind driver input file. + t echo -- whether this input file should be echoed to a file (flag) +=============================================================================== +"Test005.ipt" IfWFileName -- Name of InflowWind input file (-) +===================== File Conversion Options ================================= + false WrHAWC -- Convert all data to HAWC2 format? (flag) + false WrBladed -- Convert all data to Bladed format? (flag) + false WrVTK -- Convert all data to VTK format? (flag) +===================== Tests of Interpolation Options ========================= + DEFAULT NumTSteps -- number of timesteps to run (DEFAULT for all) (-) + 0.0 TStart -- Start time (s) + DEFAULT DT -- timestep size for driver to take (s, or DEFAULT for what the file contains) + t Summary -- Summarize the data extents in the windfile (flag) + t SummaryFile -- Write summary to file (.dvr.sum) (flag) +---- Points file input (output given as POINTSFILENAME.Velocity.dat) -------- + f PointsFile -- read in a list of output points from a file (flag) +"Test005.txt" PointsFileName -- name of points file (-) (comma separated x,y,z coordinates, # symbol for comments) +---- Output grid (Points below ground will simply be ignored) --------------- + t WindGrid -- report wind data at set of X,Y,Z coordinat (flag) + 6,0,15 GridCtrCoord -- coordinate of center of grid (m) + 1,1,0 GridDx,GridDY,GridDZ -- Step size of grid (m) + 1,1,0 GridNx,GridNY,GridNZ -- number of grid points in X, Y and Z directions (-) +END of driver input file diff --git a/docs/source/user/inflowwind/examples/inflowwind_example.dat b/docs/source/user/inflowwind/examples/inflowwind_example.dat new file mode 100644 index 0000000000..65156dfa06 --- /dev/null +++ b/docs/source/user/inflowwind/examples/inflowwind_example.dat @@ -0,0 +1,56 @@ +------- InflowWind INPUT FILE ------------------------------------------------------------------------- +Example of InflowWind input file for OpenFAST +--------------------------------------------------------------------------------------------------------------- +False Echo - Echo input data to .ech (flag) + 3 WindType - switch for wind file type (1=steady; 2=uniform; 3=binary TurbSim FF; 4=binary Bladed-style FF; 5=HAWC format; 6=User defined; 7=native Bladed FF) + 0 PropagationDir - Direction of wind propagation (meteorological rotation from aligned with X (positive rotates towards -Y) -- degrees) + 1 NWindVel - Number of points to output the wind velocity (0 to 9) + 0 WindVxiList - List of coordinates in the inertial X direction (m) + 0 WindVyiList - List of coordinates in the inertial Y direction (m) + 90 WindVziList - List of coordinates in the inertial Z direction (m) +================== Parameters for Steady Wind Conditions [used only for WindType = 1] ========================= + 0 HWindSpeed - Horizontal wind speed (m/s) + 90 RefHt - Reference height for horizontal wind speed (m) + 0.2 PLexp - Power law exponent (-) +================== Parameters for Uniform wind file [used only for WindType = 2] ============================ +"Wind/90m_12mps_twr.hh" Filename - Filename of time series data for uniform wind field. (-) + 90 RefHt - Reference height for horizontal wind speed (m) + 125.88 RefLength - Reference length for linear horizontal and vertical sheer (-) +================== Parameters for Binary TurbSim Full-Field files [used only for WindType = 3] ============== +"Wind/90m_12mps_twr.bts" Filename - Name of the Full field wind file to use (.bts) +================== Parameters for Binary Bladed-style Full-Field files [used only for WindType = 4 or WindType = 7] ========= +"Wind/90m_12mps_twr" FilenameRoot - WindType=4: Rootname of the full-field wind file to use (.wnd, .sum); WindType=7: name of the intermediate file with wind scaling values +False TowerFile - Have tower file (.twr) (flag) ignored when WindType = 7 +================== Parameters for HAWC-format binary files [Only used with WindType = 5] ===================== +"wasp\Output\basic_5u.bin" FileName_u - name of the file containing the u-component fluctuating wind (.bin) +"wasp\Output\basic_5v.bin" FileName_v - name of the file containing the v-component fluctuating wind (.bin) +"wasp\Output\basic_5w.bin" FileName_w - name of the file containing the w-component fluctuating wind (.bin) + 64 nx - number of grids in the x direction (in the 3 files above) (-) + 32 ny - number of grids in the y direction (in the 3 files above) (-) + 32 nz - number of grids in the z direction (in the 3 files above) (-) + 16 dx - distance (in meters) between points in the x direction (m) + 3 dy - distance (in meters) between points in the y direction (m) + 3 dz - distance (in meters) between points in the z direction (m) + 90 RefHt - reference height; the height (in meters) of the vertical center of the grid (m) + ------------- Scaling parameters for turbulence --------------------------------------------------------- + 2 ScaleMethod - Turbulence scaling method [0 = none, 1 = direct scaling, 2 = calculate scaling factor based on a desired standard deviation] + 1 SFx - Turbulence scaling factor for the x direction (-) [ScaleMethod=1] + 1 SFy - Turbulence scaling factor for the y direction (-) [ScaleMethod=1] + 1 SFz - Turbulence scaling factor for the z direction (-) [ScaleMethod=1] + 1.2 SigmaFx - Turbulence standard deviation to calculate scaling from in x direction (m/s) [ScaleMethod=2] + .8 SigmaFy - Turbulence standard deviation to calculate scaling from in y direction (m/s) [ScaleMethod=2] + .2 SigmaFz - Turbulence standard deviation to calculate scaling from in z direction (m/s) [ScaleMethod=2] + ------------- Mean wind profile parameters (added to HAWC-format files) --------------------------------- + 12 URef - Mean u-component wind speed at the reference height (m/s) + 2 WindProfile - Wind profile type (0=constant;1=logarithmic,2=power law) + 0.2 PLExp - Power law exponent (-) (used for PL wind profile type only) + 0.03 Z0 - Surface roughness length (m) (used for LG wind profile type only) + 0 InitPosition(x) - Initial offset in +x direction (shift of wind box) +====================== OUTPUT ================================================== +False SumPrint - Print summary data to .IfW.sum (flag) + OutList - The next line(s) contains a list of output parameters. See OutListParameters.xlsx for a listing of available output channels, (-) +"Wind1VelX" X-direction wind velocity at point WindList(1) +"Wind1VelY" Y-direction wind velocity at point WindList(1) +"Wind1VelZ" Z-direction wind velocity at point WindList(1) +END of input file (the word "END" must appear in the first 3 columns of this last OutList line) +--------------------------------------------------------------------------------------- diff --git a/docs/source/user/inflowwind/index.rst b/docs/source/user/inflowwind/index.rst new file mode 100644 index 0000000000..07c59bbdab --- /dev/null +++ b/docs/source/user/inflowwind/index.rst @@ -0,0 +1,22 @@ +InflowWind Users Guide and Theory Manual +======================================== + +.. only:: html + + This document offers a quick reference guide for the InflowWind software. + It is intended to be used by the general user in combination + with other OpenFAST manuals. The manual will be updated as new releases are + issued and as needed to provide further information on advancements or + modifications to the software. + + The information here is incomplete. Please see the + `InflowWind Manual `__ + until it has been converted to OpenFAST's documentation style. + + +.. toctree:: + :maxdepth: 2 + + driver.rst + input.rst + appendix.rst diff --git a/docs/source/user/inflowwind/input.rst b/docs/source/user/inflowwind/input.rst new file mode 100644 index 0000000000..df83a55538 --- /dev/null +++ b/docs/source/user/inflowwind/input.rst @@ -0,0 +1,83 @@ +.. _ifw_input: + +InflowWind Input Files +====================== + +.. _ifw_native_bladed: + +Native Bladed wind file support in InflowWind +--------------------------------------------- + +The ability to read native Bladed wind files (without scaling) has been added to InflowWind. +To use this feature, the ``WindType`` must be set to ``7`` on line 5 of the primary +InflowWind input file. An example of this file is given inAn example of this Native Bladed scaling file is included in +:numref:`ifw_appendix`. + +:: + + 7 WindType - switch for wind file type (1=steady; 2=uniform; 3=binary TurbSim FF; 4=binary Bladed-style FF; 5=HAWC format; 6=User defined; 7=Bladed native) + +In the section for ``WindType = 4``, the name of an intermediate Bladed +wind file should be given (including the file extension). The tower file +flag is ignored. + +:: + + ================== Parameters for Binary Bladed-style Full-Field files [used only for WindType = 4] ========= + "tw06_80hh_s200.BladedWind.ipt" FilenameRoot - Name of the Full field wind file to use (.wnd, .sum) + F TowerFile - Have tower file (.twr) (flag) + + +The intermediate Bladed wind scaling file must contain the following information, which can be retrieved +directly from the Bladed project simulation file from the +``MSTART WINDSEL`` and ``MSTART WINDV`` sections. Additionally, the file +may include an ``XOFFSET`` line, which allows the wind to be shifted by +a given distance. If not included, ``XOFFSET`` is assumed to be 0. +An example of this Native Bladed scaling file is included in +:numref:`ifw_appendix`. + + +:: + + UBAR 12 + REFHT 90 + TI 0.033333 + TI_V 0.026667 + TI_W 0.016667 + WDIR 0 + FLINC .139626222222222 + WINDF "../tw06_80hh_s200.wnd" + WSHEAR .2 + XOFFSET 0 + +In the above file, the names correspond to the following: + ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| Line | Variable Name | Units | Description | ++========+=================+=========+=================================================================================================================================================+ +| 1 | ``UBAR`` | (m/s) | Mean wind speed | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| 2 | ``REFHT`` | (m) | Reference height (turbine hub height) | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| 3 | ``TI`` | (-) | Turbulence intensity in longitudinal (mean wind flow) direction | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| 4 | ``TI_V`` | (-) | Turbulence intensity in horizontal direction (orthogonal to mean flow direction) | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| 5 | ``TI_W`` | (-) | Turbulence intensity in vertical direction (orthogonal to mean flow direction) | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| 6 | ``WDIR`` | (rad) | Wind direction (meteorological rotation direction) | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| 7 | ``FLINC`` | (rad) | Upflow angle (positive is up) | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| 8 | ``WINDF`` | (-) | Name of native Bladed wind file (absolute or relative path, 200 character limit) | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| 9 | ``WSHEAR`` | (-) | Power law wind shear exponent | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ +| 10 | ``XOFFSET`` | (m) | Turbulence box offset in the X direction (how far ahead of the turbine the turbulence box starts). If missing, this value is assumed to be 0. | ++--------+-----------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------+ + +Limitations: - Wind file is centered on hub height ("Best fit for rotor +and tower" not implemented) - Always allow wind file to wrap around +(unchecked box not implemented) - Only power-law wind profile is +implemented (not logarithmic, none, or user-defined) + diff --git a/modules/inflowwind/src/InflowWind_Subs.f90 b/modules/inflowwind/src/InflowWind_Subs.f90 index 9b3dbef73b..9d00ad387f 100644 --- a/modules/inflowwind/src/InflowWind_Subs.f90 +++ b/modules/inflowwind/src/InflowWind_Subs.f90 @@ -1831,7 +1831,7 @@ SUBROUTINE CalculateOutput( Time, InputData, p, x, xd, z, OtherStates, y, m, Fil - CASE (BladedFF_WindNumber) + CASE (BladedFF_WindNumber) !also includes BladedFF_Shr_WindNumber ! InputData only contains the Position array, so we can pass that directly. CALL IfW_BladedFFWind_CalcOutput( Time, PositionXYZprime, p%BladedFFWind, & From c6e66c7e30b2f936464491264fd4c2950cd30a01 Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Wed, 16 Sep 2020 10:21:59 -0600 Subject: [PATCH 05/16] IfW: add VFlowAngle to input file - This allows different wind boxes to be rotated about (0,0,RefHt). - fixed issue with how native-Bladed wind boxes were rotated before - removed unused variables - fixed spelling errors --- .../examples/inflowwind_driver_example.inp | 2 +- .../examples/inflowwind_example.dat | 3 +- modules/inflowwind/src/IfW_HAWCWind.f90 | 7 ---- modules/inflowwind/src/IfW_UniformWind.f90 | 2 +- modules/inflowwind/src/InflowWind.f90 | 18 +++++--- modules/inflowwind/src/InflowWind.txt | 4 +- modules/inflowwind/src/InflowWind_Subs.f90 | 41 ++++++++++++------- modules/inflowwind/src/InflowWind_Types.f90 | 22 +++++++++- 8 files changed, 67 insertions(+), 32 deletions(-) diff --git a/docs/source/user/inflowwind/examples/inflowwind_driver_example.inp b/docs/source/user/inflowwind/examples/inflowwind_driver_example.inp index 36b82cec00..6e84d1667a 100644 --- a/docs/source/user/inflowwind/examples/inflowwind_driver_example.inp +++ b/docs/source/user/inflowwind/examples/inflowwind_driver_example.inp @@ -2,7 +2,7 @@ Example file InflowWind driver input file. t echo -- whether this input file should be echoed to a file (flag) =============================================================================== -"Test005.ipt" IfWFileName -- Name of InflowWind input file (-) +"inflowwind_example.dat" IfWFileName -- Name of InflowWind input file (-) ===================== File Conversion Options ================================= false WrHAWC -- Convert all data to HAWC2 format? (flag) false WrBladed -- Convert all data to Bladed format? (flag) diff --git a/docs/source/user/inflowwind/examples/inflowwind_example.dat b/docs/source/user/inflowwind/examples/inflowwind_example.dat index 65156dfa06..a2159a4aaa 100644 --- a/docs/source/user/inflowwind/examples/inflowwind_example.dat +++ b/docs/source/user/inflowwind/examples/inflowwind_example.dat @@ -3,7 +3,8 @@ Example of InflowWind input file for OpenFAST --------------------------------------------------------------------------------------------------------------- False Echo - Echo input data to .ech (flag) 3 WindType - switch for wind file type (1=steady; 2=uniform; 3=binary TurbSim FF; 4=binary Bladed-style FF; 5=HAWC format; 6=User defined; 7=native Bladed FF) - 0 PropagationDir - Direction of wind propagation (meteorological rotation from aligned with X (positive rotates towards -Y) -- degrees) + 0 PropagationDir - Direction of wind propagation (meteorological rotation from aligned with X (positive rotates towards -Y) -- degrees) (not used for native Bladed format WindType=7) + 0 VFlowAng - Upflow angle (degrees) (not used for native Bladed format WindType=7) 1 NWindVel - Number of points to output the wind velocity (0 to 9) 0 WindVxiList - List of coordinates in the inertial X direction (m) 0 WindVyiList - List of coordinates in the inertial Y direction (m) diff --git a/modules/inflowwind/src/IfW_HAWCWind.f90 b/modules/inflowwind/src/IfW_HAWCWind.f90 index 62b9365f6a..2ebbcc2b0d 100644 --- a/modules/inflowwind/src/IfW_HAWCWind.f90 +++ b/modules/inflowwind/src/IfW_HAWCWind.f90 @@ -314,15 +314,8 @@ SUBROUTINE IfW_HAWCWind_CalcOutput(Time, PositionXYZ, p, Velocity, DiskVel, Misc INTEGER(IntKi), INTENT( OUT) :: ErrStat !< error status CHARACTER(*), INTENT( OUT) :: ErrMsg !< The error message - ! local variables - INTEGER(IntKi) :: NumPoints ! Number of points specified by the PositionXYZ array - - ! local counters - INTEGER(IntKi) :: PointNum ! a loop counter for the current point ! temporary variables - INTEGER(IntKi) :: TmpErrStat ! temporary error status - CHARACTER(ErrMsgLen) :: TmpErrMsg ! temporary error message CHARACTER(*), PARAMETER :: RoutineName = 'IfW_HAWCWind_CalcOutput' diff --git a/modules/inflowwind/src/IfW_UniformWind.f90 b/modules/inflowwind/src/IfW_UniformWind.f90 index 957719c646..558a80a290 100644 --- a/modules/inflowwind/src/IfW_UniformWind.f90 +++ b/modules/inflowwind/src/IfW_UniformWind.f90 @@ -36,7 +36,7 @@ MODULE IfW_UniformWind !! !********************************************************************************************************************************** ! LICENSING -! Copyright (C) 2015-2106 National Renewable Energy Laboratory +! Copyright (C) 2015-2016 National Renewable Energy Laboratory ! ! This file is part of InflowWind. ! diff --git a/modules/inflowwind/src/InflowWind.f90 b/modules/inflowwind/src/InflowWind.f90 index c9c5991e2c..f5eccdf4bc 100644 --- a/modules/inflowwind/src/InflowWind.f90 +++ b/modules/inflowwind/src/InflowWind.f90 @@ -338,7 +338,8 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, p%UniformWind%RefHt = InputFileData%Steady_RefHt m%UniformWind%TimeIndex = 1_IntKi - + p%ReferenceHeight = p%UniformWind%RefHt + ! Store wind file metadata InitOutData%WindFileInfo%FileName = "" InitOutData%WindFileInfo%WindType = Steady_WindNumber @@ -391,6 +392,7 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, ' IfW_Init' ) IF ( ErrStat >= AbortErrLev ) RETURN + p%ReferenceHeight = p%UniformWind%RefHt ! Store wind file metadata InitOutData%WindFileInfo%FileName = InputFileData%Uniform_FileName @@ -453,6 +455,7 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, InitOutData%WindFileInfo%FileName = InputFileData%TSFF_FileName CALL SetFFInitOutData(p%TSFFWind%FF) + p%ReferenceHeight = InitOutData%WindFileInfo%RefHt CASE ( BladedFF_WindNumber, BladedFF_Shr_WindNumber ) @@ -487,10 +490,12 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, if (InputFileData%WindType == BladedFF_Shr_WindNumber) then InputFileData%WindType = BladedFF_WindNumber + ! this overwrites the values of PropagationDir and VFlowAngle with values from the native Bladed file InputFileData%PropagationDir = BladedFF_InitOutData%PropagationDir - InputFileData%VFlowAngle = BladedFF_InitOutData%VFlowAngle + InputFileData%VFlowAngle = BladedFF_InitOutData%VFlowAngle end if - + p%ReferenceHeight = InitOutData%WindFileInfo%RefHt + CASE ( HAWC_WindNumber ) @@ -520,6 +525,7 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, ! Store wind file metadata CALL SetFFInitOutData(p%HAWCWind%FF) InitOutData%WindFileInfo%FileName = InputFileData%HAWC_FileName_u + p%ReferenceHeight = InitOutData%WindFileInfo%RefHt CASE (User_WindNumber) @@ -529,14 +535,16 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, TimeInterval, User_InitOutData, TmpErrStat, TmpErrMsg) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN - + + p%ReferenceHeight = InputFileData%Steady_RefHt ! FIXME!!!! + CASE ( FDext_WindNumber ) ! Initialize the UserWind module CALL IfW_4Dext_Init(InitInp%FDext, p%FDext, m%FDext, TimeInterval, FDext_InitOutData, TmpErrStat, TmpErrMsg) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN - + p%ReferenceHeight = p%FDext%pZero(3) + (p%FDext%n(3)/2) * p%FDext%delta(3) ! should be middle of grid, right???? FIXME CASE DEFAULT ! keep this check to make sure that all new wind types have been accounted for CALL SetErrStat(ErrID_Fatal,' Undefined wind type.',ErrStat,ErrMsg,'InflowWind_Init()') diff --git a/modules/inflowwind/src/InflowWind.txt b/modules/inflowwind/src/InflowWind.txt index 363e186295..aae6825d9a 100644 --- a/modules/inflowwind/src/InflowWind.txt +++ b/modules/inflowwind/src/InflowWind.txt @@ -141,16 +141,18 @@ typedef ^ ^ ReKi WindViUVW # Time step for integration of continuous states (if a fixed-step integrator is used) and update of discrete states: typedef ^ ParameterType CHARACTER(1024) RootFileName - - - "Root of the InflowWind input filename" - typedef ^ ^ LOGICAL CTTS_Flag - .FALSE. - "determines if coherent turbulence is used" - +typedef ^ ^ LOGICAL RotateWindBox - .FALSE. - "determines if wind will be rotated" - typedef ^ ^ DbKi DT - - - "Time step for cont. state integration & disc. state update" seconds typedef ^ ^ ReKi PropagationDir - - - "Direction of wind propagation" radians typedef ^ ^ ReKi VFlowAngle - - - "Vertical (upflow) angle" radians typedef ^ ^ ReKi RotToWind {3}{3} - - "Rotation matrix for rotating from the global XYZ coordinate system to the wind coordinate system (wind along X')" - typedef ^ ^ ReKi RotFromWind {3}{3} - - "Rotation matrix for rotating from the wind coordinate system (wind along X') back to the global XYZ coordinate system. Equal to TRANSPOSE(RotToWind)" - -typedef ^ ^ ReKi WindViXYZprime :: - - "List of XYZ coordinates for velocity measurements, translated to the wind coordinate system (prime coordinates). This equals MATMUL( RotToWind, ParamData%WindViXYZ )" meters/second +typedef ^ ^ ReKi WindViXYZprime :: - - "List of XYZ coordinates for velocity measurements, translated to the wind coordinate system (prime coordinates). This equals MATMUL( RotToWind, ParamData%WindViXYZ )" meters typedef ^ ^ IntKi WindType - 0 - "Type of wind -- set to Undef_Wind initially" - typedef ^ ^ ReKi ReferenceHeight - - - "Height of the wind turbine" meters +typedef ^ ^ ReKi RefPosition 3 - - "Reference position (point where box is rotated)" meters typedef ^ ^ IntKi NWindVel - - - "Number of points in the wind velocity list" - typedef ^ ^ ReKi WindViXYZ :: - - "List of XYZ coordinates for wind velocity measurements, 3xNWindVel" meters typedef ^ ^ IfW_UniformWind_ParameterType UniformWind - - - "Parameters from UniformWind" - diff --git a/modules/inflowwind/src/InflowWind_Subs.f90 b/modules/inflowwind/src/InflowWind_Subs.f90 index 9d00ad387f..42ebf32656 100644 --- a/modules/inflowwind/src/InflowWind_Subs.f90 +++ b/modules/inflowwind/src/InflowWind_Subs.f90 @@ -283,7 +283,7 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err ! Read PropagationDir CALL ReadVar( UnitInput, InputFileName, InputFileData%PropagationDir, 'PropagationDir', & - 'Direction of wind propagation (meteoroligical direction)', TmpErrStat, TmpErrMsg, UnitEcho ) + 'Direction of wind propagation (meteorological direction) (deg)', TmpErrStat, TmpErrMsg, UnitEcho ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) THEN CALL CleanUp() @@ -291,6 +291,17 @@ SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, Err ENDIF + ! Read Upflow angle: + CALL ReadVar( UnitInput, InputFileName, InputFileData%VFlowAngle, 'VFlowAngle', & + 'Upflow angle (deg)', TmpErrStat, TmpErrMsg, UnitEcho ) + CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) THEN + CALL CleanUp() + RETURN + ENDIF + + + ! Read the number of points for the wind velocity output CALL ReadVar( UnitInput, InputFileName, InputFileData%NWindVel, 'NWindVel', & 'Number of points to output the wind velocity (0 to 9)', & @@ -1299,7 +1310,7 @@ SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrM !! The rotation matrix _RotToWind_, (\f$ M \f$), rotates the spatial points (\f$ [ X~ Y~ Z ] \f$) into the coordinate system !! the wind (\f$ [ X'~ Y'~ Z' ] \f$). This is a rotation about the point \f$ [ 0~ 0~ H ] \f$ (H = hub reference height), first !! about the \f$ Y\f$-axis (in the negative direction), followed by a rotation about \f$Z\f$-axis (also in the negative direction - !! due to the meteorological convention used). In the implimentation, the rotation is about the coordinate \f$ [ 0~ 0~ H ] \f$ + !! due to the meteorological convention used). In the implementation, the rotation is about the coordinate \f$ [ 0~ 0~ H ] \f$ !! where \f$ H \f$ is the reference height of the turbine (hub height). !! !! \f$ [X'~ Y'~ Z'] = M \left( [X~ Y~ Z] - [ 0~ 0~ H] \right) + [ 0~ 0~ H ] \f$ @@ -1337,9 +1348,6 @@ SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrM !! the upflow angle (_VFlowAngle_). - - - ! Create the rotation matrices -- rotate from XYZ to X'Y'Z' (wind aligned along X) coordinates ! Included in this rotation is the wind upflow (inclination) angle (rotation about Y axis) p%RotToWind(1,:) = (/ COS(-p%VFlowAngle) * COS(-p%PropagationDir), COS(-p%VFlowAngle) * SIN(-p%PropagationDir), -SIN(-p%VFlowAngle) /) @@ -1349,19 +1357,22 @@ SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrM ! Create the rotation matrices -- rotate from X'Y'Z' (wind aligned along X) to global XYZ coordinates p%RotFromWind = TRANSPOSE(p%RotToWind) - ! Create the array used for holding the rotated list of WindViXYZ coordinates in the wind reference frame, and populate it CALL AllocAry( p%WindViXYZprime, 3, p%NWindVel, 'Array for WindViXYZ coordinates in the wind reference frame', & TmpErrStat, TmpErrMsg ) CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) IF ( ErrStat>= AbortErrLev ) RETURN + p%RefPosition = (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) + p%WindViXYZprime = 0.0_ReKi ! set the output points. Note rotation is about the hub height at [0 0 H]. See InflowWind_SetParameters for details. DO I = 1,p%NWindVel - p%WindViXYZprime(:,I) = MATMUL( p%RotToWind, (p%WindViXYZ(:,I) - (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) )) + (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) + p%WindViXYZprime(:,I) = MATMUL( p%RotToWind, (p%WindViXYZ(:,I) - p%RefPosition )) + p%RefPosition ENDDO - + + p%RotateWindBox = .not. (EqualRealNos (p%PropagationDir, 0.0_ReKi) .AND. EqualRealNos (p%VFlowAngle, 0.0_ReKi)) + END SUBROUTINE InflowWind_SetParameters @@ -1619,7 +1630,7 @@ SUBROUTINE SetAllOuts( p, y, m, ErrStat, ErrMsg ) END DO - !FIXME: Add in Wind1Dir, Wind1Mag etc. -- allthough those can be derived outside of FAST. + !FIXME: Add in Wind1Dir, Wind1Mag etc. -- although those can be derived outside of FAST. DO I = 1,MIN(5, p%lidar%NumPulseGate ) m%AllOuts( WindMeas(I) ) = y%lidar%lidSpeed(I) @@ -1773,12 +1784,12 @@ SUBROUTINE CalculateOutput( Time, InputData, p, x, xd, z, OtherStates, y, m, Fil ! Apply the coordinate transformation to the PositionXYZ coordinates to get the PositionXYZprime coordinate list ! If the PropagationDir is zero, we don't need to apply this and will simply copy the data. Repeat for the WindViXYZ. - IF ( EqualRealNos (p%PropagationDir, 0.0_ReKi) .AND. EqualRealNos (p%VFlowAngle, 0.0_ReKi) ) THEN + IF ( .not. p%RotateWindBox ) THEN PositionXYZprime = InputData%PositionXYZ ELSE ! NOTE: rotations are about the hub at [ 0 0 H ]. See InflowWind_SetParameters for details. DO I = 1,SIZE(InputData%PositionXYZ,DIM=2) - PositionXYZprime(:,I) = MATMUL( p%RotToWind, (InputData%PositionXYZ(:,I) - (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /)) ) + (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) + PositionXYZprime(:,I) = MATMUL( p%RotToWind, (InputData%PositionXYZ(:,I) - p%RefPosition) ) + p%RefPosition ENDDO ENDIF @@ -1979,17 +1990,17 @@ SUBROUTINE CalculateOutput( Time, InputData, p, x, xd, z, OtherStates, y, m, Fil ! UVW contains the direction components of the wind at XYZ after translation from the U'V'W' wind velocity components ! in the X'Y'Z' (wind file) coordinate frame. ! NOTE: rotations are about the hub at [ 0 0 H ]. See InflowWind_SetParameters for details. - IF ( .NOT. (EqualRealNos (p%PropagationDir, 0.0_ReKi) .AND. EqualRealNos (p%VFlowAngle, 0.0_ReKi) )) THEN + IF ( p%RotateWindBox ) THEN DO I = 1,SIZE(y%VelocityUVW,DIM=2) - y%VelocityUVW(:,I) = MATMUL( p%RotFromWind, (y%VelocityUVW(:,I) - (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /)) ) + (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) + y%VelocityUVW(:,I) = MATMUL( p%RotFromWind, y%VelocityUVW(:,I) ) ENDDO ENDIF ! We also need to rotate the reference frame for the WindViUVW array ! NOTE: rotations are about the hub at [ 0 0 H ]. See InflowWind_SetParameters for details. - IF ( .NOT. (EqualRealNos (p%PropagationDir, 0.0_ReKi) .AND. EqualRealNos (p%VFlowAngle, 0.0_ReKi) ) .AND. FillWrOut ) THEN + IF ( p%RotateWindBox .AND. FillWrOut ) THEN DO I = 1,SIZE(m%WindViUVW,DIM=2) - m%WindViUVW(:,I) = MATMUL( p%RotFromWind, (m%WindViUVW(:,I) - (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /)) ) + (/ 0.0_ReKi, 0.0_ReKi, p%ReferenceHeight /) + m%WindViUVW(:,I) = MATMUL( p%RotFromWind, m%WindViUVW(:,I) ) ENDDO ENDIF diff --git a/modules/inflowwind/src/InflowWind_Types.f90 b/modules/inflowwind/src/InflowWind_Types.f90 index 66fcb0998a..c5c4e14c60 100644 --- a/modules/inflowwind/src/InflowWind_Types.f90 +++ b/modules/inflowwind/src/InflowWind_Types.f90 @@ -157,14 +157,16 @@ MODULE InflowWind_Types TYPE, PUBLIC :: InflowWind_ParameterType CHARACTER(1024) :: RootFileName !< Root of the InflowWind input filename [-] LOGICAL :: CTTS_Flag = .FALSE. !< determines if coherent turbulence is used [-] + LOGICAL :: RotateWindBox = .FALSE. !< determines if wind will be rotated [-] REAL(DbKi) :: DT !< Time step for cont. state integration & disc. state update [seconds] REAL(ReKi) :: PropagationDir !< Direction of wind propagation [radians] REAL(ReKi) :: VFlowAngle !< Vertical (upflow) angle [radians] REAL(ReKi) , DIMENSION(1:3,1:3) :: RotToWind !< Rotation matrix for rotating from the global XYZ coordinate system to the wind coordinate system (wind along X') [-] REAL(ReKi) , DIMENSION(1:3,1:3) :: RotFromWind !< Rotation matrix for rotating from the wind coordinate system (wind along X') back to the global XYZ coordinate system. Equal to TRANSPOSE(RotToWind) [-] - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WindViXYZprime !< List of XYZ coordinates for velocity measurements, translated to the wind coordinate system (prime coordinates). This equals MATMUL( RotToWind, ParamData%WindViXYZ ) [meters/second] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WindViXYZprime !< List of XYZ coordinates for velocity measurements, translated to the wind coordinate system (prime coordinates). This equals MATMUL( RotToWind, ParamData%WindViXYZ ) [meters] INTEGER(IntKi) :: WindType = 0 !< Type of wind -- set to Undef_Wind initially [-] REAL(ReKi) :: ReferenceHeight !< Height of the wind turbine [meters] + REAL(ReKi) , DIMENSION(1:3) :: RefPosition !< Reference position (point where box is rotated) [meters] INTEGER(IntKi) :: NWindVel !< Number of points in the wind velocity list [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WindViXYZ !< List of XYZ coordinates for wind velocity measurements, 3xNWindVel [meters] TYPE(IfW_UniformWind_ParameterType) :: UniformWind !< Parameters from UniformWind [-] @@ -3048,6 +3050,7 @@ SUBROUTINE InflowWind_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg = "" DstParamData%RootFileName = SrcParamData%RootFileName DstParamData%CTTS_Flag = SrcParamData%CTTS_Flag + DstParamData%RotateWindBox = SrcParamData%RotateWindBox DstParamData%DT = SrcParamData%DT DstParamData%PropagationDir = SrcParamData%PropagationDir DstParamData%VFlowAngle = SrcParamData%VFlowAngle @@ -3069,6 +3072,7 @@ SUBROUTINE InflowWind_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ENDIF DstParamData%WindType = SrcParamData%WindType DstParamData%ReferenceHeight = SrcParamData%ReferenceHeight + DstParamData%RefPosition = SrcParamData%RefPosition DstParamData%NWindVel = SrcParamData%NWindVel IF (ALLOCATED(SrcParamData%WindViXYZ)) THEN i1_l = LBOUND(SrcParamData%WindViXYZ,1) @@ -3208,6 +3212,7 @@ SUBROUTINE InflowWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er Int_BufSz = 0 Int_BufSz = Int_BufSz + 1*LEN(InData%RootFileName) ! RootFileName Int_BufSz = Int_BufSz + 1 ! CTTS_Flag + Int_BufSz = Int_BufSz + 1 ! RotateWindBox Db_BufSz = Db_BufSz + 1 ! DT Re_BufSz = Re_BufSz + 1 ! PropagationDir Re_BufSz = Re_BufSz + 1 ! VFlowAngle @@ -3220,6 +3225,7 @@ SUBROUTINE InflowWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er END IF Int_BufSz = Int_BufSz + 1 ! WindType Re_BufSz = Re_BufSz + 1 ! ReferenceHeight + Re_BufSz = Re_BufSz + SIZE(InData%RefPosition) ! RefPosition Int_BufSz = Int_BufSz + 1 ! NWindVel Int_BufSz = Int_BufSz + 1 ! WindViXYZ allocated yes/no IF ( ALLOCATED(InData%WindViXYZ) ) THEN @@ -3408,6 +3414,8 @@ SUBROUTINE InflowWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er END DO ! I IntKiBuf(Int_Xferred) = TRANSFER(InData%CTTS_Flag, IntKiBuf(1)) Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%RotateWindBox, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 DbKiBuf(Db_Xferred) = InData%DT Db_Xferred = Db_Xferred + 1 ReKiBuf(Re_Xferred) = InData%PropagationDir @@ -3450,6 +3458,10 @@ SUBROUTINE InflowWind_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er Int_Xferred = Int_Xferred + 1 ReKiBuf(Re_Xferred) = InData%ReferenceHeight Re_Xferred = Re_Xferred + 1 + DO i1 = LBOUND(InData%RefPosition,1), UBOUND(InData%RefPosition,1) + ReKiBuf(Re_Xferred) = InData%RefPosition(i1) + Re_Xferred = Re_Xferred + 1 + END DO IntKiBuf(Int_Xferred) = InData%NWindVel Int_Xferred = Int_Xferred + 1 IF ( .NOT. ALLOCATED(InData%WindViXYZ) ) THEN @@ -3767,6 +3779,8 @@ SUBROUTINE InflowWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, END DO ! I OutData%CTTS_Flag = TRANSFER(IntKiBuf(Int_Xferred), OutData%CTTS_Flag) Int_Xferred = Int_Xferred + 1 + OutData%RotateWindBox = TRANSFER(IntKiBuf(Int_Xferred), OutData%RotateWindBox) + Int_Xferred = Int_Xferred + 1 OutData%DT = DbKiBuf(Db_Xferred) Db_Xferred = Db_Xferred + 1 OutData%PropagationDir = ReKiBuf(Re_Xferred) @@ -3820,6 +3834,12 @@ SUBROUTINE InflowWind_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Int_Xferred = Int_Xferred + 1 OutData%ReferenceHeight = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 + i1_l = LBOUND(OutData%RefPosition,1) + i1_u = UBOUND(OutData%RefPosition,1) + DO i1 = LBOUND(OutData%RefPosition,1), UBOUND(OutData%RefPosition,1) + OutData%RefPosition(i1) = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END DO OutData%NWindVel = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! WindViXYZ not allocated From 7f2ce22e3e4b2d4fdf9230d79465b41bf907daef Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Tue, 22 Sep 2020 10:23:00 -0600 Subject: [PATCH 06/16] vs build: fix merge issue + make consistent debug compile opts --- vs-build/FASTlib/FASTlib.vfproj | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/vs-build/FASTlib/FASTlib.vfproj b/vs-build/FASTlib/FASTlib.vfproj index 636bea3a34..8c0557d939 100644 --- a/vs-build/FASTlib/FASTlib.vfproj +++ b/vs-build/FASTlib/FASTlib.vfproj @@ -1155,7 +1155,15 @@ - + + + + + + + + + @@ -1168,7 +1176,6 @@ - From 357253e39976918d5d330440e31cb2a2bdaebd17 Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Tue, 29 Sep 2020 09:26:57 -0600 Subject: [PATCH 07/16] Make channel length consistent between C and Fortran sides of interface --- modules/openfast-library/src/FAST_Library.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/openfast-library/src/FAST_Library.h b/modules/openfast-library/src/FAST_Library.h index 2d1e7833ce..494769c093 100644 --- a/modules/openfast-library/src/FAST_Library.h +++ b/modules/openfast-library/src/FAST_Library.h @@ -41,10 +41,10 @@ EXTERNAL_ROUTINE void FAST_CreateCheckpoint(int * iTurb, const char *CheckpointR #define SensorType_None -1 -// make sure these parameters match with FAST_Library.f90 +// make sure these parameters match with FAST_Library.f90 and NWTC_Base.f90 #define MAXIMUM_BLADES 3 #define MAXIMUM_OUTPUTS 4000 -#define CHANNEL_LENGTH 10 +#define CHANNEL_LENGTH 20 #define MAXInitINPUTS 10 #define NumFixedInputs 2 + 2 + MAXIMUM_BLADES + 1 From 881421b007eddcd4a35a223701a9eaeffd7b3bb7 Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Mon, 26 Oct 2020 16:21:58 -0600 Subject: [PATCH 08/16] InflowWind updates - do not allow vertical flow angle for linearization (requires re-working of the math, which I haven't had time to do) - added check for weird problem with linux compilers seeming to return results from MODULO function in unexpected range. - files listed in the IfW driver will be relative to the file they are specified in - fixed some error handling - allow the uniform wind files to contain an additional column specifying the upflow angle - added `Wind*VelXY`, `Wind*VelMag`, and `WindAngXY` output channels --- docs/OtherSupporting/OutListParameters.xlsx | Bin 222909 -> 224103 bytes modules/inflowwind/src/IfW_FFWind_Base.f90 | 4 +- modules/inflowwind/src/IfW_UniformWind.f90 | 102 ++++++-- modules/inflowwind/src/IfW_UniformWind.txt | 2 + .../inflowwind/src/IfW_UniformWind_Types.f90 | 61 +++++ modules/inflowwind/src/InflowWind.f90 | 64 +++-- modules/inflowwind/src/InflowWind_Driver.f90 | 6 +- .../inflowwind/src/InflowWind_Driver_Subs.f90 | 10 +- modules/inflowwind/src/InflowWind_Subs.f90 | 224 ++++++++++++------ 9 files changed, 356 insertions(+), 117 deletions(-) diff --git a/docs/OtherSupporting/OutListParameters.xlsx b/docs/OtherSupporting/OutListParameters.xlsx index 98bf58db1a3d4545f86d00ad7300482e7da4c9b9..109f79949d7067e4cb0ee2d60ae5d6af8adb8acf 100644 GIT binary patch delta 178096 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 z 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