From e8226fc66498b25ade6719983d8e36872b5ea99f Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 13 Nov 2019 22:36:11 -0700 Subject: [PATCH 001/190] FVW: Starting implementation with main function placeholders and registry types --- modules/aerodyn14/CMakeLists.txt | 13 + modules/aerodyn14/src/AD14AeroConf_Types.f90 | 2921 +++++++++++ modules/aerodyn14/src/AeroDyn14.f90 | 257 +- modules/aerodyn14/src/AeroDyn14_Types.f90 | 3597 +++++--------- modules/aerodyn14/src/AeroSubs.f90 | 205 +- modules/aerodyn14/src/FVW.f90 | 568 +++ modules/aerodyn14/src/FVW_IO.f90 | 130 + modules/aerodyn14/src/FVW_Subs.f90 | 278 ++ modules/aerodyn14/src/FVW_Types.f90 | 4367 +++++++++++++++++ modules/aerodyn14/src/FVW_VortexTools.f90 | 11 + modules/aerodyn14/src/FVW_Wings.f90 | 194 + modules/aerodyn14/src/GenSubs.f90 | 21 +- modules/aerodyn14/src/Interpolation.f90 | 129 + modules/aerodyn14/src/Registry-AD14.txt | 92 +- .../aerodyn14/src/Registry-AD14AeroConf.txt | 56 + modules/aerodyn14/src/Registry-FVW.txt | 100 + modules/aerodyn14/src/VTK.f90 | 538 ++ modules/elastodyn/src/ElastoDyn_IO.f90 | 4 +- modules/openfast-library/src/FAST_Subs.f90 | 36 +- modules/openfast-library/src/FAST_Types.f90 | 2 + 20 files changed, 10831 insertions(+), 2688 deletions(-) create mode 100644 modules/aerodyn14/src/AD14AeroConf_Types.f90 create mode 100644 modules/aerodyn14/src/FVW.f90 create mode 100644 modules/aerodyn14/src/FVW_IO.f90 create mode 100644 modules/aerodyn14/src/FVW_Subs.f90 create mode 100644 modules/aerodyn14/src/FVW_Types.f90 create mode 100644 modules/aerodyn14/src/FVW_VortexTools.f90 create mode 100644 modules/aerodyn14/src/FVW_Wings.f90 create mode 100644 modules/aerodyn14/src/Interpolation.f90 create mode 100644 modules/aerodyn14/src/Registry-AD14AeroConf.txt create mode 100644 modules/aerodyn14/src/Registry-FVW.txt create mode 100755 modules/aerodyn14/src/VTK.f90 diff --git a/modules/aerodyn14/CMakeLists.txt b/modules/aerodyn14/CMakeLists.txt index be1846cc91..7350432f28 100644 --- a/modules/aerodyn14/CMakeLists.txt +++ b/modules/aerodyn14/CMakeLists.txt @@ -16,7 +16,9 @@ if (GENERATE_TYPES) generate_f90_types(src/Registry-AD14.txt ${CMAKE_CURRENT_LIST_DIR}/src/AeroDyn14_Types.f90) + generate_f90_types(src/Registry-AD14AeroConf.txt ${CMAKE_CURRENT_LIST_DIR}/src/AD14AeroConf_Types.f90) generate_f90_types(src/Registry-DWM.txt ${CMAKE_CURRENT_LIST_DIR}/src/DWM_Types.f90) + generate_f90_types(src/Registry-FVW.txt ${CMAKE_CURRENT_LIST_DIR}/src/FVW_Types.f90) endif() set(AD14_LIBS_SOURCES @@ -25,8 +27,19 @@ set(AD14_LIBS_SOURCES src/DWM.f90 src/DWM_Wake_Sub_ver2.f90 src/GenSubs.f90 + + src/FVW.f90 + src/FVW_IO.f90 + src/FVW_VortexTools.f90 + src/FVW_Wings.f90 + src/FVW_Subs.f90 + src/VTK.f90 + src/Interpolation.f90 + src/AeroDyn14_Types.f90 src/DWM_Types.f90 + src/FVW_Types.f90 + src/AD14AeroConf_Types.f90 ) add_library(aerodyn14lib ${AD14_LIBS_SOURCES}) diff --git a/modules/aerodyn14/src/AD14AeroConf_Types.f90 b/modules/aerodyn14/src/AD14AeroConf_Types.f90 new file mode 100644 index 0000000000..3f91a12a63 --- /dev/null +++ b/modules/aerodyn14/src/AD14AeroConf_Types.f90 @@ -0,0 +1,2921 @@ +!STARTOFREGISTRYGENERATEDFILE 'AD14AeroConf_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 +!********************************************************************************************************************************* +! AD14AeroConf_Types +!................................................................................................................................. +! This file is part of AD14AeroConf. +! +! 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 AD14AeroConf. 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 AD14AeroConf_Types +!--------------------------------------------------------------------------------------------------------------------------------- +USE NWTC_Library +IMPLICIT NONE +! ========= Marker ======= + TYPE, PUBLIC :: Marker + REAL(ReKi) , DIMENSION(1:3) :: Position + REAL(ReKi) , DIMENSION(1:3,1:3) :: Orientation + REAL(ReKi) , DIMENSION(1:3) :: TranslationVel + REAL(ReKi) , DIMENSION(1:3) :: RotationVel + END TYPE Marker +! ======================= +! ========= AD14AeroConf_MiscVarType ======= + TYPE, PUBLIC :: AD14AeroConf_MiscVarType + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: AL + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CD + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CL + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CM + REAL(ReKi) :: PMC + REAL(ReKi) :: MulTabLoc + END TYPE AD14AeroConf_MiscVarType +! ======================= +! ========= AD14AeroConf_ParameterType ======= + TYPE, PUBLIC :: AD14AeroConf_ParameterType + INTEGER(IntKi) :: MaxTable = 20 + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: NTables + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: NLift + INTEGER(IntKi) :: NumCL + INTEGER(IntKi) :: NumFoil + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: NFoil + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: MulTabMet + CHARACTER(1024) , DIMENSION(:), ALLOCATABLE :: FoilNm + END TYPE AD14AeroConf_ParameterType +! ======================= +! ========= AD14AeroConf_InputType ======= + TYPE, PUBLIC :: AD14AeroConf_InputType + TYPE(Marker) , DIMENSION(:), ALLOCATABLE :: Blade + TYPE(Marker) :: Hub + TYPE(Marker) :: RotorFurl + TYPE(Marker) :: Nacelle + TYPE(Marker) :: TailFin + TYPE(Marker) :: Tower + TYPE(Marker) :: SubStructure + TYPE(Marker) :: Foundation + REAL(ReKi) :: BladeLength + END TYPE AD14AeroConf_InputType +! ======================= +! ========= AD14AeroConf_OutputType ======= + TYPE, PUBLIC :: AD14AeroConf_OutputType + REAL(ReKi) :: Dummy + END TYPE AD14AeroConf_OutputType +! ======================= +CONTAINS + SUBROUTINE AD14AeroConf_CopyMarker( SrcMarkerData, DstMarkerData, CtrlCode, ErrStat, ErrMsg ) + TYPE(Marker), INTENT(IN) :: SrcMarkerData + TYPE(Marker), INTENT(INOUT) :: DstMarkerData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_CopyMarker' +! + ErrStat = ErrID_None + ErrMsg = "" + DstMarkerData%Position = SrcMarkerData%Position + DstMarkerData%Orientation = SrcMarkerData%Orientation + DstMarkerData%TranslationVel = SrcMarkerData%TranslationVel + DstMarkerData%RotationVel = SrcMarkerData%RotationVel + END SUBROUTINE AD14AeroConf_CopyMarker + + SUBROUTINE AD14AeroConf_DestroyMarker( MarkerData, ErrStat, ErrMsg ) + TYPE(Marker), INTENT(INOUT) :: MarkerData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_DestroyMarker' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE AD14AeroConf_DestroyMarker + + SUBROUTINE AD14AeroConf_PackMarker( 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(Marker), 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 = 'AD14AeroConf_PackMarker' + ! 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 + Re_BufSz = Re_BufSz + SIZE(InData%Position) ! Position + Re_BufSz = Re_BufSz + SIZE(InData%Orientation) ! Orientation + Re_BufSz = Re_BufSz + SIZE(InData%TranslationVel) ! TranslationVel + Re_BufSz = Re_BufSz + SIZE(InData%RotationVel) ! RotationVel + 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 + + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Position))-1 ) = PACK(InData%Position,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Position) + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Orientation))-1 ) = PACK(InData%Orientation,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Orientation) + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%TranslationVel))-1 ) = PACK(InData%TranslationVel,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%TranslationVel) + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%RotationVel))-1 ) = PACK(InData%RotationVel,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%RotationVel) + END SUBROUTINE AD14AeroConf_PackMarker + + SUBROUTINE AD14AeroConf_UnPackMarker( 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(Marker), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_UnPackMarker' + ! 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 + i1_l = LBOUND(OutData%Position,1) + i1_u = UBOUND(OutData%Position,1) + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + OutData%Position = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Position))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Position) + DEALLOCATE(mask1) + i1_l = LBOUND(OutData%Orientation,1) + i1_u = UBOUND(OutData%Orientation,1) + i2_l = LBOUND(OutData%Orientation,2) + i2_u = UBOUND(OutData%Orientation,2) + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + OutData%Orientation = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Orientation))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Orientation) + DEALLOCATE(mask2) + i1_l = LBOUND(OutData%TranslationVel,1) + i1_u = UBOUND(OutData%TranslationVel,1) + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + OutData%TranslationVel = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%TranslationVel))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%TranslationVel) + DEALLOCATE(mask1) + i1_l = LBOUND(OutData%RotationVel,1) + i1_u = UBOUND(OutData%RotationVel,1) + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + OutData%RotationVel = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%RotationVel))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%RotationVel) + DEALLOCATE(mask1) + END SUBROUTINE AD14AeroConf_UnPackMarker + + SUBROUTINE AD14AeroConf_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) + TYPE(AD14AeroConf_MiscVarType), INTENT(IN) :: SrcMiscData + TYPE(AD14AeroConf_MiscVarType), INTENT(INOUT) :: DstMiscData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_CopyMisc' +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(SrcMiscData%AL)) THEN + i1_l = LBOUND(SrcMiscData%AL,1) + i1_u = UBOUND(SrcMiscData%AL,1) + i2_l = LBOUND(SrcMiscData%AL,2) + i2_u = UBOUND(SrcMiscData%AL,2) + IF (.NOT. ALLOCATED(DstMiscData%AL)) THEN + ALLOCATE(DstMiscData%AL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%AL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%AL = SrcMiscData%AL +ENDIF +IF (ALLOCATED(SrcMiscData%CD)) THEN + i1_l = LBOUND(SrcMiscData%CD,1) + i1_u = UBOUND(SrcMiscData%CD,1) + i2_l = LBOUND(SrcMiscData%CD,2) + i2_u = UBOUND(SrcMiscData%CD,2) + i3_l = LBOUND(SrcMiscData%CD,3) + i3_u = UBOUND(SrcMiscData%CD,3) + IF (.NOT. ALLOCATED(DstMiscData%CD)) THEN + ALLOCATE(DstMiscData%CD(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%CD.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%CD = SrcMiscData%CD +ENDIF +IF (ALLOCATED(SrcMiscData%CL)) THEN + i1_l = LBOUND(SrcMiscData%CL,1) + i1_u = UBOUND(SrcMiscData%CL,1) + i2_l = LBOUND(SrcMiscData%CL,2) + i2_u = UBOUND(SrcMiscData%CL,2) + i3_l = LBOUND(SrcMiscData%CL,3) + i3_u = UBOUND(SrcMiscData%CL,3) + IF (.NOT. ALLOCATED(DstMiscData%CL)) THEN + ALLOCATE(DstMiscData%CL(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%CL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%CL = SrcMiscData%CL +ENDIF +IF (ALLOCATED(SrcMiscData%CM)) THEN + i1_l = LBOUND(SrcMiscData%CM,1) + i1_u = UBOUND(SrcMiscData%CM,1) + i2_l = LBOUND(SrcMiscData%CM,2) + i2_u = UBOUND(SrcMiscData%CM,2) + i3_l = LBOUND(SrcMiscData%CM,3) + i3_u = UBOUND(SrcMiscData%CM,3) + IF (.NOT. ALLOCATED(DstMiscData%CM)) THEN + ALLOCATE(DstMiscData%CM(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%CM.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%CM = SrcMiscData%CM +ENDIF + DstMiscData%PMC = SrcMiscData%PMC + DstMiscData%MulTabLoc = SrcMiscData%MulTabLoc + END SUBROUTINE AD14AeroConf_CopyMisc + + SUBROUTINE AD14AeroConf_DestroyMisc( MiscData, ErrStat, ErrMsg ) + TYPE(AD14AeroConf_MiscVarType), INTENT(INOUT) :: MiscData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_DestroyMisc' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(MiscData%AL)) THEN + DEALLOCATE(MiscData%AL) +ENDIF +IF (ALLOCATED(MiscData%CD)) THEN + DEALLOCATE(MiscData%CD) +ENDIF +IF (ALLOCATED(MiscData%CL)) THEN + DEALLOCATE(MiscData%CL) +ENDIF +IF (ALLOCATED(MiscData%CM)) THEN + DEALLOCATE(MiscData%CM) +ENDIF + END SUBROUTINE AD14AeroConf_DestroyMisc + + SUBROUTINE AD14AeroConf_PackMisc( 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(AD14AeroConf_MiscVarType), 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 = 'AD14AeroConf_PackMisc' + ! 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 ! AL allocated yes/no + IF ( ALLOCATED(InData%AL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! AL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%AL) ! AL + END IF + Int_BufSz = Int_BufSz + 1 ! CD allocated yes/no + IF ( ALLOCATED(InData%CD) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! CD upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%CD) ! CD + END IF + Int_BufSz = Int_BufSz + 1 ! CL allocated yes/no + IF ( ALLOCATED(InData%CL) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! CL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%CL) ! CL + END IF + Int_BufSz = Int_BufSz + 1 ! CM allocated yes/no + IF ( ALLOCATED(InData%CM) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! CM upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%CM) ! CM + END IF + Re_BufSz = Re_BufSz + 1 ! PMC + Re_BufSz = Re_BufSz + 1 ! MulTabLoc + 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 + + IF ( .NOT. ALLOCATED(InData%AL) ) 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%AL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%AL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%AL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%AL))-1 ) = PACK(InData%AL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%AL) + END IF + IF ( .NOT. ALLOCATED(InData%CD) ) 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%CD,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CD,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CD,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CD,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CD,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CD,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%CD)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CD))-1 ) = PACK(InData%CD,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%CD) + END IF + IF ( .NOT. ALLOCATED(InData%CL) ) 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%CL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CL,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CL,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CL,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%CL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CL))-1 ) = PACK(InData%CL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%CL) + END IF + IF ( .NOT. ALLOCATED(InData%CM) ) 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%CM,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CM,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CM,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CM,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CM,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CM,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%CM)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CM))-1 ) = PACK(InData%CM,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%CM) + END IF + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%PMC + Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%MulTabLoc + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14AeroConf_PackMisc + + SUBROUTINE AD14AeroConf_UnPackMisc( 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(AD14AeroConf_MiscVarType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_UnPackMisc' + ! 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 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! AL 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 + IF (ALLOCATED(OutData%AL)) DEALLOCATE(OutData%AL) + ALLOCATE(OutData%AL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%AL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%AL)>0) OutData%AL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%AL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%AL) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CD 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%CD)) DEALLOCATE(OutData%CD) + ALLOCATE(OutData%CD(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%CD.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%CD)>0) OutData%CD = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CD))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%CD) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CL 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%CL)) DEALLOCATE(OutData%CL) + ALLOCATE(OutData%CL(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%CL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%CL)>0) OutData%CL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CL))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%CL) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CM 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%CM)) DEALLOCATE(OutData%CM) + ALLOCATE(OutData%CM(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%CM.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%CM)>0) OutData%CM = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CM))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%CM) + DEALLOCATE(mask3) + END IF + OutData%PMC = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%MulTabLoc = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14AeroConf_UnPackMisc + + SUBROUTINE AD14AeroConf_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg ) + TYPE(AD14AeroConf_ParameterType), INTENT(IN) :: SrcParamData + TYPE(AD14AeroConf_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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_CopyParam' +! + ErrStat = ErrID_None + ErrMsg = "" + DstParamData%MaxTable = SrcParamData%MaxTable +IF (ALLOCATED(SrcParamData%NTables)) THEN + i1_l = LBOUND(SrcParamData%NTables,1) + i1_u = UBOUND(SrcParamData%NTables,1) + IF (.NOT. ALLOCATED(DstParamData%NTables)) THEN + ALLOCATE(DstParamData%NTables(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%NTables.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%NTables = SrcParamData%NTables +ENDIF +IF (ALLOCATED(SrcParamData%NLift)) THEN + i1_l = LBOUND(SrcParamData%NLift,1) + i1_u = UBOUND(SrcParamData%NLift,1) + IF (.NOT. ALLOCATED(DstParamData%NLift)) THEN + ALLOCATE(DstParamData%NLift(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%NLift.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%NLift = SrcParamData%NLift +ENDIF + DstParamData%NumCL = SrcParamData%NumCL + DstParamData%NumFoil = SrcParamData%NumFoil +IF (ALLOCATED(SrcParamData%NFoil)) THEN + i1_l = LBOUND(SrcParamData%NFoil,1) + i1_u = UBOUND(SrcParamData%NFoil,1) + IF (.NOT. ALLOCATED(DstParamData%NFoil)) THEN + ALLOCATE(DstParamData%NFoil(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%NFoil.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%NFoil = SrcParamData%NFoil +ENDIF +IF (ALLOCATED(SrcParamData%MulTabMet)) THEN + i1_l = LBOUND(SrcParamData%MulTabMet,1) + i1_u = UBOUND(SrcParamData%MulTabMet,1) + i2_l = LBOUND(SrcParamData%MulTabMet,2) + i2_u = UBOUND(SrcParamData%MulTabMet,2) + IF (.NOT. ALLOCATED(DstParamData%MulTabMet)) THEN + ALLOCATE(DstParamData%MulTabMet(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%MulTabMet.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%MulTabMet = SrcParamData%MulTabMet +ENDIF +IF (ALLOCATED(SrcParamData%FoilNm)) THEN + i1_l = LBOUND(SrcParamData%FoilNm,1) + i1_u = UBOUND(SrcParamData%FoilNm,1) + IF (.NOT. ALLOCATED(DstParamData%FoilNm)) THEN + ALLOCATE(DstParamData%FoilNm(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%FoilNm.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%FoilNm = SrcParamData%FoilNm +ENDIF + END SUBROUTINE AD14AeroConf_CopyParam + + SUBROUTINE AD14AeroConf_DestroyParam( ParamData, ErrStat, ErrMsg ) + TYPE(AD14AeroConf_ParameterType), INTENT(INOUT) :: ParamData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_DestroyParam' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(ParamData%NTables)) THEN + DEALLOCATE(ParamData%NTables) +ENDIF +IF (ALLOCATED(ParamData%NLift)) THEN + DEALLOCATE(ParamData%NLift) +ENDIF +IF (ALLOCATED(ParamData%NFoil)) THEN + DEALLOCATE(ParamData%NFoil) +ENDIF +IF (ALLOCATED(ParamData%MulTabMet)) THEN + DEALLOCATE(ParamData%MulTabMet) +ENDIF +IF (ALLOCATED(ParamData%FoilNm)) THEN + DEALLOCATE(ParamData%FoilNm) +ENDIF + END SUBROUTINE AD14AeroConf_DestroyParam + + SUBROUTINE AD14AeroConf_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(AD14AeroConf_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 = 'AD14AeroConf_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 ! MaxTable + Int_BufSz = Int_BufSz + 1 ! NTables allocated yes/no + IF ( ALLOCATED(InData%NTables) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! NTables upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%NTables) ! NTables + END IF + Int_BufSz = Int_BufSz + 1 ! NLift allocated yes/no + IF ( ALLOCATED(InData%NLift) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! NLift upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%NLift) ! NLift + END IF + Int_BufSz = Int_BufSz + 1 ! NumCL + Int_BufSz = Int_BufSz + 1 ! NumFoil + Int_BufSz = Int_BufSz + 1 ! NFoil allocated yes/no + IF ( ALLOCATED(InData%NFoil) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! NFoil upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%NFoil) ! NFoil + END IF + Int_BufSz = Int_BufSz + 1 ! MulTabMet allocated yes/no + IF ( ALLOCATED(InData%MulTabMet) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! MulTabMet upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%MulTabMet) ! MulTabMet + END IF + Int_BufSz = Int_BufSz + 1 ! FoilNm allocated yes/no + IF ( ALLOCATED(InData%FoilNm) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! FoilNm upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%FoilNm)*LEN(InData%FoilNm) ! FoilNm + END IF + 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:Int_Xferred+(1)-1 ) = InData%MaxTable + Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%NTables) ) 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%NTables,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%NTables,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%NTables)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%NTables))-1 ) = PACK(InData%NTables,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(InData%NTables) + END IF + IF ( .NOT. ALLOCATED(InData%NLift) ) 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%NLift,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%NLift,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%NLift)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%NLift))-1 ) = PACK(InData%NLift,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(InData%NLift) + END IF + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumCL + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumFoil + Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%NFoil) ) 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%NFoil,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%NFoil,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%NFoil)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%NFoil))-1 ) = PACK(InData%NFoil,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(InData%NFoil) + END IF + IF ( .NOT. ALLOCATED(InData%MulTabMet) ) 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%MulTabMet,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%MulTabMet,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%MulTabMet,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%MulTabMet,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%MulTabMet)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%MulTabMet))-1 ) = PACK(InData%MulTabMet,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%MulTabMet) + END IF + IF ( .NOT. ALLOCATED(InData%FoilNm) ) 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%FoilNm,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FoilNm,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%FoilNm,1), UBOUND(InData%FoilNm,1) + DO I = 1, LEN(InData%FoilNm) + IntKiBuf(Int_Xferred) = ICHAR(InData%FoilNm(i1)(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I + END DO !i1 + END IF + END SUBROUTINE AD14AeroConf_PackParam + + SUBROUTINE AD14AeroConf_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(AD14AeroConf_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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_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%MaxTable = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! NTables 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 + IF (ALLOCATED(OutData%NTables)) DEALLOCATE(OutData%NTables) + ALLOCATE(OutData%NTables(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%NTables.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%NTables)>0) OutData%NTables = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%NTables))-1 ), mask1, 0_IntKi ) + Int_Xferred = Int_Xferred + SIZE(OutData%NTables) + DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! NLift 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 + IF (ALLOCATED(OutData%NLift)) DEALLOCATE(OutData%NLift) + ALLOCATE(OutData%NLift(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%NLift.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%NLift)>0) OutData%NLift = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%NLift))-1 ), mask1, 0_IntKi ) + Int_Xferred = Int_Xferred + SIZE(OutData%NLift) + DEALLOCATE(mask1) + END IF + OutData%NumCL = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%NumFoil = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! NFoil 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 + IF (ALLOCATED(OutData%NFoil)) DEALLOCATE(OutData%NFoil) + ALLOCATE(OutData%NFoil(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%NFoil.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%NFoil)>0) OutData%NFoil = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%NFoil))-1 ), mask1, 0_IntKi ) + Int_Xferred = Int_Xferred + SIZE(OutData%NFoil) + DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! MulTabMet 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 + IF (ALLOCATED(OutData%MulTabMet)) DEALLOCATE(OutData%MulTabMet) + ALLOCATE(OutData%MulTabMet(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%MulTabMet.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%MulTabMet)>0) OutData%MulTabMet = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%MulTabMet))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%MulTabMet) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FoilNm 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 + IF (ALLOCATED(OutData%FoilNm)) DEALLOCATE(OutData%FoilNm) + ALLOCATE(OutData%FoilNm(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FoilNm.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + DO i1 = LBOUND(OutData%FoilNm,1), UBOUND(OutData%FoilNm,1) + DO I = 1, LEN(OutData%FoilNm) + OutData%FoilNm(i1)(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I + END DO !i1 + DEALLOCATE(mask1) + END IF + END SUBROUTINE AD14AeroConf_UnPackParam + + SUBROUTINE AD14AeroConf_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) + TYPE(AD14AeroConf_InputType), INTENT(IN) :: SrcInputData + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: DstInputData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_CopyInput' +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(SrcInputData%Blade)) THEN + i1_l = LBOUND(SrcInputData%Blade,1) + i1_u = UBOUND(SrcInputData%Blade,1) + IF (.NOT. ALLOCATED(DstInputData%Blade)) THEN + ALLOCATE(DstInputData%Blade(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInputData%Blade.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DO i1 = LBOUND(SrcInputData%Blade,1), UBOUND(SrcInputData%Blade,1) + CALL AD14AeroConf_Copymarker( SrcInputData%Blade(i1), DstInputData%Blade(i1), CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + ENDDO +ENDIF + CALL AD14AeroConf_Copymarker( SrcInputData%Hub, DstInputData%Hub, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14AeroConf_Copymarker( SrcInputData%RotorFurl, DstInputData%RotorFurl, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14AeroConf_Copymarker( SrcInputData%Nacelle, DstInputData%Nacelle, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14AeroConf_Copymarker( SrcInputData%TailFin, DstInputData%TailFin, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14AeroConf_Copymarker( SrcInputData%Tower, DstInputData%Tower, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14AeroConf_Copymarker( SrcInputData%SubStructure, DstInputData%SubStructure, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14AeroConf_Copymarker( SrcInputData%Foundation, DstInputData%Foundation, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + DstInputData%BladeLength = SrcInputData%BladeLength + END SUBROUTINE AD14AeroConf_CopyInput + + SUBROUTINE AD14AeroConf_DestroyInput( InputData, ErrStat, ErrMsg ) + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: InputData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_DestroyInput' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(InputData%Blade)) THEN +DO i1 = LBOUND(InputData%Blade,1), UBOUND(InputData%Blade,1) + CALL AD14AeroConf_Destroymarker( InputData%Blade(i1), ErrStat, ErrMsg ) +ENDDO + DEALLOCATE(InputData%Blade) +ENDIF + CALL AD14AeroConf_Destroymarker( InputData%Hub, ErrStat, ErrMsg ) + CALL AD14AeroConf_Destroymarker( InputData%RotorFurl, ErrStat, ErrMsg ) + CALL AD14AeroConf_Destroymarker( InputData%Nacelle, ErrStat, ErrMsg ) + CALL AD14AeroConf_Destroymarker( InputData%TailFin, ErrStat, ErrMsg ) + CALL AD14AeroConf_Destroymarker( InputData%Tower, ErrStat, ErrMsg ) + CALL AD14AeroConf_Destroymarker( InputData%SubStructure, ErrStat, ErrMsg ) + CALL AD14AeroConf_Destroymarker( InputData%Foundation, ErrStat, ErrMsg ) + END SUBROUTINE AD14AeroConf_DestroyInput + + SUBROUTINE AD14AeroConf_PackInput( 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(AD14AeroConf_InputType), 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 = 'AD14AeroConf_PackInput' + ! 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 ! Blade allocated yes/no + IF ( ALLOCATED(InData%Blade) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! Blade upper/lower bounds for each dimension + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + DO i1 = LBOUND(InData%Blade,1), UBOUND(InData%Blade,1) + Int_BufSz = Int_BufSz + 3 ! Blade: size of buffers for each call to pack subtype + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Blade(i1), ErrStat2, ErrMsg2, .TRUE. ) ! Blade + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Blade + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Blade + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Blade + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + END DO + END IF + Int_BufSz = Int_BufSz + 3 ! Hub: size of buffers for each call to pack subtype + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Hub, ErrStat2, ErrMsg2, .TRUE. ) ! Hub + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Hub + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Hub + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Hub + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! RotorFurl: size of buffers for each call to pack subtype + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%RotorFurl, ErrStat2, ErrMsg2, .TRUE. ) ! RotorFurl + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! RotorFurl + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! RotorFurl + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! RotorFurl + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! Nacelle: size of buffers for each call to pack subtype + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Nacelle, ErrStat2, ErrMsg2, .TRUE. ) ! Nacelle + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Nacelle + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Nacelle + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Nacelle + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! TailFin: size of buffers for each call to pack subtype + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%TailFin, ErrStat2, ErrMsg2, .TRUE. ) ! TailFin + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! TailFin + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! TailFin + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! TailFin + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! Tower: size of buffers for each call to pack subtype + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Tower, ErrStat2, ErrMsg2, .TRUE. ) ! Tower + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Tower + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Tower + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Tower + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! SubStructure: size of buffers for each call to pack subtype + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%SubStructure, ErrStat2, ErrMsg2, .TRUE. ) ! SubStructure + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! SubStructure + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! SubStructure + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! SubStructure + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! Foundation: size of buffers for each call to pack subtype + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Foundation, ErrStat2, ErrMsg2, .TRUE. ) ! Foundation + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Foundation + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Foundation + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Foundation + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Re_BufSz = Re_BufSz + 1 ! BladeLength + 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 + + IF ( .NOT. ALLOCATED(InData%Blade) ) 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%Blade,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Blade,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%Blade,1), UBOUND(InData%Blade,1) + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Blade(i1), ErrStat2, ErrMsg2, OnlySize ) ! Blade + 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 DO + END IF + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Hub, ErrStat2, ErrMsg2, OnlySize ) ! Hub + 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 + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%RotorFurl, ErrStat2, ErrMsg2, OnlySize ) ! RotorFurl + 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 + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Nacelle, ErrStat2, ErrMsg2, OnlySize ) ! Nacelle + 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 + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%TailFin, ErrStat2, ErrMsg2, OnlySize ) ! TailFin + 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 + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Tower, ErrStat2, ErrMsg2, OnlySize ) ! Tower + 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 + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%SubStructure, ErrStat2, ErrMsg2, OnlySize ) ! SubStructure + 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 + CALL AD14AeroConf_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Foundation, ErrStat2, ErrMsg2, OnlySize ) ! Foundation + 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 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%BladeLength + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14AeroConf_PackInput + + SUBROUTINE AD14AeroConf_UnPackInput( 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(AD14AeroConf_InputType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_UnPackInput' + ! 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 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Blade 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 + IF (ALLOCATED(OutData%Blade)) DEALLOCATE(OutData%Blade) + ALLOCATE(OutData%Blade(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Blade.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%Blade,1), UBOUND(OutData%Blade,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 AD14AeroConf_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Blade(i1), ErrStat2, ErrMsg2 ) ! Blade + 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 DO + END IF + 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 AD14AeroConf_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Hub, ErrStat2, ErrMsg2 ) ! Hub + 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) + 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 AD14AeroConf_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%RotorFurl, ErrStat2, ErrMsg2 ) ! RotorFurl + 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) + 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 AD14AeroConf_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Nacelle, ErrStat2, ErrMsg2 ) ! Nacelle + 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) + 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 AD14AeroConf_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%TailFin, ErrStat2, ErrMsg2 ) ! TailFin + 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) + 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 AD14AeroConf_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Tower, ErrStat2, ErrMsg2 ) ! Tower + 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) + 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 AD14AeroConf_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%SubStructure, ErrStat2, ErrMsg2 ) ! SubStructure + 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) + 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 AD14AeroConf_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Foundation, ErrStat2, ErrMsg2 ) ! Foundation + 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) + OutData%BladeLength = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14AeroConf_UnPackInput + + SUBROUTINE AD14AeroConf_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrMsg ) + TYPE(AD14AeroConf_OutputType), INTENT(IN) :: SrcOutputData + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: DstOutputData + INTEGER(IntKi), INTENT(IN ) :: CtrlCode + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg +! Local + INTEGER(IntKi) :: i,j,k + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_CopyOutput' +! + ErrStat = ErrID_None + ErrMsg = "" + DstOutputData%Dummy = SrcOutputData%Dummy + END SUBROUTINE AD14AeroConf_CopyOutput + + SUBROUTINE AD14AeroConf_DestroyOutput( OutputData, ErrStat, ErrMsg ) + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: OutputData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_DestroyOutput' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE AD14AeroConf_DestroyOutput + + SUBROUTINE AD14AeroConf_PackOutput( 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(AD14AeroConf_OutputType), 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 = 'AD14AeroConf_PackOutput' + ! 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 + Re_BufSz = Re_BufSz + 1 ! Dummy + 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 + + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%Dummy + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14AeroConf_PackOutput + + SUBROUTINE AD14AeroConf_UnPackOutput( 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(AD14AeroConf_OutputType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_UnPackOutput' + ! 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%Dummy = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14AeroConf_UnPackOutput + + + SUBROUTINE AD14AeroConf_Input_ExtrapInterp(u, t, u_out, t_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is given by the size of u +! +! expressions below based on either +! +! f(t) = a +! f(t) = a + b * t, or +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = u1, f(t2) = u2, f(t3) = u3 (as appropriate) +! +!.................................................................................................................................. + + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: u(:) ! Input at t1 > t2 > t3 + REAL(DbKi), INTENT(IN ) :: t(:) ! Times associated with the Inputs + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: t_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_Input_ExtrapInterp' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + if ( size(t) .ne. size(u)) then + CALL SetErrStat(ErrID_Fatal,'size(t) must equal size(u)',ErrStat,ErrMsg,RoutineName) + RETURN + endif + order = SIZE(u) - 1 + IF ( order .eq. 0 ) THEN + CALL AD14AeroConf_CopyInput(u(1), u_out, MESH_UPDATECOPY, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE IF ( order .eq. 1 ) THEN + CALL AD14AeroConf_Input_ExtrapInterp1(u(1), u(2), t, u_out, t_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE IF ( order .eq. 2 ) THEN + CALL AD14AeroConf_Input_ExtrapInterp2(u(1), u(2), u(3), t, u_out, t_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE + CALL SetErrStat(ErrID_Fatal,'size(u) must be less than 4 (order must be less than 3).',ErrStat,ErrMsg,RoutineName) + RETURN + ENDIF + END SUBROUTINE AD14AeroConf_Input_ExtrapInterp + + + SUBROUTINE AD14AeroConf_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is 1. +! +! f(t) = a + b * t, or +! +! where a and b are determined as the solution to +! f(t1) = u1, f(t2) = u2 +! +!.................................................................................................................................. + + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: u1 ! Input at t1 > t2 + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: u2 ! Input at t2 + REAL(DbKi), INTENT(IN ) :: tin(2) ! Times associated with the Inputs + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(2) ! Times associated with the Inputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_Input_ExtrapInterp1' + REAL(DbKi) :: b0 ! temporary for extrapolation/interpolation + REAL(DbKi) :: c0 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: b1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: c1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: b2 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: c2 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF +IF (ALLOCATED(u_out%Blade) .AND. ALLOCATED(u1%Blade)) THEN + DO i01 = LBOUND(u_out%Blade,1),UBOUND(u_out%Blade,1) + ALLOCATE(b1(SIZE(u_out%Blade(i01)%Position,1))) + ALLOCATE(c1(SIZE(u_out%Blade(i01)%Position,1))) + b1 = -(u1%Blade(i01)%Position - u2%Blade(i01)%Position)/t(2) + u_out%Blade(i01)%Position = u1%Blade(i01)%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO + DO i01 = LBOUND(u_out%Blade,1),UBOUND(u_out%Blade,1) + ALLOCATE(b2(SIZE(u_out%Blade(i01)%Orientation,1),SIZE(u_out%Blade(i01)%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Blade(i01)%Orientation,1),SIZE(u_out%Blade(i01)%Orientation,2) )) + b2 = -(u1%Blade(i01)%Orientation - u2%Blade(i01)%Orientation)/t(2) + u_out%Blade(i01)%Orientation = u1%Blade(i01)%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ENDDO + DO i01 = LBOUND(u_out%Blade,1),UBOUND(u_out%Blade,1) + ALLOCATE(b1(SIZE(u_out%Blade(i01)%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Blade(i01)%TranslationVel,1))) + b1 = -(u1%Blade(i01)%TranslationVel - u2%Blade(i01)%TranslationVel)/t(2) + u_out%Blade(i01)%TranslationVel = u1%Blade(i01)%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO + DO i01 = LBOUND(u_out%Blade,1),UBOUND(u_out%Blade,1) + ALLOCATE(b1(SIZE(u_out%Blade(i01)%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Blade(i01)%RotationVel,1))) + b1 = -(u1%Blade(i01)%RotationVel - u2%Blade(i01)%RotationVel)/t(2) + u_out%Blade(i01)%RotationVel = u1%Blade(i01)%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO +END IF ! check if allocated + ALLOCATE(b1(SIZE(u_out%Hub%Position,1))) + ALLOCATE(c1(SIZE(u_out%Hub%Position,1))) + b1 = -(u1%Hub%Position - u2%Hub%Position)/t(2) + u_out%Hub%Position = u1%Hub%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%Hub%Orientation,1),SIZE(u_out%Hub%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Hub%Orientation,1),SIZE(u_out%Hub%Orientation,2) )) + b2 = -(u1%Hub%Orientation - u2%Hub%Orientation)/t(2) + u_out%Hub%Orientation = u1%Hub%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%Hub%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Hub%TranslationVel,1))) + b1 = -(u1%Hub%TranslationVel - u2%Hub%TranslationVel)/t(2) + u_out%Hub%TranslationVel = u1%Hub%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Hub%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Hub%RotationVel,1))) + b1 = -(u1%Hub%RotationVel - u2%Hub%RotationVel)/t(2) + u_out%Hub%RotationVel = u1%Hub%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%RotorFurl%Position,1))) + ALLOCATE(c1(SIZE(u_out%RotorFurl%Position,1))) + b1 = -(u1%RotorFurl%Position - u2%RotorFurl%Position)/t(2) + u_out%RotorFurl%Position = u1%RotorFurl%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%RotorFurl%Orientation,1),SIZE(u_out%RotorFurl%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%RotorFurl%Orientation,1),SIZE(u_out%RotorFurl%Orientation,2) )) + b2 = -(u1%RotorFurl%Orientation - u2%RotorFurl%Orientation)/t(2) + u_out%RotorFurl%Orientation = u1%RotorFurl%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%RotorFurl%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%RotorFurl%TranslationVel,1))) + b1 = -(u1%RotorFurl%TranslationVel - u2%RotorFurl%TranslationVel)/t(2) + u_out%RotorFurl%TranslationVel = u1%RotorFurl%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%RotorFurl%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%RotorFurl%RotationVel,1))) + b1 = -(u1%RotorFurl%RotationVel - u2%RotorFurl%RotationVel)/t(2) + u_out%RotorFurl%RotationVel = u1%RotorFurl%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Nacelle%Position,1))) + ALLOCATE(c1(SIZE(u_out%Nacelle%Position,1))) + b1 = -(u1%Nacelle%Position - u2%Nacelle%Position)/t(2) + u_out%Nacelle%Position = u1%Nacelle%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%Nacelle%Orientation,1),SIZE(u_out%Nacelle%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Nacelle%Orientation,1),SIZE(u_out%Nacelle%Orientation,2) )) + b2 = -(u1%Nacelle%Orientation - u2%Nacelle%Orientation)/t(2) + u_out%Nacelle%Orientation = u1%Nacelle%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%Nacelle%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Nacelle%TranslationVel,1))) + b1 = -(u1%Nacelle%TranslationVel - u2%Nacelle%TranslationVel)/t(2) + u_out%Nacelle%TranslationVel = u1%Nacelle%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Nacelle%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Nacelle%RotationVel,1))) + b1 = -(u1%Nacelle%RotationVel - u2%Nacelle%RotationVel)/t(2) + u_out%Nacelle%RotationVel = u1%Nacelle%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TailFin%Position,1))) + ALLOCATE(c1(SIZE(u_out%TailFin%Position,1))) + b1 = -(u1%TailFin%Position - u2%TailFin%Position)/t(2) + u_out%TailFin%Position = u1%TailFin%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TailFin%Orientation,1),SIZE(u_out%TailFin%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TailFin%Orientation,1),SIZE(u_out%TailFin%Orientation,2) )) + b2 = -(u1%TailFin%Orientation - u2%TailFin%Orientation)/t(2) + u_out%TailFin%Orientation = u1%TailFin%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TailFin%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TailFin%TranslationVel,1))) + b1 = -(u1%TailFin%TranslationVel - u2%TailFin%TranslationVel)/t(2) + u_out%TailFin%TranslationVel = u1%TailFin%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TailFin%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TailFin%RotationVel,1))) + b1 = -(u1%TailFin%RotationVel - u2%TailFin%RotationVel)/t(2) + u_out%TailFin%RotationVel = u1%TailFin%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Tower%Position,1))) + ALLOCATE(c1(SIZE(u_out%Tower%Position,1))) + b1 = -(u1%Tower%Position - u2%Tower%Position)/t(2) + u_out%Tower%Position = u1%Tower%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%Tower%Orientation,1),SIZE(u_out%Tower%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Tower%Orientation,1),SIZE(u_out%Tower%Orientation,2) )) + b2 = -(u1%Tower%Orientation - u2%Tower%Orientation)/t(2) + u_out%Tower%Orientation = u1%Tower%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%Tower%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Tower%TranslationVel,1))) + b1 = -(u1%Tower%TranslationVel - u2%Tower%TranslationVel)/t(2) + u_out%Tower%TranslationVel = u1%Tower%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Tower%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Tower%RotationVel,1))) + b1 = -(u1%Tower%RotationVel - u2%Tower%RotationVel)/t(2) + u_out%Tower%RotationVel = u1%Tower%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%SubStructure%Position,1))) + ALLOCATE(c1(SIZE(u_out%SubStructure%Position,1))) + b1 = -(u1%SubStructure%Position - u2%SubStructure%Position)/t(2) + u_out%SubStructure%Position = u1%SubStructure%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%SubStructure%Orientation,1),SIZE(u_out%SubStructure%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%SubStructure%Orientation,1),SIZE(u_out%SubStructure%Orientation,2) )) + b2 = -(u1%SubStructure%Orientation - u2%SubStructure%Orientation)/t(2) + u_out%SubStructure%Orientation = u1%SubStructure%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%SubStructure%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%SubStructure%TranslationVel,1))) + b1 = -(u1%SubStructure%TranslationVel - u2%SubStructure%TranslationVel)/t(2) + u_out%SubStructure%TranslationVel = u1%SubStructure%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%SubStructure%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%SubStructure%RotationVel,1))) + b1 = -(u1%SubStructure%RotationVel - u2%SubStructure%RotationVel)/t(2) + u_out%SubStructure%RotationVel = u1%SubStructure%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Foundation%Position,1))) + ALLOCATE(c1(SIZE(u_out%Foundation%Position,1))) + b1 = -(u1%Foundation%Position - u2%Foundation%Position)/t(2) + u_out%Foundation%Position = u1%Foundation%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%Foundation%Orientation,1),SIZE(u_out%Foundation%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Foundation%Orientation,1),SIZE(u_out%Foundation%Orientation,2) )) + b2 = -(u1%Foundation%Orientation - u2%Foundation%Orientation)/t(2) + u_out%Foundation%Orientation = u1%Foundation%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%Foundation%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Foundation%TranslationVel,1))) + b1 = -(u1%Foundation%TranslationVel - u2%Foundation%TranslationVel)/t(2) + u_out%Foundation%TranslationVel = u1%Foundation%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Foundation%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Foundation%RotationVel,1))) + b1 = -(u1%Foundation%RotationVel - u2%Foundation%RotationVel)/t(2) + u_out%Foundation%RotationVel = u1%Foundation%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + b0 = -(u1%BladeLength - u2%BladeLength)/t(2) + u_out%BladeLength = u1%BladeLength + b0 * t_out + END SUBROUTINE AD14AeroConf_Input_ExtrapInterp1 + + + SUBROUTINE AD14AeroConf_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is 2. +! +! expressions below based on either +! +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = u1, f(t2) = u2, f(t3) = u3 +! +!.................................................................................................................................. + + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: u1 ! Input at t1 > t2 > t3 + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: u2 ! Input at t2 > t3 + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: u3 ! Input at t3 + REAL(DbKi), INTENT(IN ) :: tin(3) ! Times associated with the Inputs + TYPE(AD14AeroConf_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(3) ! Times associated with the Inputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + REAL(DbKi) :: b0 ! temporary for extrapolation/interpolation + REAL(DbKi) :: c0 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: b1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: c1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: b2 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: c2 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_Input_ExtrapInterp2' + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(2), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(2) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(1), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF +IF (ALLOCATED(u_out%Blade) .AND. ALLOCATED(u1%Blade)) THEN + DO i01 = LBOUND(u_out%Blade,1),UBOUND(u_out%Blade,1) + ALLOCATE(b1(SIZE(u_out%Blade(i01)%Position,1))) + ALLOCATE(c1(SIZE(u_out%Blade(i01)%Position,1))) + b1 = (t(3)**2*(u1%Blade(i01)%Position - u2%Blade(i01)%Position) + t(2)**2*(-u1%Blade(i01)%Position + u3%Blade(i01)%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Blade(i01)%Position + t(3)*u2%Blade(i01)%Position - t(2)*u3%Blade(i01)%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Blade(i01)%Position = u1%Blade(i01)%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO + DO i01 = LBOUND(u_out%Blade,1),UBOUND(u_out%Blade,1) + ALLOCATE(b2(SIZE(u_out%Blade(i01)%Orientation,1),SIZE(u_out%Blade(i01)%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Blade(i01)%Orientation,1),SIZE(u_out%Blade(i01)%Orientation,2) )) + b2 = (t(3)**2*(u1%Blade(i01)%Orientation - u2%Blade(i01)%Orientation) + t(2)**2*(-u1%Blade(i01)%Orientation + u3%Blade(i01)%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%Blade(i01)%Orientation + t(3)*u2%Blade(i01)%Orientation - t(2)*u3%Blade(i01)%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Blade(i01)%Orientation = u1%Blade(i01)%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ENDDO + DO i01 = LBOUND(u_out%Blade,1),UBOUND(u_out%Blade,1) + ALLOCATE(b1(SIZE(u_out%Blade(i01)%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Blade(i01)%TranslationVel,1))) + b1 = (t(3)**2*(u1%Blade(i01)%TranslationVel - u2%Blade(i01)%TranslationVel) + t(2)**2*(-u1%Blade(i01)%TranslationVel + u3%Blade(i01)%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Blade(i01)%TranslationVel + t(3)*u2%Blade(i01)%TranslationVel - t(2)*u3%Blade(i01)%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Blade(i01)%TranslationVel = u1%Blade(i01)%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO + DO i01 = LBOUND(u_out%Blade,1),UBOUND(u_out%Blade,1) + ALLOCATE(b1(SIZE(u_out%Blade(i01)%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Blade(i01)%RotationVel,1))) + b1 = (t(3)**2*(u1%Blade(i01)%RotationVel - u2%Blade(i01)%RotationVel) + t(2)**2*(-u1%Blade(i01)%RotationVel + u3%Blade(i01)%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Blade(i01)%RotationVel + t(3)*u2%Blade(i01)%RotationVel - t(2)*u3%Blade(i01)%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Blade(i01)%RotationVel = u1%Blade(i01)%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO +END IF ! check if allocated + ALLOCATE(b1(SIZE(u_out%Hub%Position,1))) + ALLOCATE(c1(SIZE(u_out%Hub%Position,1))) + b1 = (t(3)**2*(u1%Hub%Position - u2%Hub%Position) + t(2)**2*(-u1%Hub%Position + u3%Hub%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Hub%Position + t(3)*u2%Hub%Position - t(2)*u3%Hub%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Hub%Position = u1%Hub%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%Hub%Orientation,1),SIZE(u_out%Hub%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Hub%Orientation,1),SIZE(u_out%Hub%Orientation,2) )) + b2 = (t(3)**2*(u1%Hub%Orientation - u2%Hub%Orientation) + t(2)**2*(-u1%Hub%Orientation + u3%Hub%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%Hub%Orientation + t(3)*u2%Hub%Orientation - t(2)*u3%Hub%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Hub%Orientation = u1%Hub%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%Hub%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Hub%TranslationVel,1))) + b1 = (t(3)**2*(u1%Hub%TranslationVel - u2%Hub%TranslationVel) + t(2)**2*(-u1%Hub%TranslationVel + u3%Hub%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Hub%TranslationVel + t(3)*u2%Hub%TranslationVel - t(2)*u3%Hub%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Hub%TranslationVel = u1%Hub%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Hub%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Hub%RotationVel,1))) + b1 = (t(3)**2*(u1%Hub%RotationVel - u2%Hub%RotationVel) + t(2)**2*(-u1%Hub%RotationVel + u3%Hub%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Hub%RotationVel + t(3)*u2%Hub%RotationVel - t(2)*u3%Hub%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Hub%RotationVel = u1%Hub%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%RotorFurl%Position,1))) + ALLOCATE(c1(SIZE(u_out%RotorFurl%Position,1))) + b1 = (t(3)**2*(u1%RotorFurl%Position - u2%RotorFurl%Position) + t(2)**2*(-u1%RotorFurl%Position + u3%RotorFurl%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%RotorFurl%Position + t(3)*u2%RotorFurl%Position - t(2)*u3%RotorFurl%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%RotorFurl%Position = u1%RotorFurl%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%RotorFurl%Orientation,1),SIZE(u_out%RotorFurl%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%RotorFurl%Orientation,1),SIZE(u_out%RotorFurl%Orientation,2) )) + b2 = (t(3)**2*(u1%RotorFurl%Orientation - u2%RotorFurl%Orientation) + t(2)**2*(-u1%RotorFurl%Orientation + u3%RotorFurl%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%RotorFurl%Orientation + t(3)*u2%RotorFurl%Orientation - t(2)*u3%RotorFurl%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%RotorFurl%Orientation = u1%RotorFurl%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%RotorFurl%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%RotorFurl%TranslationVel,1))) + b1 = (t(3)**2*(u1%RotorFurl%TranslationVel - u2%RotorFurl%TranslationVel) + t(2)**2*(-u1%RotorFurl%TranslationVel + u3%RotorFurl%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%RotorFurl%TranslationVel + t(3)*u2%RotorFurl%TranslationVel - t(2)*u3%RotorFurl%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%RotorFurl%TranslationVel = u1%RotorFurl%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%RotorFurl%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%RotorFurl%RotationVel,1))) + b1 = (t(3)**2*(u1%RotorFurl%RotationVel - u2%RotorFurl%RotationVel) + t(2)**2*(-u1%RotorFurl%RotationVel + u3%RotorFurl%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%RotorFurl%RotationVel + t(3)*u2%RotorFurl%RotationVel - t(2)*u3%RotorFurl%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%RotorFurl%RotationVel = u1%RotorFurl%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Nacelle%Position,1))) + ALLOCATE(c1(SIZE(u_out%Nacelle%Position,1))) + b1 = (t(3)**2*(u1%Nacelle%Position - u2%Nacelle%Position) + t(2)**2*(-u1%Nacelle%Position + u3%Nacelle%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Nacelle%Position + t(3)*u2%Nacelle%Position - t(2)*u3%Nacelle%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Nacelle%Position = u1%Nacelle%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%Nacelle%Orientation,1),SIZE(u_out%Nacelle%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Nacelle%Orientation,1),SIZE(u_out%Nacelle%Orientation,2) )) + b2 = (t(3)**2*(u1%Nacelle%Orientation - u2%Nacelle%Orientation) + t(2)**2*(-u1%Nacelle%Orientation + u3%Nacelle%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%Nacelle%Orientation + t(3)*u2%Nacelle%Orientation - t(2)*u3%Nacelle%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Nacelle%Orientation = u1%Nacelle%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%Nacelle%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Nacelle%TranslationVel,1))) + b1 = (t(3)**2*(u1%Nacelle%TranslationVel - u2%Nacelle%TranslationVel) + t(2)**2*(-u1%Nacelle%TranslationVel + u3%Nacelle%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Nacelle%TranslationVel + t(3)*u2%Nacelle%TranslationVel - t(2)*u3%Nacelle%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Nacelle%TranslationVel = u1%Nacelle%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Nacelle%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Nacelle%RotationVel,1))) + b1 = (t(3)**2*(u1%Nacelle%RotationVel - u2%Nacelle%RotationVel) + t(2)**2*(-u1%Nacelle%RotationVel + u3%Nacelle%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Nacelle%RotationVel + t(3)*u2%Nacelle%RotationVel - t(2)*u3%Nacelle%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Nacelle%RotationVel = u1%Nacelle%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TailFin%Position,1))) + ALLOCATE(c1(SIZE(u_out%TailFin%Position,1))) + b1 = (t(3)**2*(u1%TailFin%Position - u2%TailFin%Position) + t(2)**2*(-u1%TailFin%Position + u3%TailFin%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TailFin%Position + t(3)*u2%TailFin%Position - t(2)*u3%TailFin%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TailFin%Position = u1%TailFin%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TailFin%Orientation,1),SIZE(u_out%TailFin%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TailFin%Orientation,1),SIZE(u_out%TailFin%Orientation,2) )) + b2 = (t(3)**2*(u1%TailFin%Orientation - u2%TailFin%Orientation) + t(2)**2*(-u1%TailFin%Orientation + u3%TailFin%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%TailFin%Orientation + t(3)*u2%TailFin%Orientation - t(2)*u3%TailFin%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TailFin%Orientation = u1%TailFin%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TailFin%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TailFin%TranslationVel,1))) + b1 = (t(3)**2*(u1%TailFin%TranslationVel - u2%TailFin%TranslationVel) + t(2)**2*(-u1%TailFin%TranslationVel + u3%TailFin%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TailFin%TranslationVel + t(3)*u2%TailFin%TranslationVel - t(2)*u3%TailFin%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TailFin%TranslationVel = u1%TailFin%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TailFin%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TailFin%RotationVel,1))) + b1 = (t(3)**2*(u1%TailFin%RotationVel - u2%TailFin%RotationVel) + t(2)**2*(-u1%TailFin%RotationVel + u3%TailFin%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TailFin%RotationVel + t(3)*u2%TailFin%RotationVel - t(2)*u3%TailFin%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TailFin%RotationVel = u1%TailFin%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Tower%Position,1))) + ALLOCATE(c1(SIZE(u_out%Tower%Position,1))) + b1 = (t(3)**2*(u1%Tower%Position - u2%Tower%Position) + t(2)**2*(-u1%Tower%Position + u3%Tower%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Tower%Position + t(3)*u2%Tower%Position - t(2)*u3%Tower%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Tower%Position = u1%Tower%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%Tower%Orientation,1),SIZE(u_out%Tower%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Tower%Orientation,1),SIZE(u_out%Tower%Orientation,2) )) + b2 = (t(3)**2*(u1%Tower%Orientation - u2%Tower%Orientation) + t(2)**2*(-u1%Tower%Orientation + u3%Tower%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%Tower%Orientation + t(3)*u2%Tower%Orientation - t(2)*u3%Tower%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Tower%Orientation = u1%Tower%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%Tower%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Tower%TranslationVel,1))) + b1 = (t(3)**2*(u1%Tower%TranslationVel - u2%Tower%TranslationVel) + t(2)**2*(-u1%Tower%TranslationVel + u3%Tower%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Tower%TranslationVel + t(3)*u2%Tower%TranslationVel - t(2)*u3%Tower%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Tower%TranslationVel = u1%Tower%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Tower%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Tower%RotationVel,1))) + b1 = (t(3)**2*(u1%Tower%RotationVel - u2%Tower%RotationVel) + t(2)**2*(-u1%Tower%RotationVel + u3%Tower%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Tower%RotationVel + t(3)*u2%Tower%RotationVel - t(2)*u3%Tower%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Tower%RotationVel = u1%Tower%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%SubStructure%Position,1))) + ALLOCATE(c1(SIZE(u_out%SubStructure%Position,1))) + b1 = (t(3)**2*(u1%SubStructure%Position - u2%SubStructure%Position) + t(2)**2*(-u1%SubStructure%Position + u3%SubStructure%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%SubStructure%Position + t(3)*u2%SubStructure%Position - t(2)*u3%SubStructure%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%SubStructure%Position = u1%SubStructure%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%SubStructure%Orientation,1),SIZE(u_out%SubStructure%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%SubStructure%Orientation,1),SIZE(u_out%SubStructure%Orientation,2) )) + b2 = (t(3)**2*(u1%SubStructure%Orientation - u2%SubStructure%Orientation) + t(2)**2*(-u1%SubStructure%Orientation + u3%SubStructure%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%SubStructure%Orientation + t(3)*u2%SubStructure%Orientation - t(2)*u3%SubStructure%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%SubStructure%Orientation = u1%SubStructure%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%SubStructure%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%SubStructure%TranslationVel,1))) + b1 = (t(3)**2*(u1%SubStructure%TranslationVel - u2%SubStructure%TranslationVel) + t(2)**2*(-u1%SubStructure%TranslationVel + u3%SubStructure%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%SubStructure%TranslationVel + t(3)*u2%SubStructure%TranslationVel - t(2)*u3%SubStructure%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%SubStructure%TranslationVel = u1%SubStructure%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%SubStructure%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%SubStructure%RotationVel,1))) + b1 = (t(3)**2*(u1%SubStructure%RotationVel - u2%SubStructure%RotationVel) + t(2)**2*(-u1%SubStructure%RotationVel + u3%SubStructure%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%SubStructure%RotationVel + t(3)*u2%SubStructure%RotationVel - t(2)*u3%SubStructure%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%SubStructure%RotationVel = u1%SubStructure%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Foundation%Position,1))) + ALLOCATE(c1(SIZE(u_out%Foundation%Position,1))) + b1 = (t(3)**2*(u1%Foundation%Position - u2%Foundation%Position) + t(2)**2*(-u1%Foundation%Position + u3%Foundation%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Foundation%Position + t(3)*u2%Foundation%Position - t(2)*u3%Foundation%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Foundation%Position = u1%Foundation%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%Foundation%Orientation,1),SIZE(u_out%Foundation%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%Foundation%Orientation,1),SIZE(u_out%Foundation%Orientation,2) )) + b2 = (t(3)**2*(u1%Foundation%Orientation - u2%Foundation%Orientation) + t(2)**2*(-u1%Foundation%Orientation + u3%Foundation%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%Foundation%Orientation + t(3)*u2%Foundation%Orientation - t(2)*u3%Foundation%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Foundation%Orientation = u1%Foundation%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%Foundation%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%Foundation%TranslationVel,1))) + b1 = (t(3)**2*(u1%Foundation%TranslationVel - u2%Foundation%TranslationVel) + t(2)**2*(-u1%Foundation%TranslationVel + u3%Foundation%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Foundation%TranslationVel + t(3)*u2%Foundation%TranslationVel - t(2)*u3%Foundation%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Foundation%TranslationVel = u1%Foundation%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%Foundation%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%Foundation%RotationVel,1))) + b1 = (t(3)**2*(u1%Foundation%RotationVel - u2%Foundation%RotationVel) + t(2)**2*(-u1%Foundation%RotationVel + u3%Foundation%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%Foundation%RotationVel + t(3)*u2%Foundation%RotationVel - t(2)*u3%Foundation%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%Foundation%RotationVel = u1%Foundation%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + b0 = (t(3)**2*(u1%BladeLength - u2%BladeLength) + t(2)**2*(-u1%BladeLength + u3%BladeLength))/(t(2)*t(3)*(t(2) - t(3))) + c0 = ( (t(2)-t(3))*u1%BladeLength + t(3)*u2%BladeLength - t(2)*u3%BladeLength ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%BladeLength = u1%BladeLength + b0 * t_out + c0 * t_out**2 + END SUBROUTINE AD14AeroConf_Input_ExtrapInterp2 + + + SUBROUTINE AD14AeroConf_Output_ExtrapInterp(y, t, y_out, t_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is given by the size of y +! +! expressions below based on either +! +! f(t) = a +! f(t) = a + b * t, or +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = y1, f(t2) = y2, f(t3) = y3 (as appropriate) +! +!.................................................................................................................................. + + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: y(:) ! Output at t1 > t2 > t3 + REAL(DbKi), INTENT(IN ) :: t(:) ! Times associated with the Outputs + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: t_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_Output_ExtrapInterp' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + if ( size(t) .ne. size(y)) then + CALL SetErrStat(ErrID_Fatal,'size(t) must equal size(y)',ErrStat,ErrMsg,RoutineName) + RETURN + endif + order = SIZE(y) - 1 + IF ( order .eq. 0 ) THEN + CALL AD14AeroConf_CopyOutput(y(1), y_out, MESH_UPDATECOPY, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE IF ( order .eq. 1 ) THEN + CALL AD14AeroConf_Output_ExtrapInterp1(y(1), y(2), t, y_out, t_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE IF ( order .eq. 2 ) THEN + CALL AD14AeroConf_Output_ExtrapInterp2(y(1), y(2), y(3), t, y_out, t_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE + CALL SetErrStat(ErrID_Fatal,'size(y) must be less than 4 (order must be less than 3).',ErrStat,ErrMsg,RoutineName) + RETURN + ENDIF + END SUBROUTINE AD14AeroConf_Output_ExtrapInterp + + + SUBROUTINE AD14AeroConf_Output_ExtrapInterp1(y1, y2, tin, y_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is 1. +! +! f(t) = a + b * t, or +! +! where a and b are determined as the solution to +! f(t1) = y1, f(t2) = y2 +! +!.................................................................................................................................. + + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: y1 ! Output at t1 > t2 + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: y2 ! Output at t2 + REAL(DbKi), INTENT(IN ) :: tin(2) ! Times associated with the Outputs + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(2) ! Times associated with the Outputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_Output_ExtrapInterp1' + REAL(DbKi) :: b0 ! temporary for extrapolation/interpolation + REAL(DbKi) :: c0 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + b0 = -(y1%Dummy - y2%Dummy)/t(2) + y_out%Dummy = y1%Dummy + b0 * t_out + END SUBROUTINE AD14AeroConf_Output_ExtrapInterp1 + + + SUBROUTINE AD14AeroConf_Output_ExtrapInterp2(y1, y2, y3, tin, y_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is 2. +! +! expressions below based on either +! +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = y1, f(t2) = y2, f(t3) = y3 +! +!.................................................................................................................................. + + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: y1 ! Output at t1 > t2 > t3 + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: y2 ! Output at t2 > t3 + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: y3 ! Output at t3 + REAL(DbKi), INTENT(IN ) :: tin(3) ! Times associated with the Outputs + TYPE(AD14AeroConf_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(3) ! Times associated with the Outputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + REAL(DbKi) :: b0 ! temporary for extrapolation/interpolation + REAL(DbKi) :: c0 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'AD14AeroConf_Output_ExtrapInterp2' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(2), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(2) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(1), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + b0 = (t(3)**2*(y1%Dummy - y2%Dummy) + t(2)**2*(-y1%Dummy + y3%Dummy))/(t(2)*t(3)*(t(2) - t(3))) + c0 = ( (t(2)-t(3))*y1%Dummy + t(3)*y2%Dummy - t(2)*y3%Dummy ) / (t(2)*t(3)*(t(2) - t(3))) + y_out%Dummy = y1%Dummy + b0 * t_out + c0 * t_out**2 + END SUBROUTINE AD14AeroConf_Output_ExtrapInterp2 + +END MODULE AD14AeroConf_Types +!ENDOFREGISTRYGENERATEDFILE diff --git a/modules/aerodyn14/src/AeroDyn14.f90 b/modules/aerodyn14/src/AeroDyn14.f90 index 321d114d76..f30aa4f5bd 100644 --- a/modules/aerodyn14/src/AeroDyn14.f90 +++ b/modules/aerodyn14/src/AeroDyn14.f90 @@ -23,6 +23,7 @@ MODULE AeroDyn14 USE AeroDyn14_Types USE AeroSubs + USE FVW USE NWTC_Library @@ -140,6 +141,7 @@ SUBROUTINE AD14_Init( InitInp, u, p, x, xd, z, O, y, m, Interval, InitOut, ErrSt CALL SetErrStat( ErrStatLcl,ErrMessLcl,ErrStat,ErrMess,RoutineName) IF (ErrStat >= AbortErrLev ) RETURN + p%UseFVW = InitInp%UseFVW ! allocate variables for aerodyn forces p%LinearizeFlag = .FALSE. @@ -165,6 +167,11 @@ SUBROUTINE AD14_Init( InitInp, u, p, x, xd, z, O, y, m, Interval, InitOut, ErrSt CALL AllocAry(m%Element%Alpha, p%Element%NELM, p%NumBl,'m%Element%Alpha',ErrStatLcl,ErrMessLcl ) CALL SetErrStat( ErrStatLcl,ErrMessLcl,ErrStat,ErrMess,RoutineName) END IF + + IF (.NOT. ALLOCATED(m%Element%PitNow) ) THEN + CALL AllocAry(m%Element%PitNow, p%Element%NELM, p%NumBl,'m%Element%PitNow',ErrStatLcl,ErrMessLcl ) + CALL SetErrStat( ErrStatLcl,ErrMessLcl,ErrStat,ErrMess,RoutineName) + END IF IF (ErrStat >= AbortErrLev ) RETURN @@ -456,7 +463,7 @@ SUBROUTINE AD14_Init( InitInp, u, p, x, xd, z, O, y, m, Interval, InitOut, ErrSt ! u%TurbineComponents !.......... - CALL AD14_CopyAeroConfig( InitInp%TurbineComponents, u%TurbineComponents, MESH_NEWCOPY, ErrStatLcl, ErrMessLcl ) + CALL AD14AeroConf_CopyInput( InitInp%TurbineComponents, u%TurbineComponents, MESH_NEWCOPY, ErrStatLcl, ErrMessLcl ) CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) IF (ErrStat >= AbortErrLev) RETURN @@ -582,6 +589,77 @@ SUBROUTINE AD14_Init( InitInp, u, p, x, xd, z, O, y, m, Interval, InitOut, ErrSt CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) IF (ErrStat >= AbortErrLev) RETURN ENDDO + + + !------------------------------------------------------------------------------------------------- + ! Initialize FVW module if it is used + !------------------------------------------------------------------------------------------------- + if (p%UseFVW ) then + + ! Copy some things to the InitInp. When FVW is incorporated into a different module, there may + ! be some additional logic necessary to put it into the correct form for FVW to use (AD15 stores + ! things differently) + InitInp%FVW%FVWFileName = InitInp%FVWFileName + InitInp%FVW%NumBl = p%NumBl + + ! --- TODO TODO TODO ANDY + ! Change this so that it would match AD 15 mesh + ! NOTE: This mesh does not include the azimuthal differences between blades! + ! It's just the spanwise location. + ! Also, it is off compared to the initial position of the blade + ! Also, it's centered on the hub, but that's fine for now + IF (.NOT. ALLOCATED( InitInp%FVW%Chord)) ALLOCATE ( InitInp%FVW%Chord( p%Element%NElm )) + IF (.NOT. ALLOCATED( InitInp%FVW%RElm )) ALLOCATE ( InitInp%FVW%RElm( p%Element%NElm )) + InitInp%FVW%RElm = p%Element%RElm + InitInp%FVW%Chord = p%Blade%C + ALLOCATE( InitInp%FVW%WingsMesh(p%NumBl), STAT = ErrStatLcl ) + IF (ErrStatLcl /= 0) THEN + CALL SetErrStat ( ErrID_Fatal, 'Could not allocate InitInp%FVW%WingsMesh (meshes)', ErrStat,ErrMess,RoutineName ) + RETURN + END IF + DO IB = 1, p%NumBl + CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & + ,DestMesh = InitInp%FVW%WingsMesh(IB) & + ,CtrlCode = MESH_COUSIN & + ,Orientation = .TRUE. & + ,TranslationVel = .TRUE. & + ,RotationVel = .TRUE. & + ,ErrStat = ErrStatLcl & + ,ErrMess = ErrMessLcl ) + CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) + IF (ErrStat >= AbortErrLev) RETURN + ENDDO + ! ---- END TODO + + call FVW_Init( InitInp%FVW, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, Interval, InitOut%FVW, ErrStatLcl, ErrMessLcl ) + CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) + + ! If anything is passed back in InitOut%FVW, deal with it here... + + + + ! TODO ANDY + !FIXME This really probably should be done inside of FVW_Init instead of here. + ! Not entirely sure how to pass the u%InputMarkers in though. + ALLOCATE( u%FVW%WingsMesh(p%NumBl), STAT = ErrStatLcl ) + IF (ErrStatLcl /= 0) THEN + CALL SetErrStat ( ErrID_Fatal, 'Could not allocate u%FVW%InputMarkers (meshes)', ErrStat,ErrMess,RoutineName ) + RETURN + END IF + DO IB = 1, p%NumBl + CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & + ,DestMesh = u%FVW%WingsMesh(IB) & + ,CtrlCode = MESH_COUSIN & + ,Orientation = .TRUE. & + ,TranslationVel = .TRUE. & + ,RotationVel = .TRUE. & + ,ErrStat = ErrStatLcl & + ,ErrMess = ErrMessLcl ) + CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) + IF (ErrStat >= AbortErrLev) RETURN + ENDDO + endif + !.......... @@ -649,6 +727,11 @@ SUBROUTINE AD14_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMess ) CALL DWM_End( m%DWM_Inputs, p%DWM, x%DWM, xd%DWM, z%DWM, OtherState%DWM, m%DWM_Outputs, m%DWM, ErrStat, ErrMess ) END IF ! UseDWM + !-------------------------- + + + IF (p%UseFVW ) CALL FVW_End( u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, y%FVW, m%FVW, ErrStat, ErrMess ) + !-------------------------- @@ -712,6 +795,8 @@ SUBROUTINE AD14_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrSt TYPE(AD14_ContinuousStateType) :: dxdt ! Continuous state derivatives at Time TYPE(AD14_ConstraintStateType) :: z_Residual ! Residual of the constraint state equations (Z) + type(FVW_InputType) :: u_FVW(1) !< FVW inputs + REAL(DbKi) :: utimes_FVW(1) !< Times associated with u(:), in seconds ! INTEGER(IntKi) :: ErrStat2 ! Error status of the operation (occurs after initial error) ! CHARACTER(ErrMsgLen) :: ErrMess2 ! Error message if ErrStat2 /= ErrID_None @@ -725,7 +810,42 @@ SUBROUTINE AD14_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrSt ! AeroDyn v14 DOES actually have states, but they are updated in CalcOutput because no one ever took the time to ! identify which variables are states. + if (p%UseFVW) then + !if (abs(t-utimes(2))>1e-6 ) then + ! print*,'Problem in AD14 update state, need to adapt which u we provide to FVW' + ! STOP + !endif + ! Setting u(1)%FVW + call AD14_to_FVW_u(u(1),p,u(1)%FVW,ErrStat,ErrMess) + u_FVW(1) = u(1)%FVW + utimes_FVW(1) = utimes(1) + CALL FVW_UpdateStates( t, n, u_FVW, utimes_FVW, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat, ErrMess ) + endif + END SUBROUTINE AD14_UpdateStates +!---------------------------------------------------------------------------------------------------------------------------------- + +!> Wrapper to set inputs needed by FVW from AeroDyn +SUBROUTINE AD14_to_FVW_u(u,p,u_FVW,ErrStat,ErrMess) + TYPE (AD14_InputType ),INTENT(IN ):: u ! Inputs at Time ! KS changed from IN to INOUT + TYPE (AD14_ParameterType),INTENT(IN ):: p ! Parameters + TYPE (FVW_InputType ),INTENT(INOUT):: u_FVW ! Inputs at Time ! KS changed from IN to INOUT + INTEGER (IntKi ),INTENT(OUT ):: ErrStat ! Error status of the operation + CHARACTER(* ),INTENT(OUT ):: ErrMess ! Error message if ErrStat / = ErrID_None + INTEGER(IntKi) :: iB ! Index for blades + + !CALL AD14AeroConf_CopyInput( u%TurbineComponents, u_FVW%FVWTurbineComponents, MESH_NEWCOPY, ErrStat, ErrMess ) + IF (ErrStat >= AbortErrLev) RETURN + ! NOTE: this isn't really being used as a full mesh, so we only set a few things. + ! also, if we do a direct copy, we end up with fatal errors at exit since the + ! sibling/cousin status will be incorrect + DO iB = 1,p%NumBl + u_FVW%WingsMesh(iB)%Position = u%InputMarkers(iB)%Position + u_FVW%WingsMesh(iB)%Orientation = u%InputMarkers(iB)%Orientation + u_FVW%WingsMesh(iB)%TranslationVel = u%InputMarkers(iB)%TranslationVel + ENDDO +ENDSUBROUTINE AD14_to_FVW_u + !---------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) ! Routine for computing outputs, used in both loose and tight coupling. @@ -734,13 +854,15 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) USE AeroGenSubs, ONLY: ElemOut USE DWM_Types USE DWM +!FIXME: remove InflowWind from here... + USE InflowWind !! KS REAL(DbKi), INTENT(IN ) :: Time ! Current simulation time in seconds - TYPE(AD14_InputType), INTENT(IN ) :: u ! Inputs at Time + TYPE(AD14_InputType), INTENT(INOUT) :: u ! Inputs at Time TYPE(AD14_ParameterType), INTENT(IN ) :: p ! Parameters TYPE(AD14_ContinuousStateType), INTENT(IN ) :: x ! Continuous states at Time TYPE(AD14_DiscreteStateType), INTENT(IN ) :: xd ! Discrete states at Time - TYPE(AD14_ConstraintStateType), INTENT(IN ) :: z ! Constraint states at Time + TYPE(AD14_ConstraintStateType), INTENT(INOUT) :: z ! Constraint states at Time TYPE(AD14_OtherStateType), INTENT(IN ) :: O ! Other states at Time TYPE(AD14_OutputType), INTENT(INOUT) :: y ! Outputs computed at Time (Input only so that mesh con- ! nectivity information does not have to be recalculated) @@ -752,15 +874,22 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) ! Local variables REAL(DbKi), PARAMETER :: OnePlusEpsilon = 1 + EPSILON(Time) - REAL(ReKi) :: VNElement REAL(ReKi) :: VelNormalToRotor2 + REAL(ReKi) :: VTWind REAL(ReKi) :: VNWind + REAL(ReKi) :: VNElement + REAL(ReKi) :: VTElement + REAL(ReKi) :: VN_ind + REAL(ReKi) :: VT_ind + REAL(ReKi) :: VN + REAL(ReKi) :: VT REAL(ReKi) :: VTTotal REAL(ReKi) :: DFN REAL(ReKi) :: DFT REAL(ReKi) :: PMA REAL(ReKi) :: SPitch ! sine of PitNow REAL(ReKi) :: CPitch ! cosine of PitNow + REAL(ReKi) :: Phi ! Local value of Phi REAL(ReKi) :: AvgVelNacelleRotorFurlYaw REAL(ReKi) :: AvgVelTowerBaseNacelleYaw @@ -772,6 +901,8 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) REAL(ReKi) :: rTowerBaseHub (2) REAL(ReKi) :: tmpVector (3) + REAL(ReKi) :: norm_Vector (3) ! Unit vector normal to chord + REAL(ReKi) :: tang_Vector (3) ! Unit vector tangent to chord REAL(ReKi) :: VelocityVec (3) INTEGER :: ErrStatLcL ! Error status returned by called routines. @@ -782,7 +913,10 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) INTEGER :: I CHARACTER(ErrMsgLen) :: ErrMessLcl ! Error message returned by called routines. + LOGICAL :: WakeCalc ! Are we doing wake calculations this loop? + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_AeroSubs' !KS Not sure why I added this + REAL(ReKi) :: Vind_FVW(3) ! Initialize ErrStat ErrStat = ErrID_None @@ -913,7 +1047,41 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) ! end of NewTime routine !................................................................................................. + ! Set blade element pitches + DO IBlade = 1,p%NumBl + DO IElement = 1,p%Element%NElm + ! calculate element pitch + m%Element%PitNow(IElement,IBlade) = -1.*ATAN2( -1.*DOT_PRODUCT( u%TurbineComponents%Blade(IBlade)%Orientation(1,:), & + u%InputMarkers(IBlade)%Orientation(2,:,IElement) ) , & + DOT_PRODUCT( u%TurbineComponents%Blade(IBlade)%Orientation(1,:), & + u%InputMarkers(IBlade)%Orientation(1,:,IElement) ) ) + ENDDO + ENDDO + + + + + WakeCalc = p%UseFVW ! WakeCalc is used to easily switch the Freewake on and off in this routine + + ! --- Copy of Rotor Mesh to FVW + IF (WakeCalc) THEN + ! Setting u%FVW + call AD14_to_FVW_u(u,p,u%FVW,ErrStat,ErrMess) + IF (ErrStat >= AbortErrLev) THEN + CALL CleanUp() + RETURN + END IF + ! -- Calc Output + CALL FVW_CalcOutput( Time, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, ErrStat, ErrMess ) + IF (ErrStat >= AbortErrLev) THEN + CALL CleanUp() + RETURN + END IF + endif + + Node = 0 + ! --- Loop on blades DO IBlade = 1,p%NumBl ! calculate the azimuth angle ( we add pi because AeroDyn defines 0 as pointing downward) @@ -924,24 +1092,13 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) u%TurbineComponents%RotorFurl%Orientation(2,:) ), & DOT_PRODUCT( u%TurbineComponents%Hub%Orientation(3,:), & u%TurbineComponents%RotorFurl%Orientation(3,:) ) ) + pi + (IBlade - 1)*p%TwoPiNB - - - + ! --- Loop on elements DO IElement = 1,p%Element%NElm - ! calculate element pitch - - m%Element%PitNow = -1.*ATAN2( -1.*DOT_PRODUCT( u%TurbineComponents%Blade(IBlade)%Orientation(1,:), & - u%InputMarkers(IBlade)%Orientation(2,:,IElement) ) , & - DOT_PRODUCT( u%TurbineComponents%Blade(IBlade)%Orientation(1,:), & - u%InputMarkers(IBlade)%Orientation(1,:,IElement) ) ) - - SPitch = SIN( m%Element%PitNow ) - CPitch = COS( m%Element%PitNow ) - + SPitch = SIN( m%Element%PitNow(IElement,IBlade) ) + CPitch = COS( m%Element%PitNow(IElement,IBlade) ) ! calculate distance between hub and element - tmpVector = u%InputMarkers(IBlade)%Position(:,IElement) - u%TurbineComponents%Hub%Position(:) rLocal = SQRT( DOT_PRODUCT( tmpVector, u%TurbineComponents%Hub%Orientation(2,:) )**2 & + DOT_PRODUCT( tmpVector, u%TurbineComponents%Hub%Orientation(3,:) )**2 ) @@ -1028,35 +1185,72 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) !----------------------------------------------------------------------------------------------------------------------- - - - + ! NOTE: VelocityVec is freestream with disturbances from Tower Shadow and Wakes (DWM) VelNormalToRotor2 = ( VelocityVec(3) * m%Rotor%STilt + (VelocityVec(1) * m%Rotor%CYaw & - VelocityVec(2) * m%Rotor%SYaw) * m%Rotor%CTilt )**2 !------------------------------------------------------------------------------------------- - ! reproduce GetVNVT routine: + ! Normal and tangential velocities from wind and relative blade motion !------------------------------------------------------------------------------------------- - tmpVector = -1.*SPitch*u%InputMarkers(IBlade)%Orientation(1,:,IElement) & - + CPitch*u%InputMarkers(IBlade)%Orientation(2,:,IElement) - VTTotal = DOT_PRODUCT( tmpVector, VelocityVec - u%InputMarkers(IBlade)%TranslationVel(:,IElement) ) + tang_Vector = - SPitch*u%InputMarkers(IBlade)%Orientation(1,:,IElement) & + & + CPitch*u%InputMarkers(IBlade)%Orientation(2,:,IElement) + norm_Vector = CPitch*u%InputMarkers(IBlade)%Orientation(1,:,IElement) & + & + SPitch*u%InputMarkers(IBlade)%Orientation(2,:,IElement) - tmpVector = CPitch*u%InputMarkers(IBlade)%Orientation(1,:,IElement) & - + SPitch*u%InputMarkers(IBlade)%Orientation(2,:,IElement) - VNWind = DOT_PRODUCT( tmpVector, VelocityVec ) - VNElement = -1.*DOT_PRODUCT( tmpVector, u%InputMarkers(IBlade)%TranslationVel(:,IElement ) ) + VTTotal = DOT_PRODUCT( tang_Vector, VelocityVec - u%InputMarkers(IBlade)%TranslationVel(:,IElement) ) + VTElement = - DOT_PRODUCT( tang_Vector, u%InputMarkers(IBlade)%TranslationVel(:,IElement) ) + VNElement = - DOT_PRODUCT( norm_Vector, u%InputMarkers(IBlade)%TranslationVel(:,IElement ) ) + + VTWind = DOT_PRODUCT( tang_Vector, VelocityVec) + VNWind = DOT_PRODUCT( norm_Vector, VelocityVec) !------------------------------------------------------------------------------------------- ! Get blade element forces and induced velocity !------------------------------------------------------------------------------------------- - CALL ELEMFRC( p, m, ErrStatLcl, ErrMessLcl, & + ! --------------------------------------------------------------------------------} + ! --- Setting Element% values: W2, Alpha, A, AP + ! --------------------------------------------------------------------------------{ + IF ( .NOT. WakeCalc ) THEN + ! --- BEM + CALL ELEM_INDUCTIONS( p, m, ErrStatLcl, ErrMessLcl, & AzimuthAngle, rLocal, IElement, IBlade, VelNormalToRotor2, VTTotal, VNWind, & - VNElement, DFN, DFT, PMA, m%NoLoadsCalculated ) + VNElement, m%NoLoadsCalculated) + ! Normal and tangential induced velocities + VN_ind = - VNWind * m%Element%A (IElement, IBLADE) + VT_ind = VTTotal * m%Element%AP(IElement, IBLADE) + else + ! --- FVW - Vortex code + Vind_FVW = y%FVW%Vind(:, IElement, IBlade) + VT_ind = DOT_PRODUCT( tang_Vector, Vind_FVW) + VN_ind = DOT_PRODUCT( norm_Vector, Vind_FVW) + ! Normal and tangential induction factors + m%Element%A (IElement,IBLADE) = - VN_ind / VNWind + m%Element%AP(IElement,IBLADE) = VT_ind / VTTotal + ! Copy over any outputs (y%FVW%) or miscvars (m%FVW%) needed by AD14 and anything else here + ENDIF + ! Cumulative (integrated) induction over the blades + m%InducedVel%SumInfl = m%InducedVel%SumInfl - VN_IND * RLOCAL * p%Blade%DR(IElement) + + ! --- Total flow velocity at the blade element + VN = VN_IND + VNWind + VNElement ! Normal velocity : Indution + Wind + Rel. blade vel + VT = VT_IND + VTTotal ! Tangential velocity : Indution + (Wind + Rel. blade vel) + + PHI = ATAN2( VN, VT) ! Flow angle [rad] + m%Element%ALPHA(IElement,IBlade) = PHI - m%Element%PitNow(IElement,IBlade) ! Angle of attack [rad] + CALL MPI2PI ( m%Element%ALPHA(IElement,IBlade) ) + m%Element%W2(IElement,IBlade) = VN * VN + VT * VT ! Relative velocity norm + + CALL ELEMFRC2( p, m, ErrStatLcl, ErrMessLcl, IElement, IBlade, & + DFN, DFT, PMA, m%NoLoadsCalculated, phi ) CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'AD14_CalcOutput' ) IF (ErrStat >= AbortErrLev) THEN CALL CleanUp() RETURN END IF + + IF ( p%UseFVW ) THEN + VelocityVec = VelocityVec+Vind_FVW ! TODO this might not be what's really intended for + END IF !------------------------------------------------------------------------------------------- ! Set up dynamic inflow parameters @@ -1113,7 +1307,6 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) m%NoLoadsCalculated = .FALSE. - DO IBlade=1,p%NumBl DO IElement=1,p%Element%Nelm y%OutputLoads(IBlade)%Force(:,IElement) = m%StoredForces(:,IElement,IBlade) diff --git a/modules/aerodyn14/src/AeroDyn14_Types.f90 b/modules/aerodyn14/src/AeroDyn14_Types.f90 index 315d478986..b097f4bbd1 100644 --- a/modules/aerodyn14/src/AeroDyn14_Types.f90 +++ b/modules/aerodyn14/src/AeroDyn14_Types.f90 @@ -40,51 +40,10 @@ MODULE AeroDyn14_Types USE Lidar_Types USE InflowWind_Types USE DWM_Types +USE AD14AeroConf_Types +USE FVW_Types USE NWTC_Library IMPLICIT NONE -! ========= Marker ======= - TYPE, PUBLIC :: Marker - REAL(ReKi) , DIMENSION(1:3) :: Position - REAL(ReKi) , DIMENSION(1:3,1:3) :: Orientation - REAL(ReKi) , DIMENSION(1:3) :: TranslationVel - REAL(ReKi) , DIMENSION(1:3) :: RotationVel - END TYPE Marker -! ======================= -! ========= AeroConfig ======= - TYPE, PUBLIC :: AeroConfig - TYPE(Marker) , DIMENSION(:), ALLOCATABLE :: Blade - TYPE(Marker) :: Hub - TYPE(Marker) :: RotorFurl - TYPE(Marker) :: Nacelle - TYPE(Marker) :: TailFin - TYPE(Marker) :: Tower - TYPE(Marker) :: SubStructure - TYPE(Marker) :: Foundation - REAL(ReKi) :: BladeLength - END TYPE AeroConfig -! ======================= -! ========= AirFoil ======= - TYPE, PUBLIC :: AirFoil - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: AL - REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CD - REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CL - REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CM - REAL(ReKi) :: PMC - REAL(ReKi) :: MulTabLoc - END TYPE AirFoil -! ======================= -! ========= AirFoilParms ======= - TYPE, PUBLIC :: AirFoilParms - INTEGER(IntKi) :: MaxTable = 20 - INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: NTables - INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: NLift - INTEGER(IntKi) :: NumCL - INTEGER(IntKi) :: NumFoil - INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: NFoil - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: MulTabMet - CHARACTER(1024) , DIMENSION(:), ALLOCATABLE :: FoilNm - END TYPE AirFoilParms -! ======================= ! ========= Beddoes ======= TYPE, PUBLIC :: Beddoes REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: ADOT @@ -209,22 +168,22 @@ MODULE AeroDyn14_Types ! ======================= ! ========= Element ======= TYPE, PUBLIC :: Element - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: A - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: AP - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: ALPHA - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: W2 + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: A !< - [Axial induction factor] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: AP !< - [Tangential induction factor] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: ALPHA !< - [Angle of attack] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: W2 !< - [Relative velocity norm ] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: OLD_A_NS REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: OLD_AP_NS - REAL(ReKi) :: PITNOW + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: PITNOW !< - [Current pitch angle - Based on blade orientation (to verify)] END TYPE Element ! ======================= ! ========= ElementParms ======= TYPE, PUBLIC :: ElementParms - INTEGER(IntKi) :: NELM - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: TWIST - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: RELM - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: HLCNST - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: TLCNST + INTEGER(IntKi) :: NELM !< - [Number of elements (constant)] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: TWIST !< - [Airfoil twist angle (constant)] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: RELM !< - [Radius of element (constant)] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: HLCNST !< - [Hub loss constant B/2*(r-rh)/rh (constant)] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: TLCNST !< - [Tip loss constant B/2*(R-r)/R (constant) ] END TYPE ElementParms ! ======================= ! ========= ElOutParms ======= @@ -243,6 +202,7 @@ MODULE AeroDyn14_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: PMM REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: PITSAV REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: ReyNum + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Gamma !< - [Circulation along the span, 1/2 c Vrel Cl] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: SaveVX REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: SaveVY REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: SaveVZ @@ -353,38 +313,46 @@ MODULE AeroDyn14_Types REAL(ReKi) :: BladeLength !< Blade Length [-] LOGICAL :: LinearizeFlag LOGICAL :: UseDWM = .FALSE. !< flag to determine if DWM module should be used [-] - TYPE(AeroConfig) :: TurbineComponents + TYPE(AD14AeroConf_InputType) :: TurbineComponents INTEGER(IntKi) :: NumTwrNodes !< Number of ElastoDyn tower nodes. Tower drag will be computed at those points. [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: TwrNodeLocs !< Location of ElastoDyn tower nodes with respect to the inertial origin. [-] REAL(ReKi) :: HubHt !< hub height wrt inertial origin [m] TYPE(DWM_InitInputType) :: DWM + LOGICAL :: UseFVW !< flag to determine if FVW module should be used [-] + CHARACTER(1024) :: FVWFileName !< Main FVW input file name [-] + TYPE(FVW_InitInputType) :: FVW !< [-] END TYPE AD14_InitInputType ! ======================= ! ========= AD14_InitOutputType ======= TYPE, PUBLIC :: AD14_InitOutputType TYPE(ProgDesc) :: Ver !< version information [-] TYPE(DWM_InitOutputType) :: DWM + TYPE(FVW_InitOutputType) :: FVW REAL(ReKi) :: AirDens !< Air density [kg/m^3] END TYPE AD14_InitOutputType ! ======================= ! ========= AD14_ContinuousStateType ======= TYPE, PUBLIC :: AD14_ContinuousStateType TYPE(DWM_ContinuousStateType) :: DWM + TYPE(FVW_ContinuousStateType) :: FVW END TYPE AD14_ContinuousStateType ! ======================= ! ========= AD14_DiscreteStateType ======= TYPE, PUBLIC :: AD14_DiscreteStateType TYPE(DWM_DiscreteStateType) :: DWM + TYPE(FVW_DiscreteStateType) :: FVW END TYPE AD14_DiscreteStateType ! ======================= ! ========= AD14_ConstraintStateType ======= TYPE, PUBLIC :: AD14_ConstraintStateType TYPE(DWM_ConstraintStateType) :: DWM + TYPE(FVW_ConstraintStateType) :: FVW END TYPE AD14_ConstraintStateType ! ======================= ! ========= AD14_OtherStateType ======= TYPE, PUBLIC :: AD14_OtherStateType TYPE(DWM_OtherStateType) :: DWM !< variables for DWM module [-] + TYPE(FVW_OtherStateType) :: FVW !< variables for FVW module [-] END TYPE AD14_OtherStateType ! ======================= ! ========= AD14_MiscVarType ======= @@ -392,6 +360,7 @@ MODULE AeroDyn14_Types TYPE(DWM_MiscVarType) :: DWM !< variables for DWM module [-] TYPE(DWM_InputType) :: DWM_Inputs TYPE(DWM_OutputType) :: DWM_Outputs + TYPE(FVW_MiscVarType) :: FVW !< variables for FVW module [-] REAL(DbKi) :: DT !< actual Time step [seconds] INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: ElPrNum REAL(DbKi) :: OldTime @@ -406,7 +375,7 @@ MODULE AeroDyn14_Types LOGICAL :: OnePassDynDbg = .TRUE. LOGICAL :: NoLoadsCalculated = .TRUE. INTEGER(IntKi) :: NERRORS = 0 - TYPE(AirFoil) :: AirFoil + TYPE(AD14AeroConf_MiscVarType) :: AirFoil TYPE(Beddoes) :: Beddoes TYPE(DynInflow) :: DynInflow TYPE(Element) :: Element @@ -430,6 +399,7 @@ MODULE AeroDyn14_Types LOGICAL :: LinearizeFlag LOGICAL :: OutputPlottingInfo = .FALSE. LOGICAL :: UseDWM = .FALSE. !< flag to determine if DWM module should be used [-] + LOGICAL :: UseFVW = .TRUE. !< flag to determine if FVW module should be used [-] REAL(ReKi) :: TwoPiNB !< 2*pi/num of blades [-] INTEGER(IntKi) :: NumBl !< Number of Blades [-] INTEGER(IntKi) :: NBlInpSt !< Number of Blade Input Stations [-] @@ -448,7 +418,7 @@ MODULE AeroDyn14_Types INTEGER(IntKi) :: MAXICOUNT = 1000 LOGICAL :: WrOptFile = .TRUE. !< T/F: Write an AeroDyn summary [-] INTEGER(IntKi) :: DEFAULT_Wind = -1 - TYPE(AirFoilParms) :: AirFoil + TYPE(AD14AeroConf_ParameterType) :: AirFoil TYPE(BladeParms) :: Blade TYPE(BeddoesParms) :: Beddoes TYPE(DynInflowParms) :: DynInflow @@ -458,1983 +428,29 @@ MODULE AeroDyn14_Types TYPE(WindParms) :: Wind TYPE(RotorParms) :: Rotor TYPE(DWM_ParameterType) :: DWM + TYPE(FVW_ParameterType) :: FVW + REAL(DbKi) :: IfW_DT END TYPE AD14_ParameterType ! ======================= ! ========= AD14_InputType ======= TYPE, PUBLIC :: AD14_InputType TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: InputMarkers !< Input Forces and positions for the blades (mesh) for each blade [-] TYPE(MeshType) :: Twr_InputMarkers !< Input Forces and positions for the tower (mesh) [-] - TYPE(AeroConfig) :: TurbineComponents !< Current locations of components [-] + TYPE(AD14AeroConf_InputType) :: TurbineComponents !< Current locations of components [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: MulTabLoc REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: InflowVelocity !< U,V,W wind inflow speeds at all locations on the Inputmarker and Twr_InputMarker meshes [m/s] REAL(ReKi) , DIMENSION(1:3) :: AvgInfVel !< an average disk velocity (depends on wind type and should be removed) [m/s] + TYPE(FVW_InputType) :: FVW !< variables for FVW module [-] END TYPE AD14_InputType ! ======================= ! ========= AD14_OutputType ======= TYPE, PUBLIC :: AD14_OutputType TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: OutputLoads !< Output Loads (mesh) for each blade [-] - TYPE(MeshType) :: Twr_OutputLoads !< Tower Output Loads (mesh) [-] - END TYPE AD14_OutputType -! ======================= -CONTAINS - SUBROUTINE AD14_CopyMarker( SrcMarkerData, DstMarkerData, CtrlCode, ErrStat, ErrMsg ) - TYPE(Marker), INTENT(IN) :: SrcMarkerData - TYPE(Marker), INTENT(INOUT) :: DstMarkerData - 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) :: ErrStat2 - CHARACTER(ErrMsgLen) :: ErrMsg2 - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_CopyMarker' -! - ErrStat = ErrID_None - ErrMsg = "" - DstMarkerData%Position = SrcMarkerData%Position - DstMarkerData%Orientation = SrcMarkerData%Orientation - DstMarkerData%TranslationVel = SrcMarkerData%TranslationVel - DstMarkerData%RotationVel = SrcMarkerData%RotationVel - END SUBROUTINE AD14_CopyMarker - - SUBROUTINE AD14_DestroyMarker( MarkerData, ErrStat, ErrMsg ) - TYPE(Marker), INTENT(INOUT) :: MarkerData - INTEGER(IntKi), INTENT( OUT) :: ErrStat - CHARACTER(*), INTENT( OUT) :: ErrMsg - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_DestroyMarker' - INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 -! - ErrStat = ErrID_None - ErrMsg = "" - END SUBROUTINE AD14_DestroyMarker - - SUBROUTINE AD14_PackMarker( 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(Marker), 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 = 'AD14_PackMarker' - ! 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 - Re_BufSz = Re_BufSz + SIZE(InData%Position) ! Position - Re_BufSz = Re_BufSz + SIZE(InData%Orientation) ! Orientation - Re_BufSz = Re_BufSz + SIZE(InData%TranslationVel) ! TranslationVel - Re_BufSz = Re_BufSz + SIZE(InData%RotationVel) ! RotationVel - 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 - - ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Position))-1 ) = PACK(InData%Position,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%Position) - ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Orientation))-1 ) = PACK(InData%Orientation,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%Orientation) - ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%TranslationVel))-1 ) = PACK(InData%TranslationVel,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%TranslationVel) - ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%RotationVel))-1 ) = PACK(InData%RotationVel,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%RotationVel) - END SUBROUTINE AD14_PackMarker - - SUBROUTINE AD14_UnPackMarker( 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(Marker), 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 - LOGICAL :: mask0 - LOGICAL, ALLOCATABLE :: mask1(:) - LOGICAL, ALLOCATABLE :: mask2(:,:) - LOGICAL, ALLOCATABLE :: mask3(:,:,:) - LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) - LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) - 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) :: ErrStat2 - CHARACTER(ErrMsgLen) :: ErrMsg2 - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_UnPackMarker' - ! 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 - i1_l = LBOUND(OutData%Position,1) - i1_u = UBOUND(OutData%Position,1) - ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask1 = .TRUE. - OutData%Position = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Position))-1 ), mask1, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%Position) - DEALLOCATE(mask1) - i1_l = LBOUND(OutData%Orientation,1) - i1_u = UBOUND(OutData%Orientation,1) - i2_l = LBOUND(OutData%Orientation,2) - i2_u = UBOUND(OutData%Orientation,2) - ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask2 = .TRUE. - OutData%Orientation = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Orientation))-1 ), mask2, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%Orientation) - DEALLOCATE(mask2) - i1_l = LBOUND(OutData%TranslationVel,1) - i1_u = UBOUND(OutData%TranslationVel,1) - ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask1 = .TRUE. - OutData%TranslationVel = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%TranslationVel))-1 ), mask1, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%TranslationVel) - DEALLOCATE(mask1) - i1_l = LBOUND(OutData%RotationVel,1) - i1_u = UBOUND(OutData%RotationVel,1) - ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask1 = .TRUE. - OutData%RotationVel = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%RotationVel))-1 ), mask1, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%RotationVel) - DEALLOCATE(mask1) - END SUBROUTINE AD14_UnPackMarker - - SUBROUTINE AD14_CopyAeroConfig( SrcAeroConfigData, DstAeroConfigData, CtrlCode, ErrStat, ErrMsg ) - TYPE(AeroConfig), INTENT(IN) :: SrcAeroConfigData - TYPE(AeroConfig), INTENT(INOUT) :: DstAeroConfigData - 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) :: ErrStat2 - CHARACTER(ErrMsgLen) :: ErrMsg2 - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_CopyAeroConfig' -! - ErrStat = ErrID_None - ErrMsg = "" -IF (ALLOCATED(SrcAeroConfigData%Blade)) THEN - i1_l = LBOUND(SrcAeroConfigData%Blade,1) - i1_u = UBOUND(SrcAeroConfigData%Blade,1) - IF (.NOT. ALLOCATED(DstAeroConfigData%Blade)) THEN - ALLOCATE(DstAeroConfigData%Blade(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAeroConfigData%Blade.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DO i1 = LBOUND(SrcAeroConfigData%Blade,1), UBOUND(SrcAeroConfigData%Blade,1) - CALL AD14_Copymarker( SrcAeroConfigData%Blade(i1), DstAeroConfigData%Blade(i1), CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN - ENDDO -ENDIF - CALL AD14_Copymarker( SrcAeroConfigData%Hub, DstAeroConfigData%Hub, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN - CALL AD14_Copymarker( SrcAeroConfigData%RotorFurl, DstAeroConfigData%RotorFurl, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN - CALL AD14_Copymarker( SrcAeroConfigData%Nacelle, DstAeroConfigData%Nacelle, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN - CALL AD14_Copymarker( SrcAeroConfigData%TailFin, DstAeroConfigData%TailFin, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN - CALL AD14_Copymarker( SrcAeroConfigData%Tower, DstAeroConfigData%Tower, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN - CALL AD14_Copymarker( SrcAeroConfigData%SubStructure, DstAeroConfigData%SubStructure, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN - CALL AD14_Copymarker( SrcAeroConfigData%Foundation, DstAeroConfigData%Foundation, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN - DstAeroConfigData%BladeLength = SrcAeroConfigData%BladeLength - END SUBROUTINE AD14_CopyAeroConfig - - SUBROUTINE AD14_DestroyAeroConfig( AeroConfigData, ErrStat, ErrMsg ) - TYPE(AeroConfig), INTENT(INOUT) :: AeroConfigData - INTEGER(IntKi), INTENT( OUT) :: ErrStat - CHARACTER(*), INTENT( OUT) :: ErrMsg - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_DestroyAeroConfig' - INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 -! - ErrStat = ErrID_None - ErrMsg = "" -IF (ALLOCATED(AeroConfigData%Blade)) THEN -DO i1 = LBOUND(AeroConfigData%Blade,1), UBOUND(AeroConfigData%Blade,1) - CALL AD14_Destroymarker( AeroConfigData%Blade(i1), ErrStat, ErrMsg ) -ENDDO - DEALLOCATE(AeroConfigData%Blade) -ENDIF - CALL AD14_Destroymarker( AeroConfigData%Hub, ErrStat, ErrMsg ) - CALL AD14_Destroymarker( AeroConfigData%RotorFurl, ErrStat, ErrMsg ) - CALL AD14_Destroymarker( AeroConfigData%Nacelle, ErrStat, ErrMsg ) - CALL AD14_Destroymarker( AeroConfigData%TailFin, ErrStat, ErrMsg ) - CALL AD14_Destroymarker( AeroConfigData%Tower, ErrStat, ErrMsg ) - CALL AD14_Destroymarker( AeroConfigData%SubStructure, ErrStat, ErrMsg ) - CALL AD14_Destroymarker( AeroConfigData%Foundation, ErrStat, ErrMsg ) - END SUBROUTINE AD14_DestroyAeroConfig - - SUBROUTINE AD14_PackAeroConfig( 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(AeroConfig), 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 = 'AD14_PackAeroConfig' - ! 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 ! Blade allocated yes/no - IF ( ALLOCATED(InData%Blade) ) THEN - Int_BufSz = Int_BufSz + 2*1 ! Blade upper/lower bounds for each dimension - ! Allocate buffers for subtypes, if any (we'll get sizes from these) - DO i1 = LBOUND(InData%Blade,1), UBOUND(InData%Blade,1) - Int_BufSz = Int_BufSz + 3 ! Blade: size of buffers for each call to pack subtype - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Blade(i1), ErrStat2, ErrMsg2, .TRUE. ) ! Blade - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! Blade - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! Blade - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! Blade - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF - END DO - END IF - Int_BufSz = Int_BufSz + 3 ! Hub: size of buffers for each call to pack subtype - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Hub, ErrStat2, ErrMsg2, .TRUE. ) ! Hub - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! Hub - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! Hub - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! Hub - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF - Int_BufSz = Int_BufSz + 3 ! RotorFurl: size of buffers for each call to pack subtype - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%RotorFurl, ErrStat2, ErrMsg2, .TRUE. ) ! RotorFurl - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! RotorFurl - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! RotorFurl - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! RotorFurl - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF - Int_BufSz = Int_BufSz + 3 ! Nacelle: size of buffers for each call to pack subtype - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Nacelle, ErrStat2, ErrMsg2, .TRUE. ) ! Nacelle - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! Nacelle - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! Nacelle - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! Nacelle - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF - Int_BufSz = Int_BufSz + 3 ! TailFin: size of buffers for each call to pack subtype - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%TailFin, ErrStat2, ErrMsg2, .TRUE. ) ! TailFin - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! TailFin - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! TailFin - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! TailFin - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF - Int_BufSz = Int_BufSz + 3 ! Tower: size of buffers for each call to pack subtype - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Tower, ErrStat2, ErrMsg2, .TRUE. ) ! Tower - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! Tower - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! Tower - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! Tower - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF - Int_BufSz = Int_BufSz + 3 ! SubStructure: size of buffers for each call to pack subtype - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%SubStructure, ErrStat2, ErrMsg2, .TRUE. ) ! SubStructure - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! SubStructure - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! SubStructure - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! SubStructure - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF - Int_BufSz = Int_BufSz + 3 ! Foundation: size of buffers for each call to pack subtype - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Foundation, ErrStat2, ErrMsg2, .TRUE. ) ! Foundation - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! Foundation - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! Foundation - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! Foundation - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF - Re_BufSz = Re_BufSz + 1 ! BladeLength - 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 - - IF ( .NOT. ALLOCATED(InData%Blade) ) 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%Blade,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Blade,1) - Int_Xferred = Int_Xferred + 2 - - DO i1 = LBOUND(InData%Blade,1), UBOUND(InData%Blade,1) - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Blade(i1), ErrStat2, ErrMsg2, OnlySize ) ! Blade - 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 DO - END IF - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Hub, ErrStat2, ErrMsg2, OnlySize ) ! Hub - 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 - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%RotorFurl, ErrStat2, ErrMsg2, OnlySize ) ! RotorFurl - 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 - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Nacelle, ErrStat2, ErrMsg2, OnlySize ) ! Nacelle - 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 - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%TailFin, ErrStat2, ErrMsg2, OnlySize ) ! TailFin - 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 - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Tower, ErrStat2, ErrMsg2, OnlySize ) ! Tower - 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 - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%SubStructure, ErrStat2, ErrMsg2, OnlySize ) ! SubStructure - 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 - CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Foundation, ErrStat2, ErrMsg2, OnlySize ) ! Foundation - 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 - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%BladeLength - Re_Xferred = Re_Xferred + 1 - END SUBROUTINE AD14_PackAeroConfig - - SUBROUTINE AD14_UnPackAeroConfig( 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(AeroConfig), 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 - LOGICAL :: mask0 - LOGICAL, ALLOCATABLE :: mask1(:) - LOGICAL, ALLOCATABLE :: mask2(:,:) - LOGICAL, ALLOCATABLE :: mask3(:,:,:) - LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) - LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) - INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 - INTEGER(IntKi) :: ErrStat2 - CHARACTER(ErrMsgLen) :: ErrMsg2 - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_UnPackAeroConfig' - ! 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 - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Blade 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 - IF (ALLOCATED(OutData%Blade)) DEALLOCATE(OutData%Blade) - ALLOCATE(OutData%Blade(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Blade.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - DO i1 = LBOUND(OutData%Blade,1), UBOUND(OutData%Blade,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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Blade(i1), ErrStat2, ErrMsg2 ) ! Blade - 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 DO - END IF - 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Hub, ErrStat2, ErrMsg2 ) ! Hub - 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) - 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%RotorFurl, ErrStat2, ErrMsg2 ) ! RotorFurl - 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) - 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Nacelle, ErrStat2, ErrMsg2 ) ! Nacelle - 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) - 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%TailFin, ErrStat2, ErrMsg2 ) ! TailFin - 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) - 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Tower, ErrStat2, ErrMsg2 ) ! Tower - 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) - 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%SubStructure, ErrStat2, ErrMsg2 ) ! SubStructure - 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) - 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Foundation, ErrStat2, ErrMsg2 ) ! Foundation - 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) - OutData%BladeLength = ReKiBuf( Re_Xferred ) - Re_Xferred = Re_Xferred + 1 - END SUBROUTINE AD14_UnPackAeroConfig - - SUBROUTINE AD14_CopyAirFoil( SrcAirFoilData, DstAirFoilData, CtrlCode, ErrStat, ErrMsg ) - TYPE(AirFoil), INTENT(IN) :: SrcAirFoilData - TYPE(AirFoil), INTENT(INOUT) :: DstAirFoilData - 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) :: ErrStat2 - CHARACTER(ErrMsgLen) :: ErrMsg2 - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_CopyAirFoil' -! - ErrStat = ErrID_None - ErrMsg = "" -IF (ALLOCATED(SrcAirFoilData%AL)) THEN - i1_l = LBOUND(SrcAirFoilData%AL,1) - i1_u = UBOUND(SrcAirFoilData%AL,1) - i2_l = LBOUND(SrcAirFoilData%AL,2) - i2_u = UBOUND(SrcAirFoilData%AL,2) - IF (.NOT. ALLOCATED(DstAirFoilData%AL)) THEN - ALLOCATE(DstAirFoilData%AL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilData%AL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstAirFoilData%AL = SrcAirFoilData%AL -ENDIF -IF (ALLOCATED(SrcAirFoilData%CD)) THEN - i1_l = LBOUND(SrcAirFoilData%CD,1) - i1_u = UBOUND(SrcAirFoilData%CD,1) - i2_l = LBOUND(SrcAirFoilData%CD,2) - i2_u = UBOUND(SrcAirFoilData%CD,2) - i3_l = LBOUND(SrcAirFoilData%CD,3) - i3_u = UBOUND(SrcAirFoilData%CD,3) - IF (.NOT. ALLOCATED(DstAirFoilData%CD)) THEN - ALLOCATE(DstAirFoilData%CD(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilData%CD.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstAirFoilData%CD = SrcAirFoilData%CD -ENDIF -IF (ALLOCATED(SrcAirFoilData%CL)) THEN - i1_l = LBOUND(SrcAirFoilData%CL,1) - i1_u = UBOUND(SrcAirFoilData%CL,1) - i2_l = LBOUND(SrcAirFoilData%CL,2) - i2_u = UBOUND(SrcAirFoilData%CL,2) - i3_l = LBOUND(SrcAirFoilData%CL,3) - i3_u = UBOUND(SrcAirFoilData%CL,3) - IF (.NOT. ALLOCATED(DstAirFoilData%CL)) THEN - ALLOCATE(DstAirFoilData%CL(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilData%CL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstAirFoilData%CL = SrcAirFoilData%CL -ENDIF -IF (ALLOCATED(SrcAirFoilData%CM)) THEN - i1_l = LBOUND(SrcAirFoilData%CM,1) - i1_u = UBOUND(SrcAirFoilData%CM,1) - i2_l = LBOUND(SrcAirFoilData%CM,2) - i2_u = UBOUND(SrcAirFoilData%CM,2) - i3_l = LBOUND(SrcAirFoilData%CM,3) - i3_u = UBOUND(SrcAirFoilData%CM,3) - IF (.NOT. ALLOCATED(DstAirFoilData%CM)) THEN - ALLOCATE(DstAirFoilData%CM(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilData%CM.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstAirFoilData%CM = SrcAirFoilData%CM -ENDIF - DstAirFoilData%PMC = SrcAirFoilData%PMC - DstAirFoilData%MulTabLoc = SrcAirFoilData%MulTabLoc - END SUBROUTINE AD14_CopyAirFoil - - SUBROUTINE AD14_DestroyAirFoil( AirFoilData, ErrStat, ErrMsg ) - TYPE(AirFoil), INTENT(INOUT) :: AirFoilData - INTEGER(IntKi), INTENT( OUT) :: ErrStat - CHARACTER(*), INTENT( OUT) :: ErrMsg - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_DestroyAirFoil' - INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 -! - ErrStat = ErrID_None - ErrMsg = "" -IF (ALLOCATED(AirFoilData%AL)) THEN - DEALLOCATE(AirFoilData%AL) -ENDIF -IF (ALLOCATED(AirFoilData%CD)) THEN - DEALLOCATE(AirFoilData%CD) -ENDIF -IF (ALLOCATED(AirFoilData%CL)) THEN - DEALLOCATE(AirFoilData%CL) -ENDIF -IF (ALLOCATED(AirFoilData%CM)) THEN - DEALLOCATE(AirFoilData%CM) -ENDIF - END SUBROUTINE AD14_DestroyAirFoil - - SUBROUTINE AD14_PackAirFoil( 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(AirFoil), 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 = 'AD14_PackAirFoil' - ! 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 ! AL allocated yes/no - IF ( ALLOCATED(InData%AL) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! AL upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%AL) ! AL - END IF - Int_BufSz = Int_BufSz + 1 ! CD allocated yes/no - IF ( ALLOCATED(InData%CD) ) THEN - Int_BufSz = Int_BufSz + 2*3 ! CD upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%CD) ! CD - END IF - Int_BufSz = Int_BufSz + 1 ! CL allocated yes/no - IF ( ALLOCATED(InData%CL) ) THEN - Int_BufSz = Int_BufSz + 2*3 ! CL upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%CL) ! CL - END IF - Int_BufSz = Int_BufSz + 1 ! CM allocated yes/no - IF ( ALLOCATED(InData%CM) ) THEN - Int_BufSz = Int_BufSz + 2*3 ! CM upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%CM) ! CM - END IF - Re_BufSz = Re_BufSz + 1 ! PMC - Re_BufSz = Re_BufSz + 1 ! MulTabLoc - 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 - - IF ( .NOT. ALLOCATED(InData%AL) ) 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%AL,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AL,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%AL,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AL,2) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%AL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%AL))-1 ) = PACK(InData%AL,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%AL) - END IF - IF ( .NOT. ALLOCATED(InData%CD) ) 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%CD,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CD,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%CD,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CD,2) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%CD,3) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CD,3) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%CD)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CD))-1 ) = PACK(InData%CD,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%CD) - END IF - IF ( .NOT. ALLOCATED(InData%CL) ) 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%CL,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CL,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%CL,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CL,2) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%CL,3) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CL,3) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%CL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CL))-1 ) = PACK(InData%CL,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%CL) - END IF - IF ( .NOT. ALLOCATED(InData%CM) ) 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%CM,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CM,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%CM,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CM,2) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%CM,3) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CM,3) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%CM)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CM))-1 ) = PACK(InData%CM,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%CM) - END IF - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%PMC - Re_Xferred = Re_Xferred + 1 - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%MulTabLoc - Re_Xferred = Re_Xferred + 1 - END SUBROUTINE AD14_PackAirFoil - - SUBROUTINE AD14_UnPackAirFoil( 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(AirFoil), 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 - LOGICAL :: mask0 - LOGICAL, ALLOCATABLE :: mask1(:) - LOGICAL, ALLOCATABLE :: mask2(:,:) - LOGICAL, ALLOCATABLE :: mask3(:,:,:) - LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) - LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) - 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) :: ErrStat2 - CHARACTER(ErrMsgLen) :: ErrMsg2 - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_UnPackAirFoil' - ! 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 - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! AL 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 - IF (ALLOCATED(OutData%AL)) DEALLOCATE(OutData%AL) - ALLOCATE(OutData%AL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%AL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask2 = .TRUE. - IF (SIZE(OutData%AL)>0) OutData%AL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%AL))-1 ), mask2, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%AL) - DEALLOCATE(mask2) - END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CD 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%CD)) DEALLOCATE(OutData%CD) - ALLOCATE(OutData%CD(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%CD.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask3 = .TRUE. - IF (SIZE(OutData%CD)>0) OutData%CD = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CD))-1 ), mask3, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%CD) - DEALLOCATE(mask3) - END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CL 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%CL)) DEALLOCATE(OutData%CL) - ALLOCATE(OutData%CL(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%CL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask3 = .TRUE. - IF (SIZE(OutData%CL)>0) OutData%CL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CL))-1 ), mask3, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%CL) - DEALLOCATE(mask3) - END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CM 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%CM)) DEALLOCATE(OutData%CM) - ALLOCATE(OutData%CM(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%CM.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask3 = .TRUE. - IF (SIZE(OutData%CM)>0) OutData%CM = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CM))-1 ), mask3, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%CM) - DEALLOCATE(mask3) - END IF - OutData%PMC = ReKiBuf( Re_Xferred ) - Re_Xferred = Re_Xferred + 1 - OutData%MulTabLoc = ReKiBuf( Re_Xferred ) - Re_Xferred = Re_Xferred + 1 - END SUBROUTINE AD14_UnPackAirFoil - - SUBROUTINE AD14_CopyAirFoilParms( SrcAirFoilParmsData, DstAirFoilParmsData, CtrlCode, ErrStat, ErrMsg ) - TYPE(AirFoilParms), INTENT(IN) :: SrcAirFoilParmsData - TYPE(AirFoilParms), INTENT(INOUT) :: DstAirFoilParmsData - 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) :: ErrStat2 - CHARACTER(ErrMsgLen) :: ErrMsg2 - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_CopyAirFoilParms' -! - ErrStat = ErrID_None - ErrMsg = "" - DstAirFoilParmsData%MaxTable = SrcAirFoilParmsData%MaxTable -IF (ALLOCATED(SrcAirFoilParmsData%NTables)) THEN - i1_l = LBOUND(SrcAirFoilParmsData%NTables,1) - i1_u = UBOUND(SrcAirFoilParmsData%NTables,1) - IF (.NOT. ALLOCATED(DstAirFoilParmsData%NTables)) THEN - ALLOCATE(DstAirFoilParmsData%NTables(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%NTables.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstAirFoilParmsData%NTables = SrcAirFoilParmsData%NTables -ENDIF -IF (ALLOCATED(SrcAirFoilParmsData%NLift)) THEN - i1_l = LBOUND(SrcAirFoilParmsData%NLift,1) - i1_u = UBOUND(SrcAirFoilParmsData%NLift,1) - IF (.NOT. ALLOCATED(DstAirFoilParmsData%NLift)) THEN - ALLOCATE(DstAirFoilParmsData%NLift(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%NLift.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstAirFoilParmsData%NLift = SrcAirFoilParmsData%NLift -ENDIF - DstAirFoilParmsData%NumCL = SrcAirFoilParmsData%NumCL - DstAirFoilParmsData%NumFoil = SrcAirFoilParmsData%NumFoil -IF (ALLOCATED(SrcAirFoilParmsData%NFoil)) THEN - i1_l = LBOUND(SrcAirFoilParmsData%NFoil,1) - i1_u = UBOUND(SrcAirFoilParmsData%NFoil,1) - IF (.NOT. ALLOCATED(DstAirFoilParmsData%NFoil)) THEN - ALLOCATE(DstAirFoilParmsData%NFoil(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%NFoil.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstAirFoilParmsData%NFoil = SrcAirFoilParmsData%NFoil -ENDIF -IF (ALLOCATED(SrcAirFoilParmsData%MulTabMet)) THEN - i1_l = LBOUND(SrcAirFoilParmsData%MulTabMet,1) - i1_u = UBOUND(SrcAirFoilParmsData%MulTabMet,1) - i2_l = LBOUND(SrcAirFoilParmsData%MulTabMet,2) - i2_u = UBOUND(SrcAirFoilParmsData%MulTabMet,2) - IF (.NOT. ALLOCATED(DstAirFoilParmsData%MulTabMet)) THEN - ALLOCATE(DstAirFoilParmsData%MulTabMet(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%MulTabMet.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstAirFoilParmsData%MulTabMet = SrcAirFoilParmsData%MulTabMet -ENDIF -IF (ALLOCATED(SrcAirFoilParmsData%FoilNm)) THEN - i1_l = LBOUND(SrcAirFoilParmsData%FoilNm,1) - i1_u = UBOUND(SrcAirFoilParmsData%FoilNm,1) - IF (.NOT. ALLOCATED(DstAirFoilParmsData%FoilNm)) THEN - ALLOCATE(DstAirFoilParmsData%FoilNm(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%FoilNm.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstAirFoilParmsData%FoilNm = SrcAirFoilParmsData%FoilNm -ENDIF - END SUBROUTINE AD14_CopyAirFoilParms - - SUBROUTINE AD14_DestroyAirFoilParms( AirFoilParmsData, ErrStat, ErrMsg ) - TYPE(AirFoilParms), INTENT(INOUT) :: AirFoilParmsData - INTEGER(IntKi), INTENT( OUT) :: ErrStat - CHARACTER(*), INTENT( OUT) :: ErrMsg - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_DestroyAirFoilParms' - INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 -! - ErrStat = ErrID_None - ErrMsg = "" -IF (ALLOCATED(AirFoilParmsData%NTables)) THEN - DEALLOCATE(AirFoilParmsData%NTables) -ENDIF -IF (ALLOCATED(AirFoilParmsData%NLift)) THEN - DEALLOCATE(AirFoilParmsData%NLift) -ENDIF -IF (ALLOCATED(AirFoilParmsData%NFoil)) THEN - DEALLOCATE(AirFoilParmsData%NFoil) -ENDIF -IF (ALLOCATED(AirFoilParmsData%MulTabMet)) THEN - DEALLOCATE(AirFoilParmsData%MulTabMet) -ENDIF -IF (ALLOCATED(AirFoilParmsData%FoilNm)) THEN - DEALLOCATE(AirFoilParmsData%FoilNm) -ENDIF - END SUBROUTINE AD14_DestroyAirFoilParms - - SUBROUTINE AD14_PackAirFoilParms( 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(AirFoilParms), 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 = 'AD14_PackAirFoilParms' - ! 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 ! MaxTable - Int_BufSz = Int_BufSz + 1 ! NTables allocated yes/no - IF ( ALLOCATED(InData%NTables) ) THEN - Int_BufSz = Int_BufSz + 2*1 ! NTables upper/lower bounds for each dimension - Int_BufSz = Int_BufSz + SIZE(InData%NTables) ! NTables - END IF - Int_BufSz = Int_BufSz + 1 ! NLift allocated yes/no - IF ( ALLOCATED(InData%NLift) ) THEN - Int_BufSz = Int_BufSz + 2*1 ! NLift upper/lower bounds for each dimension - Int_BufSz = Int_BufSz + SIZE(InData%NLift) ! NLift - END IF - Int_BufSz = Int_BufSz + 1 ! NumCL - Int_BufSz = Int_BufSz + 1 ! NumFoil - Int_BufSz = Int_BufSz + 1 ! NFoil allocated yes/no - IF ( ALLOCATED(InData%NFoil) ) THEN - Int_BufSz = Int_BufSz + 2*1 ! NFoil upper/lower bounds for each dimension - Int_BufSz = Int_BufSz + SIZE(InData%NFoil) ! NFoil - END IF - Int_BufSz = Int_BufSz + 1 ! MulTabMet allocated yes/no - IF ( ALLOCATED(InData%MulTabMet) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! MulTabMet upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%MulTabMet) ! MulTabMet - END IF - Int_BufSz = Int_BufSz + 1 ! FoilNm allocated yes/no - IF ( ALLOCATED(InData%FoilNm) ) THEN - Int_BufSz = Int_BufSz + 2*1 ! FoilNm upper/lower bounds for each dimension - Int_BufSz = Int_BufSz + SIZE(InData%FoilNm)*LEN(InData%FoilNm) ! FoilNm - END IF - 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:Int_Xferred+(1)-1 ) = InData%MaxTable - Int_Xferred = Int_Xferred + 1 - IF ( .NOT. ALLOCATED(InData%NTables) ) 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%NTables,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%NTables,1) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%NTables)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%NTables))-1 ) = PACK(InData%NTables,.TRUE.) - Int_Xferred = Int_Xferred + SIZE(InData%NTables) - END IF - IF ( .NOT. ALLOCATED(InData%NLift) ) 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%NLift,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%NLift,1) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%NLift)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%NLift))-1 ) = PACK(InData%NLift,.TRUE.) - Int_Xferred = Int_Xferred + SIZE(InData%NLift) - END IF - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumCL - Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumFoil - Int_Xferred = Int_Xferred + 1 - IF ( .NOT. ALLOCATED(InData%NFoil) ) 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%NFoil,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%NFoil,1) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%NFoil)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%NFoil))-1 ) = PACK(InData%NFoil,.TRUE.) - Int_Xferred = Int_Xferred + SIZE(InData%NFoil) - END IF - IF ( .NOT. ALLOCATED(InData%MulTabMet) ) 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%MulTabMet,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%MulTabMet,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%MulTabMet,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%MulTabMet,2) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%MulTabMet)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%MulTabMet))-1 ) = PACK(InData%MulTabMet,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%MulTabMet) - END IF - IF ( .NOT. ALLOCATED(InData%FoilNm) ) 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%FoilNm,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FoilNm,1) - Int_Xferred = Int_Xferred + 2 - - DO i1 = LBOUND(InData%FoilNm,1), UBOUND(InData%FoilNm,1) - DO I = 1, LEN(InData%FoilNm) - IntKiBuf(Int_Xferred) = ICHAR(InData%FoilNm(i1)(I:I), IntKi) - Int_Xferred = Int_Xferred + 1 - END DO ! I - END DO !i1 - END IF - END SUBROUTINE AD14_PackAirFoilParms - - SUBROUTINE AD14_UnPackAirFoilParms( 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(AirFoilParms), 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 - LOGICAL :: mask0 - LOGICAL, ALLOCATABLE :: mask1(:) - LOGICAL, ALLOCATABLE :: mask2(:,:) - LOGICAL, ALLOCATABLE :: mask3(:,:,:) - LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) - LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) - 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) :: ErrStat2 - CHARACTER(ErrMsgLen) :: ErrMsg2 - CHARACTER(*), PARAMETER :: RoutineName = 'AD14_UnPackAirFoilParms' - ! 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%MaxTable = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! NTables 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 - IF (ALLOCATED(OutData%NTables)) DEALLOCATE(OutData%NTables) - ALLOCATE(OutData%NTables(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%NTables.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask1 = .TRUE. - IF (SIZE(OutData%NTables)>0) OutData%NTables = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%NTables))-1 ), mask1, 0_IntKi ) - Int_Xferred = Int_Xferred + SIZE(OutData%NTables) - DEALLOCATE(mask1) - END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! NLift 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 - IF (ALLOCATED(OutData%NLift)) DEALLOCATE(OutData%NLift) - ALLOCATE(OutData%NLift(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%NLift.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask1 = .TRUE. - IF (SIZE(OutData%NLift)>0) OutData%NLift = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%NLift))-1 ), mask1, 0_IntKi ) - Int_Xferred = Int_Xferred + SIZE(OutData%NLift) - DEALLOCATE(mask1) - END IF - OutData%NumCL = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 - OutData%NumFoil = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! NFoil 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 - IF (ALLOCATED(OutData%NFoil)) DEALLOCATE(OutData%NFoil) - ALLOCATE(OutData%NFoil(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%NFoil.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask1 = .TRUE. - IF (SIZE(OutData%NFoil)>0) OutData%NFoil = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%NFoil))-1 ), mask1, 0_IntKi ) - Int_Xferred = Int_Xferred + SIZE(OutData%NFoil) - DEALLOCATE(mask1) - END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! MulTabMet 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 - IF (ALLOCATED(OutData%MulTabMet)) DEALLOCATE(OutData%MulTabMet) - ALLOCATE(OutData%MulTabMet(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%MulTabMet.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask2 = .TRUE. - IF (SIZE(OutData%MulTabMet)>0) OutData%MulTabMet = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%MulTabMet))-1 ), mask2, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%MulTabMet) - DEALLOCATE(mask2) - END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FoilNm 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 - IF (ALLOCATED(OutData%FoilNm)) DEALLOCATE(OutData%FoilNm) - ALLOCATE(OutData%FoilNm(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FoilNm.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask1 = .TRUE. - DO i1 = LBOUND(OutData%FoilNm,1), UBOUND(OutData%FoilNm,1) - DO I = 1, LEN(OutData%FoilNm) - OutData%FoilNm(i1)(I:I) = CHAR(IntKiBuf(Int_Xferred)) - Int_Xferred = Int_Xferred + 1 - END DO ! I - END DO !i1 - DEALLOCATE(mask1) - END IF - END SUBROUTINE AD14_UnPackAirFoilParms - + TYPE(MeshType) :: Twr_OutputLoads !< Tower Output Loads (mesh) [-] + TYPE(FVW_OutputType) :: FVW !< variables for FVW module [-] + END TYPE AD14_OutputType +! ======================= +CONTAINS SUBROUTINE AD14_CopyBeddoes( SrcBeddoesData, DstBeddoesData, CtrlCode, ErrStat, ErrMsg ) TYPE(Beddoes), INTENT(IN) :: SrcBeddoesData TYPE(Beddoes), INTENT(INOUT) :: DstBeddoesData @@ -7203,7 +5219,20 @@ SUBROUTINE AD14_CopyElement( SrcElementData, DstElementData, CtrlCode, ErrStat, END IF DstElementData%OLD_AP_NS = SrcElementData%OLD_AP_NS ENDIF +IF (ALLOCATED(SrcElementData%PITNOW)) THEN + i1_l = LBOUND(SrcElementData%PITNOW,1) + i1_u = UBOUND(SrcElementData%PITNOW,1) + i2_l = LBOUND(SrcElementData%PITNOW,2) + i2_u = UBOUND(SrcElementData%PITNOW,2) + IF (.NOT. ALLOCATED(DstElementData%PITNOW)) THEN + ALLOCATE(DstElementData%PITNOW(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstElementData%PITNOW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF DstElementData%PITNOW = SrcElementData%PITNOW +ENDIF END SUBROUTINE AD14_CopyElement SUBROUTINE AD14_DestroyElement( ElementData, ErrStat, ErrMsg ) @@ -7232,6 +5261,9 @@ SUBROUTINE AD14_DestroyElement( ElementData, ErrStat, ErrMsg ) ENDIF IF (ALLOCATED(ElementData%OLD_AP_NS)) THEN DEALLOCATE(ElementData%OLD_AP_NS) +ENDIF +IF (ALLOCATED(ElementData%PITNOW)) THEN + DEALLOCATE(ElementData%PITNOW) ENDIF END SUBROUTINE AD14_DestroyElement @@ -7300,7 +5332,11 @@ SUBROUTINE AD14_PackElement( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Int_BufSz = Int_BufSz + 2*2 ! OLD_AP_NS upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%OLD_AP_NS) ! OLD_AP_NS END IF - Re_BufSz = Re_BufSz + 1 ! PITNOW + Int_BufSz = Int_BufSz + 1 ! PITNOW allocated yes/no + IF ( ALLOCATED(InData%PITNOW) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! PITNOW upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%PITNOW) ! PITNOW + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -7424,8 +5460,22 @@ SUBROUTINE AD14_PackElement( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg IF (SIZE(InData%OLD_AP_NS)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%OLD_AP_NS))-1 ) = PACK(InData%OLD_AP_NS,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%OLD_AP_NS) END IF - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%PITNOW - Re_Xferred = Re_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%PITNOW) ) 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%PITNOW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%PITNOW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%PITNOW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%PITNOW,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%PITNOW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%PITNOW))-1 ) = PACK(InData%PITNOW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%PITNOW) + END IF END SUBROUTINE AD14_PackElement SUBROUTINE AD14_UnPackElement( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -7618,8 +5668,32 @@ SUBROUTINE AD14_UnPackElement( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err Re_Xferred = Re_Xferred + SIZE(OutData%OLD_AP_NS) DEALLOCATE(mask2) END IF - OutData%PITNOW = ReKiBuf( Re_Xferred ) - Re_Xferred = Re_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! PITNOW 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 + IF (ALLOCATED(OutData%PITNOW)) DEALLOCATE(OutData%PITNOW) + ALLOCATE(OutData%PITNOW(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%PITNOW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%PITNOW)>0) OutData%PITNOW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%PITNOW))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%PITNOW) + DEALLOCATE(mask2) + END IF END SUBROUTINE AD14_UnPackElement SUBROUTINE AD14_CopyElementParms( SrcElementParmsData, DstElementParmsData, CtrlCode, ErrStat, ErrMsg ) @@ -8163,6 +6237,18 @@ SUBROUTINE AD14_CopyElOutParms( SrcElOutParmsData, DstElOutParmsData, CtrlCode, END IF DstElOutParmsData%ReyNum = SrcElOutParmsData%ReyNum ENDIF +IF (ALLOCATED(SrcElOutParmsData%Gamma)) THEN + i1_l = LBOUND(SrcElOutParmsData%Gamma,1) + i1_u = UBOUND(SrcElOutParmsData%Gamma,1) + IF (.NOT. ALLOCATED(DstElOutParmsData%Gamma)) THEN + ALLOCATE(DstElOutParmsData%Gamma(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstElOutParmsData%Gamma.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstElOutParmsData%Gamma = SrcElOutParmsData%Gamma +ENDIF IF (ALLOCATED(SrcElOutParmsData%SaveVX)) THEN i1_l = LBOUND(SrcElOutParmsData%SaveVX,1) i1_u = UBOUND(SrcElOutParmsData%SaveVX,1) @@ -8311,6 +6397,9 @@ SUBROUTINE AD14_DestroyElOutParms( ElOutParmsData, ErrStat, ErrMsg ) IF (ALLOCATED(ElOutParmsData%ReyNum)) THEN DEALLOCATE(ElOutParmsData%ReyNum) ENDIF +IF (ALLOCATED(ElOutParmsData%Gamma)) THEN + DEALLOCATE(ElOutParmsData%Gamma) +ENDIF IF (ALLOCATED(ElOutParmsData%SaveVX)) THEN DEALLOCATE(ElOutParmsData%SaveVX) ENDIF @@ -8439,6 +6528,11 @@ SUBROUTINE AD14_PackElOutParms( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err Int_BufSz = Int_BufSz + 2*1 ! ReyNum upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%ReyNum) ! ReyNum END IF + Int_BufSz = Int_BufSz + 1 ! Gamma allocated yes/no + IF ( ALLOCATED(InData%Gamma) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! Gamma upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Gamma) ! Gamma + END IF Int_BufSz = Int_BufSz + 1 ! SaveVX allocated yes/no IF ( ALLOCATED(InData%SaveVX) ) THEN Int_BufSz = Int_BufSz + 2*2 ! SaveVX upper/lower bounds for each dimension @@ -8688,6 +6782,19 @@ SUBROUTINE AD14_PackElOutParms( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err IF (SIZE(InData%ReyNum)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%ReyNum))-1 ) = PACK(InData%ReyNum,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%ReyNum) END IF + IF ( .NOT. ALLOCATED(InData%Gamma) ) 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%Gamma,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Gamma)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Gamma))-1 ) = PACK(InData%Gamma,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Gamma) + END IF IF ( .NOT. ALLOCATED(InData%SaveVX) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -9156,6 +7263,29 @@ SUBROUTINE AD14_UnPackElOutParms( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Re_Xferred = Re_Xferred + SIZE(OutData%ReyNum) DEALLOCATE(mask1) END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Gamma 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 + IF (ALLOCATED(OutData%Gamma)) DEALLOCATE(OutData%Gamma) + ALLOCATE(OutData%Gamma(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Gamma.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%Gamma)>0) OutData%Gamma = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Gamma))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Gamma) + DEALLOCATE(mask1) + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! SaveVX not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -11115,7 +9245,7 @@ SUBROUTINE AD14_UnPackOrientationType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrS END SUBROUTINE AD14_UnPackOrientationType SUBROUTINE AD14_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrStat, ErrMsg ) - TYPE(AD14_InitInputType), INTENT(IN) :: SrcInitInputData + TYPE(AD14_InitInputType), INTENT(INOUT) :: SrcInitInputData TYPE(AD14_InitInputType), INTENT(INOUT) :: DstInitInputData INTEGER(IntKi), INTENT(IN ) :: CtrlCode INTEGER(IntKi), INTENT( OUT) :: ErrStat @@ -11138,7 +9268,7 @@ SUBROUTINE AD14_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, Err DstInitInputData%BladeLength = SrcInitInputData%BladeLength DstInitInputData%LinearizeFlag = SrcInitInputData%LinearizeFlag DstInitInputData%UseDWM = SrcInitInputData%UseDWM - CALL AD14_Copyaeroconfig( SrcInitInputData%TurbineComponents, DstInitInputData%TurbineComponents, CtrlCode, ErrStat2, ErrMsg2 ) + CALL AD14AeroConf_CopyInput( SrcInitInputData%TurbineComponents, DstInitInputData%TurbineComponents, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN DstInitInputData%NumTwrNodes = SrcInitInputData%NumTwrNodes @@ -11160,6 +9290,11 @@ SUBROUTINE AD14_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, Err CALL DWM_CopyInitInput( SrcInitInputData%DWM, DstInitInputData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + DstInitInputData%UseFVW = SrcInitInputData%UseFVW + DstInitInputData%FVWFileName = SrcInitInputData%FVWFileName + CALL FVW_CopyInitInput( SrcInitInputData%FVW, DstInitInputData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyInitInput SUBROUTINE AD14_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -11171,11 +9306,12 @@ SUBROUTINE AD14_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" - CALL AD14_Destroyaeroconfig( InitInputData%TurbineComponents, ErrStat, ErrMsg ) + CALL AD14AeroConf_DestroyInput( InitInputData%TurbineComponents, ErrStat, ErrMsg ) IF (ALLOCATED(InitInputData%TwrNodeLocs)) THEN DEALLOCATE(InitInputData%TwrNodeLocs) ENDIF CALL DWM_DestroyInitInput( InitInputData%DWM, ErrStat, ErrMsg ) + CALL FVW_DestroyInitInput( InitInputData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyInitInput SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -11223,7 +9359,7 @@ SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + 1 ! UseDWM ! Allocate buffers for subtypes, if any (we'll get sizes from these) Int_BufSz = Int_BufSz + 3 ! TurbineComponents: size of buffers for each call to pack subtype - CALL AD14_Packaeroconfig( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, .TRUE. ) ! TurbineComponents + CALL AD14AeroConf_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, .TRUE. ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -11263,6 +9399,25 @@ SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 1 ! UseFVW + Int_BufSz = Int_BufSz + 1*LEN(InData%FVWFileName) ! FVWFileName + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackInitInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -11312,7 +9467,7 @@ SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UseDWM , IntKiBuf(1), 1) Int_Xferred = Int_Xferred + 1 - CALL AD14_Packaeroconfig( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, OnlySize ) ! TurbineComponents + CALL AD14AeroConf_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, OnlySize ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -11388,6 +9543,40 @@ SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UseFVW , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(InData%FVWFileName) + IntKiBuf(Int_Xferred) = ICHAR(InData%FVWFileName(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I + CALL FVW_PackInitInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD14_PackInitInput SUBROUTINE AD14_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -11479,7 +9668,7 @@ SUBROUTINE AD14_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL AD14_Unpackaeroconfig( Re_Buf, Db_Buf, Int_Buf, OutData%TurbineComponents, ErrStat2, ErrMsg2 ) ! TurbineComponents + CALL AD14AeroConf_UnpackInput( Re_Buf, Db_Buf, Int_Buf, OutData%TurbineComponents, ErrStat2, ErrMsg2 ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -11556,6 +9745,52 @@ SUBROUTINE AD14_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + OutData%UseFVW = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(OutData%FVWFileName) + OutData%FVWFileName(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I + 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 FVW_UnpackInitInput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD14_UnPackInitInput SUBROUTINE AD14_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -11578,6 +9813,9 @@ SUBROUTINE AD14_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, CALL DWM_CopyInitOutput( SrcInitOutputData%DWM, DstInitOutputData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyInitOutput( SrcInitOutputData%FVW, DstInitOutputData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN DstInitOutputData%AirDens = SrcInitOutputData%AirDens END SUBROUTINE AD14_CopyInitOutput @@ -11592,6 +9830,7 @@ SUBROUTINE AD14_DestroyInitOutput( InitOutputData, ErrStat, ErrMsg ) ErrMsg = "" CALL NWTC_Library_Destroyprogdesc( InitOutputData%Ver, ErrStat, ErrMsg ) CALL DWM_DestroyInitOutput( InitOutputData%DWM, ErrStat, ErrMsg ) + CALL FVW_DestroyInitOutput( InitOutputData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyInitOutput SUBROUTINE AD14_PackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -11664,6 +9903,23 @@ SUBROUTINE AD14_PackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackInitOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF Re_BufSz = Re_BufSz + 1 ! AirDens IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) @@ -11724,6 +9980,34 @@ SUBROUTINE AD14_PackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err 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 + CALL FVW_PackInitOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 @@ -11861,6 +10145,46 @@ SUBROUTINE AD14_UnPackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, 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) + 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 FVW_UnpackInitOutput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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) @@ -11885,6 +10209,9 @@ SUBROUTINE AD14_CopyContState( SrcContStateData, DstContStateData, CtrlCode, Err CALL DWM_CopyContState( SrcContStateData%DWM, DstContStateData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyContState( SrcContStateData%FVW, DstContStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyContState SUBROUTINE AD14_DestroyContState( ContStateData, ErrStat, ErrMsg ) @@ -11897,6 +10224,7 @@ SUBROUTINE AD14_DestroyContState( ContStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL DWM_DestroyContState( ContStateData%DWM, ErrStat, ErrMsg ) + CALL FVW_DestroyContState( ContStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyContState SUBROUTINE AD14_PackContState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -11944,11 +10272,28 @@ SUBROUTINE AD14_PackContState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Re_BufSz = Re_BufSz + SIZE( Re_Buf ) DEALLOCATE(Re_Buf) END IF - IF(ALLOCATED(Db_Buf)) THEN ! DWM + IF(ALLOCATED(Db_Buf)) THEN ! DWM + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! DWM + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackContState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW Db_BufSz = Db_BufSz + SIZE( Db_Buf ) DEALLOCATE(Db_Buf) END IF - IF(ALLOCATED(Int_Buf)) THEN ! DWM + IF(ALLOCATED(Int_Buf)) THEN ! FVW Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF @@ -12007,6 +10352,34 @@ SUBROUTINE AD14_PackContState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackContState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD14_PackContState SUBROUTINE AD14_UnPackContState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -12081,6 +10454,46 @@ SUBROUTINE AD14_UnPackContState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackContState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD14_UnPackContState SUBROUTINE AD14_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrStat, ErrMsg ) @@ -12100,6 +10513,9 @@ SUBROUTINE AD14_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, Err CALL DWM_CopyDiscState( SrcDiscStateData%DWM, DstDiscStateData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyDiscState( SrcDiscStateData%FVW, DstDiscStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyDiscState SUBROUTINE AD14_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) @@ -12112,6 +10528,7 @@ SUBROUTINE AD14_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL DWM_DestroyDiscState( DiscStateData%DWM, ErrStat, ErrMsg ) + CALL FVW_DestroyDiscState( DiscStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyDiscState SUBROUTINE AD14_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -12167,6 +10584,23 @@ SUBROUTINE AD14_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackDiscState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -12222,6 +10656,34 @@ SUBROUTINE AD14_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackDiscState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD14_PackDiscState SUBROUTINE AD14_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -12296,6 +10758,46 @@ SUBROUTINE AD14_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackDiscState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD14_UnPackDiscState SUBROUTINE AD14_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg ) @@ -12315,6 +10817,9 @@ SUBROUTINE AD14_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCod CALL DWM_CopyConstrState( SrcConstrStateData%DWM, DstConstrStateData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyConstrState( SrcConstrStateData%FVW, DstConstrStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyConstrState SUBROUTINE AD14_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) @@ -12327,6 +10832,7 @@ SUBROUTINE AD14_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL DWM_DestroyConstrState( ConstrStateData%DWM, ErrStat, ErrMsg ) + CALL FVW_DestroyConstrState( ConstrStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyConstrState SUBROUTINE AD14_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -12382,6 +10888,23 @@ SUBROUTINE AD14_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -12437,6 +10960,34 @@ SUBROUTINE AD14_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD14_PackConstrState SUBROUTINE AD14_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -12511,6 +11062,46 @@ SUBROUTINE AD14_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackConstrState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD14_UnPackConstrState SUBROUTINE AD14_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -12530,6 +11121,9 @@ SUBROUTINE AD14_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, CALL DWM_CopyOtherState( SrcOtherStateData%DWM, DstOtherStateData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyOtherState( SrcOtherStateData%FVW, DstOtherStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyOtherState SUBROUTINE AD14_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) @@ -12542,6 +11136,7 @@ SUBROUTINE AD14_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL DWM_DestroyOtherState( OtherStateData%DWM, ErrStat, ErrMsg ) + CALL FVW_DestroyOtherState( OtherStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyOtherState SUBROUTINE AD14_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -12597,6 +11192,23 @@ SUBROUTINE AD14_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -12652,6 +11264,34 @@ SUBROUTINE AD14_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD14_PackOtherState SUBROUTINE AD14_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -12726,6 +11366,46 @@ SUBROUTINE AD14_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackOtherState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD14_UnPackOtherState SUBROUTINE AD14_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) @@ -12754,6 +11434,9 @@ SUBROUTINE AD14_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) CALL DWM_CopyOutput( SrcMiscData%DWM_Outputs, DstMiscData%DWM_Outputs, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyMisc( SrcMiscData%FVW, DstMiscData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN DstMiscData%DT = SrcMiscData%DT IF (ALLOCATED(SrcMiscData%ElPrNum)) THEN i1_l = LBOUND(SrcMiscData%ElPrNum,1) @@ -12779,7 +11462,7 @@ SUBROUTINE AD14_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) DstMiscData%OnePassDynDbg = SrcMiscData%OnePassDynDbg DstMiscData%NoLoadsCalculated = SrcMiscData%NoLoadsCalculated DstMiscData%NERRORS = SrcMiscData%NERRORS - CALL AD14_Copyairfoil( SrcMiscData%AirFoil, DstMiscData%AirFoil, CtrlCode, ErrStat2, ErrMsg2 ) + CALL AD14AeroConf_CopyMisc( SrcMiscData%AirFoil, DstMiscData%AirFoil, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN CALL AD14_Copybeddoes( SrcMiscData%Beddoes, DstMiscData%Beddoes, CtrlCode, ErrStat2, ErrMsg2 ) @@ -12852,10 +11535,11 @@ SUBROUTINE AD14_DestroyMisc( MiscData, ErrStat, ErrMsg ) CALL DWM_DestroyMisc( MiscData%DWM, ErrStat, ErrMsg ) CALL DWM_DestroyInput( MiscData%DWM_Inputs, ErrStat, ErrMsg ) CALL DWM_DestroyOutput( MiscData%DWM_Outputs, ErrStat, ErrMsg ) + CALL FVW_DestroyMisc( MiscData%FVW, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%ElPrNum)) THEN DEALLOCATE(MiscData%ElPrNum) ENDIF - CALL AD14_Destroyairfoil( MiscData%AirFoil, ErrStat, ErrMsg ) + CALL AD14AeroConf_DestroyMisc( MiscData%AirFoil, ErrStat, ErrMsg ) CALL AD14_Destroybeddoes( MiscData%Beddoes, ErrStat, ErrMsg ) CALL AD14_Destroydyninflow( MiscData%DynInflow, ErrStat, ErrMsg ) CALL AD14_Destroyelement( MiscData%Element, ErrStat, ErrMsg ) @@ -12958,6 +11642,23 @@ SUBROUTINE AD14_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF Db_BufSz = Db_BufSz + 1 ! DT Int_BufSz = Int_BufSz + 1 ! ElPrNum allocated yes/no IF ( ALLOCATED(InData%ElPrNum) ) THEN @@ -12977,7 +11678,7 @@ SUBROUTINE AD14_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! NoLoadsCalculated Int_BufSz = Int_BufSz + 1 ! NERRORS Int_BufSz = Int_BufSz + 3 ! AirFoil: size of buffers for each call to pack subtype - CALL AD14_Packairfoil( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, .TRUE. ) ! AirFoil + CALL AD14AeroConf_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, .TRUE. ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -13212,6 +11913,34 @@ SUBROUTINE AD14_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S 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 + CALL FVW_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 @@ -13275,7 +12004,7 @@ SUBROUTINE AD14_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NERRORS Int_Xferred = Int_Xferred + 1 - CALL AD14_Packairfoil( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, OnlySize ) ! AirFoil + CALL AD14AeroConf_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, OnlySize ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -13697,6 +12426,46 @@ SUBROUTINE AD14_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg 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) + 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 FVW_UnpackMisc( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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) @@ -13782,7 +12551,7 @@ SUBROUTINE AD14_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL AD14_Unpackairfoil( Re_Buf, Db_Buf, Int_Buf, OutData%AirFoil, ErrStat2, ErrMsg2 ) ! AirFoil + CALL AD14AeroConf_UnpackMisc( Re_Buf, Db_Buf, Int_Buf, OutData%AirFoil, ErrStat2, ErrMsg2 ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -14156,6 +12925,7 @@ SUBROUTINE AD14_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%LinearizeFlag = SrcParamData%LinearizeFlag DstParamData%OutputPlottingInfo = SrcParamData%OutputPlottingInfo DstParamData%UseDWM = SrcParamData%UseDWM + DstParamData%UseFVW = SrcParamData%UseFVW DstParamData%TwoPiNB = SrcParamData%TwoPiNB DstParamData%NumBl = SrcParamData%NumBl DstParamData%NBlInpSt = SrcParamData%NBlInpSt @@ -14174,7 +12944,7 @@ SUBROUTINE AD14_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%MAXICOUNT = SrcParamData%MAXICOUNT DstParamData%WrOptFile = SrcParamData%WrOptFile DstParamData%DEFAULT_Wind = SrcParamData%DEFAULT_Wind - CALL AD14_Copyairfoilparms( SrcParamData%AirFoil, DstParamData%AirFoil, CtrlCode, ErrStat2, ErrMsg2 ) + CALL AD14AeroConf_CopyParam( SrcParamData%AirFoil, DstParamData%AirFoil, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN CALL AD14_Copybladeparms( SrcParamData%Blade, DstParamData%Blade, CtrlCode, ErrStat2, ErrMsg2 ) @@ -14204,6 +12974,10 @@ SUBROUTINE AD14_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg CALL DWM_CopyParam( SrcParamData%DWM, DstParamData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyParam( SrcParamData%FVW, DstParamData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + DstParamData%IfW_DT = SrcParamData%IfW_DT END SUBROUTINE AD14_CopyParam SUBROUTINE AD14_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -14215,7 +12989,7 @@ SUBROUTINE AD14_DestroyParam( ParamData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" - CALL AD14_Destroyairfoilparms( ParamData%AirFoil, ErrStat, ErrMsg ) + CALL AD14AeroConf_DestroyParam( ParamData%AirFoil, ErrStat, ErrMsg ) CALL AD14_Destroybladeparms( ParamData%Blade, ErrStat, ErrMsg ) CALL AD14_Destroybeddoesparms( ParamData%Beddoes, ErrStat, ErrMsg ) CALL AD14_Destroydyninflowparms( ParamData%DynInflow, ErrStat, ErrMsg ) @@ -14225,6 +12999,7 @@ SUBROUTINE AD14_DestroyParam( ParamData, ErrStat, ErrMsg ) CALL AD14_Destroywindparms( ParamData%Wind, ErrStat, ErrMsg ) CALL AD14_Destroyrotorparms( ParamData%Rotor, ErrStat, ErrMsg ) CALL DWM_DestroyParam( ParamData%DWM, ErrStat, ErrMsg ) + CALL FVW_DestroyParam( ParamData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyParam SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -14269,6 +13044,7 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + 1 ! LinearizeFlag Int_BufSz = Int_BufSz + 1 ! OutputPlottingInfo Int_BufSz = Int_BufSz + 1 ! UseDWM + Int_BufSz = Int_BufSz + 1 ! UseFVW Re_BufSz = Re_BufSz + 1 ! TwoPiNB Int_BufSz = Int_BufSz + 1 ! NumBl Int_BufSz = Int_BufSz + 1 ! NBlInpSt @@ -14289,7 +13065,7 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + 1 ! DEFAULT_Wind ! Allocate buffers for subtypes, if any (we'll get sizes from these) Int_BufSz = Int_BufSz + 3 ! AirFoil: size of buffers for each call to pack subtype - CALL AD14_Packairfoilparms( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, .TRUE. ) ! AirFoil + CALL AD14AeroConf_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, .TRUE. ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -14458,6 +13234,24 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Db_BufSz = Db_BufSz + 1 ! IfW_DT IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -14501,6 +13295,8 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UseDWM , IntKiBuf(1), 1) Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UseFVW , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TwoPiNB Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumBl @@ -14537,7 +13333,7 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%DEFAULT_Wind Int_Xferred = Int_Xferred + 1 - CALL AD14_Packairfoilparms( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, OnlySize ) ! AirFoil + CALL AD14AeroConf_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, OnlySize ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -14817,6 +13613,36 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%IfW_DT + Db_Xferred = Db_Xferred + 1 END SUBROUTINE AD14_PackParam SUBROUTINE AD14_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -14867,6 +13693,8 @@ SUBROUTINE AD14_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 OutData%UseDWM = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) Int_Xferred = Int_Xferred + 1 + OutData%UseFVW = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 OutData%TwoPiNB = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 OutData%NumBl = IntKiBuf( Int_Xferred ) @@ -14936,7 +13764,7 @@ SUBROUTINE AD14_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL AD14_Unpackairfoilparms( Re_Buf, Db_Buf, Int_Buf, OutData%AirFoil, ErrStat2, ErrMsg2 ) ! AirFoil + CALL AD14AeroConf_UnpackParam( Re_Buf, Db_Buf, Int_Buf, OutData%AirFoil, ErrStat2, ErrMsg2 ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -15303,6 +14131,48 @@ SUBROUTINE AD14_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackParam( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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) + OutData%IfW_DT = DbKiBuf( Db_Xferred ) + Db_Xferred = Db_Xferred + 1 END SUBROUTINE AD14_UnPackParam SUBROUTINE AD14_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) @@ -15340,7 +14210,7 @@ SUBROUTINE AD14_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg CALL MeshCopy( SrcInputData%Twr_InputMarkers, DstInputData%Twr_InputMarkers, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL AD14_Copyaeroconfig( SrcInputData%TurbineComponents, DstInputData%TurbineComponents, CtrlCode, ErrStat2, ErrMsg2 ) + CALL AD14AeroConf_CopyInput( SrcInputData%TurbineComponents, DstInputData%TurbineComponents, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN IF (ALLOCATED(SrcInputData%MulTabLoc)) THEN @@ -15372,6 +14242,9 @@ SUBROUTINE AD14_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg DstInputData%InflowVelocity = SrcInputData%InflowVelocity ENDIF DstInputData%AvgInfVel = SrcInputData%AvgInfVel + CALL FVW_CopyInput( SrcInputData%FVW, DstInputData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyInput SUBROUTINE AD14_DestroyInput( InputData, ErrStat, ErrMsg ) @@ -15390,13 +14263,14 @@ SUBROUTINE AD14_DestroyInput( InputData, ErrStat, ErrMsg ) DEALLOCATE(InputData%InputMarkers) ENDIF CALL MeshDestroy( InputData%Twr_InputMarkers, ErrStat, ErrMsg ) - CALL AD14_Destroyaeroconfig( InputData%TurbineComponents, ErrStat, ErrMsg ) + CALL AD14AeroConf_DestroyInput( InputData%TurbineComponents, ErrStat, ErrMsg ) IF (ALLOCATED(InputData%MulTabLoc)) THEN DEALLOCATE(InputData%MulTabLoc) ENDIF IF (ALLOCATED(InputData%InflowVelocity)) THEN DEALLOCATE(InputData%InflowVelocity) ENDIF + CALL FVW_DestroyInput( InputData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyInput SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -15476,7 +14350,7 @@ SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, DEALLOCATE(Int_Buf) END IF Int_BufSz = Int_BufSz + 3 ! TurbineComponents: size of buffers for each call to pack subtype - CALL AD14_Packaeroconfig( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, .TRUE. ) ! TurbineComponents + CALL AD14AeroConf_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, .TRUE. ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -15503,6 +14377,23 @@ SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Re_BufSz = Re_BufSz + SIZE(InData%InflowVelocity) ! InflowVelocity END IF Re_BufSz = Re_BufSz + SIZE(InData%AvgInfVel) ! AvgInfVel + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -15599,7 +14490,7 @@ SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - CALL AD14_Packaeroconfig( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, OnlySize ) ! TurbineComponents + CALL AD14AeroConf_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, OnlySize ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -15661,6 +14552,34 @@ SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, END IF ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%AvgInfVel))-1 ) = PACK(InData%AvgInfVel,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%AvgInfVel) + CALL FVW_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD14_PackInput SUBROUTINE AD14_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -15826,7 +14745,7 @@ SUBROUTINE AD14_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL AD14_Unpackaeroconfig( Re_Buf, Db_Buf, Int_Buf, OutData%TurbineComponents, ErrStat2, ErrMsg2 ) ! TurbineComponents + CALL AD14AeroConf_UnpackInput( Re_Buf, Db_Buf, Int_Buf, OutData%TurbineComponents, ErrStat2, ErrMsg2 ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -15896,6 +14815,46 @@ SUBROUTINE AD14_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs OutData%AvgInfVel = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%AvgInfVel))-1 ), mask1, 0.0_ReKi ) Re_Xferred = Re_Xferred + SIZE(OutData%AvgInfVel) DEALLOCATE(mask1) + 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 FVW_UnpackInput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD14_UnPackInput SUBROUTINE AD14_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -15932,6 +14891,9 @@ SUBROUTINE AD14_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, Err CALL MeshCopy( SrcOutputData%Twr_OutputLoads, DstOutputData%Twr_OutputLoads, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyOutput( SrcOutputData%FVW, DstOutputData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyOutput SUBROUTINE AD14_DestroyOutput( OutputData, ErrStat, ErrMsg ) @@ -15950,6 +14912,7 @@ SUBROUTINE AD14_DestroyOutput( OutputData, ErrStat, ErrMsg ) DEALLOCATE(OutputData%OutputLoads) ENDIF CALL MeshDestroy( OutputData%Twr_OutputLoads, ErrStat, ErrMsg ) + CALL FVW_DestroyOutput( OutputData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyOutput SUBROUTINE AD14_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -16028,6 +14991,23 @@ SUBROUTINE AD14_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -16124,6 +15104,34 @@ SUBROUTINE AD14_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD14_PackOutput SUBROUTINE AD14_UnPackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -16255,6 +15263,46 @@ SUBROUTINE AD14_UnPackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrM IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackOutput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD14_UnPackOutput @@ -16341,7 +15389,6 @@ SUBROUTINE AD14_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg INTEGER(IntKi) :: ErrStat2 ! local errors CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts - INTEGER :: i11 ! dim1 level 1 counter variable for arrays of ddts ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -16362,210 +15409,8 @@ SUBROUTINE AD14_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg END IF ! check if allocated CALL MeshExtrapInterp1(u1%Twr_InputMarkers, u2%Twr_InputMarkers, tin, u_out%Twr_InputMarkers, tin_out, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) -IF (ALLOCATED(u_out%TurbineComponents%Blade) .AND. ALLOCATED(u1%TurbineComponents%Blade)) THEN - DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%Position,1))) - b1 = -(u1%TurbineComponents%Blade(i11)%Position - u2%TurbineComponents%Blade(i11)%Position)/t(2) - u_out%TurbineComponents%Blade(i11)%Position = u1%TurbineComponents%Blade(i11)%Position + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ENDDO - DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,1),SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,1),SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,2) )) - b2 = -(u1%TurbineComponents%Blade(i11)%Orientation - u2%TurbineComponents%Blade(i11)%Orientation)/t(2) - u_out%TurbineComponents%Blade(i11)%Orientation = u1%TurbineComponents%Blade(i11)%Orientation + b2 * t_out - DEALLOCATE(b2) - DEALLOCATE(c2) - ENDDO - DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%TranslationVel,1))) - b1 = -(u1%TurbineComponents%Blade(i11)%TranslationVel - u2%TurbineComponents%Blade(i11)%TranslationVel)/t(2) - u_out%TurbineComponents%Blade(i11)%TranslationVel = u1%TurbineComponents%Blade(i11)%TranslationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ENDDO - DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%RotationVel,1))) - b1 = -(u1%TurbineComponents%Blade(i11)%RotationVel - u2%TurbineComponents%Blade(i11)%RotationVel)/t(2) - u_out%TurbineComponents%Blade(i11)%RotationVel = u1%TurbineComponents%Blade(i11)%RotationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ENDDO -END IF ! check if allocated - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%Position,1))) - b1 = -(u1%TurbineComponents%Hub%Position - u2%TurbineComponents%Hub%Position)/t(2) - u_out%TurbineComponents%Hub%Position = u1%TurbineComponents%Hub%Position + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Hub%Orientation,1),SIZE(u_out%TurbineComponents%Hub%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Hub%Orientation,1),SIZE(u_out%TurbineComponents%Hub%Orientation,2) )) - b2 = -(u1%TurbineComponents%Hub%Orientation - u2%TurbineComponents%Hub%Orientation)/t(2) - u_out%TurbineComponents%Hub%Orientation = u1%TurbineComponents%Hub%Orientation + b2 * t_out - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%TranslationVel,1))) - b1 = -(u1%TurbineComponents%Hub%TranslationVel - u2%TurbineComponents%Hub%TranslationVel)/t(2) - u_out%TurbineComponents%Hub%TranslationVel = u1%TurbineComponents%Hub%TranslationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%RotationVel,1))) - b1 = -(u1%TurbineComponents%Hub%RotationVel - u2%TurbineComponents%Hub%RotationVel)/t(2) - u_out%TurbineComponents%Hub%RotationVel = u1%TurbineComponents%Hub%RotationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%Position,1))) - b1 = -(u1%TurbineComponents%RotorFurl%Position - u2%TurbineComponents%RotorFurl%Position)/t(2) - u_out%TurbineComponents%RotorFurl%Position = u1%TurbineComponents%RotorFurl%Position + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%RotorFurl%Orientation,1),SIZE(u_out%TurbineComponents%RotorFurl%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%RotorFurl%Orientation,1),SIZE(u_out%TurbineComponents%RotorFurl%Orientation,2) )) - b2 = -(u1%TurbineComponents%RotorFurl%Orientation - u2%TurbineComponents%RotorFurl%Orientation)/t(2) - u_out%TurbineComponents%RotorFurl%Orientation = u1%TurbineComponents%RotorFurl%Orientation + b2 * t_out - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%TranslationVel,1))) - b1 = -(u1%TurbineComponents%RotorFurl%TranslationVel - u2%TurbineComponents%RotorFurl%TranslationVel)/t(2) - u_out%TurbineComponents%RotorFurl%TranslationVel = u1%TurbineComponents%RotorFurl%TranslationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%RotationVel,1))) - b1 = -(u1%TurbineComponents%RotorFurl%RotationVel - u2%TurbineComponents%RotorFurl%RotationVel)/t(2) - u_out%TurbineComponents%RotorFurl%RotationVel = u1%TurbineComponents%RotorFurl%RotationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%Position,1))) - b1 = -(u1%TurbineComponents%Nacelle%Position - u2%TurbineComponents%Nacelle%Position)/t(2) - u_out%TurbineComponents%Nacelle%Position = u1%TurbineComponents%Nacelle%Position + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Nacelle%Orientation,1),SIZE(u_out%TurbineComponents%Nacelle%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Nacelle%Orientation,1),SIZE(u_out%TurbineComponents%Nacelle%Orientation,2) )) - b2 = -(u1%TurbineComponents%Nacelle%Orientation - u2%TurbineComponents%Nacelle%Orientation)/t(2) - u_out%TurbineComponents%Nacelle%Orientation = u1%TurbineComponents%Nacelle%Orientation + b2 * t_out - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%TranslationVel,1))) - b1 = -(u1%TurbineComponents%Nacelle%TranslationVel - u2%TurbineComponents%Nacelle%TranslationVel)/t(2) - u_out%TurbineComponents%Nacelle%TranslationVel = u1%TurbineComponents%Nacelle%TranslationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%RotationVel,1))) - b1 = -(u1%TurbineComponents%Nacelle%RotationVel - u2%TurbineComponents%Nacelle%RotationVel)/t(2) - u_out%TurbineComponents%Nacelle%RotationVel = u1%TurbineComponents%Nacelle%RotationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%Position,1))) - b1 = -(u1%TurbineComponents%TailFin%Position - u2%TurbineComponents%TailFin%Position)/t(2) - u_out%TurbineComponents%TailFin%Position = u1%TurbineComponents%TailFin%Position + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%TailFin%Orientation,1),SIZE(u_out%TurbineComponents%TailFin%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%TailFin%Orientation,1),SIZE(u_out%TurbineComponents%TailFin%Orientation,2) )) - b2 = -(u1%TurbineComponents%TailFin%Orientation - u2%TurbineComponents%TailFin%Orientation)/t(2) - u_out%TurbineComponents%TailFin%Orientation = u1%TurbineComponents%TailFin%Orientation + b2 * t_out - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%TranslationVel,1))) - b1 = -(u1%TurbineComponents%TailFin%TranslationVel - u2%TurbineComponents%TailFin%TranslationVel)/t(2) - u_out%TurbineComponents%TailFin%TranslationVel = u1%TurbineComponents%TailFin%TranslationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%RotationVel,1))) - b1 = -(u1%TurbineComponents%TailFin%RotationVel - u2%TurbineComponents%TailFin%RotationVel)/t(2) - u_out%TurbineComponents%TailFin%RotationVel = u1%TurbineComponents%TailFin%RotationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%Position,1))) - b1 = -(u1%TurbineComponents%Tower%Position - u2%TurbineComponents%Tower%Position)/t(2) - u_out%TurbineComponents%Tower%Position = u1%TurbineComponents%Tower%Position + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Tower%Orientation,1),SIZE(u_out%TurbineComponents%Tower%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Tower%Orientation,1),SIZE(u_out%TurbineComponents%Tower%Orientation,2) )) - b2 = -(u1%TurbineComponents%Tower%Orientation - u2%TurbineComponents%Tower%Orientation)/t(2) - u_out%TurbineComponents%Tower%Orientation = u1%TurbineComponents%Tower%Orientation + b2 * t_out - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%TranslationVel,1))) - b1 = -(u1%TurbineComponents%Tower%TranslationVel - u2%TurbineComponents%Tower%TranslationVel)/t(2) - u_out%TurbineComponents%Tower%TranslationVel = u1%TurbineComponents%Tower%TranslationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%RotationVel,1))) - b1 = -(u1%TurbineComponents%Tower%RotationVel - u2%TurbineComponents%Tower%RotationVel)/t(2) - u_out%TurbineComponents%Tower%RotationVel = u1%TurbineComponents%Tower%RotationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%Position,1))) - b1 = -(u1%TurbineComponents%SubStructure%Position - u2%TurbineComponents%SubStructure%Position)/t(2) - u_out%TurbineComponents%SubStructure%Position = u1%TurbineComponents%SubStructure%Position + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%SubStructure%Orientation,1),SIZE(u_out%TurbineComponents%SubStructure%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%SubStructure%Orientation,1),SIZE(u_out%TurbineComponents%SubStructure%Orientation,2) )) - b2 = -(u1%TurbineComponents%SubStructure%Orientation - u2%TurbineComponents%SubStructure%Orientation)/t(2) - u_out%TurbineComponents%SubStructure%Orientation = u1%TurbineComponents%SubStructure%Orientation + b2 * t_out - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%TranslationVel,1))) - b1 = -(u1%TurbineComponents%SubStructure%TranslationVel - u2%TurbineComponents%SubStructure%TranslationVel)/t(2) - u_out%TurbineComponents%SubStructure%TranslationVel = u1%TurbineComponents%SubStructure%TranslationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%RotationVel,1))) - b1 = -(u1%TurbineComponents%SubStructure%RotationVel - u2%TurbineComponents%SubStructure%RotationVel)/t(2) - u_out%TurbineComponents%SubStructure%RotationVel = u1%TurbineComponents%SubStructure%RotationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%Position,1))) - b1 = -(u1%TurbineComponents%Foundation%Position - u2%TurbineComponents%Foundation%Position)/t(2) - u_out%TurbineComponents%Foundation%Position = u1%TurbineComponents%Foundation%Position + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Foundation%Orientation,1),SIZE(u_out%TurbineComponents%Foundation%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Foundation%Orientation,1),SIZE(u_out%TurbineComponents%Foundation%Orientation,2) )) - b2 = -(u1%TurbineComponents%Foundation%Orientation - u2%TurbineComponents%Foundation%Orientation)/t(2) - u_out%TurbineComponents%Foundation%Orientation = u1%TurbineComponents%Foundation%Orientation + b2 * t_out - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%TranslationVel,1))) - b1 = -(u1%TurbineComponents%Foundation%TranslationVel - u2%TurbineComponents%Foundation%TranslationVel)/t(2) - u_out%TurbineComponents%Foundation%TranslationVel = u1%TurbineComponents%Foundation%TranslationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%RotationVel,1))) - b1 = -(u1%TurbineComponents%Foundation%RotationVel - u2%TurbineComponents%Foundation%RotationVel)/t(2) - u_out%TurbineComponents%Foundation%RotationVel = u1%TurbineComponents%Foundation%RotationVel + b1 * t_out - DEALLOCATE(b1) - DEALLOCATE(c1) - b0 = -(u1%TurbineComponents%BladeLength - u2%TurbineComponents%BladeLength)/t(2) - u_out%TurbineComponents%BladeLength = u1%TurbineComponents%BladeLength + b0 * t_out + CALL AD14AeroConf_Input_ExtrapInterp1( u1%TurbineComponents, u2%TurbineComponents, tin, u_out%TurbineComponents, tin_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ALLOCATED(u_out%MulTabLoc) .AND. ALLOCATED(u1%MulTabLoc)) THEN ALLOCATE(b2(SIZE(u_out%MulTabLoc,1),SIZE(u_out%MulTabLoc,2) )) ALLOCATE(c2(SIZE(u_out%MulTabLoc,1),SIZE(u_out%MulTabLoc,2) )) @@ -16588,6 +15433,8 @@ SUBROUTINE AD14_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg u_out%AvgInfVel = u1%AvgInfVel + b1 * t_out DEALLOCATE(b1) DEALLOCATE(c1) + CALL FVW_Input_ExtrapInterp1( u1%FVW, u2%FVW, tin, u_out%FVW, tin_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) END SUBROUTINE AD14_Input_ExtrapInterp1 @@ -16627,7 +15474,6 @@ SUBROUTINE AD14_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, Er CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors CHARACTER(*), PARAMETER :: RoutineName = 'AD14_Input_ExtrapInterp2' INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts - INTEGER :: i11 ! dim1 level 1 counter variable for arrays of ddts ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -16654,243 +15500,8 @@ SUBROUTINE AD14_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, Er END IF ! check if allocated CALL MeshExtrapInterp2(u1%Twr_InputMarkers, u2%Twr_InputMarkers, u3%Twr_InputMarkers, tin, u_out%Twr_InputMarkers, tin_out, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) -IF (ALLOCATED(u_out%TurbineComponents%Blade) .AND. ALLOCATED(u1%TurbineComponents%Blade)) THEN - DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%Position,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Blade(i11)%Position - u2%TurbineComponents%Blade(i11)%Position) + t(2)**2*(-u1%TurbineComponents%Blade(i11)%Position + u3%TurbineComponents%Blade(i11)%Position))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Blade(i11)%Position + t(3)*u2%TurbineComponents%Blade(i11)%Position - t(2)*u3%TurbineComponents%Blade(i11)%Position ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Blade(i11)%Position = u1%TurbineComponents%Blade(i11)%Position + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ENDDO - DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,1),SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,1),SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,2) )) - b2 = (t(3)**2*(u1%TurbineComponents%Blade(i11)%Orientation - u2%TurbineComponents%Blade(i11)%Orientation) + t(2)**2*(-u1%TurbineComponents%Blade(i11)%Orientation + u3%TurbineComponents%Blade(i11)%Orientation))/(t(2)*t(3)*(t(2) - t(3))) - c2 = ( (t(2)-t(3))*u1%TurbineComponents%Blade(i11)%Orientation + t(3)*u2%TurbineComponents%Blade(i11)%Orientation - t(2)*u3%TurbineComponents%Blade(i11)%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Blade(i11)%Orientation = u1%TurbineComponents%Blade(i11)%Orientation + b2 * t_out + c2 * t_out**2 - DEALLOCATE(b2) - DEALLOCATE(c2) - ENDDO - DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%TranslationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Blade(i11)%TranslationVel - u2%TurbineComponents%Blade(i11)%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Blade(i11)%TranslationVel + u3%TurbineComponents%Blade(i11)%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Blade(i11)%TranslationVel + t(3)*u2%TurbineComponents%Blade(i11)%TranslationVel - t(2)*u3%TurbineComponents%Blade(i11)%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Blade(i11)%TranslationVel = u1%TurbineComponents%Blade(i11)%TranslationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ENDDO - DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%RotationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Blade(i11)%RotationVel - u2%TurbineComponents%Blade(i11)%RotationVel) + t(2)**2*(-u1%TurbineComponents%Blade(i11)%RotationVel + u3%TurbineComponents%Blade(i11)%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Blade(i11)%RotationVel + t(3)*u2%TurbineComponents%Blade(i11)%RotationVel - t(2)*u3%TurbineComponents%Blade(i11)%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Blade(i11)%RotationVel = u1%TurbineComponents%Blade(i11)%RotationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ENDDO -END IF ! check if allocated - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%Position,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Hub%Position - u2%TurbineComponents%Hub%Position) + t(2)**2*(-u1%TurbineComponents%Hub%Position + u3%TurbineComponents%Hub%Position))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Hub%Position + t(3)*u2%TurbineComponents%Hub%Position - t(2)*u3%TurbineComponents%Hub%Position ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Hub%Position = u1%TurbineComponents%Hub%Position + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Hub%Orientation,1),SIZE(u_out%TurbineComponents%Hub%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Hub%Orientation,1),SIZE(u_out%TurbineComponents%Hub%Orientation,2) )) - b2 = (t(3)**2*(u1%TurbineComponents%Hub%Orientation - u2%TurbineComponents%Hub%Orientation) + t(2)**2*(-u1%TurbineComponents%Hub%Orientation + u3%TurbineComponents%Hub%Orientation))/(t(2)*t(3)*(t(2) - t(3))) - c2 = ( (t(2)-t(3))*u1%TurbineComponents%Hub%Orientation + t(3)*u2%TurbineComponents%Hub%Orientation - t(2)*u3%TurbineComponents%Hub%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Hub%Orientation = u1%TurbineComponents%Hub%Orientation + b2 * t_out + c2 * t_out**2 - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%TranslationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Hub%TranslationVel - u2%TurbineComponents%Hub%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Hub%TranslationVel + u3%TurbineComponents%Hub%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Hub%TranslationVel + t(3)*u2%TurbineComponents%Hub%TranslationVel - t(2)*u3%TurbineComponents%Hub%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Hub%TranslationVel = u1%TurbineComponents%Hub%TranslationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%RotationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Hub%RotationVel - u2%TurbineComponents%Hub%RotationVel) + t(2)**2*(-u1%TurbineComponents%Hub%RotationVel + u3%TurbineComponents%Hub%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Hub%RotationVel + t(3)*u2%TurbineComponents%Hub%RotationVel - t(2)*u3%TurbineComponents%Hub%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Hub%RotationVel = u1%TurbineComponents%Hub%RotationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%Position,1))) - b1 = (t(3)**2*(u1%TurbineComponents%RotorFurl%Position - u2%TurbineComponents%RotorFurl%Position) + t(2)**2*(-u1%TurbineComponents%RotorFurl%Position + u3%TurbineComponents%RotorFurl%Position))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%RotorFurl%Position + t(3)*u2%TurbineComponents%RotorFurl%Position - t(2)*u3%TurbineComponents%RotorFurl%Position ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%RotorFurl%Position = u1%TurbineComponents%RotorFurl%Position + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%RotorFurl%Orientation,1),SIZE(u_out%TurbineComponents%RotorFurl%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%RotorFurl%Orientation,1),SIZE(u_out%TurbineComponents%RotorFurl%Orientation,2) )) - b2 = (t(3)**2*(u1%TurbineComponents%RotorFurl%Orientation - u2%TurbineComponents%RotorFurl%Orientation) + t(2)**2*(-u1%TurbineComponents%RotorFurl%Orientation + u3%TurbineComponents%RotorFurl%Orientation))/(t(2)*t(3)*(t(2) - t(3))) - c2 = ( (t(2)-t(3))*u1%TurbineComponents%RotorFurl%Orientation + t(3)*u2%TurbineComponents%RotorFurl%Orientation - t(2)*u3%TurbineComponents%RotorFurl%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%RotorFurl%Orientation = u1%TurbineComponents%RotorFurl%Orientation + b2 * t_out + c2 * t_out**2 - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%TranslationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%RotorFurl%TranslationVel - u2%TurbineComponents%RotorFurl%TranslationVel) + t(2)**2*(-u1%TurbineComponents%RotorFurl%TranslationVel + u3%TurbineComponents%RotorFurl%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%RotorFurl%TranslationVel + t(3)*u2%TurbineComponents%RotorFurl%TranslationVel - t(2)*u3%TurbineComponents%RotorFurl%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%RotorFurl%TranslationVel = u1%TurbineComponents%RotorFurl%TranslationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%RotationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%RotorFurl%RotationVel - u2%TurbineComponents%RotorFurl%RotationVel) + t(2)**2*(-u1%TurbineComponents%RotorFurl%RotationVel + u3%TurbineComponents%RotorFurl%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%RotorFurl%RotationVel + t(3)*u2%TurbineComponents%RotorFurl%RotationVel - t(2)*u3%TurbineComponents%RotorFurl%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%RotorFurl%RotationVel = u1%TurbineComponents%RotorFurl%RotationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%Position,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Nacelle%Position - u2%TurbineComponents%Nacelle%Position) + t(2)**2*(-u1%TurbineComponents%Nacelle%Position + u3%TurbineComponents%Nacelle%Position))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Nacelle%Position + t(3)*u2%TurbineComponents%Nacelle%Position - t(2)*u3%TurbineComponents%Nacelle%Position ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Nacelle%Position = u1%TurbineComponents%Nacelle%Position + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Nacelle%Orientation,1),SIZE(u_out%TurbineComponents%Nacelle%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Nacelle%Orientation,1),SIZE(u_out%TurbineComponents%Nacelle%Orientation,2) )) - b2 = (t(3)**2*(u1%TurbineComponents%Nacelle%Orientation - u2%TurbineComponents%Nacelle%Orientation) + t(2)**2*(-u1%TurbineComponents%Nacelle%Orientation + u3%TurbineComponents%Nacelle%Orientation))/(t(2)*t(3)*(t(2) - t(3))) - c2 = ( (t(2)-t(3))*u1%TurbineComponents%Nacelle%Orientation + t(3)*u2%TurbineComponents%Nacelle%Orientation - t(2)*u3%TurbineComponents%Nacelle%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Nacelle%Orientation = u1%TurbineComponents%Nacelle%Orientation + b2 * t_out + c2 * t_out**2 - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%TranslationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Nacelle%TranslationVel - u2%TurbineComponents%Nacelle%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Nacelle%TranslationVel + u3%TurbineComponents%Nacelle%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Nacelle%TranslationVel + t(3)*u2%TurbineComponents%Nacelle%TranslationVel - t(2)*u3%TurbineComponents%Nacelle%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Nacelle%TranslationVel = u1%TurbineComponents%Nacelle%TranslationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%RotationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Nacelle%RotationVel - u2%TurbineComponents%Nacelle%RotationVel) + t(2)**2*(-u1%TurbineComponents%Nacelle%RotationVel + u3%TurbineComponents%Nacelle%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Nacelle%RotationVel + t(3)*u2%TurbineComponents%Nacelle%RotationVel - t(2)*u3%TurbineComponents%Nacelle%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Nacelle%RotationVel = u1%TurbineComponents%Nacelle%RotationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%Position,1))) - b1 = (t(3)**2*(u1%TurbineComponents%TailFin%Position - u2%TurbineComponents%TailFin%Position) + t(2)**2*(-u1%TurbineComponents%TailFin%Position + u3%TurbineComponents%TailFin%Position))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%TailFin%Position + t(3)*u2%TurbineComponents%TailFin%Position - t(2)*u3%TurbineComponents%TailFin%Position ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%TailFin%Position = u1%TurbineComponents%TailFin%Position + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%TailFin%Orientation,1),SIZE(u_out%TurbineComponents%TailFin%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%TailFin%Orientation,1),SIZE(u_out%TurbineComponents%TailFin%Orientation,2) )) - b2 = (t(3)**2*(u1%TurbineComponents%TailFin%Orientation - u2%TurbineComponents%TailFin%Orientation) + t(2)**2*(-u1%TurbineComponents%TailFin%Orientation + u3%TurbineComponents%TailFin%Orientation))/(t(2)*t(3)*(t(2) - t(3))) - c2 = ( (t(2)-t(3))*u1%TurbineComponents%TailFin%Orientation + t(3)*u2%TurbineComponents%TailFin%Orientation - t(2)*u3%TurbineComponents%TailFin%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%TailFin%Orientation = u1%TurbineComponents%TailFin%Orientation + b2 * t_out + c2 * t_out**2 - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%TranslationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%TailFin%TranslationVel - u2%TurbineComponents%TailFin%TranslationVel) + t(2)**2*(-u1%TurbineComponents%TailFin%TranslationVel + u3%TurbineComponents%TailFin%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%TailFin%TranslationVel + t(3)*u2%TurbineComponents%TailFin%TranslationVel - t(2)*u3%TurbineComponents%TailFin%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%TailFin%TranslationVel = u1%TurbineComponents%TailFin%TranslationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%RotationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%TailFin%RotationVel - u2%TurbineComponents%TailFin%RotationVel) + t(2)**2*(-u1%TurbineComponents%TailFin%RotationVel + u3%TurbineComponents%TailFin%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%TailFin%RotationVel + t(3)*u2%TurbineComponents%TailFin%RotationVel - t(2)*u3%TurbineComponents%TailFin%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%TailFin%RotationVel = u1%TurbineComponents%TailFin%RotationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%Position,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Tower%Position - u2%TurbineComponents%Tower%Position) + t(2)**2*(-u1%TurbineComponents%Tower%Position + u3%TurbineComponents%Tower%Position))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Tower%Position + t(3)*u2%TurbineComponents%Tower%Position - t(2)*u3%TurbineComponents%Tower%Position ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Tower%Position = u1%TurbineComponents%Tower%Position + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Tower%Orientation,1),SIZE(u_out%TurbineComponents%Tower%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Tower%Orientation,1),SIZE(u_out%TurbineComponents%Tower%Orientation,2) )) - b2 = (t(3)**2*(u1%TurbineComponents%Tower%Orientation - u2%TurbineComponents%Tower%Orientation) + t(2)**2*(-u1%TurbineComponents%Tower%Orientation + u3%TurbineComponents%Tower%Orientation))/(t(2)*t(3)*(t(2) - t(3))) - c2 = ( (t(2)-t(3))*u1%TurbineComponents%Tower%Orientation + t(3)*u2%TurbineComponents%Tower%Orientation - t(2)*u3%TurbineComponents%Tower%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Tower%Orientation = u1%TurbineComponents%Tower%Orientation + b2 * t_out + c2 * t_out**2 - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%TranslationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Tower%TranslationVel - u2%TurbineComponents%Tower%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Tower%TranslationVel + u3%TurbineComponents%Tower%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Tower%TranslationVel + t(3)*u2%TurbineComponents%Tower%TranslationVel - t(2)*u3%TurbineComponents%Tower%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Tower%TranslationVel = u1%TurbineComponents%Tower%TranslationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%RotationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Tower%RotationVel - u2%TurbineComponents%Tower%RotationVel) + t(2)**2*(-u1%TurbineComponents%Tower%RotationVel + u3%TurbineComponents%Tower%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Tower%RotationVel + t(3)*u2%TurbineComponents%Tower%RotationVel - t(2)*u3%TurbineComponents%Tower%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Tower%RotationVel = u1%TurbineComponents%Tower%RotationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%Position,1))) - b1 = (t(3)**2*(u1%TurbineComponents%SubStructure%Position - u2%TurbineComponents%SubStructure%Position) + t(2)**2*(-u1%TurbineComponents%SubStructure%Position + u3%TurbineComponents%SubStructure%Position))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%SubStructure%Position + t(3)*u2%TurbineComponents%SubStructure%Position - t(2)*u3%TurbineComponents%SubStructure%Position ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%SubStructure%Position = u1%TurbineComponents%SubStructure%Position + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%SubStructure%Orientation,1),SIZE(u_out%TurbineComponents%SubStructure%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%SubStructure%Orientation,1),SIZE(u_out%TurbineComponents%SubStructure%Orientation,2) )) - b2 = (t(3)**2*(u1%TurbineComponents%SubStructure%Orientation - u2%TurbineComponents%SubStructure%Orientation) + t(2)**2*(-u1%TurbineComponents%SubStructure%Orientation + u3%TurbineComponents%SubStructure%Orientation))/(t(2)*t(3)*(t(2) - t(3))) - c2 = ( (t(2)-t(3))*u1%TurbineComponents%SubStructure%Orientation + t(3)*u2%TurbineComponents%SubStructure%Orientation - t(2)*u3%TurbineComponents%SubStructure%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%SubStructure%Orientation = u1%TurbineComponents%SubStructure%Orientation + b2 * t_out + c2 * t_out**2 - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%TranslationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%SubStructure%TranslationVel - u2%TurbineComponents%SubStructure%TranslationVel) + t(2)**2*(-u1%TurbineComponents%SubStructure%TranslationVel + u3%TurbineComponents%SubStructure%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%SubStructure%TranslationVel + t(3)*u2%TurbineComponents%SubStructure%TranslationVel - t(2)*u3%TurbineComponents%SubStructure%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%SubStructure%TranslationVel = u1%TurbineComponents%SubStructure%TranslationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%RotationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%SubStructure%RotationVel - u2%TurbineComponents%SubStructure%RotationVel) + t(2)**2*(-u1%TurbineComponents%SubStructure%RotationVel + u3%TurbineComponents%SubStructure%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%SubStructure%RotationVel + t(3)*u2%TurbineComponents%SubStructure%RotationVel - t(2)*u3%TurbineComponents%SubStructure%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%SubStructure%RotationVel = u1%TurbineComponents%SubStructure%RotationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%Position,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%Position,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Foundation%Position - u2%TurbineComponents%Foundation%Position) + t(2)**2*(-u1%TurbineComponents%Foundation%Position + u3%TurbineComponents%Foundation%Position))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Foundation%Position + t(3)*u2%TurbineComponents%Foundation%Position - t(2)*u3%TurbineComponents%Foundation%Position ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Foundation%Position = u1%TurbineComponents%Foundation%Position + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b2(SIZE(u_out%TurbineComponents%Foundation%Orientation,1),SIZE(u_out%TurbineComponents%Foundation%Orientation,2) )) - ALLOCATE(c2(SIZE(u_out%TurbineComponents%Foundation%Orientation,1),SIZE(u_out%TurbineComponents%Foundation%Orientation,2) )) - b2 = (t(3)**2*(u1%TurbineComponents%Foundation%Orientation - u2%TurbineComponents%Foundation%Orientation) + t(2)**2*(-u1%TurbineComponents%Foundation%Orientation + u3%TurbineComponents%Foundation%Orientation))/(t(2)*t(3)*(t(2) - t(3))) - c2 = ( (t(2)-t(3))*u1%TurbineComponents%Foundation%Orientation + t(3)*u2%TurbineComponents%Foundation%Orientation - t(2)*u3%TurbineComponents%Foundation%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Foundation%Orientation = u1%TurbineComponents%Foundation%Orientation + b2 * t_out + c2 * t_out**2 - DEALLOCATE(b2) - DEALLOCATE(c2) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%TranslationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%TranslationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Foundation%TranslationVel - u2%TurbineComponents%Foundation%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Foundation%TranslationVel + u3%TurbineComponents%Foundation%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Foundation%TranslationVel + t(3)*u2%TurbineComponents%Foundation%TranslationVel - t(2)*u3%TurbineComponents%Foundation%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Foundation%TranslationVel = u1%TurbineComponents%Foundation%TranslationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%RotationVel,1))) - ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%RotationVel,1))) - b1 = (t(3)**2*(u1%TurbineComponents%Foundation%RotationVel - u2%TurbineComponents%Foundation%RotationVel) + t(2)**2*(-u1%TurbineComponents%Foundation%RotationVel + u3%TurbineComponents%Foundation%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) - c1 = ( (t(2)-t(3))*u1%TurbineComponents%Foundation%RotationVel + t(3)*u2%TurbineComponents%Foundation%RotationVel - t(2)*u3%TurbineComponents%Foundation%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%Foundation%RotationVel = u1%TurbineComponents%Foundation%RotationVel + b1 * t_out + c1 * t_out**2 - DEALLOCATE(b1) - DEALLOCATE(c1) - b0 = (t(3)**2*(u1%TurbineComponents%BladeLength - u2%TurbineComponents%BladeLength) + t(2)**2*(-u1%TurbineComponents%BladeLength + u3%TurbineComponents%BladeLength))/(t(2)*t(3)*(t(2) - t(3))) - c0 = ( (t(2)-t(3))*u1%TurbineComponents%BladeLength + t(3)*u2%TurbineComponents%BladeLength - t(2)*u3%TurbineComponents%BladeLength ) / (t(2)*t(3)*(t(2) - t(3))) - u_out%TurbineComponents%BladeLength = u1%TurbineComponents%BladeLength + b0 * t_out + c0 * t_out**2 + CALL AD14AeroConf_Input_ExtrapInterp2( u1%TurbineComponents, u2%TurbineComponents, u3%TurbineComponents, tin, u_out%TurbineComponents, tin_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ALLOCATED(u_out%MulTabLoc) .AND. ALLOCATED(u1%MulTabLoc)) THEN ALLOCATE(b2(SIZE(u_out%MulTabLoc,1),SIZE(u_out%MulTabLoc,2) )) ALLOCATE(c2(SIZE(u_out%MulTabLoc,1),SIZE(u_out%MulTabLoc,2) )) @@ -16916,6 +15527,8 @@ SUBROUTINE AD14_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, Er u_out%AvgInfVel = u1%AvgInfVel + b1 * t_out + c1 * t_out**2 DEALLOCATE(b1) DEALLOCATE(c1) + CALL FVW_Input_ExtrapInterp2( u1%FVW, u2%FVW, u3%FVW, tin, u_out%FVW, tin_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) END SUBROUTINE AD14_Input_ExtrapInterp2 @@ -17018,6 +15631,8 @@ SUBROUTINE AD14_Output_ExtrapInterp1(y1, y2, tin, y_out, tin_out, ErrStat, ErrMs END IF ! check if allocated CALL MeshExtrapInterp1(y1%Twr_OutputLoads, y2%Twr_OutputLoads, tin, y_out%Twr_OutputLoads, tin_out, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL FVW_Output_ExtrapInterp1( y1%FVW, y2%FVW, tin, y_out%FVW, tin_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) END SUBROUTINE AD14_Output_ExtrapInterp1 @@ -17079,6 +15694,8 @@ SUBROUTINE AD14_Output_ExtrapInterp2(y1, y2, y3, tin, y_out, tin_out, ErrStat, E END IF ! check if allocated CALL MeshExtrapInterp2(y1%Twr_OutputLoads, y2%Twr_OutputLoads, y3%Twr_OutputLoads, tin, y_out%Twr_OutputLoads, tin_out, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL FVW_Output_ExtrapInterp2( y1%FVW, y2%FVW, y3%FVW, tin, y_out%FVW, tin_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) END SUBROUTINE AD14_Output_ExtrapInterp2 END MODULE AeroDyn14_Types diff --git a/modules/aerodyn14/src/AeroSubs.f90 b/modules/aerodyn14/src/AeroSubs.f90 index c6141806c5..dd85d87e9f 100644 --- a/modules/aerodyn14/src/AeroSubs.f90 +++ b/modules/aerodyn14/src/AeroSubs.f90 @@ -385,6 +385,22 @@ SUBROUTINE AD14_GetInput(InitInp, P, x, xd, z, m, y, ErrStat, ErrMess ) END IF + ! --- Free vortex wake inputs + CALL ReadVar( UnIn, InitInp%ADFileName, InitInp%UseFVW, 'UseFVW', 'Use free vortex wake', ErrStat,ErrMess) + IF (ErrStat >= AbortErrLev) THEN + CLOSE(UnIn) + RETURN + END IF + CALL ReadVar( UnIn, InitInp%ADFileName, InitInp%FVWFileName, 'FVWFileName', 'Input file name for free vortex wake', ErrStat,ErrMess) + IF (ErrStat >= AbortErrLev) THEN + CLOSE(UnIn) + RETURN + END IF + if (InitInp%UseFVW) then + print*,'>>> Using FVW',trim(InitInp%FVWFileName) + else + print*,'>>> Using BEM' + endif ! Read in the air density CALL ReadVar( UnIn, InitInp%ADFileName, P%Wind%Rho, VarName='Rho', VarDescr='Air density', ErrStat=ErrStat, ErrMsg=ErrMess) @@ -1619,18 +1635,13 @@ SUBROUTINE READTwr(UnIn, InitInp, P, x, xd, z, m, y, ErrStat, ErrMess ) END SUBROUTINE READTwr -! Dynamics Program aerodynamics force interface gateway - ! **************************************************** - SUBROUTINE ELEMFRC(P, m, ErrStat, ErrMess, & - PSI, RLOCAL, J, IBlade, VNROTOR2, VT, VNW, & - VNB, DFN, DFT, PMA, Initial) -! SUBROUTINE ELEMFRC (PSI, RLOCAL, J, IBlade, VNROTOR2, VT, VNW, & -! VNB, DFN, DFT, PMA, Initial) - ! **************************************************** - ! calculates the aerodynamic forces on one - ! blade element. Inputs include all velocities. - ! Normal and tangential forces and 'A' are returned. !==================================================================================================== +!> Calculates the axial and tangential induction factor for each annular segment +! and time step (i.e. sets m%Element%A and m%Element%AP) + SUBROUTINE ELEM_INDUCTIONS( p, m, ErrStat, ErrMess, & + PSI, RLOCAL, J, IBlade, VNROTOR2, VT, VNW, & + VNB, Initial) + IMPLICIT NONE ! Passed Variables: TYPE(AD14_ParameterType), INTENT(IN) :: p ! Parameters @@ -1638,102 +1649,97 @@ SUBROUTINE ELEMFRC(P, m, ErrStat, ErrMess, & INTEGER, INTENT(OUT) :: ErrStat CHARACTER(*), INTENT(OUT) :: ErrMess - REAL(ReKi),INTENT(OUT) :: DFN - REAL(ReKi),INTENT(OUT) :: DFT - REAL(ReKi),INTENT(OUT) :: PMA REAL(ReKi),INTENT(IN) :: PSI REAL(ReKi),INTENT(IN) :: RLOCAL - REAL(ReKi),INTENT(IN) :: VNB + REAL(ReKi), INTENT(IN ) :: VNB ! Normal (relative) velocity of the element REAL(ReKi),INTENT(IN) :: VNROTOR2 REAL(ReKi),INTENT(IN) :: VNW - REAL(ReKi),INTENT(INOUT) :: VT + REAL(ReKi), INTENT(IN ) :: VT INTEGER, INTENT(IN) :: J INTEGER, INTENT(IN) :: IBlade LOGICAL, INTENT(IN) :: Initial - ! Local Variables: - - REAL(ReKi) :: CDA - REAL(ReKi) :: CLA - REAL(ReKi) :: CMA - REAL(ReKi) :: CPHI - REAL(ReKi) :: PHI - REAL(ReKi) :: QA - REAL(ReKi) :: ReNum - REAL(ReKi) :: SPHI - REAL(ReKi) :: Vinduced - REAL(ReKi) :: VN - INTEGER :: ErrStatLcL ! Error status returned by called routines. CHARACTER(ErrMsgLen) :: ErrMessLcl ! Error message returned by called routines. ErrStat = ErrID_None ErrMess = "" - - ! initialize TanInd variables -m%Element%A (J,IBLADE) = 0.0 -m%Element%AP(J,IBLADE) = 0.0 - - !-mlb Check for being at the center of rotation. ! If we are at the center of rotation, the induction equations ! are undefined, so let's just USE zeros. - -IF ( RLOCAL < 0.01 ) THEN +! initialize AxInd and TanInd variables m%Element%A (J,IBLADE) = 0.0 m%Element%AP(J,IBLADE) = 0.0 + +IF ( RLOCAL < 0.01 ) THEN + ! Already set to 0 ELSEIF( P%DYNINFL .AND. P%Blade%R * m%Rotor%REVS < 2.0 ) THEN !ACH 3/10/03 This block deals with dyn. inflow problems at low tip speed - m%Element%A (J,IBLADE) = 0.0 - m%Element%AP(J,IBLADE) = 0.0 + ! Already set to 0 m%DYNINIT = .TRUE. !Re-initialize if we begin using dynamic inflow again ELSE ! Turn wake off when using dynamic inflow and tip speed goes low. Wake will remain off. - ! Get induction factor = A using static airfoil coefficients IF ( P%WAKE .AND. .NOT. Initial) THEN IF ( P%DYNINFL ) THEN ! USE dynamic inflow model to find A CALL VINDINF( P, m, ErrStatLcl, ErrMessLcl, & - J, IBlade, RLOCAL, VNW, VNB, VT, PSI ) !possibly changes VT, A, and AP - CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'ELEMFRC' ) + J, IBlade, RLOCAL, VNW, VNB, VT, PSI ) !possibly changes A, and AP + CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'ELEM_INDUCTIONS' ) IF (ErrStat >= AbortErrLev) RETURN ELSE ! USE momentum balance to find A CALL VIND( P, m, ErrStatLcl, ErrMessLcl, & - J, IBlade, RLOCAL, VNROTOR2, VNW, VNB, VT ) !changes VT, A, and AP - CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'ELEMFRC' ) + J, IBlade, RLOCAL, VNROTOR2, VNW, VNB, VT ) !changes A, and AP + CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'ELEM_INDUCTIONS' ) IF (ErrStat >= AbortErrLev) RETURN ! Apply skewed-wake correction, if applicable IF( m%SKEW ) CALL VNMOD( P, m, ErrStatLcl, ErrMessLcl,& J, IBlade, RLOCAL, PSI ) !changes A - CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'ELEMFRC' ) + CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'ELEM_INDUCTIONS' ) IF (ErrStat >= AbortErrLev) RETURN ENDIF - ELSE - ! Ignore the wake calculation entirely - m%Element%A (J,IBLADE) = 0.0 - m%Element%AP(J,IBLADE) = 0.0 ENDIF - ENDIF -Vinduced = VNW * m%Element%A(J,IBLADE) -VN = VNW + VNB - Vinduced +END SUBROUTINE ELEM_INDUCTIONS -m%InducedVel%SumInfl = m%InducedVel%SumInfl + Vinduced * RLOCAL * p%Blade%DR(J) +SUBROUTINE ELEMFRC2( p, m, ErrStat, ErrMess, J, IBlade, & + DFN, DFT, PMA, Initial, phi ) + + IMPLICIT NONE + ! Passed Variables: + TYPE(AD14_ParameterType), INTENT(IN) :: p ! Parameters + TYPE(AD14_MiscVarType), INTENT(INOUT) :: m ! Misc/optimization variables + INTEGER, INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMess + + REAL(ReKi), INTENT( OUT) :: DFN + REAL(ReKi), INTENT( OUT) :: DFT + REAL(ReKi), INTENT( OUT) :: PMA + INTEGER, INTENT(IN) :: J + INTEGER, INTENT(IN) :: IBlade + LOGICAL, INTENT(IN) :: Initial - ! Get the angle of attack + ! Local Variables: -PHI = ATAN2( VN, VT ) -m%Element%ALPHA(J,IBlade) = PHI - m%Element%PITNOW + REAL(ReKi) :: CDA + REAL(ReKi) :: CLA + REAL(ReKi) :: CMA + REAL(ReKi) :: CPHI + REAL(ReKi), intent(in) :: PHI + REAL(ReKi) :: QA + REAL(ReKi) :: ReNum + REAL(ReKi) :: SPHI -CALL MPI2PI ( m%Element%ALPHA(J,IBlade) ) + INTEGER :: ErrStatLcL ! Error status returned by called routines. + CHARACTER(ErrMsgLen) :: ErrMessLcl ! Error message returned by called routines. -m%Element%W2(J,IBlade) = VN * VN + VT * VT + ErrStat = ErrID_None + ErrMess = "" ! Get the Reynold's number for the element ! Returns Reynold's number x 10^6 !bjj: Reynold's number x 10^-6 ? @@ -1799,22 +1805,22 @@ SUBROUTINE ELEMFRC(P, m, ErrStat, ErrMess, & m%ElOut%DFNSAV ( m%ElOut%ElPrList(J) ) = DFN m%ElOut%DFTSAV ( m%ElOut%ElPrList(J) ) = DFT m%ElOut%DynPres( m%ElOut%ElPrList(J) ) = 0.5 * P%Wind%RHO * m%Element%W2(J,IBlade) - m%ElOut%PITSAV ( m%ElOut%ElPrList(J) ) = m%Element%PITNOW * R2D + m%ElOut%PITSAV ( m%ElOut%ElPrList(J) ) = m%Element%PitNow(J,IBlade) * R2D m%ElOut%PMM ( m%ElOut%ElPrList(J) ) = PMA m%ElOut%ReyNum ( m%ElOut%ElPrList(J) ) = ReNum + m%ElOut%Gamma ( m%ElOut%ElPrList(J) ) = 0.5 * P%Blade%C(J) * sqrt(m%Element%W2(J,IBlade)) * CLA ! 1/2 c Urel Cl [m^2/s] ENDIF ENDIF RETURN -END SUBROUTINE ELEMFRC +END SUBROUTINE ELEMFRC2 !====================================================== - SUBROUTINE VIND( P, m, ErrStat, ErrMess, & + SUBROUTINE VIND( p, m, ErrStat, ErrMess, & J, IBlade, RLOCAL, VNROTOR2, VNW, VNB, VT ) -! SUBROUTINE VIND( J, IBlade, RLOCAL, VNROTOR2, VNW, VNB, VT ) - ! calculates the axial induction factor for each - ! annular segment and time step. + ! Calculates the axial and tangential induction factor for each annular segment + ! and time step (i.e. sets m%Element%A and m%Element%AP) ! *************************************************** IMPLICIT NONE ! Passed Variables: @@ -1825,7 +1831,7 @@ SUBROUTINE VIND( P, m, ErrStat, ErrMess, & REAL(ReKi), INTENT(IN ) :: VNB REAL(ReKi), INTENT(IN ) :: VNROTOR2 REAL(ReKi), INTENT(IN ) :: VNW - REAL(ReKi), INTENT(INOUT) :: VT + REAL(ReKi), INTENT(IN ) :: VT ! tangential velocity from relative blade motion and wind, no induction INTEGER, INTENT(IN ) :: J INTEGER, INTENT(IN ) :: IBlade @@ -1912,12 +1918,12 @@ SUBROUTINE VIND( P, m, ErrStat, ErrMess, & IF ( ABS( VNB ) > 100. ) THEN m%Element%A( J, IBLADE ) = 0.0 - CALL VINDERR( P, m, ErrStat, ErrMess, & + CALL VINDERR( m, ErrStat, ErrMess, & VNW, VNB, 'VNB', J, IBLADE ) RETURN ELSEIF ( ABS( VT ) > 400. ) THEN m%Element%A( J, IBLADE ) = 0.0 - CALL VINDERR( P, m, ErrStat, ErrMess, & + CALL VINDERR( m, ErrStat, ErrMess, & VNW, VT, 'VT', J, IBLADE ) RETURN ENDIF @@ -1926,7 +1932,7 @@ SUBROUTINE VIND( P, m, ErrStat, ErrMess, & CALL AXIND ( P, m, ErrStat, ErrMess, & VNW, VNB, VNA, VTA, VT, VT2_Inv, VNROTOR2, A2, A2P, & - J, SOLFACT, ALPHA, PHI, CLA, CDA, CMA, RLOCAL ) + J, IBlade, SOLFACT, ALPHA, PHI, CLA, CDA, CMA, RLOCAL ) IF (ErrStat >= AbortErrLev) RETURN DAI = A2 - AI @@ -1946,7 +1952,7 @@ SUBROUTINE VIND( P, m, ErrStat, ErrMess, & CALL AXIND ( P, m, ErrStatLcl, ErrMessLcl, & VNW, VNB, VNA, VTA, VT, VT2_Inv, VNROTOR2, A2, A2P, & - J, SOLFACT, ALPHA, PHI, CLA, CDA, CMA, RLOCAL ) + J, IBlade, SOLFACT, ALPHA, PHI, CLA, CDA, CMA, RLOCAL ) CALL SetErrStat(ErrStatLcl,ErrMessLcl,ErrStat,ErrMess,'VIND' ) IF (ErrStat >= AbortErrLev) RETURN @@ -1979,7 +1985,6 @@ SUBROUTINE VIND( P, m, ErrStat, ErrMess, & ! Passed test, we're done m%Element%A (J,IBLADE) = A2 m%Element%AP(J,IBLADE) = A2P -VT = VT * ( 1. + A2P ) !bjj: why are we changing the total velocity? m%Element%OLD_A_NS (J,IBLADE) = A2 m%Element%OLD_AP_NS (J,IBLADE) = A2P @@ -1988,7 +1993,7 @@ END SUBROUTINE VIND ! *************************************************** - SUBROUTINE VINDERR( P, m, ErrStat, ErrMess, & + SUBROUTINE VINDERR( m, ErrStat, ErrMess, & VNW, VX, VID, J, IBLADE ) ! SUBROUTINE VINDERR( VNW, VX, VID, J, IBLADE ) ! used to write warning messages to the screen @@ -1996,7 +2001,6 @@ SUBROUTINE VINDERR( P, m, ErrStat, ErrMess, & ! *************************************************** IMPLICIT NONE ! Passed Variables: - TYPE(AD14_ParameterType), INTENT(IN) :: p ! Parameters TYPE(AD14_MiscVarType), INTENT(INOUT) :: m ! Misc/optimization variables INTEGER, INTENT(OUT) :: ErrStat CHARACTER(*), INTENT(OUT) :: ErrMess @@ -2036,7 +2040,7 @@ END SUBROUTINE VINDERR ! ****************************************************** SUBROUTINE AXIND (P, m, ErrStat, ErrMess, & VNW, VNB, VNA, VTA, VT, VT2_Inv, VNROTOR2, A2, & - A2P, J, SOLFACT, ALPHA, PHI, CLA, CDA, CMA, RLOCAL ) + A2P, J, IBlade, SOLFACT, ALPHA, PHI, CLA, CDA, CMA, RLOCAL ) ! SUBROUTINE AXIND ( VNW, VNB, VNA, VTA, VT, VT2_Inv, VNROTOR2, A2, & ! A2P, J, SOLFACT, ALPHA, PHI, CLA, CDA, CMA, RLOCAL ) ! calculates a new axial induction factor from @@ -2068,6 +2072,7 @@ SUBROUTINE AXIND (P, m, ErrStat, ErrMess, & REAL(ReKi),INTENT(OUT) :: VTA INTEGER ,INTENT(IN) :: J + INTEGER ,INTENT(IN) :: IBlade ! Local Variables: @@ -2087,7 +2092,7 @@ SUBROUTINE AXIND (P, m, ErrStat, ErrMess, & ! Get airfoil CL and CD PHI = ATAN2( VNA, VTA ) -ALPHA = PHI - m%Element%PITNOW +ALPHA = PHI - m%Element%PitNow(J,IBlade) CALL MPI2PI ( ALPHA ) @@ -4114,16 +4119,16 @@ SUBROUTINE GetRM ( P, m, ErrStat, ErrMess, & !+++++++++++++++++++++++++++++++++++++++++++++++++++++ !Suzuki's method !DO mode = MaxInflo+1, maxInfl -! m%DynInflow%RMC_SAVE(IBLADE, J, mode) = fElem * XPHI( Rzero, mode ) * COS( REAL(MRvector(mode)) * psiBar ) -! m%DynInflow%RMS_SAVE(IBLADE, J, mode) = fElem * XPHI( Rzero, mode ) * SIN( REAL(MRvector(mode)) * psiBar ) +! m%DynInflow%RMC_SAVE(IBLADE, J, mode) = fElem * XPHI( Rzero, mode ) * COS( REAL(MRvector(mode), ReKi) * psiBar ) +! m%DynInflow%RMS_SAVE(IBLADE, J, mode) = fElem * XPHI( Rzero, mode ) * SIN( REAL(MRvector(mode), ReKi) * psiBar ) !END DO ! mode ! Shawler's method DO mode = p%DynInflow%MaxInflo+1, maxInfl - m%DynInflow%RMC_SAVE(IBLADE, J, mode) = fElem * XPHI( Rzero, mode, ErrStatLcl, ErrMessLcl ) * COS( REAL(MRvector(mode)) * WindPsi ) + m%DynInflow%RMC_SAVE(IBLADE, J, mode) = fElem * XPHI( Rzero, mode, ErrStatLcl, ErrMessLcl ) * COS( REAL(MRvector(mode), ReKi) * WindPsi ) CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'GetRM' ) IF (ErrStat >= AbortErrLev) RETURN - m%DynInflow%RMS_SAVE(IBLADE, J, mode) = fElem * XPHI( Rzero, mode, ErrStatLcl, ErrMessLcl ) * SIN( REAL(MRvector(mode)) * WindPsi ) + m%DynInflow%RMS_SAVE(IBLADE, J, mode) = fElem * XPHI( Rzero, mode, ErrStatLcl, ErrMessLcl ) * SIN( REAL(MRvector(mode), ReKi) * WindPsi ) CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'GetRM' ) IF (ErrStat >= AbortErrLev) RETURN @@ -4738,10 +4743,11 @@ SUBROUTINE infdist( P, m, ErrStat, ErrMess ) END SUBROUTINE infdist ! ************************************************************* - SUBROUTINE vindinf( P, m, ErrStat, ErrMess, & + SUBROUTINE VINDINF( P, m, ErrStat, ErrMess, & iradius, iblade, rlocal, vnw, VNB, VT, psi ) - ! vindinf calculates the axial induction factor for each - ! element position using the calculated inflow parameters. + ! Calculates the axial and tangential induction factor for each annular segment + ! and time step (i.e. sets m%Element%A and m%Element%AP) + ! Uses the calculated inflow parameters ! Called by ElemFrc for each element at a new time step. ! ************************************************************* @@ -4757,7 +4763,7 @@ SUBROUTINE vindinf( P, m, ErrStat, ErrMess, & REAL(ReKi),INTENT(IN) :: rlocal REAL(ReKi),INTENT(IN) :: VNB REAL(ReKi),INTENT(IN) :: vnw - REAL(ReKi),INTENT(INOUT) :: VT + REAL(ReKi),INTENT(IN ) :: VT ! Tangential velocity from relative blade motion and wind, no induction INTEGER, INTENT(IN) :: iradius INTEGER, INTENT(IN) :: iblade @@ -4801,7 +4807,7 @@ SUBROUTINE vindinf( P, m, ErrStat, ErrMess, & DO mode = 1, p%DynInflow%MaxInflo m%Element%A(iRadius,iBlade) = m%Element%A(iRadius,iBlade) & + xphi(Rzero,mode,ErrStatLcl, ErrMessLcl) * m%DynInflow%xAlpha(mode) - CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'vindinf' ) + CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'VINDINF' ) IF (ErrStat >= AbortErrLev) RETURN ! & + phis(Rzero, MRvector(mode), NJvector(mode) )* xAlpha(mode) @@ -4812,15 +4818,15 @@ SUBROUTINE vindinf( P, m, ErrStat, ErrMess, & !DO mode = MaxInflo+1, maxInfl ! A(iRadius,iBlade) = A(iRadius,iBlade) + xphi(Rzero,mode) * & !! & + phis(Rzero, MRvector(mode), NJvector(mode) ) * -! ( xAlpha(mode) * COS( REAL(MRvector(MODE)) * psibar ) & -! + xBeta (mode) * SIN( REAL(MRvector(MODE)) * psibar ) ) +! ( xAlpha(mode) * COS( REAL(MRvector(MODE), ReKi) * psibar ) & +! + xBeta (mode) * SIN( REAL(MRvector(MODE), ReKi) * psibar ) ) !END DO !mode ! Shawler: DO mode = p%DynInflow%MaxInflo+1, maxInfl m%Element%A(iRadius,iBlade) = m%Element%A(iRadius,iBlade) + xphi(Rzero,mode,ErrStatLcl, ErrMessLcl) * & - ( m%DynInflow%xAlpha(mode) * COS( REAL(MRvector(MODE)) * Windpsi ) & - + m%DynInflow%xBeta (mode) * SIN( REAL(MRvector(MODE)) * Windpsi ) ) - CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'vindinf' ) + ( m%DynInflow%xAlpha(mode) * COS( REAL(MRvector(MODE), ReKi) * Windpsi ) & + + m%DynInflow%xBeta (mode) * SIN( REAL(MRvector(MODE), ReKi) * Windpsi ) ) + CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,'VINDINF' ) IF (ErrStat >= AbortErrLev) RETURN END DO !mode @@ -4832,13 +4838,14 @@ SUBROUTINE vindinf( P, m, ErrStat, ErrMess, & ! Calculate induced swirl (a') if desired. +m%Element%AP(iRadius,iBlade) = 0.0_ReKi ! Default value + IF ( P%SWIRL ) THEN ! akihiro 10/26/99 SWRLARG = 1.0 + 4.0 * m%Element%A(iradius,iblade) * VNW * & ( (1.0 - m%Element%A(iradius,iblade)) * VNW + VNB ) / VT / VT IF ( SWRLARG > 0.0 ) THEN A2P = 0.5 * ( -1.0 + SQRT( SWRLARG ) ) - VT = VT * ( 1.0 + A2P) ! bjj: this value was not properly set before. We could also just replace the local A2P variable with AP() instead. m%Element%AP(iRadius,iBlade) = A2P ENDIF @@ -4846,7 +4853,7 @@ SUBROUTINE vindinf( P, m, ErrStat, ErrMess, & ENDIF RETURN -END SUBROUTINE vindinf +END SUBROUTINE VINDINF ! *********************************************************************** SUBROUTINE ABPRECOR( F, OLDF, DFDT, DT, N, N0 ) @@ -5190,14 +5197,14 @@ FUNCTION FGAMMA( R, J, M, N ) IF ( MOD(R+M,2) == 0 ) THEN FGAMMA = (-1)**((N+J-2*R)*.5) * 2. & - * SQRT( REAL( (2*N+1) * (2*J+1) ) ) & + * SQRT( REAL( (2*N+1) * (2*J+1), ReKi ) ) & / SQRT( HFUNC(M,N) * HFUNC(R,J) ) & - / REAL( (J+N) * (J+N+2) * ((J-N)*(J-N)-1) ) + / REAL( (J+N) * (J+N+2) * ((J-N)*(J-N)-1), ReKi ) ELSE IF ( ABS(J-N) == 1 ) THEN !bjj: why don't we use the pi() variable? or PibyTwo - FGAMMA = 3.14159265 * SIGN(1., REAL(R-M) ) * .5 & + FGAMMA = 3.14159265 * SIGN(1., REAL(R-M, ReKi) ) * .5 & / SQRT( HFUNC(M,N) * HFUNC(R,J) ) & - / SQRT( REAL( (2*N+1) * (2*J+1) ) ) + / SQRT( REAL( (2*N+1) * (2*J+1) , ReKi) ) ELSE FGAMMA = 0. @@ -5245,8 +5252,8 @@ FUNCTION HFUNC( M, N ) NPM = N + M NMM = N - M -HFUNC = ( REAL( IDUBFACT(NPM-1) ) / REAL( IDUBFACT(NPM) ) ) & - * ( REAL( IDUBFACT(NMM-1) ) / REAL( IDUBFACT(NMM) ) ) +HFUNC = ( REAL( IDUBFACT(NPM-1), ReKi ) / REAL( IDUBFACT(NPM), ReKi ) ) & + * ( REAL( IDUBFACT(NMM-1), ReKi ) / REAL( IDUBFACT(NMM), ReKi ) ) @@ -5390,11 +5397,11 @@ FUNCTION phis( Rzero, r, j ) DO q = r, j-1, 2 phis = phis & - + Rzero ** q * (-1.) **((q-r)/2) * REAL( idubfact(j+q) ) & - / REAL( idubfact(q-r) * idubfact(q+r) * idubfact(j-q-1) ) + + Rzero ** q * (-1.) **((q-r)/2) * REAL( idubfact(j+q), ReKi ) & + / REAL( idubfact(q-r) * idubfact(q+r) * idubfact(j-q-1), ReKi ) END DO !q -phis = phis * SQRT( REAL( 2*j+1 ) * hfunc(r,j) ) +phis = phis * SQRT( REAL( 2*j+1, ReKi ) * hfunc(r,j) ) RETURN diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 new file mode 100644 index 0000000000..75f00307b8 --- /dev/null +++ b/modules/aerodyn14/src/FVW.f90 @@ -0,0 +1,568 @@ +!> +!! +!! Abbreviations: +!! - FVW: Free Vortex Wake +!! - LL : Lifting Line +!! - CP : Control point +!! - NW : Near Wake +!! - FW : Far Wake +!! +MODULE FVW + USE NWTC_Library + USE FVW_Types + USE FVW_Subs + use FVW_IO + use FVW_Wings + + ! NOTE: this is a rough format that AD14 stores airfoil info. This will need + ! to be replaced by the AirFoilInfo module when we couple FVW into AD15 + USE AD14AeroConf_Types + + + IMPLICIT NONE + + PRIVATE + + type(ProgDesc), PARAMETER :: FVW_Ver = ProgDesc( 'FVW', '', '' ) + + PUBLIC :: FVW_Init ! Initialization routine + PUBLIC :: FVW_End + + PUBLIC :: FVW_CalcOutput + PUBLIC :: FVW_UpdateStates + + +CONTAINS + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine is called at the start of the simulation to perform initialization steps. +!! The parameters are set here and not changed during the simulation. +!! The initial states and initial guess for the input are defined. +subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat, ErrMsg ) +!.................................................................................................................................. + type(FVW_InitInputType), intent(inout) :: InitInp !< Input data for initialization routine (inout so we can use MOVE_ALLOC) + type(FVW_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined + type(FVW_ParameterType), intent( out) :: p !< Parameters + type(FVW_ContinuousStateType), intent( out) :: x !< Initial continuous states + type(FVW_DiscreteStateType), intent( out) :: xd !< Initial discrete states + type(FVW_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states + type(FVW_OtherStateType), intent( out) :: OtherState !< Initial other states + type(FVW_OutputType), intent( out) :: y !< Initial system outputs (outputs are not calculated; + !! only the output mesh is initialized) + type(FVW_MiscVarType), intent( out) :: m !< Initial misc/optimization variables + real(DbKi), intent(inout) :: interval !< Coupling interval in seconds: the rate that + !! (1) FVW_UpdateStates() is called in loose coupling & + !! (2) FVW_UpdateDiscState() is called in tight coupling. + !! Input is the suggested time from the glue code; + !! Output is the actual coupling interval that will be used + !! by the glue code. + type(FVW_InitOutputType), intent( out) :: InitOut !< Output for initialization routine + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + integer(IntKi) :: UnEcho ! Unit number for the echo file + character(*), parameter :: RoutineName = 'FVW_Init' + type(FVW_InputFile) :: InputFileData !< Data stored in the module's input file + + ! Initialize variables for this routine + ErrStat = ErrID_None + ErrMsg = "" + UnEcho = -1 + + ! Initialize the NWTC Subroutine Library + call NWTC_Init( EchoLibVer=.FALSE. ) + + ! Display the module information + call DispNVD( FVW_Ver ) + + ! Set Parameters and *Misc* from inputs + CALL FVW_SetParametersFromInputs(InitInp, p, m, ErrStat2, ErrMsg2); if(Failed()) return + + ! Read and parse the input file here to get other parameters and info + CALL FVW_ReadInputFile(InitInp%FVWFileName, p, InputFileData, ErrStat2, ErrMsg2); if(Failed()) return + + ! Initialize Misc Vars (may depend on input file) + p%nNWMax=100 ! TODO + p%nFWMax=100 ! TODO + CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + + ! Preliminary meshing of the wings (may depend on input file) + ! NOTE: the mesh is not located at the right position yet, the first call to calcoutput will redo some meshing + CALL Wings_Panelling_Init(InitInp%WingsMesh, InitInp%RElm, InitInp%chord, p, m, ErrStat2, ErrMsg2); if(Failed()) return + + ! Set parameters from InputFileData (need Misc allocated) + CALL FVW_SetParametersFromInputFile(InputFileData, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL FVW_ToString(p, m) ! Print to screen + + ! Initialize States Vars + CALL FVW_InitStates( x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + + ! Initialize Constraints Vars + CALL FVW_InitConstraint( z, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + + ! Panelling wings based on initial input mesh provided + ! NOTE: the mesh is not located at the right position yet, the first call to calcoutput will redo some meshing + CALL Wings_Panelling (InitInp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return + + ! Returned guessed locations where wind will be required + CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + ! Return anything in FVW_InitOutput that should be passed back to the calling code here + +CONTAINS + + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_Init') + Failed = ErrStat >= AbortErrLev + !if (Failed) call CleanUp() + end function Failed + +end subroutine FVW_Init + +! ============================================================================== +subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + character(*), parameter :: RoutineName = 'FVW_InitMiscVars' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + m%FirstCall = .True. + m%nNW = 0 ! Number of active nearwake panels + m%nFW = 0 ! Number of active farwake panels + + call AllocAry( m%LE , 3 , p%nSpan+1 , p%nWings, 'Leading Edge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%LE = -999999_ReKi; + call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%TE = -999999_ReKi; + call AllocAry( m%s_LL , p%nSpan+1 , p%nWings, 'Spanwise coord LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%s_LL= -999999_ReKi; + call AllocAry( m%chord_LL , p%nSpan+1 , p%nWings, 'Chord on LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%chord_LL= -999999_ReKi; + ! Variables at control points/elements + call AllocAry( m%Gamma_LL, p%nSpan , p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitConstraint' ); m%Gamma_LL = -999999_ReKi; + call AllocAry( m%chord_CP_LL , p%nSpan , p%nWings, 'Chord on CP LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%chord_CP_LL= -999999_ReKi; + call AllocAry( m%s_CP_LL , p%nSpan , p%nWings, 'Spanwise coord CPll', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%s_CP_LL= -999999_ReKi; + call AllocAry( m%CP_LL , 3 , p%nSpan , p%nWings, 'Control points LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%CP_LL= -999999_ReKi; + call AllocAry( m%Tang , 3 , p%nSpan , p%nWings, 'Tangential vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Tang= -999999_ReKi; + call AllocAry( m%Norm , 3 , p%nSpan , p%nWings, 'Normal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Norm= -999999_ReKi; + call AllocAry( m%Orth , 3 , p%nSpan , p%nWings, 'Orthogonal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Orth= -999999_ReKi; + call AllocAry( m%Vind_LL , 3 , p%nSpan , p%nWings, 'Vind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_LL= -999999_ReKi; + call AllocAry( m%Vtot_LL , 3 , p%nSpan , p%nWings, 'Vtot on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vtot_LL= -999999_ReKi; + call AllocAry( m%Vstr_LL , 3 , p%nSpan , p%nWings, 'Vstr on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vstr_LL= -999999_ReKi; + call AllocAry( m%Vwnd_LL , 3 , p%nSpan , p%nWings, 'Wind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_LL= -999999_ReKi; + ! Variables at panels points + call AllocAry( m%Vwnd_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_NW= -999_ReKi; + call AllocAry( m%Vwnd_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; + +end subroutine FVW_InitMiscVars +! ============================================================================== +subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) + type(FVW_ContinuousStateType), intent( out) :: x !< States + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + character(*), parameter :: RoutineName = 'FVW_InitMiscVars' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + call AllocAry( x%Gamma_NW, p%nSpan , p%nNWMax , p%nWings, 'NW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_NW = -999999_ReKi; + call AllocAry( x%Gamma_FW, 1 , p%nFWMax , p%nWings, 'FW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_FW = -999999_ReKi; + call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_NW = -999999_ReKi; + call AllocAry( x%r_FW , 3, 2 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = -999999_ReKi; + + + if (ErrStat >= AbortErrLev) return +end subroutine FVW_InitStates +! ============================================================================== +subroutine FVW_InitConstraint( z, p, m, ErrStat, ErrMsg ) + type(FVW_ConstraintStateType), intent( out) :: z !< Constraints + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + character(*), parameter :: RoutineName = 'FVW_InitMiscVars' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! + call AllocAry( z%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitConstraint' ); z%Gamma_LL = -999999_ReKi; + + if (ErrStat >= AbortErrLev) return +end subroutine FVW_InitConstraint + + +! ============================================================================== +!> Setting parameters *and misc* from module inputs +SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) + type(FVW_InitInputType), intent(inout) :: InitInp !< Input data for initialization routine (inout so we can use MOVE_ALLOC) + type(FVW_ParameterType), intent(inout) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Misc + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer(IntKi) :: iW ! Index on wings + integer(IntKi) :: iSpan + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'FVW_SetParametersFromInputs' + ErrStat = ErrID_None + ErrMsg = "" + ! + p%nWings = InitInp%NumBl + ! TODO TODO TODO Hack for AD14 mesh that is wrong + p%nWings = 1 + + ! NOTE: temporary limitation, all wings have the same nspan + p%nSpan = size(InitInp%RElm)-1 + +end subroutine FVW_SetParametersFromInputs +! ============================================================================== +!> +SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg ) + type(FVW_InputFile), intent(in ) :: InputFileData !< Data stored in the module's input file + type(FVW_ParameterType), intent(inout) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Misc + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + ErrStat = ErrID_None + ErrMsg = "" + + ! Set parameters from input file + p%IntMethod = InputFileData%IntMethod + p%CirculationMethod = InputFileData%CirculationMethod + p%FreeWake = InputFileData%FreeWake + + if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) + if (InputFileData%CirculationMethod==idCircPrescribed) then + call AllocAry( p%PrescribedCirculation, p%nSpan, 'Prescribed Circulation', ErrStat2, ErrMsg2 ); call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_SetParameters' ); p%PrescribedCirculation = -999999_ReKi; + if (.not. allocated(m%s_CP_LL)) then + print*,'Spanwise coordinate not allocated, TODO' + STOP + endif + call ReadAndInterpGamma(trim(InputFileData%CirculationFile), m%s_CP_LL(1:p%nSpan,1), m%s_LL(p%nSpan+1,1), p%PrescribedCirculation) + endif + +end subroutine FVW_SetParametersFromInputFile + +subroutine FVW_ToString(p,m) + type(FVW_ParameterType), intent(in) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Misc + print*,'------------------------------------' +end subroutine FVW_ToString + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine is called at the end of the simulation. +subroutine FVW_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) + + type(FVW_InputType), intent(inout) :: u !< System inputs + type(FVW_ParameterType), intent(inout) :: p !< Parameters + type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states + type(FVW_DiscreteStateType), intent(inout) :: xd !< Discrete states + type(FVW_ConstraintStateType), intent(inout) :: z !< Constraint states + type(FVW_OtherStateType), intent(inout) :: OtherState !< Other states + type(FVW_OutputType), intent(inout) :: y !< System outputs + type(FVW_MiscVarType), intent(inout) :: m !< Misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! Place any last minute operations or calculations here: + ! Close files here: + ! Destroy the input data: + call FVW_DestroyInput( u, ErrStat, ErrMsg ) + + ! Destroy the parameter data: + call FVW_DestroyParam( p, ErrStat, ErrMsg ) + + ! Destroy the state data: + call FVW_DestroyContState( x, ErrStat, ErrMsg ) + call FVW_DestroyDiscState( xd, ErrStat, ErrMsg ) + call FVW_DestroyConstrState( z, ErrStat, ErrMsg ) + call FVW_DestroyOtherState( OtherState, ErrStat, ErrMsg ) + call FVW_DestroyMisc( m, ErrStat, ErrMsg ) + + ! Destroy the output data: + call FVW_DestroyOutput( y, ErrStat, ErrMsg ) + +end subroutine FVW_End + + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> Loose coupling routine for solving for constraint states, integrating continuous states, and updating discrete and other states. +!! Continuous, constraint, discrete, and other states are updated for t + Interval +subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat, errMsg ) +!.................................................................................................................................. + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + integer(IntKi), intent(in ) :: n !< Current simulation time step n = 0,1,... + type(FVW_InputType), intent(inout) :: u(:) !< Inputs at utimes (out only for mesh record-keeping in ExtrapInterp routine) + real(DbKi), intent(in ) :: utimes(:) !< Times associated with u(:), in seconds + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(inout) :: x !< Input: Continuous states at t; Output: at t+dt + type(FVW_DiscreteStateType), intent(inout) :: xd !< Input: Discrete states at t; Output: at t+dt + type(FVW_ConstraintStateType), intent(inout) :: z !< Input: Constraint states at t; Output: at t+dt + type(FVW_OtherStateType), intent(inout) :: OtherState !< Input: Other states at t; Output: at t+dt + type(FVW_MiscVarType), intent(inout) :: m !< Misc/optimization variables + integer(IntKi), intent( out) :: errStat !< Error status of the operation + character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None + ! local variables + type(FVW_InputType) :: uInterp ! Interpolated/Extrapolated input + integer(intKi) :: ErrStat2 ! temporary Error status + character(ErrMsgLen) :: ErrMsg2 ! temporary Error message + type(FVW_ConstraintStateType) :: z_guess ! < + + ErrStat = ErrID_None + ErrMsg = "" + + print'(A,F10.3,A,F10.3,A,F10.3,A,I0)',' Update states, t:',t,' t_u:', utimes(1),' dt: ',utimes(1)-t,' ',n + + ! Panelling wings based on input mesh provided + CALL Wings_Panelling(u(1)%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return + + ! Distribute the Wind we requested to Inflow wind to storage Misc arrays + ! TODO ANDY + !CALL DistributeRequestedWind(u(1)%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return + + CALL FVW_CalcConstrStateResidual(t, u(1), p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return + + if (p%IntMethod .eq. idEuler1) then + call FVW_Euler1( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return + + !elseif (p%IntMethod .eq. idRK4) then + ! call FVW_RK4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) + !elseif (p%IntMethod .eq. idAB4) then + ! call FVW_AB4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) + !elseif (p%IntMethod .eq. idABM4) then + ! call FVW_ABM4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) + else + call SetErrStat(ErrID_Fatal,'Invalid time integration method:'//Num2LStr(p%IntMethod),ErrStat,ErrMsg,'FVW_UpdateState') + end IF + + if (m%FirstCall) then + m%FirstCall=.False. + endif +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_UpdateStates') + Failed = ErrStat >= AbortErrLev + !if (Failed) call CleanUp() + end function Failed + +end subroutine FVW_UpdateStates + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This is a tight coupling routine for computing derivatives of continuous states. +subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + type(FVW_InputType), intent(in ) :: u !< Inputs at t + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< Continuous states at t + type(FVW_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraint states at t + type(FVW_OtherStateType), intent(in ) :: OtherState !< Other states at t + type(FVW_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) + type(FVW_ContinuousStateType), intent( out) :: dxdt !< Continuous state derivatives at t + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer(IntKi) :: iSpan,iAge, iW + real(ReKi), dimension(3) :: U_mean + + call AllocAry( dxdt%r_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat, ErrMsg ); dxdt%r_NW= -999999_ReKi; + call AllocAry( dxdt%r_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; + + if (p%FreeWake) then + print*,'TODO free wake convection' + STOP + else + U_mean(1:3)=0 + do iW=1,p%nWings; do iAge=1,m%nNW+1; do iSpan=1,p%nSpan+1; + U_mean(1:3)= U_mean(1:3)+ m%Vwnd_NW(1:3, iSpan, iAge, iW) + enddo; enddo; enddo + U_mean(1:3) = U_mean(1:3)/ ((m%nNW+1)*(p%nSpan+1)*p%nWings) + print*,'Mean convection velocity NW: ',U_mean(1:3) + U_mean(1:3)=0 + do iW=1,p%nWings; do iAge=1,m%nFW+1; do iSpan=1,2; + U_mean(1:3)= U_mean(1:3)+ m%Vwnd_FW(1:3, iSpan, iAge, iW) + enddo; enddo; enddo + U_mean(1:3) = U_mean(1:3)/ ((m%nFW+1)*(2)*p%nWings) + print*,'Mean convection velocity FW: ',U_mean(1:3) + + + ! --- Vortex points are convected with the free stream + dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dxdt%r_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) + endif +end subroutine FVW_CalcContStateDeriv + +!---------------------------------------------------------------------------------------------------------------------------------- +subroutine FVW_Euler1( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + integer(IntKi), intent(in ) :: n !< time step number + type(FVW_InputType), intent(inout) :: u(:) !< Inputs at t + real(DbKi), intent(in ) :: utimes(:) !< times of input + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states at t on input at t + dt on output + type(FVW_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraint states at t (possibly a guess) + type(FVW_OtherStateType), intent(inout) :: OtherState !< Other states at t on input at t + dt on output + type(FVW_MiscVarType), intent(inout) :: m !< Misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! local variables + type(FVW_ContinuousStateType) :: dxdt ! time derivatives of continuous states + real(ReKi) :: dt ! Time step + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! Compute "right hand side" + CALL FVW_CalcContStateDeriv( t, u(1), p, x, xd, z, OtherState, m, dxdt, ErrStat, ErrMsg ) + + dt = utimes(1)-t ! TODO TODO is this correct + + ! Update of positions + x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dt * dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + x%r_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) = x%r_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) + dt * dxdt%r_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) + + ! Update of Gamma + ! TODO, viscous diffusion, stretching + +end subroutine FVW_Euler1 +!---------------------------------------------------------------------------------------------------------------------------------- + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This is a tight coupling routine for solving for the residual of the constraint state functions. +subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z, OtherState, m, z_out, ErrStat, ErrMsg ) + real(DbKi), intent(IN ) :: t !< Current simulation time in seconds + type(FVW_InputType), intent(IN ) :: u !< Inputs at t + type(FVW_ParameterType), intent(IN ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(IN ) :: x !< Continuous states at t + type(FVW_DiscreteStateType), intent(IN ) :: xd !< Discrete states at t + type(FVW_ConstraintStateType), intent(IN ) :: z !< Constraint states at t (possibly a guess) + type(FVW_OtherStateType), intent(IN ) :: OtherState !< Other states at t + type(FVW_MiscVarType), intent(INOUT) :: m !< Misc variables for optimization (not copied in glue code) + type(FVW_ConstraintStateType), intent( OUT) :: z_out !< Residual of the constraint state functions using + !! the input values described above + integer(IntKi), intent( OUT) :: ErrStat !< Error status of the operation + character(*), intent( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! Solve for the residual of the constraint state functions here: + !z%residual = 0.0_ReKi + !z%Gamma_LL = 0.0_ReKi + call AllocAry( z_out%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat, ErrMsg ); + z_out%Gamma_LL = -999999_ReKi; + + CALL Wings_ComputeCirculation(z_out%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) + +end subroutine FVW_CalcConstrStateResidual + +!---------------------------------------------------------------------------------------------------------------------------------- +!> Routine for computing outputs, used in both loose and tight coupling. +!! This subroutine is used to compute the output channels (motions and loads) and place them in the WriteOutput() array. +!! The descriptions of the output channels are not given here. Please see the included OutListParameters.xlsx sheet for +!! for a complete description of each output parameter. +subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) +! NOTE: no matter how many channels are selected for output, all of the outputs are calculated +! All of the calculated output channels are placed into the m%AllOuts(:), while the channels selected for outputs are +! placed in the y%WriteOutput(:) array. +!.................................................................................................................................. + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + type(FVW_InputType), intent(in ) :: u !< Inputs at Time t + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< Continuous states at t + type(FVW_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(FVW_ConstraintStateType), intent(inout) :: z !< Constraint states at t + type(FVW_OtherStateType), intent(in ) :: OtherState !< Other states at t + type(FVW_OutputType), intent(inout) :: y !< Outputs computed at t (Input only so that mesh con- + !! nectivity information does not have to be recalculated) + type(FVW_MiscVarType), intent(inout) :: m !< Misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'FVW_CalcOutput' + + ErrStat = ErrID_None + ErrMsg = "" + + print'(A,F10.3,A,L1)',' CalcOutput t:',t,' ',m%FirstCall + + if (m%FirstCall) then + print*,'>>> First Call of CalcOuput, calling panelling and constrstate' + CALL Wings_Panelling(u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL Wings_ComputeCirculation(m%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) ! For plotting only + else + m%Gamma_LL = z%Gamma_LL ! For plotting only + endif + + if (.not. allocated(y%Vind)) then + !call AllocAry( y%Vind , 3, p%nSpan, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); + call AllocAry( y%Vind , 3, p%nSpan, 3, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO TODO TODO Hack nWings=3 for output + if(Failed()) return + endif + ! Returned guessed locations where wind will be required + CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + + !if (m%FirstCall) then + !endif + + ! For now returning 0 + y%Vind(1,:,:) = 0.0_ReKi + y%Vind(2,:,:) = 0.0_ReKi + y%Vind(3,:,:) = 0.0_ReKi + + ! We don't propagate the "Old"-> "New" if update states was not called once + ! This is introduced since at init, CalcOutput is called before UpdateState + if (.not. m%FirstCall) then + call PrepareNextTimeStep() + endif + +contains + !========================================================================== + !> Computes induction on the lifting line (3/4 chord point) + ! Interpolate the values at the radial station of AeroDyn + subroutine CalcInduction_LL() + + end subroutine CalcInduction_LL + + + subroutine PrepareNextTimeStep() + ! --- Propagate wake + ! TODO + + ! + m%nNW=m%nNW+1 + if (m%nNW>p%nNWMax) m%nNW = p%nNWMax + + end subroutine PrepareNextTimeStep + + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_CalcOutput') + Failed = ErrStat >= AbortErrLev + !if (Failed) call CleanUp() + end function Failed + !========================================================================== +end subroutine FVW_CalcOutput + + +END MODULE FVW diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 new file mode 100644 index 0000000000..3e3f7502e9 --- /dev/null +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -0,0 +1,130 @@ +module FVW_IO + + USE FVW_Types + USE FVW_Subs + implicit none + +contains + +! ============================================================================== +!> Reads the input file for FVW +SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) + character(len=*), intent(in) :: FileName !< Input file name for FVW + type( FVW_ParameterType ), intent(inout) :: p !< Parameters + type(FVW_InputFile), intent(out) :: Inp !< Data stored in the module's input file + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer :: iLine + real(ReKi) :: TODO_Re + character(1024) :: PriPath ! the path to the primary input file + character(1024) :: line ! string to temporarially hold value of read line + integer(IntKi) :: UnIn + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + ErrStat = ErrID_None + ErrMsg = "" + ! Open file + CALL GetNewUnit( UnIn ) + CALL OpenFInpfile(UnIn, TRIM(FileName), ErrStat2, ErrMsg2) + if (Check( ErrStat2 /= ErrID_None , 'Could not open input file')) return + CALL GetPath( FileName, PriPath ) ! Input files will be relative to the path where the primary input file is located. + !------------------------------------- HEADER --------------------------------------------------- + CALL ReadCom(UnIn, FileName, 'FVW input file header line 1', ErrStat2, ErrMsg2 ); if(Failed()) return + CALL ReadCom(UnIn, FileName, 'FVW input file header line 2', ErrStat2, ErrMsg2 ); if(Failed()) return + !------------------------ GENERAL OPTIONS ------------------------------------------- + CALL ReadCom(UnIn,FileName, 'General option header', ErrStat2, ErrMsg2 ); if(Failed()) return + CALL ReadVar(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%FreeWake ,'FreeWake' ,'',ErrStat2,ErrMsg2); if(Failed())return + !------------------------ CIRCULATION SPECIFICATIONS ------------------------------------------- + CALL ReadCom(UnIn,FileName, 'Circulation specification header', ErrStat2, ErrMsg2 ); if(Failed()) return + CALL ReadVar(UnIn,FileName,Inp%CirculationMethod,'CirculationMethod','',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return + + ! Post pro and validation of inputs + if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) + + if (Check(.not.(ANY((/idCircNoFlowThrough,idCircPrescribed/)==Inp%CirculationMethod)), 'Circulation method not implemented')) return + + if (Check( Inp%IntMethod/=idEuler1 , 'Time integration method not implemented')) return + + call CleanUp() + +CONTAINS + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_ReadInputFile') + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + + logical function Check(Condition, ErrMsg_in) + logical, intent(in) :: Condition + character(len=*), intent(in) :: ErrMsg_in + Check=Condition + if (Check) then + call SetErrStat(ErrID_Fatal, 'Error in file '//TRIM(FileName)//': '//trim(ErrMsg_in), ErrStat, ErrMsg, 'FVW_ReadInputFile'); + call CleanUp() + endif + end function Check + + subroutine CleanUp() + close( UnIn ) + end subroutine + +END SUBROUTINE FVW_ReadInputFile + +!================================================= +subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) + use VTK ! for all the vtk_* functions + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< States + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints + type(FVW_MiscVarType), intent(in ) :: m !< MiscVars + character(*), intent(in) :: FileRootName !< Name of the file to write the output in (excluding extension) + integer(IntKi), intent(in) :: VTKcount !< Indicates number for VTK output file (when 0, the routine will also write reference information) + integer(IntKi), intent(in) :: Twidth !< Number of digits in the maximum write-out step (used to pad the VTK write-out in the filename with zeros) + ! local variables + integer:: iWing + character(1024) :: FileName + character(255) :: Label + character(Twidth) :: Tstr ! string for current VTK write-out step (padded with zeros) + real(ReKi), dimension(:,:), allocatable :: Buffer + real(ReKi), dimension(:), allocatable :: Buffer1d + integer(IntKi), dimension(:,:), allocatable :: Connectivity + integer :: iSeg + integer :: iSpan, iNW, iFW + integer :: nSpan, nNW, nWings, nFW + integer :: k + character(1), dimension(3) :: I2ABC =(/'A','B','C'/) + + ! TimeStamp + write(Tstr, '(i' // trim(Num2LStr(Twidth)) //'.'// trim(Num2LStr(Twidth)) // ')') VTKcount + + nSpan = p%nSpan + nWings = p%nWings + nNW = m%nNW + nFW = m%nFW + ! --------------------------------------------------------------------------------} + ! --- Blade + ! --------------------------------------------------------------------------------{ + ! --- Blade Quarter chord points (AC) + do iWing=1,nWings + write(Label,'(A,A)') 'BldPointCP.Bld', i2ABC(iWing) + Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' + if ( vtk_new_ascii_file(trim(filename),Label) ) then + call vtk_dataset_polydata(m%CP_LL(1:3,1:p%nSpan,iWing)) + call vtk_point_data_init() + call vtk_point_data_scalar(m%Gamma_ll( 1:p%nSpan,iWing),'Gamma_ll') + call vtk_point_data_vector(m%Vind_ll (1:3,1:p%nSpan,iWing),'Vind_ll') + call vtk_point_data_vector(m%Vtot_ll (1:3,1:p%nSpan,iWing),'Vtot_ll') + call vtk_point_data_vector(m%Vstr_ll (1:3,1:p%nSpan,iWing),'Vstr_ll') + call vtk_point_data_vector(m%Vwnd_ll (1:3,1:p%nSpan,iWing),'Vwnd_ll') + call vtk_point_data_vector(m%Tang (1:3,1:p%nSpan,iWing),'Tangent') + call vtk_point_data_vector(m%Norm (1:3,1:p%nSpan,iWing),'Normal') + call vtk_point_data_vector(m%Orth (1:3,1:p%nSpan,iWing),'Orth') + call vtk_close_file() + endif + enddo +end subroutine WrVTK_FVW + +END MODULE FVW_IO diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 new file mode 100644 index 0000000000..2fe09749f7 --- /dev/null +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -0,0 +1,278 @@ +module FVW_SUBS + + use NWTC_LIBRARY + use FVW_TYPES + + implicit none + + ! --- Module parameters + ! Circulation solving methods + integer(IntKi), parameter :: idCircNoFlowThrough = 0 + integer(IntKi), parameter :: idCircPolarData = 1 + integer(IntKi), parameter :: idCircPrescribed = 2 + ! Integration method + integer(IntKi), parameter :: idRK4 = 1 + integer(IntKi), parameter :: idAB4 = 2 + integer(IntKi), parameter :: idABM4 = 3 + integer(IntKi), parameter :: idEuler1 = 5 + +contains + +!========================================================================== +!> Helper function for 1d interpolation (interp1d) +function interpolation_array( xvals, yvals, xi, nOut, nIn ) + integer nOut, nIn, arindx, ilo + real(ReKi), dimension( nOut ) :: interpolation_array, xi + real(ReKi), dimension( nIn ) :: xvals, yvals, tmp2, tmp3 + real(ReKi) :: tmp1 + ilo = 1 + DO arindx = 1, nOut + IF ( xi( arindx ) .LT. xvals( 1 )) THEN + interpolation_array( arindx ) = yvals( 1 ) + ( xi( arindx ) - xvals( 1 )) / & + & ( xvals( 2 ) - xvals( 1 )) * ( yvals( 2 ) - yvals( 1 )) + ELSE IF ( xi( arindx ) .GT. xvals( nIn )) THEN + interpolation_array( arindx ) = yvals( nIn - 1 ) + ( xi( arindx ) - & + & xvals( nIn - 1 )) / ( xvals( nIn ) - xvals( nIn - 1 )) * & + & ( yvals( nIn ) - yvals( nIn - 1 )) + ELSE + tmp1 = real( xi( arindx ), ReKi) + tmp2 = real( xvals , ReKi) + tmp3 = real( yvals , ReKi) + interpolation_array( arindx ) = InterpBinReal( tmp1, tmp2, tmp3, ilo, nIn ) + END IF + END DO +END FUNCTION interpolation_array +!========================================================================== + +! ===================================================================================== +!> Output blade circulation +subroutine Output_Gamma(CP, Gamma_LL, iWing, iCall, Time) + real( ReKi ), dimension( :, : ), intent(in ) :: CP !< Control Points + real( ReKi ), dimension( : ), intent(in ) :: Gamma_LL !< Circulation on the lifting line + integer( IntKi ), intent(in ) :: iWing !< Wing index + integer( IntKi ), intent(in ) :: iCall !< Call ID + real(DbKi), intent(in ) :: Time + character(len=255) :: filename + integer :: i + integer :: iUnit + real(ReKi) :: norm + call GetNewUnit(iUnit) + ! TODO output folder + write(filename,'(A,I0,A,I0,A,I0,A)')'Gamma/Gamma_call',int(iCall),'_t',int(Time*10000),'_Wing',int(iWing),'.txt' + OPEN(unit = iUnit, file = trim(filename), status="unknown", action="write") + write(iUnit,'(A)') 'norm_[m],x_[m],y_[m],z_[m], Gamma_[m^2/s]' + do i=1,size(Gamma_LL) + norm=sqrt(CP(1,i)**2+CP(2,i)**2+CP(3,i)**2) + write(iUnit,'(E14.7,A,E14.7,A,E14.7,A,E14.7,A,E14.7)') norm,',', CP(1,i),',',CP(2,i),',',CP(3,i),',', Gamma_LL(i) + enddo + close(iUnit) +endsubroutine Output_Gamma +! ===================================================================================== +!> Read a delimited file containing a circulation and interpolate it on the requested Control Points +!! The input file is a delimited file with one line of header. +!! Each following line consists of two columns: r/R_[-] and Gamma_[m^2/s] +subroutine ReadAndInterpGamma(CirculationFileName, s_CP_LL, L, Gamma_CP_LL) + character(len=*), intent(in ) :: CirculationFileName !< Input file to read + real(ReKi), dimension(:), intent(in ) :: s_CP_LL !< Spanwise location of the lifting CP [m] + real(ReKi), intent(in ) :: L !< Full span of lifting line + real(ReKi), dimension(:), intent(out ) :: Gamma_CP_LL !< Interpolated circulation of the LL CP + ! Local + integer(IntKi) :: nLines + integer(IntKi) :: i + integer(IntKi) :: iStat + integer(IntKi) :: nr + integer(IntKi) :: iUnit + character(len=1054) :: line + real(ReKi), dimension(:), allocatable :: sPrescr, GammaPrescr !< Radius + ! --- + call GetNewUnit(iUnit) + open(unit = iUnit, file = CirculationFileName) + nLines=line_count(iUnit)-1 + ! Read Header + read(iUnit,*, iostat=istat) line + ! Read table: s/L [-], GammaPresc [m^2/s] + allocate(sPrescr(1:nLines), GammaPrescr(1:nLines)) + do i=1,nLines + read(iUnit,*, iostat=istat) sPrescr(i), GammaPrescr(i) + sPrescr(i) = sPrescr(i) * L + GammaPrescr(i) = - GammaPrescr(i) ! NOTE: Tempoary minus sign until implementation has reversed sign + enddo + close(iUnit) + if (istat/=0) then + print*,'Error occured while reading Circulation file' + STOP + endif + ! NOTE: TODO TODO TODO THIS ROUTINE PERFORMS NASTY EXTRAPOLATION, SHOULD BE PLATEAUED + Gamma_CP_LL = interpolation_array( sPrescr, GammaPrescr, s_CP_LL, size(s_CP_LL), nLines ) +contains + + !> Counts number of lines in a file + integer function line_count(iunit) + integer(IntKi), intent(in) :: iunit + character(len=1054) :: line + ! safety for infinite loop.. + integer(IntKi), parameter :: nline_max=100000000 ! 100 M + integer(IntKi) :: i + line_count=0 + do i=1,nline_max + line='' + read(iunit,'(A)',END=100)line + line_count=line_count+1 + enddo + if (line_count==nline_max) then + print*,'Error: MainIO: maximum number of line exceeded' + STOP + endif + 100 if(len(trim(line))>0) then + line_count=line_count+1 + endif + rewind(iunit) + end function + +endsubroutine ReadAndInterpGamma +! ===================================================================================== +!> Prescribe circulation on the blade (based on an input file for now...) +subroutine Prescribe_Gamma( Gamma_LL) + real( ReKi ), dimension( : ), intent(out ) :: Gamma_LL !< Circulation on the lifting line + integer :: i + integer :: iUnit + integer :: nr + integer :: nr_in + real(ReKi), dimension(:), allocatable :: rPrescr, GammaPrescr !< Radius + nr_in = size(Gamma_LL) + call GetNewUnit(iUnit) + open(unit = iUnit, file = './PrescribedGamma.txt') ! TODO more general + read(iUnit,*)nr + if (nr/=nr_in) then + write(*,*)'[ERROR] Number of prescribed point different from number of BS' + STOP 1 + endif + allocate(rPrescr(1:nr),GammaPrescr(1:nr)) + do i=1,nr + read(iUnit,*) rPrescr(i), GammaPrescr(i) + enddo + close(iUnit) + ! -- This part is the only part we need once the thing above has been moved + do i=1,nr + Gamma_LL(i)=GammaPrescr(i) + enddo +endsubroutine Prescribe_Gamma + +!> Establish the list of points where we will need the free stream +subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) + real(ReKi), dimension(:,:), allocatable, intent(inout) :: r_wind !< Position where wind is requested + type(FVW_ContinuousStateType), intent(in ) :: x !< States + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + integer(IntKi) :: nTot ! Total number of points + integer(IntKi) :: iSpan, iW, iAge ! Index on span, wings, panels + integer(IntKi) :: iP ! Current index of point + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + if (allocated(r_wind)) deallocate(r_wind) + + nTot = 0 + nTot = nTot + p%nWings * p%nSpan ! Lifting line Control Points + nTot = nTot + p%nWings * (p%nSpan+1) * (m%nNW+1) ! Nearwake points + nTot = nTot + p%nWings * ( 1 +1) * (m%nFW+1) ! War wake points + + call AllocAry( r_wind , 3, nTot, 'Requested Wind Points', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,'SetRequestedWindPoints'); + r_wind(1:3,1:nTot)= -999999_ReKi; + + iP=0 + + ! --- LL CP + do iW = 1, p%nWings + do iSpan = 1, p%nSpan + iP=iP+1 + r_wind(1:3,iP) = m%CP_LL(1:3, iSpan, iW) + enddo + enddo + + ! --- NW points + do iW = 1, p%nWings + do iSpan = 1, p%nSpan + 1 + do iAge = 1, m%nNW + 1 + iP=iP+1 + r_wind(1:3,iP) = x%r_NW(1:3, iSpan, iAge, iW) + enddo + enddo + enddo + + ! --- FW points + do iW = 1, p%nWings + do iSpan = 1, 2 ! root and tip + do iAge = 1, m%nFW + 1 + iP=iP+1 + r_wind(1:3,iP) = x%r_FW(1:3, iSpan, iAge, iW) + enddo + enddo + enddo +end subroutine SetRequestedWindPoints + + +!> Set the requested wind into the correponding misc variables +subroutine DistributeRequestedWind(V_wind, x, p, m, ErrStat, ErrMsg ) + real(ReKi), dimension(:,:), intent(in ) :: V_wind !< Position where wind is requested + type(FVW_ContinuousStateType), intent(in ) :: x !< States + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + integer(IntKi) :: nTot ! Total number of points + integer(IntKi) :: iSpan, iW, iAge ! Index on span, wings, panels + integer(IntKi) :: iP ! Current index of point + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! nTot, for satefy check + nTot = 0 + nTot = nTot + p%nWings * p%nSpan ! Lifting line Control Points + nTot = nTot + p%nWings * (p%nSpan+1) * (m%nNW+1) ! Nearwake points + nTot = nTot + p%nWings * ( 1 +1) * (m%nFW+1) ! War wake points + if (size(V_wind,2) This module contains the user-defined types needed in FVW. 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 FVW_Types +!--------------------------------------------------------------------------------------------------------------------------------- +USE IfW_UniformWind_Types +USE IfW_TSFFWind_Types +USE IfW_BladedFFWind_Types +USE IfW_HAWCWind_Types +USE IfW_UserWind_Types +USE IfW_4Dext_Types +USE Lidar_Types +USE InflowWind_Types +USE AD14AeroConf_Types +USE NWTC_Library +IMPLICIT NONE +! ========= FVW_ParameterType ======= + TYPE, PUBLIC :: FVW_ParameterType + INTEGER(IntKi) :: nWings !< Number of Wings [-] + INTEGER(IntKi) :: nSpan !< TODO, should be defined per wing. Number of spanwise element [-] + INTEGER(IntKi) :: nNWMax !< Maximum number of nw panels [-] + INTEGER(IntKi) :: nFWMax !< Maximum number of fw panels [-] + INTEGER(IntKi) :: IntMethod !< Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1) [-] + LOGICAL :: FreeWake !< Disable roll up, wake convects with wind only (flag) [-] + INTEGER(IntKi) :: CirculationMethod !< Method to determine the circulation [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: PrescribedCirculation !< Prescribed circulation on all lifting lines [m/s] + END TYPE FVW_ParameterType +! ======================= +! ========= FVW_OtherStateType ======= + TYPE, PUBLIC :: FVW_OtherStateType + INTEGER(IntKi) :: NULL !< Number of active near wake panels [-] + END TYPE FVW_OtherStateType +! ======================= +! ========= FVW_MiscVarType ======= + TYPE, PUBLIC :: FVW_MiscVarType + LOGICAL :: FirstCall !< True if this is the first call to update state (used in CalcOutput) [-] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: LE !< Leading edge points [-] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: TE !< Trailing edge points [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: s_LL !< Spanwise coordinate of LL elements [m] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: chord_LL !< chord on LL cp [m] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: s_CP_LL !< Spanwise coordinate of LL CP [m] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: chord_CP_LL !< chord on LL cp [m] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CP_LL !< Coordinates of LL CP [-] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Tang !< Unit Tangential vector on LL CP [-] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Norm !< Unit Normal vector on LL CP [-] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Orth !< Unit Orthogonal vector on LL CP [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Gamma_LL !< Circulation on the wing lifting line (COPY of Constraint State) [-] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vind_LL !< Induced velocity on lifting line control points [m/s] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vtot_LL !< Total velocity on lifting line control points [m/s] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vstr_LL !< Structural velocity on LL CP [m/s] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vwnd_LL !< Wind on lifting line control points [m/s] + REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: Vwnd_NW !< Wind on near wake panels [m/s] + REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: Vwnd_FW !< Wind on far wake panels [m/s] + INTEGER(IntKi) :: nNW !< Number of active near wake panels [-] + INTEGER(IntKi) :: nFW !< Number of active far wake panels [-] + END TYPE FVW_MiscVarType +! ======================= +! ========= FVW_InputType ======= + TYPE, PUBLIC :: FVW_InputType + TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: WingsMesh !< - [Input Mesh defining position and orientation of wings] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: V_wind !< Wind at requested points (r_wind) [-] + END TYPE FVW_InputType +! ======================= +! ========= FVW_OutputType ======= + TYPE, PUBLIC :: FVW_OutputType + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vind !< TODO mesh - Induced velocity vector. [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: r_wind !< List of points where wind is requested for next time step [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Cl_KJ !< Lift coefficient from circulation (Kutta-Joukowski) [-] + END TYPE FVW_OutputType +! ======================= +! ========= FVW_ContinuousStateType ======= + TYPE, PUBLIC :: FVW_ContinuousStateType + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Gamma_NW !< Circulation of the near wake panels [-] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Gamma_FW !< Circulation of the far wake panels [-] + REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: r_NW !< Position of the near wake panels [-] + REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: r_FW !< Position of the far wake panels [-] + END TYPE FVW_ContinuousStateType +! ======================= +! ========= FVW_DiscreteStateType ======= + TYPE, PUBLIC :: FVW_DiscreteStateType + INTEGER(IntKi) :: Null !< Empty to satisfy framework [-] + END TYPE FVW_DiscreteStateType +! ======================= +! ========= FVW_ConstraintStateType ======= + TYPE, PUBLIC :: FVW_ConstraintStateType + REAL(ReKi) :: residual + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Gamma_LL !< Circulation on the wing lifting line [-] + END TYPE FVW_ConstraintStateType +! ======================= +! ========= FVW_InitInputType ======= + TYPE, PUBLIC :: FVW_InitInputType + CHARACTER(1024) :: FVWFileName !< Main FVW input file name [-] + TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: WingsMesh !< Input Mesh defining position and orientation of wings (nSpan+1) [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: RElm !< radius of center of each element [-] + INTEGER(IntKi) :: NumBl !< Number of blades [-] + END TYPE FVW_InitInputType +! ======================= +! ========= FVW_InputFile ======= + TYPE, PUBLIC :: FVW_InputFile + INTEGER(IntKi) :: CirculationMethod !< Method to determine the circulation [-] + CHARACTER(1024) :: CirculationFile !< [-] + INTEGER(IntKi) :: IntMethod !< Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1, 7=Corrector/Predictor) [-] + LOGICAL :: FreeWake !< Disable roll up, wake convects with wind only (flag) [-] + END TYPE FVW_InputFile +! ======================= +! ========= FVW_InitOutputType ======= + TYPE, PUBLIC :: FVW_InitOutputType + INTEGER(IntKi) :: Null !< Empty parameter to satisfy framework [-] + END TYPE FVW_InitOutputType +! ======================= +CONTAINS + SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_ParameterType), INTENT(IN) :: SrcParamData + TYPE(FVW_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 = 'FVW_CopyParam' +! + ErrStat = ErrID_None + ErrMsg = "" + DstParamData%nWings = SrcParamData%nWings + DstParamData%nSpan = SrcParamData%nSpan + DstParamData%nNWMax = SrcParamData%nNWMax + DstParamData%nFWMax = SrcParamData%nFWMax + DstParamData%IntMethod = SrcParamData%IntMethod + DstParamData%FreeWake = SrcParamData%FreeWake + DstParamData%CirculationMethod = SrcParamData%CirculationMethod +IF (ALLOCATED(SrcParamData%PrescribedCirculation)) THEN + i1_l = LBOUND(SrcParamData%PrescribedCirculation,1) + i1_u = UBOUND(SrcParamData%PrescribedCirculation,1) + IF (.NOT. ALLOCATED(DstParamData%PrescribedCirculation)) THEN + ALLOCATE(DstParamData%PrescribedCirculation(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%PrescribedCirculation.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%PrescribedCirculation = SrcParamData%PrescribedCirculation +ENDIF + END SUBROUTINE FVW_CopyParam + + SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) + TYPE(FVW_ParameterType), INTENT(INOUT) :: ParamData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyParam' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(ParamData%PrescribedCirculation)) THEN + DEALLOCATE(ParamData%PrescribedCirculation) +ENDIF + END SUBROUTINE FVW_DestroyParam + + SUBROUTINE FVW_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(FVW_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 = 'FVW_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 ! nWings + Int_BufSz = Int_BufSz + 1 ! nSpan + Int_BufSz = Int_BufSz + 1 ! nNWMax + Int_BufSz = Int_BufSz + 1 ! nFWMax + Int_BufSz = Int_BufSz + 1 ! IntMethod + Int_BufSz = Int_BufSz + 1 ! FreeWake + Int_BufSz = Int_BufSz + 1 ! CirculationMethod + Int_BufSz = Int_BufSz + 1 ! PrescribedCirculation allocated yes/no + IF ( ALLOCATED(InData%PrescribedCirculation) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! PrescribedCirculation upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%PrescribedCirculation) ! PrescribedCirculation + END IF + 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:Int_Xferred+(1)-1 ) = InData%nWings + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nSpan + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nNWMax + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWMax + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%IntMethod + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%FreeWake , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%CirculationMethod + Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%PrescribedCirculation) ) 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%PrescribedCirculation,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%PrescribedCirculation,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%PrescribedCirculation)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%PrescribedCirculation))-1 ) = PACK(InData%PrescribedCirculation,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%PrescribedCirculation) + END IF + END SUBROUTINE FVW_PackParam + + SUBROUTINE FVW_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(FVW_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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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 = 'FVW_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%nWings = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%nSpan = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%nNWMax = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%nFWMax = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%IntMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%FreeWake = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + OutData%CirculationMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! PrescribedCirculation 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 + IF (ALLOCATED(OutData%PrescribedCirculation)) DEALLOCATE(OutData%PrescribedCirculation) + ALLOCATE(OutData%PrescribedCirculation(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%PrescribedCirculation.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%PrescribedCirculation)>0) OutData%PrescribedCirculation = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%PrescribedCirculation))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%PrescribedCirculation) + DEALLOCATE(mask1) + END IF + END SUBROUTINE FVW_UnPackParam + + SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_OtherStateType), INTENT(IN) :: SrcOtherStateData + TYPE(FVW_OtherStateType), INTENT(INOUT) :: DstOtherStateData + INTEGER(IntKi), INTENT(IN ) :: CtrlCode + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg +! Local + INTEGER(IntKi) :: i,j,k + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyOtherState' +! + ErrStat = ErrID_None + ErrMsg = "" + DstOtherStateData%NULL = SrcOtherStateData%NULL + END SUBROUTINE FVW_CopyOtherState + + SUBROUTINE FVW_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) + TYPE(FVW_OtherStateType), INTENT(INOUT) :: OtherStateData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyOtherState' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE FVW_DestroyOtherState + + SUBROUTINE FVW_PackOtherState( 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(FVW_OtherStateType), 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 = 'FVW_PackOtherState' + ! 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 ! NULL + 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:Int_Xferred+(1)-1 ) = InData%NULL + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_PackOtherState + + SUBROUTINE FVW_UnPackOtherState( 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(FVW_OtherStateType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackOtherState' + ! 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%NULL = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_UnPackOtherState + + SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_MiscVarType), INTENT(IN) :: SrcMiscData + TYPE(FVW_MiscVarType), INTENT(INOUT) :: DstMiscData + 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 = 'FVW_CopyMisc' +! + ErrStat = ErrID_None + ErrMsg = "" + DstMiscData%FirstCall = SrcMiscData%FirstCall +IF (ALLOCATED(SrcMiscData%LE)) THEN + i1_l = LBOUND(SrcMiscData%LE,1) + i1_u = UBOUND(SrcMiscData%LE,1) + i2_l = LBOUND(SrcMiscData%LE,2) + i2_u = UBOUND(SrcMiscData%LE,2) + i3_l = LBOUND(SrcMiscData%LE,3) + i3_u = UBOUND(SrcMiscData%LE,3) + IF (.NOT. ALLOCATED(DstMiscData%LE)) THEN + ALLOCATE(DstMiscData%LE(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%LE.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%LE = SrcMiscData%LE +ENDIF +IF (ALLOCATED(SrcMiscData%TE)) THEN + i1_l = LBOUND(SrcMiscData%TE,1) + i1_u = UBOUND(SrcMiscData%TE,1) + i2_l = LBOUND(SrcMiscData%TE,2) + i2_u = UBOUND(SrcMiscData%TE,2) + i3_l = LBOUND(SrcMiscData%TE,3) + i3_u = UBOUND(SrcMiscData%TE,3) + IF (.NOT. ALLOCATED(DstMiscData%TE)) THEN + ALLOCATE(DstMiscData%TE(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%TE.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%TE = SrcMiscData%TE +ENDIF +IF (ALLOCATED(SrcMiscData%s_LL)) THEN + i1_l = LBOUND(SrcMiscData%s_LL,1) + i1_u = UBOUND(SrcMiscData%s_LL,1) + i2_l = LBOUND(SrcMiscData%s_LL,2) + i2_u = UBOUND(SrcMiscData%s_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%s_LL)) THEN + ALLOCATE(DstMiscData%s_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%s_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%s_LL = SrcMiscData%s_LL +ENDIF +IF (ALLOCATED(SrcMiscData%chord_LL)) THEN + i1_l = LBOUND(SrcMiscData%chord_LL,1) + i1_u = UBOUND(SrcMiscData%chord_LL,1) + i2_l = LBOUND(SrcMiscData%chord_LL,2) + i2_u = UBOUND(SrcMiscData%chord_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%chord_LL)) THEN + ALLOCATE(DstMiscData%chord_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%chord_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%chord_LL = SrcMiscData%chord_LL +ENDIF +IF (ALLOCATED(SrcMiscData%s_CP_LL)) THEN + i1_l = LBOUND(SrcMiscData%s_CP_LL,1) + i1_u = UBOUND(SrcMiscData%s_CP_LL,1) + i2_l = LBOUND(SrcMiscData%s_CP_LL,2) + i2_u = UBOUND(SrcMiscData%s_CP_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%s_CP_LL)) THEN + ALLOCATE(DstMiscData%s_CP_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%s_CP_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%s_CP_LL = SrcMiscData%s_CP_LL +ENDIF +IF (ALLOCATED(SrcMiscData%chord_CP_LL)) THEN + i1_l = LBOUND(SrcMiscData%chord_CP_LL,1) + i1_u = UBOUND(SrcMiscData%chord_CP_LL,1) + i2_l = LBOUND(SrcMiscData%chord_CP_LL,2) + i2_u = UBOUND(SrcMiscData%chord_CP_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%chord_CP_LL)) THEN + ALLOCATE(DstMiscData%chord_CP_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%chord_CP_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%chord_CP_LL = SrcMiscData%chord_CP_LL +ENDIF +IF (ALLOCATED(SrcMiscData%CP_LL)) THEN + i1_l = LBOUND(SrcMiscData%CP_LL,1) + i1_u = UBOUND(SrcMiscData%CP_LL,1) + i2_l = LBOUND(SrcMiscData%CP_LL,2) + i2_u = UBOUND(SrcMiscData%CP_LL,2) + i3_l = LBOUND(SrcMiscData%CP_LL,3) + i3_u = UBOUND(SrcMiscData%CP_LL,3) + IF (.NOT. ALLOCATED(DstMiscData%CP_LL)) THEN + ALLOCATE(DstMiscData%CP_LL(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%CP_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%CP_LL = SrcMiscData%CP_LL +ENDIF +IF (ALLOCATED(SrcMiscData%Tang)) THEN + i1_l = LBOUND(SrcMiscData%Tang,1) + i1_u = UBOUND(SrcMiscData%Tang,1) + i2_l = LBOUND(SrcMiscData%Tang,2) + i2_u = UBOUND(SrcMiscData%Tang,2) + i3_l = LBOUND(SrcMiscData%Tang,3) + i3_u = UBOUND(SrcMiscData%Tang,3) + IF (.NOT. ALLOCATED(DstMiscData%Tang)) THEN + ALLOCATE(DstMiscData%Tang(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Tang.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Tang = SrcMiscData%Tang +ENDIF +IF (ALLOCATED(SrcMiscData%Norm)) THEN + i1_l = LBOUND(SrcMiscData%Norm,1) + i1_u = UBOUND(SrcMiscData%Norm,1) + i2_l = LBOUND(SrcMiscData%Norm,2) + i2_u = UBOUND(SrcMiscData%Norm,2) + i3_l = LBOUND(SrcMiscData%Norm,3) + i3_u = UBOUND(SrcMiscData%Norm,3) + IF (.NOT. ALLOCATED(DstMiscData%Norm)) THEN + ALLOCATE(DstMiscData%Norm(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Norm.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Norm = SrcMiscData%Norm +ENDIF +IF (ALLOCATED(SrcMiscData%Orth)) THEN + i1_l = LBOUND(SrcMiscData%Orth,1) + i1_u = UBOUND(SrcMiscData%Orth,1) + i2_l = LBOUND(SrcMiscData%Orth,2) + i2_u = UBOUND(SrcMiscData%Orth,2) + i3_l = LBOUND(SrcMiscData%Orth,3) + i3_u = UBOUND(SrcMiscData%Orth,3) + IF (.NOT. ALLOCATED(DstMiscData%Orth)) THEN + ALLOCATE(DstMiscData%Orth(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Orth.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Orth = SrcMiscData%Orth +ENDIF +IF (ALLOCATED(SrcMiscData%Gamma_LL)) THEN + i1_l = LBOUND(SrcMiscData%Gamma_LL,1) + i1_u = UBOUND(SrcMiscData%Gamma_LL,1) + i2_l = LBOUND(SrcMiscData%Gamma_LL,2) + i2_u = UBOUND(SrcMiscData%Gamma_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%Gamma_LL)) THEN + ALLOCATE(DstMiscData%Gamma_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Gamma_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Gamma_LL = SrcMiscData%Gamma_LL +ENDIF +IF (ALLOCATED(SrcMiscData%Vind_LL)) THEN + i1_l = LBOUND(SrcMiscData%Vind_LL,1) + i1_u = UBOUND(SrcMiscData%Vind_LL,1) + i2_l = LBOUND(SrcMiscData%Vind_LL,2) + i2_u = UBOUND(SrcMiscData%Vind_LL,2) + i3_l = LBOUND(SrcMiscData%Vind_LL,3) + i3_u = UBOUND(SrcMiscData%Vind_LL,3) + IF (.NOT. ALLOCATED(DstMiscData%Vind_LL)) THEN + ALLOCATE(DstMiscData%Vind_LL(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Vind_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Vind_LL = SrcMiscData%Vind_LL +ENDIF +IF (ALLOCATED(SrcMiscData%Vtot_LL)) THEN + i1_l = LBOUND(SrcMiscData%Vtot_LL,1) + i1_u = UBOUND(SrcMiscData%Vtot_LL,1) + i2_l = LBOUND(SrcMiscData%Vtot_LL,2) + i2_u = UBOUND(SrcMiscData%Vtot_LL,2) + i3_l = LBOUND(SrcMiscData%Vtot_LL,3) + i3_u = UBOUND(SrcMiscData%Vtot_LL,3) + IF (.NOT. ALLOCATED(DstMiscData%Vtot_LL)) THEN + ALLOCATE(DstMiscData%Vtot_LL(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Vtot_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Vtot_LL = SrcMiscData%Vtot_LL +ENDIF +IF (ALLOCATED(SrcMiscData%Vstr_LL)) THEN + i1_l = LBOUND(SrcMiscData%Vstr_LL,1) + i1_u = UBOUND(SrcMiscData%Vstr_LL,1) + i2_l = LBOUND(SrcMiscData%Vstr_LL,2) + i2_u = UBOUND(SrcMiscData%Vstr_LL,2) + i3_l = LBOUND(SrcMiscData%Vstr_LL,3) + i3_u = UBOUND(SrcMiscData%Vstr_LL,3) + IF (.NOT. ALLOCATED(DstMiscData%Vstr_LL)) THEN + ALLOCATE(DstMiscData%Vstr_LL(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Vstr_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Vstr_LL = SrcMiscData%Vstr_LL +ENDIF +IF (ALLOCATED(SrcMiscData%Vwnd_LL)) THEN + i1_l = LBOUND(SrcMiscData%Vwnd_LL,1) + i1_u = UBOUND(SrcMiscData%Vwnd_LL,1) + i2_l = LBOUND(SrcMiscData%Vwnd_LL,2) + i2_u = UBOUND(SrcMiscData%Vwnd_LL,2) + i3_l = LBOUND(SrcMiscData%Vwnd_LL,3) + i3_u = UBOUND(SrcMiscData%Vwnd_LL,3) + IF (.NOT. ALLOCATED(DstMiscData%Vwnd_LL)) THEN + ALLOCATE(DstMiscData%Vwnd_LL(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Vwnd_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Vwnd_LL = SrcMiscData%Vwnd_LL +ENDIF +IF (ALLOCATED(SrcMiscData%Vwnd_NW)) THEN + i1_l = LBOUND(SrcMiscData%Vwnd_NW,1) + i1_u = UBOUND(SrcMiscData%Vwnd_NW,1) + i2_l = LBOUND(SrcMiscData%Vwnd_NW,2) + i2_u = UBOUND(SrcMiscData%Vwnd_NW,2) + i3_l = LBOUND(SrcMiscData%Vwnd_NW,3) + i3_u = UBOUND(SrcMiscData%Vwnd_NW,3) + i4_l = LBOUND(SrcMiscData%Vwnd_NW,4) + i4_u = UBOUND(SrcMiscData%Vwnd_NW,4) + IF (.NOT. ALLOCATED(DstMiscData%Vwnd_NW)) THEN + ALLOCATE(DstMiscData%Vwnd_NW(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 DstMiscData%Vwnd_NW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Vwnd_NW = SrcMiscData%Vwnd_NW +ENDIF +IF (ALLOCATED(SrcMiscData%Vwnd_FW)) THEN + i1_l = LBOUND(SrcMiscData%Vwnd_FW,1) + i1_u = UBOUND(SrcMiscData%Vwnd_FW,1) + i2_l = LBOUND(SrcMiscData%Vwnd_FW,2) + i2_u = UBOUND(SrcMiscData%Vwnd_FW,2) + i3_l = LBOUND(SrcMiscData%Vwnd_FW,3) + i3_u = UBOUND(SrcMiscData%Vwnd_FW,3) + i4_l = LBOUND(SrcMiscData%Vwnd_FW,4) + i4_u = UBOUND(SrcMiscData%Vwnd_FW,4) + IF (.NOT. ALLOCATED(DstMiscData%Vwnd_FW)) THEN + ALLOCATE(DstMiscData%Vwnd_FW(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 DstMiscData%Vwnd_FW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Vwnd_FW = SrcMiscData%Vwnd_FW +ENDIF + DstMiscData%nNW = SrcMiscData%nNW + DstMiscData%nFW = SrcMiscData%nFW + END SUBROUTINE FVW_CopyMisc + + SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) + TYPE(FVW_MiscVarType), INTENT(INOUT) :: MiscData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyMisc' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(MiscData%LE)) THEN + DEALLOCATE(MiscData%LE) +ENDIF +IF (ALLOCATED(MiscData%TE)) THEN + DEALLOCATE(MiscData%TE) +ENDIF +IF (ALLOCATED(MiscData%s_LL)) THEN + DEALLOCATE(MiscData%s_LL) +ENDIF +IF (ALLOCATED(MiscData%chord_LL)) THEN + DEALLOCATE(MiscData%chord_LL) +ENDIF +IF (ALLOCATED(MiscData%s_CP_LL)) THEN + DEALLOCATE(MiscData%s_CP_LL) +ENDIF +IF (ALLOCATED(MiscData%chord_CP_LL)) THEN + DEALLOCATE(MiscData%chord_CP_LL) +ENDIF +IF (ALLOCATED(MiscData%CP_LL)) THEN + DEALLOCATE(MiscData%CP_LL) +ENDIF +IF (ALLOCATED(MiscData%Tang)) THEN + DEALLOCATE(MiscData%Tang) +ENDIF +IF (ALLOCATED(MiscData%Norm)) THEN + DEALLOCATE(MiscData%Norm) +ENDIF +IF (ALLOCATED(MiscData%Orth)) THEN + DEALLOCATE(MiscData%Orth) +ENDIF +IF (ALLOCATED(MiscData%Gamma_LL)) THEN + DEALLOCATE(MiscData%Gamma_LL) +ENDIF +IF (ALLOCATED(MiscData%Vind_LL)) THEN + DEALLOCATE(MiscData%Vind_LL) +ENDIF +IF (ALLOCATED(MiscData%Vtot_LL)) THEN + DEALLOCATE(MiscData%Vtot_LL) +ENDIF +IF (ALLOCATED(MiscData%Vstr_LL)) THEN + DEALLOCATE(MiscData%Vstr_LL) +ENDIF +IF (ALLOCATED(MiscData%Vwnd_LL)) THEN + DEALLOCATE(MiscData%Vwnd_LL) +ENDIF +IF (ALLOCATED(MiscData%Vwnd_NW)) THEN + DEALLOCATE(MiscData%Vwnd_NW) +ENDIF +IF (ALLOCATED(MiscData%Vwnd_FW)) THEN + DEALLOCATE(MiscData%Vwnd_FW) +ENDIF + END SUBROUTINE FVW_DestroyMisc + + SUBROUTINE FVW_PackMisc( 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(FVW_MiscVarType), 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 = 'FVW_PackMisc' + ! 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 ! FirstCall + Int_BufSz = Int_BufSz + 1 ! LE allocated yes/no + IF ( ALLOCATED(InData%LE) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! LE upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%LE) ! LE + END IF + Int_BufSz = Int_BufSz + 1 ! TE allocated yes/no + IF ( ALLOCATED(InData%TE) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! TE upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%TE) ! TE + END IF + Int_BufSz = Int_BufSz + 1 ! s_LL allocated yes/no + IF ( ALLOCATED(InData%s_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! s_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%s_LL) ! s_LL + END IF + Int_BufSz = Int_BufSz + 1 ! chord_LL allocated yes/no + IF ( ALLOCATED(InData%chord_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! chord_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%chord_LL) ! chord_LL + END IF + Int_BufSz = Int_BufSz + 1 ! s_CP_LL allocated yes/no + IF ( ALLOCATED(InData%s_CP_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! s_CP_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%s_CP_LL) ! s_CP_LL + END IF + Int_BufSz = Int_BufSz + 1 ! chord_CP_LL allocated yes/no + IF ( ALLOCATED(InData%chord_CP_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! chord_CP_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%chord_CP_LL) ! chord_CP_LL + END IF + Int_BufSz = Int_BufSz + 1 ! CP_LL allocated yes/no + IF ( ALLOCATED(InData%CP_LL) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! CP_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%CP_LL) ! CP_LL + END IF + Int_BufSz = Int_BufSz + 1 ! Tang allocated yes/no + IF ( ALLOCATED(InData%Tang) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Tang upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Tang) ! Tang + END IF + Int_BufSz = Int_BufSz + 1 ! Norm allocated yes/no + IF ( ALLOCATED(InData%Norm) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Norm upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Norm) ! Norm + END IF + Int_BufSz = Int_BufSz + 1 ! Orth allocated yes/no + IF ( ALLOCATED(InData%Orth) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Orth upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Orth) ! Orth + END IF + Int_BufSz = Int_BufSz + 1 ! Gamma_LL allocated yes/no + IF ( ALLOCATED(InData%Gamma_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Gamma_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Gamma_LL) ! Gamma_LL + END IF + Int_BufSz = Int_BufSz + 1 ! Vind_LL allocated yes/no + IF ( ALLOCATED(InData%Vind_LL) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Vind_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vind_LL) ! Vind_LL + END IF + Int_BufSz = Int_BufSz + 1 ! Vtot_LL allocated yes/no + IF ( ALLOCATED(InData%Vtot_LL) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Vtot_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vtot_LL) ! Vtot_LL + END IF + Int_BufSz = Int_BufSz + 1 ! Vstr_LL allocated yes/no + IF ( ALLOCATED(InData%Vstr_LL) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Vstr_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vstr_LL) ! Vstr_LL + END IF + Int_BufSz = Int_BufSz + 1 ! Vwnd_LL allocated yes/no + IF ( ALLOCATED(InData%Vwnd_LL) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Vwnd_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vwnd_LL) ! Vwnd_LL + END IF + Int_BufSz = Int_BufSz + 1 ! Vwnd_NW allocated yes/no + IF ( ALLOCATED(InData%Vwnd_NW) ) THEN + Int_BufSz = Int_BufSz + 2*4 ! Vwnd_NW upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vwnd_NW) ! Vwnd_NW + END IF + Int_BufSz = Int_BufSz + 1 ! Vwnd_FW allocated yes/no + IF ( ALLOCATED(InData%Vwnd_FW) ) THEN + Int_BufSz = Int_BufSz + 2*4 ! Vwnd_FW upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vwnd_FW) ! Vwnd_FW + END IF + Int_BufSz = Int_BufSz + 1 ! nNW + Int_BufSz = Int_BufSz + 1 ! nFW + 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:Int_Xferred+1-1 ) = TRANSFER( InData%FirstCall , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%LE) ) 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%LE,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%LE,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%LE,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%LE,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%LE,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%LE,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%LE)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%LE))-1 ) = PACK(InData%LE,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%LE) + END IF + IF ( .NOT. ALLOCATED(InData%TE) ) 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%TE,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%TE,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%TE,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%TE,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%TE,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%TE,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%TE)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%TE))-1 ) = PACK(InData%TE,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%TE) + END IF + IF ( .NOT. ALLOCATED(InData%s_LL) ) 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%s_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%s_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%s_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%s_LL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%s_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%s_LL))-1 ) = PACK(InData%s_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%s_LL) + END IF + IF ( .NOT. ALLOCATED(InData%chord_LL) ) 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%chord_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%chord_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%chord_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%chord_LL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%chord_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%chord_LL))-1 ) = PACK(InData%chord_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%chord_LL) + END IF + IF ( .NOT. ALLOCATED(InData%s_CP_LL) ) 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%s_CP_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%s_CP_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%s_CP_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%s_CP_LL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%s_CP_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%s_CP_LL))-1 ) = PACK(InData%s_CP_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%s_CP_LL) + END IF + IF ( .NOT. ALLOCATED(InData%chord_CP_LL) ) 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%chord_CP_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%chord_CP_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%chord_CP_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%chord_CP_LL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%chord_CP_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%chord_CP_LL))-1 ) = PACK(InData%chord_CP_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%chord_CP_LL) + END IF + IF ( .NOT. ALLOCATED(InData%CP_LL) ) 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%CP_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CP_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CP_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CP_LL,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CP_LL,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CP_LL,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%CP_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CP_LL))-1 ) = PACK(InData%CP_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%CP_LL) + END IF + IF ( .NOT. ALLOCATED(InData%Tang) ) 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%Tang,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Tang,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Tang,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Tang,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Tang,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Tang,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Tang)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Tang))-1 ) = PACK(InData%Tang,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Tang) + END IF + IF ( .NOT. ALLOCATED(InData%Norm) ) 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%Norm,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Norm,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Norm,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Norm,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Norm,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Norm,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Norm)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Norm))-1 ) = PACK(InData%Norm,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Norm) + END IF + IF ( .NOT. ALLOCATED(InData%Orth) ) 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%Orth,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Orth,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Orth,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Orth,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Orth,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Orth,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Orth)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Orth))-1 ) = PACK(InData%Orth,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Orth) + END IF + IF ( .NOT. ALLOCATED(InData%Gamma_LL) ) 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%Gamma_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Gamma_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_LL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Gamma_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Gamma_LL))-1 ) = PACK(InData%Gamma_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Gamma_LL) + END IF + IF ( .NOT. ALLOCATED(InData%Vind_LL) ) 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%Vind_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_LL,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_LL,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_LL,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vind_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vind_LL))-1 ) = PACK(InData%Vind_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vind_LL) + END IF + IF ( .NOT. ALLOCATED(InData%Vtot_LL) ) 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%Vtot_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vtot_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vtot_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vtot_LL,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vtot_LL,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vtot_LL,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vtot_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vtot_LL))-1 ) = PACK(InData%Vtot_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vtot_LL) + END IF + IF ( .NOT. ALLOCATED(InData%Vstr_LL) ) 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%Vstr_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vstr_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vstr_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vstr_LL,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vstr_LL,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vstr_LL,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vstr_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vstr_LL))-1 ) = PACK(InData%Vstr_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vstr_LL) + END IF + IF ( .NOT. ALLOCATED(InData%Vwnd_LL) ) 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%Vwnd_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_LL,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_LL,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_LL,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vwnd_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vwnd_LL))-1 ) = PACK(InData%Vwnd_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vwnd_LL) + END IF + IF ( .NOT. ALLOCATED(InData%Vwnd_NW) ) 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%Vwnd_NW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_NW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_NW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_NW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_NW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_NW,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_NW,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_NW,4) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vwnd_NW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vwnd_NW))-1 ) = PACK(InData%Vwnd_NW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vwnd_NW) + END IF + IF ( .NOT. ALLOCATED(InData%Vwnd_FW) ) 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%Vwnd_FW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_FW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_FW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_FW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_FW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_FW,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_FW,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_FW,4) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vwnd_FW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vwnd_FW))-1 ) = PACK(InData%Vwnd_FW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vwnd_FW) + END IF + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nNW + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFW + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_PackMisc + + SUBROUTINE FVW_UnPackMisc( 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(FVW_MiscVarType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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 = 'FVW_UnPackMisc' + ! 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%FirstCall = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! LE 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%LE)) DEALLOCATE(OutData%LE) + ALLOCATE(OutData%LE(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%LE.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%LE)>0) OutData%LE = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%LE))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%LE) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! TE 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%TE)) DEALLOCATE(OutData%TE) + ALLOCATE(OutData%TE(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%TE.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%TE)>0) OutData%TE = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%TE))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%TE) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! s_LL 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 + IF (ALLOCATED(OutData%s_LL)) DEALLOCATE(OutData%s_LL) + ALLOCATE(OutData%s_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%s_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%s_LL)>0) OutData%s_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%s_LL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%s_LL) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! chord_LL 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 + IF (ALLOCATED(OutData%chord_LL)) DEALLOCATE(OutData%chord_LL) + ALLOCATE(OutData%chord_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%chord_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%chord_LL)>0) OutData%chord_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%chord_LL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%chord_LL) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! s_CP_LL 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 + IF (ALLOCATED(OutData%s_CP_LL)) DEALLOCATE(OutData%s_CP_LL) + ALLOCATE(OutData%s_CP_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%s_CP_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%s_CP_LL)>0) OutData%s_CP_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%s_CP_LL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%s_CP_LL) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! chord_CP_LL 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 + IF (ALLOCATED(OutData%chord_CP_LL)) DEALLOCATE(OutData%chord_CP_LL) + ALLOCATE(OutData%chord_CP_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%chord_CP_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%chord_CP_LL)>0) OutData%chord_CP_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%chord_CP_LL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%chord_CP_LL) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CP_LL 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%CP_LL)) DEALLOCATE(OutData%CP_LL) + ALLOCATE(OutData%CP_LL(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%CP_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%CP_LL)>0) OutData%CP_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CP_LL))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%CP_LL) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Tang 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%Tang)) DEALLOCATE(OutData%Tang) + ALLOCATE(OutData%Tang(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%Tang.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Tang)>0) OutData%Tang = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Tang))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Tang) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Norm 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%Norm)) DEALLOCATE(OutData%Norm) + ALLOCATE(OutData%Norm(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%Norm.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Norm)>0) OutData%Norm = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Norm))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Norm) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Orth 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%Orth)) DEALLOCATE(OutData%Orth) + ALLOCATE(OutData%Orth(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%Orth.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Orth)>0) OutData%Orth = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Orth))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Orth) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Gamma_LL 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 + IF (ALLOCATED(OutData%Gamma_LL)) DEALLOCATE(OutData%Gamma_LL) + ALLOCATE(OutData%Gamma_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Gamma_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%Gamma_LL)>0) OutData%Gamma_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Gamma_LL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Gamma_LL) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vind_LL 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%Vind_LL)) DEALLOCATE(OutData%Vind_LL) + ALLOCATE(OutData%Vind_LL(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%Vind_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Vind_LL)>0) OutData%Vind_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vind_LL))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vind_LL) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vtot_LL 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%Vtot_LL)) DEALLOCATE(OutData%Vtot_LL) + ALLOCATE(OutData%Vtot_LL(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%Vtot_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Vtot_LL)>0) OutData%Vtot_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vtot_LL))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vtot_LL) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vstr_LL 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%Vstr_LL)) DEALLOCATE(OutData%Vstr_LL) + ALLOCATE(OutData%Vstr_LL(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%Vstr_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Vstr_LL)>0) OutData%Vstr_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vstr_LL))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vstr_LL) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vwnd_LL 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%Vwnd_LL)) DEALLOCATE(OutData%Vwnd_LL) + ALLOCATE(OutData%Vwnd_LL(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%Vwnd_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Vwnd_LL)>0) OutData%Vwnd_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vwnd_LL))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vwnd_LL) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vwnd_NW 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%Vwnd_NW)) DEALLOCATE(OutData%Vwnd_NW) + ALLOCATE(OutData%Vwnd_NW(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%Vwnd_NW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask4(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 mask4.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask4 = .TRUE. + IF (SIZE(OutData%Vwnd_NW)>0) OutData%Vwnd_NW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vwnd_NW))-1 ), mask4, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vwnd_NW) + DEALLOCATE(mask4) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vwnd_FW 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%Vwnd_FW)) DEALLOCATE(OutData%Vwnd_FW) + ALLOCATE(OutData%Vwnd_FW(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%Vwnd_FW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask4(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 mask4.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask4 = .TRUE. + IF (SIZE(OutData%Vwnd_FW)>0) OutData%Vwnd_FW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vwnd_FW))-1 ), mask4, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vwnd_FW) + DEALLOCATE(mask4) + END IF + OutData%nNW = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%nFW = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_UnPackMisc + + SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_InputType), INTENT(INOUT) :: SrcInputData + TYPE(FVW_InputType), INTENT(INOUT) :: DstInputData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyInput' +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(SrcInputData%WingsMesh)) THEN + i1_l = LBOUND(SrcInputData%WingsMesh,1) + i1_u = UBOUND(SrcInputData%WingsMesh,1) + IF (.NOT. ALLOCATED(DstInputData%WingsMesh)) THEN + ALLOCATE(DstInputData%WingsMesh(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInputData%WingsMesh.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DO i1 = LBOUND(SrcInputData%WingsMesh,1), UBOUND(SrcInputData%WingsMesh,1) + CALL MeshCopy( SrcInputData%WingsMesh(i1), DstInputData%WingsMesh(i1), CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + ENDDO +ENDIF +IF (ALLOCATED(SrcInputData%V_wind)) THEN + i1_l = LBOUND(SrcInputData%V_wind,1) + i1_u = UBOUND(SrcInputData%V_wind,1) + i2_l = LBOUND(SrcInputData%V_wind,2) + i2_u = UBOUND(SrcInputData%V_wind,2) + IF (.NOT. ALLOCATED(DstInputData%V_wind)) THEN + ALLOCATE(DstInputData%V_wind(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInputData%V_wind.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInputData%V_wind = SrcInputData%V_wind +ENDIF + END SUBROUTINE FVW_CopyInput + + SUBROUTINE FVW_DestroyInput( InputData, ErrStat, ErrMsg ) + TYPE(FVW_InputType), INTENT(INOUT) :: InputData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyInput' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(InputData%WingsMesh)) THEN +DO i1 = LBOUND(InputData%WingsMesh,1), UBOUND(InputData%WingsMesh,1) + CALL MeshDestroy( InputData%WingsMesh(i1), ErrStat, ErrMsg ) +ENDDO + DEALLOCATE(InputData%WingsMesh) +ENDIF +IF (ALLOCATED(InputData%V_wind)) THEN + DEALLOCATE(InputData%V_wind) +ENDIF + END SUBROUTINE FVW_DestroyInput + + SUBROUTINE FVW_PackInput( 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(FVW_InputType), 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 = 'FVW_PackInput' + ! 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 ! WingsMesh allocated yes/no + IF ( ALLOCATED(InData%WingsMesh) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! WingsMesh upper/lower bounds for each dimension + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + DO i1 = LBOUND(InData%WingsMesh,1), UBOUND(InData%WingsMesh,1) + Int_BufSz = Int_BufSz + 3 ! WingsMesh: size of buffers for each call to pack subtype + CALL MeshPack( InData%WingsMesh(i1), Re_Buf, Db_Buf, Int_Buf, ErrStat2, ErrMsg2, .TRUE. ) ! WingsMesh + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! WingsMesh + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! WingsMesh + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! WingsMesh + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + END DO + END IF + Int_BufSz = Int_BufSz + 1 ! V_wind allocated yes/no + IF ( ALLOCATED(InData%V_wind) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! V_wind upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%V_wind) ! V_wind + END IF + 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 + + IF ( .NOT. ALLOCATED(InData%WingsMesh) ) 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%WingsMesh,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%WingsMesh,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%WingsMesh,1), UBOUND(InData%WingsMesh,1) + CALL MeshPack( InData%WingsMesh(i1), Re_Buf, Db_Buf, Int_Buf, ErrStat2, ErrMsg2, OnlySize ) ! WingsMesh + 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 DO + END IF + IF ( .NOT. ALLOCATED(InData%V_wind) ) 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%V_wind,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%V_wind,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%V_wind,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%V_wind,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%V_wind)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%V_wind))-1 ) = PACK(InData%V_wind,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%V_wind) + END IF + END SUBROUTINE FVW_PackInput + + SUBROUTINE FVW_UnPackInput( 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(FVW_InputType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackInput' + ! 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 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! WingsMesh 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 + IF (ALLOCATED(OutData%WingsMesh)) DEALLOCATE(OutData%WingsMesh) + ALLOCATE(OutData%WingsMesh(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%WingsMesh.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%WingsMesh,1), UBOUND(OutData%WingsMesh,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 MeshUnpack( OutData%WingsMesh(i1), Re_Buf, Db_Buf, Int_Buf, ErrStat2, ErrMsg2 ) ! WingsMesh + 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 DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! V_wind 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 + IF (ALLOCATED(OutData%V_wind)) DEALLOCATE(OutData%V_wind) + ALLOCATE(OutData%V_wind(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%V_wind.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%V_wind)>0) OutData%V_wind = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%V_wind))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%V_wind) + DEALLOCATE(mask2) + END IF + END SUBROUTINE FVW_UnPackInput + + SUBROUTINE FVW_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_OutputType), INTENT(IN) :: SrcOutputData + TYPE(FVW_OutputType), INTENT(INOUT) :: DstOutputData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyOutput' +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(SrcOutputData%Vind)) THEN + i1_l = LBOUND(SrcOutputData%Vind,1) + i1_u = UBOUND(SrcOutputData%Vind,1) + i2_l = LBOUND(SrcOutputData%Vind,2) + i2_u = UBOUND(SrcOutputData%Vind,2) + i3_l = LBOUND(SrcOutputData%Vind,3) + i3_u = UBOUND(SrcOutputData%Vind,3) + IF (.NOT. ALLOCATED(DstOutputData%Vind)) THEN + ALLOCATE(DstOutputData%Vind(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOutputData%Vind.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOutputData%Vind = SrcOutputData%Vind +ENDIF +IF (ALLOCATED(SrcOutputData%r_wind)) THEN + i1_l = LBOUND(SrcOutputData%r_wind,1) + i1_u = UBOUND(SrcOutputData%r_wind,1) + i2_l = LBOUND(SrcOutputData%r_wind,2) + i2_u = UBOUND(SrcOutputData%r_wind,2) + IF (.NOT. ALLOCATED(DstOutputData%r_wind)) THEN + ALLOCATE(DstOutputData%r_wind(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOutputData%r_wind.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOutputData%r_wind = SrcOutputData%r_wind +ENDIF +IF (ALLOCATED(SrcOutputData%Cl_KJ)) THEN + i1_l = LBOUND(SrcOutputData%Cl_KJ,1) + i1_u = UBOUND(SrcOutputData%Cl_KJ,1) + i2_l = LBOUND(SrcOutputData%Cl_KJ,2) + i2_u = UBOUND(SrcOutputData%Cl_KJ,2) + IF (.NOT. ALLOCATED(DstOutputData%Cl_KJ)) THEN + ALLOCATE(DstOutputData%Cl_KJ(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOutputData%Cl_KJ.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOutputData%Cl_KJ = SrcOutputData%Cl_KJ +ENDIF + END SUBROUTINE FVW_CopyOutput + + SUBROUTINE FVW_DestroyOutput( OutputData, ErrStat, ErrMsg ) + TYPE(FVW_OutputType), INTENT(INOUT) :: OutputData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyOutput' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(OutputData%Vind)) THEN + DEALLOCATE(OutputData%Vind) +ENDIF +IF (ALLOCATED(OutputData%r_wind)) THEN + DEALLOCATE(OutputData%r_wind) +ENDIF +IF (ALLOCATED(OutputData%Cl_KJ)) THEN + DEALLOCATE(OutputData%Cl_KJ) +ENDIF + END SUBROUTINE FVW_DestroyOutput + + SUBROUTINE FVW_PackOutput( 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(FVW_OutputType), 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 = 'FVW_PackOutput' + ! 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 ! Vind allocated yes/no + IF ( ALLOCATED(InData%Vind) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Vind upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vind) ! Vind + END IF + Int_BufSz = Int_BufSz + 1 ! r_wind allocated yes/no + IF ( ALLOCATED(InData%r_wind) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! r_wind upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%r_wind) ! r_wind + END IF + Int_BufSz = Int_BufSz + 1 ! Cl_KJ allocated yes/no + IF ( ALLOCATED(InData%Cl_KJ) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Cl_KJ upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Cl_KJ) ! Cl_KJ + END IF + 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 + + IF ( .NOT. ALLOCATED(InData%Vind) ) 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%Vind,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vind)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vind))-1 ) = PACK(InData%Vind,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vind) + END IF + IF ( .NOT. ALLOCATED(InData%r_wind) ) 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%r_wind,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_wind,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_wind,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_wind,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%r_wind)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%r_wind))-1 ) = PACK(InData%r_wind,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%r_wind) + END IF + IF ( .NOT. ALLOCATED(InData%Cl_KJ) ) 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%Cl_KJ,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Cl_KJ,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Cl_KJ,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Cl_KJ,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Cl_KJ)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Cl_KJ))-1 ) = PACK(InData%Cl_KJ,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Cl_KJ) + END IF + END SUBROUTINE FVW_PackOutput + + SUBROUTINE FVW_UnPackOutput( 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(FVW_OutputType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackOutput' + ! 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 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vind 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%Vind)) DEALLOCATE(OutData%Vind) + ALLOCATE(OutData%Vind(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%Vind.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Vind)>0) OutData%Vind = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vind))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vind) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! r_wind 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 + IF (ALLOCATED(OutData%r_wind)) DEALLOCATE(OutData%r_wind) + ALLOCATE(OutData%r_wind(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%r_wind.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%r_wind)>0) OutData%r_wind = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%r_wind))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%r_wind) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Cl_KJ 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 + IF (ALLOCATED(OutData%Cl_KJ)) DEALLOCATE(OutData%Cl_KJ) + ALLOCATE(OutData%Cl_KJ(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Cl_KJ.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%Cl_KJ)>0) OutData%Cl_KJ = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Cl_KJ))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Cl_KJ) + DEALLOCATE(mask2) + END IF + END SUBROUTINE FVW_UnPackOutput + + SUBROUTINE FVW_CopyContState( SrcContStateData, DstContStateData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_ContinuousStateType), INTENT(IN) :: SrcContStateData + TYPE(FVW_ContinuousStateType), INTENT(INOUT) :: DstContStateData + 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 = 'FVW_CopyContState' +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(SrcContStateData%Gamma_NW)) THEN + i1_l = LBOUND(SrcContStateData%Gamma_NW,1) + i1_u = UBOUND(SrcContStateData%Gamma_NW,1) + i2_l = LBOUND(SrcContStateData%Gamma_NW,2) + i2_u = UBOUND(SrcContStateData%Gamma_NW,2) + i3_l = LBOUND(SrcContStateData%Gamma_NW,3) + i3_u = UBOUND(SrcContStateData%Gamma_NW,3) + IF (.NOT. ALLOCATED(DstContStateData%Gamma_NW)) THEN + ALLOCATE(DstContStateData%Gamma_NW(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstContStateData%Gamma_NW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstContStateData%Gamma_NW = SrcContStateData%Gamma_NW +ENDIF +IF (ALLOCATED(SrcContStateData%Gamma_FW)) THEN + i1_l = LBOUND(SrcContStateData%Gamma_FW,1) + i1_u = UBOUND(SrcContStateData%Gamma_FW,1) + i2_l = LBOUND(SrcContStateData%Gamma_FW,2) + i2_u = UBOUND(SrcContStateData%Gamma_FW,2) + i3_l = LBOUND(SrcContStateData%Gamma_FW,3) + i3_u = UBOUND(SrcContStateData%Gamma_FW,3) + IF (.NOT. ALLOCATED(DstContStateData%Gamma_FW)) THEN + ALLOCATE(DstContStateData%Gamma_FW(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstContStateData%Gamma_FW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstContStateData%Gamma_FW = SrcContStateData%Gamma_FW +ENDIF +IF (ALLOCATED(SrcContStateData%r_NW)) THEN + i1_l = LBOUND(SrcContStateData%r_NW,1) + i1_u = UBOUND(SrcContStateData%r_NW,1) + i2_l = LBOUND(SrcContStateData%r_NW,2) + i2_u = UBOUND(SrcContStateData%r_NW,2) + i3_l = LBOUND(SrcContStateData%r_NW,3) + i3_u = UBOUND(SrcContStateData%r_NW,3) + i4_l = LBOUND(SrcContStateData%r_NW,4) + i4_u = UBOUND(SrcContStateData%r_NW,4) + IF (.NOT. ALLOCATED(DstContStateData%r_NW)) THEN + ALLOCATE(DstContStateData%r_NW(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 DstContStateData%r_NW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstContStateData%r_NW = SrcContStateData%r_NW +ENDIF +IF (ALLOCATED(SrcContStateData%r_FW)) THEN + i1_l = LBOUND(SrcContStateData%r_FW,1) + i1_u = UBOUND(SrcContStateData%r_FW,1) + i2_l = LBOUND(SrcContStateData%r_FW,2) + i2_u = UBOUND(SrcContStateData%r_FW,2) + i3_l = LBOUND(SrcContStateData%r_FW,3) + i3_u = UBOUND(SrcContStateData%r_FW,3) + i4_l = LBOUND(SrcContStateData%r_FW,4) + i4_u = UBOUND(SrcContStateData%r_FW,4) + IF (.NOT. ALLOCATED(DstContStateData%r_FW)) THEN + ALLOCATE(DstContStateData%r_FW(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 DstContStateData%r_FW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstContStateData%r_FW = SrcContStateData%r_FW +ENDIF + END SUBROUTINE FVW_CopyContState + + SUBROUTINE FVW_DestroyContState( ContStateData, ErrStat, ErrMsg ) + TYPE(FVW_ContinuousStateType), INTENT(INOUT) :: ContStateData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyContState' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(ContStateData%Gamma_NW)) THEN + DEALLOCATE(ContStateData%Gamma_NW) +ENDIF +IF (ALLOCATED(ContStateData%Gamma_FW)) THEN + DEALLOCATE(ContStateData%Gamma_FW) +ENDIF +IF (ALLOCATED(ContStateData%r_NW)) THEN + DEALLOCATE(ContStateData%r_NW) +ENDIF +IF (ALLOCATED(ContStateData%r_FW)) THEN + DEALLOCATE(ContStateData%r_FW) +ENDIF + END SUBROUTINE FVW_DestroyContState + + SUBROUTINE FVW_PackContState( 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(FVW_ContinuousStateType), 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 = 'FVW_PackContState' + ! 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 ! Gamma_NW allocated yes/no + IF ( ALLOCATED(InData%Gamma_NW) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Gamma_NW upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Gamma_NW) ! Gamma_NW + END IF + Int_BufSz = Int_BufSz + 1 ! Gamma_FW allocated yes/no + IF ( ALLOCATED(InData%Gamma_FW) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Gamma_FW upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Gamma_FW) ! Gamma_FW + END IF + Int_BufSz = Int_BufSz + 1 ! r_NW allocated yes/no + IF ( ALLOCATED(InData%r_NW) ) THEN + Int_BufSz = Int_BufSz + 2*4 ! r_NW upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%r_NW) ! r_NW + END IF + Int_BufSz = Int_BufSz + 1 ! r_FW allocated yes/no + IF ( ALLOCATED(InData%r_FW) ) THEN + Int_BufSz = Int_BufSz + 2*4 ! r_FW upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%r_FW) ! r_FW + END IF + 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 + + IF ( .NOT. ALLOCATED(InData%Gamma_NW) ) 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%Gamma_NW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_NW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Gamma_NW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_NW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Gamma_NW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_NW,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Gamma_NW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Gamma_NW))-1 ) = PACK(InData%Gamma_NW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Gamma_NW) + END IF + IF ( .NOT. ALLOCATED(InData%Gamma_FW) ) 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%Gamma_FW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_FW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Gamma_FW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_FW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Gamma_FW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_FW,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Gamma_FW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Gamma_FW))-1 ) = PACK(InData%Gamma_FW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Gamma_FW) + END IF + IF ( .NOT. ALLOCATED(InData%r_NW) ) 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%r_NW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_NW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_NW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_NW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_NW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_NW,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_NW,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_NW,4) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%r_NW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%r_NW))-1 ) = PACK(InData%r_NW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%r_NW) + END IF + IF ( .NOT. ALLOCATED(InData%r_FW) ) 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%r_FW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_FW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_FW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_FW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_FW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_FW,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_FW,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_FW,4) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%r_FW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%r_FW))-1 ) = PACK(InData%r_FW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%r_FW) + END IF + END SUBROUTINE FVW_PackContState + + SUBROUTINE FVW_UnPackContState( 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(FVW_ContinuousStateType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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 = 'FVW_UnPackContState' + ! 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 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Gamma_NW 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%Gamma_NW)) DEALLOCATE(OutData%Gamma_NW) + ALLOCATE(OutData%Gamma_NW(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%Gamma_NW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Gamma_NW)>0) OutData%Gamma_NW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Gamma_NW))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Gamma_NW) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Gamma_FW 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%Gamma_FW)) DEALLOCATE(OutData%Gamma_FW) + ALLOCATE(OutData%Gamma_FW(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%Gamma_FW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Gamma_FW)>0) OutData%Gamma_FW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Gamma_FW))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Gamma_FW) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! r_NW 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%r_NW)) DEALLOCATE(OutData%r_NW) + ALLOCATE(OutData%r_NW(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%r_NW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask4(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 mask4.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask4 = .TRUE. + IF (SIZE(OutData%r_NW)>0) OutData%r_NW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%r_NW))-1 ), mask4, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%r_NW) + DEALLOCATE(mask4) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! r_FW 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%r_FW)) DEALLOCATE(OutData%r_FW) + ALLOCATE(OutData%r_FW(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%r_FW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask4(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 mask4.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask4 = .TRUE. + IF (SIZE(OutData%r_FW)>0) OutData%r_FW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%r_FW))-1 ), mask4, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%r_FW) + DEALLOCATE(mask4) + END IF + END SUBROUTINE FVW_UnPackContState + + SUBROUTINE FVW_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_DiscreteStateType), INTENT(IN) :: SrcDiscStateData + TYPE(FVW_DiscreteStateType), INTENT(INOUT) :: DstDiscStateData + INTEGER(IntKi), INTENT(IN ) :: CtrlCode + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg +! Local + INTEGER(IntKi) :: i,j,k + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyDiscState' +! + ErrStat = ErrID_None + ErrMsg = "" + DstDiscStateData%Null = SrcDiscStateData%Null + END SUBROUTINE FVW_CopyDiscState + + SUBROUTINE FVW_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) + TYPE(FVW_DiscreteStateType), INTENT(INOUT) :: DiscStateData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyDiscState' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE FVW_DestroyDiscState + + SUBROUTINE FVW_PackDiscState( 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(FVW_DiscreteStateType), 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 = 'FVW_PackDiscState' + ! 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 ! Null + 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:Int_Xferred+(1)-1 ) = InData%Null + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_PackDiscState + + SUBROUTINE FVW_UnPackDiscState( 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(FVW_DiscreteStateType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackDiscState' + ! 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%Null = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_UnPackDiscState + + SUBROUTINE FVW_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_ConstraintStateType), INTENT(IN) :: SrcConstrStateData + TYPE(FVW_ConstraintStateType), INTENT(INOUT) :: DstConstrStateData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyConstrState' +! + ErrStat = ErrID_None + ErrMsg = "" + DstConstrStateData%residual = SrcConstrStateData%residual +IF (ALLOCATED(SrcConstrStateData%Gamma_LL)) THEN + i1_l = LBOUND(SrcConstrStateData%Gamma_LL,1) + i1_u = UBOUND(SrcConstrStateData%Gamma_LL,1) + i2_l = LBOUND(SrcConstrStateData%Gamma_LL,2) + i2_u = UBOUND(SrcConstrStateData%Gamma_LL,2) + IF (.NOT. ALLOCATED(DstConstrStateData%Gamma_LL)) THEN + ALLOCATE(DstConstrStateData%Gamma_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstConstrStateData%Gamma_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstConstrStateData%Gamma_LL = SrcConstrStateData%Gamma_LL +ENDIF + END SUBROUTINE FVW_CopyConstrState + + SUBROUTINE FVW_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) + TYPE(FVW_ConstraintStateType), INTENT(INOUT) :: ConstrStateData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyConstrState' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(ConstrStateData%Gamma_LL)) THEN + DEALLOCATE(ConstrStateData%Gamma_LL) +ENDIF + END SUBROUTINE FVW_DestroyConstrState + + SUBROUTINE FVW_PackConstrState( 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(FVW_ConstraintStateType), 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 = 'FVW_PackConstrState' + ! 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 + Re_BufSz = Re_BufSz + 1 ! residual + Int_BufSz = Int_BufSz + 1 ! Gamma_LL allocated yes/no + IF ( ALLOCATED(InData%Gamma_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Gamma_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Gamma_LL) ! Gamma_LL + END IF + 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 + + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%residual + Re_Xferred = Re_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%Gamma_LL) ) 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%Gamma_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Gamma_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Gamma_LL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Gamma_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Gamma_LL))-1 ) = PACK(InData%Gamma_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Gamma_LL) + END IF + END SUBROUTINE FVW_PackConstrState + + SUBROUTINE FVW_UnPackConstrState( 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(FVW_ConstraintStateType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackConstrState' + ! 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%residual = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Gamma_LL 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 + IF (ALLOCATED(OutData%Gamma_LL)) DEALLOCATE(OutData%Gamma_LL) + ALLOCATE(OutData%Gamma_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Gamma_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%Gamma_LL)>0) OutData%Gamma_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Gamma_LL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Gamma_LL) + DEALLOCATE(mask2) + END IF + END SUBROUTINE FVW_UnPackConstrState + + SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_InitInputType), INTENT(INOUT) :: SrcInitInputData + TYPE(FVW_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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyInitInput' +! + ErrStat = ErrID_None + ErrMsg = "" + DstInitInputData%FVWFileName = SrcInitInputData%FVWFileName +IF (ALLOCATED(SrcInitInputData%WingsMesh)) THEN + i1_l = LBOUND(SrcInitInputData%WingsMesh,1) + i1_u = UBOUND(SrcInitInputData%WingsMesh,1) + IF (.NOT. ALLOCATED(DstInitInputData%WingsMesh)) THEN + ALLOCATE(DstInitInputData%WingsMesh(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%WingsMesh.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DO i1 = LBOUND(SrcInitInputData%WingsMesh,1), UBOUND(SrcInitInputData%WingsMesh,1) + CALL MeshCopy( SrcInitInputData%WingsMesh(i1), DstInitInputData%WingsMesh(i1), CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + ENDDO +ENDIF +IF (ALLOCATED(SrcInitInputData%Chord)) THEN + i1_l = LBOUND(SrcInitInputData%Chord,1) + i1_u = UBOUND(SrcInitInputData%Chord,1) + IF (.NOT. ALLOCATED(DstInitInputData%Chord)) THEN + ALLOCATE(DstInitInputData%Chord(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%Chord.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%Chord = SrcInitInputData%Chord +ENDIF +IF (ALLOCATED(SrcInitInputData%RElm)) THEN + i1_l = LBOUND(SrcInitInputData%RElm,1) + i1_u = UBOUND(SrcInitInputData%RElm,1) + IF (.NOT. ALLOCATED(DstInitInputData%RElm)) THEN + ALLOCATE(DstInitInputData%RElm(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%RElm.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%RElm = SrcInitInputData%RElm +ENDIF + DstInitInputData%NumBl = SrcInitInputData%NumBl + END SUBROUTINE FVW_CopyInitInput + + SUBROUTINE FVW_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) + TYPE(FVW_InitInputType), INTENT(INOUT) :: InitInputData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyInitInput' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(InitInputData%WingsMesh)) THEN +DO i1 = LBOUND(InitInputData%WingsMesh,1), UBOUND(InitInputData%WingsMesh,1) + CALL MeshDestroy( InitInputData%WingsMesh(i1), ErrStat, ErrMsg ) +ENDDO + DEALLOCATE(InitInputData%WingsMesh) +ENDIF +IF (ALLOCATED(InitInputData%Chord)) THEN + DEALLOCATE(InitInputData%Chord) +ENDIF +IF (ALLOCATED(InitInputData%RElm)) THEN + DEALLOCATE(InitInputData%RElm) +ENDIF + END SUBROUTINE FVW_DestroyInitInput + + SUBROUTINE FVW_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(FVW_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 = 'FVW_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*LEN(InData%FVWFileName) ! FVWFileName + Int_BufSz = Int_BufSz + 1 ! WingsMesh allocated yes/no + IF ( ALLOCATED(InData%WingsMesh) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! WingsMesh upper/lower bounds for each dimension + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + DO i1 = LBOUND(InData%WingsMesh,1), UBOUND(InData%WingsMesh,1) + Int_BufSz = Int_BufSz + 3 ! WingsMesh: size of buffers for each call to pack subtype + CALL MeshPack( InData%WingsMesh(i1), Re_Buf, Db_Buf, Int_Buf, ErrStat2, ErrMsg2, .TRUE. ) ! WingsMesh + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! WingsMesh + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! WingsMesh + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! WingsMesh + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + END DO + END IF + Int_BufSz = Int_BufSz + 1 ! Chord allocated yes/no + IF ( ALLOCATED(InData%Chord) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! Chord upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Chord) ! Chord + END IF + Int_BufSz = Int_BufSz + 1 ! RElm allocated yes/no + IF ( ALLOCATED(InData%RElm) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! RElm upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%RElm) ! RElm + END IF + Int_BufSz = Int_BufSz + 1 ! NumBl + 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 + + DO I = 1, LEN(InData%FVWFileName) + IntKiBuf(Int_Xferred) = ICHAR(InData%FVWFileName(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I + IF ( .NOT. ALLOCATED(InData%WingsMesh) ) 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%WingsMesh,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%WingsMesh,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%WingsMesh,1), UBOUND(InData%WingsMesh,1) + CALL MeshPack( InData%WingsMesh(i1), Re_Buf, Db_Buf, Int_Buf, ErrStat2, ErrMsg2, OnlySize ) ! WingsMesh + 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 DO + END IF + IF ( .NOT. ALLOCATED(InData%Chord) ) 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%Chord,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Chord,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Chord)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Chord))-1 ) = PACK(InData%Chord,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Chord) + END IF + IF ( .NOT. ALLOCATED(InData%RElm) ) 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%RElm,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%RElm,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%RElm)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%RElm))-1 ) = PACK(InData%RElm,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%RElm) + END IF + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumBl + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_PackInitInput + + SUBROUTINE FVW_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(FVW_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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_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 + DO I = 1, LEN(OutData%FVWFileName) + OutData%FVWFileName(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! WingsMesh 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 + IF (ALLOCATED(OutData%WingsMesh)) DEALLOCATE(OutData%WingsMesh) + ALLOCATE(OutData%WingsMesh(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%WingsMesh.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%WingsMesh,1), UBOUND(OutData%WingsMesh,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 MeshUnpack( OutData%WingsMesh(i1), Re_Buf, Db_Buf, Int_Buf, ErrStat2, ErrMsg2 ) ! WingsMesh + 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 DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Chord 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 + IF (ALLOCATED(OutData%Chord)) DEALLOCATE(OutData%Chord) + ALLOCATE(OutData%Chord(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Chord.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%Chord)>0) OutData%Chord = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Chord))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Chord) + DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! RElm 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 + IF (ALLOCATED(OutData%RElm)) DEALLOCATE(OutData%RElm) + ALLOCATE(OutData%RElm(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%RElm.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%RElm)>0) OutData%RElm = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%RElm))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%RElm) + DEALLOCATE(mask1) + END IF + OutData%NumBl = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_UnPackInitInput + + SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_InputFile), INTENT(IN) :: SrcInputFileData + TYPE(FVW_InputFile), INTENT(INOUT) :: DstInputFileData + INTEGER(IntKi), INTENT(IN ) :: CtrlCode + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg +! Local + INTEGER(IntKi) :: i,j,k + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyInputFile' +! + ErrStat = ErrID_None + ErrMsg = "" + DstInputFileData%CirculationMethod = SrcInputFileData%CirculationMethod + DstInputFileData%CirculationFile = SrcInputFileData%CirculationFile + DstInputFileData%IntMethod = SrcInputFileData%IntMethod + DstInputFileData%FreeWake = SrcInputFileData%FreeWake + END SUBROUTINE FVW_CopyInputFile + + SUBROUTINE FVW_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) + TYPE(FVW_InputFile), INTENT(INOUT) :: InputFileData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyInputFile' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE FVW_DestroyInputFile + + SUBROUTINE FVW_PackInputFile( 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(FVW_InputFile), 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 = 'FVW_PackInputFile' + ! 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 ! CirculationMethod + Int_BufSz = Int_BufSz + 1*LEN(InData%CirculationFile) ! CirculationFile + Int_BufSz = Int_BufSz + 1 ! IntMethod + Int_BufSz = Int_BufSz + 1 ! FreeWake + 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:Int_Xferred+(1)-1 ) = InData%CirculationMethod + Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(InData%CirculationFile) + IntKiBuf(Int_Xferred) = ICHAR(InData%CirculationFile(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%IntMethod + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%FreeWake , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_PackInputFile + + SUBROUTINE FVW_UnPackInputFile( 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(FVW_InputFile), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackInputFile' + ! 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%CirculationMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(OutData%CirculationFile) + OutData%CirculationFile(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I + OutData%IntMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%FreeWake = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_UnPackInputFile + + SUBROUTINE FVW_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_InitOutputType), INTENT(IN) :: SrcInitOutputData + TYPE(FVW_InitOutputType), INTENT(INOUT) :: DstInitOutputData + INTEGER(IntKi), INTENT(IN ) :: CtrlCode + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg +! Local + INTEGER(IntKi) :: i,j,k + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyInitOutput' +! + ErrStat = ErrID_None + ErrMsg = "" + DstInitOutputData%Null = SrcInitOutputData%Null + END SUBROUTINE FVW_CopyInitOutput + + SUBROUTINE FVW_DestroyInitOutput( InitOutputData, ErrStat, ErrMsg ) + TYPE(FVW_InitOutputType), INTENT(INOUT) :: InitOutputData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyInitOutput' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE FVW_DestroyInitOutput + + SUBROUTINE FVW_PackInitOutput( 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(FVW_InitOutputType), 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 = 'FVW_PackInitOutput' + ! 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 ! Null + 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:Int_Xferred+(1)-1 ) = InData%Null + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_PackInitOutput + + SUBROUTINE FVW_UnPackInitOutput( 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(FVW_InitOutputType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackInitOutput' + ! 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%Null = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE FVW_UnPackInitOutput + + + SUBROUTINE FVW_Input_ExtrapInterp(u, t, u_out, t_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is given by the size of u +! +! expressions below based on either +! +! f(t) = a +! f(t) = a + b * t, or +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = u1, f(t2) = u2, f(t3) = u3 (as appropriate) +! +!.................................................................................................................................. + + TYPE(FVW_InputType), INTENT(INOUT) :: u(:) ! Input at t1 > t2 > t3 + REAL(DbKi), INTENT(IN ) :: t(:) ! Times associated with the Inputs + TYPE(FVW_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: t_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_Input_ExtrapInterp' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + if ( size(t) .ne. size(u)) then + CALL SetErrStat(ErrID_Fatal,'size(t) must equal size(u)',ErrStat,ErrMsg,RoutineName) + RETURN + endif + order = SIZE(u) - 1 + IF ( order .eq. 0 ) THEN + CALL FVW_CopyInput(u(1), u_out, MESH_UPDATECOPY, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE IF ( order .eq. 1 ) THEN + CALL FVW_Input_ExtrapInterp1(u(1), u(2), t, u_out, t_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE IF ( order .eq. 2 ) THEN + CALL FVW_Input_ExtrapInterp2(u(1), u(2), u(3), t, u_out, t_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE + CALL SetErrStat(ErrID_Fatal,'size(u) must be less than 4 (order must be less than 3).',ErrStat,ErrMsg,RoutineName) + RETURN + ENDIF + END SUBROUTINE FVW_Input_ExtrapInterp + + + SUBROUTINE FVW_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is 1. +! +! f(t) = a + b * t, or +! +! where a and b are determined as the solution to +! f(t1) = u1, f(t2) = u2 +! +!.................................................................................................................................. + + TYPE(FVW_InputType), INTENT(INOUT) :: u1 ! Input at t1 > t2 + TYPE(FVW_InputType), INTENT(INOUT) :: u2 ! Input at t2 + REAL(DbKi), INTENT(IN ) :: tin(2) ! Times associated with the Inputs + TYPE(FVW_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(2) ! Times associated with the Inputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_Input_ExtrapInterp1' + REAL(DbKi) :: b0 ! temporary for extrapolation/interpolation + REAL(DbKi) :: c0 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: b1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: c1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: b2 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: c2 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF +IF (ALLOCATED(u_out%WingsMesh) .AND. ALLOCATED(u1%WingsMesh)) THEN + DO i01 = LBOUND(u_out%WingsMesh,1),UBOUND(u_out%WingsMesh,1) + CALL MeshExtrapInterp1(u1%WingsMesh(i01), u2%WingsMesh(i01), tin, u_out%WingsMesh(i01), tin_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ENDDO +END IF ! check if allocated +IF (ALLOCATED(u_out%V_wind) .AND. ALLOCATED(u1%V_wind)) THEN + ALLOCATE(b2(SIZE(u_out%V_wind,1),SIZE(u_out%V_wind,2) )) + ALLOCATE(c2(SIZE(u_out%V_wind,1),SIZE(u_out%V_wind,2) )) + b2 = -(u1%V_wind - u2%V_wind)/t(2) + u_out%V_wind = u1%V_wind + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) +END IF ! check if allocated + END SUBROUTINE FVW_Input_ExtrapInterp1 + + + SUBROUTINE FVW_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is 2. +! +! expressions below based on either +! +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = u1, f(t2) = u2, f(t3) = u3 +! +!.................................................................................................................................. + + TYPE(FVW_InputType), INTENT(INOUT) :: u1 ! Input at t1 > t2 > t3 + TYPE(FVW_InputType), INTENT(INOUT) :: u2 ! Input at t2 > t3 + TYPE(FVW_InputType), INTENT(INOUT) :: u3 ! Input at t3 + REAL(DbKi), INTENT(IN ) :: tin(3) ! Times associated with the Inputs + TYPE(FVW_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(3) ! Times associated with the Inputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + REAL(DbKi) :: b0 ! temporary for extrapolation/interpolation + REAL(DbKi) :: c0 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: b1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: c1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: b2 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: c2 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_Input_ExtrapInterp2' + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(2), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(2) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(1), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF +IF (ALLOCATED(u_out%WingsMesh) .AND. ALLOCATED(u1%WingsMesh)) THEN + DO i01 = LBOUND(u_out%WingsMesh,1),UBOUND(u_out%WingsMesh,1) + CALL MeshExtrapInterp2(u1%WingsMesh(i01), u2%WingsMesh(i01), u3%WingsMesh(i01), tin, u_out%WingsMesh(i01), tin_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ENDDO +END IF ! check if allocated +IF (ALLOCATED(u_out%V_wind) .AND. ALLOCATED(u1%V_wind)) THEN + ALLOCATE(b2(SIZE(u_out%V_wind,1),SIZE(u_out%V_wind,2) )) + ALLOCATE(c2(SIZE(u_out%V_wind,1),SIZE(u_out%V_wind,2) )) + b2 = (t(3)**2*(u1%V_wind - u2%V_wind) + t(2)**2*(-u1%V_wind + u3%V_wind))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%V_wind + t(3)*u2%V_wind - t(2)*u3%V_wind ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%V_wind = u1%V_wind + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) +END IF ! check if allocated + END SUBROUTINE FVW_Input_ExtrapInterp2 + + + SUBROUTINE FVW_Output_ExtrapInterp(y, t, y_out, t_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is given by the size of y +! +! expressions below based on either +! +! f(t) = a +! f(t) = a + b * t, or +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = y1, f(t2) = y2, f(t3) = y3 (as appropriate) +! +!.................................................................................................................................. + + TYPE(FVW_OutputType), INTENT(INOUT) :: y(:) ! Output at t1 > t2 > t3 + REAL(DbKi), INTENT(IN ) :: t(:) ! Times associated with the Outputs + TYPE(FVW_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: t_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_Output_ExtrapInterp' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + if ( size(t) .ne. size(y)) then + CALL SetErrStat(ErrID_Fatal,'size(t) must equal size(y)',ErrStat,ErrMsg,RoutineName) + RETURN + endif + order = SIZE(y) - 1 + IF ( order .eq. 0 ) THEN + CALL FVW_CopyOutput(y(1), y_out, MESH_UPDATECOPY, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE IF ( order .eq. 1 ) THEN + CALL FVW_Output_ExtrapInterp1(y(1), y(2), t, y_out, t_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE IF ( order .eq. 2 ) THEN + CALL FVW_Output_ExtrapInterp2(y(1), y(2), y(3), t, y_out, t_out, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + ELSE + CALL SetErrStat(ErrID_Fatal,'size(y) must be less than 4 (order must be less than 3).',ErrStat,ErrMsg,RoutineName) + RETURN + ENDIF + END SUBROUTINE FVW_Output_ExtrapInterp + + + SUBROUTINE FVW_Output_ExtrapInterp1(y1, y2, tin, y_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is 1. +! +! f(t) = a + b * t, or +! +! where a and b are determined as the solution to +! f(t1) = y1, f(t2) = y2 +! +!.................................................................................................................................. + + TYPE(FVW_OutputType), INTENT(INOUT) :: y1 ! Output at t1 > t2 + TYPE(FVW_OutputType), INTENT(INOUT) :: y2 ! Output at t2 + REAL(DbKi), INTENT(IN ) :: tin(2) ! Times associated with the Outputs + TYPE(FVW_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(2) ! Times associated with the Outputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_Output_ExtrapInterp1' + REAL(DbKi) :: b0 ! temporary for extrapolation/interpolation + REAL(DbKi) :: c0 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: b1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: c1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: b2 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: c2 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:,:) :: b3 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:,:) :: c3 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF +IF (ALLOCATED(y_out%Vind) .AND. ALLOCATED(y1%Vind)) THEN + ALLOCATE(b3(SIZE(y_out%Vind,1),SIZE(y_out%Vind,2), & + SIZE(y_out%Vind,3) )) + ALLOCATE(c3(SIZE(y_out%Vind,1),SIZE(y_out%Vind,2), & + SIZE(y_out%Vind,3) )) + b3 = -(y1%Vind - y2%Vind)/t(2) + y_out%Vind = y1%Vind + b3 * t_out + DEALLOCATE(b3) + DEALLOCATE(c3) +END IF ! check if allocated +IF (ALLOCATED(y_out%r_wind) .AND. ALLOCATED(y1%r_wind)) THEN + ALLOCATE(b2(SIZE(y_out%r_wind,1),SIZE(y_out%r_wind,2) )) + ALLOCATE(c2(SIZE(y_out%r_wind,1),SIZE(y_out%r_wind,2) )) + b2 = -(y1%r_wind - y2%r_wind)/t(2) + y_out%r_wind = y1%r_wind + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) +END IF ! check if allocated +IF (ALLOCATED(y_out%Cl_KJ) .AND. ALLOCATED(y1%Cl_KJ)) THEN + ALLOCATE(b2(SIZE(y_out%Cl_KJ,1),SIZE(y_out%Cl_KJ,2) )) + ALLOCATE(c2(SIZE(y_out%Cl_KJ,1),SIZE(y_out%Cl_KJ,2) )) + b2 = -(y1%Cl_KJ - y2%Cl_KJ)/t(2) + y_out%Cl_KJ = y1%Cl_KJ + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) +END IF ! check if allocated + END SUBROUTINE FVW_Output_ExtrapInterp1 + + + SUBROUTINE FVW_Output_ExtrapInterp2(y1, y2, y3, tin, y_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is 2. +! +! expressions below based on either +! +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = y1, f(t2) = y2, f(t3) = y3 +! +!.................................................................................................................................. + + TYPE(FVW_OutputType), INTENT(INOUT) :: y1 ! Output at t1 > t2 > t3 + TYPE(FVW_OutputType), INTENT(INOUT) :: y2 ! Output at t2 > t3 + TYPE(FVW_OutputType), INTENT(INOUT) :: y3 ! Output at t3 + REAL(DbKi), INTENT(IN ) :: tin(3) ! Times associated with the Outputs + TYPE(FVW_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(3) ! Times associated with the Outputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + REAL(DbKi) :: b0 ! temporary for extrapolation/interpolation + REAL(DbKi) :: c0 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: b1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:) :: c1 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: b2 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:) :: c2 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:,:) :: b3 ! temporary for extrapolation/interpolation + REAL(DbKi),ALLOCATABLE,DIMENSION(:,:,:) :: c3 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_Output_ExtrapInterp2' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(2), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(2) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(1), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF +IF (ALLOCATED(y_out%Vind) .AND. ALLOCATED(y1%Vind)) THEN + ALLOCATE(b3(SIZE(y_out%Vind,1),SIZE(y_out%Vind,2), & + SIZE(y_out%Vind,3) )) + ALLOCATE(c3(SIZE(y_out%Vind,1),SIZE(y_out%Vind,2), & + SIZE(y_out%Vind,3) )) + b3 = (t(3)**2*(y1%Vind - y2%Vind) + t(2)**2*(-y1%Vind + y3%Vind))/(t(2)*t(3)*(t(2) - t(3))) + c3 = ( (t(2)-t(3))*y1%Vind + t(3)*y2%Vind - t(2)*y3%Vind ) / (t(2)*t(3)*(t(2) - t(3))) + y_out%Vind = y1%Vind + b3 * t_out + c3 * t_out**2 + DEALLOCATE(b3) + DEALLOCATE(c3) +END IF ! check if allocated +IF (ALLOCATED(y_out%r_wind) .AND. ALLOCATED(y1%r_wind)) THEN + ALLOCATE(b2(SIZE(y_out%r_wind,1),SIZE(y_out%r_wind,2) )) + ALLOCATE(c2(SIZE(y_out%r_wind,1),SIZE(y_out%r_wind,2) )) + b2 = (t(3)**2*(y1%r_wind - y2%r_wind) + t(2)**2*(-y1%r_wind + y3%r_wind))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*y1%r_wind + t(3)*y2%r_wind - t(2)*y3%r_wind ) / (t(2)*t(3)*(t(2) - t(3))) + y_out%r_wind = y1%r_wind + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) +END IF ! check if allocated +IF (ALLOCATED(y_out%Cl_KJ) .AND. ALLOCATED(y1%Cl_KJ)) THEN + ALLOCATE(b2(SIZE(y_out%Cl_KJ,1),SIZE(y_out%Cl_KJ,2) )) + ALLOCATE(c2(SIZE(y_out%Cl_KJ,1),SIZE(y_out%Cl_KJ,2) )) + b2 = (t(3)**2*(y1%Cl_KJ - y2%Cl_KJ) + t(2)**2*(-y1%Cl_KJ + y3%Cl_KJ))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*y1%Cl_KJ + t(3)*y2%Cl_KJ - t(2)*y3%Cl_KJ ) / (t(2)*t(3)*(t(2) - t(3))) + y_out%Cl_KJ = y1%Cl_KJ + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) +END IF ! check if allocated + END SUBROUTINE FVW_Output_ExtrapInterp2 + +END MODULE FVW_Types +!ENDOFREGISTRYGENERATEDFILE diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn14/src/FVW_VortexTools.f90 new file mode 100644 index 0000000000..7e75e9ad45 --- /dev/null +++ b/modules/aerodyn14/src/FVW_VortexTools.f90 @@ -0,0 +1,11 @@ +MODULE FVW_VortexTools + ! Contains Typical Tools for vortex methods + ! Should be independent of the Framework and only low level functions + + IMPLICIT NONE + +CONTAINS + + + +END MODULE FVW_VortexTools diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 new file mode 100644 index 0000000000..b49a6706c0 --- /dev/null +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -0,0 +1,194 @@ +module FVW_Wings + + use NWTC_Library + use FVW_Types + use FVW_Subs + + implicit none + +contains + + !---------------------------------------------------------------------------------------------------------------------------------- + !> Based on an input mesh, sets the following: + !! - s_LL : Dimensionless spanwise coordinate of LL + !! - s_CP_LL : Dimensionless spanwise coordinate of LL CP + !! - chord_LL : chord on LL + !! - chord_LL_CP: chord on LL cp + subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) + type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh + real(ReKi), dimension(:), intent(in ) :: r !< + real(ReKi), dimension(:), intent(in ) :: chord !< + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + integer(IntKi) :: iW, iSpan + real(ReKi), dimension(3) :: First, Last, P1, P2, Pmid, DP + real(ReKi) :: ds, length + real(ReKi) :: c1,c2 + real(ReKi), dimension(:),allocatable :: s_in !< Dimensionless spanwise coordinate of input + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! --- Meshing + do iW = 1,p%nWings + if (allocated(s_in)) deallocate(s_in) + allocate(s_in(1:Meshes(iW)%nNodes)) + ! --- Computing spanwise coordinate of input mesh normalized from 0 to 1 + s_in(:) = -999 + First = Meshes(iW)%Position(1:3,1 ) + Last = Meshes(iW)%Position(1:3,p%nSpan+1) + DP = Last - First + length = TwoNorm(DP) + do iSpan = 1, Meshes(iW)%nNodes + P1 = Meshes(iW)%Position(1:3, iSpan ) + DP = P1-First + s_in(iSpan) = TwoNorm(DP) / length + enddo + + ! --- Setting up Lifting line variables based on input and a "meshing" method (TODO) + if (Meshes(iW)%nNodes /= p%nSpan+1) then + ! TODO Possibly interpolate based on FVW meshing + print*,'TODO different discretization InputMesh / vortex code' + STOP + endif + print*,'Input mesh size',Meshes(iW)%nNodes,' Number of vortex element', p%nSpan + do iSpan = 1, p%nSpan+1 + m%s_LL (iSpan, iW) = s_in(iSpan) + m%chord_LL(iSpan, iW) = chord(iSpan) + enddo + ! --- Control points + ! TODO possibly Control points are not exactly at the middle depending on "meshing" method + do iSpan = 1, p%nSpan + m%s_CP_LL (iSpan, iW) = (m%s_LL (iSpan,iW)+ m%s_LL (iSpan+1,iW))/2 + m%chord_LL(iSpan, iW) = (m%chord_LL(iSpan,iW)+ m%chord_LL(iSpan+1,iW))/2 + enddo + enddo + end subroutine Wings_Panelling_Init + + !---------------------------------------------------------------------------------------------------------------------------------- + !> Based on an input mesh, sets the following: + !! - LE : Leading edge points (3 x nSpan+1 x nWings) + !! - TE : Trailing edge points (3 x nSpan+1 x nWings) + !! - CP_LL : Coordinates of LL CP" (3 x nSpan x nWings) + !! - Tang : Unit Tangential vector on LL CP" - + !! - Norm : Unit Normal vector on LL CP " - + !! - Orth : Unit Orthogonal vector on LL CP" - + !! - Vstr_LL : Structural velocity on LL CP" m/s + subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) + use Interpolation, only: interp_lin + type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + integer(IntKi) ::iSpan , iW + real(ReKi), dimension(3) :: P_ref ! Reference point of Input Mesh (e.g. AeroDynamic Center?) + real(ReKi), dimension(3) :: DP_LE ! Distance between reference point and Leading edge + real(ReKi), dimension(3) :: DP_TE ! Distance between reference point and trailing edge + real(ReKi), dimension(3) :: P1,P2,P3,P4,P5,P7,P8,P6,P9,P10 + real(ReKi), dimension(3) :: DP1, DP2, DP3 + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! --- Position of leading edge and trailing edge + ! TODO, this assumes one to one between InputMesh and FVW Mesh + ! + do iW = 1,p%nWings + do iSpan = 1,p%nSpan+1 + P_ref = Meshes(iW)%Position(1:3, iSpan ) + DP_LE(1:3) = 0.0 + DP_LE(1) = +m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 + DP_TE(1:3) = 0.0 + DP_TE(1) = -m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 + m%LE(1:3, iSpan, iW) = P_ref + DP_LE + m%TE(1:3, iSpan, iW) = P_ref + DP_TE + enddo + enddo + ! --- Generic code below to compute normal/tangential vectors of a lifting line panel + ! Notations follow vanGarrel [TODO REF] + do iW = 1,p%nWings + do iSpan = 1,p%nSpan+1 + P1 = m%LE(:,iSpan , iw) + P4 = m%LE(:,iSpan+1, iw) + P3 = m%TE(:,iSpan+1, iw) + P2 = m%TE(:,iSpan , iw) + P8 = (P1+P4)/2 + P6 = (P2+P3)/2 + P5 = (P1+P2)/2 + P7 = (P4+P3)/2 + P9 = 0.75_ReKi*P1+0.25_ReKi*P2 + P10 = 0.75_ReKi*P4+0.25_ReKi*P3 + DP1 = P6-P8 + DP2 = P10-P9 + DP3 = P7-P5 + m%Norm(1:3,iSpan,iW) = cross_product(DP1,DP2) + m%Norm(1:3,iSpan,iW) = m%Norm(1:3,iSpan,iW)/norm2(m%Norm(1:3,iSpan,iW)) + m%Tang(1:3,iSpan,iW) = (DP1)/norm2(DP1) ! tangential unit vector, along chord + ! m%Tscoord(1:3,iSpan) = (DP3)/norm2(DP3) ! tangential unit vector, along span, follows ref line + ! m%dl(1:3,iSpan) = DP2 + m%Orth(1:3,iSpan,iW) = cross_product(m%Norm(1:3,iSpan,iW),m%Tang(1:3,iSpan,iW)) ! orthogonal vector to N and T + end do + enddo + + ! --- Position of control points + ! NOTE: separated from other loops just in case a special discretization is used + do iW = 1,p%nWings + call interp_lin(m%s_LL(:,iW), m%TE(1,:,iW)*0.25_ReKi+m%LE(1,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(1,:,iW)) + call interp_lin(m%s_LL(:,iW), m%TE(2,:,iW)*0.25_ReKi+m%LE(2,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(2,:,iW)) + call interp_lin(m%s_LL(:,iW), m%TE(3,:,iW)*0.25_ReKi+m%LE(3,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(3,:,iW)) + enddo + end subroutine Wings_Panelling + + + + !---------------------------------------------------------------------------------------------------------------------------------- + !> + subroutine Wings_ComputeCirculation(Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg) + real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines + real(ReKi), dimension(:,:), intent(in ) :: Gamma_LL_prev !< Previous/Guessed circulation + type(FVW_InputType), intent(in ) :: u !< Parameters + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local + integer(IntKi) :: iW + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + if (p%CirculationMethod==idCircPrescribed) then + print*,'>>>Prescribing circulation' + do iW = 1, p%nWings !Loop over lifting lines + Gamma_LL(1:p%nSpan,iW) = p%PrescribedCirculation(1:p%nSpan) + enddo + + else if (p%CirculationMethod==idCircPolarData) then + ! --- Solve for circulation using polar data + ! TODO + print*,'Circulation method nor implemented', p%CirculationMethod + STOP + + else if (p%CirculationMethod==idCircNoFlowThrough) then + ! --- Solve for circulation using the no-flow through condition + ! TODO + print*,'Circulation method nor implemented', p%CirculationMethod + STOP + else + print*,'Circulation method nor implemented', p%CirculationMethod ! Will never happen + STOP + endif + + endsubroutine Wings_ComputeCirculation + +end module FVW_Wings diff --git a/modules/aerodyn14/src/GenSubs.f90 b/modules/aerodyn14/src/GenSubs.f90 index 54fe3e9f57..e02fd5cbc4 100644 --- a/modules/aerodyn14/src/GenSubs.f90 +++ b/modules/aerodyn14/src/GenSubs.f90 @@ -332,6 +332,9 @@ SUBROUTINE AllocArrays ( InitInp, P, xc, xd, z, m, y, Arg ) IF (.NOT. ALLOCATED(m%ElOut%ReyNum)) ALLOCATE ( m%ElOut%ReyNum(NumElOut) , STAT=Sttus ) IF ( Sttus /= 0 ) CALL ProgAbort ( ' Error allocating memory for ReyNum array.' ) + IF (.NOT. ALLOCATED(m%ElOut%Gamma)) ALLOCATE ( m%ElOut%Gamma(NumElOut) , STAT=Sttus ) + IF ( Sttus /= 0 ) CALL ProgAbort ( ' Error allocating memory for Gamma array.' ) + IF (.NOT. ALLOCATED(m%ElOut%ElPrNum)) ALLOCATE ( m%ElOut%ElPrNum(NumElOut) , STAT=Sttus ) IF ( Sttus /= 0 ) CALL ProgAbort ( ' Error allocating memory for ElPrNum array.' ) m%ElOut%ElPrNum ( : ) = 0 @@ -462,7 +465,7 @@ SUBROUTINE ElemOpen (ElemFile, P, m, ErrStat, ErrMsg, AD14_Ver ) Frmt = '( A4, 3(A1,A2,I2.2), (: A1, A, I2.2 ) )' IF ( p%PMOMENT ) THEN - WRITE(Frmt(22:24), '(I3)') 14*m%ElOut%NumElOut + WRITE(Frmt(22:24), '(I3)') 15*m%ElOut%NumElOut WRITE(p%UnElem, Frmt) 'Time', & TAB, 'VX', p%Element%NELM, & TAB, 'VY', p%Element%NELM, & @@ -481,10 +484,11 @@ SUBROUTINE ElemOpen (ElemFile, P, m, ErrStat, ErrMsg, AD14_Ver ) TAB, 'ForcT', m%ElOut%ElPrNum(JE), & TAB, 'Pmomt', m%ElOut%ElPrNum(JE), & TAB, 'ReNum', m%ElOut%ElPrNum(JE), & + TAB, 'Gamma', m%ElOut%ElPrNum(JE), & JE = 1, m%ElOut%NumElOut ) Frmt = '( A5, 3(A1,A8), (: A1, A ) )' - WRITE(Frmt(17:19), '(I3)') 14*m%ElOut%NumElOut + WRITE(Frmt(17:19), '(I3)') 15*m%ElOut%NumElOut WRITE(p%UnElem, Frmt) '(sec)', & TAB, '('//TRIM(Dst_Unit)//'/sec)', & TAB, '('//TRIM(Dst_Unit)//'/sec)', & @@ -503,10 +507,11 @@ SUBROUTINE ElemOpen (ElemFile, P, m, ErrStat, ErrMsg, AD14_Ver ) TAB, '('//TRIM(Frc_Unit)//')', & TAB, '('//TRIM(Frc_Unit)//'-'//TRIM(Dst_Unit)//')', & TAB, '(x10^6)', & + TAB, '(m^2/sec)', & JE = 1, m%ElOut%NumElOut ) ELSE - WRITE(Frmt(22:24), '(I3)') 12*m%ElOut%NumElOut + WRITE(Frmt(22:24), '(I3)') 13*m%ElOut%NumElOut WRITE(p%UnElem, Frmt) 'Time', & TAB, 'VX', p%Element%NELM, & TAB, 'VY', p%Element%NELM, & @@ -523,10 +528,11 @@ SUBROUTINE ElemOpen (ElemFile, P, m, ErrStat, ErrMsg, AD14_Ver ) TAB, 'ForcN', m%ElOut%ElPrNum(JE), & TAB, 'ForcT', m%ElOut%ElPrNum(JE), & TAB, 'ReNum', m%ElOut%ElPrNum(JE), & + TAB, 'Gamma', m%ElOut%ElPrNum(JE), & JE = 1, m%ElOut%NumElOut ) Frmt = '( A5, 3(A1,A8), (: A1, A ) )' - WRITE(Frmt(17:19), '(I3)') 12*m%ElOut%NumElOut + WRITE(Frmt(17:19), '(I3)') 13*m%ElOut%NumElOut WRITE(p%UnElem, Frmt) '(sec)', & TAB, '('//TRIM(Dst_Unit)//'/sec)', & TAB, '('//TRIM(Dst_Unit)//'/sec)', & @@ -543,6 +549,7 @@ SUBROUTINE ElemOpen (ElemFile, P, m, ErrStat, ErrMsg, AD14_Ver ) TAB, '('//TRIM(Frc_Unit)//')', & TAB, '('//TRIM(Frc_Unit)//')', & TAB, '(x10^6)', & + TAB, '(m^2/sec)', & JE = 1, m%ElOut%NumElOut ) ENDIF @@ -596,7 +603,7 @@ SUBROUTINE ElemOut( time, P, m ) Frmt = '( F10.3, ( : A1, ES12.5 ) )' IF ( P%PMOMENT ) THEN - WRITE(Frmt(10:12), '(I3)') 14*m%ElOut%NumElOut + 3 + WRITE(Frmt(10:12), '(I3)') 15*m%ElOut%NumElOut + 3 WRITE(p%UnElem,Frmt) TIME, & TAB, m%ElOut%VXSAV, & TAB, m%ElOut%VYSAV, & @@ -615,11 +622,12 @@ SUBROUTINE ElemOut( time, P, m ) TAB, m%ElOut%DFTSAV (JE), & TAB, m%ElOut%PMM (JE), & TAB, m%ElOut%ReyNum (JE), & + TAB, m%ElOut%Gamma (JE), & JE= 1, m%ElOut%NumElOut ) ELSE - WRITE(Frmt(10:12), '(I3)') 12*m%ElOut%NumElOut + 3 + WRITE(Frmt(10:12), '(I3)') 13*m%ElOut%NumElOut + 3 WRITE(p%UnElem,Frmt) TIME, & TAB, m%ElOut%VXSAV, & TAB, m%ElOut%VYSAV, & @@ -636,6 +644,7 @@ SUBROUTINE ElemOut( time, P, m ) TAB, m%ElOut%DFNSAV (JE), & TAB, m%ElOut%DFTSAV (JE), & TAB, m%ElOut%ReyNum (JE), & + TAB, m%ElOut%Gamma (JE), & JE= 1, m%ElOut%NumElOut ) ENDIF ! PMOMENT diff --git a/modules/aerodyn14/src/Interpolation.f90 b/modules/aerodyn14/src/Interpolation.f90 new file mode 100644 index 0000000000..864ebc6fca --- /dev/null +++ b/modules/aerodyn14/src/Interpolation.f90 @@ -0,0 +1,129 @@ +!> +module Interpolation + use NWTC_Library + implicit none + + interface interp_lin; module procedure interp_lin11, interp_lin10 + end interface + + private + + public:: interp_lin + public:: interp_lin0 + +contains + + !-------------------------------------------------------------------- + ! --- Linear Interp + !-------------------------------------------------------------------- + function interp_lin0(x,x0,x1,f0,f1) ! Linear interpolation function + real(ReKi) ::interp_lin0 + real(ReKi),intent(in):: x,x0,x1,f0,f1 + real(ReKi) :: eps=1.0e-7 + if (abs(x0-x1) Linear interpolation between one dim arrays. ASSUMES xknown to have increasing values + subroutine interp_lin11_incr(xknown,yknown,xnew,ynew) + real(ReKi),dimension(:),intent(in):: xknown,yknown + real(ReKi),dimension(:),intent(in):: xnew + real(ReKi),dimension(:),intent(out):: ynew + integer i + integer itmp + integer:: nknown + nknown=size(xknown) + do i=1,size(xnew) + itmp=minloc(abs(xnew(i)-xknown),dim=1) + if (itmp==nknown) then + if (xknown(itmp)>xnew(i)) then + ynew(i)=interp_lin0(xnew(i),xknown(itmp-1),xknown(itmp),yknown(itmp-1),yknown(itmp)) + else + ! The current x is above the max of xknown + ! extrapolation required, here fixed to upper bound + ynew(i)=yknown(nknown) + endif + elseif (xknown(itmp) + subroutine interp_lin11(xknown,yknown,xnew,ynew) ! Linear interpolation between one dim arrays + real(ReKi),dimension(:),intent(in):: xknown,yknown + real(ReKi),dimension(:),intent(in):: xnew + real(ReKi),dimension(:),intent(out):: ynew + ! + integer :: n + n=size(xknown) + if(n<2) then + print*, 'interp_lin error, input argument should be at least of size 2' + elseif(size(yknown)/=n) then + print*, 'interp_lin error, dimension of two first inputs mismatch' + elseif(size(xnew)/=size(ynew)) then + print*, 'interp_lin error, dimension of two last inputs mismatch' + else + if(xknown(2)>xknown(1)) then + ! increasing x values + call interp_lin11_incr(xknown,yknown,xnew,ynew) + else + ! decreasing x values + call interp_lin11_incr(xknown(n:1:-1),yknown(n:1:-1),xnew,ynew) + endif + endif + + end subroutine + + + subroutine interp_lin10(xknown,yknown,xnew,ynew) ! Linear interpolation between one dim array at scalar location + real(ReKi),dimension(:),intent(in):: xknown,yknown + real(ReKi),intent(in):: xnew + real(ReKi),intent(out):: ynew + integer itmp + integer:: nknown + nknown=size(xknown) + if(size(xknown)/=size(yknown)) then + print*, 'interp_lin error, dimension of two first inputs mismatch' + else + if(xknown(2)xnew) then + ynew=interp_lin0(xnew,xknown(itmp-1),xknown(itmp),yknown(itmp-1),yknown(itmp)) + else + ! The current x is above the max of xknown + ! extrapolation required, here fixed to upper bound + ynew=yknown(nknown) + endif + elseif (xknown(itmp) +# +# Use ^ as a shortcut for the value in the same column from the previous line. +################################################################################################################################### +include Registry_NWTC_Library.txt + + +## This bit is redundant with AD14 registry. Could not figure out how to have both this and the AD14 registry use a third common chunk +# AeroDyn Subtypes +typedef AD14AeroConf/AD14AeroConf Marker Reki Position 3 0.0 - - +typedef ^ Marker ^ Orientation {3}{3} 0.0 - - +typedef ^ Marker ^ TranslationVel 3 0.0 - - +typedef ^ Marker ^ RotationVel 3 0.0 - - + + +# Airfoil +typedef AD14AeroConf/AD14AeroConf MiscVarType ReKi AL :: - - - +typedef ^ MiscVarType ReKi CD ::: - - - +typedef ^ MiscVarType ReKi CL ::: - - - +typedef ^ MiscVarType ReKi CM ::: - - - +typedef ^ MiscVarType ReKi PMC - - - - +typedef ^ MiscVarType ReKi MulTabLoc - - - - + +# Parameters: this used to be called AirFoilParms +typedef ^ ParameterType IntKi MaxTable - 20 - - +typedef ^ ParameterType IntKi NTables : - - - +typedef ^ ParameterType IntKi NLift : - - - +typedef ^ ParameterType IntKi NumCL - - - - +typedef ^ ParameterType IntKi NumFoil - - - - +typedef ^ ParameterType IntKi NFoil : - - - +typedef ^ ParameterType ReKi MulTabMet :: - - - +typedef ^ ParameterType CHARACTER(1024) FoilNm : "Number of airfoil data sets" - - + + +# Aero input-type --> this used to be called AeroConfig +typedef AD14AeroConf/AD14AeroConf InputType Marker Blade : - - - +typedef ^ InputType ^ Hub - - - - +typedef ^ InputType ^ RotorFurl - - - - +typedef ^ InputType ^ Nacelle - - - - +typedef ^ InputType ^ TailFin - - - - +typedef ^ InputType ^ Tower - - - - +typedef ^ InputType ^ SubStructure - - - - +typedef ^ InputType ^ Foundation - - - - +typedef ^ InputType ReKi BladeLength - - - - + +# Dummy outputtype so that the registry is happy +typedef AD14AeroConf/AD14AeroConf OutputType ReKi Dummy - - - - + diff --git a/modules/aerodyn14/src/Registry-FVW.txt b/modules/aerodyn14/src/Registry-FVW.txt new file mode 100644 index 0000000000..2a4e1585a5 --- /dev/null +++ b/modules/aerodyn14/src/Registry-FVW.txt @@ -0,0 +1,100 @@ +################################################################################################################################## +# Registry for FVW +# Entries are of the form +# keyword "" "" +################################################################################################################################## +include Registry_NWTC_Library.txt +usefrom InflowWind.txt +usefrom Registry-AD14AeroConf.txt + +##################### Registry for FVW ############### +# ..... PARAMETERS ............. +#FVW_ParameterType +typedef FVW/FVW ParameterType IntKi nWings - - - "Number of Wings" - +typedef ^ ^ IntKi nSpan - - - "TODO, should be defined per wing. Number of spanwise element" - +typedef ^ ^ IntKi nNWMax - - - "Maximum number of nw panels" - +typedef ^ ^ IntKi nFWMax - - - "Maximum number of fw panels" - +typedef ^ ^ IntKi IntMethod - - - "Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1)" - +typedef ^ ^ LOGICAL FreeWake - - - "Disable roll up, wake convects with wind only (flag)" - +typedef ^ ^ IntKi CirculationMethod - - - "Method to determine the circulation" - +typedef ^ ^ ReKi PrescribedCirculation : - - "Prescribed circulation on all lifting lines" "m/s" + +# ....... OtherStateType ............ +# FVW_OtherStateType +typedef FVW/FVW OtherStateType IntKi NULL - - - "Number of active near wake panels" - + +# ....... MiscVars ............ +# FVW_MiscVarType +typedef FVW/FVW MiscVarType Logical FirstCall - - - "True if this is the first call to update state (used in CalcOutput)" - +# Variables at wing extent +typedef ^ ^ ReKi LE ::: - - "Leading edge points" - +typedef ^ ^ ReKi TE ::: - - "Trailing edge points" - +typedef ^ ^ ReKi s_LL :: - - "Spanwise coordinate of LL elements" m +typedef ^ ^ ReKi chord_LL :: - - "chord on LL cp " m +# Variables at control point - Dimensions nSpan +typedef ^ ^ ReKi s_CP_LL :: - - "Spanwise coordinate of LL CP" m +typedef ^ ^ ReKi chord_CP_LL :: - - "chord on LL cp " m +typedef ^ ^ ReKi CP_LL ::: - - "Coordinates of LL CP" - +typedef ^ ^ ReKi Tang ::: - - "Unit Tangential vector on LL CP" - +typedef ^ ^ ReKi Norm ::: - - "Unit Normal vector on LL CP " - +typedef ^ ^ ReKi Orth ::: - - "Unit Orthogonal vector on LL CP" - +typedef ^ ^ Reki Gamma_LL :: - - "Circulation on the wing lifting line (COPY of Constraint State)" - +typedef ^ ^ ReKi Vind_LL ::: - - "Induced velocity on lifting line control points" m/s +typedef ^ ^ ReKi Vtot_LL ::: - - "Total velocity on lifting line control points" m/s +typedef ^ ^ ReKi Vstr_LL ::: - - "Structural velocity on LL CP" m/s +typedef ^ ^ ReKi Vwnd_LL ::: - - "Wind on lifting line control points" m/s +typedef ^ ^ ReKi Vwnd_NW :::: - - "Wind on near wake panels" m/s +typedef ^ ^ ReKi Vwnd_FW :::: - - "Wind on far wake panels" m/s +typedef ^ ^ IntKi nNW - - - "Number of active near wake panels" - +typedef ^ ^ IntKi nFW - - - "Number of active far wake panels" - + +# ........ Input ............ +# FVW_InputType +typedef FVW/FVW InputType MeshType WingsMesh : - - - "Input Mesh defining position and orientation of wings" +typedef ^ ^ ReKi V_wind :: - - "Wind at requested points (r_wind)" - + +# ........ Output ............ +# FVW_OutputType +typedef FVW/FVW OutputType ReKi Vind ::: - - "TODO mesh - Induced velocity vector. " - +typedef ^ ^ ReKi r_wind :: - - "List of points where wind is requested for next time step" - +typedef ^ ^ ReKi Cl_KJ :: - - "Lift coefficient from circulation (Kutta-Joukowski)" - + +#.......... ContinuousStateType ...... +# FVW_ContinuousStateType +typedef FVW/FVW ContinuousStateType ReKi Gamma_NW ::: - - "Circulation of the near wake panels" - +typedef ^ ^ ReKi Gamma_FW ::: - - "Circulation of the far wake panels" - +typedef ^ ^ ReKi r_NW :::: - - "Position of the near wake panels" - +typedef ^ ^ ReKi r_FW :::: - - "Position of the far wake panels" - + + +#.......... DiscreteStateType ...... +# FVW_DiscreteStateType +typedef FVW/FVW DiscreteStateType IntKi Null - - - "Empty to satisfy framework" - + +#.......... ConstraintStateType ...... +# FVW_ConstraintStateType +typedef FVW/FVW ConstraintStateType Reki residual - - "Residual" - +typedef ^ ^ Reki Gamma_LL :: - - "Circulation on the wing lifting line" - + + +#.......... InitInputType ...... +# FVW_InitInputType +typedef FVW/FVW InitInputType CHARACTER(1024) FVWFileName - - - "Main FVW input file name" - +typedef ^ ^ MeshType WingsMesh : - - "Input Mesh defining position and orientation of wings (nSpan+1) " - +typedef ^ ^ ReKi Chord : - - "Chord of each blade element from input file" - +typedef ^ ^ ReKi RElm : - - "radius of center of each element" - +typedef ^ ^ IntKi NumBl - - - "Number of blades" - + +#.......... InitInputType ...... +# FVW_InputFile +typedef FVW/FVW FVW_InputFile IntKi CirculationMethod - - - "Method to determine the circulation" - +typedef ^ ^ CHARACTER(1024) CirculationFile - - - "" - +typedef ^ ^ IntKi IntMethod - - - "Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1, 7=Corrector/Predictor)" - +typedef ^ ^ LOGICAL FreeWake - - - "Disable roll up, wake convects with wind only (flag)" - + +#.......... InitOutputType ...... +# FVW_InitOutputType +typedef FVW/FVW InitOutputType IntKi Null - - - "Empty parameter to satisfy framework" - + + + diff --git a/modules/aerodyn14/src/VTK.f90 b/modules/aerodyn14/src/VTK.f90 new file mode 100755 index 0000000000..6d5752d79e --- /dev/null +++ b/modules/aerodyn14/src/VTK.f90 @@ -0,0 +1,538 @@ +module VTK + !use PrecisionMod, only: ReKi + use NWTC_Library, only: ReKi + implicit none +! character(8), parameter :: RFMT='F14.5' + !character(8), parameter :: RFMT='E24.15E3' + character(8), parameter :: RFMT='E17.8E3' + character(8), parameter :: IFMT='I7' + + integer, save :: vtk_unit + logical, save :: bFileOpen=.false. + + integer, save :: nData=0; + integer, save :: nPoints=0; + + logical, save :: bOverWritWarned = .true. + + + logical, save :: bBinary = .false. + character(len=255), save :: buffer + character(1), parameter :: NL = char(10) ! New Line character + + + ! Reference Frame + logical,save :: bChangeFrame =.false. + real(ReKi),dimension(3,3),save :: T_g2b + real(ReKi),dimension(3) ,save :: PO_g + + + interface vtk_dataset_structured_grid; module procedure & + vtk_dataset_structured_grid_flat, & + vtk_dataset_structured_grid_grid + end interface + + interface vtk_point_data_vector; module procedure & + vtk_point_data_vector_flat, & + vtk_point_data_vector_grid2D,& + vtk_point_data_vector_grid + end interface + interface vtk_point_data_scalar; module procedure & + vtk_point_data_scalar_flat, & + vtk_point_data_scalar_grid2D, & + vtk_point_data_scalar_grid + end interface + + public + private:: vtk_unit, bFileOpen, nData, nPoints,bBinary, buffer + private :: bChangeFrame, T_g2b, PO_g + +contains + + + !> + subroutine set_vtk_binary_format(bBin) + logical, intent(in)::bBin + bBinary=bBin + end subroutine + + + !> Save a coordinate transform + ! ALL VTK Will be exported in this coordinate system! + subroutine set_vtk_coordinate_transform(T_g2b_in,PO_g_in) + real(ReKi),dimension(3,3), intent(in) :: T_g2b_in + real(ReKi),dimension(3) , intent(in) :: PO_g_in + ! + bChangeFrame=.true. + T_g2b=T_g2b_in + PO_g=PO_g_in + end subroutine + + + + logical function vtk_new_ascii_file(filename,label,bDEBUG) + !use MainIO, only: get_free_unit ,check_io + !use MainIOData, only: bSTOP_ALLOWED + !use FileSystem, only: file_exists + !use Logging, only: log_warning,log_error,log_info + ! + character(len=*),intent(in) :: filename + character(len=*),intent(in) :: label + logical, intent(in),optional ::bDEBUG + ! + integer :: iostatvar + character(len=255) :: iomessage + logical :: b + + if (.not.bFileOpen) then + !if (present(bDEBUG)) then + ! if (bDEBUG) call log_info('Opening VTK file:'//filename) + !endif + + !if (file_exists(filename) .and. .not.bOverWritWarned) then + !call log_warning('Overwritting vtk file '//filename) + !call log_warning('Further overwritting warnings will not be displayed') + ! bOverWritWarned=.true. + !endif + + !vtk_unit=get_free_unit(); + vtk_unit=123455 + if (bBinary) then + ! Fortran 2003 stream, otherwise intel fortran ! + !form='UNFORMATTED',access='SEQUENTIAL',action='WRITE',convert='BIG_ENDIAN',recordtype='STREAM',buffered='YES', + !print*,'Not available for this compiler' !COMPAQ-COMPILER + !STOP !COMPAQ-COMPILER + open(unit = vtk_unit,file= trim(adjustl(filename)),form='UNFORMATTED',access = 'stream',& !OTHER-COMPILER + action = 'WRITE',convert= 'BIG_ENDIAN',iostat=iostatvar,status='replace') !OTHER-COMPILER + else + open(vtk_unit,file=trim(adjustl(filename)),iostat=iostatvar,action="write",status='replace') + endif + !b=check_io(iostatvar,iomessage); + !if(.not.b .and. bSTOP_ALLOWED) then + ! STOP ! io errors are fatal + !endif + + if (iostatvar == 0) then + if (bBinary) then + write(vtk_unit)'# vtk DataFile Version 3.0'//NL + write(vtk_unit)trim(label)//NL + write(vtk_unit)'BINARY'//NL + else + write(vtk_unit,'(a)') '# vtk DataFile Version 2.0' + write(vtk_unit,'(a)') label + write(vtk_unit,'(a)') 'ASCII' + write(vtk_unit,'(a)') ' ' + endif + + bFileOpen=.true. + nData=-1; + endif + else + b=.false. + !call log_error('VTK: Cannot open two vtk files at the same time, call vtk_close first') + endif + if (iostatvar ==0) then + vtk_new_ascii_file=.true. + else + vtk_new_ascii_file=.false. + endif + end function + + subroutine vtk_close_file() + if ( bFileOpen ) then + close(vtk_unit) + bFileOpen=.false. + endif + endsubroutine + + + ! ------------------------------------------------------------------------- + ! --- POLYDATA STUFF + ! ------------------------------------------------------------------------- + subroutine vtk_dataset_polydata(Points) + real(ReKi), dimension(:,:),intent(in) :: Points !< 3 x n + integer :: i + if ( bFileOpen ) then + nPoints=size(Points,2) + if (bBinary) then + write(vtk_unit)'DATASET POLYDATA'//NL + write(buffer,'(A,I0,A)') 'POINTS ', nPoints ,' double' + write(vtk_unit)trim(buffer)//NL + if (bChangeFrame) then + do i=1,nPoints + write(vtk_unit)matmul(T_g2b,Points(1:3,i)-PO_g) + enddo + else + do i=1,nPoints + write(vtk_unit)Points(1:3,i) + enddo + endif + write(vtk_unit)NL + else + write(vtk_unit,'(A)') 'DATASET POLYDATA' + write(vtk_unit,'(A,I0,A)') 'POINTS ', nPoints ,' double' + if (bChangeFrame) then + do i=1,nPoints + write(vtk_unit,'(3'//RFMT//')') matmul(T_g2b,Points(1:3,i)-PO_g) + enddo + else + do i=1,nPoints + write(vtk_unit,'(3'//RFMT//')') Points(1:3,i) + enddo + endif + write(vtk_unit,*) ' ' + endif + endif + end subroutine + + + subroutine vtk_lines(L) + integer, dimension(:,:),intent(in) :: L !< 2 x n + + integer :: i + + if ( bFileOpen ) then + nData=size(L,2) + if (bBinary) then + write(buffer,'(A,I0,A,I0)')'LINES ',nData,' ',3*nData + write(vtk_unit)trim(buffer)//NL + do i=1,nData + write(vtk_unit)2,L(1:2,i) + enddo + write(vtk_unit)NL + else + write(vtk_unit,'(A,I0,A,I0)')'LINES ',nData,' ',3*nData + do i=1,nData + write(vtk_unit,'(3'//IFMT//')') 2, L(1:2,i) + enddo + write(vtk_unit,*) ' ' + endif + endif + end subroutine + + subroutine vtk_quad(Q) + integer, dimension(:,:),intent(in) :: Q !< 4 x n + integer :: i + if ( bFileOpen ) then + nData=size(Q,2) + if (bBinary) then + write(buffer,'(A,I0,A,I0)')'POLYGONS ',nData,' ',5*nData + write(vtk_unit)trim(buffer)//NL + do i=1,nData + write(vtk_unit)4,Q(1:4,i) + enddo + write(vtk_unit)NL + else + write(vtk_unit,'(A,I0,A,I0)') 'POLYGONS ', nData,' ',5*nData + do i=1,nData + write(vtk_unit,'(5'//IFMT//')') 4, Q(1:4,i) + enddo + write(vtk_unit,*) ' ' + endif + endif + end subroutine + + ! ------------------------------------------------------------------------- + ! --- RECTILINEAR + ! ------------------------------------------------------------------------- + subroutine vtk_dataset_rectilinear(v1,v2,v3) + real(ReKi), dimension(:),intent(in) :: v1,v2,v3 !< n + + if ( bFileOpen ) then + nPoints=size(v1)*size(v2)*size(v3) + if (bBinary) then + write(vtk_unit) 'DATASET RECTILINEAR_GRID'//NL + write(buffer,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', size(v1),' ',size(v2),' ',size(v3) + write(vtk_unit) trim(buffer)//NL + write(buffer,'(A,I0,A)') 'X_COORDINATES ', size(v1), ' double' + write(vtk_unit) trim(buffer)//NL + write(vtk_unit)v1 + write(vtk_unit)NL + write(buffer,'(A,I0,A)') 'Y_COORDINATES ', size(v2), ' double' + write(vtk_unit) trim(buffer)//NL + write(vtk_unit)v2 + write(vtk_unit)NL + write(buffer,'(A,I0,A)') 'Z_COORDINATES ', size(v3), ' double' + write(vtk_unit) trim(buffer)//NL + write(vtk_unit)v3 + !write(vtk_unit)NL + else + write(vtk_unit,'(A)') 'DATASET RECTILINEAR_GRID' + write(vtk_unit,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', size(v1),' ',size(v2),' ',size(v3) + write(vtk_unit,'(A,I0,A)') 'X_COORDINATES ', size(v1), ' double' + write(vtk_unit,'('//RFMT//')') v1 + write(vtk_unit,'(A,I0,A)') 'Y_COORDINATES ', size(v2), ' double' + write(vtk_unit,'('//RFMT//')') v2 + write(vtk_unit,'(A,I0,A)') 'Z_COORDINATES ', size(v3), ' double' + write(vtk_unit,'('//RFMT//')') v3 + write(vtk_unit,*) ' ' + endif + endif + end subroutine + + ! ------------------------------------------------------------------------- + ! --- STRUCTURED GRID (Points dumped without for loop since memory is in proper order) + ! ------------------------------------------------------------------------- + !> Subroutine using flat data as input (not in natural order) + subroutine vtk_dataset_structured_grid_flat(D,n1,n2,n3) + integer , intent(in) :: n1,n2,n3 + real(ReKi), dimension(:,:),intent(in)::D + if ( bFileOpen ) then + nPoints=n1*n2*n3 + if (bBinary) then + write(vtk_unit) 'DATASET STRUCTURED_GRID'//NL + write(buffer,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 + write(vtk_unit) trim(buffer)//NL + write(buffer,'(A,I0,A)') 'POINTS ', nPoints, ' double' + write(vtk_unit) trim(buffer)//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,'(A)') 'DATASET STRUCTURED_GRID' + write(vtk_unit,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 + write(vtk_unit,'(A,I0,A)') 'POINTS ', nPoints, ' double' + write(vtk_unit,'(3'//RFMT//')')D + write(vtk_unit,*) ' ' + endif + endif + end subroutine + + !> Using Grid data 4d as input + subroutine vtk_dataset_structured_grid_grid(D,n1,n2,n3) + integer , intent(in) :: n1,n2,n3 + real(ReKi), dimension(:,:,:,:),intent(in)::D + + if ( bFileOpen ) then + nPoints=n1*n2*n3 + if (bBinary) then + write(vtk_unit) 'DATASET STRUCTURED_GRID'//NL + write(buffer,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 + write(vtk_unit) trim(buffer)//NL + write(buffer,'(A,I0,A)') 'POINTS ', nPoints, ' double' + write(vtk_unit) trim(buffer)//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,'(A)') 'DATASET STRUCTURED_GRID' + write(vtk_unit,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 + write(vtk_unit,'(A,I0,A)') 'POINTS ', nPoints, ' double' + write(vtk_unit,'(3'//RFMT//')')D + write(vtk_unit,*) ' ' + endif + endif + end subroutine + + + + ! ------------------------------------------------------------------------- + ! --- POINT DATA + ! ------------------------------------------------------------------------- + subroutine vtk_point_data_init() + if ( bFileOpen ) then + if(bBinary) then + write(buffer,'(A,I0)')'POINT_DATA ',nPoints + write(vtk_unit)trim(buffer)//NL + else + write(vtk_unit,'(A,I0)') 'POINT_DATA ', nPoints + endif + endif + end subroutine + + subroutine vtk_point_data_scalar_flat(D,sname) + real(ReKi), dimension(:),intent(in)::D + character(len=*),intent(in) ::sname + + if ( bFileOpen ) then + if (bBinary) then + write(vtk_unit)'SCALARS '//trim(sname)//' double'//NL + write(vtk_unit)'LOOKUP_TABLE default'//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,'(A,A,A)') 'SCALARS ', sname, ' double' + write(vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(vtk_unit,'(1'//RFMT//')')D + endif + endif + end subroutine + + subroutine vtk_point_data_scalar_grid(D,sname) + real(ReKi), dimension(:,:,:,:),intent(in)::D + character(len=*),intent(in) ::sname + + if ( bFileOpen ) then + if (bBinary) then + write(vtk_unit)'SCALARS '//trim(sname)//' double'//NL + write(vtk_unit)'LOOKUP_TABLE default'//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,'(A,A,A)') 'SCALARS ', sname, ' double' + write(vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(vtk_unit,'(1'//RFMT//')')D + endif + endif + end subroutine + + subroutine vtk_point_data_scalar_grid2D(D,sname) + real(ReKi), dimension(:,:,:),intent(in)::D + character(len=*),intent(in) ::sname + + if ( bFileOpen ) then + if (bBinary) then + write(vtk_unit)'SCALARS '//trim(sname)//' double'//NL + write(vtk_unit)'LOOKUP_TABLE default'//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,'(A,A,A)') 'SCALARS ', sname, ' double' + write(vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(vtk_unit,'(1'//RFMT//')')D + endif + endif + end subroutine + + !> + subroutine vtk_point_data_vector_flat(D,sname) + real(ReKi), dimension(:,:),intent(in) :: D !< 3 x n + character(len=*),intent(in) ::sname + if ( bFileOpen ) then + if (bBinary) then + write(vtk_unit)'VECTORS '//trim(sname)//' double'//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' + write(vtk_unit,'(3'//RFMT//')')D + endif + endif + end subroutine + !> + subroutine vtk_point_data_vector_grid(D,sname) + real(ReKi), dimension(:,:,:,:),intent(in) :: D !< 3 x n + character(len=*),intent(in) ::sname + if ( bFileOpen ) then + if (bBinary) then + write(vtk_unit)'VECTORS '//trim(sname)//' double'//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' + write(vtk_unit,'(3'//RFMT//')')D + endif + endif + end subroutine + !> + subroutine vtk_point_data_vector_grid2D(D,sname) + real(ReKi), dimension(:,:,:),intent(in) :: D !< + character(len=*),intent(in) ::sname + if ( bFileOpen ) then + if (bBinary) then + write(vtk_unit)'VECTORS '//trim(sname)//' double'//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' + write(vtk_unit,'(3'//RFMT//')')D + endif + endif + end subroutine + + + ! ------------------------------------------------------------------------- + ! --- CELL DATA + ! ------------------------------------------------------------------------- + subroutine vtk_cell_data_init() + if ( bFileOpen ) then + if (bBinary) then + write(buffer,'(A,I0)')'CELL_DATA ',nData + write(vtk_unit)trim(buffer)//NL + else + write(vtk_unit,'(A,I0)') 'CELL_DATA ', nData + endif + endif + end subroutine + + subroutine vtk_cell_data_scalar(D,sname) + real(ReKi), dimension(:),intent(in)::D + character(len=*),intent(in) ::sname + + if ( bFileOpen ) then + if (bBinary) then + write(vtk_unit)'SCALARS '//trim(sname)//' double 1'//NL + write(vtk_unit)'LOOKUP_TABLE default'//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,fmt='(A,A,A)') 'SCALARS ', sname, ' double' + write(vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(vtk_unit,'(1'//RFMT//')')D + endif + endif + end subroutine + + + subroutine vtk_cell_data_vector(D,sname) + real(ReKi), dimension(:,:),intent(in) :: D !< 3 x n + character(len=*),intent(in) ::sname + if ( bFileOpen ) then + if (bBinary) then + write(vtk_unit)'VECTORS '//trim(sname)//' double'//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' + write(vtk_unit,'(3'//RFMT//')')D + endif + endif + end subroutine + + ! --------------------------------------------------------------------------------} + ! --- VTK Tools + ! --------------------------------------------------------------------------------{ + !> Exports a Plane From a mesh + subroutine export_plane_grid3d(fname,v1,v2,v3,Values) + character(len=*),intent(in) :: fname + real(ReKi),dimension(:), intent(in) :: v1,v2,v3 + real(ReKi),dimension(:,:,:,:), intent(in) :: Values + ! Variables + integer :: nD + + ! Writting + if ( vtk_new_ascii_file(trim(fname),'plane', .false.)) then + nD=size(Values,1) + call vtk_dataset_rectilinear(v1,v2,v3) + ! Output as a structured grid, No need to reorder + call vtk_point_data_init() + ! Could be a function of nDim, be careful + if(nD==3) then + call vtk_point_data_vector(Values(1:3,:,:,:),'Velocity') ! Label... + endif + + call vtk_close_file() + endif ! file opening + end subroutine + + !> Exports a Plane From a mesh + subroutine export_plane_grid2d(fname,v1,v2,v3,Values) + character(len=*),intent(in) :: fname + real(ReKi),dimension(:), intent(in) :: v1,v2,v3 + real(ReKi),dimension(:,:,:), intent(in) :: Values + ! Variables + integer :: nD + + ! Writting + if ( vtk_new_ascii_file(trim(fname),'plane', .false.)) then + nD=size(Values,1) + call vtk_dataset_rectilinear(v1,v2,v3) + ! Output as a structured grid, No need to reorder + call vtk_point_data_init() + ! Could be a function of nDim, be careful + if(nD==3) then + call vtk_point_data_vector(Values(1:3,:,:),'Velocity') ! Label... + endif + + call vtk_close_file() + endif ! file opening + end subroutine +end module VTK diff --git a/modules/elastodyn/src/ElastoDyn_IO.f90 b/modules/elastodyn/src/ElastoDyn_IO.f90 index 8222a52495..33187b021a 100644 --- a/modules/elastodyn/src/ElastoDyn_IO.f90 +++ b/modules/elastodyn/src/ElastoDyn_IO.f90 @@ -1971,9 +1971,9 @@ SUBROUTINE ReadBladeMeshFileAD( BladeKInputFileMesh, MeshFile, UnEc, ErrStat, Er CALL Conv2UC( Line ) IF ( INDEX(Line, "NEWTOWER" ) > 0 ) THEN - NumLin2Skp = 7 + NumLin2Skp = 7+2 ! +2 for FVW ELSE - NumLin2Skp = 5 + NumLin2Skp = 5+2+2 ! +2 for FVW END IF DO I = 1,NumLin2Skp diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 338ada5769..d95ee858e5 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -586,6 +586,22 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, IfW%OtherSt(STATE_CURR), IfW%y, IfW%m, p_FAST%dt_module( MODULE_IfW ), InitOutData_IfW, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +!FIXME: how do I remove this? There is some confusing stuff going on... + IF ( p_FAST%CompAero == Module_AD14 ) THEN !!KS -- added this section + !TODO TODO ANDY + !AD14%m%FVW%FVW_Wind%InitInputData = InitInData_IfW +!tempo!rary hard coded size while we figure out how many points FVW actually needs: this will be requested by AD14 in addition to the nodes it normally needs + !AD14%m%FVW%FVW_Wind%InitInputData%NumWindPoints = 1000 ! TODO + !CALL InflowWind_Init( AD14%m%FVW%FVW_Wind%InitInputData,AD14%m%FVW%FVW_Wind%InputData,& + ! & AD14%m%FVW%FVW_Wind%ParamData,AD14%m%FVW%FVW_Wind%ContData,& + ! & AD14%m%FVW%FVW_Wind%DiscData,AD14%m%FVW%FVW_Wind%ConstrData,& + ! & AD14%m%FVW%FVW_Wind%OtherData, AD14%m%FVW%FVW_Wind%OutputData,& + ! & AD14%m%FVW%FVW_Wind%MiscData,p_FAST%dt_module(MODULE_IfW ),& + ! & AD14%m%FVW%FVW_Wind%InitOutputData, ErrStat2, ErrMsg2 ) + + !AD14%p%IfW_DT = p_FAST%dt_module( MODULE_IfW ) + END IF + p_FAST%ModuleInitialized(Module_IfW) = .TRUE. CALL SetModuleSubstepTime(Module_IfW, p_FAST, y_FAST, ErrStat2, ErrMsg2) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -3456,9 +3472,11 @@ SUBROUTINE SetModuleSubstepTime(ModuleID, p_FAST, y_FAST, ErrStat, ErrMsg) ELSE IF ( p_FAST%dt_module( ModuleID ) > p_FAST%dt ) THEN ErrStat = ErrID_Fatal - ErrMsg = "The "//TRIM(y_FAST%Module_Ver(ModuleID)%Name)//" module time step ("//& - TRIM(Num2LStr(p_FAST%dt_module( ModuleID )))// & - " s) cannot be larger than FAST time step ("//TRIM(Num2LStr(p_FAST%dt))//" s)." + ! + !ErrMsg = "The "//TRIM(y_FAST%Module_Ver(ModuleID)%Name)//" module time step ("//& + ! TRIM(Num2LStr(p_FAST%dt_module( ModuleID )))// & + ! " s) cannot be larger than FAST time step ("//TRIM(Num2LStr(p_FAST%dt))//" s)." + p_FAST%n_substeps(ModuleID) = 0 !KS -- and added this line ELSE ! calculate the number of subcycles: p_FAST%n_substeps(ModuleID) = NINT( p_FAST%dt / p_FAST%dt_module( ModuleID ) ) @@ -5190,6 +5208,7 @@ END SUBROUTINE WrVTK_AllMeshes !> This routine writes a minimal subset of meshes (enough to visualize the turbine) to VTK-formatted files. It doesn't bother with !! returning an error code. SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + use FVW_IO, only: WrVTK_FVW TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(IN ) :: y_FAST !< Output variables for the glue code @@ -5260,6 +5279,11 @@ SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, END DO END IF + ! Free wake + IF ( p_FAST%CompAero == Module_AD14 ) THEN ! These meshes may have airfoil data associated with nodes... + call WrVTK_FVW(AD14%p%FVW, AD14%x(1)%FVW, AD14%z(1)%FVW, AD14%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + END IF + ! Tower motions call MeshWrVTK(p_FAST%TurbinePos, ED%Output(1)%TowerLn2Mesh, trim(VTK_path)//'.ED_TowerLn2Mesh_motion', & y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth ) @@ -5301,6 +5325,7 @@ END SUBROUTINE WrVTK_BasicMeshes !> This routine writes a minimal subset of meshes with surfaces to VTK-formatted files. It doesn't bother with !! returning an error code. SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + use FVW_IO, only: WrVTK_FVW REAL(DbKi), INTENT(IN ) :: t_global !< Current global time TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code @@ -5381,6 +5406,11 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, A y_FAST%VTK_count, OutputFields, ErrStat2, ErrMsg2, Twidth , verts=p_FAST%VTK_Surface%BladeShape(K)%AirfoilCoords ) END DO END IF + + ! Free wake + IF ( p_FAST%CompAero == Module_AD14 ) THEN ! These meshes may have airfoil data associated with nodes... + call WrVTK_FVW(AD14%p%FVW, AD14%x(1)%FVW, AD14%z(1)%FVW, AD14%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + END IF ! Tower motions call MeshWrVTK_Ln2Surface (p_FAST%TurbinePos, ED%Output(1)%TowerLn2Mesh, trim(VTK_path)//'.TowerSurface', & diff --git a/modules/openfast-library/src/FAST_Types.f90 b/modules/openfast-library/src/FAST_Types.f90 index 9a34e11395..433d82b367 100644 --- a/modules/openfast-library/src/FAST_Types.f90 +++ b/modules/openfast-library/src/FAST_Types.f90 @@ -44,6 +44,8 @@ MODULE FAST_Types USE Lidar_Types USE InflowWind_Types USE DWM_Types +USE AD14AeroConf_Types +USE FVW_Types USE AeroDyn14_Types USE AirfoilInfo_Types USE UnsteadyAero_Types From 53f78e74dce06a8bd0ee0b53c8da0a3bdf53fdd0 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 18 Nov 2019 14:05:07 -0700 Subject: [PATCH 002/190] Add array interpolation routine to library. Remove Interpolation.f90 --- modules/aerodyn14/CMakeLists.txt | 1 - modules/aerodyn14/src/FVW_Wings.f90 | 7 +- modules/aerodyn14/src/Interpolation.f90 | 129 ----------------------- modules/nwtc-library/src/NWTC_Num.f90 | 133 ++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 134 deletions(-) delete mode 100644 modules/aerodyn14/src/Interpolation.f90 diff --git a/modules/aerodyn14/CMakeLists.txt b/modules/aerodyn14/CMakeLists.txt index 7350432f28..9fbeddbf8d 100644 --- a/modules/aerodyn14/CMakeLists.txt +++ b/modules/aerodyn14/CMakeLists.txt @@ -34,7 +34,6 @@ set(AD14_LIBS_SOURCES src/FVW_Wings.f90 src/FVW_Subs.f90 src/VTK.f90 - src/Interpolation.f90 src/AeroDyn14_Types.f90 src/DWM_Types.f90 diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index b49a6706c0..a21ea44ac5 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -81,7 +81,6 @@ end subroutine Wings_Panelling_Init !! - Orth : Unit Orthogonal vector on LL CP" - !! - Vstr_LL : Structural velocity on LL CP" m/s subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) - use Interpolation, only: interp_lin type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables @@ -142,9 +141,9 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) ! --- Position of control points ! NOTE: separated from other loops just in case a special discretization is used do iW = 1,p%nWings - call interp_lin(m%s_LL(:,iW), m%TE(1,:,iW)*0.25_ReKi+m%LE(1,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(1,:,iW)) - call interp_lin(m%s_LL(:,iW), m%TE(2,:,iW)*0.25_ReKi+m%LE(2,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(2,:,iW)) - call interp_lin(m%s_LL(:,iW), m%TE(3,:,iW)*0.25_ReKi+m%LE(3,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(3,:,iW)) + call InterpArray(m%s_LL(:,iW), m%TE(1,:,iW)*0.25_ReKi+m%LE(1,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(1,:,iW)) + call InterpArray(m%s_LL(:,iW), m%TE(2,:,iW)*0.25_ReKi+m%LE(2,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(2,:,iW)) + call InterpArray(m%s_LL(:,iW), m%TE(3,:,iW)*0.25_ReKi+m%LE(3,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(3,:,iW)) enddo end subroutine Wings_Panelling diff --git a/modules/aerodyn14/src/Interpolation.f90 b/modules/aerodyn14/src/Interpolation.f90 deleted file mode 100644 index 864ebc6fca..0000000000 --- a/modules/aerodyn14/src/Interpolation.f90 +++ /dev/null @@ -1,129 +0,0 @@ -!> -module Interpolation - use NWTC_Library - implicit none - - interface interp_lin; module procedure interp_lin11, interp_lin10 - end interface - - private - - public:: interp_lin - public:: interp_lin0 - -contains - - !-------------------------------------------------------------------- - ! --- Linear Interp - !-------------------------------------------------------------------- - function interp_lin0(x,x0,x1,f0,f1) ! Linear interpolation function - real(ReKi) ::interp_lin0 - real(ReKi),intent(in):: x,x0,x1,f0,f1 - real(ReKi) :: eps=1.0e-7 - if (abs(x0-x1) Linear interpolation between one dim arrays. ASSUMES xknown to have increasing values - subroutine interp_lin11_incr(xknown,yknown,xnew,ynew) - real(ReKi),dimension(:),intent(in):: xknown,yknown - real(ReKi),dimension(:),intent(in):: xnew - real(ReKi),dimension(:),intent(out):: ynew - integer i - integer itmp - integer:: nknown - nknown=size(xknown) - do i=1,size(xnew) - itmp=minloc(abs(xnew(i)-xknown),dim=1) - if (itmp==nknown) then - if (xknown(itmp)>xnew(i)) then - ynew(i)=interp_lin0(xnew(i),xknown(itmp-1),xknown(itmp),yknown(itmp-1),yknown(itmp)) - else - ! The current x is above the max of xknown - ! extrapolation required, here fixed to upper bound - ynew(i)=yknown(nknown) - endif - elseif (xknown(itmp) - subroutine interp_lin11(xknown,yknown,xnew,ynew) ! Linear interpolation between one dim arrays - real(ReKi),dimension(:),intent(in):: xknown,yknown - real(ReKi),dimension(:),intent(in):: xnew - real(ReKi),dimension(:),intent(out):: ynew - ! - integer :: n - n=size(xknown) - if(n<2) then - print*, 'interp_lin error, input argument should be at least of size 2' - elseif(size(yknown)/=n) then - print*, 'interp_lin error, dimension of two first inputs mismatch' - elseif(size(xnew)/=size(ynew)) then - print*, 'interp_lin error, dimension of two last inputs mismatch' - else - if(xknown(2)>xknown(1)) then - ! increasing x values - call interp_lin11_incr(xknown,yknown,xnew,ynew) - else - ! decreasing x values - call interp_lin11_incr(xknown(n:1:-1),yknown(n:1:-1),xnew,ynew) - endif - endif - - end subroutine - - - subroutine interp_lin10(xknown,yknown,xnew,ynew) ! Linear interpolation between one dim array at scalar location - real(ReKi),dimension(:),intent(in):: xknown,yknown - real(ReKi),intent(in):: xnew - real(ReKi),intent(out):: ynew - integer itmp - integer:: nknown - nknown=size(xknown) - if(size(xknown)/=size(yknown)) then - print*, 'interp_lin error, dimension of two first inputs mismatch' - else - if(xknown(2)xnew) then - ynew=interp_lin0(xnew,xknown(itmp-1),xknown(itmp),yknown(itmp-1),yknown(itmp)) - else - ! The current x is above the max of xknown - ! extrapolation required, here fixed to upper bound - ynew=yknown(nknown) - endif - elseif (xknown(itmp) \copydoc nwtc_num::interparrayr4 + INTERFACE InterpArray + MODULE PROCEDURE InterpArrayR4 + MODULE PROCEDURE InterpArrayR8 + MODULE PROCEDURE InterpArrayR16 + END INTERFACE + !> \copydoc nwtc_num::interpwrappedstpreal4 INTERFACE InterpWrappedStpReal MODULE PROCEDURE InterpWrappedStpReal4 @@ -3624,6 +3631,132 @@ FUNCTION InterpWrappedStpReal16( XValIn, XAry, YAry, Ind, AryLen ) END FUNCTION InterpWrappedStpReal16 !======================================================================= +!> This subroutine calculates interpolated values for an array of input values. +!! The size of the xknown and yknown arrays must match, and the size of the +!! xnew and ynew arrays must match. Xknown must be in ascending order. +!! Values outside the range of xknown are fixed to the end points. + SUBROUTINE InterpArrayR4( xknown, yknown, xnew, ynew ) + REAL(SiKi), INTENT(IN ) :: xknown(:) + REAL(SiKi), INTENT(IN ) :: yknown(:) + REAL(SiKi), INTENT(IN ) :: xnew(:) + REAL(SiKi), INTENT( OUT) :: ynew(:) + integer(IntKi) i,itmp,nknown + nknown=size(xknown) + do i=1,size(xnew) + itmp=minloc(abs(xnew(i)-xknown),dim=1) + if (itmp==nknown) then + if (xknown(itmp)>xnew(i)) then + ynew(i)=interp_lin0(xnew(i),xknown(itmp-1),xknown(itmp),yknown(itmp-1),yknown(itmp)) + else + ! The current x is above the max of xknown + ! extrapolation required, here fixed to upper bound + ynew(i)=yknown(nknown) + endif + elseif (xknown(itmp) \copydoc nwtc_num::interparrayr4 + SUBROUTINE InterpArrayR8( xknown, yknown, xnew, ynew ) + REAL(R8Ki), INTENT(IN ) :: xknown(:) + REAL(R8Ki), INTENT(IN ) :: yknown(:) + REAL(R8Ki), INTENT(IN ) :: xnew(:) + REAL(R8Ki), INTENT( OUT) :: ynew(:) + integer(IntKi) i,itmp,nknown + nknown=size(xknown) + do i=1,size(xnew) + itmp=minloc(abs(xnew(i)-xknown),dim=1) + if (itmp==nknown) then + if (xknown(itmp)>xnew(i)) then + ynew(i)=interp_lin0(xnew(i),xknown(itmp-1),xknown(itmp),yknown(itmp-1),yknown(itmp)) + else + ! The current x is above the max of xknown + ! extrapolation required, here fixed to upper bound + ynew(i)=yknown(nknown) + endif + elseif (xknown(itmp) \copydoc nwtc_num::interparrayr4 + SUBROUTINE InterpArrayR16( xknown, yknown, xnew, ynew ) + REAL(QuKi), INTENT(IN ) :: xknown(:) + REAL(QuKi), INTENT(IN ) :: yknown(:) + REAL(QuKi), INTENT(IN ) :: xnew(:) + REAL(QuKi), INTENT( OUT) :: ynew(:) + integer(IntKi) i,itmp,nknown + nknown=size(xknown) + do i=1,size(xnew) + itmp=minloc(abs(xnew(i)-xknown),dim=1) + if (itmp==nknown) then + if (xknown(itmp)>xnew(i)) then + ynew(i)=interp_lin0(xnew(i),xknown(itmp-1),xknown(itmp),yknown(itmp-1),yknown(itmp)) + else + ! The current x is above the max of xknown + ! extrapolation required, here fixed to upper bound + ynew(i)=yknown(nknown) + endif + elseif (xknown(itmp) This subroutine calculates the iosparametric coordinates, isopc, which is a value between -1 and 1 !! (for each dimension of a dataset), indicating where InCoord falls between posLo and posHi. !! It is used in InterpStpReal2D (nwtcnum::interpstpreal2d) and InterpStpReal3D (nwtcnum::interpstpreal3d). From e998d6f80249c6c394f61f190777c7ef224bbe62 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 18 Nov 2019 15:16:32 -0700 Subject: [PATCH 003/190] FVW: adding vortex tools, Biot-Savart and test, LatticeToSegment --- modules/aerodyn14/CMakeLists.txt | 2 + modules/aerodyn14/src/FVW_BiotSavart.f90 | 186 ++++++++++++++++++++++ modules/aerodyn14/src/FVW_Tests.f90 | 146 +++++++++++++++++ modules/aerodyn14/src/FVW_VortexTools.f90 | 106 +++++++++++- 4 files changed, 439 insertions(+), 1 deletion(-) create mode 100644 modules/aerodyn14/src/FVW_BiotSavart.f90 create mode 100644 modules/aerodyn14/src/FVW_Tests.f90 diff --git a/modules/aerodyn14/CMakeLists.txt b/modules/aerodyn14/CMakeLists.txt index 7350432f28..d893dee59d 100644 --- a/modules/aerodyn14/CMakeLists.txt +++ b/modules/aerodyn14/CMakeLists.txt @@ -33,6 +33,8 @@ set(AD14_LIBS_SOURCES src/FVW_VortexTools.f90 src/FVW_Wings.f90 src/FVW_Subs.f90 + src/FVW_BiotSavart.f90 + src/FVW_Tests.f90 src/VTK.f90 src/Interpolation.f90 diff --git a/modules/aerodyn14/src/FVW_BiotSavart.f90 b/modules/aerodyn14/src/FVW_BiotSavart.f90 new file mode 100644 index 0000000000..cd19b1c1ab --- /dev/null +++ b/modules/aerodyn14/src/FVW_BiotSavart.f90 @@ -0,0 +1,186 @@ +module FVW_BiotSavart + + use NWTC_Library, only: ReKi, IntKi + + implicit none + + real(ReKi),parameter :: PRECISION_UI = epsilon(1.0_ReKi)/100 !< NOTE assuming problem of size 1 + real(ReKi),parameter :: MIN_EXP_VALUE=-10.0_ReKi + real(ReKi),parameter :: MINDENOM=1e-15_ReKi + real(ReKi),parameter :: MINNORMSIMP=1e-6_ReKi + + integer(IntKi), parameter :: idSegSmoothRankine = 1 + integer(IntKi), parameter :: idSegSmoothLambOseen = 2 + integer(IntKi), parameter :: idSegSmoothVatistas = 3 + integer(IntKi), parameter :: idSegSmoothCompact = 4 + integer(IntKi), parameter :: idSegSmoothCompactDim = 5 +contains + +!> Induced velocity from a list of segments defined by Connectivity (SegConnct) and Points (SegPoints) +!! NOTE: this funciton has side effects and expect Uind_out to be initialized! +!! The function can compute the velocity on part of the segments and part of the control points. +!! This feature is useful if some parallelization is used, while common storage vectors are used. +!! +subroutine ui_seg(CPs, iCPStart, iCPEnd, nCPsTot, & + SegPoints, SegConnct, SegGamma, iSegStart, iSegEnd, nSegTot, nSegPTot, & + SmoothModel, SmoothParam, Uind_out) + use OMP_LIB + implicit none + real(ReKi), dimension(3,nCPsTot), intent(in) :: CPs !< Control points + integer(IntKi), intent(in) :: iCPStart !< Index where we start in Control points array + integer(IntKi), intent(in) :: iCPEnd !< Index where we end in Control points array + integer(IntKi), intent(in) :: nCPsTot !< Total number of control points + real(ReKi), dimension(3,nSegPTot), intent(in) :: SegPoints !< Segment points + integer(IntKi), dimension(2,nSegTot), intent(in) :: SegConnct !< Connectivity, indices of segments points iSeg1, iSeg2 + real(ReKi), dimension(nSegTot), intent(in) :: SegGamma !< Segment circulation + integer(IntKi),intent(in) :: iSegStart !< Index in SegConnct, and SegGamma where we start + integer(IntKi),intent(in) :: iSegEnd !< Index in SegConnct, and SegGamma where we end + integer(IntKi), intent(in) :: nSegTot !< Total number of segments + integer(IntKi), intent(in) :: nSegPTot !< Total number of segment points + integer(IntKi), intent(in) :: SmoothModel !< Smooth model + real(ReKi), dimension(nSegTot), intent(in) :: SmoothParam !< Smooth parameter + real(ReKi), dimension(3,nCPsTot), intent(inout) :: Uind_out !< Induced velocity vector - Side effects!!! + ! Variables + integer(IntKi) :: icp,is + ! Part of argument arrays + real(ReKi), dimension(3) :: Uind !< + real(ReKi), dimension(3) :: P1 !< Point1 of a given segment + real(ReKi), dimension(3) :: P2 !< Point2 of a given segment + real(ReKi), dimension(3) :: DP1, DP2 !< + real(ReKi) :: norm2_r0 !< + real(ReKi) :: Gam !< + real(ReKi) :: visc_param !< + ! Variables declaration + real(ReKi),dimension(3) :: crossprod !< + real(ReKi) :: denom !< + real(ReKi) :: denominator !< + real(ReKi) :: h2 !< Square of h + real(ReKi) :: h !< Only used by one model + real(ReKi) :: Kv !< + real(ReKi) :: norm_a !< + real(ReKi) :: norm_b !< + real(ReKi) :: norm2_crossprod !< + real(ReKi) :: xa !< + real(ReKi) :: ya !< + real(ReKi) :: za !< + real(ReKi) :: xb !< + real(ReKi) :: yb !< + real(ReKi) :: zb !< + real(ReKi) :: visc_param2 !< Square of viscous param + real(ReKi) :: exp_value !< + real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) + + !$OMP PARALLEL default(shared) + !$OMP do private(& + !$OMP& icp,is,Uind,P1,P2,DP1,DP2,norm2_r0,Gam,visc_param,& + !$OMP& crossprod,denom,denominator,h2,h,Kv,norm_a,norm_b,norm2_crossprod,xa,ya,za,xb,yb,zb,visc_param2,exp_value& + !$OMP& ) schedule(runtime) + ! loop on CPs + do icp=iCPStart,iCPEnd + do is=iSegStart,iSegEnd ! loop on selected segments + ! Kind of arguments for Segment 11 + Uind=0.0_ReKi + P1 = SegPoints(1:3, SegConnct(1,is)) ! Segment extremity points + P2 = SegPoints(1:3, SegConnct(2,is)) + DP1 = CPs(:,icp)-P1; + DP2 = CPs(:,icp)-P2 + Gam = SegGamma(is) + visc_param = SmoothParam(is) + ! --- inlining of Segment 11 + xa=DP1(1) + ya=DP1(2) + za=DP1(3) + xb=DP2(1) + yb=DP2(2) + zb=DP2(3) + !--- Simple test if on an extremity point + if(abs(xa)+abs(ya)+abs(za)rc + ! orthogonal distance r1xr2/r0 + h2 = norm2_crossprod/ norm2_r0 + visc_param2 = visc_param**2 + if (h2rc + ! orthogonal distance r1xr2/r0 + h2 = norm2_crossprod/ norm2_r0 + visc_param2 = visc_param**2 + exp_value = -1.25643_ReKi*(h2)/(visc_param2) + if(exp_valuerc + ! orthogonal distance r1xr2/r0 + h2 = norm2_crossprod/ norm2_r0 + visc_param2 = visc_param**2 + ! h = (norm_a+norm_b)/2; + Kv = h2/sqrt(visc_param2**2+h2**2) + case ( idSegSmoothCompact ) !!Cut-off radius as function of length: delta^2*norm(r0)^2 + Kv=1.0_ReKi + denominator=denominator+visc_param*visc_param*norm2_r0 + case ( idSegSmoothCompactDim ) !!Cut-off radius dimension fixed: (delta l_0)^2 + Kv=1.0_ReKi + denominator=denominator+visc_param*visc_param + case ( 33 ) !!Vatistas n=2 - visc_paramt<=>rc + ! See Hoydonck 2012 + h = (norm_a+norm_b)/2.0_ReKi + visc_param2 = visc_param**2 + if(h<2*sqrt(norm2_r0)) then + ! use Vatistas n=2, normal one + h2 = norm2_crossprod/norm2_r0 + end if + Kv = (h2)/sqrt(visc_param2**2+h2**2) + case default + Kv=1.0_ReKi + end select + + Kv=Gam*fourpi_inv*Kv*(norm_a+norm_b)/denominator + Uind(1:3) = Kv*crossprod(1:3) + !print "(A,3F21.16)","ui :",Uind(1:3) + end if ! denominator size or distances too small + end if ! + ! --- END inlining of Segment 11 + Uind_out(1:3,icp) = Uind_out(1:3,icp)+Uind(1:3) + end do ! Loop on segments + enddo ! Loop on control points + !$OMP END DO + !$OMP END PARALLEL +end subroutine + + +end module FVW_BiotSavart diff --git a/modules/aerodyn14/src/FVW_Tests.f90 b/modules/aerodyn14/src/FVW_Tests.f90 new file mode 100644 index 0000000000..e52ccfac2e --- /dev/null +++ b/modules/aerodyn14/src/FVW_Tests.f90 @@ -0,0 +1,146 @@ +module FVW_Tests + + use NWTC_Library + + use FVW_Types + use FVW_Subs + use FVW_VortexTools + use FVW_Wings + use FVW_IO + use FVW_BiotSavart + + implicit none + +contains + + !> + subroutine Test_LatticeToSegment(iStat) + integer(IntKi), intent( out) :: iStat !< Status for test + ! Local + integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity + real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points + real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation + real(ReKi), dimension(:), allocatable :: SegSmooth !< + ! + real(ReKi), dimension(:,:,:), allocatable :: LatticePoints1 !< Lattice Points + real(ReKi), dimension(:,:,:), allocatable :: LatticePoints2 !< Lattice Points + real(ReKi), dimension(:,:), allocatable :: LatticeGamma1 !< Lattice Circulation + real(ReKi), dimension(:,:), allocatable :: LatticeGamma2 !< Lattice Circulation + real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints + real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity + integer(IntKi) :: iHeadC + integer(IntKi) :: iHeadP + integer(IntKi) :: i,j,k + integer(IntKi) :: nP + integer(IntKi) :: nC + integer(IntKi) :: nP1, nP2 + integer(IntKi) :: nC1, nC2 + integer(IntKi) :: nDepth, nSpan + integer(IntKi) :: SmoothModel + + ! --- Creating two lattice + allocate(LatticePoints1(3,2,2)) + allocate(LatticePoints2(3,3,4)) + allocate(LatticeGamma1(1,1)) ; + allocate(LatticeGamma2(2,3)) ; + LatticeGamma1=1 + ! Test shed vorticity + LatticeGamma2(:,1)=1 + LatticeGamma2(:,2)=2 + LatticeGamma2(:,3)=3 + ! Test trailed vorticity +! LatticeGamma2(1,:)=1 +! LatticeGamma2(2,:)=2 + CALL MeshMe(LatticePoints1,(/0.,0.,0./)) + CALL MeshMe(LatticePoints2,(/0.,0.,1./)) + + CALL WrVTK_Lattice('Points1.vtk',LatticePoints1, LatticeGamma1) + CALL WrVTK_Lattice('Points2.vtk',LatticePoints2, LatticeGamma2) + + ! --- Convert lattice 1 to segments + nSpan = size(LatticePoints1,2) + nDepth = size(LatticePoints1,3) + nP1 = nSpan*nDepth + nC1 = 2*(nSpan*nDepth)-nSpan-nDepth + allocate(SegConnct(1:2,1:nC1)); SegConnct=-1 + allocate(SegPoints(1:3,1:nP1)); SegPoints=-1 + allocate(SegGamma (1:nC1) ); SegGamma=-999 + allocate(SegSmooth(1:nC1) ); SegSmooth=0.0_ReKi + + iHeadP=1 + iHeadC=1 + CALL LatticeToSegments(LatticePoints1, LatticeGamma1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL printall() + CALL WrVTK_Segments('Points1_seg.vtk', SegPoints, SegConnct, SegGamma) + + allocate(Uind(1:3,1) ); Uind=0.0_ReKi + allocate(CPs (1:3,1) ); + CPs(1:3,1)=(/1.5,1.5,0./) + SegSmooth=100.0_ReKi + SmoothModel=0 ! No smooth + CALL ui_seg(CPs, 1, 1, 1, & + SegPoints, SegConnct, SegGamma, 1, nC1, nC1, nP1, & + SmoothModel, SegSmooth, Uind) + print*,'Uind',Uind + + ! --- Convert lattice 2 to segments + nSpan = size(LatticePoints2,2) + nDepth = size(LatticePoints2,3) + nP2 = nSpan*nDepth + nC2 = 2*(nSpan*nDepth)-nSpan-nDepth + deallocate(SegConnct) + deallocate(SegPoints) + deallocate(SegGamma) + allocate(SegConnct(1:2,1:nC2)); SegConnct=-1 + allocate(SegPoints(1:3,1:nP2)); SegPoints=-1 + allocate(SegGamma (1:nC2) ); SegGamma=-9999 + iHeadP=1 + iHeadC=1 + CALL LatticeToSegments(LatticePoints2, LatticeGamma2, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL printall() + CALL WrVTK_Segments('Points2_seg.vtk', SegPoints, SegConnct, SegGamma) + + ! --- Concatenate both + nP = nP1 + nP2 + nC = nC1 + nC2 + iHeadP=1 + iHeadC=1 + deallocate(SegConnct) + deallocate(SegPoints) + deallocate(SegGamma) + allocate(SegConnct(1:2,1:nC)); SegConnct=-1 + allocate(SegPoints(1:3,1:nP)); SegPoints=-1 + allocate(SegGamma (1:nC) ); SegGamma=-9999 + CALL LatticeToSegments(LatticePoints1, LatticeGamma1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(LatticePoints2, LatticeGamma2, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL printall() + CALL WrVTK_Segments('PointsBoth_seg.vtk', SegPoints, SegConnct, SegGamma) + + + contains + subroutine printall() + print*,'Points' + do i=1,size(SegPoints,2) + print*,'i',i,'Coords:', SegPoints(1:3,i) + enddo + print*,'Connectivity' + do i=1,size(SegConnct,2) + print*,'i',i,'Conn:', SegConnct(1:2,i),'Gam:', SegGamma(i) + enddo + print*,'-----------------------------' + endsubroutine + + subroutine MeshMe(M,offset) + real(ReKi), dimension(:,:,:), intent(inout) :: M + real(ReKi), dimension(3) , intent(in ):: offset + do j=1,size(M,3) + do i=1,size(M,2) + M(1,i,j)=i + offset(1) + M(2,i,j)=j + offset(2) + M(3,i,j)=0 + offset(3) + enddo + enddo + endsubroutine + endsubroutine + +end module FVW_Tests diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn14/src/FVW_VortexTools.f90 index 7e75e9ad45..1bda1d1bf1 100644 --- a/modules/aerodyn14/src/FVW_VortexTools.f90 +++ b/modules/aerodyn14/src/FVW_VortexTools.f90 @@ -1,11 +1,115 @@ MODULE FVW_VortexTools ! Contains Typical Tools for vortex methods - ! Should be independent of the Framework and only low level functions + + ! Should be *independent* of the Framework and any derived type + + ! Only low level functions ! + + use NWTC_LIBRARY IMPLICIT NONE CONTAINS + subroutine LatticeToSegments(LatticePoints, LatticeGamma, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Points 3 x nSpan x nDepth + real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< GammaPanl nSpan x nDepth + real(ReKi), dimension(:,:), intent(inout) :: SegPoints !< + integer(IntKi), dimension(:,:), intent(inout) :: SegConnct !< + real(ReKi), dimension(:), intent(inout) :: SegGamma !< + integer(IntKi), intent( out) :: iHeadP !< Index indicating where to start in SegPoints + integer(IntKi), intent( out) :: iHeadC !< Index indicating where to start in SegConnct + ! Local + integer(IntKi) :: nSpan, nDepth + integer(IntKi) :: iSpan, iDepth + real(ReKi) :: c1,c2 + integer(IntKi) :: iHeadP0, iseg1, iseg2, iseg3 ,iseg4 !< Index indicating where to start in SegPoints + real(ReKi) :: Gamma12 + real(ReKi) :: Gamma41 + + nSpan = size(LatticePoints,2) + nDepth= size(LatticePoints,3) + + + iHeadP0=iHeadP ! Storing + + ! We will need all the points, we flatten the point array + ! Loop order is important + ! Points are flattened as follows: + ! + ! 3---6 + ! | | + ! 2---5 + ! | | + ! 1---4 + ! + do iDepth = 1, nDepth + do iSpan = 1, nSpan + SegPoints(1:3,iHeadP) = LatticePoints(1:3, iSpan, iDepth) + iHeadP=iHeadP+1 + enddo + enddo + + ! --- Creating segments + ! Naming convention for point indices and segments of a panel: + ! 2---3 + ! | | + ! 1---4 + ! We go "Panel per panel" , for a given Panel, we create + ! - Segment 1-2 + ! - Segment 1-4 + ! - Segment 4-3 if the last Depth panel + ! - Segment 2-3 if the last Span panel + ! Circulation is defined positive as follows (clockwise): + ! 2->-3 + ! ^ v + ! 1-<-4 + do iDepth = 1, nDepth-1 + do iSpan = 1, nSpan-1 + iseg1 = iHeadP0 + (iSpan-1) +(iDepth-1)*nSpan ! Point 1 + iseg2 = iHeadP0 + (iSpan ) +(iDepth-1)*nSpan ! Point 2 + iseg3 = iHeadP0 + (iSpan ) +(iDepth )*nSpan ! Point 3 + iseg4 = iHeadP0 + (iSpan-1) +(iDepth )*nSpan ! Point 4 + if (iDepth==1) then + Gamma12 = LatticeGamma(iSpan,iDepth) + else + Gamma12 = LatticeGamma(iSpan,iDepth)-LatticeGamma(iSpan,iDepth-1) + endif + if (iSpan==1) then + Gamma41 = LatticeGamma(iSpan,iDepth) + else + Gamma41 = LatticeGamma(iSpan,iDepth)-LatticeGamma(iSpan-1,iDepth) + endif + !print*,iseg1,iseg2,iseg3,iseg4 + ! Segment 1-2 + SegConnct(1,iHeadC) = iseg1 + SegConnct(2,iHeadC) = iseg2 + SegGamma (iHeadC ) = Gamma12 + iHeadC=iHeadC+1 + ! Segment 1-4 + SegConnct(1,iHeadC) = iseg1 + SegConnct(2,iHeadC) = iseg4 + SegGamma (iHeadC ) = -Gamma41 + iHeadC=iHeadC+1 + ! Segment 4-3 + if (iDepth==nDepth-1) then + SegConnct(1,iHeadC) = iseg4 + SegConnct(2,iHeadC) = iseg3 + SegGamma (iHeadC ) = - LatticeGamma(iSpan,iDepth) + iHeadC=iHeadC+1 + endif + ! Segment 2-3 + if (iSpan==nSpan-1) then + SegConnct(1,iHeadC) = iseg2 + SegConnct(2,iHeadC) = iseg3 + SegGamma (iHeadC ) = LatticeGamma(iSpan,iDepth) + iHeadC=iHeadC+1 + endif + enddo + enddo + + end subroutine + END MODULE FVW_VortexTools From 63207cb90188dfd1b65d2b297f0379be05308b6d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 18 Nov 2019 15:17:38 -0700 Subject: [PATCH 004/190] FVW: Implemented near wake --- modules/aerodyn14/src/FVW.f90 | 186 ++++++++++++++++--- modules/aerodyn14/src/FVW_IO.f90 | 191 +++++++++++++++++-- modules/aerodyn14/src/FVW_Subs.f90 | 54 +++++- modules/aerodyn14/src/FVW_Types.f90 | 243 +++++++++++++++++++++++++ modules/aerodyn14/src/FVW_Wings.f90 | 64 ++++++- modules/aerodyn14/src/Registry-FVW.txt | 3 + modules/aerodyn14/src/VTK.f90 | 24 ++- 7 files changed, 718 insertions(+), 47 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 75f00307b8..ecd84c31f1 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -13,6 +13,8 @@ MODULE FVW USE FVW_Subs use FVW_IO use FVW_Wings + use FVW_BiotSavart + use FVW_Tests ! NOTE: this is a rough format that AD14 stores airfoil info. This will need ! to be replaced by the AirFoilInfo module when we couple FVW into AD15 @@ -110,6 +112,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return ! Return anything in FVW_InitOutput that should be passed back to the calling code here + CONTAINS logical function Failed() @@ -142,7 +145,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%s_LL , p%nSpan+1 , p%nWings, 'Spanwise coord LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%s_LL= -999999_ReKi; call AllocAry( m%chord_LL , p%nSpan+1 , p%nWings, 'Chord on LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%chord_LL= -999999_ReKi; ! Variables at control points/elements - call AllocAry( m%Gamma_LL, p%nSpan , p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitConstraint' ); m%Gamma_LL = -999999_ReKi; + call AllocAry( m%Gamma_LL, p%nSpan , p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Gamma_LL = -999999_ReKi; call AllocAry( m%chord_CP_LL , p%nSpan , p%nWings, 'Chord on CP LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%chord_CP_LL= -999999_ReKi; call AllocAry( m%s_CP_LL , p%nSpan , p%nWings, 'Spanwise coord CPll', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%s_CP_LL= -999999_ReKi; call AllocAry( m%CP_LL , 3 , p%nSpan , p%nWings, 'Control points LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%CP_LL= -999999_ReKi; @@ -154,9 +157,17 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%Vstr_LL , 3 , p%nSpan , p%nWings, 'Vstr on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vstr_LL= -999999_ReKi; call AllocAry( m%Vwnd_LL , 3 , p%nSpan , p%nWings, 'Wind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_LL= -999999_ReKi; ! Variables at panels points + call AllocAry( m%r_LL , 3 , p%nSpan+1 , 2 , p%nWings, 'Lifting Line Panels', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%r_LL= -999999_ReKi; call AllocAry( m%Vwnd_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_NW= -999_ReKi; call AllocAry( m%Vwnd_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; - + call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_NW= -999_ReKi; + call AllocAry( m%Vind_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; + m%Vwnd_NW(1,:,:,:)= 8 + m%Vwnd_NW(2,:,:,:)= 0 + m%Vwnd_NW(3,:,:,:)= 0 + m%Vwnd_FW(1,:,:,:)= 8 + m%Vwnd_FW(2,:,:,:)= 0 + m%Vwnd_FW(3,:,:,:)= 0 end subroutine FVW_InitMiscVars ! ============================================================================== subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) @@ -219,7 +230,7 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) ! p%nWings = InitInp%NumBl ! TODO TODO TODO Hack for AD14 mesh that is wrong - p%nWings = 1 + !p%nWings = 1 ! NOTE: temporary limitation, all wings have the same nspan p%nSpan = size(InitInp%RElm)-1 @@ -321,14 +332,15 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None ! local variables type(FVW_InputType) :: uInterp ! Interpolated/Extrapolated input - integer(intKi) :: ErrStat2 ! temporary Error status + integer(IntKi) :: ErrStat2 ! temporary Error status character(ErrMsgLen) :: ErrMsg2 ! temporary Error message type(FVW_ConstraintStateType) :: z_guess ! < + integer(IntKi) :: iW, iSpan, iAge ErrStat = ErrID_None ErrMsg = "" - print'(A,F10.3,A,F10.3,A,F10.3,A,I0)',' Update states, t:',t,' t_u:', utimes(1),' dt: ',utimes(1)-t,' ',n + print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0)',' Update states, t:',t,' t_u:', utimes(1),' dt: ',utimes(1)-t,' ',n,' nNW:',m%nNW ! Panelling wings based on input mesh provided CALL Wings_Panelling(u(1)%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -337,8 +349,13 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! TODO ANDY !CALL DistributeRequestedWind(u(1)%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return + ! Solve for circulation at t CALL FVW_CalcConstrStateResidual(t, u(1), p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return + ! Map circulation and positions between LL and NW + ! Changes: x only + CALL Wings_Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) + if (p%IntMethod .eq. idEuler1) then call FVW_Euler1( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -352,9 +369,65 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta call SetErrStat(ErrID_Fatal,'Invalid time integration method:'//Num2LStr(p%IntMethod),ErrStat,ErrMsg,'FVW_UpdateState') end IF + ! -- Propagate wake m%nNW+1+1 + do iW=1,p%nWings + do iAge=p%nNWMax+1,2,-1 + do iSpan=1,p%nSpan+1 + x%r_NW(1:3,iSpan,iAge,iW) = x%r_NW(1:3,iSpan,iAge-1,iW) + enddo + enddo + x%r_NW(1:3,:,1,iW) = -999.0_ReKi + enddo + do iW=1,p%nWings + do iAge=p%nNWMax,2,-1 + do iSpan=1,p%nSpan + x%Gamma_NW(iSpan,iAge,iW) = x%Gamma_NW(iSpan,iAge-1,iW) + enddo + enddo + x%Gamma_NW(:,1,iW) = -999.0_ReKi + enddo +! do iAge=1,m%nNW+1+1 +! print*,'iAge',iAge +! print*,'Prop', x%r_NW(1, 1:p%nSpan+1, iAge,1) +! print*,'Prop', x%r_NW(2, 1:p%nSpan+1, iAge,1) +! print*,'Prop', x%r_NW(3, 1:p%nSpan+1, iAge,1) +! enddo +! do iW=1,p%nWings +! print*,'Wing',iW +! do iAge=1,m%nNW+1+1 +! print*,'Prop',x%Gamma_NW(:,iAge,iW) +! enddo +! enddo + + ! Solve for circulation at t+dt + CALL FVW_CalcConstrStateResidual(t, u(1), p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return + + ! Map circulation and positions between LL and NW + ! Changes: x only + CALL Wings_Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) + +! do iW=1,p%nWings +! print*,'Wing',iW +! do iAge=1,m%nNW+1+1 +! print*,'Map',x%Gamma_NW(:,iAge,iW) +! enddo +! enddo +! do iAge=1,m%nNW+1+1 +! print*,'iAge',iAge +! print*,'Map', x%r_NW(1, 1:p%nSpan+1, iAge,1) +! print*,'Map', x%r_NW(2, 1:p%nSpan+1, iAge,1) +! print*,'Map', x%r_NW(3, 1:p%nSpan+1, iAge,1) +! enddo + +! if (m%nNW>3) STOP + if (m%FirstCall) then m%FirstCall=.False. endif + + + call FVW_DestroyConstrState(z_guess, ErrStat2, ErrMsg2); + contains logical function Failed() call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_UpdateStates') @@ -380,16 +453,67 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iSpan,iAge, iW + integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP real(ReKi), dimension(3) :: U_mean + integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity + real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points + real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation + real(ReKi), dimension(:) , allocatable :: SegSmooth !< Segment smooth parameter + real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints + real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity + integer(IntKi) :: SmoothModel call AllocAry( dxdt%r_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat, ErrMsg ); dxdt%r_NW= -999999_ReKi; - call AllocAry( dxdt%r_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; + call AllocAry( dxdt%r_FW , 3 , 2 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; if (p%FreeWake) then print*,'TODO free wake convection' STOP else + + ! --- Packing all vortex elements into a list of segments + call PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, nSeg, nSegP) + print*,'Number of segments',nSeg, 'Number of points',nSegP + + ! --- Computing induced velocity + ! TODO for now point by point.. + allocate(SegSmooth(1:nSeg)); + SegSmooth=10 + allocate(Uind(1:3,1)) + allocate(CPs (1:3,1)) + CPs=0 + Uind=0 + SmoothModel=idSegSmoothLambOseen + ! --- On NW + do iW=1,p%nWings + do iAge=1,m%nNW+1 + do iSpan=1,p%nSpan+1 + Uind(1:3,1)=0.0_ReKi + CPs(1:3,1) = x%r_NW(1:3,iSpan,iAge,iW) + + CALL ui_seg(CPs, 1, 1, 1, & + SegPoints, SegConnct, SegGamma, 1, nSeg, nSeg, nSegP, & + SmoothModel, SegSmooth, Uind) +! print*,'Uind',Uind + m%Vind_NW(1:3,iSpan,iAge,iW) = Uind(1:3,1) + enddo + enddo + enddo + deallocate(SegConnct) + deallocate(SegGamma) + deallocate(SegPoints) + deallocate(SegSmooth) + deallocate(Uind) + deallocate(CPs) + + U_mean(1:3)=0 + do iW=1,p%nWings; do iAge=1,m%nNW+1; do iSpan=1,p%nSpan+1; + U_mean(1:3)= U_mean(1:3)+ m%Vind_NW(1:3, iSpan, iAge, iW) + enddo; enddo; enddo + U_mean(1:3) = U_mean(1:3)/ ((m%nNW+1)*(p%nSpan+1)*p%nWings) + print*,'Mean convection velocity NW: ',U_mean(1:3) + + U_mean(1:3)=0 do iW=1,p%nWings; do iAge=1,m%nNW+1; do iSpan=1,p%nSpan+1; U_mean(1:3)= U_mean(1:3)+ m%Vwnd_NW(1:3, iSpan, iAge, iW) @@ -405,8 +529,10 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! --- Vortex points are convected with the free stream - dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) - dxdt%r_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) + dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) + + dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + m%Vind_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) endif end subroutine FVW_CalcContStateDeriv @@ -427,6 +553,7 @@ subroutine FVW_Euler1( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, Err ! local variables type(FVW_ContinuousStateType) :: dxdt ! time derivatives of continuous states real(ReKi) :: dt ! Time step + integer(IntKi) :: iAge ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -436,13 +563,28 @@ subroutine FVW_Euler1( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, Err dt = utimes(1)-t ! TODO TODO is this correct +! do iAge=1,m%nNW+1 +! print*,'iAge',iAge +! print*,'Before', x%r_NW(1, 1:p%nSpan+1, iAge,1) +! print*,'Before', x%r_NW(2, 1:p%nSpan+1, iAge,1) +! print*,'Before', x%r_NW(3, 1:p%nSpan+1, iAge,1) +! enddo + ! Update of positions x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dt * dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) - x%r_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) = x%r_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) + dt * dxdt%r_FW(1:3, 1:p%nSpan+1, 1:m%nFW+1, 1:p%nWings) - + x%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = x%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) + dt * dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) + +! do iAge=1,m%nNW+1 +! print*,'iAge',iAge +! print*,'After', x%r_NW(1, 1:p%nSpan+1, iAge,1) +! print*,'After', x%r_NW(2, 1:p%nSpan+1, iAge,1) +! print*,'After', x%r_NW(3, 1:p%nSpan+1, iAge,1) +! enddo ! Update of Gamma ! TODO, viscous diffusion, stretching + call FVW_DestroyContState(dxdt, ErrStat, ErrMsg) + end subroutine FVW_Euler1 !---------------------------------------------------------------------------------------------------------------------------------- @@ -450,15 +592,15 @@ end subroutine FVW_Euler1 !---------------------------------------------------------------------------------------------------------------------------------- !> This is a tight coupling routine for solving for the residual of the constraint state functions. subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z, OtherState, m, z_out, ErrStat, ErrMsg ) - real(DbKi), intent(IN ) :: t !< Current simulation time in seconds - type(FVW_InputType), intent(IN ) :: u !< Inputs at t - type(FVW_ParameterType), intent(IN ) :: p !< Parameters - type(FVW_ContinuousStateType), intent(IN ) :: x !< Continuous states at t - type(FVW_DiscreteStateType), intent(IN ) :: xd !< Discrete states at t - type(FVW_ConstraintStateType), intent(IN ) :: z !< Constraint states at t (possibly a guess) - type(FVW_OtherStateType), intent(IN ) :: OtherState !< Other states at t - type(FVW_MiscVarType), intent(INOUT) :: m !< Misc variables for optimization (not copied in glue code) - type(FVW_ConstraintStateType), intent( OUT) :: z_out !< Residual of the constraint state functions using + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + type(FVW_InputType), intent(in ) :: u !< Inputs at t + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< Continuous states at t + type(FVW_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraint states at t (possibly a guess) + type(FVW_OtherStateType), intent(in ) :: OtherState !< Other states at t + type(FVW_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) + type(FVW_ConstraintStateType), intent( out) :: z_out !< Residual of the constraint state functions using !! the input values described above integer(IntKi), intent( OUT) :: ErrStat !< Error status of the operation character(*), intent( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None @@ -471,7 +613,7 @@ subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z, OtherState, m, z_out, call AllocAry( z_out%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat, ErrMsg ); z_out%Gamma_LL = -999999_ReKi; - CALL Wings_ComputeCirculation(z_out%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) + CALL Wings_ComputeCirculation(t, z_out%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) end subroutine FVW_CalcConstrStateResidual @@ -510,7 +652,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg if (m%FirstCall) then print*,'>>> First Call of CalcOuput, calling panelling and constrstate' CALL Wings_Panelling(u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return - CALL Wings_ComputeCirculation(m%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) ! For plotting only + CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) ! For plotting only else m%Gamma_LL = z%Gamma_LL ! For plotting only endif diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index 3e3f7502e9..9e6379e77f 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -2,6 +2,7 @@ module FVW_IO USE FVW_Types USE FVW_Subs + use FVW_VortexTools implicit none contains @@ -84,18 +85,22 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) integer(IntKi), intent(in) :: VTKcount !< Indicates number for VTK output file (when 0, the routine will also write reference information) integer(IntKi), intent(in) :: Twidth !< Number of digits in the maximum write-out step (used to pad the VTK write-out in the filename with zeros) ! local variables - integer:: iWing + integer:: iW character(1024) :: FileName character(255) :: Label character(Twidth) :: Tstr ! string for current VTK write-out step (padded with zeros) - real(ReKi), dimension(:,:), allocatable :: Buffer - real(ReKi), dimension(:), allocatable :: Buffer1d - integer(IntKi), dimension(:,:), allocatable :: Connectivity integer :: iSeg integer :: iSpan, iNW, iFW integer :: nSpan, nNW, nWings, nFW integer :: k + real(ReKi), dimension(:,:), allocatable :: Buffer2d character(1), dimension(3) :: I2ABC =(/'A','B','C'/) + ! + integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity + real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points + real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation + integer(IntKi) :: iHeadC, iHeadP, nC, nP + !real(ReKi), dimension(:), allocatable :: SegSmooth !< ! TimeStamp write(Tstr, '(i' // trim(Num2LStr(Twidth)) //'.'// trim(Num2LStr(Twidth)) // ')') VTKcount @@ -108,23 +113,179 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! --- Blade ! --------------------------------------------------------------------------------{ ! --- Blade Quarter chord points (AC) - do iWing=1,nWings - write(Label,'(A,A)') 'BldPointCP.Bld', i2ABC(iWing) + do iW=1,nWings + write(Label,'(A,A)') 'BldPointCP.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' if ( vtk_new_ascii_file(trim(filename),Label) ) then - call vtk_dataset_polydata(m%CP_LL(1:3,1:p%nSpan,iWing)) + call vtk_dataset_polydata(m%CP_LL(1:3,1:p%nSpan,iW)) call vtk_point_data_init() - call vtk_point_data_scalar(m%Gamma_ll( 1:p%nSpan,iWing),'Gamma_ll') - call vtk_point_data_vector(m%Vind_ll (1:3,1:p%nSpan,iWing),'Vind_ll') - call vtk_point_data_vector(m%Vtot_ll (1:3,1:p%nSpan,iWing),'Vtot_ll') - call vtk_point_data_vector(m%Vstr_ll (1:3,1:p%nSpan,iWing),'Vstr_ll') - call vtk_point_data_vector(m%Vwnd_ll (1:3,1:p%nSpan,iWing),'Vwnd_ll') - call vtk_point_data_vector(m%Tang (1:3,1:p%nSpan,iWing),'Tangent') - call vtk_point_data_vector(m%Norm (1:3,1:p%nSpan,iWing),'Normal') - call vtk_point_data_vector(m%Orth (1:3,1:p%nSpan,iWing),'Orth') + call vtk_point_data_scalar(m%Gamma_ll( 1:p%nSpan,iW),'Gamma_ll') + call vtk_point_data_vector(m%Vind_ll (1:3,1:p%nSpan,iW),'Vind_ll') + call vtk_point_data_vector(m%Vtot_ll (1:3,1:p%nSpan,iW),'Vtot_ll') + call vtk_point_data_vector(m%Vstr_ll (1:3,1:p%nSpan,iW),'Vstr_ll') + call vtk_point_data_vector(m%Vwnd_ll (1:3,1:p%nSpan,iW),'Vwnd_ll') + call vtk_point_data_vector(m%Tang (1:3,1:p%nSpan,iW),'Tangent') + call vtk_point_data_vector(m%Norm (1:3,1:p%nSpan,iW),'Normal') + call vtk_point_data_vector(m%Orth (1:3,1:p%nSpan,iW),'Orth') call vtk_close_file() endif enddo + ! --- Lifting line panels + allocate(Buffer2d(1,nSpan)) + do iW=1,nWings + write(Label,'(A,A)') 'LL.Bld', i2ABC(iW) + Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' + Buffer2d(1,:)=m%Gamma_LL(:,iW) + call WrVTK_Lattice(FileName, m%r_LL(1:3,:,:,iW), Buffer2d) + enddo + ! --------------------------------------------------------------------------------} + ! --- Near wake + ! --------------------------------------------------------------------------------{ + ! --- Near wake panels + do iW=1,nWings + write(Label,'(A,A)') 'NW.Bld', i2ABC(iW) + Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' + call WrVTK_Lattice(FileName, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW)) + enddo + ! --------------------------------------------------------------------------------} + ! --- Far wake + ! --------------------------------------------------------------------------------{ + ! --- Far wake panels + !do iW=1,nWings + ! write(Label,'(A,A)') 'FW.Bld', i2ABC(iW) + ! Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' + ! call WrVTK_Lattice(FileName, x%r_FW(1:3,:,:,iW), x%Gamma_FW(:,:,iW)) + !enddo + ! --------------------------------------------------------------------------------} + ! --- All Segments + ! --------------------------------------------------------------------------------{ + nP = nWings * ( (nSpan+1)*(nNW+1) ) + nC = nWings * (2*(nSpan+1)*(nNW+1)-nSpan-nNW-2) +! nP = nP + nWings * (nSpan+1)*2 +! nC = nC + nWings * (2*(nSpan+1)*(2)-nSpan-1-2) + allocate(SegConnct(1:2,1:nC)); SegConnct=-1 + allocate(SegPoints(1:3,1:nP)); SegPoints=-1 + allocate(SegGamma (1:nC)); SegGamma =-1 + iHeadP=1 + iHeadC=1 + do iW=1,nWings + CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + enddo +! if (allocated(Buffer2d)) deallocate(Buffer2d) +! allocate(Buffer2d(1,nSpan)) +! do iW=1,nWings +! Buffer2d(1,:)=m%Gamma_LL(:,iW) +! CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), Buffer2d, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) +! enddo + Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' + CALL WrVTK_Segments(Filename, SegPoints, SegConnct, SegGamma) + + if ((iHeadP-1)/=nP) then + print*,'IO: Number of points wrongly estimated',nP, iHeadP-1 +! STOP + endif + if ((iHeadC-1)/=nC) then + print*,'IO: Number of segments wrongly estimated',nC, iHeadC-1 +! STOP + endif + + end subroutine WrVTK_FVW + +subroutine WrVTK_Segments(filename, SegPoints, SegConnct, SegGamma) + use VTK + character(len=*),intent(in) :: filename + real(ReKi), dimension(:,:), intent(in) :: SegPoints !< + integer(IntKi), dimension(:,:), intent(in) :: SegConnct !< + real(ReKi), dimension(:) , intent(in) :: SegGamma !< + if ( vtk_new_ascii_file(filename,'Sgmt') ) then + call vtk_dataset_polydata(SegPoints(1:3,:)) + call vtk_lines(SegConnct(1:2,:)-1) ! NOTE: VTK indexing at 0 + call vtk_cell_data_init() + call vtk_cell_data_scalar(SegGamma,'SegGamma') + !call vtk_point_data_init() + !call vtk_point_data_vector(Sgmt%UconvP(1:3,1:Sgmt%nP_Storage),'Uconv') + call vtk_close_file() + endif +end subroutine + +subroutine WrVTK_Lattice(filename, LatticePoints, LatticeGamma, LatticeData3d) + use VTK ! for all the vtk_* functions + character(len=*), intent(in) :: filename + real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Array of points 3 x nSpan x nDepth + real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< Array of nSpan x nDepth + real(Reki), dimension(:,:,:), intent(in ), optional :: LatticeData3d !< Array of n x nSpan x nDepth + ! + integer(IntKi), dimension(:,:), allocatable :: Connectivity + real(ReKi), dimension(:,:), allocatable :: Points + + CALL LatticeToPanlConnectivity(LatticePoints, Connectivity, Points) + + if ( vtk_new_ascii_file(filename,'')) then + call vtk_dataset_polydata(Points) + call vtk_quad(Connectivity) + call vtk_cell_data_init() + call vtk_cell_data_scalar(LatticeGamma,'Gamma_NW') + call vtk_close_file() + endif + +end subroutine WrVTK_Lattice + +subroutine LatticeToPanlConnectivity(LatticePoints, Connectivity, Points) + real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Array of points 3 x nSpan x nDepth + integer(IntKi), dimension(:,:), allocatable :: Connectivity + real(ReKi), dimension(:,:), allocatable :: Points + ! Local + integer(IntKi) :: nSpan, nDepth + integer(IntKi) :: iSpan, iDepth, k + nSpan = size(LatticePoints,2) + nDepth = size(LatticePoints,3) + + if (allocated(Connectivity)) deallocate(Connectivity) + allocate(Connectivity(1:4, 1:(nSpan-1)*(nDepth-1))) + if (allocated(Points)) deallocate(Points) + allocate(Points(1:3, 1:nSpan*nDepth)) + + k=1 + do iDepth=1,nDepth-1; do iSpan=1,nSpan-1 + Connectivity(1,k)=(iDepth-1)*nSpan+(iSpan-1) + Connectivity(2,k)=(iDepth-1)*nSpan+(iSpan ) + Connectivity(3,k)=(iDepth )*nSpan+(iSpan) + Connectivity(4,k)=(iDepth )*nSpan+(iSpan-1) + k=k+1 + enddo; enddo + + k=1 + do iDepth=1,nDepth; do iSpan=1,nSpan + Points(1:3,k) = LatticePoints(1:3,iSpan,iDepth) + k=k+1 + enddo; enddo + +! do iWing=1,p%NumBl +! if ( vtk_new_ascii_file(trim(filename),Label) ) then +! ! Buffer for points +! k=1; do iNW=1,nNW; do iSpan=1,nSpan +! Buffer(1:3,k) = Misc%NWake%r_nearj(1:3,iSpan,iNW,iWing) +! k=k+1 +! enddo; enddo +! call vtk_dataset_polydata(Buffer) +! call vtk_quad(Connectivity) +! call vtk_cell_data_init() +! ! Buffer for Gammas m1 +! k=1; do iNW=1,(nNW-1); do iSpan=1,(nSpan-1) +! if (iSpan0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%TE))-1 ) = PACK(InData%TE,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%TE) END IF + IF ( .NOT. ALLOCATED(InData%r_LL) ) 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%r_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_LL,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_LL,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_LL,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%r_LL,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_LL,4) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%r_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%r_LL))-1 ) = PACK(InData%r_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%r_LL) + END IF IF ( .NOT. ALLOCATED(InData%s_LL) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -1316,6 +1419,50 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%Vwnd_FW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vwnd_FW))-1 ) = PACK(InData%Vwnd_FW,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%Vwnd_FW) + END IF + IF ( .NOT. ALLOCATED(InData%Vind_NW) ) 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%Vind_NW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_NW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_NW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_NW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_NW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_NW,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_NW,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_NW,4) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vind_NW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vind_NW))-1 ) = PACK(InData%Vind_NW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vind_NW) + END IF + IF ( .NOT. ALLOCATED(InData%Vind_FW) ) 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%Vind_FW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_FW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_FW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_FW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_FW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_FW,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_FW,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_FW,4) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vind_FW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vind_FW))-1 ) = PACK(InData%Vind_FW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vind_FW) END IF IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nNW Int_Xferred = Int_Xferred + 1 @@ -1419,6 +1566,38 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%TE) DEALLOCATE(mask3) END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! r_LL 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%r_LL)) DEALLOCATE(OutData%r_LL) + ALLOCATE(OutData%r_LL(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%r_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask4(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 mask4.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask4 = .TRUE. + IF (SIZE(OutData%r_LL)>0) OutData%r_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%r_LL))-1 ), mask4, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%r_LL) + DEALLOCATE(mask4) + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! s_LL not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -1844,6 +2023,70 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg IF (SIZE(OutData%Vwnd_FW)>0) OutData%Vwnd_FW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vwnd_FW))-1 ), mask4, 0.0_ReKi ) Re_Xferred = Re_Xferred + SIZE(OutData%Vwnd_FW) DEALLOCATE(mask4) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vind_NW 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%Vind_NW)) DEALLOCATE(OutData%Vind_NW) + ALLOCATE(OutData%Vind_NW(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%Vind_NW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask4(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 mask4.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask4 = .TRUE. + IF (SIZE(OutData%Vind_NW)>0) OutData%Vind_NW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vind_NW))-1 ), mask4, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vind_NW) + DEALLOCATE(mask4) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vind_FW 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%Vind_FW)) DEALLOCATE(OutData%Vind_FW) + ALLOCATE(OutData%Vind_FW(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%Vind_FW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask4(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 mask4.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask4 = .TRUE. + IF (SIZE(OutData%Vind_FW)>0) OutData%Vind_FW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vind_FW))-1 ), mask4, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vind_FW) + DEALLOCATE(mask4) END IF OutData%nNW = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index b49a6706c0..fd3daf011c 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -106,9 +106,9 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) do iSpan = 1,p%nSpan+1 P_ref = Meshes(iW)%Position(1:3, iSpan ) DP_LE(1:3) = 0.0 - DP_LE(1) = +m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 + DP_LE(1) = -m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 DP_TE(1:3) = 0.0 - DP_TE(1) = -m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 + DP_TE(1) = +m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 m%LE(1:3, iSpan, iW) = P_ref + DP_LE m%TE(1:3, iSpan, iW) = P_ref + DP_TE enddo @@ -139,20 +139,62 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) end do enddo - ! --- Position of control points + ! --- Lifting Line/ Bound Circulation panel + ! For now: goes from 1/4 chord to TE + ! More panelling options may be considered in the future + do iW = 1,p%nWings + do iSpan = 1,p%nSpan+1 + m%r_LL(1:3,iSpan,1,iW)= m%TE(1:3,iSpan,iW)*0.25_ReKi+m%LE(1:3,iSpan,iW)*0.75_ReKi ! 1/4 chord + m%r_LL(1:3,iSpan,2,iW)= m%TE(1:3,iSpan,iW) ! TE + enddo + enddo + + ! --- Position of control points CP_LL + ! For now: placed at the "chordwise" middle of the LL panel ! NOTE: separated from other loops just in case a special discretization is used do iW = 1,p%nWings - call interp_lin(m%s_LL(:,iW), m%TE(1,:,iW)*0.25_ReKi+m%LE(1,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(1,:,iW)) - call interp_lin(m%s_LL(:,iW), m%TE(2,:,iW)*0.25_ReKi+m%LE(2,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(2,:,iW)) - call interp_lin(m%s_LL(:,iW), m%TE(3,:,iW)*0.25_ReKi+m%LE(3,:,iW)*0.75_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(3,:,iW)) + call interp_lin(m%s_LL(:,iW), m%r_LL(1,:,1,iW)*0.5_ReKi+m%r_LL(1,:,2,iW)*0.5_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(1,:,iW)) + call interp_lin(m%s_LL(:,iW), m%r_LL(2,:,1,iW)*0.5_ReKi+m%r_LL(2,:,2,iW)*0.5_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(2,:,iW)) + call interp_lin(m%s_LL(:,iW), m%r_LL(3,:,1,iW)*0.5_ReKi+m%r_LL(3,:,2,iW)*0.5_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(3,:,iW)) enddo + end subroutine Wings_Panelling + !> Make sure the First panel of the NW match the last panel of the Trailing edge + !! - Same position of points + !! - Same circulation + subroutine Wings_Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) + use Interpolation, only: interp_lin + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states + type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + integer(IntKi) ::iSpan , iW + + ! First panel of NW has same position as last panel of lifting line + do iW = 1,p%nWings + do iSpan = 1,p%nSpan+1 + x%r_NW(1:3, iSpan, 1, iW) = m%r_LL(1:3, iSpan, 2, iW) + enddo + enddo + ! Circulations of last panel of lifting line are the same as first NW panel + do iW = 1,p%nWings + do iSpan = 1,p%nSpan + x%Gamma_NW(iSpan, 1, iW) = z%Gamma_LL(iSpan,iW) + enddo + enddo + end subroutine Wings_Map_LL_NW !---------------------------------------------------------------------------------------------------------------------------------- !> - subroutine Wings_ComputeCirculation(Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg) + subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines real(ReKi), dimension(:,:), intent(in ) :: Gamma_LL_prev !< Previous/Guessed circulation type(FVW_InputType), intent(in ) :: u !< Parameters @@ -170,7 +212,13 @@ subroutine Wings_ComputeCirculation(Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat if (p%CirculationMethod==idCircPrescribed) then print*,'>>>Prescribing circulation' do iW = 1, p%nWings !Loop over lifting lines - Gamma_LL(1:p%nSpan,iW) = p%PrescribedCirculation(1:p%nSpan) + if (t<5) then + ! Slow start + print*,'Slow start' + Gamma_LL(1:p%nSpan,iW) = (t/5)*p%PrescribedCirculation(1:p%nSpan) + else + Gamma_LL(1:p%nSpan,iW) = p%PrescribedCirculation(1:p%nSpan) + endif enddo else if (p%CirculationMethod==idCircPolarData) then diff --git a/modules/aerodyn14/src/Registry-FVW.txt b/modules/aerodyn14/src/Registry-FVW.txt index 2a4e1585a5..3886474114 100644 --- a/modules/aerodyn14/src/Registry-FVW.txt +++ b/modules/aerodyn14/src/Registry-FVW.txt @@ -29,6 +29,7 @@ typedef FVW/FVW MiscVarType Logical # Variables at wing extent typedef ^ ^ ReKi LE ::: - - "Leading edge points" - typedef ^ ^ ReKi TE ::: - - "Trailing edge points" - +typedef ^ ^ ReKi r_LL :::: - - "Position of the Lifting line panels" - typedef ^ ^ ReKi s_LL :: - - "Spanwise coordinate of LL elements" m typedef ^ ^ ReKi chord_LL :: - - "chord on LL cp " m # Variables at control point - Dimensions nSpan @@ -45,6 +46,8 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi Vwnd_LL ::: - - "Wind on lifting line control points" m/s typedef ^ ^ ReKi Vwnd_NW :::: - - "Wind on near wake panels" m/s typedef ^ ^ ReKi Vwnd_FW :::: - - "Wind on far wake panels" m/s +typedef ^ ^ ReKi Vind_NW :::: - - "Induced velocity on near wake panels" m/s +typedef ^ ^ ReKi Vind_FW :::: - - "Induced velocity on far wake panels" m/s typedef ^ ^ IntKi nNW - - - "Number of active near wake panels" - typedef ^ ^ IntKi nFW - - - "Number of active far wake panels" - diff --git a/modules/aerodyn14/src/VTK.f90 b/modules/aerodyn14/src/VTK.f90 index 6d5752d79e..b4ac5d6397 100755 --- a/modules/aerodyn14/src/VTK.f90 +++ b/modules/aerodyn14/src/VTK.f90 @@ -42,6 +42,10 @@ module VTK vtk_point_data_scalar_grid2D, & vtk_point_data_scalar_grid end interface + interface vtk_cell_data_scalar; module procedure & + vtk_cell_data_scalar_1d,& + vtk_cell_data_scalar_2d + end interface public private:: vtk_unit, bFileOpen, nData, nPoints,bBinary, buffer @@ -453,7 +457,7 @@ subroutine vtk_cell_data_init() endif end subroutine - subroutine vtk_cell_data_scalar(D,sname) + subroutine vtk_cell_data_scalar_1d(D,sname) real(ReKi), dimension(:),intent(in)::D character(len=*),intent(in) ::sname @@ -470,6 +474,24 @@ subroutine vtk_cell_data_scalar(D,sname) endif endif end subroutine + + subroutine vtk_cell_data_scalar_2d(D,sname) + real(ReKi), dimension(:,:),intent(in)::D + character(len=*),intent(in) ::sname + + if ( bFileOpen ) then + if (bBinary) then + write(vtk_unit)'SCALARS '//trim(sname)//' double 1'//NL + write(vtk_unit)'LOOKUP_TABLE default'//NL + write(vtk_unit)D + write(vtk_unit)NL + else + write(vtk_unit,fmt='(A,A,A)') 'SCALARS ', sname, ' double' + write(vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(vtk_unit,'(1'//RFMT//')')D + endif + endif + end subroutine subroutine vtk_cell_data_vector(D,sname) From 3f711422e119ef210d0cde85e593bfd3be5de73c Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 19 Nov 2019 10:01:26 -0700 Subject: [PATCH 005/190] VC-AD15: start changing AD15 to include FVW --- modules/aerodyn/src/AeroDyn.f90 | 14 ++++---- modules/aerodyn/src/AeroDyn_IO.f90 | 41 +++++++++++++----------- modules/aerodyn/src/AeroDyn_Registry.txt | 2 +- modules/aerodyn/src/AeroDyn_Types.f90 | 2 +- 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index a444b2ede7..6a5ca2adad 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -27,6 +27,7 @@ module AeroDyn use BEMT use AirfoilInfo use NWTC_LAPACK +! use FVW implicit none @@ -1505,9 +1506,9 @@ SUBROUTINE ValidateInputData( InitInp, InputFileData, NumBl, ErrStat, ErrMsg ) if (NumBl > MaxBl .or. NumBl < 1) call SetErrStat( ErrID_Fatal, 'Number of blades must be between 1 and '//trim(num2lstr(MaxBl))//'.', ErrSTat, ErrMsg, RoutineName ) if (InputFileData%DTAero <= 0.0) call SetErrStat ( ErrID_Fatal, 'DTAero must be greater than zero.', ErrStat, ErrMsg, RoutineName ) - if (InputFileData%WakeMod /= WakeMod_None .and. InputFileData%WakeMod /= WakeMod_BEMT .and. InputFileData%WakeMod /= WakeMod_DBEMT) then - call SetErrStat ( ErrID_Fatal, 'WakeMod must '//trim(num2lstr(WakeMod_None))//' (none), '//trim(num2lstr(WakeMod_BEMT))//' (BEMT),'// & - 'or '//trim(num2lstr(WakeMod_DBEMT))//' (DBEMT).', ErrStat, ErrMsg, RoutineName ) + if (InputFileData%WakeMod /= WakeMod_None .and. InputFileData%WakeMod /= WakeMod_BEMT .and. InputFileData%WakeMod /= WakeMod_DBEMT .and. InputFileData%WakeMod /= WakeMod_FVW) then + call SetErrStat ( ErrID_Fatal, 'WakeMod must be value of '//trim(num2lstr(WakeMod_None))//' (none), '//trim(num2lstr(WakeMod_BEMT))//' (BEMT), '// & + trim(num2lstr(WakeMod_DBEMT))//' (DBEMT), or '//trim(num2lstr(WakeMod_FVW))//' (FVW).',ErrStat, ErrMsg, RoutineName ) end if if (InputFileData%AFAeroMod /= AFAeroMod_Steady .and. InputFileData%AFAeroMod /= AFAeroMod_BL_unsteady) then @@ -1674,8 +1675,8 @@ SUBROUTINE ValidateInputData( InitInp, InputFileData, NumBl, ErrStat, ErrMsg ) call SetErrStat( ErrID_Fatal, 'Steady blade airfoil aerodynamics must be used for linearization. Set AFAeroMod=1.', ErrStat, ErrMsg, RoutineName ) end if - if (InputFileData%WakeMod == WakeMod_DBEMT) then - call SetErrStat( ErrID_Fatal, 'DBEMT cannot currently be used for linearization. Set WakeMod=0 or WakeMod=1.', ErrStat, ErrMsg, RoutineName ) + if (InputFileData%WakeMod == WakeMod_DBEMT .or. InputFileData%WakeMod == WakeMod_FVW) then + call SetErrStat( ErrID_Fatal, 'DBEMT and FVW cannot currently be used for linearization. Set WakeMod=0 or WakeMod=1.', ErrStat, ErrMsg, RoutineName ) end if end if @@ -1827,7 +1828,8 @@ SUBROUTINE Init_BEMTmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, InitInp%aTol = InputFileData%IndToler InitInp%useTipLoss = InputFileData%TipLoss InitInp%useHubLoss = InputFileData%HubLoss - InitInp%useInduction = InputFileData%WakeMod /= WakeMod_none +!FIXME: check the next flag. Not sure if FVW can be used with it or not. + InitInp%useInduction = (InputFileData%WakeMod /= WakeMod_none .and. InputFileData%WakeMod /= WakeMod_FVW) InitInp%useTanInd = InputFileData%TanInd InitInp%useAIDrag = InputFileData%AIDrag InitInp%useTIDrag = InputFileData%TIDrag diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index bfb319937a..f5070f596f 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1500,6 +1500,7 @@ MODULE AeroDyn_IO integer(intKi), parameter :: WakeMod_none = 0 integer(intKi), parameter :: WakeMod_BEMT = 1 integer(intKi), parameter :: WakeMod_DBEMT = 2 + integer(intKi), parameter :: WakeMod_FVW = 3 integer(intKi), parameter :: AFAeroMod_steady = 1 ! steady model integer(intKi), parameter :: AFAeroMod_BL_unsteady = 2 ! Beddoes-Leishman unsteady model @@ -1990,8 +1991,8 @@ SUBROUTINE ReadPrimaryFile( InputFile, InputFileData, ADBlFile, OutFileRoot, UnE CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) END IF - ! WakeMod - Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT} (-): - CALL ReadVar( UnIn, InputFile, InputFileData%WakeMod, "WakeMod", "Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT} (-)", ErrStat2, ErrMsg2, UnEc) + ! WakeMod - Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT, 3=FVW} (-): + CALL ReadVar( UnIn, InputFile, InputFileData%WakeMod, "WakeMod", "Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT, 3=FVW} (-)", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! AFAeroMod - Type of airfoil aerodynamics model {1=steady model, 2=Beddoes-Leishman unsteady model} (-): @@ -2071,11 +2072,11 @@ SUBROUTINE ReadPrimaryFile( InputFile, InputFileData, ADBlFile, OutFileRoot, UnE CALL ReadCom( UnIn, InputFile, 'Section Header: Blade-Element/Momentum Theory Options', ErrStat2, ErrMsg2, UnEc ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - ! SkewMod - Type of skewed-wake correction model {1=uncoupled, 2=Pitt/Peters, 3=coupled} (-) [unused when WakeMod=0]: - CALL ReadVar( UnIn, InputFile, InputFileData%SkewMod, "SkewMod", "Type of skewed-wake correction model {1=uncoupled, 2=Pitt/Peters, 3=coupled} (-) [unused when WakeMod=0]", ErrStat2, ErrMsg2, UnEc) + ! SkewMod - Type of skewed-wake correction model {1=uncoupled, 2=Pitt/Peters, 3=coupled} (-) [unused when WakeMod={0|3}]: + CALL ReadVar( UnIn, InputFile, InputFileData%SkewMod, "SkewMod", "Type of skewed-wake correction model {1=uncoupled, 2=Pitt/Peters, 3=coupled} (-) [unused when WakeMod={0|3}]", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - ! SkewModFactor - Constant used in Pitt/Peters skewed wake model {or default is 15/32*pi} (-) [used only when WakeMod/=0 and SkewMod=2]: + ! SkewModFactor - Constant used in Pitt/Peters skewed wake model {or default is 15/32*pi} (-) [used only when WakeMod/={0|3} and SkewMod=2]: Line = "" CALL ReadVar( UnIn, InputFile, Line, "SkewModFactor", "Constant used in Pitt/Peters skewed wake model {or default} (-)", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -2090,29 +2091,29 @@ SUBROUTINE ReadPrimaryFile( InputFile, InputFileData, ADBlFile, OutFileRoot, UnE END IF - ! TipLoss - Use the Prandtl tip-loss model? (flag) [unused when WakeMod=0]: - CALL ReadVar( UnIn, InputFile, InputFileData%TipLoss, "TipLoss", "Use the Prandtl tip-loss model? (flag) [unused when WakeMod=0]", ErrStat2, ErrMsg2, UnEc) + ! TipLoss - Use the Prandtl tip-loss model? (flag) [unused when WakeMod={0|3}]: + CALL ReadVar( UnIn, InputFile, InputFileData%TipLoss, "TipLoss", "Use the Prandtl tip-loss model? (flag) [unused when WakeMod={0|3}]", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - ! HubLoss - Use the Prandtl hub-loss model? (flag) [unused when WakeMod=0]: - CALL ReadVar( UnIn, InputFile, InputFileData%HubLoss, "HubLoss", "Use the Prandtl hub-loss model? (flag) [unused when WakeMod=0]", ErrStat2, ErrMsg2, UnEc) + ! HubLoss - Use the Prandtl hub-loss model? (flag) [unused when WakeMod={0|3}]: + CALL ReadVar( UnIn, InputFile, InputFileData%HubLoss, "HubLoss", "Use the Prandtl hub-loss model? (flag) [unused when WakeMod={0|3}]", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - ! TanInd - Include tangential induction in BEMT calculations? (flag) [unused when WakeMod=0]: - CALL ReadVar( UnIn, InputFile, InputFileData%TanInd, "TanInd", "Include tangential induction in BEMT calculations? (flag) [unused when WakeMod=0]", ErrStat2, ErrMsg2, UnEc) + ! TanInd - Include tangential induction in BEMT calculations? (flag) [unused when WakeMod={0|3}]: + CALL ReadVar( UnIn, InputFile, InputFileData%TanInd, "TanInd", "Include tangential induction in BEMT calculations? (flag) [unused when WakeMod={0|3}]", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - ! AIDrag - Include the drag term in the axial-induction calculation? (flag) [unused when WakeMod=0]: - CALL ReadVar( UnIn, InputFile, InputFileData%AIDrag, "AIDrag", "Include the drag term in the axial-induction calculation? (flag) [unused when WakeMod=0]", ErrStat2, ErrMsg2, UnEc) + ! AIDrag - Include the drag term in the axial-induction calculation? (flag) [unused when WakeMod={0|3}]: + CALL ReadVar( UnIn, InputFile, InputFileData%AIDrag, "AIDrag", "Include the drag term in the axial-induction calculation? (flag) [unused when WakeMod={0|3}]", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - ! TIDrag - Include the drag term in the tangential-induction calculation? (flag) [unused when WakeMod=0 or TanInd=FALSE]: - CALL ReadVar( UnIn, InputFile, InputFileData%TIDrag, "TIDrag", "Include the drag term in the tangential-induction calculation? (flag) [unused when WakeMod=0 or TanInd=FALSE]", ErrStat2, ErrMsg2, UnEc) + ! TIDrag - Include the drag term in the tangential-induction calculation? (flag) [unused when WakeMod={0|3} or TanInd=FALSE]: + CALL ReadVar( UnIn, InputFile, InputFileData%TIDrag, "TIDrag", "Include the drag term in the tangential-induction calculation? (flag) [unused when WakeMod={0|3} or TanInd=FALSE]", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - ! IndToler - Convergence tolerance for BEM induction factors (or "default"] (-) [unused when WakeMod=0]: + ! IndToler - Convergence tolerance for BEM induction factors (or "default"] (-) [unused when WakeMod={0|3}]: Line = "" - CALL ReadVar( UnIn, InputFile, Line, "IndToler", "Convergence tolerance for BEM induction factors (-) [unused when WakeMod=0]", ErrStat2, ErrMsg2, UnEc) + CALL ReadVar( UnIn, InputFile, Line, "IndToler", "Convergence tolerance for BEM induction factors (-) [unused when WakeMod={0|3}]", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) CALL Conv2UC( Line ) @@ -2129,8 +2130,8 @@ SUBROUTINE ReadPrimaryFile( InputFile, InputFileData, ADBlFile, OutFileRoot, UnE END IF - ! MaxIter - Maximum number of iteration steps [unused when WakeMod=0] (-): - CALL ReadVar( UnIn, InputFile, InputFileData%MaxIter, "MaxIter", "Maximum number of iteration steps (-) [unused when WakeMod=0]", ErrStat2, ErrMsg2, UnEc) + ! MaxIter - Maximum number of iteration steps [unused when WakeMod={0|3}] (-): + CALL ReadVar( UnIn, InputFile, InputFileData%MaxIter, "MaxIter", "Maximum number of iteration steps (-) [unused when WakeMod={0|3}]", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! Return on error at end of section @@ -2548,6 +2549,8 @@ SUBROUTINE AD_PrintSum( InputFileData, p, u, y, ErrStat, ErrMsg ) Msg = 'Blade-Element/Momentum Theory' case (WakeMod_DBEMT) Msg = 'Dynamic Blade-Element/Momentum Theory' + case (WakeMod_FVW) + Msg = 'Free Vortex Wake Theory' case (WakeMod_None) Msg = 'steady' case default diff --git a/modules/aerodyn/src/AeroDyn_Registry.txt b/modules/aerodyn/src/AeroDyn_Registry.txt index 9f542dcde5..33366abb7a 100644 --- a/modules/aerodyn/src/AeroDyn_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Registry.txt @@ -61,7 +61,7 @@ typedef ^ InitOutputType ReKi TwrDiam {:} - - "Diameter of tower at node" m # ..... Primary Input file data ................................................................................................... typedef ^ AD_InputFile DbKi DTAero - - - "Time interval for aerodynamic calculations {or "default"}" s -typedef ^ AD_InputFile IntKi WakeMod - - - "Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT}" - +typedef ^ AD_InputFile IntKi WakeMod - - - "Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT, 3=FVW}" - typedef ^ AD_InputFile IntKi AFAeroMod - - - "Type of blade airfoil aerodynamics model {1=steady model, 2=Beddoes-Leishman unsteady model}" - typedef ^ AD_InputFile IntKi TwrPotent - - - "Type tower influence on wind based on potential flow around the tower {0=none, 1=baseline potential flow, 2=potential flow with Bak correction}" - typedef ^ AD_InputFile LOGICAL TwrShadow - - - "Calculate tower influence on wind based on downstream tower shadow?" - diff --git a/modules/aerodyn/src/AeroDyn_Types.f90 b/modules/aerodyn/src/AeroDyn_Types.f90 index 7a5f329040..e89e839f7b 100644 --- a/modules/aerodyn/src/AeroDyn_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Types.f90 @@ -89,7 +89,7 @@ MODULE AeroDyn_Types ! ========= AD_InputFile ======= TYPE, PUBLIC :: AD_InputFile REAL(DbKi) :: DTAero !< Time interval for aerodynamic calculations {or "default"} [s] - INTEGER(IntKi) :: WakeMod !< Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT} [-] + INTEGER(IntKi) :: WakeMod !< Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT, 3=FVW} [-] INTEGER(IntKi) :: AFAeroMod !< Type of blade airfoil aerodynamics model {1=steady model, 2=Beddoes-Leishman unsteady model} [-] INTEGER(IntKi) :: TwrPotent !< Type tower influence on wind based on potential flow around the tower {0=none, 1=baseline potential flow, 2=potential flow with Bak correction} [-] LOGICAL :: TwrShadow !< Calculate tower influence on wind based on downstream tower shadow? [-] From db6fc4e0f0791b02da995bac59c997316fcf9748 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 19 Nov 2019 10:23:49 -0700 Subject: [PATCH 006/190] change file to permissions 644 --- modules/aerodyn14/src/VTK.f90 | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 modules/aerodyn14/src/VTK.f90 diff --git a/modules/aerodyn14/src/VTK.f90 b/modules/aerodyn14/src/VTK.f90 old mode 100755 new mode 100644 From 6a38af5b98bd226d1c3ff021a79a7c8a2b878491 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 19 Nov 2019 11:17:48 -0700 Subject: [PATCH 007/190] FVW: remove FVW from AD14, and move files to AD15 --- modules/aerodyn/CMakeLists.txt | 11 + modules/aerodyn/src/AeroDyn.f90 | 141 ++- modules/{aerodyn14 => aerodyn}/src/FVW.f90 | 0 .../src/FVW_BiotSavart.f90 | 0 modules/{aerodyn14 => aerodyn}/src/FVW_IO.f90 | 0 .../{aerodyn14 => aerodyn}/src/FVW_Subs.f90 | 0 .../{aerodyn14 => aerodyn}/src/FVW_Tests.f90 | 0 .../{aerodyn14 => aerodyn}/src/FVW_Types.f90 | 0 .../src/VTK.f90 => aerodyn/src/FVW_VTK.f90} | 0 .../src/FVW_VortexTools.f90 | 0 .../{aerodyn14 => aerodyn}/src/FVW_Wings.f90 | 0 .../src/Registry-FVW.txt | 0 modules/aerodyn14/CMakeLists.txt | 11 - modules/aerodyn14/src/AeroDyn14.f90 | 166 +-- modules/aerodyn14/src/AeroDyn14_Types.f90 | 972 +----------------- modules/aerodyn14/src/AeroSubs.f90 | 17 - modules/aerodyn14/src/Registry-AD14.txt | 14 - modules/elastodyn/src/ElastoDyn_IO.f90 | 4 +- modules/openfast-library/src/FAST_Subs.f90 | 24 +- 19 files changed, 193 insertions(+), 1167 deletions(-) rename modules/{aerodyn14 => aerodyn}/src/FVW.f90 (100%) rename modules/{aerodyn14 => aerodyn}/src/FVW_BiotSavart.f90 (100%) rename modules/{aerodyn14 => aerodyn}/src/FVW_IO.f90 (100%) rename modules/{aerodyn14 => aerodyn}/src/FVW_Subs.f90 (100%) rename modules/{aerodyn14 => aerodyn}/src/FVW_Tests.f90 (100%) rename modules/{aerodyn14 => aerodyn}/src/FVW_Types.f90 (100%) rename modules/{aerodyn14/src/VTK.f90 => aerodyn/src/FVW_VTK.f90} (100%) rename modules/{aerodyn14 => aerodyn}/src/FVW_VortexTools.f90 (100%) rename modules/{aerodyn14 => aerodyn}/src/FVW_Wings.f90 (100%) rename modules/{aerodyn14 => aerodyn}/src/Registry-FVW.txt (100%) diff --git a/modules/aerodyn/CMakeLists.txt b/modules/aerodyn/CMakeLists.txt index ba739e9dd8..12e46eadf3 100644 --- a/modules/aerodyn/CMakeLists.txt +++ b/modules/aerodyn/CMakeLists.txt @@ -21,6 +21,7 @@ if (GENERATE_TYPES) generate_f90_types(src/DBEMT_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/DBEMT_Types.f90) generate_f90_types(src/UnsteadyAero_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/UnsteadyAero_Types.f90) generate_f90_types(src/AeroDyn_Driver_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/AeroDyn_Driver_Types.f90 -noextrap) + generate_f90_types(src/Registry-FVW.txt ${CMAKE_CURRENT_LIST_DIR}/src/FVW_Types.f90) endif() # AeroDyn lib @@ -39,6 +40,16 @@ set(AD_LIBS_SOURCES src/BEMT_Types.f90 src/DBEMT_Types.f90 src/UnsteadyAero_Types.f90 + + src/FVW.f90 + src/FVW_IO.f90 + src/FVW_VortexTools.f90 + src/FVW_Wings.f90 + src/FVW_Subs.f90 + src/FVW_BiotSavart.f90 + src/FVW_Tests.f90 + src/FVW_Types.f90 + src/FVW_VTK.f90 ) add_library(aerodynlib ${AD_LIBS_SOURCES}) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 6a5ca2adad..31d5de7423 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -378,7 +378,76 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut call BEMT_CopyInput( m%BEMT_u(1), m%BEMT_u(2), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - +!!! !------------------------------------------------------------------------------------------------- +!!! ! Initialize FVW module if it is used +!!! !------------------------------------------------------------------------------------------------- +!!! if (p%UseFVW ) then +!!! +!!! ! Copy some things to the InitInp. When FVW is incorporated into a different module, there may +!!! ! be some additional logic necessary to put it into the correct form for FVW to use (AD15 stores +!!! ! things differently) +!!! InitInp%FVW%FVWFileName = InitInp%FVWFileName +!!! InitInp%FVW%NumBl = p%NumBl +!!! +!!! ! --- TODO TODO TODO ANDY +!!! ! Change this so that it would match AD 15 mesh +!!! ! NOTE: This mesh does not include the azimuthal differences between blades! +!!! ! It's just the spanwise location. +!!! ! Also, it is off compared to the initial position of the blade +!!! ! Also, it's centered on the hub, but that's fine for now +!!! IF (.NOT. ALLOCATED( InitInp%FVW%Chord)) ALLOCATE ( InitInp%FVW%Chord( p%Element%NElm )) +!!! IF (.NOT. ALLOCATED( InitInp%FVW%RElm )) ALLOCATE ( InitInp%FVW%RElm( p%Element%NElm )) +!!! InitInp%FVW%RElm = p%Element%RElm +!!! InitInp%FVW%Chord = p%Blade%C +!!! ALLOCATE( InitInp%FVW%WingsMesh(p%NumBl), STAT = ErrStatLcl ) +!!! IF (ErrStatLcl /= 0) THEN +!!! CALL SetErrStat ( ErrID_Fatal, 'Could not allocate InitInp%FVW%WingsMesh (meshes)', ErrStat,ErrMess,RoutineName ) +!!! RETURN +!!! END IF +!!! DO IB = 1, p%NumBl +!!! CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & +!!! ,DestMesh = InitInp%FVW%WingsMesh(IB) & +!!! ,CtrlCode = MESH_COUSIN & +!!! ,Orientation = .TRUE. & +!!! ,TranslationVel = .TRUE. & +!!! ,RotationVel = .TRUE. & +!!! ,ErrStat = ErrStatLcl & +!!! ,ErrMess = ErrMessLcl ) +!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) +!!! IF (ErrStat >= AbortErrLev) RETURN +!!! ENDDO +!!! ! ---- END TODO +!!! +!!! call FVW_Init( InitInp%FVW, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, Interval, InitOut%FVW, ErrStatLcl, ErrMessLcl ) +!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) +!!! +!!! ! If anything is passed back in InitOut%FVW, deal with it here... +!!! +!!! +!!! +!!! ! TODO ANDY +!!! !FIXME This really probably should be done inside of FVW_Init instead of here. +!!! ! Not entirely sure how to pass the u%InputMarkers in though. +!!! ALLOCATE( u%FVW%WingsMesh(p%NumBl), STAT = ErrStatLcl ) +!!! IF (ErrStatLcl /= 0) THEN +!!! CALL SetErrStat ( ErrID_Fatal, 'Could not allocate u%FVW%InputMarkers (meshes)', ErrStat,ErrMess,RoutineName ) +!!! RETURN +!!! END IF +!!! DO IB = 1, p%NumBl +!!! CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & +!!! ,DestMesh = u%FVW%WingsMesh(IB) & +!!! ,CtrlCode = MESH_COUSIN & +!!! ,Orientation = .TRUE. & +!!! ,TranslationVel = .TRUE. & +!!! ,RotationVel = .TRUE. & +!!! ,ErrStat = ErrStatLcl & +!!! ,ErrMess = ErrMessLcl ) +!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) +!!! IF (ErrStat >= AbortErrLev) RETURN +!!! ENDDO +!!! endif +!!! + !............................................................................................ ! Define outputs here !............................................................................................ @@ -1000,6 +1069,8 @@ subroutine AD_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) ! Close files here: +!!! IF (p%UseFVW ) CALL FVW_End( u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, y%FVW, m%FVW, ErrStat, ErrMess ) + ! Destroy the input data: @@ -1088,6 +1159,20 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat call BEMT_UpdateStates(t, n, m%BEMT_u(1), m%BEMT_u(2), p%BEMT, x%BEMT, xd%BEMT, z%BEMT, OtherState%BEMT, p%AFI%AFInfo, m%BEMT, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +!!! type(FVW_InputType) :: u_FVW(1) !< FVW inputs +!!! REAL(DbKi) :: utimes_FVW(1) !< Times associated with u(:), in seconds +!!! +!!! if (p%UseFVW) then +!!! !if (abs(t-utimes(2))>1e-6 ) then +!!! ! print*,'Problem in AD14 update state, need to adapt which u we provide to FVW' +!!! ! STOP +!!! !endif +!!! ! Setting u(1)%FVW +!!! call AD14_to_FVW_u(u(1),p,u(1)%FVW,ErrStat,ErrMess) +!!! u_FVW(1) = u(1)%FVW +!!! utimes_FVW(1) = utimes(1) +!!! CALL FVW_UpdateStates( t, n, u_FVW, utimes_FVW, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat, ErrMess ) +!!! endif call Cleanup() @@ -1145,6 +1230,38 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) call SetOutputsFromBEMT(p, m, y ) +!!! REAL(ReKi) :: Vind_FVW(3) +!!! +!!! WakeCalc = p%UseFVW ! WakeCalc is used to easily switch the Freewake on and off in this routine +!!! +!!! ! --- Copy of Rotor Mesh to FVW +!!! IF (WakeCalc) THEN +!!! ! Setting u%FVW +!!! call AD14_to_FVW_u(u,p,u%FVW,ErrStat,ErrMess) +!!! IF (ErrStat >= AbortErrLev) THEN +!!! CALL CleanUp() +!!! RETURN +!!! END IF +!!! ! -- Calc Output +!!! CALL FVW_CalcOutput( Time, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, ErrStat, ErrMess ) +!!! IF (ErrStat >= AbortErrLev) THEN +!!! CALL CleanUp() +!!! RETURN +!!! END IF +!!! endif +!!! +!!! ! --- FVW - Vortex code +!!! Vind_FVW = y%FVW%Vind(:, IElement, IBlade) +!!! VT_ind = DOT_PRODUCT( tang_Vector, Vind_FVW) +!!! VN_ind = DOT_PRODUCT( norm_Vector, Vind_FVW) +!!! ! Normal and tangential induction factors +!!! m%Element%A (IElement,IBLADE) = - VN_ind / VNWind +!!! m%Element%AP(IElement,IBLADE) = VT_ind / VTTotal +!!! ! Copy over any outputs (y%FVW%) or miscvars (m%FVW%) needed by AD14 and anything else here +!!! IF ( p%UseFVW ) THEN +!!! VelocityVec = VelocityVec+Vind_FVW ! TODO this might not be what's really intended for +!!! END IF + if ( p%TwrAero ) then call ADTwr_CalcOutput(p, u, m, y, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1202,6 +1319,28 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) end subroutine AD_CalcOutput +!!!!---------------------------------------------------------------------------------------------------------------------------------- +!!! +!!!!> Wrapper to set inputs needed by FVW from AeroDyn +!!!SUBROUTINE AD14_to_FVW_u(u,p,u_FVW,ErrStat,ErrMess) +!!! TYPE (AD14_InputType ),INTENT(IN ):: u ! Inputs at Time ! KS changed from IN to INOUT +!!! TYPE (AD14_ParameterType),INTENT(IN ):: p ! Parameters +!!! TYPE (FVW_InputType ),INTENT(INOUT):: u_FVW ! Inputs at Time ! KS changed from IN to INOUT +!!! INTEGER (IntKi ),INTENT(OUT ):: ErrStat ! Error status of the operation +!!! CHARACTER(* ),INTENT(OUT ):: ErrMess ! Error message if ErrStat / = ErrID_None +!!! INTEGER(IntKi) :: iB ! Index for blades +!!! +!!! !CALL AD14AeroConf_CopyInput( u%TurbineComponents, u_FVW%FVWTurbineComponents, MESH_NEWCOPY, ErrStat, ErrMess ) +!!! IF (ErrStat >= AbortErrLev) RETURN +!!! ! NOTE: this isn't really being used as a full mesh, so we only set a few things. +!!! ! also, if we do a direct copy, we end up with fatal errors at exit since the +!!! ! sibling/cousin status will be incorrect +!!! DO iB = 1,p%NumBl +!!! u_FVW%WingsMesh(iB)%Position = u%InputMarkers(iB)%Position +!!! u_FVW%WingsMesh(iB)%Orientation = u%InputMarkers(iB)%Orientation +!!! u_FVW%WingsMesh(iB)%TranslationVel = u%InputMarkers(iB)%TranslationVel +!!! ENDDO +!!!ENDSUBROUTINE AD14_to_FVW_u !---------------------------------------------------------------------------------------------------------------------------------- !> Tight coupling routine for solving for the residual of the constraint state equations subroutine AD_CalcConstrStateResidual( Time, u, p, x, xd, z, OtherState, m, z_residual, ErrStat, ErrMsg ) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 similarity index 100% rename from modules/aerodyn14/src/FVW.f90 rename to modules/aerodyn/src/FVW.f90 diff --git a/modules/aerodyn14/src/FVW_BiotSavart.f90 b/modules/aerodyn/src/FVW_BiotSavart.f90 similarity index 100% rename from modules/aerodyn14/src/FVW_BiotSavart.f90 rename to modules/aerodyn/src/FVW_BiotSavart.f90 diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 similarity index 100% rename from modules/aerodyn14/src/FVW_IO.f90 rename to modules/aerodyn/src/FVW_IO.f90 diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 similarity index 100% rename from modules/aerodyn14/src/FVW_Subs.f90 rename to modules/aerodyn/src/FVW_Subs.f90 diff --git a/modules/aerodyn14/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 similarity index 100% rename from modules/aerodyn14/src/FVW_Tests.f90 rename to modules/aerodyn/src/FVW_Tests.f90 diff --git a/modules/aerodyn14/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 similarity index 100% rename from modules/aerodyn14/src/FVW_Types.f90 rename to modules/aerodyn/src/FVW_Types.f90 diff --git a/modules/aerodyn14/src/VTK.f90 b/modules/aerodyn/src/FVW_VTK.f90 similarity index 100% rename from modules/aerodyn14/src/VTK.f90 rename to modules/aerodyn/src/FVW_VTK.f90 diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 similarity index 100% rename from modules/aerodyn14/src/FVW_VortexTools.f90 rename to modules/aerodyn/src/FVW_VortexTools.f90 diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 similarity index 100% rename from modules/aerodyn14/src/FVW_Wings.f90 rename to modules/aerodyn/src/FVW_Wings.f90 diff --git a/modules/aerodyn14/src/Registry-FVW.txt b/modules/aerodyn/src/Registry-FVW.txt similarity index 100% rename from modules/aerodyn14/src/Registry-FVW.txt rename to modules/aerodyn/src/Registry-FVW.txt diff --git a/modules/aerodyn14/CMakeLists.txt b/modules/aerodyn14/CMakeLists.txt index 1e87f16a9d..1a0e7634ea 100644 --- a/modules/aerodyn14/CMakeLists.txt +++ b/modules/aerodyn14/CMakeLists.txt @@ -18,7 +18,6 @@ if (GENERATE_TYPES) generate_f90_types(src/Registry-AD14.txt ${CMAKE_CURRENT_LIST_DIR}/src/AeroDyn14_Types.f90) generate_f90_types(src/Registry-AD14AeroConf.txt ${CMAKE_CURRENT_LIST_DIR}/src/AD14AeroConf_Types.f90) generate_f90_types(src/Registry-DWM.txt ${CMAKE_CURRENT_LIST_DIR}/src/DWM_Types.f90) - generate_f90_types(src/Registry-FVW.txt ${CMAKE_CURRENT_LIST_DIR}/src/FVW_Types.f90) endif() set(AD14_LIBS_SOURCES @@ -28,18 +27,8 @@ set(AD14_LIBS_SOURCES src/DWM_Wake_Sub_ver2.f90 src/GenSubs.f90 - src/FVW.f90 - src/FVW_IO.f90 - src/FVW_VortexTools.f90 - src/FVW_Wings.f90 - src/FVW_Subs.f90 - src/FVW_BiotSavart.f90 - src/FVW_Tests.f90 - src/VTK.f90 - src/AeroDyn14_Types.f90 src/DWM_Types.f90 - src/FVW_Types.f90 src/AD14AeroConf_Types.f90 ) diff --git a/modules/aerodyn14/src/AeroDyn14.f90 b/modules/aerodyn14/src/AeroDyn14.f90 index f30aa4f5bd..d8f2414249 100644 --- a/modules/aerodyn14/src/AeroDyn14.f90 +++ b/modules/aerodyn14/src/AeroDyn14.f90 @@ -23,7 +23,6 @@ MODULE AeroDyn14 USE AeroDyn14_Types USE AeroSubs - USE FVW USE NWTC_Library @@ -141,7 +140,6 @@ SUBROUTINE AD14_Init( InitInp, u, p, x, xd, z, O, y, m, Interval, InitOut, ErrSt CALL SetErrStat( ErrStatLcl,ErrMessLcl,ErrStat,ErrMess,RoutineName) IF (ErrStat >= AbortErrLev ) RETURN - p%UseFVW = InitInp%UseFVW ! allocate variables for aerodyn forces p%LinearizeFlag = .FALSE. @@ -591,75 +589,6 @@ SUBROUTINE AD14_Init( InitInp, u, p, x, xd, z, O, y, m, Interval, InitOut, ErrSt ENDDO - !------------------------------------------------------------------------------------------------- - ! Initialize FVW module if it is used - !------------------------------------------------------------------------------------------------- - if (p%UseFVW ) then - - ! Copy some things to the InitInp. When FVW is incorporated into a different module, there may - ! be some additional logic necessary to put it into the correct form for FVW to use (AD15 stores - ! things differently) - InitInp%FVW%FVWFileName = InitInp%FVWFileName - InitInp%FVW%NumBl = p%NumBl - - ! --- TODO TODO TODO ANDY - ! Change this so that it would match AD 15 mesh - ! NOTE: This mesh does not include the azimuthal differences between blades! - ! It's just the spanwise location. - ! Also, it is off compared to the initial position of the blade - ! Also, it's centered on the hub, but that's fine for now - IF (.NOT. ALLOCATED( InitInp%FVW%Chord)) ALLOCATE ( InitInp%FVW%Chord( p%Element%NElm )) - IF (.NOT. ALLOCATED( InitInp%FVW%RElm )) ALLOCATE ( InitInp%FVW%RElm( p%Element%NElm )) - InitInp%FVW%RElm = p%Element%RElm - InitInp%FVW%Chord = p%Blade%C - ALLOCATE( InitInp%FVW%WingsMesh(p%NumBl), STAT = ErrStatLcl ) - IF (ErrStatLcl /= 0) THEN - CALL SetErrStat ( ErrID_Fatal, 'Could not allocate InitInp%FVW%WingsMesh (meshes)', ErrStat,ErrMess,RoutineName ) - RETURN - END IF - DO IB = 1, p%NumBl - CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & - ,DestMesh = InitInp%FVW%WingsMesh(IB) & - ,CtrlCode = MESH_COUSIN & - ,Orientation = .TRUE. & - ,TranslationVel = .TRUE. & - ,RotationVel = .TRUE. & - ,ErrStat = ErrStatLcl & - ,ErrMess = ErrMessLcl ) - CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) - IF (ErrStat >= AbortErrLev) RETURN - ENDDO - ! ---- END TODO - - call FVW_Init( InitInp%FVW, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, Interval, InitOut%FVW, ErrStatLcl, ErrMessLcl ) - CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) - - ! If anything is passed back in InitOut%FVW, deal with it here... - - - - ! TODO ANDY - !FIXME This really probably should be done inside of FVW_Init instead of here. - ! Not entirely sure how to pass the u%InputMarkers in though. - ALLOCATE( u%FVW%WingsMesh(p%NumBl), STAT = ErrStatLcl ) - IF (ErrStatLcl /= 0) THEN - CALL SetErrStat ( ErrID_Fatal, 'Could not allocate u%FVW%InputMarkers (meshes)', ErrStat,ErrMess,RoutineName ) - RETURN - END IF - DO IB = 1, p%NumBl - CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & - ,DestMesh = u%FVW%WingsMesh(IB) & - ,CtrlCode = MESH_COUSIN & - ,Orientation = .TRUE. & - ,TranslationVel = .TRUE. & - ,RotationVel = .TRUE. & - ,ErrStat = ErrStatLcl & - ,ErrMess = ErrMessLcl ) - CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) - IF (ErrStat >= AbortErrLev) RETURN - ENDDO - endif - !.......... @@ -727,11 +656,6 @@ SUBROUTINE AD14_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMess ) CALL DWM_End( m%DWM_Inputs, p%DWM, x%DWM, xd%DWM, z%DWM, OtherState%DWM, m%DWM_Outputs, m%DWM, ErrStat, ErrMess ) END IF ! UseDWM - !-------------------------- - - - IF (p%UseFVW ) CALL FVW_End( u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, y%FVW, m%FVW, ErrStat, ErrMess ) - !-------------------------- @@ -795,8 +719,6 @@ SUBROUTINE AD14_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrSt TYPE(AD14_ContinuousStateType) :: dxdt ! Continuous state derivatives at Time TYPE(AD14_ConstraintStateType) :: z_Residual ! Residual of the constraint state equations (Z) - type(FVW_InputType) :: u_FVW(1) !< FVW inputs - REAL(DbKi) :: utimes_FVW(1) !< Times associated with u(:), in seconds ! INTEGER(IntKi) :: ErrStat2 ! Error status of the operation (occurs after initial error) ! CHARACTER(ErrMsgLen) :: ErrMess2 ! Error message if ErrStat2 /= ErrID_None @@ -810,41 +732,7 @@ SUBROUTINE AD14_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrSt ! AeroDyn v14 DOES actually have states, but they are updated in CalcOutput because no one ever took the time to ! identify which variables are states. - if (p%UseFVW) then - !if (abs(t-utimes(2))>1e-6 ) then - ! print*,'Problem in AD14 update state, need to adapt which u we provide to FVW' - ! STOP - !endif - ! Setting u(1)%FVW - call AD14_to_FVW_u(u(1),p,u(1)%FVW,ErrStat,ErrMess) - u_FVW(1) = u(1)%FVW - utimes_FVW(1) = utimes(1) - CALL FVW_UpdateStates( t, n, u_FVW, utimes_FVW, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat, ErrMess ) - endif - END SUBROUTINE AD14_UpdateStates -!---------------------------------------------------------------------------------------------------------------------------------- - -!> Wrapper to set inputs needed by FVW from AeroDyn -SUBROUTINE AD14_to_FVW_u(u,p,u_FVW,ErrStat,ErrMess) - TYPE (AD14_InputType ),INTENT(IN ):: u ! Inputs at Time ! KS changed from IN to INOUT - TYPE (AD14_ParameterType),INTENT(IN ):: p ! Parameters - TYPE (FVW_InputType ),INTENT(INOUT):: u_FVW ! Inputs at Time ! KS changed from IN to INOUT - INTEGER (IntKi ),INTENT(OUT ):: ErrStat ! Error status of the operation - CHARACTER(* ),INTENT(OUT ):: ErrMess ! Error message if ErrStat / = ErrID_None - INTEGER(IntKi) :: iB ! Index for blades - - !CALL AD14AeroConf_CopyInput( u%TurbineComponents, u_FVW%FVWTurbineComponents, MESH_NEWCOPY, ErrStat, ErrMess ) - IF (ErrStat >= AbortErrLev) RETURN - ! NOTE: this isn't really being used as a full mesh, so we only set a few things. - ! also, if we do a direct copy, we end up with fatal errors at exit since the - ! sibling/cousin status will be incorrect - DO iB = 1,p%NumBl - u_FVW%WingsMesh(iB)%Position = u%InputMarkers(iB)%Position - u_FVW%WingsMesh(iB)%Orientation = u%InputMarkers(iB)%Orientation - u_FVW%WingsMesh(iB)%TranslationVel = u%InputMarkers(iB)%TranslationVel - ENDDO -ENDSUBROUTINE AD14_to_FVW_u !---------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) @@ -913,11 +801,8 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) INTEGER :: I CHARACTER(ErrMsgLen) :: ErrMessLcl ! Error message returned by called routines. - LOGICAL :: WakeCalc ! Are we doing wake calculations this loop? CHARACTER(*), PARAMETER :: RoutineName = 'AD14_AeroSubs' !KS Not sure why I added this - REAL(ReKi) :: Vind_FVW(3) - ! Initialize ErrStat ErrStat = ErrID_None ErrMess = "" @@ -1059,27 +944,6 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) ENDDO - - - WakeCalc = p%UseFVW ! WakeCalc is used to easily switch the Freewake on and off in this routine - - ! --- Copy of Rotor Mesh to FVW - IF (WakeCalc) THEN - ! Setting u%FVW - call AD14_to_FVW_u(u,p,u%FVW,ErrStat,ErrMess) - IF (ErrStat >= AbortErrLev) THEN - CALL CleanUp() - RETURN - END IF - ! -- Calc Output - CALL FVW_CalcOutput( Time, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, ErrStat, ErrMess ) - IF (ErrStat >= AbortErrLev) THEN - CALL CleanUp() - RETURN - END IF - endif - - Node = 0 ! --- Loop on blades DO IBlade = 1,p%NumBl @@ -1210,24 +1074,14 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) ! --------------------------------------------------------------------------------} ! --- Setting Element% values: W2, Alpha, A, AP ! --------------------------------------------------------------------------------{ - IF ( .NOT. WakeCalc ) THEN - ! --- BEM - CALL ELEM_INDUCTIONS( p, m, ErrStatLcl, ErrMessLcl, & - AzimuthAngle, rLocal, IElement, IBlade, VelNormalToRotor2, VTTotal, VNWind, & - VNElement, m%NoLoadsCalculated) - ! Normal and tangential induced velocities - VN_ind = - VNWind * m%Element%A (IElement, IBLADE) - VT_ind = VTTotal * m%Element%AP(IElement, IBLADE) - else - ! --- FVW - Vortex code - Vind_FVW = y%FVW%Vind(:, IElement, IBlade) - VT_ind = DOT_PRODUCT( tang_Vector, Vind_FVW) - VN_ind = DOT_PRODUCT( norm_Vector, Vind_FVW) - ! Normal and tangential induction factors - m%Element%A (IElement,IBLADE) = - VN_ind / VNWind - m%Element%AP(IElement,IBLADE) = VT_ind / VTTotal - ! Copy over any outputs (y%FVW%) or miscvars (m%FVW%) needed by AD14 and anything else here - ENDIF + ! --- BEM + CALL ELEM_INDUCTIONS( p, m, ErrStatLcl, ErrMessLcl, & + AzimuthAngle, rLocal, IElement, IBlade, VelNormalToRotor2, VTTotal, VNWind, & + VNElement, m%NoLoadsCalculated) + ! Normal and tangential induced velocities + VN_ind = - VNWind * m%Element%A (IElement, IBLADE) + VT_ind = VTTotal * m%Element%AP(IElement, IBLADE) + ! Cumulative (integrated) induction over the blades m%InducedVel%SumInfl = m%InducedVel%SumInfl - VN_IND * RLOCAL * p%Blade%DR(IElement) @@ -1248,10 +1102,6 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) RETURN END IF - IF ( p%UseFVW ) THEN - VelocityVec = VelocityVec+Vind_FVW ! TODO this might not be what's really intended for - END IF - !------------------------------------------------------------------------------------------- ! Set up dynamic inflow parameters !------------------------------------------------------------------------------------------- diff --git a/modules/aerodyn14/src/AeroDyn14_Types.f90 b/modules/aerodyn14/src/AeroDyn14_Types.f90 index b097f4bbd1..9b1eaed11a 100644 --- a/modules/aerodyn14/src/AeroDyn14_Types.f90 +++ b/modules/aerodyn14/src/AeroDyn14_Types.f90 @@ -41,7 +41,6 @@ MODULE AeroDyn14_Types USE InflowWind_Types USE DWM_Types USE AD14AeroConf_Types -USE FVW_Types USE NWTC_Library IMPLICIT NONE ! ========= Beddoes ======= @@ -318,41 +317,33 @@ MODULE AeroDyn14_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: TwrNodeLocs !< Location of ElastoDyn tower nodes with respect to the inertial origin. [-] REAL(ReKi) :: HubHt !< hub height wrt inertial origin [m] TYPE(DWM_InitInputType) :: DWM - LOGICAL :: UseFVW !< flag to determine if FVW module should be used [-] - CHARACTER(1024) :: FVWFileName !< Main FVW input file name [-] - TYPE(FVW_InitInputType) :: FVW !< [-] END TYPE AD14_InitInputType ! ======================= ! ========= AD14_InitOutputType ======= TYPE, PUBLIC :: AD14_InitOutputType TYPE(ProgDesc) :: Ver !< version information [-] TYPE(DWM_InitOutputType) :: DWM - TYPE(FVW_InitOutputType) :: FVW REAL(ReKi) :: AirDens !< Air density [kg/m^3] END TYPE AD14_InitOutputType ! ======================= ! ========= AD14_ContinuousStateType ======= TYPE, PUBLIC :: AD14_ContinuousStateType TYPE(DWM_ContinuousStateType) :: DWM - TYPE(FVW_ContinuousStateType) :: FVW END TYPE AD14_ContinuousStateType ! ======================= ! ========= AD14_DiscreteStateType ======= TYPE, PUBLIC :: AD14_DiscreteStateType TYPE(DWM_DiscreteStateType) :: DWM - TYPE(FVW_DiscreteStateType) :: FVW END TYPE AD14_DiscreteStateType ! ======================= ! ========= AD14_ConstraintStateType ======= TYPE, PUBLIC :: AD14_ConstraintStateType TYPE(DWM_ConstraintStateType) :: DWM - TYPE(FVW_ConstraintStateType) :: FVW END TYPE AD14_ConstraintStateType ! ======================= ! ========= AD14_OtherStateType ======= TYPE, PUBLIC :: AD14_OtherStateType TYPE(DWM_OtherStateType) :: DWM !< variables for DWM module [-] - TYPE(FVW_OtherStateType) :: FVW !< variables for FVW module [-] END TYPE AD14_OtherStateType ! ======================= ! ========= AD14_MiscVarType ======= @@ -360,7 +351,6 @@ MODULE AeroDyn14_Types TYPE(DWM_MiscVarType) :: DWM !< variables for DWM module [-] TYPE(DWM_InputType) :: DWM_Inputs TYPE(DWM_OutputType) :: DWM_Outputs - TYPE(FVW_MiscVarType) :: FVW !< variables for FVW module [-] REAL(DbKi) :: DT !< actual Time step [seconds] INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: ElPrNum REAL(DbKi) :: OldTime @@ -399,7 +389,6 @@ MODULE AeroDyn14_Types LOGICAL :: LinearizeFlag LOGICAL :: OutputPlottingInfo = .FALSE. LOGICAL :: UseDWM = .FALSE. !< flag to determine if DWM module should be used [-] - LOGICAL :: UseFVW = .TRUE. !< flag to determine if FVW module should be used [-] REAL(ReKi) :: TwoPiNB !< 2*pi/num of blades [-] INTEGER(IntKi) :: NumBl !< Number of Blades [-] INTEGER(IntKi) :: NBlInpSt !< Number of Blade Input Stations [-] @@ -428,7 +417,6 @@ MODULE AeroDyn14_Types TYPE(WindParms) :: Wind TYPE(RotorParms) :: Rotor TYPE(DWM_ParameterType) :: DWM - TYPE(FVW_ParameterType) :: FVW REAL(DbKi) :: IfW_DT END TYPE AD14_ParameterType ! ======================= @@ -440,14 +428,12 @@ MODULE AeroDyn14_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: MulTabLoc REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: InflowVelocity !< U,V,W wind inflow speeds at all locations on the Inputmarker and Twr_InputMarker meshes [m/s] REAL(ReKi) , DIMENSION(1:3) :: AvgInfVel !< an average disk velocity (depends on wind type and should be removed) [m/s] - TYPE(FVW_InputType) :: FVW !< variables for FVW module [-] END TYPE AD14_InputType ! ======================= ! ========= AD14_OutputType ======= TYPE, PUBLIC :: AD14_OutputType TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: OutputLoads !< Output Loads (mesh) for each blade [-] TYPE(MeshType) :: Twr_OutputLoads !< Tower Output Loads (mesh) [-] - TYPE(FVW_OutputType) :: FVW !< variables for FVW module [-] END TYPE AD14_OutputType ! ======================= CONTAINS @@ -9245,7 +9231,7 @@ SUBROUTINE AD14_UnPackOrientationType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrS END SUBROUTINE AD14_UnPackOrientationType SUBROUTINE AD14_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrStat, ErrMsg ) - TYPE(AD14_InitInputType), INTENT(INOUT) :: SrcInitInputData + TYPE(AD14_InitInputType), INTENT(IN) :: SrcInitInputData TYPE(AD14_InitInputType), INTENT(INOUT) :: DstInitInputData INTEGER(IntKi), INTENT(IN ) :: CtrlCode INTEGER(IntKi), INTENT( OUT) :: ErrStat @@ -9290,11 +9276,6 @@ SUBROUTINE AD14_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, Err CALL DWM_CopyInitInput( SrcInitInputData%DWM, DstInitInputData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - DstInitInputData%UseFVW = SrcInitInputData%UseFVW - DstInitInputData%FVWFileName = SrcInitInputData%FVWFileName - CALL FVW_CopyInitInput( SrcInitInputData%FVW, DstInitInputData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyInitInput SUBROUTINE AD14_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -9311,7 +9292,6 @@ SUBROUTINE AD14_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) DEALLOCATE(InitInputData%TwrNodeLocs) ENDIF CALL DWM_DestroyInitInput( InitInputData%DWM, ErrStat, ErrMsg ) - CALL FVW_DestroyInitInput( InitInputData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyInitInput SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -9399,25 +9379,6 @@ SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 1 ! UseFVW - Int_BufSz = Int_BufSz + 1*LEN(InData%FVWFileName) ! FVWFileName - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackInitInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - 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 @@ -9543,40 +9504,6 @@ SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UseFVW , IntKiBuf(1), 1) - Int_Xferred = Int_Xferred + 1 - DO I = 1, LEN(InData%FVWFileName) - IntKiBuf(Int_Xferred) = ICHAR(InData%FVWFileName(I:I), IntKi) - Int_Xferred = Int_Xferred + 1 - END DO ! I - CALL FVW_PackInitInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 AD14_PackInitInput SUBROUTINE AD14_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -9745,52 +9672,6 @@ SUBROUTINE AD14_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) - OutData%UseFVW = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) - Int_Xferred = Int_Xferred + 1 - DO I = 1, LEN(OutData%FVWFileName) - OutData%FVWFileName(I:I) = CHAR(IntKiBuf(Int_Xferred)) - Int_Xferred = Int_Xferred + 1 - END DO ! I - 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 FVW_UnpackInitInput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW - 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 AD14_UnPackInitInput SUBROUTINE AD14_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -9813,9 +9694,6 @@ SUBROUTINE AD14_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, CALL DWM_CopyInitOutput( SrcInitOutputData%DWM, DstInitOutputData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL FVW_CopyInitOutput( SrcInitOutputData%FVW, DstInitOutputData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN DstInitOutputData%AirDens = SrcInitOutputData%AirDens END SUBROUTINE AD14_CopyInitOutput @@ -9830,7 +9708,6 @@ SUBROUTINE AD14_DestroyInitOutput( InitOutputData, ErrStat, ErrMsg ) ErrMsg = "" CALL NWTC_Library_Destroyprogdesc( InitOutputData%Ver, ErrStat, ErrMsg ) CALL DWM_DestroyInitOutput( InitOutputData%DWM, ErrStat, ErrMsg ) - CALL FVW_DestroyInitOutput( InitOutputData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyInitOutput SUBROUTINE AD14_PackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -9903,23 +9780,6 @@ SUBROUTINE AD14_PackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackInitOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF Re_BufSz = Re_BufSz + 1 ! AirDens IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) @@ -9980,34 +9840,6 @@ SUBROUTINE AD14_PackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err 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 - CALL FVW_PackInitOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 @@ -10145,46 +9977,6 @@ SUBROUTINE AD14_UnPackInitOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, 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) - 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 FVW_UnpackInitOutput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW - 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) @@ -10209,9 +10001,6 @@ SUBROUTINE AD14_CopyContState( SrcContStateData, DstContStateData, CtrlCode, Err CALL DWM_CopyContState( SrcContStateData%DWM, DstContStateData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL FVW_CopyContState( SrcContStateData%FVW, DstContStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyContState SUBROUTINE AD14_DestroyContState( ContStateData, ErrStat, ErrMsg ) @@ -10224,7 +10013,6 @@ SUBROUTINE AD14_DestroyContState( ContStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL DWM_DestroyContState( ContStateData%DWM, ErrStat, ErrMsg ) - CALL FVW_DestroyContState( ContStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyContState SUBROUTINE AD14_PackContState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -10280,23 +10068,6 @@ SUBROUTINE AD14_PackContState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackContState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - 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 @@ -10352,34 +10123,6 @@ SUBROUTINE AD14_PackContState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - CALL FVW_PackContState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 AD14_PackContState SUBROUTINE AD14_UnPackContState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -10454,46 +10197,6 @@ SUBROUTINE AD14_UnPackContState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) - 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 FVW_UnpackContState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW - 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 AD14_UnPackContState SUBROUTINE AD14_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrStat, ErrMsg ) @@ -10513,9 +10216,6 @@ SUBROUTINE AD14_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, Err CALL DWM_CopyDiscState( SrcDiscStateData%DWM, DstDiscStateData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL FVW_CopyDiscState( SrcDiscStateData%FVW, DstDiscStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyDiscState SUBROUTINE AD14_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) @@ -10528,7 +10228,6 @@ SUBROUTINE AD14_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL DWM_DestroyDiscState( DiscStateData%DWM, ErrStat, ErrMsg ) - CALL FVW_DestroyDiscState( DiscStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyDiscState SUBROUTINE AD14_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -10584,23 +10283,6 @@ SUBROUTINE AD14_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackDiscState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - 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 @@ -10656,34 +10338,6 @@ SUBROUTINE AD14_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - CALL FVW_PackDiscState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 AD14_PackDiscState SUBROUTINE AD14_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -10709,55 +10363,15 @@ SUBROUTINE AD14_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'AD14_UnPackDiscState' ! 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 - 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 DWM_UnpackDiscState( Re_Buf, Db_Buf, Int_Buf, OutData%DWM, ErrStat2, ErrMsg2 ) ! DWM - 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) + 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 Buf_size=IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 IF(Buf_size > 0) THEN @@ -10791,7 +10405,7 @@ SUBROUTINE AD14_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL FVW_UnpackDiscState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + CALL DWM_UnpackDiscState( Re_Buf, Db_Buf, Int_Buf, OutData%DWM, ErrStat2, ErrMsg2 ) ! DWM CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -10817,9 +10431,6 @@ SUBROUTINE AD14_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCod CALL DWM_CopyConstrState( SrcConstrStateData%DWM, DstConstrStateData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL FVW_CopyConstrState( SrcConstrStateData%FVW, DstConstrStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyConstrState SUBROUTINE AD14_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) @@ -10832,7 +10443,6 @@ SUBROUTINE AD14_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL DWM_DestroyConstrState( ConstrStateData%DWM, ErrStat, ErrMsg ) - CALL FVW_DestroyConstrState( ConstrStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyConstrState SUBROUTINE AD14_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -10888,23 +10498,6 @@ SUBROUTINE AD14_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - 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 @@ -10960,34 +10553,6 @@ SUBROUTINE AD14_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Er ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 AD14_PackConstrState SUBROUTINE AD14_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -11062,46 +10627,6 @@ SUBROUTINE AD14_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) - 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 FVW_UnpackConstrState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW - 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 AD14_UnPackConstrState SUBROUTINE AD14_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -11121,9 +10646,6 @@ SUBROUTINE AD14_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, CALL DWM_CopyOtherState( SrcOtherStateData%DWM, DstOtherStateData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL FVW_CopyOtherState( SrcOtherStateData%FVW, DstOtherStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyOtherState SUBROUTINE AD14_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) @@ -11136,7 +10658,6 @@ SUBROUTINE AD14_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL DWM_DestroyOtherState( OtherStateData%DWM, ErrStat, ErrMsg ) - CALL FVW_DestroyOtherState( OtherStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyOtherState SUBROUTINE AD14_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -11192,23 +10713,6 @@ SUBROUTINE AD14_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - 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 @@ -11264,34 +10768,6 @@ SUBROUTINE AD14_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Err ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - CALL FVW_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 AD14_PackOtherState SUBROUTINE AD14_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -11366,46 +10842,6 @@ SUBROUTINE AD14_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) - 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 FVW_UnpackOtherState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW - 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 AD14_UnPackOtherState SUBROUTINE AD14_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) @@ -11434,9 +10870,6 @@ SUBROUTINE AD14_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) CALL DWM_CopyOutput( SrcMiscData%DWM_Outputs, DstMiscData%DWM_Outputs, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL FVW_CopyMisc( SrcMiscData%FVW, DstMiscData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN DstMiscData%DT = SrcMiscData%DT IF (ALLOCATED(SrcMiscData%ElPrNum)) THEN i1_l = LBOUND(SrcMiscData%ElPrNum,1) @@ -11535,7 +10968,6 @@ SUBROUTINE AD14_DestroyMisc( MiscData, ErrStat, ErrMsg ) CALL DWM_DestroyMisc( MiscData%DWM, ErrStat, ErrMsg ) CALL DWM_DestroyInput( MiscData%DWM_Inputs, ErrStat, ErrMsg ) CALL DWM_DestroyOutput( MiscData%DWM_Outputs, ErrStat, ErrMsg ) - CALL FVW_DestroyMisc( MiscData%FVW, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%ElPrNum)) THEN DEALLOCATE(MiscData%ElPrNum) ENDIF @@ -11642,23 +11074,6 @@ SUBROUTINE AD14_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF Db_BufSz = Db_BufSz + 1 ! DT Int_BufSz = Int_BufSz + 1 ! ElPrNum allocated yes/no IF ( ALLOCATED(InData%ElPrNum) ) THEN @@ -11913,34 +11328,6 @@ SUBROUTINE AD14_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S 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 - CALL FVW_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 @@ -12304,51 +11691,11 @@ SUBROUTINE AD14_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg REAL(DbKi), ALLOCATABLE :: Db_Buf(:) INTEGER(IntKi), ALLOCATABLE :: Int_Buf(:) ! - ErrStat = ErrID_None - ErrMsg = "" - Re_Xferred = 1 - Db_Xferred = 1 - 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 DWM_UnpackMisc( Re_Buf, Db_Buf, Int_Buf, OutData%DWM, ErrStat2, ErrMsg2 ) ! DWM - 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) + ErrStat = ErrID_None + ErrMsg = "" + Re_Xferred = 1 + Db_Xferred = 1 + Int_Xferred = 1 Buf_size=IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 IF(Buf_size > 0) THEN @@ -12382,7 +11729,7 @@ SUBROUTINE AD14_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL DWM_UnpackInput( Re_Buf, Db_Buf, Int_Buf, OutData%DWM_Inputs, ErrStat2, ErrMsg2 ) ! DWM_Inputs + CALL DWM_UnpackMisc( Re_Buf, Db_Buf, Int_Buf, OutData%DWM, ErrStat2, ErrMsg2 ) ! DWM CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -12422,7 +11769,7 @@ SUBROUTINE AD14_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL DWM_UnpackOutput( Re_Buf, Db_Buf, Int_Buf, OutData%DWM_Outputs, ErrStat2, ErrMsg2 ) ! DWM_Outputs + CALL DWM_UnpackInput( Re_Buf, Db_Buf, Int_Buf, OutData%DWM_Inputs, ErrStat2, ErrMsg2 ) ! DWM_Inputs CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -12462,7 +11809,7 @@ SUBROUTINE AD14_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL FVW_UnpackMisc( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + CALL DWM_UnpackOutput( Re_Buf, Db_Buf, Int_Buf, OutData%DWM_Outputs, ErrStat2, ErrMsg2 ) ! DWM_Outputs CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -12925,7 +12272,6 @@ SUBROUTINE AD14_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%LinearizeFlag = SrcParamData%LinearizeFlag DstParamData%OutputPlottingInfo = SrcParamData%OutputPlottingInfo DstParamData%UseDWM = SrcParamData%UseDWM - DstParamData%UseFVW = SrcParamData%UseFVW DstParamData%TwoPiNB = SrcParamData%TwoPiNB DstParamData%NumBl = SrcParamData%NumBl DstParamData%NBlInpSt = SrcParamData%NBlInpSt @@ -12974,9 +12320,6 @@ SUBROUTINE AD14_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg CALL DWM_CopyParam( SrcParamData%DWM, DstParamData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL FVW_CopyParam( SrcParamData%FVW, DstParamData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN DstParamData%IfW_DT = SrcParamData%IfW_DT END SUBROUTINE AD14_CopyParam @@ -12999,7 +12342,6 @@ SUBROUTINE AD14_DestroyParam( ParamData, ErrStat, ErrMsg ) CALL AD14_Destroywindparms( ParamData%Wind, ErrStat, ErrMsg ) CALL AD14_Destroyrotorparms( ParamData%Rotor, ErrStat, ErrMsg ) CALL DWM_DestroyParam( ParamData%DWM, ErrStat, ErrMsg ) - CALL FVW_DestroyParam( ParamData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyParam SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -13044,7 +12386,6 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + 1 ! LinearizeFlag Int_BufSz = Int_BufSz + 1 ! OutputPlottingInfo Int_BufSz = Int_BufSz + 1 ! UseDWM - Int_BufSz = Int_BufSz + 1 ! UseFVW Re_BufSz = Re_BufSz + 1 ! TwoPiNB Int_BufSz = Int_BufSz + 1 ! NumBl Int_BufSz = Int_BufSz + 1 ! NBlInpSt @@ -13234,23 +12575,6 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF Db_BufSz = Db_BufSz + 1 ! IfW_DT IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) @@ -13295,8 +12619,6 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UseDWM , IntKiBuf(1), 1) Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UseFVW , IntKiBuf(1), 1) - Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TwoPiNB Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumBl @@ -13589,34 +12911,6 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, 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 - CALL FVW_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 @@ -13693,8 +12987,6 @@ SUBROUTINE AD14_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 OutData%UseDWM = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) Int_Xferred = Int_Xferred + 1 - OutData%UseFVW = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) - Int_Xferred = Int_Xferred + 1 OutData%TwoPiNB = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 OutData%NumBl = IntKiBuf( Int_Xferred ) @@ -14128,46 +13420,6 @@ SUBROUTINE AD14_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs 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) - 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 FVW_UnpackParam( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW - 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) @@ -14242,9 +13494,6 @@ SUBROUTINE AD14_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg DstInputData%InflowVelocity = SrcInputData%InflowVelocity ENDIF DstInputData%AvgInfVel = SrcInputData%AvgInfVel - CALL FVW_CopyInput( SrcInputData%FVW, DstInputData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyInput SUBROUTINE AD14_DestroyInput( InputData, ErrStat, ErrMsg ) @@ -14270,7 +13519,6 @@ SUBROUTINE AD14_DestroyInput( InputData, ErrStat, ErrMsg ) IF (ALLOCATED(InputData%InflowVelocity)) THEN DEALLOCATE(InputData%InflowVelocity) ENDIF - CALL FVW_DestroyInput( InputData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyInput SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -14377,23 +13625,6 @@ SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Re_BufSz = Re_BufSz + SIZE(InData%InflowVelocity) ! InflowVelocity END IF Re_BufSz = Re_BufSz + SIZE(InData%AvgInfVel) ! AvgInfVel - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - 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 @@ -14552,34 +13783,6 @@ SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, END IF ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%AvgInfVel))-1 ) = PACK(InData%AvgInfVel,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%AvgInfVel) - CALL FVW_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 AD14_PackInput SUBROUTINE AD14_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -14815,46 +14018,6 @@ SUBROUTINE AD14_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs OutData%AvgInfVel = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%AvgInfVel))-1 ), mask1, 0.0_ReKi ) Re_Xferred = Re_Xferred + SIZE(OutData%AvgInfVel) DEALLOCATE(mask1) - 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 FVW_UnpackInput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW - 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 AD14_UnPackInput SUBROUTINE AD14_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -14891,9 +14054,6 @@ SUBROUTINE AD14_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, Err CALL MeshCopy( SrcOutputData%Twr_OutputLoads, DstOutputData%Twr_OutputLoads, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL FVW_CopyOutput( SrcOutputData%FVW, DstOutputData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD14_CopyOutput SUBROUTINE AD14_DestroyOutput( OutputData, ErrStat, ErrMsg ) @@ -14912,7 +14072,6 @@ SUBROUTINE AD14_DestroyOutput( OutputData, ErrStat, ErrMsg ) DEALLOCATE(OutputData%OutputLoads) ENDIF CALL MeshDestroy( OutputData%Twr_OutputLoads, ErrStat, ErrMsg ) - CALL FVW_DestroyOutput( OutputData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD14_DestroyOutput SUBROUTINE AD14_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -14991,23 +14150,6 @@ SUBROUTINE AD14_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - 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 @@ -15104,34 +14246,6 @@ SUBROUTINE AD14_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - CALL FVW_PackOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 AD14_PackOutput SUBROUTINE AD14_UnPackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -15263,46 +14377,6 @@ SUBROUTINE AD14_UnPackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrM IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) - 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 FVW_UnpackOutput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW - 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 AD14_UnPackOutput @@ -15433,8 +14507,6 @@ SUBROUTINE AD14_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg u_out%AvgInfVel = u1%AvgInfVel + b1 * t_out DEALLOCATE(b1) DEALLOCATE(c1) - CALL FVW_Input_ExtrapInterp1( u1%FVW, u2%FVW, tin, u_out%FVW, tin_out, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) END SUBROUTINE AD14_Input_ExtrapInterp1 @@ -15527,8 +14599,6 @@ SUBROUTINE AD14_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, Er u_out%AvgInfVel = u1%AvgInfVel + b1 * t_out + c1 * t_out**2 DEALLOCATE(b1) DEALLOCATE(c1) - CALL FVW_Input_ExtrapInterp2( u1%FVW, u2%FVW, u3%FVW, tin, u_out%FVW, tin_out, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) END SUBROUTINE AD14_Input_ExtrapInterp2 @@ -15631,8 +14701,6 @@ SUBROUTINE AD14_Output_ExtrapInterp1(y1, y2, tin, y_out, tin_out, ErrStat, ErrMs END IF ! check if allocated CALL MeshExtrapInterp1(y1%Twr_OutputLoads, y2%Twr_OutputLoads, tin, y_out%Twr_OutputLoads, tin_out, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - CALL FVW_Output_ExtrapInterp1( y1%FVW, y2%FVW, tin, y_out%FVW, tin_out, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) END SUBROUTINE AD14_Output_ExtrapInterp1 @@ -15694,8 +14762,6 @@ SUBROUTINE AD14_Output_ExtrapInterp2(y1, y2, y3, tin, y_out, tin_out, ErrStat, E END IF ! check if allocated CALL MeshExtrapInterp2(y1%Twr_OutputLoads, y2%Twr_OutputLoads, y3%Twr_OutputLoads, tin, y_out%Twr_OutputLoads, tin_out, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - CALL FVW_Output_ExtrapInterp2( y1%FVW, y2%FVW, y3%FVW, tin, y_out%FVW, tin_out, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) END SUBROUTINE AD14_Output_ExtrapInterp2 END MODULE AeroDyn14_Types diff --git a/modules/aerodyn14/src/AeroSubs.f90 b/modules/aerodyn14/src/AeroSubs.f90 index dd85d87e9f..3ca457193a 100644 --- a/modules/aerodyn14/src/AeroSubs.f90 +++ b/modules/aerodyn14/src/AeroSubs.f90 @@ -385,23 +385,6 @@ SUBROUTINE AD14_GetInput(InitInp, P, x, xd, z, m, y, ErrStat, ErrMess ) END IF - ! --- Free vortex wake inputs - CALL ReadVar( UnIn, InitInp%ADFileName, InitInp%UseFVW, 'UseFVW', 'Use free vortex wake', ErrStat,ErrMess) - IF (ErrStat >= AbortErrLev) THEN - CLOSE(UnIn) - RETURN - END IF - CALL ReadVar( UnIn, InitInp%ADFileName, InitInp%FVWFileName, 'FVWFileName', 'Input file name for free vortex wake', ErrStat,ErrMess) - IF (ErrStat >= AbortErrLev) THEN - CLOSE(UnIn) - RETURN - END IF - if (InitInp%UseFVW) then - print*,'>>> Using FVW',trim(InitInp%FVWFileName) - else - print*,'>>> Using BEM' - endif - ! Read in the air density CALL ReadVar( UnIn, InitInp%ADFileName, P%Wind%Rho, VarName='Rho', VarDescr='Air density', ErrStat=ErrStat, ErrMsg=ErrMess) IF (ErrStat >= AbortErrLev) THEN diff --git a/modules/aerodyn14/src/Registry-AD14.txt b/modules/aerodyn14/src/Registry-AD14.txt index c979e19956..8945c9bd26 100644 --- a/modules/aerodyn14/src/Registry-AD14.txt +++ b/modules/aerodyn14/src/Registry-AD14.txt @@ -11,7 +11,6 @@ ################################################################################################################################### include Registry_NWTC_Library.txt usefrom Registry-DWM.txt -usefrom Registry-FVW.txt usefrom Registry-AD14AeroConf.txt @@ -255,33 +254,25 @@ typedef ^ InitInputType INTEGER NumTwrNodes - - - "Number of ElastoDyn tower nod typedef ^ InitInputType ReKi TwrNodeLocs {:}{:} - - "Location of ElastoDyn tower nodes with respect to the inertial origin." - typedef ^ InitInputType ReKi HubHt - - - "hub height wrt inertial origin" m typedef ^ InitInputType DWM_InitInputType DWM - - - - -typedef ^ InitInputType LOGICAL UseFVW - - - "flag to determine if FVW module should be used" - -typedef ^ InitInputType CHARACTER(1024) FVWFileName - - - "Main FVW input file name" - -typedef ^ InitInputType FVW_InitInputType FVW - - - "" - # Define outputs from the initialization routine here: typedef AeroDyn14/AD14 InitOutputType ProgDesc Ver - - - "version information" - typedef ^ InitOutputType DWM_InitOutputType DWM - - - - - -typedef ^ InitOutputType FVW_InitOutputType FVW - - - - - typedef ^ InitOutputType ReKi AirDens - - - "Air density" kg/m^3 # ..... States .................................................................................................................... # Define continuous (differentiable) states here: typedef ^ ContinuousStateType DWM_ContinuousStateType DWM - - - - -typedef ^ ContinuousStateType FVW_ContinuousStateType FVW - - - - # Define discrete (nondifferentiable) states here: typedef ^ DiscreteStateType DWM_DiscreteStateType DWM - - - - -typedef ^ DiscreteStateType FVW_DiscreteStateType FVW - - - - # Define constraint states here: typedef ^ ConstraintStateType DWM_ConstraintStateType DWM - - - - -typedef ^ ConstraintStateType FVW_ConstraintStateType FVW - - - - # Define any other states, including integer or logical states here: typedef ^ OtherStateType DWM_OtherStateType DWM - - - "variables for DWM module" -typedef ^ OtherStateType FVW_OtherStateType FVW - - - "variables for FVW module" # ..... Misc/Optimization variables................................................................................................. @@ -290,7 +281,6 @@ typedef ^ OtherStateType FVW_OtherStateType FVW - - - "variables for FVW module typedef ^ MiscVarType DWM_MiscVarType DWM - - - "variables for DWM module" typedef ^ MiscVarType DWM_InputType DWM_Inputs - - - - typedef ^ MiscVarType DWM_OutputType DWM_Outputs - - - - -typedef ^ MiscVarType FVW_MiscVarType FVW - - - "variables for FVW module" # # JM: At this point, I don't know which of these are actually discrete, continuous, or constraint, so they're all misc variables for now @@ -335,7 +325,6 @@ typedef ^ ParameterType LOGICAL MultiTab - typedef ^ ParameterType LOGICAL LinearizeFlag typedef ^ ParameterType LOGICAL OutputPlottingInfo - .FALSE. typedef ^ ParameterType LOGICAL UseDWM - .FALSE. - "flag to determine if DWM module should be used" - -typedef ^ ParameterType LOGICAL UseFVW - .TRUE. - "flag to determine if FVW module should be used" - typedef ^ ParameterType ReKi TwoPiNB - - - "2*pi/num of blades" - typedef ^ ParameterType INTEGER NumBl - - - "Number of Blades" typedef ^ ParameterType INTEGER NBlInpSt - - - "Number of Blade Input Stations" @@ -365,7 +354,6 @@ typedef ^ ParameterType InducedVelParms InducedVel typedef ^ ParameterType WindParms Wind typedef ^ ParameterType RotorParms Rotor typedef ^ ParameterType DWM_ParameterType DWM - - - - -typedef ^ ParameterType FVW_ParameterType FVW - - - - ##FIXME: Remove these!!!! typedef ^ ParameterType DbKi IfW_DT - - - - @@ -380,7 +368,6 @@ typedef ^ InputType AD14AeroConf_InputType TurbineComponents - - - "Current loca typedef ^ InputType ReKi MulTabLoc {:}{:} typedef ^ InputType ReKi InflowVelocity {:}{:} - - "U,V,W wind inflow speeds at all locations on the Inputmarker and Twr_InputMarker meshes" "m/s" typedef ^ InputType ReKi AvgInfVel {3} - - "an average disk velocity (depends on wind type and should be removed)" "m/s" -typedef ^ InputType FVW_InputType FVW - - - "variables for FVW module" # ..... Outputs ................................................................................................................... # Define outputs that are contained on the mesh here: @@ -388,4 +375,3 @@ typedef ^ InputType FVW_InputType FVW - - - "variables for FVW module" # Define outputs that are not on this mesh here: typedef ^ OutputType MeshType OutputLoads {:} - - "Output Loads (mesh) for each blade" - typedef ^ OutputType MeshType Twr_OutputLoads - - - "Tower Output Loads (mesh)" - -typedef ^ OutputType FVW_OutputType FVW - - - "variables for FVW module" diff --git a/modules/elastodyn/src/ElastoDyn_IO.f90 b/modules/elastodyn/src/ElastoDyn_IO.f90 index 33187b021a..8222a52495 100644 --- a/modules/elastodyn/src/ElastoDyn_IO.f90 +++ b/modules/elastodyn/src/ElastoDyn_IO.f90 @@ -1971,9 +1971,9 @@ SUBROUTINE ReadBladeMeshFileAD( BladeKInputFileMesh, MeshFile, UnEc, ErrStat, Er CALL Conv2UC( Line ) IF ( INDEX(Line, "NEWTOWER" ) > 0 ) THEN - NumLin2Skp = 7+2 ! +2 for FVW + NumLin2Skp = 7 ELSE - NumLin2Skp = 5+2+2 ! +2 for FVW + NumLin2Skp = 5 END IF DO I = 1,NumLin2Skp diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index d95ee858e5..e61706d143 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -586,8 +586,8 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, IfW%OtherSt(STATE_CURR), IfW%y, IfW%m, p_FAST%dt_module( MODULE_IfW ), InitOutData_IfW, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -!FIXME: how do I remove this? There is some confusing stuff going on... - IF ( p_FAST%CompAero == Module_AD14 ) THEN !!KS -- added this section +!FIXME:FVW how do I remove this? There is some confusing stuff going on... +! IF ( p_FAST%CompAero == Module_AD14 ) THEN !!KS -- added this section !TODO TODO ANDY !AD14%m%FVW%FVW_Wind%InitInputData = InitInData_IfW !tempo!rary hard coded size while we figure out how many points FVW actually needs: this will be requested by AD14 in addition to the nodes it normally needs @@ -600,7 +600,7 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, ! & AD14%m%FVW%FVW_Wind%InitOutputData, ErrStat2, ErrMsg2 ) !AD14%p%IfW_DT = p_FAST%dt_module( MODULE_IfW ) - END IF +! END IF p_FAST%ModuleInitialized(Module_IfW) = .TRUE. CALL SetModuleSubstepTime(Module_IfW, p_FAST, y_FAST, ErrStat2, ErrMsg2) @@ -5208,7 +5208,7 @@ END SUBROUTINE WrVTK_AllMeshes !> This routine writes a minimal subset of meshes (enough to visualize the turbine) to VTK-formatted files. It doesn't bother with !! returning an error code. SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) - use FVW_IO, only: WrVTK_FVW +! use FVW_IO, only: WrVTK_FVW TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(IN ) :: y_FAST !< Output variables for the glue code @@ -5280,9 +5280,10 @@ SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, END IF ! Free wake - IF ( p_FAST%CompAero == Module_AD14 ) THEN ! These meshes may have airfoil data associated with nodes... - call WrVTK_FVW(AD14%p%FVW, AD14%x(1)%FVW, AD14%z(1)%FVW, AD14%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) - END IF +!FIXME:FVW +! IF ( p_FAST%CompAero == Module_AD14 ) THEN ! These meshes may have airfoil data associated with nodes... +! call WrVTK_FVW(AD14%p%FVW, AD14%x(1)%FVW, AD14%z(1)%FVW, AD14%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) +! END IF ! Tower motions call MeshWrVTK(p_FAST%TurbinePos, ED%Output(1)%TowerLn2Mesh, trim(VTK_path)//'.ED_TowerLn2Mesh_motion', & @@ -5325,7 +5326,7 @@ END SUBROUTINE WrVTK_BasicMeshes !> This routine writes a minimal subset of meshes with surfaces to VTK-formatted files. It doesn't bother with !! returning an error code. SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) - use FVW_IO, only: WrVTK_FVW +! use FVW_IO, only: WrVTK_FVW REAL(DbKi), INTENT(IN ) :: t_global !< Current global time TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code @@ -5408,9 +5409,10 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, A END IF ! Free wake - IF ( p_FAST%CompAero == Module_AD14 ) THEN ! These meshes may have airfoil data associated with nodes... - call WrVTK_FVW(AD14%p%FVW, AD14%x(1)%FVW, AD14%z(1)%FVW, AD14%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) - END IF +!FIXME:FVW +! IF ( p_FAST%CompAero == Module_AD14 ) THEN ! These meshes may have airfoil data associated with nodes... +! call WrVTK_FVW(AD14%p%FVW, AD14%x(1)%FVW, AD14%z(1)%FVW, AD14%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) +! END IF ! Tower motions call MeshWrVTK_Ln2Surface (p_FAST%TurbinePos, ED%Output(1)%TowerLn2Mesh, trim(VTK_path)//'.TowerSurface', & From 1e2a89027dea942c99950b5a0b88c2028a8ec9b3 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 19 Nov 2019 12:22:47 -0700 Subject: [PATCH 008/190] [BugFix] vtk output with AD15 WakeMod==0 segfaults --- modules/openfast-library/src/FAST_Subs.f90 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index e61706d143..e22b70362f 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -5113,7 +5113,10 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O END DO call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%HubMotion, trim(VTK_path)//'.AD_HubMotion', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth ) !call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%TowerMotion, trim(p_FAST%OutFileRoot)//'.AD_TowerMotion', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2 ) - + end if + + if (allocated(AD%y%BladeLoad)) then + DO K=1,NumBl call MeshWrVTK(p_FAST%TurbinePos, AD%y%BladeLoad(K), trim(VTK_path)//'.AD_Blade'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth, AD%Input(1)%BladeMotion(k) ) END DO From 69590e34895773eddbd4f6e2d3903e223d6b5e89 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 19 Nov 2019 12:40:04 -0700 Subject: [PATCH 009/190] FVW AD15: rename FVW registry --- modules/aerodyn/CMakeLists.txt | 2 +- modules/aerodyn/src/{Registry-FVW.txt => FVW_Registry.txt} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename modules/aerodyn/src/{Registry-FVW.txt => FVW_Registry.txt} (100%) diff --git a/modules/aerodyn/CMakeLists.txt b/modules/aerodyn/CMakeLists.txt index 12e46eadf3..bb782f30da 100644 --- a/modules/aerodyn/CMakeLists.txt +++ b/modules/aerodyn/CMakeLists.txt @@ -21,7 +21,7 @@ if (GENERATE_TYPES) generate_f90_types(src/DBEMT_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/DBEMT_Types.f90) generate_f90_types(src/UnsteadyAero_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/UnsteadyAero_Types.f90) generate_f90_types(src/AeroDyn_Driver_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/AeroDyn_Driver_Types.f90 -noextrap) - generate_f90_types(src/Registry-FVW.txt ${CMAKE_CURRENT_LIST_DIR}/src/FVW_Types.f90) + generate_f90_types(src/FVW_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/FVW_Types.f90) endif() # AeroDyn lib diff --git a/modules/aerodyn/src/Registry-FVW.txt b/modules/aerodyn/src/FVW_Registry.txt similarity index 100% rename from modules/aerodyn/src/Registry-FVW.txt rename to modules/aerodyn/src/FVW_Registry.txt From a64486e8e949fb5c6ed96cc4a0830c078c3e2d0b Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 19 Nov 2019 14:09:27 -0700 Subject: [PATCH 010/190] FVW ad15: registry items in AD15. Start init mods --- modules/aerodyn/src/AeroDyn.f90 | 217 ++++--- modules/aerodyn/src/AeroDyn_IO.f90 | 9 + modules/aerodyn/src/AeroDyn_Registry.txt | 12 +- modules/aerodyn/src/AeroDyn_Types.f90 | 773 ++++++++++++++++++++++- modules/aerodyn/src/FVW.f90 | 2 +- modules/aerodyn/src/FVW_IO.f90 | 2 +- modules/aerodyn/src/FVW_Registry.txt | 5 +- modules/aerodyn/src/FVW_Types.f90 | 45 +- modules/aerodyn/src/FVW_Wings.f90 | 4 +- 9 files changed, 967 insertions(+), 102 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 31d5de7423..63827ba051 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -377,76 +377,25 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut call BEMT_CopyInput( m%BEMT_u(1), m%BEMT_u(2), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - -!!! !------------------------------------------------------------------------------------------------- -!!! ! Initialize FVW module if it is used -!!! !------------------------------------------------------------------------------------------------- -!!! if (p%UseFVW ) then -!!! -!!! ! Copy some things to the InitInp. When FVW is incorporated into a different module, there may -!!! ! be some additional logic necessary to put it into the correct form for FVW to use (AD15 stores -!!! ! things differently) -!!! InitInp%FVW%FVWFileName = InitInp%FVWFileName -!!! InitInp%FVW%NumBl = p%NumBl -!!! -!!! ! --- TODO TODO TODO ANDY -!!! ! Change this so that it would match AD 15 mesh -!!! ! NOTE: This mesh does not include the azimuthal differences between blades! -!!! ! It's just the spanwise location. -!!! ! Also, it is off compared to the initial position of the blade -!!! ! Also, it's centered on the hub, but that's fine for now -!!! IF (.NOT. ALLOCATED( InitInp%FVW%Chord)) ALLOCATE ( InitInp%FVW%Chord( p%Element%NElm )) -!!! IF (.NOT. ALLOCATED( InitInp%FVW%RElm )) ALLOCATE ( InitInp%FVW%RElm( p%Element%NElm )) -!!! InitInp%FVW%RElm = p%Element%RElm -!!! InitInp%FVW%Chord = p%Blade%C -!!! ALLOCATE( InitInp%FVW%WingsMesh(p%NumBl), STAT = ErrStatLcl ) -!!! IF (ErrStatLcl /= 0) THEN -!!! CALL SetErrStat ( ErrID_Fatal, 'Could not allocate InitInp%FVW%WingsMesh (meshes)', ErrStat,ErrMess,RoutineName ) -!!! RETURN -!!! END IF -!!! DO IB = 1, p%NumBl -!!! CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & -!!! ,DestMesh = InitInp%FVW%WingsMesh(IB) & -!!! ,CtrlCode = MESH_COUSIN & -!!! ,Orientation = .TRUE. & -!!! ,TranslationVel = .TRUE. & -!!! ,RotationVel = .TRUE. & -!!! ,ErrStat = ErrStatLcl & -!!! ,ErrMess = ErrMessLcl ) -!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) -!!! IF (ErrStat >= AbortErrLev) RETURN -!!! ENDDO -!!! ! ---- END TODO -!!! -!!! call FVW_Init( InitInp%FVW, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, Interval, InitOut%FVW, ErrStatLcl, ErrMessLcl ) -!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) -!!! -!!! ! If anything is passed back in InitOut%FVW, deal with it here... -!!! -!!! -!!! -!!! ! TODO ANDY -!!! !FIXME This really probably should be done inside of FVW_Init instead of here. -!!! ! Not entirely sure how to pass the u%InputMarkers in though. -!!! ALLOCATE( u%FVW%WingsMesh(p%NumBl), STAT = ErrStatLcl ) -!!! IF (ErrStatLcl /= 0) THEN -!!! CALL SetErrStat ( ErrID_Fatal, 'Could not allocate u%FVW%InputMarkers (meshes)', ErrStat,ErrMess,RoutineName ) -!!! RETURN -!!! END IF -!!! DO IB = 1, p%NumBl -!!! CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & -!!! ,DestMesh = u%FVW%WingsMesh(IB) & -!!! ,CtrlCode = MESH_COUSIN & -!!! ,Orientation = .TRUE. & -!!! ,TranslationVel = .TRUE. & -!!! ,RotationVel = .TRUE. & -!!! ,ErrStat = ErrStatLcl & -!!! ,ErrMess = ErrMessLcl ) -!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) -!!! IF (ErrStat >= AbortErrLev) RETURN -!!! ENDDO -!!! endif -!!! + + + !------------------------------------------------------------------------------------------------- + ! Initialize FVW module if it is used + !------------------------------------------------------------------------------------------------- + + if (p%WakeMod == WakeMod_FVW) then + call Init_FVWmodule( InputFileData, u, m%FVW_u(1), p, x%FVW, xd%FVW, z%FVW, & + OtherState%FVW, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (ErrStat >= AbortErrLev) then + call Cleanup() + return + end if + + call FVW_CopyInput( m%FVW_u(1), m%FVW_u(2), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + endif + !............................................................................................ ! Define outputs here @@ -2058,8 +2007,134 @@ subroutine Cleanup() call BEMT_DestroyInitInput( InitInp, ErrStat2, ErrMsg2 ) call BEMT_DestroyInitOutput( InitOut, ErrStat2, ErrMsg2 ) end subroutine Cleanup - END SUBROUTINE Init_BEMTmodule + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine initializes the FVW module from within AeroDyn. +SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) +!.................................................................................................................................. + + type(AD_InputFile), intent(in ) :: InputFileData !< All the data in the AeroDyn input file + type(AD_InputType), intent(in ) :: u_AD !< AD inputs - used for input mesh node positions + type(FVW_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined + type(AD_ParameterType), intent(inout) :: p !< Parameters ! intent out b/c we set the FVW parameters here + type(FVW_ContinuousStateType), intent( out) :: x !< Initial continuous states + type(FVW_DiscreteStateType), intent( out) :: xd !< Initial discrete states + type(FVW_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states + type(FVW_OtherStateType), intent( out) :: OtherState !< Initial other states + type(FVW_OutputType), intent( out) :: y !< Initial system outputs (outputs are not calculated; + !! only the output mesh is initialized) + type(FVW_MiscVarType), intent( out) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: errStat !< Error status of the operation + character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None + + + ! Local variables + real(DbKi) :: Interval ! Coupling interval in seconds: the rate that + ! (1) FVW_UpdateStates() is called in loose coupling & + ! (2) FVW_UpdateDiscState() is called in tight coupling. + ! Input is the suggested time from the glue code; + ! Output is the actual coupling interval that will be used + ! by the glue code. + type(FVW_InitInputType) :: InitInp ! Input data for initialization routine + type(FVW_InitOutputType) :: InitOut ! Output for initialization routine + + integer(intKi) :: j ! node index + integer(intKi) :: k ! blade index +! real(ReKi) :: tmp(3), tmp_sz_y, tmp_sz +! real(ReKi) :: y_hat_disk(3) +! real(ReKi) :: z_hat_disk(3) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'Init_FVWmodule' + + ! note here that each blade is required to have the same number of nodes + + ErrStat = ErrID_None + ErrMsg = "" + + + ! set initialization data here: + InitInp%FVWFileName = InputFileData%FVWFileName + InitInp%numBlades = p%numBlades + InitInp%numBladeNodes = p%numBlNds + +! --- TODO TODO TODO ANDY +! Change this so that it would match AD 15 mesh +! NOTE: This mesh does not include the azimuthal differences between blades! +! It's just the spanwise location. +! Also, it is off compared to the initial position of the blade +! Also, it's centered on the hub, but that's fine for now + call AllocAry(InitInp%Chord, InitInp%numBladeNodes,InitInp%numBlades,'chord', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +! call AllocAry(InitInp%AFindx,InitInp%numBladeNodes,InitInp%numBlades,'AFindx',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +! call AllocAry(InitInp%zHub, InitInp%numBlades,'zHub', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +! call AllocAry(InitInp%zLocal,InitInp%numBladeNodes,InitInp%numBlades,'zLocal',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +! call AllocAry(InitInp%rLocal,InitInp%numBladeNodes,InitInp%numBlades,'rLocal',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +! call AllocAry(InitInp%zTip, InitInp%numBlades,'zTip', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +!!! IF (.NOT. ALLOCATED( InitInp%Chord)) ALLOCATE ( InitInp%Chord( p%Element%NElm )) +!!! IF (.NOT. ALLOCATED( InitInp%RElm )) ALLOCATE ( InitInp%RElm( p%Element%NElm )) +!!! InitInp%RElm = p%Element%RElm +!!! InitInp%Chord = p%Blade%C +!!! ALLOCATE( InitInp%WingsMesh(p%NumBlades), STAT = ErrStatLcl ) +!!! IF (ErrStatLcl /= 0) THEN +!!! CALL SetErrStat ( ErrID_Fatal, 'Could not allocate InitInp%WingsMesh (meshes)', ErrStat,ErrMess,RoutineName ) +!!! RETURN +!!! END IF +!!! DO IB = 1, p%NumBlades +!!! CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & +!!! ,DestMesh = InitInp%WingsMesh(IB) & +!!! ,CtrlCode = MESH_COUSIN & +!!! ,Orientation = .TRUE. & +!!! ,TranslationVel = .TRUE. & +!!! ,RotationVel = .TRUE. & +!!! ,ErrStat = ErrStatLcl & +!!! ,ErrMess = ErrMessLcl ) +!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) +!!! IF (ErrStat >= AbortErrLev) RETURN +!!! ENDDO +!!! ! ---- END TODO +!!! +!!! call FVW_Init( InitInp%FVW, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, Interval, InitOut%FVW, ErrStatLcl, ErrMessLcl ) +!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) +!!! +!!! ! If anything is passed back in InitOut%FVW, deal with it here... +!!! +!!! +!!! +!!! ! TODO ANDY +!!! !FIXME This really probably should be done inside of FVW_Init instead of here. +!!! ! Not entirely sure how to pass the u%InputMarkers in though. +!!! ALLOCATE( u%FVW%WingsMesh(p%NumBlades), STAT = ErrStatLcl ) +!!! IF (ErrStatLcl /= 0) THEN +!!! CALL SetErrStat ( ErrID_Fatal, 'Could not allocate u%FVW%InputMarkers (meshes)', ErrStat,ErrMess,RoutineName ) +!!! RETURN +!!! END IF +!!! DO IB = 1, p%NumBlades +!!! CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & +!!! ,DestMesh = u%FVW%WingsMesh(IB) & +!!! ,CtrlCode = MESH_COUSIN & +!!! ,Orientation = .TRUE. & +!!! ,TranslationVel = .TRUE. & +!!! ,RotationVel = .TRUE. & +!!! ,ErrStat = ErrStatLcl & +!!! ,ErrMess = ErrMessLcl ) +!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) +!!! IF (ErrStat >= AbortErrLev) RETURN +!!! ENDDO + + do k=1,p%numBlades + do j=1,p%NumBlNds + InitInp%chord (j,k) = InputFileData%BladeProps(k)%BlChord(j) +! InitInp%AFindx(j,k) = InputFileData%BladeProps(k)%BlAFID(j) + end do + end do + +contains + subroutine Cleanup() + call FVW_DestroyInitInput( InitInp, ErrStat2, ErrMsg2 ) + call FVW_DestroyInitOutput( InitOut, ErrStat2, ErrMsg2 ) + end subroutine Cleanup +END SUBROUTINE Init_FVWmodule !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine calculates the tower loads for the AeroDyn TowerLoad output mesh. SUBROUTINE ADTwr_CalcOutput(p, u, m, y, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index f5070f596f..22ceed85cf 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -2154,6 +2154,15 @@ SUBROUTINE ReadPrimaryFile( InputFile, InputFileData, ADBlFile, OutFileRoot, UnE CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + !----------- FREE VORTEX WAKE (FVW) THEORY OPTIONS ------------------------------ + CALL ReadCom( UnIn, InputFile, 'Section Header: Free Vortex Wake (FVW) Theory Options', ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + CALL ReadVar ( UnIn, InputFile, InputFileData%FVWFileName, 'FVWFileName', 'FVW input file name', ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF ( PathIsRelative( InputFileData%FVWFileName ) ) InputFileData%FVWFileName = TRIM(PriPath)//TRIM(InputFileData%FVWFileName) + + !----------- BEDDOES-LEISHMAN UNSTEADY AIRFOIL AERODYNAMICS OPTIONS ------------- CALL ReadCom( UnIn, InputFile, 'Section Header: Beddoes-Leishman Unsteady Airfoil Aerodynamics Options', ErrStat2, ErrMsg2, UnEc ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) diff --git a/modules/aerodyn/src/AeroDyn_Registry.txt b/modules/aerodyn/src/AeroDyn_Registry.txt index 33366abb7a..f75b989416 100644 --- a/modules/aerodyn/src/AeroDyn_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Registry.txt @@ -13,6 +13,7 @@ include Registry_NWTC_Library.txt usefrom AirfoilInfo_Registry.txt usefrom BEMT_Registry.txt +usefrom FVW_Registry.txt usefrom UnsteadyAero_Registry.txt # ..... Initialization data ....................................................................................................... @@ -91,6 +92,7 @@ typedef ^ AD_InputFile ReKi InCol_Cd - - - "The column in the airfoil tables tha typedef ^ AD_InputFile ReKi InCol_Cm - - - "The column in the airfoil tables that contains the pitching-moment coefficient; use zero if there is no Cm column" - typedef ^ AD_InputFile ReKi InCol_Cpmin - - - "The column in the airfoil tables that contains the drag coefficient; use zero if there is no Cpmin column" - typedef ^ AD_InputFile IntKi NumAFfiles - - - "Number of airfoil files used" - +typedef ^ AD_InputFile CHARACTER(1024) FVWFileName - - - "FVW input filename" "quoted string" typedef ^ AD_InputFile CHARACTER(1024) AFNames {:} - - "Airfoil file names (NumAF lines)" "quoted strings" typedef ^ AD_InputFile LOGICAL UseBlCm - - - "Include aerodynamic pitching moment in calculations?" flag #typedef ^ AD_InputFile IntKi NumBlNds - - - "Number of blade nodes used in the analysis" - @@ -112,20 +114,27 @@ typedef ^ AD_InputFile IntKi DBEMT_Mod - - - "Type of dynamic BEMT (DBEMT) model # ..... States .................................................................................................................... # Define continuous (differentiable) states here: typedef ^ ContinuousStateType BEMT_ContinuousStateType BEMT - - - "Continuous states from the BEMT module" - +typedef ^ ContinuousStateType FVW_ContinuousStateType FVW - - - "Continuous states from the FVW module" - # Define discrete (nondifferentiable) states here: typedef ^ DiscreteStateType BEMT_DiscreteStateType BEMT - - - "Discrete states from the BEMT module" - +typedef ^ DiscreteStateType FVW_DiscreteStateType FVW - - - "Discrete states from the FVW module" - # Define constraint states here: typedef ^ ConstraintStateType BEMT_ConstraintStateType BEMT - - - "Constraint states from the BEMT module" - +typedef ^ ConstraintStateType FVW_ConstraintStateType FVW - - - "Constraint states from the FVW module" - # Define "other" states here: typedef ^ OtherStateType BEMT_OtherStateType BEMT - - - "OtherStates from the BEMT module" - +typedef ^ OtherStateType FVW_OtherStateType FVW - - - "OtherStates from the FVW module" - # Define misc/optimization variables (any data that are not considered actual states) here: typedef ^ MiscVarType BEMT_MiscVarType BEMT - - - "MiscVars from the BEMT module" - typedef ^ MiscVarType BEMT_OutputType BEMT_y - - - "Outputs from the BEMT module" - typedef ^ MiscVarType BEMT_InputType BEMT_u 2 - - "Inputs to the BEMT module" - +typedef ^ MiscVarType FVW_MiscVarType FVW - - - "MiscVars from the FVW module" - +typedef ^ MiscVarType FVW_OutputType FVW_y - - - "Outputs from the FVW module" - +typedef ^ MiscVarType FVW_InputType FVW_u 2 - - "Inputs to the FVW module" - typedef ^ MiscVarType ReKi DisturbedInflow {:}{:}{:} - - "InflowOnBlade values modified by tower influence" m/s typedef ^ MiscVarType ReKi WithoutSweepPitchTwist {:}{:}{:}{:} - - "Coordinate system equivalent to BladeMotion Orientation, but without live sweep, blade-pitch, and twist angles" - typedef ^ MiscVarType ReKi AllOuts {:} - - "An array holding the value of all of the calculated (not only selected) output channels" - @@ -150,7 +159,7 @@ typedef ^ MiscVarType Logical CavitWarnSet {:}{:} - - "cavitation warning issu # 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 continuous state integration & discrete state update" seconds -typedef ^ ParameterType IntKi WakeMod - - - "Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT}" - +typedef ^ ParameterType IntKi WakeMod - - - "Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT, 3=FVW}" - typedef ^ ParameterType IntKi TwrPotent - - - "Type tower influence on wind based on potential flow around the tower {0=none, 1=baseline potential flow, 2=potential flow with Bak correction}" - typedef ^ ParameterType LOGICAL TwrShadow - - - "Calculate tower influence on wind based on downstream tower shadow?" - typedef ^ ParameterType LOGICAL TwrAero - - - "Calculate tower aerodynamic loads?" flag @@ -170,6 +179,7 @@ typedef ^ ParameterType ReKi Pvap - - - "Vapour pressure" Pa typedef ^ ParameterType ReKi FluidDepth - - - "Submerged hub height" m typedef ^ ParameterType AFI_ParameterType AFI - - - "AirfoilInfo parameters" typedef ^ ParameterType BEMT_ParameterType BEMT - - - "Parameters for BEMT module" +typedef ^ ParameterType FVW_ParameterType FVW - - - "Parameters for FVW module" # parameters for output typedef ^ ParameterType IntKi NumOuts - - - "Number of parameters in the output list (number of outputs requested)" - typedef ^ ParameterType CHARACTER(1024) RootName - - - "RootName for writing output files" - diff --git a/modules/aerodyn/src/AeroDyn_Types.f90 b/modules/aerodyn/src/AeroDyn_Types.f90 index e89e839f7b..0d142fcb11 100644 --- a/modules/aerodyn/src/AeroDyn_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Types.f90 @@ -35,6 +35,16 @@ MODULE AeroDyn_Types USE UnsteadyAero_Types USE DBEMT_Types USE BEMT_Types +USE IfW_UniformWind_Types +USE IfW_TSFFWind_Types +USE IfW_BladedFFWind_Types +USE IfW_HAWCWind_Types +USE IfW_UserWind_Types +USE IfW_4Dext_Types +USE Lidar_Types +USE InflowWind_Types +USE AD14AeroConf_Types +USE FVW_Types USE NWTC_Library IMPLICIT NONE ! ========= AD_InitInputType ======= @@ -119,6 +129,7 @@ MODULE AeroDyn_Types REAL(ReKi) :: InCol_Cm !< The column in the airfoil tables that contains the pitching-moment coefficient; use zero if there is no Cm column [-] REAL(ReKi) :: InCol_Cpmin !< The column in the airfoil tables that contains the drag coefficient; use zero if there is no Cpmin column [-] INTEGER(IntKi) :: NumAFfiles !< Number of airfoil files used [-] + CHARACTER(1024) :: FVWFileName !< FVW input filename [quoted string] CHARACTER(1024) , DIMENSION(:), ALLOCATABLE :: AFNames !< Airfoil file names (NumAF lines) [quoted strings] LOGICAL :: UseBlCm !< Include aerodynamic pitching moment in calculations? [flag] TYPE(AD_BladePropsType) , DIMENSION(:), ALLOCATABLE :: BladeProps !< blade property information from blade input files [-] @@ -140,21 +151,25 @@ MODULE AeroDyn_Types ! ========= AD_ContinuousStateType ======= TYPE, PUBLIC :: AD_ContinuousStateType TYPE(BEMT_ContinuousStateType) :: BEMT !< Continuous states from the BEMT module [-] + TYPE(FVW_ContinuousStateType) :: FVW !< Continuous states from the FVW module [-] END TYPE AD_ContinuousStateType ! ======================= ! ========= AD_DiscreteStateType ======= TYPE, PUBLIC :: AD_DiscreteStateType TYPE(BEMT_DiscreteStateType) :: BEMT !< Discrete states from the BEMT module [-] + TYPE(FVW_DiscreteStateType) :: FVW !< Discrete states from the FVW module [-] END TYPE AD_DiscreteStateType ! ======================= ! ========= AD_ConstraintStateType ======= TYPE, PUBLIC :: AD_ConstraintStateType TYPE(BEMT_ConstraintStateType) :: BEMT !< Constraint states from the BEMT module [-] + TYPE(FVW_ConstraintStateType) :: FVW !< Constraint states from the FVW module [-] END TYPE AD_ConstraintStateType ! ======================= ! ========= AD_OtherStateType ======= TYPE, PUBLIC :: AD_OtherStateType TYPE(BEMT_OtherStateType) :: BEMT !< OtherStates from the BEMT module [-] + TYPE(FVW_OtherStateType) :: FVW !< OtherStates from the FVW module [-] END TYPE AD_OtherStateType ! ======================= ! ========= AD_MiscVarType ======= @@ -162,6 +177,9 @@ MODULE AeroDyn_Types TYPE(BEMT_MiscVarType) :: BEMT !< MiscVars from the BEMT module [-] TYPE(BEMT_OutputType) :: BEMT_y !< Outputs from the BEMT module [-] TYPE(BEMT_InputType) , DIMENSION(1:2) :: BEMT_u !< Inputs to the BEMT module [-] + TYPE(FVW_MiscVarType) :: FVW !< MiscVars from the FVW module [-] + TYPE(FVW_OutputType) :: FVW_y !< Outputs from the FVW module [-] + TYPE(FVW_InputType) , DIMENSION(1:2) :: FVW_u !< Inputs to the FVW module [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: DisturbedInflow !< InflowOnBlade values modified by tower influence [m/s] REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: WithoutSweepPitchTwist !< Coordinate system equivalent to BladeMotion Orientation, but without live sweep, blade-pitch, and twist angles [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AllOuts !< An array holding the value of all of the calculated (not only selected) output channels [-] @@ -186,7 +204,7 @@ MODULE AeroDyn_Types ! ========= AD_ParameterType ======= TYPE, PUBLIC :: AD_ParameterType REAL(DbKi) :: DT !< Time step for continuous state integration & discrete state update [seconds] - INTEGER(IntKi) :: WakeMod !< Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT} [-] + INTEGER(IntKi) :: WakeMod !< Type of wake/induction model {0=none, 1=BEMT, 2=DBEMT, 3=FVW} [-] INTEGER(IntKi) :: TwrPotent !< Type tower influence on wind based on potential flow around the tower {0=none, 1=baseline potential flow, 2=potential flow with Bak correction} [-] LOGICAL :: TwrShadow !< Calculate tower influence on wind based on downstream tower shadow? [-] LOGICAL :: TwrAero !< Calculate tower aerodynamic loads? [flag] @@ -206,6 +224,7 @@ MODULE AeroDyn_Types REAL(ReKi) :: FluidDepth !< Submerged hub height [m] TYPE(AFI_ParameterType) :: AFI !< AirfoilInfo parameters [-] TYPE(BEMT_ParameterType) :: BEMT !< Parameters for BEMT module [-] + TYPE(FVW_ParameterType) :: FVW !< Parameters for FVW module [-] INTEGER(IntKi) :: NumOuts !< Number of parameters in the output list (number of outputs requested) [-] CHARACTER(1024) :: RootName !< RootName for writing output files [-] TYPE(OutParmType) , DIMENSION(:), ALLOCATABLE :: OutParam !< Names and units (and other characteristics) of all requested output parameters [-] @@ -2512,6 +2531,7 @@ SUBROUTINE AD_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrSt DstInputFileData%InCol_Cm = SrcInputFileData%InCol_Cm DstInputFileData%InCol_Cpmin = SrcInputFileData%InCol_Cpmin DstInputFileData%NumAFfiles = SrcInputFileData%NumAFfiles + DstInputFileData%FVWFileName = SrcInputFileData%FVWFileName IF (ALLOCATED(SrcInputFileData%AFNames)) THEN i1_l = LBOUND(SrcInputFileData%AFNames,1) i1_u = UBOUND(SrcInputFileData%AFNames,1) @@ -2698,6 +2718,7 @@ SUBROUTINE AD_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Re_BufSz = Re_BufSz + 1 ! InCol_Cm Re_BufSz = Re_BufSz + 1 ! InCol_Cpmin Int_BufSz = Int_BufSz + 1 ! NumAFfiles + Int_BufSz = Int_BufSz + 1*LEN(InData%FVWFileName) ! FVWFileName Int_BufSz = Int_BufSz + 1 ! AFNames allocated yes/no IF ( ALLOCATED(InData%AFNames) ) THEN Int_BufSz = Int_BufSz + 2*1 ! AFNames upper/lower bounds for each dimension @@ -2846,6 +2867,10 @@ SUBROUTINE AD_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumAFfiles Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(InData%FVWFileName) + IntKiBuf(Int_Xferred) = ICHAR(InData%FVWFileName(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I IF ( .NOT. ALLOCATED(InData%AFNames) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -3077,6 +3102,10 @@ SUBROUTINE AD_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err Re_Xferred = Re_Xferred + 1 OutData%NumAFfiles = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(OutData%FVWFileName) + OutData%FVWFileName(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! AFNames not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -3313,6 +3342,9 @@ SUBROUTINE AD_CopyContState( SrcContStateData, DstContStateData, CtrlCode, ErrSt CALL BEMT_CopyContState( SrcContStateData%BEMT, DstContStateData%BEMT, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyContState( SrcContStateData%FVW, DstContStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD_CopyContState SUBROUTINE AD_DestroyContState( ContStateData, ErrStat, ErrMsg ) @@ -3325,6 +3357,7 @@ SUBROUTINE AD_DestroyContState( ContStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL BEMT_DestroyContState( ContStateData%BEMT, ErrStat, ErrMsg ) + CALL FVW_DestroyContState( ContStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD_DestroyContState SUBROUTINE AD_PackContState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -3380,6 +3413,23 @@ SUBROUTINE AD_PackContState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackContState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -3435,6 +3485,34 @@ SUBROUTINE AD_PackContState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackContState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD_PackContState SUBROUTINE AD_UnPackContState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3509,6 +3587,46 @@ SUBROUTINE AD_UnPackContState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackContState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD_UnPackContState SUBROUTINE AD_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrStat, ErrMsg ) @@ -3528,6 +3646,9 @@ SUBROUTINE AD_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrSt CALL BEMT_CopyDiscState( SrcDiscStateData%BEMT, DstDiscStateData%BEMT, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyDiscState( SrcDiscStateData%FVW, DstDiscStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD_CopyDiscState SUBROUTINE AD_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) @@ -3540,6 +3661,7 @@ SUBROUTINE AD_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL BEMT_DestroyDiscState( DiscStateData%BEMT, ErrStat, ErrMsg ) + CALL FVW_DestroyDiscState( DiscStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD_DestroyDiscState SUBROUTINE AD_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -3595,6 +3717,23 @@ SUBROUTINE AD_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackDiscState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -3650,6 +3789,34 @@ SUBROUTINE AD_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackDiscState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD_PackDiscState SUBROUTINE AD_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3724,6 +3891,46 @@ SUBROUTINE AD_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackDiscState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD_UnPackDiscState SUBROUTINE AD_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg ) @@ -3743,6 +3950,9 @@ SUBROUTINE AD_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, CALL BEMT_CopyConstrState( SrcConstrStateData%BEMT, DstConstrStateData%BEMT, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyConstrState( SrcConstrStateData%FVW, DstConstrStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD_CopyConstrState SUBROUTINE AD_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) @@ -3755,6 +3965,7 @@ SUBROUTINE AD_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL BEMT_DestroyConstrState( ConstrStateData%BEMT, ErrStat, ErrMsg ) + CALL FVW_DestroyConstrState( ConstrStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD_DestroyConstrState SUBROUTINE AD_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -3810,6 +4021,23 @@ SUBROUTINE AD_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -3865,6 +4093,34 @@ SUBROUTINE AD_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD_PackConstrState SUBROUTINE AD_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3939,6 +4195,46 @@ SUBROUTINE AD_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackConstrState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD_UnPackConstrState SUBROUTINE AD_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -3958,6 +4254,9 @@ SUBROUTINE AD_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, Er CALL BEMT_CopyOtherState( SrcOtherStateData%BEMT, DstOtherStateData%BEMT, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyOtherState( SrcOtherStateData%FVW, DstOtherStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD_CopyOtherState SUBROUTINE AD_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) @@ -3970,6 +4269,7 @@ SUBROUTINE AD_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL BEMT_DestroyOtherState( OtherStateData%BEMT, ErrStat, ErrMsg ) + CALL FVW_DestroyOtherState( OtherStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD_DestroyOtherState SUBROUTINE AD_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -4025,15 +4325,32 @@ SUBROUTINE AD_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs 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 - 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 ) + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 + 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 @@ -4080,6 +4397,34 @@ SUBROUTINE AD_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD_PackOtherState SUBROUTINE AD_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4154,6 +4499,46 @@ SUBROUTINE AD_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackOtherState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD_UnPackOtherState SUBROUTINE AD_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) @@ -4185,6 +4570,17 @@ SUBROUTINE AD_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN ENDDO + CALL FVW_CopyMisc( SrcMiscData%FVW, DstMiscData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyOutput( SrcMiscData%FVW_y, DstMiscData%FVW_y, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + DO i1 = LBOUND(SrcMiscData%FVW_u,1), UBOUND(SrcMiscData%FVW_u,1) + CALL FVW_CopyInput( SrcMiscData%FVW_u(i1), DstMiscData%FVW_u(i1), CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + ENDDO IF (ALLOCATED(SrcMiscData%DisturbedInflow)) THEN i1_l = LBOUND(SrcMiscData%DisturbedInflow,1) i1_u = UBOUND(SrcMiscData%DisturbedInflow,1) @@ -4416,6 +4812,11 @@ SUBROUTINE AD_DestroyMisc( MiscData, ErrStat, ErrMsg ) CALL BEMT_DestroyOutput( MiscData%BEMT_y, ErrStat, ErrMsg ) DO i1 = LBOUND(MiscData%BEMT_u,1), UBOUND(MiscData%BEMT_u,1) CALL BEMT_DestroyInput( MiscData%BEMT_u(i1), ErrStat, ErrMsg ) +ENDDO + CALL FVW_DestroyMisc( MiscData%FVW, ErrStat, ErrMsg ) + CALL FVW_DestroyOutput( MiscData%FVW_y, ErrStat, ErrMsg ) +DO i1 = LBOUND(MiscData%FVW_u,1), UBOUND(MiscData%FVW_u,1) + CALL FVW_DestroyInput( MiscData%FVW_u(i1), ErrStat, ErrMsg ) ENDDO IF (ALLOCATED(MiscData%DisturbedInflow)) THEN DEALLOCATE(MiscData%DisturbedInflow) @@ -4557,6 +4958,59 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz DEALLOCATE(Int_Buf) END IF END DO + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! FVW_y: size of buffers for each call to pack subtype + CALL FVW_PackOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW_y, ErrStat2, ErrMsg2, .TRUE. ) ! FVW_y + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW_y + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW_y + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW_y + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + DO i1 = LBOUND(InData%FVW_u,1), UBOUND(InData%FVW_u,1) + Int_BufSz = Int_BufSz + 3 ! FVW_u: size of buffers for each call to pack subtype + CALL FVW_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW_u(i1), ErrStat2, ErrMsg2, .TRUE. ) ! FVW_u + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW_u + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW_u + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW_u + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + END DO Int_BufSz = Int_BufSz + 1 ! DisturbedInflow allocated yes/no IF ( ALLOCATED(InData%DisturbedInflow) ) THEN Int_BufSz = Int_BufSz + 2*3 ! DisturbedInflow upper/lower bounds for each dimension @@ -4783,6 +5237,92 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF END DO + CALL FVW_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 + CALL FVW_PackOutput( Re_Buf, Db_Buf, Int_Buf, InData%FVW_y, ErrStat2, ErrMsg2, OnlySize ) ! FVW_y + 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 + DO i1 = LBOUND(InData%FVW_u,1), UBOUND(InData%FVW_u,1) + CALL FVW_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW_u(i1), ErrStat2, ErrMsg2, OnlySize ) ! FVW_u + 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 DO IF ( .NOT. ALLOCATED(InData%DisturbedInflow) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -5241,6 +5781,130 @@ SUBROUTINE AD_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) 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 FVW_UnpackMisc( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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) + 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 FVW_UnpackOutput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW_y, ErrStat2, ErrMsg2 ) ! FVW_y + 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) + i1_l = LBOUND(OutData%FVW_u,1) + i1_u = UBOUND(OutData%FVW_u,1) + DO i1 = LBOUND(OutData%FVW_u,1), UBOUND(OutData%FVW_u,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 FVW_UnpackInput( Re_Buf, Db_Buf, Int_Buf, OutData%FVW_u(i1), ErrStat2, ErrMsg2 ) ! FVW_u + 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 DO IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! DisturbedInflow not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -5787,6 +6451,9 @@ SUBROUTINE AD_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg ) CALL BEMT_CopyParam( SrcParamData%BEMT, DstParamData%BEMT, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyParam( SrcParamData%FVW, DstParamData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN DstParamData%NumOuts = SrcParamData%NumOuts DstParamData%RootName = SrcParamData%RootName IF (ALLOCATED(SrcParamData%OutParam)) THEN @@ -5855,6 +6522,7 @@ SUBROUTINE AD_DestroyParam( ParamData, ErrStat, ErrMsg ) ENDIF CALL AFI_DestroyParam( ParamData%AFI, ErrStat, ErrMsg ) CALL BEMT_DestroyParam( ParamData%BEMT, ErrStat, ErrMsg ) + CALL FVW_DestroyParam( ParamData%FVW, ErrStat, ErrMsg ) IF (ALLOCATED(ParamData%OutParam)) THEN DO i1 = LBOUND(ParamData%OutParam,1), UBOUND(ParamData%OutParam,1) CALL NWTC_Library_Destroyoutparmtype( ParamData%OutParam(i1), ErrStat, ErrMsg ) @@ -5966,6 +6634,23 @@ SUBROUTINE AD_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF Int_BufSz = Int_BufSz + 1 ! NumOuts Int_BufSz = Int_BufSz + 1*LEN(InData%RootName) ! RootName Int_BufSz = Int_BufSz + 1 ! OutParam allocated yes/no @@ -6125,6 +6810,34 @@ SUBROUTINE AD_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si 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 + CALL FVW_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 @@ -6428,6 +7141,46 @@ SUBROUTINE AD_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg 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) + 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 FVW_UnpackParam( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index ecd84c31f1..f7c96127a6 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -228,7 +228,7 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" ! - p%nWings = InitInp%NumBl + p%nWings = InitInp%NumBlades ! TODO TODO TODO Hack for AD14 mesh that is wrong !p%nWings = 1 diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 9e6379e77f..bdcaebf8e4 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -262,7 +262,7 @@ subroutine LatticeToPanlConnectivity(LatticePoints, Connectivity, Points) k=k+1 enddo; enddo -! do iWing=1,p%NumBl +! do iWing=1,p%NumBlades ! if ( vtk_new_ascii_file(trim(filename),Label) ) then ! ! Buffer for points ! k=1; do iNW=1,nNW; do iSpan=1,nSpan diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 3886474114..c06980157a 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -84,9 +84,10 @@ typedef ^ ^ Reki # FVW_InitInputType typedef FVW/FVW InitInputType CHARACTER(1024) FVWFileName - - - "Main FVW input file name" - typedef ^ ^ MeshType WingsMesh : - - "Input Mesh defining position and orientation of wings (nSpan+1) " - -typedef ^ ^ ReKi Chord : - - "Chord of each blade element from input file" - +typedef ^ ^ ReKi Chord :: - - "Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number]" - typedef ^ ^ ReKi RElm : - - "radius of center of each element" - -typedef ^ ^ IntKi NumBl - - - "Number of blades" - +typedef ^ ^ IntKi NumBlades - - - "Number of blades" - +typedef ^ ^ IntKi NumBladeNodes - - - "Number of nodes on each blade" - #.......... InitInputType ...... # FVW_InputFile diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 8345abf735..a7dfffe4b7 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -122,9 +122,10 @@ MODULE FVW_Types TYPE, PUBLIC :: FVW_InitInputType CHARACTER(1024) :: FVWFileName !< Main FVW input file name [-] TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: WingsMesh !< Input Mesh defining position and orientation of wings (nSpan+1) [-] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number] [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: RElm !< radius of center of each element [-] - INTEGER(IntKi) :: NumBl !< Number of blades [-] + INTEGER(IntKi) :: NumBlades !< Number of blades [-] + INTEGER(IntKi) :: NumBladeNodes !< Number of nodes on each blade [-] END TYPE FVW_InitInputType ! ======================= ! ========= FVW_InputFile ======= @@ -3537,6 +3538,7 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS ! 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyInitInput' @@ -3563,8 +3565,10 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS IF (ALLOCATED(SrcInitInputData%Chord)) THEN i1_l = LBOUND(SrcInitInputData%Chord,1) i1_u = UBOUND(SrcInitInputData%Chord,1) + i2_l = LBOUND(SrcInitInputData%Chord,2) + i2_u = UBOUND(SrcInitInputData%Chord,2) IF (.NOT. ALLOCATED(DstInitInputData%Chord)) THEN - ALLOCATE(DstInitInputData%Chord(i1_l:i1_u),STAT=ErrStat2) + ALLOCATE(DstInitInputData%Chord(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%Chord.', ErrStat, ErrMsg,RoutineName) RETURN @@ -3584,7 +3588,8 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS END IF DstInitInputData%RElm = SrcInitInputData%RElm ENDIF - DstInitInputData%NumBl = SrcInitInputData%NumBl + DstInitInputData%NumBlades = SrcInitInputData%NumBlades + DstInitInputData%NumBladeNodes = SrcInitInputData%NumBladeNodes END SUBROUTINE FVW_CopyInitInput SUBROUTINE FVW_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -3672,7 +3677,7 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs END IF Int_BufSz = Int_BufSz + 1 ! Chord allocated yes/no IF ( ALLOCATED(InData%Chord) ) THEN - Int_BufSz = Int_BufSz + 2*1 ! Chord upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + 2*2 ! Chord upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%Chord) ! Chord END IF Int_BufSz = Int_BufSz + 1 ! RElm allocated yes/no @@ -3680,7 +3685,8 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 2*1 ! RElm upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%RElm) ! RElm END IF - Int_BufSz = Int_BufSz + 1 ! NumBl + Int_BufSz = Int_BufSz + 1 ! NumBlades + Int_BufSz = Int_BufSz + 1 ! NumBladeNodes IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -3761,6 +3767,9 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf( Int_Xferred ) = LBOUND(InData%Chord,1) IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Chord,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Chord,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Chord,2) Int_Xferred = Int_Xferred + 2 IF (SIZE(InData%Chord)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Chord))-1 ) = PACK(InData%Chord,.TRUE.) @@ -3779,7 +3788,9 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs IF (SIZE(InData%RElm)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%RElm))-1 ) = PACK(InData%RElm,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%RElm) END IF - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumBl + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumBlades + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumBladeNodes Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackInitInput @@ -3803,6 +3814,7 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackInitInput' @@ -3883,21 +3895,24 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er 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 IF (ALLOCATED(OutData%Chord)) DEALLOCATE(OutData%Chord) - ALLOCATE(OutData%Chord(i1_l:i1_u),STAT=ErrStat2) + ALLOCATE(OutData%Chord(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Chord.', ErrStat, ErrMsg,RoutineName) RETURN END IF - ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) RETURN END IF - mask1 = .TRUE. - IF (SIZE(OutData%Chord)>0) OutData%Chord = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Chord))-1 ), mask1, 0.0_ReKi ) + mask2 = .TRUE. + IF (SIZE(OutData%Chord)>0) OutData%Chord = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Chord))-1 ), mask2, 0.0_ReKi ) Re_Xferred = Re_Xferred + SIZE(OutData%Chord) - DEALLOCATE(mask1) + DEALLOCATE(mask2) END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! RElm not allocated Int_Xferred = Int_Xferred + 1 @@ -3922,7 +3937,9 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Re_Xferred = Re_Xferred + SIZE(OutData%RElm) DEALLOCATE(mask1) END IF - OutData%NumBl = IntKiBuf( Int_Xferred ) + OutData%NumBlades = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%NumBladeNodes = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackInitInput diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index f5f3a9f5f0..5c0ff77dbf 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -17,7 +17,7 @@ module FVW_Wings subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh real(ReKi), dimension(:), intent(in ) :: r !< - real(ReKi), dimension(:), intent(in ) :: chord !< + real(ReKi), dimension(:,:), intent(in ) :: chord !< type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation @@ -60,7 +60,7 @@ subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) print*,'Input mesh size',Meshes(iW)%nNodes,' Number of vortex element', p%nSpan do iSpan = 1, p%nSpan+1 m%s_LL (iSpan, iW) = s_in(iSpan) - m%chord_LL(iSpan, iW) = chord(iSpan) + m%chord_LL(iSpan, iW) = chord(iSpan,iW) enddo ! --- Control points ! TODO possibly Control points are not exactly at the middle depending on "meshing" method From fce0d4436aa7c9673d660f6b3c827decdfecbf6b Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 12:38:35 -0700 Subject: [PATCH 011/190] FVW: Adding induced velocity functions --- modules/aerodyn14/src/FVW_BiotSavart.f90 | 240 ++++++++++++++++++++--- 1 file changed, 210 insertions(+), 30 deletions(-) diff --git a/modules/aerodyn14/src/FVW_BiotSavart.f90 b/modules/aerodyn14/src/FVW_BiotSavart.f90 index cd19b1c1ab..d882b51720 100644 --- a/modules/aerodyn14/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn14/src/FVW_BiotSavart.f90 @@ -16,13 +16,154 @@ module FVW_BiotSavart integer(IntKi), parameter :: idSegSmoothCompactDim = 5 contains + +!> Induced velocity from one segment at one control points +subroutine ui_seg_11(DeltaPa, DeltaPb, Gam, SmoothModel , SmoothParam, Ui) + implicit none + ! Input/output arguments + real(ReKi), dimension(3), intent(in) :: DeltaPa !< 3 x 1 Pcp-P1 + real(ReKi), dimension(3), intent(in) :: DeltaPb !< 3 x 1 Pcp-P2 + real(ReKi), intent(in) :: Gam !< + integer, intent(in) :: SmoothModel !< + real(ReKi), intent(in) :: SmoothParam !< + real(ReKi), dimension(3),intent(out) :: Ui !< No Side effects + ! Variables declaration + real(ReKi),dimension(3) :: crossprod !< + real(ReKi),dimension(3) :: D !< + real(ReKi) :: denom !< + real(ReKi) :: denominator !< + real(ReKi) :: h2 !< Square of h + real(ReKi) :: h !< Only used by one model + real(ReKi) :: Kv !< + real(ReKi) :: norm_a !< + real(ReKi) :: norm_b !< + real(ReKi) :: norm2_r0 !< + real(ReKi) :: norm2_crossprod !< + real(ReKi) :: xa !< + real(ReKi) :: ya !< + real(ReKi) :: za !< + real(ReKi) :: xb !< + real(ReKi) :: yb !< + real(ReKi) :: zb !< + real(ReKi) :: SmoothParam2 !< Square of viscous param + real(ReKi) :: exp_value !< + real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) + ! + xa=DeltaPa(1) + ya=DeltaPa(2) + za=DeltaPa(3) + xb=DeltaPb(1) + yb=DeltaPb(2) + zb=DeltaPb(3) + !--- Simple test if on an extremity point + if(abs(xa)+abs(ya)+abs(za)rc + ! orthogonal distance r1xr2/r0 + h2 = norm2_crossprod/ norm2_r0 + SmoothParam2=SmoothParam**2 + if (h2rc + ! orthogonal distance r1xr2/r0 + h2 = norm2_crossprod/ norm2_r0 + SmoothParam2=SmoothParam**2 + exp_value = -1.25643_ReKi*(h2)/(SmoothParam2) + if(exp_valuerc + ! orthogonal distance r1xr2/r0 + h2 = norm2_crossprod/ norm2_r0 + SmoothParam2=SmoothParam**2 + ! h = (norm_a+norm_b)/2; + Kv = h2/sqrt(SmoothParam2**2+h2**2) + case ( idSegSmoothCompact ) !!Cut-off radius as function of length: delta^2*norm(r0)^2 + h2 = norm2_crossprod/ norm2_r0 + Kv=1.0_ReKi + ! delta*norm(r0)^2 + denominator=denominator+SmoothParam*norm2_r0 + SmoothParam2=SmoothParam**2 ! TODO + case ( idSegSmoothCompactDim ) !!Cut-off radius dimension fixed: (delta l_0)^2 + h2 = norm2_crossprod/ norm2_r0 + Kv=1.0_ReKi + ! (delta l_0)^2 + denominator=denominator+SmoothParam + SmoothParam2=SmoothParam**2 ! TODO + case ( 33 ) !!Vatistas n=2 - SmoothParamt<=>rc + ! See Hoydonck 2012. and Script MainRingSensitivityN + ! Not matture enough + h = (norm_a+norm_b)/2.0_ReKi + SmoothParam2=SmoothParam**2 + if(h<2*sqrt(norm2_r0)) then + ! use Vatistas n=2, normal one + h2 = norm2_crossprod/norm2_r0 + else + h2 = 1._ReKi ! TODO + endif + Kv = (h2)/sqrt(SmoothParam2**2+h2**2) + case DEFAULT + Kv=1.0_ReKi + end select + + Kv=Gam*fourpi_inv*Kv*(norm_a+norm_b)/denominator + Ui(1:3) = Kv*crossprod(1:3) + end if ! denominator size or distances too small + end if ! + ! printf("!4.3f !4.3f !4.3f !4.3f !4.3fNewLine",Uout(1),Uout(2),Uout(3),Kv,denominator); +end subroutine ui_seg_11 + + !> Induced velocity from a list of segments defined by Connectivity (SegConnct) and Points (SegPoints) !! NOTE: this funciton has side effects and expect Uind_out to be initialized! !! The function can compute the velocity on part of the segments and part of the control points. !! This feature is useful if some parallelization is used, while common storage vectors are used. !! -subroutine ui_seg(CPs, iCPStart, iCPEnd, nCPsTot, & - SegPoints, SegConnct, SegGamma, iSegStart, iSegEnd, nSegTot, nSegPTot, & +subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & + iSegStart, iSegEnd, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, & SmoothModel, SmoothParam, Uind_out) use OMP_LIB implicit none @@ -49,7 +190,7 @@ subroutine ui_seg(CPs, iCPStart, iCPEnd, nCPsTot, & real(ReKi), dimension(3) :: DP1, DP2 !< real(ReKi) :: norm2_r0 !< real(ReKi) :: Gam !< - real(ReKi) :: visc_param !< + real(ReKi) :: SmoothParam1 !< ! Variables declaration real(ReKi),dimension(3) :: crossprod !< real(ReKi) :: denom !< @@ -66,15 +207,15 @@ subroutine ui_seg(CPs, iCPStart, iCPEnd, nCPsTot, & real(ReKi) :: xb !< real(ReKi) :: yb !< real(ReKi) :: zb !< - real(ReKi) :: visc_param2 !< Square of viscous param + real(ReKi) :: SmoothParam2 !< Square of viscous param real(ReKi) :: exp_value !< real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) - !$OMP PARALLEL default(shared) - !$OMP do private(& - !$OMP& icp,is,Uind,P1,P2,DP1,DP2,norm2_r0,Gam,visc_param,& - !$OMP& crossprod,denom,denominator,h2,h,Kv,norm_a,norm_b,norm2_crossprod,xa,ya,za,xb,yb,zb,visc_param2,exp_value& - !$OMP& ) schedule(runtime) + !OMP PARALLEL default(shared) + !OMP do private(& + !OMP& icp,is,Uind,P1,P2,DP1,DP2,norm2_r0,Gam,SmoothParam,& + !OMP& crossprod,denom,denominator,h2,h,Kv,norm_a,norm_b,norm2_crossprod,xa,ya,za,xb,yb,zb,SmoothParam2,exp_value& + !OMP& ) schedule(runtime) ! loop on CPs do icp=iCPStart,iCPEnd do is=iSegStart,iSegEnd ! loop on selected segments @@ -85,8 +226,8 @@ subroutine ui_seg(CPs, iCPStart, iCPEnd, nCPsTot, & DP1 = CPs(:,icp)-P1; DP2 = CPs(:,icp)-P2 Gam = SegGamma(is) - visc_param = SmoothParam(is) - ! --- inlining of Segment 11 + SmoothParam1= SmoothParam(is) + ! --- inlining of ui_seg_11 xa=DP1(1) ya=DP1(2) za=DP1(3) @@ -109,10 +250,7 @@ subroutine ui_seg(CPs, iCPStart, iCPEnd, nCPsTot, & crossprod(2) = za*xb-xa*zb crossprod(3) = xa*yb-ya*xb norm2_crossprod=crossprod(1)**2+ crossprod(2)**2+ crossprod(3)**2 - !print "(A,6F21.16)","xa xb:",xa,ya,za,xb,yb,zb ! check for singularity - ! TODO this check is viscous model dependent(e.g. crossprod)... have to go towards different funcitons soon.. - ! to avoid singularity, save a bit of computation and use limit values if (denominatorrc ! orthogonal distance r1xr2/r0 h2 = norm2_crossprod/ norm2_r0 - visc_param2 = visc_param**2 - if (h2rc ! orthogonal distance r1xr2/r0 h2 = norm2_crossprod/ norm2_r0 - visc_param2 = visc_param**2 - exp_value = -1.25643_ReKi*(h2)/(visc_param2) + SmoothParam2 = SmoothParam1**2 + exp_value = -1.25643_ReKi*(h2)/(SmoothParam2) if(exp_valuerc + case ( idSegSmoothVatistas ) !!Vatistas n=2 - SmoothParam<=>rc ! orthogonal distance r1xr2/r0 h2 = norm2_crossprod/ norm2_r0 - visc_param2 = visc_param**2 + SmoothParam2 = SmoothParam1**2 ! h = (norm_a+norm_b)/2; - Kv = h2/sqrt(visc_param2**2+h2**2) + Kv = h2/sqrt(SmoothParam2**2+h2**2) case ( idSegSmoothCompact ) !!Cut-off radius as function of length: delta^2*norm(r0)^2 Kv=1.0_ReKi - denominator=denominator+visc_param*visc_param*norm2_r0 + denominator=denominator+SmoothParam1*SmoothParam1*norm2_r0 case ( idSegSmoothCompactDim ) !!Cut-off radius dimension fixed: (delta l_0)^2 Kv=1.0_ReKi - denominator=denominator+visc_param*visc_param - case ( 33 ) !!Vatistas n=2 - visc_paramt<=>rc + denominator=denominator+SmoothParam1*SmoothParam1 + case ( 33 ) !!Vatistas n=2 - SmoothParamt<=>rc ! See Hoydonck 2012 h = (norm_a+norm_b)/2.0_ReKi - visc_param2 = visc_param**2 + SmoothParam2 = SmoothParam1**2 if(h<2*sqrt(norm2_r0)) then ! use Vatistas n=2, normal one h2 = norm2_crossprod/norm2_r0 end if - Kv = (h2)/sqrt(visc_param2**2+h2**2) + Kv = (h2)/sqrt(SmoothParam2**2+h2**2) case default Kv=1.0_ReKi end select @@ -178,9 +316,51 @@ subroutine ui_seg(CPs, iCPStart, iCPEnd, nCPsTot, & Uind_out(1:3,icp) = Uind_out(1:3,icp)+Uind(1:3) end do ! Loop on segments enddo ! Loop on control points - !$OMP END DO - !$OMP END PARALLEL + !OMP END DO + !OMP END PARALLEL end subroutine +!> Velocity induced by one vortex quad on nCPs Control Points +subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, SmoothModel, SmoothParam, Uind) + ! Arguments declarations + integer, intent(in) :: nCPs !< + real(ReKi), dimension(3,nCPs), intent(in) :: CPs !< + real(ReKi), dimension(3), intent(in) :: P1,P2,P3,P4 !< Coordinates of vortex quadrilateral + real(ReKi), intent(in) :: Gamm + integer(IntKi) , intent(in) :: SmoothModel !< Most likely should be 0 + real(ReKi), intent(in) :: SmoothParam !< + real(ReKi), dimension(3,nCPs), intent(out) :: Uind !< no side effects!!! + ! Variable declarations + real(ReKi), dimension(3) :: CP !< + real(ReKi), dimension(3) :: Uindtmp !< + real(ReKi), dimension(3) :: DP1 !< + real(ReKi), dimension(3) :: DP2 !< + integer :: icp + ! + !OMP PARALLEL DEFAULT(SHARED) + !OMP DO PRIVATE(icp,CP,Uindtmp,DP1,DP2) schedule(runtime) + do icp=1,nCPs + CP(1:3)=CPs(1:3,icp) + ! 1-2 segment + DP1=CP-P1; DP2=CP-P2; + call ui_seg_11 ( DP1, DP2, Gamm, SmoothModel, SmoothParam, Uindtmp) + Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) + ! 3-4 segment + DP1=CP-P3; DP2=CP-P4; + call ui_seg_11 ( DP1, DP2, Gamm, SmoothModel, SmoothParam, Uindtmp) + Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) + ! 2-3 segment + DP1=CP-P2; DP2=CP-P3; + call ui_seg_11 ( DP1, DP2, Gamm, SmoothModel, SmoothParam, Uindtmp) + Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) + ! 4-1 segment + DP1=CP-P4; DP2=CP-P1; + call ui_seg_11 ( DP1, DP2, Gamm, SmoothModel, SmoothParam, Uindtmp) + Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) + end do ! loop on CPs + !OMP END DO + !OMP END PARALLEL +end subroutine ui_quad_n1 + end module FVW_BiotSavart From b7db329d471819101026dff4c4e8006b2528d828 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 12:41:39 -0700 Subject: [PATCH 012/190] FVW: pack functions, intent(out), and no seg at t=0 --- modules/aerodyn14/src/FVW_IO.f90 | 24 +++++++--- modules/aerodyn14/src/FVW_Subs.f90 | 8 ++-- modules/aerodyn14/src/FVW_VortexTools.f90 | 58 ++++++++++++++++------- 3 files changed, 61 insertions(+), 29 deletions(-) diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index 9e6379e77f..2f77efe581 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -36,16 +36,21 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) !------------------------ GENERAL OPTIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'General option header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%FreeWake ,'FreeWake' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%FreeWakeStart,'FreeWakeStart' ,'',ErrStat2,ErrMsg2); if(Failed())return !------------------------ CIRCULATION SPECIFICATIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'Circulation specification header', ErrStat2, ErrMsg2 ); if(Failed()) return - CALL ReadVar(UnIn,FileName,Inp%CirculationMethod,'CirculationMethod','',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod','',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CircSolvConvCrit ,'CircSolvConvCrit ','',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CircSolvRelaxation,'CircSolvRelaxation','',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CircSolvMaxIter ,'CircSolvMaxIter','',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%PrescribedPolar ,'PrescribedPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return ! Post pro and validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) - if (Check(.not.(ANY((/idCircNoFlowThrough,idCircPrescribed/)==Inp%CirculationMethod)), 'Circulation method not implemented')) return + if (Check(.not.(ANY((/idCircPrescribed,idCircPolarData/)==Inp%CirculationMethod)), 'Circulation method not implemented')) return if (Check( Inp%IntMethod/=idEuler1 , 'Time integration method not implemented')) return @@ -160,7 +165,12 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! --- All Segments ! --------------------------------------------------------------------------------{ nP = nWings * ( (nSpan+1)*(nNW+1) ) - nC = nWings * (2*(nSpan+1)*(nNW+1)-nSpan-nNW-2) + if (nNW==0) then + nC=0 ! TODO export of single line not supported by Lattice to Segments + else + nC = nWings * (2*(nSpan+1)*(nNW+1)-nSpan-nNW-2) + endif +! nC = nWings * (2*(nSpan+1)*(nNW+1)-nSpan-nNW-2) ! nP = nP + nWings * (nSpan+1)*2 ! nC = nC + nWings * (2*(nSpan+1)*(2)-nSpan-1-2) allocate(SegConnct(1:2,1:nC)); SegConnct=-1 @@ -182,11 +192,11 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) if ((iHeadP-1)/=nP) then print*,'IO: Number of points wrongly estimated',nP, iHeadP-1 -! STOP + STOP endif if ((iHeadC-1)/=nC) then print*,'IO: Number of segments wrongly estimated',nC, iHeadC-1 -! STOP + STOP endif diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index 851e396a32..bf9b34a787 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -291,9 +291,9 @@ subroutine PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, n real(ReKi), dimension(:,:), allocatable :: Buffer2d !real(ReKi), dimension(:), allocatable :: SegSmooth !< - ! Counting total number of segments + ! Counting total number of segments TODO add FarWake nP = p%nWings * ( (p%nSpan+1)*(m%nNW+1) ) - nC = p%nWings * (2*(p%nSpan+1)*(m%nNW+1)-p%nSpan-m%nNW-2) + nC = p%nWings * (2*(p%nSpan+1)*(m%nNW+1)-p%nSpan-m%nNW-2) ! nP = nP + p%nWings * (p%nSpan+1)*2 ! nC = nC + p%nWings * (2*(p%nSpan+1)*(2)-p%nSpan-1-2) @@ -317,11 +317,11 @@ subroutine PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, n ! enddo if ((iHeadP-1)/=nP) then print*,'Number of points wrongly estimated',nP, iHeadP-1 -! STOP + STOP endif if ((iHeadC-1)/=nC) then print*,'Number of segments wrongly estimated',nC, iHeadC-1 -! STOP + STOP endif nSeg = iHeadC-1 nSegP = iHeadP-1 diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn14/src/FVW_VortexTools.f90 index 1bda1d1bf1..2d8a5b9acc 100644 --- a/modules/aerodyn14/src/FVW_VortexTools.f90 +++ b/modules/aerodyn14/src/FVW_VortexTools.f90 @@ -11,14 +11,50 @@ MODULE FVW_VortexTools CONTAINS + subroutine VecToLattice(PointVectors, LatticeVectors, iHeadP) + real(Reki), dimension(:,:), intent(in ) :: PointVectors !< nVal x n + real(ReKi), dimension(:,:,:), intent(inout) :: LatticeVectors !< nVal x nSpan x nDepth + integer(IntKi), intent(inout) :: iHeadP !< Index indicating where to start in PointVectors + integer(IntKi) :: iSpan, iDepth + do iDepth = 1, size(LatticeVectors,3) + do iSpan = 1, size(LatticeVectors,2) + LatticeVectors(:, iSpan, iDepth) = PointVectors(:, iHeadP) + iHeadP=iHeadP+1 + enddo + enddo + end subroutine + + subroutine LatticeToPoints(LatticePoints, Points, iHeadP) + real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Points 3 x nSpan x nDepth + real(ReKi), dimension(:,:), intent(inout) :: Points !< + integer(IntKi), intent(inout) :: iHeadP !< Index indicating where to start in Points + ! Local + integer(IntKi) :: iSpan, iDepth + ! Points are flattened as follows: (Loop order is important) + ! + ! 3---6 + ! | | + ! 2---5 + ! | | + ! 1---4 + ! + do iDepth = 1, size(LatticePoints,3) + do iSpan = 1, size(LatticePoints,2) + Points(1:3,iHeadP) = LatticePoints(1:3, iSpan, iDepth) + iHeadP=iHeadP+1 + enddo + enddo + + endsubroutine LatticeToPoints + subroutine LatticeToSegments(LatticePoints, LatticeGamma, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Points 3 x nSpan x nDepth real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< GammaPanl nSpan x nDepth real(ReKi), dimension(:,:), intent(inout) :: SegPoints !< integer(IntKi), dimension(:,:), intent(inout) :: SegConnct !< real(ReKi), dimension(:), intent(inout) :: SegGamma !< - integer(IntKi), intent( out) :: iHeadP !< Index indicating where to start in SegPoints - integer(IntKi), intent( out) :: iHeadC !< Index indicating where to start in SegConnct + integer(IntKi), intent(inout) :: iHeadP !< Index indicating where to start in SegPoints + integer(IntKi), intent(inout) :: iHeadC !< Index indicating where to start in SegConnct ! Local integer(IntKi) :: nSpan, nDepth integer(IntKi) :: iSpan, iDepth @@ -32,23 +68,9 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, SegPoints, SegConnct, iHeadP0=iHeadP ! Storing - + ! --- Flattening LatticePoints into SegPoints array, and increment iHeadP ! We will need all the points, we flatten the point array - ! Loop order is important - ! Points are flattened as follows: - ! - ! 3---6 - ! | | - ! 2---5 - ! | | - ! 1---4 - ! - do iDepth = 1, nDepth - do iSpan = 1, nSpan - SegPoints(1:3,iHeadP) = LatticePoints(1:3, iSpan, iDepth) - iHeadP=iHeadP+1 - enddo - enddo + call LatticeToPoints(LatticePoints, SegPoints, iHeadP) ! --- Creating segments ! Naming convention for point indices and segments of a panel: From ce0928d457a20f9572951604b402f231e901abd0 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 12:42:00 -0700 Subject: [PATCH 013/190] FVW: fix index error --- modules/aerodyn14/src/FVW_Wings.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index fd3daf011c..ff443cca11 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -116,7 +116,7 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) ! --- Generic code below to compute normal/tangential vectors of a lifting line panel ! Notations follow vanGarrel [TODO REF] do iW = 1,p%nWings - do iSpan = 1,p%nSpan+1 + do iSpan = 1,p%nSpan P1 = m%LE(:,iSpan , iw) P4 = m%LE(:,iSpan+1, iw) P3 = m%TE(:,iSpan+1, iw) From 0e1a0240e2568b3289529defb862a8b01fb95de6 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 12:43:46 -0700 Subject: [PATCH 014/190] FVW: adding inputs for circulation solving --- modules/aerodyn14/src/FVW.f90 | 13 +- modules/aerodyn14/src/FVW_Tests.f90 | 4 +- modules/aerodyn14/src/FVW_Types.f90 | 231 ++++++++++++++++++++++++- modules/aerodyn14/src/Registry-FVW.txt | 18 +- 4 files changed, 251 insertions(+), 15 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index ecd84c31f1..6df64e7f19 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -152,6 +152,8 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%Tang , 3 , p%nSpan , p%nWings, 'Tangential vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Tang= -999999_ReKi; call AllocAry( m%Norm , 3 , p%nSpan , p%nWings, 'Normal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Norm= -999999_ReKi; call AllocAry( m%Orth , 3 , p%nSpan , p%nWings, 'Orthogonal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Orth= -999999_ReKi; + call AllocAry( m%dl , 3 , p%nSpan , p%nWings, 'Orthogonal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%dl= -999999_ReKi; + call AllocAry( m%Area , p%nSpan , p%nWings, 'LL Panel area ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Area = -999999_ReKi; call AllocAry( m%Vind_LL , 3 , p%nSpan , p%nWings, 'Vind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_LL= -999999_ReKi; call AllocAry( m%Vtot_LL , 3 , p%nSpan , p%nWings, 'Vtot on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vtot_LL= -999999_ReKi; call AllocAry( m%Vstr_LL , 3 , p%nSpan , p%nWings, 'Vstr on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vstr_LL= -999999_ReKi; @@ -251,9 +253,14 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg ErrMsg = "" ! Set parameters from input file - p%IntMethod = InputFileData%IntMethod - p%CirculationMethod = InputFileData%CirculationMethod - p%FreeWake = InputFileData%FreeWake + p%IntMethod = InputFileData%IntMethod + p%CirculationMethod = InputFileData%CirculationMethod + p%CircSolvConvCrit = InputFileData%CircSolvConvCrit + p%CircSolvRelaxation = InputFileData%CircSolvRelaxation + p%CircSolvMaxIter = InputFileData%CircSolvMaxIter + p%FreeWakeStart = InputFileData%FreeWakeStart + p%PrescribedPolar = InputFileData%PrescribedPolar + p%FullCirculationStart = InputFileData%FullCirculationStart if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) if (InputFileData%CirculationMethod==idCircPrescribed) then diff --git a/modules/aerodyn14/src/FVW_Tests.f90 b/modules/aerodyn14/src/FVW_Tests.f90 index e52ccfac2e..924412ed2b 100644 --- a/modules/aerodyn14/src/FVW_Tests.f90 +++ b/modules/aerodyn14/src/FVW_Tests.f90 @@ -78,8 +78,8 @@ subroutine Test_LatticeToSegment(iStat) CPs(1:3,1)=(/1.5,1.5,0./) SegSmooth=100.0_ReKi SmoothModel=0 ! No smooth - CALL ui_seg(CPs, 1, 1, 1, & - SegPoints, SegConnct, SegGamma, 1, nC1, nC1, nP1, & + CALL ui_seg(1, 1, 1, CPs, & + 1, nC1, nC1, nP1, SegPoints, SegConnct, SegGamma, & SmoothModel, SegSmooth, Uind) print*,'Uind',Uind diff --git a/modules/aerodyn14/src/FVW_Types.f90 b/modules/aerodyn14/src/FVW_Types.f90 index 8345abf735..7a14d9d6b9 100644 --- a/modules/aerodyn14/src/FVW_Types.f90 +++ b/modules/aerodyn14/src/FVW_Types.f90 @@ -49,9 +49,14 @@ MODULE FVW_Types INTEGER(IntKi) :: nNWMax !< Maximum number of nw panels [-] INTEGER(IntKi) :: nFWMax !< Maximum number of fw panels [-] INTEGER(IntKi) :: IntMethod !< Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1) [-] - LOGICAL :: FreeWake !< Disable roll up, wake convects with wind only (flag) [-] + REAL(ReKi) :: FreeWakeStart !< Time when wake starts convecting (rolling up) [s] + REAL(ReKi) :: FullCirculationStart !< Time when the circulation is full [s] INTEGER(IntKi) :: CirculationMethod !< Method to determine the circulation [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: PrescribedCirculation !< Prescribed circulation on all lifting lines [m/s] + INTEGER(IntKi) :: CircSolvMaxIter !< Maximum number of iterations for circulation solving [-] + REAL(ReKi) :: CircSolvConvCrit !< Convergence criterion for circulation solving [-] + REAL(ReKi) :: CircSolvRelaxation !< Relaxation factor for circulation solving [-] + INTEGER(IntKi) :: PrescribedPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] END TYPE FVW_ParameterType ! ======================= ! ========= FVW_OtherStateType ======= @@ -73,6 +78,8 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Tang !< Unit Tangential vector on LL CP [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Norm !< Unit Normal vector on LL CP [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Orth !< Unit Orthogonal vector on LL CP [-] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: dl !< Vector of elementary length along the LL [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Area !< Area of each LL panel [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Gamma_LL !< Circulation on the wing lifting line (COPY of Constraint State) [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vind_LL !< Induced velocity on lifting line control points [m/s] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vtot_LL !< Total velocity on lifting line control points [m/s] @@ -130,9 +137,15 @@ MODULE FVW_Types ! ========= FVW_InputFile ======= TYPE, PUBLIC :: FVW_InputFile INTEGER(IntKi) :: CirculationMethod !< Method to determine the circulation [-] - CHARACTER(1024) :: CirculationFile !< [-] + CHARACTER(1024) :: CirculationFile !< Prescribed circulation file [-] + INTEGER(IntKi) :: CircSolvMaxIter !< Maximum number of iterations for circulation solving [-] + REAL(ReKi) :: CircSolvConvCrit !< Convergence criterion for circulation solving [-] + REAL(ReKi) :: CircSolvRelaxation !< Relaxation factor for circulation solving [-] INTEGER(IntKi) :: IntMethod !< Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1, 7=Corrector/Predictor) [-] LOGICAL :: FreeWake !< Disable roll up, wake convects with wind only (flag) [-] + REAL(ReKi) :: FreeWakeStart !< Time when wake starts convecting (rolling up) [s] + REAL(ReKi) :: FullCirculationStart !< Time when the circulation is full [s] + INTEGER(IntKi) :: PrescribedPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] END TYPE FVW_InputFile ! ======================= ! ========= FVW_InitOutputType ======= @@ -164,7 +177,8 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%nNWMax = SrcParamData%nNWMax DstParamData%nFWMax = SrcParamData%nFWMax DstParamData%IntMethod = SrcParamData%IntMethod - DstParamData%FreeWake = SrcParamData%FreeWake + DstParamData%FreeWakeStart = SrcParamData%FreeWakeStart + DstParamData%FullCirculationStart = SrcParamData%FullCirculationStart DstParamData%CirculationMethod = SrcParamData%CirculationMethod IF (ALLOCATED(SrcParamData%PrescribedCirculation)) THEN i1_l = LBOUND(SrcParamData%PrescribedCirculation,1) @@ -178,6 +192,10 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg END IF DstParamData%PrescribedCirculation = SrcParamData%PrescribedCirculation ENDIF + DstParamData%CircSolvMaxIter = SrcParamData%CircSolvMaxIter + DstParamData%CircSolvConvCrit = SrcParamData%CircSolvConvCrit + DstParamData%CircSolvRelaxation = SrcParamData%CircSolvRelaxation + DstParamData%PrescribedPolar = SrcParamData%PrescribedPolar END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -234,13 +252,18 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! nNWMax Int_BufSz = Int_BufSz + 1 ! nFWMax Int_BufSz = Int_BufSz + 1 ! IntMethod - Int_BufSz = Int_BufSz + 1 ! FreeWake + Re_BufSz = Re_BufSz + 1 ! FreeWakeStart + Re_BufSz = Re_BufSz + 1 ! FullCirculationStart Int_BufSz = Int_BufSz + 1 ! CirculationMethod Int_BufSz = Int_BufSz + 1 ! PrescribedCirculation allocated yes/no IF ( ALLOCATED(InData%PrescribedCirculation) ) THEN Int_BufSz = Int_BufSz + 2*1 ! PrescribedCirculation upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%PrescribedCirculation) ! PrescribedCirculation END IF + Int_BufSz = Int_BufSz + 1 ! CircSolvMaxIter + Re_BufSz = Re_BufSz + 1 ! CircSolvConvCrit + Re_BufSz = Re_BufSz + 1 ! CircSolvRelaxation + Int_BufSz = Int_BufSz + 1 ! PrescribedPolar IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -278,8 +301,10 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%IntMethod Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%FreeWake , IntKiBuf(1), 1) - Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%FreeWakeStart + Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%FullCirculationStart + Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%CirculationMethod Int_Xferred = Int_Xferred + 1 IF ( .NOT. ALLOCATED(InData%PrescribedCirculation) ) THEN @@ -295,6 +320,14 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S IF (SIZE(InData%PrescribedCirculation)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%PrescribedCirculation))-1 ) = PACK(InData%PrescribedCirculation,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%PrescribedCirculation) END IF + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%CircSolvMaxIter + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%CircSolvConvCrit + Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%CircSolvRelaxation + Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%PrescribedPolar + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -343,8 +376,10 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%IntMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 - OutData%FreeWake = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) - Int_Xferred = Int_Xferred + 1 + OutData%FreeWakeStart = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%FullCirculationStart = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 OutData%CirculationMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! PrescribedCirculation not allocated @@ -370,6 +405,14 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%PrescribedCirculation) DEALLOCATE(mask1) END IF + OutData%CircSolvMaxIter = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%CircSolvConvCrit = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%CircSolvRelaxation = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%PrescribedPolar = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -692,6 +735,36 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) END IF DstMiscData%Orth = SrcMiscData%Orth ENDIF +IF (ALLOCATED(SrcMiscData%dl)) THEN + i1_l = LBOUND(SrcMiscData%dl,1) + i1_u = UBOUND(SrcMiscData%dl,1) + i2_l = LBOUND(SrcMiscData%dl,2) + i2_u = UBOUND(SrcMiscData%dl,2) + i3_l = LBOUND(SrcMiscData%dl,3) + i3_u = UBOUND(SrcMiscData%dl,3) + IF (.NOT. ALLOCATED(DstMiscData%dl)) THEN + ALLOCATE(DstMiscData%dl(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%dl.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%dl = SrcMiscData%dl +ENDIF +IF (ALLOCATED(SrcMiscData%Area)) THEN + i1_l = LBOUND(SrcMiscData%Area,1) + i1_u = UBOUND(SrcMiscData%Area,1) + i2_l = LBOUND(SrcMiscData%Area,2) + i2_u = UBOUND(SrcMiscData%Area,2) + IF (.NOT. ALLOCATED(DstMiscData%Area)) THEN + ALLOCATE(DstMiscData%Area(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Area.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Area = SrcMiscData%Area +ENDIF IF (ALLOCATED(SrcMiscData%Gamma_LL)) THEN i1_l = LBOUND(SrcMiscData%Gamma_LL,1) i1_u = UBOUND(SrcMiscData%Gamma_LL,1) @@ -888,6 +961,12 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%Orth)) THEN DEALLOCATE(MiscData%Orth) ENDIF +IF (ALLOCATED(MiscData%dl)) THEN + DEALLOCATE(MiscData%dl) +ENDIF +IF (ALLOCATED(MiscData%Area)) THEN + DEALLOCATE(MiscData%Area) +ENDIF IF (ALLOCATED(MiscData%Gamma_LL)) THEN DEALLOCATE(MiscData%Gamma_LL) ENDIF @@ -1008,6 +1087,16 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*3 ! Orth upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%Orth) ! Orth END IF + Int_BufSz = Int_BufSz + 1 ! dl allocated yes/no + IF ( ALLOCATED(InData%dl) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! dl upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%dl) ! dl + END IF + Int_BufSz = Int_BufSz + 1 ! Area allocated yes/no + IF ( ALLOCATED(InData%Area) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Area upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Area) ! Area + END IF Int_BufSz = Int_BufSz + 1 ! Gamma_LL allocated yes/no IF ( ALLOCATED(InData%Gamma_LL) ) THEN Int_BufSz = Int_BufSz + 2*2 ! Gamma_LL upper/lower bounds for each dimension @@ -1284,6 +1373,41 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%Orth)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Orth))-1 ) = PACK(InData%Orth,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%Orth) END IF + IF ( .NOT. ALLOCATED(InData%dl) ) 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%dl,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dl,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%dl,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dl,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%dl,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dl,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%dl)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%dl))-1 ) = PACK(InData%dl,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%dl) + END IF + IF ( .NOT. ALLOCATED(InData%Area) ) 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%Area,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Area,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Area,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Area,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Area)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Area))-1 ) = PACK(InData%Area,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Area) + END IF IF ( .NOT. ALLOCATED(InData%Gamma_LL) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -1818,6 +1942,61 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%Orth) DEALLOCATE(mask3) END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! dl 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%dl)) DEALLOCATE(OutData%dl) + ALLOCATE(OutData%dl(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%dl.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%dl)>0) OutData%dl = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%dl))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%dl) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Area 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 + IF (ALLOCATED(OutData%Area)) DEALLOCATE(OutData%Area) + ALLOCATE(OutData%Area(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Area.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%Area)>0) OutData%Area = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Area))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Area) + DEALLOCATE(mask2) + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Gamma_LL not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -3942,8 +4121,14 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS ErrMsg = "" DstInputFileData%CirculationMethod = SrcInputFileData%CirculationMethod DstInputFileData%CirculationFile = SrcInputFileData%CirculationFile + DstInputFileData%CircSolvMaxIter = SrcInputFileData%CircSolvMaxIter + DstInputFileData%CircSolvConvCrit = SrcInputFileData%CircSolvConvCrit + DstInputFileData%CircSolvRelaxation = SrcInputFileData%CircSolvRelaxation DstInputFileData%IntMethod = SrcInputFileData%IntMethod DstInputFileData%FreeWake = SrcInputFileData%FreeWake + DstInputFileData%FreeWakeStart = SrcInputFileData%FreeWakeStart + DstInputFileData%FullCirculationStart = SrcInputFileData%FullCirculationStart + DstInputFileData%PrescribedPolar = SrcInputFileData%PrescribedPolar END SUBROUTINE FVW_CopyInputFile SUBROUTINE FVW_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) @@ -3994,8 +4179,14 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = 0 Int_BufSz = Int_BufSz + 1 ! CirculationMethod Int_BufSz = Int_BufSz + 1*LEN(InData%CirculationFile) ! CirculationFile + Int_BufSz = Int_BufSz + 1 ! CircSolvMaxIter + Re_BufSz = Re_BufSz + 1 ! CircSolvConvCrit + Re_BufSz = Re_BufSz + 1 ! CircSolvRelaxation Int_BufSz = Int_BufSz + 1 ! IntMethod Int_BufSz = Int_BufSz + 1 ! FreeWake + Re_BufSz = Re_BufSz + 1 ! FreeWakeStart + Re_BufSz = Re_BufSz + 1 ! FullCirculationStart + Int_BufSz = Int_BufSz + 1 ! PrescribedPolar IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4029,10 +4220,22 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs IntKiBuf(Int_Xferred) = ICHAR(InData%CirculationFile(I:I), IntKi) Int_Xferred = Int_Xferred + 1 END DO ! I + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%CircSolvMaxIter + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%CircSolvConvCrit + Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%CircSolvRelaxation + Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%IntMethod Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%FreeWake , IntKiBuf(1), 1) Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%FreeWakeStart + Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%FullCirculationStart + Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%PrescribedPolar + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackInputFile SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4073,10 +4276,22 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er OutData%CirculationFile(I:I) = CHAR(IntKiBuf(Int_Xferred)) Int_Xferred = Int_Xferred + 1 END DO ! I + OutData%CircSolvMaxIter = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%CircSolvConvCrit = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%CircSolvRelaxation = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 OutData%IntMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%FreeWake = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) Int_Xferred = Int_Xferred + 1 + OutData%FreeWakeStart = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%FullCirculationStart = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%PrescribedPolar = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackInputFile SUBROUTINE FVW_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn14/src/Registry-FVW.txt b/modules/aerodyn14/src/Registry-FVW.txt index 3886474114..88279144b8 100644 --- a/modules/aerodyn14/src/Registry-FVW.txt +++ b/modules/aerodyn14/src/Registry-FVW.txt @@ -15,9 +15,14 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi nNWMax - - - "Maximum number of nw panels" - typedef ^ ^ IntKi nFWMax - - - "Maximum number of fw panels" - typedef ^ ^ IntKi IntMethod - - - "Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1)" - -typedef ^ ^ LOGICAL FreeWake - - - "Disable roll up, wake convects with wind only (flag)" - +typedef ^ ^ ReKi FreeWakeStart - - - "Time when wake starts convecting (rolling up)" s +typedef ^ ^ ReKi FullCirculationStart - - - "Time when the circulation is full" s typedef ^ ^ IntKi CirculationMethod - - - "Method to determine the circulation" - typedef ^ ^ ReKi PrescribedCirculation : - - "Prescribed circulation on all lifting lines" "m/s" +typedef ^ ^ IntKi CircSolvMaxIter - - - "Maximum number of iterations for circulation solving" - +typedef ^ ^ ReKi CircSolvConvCrit - - - "Convergence criterion for circulation solving" - +typedef ^ ^ ReKi CircSolvRelaxation - - - "Relaxation factor for circulation solving" - +typedef ^ ^ IntKi PrescribedPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - # ....... OtherStateType ............ # FVW_OtherStateType @@ -39,6 +44,8 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi Tang ::: - - "Unit Tangential vector on LL CP" - typedef ^ ^ ReKi Norm ::: - - "Unit Normal vector on LL CP " - typedef ^ ^ ReKi Orth ::: - - "Unit Orthogonal vector on LL CP" - +typedef ^ ^ ReKi dl ::: - - "Vector of elementary length along the LL" - +typedef ^ ^ ReKi Area :: - - "Area of each LL panel" - typedef ^ ^ Reki Gamma_LL :: - - "Circulation on the wing lifting line (COPY of Constraint State)" - typedef ^ ^ ReKi Vind_LL ::: - - "Induced velocity on lifting line control points" m/s typedef ^ ^ ReKi Vtot_LL ::: - - "Total velocity on lifting line control points" m/s @@ -91,9 +98,16 @@ typedef ^ ^ IntKi #.......... InitInputType ...... # FVW_InputFile typedef FVW/FVW FVW_InputFile IntKi CirculationMethod - - - "Method to determine the circulation" - -typedef ^ ^ CHARACTER(1024) CirculationFile - - - "" - +typedef ^ ^ CHARACTER(1024) CirculationFile - - - "Prescribed circulation file" - +typedef ^ ^ IntKi CircSolvMaxIter - - - "Maximum number of iterations for circulation solving" - +typedef ^ ^ ReKi CircSolvConvCrit - - - "Convergence criterion for circulation solving" - +typedef ^ ^ ReKi CircSolvRelaxation - - - "Relaxation factor for circulation solving" - + typedef ^ ^ IntKi IntMethod - - - "Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1, 7=Corrector/Predictor)" - typedef ^ ^ LOGICAL FreeWake - - - "Disable roll up, wake convects with wind only (flag)" - +typedef ^ ^ ReKi FreeWakeStart - - - "Time when wake starts convecting (rolling up)" s +typedef ^ ^ ReKi FullCirculationStart - - - "Time when the circulation is full" s +typedef ^ ^ IntKi PrescribedPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - #.......... InitOutputType ...... # FVW_InitOutputType From dd9b61f3303db5f8b5c9fd392026aba9ddc55344 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 12:44:36 -0700 Subject: [PATCH 015/190] FVW: starting circulation solving and unpacking of ui --- modules/aerodyn14/src/FVW.f90 | 109 ++++++++++++++------ modules/aerodyn14/src/FVW_Subs.f90 | 51 +++++++++- modules/aerodyn14/src/FVW_Wings.f90 | 153 ++++++++++++++++++++++++++-- 3 files changed, 271 insertions(+), 42 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 6df64e7f19..7694afc72f 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -460,7 +460,7 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP + integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs real(ReKi), dimension(3) :: U_mean integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points @@ -473,45 +473,77 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt call AllocAry( dxdt%r_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat, ErrMsg ); dxdt%r_NW= -999999_ReKi; call AllocAry( dxdt%r_FW , 3 , 2 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; - if (p%FreeWake) then - print*,'TODO free wake convection' - STOP - else + if (t> p%FreeWakeStart) then +! print*,' Free wake convection' +! ! --- Packing all vortex elements into a list of segments +! call PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, nSeg, nSegP) +! print*,'Number of segments',nSeg, 'Number of points',nSegP +! +! ! --- Computing induced velocity +! ! TODO for now point by point.. +! allocate(SegSmooth(1:nSeg)); +! SegSmooth=10 +! SmoothModel=idSegSmoothLambOseen +! ! --- On NW +! allocate(Uind(1:3,1)) +! allocate(CPs (1:3,1)) +! CPs=0 +! Uind=0 +! do iW=1,p%nWings +! do iAge=1,m%nNW+1 +! do iSpan=1,p%nSpan+1 +! Uind(1:3,1)=0.0_ReKi +! CPs(1:3,1) = x%r_NW(1:3,iSpan,iAge,iW) +! +! CALL ui_seg(1, 1, 1, CPs, & +! 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, & +! SmoothModel, SegSmooth, Uind) +! ! print*,'Uind',Uind +! m%Vind_NW(1:3,iSpan,iAge,iW) = Uind(1:3,1) +! enddo +! enddo +! enddo +! deallocate(Uind) +! deallocate(CPs) +! print*,'1 ', m%Vind_NW(1:3,1,1,1) +! print*,'1last', m%Vind_NW(1:3,p%nSpan+1,1,1) +! print*,'llast', m%Vind_NW(1:3,p%nSpan+1,m%nNW,p%nWings) + ! --- Packing all vortex elements into a list of segments call PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, nSeg, nSegP) print*,'Number of segments',nSeg, 'Number of points',nSegP - +! ! --- Computing induced velocity - ! TODO for now point by point.. allocate(SegSmooth(1:nSeg)); SegSmooth=10 - allocate(Uind(1:3,1)) - allocate(CPs (1:3,1)) - CPs=0 - Uind=0 SmoothModel=idSegSmoothLambOseen - ! --- On NW - do iW=1,p%nWings - do iAge=1,m%nNW+1 - do iSpan=1,p%nSpan+1 - Uind(1:3,1)=0.0_ReKi - CPs(1:3,1) = x%r_NW(1:3,iSpan,iAge,iW) - - CALL ui_seg(CPs, 1, 1, 1, & - SegPoints, SegConnct, SegGamma, 1, nSeg, nSeg, nSegP, & - SmoothModel, SegSmooth, Uind) -! print*,'Uind',Uind - m%Vind_NW(1:3,iSpan,iAge,iW) = Uind(1:3,1) - enddo - enddo - enddo + m%Vind_NW = -9999 + nCPs=nSegP + allocate(CPs (1:3,1:nCPs)) + allocate(Uind(1:3,1:nCPs)) + Uind=0.0_ReKi !< important due to side effects of ui_seg + ! --- + call PackConvectingPoints(p, m, x, z, CPs, nCPs) + print*,'Number of points packed for Convection:',nCPs, nSegP + CALL ui_seg( 1, nCPs, nCPs, CPs, & + 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, & + SmoothModel, SegSmooth, Uind) + print*,'1 ',Uind(1:3,1) + call UnPackInducedVelocity(p, m, x, z, Uind) + + print*,'1 ', m%Vind_NW(1:3,1,1,1) + print*,'1last', m%Vind_NW(1:3,p%nSpan+1,1,1) + print*,'llast', m%Vind_NW(1:3,p%nSpan+1,m%nNW,p%nWings) + + deallocate(Uind) + deallocate(CPs) + deallocate(SegConnct) deallocate(SegGamma) deallocate(SegPoints) deallocate(SegSmooth) - deallocate(Uind) - deallocate(CPs) + U_mean(1:3)=0 do iW=1,p%nWings; do iAge=1,m%nNW+1; do iSpan=1,p%nSpan+1; @@ -519,8 +551,27 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt enddo; enddo; enddo U_mean(1:3) = U_mean(1:3)/ ((m%nNW+1)*(p%nSpan+1)*p%nWings) print*,'Mean convection velocity NW: ',U_mean(1:3) + U_mean(1:3)=0 + do iW=1,p%nWings; do iAge=1,m%nNW+1; do iSpan=1,p%nSpan+1; + U_mean(1:3)= U_mean(1:3)+ m%Vwnd_NW(1:3, iSpan, iAge, iW) + enddo; enddo; enddo + U_mean(1:3) = U_mean(1:3)/ ((m%nNW+1)*(p%nSpan+1)*p%nWings) + print*,'Mean convection velocity NW: ',U_mean(1:3) + U_mean(1:3)=0 + do iW=1,p%nWings; do iAge=1,m%nFW+1; do iSpan=1,2; + U_mean(1:3)= U_mean(1:3)+ m%Vwnd_FW(1:3, iSpan, iAge, iW) + enddo; enddo; enddo + U_mean(1:3) = U_mean(1:3)/ ((m%nFW+1)*(2)*p%nWings) + print*,'Mean convection velocity FW: ',U_mean(1:3) + ! --- Vortex points are convected with the free stream and induced velocity + dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + m%Vind_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + !dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) ! TODO TODO +! STOP +! dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + else + U_mean(1:3)=0 do iW=1,p%nWings; do iAge=1,m%nNW+1; do iSpan=1,p%nSpan+1; U_mean(1:3)= U_mean(1:3)+ m%Vwnd_NW(1:3, iSpan, iAge, iW) @@ -538,8 +589,6 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! --- Vortex points are convected with the free stream dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) - - dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + m%Vind_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) endif end subroutine FVW_CalcContStateDeriv diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index bf9b34a787..8e95a0cc65 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -8,9 +8,13 @@ module FVW_SUBS ! --- Module parameters ! Circulation solving methods - integer(IntKi), parameter :: idCircNoFlowThrough = 0 - integer(IntKi), parameter :: idCircPolarData = 1 + integer(IntKi), parameter :: idCircPolarData = 0 + integer(IntKi), parameter :: idCircNoFlowThrough = 1 integer(IntKi), parameter :: idCircPrescribed = 2 + ! Polar data + integer(IntKi), parameter :: idPolarAeroDyn = 0 + integer(IntKi), parameter :: idPolar2PiAlpha = 1 + integer(IntKi), parameter :: idPolar2PiSinAlpha = 2 ! Integration method integer(IntKi), parameter :: idRK4 = 1 integer(IntKi), parameter :: idAB4 = 2 @@ -276,6 +280,49 @@ subroutine DistributeRequestedWind(V_wind, x, p, m, ErrStat, ErrMsg ) end subroutine DistributeRequestedWind +!> Distribute the induced velocity to the proper location +subroutine UnPackInducedVelocity(p, m, x, z, Uind) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + type(FVW_ContinuousStateType), intent(in ) :: x !< States + type(FVW_ConstraintStateType), intent(in ) :: z !< Initial misc/optimization variables + real(ReKi), dimension(:,:) , intent(in ) :: Uind !< Induced velocity + ! Local + integer(IntKi) :: iW, iHeadP + iHeadP=1 + do iW=1,p%nWings + CALL VecToLattice(Uind, m%Vind_NW(:,:,1:m%nNW+1,iW), iHeadP) + enddo + if ((iHeadP-1)/=size(Uind,2)) then + print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 + STOP + endif +end subroutine + + +!> Distribute the induced velocity to the convecting points +subroutine PackConvectingPoints(p, m, x, z, Points, nPoints) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_ContinuousStateType), intent(in ) :: x !< States + type(FVW_ConstraintStateType), intent(in ) :: z !< Initial misc/optimization variables + real(ReKi), dimension(:,:) , intent(inout) :: Points !< Points packed + integer(IntKi), intent( out) :: nPoints !< Number of points packed + ! Local + integer(IntKi) :: iW, iHeadP + + ! --- Compute number of convecting points + iHeadP=1 + do iW=1,p%nWings + CALL LatticeToPoints(x%r_NW(1:3,:,1:m%nNW+1,iW) , Points, iHeadP) + enddo + if ((iHeadP-1)/=size(Points,2)) then + print*,'PackConvectingPoints: Number of points wrongly estimated',size(Points,2), iHeadP-1 + STOP + endif + nPoints=iHeadP-1 +end subroutine + subroutine PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, nSeg, nSegP) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index ff443cca11..d692fdb012 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -134,8 +134,9 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) m%Norm(1:3,iSpan,iW) = m%Norm(1:3,iSpan,iW)/norm2(m%Norm(1:3,iSpan,iW)) m%Tang(1:3,iSpan,iW) = (DP1)/norm2(DP1) ! tangential unit vector, along chord ! m%Tscoord(1:3,iSpan) = (DP3)/norm2(DP3) ! tangential unit vector, along span, follows ref line - ! m%dl(1:3,iSpan) = DP2 + m%dl (1:3,iSpan,iW) = DP2 m%Orth(1:3,iSpan,iW) = cross_product(m%Norm(1:3,iSpan,iW),m%Tang(1:3,iSpan,iW)) ! orthogonal vector to N and T + m%Area(iSpan, iW) = norm2(cross_product(DP1,DP3)); end do enddo @@ -212,20 +213,14 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS if (p%CirculationMethod==idCircPrescribed) then print*,'>>>Prescribing circulation' do iW = 1, p%nWings !Loop over lifting lines - if (t<5) then - ! Slow start - print*,'Slow start' - Gamma_LL(1:p%nSpan,iW) = (t/5)*p%PrescribedCirculation(1:p%nSpan) - else - Gamma_LL(1:p%nSpan,iW) = p%PrescribedCirculation(1:p%nSpan) - endif + Gamma_LL(1:p%nSpan,iW) = p%PrescribedCirculation(1:p%nSpan) enddo else if (p%CirculationMethod==idCircPolarData) then ! --- Solve for circulation using polar data ! TODO - print*,'Circulation method nor implemented', p%CirculationMethod - STOP + print*,'>>>Circulation solving with polar data' + CALL Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg) else if (p%CirculationMethod==idCircNoFlowThrough) then ! --- Solve for circulation using the no-flow through condition @@ -237,6 +232,144 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS STOP endif + if (t + subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines + real(ReKi), dimension(:,:), intent(in ) :: Gamma_LL_prev !< Previous/Guessed circulation + type(FVW_InputType), intent(in ) :: u !< Parameters + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local + real(ReKi), dimension(:,:), allocatable :: DGamma !< + real(ReKi), dimension(:,:), allocatable :: GammaLastIter !< + logical :: bConverged !< + integer(IntKi) :: iIter !< iteration step number + real(ReKi) :: MeanGamma + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + print*,'Parameters for circulation solv: ',p%CircSolvConvCrit ,p%CircSolvRelaxation ,p%CircSolvMaxIter + + allocate(DGamma (1:p%nSPan,1:p%nWings)) + allocate(GammaLastIter(1:p%nSPan,1:p%nWings)) + ! + GammaLastIter = Gamma_LL_prev + + ! Building Vrel_cst This part do not change wihtin the iteration loop + ! Remember: uiu0 contains U0 and vorticity (free and prescribed) + !do icp=1,SW%ncp_ll_tot + ! TODO + ! SW%Vrel_ll_cst(1:3,icp) = SW%U_uiu0(1:3,icp) - SW%U_body(1:3,icp) + SW%U_solv(1:3,icp) + !end do + + ! --- Convergence loop until near wake gives induction coherent with circulation + bConverged=.false. + iIter=0 + do while (.not.(bConverged) .and. iIter Compute circulation based on polar data + !! Uses m%Vtot_ll to compute Gamma_ll + subroutine CirculationFromPolarData(Gamma_LL, p, m) + real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + ! Local + integer(IntKi) :: iW, iCP !< Index on wings and spanwise control points + real(ReKi), dimension(3) :: N, Tc !< Normal and Tangent vector + real(ReKi), dimension(3) :: Vrel, Vrel_orth, Vjouk, Vjouk_orth + real(ReKi) :: Vrel_orth_norm, Vjouk_orth_norm + real(ReKi) :: alpha, Re, Cl + + do iW=1,p%nWings + do icp=1,p%nSpan + ! Aliases to shorten notations + N = m%Norm(1:3, icp, iW) + Tc = m%Tang(1:3, icp, iW) + Vrel = m%Vtot_LL(1:3,icp,iW) + ! "Orth": cross sectional plane of the lifting line + Vrel_orth(1:3) = dot_product(Vrel,N)*N + dot_product(Vrel,Tc)*Tc + Vrel_orth_norm = norm2(Vrel_orth(1:3)) + Vjouk(1:3) = cross_product(Vrel,m%dl(1:3,icp,iW)) + Vjouk_orth(1:3) = dot_product(Vjouk,N)*N + dot_product(Vjouk,Tc)*Tc + Vjouk_orth_norm = norm2(Vjouk_orth) + + alpha = atan2(dot_product(Vrel,N) , dot_product(Vrel,Tc) ) ! [rad] + !Re = LL%Vrel_orth_norm(icp)*LL%chord(icp)/KinVisc/(1.E6_MK) ! TODO TODO TODO KinVisc + + if (p%PrescribedPolar==idPolarAeroDyn) then + print*,'TODO TODO TODO Get Cl, Cd, Cm from alpha, Re and AirfoilInfo' + STOP + else if (p%PrescribedPolar==idPolar2PiAlpha) then + Cl=TwoPi*alpha + else if (p%PrescribedPolar==idPolar2PiSinAlpha) then + Cl=TwoPi*sin(alpha) + else + print*,'Unknown PrescribedPolar value' + STOP + endif + ! Simple method: + ! Gamma_LL=(0.5 * Cl * Vrel_orth_norm*chord) + ! VanGarrel's method: + Gamma_LL(icp,iW) =(0.5_ReKi * Cl * Vrel_orth_norm**2*m%Area(icp,iW)/(Vjouk_orth_norm)) + enddo + enddo + end subroutine CirculationFromPolarData + + + end module FVW_Wings From 3e055521d9d8dab61d3145b448f7948081581396 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 20 Nov 2019 12:45:03 -0700 Subject: [PATCH 016/190] FVW: add the WingsMesh passing from AD15 --- modules/aerodyn/src/AeroDyn.f90 | 252 +++++++++++++-------- modules/aerodyn/src/FVW.f90 | 6 +- modules/aerodyn/src/FVW_Registry.txt | 19 +- modules/aerodyn/src/FVW_Types.f90 | 325 ++++++++++++++++++++++++++- modules/aerodyn/src/FVW_Wings.f90 | 6 +- 5 files changed, 497 insertions(+), 111 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 63827ba051..5b43fe6a92 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -27,7 +27,7 @@ module AeroDyn use BEMT use AirfoilInfo use NWTC_LAPACK -! use FVW + use FVW implicit none @@ -1178,7 +1178,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call SetOutputsFromBEMT(p, m, y ) - + !!! REAL(ReKi) :: Vind_FVW(3) !!! !!! WakeCalc = p%UseFVW ! WakeCalc is used to easily switch the Freewake on and off in this routine @@ -1268,28 +1268,6 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) end subroutine AD_CalcOutput -!!!!---------------------------------------------------------------------------------------------------------------------------------- -!!! -!!!!> Wrapper to set inputs needed by FVW from AeroDyn -!!!SUBROUTINE AD14_to_FVW_u(u,p,u_FVW,ErrStat,ErrMess) -!!! TYPE (AD14_InputType ),INTENT(IN ):: u ! Inputs at Time ! KS changed from IN to INOUT -!!! TYPE (AD14_ParameterType),INTENT(IN ):: p ! Parameters -!!! TYPE (FVW_InputType ),INTENT(INOUT):: u_FVW ! Inputs at Time ! KS changed from IN to INOUT -!!! INTEGER (IntKi ),INTENT(OUT ):: ErrStat ! Error status of the operation -!!! CHARACTER(* ),INTENT(OUT ):: ErrMess ! Error message if ErrStat / = ErrID_None -!!! INTEGER(IntKi) :: iB ! Index for blades -!!! -!!! !CALL AD14AeroConf_CopyInput( u%TurbineComponents, u_FVW%FVWTurbineComponents, MESH_NEWCOPY, ErrStat, ErrMess ) -!!! IF (ErrStat >= AbortErrLev) RETURN -!!! ! NOTE: this isn't really being used as a full mesh, so we only set a few things. -!!! ! also, if we do a direct copy, we end up with fatal errors at exit since the -!!! ! sibling/cousin status will be incorrect -!!! DO iB = 1,p%NumBl -!!! u_FVW%WingsMesh(iB)%Position = u%InputMarkers(iB)%Position -!!! u_FVW%WingsMesh(iB)%Orientation = u%InputMarkers(iB)%Orientation -!!! u_FVW%WingsMesh(iB)%TranslationVel = u%InputMarkers(iB)%TranslationVel -!!! ENDDO -!!!ENDSUBROUTINE AD14_to_FVW_u !---------------------------------------------------------------------------------------------------------------------------------- !> Tight coupling routine for solving for the residual of the constraint state equations subroutine AD_CalcConstrStateResidual( Time, u, p, x, xd, z, OtherState, m, z_residual, ErrStat, ErrMsg ) @@ -1368,8 +1346,13 @@ subroutine SetInputs(p, u, m, indx, errStat, errMsg) ! This needs to extract the inputs from the AD data types (mesh) and massage them for the BEMT module call SetInputsForBEMT(p, u, m, indx, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - - + + if (p%WakeMod == WakeMod_FVW) then + ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module + call SetInputsForFVW(p, u, m, indx, errStat2, errMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + endif + end subroutine SetInputs !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine sets m%BEMT_u(indx). @@ -1526,6 +1509,38 @@ subroutine SetInputsForBEMT(p, u, m, indx, errStat, errMsg) end do !k=blades end subroutine SetInputsForBEMT + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This subroutine sets m%FVW_u(indx). +subroutine SetInputsForFVW(p, u, m, indx, errStat, errMsg) + + type(AD_ParameterType), intent(in ) :: p !< AD parameters + type(AD_InputType), intent(in ) :: u !< AD Inputs at Time + type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables + integer, intent(in ) :: indx !< index into m%FVW_u array; must be 1 or 2 (but not checked here) + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + integer(intKi) :: k ! loop counter for blades + integer(intKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SetInputsForFVW' + + + ! Rather than use a meshcopy, we will just copy what we need to the WingsMesh + ! NOTE: MeshCopy requires the source mesh to be INOUT intent + do k=1,p%NumBlades + if ( u%BladeMotion(k)%nNodes /= m%FVW_u(indx)%WingsMesh(k)%nNodes ) then + ErrStat = ErrID_Fatal + ErrMsg = RoutineName//": WingsMesh contains different number of nodes than the BladeMotion mesh" + return + endif + m%FVW_u(indx)%WingsMesh(k)%TranslationDisp = u%BladeMotion(k)%TranslationDisp + m%FVW_u(indx)%WingsMesh(k)%Orientation = u%BladeMotion(k)%Orientation + m%FVW_u(indx)%WingsMesh(k)%TranslationVel = u%BladeMotion(k)%TranslationVel + enddo +!FIXME: do we want the hub orientation and rotation? Maybe motion also? u%HubMotion%Orientation(:,:,1) +end subroutine SetInputsForFVW !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine converts outputs from BEMT (stored in m%BEMT_y) into values on the AeroDyn BladeLoad output mesh. subroutine SetOutputsFromBEMT(p, m, y ) @@ -1945,6 +1960,7 @@ SUBROUTINE Init_BEMTmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, if (EqualRealNos(InitInp%zHub(k),0.0_ReKi) ) & call SetErrStat( ErrID_Fatal, "zHub for blade "//trim(num2lstr(k))//" is zero.", ErrStat, ErrMsg, RoutineName) + ! zLocal is the istance along blade curve -- NOTE: this is an approximation. InitInp%zLocal(1,k) = InitInp%zHub(k) + TwoNorm( u_AD%BladeMotion(k)%Position(:,1) - u_AD%BladeRootMotion(k)%Position(:,1) ) do j=2,p%NumBlNds InitInp%zLocal(j,k) = InitInp%zLocal(j-1,k) + TwoNorm( u_AD%BladeMotion(k)%Position(:,j) - u_AD%BladeMotion(k)%Position(:,j-1) ) @@ -2015,7 +2031,7 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m !.................................................................................................................................. type(AD_InputFile), intent(in ) :: InputFileData !< All the data in the AeroDyn input file - type(AD_InputType), intent(in ) :: u_AD !< AD inputs - used for input mesh node positions + type(AD_InputType), intent(inout) :: u_AD !< AD inputs - used for input mesh node positions (intent out for meshcopy) type(FVW_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined type(AD_ParameterType), intent(inout) :: p !< Parameters ! intent out b/c we set the FVW parameters here type(FVW_ContinuousStateType), intent( out) :: x !< Initial continuous states @@ -2040,10 +2056,10 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m type(FVW_InitOutputType) :: InitOut ! Output for initialization routine integer(intKi) :: j ! node index - integer(intKi) :: k ! blade index -! real(ReKi) :: tmp(3), tmp_sz_y, tmp_sz -! real(ReKi) :: y_hat_disk(3) -! real(ReKi) :: z_hat_disk(3) + integer(intKi) :: IB ! blade index + real(ReKi) :: tmp(3), tmp_sz_y, tmp_sz + real(ReKi) :: y_hat_disk(3) + real(ReKi) :: z_hat_disk(3) integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'Init_FVWmodule' @@ -2054,80 +2070,136 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m ErrMsg = "" +print*,'===================== Setup before call to FVW_Init =====================' ! set initialization data here: InitInp%FVWFileName = InputFileData%FVWFileName InitInp%numBlades = p%numBlades InitInp%numBladeNodes = p%numBlNds ! --- TODO TODO TODO ANDY +!FIXME: check the following now that we are in AD15. ! Change this so that it would match AD 15 mesh ! NOTE: This mesh does not include the azimuthal differences between blades! ! It's just the spanwise location. ! Also, it is off compared to the initial position of the blade ! Also, it's centered on the hub, but that's fine for now call AllocAry(InitInp%Chord, InitInp%numBladeNodes,InitInp%numBlades,'chord', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -! call AllocAry(InitInp%AFindx,InitInp%numBladeNodes,InitInp%numBlades,'AFindx',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -! call AllocAry(InitInp%zHub, InitInp%numBlades,'zHub', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -! call AllocAry(InitInp%zLocal,InitInp%numBladeNodes,InitInp%numBlades,'zLocal',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -! call AllocAry(InitInp%rLocal,InitInp%numBladeNodes,InitInp%numBlades,'rLocal',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -! call AllocAry(InitInp%zTip, InitInp%numBlades,'zTip', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -!!! IF (.NOT. ALLOCATED( InitInp%Chord)) ALLOCATE ( InitInp%Chord( p%Element%NElm )) + call AllocAry(InitInp%AFindx,InitInp%numBladeNodes,InitInp%numBlades,'AFindx',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry(InitInp%zHub, InitInp%numBlades,'zHub', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry(InitInp%zLocal,InitInp%numBladeNodes,InitInp%numBlades,'zLocal',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry(InitInp%rLocal,InitInp%numBladeNodes,InitInp%numBlades,'rLocal',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry(InitInp%zTip, InitInp%numBlades,'zTip', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + !!! IF (.NOT. ALLOCATED( InitInp%RElm )) ALLOCATE ( InitInp%RElm( p%Element%NElm )) !!! InitInp%RElm = p%Element%RElm -!!! InitInp%Chord = p%Blade%C -!!! ALLOCATE( InitInp%WingsMesh(p%NumBlades), STAT = ErrStatLcl ) -!!! IF (ErrStatLcl /= 0) THEN -!!! CALL SetErrStat ( ErrID_Fatal, 'Could not allocate InitInp%WingsMesh (meshes)', ErrStat,ErrMess,RoutineName ) -!!! RETURN -!!! END IF -!!! DO IB = 1, p%NumBlades -!!! CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & -!!! ,DestMesh = InitInp%WingsMesh(IB) & -!!! ,CtrlCode = MESH_COUSIN & -!!! ,Orientation = .TRUE. & -!!! ,TranslationVel = .TRUE. & -!!! ,RotationVel = .TRUE. & -!!! ,ErrStat = ErrStatLcl & -!!! ,ErrMess = ErrMessLcl ) -!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) -!!! IF (ErrStat >= AbortErrLev) RETURN -!!! ENDDO -!!! ! ---- END TODO -!!! -!!! call FVW_Init( InitInp%FVW, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, Interval, InitOut%FVW, ErrStatLcl, ErrMessLcl ) -!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) -!!! -!!! ! If anything is passed back in InitOut%FVW, deal with it here... -!!! -!!! -!!! -!!! ! TODO ANDY -!!! !FIXME This really probably should be done inside of FVW_Init instead of here. -!!! ! Not entirely sure how to pass the u%InputMarkers in though. -!!! ALLOCATE( u%FVW%WingsMesh(p%NumBlades), STAT = ErrStatLcl ) -!!! IF (ErrStatLcl /= 0) THEN -!!! CALL SetErrStat ( ErrID_Fatal, 'Could not allocate u%FVW%InputMarkers (meshes)', ErrStat,ErrMess,RoutineName ) -!!! RETURN -!!! END IF -!!! DO IB = 1, p%NumBlades -!!! CALL MeshCopy ( SrcMesh = u%InputMarkers(IB) & -!!! ,DestMesh = u%FVW%WingsMesh(IB) & -!!! ,CtrlCode = MESH_COUSIN & -!!! ,Orientation = .TRUE. & -!!! ,TranslationVel = .TRUE. & -!!! ,RotationVel = .TRUE. & -!!! ,ErrStat = ErrStatLcl & -!!! ,ErrMess = ErrMessLcl ) -!!! CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) -!!! IF (ErrStat >= AbortErrLev) RETURN -!!! ENDDO + if ( ErrStat >= AbortErrLev ) then + call Cleanup() + return + end if - do k=1,p%numBlades - do j=1,p%NumBlNds - InitInp%chord (j,k) = InputFileData%BladeProps(k)%BlChord(j) -! InitInp%AFindx(j,k) = InputFileData%BladeProps(k)%BlAFID(j) - end do - end do + + ! Hub + do IB=1,p%numBlades + InitInp%zHub(IB) = TwoNorm( u_AD%BladeRootMotion(IB)%Position(:,1) - u_AD%HubMotion%Position(:,1) ) + if (EqualRealNos(InitInp%zHub(IB),0.0_ReKi) ) & + call SetErrStat( ErrID_Fatal, "zHub for blade "//trim(num2lstr(IB))//" is zero.", ErrStat, ErrMsg, RoutineName) + enddo + if (ErrStat >= AbortErrLev) then + call CleanUp() + RETURN + endif + + ! Distance along blade curve -- NOTE: this is an approximation. + do IB=1,p%numBlades + InitInp%zLocal(1,IB) = InitInp%zHub(IB) + TwoNorm( u_AD%BladeMotion(IB)%Position(:,1) - u_AD%BladeRootMotion(IB)%Position(:,1) ) + do j=2,p%NumBlNds + InitInp%zLocal(j,IB) = InitInp%zLocal(j-1,IB) + TwoNorm( u_AD%BladeMotion(IB)%Position(:,j) - u_AD%BladeMotion(IB)%Position(:,j-1) ) + end do !j=nodes + end do !IB=blades + + ! Blade tip curve distance + do IB=1,p%numBlades + InitInp%zTip(IB) = InitInp%zLocal(p%NumBlNds,IB) + end do !IB=blades + + ! Distance from blade to hub axis (includes hub radius) + y_hat_disk = u_AD%HubMotion%Orientation(2,:,1) + z_hat_disk = u_AD%HubMotion%Orientation(3,:,1) + do IB=1,p%numBlades + do j=1,p%NumBlNds + ! displaced position of the jth node in the kth blade relative to the hub: + tmp = u_AD%BladeMotion(IB)%Position(:,j) - u_AD%HubMotion%Position(:,1) + ! local radius (normalized distance from rotor centerline) + tmp_sz_y = dot_product( tmp, y_hat_disk )**2 + tmp_sz = dot_product( tmp, z_hat_disk )**2 + InitInp%rLocal(j,IB) = sqrt( tmp_sz + tmp_sz_y ) + end do !j=nodes + end do !IB=blades + + + ! Copy over chord information + do IB=1,p%numBlades + do j=1,p%NumBlNds + InitInp%Chord (j,IB) = InputFileData%BladeProps(IB)%BlChord(j) + InitInp%AFindx(j,IB) = InputFileData%BladeProps(IB)%BlAFID(j) + end do + end do + +!FIXME: do we need this mesh at all? + ALLOCATE( InitInp%WingsMesh(p%NumBlades), STAT = ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat ( ErrID_Fatal, 'Could not allocate InitInp%WingsMesh (meshes)', ErrStat,ErrMsg,RoutineName ) + RETURN + END IF + DO IB = 1, p%NumBlades + CALL MeshCopy ( SrcMesh = u_AD%BladeMotion(IB) & + ,DestMesh = InitInp%WingsMesh(IB) & + ,CtrlCode = MESH_COUSIN & + ,Orientation = .TRUE. & + ,TranslationVel = .TRUE. & + ,RotationVel = .TRUE. & + ,ErrStat = ErrStat2 & + ,ErrMess = ErrMsg2 ) + CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) + IF (ErrStat >= AbortErrLev) RETURN + ENDDO + ! ---- END TODO + + + +!FIXME: Should we be passing any AFinfo? Is that needed in FVW for anything? +! call FVW_Init( u_AD%BladeMotion, InitInp, u, p%FVW, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat2, ErrMsg2 ) + call FVW_Init( InitInp, u, p%FVW, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat2, ErrMsg2 ) + CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + ! TODO ANDY + !FIXME This really probably should be done inside of FVW_Init instead of here. + ! Not entirely sure how to pass the u%InputMarkers in though. + ALLOCATE( u%WingsMesh(p%NumBlades), STAT = ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat ( ErrID_Fatal, 'Could not allocate u%InputMarkers (meshes)', ErrStat,ErrMsg,RoutineName ) + RETURN + END IF + DO IB = 1, p%NumBlades + CALL MeshCopy ( SrcMesh = u_AD%BladeMotion(IB) & + ,DestMesh = u%WingsMesh(IB) & + ,CtrlCode = MESH_COUSIN & + ,Orientation = .TRUE. & + ,TranslationVel = .TRUE. & + ,RotationVel = .TRUE. & + ,ErrStat = ErrStat2 & + ,ErrMess = ErrMsg2 ) + CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) + IF (ErrStat >= AbortErrLev) RETURN + + u%WingsMesh(IB)%RemapFlag = .TRUE. + ENDDO + + + ! If anything is passed back in InitOut%FVW, deal with it here... + + if (.not. equalRealNos(Interval, p%DT) ) & + call SetErrStat( ErrID_Fatal, "DTAero was changed in Init_FVWmodule(); this is not allowed.", ErrStat2, ErrMsg2, RoutineName) contains subroutine Cleanup() diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index f7c96127a6..e7c160e1c6 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -92,7 +92,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! Preliminary meshing of the wings (may depend on input file) ! NOTE: the mesh is not located at the right position yet, the first call to calcoutput will redo some meshing - CALL Wings_Panelling_Init(InitInp%WingsMesh, InitInp%RElm, InitInp%chord, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL Wings_Panelling_Init(InitInp%WingsMesh, InitInp%zLocal, InitInp%chord, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Set parameters from InputFileData (need Misc allocated) CALL FVW_SetParametersFromInputFile(InputFileData, p, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -229,11 +229,9 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) ErrMsg = "" ! p%nWings = InitInp%NumBlades - ! TODO TODO TODO Hack for AD14 mesh that is wrong - !p%nWings = 1 ! NOTE: temporary limitation, all wings have the same nspan - p%nSpan = size(InitInp%RElm)-1 + p%nSpan = InitInp%numBladeNodes-1 end subroutine FVW_SetParametersFromInputs ! ============================================================================== diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index c06980157a..5dd6f928b3 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -34,8 +34,8 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi chord_LL :: - - "chord on LL cp " m # Variables at control point - Dimensions nSpan typedef ^ ^ ReKi s_CP_LL :: - - "Spanwise coordinate of LL CP" m -typedef ^ ^ ReKi chord_CP_LL :: - - "chord on LL cp " m -typedef ^ ^ ReKi CP_LL ::: - - "Coordinates of LL CP" - +typedef ^ ^ ReKi chord_CP_LL :: - - "chord on LL cp " m +typedef ^ ^ ReKi CP_LL ::: - - "Coordinates of LL CP" - typedef ^ ^ ReKi Tang ::: - - "Unit Tangential vector on LL CP" - typedef ^ ^ ReKi Norm ::: - - "Unit Normal vector on LL CP " - typedef ^ ^ ReKi Orth ::: - - "Unit Orthogonal vector on LL CP" - @@ -48,13 +48,13 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi Vwnd_FW :::: - - "Wind on far wake panels" m/s typedef ^ ^ ReKi Vind_NW :::: - - "Induced velocity on near wake panels" m/s typedef ^ ^ ReKi Vind_FW :::: - - "Induced velocity on far wake panels" m/s -typedef ^ ^ IntKi nNW - - - "Number of active near wake panels" - -typedef ^ ^ IntKi nFW - - - "Number of active far wake panels" - +typedef ^ ^ IntKi nNW - - - "Number of active near wake panels" - +typedef ^ ^ IntKi nFW - - - "Number of active far wake panels" - # ........ Input ............ # FVW_InputType -typedef FVW/FVW InputType MeshType WingsMesh : - - - "Input Mesh defining position and orientation of wings" -typedef ^ ^ ReKi V_wind :: - - "Wind at requested points (r_wind)" - +typedef FVW/FVW InputType MeshType WingsMesh : - - "Input Mesh defining position and orientation of wings" +typedef ^ ^ ReKi V_wind :: - - "Wind at requested points (r_wind)" - # ........ Output ............ # FVW_OutputType @@ -72,7 +72,7 @@ typedef ^ ^ ReKi #.......... DiscreteStateType ...... # FVW_DiscreteStateType -typedef FVW/FVW DiscreteStateType IntKi Null - - - "Empty to satisfy framework" - +typedef FVW/FVW DiscreteStateType ReKi NULL - - - "Empty to satisfy framework" - #.......... ConstraintStateType ...... # FVW_ConstraintStateType @@ -84,8 +84,13 @@ typedef ^ ^ Reki # FVW_InitInputType typedef FVW/FVW InitInputType CHARACTER(1024) FVWFileName - - - "Main FVW input file name" - typedef ^ ^ MeshType WingsMesh : - - "Input Mesh defining position and orientation of wings (nSpan+1) " - +typedef ^ ^ IntKi AFindx :: - - "Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number]" - typedef ^ ^ ReKi Chord :: - - "Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number]" - typedef ^ ^ ReKi RElm : - - "radius of center of each element" - +typedef ^ ^ ReKi zHub : - - "Distance to hub for each blade" m +typedef ^ ^ ReKi zLocal :: - - "Distance to blade node, measured along the blade" m +typedef ^ ^ ReKi zTip : - - "Distance to blade tip, measured along the blade" m +typedef ^ ^ ReKi rLocal :: - - "Radial distance to blade node from the center of rotation, measured in the rotor plane, needed for DBEMT" m typedef ^ ^ IntKi NumBlades - - - "Number of blades" - typedef ^ ^ IntKi NumBladeNodes - - - "Number of nodes on each blade" - diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index a7dfffe4b7..860856ac15 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -88,7 +88,7 @@ MODULE FVW_Types ! ======================= ! ========= FVW_InputType ======= TYPE, PUBLIC :: FVW_InputType - TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: WingsMesh !< - [Input Mesh defining position and orientation of wings] + TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: WingsMesh !< Input Mesh defining position and orientation of wings [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: V_wind !< Wind at requested points (r_wind) [-] END TYPE FVW_InputType ! ======================= @@ -109,7 +109,7 @@ MODULE FVW_Types ! ======================= ! ========= FVW_DiscreteStateType ======= TYPE, PUBLIC :: FVW_DiscreteStateType - INTEGER(IntKi) :: Null !< Empty to satisfy framework [-] + REAL(ReKi) :: NULL !< Empty to satisfy framework [-] END TYPE FVW_DiscreteStateType ! ======================= ! ========= FVW_ConstraintStateType ======= @@ -122,8 +122,13 @@ MODULE FVW_Types TYPE, PUBLIC :: FVW_InitInputType CHARACTER(1024) :: FVWFileName !< Main FVW input file name [-] TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: WingsMesh !< Input Mesh defining position and orientation of wings (nSpan+1) [-] + INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number] [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number] [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: RElm !< radius of center of each element [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: zHub !< Distance to hub for each blade [m] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: zLocal !< Distance to blade node, measured along the blade [m] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: zTip !< Distance to blade tip, measured along the blade [m] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: rLocal !< Radial distance to blade node from the center of rotation, measured in the rotor plane, needed for DBEMT [m] INTEGER(IntKi) :: NumBlades !< Number of blades [-] INTEGER(IntKi) :: NumBladeNodes !< Number of nodes on each blade [-] END TYPE FVW_InitInputType @@ -3213,7 +3218,7 @@ SUBROUTINE FVW_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrS ! ErrStat = ErrID_None ErrMsg = "" - DstDiscStateData%Null = SrcDiscStateData%Null + DstDiscStateData%NULL = SrcDiscStateData%NULL END SUBROUTINE FVW_CopyDiscState SUBROUTINE FVW_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) @@ -3262,7 +3267,7 @@ SUBROUTINE FVW_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_BufSz = 0 Db_BufSz = 0 Int_BufSz = 0 - Int_BufSz = Int_BufSz + 1 ! Null + Re_BufSz = Re_BufSz + 1 ! NULL IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -3290,8 +3295,8 @@ SUBROUTINE FVW_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Db_Xferred = 1 Int_Xferred = 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%Null - Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%NULL + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_PackDiscState SUBROUTINE FVW_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3326,8 +3331,8 @@ SUBROUTINE FVW_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Re_Xferred = 1 Db_Xferred = 1 Int_Xferred = 1 - OutData%Null = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 + OutData%NULL = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_UnPackDiscState SUBROUTINE FVW_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg ) @@ -3562,6 +3567,20 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS IF (ErrStat>=AbortErrLev) RETURN ENDDO ENDIF +IF (ALLOCATED(SrcInitInputData%AFindx)) THEN + i1_l = LBOUND(SrcInitInputData%AFindx,1) + i1_u = UBOUND(SrcInitInputData%AFindx,1) + i2_l = LBOUND(SrcInitInputData%AFindx,2) + i2_u = UBOUND(SrcInitInputData%AFindx,2) + IF (.NOT. ALLOCATED(DstInitInputData%AFindx)) THEN + ALLOCATE(DstInitInputData%AFindx(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%AFindx.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%AFindx = SrcInitInputData%AFindx +ENDIF IF (ALLOCATED(SrcInitInputData%Chord)) THEN i1_l = LBOUND(SrcInitInputData%Chord,1) i1_u = UBOUND(SrcInitInputData%Chord,1) @@ -3587,6 +3606,58 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS END IF END IF DstInitInputData%RElm = SrcInitInputData%RElm +ENDIF +IF (ALLOCATED(SrcInitInputData%zHub)) THEN + i1_l = LBOUND(SrcInitInputData%zHub,1) + i1_u = UBOUND(SrcInitInputData%zHub,1) + IF (.NOT. ALLOCATED(DstInitInputData%zHub)) THEN + ALLOCATE(DstInitInputData%zHub(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%zHub.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%zHub = SrcInitInputData%zHub +ENDIF +IF (ALLOCATED(SrcInitInputData%zLocal)) THEN + i1_l = LBOUND(SrcInitInputData%zLocal,1) + i1_u = UBOUND(SrcInitInputData%zLocal,1) + i2_l = LBOUND(SrcInitInputData%zLocal,2) + i2_u = UBOUND(SrcInitInputData%zLocal,2) + IF (.NOT. ALLOCATED(DstInitInputData%zLocal)) THEN + ALLOCATE(DstInitInputData%zLocal(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%zLocal.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%zLocal = SrcInitInputData%zLocal +ENDIF +IF (ALLOCATED(SrcInitInputData%zTip)) THEN + i1_l = LBOUND(SrcInitInputData%zTip,1) + i1_u = UBOUND(SrcInitInputData%zTip,1) + IF (.NOT. ALLOCATED(DstInitInputData%zTip)) THEN + ALLOCATE(DstInitInputData%zTip(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%zTip.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%zTip = SrcInitInputData%zTip +ENDIF +IF (ALLOCATED(SrcInitInputData%rLocal)) THEN + i1_l = LBOUND(SrcInitInputData%rLocal,1) + i1_u = UBOUND(SrcInitInputData%rLocal,1) + i2_l = LBOUND(SrcInitInputData%rLocal,2) + i2_u = UBOUND(SrcInitInputData%rLocal,2) + IF (.NOT. ALLOCATED(DstInitInputData%rLocal)) THEN + ALLOCATE(DstInitInputData%rLocal(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%rLocal.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%rLocal = SrcInitInputData%rLocal ENDIF DstInitInputData%NumBlades = SrcInitInputData%NumBlades DstInitInputData%NumBladeNodes = SrcInitInputData%NumBladeNodes @@ -3607,11 +3678,26 @@ SUBROUTINE FVW_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) ENDDO DEALLOCATE(InitInputData%WingsMesh) ENDIF +IF (ALLOCATED(InitInputData%AFindx)) THEN + DEALLOCATE(InitInputData%AFindx) +ENDIF IF (ALLOCATED(InitInputData%Chord)) THEN DEALLOCATE(InitInputData%Chord) ENDIF IF (ALLOCATED(InitInputData%RElm)) THEN DEALLOCATE(InitInputData%RElm) +ENDIF +IF (ALLOCATED(InitInputData%zHub)) THEN + DEALLOCATE(InitInputData%zHub) +ENDIF +IF (ALLOCATED(InitInputData%zLocal)) THEN + DEALLOCATE(InitInputData%zLocal) +ENDIF +IF (ALLOCATED(InitInputData%zTip)) THEN + DEALLOCATE(InitInputData%zTip) +ENDIF +IF (ALLOCATED(InitInputData%rLocal)) THEN + DEALLOCATE(InitInputData%rLocal) ENDIF END SUBROUTINE FVW_DestroyInitInput @@ -3675,6 +3761,11 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs END IF END DO END IF + Int_BufSz = Int_BufSz + 1 ! AFindx allocated yes/no + IF ( ALLOCATED(InData%AFindx) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! AFindx upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%AFindx) ! AFindx + END IF Int_BufSz = Int_BufSz + 1 ! Chord allocated yes/no IF ( ALLOCATED(InData%Chord) ) THEN Int_BufSz = Int_BufSz + 2*2 ! Chord upper/lower bounds for each dimension @@ -3684,6 +3775,26 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs IF ( ALLOCATED(InData%RElm) ) THEN Int_BufSz = Int_BufSz + 2*1 ! RElm upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%RElm) ! RElm + END IF + Int_BufSz = Int_BufSz + 1 ! zHub allocated yes/no + IF ( ALLOCATED(InData%zHub) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! zHub upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%zHub) ! zHub + END IF + Int_BufSz = Int_BufSz + 1 ! zLocal allocated yes/no + IF ( ALLOCATED(InData%zLocal) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! zLocal upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%zLocal) ! zLocal + END IF + Int_BufSz = Int_BufSz + 1 ! zTip allocated yes/no + IF ( ALLOCATED(InData%zTip) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! zTip upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%zTip) ! zTip + END IF + Int_BufSz = Int_BufSz + 1 ! rLocal allocated yes/no + IF ( ALLOCATED(InData%rLocal) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! rLocal upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%rLocal) ! rLocal END IF Int_BufSz = Int_BufSz + 1 ! NumBlades Int_BufSz = Int_BufSz + 1 ! NumBladeNodes @@ -3759,6 +3870,22 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs ENDIF END DO END IF + IF ( .NOT. ALLOCATED(InData%AFindx) ) 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%AFindx,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AFindx,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%AFindx,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AFindx,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%AFindx)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%AFindx))-1 ) = PACK(InData%AFindx,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(InData%AFindx) + END IF IF ( .NOT. ALLOCATED(InData%Chord) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -3787,6 +3914,64 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs IF (SIZE(InData%RElm)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%RElm))-1 ) = PACK(InData%RElm,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%RElm) + END IF + IF ( .NOT. ALLOCATED(InData%zHub) ) 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%zHub,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%zHub,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%zHub)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%zHub))-1 ) = PACK(InData%zHub,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%zHub) + END IF + IF ( .NOT. ALLOCATED(InData%zLocal) ) 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%zLocal,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%zLocal,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%zLocal,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%zLocal,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%zLocal)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%zLocal))-1 ) = PACK(InData%zLocal,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%zLocal) + END IF + IF ( .NOT. ALLOCATED(InData%zTip) ) 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%zTip,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%zTip,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%zTip)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%zTip))-1 ) = PACK(InData%zTip,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%zTip) + END IF + IF ( .NOT. ALLOCATED(InData%rLocal) ) 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%rLocal,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%rLocal,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%rLocal,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%rLocal,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%rLocal)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%rLocal))-1 ) = PACK(InData%rLocal,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%rLocal) END IF IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumBlades Int_Xferred = Int_Xferred + 1 @@ -3888,6 +4073,32 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) END DO END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! AFindx 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 + IF (ALLOCATED(OutData%AFindx)) DEALLOCATE(OutData%AFindx) + ALLOCATE(OutData%AFindx(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%AFindx.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%AFindx)>0) OutData%AFindx = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%AFindx))-1 ), mask2, 0_IntKi ) + Int_Xferred = Int_Xferred + SIZE(OutData%AFindx) + DEALLOCATE(mask2) + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Chord not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -3936,6 +4147,104 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er IF (SIZE(OutData%RElm)>0) OutData%RElm = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%RElm))-1 ), mask1, 0.0_ReKi ) Re_Xferred = Re_Xferred + SIZE(OutData%RElm) DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! zHub 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 + IF (ALLOCATED(OutData%zHub)) DEALLOCATE(OutData%zHub) + ALLOCATE(OutData%zHub(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%zHub.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%zHub)>0) OutData%zHub = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%zHub))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%zHub) + DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! zLocal 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 + IF (ALLOCATED(OutData%zLocal)) DEALLOCATE(OutData%zLocal) + ALLOCATE(OutData%zLocal(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%zLocal.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%zLocal)>0) OutData%zLocal = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%zLocal))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%zLocal) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! zTip 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 + IF (ALLOCATED(OutData%zTip)) DEALLOCATE(OutData%zTip) + ALLOCATE(OutData%zTip(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%zTip.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%zTip)>0) OutData%zTip = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%zTip))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%zTip) + DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! rLocal 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 + IF (ALLOCATED(OutData%rLocal)) DEALLOCATE(OutData%rLocal) + ALLOCATE(OutData%rLocal(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%rLocal.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%rLocal)>0) OutData%rLocal = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%rLocal))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%rLocal) + DEALLOCATE(mask2) END IF OutData%NumBlades = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 5c0ff77dbf..0e5590be14 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -16,7 +16,7 @@ module FVW_Wings !! - chord_LL_CP: chord on LL cp subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh - real(ReKi), dimension(:), intent(in ) :: r !< + real(ReKi), dimension(:,:), intent(in ) :: r !< real(ReKi), dimension(:,:), intent(in ) :: chord !< type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables @@ -40,6 +40,8 @@ subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) if (allocated(s_in)) deallocate(s_in) allocate(s_in(1:Meshes(iW)%nNodes)) ! --- Computing spanwise coordinate of input mesh normalized from 0 to 1 +!FIXME: does this work for a highly curved blade? +!also note: this info also exists in InitInp%zLocal or InitInp%rLocal s_in(:) = -999 First = Meshes(iW)%Position(1:3,1 ) Last = Meshes(iW)%Position(1:3,p%nSpan+1) @@ -115,7 +117,7 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) ! --- Generic code below to compute normal/tangential vectors of a lifting line panel ! Notations follow vanGarrel [TODO REF] do iW = 1,p%nWings - do iSpan = 1,p%nSpan+1 + do iSpan = 1,p%nSpan P1 = m%LE(:,iSpan , iw) P4 = m%LE(:,iSpan+1, iw) P3 = m%TE(:,iSpan+1, iw) From 4f00887ab602c2307cd23a0a7ffde5487e574651 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 20 Nov 2019 14:22:23 -0700 Subject: [PATCH 017/190] FVW: Rearrange the WingsMesh. Used MOVE_ALLOC to move from InitInp to u% The WingsMesh now exactly follows the u_AD%BladeMotion mesh --- modules/aerodyn/src/AeroDyn.f90 | 69 ++++++++-------------- modules/aerodyn/src/FVW.f90 | 13 ++-- modules/aerodyn/src/FVW_Wings.f90 | 1 + modules/openfast-library/src/FAST_Subs.f90 | 17 +++--- 4 files changed, 40 insertions(+), 60 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 5b43fe6a92..89959bb81d 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -395,8 +395,8 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut call FVW_CopyInput( m%FVW_u(1), m%FVW_u(2), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) endif - - + + !............................................................................................ ! Define outputs here !............................................................................................ @@ -1528,7 +1528,8 @@ subroutine SetInputsForFVW(p, u, m, indx, errStat, errMsg) ! Rather than use a meshcopy, we will just copy what we need to the WingsMesh - ! NOTE: MeshCopy requires the source mesh to be INOUT intent + ! NOTE: MeshCopy requires the source mesh to be INOUT intent + ! NOTE2: If we change the WingsMesh to not be identical to the BladeMotion mesh, add the mapping stuff here. do k=1,p%NumBlades if ( u%BladeMotion(k)%nNodes /= m%FVW_u(indx)%WingsMesh(k)%nNodes ) then ErrStat = ErrID_Fatal @@ -2145,56 +2146,32 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m end do end do -!FIXME: do we need this mesh at all? - ALLOCATE( InitInp%WingsMesh(p%NumBlades), STAT = ErrStat2 ) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat ( ErrID_Fatal, 'Could not allocate InitInp%WingsMesh (meshes)', ErrStat,ErrMsg,RoutineName ) - RETURN - END IF - DO IB = 1, p%NumBlades - CALL MeshCopy ( SrcMesh = u_AD%BladeMotion(IB) & - ,DestMesh = InitInp%WingsMesh(IB) & - ,CtrlCode = MESH_COUSIN & - ,Orientation = .TRUE. & - ,TranslationVel = .TRUE. & - ,RotationVel = .TRUE. & - ,ErrStat = ErrStat2 & - ,ErrMess = ErrMsg2 ) - CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) - IF (ErrStat >= AbortErrLev) RETURN - ENDDO - ! ---- END TODO + ! Copy the mesh over for InitInp to FVW. We would not need to copy this if we decided to break the Framework + ! by passing u_AD%BladeMotion directly into FVW_Init, but nothing is really gained by doing that. + ALLOCATE( InitInp%WingsMesh(p%NumBlades), STAT = ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat ( ErrID_Fatal, 'Could not allocate InitInp%WingsMesh (meshes)', ErrStat,ErrMsg,RoutineName ) + RETURN + END IF + DO IB = 1, p%NumBlades + CALL MeshCopy ( SrcMesh = u_AD%BladeMotion(IB) & + ,DestMesh = InitInp%WingsMesh(IB) & + ,CtrlCode = MESH_COUSIN & + ,Orientation = .TRUE. & + ,TranslationVel = .TRUE. & + ,RotationVel = .TRUE. & + ,ErrStat = ErrStat2 & + ,ErrMess = ErrMsg2 ) + CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) + IF (ErrStat >= AbortErrLev) RETURN + ENDDO !FIXME: Should we be passing any AFinfo? Is that needed in FVW for anything? -! call FVW_Init( u_AD%BladeMotion, InitInp, u, p%FVW, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat2, ErrMsg2 ) call FVW_Init( InitInp, u, p%FVW, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat2, ErrMsg2 ) CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - ! TODO ANDY - !FIXME This really probably should be done inside of FVW_Init instead of here. - ! Not entirely sure how to pass the u%InputMarkers in though. - ALLOCATE( u%WingsMesh(p%NumBlades), STAT = ErrStat2 ) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat ( ErrID_Fatal, 'Could not allocate u%InputMarkers (meshes)', ErrStat,ErrMsg,RoutineName ) - RETURN - END IF - DO IB = 1, p%NumBlades - CALL MeshCopy ( SrcMesh = u_AD%BladeMotion(IB) & - ,DestMesh = u%WingsMesh(IB) & - ,CtrlCode = MESH_COUSIN & - ,Orientation = .TRUE. & - ,TranslationVel = .TRUE. & - ,RotationVel = .TRUE. & - ,ErrStat = ErrStat2 & - ,ErrMess = ErrMsg2 ) - CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) - IF (ErrStat >= AbortErrLev) RETURN - - u%WingsMesh(IB)%RemapFlag = .TRUE. - ENDDO - ! If anything is passed back in InitOut%FVW, deal with it here... diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index e7c160e1c6..20db032ccb 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -90,9 +90,11 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu p%nFWMax=100 ! TODO CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return - ! Preliminary meshing of the wings (may depend on input file) - ! NOTE: the mesh is not located at the right position yet, the first call to calcoutput will redo some meshing - CALL Wings_Panelling_Init(InitInp%WingsMesh, InitInp%zLocal, InitInp%chord, p, m, ErrStat2, ErrMsg2); if(Failed()) return + ! Move the InitInp%WingsMesh to u + CALL MOVE_ALLOC( InitInp%WingsMesh, u%WingsMesh ) ! Move from InitInp to u + + ! This mesh is passed in as a cousin of the BladeMotion mesh. + CALL Wings_Panelling_Init(u%WingsMesh, InitInp%zLocal, InitInp%chord, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Set parameters from InputFileData (need Misc allocated) CALL FVW_SetParametersFromInputFile(InputFileData, p, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -105,14 +107,13 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL FVW_InitConstraint( z, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return ! Panelling wings based on initial input mesh provided - ! NOTE: the mesh is not located at the right position yet, the first call to calcoutput will redo some meshing - CALL Wings_Panelling (InitInp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return + ! This mesh is now a cousin of the BladeMotion mesh from AD. + CALL Wings_Panelling (u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Returned guessed locations where wind will be required CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return ! Return anything in FVW_InitOutput that should be passed back to the calling code here - CONTAINS logical function Failed() diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 0e5590be14..11a3f0d2ec 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -70,6 +70,7 @@ subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) m%s_CP_LL (iSpan, iW) = (m%s_LL (iSpan,iW)+ m%s_LL (iSpan+1,iW))/2 m%chord_LL(iSpan, iW) = (m%chord_LL(iSpan,iW)+ m%chord_LL(iSpan+1,iW))/2 enddo +call MeshPrintInfo(CU, Meshes(iW) ) enddo end subroutine Wings_Panelling_Init diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index e22b70362f..e805f56354 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -5123,7 +5123,14 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O call MeshWrVTK(p_FAST%TurbinePos, AD%y%TowerLoad, trim(VTK_path)//'.AD_Tower', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth, AD%Input(1)%TowerMotion ) end if - + + ! FVW submodule of AD15 + if (allocated(AD%m%FVW_u(1)%WingsMesh)) then + DO K=1,NumBl + call MeshWrVTK(p_FAST%TurbinePos, AD%m%FVW_u(1)%WingsMesh(k), trim(VTK_path)//'.FVW_WingsMesh'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth, AD%Input(1)%BladeMotion(k) ) + !call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%BladeMotion(K), trim(p_FAST%OutFileRoot)//'.AD_BladeMotion'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2 ) + END DO + end if END IF ! HydroDyn @@ -5274,7 +5281,7 @@ SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, DO K=1,NumBl call MeshWrVTK(p_FAST%TurbinePos, BD%y(k)%BldMotion, trim(VTK_path)//'.BD_BldMotion'//trim(num2lstr(k)), & y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth ) - END DO + END DO ELSE DO K=1,NumBl call MeshWrVTK(p_FAST%TurbinePos, ED%Output(1)%BladeLn2Mesh(K), trim(VTK_path)//'.ED_BladeLn2Mesh_motion'//trim(num2lstr(k)), & @@ -5282,12 +5289,6 @@ SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, END DO END IF - ! Free wake -!FIXME:FVW -! IF ( p_FAST%CompAero == Module_AD14 ) THEN ! These meshes may have airfoil data associated with nodes... -! call WrVTK_FVW(AD14%p%FVW, AD14%x(1)%FVW, AD14%z(1)%FVW, AD14%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) -! END IF - ! Tower motions call MeshWrVTK(p_FAST%TurbinePos, ED%Output(1)%TowerLn2Mesh, trim(VTK_path)//'.ED_TowerLn2Mesh_motion', & y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth ) From f17af35420b52aa6f1ed777f65f5ba3c9834f04b Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 15:06:12 -0700 Subject: [PATCH 018/190] FVW: dedicated function for wake inductions, depth start index for packing --- modules/aerodyn14/src/FVW.f90 | 139 ++++------------------ modules/aerodyn14/src/FVW_IO.f90 | 2 +- modules/aerodyn14/src/FVW_Subs.f90 | 83 ++++++++++--- modules/aerodyn14/src/FVW_Tests.f90 | 8 +- modules/aerodyn14/src/FVW_VortexTools.f90 | 30 +++-- 5 files changed, 117 insertions(+), 145 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 7694afc72f..3cc92dfbda 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -460,110 +460,31 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs + integer(IntKi) :: iSpan,iAge, iW + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message real(ReKi), dimension(3) :: U_mean - integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity - real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points - real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation - real(ReKi), dimension(:) , allocatable :: SegSmooth !< Segment smooth parameter - real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints - real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity - integer(IntKi) :: SmoothModel + + ErrStat = ErrID_None + ErrMsg = "" call AllocAry( dxdt%r_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat, ErrMsg ); dxdt%r_NW= -999999_ReKi; call AllocAry( dxdt%r_FW , 3 , 2 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; if (t> p%FreeWakeStart) then -! print*,' Free wake convection' -! ! --- Packing all vortex elements into a list of segments -! call PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, nSeg, nSegP) -! print*,'Number of segments',nSeg, 'Number of points',nSegP -! -! ! --- Computing induced velocity -! ! TODO for now point by point.. -! allocate(SegSmooth(1:nSeg)); -! SegSmooth=10 -! SmoothModel=idSegSmoothLambOseen -! ! --- On NW -! allocate(Uind(1:3,1)) -! allocate(CPs (1:3,1)) -! CPs=0 -! Uind=0 -! do iW=1,p%nWings -! do iAge=1,m%nNW+1 -! do iSpan=1,p%nSpan+1 -! Uind(1:3,1)=0.0_ReKi -! CPs(1:3,1) = x%r_NW(1:3,iSpan,iAge,iW) -! -! CALL ui_seg(1, 1, 1, CPs, & -! 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, & -! SmoothModel, SegSmooth, Uind) -! ! print*,'Uind',Uind -! m%Vind_NW(1:3,iSpan,iAge,iW) = Uind(1:3,1) -! enddo -! enddo -! enddo -! deallocate(Uind) -! deallocate(CPs) -! print*,'1 ', m%Vind_NW(1:3,1,1,1) -! print*,'1last', m%Vind_NW(1:3,p%nSpan+1,1,1) -! print*,'llast', m%Vind_NW(1:3,p%nSpan+1,m%nNW,p%nWings) - - ! --- Packing all vortex elements into a list of segments - call PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, nSeg, nSegP) - print*,'Number of segments',nSeg, 'Number of points',nSegP -! - ! --- Computing induced velocity - allocate(SegSmooth(1:nSeg)); - SegSmooth=10 - SmoothModel=idSegSmoothLambOseen - m%Vind_NW = -9999 - nCPs=nSegP - allocate(CPs (1:3,1:nCPs)) - allocate(Uind(1:3,1:nCPs)) - Uind=0.0_ReKi !< important due to side effects of ui_seg - ! --- - call PackConvectingPoints(p, m, x, z, CPs, nCPs) - print*,'Number of points packed for Convection:',nCPs, nSegP - CALL ui_seg( 1, nCPs, nCPs, CPs, & - 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, & - SmoothModel, SegSmooth, Uind) - print*,'1 ',Uind(1:3,1) - call UnPackInducedVelocity(p, m, x, z, Uind) - - print*,'1 ', m%Vind_NW(1:3,1,1,1) - print*,'1last', m%Vind_NW(1:3,p%nSpan+1,1,1) - print*,'llast', m%Vind_NW(1:3,p%nSpan+1,m%nNW,p%nWings) - - deallocate(Uind) - deallocate(CPs) - - deallocate(SegConnct) - deallocate(SegGamma) - deallocate(SegPoints) - deallocate(SegSmooth) - - - U_mean(1:3)=0 - do iW=1,p%nWings; do iAge=1,m%nNW+1; do iSpan=1,p%nSpan+1; - U_mean(1:3)= U_mean(1:3)+ m%Vind_NW(1:3, iSpan, iAge, iW) - enddo; enddo; enddo - U_mean(1:3) = U_mean(1:3)/ ((m%nNW+1)*(p%nSpan+1)*p%nWings) - print*,'Mean convection velocity NW: ',U_mean(1:3) - U_mean(1:3)=0 - do iW=1,p%nWings; do iAge=1,m%nNW+1; do iSpan=1,p%nSpan+1; - U_mean(1:3)= U_mean(1:3)+ m%Vwnd_NW(1:3, iSpan, iAge, iW) - enddo; enddo; enddo - U_mean(1:3) = U_mean(1:3)/ ((m%nNW+1)*(p%nSpan+1)*p%nWings) - print*,'Mean convection velocity NW: ',U_mean(1:3) - U_mean(1:3)=0 - do iW=1,p%nWings; do iAge=1,m%nFW+1; do iSpan=1,2; - U_mean(1:3)= U_mean(1:3)+ m%Vwnd_FW(1:3, iSpan, iAge, iW) - enddo; enddo; enddo - U_mean(1:3) = U_mean(1:3)/ ((m%nFW+1)*(2)*p%nWings) - print*,'Mean convection velocity FW: ',U_mean(1:3) - + ! --- Compute Induced velocities on the Near wake and far wake based on the marker postions: + ! (expensive N^2 call) + ! In : x%r_NW, r%r_FW + ! Out: m%Vind_NW, m%Vind_FW + m%Vind_NW=-999999._ReKi + m%Vind_FW=-999999._ReKi + call WakeInducedVelocities(p, x, m, ErrStat2, ErrMsg2) + + call print_mean_4d( m%Vind_NW(:,:, 1:m%nNW+1,:), 'Mean induced vel. NW') + call print_mean_4d( m%Vind_FW(:,:, 1:m%nFW+1,:), 'Mean induced vel. FW') + call print_mean_4d( m%Vwnd_NW(:,:, 1:m%nNW+1,:), 'Mean wind vel. NW') + call print_mean_4d( m%Vwnd_FW(:,:, 1:m%nFW+1,:), 'Mean wind vel. FW') ! --- Vortex points are convected with the free stream and induced velocity dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + m%Vind_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) @@ -572,19 +493,8 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) else - U_mean(1:3)=0 - do iW=1,p%nWings; do iAge=1,m%nNW+1; do iSpan=1,p%nSpan+1; - U_mean(1:3)= U_mean(1:3)+ m%Vwnd_NW(1:3, iSpan, iAge, iW) - enddo; enddo; enddo - U_mean(1:3) = U_mean(1:3)/ ((m%nNW+1)*(p%nSpan+1)*p%nWings) - print*,'Mean convection velocity NW: ',U_mean(1:3) - U_mean(1:3)=0 - do iW=1,p%nWings; do iAge=1,m%nFW+1; do iSpan=1,2; - U_mean(1:3)= U_mean(1:3)+ m%Vwnd_FW(1:3, iSpan, iAge, iW) - enddo; enddo; enddo - U_mean(1:3) = U_mean(1:3)/ ((m%nFW+1)*(2)*p%nWings) - print*,'Mean convection velocity FW: ',U_mean(1:3) - + call print_mean_4d( m%Vwnd_NW(:,1:m%nNW+1,:,:), 'Mean wind vel. NW') + call print_mean_4d( m%Vwnd_FW(:,1:m%nFW+1,:,:), 'Mean wind vel. FW') ! --- Vortex points are convected with the free stream dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) @@ -714,8 +624,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg endif if (.not. allocated(y%Vind)) then - !call AllocAry( y%Vind , 3, p%nSpan, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); - call AllocAry( y%Vind , 3, p%nSpan, 3, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO TODO TODO Hack nWings=3 for output + call AllocAry( y%Vind , 3, p%nSpan, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); if(Failed()) return endif ! Returned guessed locations where wind will be required @@ -739,9 +648,9 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg !========================================================================== !> Computes induction on the lifting line (3/4 chord point) ! Interpolate the values at the radial station of AeroDyn - subroutine CalcInduction_LL() - - end subroutine CalcInduction_LL +! subroutine CalcInduction_LL() +! +! end subroutine CalcInduction_LL subroutine PrepareNextTimeStep() diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index 2f77efe581..a0067c2164 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -179,7 +179,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) iHeadP=1 iHeadC=1 do iW=1,nWings - CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) enddo ! if (allocated(Buffer2d)) deallocate(Buffer2d) ! allocate(Buffer2d(1,nSpan)) diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index 8e95a0cc65..b467fb1f76 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -3,6 +3,7 @@ module FVW_SUBS use NWTC_LIBRARY use FVW_TYPES use FVW_VortexTools + use FVW_BiotSavart implicit none @@ -21,6 +22,8 @@ module FVW_SUBS integer(IntKi), parameter :: idABM4 = 3 integer(IntKi), parameter :: idEuler1 = 5 + ! Implementation + integer(IntKi), parameter :: iNWStart=2 !< Index in r%NW where the near wake start (if >1 then the Wing panels are included in r_NW) contains !========================================================================== @@ -281,17 +284,16 @@ end subroutine DistributeRequestedWind !> Distribute the induced velocity to the proper location -subroutine UnPackInducedVelocity(p, m, x, z, Uind) +subroutine UnPackInducedVelocity(p, m, x, Uind) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables type(FVW_ContinuousStateType), intent(in ) :: x !< States - type(FVW_ConstraintStateType), intent(in ) :: z !< Initial misc/optimization variables real(ReKi), dimension(:,:) , intent(in ) :: Uind !< Induced velocity ! Local integer(IntKi) :: iW, iHeadP iHeadP=1 do iW=1,p%nWings - CALL VecToLattice(Uind, m%Vind_NW(:,:,1:m%nNW+1,iW), iHeadP) + CALL VecToLattice(Uind, 1, m%Vind_NW(:,:,1:m%nNW+1,iW), iHeadP) enddo if ((iHeadP-1)/=size(Uind,2)) then print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 @@ -301,11 +303,10 @@ subroutine UnPackInducedVelocity(p, m, x, z, Uind) !> Distribute the induced velocity to the convecting points -subroutine PackConvectingPoints(p, m, x, z, Points, nPoints) +subroutine PackConvectingPoints(p, m, x, Points, nPoints) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables type(FVW_ContinuousStateType), intent(in ) :: x !< States - type(FVW_ConstraintStateType), intent(in ) :: z !< Initial misc/optimization variables real(ReKi), dimension(:,:) , intent(inout) :: Points !< Points packed integer(IntKi), intent( out) :: nPoints !< Number of points packed ! Local @@ -314,7 +315,7 @@ subroutine PackConvectingPoints(p, m, x, z, Points, nPoints) ! --- Compute number of convecting points iHeadP=1 do iW=1,p%nWings - CALL LatticeToPoints(x%r_NW(1:3,:,1:m%nNW+1,iW) , Points, iHeadP) + CALL LatticeToPoints(x%r_NW(1:3,:,1:m%nNW+1,iW), 1, Points, iHeadP) enddo if ((iHeadP-1)/=size(Points,2)) then print*,'PackConvectingPoints: Number of points wrongly estimated',size(Points,2), iHeadP-1 @@ -323,11 +324,11 @@ subroutine PackConvectingPoints(p, m, x, z, Points, nPoints) nPoints=iHeadP-1 end subroutine -subroutine PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, nSeg, nSegP) +subroutine PackPanelsToSegments(p, m, x, iNWStart, SegConnct, SegPoints, SegGamma, nSeg, nSegP) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables type(FVW_ContinuousStateType), intent(in ) :: x !< States - type(FVW_ConstraintStateType), intent(in ) :: z !< Initial misc/optimization variables + integer(IntKi), intent(in ) :: iNWStart !< Index where we start packing for NW panels integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation @@ -339,8 +340,8 @@ subroutine PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, n !real(ReKi), dimension(:), allocatable :: SegSmooth !< ! Counting total number of segments TODO add FarWake - nP = p%nWings * ( (p%nSpan+1)*(m%nNW+1) ) - nC = p%nWings * (2*(p%nSpan+1)*(m%nNW+1)-p%nSpan-m%nNW-2) + nP = p%nWings * ( (p%nSpan+1)*(m%nNW-iNWStart+2) ) + nC = p%nWings * (2*(p%nSpan+1)*(m%nNW-iNWStart+2)-(p%nSpan+1)-(m%nNW-iNWStart+1+1)) ! nP = nP + p%nWings * (p%nSpan+1)*2 ! nC = nC + p%nWings * (2*(p%nSpan+1)*(2)-p%nSpan-1-2) @@ -356,22 +357,68 @@ subroutine PackAllPanelsToSegments(p, m, x, z, SegConnct, SegPoints, SegGamma, n iHeadP=1 iHeadC=1 do iW=1,p%nWings - CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iNWStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) enddo -! do iW=1,p%nWings -! Buffer2d(1,:)=m%Gamma_LL(:,iW) -! CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), Buffer2d, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) -! enddo if ((iHeadP-1)/=nP) then - print*,'Number of points wrongly estimated',nP, iHeadP-1 + print*,'PackPanelsToSegments: Number of points wrongly estimated',nP, iHeadP-1 STOP endif if ((iHeadC-1)/=nC) then - print*,'Number of segments wrongly estimated',nC, iHeadC-1 + print*,'PackPanelsToSegments: Number of segments wrongly estimated',nC, iHeadC-1 STOP endif nSeg = iHeadC-1 nSegP = iHeadP-1 -end subroutine PackAllPanelsToSegments +end subroutine PackPanelsToSegments + +subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< States + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + ! Local variables + integer(IntKi) :: SmoothModel !< TODO input file parameter + integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs + integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity + real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points + real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation + real(ReKi), dimension(:) , allocatable :: SegSmooth !< Segment smooth parameter + real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints + real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + m%Vind_NW = -9999._ReKi !< Safety + m%Vind_FW = -9999._ReKi !< Safety + + ! --- Packing all vortex elements into a list of segments + call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) + print*,'Number of segments',nSeg, 'Number of points',nSegP + ! + ! --- Computing induced velocity + allocate(SegSmooth(1:nSeg)); + SegSmooth=10 + SmoothModel=idSegSmoothLambOseen + nCPs=nSegP + allocate(CPs (1:3,1:nCPs)) + allocate(Uind(1:3,1:nCPs)) + Uind=0.0_ReKi !< important due to side effects of ui_seg + ! --- + call PackConvectingPoints(p, m, x, CPs, nCPs) + print*,'Number of points packed for Convection:',nCPs, nSegP + CALL ui_seg( 1, nCPs, nCPs, CPs, & + 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, & + SmoothModel, SegSmooth, Uind) + call UnPackInducedVelocity(p, m, x, Uind) + + deallocate(Uind) + deallocate(CPs) + deallocate(SegConnct) + deallocate(SegGamma) + deallocate(SegPoints) + deallocate(SegSmooth) +end subroutine + + + end module FVW_Subs diff --git a/modules/aerodyn14/src/FVW_Tests.f90 b/modules/aerodyn14/src/FVW_Tests.f90 index 924412ed2b..8c92d5519a 100644 --- a/modules/aerodyn14/src/FVW_Tests.f90 +++ b/modules/aerodyn14/src/FVW_Tests.f90 @@ -69,7 +69,7 @@ subroutine Test_LatticeToSegment(iStat) iHeadP=1 iHeadC=1 - CALL LatticeToSegments(LatticePoints1, LatticeGamma1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) CALL printall() CALL WrVTK_Segments('Points1_seg.vtk', SegPoints, SegConnct, SegGamma) @@ -96,7 +96,7 @@ subroutine Test_LatticeToSegment(iStat) allocate(SegGamma (1:nC2) ); SegGamma=-9999 iHeadP=1 iHeadC=1 - CALL LatticeToSegments(LatticePoints2, LatticeGamma2, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) CALL printall() CALL WrVTK_Segments('Points2_seg.vtk', SegPoints, SegConnct, SegGamma) @@ -111,8 +111,8 @@ subroutine Test_LatticeToSegment(iStat) allocate(SegConnct(1:2,1:nC)); SegConnct=-1 allocate(SegPoints(1:3,1:nP)); SegPoints=-1 allocate(SegGamma (1:nC) ); SegGamma=-9999 - CALL LatticeToSegments(LatticePoints1, LatticeGamma1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) - CALL LatticeToSegments(LatticePoints2, LatticeGamma2, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) CALL printall() CALL WrVTK_Segments('PointsBoth_seg.vtk', SegPoints, SegConnct, SegGamma) diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn14/src/FVW_VortexTools.f90 index 2d8a5b9acc..2f485a6bad 100644 --- a/modules/aerodyn14/src/FVW_VortexTools.f90 +++ b/modules/aerodyn14/src/FVW_VortexTools.f90 @@ -11,12 +11,13 @@ MODULE FVW_VortexTools CONTAINS - subroutine VecToLattice(PointVectors, LatticeVectors, iHeadP) + subroutine VecToLattice(PointVectors, iDepthStart, LatticeVectors, iHeadP) real(Reki), dimension(:,:), intent(in ) :: PointVectors !< nVal x n + integer(IntKi), intent(in ) :: iDepthStart !< Start index for depth dimension real(ReKi), dimension(:,:,:), intent(inout) :: LatticeVectors !< nVal x nSpan x nDepth integer(IntKi), intent(inout) :: iHeadP !< Index indicating where to start in PointVectors integer(IntKi) :: iSpan, iDepth - do iDepth = 1, size(LatticeVectors,3) + do iDepth = iDepthStart, size(LatticeVectors,3) do iSpan = 1, size(LatticeVectors,2) LatticeVectors(:, iSpan, iDepth) = PointVectors(:, iHeadP) iHeadP=iHeadP+1 @@ -24,8 +25,9 @@ subroutine VecToLattice(PointVectors, LatticeVectors, iHeadP) enddo end subroutine - subroutine LatticeToPoints(LatticePoints, Points, iHeadP) + subroutine LatticeToPoints(LatticePoints, iDepthStart, Points, iHeadP) real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Points 3 x nSpan x nDepth + integer(IntKi), intent(in ) :: iDepthStart !< Start index for depth dimension real(ReKi), dimension(:,:), intent(inout) :: Points !< integer(IntKi), intent(inout) :: iHeadP !< Index indicating where to start in Points ! Local @@ -38,7 +40,7 @@ subroutine LatticeToPoints(LatticePoints, Points, iHeadP) ! | | ! 1---4 ! - do iDepth = 1, size(LatticePoints,3) + do iDepth = iDepthStart, size(LatticePoints,3) do iSpan = 1, size(LatticePoints,2) Points(1:3,iHeadP) = LatticePoints(1:3, iSpan, iDepth) iHeadP=iHeadP+1 @@ -47,9 +49,10 @@ subroutine LatticeToPoints(LatticePoints, Points, iHeadP) endsubroutine LatticeToPoints - subroutine LatticeToSegments(LatticePoints, LatticeGamma, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Points 3 x nSpan x nDepth real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< GammaPanl nSpan x nDepth + integer(IntKi), intent(in ) :: iDepthStart !< Start index for depth dimension real(ReKi), dimension(:,:), intent(inout) :: SegPoints !< integer(IntKi), dimension(:,:), intent(inout) :: SegConnct !< real(ReKi), dimension(:), intent(inout) :: SegGamma !< @@ -70,7 +73,7 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, SegPoints, SegConnct, iHeadP0=iHeadP ! Storing ! --- Flattening LatticePoints into SegPoints array, and increment iHeadP ! We will need all the points, we flatten the point array - call LatticeToPoints(LatticePoints, SegPoints, iHeadP) + call LatticeToPoints(LatticePoints, iDepthStart, SegPoints, iHeadP) ! --- Creating segments ! Naming convention for point indices and segments of a panel: @@ -86,7 +89,7 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, SegPoints, SegConnct, ! 2->-3 ! ^ v ! 1-<-4 - do iDepth = 1, nDepth-1 + do iDepth = iDepthStart, nDepth-1 do iSpan = 1, nSpan-1 iseg1 = iHeadP0 + (iSpan-1) +(iDepth-1)*nSpan ! Point 1 iseg2 = iHeadP0 + (iSpan ) +(iDepth-1)*nSpan ! Point 2 @@ -132,6 +135,19 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, SegPoints, SegConnct, end subroutine + subroutine print_mean_4d(M, Label) + real(ReKi), dimension(:,:,:,:), intent(in) :: M + character(len=*), intent(in) :: Label + integer(IntKi) :: i, j, k + real(ReKi), dimension(3) :: U + ! + U(1:3)=0 + do i=1,size(M,4); do j=1,size(M,3); do k=1,size(M,2); + U(1:3)= U(1:3)+ M(1:3, k, j, i) + enddo; enddo; enddo; + U(1:3)=U(1:3)/ (size(M,4)*size(M,3)*size(M,2)) + print'(A20,3F12.4)',trim(Label),U + end subroutine END MODULE FVW_VortexTools From 258d99f1ba508aae483bb731c3d8007971c2f510 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 20 Nov 2019 15:54:49 -0700 Subject: [PATCH 019/190] FVW: add FVW updateStates and FVW_End calls from AD15 --- modules/aerodyn/src/AeroDyn.f90 | 28 ++++++++++------------------ modules/aerodyn/src/FVW.f90 | 5 +++-- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 89959bb81d..c5eb3f8fc3 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1013,13 +1013,13 @@ subroutine AD_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) ErrMsg = "" - ! Place any last minute operations or calculations here: - + ! End the FVW submodule + if (p%WakeMod == WakeMod_FVW ) then + call FVW_End( m%FVW_u, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg ) + endif ! Close files here: -!!! IF (p%UseFVW ) CALL FVW_End( u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, y%FVW, m%FVW, ErrStat, ErrMess ) - ! Destroy the input data: @@ -1108,20 +1108,12 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat call BEMT_UpdateStates(t, n, m%BEMT_u(1), m%BEMT_u(2), p%BEMT, x%BEMT, xd%BEMT, z%BEMT, OtherState%BEMT, p%AFI%AFInfo, m%BEMT, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) -!!! type(FVW_InputType) :: u_FVW(1) !< FVW inputs -!!! REAL(DbKi) :: utimes_FVW(1) !< Times associated with u(:), in seconds -!!! -!!! if (p%UseFVW) then -!!! !if (abs(t-utimes(2))>1e-6 ) then -!!! ! print*,'Problem in AD14 update state, need to adapt which u we provide to FVW' -!!! ! STOP -!!! !endif -!!! ! Setting u(1)%FVW -!!! call AD14_to_FVW_u(u(1),p,u(1)%FVW,ErrStat,ErrMess) -!!! u_FVW(1) = u(1)%FVW -!!! utimes_FVW(1) = utimes(1) -!!! CALL FVW_UpdateStates( t, n, u_FVW, utimes_FVW, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat, ErrMess ) -!!! endif + ! Call the FVW sub module + if (p%WakeMod == WakeMod_FVW) then + ! Note: the setup is handled above in the SetInputs routine + call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + endif call Cleanup() diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 20db032ccb..c1ce7b7717 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -276,7 +276,7 @@ end subroutine FVW_ToString !> This routine is called at the end of the simulation. subroutine FVW_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) - type(FVW_InputType), intent(inout) :: u !< System inputs + type(FVW_InputType), intent(inout) :: u(2) !< System inputs type(FVW_ParameterType), intent(inout) :: p !< Parameters type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states type(FVW_DiscreteStateType), intent(inout) :: xd !< Discrete states @@ -293,7 +293,8 @@ subroutine FVW_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) ! Place any last minute operations or calculations here: ! Close files here: ! Destroy the input data: - call FVW_DestroyInput( u, ErrStat, ErrMsg ) + call FVW_DestroyInput( u(1), ErrStat, ErrMsg ) + call FVW_DestroyInput( u(2), ErrStat, ErrMsg ) ! Destroy the parameter data: call FVW_DestroyParam( p, ErrStat, ErrMsg ) From 2597e8d631e6d51c84217377601f92d2434b81ac Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 16:00:29 -0700 Subject: [PATCH 020/190] FVW: LL part of NW --- modules/aerodyn14/src/FVW.f90 | 18 ++++++++--------- modules/aerodyn14/src/FVW_IO.f90 | 31 +++++++++++------------------ modules/aerodyn14/src/FVW_Subs.f90 | 10 +++++----- modules/aerodyn14/src/FVW_Wings.f90 | 11 ++++++---- 4 files changed, 32 insertions(+), 38 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 3cc92dfbda..26a08747e0 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -137,8 +137,8 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) ErrMsg = "" m%FirstCall = .True. - m%nNW = 0 ! Number of active nearwake panels - m%nFW = 0 ! Number of active farwake panels + m%nNW = iNWStart-1 ! Number of active nearwake panels + m%nFW = 0 ! Number of active farwake panels call AllocAry( m%LE , 3 , p%nSpan+1 , p%nWings, 'Leading Edge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%LE = -999999_ReKi; call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%TE = -999999_ReKi; @@ -231,8 +231,6 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) ErrMsg = "" ! p%nWings = InitInp%NumBl - ! TODO TODO TODO Hack for AD14 mesh that is wrong - !p%nWings = 1 ! NOTE: temporary limitation, all wings have the same nspan p%nSpan = size(InitInp%RElm)-1 @@ -378,20 +376,20 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! -- Propagate wake m%nNW+1+1 do iW=1,p%nWings - do iAge=p%nNWMax+1,2,-1 + do iAge=p%nNWMax+1,iNWStart+1,-1 ! TODO TODO TODO Might need update do iSpan=1,p%nSpan+1 x%r_NW(1:3,iSpan,iAge,iW) = x%r_NW(1:3,iSpan,iAge-1,iW) enddo enddo - x%r_NW(1:3,:,1,iW) = -999.0_ReKi + x%r_NW(1:3,:,1:iNWStart-1,iW) = -999.0_ReKi enddo do iW=1,p%nWings - do iAge=p%nNWMax,2,-1 + do iAge=p%nNWMax,iNWStart+1,-1 do iSpan=1,p%nSpan x%Gamma_NW(iSpan,iAge,iW) = x%Gamma_NW(iSpan,iAge-1,iW) enddo enddo - x%Gamma_NW(:,1,iW) = -999.0_ReKi + x%Gamma_NW(:,1:iNWStart-1,iW) = -999.0_ReKi enddo ! do iAge=1,m%nNW+1+1 ! print*,'iAge',iAge @@ -477,8 +475,6 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! (expensive N^2 call) ! In : x%r_NW, r%r_FW ! Out: m%Vind_NW, m%Vind_FW - m%Vind_NW=-999999._ReKi - m%Vind_FW=-999999._ReKi call WakeInducedVelocities(p, x, m, ErrStat2, ErrMsg2) call print_mean_4d( m%Vind_NW(:,:, 1:m%nNW+1,:), 'Mean induced vel. NW') @@ -500,6 +496,8 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) endif + ! Bound point does not convect + dxdt%r_NW(1:3, :, 1:iNWStart-1, :)=0 end subroutine FVW_CalcContStateDeriv !---------------------------------------------------------------------------------------------------------------------------------- diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index a0067c2164..4a2e35fd19 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -136,12 +136,10 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) endif enddo ! --- Lifting line panels - allocate(Buffer2d(1,nSpan)) do iW=1,nWings write(Label,'(A,A)') 'LL.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - Buffer2d(1,:)=m%Gamma_LL(:,iW) - call WrVTK_Lattice(FileName, m%r_LL(1:3,:,:,iW), Buffer2d) + call WrVTK_Lattice(FileName, m%r_LL(1:3,:,:,iW), m%Gamma_LL(:,iW:iW)) enddo ! --------------------------------------------------------------------------------} ! --- Near wake @@ -150,7 +148,11 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,nWings write(Label,'(A,A)') 'NW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - call WrVTK_Lattice(FileName, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW)) + if (m%nNW==1) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel + call WrVTK_Lattice(FileName, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW)) + else + call WrVTK_Lattice(FileName, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW)) + endif enddo ! --------------------------------------------------------------------------------} ! --- Far wake @@ -165,28 +167,19 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! --- All Segments ! --------------------------------------------------------------------------------{ nP = nWings * ( (nSpan+1)*(nNW+1) ) - if (nNW==0) then - nC=0 ! TODO export of single line not supported by Lattice to Segments - else - nC = nWings * (2*(nSpan+1)*(nNW+1)-nSpan-nNW-2) - endif -! nC = nWings * (2*(nSpan+1)*(nNW+1)-nSpan-nNW-2) -! nP = nP + nWings * (nSpan+1)*2 -! nC = nC + nWings * (2*(nSpan+1)*(2)-nSpan-1-2) + nC = nWings * (2*(nSpan+1)*(nNW+1)-nSpan-nNW-2) allocate(SegConnct(1:2,1:nC)); SegConnct=-1 allocate(SegPoints(1:3,1:nP)); SegPoints=-1 allocate(SegGamma (1:nC)); SegGamma =-1 iHeadP=1 iHeadC=1 do iW=1,nWings - CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + if (m%nNW==1) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel + CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + else + CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + endif enddo -! if (allocated(Buffer2d)) deallocate(Buffer2d) -! allocate(Buffer2d(1,nSpan)) -! do iW=1,nWings -! Buffer2d(1,:)=m%Gamma_LL(:,iW) -! CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), Buffer2d, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) -! enddo Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' CALL WrVTK_Segments(Filename, SegPoints, SegConnct, SegGamma) diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index b467fb1f76..2d98a7a4d0 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -324,11 +324,11 @@ subroutine PackConvectingPoints(p, m, x, Points, nPoints) nPoints=iHeadP-1 end subroutine -subroutine PackPanelsToSegments(p, m, x, iNWStart, SegConnct, SegPoints, SegGamma, nSeg, nSegP) +subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegGamma, nSeg, nSegP) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables type(FVW_ContinuousStateType), intent(in ) :: x !< States - integer(IntKi), intent(in ) :: iNWStart !< Index where we start packing for NW panels + integer(IntKi), intent(in ) :: iDepthStart !< Index where we start packing for NW panels integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation @@ -340,8 +340,8 @@ subroutine PackPanelsToSegments(p, m, x, iNWStart, SegConnct, SegPoints, SegGamm !real(ReKi), dimension(:), allocatable :: SegSmooth !< ! Counting total number of segments TODO add FarWake - nP = p%nWings * ( (p%nSpan+1)*(m%nNW-iNWStart+2) ) - nC = p%nWings * (2*(p%nSpan+1)*(m%nNW-iNWStart+2)-(p%nSpan+1)-(m%nNW-iNWStart+1+1)) + nP = p%nWings * ( (p%nSpan+1)*(m%nNW-iDepthStart+2) ) + nC = p%nWings * (2*(p%nSpan+1)*(m%nNW-iDepthStart+2)-(p%nSpan+1)-(m%nNW-iDepthStart+1+1)) ! nP = nP + p%nWings * (p%nSpan+1)*2 ! nC = nC + p%nWings * (2*(p%nSpan+1)*(2)-p%nSpan-1-2) @@ -357,7 +357,7 @@ subroutine PackPanelsToSegments(p, m, x, iNWStart, SegConnct, SegPoints, SegGamm iHeadP=1 iHeadC=1 do iW=1,p%nWings - CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iNWStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) enddo if ((iHeadP-1)/=nP) then print*,'PackPanelsToSegments: Number of points wrongly estimated',nP, iHeadP-1 diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index d692fdb012..3938069db5 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -178,16 +178,19 @@ subroutine Wings_Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) character(ErrMsgLen) :: ErrMsg2 ! temporary error message integer(IntKi) ::iSpan , iW - ! First panel of NW has same position as last panel of lifting line + ! First panel of NW is the last lifting line panel do iW = 1,p%nWings do iSpan = 1,p%nSpan+1 - x%r_NW(1:3, iSpan, 1, iW) = m%r_LL(1:3, iSpan, 2, iW) + x%r_NW(1:3, iSpan, iNWStart-1, iW) = m%r_LL(1:3, iSpan, 1, iW) ! iAge=1 + x%r_NW(1:3, iSpan, iNWStart , iW) = m%r_LL(1:3, iSpan, 2, iW) ! iAge=2 enddo enddo - ! Circulations of last panel of lifting line are the same as first NW panel + ! First panel of NW is the last lifting line panel + ! Circulations are the same on both side of the TE do iW = 1,p%nWings do iSpan = 1,p%nSpan - x%Gamma_NW(iSpan, 1, iW) = z%Gamma_LL(iSpan,iW) + x%Gamma_NW(iSpan, iNWStart-1, iW) = z%Gamma_LL(iSpan,iW) ! iAge=1 + x%Gamma_NW(iSpan, iNWStart , iW) = z%Gamma_LL(iSpan,iW) ! iAge=2 enddo enddo end subroutine Wings_Map_LL_NW From fd53976bfd273e107d2d8baf72b0e3a2a782a2f7 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 20 Nov 2019 16:28:55 -0700 Subject: [PATCH 021/190] FVW: Added the output of wakes -- not functional --- modules/aerodyn/src/FVW_IO.f90 | 1 + modules/openfast-library/src/FAST_Subs.f90 | 47 ++++++++-------------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index bdcaebf8e4..494290f5b7 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -105,6 +105,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! TimeStamp write(Tstr, '(i' // trim(Num2LStr(Twidth)) //'.'// trim(Num2LStr(Twidth)) // ')') VTKcount +print*,'Printing FVW data to vtk' nSpan = p%nSpan nWings = p%nWings nNW = m%nNW diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index e805f56354..6620662204 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -586,22 +586,6 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, IfW%OtherSt(STATE_CURR), IfW%y, IfW%m, p_FAST%dt_module( MODULE_IfW ), InitOutData_IfW, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -!FIXME:FVW how do I remove this? There is some confusing stuff going on... -! IF ( p_FAST%CompAero == Module_AD14 ) THEN !!KS -- added this section - !TODO TODO ANDY - !AD14%m%FVW%FVW_Wind%InitInputData = InitInData_IfW -!tempo!rary hard coded size while we figure out how many points FVW actually needs: this will be requested by AD14 in addition to the nodes it normally needs - !AD14%m%FVW%FVW_Wind%InitInputData%NumWindPoints = 1000 ! TODO - !CALL InflowWind_Init( AD14%m%FVW%FVW_Wind%InitInputData,AD14%m%FVW%FVW_Wind%InputData,& - ! & AD14%m%FVW%FVW_Wind%ParamData,AD14%m%FVW%FVW_Wind%ContData,& - ! & AD14%m%FVW%FVW_Wind%DiscData,AD14%m%FVW%FVW_Wind%ConstrData,& - ! & AD14%m%FVW%FVW_Wind%OtherData, AD14%m%FVW%FVW_Wind%OutputData,& - ! & AD14%m%FVW%FVW_Wind%MiscData,p_FAST%dt_module(MODULE_IfW ),& - ! & AD14%m%FVW%FVW_Wind%InitOutputData, ErrStat2, ErrMsg2 ) - - !AD14%p%IfW_DT = p_FAST%dt_module( MODULE_IfW ) -! END IF - p_FAST%ModuleInitialized(Module_IfW) = .TRUE. CALL SetModuleSubstepTime(Module_IfW, p_FAST, y_FAST, ErrStat2, ErrMsg2) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -3472,11 +3456,9 @@ SUBROUTINE SetModuleSubstepTime(ModuleID, p_FAST, y_FAST, ErrStat, ErrMsg) ELSE IF ( p_FAST%dt_module( ModuleID ) > p_FAST%dt ) THEN ErrStat = ErrID_Fatal - ! - !ErrMsg = "The "//TRIM(y_FAST%Module_Ver(ModuleID)%Name)//" module time step ("//& - ! TRIM(Num2LStr(p_FAST%dt_module( ModuleID )))// & - ! " s) cannot be larger than FAST time step ("//TRIM(Num2LStr(p_FAST%dt))//" s)." - p_FAST%n_substeps(ModuleID) = 0 !KS -- and added this line + ErrMsg = "The "//TRIM(y_FAST%Module_Ver(ModuleID)%Name)//" module time step ("//& + TRIM(Num2LStr(p_FAST%dt_module( ModuleID )))// & + " s) cannot be larger than FAST time step ("//TRIM(Num2LStr(p_FAST%dt))//" s)." ELSE ! calculate the number of subcycles: p_FAST%n_substeps(ModuleID) = NINT( p_FAST%dt / p_FAST%dt_module( ModuleID ) ) @@ -4973,6 +4955,7 @@ END SUBROUTINE FillOutputAry !---------------------------------------------------------------------------------------------------------------------------------- !> This routine writes all the committed meshes to VTK-formatted files. It doesn't bother with returning an error code. SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + use FVW_IO, only: WrVTK_FVW TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(IN ) :: y_FAST !< Output variables for the glue code @@ -5114,7 +5097,7 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%HubMotion, trim(VTK_path)//'.AD_HubMotion', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth ) !call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%TowerMotion, trim(p_FAST%OutFileRoot)//'.AD_TowerMotion', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2 ) end if - + if (allocated(AD%y%BladeLoad)) then DO K=1,NumBl @@ -5131,6 +5114,11 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O !call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%BladeMotion(K), trim(p_FAST%OutFileRoot)//'.AD_BladeMotion'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2 ) END DO end if + ! Free wake +!FIXME: Should the wake info be in a different routine? + if (allocated(AD%m%FVW_u(1)%WingsMesh)) then + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + end if END IF ! HydroDyn @@ -5218,7 +5206,6 @@ END SUBROUTINE WrVTK_AllMeshes !> This routine writes a minimal subset of meshes (enough to visualize the turbine) to VTK-formatted files. It doesn't bother with !! returning an error code. SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) -! use FVW_IO, only: WrVTK_FVW TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(IN ) :: y_FAST !< Output variables for the glue code @@ -5330,7 +5317,7 @@ END SUBROUTINE WrVTK_BasicMeshes !> This routine writes a minimal subset of meshes with surfaces to VTK-formatted files. It doesn't bother with !! returning an error code. SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) -! use FVW_IO, only: WrVTK_FVW + use FVW_IO, only: WrVTK_FVW REAL(DbKi), INTENT(IN ) :: t_global !< Current global time TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code @@ -5412,12 +5399,12 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, A END DO END IF - ! Free wake -!FIXME:FVW -! IF ( p_FAST%CompAero == Module_AD14 ) THEN ! These meshes may have airfoil data associated with nodes... -! call WrVTK_FVW(AD14%p%FVW, AD14%x(1)%FVW, AD14%z(1)%FVW, AD14%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) -! END IF - +! Free wake +!FIXME: is there a better way of checking? + if (allocated(AD%m%FVW_u(1)%WingsMesh)) then + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + end if + ! Tower motions call MeshWrVTK_Ln2Surface (p_FAST%TurbinePos, ED%Output(1)%TowerLn2Mesh, trim(VTK_path)//'.TowerSurface', & y_FAST%VTK_count, OutputFields, ErrStat2, ErrMsg2, Twidth, p_FAST%VTK_Surface%NumSectors, p_FAST%VTK_Surface%TowerRad ) From b8e94285953f8310dd617e73135132a449353248 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 17:36:06 -0700 Subject: [PATCH 022/190] FVW: Number of panels from input file --- modules/aerodyn14/src/FVW.f90 | 42 +++--- modules/aerodyn14/src/FVW_BiotSavart.f90 | 5 +- modules/aerodyn14/src/FVW_IO.f90 | 9 ++ modules/aerodyn14/src/FVW_Subs.f90 | 152 +++++++++++++++------- modules/aerodyn14/src/FVW_Types.f90 | 14 ++ modules/aerodyn14/src/FVW_VortexTools.f90 | 14 ++ modules/aerodyn14/src/Registry-FVW.txt | 2 + 7 files changed, 169 insertions(+), 69 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 26a08747e0..4404e5dde3 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -85,9 +85,11 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! Read and parse the input file here to get other parameters and info CALL FVW_ReadInputFile(InitInp%FVWFileName, p, InputFileData, ErrStat2, ErrMsg2); if(Failed()) return + ! Trigger required before allocations + p%nNWMax=InputFileData%nNWPanels+1 ! +1 since LL panel included in NW + p%nFWMax=max(InputFileData%nFWPanels,0) + ! Initialize Misc Vars (may depend on input file) - p%nNWMax=100 ! TODO - p%nFWMax=100 ! TODO CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return ! Preliminary meshing of the wings (may depend on input file) @@ -164,10 +166,10 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%Vwnd_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_NW= -999_ReKi; call AllocAry( m%Vind_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; - m%Vwnd_NW(1,:,:,:)= 8 + m%Vwnd_NW(1,:,:,:)= 10 m%Vwnd_NW(2,:,:,:)= 0 m%Vwnd_NW(3,:,:,:)= 0 - m%Vwnd_FW(1,:,:,:)= 8 + m%Vwnd_FW(1,:,:,:)= 10 m%Vwnd_FW(2,:,:,:)= 0 m%Vwnd_FW(3,:,:,:)= 0 end subroutine FVW_InitMiscVars @@ -489,8 +491,8 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) else - call print_mean_4d( m%Vwnd_NW(:,1:m%nNW+1,:,:), 'Mean wind vel. NW') - call print_mean_4d( m%Vwnd_FW(:,1:m%nFW+1,:,:), 'Mean wind vel. FW') + call print_mean_4d( m%Vwnd_NW(:,:,1:m%nNW+1,:), 'Mean wind vel. NW') + call print_mean_4d( m%Vwnd_FW(:,:,1:m%nFW+1,:), 'Mean wind vel. FW') ! --- Vortex points are convected with the free stream dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) @@ -604,6 +606,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables + integer(IntKi) :: iSpan, iW integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'FVW_CalcOutput' @@ -622,19 +625,23 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg endif if (.not. allocated(y%Vind)) then - call AllocAry( y%Vind , 3, p%nSpan, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); + call AllocAry( y%Vind , 3, p%nSpan+1, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO potentially nSpan+1 for AD15 if(Failed()) return endif ! Returned guessed locations where wind will be required CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return - !if (m%FirstCall) then - !endif + ! Induction on the lifting line control point + call LiftingLineInducedVelocities(p, x, m, ErrStat2, ErrMsg2); if(Failed()) return - ! For now returning 0 - y%Vind(1,:,:) = 0.0_ReKi - y%Vind(2,:,:) = 0.0_ReKi - y%Vind(3,:,:) = 0.0_ReKi + ! Interpolation to AeroDyn radial station TODO TODO TODO + y%Vind(1:3,:,:) = 0.0_ReKi + do iW=1,p%nWings + do iSpan=1,p%nSpan + y%Vind(1:3,iSpan,iW) = m%Vind_LL(1:3,iSpan,iW) + enddo + enddo + call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') ! We don't propagate the "Old"-> "New" if update states was not called once ! This is introduced since at init, CalcOutput is called before UpdateState @@ -643,18 +650,9 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg endif contains - !========================================================================== - !> Computes induction on the lifting line (3/4 chord point) - ! Interpolate the values at the radial station of AeroDyn -! subroutine CalcInduction_LL() -! -! end subroutine CalcInduction_LL - subroutine PrepareNextTimeStep() ! --- Propagate wake - ! TODO - ! m%nNW=m%nNW+1 if (m%nNW>p%nNWMax) m%nNW = p%nNWMax diff --git a/modules/aerodyn14/src/FVW_BiotSavart.f90 b/modules/aerodyn14/src/FVW_BiotSavart.f90 index d882b51720..a12231693b 100644 --- a/modules/aerodyn14/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn14/src/FVW_BiotSavart.f90 @@ -9,6 +9,7 @@ module FVW_BiotSavart real(ReKi),parameter :: MINDENOM=1e-15_ReKi real(ReKi),parameter :: MINNORMSIMP=1e-6_ReKi + integer(IntKi), parameter :: idSegSmoothNone = 0 integer(IntKi), parameter :: idSegSmoothRankine = 1 integer(IntKi), parameter :: idSegSmoothLambOseen = 2 integer(IntKi), parameter :: idSegSmoothVatistas = 3 @@ -94,7 +95,7 @@ subroutine ui_seg_11(DeltaPa, DeltaPb, Gam, SmoothModel , SmoothParam, Ui) !--- Normal Procedure ! smooth model select case (SmoothModel) ! - case ( 0 ) !! No vortex core model + case ( idSegSmoothNone ) !! No vortex core model Kv=1.0_ReKi case ( idSegSmoothRankine ) !!Rankine - t<=>rc ! orthogonal distance r1xr2/r0 @@ -261,7 +262,7 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & ! smooth model h2 = 0.0_ReKi select case ((SmoothModel)) ! - case ( 0 ) !! No vortex core model + case ( idSegSmoothNone ) !! No vortex core model Kv=1.0_ReKi case ( idSegSmoothRankine ) !!Rankine - t<=>rc ! orthogonal distance r1xr2/r0 diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index 4a2e35fd19..547a135aee 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -46,6 +46,10 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%PrescribedPolar ,'PrescribedPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return + !------------------------ WAKE OPTIONS ------------------------------------------- + CALL ReadCom(UnIn,FileName, 'Wake options header', ErrStat2, ErrMsg2 ); if(Failed()) return + CALL ReadVar(UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return ! Post pro and validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) @@ -54,6 +58,8 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check( Inp%IntMethod/=idEuler1 , 'Time integration method not implemented')) return + if (Check( Inp%nNWPanels<0 , 'Number of near wake panels must be posivive')) return + call CleanUp() CONTAINS @@ -107,6 +113,9 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) integer(IntKi) :: iHeadC, iHeadP, nC, nP !real(ReKi), dimension(:), allocatable :: SegSmooth !< + ! + call set_vtk_binary_format(.false.) + ! TimeStamp write(Tstr, '(i' // trim(Num2LStr(Twidth)) //'.'// trim(Num2LStr(Twidth)) // ')') VTKcount diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index 2d98a7a4d0..f8d6befa60 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -283,46 +283,8 @@ subroutine DistributeRequestedWind(V_wind, x, p, m, ErrStat, ErrMsg ) end subroutine DistributeRequestedWind -!> Distribute the induced velocity to the proper location -subroutine UnPackInducedVelocity(p, m, x, Uind) - type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables - type(FVW_ContinuousStateType), intent(in ) :: x !< States - real(ReKi), dimension(:,:) , intent(in ) :: Uind !< Induced velocity - ! Local - integer(IntKi) :: iW, iHeadP - iHeadP=1 - do iW=1,p%nWings - CALL VecToLattice(Uind, 1, m%Vind_NW(:,:,1:m%nNW+1,iW), iHeadP) - enddo - if ((iHeadP-1)/=size(Uind,2)) then - print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 - STOP - endif -end subroutine -!> Distribute the induced velocity to the convecting points -subroutine PackConvectingPoints(p, m, x, Points, nPoints) - type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables - type(FVW_ContinuousStateType), intent(in ) :: x !< States - real(ReKi), dimension(:,:) , intent(inout) :: Points !< Points packed - integer(IntKi), intent( out) :: nPoints !< Number of points packed - ! Local - integer(IntKi) :: iW, iHeadP - - ! --- Compute number of convecting points - iHeadP=1 - do iW=1,p%nWings - CALL LatticeToPoints(x%r_NW(1:3,:,1:m%nNW+1,iW), 1, Points, iHeadP) - enddo - if ((iHeadP-1)/=size(Points,2)) then - print*,'PackConvectingPoints: Number of points wrongly estimated',size(Points,2), iHeadP-1 - STOP - endif - nPoints=iHeadP-1 -end subroutine subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegGamma, nSeg, nSegP) type(FVW_ParameterType), intent(in ) :: p !< Parameters @@ -371,13 +333,16 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG nSegP = iHeadP-1 end subroutine PackPanelsToSegments +!> Compute induced velocities from all vortex elements onto all the vortex elements +!! In : x%r_NW, x%r_FW, x%Gamma_NW, x%Gamma_FW +!! Out: m%Vind_NW, m%Vind_FW subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables ! Local variables integer(IntKi) :: SmoothModel !< TODO input file parameter - integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs + integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs, iHeadP integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation @@ -403,12 +368,9 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) allocate(Uind(1:3,1:nCPs)) Uind=0.0_ReKi !< important due to side effects of ui_seg ! --- - call PackConvectingPoints(p, m, x, CPs, nCPs) - print*,'Number of points packed for Convection:',nCPs, nSegP - CALL ui_seg( 1, nCPs, nCPs, CPs, & - 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, & - SmoothModel, SegSmooth, Uind) - call UnPackInducedVelocity(p, m, x, Uind) + call PackConvectingPoints() + call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, SmoothModel, SegSmooth, Uind) + call UnPackInducedVelocity() deallocate(Uind) deallocate(CPs) @@ -416,8 +378,108 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) deallocate(SegGamma) deallocate(SegPoints) deallocate(SegSmooth) +contains + !> Pack all the points that convect + subroutine PackConvectingPoints() + iHeadP=1 + do iW=1,p%nWings + CALL LatticeToPoints(x%r_NW(1:3,:,1:m%nNW+1,iW), 1, CPs, iHeadP) + enddo + if ((iHeadP-1)/=size(CPs,2)) then + print*,'PackConvectingPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 + STOP + endif + nCPs=iHeadP-1 + print*,'Number of points packed for Convection:',nCPs, nSegP + end subroutine + !> Distribute the induced velocity to the proper location + subroutine UnPackInducedVelocity() + iHeadP=1 + do iW=1,p%nWings + CALL VecToLattice(Uind, 1, m%Vind_NW(:,:,1:m%nNW+1,iW), iHeadP) + enddo + if ((iHeadP-1)/=size(Uind,2)) then + print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 + STOP + endif + end subroutine + end subroutine +!> Compute induced velocities from all vortex elements onto the lifting line control points +!! In : x%r_NW, x%r_FW, x%Gamma_NW, x%Gamma_FW +!! Out: m%Vind_LL +subroutine LiftingLineInducedVelocities(p, x, m, ErrStat, ErrMsg) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< States + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + ! Local variables + integer(IntKi) :: SmoothModel !< TODO input file parameter + integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs, iHeadP + integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity + real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points + real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation + real(ReKi), dimension(:) , allocatable :: SegSmooth !< Segment smooth parameter + real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints + real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + m%Vind_NW = -9999._ReKi !< Safety + m%Vind_FW = -9999._ReKi !< Safety + + ! --- Packing all vortex elements into a list of segments + call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) + print*,'Number of segments',nSeg, 'Number of points',nSegP + + ! --- Computing induced velocity + allocate(SegSmooth(1:nSeg)); + SegSmooth=10 + SmoothModel=idSegSmoothLambOseen + + nCPs=p%nWings * p%nSpan + + allocate(CPs (1:3,1:nCPs)) + allocate(Uind(1:3,1:nCPs)) + Uind=0.0_ReKi !< important due to side effects of ui_seg + ! --- + call PackLiftingLinePoints() + call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, SmoothModel, SegSmooth, Uind) + call UnPackLiftingLineVelocities() + + deallocate(Uind) + deallocate(CPs) + deallocate(SegConnct) + deallocate(SegGamma) + deallocate(SegPoints) + deallocate(SegSmooth) +contains + !> Pack all the control points + subroutine PackLiftingLinePoints() + iHeadP=1 + do iW=1,p%nWings + CALL LatticeToPoints(m%CP_LL(1:3,:,iW:iW), 1, CPs, iHeadP) + enddo + if ((iHeadP-1)/=size(CPs,2)) then + print*,'PackLLPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 + STOP + endif + nCPs=iHeadP-1 + print*,'Number of points packed for LL:',nCPs, nSegP + end subroutine + + !> Distribute the induced velocity to the proper location + subroutine UnPackLiftingLineVelocities() + iHeadP=1 + do iW=1,p%nWings + CALL VecToLattice(Uind, 1, m%Vind_LL(1:3,:,iW:iW), iHeadP) + enddo + if ((iHeadP-1)/=size(Uind,2)) then + print*,'UnPackLiftingLineVelocities: Number of points wrongly estimated',size(Uind,2), iHeadP-1 + STOP + endif + end subroutine +end subroutine diff --git a/modules/aerodyn14/src/FVW_Types.f90 b/modules/aerodyn14/src/FVW_Types.f90 index 7a14d9d6b9..a0fa91fd6b 100644 --- a/modules/aerodyn14/src/FVW_Types.f90 +++ b/modules/aerodyn14/src/FVW_Types.f90 @@ -146,6 +146,8 @@ MODULE FVW_Types REAL(ReKi) :: FreeWakeStart !< Time when wake starts convecting (rolling up) [s] REAL(ReKi) :: FullCirculationStart !< Time when the circulation is full [s] INTEGER(IntKi) :: PrescribedPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] + INTEGER(IntKi) :: nNWPanels !< Number of nw panels [-] + INTEGER(IntKi) :: nFWPanels !< Number of fw panels [-] END TYPE FVW_InputFile ! ======================= ! ========= FVW_InitOutputType ======= @@ -4129,6 +4131,8 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%FreeWakeStart = SrcInputFileData%FreeWakeStart DstInputFileData%FullCirculationStart = SrcInputFileData%FullCirculationStart DstInputFileData%PrescribedPolar = SrcInputFileData%PrescribedPolar + DstInputFileData%nNWPanels = SrcInputFileData%nNWPanels + DstInputFileData%nFWPanels = SrcInputFileData%nFWPanels END SUBROUTINE FVW_CopyInputFile SUBROUTINE FVW_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) @@ -4187,6 +4191,8 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_BufSz = Re_BufSz + 1 ! FreeWakeStart Re_BufSz = Re_BufSz + 1 ! FullCirculationStart Int_BufSz = Int_BufSz + 1 ! PrescribedPolar + Int_BufSz = Int_BufSz + 1 ! nNWPanels + Int_BufSz = Int_BufSz + 1 ! nFWPanels IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4236,6 +4242,10 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%PrescribedPolar Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nNWPanels + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWPanels + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackInputFile SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4292,6 +4302,10 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Re_Xferred = Re_Xferred + 1 OutData%PrescribedPolar = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%nNWPanels = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%nFWPanels = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackInputFile SUBROUTINE FVW_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn14/src/FVW_VortexTools.f90 index 2f485a6bad..6dcb54102d 100644 --- a/modules/aerodyn14/src/FVW_VortexTools.f90 +++ b/modules/aerodyn14/src/FVW_VortexTools.f90 @@ -149,5 +149,19 @@ subroutine print_mean_4d(M, Label) print'(A20,3F12.4)',trim(Label),U end subroutine + subroutine print_mean_3d(M, Label) + real(ReKi), dimension(:,:,:), intent(in) :: M + character(len=*), intent(in) :: Label + integer(IntKi) :: i, j + real(ReKi), dimension(3) :: U + ! + U(1:3)=0 + do i=1,size(M,3); do j=1,size(M,2) + U(1:3)= U(1:3)+ M(1:3, j, i) + enddo; enddo; + U(1:3)=U(1:3)/ (size(M,3)*size(M,2)) + print'(A20,3F12.4)',trim(Label),U + end subroutine + END MODULE FVW_VortexTools diff --git a/modules/aerodyn14/src/Registry-FVW.txt b/modules/aerodyn14/src/Registry-FVW.txt index 88279144b8..95dbd4350b 100644 --- a/modules/aerodyn14/src/Registry-FVW.txt +++ b/modules/aerodyn14/src/Registry-FVW.txt @@ -108,6 +108,8 @@ typedef ^ ^ LOGICAL typedef ^ ^ ReKi FreeWakeStart - - - "Time when wake starts convecting (rolling up)" s typedef ^ ^ ReKi FullCirculationStart - - - "Time when the circulation is full" s typedef ^ ^ IntKi PrescribedPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - +typedef ^ ^ IntKi nNWPanels - - - "Number of nw panels" - +typedef ^ ^ IntKi nFWPanels - - - "Number of fw panels" - #.......... InitOutputType ...... # FVW_InitOutputType From aae7ddad76b81d1f1d92b9fd82ccd2387619e8f6 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 18:12:12 -0700 Subject: [PATCH 023/190] FVW: Regularization options in input file --- modules/aerodyn14/src/FVW.f90 | 3 + modules/aerodyn14/src/FVW_BiotSavart.f90 | 142 ++++++++++++----------- modules/aerodyn14/src/FVW_IO.f90 | 9 +- modules/aerodyn14/src/FVW_Subs.f90 | 53 +++++---- modules/aerodyn14/src/FVW_Types.f90 | 42 +++++++ modules/aerodyn14/src/Registry-FVW.txt | 6 + 6 files changed, 164 insertions(+), 91 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 4404e5dde3..c1ec8f5ab4 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -261,6 +261,9 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%FreeWakeStart = InputFileData%FreeWakeStart p%PrescribedPolar = InputFileData%PrescribedPolar p%FullCirculationStart = InputFileData%FullCirculationStart + p%RegFunction = InputFileData%RegFunction + p%WakeRegMethod = InputFileData%WakeRegMethod + p%WakeRegFactor = InputFileData%WakeRegFactor if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) if (InputFileData%CirculationMethod==idCircPrescribed) then diff --git a/modules/aerodyn14/src/FVW_BiotSavart.f90 b/modules/aerodyn14/src/FVW_BiotSavart.f90 index a12231693b..28ffc1bc3f 100644 --- a/modules/aerodyn14/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn14/src/FVW_BiotSavart.f90 @@ -9,25 +9,27 @@ module FVW_BiotSavart real(ReKi),parameter :: MINDENOM=1e-15_ReKi real(ReKi),parameter :: MINNORMSIMP=1e-6_ReKi - integer(IntKi), parameter :: idSegSmoothNone = 0 - integer(IntKi), parameter :: idSegSmoothRankine = 1 - integer(IntKi), parameter :: idSegSmoothLambOseen = 2 - integer(IntKi), parameter :: idSegSmoothVatistas = 3 - integer(IntKi), parameter :: idSegSmoothCompact = 4 - integer(IntKi), parameter :: idSegSmoothCompactDim = 5 + integer(IntKi), parameter :: idRegNone = 0 + integer(IntKi), parameter :: idRegRankine = 1 + integer(IntKi), parameter :: idRegLambOseen = 2 + integer(IntKi), parameter :: idRegVatistas = 3 + integer(IntKi), parameter :: idRegCompact = 4 + integer(IntKi), parameter :: idRegCompactDim = 5 + integer(IntKi), parameter, dimension(5) :: idRegVALID = (/idRegNone,idRegRankine,idRegLambOseen,idRegVatistas,idRegCompact/) + contains !> Induced velocity from one segment at one control points -subroutine ui_seg_11(DeltaPa, DeltaPb, Gam, SmoothModel , SmoothParam, Ui) +subroutine ui_seg_11(DeltaPa, DeltaPb, Gam, RegModel , RegParam, Ui) implicit none ! Input/output arguments - real(ReKi), dimension(3), intent(in) :: DeltaPa !< 3 x 1 Pcp-P1 - real(ReKi), dimension(3), intent(in) :: DeltaPb !< 3 x 1 Pcp-P2 - real(ReKi), intent(in) :: Gam !< - integer, intent(in) :: SmoothModel !< - real(ReKi), intent(in) :: SmoothParam !< - real(ReKi), dimension(3),intent(out) :: Ui !< No Side effects + real(ReKi), dimension(3), intent(in) :: DeltaPa !< 3 x 1 Pcp-P1 + real(ReKi), dimension(3), intent(in) :: DeltaPb !< 3 x 1 Pcp-P2 + real(ReKi), intent(in) :: Gam !< Circulation + integer, intent(in) :: RegModel !< Regularization model + real(ReKi), intent(in) :: RegParam !< Regularization parameter + real(ReKi), dimension(3),intent(out) :: Ui !< Induced velocity (no side effects) ! Variables declaration real(ReKi),dimension(3) :: crossprod !< real(ReKi),dimension(3) :: D !< @@ -46,7 +48,7 @@ subroutine ui_seg_11(DeltaPa, DeltaPb, Gam, SmoothModel , SmoothParam, Ui) real(ReKi) :: xb !< real(ReKi) :: yb !< real(ReKi) :: zb !< - real(ReKi) :: SmoothParam2 !< Square of viscous param + real(ReKi) :: RegParam2 !< Square of viscous param real(ReKi) :: exp_value !< real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) ! @@ -85,7 +87,7 @@ subroutine ui_seg_11(DeltaPa, DeltaPb, Gam, SmoothModel , SmoothParam, Ui) endif endif Ui(1:3)=0.0_ReKi - if( SmoothModel==0) then + if( RegModel==0) then return end if else @@ -94,58 +96,58 @@ subroutine ui_seg_11(DeltaPa, DeltaPb, Gam, SmoothModel , SmoothParam, Ui) ! TODO far distance !--- Normal Procedure ! smooth model - select case (SmoothModel) ! - case ( idSegSmoothNone ) !! No vortex core model + select case (RegModel) ! + case ( idRegNone ) !! No vortex core model Kv=1.0_ReKi - case ( idSegSmoothRankine ) !!Rankine - t<=>rc + case ( idRegRankine ) !!Rankine - t<=>rc ! orthogonal distance r1xr2/r0 h2 = norm2_crossprod/ norm2_r0 - SmoothParam2=SmoothParam**2 - if (h2rc + case ( idRegLambOseen ) !!Lamb-Oseen - vsic_param<=>rc ! orthogonal distance r1xr2/r0 h2 = norm2_crossprod/ norm2_r0 - SmoothParam2=SmoothParam**2 - exp_value = -1.25643_ReKi*(h2)/(SmoothParam2) + RegParam2=RegParam**2 + exp_value = -1.25643_ReKi*(h2)/(RegParam2) if(exp_valuerc + case ( idRegVatistas ) !!Vatistas n=2 - RegParam<=>rc ! orthogonal distance r1xr2/r0 h2 = norm2_crossprod/ norm2_r0 - SmoothParam2=SmoothParam**2 + RegParam2=RegParam**2 ! h = (norm_a+norm_b)/2; - Kv = h2/sqrt(SmoothParam2**2+h2**2) - case ( idSegSmoothCompact ) !!Cut-off radius as function of length: delta^2*norm(r0)^2 + Kv = h2/sqrt(RegParam2**2+h2**2) + case ( idRegCompact ) !!Cut-off radius as function of length: delta^2*norm(r0)^2 h2 = norm2_crossprod/ norm2_r0 Kv=1.0_ReKi ! delta*norm(r0)^2 - denominator=denominator+SmoothParam*norm2_r0 - SmoothParam2=SmoothParam**2 ! TODO - case ( idSegSmoothCompactDim ) !!Cut-off radius dimension fixed: (delta l_0)^2 + denominator=denominator+RegParam*norm2_r0 + RegParam2=RegParam**2 ! TODO + case ( idRegCompactDim ) !!Cut-off radius dimension fixed: (delta l_0)^2 h2 = norm2_crossprod/ norm2_r0 Kv=1.0_ReKi ! (delta l_0)^2 - denominator=denominator+SmoothParam - SmoothParam2=SmoothParam**2 ! TODO - case ( 33 ) !!Vatistas n=2 - SmoothParamt<=>rc + denominator=denominator+RegParam + RegParam2=RegParam**2 ! TODO + case ( 33 ) !!Vatistas n=2 - RegParamt<=>rc ! See Hoydonck 2012. and Script MainRingSensitivityN ! Not matture enough h = (norm_a+norm_b)/2.0_ReKi - SmoothParam2=SmoothParam**2 + RegParam2=RegParam**2 if(h<2*sqrt(norm2_r0)) then ! use Vatistas n=2, normal one h2 = norm2_crossprod/norm2_r0 else h2 = 1._ReKi ! TODO endif - Kv = (h2)/sqrt(SmoothParam2**2+h2**2) + Kv = (h2)/sqrt(RegParam2**2+h2**2) case DEFAULT Kv=1.0_ReKi end select @@ -165,7 +167,7 @@ end subroutine ui_seg_11 !! subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & iSegStart, iSegEnd, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, & - SmoothModel, SmoothParam, Uind_out) + RegModel, RegParam, Uind_out) use OMP_LIB implicit none real(ReKi), dimension(3,nCPsTot), intent(in) :: CPs !< Control points @@ -179,8 +181,8 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & integer(IntKi),intent(in) :: iSegEnd !< Index in SegConnct, and SegGamma where we end integer(IntKi), intent(in) :: nSegTot !< Total number of segments integer(IntKi), intent(in) :: nSegPTot !< Total number of segment points - integer(IntKi), intent(in) :: SmoothModel !< Smooth model - real(ReKi), dimension(nSegTot), intent(in) :: SmoothParam !< Smooth parameter + integer(IntKi), intent(in) :: RegModel !< Regularization model + real(ReKi), dimension(nSegTot), intent(in) :: RegParam !< Regularization parameter real(ReKi), dimension(3,nCPsTot), intent(inout) :: Uind_out !< Induced velocity vector - Side effects!!! ! Variables integer(IntKi) :: icp,is @@ -191,7 +193,7 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & real(ReKi), dimension(3) :: DP1, DP2 !< real(ReKi) :: norm2_r0 !< real(ReKi) :: Gam !< - real(ReKi) :: SmoothParam1 !< + real(ReKi) :: RegParam1 !< ! Variables declaration real(ReKi),dimension(3) :: crossprod !< real(ReKi) :: denom !< @@ -208,14 +210,14 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & real(ReKi) :: xb !< real(ReKi) :: yb !< real(ReKi) :: zb !< - real(ReKi) :: SmoothParam2 !< Square of viscous param + real(ReKi) :: RegParam2 !< Square of viscous param real(ReKi) :: exp_value !< real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) !OMP PARALLEL default(shared) !OMP do private(& - !OMP& icp,is,Uind,P1,P2,DP1,DP2,norm2_r0,Gam,SmoothParam,& - !OMP& crossprod,denom,denominator,h2,h,Kv,norm_a,norm_b,norm2_crossprod,xa,ya,za,xb,yb,zb,SmoothParam2,exp_value& + !OMP& icp,is,Uind,P1,P2,DP1,DP2,norm2_r0,Gam,RegParam,& + !OMP& crossprod,denom,denominator,h2,h,Kv,norm_a,norm_b,norm2_crossprod,xa,ya,za,xb,yb,zb,RegParam2,exp_value& !OMP& ) schedule(runtime) ! loop on CPs do icp=iCPStart,iCPEnd @@ -227,7 +229,7 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & DP1 = CPs(:,icp)-P1; DP2 = CPs(:,icp)-P2 Gam = SegGamma(is) - SmoothParam1= SmoothParam(is) + RegParam1= RegParam(is) ! --- inlining of ui_seg_11 xa=DP1(1) ya=DP1(2) @@ -261,49 +263,49 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & !--- Normal Procedure ! smooth model h2 = 0.0_ReKi - select case ((SmoothModel)) ! - case ( idSegSmoothNone ) !! No vortex core model + select case ((RegModel)) ! + case ( idRegNone ) !! No vortex core model Kv=1.0_ReKi - case ( idSegSmoothRankine ) !!Rankine - t<=>rc + case ( idRegRankine ) !!Rankine - t<=>rc ! orthogonal distance r1xr2/r0 h2 = norm2_crossprod/ norm2_r0 - SmoothParam2 = SmoothParam1**2 - if (h2rc + case ( idRegLambOseen ) !!Lamb-Oseen - vsic_param<=>rc ! orthogonal distance r1xr2/r0 h2 = norm2_crossprod/ norm2_r0 - SmoothParam2 = SmoothParam1**2 - exp_value = -1.25643_ReKi*(h2)/(SmoothParam2) + RegParam2 = RegParam1**2 + exp_value = -1.25643_ReKi*(h2)/(RegParam2) if(exp_valuerc + case ( idRegVatistas ) !!Vatistas n=2 - RegParam<=>rc ! orthogonal distance r1xr2/r0 h2 = norm2_crossprod/ norm2_r0 - SmoothParam2 = SmoothParam1**2 + RegParam2 = RegParam1**2 ! h = (norm_a+norm_b)/2; - Kv = h2/sqrt(SmoothParam2**2+h2**2) - case ( idSegSmoothCompact ) !!Cut-off radius as function of length: delta^2*norm(r0)^2 + Kv = h2/sqrt(RegParam2**2+h2**2) + case ( idRegCompact ) !!Cut-off radius as function of length: delta^2*norm(r0)^2 Kv=1.0_ReKi - denominator=denominator+SmoothParam1*SmoothParam1*norm2_r0 - case ( idSegSmoothCompactDim ) !!Cut-off radius dimension fixed: (delta l_0)^2 + denominator=denominator+RegParam1*RegParam1*norm2_r0 + case ( idRegCompactDim ) !!Cut-off radius dimension fixed: (delta l_0)^2 Kv=1.0_ReKi - denominator=denominator+SmoothParam1*SmoothParam1 - case ( 33 ) !!Vatistas n=2 - SmoothParamt<=>rc + denominator=denominator+RegParam1*RegParam1 + case ( 33 ) !!Vatistas n=2 - RegParamt<=>rc ! See Hoydonck 2012 h = (norm_a+norm_b)/2.0_ReKi - SmoothParam2 = SmoothParam1**2 + RegParam2 = RegParam1**2 if(h<2*sqrt(norm2_r0)) then ! use Vatistas n=2, normal one h2 = norm2_crossprod/norm2_r0 end if - Kv = (h2)/sqrt(SmoothParam2**2+h2**2) + Kv = (h2)/sqrt(RegParam2**2+h2**2) case default Kv=1.0_ReKi end select @@ -323,14 +325,14 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & !> Velocity induced by one vortex quad on nCPs Control Points -subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, SmoothModel, SmoothParam, Uind) +subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, RegModel, RegParam, Uind) ! Arguments declarations integer, intent(in) :: nCPs !< real(ReKi), dimension(3,nCPs), intent(in) :: CPs !< real(ReKi), dimension(3), intent(in) :: P1,P2,P3,P4 !< Coordinates of vortex quadrilateral real(ReKi), intent(in) :: Gamm - integer(IntKi) , intent(in) :: SmoothModel !< Most likely should be 0 - real(ReKi), intent(in) :: SmoothParam !< + integer(IntKi) , intent(in) :: RegModel !< Regularization model (e.g. LambOseen) + real(ReKi), intent(in) :: RegParam !< Regularization parameter real(ReKi), dimension(3,nCPs), intent(out) :: Uind !< no side effects!!! ! Variable declarations real(ReKi), dimension(3) :: CP !< @@ -345,19 +347,19 @@ subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, SmoothModel, SmoothParam, CP(1:3)=CPs(1:3,icp) ! 1-2 segment DP1=CP-P1; DP2=CP-P2; - call ui_seg_11 ( DP1, DP2, Gamm, SmoothModel, SmoothParam, Uindtmp) + call ui_seg_11 ( DP1, DP2, Gamm, RegModel, RegParam, Uindtmp) Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) ! 3-4 segment DP1=CP-P3; DP2=CP-P4; - call ui_seg_11 ( DP1, DP2, Gamm, SmoothModel, SmoothParam, Uindtmp) + call ui_seg_11 ( DP1, DP2, Gamm, RegModel, RegParam, Uindtmp) Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) ! 2-3 segment DP1=CP-P2; DP2=CP-P3; - call ui_seg_11 ( DP1, DP2, Gamm, SmoothModel, SmoothParam, Uindtmp) + call ui_seg_11 ( DP1, DP2, Gamm, RegModel, RegParam, Uindtmp) Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) ! 4-1 segment DP1=CP-P4; DP2=CP-P1; - call ui_seg_11 ( DP1, DP2, Gamm, SmoothModel, SmoothParam, Uindtmp) + call ui_seg_11 ( DP1, DP2, Gamm, RegModel, RegParam, Uindtmp) Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) end do ! loop on CPs !OMP END DO diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index 547a135aee..7c5d831f9c 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -50,8 +50,11 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadCom(UnIn,FileName, 'Wake options header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%WakeRegFactor ,'WakeRegFactor' ,'',ErrStat2,ErrMsg2); if(Failed())return - ! Post pro and validation of inputs + ! --- Validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) if (Check(.not.(ANY((/idCircPrescribed,idCircPolarData/)==Inp%CirculationMethod)), 'Circulation method not implemented')) return @@ -60,6 +63,10 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check( Inp%nNWPanels<0 , 'Number of near wake panels must be posivive')) return + if (Check(.not.(ANY(idRegVALID ==Inp%RegFunction )), 'Regularization function not implemented')) return + if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method not implemented')) return + if (Check(Inp%WakeRegFactor<0 , 'Wake regularization factor should be positive')) return + call CleanUp() CONTAINS diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index f8d6befa60..ec620ba67b 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -21,6 +21,11 @@ module FVW_SUBS integer(IntKi), parameter :: idAB4 = 2 integer(IntKi), parameter :: idABM4 = 3 integer(IntKi), parameter :: idEuler1 = 5 + ! Regularization Method + integer(IntKi), parameter :: idRegConstant = 0 + integer(IntKi), parameter :: idRegStretching = 1 + integer(IntKi), parameter :: idRegAge = 2 + integer(IntKi), parameter, dimension(3) :: idRegMethodVALID = (/idRegConstant,idRegStretching,idRegAge/) ! Implementation integer(IntKi), parameter :: iNWStart=2 !< Index in r%NW where the near wake start (if >1 then the Wing panels are included in r_NW) @@ -341,12 +346,11 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables ! Local variables - integer(IntKi) :: SmoothModel !< TODO input file parameter integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs, iHeadP - integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity - real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points - real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation - real(ReKi), dimension(:) , allocatable :: SegSmooth !< Segment smooth parameter + integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity + real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points + real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation + real(ReKi), dimension(:) , allocatable :: SegEpsilon !< Segment regularization parameter real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi), intent( out) :: ErrStat !< Error status of the operation @@ -358,18 +362,24 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) ! --- Packing all vortex elements into a list of segments call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) print*,'Number of segments',nSeg, 'Number of points',nSegP - ! + + ! --- Setting up regularization + allocate(SegEpsilon(1:nSeg)); + if (p%WakeRegMethod==idRegConstant) then + SegEpsilon=p%WakeRegFactor ! TODO + else + print*,'Regularization method not implemented',p%WakeRegMethod + STOP + endif + ! --- Computing induced velocity - allocate(SegSmooth(1:nSeg)); - SegSmooth=10 - SmoothModel=idSegSmoothLambOseen nCPs=nSegP allocate(CPs (1:3,1:nCPs)) allocate(Uind(1:3,1:nCPs)) Uind=0.0_ReKi !< important due to side effects of ui_seg ! --- call PackConvectingPoints() - call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, SmoothModel, SegSmooth, Uind) + call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, p%RegFunction, SegEpsilon, Uind) call UnPackInducedVelocity() deallocate(Uind) @@ -377,7 +387,7 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) deallocate(SegConnct) deallocate(SegGamma) deallocate(SegPoints) - deallocate(SegSmooth) + deallocate(SegEpsilon) contains !> Pack all the points that convect subroutine PackConvectingPoints() @@ -414,12 +424,11 @@ subroutine LiftingLineInducedVelocities(p, x, m, ErrStat, ErrMsg) type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables ! Local variables - integer(IntKi) :: SmoothModel !< TODO input file parameter integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs, iHeadP integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation - real(ReKi), dimension(:) , allocatable :: SegSmooth !< Segment smooth parameter + real(ReKi), dimension(:) , allocatable :: SegEpsilon !< Segment smooth parameter real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi), intent( out) :: ErrStat !< Error status of the operation @@ -432,19 +441,23 @@ subroutine LiftingLineInducedVelocities(p, x, m, ErrStat, ErrMsg) call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) print*,'Number of segments',nSeg, 'Number of points',nSegP - ! --- Computing induced velocity - allocate(SegSmooth(1:nSeg)); - SegSmooth=10 - SmoothModel=idSegSmoothLambOseen + ! --- Setting up regularization + allocate(SegEpsilon(1:nSeg)); + if (p%WakeRegMethod==idRegConstant) then + SegEpsilon=p%WakeRegFactor ! TODO + else + print*,'Regularization method not implemented',p%WakeRegMethod + STOP + endif + ! --- Computing induced velocity nCPs=p%nWings * p%nSpan - allocate(CPs (1:3,1:nCPs)) allocate(Uind(1:3,1:nCPs)) Uind=0.0_ReKi !< important due to side effects of ui_seg ! --- call PackLiftingLinePoints() - call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, SmoothModel, SegSmooth, Uind) + call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, p%RegFunction, SegEpsilon, Uind) call UnPackLiftingLineVelocities() deallocate(Uind) @@ -452,7 +465,7 @@ subroutine LiftingLineInducedVelocities(p, x, m, ErrStat, ErrMsg) deallocate(SegConnct) deallocate(SegGamma) deallocate(SegPoints) - deallocate(SegSmooth) + deallocate(SegEpsilon) contains !> Pack all the control points subroutine PackLiftingLinePoints() diff --git a/modules/aerodyn14/src/FVW_Types.f90 b/modules/aerodyn14/src/FVW_Types.f90 index a0fa91fd6b..13a5a95849 100644 --- a/modules/aerodyn14/src/FVW_Types.f90 +++ b/modules/aerodyn14/src/FVW_Types.f90 @@ -57,6 +57,9 @@ MODULE FVW_Types REAL(ReKi) :: CircSolvConvCrit !< Convergence criterion for circulation solving [-] REAL(ReKi) :: CircSolvRelaxation !< Relaxation factor for circulation solving [-] INTEGER(IntKi) :: PrescribedPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] + INTEGER(IntKi) :: RegFunction !< Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart) [-] + INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] + REAL(ReKi) :: WakeRegFactor !< Factor used in the regularization [-] END TYPE FVW_ParameterType ! ======================= ! ========= FVW_OtherStateType ======= @@ -148,6 +151,9 @@ MODULE FVW_Types INTEGER(IntKi) :: PrescribedPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] INTEGER(IntKi) :: nNWPanels !< Number of nw panels [-] INTEGER(IntKi) :: nFWPanels !< Number of fw panels [-] + INTEGER(IntKi) :: RegFunction !< Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart) [-] + INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] + REAL(ReKi) :: WakeRegFactor !< Factor used in the regularization [-] END TYPE FVW_InputFile ! ======================= ! ========= FVW_InitOutputType ======= @@ -198,6 +204,9 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%CircSolvConvCrit = SrcParamData%CircSolvConvCrit DstParamData%CircSolvRelaxation = SrcParamData%CircSolvRelaxation DstParamData%PrescribedPolar = SrcParamData%PrescribedPolar + DstParamData%RegFunction = SrcParamData%RegFunction + DstParamData%WakeRegMethod = SrcParamData%WakeRegMethod + DstParamData%WakeRegFactor = SrcParamData%WakeRegFactor END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -266,6 +275,9 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_BufSz = Re_BufSz + 1 ! CircSolvConvCrit Re_BufSz = Re_BufSz + 1 ! CircSolvRelaxation Int_BufSz = Int_BufSz + 1 ! PrescribedPolar + Int_BufSz = Int_BufSz + 1 ! RegFunction + Int_BufSz = Int_BufSz + 1 ! WakeRegMethod + Re_BufSz = Re_BufSz + 1 ! WakeRegFactor IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -330,6 +342,12 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%PrescribedPolar Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%RegFunction + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WakeRegMethod + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegFactor + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -415,6 +433,12 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + 1 OutData%PrescribedPolar = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%RegFunction = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%WakeRegMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%WakeRegFactor = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -4133,6 +4157,9 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%PrescribedPolar = SrcInputFileData%PrescribedPolar DstInputFileData%nNWPanels = SrcInputFileData%nNWPanels DstInputFileData%nFWPanels = SrcInputFileData%nFWPanels + DstInputFileData%RegFunction = SrcInputFileData%RegFunction + DstInputFileData%WakeRegMethod = SrcInputFileData%WakeRegMethod + DstInputFileData%WakeRegFactor = SrcInputFileData%WakeRegFactor END SUBROUTINE FVW_CopyInputFile SUBROUTINE FVW_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) @@ -4193,6 +4220,9 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! PrescribedPolar Int_BufSz = Int_BufSz + 1 ! nNWPanels Int_BufSz = Int_BufSz + 1 ! nFWPanels + Int_BufSz = Int_BufSz + 1 ! RegFunction + Int_BufSz = Int_BufSz + 1 ! WakeRegMethod + Re_BufSz = Re_BufSz + 1 ! WakeRegFactor IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4246,6 +4276,12 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWPanels Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%RegFunction + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WakeRegMethod + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegFactor + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_PackInputFile SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4306,6 +4342,12 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%nFWPanels = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%RegFunction = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%WakeRegMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%WakeRegFactor = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_UnPackInputFile SUBROUTINE FVW_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn14/src/Registry-FVW.txt b/modules/aerodyn14/src/Registry-FVW.txt index 95dbd4350b..b1d0ef66af 100644 --- a/modules/aerodyn14/src/Registry-FVW.txt +++ b/modules/aerodyn14/src/Registry-FVW.txt @@ -23,6 +23,9 @@ typedef ^ ^ IntKi typedef ^ ^ ReKi CircSolvConvCrit - - - "Convergence criterion for circulation solving" - typedef ^ ^ ReKi CircSolvRelaxation - - - "Relaxation factor for circulation solving" - typedef ^ ^ IntKi PrescribedPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - +typedef ^ ^ IntKi RegFunction - - - "Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart)" - +typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - +typedef ^ ^ ReKi WakeRegFactor - - - "Factor used in the regularization " # ....... OtherStateType ............ # FVW_OtherStateType @@ -110,6 +113,9 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi PrescribedPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - typedef ^ ^ IntKi nNWPanels - - - "Number of nw panels" - typedef ^ ^ IntKi nFWPanels - - - "Number of fw panels" - +typedef ^ ^ IntKi RegFunction - - - "Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart)" - +typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - +typedef ^ ^ ReKi WakeRegFactor - - - "Factor used in the regularization " #.......... InitOutputType ...... # FVW_InitOutputType From be89139ddaedae684a7cfb6f7c2d8ac30e791ca5 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 20 Nov 2019 19:41:00 -0700 Subject: [PATCH 024/190] FVW: Implementation of FarWake --- modules/aerodyn14/src/AeroDyn14.f90 | 18 ++- modules/aerodyn14/src/FVW.f90 | 147 +++++++++---------- modules/aerodyn14/src/FVW_IO.f90 | 39 +++-- modules/aerodyn14/src/FVW_Subs.f90 | 211 +++++++++++++++++++++++----- modules/aerodyn14/src/FVW_Wings.f90 | 33 +---- 5 files changed, 285 insertions(+), 163 deletions(-) diff --git a/modules/aerodyn14/src/AeroDyn14.f90 b/modules/aerodyn14/src/AeroDyn14.f90 index f30aa4f5bd..88c24fa89c 100644 --- a/modules/aerodyn14/src/AeroDyn14.f90 +++ b/modules/aerodyn14/src/AeroDyn14.f90 @@ -795,8 +795,9 @@ SUBROUTINE AD14_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrSt TYPE(AD14_ContinuousStateType) :: dxdt ! Continuous state derivatives at Time TYPE(AD14_ConstraintStateType) :: z_Residual ! Residual of the constraint state equations (Z) - type(FVW_InputType) :: u_FVW(1) !< FVW inputs - REAL(DbKi) :: utimes_FVW(1) !< Times associated with u(:), in seconds + type(FVW_InputType) , allocatable :: u_FVW(:) !< FVW inputs + REAL(DbKi) , allocatable :: utimes_FVW(:) !< Times associated with u(:), in seconds + integer(IntKi) :: it ! INTEGER(IntKi) :: ErrStat2 ! Error status of the operation (occurs after initial error) ! CHARACTER(ErrMsgLen) :: ErrMess2 ! Error message if ErrStat2 /= ErrID_None @@ -815,11 +816,18 @@ SUBROUTINE AD14_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrSt ! print*,'Problem in AD14 update state, need to adapt which u we provide to FVW' ! STOP !endif + allocate(u_FVW (size(utimes))) + allocate(utimes_FVW(size(utimes))) ! Setting u(1)%FVW - call AD14_to_FVW_u(u(1),p,u(1)%FVW,ErrStat,ErrMess) - u_FVW(1) = u(1)%FVW - utimes_FVW(1) = utimes(1) + do it=1,size(utimes) + call AD14_to_FVW_u(u(it),p,u(it)%FVW,ErrStat,ErrMess) + u_FVW(it) = u(it)%FVW + utimes_FVW(it) = utimes(it) + !print*,'P',it, u(it)%InputMarkers(1)%Position(1:3,1) + enddo CALL FVW_UpdateStates( t, n, u_FVW, utimes_FVW, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat, ErrMess ) + deallocate(u_FVW) + deallocate(utimes_FVW) endif END SUBROUTINE AD14_UpdateStates diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index c1ec8f5ab4..7d0d73bd17 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -189,8 +189,8 @@ subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) call AllocAry( x%Gamma_NW, p%nSpan , p%nNWMax , p%nWings, 'NW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_NW = -999999_ReKi; call AllocAry( x%Gamma_FW, 1 , p%nFWMax , p%nWings, 'FW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_FW = -999999_ReKi; - call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_NW = -999999_ReKi; - call AllocAry( x%r_FW , 3, 2 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = -999999_ReKi; + call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_NW = -99_ReKi; + call AllocAry( x%r_FW , 3, 2 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = -99_ReKi; if (ErrStat >= AbortErrLev) return @@ -346,29 +346,45 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta character(ErrMsgLen) :: ErrMsg2 ! temporary Error message type(FVW_ConstraintStateType) :: z_guess ! < integer(IntKi) :: iW, iSpan, iAge + real(ReKi) :: dt ErrStat = ErrID_None ErrMsg = "" - print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0)',' Update states, t:',t,' t_u:', utimes(1),' dt: ',utimes(1)-t,' ',n,' nNW:',m%nNW + dt=utimes(1)-t ! TODO TODO TODO - ! Panelling wings based on input mesh provided - CALL Wings_Panelling(u(1)%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return + print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' dt: ',dt,' ',n,' nNW:',m%nNW,' nFW:',m%nFW + + ! --- Evaluation at t + ! Inputs at t + ! TODO TODO TODO AD14 HACK! inputs at other times are wrong with AD14 + ! call FVW_CopyInput( u(1), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return + ! call FVW_Input_ExtrapInterp(u,utimes,uInterp,t, ErrStat2, ErrMsg2); if(Failed()) return + if (m%FirstCall) then + call FVW_CopyInput( u(2), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return + ! NOTE AD14 HACK should be put outside! + ! Panelling wings based on input mesh provided + call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return + else + call FVW_CopyInput( u(1), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return + endif ! Distribute the Wind we requested to Inflow wind to storage Misc arrays ! TODO ANDY !CALL DistributeRequestedWind(u(1)%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Solve for circulation at t - CALL FVW_CalcConstrStateResidual(t, u(1), p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return + call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return - ! Map circulation and positions between LL and NW + ! Map circulation and positions between LL and NW and then NW and FW ! Changes: x only - CALL Wings_Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) + call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2) + call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) + !call print_x_NW_FW(p, m, z, x,'Map_') + ! --- Integration between t and t+dt if (p%IntMethod .eq. idEuler1) then - call FVW_Euler1( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return - + call FVW_Euler1( t, dt, uInterp, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return !elseif (p%IntMethod .eq. idRK4) then ! call FVW_RK4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) !elseif (p%IntMethod .eq. idAB4) then @@ -378,64 +394,39 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta else call SetErrStat(ErrID_Fatal,'Invalid time integration method:'//Num2LStr(p%IntMethod),ErrStat,ErrMsg,'FVW_UpdateState') end IF + !call print_x_NW_FW(p, m, z, x,'Conv') - ! -- Propagate wake m%nNW+1+1 - do iW=1,p%nWings - do iAge=p%nNWMax+1,iNWStart+1,-1 ! TODO TODO TODO Might need update - do iSpan=1,p%nSpan+1 - x%r_NW(1:3,iSpan,iAge,iW) = x%r_NW(1:3,iSpan,iAge-1,iW) - enddo - enddo - x%r_NW(1:3,:,1:iNWStart-1,iW) = -999.0_ReKi - enddo - do iW=1,p%nWings - do iAge=p%nNWMax,iNWStart+1,-1 - do iSpan=1,p%nSpan - x%Gamma_NW(iSpan,iAge,iW) = x%Gamma_NW(iSpan,iAge-1,iW) - enddo - enddo - x%Gamma_NW(:,1:iNWStart-1,iW) = -999.0_ReKi - enddo -! do iAge=1,m%nNW+1+1 -! print*,'iAge',iAge -! print*,'Prop', x%r_NW(1, 1:p%nSpan+1, iAge,1) -! print*,'Prop', x%r_NW(2, 1:p%nSpan+1, iAge,1) -! print*,'Prop', x%r_NW(3, 1:p%nSpan+1, iAge,1) -! enddo -! do iW=1,p%nWings -! print*,'Wing',iW -! do iAge=1,m%nNW+1+1 -! print*,'Prop',x%Gamma_NW(:,iAge,iW) -! enddo -! enddo + + ! --- t+dt + + ! Propagation/creation of new layer of panels + call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2) + !call print_x_NW_FW(p, m, z, x,'Prop_') + + ! Inputs at t+dt + ! TODO TODO TODO inputs at other times are wrong with AD14 + !call FVW_Input_ExtrapInterp(u,utimes,uInterp,t+dt, ErrStat2, ErrMsg2); if(Failed()) return + call FVW_CopyInput( u(1), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return + + ! Panelling wings based on input mesh at t+dt + call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Solve for circulation at t+dt - CALL FVW_CalcConstrStateResidual(t, u(1), p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return + call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return - ! Map circulation and positions between LL and NW + ! Map circulation and positions between LL and NW and then NW and FW ! Changes: x only - CALL Wings_Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) + call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2) + call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) + !call print_x_NW_FW(p, m, z, x,'Map_') -! do iW=1,p%nWings -! print*,'Wing',iW -! do iAge=1,m%nNW+1+1 -! print*,'Map',x%Gamma_NW(:,iAge,iW) -! enddo -! enddo -! do iAge=1,m%nNW+1+1 -! print*,'iAge',iAge -! print*,'Map', x%r_NW(1, 1:p%nSpan+1, iAge,1) -! print*,'Map', x%r_NW(2, 1:p%nSpan+1, iAge,1) -! print*,'Map', x%r_NW(3, 1:p%nSpan+1, iAge,1) -! enddo - -! if (m%nNW>3) STOP + !if (m%nFW>4) STOP + !if (t>0.5) STOP if (m%FirstCall) then m%FirstCall=.False. endif - call FVW_DestroyConstrState(z_guess, ErrStat2, ErrMsg2); contains @@ -482,35 +473,34 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! Out: m%Vind_NW, m%Vind_FW call WakeInducedVelocities(p, x, m, ErrStat2, ErrMsg2) - call print_mean_4d( m%Vind_NW(:,:, 1:m%nNW+1,:), 'Mean induced vel. NW') - call print_mean_4d( m%Vind_FW(:,:, 1:m%nFW+1,:), 'Mean induced vel. FW') - call print_mean_4d( m%Vwnd_NW(:,:, 1:m%nNW+1,:), 'Mean wind vel. NW') - call print_mean_4d( m%Vwnd_FW(:,:, 1:m%nFW+1,:), 'Mean wind vel. FW') + !call print_mean_4d( m%Vind_NW(:,:, 1:m%nNW+1,:), 'Mean induced vel. NW') + !call print_mean_4d( m%Vind_FW(:,:, 1:m%nFW+1,:), 'Mean induced vel. FW') + !call print_mean_4d( m%Vwnd_NW(:,:, 1:m%nNW+1,:), 'Mean wind vel. NW') + !call print_mean_4d( m%Vwnd_FW(:,:, 1:m%nFW+1,:), 'Mean wind vel. FW') ! --- Vortex points are convected with the free stream and induced velocity dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + m%Vind_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) - !dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) ! TODO TODO -! STOP -! dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) ! TODO/NOTE for now, FW convects with free wind else call print_mean_4d( m%Vwnd_NW(:,:,1:m%nNW+1,:), 'Mean wind vel. NW') - call print_mean_4d( m%Vwnd_FW(:,:,1:m%nFW+1,:), 'Mean wind vel. FW') + !call print_mean_4d( m%Vwnd_FW(:,:,1:m%nFW+1,:), 'Mean wind vel. FW') ! --- Vortex points are convected with the free stream dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) endif - ! Bound point does not convect + ! First NW point does not convect (bound to LL) dxdt%r_NW(1:3, :, 1:iNWStart-1, :)=0 + ! First FW point does not convect (bound to NW) + !dxdt%r_FW(1:3, :, 1, :)=0 end subroutine FVW_CalcContStateDeriv !---------------------------------------------------------------------------------------------------------------------------------- -subroutine FVW_Euler1( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) +subroutine FVW_Euler1( t, dt, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) real(DbKi), intent(in ) :: t !< Current simulation time in seconds - integer(IntKi), intent(in ) :: n !< time step number - type(FVW_InputType), intent(inout) :: u(:) !< Inputs at t - real(DbKi), intent(in ) :: utimes(:) !< times of input + real(ReKi), intent(in ) :: dt !< Time step + type(FVW_InputType), intent(in ) :: u !< Input at t type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states at t on input at t + dt on output type(FVW_DiscreteStateType), intent(in ) :: xd !< Discrete states at t @@ -521,16 +511,14 @@ subroutine FVW_Euler1( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, Err character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! local variables type(FVW_ContinuousStateType) :: dxdt ! time derivatives of continuous states - real(ReKi) :: dt ! Time step integer(IntKi) :: iAge ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" ! Compute "right hand side" - CALL FVW_CalcContStateDeriv( t, u(1), p, x, xd, z, OtherState, m, dxdt, ErrStat, ErrMsg ) + CALL FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat, ErrMsg ) - dt = utimes(1)-t ! TODO TODO is this correct ! do iAge=1,m%nNW+1 ! print*,'iAge',iAge @@ -617,7 +605,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ErrStat = ErrID_None ErrMsg = "" - print'(A,F10.3,A,L1)',' CalcOutput t:',t,' ',m%FirstCall + print'(A,F10.3,A,L1,A,I0,A,I0)','CalcOutput t:',t,' ',m%FirstCall,' nNW:',m%nNW,' nFW:',m%nFW if (m%FirstCall) then print*,'>>> First Call of CalcOuput, calling panelling and constrstate' @@ -644,7 +632,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg y%Vind(1:3,iSpan,iW) = m%Vind_LL(1:3,iSpan,iW) enddo enddo - call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') + !call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') ! We don't propagate the "Old"-> "New" if update states was not called once ! This is introduced since at init, CalcOutput is called before UpdateState @@ -655,10 +643,11 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg contains subroutine PrepareNextTimeStep() - ! --- Propagate wake - ! - m%nNW=m%nNW+1 - if (m%nNW>p%nNWMax) m%nNW = p%nNWMax + ! --- Increase wake length if maximum not reached + if (m%nNW==p%nNWMax) then ! a far wake exist + m%nFW=min(m%nFW+1, p%nFWMax) + endif + m%nNW=min(m%nNW+1, p%nNWMax) end subroutine PrepareNextTimeStep diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index 7c5d831f9c..f4a8cd25e2 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -67,6 +67,8 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method not implemented')) return if (Check(Inp%WakeRegFactor<0 , 'Wake regularization factor should be positive')) return + ! At least one NW panel if FW, this shoudln't be a problem since the LL is in NW, but safety for now + !if (Check( (Inp%nNWPanels<=0).and.(Inp%nFWPanels>0) , 'At least one near wake panel is required if the number of far wake panel is >0')) return call CleanUp() CONTAINS @@ -93,6 +95,8 @@ subroutine CleanUp() END SUBROUTINE FVW_ReadInputFile !================================================= +!> Export FVW variables to VTK +!! NOTE: when entering this function nNW and nFW has been ncremented by 1 subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) use VTK ! for all the vtk_* functions type(FVW_ParameterType), intent(in ) :: p !< Parameters @@ -109,7 +113,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) character(Twidth) :: Tstr ! string for current VTK write-out step (padded with zeros) integer :: iSeg integer :: iSpan, iNW, iFW - integer :: nSpan, nNW, nWings, nFW + integer :: nSpan, nWings integer :: k real(ReKi), dimension(:,:), allocatable :: Buffer2d character(1), dimension(3) :: I2ABC =(/'A','B','C'/) @@ -120,6 +124,8 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) integer(IntKi) :: iHeadC, iHeadP, nC, nP !real(ReKi), dimension(:), allocatable :: SegSmooth !< + print*,'------------------------------------------------------------------------------' + print'(A,L1,A,I0,A,I0,A,I0)','VTK Output - First call ',m%FirstCall, ' nNW:',m%nNW,' nFW:',m%nFW,' i:',VTKCount ! call set_vtk_binary_format(.false.) @@ -128,8 +134,6 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) nSpan = p%nSpan nWings = p%nWings - nNW = m%nNW - nFW = m%nFW ! --------------------------------------------------------------------------------} ! --- Blade ! --------------------------------------------------------------------------------{ @@ -164,7 +168,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,nWings write(Label,'(A,A)') 'NW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - if (m%nNW==1) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel + if (m%FirstCall) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel call WrVTK_Lattice(FileName, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW)) else call WrVTK_Lattice(FileName, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW)) @@ -174,16 +178,20 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! --- Far wake ! --------------------------------------------------------------------------------{ ! --- Far wake panels - !do iW=1,nWings - ! write(Label,'(A,A)') 'FW.Bld', i2ABC(iW) - ! Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - ! call WrVTK_Lattice(FileName, x%r_FW(1:3,:,:,iW), x%Gamma_FW(:,:,iW)) - !enddo + do iW=1,nWings + write(Label,'(A,A)') 'FW.Bld', i2ABC(iW) + Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' + call WrVTK_Lattice(FileName, x%r_FW(1:3,1:2,1:m%nFW+1,iW), x%Gamma_FW(1:1,1:m%nFW,iW)) + enddo ! --------------------------------------------------------------------------------} ! --- All Segments ! --------------------------------------------------------------------------------{ - nP = nWings * ( (nSpan+1)*(nNW+1) ) - nC = nWings * (2*(nSpan+1)*(nNW+1)-nSpan-nNW-2) + nP = nWings * ( (nSpan+1)*(m%nNW+1) ) + nC = nWings * (2*(nSpan+1)*(m%nNW+1)-nSpan-m%nNW-2) + if (m%nFW>0) then + nP = nP + p%nWings * ( 2 *(m%nFW+1) ) + nC = nC + p%nWings * (3*m%nFW+1 ) + endif allocate(SegConnct(1:2,1:nC)); SegConnct=-1 allocate(SegPoints(1:3,1:nP)); SegPoints=-1 allocate(SegGamma (1:nC)); SegGamma =-1 @@ -196,6 +204,11 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) endif enddo + if (m%nFW>0) then !TODO TODO TODO + do iW=1,p%nWings + CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + enddo + endif Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' CALL WrVTK_Segments(Filename, SegPoints, SegConnct, SegGamma) @@ -222,7 +235,7 @@ subroutine WrVTK_Segments(filename, SegPoints, SegConnct, SegGamma) call vtk_dataset_polydata(SegPoints(1:3,:)) call vtk_lines(SegConnct(1:2,:)-1) ! NOTE: VTK indexing at 0 call vtk_cell_data_init() - call vtk_cell_data_scalar(SegGamma,'SegGamma') + call vtk_cell_data_scalar(SegGamma,'Gamma') !call vtk_point_data_init() !call vtk_point_data_vector(Sgmt%UconvP(1:3,1:Sgmt%nP_Storage),'Uconv') call vtk_close_file() @@ -245,7 +258,7 @@ subroutine WrVTK_Lattice(filename, LatticePoints, LatticeGamma, LatticeData3d) call vtk_dataset_polydata(Points) call vtk_quad(Connectivity) call vtk_cell_data_init() - call vtk_cell_data_scalar(LatticeGamma,'Gamma_NW') + call vtk_cell_data_scalar(LatticeGamma,'Gamma') call vtk_close_file() endif diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index ec620ba67b..db397fa229 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -144,33 +144,158 @@ integer function line_count(iunit) endsubroutine ReadAndInterpGamma ! ===================================================================================== -!> Prescribe circulation on the blade (based on an input file for now...) -subroutine Prescribe_Gamma( Gamma_LL) - real( ReKi ), dimension( : ), intent(out ) :: Gamma_LL !< Circulation on the lifting line - integer :: i - integer :: iUnit - integer :: nr - integer :: nr_in - real(ReKi), dimension(:), allocatable :: rPrescr, GammaPrescr !< Radius - nr_in = size(Gamma_LL) - call GetNewUnit(iUnit) - open(unit = iUnit, file = './PrescribedGamma.txt') ! TODO more general - read(iUnit,*)nr - if (nr/=nr_in) then - write(*,*)'[ERROR] Number of prescribed point different from number of BS' - STOP 1 + +! -------------------------------------------------------------------------------- +! --- Mapping functions +! -------------------------------------------------------------------------------- + +!> Make sure the First panel of the NW match the last panel of the Trailing edge +!! - Same position of points +!! - Same circulation +subroutine Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) + use Interpolation, only: interp_lin + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states + type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local + integer(IntKi) :: iSpan , iW + ErrStat = ErrID_None + ErrMsg = "" + + ! First panel of NW is the last lifting line panel + do iW = 1,p%nWings + do iSpan = 1,p%nSpan+1 + x%r_NW(1:3, iSpan, iNWStart-1, iW) = m%r_LL(1:3, iSpan, 1, iW) ! iAge=1 + x%r_NW(1:3, iSpan, iNWStart , iW) = m%r_LL(1:3, iSpan, 2, iW) ! iAge=2 + enddo + enddo + ! First panel of NW is the last lifting line panel + do iW = 1,p%nWings + do iSpan = 1,p%nSpan + x%Gamma_NW(iSpan, iNWStart-1, iW) = z%Gamma_LL(iSpan,iW) ! iAge=1 + enddo + enddo + ! Circulations are the same on both side of the TE + if (p%nNWMax>iNWStart-1) then + do iW = 1,p%nWings + do iSpan = 1,p%nSpan + x%Gamma_NW(iSpan, iNWStart , iW) = z%Gamma_LL(iSpan,iW) ! iAge=2 + enddo + enddo + endif +end subroutine Map_LL_NW + +!> Map the last NW panel with the first FW panel +subroutine Map_NW_FW(p, m, z, x, ErrStat, ErrMsg) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states + type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: iSpan , iW + real(ReKi) :: FWGamma + integer(IntKi), parameter :: iAgeFW=1 !< we update the first FW panel + ErrStat = ErrID_None + ErrMsg = "" + + ! First Panel of Farwake has coordinates of last panel of near wake always + if (p%nFWMax>0) then + do iW=1,p%nWings + x%r_FW(1:3,1,iAgeFW,iW) = x%r_NW(1:3,1 ,p%nNWMax+1,iW) ! Point 1 (root) + x%r_FW(1:3,2,iAgeFW,iW) = x%r_NW(1:3,p%nSpan+1 ,p%nNWMax+1,iW) ! Point 2 (tip) + enddo + if (m%nNW==p%nNWMax) then + ! First circulation of Farwake is taken as the mean circulation of last NW column + do iW=1,p%nWings + FWGamma = sum(x%Gamma_NW(:,p%nNWMax,iW))/p%nSpan + x%Gamma_FW(1,iAgeFW,iW) = FWGamma + enddo + endif + endif +endsubroutine Map_NW_FW + +!> Propage the postions and circulation one index forward (loop from end to start) +subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states + type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: iSpan, iAge, iW + ErrStat = ErrID_None + ErrMsg = "" + + ! -- Propagate far wake + do iW=1,p%nWings + do iAge=p%nFWMax+1,2,-1 ! + do iSpan=1,2 + x%r_FW(1:3,iSpan,iAge,iW) = x%r_FW(1:3,iSpan,iAge-1,iW) + enddo + enddo + x%r_FW(1:3,1:2,1,iW) = -999.0_ReKi ! Nullified + enddo + if (p%nFWMax>0) then + do iW=1,p%nWings + do iAge=p%nFWMax,2,-1 + x%Gamma_FW(1,iAge,iW) = x%Gamma_FW(1,iAge-1,iW) + enddo + x%Gamma_FW(1,1,iW) = -999.0_ReKi ! Nullified + enddo endif - allocate(rPrescr(1:nr),GammaPrescr(1:nr)) - do i=1,nr - read(iUnit,*) rPrescr(i), GammaPrescr(i) + ! --- Propagate near wake + do iW=1,p%nWings + do iAge=p%nNWMax+1,iNWStart+1,-1 ! TODO TODO TODO Might need update + do iSpan=1,p%nSpan+1 + x%r_NW(1:3,iSpan,iAge,iW) = x%r_NW(1:3,iSpan,iAge-1,iW) + enddo + enddo + x%r_NW(1:3,:,1:iNWStart,iW) = -999.0_ReKi ! Nullified enddo - close(iUnit) - ! -- This part is the only part we need once the thing above has been moved - do i=1,nr - Gamma_LL(i)=GammaPrescr(i) + if (p%nNWMax>1) then + do iW=1,p%nWings + do iAge=p%nNWMax,iNWStart+1,-1 + do iSpan=1,p%nSpan + x%Gamma_NW(iSpan,iAge,iW) = x%Gamma_NW(iSpan,iAge-1,iW) + enddo + enddo + x%Gamma_NW(:,1:iNWStart,iW) = -999.0_ReKi ! Nullified + enddo + endif +end subroutine PropagateWake + + +subroutine print_r_NW_FW(p, m, z, x, label) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states + type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states + character(len=*),intent(in) :: label + integer(IntKi) :: iAge + print*,'NW' + do iAge=1,p%nNWMax+1 + print*,'iAge',iAge + print*,trim(label), x%r_NW(1, 1, iAge,1), x%r_NW(1, p%nSpan+1, iAge,1) + print*,trim(label), x%r_NW(2, 1, iAge,1), x%r_NW(2, p%nSpan+1, iAge,1) + print*,trim(label), x%r_NW(3, 1, iAge,1), x%r_NW(3, p%nSpan+1, iAge,1) enddo -endsubroutine Prescribe_Gamma + print*,'FW' + do iAge=1,p%nFWMax+1 + print*,'iAge',iAge + print*,trim(label), x%r_FW(1, 1:2, iAge,1) + print*,trim(label), x%r_FW(2, 1:2, iAge,1) + print*,trim(label), x%r_FW(3, 1:2, iAge,1) + enddo +endsubroutine + +! -------------------------------------------------------------------------------- +! --- PACKING/UNPACKING FUNCTIONS +! -------------------------------------------------------------------------------- !> Establish the list of points where we will need the free stream subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) real(ReKi), dimension(:,:), allocatable, intent(inout) :: r_wind !< Position where wind is requested @@ -184,7 +309,6 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) integer(IntKi) :: nTot ! Total number of points integer(IntKi) :: iSpan, iW, iAge ! Index on span, wings, panels integer(IntKi) :: iP ! Current index of point - ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" if (allocated(r_wind)) deallocate(r_wind) @@ -309,8 +433,10 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG ! Counting total number of segments TODO add FarWake nP = p%nWings * ( (p%nSpan+1)*(m%nNW-iDepthStart+2) ) nC = p%nWings * (2*(p%nSpan+1)*(m%nNW-iDepthStart+2)-(p%nSpan+1)-(m%nNW-iDepthStart+1+1)) -! nP = nP + p%nWings * (p%nSpan+1)*2 -! nC = nC + p%nWings * (2*(p%nSpan+1)*(2)-p%nSpan-1-2) + if (m%nFW>0) then + nP = nP + p%nWings * ( 2 *(m%nFW+1) ) + nC = nC + p%nWings * (3*m%nFW+1 ) + endif if (allocated(SegConnct)) deallocate(SegConnct) if (allocated(SegPoints)) deallocate(SegPoints) @@ -326,6 +452,11 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG do iW=1,p%nWings CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) enddo + if (m%nFW>0) then + do iW=1,p%nWings + CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + enddo + endif if ((iHeadP-1)/=nP) then print*,'PackPanelsToSegments: Number of points wrongly estimated',nP, iHeadP-1 STOP @@ -373,7 +504,7 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) endif ! --- Computing induced velocity - nCPs=nSegP + nCPs=nSegP ! NOTE This is only try if Far wake convects as well! allocate(CPs (1:3,1:nCPs)) allocate(Uind(1:3,1:nCPs)) Uind=0.0_ReKi !< important due to side effects of ui_seg @@ -395,12 +526,17 @@ subroutine PackConvectingPoints() do iW=1,p%nWings CALL LatticeToPoints(x%r_NW(1:3,:,1:m%nNW+1,iW), 1, CPs, iHeadP) enddo + if (m%nFW>0) then + do iW=1,p%nWings + CALL LatticeToPoints(x%r_FW(1:3,:,1:m%nFW+1,iW), 1, CPs, iHeadP) + enddo + endif if ((iHeadP-1)/=size(CPs,2)) then print*,'PackConvectingPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 STOP endif nCPs=iHeadP-1 - print*,'Number of points packed for Convection:',nCPs, nSegP + !print*,'Number of points packed for Convection:',nCPs, nSegP end subroutine !> Distribute the induced velocity to the proper location subroutine UnPackInducedVelocity() @@ -408,10 +544,17 @@ subroutine UnPackInducedVelocity() do iW=1,p%nWings CALL VecToLattice(Uind, 1, m%Vind_NW(:,:,1:m%nNW+1,iW), iHeadP) enddo - if ((iHeadP-1)/=size(Uind,2)) then - print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 - STOP - endif + ! TODO + m%Vind_FW=0.0_ReKi + !if (m%nFW>0) then + ! do iW=1,p%nWings + ! CALL VecToLattice(Uind, 1, m%Vind_FW(:,1:2,1:m%nFW+1,iW), iHeadP) + ! enddo + !endif + !if ((iHeadP-1)/=size(Uind,2)) then + ! print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 + ! STOP + !endif end subroutine end subroutine @@ -439,7 +582,7 @@ subroutine LiftingLineInducedVelocities(p, x, m, ErrStat, ErrMsg) ! --- Packing all vortex elements into a list of segments call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) - print*,'Number of segments',nSeg, 'Number of points',nSegP + !print*,'Number of segments',nSeg, 'Number of points',nSegP ! --- Setting up regularization allocate(SegEpsilon(1:nSeg)); @@ -478,7 +621,7 @@ subroutine PackLiftingLinePoints() STOP endif nCPs=iHeadP-1 - print*,'Number of points packed for LL:',nCPs, nSegP + !print*,'Number of points packed for LL:',nCPs, nSegP end subroutine !> Distribute the induced velocity to the proper location diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index 3938069db5..dbcb903fbf 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -162,38 +162,7 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) end subroutine Wings_Panelling - !> Make sure the First panel of the NW match the last panel of the Trailing edge - !! - Same position of points - !! - Same circulation - subroutine Wings_Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) - use Interpolation, only: interp_lin - type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables - type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states - type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states - integer(IntKi), intent( out) :: ErrStat !< Error status of the operation - character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - ! Local - integer(IntKi) :: ErrStat2 ! temporary error status of the operation - character(ErrMsgLen) :: ErrMsg2 ! temporary error message - integer(IntKi) ::iSpan , iW - ! First panel of NW is the last lifting line panel - do iW = 1,p%nWings - do iSpan = 1,p%nSpan+1 - x%r_NW(1:3, iSpan, iNWStart-1, iW) = m%r_LL(1:3, iSpan, 1, iW) ! iAge=1 - x%r_NW(1:3, iSpan, iNWStart , iW) = m%r_LL(1:3, iSpan, 2, iW) ! iAge=2 - enddo - enddo - ! First panel of NW is the last lifting line panel - ! Circulations are the same on both side of the TE - do iW = 1,p%nWings - do iSpan = 1,p%nSpan - x%Gamma_NW(iSpan, iNWStart-1, iW) = z%Gamma_LL(iSpan,iW) ! iAge=1 - x%Gamma_NW(iSpan, iNWStart , iW) = z%Gamma_LL(iSpan,iW) ! iAge=2 - enddo - enddo - end subroutine Wings_Map_LL_NW !---------------------------------------------------------------------------------------------------------------------------------- !> @@ -214,7 +183,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS ErrMsg = "" if (p%CirculationMethod==idCircPrescribed) then - print*,'>>>Prescribing circulation' + !print*,'>>>Prescribing circulation' do iW = 1, p%nWings !Loop over lifting lines Gamma_LL(1:p%nSpan,iW) = p%PrescribedCirculation(1:p%nSpan) enddo From e02bacae4a659153f7826d29b8078efd69a6ab5a Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 00:26:27 -0700 Subject: [PATCH 025/190] FVW: Introducing number of free FW panels, and FW nSpan --- modules/aerodyn14/src/FVW.f90 | 50 +++++------- modules/aerodyn14/src/FVW_IO.f90 | 18 +++-- modules/aerodyn14/src/FVW_Subs.f90 | 95 ++++++++++++++--------- modules/aerodyn14/src/FVW_Types.f90 | 14 ++++ modules/aerodyn14/src/FVW_VortexTools.f90 | 1 + modules/aerodyn14/src/Registry-FVW.txt | 2 + 6 files changed, 106 insertions(+), 74 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 7d0d73bd17..e34ada9b55 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -86,8 +86,9 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL FVW_ReadInputFile(InitInp%FVWFileName, p, InputFileData, ErrStat2, ErrMsg2); if(Failed()) return ! Trigger required before allocations - p%nNWMax=InputFileData%nNWPanels+1 ! +1 since LL panel included in NW - p%nFWMax=max(InputFileData%nFWPanels,0) + p%nNWMax = max(InputFileData%nNWPanels,0)+1 ! +1 since LL panel included in NW + p%nFWMax = max(InputFileData%nFWPanels,0) + p%nFWFree = max(InputFileData%nFWPanelsFree,0) ! Initialize Misc Vars (may depend on input file) CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return @@ -165,7 +166,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%Vwnd_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_NW= -999_ReKi; call AllocAry( m%Vwnd_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_NW= -999_ReKi; - call AllocAry( m%Vind_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; + call AllocAry( m%Vind_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; m%Vwnd_NW(1,:,:,:)= 10 m%Vwnd_NW(2,:,:,:)= 0 m%Vwnd_NW(3,:,:,:)= 0 @@ -188,9 +189,9 @@ subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) ErrMsg = "" call AllocAry( x%Gamma_NW, p%nSpan , p%nNWMax , p%nWings, 'NW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_NW = -999999_ReKi; - call AllocAry( x%Gamma_FW, 1 , p%nFWMax , p%nWings, 'FW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_FW = -999999_ReKi; + call AllocAry( x%Gamma_FW, FWnSpan , p%nFWMax , p%nWings, 'FW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_FW = -999999_ReKi; call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_NW = -99_ReKi; - call AllocAry( x%r_FW , 3, 2 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = -99_ReKi; + call AllocAry( x%r_FW , 3, FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = -99_ReKi; if (ErrStat >= AbortErrLev) return @@ -454,33 +455,36 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iSpan,iAge, iW integer(IntKi) :: ErrStat2 ! temporary error status of the operation character(ErrMsgLen) :: ErrMsg2 ! temporary error message - real(ReKi), dimension(3) :: U_mean + integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step ErrStat = ErrID_None ErrMsg = "" call AllocAry( dxdt%r_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat, ErrMsg ); dxdt%r_NW= -999999_ReKi; - call AllocAry( dxdt%r_FW , 3 , 2 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; + call AllocAry( dxdt%r_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; if (t> p%FreeWakeStart) then + nFWEff = min(m%nFW, p%nFWFree) ! --- Compute Induced velocities on the Near wake and far wake based on the marker postions: ! (expensive N^2 call) ! In : x%r_NW, r%r_FW ! Out: m%Vind_NW, m%Vind_FW call WakeInducedVelocities(p, x, m, ErrStat2, ErrMsg2) + m%Vind_FW(1:3, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) =0 ! Ensuring no convection velocity for panels that user doesnt want free - !call print_mean_4d( m%Vind_NW(:,:, 1:m%nNW+1,:), 'Mean induced vel. NW') - !call print_mean_4d( m%Vind_FW(:,:, 1:m%nFW+1,:), 'Mean induced vel. FW') - !call print_mean_4d( m%Vwnd_NW(:,:, 1:m%nNW+1,:), 'Mean wind vel. NW') - !call print_mean_4d( m%Vwnd_FW(:,:, 1:m%nFW+1,:), 'Mean wind vel. FW') + call print_mean_4d( m%Vind_NW(:,:, 1:m%nNW+1,:), 'Mean induced vel. NW') + if (nFWEff>0) then + call print_mean_4d( m%Vind_FW(:,:, 1:nFWEff ,:), 'Mean induced vel. FW') + endif + call print_mean_4d( m%Vwnd_NW(:,:, 1:m%nNW+1,:), 'Mean wind vel. NW') + call print_mean_4d( m%Vwnd_FW(:,:, 1:m%nFW+1,:), 'Mean wind vel. FW') ! --- Vortex points are convected with the free stream and induced velocity dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + m%Vind_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) - dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) ! TODO/NOTE for now, FW convects with free wind + dxdt%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + m%Vind_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) else call print_mean_4d( m%Vwnd_NW(:,:,1:m%nNW+1,:), 'Mean wind vel. NW') @@ -488,7 +492,7 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! --- Vortex points are convected with the free stream dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) - dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) + dxdt%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) endif ! First NW point does not convect (bound to LL) dxdt%r_NW(1:3, :, 1:iNWStart-1, :)=0 @@ -519,24 +523,10 @@ subroutine FVW_Euler1( t, dt, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) ! Compute "right hand side" CALL FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat, ErrMsg ) - -! do iAge=1,m%nNW+1 -! print*,'iAge',iAge -! print*,'Before', x%r_NW(1, 1:p%nSpan+1, iAge,1) -! print*,'Before', x%r_NW(2, 1:p%nSpan+1, iAge,1) -! print*,'Before', x%r_NW(3, 1:p%nSpan+1, iAge,1) -! enddo - ! Update of positions x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dt * dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) - x%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) = x%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) + dt * dxdt%r_FW(1:3, 1:2 , 1:m%nFW+1, 1:p%nWings) - -! do iAge=1,m%nNW+1 -! print*,'iAge',iAge -! print*,'After', x%r_NW(1, 1:p%nSpan+1, iAge,1) -! print*,'After', x%r_NW(2, 1:p%nSpan+1, iAge,1) -! print*,'After', x%r_NW(3, 1:p%nSpan+1, iAge,1) -! enddo + x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) = x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + dt * dxdt%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + ! Update of Gamma ! TODO, viscous diffusion, stretching diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index f4a8cd25e2..650ac73521 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -50,6 +50,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadCom(UnIn,FileName, 'Wake options header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%WakeRegFactor ,'WakeRegFactor' ,'',ErrStat2,ErrMsg2); if(Failed())return @@ -61,7 +62,10 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check( Inp%IntMethod/=idEuler1 , 'Time integration method not implemented')) return - if (Check( Inp%nNWPanels<0 , 'Number of near wake panels must be posivive')) return + if (Check( Inp%nNWPanels<0 , 'Number of near wake panels must be >=0')) return + if (Check( Inp%nFWPanels<0 , 'Number of far wake panels must be >=0')) return + if (Check( Inp%nFWPanelsFree<0 , 'Number of free far wake panels must be >=0')) return + if (Check( Inp%nFWPanelsFree>Inp%nFWPanels , 'Number of free far wake panels must be <=Number of far wake panels')) return if (Check(.not.(ANY(idRegVALID ==Inp%RegFunction )), 'Regularization function not implemented')) return if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method not implemented')) return @@ -181,16 +185,16 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,nWings write(Label,'(A,A)') 'FW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - call WrVTK_Lattice(FileName, x%r_FW(1:3,1:2,1:m%nFW+1,iW), x%Gamma_FW(1:1,1:m%nFW,iW)) + call WrVTK_Lattice(FileName, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW)) enddo ! --------------------------------------------------------------------------------} ! --- All Segments ! --------------------------------------------------------------------------------{ - nP = nWings * ( (nSpan+1)*(m%nNW+1) ) - nC = nWings * (2*(nSpan+1)*(m%nNW+1)-nSpan-m%nNW-2) - if (m%nFW>0) then - nP = nP + p%nWings * ( 2 *(m%nFW+1) ) - nC = nC + p%nWings * (3*m%nFW+1 ) + nP = p%nWings * ( (p%nSpan+1)*(m%nNW+1) ) + nC = p%nWings * (2*(p%nSpan+1)*(m%nNW+1)-(p%nSpan+1)-(m%nNW+1)) + if (m%nFW>0) then + nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) + nC = nC + p%nWings * (2*(FWnSpan+1)*(m%nFW+1)-(FWnSpan+1)-(m%nFW+1)) endif allocate(SegConnct(1:2,1:nC)); SegConnct=-1 allocate(SegPoints(1:3,1:nP)); SegPoints=-1 diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index db397fa229..77db40a095 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -29,6 +29,7 @@ module FVW_SUBS ! Implementation integer(IntKi), parameter :: iNWStart=2 !< Index in r%NW where the near wake start (if >1 then the Wing panels are included in r_NW) + integer(IntKi), parameter :: FWnSpan=1 !< Number of spanwise far wake panels ! TODO make it an input later contains !========================================================================== @@ -205,14 +206,21 @@ subroutine Map_NW_FW(p, m, z, x, ErrStat, ErrMsg) ! First Panel of Farwake has coordinates of last panel of near wake always if (p%nFWMax>0) then do iW=1,p%nWings - x%r_FW(1:3,1,iAgeFW,iW) = x%r_NW(1:3,1 ,p%nNWMax+1,iW) ! Point 1 (root) - x%r_FW(1:3,2,iAgeFW,iW) = x%r_NW(1:3,p%nSpan+1 ,p%nNWMax+1,iW) ! Point 2 (tip) + x%r_FW(1:3,1 ,iAgeFW,iW) = x%r_NW(1:3,1 ,p%nNWMax+1,iW) ! Point 1 (root) + x%r_FW(1:3,FWnSpan+1,iAgeFW,iW) = x%r_NW(1:3,p%nSpan+1 ,p%nNWMax+1,iW) ! Point FWnSpan (tip) + if ((FWnSpan==2)) then + ! in between point + x%r_FW(1:3,2,iAgeFW,iW) = x%r_NW(1:3,int(p%nSpan+1)/4 ,p%nNWMax+1,iW) ! Point (mid) + else if ((FWnSpan>2)) then + print*,'Error: FWnSpan>2 not implemented.' + STOP + endif enddo if (m%nNW==p%nNWMax) then ! First circulation of Farwake is taken as the mean circulation of last NW column do iW=1,p%nWings FWGamma = sum(x%Gamma_NW(:,p%nNWMax,iW))/p%nSpan - x%Gamma_FW(1,iAgeFW,iW) = FWGamma + x%Gamma_FW(1:FWnSpan,iAgeFW,iW) = FWGamma enddo endif endif @@ -233,18 +241,20 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) ! -- Propagate far wake do iW=1,p%nWings do iAge=p%nFWMax+1,2,-1 ! - do iSpan=1,2 + do iSpan=1,FWnSpan+1 x%r_FW(1:3,iSpan,iAge,iW) = x%r_FW(1:3,iSpan,iAge-1,iW) enddo enddo - x%r_FW(1:3,1:2,1,iW) = -999.0_ReKi ! Nullified + x%r_FW(1:3,1:FWnSpan,1,iW) = -999.0_ReKi ! Nullified enddo if (p%nFWMax>0) then do iW=1,p%nWings do iAge=p%nFWMax,2,-1 - x%Gamma_FW(1,iAge,iW) = x%Gamma_FW(1,iAge-1,iW) + do iSpan=1,FWnSpan + x%Gamma_FW(iSpan,iAge,iW) = x%Gamma_FW(iSpan,iAge-1,iW) + enddo enddo - x%Gamma_FW(1,1,iW) = -999.0_ReKi ! Nullified + x%Gamma_FW(1,1:FWnSpan-1,iW) = -999.0_ReKi ! Nullified enddo endif ! --- Propagate near wake @@ -286,9 +296,9 @@ subroutine print_r_NW_FW(p, m, z, x, label) print*,'FW' do iAge=1,p%nFWMax+1 print*,'iAge',iAge - print*,trim(label), x%r_FW(1, 1:2, iAge,1) - print*,trim(label), x%r_FW(2, 1:2, iAge,1) - print*,trim(label), x%r_FW(3, 1:2, iAge,1) + print*,trim(label), x%r_FW(1, 1, iAge,1), x%r_FW(1, FWnSpan+1, iAge,1) + print*,trim(label), x%r_FW(2, 1, iAge,1), x%r_FW(2, FWnSpan+1, iAge,1) + print*,trim(label), x%r_FW(3, 1, iAge,1), x%r_FW(3, FWnSpan+1, iAge,1) enddo endsubroutine @@ -316,7 +326,7 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) nTot = 0 nTot = nTot + p%nWings * p%nSpan ! Lifting line Control Points nTot = nTot + p%nWings * (p%nSpan+1) * (m%nNW+1) ! Nearwake points - nTot = nTot + p%nWings * ( 1 +1) * (m%nFW+1) ! War wake points + nTot = nTot + p%nWings * (FWnSpan+1) * (m%nFW+1) ! War wake points call AllocAry( r_wind , 3, nTot, 'Requested Wind Points', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,'SetRequestedWindPoints'); r_wind(1:3,1:nTot)= -999999_ReKi; @@ -343,7 +353,7 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) ! --- FW points do iW = 1, p%nWings - do iSpan = 1, 2 ! root and tip + do iSpan = 1, FWnSpan+1 ! root and tip do iAge = 1, m%nFW + 1 iP=iP+1 r_wind(1:3,iP) = x%r_FW(1:3, iSpan, iAge, iW) @@ -374,7 +384,7 @@ subroutine DistributeRequestedWind(V_wind, x, p, m, ErrStat, ErrMsg ) nTot = 0 nTot = nTot + p%nWings * p%nSpan ! Lifting line Control Points nTot = nTot + p%nWings * (p%nSpan+1) * (m%nNW+1) ! Nearwake points - nTot = nTot + p%nWings * ( 1 +1) * (m%nFW+1) ! War wake points + nTot = nTot + p%nWings * (FWnSpan+1) * (m%nFW+1) ! Far wake points if (size(V_wind,2)0) then - nP = nP + p%nWings * ( 2 *(m%nFW+1) ) - nC = nC + p%nWings * (3*m%nFW+1 ) + nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) + nC = nC + p%nWings * (2*(FWnSpan+1)*(m%nFW+1)-(FWnSpan+1)-(m%nFW+1)) endif if (allocated(SegConnct)) deallocate(SegConnct) @@ -486,13 +496,14 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step + nFWEff = min(m%nFW, p%nFWFree) m%Vind_NW = -9999._ReKi !< Safety m%Vind_FW = -9999._ReKi !< Safety ! --- Packing all vortex elements into a list of segments call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) - print*,'Number of segments',nSeg, 'Number of points',nSegP ! --- Setting up regularization allocate(SegEpsilon(1:nSeg)); @@ -504,12 +515,8 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) endif ! --- Computing induced velocity - nCPs=nSegP ! NOTE This is only try if Far wake convects as well! - allocate(CPs (1:3,1:nCPs)) - allocate(Uind(1:3,1:nCPs)) - Uind=0.0_ReKi !< important due to side effects of ui_seg - ! --- call PackConvectingPoints() + print'(A,I0,A,I0,A,I0)','Convection - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, p%RegFunction, SegEpsilon, Uind) call UnPackInducedVelocity() @@ -522,21 +529,32 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) contains !> Pack all the points that convect subroutine PackConvectingPoints() + ! Counting total number of control points that convects + nCPs = p%nWings * ( (p%nSpan+1)*(m%nNW+1) ) + if (nFWEff>0) then + nCPs = nCPs + p%nWings * ((FWnSpan+1)*(nFWEff+1) ) + endif + + ! Allocation + allocate(CPs (1:3,1:nCPs)) + allocate(Uind(1:3,1:nCPs)) + Uind=0.0_ReKi !< important due to side effects of ui_seg + + ! Packing iHeadP=1 do iW=1,p%nWings CALL LatticeToPoints(x%r_NW(1:3,:,1:m%nNW+1,iW), 1, CPs, iHeadP) enddo - if (m%nFW>0) then + if (nFWEff>0) then do iW=1,p%nWings - CALL LatticeToPoints(x%r_FW(1:3,:,1:m%nFW+1,iW), 1, CPs, iHeadP) + CALL LatticeToPoints(x%r_FW(1:3,:,1:nFWEff+1,iW), 1, CPs, iHeadP) enddo endif + if ((iHeadP-1)/=size(CPs,2)) then print*,'PackConvectingPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 STOP endif - nCPs=iHeadP-1 - !print*,'Number of points packed for Convection:',nCPs, nSegP end subroutine !> Distribute the induced velocity to the proper location subroutine UnPackInducedVelocity() @@ -545,16 +563,19 @@ subroutine UnPackInducedVelocity() CALL VecToLattice(Uind, 1, m%Vind_NW(:,:,1:m%nNW+1,iW), iHeadP) enddo ! TODO - m%Vind_FW=0.0_ReKi - !if (m%nFW>0) then - ! do iW=1,p%nWings - ! CALL VecToLattice(Uind, 1, m%Vind_FW(:,1:2,1:m%nFW+1,iW), iHeadP) - ! enddo - !endif - !if ((iHeadP-1)/=size(Uind,2)) then - ! print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 - ! STOP - !endif + if (nFWEff>0) then + do iW=1,p%nWings + CALL VecToLattice(Uind, 1, m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,iW), iHeadP) + enddo + if (any(m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,:)<-99)) then + print*,'UnPackInducedVelocity: Problem in FW induced velocity on FW points' + STOP + endif + endif + if ((iHeadP-1)/=size(Uind,2)) then + print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 + STOP + endif end subroutine end subroutine diff --git a/modules/aerodyn14/src/FVW_Types.f90 b/modules/aerodyn14/src/FVW_Types.f90 index 13a5a95849..e722993f71 100644 --- a/modules/aerodyn14/src/FVW_Types.f90 +++ b/modules/aerodyn14/src/FVW_Types.f90 @@ -48,6 +48,7 @@ MODULE FVW_Types INTEGER(IntKi) :: nSpan !< TODO, should be defined per wing. Number of spanwise element [-] INTEGER(IntKi) :: nNWMax !< Maximum number of nw panels [-] INTEGER(IntKi) :: nFWMax !< Maximum number of fw panels [-] + INTEGER(IntKi) :: nFWFree !< Number of fw panels that are free [-] INTEGER(IntKi) :: IntMethod !< Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1) [-] REAL(ReKi) :: FreeWakeStart !< Time when wake starts convecting (rolling up) [s] REAL(ReKi) :: FullCirculationStart !< Time when the circulation is full [s] @@ -151,6 +152,7 @@ MODULE FVW_Types INTEGER(IntKi) :: PrescribedPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] INTEGER(IntKi) :: nNWPanels !< Number of nw panels [-] INTEGER(IntKi) :: nFWPanels !< Number of fw panels [-] + INTEGER(IntKi) :: nFWPanelsFree !< Number of fw panels that are free [-] INTEGER(IntKi) :: RegFunction !< Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart) [-] INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] REAL(ReKi) :: WakeRegFactor !< Factor used in the regularization [-] @@ -184,6 +186,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%nSpan = SrcParamData%nSpan DstParamData%nNWMax = SrcParamData%nNWMax DstParamData%nFWMax = SrcParamData%nFWMax + DstParamData%nFWFree = SrcParamData%nFWFree DstParamData%IntMethod = SrcParamData%IntMethod DstParamData%FreeWakeStart = SrcParamData%FreeWakeStart DstParamData%FullCirculationStart = SrcParamData%FullCirculationStart @@ -262,6 +265,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! nSpan Int_BufSz = Int_BufSz + 1 ! nNWMax Int_BufSz = Int_BufSz + 1 ! nFWMax + Int_BufSz = Int_BufSz + 1 ! nFWFree Int_BufSz = Int_BufSz + 1 ! IntMethod Re_BufSz = Re_BufSz + 1 ! FreeWakeStart Re_BufSz = Re_BufSz + 1 ! FullCirculationStart @@ -313,6 +317,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWMax Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWFree + Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%IntMethod Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%FreeWakeStart @@ -394,6 +400,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%nFWMax = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%nFWFree = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%IntMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%FreeWakeStart = ReKiBuf( Re_Xferred ) @@ -4157,6 +4165,7 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%PrescribedPolar = SrcInputFileData%PrescribedPolar DstInputFileData%nNWPanels = SrcInputFileData%nNWPanels DstInputFileData%nFWPanels = SrcInputFileData%nFWPanels + DstInputFileData%nFWPanelsFree = SrcInputFileData%nFWPanelsFree DstInputFileData%RegFunction = SrcInputFileData%RegFunction DstInputFileData%WakeRegMethod = SrcInputFileData%WakeRegMethod DstInputFileData%WakeRegFactor = SrcInputFileData%WakeRegFactor @@ -4220,6 +4229,7 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! PrescribedPolar Int_BufSz = Int_BufSz + 1 ! nNWPanels Int_BufSz = Int_BufSz + 1 ! nFWPanels + Int_BufSz = Int_BufSz + 1 ! nFWPanelsFree Int_BufSz = Int_BufSz + 1 ! RegFunction Int_BufSz = Int_BufSz + 1 ! WakeRegMethod Re_BufSz = Re_BufSz + 1 ! WakeRegFactor @@ -4276,6 +4286,8 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWPanels Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWPanelsFree + Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%RegFunction Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WakeRegMethod @@ -4342,6 +4354,8 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%nFWPanels = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%nFWPanelsFree = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%RegFunction = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%WakeRegMethod = IntKiBuf( Int_Xferred ) diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn14/src/FVW_VortexTools.f90 index 6dcb54102d..9d3ebca6f5 100644 --- a/modules/aerodyn14/src/FVW_VortexTools.f90 +++ b/modules/aerodyn14/src/FVW_VortexTools.f90 @@ -147,6 +147,7 @@ subroutine print_mean_4d(M, Label) enddo; enddo; enddo; U(1:3)=U(1:3)/ (size(M,4)*size(M,3)*size(M,2)) print'(A20,3F12.4)',trim(Label),U + if(U(1)<-99) STOP end subroutine subroutine print_mean_3d(M, Label) diff --git a/modules/aerodyn14/src/Registry-FVW.txt b/modules/aerodyn14/src/Registry-FVW.txt index b1d0ef66af..da35aa9829 100644 --- a/modules/aerodyn14/src/Registry-FVW.txt +++ b/modules/aerodyn14/src/Registry-FVW.txt @@ -14,6 +14,7 @@ typedef FVW/FVW ParameterType IntKi typedef ^ ^ IntKi nSpan - - - "TODO, should be defined per wing. Number of spanwise element" - typedef ^ ^ IntKi nNWMax - - - "Maximum number of nw panels" - typedef ^ ^ IntKi nFWMax - - - "Maximum number of fw panels" - +typedef ^ ^ IntKi nFWFree - - - "Number of fw panels that are free" - typedef ^ ^ IntKi IntMethod - - - "Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1)" - typedef ^ ^ ReKi FreeWakeStart - - - "Time when wake starts convecting (rolling up)" s typedef ^ ^ ReKi FullCirculationStart - - - "Time when the circulation is full" s @@ -113,6 +114,7 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi PrescribedPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - typedef ^ ^ IntKi nNWPanels - - - "Number of nw panels" - typedef ^ ^ IntKi nFWPanels - - - "Number of fw panels" - +typedef ^ ^ IntKi nFWPanelsFree - - - "Number of fw panels that are free" - typedef ^ ^ IntKi RegFunction - - - "Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart)" - typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - typedef ^ ^ ReKi WakeRegFactor - - - "Factor used in the regularization " From 0cf390aa939e8089f5c8a8492b8d0329fdc4fa8e Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 13:26:14 -0700 Subject: [PATCH 026/190] FVW: Implemented circulation solving (need mode work), added VTK options and HACK --- modules/aerodyn14/src/FVW.f90 | 34 ++++-- modules/aerodyn14/src/FVW_BiotSavart.f90 | 2 +- modules/aerodyn14/src/FVW_IO.f90 | 25 +++-- modules/aerodyn14/src/FVW_Subs.f90 | 130 ++++++++++++---------- modules/aerodyn14/src/FVW_Types.f90 | 35 ++++++ modules/aerodyn14/src/FVW_VortexTools.f90 | 8 +- modules/aerodyn14/src/FVW_Wings.f90 | 113 ++++++++++++++----- modules/aerodyn14/src/Registry-FVW.txt | 5 + 8 files changed, 242 insertions(+), 110 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index e34ada9b55..62e5c85fd2 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -93,6 +93,15 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! Initialize Misc Vars (may depend on input file) CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + ! Wind Speed hack, TODO temporary + m%Vwnd_LL(:,:,:) = 0 + m%Vwnd_NW(:,:,:,:) = 0 + m%Vwnd_FW(:,:,:,:) = 0 + m%Vwnd_LL(1,:,:) = InputFileData%Uinf + m%Vwnd_NW(1,:,:,:) = InputFileData%Uinf + m%Vwnd_FW(1,:,:,:) = InputFileData%Uinf + + ! Preliminary meshing of the wings (may depend on input file) ! NOTE: the mesh is not located at the right position yet, the first call to calcoutput will redo some meshing CALL Wings_Panelling_Init(InitInp%WingsMesh, InitInp%RElm, InitInp%chord, p, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -167,12 +176,6 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%Vwnd_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_NW= -999_ReKi; call AllocAry( m%Vind_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; - m%Vwnd_NW(1,:,:,:)= 10 - m%Vwnd_NW(2,:,:,:)= 0 - m%Vwnd_NW(3,:,:,:)= 0 - m%Vwnd_FW(1,:,:,:)= 10 - m%Vwnd_FW(2,:,:,:)= 0 - m%Vwnd_FW(3,:,:,:)= 0 end subroutine FVW_InitMiscVars ! ============================================================================== subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) @@ -265,6 +268,8 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%RegFunction = InputFileData%RegFunction p%WakeRegMethod = InputFileData%WakeRegMethod p%WakeRegFactor = InputFileData%WakeRegFactor + p%WrVTK = InputFileData%WrVTK + p%VTKBlades = InputFileData%VTKBlades if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) if (InputFileData%CirculationMethod==idCircPrescribed) then @@ -375,6 +380,8 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta !CALL DistributeRequestedWind(u(1)%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Solve for circulation at t + call AllocAry( z_guess%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat, ErrMsg ); + z_guess%Gamma_LL = m%Gamma_LL call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return ! Map circulation and positions between LL and NW and then NW and FW @@ -538,13 +545,13 @@ end subroutine FVW_Euler1 !---------------------------------------------------------------------------------------------------------------------------------- !> This is a tight coupling routine for solving for the residual of the constraint state functions. -subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z, OtherState, m, z_out, ErrStat, ErrMsg ) +subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, z_out, ErrStat, ErrMsg ) real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(FVW_InputType), intent(in ) :: u !< Inputs at t type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< Continuous states at t type(FVW_DiscreteStateType), intent(in ) :: xd !< Discrete states at t - type(FVW_ConstraintStateType), intent(in ) :: z !< Constraint states at t (possibly a guess) + type(FVW_ConstraintStateType), intent(in ) :: z_guess !< Constraint states at t (possibly a guess) type(FVW_OtherStateType), intent(in ) :: OtherState !< Other states at t type(FVW_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) type(FVW_ConstraintStateType), intent( out) :: z_out !< Residual of the constraint state functions using @@ -560,7 +567,7 @@ subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z, OtherState, m, z_out, call AllocAry( z_out%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat, ErrMsg ); z_out%Gamma_LL = -999999_ReKi; - CALL Wings_ComputeCirculation(t, z_out%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) + CALL Wings_ComputeCirculation(t, z_out%Gamma_LL, z_guess%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) end subroutine FVW_CalcConstrStateResidual @@ -598,7 +605,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg print'(A,F10.3,A,L1,A,I0,A,I0)','CalcOutput t:',t,' ',m%FirstCall,' nNW:',m%nNW,' nFW:',m%nFW if (m%FirstCall) then - print*,'>>> First Call of CalcOuput, calling panelling and constrstate' + print*,'>>> First Call of CalcOutput, calling panelling and constrstate' CALL Wings_Panelling(u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) ! For plotting only else @@ -613,7 +620,9 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return ! Induction on the lifting line control point - call LiftingLineInducedVelocities(p, x, m, ErrStat2, ErrMsg2); if(Failed()) return + ! Set m%Vind_LL + m%Vind_LL=-9999.0_ReKi + call LiftingLineInducedVelocities(p, x, 1, m, ErrStat2, ErrMsg2); if(Failed()) return ! Interpolation to AeroDyn radial station TODO TODO TODO y%Vind(1:3,:,:) = 0.0_ReKi @@ -622,6 +631,9 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg y%Vind(1:3,iSpan,iW) = m%Vind_LL(1:3,iSpan,iW) enddo enddo + + ! For plotting only + m%Vtot_ll = m%Vind_LL + m%Vwnd_LL - m%Vstr_ll !call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') ! We don't propagate the "Old"-> "New" if update states was not called once diff --git a/modules/aerodyn14/src/FVW_BiotSavart.f90 b/modules/aerodyn14/src/FVW_BiotSavart.f90 index 28ffc1bc3f..e8cdf8bf06 100644 --- a/modules/aerodyn14/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn14/src/FVW_BiotSavart.f90 @@ -333,7 +333,7 @@ subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, RegModel, RegParam, Uind) real(ReKi), intent(in) :: Gamm integer(IntKi) , intent(in) :: RegModel !< Regularization model (e.g. LambOseen) real(ReKi), intent(in) :: RegParam !< Regularization parameter - real(ReKi), dimension(3,nCPs), intent(out) :: Uind !< no side effects!!! + real(ReKi), dimension(3,nCPs), intent(inout) :: Uind !< side effects!!! ! Variable declarations real(ReKi), dimension(3) :: CP !< real(ReKi), dimension(3) :: Uindtmp !< diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index 650ac73521..33d0618918 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -34,18 +34,18 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadCom(UnIn, FileName, 'FVW input file header line 1', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadCom(UnIn, FileName, 'FVW input file header line 2', ErrStat2, ErrMsg2 ); if(Failed()) return !------------------------ GENERAL OPTIONS ------------------------------------------- - CALL ReadCom(UnIn,FileName, 'General option header', ErrStat2, ErrMsg2 ); if(Failed()) return - CALL ReadVar(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%FreeWakeStart,'FreeWakeStart' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadCom(UnIn,FileName, 'General option header', ErrStat2, ErrMsg2 ); if(Failed()) return + CALL ReadVar(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%FreeWakeStart ,'FreeWakeStart' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart' ,'',ErrStat2,ErrMsg2); if(Failed())return !------------------------ CIRCULATION SPECIFICATIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'Circulation specification header', ErrStat2, ErrMsg2 ); if(Failed()) return - CALL ReadVar(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod','',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%CircSolvConvCrit ,'CircSolvConvCrit ','',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CircSolvConvCrit ,'CircSolvConvCrit ' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%CircSolvRelaxation,'CircSolvRelaxation','',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%CircSolvMaxIter ,'CircSolvMaxIter','',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%PrescribedPolar ,'PrescribedPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CircSolvMaxIter ,'CircSolvMaxIter' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%PrescribedPolar ,'PrescribedPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return !------------------------ WAKE OPTIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'Wake options header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return @@ -54,6 +54,13 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%WakeRegFactor ,'WakeRegFactor' ,'',ErrStat2,ErrMsg2); if(Failed())return + !------------------------ OUTPUT OPTIONS ----------------------------------------- + CALL ReadCom(UnIn,FileName, 'Output options header', ErrStat2, ErrMsg2 ); if(Failed()) return + CALL ReadVar(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%VTKBlades , 'VTKBlades' ,'',ErrStat2,ErrMsg2); if(Failed())return + !------------------------ HACK OPTIONS ----------------------------------------- + CALL ReadCom(UnIn,FileName, 'Hack options header', ErrStat2, ErrMsg2 ); if(Failed()) return + CALL ReadVar(UnIn,FileName,Inp%Uinf , 'Uinf' ,'',ErrStat2,ErrMsg2); if(Failed())return ! --- Validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index 77db40a095..a3344d851a 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -441,42 +441,52 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG !real(ReKi), dimension(:), allocatable :: SegSmooth !< ! Counting total number of segments - nP = p%nWings * ( (p%nSpan+1)*(m%nNW-iDepthStart+2) ) - nC = p%nWings * (2*(p%nSpan+1)*(m%nNW-iDepthStart+2)-(p%nSpan+1)-(m%nNW-iDepthStart+1+1)) + nP=0 + nC=0 + if ((m%nNW-iDepthStart)>=0) then + nP = p%nWings * ( (p%nSpan+1)*(m%nNW-iDepthStart+2) ) + nC = p%nWings * (2*(p%nSpan+1)*(m%nNW-iDepthStart+2)-(p%nSpan+1)-(m%nNW-iDepthStart+1+1)) + endif if (m%nFW>0) then nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) nC = nC + p%nWings * (2*(FWnSpan+1)*(m%nFW+1)-(FWnSpan+1)-(m%nFW+1)) endif - if (allocated(SegConnct)) deallocate(SegConnct) - if (allocated(SegPoints)) deallocate(SegPoints) - if (allocated(SegGamma)) deallocate(SegGamma) - if (allocated(Buffer2d)) deallocate(Buffer2d) - allocate(SegConnct(1:2,1:nC)); SegConnct=-1 - allocate(SegPoints(1:3,1:nP)); SegPoints=-1 - allocate(SegGamma (1:nC)); SegGamma =-1 - allocate(Buffer2d(1,p%nSpan)) - ! - iHeadP=1 - iHeadC=1 - do iW=1,p%nWings - CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) - enddo - if (m%nFW>0) then + if (nP>0) then + if (allocated(SegConnct)) deallocate(SegConnct) + if (allocated(SegPoints)) deallocate(SegPoints) + if (allocated(SegGamma)) deallocate(SegGamma) + if (allocated(Buffer2d)) deallocate(Buffer2d) + allocate(SegConnct(1:2,1:nC)); SegConnct=-1 + allocate(SegPoints(1:3,1:nP)); SegPoints=-1 + allocate(SegGamma (1:nC)); SegGamma =-1 + allocate(Buffer2d(1,p%nSpan)) + ! + iHeadP=1 + iHeadC=1 do iW=1,p%nWings - CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) enddo + if (m%nFW>0) then + do iW=1,p%nWings + CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + enddo + endif + if ((iHeadP-1)/=nP) then + print*,'PackPanelsToSegments: Number of points wrongly estimated',nP, iHeadP-1 + STOP + endif + if ((iHeadC-1)/=nC) then + print*,'PackPanelsToSegments: Number of segments wrongly estimated',nC, iHeadC-1 + STOP + endif + nSeg = iHeadC-1 + nSegP = iHeadP-1 + else + print*,'PackPanelsToSegments: nP=',nP + nSeg = 0 + nSegP = 0 endif - if ((iHeadP-1)/=nP) then - print*,'PackPanelsToSegments: Number of points wrongly estimated',nP, iHeadP-1 - STOP - endif - if ((iHeadC-1)/=nC) then - print*,'PackPanelsToSegments: Number of segments wrongly estimated',nC, iHeadC-1 - STOP - endif - nSeg = iHeadC-1 - nSegP = iHeadP-1 end subroutine PackPanelsToSegments !> Compute induced velocities from all vortex elements onto all the vortex elements @@ -583,9 +593,10 @@ subroutine UnPackInducedVelocity() !> Compute induced velocities from all vortex elements onto the lifting line control points !! In : x%r_NW, x%r_FW, x%Gamma_NW, x%Gamma_FW !! Out: m%Vind_LL -subroutine LiftingLineInducedVelocities(p, x, m, ErrStat, ErrMsg) +subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< States + integer(IntKi), intent(in ) :: iDepthStart !< Index where we start packing for NW panels type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables ! Local variables integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs, iHeadP @@ -597,39 +608,44 @@ subroutine LiftingLineInducedVelocities(p, x, m, ErrStat, ErrMsg) real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - - m%Vind_NW = -9999._ReKi !< Safety - m%Vind_FW = -9999._ReKi !< Safety + integer(IntKi) :: i + m%Vind_LL = -9999._ReKi !< Safety ! --- Packing all vortex elements into a list of segments - call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) - !print*,'Number of segments',nSeg, 'Number of points',nSegP - - ! --- Setting up regularization - allocate(SegEpsilon(1:nSeg)); - if (p%WakeRegMethod==idRegConstant) then - SegEpsilon=p%WakeRegFactor ! TODO - else - print*,'Regularization method not implemented',p%WakeRegMethod - STOP - endif + call PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegGamma, nSeg, nSegP) ! --- Computing induced velocity - nCPs=p%nWings * p%nSpan - allocate(CPs (1:3,1:nCPs)) - allocate(Uind(1:3,1:nCPs)) - Uind=0.0_ReKi !< important due to side effects of ui_seg - ! --- - call PackLiftingLinePoints() - call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, p%RegFunction, SegEpsilon, Uind) - call UnPackLiftingLineVelocities() + if (nSegP==0) then + nCPs=0 + m%Vind_LL = 0.0_ReKi + print'(A,I0,A,I0,A,I0,A)','Induction - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs, ' -> No induction' + else + ! --- Setting up regularization + allocate(SegEpsilon(1:nSeg)); + if (p%WakeRegMethod==idRegConstant) then + SegEpsilon=p%WakeRegFactor ! TODO + else + print*,'Regularization method not implemented',p%WakeRegMethod + STOP + endif - deallocate(Uind) - deallocate(CPs) - deallocate(SegConnct) - deallocate(SegGamma) - deallocate(SegPoints) - deallocate(SegEpsilon) + nCPs=p%nWings * p%nSpan + allocate(CPs (1:3,1:nCPs)) + allocate(Uind(1:3,1:nCPs)) + Uind=0.0_ReKi !< important due to side effects of ui_seg + ! --- + call PackLiftingLinePoints() + print'(A,I0,A,I0,A,I0)','Induction - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs + call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, p%RegFunction, SegEpsilon, Uind) + call UnPackLiftingLineVelocities() + + deallocate(Uind) + deallocate(CPs) + deallocate(SegConnct) + deallocate(SegGamma) + deallocate(SegPoints) + deallocate(SegEpsilon) + endif contains !> Pack all the control points subroutine PackLiftingLinePoints() diff --git a/modules/aerodyn14/src/FVW_Types.f90 b/modules/aerodyn14/src/FVW_Types.f90 index e722993f71..7e9dd2c807 100644 --- a/modules/aerodyn14/src/FVW_Types.f90 +++ b/modules/aerodyn14/src/FVW_Types.f90 @@ -61,6 +61,8 @@ MODULE FVW_Types INTEGER(IntKi) :: RegFunction !< Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart) [-] INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] REAL(ReKi) :: WakeRegFactor !< Factor used in the regularization [-] + INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] + INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] END TYPE FVW_ParameterType ! ======================= ! ========= FVW_OtherStateType ======= @@ -156,6 +158,9 @@ MODULE FVW_Types INTEGER(IntKi) :: RegFunction !< Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart) [-] INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] REAL(ReKi) :: WakeRegFactor !< Factor used in the regularization [-] + INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] + INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] + REAL(ReKi) :: Uinf !< TODO TODO TEMPORARY HACK [-] END TYPE FVW_InputFile ! ======================= ! ========= FVW_InitOutputType ======= @@ -210,6 +215,8 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%RegFunction = SrcParamData%RegFunction DstParamData%WakeRegMethod = SrcParamData%WakeRegMethod DstParamData%WakeRegFactor = SrcParamData%WakeRegFactor + DstParamData%WrVTK = SrcParamData%WrVTK + DstParamData%VTKBlades = SrcParamData%VTKBlades END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -282,6 +289,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! RegFunction Int_BufSz = Int_BufSz + 1 ! WakeRegMethod Re_BufSz = Re_BufSz + 1 ! WakeRegFactor + Int_BufSz = Int_BufSz + 1 ! WrVTK + Int_BufSz = Int_BufSz + 1 ! VTKBlades IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -354,6 +363,10 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegFactor Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -447,6 +460,10 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%WakeRegFactor = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%WrVTK = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%VTKBlades = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -4169,6 +4186,9 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%RegFunction = SrcInputFileData%RegFunction DstInputFileData%WakeRegMethod = SrcInputFileData%WakeRegMethod DstInputFileData%WakeRegFactor = SrcInputFileData%WakeRegFactor + DstInputFileData%WrVTK = SrcInputFileData%WrVTK + DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades + DstInputFileData%Uinf = SrcInputFileData%Uinf END SUBROUTINE FVW_CopyInputFile SUBROUTINE FVW_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) @@ -4233,6 +4253,9 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! RegFunction Int_BufSz = Int_BufSz + 1 ! WakeRegMethod Re_BufSz = Re_BufSz + 1 ! WakeRegFactor + Int_BufSz = Int_BufSz + 1 ! WrVTK + Int_BufSz = Int_BufSz + 1 ! VTKBlades + Re_BufSz = Re_BufSz + 1 ! Uinf IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4294,6 +4317,12 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegFactor Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%Uinf + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_PackInputFile SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4362,6 +4391,12 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%WakeRegFactor = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%WrVTK = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%VTKBlades = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%Uinf = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_UnPackInputFile SUBROUTINE FVW_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn14/src/FVW_VortexTools.f90 index 9d3ebca6f5..2153ffbe70 100644 --- a/modules/aerodyn14/src/FVW_VortexTools.f90 +++ b/modules/aerodyn14/src/FVW_VortexTools.f90 @@ -91,10 +91,10 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints ! 1-<-4 do iDepth = iDepthStart, nDepth-1 do iSpan = 1, nSpan-1 - iseg1 = iHeadP0 + (iSpan-1) +(iDepth-1)*nSpan ! Point 1 - iseg2 = iHeadP0 + (iSpan ) +(iDepth-1)*nSpan ! Point 2 - iseg3 = iHeadP0 + (iSpan ) +(iDepth )*nSpan ! Point 3 - iseg4 = iHeadP0 + (iSpan-1) +(iDepth )*nSpan ! Point 4 + iseg1 = iHeadP0 + (iSpan-1) +(iDepth-1-iDepthStart+1)*nSpan ! Point 1 + iseg2 = iHeadP0 + (iSpan ) +(iDepth-1-iDepthStart+1)*nSpan ! Point 2 + iseg3 = iHeadP0 + (iSpan ) +(iDepth -iDepthStart+1)*nSpan ! Point 3 + iseg4 = iHeadP0 + (iSpan-1) +(iDepth -iDepthStart+1)*nSpan ! Point 4 if (iDepth==1) then Gamma12 = LatticeGamma(iSpan,iDepth) else diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index dbcb903fbf..af48553a73 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -159,6 +159,14 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) call interp_lin(m%s_LL(:,iW), m%r_LL(3,:,1,iW)*0.5_ReKi+m%r_LL(3,:,2,iW)*0.5_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(3,:,iW)) enddo + ! --- Structural velocity on LL + ! TODO: difference meshes in/LL + do iW = 1,p%nWings + call interp_lin(m%s_LL(:,iW), Meshes(iW)%TranslationVel(1,:) ,m%s_CP_LL(:,iW), m%Vstr_LL(1,:,iW)) + call interp_lin(m%s_LL(:,iW), Meshes(iW)%TranslationVel(2,:) ,m%s_CP_LL(:,iW), m%Vstr_LL(2,:,iW)) + call interp_lin(m%s_LL(:,iW), Meshes(iW)%TranslationVel(3,:) ,m%s_CP_LL(:,iW), m%Vstr_LL(3,:,iW)) + enddo + end subroutine Wings_Panelling @@ -173,7 +181,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS type(FVW_InputType), intent(in ) :: u !< Parameters type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< Parameters - type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local @@ -191,7 +199,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS else if (p%CirculationMethod==idCircPolarData) then ! --- Solve for circulation using polar data ! TODO - print*,'>>>Circulation solving with polar data' + print*,'>>>>>>>>>>>>>>>>> Circulation solving with polar data' CALL Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg) else if (p%CirculationMethod==idCircNoFlowThrough) then @@ -222,7 +230,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x type(FVW_InputType), intent(in ) :: u !< Parameters type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< Parameters - type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local @@ -231,42 +239,72 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x logical :: bConverged !< integer(IntKi) :: iIter !< iteration step number real(ReKi) :: MeanGamma + real(ReKi), dimension(:,:,:), allocatable :: Vcst !< Part of the velocity that is constant + real(ReKi), dimension(:,:,:), allocatable :: Vvar !< Part of the velocity that is varies due to the solve + integer(IntKi) :: iW, iSpan, iDepth, iWCP, nCPs + real(ReKi), dimension(3) :: P1, P2, P3, P4 + real(ReKi) :: Gamm + ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" print*,'Parameters for circulation solv: ',p%CircSolvConvCrit ,p%CircSolvRelaxation ,p%CircSolvMaxIter - allocate(DGamma (1:p%nSPan,1:p%nWings)) - allocate(GammaLastIter(1:p%nSPan,1:p%nWings)) + allocate(DGamma (1:p%nSpan,1:p%nWings)) + allocate(GammaLastIter(1:p%nSpan,1:p%nWings)) ! - GammaLastIter = Gamma_LL_prev + if (m%FirstCall) then + GammaLastIter = 0 ! TODO better guess + else + GammaLastIter(1:p%nSpan,1:p%nWings) = Gamma_LL_prev(1:p%nSpan,1:p%nWings) + endif + + ! --- Setting up Vcst: part of the velocity that is constant withing the iteration loop + ! Vrel_ll_cst = U_u0 - U_body + call AllocAry(Vvar, 3, p%nSpan, p%nWings, 'Vvar', ErrStat, ErrMsg) + call AllocAry(Vcst, 3, p%nSpan, p%nWings, 'Vcst', ErrStat, ErrMsg) - ! Building Vrel_cst This part do not change wihtin the iteration loop - ! Remember: uiu0 contains U0 and vorticity (free and prescribed) - !do icp=1,SW%ncp_ll_tot - ! TODO - ! SW%Vrel_ll_cst(1:3,icp) = SW%U_uiu0(1:3,icp) - SW%U_body(1:3,icp) + SW%U_solv(1:3,icp) - !end do + ! Set m%Vind_LL Induced velocity from Known wake only (after iNWStart+1) + call LiftingLineInducedVelocities(p, x, iNWStart+1, m, ErrStat, ErrMsg) + Vcst = m%Vind_LL + m%Vwnd_LL - m%Vstr_ll + + if (any(m%Vind_LL(1:3,:,:)<-99)) then + print*,'Wings_ComputeCirculationPolarData: Problem in induced velocity on LL points' + STOP + endif + if (any(m%Vwnd_LL(1:3,:,:)<-99)) then + print*,'Wings_ComputeCirculationPolarData: Problem in wind velocity on LL points' + STOP + endif ! --- Convergence loop until near wake gives induction coherent with circulation bConverged=.false. iIter=0 do while (.not.(bConverged) .and. iIter Date: Thu, 21 Nov 2019 15:10:14 -0700 Subject: [PATCH 027/190] FVW: VTK output option and WingRegFactor added (not used) --- modules/aerodyn14/src/FVW.f90 | 19 ++++++++++-- modules/aerodyn14/src/FVW_IO.f90 | 18 +++++------ modules/aerodyn14/src/FVW_Types.f90 | 41 +++++++++++++++++++------- modules/aerodyn14/src/FVW_Wings.f90 | 12 +++++--- modules/aerodyn14/src/Registry-FVW.txt | 7 +++-- 5 files changed, 69 insertions(+), 28 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 62e5c85fd2..dae97df3fe 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -90,6 +90,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu p%nFWMax = max(InputFileData%nFWPanels,0) p%nFWFree = max(InputFileData%nFWPanelsFree,0) + ! Initialize Misc Vars (may depend on input file) CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return @@ -151,6 +152,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%FirstCall = .True. m%nNW = iNWStart-1 ! Number of active nearwake panels m%nFW = 0 ! Number of active farwake panels + m%iStep = 0 ! Current step number call AllocAry( m%LE , 3 , p%nSpan+1 , p%nWings, 'Leading Edge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%LE = -999999_ReKi; call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%TE = -999999_ReKi; @@ -263,13 +265,14 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%CircSolvRelaxation = InputFileData%CircSolvRelaxation p%CircSolvMaxIter = InputFileData%CircSolvMaxIter p%FreeWakeStart = InputFileData%FreeWakeStart - p%PrescribedPolar = InputFileData%PrescribedPolar + p%CircSolvPolar = InputFileData%CircSolvPolar p%FullCirculationStart = InputFileData%FullCirculationStart p%RegFunction = InputFileData%RegFunction p%WakeRegMethod = InputFileData%WakeRegMethod p%WakeRegFactor = InputFileData%WakeRegFactor + p%WingRegFactor = InputFileData%WingRegFactor p%WrVTK = InputFileData%WrVTK - p%VTKBlades = InputFileData%VTKBlades + p%VTKBlades = min(max(InputFileData%VTKBlades,0),p%nWings) if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) if (InputFileData%CirculationMethod==idCircPrescribed) then @@ -358,6 +361,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ErrMsg = "" dt=utimes(1)-t ! TODO TODO TODO + m%iStep=n print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' dt: ',dt,' ',n,' nNW:',m%nNW,' nFW:',m%nFW @@ -642,6 +646,17 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg call PrepareNextTimeStep() endif + + ! --- Write to VTK + if (p%WrVTK==1) then + if (m%FirstCall) then + call MKDIR('vtk_out') + call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%iStep, 9) + else + call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%iStep+1, 9) + endif + endif + contains subroutine PrepareNextTimeStep() diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index 33d0618918..8209412cb4 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -44,8 +44,8 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar(UnIn,FileName,Inp%CircSolvConvCrit ,'CircSolvConvCrit ' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%CircSolvRelaxation,'CircSolvRelaxation','',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%CircSolvMaxIter ,'CircSolvMaxIter' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%CircSolvPolar ,'CircSolvPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%PrescribedPolar ,'PrescribedPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return !------------------------ WAKE OPTIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'Wake options header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return @@ -54,6 +54,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%WakeRegFactor ,'WakeRegFactor' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%WingRegFactor ,'WingRegFactor' ,'',ErrStat2,ErrMsg2); if(Failed())return !------------------------ OUTPUT OPTIONS ----------------------------------------- CALL ReadCom(UnIn,FileName, 'Output options header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'',ErrStat2,ErrMsg2); if(Failed())return @@ -77,6 +78,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check(.not.(ANY(idRegVALID ==Inp%RegFunction )), 'Regularization function not implemented')) return if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method not implemented')) return if (Check(Inp%WakeRegFactor<0 , 'Wake regularization factor should be positive')) return + if (Check(Inp%WingRegFactor<0 , 'Wing regularization factor should be positive')) return ! At least one NW panel if FW, this shoudln't be a problem since the LL is in NW, but safety for now !if (Check( (Inp%nNWPanels<=0).and.(Inp%nFWPanels>0) , 'At least one near wake panel is required if the number of far wake panel is >0')) return @@ -124,7 +126,6 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) character(Twidth) :: Tstr ! string for current VTK write-out step (padded with zeros) integer :: iSeg integer :: iSpan, iNW, iFW - integer :: nSpan, nWings integer :: k real(ReKi), dimension(:,:), allocatable :: Buffer2d character(1), dimension(3) :: I2ABC =(/'A','B','C'/) @@ -143,13 +144,11 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! TimeStamp write(Tstr, '(i' // trim(Num2LStr(Twidth)) //'.'// trim(Num2LStr(Twidth)) // ')') VTKcount - nSpan = p%nSpan - nWings = p%nWings ! --------------------------------------------------------------------------------} ! --- Blade ! --------------------------------------------------------------------------------{ ! --- Blade Quarter chord points (AC) - do iW=1,nWings + do iW=1,p%VTKBlades write(Label,'(A,A)') 'BldPointCP.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' if ( vtk_new_ascii_file(trim(filename),Label) ) then @@ -167,7 +166,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) endif enddo ! --- Lifting line panels - do iW=1,nWings + do iW=1,p%VTKBlades write(Label,'(A,A)') 'LL.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' call WrVTK_Lattice(FileName, m%r_LL(1:3,:,:,iW), m%Gamma_LL(:,iW:iW)) @@ -176,7 +175,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! --- Near wake ! --------------------------------------------------------------------------------{ ! --- Near wake panels - do iW=1,nWings + do iW=1,p%VTKBlades write(Label,'(A,A)') 'NW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' if (m%FirstCall) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel @@ -189,7 +188,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! --- Far wake ! --------------------------------------------------------------------------------{ ! --- Far wake panels - do iW=1,nWings + do iW=1,p%VTKBlades write(Label,'(A,A)') 'FW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' call WrVTK_Lattice(FileName, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW)) @@ -208,7 +207,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) allocate(SegGamma (1:nC)); SegGamma =-1 iHeadP=1 iHeadC=1 - do iW=1,nWings + do iW=1,p%nWings if (m%nNW==1) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) else @@ -232,7 +231,6 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) STOP endif - end subroutine WrVTK_FVW diff --git a/modules/aerodyn14/src/FVW_Types.f90 b/modules/aerodyn14/src/FVW_Types.f90 index 7e9dd2c807..2f2b36d798 100644 --- a/modules/aerodyn14/src/FVW_Types.f90 +++ b/modules/aerodyn14/src/FVW_Types.f90 @@ -57,10 +57,11 @@ MODULE FVW_Types INTEGER(IntKi) :: CircSolvMaxIter !< Maximum number of iterations for circulation solving [-] REAL(ReKi) :: CircSolvConvCrit !< Convergence criterion for circulation solving [-] REAL(ReKi) :: CircSolvRelaxation !< Relaxation factor for circulation solving [-] - INTEGER(IntKi) :: PrescribedPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] + INTEGER(IntKi) :: CircSolvPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] INTEGER(IntKi) :: RegFunction !< Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart) [-] INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] REAL(ReKi) :: WakeRegFactor !< Factor used in the regularization [-] + REAL(ReKi) :: WingRegFactor !< Factor used in the regularization [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] END TYPE FVW_ParameterType @@ -97,6 +98,7 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: Vind_FW !< Induced velocity on far wake panels [m/s] INTEGER(IntKi) :: nNW !< Number of active near wake panels [-] INTEGER(IntKi) :: nFW !< Number of active far wake panels [-] + INTEGER(IntKi) :: iStep !< Current step number [-] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -151,13 +153,14 @@ MODULE FVW_Types LOGICAL :: FreeWake !< Disable roll up, wake convects with wind only (flag) [-] REAL(ReKi) :: FreeWakeStart !< Time when wake starts convecting (rolling up) [s] REAL(ReKi) :: FullCirculationStart !< Time when the circulation is full [s] - INTEGER(IntKi) :: PrescribedPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] + INTEGER(IntKi) :: CircSolvPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] INTEGER(IntKi) :: nNWPanels !< Number of nw panels [-] INTEGER(IntKi) :: nFWPanels !< Number of fw panels [-] INTEGER(IntKi) :: nFWPanelsFree !< Number of fw panels that are free [-] INTEGER(IntKi) :: RegFunction !< Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart) [-] INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] REAL(ReKi) :: WakeRegFactor !< Factor used in the regularization [-] + REAL(ReKi) :: WingRegFactor !< Factor used in the regularization [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] REAL(ReKi) :: Uinf !< TODO TODO TEMPORARY HACK [-] @@ -211,10 +214,11 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%CircSolvMaxIter = SrcParamData%CircSolvMaxIter DstParamData%CircSolvConvCrit = SrcParamData%CircSolvConvCrit DstParamData%CircSolvRelaxation = SrcParamData%CircSolvRelaxation - DstParamData%PrescribedPolar = SrcParamData%PrescribedPolar + DstParamData%CircSolvPolar = SrcParamData%CircSolvPolar DstParamData%RegFunction = SrcParamData%RegFunction DstParamData%WakeRegMethod = SrcParamData%WakeRegMethod DstParamData%WakeRegFactor = SrcParamData%WakeRegFactor + DstParamData%WingRegFactor = SrcParamData%WingRegFactor DstParamData%WrVTK = SrcParamData%WrVTK DstParamData%VTKBlades = SrcParamData%VTKBlades END SUBROUTINE FVW_CopyParam @@ -285,10 +289,11 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! CircSolvMaxIter Re_BufSz = Re_BufSz + 1 ! CircSolvConvCrit Re_BufSz = Re_BufSz + 1 ! CircSolvRelaxation - Int_BufSz = Int_BufSz + 1 ! PrescribedPolar + Int_BufSz = Int_BufSz + 1 ! CircSolvPolar Int_BufSz = Int_BufSz + 1 ! RegFunction Int_BufSz = Int_BufSz + 1 ! WakeRegMethod Re_BufSz = Re_BufSz + 1 ! WakeRegFactor + Re_BufSz = Re_BufSz + 1 ! WingRegFactor Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades IF ( Re_BufSz .GT. 0 ) THEN @@ -355,7 +360,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_Xferred = Re_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%CircSolvRelaxation Re_Xferred = Re_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%PrescribedPolar + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%CircSolvPolar Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%RegFunction Int_Xferred = Int_Xferred + 1 @@ -363,6 +368,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegFactor Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WingRegFactor + Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades @@ -452,7 +459,7 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + 1 OutData%CircSolvRelaxation = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 - OutData%PrescribedPolar = IntKiBuf( Int_Xferred ) + OutData%CircSolvPolar = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%RegFunction = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 @@ -460,6 +467,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%WakeRegFactor = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%WingRegFactor = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 OutData%WrVTK = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%VTKBlades = IntKiBuf( Int_Xferred ) @@ -968,6 +977,7 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) ENDIF DstMiscData%nNW = SrcMiscData%nNW DstMiscData%nFW = SrcMiscData%nFW + DstMiscData%iStep = SrcMiscData%iStep END SUBROUTINE FVW_CopyMisc SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -1195,6 +1205,7 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si END IF Int_BufSz = Int_BufSz + 1 ! nNW Int_BufSz = Int_BufSz + 1 ! nFW + Int_BufSz = Int_BufSz + 1 ! iStep IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -1643,6 +1654,8 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFW Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%iStep + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2322,6 +2335,8 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%nFW = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%iStep = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) @@ -4179,13 +4194,14 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%FreeWake = SrcInputFileData%FreeWake DstInputFileData%FreeWakeStart = SrcInputFileData%FreeWakeStart DstInputFileData%FullCirculationStart = SrcInputFileData%FullCirculationStart - DstInputFileData%PrescribedPolar = SrcInputFileData%PrescribedPolar + DstInputFileData%CircSolvPolar = SrcInputFileData%CircSolvPolar DstInputFileData%nNWPanels = SrcInputFileData%nNWPanels DstInputFileData%nFWPanels = SrcInputFileData%nFWPanels DstInputFileData%nFWPanelsFree = SrcInputFileData%nFWPanelsFree DstInputFileData%RegFunction = SrcInputFileData%RegFunction DstInputFileData%WakeRegMethod = SrcInputFileData%WakeRegMethod DstInputFileData%WakeRegFactor = SrcInputFileData%WakeRegFactor + DstInputFileData%WingRegFactor = SrcInputFileData%WingRegFactor DstInputFileData%WrVTK = SrcInputFileData%WrVTK DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades DstInputFileData%Uinf = SrcInputFileData%Uinf @@ -4246,13 +4262,14 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! FreeWake Re_BufSz = Re_BufSz + 1 ! FreeWakeStart Re_BufSz = Re_BufSz + 1 ! FullCirculationStart - Int_BufSz = Int_BufSz + 1 ! PrescribedPolar + Int_BufSz = Int_BufSz + 1 ! CircSolvPolar Int_BufSz = Int_BufSz + 1 ! nNWPanels Int_BufSz = Int_BufSz + 1 ! nFWPanels Int_BufSz = Int_BufSz + 1 ! nFWPanelsFree Int_BufSz = Int_BufSz + 1 ! RegFunction Int_BufSz = Int_BufSz + 1 ! WakeRegMethod Re_BufSz = Re_BufSz + 1 ! WakeRegFactor + Re_BufSz = Re_BufSz + 1 ! WingRegFactor Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades Re_BufSz = Re_BufSz + 1 ! Uinf @@ -4303,7 +4320,7 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_Xferred = Re_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%FullCirculationStart Re_Xferred = Re_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%PrescribedPolar + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%CircSolvPolar Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nNWPanels Int_Xferred = Int_Xferred + 1 @@ -4317,6 +4334,8 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegFactor Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WingRegFactor + Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades @@ -4377,7 +4396,7 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Re_Xferred = Re_Xferred + 1 OutData%FullCirculationStart = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 - OutData%PrescribedPolar = IntKiBuf( Int_Xferred ) + OutData%CircSolvPolar = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%nNWPanels = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 @@ -4391,6 +4410,8 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%WakeRegFactor = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%WingRegFactor = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 OutData%WrVTK = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%VTKBlades = IntKiBuf( Int_Xferred ) diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index af48553a73..f35612103e 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -96,6 +96,7 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) real(ReKi), dimension(3) :: DP_TE ! Distance between reference point and trailing edge real(ReKi), dimension(3) :: P1,P2,P3,P4,P5,P7,P8,P6,P9,P10 real(ReKi), dimension(3) :: DP1, DP2, DP3 + !real(ReKi), dimension(3,3) :: MRot ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -109,6 +110,9 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) DP_LE(1) = -m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 DP_TE(1:3) = 0.0 DP_TE(1) = +m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 + !MRot=Meshes(iW)%Orientation(1:3,1:3,iSpan) ! NOTE: this wont work + !DP_LE = matmul(MRot,DP_LE) + !DP_TE = matmul(MRot,DP_TE) m%LE(1:3, iSpan, iW) = P_ref + DP_LE m%TE(1:3, iSpan, iW) = P_ref + DP_TE enddo @@ -377,15 +381,15 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m) alpha = atan2(dot_product(Vrel,N) , dot_product(Vrel,Tc) ) ! [rad] !Re = LL%Vrel_orth_norm(icp)*LL%chord(icp)/KinVisc/(1.E6_MK) ! TODO TODO TODO KinVisc - if (p%PrescribedPolar==idPolarAeroDyn) then + if (p%CircSolvPolar==idPolarAeroDyn) then print*,'TODO TODO TODO Get Cl, Cd, Cm from alpha, Re and AirfoilInfo' STOP - else if (p%PrescribedPolar==idPolar2PiAlpha) then + else if (p%CircSolvPolar==idPolar2PiAlpha) then Cl=TwoPi*alpha - else if (p%PrescribedPolar==idPolar2PiSinAlpha) then + else if (p%CircSolvPolar==idPolar2PiSinAlpha) then Cl=TwoPi*sin(alpha) else - print*,'Unknown PrescribedPolar value' + print*,'Unknown CircSolvPolar value' STOP endif ! Simple method: diff --git a/modules/aerodyn14/src/Registry-FVW.txt b/modules/aerodyn14/src/Registry-FVW.txt index 09f5de8efc..cfd5b4c647 100644 --- a/modules/aerodyn14/src/Registry-FVW.txt +++ b/modules/aerodyn14/src/Registry-FVW.txt @@ -23,10 +23,11 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi CircSolvMaxIter - - - "Maximum number of iterations for circulation solving" - typedef ^ ^ ReKi CircSolvConvCrit - - - "Convergence criterion for circulation solving" - typedef ^ ^ ReKi CircSolvRelaxation - - - "Relaxation factor for circulation solving" - -typedef ^ ^ IntKi PrescribedPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - +typedef ^ ^ IntKi CircSolvPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - typedef ^ ^ IntKi RegFunction - - - "Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart)" - typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - typedef ^ ^ ReKi WakeRegFactor - - - "Factor used in the regularization " +typedef ^ ^ ReKi WingRegFactor - - - "Factor used in the regularization " typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - @@ -63,6 +64,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi Vind_FW :::: - - "Induced velocity on far wake panels" m/s typedef ^ ^ IntKi nNW - - - "Number of active near wake panels" - typedef ^ ^ IntKi nFW - - - "Number of active far wake panels" - +typedef ^ ^ IntKi iStep - - - "Current step number" - # ........ Input ............ # FVW_InputType @@ -113,13 +115,14 @@ typedef ^ ^ IntKi typedef ^ ^ LOGICAL FreeWake - - - "Disable roll up, wake convects with wind only (flag)" - typedef ^ ^ ReKi FreeWakeStart - - - "Time when wake starts convecting (rolling up)" s typedef ^ ^ ReKi FullCirculationStart - - - "Time when the circulation is full" s -typedef ^ ^ IntKi PrescribedPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - +typedef ^ ^ IntKi CircSolvPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - typedef ^ ^ IntKi nNWPanels - - - "Number of nw panels" - typedef ^ ^ IntKi nFWPanels - - - "Number of fw panels" - typedef ^ ^ IntKi nFWPanelsFree - - - "Number of fw panels that are free" - typedef ^ ^ IntKi RegFunction - - - "Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart)" - typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - typedef ^ ^ ReKi WakeRegFactor - - - "Factor used in the regularization " +typedef ^ ^ ReKi WingRegFactor - - - "Factor used in the regularization " typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ ReKi Uinf - - - "TODO TODO TEMPORARY HACK" From 4a4b55c4aad94a06e8ac1a06d39d62b63794bc68 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 20:01:10 -0700 Subject: [PATCH 028/190] FVW: Bug Fix: starting at iDepth --- modules/aerodyn14/src/FVW_VortexTools.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn14/src/FVW_VortexTools.f90 index 2153ffbe70..1c286c7d17 100644 --- a/modules/aerodyn14/src/FVW_VortexTools.f90 +++ b/modules/aerodyn14/src/FVW_VortexTools.f90 @@ -95,7 +95,7 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints iseg2 = iHeadP0 + (iSpan ) +(iDepth-1-iDepthStart+1)*nSpan ! Point 2 iseg3 = iHeadP0 + (iSpan ) +(iDepth -iDepthStart+1)*nSpan ! Point 3 iseg4 = iHeadP0 + (iSpan-1) +(iDepth -iDepthStart+1)*nSpan ! Point 4 - if (iDepth==1) then + if (iDepth==iDepthStart) then Gamma12 = LatticeGamma(iSpan,iDepth) else Gamma12 = LatticeGamma(iSpan,iDepth)-LatticeGamma(iSpan,iDepth-1) From b85b7493b6abeb332b8d282b52cb3e397f5a9e62 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 20:02:57 -0700 Subject: [PATCH 029/190] FVW: Bug Fix: mapping required before final circulation solve --- modules/aerodyn14/src/FVW.f90 | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index dae97df3fe..f75a0af584 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -383,7 +383,8 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! TODO ANDY !CALL DistributeRequestedWind(u(1)%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return - ! Solve for circulation at t + ! --- Solve for circulation at t + ! Returns: z%Gamma_LL (at t) call AllocAry( z_guess%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat, ErrMsg ); z_guess%Gamma_LL = m%Gamma_LL call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return @@ -423,10 +424,17 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! Panelling wings based on input mesh at t+dt call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return - ! Solve for circulation at t+dt - call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return - - ! Map circulation and positions between LL and NW and then NW and FW + ! Updating positions of first NW and FW panels (Circulation also updated but irrelevant) + ! Changes: x only + call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2) + call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) + + ! --- Solve for circulation at t+dt + ! Returns: z%Gamma_LL (at t+dt) + z_guess%Gamma_LL = z%Gamma_LL ! We use as guess the circulation from the previous time step (see above) + call FVW_CalcConstrStateResidual(t+dt, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2, 2); if(Failed()) return + + ! Updating circulation of near wake panel (and position but irrelevant) ! Changes: x only call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2) call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) From 1f64e28195476726167759e456c432b3beaad95d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 20:05:48 -0700 Subject: [PATCH 030/190] FVW: Introducing a hack parameter for convenience --- modules/aerodyn14/src/FVW.f90 | 9 +++++++++ modules/aerodyn14/src/FVW_IO.f90 | 1 + modules/aerodyn14/src/FVW_Types.f90 | 14 ++++++++++++++ modules/aerodyn14/src/FVW_Wings.f90 | 3 +++ modules/aerodyn14/src/Registry-FVW.txt | 2 ++ 5 files changed, 29 insertions(+) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index f75a0af584..9bc8423e14 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -90,6 +90,9 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu p%nFWMax = max(InputFileData%nFWPanels,0) p%nFWFree = max(InputFileData%nFWPanelsFree,0) + if (InputFileData%HACK==1) then + p%nWings=1 ! Elliptical wing temporary hack + endif ! Initialize Misc Vars (may depend on input file) CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return @@ -101,6 +104,11 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu m%Vwnd_LL(1,:,:) = InputFileData%Uinf m%Vwnd_NW(1,:,:,:) = InputFileData%Uinf m%Vwnd_FW(1,:,:,:) = InputFileData%Uinf + if (InputFileData%HACK==1) then + m%Vwnd_LL(3,:,:) =0.1 + m%Vwnd_NW(3,:,:,:) =0.1 + m%Vwnd_FW(3,:,:,:) =0.1 + endif ! Preliminary meshing of the wings (may depend on input file) @@ -273,6 +281,7 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%WingRegFactor = InputFileData%WingRegFactor p%WrVTK = InputFileData%WrVTK p%VTKBlades = min(max(InputFileData%VTKBlades,0),p%nWings) + p%HACK = InputFileData%HACK if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) if (InputFileData%CirculationMethod==idCircPrescribed) then diff --git a/modules/aerodyn14/src/FVW_IO.f90 b/modules/aerodyn14/src/FVW_IO.f90 index 8209412cb4..23b83ee2d5 100644 --- a/modules/aerodyn14/src/FVW_IO.f90 +++ b/modules/aerodyn14/src/FVW_IO.f90 @@ -62,6 +62,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) !------------------------ HACK OPTIONS ----------------------------------------- CALL ReadCom(UnIn,FileName, 'Hack options header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%Uinf , 'Uinf' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%HACK , 'HACK' ,'',ErrStat2,ErrMsg2); if(Failed())return ! --- Validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) diff --git a/modules/aerodyn14/src/FVW_Types.f90 b/modules/aerodyn14/src/FVW_Types.f90 index 2f2b36d798..917c9597f5 100644 --- a/modules/aerodyn14/src/FVW_Types.f90 +++ b/modules/aerodyn14/src/FVW_Types.f90 @@ -64,6 +64,7 @@ MODULE FVW_Types REAL(ReKi) :: WingRegFactor !< Factor used in the regularization [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] + INTEGER(IntKi) :: HACK !< HACK ID [-] END TYPE FVW_ParameterType ! ======================= ! ========= FVW_OtherStateType ======= @@ -164,6 +165,7 @@ MODULE FVW_Types INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] REAL(ReKi) :: Uinf !< TODO TODO TEMPORARY HACK [-] + INTEGER(IntKi) :: HACK !< HACK ID [-] END TYPE FVW_InputFile ! ======================= ! ========= FVW_InitOutputType ======= @@ -221,6 +223,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%WingRegFactor = SrcParamData%WingRegFactor DstParamData%WrVTK = SrcParamData%WrVTK DstParamData%VTKBlades = SrcParamData%VTKBlades + DstParamData%HACK = SrcParamData%HACK END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -296,6 +299,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_BufSz = Re_BufSz + 1 ! WingRegFactor Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades + Int_BufSz = Int_BufSz + 1 ! HACK IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -374,6 +378,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%HACK + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -473,6 +479,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%VTKBlades = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%HACK = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -4205,6 +4213,7 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%WrVTK = SrcInputFileData%WrVTK DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades DstInputFileData%Uinf = SrcInputFileData%Uinf + DstInputFileData%HACK = SrcInputFileData%HACK END SUBROUTINE FVW_CopyInputFile SUBROUTINE FVW_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) @@ -4273,6 +4282,7 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades Re_BufSz = Re_BufSz + 1 ! Uinf + Int_BufSz = Int_BufSz + 1 ! HACK IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4342,6 +4352,8 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%Uinf Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%HACK + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackInputFile SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4418,6 +4430,8 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%Uinf = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%HACK = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackInputFile SUBROUTINE FVW_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index f35612103e..f8651731f8 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -106,6 +106,9 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) do iW = 1,p%nWings do iSpan = 1,p%nSpan+1 P_ref = Meshes(iW)%Position(1:3, iSpan ) + if (p%HACK==1) then + P_ref(3)=100 + endif DP_LE(1:3) = 0.0 DP_LE(1) = -m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 DP_TE(1:3) = 0.0 diff --git a/modules/aerodyn14/src/Registry-FVW.txt b/modules/aerodyn14/src/Registry-FVW.txt index cfd5b4c647..8b8c3977bd 100644 --- a/modules/aerodyn14/src/Registry-FVW.txt +++ b/modules/aerodyn14/src/Registry-FVW.txt @@ -30,6 +30,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi WingRegFactor - - - "Factor used in the regularization " typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - +typedef ^ ^ IntKi HACK - - - "HACK ID" - # ....... OtherStateType ............ # FVW_OtherStateType @@ -126,6 +127,7 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ ReKi Uinf - - - "TODO TODO TEMPORARY HACK" +typedef ^ ^ IntKi HACK - - - "HACK ID" - #.......... InitOutputType ...... # FVW_InitOutputType From b9bcda133cff235ce0608314b0b77f288b5ac269 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 20:07:29 -0700 Subject: [PATCH 031/190] FVW: using a better guess for first wing solve iteration --- modules/aerodyn14/src/FVW_Wings.f90 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index f8651731f8..ae6839497d 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -256,13 +256,16 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x ErrStat = ErrID_None ErrMsg = "" - print*,'Parameters for circulation solv: ',p%CircSolvConvCrit ,p%CircSolvRelaxation ,p%CircSolvMaxIter + !print*,'Parameters for circulation solv: ',p%CircSolvConvCrit ,p%CircSolvRelaxation ,p%CircSolvMaxIter allocate(DGamma (1:p%nSpan,1:p%nWings)) allocate(GammaLastIter(1:p%nSpan,1:p%nWings)) - ! + + ! --- Last iteration circulation if (m%FirstCall) then - GammaLastIter = 0 ! TODO better guess + ! We find a guess by looking simply at the Wind and Elasticity velocity + m%Vtot_ll = m%Vwnd_LL - m%Vstr_ll + call CirculationFromPolarData(GammaLastIter, p, m) else GammaLastIter(1:p%nSpan,1:p%nWings) = Gamma_LL_prev(1:p%nSpan,1:p%nWings) endif From dad353950e474fbe78854a766e6a73c2ff84455f Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 20:08:09 -0700 Subject: [PATCH 032/190] FVW: Bug Fix: chord was modified instead of settting CP_LL --- modules/aerodyn14/src/FVW_Wings.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index ae6839497d..f993fd6766 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -65,8 +65,8 @@ subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) ! --- Control points ! TODO possibly Control points are not exactly at the middle depending on "meshing" method do iSpan = 1, p%nSpan - m%s_CP_LL (iSpan, iW) = (m%s_LL (iSpan,iW)+ m%s_LL (iSpan+1,iW))/2 - m%chord_LL(iSpan, iW) = (m%chord_LL(iSpan,iW)+ m%chord_LL(iSpan+1,iW))/2 + m%s_CP_LL (iSpan, iW) = (m%s_LL (iSpan,iW)+ m%s_LL (iSpan+1,iW))/2 + m%chord_CP_LL(iSpan, iW) = (m%chord_LL(iSpan,iW)+ m%chord_LL(iSpan+1,iW))/2 enddo enddo end subroutine Wings_Panelling_Init From b17933b43109b938d2f95b7814700bdd31f032cd Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 20:09:31 -0700 Subject: [PATCH 033/190] FVW: Control points put exactly on the lifting line (should be changed for no flow through --- modules/aerodyn14/src/FVW_Wings.f90 | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index f993fd6766..a62f1bef08 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -110,9 +110,9 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) P_ref(3)=100 endif DP_LE(1:3) = 0.0 - DP_LE(1) = -m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 + DP_LE(1) = -m%chord_LL(iSpan,iW)/4. ! TODO TODO TODO Use orientation and might not be c/2 DP_TE(1:3) = 0.0 - DP_TE(1) = +m%chord_LL(iSpan,iW)/2 ! TODO TODO TODO Use orientation and might not be c/2 + DP_TE(1) = +3.*m%chord_LL(iSpan,iW)/4. ! TODO TODO TODO Use orientation and might not be c/2 !MRot=Meshes(iW)%Orientation(1:3,1:3,iSpan) ! NOTE: this wont work !DP_LE = matmul(MRot,DP_LE) !DP_TE = matmul(MRot,DP_TE) @@ -146,7 +146,6 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) m%Area(iSpan, iW) = norm2(cross_product(DP1,DP3)); end do enddo - ! --- Lifting Line/ Bound Circulation panel ! For now: goes from 1/4 chord to TE ! More panelling options may be considered in the future @@ -158,12 +157,12 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) enddo ! --- Position of control points CP_LL - ! For now: placed at the "chordwise" middle of the LL panel + ! For now: placed exactly on the LL panel ! NOTE: separated from other loops just in case a special discretization is used do iW = 1,p%nWings - call interp_lin(m%s_LL(:,iW), m%r_LL(1,:,1,iW)*0.5_ReKi+m%r_LL(1,:,2,iW)*0.5_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(1,:,iW)) - call interp_lin(m%s_LL(:,iW), m%r_LL(2,:,1,iW)*0.5_ReKi+m%r_LL(2,:,2,iW)*0.5_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(2,:,iW)) - call interp_lin(m%s_LL(:,iW), m%r_LL(3,:,1,iW)*0.5_ReKi+m%r_LL(3,:,2,iW)*0.5_ReKi ,m%s_CP_LL(:,iW), m%CP_LL(3,:,iW)) + call interp_lin(m%s_LL(:,iW), m%r_LL(1,:,1,iW), m%s_CP_LL(:,iW), m%CP_LL(1,:,iW)) + call interp_lin(m%s_LL(:,iW), m%r_LL(2,:,1,iW), m%s_CP_LL(:,iW), m%CP_LL(2,:,iW)) + call interp_lin(m%s_LL(:,iW), m%r_LL(3,:,1,iW), m%s_CP_LL(:,iW), m%CP_LL(3,:,iW)) enddo ! --- Structural velocity on LL From 92655db11b100740de238699f0528f2e397ce0f8 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 20:58:15 -0700 Subject: [PATCH 034/190] FVW: Adding checks and screen outputs --- modules/aerodyn14/src/FVW.f90 | 15 +++-- modules/aerodyn14/src/FVW_Subs.f90 | 18 +++--- modules/aerodyn14/src/FVW_VortexTools.f90 | 4 +- modules/aerodyn14/src/FVW_Wings.f90 | 75 +++++++++++++++-------- 4 files changed, 72 insertions(+), 40 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 9bc8423e14..3f6be0bd5c 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -396,7 +396,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! Returns: z%Gamma_LL (at t) call AllocAry( z_guess%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat, ErrMsg ); z_guess%Gamma_LL = m%Gamma_LL - call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2); if(Failed()) return + call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2, 1); if(Failed()) return ! Map circulation and positions between LL and NW and then NW and FW ! Changes: x only @@ -566,7 +566,7 @@ end subroutine FVW_Euler1 !---------------------------------------------------------------------------------------------------------------------------------- !> This is a tight coupling routine for solving for the residual of the constraint state functions. -subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, z_out, ErrStat, ErrMsg ) +subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, z_out, ErrStat, ErrMsg, iLabel) real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(FVW_InputType), intent(in ) :: u !< Inputs at t type(FVW_ParameterType), intent(in ) :: p !< Parameters @@ -576,6 +576,7 @@ subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, type(FVW_OtherStateType), intent(in ) :: OtherState !< Other states at t type(FVW_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) type(FVW_ConstraintStateType), intent( out) :: z_out !< Residual of the constraint state functions using + integer(IntKi), intent(in) :: iLabel !! the input values described above integer(IntKi), intent( OUT) :: ErrStat !< Error status of the operation character(*), intent( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None @@ -588,7 +589,7 @@ subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, call AllocAry( z_out%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat, ErrMsg ); z_out%Gamma_LL = -999999_ReKi; - CALL Wings_ComputeCirculation(t, z_out%Gamma_LL, z_guess%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) + CALL Wings_ComputeCirculation(t, z_out%Gamma_LL, z_guess%Gamma_LL, u, p, x, m, ErrStat, ErrMsg, iLabel) end subroutine FVW_CalcConstrStateResidual @@ -628,13 +629,14 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg if (m%FirstCall) then print*,'>>> First Call of CalcOutput, calling panelling and constrstate' CALL Wings_Panelling(u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return - CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg) ! For plotting only + CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg, 0) ! For plotting only else m%Gamma_LL = z%Gamma_LL ! For plotting only endif if (.not. allocated(y%Vind)) then - call AllocAry( y%Vind , 3, p%nSpan+1, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO potentially nSpan+1 for AD15 + !call AllocAry( y%Vind , 3, p%nSpan+1, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO potentially nSpan+1 for AD15 + call AllocAry( y%Vind , 3, p%nSpan+1, 3, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! NOTE: temporary hack 3 blades if(Failed()) return endif ! Returned guessed locations where wind will be required @@ -655,7 +657,8 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ! For plotting only m%Vtot_ll = m%Vind_LL + m%Vwnd_LL - m%Vstr_ll - !call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') + call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') + call print_mean_3d(m%Vtot_LL,'Mean relativevel. LL') ! We don't propagate the "Old"-> "New" if update states was not called once ! This is introduced since at init, CalcOutput is called before UpdateState diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index a3344d851a..671c48e681 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -60,19 +60,21 @@ END FUNCTION interpolation_array ! ===================================================================================== !> Output blade circulation -subroutine Output_Gamma(CP, Gamma_LL, iWing, iCall, Time) +subroutine Output_Gamma(CP, Gamma_LL, iWing, iStep, iLabel, iIter) real( ReKi ), dimension( :, : ), intent(in ) :: CP !< Control Points real( ReKi ), dimension( : ), intent(in ) :: Gamma_LL !< Circulation on the lifting line integer( IntKi ), intent(in ) :: iWing !< Wing index - integer( IntKi ), intent(in ) :: iCall !< Call ID - real(DbKi), intent(in ) :: Time + integer( IntKi ), intent(in ) :: iStep !< Call ID + integer( IntKi ), intent(in ) :: iLabel !< Call ID + integer( IntKi ), intent(in ) :: iIter !< Call ID character(len=255) :: filename integer :: i integer :: iUnit real(ReKi) :: norm call GetNewUnit(iUnit) ! TODO output folder - write(filename,'(A,I0,A,I0,A,I0,A)')'Gamma/Gamma_call',int(iCall),'_t',int(Time*10000),'_Wing',int(iWing),'.txt' + CALL MKDIR('Gamma') + write(filename,'(A,I0,A,I0,A,I0,A,I0,A)')'Gamma/Gamma_step',int(iStep),'_lab',iLabel,'_it',iIter,'_Wing',int(iWing),'.txt' OPEN(unit = iUnit, file = trim(filename), status="unknown", action="write") write(iUnit,'(A)') 'norm_[m],x_[m],y_[m],z_[m], Gamma_[m^2/s]' do i=1,size(Gamma_LL) @@ -245,7 +247,7 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) x%r_FW(1:3,iSpan,iAge,iW) = x%r_FW(1:3,iSpan,iAge-1,iW) enddo enddo - x%r_FW(1:3,1:FWnSpan,1,iW) = -999.0_ReKi ! Nullified + x%r_FW(1:3,1:FWnSpan,1,iW) = -999.9_ReKi ! Nullified enddo if (p%nFWMax>0) then do iW=1,p%nWings @@ -254,7 +256,7 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) x%Gamma_FW(iSpan,iAge,iW) = x%Gamma_FW(iSpan,iAge-1,iW) enddo enddo - x%Gamma_FW(1,1:FWnSpan-1,iW) = -999.0_ReKi ! Nullified + x%Gamma_FW(1,1:FWnSpan-1,iW) = -999.9_ReKi ! Nullified enddo endif ! --- Propagate near wake @@ -264,7 +266,7 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) x%r_NW(1:3,iSpan,iAge,iW) = x%r_NW(1:3,iSpan,iAge-1,iW) enddo enddo - x%r_NW(1:3,:,1:iNWStart,iW) = -999.0_ReKi ! Nullified + x%r_NW(1:3,:,1:iNWStart,iW) = -999.9_ReKi ! Nullified enddo if (p%nNWMax>1) then do iW=1,p%nWings @@ -273,7 +275,7 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) x%Gamma_NW(iSpan,iAge,iW) = x%Gamma_NW(iSpan,iAge-1,iW) enddo enddo - x%Gamma_NW(:,1:iNWStart,iW) = -999.0_ReKi ! Nullified + x%Gamma_NW(:,1:iNWStart,iW) = -999.9_ReKi ! Nullified enddo endif end subroutine PropagateWake diff --git a/modules/aerodyn14/src/FVW_VortexTools.f90 b/modules/aerodyn14/src/FVW_VortexTools.f90 index 1c286c7d17..483c906d1d 100644 --- a/modules/aerodyn14/src/FVW_VortexTools.f90 +++ b/modules/aerodyn14/src/FVW_VortexTools.f90 @@ -146,7 +146,7 @@ subroutine print_mean_4d(M, Label) U(1:3)= U(1:3)+ M(1:3, k, j, i) enddo; enddo; enddo; U(1:3)=U(1:3)/ (size(M,4)*size(M,3)*size(M,2)) - print'(A20,3F12.4)',trim(Label),U + print'(A25,3F12.4)',trim(Label),U if(U(1)<-99) STOP end subroutine @@ -161,7 +161,7 @@ subroutine print_mean_3d(M, Label) U(1:3)= U(1:3)+ M(1:3, j, i) enddo; enddo; U(1:3)=U(1:3)/ (size(M,3)*size(M,2)) - print'(A20,3F12.4)',trim(Label),U + print'(A26,3F12.4)',trim(Label)//' ',U end subroutine diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index a62f1bef08..ca4a48dbab 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -175,12 +175,27 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) end subroutine Wings_Panelling - +! print*,' Norm Tang ' +! print*, m%Norm(1:3,5,1) +! print*, m%Tang(1:3,5,1) +! print*,' ' +! print*,'LE1',m%LE(1,:,1) +! print*,'LE2',m%LE(2,:,1) +! print*,'LE3',m%LE(3,:,1) +! print*,'' +! print*,'TE1',m%LE(1,:,1) +! print*,'TE2',m%LE(2,:,1) +! print*,'TE3',m%LE(3,:,1) +! print*,'' +! print*,'CP1',m%CP_LL(1,:,1) +! print*,'CP2',m%CP_LL(2,:,1) +! print*,'CP3',m%CP_LL(3,:,1) +! !---------------------------------------------------------------------------------------------------------------------------------- !> - subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg) + subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg, iLabel) real(DbKi), intent(in ) :: t !< Current simulation time in seconds real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines real(ReKi), dimension(:,:), intent(in ) :: Gamma_LL_prev !< Previous/Guessed circulation @@ -190,6 +205,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi), intent(in) :: iLabel ! Local integer(IntKi) :: iW ! Initialize ErrStat @@ -204,9 +220,8 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS else if (p%CirculationMethod==idCircPolarData) then ! --- Solve for circulation using polar data - ! TODO - print*,'>>>>>>>>>>>>>>>>> Circulation solving with polar data' - CALL Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg) + !print*,'>>>>>>>>>>>>>>>>> Circulation solving with polar data >>>>>>>>>>>>>> CALL ',iLabel + CALL Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg, iLabel) else if (p%CirculationMethod==idCircNoFlowThrough) then ! --- Solve for circulation using the no-flow through condition @@ -229,7 +244,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS !---------------------------------------------------------------------------------------------------------------------------------- !> - subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg) + subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg, iLabel) real(DbKi), intent(in ) :: t !< Current simulation time in seconds real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines real(ReKi), dimension(:,:), intent(in ) :: Gamma_LL_prev !< Previous/Guessed circulation @@ -239,6 +254,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi), intent(in) :: iLabel ! Local real(ReKi), dimension(:,:), allocatable :: DGamma !< real(ReKi), dimension(:,:), allocatable :: GammaLastIter !< @@ -269,6 +285,12 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x GammaLastIter(1:p%nSpan,1:p%nWings) = Gamma_LL_prev(1:p%nSpan,1:p%nWings) endif + if (any(x%r_NW(1,:,1:m%nNW+1,:)<-999)) then + print*,'Wings_ComputeCirculationPolarData: Problem in input NW points' + STOP + endif + + ! --- Setting up Vcst: part of the velocity that is constant withing the iteration loop ! Vrel_ll_cst = U_u0 - U_body call AllocAry(Vvar, 3, p%nSpan, p%nWings, 'Vvar', ErrStat, ErrMsg) @@ -311,7 +333,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x enddo enddo ! Total velocity on the lifting line - m%vtot_ll = vcst + vvar + m%Vtot_ll = Vcst + Vvar !call print_mean_3d( Vvar(:,:,:), 'Mean induced vel. LL (var)') !call print_mean_3d( m%Vtot_LL(:,:,:), 'Mean relativevel. LL (tot)') ! --- Computing circulation based on Vtot_LL @@ -327,33 +349,38 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x MeanGamma = sum(abs(GammaLastIter))/(p%nWings*p%nSpan) !print*,'Crit',maxval(abs(DGamma))/(MeanGamma) bConverged = maxval(abs(DGamma))/(MeanGamma)0.01) STOP + !if (m%iStep==3) STOP end subroutine From 1a315c315b35948c6df53caa672072b653aff05e Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 21:33:38 -0700 Subject: [PATCH 035/190] FVW: small fix of convection of FW when not needed --- modules/aerodyn14/src/FVW.f90 | 13 +++++++------ modules/aerodyn14/src/FVW_Subs.f90 | 12 +++++++++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/modules/aerodyn14/src/FVW.f90 b/modules/aerodyn14/src/FVW.f90 index 3f6be0bd5c..d05acc91a6 100644 --- a/modules/aerodyn14/src/FVW.f90 +++ b/modules/aerodyn14/src/FVW.f90 @@ -203,8 +203,8 @@ subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) call AllocAry( x%Gamma_NW, p%nSpan , p%nNWMax , p%nWings, 'NW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_NW = -999999_ReKi; call AllocAry( x%Gamma_FW, FWnSpan , p%nFWMax , p%nWings, 'FW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_FW = -999999_ReKi; - call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_NW = -99_ReKi; - call AllocAry( x%r_FW , 3, FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = -99_ReKi; + call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_NW = -999_ReKi; + call AllocAry( x%r_FW , 3, FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = -999_ReKi; if (ErrStat >= AbortErrLev) return @@ -420,7 +420,6 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! --- t+dt - ! Propagation/creation of new layer of panels call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Prop_') @@ -437,6 +436,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! Changes: x only call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2) call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) + !call print_x_NW_FW(p, m, z, x,'Map2') ! --- Solve for circulation at t+dt ! Returns: z%Gamma_LL (at t+dt) @@ -447,7 +447,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! Changes: x only call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2) call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) - !call print_x_NW_FW(p, m, z, x,'Map_') + !call print_x_NW_FW(p, m, z, x,'Map3') !if (m%nFW>4) STOP !if (t>0.5) STOP @@ -553,8 +553,9 @@ subroutine FVW_Euler1( t, dt, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) ! Update of positions x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dt * dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) - x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) = x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + dt * dxdt%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) - + if ( m%nFW>0) then + x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) = x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + dt * dxdt%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + endif ! Update of Gamma ! TODO, viscous diffusion, stretching diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index 671c48e681..28e9de8c50 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -281,21 +281,22 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) end subroutine PropagateWake -subroutine print_r_NW_FW(p, m, z, x, label) +subroutine print_x_NW_FW(p, m, z, x, label) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states character(len=*),intent(in) :: label integer(IntKi) :: iAge - print*,'NW' + print*,'-------------------------' + print*,' NW .....................' do iAge=1,p%nNWMax+1 print*,'iAge',iAge print*,trim(label), x%r_NW(1, 1, iAge,1), x%r_NW(1, p%nSpan+1, iAge,1) print*,trim(label), x%r_NW(2, 1, iAge,1), x%r_NW(2, p%nSpan+1, iAge,1) print*,trim(label), x%r_NW(3, 1, iAge,1), x%r_NW(3, p%nSpan+1, iAge,1) enddo - print*,'FW' + print*,'FW <<<<<<<<<<<<<<<<<<<<' do iAge=1,p%nFWMax+1 print*,'iAge',iAge print*,trim(label), x%r_FW(1, 1, iAge,1), x%r_FW(1, FWnSpan+1, iAge,1) @@ -563,6 +564,11 @@ subroutine PackConvectingPoints() enddo endif + if (any(CPs(1,:)<=-99)) then + print*,'WakeInducedVelocities: Problem in Control points' + STOP + endif + if ((iHeadP-1)/=size(CPs,2)) then print*,'PackConvectingPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 STOP From 7df9f2c347be5e4aefb1f8a7ecd6849843d032e3 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 22:39:34 -0700 Subject: [PATCH 036/190] FVW: using openmp --- modules/aerodyn14/src/FVW_BiotSavart.f90 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/aerodyn14/src/FVW_BiotSavart.f90 b/modules/aerodyn14/src/FVW_BiotSavart.f90 index e8cdf8bf06..84d41f96ff 100644 --- a/modules/aerodyn14/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn14/src/FVW_BiotSavart.f90 @@ -193,7 +193,8 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & real(ReKi), dimension(3) :: DP1, DP2 !< real(ReKi) :: norm2_r0 !< real(ReKi) :: Gam !< - real(ReKi) :: RegParam1 !< + real(ReKi) :: RegParam1 !< + real(ReKi) :: RegParam2 !< Square of viscous param ! Variables declaration real(ReKi),dimension(3) :: crossprod !< real(ReKi) :: denom !< @@ -210,15 +211,14 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & real(ReKi) :: xb !< real(ReKi) :: yb !< real(ReKi) :: zb !< - real(ReKi) :: RegParam2 !< Square of viscous param real(ReKi) :: exp_value !< real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) - !OMP PARALLEL default(shared) - !OMP do private(& - !OMP& icp,is,Uind,P1,P2,DP1,DP2,norm2_r0,Gam,RegParam,& - !OMP& crossprod,denom,denominator,h2,h,Kv,norm_a,norm_b,norm2_crossprod,xa,ya,za,xb,yb,zb,RegParam2,exp_value& - !OMP& ) schedule(runtime) + !$OMP PARALLEL default(shared) + !$OMP do private(& + !$OMP& icp,is,Uind,P1,P2,DP1,DP2,norm2_r0,Gam,RegParam1,RegParam2,& + !$OMP& crossprod,denom,denominator,h2,h,Kv,norm_a,norm_b,norm2_crossprod,xa,ya,za,xb,yb,zb,exp_value& + !$OMP& ) schedule(runtime) ! loop on CPs do icp=iCPStart,iCPEnd do is=iSegStart,iSegEnd ! loop on selected segments @@ -319,8 +319,8 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & Uind_out(1:3,icp) = Uind_out(1:3,icp)+Uind(1:3) end do ! Loop on segments enddo ! Loop on control points - !OMP END DO - !OMP END PARALLEL + !$OMP END DO + !$OMP END PARALLEL end subroutine From 208d7d48623c10f9b0679712003b7ecf66d0188e Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 21 Nov 2019 22:50:01 -0700 Subject: [PATCH 037/190] FVW: using maxval --- modules/aerodyn14/src/FVW_Subs.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/aerodyn14/src/FVW_Subs.f90 b/modules/aerodyn14/src/FVW_Subs.f90 index 28e9de8c50..1a80ab09f6 100644 --- a/modules/aerodyn14/src/FVW_Subs.f90 +++ b/modules/aerodyn14/src/FVW_Subs.f90 @@ -219,9 +219,10 @@ subroutine Map_NW_FW(p, m, z, x, ErrStat, ErrMsg) endif enddo if (m%nNW==p%nNWMax) then - ! First circulation of Farwake is taken as the mean circulation of last NW column + ! First circulation of Farwake is taken as the max circulation of last NW column do iW=1,p%nWings - FWGamma = sum(x%Gamma_NW(:,p%nNWMax,iW))/p%nSpan + !FWGamma = sum(x%Gamma_NW(:,p%nNWMax,iW))/p%nSpan + FWGamma = maxval(x%Gamma_NW(:,p%nNWMax,iW)) x%Gamma_FW(1:FWnSpan,iAgeFW,iW) = FWGamma enddo endif From 40f76abde071b03d26018e938900b2206cb6ae07 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 22 Nov 2019 09:13:57 -0700 Subject: [PATCH 038/190] FVW: small change for Elliptic --- modules/aerodyn14/src/FVW_Wings.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/aerodyn14/src/FVW_Wings.f90 b/modules/aerodyn14/src/FVW_Wings.f90 index ca4a48dbab..3b9833327d 100644 --- a/modules/aerodyn14/src/FVW_Wings.f90 +++ b/modules/aerodyn14/src/FVW_Wings.f90 @@ -108,6 +108,7 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) P_ref = Meshes(iW)%Position(1:3, iSpan ) if (p%HACK==1) then P_ref(3)=100 + P_ref(1)=0 endif DP_LE(1:3) = 0.0 DP_LE(1) = -m%chord_LL(iSpan,iW)/4. ! TODO TODO TODO Use orientation and might not be c/2 @@ -327,7 +328,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x P4=x%r_NW(1:3,iSpan ,iDepth+1,iW) Gamm=GammaLastIter(iSpan, iW) do iWCP=1,p%nWings - call ui_quad_n1(m%CP_ll(1:3,1:p%nSpan,iWCP), nCPs, P1, P2, P3, P4, Gamm, p%RegFunction, p%WakeRegFactor, Vvar(1:3,1:p%nSpan,iWCP)) + call ui_quad_n1(m%CP_LL(1:3,1:p%nSpan,iWCP), nCPs, P1, P2, P3, P4, Gamm, p%RegFunction, p%WakeRegFactor, Vvar(1:3,1:p%nSpan,iWCP)) enddo enddo enddo From 96363be44d9dba90079aae5eb970e10ec953df41 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 22 Nov 2019 09:31:04 -0700 Subject: [PATCH 039/190] FVW: updated so that FVW_CalcOutput now does something. The wake is not rotating, but it is at least doing something. --- modules/aerodyn/src/AeroDyn.f90 | 13 +- modules/aerodyn/src/AeroDyn_Registry.txt | 2 +- modules/aerodyn/src/AeroDyn_Types.f90 | 180 ++++++++++----------- modules/aerodyn/src/FVW.f90 | 1 + modules/aerodyn/src/FVW_Subs.f90 | 2 + modules/aerodyn/src/FVW_Wings.f90 | 3 + modules/openfast-library/src/FAST_Subs.f90 | 4 +- 7 files changed, 107 insertions(+), 98 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index c5eb3f8fc3..b53b38c684 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -384,7 +384,7 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut !------------------------------------------------------------------------------------------------- if (p%WakeMod == WakeMod_FVW) then - call Init_FVWmodule( InputFileData, u, m%FVW_u(1), p, x%FVW, xd%FVW, z%FVW, & + call Init_FVWmodule( InputFileData, u, m%FVW_u(1), p, x%FVW, xd%FVW, m%FVW_z, & OtherState%FVW, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) if (ErrStat >= AbortErrLev) then @@ -1015,7 +1015,7 @@ subroutine AD_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) ! End the FVW submodule if (p%WakeMod == WakeMod_FVW ) then - call FVW_End( m%FVW_u, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg ) + call FVW_End( m%FVW_u, p%FVW, x%FVW, xd%FVW, m%FVW_z, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg ) endif ! Close files here: @@ -1111,7 +1111,7 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat ! Call the FVW sub module if (p%WakeMod == WakeMod_FVW) then ! Note: the setup is handled above in the SetInputs routine - call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) + call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, m%FVW_z, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif @@ -1183,8 +1183,11 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) !!! CALL CleanUp() !!! RETURN !!! END IF -!!! ! -- Calc Output -!!! CALL FVW_CalcOutput( Time, u%FVW, p%FVW, x%FVW, xd%FVW, z%FVW, O%FVW, y%FVW, m%FVW, ErrStat, ErrMess ) + if (p%WakeMod == WakeMod_FVW) then + ! -- Calc Output + CALL FVW_CalcOutput( t, m%FVW_u(indx), p%FVW, x%FVW, xd%FVW, m%FVW_z, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + endif !!! IF (ErrStat >= AbortErrLev) THEN !!! CALL CleanUp() !!! RETURN diff --git a/modules/aerodyn/src/AeroDyn_Registry.txt b/modules/aerodyn/src/AeroDyn_Registry.txt index f75b989416..4402e6fb16 100644 --- a/modules/aerodyn/src/AeroDyn_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Registry.txt @@ -122,7 +122,6 @@ typedef ^ DiscreteStateType FVW_DiscreteStateType FVW - - - "Discrete states fro # Define constraint states here: typedef ^ ConstraintStateType BEMT_ConstraintStateType BEMT - - - "Constraint states from the BEMT module" - -typedef ^ ConstraintStateType FVW_ConstraintStateType FVW - - - "Constraint states from the FVW module" - # Define "other" states here: typedef ^ OtherStateType BEMT_OtherStateType BEMT - - - "OtherStates from the BEMT module" - @@ -135,6 +134,7 @@ typedef ^ MiscVarType BEMT_InputType BEMT_u 2 - - "Inputs to the BEMT module" - typedef ^ MiscVarType FVW_MiscVarType FVW - - - "MiscVars from the FVW module" - typedef ^ MiscVarType FVW_OutputType FVW_y - - - "Outputs from the FVW module" - typedef ^ MiscVarType FVW_InputType FVW_u 2 - - "Inputs to the FVW module" - +typedef ^ MiscVarType FVW_ConstraintStateType FVW_z - - - "Constraint states from the FVW module" - typedef ^ MiscVarType ReKi DisturbedInflow {:}{:}{:} - - "InflowOnBlade values modified by tower influence" m/s typedef ^ MiscVarType ReKi WithoutSweepPitchTwist {:}{:}{:}{:} - - "Coordinate system equivalent to BladeMotion Orientation, but without live sweep, blade-pitch, and twist angles" - typedef ^ MiscVarType ReKi AllOuts {:} - - "An array holding the value of all of the calculated (not only selected) output channels" - diff --git a/modules/aerodyn/src/AeroDyn_Types.f90 b/modules/aerodyn/src/AeroDyn_Types.f90 index 0d142fcb11..0ffd298838 100644 --- a/modules/aerodyn/src/AeroDyn_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Types.f90 @@ -163,7 +163,6 @@ MODULE AeroDyn_Types ! ========= AD_ConstraintStateType ======= TYPE, PUBLIC :: AD_ConstraintStateType TYPE(BEMT_ConstraintStateType) :: BEMT !< Constraint states from the BEMT module [-] - TYPE(FVW_ConstraintStateType) :: FVW !< Constraint states from the FVW module [-] END TYPE AD_ConstraintStateType ! ======================= ! ========= AD_OtherStateType ======= @@ -180,6 +179,7 @@ MODULE AeroDyn_Types TYPE(FVW_MiscVarType) :: FVW !< MiscVars from the FVW module [-] TYPE(FVW_OutputType) :: FVW_y !< Outputs from the FVW module [-] TYPE(FVW_InputType) , DIMENSION(1:2) :: FVW_u !< Inputs to the FVW module [-] + TYPE(FVW_ConstraintStateType) :: FVW_z !< Constraint states from the FVW module [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: DisturbedInflow !< InflowOnBlade values modified by tower influence [m/s] REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: WithoutSweepPitchTwist !< Coordinate system equivalent to BladeMotion Orientation, but without live sweep, blade-pitch, and twist angles [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AllOuts !< An array holding the value of all of the calculated (not only selected) output channels [-] @@ -3950,9 +3950,6 @@ SUBROUTINE AD_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, CALL BEMT_CopyConstrState( SrcConstrStateData%BEMT, DstConstrStateData%BEMT, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL FVW_CopyConstrState( SrcConstrStateData%FVW, DstConstrStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD_CopyConstrState SUBROUTINE AD_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) @@ -3965,7 +3962,6 @@ SUBROUTINE AD_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL BEMT_DestroyConstrState( ConstrStateData%BEMT, ErrStat, ErrMsg ) - CALL FVW_DestroyConstrState( ConstrStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD_DestroyConstrState SUBROUTINE AD_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -4021,23 +4017,6 @@ SUBROUTINE AD_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype - CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW - 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 @@ -4093,34 +4072,6 @@ SUBROUTINE AD_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW - 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 AD_PackConstrState SUBROUTINE AD_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4195,46 +4146,6 @@ SUBROUTINE AD_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) - 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 FVW_UnpackConstrState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW - 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 AD_UnPackConstrState SUBROUTINE AD_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -4581,6 +4492,9 @@ SUBROUTINE AD_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN ENDDO + CALL FVW_CopyConstrState( SrcMiscData%FVW_z, DstMiscData%FVW_z, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN IF (ALLOCATED(SrcMiscData%DisturbedInflow)) THEN i1_l = LBOUND(SrcMiscData%DisturbedInflow,1) i1_u = UBOUND(SrcMiscData%DisturbedInflow,1) @@ -4818,6 +4732,7 @@ SUBROUTINE AD_DestroyMisc( MiscData, ErrStat, ErrMsg ) DO i1 = LBOUND(MiscData%FVW_u,1), UBOUND(MiscData%FVW_u,1) CALL FVW_DestroyInput( MiscData%FVW_u(i1), ErrStat, ErrMsg ) ENDDO + CALL FVW_DestroyConstrState( MiscData%FVW_z, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%DisturbedInflow)) THEN DEALLOCATE(MiscData%DisturbedInflow) ENDIF @@ -5011,6 +4926,23 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz DEALLOCATE(Int_Buf) END IF END DO + Int_BufSz = Int_BufSz + 3 ! FVW_z: size of buffers for each call to pack subtype + CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW_z, ErrStat2, ErrMsg2, .TRUE. ) ! FVW_z + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW_z + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW_z + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW_z + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF Int_BufSz = Int_BufSz + 1 ! DisturbedInflow allocated yes/no IF ( ALLOCATED(InData%DisturbedInflow) ) THEN Int_BufSz = Int_BufSz + 2*3 ! DisturbedInflow upper/lower bounds for each dimension @@ -5323,6 +5255,34 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF END DO + CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW_z, ErrStat2, ErrMsg2, OnlySize ) ! FVW_z + 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 IF ( .NOT. ALLOCATED(InData%DisturbedInflow) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -5905,6 +5865,46 @@ SUBROUTINE AD_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) 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 FVW_UnpackConstrState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW_z, ErrStat2, ErrMsg2 ) ! FVW_z + 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) IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! DisturbedInflow not allocated Int_Xferred = Int_Xferred + 1 ELSE diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index c1ce7b7717..e1968d7048 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -632,6 +632,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< Continuous states at t type(FVW_DiscreteStateType), intent(in ) :: xd !< Discrete states at t +!FIXME:TODO: AD15_CalcOutput has constraint states as intent(in) only. This is forcing me to store z in the AD15 miscvars for now. type(FVW_ConstraintStateType), intent(inout) :: z !< Constraint states at t type(FVW_OtherStateType), intent(in ) :: OtherState !< Other states at t type(FVW_OutputType), intent(inout) :: y !< Outputs computed at t (Input only so that mesh con- diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 851e396a32..1eb3f5425a 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -175,6 +175,7 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" +!FIXME: let's check if we need to deallocate / reallocate. if (allocated(r_wind)) deallocate(r_wind) nTot = 0 @@ -182,6 +183,7 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) nTot = nTot + p%nWings * (p%nSpan+1) * (m%nNW+1) ! Nearwake points nTot = nTot + p%nWings * ( 1 +1) * (m%nFW+1) ! War wake points +print*,'nTot wind points to request: ',nTot call AllocAry( r_wind , 3, nTot, 'Requested Wind Points', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,'SetRequestedWindPoints'); r_wind(1:3,1:nTot)= -999999_ReKi; diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 11a3f0d2ec..c1b07703a0 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -65,6 +65,7 @@ subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) m%chord_LL(iSpan, iW) = chord(iSpan,iW) enddo ! --- Control points +!TODO: does it make sense to keep the global position info here? It might make it simpler to keep track of the nodes for requesting wind velocity info. ! TODO possibly Control points are not exactly at the middle depending on "meshing" method do iSpan = 1, p%nSpan m%s_CP_LL (iSpan, iW) = (m%s_LL (iSpan,iW)+ m%s_LL (iSpan+1,iW))/2 @@ -141,6 +142,7 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) end do enddo +!FIXME: does it make sense to use the position mesh for this info? ! --- Lifting Line/ Bound Circulation panel ! For now: goes from 1/4 chord to TE ! More panelling options may be considered in the future @@ -211,6 +213,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS ErrStat = ErrID_None ErrMsg = "" +!FIXME: Gamma_LL is currently stored as a constraint state. This routine is called from places where constraint states are considered intent(in) only. if (p%CirculationMethod==idCircPrescribed) then print*,'>>>Prescribing circulation' do iW = 1, p%nWings !Loop over lifting lines diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 6620662204..8e9ec3b659 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -5117,7 +5117,7 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O ! Free wake !FIXME: Should the wake info be in a different routine? if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%m%FVW_z, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) end if END IF @@ -5402,7 +5402,7 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, A ! Free wake !FIXME: is there a better way of checking? if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%m%FVW_z, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) end if ! Tower motions From ef47601db7e5ff7f4ca22691dac228f44c9fca3f Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 22 Nov 2019 12:54:00 -0700 Subject: [PATCH 040/190] FVW bugfix: wrong fields in meshes used for wake propogation --- modules/aerodyn/src/FVW_Wings.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 17c88c7ea6..06e2f1b90e 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -108,7 +108,7 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) ! do iW = 1,p%nWings do iSpan = 1,p%nSpan+1 - P_ref = Meshes(iW)%Position(1:3, iSpan ) + P_ref = Meshes(iW)%Position(1:3, iSpan )+Meshes(iW)%TranslationDisp(1:3, iSpan) if (p%HACK==1) then P_ref(3)=100 P_ref(1)=0 From 15b25891616d23ba80eb6e65e686ec9a9c163e80 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 22 Nov 2019 14:03:04 -0700 Subject: [PATCH 041/190] FVW: add orientation info to getting leading and trailing edges --- modules/aerodyn/src/AeroDyn.f90 | 8 +- modules/aerodyn/src/AeroDyn_Registry.txt | 2 +- modules/aerodyn/src/AeroDyn_Types.f90 | 180 ++++++++++----------- modules/aerodyn/src/FVW.f90 | 2 +- modules/aerodyn/src/FVW_Wings.f90 | 5 +- modules/openfast-library/src/FAST_Subs.f90 | 4 +- 6 files changed, 100 insertions(+), 101 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index b53b38c684..a5b18a9e5f 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -384,7 +384,7 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut !------------------------------------------------------------------------------------------------- if (p%WakeMod == WakeMod_FVW) then - call Init_FVWmodule( InputFileData, u, m%FVW_u(1), p, x%FVW, xd%FVW, m%FVW_z, & + call Init_FVWmodule( InputFileData, u, m%FVW_u(1), p, x%FVW, xd%FVW, z%FVW, & OtherState%FVW, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) if (ErrStat >= AbortErrLev) then @@ -1015,7 +1015,7 @@ subroutine AD_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) ! End the FVW submodule if (p%WakeMod == WakeMod_FVW ) then - call FVW_End( m%FVW_u, p%FVW, x%FVW, xd%FVW, m%FVW_z, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg ) + call FVW_End( m%FVW_u, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg ) endif ! Close files here: @@ -1111,7 +1111,7 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat ! Call the FVW sub module if (p%WakeMod == WakeMod_FVW) then ! Note: the setup is handled above in the SetInputs routine - call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, m%FVW_z, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) + call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif @@ -1185,7 +1185,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) !!! END IF if (p%WakeMod == WakeMod_FVW) then ! -- Calc Output - CALL FVW_CalcOutput( t, m%FVW_u(indx), p%FVW, x%FVW, xd%FVW, m%FVW_z, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) + CALL FVW_CalcOutput( t, m%FVW_u(indx), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif !!! IF (ErrStat >= AbortErrLev) THEN diff --git a/modules/aerodyn/src/AeroDyn_Registry.txt b/modules/aerodyn/src/AeroDyn_Registry.txt index 4402e6fb16..f75b989416 100644 --- a/modules/aerodyn/src/AeroDyn_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Registry.txt @@ -122,6 +122,7 @@ typedef ^ DiscreteStateType FVW_DiscreteStateType FVW - - - "Discrete states fro # Define constraint states here: typedef ^ ConstraintStateType BEMT_ConstraintStateType BEMT - - - "Constraint states from the BEMT module" - +typedef ^ ConstraintStateType FVW_ConstraintStateType FVW - - - "Constraint states from the FVW module" - # Define "other" states here: typedef ^ OtherStateType BEMT_OtherStateType BEMT - - - "OtherStates from the BEMT module" - @@ -134,7 +135,6 @@ typedef ^ MiscVarType BEMT_InputType BEMT_u 2 - - "Inputs to the BEMT module" - typedef ^ MiscVarType FVW_MiscVarType FVW - - - "MiscVars from the FVW module" - typedef ^ MiscVarType FVW_OutputType FVW_y - - - "Outputs from the FVW module" - typedef ^ MiscVarType FVW_InputType FVW_u 2 - - "Inputs to the FVW module" - -typedef ^ MiscVarType FVW_ConstraintStateType FVW_z - - - "Constraint states from the FVW module" - typedef ^ MiscVarType ReKi DisturbedInflow {:}{:}{:} - - "InflowOnBlade values modified by tower influence" m/s typedef ^ MiscVarType ReKi WithoutSweepPitchTwist {:}{:}{:}{:} - - "Coordinate system equivalent to BladeMotion Orientation, but without live sweep, blade-pitch, and twist angles" - typedef ^ MiscVarType ReKi AllOuts {:} - - "An array holding the value of all of the calculated (not only selected) output channels" - diff --git a/modules/aerodyn/src/AeroDyn_Types.f90 b/modules/aerodyn/src/AeroDyn_Types.f90 index 0ffd298838..0d142fcb11 100644 --- a/modules/aerodyn/src/AeroDyn_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Types.f90 @@ -163,6 +163,7 @@ MODULE AeroDyn_Types ! ========= AD_ConstraintStateType ======= TYPE, PUBLIC :: AD_ConstraintStateType TYPE(BEMT_ConstraintStateType) :: BEMT !< Constraint states from the BEMT module [-] + TYPE(FVW_ConstraintStateType) :: FVW !< Constraint states from the FVW module [-] END TYPE AD_ConstraintStateType ! ======================= ! ========= AD_OtherStateType ======= @@ -179,7 +180,6 @@ MODULE AeroDyn_Types TYPE(FVW_MiscVarType) :: FVW !< MiscVars from the FVW module [-] TYPE(FVW_OutputType) :: FVW_y !< Outputs from the FVW module [-] TYPE(FVW_InputType) , DIMENSION(1:2) :: FVW_u !< Inputs to the FVW module [-] - TYPE(FVW_ConstraintStateType) :: FVW_z !< Constraint states from the FVW module [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: DisturbedInflow !< InflowOnBlade values modified by tower influence [m/s] REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: WithoutSweepPitchTwist !< Coordinate system equivalent to BladeMotion Orientation, but without live sweep, blade-pitch, and twist angles [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AllOuts !< An array holding the value of all of the calculated (not only selected) output channels [-] @@ -3950,6 +3950,9 @@ SUBROUTINE AD_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, CALL BEMT_CopyConstrState( SrcConstrStateData%BEMT, DstConstrStateData%BEMT, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN + CALL FVW_CopyConstrState( SrcConstrStateData%FVW, DstConstrStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE AD_CopyConstrState SUBROUTINE AD_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) @@ -3962,6 +3965,7 @@ SUBROUTINE AD_DestroyConstrState( ConstrStateData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" CALL BEMT_DestroyConstrState( ConstrStateData%BEMT, ErrStat, ErrMsg ) + CALL FVW_DestroyConstrState( ConstrStateData%FVW, ErrStat, ErrMsg ) END SUBROUTINE AD_DestroyConstrState SUBROUTINE AD_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -4017,6 +4021,23 @@ SUBROUTINE AD_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 3 ! FVW: size of buffers for each call to pack subtype + CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, .TRUE. ) ! FVW + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! FVW + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! FVW + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! FVW + 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 @@ -4072,6 +4093,34 @@ SUBROUTINE AD_PackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW, ErrStat2, ErrMsg2, OnlySize ) ! FVW + 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 AD_PackConstrState SUBROUTINE AD_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4146,6 +4195,46 @@ SUBROUTINE AD_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + 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 FVW_UnpackConstrState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW, ErrStat2, ErrMsg2 ) ! FVW + 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 AD_UnPackConstrState SUBROUTINE AD_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -4492,9 +4581,6 @@ SUBROUTINE AD_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN ENDDO - CALL FVW_CopyConstrState( SrcMiscData%FVW_z, DstMiscData%FVW_z, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN IF (ALLOCATED(SrcMiscData%DisturbedInflow)) THEN i1_l = LBOUND(SrcMiscData%DisturbedInflow,1) i1_u = UBOUND(SrcMiscData%DisturbedInflow,1) @@ -4732,7 +4818,6 @@ SUBROUTINE AD_DestroyMisc( MiscData, ErrStat, ErrMsg ) DO i1 = LBOUND(MiscData%FVW_u,1), UBOUND(MiscData%FVW_u,1) CALL FVW_DestroyInput( MiscData%FVW_u(i1), ErrStat, ErrMsg ) ENDDO - CALL FVW_DestroyConstrState( MiscData%FVW_z, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%DisturbedInflow)) THEN DEALLOCATE(MiscData%DisturbedInflow) ENDIF @@ -4926,23 +5011,6 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz DEALLOCATE(Int_Buf) END IF END DO - Int_BufSz = Int_BufSz + 3 ! FVW_z: size of buffers for each call to pack subtype - CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW_z, ErrStat2, ErrMsg2, .TRUE. ) ! FVW_z - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! FVW_z - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! FVW_z - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! FVW_z - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF Int_BufSz = Int_BufSz + 1 ! DisturbedInflow allocated yes/no IF ( ALLOCATED(InData%DisturbedInflow) ) THEN Int_BufSz = Int_BufSz + 2*3 ! DisturbedInflow upper/lower bounds for each dimension @@ -5255,34 +5323,6 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF END DO - CALL FVW_PackConstrState( Re_Buf, Db_Buf, Int_Buf, InData%FVW_z, ErrStat2, ErrMsg2, OnlySize ) ! FVW_z - 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 IF ( .NOT. ALLOCATED(InData%DisturbedInflow) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -5865,46 +5905,6 @@ SUBROUTINE AD_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) 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 FVW_UnpackConstrState( Re_Buf, Db_Buf, Int_Buf, OutData%FVW_z, ErrStat2, ErrMsg2 ) ! FVW_z - 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) IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! DisturbedInflow not allocated Int_Xferred = Int_Xferred + 1 ELSE diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 1918c6b97a..d592a90fbe 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -611,7 +611,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg type(FVW_ContinuousStateType), intent(in ) :: x !< Continuous states at t type(FVW_DiscreteStateType), intent(in ) :: xd !< Discrete states at t !FIXME:TODO: AD15_CalcOutput has constraint states as intent(in) only. This is forcing me to store z in the AD15 miscvars for now. - type(FVW_ConstraintStateType), intent(inout) :: z !< Constraint states at t + type(FVW_ConstraintStateType), intent(in ) :: z !< Constraint states at t type(FVW_OtherStateType), intent(in ) :: OtherState !< Other states at t type(FVW_OutputType), intent(inout) :: y !< Outputs computed at t (Input only so that mesh con- !! nectivity information does not have to be recalculated) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 06e2f1b90e..346ab319ba 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -71,7 +71,6 @@ subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) m%s_CP_LL (iSpan, iW) = (m%s_LL (iSpan,iW)+ m%s_LL (iSpan+1,iW))/2 m%chord_CP_LL(iSpan, iW) = (m%chord_LL(iSpan,iW)+ m%chord_LL(iSpan+1,iW))/2 enddo -call MeshPrintInfo(CU, Meshes(iW) ) enddo end subroutine Wings_Panelling_Init @@ -120,8 +119,8 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) !MRot=Meshes(iW)%Orientation(1:3,1:3,iSpan) ! NOTE: this wont work !DP_LE = matmul(MRot,DP_LE) !DP_TE = matmul(MRot,DP_TE) - m%LE(1:3, iSpan, iW) = P_ref + DP_LE - m%TE(1:3, iSpan, iW) = P_ref + DP_TE + m%LE(1:3, iSpan, iW) = P_ref + DP_LE(1)*Meshes(iW)%Orientation(2,1:3,iSpan) + m%TE(1:3, iSpan, iW) = P_ref + DP_TE(1)*Meshes(iW)%Orientation(2,1:3,iSpan) enddo enddo ! --- Generic code below to compute normal/tangential vectors of a lifting line panel diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 8e9ec3b659..6620662204 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -5117,7 +5117,7 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O ! Free wake !FIXME: Should the wake info be in a different routine? if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%m%FVW_z, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) end if END IF @@ -5402,7 +5402,7 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, A ! Free wake !FIXME: is there a better way of checking? if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%m%FVW_z, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) end if ! Tower motions From 36cd7e4197b857b705f114f6c231761dd8ba1c29 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 22 Nov 2019 16:30:01 -0700 Subject: [PATCH 042/190] FVW: some bugfixes on interp order etc. --- modules/aerodyn/src/AeroDyn.f90 | 51 +++++++++++++----------- modules/aerodyn/src/AeroDyn_Registry.txt | 2 +- modules/aerodyn/src/AeroDyn_Types.f90 | 47 ++++++++++++++++++++-- modules/aerodyn/src/FVW.f90 | 27 +++++-------- 4 files changed, 83 insertions(+), 44 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index a5b18a9e5f..8c4dc1c60a 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -384,6 +384,8 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut !------------------------------------------------------------------------------------------------- if (p%WakeMod == WakeMod_FVW) then +!FIXME: figure out how to allocate based on the order of interpolation for extrap_unterp. + if (.not. allocated(m%FVW_u)) Allocate(m%FVW_u(3)) call Init_FVWmodule( InputFileData, u, m%FVW_u(1), p, x%FVW, xd%FVW, z%FVW, & OtherState%FVW, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -1110,7 +1112,11 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat ! Call the FVW sub module if (p%WakeMod == WakeMod_FVW) then + ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module + call SetInputsForFVW(p, u, m, errStat2, errMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Note: the setup is handled above in the SetInputs routine +!FIXME: do we want the hub orientation and rotation? Maybe motion also? u%HubMotion%Orientation(:,:,1) call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif @@ -1184,8 +1190,11 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) !!! RETURN !!! END IF if (p%WakeMod == WakeMod_FVW) then + ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module + call SetInputsForFVW(p, (/u/), m, errStat2, errMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! -- Calc Output - CALL FVW_CalcOutput( t, m%FVW_u(indx), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) + CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif !!! IF (ErrStat >= AbortErrLev) THEN @@ -1342,11 +1351,6 @@ subroutine SetInputs(p, u, m, indx, errStat, errMsg) call SetInputsForBEMT(p, u, m, indx, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (p%WakeMod == WakeMod_FVW) then - ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module - call SetInputsForFVW(p, u, m, indx, errStat2, errMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - endif end subroutine SetInputs !---------------------------------------------------------------------------------------------------------------------------------- @@ -1507,35 +1511,35 @@ end subroutine SetInputsForBEMT !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine sets m%FVW_u(indx). -subroutine SetInputsForFVW(p, u, m, indx, errStat, errMsg) +subroutine SetInputsForFVW(p, u, m, errStat, errMsg) type(AD_ParameterType), intent(in ) :: p !< AD parameters - type(AD_InputType), intent(in ) :: u !< AD Inputs at Time + type(AD_InputType), intent(in ) :: u(:) !< AD Inputs at Time type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables - integer, intent(in ) :: indx !< index into m%FVW_u array; must be 1 or 2 (but not checked here) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(intKi) :: tIndx integer(intKi) :: k ! loop counter for blades integer(intKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'SetInputsForFVW' - - ! Rather than use a meshcopy, we will just copy what we need to the WingsMesh - ! NOTE: MeshCopy requires the source mesh to be INOUT intent - ! NOTE2: If we change the WingsMesh to not be identical to the BladeMotion mesh, add the mapping stuff here. - do k=1,p%NumBlades - if ( u%BladeMotion(k)%nNodes /= m%FVW_u(indx)%WingsMesh(k)%nNodes ) then - ErrStat = ErrID_Fatal - ErrMsg = RoutineName//": WingsMesh contains different number of nodes than the BladeMotion mesh" - return - endif - m%FVW_u(indx)%WingsMesh(k)%TranslationDisp = u%BladeMotion(k)%TranslationDisp - m%FVW_u(indx)%WingsMesh(k)%Orientation = u%BladeMotion(k)%Orientation - m%FVW_u(indx)%WingsMesh(k)%TranslationVel = u%BladeMotion(k)%TranslationVel + do tIndx=1,size(u) + ! Rather than use a meshcopy, we will just copy what we need to the WingsMesh + ! NOTE: MeshCopy requires the source mesh to be INOUT intent + ! NOTE2: If we change the WingsMesh to not be identical to the BladeMotion mesh, add the mapping stuff here. + do k=1,p%NumBlades + if ( u(tIndx)%BladeMotion(k)%nNodes /= m%FVW_u(tIndx)%WingsMesh(k)%nNodes ) then + ErrStat = ErrID_Fatal + ErrMsg = RoutineName//": WingsMesh contains different number of nodes than the BladeMotion mesh" + return + endif + m%FVW_u(tIndx)%WingsMesh(k)%TranslationDisp = u(tIndx)%BladeMotion(k)%TranslationDisp + m%FVW_u(tIndx)%WingsMesh(k)%Orientation = u(tIndx)%BladeMotion(k)%Orientation + m%FVW_u(tIndx)%WingsMesh(k)%TranslationVel = u(tIndx)%BladeMotion(k)%TranslationVel + enddo enddo -!FIXME: do we want the hub orientation and rotation? Maybe motion also? u%HubMotion%Orientation(:,:,1) end subroutine SetInputsForFVW !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine converts outputs from BEMT (stored in m%BEMT_y) into values on the AeroDyn BladeLoad output mesh. @@ -2072,6 +2076,7 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m InitInp%numBlades = p%numBlades InitInp%numBladeNodes = p%numBlNds + ! --- TODO TODO TODO ANDY !FIXME: check the following now that we are in AD15. ! Change this so that it would match AD 15 mesh diff --git a/modules/aerodyn/src/AeroDyn_Registry.txt b/modules/aerodyn/src/AeroDyn_Registry.txt index f75b989416..ecdb3563be 100644 --- a/modules/aerodyn/src/AeroDyn_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Registry.txt @@ -134,7 +134,7 @@ typedef ^ MiscVarType BEMT_OutputType BEMT_y - - - "Outputs from the BEMT module typedef ^ MiscVarType BEMT_InputType BEMT_u 2 - - "Inputs to the BEMT module" - typedef ^ MiscVarType FVW_MiscVarType FVW - - - "MiscVars from the FVW module" - typedef ^ MiscVarType FVW_OutputType FVW_y - - - "Outputs from the FVW module" - -typedef ^ MiscVarType FVW_InputType FVW_u 2 - - "Inputs to the FVW module" - +typedef ^ MiscVarType FVW_InputType FVW_u : - - "Inputs to the FVW module" - typedef ^ MiscVarType ReKi DisturbedInflow {:}{:}{:} - - "InflowOnBlade values modified by tower influence" m/s typedef ^ MiscVarType ReKi WithoutSweepPitchTwist {:}{:}{:}{:} - - "Coordinate system equivalent to BladeMotion Orientation, but without live sweep, blade-pitch, and twist angles" - typedef ^ MiscVarType ReKi AllOuts {:} - - "An array holding the value of all of the calculated (not only selected) output channels" - diff --git a/modules/aerodyn/src/AeroDyn_Types.f90 b/modules/aerodyn/src/AeroDyn_Types.f90 index 0d142fcb11..0dfd8abdaf 100644 --- a/modules/aerodyn/src/AeroDyn_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Types.f90 @@ -179,7 +179,7 @@ MODULE AeroDyn_Types TYPE(BEMT_InputType) , DIMENSION(1:2) :: BEMT_u !< Inputs to the BEMT module [-] TYPE(FVW_MiscVarType) :: FVW !< MiscVars from the FVW module [-] TYPE(FVW_OutputType) :: FVW_y !< Outputs from the FVW module [-] - TYPE(FVW_InputType) , DIMENSION(1:2) :: FVW_u !< Inputs to the FVW module [-] + TYPE(FVW_InputType) , DIMENSION(:), ALLOCATABLE :: FVW_u !< Inputs to the FVW module [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: DisturbedInflow !< InflowOnBlade values modified by tower influence [m/s] REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: WithoutSweepPitchTwist !< Coordinate system equivalent to BladeMotion Orientation, but without live sweep, blade-pitch, and twist angles [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AllOuts !< An array holding the value of all of the calculated (not only selected) output channels [-] @@ -4576,11 +4576,22 @@ SUBROUTINE AD_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) CALL FVW_CopyOutput( SrcMiscData%FVW_y, DstMiscData%FVW_y, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN +IF (ALLOCATED(SrcMiscData%FVW_u)) THEN + i1_l = LBOUND(SrcMiscData%FVW_u,1) + i1_u = UBOUND(SrcMiscData%FVW_u,1) + IF (.NOT. ALLOCATED(DstMiscData%FVW_u)) THEN + ALLOCATE(DstMiscData%FVW_u(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%FVW_u.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF DO i1 = LBOUND(SrcMiscData%FVW_u,1), UBOUND(SrcMiscData%FVW_u,1) CALL FVW_CopyInput( SrcMiscData%FVW_u(i1), DstMiscData%FVW_u(i1), CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN ENDDO +ENDIF IF (ALLOCATED(SrcMiscData%DisturbedInflow)) THEN i1_l = LBOUND(SrcMiscData%DisturbedInflow,1) i1_u = UBOUND(SrcMiscData%DisturbedInflow,1) @@ -4815,9 +4826,12 @@ SUBROUTINE AD_DestroyMisc( MiscData, ErrStat, ErrMsg ) ENDDO CALL FVW_DestroyMisc( MiscData%FVW, ErrStat, ErrMsg ) CALL FVW_DestroyOutput( MiscData%FVW_y, ErrStat, ErrMsg ) +IF (ALLOCATED(MiscData%FVW_u)) THEN DO i1 = LBOUND(MiscData%FVW_u,1), UBOUND(MiscData%FVW_u,1) CALL FVW_DestroyInput( MiscData%FVW_u(i1), ErrStat, ErrMsg ) ENDDO + DEALLOCATE(MiscData%FVW_u) +ENDIF IF (ALLOCATED(MiscData%DisturbedInflow)) THEN DEALLOCATE(MiscData%DisturbedInflow) ENDIF @@ -4992,6 +5006,9 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 1 ! FVW_u allocated yes/no + IF ( ALLOCATED(InData%FVW_u) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! FVW_u upper/lower bounds for each dimension DO i1 = LBOUND(InData%FVW_u,1), UBOUND(InData%FVW_u,1) Int_BufSz = Int_BufSz + 3 ! FVW_u: size of buffers for each call to pack subtype CALL FVW_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW_u(i1), ErrStat2, ErrMsg2, .TRUE. ) ! FVW_u @@ -5011,6 +5028,7 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz DEALLOCATE(Int_Buf) END IF END DO + END IF Int_BufSz = Int_BufSz + 1 ! DisturbedInflow allocated yes/no IF ( ALLOCATED(InData%DisturbedInflow) ) THEN Int_BufSz = Int_BufSz + 2*3 ! DisturbedInflow upper/lower bounds for each dimension @@ -5293,6 +5311,16 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + IF ( .NOT. ALLOCATED(InData%FVW_u) ) 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%FVW_u,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FVW_u,1) + Int_Xferred = Int_Xferred + 2 + DO i1 = LBOUND(InData%FVW_u,1), UBOUND(InData%FVW_u,1) CALL FVW_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%FVW_u(i1), ErrStat2, ErrMsg2, OnlySize ) ! FVW_u CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -5323,6 +5351,7 @@ SUBROUTINE AD_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Siz IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF END DO + END IF IF ( .NOT. ALLOCATED(InData%DisturbedInflow) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -5861,8 +5890,19 @@ SUBROUTINE AD_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) - i1_l = LBOUND(OutData%FVW_u,1) - i1_u = UBOUND(OutData%FVW_u,1) + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FVW_u 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 + IF (ALLOCATED(OutData%FVW_u)) DEALLOCATE(OutData%FVW_u) + ALLOCATE(OutData%FVW_u(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FVW_u.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF DO i1 = LBOUND(OutData%FVW_u,1), UBOUND(OutData%FVW_u,1) Buf_size=IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 @@ -5905,6 +5945,7 @@ SUBROUTINE AD_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) END DO + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! DisturbedInflow not allocated Int_Xferred = Int_Xferred + 1 ELSE diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index d592a90fbe..4d928c8d55 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -305,7 +305,7 @@ end subroutine FVW_ToString !> This routine is called at the end of the simulation. subroutine FVW_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) - type(FVW_InputType), intent(inout) :: u(2) !< System inputs + type(FVW_InputType), intent(inout) :: u(:) !< System inputs type(FVW_ParameterType), intent(inout) :: p !< Parameters type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states type(FVW_DiscreteStateType), intent(inout) :: xd !< Discrete states @@ -316,14 +316,17 @@ subroutine FVW_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: i + ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" ! Place any last minute operations or calculations here: ! Close files here: ! Destroy the input data: - call FVW_DestroyInput( u(1), ErrStat, ErrMsg ) - call FVW_DestroyInput( u(2), ErrStat, ErrMsg ) + do i=1,size(u) + call FVW_DestroyInput( u(i), ErrStat, ErrMsg ) + enddo ! Destroy the parameter data: call FVW_DestroyParam( p, ErrStat, ErrMsg ) @@ -377,17 +380,9 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! --- Evaluation at t ! Inputs at t - ! TODO TODO TODO AD14 HACK! inputs at other times are wrong with AD14 - ! call FVW_CopyInput( u(1), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return - ! call FVW_Input_ExtrapInterp(u,utimes,uInterp,t, ErrStat2, ErrMsg2); if(Failed()) return - if (m%FirstCall) then - call FVW_CopyInput( u(2), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return - ! NOTE AD14 HACK should be put outside! - ! Panelling wings based on input mesh provided - call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return - else - call FVW_CopyInput( u(1), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return - endif + call FVW_CopyInput( u(2), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return + call FVW_Input_ExtrapInterp(u(1:size(utimes)),utimes(:),uInterp,t, ErrStat2, ErrMsg2); if(Failed()) return + call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Distribute the Wind we requested to Inflow wind to storage Misc arrays ! TODO ANDY @@ -426,9 +421,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta !call print_x_NW_FW(p, m, z, x,'Prop_') ! Inputs at t+dt - ! TODO TODO TODO inputs at other times are wrong with AD14 - !call FVW_Input_ExtrapInterp(u,utimes,uInterp,t+dt, ErrStat2, ErrMsg2); if(Failed()) return - call FVW_CopyInput( u(1), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return + call FVW_Input_ExtrapInterp(u(1:size(utimes)),utimes,uInterp,t+dt, ErrStat2, ErrMsg2); if(Failed()) return ! Panelling wings based on input mesh at t+dt call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return From 722d9295385a5c5b208ed7e1452ec7f93215375e Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 25 Nov 2019 11:55:02 -0700 Subject: [PATCH 043/190] FVW: fix segfault on interp order 2 from glue code Not what I consider an ideal fix, but it doesn't seg fault anymore. --- modules/aerodyn/src/AeroDyn.f90 | 56 +++++++++++---------------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 8c4dc1c60a..19c45e2eaf 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -382,10 +382,12 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut !------------------------------------------------------------------------------------------------- ! Initialize FVW module if it is used !------------------------------------------------------------------------------------------------- - + ! Unfortunately we do not know the interpolation order used by OpenFAST glue code at this point, + ! so we can't size things exactly. This means that we either must size too big here, or we must + ! resize in the FVW code at the first CalcOutput call. This is a bit problematic for efficiency + ! but not a complete deal-breaker. if (p%WakeMod == WakeMod_FVW) then -!FIXME: figure out how to allocate based on the order of interpolation for extrap_unterp. - if (.not. allocated(m%FVW_u)) Allocate(m%FVW_u(3)) + if (.not. allocated(m%FVW_u)) Allocate(m%FVW_u(3)) !size(u))) call Init_FVWmodule( InputFileData, u, m%FVW_u(1), p, x%FVW, xd%FVW, z%FVW, & OtherState%FVW, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -393,9 +395,11 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut call Cleanup() return end if - - call FVW_CopyInput( m%FVW_u(1), m%FVW_u(2), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ! populate the rest of the FVW_u so that extrap-interp will work + do i=2,3 !size(u) + call FVW_CopyInput( m%FVW_u(1), m%FVW_u(i), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + enddo endif @@ -1091,6 +1095,7 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat end if ! set values of m%BEMT_u(2) from inputs interpolated at t+dt: + ! NOTE: framework has t+dt at u(1) call AD_Input_ExtrapInterp(u,utimes,uInterp,t+p%DT, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1098,6 +1103,7 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! set values of m%BEMT_u(1) from inputs (uInterp) interpolated at t: + ! NOTE: framework has t at u(2) ! I'm doing this second in case we want the other misc vars at t as before, but I don't think it matters call AD_Input_ExtrapInterp(u,utimes,uInterp, t, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1107,6 +1113,7 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat ! Call into the BEMT update states NOTE: This is a non-standard framework interface!!!!! GJH + ! Also note BEMT_u(1) and BEMT_u(2) are not following the framework convention for t+dt, t call BEMT_UpdateStates(t, n, m%BEMT_u(1), m%BEMT_u(2), p%BEMT, x%BEMT, xd%BEMT, z%BEMT, OtherState%BEMT, p%AFI%AFInfo, m%BEMT, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1116,7 +1123,6 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat call SetInputsForFVW(p, u, m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Note: the setup is handled above in the SetInputs routine -!FIXME: do we want the hub orientation and rotation? Maybe motion also? u%HubMotion%Orientation(:,:,1) call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif @@ -1153,6 +1159,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None +!FIXME: there are inconsistencies in the usage of m%BEMT_u(i) from the way the framework is setup integer, parameter :: indx = 1 ! m%BEMT_u(1) is at t; m%BEMT_u(2) is t+dt integer(intKi) :: i integer(intKi) :: j @@ -1177,43 +1184,16 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) call SetOutputsFromBEMT(p, m, y ) -!!! REAL(ReKi) :: Vind_FVW(3) -!!! -!!! WakeCalc = p%UseFVW ! WakeCalc is used to easily switch the Freewake on and off in this routine -!!! -!!! ! --- Copy of Rotor Mesh to FVW -!!! IF (WakeCalc) THEN -!!! ! Setting u%FVW -!!! call AD14_to_FVW_u(u,p,u%FVW,ErrStat,ErrMess) -!!! IF (ErrStat >= AbortErrLev) THEN -!!! CALL CleanUp() -!!! RETURN -!!! END IF if (p%WakeMod == WakeMod_FVW) then ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module call SetInputsForFVW(p, (/u/), m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - ! -- Calc Output - CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) + ! Calculate Outputs at time t + CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + + ! Add anything that should be passed here endif -!!! IF (ErrStat >= AbortErrLev) THEN -!!! CALL CleanUp() -!!! RETURN -!!! END IF -!!! endif -!!! -!!! ! --- FVW - Vortex code -!!! Vind_FVW = y%FVW%Vind(:, IElement, IBlade) -!!! VT_ind = DOT_PRODUCT( tang_Vector, Vind_FVW) -!!! VN_ind = DOT_PRODUCT( norm_Vector, Vind_FVW) -!!! ! Normal and tangential induction factors -!!! m%Element%A (IElement,IBLADE) = - VN_ind / VNWind -!!! m%Element%AP(IElement,IBLADE) = VT_ind / VTTotal -!!! ! Copy over any outputs (y%FVW%) or miscvars (m%FVW%) needed by AD14 and anything else here -!!! IF ( p%UseFVW ) THEN -!!! VelocityVec = VelocityVec+Vind_FVW ! TODO this might not be what's really intended for -!!! END IF if ( p%TwrAero ) then call ADTwr_CalcOutput(p, u, m, y, ErrStat2, ErrMsg2 ) From e75b282601da48a8eaa5902642c2c1873535d3de Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 25 Nov 2019 11:55:02 -0700 Subject: [PATCH 044/190] FVW: fix segfault on interp order 2 from glue code Not what I consider an ideal fix, but it doesn't seg fault anymore. --- modules/aerodyn/src/AeroDyn.f90 | 56 +++++++------------- modules/openfast-library/src/FAST_Solver.f90 | 3 +- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 8c4dc1c60a..19c45e2eaf 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -382,10 +382,12 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut !------------------------------------------------------------------------------------------------- ! Initialize FVW module if it is used !------------------------------------------------------------------------------------------------- - + ! Unfortunately we do not know the interpolation order used by OpenFAST glue code at this point, + ! so we can't size things exactly. This means that we either must size too big here, or we must + ! resize in the FVW code at the first CalcOutput call. This is a bit problematic for efficiency + ! but not a complete deal-breaker. if (p%WakeMod == WakeMod_FVW) then -!FIXME: figure out how to allocate based on the order of interpolation for extrap_unterp. - if (.not. allocated(m%FVW_u)) Allocate(m%FVW_u(3)) + if (.not. allocated(m%FVW_u)) Allocate(m%FVW_u(3)) !size(u))) call Init_FVWmodule( InputFileData, u, m%FVW_u(1), p, x%FVW, xd%FVW, z%FVW, & OtherState%FVW, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -393,9 +395,11 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut call Cleanup() return end if - - call FVW_CopyInput( m%FVW_u(1), m%FVW_u(2), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ! populate the rest of the FVW_u so that extrap-interp will work + do i=2,3 !size(u) + call FVW_CopyInput( m%FVW_u(1), m%FVW_u(i), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + enddo endif @@ -1091,6 +1095,7 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat end if ! set values of m%BEMT_u(2) from inputs interpolated at t+dt: + ! NOTE: framework has t+dt at u(1) call AD_Input_ExtrapInterp(u,utimes,uInterp,t+p%DT, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1098,6 +1103,7 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! set values of m%BEMT_u(1) from inputs (uInterp) interpolated at t: + ! NOTE: framework has t at u(2) ! I'm doing this second in case we want the other misc vars at t as before, but I don't think it matters call AD_Input_ExtrapInterp(u,utimes,uInterp, t, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1107,6 +1113,7 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat ! Call into the BEMT update states NOTE: This is a non-standard framework interface!!!!! GJH + ! Also note BEMT_u(1) and BEMT_u(2) are not following the framework convention for t+dt, t call BEMT_UpdateStates(t, n, m%BEMT_u(1), m%BEMT_u(2), p%BEMT, x%BEMT, xd%BEMT, z%BEMT, OtherState%BEMT, p%AFI%AFInfo, m%BEMT, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1116,7 +1123,6 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat call SetInputsForFVW(p, u, m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Note: the setup is handled above in the SetInputs routine -!FIXME: do we want the hub orientation and rotation? Maybe motion also? u%HubMotion%Orientation(:,:,1) call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif @@ -1153,6 +1159,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None +!FIXME: there are inconsistencies in the usage of m%BEMT_u(i) from the way the framework is setup integer, parameter :: indx = 1 ! m%BEMT_u(1) is at t; m%BEMT_u(2) is t+dt integer(intKi) :: i integer(intKi) :: j @@ -1177,43 +1184,16 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) call SetOutputsFromBEMT(p, m, y ) -!!! REAL(ReKi) :: Vind_FVW(3) -!!! -!!! WakeCalc = p%UseFVW ! WakeCalc is used to easily switch the Freewake on and off in this routine -!!! -!!! ! --- Copy of Rotor Mesh to FVW -!!! IF (WakeCalc) THEN -!!! ! Setting u%FVW -!!! call AD14_to_FVW_u(u,p,u%FVW,ErrStat,ErrMess) -!!! IF (ErrStat >= AbortErrLev) THEN -!!! CALL CleanUp() -!!! RETURN -!!! END IF if (p%WakeMod == WakeMod_FVW) then ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module call SetInputsForFVW(p, (/u/), m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - ! -- Calc Output - CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) + ! Calculate Outputs at time t + CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + + ! Add anything that should be passed here endif -!!! IF (ErrStat >= AbortErrLev) THEN -!!! CALL CleanUp() -!!! RETURN -!!! END IF -!!! endif -!!! -!!! ! --- FVW - Vortex code -!!! Vind_FVW = y%FVW%Vind(:, IElement, IBlade) -!!! VT_ind = DOT_PRODUCT( tang_Vector, Vind_FVW) -!!! VN_ind = DOT_PRODUCT( norm_Vector, Vind_FVW) -!!! ! Normal and tangential induction factors -!!! m%Element%A (IElement,IBLADE) = - VN_ind / VNWind -!!! m%Element%AP(IElement,IBLADE) = VT_ind / VTTotal -!!! ! Copy over any outputs (y%FVW%) or miscvars (m%FVW%) needed by AD14 and anything else here -!!! IF ( p%UseFVW ) THEN -!!! VelocityVec = VelocityVec+Vind_FVW ! TODO this might not be what's really intended for -!!! END IF if ( p%TwrAero ) then call ADTwr_CalcOutput(p, u, m, y, ErrStat2, ErrMsg2 ) diff --git a/modules/openfast-library/src/FAST_Solver.f90 b/modules/openfast-library/src/FAST_Solver.f90 index 613da450a3..bef34003f8 100644 --- a/modules/openfast-library/src/FAST_Solver.f90 +++ b/modules/openfast-library/src/FAST_Solver.f90 @@ -4860,7 +4860,8 @@ SUBROUTINE SolveOption2c_Inp2AD_SrvD(this_time, this_state, p_FAST, m_FAST, ED, IF (p_FAST%CompInflow == Module_IfW) THEN - + ! Not certain if this is a good way to indicate more wind points were requested, or if a better method has been used. -- ADP + IfW%OtherSt(this_state)%ResizeWindArrays = AD%OtherSt(this_state)%ResizeWindArrays CALL InflowWind_CalcOutput( this_time, IfW%Input(1), IfW%p, IfW%x(this_state), IfW%xd(this_state), IfW%z(this_state), & IfW%OtherSt(this_state), IfW%y, IfW%m, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) From ea8e03db29128dcbc9e6d1d82dcb5b5b9b9c5850 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 3 Dec 2019 18:04:11 -0700 Subject: [PATCH 045/190] FVW: some changes for IfW points/velocity passing --- modules/aerodyn/src/AeroDyn.f90 | 11 +- modules/aerodyn/src/AeroDyn_Registry.txt | 2 + modules/aerodyn/src/AeroDyn_Types.f90 | 151 +++++++++++++++++++ modules/aerodyn/src/FVW_Subs.f90 | 1 + modules/openfast-library/src/FAST_Solver.f90 | 23 ++- 5 files changed, 178 insertions(+), 10 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 19c45e2eaf..1bd5fb60e1 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -2153,10 +2153,17 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - ! If anything is passed back in InitOut%FVW, deal with it here... + ! If anything is passed back in InitOut%FVW, deal with it here.. + + ! set the size of the input and xd arrays for passing wind info to FVW. + if (ALLOCATED(y%r_wind)) then + call AllocAry(u_AD%InflowWakeVel, 3, size(y%r_wind,DIM=2), 'InflowWakeVel', ErrStat2,ErrMsg2) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +!FIXME: figure out where we are passing the output r_wind + endif if (.not. equalRealNos(Interval, p%DT) ) & - call SetErrStat( ErrID_Fatal, "DTAero was changed in Init_FVWmodule(); this is not allowed.", ErrStat2, ErrMsg2, RoutineName) + call SetErrStat( ErrID_Fatal, "DTAero was changed in Init_FVWmodule(); this is not allowed yet.", ErrStat2, ErrMsg2, RoutineName) contains subroutine Cleanup() diff --git a/modules/aerodyn/src/AeroDyn_Registry.txt b/modules/aerodyn/src/AeroDyn_Registry.txt index ecdb3563be..4e4b59ce32 100644 --- a/modules/aerodyn/src/AeroDyn_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Registry.txt @@ -119,6 +119,7 @@ typedef ^ ContinuousStateType FVW_ContinuousStateType FVW - - - "Continuous stat # Define discrete (nondifferentiable) states here: typedef ^ DiscreteStateType BEMT_DiscreteStateType BEMT - - - "Discrete states from the BEMT module" - typedef ^ DiscreteStateType FVW_DiscreteStateType FVW - - - "Discrete states from the FVW module" - +typedef ^ DiscreteStateType ReKi WakeLocationPoints {:}{:} - - "wake points velocity" m/s # Define constraint states here: typedef ^ ConstraintStateType BEMT_ConstraintStateType BEMT - - - "Constraint states from the BEMT module" - @@ -201,6 +202,7 @@ typedef ^ InputType MeshType BladeMotion {:} - - "motion on each blade" - # Define inputs that are not on this mesh here: typedef ^ InputType ReKi InflowOnBlade {:}{:}{:} - - "U,V,W at nodes on each blade (note if we change the requirement that NumNodes is the same for each blade, this will need to change)" m/s typedef ^ InputType ReKi InflowOnTower {:}{:} - - "U,V,W at nodes on the tower" m/s +typedef ^ InputType ReKi InflowWakeVel {:}{:} - - "U,V,W at wake points" m/s # ..... Outputs ................................................................................................................... # Define outputs that are contained on the mesh here: diff --git a/modules/aerodyn/src/AeroDyn_Types.f90 b/modules/aerodyn/src/AeroDyn_Types.f90 index 0dfd8abdaf..ae2546687d 100644 --- a/modules/aerodyn/src/AeroDyn_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Types.f90 @@ -158,6 +158,7 @@ MODULE AeroDyn_Types TYPE, PUBLIC :: AD_DiscreteStateType TYPE(BEMT_DiscreteStateType) :: BEMT !< Discrete states from the BEMT module [-] TYPE(FVW_DiscreteStateType) :: FVW !< Discrete states from the FVW module [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WakeLocationPoints !< wake points velocity [m/s] END TYPE AD_DiscreteStateType ! ======================= ! ========= AD_ConstraintStateType ======= @@ -245,6 +246,7 @@ MODULE AeroDyn_Types TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: BladeMotion !< motion on each blade [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: InflowOnBlade !< U,V,W at nodes on each blade (note if we change the requirement that NumNodes is the same for each blade, this will need to change) [m/s] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: InflowOnTower !< U,V,W at nodes on the tower [m/s] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: InflowWakeVel !< U,V,W at wake points [m/s] END TYPE AD_InputType ! ======================= ! ========= AD_OutputType ======= @@ -3637,6 +3639,8 @@ SUBROUTINE AD_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrSt 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'AD_CopyDiscState' @@ -3649,6 +3653,20 @@ SUBROUTINE AD_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrSt CALL FVW_CopyDiscState( SrcDiscStateData%FVW, DstDiscStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN +IF (ALLOCATED(SrcDiscStateData%WakeLocationPoints)) THEN + i1_l = LBOUND(SrcDiscStateData%WakeLocationPoints,1) + i1_u = UBOUND(SrcDiscStateData%WakeLocationPoints,1) + i2_l = LBOUND(SrcDiscStateData%WakeLocationPoints,2) + i2_u = UBOUND(SrcDiscStateData%WakeLocationPoints,2) + IF (.NOT. ALLOCATED(DstDiscStateData%WakeLocationPoints)) THEN + ALLOCATE(DstDiscStateData%WakeLocationPoints(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%WakeLocationPoints.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstDiscStateData%WakeLocationPoints = SrcDiscStateData%WakeLocationPoints +ENDIF END SUBROUTINE AD_CopyDiscState SUBROUTINE AD_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) @@ -3662,6 +3680,9 @@ SUBROUTINE AD_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) ErrMsg = "" CALL BEMT_DestroyDiscState( DiscStateData%BEMT, ErrStat, ErrMsg ) CALL FVW_DestroyDiscState( DiscStateData%FVW, ErrStat, ErrMsg ) +IF (ALLOCATED(DiscStateData%WakeLocationPoints)) THEN + DEALLOCATE(DiscStateData%WakeLocationPoints) +ENDIF END SUBROUTINE AD_DestroyDiscState SUBROUTINE AD_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -3734,6 +3755,11 @@ SUBROUTINE AD_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 1 ! WakeLocationPoints allocated yes/no + IF ( ALLOCATED(InData%WakeLocationPoints) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! WakeLocationPoints upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%WakeLocationPoints) ! WakeLocationPoints + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -3817,6 +3843,22 @@ SUBROUTINE AD_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + IF ( .NOT. ALLOCATED(InData%WakeLocationPoints) ) 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%WakeLocationPoints,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%WakeLocationPoints,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%WakeLocationPoints,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%WakeLocationPoints,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%WakeLocationPoints)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%WakeLocationPoints))-1 ) = PACK(InData%WakeLocationPoints,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%WakeLocationPoints) + END IF END SUBROUTINE AD_PackDiscState SUBROUTINE AD_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3838,6 +3880,8 @@ SUBROUTINE AD_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err LOGICAL, ALLOCATABLE :: mask3(:,:,:) LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'AD_UnPackDiscState' @@ -3931,6 +3975,32 @@ SUBROUTINE AD_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! WakeLocationPoints 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 + IF (ALLOCATED(OutData%WakeLocationPoints)) DEALLOCATE(OutData%WakeLocationPoints) + ALLOCATE(OutData%WakeLocationPoints(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%WakeLocationPoints.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%WakeLocationPoints)>0) OutData%WakeLocationPoints = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%WakeLocationPoints))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%WakeLocationPoints) + DEALLOCATE(mask2) + END IF END SUBROUTINE AD_UnPackDiscState SUBROUTINE AD_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg ) @@ -7450,6 +7520,20 @@ SUBROUTINE AD_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) END IF END IF DstInputData%InflowOnTower = SrcInputData%InflowOnTower +ENDIF +IF (ALLOCATED(SrcInputData%InflowWakeVel)) THEN + i1_l = LBOUND(SrcInputData%InflowWakeVel,1) + i1_u = UBOUND(SrcInputData%InflowWakeVel,1) + i2_l = LBOUND(SrcInputData%InflowWakeVel,2) + i2_u = UBOUND(SrcInputData%InflowWakeVel,2) + IF (.NOT. ALLOCATED(DstInputData%InflowWakeVel)) THEN + ALLOCATE(DstInputData%InflowWakeVel(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInputData%InflowWakeVel.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInputData%InflowWakeVel = SrcInputData%InflowWakeVel ENDIF END SUBROUTINE AD_CopyInput @@ -7481,6 +7565,9 @@ SUBROUTINE AD_DestroyInput( InputData, ErrStat, ErrMsg ) ENDIF IF (ALLOCATED(InputData%InflowOnTower)) THEN DEALLOCATE(InputData%InflowOnTower) +ENDIF +IF (ALLOCATED(InputData%InflowWakeVel)) THEN + DEALLOCATE(InputData%InflowWakeVel) ENDIF END SUBROUTINE AD_DestroyInput @@ -7610,6 +7697,11 @@ SUBROUTINE AD_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*2 ! InflowOnTower upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%InflowOnTower) ! InflowOnTower END IF + Int_BufSz = Int_BufSz + 1 ! InflowWakeVel allocated yes/no + IF ( ALLOCATED(InData%InflowWakeVel) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! InflowWakeVel upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%InflowWakeVel) ! InflowWakeVel + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -7810,6 +7902,22 @@ SUBROUTINE AD_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%InflowOnTower)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%InflowOnTower))-1 ) = PACK(InData%InflowOnTower,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%InflowOnTower) END IF + IF ( .NOT. ALLOCATED(InData%InflowWakeVel) ) 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%InflowWakeVel,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%InflowWakeVel,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%InflowWakeVel,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%InflowWakeVel,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%InflowWakeVel)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%InflowWakeVel))-1 ) = PACK(InData%InflowWakeVel,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%InflowWakeVel) + END IF END SUBROUTINE AD_PackInput SUBROUTINE AD_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -8094,6 +8202,32 @@ SUBROUTINE AD_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%InflowOnTower) DEALLOCATE(mask2) END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! InflowWakeVel 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 + IF (ALLOCATED(OutData%InflowWakeVel)) DEALLOCATE(OutData%InflowWakeVel) + ALLOCATE(OutData%InflowWakeVel(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%InflowWakeVel.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%InflowWakeVel)>0) OutData%InflowWakeVel = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%InflowWakeVel))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%InflowWakeVel) + DEALLOCATE(mask2) + END IF END SUBROUTINE AD_UnPackInput SUBROUTINE AD_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -8642,6 +8776,14 @@ SUBROUTINE AD_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg ) u_out%InflowOnTower = u1%InflowOnTower + b2 * t_out DEALLOCATE(b2) DEALLOCATE(c2) +END IF ! check if allocated +IF (ALLOCATED(u_out%InflowWakeVel) .AND. ALLOCATED(u1%InflowWakeVel)) THEN + ALLOCATE(b2(SIZE(u_out%InflowWakeVel,1),SIZE(u_out%InflowWakeVel,2) )) + ALLOCATE(c2(SIZE(u_out%InflowWakeVel,1),SIZE(u_out%InflowWakeVel,2) )) + b2 = -(u1%InflowWakeVel - u2%InflowWakeVel)/t(2) + u_out%InflowWakeVel = u1%InflowWakeVel + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) END IF ! check if allocated END SUBROUTINE AD_Input_ExtrapInterp1 @@ -8737,6 +8879,15 @@ SUBROUTINE AD_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, ErrM u_out%InflowOnTower = u1%InflowOnTower + b2 * t_out + c2 * t_out**2 DEALLOCATE(b2) DEALLOCATE(c2) +END IF ! check if allocated +IF (ALLOCATED(u_out%InflowWakeVel) .AND. ALLOCATED(u1%InflowWakeVel)) THEN + ALLOCATE(b2(SIZE(u_out%InflowWakeVel,1),SIZE(u_out%InflowWakeVel,2) )) + ALLOCATE(c2(SIZE(u_out%InflowWakeVel,1),SIZE(u_out%InflowWakeVel,2) )) + b2 = (t(3)**2*(u1%InflowWakeVel - u2%InflowWakeVel) + t(2)**2*(-u1%InflowWakeVel + u3%InflowWakeVel))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%InflowWakeVel + t(3)*u2%InflowWakeVel - t(2)*u3%InflowWakeVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%InflowWakeVel = u1%InflowWakeVel + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) END IF ! check if allocated END SUBROUTINE AD_Input_ExtrapInterp2 diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index eccc5dc173..e856e70d09 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -326,6 +326,7 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" !FIXME: let's check if we need to deallocate / reallocate. +!FIXME: we want to allocate this to the maximum we will need. (used in AD15 to set size of several other things) if (allocated(r_wind)) deallocate(r_wind) nTot = 0 diff --git a/modules/openfast-library/src/FAST_Solver.f90 b/modules/openfast-library/src/FAST_Solver.f90 index bef34003f8..308cd5b043 100644 --- a/modules/openfast-library/src/FAST_Solver.f90 +++ b/modules/openfast-library/src/FAST_Solver.f90 @@ -264,12 +264,13 @@ SUBROUTINE ED_InputSolve( p_FAST, u_ED, y_ED, p_AD14, y_AD14, y_AD, y_SrvD, u_AD END SUBROUTINE ED_InputSolve !---------------------------------------------------------------------------------------------------------------------------------- !> This routine determines the points in space where InflowWind needs to compute wind speeds. -SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD14, u_AD, y_ED, ErrStat, ErrMsg ) +SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD14, u_AD, xd_AD, y_ED, ErrStat, ErrMsg ) TYPE(InflowWind_InputType), INTENT(INOUT) :: u_IfW !< The inputs to InflowWind TYPE(InflowWind_ParameterType), INTENT(IN ) :: p_IfW !< The parameters to InflowWind TYPE(AD14_InputType), INTENT(IN) :: u_AD14 !< The input meshes (already calculated) from AeroDyn14 TYPE(AD_InputType), INTENT(IN) :: u_AD !< The input meshes (already calculated) from AeroDyn + TYPE(AD_DiscreteStateType), INTENT(IN) :: xd_AD !< The wake points from AeroDyn (Free Vortex Wake) TYPE(ED_OutputType), INTENT(IN) :: y_ED !< The outputs of the structural dynamics module TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< FAST parameter data TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< misc FAST data, including inputs from external codes like Simulink @@ -322,11 +323,17 @@ SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD14, u_AD, y_ED, Err END DO !K = 1,p%NumBl DO J=1,u_AD%TowerMotion%nnodes - Node = Node + 1 + Node = Node + 1 u_IfW%PositionXYZ(:,Node) = u_AD%TowerMotion%TranslationDisp(:,J) + u_AD%TowerMotion%Position(:,J) END DO - + ! vortex points from FVW in AD15 + IF (ALLOCATED(xd_AD%WakeLocationPoints)) then + DO J=1,size(xd_AD%WakeLocationPoints,DIM=2) + Node = Node + 1 + u_IfW%PositionXYZ(:,Node) = xd_AD%WakeLocationPoints(:,J) + END DO + END IF END IF @@ -410,6 +417,7 @@ SUBROUTINE AD_InputSolve_IfW( p_FAST, u_AD, y_IfW, y_OpFM, ErrStat, ErrMsg ) node = node + 1 end do end if +!FIXME: add the FVW wake points velocity array handoff here ELSEIF ( p_FAST%CompInflow == MODULE_OpFM ) THEN node = 2 !start of inputs to AD15 @@ -560,7 +568,7 @@ SUBROUTINE AD14_InputSolve_IfW( p_FAST, u_AD14, y_IfW, y_OpFM, ErrStat, ErrMsg ) END IF u_AD14%AvgInfVel = y_IfW%DiskVel - + END SUBROUTINE AD14_InputSolve_IfW !---------------------------------------------------------------------------------------------------------------------------------- @@ -4488,7 +4496,7 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca END IF IF ( p_FAST%CompInflow == Module_IfW ) THEN - CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD14%Input(1), AD%Input(1), ED%Output(1), ErrStat2, ErrMsg2 ) + CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD14%Input(1), AD%Input(1), AD%xd(1), ED%Output(1), ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ELSE IF ( p_FAST%CompInflow == Module_OpFM ) THEN ! OpenFOAM is the driver and it sets these inputs outside of this solve; the OpenFOAM inputs and outputs thus don't change @@ -4808,7 +4816,7 @@ SUBROUTINE SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, BD, IF (p_FAST%CompInflow == Module_IfW) THEN ! must be done after ED_CalcOutput and before AD_CalcOutput and SrvD - CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD14%Input(1), AD%Input(1), ED%Output(1), ErrStat2, ErrMsg2 ) + CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD14%Input(1), AD%Input(1), AD%xd(1), ED%Output(1), ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) !ELSE IF ( p_FAST%CompInflow == Module_OpFM ) THEN ! ! OpenFOAM is the driver and it computes outputs outside of this solve; the OpenFOAM inputs and outputs thus don't change @@ -4860,8 +4868,7 @@ SUBROUTINE SolveOption2c_Inp2AD_SrvD(this_time, this_state, p_FAST, m_FAST, ED, IF (p_FAST%CompInflow == Module_IfW) THEN - ! Not certain if this is a good way to indicate more wind points were requested, or if a better method has been used. -- ADP - IfW%OtherSt(this_state)%ResizeWindArrays = AD%OtherSt(this_state)%ResizeWindArrays + CALL InflowWind_CalcOutput( this_time, IfW%Input(1), IfW%p, IfW%x(this_state), IfW%xd(this_state), IfW%z(this_state), & IfW%OtherSt(this_state), IfW%y, IfW%m, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) From 13ccf671e3bdae6321ca63352835be4e19c9f85e Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 4 Dec 2019 16:15:19 -0700 Subject: [PATCH 046/190] FVW: more wind passing stuff Move allocation of wind velocity array to init. This will be a static size for the maximum possible size. We will add some shortcuts to IfW to ignore requests at (0,0,0) Also fixed some issues with the way I was testing for outputting VTK --- modules/aerodyn/src/AeroDyn.f90 | 5 +-- modules/aerodyn/src/FVW.f90 | 39 ++++++++++++++++++---- modules/aerodyn/src/FVW_Registry.txt | 6 ++-- modules/aerodyn/src/FVW_Subs.f90 | 12 +++---- modules/aerodyn/src/FVW_Types.f90 | 6 ++-- modules/openfast-library/src/FAST_Subs.f90 | 16 +++++---- 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index e76bd86b09..e3c80df036 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1166,7 +1166,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None -!FIXME: there are inconsistencies in the usage of m%BEMT_u(i) from the way the framework is setup + ! NOTE: there are inconsistencies in the usage of m%BEMT_u(i) from the way the framework is setup integer, parameter :: indx = 1 ! m%BEMT_u(1) is at t; m%BEMT_u(2) is t+dt integer(intKi) :: i integer(intKi) :: j @@ -1200,8 +1200,9 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Add anything that should be passed here +!FIXME: pass out the wind request points here endif - + if ( p%TwrAero ) then call ADTwr_CalcOutput(p, u, m, y, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 9b1b37aab9..68951468cf 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -130,6 +130,9 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! This mesh is now a cousin of the BladeMotion mesh from AD. CALL Wings_Panelling (u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return + ! Initialize output + CALL FVW_Init_Y( p, y, ErrStat2, ErrMsg2); if(Failed()) return + ! Returned guessed locations where wind will be required CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return ! Return anything in FVW_InitOutput that should be passed back to the calling code here @@ -227,6 +230,35 @@ subroutine FVW_InitConstraint( z, p, m, ErrStat, ErrMsg ) if (ErrStat >= AbortErrLev) return end subroutine FVW_InitConstraint +! ============================================================================== +subroutine FVW_Init_Y( p, y, ErrStat, ErrMsg ) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_OutputType), intent( out) :: y !< Constraints + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: nMax ! Total number of wind points possible + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + character(*), parameter :: RoutineName = 'FVW_InitMiscVars' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! + nMax = 0 + nMax = nMax + p%nWings * p%nSpan ! Lifting line Control Points + nMax = nMax + p%nWings * (p%nSpan+1) * (p%nNWMax+1) ! Nearwake points + nMax = nMax + p%nWings * (FWnSpan+1) * (p%nFWMax+1) ! Far wake points + + call AllocAry( y%r_wind, 3, nMax, 'Requested wind points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) + !call AllocAry( y%Vind , 3, p%nSpan+1, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO potentially nSpan+1 for AD15 + call AllocAry( y%Vind , 3, p%nSpan+1, 3, 'Induced velocity vector', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) ! NOTE: temporary hack 3 blades +!FIXME: TODO: allocate y%Cl_KJ (2d). Placeholder for now + call AllocAry(y%Cl_KJ, 1, 1, 'Lift coefficient from circulation (Kutta-Joukowski)', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) + if (ErrStat >= AbortErrLev) return + y%r_wind = 0.0_ReKi ! set to zero so InflowWind can shortcut calculations + y%Vind = 0.0_ReKi + return +end subroutine FVW_Init_Y ! ============================================================================== @@ -479,7 +511,7 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! Local variables integer(IntKi) :: ErrStat2 ! temporary error status of the operation character(ErrMsgLen) :: ErrMsg2 ! temporary error message - integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step + integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current time step ErrStat = ErrID_None ErrMsg = "" @@ -630,11 +662,6 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg m%Gamma_LL = z%Gamma_LL ! For plotting only endif - if (.not. allocated(y%Vind)) then - !call AllocAry( y%Vind , 3, p%nSpan+1, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO potentially nSpan+1 for AD15 - call AllocAry( y%Vind , 3, p%nSpan+1, 3, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! NOTE: temporary hack 3 blades - if(Failed()) return - endif ! Returned guessed locations where wind will be required CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 1cd392aaf1..1a60bed464 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -10,9 +10,9 @@ include Registry_NWTC_Library.txt #FVW_ParameterType typedef FVW/FVW ParameterType IntKi nWings - - - "Number of Wings" - typedef ^ ^ IntKi nSpan - - - "TODO, should be defined per wing. Number of spanwise element" - -typedef ^ ^ IntKi nNWMax - - - "Maximum number of nw panels" - -typedef ^ ^ IntKi nFWMax - - - "Maximum number of fw panels" - -typedef ^ ^ IntKi nFWFree - - - "Number of fw panels that are free" - +typedef ^ ^ IntKi nNWMax - - - "Maximum number of nw panels, per wing" - +typedef ^ ^ IntKi nFWMax - - - "Maximum number of fw panels, per wing" - +typedef ^ ^ IntKi nFWFree - - - "Number of fw panels that are free, per wing" - typedef ^ ^ IntKi IntMethod - - - "Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1)" - typedef ^ ^ ReKi FreeWakeStart - - - "Time when wake starts convecting (rolling up)" s typedef ^ ^ ReKi FullCirculationStart - - - "Time when the circulation is full" s diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index e856e70d09..4a158f0bbe 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -311,6 +311,9 @@ subroutine print_x_NW_FW(p, m, z, x, label) ! --- PACKING/UNPACKING FUNCTIONS ! -------------------------------------------------------------------------------- !> Establish the list of points where we will need the free stream +!! The r_wind array is allocated at initialization to the largest size possible. This is to +!! ensure that we do not violate requirements in the framework later for changing the size +!! of input and output arrays. subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) real(ReKi), dimension(:,:), allocatable, intent(inout) :: r_wind !< Position where wind is requested type(FVW_ContinuousStateType), intent(in ) :: x !< States @@ -320,22 +323,17 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None integer(IntKi) :: ErrStat2 ! temporary error status of the operation character(ErrMsgLen) :: ErrMsg2 ! temporary error message - integer(IntKi) :: nTot ! Total number of points + integer(IntKi) :: nTot ! Total number of points requested integer(IntKi) :: iSpan, iW, iAge ! Index on span, wings, panels integer(IntKi) :: iP ! Current index of point ErrStat = ErrID_None ErrMsg = "" -!FIXME: let's check if we need to deallocate / reallocate. -!FIXME: we want to allocate this to the maximum we will need. (used in AD15 to set size of several other things) - if (allocated(r_wind)) deallocate(r_wind) nTot = 0 nTot = nTot + p%nWings * p%nSpan ! Lifting line Control Points nTot = nTot + p%nWings * (p%nSpan+1) * (m%nNW+1) ! Nearwake points - nTot = nTot + p%nWings * (FWnSpan+1) * (m%nFW+1) ! War wake points + nTot = nTot + p%nWings * (FWnSpan+1) * (m%nFW+1) ! Far wake points -print*,'nTot wind points to request: ',nTot - call AllocAry( r_wind , 3, nTot, 'Requested Wind Points', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,'SetRequestedWindPoints'); r_wind(1:3,1:nTot)= -999999_ReKi; iP=0 diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 7e86096760..d6444b565a 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -37,9 +37,9 @@ MODULE FVW_Types TYPE, PUBLIC :: FVW_ParameterType INTEGER(IntKi) :: nWings !< Number of Wings [-] INTEGER(IntKi) :: nSpan !< TODO, should be defined per wing. Number of spanwise element [-] - INTEGER(IntKi) :: nNWMax !< Maximum number of nw panels [-] - INTEGER(IntKi) :: nFWMax !< Maximum number of fw panels [-] - INTEGER(IntKi) :: nFWFree !< Number of fw panels that are free [-] + INTEGER(IntKi) :: nNWMax !< Maximum number of nw panels, per wing [-] + INTEGER(IntKi) :: nFWMax !< Maximum number of fw panels, per wing [-] + INTEGER(IntKi) :: nFWFree !< Number of fw panels that are free, per wing [-] INTEGER(IntKi) :: IntMethod !< Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1) [-] REAL(ReKi) :: FreeWakeStart !< Time when wake starts convecting (rolling up) [s] REAL(ReKi) :: FullCirculationStart !< Time when the circulation is full [s] diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 6620662204..6c17c951a1 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -5108,16 +5108,18 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O end if ! FVW submodule of AD15 - if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - DO K=1,NumBl - call MeshWrVTK(p_FAST%TurbinePos, AD%m%FVW_u(1)%WingsMesh(k), trim(VTK_path)//'.FVW_WingsMesh'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth, AD%Input(1)%BladeMotion(k) ) - !call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%BladeMotion(K), trim(p_FAST%OutFileRoot)//'.AD_BladeMotion'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2 ) - END DO + if (allocated(AD%m%FVW_u)) then + if (allocated(AD%m%FVW_u(1)%WingsMesh)) then + DO K=1,NumBl + call MeshWrVTK(p_FAST%TurbinePos, AD%m%FVW_u(1)%WingsMesh(k), trim(VTK_path)//'.FVW_WingsMesh'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth, AD%Input(1)%BladeMotion(k) ) + !call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%BladeMotion(K), trim(p_FAST%OutFileRoot)//'.AD_BladeMotion'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2 ) + END DO + end if end if ! Free wake !FIXME: Should the wake info be in a different routine? - if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + if (allocated(AD%m%FVW_u)) then + if (allocated(AD%m%FVW_u(1)%WingsMesh)) call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) end if END IF From f0b00b5708a76dd0c3e23f6d2229f225b18f6f57 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 13 Dec 2019 15:37:07 -0700 Subject: [PATCH 047/190] FVW: rearrange and get IfW passing completely functional --- modules/aerodyn/src/AeroDyn.f90 | 69 +++++--- modules/aerodyn/src/AeroDyn_Registry.txt | 2 +- modules/aerodyn/src/AeroDyn_Types.f90 | 138 +++++++-------- modules/aerodyn/src/FVW.f90 | 100 ++++++----- modules/aerodyn/src/FVW_Registry.txt | 11 +- modules/aerodyn/src/FVW_Subs.f90 | 95 +++-------- modules/aerodyn/src/FVW_Types.f90 | 168 ++++++++++--------- modules/openfast-library/src/FAST_Solver.f90 | 52 ++++-- modules/openfast-library/src/FAST_Subs.f90 | 3 + 9 files changed, 326 insertions(+), 312 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index e3c80df036..7a78b1ed86 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -426,6 +426,13 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut call Init_MiscVars(m, p, u, y, errStat2, errMsg2) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + !............................................................................................ + ! Initialize other states + !............................................................................................ + ! The wake from FVW is stored in other states. This may not be the best place to put it! + call Init_OtherStates(m, p, OtherState, errStat2, errMsg2) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + !............................................................................................ ! Define initialization output here !............................................................................................ @@ -519,7 +526,7 @@ subroutine Init_MiscVars(m, p, u, y, errStat, errMsg) integer(intKi) :: k integer(intKi) :: ErrStat2 ! temporary Error status character(ErrMsgLen) :: ErrMsg2 ! temporary Error message - character(*), parameter :: RoutineName = 'Init_OtherStates' + character(*), parameter :: RoutineName = 'Init_MiscVars' ! Initialize variables for this routine @@ -606,6 +613,28 @@ subroutine Init_MiscVars(m, p, u, y, errStat, errMsg) end subroutine Init_MiscVars !---------------------------------------------------------------------------------------------------------------------------------- +!> This routine initializes (allocates) the misc variables for use during the simulation. +subroutine Init_OtherStates(m, p, OtherState, errStat, errMsg) + type(AD_MiscVarType), intent(in ) :: m !< misc/optimization data (not defined in submodules) + type(AD_ParameterType), intent(in ) :: p !< Parameters + type(AD_OtherStateType), intent(inout) :: OtherState !< Discrete states + integer(IntKi), intent( out) :: errStat !< Error status of the operation + character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer(intKi) :: ErrStat2 ! temporary Error status + character(ErrMsgLen) :: ErrMsg2 ! temporary Error message + character(*), parameter :: RoutineName = 'Init_OtherStates' + + errStat = ErrID_None + errMsg = "" + ! store Wake positions in otherstates. This may not be the best location + if (allocated(m%FVW%r_wind)) then + call AllocAry( OtherState%WakeLocationPoints, 3_IntKi, size(m%FVW%r_wind,DIM=2), ' OtherState%WakeLocationPoints', ErrStat2, ErrMsg2 ) ! must be same size as m%r_wind from FVW + call SetErrStat( errStat2, errMsg2, errStat, errMsg, RoutineName ) + OtherState%WakeLocationPoints = m%FVW%r_wind + endif +end subroutine Init_OtherStates +!---------------------------------------------------------------------------------------------------------------------------------- !> This routine initializes AeroDyn meshes and output array variables for use during the simulation. subroutine Init_y(y, u, p, errStat, errMsg) type(AD_OutputType), intent( out) :: y !< Module outputs @@ -1132,6 +1161,10 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat ! Note: the setup is handled above in the SetInputs routine call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + ! The wind points are passed out as other states. These really correspond to the propogation of the vortex to the next wind position. + if (allocated(OtherState%WakeLocationPoints)) then + OtherState%WakeLocationPoints = m%FVW%r_wind + endif endif call Cleanup() @@ -1196,11 +1229,8 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) call SetInputsForFVW(p, (/u/), m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Calculate Outputs at time t - CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat, ErrMsg2 ) + CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - - ! Add anything that should be passed here -!FIXME: pass out the wind request points here endif if ( p%TwrAero ) then @@ -1529,6 +1559,9 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) m%FVW_u(tIndx)%WingsMesh(k)%Orientation = u(tIndx)%BladeMotion(k)%Orientation m%FVW_u(tIndx)%WingsMesh(k)%TranslationVel = u(tIndx)%BladeMotion(k)%TranslationVel enddo + if (ALLOCATED(m%FVW_u(tIndx)%V_wind)) then + m%FVW_u(tIndx)%V_wind = u(tIndx)%InflowWakeVel + endif enddo end subroutine SetInputsForFVW !---------------------------------------------------------------------------------------------------------------------------------- @@ -2049,21 +2082,16 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m ErrStat = ErrID_None ErrMsg = "" - -print*,'===================== Setup before call to FVW_Init =====================' ! set initialization data here: InitInp%FVWFileName = InputFileData%FVWFileName InitInp%numBlades = p%numBlades InitInp%numBladeNodes = p%numBlNds + InitInp%DT = p%DT ! NOTE: if we subcycle FVW, this will need modification - -! --- TODO TODO TODO ANDY -!FIXME: check the following now that we are in AD15. -! Change this so that it would match AD 15 mesh -! NOTE: This mesh does not include the azimuthal differences between blades! -! It's just the spanwise location. -! Also, it is off compared to the initial position of the blade -! Also, it's centered on the hub, but that's fine for now + ! NOTE: The following are not meshes + ! It's just the spanwise location. + ! Also, it is off compared to the initial position of the blade + ! Also, it's centered on the hub, but that's fine for now call AllocAry(InitInp%Chord, InitInp%numBladeNodes,InitInp%numBlades,'chord', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) call AllocAry(InitInp%AFindx,InitInp%numBladeNodes,InitInp%numBlades,'AFindx',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) call AllocAry(InitInp%zHub, InitInp%numBlades,'zHub', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -2071,8 +2099,6 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m call AllocAry(InitInp%rLocal,InitInp%numBladeNodes,InitInp%numBlades,'rLocal',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) call AllocAry(InitInp%zTip, InitInp%numBlades,'zTip', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -!!! IF (.NOT. ALLOCATED( InitInp%RElm )) ALLOCATE ( InitInp%RElm( p%Element%NElm )) -!!! InitInp%RElm = p%Element%RElm if ( ErrStat >= AbortErrLev ) then call Cleanup() return @@ -2147,19 +2173,14 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m IF (ErrStat >= AbortErrLev) RETURN ENDDO - !FIXME: Should we be passing any AFinfo? Is that needed in FVW for anything? call FVW_Init( InitInp, u, p%FVW, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat2, ErrMsg2 ) CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - - ! If anything is passed back in InitOut%FVW, deal with it here.. - ! set the size of the input and xd arrays for passing wind info to FVW. - if (ALLOCATED(y%r_wind)) then - call AllocAry(u_AD%InflowWakeVel, 3, size(y%r_wind,DIM=2), 'InflowWakeVel', ErrStat2,ErrMsg2) + if (ALLOCATED(m%r_wind)) then + call AllocAry(u_AD%InflowWakeVel, 3, size(m%r_wind,DIM=2), 'InflowWakeVel', ErrStat2,ErrMsg2) call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) -!FIXME: figure out where we are passing the output r_wind endif if (.not. equalRealNos(Interval, p%DT) ) & diff --git a/modules/aerodyn/src/AeroDyn_Registry.txt b/modules/aerodyn/src/AeroDyn_Registry.txt index 20d77829db..34aa4d4e8b 100644 --- a/modules/aerodyn/src/AeroDyn_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Registry.txt @@ -120,7 +120,6 @@ typedef ^ ContinuousStateType FVW_ContinuousStateType FVW - - - "Continuous stat # Define discrete (nondifferentiable) states here: typedef ^ DiscreteStateType BEMT_DiscreteStateType BEMT - - - "Discrete states from the BEMT module" - typedef ^ DiscreteStateType FVW_DiscreteStateType FVW - - - "Discrete states from the FVW module" - -typedef ^ DiscreteStateType ReKi WakeLocationPoints {:}{:} - - "wake points velocity" m/s # Define constraint states here: typedef ^ ConstraintStateType BEMT_ConstraintStateType BEMT - - - "Constraint states from the BEMT module" - @@ -129,6 +128,7 @@ typedef ^ ConstraintStateType FVW_ConstraintStateType FVW - - - "Constraint stat # Define "other" states here: typedef ^ OtherStateType BEMT_OtherStateType BEMT - - - "OtherStates from the BEMT module" - typedef ^ OtherStateType FVW_OtherStateType FVW - - - "OtherStates from the FVW module" - +typedef ^ OtherStateType ReKi WakeLocationPoints {:}{:} - - "wake points velocity" m/s # Define misc/optimization variables (any data that are not considered actual states) here: typedef ^ MiscVarType BEMT_MiscVarType BEMT - - - "MiscVars from the BEMT module" - diff --git a/modules/aerodyn/src/AeroDyn_Types.f90 b/modules/aerodyn/src/AeroDyn_Types.f90 index f49574e2de..1d5f727922 100644 --- a/modules/aerodyn/src/AeroDyn_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Types.f90 @@ -150,7 +150,6 @@ MODULE AeroDyn_Types TYPE, PUBLIC :: AD_DiscreteStateType TYPE(BEMT_DiscreteStateType) :: BEMT !< Discrete states from the BEMT module [-] TYPE(FVW_DiscreteStateType) :: FVW !< Discrete states from the FVW module [-] - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WakeLocationPoints !< wake points velocity [m/s] END TYPE AD_DiscreteStateType ! ======================= ! ========= AD_ConstraintStateType ======= @@ -163,6 +162,7 @@ MODULE AeroDyn_Types TYPE, PUBLIC :: AD_OtherStateType TYPE(BEMT_OtherStateType) :: BEMT !< OtherStates from the BEMT module [-] TYPE(FVW_OtherStateType) :: FVW !< OtherStates from the FVW module [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WakeLocationPoints !< wake points velocity [m/s] END TYPE AD_OtherStateType ! ======================= ! ========= AD_MiscVarType ======= @@ -3638,8 +3638,6 @@ SUBROUTINE AD_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrSt 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'AD_CopyDiscState' @@ -3652,20 +3650,6 @@ SUBROUTINE AD_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrSt CALL FVW_CopyDiscState( SrcDiscStateData%FVW, DstDiscStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN -IF (ALLOCATED(SrcDiscStateData%WakeLocationPoints)) THEN - i1_l = LBOUND(SrcDiscStateData%WakeLocationPoints,1) - i1_u = UBOUND(SrcDiscStateData%WakeLocationPoints,1) - i2_l = LBOUND(SrcDiscStateData%WakeLocationPoints,2) - i2_u = UBOUND(SrcDiscStateData%WakeLocationPoints,2) - IF (.NOT. ALLOCATED(DstDiscStateData%WakeLocationPoints)) THEN - ALLOCATE(DstDiscStateData%WakeLocationPoints(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%WakeLocationPoints.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstDiscStateData%WakeLocationPoints = SrcDiscStateData%WakeLocationPoints -ENDIF END SUBROUTINE AD_CopyDiscState SUBROUTINE AD_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) @@ -3679,9 +3663,6 @@ SUBROUTINE AD_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) ErrMsg = "" CALL BEMT_DestroyDiscState( DiscStateData%BEMT, ErrStat, ErrMsg ) CALL FVW_DestroyDiscState( DiscStateData%FVW, ErrStat, ErrMsg ) -IF (ALLOCATED(DiscStateData%WakeLocationPoints)) THEN - DEALLOCATE(DiscStateData%WakeLocationPoints) -ENDIF END SUBROUTINE AD_DestroyDiscState SUBROUTINE AD_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -3754,11 +3735,6 @@ SUBROUTINE AD_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Int_BufSz = Int_BufSz + 1 ! WakeLocationPoints allocated yes/no - IF ( ALLOCATED(InData%WakeLocationPoints) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! WakeLocationPoints upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%WakeLocationPoints) ! WakeLocationPoints - END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -3842,22 +3818,6 @@ SUBROUTINE AD_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - IF ( .NOT. ALLOCATED(InData%WakeLocationPoints) ) 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%WakeLocationPoints,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%WakeLocationPoints,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%WakeLocationPoints,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%WakeLocationPoints,2) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%WakeLocationPoints)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%WakeLocationPoints))-1 ) = PACK(InData%WakeLocationPoints,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%WakeLocationPoints) - END IF END SUBROUTINE AD_PackDiscState SUBROUTINE AD_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3879,8 +3839,6 @@ SUBROUTINE AD_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err LOGICAL, ALLOCATABLE :: mask3(:,:,:) LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) - 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'AD_UnPackDiscState' @@ -3974,32 +3932,6 @@ SUBROUTINE AD_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! WakeLocationPoints 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 - IF (ALLOCATED(OutData%WakeLocationPoints)) DEALLOCATE(OutData%WakeLocationPoints) - ALLOCATE(OutData%WakeLocationPoints(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%WakeLocationPoints.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask2 = .TRUE. - IF (SIZE(OutData%WakeLocationPoints)>0) OutData%WakeLocationPoints = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%WakeLocationPoints))-1 ), mask2, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%WakeLocationPoints) - DEALLOCATE(mask2) - END IF END SUBROUTINE AD_UnPackDiscState SUBROUTINE AD_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg ) @@ -4314,6 +4246,8 @@ SUBROUTINE AD_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, Er 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'AD_CopyOtherState' @@ -4326,6 +4260,20 @@ SUBROUTINE AD_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, Er CALL FVW_CopyOtherState( SrcOtherStateData%FVW, DstOtherStateData%FVW, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN +IF (ALLOCATED(SrcOtherStateData%WakeLocationPoints)) THEN + i1_l = LBOUND(SrcOtherStateData%WakeLocationPoints,1) + i1_u = UBOUND(SrcOtherStateData%WakeLocationPoints,1) + i2_l = LBOUND(SrcOtherStateData%WakeLocationPoints,2) + i2_u = UBOUND(SrcOtherStateData%WakeLocationPoints,2) + IF (.NOT. ALLOCATED(DstOtherStateData%WakeLocationPoints)) THEN + ALLOCATE(DstOtherStateData%WakeLocationPoints(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%WakeLocationPoints.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%WakeLocationPoints = SrcOtherStateData%WakeLocationPoints +ENDIF END SUBROUTINE AD_CopyOtherState SUBROUTINE AD_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) @@ -4339,6 +4287,9 @@ SUBROUTINE AD_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) ErrMsg = "" CALL BEMT_DestroyOtherState( OtherStateData%BEMT, ErrStat, ErrMsg ) CALL FVW_DestroyOtherState( OtherStateData%FVW, ErrStat, ErrMsg ) +IF (ALLOCATED(OtherStateData%WakeLocationPoints)) THEN + DEALLOCATE(OtherStateData%WakeLocationPoints) +ENDIF END SUBROUTINE AD_DestroyOtherState SUBROUTINE AD_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -4411,6 +4362,11 @@ SUBROUTINE AD_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 1 ! WakeLocationPoints allocated yes/no + IF ( ALLOCATED(InData%WakeLocationPoints) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! WakeLocationPoints upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%WakeLocationPoints) ! WakeLocationPoints + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4494,6 +4450,22 @@ SUBROUTINE AD_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + IF ( .NOT. ALLOCATED(InData%WakeLocationPoints) ) 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%WakeLocationPoints,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%WakeLocationPoints,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%WakeLocationPoints,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%WakeLocationPoints,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%WakeLocationPoints)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%WakeLocationPoints))-1 ) = PACK(InData%WakeLocationPoints,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%WakeLocationPoints) + END IF END SUBROUTINE AD_PackOtherState SUBROUTINE AD_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4515,6 +4487,8 @@ SUBROUTINE AD_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er LOGICAL, ALLOCATABLE :: mask3(:,:,:) LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'AD_UnPackOtherState' @@ -4608,6 +4582,32 @@ SUBROUTINE AD_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! WakeLocationPoints 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 + IF (ALLOCATED(OutData%WakeLocationPoints)) DEALLOCATE(OutData%WakeLocationPoints) + ALLOCATE(OutData%WakeLocationPoints(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%WakeLocationPoints.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%WakeLocationPoints)>0) OutData%WakeLocationPoints = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%WakeLocationPoints))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%WakeLocationPoints) + DEALLOCATE(mask2) + END IF END SUBROUTINE AD_UnPackOtherState SUBROUTINE AD_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 68951468cf..47420766cd 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -131,10 +131,10 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL Wings_Panelling (u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Initialize output - CALL FVW_Init_Y( p, y, ErrStat2, ErrMsg2); if(Failed()) return + CALL FVW_Init_Y( p, u, y, ErrStat2, ErrMsg2); if(Failed()) return ! Returned guessed locations where wind will be required - CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + CALL SetRequestedWindPoints(m%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return ! Return anything in FVW_InitOutput that should be passed back to the calling code here CONTAINS @@ -153,6 +153,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: nMax ! Total number of wind points possible integer(IntKi) :: ErrStat2 ! temporary error status of the operation character(ErrMsgLen) :: ErrMsg2 ! temporary error message character(*), parameter :: RoutineName = 'FVW_InitMiscVars' @@ -164,6 +165,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%nNW = iNWStart-1 ! Number of active nearwake panels m%nFW = 0 ! Number of active farwake panels m%iStep = 0 ! Current step number + m%iStepPrev = 0 ! Previous step number (used to check if a correction step is calculated) call AllocAry( m%LE , 3 , p%nSpan+1 , p%nWings, 'Leading Edge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%LE = -999999_ReKi; call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%TE = -999999_ReKi; @@ -188,7 +190,13 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%Vwnd_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_NW= -999_ReKi; call AllocAry( m%Vwnd_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_NW= -999_ReKi; - call AllocAry( m%Vind_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; + ! Wind request points + nMax = 0 + nMax = nMax + p%nSpan * p%nWings ! Lifting line Control Points + nMax = nMax + (p%nSpan+1) * (p%nNWMax+1) * p%nWings ! Nearwake points + nMax = nMax + (FWnSpan+1) * (p%nFWMax+1) * p%nWings ! Far wake points + call AllocAry( m%r_wind, 3, nMax, 'Requested wind points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ) + m%r_wind = 0.0_ReKi ! set to zero so InflowWind can shortcut calculations end subroutine FVW_InitMiscVars ! ============================================================================== subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) @@ -206,9 +214,9 @@ subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) call AllocAry( x%Gamma_NW, p%nSpan , p%nNWMax , p%nWings, 'NW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_NW = -999999_ReKi; call AllocAry( x%Gamma_FW, FWnSpan , p%nFWMax , p%nWings, 'FW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_FW = -999999_ReKi; - call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_NW = -999_ReKi; - call AllocAry( x%r_FW , 3, FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = -999_ReKi; - + ! set x%r_NW and x%r_FW to (0,0,0) so that InflowWind can shortcut the calculations + call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_NW = 0.0_ReKi; + call AllocAry( x%r_FW , 3, FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = 0.0_ReKi; if (ErrStat >= AbortErrLev) return end subroutine FVW_InitStates @@ -231,8 +239,10 @@ subroutine FVW_InitConstraint( z, p, m, ErrStat, ErrMsg ) if (ErrStat >= AbortErrLev) return end subroutine FVW_InitConstraint ! ============================================================================== -subroutine FVW_Init_Y( p, y, ErrStat, ErrMsg ) +subroutine FVW_Init_Y( p, u, y, ErrStat, ErrMsg ) +!TODO: move the r_wind to Miscvars type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_InputType), intent(inout) :: u !< An initial guess for the input; input mesh must be defined type(FVW_OutputType), intent( out) :: y !< Constraints integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None @@ -245,17 +255,16 @@ subroutine FVW_Init_Y( p, y, ErrStat, ErrMsg ) ErrMsg = "" ! nMax = 0 - nMax = nMax + p%nWings * p%nSpan ! Lifting line Control Points - nMax = nMax + p%nWings * (p%nSpan+1) * (p%nNWMax+1) ! Nearwake points - nMax = nMax + p%nWings * (FWnSpan+1) * (p%nFWMax+1) ! Far wake points + nMax = nMax + p%nSpan * p%nWings ! Lifting line Control Points + nMax = nMax + (p%nSpan+1) * (p%nNWMax+1) * p%nWings ! Nearwake points + nMax = nMax + (FWnSpan+1) * (p%nFWMax+1) * p%nWings ! Far wake points - call AllocAry( y%r_wind, 3, nMax, 'Requested wind points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) + call AllocAry( u%V_wind, 3, nMax, 'Wind Velocity at points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) !call AllocAry( y%Vind , 3, p%nSpan+1, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO potentially nSpan+1 for AD15 call AllocAry( y%Vind , 3, p%nSpan+1, 3, 'Induced velocity vector', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) ! NOTE: temporary hack 3 blades !FIXME: TODO: allocate y%Cl_KJ (2d). Placeholder for now call AllocAry(y%Cl_KJ, 1, 1, 'Lift coefficient from circulation (Kutta-Joukowski)', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) if (ErrStat >= AbortErrLev) return - y%r_wind = 0.0_ReKi ! set to zero so InflowWind can shortcut calculations y%Vind = 0.0_ReKi return end subroutine FVW_Init_Y @@ -283,6 +292,9 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) ! NOTE: temporary limitation, all wings have the same nspan p%nSpan = InitInp%numBladeNodes-1 + ! Set time step + p%DT = InitInp%DT + end subroutine FVW_SetParametersFromInputs ! ============================================================================== !> @@ -405,11 +417,20 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ErrStat = ErrID_None ErrMsg = "" +!FIXME: why not use p%DT? DT from AD15 does not change. dt=utimes(1)-t ! TODO TODO TODO - m%iStep=n + m%iStepPrev = m%iStep + m%iStep = n print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' dt: ',dt,' ',n,' nNW:',m%nNW,' nFW:',m%nFW + ! We don't propagate the "Old"-> "New" if we we are not taking another timestep (such as during a correction step) + if (m%iStep /= m%iStepPrev) then + call PrepareNextTimeStep() + endif + + + ! --- Evaluation at t ! Inputs at t call FVW_CopyInput( u(2), uInterp, MESH_NEWCOPY, ErrStat2, ErrMsg2); if(Failed()) return @@ -417,8 +438,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Distribute the Wind we requested to Inflow wind to storage Misc arrays - ! TODO ANDY - !CALL DistributeRequestedWind(u(1)%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL DistributeRequestedWind(u(1)%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! --- Solve for circulation at t ! Returns: z%Gamma_LL (at t) @@ -475,8 +495,8 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Map3') - !if (m%nFW>4) STOP - !if (t>0.5) STOP + ! set the wind points required for t+dt timestep + CALL SetRequestedWindPoints(m%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return if (m%FirstCall) then m%FirstCall=.False. @@ -485,6 +505,14 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta call FVW_DestroyConstrState(z_guess, ErrStat2, ErrMsg2); contains + subroutine PrepareNextTimeStep() + ! --- Increase wake length if maximum not reached + if (m%nNW==p%nNWMax) then ! a far wake exist + m%nFW=min(m%nFW+1, p%nFWMax) + endif + m%nNW=min(m%nNW+1, p%nNWMax) + end subroutine PrepareNextTimeStep + logical function Failed() call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_UpdateStates') Failed = ErrStat >= AbortErrLev @@ -654,16 +682,12 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg print'(A,F10.3,A,L1,A,I0,A,I0)','CalcOutput t:',t,' ',m%FirstCall,' nNW:',m%nNW,' nFW:',m%nFW - if (m%FirstCall) then - print*,'>>> First Call of CalcOutput, calling panelling and constrstate' - CALL Wings_Panelling(u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return - CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat, ErrMsg, 0) ! For plotting only - else - m%Gamma_LL = z%Gamma_LL ! For plotting only - endif + ! if we are on a correction step, CalcOutput may be called again with different inputs + CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only + + ! Set the wind velocity at vortex + CALL DistributeRequestedWind(u%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return - ! Returned guessed locations where wind will be required - CALL SetRequestedWindPoints(y%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return ! Induction on the lifting line control point ! Set m%Vind_LL @@ -683,33 +707,17 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') call print_mean_3d(m%Vtot_LL,'Mean relativevel. LL') - ! We don't propagate the "Old"-> "New" if update states was not called once - ! This is introduced since at init, CalcOutput is called before UpdateState - if (.not. m%FirstCall) then - call PrepareNextTimeStep() - endif - - - ! --- Write to VTK + ! --- Write to VTK +!FIXME: This should be glue code level (all VTK output writing is done there) if (p%WrVTK==1) then if (m%FirstCall) then call MKDIR('vtk_out') - call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%iStep, 9) - else - call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%iStep+1, 9) endif + call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%iStep, 9) endif -contains - - subroutine PrepareNextTimeStep() - ! --- Increase wake length if maximum not reached - if (m%nNW==p%nNWMax) then ! a far wake exist - m%nFW=min(m%nFW+1, p%nFWMax) - endif - m%nNW=min(m%nNW+1, p%nNWMax) - end subroutine PrepareNextTimeStep +contains logical function Failed() call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_CalcOutput') diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 1a60bed464..d01f0b36f2 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -29,6 +29,7 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ IntKi HACK - - - "HACK ID" - +typedef ^ ^ DbKi DT - - - "Time interval for calls calculations" s # ....... OtherStateType ............ # FVW_OtherStateType @@ -52,7 +53,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi Orth ::: - - "Unit Orthogonal vector on LL CP" - typedef ^ ^ ReKi dl ::: - - "Vector of elementary length along the LL" - typedef ^ ^ ReKi Area :: - - "Area of each LL panel" - -typedef ^ ^ Reki Gamma_LL :: - - "Circulation on the wing lifting line (COPY of Constraint State)" - +typedef ^ ^ Reki Gamma_LL :: - - "Circulation on the wing lifting line (COPY of Constraint State)" - typedef ^ ^ ReKi Vind_LL ::: - - "Induced velocity on lifting line control points" m/s typedef ^ ^ ReKi Vtot_LL ::: - - "Total velocity on lifting line control points" m/s typedef ^ ^ ReKi Vstr_LL ::: - - "Structural velocity on LL CP" m/s @@ -63,7 +64,9 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi Vind_FW :::: - - "Induced velocity on far wake panels" m/s typedef ^ ^ IntKi nNW - - - "Number of active near wake panels" - typedef ^ ^ IntKi nFW - - - "Number of active far wake panels" - -typedef ^ ^ IntKi iStep - - - "Current step number" - +typedef ^ ^ IntKi iStep - - - "Current step number" - +typedef ^ ^ IntKi iStepPrev - - - "Previous step number" - +typedef ^ ^ ReKi r_wind :: - - "List of points where wind is requested for next time step" - # ........ Input ............ # FVW_InputType @@ -73,7 +76,6 @@ typedef ^ ^ ReKi # ........ Output ............ # FVW_OutputType typedef FVW/FVW OutputType ReKi Vind ::: - - "TODO mesh - Induced velocity vector. " - -typedef ^ ^ ReKi r_wind :: - - "List of points where wind is requested for next time step" - typedef ^ ^ ReKi Cl_KJ :: - - "Lift coefficient from circulation (Kutta-Joukowski)" - #.......... ContinuousStateType ...... @@ -107,8 +109,9 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi rLocal :: - - "Radial distance to blade node from the center of rotation, measured in the rotor plane, needed for DBEMT" m typedef ^ ^ IntKi NumBlades - - - "Number of blades" - typedef ^ ^ IntKi NumBladeNodes - - - "Number of nodes on each blade" - +typedef ^ ^ DbKi DT - - - "Time interval for calls (from AD15)" s -#.......... InitInputType ...... +#.......... InputFileType ...... # FVW_InputFile typedef FVW/FVW FVW_InputFile IntKi CirculationMethod - - - "Method to determine the circulation" - typedef ^ ^ CHARACTER(1024) CirculationFile - - - "Prescribed circulation file" - diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 4a158f0bbe..178430e74d 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -321,11 +321,11 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - integer(IntKi) :: ErrStat2 ! temporary error status of the operation - character(ErrMsgLen) :: ErrMsg2 ! temporary error message - integer(IntKi) :: nTot ! Total number of points requested - integer(IntKi) :: iSpan, iW, iAge ! Index on span, wings, panels - integer(IntKi) :: iP ! Current index of point + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + integer(IntKi) :: nTot ! Total number of points requested + integer(IntKi) :: iSpan, iW, iAge ! Index on span, wings, panels + integer(IntKi) :: iP,iP_start,iP_end ! Current index of point, start and end of range ErrStat = ErrID_None ErrMsg = "" @@ -334,37 +334,21 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) nTot = nTot + p%nWings * (p%nSpan+1) * (m%nNW+1) ! Nearwake points nTot = nTot + p%nWings * (FWnSpan+1) * (m%nFW+1) ! Far wake points - r_wind(1:3,1:nTot)= -999999_ReKi; - - iP=0 + ! Using array reshaping to ensure a given near or far wake point is always at the same location in the array. ! --- LL CP - do iW = 1, p%nWings - do iSpan = 1, p%nSpan - iP=iP+1 - r_wind(1:3,iP) = m%CP_LL(1:3, iSpan, iW) - enddo - enddo - + iP_start=1 + iP_end=p%nWings*p%nSpan + r_wind(1:3,iP_start:iP_end) = reshape( m%CP_LL(1:3,1:p%nSpan,1:p%nWings), (/ 3, p%nSpan*p%nWings /)) ! --- NW points - do iW = 1, p%nWings - do iSpan = 1, p%nSpan + 1 - do iAge = 1, m%nNW + 1 - iP=iP+1 - r_wind(1:3,iP) = x%r_NW(1:3, iSpan, iAge, iW) - enddo - enddo - enddo - + iP_start=iP_end+1 + iP_end=iP_start-1+(p%nSpan+1)*(p%nNWMax+1)*p%nWings + r_wind(1:3,iP_start:iP_end) = reshape( x%r_NW(1:3,1:p%nSpan+1,1:p%nNWMax+1,1:p%nWings), (/ 3, (p%nSpan+1)*(p%nNWMax+1)*p%nWings /)) ! --- FW points - do iW = 1, p%nWings - do iSpan = 1, FWnSpan+1 ! root and tip - do iAge = 1, m%nFW + 1 - iP=iP+1 - r_wind(1:3,iP) = x%r_FW(1:3, iSpan, iAge, iW) - enddo - enddo - enddo + iP_start=iP_end+1 + iP_end=iP_start-1+(FWnSpan+1)*(p%nFWMax+1)*p%nWings + r_wind(1:3,iP_start:iP_end) = reshape( x%r_FW(1:3,1:FWnSpan+1,1:p%nFWMax+1,1:p%nWings), (/ 3, (FWnSpan+1)*(p%nFWMax+1)*p%nWings /)) + end subroutine SetRequestedWindPoints @@ -380,49 +364,24 @@ subroutine DistributeRequestedWind(V_wind, x, p, m, ErrStat, ErrMsg ) character(ErrMsgLen) :: ErrMsg2 ! temporary error message integer(IntKi) :: nTot ! Total number of points integer(IntKi) :: iSpan, iW, iAge ! Index on span, wings, panels - integer(IntKi) :: iP ! Current index of point + integer(IntKi) :: iP,iP_start,iP_end ! Current index of point, start and end of range ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" - ! nTot, for satefy check - nTot = 0 - nTot = nTot + p%nWings * p%nSpan ! Lifting line Control Points - nTot = nTot + p%nWings * (p%nSpan+1) * (m%nNW+1) ! Nearwake points - nTot = nTot + p%nWings * (FWnSpan+1) * (m%nFW+1) ! Far wake points - if (size(V_wind,2)0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%r_wind))-1 ) = PACK(InData%r_wind,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%r_wind) + END IF END SUBROUTINE FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2342,6 +2393,34 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%iStep = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%iStepPrev = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! r_wind 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 + IF (ALLOCATED(OutData%r_wind)) DEALLOCATE(OutData%r_wind) + ALLOCATE(OutData%r_wind(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%r_wind.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%r_wind)>0) OutData%r_wind = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%r_wind))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%r_wind) + DEALLOCATE(mask2) + END IF END SUBROUTINE FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) @@ -2713,20 +2792,6 @@ SUBROUTINE FVW_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrM END IF DstOutputData%Vind = SrcOutputData%Vind ENDIF -IF (ALLOCATED(SrcOutputData%r_wind)) THEN - i1_l = LBOUND(SrcOutputData%r_wind,1) - i1_u = UBOUND(SrcOutputData%r_wind,1) - i2_l = LBOUND(SrcOutputData%r_wind,2) - i2_u = UBOUND(SrcOutputData%r_wind,2) - IF (.NOT. ALLOCATED(DstOutputData%r_wind)) THEN - ALLOCATE(DstOutputData%r_wind(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOutputData%r_wind.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstOutputData%r_wind = SrcOutputData%r_wind -ENDIF IF (ALLOCATED(SrcOutputData%Cl_KJ)) THEN i1_l = LBOUND(SrcOutputData%Cl_KJ,1) i1_u = UBOUND(SrcOutputData%Cl_KJ,1) @@ -2755,9 +2820,6 @@ SUBROUTINE FVW_DestroyOutput( OutputData, ErrStat, ErrMsg ) IF (ALLOCATED(OutputData%Vind)) THEN DEALLOCATE(OutputData%Vind) ENDIF -IF (ALLOCATED(OutputData%r_wind)) THEN - DEALLOCATE(OutputData%r_wind) -ENDIF IF (ALLOCATED(OutputData%Cl_KJ)) THEN DEALLOCATE(OutputData%Cl_KJ) ENDIF @@ -2803,11 +2865,6 @@ SUBROUTINE FVW_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + 2*3 ! Vind upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%Vind) ! Vind END IF - Int_BufSz = Int_BufSz + 1 ! r_wind allocated yes/no - IF ( ALLOCATED(InData%r_wind) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! r_wind upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%r_wind) ! r_wind - END IF Int_BufSz = Int_BufSz + 1 ! Cl_KJ allocated yes/no IF ( ALLOCATED(InData%Cl_KJ) ) THEN Int_BufSz = Int_BufSz + 2*2 ! Cl_KJ upper/lower bounds for each dimension @@ -2859,22 +2916,6 @@ SUBROUTINE FVW_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, IF (SIZE(InData%Vind)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vind))-1 ) = PACK(InData%Vind,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%Vind) END IF - IF ( .NOT. ALLOCATED(InData%r_wind) ) 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%r_wind,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_wind,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%r_wind,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%r_wind,2) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%r_wind)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%r_wind))-1 ) = PACK(InData%r_wind,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%r_wind) - END IF IF ( .NOT. ALLOCATED(InData%Cl_KJ) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -2957,32 +2998,6 @@ SUBROUTINE FVW_UnPackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs Re_Xferred = Re_Xferred + SIZE(OutData%Vind) DEALLOCATE(mask3) END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! r_wind 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 - IF (ALLOCATED(OutData%r_wind)) DEALLOCATE(OutData%r_wind) - ALLOCATE(OutData%r_wind(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%r_wind.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask2 = .TRUE. - IF (SIZE(OutData%r_wind)>0) OutData%r_wind = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%r_wind))-1 ), mask2, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%r_wind) - DEALLOCATE(mask2) - END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Cl_KJ not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -3905,6 +3920,7 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS ENDIF DstInitInputData%NumBlades = SrcInitInputData%NumBlades DstInitInputData%NumBladeNodes = SrcInitInputData%NumBladeNodes + DstInitInputData%DT = SrcInitInputData%DT END SUBROUTINE FVW_CopyInitInput SUBROUTINE FVW_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -4042,6 +4058,7 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs END IF Int_BufSz = Int_BufSz + 1 ! NumBlades Int_BufSz = Int_BufSz + 1 ! NumBladeNodes + Db_BufSz = Db_BufSz + 1 ! DT IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4221,6 +4238,8 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumBladeNodes Int_Xferred = Int_Xferred + 1 + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DT + Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_PackInitInput SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4494,6 +4513,8 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%NumBladeNodes = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%DT = DbKiBuf( Db_Xferred ) + Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_UnPackInitInput SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrStat, ErrMsg ) @@ -5173,14 +5194,6 @@ SUBROUTINE FVW_Output_ExtrapInterp1(y1, y2, tin, y_out, tin_out, ErrStat, ErrMsg DEALLOCATE(b3) DEALLOCATE(c3) END IF ! check if allocated -IF (ALLOCATED(y_out%r_wind) .AND. ALLOCATED(y1%r_wind)) THEN - ALLOCATE(b2(SIZE(y_out%r_wind,1),SIZE(y_out%r_wind,2) )) - ALLOCATE(c2(SIZE(y_out%r_wind,1),SIZE(y_out%r_wind,2) )) - b2 = -(y1%r_wind - y2%r_wind)/t(2) - y_out%r_wind = y1%r_wind + b2 * t_out - DEALLOCATE(b2) - DEALLOCATE(c2) -END IF ! check if allocated IF (ALLOCATED(y_out%Cl_KJ) .AND. ALLOCATED(y1%Cl_KJ)) THEN ALLOCATE(b2(SIZE(y_out%Cl_KJ,1),SIZE(y_out%Cl_KJ,2) )) ALLOCATE(c2(SIZE(y_out%Cl_KJ,1),SIZE(y_out%Cl_KJ,2) )) @@ -5258,15 +5271,6 @@ SUBROUTINE FVW_Output_ExtrapInterp2(y1, y2, y3, tin, y_out, tin_out, ErrStat, Er DEALLOCATE(b3) DEALLOCATE(c3) END IF ! check if allocated -IF (ALLOCATED(y_out%r_wind) .AND. ALLOCATED(y1%r_wind)) THEN - ALLOCATE(b2(SIZE(y_out%r_wind,1),SIZE(y_out%r_wind,2) )) - ALLOCATE(c2(SIZE(y_out%r_wind,1),SIZE(y_out%r_wind,2) )) - b2 = (t(3)**2*(y1%r_wind - y2%r_wind) + t(2)**2*(-y1%r_wind + y3%r_wind))/(t(2)*t(3)*(t(2) - t(3))) - c2 = ( (t(2)-t(3))*y1%r_wind + t(3)*y2%r_wind - t(2)*y3%r_wind ) / (t(2)*t(3)*(t(2) - t(3))) - y_out%r_wind = y1%r_wind + b2 * t_out + c2 * t_out**2 - DEALLOCATE(b2) - DEALLOCATE(c2) -END IF ! check if allocated IF (ALLOCATED(y_out%Cl_KJ) .AND. ALLOCATED(y1%Cl_KJ)) THEN ALLOCATE(b2(SIZE(y_out%Cl_KJ,1),SIZE(y_out%Cl_KJ,2) )) ALLOCATE(c2(SIZE(y_out%Cl_KJ,1),SIZE(y_out%Cl_KJ,2) )) diff --git a/modules/openfast-library/src/FAST_Solver.f90 b/modules/openfast-library/src/FAST_Solver.f90 index 308cd5b043..e28260260c 100644 --- a/modules/openfast-library/src/FAST_Solver.f90 +++ b/modules/openfast-library/src/FAST_Solver.f90 @@ -264,13 +264,13 @@ SUBROUTINE ED_InputSolve( p_FAST, u_ED, y_ED, p_AD14, y_AD14, y_AD, y_SrvD, u_AD END SUBROUTINE ED_InputSolve !---------------------------------------------------------------------------------------------------------------------------------- !> This routine determines the points in space where InflowWind needs to compute wind speeds. -SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD14, u_AD, xd_AD, y_ED, ErrStat, ErrMsg ) +SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD14, u_AD, OtherSt_AD, y_ED, ErrStat, ErrMsg ) - TYPE(InflowWind_InputType), INTENT(INOUT) :: u_IfW !< The inputs to InflowWind + TYPE(InflowWind_InputType), INTENT(INOUT) :: u_IfW(:) !< The inputs to InflowWind TYPE(InflowWind_ParameterType), INTENT(IN ) :: p_IfW !< The parameters to InflowWind TYPE(AD14_InputType), INTENT(IN) :: u_AD14 !< The input meshes (already calculated) from AeroDyn14 TYPE(AD_InputType), INTENT(IN) :: u_AD !< The input meshes (already calculated) from AeroDyn - TYPE(AD_DiscreteStateType), INTENT(IN) :: xd_AD !< The wake points from AeroDyn (Free Vortex Wake) + TYPE(AD_OtherStateType), INTENT(IN) :: OtherSt_AD !< The wake points from AeroDyn are in here (Free Vortex Wake) TYPE(ED_OutputType), INTENT(IN) :: y_ED !< The outputs of the structural dynamics module TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< FAST parameter data TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< misc FAST data, including inputs from external codes like Simulink @@ -294,7 +294,7 @@ SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD14, u_AD, xd_AD, y_ Node = 0 IF (p_FAST%CompServo == MODULE_SrvD) THEN Node = Node + 1 - u_IfW%PositionXYZ(:,Node) = y_ED%HubPtMotion%Position(:,1) ! undisplaced position. Maybe we want to use the displaced position (y_ED%HubPtMotion%TranslationDisp) at some point in time. + u_IfW(1)%PositionXYZ(:,Node) = y_ED%HubPtMotion%Position(:,1) ! undisplaced position. Maybe we want to use the displaced position (y_ED%HubPtMotion%TranslationDisp) at some point in time. END IF IF (p_FAST%CompAero == MODULE_AD14) THEN @@ -302,13 +302,13 @@ SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD14, u_AD, xd_AD, y_ DO K = 1,SIZE(u_AD14%InputMarkers) DO J = 1,u_AD14%InputMarkers(K)%nnodes !this mesh isn't properly set up (it's got the global [absolute] position and no reference position) Node = Node + 1 - u_IfW%PositionXYZ(:,Node) = u_AD14%InputMarkers(K)%Position(:,J) + u_IfW(1)%PositionXYZ(:,Node) = u_AD14%InputMarkers(K)%Position(:,J) END DO !J = 1,p%BldNodes ! Loop through the blade nodes / elements END DO !K = 1,p%NumBl DO J=1,u_AD14%Twr_InputMarkers%nnodes Node = Node + 1 - u_IfW%PositionXYZ(:,Node) = u_AD14%Twr_InputMarkers%TranslationDisp(:,J) + u_AD14%Twr_InputMarkers%Position(:,J) + u_IfW(1)%PositionXYZ(:,Node) = u_AD14%Twr_InputMarkers%TranslationDisp(:,J) + u_AD14%Twr_InputMarkers%Position(:,J) END DO ELSEIF (p_FAST%CompAero == MODULE_AD) THEN @@ -317,27 +317,31 @@ SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD14, u_AD, xd_AD, y_ DO J = 1,u_AD%BladeMotion(k)%Nnodes Node = Node + 1 - u_IfW%PositionXYZ(:,Node) = u_AD%BladeMotion(k)%TranslationDisp(:,j) + u_AD%BladeMotion(k)%Position(:,j) + u_IfW(1)%PositionXYZ(:,Node) = u_AD%BladeMotion(k)%TranslationDisp(:,j) + u_AD%BladeMotion(k)%Position(:,j) END DO !J = 1,p%BldNodes ! Loop through the blade nodes / elements END DO !K = 1,p%NumBl DO J=1,u_AD%TowerMotion%nnodes Node = Node + 1 - u_IfW%PositionXYZ(:,Node) = u_AD%TowerMotion%TranslationDisp(:,J) + u_AD%TowerMotion%Position(:,J) + u_IfW(1)%PositionXYZ(:,Node) = u_AD%TowerMotion%TranslationDisp(:,J) + u_AD%TowerMotion%Position(:,J) END DO ! vortex points from FVW in AD15 - IF (ALLOCATED(xd_AD%WakeLocationPoints)) then - DO J=1,size(xd_AD%WakeLocationPoints,DIM=2) + if (allocated(OtherSt_AD%WakeLocationPoints)) then + do J=1,size(OtherSt_AD%WakeLocationPoints,DIM=2) Node = Node + 1 - u_IfW%PositionXYZ(:,Node) = xd_AD%WakeLocationPoints(:,J) - END DO - END IF + u_IfW(1)%PositionXYZ(:,Node) = OtherSt_AD%WakeLocationPoints(:,J) + ! rewrite the history of this so that extrapolation doesn't make a mess of things + do k=2,size(u_IfW) + if (allocated(u_IfW(k)%PositionXYZ)) u_IfW(k)%PositionXYZ(:,Node) = u_IfW(1)%PositionXYZ(:,Node) + end do + enddo + end if END IF - CALL IfW_SetExternalInputs( p_IfW, m_FAST, y_ED, u_IfW ) + CALL IfW_SetExternalInputs( p_IfW, m_FAST, y_ED, u_IfW(1) ) END SUBROUTINE IfW_InputSolve @@ -417,7 +421,15 @@ SUBROUTINE AD_InputSolve_IfW( p_FAST, u_AD, y_IfW, y_OpFM, ErrStat, ErrMsg ) node = node + 1 end do end if -!FIXME: add the FVW wake points velocity array handoff here + ! velocity at vortex wake points velocity array handoff here + if ( allocated(u_AD%InflowWakeVel) ) then + Nnodes = size(u_AD%InflowWakeVel,DIM=2) + do j=1,Nnodes + u_AD%InflowWakeVel(:,j) = y_IfW%VelocityUVW(:,node) + node = node + 1 + end do + end if + ELSEIF ( p_FAST%CompInflow == MODULE_OpFM ) THEN node = 2 !start of inputs to AD15 @@ -4496,7 +4508,7 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca END IF IF ( p_FAST%CompInflow == Module_IfW ) THEN - CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD14%Input(1), AD%Input(1), AD%xd(1), ED%Output(1), ErrStat2, ErrMsg2 ) + CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(:), IfW%p, AD14%Input(1), AD%Input(1), AD%OtherSt(1), ED%Output(1), ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ELSE IF ( p_FAST%CompInflow == Module_OpFM ) THEN ! OpenFOAM is the driver and it sets these inputs outside of this solve; the OpenFOAM inputs and outputs thus don't change @@ -4816,7 +4828,7 @@ SUBROUTINE SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, BD, IF (p_FAST%CompInflow == Module_IfW) THEN ! must be done after ED_CalcOutput and before AD_CalcOutput and SrvD - CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD14%Input(1), AD%Input(1), AD%xd(1), ED%Output(1), ErrStat2, ErrMsg2 ) + CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(:), IfW%p, AD14%Input(1), AD%Input(1), AD%OtherSt(1), ED%Output(1), ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) !ELSE IF ( p_FAST%CompInflow == Module_OpFM ) THEN ! ! OpenFOAM is the driver and it computes outputs outside of this solve; the OpenFOAM inputs and outputs thus don't change @@ -5175,7 +5187,7 @@ SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) CALL AD_CopyOtherState( AD%OtherSt(STATE_CURR), AD%OtherSt(STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - + DO j_ss = 1, p_FAST%n_substeps( MODULE_AD ) n_t_module = n_t_global*p_FAST%n_substeps( MODULE_AD ) + j_ss - 1 t_module = n_t_module*p_FAST%dt_module( MODULE_AD ) + t_initial @@ -5183,6 +5195,10 @@ SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED CALL AD_UpdateStates( t_module, n_t_module, AD%Input, AD%InputTimes, AD%p, AD%x(STATE_PRED), & AD%xd(STATE_PRED), AD%z(STATE_PRED), AD%OtherSt(STATE_PRED), AD%m, ErrStat2, ErrMsg2 ) CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ! We don't want to extrapolate any values for the WakeLocations (those are exactly calculated) + if (allocated(AD%OtherSt(STATE_PRED)%WakeLocationPoints)) then + AD%OtherSt(STATE_CURR)%WakeLocationPoints = AD%OtherSt(STATE_PRED)%WakeLocationPoints + endif END DO !j_ss END IF diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 6c17c951a1..436ee6b7fd 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -558,6 +558,9 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, DO k=1,InitOutData_ED%NumBl InitInData_IfW%NumWindPoints = InitInData_IfW%NumWindPoints + AD%Input(1)%BladeMotion(k)%NNodes END DO + if (allocated(AD%OtherSt(STATE_CURR)%WakeLocationPoints)) then + InitInData_IfW%NumWindPoints = InitInData_IfW%NumWindPoints + size(AD%OtherSt(STATE_CURR)%WakeLocationPoints,DIM=2) + end if END IF ! lidar From 53c3d0a21c281d8be807db7e79b749b6a1c359bf Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 13 Dec 2019 16:02:08 -0700 Subject: [PATCH 048/190] FVW: missing allocation of vind far wake. --- modules/aerodyn/src/FVW.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 47420766cd..d2cc70f2f5 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -190,6 +190,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%Vwnd_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_NW= -999_ReKi; call AllocAry( m%Vwnd_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_NW= -999_ReKi; + call AllocAry( m%Vind_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; ! Wind request points nMax = 0 nMax = nMax + p%nSpan * p%nWings ! Lifting line Control Points From 5ad320697ac31464d8a67eae88108f42d22308c9 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 13 Dec 2019 16:02:41 -0700 Subject: [PATCH 049/190] FVW: add short cut to IfW to skip any points at (0.0,0.0,0.0) --- modules/inflowwind/src/IfW_BladedFFWind.f90 | 28 +++++++++++-------- modules/inflowwind/src/IfW_HAWCWind.f90 | 30 ++++++++++++--------- modules/inflowwind/src/IfW_TSFFWind.f90 | 30 ++++++++++++--------- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/modules/inflowwind/src/IfW_BladedFFWind.f90 b/modules/inflowwind/src/IfW_BladedFFWind.f90 index dac44e47c8..224c646b4c 100644 --- a/modules/inflowwind/src/IfW_BladedFFWind.f90 +++ b/modules/inflowwind/src/IfW_BladedFFWind.f90 @@ -1604,17 +1604,23 @@ SUBROUTINE IfW_BladedFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, D ! 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 + ! If the position is (0,0,0), assume it was never set and skip calculating + if ( PositionXYZ(1,PointNum) /= 0.0_ReKi .and. & + PositionXYZ(2,PointNum) /= 0.0_ReKi .and. & + PositionXYZ(3,PointNum) /= 0.0_ReKi ) then + + ! 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 + endif ENDDO diff --git a/modules/inflowwind/src/IfW_HAWCWind.f90 b/modules/inflowwind/src/IfW_HAWCWind.f90 index 6861a1705c..2e260f64f7 100644 --- a/modules/inflowwind/src/IfW_HAWCWind.f90 +++ b/modules/inflowwind/src/IfW_HAWCWind.f90 @@ -532,18 +532,24 @@ SUBROUTINE IfW_HAWCWind_CalcOutput(Time, PositionXYZ, p, Velocity, DiskVel, Misc ! 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 + ! If the position is (0,0,0), assume it was never set and skip calculating + if ( PositionXYZ(1,PointNum) /= 0.0_ReKi .and. & + PositionXYZ(2,PointNum) /= 0.0_ReKi .and. & + PositionXYZ(3,PointNum) /= 0.0_ReKi ) then + + ! 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 + endif ENDDO diff --git a/modules/inflowwind/src/IfW_TSFFWind.f90 b/modules/inflowwind/src/IfW_TSFFWind.f90 index 56872d883c..8da3306b27 100644 --- a/modules/inflowwind/src/IfW_TSFFWind.f90 +++ b/modules/inflowwind/src/IfW_TSFFWind.f90 @@ -642,18 +642,24 @@ SUBROUTINE IfW_TSFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, Disk ! 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 + ! If the position is (0,0,0), assume it was never set and skip calculating + if ( PositionXYZ(1,PointNum) /= 0.0_ReKi .and. & + PositionXYZ(2,PointNum) /= 0.0_ReKi .and. & + PositionXYZ(3,PointNum) /= 0.0_ReKi ) then + + ! 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 + endif ENDDO From e8fa37e16d5c509ef8bd2576a6b54ac9a262d17b Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 14 Jan 2020 14:34:01 -0700 Subject: [PATCH 050/190] FVW: added AFInfo into the CirculationFromPolarData routine --- modules/aerodyn/src/AeroDyn.f90 | 6 +-- modules/aerodyn/src/FVW.f90 | 33 +++++++----- modules/aerodyn/src/FVW_Registry.txt | 2 + modules/aerodyn/src/FVW_Types.f90 | 66 ++++++++++++++++++++++++ modules/aerodyn/src/FVW_Wings.f90 | 77 ++++++++++++++++++++++------ 5 files changed, 151 insertions(+), 33 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 7a78b1ed86..9feaeee35d 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1159,7 +1159,7 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat call SetInputsForFVW(p, u, m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Note: the setup is handled above in the SetInputs routine - call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2 ) + call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, p%AFI, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! The wind points are passed out as other states. These really correspond to the propogation of the vortex to the next wind position. if (allocated(OtherState%WakeLocationPoints)) then @@ -1229,7 +1229,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) call SetInputsForFVW(p, (/u/), m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Calculate Outputs at time t - CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) + CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, p%AFI, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif @@ -2173,7 +2173,7 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m IF (ErrStat >= AbortErrLev) RETURN ENDDO -!FIXME: Should we be passing any AFinfo? Is that needed in FVW for anything? + ! NOTE: not passing p%AFI at present. We are not storing it in FVW's parameters. call FVW_Init( InitInp, u, p%FVW, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat2, ErrMsg2 ) CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index d2cc70f2f5..a5591a0b1d 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -15,11 +15,7 @@ MODULE FVW use FVW_Wings use FVW_BiotSavart use FVW_Tests - - ! NOTE: this is a rough format that AD14 stores airfoil info. This will need - ! to be replaced by the AirFoilInfo module when we couple FVW into AD15 -! USE AD14AeroConf_Types - + use AirFoilInfo IMPLICIT NONE @@ -49,6 +45,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu type(FVW_DiscreteStateType), intent( out) :: xd !< Initial discrete states type(FVW_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states type(FVW_OtherStateType), intent( out) :: OtherState !< Initial other states +! type(AFI_ParameterType), intent(in ) :: AFInfo(:) ! The airfoil parameter data type(FVW_OutputType), intent( out) :: y !< Initial system outputs (outputs are not calculated; !! only the output mesh is initialized) type(FVW_MiscVarType), intent( out) :: m !< Initial misc/optimization variables @@ -100,6 +97,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! Move the InitInp%WingsMesh to u CALL MOVE_ALLOC( InitInp%WingsMesh, u%WingsMesh ) ! Move from InitInp to u +!NOTE: We do not have the windspeed until after the FVW initialization (IfW is not initialized until after AD15) ! Wind Speed hack, TODO temporary m%Vwnd_LL(:,:,:) = 0 m%Vwnd_NW(:,:,:,:) = 0 @@ -296,6 +294,11 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) ! Set time step p%DT = InitInp%DT + ! Set indexing to AFI tables -- this is set from the AD15 calling code. + call AllocAry(p%AFindx,size(InitInp%AFindx,1),size(InitInp%AFindx,2),'AFindx',ErrStat,ErrMsg) + p%AFindx = InitInp%AFindx ! Copying in case AD15 still needs these + + end subroutine FVW_SetParametersFromInputs ! ============================================================================== !> @@ -393,7 +396,7 @@ end subroutine FVW_End !---------------------------------------------------------------------------------------------------------------------------------- !> Loose coupling routine for solving for constraint states, integrating continuous states, and updating discrete and other states. !! Continuous, constraint, discrete, and other states are updated for t + Interval -subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat, errMsg ) +subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m, errStat, errMsg ) !.................................................................................................................................. real(DbKi), intent(in ) :: t !< Current simulation time in seconds integer(IntKi), intent(in ) :: n !< Current simulation time step n = 0,1,... @@ -404,6 +407,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta type(FVW_DiscreteStateType), intent(inout) :: xd !< Input: Discrete states at t; Output: at t+dt type(FVW_ConstraintStateType), intent(inout) :: z !< Input: Constraint states at t; Output: at t+dt type(FVW_OtherStateType), intent(inout) :: OtherState !< Input: Other states at t; Output: at t+dt + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data type(FVW_MiscVarType), intent(inout) :: m !< Misc/optimization variables integer(IntKi), intent( out) :: errStat !< Error status of the operation character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None @@ -445,7 +449,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! Returns: z%Gamma_LL (at t) call AllocAry( z_guess%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat, ErrMsg ); z_guess%Gamma_LL = m%Gamma_LL - call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2, 1); if(Failed()) return + call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 1); if(Failed()) return ! Map circulation and positions between LL and NW and then NW and FW ! Changes: x only @@ -488,7 +492,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errSta ! --- Solve for circulation at t+dt ! Returns: z%Gamma_LL (at t+dt) z_guess%Gamma_LL = z%Gamma_LL ! We use as guess the circulation from the previous time step (see above) - call FVW_CalcConstrStateResidual(t+dt, uInterp, p, x, xd, z_guess, OtherState, m, z, ErrStat2, ErrMsg2, 2); if(Failed()) return + call FVW_CalcConstrStateResidual(t+dt, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 2); if(Failed()) return ! Updating circulation of near wake panel (and position but irrelevant) ! Changes: x only @@ -622,7 +626,7 @@ end subroutine FVW_Euler1 !---------------------------------------------------------------------------------------------------------------------------------- !> This is a tight coupling routine for solving for the residual of the constraint state functions. -subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, z_out, ErrStat, ErrMsg, iLabel) +subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, z_out, AFInfo, ErrStat, ErrMsg, iLabel) real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(FVW_InputType), intent(in ) :: u !< Inputs at t type(FVW_ParameterType), intent(in ) :: p !< Parameters @@ -631,7 +635,9 @@ subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, type(FVW_ConstraintStateType), intent(in ) :: z_guess !< Constraint states at t (possibly a guess) type(FVW_OtherStateType), intent(in ) :: OtherState !< Other states at t type(FVW_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) - type(FVW_ConstraintStateType), intent( out) :: z_out !< Residual of the constraint state functions using + type(FVW_ConstraintStateType), intent( out) :: z_out !< Residual of the constraint state functions using + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data + integer(IntKi), intent(in) :: iLabel !! the input values described above integer(IntKi), intent( OUT) :: ErrStat !< Error status of the operation @@ -645,7 +651,7 @@ subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, call AllocAry( z_out%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat, ErrMsg ); z_out%Gamma_LL = -999999_ReKi; - CALL Wings_ComputeCirculation(t, z_out%Gamma_LL, z_guess%Gamma_LL, u, p, x, m, ErrStat, ErrMsg, iLabel) + CALL Wings_ComputeCirculation(t, z_out%Gamma_LL, z_guess%Gamma_LL, u, p, x, m, AFInfo, ErrStat, ErrMsg, iLabel) end subroutine FVW_CalcConstrStateResidual @@ -654,7 +660,7 @@ end subroutine FVW_CalcConstrStateResidual !! This subroutine is used to compute the output channels (motions and loads) and place them in the WriteOutput() array. !! The descriptions of the output channels are not given here. Please see the included OutListParameters.xlsx sheet for !! for a complete description of each output parameter. -subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) +subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ErrMsg ) ! NOTE: no matter how many channels are selected for output, all of the outputs are calculated ! All of the calculated output channels are placed into the m%AllOuts(:), while the channels selected for outputs are ! placed in the y%WriteOutput(:) array. @@ -667,6 +673,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg !FIXME:TODO: AD15_CalcOutput has constraint states as intent(in) only. This is forcing me to store z in the AD15 miscvars for now. type(FVW_ConstraintStateType), intent(in ) :: z !< Constraint states at t type(FVW_OtherStateType), intent(in ) :: OtherState !< Other states at t + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data type(FVW_OutputType), intent(inout) :: y !< Outputs computed at t (Input only so that mesh con- !! nectivity information does not have to be recalculated) type(FVW_MiscVarType), intent(inout) :: m !< Misc/optimization variables @@ -684,7 +691,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg print'(A,F10.3,A,L1,A,I0,A,I0)','CalcOutput t:',t,' ',m%FirstCall,' nNW:',m%nNW,' nFW:',m%nFW ! if we are on a correction step, CalcOutput may be called again with different inputs - CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only + CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, AFInfo, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only ! Set the wind velocity at vortex CALL DistributeRequestedWind(u%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index d01f0b36f2..db5ccd7f3d 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -4,12 +4,14 @@ # keyword "" "" ################################################################################################################################## include Registry_NWTC_Library.txt +usefrom AirfoilInfo_Registry.txt ##################### Registry for FVW ############### # ..... PARAMETERS ............. #FVW_ParameterType typedef FVW/FVW ParameterType IntKi nWings - - - "Number of Wings" - typedef ^ ^ IntKi nSpan - - - "TODO, should be defined per wing. Number of spanwise element" - +typedef ^ ^ IntKi AFindx :: - - "Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number]" - typedef ^ ^ IntKi nNWMax - - - "Maximum number of nw panels, per wing" - typedef ^ ^ IntKi nFWMax - - - "Maximum number of fw panels, per wing" - typedef ^ ^ IntKi nFWFree - - - "Number of fw panels that are free, per wing" - diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 7102a19e2c..2e7695400c 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -31,12 +31,14 @@ !! unpack routines associated with each defined data type. This code is automatically generated by the FAST Registry. MODULE FVW_Types !--------------------------------------------------------------------------------------------------------------------------------- +USE AirfoilInfo_Types USE NWTC_Library IMPLICIT NONE ! ========= FVW_ParameterType ======= TYPE, PUBLIC :: FVW_ParameterType INTEGER(IntKi) :: nWings !< Number of Wings [-] INTEGER(IntKi) :: nSpan !< TODO, should be defined per wing. Number of spanwise element [-] + INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number] [-] INTEGER(IntKi) :: nNWMax !< Maximum number of nw panels, per wing [-] INTEGER(IntKi) :: nFWMax !< Maximum number of fw panels, per wing [-] INTEGER(IntKi) :: nFWFree !< Number of fw panels that are free, per wing [-] @@ -194,6 +196,20 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg ErrMsg = "" DstParamData%nWings = SrcParamData%nWings DstParamData%nSpan = SrcParamData%nSpan +IF (ALLOCATED(SrcParamData%AFindx)) THEN + i1_l = LBOUND(SrcParamData%AFindx,1) + i1_u = UBOUND(SrcParamData%AFindx,1) + i2_l = LBOUND(SrcParamData%AFindx,2) + i2_u = UBOUND(SrcParamData%AFindx,2) + IF (.NOT. ALLOCATED(DstParamData%AFindx)) THEN + ALLOCATE(DstParamData%AFindx(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%AFindx.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%AFindx = SrcParamData%AFindx +ENDIF DstParamData%nNWMax = SrcParamData%nNWMax DstParamData%nFWMax = SrcParamData%nFWMax DstParamData%nFWFree = SrcParamData%nFWFree @@ -236,6 +252,9 @@ SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" +IF (ALLOCATED(ParamData%AFindx)) THEN + DEALLOCATE(ParamData%AFindx) +ENDIF IF (ALLOCATED(ParamData%PrescribedCirculation)) THEN DEALLOCATE(ParamData%PrescribedCirculation) ENDIF @@ -278,6 +297,11 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = 0 Int_BufSz = Int_BufSz + 1 ! nWings Int_BufSz = Int_BufSz + 1 ! nSpan + Int_BufSz = Int_BufSz + 1 ! AFindx allocated yes/no + IF ( ALLOCATED(InData%AFindx) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! AFindx upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%AFindx) ! AFindx + END IF Int_BufSz = Int_BufSz + 1 ! nNWMax Int_BufSz = Int_BufSz + 1 ! nFWMax Int_BufSz = Int_BufSz + 1 ! nFWFree @@ -333,6 +357,22 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nSpan Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%AFindx) ) 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%AFindx,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AFindx,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%AFindx,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AFindx,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%AFindx)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%AFindx))-1 ) = PACK(InData%AFindx,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(InData%AFindx) + END IF IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nNWMax Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWMax @@ -426,6 +466,32 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%nSpan = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! AFindx 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 + IF (ALLOCATED(OutData%AFindx)) DEALLOCATE(OutData%AFindx) + ALLOCATE(OutData%AFindx(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%AFindx.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%AFindx)>0) OutData%AFindx = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%AFindx))-1 ), mask2, 0_IntKi ) + Int_Xferred = Int_Xferred + SIZE(OutData%AFindx) + DEALLOCATE(mask2) + END IF OutData%nNWMax = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%nFWMax = IntKiBuf( Int_Xferred ) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 346ab319ba..6ab91e3b35 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -3,6 +3,7 @@ module FVW_Wings use NWTC_Library use FVW_Types use FVW_Subs + use AirfoilInfo implicit none @@ -199,7 +200,7 @@ end subroutine Wings_Panelling !---------------------------------------------------------------------------------------------------------------------------------- !> - subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg, iLabel) + subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, ErrStat, ErrMsg, iLabel) real(DbKi), intent(in ) :: t !< Current simulation time in seconds real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines real(ReKi), dimension(:,:), intent(in ) :: Gamma_LL_prev !< Previous/Guessed circulation @@ -207,6 +208,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None integer(IntKi), intent(in) :: iLabel @@ -226,7 +228,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS else if (p%CirculationMethod==idCircPolarData) then ! --- Solve for circulation using polar data !print*,'>>>>>>>>>>>>>>>>> Circulation solving with polar data >>>>>>>>>>>>>> CALL ',iLabel - CALL Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg, iLabel) + CALL Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, ErrStat, ErrMsg, iLabel) else if (p%CirculationMethod==idCircNoFlowThrough) then ! --- Solve for circulation using the no-flow through condition @@ -249,7 +251,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrS !---------------------------------------------------------------------------------------------------------------------------------- !> - subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, ErrStat, ErrMsg, iLabel) + subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, ErrStat, ErrMsg, iLabel) real(DbKi), intent(in ) :: t !< Current simulation time in seconds real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines real(ReKi), dimension(:,:), intent(in ) :: Gamma_LL_prev !< Previous/Guessed circulation @@ -257,6 +259,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None integer(IntKi), intent(in) :: iLabel @@ -271,6 +274,9 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x integer(IntKi) :: iW, iSpan, iDepth, iWCP, nCPs real(ReKi), dimension(3) :: P1, P2, P3, P4 real(ReKi) :: Gamm + ! Error handling + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 ! Initialize ErrStat ErrStat = ErrID_None @@ -285,7 +291,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x if (m%FirstCall) then ! We find a guess by looking simply at the Wind and Elasticity velocity m%Vtot_ll = m%Vwnd_LL - m%Vstr_ll - call CirculationFromPolarData(GammaLastIter, p, m) + call CirculationFromPolarData(GammaLastIter, p, m, AFInfo,ErrStat2,ErrMsg2); if(Failed()) return; else GammaLastIter(1:p%nSpan,1:p%nWings) = Gamma_LL_prev(1:p%nSpan,1:p%nWings) endif @@ -298,11 +304,12 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x ! --- Setting up Vcst: part of the velocity that is constant withing the iteration loop ! Vrel_ll_cst = U_u0 - U_body - call AllocAry(Vvar, 3, p%nSpan, p%nWings, 'Vvar', ErrStat, ErrMsg) - call AllocAry(Vcst, 3, p%nSpan, p%nWings, 'Vcst', ErrStat, ErrMsg) + call AllocAry(Vvar, 3, p%nSpan, p%nWings, 'Vvar', ErrStat2, ErrMsg2); if(Failed()) return; + call AllocAry(Vcst, 3, p%nSpan, p%nWings, 'Vcst', ErrStat2, ErrMsg2); if(Failed()) return; ! Set m%Vind_LL Induced velocity from Known wake only (after iNWStart+1) - call LiftingLineInducedVelocities(p, x, iNWStart+1, m, ErrStat, ErrMsg) + call LiftingLineInducedVelocities(p, x, iNWStart+1, m, ErrStat2, ErrMsg2); if(Failed()) return; + Vcst = m%Vind_LL + m%Vwnd_LL - m%Vstr_ll if (any(m%Vind_LL(1:3,:,:)<-99)) then @@ -342,7 +349,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x !call print_mean_3d( Vvar(:,:,:), 'Mean induced vel. LL (var)') !call print_mean_3d( m%Vtot_LL(:,:,:), 'Mean relativevel. LL (tot)') ! --- Computing circulation based on Vtot_LL - call CirculationFromPolarData(Gamma_LL, p, m) + call CirculationFromPolarData(Gamma_LL, p, m, AFInfo,ErrStat2,ErrMsg2); if(Failed()) return; ! --------------------------------------------- ! Differences between iterations and relaxation @@ -379,28 +386,49 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x !print*,'m%Vcst_LL',Vcst(1,:,:) m%Vind_LL=-9999._ReKi !< Safety (the induction above was not the true one) m%Vtot_LL=-9999._ReKi !< Safety - deallocate(DGamma ) - deallocate(GammaLastIter) - deallocate(Vcst) - deallocate(Vvar) !print*,'Gamm: ',Gamma_LL(1, 1), Gamma_LL(p%nSpan,1) !if (abs(Gamma_LL(1, 1)-Gamma_LL(p%nSpan,1))>0.01) STOP !if (m%iStep==3) STOP - end subroutine + call CleanUp() + contains + + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'Wings_ComputeCirculationPolarData') + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + subroutine CleanUp() + if(allocated(DGamma )) deallocate(DGamma ) + if(allocated(GammaLastIter)) deallocate(GammaLastIter) + if(allocated(Vcst)) deallocate(Vcst) + if(allocated(Vvar)) deallocate(Vvar) + end subroutine + end subroutine Wings_ComputeCirculationPolarData !> Compute circulation based on polar data !! Uses m%Vtot_ll to compute Gamma_ll - subroutine CirculationFromPolarData(Gamma_LL, p, m) + subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local integer(IntKi) :: iW, iCP !< Index on wings and spanwise control points real(ReKi), dimension(3) :: N, Tc !< Normal and Tangent vector real(ReKi), dimension(3) :: Vrel, Vrel_orth, Vjouk, Vjouk_orth real(ReKi) :: Vrel_orth_norm, Vjouk_orth_norm - real(ReKi) :: alpha, Re, Cl + real(ReKi) :: alpha, Re, Cl, Cd, Cm + type(AFI_OutputType) :: AFI_interp + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" do iW=1,p%nWings do icp=1,p%nSpan @@ -417,10 +445,16 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m) alpha = atan2(dot_product(Vrel,N) , dot_product(Vrel,Tc) ) ! [rad] !Re = LL%Vrel_orth_norm(icp)*LL%chord(icp)/KinVisc/(1.E6_MK) ! TODO TODO TODO KinVisc +Re=1.0_ReKi if (p%CircSolvPolar==idPolarAeroDyn) then - print*,'TODO TODO TODO Get Cl, Cd, Cm from alpha, Re and AirfoilInfo' - STOP + ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. + call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, AFInfo(p%AFindx(icp,iW)), AFI_interp, ErrStat2, ErrMsg2 ); if(Failed()) return; + Cl = AFI_interp%Cl + Cd = AFI_interp%Cd + Cm = AFI_interp%Cm +! Cpmin = AFI_interp%Cpmin +print*,'Manu: fix Re for AFI in CirculationFromPolarData' else if (p%CircSolvPolar==idPolar2PiAlpha) then Cl=TwoPi*alpha else if (p%CircSolvPolar==idPolar2PiSinAlpha) then @@ -438,6 +472,15 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m) !endif enddo enddo + contains + logical function Failed() + character(25) :: NodeText + if (ErrStat2 /= ErrID_None) then + NodeText = '(node '//trim(num2lstr(icp))//', blade '//trim(num2lstr(iW))//')' + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'CirculationFromPolarData'//trim(NodeText)) + end if + Failed = ErrStat >= AbortErrLev + end function Failed end subroutine CirculationFromPolarData From b0110cb48759ce035ac97ad750b68fde9fbb03dd Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 22 Jan 2020 10:42:02 -0700 Subject: [PATCH 051/190] FVW: turn off all BEMT stuff if using FVW. Set placeholders for outputs --- modules/aerodyn/src/AeroDyn.f90 | 128 +++++--- modules/aerodyn/src/AeroDyn_IO.f90 | 483 +++++++++++++++++++---------- 2 files changed, 402 insertions(+), 209 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 9feaeee35d..4d667f43f3 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -370,17 +370,18 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut ! initialize BEMT after setting parameters and inputs because we are going to use the already- ! calculated node positions from the input meshes - call Init_BEMTmodule( InputFileData, u, m%BEMT_u(1), p, x%BEMT, xd%BEMT, z%BEMT, & - OtherState%BEMT, m%BEMT_y, m%BEMT, ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - if (ErrStat >= AbortErrLev) then - call Cleanup() - return - end if - - call BEMT_CopyInput( m%BEMT_u(1), m%BEMT_u(2), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (p%WakeMod /= WakeMod_FVW) then + call Init_BEMTmodule( InputFileData, u, m%BEMT_u(1), p, x%BEMT, xd%BEMT, z%BEMT, & + OtherState%BEMT, m%BEMT_y, m%BEMT, ErrStat2, ErrMsg2 ) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (ErrStat >= AbortErrLev) then + call Cleanup() + return + end if + call BEMT_CopyInput( m%BEMT_u(1), m%BEMT_u(2), MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + endif !------------------------------------------------------------------------------------------------- ! Initialize FVW module if it is used @@ -507,9 +508,10 @@ subroutine AD_ReInit(p, x, xd, z, OtherState, m, Interval, ErrStat, ErrMsg ) ! we could get around this by figuring out what needs to change when we modify the dt parameter... probably just some unused-parameters ! and the UA filter end if - - call BEMT_ReInit(p%BEMT,x%BEMT,xd%BEMT,z%BEMT,OtherState%BEMT,m%BEMT,p%AFI) - + + if (p%WakeMod /= WakeMod_FVW) & + call BEMT_ReInit(p%BEMT,x%BEMT,xd%BEMT,z%BEMT,OtherState%BEMT,m%BEMT,p%AFI) + end subroutine AD_ReInit !---------------------------------------------------------------------------------------------------------------------------------- !> This routine initializes (allocates) the misc variables for use during the simulation. @@ -1147,14 +1149,14 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat call SetInputs(p, uInterp, m, 1, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - - ! Call into the BEMT update states NOTE: This is a non-standard framework interface!!!!! GJH - ! Also note BEMT_u(1) and BEMT_u(2) are not following the framework convention for t+dt, t - call BEMT_UpdateStates(t, n, m%BEMT_u(1), m%BEMT_u(2), p%BEMT, x%BEMT, xd%BEMT, z%BEMT, OtherState%BEMT, p%AFI, m%BEMT, errStat2, errMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - - ! Call the FVW sub module - if (p%WakeMod == WakeMod_FVW) then + + if (p%WakeMod /= WakeMod_FVW) then + ! Call into the BEMT update states NOTE: This is a non-standard framework interface!!!!! GJH + ! Also note BEMT_u(1) and BEMT_u(2) are not following the framework convention for t+dt, t + call BEMT_UpdateStates(t, n, m%BEMT_u(1), m%BEMT_u(2), p%BEMT, x%BEMT, xd%BEMT, z%BEMT, OtherState%BEMT, p%AFI, m%BEMT, errStat2, errMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + + else ! Call the FVW sub module ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module call SetInputsForFVW(p, u, m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1212,25 +1214,28 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" - - call SetInputs(p, u, m, indx, errStat2, errMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - - ! Call the BEMT module CalcOutput. Notice that the BEMT outputs are purposely attached to AeroDyn's MiscVar structure to - ! avoid issues with the coupling code - - call BEMT_CalcOutput(t, m%BEMT_u(indx), p%BEMT, x%BEMT, xd%BEMT, z%BEMT, OtherState%BEMT, p%AFI, m%BEMT_y, m%BEMT, ErrStat2, ErrMsg2 ) + + call SetInputs(p, u, m, indx, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - - call SetOutputsFromBEMT(p, m, y ) - if (p%WakeMod == WakeMod_FVW) then + if (p%WakeMod /= WakeMod_FVW) then + ! Call the BEMT module CalcOutput. Notice that the BEMT outputs are purposely attached to AeroDyn's MiscVar structure to + ! avoid issues with the coupling code + + call BEMT_CalcOutput(t, m%BEMT_u(indx), p%BEMT, x%BEMT, xd%BEMT, z%BEMT, OtherState%BEMT, p%AFI, m%BEMT_y, m%BEMT, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + + call SetOutputsFromBEMT(p, m, y ) + + else !(p%WakeMod == WakeMod_FVW) ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module call SetInputsForFVW(p, (/u/), m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Calculate Outputs at time t CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, p%AFI, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + + call SetOutputsFromFVW(p, m, y ) endif if ( p%TwrAero ) then @@ -1364,10 +1369,12 @@ subroutine SetInputs(p, u, m, indx, errStat, errMsg) else m%DisturbedInflow = u%InflowOnBlade end if - - ! This needs to extract the inputs from the AD data types (mesh) and massage them for the BEMT module - call SetInputsForBEMT(p, u, m, indx, errStat2, errMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + + if (p%WakeMod /= WakeMod_FVW) then + ! This needs to extract the inputs from the AD data types (mesh) and massage them for the BEMT module + call SetInputsForBEMT(p, u, m, indx, errStat2, errMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + endif end subroutine SetInputs @@ -1607,6 +1614,53 @@ subroutine SetOutputsFromBEMT(p, m, y ) end subroutine SetOutputsFromBEMT + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This subroutine converts outputs from FVW (stored in m%FVW_y) into values on the AeroDyn BladeLoad output mesh. +subroutine SetOutputsFromFVW(p, m, y) + type(AD_ParameterType), intent(in ) :: p !< AD parameters + type(AD_OutputType), intent(inout) :: y !< AD outputs + type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables + + integer(intKi) :: j ! loop counter for nodes + integer(intKi) :: k ! loop counter for blades + real(reki) :: force(3) + real(reki) :: moment(3) + real(reki) :: q + + force(3) = 0.0_ReKi + moment(1:2) = 0.0_ReKi + do k=1,p%NumBlades + do j=1,p%NumBlNds +! q = 0.5 * p%airDens * m%BEMT_y%Vrel(j,k)**2 ! dynamic pressure of the jth node in the kth blade +! force(1) = m%BEMT_y%cx(j,k) * q * p%BEMT%chord(j,k) ! X = normal force per unit length (normal to the plane, not chord) of the jth node in the kth blade +! force(2) = -m%BEMT_y%cy(j,k) * q * p%BEMT%chord(j,k) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade +! moment(3)= m%BEMT_y%cm(j,k) * q * p%BEMT%chord(j,k)**2 ! M = pitching moment per unit length of the jth node in the kth blade + end do !j=nodes + end do !k=blades + + do k=1,p%NumBlades + do j=1,p%NumBlNds +! ! save these values for possible output later: +! m%X(j,k) = force(1) +! m%Y(j,k) = force(2) +! m%M(j,k) = moment(3) + end do !j=nodes + end do !k=blades + + do k=1,p%NumBlades + do j=1,p%NumBlNds + ! note: because force and moment are 1-d arrays, I'm calculating the transpose of the force and moment outputs + ! so that I don't have to take the transpose of WithoutSweepPitchTwist(:,:,j,k) +! y%BladeLoad(k)%Force(:,j) = matmul( force, m%WithoutSweepPitchTwist(:,:,j,k) ) ! force per unit length of the jth node in the kth blade +! y%BladeLoad(k)%Moment(:,j) = matmul( moment, m%WithoutSweepPitchTwist(:,:,j,k) ) ! moment per unit length of the jth node in the kth blade + + end do !j=nodes + end do !k=blades + + +end subroutine SetOutputsFromFVW !---------------------------------------------------------------------------------------------------------------------------------- !> This routine validates the inputs from the AeroDyn input files. SUBROUTINE ValidateInputData( InitInp, InputFileData, NumBl, ErrStat, ErrMsg ) @@ -1656,7 +1710,7 @@ SUBROUTINE ValidateInputData( InitInp, InputFileData, NumBl, ErrStat, ErrMsg ) ! BEMT/DBEMT inputs ! bjj: these checks should probably go into BEMT where they are used... - if (InputFileData%WakeMod /= WakeMod_none) then + if (InputFileData%WakeMod /= WakeMod_none .and. InputFileData%WakeMod /= WakeMod_FVW) then if ( InputFileData%MaxIter < 1 ) call SetErrStat( ErrID_Fatal, 'MaxIter must be greater than 0.', ErrStat, ErrMsg, RoutineName ) if ( InputFileData%IndToler < 0.0 .or. EqualRealNos(InputFileData%IndToler, 0.0_ReKi) ) & diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index b401005403..9c59728244 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1537,54 +1537,58 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) ! start routine: ErrStat = ErrID_None ErrMsg = "" - - - - ! blade outputs - do k=1,p%numBlades - - ! m%AllOuts( BPitch( k) ) = calculated in SetInputsForBEMT - - do j=1,p%NumBlNds - - i = (k-1)*p%NumBlNds*23 + (j-1)*23 + 1 - - m%AllOuts( i ) = m%BEMT_u(indx)%theta(j,k)*R2D - m%AllOuts( i+1 ) = m%BEMT_u(indx)%psi(k)*R2D - m%AllOuts( i+2 ) = -m%BEMT_u(indx)%Vx(j,k) - m%AllOuts( i+3 ) = m%BEMT_u(indx)%Vy(j,k) - - m%AllOuts( i+4 ) = m%BEMT_y%axInduction(j,k) - m%AllOuts( i+5 ) = m%BEMT_y%tanInduction(j,k) - m%AllOuts( i+6 ) = m%BEMT_y%Vrel(j,k) - m%AllOuts( i+7 ) = m%BEMT_y%phi(j,k)*R2D - m%AllOuts( i+8 ) = (m%BEMT_y%phi(j,k) - m%BEMT_u(indx)%theta(j,k))*R2D - - - m%AllOuts( i+9 ) = m%BEMT_y%Cl(j,k) - m%AllOuts( i+10 ) = m%BEMT_y%Cd(j,k) - m%AllOuts( i+11 ) = m%BEMT_y%Cm(j,k) - m%AllOuts( i+12 ) = m%BEMT_y%Cx(j,k) - m%AllOuts( i+13 ) = m%BEMT_y%Cy(j,k) - - ct=cos(m%BEMT_u(indx)%theta(j,k)) - st=sin(m%BEMT_u(indx)%theta(j,k)) - m%AllOuts( i+14 ) = m%BEMT_y%Cx(j,k)*ct + m%BEMT_y%Cy(j,k)*st - m%AllOuts( i+15 ) = -m%BEMT_y%Cx(j,k)*st + m%BEMT_y%Cy(j,k)*ct - - cp=cos(m%BEMT_y%phi(j,k)) - sp=sin(m%BEMT_y%phi(j,k)) - m%AllOuts( i+16 ) = m%X(j,k)*cp - m%Y(j,k)*sp - m%AllOuts( i+17 ) = m%X(j,k)*sp + m%Y(j,k)*cp - m%AllOuts( i+18 ) = m%M(j,k) - m%AllOuts( i+19 ) = m%X(j,k) - m%AllOuts( i+20 ) = -m%Y(j,k) - m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st - m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct - - end do ! nodes - end do ! blades - + + + + if (p%WakeMod /= WakeMod_FVW) then + ! blade outputs + do k=1,p%numBlades + + ! m%AllOuts( BPitch( k) ) = calculated in SetInputsForBEMT + + do j=1,p%NumBlNds + + i = (k-1)*p%NumBlNds*23 + (j-1)*23 + 1 + + m%AllOuts( i ) = m%BEMT_u(indx)%theta(j,k)*R2D + m%AllOuts( i+1 ) = m%BEMT_u(indx)%psi(k)*R2D + m%AllOuts( i+2 ) = -m%BEMT_u(indx)%Vx(j,k) + m%AllOuts( i+3 ) = m%BEMT_u(indx)%Vy(j,k) + + m%AllOuts( i+4 ) = m%BEMT_y%axInduction(j,k) + m%AllOuts( i+5 ) = m%BEMT_y%tanInduction(j,k) + m%AllOuts( i+6 ) = m%BEMT_y%Vrel(j,k) + m%AllOuts( i+7 ) = m%BEMT_y%phi(j,k)*R2D + m%AllOuts( i+8 ) = (m%BEMT_y%phi(j,k) - m%BEMT_u(indx)%theta(j,k))*R2D + + + m%AllOuts( i+9 ) = m%BEMT_y%Cl(j,k) + m%AllOuts( i+10 ) = m%BEMT_y%Cd(j,k) + m%AllOuts( i+11 ) = m%BEMT_y%Cm(j,k) + m%AllOuts( i+12 ) = m%BEMT_y%Cx(j,k) + m%AllOuts( i+13 ) = m%BEMT_y%Cy(j,k) + + ct=cos(m%BEMT_u(indx)%theta(j,k)) + st=sin(m%BEMT_u(indx)%theta(j,k)) + m%AllOuts( i+14 ) = m%BEMT_y%Cx(j,k)*ct + m%BEMT_y%Cy(j,k)*st + m%AllOuts( i+15 ) = -m%BEMT_y%Cx(j,k)*st + m%BEMT_y%Cy(j,k)*ct + + cp=cos(m%BEMT_y%phi(j,k)) + sp=sin(m%BEMT_y%phi(j,k)) + m%AllOuts( i+16 ) = m%X(j,k)*cp - m%Y(j,k)*sp + m%AllOuts( i+17 ) = m%X(j,k)*sp + m%Y(j,k)*cp + m%AllOuts( i+18 ) = m%M(j,k) + m%AllOuts( i+19 ) = m%X(j,k) + m%AllOuts( i+20 ) = -m%Y(j,k) + m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st + m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct + + end do ! nodes + end do ! blades + else ! (p%WakeMod == WakeMod_FVW) + m%AllOuts = 0.0_ReKi + endif + END SUBROUTINE Calc_WriteDbgOutput !---------------------------------------------------------------------------------------------------------------------------------- @@ -1636,79 +1640,14 @@ SUBROUTINE Calc_WriteOutput( p, u, m, y, OtherState, indx, ErrStat, ErrMsg ) m%AllOuts( TwNFdy( beta) ) = m%Y_Twr(j) end do ! out nodes - - ! blade outputs - do k=1,p%numBlades - m%AllOuts( BAzimuth(k) ) = m%BEMT_u(indx)%psi(k)*R2D - ! m%AllOuts( BPitch( k) ) = calculated in SetInputsForBEMT - - do beta=1,p%NBlOuts - - j=p%BlOutNd(beta) - - - tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%InflowOnBlade(:,j,k) ) - m%AllOuts( BNVUndx(beta,k) ) = tmp(1) - m%AllOuts( BNVUndy(beta,k) ) = tmp(2) - m%AllOuts( BNVUndz(beta,k) ) = tmp(3) - - tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), m%DisturbedInflow(:,j,k) ) - m%AllOuts( BNVDisx(beta,k) ) = tmp(1) - m%AllOuts( BNVDisy(beta,k) ) = tmp(2) - m%AllOuts( BNVDisz(beta,k) ) = tmp(3) - - tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%TranslationVel(:,j) ) - m%AllOuts( BNSTVx( beta,k) ) = tmp(1) - m%AllOuts( BNSTVy( beta,k) ) = tmp(2) - m%AllOuts( BNSTVz( beta,k) ) = tmp(3) - - m%AllOuts( BNVrel( beta,k) ) = m%BEMT_y%Vrel(j,k) - m%AllOuts( BNDynP( beta,k) ) = 0.5 * p%airDens * m%BEMT_y%Vrel(j,k)**2 - m%AllOuts( BNRe( beta,k) ) = p%BEMT%chord(j,k) * m%BEMT_y%Vrel(j,k) / p%KinVisc / 1.0E6 - m%AllOuts( BNM( beta,k) ) = m%BEMT_y%Vrel(j,k) / p%SpdSound - m%AllOuts( BNVIndx(beta,k) ) = - m%BEMT_u(indx)%Vx(j,k) * m%BEMT_y%axInduction( j,k) - m%AllOuts( BNVIndy(beta,k) ) = m%BEMT_u(indx)%Vy(j,k) * m%BEMT_y%tanInduction(j,k) - - m%AllOuts( BNAxInd(beta,k) ) = m%BEMT_y%axInduction(j,k) - m%AllOuts( BNTnInd(beta,k) ) = m%BEMT_y%tanInduction(j,k) + if (p%WakeMod /= WakeMod_FVW) then + call Calc_WriteOutput_BEMT + else + call Calc_WriteOutput_FVW + endif + - m%AllOuts( BNAlpha(beta,k) ) = Rad2M180to180Deg( m%BEMT_y%phi(j,k) - m%BEMT_u(indx)%theta(j,k) ) - m%AllOuts( BNTheta(beta,k) ) = m%BEMT_u(indx)%theta(j,k)*R2D - m%AllOuts( BNPhi( beta,k) ) = m%BEMT_y%phi(j,k)*R2D - m%AllOuts( BNCurve(beta,k) ) = m%Curve(j,k)*R2D - - !m%AllOuts( BNCl( beta,k) ) = m%BEMT_y%Cl(j,k) - !m%AllOuts( BNCd( beta,k) ) = m%BEMT_y%Cd(j,k) - - m%AllOuts( BNCpmin( beta,k) ) = m%BEMT_y%Cpmin(j,k) - m%AllOuts( BNSigCr( beta,k) ) = m%SigmaCavitCrit(j,k) - m%AllOuts( BNSgCav( beta,k) ) = m%SigmaCavit(j,k) - - cp=cos(m%BEMT_y%phi(j,k)) - sp=sin(m%BEMT_y%phi(j,k)) - m%AllOuts( BNCl( beta,k) ) = m%BEMT_y%Cx(j,k)*cp + m%BEMT_y%Cy(j,k)*sp - m%AllOuts( BNCd( beta,k) ) = m%BEMT_y%Cx(j,k)*sp - m%BEMT_y%Cy(j,k)*cp - m%AllOuts( BNCm( beta,k) ) = m%BEMT_y%Cm(j,k) - m%AllOuts( BNCx( beta,k) ) = m%BEMT_y%Cx(j,k) - m%AllOuts( BNCy( beta,k) ) = m%BEMT_y%Cy(j,k) - - ct=cos(m%BEMT_u(indx)%theta(j,k)) - st=sin(m%BEMT_u(indx)%theta(j,k)) - m%AllOuts( BNCn( beta,k) ) = m%BEMT_y%Cx(j,k)*ct + m%BEMT_y%Cy(j,k)*st - m%AllOuts( BNCt( beta,k) ) =-m%BEMT_y%Cx(j,k)*st + m%BEMT_y%Cy(j,k)*ct - - m%AllOuts( BNFl( beta,k) ) = m%X(j,k)*cp - m%Y(j,k)*sp - m%AllOuts( BNFd( beta,k) ) = m%X(j,k)*sp + m%Y(j,k)*cp - m%AllOuts( BNMm( beta,k) ) = m%M(j,k) - m%AllOuts( BNFx( beta,k) ) = m%X(j,k) - m%AllOuts( BNFy( beta,k) ) = -m%Y(j,k) - m%AllOuts( BNFn( beta,k) ) = m%X(j,k)*ct - m%Y(j,k)*st - m%AllOuts( BNFt( beta,k) ) = -m%X(j,k)*st - m%Y(j,k)*ct - - end do ! nodes - end do ! blades - ! blade node tower clearance (requires tower influence calculation): if (p%TwrPotent /= TwrPotent_none .or. p%TwrShadow) then do k=1,p%numBlades @@ -1719,61 +1658,261 @@ SUBROUTINE Calc_WriteOutput( p, u, m, y, OtherState, indx, ErrStat, ErrMsg ) end do end if - ! rotor outputs: - rmax = 0.0_ReKi - do k=1,p%NumBlades - do j=1,p%NumBlNds - rmax = max(rmax, m%BEMT_u(indx)%rLocal(j,k) ) - end do !j=nodes - end do !k=blades - - m%AllOuts( RtSpeed ) = m%BEMT_u(indx)%omega*RPS2RPM - m%AllOuts( RtArea ) = pi*rmax**2 - - tmp = matmul( u%HubMotion%Orientation(:,:,1), m%V_DiskAvg ) - m%AllOuts( RtVAvgxh ) = tmp(1) - m%AllOuts( RtVAvgyh ) = tmp(2) - m%AllOuts( RtVAvgzh ) = tmp(3) - - m%AllOuts( RtSkew ) = m%BEMT_u(indx)%chi0*R2D - - ! integrate force/moments over blades by performing mesh transfer to hub point: - force = 0.0_ReKi - moment = 0.0_ReKi - do k=1,p%NumBlades - call Transfer_Line2_to_Point( y%BladeLoad(k), m%HubLoad, m%B_L_2_H_P(k), ErrStat2, ErrMsg2, u%BladeMotion(k), u%HubMotion ) - force = force + m%HubLoad%force( :,1) - moment = moment + m%HubLoad%moment(:,1) - end do - tmp = matmul( u%HubMotion%Orientation(:,:,1), force ) - m%AllOuts( RtAeroFxh ) = tmp(1) - m%AllOuts( RtAeroFyh ) = tmp(2) - m%AllOuts( RtAeroFzh ) = tmp(3) - - tmp = matmul( u%HubMotion%Orientation(:,:,1), moment ) - m%AllOuts( RtAeroMxh ) = tmp(1) - m%AllOuts( RtAeroMyh ) = tmp(2) - m%AllOuts( RtAeroMzh ) = tmp(3) - - m%AllOuts( RtAeroPwr ) = m%BEMT_u(indx)%omega * m%AllOuts( RtAeroMxh ) - - - if ( EqualRealNos( m%V_dot_x, 0.0_ReKi ) ) then - m%AllOuts( RtTSR ) = 0.0_ReKi - m%AllOuts( RtAeroCp ) = 0.0_ReKi - m%AllOuts( RtAeroCq ) = 0.0_ReKi - m%AllOuts( RtAeroCt ) = 0.0_ReKi - else - denom = 0.5*p%AirDens*m%AllOuts( RtArea )*m%V_dot_x**2 - m%AllOuts( RtTSR ) = m%BEMT_u(indx)%omega * rmax / m%V_dot_x - - m%AllOuts( RtAeroCp ) = m%AllOuts( RtAeroPwr ) / (denom * m%V_dot_x) - m%AllOuts( RtAeroCq ) = m%AllOuts( RtAeroMxh ) / (denom * rmax) - m%AllOuts( RtAeroCt ) = m%AllOuts( RtAeroFxh ) / denom - end if - !m%AllOuts( DBEMTau1 ) = OtherState%BEMT%DBEMT%tau1 +CONTAINS + subroutine Calc_WriteOutput_BEMT + ! blade outputs + do k=1,p%numBlades + m%AllOuts( BAzimuth(k) ) = m%BEMT_u(indx)%psi(k)*R2D + ! m%AllOuts( BPitch( k) ) = calculated in SetInputsForBEMT + + do beta=1,p%NBlOuts + + j=p%BlOutNd(beta) + + tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%InflowOnBlade(:,j,k) ) + m%AllOuts( BNVUndx(beta,k) ) = tmp(1) + m%AllOuts( BNVUndy(beta,k) ) = tmp(2) + m%AllOuts( BNVUndz(beta,k) ) = tmp(3) + + tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), m%DisturbedInflow(:,j,k) ) + m%AllOuts( BNVDisx(beta,k) ) = tmp(1) + m%AllOuts( BNVDisy(beta,k) ) = tmp(2) + m%AllOuts( BNVDisz(beta,k) ) = tmp(3) + + tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%TranslationVel(:,j) ) + m%AllOuts( BNSTVx( beta,k) ) = tmp(1) + m%AllOuts( BNSTVy( beta,k) ) = tmp(2) + m%AllOuts( BNSTVz( beta,k) ) = tmp(3) + + m%AllOuts( BNVrel( beta,k) ) = m%BEMT_y%Vrel(j,k) + m%AllOuts( BNDynP( beta,k) ) = 0.5 * p%airDens * m%BEMT_y%Vrel(j,k)**2 + m%AllOuts( BNRe( beta,k) ) = p%BEMT%chord(j,k) * m%BEMT_y%Vrel(j,k) / p%KinVisc / 1.0E6 + m%AllOuts( BNM( beta,k) ) = m%BEMT_y%Vrel(j,k) / p%SpdSound + + m%AllOuts( BNVIndx(beta,k) ) = - m%BEMT_u(indx)%Vx(j,k) * m%BEMT_y%axInduction( j,k) + m%AllOuts( BNVIndy(beta,k) ) = m%BEMT_u(indx)%Vy(j,k) * m%BEMT_y%tanInduction(j,k) + + m%AllOuts( BNAxInd(beta,k) ) = m%BEMT_y%axInduction(j,k) + m%AllOuts( BNTnInd(beta,k) ) = m%BEMT_y%tanInduction(j,k) + + m%AllOuts( BNAlpha(beta,k) ) = Rad2M180to180Deg( m%BEMT_y%phi(j,k) - m%BEMT_u(indx)%theta(j,k) ) + m%AllOuts( BNTheta(beta,k) ) = m%BEMT_u(indx)%theta(j,k)*R2D + m%AllOuts( BNPhi( beta,k) ) = m%BEMT_y%phi(j,k)*R2D + m%AllOuts( BNCurve(beta,k) ) = m%Curve(j,k)*R2D + + !m%AllOuts( BNCl( beta,k) ) = m%BEMT_y%Cl(j,k) + !m%AllOuts( BNCd( beta,k) ) = m%BEMT_y%Cd(j,k) + + m%AllOuts( BNCpmin( beta,k) ) = m%BEMT_y%Cpmin(j,k) + m%AllOuts( BNSigCr( beta,k) ) = m%SigmaCavitCrit(j,k) + m%AllOuts( BNSgCav( beta,k) ) = m%SigmaCavit(j,k) + + cp=cos(m%BEMT_y%phi(j,k)) + sp=sin(m%BEMT_y%phi(j,k)) + m%AllOuts( BNCl( beta,k) ) = m%BEMT_y%Cx(j,k)*cp + m%BEMT_y%Cy(j,k)*sp + m%AllOuts( BNCd( beta,k) ) = m%BEMT_y%Cx(j,k)*sp - m%BEMT_y%Cy(j,k)*cp + m%AllOuts( BNCm( beta,k) ) = m%BEMT_y%Cm(j,k) + m%AllOuts( BNCx( beta,k) ) = m%BEMT_y%Cx(j,k) + m%AllOuts( BNCy( beta,k) ) = m%BEMT_y%Cy(j,k) + + ct=cos(m%BEMT_u(indx)%theta(j,k)) + st=sin(m%BEMT_u(indx)%theta(j,k)) + m%AllOuts( BNCn( beta,k) ) = m%BEMT_y%Cx(j,k)*ct + m%BEMT_y%Cy(j,k)*st + m%AllOuts( BNCt( beta,k) ) =-m%BEMT_y%Cx(j,k)*st + m%BEMT_y%Cy(j,k)*ct + + m%AllOuts( BNFl( beta,k) ) = m%X(j,k)*cp - m%Y(j,k)*sp + m%AllOuts( BNFd( beta,k) ) = m%X(j,k)*sp + m%Y(j,k)*cp + m%AllOuts( BNMm( beta,k) ) = m%M(j,k) + m%AllOuts( BNFx( beta,k) ) = m%X(j,k) + m%AllOuts( BNFy( beta,k) ) = -m%Y(j,k) + m%AllOuts( BNFn( beta,k) ) = m%X(j,k)*ct - m%Y(j,k)*st + m%AllOuts( BNFt( beta,k) ) = -m%X(j,k)*st - m%Y(j,k)*ct + + end do ! nodes + end do ! blades + + ! rotor outputs: + rmax = 0.0_ReKi + do k=1,p%NumBlades + do j=1,p%NumBlNds + rmax = max(rmax, m%BEMT_u(indx)%rLocal(j,k) ) + end do !j=nodes + end do !k=blades + + m%AllOuts( RtSpeed ) = m%BEMT_u(indx)%omega*RPS2RPM + m%AllOuts( RtArea ) = pi*rmax**2 + + tmp = matmul( u%HubMotion%Orientation(:,:,1), m%V_DiskAvg ) + m%AllOuts( RtVAvgxh ) = tmp(1) + m%AllOuts( RtVAvgyh ) = tmp(2) + m%AllOuts( RtVAvgzh ) = tmp(3) + + m%AllOuts( RtSkew ) = m%BEMT_u(indx)%chi0*R2D + + ! integrate force/moments over blades by performing mesh transfer to hub point: + force = 0.0_ReKi + moment = 0.0_ReKi + do k=1,p%NumBlades + call Transfer_Line2_to_Point( y%BladeLoad(k), m%HubLoad, m%B_L_2_H_P(k), ErrStat2, ErrMsg2, u%BladeMotion(k), u%HubMotion ) + force = force + m%HubLoad%force( :,1) + moment = moment + m%HubLoad%moment(:,1) + end do + tmp = matmul( u%HubMotion%Orientation(:,:,1), force ) + m%AllOuts( RtAeroFxh ) = tmp(1) + m%AllOuts( RtAeroFyh ) = tmp(2) + m%AllOuts( RtAeroFzh ) = tmp(3) + + tmp = matmul( u%HubMotion%Orientation(:,:,1), moment ) + m%AllOuts( RtAeroMxh ) = tmp(1) + m%AllOuts( RtAeroMyh ) = tmp(2) + m%AllOuts( RtAeroMzh ) = tmp(3) + + m%AllOuts( RtAeroPwr ) = m%BEMT_u(indx)%omega * m%AllOuts( RtAeroMxh ) + + + if ( EqualRealNos( m%V_dot_x, 0.0_ReKi ) ) then + m%AllOuts( RtTSR ) = 0.0_ReKi + m%AllOuts( RtAeroCp ) = 0.0_ReKi + m%AllOuts( RtAeroCq ) = 0.0_ReKi + m%AllOuts( RtAeroCt ) = 0.0_ReKi + else + denom = 0.5*p%AirDens*m%AllOuts( RtArea )*m%V_dot_x**2 + m%AllOuts( RtTSR ) = m%BEMT_u(indx)%omega * rmax / m%V_dot_x + + m%AllOuts( RtAeroCp ) = m%AllOuts( RtAeroPwr ) / (denom * m%V_dot_x) + m%AllOuts( RtAeroCq ) = m%AllOuts( RtAeroMxh ) / (denom * rmax) + m%AllOuts( RtAeroCt ) = m%AllOuts( RtAeroFxh ) / denom + end if + end subroutine Calc_WriteOutput_BEMT + + subroutine Calc_WriteOutput_FVW + ! blade outputs + do k=1,p%numBlades +! m%AllOuts( BAzimuth(k) ) = m%BEMT_u(indx)%psi(k)*R2D +! ! m%AllOuts( BPitch( k) ) = calculated in SetInputsForBEMT +! + do beta=1,p%NBlOuts +! +! j=p%BlOutNd(beta) +! +! tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%InflowOnBlade(:,j,k) ) +! m%AllOuts( BNVUndx(beta,k) ) = tmp(1) +! m%AllOuts( BNVUndy(beta,k) ) = tmp(2) +! m%AllOuts( BNVUndz(beta,k) ) = tmp(3) +! +! tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), m%DisturbedInflow(:,j,k) ) +! m%AllOuts( BNVDisx(beta,k) ) = tmp(1) +! m%AllOuts( BNVDisy(beta,k) ) = tmp(2) +! m%AllOuts( BNVDisz(beta,k) ) = tmp(3) +! +! tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%TranslationVel(:,j) ) +! m%AllOuts( BNSTVx( beta,k) ) = tmp(1) +! m%AllOuts( BNSTVy( beta,k) ) = tmp(2) +! m%AllOuts( BNSTVz( beta,k) ) = tmp(3) +! +! m%AllOuts( BNVrel( beta,k) ) = m%BEMT_y%Vrel(j,k) +! m%AllOuts( BNDynP( beta,k) ) = 0.5 * p%airDens * m%BEMT_y%Vrel(j,k)**2 +! m%AllOuts( BNRe( beta,k) ) = p%BEMT%chord(j,k) * m%BEMT_y%Vrel(j,k) / p%KinVisc / 1.0E6 +! m%AllOuts( BNM( beta,k) ) = m%BEMT_y%Vrel(j,k) / p%SpdSound +! +! m%AllOuts( BNVIndx(beta,k) ) = - m%BEMT_u(indx)%Vx(j,k) * m%BEMT_y%axInduction( j,k) +! m%AllOuts( BNVIndy(beta,k) ) = m%BEMT_u(indx)%Vy(j,k) * m%BEMT_y%tanInduction(j,k) +! +! m%AllOuts( BNAxInd(beta,k) ) = m%BEMT_y%axInduction(j,k) +! m%AllOuts( BNTnInd(beta,k) ) = m%BEMT_y%tanInduction(j,k) +! +! m%AllOuts( BNAlpha(beta,k) ) = Rad2M180to180Deg( m%BEMT_y%phi(j,k) - m%BEMT_u(indx)%theta(j,k) ) +! m%AllOuts( BNTheta(beta,k) ) = m%BEMT_u(indx)%theta(j,k)*R2D +! m%AllOuts( BNPhi( beta,k) ) = m%BEMT_y%phi(j,k)*R2D +! m%AllOuts( BNCurve(beta,k) ) = m%Curve(j,k)*R2D +! +! !m%AllOuts( BNCl( beta,k) ) = m%BEMT_y%Cl(j,k) +! !m%AllOuts( BNCd( beta,k) ) = m%BEMT_y%Cd(j,k) +! +! m%AllOuts( BNCpmin( beta,k) ) = m%BEMT_y%Cpmin(j,k) +! m%AllOuts( BNSigCr( beta,k) ) = m%SigmaCavitCrit(j,k) +! m%AllOuts( BNSgCav( beta,k) ) = m%SigmaCavit(j,k) +! +! cp=cos(m%BEMT_y%phi(j,k)) +! sp=sin(m%BEMT_y%phi(j,k)) +! m%AllOuts( BNCl( beta,k) ) = m%BEMT_y%Cx(j,k)*cp + m%BEMT_y%Cy(j,k)*sp +! m%AllOuts( BNCd( beta,k) ) = m%BEMT_y%Cx(j,k)*sp - m%BEMT_y%Cy(j,k)*cp +! m%AllOuts( BNCm( beta,k) ) = m%BEMT_y%Cm(j,k) +! m%AllOuts( BNCx( beta,k) ) = m%BEMT_y%Cx(j,k) +! m%AllOuts( BNCy( beta,k) ) = m%BEMT_y%Cy(j,k) +! +! ct=cos(m%BEMT_u(indx)%theta(j,k)) +! st=sin(m%BEMT_u(indx)%theta(j,k)) +! m%AllOuts( BNCn( beta,k) ) = m%BEMT_y%Cx(j,k)*ct + m%BEMT_y%Cy(j,k)*st +! m%AllOuts( BNCt( beta,k) ) =-m%BEMT_y%Cx(j,k)*st + m%BEMT_y%Cy(j,k)*ct +! +! m%AllOuts( BNFl( beta,k) ) = m%X(j,k)*cp - m%Y(j,k)*sp +! m%AllOuts( BNFd( beta,k) ) = m%X(j,k)*sp + m%Y(j,k)*cp +! m%AllOuts( BNMm( beta,k) ) = m%M(j,k) +! m%AllOuts( BNFx( beta,k) ) = m%X(j,k) +! m%AllOuts( BNFy( beta,k) ) = -m%Y(j,k) +! m%AllOuts( BNFn( beta,k) ) = m%X(j,k)*ct - m%Y(j,k)*st +! m%AllOuts( BNFt( beta,k) ) = -m%X(j,k)*st - m%Y(j,k)*ct +! + end do ! nodes + end do ! blades +! +! ! rotor outputs: +! rmax = 0.0_ReKi +! do k=1,p%NumBlades +! do j=1,p%NumBlNds +! rmax = max(rmax, m%BEMT_u(indx)%rLocal(j,k) ) +! end do !j=nodes +! end do !k=blades +! +! m%AllOuts( RtSpeed ) = m%BEMT_u(indx)%omega*RPS2RPM +! m%AllOuts( RtArea ) = pi*rmax**2 +! +! tmp = matmul( u%HubMotion%Orientation(:,:,1), m%V_DiskAvg ) +! m%AllOuts( RtVAvgxh ) = tmp(1) +! m%AllOuts( RtVAvgyh ) = tmp(2) +! m%AllOuts( RtVAvgzh ) = tmp(3) +! +! m%AllOuts( RtSkew ) = m%BEMT_u(indx)%chi0*R2D +! +! ! integrate force/moments over blades by performing mesh transfer to hub point: +! force = 0.0_ReKi +! moment = 0.0_ReKi +! do k=1,p%NumBlades +! call Transfer_Line2_to_Point( y%BladeLoad(k), m%HubLoad, m%B_L_2_H_P(k), ErrStat2, ErrMsg2, u%BladeMotion(k), u%HubMotion ) +! force = force + m%HubLoad%force( :,1) +! moment = moment + m%HubLoad%moment(:,1) +! end do +! tmp = matmul( u%HubMotion%Orientation(:,:,1), force ) +! m%AllOuts( RtAeroFxh ) = tmp(1) +! m%AllOuts( RtAeroFyh ) = tmp(2) +! m%AllOuts( RtAeroFzh ) = tmp(3) +! +! tmp = matmul( u%HubMotion%Orientation(:,:,1), moment ) +! m%AllOuts( RtAeroMxh ) = tmp(1) +! m%AllOuts( RtAeroMyh ) = tmp(2) +! m%AllOuts( RtAeroMzh ) = tmp(3) +! +! m%AllOuts( RtAeroPwr ) = m%BEMT_u(indx)%omega * m%AllOuts( RtAeroMxh ) +! +! +! if ( EqualRealNos( m%V_dot_x, 0.0_ReKi ) ) then +! m%AllOuts( RtTSR ) = 0.0_ReKi +! m%AllOuts( RtAeroCp ) = 0.0_ReKi +! m%AllOuts( RtAeroCq ) = 0.0_ReKi +! m%AllOuts( RtAeroCt ) = 0.0_ReKi +! else +! denom = 0.5*p%AirDens*m%AllOuts( RtArea )*m%V_dot_x**2 +! m%AllOuts( RtTSR ) = m%BEMT_u(indx)%omega * rmax / m%V_dot_x +! +! m%AllOuts( RtAeroCp ) = m%AllOuts( RtAeroPwr ) / (denom * m%V_dot_x) +! m%AllOuts( RtAeroCq ) = m%AllOuts( RtAeroMxh ) / (denom * rmax) +! m%AllOuts( RtAeroCt ) = m%AllOuts( RtAeroFxh ) / denom +! end if + + end subroutine Calc_WriteOutput_FVW END SUBROUTINE Calc_WriteOutput !---------------------------------------------------------------------------------------------------------------------------------- From 64f874466feb79802fb857d29b7a43812fd42442 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 22 Jan 2020 12:37:33 -0700 Subject: [PATCH 052/190] FVW: fix mistake in InflowWind shortcuts I added --- modules/inflowwind/src/IfW_BladedFFWind.f90 | 4 +--- modules/inflowwind/src/IfW_HAWCWind.f90 | 4 +--- modules/inflowwind/src/IfW_TSFFWind.f90 | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/inflowwind/src/IfW_BladedFFWind.f90 b/modules/inflowwind/src/IfW_BladedFFWind.f90 index 224c646b4c..d5cf83db65 100644 --- a/modules/inflowwind/src/IfW_BladedFFWind.f90 +++ b/modules/inflowwind/src/IfW_BladedFFWind.f90 @@ -1605,9 +1605,7 @@ SUBROUTINE IfW_BladedFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, D DO PointNum = 1, NumPoints ! If the position is (0,0,0), assume it was never set and skip calculating - if ( PositionXYZ(1,PointNum) /= 0.0_ReKi .and. & - PositionXYZ(2,PointNum) /= 0.0_ReKi .and. & - PositionXYZ(3,PointNum) /= 0.0_ReKi ) then + if ( TwoNorm(PositionXYZ(1:3,PointNum)) > 0.0_ReKi ) then ! Calculate the velocity for the position Velocity(:,PointNum) = FF_Interp(Time,PositionXYZ(:,PointNum),ParamData,MiscVars,TmpErrStat,TmpErrMsg) diff --git a/modules/inflowwind/src/IfW_HAWCWind.f90 b/modules/inflowwind/src/IfW_HAWCWind.f90 index 2e260f64f7..fabefc0fb3 100644 --- a/modules/inflowwind/src/IfW_HAWCWind.f90 +++ b/modules/inflowwind/src/IfW_HAWCWind.f90 @@ -533,9 +533,7 @@ SUBROUTINE IfW_HAWCWind_CalcOutput(Time, PositionXYZ, p, Velocity, DiskVel, Misc DO PointNum = 1, NumPoints ! If the position is (0,0,0), assume it was never set and skip calculating - if ( PositionXYZ(1,PointNum) /= 0.0_ReKi .and. & - PositionXYZ(2,PointNum) /= 0.0_ReKi .and. & - PositionXYZ(3,PointNum) /= 0.0_ReKi ) then + if ( TwoNorm(PositionXYZ(1:3,PointNum)) > 0.0_ReKi ) then ! Calculate the velocity for the position Velocity(:,PointNum) = FF_Interp(Time,PositionXYZ(:,PointNum),p,MiscVars,TmpErrStat,TmpErrMsg) diff --git a/modules/inflowwind/src/IfW_TSFFWind.f90 b/modules/inflowwind/src/IfW_TSFFWind.f90 index 8da3306b27..567b5114af 100644 --- a/modules/inflowwind/src/IfW_TSFFWind.f90 +++ b/modules/inflowwind/src/IfW_TSFFWind.f90 @@ -643,9 +643,7 @@ SUBROUTINE IfW_TSFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, Disk DO PointNum = 1, NumPoints ! If the position is (0,0,0), assume it was never set and skip calculating - if ( PositionXYZ(1,PointNum) /= 0.0_ReKi .and. & - PositionXYZ(2,PointNum) /= 0.0_ReKi .and. & - PositionXYZ(3,PointNum) /= 0.0_ReKi ) then + if ( TwoNorm(PositionXYZ(1:3,PointNum)) > 0.0_ReKi ) then ! Calculate the velocity for the position Velocity(:,PointNum) = FF_Interp(Time,PositionXYZ(:,PointNum),ParamData,MiscVars,TmpErrStat,TmpErrMsg) From 1c2b04ece959845167d22e37e5fed2abed164383 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 22 Jan 2020 12:54:10 -0700 Subject: [PATCH 053/190] FVW: split SetInputsForBEMT routine into smaller pieces --- modules/aerodyn/src/AeroDyn.f90 | 197 ++++++++++++++++++++------------ 1 file changed, 124 insertions(+), 73 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 4d667f43f3..20dd91b21d 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1392,15 +1392,13 @@ subroutine SetInputsForBEMT(p, u, m, indx, errStat, errMsg) ! local variables real(ReKi) :: x_hat(3) real(ReKi) :: y_hat(3) - real(ReKi) :: z_hat(3) real(ReKi) :: x_hat_disk(3) real(ReKi) :: y_hat_disk(3) real(ReKi) :: z_hat_disk(3) real(ReKi) :: tmp(3) - real(R8Ki) :: theta(3) - real(R8Ki) :: orientation(3,3) - real(R8Ki) :: orientation_nopitch(3,3) real(ReKi) :: tmp_sz, tmp_sz_y + real(R8Ki) :: thetaBladeNds(p%NumBlNds,p%NumBlades) + real(ReKi) :: Azimuth(p%NumBlNds) integer(intKi) :: j ! loop counter for nodes integer(intKi) :: k ! loop counter for blades @@ -1409,34 +1407,13 @@ subroutine SetInputsForBEMT(p, u, m, indx, errStat, errMsg) character(*), parameter :: RoutineName = 'SetInputsForBEMT' - ErrStat = ErrID_None - ErrMsg = "" - - - ! calculate disk-averaged relative wind speed, V_DiskAvg - m%V_diskAvg = 0.0_ReKi - do k=1,p%NumBlades - do j=1,p%NumBlNds - tmp = m%DisturbedInflow(:,j,k) - u%BladeMotion(k)%TranslationVel(:,j) - m%V_diskAvg = m%V_diskAvg + tmp - end do - end do - m%V_diskAvg = m%V_diskAvg / real( p%NumBlades * p%NumBlNds, ReKi ) - - ! orientation vectors: - x_hat_disk = u%HubMotion%Orientation(1,:,1) !actually also x_hat_hub - - m%V_dot_x = dot_product( m%V_diskAvg, x_hat_disk ) + ! Get disk average values and orientations + call DiskAvgValues(p, u, m, x_hat_disk, y_hat_disk, z_hat_disk, Azimuth) + call GeomWithoutSweepPitchTwist(p,u,m,thetaBladeNds,ErrStat,ErrMsg) + if (ErrStat >= AbortErrLev) return + + ! Velocity in disk normal m%BEMT_u(indx)%Un_disk = m%V_dot_x - tmp = m%V_dot_x * x_hat_disk - m%V_diskAvg - tmp_sz = TwoNorm(tmp) - if ( EqualRealNos( tmp_sz, 0.0_ReKi ) ) then - y_hat_disk = u%HubMotion%Orientation(2,:,1) - z_hat_disk = u%HubMotion%Orientation(3,:,1) - else - y_hat_disk = tmp / tmp_sz - z_hat_disk = cross_product( m%V_diskAvg, x_hat_disk ) / tmp_sz - end if ! "Angular velocity of rotor" rad/s m%BEMT_u(indx)%omega = dot_product( u%HubMotion%RotationVel(:,1), x_hat_disk ) @@ -1455,54 +1432,15 @@ subroutine SetInputsForBEMT(p, u, m, indx, errStat, errMsg) end if ! "Azimuth angle" rad - do k=1,p%NumBlades - z_hat = u%BladeRootMotion(k)%Orientation(3,:,1) - tmp_sz_y = -1.0*dot_product(z_hat,y_hat_disk) - tmp_sz = dot_product(z_hat,z_hat_disk) - if ( EqualRealNos(tmp_sz_y,0.0_ReKi) .and. EqualRealNos(tmp_sz,0.0_ReKi) ) then - m%BEMT_u(indx)%psi(k) = 0.0_ReKi - else - m%BEMT_u(indx)%psi(k) = atan2( tmp_sz_y, tmp_sz ) - end if - end do - + m%bemt_u(indx)%psi = Azimuth + ! theta, "Twist angle (includes all sources of twist)" rad ! Vx, "Local axial velocity at node" m/s ! Vy, "Local tangential velocity at node" m/s do k=1,p%NumBlades - - ! construct system equivalent to u%BladeRootMotion(k)%Orientation, but without the blade-pitch angle: - - !orientation = matmul( u%BladeRootMotion(k)%Orientation(:,:,1), transpose(u%HubMotion%Orientation(:,:,1)) ) - call LAPACK_gemm( 'n', 't', 1.0_R8Ki, u%BladeRootMotion(k)%Orientation(:,:,1), u%HubMotion%Orientation(:,:,1), 0.0_R8Ki, orientation, errStat2, errMsg2) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - theta = EulerExtract( orientation ) !hub_theta_root(k) -#ifndef DBG_OUTS - m%AllOuts( BPitch( k) ) = -theta(3)*R2D ! save this value of pitch for potential output -#endif - theta(3) = 0.0_ReKi - m%hub_theta_x_root(k) = theta(1) ! save this value for FAST.Farm - - orientation = EulerConstruct( theta ) - orientation_nopitch = matmul( orientation, u%HubMotion%Orientation(:,:,1) ) ! withoutPitch_theta_Root(k) - do j=1,p%NumBlNds - ! form coordinate system equivalent to u%BladeMotion(k)%Orientation(:,:,j) but without live sweep (due to in-plane - ! deflection), blade-pitch and twist (aerodynamic + elastic) angles: - - ! orientation = matmul( u%BladeMotion(k)%Orientation(:,:,j), transpose(orientation_nopitch) ) - call LAPACK_gemm( 'n', 't', 1.0_R8Ki, u%BladeMotion(k)%Orientation(:,:,j), orientation_nopitch, 0.0_R8Ki, orientation, errStat2, errMsg2) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - theta = EulerExtract( orientation ) !root(k)WithoutPitch_theta(j)_blade(k) - - m%BEMT_u(indx)%theta(j,k) = -theta(3) ! local pitch + twist (aerodyanmic + elastic) angle of the jth node in the kth blade - - - theta(1) = 0.0_ReKi - theta(3) = 0.0_ReKi - m%Curve(j,k) = theta(2) ! save value for possible output later - m%WithoutSweepPitchTwist(:,:,j,k) = matmul( EulerConstruct( theta ), orientation_nopitch ) ! WithoutSweepPitch+Twist_theta(j)_Blade(k) + m%BEMT_u(indx)%theta(j,k) = thetaBladeNds(j,k) ! local pitch + twist (aerodyanmic + elastic) angle of the jth node in the kth blade x_hat = m%WithoutSweepPitchTwist(1,:,j,k) y_hat = m%WithoutSweepPitchTwist(2,:,j,k) @@ -1536,6 +1474,116 @@ subroutine SetInputsForBEMT(p, u, m, indx, errStat, errMsg) end subroutine SetInputsForBEMT +subroutine DiskAvgValues(p, u, m, x_hat_disk, y_hat_disk, z_hat_disk, Azimuth) + type(AD_ParameterType), intent(in ) :: p !< AD parameters + type(AD_InputType), intent(in ) :: u !< AD Inputs at Time + type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables + real(ReKi), intent( out) :: x_hat_disk(3) + real(ReKi), intent( out) :: y_hat_disk(3) + real(ReKi), intent( out) :: z_hat_disk(3) + real(R8Ki), intent( out) :: Azimuth(p%NumBlNds) + real(ReKi) :: z_hat(3) + real(ReKi) :: tmp(3) + real(ReKi) :: tmp_sz, tmp_sz_y + integer(intKi) :: j ! loop counter for nodes + integer(intKi) :: k ! loop counter for blades + + ! calculate disk-averaged relative wind speed, V_DiskAvg + m%V_diskAvg = 0.0_ReKi + do k=1,p%NumBlades + do j=1,p%NumBlNds + tmp = m%DisturbedInflow(:,j,k) - u%BladeMotion(k)%TranslationVel(:,j) + m%V_diskAvg = m%V_diskAvg + tmp + end do + end do + m%V_diskAvg = m%V_diskAvg / real( p%NumBlades * p%NumBlNds, ReKi ) + + ! orientation vectors: + x_hat_disk = u%HubMotion%Orientation(1,:,1) !actually also x_hat_hub + + m%V_dot_x = dot_product( m%V_diskAvg, x_hat_disk ) + tmp = m%V_dot_x * x_hat_disk - m%V_diskAvg + tmp_sz = TwoNorm(tmp) + if ( EqualRealNos( tmp_sz, 0.0_ReKi ) ) then + y_hat_disk = u%HubMotion%Orientation(2,:,1) + z_hat_disk = u%HubMotion%Orientation(3,:,1) + else + y_hat_disk = tmp / tmp_sz + z_hat_disk = cross_product( m%V_diskAvg, x_hat_disk ) / tmp_sz + end if + + ! "Azimuth angle" rad + do k=1,p%NumBlades + z_hat = u%BladeRootMotion(k)%Orientation(3,:,1) + tmp_sz_y = -1.0*dot_product(z_hat,y_hat_disk) + tmp_sz = dot_product(z_hat,z_hat_disk) + if ( EqualRealNos(tmp_sz_y,0.0_ReKi) .and. EqualRealNos(tmp_sz,0.0_ReKi) ) then + Azimuth(k) = 0.0_ReKi + else + Azimuth(k) = atan2( tmp_sz_y, tmp_sz ) + end if + end do +end subroutine DiskAvgValues +subroutine GeomWithoutSweepPitchTwist(p,u,m,thetaBladeNds,ErrStat,ErrMsg) + type(AD_ParameterType), intent(in ) :: p !< AD parameters + type(AD_InputType), intent(in ) :: u !< AD Inputs at Time + type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables + real(R8Ki), intent( out) :: thetaBladeNds(p%NumBlNds,p%NumBlades) + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + real(R8Ki) :: theta(3) + real(R8Ki) :: orientation(3,3) + real(R8Ki) :: orientation_nopitch(3,3) + + integer(intKi) :: j ! loop counter for nodes + integer(intKi) :: k ! loop counter for blades + integer(intKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'GeomWithoutSweepPitchTwist' + + ErrStat = ErrID_None + ErrMsg = "" + + ! theta, "Twist angle (includes all sources of twist)" rad + ! Vx, "Local axial velocity at node" m/s + ! Vy, "Local tangential velocity at node" m/s + do k=1,p%NumBlades + + ! construct system equivalent to u%BladeRootMotion(k)%Orientation, but without the blade-pitch angle: + + call LAPACK_gemm( 'n', 't', 1.0_R8Ki, u%BladeRootMotion(k)%Orientation(:,:,1), u%HubMotion%Orientation(:,:,1), 0.0_R8Ki, orientation, errStat2, errMsg2) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + theta = EulerExtract( orientation ) !hub_theta_root(k) +#ifndef DBG_OUTS + m%AllOuts( BPitch( k) ) = -theta(3)*R2D ! save this value of pitch for potential output +#endif + theta(3) = 0.0_ReKi + m%hub_theta_x_root(k) = theta(1) ! save this value for FAST.Farm + + orientation = EulerConstruct( theta ) + orientation_nopitch = matmul( orientation, u%HubMotion%Orientation(:,:,1) ) ! withoutPitch_theta_Root(k) + + do j=1,p%NumBlNds + + ! form coordinate system equivalent to u%BladeMotion(k)%Orientation(:,:,j) but without live sweep (due to in-plane + ! deflection), blade-pitch and twist (aerodynamic + elastic) angles: + + ! orientation = matmul( u%BladeMotion(k)%Orientation(:,:,j), transpose(orientation_nopitch) ) + call LAPACK_gemm( 'n', 't', 1.0_R8Ki, u%BladeMotion(k)%Orientation(:,:,j), orientation_nopitch, 0.0_R8Ki, orientation, errStat2, errMsg2) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + theta = EulerExtract( orientation ) !root(k)WithoutPitch_theta(j)_blade(k) + + thetaBladeNds(j,k) = -theta(3) ! local pitch + twist (aerodyanmic + elastic) angle of the jth node in the kth blade + + + theta(1) = 0.0_ReKi + theta(3) = 0.0_ReKi + m%Curve(j,k) = theta(2) ! save value for possible output later + m%WithoutSweepPitchTwist(:,:,j,k) = matmul( EulerConstruct( theta ), orientation_nopitch ) ! WithoutSweepPitch+Twist_theta(j)_Blade(k) + + end do !j=nodes + end do !k=blades +end subroutine GeomWithoutSweepPitchTwist !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine sets m%FVW_u(indx). subroutine SetInputsForFVW(p, u, m, errStat, errMsg) @@ -1546,6 +1594,9 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None +! real(ReKi) :: x_hat_disk(3) +! real(ReKi) :: y_hat_disk(3) +! real(ReKi) :: z_hat_disk(3) integer(intKi) :: tIndx integer(intKi) :: k ! loop counter for blades integer(intKi) :: ErrStat2 From e033a88451e9005f569efe7678ee7bab8ecfabab Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 24 Jan 2020 11:04:34 -0700 Subject: [PATCH 054/190] FVW: start of updates for outputs -- incomplete. --- modules/aerodyn/src/AeroDyn.f90 | 64 +++++++++--- modules/aerodyn/src/AeroDyn_IO.f90 | 147 +++++++++++++-------------- modules/aerodyn/src/FVW.f90 | 32 +++--- modules/aerodyn/src/FVW_Registry.txt | 5 +- modules/aerodyn/src/FVW_Subs.f90 | 97 ++++++++++++++++++ modules/aerodyn/src/FVW_Types.f90 | 79 ++++++++++++++ modules/aerodyn/src/FVW_Wings.f90 | 17 +++- 7 files changed, 328 insertions(+), 113 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 20dd91b21d..c9b90756a9 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -29,6 +29,7 @@ module AeroDyn use NWTC_LAPACK use UnsteadyAero use FVW + use FVW_Subs, only: FVW_AeroOuts implicit none @@ -1594,9 +1595,16 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None -! real(ReKi) :: x_hat_disk(3) -! real(ReKi) :: y_hat_disk(3) -! real(ReKi) :: z_hat_disk(3) +! real(ReKi) :: x_hat(3) +! real(ReKi) :: y_hat(3) + real(ReKi) :: x_hat_disk(3) + real(ReKi) :: y_hat_disk(3) + real(ReKi) :: z_hat_disk(3) +! real(ReKi) :: tmp(3) +! real(ReKi) :: tmp_sz, tmp_sz_y + real(R8Ki) :: thetaBladeNds(p%NumBlNds,p%NumBlades) + real(ReKi) :: Azimuth(p%NumBlNds) + integer(intKi) :: tIndx integer(intKi) :: k ! loop counter for blades integer(intKi) :: ErrStat2 @@ -1604,6 +1612,11 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) character(*), parameter :: RoutineName = 'SetInputsForFVW' do tIndx=1,size(u) + ! Get disk average values and orientations + call DiskAvgValues(p, u(tIndx), m, x_hat_disk, y_hat_disk, z_hat_disk, Azimuth) + call GeomWithoutSweepPitchTwist(p,u(tIndx),m,thetaBladeNds,ErrStat,ErrMsg) + if (ErrStat >= AbortErrLev) return + ! Rather than use a meshcopy, we will just copy what we need to the WingsMesh ! NOTE: MeshCopy requires the source mesh to be INOUT intent ! NOTE2: If we change the WingsMesh to not be identical to the BladeMotion mesh, add the mapping stuff here. @@ -1680,32 +1693,50 @@ subroutine SetOutputsFromFVW(p, m, y) real(reki) :: moment(3) real(reki) :: q +!TODO: Manu!!!! +!TODO: how are we setting the loads at the nodes for coupling to OpenFAST???? +!NOTE: size of _y%c[xym] is 1:p%NumBldNds-1 force(3) = 0.0_ReKi moment(1:2) = 0.0_ReKi +!TODO: Our outputs are on the lifting line! Not at blade nodes! do k=1,p%NumBlades - do j=1,p%NumBlNds -! q = 0.5 * p%airDens * m%BEMT_y%Vrel(j,k)**2 ! dynamic pressure of the jth node in the kth blade -! force(1) = m%BEMT_y%cx(j,k) * q * p%BEMT%chord(j,k) ! X = normal force per unit length (normal to the plane, not chord) of the jth node in the kth blade -! force(2) = -m%BEMT_y%cy(j,k) * q * p%BEMT%chord(j,k) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade -! moment(3)= m%BEMT_y%cm(j,k) * q * p%BEMT%chord(j,k)**2 ! M = pitching moment per unit length of the jth node in the kth blade + do j=2,p%NumBlNds + +!TODO: populate this with what we need. +! call FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, KinVisc, Chord, & +! AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) +! +! call AirFoilInfo here +! with Cl Cd etc, calculate the values for the Cn Ct Cx Cy etc. Then get forces. + +!FIXME: calculate Cx Cy Cm here. So put Airfoil Coeff calcs here. +! q = 0.5 * p%airDens * Vrel(j-1,k)**2 ! dynamic pressure of the jth node in the kth blade +! force(1) = cx(j-1,k) * q * p%FVW%chord(j,k) ! X = normal force per unit length (normal to the plane, not chord) of the jth node in the kth blade +! force(2) = -cy(j-1,k) * q * p%FVW%chord(j,k) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade +! moment(3)= cm(j-1,k) * q * p%FVW%chord(j,k)**2 ! M = pitching moment per unit length of the jth node in the kth blade end do !j=nodes end do !k=blades +!Keep this part V + +!TODO: Our outputs are on the lifting line! Not at blade nodes! do k=1,p%NumBlades - do j=1,p%NumBlNds -! ! save these values for possible output later: -! m%X(j,k) = force(1) -! m%Y(j,k) = force(2) -! m%M(j,k) = moment(3) + do j=2,p%NumBlNds + ! save these values for possible output later: + m%X(j,k) = force(1) + m%Y(j,k) = force(2) + m%M(j,k) = moment(3) end do !j=nodes end do !k=blades +!TODO: Manu!!!! +!TODO: Our outputs are on the lifting line! Not at blade nodes! The following is not really correct! Some kind of mapping should be done. do k=1,p%NumBlades - do j=1,p%NumBlNds + do j=2,p%NumBlNds ! note: because force and moment are 1-d arrays, I'm calculating the transpose of the force and moment outputs ! so that I don't have to take the transpose of WithoutSweepPitchTwist(:,:,j,k) -! y%BladeLoad(k)%Force(:,j) = matmul( force, m%WithoutSweepPitchTwist(:,:,j,k) ) ! force per unit length of the jth node in the kth blade -! y%BladeLoad(k)%Moment(:,j) = matmul( moment, m%WithoutSweepPitchTwist(:,:,j,k) ) ! moment per unit length of the jth node in the kth blade + y%BladeLoad(k)%Force(:,j) = matmul( force, m%WithoutSweepPitchTwist(:,:,j,k) ) ! force per unit length of the jth node in the kth blade + y%BladeLoad(k)%Moment(:,j) = matmul( moment, m%WithoutSweepPitchTwist(:,:,j,k) ) ! moment per unit length of the jth node in the kth blade end do !j=nodes end do !k=blades @@ -2192,6 +2223,7 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m InitInp%numBlades = p%numBlades InitInp%numBladeNodes = p%numBlNds InitInp%DT = p%DT ! NOTE: if we subcycle FVW, this will need modification + InitInp%KinVisc = p%KinVisc ! NOTE: The following are not meshes ! It's just the spanwise location. diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 9c59728244..060e538ab2 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1586,7 +1586,53 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) end do ! nodes end do ! blades else ! (p%WakeMod == WakeMod_FVW) - m%AllOuts = 0.0_ReKi + ! blade outputs + do k=1,p%numBlades + do j=2,p%NumBlNds ! NOTE: the LL spans are NumBldNds-1 long +!TODO: FVW -- put all the ugly mappings here? + + i = (k-1)*p%NumBlNds*23 + (j-1)*23 + 1 +!FVW_AeroOuts(MGlobalToSection, HubOrient, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, & +! Node, Blade, KinVisc, Chord, AFInfo, & +! CldAirfoil, Cm, CpMin, & ! CxySection, CntDisk, & +! AxInd, TanInd, Vrel, phi, alpha, Re, & +! ErrStat, ErrMsg ) + +! m%AllOuts( i ) = m%FVW_u(indx)%theta(j-1,k)*R2D +! m%AllOuts( i+1 ) = m%FVW_u(indx)%psi(k)*R2D +! m%AllOuts( i+2 ) = -m%FVW_u(indx)%Vx(j-1,k) +! m%AllOuts( i+3 ) = m%FVW_u(indx)%Vy(j-1,k) + +! m%AllOuts( i+4 ) = m%FVW_y%axInd(j-1,k) +! m%AllOuts( i+5 ) = m%FVW_y%tanInd(j-1,k) +! m%AllOuts( i+6 ) = m%FVW_y%Vrel(j-1,k) +! m%AllOuts( i+7 ) = m%FVW_y%phi(j-1,k)*R2D +! m%AllOuts( i+8 ) = (m%FVW_y%phi(j-1,k) - m%BEMT_u(indx)%theta(j-1,k))*R2D +! m%AllOuts( i+8 ) = m%FVW_y%alpha(j-1,k)*R2D +! +! m%AllOuts( i+9 ) = m%FVW_y%Cl(j-1,k) +! m%AllOuts( i+10 ) = m%FVW_y%Cd(j-1,k) +! m%AllOuts( i+11 ) = m%FVW_y%Cm(j-1,k) +! m%AllOuts( i+12 ) = m%FVW_y%Cx(j-1,k) +! m%AllOuts( i+13 ) = m%FVW_y%Cy(j-1,k) + +! ct=cos(m%FVW_u(indx)%theta(j,k)) +! st=sin(m%FVW_u(indx)%theta(j,k)) +! m%AllOuts( i+14 ) = m%FVW_y%Cx(j,k)*ct + m%BEMT_y%Cy(j,k)*st +! m%AllOuts( i+15 ) = -m%FVW_y%Cx(j,k)*st + m%BEMT_y%Cy(j,k)*ct + +! cp=cos(m%FVW_y%phi(j,k)) +! sp=sin(m%FVW_y%phi(j,k)) +! m%AllOuts( i+16 ) = m%X(j,k)*cp - m%Y(j,k)*sp +! m%AllOuts( i+17 ) = m%X(j,k)*sp + m%Y(j,k)*cp +! m%AllOuts( i+18 ) = m%M(j,k) +! m%AllOuts( i+19 ) = m%X(j,k) +! m%AllOuts( i+20 ) = -m%Y(j,k) +! m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st +! m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct + + end do ! nodes + end do ! blades endif END SUBROUTINE Calc_WriteDbgOutput @@ -1790,72 +1836,15 @@ end subroutine Calc_WriteOutput_BEMT subroutine Calc_WriteOutput_FVW ! blade outputs do k=1,p%numBlades -! m%AllOuts( BAzimuth(k) ) = m%BEMT_u(indx)%psi(k)*R2D -! ! m%AllOuts( BPitch( k) ) = calculated in SetInputsForBEMT -! do beta=1,p%NBlOuts + j=p%BlOutNd(beta) +!TODO: populate this with what we need. +! call FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, KinVisc, Chord, & +! AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) ! -! j=p%BlOutNd(beta) -! -! tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%InflowOnBlade(:,j,k) ) -! m%AllOuts( BNVUndx(beta,k) ) = tmp(1) -! m%AllOuts( BNVUndy(beta,k) ) = tmp(2) -! m%AllOuts( BNVUndz(beta,k) ) = tmp(3) -! -! tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), m%DisturbedInflow(:,j,k) ) -! m%AllOuts( BNVDisx(beta,k) ) = tmp(1) -! m%AllOuts( BNVDisy(beta,k) ) = tmp(2) -! m%AllOuts( BNVDisz(beta,k) ) = tmp(3) -! -! tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%TranslationVel(:,j) ) -! m%AllOuts( BNSTVx( beta,k) ) = tmp(1) -! m%AllOuts( BNSTVy( beta,k) ) = tmp(2) -! m%AllOuts( BNSTVz( beta,k) ) = tmp(3) -! -! m%AllOuts( BNVrel( beta,k) ) = m%BEMT_y%Vrel(j,k) -! m%AllOuts( BNDynP( beta,k) ) = 0.5 * p%airDens * m%BEMT_y%Vrel(j,k)**2 -! m%AllOuts( BNRe( beta,k) ) = p%BEMT%chord(j,k) * m%BEMT_y%Vrel(j,k) / p%KinVisc / 1.0E6 -! m%AllOuts( BNM( beta,k) ) = m%BEMT_y%Vrel(j,k) / p%SpdSound -! -! m%AllOuts( BNVIndx(beta,k) ) = - m%BEMT_u(indx)%Vx(j,k) * m%BEMT_y%axInduction( j,k) -! m%AllOuts( BNVIndy(beta,k) ) = m%BEMT_u(indx)%Vy(j,k) * m%BEMT_y%tanInduction(j,k) -! -! m%AllOuts( BNAxInd(beta,k) ) = m%BEMT_y%axInduction(j,k) -! m%AllOuts( BNTnInd(beta,k) ) = m%BEMT_y%tanInduction(j,k) -! -! m%AllOuts( BNAlpha(beta,k) ) = Rad2M180to180Deg( m%BEMT_y%phi(j,k) - m%BEMT_u(indx)%theta(j,k) ) -! m%AllOuts( BNTheta(beta,k) ) = m%BEMT_u(indx)%theta(j,k)*R2D -! m%AllOuts( BNPhi( beta,k) ) = m%BEMT_y%phi(j,k)*R2D -! m%AllOuts( BNCurve(beta,k) ) = m%Curve(j,k)*R2D -! -! !m%AllOuts( BNCl( beta,k) ) = m%BEMT_y%Cl(j,k) -! !m%AllOuts( BNCd( beta,k) ) = m%BEMT_y%Cd(j,k) -! -! m%AllOuts( BNCpmin( beta,k) ) = m%BEMT_y%Cpmin(j,k) -! m%AllOuts( BNSigCr( beta,k) ) = m%SigmaCavitCrit(j,k) -! m%AllOuts( BNSgCav( beta,k) ) = m%SigmaCavit(j,k) -! -! cp=cos(m%BEMT_y%phi(j,k)) -! sp=sin(m%BEMT_y%phi(j,k)) -! m%AllOuts( BNCl( beta,k) ) = m%BEMT_y%Cx(j,k)*cp + m%BEMT_y%Cy(j,k)*sp -! m%AllOuts( BNCd( beta,k) ) = m%BEMT_y%Cx(j,k)*sp - m%BEMT_y%Cy(j,k)*cp -! m%AllOuts( BNCm( beta,k) ) = m%BEMT_y%Cm(j,k) -! m%AllOuts( BNCx( beta,k) ) = m%BEMT_y%Cx(j,k) -! m%AllOuts( BNCy( beta,k) ) = m%BEMT_y%Cy(j,k) -! -! ct=cos(m%BEMT_u(indx)%theta(j,k)) -! st=sin(m%BEMT_u(indx)%theta(j,k)) -! m%AllOuts( BNCn( beta,k) ) = m%BEMT_y%Cx(j,k)*ct + m%BEMT_y%Cy(j,k)*st -! m%AllOuts( BNCt( beta,k) ) =-m%BEMT_y%Cx(j,k)*st + m%BEMT_y%Cy(j,k)*ct -! -! m%AllOuts( BNFl( beta,k) ) = m%X(j,k)*cp - m%Y(j,k)*sp -! m%AllOuts( BNFd( beta,k) ) = m%X(j,k)*sp + m%Y(j,k)*cp -! m%AllOuts( BNMm( beta,k) ) = m%M(j,k) -! m%AllOuts( BNFx( beta,k) ) = m%X(j,k) -! m%AllOuts( BNFy( beta,k) ) = -m%Y(j,k) -! m%AllOuts( BNFn( beta,k) ) = m%X(j,k)*ct - m%Y(j,k)*st -! m%AllOuts( BNFt( beta,k) ) = -m%X(j,k)*st - m%Y(j,k)*ct -! +! call AirFoilInfo here +! with Cl Cd etc, calculate the values for the Cn Ct Cx Cy etc. Then get forces. + end do ! nodes end do ! blades ! @@ -1876,20 +1865,20 @@ subroutine Calc_WriteOutput_FVW ! m%AllOuts( RtVAvgzh ) = tmp(3) ! ! m%AllOuts( RtSkew ) = m%BEMT_u(indx)%chi0*R2D -! -! ! integrate force/moments over blades by performing mesh transfer to hub point: -! force = 0.0_ReKi -! moment = 0.0_ReKi -! do k=1,p%NumBlades -! call Transfer_Line2_to_Point( y%BladeLoad(k), m%HubLoad, m%B_L_2_H_P(k), ErrStat2, ErrMsg2, u%BladeMotion(k), u%HubMotion ) -! force = force + m%HubLoad%force( :,1) -! moment = moment + m%HubLoad%moment(:,1) -! end do -! tmp = matmul( u%HubMotion%Orientation(:,:,1), force ) -! m%AllOuts( RtAeroFxh ) = tmp(1) -! m%AllOuts( RtAeroFyh ) = tmp(2) -! m%AllOuts( RtAeroFzh ) = tmp(3) -! + + ! integrate force/moments over blades by performing mesh transfer to hub point: + force = 0.0_ReKi + moment = 0.0_ReKi + do k=1,p%NumBlades + call Transfer_Line2_to_Point( y%BladeLoad(k), m%HubLoad, m%B_L_2_H_P(k), ErrStat2, ErrMsg2, u%BladeMotion(k), u%HubMotion ) + force = force + m%HubLoad%force( :,1) + moment = moment + m%HubLoad%moment(:,1) + end do + tmp = matmul( u%HubMotion%Orientation(:,:,1), force ) + m%AllOuts( RtAeroFxh ) = tmp(1) + m%AllOuts( RtAeroFyh ) = tmp(2) + m%AllOuts( RtAeroFzh ) = tmp(3) + ! tmp = matmul( u%HubMotion%Orientation(:,:,1), moment ) ! m%AllOuts( RtAeroMxh ) = tmp(1) ! m%AllOuts( RtAeroMyh ) = tmp(2) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index a5591a0b1d..6d958ca471 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -45,7 +45,6 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu type(FVW_DiscreteStateType), intent( out) :: xd !< Initial discrete states type(FVW_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states type(FVW_OtherStateType), intent( out) :: OtherState !< Initial other states -! type(AFI_ParameterType), intent(in ) :: AFInfo(:) ! The airfoil parameter data type(FVW_OutputType), intent( out) :: y !< Initial system outputs (outputs are not calculated; !! only the output mesh is initialized) type(FVW_MiscVarType), intent( out) :: m !< Initial misc/optimization variables @@ -112,7 +111,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu endif ! This mesh is passed in as a cousin of the BladeMotion mesh. - CALL Wings_Panelling_Init(u%WingsMesh, InitInp%zLocal, InitInp%chord, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL Wings_Panelling_Init(u%WingsMesh, InitInp%zLocal, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Set parameters from InputFileData (need Misc allocated) CALL FVW_SetParametersFromInputFile(InputFileData, p, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -248,7 +247,7 @@ subroutine FVW_Init_Y( p, u, y, ErrStat, ErrMsg ) integer(IntKi) :: nMax ! Total number of wind points possible integer(IntKi) :: ErrStat2 ! temporary error status of the operation character(ErrMsgLen) :: ErrMsg2 ! temporary error message - character(*), parameter :: RoutineName = 'FVW_InitMiscVars' + character(*), parameter :: RoutineName = 'FVW_Init_Y' ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -258,11 +257,11 @@ subroutine FVW_Init_Y( p, u, y, ErrStat, ErrMsg ) nMax = nMax + (p%nSpan+1) * (p%nNWMax+1) * p%nWings ! Nearwake points nMax = nMax + (FWnSpan+1) * (p%nFWMax+1) * p%nWings ! Far wake points - call AllocAry( u%V_wind, 3, nMax, 'Wind Velocity at points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) + call AllocAry( u%V_wind, 3, nMax, 'Wind Velocity at points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) !call AllocAry( y%Vind , 3, p%nSpan+1, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO potentially nSpan+1 for AD15 - call AllocAry( y%Vind , 3, p%nSpan+1, 3, 'Induced velocity vector', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) ! NOTE: temporary hack 3 blades + call AllocAry( y%Vind , 3, p%nSpan+1, 3, 'Induced velocity vector', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) ! NOTE: temporary hack 3 blades !FIXME: TODO: allocate y%Cl_KJ (2d). Placeholder for now - call AllocAry(y%Cl_KJ, 1, 1, 'Lift coefficient from circulation (Kutta-Joukowski)', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_Init_Y' ) + call AllocAry(y%Cl_KJ , 1, 1, 'Lift coefficient from circulation (Kutta-Joukowski)', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) if (ErrStat >= AbortErrLev) return y%Vind = 0.0_ReKi return @@ -294,10 +293,15 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) ! Set time step p%DT = InitInp%DT + ! Kinematic air viscosity + p%KinVisc = InitInp%KinVisc + ! Set indexing to AFI tables -- this is set from the AD15 calling code. call AllocAry(p%AFindx,size(InitInp%AFindx,1),size(InitInp%AFindx,2),'AFindx',ErrStat,ErrMsg) p%AFindx = InitInp%AFindx ! Copying in case AD15 still needs these + ! Set the Chord values + call move_alloc(InitInp%Chord, p%Chord) end subroutine FVW_SetParametersFromInputs ! ============================================================================== @@ -637,11 +641,10 @@ subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, type(FVW_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) type(FVW_ConstraintStateType), intent( out) :: z_out !< Residual of the constraint state functions using type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data + integer(IntKi), intent(in ) :: iLabel + integer(IntKi), intent( OUT) :: ErrStat !< Error status of the operation + character(*), intent( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None - integer(IntKi), intent(in) :: iLabel - !! the input values described above - integer(IntKi), intent( OUT) :: ErrStat !< Error status of the operation - character(*), intent( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -690,12 +693,12 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, print'(A,F10.3,A,L1,A,I0,A,I0)','CalcOutput t:',t,' ',m%FirstCall,' nNW:',m%nNW,' nFW:',m%nFW - ! if we are on a correction step, CalcOutput may be called again with different inputs - CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, AFInfo, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only - ! Set the wind velocity at vortex CALL DistributeRequestedWind(u%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return + ! if we are on a correction step, CalcOutput may be called again with different inputs + CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, AFInfo, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only + ! Induction on the lifting line control point ! Set m%Vind_LL @@ -703,9 +706,12 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, call LiftingLineInducedVelocities(p, x, 1, m, ErrStat2, ErrMsg2); if(Failed()) return ! Interpolation to AeroDyn radial station TODO TODO TODO +!TODO: interpolate Vind to AD discretization +! Cl, Cd, Cm y%Vind(1:3,:,:) = 0.0_ReKi do iW=1,p%nWings do iSpan=1,p%nSpan +!Interpolate here y%Vind(1:3,iSpan,iW) = m%Vind_LL(1:3,iSpan,iW) enddo enddo diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index db5ccd7f3d..e842646427 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -4,7 +4,7 @@ # keyword "" "" ################################################################################################################################## include Registry_NWTC_Library.txt -usefrom AirfoilInfo_Registry.txt +usefrom AirfoilInfo_Registry.txt ##################### Registry for FVW ############### # ..... PARAMETERS ............. @@ -12,6 +12,7 @@ usefrom AirfoilInfo_Registry.txt typedef FVW/FVW ParameterType IntKi nWings - - - "Number of Wings" - typedef ^ ^ IntKi nSpan - - - "TODO, should be defined per wing. Number of spanwise element" - typedef ^ ^ IntKi AFindx :: - - "Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number]" - +typedef ^ ^ ReKi Chord :: - - "Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number]" - typedef ^ ^ IntKi nNWMax - - - "Maximum number of nw panels, per wing" - typedef ^ ^ IntKi nFWMax - - - "Maximum number of fw panels, per wing" - typedef ^ ^ IntKi nFWFree - - - "Number of fw panels that are free, per wing" - @@ -32,6 +33,7 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ IntKi HACK - - - "HACK ID" - typedef ^ ^ DbKi DT - - - "Time interval for calls calculations" s +typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s # ....... OtherStateType ............ # FVW_OtherStateType @@ -112,6 +114,7 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi NumBlades - - - "Number of blades" - typedef ^ ^ IntKi NumBladeNodes - - - "Number of nodes on each blade" - typedef ^ ^ DbKi DT - - - "Time interval for calls (from AD15)" s +typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s #.......... InputFileType ...... # FVW_InputFile diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 178430e74d..0c6c4abc68 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -4,6 +4,7 @@ module FVW_SUBS use FVW_TYPES use FVW_VortexTools use FVW_BiotSavart + use AirFoilInfo, only: AFI_ComputeAirfoilCoefs implicit none @@ -644,5 +645,101 @@ subroutine UnPackLiftingLineVelocities() end subroutine +subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, KinVisc, Chord, & + AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) + real(ReKi), intent(in ) :: MGlobalToSection(3,3) ! m%WithoutSweepPitchTwist(:,:,j,k) global coord + real(ReKi), intent(in ) :: NodeOrient(3,3) ! u%BladeMotion(k)%Orientation(1:3,1:3,j) global coord + real(ReKi), intent(in ) :: StructVel(3) ! Structural velocity global coord + real(ReKi), intent(in ) :: Vind_Glob(3) ! Induced wind velocity global coord + real(ReKi), intent(in ) :: DisturbedInflow(3) ! Disturbed inflow global coord + + real(ReKi), intent(in ) :: KinVisc ! Viscosity + real(ReKi), intent(in ) :: Chord ! chord length + real(ReKi), intent( out) :: AxInd ! axial induction + real(ReKi), intent( out) :: TanInd ! Tangential induction + real(ReKi), intent( out) :: Vrel ! Relative velocity (scalar) + real(Reki), intent( out) :: phi ! Flow angle + real(Reki), intent( out) :: alpha ! angle of attack + real(ReKi), intent( out) :: Re ! Reynolds number + + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + + ! Local vars + real(ReKi) :: x_hat(3) ! Vector in airfoil cross section, flapwise direction global coord + real(ReKi) :: y_hat(3) ! Vector in airfoil cross section, chordwise direction global coord + real(ReKi) :: VStruct(3) ! Struct Velocity, section coord + real(ReKi) :: VInd(3) ! Induced Velocity, section coord + real(ReKi) :: UWind(3) ! Disturbed wind velocity, section coord + real(ReKi) :: U0(3) ! Vector of UWidn - VStruct section coord + real(ReKi) :: UTotLocal(3) ! Vector of total relative velocity section coord + real(ReKi) :: UTotGlobal(3) ! Vector of total relative velocity global coord + real(ReKi) :: UTotAirfoil(3) ! Vector of total relative velocity airfoil coord + type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table + + x_hat = MGlobalToSection(1,:) ! ADP: We want this Airfoil cross section flapwise. Is this it??? + y_hat = MGlobalToSection(2,:) ! ADP: We want this Airfoil cross section chordwise + + ! Section coordinates + VStruct(1)=dot_product(x_hat, StructVel) + VStruct(2)=dot_product(y_hat, StructVel) + VStruct(3)=0.0_ReKi + + ! Section coordinates + VInd(1)=dot_product(Vind_Glob(1:3),x_hat) + VInd(2)=dot_product(Vind_Glob(1:3),y_hat) + Vind(3)=0.0_ReKi + + ! disturbed wind in section coords + UWind(1) =dot_product( DisturbedInflow(:), x_hat) + UWind(2) =dot_product( DisturbedInflow(:), y_hat) + UWind(3) = 0.0_ReKi + + U0 = UWind - VStruct +!FIXME: are these calcs correct? Expect scalar output. + AxInd = norm2(VInd) /U0(1) ! What is the sign here???? + TanInd = norm2(VInd) /U0(2) + + ! Section coordinates + UTotLocal = UWind - VStruct + Vind + Vrel = norm2(UTotLocal) + + phi = atan2( UTotLocal(1), UTotLocal(2) ) ! flow angle + + UTotGlobal = DisturbedInflow(:) - StructVel + Vind(1:3) + UTotAirfoil = matmul(UTotGlobal, NodeOrient ) ! Airfoil coord +!TODO: is there a +/- error here? + alpha = atan2( UTotAirfoil(1), UTotAirfoil(2) ) + + Re = Chord * Vrel / KinVisc / 1.0E6 + + +!TODO: move the rest of this someplace else. We don't always need these values out, so we will calculate them separately. +! real(ReKi), intent(in ) :: HubOrient(3,3) ! u%HubMotion%Orientation(:,:,1) global coord +! integer(IntKi), intent(in ) :: Node ! Node number +! integer(IntKi), intent(in ) :: Blade ! Blade number +! type(AFI_ParameterType), intent(in ) :: AFInfo !< The airfoil parameter data for this node +! real(ReKi), intent( out) :: CldAirfoil(3) ! Cl and Cd in airfoil coords +! real(ReKi), intent( out) :: Cm ! Cm in airfoil coords +! real(ReKi), intent( out) :: CpMin ! CpMin in airfoil coords +! real(ReKi), intent( out) :: CxySection(3) ! Cx and Cy in section coords +! real(ReKi), intent( out) :: CntDisk(3) ! Cn and Ct in hub/Disk coords +! ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. +!!TODO: AFinfo is usually expecting values at the node. +! call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, AFInfo, AFI_interp, ErrStat, ErrMsg ) +! CldAirfoil = (/ AFI_interp%Cl, AFI_interp%Cd, 0.0_ReKi /) ! Airfoil coord Cl and Cd +! Cm = AFI_interp%Cm +! CpMin = AFI_interp%CpMin + +!TODO: move these elsewhere + ! Simple method: + ! Gamma_LL=(0.5 * Cl * Vrel_orth_norm*chord) + ! VanGarrel's method: +!FIXME: add to BEM also +! GammaVal = 0.5 * Chord * Vrel * CldAirfoil(1) +! CxySection = matmul( MGlobalToSection, matmul( CldAirfoil, NodeOrientation ) ) +! CntDisk = matmul( MGlobalToSection, matmul( CldAirfoil, HubOrient ) ) +end subroutine FVW_AeroOuts + end module FVW_Subs diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 2e7695400c..b3dbdfc948 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -39,6 +39,7 @@ MODULE FVW_Types INTEGER(IntKi) :: nWings !< Number of Wings [-] INTEGER(IntKi) :: nSpan !< TODO, should be defined per wing. Number of spanwise element [-] INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number] [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number] [-] INTEGER(IntKi) :: nNWMax !< Maximum number of nw panels, per wing [-] INTEGER(IntKi) :: nFWMax !< Maximum number of fw panels, per wing [-] INTEGER(IntKi) :: nFWFree !< Number of fw panels that are free, per wing [-] @@ -59,6 +60,7 @@ MODULE FVW_Types INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] INTEGER(IntKi) :: HACK !< HACK ID [-] REAL(DbKi) :: DT !< Time interval for calls calculations [s] + REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] END TYPE FVW_ParameterType ! ======================= ! ========= FVW_OtherStateType ======= @@ -143,6 +145,7 @@ MODULE FVW_Types INTEGER(IntKi) :: NumBlades !< Number of blades [-] INTEGER(IntKi) :: NumBladeNodes !< Number of nodes on each blade [-] REAL(DbKi) :: DT !< Time interval for calls (from AD15) [s] + REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] END TYPE FVW_InitInputType ! ======================= ! ========= FVW_InputFile ======= @@ -209,6 +212,20 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg END IF END IF DstParamData%AFindx = SrcParamData%AFindx +ENDIF +IF (ALLOCATED(SrcParamData%Chord)) THEN + i1_l = LBOUND(SrcParamData%Chord,1) + i1_u = UBOUND(SrcParamData%Chord,1) + i2_l = LBOUND(SrcParamData%Chord,2) + i2_u = UBOUND(SrcParamData%Chord,2) + IF (.NOT. ALLOCATED(DstParamData%Chord)) THEN + ALLOCATE(DstParamData%Chord(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%Chord.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%Chord = SrcParamData%Chord ENDIF DstParamData%nNWMax = SrcParamData%nNWMax DstParamData%nFWMax = SrcParamData%nFWMax @@ -241,6 +258,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%VTKBlades = SrcParamData%VTKBlades DstParamData%HACK = SrcParamData%HACK DstParamData%DT = SrcParamData%DT + DstParamData%KinVisc = SrcParamData%KinVisc END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -255,6 +273,9 @@ SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) IF (ALLOCATED(ParamData%AFindx)) THEN DEALLOCATE(ParamData%AFindx) ENDIF +IF (ALLOCATED(ParamData%Chord)) THEN + DEALLOCATE(ParamData%Chord) +ENDIF IF (ALLOCATED(ParamData%PrescribedCirculation)) THEN DEALLOCATE(ParamData%PrescribedCirculation) ENDIF @@ -301,6 +322,11 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S IF ( ALLOCATED(InData%AFindx) ) THEN Int_BufSz = Int_BufSz + 2*2 ! AFindx upper/lower bounds for each dimension Int_BufSz = Int_BufSz + SIZE(InData%AFindx) ! AFindx + END IF + Int_BufSz = Int_BufSz + 1 ! Chord allocated yes/no + IF ( ALLOCATED(InData%Chord) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Chord upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Chord) ! Chord END IF Int_BufSz = Int_BufSz + 1 ! nNWMax Int_BufSz = Int_BufSz + 1 ! nFWMax @@ -326,6 +352,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! VTKBlades Int_BufSz = Int_BufSz + 1 ! HACK Db_BufSz = Db_BufSz + 1 ! DT + Re_BufSz = Re_BufSz + 1 ! KinVisc IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -372,6 +399,22 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S IF (SIZE(InData%AFindx)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%AFindx))-1 ) = PACK(InData%AFindx,.TRUE.) Int_Xferred = Int_Xferred + SIZE(InData%AFindx) + END IF + IF ( .NOT. ALLOCATED(InData%Chord) ) 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%Chord,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Chord,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Chord,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Chord,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Chord)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Chord))-1 ) = PACK(InData%Chord,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Chord) END IF IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nNWMax Int_Xferred = Int_Xferred + 1 @@ -424,6 +467,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DT Db_Xferred = Db_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%KinVisc + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -491,6 +536,32 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg IF (SIZE(OutData%AFindx)>0) OutData%AFindx = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%AFindx))-1 ), mask2, 0_IntKi ) Int_Xferred = Int_Xferred + SIZE(OutData%AFindx) DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Chord 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 + IF (ALLOCATED(OutData%Chord)) DEALLOCATE(OutData%Chord) + ALLOCATE(OutData%Chord(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Chord.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%Chord)>0) OutData%Chord = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Chord))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Chord) + DEALLOCATE(mask2) END IF OutData%nNWMax = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 @@ -553,6 +624,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%DT = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 + OutData%KinVisc = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -3987,6 +4060,7 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS DstInitInputData%NumBlades = SrcInitInputData%NumBlades DstInitInputData%NumBladeNodes = SrcInitInputData%NumBladeNodes DstInitInputData%DT = SrcInitInputData%DT + DstInitInputData%KinVisc = SrcInitInputData%KinVisc END SUBROUTINE FVW_CopyInitInput SUBROUTINE FVW_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -4125,6 +4199,7 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! NumBlades Int_BufSz = Int_BufSz + 1 ! NumBladeNodes Db_BufSz = Db_BufSz + 1 ! DT + Re_BufSz = Re_BufSz + 1 ! KinVisc IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4306,6 +4381,8 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DT Db_Xferred = Db_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%KinVisc + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_PackInitInput SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4581,6 +4658,8 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%DT = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 + OutData%KinVisc = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_UnPackInitInput SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 6ab91e3b35..ca6584fc46 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -3,7 +3,6 @@ module FVW_Wings use NWTC_Library use FVW_Types use FVW_Subs - use AirfoilInfo implicit none @@ -15,10 +14,9 @@ module FVW_Wings !! - s_CP_LL : Dimensionless spanwise coordinate of LL CP !! - chord_LL : chord on LL !! - chord_LL_CP: chord on LL cp - subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) + subroutine Wings_Panelling_Init(Meshes, r, p, m, ErrStat, ErrMsg ) type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh real(ReKi), dimension(:,:), intent(in ) :: r !< - real(ReKi), dimension(:,:), intent(in ) :: chord !< type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation @@ -57,13 +55,14 @@ subroutine Wings_Panelling_Init(Meshes, r, chord, p, m, ErrStat, ErrMsg ) ! --- Setting up Lifting line variables based on input and a "meshing" method (TODO) if (Meshes(iW)%nNodes /= p%nSpan+1) then ! TODO Possibly interpolate based on FVW meshing + ! NOTE: p%chord is copied from the InitInput print*,'TODO different discretization InputMesh / vortex code' STOP endif print*,'Input mesh size',Meshes(iW)%nNodes,' Number of vortex element', p%nSpan do iSpan = 1, p%nSpan+1 m%s_LL (iSpan, iW) = s_in(iSpan) - m%chord_LL(iSpan, iW) = chord(iSpan,iW) + m%chord_LL(iSpan, iW) = p%chord(iSpan,iW) enddo ! --- Control points !TODO: does it make sense to keep the global position info here? It might make it simpler to keep track of the nodes for requesting wind velocity info. @@ -445,10 +444,19 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) alpha = atan2(dot_product(Vrel,N) , dot_product(Vrel,Tc) ) ! [rad] !Re = LL%Vrel_orth_norm(icp)*LL%chord(icp)/KinVisc/(1.E6_MK) ! TODO TODO TODO KinVisc +!FIXME: Pass p%KinVisc into here from AD Re=1.0_ReKi +!FIXME: do I need this here anymore??? or can I call +!FVW_AeroOuts(MGlobalToSection, HubOrient, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, & +! Node, Blade, KinVisc, Chord, AFInfo, & +! CldAirfoil, Cm, CpMin, & ! CxySection, CntDisk, & +! AxInd, TanInd, Vrel, phi, alpha, Re, & +! ErrStat, ErrMsg ) + if (p%CircSolvPolar==idPolarAeroDyn) then ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. +!TODO: AFindx is on the nodes, not control points. call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, AFInfo(p%AFindx(icp,iW)), AFI_interp, ErrStat2, ErrMsg2 ); if(Failed()) return; Cl = AFI_interp%Cl Cd = AFI_interp%Cd @@ -470,6 +478,7 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) !if ((iW==1).and.icp==3) then ! print*,'CL',Cl,alpha,Vrel_orth_norm,m%Area(icp,iW) !endif + enddo enddo contains From 7a63db62e8769feefe234f0e54aaba88554c8597 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 24 Jan 2020 12:45:40 -0700 Subject: [PATCH 055/190] FVW: more changes for outputs. Compiles now. --- modules/aerodyn/src/AeroDyn_IO.f90 | 40 +++++++++++++++++++++++++++--- modules/aerodyn/src/FVW_Subs.f90 | 5 +--- modules/aerodyn/src/FVW_Wings.f90 | 4 +-- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 060e538ab2..3fcd9a6141 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -23,6 +23,8 @@ MODULE AeroDyn_IO use NWTC_Library use AeroDyn_Types use BEMTUncoupled, only : SkewMod_Uncoupled, SkewMod_PittPeters, VelocityIsZero + use AirFoilInfo, only : AFI_ComputeAirfoilCoefs + use FVW_Subs, only : FVW_AeroOuts implicit none @@ -1586,6 +1588,28 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) end do ! nodes end do ! blades else ! (p%WakeMod == WakeMod_FVW) + call calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) + endif + +contains + subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) + TYPE(AD_ParameterType), INTENT(IN ) :: p ! The module parameters + TYPE(AD_InputType), INTENT(IN ) :: u ! inputs + TYPE(AD_MiscVarType), INTENT(INOUT) :: m ! misc variables + TYPE(AD_OutputType), INTENT(IN ) :: y ! outputs + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! The error status code + CHARACTER(*), INTENT( OUT) :: ErrMsg ! The error message, if an error occurred + + ! local variables + integer, parameter :: indx = 1 ! m%BEMT_u(1) is at t; m%BEMT_u(2) is t+dt + CHARACTER(*), PARAMETER :: RoutineName = 'Calc_WriteOutput' + + INTEGER(IntKi) :: j,k,i +! REAL(ReKi) :: ct, st ! cosine, sine of theta +! REAL(ReKi) :: cp, sp ! cosine, sine of phi + real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re + type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table + ! blade outputs do k=1,p%numBlades do j=2,p%NumBlNds ! NOTE: the LL spans are NumBldNds-1 long @@ -1597,6 +1621,10 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) ! CldAirfoil, Cm, CpMin, & ! CxySection, CntDisk, & ! AxInd, TanInd, Vrel, phi, alpha, Re, & ! ErrStat, ErrMsg ) + call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), u%BladeMotion(k)%TranslationVel(1:3,j), & + m%FVW_y%Vind(1:3,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), p%KinVisc, p%FVW%Chord(j,k), & + AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) + call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) ! m%AllOuts( i ) = m%FVW_u(indx)%theta(j-1,k)*R2D ! m%AllOuts( i+1 ) = m%FVW_u(indx)%psi(k)*R2D @@ -1633,8 +1661,7 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) end do ! nodes end do ! blades - endif - + end subroutine Calc_WriteDbgOutputFVW END SUBROUTINE Calc_WriteDbgOutput !---------------------------------------------------------------------------------------------------------------------------------- @@ -1834,6 +1861,9 @@ subroutine Calc_WriteOutput_BEMT end subroutine Calc_WriteOutput_BEMT subroutine Calc_WriteOutput_FVW + real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re + type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table + ! blade outputs do k=1,p%numBlades do beta=1,p%NBlOuts @@ -1841,8 +1871,10 @@ subroutine Calc_WriteOutput_FVW !TODO: populate this with what we need. ! call FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, KinVisc, Chord, & ! AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) -! -! call AirFoilInfo here + call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), u%BladeMotion(k)%TranslationVel(1:3,j), & + m%FVW_y%Vind(1:3,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), p%KinVisc, p%FVW%Chord(j,k), & + AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) + call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) ! with Cl Cd etc, calculate the values for the Cn Ct Cx Cy etc. Then get forces. end do ! nodes diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 0c6c4abc68..bcb80d2363 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -4,7 +4,6 @@ module FVW_SUBS use FVW_TYPES use FVW_VortexTools use FVW_BiotSavart - use AirFoilInfo, only: AFI_ComputeAirfoilCoefs implicit none @@ -652,7 +651,6 @@ subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, Dis real(ReKi), intent(in ) :: StructVel(3) ! Structural velocity global coord real(ReKi), intent(in ) :: Vind_Glob(3) ! Induced wind velocity global coord real(ReKi), intent(in ) :: DisturbedInflow(3) ! Disturbed inflow global coord - real(ReKi), intent(in ) :: KinVisc ! Viscosity real(ReKi), intent(in ) :: Chord ! chord length real(ReKi), intent( out) :: AxInd ! axial induction @@ -661,7 +659,6 @@ subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, Dis real(Reki), intent( out) :: phi ! Flow angle real(Reki), intent( out) :: alpha ! angle of attack real(ReKi), intent( out) :: Re ! Reynolds number - integer(IntKi), intent( out) :: ErrStat character(ErrMsgLen), intent( out) :: ErrMsg @@ -675,7 +672,6 @@ subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, Dis real(ReKi) :: UTotLocal(3) ! Vector of total relative velocity section coord real(ReKi) :: UTotGlobal(3) ! Vector of total relative velocity global coord real(ReKi) :: UTotAirfoil(3) ! Vector of total relative velocity airfoil coord - type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table x_hat = MGlobalToSection(1,:) ! ADP: We want this Airfoil cross section flapwise. Is this it??? y_hat = MGlobalToSection(2,:) ! ADP: We want this Airfoil cross section chordwise @@ -724,6 +720,7 @@ subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, Dis ! real(ReKi), intent( out) :: CpMin ! CpMin in airfoil coords ! real(ReKi), intent( out) :: CxySection(3) ! Cx and Cy in section coords ! real(ReKi), intent( out) :: CntDisk(3) ! Cn and Ct in hub/Disk coords +! type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table ! ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. !!TODO: AFinfo is usually expecting values at the node. ! call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, AFInfo, AFI_interp, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index ca6584fc46..b65ce2c907 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -3,7 +3,8 @@ module FVW_Wings use NWTC_Library use FVW_Types use FVW_Subs - + use AirFoilInfo, only : AFI_ComputeAirfoilCoefs + implicit none contains @@ -462,7 +463,6 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) Cd = AFI_interp%Cd Cm = AFI_interp%Cm ! Cpmin = AFI_interp%Cpmin -print*,'Manu: fix Re for AFI in CirculationFromPolarData' else if (p%CircSolvPolar==idPolar2PiAlpha) then Cl=TwoPi*alpha else if (p%CircSolvPolar==idPolar2PiSinAlpha) then From bbef92bce5deedd46d308fb7014dbcb45a1945cc Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 24 Jan 2020 13:44:03 -0700 Subject: [PATCH 056/190] FVW: some outputs with the DBG_OUTS flag --- modules/aerodyn/src/AeroDyn.f90 | 71 +++++++++++++--------- modules/aerodyn/src/AeroDyn_IO.f90 | 97 ++++++++++++++++-------------- modules/aerodyn/src/FVW_Subs.f90 | 4 +- 3 files changed, 97 insertions(+), 75 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index c9b90756a9..2dfb9c63bc 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1236,7 +1236,8 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, p%AFI, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - call SetOutputsFromFVW(p, m, y ) + call SetOutputsFromFVW( u, p, m, y, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif if ( p%TwrAero ) then @@ -1682,16 +1683,35 @@ end subroutine SetOutputsFromBEMT !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine converts outputs from FVW (stored in m%FVW_y) into values on the AeroDyn BladeLoad output mesh. -subroutine SetOutputsFromFVW(p, m, y) - type(AD_ParameterType), intent(in ) :: p !< AD parameters - type(AD_OutputType), intent(inout) :: y !< AD outputs - type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables +subroutine SetOutputsFromFVW(u, p, m, y, ErrStat, ErrMsg) + TYPE(AD_InputType), intent(in ) :: u !< Inputs at Time t + type(AD_ParameterType), intent(in ) :: p !< AD parameters + type(AD_OutputType), intent(inout) :: y !< AD outputs + type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + integer(intKi) :: j ! loop counter for nodes + integer(intKi) :: k ! loop counter for blades + real(reki) :: force(3) + real(reki) :: moment(3) + real(reki) :: q + REAL(ReKi) :: ct, st ! cosine, sine of theta + REAL(ReKi) :: cp, sp ! cosine, sine of phi + real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re, theta + type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table + real(ReKi) :: CldAirfoil(3) ! Cl and Cd in airfoil coords + real(ReKi) :: CxySection(3) ! Cx and Cy in section coords +! real(ReKi) :: CntDisk(3) ! Cn and Ct in hub/Disk coords + real(ReKi) :: U0Wind(3) ! Wind in section coords + + integer(intKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + + ErrStat2 = 0 + ErrMsg2 = "" +!TODO: we need to map the Vind from the lifting line control points to the blade nodes!!!!! - integer(intKi) :: j ! loop counter for nodes - integer(intKi) :: k ! loop counter for blades - real(reki) :: force(3) - real(reki) :: moment(3) - real(reki) :: q !TODO: Manu!!!! !TODO: how are we setting the loads at the nodes for coupling to OpenFAST???? @@ -1701,25 +1721,22 @@ subroutine SetOutputsFromFVW(p, m, y) !TODO: Our outputs are on the lifting line! Not at blade nodes! do k=1,p%NumBlades do j=2,p%NumBlNds - -!TODO: populate this with what we need. -! call FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, KinVisc, Chord, & -! AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) -! -! call AirFoilInfo here -! with Cl Cd etc, calculate the values for the Cn Ct Cx Cy etc. Then get forces. - -!FIXME: calculate Cx Cy Cm here. So put Airfoil Coeff calcs here. -! q = 0.5 * p%airDens * Vrel(j-1,k)**2 ! dynamic pressure of the jth node in the kth blade -! force(1) = cx(j-1,k) * q * p%FVW%chord(j,k) ! X = normal force per unit length (normal to the plane, not chord) of the jth node in the kth blade -! force(2) = -cy(j-1,k) * q * p%FVW%chord(j,k) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade -! moment(3)= cm(j-1,k) * q * p%FVW%chord(j,k)**2 ! M = pitching moment per unit length of the jth node in the kth blade + call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), u%BladeMotion(k)%TranslationVel(1:3,j), & + m%FVW_y%Vind(1:3,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), p%KinVisc, p%FVW%Chord(j,k), & + AxInd, TanInd, Vrel, phi, alpha, Re, U0Wind, ErrStat, ErrMsg ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') + call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') + CldAirfoil = (/ AFI_interp%Cl, AFI_interp%Cd, 0.0_ReKi /) ! Airfoil coord Cl and Cd + CxySection = matmul( m%WithoutSweepPitchTwist(:,:,j,k), matmul( CldAirfoil, u%BladeMotion(k)%Orientation(1:3,1:3,j) ) ) + + q = 0.5 * p%airDens * Vrel**2 ! dynamic pressure of the jth node in the kth blade + force(1) = CxySection(1) * q * p%FVW%Chord(j,k) ! X = normal force per unit length (normal to the plane, not chord) of the jth node in the kth blade + force(2) = -CxySection(2) * q * p%FVW%Chord(j,k) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade + moment(3)= AFI_interp%Cm * q * p%FVW%Chord(j,k)**2 ! M = pitching moment per unit length of the jth node in the kth blade end do !j=nodes end do !k=blades -!Keep this part V - -!TODO: Our outputs are on the lifting line! Not at blade nodes! do k=1,p%NumBlades do j=2,p%NumBlNds ! save these values for possible output later: @@ -1729,8 +1746,6 @@ subroutine SetOutputsFromFVW(p, m, y) end do !j=nodes end do !k=blades -!TODO: Manu!!!! -!TODO: Our outputs are on the lifting line! Not at blade nodes! The following is not really correct! Some kind of mapping should be done. do k=1,p%NumBlades do j=2,p%NumBlNds ! note: because force and moment are 1-d arrays, I'm calculating the transpose of the force and moment outputs diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 3fcd9a6141..39ec59d8d6 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1605,60 +1605,66 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) CHARACTER(*), PARAMETER :: RoutineName = 'Calc_WriteOutput' INTEGER(IntKi) :: j,k,i -! REAL(ReKi) :: ct, st ! cosine, sine of theta -! REAL(ReKi) :: cp, sp ! cosine, sine of phi - real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re - type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table + REAL(ReKi) :: ct, st ! cosine, sine of theta + REAL(ReKi) :: cp, sp ! cosine, sine of phi + real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re, theta + real(ReKi) :: GammaVal ! Vorticity + type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table + real(ReKi) :: CldAirfoil(3) ! Cl and Cd in airfoil coords + real(ReKi) :: CxySection(3) ! Cx and Cy in section coords + real(ReKi) :: CntDisk(3) ! Cn and Ct in hub/Disk coords + real(ReKi) :: U0Wind(3) ! Wind in section coords ! blade outputs do k=1,p%numBlades do j=2,p%NumBlNds ! NOTE: the LL spans are NumBldNds-1 long -!TODO: FVW -- put all the ugly mappings here? +!TODO: FVW -- put all the ugly mappings here???? i = (k-1)*p%NumBlNds*23 + (j-1)*23 + 1 -!FVW_AeroOuts(MGlobalToSection, HubOrient, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, & -! Node, Blade, KinVisc, Chord, AFInfo, & -! CldAirfoil, Cm, CpMin, & ! CxySection, CntDisk, & -! AxInd, TanInd, Vrel, phi, alpha, Re, & -! ErrStat, ErrMsg ) + call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), u%BladeMotion(k)%TranslationVel(1:3,j), & m%FVW_y%Vind(1:3,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), p%KinVisc, p%FVW%Chord(j,k), & - AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) + AxInd, TanInd, Vrel, phi, alpha, Re, U0Wind, ErrStat, ErrMsg ) call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) -! m%AllOuts( i ) = m%FVW_u(indx)%theta(j-1,k)*R2D -! m%AllOuts( i+1 ) = m%FVW_u(indx)%psi(k)*R2D -! m%AllOuts( i+2 ) = -m%FVW_u(indx)%Vx(j-1,k) -! m%AllOuts( i+3 ) = m%FVW_u(indx)%Vy(j-1,k) - -! m%AllOuts( i+4 ) = m%FVW_y%axInd(j-1,k) -! m%AllOuts( i+5 ) = m%FVW_y%tanInd(j-1,k) -! m%AllOuts( i+6 ) = m%FVW_y%Vrel(j-1,k) -! m%AllOuts( i+7 ) = m%FVW_y%phi(j-1,k)*R2D -! m%AllOuts( i+8 ) = (m%FVW_y%phi(j-1,k) - m%BEMT_u(indx)%theta(j-1,k))*R2D -! m%AllOuts( i+8 ) = m%FVW_y%alpha(j-1,k)*R2D -! -! m%AllOuts( i+9 ) = m%FVW_y%Cl(j-1,k) -! m%AllOuts( i+10 ) = m%FVW_y%Cd(j-1,k) -! m%AllOuts( i+11 ) = m%FVW_y%Cm(j-1,k) -! m%AllOuts( i+12 ) = m%FVW_y%Cx(j-1,k) -! m%AllOuts( i+13 ) = m%FVW_y%Cy(j-1,k) - -! ct=cos(m%FVW_u(indx)%theta(j,k)) -! st=sin(m%FVW_u(indx)%theta(j,k)) -! m%AllOuts( i+14 ) = m%FVW_y%Cx(j,k)*ct + m%BEMT_y%Cy(j,k)*st -! m%AllOuts( i+15 ) = -m%FVW_y%Cx(j,k)*st + m%BEMT_y%Cy(j,k)*ct - -! cp=cos(m%FVW_y%phi(j,k)) -! sp=sin(m%FVW_y%phi(j,k)) -! m%AllOuts( i+16 ) = m%X(j,k)*cp - m%Y(j,k)*sp -! m%AllOuts( i+17 ) = m%X(j,k)*sp + m%Y(j,k)*cp -! m%AllOuts( i+18 ) = m%M(j,k) -! m%AllOuts( i+19 ) = m%X(j,k) -! m%AllOuts( i+20 ) = -m%Y(j,k) -! m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st -! m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct - + CldAirfoil = (/ AFI_interp%Cl, AFI_interp%Cd, 0.0_ReKi /) ! Airfoil coord Cl and Cd + CxySection = matmul( m%WithoutSweepPitchTwist(:,:,j,k), matmul( CldAirfoil, u%BladeMotion(k)%Orientation(1:3,1:3,j) ) ) + CntDisk = matmul( m%WithoutSweepPitchTwist(:,:,j,k), matmul( CldAirfoil, u%HubMotion%Orientation(:,:,1) ) ) + theta = phi - alpha +!TODO: add this to the output list +! GammaVal = 0.5 * p%FVW%Chord(j,k) * Vrel * CldAirfoil(1) + + m%AllOuts( i ) = theta*R2D ! "Twst" +! m%AllOuts( i+1 ) = m%FVW_u(indx)%psi(k)*R2D ! "Psi" + m%AllOuts( i+2 ) = -U0Wind(1) ! "Vx" + m%AllOuts( i+3 ) = U0Wind(2) ! "Vy" + + m%AllOuts( i+4 ) = AxInd ! "AIn" + m%AllOuts( i+5 ) = TanInd ! "ApIn" + m%AllOuts( i+6 ) = Vrel ! "Vrel" + m%AllOuts( i+7 ) = phi*R2D ! "Phi" + m%AllOuts( i+8 ) = alpha*R2D ! "AOA" + + m%AllOuts( i+9 ) = AFI_interp%Cl ! "Cl" + m%AllOuts( i+10 ) = AFI_interp%Cd ! "Cd" + m%AllOuts( i+11 ) = AFI_interp%Cm ! "Cm" + m%AllOuts( i+12 ) = CxySection(1) ! "Cx" + m%AllOuts( i+13 ) = CxySection(2) ! "Cy" + + ct=cos(theta) + st=sin(theta) + m%AllOuts( i+14 ) = CxySection(1)*ct + CxySection(2)*st ! "Cn" + m%AllOuts( i+15 ) = -CxySection(1)*st + CxySection(2)*ct ! "Ct" + + cp=cos(phi) + sp=sin(phi) + m%AllOuts( i+16 ) = m%X(j,k)*cp - m%Y(j,k)*sp ! "Fl" + m%AllOuts( i+17 ) = m%X(j,k)*sp + m%Y(j,k)*cp ! "Fd" + m%AllOuts( i+18 ) = m%M(j,k) ! "M" + m%AllOuts( i+19 ) = m%X(j,k) ! "Fx" + m%AllOuts( i+20 ) = -m%Y(j,k) ! "Fy" + m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st ! "Fn" + m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct ! "Ft" end do ! nodes end do ! blades end subroutine Calc_WriteDbgOutputFVW @@ -1863,6 +1869,7 @@ end subroutine Calc_WriteOutput_BEMT subroutine Calc_WriteOutput_FVW real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table + real(ReKi) :: U0Wind(3) ! Wind in section coords ! blade outputs do k=1,p%numBlades @@ -1873,7 +1880,7 @@ subroutine Calc_WriteOutput_FVW ! AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), u%BladeMotion(k)%TranslationVel(1:3,j), & m%FVW_y%Vind(1:3,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), p%KinVisc, p%FVW%Chord(j,k), & - AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) + AxInd, TanInd, Vrel, phi, alpha, Re, U0Wind, ErrStat, ErrMsg ) call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) ! with Cl Cd etc, calculate the values for the Cn Ct Cx Cy etc. Then get forces. diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index bcb80d2363..70bfa29332 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -645,7 +645,7 @@ subroutine UnPackLiftingLineVelocities() subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, KinVisc, Chord, & - AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) + AxInd, TanInd, Vrel, phi, alpha, Re, U0, ErrStat, ErrMsg ) real(ReKi), intent(in ) :: MGlobalToSection(3,3) ! m%WithoutSweepPitchTwist(:,:,j,k) global coord real(ReKi), intent(in ) :: NodeOrient(3,3) ! u%BladeMotion(k)%Orientation(1:3,1:3,j) global coord real(ReKi), intent(in ) :: StructVel(3) ! Structural velocity global coord @@ -659,6 +659,7 @@ subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, Dis real(Reki), intent( out) :: phi ! Flow angle real(Reki), intent( out) :: alpha ! angle of attack real(ReKi), intent( out) :: Re ! Reynolds number + real(ReKi), intent( out) :: U0(3) ! Vector of UWidn - VStruct section coord integer(IntKi), intent( out) :: ErrStat character(ErrMsgLen), intent( out) :: ErrMsg @@ -668,7 +669,6 @@ subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, Dis real(ReKi) :: VStruct(3) ! Struct Velocity, section coord real(ReKi) :: VInd(3) ! Induced Velocity, section coord real(ReKi) :: UWind(3) ! Disturbed wind velocity, section coord - real(ReKi) :: U0(3) ! Vector of UWidn - VStruct section coord real(ReKi) :: UTotLocal(3) ! Vector of total relative velocity section coord real(ReKi) :: UTotGlobal(3) ! Vector of total relative velocity global coord real(ReKi) :: UTotAirfoil(3) ! Vector of total relative velocity airfoil coord From d1a314db34a2578f6180ae15b58ee7182837a8ea Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 24 Jan 2020 13:49:01 -0700 Subject: [PATCH 057/190] FVW: minor update to Re calc in FVW_Wings --- modules/aerodyn/src/FVW_Subs.f90 | 28 ---------------------------- modules/aerodyn/src/FVW_Wings.f90 | 12 +----------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 70bfa29332..6c83688eae 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -708,34 +708,6 @@ subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, Dis alpha = atan2( UTotAirfoil(1), UTotAirfoil(2) ) Re = Chord * Vrel / KinVisc / 1.0E6 - - -!TODO: move the rest of this someplace else. We don't always need these values out, so we will calculate them separately. -! real(ReKi), intent(in ) :: HubOrient(3,3) ! u%HubMotion%Orientation(:,:,1) global coord -! integer(IntKi), intent(in ) :: Node ! Node number -! integer(IntKi), intent(in ) :: Blade ! Blade number -! type(AFI_ParameterType), intent(in ) :: AFInfo !< The airfoil parameter data for this node -! real(ReKi), intent( out) :: CldAirfoil(3) ! Cl and Cd in airfoil coords -! real(ReKi), intent( out) :: Cm ! Cm in airfoil coords -! real(ReKi), intent( out) :: CpMin ! CpMin in airfoil coords -! real(ReKi), intent( out) :: CxySection(3) ! Cx and Cy in section coords -! real(ReKi), intent( out) :: CntDisk(3) ! Cn and Ct in hub/Disk coords -! type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table -! ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. -!!TODO: AFinfo is usually expecting values at the node. -! call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, AFInfo, AFI_interp, ErrStat, ErrMsg ) -! CldAirfoil = (/ AFI_interp%Cl, AFI_interp%Cd, 0.0_ReKi /) ! Airfoil coord Cl and Cd -! Cm = AFI_interp%Cm -! CpMin = AFI_interp%CpMin - -!TODO: move these elsewhere - ! Simple method: - ! Gamma_LL=(0.5 * Cl * Vrel_orth_norm*chord) - ! VanGarrel's method: -!FIXME: add to BEM also -! GammaVal = 0.5 * Chord * Vrel * CldAirfoil(1) -! CxySection = matmul( MGlobalToSection, matmul( CldAirfoil, NodeOrientation ) ) -! CntDisk = matmul( MGlobalToSection, matmul( CldAirfoil, HubOrient ) ) end subroutine FVW_AeroOuts diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index b65ce2c907..f8bcd8bf7a 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -444,16 +444,7 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) Vjouk_orth_norm = norm2(Vjouk_orth) alpha = atan2(dot_product(Vrel,N) , dot_product(Vrel,Tc) ) ! [rad] - !Re = LL%Vrel_orth_norm(icp)*LL%chord(icp)/KinVisc/(1.E6_MK) ! TODO TODO TODO KinVisc -!FIXME: Pass p%KinVisc into here from AD -Re=1.0_ReKi - -!FIXME: do I need this here anymore??? or can I call -!FVW_AeroOuts(MGlobalToSection, HubOrient, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, & -! Node, Blade, KinVisc, Chord, AFInfo, & -! CldAirfoil, Cm, CpMin, & ! CxySection, CntDisk, & -! AxInd, TanInd, Vrel, phi, alpha, Re, & -! ErrStat, ErrMsg ) + Re = p%Chord(icp,iW) * norm2(Vrel) / p%KinVisc / 1.0E6 if (p%CircSolvPolar==idPolarAeroDyn) then ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. @@ -462,7 +453,6 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) Cl = AFI_interp%Cl Cd = AFI_interp%Cd Cm = AFI_interp%Cm -! Cpmin = AFI_interp%Cpmin else if (p%CircSolvPolar==idPolar2PiAlpha) then Cl=TwoPi*alpha else if (p%CircSolvPolar==idPolar2PiSinAlpha) then From 82b693b29a6c716f978bcb2e124efaf0529d38cf Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 27 Jan 2020 21:28:04 -0700 Subject: [PATCH 058/190] FVW: fixed units in dbg outs --- modules/aerodyn/src/AeroDyn.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 2dfb9c63bc..a961906d61 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -124,9 +124,9 @@ subroutine AD_SetInitOut(p, InputFileData, InitOut, errStat, errMsg) InitOut%WriteOutputHdr( m + 4 ) = trim(chanPrefix)//"Vy" InitOut%WriteOutputUnt( m + 4 ) = ' (m/s) ' InitOut%WriteOutputHdr( m + 5 ) = ' '//trim(chanPrefix)//"AIn" - InitOut%WriteOutputUnt( m + 5 ) = ' (deg) ' + InitOut%WriteOutputUnt( m + 5 ) = ' (-) ' InitOut%WriteOutputHdr( m + 6 ) = ' '//trim(chanPrefix)//"ApIn" - InitOut%WriteOutputUnt( m + 6 ) = ' (deg) ' + InitOut%WriteOutputUnt( m + 6 ) = ' (-) ' InitOut%WriteOutputHdr( m + 7 ) = trim(chanPrefix)//"Vrel" InitOut%WriteOutputUnt( m + 7 ) = ' (m/s) ' InitOut%WriteOutputHdr( m + 8 ) = ' '//trim(chanPrefix)//"Phi" From 827fe1434a0eb596ce628cbbe57b13054a9e768a Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 27 Jan 2020 21:29:40 -0700 Subject: [PATCH 059/190] FVW: adding gamma to Aerodyn Dbg outs --- modules/aerodyn/src/AeroDyn.f90 | 6 ++++-- modules/aerodyn/src/AeroDyn_IO.f90 | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index a961906d61..ad16372abe 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -111,7 +111,7 @@ subroutine AD_SetInitOut(p, InputFileData, InitOut, errStat, errMsg) do k=1,p%numBlades do j=1, p%NumBlNds - m = (k-1)*p%NumBlNds*23 + (j-1)*23 + m = (k-1)*p%NumBlNds*24 + (j-1)*24 WRITE (TmpChar,'(I3.3)') j chanPrefix = "B"//trim(num2lstr(k))//"N"//TmpChar @@ -161,6 +161,8 @@ subroutine AD_SetInitOut(p, InputFileData, InitOut, errStat, errMsg) InitOut%WriteOutputUnt( m + 22 ) = ' (N/m) ' InitOut%WriteOutputHdr( m + 23 ) = ' '//trim(chanPrefix)//"Ft" InitOut%WriteOutputUnt( m + 23 ) = ' (N/m) ' + InitOut%WriteOutputHdr( m + 24 ) = ' '//trim(chanPrefix)//"Gam" + InitOut%WriteOutputUnt( m + 24 ) = ' (m^2/s) ' end do end do @@ -1011,7 +1013,7 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) !p%RootName = TRIM(InitInp%RootName)//'.AD' ! set earlier to it could be used #ifdef DBG_OUTS - p%NBlOuts = 23 + p%NBlOuts = 24 p%numOuts = p%NumBlNds*p%NumBlades*p%NBlOuts p%NTwOuts = 0 diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 39ec59d8d6..a25b76a32e 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1550,7 +1550,7 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) do j=1,p%NumBlNds - i = (k-1)*p%NumBlNds*23 + (j-1)*23 + 1 + i = (k-1)*p%NumBlNds*24 + (j-1)*24 + 1 m%AllOuts( i ) = m%BEMT_u(indx)%theta(j,k)*R2D m%AllOuts( i+1 ) = m%BEMT_u(indx)%psi(k)*R2D @@ -1584,6 +1584,7 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) m%AllOuts( i+20 ) = -m%Y(j,k) m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct + m%AllOuts( i+23 ) = 0.5_ReKi * p%BEMT%chord(j,k) * m%BEMT_y%Vrel(j,k) * m%BEMT_y%Cl(j,k) ! "Gam" [m^2/s] end do ! nodes end do ! blades @@ -1665,6 +1666,7 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) m%AllOuts( i+20 ) = -m%Y(j,k) ! "Fy" m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st ! "Fn" m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct ! "Ft" + m%AllOuts( i+23 ) = 0.5_ReKi * p%FVW%Chord(j,k) * Vrel * AFI_interp%Cl ! "Gam" [m^2/s] end do ! nodes end do ! blades end subroutine Calc_WriteDbgOutputFVW From b51f5605e1508a7f5bb676961b759c916a85cead Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 27 Jan 2020 21:30:25 -0700 Subject: [PATCH 060/190] FVW: storing pitch and twist --- modules/aerodyn/src/AeroDyn.f90 | 1 + modules/aerodyn/src/FVW.f90 | 1 + modules/aerodyn/src/FVW_Registry.txt | 3 +- modules/aerodyn/src/FVW_Types.f90 | 65 ++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index ad16372abe..c5cba472c4 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1629,6 +1629,7 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) ErrMsg = RoutineName//": WingsMesh contains different number of nodes than the BladeMotion mesh" return endif + m%FVW%PitchAndTwist(:,k) = thetaBladeNds(:,k) ! local pitch + twist (aerodyanmic + elastic) angle of the jth node in the kth blade m%FVW_u(tIndx)%WingsMesh(k)%TranslationDisp = u(tIndx)%BladeMotion(k)%TranslationDisp m%FVW_u(tIndx)%WingsMesh(k)%Orientation = u(tIndx)%BladeMotion(k)%Orientation m%FVW_u(tIndx)%WingsMesh(k)%TranslationVel = u(tIndx)%BladeMotion(k)%TranslationVel diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 6d958ca471..166e232e51 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -168,6 +168,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%TE = -999999_ReKi; call AllocAry( m%s_LL , p%nSpan+1 , p%nWings, 'Spanwise coord LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%s_LL= -999999_ReKi; call AllocAry( m%chord_LL , p%nSpan+1 , p%nWings, 'Chord on LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%chord_LL= -999999_ReKi; + call AllocAry( m%PitchAndTwist , p%nSpan+1 , p%nWings, 'Pitch and twist ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%PitchAndTwist= -999999_ReKi; ! Variables at control points/elements call AllocAry( m%Gamma_LL, p%nSpan , p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Gamma_LL = -999999_ReKi; call AllocAry( m%chord_CP_LL , p%nSpan , p%nWings, 'Chord on CP LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%chord_CP_LL= -999999_ReKi; diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index e842646427..9fcd19bc00 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -70,7 +70,8 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi nFW - - - "Number of active far wake panels" - typedef ^ ^ IntKi iStep - - - "Current step number" - typedef ^ ^ IntKi iStepPrev - - - "Previous step number" - -typedef ^ ^ ReKi r_wind :: - - "List of points where wind is requested for next time step" - +typedef ^ ^ ReKi r_wind :: - - "List of points where wind is requested for next time step" - +typedef ^ ^ ReKi PitchAndTwist :: - - "Twist angle (includes all sources of twist) [Array of size (NumBlNds,numBlades)]" rad # ........ Input ............ # FVW_InputType diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index b3dbdfc948..bb313b4cd9 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -98,6 +98,7 @@ MODULE FVW_Types INTEGER(IntKi) :: iStep !< Current step number [-] INTEGER(IntKi) :: iStepPrev !< Previous step number [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: r_wind !< List of points where wind is requested for next time step [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: PitchAndTwist !< Twist angle (includes all sources of twist) [Array of size (NumBlNds,numBlades)] [rad] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -1145,6 +1146,20 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) END IF END IF DstMiscData%r_wind = SrcMiscData%r_wind +ENDIF +IF (ALLOCATED(SrcMiscData%PitchAndTwist)) THEN + i1_l = LBOUND(SrcMiscData%PitchAndTwist,1) + i1_u = UBOUND(SrcMiscData%PitchAndTwist,1) + i2_l = LBOUND(SrcMiscData%PitchAndTwist,2) + i2_u = UBOUND(SrcMiscData%PitchAndTwist,2) + IF (.NOT. ALLOCATED(DstMiscData%PitchAndTwist)) THEN + ALLOCATE(DstMiscData%PitchAndTwist(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%PitchAndTwist.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%PitchAndTwist = SrcMiscData%PitchAndTwist ENDIF END SUBROUTINE FVW_CopyMisc @@ -1225,6 +1240,9 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) ENDIF IF (ALLOCATED(MiscData%r_wind)) THEN DEALLOCATE(MiscData%r_wind) +ENDIF +IF (ALLOCATED(MiscData%PitchAndTwist)) THEN + DEALLOCATE(MiscData%PitchAndTwist) ENDIF END SUBROUTINE FVW_DestroyMisc @@ -1383,6 +1401,11 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*2 ! r_wind upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%r_wind) ! r_wind END IF + Int_BufSz = Int_BufSz + 1 ! PitchAndTwist allocated yes/no + IF ( ALLOCATED(InData%PitchAndTwist) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! PitchAndTwist upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%PitchAndTwist) ! PitchAndTwist + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -1851,6 +1874,22 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%r_wind)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%r_wind))-1 ) = PACK(InData%r_wind,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%r_wind) END IF + IF ( .NOT. ALLOCATED(InData%PitchAndTwist) ) 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%PitchAndTwist,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%PitchAndTwist,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%PitchAndTwist,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%PitchAndTwist,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%PitchAndTwist)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%PitchAndTwist))-1 ) = PACK(InData%PitchAndTwist,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%PitchAndTwist) + END IF END SUBROUTINE FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2560,6 +2599,32 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%r_wind) DEALLOCATE(mask2) END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! PitchAndTwist 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 + IF (ALLOCATED(OutData%PitchAndTwist)) DEALLOCATE(OutData%PitchAndTwist) + ALLOCATE(OutData%PitchAndTwist(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%PitchAndTwist.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%PitchAndTwist)>0) OutData%PitchAndTwist = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%PitchAndTwist))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%PitchAndTwist) + DEALLOCATE(mask2) + END IF END SUBROUTINE FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) From c8e2efae94e5316a11dfb8bc0360bbfd579c07f4 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 27 Jan 2020 23:00:01 -0700 Subject: [PATCH 061/190] FVW: most aerodyn outputs computed with FVW --- modules/aerodyn/src/AeroDyn.f90 | 41 +++---- modules/aerodyn/src/AeroDyn_IO.f90 | 178 ++++++++++++++++++++--------- modules/aerodyn/src/FVW_Subs.f90 | 100 ++++++++-------- 3 files changed, 182 insertions(+), 137 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index c5cba472c4..d1f36c56e2 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1402,7 +1402,7 @@ subroutine SetInputsForBEMT(p, u, m, indx, errStat, errMsg) real(ReKi) :: tmp(3) real(ReKi) :: tmp_sz, tmp_sz_y real(R8Ki) :: thetaBladeNds(p%NumBlNds,p%NumBlades) - real(ReKi) :: Azimuth(p%NumBlNds) + real(R8Ki) :: Azimuth(p%NumBlNds) integer(intKi) :: j ! loop counter for nodes integer(intKi) :: k ! loop counter for blades @@ -1606,7 +1606,7 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) ! real(ReKi) :: tmp(3) ! real(ReKi) :: tmp_sz, tmp_sz_y real(R8Ki) :: thetaBladeNds(p%NumBlNds,p%NumBlades) - real(ReKi) :: Azimuth(p%NumBlNds) + real(R8Ki) :: Azimuth(p%NumBlNds) integer(intKi) :: tIndx integer(intKi) :: k ! loop counter for blades @@ -1703,54 +1703,41 @@ subroutine SetOutputsFromFVW(u, p, m, y, ErrStat, ErrMsg) REAL(ReKi) :: cp, sp ! cosine, sine of phi real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re, theta type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table - real(ReKi) :: CldAirfoil(3) ! Cl and Cd in airfoil coords - real(ReKi) :: CxySection(3) ! Cx and Cy in section coords -! real(ReKi) :: CntDisk(3) ! Cn and Ct in hub/Disk coords - real(ReKi) :: U0Wind(3) ! Wind in section coords + real(ReKi) :: UrelWind_s(3) ! Relative wind (wind+str) in section coords + real(ReKi) :: Cx, Cy integer(intKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 ErrStat2 = 0 ErrMsg2 = "" -!TODO: we need to map the Vind from the lifting line control points to the blade nodes!!!!! -!TODO: Manu!!!! -!TODO: how are we setting the loads at the nodes for coupling to OpenFAST???? -!NOTE: size of _y%c[xym] is 1:p%NumBldNds-1 force(3) = 0.0_ReKi moment(1:2) = 0.0_ReKi -!TODO: Our outputs are on the lifting line! Not at blade nodes! do k=1,p%NumBlades - do j=2,p%NumBlNds - call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), u%BladeMotion(k)%TranslationVel(1:3,j), & - m%FVW_y%Vind(1:3,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), p%KinVisc, p%FVW%Chord(j,k), & - AxInd, TanInd, Vrel, phi, alpha, Re, U0Wind, ErrStat, ErrMsg ) + do j=2,p%NumBlNds !TODO: Our outputs are on the lifting line! Not at blade nodes! + call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), m%FVW%PitchAndTwist(j,k), u%BladeMotion(k)%TranslationVel(1:3,j), & + m%FVW_y%Vind(1:3,j,k), m%DisturbedInflow(:,j,k) , p%KinVisc, p%FVW%Chord(j,k), & + AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') - CldAirfoil = (/ AFI_interp%Cl, AFI_interp%Cd, 0.0_ReKi /) ! Airfoil coord Cl and Cd - CxySection = matmul( m%WithoutSweepPitchTwist(:,:,j,k), matmul( CldAirfoil, u%BladeMotion(k)%Orientation(1:3,1:3,j) ) ) + cp = cos(phi) + sp = sin(phi) + Cx = AFI_interp%Cl*cp + AFI_interp%Cd*sp + Cy = AFI_interp%Cl*sp - AFI_interp%Cd*cp q = 0.5 * p%airDens * Vrel**2 ! dynamic pressure of the jth node in the kth blade - force(1) = CxySection(1) * q * p%FVW%Chord(j,k) ! X = normal force per unit length (normal to the plane, not chord) of the jth node in the kth blade - force(2) = -CxySection(2) * q * p%FVW%Chord(j,k) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade + force(1) = Cx * q * p%FVW%Chord(j,k) ! X = normal force per unit length (normal to the plane, not chord) of the jth node in the kth blade + force(2) = -Cy * q * p%FVW%Chord(j,k) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade moment(3)= AFI_interp%Cm * q * p%FVW%Chord(j,k)**2 ! M = pitching moment per unit length of the jth node in the kth blade - end do !j=nodes - end do !k=blades - do k=1,p%NumBlades - do j=2,p%NumBlNds ! save these values for possible output later: m%X(j,k) = force(1) m%Y(j,k) = force(2) m%M(j,k) = moment(3) - end do !j=nodes - end do !k=blades - do k=1,p%NumBlades - do j=2,p%NumBlNds ! note: because force and moment are 1-d arrays, I'm calculating the transpose of the force and moment outputs ! so that I don't have to take the transpose of WithoutSweepPitchTwist(:,:,j,k) y%BladeLoad(k)%Force(:,j) = matmul( force, m%WithoutSweepPitchTwist(:,:,j,k) ) ! force per unit length of the jth node in the kth blade diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index a25b76a32e..d29641548d 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1611,34 +1611,31 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re, theta real(ReKi) :: GammaVal ! Vorticity type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table - real(ReKi) :: CldAirfoil(3) ! Cl and Cd in airfoil coords - real(ReKi) :: CxySection(3) ! Cx and Cy in section coords - real(ReKi) :: CntDisk(3) ! Cn and Ct in hub/Disk coords - real(ReKi) :: U0Wind(3) ! Wind in section coords + real(ReKi) :: UrelWind_s(3) ! Wind in section coords + real(ReKi) :: Vwnd(3) + real(ReKi) :: Cx, Cy ! blade outputs do k=1,p%numBlades - do j=2,p%NumBlNds ! NOTE: the LL spans are NumBldNds-1 long -!TODO: FVW -- put all the ugly mappings here???? + do j=2,p%NumBlNds ! TODO TODO TODO start at 1 +!TODO: Merge with BEM to avoid all code redundancy (discuss with Bonnie) - i = (k-1)*p%NumBlNds*23 + (j-1)*23 + 1 + i = (k-1)*p%NumBlNds*24 + (j-1)*24 + 1 - call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), u%BladeMotion(k)%TranslationVel(1:3,j), & - m%FVW_y%Vind(1:3,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), p%KinVisc, p%FVW%Chord(j,k), & - AxInd, TanInd, Vrel, phi, alpha, Re, U0Wind, ErrStat, ErrMsg ) + ! --- Computing main aero variables from induction - setting local variables + Vwnd = m%DisturbedInflow(:,j,k) + call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), m%FVW%PitchAndTwist(j,k), u%BladeMotion(k)%TranslationVel(1:3,j), & + m%FVW_y%Vind(1:3,j,k), Vwnd, p%KinVisc, p%FVW%Chord(j,k), & + AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) - CldAirfoil = (/ AFI_interp%Cl, AFI_interp%Cd, 0.0_ReKi /) ! Airfoil coord Cl and Cd - CxySection = matmul( m%WithoutSweepPitchTwist(:,:,j,k), matmul( CldAirfoil, u%BladeMotion(k)%Orientation(1:3,1:3,j) ) ) - CntDisk = matmul( m%WithoutSweepPitchTwist(:,:,j,k), matmul( CldAirfoil, u%HubMotion%Orientation(:,:,1) ) ) - theta = phi - alpha -!TODO: add this to the output list -! GammaVal = 0.5 * p%FVW%Chord(j,k) * Vrel * CldAirfoil(1) + theta = m%FVW%PitchAndTwist(j,k) - m%AllOuts( i ) = theta*R2D ! "Twst" + ! --- Setting aerodyn outputs (more or less generic code) + m%AllOuts( i ) = theta*R2D ! "Twst" = phi-alpha ! m%AllOuts( i+1 ) = m%FVW_u(indx)%psi(k)*R2D ! "Psi" - m%AllOuts( i+2 ) = -U0Wind(1) ! "Vx" - m%AllOuts( i+3 ) = U0Wind(2) ! "Vy" + m%AllOuts( i+2 ) = -UrelWind_s(1) ! "Vx" + m%AllOuts( i+3 ) = UrelWind_s(2) ! "Vy" m%AllOuts( i+4 ) = AxInd ! "AIn" m%AllOuts( i+5 ) = TanInd ! "ApIn" @@ -1646,16 +1643,20 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) m%AllOuts( i+7 ) = phi*R2D ! "Phi" m%AllOuts( i+8 ) = alpha*R2D ! "AOA" + cp = cos(phi) + sp = sin(phi) + Cx = AFI_interp%Cl*cp + AFI_interp%Cd*sp + Cy = AFI_interp%Cl*sp - AFI_interp%Cd*cp m%AllOuts( i+9 ) = AFI_interp%Cl ! "Cl" m%AllOuts( i+10 ) = AFI_interp%Cd ! "Cd" m%AllOuts( i+11 ) = AFI_interp%Cm ! "Cm" - m%AllOuts( i+12 ) = CxySection(1) ! "Cx" - m%AllOuts( i+13 ) = CxySection(2) ! "Cy" + m%AllOuts( i+12 ) = Cx ! "Cx" + m%AllOuts( i+13 ) = Cy ! "Cy" ct=cos(theta) st=sin(theta) - m%AllOuts( i+14 ) = CxySection(1)*ct + CxySection(2)*st ! "Cn" - m%AllOuts( i+15 ) = -CxySection(1)*st + CxySection(2)*ct ! "Ct" + m%AllOuts( i+14 ) = Cx*ct + Cy*st ! "Cn" + m%AllOuts( i+15 ) = -Cx*st + Cy*ct ! "Ct" cp=cos(phi) sp=sin(phi) @@ -1871,20 +1872,85 @@ end subroutine Calc_WriteOutput_BEMT subroutine Calc_WriteOutput_FVW real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table - real(ReKi) :: U0Wind(3) ! Wind in section coords + real(ReKi) :: UrelWind_s(3) ! Relative Wind in section coords + real(ReKi) :: Vind(3) ! + real(ReKi) :: Vstr(3) ! + real(ReKi) :: Vwnd(3) ! + real(ReKi) :: Cx, Cy, cphi, sphi, theta ! blade outputs do k=1,p%numBlades do beta=1,p%NBlOuts j=p%BlOutNd(beta) -!TODO: populate this with what we need. -! call FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, KinVisc, Chord, & -! AxInd, TanInd, Vrel, phi, alpha, Re, ErrStat, ErrMsg ) - call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), u%BladeMotion(k)%TranslationVel(1:3,j), & - m%FVW_y%Vind(1:3,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), p%KinVisc, p%FVW%Chord(j,k), & - AxInd, TanInd, Vrel, phi, alpha, Re, U0Wind, ErrStat, ErrMsg ) + ! --- Computing main aero variables from induction - setting local variables + Vind = m%FVW_y%Vind(1:3,j,k) + Vstr = u%BladeMotion(k)%TranslationVel(1:3,j) + Vwnd = m%DisturbedInflow(:,j,k) + call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), m%FVW%PitchAndTwist(j,k), Vstr , & + Vind(1:3), Vwnd , p%KinVisc, p%FVW%Chord(j,k), & + AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) + call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) -! with Cl Cd etc, calculate the values for the Cn Ct Cx Cy etc. Then get forces. + theta = m%FVW%PitchAndTwist(j,k) + + ! --- Setting AD outputs + tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%InflowOnBlade(:,j,k) ) + m%AllOuts( BNVUndx(beta,k) ) = tmp(1) + m%AllOuts( BNVUndy(beta,k) ) = tmp(2) + m%AllOuts( BNVUndz(beta,k) ) = tmp(3) + + tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), m%DisturbedInflow(:,j,k) ) + m%AllOuts( BNVDisx(beta,k) ) = tmp(1) + m%AllOuts( BNVDisy(beta,k) ) = tmp(2) + m%AllOuts( BNVDisz(beta,k) ) = tmp(3) + + tmp = matmul( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%TranslationVel(:,j) ) + m%AllOuts( BNSTVx( beta,k) ) = tmp(1) + m%AllOuts( BNSTVy( beta,k) ) = tmp(2) + m%AllOuts( BNSTVz( beta,k) ) = tmp(3) + + m%AllOuts( BNVrel( beta,k) ) = Vrel + m%AllOuts( BNDynP( beta,k) ) = 0.5 * p%airDens * Vrel**2 + m%AllOuts( BNRe( beta,k) ) = Re + m%AllOuts( BNM( beta,k) ) = Vrel / p%SpdSound + + m%AllOuts( BNVIndx(beta,k) ) = -UrelWind_s(1) * AxInd + m%AllOuts( BNVIndy(beta,k) ) = UrelWind_s(2) * TanInd + + m%AllOuts( BNAxInd(beta,k) ) = AxInd + m%AllOuts( BNTnInd(beta,k) ) = TanInd + + m%AllOuts( BNAlpha(beta,k) ) = alpha*R2D + m%AllOuts( BNTheta(beta,k) ) = theta*R2D + m%AllOuts( BNPhi( beta,k) ) = phi*R2D +! m%AllOuts( BNCurve(beta,k) ) = m%Curve(j,k)*R2D + +! m%AllOuts( BNCpmin( beta,k) ) = m%BEMT_y%Cpmin(j,k) + m%AllOuts( BNSigCr( beta,k) ) = m%SigmaCavitCrit(j,k) + m%AllOuts( BNSgCav( beta,k) ) = m%SigmaCavit(j,k) + + cp = cos(phi) + sp = sin(phi) + Cx = AFI_interp%Cl*cp + AFI_interp%Cd*sp + Cy = AFI_interp%Cl*sp - AFI_interp%Cd*cp + m%AllOuts( BNCl( beta,k) ) = AFI_interp%Cl + m%AllOuts( BNCd( beta,k) ) = AFI_interp%Cd + m%AllOuts( BNCm( beta,k) ) = AFI_interp%Cm + m%AllOuts( BNCx( beta,k) ) = Cx + m%AllOuts( BNCy( beta,k) ) = Cy +! + ct=cos(theta) + st=sin(theta) + m%AllOuts( BNCn( beta,k) ) = Cx*ct + Cy*st + m%AllOuts( BNCt( beta,k) ) =-Cx*st + Cy*ct +! + m%AllOuts( BNFl( beta,k) ) = m%X(j,k)*cp - m%Y(j,k)*sp + m%AllOuts( BNFd( beta,k) ) = m%X(j,k)*sp + m%Y(j,k)*cp + m%AllOuts( BNMm( beta,k) ) = m%M(j,k) + m%AllOuts( BNFx( beta,k) ) = m%X(j,k) + m%AllOuts( BNFy( beta,k) ) = -m%Y(j,k) + m%AllOuts( BNFn( beta,k) ) = m%X(j,k)*ct - m%Y(j,k)*st + m%AllOuts( BNFt( beta,k) ) = -m%X(j,k)*st - m%Y(j,k)*ct end do ! nodes end do ! blades @@ -1893,19 +1959,19 @@ subroutine Calc_WriteOutput_FVW ! rmax = 0.0_ReKi ! do k=1,p%NumBlades ! do j=1,p%NumBlNds -! rmax = max(rmax, m%BEMT_u(indx)%rLocal(j,k) ) +! rmax = max(rmax, m%BEMT_u(indx)%rLocal(j,k) ) ! TODO TODO TODO ! end do !j=nodes ! end do !k=blades ! ! m%AllOuts( RtSpeed ) = m%BEMT_u(indx)%omega*RPS2RPM -! m%AllOuts( RtArea ) = pi*rmax**2 +! m%AllOuts( RtArea ) = pi*rmax**2 ! TODO TODO TODO ! -! tmp = matmul( u%HubMotion%Orientation(:,:,1), m%V_DiskAvg ) -! m%AllOuts( RtVAvgxh ) = tmp(1) -! m%AllOuts( RtVAvgyh ) = tmp(2) -! m%AllOuts( RtVAvgzh ) = tmp(3) + tmp = matmul( u%HubMotion%Orientation(:,:,1), m%V_DiskAvg ) + m%AllOuts( RtVAvgxh ) = tmp(1) + m%AllOuts( RtVAvgyh ) = tmp(2) + m%AllOuts( RtVAvgzh ) = tmp(3) ! -! m%AllOuts( RtSkew ) = m%BEMT_u(indx)%chi0*R2D +! m%AllOuts( RtSkew ) = m%BEMT_u(indx)%chi0*R2D ! TODO TODO TODO Not applicable ! integrate force/moments over blades by performing mesh transfer to hub point: force = 0.0_ReKi @@ -1920,27 +1986,27 @@ subroutine Calc_WriteOutput_FVW m%AllOuts( RtAeroFyh ) = tmp(2) m%AllOuts( RtAeroFzh ) = tmp(3) -! tmp = matmul( u%HubMotion%Orientation(:,:,1), moment ) -! m%AllOuts( RtAeroMxh ) = tmp(1) -! m%AllOuts( RtAeroMyh ) = tmp(2) -! m%AllOuts( RtAeroMzh ) = tmp(3) -! -! m%AllOuts( RtAeroPwr ) = m%BEMT_u(indx)%omega * m%AllOuts( RtAeroMxh ) + tmp = matmul( u%HubMotion%Orientation(:,:,1), moment ) + m%AllOuts( RtAeroMxh ) = tmp(1) + m%AllOuts( RtAeroMyh ) = tmp(2) + m%AllOuts( RtAeroMzh ) = tmp(3) ! +! m%AllOuts( RtAeroPwr ) = m%BEMT_u(indx)%omega * m%AllOuts( RtAeroMxh )! TODO TODO TODO ! -! if ( EqualRealNos( m%V_dot_x, 0.0_ReKi ) ) then -! m%AllOuts( RtTSR ) = 0.0_ReKi -! m%AllOuts( RtAeroCp ) = 0.0_ReKi -! m%AllOuts( RtAeroCq ) = 0.0_ReKi -! m%AllOuts( RtAeroCt ) = 0.0_ReKi -! else -! denom = 0.5*p%AirDens*m%AllOuts( RtArea )*m%V_dot_x**2 -! m%AllOuts( RtTSR ) = m%BEMT_u(indx)%omega * rmax / m%V_dot_x ! -! m%AllOuts( RtAeroCp ) = m%AllOuts( RtAeroPwr ) / (denom * m%V_dot_x) -! m%AllOuts( RtAeroCq ) = m%AllOuts( RtAeroMxh ) / (denom * rmax) -! m%AllOuts( RtAeroCt ) = m%AllOuts( RtAeroFxh ) / denom -! end if + if ( EqualRealNos( m%V_dot_x, 0.0_ReKi ) ) then + m%AllOuts( RtTSR ) = 0.0_ReKi + m%AllOuts( RtAeroCp ) = 0.0_ReKi + m%AllOuts( RtAeroCq ) = 0.0_ReKi + m%AllOuts( RtAeroCt ) = 0.0_ReKi + else + ! TODO TODO TODO (need rotor area) +! denom = 0.5*p%AirDens*m%AllOuts( RtArea )*m%V_dot_x**2 +! m%AllOuts( RtTSR ) = m%BEMT_u(indx)%omega * rmax / m%V_dot_x +! m%AllOuts( RtAeroCp ) = m%AllOuts( RtAeroPwr ) / (denom * m%V_dot_x) +! m%AllOuts( RtAeroCq ) = m%AllOuts( RtAeroMxh ) / (denom * rmax) +! m%AllOuts( RtAeroCt ) = m%AllOuts( RtAeroFxh ) / denom + end if end subroutine Calc_WriteOutput_FVW diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 6c83688eae..db1e49f0b9 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -156,7 +156,6 @@ integer function line_count(iunit) !! - Same position of points !! - Same circulation subroutine Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) - use Interpolation, only: interp_lin type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states @@ -644,70 +643,63 @@ subroutine UnPackLiftingLineVelocities() end subroutine -subroutine FVW_AeroOuts( MGlobalToSection, NodeOrient, StructVel, Vind_Glob, DisturbedInflow, KinVisc, Chord, & - AxInd, TanInd, Vrel, phi, alpha, Re, U0, ErrStat, ErrMsg ) - real(ReKi), intent(in ) :: MGlobalToSection(3,3) ! m%WithoutSweepPitchTwist(:,:,j,k) global coord - real(ReKi), intent(in ) :: NodeOrient(3,3) ! u%BladeMotion(k)%Orientation(1:3,1:3,j) global coord - real(ReKi), intent(in ) :: StructVel(3) ! Structural velocity global coord - real(ReKi), intent(in ) :: Vind_Glob(3) ! Induced wind velocity global coord - real(ReKi), intent(in ) :: DisturbedInflow(3) ! Disturbed inflow global coord +!> Compute typical aerodynamic outputs based on: +!! - the lifting line velocities in global coordinates +!! - some transformation matrices +!! - M_ag : from global to airfoil (this is well defined, also called "n-t" system in AeroDyn) +!! - M_sg : from global to section (this is ill-defined), this coordinate is used to define the "axial" and "tangential" inductions +subroutine FVW_AeroOuts( M_sg, M_ag, PitchAndTwist, Vstr_g, Vind_g, Vwnd_g, KinVisc, Chord, & + AxInd, TanInd, Vrel_norm, phi, alpha, Re, Urel_s, ErrStat, ErrMsg ) + real(ReKi), intent(in ) :: M_sg(3,3) ! m%WithoutSweepPitchTwist global coord to "section" coord + real(DbKi), intent(in ) :: M_ag(3,3) ! u%BladeMotion(k)%Orientation(1:3,1:3,j) global coord to airfoil coord + real(ReKi), intent(in ) :: PitchAndTwist ! Pitch and twist of section + real(ReKi), intent(in ) :: Vstr_g(3) ! Structural velocity global coord + real(ReKi), intent(in ) :: Vind_g(3) ! Induced wind velocity global coord + real(ReKi), intent(in ) :: Vwnd_g(3) ! Disturbed inflow global coord real(ReKi), intent(in ) :: KinVisc ! Viscosity real(ReKi), intent(in ) :: Chord ! chord length real(ReKi), intent( out) :: AxInd ! axial induction real(ReKi), intent( out) :: TanInd ! Tangential induction - real(ReKi), intent( out) :: Vrel ! Relative velocity (scalar) + real(ReKi), intent( out) :: Vrel_norm ! Relative velocity norm real(Reki), intent( out) :: phi ! Flow angle real(Reki), intent( out) :: alpha ! angle of attack real(ReKi), intent( out) :: Re ! Reynolds number - real(ReKi), intent( out) :: U0(3) ! Vector of UWidn - VStruct section coord + real(ReKi), intent( out) :: Urel_s(3) ! Relative wind of the airfoil (Vwnd - Vstr) section coord integer(IntKi), intent( out) :: ErrStat character(ErrMsgLen), intent( out) :: ErrMsg ! Local vars - real(ReKi) :: x_hat(3) ! Vector in airfoil cross section, flapwise direction global coord - real(ReKi) :: y_hat(3) ! Vector in airfoil cross section, chordwise direction global coord - real(ReKi) :: VStruct(3) ! Struct Velocity, section coord - real(ReKi) :: VInd(3) ! Induced Velocity, section coord - real(ReKi) :: UWind(3) ! Disturbed wind velocity, section coord - real(ReKi) :: UTotLocal(3) ! Vector of total relative velocity section coord - real(ReKi) :: UTotGlobal(3) ! Vector of total relative velocity global coord - real(ReKi) :: UTotAirfoil(3) ! Vector of total relative velocity airfoil coord - - x_hat = MGlobalToSection(1,:) ! ADP: We want this Airfoil cross section flapwise. Is this it??? - y_hat = MGlobalToSection(2,:) ! ADP: We want this Airfoil cross section chordwise - - ! Section coordinates - VStruct(1)=dot_product(x_hat, StructVel) - VStruct(2)=dot_product(y_hat, StructVel) - VStruct(3)=0.0_ReKi - - ! Section coordinates - VInd(1)=dot_product(Vind_Glob(1:3),x_hat) - VInd(2)=dot_product(Vind_Glob(1:3),y_hat) - Vind(3)=0.0_ReKi - - ! disturbed wind in section coords - UWind(1) =dot_product( DisturbedInflow(:), x_hat) - UWind(2) =dot_product( DisturbedInflow(:), y_hat) - UWind(3) = 0.0_ReKi - - U0 = UWind - VStruct -!FIXME: are these calcs correct? Expect scalar output. - AxInd = norm2(VInd) /U0(1) ! What is the sign here???? - TanInd = norm2(VInd) /U0(2) - - ! Section coordinates - UTotLocal = UWind - VStruct + Vind - Vrel = norm2(UTotLocal) - - phi = atan2( UTotLocal(1), UTotLocal(2) ) ! flow angle - - UTotGlobal = DisturbedInflow(:) - StructVel + Vind(1:3) - UTotAirfoil = matmul(UTotGlobal, NodeOrient ) ! Airfoil coord -!TODO: is there a +/- error here? - alpha = atan2( UTotAirfoil(1), UTotAirfoil(2) ) - - Re = Chord * Vrel / KinVisc / 1.0E6 + real(ReKi) :: Vstr_s(3) ! Struct Velocity, section coord + real(ReKi) :: Vind_s(3) ! Induced Velocity, section coord + real(ReKi) :: Vwnd_s(3) ! Disturbed wind velocity, section coord + real(ReKi) :: Vtot_g(3) ! Vector of total relative velocity section coord + real(ReKi) :: Vtot_a(3) ! Vector of total relative velocity global coord + real(ReKi) :: Vtot_s(3) ! Vector of total relative velocity global coord + !real(DbKi), dimension(3,3) :: M_sa !< Transformation matrix from airfoil to section coord + !real(DbKi), dimension(3,3) :: M_sg2 !< Transformation matrix from global to section coord + ! --- Transformation from airfoil to section (KEEP ME) + !M_sa(1,1:3) = (/ cos(PitchAndTwist*1._DbKi), sin(PitchAndTwist*1._DbKi), 0.0_DbKi /) + !M_sa(2,1:3) = (/ -sin(PitchAndTwist*1._DbKi), cos(PitchAndTwist*1._DbKi), 0.0_DbKi /) + !M_sa(3,1:3) = (/ 0.0_DbKi, 0.0_DbKi, 1.0_DbKi /) + !M_sg= matmul(M_sa, M_ag ) + + ! --- Airfoil coordinates: used to define alpha, and Vrel, alos called "n-t" system + Vtot_g = Vwnd_g - Vstr_g + Vind_g + Vtot_a = matmul(M_ag, Vtot_g) + alpha = atan2( Vtot_a(1), Vtot_a(2) ) + Vrel_norm = sqrt(Vtot_a(1)**2 + Vtot_a(2)**2) ! NOTE: z component shoudn't be used + Re = Chord * Vrel_norm / KinVisc / 1.0E6 + + ! Section coordinates: used to define axial induction andflow angle + Vstr_s = matmul(M_sg, Vstr_g) + Vind_s = matmul(M_sg, Vind_g) + Vwnd_s = matmul(M_sg, Vwnd_g) + Urel_s = Vwnd_s - Vstr_s ! relative wind + Vtot_s = Vwnd_s - Vstr_s + Vind_s + AxInd = -Vind_s(1)/Urel_s(1) + TanInd = Vind_s(2)/Urel_s(2) + phi = atan2( Vtot_s(1), Vtot_s(2) ) ! flow angle + end subroutine FVW_AeroOuts From c59bc05f2ba1f3fe4bf01f5f99b6425308dbb82e Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 27 Jan 2020 23:23:39 -0700 Subject: [PATCH 062/190] FVW: interpolating output on aerodyn nodes, extrap to consider for end points --- modules/aerodyn/src/AeroDyn.f90 | 3 ++- modules/aerodyn/src/AeroDyn_IO.f90 | 4 +++- modules/aerodyn/src/FVW.f90 | 27 +++++++++++++++------------ modules/aerodyn/src/FVW_Wings.f90 | 2 +- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index d1f36c56e2..939a1ef773 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1716,11 +1716,12 @@ subroutine SetOutputsFromFVW(u, p, m, y, ErrStat, ErrMsg) force(3) = 0.0_ReKi moment(1:2) = 0.0_ReKi do k=1,p%NumBlades - do j=2,p%NumBlNds !TODO: Our outputs are on the lifting line! Not at blade nodes! + do j=1,p%NumBlNds call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), m%FVW%PitchAndTwist(j,k), u%BladeMotion(k)%TranslationVel(1:3,j), & m%FVW_y%Vind(1:3,j,k), m%DisturbedInflow(:,j,k) , p%KinVisc, p%FVW%Chord(j,k), & AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') + ! NOTE: using airfoil coeffs at nodes call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') cp = cos(phi) diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index d29641548d..3c5963a034 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1617,7 +1617,7 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) ! blade outputs do k=1,p%numBlades - do j=2,p%NumBlNds ! TODO TODO TODO start at 1 + do j=1,p%NumBlNds !TODO: Merge with BEM to avoid all code redundancy (discuss with Bonnie) i = (k-1)*p%NumBlNds*24 + (j-1)*24 + 1 @@ -1627,6 +1627,7 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), m%FVW%PitchAndTwist(j,k), u%BladeMotion(k)%TranslationVel(1:3,j), & m%FVW_y%Vind(1:3,j,k), Vwnd, p%KinVisc, p%FVW%Chord(j,k), & AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) + ! NOTE: using airfoil coeffs at nodes call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) theta = m%FVW%PitchAndTwist(j,k) @@ -1890,6 +1891,7 @@ subroutine Calc_WriteOutput_FVW Vind(1:3), Vwnd , p%KinVisc, p%FVW%Chord(j,k), & AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) + ! NOTE: using airfoil coeffs at nodes call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) theta = m%FVW%PitchAndTwist(j,k) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 166e232e51..86c8ad8809 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -259,10 +259,8 @@ subroutine FVW_Init_Y( p, u, y, ErrStat, ErrMsg ) nMax = nMax + (FWnSpan+1) * (p%nFWMax+1) * p%nWings ! Far wake points call AllocAry( u%V_wind, 3, nMax, 'Wind Velocity at points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) - !call AllocAry( y%Vind , 3, p%nSpan+1, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO potentially nSpan+1 for AD15 - call AllocAry( y%Vind , 3, p%nSpan+1, 3, 'Induced velocity vector', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) ! NOTE: temporary hack 3 blades -!FIXME: TODO: allocate y%Cl_KJ (2d). Placeholder for now - call AllocAry(y%Cl_KJ , 1, 1, 'Lift coefficient from circulation (Kutta-Joukowski)', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) + call AllocAry( y%Vind , 3, p%nSpan+1, p%nWings, 'Induced velocity vector', ErrStat2, ErrMsg2 ); ! TODO potentially nSpan+1 for AD15 + !call AllocAry( y%Cl_KJ , 1, 1, 'Lift coefficient from circulation (Kutta-Joukowski)', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) if (ErrStat >= AbortErrLev) return y%Vind = 0.0_ReKi return @@ -684,7 +682,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iSpan, iW + integer(IntKi) :: iSpan, iW, n integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'FVW_CalcOutput' @@ -706,15 +704,20 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, m%Vind_LL=-9999.0_ReKi call LiftingLineInducedVelocities(p, x, 1, m, ErrStat2, ErrMsg2); if(Failed()) return - ! Interpolation to AeroDyn radial station TODO TODO TODO -!TODO: interpolate Vind to AD discretization -! Cl, Cd, Cm + ! Induction on the mesh points (AeroDyn nodes) + n=p%nSpan y%Vind(1:3,:,:) = 0.0_ReKi do iW=1,p%nWings - do iSpan=1,p%nSpan -!Interpolate here - y%Vind(1:3,iSpan,iW) = m%Vind_LL(1:3,iSpan,iW) - enddo + ! Linear interpolation for msot points (2:n) + call InterpArray(m%s_CP_LL(:,iW), m%Vind_LL(1,:,iW), m%s_LL(2:n,iW), y%Vind(1,2:n,iW)) + call InterpArray(m%s_CP_LL(:,iW), m%Vind_LL(2,:,iW), m%s_LL(2:n,iW), y%Vind(2,2:n,iW)) + call InterpArray(m%s_CP_LL(:,iW), m%Vind_LL(3,:,iW), m%s_LL(2:n,iW), y%Vind(3,2:n,iW)) + ! Special case for end points (1 and n+1) + y%Vind(1:3, 1 , iW) = 0.0_ReKi ! TODO backward interpolation? + y%Vind(1:3, n+1, iW) = 0.0_ReKi ! TODO forward interpolation? + !do iSpan=1,p%nSpan+1q + !y%Vind(1:3,iSpan,iW) = m%Vind_LL(1:3,iSpan,iW) + !enddo enddo ! For plotting only diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index f8bcd8bf7a..26eb9dc6e8 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -447,7 +447,7 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) Re = p%Chord(icp,iW) * norm2(Vrel) / p%KinVisc / 1.0E6 if (p%CircSolvPolar==idPolarAeroDyn) then - ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. + ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. !TODO: AFindx is on the nodes, not control points. call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, AFInfo(p%AFindx(icp,iW)), AFI_interp, ErrStat2, ErrMsg2 ); if(Failed()) return; Cl = AFI_interp%Cl From 0e430fa530ecf183f24fc1049c90c61dae3be869 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 28 Jan 2020 14:38:23 -0700 Subject: [PATCH 063/190] FVW: input file default values and update of some IDs to start at 1 --- modules/aerodyn/src/FVW.f90 | 15 +--------- modules/aerodyn/src/FVW_IO.f90 | 41 +++++++++++++--------------- modules/aerodyn/src/FVW_Registry.txt | 2 -- modules/aerodyn/src/FVW_Subs.f90 | 19 +++++++------ modules/aerodyn/src/FVW_Types.f90 | 14 ---------- modules/aerodyn/src/FVW_Wings.f90 | 30 +++++++++----------- 6 files changed, 43 insertions(+), 78 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 86c8ad8809..c896716298 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -86,10 +86,6 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu p%nFWMax = max(InputFileData%nFWPanels,0) p%nFWFree = max(InputFileData%nFWPanelsFree,0) - if (InputFileData%HACK==1) then - p%nWings=1 ! Elliptical wing temporary hack - endif - ! Initialize Misc Vars (may depend on input file) CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return @@ -97,18 +93,10 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL MOVE_ALLOC( InitInp%WingsMesh, u%WingsMesh ) ! Move from InitInp to u !NOTE: We do not have the windspeed until after the FVW initialization (IfW is not initialized until after AD15) - ! Wind Speed hack, TODO temporary + ! Wind Speed hack, TODO temporary NOTE: it is still needed? m%Vwnd_LL(:,:,:) = 0 m%Vwnd_NW(:,:,:,:) = 0 m%Vwnd_FW(:,:,:,:) = 0 - m%Vwnd_LL(1,:,:) = InputFileData%Uinf - m%Vwnd_NW(1,:,:,:) = InputFileData%Uinf - m%Vwnd_FW(1,:,:,:) = InputFileData%Uinf - if (InputFileData%HACK==1) then - m%Vwnd_LL(3,:,:) =0.1 - m%Vwnd_NW(3,:,:,:) =0.1 - m%Vwnd_FW(3,:,:,:) =0.1 - endif ! This mesh is passed in as a cousin of the BladeMotion mesh. CALL Wings_Panelling_Init(u%WingsMesh, InitInp%zLocal, p, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -332,7 +320,6 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%WingRegFactor = InputFileData%WingRegFactor p%WrVTK = InputFileData%WrVTK p%VTKBlades = min(max(InputFileData%VTKBlades,0),p%nWings) - p%HACK = InputFileData%HACK if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) if (InputFileData%CirculationMethod==idCircPrescribed) then diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 65a2d31bde..aea16bbded 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -34,42 +34,39 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadCom(UnIn, FileName, 'FVW input file header line 1', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadCom(UnIn, FileName, 'FVW input file header line 2', ErrStat2, ErrMsg2 ); if(Failed()) return !------------------------ GENERAL OPTIONS ------------------------------------------- - CALL ReadCom(UnIn,FileName, 'General option header', ErrStat2, ErrMsg2 ); if(Failed()) return - CALL ReadVar(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%FreeWakeStart ,'FreeWakeStart' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadCom (UnIn,FileName, 'General option header', ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadVarWDefault(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'', idEuler1, ErrStat2,ErrMsg2); if(Failed())return + + CALL ReadVarWDefault(UnIn,FileName,Inp%FreeWakeStart ,'FreeWakeStart' ,'', 0.0_ReKi, ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart','', 0.0_ReKi, ErrStat2,ErrMsg2); if(Failed())return !------------------------ CIRCULATION SPECIFICATIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'Circulation specification header', ErrStat2, ErrMsg2 ); if(Failed()) return - CALL ReadVar(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%CircSolvConvCrit ,'CircSolvConvCrit ' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%CircSolvRelaxation,'CircSolvRelaxation','',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%CircSolvMaxIter ,'CircSolvMaxIter' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%CircSolvPolar ,'CircSolvPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod' ,'', idCircPolarData, ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvConvCrit ,'CircSolvConvCrit ' ,'', 0.01 , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvRelaxation,'CircSolvRelaxation','', 0.1 , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvMaxIter ,'CircSolvMaxIter' ,'', 30 , ErrStat2,ErrMsg2); if(Failed())return + !CALL ReadVar(UnIn,FileName,Inp%CircSolvPolar ,'CircSolvPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return !------------------------ WAKE OPTIONS ------------------------------------------- - CALL ReadCom(UnIn,FileName, 'Wake options header', ErrStat2, ErrMsg2 ); if(Failed()) return - CALL ReadVar(UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%WakeRegFactor ,'WakeRegFactor' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%WingRegFactor ,'WingRegFactor' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadCom (UnIn,FileName, 'Wake options header' , ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadVar (UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar (UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar (UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',idRegCompact , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',idRegConstant, ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar (UnIn,FileName,Inp%WakeRegFactor ,'WakeRegFactor' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar (UnIn,FileName,Inp%WingRegFactor ,'WingRegFactor' ,'' , ErrStat2,ErrMsg2); if(Failed())return !------------------------ OUTPUT OPTIONS ----------------------------------------- CALL ReadCom(UnIn,FileName, 'Output options header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%VTKBlades , 'VTKBlades' ,'',ErrStat2,ErrMsg2); if(Failed())return - !------------------------ HACK OPTIONS ----------------------------------------- - CALL ReadCom(UnIn,FileName, 'Hack options header', ErrStat2, ErrMsg2 ); if(Failed()) return - CALL ReadVar(UnIn,FileName,Inp%Uinf , 'Uinf' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%HACK , 'HACK' ,'',ErrStat2,ErrMsg2); if(Failed())return ! --- Validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) if (Check(.not.(ANY((/idCircPrescribed,idCircPolarData/)==Inp%CirculationMethod)), 'Circulation method not implemented')) return - if (Check( Inp%IntMethod/=idEuler1 , 'Time integration method not implemented')) return + if (Check( Inp%IntMethod/=idEuler1 , 'Time integration method not yet implemented. Use Euler 1st order method for now.')) return if (Check( Inp%nNWPanels<0 , 'Number of near wake panels must be >=0')) return if (Check( Inp%nFWPanels<0 , 'Number of far wake panels must be >=0')) return diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 9fcd19bc00..c222689201 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -139,8 +139,6 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi WingRegFactor - - - "Factor used in the regularization " typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - -typedef ^ ^ ReKi Uinf - - - "TODO TODO TEMPORARY HACK" -typedef ^ ^ IntKi HACK - - - "HACK ID" - #.......... InitOutputType ...... # FVW_InitOutputType diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index db1e49f0b9..864a44655d 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -9,22 +9,23 @@ module FVW_SUBS ! --- Module parameters ! Circulation solving methods - integer(IntKi), parameter :: idCircPolarData = 0 - integer(IntKi), parameter :: idCircNoFlowThrough = 1 - integer(IntKi), parameter :: idCircPrescribed = 2 + integer(IntKi), parameter :: idCircPolarData = 1 + integer(IntKi), parameter :: idCircNoFlowThrough = 2 + integer(IntKi), parameter :: idCircPrescribed = 3 ! Polar data - integer(IntKi), parameter :: idPolarAeroDyn = 0 - integer(IntKi), parameter :: idPolar2PiAlpha = 1 - integer(IntKi), parameter :: idPolar2PiSinAlpha = 2 + !integer(IntKi), parameter :: idPolarAeroDyn = 0 + !integer(IntKi), parameter :: idPolar2PiAlpha = 1 + !integer(IntKi), parameter :: idPolar2PiSinAlpha = 2 ! Integration method integer(IntKi), parameter :: idRK4 = 1 integer(IntKi), parameter :: idAB4 = 2 integer(IntKi), parameter :: idABM4 = 3 + integer(IntKi), parameter :: idPredictor= 4 integer(IntKi), parameter :: idEuler1 = 5 ! Regularization Method - integer(IntKi), parameter :: idRegConstant = 0 - integer(IntKi), parameter :: idRegStretching = 1 - integer(IntKi), parameter :: idRegAge = 2 + integer(IntKi), parameter :: idRegConstant = 1 + integer(IntKi), parameter :: idRegStretching = 2 + integer(IntKi), parameter :: idRegAge = 3 integer(IntKi), parameter, dimension(3) :: idRegMethodVALID = (/idRegConstant,idRegStretching,idRegAge/) ! Implementation diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index bb313b4cd9..b1e0864498 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -170,8 +170,6 @@ MODULE FVW_Types REAL(ReKi) :: WingRegFactor !< Factor used in the regularization [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] - REAL(ReKi) :: Uinf !< TODO TODO TEMPORARY HACK [-] - INTEGER(IntKi) :: HACK !< HACK ID [-] END TYPE FVW_InputFile ! ======================= ! ========= FVW_InitOutputType ======= @@ -4760,8 +4758,6 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%WingRegFactor = SrcInputFileData%WingRegFactor DstInputFileData%WrVTK = SrcInputFileData%WrVTK DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades - DstInputFileData%Uinf = SrcInputFileData%Uinf - DstInputFileData%HACK = SrcInputFileData%HACK END SUBROUTINE FVW_CopyInputFile SUBROUTINE FVW_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) @@ -4829,8 +4825,6 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_BufSz = Re_BufSz + 1 ! WingRegFactor Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades - Re_BufSz = Re_BufSz + 1 ! Uinf - Int_BufSz = Int_BufSz + 1 ! HACK IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4898,10 +4892,6 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades Int_Xferred = Int_Xferred + 1 - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%Uinf - Re_Xferred = Re_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%HACK - Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackInputFile SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4976,10 +4966,6 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%VTKBlades = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 - OutData%Uinf = ReKiBuf( Re_Xferred ) - Re_Xferred = Re_Xferred + 1 - OutData%HACK = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackInputFile SUBROUTINE FVW_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 26eb9dc6e8..c5e5b72347 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -109,10 +109,6 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) do iW = 1,p%nWings do iSpan = 1,p%nSpan+1 P_ref = Meshes(iW)%Position(1:3, iSpan )+Meshes(iW)%TranslationDisp(1:3, iSpan) - if (p%HACK==1) then - P_ref(3)=100 - P_ref(1)=0 - endif DP_LE(1:3) = 0.0 DP_LE(1) = -m%chord_LL(iSpan,iW)/4. ! TODO TODO TODO Use orientation and might not be c/2 DP_TE(1:3) = 0.0 @@ -446,21 +442,21 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) alpha = atan2(dot_product(Vrel,N) , dot_product(Vrel,Tc) ) ! [rad] Re = p%Chord(icp,iW) * norm2(Vrel) / p%KinVisc / 1.0E6 - if (p%CircSolvPolar==idPolarAeroDyn) then + !if (p%CircSolvPolar==idPolarAeroDyn) then ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. !TODO: AFindx is on the nodes, not control points. - call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, AFInfo(p%AFindx(icp,iW)), AFI_interp, ErrStat2, ErrMsg2 ); if(Failed()) return; - Cl = AFI_interp%Cl - Cd = AFI_interp%Cd - Cm = AFI_interp%Cm - else if (p%CircSolvPolar==idPolar2PiAlpha) then - Cl=TwoPi*alpha - else if (p%CircSolvPolar==idPolar2PiSinAlpha) then - Cl=TwoPi*sin(alpha) - else - print*,'Unknown CircSolvPolar value' - STOP - endif + call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, AFInfo(p%AFindx(icp,iW)), AFI_interp, ErrStat2, ErrMsg2 ); if(Failed()) return; + Cl = AFI_interp%Cl + Cd = AFI_interp%Cd + Cm = AFI_interp%Cm + !else if (p%CircSolvPolar==idPolar2PiAlpha) then + ! Cl=TwoPi*alpha + !else if (p%CircSolvPolar==idPolar2PiSinAlpha) then + ! Cl=TwoPi*sin(alpha) + !else + ! print*,'Unknown CircSolvPolar value' + ! STOP + !endif ! Simple method: ! Gamma_LL=(0.5 * Cl * Vrel_orth_norm*chord) ! VanGarrel's method: From 7abd1d076a2bde1a53325cc85eabd0377fa6f615 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 11 Feb 2020 11:56:29 -0700 Subject: [PATCH 064/190] FVW: add DTfvw option --- cmake/OpenfastFortranOptions.cmake | 4 +-- modules/aerodyn/src/FVW.f90 | 51 +++++++++++++++++----------- modules/aerodyn/src/FVW_IO.f90 | 1 + modules/aerodyn/src/FVW_Registry.txt | 4 +++ modules/aerodyn/src/FVW_Types.f90 | 28 +++++++++++++++ 5 files changed, 66 insertions(+), 22 deletions(-) diff --git a/cmake/OpenfastFortranOptions.cmake b/cmake/OpenfastFortranOptions.cmake index 786217cf8f..9cc1447386 100644 --- a/cmake/OpenfastFortranOptions.cmake +++ b/cmake/OpenfastFortranOptions.cmake @@ -83,7 +83,7 @@ macro(set_fast_gfortran) endif(NOT WIN32) # Fix free-form compilation for OpenFAST - set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -ffree-line-length-none -cpp") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -ffree-line-length-none -cpp -fopenmp") # Deal with Double/Single precision if (DOUBLE_PRECISION) @@ -93,7 +93,7 @@ macro(set_fast_gfortran) # debug flags if(CMAKE_BUILD_TYPE MATCHES Debug) - set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -fcheck=all -pedantic -fbacktrace" ) + set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -fcheck=all -pedantic -fbacktrace -fopenmp" ) endif() if(CYGWIN) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 86c8ad8809..d9cec2d0df 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -85,6 +85,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu p%nNWMax = max(InputFileData%nNWPanels,0)+1 ! +1 since LL panel included in NW p%nFWMax = max(InputFileData%nFWPanels,0) p%nFWFree = max(InputFileData%nFWPanelsFree,0) + p%DTfvw = InputFileData%DTfvw if (InputFileData%HACK==1) then p%nWings=1 ! Elliptical wing temporary hack @@ -406,10 +407,10 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m type(FVW_InputType), intent(inout) :: u(:) !< Inputs at utimes (out only for mesh record-keeping in ExtrapInterp routine) real(DbKi), intent(in ) :: utimes(:) !< Times associated with u(:), in seconds type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_ContinuousStateType), intent(inout) :: x !< Input: Continuous states at t; Output: at t+dt - type(FVW_DiscreteStateType), intent(inout) :: xd !< Input: Discrete states at t; Output: at t+dt - type(FVW_ConstraintStateType), intent(inout) :: z !< Input: Constraint states at t; Output: at t+dt - type(FVW_OtherStateType), intent(inout) :: OtherState !< Input: Other states at t; Output: at t+dt + type(FVW_ContinuousStateType), intent(inout) :: x !< Input: Continuous states at t; Output: at t+dtaero + type(FVW_DiscreteStateType), intent(inout) :: xd !< Input: Discrete states at t; Output: at t+dtaero + type(FVW_ConstraintStateType), intent(inout) :: z !< Input: Constraint states at t; Output: at t+dtaero + type(FVW_OtherStateType), intent(inout) :: OtherState !< Input: Other states at t; Output: at t+dtaero type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data type(FVW_MiscVarType), intent(inout) :: m !< Misc/optimization variables integer(IntKi), intent( out) :: errStat !< Error status of the operation @@ -420,17 +421,26 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m character(ErrMsgLen) :: ErrMsg2 ! temporary Error message type(FVW_ConstraintStateType) :: z_guess ! < integer(IntKi) :: iW, iSpan, iAge - real(ReKi) :: dt + real(ReKi) :: dtaero + real(DbKi), parameter :: OnePlusEpsilon = 1 + EPSILON(t) ErrStat = ErrID_None ErrMsg = "" -!FIXME: why not use p%DT? DT from AD15 does not change. - dt=utimes(1)-t ! TODO TODO TODO + ! dtaero is the timestep that FVW is called at (DTaero of AD15) + dtaero=utimes(1)-t ! TODO TODO TODO m%iStepPrev = m%iStep m%iStep = n - print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' dt: ',dt,' ',n,' nNW:',m%nNW,' nFW:',m%nFW + if ( ( t*OnePlusEpsilon - m%OldTime ) >= p%DTfvw ) then + m%OldTime = t + m%ComputeWakeInduced = .TRUE. ! It's time to update the induced velocities from wake + else + m%ComputeWakeInduced = .FALSE. + endif + + + print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' dtaero: ',dtaero,' ',n,' nNW:',m%nNW,' nFW:',m%nFW ! We don't propagate the "Old"-> "New" if we we are not taking another timestep (such as during a correction step) if (m%iStep /= m%iStepPrev) then @@ -460,9 +470,9 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Map_') - ! --- Integration between t and t+dt + ! --- Integration between t and t+dtaero if (p%IntMethod .eq. idEuler1) then - call FVW_Euler1( t, dt, uInterp, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return + call FVW_Euler1( t, dtaero, uInterp, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return !elseif (p%IntMethod .eq. idRK4) then ! call FVW_RK4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) !elseif (p%IntMethod .eq. idAB4) then @@ -475,15 +485,15 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m !call print_x_NW_FW(p, m, z, x,'Conv') - ! --- t+dt + ! --- t+dtaero ! Propagation/creation of new layer of panels call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Prop_') - ! Inputs at t+dt - call FVW_Input_ExtrapInterp(u(1:size(utimes)),utimes,uInterp,t+dt, ErrStat2, ErrMsg2); if(Failed()) return + ! Inputs at t+dtaero + call FVW_Input_ExtrapInterp(u(1:size(utimes)),utimes,uInterp,t+dtaero, ErrStat2, ErrMsg2); if(Failed()) return - ! Panelling wings based on input mesh at t+dt + ! Panelling wings based on input mesh at t+dtaero call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Updating positions of first NW and FW panels (Circulation also updated but irrelevant) @@ -492,10 +502,10 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Map2') - ! --- Solve for circulation at t+dt - ! Returns: z%Gamma_LL (at t+dt) + ! --- Solve for circulation at t+dtaero + ! Returns: z%Gamma_LL (at t+dtaero) z_guess%Gamma_LL = z%Gamma_LL ! We use as guess the circulation from the previous time step (see above) - call FVW_CalcConstrStateResidual(t+dt, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 2); if(Failed()) return + call FVW_CalcConstrStateResidual(t+dtaero, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 2); if(Failed()) return ! Updating circulation of near wake panel (and position but irrelevant) ! Changes: x only @@ -503,7 +513,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Map3') - ! set the wind points required for t+dt timestep + ! set the wind points required for t+dtaero timestep CALL SetRequestedWindPoints(m%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return if (m%FirstCall) then @@ -555,13 +565,14 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt call AllocAry( dxdt%r_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat, ErrMsg ); dxdt%r_NW= -999999_ReKi; call AllocAry( dxdt%r_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; - if (t> p%FreeWakeStart) then + ! Only calculate freewake after start time and if on a timestep when it should be calculated. + if (t> p%FreeWakeStart .and. m%ComputeWakeInduced) then nFWEff = min(m%nFW, p%nFWFree) ! --- Compute Induced velocities on the Near wake and far wake based on the marker postions: ! (expensive N^2 call) ! In : x%r_NW, r%r_FW - ! Out: m%Vind_NW, m%Vind_FW + ! Out: m%Vind_NW, m%Vind_FW call WakeInducedVelocities(p, x, m, ErrStat2, ErrMsg2) m%Vind_FW(1:3, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) =0 ! Ensuring no convection velocity for panels that user doesnt want free diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 65a2d31bde..62e211841d 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -38,6 +38,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%FreeWakeStart ,'FreeWakeStart' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,Inp%DTfvw ,'DTfvw' ,'',ErrStat2,ErrMsg2); if(Failed())return !------------------------ CIRCULATION SPECIFICATIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'Circulation specification header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod' ,'',ErrStat2,ErrMsg2); if(Failed())return diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 9fcd19bc00..9b44c8b4ad 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -33,6 +33,7 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ IntKi HACK - - - "HACK ID" - typedef ^ ^ DbKi DT - - - "Time interval for calls calculations" s +typedef ^ ^ DbKi DTfvw - - - "Time interval for calculating wake induced velocities" s typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s # ....... OtherStateType ............ @@ -72,6 +73,8 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi iStepPrev - - - "Previous step number" - typedef ^ ^ ReKi r_wind :: - - "List of points where wind is requested for next time step" - typedef ^ ^ ReKi PitchAndTwist :: - - "Twist angle (includes all sources of twist) [Array of size (NumBlNds,numBlades)]" rad +typedef ^ ^ Logical ComputeWakeInduced - - - "Compute induced velocities on this timestep" - +typedef ^ ^ DbKi OldTime - - - "Time the wake induction velocities were last calculated" s # ........ Input ............ # FVW_InputType @@ -129,6 +132,7 @@ typedef ^ ^ IntKi typedef ^ ^ LOGICAL FreeWake - - - "Disable roll up, wake convects with wind only (flag)" - typedef ^ ^ ReKi FreeWakeStart - - - "Time when wake starts convecting (rolling up)" s typedef ^ ^ ReKi FullCirculationStart - - - "Time when the circulation is full" s +typedef ^ ^ DbKi DTfvw - - - "Time interval for calculating wake induced velocities" s typedef ^ ^ IntKi CircSolvPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - typedef ^ ^ IntKi nNWPanels - - - "Number of nw panels" - typedef ^ ^ IntKi nFWPanels - - - "Number of fw panels" - diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index bb313b4cd9..6675ed29cf 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -60,6 +60,7 @@ MODULE FVW_Types INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] INTEGER(IntKi) :: HACK !< HACK ID [-] REAL(DbKi) :: DT !< Time interval for calls calculations [s] + REAL(DbKi) :: DTfvw !< Time interval for calculating wake induced velocities [s] REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] END TYPE FVW_ParameterType ! ======================= @@ -99,6 +100,8 @@ MODULE FVW_Types INTEGER(IntKi) :: iStepPrev !< Previous step number [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: r_wind !< List of points where wind is requested for next time step [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: PitchAndTwist !< Twist angle (includes all sources of twist) [Array of size (NumBlNds,numBlades)] [rad] + LOGICAL :: ComputeWakeInduced !< Compute induced velocities on this timestep [-] + REAL(DbKi) :: OldTime !< Time the wake induction velocities were last calculated [s] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -160,6 +163,7 @@ MODULE FVW_Types LOGICAL :: FreeWake !< Disable roll up, wake convects with wind only (flag) [-] REAL(ReKi) :: FreeWakeStart !< Time when wake starts convecting (rolling up) [s] REAL(ReKi) :: FullCirculationStart !< Time when the circulation is full [s] + REAL(DbKi) :: DTfvw !< Time interval for calculating wake induced velocities [s] INTEGER(IntKi) :: CircSolvPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] INTEGER(IntKi) :: nNWPanels !< Number of nw panels [-] INTEGER(IntKi) :: nFWPanels !< Number of fw panels [-] @@ -259,6 +263,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%VTKBlades = SrcParamData%VTKBlades DstParamData%HACK = SrcParamData%HACK DstParamData%DT = SrcParamData%DT + DstParamData%DTfvw = SrcParamData%DTfvw DstParamData%KinVisc = SrcParamData%KinVisc END SUBROUTINE FVW_CopyParam @@ -353,6 +358,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! VTKBlades Int_BufSz = Int_BufSz + 1 ! HACK Db_BufSz = Db_BufSz + 1 ! DT + Db_BufSz = Db_BufSz + 1 ! DTfvw Re_BufSz = Re_BufSz + 1 ! KinVisc IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) @@ -468,6 +474,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DT Db_Xferred = Db_Xferred + 1 + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTfvw + Db_Xferred = Db_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%KinVisc Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_PackParam @@ -625,6 +633,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%DT = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 + OutData%DTfvw = DbKiBuf( Db_Xferred ) + Db_Xferred = Db_Xferred + 1 OutData%KinVisc = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_UnPackParam @@ -1161,6 +1171,8 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) END IF DstMiscData%PitchAndTwist = SrcMiscData%PitchAndTwist ENDIF + DstMiscData%ComputeWakeInduced = SrcMiscData%ComputeWakeInduced + DstMiscData%OldTime = SrcMiscData%OldTime END SUBROUTINE FVW_CopyMisc SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -1406,6 +1418,8 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*2 ! PitchAndTwist upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%PitchAndTwist) ! PitchAndTwist END IF + Int_BufSz = Int_BufSz + 1 ! ComputeWakeInduced + Db_BufSz = Db_BufSz + 1 ! OldTime IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -1890,6 +1904,10 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%PitchAndTwist)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%PitchAndTwist))-1 ) = PACK(InData%PitchAndTwist,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%PitchAndTwist) END IF + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%ComputeWakeInduced , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%OldTime + Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2625,6 +2643,10 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%PitchAndTwist) DEALLOCATE(mask2) END IF + OutData%ComputeWakeInduced = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + OutData%OldTime = DbKiBuf( Db_Xferred ) + Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) @@ -4750,6 +4772,7 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%FreeWake = SrcInputFileData%FreeWake DstInputFileData%FreeWakeStart = SrcInputFileData%FreeWakeStart DstInputFileData%FullCirculationStart = SrcInputFileData%FullCirculationStart + DstInputFileData%DTfvw = SrcInputFileData%DTfvw DstInputFileData%CircSolvPolar = SrcInputFileData%CircSolvPolar DstInputFileData%nNWPanels = SrcInputFileData%nNWPanels DstInputFileData%nFWPanels = SrcInputFileData%nFWPanels @@ -4819,6 +4842,7 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! FreeWake Re_BufSz = Re_BufSz + 1 ! FreeWakeStart Re_BufSz = Re_BufSz + 1 ! FullCirculationStart + Db_BufSz = Db_BufSz + 1 ! DTfvw Int_BufSz = Int_BufSz + 1 ! CircSolvPolar Int_BufSz = Int_BufSz + 1 ! nNWPanels Int_BufSz = Int_BufSz + 1 ! nFWPanels @@ -4878,6 +4902,8 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_Xferred = Re_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%FullCirculationStart Re_Xferred = Re_Xferred + 1 + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTfvw + Db_Xferred = Db_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%CircSolvPolar Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nNWPanels @@ -4956,6 +4982,8 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Re_Xferred = Re_Xferred + 1 OutData%FullCirculationStart = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%DTfvw = DbKiBuf( Db_Xferred ) + Db_Xferred = Db_Xferred + 1 OutData%CircSolvPolar = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%nNWPanels = IntKiBuf( Int_Xferred ) From 894a34677b504f2110f2b1a60b8dedd41252d476 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 11 Feb 2020 12:05:57 -0700 Subject: [PATCH 065/190] FVW: bug in orientation matrix kind --- modules/aerodyn/src/FVW_Subs.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 864a44655d..0f0a98fdc5 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -652,7 +652,7 @@ subroutine UnPackLiftingLineVelocities() subroutine FVW_AeroOuts( M_sg, M_ag, PitchAndTwist, Vstr_g, Vind_g, Vwnd_g, KinVisc, Chord, & AxInd, TanInd, Vrel_norm, phi, alpha, Re, Urel_s, ErrStat, ErrMsg ) real(ReKi), intent(in ) :: M_sg(3,3) ! m%WithoutSweepPitchTwist global coord to "section" coord - real(DbKi), intent(in ) :: M_ag(3,3) ! u%BladeMotion(k)%Orientation(1:3,1:3,j) global coord to airfoil coord + real(R8Ki), intent(in ) :: M_ag(3,3) ! u%BladeMotion(k)%Orientation(1:3,1:3,j) global coord to airfoil coord real(ReKi), intent(in ) :: PitchAndTwist ! Pitch and twist of section real(ReKi), intent(in ) :: Vstr_g(3) ! Structural velocity global coord real(ReKi), intent(in ) :: Vind_g(3) ! Induced wind velocity global coord From 2a6f6acd520c74a283ccbc1212b91423141fad6e Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 12 Feb 2020 09:59:55 -0700 Subject: [PATCH 066/190] FVW: finalize DTfvw for only calculating interaction terms on DTfvw timesteps --- modules/aerodyn/src/FVW.f90 | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 714c5b620d..cd2c6e1e1d 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -185,6 +185,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) nMax = nMax + (FWnSpan+1) * (p%nFWMax+1) * p%nWings ! Far wake points call AllocAry( m%r_wind, 3, nMax, 'Requested wind points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ) m%r_wind = 0.0_ReKi ! set to zero so InflowWind can shortcut calculations + m%OldTime = 0.0_DbKi-p%DTfvw ! First time for wake interaction calculations end subroutine FVW_InitMiscVars ! ============================================================================== subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) @@ -228,7 +229,6 @@ subroutine FVW_InitConstraint( z, p, m, ErrStat, ErrMsg ) end subroutine FVW_InitConstraint ! ============================================================================== subroutine FVW_Init_Y( p, u, y, ErrStat, ErrMsg ) -!TODO: move the r_wind to Miscvars type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_InputType), intent(inout) :: u !< An initial guess for the input; input mesh must be defined type(FVW_OutputType), intent( out) :: y !< Constraints @@ -409,17 +409,21 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m type(FVW_ConstraintStateType) :: z_guess ! < integer(IntKi) :: iW, iSpan, iAge real(ReKi) :: dtaero - real(DbKi), parameter :: OnePlusEpsilon = 1 + EPSILON(t) + real(DbKi), parameter :: OneMinusEpsilon = 1 - 10000*EPSILON(t) ErrStat = ErrID_None ErrMsg = "" ! dtaero is the timestep that FVW is called at (DTaero of AD15) - dtaero=utimes(1)-t ! TODO TODO TODO - m%iStepPrev = m%iStep - m%iStep = n + dtaero=utimes(1)-t + ! Increment step if we are not on a correction step (n also wouldn't change) + if (t > m%OldTime) then + m%iStepPrev = m%iStep + m%iStep = n + endif - if ( ( t*OnePlusEpsilon - m%OldTime ) >= p%DTfvw ) then + ! Compute Inuced wake effects only if time since last compute is > DTfvw + if ( ( t - m%OldTime ) >= p%DTfvw*OneMinusEpsilon ) then m%OldTime = t m%ComputeWakeInduced = .TRUE. ! It's time to update the induced velocities from wake else @@ -430,7 +434,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' dtaero: ',dtaero,' ',n,' nNW:',m%nNW,' nFW:',m%nFW ! We don't propagate the "Old"-> "New" if we we are not taking another timestep (such as during a correction step) - if (m%iStep /= m%iStepPrev) then + if ((m%iStep /= m%iStepPrev) .and. m%ComputeWakeInduced) then call PrepareNextTimeStep() endif @@ -472,10 +476,12 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m !call print_x_NW_FW(p, m, z, x,'Conv') - ! --- t+dtaero - ! Propagation/creation of new layer of panels - call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2) - !call print_x_NW_FW(p, m, z, x,'Prop_') + if (m%ComputeWakeInduced) then + ! --- t+dtaero + ! Propagation/creation of new layer of panels + call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2) + !call print_x_NW_FW(p, m, z, x,'Prop_') + endif ! Inputs at t+dtaero call FVW_Input_ExtrapInterp(u(1:size(utimes)),utimes,uInterp,t+dtaero, ErrStat2, ErrMsg2); if(Failed()) return @@ -553,9 +559,9 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt call AllocAry( dxdt%r_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; ! Only calculate freewake after start time and if on a timestep when it should be calculated. - if (t> p%FreeWakeStart .and. m%ComputeWakeInduced) then + if ((t> p%FreeWakeStart) .and. m%ComputeWakeInduced) then nFWEff = min(m%nFW, p%nFWFree) - + ! --- Compute Induced velocities on the Near wake and far wake based on the marker postions: ! (expensive N^2 call) ! In : x%r_NW, r%r_FW From 282cd17721efa635dbb1045dcfa8cfd3e3c6f44e Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 12 Feb 2020 11:47:44 -0700 Subject: [PATCH 067/190] FVW: add option to reduce VTK output frequency --- modules/aerodyn/src/AeroDyn.f90 | 2 +- modules/aerodyn/src/FVW.f90 | 71 +++++++++++++--------------- modules/aerodyn/src/FVW_IO.f90 | 47 ++++++++++++++++-- modules/aerodyn/src/FVW_Registry.txt | 16 ++++--- modules/aerodyn/src/FVW_Types.f90 | 68 +++++++++++++++----------- modules/aerodyn/src/FVW_Wings.f90 | 2 +- 6 files changed, 130 insertions(+), 76 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 939a1ef773..3146f13b3d 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -2228,7 +2228,7 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m InitInp%FVWFileName = InputFileData%FVWFileName InitInp%numBlades = p%numBlades InitInp%numBladeNodes = p%numBlNds - InitInp%DT = p%DT ! NOTE: if we subcycle FVW, this will need modification + InitInp%DTaero = p%DT ! NOTE: FVW can run a lower timestep internally InitInp%KinVisc = p%KinVisc ! NOTE: The following are not meshes diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index cd2c6e1e1d..1d65b765ae 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -29,6 +29,7 @@ MODULE FVW PUBLIC :: FVW_CalcOutput PUBLIC :: FVW_UpdateStates + real(DbKi), parameter :: OneMinusEpsilon = 1 - 10000*EPSILON(1.0_DbKi) CONTAINS @@ -86,6 +87,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu p%nFWMax = max(InputFileData%nFWPanels,0) p%nFWFree = max(InputFileData%nFWPanelsFree,0) p%DTfvw = InputFileData%DTfvw + p%DTvtk = InputFileData%DTvtk ! Initialize Misc Vars (may depend on input file) CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return @@ -150,8 +152,8 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%FirstCall = .True. m%nNW = iNWStart-1 ! Number of active nearwake panels m%nFW = 0 ! Number of active farwake panels - m%iStep = 0 ! Current step number - m%iStepPrev = 0 ! Previous step number (used to check if a correction step is calculated) + m%VTKstep = 0 ! Current step number for vtk output + m%VTKlastTime = 0.0_DbKi - p%DTvtk ! set negative so we ouput first vtk files call AllocAry( m%LE , 3 , p%nSpan+1 , p%nWings, 'Leading Edge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%LE = -999999_ReKi; call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%TE = -999999_ReKi; @@ -185,7 +187,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) nMax = nMax + (FWnSpan+1) * (p%nFWMax+1) * p%nWings ! Far wake points call AllocAry( m%r_wind, 3, nMax, 'Requested wind points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ) m%r_wind = 0.0_ReKi ! set to zero so InflowWind can shortcut calculations - m%OldTime = 0.0_DbKi-p%DTfvw ! First time for wake interaction calculations + m%OldWakeTime = 0.0_DbKi-p%DTfvw ! First time for wake interaction calculations. Set negative so calculate at T=0 end subroutine FVW_InitMiscVars ! ============================================================================== subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) @@ -279,7 +281,7 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) p%nSpan = InitInp%numBladeNodes-1 ! Set time step - p%DT = InitInp%DT + p%DTaero = InitInp%DTaero ! Kinematic air viscosity p%KinVisc = InitInp%KinVisc @@ -394,10 +396,10 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m type(FVW_InputType), intent(inout) :: u(:) !< Inputs at utimes (out only for mesh record-keeping in ExtrapInterp routine) real(DbKi), intent(in ) :: utimes(:) !< Times associated with u(:), in seconds type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_ContinuousStateType), intent(inout) :: x !< Input: Continuous states at t; Output: at t+dtaero - type(FVW_DiscreteStateType), intent(inout) :: xd !< Input: Discrete states at t; Output: at t+dtaero - type(FVW_ConstraintStateType), intent(inout) :: z !< Input: Constraint states at t; Output: at t+dtaero - type(FVW_OtherStateType), intent(inout) :: OtherState !< Input: Other states at t; Output: at t+dtaero + type(FVW_ContinuousStateType), intent(inout) :: x !< Input: Continuous states at t; Output: at t+DTaero + type(FVW_DiscreteStateType), intent(inout) :: xd !< Input: Discrete states at t; Output: at t+DTaero + type(FVW_ConstraintStateType), intent(inout) :: z !< Input: Constraint states at t; Output: at t+DTaero + type(FVW_OtherStateType), intent(inout) :: OtherState !< Input: Other states at t; Output: at t+DTaero type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data type(FVW_MiscVarType), intent(inout) :: m !< Misc/optimization variables integer(IntKi), intent( out) :: errStat !< Error status of the operation @@ -408,33 +410,24 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m character(ErrMsgLen) :: ErrMsg2 ! temporary Error message type(FVW_ConstraintStateType) :: z_guess ! < integer(IntKi) :: iW, iSpan, iAge - real(ReKi) :: dtaero - real(DbKi), parameter :: OneMinusEpsilon = 1 - 10000*EPSILON(t) ErrStat = ErrID_None ErrMsg = "" - ! dtaero is the timestep that FVW is called at (DTaero of AD15) - dtaero=utimes(1)-t - ! Increment step if we are not on a correction step (n also wouldn't change) - if (t > m%OldTime) then - m%iStepPrev = m%iStep - m%iStep = n - endif ! Compute Inuced wake effects only if time since last compute is > DTfvw - if ( ( t - m%OldTime ) >= p%DTfvw*OneMinusEpsilon ) then - m%OldTime = t + if ( ( t - m%OldWakeTime ) >= p%DTfvw*OneMinusEpsilon ) then + m%OldWakeTime = t m%ComputeWakeInduced = .TRUE. ! It's time to update the induced velocities from wake else m%ComputeWakeInduced = .FALSE. endif - print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' dtaero: ',dtaero,' ',n,' nNW:',m%nNW,' nFW:',m%nFW + print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' p%DTaero: ',p%DTaero,' ',n,' nNW:',m%nNW,' nFW:',m%nFW - ! We don't propagate the "Old"-> "New" if we we are not taking another timestep (such as during a correction step) - if ((m%iStep /= m%iStepPrev) .and. m%ComputeWakeInduced) then + ! We don't propagate the "Old"-> "New" if we we are not calculating wake interaction + if (m%ComputeWakeInduced) then call PrepareNextTimeStep() endif @@ -461,9 +454,9 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Map_') - ! --- Integration between t and t+dtaero + ! --- Integration between t and t+DTaero if (p%IntMethod .eq. idEuler1) then - call FVW_Euler1( t, dtaero, uInterp, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return + call FVW_Euler1( t, uInterp, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return !elseif (p%IntMethod .eq. idRK4) then ! call FVW_RK4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) !elseif (p%IntMethod .eq. idAB4) then @@ -477,16 +470,16 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m if (m%ComputeWakeInduced) then - ! --- t+dtaero + ! --- t+DTaero ! Propagation/creation of new layer of panels call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Prop_') endif - ! Inputs at t+dtaero - call FVW_Input_ExtrapInterp(u(1:size(utimes)),utimes,uInterp,t+dtaero, ErrStat2, ErrMsg2); if(Failed()) return + ! Inputs at t+DTaero + call FVW_Input_ExtrapInterp(u(1:size(utimes)),utimes,uInterp,t+p%DTaero, ErrStat2, ErrMsg2); if(Failed()) return - ! Panelling wings based on input mesh at t+dtaero + ! Panelling wings based on input mesh at t+p%DTaero call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Updating positions of first NW and FW panels (Circulation also updated but irrelevant) @@ -495,10 +488,10 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Map2') - ! --- Solve for circulation at t+dtaero - ! Returns: z%Gamma_LL (at t+dtaero) + ! --- Solve for circulation at t+p%DTaero + ! Returns: z%Gamma_LL (at t+p%DTaero) z_guess%Gamma_LL = z%Gamma_LL ! We use as guess the circulation from the previous time step (see above) - call FVW_CalcConstrStateResidual(t+dtaero, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 2); if(Failed()) return + call FVW_CalcConstrStateResidual(t+p%DTaero, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 2); if(Failed()) return ! Updating circulation of near wake panel (and position but irrelevant) ! Changes: x only @@ -506,7 +499,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) !call print_x_NW_FW(p, m, z, x,'Map3') - ! set the wind points required for t+dtaero timestep + ! set the wind points required for t+p%DTaero timestep CALL SetRequestedWindPoints(m%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return if (m%FirstCall) then @@ -595,9 +588,8 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt end subroutine FVW_CalcContStateDeriv !---------------------------------------------------------------------------------------------------------------------------------- -subroutine FVW_Euler1( t, dt, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) +subroutine FVW_Euler1( t, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) real(DbKi), intent(in ) :: t !< Current simulation time in seconds - real(ReKi), intent(in ) :: dt !< Time step type(FVW_InputType), intent(in ) :: u !< Input at t type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states at t on input at t + dt on output @@ -610,10 +602,12 @@ subroutine FVW_Euler1( t, dt, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) ! local variables type(FVW_ContinuousStateType) :: dxdt ! time derivatives of continuous states integer(IntKi) :: iAge + real(ReKi) :: dt ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" + dt = real(p%DTaero,ReKi) ! Compute "right hand side" CALL FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat, ErrMsg ) @@ -729,13 +723,16 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') call print_mean_3d(m%Vtot_LL,'Mean relativevel. LL') - ! --- Write to VTK -!FIXME: This should be glue code level (all VTK output writing is done there) + ! --- Write to local VTK at fps requested if (p%WrVTK==1) then if (m%FirstCall) then call MKDIR('vtk_out') endif - call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%iStep, 9) + if ( ( t - m%VTKlastTime ) >= p%DTvtk*OneMinusEpsilon ) then + m%VTKlastTime = t + call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%VTKstep, 9) + m%VTKstep = m%VTKstep + 1 ! Increment VTK counter + endif endif diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 7c89a06454..0da8c2a377 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -18,8 +18,9 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) ! Local variables integer :: iLine real(ReKi) :: TODO_Re - character(1024) :: PriPath ! the path to the primary input file - character(1024) :: line ! string to temporarially hold value of read line + character(1024) :: PriPath ! the path to the primary input file + character(1024) :: VTK_fps_line ! string to temporarially hold value of read line for VTK_fps + integer(IntKi) :: LineLen integer(IntKi) :: UnIn integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 @@ -58,9 +59,10 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar (UnIn,FileName,Inp%WakeRegFactor ,'WakeRegFactor' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%WingRegFactor ,'WingRegFactor' ,'' , ErrStat2,ErrMsg2); if(Failed())return !------------------------ OUTPUT OPTIONS ----------------------------------------- - CALL ReadCom(UnIn,FileName, 'Output options header', ErrStat2, ErrMsg2 ); if(Failed()) return + CALL ReadCom(UnIn,FileName, 'Output options header', ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%VTKBlades , 'VTKBlades' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar(UnIn,FileName,VTK_fps_line , 'VTK_fps' ,'',ErrStat2,ErrMsg2); if(Failed())return ! --- Validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) @@ -69,6 +71,8 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check( Inp%IntMethod/=idEuler1 , 'Time integration method not yet implemented. Use Euler 1st order method for now.')) return + if (Check( Inp%DTfvw < p%DTaero, 'DTfvw must be >= DTaero from AD15.')) return + if (Check( Inp%nNWPanels<0 , 'Number of near wake panels must be >=0')) return if (Check( Inp%nFWPanels<0 , 'Number of far wake panels must be >=0')) return if (Check( Inp%nFWPanelsFree<0 , 'Number of free far wake panels must be >=0')) return @@ -79,6 +83,8 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check(Inp%WakeRegFactor<0 , 'Wake regularization factor should be positive')) return if (Check(Inp%WingRegFactor<0 , 'Wing regularization factor should be positive')) return + Inp%DTvtk = Get_DTvtk( VTK_fps_line, p%DTaero, Inp%DTfvw ) + ! At least one NW panel if FW, this shoudln't be a problem since the LL is in NW, but safety for now !if (Check( (Inp%nNWPanels<=0).and.(Inp%nFWPanels>0) , 'At least one near wake panel is required if the number of far wake panel is >0')) return call CleanUp() @@ -104,6 +110,41 @@ subroutine CleanUp() close( UnIn ) end subroutine + real(DbKi) function Get_DTvtk( VTK_fps_line, DTaero, DTfvw ) + character(len=*), intent(inout) :: VTK_fps_line + real(DbKi), intent(in ) :: DTaero + real(DbKi), intent(in ) :: DTfvw + real(DbKi) :: VTK_fps + integer(IntKi) :: IOS + integer(IntKi) :: TmpRate + real(DbKi) :: TmpTime + + call Conv2UC( VTK_fps_line ) + if ( index(VTK_fps_line, "DEFAULT" ) == 1 ) then ! at DTfvw frequency + Get_DTvtk = DTfvw + elseif ( index(VTK_fps_line, "ALL" ) == 1 ) then ! at DTaero frequency + Get_DTvtk = DTaero + else ! read a number. Calculate this later. {will use closest integer multiple of DT} + read( VTK_fps_line, *, IOSTAT=IOS) VTK_fps + CALL CheckIOS ( IOS, FileName, 'VTK_fps', NumType, ErrStat2, ErrMsg2 ); if (Failed()) return; + + ! convert frames-per-second to seconds per sample: + if ( EqualRealNos(VTK_fps, 0.0_DbKi) ) then + Get_DTvtk = HUGE(1.0_DbKi) + else + TmpTime = 1.0_DbKi / VTK_fps + TmpRate = max( NINT( TmpTime / DTaero ),1_IntKi ) ! Can't be smaller that DTaero + Get_DTvtk = TmpRate * DTaero + ! warn if DTvtk is not TmpTime + if (.not. EqualRealNos(Get_DTvtk, TmpTime)) then + call SetErrStat(ErrID_Info, '1/VTK_fps is not an integer multiple of DT. FVW will output VTK information at '//& + trim(num2lstr(1.0_DbKi/(TmpRate*DTaero)))//' fps, the closest rate possible.',ErrStat,ErrMsg,'FVW_ReadInputFile') + end if + end if + end if + end function Get_DTvtk + + END SUBROUTINE FVW_ReadInputFile !================================================= diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index e1085aa028..0c0fa58a26 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -32,9 +32,10 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ IntKi HACK - - - "HACK ID" - -typedef ^ ^ DbKi DT - - - "Time interval for calls calculations" s +typedef ^ ^ DbKi DTaero - - - "Time interval for calls calculations" s typedef ^ ^ DbKi DTfvw - - - "Time interval for calculating wake induced velocities" s -typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s +typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s +typedef ^ ^ DbKi DTvtk - - - "DT between vtk writes" s # ....... OtherStateType ............ # FVW_OtherStateType @@ -69,12 +70,12 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi Vind_FW :::: - - "Induced velocity on far wake panels" m/s typedef ^ ^ IntKi nNW - - - "Number of active near wake panels" - typedef ^ ^ IntKi nFW - - - "Number of active far wake panels" - -typedef ^ ^ IntKi iStep - - - "Current step number" - -typedef ^ ^ IntKi iStepPrev - - - "Previous step number" - +typedef ^ ^ IntKi VTKstep - - - "Current vtk output step number" - +typedef ^ ^ DbKi VTKlastTime - - - "Time the last VTK file set was written out" s typedef ^ ^ ReKi r_wind :: - - "List of points where wind is requested for next time step" - typedef ^ ^ ReKi PitchAndTwist :: - - "Twist angle (includes all sources of twist) [Array of size (NumBlNds,numBlades)]" rad typedef ^ ^ Logical ComputeWakeInduced - - - "Compute induced velocities on this timestep" - -typedef ^ ^ DbKi OldTime - - - "Time the wake induction velocities were last calculated" s +typedef ^ ^ DbKi OldWakeTime - - - "Time the wake induction velocities were last calculated" s # ........ Input ............ # FVW_InputType @@ -117,8 +118,8 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi rLocal :: - - "Radial distance to blade node from the center of rotation, measured in the rotor plane, needed for DBEMT" m typedef ^ ^ IntKi NumBlades - - - "Number of blades" - typedef ^ ^ IntKi NumBladeNodes - - - "Number of nodes on each blade" - -typedef ^ ^ DbKi DT - - - "Time interval for calls (from AD15)" s -typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s +typedef ^ ^ DbKi DTaero - - - "Time interval for calls (from AD15)" s +typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s #.......... InputFileType ...... # FVW_InputFile @@ -143,6 +144,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi WingRegFactor - - - "Factor used in the regularization " typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - +typedef ^ ^ DbKi DTvtk - - - "Requested timestep between VTK outputs (calculated from the VTK_fps read in)" s #.......... InitOutputType ...... # FVW_InitOutputType diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 25815d097b..28d9020a44 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -59,9 +59,10 @@ MODULE FVW_Types INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] INTEGER(IntKi) :: HACK !< HACK ID [-] - REAL(DbKi) :: DT !< Time interval for calls calculations [s] + REAL(DbKi) :: DTaero !< Time interval for calls calculations [s] REAL(DbKi) :: DTfvw !< Time interval for calculating wake induced velocities [s] REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] + REAL(DbKi) :: DTvtk !< DT between vtk writes [s] END TYPE FVW_ParameterType ! ======================= ! ========= FVW_OtherStateType ======= @@ -96,12 +97,12 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: Vind_FW !< Induced velocity on far wake panels [m/s] INTEGER(IntKi) :: nNW !< Number of active near wake panels [-] INTEGER(IntKi) :: nFW !< Number of active far wake panels [-] - INTEGER(IntKi) :: iStep !< Current step number [-] - INTEGER(IntKi) :: iStepPrev !< Previous step number [-] + INTEGER(IntKi) :: VTKstep !< Current vtk output step number [-] + REAL(DbKi) :: VTKlastTime !< Time the last VTK file set was written out [s] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: r_wind !< List of points where wind is requested for next time step [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: PitchAndTwist !< Twist angle (includes all sources of twist) [Array of size (NumBlNds,numBlades)] [rad] LOGICAL :: ComputeWakeInduced !< Compute induced velocities on this timestep [-] - REAL(DbKi) :: OldTime !< Time the wake induction velocities were last calculated [s] + REAL(DbKi) :: OldWakeTime !< Time the wake induction velocities were last calculated [s] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -148,7 +149,7 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: rLocal !< Radial distance to blade node from the center of rotation, measured in the rotor plane, needed for DBEMT [m] INTEGER(IntKi) :: NumBlades !< Number of blades [-] INTEGER(IntKi) :: NumBladeNodes !< Number of nodes on each blade [-] - REAL(DbKi) :: DT !< Time interval for calls (from AD15) [s] + REAL(DbKi) :: DTaero !< Time interval for calls (from AD15) [s] REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] END TYPE FVW_InitInputType ! ======================= @@ -174,6 +175,7 @@ MODULE FVW_Types REAL(ReKi) :: WingRegFactor !< Factor used in the regularization [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] + REAL(DbKi) :: DTvtk !< Requested timestep between VTK outputs (calculated from the VTK_fps read in) [s] END TYPE FVW_InputFile ! ======================= ! ========= FVW_InitOutputType ======= @@ -260,9 +262,10 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%WrVTK = SrcParamData%WrVTK DstParamData%VTKBlades = SrcParamData%VTKBlades DstParamData%HACK = SrcParamData%HACK - DstParamData%DT = SrcParamData%DT + DstParamData%DTaero = SrcParamData%DTaero DstParamData%DTfvw = SrcParamData%DTfvw DstParamData%KinVisc = SrcParamData%KinVisc + DstParamData%DTvtk = SrcParamData%DTvtk END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -355,9 +358,10 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades Int_BufSz = Int_BufSz + 1 ! HACK - Db_BufSz = Db_BufSz + 1 ! DT + Db_BufSz = Db_BufSz + 1 ! DTaero Db_BufSz = Db_BufSz + 1 ! DTfvw Re_BufSz = Re_BufSz + 1 ! KinVisc + Db_BufSz = Db_BufSz + 1 ! DTvtk IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -470,12 +474,14 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%HACK Int_Xferred = Int_Xferred + 1 - DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DT + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTaero Db_Xferred = Db_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTfvw Db_Xferred = Db_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%KinVisc Re_Xferred = Re_Xferred + 1 + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTvtk + Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -629,12 +635,14 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%HACK = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 - OutData%DT = DbKiBuf( Db_Xferred ) + OutData%DTaero = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 OutData%DTfvw = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 OutData%KinVisc = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%DTvtk = DbKiBuf( Db_Xferred ) + Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -1139,8 +1147,8 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) ENDIF DstMiscData%nNW = SrcMiscData%nNW DstMiscData%nFW = SrcMiscData%nFW - DstMiscData%iStep = SrcMiscData%iStep - DstMiscData%iStepPrev = SrcMiscData%iStepPrev + DstMiscData%VTKstep = SrcMiscData%VTKstep + DstMiscData%VTKlastTime = SrcMiscData%VTKlastTime IF (ALLOCATED(SrcMiscData%r_wind)) THEN i1_l = LBOUND(SrcMiscData%r_wind,1) i1_u = UBOUND(SrcMiscData%r_wind,1) @@ -1170,7 +1178,7 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) DstMiscData%PitchAndTwist = SrcMiscData%PitchAndTwist ENDIF DstMiscData%ComputeWakeInduced = SrcMiscData%ComputeWakeInduced - DstMiscData%OldTime = SrcMiscData%OldTime + DstMiscData%OldWakeTime = SrcMiscData%OldWakeTime END SUBROUTINE FVW_CopyMisc SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -1404,8 +1412,8 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si END IF Int_BufSz = Int_BufSz + 1 ! nNW Int_BufSz = Int_BufSz + 1 ! nFW - Int_BufSz = Int_BufSz + 1 ! iStep - Int_BufSz = Int_BufSz + 1 ! iStepPrev + Int_BufSz = Int_BufSz + 1 ! VTKstep + Db_BufSz = Db_BufSz + 1 ! VTKlastTime Int_BufSz = Int_BufSz + 1 ! r_wind allocated yes/no IF ( ALLOCATED(InData%r_wind) ) THEN Int_BufSz = Int_BufSz + 2*2 ! r_wind upper/lower bounds for each dimension @@ -1417,7 +1425,7 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Re_BufSz = Re_BufSz + SIZE(InData%PitchAndTwist) ! PitchAndTwist END IF Int_BufSz = Int_BufSz + 1 ! ComputeWakeInduced - Db_BufSz = Db_BufSz + 1 ! OldTime + Db_BufSz = Db_BufSz + 1 ! OldWakeTime IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -1866,10 +1874,10 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFW Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%iStep - Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%iStepPrev + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKstep Int_Xferred = Int_Xferred + 1 + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%VTKlastTime + Db_Xferred = Db_Xferred + 1 IF ( .NOT. ALLOCATED(InData%r_wind) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -1904,7 +1912,7 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si END IF IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%ComputeWakeInduced , IntKiBuf(1), 1) Int_Xferred = Int_Xferred + 1 - DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%OldTime + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%OldWakeTime Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_PackMisc @@ -2585,10 +2593,10 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%nFW = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 - OutData%iStep = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 - OutData%iStepPrev = IntKiBuf( Int_Xferred ) + OutData%VTKstep = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%VTKlastTime = DbKiBuf( Db_Xferred ) + Db_Xferred = Db_Xferred + 1 IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! r_wind not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -2643,7 +2651,7 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg END IF OutData%ComputeWakeInduced = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) Int_Xferred = Int_Xferred + 1 - OutData%OldTime = DbKiBuf( Db_Xferred ) + OutData%OldWakeTime = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_UnPackMisc @@ -4144,7 +4152,7 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS ENDIF DstInitInputData%NumBlades = SrcInitInputData%NumBlades DstInitInputData%NumBladeNodes = SrcInitInputData%NumBladeNodes - DstInitInputData%DT = SrcInitInputData%DT + DstInitInputData%DTaero = SrcInitInputData%DTaero DstInitInputData%KinVisc = SrcInitInputData%KinVisc END SUBROUTINE FVW_CopyInitInput @@ -4283,7 +4291,7 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs END IF Int_BufSz = Int_BufSz + 1 ! NumBlades Int_BufSz = Int_BufSz + 1 ! NumBladeNodes - Db_BufSz = Db_BufSz + 1 ! DT + Db_BufSz = Db_BufSz + 1 ! DTaero Re_BufSz = Re_BufSz + 1 ! KinVisc IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) @@ -4464,7 +4472,7 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumBladeNodes Int_Xferred = Int_Xferred + 1 - DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DT + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTaero Db_Xferred = Db_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%KinVisc Re_Xferred = Re_Xferred + 1 @@ -4741,7 +4749,7 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%NumBladeNodes = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 - OutData%DT = DbKiBuf( Db_Xferred ) + OutData%DTaero = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 OutData%KinVisc = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 @@ -4781,6 +4789,7 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%WingRegFactor = SrcInputFileData%WingRegFactor DstInputFileData%WrVTK = SrcInputFileData%WrVTK DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades + DstInputFileData%DTvtk = SrcInputFileData%DTvtk END SUBROUTINE FVW_CopyInputFile SUBROUTINE FVW_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) @@ -4849,6 +4858,7 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_BufSz = Re_BufSz + 1 ! WingRegFactor Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades + Db_BufSz = Db_BufSz + 1 ! DTvtk IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4918,6 +4928,8 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades Int_Xferred = Int_Xferred + 1 + DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTvtk + Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_PackInputFile SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4994,6 +5006,8 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%VTKBlades = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%DTvtk = DbKiBuf( Db_Xferred ) + Db_Xferred = Db_Xferred + 1 END SUBROUTINE FVW_UnPackInputFile SUBROUTINE FVW_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index c5e5b72347..3eefffaaf3 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -367,7 +367,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x ! We return Gamma_LL endif - ! KEEP ME: + ! KEEP ME: --- ADP: removed m%iStep in favor of m%VTKstep !iW=1 !call Output_Gamma(m%CP_ll(1:3,:,iW), Gamma_LL(:,iW), iW, m%iStep, iLabel, iIter) From fe4eb08c882b3d224a34a962bf2d77dbf2241d90 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 12 Feb 2020 18:02:30 -0700 Subject: [PATCH 068/190] FVW: cleanup the routine to set requested wind points --- modules/aerodyn/src/FVW.f90 | 9 +++++---- modules/aerodyn/src/FVW_Subs.f90 | 31 ++++--------------------------- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 1d65b765ae..c3fd11b874 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -29,6 +29,7 @@ MODULE FVW PUBLIC :: FVW_CalcOutput PUBLIC :: FVW_UpdateStates + ! parameter for deciding if enough time has elapsed (Wake calculation, and vtk output) real(DbKi), parameter :: OneMinusEpsilon = 1 - 10000*EPSILON(1.0_DbKi) CONTAINS @@ -122,7 +123,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL FVW_Init_Y( p, u, y, ErrStat2, ErrMsg2); if(Failed()) return ! Returned guessed locations where wind will be required - CALL SetRequestedWindPoints(m%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + CALL SetRequestedWindPoints(m%r_wind, x, p, m ) ! Return anything in FVW_InitOutput that should be passed back to the calling code here CONTAINS @@ -440,7 +441,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Distribute the Wind we requested to Inflow wind to storage Misc arrays - CALL DistributeRequestedWind(u(1)%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL DistributeRequestedWind(u(1)%V_wind, x, p, m) ! --- Solve for circulation at t ! Returns: z%Gamma_LL (at t) @@ -500,7 +501,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m !call print_x_NW_FW(p, m, z, x,'Map3') ! set the wind points required for t+p%DTaero timestep - CALL SetRequestedWindPoints(m%r_wind, x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + CALL SetRequestedWindPoints(m%r_wind, x, p, m) if (m%FirstCall) then m%FirstCall=.False. @@ -691,7 +692,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, print'(A,F10.3,A,L1,A,I0,A,I0)','CalcOutput t:',t,' ',m%FirstCall,' nNW:',m%nNW,' nFW:',m%nFW ! Set the wind velocity at vortex - CALL DistributeRequestedWind(u%V_wind, x, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL DistributeRequestedWind(u%V_wind, x, p, m) ! if we are on a correction step, CalcOutput may be called again with different inputs CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, AFInfo, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 0f0a98fdc5..9a250a0d01 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -314,28 +314,14 @@ subroutine print_x_NW_FW(p, m, z, x, label) !! The r_wind array is allocated at initialization to the largest size possible. This is to !! ensure that we do not violate requirements in the framework later for changing the size !! of input and output arrays. -subroutine SetRequestedWindPoints(r_wind, x, p, m, ErrStat, ErrMsg ) +subroutine SetRequestedWindPoints(r_wind, x, p, m) real(ReKi), dimension(:,:), allocatable, intent(inout) :: r_wind !< Position where wind is requested type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables - integer(IntKi), intent( out) :: ErrStat !< Error status of the operation - character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - integer(IntKi) :: ErrStat2 ! temporary error status of the operation - character(ErrMsgLen) :: ErrMsg2 ! temporary error message - integer(IntKi) :: nTot ! Total number of points requested - integer(IntKi) :: iSpan, iW, iAge ! Index on span, wings, panels - integer(IntKi) :: iP,iP_start,iP_end ! Current index of point, start and end of range - ErrStat = ErrID_None - ErrMsg = "" - - nTot = 0 - nTot = nTot + p%nWings * p%nSpan ! Lifting line Control Points - nTot = nTot + p%nWings * (p%nSpan+1) * (m%nNW+1) ! Nearwake points - nTot = nTot + p%nWings * (FWnSpan+1) * (m%nFW+1) ! Far wake points + integer(IntKi) :: iP_start,iP_end ! Current index of point, start and end of range ! Using array reshaping to ensure a given near or far wake point is always at the same location in the array. - ! --- LL CP iP_start=1 iP_end=p%nWings*p%nSpan @@ -353,21 +339,12 @@ end subroutine SetRequestedWindPoints !> Set the requested wind into the correponding misc variables -subroutine DistributeRequestedWind(V_wind, x, p, m, ErrStat, ErrMsg ) +subroutine DistributeRequestedWind(V_wind, x, p, m) real(ReKi), dimension(:,:), intent(in ) :: V_wind !< Position where wind is requested type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables - integer(IntKi), intent( out) :: ErrStat !< Error status of the operation - character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - integer(IntKi) :: ErrStat2 ! temporary error status of the operation - character(ErrMsgLen) :: ErrMsg2 ! temporary error message - integer(IntKi) :: nTot ! Total number of points - integer(IntKi) :: iSpan, iW, iAge ! Index on span, wings, panels - integer(IntKi) :: iP,iP_start,iP_end ! Current index of point, start and end of range - ! Initialize ErrStat - ErrStat = ErrID_None - ErrMsg = "" + integer(IntKi) :: iP_start,iP_end ! Current index of point, start and end of range ! Using array reshaping to ensure a given near or far wake point is always at the same location in the array. ! --- LL CP From 4277463fd9bbd1478c765f32449642234eb64ad1 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 12 Feb 2020 18:08:04 -0700 Subject: [PATCH 069/190] FVW: bugfix -- move the incrementing of the NW panels to later (see code for reasoning) --- modules/aerodyn/src/FVW.f90 | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index c3fd11b874..0c78a99a1f 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -425,13 +425,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m endif - print'(A,F10.3,A,F10.3,A,F10.3,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' p%DTaero: ',p%DTaero,' ',n,' nNW:',m%nNW,' nFW:',m%nFW - - ! We don't propagate the "Old"-> "New" if we we are not calculating wake interaction - if (m%ComputeWakeInduced) then - call PrepareNextTimeStep() - endif - + print'(A,F10.3,A,F10.3,A,L1,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' ComputeWakeInduced: ',m%ComputeWakeInduced,' Step:',n,' nNW:',m%nNW,' nFW:',m%nFW ! --- Evaluation at t @@ -470,7 +464,12 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m !call print_x_NW_FW(p, m, z, x,'Conv') - if (m%ComputeWakeInduced) then + if (m%ComputeWakeInduced) then +!FIXME: Manu: check my relocation of the PrepareNextTimeStep call. I moved it because the above routines use m%nNW+1, +! in their calculations, which shifted everything one extra age. That caused odd results with the requested +! wind positions if the first call to ui_seg occured before the near wake was fully populated. +! This may also have been your array bounds issue (with m%nFW+1 +1 occuring). + call PrepareNextTimeStep() ! --- t+DTaero ! Propagation/creation of new layer of panels call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2) @@ -553,7 +552,7 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt call AllocAry( dxdt%r_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; ! Only calculate freewake after start time and if on a timestep when it should be calculated. - if ((t> p%FreeWakeStart) .and. m%ComputeWakeInduced) then + if ((t>= p%FreeWakeStart) .and. m%ComputeWakeInduced) then nFWEff = min(m%nFW, p%nFWFree) ! --- Compute Induced velocities on the Near wake and far wake based on the marker postions: From f00934e5ffab9398ef13e25d1f8d080a8262b609 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Thu, 13 Feb 2020 08:06:25 -0700 Subject: [PATCH 070/190] FVW: bugfix to get calculation of Wake and VTK output at T=0 --- modules/aerodyn/src/FVW.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 0c78a99a1f..40984c735d 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -154,7 +154,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%nNW = iNWStart-1 ! Number of active nearwake panels m%nFW = 0 ! Number of active farwake panels m%VTKstep = 0 ! Current step number for vtk output - m%VTKlastTime = 0.0_DbKi - p%DTvtk ! set negative so we ouput first vtk files + m%VTKlastTime = -HUGE(1.0_DbKi) call AllocAry( m%LE , 3 , p%nSpan+1 , p%nWings, 'Leading Edge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%LE = -999999_ReKi; call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%TE = -999999_ReKi; @@ -188,7 +188,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) nMax = nMax + (FWnSpan+1) * (p%nFWMax+1) * p%nWings ! Far wake points call AllocAry( m%r_wind, 3, nMax, 'Requested wind points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ) m%r_wind = 0.0_ReKi ! set to zero so InflowWind can shortcut calculations - m%OldWakeTime = 0.0_DbKi-p%DTfvw ! First time for wake interaction calculations. Set negative so calculate at T=0 + m%OldWakeTime = -HUGE(1.0_DbKi) end subroutine FVW_InitMiscVars ! ============================================================================== subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) From dba939a3ccf6f63eaaa528839fee1a763df74a18 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 13 Mar 2020 17:06:41 -0600 Subject: [PATCH 071/190] FVW: vtk files have step index --- modules/aerodyn/src/FVW.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 40984c735d..eabbf7a657 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -731,8 +731,8 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, if ( ( t - m%VTKlastTime ) >= p%DTvtk*OneMinusEpsilon ) then m%VTKlastTime = t call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%VTKstep, 9) - m%VTKstep = m%VTKstep + 1 ! Increment VTK counter endif + m%VTKstep = m%VTKstep + 1 ! Increment VTK counter no matter what endif From abc0dc6a98215829402ff5edbc1423847e88113f Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 13 Mar 2020 17:07:14 -0600 Subject: [PATCH 072/190] FVW: default time it aero time --- modules/aerodyn/src/FVW_IO.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 0da8c2a377..4daccd050d 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -40,7 +40,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVarWDefault(UnIn,FileName,Inp%FreeWakeStart ,'FreeWakeStart' ,'', 0.0_ReKi, ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart','', 0.0_ReKi, ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%DTfvw ,'DTfvw' ,'', 0.2_DbKi, ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%DTfvw ,'DTfvw' ,'', p%DTaero, ErrStat2,ErrMsg2); if(Failed())return !------------------------ CIRCULATION SPECIFICATIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'Circulation specification header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod' ,'', idCircPolarData, ErrStat2,ErrMsg2); if(Failed())return From f1f4e157938dc8a594e8fe16d8fa1d0ab7ad469d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 13 Mar 2020 18:17:49 -0600 Subject: [PATCH 073/190] FVW: RegParam has length [m], simplifying BiotSavart, branching outside of for loop (copy-pasting) --- modules/aerodyn/src/FVW_BiotSavart.f90 | 542 ++++++++++++------------- modules/aerodyn/src/FVW_IO.f90 | 2 +- 2 files changed, 270 insertions(+), 274 deletions(-) diff --git a/modules/aerodyn/src/FVW_BiotSavart.f90 b/modules/aerodyn/src/FVW_BiotSavart.f90 index 84d41f96ff..1c3fac8fb9 100644 --- a/modules/aerodyn/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn/src/FVW_BiotSavart.f90 @@ -1,175 +1,105 @@ module FVW_BiotSavart use NWTC_Library, only: ReKi, IntKi + use OMP_LIB implicit none real(ReKi),parameter :: PRECISION_UI = epsilon(1.0_ReKi)/100 !< NOTE assuming problem of size 1 real(ReKi),parameter :: MIN_EXP_VALUE=-10.0_ReKi - real(ReKi),parameter :: MINDENOM=1e-15_ReKi + real(ReKi),parameter :: MINDENOM=0.0_ReKi +! real(ReKi),parameter :: MINDENOM=1e-15_ReKi real(ReKi),parameter :: MINNORMSIMP=1e-6_ReKi integer(IntKi), parameter :: idRegNone = 0 integer(IntKi), parameter :: idRegRankine = 1 integer(IntKi), parameter :: idRegLambOseen = 2 integer(IntKi), parameter :: idRegVatistas = 3 - integer(IntKi), parameter :: idRegCompact = 4 - integer(IntKi), parameter :: idRegCompactDim = 5 - integer(IntKi), parameter, dimension(5) :: idRegVALID = (/idRegNone,idRegRankine,idRegLambOseen,idRegVatistas,idRegCompact/) + integer(IntKi), parameter :: idRegOffset = 4 + integer(IntKi), parameter, dimension(5) :: idRegVALID = (/idRegNone,idRegRankine,idRegLambOseen,idRegVatistas,idRegOffset/) + + real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) contains !> Induced velocity from one segment at one control points -subroutine ui_seg_11(DeltaPa, DeltaPb, Gam, RegModel , RegParam, Ui) - implicit none +subroutine ui_seg_11(DeltaPa, DeltaPb, SegGamma, RegFunction, RegParam1, Uind) ! Input/output arguments - real(ReKi), dimension(3), intent(in) :: DeltaPa !< 3 x 1 Pcp-P1 - real(ReKi), dimension(3), intent(in) :: DeltaPb !< 3 x 1 Pcp-P2 - real(ReKi), intent(in) :: Gam !< Circulation - integer, intent(in) :: RegModel !< Regularization model - real(ReKi), intent(in) :: RegParam !< Regularization parameter - real(ReKi), dimension(3),intent(out) :: Ui !< Induced velocity (no side effects) + real(ReKi), dimension(3), intent(in) :: DeltaPa !< 3 x 1 Pcp-P1 [m] + real(ReKi), dimension(3), intent(in) :: DeltaPb !< 3 x 1 Pcp-P2 [m] + real(ReKi), intent(in) :: SegGamma !< Circulation [m^2/s] + integer, intent(in) :: RegFunction!< Regularization model + real(ReKi), intent(in) :: RegParam1 !< Regularization parameter (core radius) [m] + real(ReKi), dimension(3),intent(out) :: Uind !< Induced velocity (no side effects) [m/s] ! Variables declaration real(ReKi),dimension(3) :: crossprod !< - real(ReKi),dimension(3) :: D !< - real(ReKi) :: denom !< real(ReKi) :: denominator !< - real(ReKi) :: h2 !< Square of h - real(ReKi) :: h !< Only used by one model + real(ReKi) :: r_bar2 !< (r/rc)^2 real(ReKi) :: Kv !< - real(ReKi) :: norm_a !< - real(ReKi) :: norm_b !< - real(ReKi) :: norm2_r0 !< - real(ReKi) :: norm2_crossprod !< - real(ReKi) :: xa !< - real(ReKi) :: ya !< - real(ReKi) :: za !< - real(ReKi) :: xb !< - real(ReKi) :: yb !< - real(ReKi) :: zb !< - real(ReKi) :: RegParam2 !< Square of viscous param + real(ReKi) :: norm_a, norm_b !< + real(ReKi) :: norm2_r0 !< Squared length of the segment + real(ReKi) :: norm2_orth !< Squared distance orthogonal to the segment + real(ReKi) :: xa, ya, za, xb, yb, zb !< Coordinates of X-Xa and X-Xb real(ReKi) :: exp_value !< - real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) ! - xa=DeltaPa(1) - ya=DeltaPa(2) - za=DeltaPa(3) - xb=DeltaPb(1) - yb=DeltaPb(2) - zb=DeltaPb(3) - !--- Simple test if on an extremity point - if(abs(xa)+abs(ya)+abs(za)PRECISION_UI) then + crossprod(1) = ya*zb-za*yb; crossprod(2) = za*xb-xa*zb; crossprod(3) = xa*yb-ya*xb + norm2_orth = crossprod(1)**2 + crossprod(2)**2 + crossprod(3)**2 + if (norm2_orth>PRECISION_UI) then ! On the singularity, Uind(1:3)=0.0_ReKi + norm2_r0 = (xa-xb)*(xa-xb) + (ya-yb)*(ya-yb) +(za-zb)*(za-zb) + if (norm2_r0>PRECISION_UI) then ! segment of zero length + ! --- Far field TODO + ! --- Regularization (close field) + norm2_orth = norm2_orth/norm2_r0 ! d = (r1xr2)/r0 + select case (RegFunction) ! + case ( idRegNone ) ! No vortex core model + Kv=1.0_ReKi + case ( idRegRankine ) ! Rankine + r_bar2 = norm2_orth/ RegParam1**2 + if (r_bar2<1) then + Kv=r_bar2 + else + Kv=1.0_ReKi + end if + case ( idRegLambOseen ) ! Lamb-Oseen + r_bar2 = norm2_orth/ RegParam1**2 + exp_value = -1.25643_ReKi*r_bar2 + if(exp_valuerc - ! orthogonal distance r1xr2/r0 - h2 = norm2_crossprod/ norm2_r0 - RegParam2=RegParam**2 - if (h2rc - ! orthogonal distance r1xr2/r0 - h2 = norm2_crossprod/ norm2_r0 - RegParam2=RegParam**2 - exp_value = -1.25643_ReKi*(h2)/(RegParam2) - if(exp_valuerc - ! orthogonal distance r1xr2/r0 - h2 = norm2_crossprod/ norm2_r0 - RegParam2=RegParam**2 - ! h = (norm_a+norm_b)/2; - Kv = h2/sqrt(RegParam2**2+h2**2) - case ( idRegCompact ) !!Cut-off radius as function of length: delta^2*norm(r0)^2 - h2 = norm2_crossprod/ norm2_r0 - Kv=1.0_ReKi - ! delta*norm(r0)^2 - denominator=denominator+RegParam*norm2_r0 - RegParam2=RegParam**2 ! TODO - case ( idRegCompactDim ) !!Cut-off radius dimension fixed: (delta l_0)^2 - h2 = norm2_crossprod/ norm2_r0 - Kv=1.0_ReKi - ! (delta l_0)^2 - denominator=denominator+RegParam - RegParam2=RegParam**2 ! TODO - case ( 33 ) !!Vatistas n=2 - RegParamt<=>rc - ! See Hoydonck 2012. and Script MainRingSensitivityN - ! Not matture enough - h = (norm_a+norm_b)/2.0_ReKi - RegParam2=RegParam**2 - if(h<2*sqrt(norm2_r0)) then - ! use Vatistas n=2, normal one - h2 = norm2_crossprod/norm2_r0 - else - h2 = 1._ReKi ! TODO - endif - Kv = (h2)/sqrt(RegParam2**2+h2**2) - case DEFAULT - Kv=1.0_ReKi - end select - - Kv=Gam*fourpi_inv*Kv*(norm_a+norm_b)/denominator - Ui(1:3) = Kv*crossprod(1:3) - end if ! denominator size or distances too small - end if ! - ! printf("!4.3f !4.3f !4.3f !4.3f !4.3fNewLine",Uout(1),Uout(2),Uout(3),Kv,denominator); + endif + endif end subroutine ui_seg_11 !> Induced velocity from a list of segments defined by Connectivity (SegConnct) and Points (SegPoints) -!! NOTE: this funciton has side effects and expect Uind_out to be initialized! +!! NOTE: this function has side effects and expects Uind_out to be initialized! !! The function can compute the velocity on part of the segments and part of the control points. !! This feature is useful if some parallelization is used, while common storage vectors are used. -!! subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & iSegStart, iSegEnd, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, & - RegModel, RegParam, Uind_out) - use OMP_LIB - implicit none + RegFunction, RegParam, Uind_out) real(ReKi), dimension(3,nCPsTot), intent(in) :: CPs !< Control points integer(IntKi), intent(in) :: iCPStart !< Index where we start in Control points array integer(IntKi), intent(in) :: iCPEnd !< Index where we end in Control points array @@ -181,159 +111,225 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & integer(IntKi),intent(in) :: iSegEnd !< Index in SegConnct, and SegGamma where we end integer(IntKi), intent(in) :: nSegTot !< Total number of segments integer(IntKi), intent(in) :: nSegPTot !< Total number of segment points - integer(IntKi), intent(in) :: RegModel !< Regularization model + integer(IntKi), intent(in) :: RegFunction !< Regularization model real(ReKi), dimension(nSegTot), intent(in) :: RegParam !< Regularization parameter real(ReKi), dimension(3,nCPsTot), intent(inout) :: Uind_out !< Induced velocity vector - Side effects!!! ! Variables - integer(IntKi) :: icp,is - ! Part of argument arrays + integer(IntKi) :: icp, is real(ReKi), dimension(3) :: Uind !< - real(ReKi), dimension(3) :: P1 !< Point1 of a given segment - real(ReKi), dimension(3) :: P2 !< Point2 of a given segment - real(ReKi), dimension(3) :: DP1, DP2 !< - real(ReKi) :: norm2_r0 !< - real(ReKi) :: Gam !< - real(ReKi) :: RegParam1 !< - real(ReKi) :: RegParam2 !< Square of viscous param + real(ReKi), dimension(3) :: P1, P2 !< Extremities of a given segment ! Variables declaration real(ReKi),dimension(3) :: crossprod !< - real(ReKi) :: denom !< real(ReKi) :: denominator !< - real(ReKi) :: h2 !< Square of h - real(ReKi) :: h !< Only used by one model + real(ReKi) :: r_bar2 !< (r/rc)^2 real(ReKi) :: Kv !< - real(ReKi) :: norm_a !< - real(ReKi) :: norm_b !< - real(ReKi) :: norm2_crossprod !< - real(ReKi) :: xa !< - real(ReKi) :: ya !< - real(ReKi) :: za !< - real(ReKi) :: xb !< - real(ReKi) :: yb !< - real(ReKi) :: zb !< + real(ReKi) :: norm_a, norm_b !< + real(ReKi) :: norm2_orth !< + real(ReKi) :: norm2_r0 !< Squared length of the segment d = (r1xr2)/r0 + real(ReKi) :: xa, ya, za, xb, yb, zb !< Coordinates of X-Xa and X-Xb real(ReKi) :: exp_value !< - real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) - !$OMP PARALLEL default(shared) - !$OMP do private(& - !$OMP& icp,is,Uind,P1,P2,DP1,DP2,norm2_r0,Gam,RegParam1,RegParam2,& - !$OMP& crossprod,denom,denominator,h2,h,Kv,norm_a,norm_b,norm2_crossprod,xa,ya,za,xb,yb,zb,exp_value& - !$OMP& ) schedule(runtime) - ! loop on CPs - do icp=iCPStart,iCPEnd - do is=iSegStart,iSegEnd ! loop on selected segments - ! Kind of arguments for Segment 11 - Uind=0.0_ReKi - P1 = SegPoints(1:3, SegConnct(1,is)) ! Segment extremity points - P2 = SegPoints(1:3, SegConnct(2,is)) - DP1 = CPs(:,icp)-P1; - DP2 = CPs(:,icp)-P2 - Gam = SegGamma(is) - RegParam1= RegParam(is) - ! --- inlining of ui_seg_11 - xa=DP1(1) - ya=DP1(2) - za=DP1(3) - xb=DP2(1) - yb=DP2(2) - zb=DP2(3) - !--- Simple test if on an extremity point - if(abs(xa)+abs(ya)+abs(za)rc - ! orthogonal distance r1xr2/r0 - h2 = norm2_crossprod/ norm2_r0 - RegParam2 = RegParam1**2 - if (h2rc - ! orthogonal distance r1xr2/r0 - h2 = norm2_crossprod/ norm2_r0 - RegParam2 = RegParam1**2 - exp_value = -1.25643_ReKi*(h2)/(RegParam2) - if(exp_valuerc - ! orthogonal distance r1xr2/r0 - h2 = norm2_crossprod/ norm2_r0 - RegParam2 = RegParam1**2 - ! h = (norm_a+norm_b)/2; - Kv = h2/sqrt(RegParam2**2+h2**2) - case ( idRegCompact ) !!Cut-off radius as function of length: delta^2*norm(r0)^2 - Kv=1.0_ReKi - denominator=denominator+RegParam1*RegParam1*norm2_r0 - case ( idRegCompactDim ) !!Cut-off radius dimension fixed: (delta l_0)^2 - Kv=1.0_ReKi - denominator=denominator+RegParam1*RegParam1 - case ( 33 ) !!Vatistas n=2 - RegParamt<=>rc - ! See Hoydonck 2012 - h = (norm_a+norm_b)/2.0_ReKi - RegParam2 = RegParam1**2 - if(h<2*sqrt(norm2_r0)) then - ! use Vatistas n=2, normal one - h2 = norm2_crossprod/norm2_r0 + ! Branching based on regularization model + ! NOTE: copy paste of code is done for optimization! + ! The only thing changing is the part labelled "regularization" + select case (RegFunction) + case ( idRegNone ) ! No vortex core + !$OMP PARALLEL default(shared) + !$OMP do private(icp,is,Uind,P1,P2,crossprod,denominator,Kv,norm_a,norm_b,norm2_r0,norm2_orth,xa,ya,za,xb,yb,zb) schedule(runtime) + do icp=iCPStart,iCPEnd ! loop on CPs + do is=iSegStart,iSegEnd ! loop on selected segments + Uind = 0.0_ReKi + P1 = SegPoints(1:3, SegConnct(1,is)) ! Segment extremity points + P2 = SegPoints(1:3, SegConnct(2,is)) + xa=CPs(1,icp)-P1(1); ya=CPs(2,icp)-P1(2); za=CPs(3,icp)-P1(3); + xb=CPs(1,icp)-P2(1); yb=CPs(2,icp)-P2(2); zb=CPs(3,icp)-P2(3); + norm_a = sqrt(xa*xa + ya*ya + za*za) + norm_b = sqrt(xb*xb + yb*yb + zb*zb) + denominator = norm_a*norm_b*(norm_a*norm_b + xa*xb+ya*yb+za*zb) + ! --- Far field TODO + if (denominator>PRECISION_UI) then + crossprod(1) = ya*zb-za*yb; crossprod(2) = za*xb-xa*zb; crossprod(3) = xa*yb-ya*xb + norm2_orth = crossprod(1)**2 + crossprod(2)**2 + crossprod(3)**2 + if (norm2_orth>PRECISION_UI) then ! On the singularity, Uind(1:3)=0.0_ReKi + norm2_r0 = (xa-xb)*(xa-xb) + (ya-yb)*(ya-yb) +(za-zb)*(za-zb) + if (norm2_r0>PRECISION_UI) then + ! --- Far field TODO + ! --- NO Regularization (close field) + Kv = SegGamma(is)*fourpi_inv*(norm_a+norm_b)/(denominator + MINDENOM) + Uind(1:3) = Kv*crossprod(1:3) + end if + end if + end if + Uind_out(1:3,icp) = Uind_out(1:3,icp)+Uind(1:3) + end do ! Loop on segments + enddo ! Loop on control points + !$OMP END DO + !$OMP END PARALLEL + + case ( idRegRankine ) ! Rankine + !$OMP PARALLEL default(shared) + !$OMP do private(icp,is,Uind,P1,P2,crossprod,denominator,r_bar2,Kv,norm_a,norm_b,norm2_r0,norm2_orth,xa,ya,za,xb,yb,zb) schedule(runtime) + do icp=iCPStart,iCPEnd ! loop on CPs + do is=iSegStart,iSegEnd ! loop on selected segments + Uind = 0.0_ReKi + P1 = SegPoints(1:3, SegConnct(1,is)) ! Segment extremity points + P2 = SegPoints(1:3, SegConnct(2,is)) + xa=CPs(1,icp)-P1(1); ya=CPs(2,icp)-P1(2); za=CPs(3,icp)-P1(3); + xb=CPs(1,icp)-P2(1); yb=CPs(2,icp)-P2(2); zb=CPs(3,icp)-P2(3); + norm_a = sqrt(xa*xa + ya*ya + za*za) + norm_b = sqrt(xb*xb + yb*yb + zb*zb) + denominator = norm_a*norm_b*(norm_a*norm_b + xa*xb+ya*yb+za*zb) + if (denominator>PRECISION_UI) then + crossprod(1) = ya*zb-za*yb; crossprod(2) = za*xb-xa*zb; crossprod(3) = xa*yb-ya*xb + norm2_orth = crossprod(1)**2 + crossprod(2)**2 + crossprod(3)**2 + if (norm2_orth>PRECISION_UI) then ! On the singularity, Uind(1:3)=0.0_ReKi + norm2_r0 = (xa-xb)*(xa-xb) + (ya-yb)*(ya-yb) +(za-zb)*(za-zb) + if (norm2_r0>PRECISION_UI) then + ! --- Far field TODO + ! --- Regularization (close field) --- Rankine + norm2_orth = norm2_orth/norm2_r0 ! d = (r1xr2)/r0 + r_bar2 = norm2_orth/ RegParam(is)**2 + if (r_bar2<1) then + Kv=r_bar2 + else + Kv=1.0_ReKi + end if + Kv = SegGamma(is)*fourpi_inv*Kv*(norm_a+norm_b)/(denominator + MINDENOM) + Uind(1:3) = Kv*crossprod(1:3) + end if + end if ! denominator size or distances too small + end if ! + Uind_out(1:3,icp) = Uind_out(1:3,icp)+Uind(1:3) + end do ! Loop on segments + enddo ! Loop on control points + !$OMP END DO + !$OMP END PARALLEL + + case ( idRegLambOseen ) ! LambOseen + !$OMP PARALLEL default(shared) + !$OMP do private(icp,is,Uind,P1,P2,crossprod,denominator,r_bar2,Kv,norm_a,norm_b,norm2_r0,norm2_orth,xa,ya,za,xb,yb,zb,exp_value) schedule(runtime) + do icp=iCPStart,iCPEnd ! loop on CPs + do is=iSegStart,iSegEnd ! loop on selected segments + Uind = 0.0_ReKi + P1 = SegPoints(1:3, SegConnct(1,is)) ! Segment extremity points + P2 = SegPoints(1:3, SegConnct(2,is)) + xa=CPs(1,icp)-P1(1); ya=CPs(2,icp)-P1(2); za=CPs(3,icp)-P1(3); + xb=CPs(1,icp)-P2(1); yb=CPs(2,icp)-P2(2); zb=CPs(3,icp)-P2(3); + norm_a = sqrt(xa*xa + ya*ya + za*za) + norm_b = sqrt(xb*xb + yb*yb + zb*zb) + denominator = norm_a*norm_b*(norm_a*norm_b + xa*xb+ya*yb+za*zb) + if (denominator>PRECISION_UI) then + crossprod(1) = ya*zb-za*yb; crossprod(2) = za*xb-xa*zb; crossprod(3) = xa*yb-ya*xb + norm2_orth = crossprod(1)**2 + crossprod(2)**2 + crossprod(3)**2 + if (norm2_orth>PRECISION_UI) then ! On the singularity, Uind(1:3)=0.0_ReKi + norm2_r0 = (xa-xb)*(xa-xb) + (ya-yb)*(ya-yb) +(za-zb)*(za-zb) + if (norm2_r0>PRECISION_UI) then + ! --- Far field TODO + ! --- Regularization (close field) --- Lamb Oseen + norm2_orth = norm2_orth/norm2_r0 ! d = (r1xr2)/r0 + r_bar2 = norm2_orth/ RegParam(is)**2 + exp_value = -1.25643_ReKi*r_bar2 + if(exp_valuePRECISION_UI) then + crossprod(1) = ya*zb-za*yb; crossprod(2) = za*xb-xa*zb; crossprod(3) = xa*yb-ya*xb + norm2_orth = crossprod(1)**2 + crossprod(2)**2 + crossprod(3)**2 + if (norm2_orth>PRECISION_UI) then ! On the singularity, Uind(1:3)=0.0_ReKi + norm2_r0 = (xa-xb)*(xa-xb) + (ya-yb)*(ya-yb) +(za-zb)*(za-zb) + if (norm2_r0>PRECISION_UI) then + ! --- Far field TODO + ! --- Regularization (close field) --- Vatistas + norm2_orth = norm2_orth/norm2_r0 ! d = (r1xr2)/r0 + r_bar2 = norm2_orth/RegParam(is)**2 + Kv = r_bar2/sqrt(1+r_bar2**2) + Kv = SegGamma(is)*fourpi_inv*Kv*(norm_a+norm_b)/(denominator + MINDENOM) + Uind(1:3) = Kv*crossprod(1:3) + end if + end if ! denominator size or distances too small + end if ! + Uind_out(1:3,icp) = Uind_out(1:3,icp)+Uind(1:3) + end do ! Loop on segments + enddo ! Loop on control points + !$OMP END DO + !$OMP END PARALLEL + + case ( idRegOffset ) ! Denominator offset + !$OMP PARALLEL default(shared) + !$OMP do private(icp,is,Uind,P1,P2,crossprod,denominator,r_bar2,Kv,norm_a,norm_b,norm2_r0,norm2_orth,xa,ya,za,xb,yb,zb) schedule(runtime) + do icp=iCPStart,iCPEnd ! loop on CPs + do is=iSegStart,iSegEnd ! loop on selected segments + Uind = 0.0_ReKi + P1 = SegPoints(1:3, SegConnct(1,is)) ! Segment extremity points + P2 = SegPoints(1:3, SegConnct(2,is)) + xa=CPs(1,icp)-P1(1); ya=CPs(2,icp)-P1(2); za=CPs(3,icp)-P1(3); + xb=CPs(1,icp)-P2(1); yb=CPs(2,icp)-P2(2); zb=CPs(3,icp)-P2(3); + norm_a = sqrt(xa*xa + ya*ya + za*za) + norm_b = sqrt(xb*xb + yb*yb + zb*zb) + denominator = norm_a*norm_b*(norm_a*norm_b + xa*xb+ya*yb+za*zb) + if (denominator>PRECISION_UI) then + crossprod(1) = ya*zb-za*yb; crossprod(2) = za*xb-xa*zb; crossprod(3) = xa*yb-ya*xb + norm2_orth = crossprod(1)**2 + crossprod(2)**2 + crossprod(3)**2 + if (norm2_orth>PRECISION_UI) then ! On the singularity, Uind(1:3)=0.0_ReKi + norm2_r0 = (xa-xb)*(xa-xb) + (ya-yb)*(ya-yb) +(za-zb)*(za-zb) + if (norm2_r0>PRECISION_UI) then + ! --- Far field TODO + ! --- Regularization (close field) -- Offset + denominator = denominator+RegParam(is)**2 + Kv = SegGamma(is)*fourpi_inv*(norm_a+norm_b)/(denominator + MINDENOM) + Uind(1:3) = Kv*crossprod(1:3) end if - Kv = (h2)/sqrt(RegParam2**2+h2**2) - case default - Kv=1.0_ReKi - end select + end if ! denominator size or distances too small + end if ! + Uind_out(1:3,icp) = Uind_out(1:3,icp)+Uind(1:3) + end do ! Loop on segments + enddo ! Loop on control points + !$OMP END DO + !$OMP END PARALLEL + case default + print*,'[ERROR] Unknown RegFunction',RegFunction + stop + end select - Kv=Gam*fourpi_inv*Kv*(norm_a+norm_b)/denominator - Uind(1:3) = Kv*crossprod(1:3) - !print "(A,3F21.16)","ui :",Uind(1:3) - end if ! denominator size or distances too small - end if ! - ! --- END inlining of Segment 11 - Uind_out(1:3,icp) = Uind_out(1:3,icp)+Uind(1:3) - end do ! Loop on segments - enddo ! Loop on control points - !$OMP END DO - !$OMP END PARALLEL end subroutine !> Velocity induced by one vortex quad on nCPs Control Points -subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, RegModel, RegParam, Uind) +subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, RegFunction, RegParam, Uind) ! Arguments declarations integer, intent(in) :: nCPs !< real(ReKi), dimension(3,nCPs), intent(in) :: CPs !< real(ReKi), dimension(3), intent(in) :: P1,P2,P3,P4 !< Coordinates of vortex quadrilateral real(ReKi), intent(in) :: Gamm - integer(IntKi) , intent(in) :: RegModel !< Regularization model (e.g. LambOseen) - real(ReKi), intent(in) :: RegParam !< Regularization parameter - real(ReKi), dimension(3,nCPs), intent(inout) :: Uind !< side effects!!! + integer(IntKi) , intent(in) :: RegFunction !< Regularization model (e.g. LambOseen) + real(ReKi), intent(in) :: RegParam !< Regularization parameter [m] + real(ReKi), dimension(3,nCPs), intent(inout) :: Uind !< side effects!!! ! Variable declarations real(ReKi), dimension(3) :: CP !< real(ReKi), dimension(3) :: Uindtmp !< @@ -347,19 +343,19 @@ subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, RegModel, RegParam, Uind) CP(1:3)=CPs(1:3,icp) ! 1-2 segment DP1=CP-P1; DP2=CP-P2; - call ui_seg_11 ( DP1, DP2, Gamm, RegModel, RegParam, Uindtmp) + call ui_seg_11 ( DP1, DP2, Gamm, RegFunction, RegParam, Uindtmp) Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) ! 3-4 segment DP1=CP-P3; DP2=CP-P4; - call ui_seg_11 ( DP1, DP2, Gamm, RegModel, RegParam, Uindtmp) + call ui_seg_11 ( DP1, DP2, Gamm, RegFunction, RegParam, Uindtmp) Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) ! 2-3 segment DP1=CP-P2; DP2=CP-P3; - call ui_seg_11 ( DP1, DP2, Gamm, RegModel, RegParam, Uindtmp) + call ui_seg_11 ( DP1, DP2, Gamm, RegFunction, RegParam, Uindtmp) Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) ! 4-1 segment DP1=CP-P4; DP2=CP-P1; - call ui_seg_11 ( DP1, DP2, Gamm, RegModel, RegParam, Uindtmp) + call ui_seg_11 ( DP1, DP2, Gamm, RegFunction, RegParam, Uindtmp) Uind(1:3,icp) = Uind(1:3,icp)+Uindtmp(1:3) end do ! loop on CPs !OMP END DO diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 4daccd050d..f90c5300b0 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -54,7 +54,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar (UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'' , ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',idRegCompact , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',idRegVatistas, ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',idRegConstant, ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%WakeRegFactor ,'WakeRegFactor' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%WingRegFactor ,'WingRegFactor' ,'' , ErrStat2,ErrMsg2); if(Failed())return From 098f72bf94cff8f25f05ea9830ca7dd5cea1b241 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 13 Mar 2020 21:01:48 -0600 Subject: [PATCH 074/190] FVW: adding Biot-Savart tests --- modules/aerodyn/src/FVW.f90 | 3 + modules/aerodyn/src/FVW_Tests.f90 | 315 +++++++++++++++++++++++++++++- 2 files changed, 317 insertions(+), 1 deletion(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index eabbf7a657..ab703eae34 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -126,6 +126,9 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL SetRequestedWindPoints(m%r_wind, x, p, m ) ! Return anything in FVW_InitOutput that should be passed back to the calling code here + ! TODO remove me for developement (and when OpenFAST has a framework for tests) + CALL FVW_RunTests(ErrStat2, ErrMsg2); if (Failed()) return + CONTAINS logical function Failed() diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index 8c92d5519a..279b8297f0 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -11,7 +11,308 @@ module FVW_Tests implicit none + public :: FVW_RunTests + private + + character(len=255),save :: testname + interface test_equal; module procedure & + test_equal_i1, & + test_equal_i0 + end interface + interface test_almost_equal; module procedure & + test_almost_equal_0, & + test_almost_equal_1, & + test_almost_equal_2 + end interface contains + ! -------------------------------------------------------------------------------- + ! --- Helper functions (should be part of NWTC library) + ! -------------------------------------------------------------------------------- + subroutine test_success(info,bPrint_in) + character(len=*), intent(in) :: info + logical, intent(in), optional :: bPrint_in + if(present(bPrint_in)) then + if(bPrint_in) then + write(*,'(A)')'[ OK ] '//trim(testname)//': '//trim(Info) + endif + else + write(*,'(A)')'[ OK ] '//trim(testname)//': '//trim(Info) + endif + end subroutine + + subroutine test_fail(info,bPrint_in,bStop_in) + character(len=*), intent(in) :: info + logical, intent(in), optional :: bPrint_in + logical, intent(in), optional :: bStop_in + if(present(bPrint_in)) then + if(bPrint_in) then + write(*,'(A)')'[FAIL] '//trim(testname)//': '//trim(Info) + endif + else + write(*,'(A)')'[FAIL] '//trim(testname)//': '//trim(Info) + endif + if(present(bStop_in)) then + if(bStop_in) then + STOP + endif + else + STOP + endif + end subroutine + + subroutine test_equal_i0(Var,iTry,iRef) + ! Arguments + character(len=*), intent(in) :: Var + integer, intent(in) :: iTry !< + integer, intent(in) :: iRef !< + ! Variables + character(len=255) :: InfoAbs + if(iRef/=iTry) then + write(InfoAbs,'(A,I0,A,I0)') trim(Var),iRef,'/',iTry + call test_fail(InfoAbs) + STOP -1 !OTHER-COMPILER + STOP ! COMPAQ-COMPILER + else + write(InfoAbs,'(A,A,I0)') trim(Var),' ok ',iRef + call test_success(InfoAbs) + endif + end subroutine + + subroutine test_equal_i1(Var,VecTry,VecRef,bTest,bPrintOnly,bPassed) + ! Arguments + character(len=*), intent(in) :: Var + integer, dimension(:), intent(in) :: VecTry !< + integer, dimension(:), intent(in) :: VecRef !< + logical, intent(in) :: bTest + logical, intent(in) :: bPrintOnly + logical, intent(out),optional :: bPassed + ! Variables + character(len=255) :: InfoAbs + integer :: i,cpt + ! + cpt=0 + do i=1,size(VecRef) + if(VecRef(i)/=VecTry(i)) then + cpt=cpt+1 + endif + enddo + if(cpt>0) then + write(InfoAbs,'(A,I0)') trim(Var)//' Elements different: ',cpt + if(present(bPassed)) then + bPassed=.false. + endif + else + write(InfoAbs,'(A)') trim(Var)//' reproduced to identity' + if(present(bPassed)) then + bPassed=.true. + endif + endif + if(bPrintOnly) then + print'(A)',trim(InfoAbs) + endif + if(bTest) then + if(cpt>0) then + call test_fail(InfoAbs) + STOP + else + call test_success(InfoAbs) + endif + endif + end subroutine + + subroutine test_almost_equal_0(Var,Ref,Try,MINNORM,bStop,bPrint,bPassed) + ! Arguments + character(len=*), intent(in) :: Var + real(ReKi), intent(in) :: Ref !< + real(ReKi), intent(in) :: Try !< + real(ReKi), intent(in) :: MINNORM + logical, intent(in) :: bStop + logical, intent(in) :: bPrint + logical, intent(out),optional :: bPassed + ! Variables + character(len=255) :: InfoAbs + real(ReKi) :: delta + integer :: cpt + ! + cpt=0 + delta=abs(Ref-Try) + if(delta>MINNORM) then + write(InfoAbs,'(A,ES8.1E2,A,ES8.1E2,A,I0)') trim(Var)//' tol: ',MINNORM,', mean: ',delta,' - Failed:',cpt + call test_fail(InfoAbs,bPrint,bStop) + else + write(InfoAbs,'(A,ES8.1E2,A,ES8.1E2)') trim(Var)//' tol: ',MINNORM,', mean: ',delta + call test_success(InfoAbs,bPrint) + endif + if(present(bPassed)) then + bPassed=delta>MINNORM + endif + end subroutine + subroutine test_almost_equal_1(Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) + ! Arguments + character(len=*), intent(in) :: Var + real(ReKi), dimension(:), intent(in) :: VecRef !< + real(ReKi), dimension(:), intent(in) :: VecTry !< + real(ReKi), intent(in) :: MINNORM + logical, intent(in) :: bStop + logical, intent(in) :: bPrint + logical, intent(out),optional :: bPassed + ! Variables + character(len=255) :: InfoAbs + integer :: i,cpt + real(ReKi) :: delta + real(ReKi) :: delta_cum + ! + cpt=0 + delta_cum=0.0_ReKi + do i=1,size(VecRef,1) + delta=abs(VecRef(i)-VecTry(i)) + delta_cum=delta_cum+delta + if(delta>MINNORM) then + cpt=cpt+1 + endif + enddo + delta_cum=delta_cum/size(VecRef) + + if(cpt>0) then + write(InfoAbs,'(A,ES8.1E2,A,ES8.1E2,A,I0)') trim(Var)//' tol: ',MINNORM,', mean: ',delta_cum,' - Failed:',cpt + call test_fail(InfoAbs,bPrint,bStop) + else + write(InfoAbs,'(A,ES8.1E2,A,ES8.1E2)') trim(Var)//' tol: ',MINNORM,', mean: ',delta_cum + call test_success(InfoAbs,bPrint) + endif + if(present(bPassed)) then + bPassed=(cpt==0) + endif + end subroutine + subroutine test_almost_equal_2(Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) + ! Arguments + character(len=*), intent(in) :: Var + real(ReKi), dimension(:,:), intent(in) :: VecRef !< + real(ReKi), dimension(:,:), intent(in) :: VecTry !< + real(ReKi), intent(in) :: MINNORM + logical, intent(in) :: bStop + logical, intent(in) :: bPrint + logical, intent(out),optional :: bPassed + ! Variables + real(ReKi), dimension(:),allocatable :: VecRef2 !< + real(ReKi), dimension(:),allocatable :: VecTry2 !< + integer :: p, i,j,n1,n2,nCPs + ! + n1 = size(VecRef,1); n2 = size(VecRef,2); nCPs=n1*n2 + allocate ( VecRef2 (n1*n2) ) ; allocate ( VecTry2 (n1*n2) ) + p=0 + do j=1,n2; do i=1,n1 + p=p+1 + VecRef2(p)=VecRef(i,j) + VecTry2(p)=VecTry(i,j) + enddo; enddo; + call test_almost_equal(Var,VecRef2,VecTry2,MINNORM,bStop,bPrint,bPassed) + end subroutine + + ! --------------------------------------------------------------------------------} + ! --- Specific FVW tests + ! --------------------------------------------------------------------------------{ + !> + subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) + integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation + character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + ! + real(ReKi), dimension(3) :: P1,P2,P3,CP + real(ReKi), dimension(3) :: U1,U2 + real(ReKi) :: SegGamma1 !< Circulation [m^2/s] + real(ReKi) :: RegParam1 !< + integer(IntKi) :: i1,i2 + integer(IntKi) :: RegFunction + integer(IntKi), parameter :: nSegTot = 2 + integer(IntKi), parameter :: nSegPTot = 3 + integer(IntKi), parameter :: nCPsTot = 1 + real(ReKi), dimension(3,nCPsTot) :: CPs !< Control points + real(ReKi), dimension(3,nSegPTot) :: SegPoints !< Segment points + integer(IntKi), dimension(2,nSegTot) :: SegConnct !< Connectivity, indices of segments points iSeg1, iSeg2 + real(ReKi), dimension(nSegTot) :: SegGamma !< Segment circulation + real(ReKi), dimension(nSegTot) :: RegParam !< Regularization parameter + real(ReKi), dimension(3,nCPsTot) :: Uind_out !< Induced velocity vector - Side effects!!! + real(ReKi), dimension(3,4) :: CPs_test !< + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! --- Test that the two functions return the same values + P1=(/0. ,0.,-1./) + P2=(/0. ,0., 1./) + CPs_test(:,1) = (/ 0.0, 0., 0.0 /) ! Middle + CPs_test(:,2) = P1 ! Extremity + CPs_test(:,3) = (/ 0.05, 0., -0.5 /) ! Close + CPs_test(:,4) = (/ 10., 0., 0.0 /) ! Far + do i2 = 1, size(CPs_test,2) + ! Segment param + CP=CPs_test(:,i2) + SegGamma1=1 + RegParam1=0.5 + ! One segment param + SegConnct(:,1)=(/1,2/) + SegPoints(:,1) = P1 + SegPoints(:,2) = P2 + SegGamma(:) = SegGamma1 + RegParam(:) = RegParam1 + CPs (:,1) = CP + do i1=1,5 + RegFunction = idRegVALID(i1) + ! Method 1 + Uind_out =0.0_ReKi + call ui_seg(1, 1, nCPsTot, CPs, & + 1, 1, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, & + RegFunction, RegParam, Uind_out) + ! Method 2 + call ui_seg_11(CP-P1, CP-P2, SegGamma1, RegFunction, RegParam1, U1) + ! Test + print*,'Reg function', RegFunction, 'CP',CP + print*,'Uind_out',Uind_out + print*,'U1 ',U1 + call test_almost_equal('Uind method1/2', U1, Uind_out(:,1), 1e-4, .true.,.true.) + !call test_almost_equal('Uind method1/2', U1, Uind_out(:,1), 1e-4, .false.,.true.) + enddo + enddo + + ! --- Test that the two segments or one segment returns the same value + P1=(/0. ,0.,-1./) + P2=(/0. ,0., 1./) + P3=(/0. ,0., 0./) + CPs_test(:,1) = (/ 0.0, 0., 0.0 /) ! Middle + CPs_test(:,2) = P1 ! Extremity + CPs_test(:,3) = (/ 0.05, 0., -0.5 /) ! Close + CPs_test(:,4) = (/ 100., 0., -0.5 /) ! Far + do i2 = 1,size(CPs_test,2) + ! Segment param + CP=CPs_test(:,i2) + SegGamma1=1 + RegParam1=0.5 + ! One segment param + SegConnct(:,1)=(/1,2/) + SegConnct(:,2)=(/2,3/) + SegPoints(:,1) = P1 + SegPoints(:,2) = P3 + SegPoints(:,3) = P2 + SegGamma(:) = SegGamma1 + RegParam(:) = RegParam1 + CPs (:,1) = CP + do i1=1,4 ! NOTE stopping at 4 since Offset is not linear + RegFunction = idRegVALID(i1) + ! Method 1 + Uind_out =0.0_ReKi + call ui_seg(1, 1, nCPsTot, CPs, & + 1, 2, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, & + RegFunction, RegParam, Uind_out) + ! Method 2 + call ui_seg_11(CP-P1, CP-P2, SegGamma1, RegFunction, RegParam1, U1) + !print*,'Reg function', RegFunction, 'CP',CP + !print*,'Uind_out',Uind_out + !print*,'U1 ',U1 + call test_almost_equal('Uind 1seg/2seg', U1, Uind_out(:,1), 1e-4, .true.,.true.) + enddo + enddo + end subroutine !> subroutine Test_LatticeToSegment(iStat) @@ -141,6 +442,18 @@ subroutine MeshMe(M,offset) enddo enddo endsubroutine - endsubroutine + endsubroutine Test_LatticeToSegment + + !> Main test function + subroutine FVW_RunTests(ErrStat,ErrMsg) + integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation + character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + call Test_BiotSavart_Sgmt(ErrStat2, ErrMsg2) + end subroutine FVW_RunTests end module FVW_Tests From 4e16a100d0ab6cf08985296c76c1b3f33f3e9982 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 13 Mar 2020 21:55:51 -0600 Subject: [PATCH 075/190] FVW: storing IJ indices when packing, outputing age --- modules/aerodyn/src/FVW_BiotSavart.f90 | 2 +- modules/aerodyn/src/FVW_IO.f90 | 39 ++++++------------------- modules/aerodyn/src/FVW_Subs.f90 | 6 ++-- modules/aerodyn/src/FVW_VortexTools.f90 | 8 +++++ 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/modules/aerodyn/src/FVW_BiotSavart.f90 b/modules/aerodyn/src/FVW_BiotSavart.f90 index 1c3fac8fb9..38e5419b82 100644 --- a/modules/aerodyn/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn/src/FVW_BiotSavart.f90 @@ -105,7 +105,7 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & integer(IntKi), intent(in) :: iCPEnd !< Index where we end in Control points array integer(IntKi), intent(in) :: nCPsTot !< Total number of control points real(ReKi), dimension(3,nSegPTot), intent(in) :: SegPoints !< Segment points - integer(IntKi), dimension(2,nSegTot), intent(in) :: SegConnct !< Connectivity, indices of segments points iSeg1, iSeg2 + integer(IntKi), dimension(:,:), intent(in) :: SegConnct !< Connectivity, indices of segments points iSeg1, iSeg2, iDepth, iSpan real(ReKi), dimension(nSegTot), intent(in) :: SegGamma !< Segment circulation integer(IntKi),intent(in) :: iSegStart !< Index in SegConnct, and SegGamma where we start integer(IntKi),intent(in) :: iSegEnd !< Index in SegConnct, and SegGamma where we end diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index f90c5300b0..7a926c5c9b 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -173,7 +173,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation - integer(IntKi) :: iHeadC, iHeadP, nC, nP + integer(IntKi) :: iHeadC, iHeadP, nSeg, nSegP, iHeadC_bkp !real(ReKi), dimension(:), allocatable :: SegSmooth !< print*,'------------------------------------------------------------------------------' @@ -236,41 +236,18 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! --------------------------------------------------------------------------------} ! --- All Segments ! --------------------------------------------------------------------------------{ - nP = p%nWings * ( (p%nSpan+1)*(m%nNW+1) ) - nC = p%nWings * (2*(p%nSpan+1)*(m%nNW+1)-(p%nSpan+1)-(m%nNW+1)) - if (m%nFW>0) then - nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) - nC = nC + p%nWings * (2*(FWnSpan+1)*(m%nFW+1)-(FWnSpan+1)-(m%nFW+1)) - endif - allocate(SegConnct(1:2,1:nC)); SegConnct=-1 - allocate(SegPoints(1:3,1:nP)); SegPoints=-1 - allocate(SegGamma (1:nC)); SegGamma =-1 - iHeadP=1 - iHeadC=1 - do iW=1,p%nWings - if (m%nNW==1) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel - CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) - else - CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) - endif - enddo - if (m%nFW>0) then !TODO TODO TODO + call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) + if (m%nNW==1) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel + iHeadP=1 + iHeadC=1 do iW=1,p%nWings - CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) enddo endif + Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' CALL WrVTK_Segments(Filename, SegPoints, SegConnct, SegGamma) - if ((iHeadP-1)/=nP) then - print*,'IO: Number of points wrongly estimated',nP, iHeadP-1 - STOP - endif - if ((iHeadC-1)/=nC) then - print*,'IO: Number of segments wrongly estimated',nC, iHeadC-1 - STOP - endif - end subroutine WrVTK_FVW @@ -285,6 +262,8 @@ subroutine WrVTK_Segments(filename, SegPoints, SegConnct, SegGamma) call vtk_lines(SegConnct(1:2,:)-1) ! NOTE: VTK indexing at 0 call vtk_cell_data_init() call vtk_cell_data_scalar(SegGamma,'Gamma') + call vtk_cell_data_scalar(real(SegConnct(3,:), ReKi),'Age') + !call vtk_cell_data_scalar(real(SegConnct(4,:), ReKi),'Span') !call vtk_point_data_init() !call vtk_point_data_vector(Sgmt%UconvP(1:3,1:Sgmt%nP_Storage),'Uconv') call vtk_close_file() diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 9a250a0d01..12b1e7c7d8 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -377,7 +377,7 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG integer(IntKi), intent(out) :: nSeg !< Total number of segments after packing integer(IntKi), intent(out) :: nSegP !< Total number of segments points after packing ! Local - integer(IntKi) :: iHeadC, iHeadP, nC, nP, iW + integer(IntKi) :: iHeadC, iHeadP, nC, nP, iW, iHeadC_bkp real(ReKi), dimension(:,:), allocatable :: Buffer2d !real(ReKi), dimension(:), allocatable :: SegSmooth !< @@ -398,7 +398,7 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG if (allocated(SegPoints)) deallocate(SegPoints) if (allocated(SegGamma)) deallocate(SegGamma) if (allocated(Buffer2d)) deallocate(Buffer2d) - allocate(SegConnct(1:2,1:nC)); SegConnct=-1 + allocate(SegConnct(1:4,1:nC)); SegConnct=-1 allocate(SegPoints(1:3,1:nP)); SegPoints=-1 allocate(SegGamma (1:nC)); SegGamma =-1 allocate(Buffer2d(1,p%nSpan)) @@ -409,9 +409,11 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) enddo if (m%nFW>0) then + iHeadC_bkp = iHeadC do iW=1,p%nWings CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) enddo + SegConnct(3,iHeadC_bkp:) = SegConnct(3,iHeadC_bkp:) + m%nNW endif if ((iHeadP-1)/=nP) then print*,'PackPanelsToSegments: Number of points wrongly estimated',nP, iHeadP-1 diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index 483c906d1d..16ef00cacc 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -109,17 +109,23 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints ! Segment 1-2 SegConnct(1,iHeadC) = iseg1 SegConnct(2,iHeadC) = iseg2 + SegConnct(3,iHeadC) = iDepth + SegConnct(4,iHeadC) = iSpan SegGamma (iHeadC ) = Gamma12 iHeadC=iHeadC+1 ! Segment 1-4 SegConnct(1,iHeadC) = iseg1 SegConnct(2,iHeadC) = iseg4 + SegConnct(3,iHeadC) = iDepth + SegConnct(4,iHeadC) = iSpan SegGamma (iHeadC ) = -Gamma41 iHeadC=iHeadC+1 ! Segment 4-3 if (iDepth==nDepth-1) then SegConnct(1,iHeadC) = iseg4 SegConnct(2,iHeadC) = iseg3 + SegConnct(3,iHeadC) = iDepth + SegConnct(4,iHeadC) = iSpan SegGamma (iHeadC ) = - LatticeGamma(iSpan,iDepth) iHeadC=iHeadC+1 endif @@ -127,6 +133,8 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints if (iSpan==nSpan-1) then SegConnct(1,iHeadC) = iseg2 SegConnct(2,iHeadC) = iseg3 + SegConnct(3,iHeadC) = iDepth + SegConnct(4,iHeadC) = iSpan SegGamma (iHeadC ) = LatticeGamma(iSpan,iDepth) iHeadC=iHeadC+1 endif From 4b0a32c8fac098b7d838735b49b006d8391b3cd7 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 16 Mar 2020 16:27:50 -0600 Subject: [PATCH 076/190] FVW: introducing core-spreading regularization, better regularization handling, more input params --- modules/aerodyn/src/FVW.f90 | 7 ++- modules/aerodyn/src/FVW_IO.f90 | 57 +++++++++++-------- modules/aerodyn/src/FVW_Registry.txt | 14 +++-- modules/aerodyn/src/FVW_Subs.f90 | 83 +++++++++++++++++++++------- modules/aerodyn/src/FVW_Tests.f90 | 16 +++--- modules/aerodyn/src/FVW_Types.f90 | 82 ++++++++++++++++++++------- modules/aerodyn/src/FVW_Wings.f90 | 2 +- 7 files changed, 185 insertions(+), 76 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index ab703eae34..0829591c54 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -321,10 +321,13 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%FreeWakeStart = InputFileData%FreeWakeStart p%CircSolvPolar = InputFileData%CircSolvPolar p%FullCirculationStart = InputFileData%FullCirculationStart + p%DiffusionMethod = InputFileData%DiffusionMethod p%RegFunction = InputFileData%RegFunction + p%RegDeterMethod = InputFileData%RegDeterMethod p%WakeRegMethod = InputFileData%WakeRegMethod - p%WakeRegFactor = InputFileData%WakeRegFactor - p%WingRegFactor = InputFileData%WingRegFactor + p%WakeRegParam = InputFileData%WakeRegParam + p%WingRegParam = InputFileData%WingRegParam + p%CoreSpreadEddyVisc = InputFileData%CoreSpreadEddyVisc p%WrVTK = InputFileData%WrVTK p%VTKBlades = min(max(InputFileData%VTKBlades,0),p%nWings) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 7a926c5c9b..57850a171e 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -50,14 +50,17 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) !CALL ReadVar(UnIn,FileName,Inp%CircSolvPolar ,'CircSolvPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return !------------------------ WAKE OPTIONS ------------------------------------------- - CALL ReadCom (UnIn,FileName, 'Wake options header' , ErrStat2,ErrMsg2); if(Failed()) return - CALL ReadVar (UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar (UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar (UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'' , ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',idRegVatistas, ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',idRegConstant, ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar (UnIn,FileName,Inp%WakeRegFactor ,'WakeRegFactor' ,'' , ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar (UnIn,FileName,Inp%WingRegFactor ,'WingRegFactor' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadCom (UnIn,FileName, 'Wake options header' , ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadVar (UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar (UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar (UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%DiffusionMethod ,'DiffusionMethod' ,'',idDiffusionNone , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%RegDeterMethod ,'RegDeterMethod' ,'',idRegDeterManual, ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',idRegVatistas , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',idRegConstant , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar (UnIn,FileName,Inp%WakeRegParam ,'WakeRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar (UnIn,FileName,Inp%WingRegParam ,'WingRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%CoreSpreadEddyVisc ,'CoreSpreadEddyVisc','',10.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return !------------------------ OUTPUT OPTIONS ----------------------------------------- CALL ReadCom(UnIn,FileName, 'Output options header', ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVar(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'',ErrStat2,ErrMsg2); if(Failed())return @@ -67,9 +70,12 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) ! --- Validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) - if (Check(.not.(ANY((/idCircPrescribed,idCircPolarData/)==Inp%CirculationMethod)), 'Circulation method not implemented')) return - - if (Check( Inp%IntMethod/=idEuler1 , 'Time integration method not yet implemented. Use Euler 1st order method for now.')) return + if (Check(.not.(ANY(idCircVALID ==Inp%CirculationMethod)), 'Circulation method (CircSolvingMethod) not implemented')) return + if (Check(.not.(ANY(idIntMethodVALID==Inp%IntMethod )) , 'Time integration method (IntMethod) not yet implemented. Use Euler 1st order method for now.')) return + if (Check(.not.(ANY(idDiffusionVALID==Inp%DiffusionMethod)) , 'Diffusion method (DiffusionMethod) not yet implemented. Use None for now.')) return + if (Check(.not.(ANY(idRegDeterVALID ==Inp%RegDeterMethod)) , 'Regularization determination method (RegDeterMethod) not yet implemented. Use Manual method for now.')) return + if (Check(.not.(ANY(idRegVALID ==Inp%RegFunction )), 'Regularization function (RegFunction) not implemented')) return + if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method (WakeRegMethod) not implemented')) return if (Check( Inp%DTfvw < p%DTaero, 'DTfvw must be >= DTaero from AD15.')) return @@ -78,10 +84,9 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check( Inp%nFWPanelsFree<0 , 'Number of free far wake panels must be >=0')) return if (Check( Inp%nFWPanelsFree>Inp%nFWPanels , 'Number of free far wake panels must be <=Number of far wake panels')) return - if (Check(.not.(ANY(idRegVALID ==Inp%RegFunction )), 'Regularization function not implemented')) return - if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method not implemented')) return - if (Check(Inp%WakeRegFactor<0 , 'Wake regularization factor should be positive')) return - if (Check(Inp%WingRegFactor<0 , 'Wing regularization factor should be positive')) return + if (Check(Inp%WakeRegParam<0 , 'Wake regularization parameter (WakeRegParam) should be positive')) return + if (Check(Inp%WingRegParam<0 , 'Wing regularization parameter (WakeRegParam) should be positive')) return + if (Check(Inp%CoreSpreadEddyVisc<0 , 'Core spreading eddy viscosity (CoreSpreadEddyVisc) should be positive')) return Inp%DTvtk = Get_DTvtk( VTK_fps_line, p%DTaero, Inp%DTfvw ) @@ -173,8 +178,10 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation + real(ReKi), dimension(:), allocatable :: SegEpsilon !< integer(IntKi) :: iHeadC, iHeadP, nSeg, nSegP, iHeadC_bkp - !real(ReKi), dimension(:), allocatable :: SegSmooth !< + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 print*,'------------------------------------------------------------------------------' print'(A,L1,A,I0,A,I0,A,I0)','VTK Output - First call ',m%FirstCall, ' nNW:',m%nNW,' nFW:',m%nFW,' i:',VTKCount @@ -245,23 +252,29 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) enddo endif + allocate(SegEpsilon(1:size(SegConnct,2))) + call WakeRegularization(p, x, m, SegConnct, SegPoints, SegGamma, SegEpsilon, ErrStat2, ErrMsg2) + Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' - CALL WrVTK_Segments(Filename, SegPoints, SegConnct, SegGamma) + CALL WrVTK_Segments(Filename, SegPoints, SegConnct, SegGamma, SegEpsilon) + deallocate(SegEpsilon) end subroutine WrVTK_FVW -subroutine WrVTK_Segments(filename, SegPoints, SegConnct, SegGamma) +subroutine WrVTK_Segments(filename, SegPoints, SegConnct, SegGamma, SegEpsilon) use VTK character(len=*),intent(in) :: filename - real(ReKi), dimension(:,:), intent(in) :: SegPoints !< - integer(IntKi), dimension(:,:), intent(in) :: SegConnct !< - real(ReKi), dimension(:) , intent(in) :: SegGamma !< + real(ReKi), dimension(:,:), intent(in) :: SegPoints !< + integer(IntKi), dimension(:,:), intent(in) :: SegConnct !< + real(ReKi), dimension(:) , intent(in) :: SegGamma !< + real(ReKi), dimension(:) , intent(in) :: SegEpsilon !< if ( vtk_new_ascii_file(filename,'Sgmt') ) then call vtk_dataset_polydata(SegPoints(1:3,:)) call vtk_lines(SegConnct(1:2,:)-1) ! NOTE: VTK indexing at 0 call vtk_cell_data_init() - call vtk_cell_data_scalar(SegGamma,'Gamma') + call vtk_cell_data_scalar(SegGamma ,'Gamma') + call vtk_cell_data_scalar(SegEpsilon,'Epsilon') call vtk_cell_data_scalar(real(SegConnct(3,:), ReKi),'Age') !call vtk_cell_data_scalar(real(SegConnct(4,:), ReKi),'Span') !call vtk_point_data_init() diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 0c0fa58a26..0f1469370a 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -25,10 +25,13 @@ typedef ^ ^ IntKi typedef ^ ^ ReKi CircSolvConvCrit - - - "Convergence criterion for circulation solving" - typedef ^ ^ ReKi CircSolvRelaxation - - - "Relaxation factor for circulation solving" - typedef ^ ^ IntKi CircSolvPolar - - - "(0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha)" - +typedef ^ ^ IntKi DiffusionMethod - - - "Diffusion method (None, CoreSpreading, PSE)" - +typedef ^ ^ ReKi CoreSpreadEddyVisc - - - "Eddy viscosity used in the core spreading method" +typedef ^ ^ IntKi RegDeterMethod - - - "Regularization determinatino method (manual, automatic)" - typedef ^ ^ IntKi RegFunction - - - "Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart)" - typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - -typedef ^ ^ ReKi WakeRegFactor - - - "Factor used in the regularization " -typedef ^ ^ ReKi WingRegFactor - - - "Factor used in the regularization " +typedef ^ ^ ReKi WakeRegParam - - - "Initial value of the regularization parameter" +typedef ^ ^ ReKi WingRegParam - - - "Regularization parameter of the wing" typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ IntKi HACK - - - "HACK ID" - @@ -138,10 +141,13 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi nNWPanels - - - "Number of nw panels" - typedef ^ ^ IntKi nFWPanels - - - "Number of fw panels" - typedef ^ ^ IntKi nFWPanelsFree - - - "Number of fw panels that are free" - +typedef ^ ^ IntKi DiffusionMethod - - - "Diffusion method (None, CoreSpreading, PSE)" - +typedef ^ ^ ReKi CoreSpreadEddyVisc - - - "Eddy viscosity used in the core spreading method" +typedef ^ ^ IntKi RegDeterMethod - - - "Regularization determinatino method (manual, automatic)" - typedef ^ ^ IntKi RegFunction - - - "Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart)" - typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - -typedef ^ ^ ReKi WakeRegFactor - - - "Factor used in the regularization " -typedef ^ ^ ReKi WingRegFactor - - - "Factor used in the regularization " +typedef ^ ^ ReKi WakeRegParam - - - "Factor used in the regularization " +typedef ^ ^ ReKi WingRegParam - - - "Factor used in the regularization " typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ DbKi DTvtk - - - "Requested timestep between VTK outputs (calculated from the VTK_fps read in)" s diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 12b1e7c7d8..540ed7ad5b 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -12,21 +12,30 @@ module FVW_SUBS integer(IntKi), parameter :: idCircPolarData = 1 integer(IntKi), parameter :: idCircNoFlowThrough = 2 integer(IntKi), parameter :: idCircPrescribed = 3 - ! Polar data - !integer(IntKi), parameter :: idPolarAeroDyn = 0 - !integer(IntKi), parameter :: idPolar2PiAlpha = 1 - !integer(IntKi), parameter :: idPolar2PiSinAlpha = 2 + integer(IntKi), parameter, dimension(2) :: idCircVALID = (/idCircPolarData, idCircPrescribed /) ! Integration method integer(IntKi), parameter :: idRK4 = 1 integer(IntKi), parameter :: idAB4 = 2 integer(IntKi), parameter :: idABM4 = 3 integer(IntKi), parameter :: idPredictor= 4 integer(IntKi), parameter :: idEuler1 = 5 + integer(IntKi), parameter, dimension(1) :: idIntMethodVALID = (/idEuler1 /) + ! Diffusion method + integer(IntKi), parameter :: idDiffusionNone = 0 + integer(IntKi), parameter :: idDiffusionCoreSpread = 1 + integer(IntKi), parameter :: idDiffusionPSE = 2 + integer(IntKi), parameter, dimension(1) :: idDiffusionVALID = (/idDiffusionNone /) ! Regularization Method integer(IntKi), parameter :: idRegConstant = 1 integer(IntKi), parameter :: idRegStretching = 2 integer(IntKi), parameter :: idRegAge = 3 - integer(IntKi), parameter, dimension(3) :: idRegMethodVALID = (/idRegConstant,idRegStretching,idRegAge/) + integer(IntKi), parameter, dimension(2) :: idRegMethodVALID = (/idRegConstant,idRegAge/) + ! Regularization determination method + integer(IntKi), parameter :: idRegDeterManual = 0 + integer(IntKi), parameter :: idRegDeterAuto = 1 + integer(IntKi), parameter, dimension(1) :: idRegDeterVALID = (/idRegDeterManual /) + + real(ReKi), parameter :: CoreSpreadAlpha = 1.25643 ! Implementation integer(IntKi), parameter :: iNWStart=2 !< Index in r%NW where the near wake start (if >1 then the Wing panels are included in r_NW) @@ -432,6 +441,47 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG endif end subroutine PackPanelsToSegments +!> Set up regularization parameter based on diffusion method and regularization method +!! NOTE: this should preferably be done at the "panel"/vortex sheet level +subroutine WakeRegularization(p, x, m, SegConnct, SegPoints, SegGamma, SegEpsilon, ErrStat, ErrMsg) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< States + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + integer(IntKi),dimension(:,:) , intent(in ) :: SegConnct !< Segment connectivity + real(ReKi), dimension(:,:) , intent(in ) :: SegPoints !< Segment Points + real(ReKi), dimension(:) , intent(in ) :: SegGamma !< Segment Circulation + real(ReKi), dimension(:) , intent( out) :: SegEpsilon !< Segment regularization parameter + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer(IntKi) :: iSeg + real(ReKi) :: time + ErrStat = ErrID_None + ErrMsg = "" + + ! + if (p%WakeRegMethod==idRegConstant) then + SegEpsilon=p%WakeRegParam + + else if (p%WakeRegMethod==idRegStretching) then + ! TODO + ErrStat = ErrID_Fatal + ErrMsg ='Regularization method not implemented' + + else if (p%WakeRegMethod==idRegAge) then + do iSeg=1,size(SegEpsilon,1) ! loop on segments + time = (SegConnct(3, iSeg)-1) * p%DTfvw ! column 3 contains "iDepth", or "iAge", from 1 to nSteps + SegEpsilon(iSeg) = sqrt( 4._ReKi * CoreSpreadAlpha * p%CoreSpreadEddyVisc * p%KinVisc* time + p%WakeRegParam**2 ) + enddo + + else + ErrStat = ErrID_Fatal + ErrMsg ='Regularization method not implemented' + endif + +end subroutine WakeRegularization + + !> Compute induced velocities from all vortex elements onto all the vortex elements !! In : x%r_NW, x%r_FW, x%Gamma_NW, x%Gamma_FW !! Out: m%Vind_NW, m%Vind_FW @@ -439,6 +489,8 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs, iHeadP integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity @@ -447,8 +499,6 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) real(ReKi), dimension(:) , allocatable :: SegEpsilon !< Segment regularization parameter real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity - integer(IntKi), intent( out) :: ErrStat !< Error status of the operation - character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step nFWEff = min(m%nFW, p%nFWFree) @@ -456,16 +506,12 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) m%Vind_FW = -9999._ReKi !< Safety ! --- Packing all vortex elements into a list of segments + ! NOTE: allocates SegConnct, SegPoints, SegGamma.. call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) ! --- Setting up regularization allocate(SegEpsilon(1:nSeg)); - if (p%WakeRegMethod==idRegConstant) then - SegEpsilon=p%WakeRegFactor ! TODO - else - print*,'Regularization method not implemented',p%WakeRegMethod - STOP - endif + call WakeRegularization(p, x, m, SegConnct, SegPoints, SegGamma, SegEpsilon, ErrStat, ErrMsg) ! --- Computing induced velocity call PackConvectingPoints() @@ -557,6 +603,8 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None integer(IntKi) :: i + ErrStat = ErrID_None + ErrMsg = "" m%Vind_LL = -9999._ReKi !< Safety ! --- Packing all vortex elements into a list of segments @@ -570,12 +618,7 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) else ! --- Setting up regularization allocate(SegEpsilon(1:nSeg)); - if (p%WakeRegMethod==idRegConstant) then - SegEpsilon=p%WakeRegFactor ! TODO - else - print*,'Regularization method not implemented',p%WakeRegMethod - STOP - endif + call WakeRegularization(p, x, m, SegConnct, SegPoints, SegGamma, SegEpsilon, ErrStat, ErrMsg) nCPs=p%nWings * p%nSpan allocate(CPs (1:3,1:nCPs)) @@ -655,6 +698,8 @@ subroutine FVW_AeroOuts( M_sg, M_ag, PitchAndTwist, Vstr_g, Vind_g, Vwnd_g, Kin real(ReKi) :: Vtot_g(3) ! Vector of total relative velocity section coord real(ReKi) :: Vtot_a(3) ! Vector of total relative velocity global coord real(ReKi) :: Vtot_s(3) ! Vector of total relative velocity global coord + ErrStat = ErrID_None + ErrMsg = "" !real(DbKi), dimension(3,3) :: M_sa !< Transformation matrix from airfoil to section coord !real(DbKi), dimension(3,3) :: M_sg2 !< Transformation matrix from global to section coord ! --- Transformation from airfoil to section (KEEP ME) diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index 279b8297f0..dbd279923b 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -321,7 +321,7 @@ subroutine Test_LatticeToSegment(iStat) integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation - real(ReKi), dimension(:), allocatable :: SegSmooth !< + real(ReKi), dimension(:), allocatable :: SegEpsilon !< ! real(ReKi), dimension(:,:,:), allocatable :: LatticePoints1 !< Lattice Points real(ReKi), dimension(:,:,:), allocatable :: LatticePoints2 !< Lattice Points @@ -366,23 +366,23 @@ subroutine Test_LatticeToSegment(iStat) allocate(SegConnct(1:2,1:nC1)); SegConnct=-1 allocate(SegPoints(1:3,1:nP1)); SegPoints=-1 allocate(SegGamma (1:nC1) ); SegGamma=-999 - allocate(SegSmooth(1:nC1) ); SegSmooth=0.0_ReKi + allocate(SegEpsilon(1:nC1) ); SegEpsilon=0.0_ReKi iHeadP=1 iHeadC=1 CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) CALL printall() - CALL WrVTK_Segments('Points1_seg.vtk', SegPoints, SegConnct, SegGamma) + CALL WrVTK_Segments('Points1_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) allocate(Uind(1:3,1) ); Uind=0.0_ReKi allocate(CPs (1:3,1) ); CPs(1:3,1)=(/1.5,1.5,0./) - SegSmooth=100.0_ReKi + SegEpsilon=100.0_ReKi SmoothModel=0 ! No smooth CALL ui_seg(1, 1, 1, CPs, & 1, nC1, nC1, nP1, SegPoints, SegConnct, SegGamma, & - SmoothModel, SegSmooth, Uind) - print*,'Uind',Uind + SmoothModel, SegEpsilon, Uind) + print*,'Uind',Uind ! --- Convert lattice 2 to segments nSpan = size(LatticePoints2,2) @@ -399,7 +399,7 @@ subroutine Test_LatticeToSegment(iStat) iHeadC=1 CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) CALL printall() - CALL WrVTK_Segments('Points2_seg.vtk', SegPoints, SegConnct, SegGamma) + CALL WrVTK_Segments('Points2_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) ! --- Concatenate both nP = nP1 + nP2 @@ -415,7 +415,7 @@ subroutine Test_LatticeToSegment(iStat) CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) CALL printall() - CALL WrVTK_Segments('PointsBoth_seg.vtk', SegPoints, SegConnct, SegGamma) + CALL WrVTK_Segments('PointsBoth_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) contains diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 28d9020a44..63d91929ab 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -52,10 +52,13 @@ MODULE FVW_Types REAL(ReKi) :: CircSolvConvCrit !< Convergence criterion for circulation solving [-] REAL(ReKi) :: CircSolvRelaxation !< Relaxation factor for circulation solving [-] INTEGER(IntKi) :: CircSolvPolar !< (0=Use AD polars, 1=2PiAlpha, 2=sin(2pialpha) [-] + INTEGER(IntKi) :: DiffusionMethod !< Diffusion method (None, CoreSpreading, PSE) [-] + REAL(ReKi) :: CoreSpreadEddyVisc !< Eddy viscosity used in the core spreading method [-] + INTEGER(IntKi) :: RegDeterMethod !< Regularization determinatino method (manual, automatic) [-] INTEGER(IntKi) :: RegFunction !< Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart) [-] INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] - REAL(ReKi) :: WakeRegFactor !< Factor used in the regularization [-] - REAL(ReKi) :: WingRegFactor !< Factor used in the regularization [-] + REAL(ReKi) :: WakeRegParam !< Initial value of the regularization parameter [-] + REAL(ReKi) :: WingRegParam !< Regularization parameter of the wing [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] INTEGER(IntKi) :: HACK !< HACK ID [-] @@ -169,10 +172,13 @@ MODULE FVW_Types INTEGER(IntKi) :: nNWPanels !< Number of nw panels [-] INTEGER(IntKi) :: nFWPanels !< Number of fw panels [-] INTEGER(IntKi) :: nFWPanelsFree !< Number of fw panels that are free [-] + INTEGER(IntKi) :: DiffusionMethod !< Diffusion method (None, CoreSpreading, PSE) [-] + REAL(ReKi) :: CoreSpreadEddyVisc !< Eddy viscosity used in the core spreading method [-] + INTEGER(IntKi) :: RegDeterMethod !< Regularization determinatino method (manual, automatic) [-] INTEGER(IntKi) :: RegFunction !< Type of regularizaion function (LambOseen, Vatistas, see FVW_BiotSavart) [-] INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] - REAL(ReKi) :: WakeRegFactor !< Factor used in the regularization [-] - REAL(ReKi) :: WingRegFactor !< Factor used in the regularization [-] + REAL(ReKi) :: WakeRegParam !< Factor used in the regularization [-] + REAL(ReKi) :: WingRegParam !< Factor used in the regularization [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] REAL(DbKi) :: DTvtk !< Requested timestep between VTK outputs (calculated from the VTK_fps read in) [s] @@ -255,10 +261,13 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%CircSolvConvCrit = SrcParamData%CircSolvConvCrit DstParamData%CircSolvRelaxation = SrcParamData%CircSolvRelaxation DstParamData%CircSolvPolar = SrcParamData%CircSolvPolar + DstParamData%DiffusionMethod = SrcParamData%DiffusionMethod + DstParamData%CoreSpreadEddyVisc = SrcParamData%CoreSpreadEddyVisc + DstParamData%RegDeterMethod = SrcParamData%RegDeterMethod DstParamData%RegFunction = SrcParamData%RegFunction DstParamData%WakeRegMethod = SrcParamData%WakeRegMethod - DstParamData%WakeRegFactor = SrcParamData%WakeRegFactor - DstParamData%WingRegFactor = SrcParamData%WingRegFactor + DstParamData%WakeRegParam = SrcParamData%WakeRegParam + DstParamData%WingRegParam = SrcParamData%WingRegParam DstParamData%WrVTK = SrcParamData%WrVTK DstParamData%VTKBlades = SrcParamData%VTKBlades DstParamData%HACK = SrcParamData%HACK @@ -351,10 +360,13 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_BufSz = Re_BufSz + 1 ! CircSolvConvCrit Re_BufSz = Re_BufSz + 1 ! CircSolvRelaxation Int_BufSz = Int_BufSz + 1 ! CircSolvPolar + Int_BufSz = Int_BufSz + 1 ! DiffusionMethod + Re_BufSz = Re_BufSz + 1 ! CoreSpreadEddyVisc + Int_BufSz = Int_BufSz + 1 ! RegDeterMethod Int_BufSz = Int_BufSz + 1 ! RegFunction Int_BufSz = Int_BufSz + 1 ! WakeRegMethod - Re_BufSz = Re_BufSz + 1 ! WakeRegFactor - Re_BufSz = Re_BufSz + 1 ! WingRegFactor + Re_BufSz = Re_BufSz + 1 ! WakeRegParam + Re_BufSz = Re_BufSz + 1 ! WingRegParam Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades Int_BufSz = Int_BufSz + 1 ! HACK @@ -460,13 +472,19 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%CircSolvPolar Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%DiffusionMethod + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%CoreSpreadEddyVisc + Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%RegDeterMethod + Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%RegFunction Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WakeRegMethod Int_Xferred = Int_Xferred + 1 - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegFactor + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegParam Re_Xferred = Re_Xferred + 1 - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WingRegFactor + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WingRegParam Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK Int_Xferred = Int_Xferred + 1 @@ -621,13 +639,19 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + 1 OutData%CircSolvPolar = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%DiffusionMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%CoreSpreadEddyVisc = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%RegDeterMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%RegFunction = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%WakeRegMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 - OutData%WakeRegFactor = ReKiBuf( Re_Xferred ) + OutData%WakeRegParam = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 - OutData%WingRegFactor = ReKiBuf( Re_Xferred ) + OutData%WingRegParam = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 OutData%WrVTK = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 @@ -4783,10 +4807,13 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%nNWPanels = SrcInputFileData%nNWPanels DstInputFileData%nFWPanels = SrcInputFileData%nFWPanels DstInputFileData%nFWPanelsFree = SrcInputFileData%nFWPanelsFree + DstInputFileData%DiffusionMethod = SrcInputFileData%DiffusionMethod + DstInputFileData%CoreSpreadEddyVisc = SrcInputFileData%CoreSpreadEddyVisc + DstInputFileData%RegDeterMethod = SrcInputFileData%RegDeterMethod DstInputFileData%RegFunction = SrcInputFileData%RegFunction DstInputFileData%WakeRegMethod = SrcInputFileData%WakeRegMethod - DstInputFileData%WakeRegFactor = SrcInputFileData%WakeRegFactor - DstInputFileData%WingRegFactor = SrcInputFileData%WingRegFactor + DstInputFileData%WakeRegParam = SrcInputFileData%WakeRegParam + DstInputFileData%WingRegParam = SrcInputFileData%WingRegParam DstInputFileData%WrVTK = SrcInputFileData%WrVTK DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades DstInputFileData%DTvtk = SrcInputFileData%DTvtk @@ -4852,10 +4879,13 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! nNWPanels Int_BufSz = Int_BufSz + 1 ! nFWPanels Int_BufSz = Int_BufSz + 1 ! nFWPanelsFree + Int_BufSz = Int_BufSz + 1 ! DiffusionMethod + Re_BufSz = Re_BufSz + 1 ! CoreSpreadEddyVisc + Int_BufSz = Int_BufSz + 1 ! RegDeterMethod Int_BufSz = Int_BufSz + 1 ! RegFunction Int_BufSz = Int_BufSz + 1 ! WakeRegMethod - Re_BufSz = Re_BufSz + 1 ! WakeRegFactor - Re_BufSz = Re_BufSz + 1 ! WingRegFactor + Re_BufSz = Re_BufSz + 1 ! WakeRegParam + Re_BufSz = Re_BufSz + 1 ! WingRegParam Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades Db_BufSz = Db_BufSz + 1 ! DTvtk @@ -4916,13 +4946,19 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWPanelsFree Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%DiffusionMethod + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%CoreSpreadEddyVisc + Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%RegDeterMethod + Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%RegFunction Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WakeRegMethod Int_Xferred = Int_Xferred + 1 - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegFactor + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WakeRegParam Re_Xferred = Re_Xferred + 1 - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WingRegFactor + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WingRegParam Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK Int_Xferred = Int_Xferred + 1 @@ -4994,13 +5030,19 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%nFWPanelsFree = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%DiffusionMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%CoreSpreadEddyVisc = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%RegDeterMethod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%RegFunction = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%WakeRegMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 - OutData%WakeRegFactor = ReKiBuf( Re_Xferred ) + OutData%WakeRegParam = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 - OutData%WingRegFactor = ReKiBuf( Re_Xferred ) + OutData%WingRegParam = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 OutData%WrVTK = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 3eefffaaf3..b63e46cbbb 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -335,7 +335,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x P4=x%r_NW(1:3,iSpan ,iDepth+1,iW) Gamm=GammaLastIter(iSpan, iW) do iWCP=1,p%nWings - call ui_quad_n1(m%CP_LL(1:3,1:p%nSpan,iWCP), nCPs, P1, P2, P3, P4, Gamm, p%RegFunction, p%WakeRegFactor, Vvar(1:3,1:p%nSpan,iWCP)) + call ui_quad_n1(m%CP_LL(1:3,1:p%nSpan,iWCP), nCPs, P1, P2, P3, P4, Gamm, p%RegFunction, p%WingRegParam, Vvar(1:3,1:p%nSpan,iWCP)) enddo enddo enddo From ef6f347becdb43d924bc7df86a67962051777537 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 16 Mar 2020 18:17:10 -0600 Subject: [PATCH 077/190] FVW: starting auto-reg, computing panel diagonals --- modules/aerodyn/src/FVW.f90 | 6 ++- modules/aerodyn/src/FVW_Registry.txt | 1 + modules/aerodyn/src/FVW_Subs.f90 | 61 +++++++++++++++++++++++++- modules/aerodyn/src/FVW_Types.f90 | 65 ++++++++++++++++++++++++++++ modules/aerodyn/src/FVW_Wings.f90 | 13 +++++- 5 files changed, 142 insertions(+), 4 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 0829591c54..145c0a7801 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -107,7 +107,6 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! Set parameters from InputFileData (need Misc allocated) CALL FVW_SetParametersFromInputFile(InputFileData, p, m, ErrStat2, ErrMsg2); if(Failed()) return - CALL FVW_ToString(p, m) ! Print to screen ! Initialize States Vars CALL FVW_InitStates( x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return @@ -118,6 +117,8 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! Panelling wings based on initial input mesh provided ! This mesh is now a cousin of the BladeMotion mesh from AD. CALL Wings_Panelling (u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL FVW_InitRegularization(p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL FVW_ToString(p, m) ! Print to screen ! Initialize output CALL FVW_Init_Y( p, u, y, ErrStat2, ErrMsg2); if(Failed()) return @@ -174,6 +175,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%Orth , 3 , p%nSpan , p%nWings, 'Orthogonal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Orth= -999999_ReKi; call AllocAry( m%dl , 3 , p%nSpan , p%nWings, 'Orthogonal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%dl= -999999_ReKi; call AllocAry( m%Area , p%nSpan , p%nWings, 'LL Panel area ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Area = -999999_ReKi; + call AllocAry( m%diag_LL , p%nSpan , p%nWings, 'LL Panel diagonals ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%diag_LL = -999999_ReKi; call AllocAry( m%Vind_LL , 3 , p%nSpan , p%nWings, 'Vind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_LL= -999999_ReKi; call AllocAry( m%Vtot_LL , 3 , p%nSpan , p%nWings, 'Vtot on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vtot_LL= -999999_ReKi; call AllocAry( m%Vstr_LL , 3 , p%nSpan , p%nWings, 'Vstr on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vstr_LL= -999999_ReKi; @@ -346,7 +348,7 @@ end subroutine FVW_SetParametersFromInputFile subroutine FVW_ToString(p,m) type(FVW_ParameterType), intent(in) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Misc - print*,'------------------------------------' + print*,'-----------------------------------------------------------------------------------------' end subroutine FVW_ToString !---------------------------------------------------------------------------------------------------------------------------------- diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 0f1469370a..0dc130bbd0 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -62,6 +62,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi Orth ::: - - "Unit Orthogonal vector on LL CP" - typedef ^ ^ ReKi dl ::: - - "Vector of elementary length along the LL" - typedef ^ ^ ReKi Area :: - - "Area of each LL panel" - +typedef ^ ^ ReKi diag_LL :: - - "Diagonal length of each LL panel" - typedef ^ ^ Reki Gamma_LL :: - - "Circulation on the wing lifting line (COPY of Constraint State)" - typedef ^ ^ ReKi Vind_LL ::: - - "Induced velocity on lifting line control points" m/s typedef ^ ^ ReKi Vtot_LL ::: - - "Total velocity on lifting line control points" m/s diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 540ed7ad5b..ed0f03ec7d 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -33,7 +33,7 @@ module FVW_SUBS ! Regularization determination method integer(IntKi), parameter :: idRegDeterManual = 0 integer(IntKi), parameter :: idRegDeterAuto = 1 - integer(IntKi), parameter, dimension(1) :: idRegDeterVALID = (/idRegDeterManual /) + integer(IntKi), parameter, dimension(2) :: idRegDeterVALID = (/idRegDeterManual, idRegDeterAuto /) real(ReKi), parameter :: CoreSpreadAlpha = 1.25643 @@ -441,6 +441,65 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG endif end subroutine PackPanelsToSegments +!> Set up regularization parameter based on diffusion method and regularization method +!! NOTE: this should preferably be done at the "panel"/vortex sheet level +subroutine FVW_InitRegularization(p, m, ErrStat, ErrMsg) + type(FVW_ParameterType), intent(inout) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer(IntKi) :: iSeg + real(ReKi) :: ds_min, ds_max, ds_mean !< min,max and mean of spanwise sections + real(ReKi) :: c_min, c_max, c_mean !< min,max and mean of chord + real(ReKi) :: d_min, d_max, d_mean !< min,max and mean of panel diagonal + real(ReKi) :: RegParam + real(ReKi) :: Span !< "Blade span" + integer :: iSpan, iW + ErrStat = ErrID_None + ErrMsg = "" + ! --- Compute min max and mean spanwise section lengths + iW =1 + ds_min = minval(m%s_ll(2:p%nSpan+1,iW)-m%s_ll(1:p%nSpan,iW)) + ds_max = maxval(m%s_ll(2:p%nSpan+1,iW)-m%s_ll(1:p%nSpan,iW)) + ds_mean = sum(m%s_ll(2:p%nSpan+1,iW)-m%s_ll(1:p%nSpan,iW))/(p%nSpan+1) + c_min = minval(m%chord_LL(:,iW)) + c_max = maxval(m%chord_LL(:,iW)) + c_mean = sum (m%chord_LL(:,iW))/(p%nSpan+1) + d_min = minval(m%diag_LL(:,iW)) + d_max = maxval(m%diag_LL(:,iW)) + d_mean = sum (m%diag_LL(:,iW))/(p%nSpan+1) + Span = m%s_ll(p%nSpan+1,iW)-m%s_ll(1,iW) + RegParam = ds_mean*10 + write(*,'(A)')'-----------------------------------------------------------------------------------------' + write(*,'(A)')'Regularization Info' + write(*,'(A,1F8.4,A)') 'Span : ',Span + write(*,'(A,3F8.4,A)') 'Chord : ',c_min,c_mean,c_max,' (min, mean, max)' + write(*,'(A,3F8.4,A)') 'Spanwise distretization: ',ds_min,ds_mean,ds_max,' (min, mean, max)' + write(*,'(A,3F8.4,A)') 'Diagonal distretization: ',d_min,d_mean,d_max,' (min, mean, max)' + write(*,'(A,1F8.4)') 'RegParam (Recommended) : ',RegParam + write(*,'(A,1F8.4)') 'RegParam (Input ) : ',p%WakeRegParam + if (p%RegDeterMethod==idRegDeterAuto) then + ! TODO this is beta + print*,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + print*,'!!! NOTE: using optmized wake regularization parameters is still a beta feature!' + print*,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + p%WakeRegParam = RegParam + p%WingRegParam = RegParam + p%CoreSpreadEddyVisc = 100 + p%RegFunction = idRegVatistas + endif + ! KEEP ME: potentially perform pre-computation here + !if (p%WakeRegMethod==idRegConstant) then + !else if (p%WakeRegMethod==idRegStretching) then + !else if (p%WakeRegMethod==idRegAge) then + !else + ! ErrStat = ErrID_Fatal + ! ErrMsg ='Regularization method not implemented' + !endif +end subroutine FVW_InitRegularization + + !> Set up regularization parameter based on diffusion method and regularization method !! NOTE: this should preferably be done at the "panel"/vortex sheet level subroutine WakeRegularization(p, x, m, SegConnct, SegPoints, SegGamma, SegEpsilon, ErrStat, ErrMsg) diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 63d91929ab..95bce38443 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -89,6 +89,7 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Orth !< Unit Orthogonal vector on LL CP [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: dl !< Vector of elementary length along the LL [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Area !< Area of each LL panel [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: diag_LL !< Diagonal length of each LL panel [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Gamma_LL !< Circulation on the wing lifting line (COPY of Constraint State) [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vind_LL !< Induced velocity on lifting line control points [m/s] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vtot_LL !< Total velocity on lifting line control points [m/s] @@ -1019,6 +1020,20 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) END IF DstMiscData%Area = SrcMiscData%Area ENDIF +IF (ALLOCATED(SrcMiscData%diag_LL)) THEN + i1_l = LBOUND(SrcMiscData%diag_LL,1) + i1_u = UBOUND(SrcMiscData%diag_LL,1) + i2_l = LBOUND(SrcMiscData%diag_LL,2) + i2_u = UBOUND(SrcMiscData%diag_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%diag_LL)) THEN + ALLOCATE(DstMiscData%diag_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%diag_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%diag_LL = SrcMiscData%diag_LL +ENDIF IF (ALLOCATED(SrcMiscData%Gamma_LL)) THEN i1_l = LBOUND(SrcMiscData%Gamma_LL,1) i1_u = UBOUND(SrcMiscData%Gamma_LL,1) @@ -1253,6 +1268,9 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%Area)) THEN DEALLOCATE(MiscData%Area) ENDIF +IF (ALLOCATED(MiscData%diag_LL)) THEN + DEALLOCATE(MiscData%diag_LL) +ENDIF IF (ALLOCATED(MiscData%Gamma_LL)) THEN DEALLOCATE(MiscData%Gamma_LL) ENDIF @@ -1389,6 +1407,11 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*2 ! Area upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%Area) ! Area END IF + Int_BufSz = Int_BufSz + 1 ! diag_LL allocated yes/no + IF ( ALLOCATED(InData%diag_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! diag_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%diag_LL) ! diag_LL + END IF Int_BufSz = Int_BufSz + 1 ! Gamma_LL allocated yes/no IF ( ALLOCATED(InData%Gamma_LL) ) THEN Int_BufSz = Int_BufSz + 2*2 ! Gamma_LL upper/lower bounds for each dimension @@ -1714,6 +1737,22 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%Area)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Area))-1 ) = PACK(InData%Area,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%Area) END IF + IF ( .NOT. ALLOCATED(InData%diag_LL) ) 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%diag_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%diag_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%diag_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%diag_LL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%diag_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%diag_LL))-1 ) = PACK(InData%diag_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%diag_LL) + END IF IF ( .NOT. ALLOCATED(InData%Gamma_LL) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -2343,6 +2382,32 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%Area) DEALLOCATE(mask2) END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! diag_LL 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 + IF (ALLOCATED(OutData%diag_LL)) DEALLOCATE(OutData%diag_LL) + ALLOCATE(OutData%diag_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%diag_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%diag_LL)>0) OutData%diag_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%diag_LL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%diag_LL) + DEALLOCATE(mask2) + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Gamma_LL not allocated Int_Xferred = Int_Xferred + 1 ELSE diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index b63e46cbbb..104c5c30f7 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -42,6 +42,7 @@ subroutine Wings_Panelling_Init(Meshes, r, p, m, ErrStat, ErrMsg ) ! --- Computing spanwise coordinate of input mesh normalized from 0 to 1 !FIXME: does this work for a highly curved blade? !also note: this info also exists in InitInp%zLocal or InitInp%rLocal +!TODO: Implement better curvilinear coordinate s_in(:) = -999 First = Meshes(iW)%Position(1:3,1 ) Last = Meshes(iW)%Position(1:3,p%nSpan+1) @@ -122,6 +123,14 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) enddo ! --- Generic code below to compute normal/tangential vectors of a lifting line panel ! Notations follow vanGarrel [TODO REF] + ! + ! + ! P4 -P10---P7------ P3 + ! | + ! P8 P6 + ! | + ! P1 -P9----P5------ P2 + ! do iW = 1,p%nWings do iSpan = 1,p%nSpan P1 = m%LE(:,iSpan , iw) @@ -143,7 +152,9 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) ! m%Tscoord(1:3,iSpan) = (DP3)/norm2(DP3) ! tangential unit vector, along span, follows ref line m%dl (1:3,iSpan,iW) = DP2 m%Orth(1:3,iSpan,iW) = cross_product(m%Norm(1:3,iSpan,iW),m%Tang(1:3,iSpan,iW)) ! orthogonal vector to N and T - m%Area(iSpan, iW) = norm2(cross_product(DP1,DP3)); + m%Area(iSpan, iW) = norm2(cross_product(DP1,DP3)) + DP3 = P1-P3 + m%diag_LL(iSpan, iW) = norm2(DP3) end do enddo !FIXME: does it make sense to use the position mesh for this info? From 0c1ac3bd167a27beb1657b256723c8be802898e2 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 16 Mar 2020 21:41:30 -0600 Subject: [PATCH 078/190] FVW: fixed curvilinear spanwise coordinate --- modules/aerodyn/src/FVW_Subs.f90 | 6 +++--- modules/aerodyn/src/FVW_Wings.f90 | 20 ++++++-------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index ed0f03ec7d..add3f8f66e 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -470,13 +470,13 @@ subroutine FVW_InitRegularization(p, m, ErrStat, ErrMsg) d_max = maxval(m%diag_LL(:,iW)) d_mean = sum (m%diag_LL(:,iW))/(p%nSpan+1) Span = m%s_ll(p%nSpan+1,iW)-m%s_ll(1,iW) - RegParam = ds_mean*10 + RegParam = ds_mean*2 write(*,'(A)')'-----------------------------------------------------------------------------------------' write(*,'(A)')'Regularization Info' write(*,'(A,1F8.4,A)') 'Span : ',Span write(*,'(A,3F8.4,A)') 'Chord : ',c_min,c_mean,c_max,' (min, mean, max)' - write(*,'(A,3F8.4,A)') 'Spanwise distretization: ',ds_min,ds_mean,ds_max,' (min, mean, max)' - write(*,'(A,3F8.4,A)') 'Diagonal distretization: ',d_min,d_mean,d_max,' (min, mean, max)' + write(*,'(A,3F8.4,A)') 'Spanwise discretization: ',ds_min,ds_mean,ds_max,' (min, mean, max)' + write(*,'(A,3F8.4,A)') 'Diagonal discretization: ',d_min,d_mean,d_max,' (min, mean, max)' write(*,'(A,1F8.4)') 'RegParam (Recommended) : ',RegParam write(*,'(A,1F8.4)') 'RegParam (Input ) : ',p%WakeRegParam if (p%RegDeterMethod==idRegDeterAuto) then diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 104c5c30f7..ba9d10f772 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -26,9 +26,7 @@ subroutine Wings_Panelling_Init(Meshes, r, p, m, ErrStat, ErrMsg ) integer(IntKi) :: ErrStat2 ! temporary error status of the operation character(ErrMsgLen) :: ErrMsg2 ! temporary error message integer(IntKi) :: iW, iSpan - real(ReKi), dimension(3) :: First, Last, P1, P2, Pmid, DP - real(ReKi) :: ds, length - real(ReKi) :: c1,c2 + real(ReKi), dimension(3) :: DP real(ReKi), dimension(:),allocatable :: s_in !< Dimensionless spanwise coordinate of input ! Initialize ErrStat @@ -40,18 +38,12 @@ subroutine Wings_Panelling_Init(Meshes, r, p, m, ErrStat, ErrMsg ) if (allocated(s_in)) deallocate(s_in) allocate(s_in(1:Meshes(iW)%nNodes)) ! --- Computing spanwise coordinate of input mesh normalized from 0 to 1 -!FIXME: does this work for a highly curved blade? -!also note: this info also exists in InitInp%zLocal or InitInp%rLocal -!TODO: Implement better curvilinear coordinate +!Note: this info also exists in InitInp%zLocal or InitInp%rLocal s_in(:) = -999 - First = Meshes(iW)%Position(1:3,1 ) - Last = Meshes(iW)%Position(1:3,p%nSpan+1) - DP = Last - First - length = TwoNorm(DP) - do iSpan = 1, Meshes(iW)%nNodes - P1 = Meshes(iW)%Position(1:3, iSpan ) - DP = P1-First - s_in(iSpan) = TwoNorm(DP) / length + s_in(1) = 0 + do iSpan = 2, Meshes(iW)%nNodes + DP = Meshes(iW)%Position(1:3, iSpan) - Meshes(iW)%Position(1:3, iSpan-1) + s_in(iSpan) = s_in(iSpan-1) + norm2(DP) enddo ! --- Setting up Lifting line variables based on input and a "meshing" method (TODO) From 2f7640328f734601d91301923a2422e804c42346 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 16 Mar 2020 22:28:21 -0600 Subject: [PATCH 079/190] FVW: possibility to export in Hub coordinate system with --- modules/aerodyn/src/AeroDyn.f90 | 2 + modules/aerodyn/src/FVW.f90 | 7 ++ modules/aerodyn/src/FVW_IO.f90 | 11 +-- modules/aerodyn/src/FVW_Registry.txt | 18 +++-- modules/aerodyn/src/FVW_Types.f90 | 117 +++++++++++++++++++++------ modules/aerodyn/src/FVW_Wings.f90 | 1 - 6 files changed, 118 insertions(+), 38 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 3146f13b3d..4933465334 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1633,6 +1633,8 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) m%FVW_u(tIndx)%WingsMesh(k)%TranslationDisp = u(tIndx)%BladeMotion(k)%TranslationDisp m%FVW_u(tIndx)%WingsMesh(k)%Orientation = u(tIndx)%BladeMotion(k)%Orientation m%FVW_u(tIndx)%WingsMesh(k)%TranslationVel = u(tIndx)%BladeMotion(k)%TranslationVel + m%FVW_u(tIndx)%HubPosition = u(tIndx)%HubMotion%Position(:,1) + m%FVW_u(tIndx)%HubOrientation = u(tIndx)%HubMotion%Orientation(:,:,1) enddo if (ALLOCATED(m%FVW_u(tIndx)%V_wind)) then m%FVW_u(tIndx)%V_wind = u(tIndx)%InflowWakeVel diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 145c0a7801..ecc079b4c6 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -332,6 +332,7 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%CoreSpreadEddyVisc = InputFileData%CoreSpreadEddyVisc p%WrVTK = InputFileData%WrVTK p%VTKBlades = min(max(InputFileData%VTKBlades,0),p%nWings) + p%VTKCoord = InputFileData%VTKCoord if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) if (InputFileData%CirculationMethod==idCircPrescribed) then @@ -673,6 +674,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ! All of the calculated output channels are placed into the m%AllOuts(:), while the channels selected for outputs are ! placed in the y%WriteOutput(:) array. !.................................................................................................................................. + use VTK, only: set_vtk_coordinate_transform real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(FVW_InputType), intent(in ) :: u !< Inputs at Time t type(FVW_ParameterType), intent(in ) :: p !< Parameters @@ -738,6 +740,11 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, endif if ( ( t - m%VTKlastTime ) >= p%DTvtk*OneMinusEpsilon ) then m%VTKlastTime = t + if (p%VTKCoord==2) then + ! Hub reference coordinates, for export only + ! ALL VTK Will be exported in this coordinate system! + call set_vtk_coordinate_transform(u%HubOrientation,u%HubPosition) + endif call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%VTKstep, 9) endif m%VTKstep = m%VTKstep + 1 ! Increment VTK counter no matter what diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 57850a171e..7474d935ae 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -62,10 +62,11 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar (UnIn,FileName,Inp%WingRegParam ,'WingRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CoreSpreadEddyVisc ,'CoreSpreadEddyVisc','',10.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return !------------------------ OUTPUT OPTIONS ----------------------------------------- - CALL ReadCom(UnIn,FileName, 'Output options header', ErrStat2,ErrMsg2); if(Failed()) return - CALL ReadVar(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,Inp%VTKBlades , 'VTKBlades' ,'',ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar(UnIn,FileName,VTK_fps_line , 'VTK_fps' ,'',ErrStat2,ErrMsg2); if(Failed())return + CALL ReadCom (UnIn,FileName, 'Output options header' ,ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadVarWDefault(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'', 0 ,ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%VTKBlades , 'VTKBlades' ,'', 1 ,ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%VTKCoord , 'VTKCoord' ,'', 1 ,ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVar (UnIn,FileName,VTK_fps_line , 'VTK_fps' ,'' ,ErrStat2,ErrMsg2); if(Failed())return ! --- Validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) @@ -186,7 +187,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) print*,'------------------------------------------------------------------------------' print'(A,L1,A,I0,A,I0,A,I0)','VTK Output - First call ',m%FirstCall, ' nNW:',m%nNW,' nFW:',m%nFW,' i:',VTKCount ! - call set_vtk_binary_format(.false.) + call set_vtk_binary_format(.false.) ! TODO binary fails ! TimeStamp write(Tstr, '(i' // trim(Num2LStr(Twidth)) //'.'// trim(Num2LStr(Twidth)) // ')') VTKcount diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 0dc130bbd0..64fd6febf4 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -11,8 +11,8 @@ usefrom AirfoilInfo_Registry.txt #FVW_ParameterType typedef FVW/FVW ParameterType IntKi nWings - - - "Number of Wings" - typedef ^ ^ IntKi nSpan - - - "TODO, should be defined per wing. Number of spanwise element" - -typedef ^ ^ IntKi AFindx :: - - "Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number]" - -typedef ^ ^ ReKi Chord :: - - "Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number]" - +typedef ^ ^ IntKi AFindx :: - - "Index to the airfoils from AD15 [idx1= BladeNode, idx2=Blade number]" - +typedef ^ ^ ReKi Chord :: - - "Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number]" - typedef ^ ^ IntKi nNWMax - - - "Maximum number of nw panels, per wing" - typedef ^ ^ IntKi nFWMax - - - "Maximum number of fw panels, per wing" - typedef ^ ^ IntKi nFWFree - - - "Number of fw panels that are free, per wing" - @@ -32,13 +32,14 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - typedef ^ ^ ReKi WakeRegParam - - - "Initial value of the regularization parameter" typedef ^ ^ ReKi WingRegParam - - - "Regularization parameter of the wing" -typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - -typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - -typedef ^ ^ IntKi HACK - - - "HACK ID" - typedef ^ ^ DbKi DTaero - - - "Time interval for calls calculations" s typedef ^ ^ DbKi DTfvw - - - "Time interval for calculating wake induced velocities" s typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s +# Parametesr output options +typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - +typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ DbKi DTvtk - - - "DT between vtk writes" s +typedef ^ ^ IntKi VTKCoord - - - "Switch for VTK outputs coordinate system" - # ....... OtherStateType ............ # FVW_OtherStateType @@ -85,6 +86,8 @@ typedef ^ ^ DbKi # FVW_InputType typedef FVW/FVW InputType MeshType WingsMesh : - - "Input Mesh defining position and orientation of wings" typedef ^ ^ ReKi V_wind :: - - "Wind at requested points (r_wind)" - +typedef ^ ^ ReKi HubOrientation {3}{3} - - "Orientation of hub coordinate system (for output only)" - +typedef ^ ^ ReKi HubPosition {3} - - "Origin of hub (for output only)" - # ........ Output ............ # FVW_OutputType @@ -113,8 +116,8 @@ typedef ^ ^ Reki # FVW_InitInputType typedef FVW/FVW InitInputType CHARACTER(1024) FVWFileName - - - "Main FVW input file name" - typedef ^ ^ MeshType WingsMesh : - - "Input Mesh defining position and orientation of wings (nSpan+1) " - -typedef ^ ^ IntKi AFindx :: - - "Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number]" - -typedef ^ ^ ReKi Chord :: - - "Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number]" - +typedef ^ ^ IntKi AFindx :: - - "Index to the airfoils from AD15 [idx1=BladeNode, idx2=Blade number]" - +typedef ^ ^ ReKi Chord :: - - "Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number]" - typedef ^ ^ ReKi RElm : - - "radius of center of each element" - typedef ^ ^ ReKi zHub : - - "Distance to hub for each blade" m typedef ^ ^ ReKi zLocal :: - - "Distance to blade node, measured along the blade" m @@ -152,6 +155,7 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ DbKi DTvtk - - - "Requested timestep between VTK outputs (calculated from the VTK_fps read in)" s +typedef ^ ^ IntKi VTKCoord - - - "Switch for VTK outputs coordinate system" - #.......... InitOutputType ...... # FVW_InitOutputType diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 95bce38443..836f7b9310 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -38,8 +38,8 @@ MODULE FVW_Types TYPE, PUBLIC :: FVW_ParameterType INTEGER(IntKi) :: nWings !< Number of Wings [-] INTEGER(IntKi) :: nSpan !< TODO, should be defined per wing. Number of spanwise element [-] - INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number] [-] - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number] [-] + INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [idx1= BladeNode, idx2=Blade number] [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number] [-] INTEGER(IntKi) :: nNWMax !< Maximum number of nw panels, per wing [-] INTEGER(IntKi) :: nFWMax !< Maximum number of fw panels, per wing [-] INTEGER(IntKi) :: nFWFree !< Number of fw panels that are free, per wing [-] @@ -59,13 +59,13 @@ MODULE FVW_Types INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] REAL(ReKi) :: WakeRegParam !< Initial value of the regularization parameter [-] REAL(ReKi) :: WingRegParam !< Regularization parameter of the wing [-] - INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] - INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] - INTEGER(IntKi) :: HACK !< HACK ID [-] REAL(DbKi) :: DTaero !< Time interval for calls calculations [s] REAL(DbKi) :: DTfvw !< Time interval for calculating wake induced velocities [s] REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] + INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] + INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] REAL(DbKi) :: DTvtk !< DT between vtk writes [s] + INTEGER(IntKi) :: VTKCoord !< Switch for VTK outputs coordinate system [-] END TYPE FVW_ParameterType ! ======================= ! ========= FVW_OtherStateType ======= @@ -113,6 +113,8 @@ MODULE FVW_Types TYPE, PUBLIC :: FVW_InputType TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: WingsMesh !< Input Mesh defining position and orientation of wings [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: V_wind !< Wind at requested points (r_wind) [-] + REAL(ReKi) , DIMENSION(1:3,1:3) :: HubOrientation !< Orientation of hub coordinate system (for output only) [-] + REAL(ReKi) , DIMENSION(1:3) :: HubPosition !< Origin of hub (for output only) [-] END TYPE FVW_InputType ! ======================= ! ========= FVW_OutputType ======= @@ -144,8 +146,8 @@ MODULE FVW_Types TYPE, PUBLIC :: FVW_InitInputType CHARACTER(1024) :: FVWFileName !< Main FVW input file name [-] TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: WingsMesh !< Input Mesh defining position and orientation of wings (nSpan+1) [-] - INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [idx 1: BladeNode, idx2: Blade number] [-] - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [idx 1: BladeNode, idx2: Blade number] [-] + INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [idx1=BladeNode, idx2=Blade number] [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number] [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: RElm !< radius of center of each element [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: zHub !< Distance to hub for each blade [m] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: zLocal !< Distance to blade node, measured along the blade [m] @@ -183,6 +185,7 @@ MODULE FVW_Types INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] REAL(DbKi) :: DTvtk !< Requested timestep between VTK outputs (calculated from the VTK_fps read in) [s] + INTEGER(IntKi) :: VTKCoord !< Switch for VTK outputs coordinate system [-] END TYPE FVW_InputFile ! ======================= ! ========= FVW_InitOutputType ======= @@ -269,13 +272,13 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%WakeRegMethod = SrcParamData%WakeRegMethod DstParamData%WakeRegParam = SrcParamData%WakeRegParam DstParamData%WingRegParam = SrcParamData%WingRegParam - DstParamData%WrVTK = SrcParamData%WrVTK - DstParamData%VTKBlades = SrcParamData%VTKBlades - DstParamData%HACK = SrcParamData%HACK DstParamData%DTaero = SrcParamData%DTaero DstParamData%DTfvw = SrcParamData%DTfvw DstParamData%KinVisc = SrcParamData%KinVisc + DstParamData%WrVTK = SrcParamData%WrVTK + DstParamData%VTKBlades = SrcParamData%VTKBlades DstParamData%DTvtk = SrcParamData%DTvtk + DstParamData%VTKCoord = SrcParamData%VTKCoord END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -368,13 +371,13 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! WakeRegMethod Re_BufSz = Re_BufSz + 1 ! WakeRegParam Re_BufSz = Re_BufSz + 1 ! WingRegParam - Int_BufSz = Int_BufSz + 1 ! WrVTK - Int_BufSz = Int_BufSz + 1 ! VTKBlades - Int_BufSz = Int_BufSz + 1 ! HACK Db_BufSz = Db_BufSz + 1 ! DTaero Db_BufSz = Db_BufSz + 1 ! DTfvw Re_BufSz = Re_BufSz + 1 ! KinVisc + Int_BufSz = Int_BufSz + 1 ! WrVTK + Int_BufSz = Int_BufSz + 1 ! VTKBlades Db_BufSz = Db_BufSz + 1 ! DTvtk + Int_BufSz = Int_BufSz + 1 ! VTKCoord IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -487,20 +490,20 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_Xferred = Re_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WingRegParam Re_Xferred = Re_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK - Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades - Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%HACK - Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTaero Db_Xferred = Db_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTfvw Db_Xferred = Db_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%KinVisc Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades + Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTvtk Db_Xferred = Db_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKCoord + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -654,20 +657,20 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + 1 OutData%WingRegParam = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 - OutData%WrVTK = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 - OutData%VTKBlades = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 - OutData%HACK = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 OutData%DTaero = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 OutData%DTfvw = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 OutData%KinVisc = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%WrVTK = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%VTKBlades = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%DTvtk = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 + OutData%VTKCoord = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -2790,6 +2793,8 @@ SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg END IF DstInputData%V_wind = SrcInputData%V_wind ENDIF + DstInputData%HubOrientation = SrcInputData%HubOrientation + DstInputData%HubPosition = SrcInputData%HubPosition END SUBROUTINE FVW_CopyInput SUBROUTINE FVW_DestroyInput( InputData, ErrStat, ErrMsg ) @@ -2876,6 +2881,8 @@ SUBROUTINE FVW_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 2*2 ! V_wind upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%V_wind) ! V_wind END IF + Re_BufSz = Re_BufSz + SIZE(InData%HubOrientation) ! HubOrientation + Re_BufSz = Re_BufSz + SIZE(InData%HubPosition) ! HubPosition IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -2960,6 +2967,10 @@ SUBROUTINE FVW_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S IF (SIZE(InData%V_wind)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%V_wind))-1 ) = PACK(InData%V_wind,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%V_wind) END IF + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%HubOrientation))-1 ) = PACK(InData%HubOrientation,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%HubOrientation) + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%HubPosition))-1 ) = PACK(InData%HubPosition,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%HubPosition) END SUBROUTINE FVW_PackInput SUBROUTINE FVW_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3078,6 +3089,30 @@ SUBROUTINE FVW_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%V_wind) DEALLOCATE(mask2) END IF + i1_l = LBOUND(OutData%HubOrientation,1) + i1_u = UBOUND(OutData%HubOrientation,1) + i2_l = LBOUND(OutData%HubOrientation,2) + i2_u = UBOUND(OutData%HubOrientation,2) + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + OutData%HubOrientation = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%HubOrientation))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%HubOrientation) + DEALLOCATE(mask2) + i1_l = LBOUND(OutData%HubPosition,1) + i1_u = UBOUND(OutData%HubPosition,1) + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + OutData%HubPosition = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%HubPosition))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%HubPosition) + DEALLOCATE(mask1) END SUBROUTINE FVW_UnPackInput SUBROUTINE FVW_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -4882,6 +4917,7 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%WrVTK = SrcInputFileData%WrVTK DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades DstInputFileData%DTvtk = SrcInputFileData%DTvtk + DstInputFileData%VTKCoord = SrcInputFileData%VTKCoord END SUBROUTINE FVW_CopyInputFile SUBROUTINE FVW_DestroyInputFile( InputFileData, ErrStat, ErrMsg ) @@ -4954,6 +4990,7 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades Db_BufSz = Db_BufSz + 1 ! DTvtk + Int_BufSz = Int_BufSz + 1 ! VTKCoord IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -5031,6 +5068,8 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTvtk Db_Xferred = Db_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKCoord + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackInputFile SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -5115,6 +5154,8 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%DTvtk = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 + OutData%VTKCoord = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackInputFile SUBROUTINE FVW_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -5358,6 +5399,18 @@ SUBROUTINE FVW_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg DEALLOCATE(b2) DEALLOCATE(c2) END IF ! check if allocated + ALLOCATE(b2(SIZE(u_out%HubOrientation,1),SIZE(u_out%HubOrientation,2) )) + ALLOCATE(c2(SIZE(u_out%HubOrientation,1),SIZE(u_out%HubOrientation,2) )) + b2 = -(u1%HubOrientation - u2%HubOrientation)/t(2) + u_out%HubOrientation = u1%HubOrientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%HubPosition,1))) + ALLOCATE(c1(SIZE(u_out%HubPosition,1))) + b1 = -(u1%HubPosition - u2%HubPosition)/t(2) + u_out%HubPosition = u1%HubPosition + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) END SUBROUTINE FVW_Input_ExtrapInterp1 @@ -5430,6 +5483,20 @@ SUBROUTINE FVW_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, Err DEALLOCATE(b2) DEALLOCATE(c2) END IF ! check if allocated + ALLOCATE(b2(SIZE(u_out%HubOrientation,1),SIZE(u_out%HubOrientation,2) )) + ALLOCATE(c2(SIZE(u_out%HubOrientation,1),SIZE(u_out%HubOrientation,2) )) + b2 = (t(3)**2*(u1%HubOrientation - u2%HubOrientation) + t(2)**2*(-u1%HubOrientation + u3%HubOrientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%HubOrientation + t(3)*u2%HubOrientation - t(2)*u3%HubOrientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%HubOrientation = u1%HubOrientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%HubPosition,1))) + ALLOCATE(c1(SIZE(u_out%HubPosition,1))) + b1 = (t(3)**2*(u1%HubPosition - u2%HubPosition) + t(2)**2*(-u1%HubPosition + u3%HubPosition))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%HubPosition + t(3)*u2%HubPosition - t(2)*u3%HubPosition ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%HubPosition = u1%HubPosition + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) END SUBROUTINE FVW_Input_ExtrapInterp2 diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index ba9d10f772..5def176ffd 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -98,7 +98,6 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) ErrMsg = "" ! --- Position of leading edge and trailing edge ! TODO, this assumes one to one between InputMesh and FVW Mesh - ! do iW = 1,p%nWings do iSpan = 1,p%nSpan+1 P_ref = Meshes(iW)%Position(1:3, iSpan )+Meshes(iW)%TranslationDisp(1:3, iSpan) From bf527b3db3a7c1c807598449a3529db0a0826ae1 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 16 Mar 2020 22:36:19 -0600 Subject: [PATCH 080/190] FVW: default values for nFWPanelsFree --- modules/aerodyn/src/FVW.f90 | 2 +- modules/aerodyn/src/FVW_IO.f90 | 4 ++-- modules/aerodyn/src/FVW_VortexTools.f90 | 3 ++- modules/aerodyn/src/FVW_Wings.f90 | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index ecc079b4c6..216528ae2a 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -434,7 +434,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m endif - print'(A,F10.3,A,F10.3,A,L1,A,I0,A,I0,A,I0)','Update states, t:',t,' t_u:', utimes(1),' ComputeWakeInduced: ',m%ComputeWakeInduced,' Step:',n,' nNW:',m%nNW,' nFW:',m%nFW + print'(A,F10.3,A,F10.3,A,I0,A,I0,A,I0,A,L1)','Update states, t:',t,' t_u:', utimes(1),' Step:',n,' nNW:',m%nNW,' nFW:',m%nFW,' ComputeWake: ',m%ComputeWakeInduced ! --- Evaluation at t diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 7474d935ae..1716d3ea78 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -53,7 +53,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadCom (UnIn,FileName, 'Wake options header' , ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVar (UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVar (UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'' , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'', Inp%nFWPanels , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%DiffusionMethod ,'DiffusionMethod' ,'',idDiffusionNone , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%RegDeterMethod ,'RegDeterMethod' ,'',idRegDeterManual, ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',idRegVatistas , ErrStat2,ErrMsg2); if(Failed())return @@ -276,7 +276,7 @@ subroutine WrVTK_Segments(filename, SegPoints, SegConnct, SegGamma, SegEpsilon) call vtk_cell_data_init() call vtk_cell_data_scalar(SegGamma ,'Gamma') call vtk_cell_data_scalar(SegEpsilon,'Epsilon') - call vtk_cell_data_scalar(real(SegConnct(3,:), ReKi),'Age') +! call vtk_cell_data_scalar(real(SegConnct(3,:), ReKi),'Age') !call vtk_cell_data_scalar(real(SegConnct(4,:), ReKi),'Span') !call vtk_point_data_init() !call vtk_point_data_vector(Sgmt%UconvP(1:3,1:Sgmt%nP_Storage),'Uconv') diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index 16ef00cacc..11b56be93b 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -169,7 +169,8 @@ subroutine print_mean_3d(M, Label) U(1:3)= U(1:3)+ M(1:3, j, i) enddo; enddo; U(1:3)=U(1:3)/ (size(M,3)*size(M,2)) - print'(A26,3F12.4)',trim(Label)//' ',U + !print'(A26,3F12.4)',trim(Label)//' ',U + print'(A24,3F12.4)',trim(Label),U end subroutine diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 5def176ffd..faafc69073 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -362,7 +362,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x end do ! convergence loop if (iIter==p%CircSolvMaxIter) then - print*,'Maximum number of iterations reached: ',iIter + print'(A,I0,A,I0,A)','Circulation solve, call ',iLabel,', done after ........................ nIter: ', iIter, ' <<< Max reached' Gamma_LL=GammaLastIter ! returning relaxed value if not converged else print'(A,I0,A,I0)','Circulation solve, call ',iLabel,', done after ........................ nIter: ', iIter @@ -377,8 +377,8 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x !call print_mean_3d( m%Vstr_LL(:,:,:), 'Mean struct vel. LL (cst)') !call print_mean_3d( m%Vind_LL(:,:,:), 'Mean induced vel. LL (cst)') !call print_mean_3d( Vvar(:,:,:) , 'Mean induced vel. LL (var)') - call print_mean_3d( Vvar+m%Vind_LL(:,:,:), 'Mean induced vel. LL (tot)') - call print_mean_3d( m%Vtot_LL(:,:,:), 'Mean relativevel. LL (tot)') + !call print_mean_3d( Vvar+m%Vind_LL(:,:,:), 'Mean induced vel. LL (tot)') + !call print_mean_3d( m%Vtot_LL(:,:,:), 'Mean relativevel. LL (tot)') !print*,'m%Vind_LL',m%Vind_LL(1,:,:) !print*,'m%Vwnd_LL',m%Vwnd_LL(1,:,:) !print*,'m%Vcst_LL',Vcst(1,:,:) From f08b6ca1bff915f860e45374ba6fada790b9c8d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 17 Mar 2020 12:30:49 -0600 Subject: [PATCH 081/190] FVW: adding option for FW shed vorticity --- modules/aerodyn/src/FVW.f90 | 1 + modules/aerodyn/src/FVW_IO.f90 | 4 +- modules/aerodyn/src/FVW_Registry.txt | 2 + modules/aerodyn/src/FVW_Subs.f90 | 11 +++-- modules/aerodyn/src/FVW_Tests.f90 | 8 ++-- modules/aerodyn/src/FVW_Types.f90 | 14 +++++++ modules/aerodyn/src/FVW_VortexTools.f90 | 31 ++++++++------ modules/nwtc-library/src/NWTC_IO.f90 | 54 ++++++++++++++++++++++++- 8 files changed, 101 insertions(+), 24 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 216528ae2a..fde83b3628 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -323,6 +323,7 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%FreeWakeStart = InputFileData%FreeWakeStart p%CircSolvPolar = InputFileData%CircSolvPolar p%FullCirculationStart = InputFileData%FullCirculationStart + p%FWShedVorticity = InputFileData%FWShedVorticity p%DiffusionMethod = InputFileData%DiffusionMethod p%RegFunction = InputFileData%RegFunction p%RegDeterMethod = InputFileData%RegDeterMethod diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 1716d3ea78..fb1be038bb 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -54,6 +54,8 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar (UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'', Inp%nFWPanels , ErrStat2,ErrMsg2); if(Failed())return + + CALL ReadVarWDefault(UnIn,FileName,Inp%FWShedVorticity ,'FWShedVorticity' ,'', .False. , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%DiffusionMethod ,'DiffusionMethod' ,'',idDiffusionNone , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%RegDeterMethod ,'RegDeterMethod' ,'',idRegDeterManual, ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',idRegVatistas , ErrStat2,ErrMsg2); if(Failed())return @@ -249,7 +251,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) iHeadP=1 iHeadC=1 do iW=1,p%nWings - CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .True. ) enddo endif diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 64fd6febf4..cb54ad0fef 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -16,6 +16,7 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi nNWMax - - - "Maximum number of nw panels, per wing" - typedef ^ ^ IntKi nFWMax - - - "Maximum number of fw panels, per wing" - typedef ^ ^ IntKi nFWFree - - - "Number of fw panels that are free, per wing" - +typedef ^ ^ Logical FWShedVorticity - - - "Include shed vorticity in the far wake" - typedef ^ ^ IntKi IntMethod - - - "Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1)" - typedef ^ ^ ReKi FreeWakeStart - - - "Time when wake starts convecting (rolling up)" s typedef ^ ^ ReKi FullCirculationStart - - - "Time when the circulation is full" s @@ -145,6 +146,7 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi nNWPanels - - - "Number of nw panels" - typedef ^ ^ IntKi nFWPanels - - - "Number of fw panels" - typedef ^ ^ IntKi nFWPanelsFree - - - "Number of fw panels that are free" - +typedef ^ ^ Logical FWShedVorticity - - - "Include shed vorticity in the far wake" - typedef ^ ^ IntKi DiffusionMethod - - - "Diffusion method (None, CoreSpreading, PSE)" - typedef ^ ^ ReKi CoreSpreadEddyVisc - - - "Eddy viscosity used in the core spreading method" typedef ^ ^ IntKi RegDeterMethod - - - "Regularization determinatino method (manual, automatic)" - diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index add3f8f66e..86dd22d82b 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -388,7 +388,6 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG ! Local integer(IntKi) :: iHeadC, iHeadP, nC, nP, iW, iHeadC_bkp real(ReKi), dimension(:,:), allocatable :: Buffer2d - !real(ReKi), dimension(:), allocatable :: SegSmooth !< ! Counting total number of segments nP=0 @@ -399,7 +398,11 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG endif if (m%nFW>0) then nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) - nC = nC + p%nWings * (2*(FWnSpan+1)*(m%nFW+1)-(FWnSpan+1)-(m%nFW+1)) + if (p%FWShedVorticity) then + nC = nC + p%nWings * (2*(FWnSpan+1)*(m%nFW+1)-(FWnSpan+1)-(m%nFW+1)) + else + nC = nC + p%nWings * ( (FWnSpan+1)*(m%nFW) ) ! No Shed vorticity + endif endif if (nP>0) then @@ -415,12 +418,12 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG iHeadP=1 iHeadC=1 do iW=1,p%nWings - CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .True. ) enddo if (m%nFW>0) then iHeadC_bkp = iHeadC do iW=1,p%nWings - CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , p%FWShedVorticity) enddo SegConnct(3,iHeadC_bkp:) = SegConnct(3,iHeadC_bkp:) + m%nNW endif diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index dbd279923b..7d28081c24 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -370,7 +370,7 @@ subroutine Test_LatticeToSegment(iStat) iHeadP=1 iHeadC=1 - CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. ) CALL printall() CALL WrVTK_Segments('Points1_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) @@ -397,7 +397,7 @@ subroutine Test_LatticeToSegment(iStat) allocate(SegGamma (1:nC2) ); SegGamma=-9999 iHeadP=1 iHeadC=1 - CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , .true.) CALL printall() CALL WrVTK_Segments('Points2_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) @@ -412,8 +412,8 @@ subroutine Test_LatticeToSegment(iStat) allocate(SegConnct(1:2,1:nC)); SegConnct=-1 allocate(SegPoints(1:3,1:nP)); SegPoints=-1 allocate(SegGamma (1:nC) ); SegGamma=-9999 - CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) - CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. ) + CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. ) CALL printall() CALL WrVTK_Segments('PointsBoth_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 836f7b9310..f41d63431d 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -43,6 +43,7 @@ MODULE FVW_Types INTEGER(IntKi) :: nNWMax !< Maximum number of nw panels, per wing [-] INTEGER(IntKi) :: nFWMax !< Maximum number of fw panels, per wing [-] INTEGER(IntKi) :: nFWFree !< Number of fw panels that are free, per wing [-] + LOGICAL :: FWShedVorticity !< Include shed vorticity in the far wake [-] INTEGER(IntKi) :: IntMethod !< Integration Method (1=RK4, 2=AB4, 3=ABM4, 5=Euler1) [-] REAL(ReKi) :: FreeWakeStart !< Time when wake starts convecting (rolling up) [s] REAL(ReKi) :: FullCirculationStart !< Time when the circulation is full [s] @@ -175,6 +176,7 @@ MODULE FVW_Types INTEGER(IntKi) :: nNWPanels !< Number of nw panels [-] INTEGER(IntKi) :: nFWPanels !< Number of fw panels [-] INTEGER(IntKi) :: nFWPanelsFree !< Number of fw panels that are free [-] + LOGICAL :: FWShedVorticity !< Include shed vorticity in the far wake [-] INTEGER(IntKi) :: DiffusionMethod !< Diffusion method (None, CoreSpreading, PSE) [-] REAL(ReKi) :: CoreSpreadEddyVisc !< Eddy viscosity used in the core spreading method [-] INTEGER(IntKi) :: RegDeterMethod !< Regularization determinatino method (manual, automatic) [-] @@ -245,6 +247,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%nNWMax = SrcParamData%nNWMax DstParamData%nFWMax = SrcParamData%nFWMax DstParamData%nFWFree = SrcParamData%nFWFree + DstParamData%FWShedVorticity = SrcParamData%FWShedVorticity DstParamData%IntMethod = SrcParamData%IntMethod DstParamData%FreeWakeStart = SrcParamData%FreeWakeStart DstParamData%FullCirculationStart = SrcParamData%FullCirculationStart @@ -351,6 +354,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! nNWMax Int_BufSz = Int_BufSz + 1 ! nFWMax Int_BufSz = Int_BufSz + 1 ! nFWFree + Int_BufSz = Int_BufSz + 1 ! FWShedVorticity Int_BufSz = Int_BufSz + 1 ! IntMethod Re_BufSz = Re_BufSz + 1 ! FreeWakeStart Re_BufSz = Re_BufSz + 1 ! FullCirculationStart @@ -447,6 +451,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWFree Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%FWShedVorticity , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%IntMethod Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%FreeWakeStart @@ -604,6 +610,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%nFWFree = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%FWShedVorticity = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 OutData%IntMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%FreeWakeStart = ReKiBuf( Re_Xferred ) @@ -4907,6 +4915,7 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%nNWPanels = SrcInputFileData%nNWPanels DstInputFileData%nFWPanels = SrcInputFileData%nFWPanels DstInputFileData%nFWPanelsFree = SrcInputFileData%nFWPanelsFree + DstInputFileData%FWShedVorticity = SrcInputFileData%FWShedVorticity DstInputFileData%DiffusionMethod = SrcInputFileData%DiffusionMethod DstInputFileData%CoreSpreadEddyVisc = SrcInputFileData%CoreSpreadEddyVisc DstInputFileData%RegDeterMethod = SrcInputFileData%RegDeterMethod @@ -4980,6 +4989,7 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! nNWPanels Int_BufSz = Int_BufSz + 1 ! nFWPanels Int_BufSz = Int_BufSz + 1 ! nFWPanelsFree + Int_BufSz = Int_BufSz + 1 ! FWShedVorticity Int_BufSz = Int_BufSz + 1 ! DiffusionMethod Re_BufSz = Re_BufSz + 1 ! CoreSpreadEddyVisc Int_BufSz = Int_BufSz + 1 ! RegDeterMethod @@ -5048,6 +5058,8 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFWPanelsFree Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%FWShedVorticity , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%DiffusionMethod Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%CoreSpreadEddyVisc @@ -5134,6 +5146,8 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%nFWPanelsFree = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%FWShedVorticity = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 OutData%DiffusionMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%CoreSpreadEddyVisc = ReKiBuf( Re_Xferred ) diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index 11b56be93b..5f56b4651d 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -49,7 +49,7 @@ subroutine LatticeToPoints(LatticePoints, iDepthStart, Points, iHeadP) endsubroutine LatticeToPoints - subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC ) + subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, bShedVorticity ) real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Points 3 x nSpan x nDepth real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< GammaPanl nSpan x nDepth integer(IntKi), intent(in ) :: iDepthStart !< Start index for depth dimension @@ -58,6 +58,7 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints real(ReKi), dimension(:), intent(inout) :: SegGamma !< integer(IntKi), intent(inout) :: iHeadP !< Index indicating where to start in SegPoints integer(IntKi), intent(inout) :: iHeadC !< Index indicating where to start in SegConnct + logical , intent(in ) :: bShedVorticity !< Shed vorticity is included if true ! Local integer(IntKi) :: nSpan, nDepth integer(IntKi) :: iSpan, iDepth @@ -107,12 +108,14 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints endif !print*,iseg1,iseg2,iseg3,iseg4 ! Segment 1-2 - SegConnct(1,iHeadC) = iseg1 - SegConnct(2,iHeadC) = iseg2 - SegConnct(3,iHeadC) = iDepth - SegConnct(4,iHeadC) = iSpan - SegGamma (iHeadC ) = Gamma12 - iHeadC=iHeadC+1 + if (bShedVorticity) then + SegConnct(1,iHeadC) = iseg1 + SegConnct(2,iHeadC) = iseg2 + SegConnct(3,iHeadC) = iDepth + SegConnct(4,iHeadC) = iSpan + SegGamma (iHeadC ) = Gamma12 + iHeadC=iHeadC+1 + endif ! Segment 1-4 SegConnct(1,iHeadC) = iseg1 SegConnct(2,iHeadC) = iseg4 @@ -122,12 +125,14 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints iHeadC=iHeadC+1 ! Segment 4-3 if (iDepth==nDepth-1) then - SegConnct(1,iHeadC) = iseg4 - SegConnct(2,iHeadC) = iseg3 - SegConnct(3,iHeadC) = iDepth - SegConnct(4,iHeadC) = iSpan - SegGamma (iHeadC ) = - LatticeGamma(iSpan,iDepth) - iHeadC=iHeadC+1 + if (bShedVorticity) then + SegConnct(1,iHeadC) = iseg4 + SegConnct(2,iHeadC) = iseg3 + SegConnct(3,iHeadC) = iDepth + SegConnct(4,iHeadC) = iSpan + SegGamma (iHeadC ) = - LatticeGamma(iSpan,iDepth) + iHeadC=iHeadC+1 + endif endif ! Segment 2-3 if (iSpan==nSpan-1) then diff --git a/modules/nwtc-library/src/NWTC_IO.f90 b/modules/nwtc-library/src/NWTC_IO.f90 index 6d6da2978d..e2b943f3aa 100644 --- a/modules/nwtc-library/src/NWTC_IO.f90 +++ b/modules/nwtc-library/src/NWTC_IO.f90 @@ -136,7 +136,7 @@ MODULE NWTC_IO END INTERFACE !> \copydoc nwtc_io::parsechvarwdefault - INTERFACE ParseVarWDefault ! Parses a character variable name and value from a string, potentially sets to a default value if "Default" is parsed. + INTERFACE ParseVarWDefault ! Parses a boolean variable name and value from a string, potentially sets to a default value if "Default" is parsed. MODULE PROCEDURE ParseChVarWDefault ! Parses a character string from a string, potentially sets to a default value if "Default" is parsed. MODULE PROCEDURE ParseDbVarWDefault ! Parses a double-precision REAL from a string, potentially sets to a default value if "Default" is parsed. MODULE PROCEDURE ParseInVarWDefault ! Parses an INTEGER from a string, potentially sets to a default value if "Default" is parsed. @@ -166,7 +166,7 @@ MODULE NWTC_IO INTERFACE ReadVarWDefault !MODULE PROCEDURE ReadCVar MODULE PROCEDURE ReadIVarWDefault - !MODULE PROCEDURE ReadLVar + MODULE PROCEDURE ReadLVarWDefault ! Logical MODULE PROCEDURE ReadR4VarWDefault ! 4-byte real MODULE PROCEDURE ReadR8VarWDefault ! 8-byte real MODULE PROCEDURE ReadR16VarWDefault ! 16-byte real @@ -5463,6 +5463,56 @@ SUBROUTINE ReadIVarWDefault ( UnIn, Fil, Var, VarName, VarDescr, VarDefault, Err RETURN END SUBROUTINE ReadIVarWDefault !======================================================================= +!> This routine reads a logical variable from the next line of the input file. +!! Use ReadVarWDefault (nwtc_io::readvarwdefault) instead of directly calling a specific routine in the generic interface. +!! WARNING: this routine limits the size of the number being read to 30 characters + SUBROUTINE ReadLVarWDefault ( UnIn, Fil, Var, VarName, VarDescr, VarDefault, ErrStat, ErrMsg, UnEc ) + + ! Argument declarations: + + LOGICAL, INTENT(OUT) :: Var !< variable being read + LOGICAL, INTENT(IN) :: VarDefault !< default value of variable being read + INTEGER, INTENT(IN) :: UnIn !< I/O unit for input file. + INTEGER, INTENT(IN), OPTIONAL:: UnEc !< I/O unit for echo file. If present and > 0, write to UnEc + INTEGER(IntKi), INTENT(OUT) :: ErrStat !< Error status; if present, program does not abort on error + CHARACTER(*), INTENT(OUT) :: ErrMsg !< Error message + + CHARACTER(*), INTENT(IN) :: Fil !< Name of the input file. + CHARACTER(*), INTENT(IN) :: VarDescr !< Text string describing the variable. + CHARACTER(*), INTENT(IN) :: VarName !< Text string containing the variable name. + + + ! Local declarations: + + INTEGER :: IOS ! I/O status returned from the read statement. + + CHARACTER(30) :: Word ! String to hold the first word on the line. + + + CALL ReadNum ( UnIn, Fil, Word, VarName, ErrStat, ErrMsg ) + IF ( ErrStat >= AbortErrLev ) RETURN ! If we're about to read a T/F and treat it as a number, we have a less severe ErrStat + + CALL Conv2UC( Word ) + IF ( INDEX(Word, "DEFAULT" ) /= 1 ) THEN ! If it's not "default", read this variable; otherwise use the DEFAULT value + READ (Word,*,IOSTAT=IOS) Var + + CALL CheckIOS ( IOS, Fil, VarName, NumType, ErrStat, ErrMsg ) + + IF (ErrStat >= AbortErrLev) RETURN + ELSE + Var = VarDefault + END IF + + IF ( PRESENT(UnEc) ) THEN + IF ( UnEc > 0 ) & + WRITE (UnEc,Ec_IntFrmt) Var, VarName, VarDescr + END IF + + + RETURN + END SUBROUTINE ReadLVarWDefault +!======================================================================= + !> \copydoc nwtc_io::readcary SUBROUTINE ReadLAry ( UnIn, Fil, Ary, AryLen, AryName, AryDescr, ErrStat, ErrMsg, UnEc ) From 9cbb64538b61888206a79a5a535362caa8acda68 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 17 Mar 2020 14:40:50 -0600 Subject: [PATCH 082/190] FVW: fixed bug when nFW=0 --- modules/aerodyn/src/FVW_Subs.f90 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 86dd22d82b..1d9cf327bd 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -340,9 +340,11 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m) iP_end=iP_start-1+(p%nSpan+1)*(p%nNWMax+1)*p%nWings r_wind(1:3,iP_start:iP_end) = reshape( x%r_NW(1:3,1:p%nSpan+1,1:p%nNWMax+1,1:p%nWings), (/ 3, (p%nSpan+1)*(p%nNWMax+1)*p%nWings /)) ! --- FW points - iP_start=iP_end+1 - iP_end=iP_start-1+(FWnSpan+1)*(p%nFWMax+1)*p%nWings - r_wind(1:3,iP_start:iP_end) = reshape( x%r_FW(1:3,1:FWnSpan+1,1:p%nFWMax+1,1:p%nWings), (/ 3, (FWnSpan+1)*(p%nFWMax+1)*p%nWings /)) + if (p%nFWMax>0) then + iP_start=iP_end+1 + iP_end=iP_start-1+(FWnSpan+1)*(p%nFWMax+1)*p%nWings + r_wind(1:3,iP_start:iP_end) = reshape( x%r_FW(1:3,1:FWnSpan+1,1:p%nFWMax+1,1:p%nWings), (/ 3, (FWnSpan+1)*(p%nFWMax+1)*p%nWings /)) + endif end subroutine SetRequestedWindPoints @@ -365,9 +367,11 @@ subroutine DistributeRequestedWind(V_wind, x, p, m) iP_end=iP_start-1+(p%nSpan+1)*(p%nNWMax+1)*p%nWings m%Vwnd_NW(1:3,1:p%nSpan+1,1:p%nNWMax+1,1:p%nWings) = reshape( V_wind(1:3,iP_start:iP_end), (/ 3, p%nSpan+1, p%nNWMax+1, p%nWings/)) ! --- FW points - iP_start=iP_end+1 - iP_end=iP_start-1+(FWnSpan+1)*(p%nFWMax+1)*p%nWings - m%Vwnd_FW(1:3,1:p%nSpan+1,1:FWnSpan+1,1:p%nWings) = reshape( V_wind(1:3,iP_start:iP_end), (/ 3, FWnSpan+1, p%nFWMax+1, p%nWings /)) + if (p%nFWMax>0) then + iP_start=iP_end+1 + iP_end=iP_start-1+(FWnSpan+1)*(p%nFWMax+1)*p%nWings + m%Vwnd_FW(1:3,1:p%nSpan+1,1:FWnSpan+1,1:p%nWings) = reshape( V_wind(1:3,iP_start:iP_end), (/ 3, FWnSpan+1, p%nFWMax+1, p%nWings /)) + endif end subroutine DistributeRequestedWind From 220a7f22c78e14b87a0add7cb6c1cb7112eaed36 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 17 Mar 2020 15:04:14 -0600 Subject: [PATCH 083/190] FVW: fixed hub coordinate system export --- modules/aerodyn/src/AeroDyn.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 4933465334..df05463e34 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1633,7 +1633,7 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) m%FVW_u(tIndx)%WingsMesh(k)%TranslationDisp = u(tIndx)%BladeMotion(k)%TranslationDisp m%FVW_u(tIndx)%WingsMesh(k)%Orientation = u(tIndx)%BladeMotion(k)%Orientation m%FVW_u(tIndx)%WingsMesh(k)%TranslationVel = u(tIndx)%BladeMotion(k)%TranslationVel - m%FVW_u(tIndx)%HubPosition = u(tIndx)%HubMotion%Position(:,1) + m%FVW_u(tIndx)%HubPosition = u(tIndx)%HubMotion%Position(:,1) + u(tIndx)%HubMotion%TranslationDisp(:,1) m%FVW_u(tIndx)%HubOrientation = u(tIndx)%HubMotion%Orientation(:,:,1) enddo if (ALLOCATED(m%FVW_u(tIndx)%V_wind)) then From aaac942c7720abd25c9a250adc937b2ec2d4f3da Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 10:24:45 -0600 Subject: [PATCH 084/190] FVW: including length into denominator offset regularizatino --- modules/aerodyn/src/FVW_BiotSavart.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/aerodyn/src/FVW_BiotSavart.f90 b/modules/aerodyn/src/FVW_BiotSavart.f90 index 38e5419b82..4b0c3afdec 100644 --- a/modules/aerodyn/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn/src/FVW_BiotSavart.f90 @@ -81,7 +81,7 @@ subroutine ui_seg_11(DeltaPa, DeltaPb, SegGamma, RegFunction, RegParam1, Uind) Kv = r_bar2/sqrt(1.0_ReKi+r_bar2**2) case ( idRegOffset ) ! Cut-off radius Kv = 1.0_ReKi - denominator=denominator+RegParam1**2 + denominator=denominator+RegParam1**2*norm2_r0 case default Kv=1.0_ReKi !< Should be an error end select @@ -301,7 +301,7 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & if (norm2_r0>PRECISION_UI) then ! --- Far field TODO ! --- Regularization (close field) -- Offset - denominator = denominator+RegParam(is)**2 + denominator = denominator+RegParam(is)**2*norm2_r0 Kv = SegGamma(is)*fourpi_inv*(norm_a+norm_b)/(denominator + MINDENOM) Uind(1:3) = Kv*crossprod(1:3) end if From 82015dd8efbb79d4d377bb30300c46e9232285df Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 11:13:22 -0600 Subject: [PATCH 085/190] FVW: fixed bug in far wake wind --- modules/aerodyn/src/FVW.f90 | 2 +- modules/aerodyn/src/FVW_Subs.f90 | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index fde83b3628..4b57046916 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -183,7 +183,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) ! Variables at panels points call AllocAry( m%r_LL , 3 , p%nSpan+1 , 2 , p%nWings, 'Lifting Line Panels', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%r_LL= -999999_ReKi; call AllocAry( m%Vwnd_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_NW= -999_ReKi; - call AllocAry( m%Vwnd_FW , 3 , p%nSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; + call AllocAry( m%Vwnd_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_NW= -999_ReKi; call AllocAry( m%Vind_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; ! Wind request points diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 1d9cf327bd..2bef583c20 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -330,7 +330,9 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m) type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables integer(IntKi) :: iP_start,iP_end ! Current index of point, start and end of range - ! Using array reshaping to ensure a given near or far wake point is always at the same location in the array. + ! Using array reshaping to ensure a given near or far wake point is always at the same location in the array. + ! NOTE: Maximum number of points are passed, whether they "exist" or not. + ! NOTE: InflowWind ignores points at (0,0,0) ! --- LL CP iP_start=1 iP_end=p%nWings*p%nSpan @@ -357,7 +359,8 @@ subroutine DistributeRequestedWind(V_wind, x, p, m) type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi) :: iP_start,iP_end ! Current index of point, start and end of range - ! Using array reshaping to ensure a given near or far wake point is always at the same location in the array. + ! Using array reshaping to ensure a given near or far wake point is always at the same location in the array. + ! NOTE: Maximum number of points are passed, whether they "exist" or not. ! --- LL CP iP_start=1 iP_end=p%nWings*p%nSpan @@ -370,7 +373,7 @@ subroutine DistributeRequestedWind(V_wind, x, p, m) if (p%nFWMax>0) then iP_start=iP_end+1 iP_end=iP_start-1+(FWnSpan+1)*(p%nFWMax+1)*p%nWings - m%Vwnd_FW(1:3,1:p%nSpan+1,1:FWnSpan+1,1:p%nWings) = reshape( V_wind(1:3,iP_start:iP_end), (/ 3, FWnSpan+1, p%nFWMax+1, p%nWings /)) + m%Vwnd_FW(1:3,1:FWnSpan+1,1:p%nFWMax+1,1:p%nWings) = reshape( V_wind(1:3,iP_start:iP_end), (/ 3, FWnSpan+1, p%nFWMax+1, p%nWings /)) endif end subroutine DistributeRequestedWind From bedce970e0f7b2aaea5d7eed17a250d2893d384e Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 11:53:05 -0600 Subject: [PATCH 086/190] FVW: scaling of gamma reverse before iteration --- modules/aerodyn/src/FVW_Wings.f90 | 43 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index faafc69073..4c758ae743 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -216,40 +216,44 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFIn ErrStat = ErrID_None ErrMsg = "" -!FIXME: Gamma_LL is currently stored as a constraint state. This routine is called from places where constraint states are considered intent(in) only. + if (t>>Prescribing circulation' do iW = 1, p%nWings !Loop over lifting lines Gamma_LL(1:p%nSpan,iW) = p%PrescribedCirculation(1:p%nSpan) enddo else if (p%CirculationMethod==idCircPolarData) then ! --- Solve for circulation using polar data - !print*,'>>>>>>>>>>>>>>>>> Circulation solving with polar data >>>>>>>>>>>>>> CALL ',iLabel - CALL Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, ErrStat, ErrMsg, iLabel) + CALL Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, GammaScale, ErrStat, ErrMsg, iLabel) else if (p%CirculationMethod==idCircNoFlowThrough) then ! --- Solve for circulation using the no-flow through condition - ! TODO - print*,'Circulation method nor implemented', p%CirculationMethod - STOP + ErrMsg='Circulation method nor implemented'; ErrStat=ErrID_Fatal; return ! should never happen else - print*,'Circulation method nor implemented', p%CirculationMethod ! Will never happen - STOP + ErrMsg='Circulation method nor implemented'; ErrStat=ErrID_Fatal; return ! should never happen endif - if (t - subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, ErrStat, ErrMsg, iLabel) + subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, GammaScale, ErrStat, ErrMsg, iLabel) real(DbKi), intent(in ) :: t !< Current simulation time in seconds real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines real(ReKi), dimension(:,:), intent(in ) :: Gamma_LL_prev !< Previous/Guessed circulation @@ -257,6 +261,7 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + real(ReKi), intent(in ) :: GammaScale !< Scaling factor used at init type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None @@ -291,12 +296,12 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x m%Vtot_ll = m%Vwnd_LL - m%Vstr_ll call CirculationFromPolarData(GammaLastIter, p, m, AFInfo,ErrStat2,ErrMsg2); if(Failed()) return; else - GammaLastIter(1:p%nSpan,1:p%nWings) = Gamma_LL_prev(1:p%nSpan,1:p%nWings) + ! NOTE: we need to inverse the scaling to speed up the convergence + GammaLastIter(1:p%nSpan,1:p%nWings) = Gamma_LL_prev(1:p%nSpan,1:p%nWings) / GammaScale endif if (any(x%r_NW(1,:,1:m%nNW+1,:)<-999)) then - print*,'Wings_ComputeCirculationPolarData: Problem in input NW points' - STOP + ErrMsg='Wings_ComputeCirculationPolarData: Problem in input NW points'; ErrStat=ErrID_Fatal; return endif From 613780155e6c8d03d79acfd10b12466f89d66a99 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 12:02:04 -0600 Subject: [PATCH 087/190] FVW: cleanup or stdout and error handling, introduced DEV_VERSION param, kept few temporary STOPS --- modules/aerodyn/src/FVW.f90 | 115 ++++++++++++++++-------- modules/aerodyn/src/FVW_IO.f90 | 6 +- modules/aerodyn/src/FVW_Subs.f90 | 47 +++++----- modules/aerodyn/src/FVW_VortexTools.f90 | 2 - modules/aerodyn/src/FVW_Wings.f90 | 81 ++++------------- 5 files changed, 125 insertions(+), 126 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 4b57046916..03f77c246f 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -127,8 +127,9 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL SetRequestedWindPoints(m%r_wind, x, p, m ) ! Return anything in FVW_InitOutput that should be passed back to the calling code here - ! TODO remove me for developement (and when OpenFAST has a framework for tests) - CALL FVW_RunTests(ErrStat2, ErrMsg2); if (Failed()) return + if (DEV_VERSION) then + CALL FVW_RunTests(ErrStat2, ErrMsg2); if (Failed()) return + endif CONTAINS @@ -339,8 +340,9 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg if (InputFileData%CirculationMethod==idCircPrescribed) then call AllocAry( p%PrescribedCirculation, p%nSpan, 'Prescribed Circulation', ErrStat2, ErrMsg2 ); call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_SetParameters' ); p%PrescribedCirculation = -999999_ReKi; if (.not. allocated(m%s_CP_LL)) then - print*,'Spanwise coordinate not allocated, TODO' - STOP + ErrMsg = 'Spanwise coordinate not allocated.' + ErrStat = ErrID_Fatal + return endif call ReadAndInterpGamma(trim(InputFileData%CirculationFile), m%s_CP_LL(1:p%nSpan,1), m%s_LL(p%nSpan+1,1), p%PrescribedCirculation) endif @@ -350,7 +352,9 @@ end subroutine FVW_SetParametersFromInputFile subroutine FVW_ToString(p,m) type(FVW_ParameterType), intent(in) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Misc - print*,'-----------------------------------------------------------------------------------------' + if (DEV_VERSION) then + print*,'-----------------------------------------------------------------------------------------' + endif end subroutine FVW_ToString !---------------------------------------------------------------------------------------------------------------------------------- @@ -425,8 +429,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ErrStat = ErrID_None ErrMsg = "" - - ! Compute Inuced wake effects only if time since last compute is > DTfvw + ! Compute Induced wake effects only if time since last compute is > DTfvw if ( ( t - m%OldWakeTime ) >= p%DTfvw*OneMinusEpsilon ) then m%OldWakeTime = t m%ComputeWakeInduced = .TRUE. ! It's time to update the induced velocities from wake @@ -435,7 +438,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m endif - print'(A,F10.3,A,F10.3,A,I0,A,I0,A,I0,A,L1)','Update states, t:',t,' t_u:', utimes(1),' Step:',n,' nNW:',m%nNW,' nFW:',m%nFW,' ComputeWake: ',m%ComputeWakeInduced + if (DEV_VERSION) print'(A,F10.3,A,F10.3,A,I0,A,I0,A,I0,A,L1)','Update states, t:',t,' t_u:', utimes(1),' Step:',n,' nNW:',m%nNW,' nFW:',m%nFW,' ComputeWake: ',m%ComputeWakeInduced ! --- Evaluation at t @@ -455,8 +458,8 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! Map circulation and positions between LL and NW and then NW and FW ! Changes: x only - call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2) - call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) + call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return !call print_x_NW_FW(p, m, z, x,'Map_') ! --- Integration between t and t+DTaero @@ -482,7 +485,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call PrepareNextTimeStep() ! --- t+DTaero ! Propagation/creation of new layer of panels - call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2) + call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return !call print_x_NW_FW(p, m, z, x,'Prop_') endif @@ -494,8 +497,9 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! Updating positions of first NW and FW panels (Circulation also updated but irrelevant) ! Changes: x only - call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2) - call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) + call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + !call print_x_NW_FW(p, m, z, x,'Map2') ! --- Solve for circulation at t+p%DTaero @@ -505,8 +509,8 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! Updating circulation of near wake panel (and position but irrelevant) ! Changes: x only - call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2) - call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2) + call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return !call print_x_NW_FW(p, m, z, x,'Map3') ! set the wind points required for t+p%DTaero timestep @@ -516,7 +520,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m m%FirstCall=.False. endif - call FVW_DestroyConstrState(z_guess, ErrStat2, ErrMsg2); + call FVW_DestroyConstrState(z_guess, ErrStat2, ErrMsg2); if(Failed()) return contains subroutine PrepareNextTimeStep() @@ -551,15 +555,18 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: ErrStat2 ! temporary error status of the operation - character(ErrMsgLen) :: ErrMsg2 ! temporary error message - integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current time step + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current time step + integer(IntKi) :: i,j,k + real(ReKi), dimension(3) :: VmeanFW ! Mean velocity of the far wake ErrStat = ErrID_None ErrMsg = "" - call AllocAry( dxdt%r_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat, ErrMsg ); dxdt%r_NW= -999999_ReKi; - call AllocAry( dxdt%r_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat, ErrMsg ); dxdt%r_FW= -999999_ReKi; + call AllocAry( dxdt%r_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat2, ErrMsg2); dxdt%r_NW= -999999_ReKi; + call AllocAry( dxdt%r_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2); dxdt%r_FW= -999999_ReKi; + if(Failed()) return ! Only calculate freewake after start time and if on a timestep when it should be calculated. if ((t>= p%FreeWakeStart) .and. m%ComputeWakeInduced) then @@ -569,23 +576,38 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! (expensive N^2 call) ! In : x%r_NW, r%r_FW ! Out: m%Vind_NW, m%Vind_FW - call WakeInducedVelocities(p, x, m, ErrStat2, ErrMsg2) - m%Vind_FW(1:3, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) =0 ! Ensuring no convection velocity for panels that user doesnt want free - - call print_mean_4d( m%Vind_NW(:,:, 1:m%nNW+1,:), 'Mean induced vel. NW') - if (nFWEff>0) then - call print_mean_4d( m%Vind_FW(:,:, 1:nFWEff ,:), 'Mean induced vel. FW') + call WakeInducedVelocities(p, x, m, ErrStat2, ErrMsg2); if(Failed()) return + + ! --- Induced velocity of the non-free far wake is taken as the mean velocity in the free far-wake + VmeanFW(1:3)=0 + do i=1,size(m%Vind_FW,4); do j=1,nFWEff; do k=1,size(m%Vind_FW,2); + VmeanFW(1:3) = VmeanFW(1:3) + m%Vind_FW(1:3, k, j, i) + enddo; enddo; enddo; + VmeanFW(1:3) = VmeanFW(1:3) / (size(m%Vind_FW,4)*nFWEff*size(m%Vind_FW,2)) + m%Vind_FW(1, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) = VmeanFW(1) ! + m%Vind_FW(2, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) = VmeanFW(2) ! + m%Vind_FW(3, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) = VmeanFW(3) ! + + if (DEV_VERSION) then + call print_mean_4d( m%Vind_NW(:,:, 1:m%nNW+1,:), 'Mean induced vel. NW') + if (nFWEff>0) then + call print_mean_4d( m%Vind_FW(:,:, 1:nFWEff ,:), 'Mean induced vel. FW') + endif + print'(A25,3F12.4)','MeanFW (non free)',VmeanFW + call print_mean_4d( m%Vwnd_NW(:,:, 1:m%nNW+1,:), 'Mean wind vel. NW') + call print_mean_4d( m%Vwnd_FW(:,:, 1:nFWEff+1,:), 'Mean wind vel. FWEff') + call print_mean_4d( m%Vwnd_FW(:,:, p%nFWFree:m%nFW+1,:), 'Mean wind vel. FWNF') + call print_mean_4d( m%Vwnd_FW(:,:, 1:m%nFW,:), 'Mean wind vel. FW') endif - call print_mean_4d( m%Vwnd_NW(:,:, 1:m%nNW+1,:), 'Mean wind vel. NW') - call print_mean_4d( m%Vwnd_FW(:,:, 1:m%nFW+1,:), 'Mean wind vel. FW') ! --- Vortex points are convected with the free stream and induced velocity dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + m%Vind_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) dxdt%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) = m%Vwnd_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + m%Vind_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) else - - call print_mean_4d( m%Vwnd_NW(:,:,1:m%nNW+1,:), 'Mean wind vel. NW') - !call print_mean_4d( m%Vwnd_FW(:,:,1:m%nFW+1,:), 'Mean wind vel. FW') + if(DEV_VERSION) then + call print_mean_4d( m%Vwnd_NW(:,:,1:m%nNW+1,:), 'Mean wind vel. NW') + !call print_mean_4d( m%Vwnd_FW(:,:,1:m%nFW+1,:), 'Mean wind vel. FW') + endif ! --- Vortex points are convected with the free stream dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = m%Vwnd_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) @@ -595,6 +617,11 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt dxdt%r_NW(1:3, :, 1:iNWStart-1, :)=0 ! First FW point does not convect (bound to NW) !dxdt%r_FW(1:3, :, 1, :)=0 +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_CalcContStateDeriv') + Failed = ErrStat >= AbortErrLev + end function Failed end subroutine FVW_CalcContStateDeriv !---------------------------------------------------------------------------------------------------------------------------------- @@ -610,16 +637,18 @@ subroutine FVW_Euler1( t, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! local variables - type(FVW_ContinuousStateType) :: dxdt ! time derivatives of continuous states + type(FVW_ContinuousStateType) :: dxdt ! time derivatives of continuous states integer(IntKi) :: iAge real(ReKi) :: dt + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" dt = real(p%DTaero,ReKi) ! Compute "right hand side" - CALL FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat, ErrMsg ) + CALL FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat2, ErrMsg2); if (Failed()) return ! Update of positions x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dt * dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) @@ -629,8 +658,12 @@ subroutine FVW_Euler1( t, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) ! Update of Gamma ! TODO, viscous diffusion, stretching - call FVW_DestroyContState(dxdt, ErrStat, ErrMsg) - + call FVW_DestroyContState(dxdt, ErrStat2, ErrMsg2); if(Failed()) return +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_Euler1') + Failed = ErrStat >= AbortErrLev + end function Failed end subroutine FVW_Euler1 !---------------------------------------------------------------------------------------------------------------------------------- @@ -699,7 +732,9 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ErrStat = ErrID_None ErrMsg = "" - print'(A,F10.3,A,L1,A,I0,A,I0)','CalcOutput t:',t,' ',m%FirstCall,' nNW:',m%nNW,' nFW:',m%nFW + if (DEV_VERSION) then + print'(A,F10.3,A,L1,A,I0,A,I0)','CalcOutput t:',t,' ',m%FirstCall,' nNW:',m%nNW,' nFW:',m%nFW + endif ! Set the wind velocity at vortex CALL DistributeRequestedWind(u%V_wind, x, p, m) @@ -731,8 +766,10 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ! For plotting only m%Vtot_ll = m%Vind_LL + m%Vwnd_LL - m%Vstr_ll - call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') - call print_mean_3d(m%Vtot_LL,'Mean relativevel. LL') + if (DEV_VERSION) then + call print_mean_3d(m%Vind_LL,'Mean induced vel. LL') + call print_mean_3d(m%Vtot_LL,'Mean relativevel. LL') + endif ! --- Write to local VTK at fps requested if (p%WrVTK==1) then diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index fb1be038bb..0498aa1784 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -186,8 +186,10 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 - print*,'------------------------------------------------------------------------------' - print'(A,L1,A,I0,A,I0,A,I0)','VTK Output - First call ',m%FirstCall, ' nNW:',m%nNW,' nFW:',m%nFW,' i:',VTKCount + if (DEV_VERSION) then + print*,'------------------------------------------------------------------------------' + print'(A,L1,A,I0,A,I0,A,I0)','VTK Output - First call ',m%FirstCall, ' nNW:',m%nNW,' nFW:',m%nFW,' i:',VTKCount + endif ! call set_vtk_binary_format(.false.) ! TODO binary fails diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 2bef583c20..5787383f97 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -40,6 +40,7 @@ module FVW_SUBS ! Implementation integer(IntKi), parameter :: iNWStart=2 !< Index in r%NW where the near wake start (if >1 then the Wing panels are included in r_NW) integer(IntKi), parameter :: FWnSpan=1 !< Number of spanwise far wake panels ! TODO make it an input later + logical , parameter :: DEV_VERSION=.FALSE. contains !========================================================================== @@ -126,7 +127,6 @@ subroutine ReadAndInterpGamma(CirculationFileName, s_CP_LL, L, Gamma_CP_LL) close(iUnit) if (istat/=0) then print*,'Error occured while reading Circulation file' - STOP endif ! NOTE: TODO TODO TODO THIS ROUTINE PERFORMS NASTY EXTRAPOLATION, SHOULD BE PLATEAUED Gamma_CP_LL = interpolation_array( sPrescr, GammaPrescr, s_CP_LL, size(s_CP_LL), nLines ) @@ -146,8 +146,7 @@ integer function line_count(iunit) line_count=line_count+1 enddo if (line_count==nline_max) then - print*,'Error: MainIO: maximum number of line exceeded' - STOP + print*,'Error: maximum number of line exceeded' endif 100 if(len(trim(line))>0) then line_count=line_count+1 @@ -223,8 +222,9 @@ subroutine Map_NW_FW(p, m, z, x, ErrStat, ErrMsg) ! in between point x%r_FW(1:3,2,iAgeFW,iW) = x%r_NW(1:3,int(p%nSpan+1)/4 ,p%nNWMax+1,iW) ! Point (mid) else if ((FWnSpan>2)) then - print*,'Error: FWnSpan>2 not implemented.' - STOP + ErrMsg='Error: FWnSpan>2 not implemented.' + ErrStat=ErrID_Fatal + return endif enddo if (m%nNW==p%nNWMax) then @@ -436,16 +436,15 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG endif if ((iHeadP-1)/=nP) then print*,'PackPanelsToSegments: Number of points wrongly estimated',nP, iHeadP-1 - STOP + STOP ! Keep me. The check will be removed once the code is well established endif if ((iHeadC-1)/=nC) then print*,'PackPanelsToSegments: Number of segments wrongly estimated',nC, iHeadC-1 - STOP + STOP ! Keep me. The check will be removed once the code is well established endif nSeg = iHeadC-1 nSegP = iHeadP-1 else - print*,'PackPanelsToSegments: nP=',nP nSeg = 0 nSegP = 0 endif @@ -569,6 +568,9 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step + ErrStat= ErrID_None + ErrMsg ='' + nFWEff = min(m%nFW, p%nFWFree) m%Vind_NW = -9999._ReKi !< Safety @@ -584,7 +586,9 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) ! --- Computing induced velocity call PackConvectingPoints() - print'(A,I0,A,I0,A,I0)','Convection - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs + if (DEV_VERSION) then + print'(A,I0,A,I0,A,I0)','Convection - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs + endif call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, p%RegFunction, SegEpsilon, Uind) call UnPackInducedVelocity() @@ -620,13 +624,12 @@ subroutine PackConvectingPoints() endif if (any(CPs(1,:)<=-99)) then - print*,'WakeInducedVelocities: Problem in Control points' - STOP + ErrMsg='PackConvectingPoints: Problem in Control points'; ErrStat=ErrID_Fatal; return endif - if ((iHeadP-1)/=size(CPs,2)) then print*,'PackConvectingPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 - STOP + STOP ! Keep me. The check will be removed once the code is well established + ErrMsg='PackConvectingPoints: Number of points wrongly estimated '; ErrStat=ErrID_Fatal; return endif end subroutine !> Distribute the induced velocity to the proper location @@ -641,13 +644,13 @@ subroutine UnPackInducedVelocity() CALL VecToLattice(Uind, 1, m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,iW), iHeadP) enddo if (any(m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,:)<-99)) then - print*,'UnPackInducedVelocity: Problem in FW induced velocity on FW points' - STOP + ErrMsg='UnPackInducedVelocity: Problem in FW induced velocity on FW points'; ErrStat=ErrID_Fatal; return endif endif if ((iHeadP-1)/=size(Uind,2)) then print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 - STOP + STOP ! Keep me. The check will be removed once the code is well established + ErrMsg='UnPackInducedVelocity: Number of points wrongly estimated'; ErrStat=ErrID_Fatal; return endif end subroutine @@ -683,7 +686,9 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) if (nSegP==0) then nCPs=0 m%Vind_LL = 0.0_ReKi - print'(A,I0,A,I0,A,I0,A)','Induction - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs, ' -> No induction' + if (DEV_VERSION) then + print'(A,I0,A,I0,A,I0,A)','Induction - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs, ' -> No induction' + endif else ! --- Setting up regularization allocate(SegEpsilon(1:nSeg)); @@ -695,7 +700,9 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) Uind=0.0_ReKi !< important due to side effects of ui_seg ! --- call PackLiftingLinePoints() - print'(A,I0,A,I0,A,I0)','Induction - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs + if (DEV_VERSION) then + print'(A,I0,A,I0,A,I0)','Induction - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs + endif call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, p%RegFunction, SegEpsilon, Uind) call UnPackLiftingLineVelocities() @@ -715,7 +722,7 @@ subroutine PackLiftingLinePoints() enddo if ((iHeadP-1)/=size(CPs,2)) then print*,'PackLLPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 - STOP + STOP ! Keep me. The check will be removed once the code is well established endif nCPs=iHeadP-1 !print*,'Number of points packed for LL:',nCPs, nSegP @@ -729,7 +736,7 @@ subroutine UnPackLiftingLineVelocities() enddo if ((iHeadP-1)/=size(Uind,2)) then print*,'UnPackLiftingLineVelocities: Number of points wrongly estimated',size(Uind,2), iHeadP-1 - STOP + STOP ! Keep me. The check will be removed once the code is well established endif end subroutine end subroutine diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index 5f56b4651d..c96dee2b6f 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -160,7 +160,6 @@ subroutine print_mean_4d(M, Label) enddo; enddo; enddo; U(1:3)=U(1:3)/ (size(M,4)*size(M,3)*size(M,2)) print'(A25,3F12.4)',trim(Label),U - if(U(1)<-99) STOP end subroutine subroutine print_mean_3d(M, Label) @@ -174,7 +173,6 @@ subroutine print_mean_3d(M, Label) U(1:3)= U(1:3)+ M(1:3, j, i) enddo; enddo; U(1:3)=U(1:3)/ (size(M,3)*size(M,2)) - !print'(A26,3F12.4)',trim(Label)//' ',U print'(A24,3F12.4)',trim(Label),U end subroutine diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 4c758ae743..5e16a1eaa6 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -38,7 +38,7 @@ subroutine Wings_Panelling_Init(Meshes, r, p, m, ErrStat, ErrMsg ) if (allocated(s_in)) deallocate(s_in) allocate(s_in(1:Meshes(iW)%nNodes)) ! --- Computing spanwise coordinate of input mesh normalized from 0 to 1 -!Note: this info also exists in InitInp%zLocal or InitInp%rLocal +!Note: this info also exists in InitInp%zLocal s_in(:) = -999 s_in(1) = 0 do iSpan = 2, Meshes(iW)%nNodes @@ -50,8 +50,7 @@ subroutine Wings_Panelling_Init(Meshes, r, p, m, ErrStat, ErrMsg ) if (Meshes(iW)%nNodes /= p%nSpan+1) then ! TODO Possibly interpolate based on FVW meshing ! NOTE: p%chord is copied from the InitInput - print*,'TODO different discretization InputMesh / vortex code' - STOP + ErrMsg ='TODO different discretization InputMesh / vortex code'; ErrStat=ErrID_Fatal; return endif print*,'Input mesh size',Meshes(iW)%nNodes,' Number of vortex element', p%nSpan do iSpan = 1, p%nSpan+1 @@ -92,29 +91,24 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) real(ReKi), dimension(3) :: DP_TE ! Distance between reference point and trailing edge real(ReKi), dimension(3) :: P1,P2,P3,P4,P5,P7,P8,P6,P9,P10 real(ReKi), dimension(3) :: DP1, DP2, DP3 - !real(ReKi), dimension(3,3) :: MRot ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" - ! --- Position of leading edge and trailing edge - ! TODO, this assumes one to one between InputMesh and FVW Mesh + ! --- Position of leading edge (LE) and trailing edge (TE) + ! NOTE, this assumes one to one between InputMesh and FVW Mesh do iW = 1,p%nWings do iSpan = 1,p%nSpan+1 P_ref = Meshes(iW)%Position(1:3, iSpan )+Meshes(iW)%TranslationDisp(1:3, iSpan) DP_LE(1:3) = 0.0 - DP_LE(1) = -m%chord_LL(iSpan,iW)/4. ! TODO TODO TODO Use orientation and might not be c/2 + DP_LE(1) = -m%chord_LL(iSpan,iW)/4. DP_TE(1:3) = 0.0 - DP_TE(1) = +3.*m%chord_LL(iSpan,iW)/4. ! TODO TODO TODO Use orientation and might not be c/2 - !MRot=Meshes(iW)%Orientation(1:3,1:3,iSpan) ! NOTE: this wont work - !DP_LE = matmul(MRot,DP_LE) - !DP_TE = matmul(MRot,DP_TE) + DP_TE(1) = +3.*m%chord_LL(iSpan,iW)/4. m%LE(1:3, iSpan, iW) = P_ref + DP_LE(1)*Meshes(iW)%Orientation(2,1:3,iSpan) m%TE(1:3, iSpan, iW) = P_ref + DP_TE(1)*Meshes(iW)%Orientation(2,1:3,iSpan) enddo enddo ! --- Generic code below to compute normal/tangential vectors of a lifting line panel - ! Notations follow vanGarrel [TODO REF] - ! + ! Notations follow vanGarrel [ECN-C--03-079, Development of a wind turbine aerodynamics simulation module,2003] ! ! P4 -P10---P7------ P3 ! | @@ -175,27 +169,8 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) call InterpArray(m%s_LL(:,iW), Meshes(iW)%TranslationVel(2,:) ,m%s_CP_LL(:,iW), m%Vstr_LL(2,:,iW)) call InterpArray(m%s_LL(:,iW), Meshes(iW)%TranslationVel(3,:) ,m%s_CP_LL(:,iW), m%Vstr_LL(3,:,iW)) enddo - end subroutine Wings_Panelling -! print*,' Norm Tang ' -! print*, m%Norm(1:3,5,1) -! print*, m%Tang(1:3,5,1) -! print*,' ' -! print*,'LE1',m%LE(1,:,1) -! print*,'LE2',m%LE(2,:,1) -! print*,'LE3',m%LE(3,:,1) -! print*,'' -! print*,'TE1',m%LE(1,:,1) -! print*,'TE2',m%LE(2,:,1) -! print*,'TE3',m%LE(3,:,1) -! print*,'' -! print*,'CP1',m%CP_LL(1,:,1) -! print*,'CP2',m%CP_LL(2,:,1) -! print*,'CP3',m%CP_LL(3,:,1) -! - - !---------------------------------------------------------------------------------------------------------------------------------- !> subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, ErrStat, ErrMsg, iLabel) @@ -212,7 +187,8 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFIn integer(IntKi), intent(in) :: iLabel ! Local integer(IntKi) :: iW - ! Initialize ErrStat + real(ReKi) :: s + real(ReKi) :: GammaScale ErrStat = ErrID_None ErrMsg = "" @@ -316,12 +292,10 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x Vcst = m%Vind_LL + m%Vwnd_LL - m%Vstr_ll if (any(m%Vind_LL(1:3,:,:)<-99)) then - print*,'Wings_ComputeCirculationPolarData: Problem in induced velocity on LL points' - STOP + ErrMsg='Wings_ComputeCirculationPolarData: Problem in induced velocity on LL points'; ErrStat=ErrID_Fatal; return endif if (any(m%Vwnd_LL(1:3,:,:)<-99)) then - print*,'Wings_ComputeCirculationPolarData: Problem in wind velocity on LL points' - STOP + ErrMsg='Wings_ComputeCirculationPolarData: Problem in wind velocity on LL points'; ErrStat=ErrID_Fatal; return endif ! --- Convergence loop until near wake gives induction coherent with circulation @@ -329,7 +303,6 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x iIter=0 do while (.not.(bConverged) .and. iIter0.01) STOP - !if (m%iStep==3) STOP call CleanUp() contains @@ -456,22 +425,10 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) Cl = AFI_interp%Cl Cd = AFI_interp%Cd Cm = AFI_interp%Cm - !else if (p%CircSolvPolar==idPolar2PiAlpha) then - ! Cl=TwoPi*alpha - !else if (p%CircSolvPolar==idPolar2PiSinAlpha) then - ! Cl=TwoPi*sin(alpha) - !else - ! print*,'Unknown CircSolvPolar value' - ! STOP - !endif ! Simple method: ! Gamma_LL=(0.5 * Cl * Vrel_orth_norm*chord) ! VanGarrel's method: Gamma_LL(icp,iW) =(0.5_ReKi * Cl * Vrel_orth_norm**2*m%Area(icp,iW)/(Vjouk_orth_norm)) - !if ((iW==1).and.icp==3) then - ! print*,'CL',Cl,alpha,Vrel_orth_norm,m%Area(icp,iW) - !endif - enddo enddo contains @@ -485,6 +442,4 @@ logical function Failed() end function Failed end subroutine CirculationFromPolarData - - end module FVW_Wings From a9648c48e761b50607abb2107f0735dd4fcef599 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 12:21:18 -0600 Subject: [PATCH 088/190] FVW: added comment for future root/tip vortex location --- modules/aerodyn/src/FVW_Subs.f90 | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 5787383f97..3bd25c79aa 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -207,16 +207,34 @@ subroutine Map_NW_FW(p, m, z, x, ErrStat, ErrMsg) type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - integer(IntKi) :: iSpan , iW - real(ReKi) :: FWGamma + integer(IntKi) :: iSpan , iW, iRoot + real(ReKi), dimension(p%nWings) :: FWGamma integer(IntKi), parameter :: iAgeFW=1 !< we update the first FW panel ErrStat = ErrID_None ErrMsg = "" ! First Panel of Farwake has coordinates of last panel of near wake always if (p%nFWMax>0) then + FWGamma(:)=0.0_ReKi + if (m%nNW==p%nNWMax) then + ! First circulation of Farwake is taken as the max circulation of last NW column + do iW=1,p%nWings + !FWGamma = sum(x%Gamma_NW(:,p%nNWMax,iW))/p%nSpan + FWGamma(iW) = maxval(x%Gamma_NW(:,p%nNWMax,iW)) + x%Gamma_FW(1:FWnSpan,iAgeFW,iW) = FWGamma(iW) + enddo + endif + do iW=1,p%nWings - x%r_FW(1:3,1 ,iAgeFW,iW) = x%r_NW(1:3,1 ,p%nNWMax+1,iW) ! Point 1 (root) + ! Find first point (in half span) where circulation is more than 0.1% of MaxGamma, call it the root + iRoot=1 + ! NOTE: this below won't work for a wing + ! Need to go from maxgamma location, and integrate spanwise position on both side to find location of tip and root vortex + !do while ((iRoot Date: Wed, 18 Mar 2020 13:14:12 -0600 Subject: [PATCH 089/190] FVW: adding some status to screen --- modules/aerodyn/src/FVW.f90 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 03f77c246f..ed4befe2a2 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -424,7 +424,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m integer(IntKi) :: ErrStat2 ! temporary Error status character(ErrMsgLen) :: ErrMsg2 ! temporary Error message type(FVW_ConstraintStateType) :: z_guess ! < - integer(IntKi) :: iW, iSpan, iAge + integer(IntKi) :: iW, iSpan, nP, nFWEff ErrStat = ErrID_None ErrMsg = "" @@ -438,6 +438,10 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m endif + nP = p%nWings * ( (p%nSpan+1)*(m%nNW-1+2) +(FWnSpan+1)*(m%nFW+1) ) + nFWEff = min(m%nFW, p%nFWFree) + ! --- Display some status to screen + if (mod(n,10)==0) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0)','FVW status - t:',t,' Step:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP if (DEV_VERSION) print'(A,F10.3,A,F10.3,A,I0,A,I0,A,I0,A,L1)','Update states, t:',t,' t_u:', utimes(1),' Step:',n,' nNW:',m%nNW,' nFW:',m%nFW,' ComputeWake: ',m%ComputeWakeInduced @@ -638,7 +642,6 @@ subroutine FVW_Euler1( t, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! local variables type(FVW_ContinuousStateType) :: dxdt ! time derivatives of continuous states - integer(IntKi) :: iAge real(ReKi) :: dt integer(IntKi) :: ErrStat2 ! temporary error status of the operation character(ErrMsgLen) :: ErrMsg2 ! temporary error message @@ -646,7 +649,7 @@ subroutine FVW_Euler1( t, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" - dt = real(p%DTaero,ReKi) + dt = real(p%DTaero,ReKi) ! NOTE: this is DTaero not DTfvw since we integrate at each sub time step ! Compute "right hand side" CALL FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat2, ErrMsg2); if (Failed()) return @@ -785,8 +788,8 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, endif call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%VTKstep, 9) endif - m%VTKstep = m%VTKstep + 1 ! Increment VTK counter no matter what endif + m%VTKstep = m%VTKstep + 1 ! Increment VTK counter no matter what contains From e7939002a5c1597d7ac2329e34cdf34954281a3b Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 13:46:08 -0600 Subject: [PATCH 090/190] FVW: adding simple step timing --- modules/aerodyn/src/FVW.f90 | 12 ++++++++++-- modules/aerodyn/src/FVW_Registry.txt | 1 + modules/aerodyn/src/FVW_Subs.f90 | 18 ++++++++++-------- modules/aerodyn/src/FVW_Types.f90 | 7 +++++++ modules/aerodyn/src/FVW_Wings.f90 | 1 - 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index ed4befe2a2..3d451e8d46 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -160,6 +160,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%nFW = 0 ! Number of active farwake panels m%VTKstep = 0 ! Current step number for vtk output m%VTKlastTime = -HUGE(1.0_DbKi) + m%tSpent = 0 call AllocAry( m%LE , 3 , p%nSpan+1 , p%nWings, 'Leading Edge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%LE = -999999_ReKi; call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%TE = -999999_ReKi; @@ -425,6 +426,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m character(ErrMsgLen) :: ErrMsg2 ! temporary Error message type(FVW_ConstraintStateType) :: z_guess ! < integer(IntKi) :: iW, iSpan, nP, nFWEff + integer, dimension(8) :: time1, time2, time_diff ErrStat = ErrID_None ErrMsg = "" @@ -433,6 +435,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m if ( ( t - m%OldWakeTime ) >= p%DTfvw*OneMinusEpsilon ) then m%OldWakeTime = t m%ComputeWakeInduced = .TRUE. ! It's time to update the induced velocities from wake + call date_and_time(values=time1) else m%ComputeWakeInduced = .FALSE. endif @@ -441,7 +444,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m nP = p%nWings * ( (p%nSpan+1)*(m%nNW-1+2) +(FWnSpan+1)*(m%nFW+1) ) nFWEff = min(m%nFW, p%nFWFree) ! --- Display some status to screen - if (mod(n,10)==0) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0)','FVW status - t:',t,' Step:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP + if (mod(n,10)==0) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F6.1,A)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's' if (DEV_VERSION) print'(A,F10.3,A,F10.3,A,I0,A,I0,A,I0,A,L1)','Update states, t:',t,' t_u:', utimes(1),' Step:',n,' nNW:',m%nNW,' nFW:',m%nFW,' ComputeWake: ',m%ComputeWakeInduced @@ -523,7 +526,12 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m if (m%FirstCall) then m%FirstCall=.False. endif - + if (m%ComputeWakeInduced) then + ! Profiling of expensive time step + call date_and_time(values=time2) + time_diff=time2-time1 + m%tSpent = time_diff(5)*3600+time_diff(6)*60 +time_diff(7)+0.001*time_diff(8) + endif call FVW_DestroyConstrState(z_guess, ErrStat2, ErrMsg2); if(Failed()) return contains diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index cb54ad0fef..e287ee5a5a 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -82,6 +82,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi PitchAndTwist :: - - "Twist angle (includes all sources of twist) [Array of size (NumBlNds,numBlades)]" rad typedef ^ ^ Logical ComputeWakeInduced - - - "Compute induced velocities on this timestep" - typedef ^ ^ DbKi OldWakeTime - - - "Time the wake induction velocities were last calculated" s +typedef ^ ^ ReKi tSpent - - - "Time spent in expensive Biot-Savart computation" s # ........ Input ............ # FVW_InputType diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 3bd25c79aa..8998d70533 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -490,14 +490,16 @@ subroutine FVW_InitRegularization(p, m, ErrStat, ErrMsg) d_mean = sum (m%diag_LL(:,iW))/(p%nSpan+1) Span = m%s_ll(p%nSpan+1,iW)-m%s_ll(1,iW) RegParam = ds_mean*2 - write(*,'(A)')'-----------------------------------------------------------------------------------------' - write(*,'(A)')'Regularization Info' - write(*,'(A,1F8.4,A)') 'Span : ',Span - write(*,'(A,3F8.4,A)') 'Chord : ',c_min,c_mean,c_max,' (min, mean, max)' - write(*,'(A,3F8.4,A)') 'Spanwise discretization: ',ds_min,ds_mean,ds_max,' (min, mean, max)' - write(*,'(A,3F8.4,A)') 'Diagonal discretization: ',d_min,d_mean,d_max,' (min, mean, max)' - write(*,'(A,1F8.4)') 'RegParam (Recommended) : ',RegParam - write(*,'(A,1F8.4)') 'RegParam (Input ) : ',p%WakeRegParam + if (DEV_VERSION) then + write(*,'(A)')'-----------------------------------------------------------------------------------------' + write(*,'(A)')'Regularization Info' + write(*,'(A,1F8.4,A)') 'Span : ',Span + write(*,'(A,3F8.4,A)') 'Chord : ',c_min,c_mean,c_max,' (min, mean, max)' + write(*,'(A,3F8.4,A)') 'Spanwise discretization: ',ds_min,ds_mean,ds_max,' (min, mean, max)' + write(*,'(A,3F8.4,A)') 'Diagonal discretization: ',d_min,d_mean,d_max,' (min, mean, max)' + write(*,'(A,1F8.4)') 'RegParam (Recommended) : ',RegParam + write(*,'(A,1F8.4)') 'RegParam (Input ) : ',p%WakeRegParam + endif if (p%RegDeterMethod==idRegDeterAuto) then ! TODO this is beta print*,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index f41d63431d..b0b377a5fa 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -108,6 +108,7 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: PitchAndTwist !< Twist angle (includes all sources of twist) [Array of size (NumBlNds,numBlades)] [rad] LOGICAL :: ComputeWakeInduced !< Compute induced velocities on this timestep [-] REAL(DbKi) :: OldWakeTime !< Time the wake induction velocities were last calculated [s] + REAL(ReKi) :: tSpent !< Time spent in expensive Biot-Savart computation [s] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -1229,6 +1230,7 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) ENDIF DstMiscData%ComputeWakeInduced = SrcMiscData%ComputeWakeInduced DstMiscData%OldWakeTime = SrcMiscData%OldWakeTime + DstMiscData%tSpent = SrcMiscData%tSpent END SUBROUTINE FVW_CopyMisc SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -1484,6 +1486,7 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si END IF Int_BufSz = Int_BufSz + 1 ! ComputeWakeInduced Db_BufSz = Db_BufSz + 1 ! OldWakeTime + Re_BufSz = Re_BufSz + 1 ! tSpent IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -1988,6 +1991,8 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%OldWakeTime Db_Xferred = Db_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%tSpent + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2753,6 +2758,8 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%OldWakeTime = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 + OutData%tSpent = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 5e16a1eaa6..4763c11722 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -52,7 +52,6 @@ subroutine Wings_Panelling_Init(Meshes, r, p, m, ErrStat, ErrMsg ) ! NOTE: p%chord is copied from the InitInput ErrMsg ='TODO different discretization InputMesh / vortex code'; ErrStat=ErrID_Fatal; return endif - print*,'Input mesh size',Meshes(iW)%nNodes,' Number of vortex element', p%nSpan do iSpan = 1, p%nSpan+1 m%s_LL (iSpan, iW) = s_in(iSpan) m%chord_LL(iSpan, iW) = p%chord(iSpan,iW) From 8fd2e8fc7ff326ccead2c00dd8f1d8efb82a1d96 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 18:05:40 -0600 Subject: [PATCH 091/190] FVW: output to vtk_fvw, now using rootname --- modules/aerodyn/src/AeroDyn.f90 | 1 + modules/aerodyn/src/FVW.f90 | 20 +++++++------------- modules/aerodyn/src/FVW_Registry.txt | 2 ++ modules/aerodyn/src/FVW_Types.f90 | 22 ++++++++++++++++++++++ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index df05463e34..c0a0cdd52b 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -2232,6 +2232,7 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m InitInp%numBladeNodes = p%numBlNds InitInp%DTaero = p%DT ! NOTE: FVW can run a lower timestep internally InitInp%KinVisc = p%KinVisc + InitInp%RootName = p%RootName(1:len_trim(p%RootName)-2) ! Removing "AD" ! NOTE: The following are not meshes ! It's just the spanwise location. diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 3d451e8d46..275609b378 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -283,17 +283,11 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" ! - p%nWings = InitInp%NumBlades - - ! NOTE: temporary limitation, all wings have the same nspan - p%nSpan = InitInp%numBladeNodes-1 - - ! Set time step - p%DTaero = InitInp%DTaero - - ! Kinematic air viscosity - p%KinVisc = InitInp%KinVisc - + p%nWings = InitInp%NumBlades + p%nSpan = InitInp%numBladeNodes-1 ! NOTE: temporary limitation, all wings have the same nspan + p%DTaero = InitInp%DTaero ! AeroDyn Time step + p%KinVisc = InitInp%KinVisc ! Kinematic air viscosity + p%RootName = InitInp%RootName ! Rootname for outputs ! Set indexing to AFI tables -- this is set from the AD15 calling code. call AllocAry(p%AFindx,size(InitInp%AFindx,1),size(InitInp%AFindx,2),'AFindx',ErrStat,ErrMsg) p%AFindx = InitInp%AFindx ! Copying in case AD15 still needs these @@ -785,7 +779,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ! --- Write to local VTK at fps requested if (p%WrVTK==1) then if (m%FirstCall) then - call MKDIR('vtk_out') + call MKDIR('vtk_fvw') endif if ( ( t - m%VTKlastTime ) >= p%DTvtk*OneMinusEpsilon ) then m%VTKlastTime = t @@ -794,7 +788,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ! ALL VTK Will be exported in this coordinate system! call set_vtk_coordinate_transform(u%HubOrientation,u%HubPosition) endif - call WrVTK_FVW(p, x, z, m, 'vtk_out/FVW', m%VTKstep, 9) + call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW', m%VTKstep, 9) endif endif m%VTKstep = m%VTKstep + 1 ! Increment VTK counter no matter what diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index e287ee5a5a..cd150a81c0 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -41,6 +41,7 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ DbKi DTvtk - - - "DT between vtk writes" s typedef ^ ^ IntKi VTKCoord - - - "Switch for VTK outputs coordinate system" - +typedef ^ ^ CHARACTER(1024) RootName - - - "RootName for writing output files" - # ....... OtherStateType ............ # FVW_OtherStateType @@ -117,6 +118,7 @@ typedef ^ ^ Reki #.......... InitInputType ...... # FVW_InitInputType typedef FVW/FVW InitInputType CHARACTER(1024) FVWFileName - - - "Main FVW input file name" - +typedef ^ ^ CHARACTER(1024) RootName - - - "RootName for writing output files" - typedef ^ ^ MeshType WingsMesh : - - "Input Mesh defining position and orientation of wings (nSpan+1) " - typedef ^ ^ IntKi AFindx :: - - "Index to the airfoils from AD15 [idx1=BladeNode, idx2=Blade number]" - typedef ^ ^ ReKi Chord :: - - "Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number]" - diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index b0b377a5fa..d6b3b2aeec 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -67,6 +67,7 @@ MODULE FVW_Types INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] REAL(DbKi) :: DTvtk !< DT between vtk writes [s] INTEGER(IntKi) :: VTKCoord !< Switch for VTK outputs coordinate system [-] + CHARACTER(1024) :: RootName !< RootName for writing output files [-] END TYPE FVW_ParameterType ! ======================= ! ========= FVW_OtherStateType ======= @@ -147,6 +148,7 @@ MODULE FVW_Types ! ========= FVW_InitInputType ======= TYPE, PUBLIC :: FVW_InitInputType CHARACTER(1024) :: FVWFileName !< Main FVW input file name [-] + CHARACTER(1024) :: RootName !< RootName for writing output files [-] TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: WingsMesh !< Input Mesh defining position and orientation of wings (nSpan+1) [-] INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [idx1=BladeNode, idx2=Blade number] [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number] [-] @@ -283,6 +285,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%VTKBlades = SrcParamData%VTKBlades DstParamData%DTvtk = SrcParamData%DTvtk DstParamData%VTKCoord = SrcParamData%VTKCoord + DstParamData%RootName = SrcParamData%RootName END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -383,6 +386,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! VTKBlades Db_BufSz = Db_BufSz + 1 ! DTvtk Int_BufSz = Int_BufSz + 1 ! VTKCoord + Int_BufSz = Int_BufSz + 1*LEN(InData%RootName) ! RootName IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -511,6 +515,10 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Db_Xferred = Db_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKCoord Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(InData%RootName) + IntKiBuf(Int_Xferred) = ICHAR(InData%RootName(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -680,6 +688,10 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Db_Xferred = Db_Xferred + 1 OutData%VTKCoord = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(OutData%RootName) + OutData%RootName(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) @@ -4181,6 +4193,7 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS ErrStat = ErrID_None ErrMsg = "" DstInitInputData%FVWFileName = SrcInitInputData%FVWFileName + DstInitInputData%RootName = SrcInitInputData%RootName IF (ALLOCATED(SrcInitInputData%WingsMesh)) THEN i1_l = LBOUND(SrcInitInputData%WingsMesh,1) i1_u = UBOUND(SrcInitInputData%WingsMesh,1) @@ -4369,6 +4382,7 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Db_BufSz = 0 Int_BufSz = 0 Int_BufSz = Int_BufSz + 1*LEN(InData%FVWFileName) ! FVWFileName + Int_BufSz = Int_BufSz + 1*LEN(InData%RootName) ! RootName Int_BufSz = Int_BufSz + 1 ! WingsMesh allocated yes/no IF ( ALLOCATED(InData%WingsMesh) ) THEN Int_BufSz = Int_BufSz + 2*1 ! WingsMesh upper/lower bounds for each dimension @@ -4463,6 +4477,10 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs IntKiBuf(Int_Xferred) = ICHAR(InData%FVWFileName(I:I), IntKi) Int_Xferred = Int_Xferred + 1 END DO ! I + DO I = 1, LEN(InData%RootName) + IntKiBuf(Int_Xferred) = ICHAR(InData%RootName(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I IF ( .NOT. ALLOCATED(InData%WingsMesh) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -4655,6 +4673,10 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er OutData%FVWFileName(I:I) = CHAR(IntKiBuf(Int_Xferred)) Int_Xferred = Int_Xferred + 1 END DO ! I + DO I = 1, LEN(OutData%RootName) + OutData%RootName(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! WingsMesh not allocated Int_Xferred = Int_Xferred + 1 ELSE From f11eab6d359f672e5d8f1a1b77c30cac17712f1d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 21:07:29 -0600 Subject: [PATCH 092/190] FVW: using full cosine approximation for CP panelling --- modules/aerodyn/src/FVW_Wings.f90 | 60 ++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 4763c11722..bcecfad0a6 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -8,6 +8,52 @@ module FVW_Wings implicit none contains + + !> Meshing function. Create a 1D mesh of size `n`, based on a `method` and an input vector `x` + subroutine Meshing(method, x, n, y) + ! Arguments declarations + character(len=*), intent(in) :: method !< String defining the method used + integer(IntKi), intent(in) :: n !< size of vector y + real(ReKi), dimension(:),intent(in) :: x !< input vector of nodes or (/ min, max/) + real(ReKi), dimension(:), intent(out) :: y !< output vector with meshing values + ! Variable declarations + real(ReKi), dimension(:),allocatable :: dx !< + integer::jr + y = 0.0_ReKi + select case (method) ! + case ('middle') ! + allocate(dx(1:n)) + dx=diff(x) ! dx is the width of each panel + y(1:n)=x(1:n)+dx(1:n)/2._ReKi + deallocate(dx) + + case ('fullcosineapprox') ! + ! x is assumed to be of size n+1 + if (n==1) then + y=(x(1)+x(2))/2._ReKi ! middle + endif + allocate(dx(1:n)) + dx=diff(x) ! dx is the width of each panel + y(1) = x(1)+(dx(1) /(dx(1) +dx(2)))*dx(1) + y(n) = x(n)+(dx(n-1)/(dx(n-1)+dx(n)))*dx(n) + do jr=2,n-1 + y(jr)=x(jr)+0.25_ReKi*(dx(jr-1)/(dx(jr-1)+dx(jr)) + dx(jr)/(dx(jr)+dx(jr+1))+1 )*dx(jr) + end do + deallocate(dx) + end select + + contains + !> Compute: x(2:n)-x(1:n-1) + function diff(d) + real(ReKi),dimension(:),intent(in) ::d + real(ReKi),dimension(size(d)-1) ::diff + integer::i + do i=1,size(d)-1 + diff(i)=d(i+1)-d(i) + enddo + end function + end subroutine Meshing + !---------------------------------------------------------------------------------------------------------------------------------- !> Based on an input mesh, sets the following: @@ -56,16 +102,14 @@ subroutine Wings_Panelling_Init(Meshes, r, p, m, ErrStat, ErrMsg ) m%s_LL (iSpan, iW) = s_in(iSpan) m%chord_LL(iSpan, iW) = p%chord(iSpan,iW) enddo - ! --- Control points -!TODO: does it make sense to keep the global position info here? It might make it simpler to keep track of the nodes for requesting wind velocity info. - ! TODO possibly Control points are not exactly at the middle depending on "meshing" method - do iSpan = 1, p%nSpan - m%s_CP_LL (iSpan, iW) = (m%s_LL (iSpan,iW)+ m%s_LL (iSpan+1,iW))/2 - m%chord_CP_LL(iSpan, iW) = (m%chord_LL(iSpan,iW)+ m%chord_LL(iSpan+1,iW))/2 - enddo + ! --- Control points spanwise location + ! NOTE: we use the cos approximation of VanGarrel. For equispacing, it returns mid point + ! otehrwise, points are slightly closer to panels that are shorter + !call Meshing('middle' , m%s_LL(:,iW), p%nSpan, m%s_CP_LL(:,iW)) + call Meshing('fullcosineapprox' , m%s_LL(:,iW), p%nSpan, m%s_CP_LL(:,iW)) + call InterpArray(m%s_LL(:,iW), m%chord_LL(:,iW), m%s_CP_LL(:,iW), m%chord_CP_LL(:,iW)) enddo end subroutine Wings_Panelling_Init - !---------------------------------------------------------------------------------------------------------------------------------- !> Based on an input mesh, sets the following: !! - LE : Leading edge points (3 x nSpan+1 x nWings) From 774c002683c932649acd5f7147530d152380a175 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 21:49:38 -0600 Subject: [PATCH 093/190] FVW: lin. extrap for 1st and last point passed to AD --- modules/aerodyn/src/FVW.f90 | 40 +++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 275609b378..126dea2234 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -729,7 +729,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iSpan, iW, n + integer(IntKi) :: iSpan, iW, n, i0, i1, i2 integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'FVW_CalcOutput' @@ -757,16 +757,28 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, n=p%nSpan y%Vind(1:3,:,:) = 0.0_ReKi do iW=1,p%nWings - ! Linear interpolation for msot points (2:n) + ! --- Linear interpolation for most points (2:n) call InterpArray(m%s_CP_LL(:,iW), m%Vind_LL(1,:,iW), m%s_LL(2:n,iW), y%Vind(1,2:n,iW)) call InterpArray(m%s_CP_LL(:,iW), m%Vind_LL(2,:,iW), m%s_LL(2:n,iW), y%Vind(2,2:n,iW)) call InterpArray(m%s_CP_LL(:,iW), m%Vind_LL(3,:,iW), m%s_LL(2:n,iW), y%Vind(3,2:n,iW)) - ! Special case for end points (1 and n+1) - y%Vind(1:3, 1 , iW) = 0.0_ReKi ! TODO backward interpolation? - y%Vind(1:3, n+1, iW) = 0.0_ReKi ! TODO forward interpolation? - !do iSpan=1,p%nSpan+1q - !y%Vind(1:3,iSpan,iW) = m%Vind_LL(1:3,iSpan,iW) - !enddo + ! --- Special case for end points (1 and n+1) + y%Vind(1:3, 1 , iW) = 0.0_ReKi + y%Vind(1:3, n+1, iW) = 0.0_ReKi + if (p%nSpan>1) then + ! If more than 2 panels, use extrapolation + i0=1; i1=1; i2=2; ! Using CP 1 and 2 to find point 1 + y%Vind(1, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(1,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(1,i2,iW)) + y%Vind(2, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(2,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(2,i2,iW)) + y%Vind(3, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(3,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(3,i2,iW)) + i0=n+1; i1=n; i2=n-1; ! Using CP n and n-2 to find point n+1 + y%Vind(1, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(1,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(1,i2,iW)) + y%Vind(2, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(2,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(2,i2,iW)) + y%Vind(3, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(3,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(3,i2,iW)) + else + ! If one panel, duplicate the unique point on both side + y%Vind(1:3, 1, iW) = m%Vind_LL(1:3, 1, iW) + y%Vind(1:3, 2, iW) = m%Vind_LL(1:3, 1, iW) + endif enddo ! For plotting only @@ -799,10 +811,16 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, logical function Failed() call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_CalcOutput') Failed = ErrStat >= AbortErrLev - !if (Failed) call CleanUp() end function Failed - !========================================================================== -end subroutine FVW_CalcOutput + !> Perform linear extrapolation to get value of y(x0), using y(x1) and y(x2) + real(ReKi) function lin_extrap(x0, x1, y1, x2, y2) result(y0) + real(ReKi), intent(in) :: x0, x1, y1, x2, y2 + real(ReKi) :: a + a = (x0-x1)/(x0-x2) + y0 = 1._ReKi/(1._ReKi-a) * (y1-a*y2) + end function lin_extrap + +end subroutine FVW_CalcOutput END MODULE FVW From 2bee941ca396361625229fae1778de8110bfd83d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 23:31:08 -0600 Subject: [PATCH 094/190] FVW: removing last NW shed vorticity when FW has no shed vorticity --- modules/aerodyn/src/FVW_IO.f90 | 7 ++++++- modules/aerodyn/src/FVW_Subs.f90 | 16 +++++++++++----- modules/aerodyn/src/FVW_Tests.f90 | 8 ++++---- modules/aerodyn/src/FVW_VortexTools.f90 | 18 +++++++++++------- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 0498aa1784..e166f6eb6c 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -91,6 +91,11 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check(Inp%WingRegParam<0 , 'Wing regularization parameter (WakeRegParam) should be positive')) return if (Check(Inp%CoreSpreadEddyVisc<0 , 'Core spreading eddy viscosity (CoreSpreadEddyVisc) should be positive')) return + ! Removing the shed vorticity is a dangerous option if this is done too close to the blades. + ! To be safe, we will no matter what ensure that the last segments of NW are 0 if FWShedVorticity is False (see PackPanelsToSegments) + ! Still we force the user to be responsible. + if (Check((.not.(Inp%FWShedVorticity)) .and. Inp%nNWPanels<50, '`FWShedVorticity` should be true if `nNWPanels`<50. Alternatively, use a larger number of NWPanels ')) return + Inp%DTvtk = Get_DTvtk( VTK_fps_line, p%DTaero, Inp%DTfvw ) ! At least one NW panel if FW, this shoudln't be a problem since the LL is in NW, but safety for now @@ -253,7 +258,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) iHeadP=1 iHeadC=1 do iW=1,p%nWings - CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .True. ) + CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .True. , .True.) enddo endif diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 8998d70533..4a23e416a5 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -404,7 +404,11 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG integer(IntKi), intent(out) :: nSegP !< Total number of segments points after packing ! Local integer(IntKi) :: iHeadC, iHeadP, nC, nP, iW, iHeadC_bkp - real(ReKi), dimension(:,:), allocatable :: Buffer2d + logical :: LastNWShed + + ! If the FW contains Shed vorticity, we include the last shed vorticity form the NW, orhtwerise, we don't! + ! It's important not to include it, otherwise a strong vortex will be present there with no compensating vorticity from the FW + LastNWShed = (p%FWShedVorticity ) .or. (m%nNW=0) then nP = p%nWings * ( (p%nSpan+1)*(m%nNW-iDepthStart+2) ) nC = p%nWings * (2*(p%nSpan+1)*(m%nNW-iDepthStart+2)-(p%nSpan+1)-(m%nNW-iDepthStart+1+1)) + if (.not.LastNWShed) then + nC = nC - p%nWings * (p%nSpan) ! Removing last set of sehd segments + endif endif if (m%nFW>0) then nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) @@ -426,21 +433,20 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG if (allocated(SegConnct)) deallocate(SegConnct) if (allocated(SegPoints)) deallocate(SegPoints) if (allocated(SegGamma)) deallocate(SegGamma) - if (allocated(Buffer2d)) deallocate(Buffer2d) allocate(SegConnct(1:4,1:nC)); SegConnct=-1 allocate(SegPoints(1:3,1:nP)); SegPoints=-1 allocate(SegGamma (1:nC)); SegGamma =-1 - allocate(Buffer2d(1,p%nSpan)) + ! iHeadP=1 iHeadC=1 do iW=1,p%nWings - CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .True. ) + CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .True., LastNWShed ) enddo if (m%nFW>0) then iHeadC_bkp = iHeadC do iW=1,p%nWings - CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , p%FWShedVorticity) + CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , p%FWShedVorticity, .False.) enddo SegConnct(3,iHeadC_bkp:) = SegConnct(3,iHeadC_bkp:) + m%nNW endif diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index 7d28081c24..b745fca855 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -370,7 +370,7 @@ subroutine Test_LatticeToSegment(iStat) iHeadP=1 iHeadC=1 - CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. ) + CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true., .true. ) CALL printall() CALL WrVTK_Segments('Points1_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) @@ -397,7 +397,7 @@ subroutine Test_LatticeToSegment(iStat) allocate(SegGamma (1:nC2) ); SegGamma=-9999 iHeadP=1 iHeadC=1 - CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , .true.) + CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , .true., .true.) CALL printall() CALL WrVTK_Segments('Points2_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) @@ -412,8 +412,8 @@ subroutine Test_LatticeToSegment(iStat) allocate(SegConnct(1:2,1:nC)); SegConnct=-1 allocate(SegPoints(1:3,1:nP)); SegPoints=-1 allocate(SegGamma (1:nC) ); SegGamma=-9999 - CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. ) - CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. ) + CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. , .true.) + CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. , .true.) CALL printall() CALL WrVTK_Segments('PointsBoth_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index c96dee2b6f..c5bcbe6cbc 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -49,7 +49,7 @@ subroutine LatticeToPoints(LatticePoints, iDepthStart, Points, iHeadP) endsubroutine LatticeToPoints - subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, bShedVorticity ) + subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, bShedVorticity, bShedLastVorticity ) real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Points 3 x nSpan x nDepth real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< GammaPanl nSpan x nDepth integer(IntKi), intent(in ) :: iDepthStart !< Start index for depth dimension @@ -59,6 +59,7 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints integer(IntKi), intent(inout) :: iHeadP !< Index indicating where to start in SegPoints integer(IntKi), intent(inout) :: iHeadC !< Index indicating where to start in SegConnct logical , intent(in ) :: bShedVorticity !< Shed vorticity is included if true + logical , intent(in ) :: bShedLastVorticity !< Shed the last vorticity segment if true ! Local integer(IntKi) :: nSpan, nDepth integer(IntKi) :: iSpan, iDepth @@ -126,12 +127,15 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints ! Segment 4-3 if (iDepth==nDepth-1) then if (bShedVorticity) then - SegConnct(1,iHeadC) = iseg4 - SegConnct(2,iHeadC) = iseg3 - SegConnct(3,iHeadC) = iDepth - SegConnct(4,iHeadC) = iSpan - SegGamma (iHeadC ) = - LatticeGamma(iSpan,iDepth) - iHeadC=iHeadC+1 + ! We shed vorticity, but if it's the last panel, and bShedLastVorticity is false, we don't + if ((iDepth Date: Wed, 18 Mar 2020 23:55:58 -0600 Subject: [PATCH 095/190] FVW: fixed FW convection if no FWEFF panel present --- modules/aerodyn/src/FVW.f90 | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 126dea2234..0e44f82ea2 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -565,7 +565,7 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt character(ErrMsgLen) :: ErrMsg2 ! temporary error message integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current time step integer(IntKi) :: i,j,k - real(ReKi), dimension(3) :: VmeanFW ! Mean velocity of the far wake + real(ReKi), dimension(3) :: VmeanFW, VmeanNW ! Mean velocity of the near wake and far wake ErrStat = ErrID_None ErrMsg = "" @@ -584,12 +584,26 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ! Out: m%Vind_NW, m%Vind_FW call WakeInducedVelocities(p, x, m, ErrStat2, ErrMsg2); if(Failed()) return - ! --- Induced velocity of the non-free far wake is taken as the mean velocity in the free far-wake + ! --- Mean induced velocity over the near wake (NW) + VmeanNW(1:3)=0 + if (m%nNW >1) then + do i=1,size(m%Vind_NW,4); do j=2,m%nNW+1; do k=1,size(m%Vind_NW,2); + VmeanNW(1:3) = VmeanNW(1:3) + m%Vind_NW(1:3, k, j, i) + enddo; enddo; enddo; + VmeanNW(1:3) = VmeanNW(1:3) / (size(m%Vind_NW,4)*m%nNW*size(m%Vind_NW,2)) + endif + ! --- Induced velocity over the free far wake (FWEff) VmeanFW(1:3)=0 - do i=1,size(m%Vind_FW,4); do j=1,nFWEff; do k=1,size(m%Vind_FW,2); - VmeanFW(1:3) = VmeanFW(1:3) + m%Vind_FW(1:3, k, j, i) - enddo; enddo; enddo; - VmeanFW(1:3) = VmeanFW(1:3) / (size(m%Vind_FW,4)*nFWEff*size(m%Vind_FW,2)) + if (nFWEff >0) then + do i=1,size(m%Vind_FW,4); do j=1,nFWEff; do k=1,size(m%Vind_FW,2); + VmeanFW(1:3) = VmeanFW(1:3) + m%Vind_FW(1:3, k, j, i) + enddo; enddo; enddo; + VmeanFW(1:3) = VmeanFW(1:3) / (size(m%Vind_FW,4)*nFWEff*size(m%Vind_FW,2)) + else + VmeanFW=VmeanNW + endif + + ! --- Convecting non-free FW with a constant induced velocity (and free stream) m%Vind_FW(1, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) = VmeanFW(1) ! m%Vind_FW(2, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) = VmeanFW(2) ! m%Vind_FW(3, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) = VmeanFW(3) ! From a1806a41dff61fed45d973b8b86ba2606dbc192a Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 18 Mar 2020 23:56:57 -0600 Subject: [PATCH 096/190] FVW: default circulation criteria more strict, and more eddy visc --- modules/aerodyn/src/FVW_IO.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index e166f6eb6c..59ce323b73 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -44,7 +44,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) !------------------------ CIRCULATION SPECIFICATIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'Circulation specification header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod' ,'', idCircPolarData, ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvConvCrit ,'CircSolvConvCrit ' ,'', 0.01 , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvConvCrit ,'CircSolvConvCrit ' ,'', 0.001 , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvRelaxation,'CircSolvRelaxation','', 0.1 , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvMaxIter ,'CircSolvMaxIter' ,'', 30 , ErrStat2,ErrMsg2); if(Failed())return !CALL ReadVar(UnIn,FileName,Inp%CircSolvPolar ,'CircSolvPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return @@ -62,7 +62,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVarWDefault(UnIn,FileName,Inp%WakeRegMethod ,'WakeRegMethod' ,'',idRegConstant , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%WakeRegParam ,'WakeRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%WingRegParam ,'WingRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%CoreSpreadEddyVisc ,'CoreSpreadEddyVisc','',10.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%CoreSpreadEddyVisc ,'CoreSpreadEddyVisc','',100.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return !------------------------ OUTPUT OPTIONS ----------------------------------------- CALL ReadCom (UnIn,FileName, 'Output options header' ,ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'', 0 ,ErrStat2,ErrMsg2); if(Failed())return From 20b0173df54092749de80779340167705b849cfa Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 19 Mar 2020 00:25:42 -0600 Subject: [PATCH 097/190] FVW: dt earlier in input file, and default smooth circulation --- modules/aerodyn/src/FVW_IO.f90 | 11 +++++------ modules/aerodyn/src/FVW_Subs.f90 | 15 +++++++++++---- modules/aerodyn/src/FVW_Wings.f90 | 14 +++++++++++--- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 59ce323b73..0f72d61a4c 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -35,12 +35,11 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadCom(UnIn, FileName, 'FVW input file header line 1', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadCom(UnIn, FileName, 'FVW input file header line 2', ErrStat2, ErrMsg2 ); if(Failed()) return !------------------------ GENERAL OPTIONS ------------------------------------------- - CALL ReadCom (UnIn,FileName, 'General option header', ErrStat2,ErrMsg2); if(Failed()) return - CALL ReadVarWDefault(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'', idEuler1, ErrStat2,ErrMsg2); if(Failed())return - - CALL ReadVarWDefault(UnIn,FileName,Inp%FreeWakeStart ,'FreeWakeStart' ,'', 0.0_ReKi, ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart','', 0.0_ReKi, ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%DTfvw ,'DTfvw' ,'', p%DTaero, ErrStat2,ErrMsg2); if(Failed())return + CALL ReadCom (UnIn,FileName, 'General option header' , ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadVarWDefault(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'', idEuler1 , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%DTfvw ,'DTfvw' ,'', p%DTaero , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%FreeWakeStart ,'FreeWakeStart' ,'', 0.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart','', real(20.0_ReKi*Inp%DTfvw,ReKi), ErrStat2,ErrMsg2); if(Failed())return !------------------------ CIRCULATION SPECIFICATIONS ------------------------------------------- CALL ReadCom(UnIn,FileName, 'Circulation specification header', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod' ,'', idCircPolarData, ErrStat2,ErrMsg2); if(Failed())return diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 4a23e416a5..f0a93c967c 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -511,10 +511,17 @@ subroutine FVW_InitRegularization(p, m, ErrStat, ErrMsg) print*,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' print*,'!!! NOTE: using optmized wake regularization parameters is still a beta feature!' print*,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' - p%WakeRegParam = RegParam - p%WingRegParam = RegParam - p%CoreSpreadEddyVisc = 100 - p%RegFunction = idRegVatistas + p%WakeRegMethod = idRegConstant + p%RegFunction = idRegVatistas + p%WakeRegParam = RegParam + p%WingRegParam = RegParam + p%CoreSpreadEddyVisc = 100 + write(*,'(A)' ) 'The following regularization parameters will be used:' + write(*,'(A,I0)' ) 'WakeRegMethod : ', p%WakeRegMethod + write(*,'(A,I0)' ) 'RegFunction : ', p%RegFunction + write(*,'(A,1F8.4)') 'WakeRegParam : ', p%WakeRegParam + write(*,'(A,1F8.4)') 'WingRegParam : ', p%WingRegParam + write(*,'(A,1F8.4)') 'CoreSpreadEddyVisc: ', p%CoreSpreadEddyVisc endif ! KEEP ME: potentially perform pre-computation here !if (p%WakeRegMethod==idRegConstant) then diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index bcecfad0a6..a3de2ffa67 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -241,8 +241,12 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFIn GammaScale=0.0_ReKi else s=(t/p%FullCirculationStart) - !GammaScale = 1._ReKi- 1._ReKi/(1._ReKi+exp((1-2*s)/(s*(s-1._ReKi)))) ! Using a smooth approsimation of HeavySide function - GammaScale = s ! Using a linear scaling + ! If we have at least 10 points we use a smooth Heavyside, otehrwise we use a simple linear scaling + if (p%FullCirculationStart/p%DTfvw >= 9) then + GammaScale = 1._ReKi- 1._ReKi/(1._ReKi+exp((1-2*s)/(s*(s-1._ReKi)))) ! Using a smooth approsimation of HeavySide function + else + GammaScale = s ! Using a linear scaling + endif endif else GammaScale=1.0_ReKi @@ -316,7 +320,11 @@ subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x call CirculationFromPolarData(GammaLastIter, p, m, AFInfo,ErrStat2,ErrMsg2); if(Failed()) return; else ! NOTE: we need to inverse the scaling to speed up the convergence - GammaLastIter(1:p%nSpan,1:p%nWings) = Gamma_LL_prev(1:p%nSpan,1:p%nWings) / GammaScale + if (.not. EqualRealNos(GammaScale, 0.0_ReKi)) then + GammaLastIter(1:p%nSpan,1:p%nWings) = Gamma_LL_prev(1:p%nSpan,1:p%nWings) / GammaScale + else + GammaLastIter(1:p%nSpan,1:p%nWings) = Gamma_LL_prev(1:p%nSpan,1:p%nWings) + endif endif if (any(x%r_NW(1,:,1:m%nNW+1,:)<-999)) then From 3ae2e08325ec3fb09f72c3eea534f25b7b7f5e24 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 19 Mar 2020 01:42:35 -0600 Subject: [PATCH 098/190] FVW: fixed bug for n=1 --- modules/aerodyn/src/FVW_Wings.f90 | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index a3de2ffa67..217067fed8 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -30,16 +30,18 @@ subroutine Meshing(method, x, n, y) case ('fullcosineapprox') ! ! x is assumed to be of size n+1 if (n==1) then - y=(x(1)+x(2))/2._ReKi ! middle + y(1)=(x(1)+x(2))/2._ReKi ! middle + return + else + allocate(dx(1:n)) + dx=diff(x) ! dx is the width of each panel + y(1) = x(1)+(dx(1) /(dx(1) +dx(2)))*dx(1) + y(n) = x(n)+(dx(n-1)/(dx(n-1)+dx(n)))*dx(n) + do jr=2,n-1 + y(jr)=x(jr)+0.25_ReKi*(dx(jr-1)/(dx(jr-1)+dx(jr)) + dx(jr)/(dx(jr)+dx(jr+1))+1 )*dx(jr) + end do + deallocate(dx) endif - allocate(dx(1:n)) - dx=diff(x) ! dx is the width of each panel - y(1) = x(1)+(dx(1) /(dx(1) +dx(2)))*dx(1) - y(n) = x(n)+(dx(n-1)/(dx(n-1)+dx(n)))*dx(n) - do jr=2,n-1 - y(jr)=x(jr)+0.25_ReKi*(dx(jr-1)/(dx(jr-1)+dx(jr)) + dx(jr)/(dx(jr)+dx(jr+1))+1 )*dx(jr) - end do - deallocate(dx) end select contains From c091ab03cadc7ffdb6d0ebacd231a35fcd708954 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 19 Mar 2020 01:44:38 -0600 Subject: [PATCH 099/190] FVW: fixed bug in azimuth dimension --- modules/aerodyn/src/AeroDyn.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index c0a0cdd52b..63c3cf595e 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1485,7 +1485,7 @@ subroutine DiskAvgValues(p, u, m, x_hat_disk, y_hat_disk, z_hat_disk, Azimuth) real(ReKi), intent( out) :: x_hat_disk(3) real(ReKi), intent( out) :: y_hat_disk(3) real(ReKi), intent( out) :: z_hat_disk(3) - real(R8Ki), intent( out) :: Azimuth(p%NumBlNds) + real(R8Ki), intent( out) :: Azimuth(p%NumBlades) real(ReKi) :: z_hat(3) real(ReKi) :: tmp(3) real(ReKi) :: tmp_sz, tmp_sz_y From 58657c8eac9f4a4c12594f86d02c878489f0e010 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 19 Mar 2020 02:10:43 -0600 Subject: [PATCH 100/190] FVW: bug fix, last shed segment of FW --- modules/aerodyn/src/FVW_Subs.f90 | 5 +++-- modules/aerodyn/src/FVW_VortexTools.f90 | 17 +++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index f0a93c967c..2e0aabe98f 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -408,7 +408,7 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG ! If the FW contains Shed vorticity, we include the last shed vorticity form the NW, orhtwerise, we don't! ! It's important not to include it, otherwise a strong vortex will be present there with no compensating vorticity from the FW - LastNWShed = (p%FWShedVorticity ) .or. (m%nNW0) then nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) if (p%FWShedVorticity) then @@ -446,7 +447,7 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG if (m%nFW>0) then iHeadC_bkp = iHeadC do iW=1,p%nWings - CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , p%FWShedVorticity, .False.) + CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , p%FWShedVorticity, p%FWShedVorticity) enddo SegConnct(3,iHeadC_bkp:) = SegConnct(3,iHeadC_bkp:) + m%nNW endif diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index c5bcbe6cbc..1da3228f6d 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -126,16 +126,13 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints iHeadC=iHeadC+1 ! Segment 4-3 if (iDepth==nDepth-1) then - if (bShedVorticity) then - ! We shed vorticity, but if it's the last panel, and bShedLastVorticity is false, we don't - if ((iDepth Date: Thu, 19 Mar 2020 02:11:29 -0600 Subject: [PATCH 101/190] FVW: more digits in time output --- modules/aerodyn/src/FVW.f90 | 2 +- modules/aerodyn/src/FVW_IO.f90 | 2 +- modules/aerodyn/src/FVW_Subs.f90 | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 0e44f82ea2..43ac32e349 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -438,7 +438,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m nP = p%nWings * ( (p%nSpan+1)*(m%nNW-1+2) +(FWnSpan+1)*(m%nFW+1) ) nFWEff = min(m%nFW, p%nFWFree) ! --- Display some status to screen - if (mod(n,10)==0) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F6.1,A)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's' + if (mod(n,10)==0) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F7.2,A)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's' if (DEV_VERSION) print'(A,F10.3,A,F10.3,A,I0,A,I0,A,I0,A,L1)','Update states, t:',t,' t_u:', utimes(1),' Step:',n,' nNW:',m%nNW,' nFW:',m%nFW,' ComputeWake: ',m%ComputeWakeInduced diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 0f72d61a4c..cbd50160ef 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -93,7 +93,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) ! Removing the shed vorticity is a dangerous option if this is done too close to the blades. ! To be safe, we will no matter what ensure that the last segments of NW are 0 if FWShedVorticity is False (see PackPanelsToSegments) ! Still we force the user to be responsible. - if (Check((.not.(Inp%FWShedVorticity)) .and. Inp%nNWPanels<50, '`FWShedVorticity` should be true if `nNWPanels`<50. Alternatively, use a larger number of NWPanels ')) return + if (Check((.not.(Inp%FWShedVorticity)) .and. Inp%nNWPanels<30, '`FWShedVorticity` should be true if `nNWPanels`<30. Alternatively, use a larger number of NWPanels ')) return Inp%DTvtk = Get_DTvtk( VTK_fps_line, p%DTaero, Inp%DTfvw ) diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 2e0aabe98f..cb4a9b2b8a 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -420,7 +420,6 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG nC = nC - p%nWings * (p%nSpan) ! Removing last set of sehd segments endif endif - nC1=nC if (m%nFW>0) then nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) if (p%FWShedVorticity) then From 13afa0ed49cb7fde9f9a783c34c5de9e9528fb47 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 2 Apr 2020 13:34:02 -0600 Subject: [PATCH 102/190] FVW: reverting some of the Ad14 changes --- modules/aerodyn14/CMakeLists.txt | 3 - modules/aerodyn14/src/AeroDyn14.f90 | 8 +- modules/aerodyn14/src/AeroDyn14_Types.f90 | 2504 ++++++++++++++++++- modules/aerodyn14/src/Registry-AD14.txt | 57 +- modules/openfast-library/src/FAST_Types.f90 | 1 - 5 files changed, 2516 insertions(+), 57 deletions(-) diff --git a/modules/aerodyn14/CMakeLists.txt b/modules/aerodyn14/CMakeLists.txt index 1a0e7634ea..be1846cc91 100644 --- a/modules/aerodyn14/CMakeLists.txt +++ b/modules/aerodyn14/CMakeLists.txt @@ -16,7 +16,6 @@ if (GENERATE_TYPES) generate_f90_types(src/Registry-AD14.txt ${CMAKE_CURRENT_LIST_DIR}/src/AeroDyn14_Types.f90) - generate_f90_types(src/Registry-AD14AeroConf.txt ${CMAKE_CURRENT_LIST_DIR}/src/AD14AeroConf_Types.f90) generate_f90_types(src/Registry-DWM.txt ${CMAKE_CURRENT_LIST_DIR}/src/DWM_Types.f90) endif() @@ -26,10 +25,8 @@ set(AD14_LIBS_SOURCES src/DWM.f90 src/DWM_Wake_Sub_ver2.f90 src/GenSubs.f90 - src/AeroDyn14_Types.f90 src/DWM_Types.f90 - src/AD14AeroConf_Types.f90 ) add_library(aerodyn14lib ${AD14_LIBS_SOURCES}) diff --git a/modules/aerodyn14/src/AeroDyn14.f90 b/modules/aerodyn14/src/AeroDyn14.f90 index d8f2414249..b608cef645 100644 --- a/modules/aerodyn14/src/AeroDyn14.f90 +++ b/modules/aerodyn14/src/AeroDyn14.f90 @@ -461,7 +461,7 @@ SUBROUTINE AD14_Init( InitInp, u, p, x, xd, z, O, y, m, Interval, InitOut, ErrSt ! u%TurbineComponents !.......... - CALL AD14AeroConf_CopyInput( InitInp%TurbineComponents, u%TurbineComponents, MESH_NEWCOPY, ErrStatLcl, ErrMessLcl ) + CALL AD14_CopyAeroConfig( InitInp%TurbineComponents, u%TurbineComponents, MESH_NEWCOPY, ErrStatLcl, ErrMessLcl ) CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) IF (ErrStat >= AbortErrLev) RETURN @@ -587,8 +587,6 @@ SUBROUTINE AD14_Init( InitInp, u, p, x, xd, z, O, y, m, Interval, InitOut, ErrSt CALL SetErrStat ( ErrStatLcl, ErrMessLcl, ErrStat,ErrMess,RoutineName ) IF (ErrStat >= AbortErrLev) RETURN ENDDO - - !.......... @@ -742,8 +740,6 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) USE AeroGenSubs, ONLY: ElemOut USE DWM_Types USE DWM -!FIXME: remove InflowWind from here... - USE InflowWind !! KS REAL(DbKi), INTENT(IN ) :: Time ! Current simulation time in seconds TYPE(AD14_InputType), INTENT(INOUT) :: u ! Inputs at Time @@ -1101,7 +1097,7 @@ SUBROUTINE AD14_CalcOutput( Time, u, p, x, xd, z, O, y, m, ErrStat, ErrMess ) CALL CleanUp() RETURN END IF - + !------------------------------------------------------------------------------------------- ! Set up dynamic inflow parameters !------------------------------------------------------------------------------------------- diff --git a/modules/aerodyn14/src/AeroDyn14_Types.f90 b/modules/aerodyn14/src/AeroDyn14_Types.f90 index 9b1eaed11a..42b9934204 100644 --- a/modules/aerodyn14/src/AeroDyn14_Types.f90 +++ b/modules/aerodyn14/src/AeroDyn14_Types.f90 @@ -40,9 +40,51 @@ MODULE AeroDyn14_Types USE Lidar_Types USE InflowWind_Types USE DWM_Types -USE AD14AeroConf_Types USE NWTC_Library IMPLICIT NONE +! ========= Marker ======= + TYPE, PUBLIC :: Marker + REAL(ReKi) , DIMENSION(1:3) :: Position + REAL(ReKi) , DIMENSION(1:3,1:3) :: Orientation + REAL(ReKi) , DIMENSION(1:3) :: TranslationVel + REAL(ReKi) , DIMENSION(1:3) :: RotationVel + END TYPE Marker +! ======================= +! ========= AeroConfig ======= + TYPE, PUBLIC :: AeroConfig + TYPE(Marker) , DIMENSION(:), ALLOCATABLE :: Blade + TYPE(Marker) :: Hub + TYPE(Marker) :: RotorFurl + TYPE(Marker) :: Nacelle + TYPE(Marker) :: TailFin + TYPE(Marker) :: Tower + TYPE(Marker) :: SubStructure + TYPE(Marker) :: Foundation + REAL(ReKi) :: BladeLength + END TYPE AeroConfig +! ======================= +! ========= AirFoil ======= + TYPE, PUBLIC :: AirFoil + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: AL + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CD + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CL + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CM + REAL(ReKi) :: PMC + REAL(ReKi) :: MulTabLoc + END TYPE AirFoil +! ======================= +! ========= AirFoilParms ======= + TYPE, PUBLIC :: AirFoilParms + INTEGER(IntKi) :: MaxTable = 20 + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: NTables + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: NLift + INTEGER(IntKi) :: NumCL + INTEGER(IntKi) :: NumFoil + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: NFoil + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: MulTabMet + CHARACTER(1024) , DIMENSION(:), ALLOCATABLE :: FoilNm + END TYPE AirFoilParms +! ======================= ! ========= Beddoes ======= TYPE, PUBLIC :: Beddoes REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: ADOT @@ -312,7 +354,7 @@ MODULE AeroDyn14_Types REAL(ReKi) :: BladeLength !< Blade Length [-] LOGICAL :: LinearizeFlag LOGICAL :: UseDWM = .FALSE. !< flag to determine if DWM module should be used [-] - TYPE(AD14AeroConf_InputType) :: TurbineComponents + TYPE(AeroConfig) :: TurbineComponents INTEGER(IntKi) :: NumTwrNodes !< Number of ElastoDyn tower nodes. Tower drag will be computed at those points. [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: TwrNodeLocs !< Location of ElastoDyn tower nodes with respect to the inertial origin. [-] REAL(ReKi) :: HubHt !< hub height wrt inertial origin [m] @@ -365,7 +407,7 @@ MODULE AeroDyn14_Types LOGICAL :: OnePassDynDbg = .TRUE. LOGICAL :: NoLoadsCalculated = .TRUE. INTEGER(IntKi) :: NERRORS = 0 - TYPE(AD14AeroConf_MiscVarType) :: AirFoil + TYPE(AirFoil) :: AirFoil TYPE(Beddoes) :: Beddoes TYPE(DynInflow) :: DynInflow TYPE(Element) :: Element @@ -407,7 +449,7 @@ MODULE AeroDyn14_Types INTEGER(IntKi) :: MAXICOUNT = 1000 LOGICAL :: WrOptFile = .TRUE. !< T/F: Write an AeroDyn summary [-] INTEGER(IntKi) :: DEFAULT_Wind = -1 - TYPE(AD14AeroConf_ParameterType) :: AirFoil + TYPE(AirFoilParms) :: AirFoil TYPE(BladeParms) :: Blade TYPE(BeddoesParms) :: Beddoes TYPE(DynInflowParms) :: DynInflow @@ -417,14 +459,13 @@ MODULE AeroDyn14_Types TYPE(WindParms) :: Wind TYPE(RotorParms) :: Rotor TYPE(DWM_ParameterType) :: DWM - REAL(DbKi) :: IfW_DT END TYPE AD14_ParameterType ! ======================= ! ========= AD14_InputType ======= TYPE, PUBLIC :: AD14_InputType TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: InputMarkers !< Input Forces and positions for the blades (mesh) for each blade [-] TYPE(MeshType) :: Twr_InputMarkers !< Input Forces and positions for the tower (mesh) [-] - TYPE(AD14AeroConf_InputType) :: TurbineComponents !< Current locations of components [-] + TYPE(AeroConfig) :: TurbineComponents !< Current locations of components [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: MulTabLoc REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: InflowVelocity !< U,V,W wind inflow speeds at all locations on the Inputmarker and Twr_InputMarker meshes [m/s] REAL(ReKi) , DIMENSION(1:3) :: AvgInfVel !< an average disk velocity (depends on wind type and should be removed) [m/s] @@ -437,6 +478,1964 @@ MODULE AeroDyn14_Types END TYPE AD14_OutputType ! ======================= CONTAINS + SUBROUTINE AD14_CopyMarker( SrcMarkerData, DstMarkerData, CtrlCode, ErrStat, ErrMsg ) + TYPE(Marker), INTENT(IN) :: SrcMarkerData + TYPE(Marker), INTENT(INOUT) :: DstMarkerData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_CopyMarker' +! + ErrStat = ErrID_None + ErrMsg = "" + DstMarkerData%Position = SrcMarkerData%Position + DstMarkerData%Orientation = SrcMarkerData%Orientation + DstMarkerData%TranslationVel = SrcMarkerData%TranslationVel + DstMarkerData%RotationVel = SrcMarkerData%RotationVel + END SUBROUTINE AD14_CopyMarker + + SUBROUTINE AD14_DestroyMarker( MarkerData, ErrStat, ErrMsg ) + TYPE(Marker), INTENT(INOUT) :: MarkerData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_DestroyMarker' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE AD14_DestroyMarker + + SUBROUTINE AD14_PackMarker( 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(Marker), 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 = 'AD14_PackMarker' + ! 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 + Re_BufSz = Re_BufSz + SIZE(InData%Position) ! Position + Re_BufSz = Re_BufSz + SIZE(InData%Orientation) ! Orientation + Re_BufSz = Re_BufSz + SIZE(InData%TranslationVel) ! TranslationVel + Re_BufSz = Re_BufSz + SIZE(InData%RotationVel) ! RotationVel + 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 + + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Position))-1 ) = PACK(InData%Position,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Position) + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Orientation))-1 ) = PACK(InData%Orientation,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Orientation) + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%TranslationVel))-1 ) = PACK(InData%TranslationVel,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%TranslationVel) + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%RotationVel))-1 ) = PACK(InData%RotationVel,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%RotationVel) + END SUBROUTINE AD14_PackMarker + + SUBROUTINE AD14_UnPackMarker( 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(Marker), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_UnPackMarker' + ! 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 + i1_l = LBOUND(OutData%Position,1) + i1_u = UBOUND(OutData%Position,1) + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + OutData%Position = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Position))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Position) + DEALLOCATE(mask1) + i1_l = LBOUND(OutData%Orientation,1) + i1_u = UBOUND(OutData%Orientation,1) + i2_l = LBOUND(OutData%Orientation,2) + i2_u = UBOUND(OutData%Orientation,2) + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + OutData%Orientation = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Orientation))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Orientation) + DEALLOCATE(mask2) + i1_l = LBOUND(OutData%TranslationVel,1) + i1_u = UBOUND(OutData%TranslationVel,1) + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + OutData%TranslationVel = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%TranslationVel))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%TranslationVel) + DEALLOCATE(mask1) + i1_l = LBOUND(OutData%RotationVel,1) + i1_u = UBOUND(OutData%RotationVel,1) + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + OutData%RotationVel = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%RotationVel))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%RotationVel) + DEALLOCATE(mask1) + END SUBROUTINE AD14_UnPackMarker + + SUBROUTINE AD14_CopyAeroConfig( SrcAeroConfigData, DstAeroConfigData, CtrlCode, ErrStat, ErrMsg ) + TYPE(AeroConfig), INTENT(IN) :: SrcAeroConfigData + TYPE(AeroConfig), INTENT(INOUT) :: DstAeroConfigData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_CopyAeroConfig' +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(SrcAeroConfigData%Blade)) THEN + i1_l = LBOUND(SrcAeroConfigData%Blade,1) + i1_u = UBOUND(SrcAeroConfigData%Blade,1) + IF (.NOT. ALLOCATED(DstAeroConfigData%Blade)) THEN + ALLOCATE(DstAeroConfigData%Blade(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAeroConfigData%Blade.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DO i1 = LBOUND(SrcAeroConfigData%Blade,1), UBOUND(SrcAeroConfigData%Blade,1) + CALL AD14_Copymarker( SrcAeroConfigData%Blade(i1), DstAeroConfigData%Blade(i1), CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + ENDDO +ENDIF + CALL AD14_Copymarker( SrcAeroConfigData%Hub, DstAeroConfigData%Hub, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14_Copymarker( SrcAeroConfigData%RotorFurl, DstAeroConfigData%RotorFurl, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14_Copymarker( SrcAeroConfigData%Nacelle, DstAeroConfigData%Nacelle, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14_Copymarker( SrcAeroConfigData%TailFin, DstAeroConfigData%TailFin, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14_Copymarker( SrcAeroConfigData%Tower, DstAeroConfigData%Tower, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14_Copymarker( SrcAeroConfigData%SubStructure, DstAeroConfigData%SubStructure, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL AD14_Copymarker( SrcAeroConfigData%Foundation, DstAeroConfigData%Foundation, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + DstAeroConfigData%BladeLength = SrcAeroConfigData%BladeLength + END SUBROUTINE AD14_CopyAeroConfig + + SUBROUTINE AD14_DestroyAeroConfig( AeroConfigData, ErrStat, ErrMsg ) + TYPE(AeroConfig), INTENT(INOUT) :: AeroConfigData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_DestroyAeroConfig' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(AeroConfigData%Blade)) THEN +DO i1 = LBOUND(AeroConfigData%Blade,1), UBOUND(AeroConfigData%Blade,1) + CALL AD14_Destroymarker( AeroConfigData%Blade(i1), ErrStat, ErrMsg ) +ENDDO + DEALLOCATE(AeroConfigData%Blade) +ENDIF + CALL AD14_Destroymarker( AeroConfigData%Hub, ErrStat, ErrMsg ) + CALL AD14_Destroymarker( AeroConfigData%RotorFurl, ErrStat, ErrMsg ) + CALL AD14_Destroymarker( AeroConfigData%Nacelle, ErrStat, ErrMsg ) + CALL AD14_Destroymarker( AeroConfigData%TailFin, ErrStat, ErrMsg ) + CALL AD14_Destroymarker( AeroConfigData%Tower, ErrStat, ErrMsg ) + CALL AD14_Destroymarker( AeroConfigData%SubStructure, ErrStat, ErrMsg ) + CALL AD14_Destroymarker( AeroConfigData%Foundation, ErrStat, ErrMsg ) + END SUBROUTINE AD14_DestroyAeroConfig + + SUBROUTINE AD14_PackAeroConfig( 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(AeroConfig), 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 = 'AD14_PackAeroConfig' + ! 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 ! Blade allocated yes/no + IF ( ALLOCATED(InData%Blade) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! Blade upper/lower bounds for each dimension + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + DO i1 = LBOUND(InData%Blade,1), UBOUND(InData%Blade,1) + Int_BufSz = Int_BufSz + 3 ! Blade: size of buffers for each call to pack subtype + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Blade(i1), ErrStat2, ErrMsg2, .TRUE. ) ! Blade + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Blade + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Blade + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Blade + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + END DO + END IF + Int_BufSz = Int_BufSz + 3 ! Hub: size of buffers for each call to pack subtype + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Hub, ErrStat2, ErrMsg2, .TRUE. ) ! Hub + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Hub + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Hub + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Hub + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! RotorFurl: size of buffers for each call to pack subtype + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%RotorFurl, ErrStat2, ErrMsg2, .TRUE. ) ! RotorFurl + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! RotorFurl + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! RotorFurl + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! RotorFurl + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! Nacelle: size of buffers for each call to pack subtype + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Nacelle, ErrStat2, ErrMsg2, .TRUE. ) ! Nacelle + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Nacelle + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Nacelle + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Nacelle + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! TailFin: size of buffers for each call to pack subtype + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%TailFin, ErrStat2, ErrMsg2, .TRUE. ) ! TailFin + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! TailFin + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! TailFin + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! TailFin + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! Tower: size of buffers for each call to pack subtype + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Tower, ErrStat2, ErrMsg2, .TRUE. ) ! Tower + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Tower + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Tower + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Tower + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! SubStructure: size of buffers for each call to pack subtype + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%SubStructure, ErrStat2, ErrMsg2, .TRUE. ) ! SubStructure + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! SubStructure + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! SubStructure + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! SubStructure + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! Foundation: size of buffers for each call to pack subtype + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Foundation, ErrStat2, ErrMsg2, .TRUE. ) ! Foundation + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! Foundation + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! Foundation + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! Foundation + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Re_BufSz = Re_BufSz + 1 ! BladeLength + 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 + + IF ( .NOT. ALLOCATED(InData%Blade) ) 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%Blade,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Blade,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%Blade,1), UBOUND(InData%Blade,1) + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Blade(i1), ErrStat2, ErrMsg2, OnlySize ) ! Blade + 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 DO + END IF + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Hub, ErrStat2, ErrMsg2, OnlySize ) ! Hub + 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 + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%RotorFurl, ErrStat2, ErrMsg2, OnlySize ) ! RotorFurl + 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 + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Nacelle, ErrStat2, ErrMsg2, OnlySize ) ! Nacelle + 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 + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%TailFin, ErrStat2, ErrMsg2, OnlySize ) ! TailFin + 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 + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Tower, ErrStat2, ErrMsg2, OnlySize ) ! Tower + 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 + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%SubStructure, ErrStat2, ErrMsg2, OnlySize ) ! SubStructure + 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 + CALL AD14_Packmarker( Re_Buf, Db_Buf, Int_Buf, InData%Foundation, ErrStat2, ErrMsg2, OnlySize ) ! Foundation + 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 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%BladeLength + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14_PackAeroConfig + + SUBROUTINE AD14_UnPackAeroConfig( 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(AeroConfig), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + INTEGER(IntKi) :: i1, i1_l, i1_u ! bounds (upper/lower) for an array dimension 1 + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_UnPackAeroConfig' + ! 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 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Blade 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 + IF (ALLOCATED(OutData%Blade)) DEALLOCATE(OutData%Blade) + ALLOCATE(OutData%Blade(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Blade.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%Blade,1), UBOUND(OutData%Blade,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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Blade(i1), ErrStat2, ErrMsg2 ) ! Blade + 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 DO + END IF + 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Hub, ErrStat2, ErrMsg2 ) ! Hub + 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) + 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%RotorFurl, ErrStat2, ErrMsg2 ) ! RotorFurl + 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) + 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Nacelle, ErrStat2, ErrMsg2 ) ! Nacelle + 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) + 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%TailFin, ErrStat2, ErrMsg2 ) ! TailFin + 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) + 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Tower, ErrStat2, ErrMsg2 ) ! Tower + 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) + 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%SubStructure, ErrStat2, ErrMsg2 ) ! SubStructure + 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) + 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 AD14_Unpackmarker( Re_Buf, Db_Buf, Int_Buf, OutData%Foundation, ErrStat2, ErrMsg2 ) ! Foundation + 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) + OutData%BladeLength = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14_UnPackAeroConfig + + SUBROUTINE AD14_CopyAirFoil( SrcAirFoilData, DstAirFoilData, CtrlCode, ErrStat, ErrMsg ) + TYPE(AirFoil), INTENT(IN) :: SrcAirFoilData + TYPE(AirFoil), INTENT(INOUT) :: DstAirFoilData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_CopyAirFoil' +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(SrcAirFoilData%AL)) THEN + i1_l = LBOUND(SrcAirFoilData%AL,1) + i1_u = UBOUND(SrcAirFoilData%AL,1) + i2_l = LBOUND(SrcAirFoilData%AL,2) + i2_u = UBOUND(SrcAirFoilData%AL,2) + IF (.NOT. ALLOCATED(DstAirFoilData%AL)) THEN + ALLOCATE(DstAirFoilData%AL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilData%AL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstAirFoilData%AL = SrcAirFoilData%AL +ENDIF +IF (ALLOCATED(SrcAirFoilData%CD)) THEN + i1_l = LBOUND(SrcAirFoilData%CD,1) + i1_u = UBOUND(SrcAirFoilData%CD,1) + i2_l = LBOUND(SrcAirFoilData%CD,2) + i2_u = UBOUND(SrcAirFoilData%CD,2) + i3_l = LBOUND(SrcAirFoilData%CD,3) + i3_u = UBOUND(SrcAirFoilData%CD,3) + IF (.NOT. ALLOCATED(DstAirFoilData%CD)) THEN + ALLOCATE(DstAirFoilData%CD(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilData%CD.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstAirFoilData%CD = SrcAirFoilData%CD +ENDIF +IF (ALLOCATED(SrcAirFoilData%CL)) THEN + i1_l = LBOUND(SrcAirFoilData%CL,1) + i1_u = UBOUND(SrcAirFoilData%CL,1) + i2_l = LBOUND(SrcAirFoilData%CL,2) + i2_u = UBOUND(SrcAirFoilData%CL,2) + i3_l = LBOUND(SrcAirFoilData%CL,3) + i3_u = UBOUND(SrcAirFoilData%CL,3) + IF (.NOT. ALLOCATED(DstAirFoilData%CL)) THEN + ALLOCATE(DstAirFoilData%CL(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilData%CL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstAirFoilData%CL = SrcAirFoilData%CL +ENDIF +IF (ALLOCATED(SrcAirFoilData%CM)) THEN + i1_l = LBOUND(SrcAirFoilData%CM,1) + i1_u = UBOUND(SrcAirFoilData%CM,1) + i2_l = LBOUND(SrcAirFoilData%CM,2) + i2_u = UBOUND(SrcAirFoilData%CM,2) + i3_l = LBOUND(SrcAirFoilData%CM,3) + i3_u = UBOUND(SrcAirFoilData%CM,3) + IF (.NOT. ALLOCATED(DstAirFoilData%CM)) THEN + ALLOCATE(DstAirFoilData%CM(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilData%CM.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstAirFoilData%CM = SrcAirFoilData%CM +ENDIF + DstAirFoilData%PMC = SrcAirFoilData%PMC + DstAirFoilData%MulTabLoc = SrcAirFoilData%MulTabLoc + END SUBROUTINE AD14_CopyAirFoil + + SUBROUTINE AD14_DestroyAirFoil( AirFoilData, ErrStat, ErrMsg ) + TYPE(AirFoil), INTENT(INOUT) :: AirFoilData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_DestroyAirFoil' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(AirFoilData%AL)) THEN + DEALLOCATE(AirFoilData%AL) +ENDIF +IF (ALLOCATED(AirFoilData%CD)) THEN + DEALLOCATE(AirFoilData%CD) +ENDIF +IF (ALLOCATED(AirFoilData%CL)) THEN + DEALLOCATE(AirFoilData%CL) +ENDIF +IF (ALLOCATED(AirFoilData%CM)) THEN + DEALLOCATE(AirFoilData%CM) +ENDIF + END SUBROUTINE AD14_DestroyAirFoil + + SUBROUTINE AD14_PackAirFoil( 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(AirFoil), 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 = 'AD14_PackAirFoil' + ! 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 ! AL allocated yes/no + IF ( ALLOCATED(InData%AL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! AL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%AL) ! AL + END IF + Int_BufSz = Int_BufSz + 1 ! CD allocated yes/no + IF ( ALLOCATED(InData%CD) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! CD upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%CD) ! CD + END IF + Int_BufSz = Int_BufSz + 1 ! CL allocated yes/no + IF ( ALLOCATED(InData%CL) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! CL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%CL) ! CL + END IF + Int_BufSz = Int_BufSz + 1 ! CM allocated yes/no + IF ( ALLOCATED(InData%CM) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! CM upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%CM) ! CM + END IF + Re_BufSz = Re_BufSz + 1 ! PMC + Re_BufSz = Re_BufSz + 1 ! MulTabLoc + 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 + + IF ( .NOT. ALLOCATED(InData%AL) ) 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%AL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%AL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%AL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%AL))-1 ) = PACK(InData%AL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%AL) + END IF + IF ( .NOT. ALLOCATED(InData%CD) ) 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%CD,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CD,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CD,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CD,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CD,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CD,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%CD)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CD))-1 ) = PACK(InData%CD,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%CD) + END IF + IF ( .NOT. ALLOCATED(InData%CL) ) 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%CL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CL,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CL,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CL,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%CL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CL))-1 ) = PACK(InData%CL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%CL) + END IF + IF ( .NOT. ALLOCATED(InData%CM) ) 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%CM,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CM,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CM,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CM,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CM,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CM,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%CM)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CM))-1 ) = PACK(InData%CM,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%CM) + END IF + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%PMC + Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%MulTabLoc + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14_PackAirFoil + + SUBROUTINE AD14_UnPackAirFoil( 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(AirFoil), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_UnPackAirFoil' + ! 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 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! AL 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 + IF (ALLOCATED(OutData%AL)) DEALLOCATE(OutData%AL) + ALLOCATE(OutData%AL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%AL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%AL)>0) OutData%AL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%AL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%AL) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CD 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%CD)) DEALLOCATE(OutData%CD) + ALLOCATE(OutData%CD(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%CD.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%CD)>0) OutData%CD = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CD))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%CD) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CL 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%CL)) DEALLOCATE(OutData%CL) + ALLOCATE(OutData%CL(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%CL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%CL)>0) OutData%CL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CL))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%CL) + DEALLOCATE(mask3) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CM 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%CM)) DEALLOCATE(OutData%CM) + ALLOCATE(OutData%CM(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%CM.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%CM)>0) OutData%CM = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CM))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%CM) + DEALLOCATE(mask3) + END IF + OutData%PMC = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%MulTabLoc = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + END SUBROUTINE AD14_UnPackAirFoil + + SUBROUTINE AD14_CopyAirFoilParms( SrcAirFoilParmsData, DstAirFoilParmsData, CtrlCode, ErrStat, ErrMsg ) + TYPE(AirFoilParms), INTENT(IN) :: SrcAirFoilParmsData + TYPE(AirFoilParms), INTENT(INOUT) :: DstAirFoilParmsData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_CopyAirFoilParms' +! + ErrStat = ErrID_None + ErrMsg = "" + DstAirFoilParmsData%MaxTable = SrcAirFoilParmsData%MaxTable +IF (ALLOCATED(SrcAirFoilParmsData%NTables)) THEN + i1_l = LBOUND(SrcAirFoilParmsData%NTables,1) + i1_u = UBOUND(SrcAirFoilParmsData%NTables,1) + IF (.NOT. ALLOCATED(DstAirFoilParmsData%NTables)) THEN + ALLOCATE(DstAirFoilParmsData%NTables(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%NTables.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstAirFoilParmsData%NTables = SrcAirFoilParmsData%NTables +ENDIF +IF (ALLOCATED(SrcAirFoilParmsData%NLift)) THEN + i1_l = LBOUND(SrcAirFoilParmsData%NLift,1) + i1_u = UBOUND(SrcAirFoilParmsData%NLift,1) + IF (.NOT. ALLOCATED(DstAirFoilParmsData%NLift)) THEN + ALLOCATE(DstAirFoilParmsData%NLift(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%NLift.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstAirFoilParmsData%NLift = SrcAirFoilParmsData%NLift +ENDIF + DstAirFoilParmsData%NumCL = SrcAirFoilParmsData%NumCL + DstAirFoilParmsData%NumFoil = SrcAirFoilParmsData%NumFoil +IF (ALLOCATED(SrcAirFoilParmsData%NFoil)) THEN + i1_l = LBOUND(SrcAirFoilParmsData%NFoil,1) + i1_u = UBOUND(SrcAirFoilParmsData%NFoil,1) + IF (.NOT. ALLOCATED(DstAirFoilParmsData%NFoil)) THEN + ALLOCATE(DstAirFoilParmsData%NFoil(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%NFoil.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstAirFoilParmsData%NFoil = SrcAirFoilParmsData%NFoil +ENDIF +IF (ALLOCATED(SrcAirFoilParmsData%MulTabMet)) THEN + i1_l = LBOUND(SrcAirFoilParmsData%MulTabMet,1) + i1_u = UBOUND(SrcAirFoilParmsData%MulTabMet,1) + i2_l = LBOUND(SrcAirFoilParmsData%MulTabMet,2) + i2_u = UBOUND(SrcAirFoilParmsData%MulTabMet,2) + IF (.NOT. ALLOCATED(DstAirFoilParmsData%MulTabMet)) THEN + ALLOCATE(DstAirFoilParmsData%MulTabMet(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%MulTabMet.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstAirFoilParmsData%MulTabMet = SrcAirFoilParmsData%MulTabMet +ENDIF +IF (ALLOCATED(SrcAirFoilParmsData%FoilNm)) THEN + i1_l = LBOUND(SrcAirFoilParmsData%FoilNm,1) + i1_u = UBOUND(SrcAirFoilParmsData%FoilNm,1) + IF (.NOT. ALLOCATED(DstAirFoilParmsData%FoilNm)) THEN + ALLOCATE(DstAirFoilParmsData%FoilNm(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstAirFoilParmsData%FoilNm.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstAirFoilParmsData%FoilNm = SrcAirFoilParmsData%FoilNm +ENDIF + END SUBROUTINE AD14_CopyAirFoilParms + + SUBROUTINE AD14_DestroyAirFoilParms( AirFoilParmsData, ErrStat, ErrMsg ) + TYPE(AirFoilParms), INTENT(INOUT) :: AirFoilParmsData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_DestroyAirFoilParms' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" +IF (ALLOCATED(AirFoilParmsData%NTables)) THEN + DEALLOCATE(AirFoilParmsData%NTables) +ENDIF +IF (ALLOCATED(AirFoilParmsData%NLift)) THEN + DEALLOCATE(AirFoilParmsData%NLift) +ENDIF +IF (ALLOCATED(AirFoilParmsData%NFoil)) THEN + DEALLOCATE(AirFoilParmsData%NFoil) +ENDIF +IF (ALLOCATED(AirFoilParmsData%MulTabMet)) THEN + DEALLOCATE(AirFoilParmsData%MulTabMet) +ENDIF +IF (ALLOCATED(AirFoilParmsData%FoilNm)) THEN + DEALLOCATE(AirFoilParmsData%FoilNm) +ENDIF + END SUBROUTINE AD14_DestroyAirFoilParms + + SUBROUTINE AD14_PackAirFoilParms( 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(AirFoilParms), 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 = 'AD14_PackAirFoilParms' + ! 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 ! MaxTable + Int_BufSz = Int_BufSz + 1 ! NTables allocated yes/no + IF ( ALLOCATED(InData%NTables) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! NTables upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%NTables) ! NTables + END IF + Int_BufSz = Int_BufSz + 1 ! NLift allocated yes/no + IF ( ALLOCATED(InData%NLift) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! NLift upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%NLift) ! NLift + END IF + Int_BufSz = Int_BufSz + 1 ! NumCL + Int_BufSz = Int_BufSz + 1 ! NumFoil + Int_BufSz = Int_BufSz + 1 ! NFoil allocated yes/no + IF ( ALLOCATED(InData%NFoil) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! NFoil upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%NFoil) ! NFoil + END IF + Int_BufSz = Int_BufSz + 1 ! MulTabMet allocated yes/no + IF ( ALLOCATED(InData%MulTabMet) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! MulTabMet upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%MulTabMet) ! MulTabMet + END IF + Int_BufSz = Int_BufSz + 1 ! FoilNm allocated yes/no + IF ( ALLOCATED(InData%FoilNm) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! FoilNm upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%FoilNm)*LEN(InData%FoilNm) ! FoilNm + END IF + 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:Int_Xferred+(1)-1 ) = InData%MaxTable + Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%NTables) ) 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%NTables,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%NTables,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%NTables)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%NTables))-1 ) = PACK(InData%NTables,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(InData%NTables) + END IF + IF ( .NOT. ALLOCATED(InData%NLift) ) 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%NLift,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%NLift,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%NLift)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%NLift))-1 ) = PACK(InData%NLift,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(InData%NLift) + END IF + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumCL + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NumFoil + Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%NFoil) ) 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%NFoil,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%NFoil,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%NFoil)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%NFoil))-1 ) = PACK(InData%NFoil,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(InData%NFoil) + END IF + IF ( .NOT. ALLOCATED(InData%MulTabMet) ) 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%MulTabMet,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%MulTabMet,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%MulTabMet,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%MulTabMet,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%MulTabMet)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%MulTabMet))-1 ) = PACK(InData%MulTabMet,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%MulTabMet) + END IF + IF ( .NOT. ALLOCATED(InData%FoilNm) ) 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%FoilNm,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%FoilNm,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%FoilNm,1), UBOUND(InData%FoilNm,1) + DO I = 1, LEN(InData%FoilNm) + IntKiBuf(Int_Xferred) = ICHAR(InData%FoilNm(i1)(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I + END DO !i1 + END IF + END SUBROUTINE AD14_PackAirFoilParms + + SUBROUTINE AD14_UnPackAirFoilParms( 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(AirFoilParms), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AD14_UnPackAirFoilParms' + ! 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%MaxTable = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! NTables 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 + IF (ALLOCATED(OutData%NTables)) DEALLOCATE(OutData%NTables) + ALLOCATE(OutData%NTables(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%NTables.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%NTables)>0) OutData%NTables = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%NTables))-1 ), mask1, 0_IntKi ) + Int_Xferred = Int_Xferred + SIZE(OutData%NTables) + DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! NLift 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 + IF (ALLOCATED(OutData%NLift)) DEALLOCATE(OutData%NLift) + ALLOCATE(OutData%NLift(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%NLift.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%NLift)>0) OutData%NLift = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%NLift))-1 ), mask1, 0_IntKi ) + Int_Xferred = Int_Xferred + SIZE(OutData%NLift) + DEALLOCATE(mask1) + END IF + OutData%NumCL = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%NumFoil = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! NFoil 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 + IF (ALLOCATED(OutData%NFoil)) DEALLOCATE(OutData%NFoil) + ALLOCATE(OutData%NFoil(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%NFoil.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%NFoil)>0) OutData%NFoil = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%NFoil))-1 ), mask1, 0_IntKi ) + Int_Xferred = Int_Xferred + SIZE(OutData%NFoil) + DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! MulTabMet 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 + IF (ALLOCATED(OutData%MulTabMet)) DEALLOCATE(OutData%MulTabMet) + ALLOCATE(OutData%MulTabMet(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%MulTabMet.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%MulTabMet)>0) OutData%MulTabMet = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%MulTabMet))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%MulTabMet) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FoilNm 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 + IF (ALLOCATED(OutData%FoilNm)) DEALLOCATE(OutData%FoilNm) + ALLOCATE(OutData%FoilNm(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%FoilNm.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + DO i1 = LBOUND(OutData%FoilNm,1), UBOUND(OutData%FoilNm,1) + DO I = 1, LEN(OutData%FoilNm) + OutData%FoilNm(i1)(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I + END DO !i1 + DEALLOCATE(mask1) + END IF + END SUBROUTINE AD14_UnPackAirFoilParms + SUBROUTINE AD14_CopyBeddoes( SrcBeddoesData, DstBeddoesData, CtrlCode, ErrStat, ErrMsg ) TYPE(Beddoes), INTENT(IN) :: SrcBeddoesData TYPE(Beddoes), INTENT(INOUT) :: DstBeddoesData @@ -9254,7 +11253,7 @@ SUBROUTINE AD14_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, Err DstInitInputData%BladeLength = SrcInitInputData%BladeLength DstInitInputData%LinearizeFlag = SrcInitInputData%LinearizeFlag DstInitInputData%UseDWM = SrcInitInputData%UseDWM - CALL AD14AeroConf_CopyInput( SrcInitInputData%TurbineComponents, DstInitInputData%TurbineComponents, CtrlCode, ErrStat2, ErrMsg2 ) + CALL AD14_Copyaeroconfig( SrcInitInputData%TurbineComponents, DstInitInputData%TurbineComponents, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN DstInitInputData%NumTwrNodes = SrcInitInputData%NumTwrNodes @@ -9287,7 +11286,7 @@ SUBROUTINE AD14_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" - CALL AD14AeroConf_DestroyInput( InitInputData%TurbineComponents, ErrStat, ErrMsg ) + CALL AD14_Destroyaeroconfig( InitInputData%TurbineComponents, ErrStat, ErrMsg ) IF (ALLOCATED(InitInputData%TwrNodeLocs)) THEN DEALLOCATE(InitInputData%TwrNodeLocs) ENDIF @@ -9339,7 +11338,7 @@ SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + 1 ! UseDWM ! Allocate buffers for subtypes, if any (we'll get sizes from these) Int_BufSz = Int_BufSz + 3 ! TurbineComponents: size of buffers for each call to pack subtype - CALL AD14AeroConf_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, .TRUE. ) ! TurbineComponents + CALL AD14_Packaeroconfig( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, .TRUE. ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -9428,7 +11427,7 @@ SUBROUTINE AD14_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UseDWM , IntKiBuf(1), 1) Int_Xferred = Int_Xferred + 1 - CALL AD14AeroConf_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, OnlySize ) ! TurbineComponents + CALL AD14_Packaeroconfig( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, OnlySize ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -9595,7 +11594,7 @@ SUBROUTINE AD14_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL AD14AeroConf_UnpackInput( Re_Buf, Db_Buf, Int_Buf, OutData%TurbineComponents, ErrStat2, ErrMsg2 ) ! TurbineComponents + CALL AD14_Unpackaeroconfig( Re_Buf, Db_Buf, Int_Buf, OutData%TurbineComponents, ErrStat2, ErrMsg2 ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -10895,7 +12894,7 @@ SUBROUTINE AD14_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) DstMiscData%OnePassDynDbg = SrcMiscData%OnePassDynDbg DstMiscData%NoLoadsCalculated = SrcMiscData%NoLoadsCalculated DstMiscData%NERRORS = SrcMiscData%NERRORS - CALL AD14AeroConf_CopyMisc( SrcMiscData%AirFoil, DstMiscData%AirFoil, CtrlCode, ErrStat2, ErrMsg2 ) + CALL AD14_Copyairfoil( SrcMiscData%AirFoil, DstMiscData%AirFoil, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN CALL AD14_Copybeddoes( SrcMiscData%Beddoes, DstMiscData%Beddoes, CtrlCode, ErrStat2, ErrMsg2 ) @@ -10971,7 +12970,7 @@ SUBROUTINE AD14_DestroyMisc( MiscData, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%ElPrNum)) THEN DEALLOCATE(MiscData%ElPrNum) ENDIF - CALL AD14AeroConf_DestroyMisc( MiscData%AirFoil, ErrStat, ErrMsg ) + CALL AD14_Destroyairfoil( MiscData%AirFoil, ErrStat, ErrMsg ) CALL AD14_Destroybeddoes( MiscData%Beddoes, ErrStat, ErrMsg ) CALL AD14_Destroydyninflow( MiscData%DynInflow, ErrStat, ErrMsg ) CALL AD14_Destroyelement( MiscData%Element, ErrStat, ErrMsg ) @@ -11093,7 +13092,7 @@ SUBROUTINE AD14_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! NoLoadsCalculated Int_BufSz = Int_BufSz + 1 ! NERRORS Int_BufSz = Int_BufSz + 3 ! AirFoil: size of buffers for each call to pack subtype - CALL AD14AeroConf_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, .TRUE. ) ! AirFoil + CALL AD14_Packairfoil( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, .TRUE. ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -11391,7 +13390,7 @@ SUBROUTINE AD14_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NERRORS Int_Xferred = Int_Xferred + 1 - CALL AD14AeroConf_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, OnlySize ) ! AirFoil + CALL AD14_Packairfoil( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, OnlySize ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -11898,7 +13897,7 @@ SUBROUTINE AD14_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL AD14AeroConf_UnpackMisc( Re_Buf, Db_Buf, Int_Buf, OutData%AirFoil, ErrStat2, ErrMsg2 ) ! AirFoil + CALL AD14_Unpackairfoil( Re_Buf, Db_Buf, Int_Buf, OutData%AirFoil, ErrStat2, ErrMsg2 ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -12290,7 +14289,7 @@ SUBROUTINE AD14_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%MAXICOUNT = SrcParamData%MAXICOUNT DstParamData%WrOptFile = SrcParamData%WrOptFile DstParamData%DEFAULT_Wind = SrcParamData%DEFAULT_Wind - CALL AD14AeroConf_CopyParam( SrcParamData%AirFoil, DstParamData%AirFoil, CtrlCode, ErrStat2, ErrMsg2 ) + CALL AD14_Copyairfoilparms( SrcParamData%AirFoil, DstParamData%AirFoil, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN CALL AD14_Copybladeparms( SrcParamData%Blade, DstParamData%Blade, CtrlCode, ErrStat2, ErrMsg2 ) @@ -12320,7 +14319,6 @@ SUBROUTINE AD14_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg CALL DWM_CopyParam( SrcParamData%DWM, DstParamData%DWM, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN - DstParamData%IfW_DT = SrcParamData%IfW_DT END SUBROUTINE AD14_CopyParam SUBROUTINE AD14_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -12332,7 +14330,7 @@ SUBROUTINE AD14_DestroyParam( ParamData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" - CALL AD14AeroConf_DestroyParam( ParamData%AirFoil, ErrStat, ErrMsg ) + CALL AD14_Destroyairfoilparms( ParamData%AirFoil, ErrStat, ErrMsg ) CALL AD14_Destroybladeparms( ParamData%Blade, ErrStat, ErrMsg ) CALL AD14_Destroybeddoesparms( ParamData%Beddoes, ErrStat, ErrMsg ) CALL AD14_Destroydyninflowparms( ParamData%DynInflow, ErrStat, ErrMsg ) @@ -12406,7 +14404,7 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + 1 ! DEFAULT_Wind ! Allocate buffers for subtypes, if any (we'll get sizes from these) Int_BufSz = Int_BufSz + 3 ! AirFoil: size of buffers for each call to pack subtype - CALL AD14AeroConf_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, .TRUE. ) ! AirFoil + CALL AD14_Packairfoilparms( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, .TRUE. ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -12575,7 +14573,6 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF - Db_BufSz = Db_BufSz + 1 ! IfW_DT IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -12655,7 +14652,7 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%DEFAULT_Wind Int_Xferred = Int_Xferred + 1 - CALL AD14AeroConf_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, OnlySize ) ! AirFoil + CALL AD14_Packairfoilparms( Re_Buf, Db_Buf, Int_Buf, InData%AirFoil, ErrStat2, ErrMsg2, OnlySize ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -12935,8 +14932,6 @@ SUBROUTINE AD14_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%IfW_DT - Db_Xferred = Db_Xferred + 1 END SUBROUTINE AD14_PackParam SUBROUTINE AD14_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -13056,7 +15051,7 @@ SUBROUTINE AD14_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL AD14AeroConf_UnpackParam( Re_Buf, Db_Buf, Int_Buf, OutData%AirFoil, ErrStat2, ErrMsg2 ) ! AirFoil + CALL AD14_Unpackairfoilparms( Re_Buf, Db_Buf, Int_Buf, OutData%AirFoil, ErrStat2, ErrMsg2 ) ! AirFoil CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -13423,8 +15418,6 @@ SUBROUTINE AD14_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs IF(ALLOCATED(Re_Buf )) DEALLOCATE(Re_Buf ) IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) - OutData%IfW_DT = DbKiBuf( Db_Xferred ) - Db_Xferred = Db_Xferred + 1 END SUBROUTINE AD14_UnPackParam SUBROUTINE AD14_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) @@ -13462,7 +15455,7 @@ SUBROUTINE AD14_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg CALL MeshCopy( SrcInputData%Twr_InputMarkers, DstInputData%Twr_InputMarkers, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat>=AbortErrLev) RETURN - CALL AD14AeroConf_CopyInput( SrcInputData%TurbineComponents, DstInputData%TurbineComponents, CtrlCode, ErrStat2, ErrMsg2 ) + CALL AD14_Copyaeroconfig( SrcInputData%TurbineComponents, DstInputData%TurbineComponents, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN IF (ALLOCATED(SrcInputData%MulTabLoc)) THEN @@ -13512,7 +15505,7 @@ SUBROUTINE AD14_DestroyInput( InputData, ErrStat, ErrMsg ) DEALLOCATE(InputData%InputMarkers) ENDIF CALL MeshDestroy( InputData%Twr_InputMarkers, ErrStat, ErrMsg ) - CALL AD14AeroConf_DestroyInput( InputData%TurbineComponents, ErrStat, ErrMsg ) + CALL AD14_Destroyaeroconfig( InputData%TurbineComponents, ErrStat, ErrMsg ) IF (ALLOCATED(InputData%MulTabLoc)) THEN DEALLOCATE(InputData%MulTabLoc) ENDIF @@ -13598,7 +15591,7 @@ SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, DEALLOCATE(Int_Buf) END IF Int_BufSz = Int_BufSz + 3 ! TurbineComponents: size of buffers for each call to pack subtype - CALL AD14AeroConf_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, .TRUE. ) ! TurbineComponents + CALL AD14_Packaeroconfig( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, .TRUE. ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -13721,7 +15714,7 @@ SUBROUTINE AD14_PackInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF - CALL AD14AeroConf_PackInput( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, OnlySize ) ! TurbineComponents + CALL AD14_Packaeroconfig( Re_Buf, Db_Buf, Int_Buf, InData%TurbineComponents, ErrStat2, ErrMsg2, OnlySize ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -13948,7 +15941,7 @@ SUBROUTINE AD14_UnPackInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs Int_Buf = IntKiBuf( Int_Xferred:Int_Xferred+Buf_size-1 ) Int_Xferred = Int_Xferred + Buf_size END IF - CALL AD14AeroConf_UnpackInput( Re_Buf, Db_Buf, Int_Buf, OutData%TurbineComponents, ErrStat2, ErrMsg2 ) ! TurbineComponents + CALL AD14_Unpackaeroconfig( Re_Buf, Db_Buf, Int_Buf, OutData%TurbineComponents, ErrStat2, ErrMsg2 ) ! TurbineComponents CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -14463,6 +16456,7 @@ SUBROUTINE AD14_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg INTEGER(IntKi) :: ErrStat2 ! local errors CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + INTEGER :: i11 ! dim1 level 1 counter variable for arrays of ddts ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -14483,8 +16477,210 @@ SUBROUTINE AD14_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg END IF ! check if allocated CALL MeshExtrapInterp1(u1%Twr_InputMarkers, u2%Twr_InputMarkers, tin, u_out%Twr_InputMarkers, tin_out, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - CALL AD14AeroConf_Input_ExtrapInterp1( u1%TurbineComponents, u2%TurbineComponents, tin, u_out%TurbineComponents, tin_out, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) +IF (ALLOCATED(u_out%TurbineComponents%Blade) .AND. ALLOCATED(u1%TurbineComponents%Blade)) THEN + DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%Position,1))) + b1 = -(u1%TurbineComponents%Blade(i11)%Position - u2%TurbineComponents%Blade(i11)%Position)/t(2) + u_out%TurbineComponents%Blade(i11)%Position = u1%TurbineComponents%Blade(i11)%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO + DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,1),SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,1),SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,2) )) + b2 = -(u1%TurbineComponents%Blade(i11)%Orientation - u2%TurbineComponents%Blade(i11)%Orientation)/t(2) + u_out%TurbineComponents%Blade(i11)%Orientation = u1%TurbineComponents%Blade(i11)%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ENDDO + DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%TranslationVel,1))) + b1 = -(u1%TurbineComponents%Blade(i11)%TranslationVel - u2%TurbineComponents%Blade(i11)%TranslationVel)/t(2) + u_out%TurbineComponents%Blade(i11)%TranslationVel = u1%TurbineComponents%Blade(i11)%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO + DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%RotationVel,1))) + b1 = -(u1%TurbineComponents%Blade(i11)%RotationVel - u2%TurbineComponents%Blade(i11)%RotationVel)/t(2) + u_out%TurbineComponents%Blade(i11)%RotationVel = u1%TurbineComponents%Blade(i11)%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO +END IF ! check if allocated + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%Position,1))) + b1 = -(u1%TurbineComponents%Hub%Position - u2%TurbineComponents%Hub%Position)/t(2) + u_out%TurbineComponents%Hub%Position = u1%TurbineComponents%Hub%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Hub%Orientation,1),SIZE(u_out%TurbineComponents%Hub%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Hub%Orientation,1),SIZE(u_out%TurbineComponents%Hub%Orientation,2) )) + b2 = -(u1%TurbineComponents%Hub%Orientation - u2%TurbineComponents%Hub%Orientation)/t(2) + u_out%TurbineComponents%Hub%Orientation = u1%TurbineComponents%Hub%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%TranslationVel,1))) + b1 = -(u1%TurbineComponents%Hub%TranslationVel - u2%TurbineComponents%Hub%TranslationVel)/t(2) + u_out%TurbineComponents%Hub%TranslationVel = u1%TurbineComponents%Hub%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%RotationVel,1))) + b1 = -(u1%TurbineComponents%Hub%RotationVel - u2%TurbineComponents%Hub%RotationVel)/t(2) + u_out%TurbineComponents%Hub%RotationVel = u1%TurbineComponents%Hub%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%Position,1))) + b1 = -(u1%TurbineComponents%RotorFurl%Position - u2%TurbineComponents%RotorFurl%Position)/t(2) + u_out%TurbineComponents%RotorFurl%Position = u1%TurbineComponents%RotorFurl%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%RotorFurl%Orientation,1),SIZE(u_out%TurbineComponents%RotorFurl%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%RotorFurl%Orientation,1),SIZE(u_out%TurbineComponents%RotorFurl%Orientation,2) )) + b2 = -(u1%TurbineComponents%RotorFurl%Orientation - u2%TurbineComponents%RotorFurl%Orientation)/t(2) + u_out%TurbineComponents%RotorFurl%Orientation = u1%TurbineComponents%RotorFurl%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%TranslationVel,1))) + b1 = -(u1%TurbineComponents%RotorFurl%TranslationVel - u2%TurbineComponents%RotorFurl%TranslationVel)/t(2) + u_out%TurbineComponents%RotorFurl%TranslationVel = u1%TurbineComponents%RotorFurl%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%RotationVel,1))) + b1 = -(u1%TurbineComponents%RotorFurl%RotationVel - u2%TurbineComponents%RotorFurl%RotationVel)/t(2) + u_out%TurbineComponents%RotorFurl%RotationVel = u1%TurbineComponents%RotorFurl%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%Position,1))) + b1 = -(u1%TurbineComponents%Nacelle%Position - u2%TurbineComponents%Nacelle%Position)/t(2) + u_out%TurbineComponents%Nacelle%Position = u1%TurbineComponents%Nacelle%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Nacelle%Orientation,1),SIZE(u_out%TurbineComponents%Nacelle%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Nacelle%Orientation,1),SIZE(u_out%TurbineComponents%Nacelle%Orientation,2) )) + b2 = -(u1%TurbineComponents%Nacelle%Orientation - u2%TurbineComponents%Nacelle%Orientation)/t(2) + u_out%TurbineComponents%Nacelle%Orientation = u1%TurbineComponents%Nacelle%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%TranslationVel,1))) + b1 = -(u1%TurbineComponents%Nacelle%TranslationVel - u2%TurbineComponents%Nacelle%TranslationVel)/t(2) + u_out%TurbineComponents%Nacelle%TranslationVel = u1%TurbineComponents%Nacelle%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%RotationVel,1))) + b1 = -(u1%TurbineComponents%Nacelle%RotationVel - u2%TurbineComponents%Nacelle%RotationVel)/t(2) + u_out%TurbineComponents%Nacelle%RotationVel = u1%TurbineComponents%Nacelle%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%Position,1))) + b1 = -(u1%TurbineComponents%TailFin%Position - u2%TurbineComponents%TailFin%Position)/t(2) + u_out%TurbineComponents%TailFin%Position = u1%TurbineComponents%TailFin%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%TailFin%Orientation,1),SIZE(u_out%TurbineComponents%TailFin%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%TailFin%Orientation,1),SIZE(u_out%TurbineComponents%TailFin%Orientation,2) )) + b2 = -(u1%TurbineComponents%TailFin%Orientation - u2%TurbineComponents%TailFin%Orientation)/t(2) + u_out%TurbineComponents%TailFin%Orientation = u1%TurbineComponents%TailFin%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%TranslationVel,1))) + b1 = -(u1%TurbineComponents%TailFin%TranslationVel - u2%TurbineComponents%TailFin%TranslationVel)/t(2) + u_out%TurbineComponents%TailFin%TranslationVel = u1%TurbineComponents%TailFin%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%RotationVel,1))) + b1 = -(u1%TurbineComponents%TailFin%RotationVel - u2%TurbineComponents%TailFin%RotationVel)/t(2) + u_out%TurbineComponents%TailFin%RotationVel = u1%TurbineComponents%TailFin%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%Position,1))) + b1 = -(u1%TurbineComponents%Tower%Position - u2%TurbineComponents%Tower%Position)/t(2) + u_out%TurbineComponents%Tower%Position = u1%TurbineComponents%Tower%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Tower%Orientation,1),SIZE(u_out%TurbineComponents%Tower%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Tower%Orientation,1),SIZE(u_out%TurbineComponents%Tower%Orientation,2) )) + b2 = -(u1%TurbineComponents%Tower%Orientation - u2%TurbineComponents%Tower%Orientation)/t(2) + u_out%TurbineComponents%Tower%Orientation = u1%TurbineComponents%Tower%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%TranslationVel,1))) + b1 = -(u1%TurbineComponents%Tower%TranslationVel - u2%TurbineComponents%Tower%TranslationVel)/t(2) + u_out%TurbineComponents%Tower%TranslationVel = u1%TurbineComponents%Tower%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%RotationVel,1))) + b1 = -(u1%TurbineComponents%Tower%RotationVel - u2%TurbineComponents%Tower%RotationVel)/t(2) + u_out%TurbineComponents%Tower%RotationVel = u1%TurbineComponents%Tower%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%Position,1))) + b1 = -(u1%TurbineComponents%SubStructure%Position - u2%TurbineComponents%SubStructure%Position)/t(2) + u_out%TurbineComponents%SubStructure%Position = u1%TurbineComponents%SubStructure%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%SubStructure%Orientation,1),SIZE(u_out%TurbineComponents%SubStructure%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%SubStructure%Orientation,1),SIZE(u_out%TurbineComponents%SubStructure%Orientation,2) )) + b2 = -(u1%TurbineComponents%SubStructure%Orientation - u2%TurbineComponents%SubStructure%Orientation)/t(2) + u_out%TurbineComponents%SubStructure%Orientation = u1%TurbineComponents%SubStructure%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%TranslationVel,1))) + b1 = -(u1%TurbineComponents%SubStructure%TranslationVel - u2%TurbineComponents%SubStructure%TranslationVel)/t(2) + u_out%TurbineComponents%SubStructure%TranslationVel = u1%TurbineComponents%SubStructure%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%RotationVel,1))) + b1 = -(u1%TurbineComponents%SubStructure%RotationVel - u2%TurbineComponents%SubStructure%RotationVel)/t(2) + u_out%TurbineComponents%SubStructure%RotationVel = u1%TurbineComponents%SubStructure%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%Position,1))) + b1 = -(u1%TurbineComponents%Foundation%Position - u2%TurbineComponents%Foundation%Position)/t(2) + u_out%TurbineComponents%Foundation%Position = u1%TurbineComponents%Foundation%Position + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Foundation%Orientation,1),SIZE(u_out%TurbineComponents%Foundation%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Foundation%Orientation,1),SIZE(u_out%TurbineComponents%Foundation%Orientation,2) )) + b2 = -(u1%TurbineComponents%Foundation%Orientation - u2%TurbineComponents%Foundation%Orientation)/t(2) + u_out%TurbineComponents%Foundation%Orientation = u1%TurbineComponents%Foundation%Orientation + b2 * t_out + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%TranslationVel,1))) + b1 = -(u1%TurbineComponents%Foundation%TranslationVel - u2%TurbineComponents%Foundation%TranslationVel)/t(2) + u_out%TurbineComponents%Foundation%TranslationVel = u1%TurbineComponents%Foundation%TranslationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%RotationVel,1))) + b1 = -(u1%TurbineComponents%Foundation%RotationVel - u2%TurbineComponents%Foundation%RotationVel)/t(2) + u_out%TurbineComponents%Foundation%RotationVel = u1%TurbineComponents%Foundation%RotationVel + b1 * t_out + DEALLOCATE(b1) + DEALLOCATE(c1) + b0 = -(u1%TurbineComponents%BladeLength - u2%TurbineComponents%BladeLength)/t(2) + u_out%TurbineComponents%BladeLength = u1%TurbineComponents%BladeLength + b0 * t_out IF (ALLOCATED(u_out%MulTabLoc) .AND. ALLOCATED(u1%MulTabLoc)) THEN ALLOCATE(b2(SIZE(u_out%MulTabLoc,1),SIZE(u_out%MulTabLoc,2) )) ALLOCATE(c2(SIZE(u_out%MulTabLoc,1),SIZE(u_out%MulTabLoc,2) )) @@ -14546,6 +16742,7 @@ SUBROUTINE AD14_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, Er CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors CHARACTER(*), PARAMETER :: RoutineName = 'AD14_Input_ExtrapInterp2' INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + INTEGER :: i11 ! dim1 level 1 counter variable for arrays of ddts ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -14572,8 +16769,243 @@ SUBROUTINE AD14_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, Er END IF ! check if allocated CALL MeshExtrapInterp2(u1%Twr_InputMarkers, u2%Twr_InputMarkers, u3%Twr_InputMarkers, tin, u_out%Twr_InputMarkers, tin_out, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - CALL AD14AeroConf_Input_ExtrapInterp2( u1%TurbineComponents, u2%TurbineComponents, u3%TurbineComponents, tin, u_out%TurbineComponents, tin_out, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) +IF (ALLOCATED(u_out%TurbineComponents%Blade) .AND. ALLOCATED(u1%TurbineComponents%Blade)) THEN + DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%Position,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Blade(i11)%Position - u2%TurbineComponents%Blade(i11)%Position) + t(2)**2*(-u1%TurbineComponents%Blade(i11)%Position + u3%TurbineComponents%Blade(i11)%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Blade(i11)%Position + t(3)*u2%TurbineComponents%Blade(i11)%Position - t(2)*u3%TurbineComponents%Blade(i11)%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Blade(i11)%Position = u1%TurbineComponents%Blade(i11)%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO + DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,1),SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,1),SIZE(u_out%TurbineComponents%Blade(i11)%Orientation,2) )) + b2 = (t(3)**2*(u1%TurbineComponents%Blade(i11)%Orientation - u2%TurbineComponents%Blade(i11)%Orientation) + t(2)**2*(-u1%TurbineComponents%Blade(i11)%Orientation + u3%TurbineComponents%Blade(i11)%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%TurbineComponents%Blade(i11)%Orientation + t(3)*u2%TurbineComponents%Blade(i11)%Orientation - t(2)*u3%TurbineComponents%Blade(i11)%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Blade(i11)%Orientation = u1%TurbineComponents%Blade(i11)%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ENDDO + DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%TranslationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Blade(i11)%TranslationVel - u2%TurbineComponents%Blade(i11)%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Blade(i11)%TranslationVel + u3%TurbineComponents%Blade(i11)%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Blade(i11)%TranslationVel + t(3)*u2%TurbineComponents%Blade(i11)%TranslationVel - t(2)*u3%TurbineComponents%Blade(i11)%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Blade(i11)%TranslationVel = u1%TurbineComponents%Blade(i11)%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO + DO i11 = LBOUND(u_out%TurbineComponents%Blade,1),UBOUND(u_out%TurbineComponents%Blade,1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Blade(i11)%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Blade(i11)%RotationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Blade(i11)%RotationVel - u2%TurbineComponents%Blade(i11)%RotationVel) + t(2)**2*(-u1%TurbineComponents%Blade(i11)%RotationVel + u3%TurbineComponents%Blade(i11)%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Blade(i11)%RotationVel + t(3)*u2%TurbineComponents%Blade(i11)%RotationVel - t(2)*u3%TurbineComponents%Blade(i11)%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Blade(i11)%RotationVel = u1%TurbineComponents%Blade(i11)%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ENDDO +END IF ! check if allocated + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%Position,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Hub%Position - u2%TurbineComponents%Hub%Position) + t(2)**2*(-u1%TurbineComponents%Hub%Position + u3%TurbineComponents%Hub%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Hub%Position + t(3)*u2%TurbineComponents%Hub%Position - t(2)*u3%TurbineComponents%Hub%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Hub%Position = u1%TurbineComponents%Hub%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Hub%Orientation,1),SIZE(u_out%TurbineComponents%Hub%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Hub%Orientation,1),SIZE(u_out%TurbineComponents%Hub%Orientation,2) )) + b2 = (t(3)**2*(u1%TurbineComponents%Hub%Orientation - u2%TurbineComponents%Hub%Orientation) + t(2)**2*(-u1%TurbineComponents%Hub%Orientation + u3%TurbineComponents%Hub%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%TurbineComponents%Hub%Orientation + t(3)*u2%TurbineComponents%Hub%Orientation - t(2)*u3%TurbineComponents%Hub%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Hub%Orientation = u1%TurbineComponents%Hub%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%TranslationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Hub%TranslationVel - u2%TurbineComponents%Hub%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Hub%TranslationVel + u3%TurbineComponents%Hub%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Hub%TranslationVel + t(3)*u2%TurbineComponents%Hub%TranslationVel - t(2)*u3%TurbineComponents%Hub%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Hub%TranslationVel = u1%TurbineComponents%Hub%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Hub%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Hub%RotationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Hub%RotationVel - u2%TurbineComponents%Hub%RotationVel) + t(2)**2*(-u1%TurbineComponents%Hub%RotationVel + u3%TurbineComponents%Hub%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Hub%RotationVel + t(3)*u2%TurbineComponents%Hub%RotationVel - t(2)*u3%TurbineComponents%Hub%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Hub%RotationVel = u1%TurbineComponents%Hub%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%Position,1))) + b1 = (t(3)**2*(u1%TurbineComponents%RotorFurl%Position - u2%TurbineComponents%RotorFurl%Position) + t(2)**2*(-u1%TurbineComponents%RotorFurl%Position + u3%TurbineComponents%RotorFurl%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%RotorFurl%Position + t(3)*u2%TurbineComponents%RotorFurl%Position - t(2)*u3%TurbineComponents%RotorFurl%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%RotorFurl%Position = u1%TurbineComponents%RotorFurl%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%RotorFurl%Orientation,1),SIZE(u_out%TurbineComponents%RotorFurl%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%RotorFurl%Orientation,1),SIZE(u_out%TurbineComponents%RotorFurl%Orientation,2) )) + b2 = (t(3)**2*(u1%TurbineComponents%RotorFurl%Orientation - u2%TurbineComponents%RotorFurl%Orientation) + t(2)**2*(-u1%TurbineComponents%RotorFurl%Orientation + u3%TurbineComponents%RotorFurl%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%TurbineComponents%RotorFurl%Orientation + t(3)*u2%TurbineComponents%RotorFurl%Orientation - t(2)*u3%TurbineComponents%RotorFurl%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%RotorFurl%Orientation = u1%TurbineComponents%RotorFurl%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%TranslationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%RotorFurl%TranslationVel - u2%TurbineComponents%RotorFurl%TranslationVel) + t(2)**2*(-u1%TurbineComponents%RotorFurl%TranslationVel + u3%TurbineComponents%RotorFurl%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%RotorFurl%TranslationVel + t(3)*u2%TurbineComponents%RotorFurl%TranslationVel - t(2)*u3%TurbineComponents%RotorFurl%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%RotorFurl%TranslationVel = u1%TurbineComponents%RotorFurl%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%RotorFurl%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%RotorFurl%RotationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%RotorFurl%RotationVel - u2%TurbineComponents%RotorFurl%RotationVel) + t(2)**2*(-u1%TurbineComponents%RotorFurl%RotationVel + u3%TurbineComponents%RotorFurl%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%RotorFurl%RotationVel + t(3)*u2%TurbineComponents%RotorFurl%RotationVel - t(2)*u3%TurbineComponents%RotorFurl%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%RotorFurl%RotationVel = u1%TurbineComponents%RotorFurl%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%Position,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Nacelle%Position - u2%TurbineComponents%Nacelle%Position) + t(2)**2*(-u1%TurbineComponents%Nacelle%Position + u3%TurbineComponents%Nacelle%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Nacelle%Position + t(3)*u2%TurbineComponents%Nacelle%Position - t(2)*u3%TurbineComponents%Nacelle%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Nacelle%Position = u1%TurbineComponents%Nacelle%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Nacelle%Orientation,1),SIZE(u_out%TurbineComponents%Nacelle%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Nacelle%Orientation,1),SIZE(u_out%TurbineComponents%Nacelle%Orientation,2) )) + b2 = (t(3)**2*(u1%TurbineComponents%Nacelle%Orientation - u2%TurbineComponents%Nacelle%Orientation) + t(2)**2*(-u1%TurbineComponents%Nacelle%Orientation + u3%TurbineComponents%Nacelle%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%TurbineComponents%Nacelle%Orientation + t(3)*u2%TurbineComponents%Nacelle%Orientation - t(2)*u3%TurbineComponents%Nacelle%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Nacelle%Orientation = u1%TurbineComponents%Nacelle%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%TranslationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Nacelle%TranslationVel - u2%TurbineComponents%Nacelle%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Nacelle%TranslationVel + u3%TurbineComponents%Nacelle%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Nacelle%TranslationVel + t(3)*u2%TurbineComponents%Nacelle%TranslationVel - t(2)*u3%TurbineComponents%Nacelle%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Nacelle%TranslationVel = u1%TurbineComponents%Nacelle%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Nacelle%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Nacelle%RotationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Nacelle%RotationVel - u2%TurbineComponents%Nacelle%RotationVel) + t(2)**2*(-u1%TurbineComponents%Nacelle%RotationVel + u3%TurbineComponents%Nacelle%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Nacelle%RotationVel + t(3)*u2%TurbineComponents%Nacelle%RotationVel - t(2)*u3%TurbineComponents%Nacelle%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Nacelle%RotationVel = u1%TurbineComponents%Nacelle%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%Position,1))) + b1 = (t(3)**2*(u1%TurbineComponents%TailFin%Position - u2%TurbineComponents%TailFin%Position) + t(2)**2*(-u1%TurbineComponents%TailFin%Position + u3%TurbineComponents%TailFin%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%TailFin%Position + t(3)*u2%TurbineComponents%TailFin%Position - t(2)*u3%TurbineComponents%TailFin%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%TailFin%Position = u1%TurbineComponents%TailFin%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%TailFin%Orientation,1),SIZE(u_out%TurbineComponents%TailFin%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%TailFin%Orientation,1),SIZE(u_out%TurbineComponents%TailFin%Orientation,2) )) + b2 = (t(3)**2*(u1%TurbineComponents%TailFin%Orientation - u2%TurbineComponents%TailFin%Orientation) + t(2)**2*(-u1%TurbineComponents%TailFin%Orientation + u3%TurbineComponents%TailFin%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%TurbineComponents%TailFin%Orientation + t(3)*u2%TurbineComponents%TailFin%Orientation - t(2)*u3%TurbineComponents%TailFin%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%TailFin%Orientation = u1%TurbineComponents%TailFin%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%TranslationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%TailFin%TranslationVel - u2%TurbineComponents%TailFin%TranslationVel) + t(2)**2*(-u1%TurbineComponents%TailFin%TranslationVel + u3%TurbineComponents%TailFin%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%TailFin%TranslationVel + t(3)*u2%TurbineComponents%TailFin%TranslationVel - t(2)*u3%TurbineComponents%TailFin%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%TailFin%TranslationVel = u1%TurbineComponents%TailFin%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%TailFin%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%TailFin%RotationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%TailFin%RotationVel - u2%TurbineComponents%TailFin%RotationVel) + t(2)**2*(-u1%TurbineComponents%TailFin%RotationVel + u3%TurbineComponents%TailFin%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%TailFin%RotationVel + t(3)*u2%TurbineComponents%TailFin%RotationVel - t(2)*u3%TurbineComponents%TailFin%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%TailFin%RotationVel = u1%TurbineComponents%TailFin%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%Position,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Tower%Position - u2%TurbineComponents%Tower%Position) + t(2)**2*(-u1%TurbineComponents%Tower%Position + u3%TurbineComponents%Tower%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Tower%Position + t(3)*u2%TurbineComponents%Tower%Position - t(2)*u3%TurbineComponents%Tower%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Tower%Position = u1%TurbineComponents%Tower%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Tower%Orientation,1),SIZE(u_out%TurbineComponents%Tower%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Tower%Orientation,1),SIZE(u_out%TurbineComponents%Tower%Orientation,2) )) + b2 = (t(3)**2*(u1%TurbineComponents%Tower%Orientation - u2%TurbineComponents%Tower%Orientation) + t(2)**2*(-u1%TurbineComponents%Tower%Orientation + u3%TurbineComponents%Tower%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%TurbineComponents%Tower%Orientation + t(3)*u2%TurbineComponents%Tower%Orientation - t(2)*u3%TurbineComponents%Tower%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Tower%Orientation = u1%TurbineComponents%Tower%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%TranslationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Tower%TranslationVel - u2%TurbineComponents%Tower%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Tower%TranslationVel + u3%TurbineComponents%Tower%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Tower%TranslationVel + t(3)*u2%TurbineComponents%Tower%TranslationVel - t(2)*u3%TurbineComponents%Tower%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Tower%TranslationVel = u1%TurbineComponents%Tower%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Tower%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Tower%RotationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Tower%RotationVel - u2%TurbineComponents%Tower%RotationVel) + t(2)**2*(-u1%TurbineComponents%Tower%RotationVel + u3%TurbineComponents%Tower%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Tower%RotationVel + t(3)*u2%TurbineComponents%Tower%RotationVel - t(2)*u3%TurbineComponents%Tower%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Tower%RotationVel = u1%TurbineComponents%Tower%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%Position,1))) + b1 = (t(3)**2*(u1%TurbineComponents%SubStructure%Position - u2%TurbineComponents%SubStructure%Position) + t(2)**2*(-u1%TurbineComponents%SubStructure%Position + u3%TurbineComponents%SubStructure%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%SubStructure%Position + t(3)*u2%TurbineComponents%SubStructure%Position - t(2)*u3%TurbineComponents%SubStructure%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%SubStructure%Position = u1%TurbineComponents%SubStructure%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%SubStructure%Orientation,1),SIZE(u_out%TurbineComponents%SubStructure%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%SubStructure%Orientation,1),SIZE(u_out%TurbineComponents%SubStructure%Orientation,2) )) + b2 = (t(3)**2*(u1%TurbineComponents%SubStructure%Orientation - u2%TurbineComponents%SubStructure%Orientation) + t(2)**2*(-u1%TurbineComponents%SubStructure%Orientation + u3%TurbineComponents%SubStructure%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%TurbineComponents%SubStructure%Orientation + t(3)*u2%TurbineComponents%SubStructure%Orientation - t(2)*u3%TurbineComponents%SubStructure%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%SubStructure%Orientation = u1%TurbineComponents%SubStructure%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%TranslationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%SubStructure%TranslationVel - u2%TurbineComponents%SubStructure%TranslationVel) + t(2)**2*(-u1%TurbineComponents%SubStructure%TranslationVel + u3%TurbineComponents%SubStructure%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%SubStructure%TranslationVel + t(3)*u2%TurbineComponents%SubStructure%TranslationVel - t(2)*u3%TurbineComponents%SubStructure%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%SubStructure%TranslationVel = u1%TurbineComponents%SubStructure%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%SubStructure%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%SubStructure%RotationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%SubStructure%RotationVel - u2%TurbineComponents%SubStructure%RotationVel) + t(2)**2*(-u1%TurbineComponents%SubStructure%RotationVel + u3%TurbineComponents%SubStructure%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%SubStructure%RotationVel + t(3)*u2%TurbineComponents%SubStructure%RotationVel - t(2)*u3%TurbineComponents%SubStructure%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%SubStructure%RotationVel = u1%TurbineComponents%SubStructure%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%Position,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%Position,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Foundation%Position - u2%TurbineComponents%Foundation%Position) + t(2)**2*(-u1%TurbineComponents%Foundation%Position + u3%TurbineComponents%Foundation%Position))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Foundation%Position + t(3)*u2%TurbineComponents%Foundation%Position - t(2)*u3%TurbineComponents%Foundation%Position ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Foundation%Position = u1%TurbineComponents%Foundation%Position + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b2(SIZE(u_out%TurbineComponents%Foundation%Orientation,1),SIZE(u_out%TurbineComponents%Foundation%Orientation,2) )) + ALLOCATE(c2(SIZE(u_out%TurbineComponents%Foundation%Orientation,1),SIZE(u_out%TurbineComponents%Foundation%Orientation,2) )) + b2 = (t(3)**2*(u1%TurbineComponents%Foundation%Orientation - u2%TurbineComponents%Foundation%Orientation) + t(2)**2*(-u1%TurbineComponents%Foundation%Orientation + u3%TurbineComponents%Foundation%Orientation))/(t(2)*t(3)*(t(2) - t(3))) + c2 = ( (t(2)-t(3))*u1%TurbineComponents%Foundation%Orientation + t(3)*u2%TurbineComponents%Foundation%Orientation - t(2)*u3%TurbineComponents%Foundation%Orientation ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Foundation%Orientation = u1%TurbineComponents%Foundation%Orientation + b2 * t_out + c2 * t_out**2 + DEALLOCATE(b2) + DEALLOCATE(c2) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%TranslationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%TranslationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Foundation%TranslationVel - u2%TurbineComponents%Foundation%TranslationVel) + t(2)**2*(-u1%TurbineComponents%Foundation%TranslationVel + u3%TurbineComponents%Foundation%TranslationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Foundation%TranslationVel + t(3)*u2%TurbineComponents%Foundation%TranslationVel - t(2)*u3%TurbineComponents%Foundation%TranslationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Foundation%TranslationVel = u1%TurbineComponents%Foundation%TranslationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + ALLOCATE(b1(SIZE(u_out%TurbineComponents%Foundation%RotationVel,1))) + ALLOCATE(c1(SIZE(u_out%TurbineComponents%Foundation%RotationVel,1))) + b1 = (t(3)**2*(u1%TurbineComponents%Foundation%RotationVel - u2%TurbineComponents%Foundation%RotationVel) + t(2)**2*(-u1%TurbineComponents%Foundation%RotationVel + u3%TurbineComponents%Foundation%RotationVel))/(t(2)*t(3)*(t(2) - t(3))) + c1 = ( (t(2)-t(3))*u1%TurbineComponents%Foundation%RotationVel + t(3)*u2%TurbineComponents%Foundation%RotationVel - t(2)*u3%TurbineComponents%Foundation%RotationVel ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%Foundation%RotationVel = u1%TurbineComponents%Foundation%RotationVel + b1 * t_out + c1 * t_out**2 + DEALLOCATE(b1) + DEALLOCATE(c1) + b0 = (t(3)**2*(u1%TurbineComponents%BladeLength - u2%TurbineComponents%BladeLength) + t(2)**2*(-u1%TurbineComponents%BladeLength + u3%TurbineComponents%BladeLength))/(t(2)*t(3)*(t(2) - t(3))) + c0 = ( (t(2)-t(3))*u1%TurbineComponents%BladeLength + t(3)*u2%TurbineComponents%BladeLength - t(2)*u3%TurbineComponents%BladeLength ) / (t(2)*t(3)*(t(2) - t(3))) + u_out%TurbineComponents%BladeLength = u1%TurbineComponents%BladeLength + b0 * t_out + c0 * t_out**2 IF (ALLOCATED(u_out%MulTabLoc) .AND. ALLOCATED(u1%MulTabLoc)) THEN ALLOCATE(b2(SIZE(u_out%MulTabLoc,1),SIZE(u_out%MulTabLoc,2) )) ALLOCATE(c2(SIZE(u_out%MulTabLoc,1),SIZE(u_out%MulTabLoc,2) )) diff --git a/modules/aerodyn14/src/Registry-AD14.txt b/modules/aerodyn14/src/Registry-AD14.txt index 8945c9bd26..59eceb2c7b 100644 --- a/modules/aerodyn14/src/Registry-AD14.txt +++ b/modules/aerodyn14/src/Registry-AD14.txt @@ -11,11 +11,49 @@ ################################################################################################################################### include Registry_NWTC_Library.txt usefrom Registry-DWM.txt -usefrom Registry-AD14AeroConf.txt +# AeroDyn Subtypes +typedef AeroDyn14/AD14 Marker Reki Position 3 0.0 - - +typedef ^ Marker ^ Orientation {3}{3} 0.0 - - +typedef ^ Marker ^ TranslationVel 3 0.0 - - +typedef ^ Marker ^ RotationVel 3 0.0 - - + +#ADOptions +#typedef AeroDyn14/AD14 ADOptions CHARACTER(1024) ADFile +#typedef ^ ADOptions CHARACTER(1024) RootName +#typedef ^ ADOptions CHARACTER(1024) SumFile +#typedef ^ ADOptions LOGICAL WrSumFile + +#AeroConfig +typedef AeroDyn14/AD14 AeroConfig Marker Blade {:} - - - +typedef ^ AeroConfig ^ Hub - - - - +typedef ^ AeroConfig ^ RotorFurl - - - - +typedef ^ AeroConfig ^ Nacelle - - - - +typedef ^ AeroConfig ^ TailFin - - - - +typedef ^ AeroConfig ^ Tower - - - - +typedef ^ AeroConfig ^ SubStructure - - - - +typedef ^ AeroConfig ^ Foundation - - - - +typedef ^ AeroConfig ReKi BladeLength - - - - + + +#Airfoil +typedef AeroDyn14/AD14 AirFoil ReKi AL {:}{:} - - - +typedef ^ AirFoil ReKi CD {:}{:}{:} - - - +typedef ^ AirFoil ReKi CL {:}{:}{:} - - - +typedef ^ AirFoil ReKi CM {:}{:}{:} - - - +typedef ^ AirFoil ReKi PMC - - - - +typedef ^ AirFoil ReKi MulTabLoc - +typedef ^ AirFoilParms IntKi MaxTable - 20 - +typedef ^ AirFoilParms IntKi NTables {:} - - - +typedef ^ AirFoilParms IntKi NLift {:} - - - +typedef ^ AirFoilParms IntKi NumCL - - - - +typedef ^ AirFoilParms IntKi NumFoil - - - +typedef ^ AirFoilParms IntKi NFoil {:} - - - +typedef ^ AirFoilParms ReKi MulTabMet {:}{:} - - - +typedef ^ AirFoilParms CHARACTER(1024) FoilNm {:} "Number of airfoil data sets" #Beddoes -typedef AeroDyn14/AD14 Beddoes ReKi ADOT {:}{:} - - - +typedef ^ Beddoes ReKi ADOT {:}{:} - - - typedef ^ Beddoes ReKi ADOT1 {:}{:} - - - typedef ^ Beddoes ReKi AFE {:}{:} - - - typedef ^ Beddoes ReKi AFE1 {:}{:} - - - @@ -249,15 +287,15 @@ typedef ^ InitInputType ReKi BladeLength - - - "Blade Length" #typedef ^ InitInputType OrientationType InitBladeOrient {:}{:} - - "Positions of the blades, initially, from FAST" typedef ^ InitInputType LOGICAL LinearizeFlag typedef ^ InitInputType LOGICAL UseDWM - .FALSE. - "flag to determine if DWM module should be used" - -typedef ^ InitInputType AD14AeroConf_InputType TurbineComponents - - - - +typedef ^ InitInputType AeroConfig TurbineComponents - - - - typedef ^ InitInputType INTEGER NumTwrNodes - - - "Number of ElastoDyn tower nodes. Tower drag will be computed at those points." typedef ^ InitInputType ReKi TwrNodeLocs {:}{:} - - "Location of ElastoDyn tower nodes with respect to the inertial origin." - typedef ^ InitInputType ReKi HubHt - - - "hub height wrt inertial origin" m typedef ^ InitInputType DWM_InitInputType DWM - - - - # Define outputs from the initialization routine here: -typedef AeroDyn14/AD14 InitOutputType ProgDesc Ver - - - "version information" - -typedef ^ InitOutputType DWM_InitOutputType DWM - - - - - +typedef AeroDyn14/AD14 InitOutputType ProgDesc Ver - - - "version information" +typedef ^ InitOutputType DWM_InitOutputType DWM - - - - typedef ^ InitOutputType ReKi AirDens - - - "Air density" kg/m^3 # ..... States .................................................................................................................... @@ -299,7 +337,7 @@ typedef ^ MiscVarType LOGICAL AFLAGTwrInflu - .FALSE. - # was saved latch in Get typedef ^ MiscVarType LOGICAL OnePassDynDbg - .TRUE. - # typedef ^ MiscVarType LOGICAL NoLoadsCalculated - .TRUE. - - typedef ^ MiscVarType IntKi NERRORS - 0 - # was saved variable in vinderr -typedef ^ MiscVarType AD14AeroConf_MiscVarType AirFoil +typedef ^ MiscVarType AirFoil AirFoil typedef ^ MiscVarType Beddoes Beddoes typedef ^ MiscVarType DynInflow DynInflow typedef ^ MiscVarType Element Element @@ -344,7 +382,7 @@ typedef ^ ParameterType INTEGER UnWndOut - -1 # note, these are not minus signs typedef ^ ParameterType INTEGER MAXICOUNT - 1000 - # used in VIND typedef ^ ParameterType LOGICAL WrOptFile - .TRUE. - "T/F: Write an AeroDyn summary" typedef ^ ParameterType IntKi DEFAULT_Wind - -1 - -typedef ^ ParameterType AD14AeroConf_ParameterType AirFoil +typedef ^ ParameterType AirFoilParms AirFoil typedef ^ ParameterType BladeParms Blade typedef ^ ParameterType BeddoesParms Beddoes typedef ^ ParameterType DynInflowParms DynInflow @@ -355,16 +393,13 @@ typedef ^ ParameterType WindParms Wind typedef ^ ParameterType RotorParms Rotor typedef ^ ParameterType DWM_ParameterType DWM - - - - -##FIXME: Remove these!!!! -typedef ^ ParameterType DbKi IfW_DT - - - - - # ..... Inputs .................................................................................................................... # Define inputs that are contained on the mesh here: #typedef ^ InputType MeshType MeshedInput - - - "Meshed input data" - # Define inputs that are not on this mesh here: typedef ^ InputType MeshType InputMarkers {:} - - "Input Forces and positions for the blades (mesh) for each blade" - typedef ^ InputType MeshType Twr_InputMarkers - - - "Input Forces and positions for the tower (mesh)" - -typedef ^ InputType AD14AeroConf_InputType TurbineComponents - - - "Current locations of components" +typedef ^ InputType AeroConfig TurbineComponents - - - "Current locations of components" typedef ^ InputType ReKi MulTabLoc {:}{:} typedef ^ InputType ReKi InflowVelocity {:}{:} - - "U,V,W wind inflow speeds at all locations on the Inputmarker and Twr_InputMarker meshes" "m/s" typedef ^ InputType ReKi AvgInfVel {3} - - "an average disk velocity (depends on wind type and should be removed)" "m/s" diff --git a/modules/openfast-library/src/FAST_Types.f90 b/modules/openfast-library/src/FAST_Types.f90 index 6a0e586429..f6d57fb83a 100644 --- a/modules/openfast-library/src/FAST_Types.f90 +++ b/modules/openfast-library/src/FAST_Types.f90 @@ -44,7 +44,6 @@ MODULE FAST_Types USE Lidar_Types USE InflowWind_Types USE DWM_Types -USE AD14AeroConf_Types USE AeroDyn14_Types USE AirfoilInfo_Types USE UnsteadyAero_Types From 48ae727473be02b20e75485d6655af953e9dc7e5 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 2 Apr 2020 13:36:51 -0600 Subject: [PATCH 103/190] FVW: handling of unused variables --- modules/aerodyn/src/FVW.f90 | 33 ++++++++++++++----------- modules/aerodyn/src/FVW_IO.f90 | 14 +++-------- modules/aerodyn/src/FVW_Subs.f90 | 22 ++++++++--------- modules/aerodyn/src/FVW_Tests.f90 | 16 ++++++------ modules/aerodyn/src/FVW_VTK.f90 | 28 ++++----------------- modules/aerodyn/src/FVW_VortexTools.f90 | 1 - modules/aerodyn/src/FVW_Wings.f90 | 26 +++++++++---------- 7 files changed, 57 insertions(+), 83 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 43ac32e349..b78d092138 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -78,7 +78,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu call DispNVD( FVW_Ver ) ! Set Parameters and *Misc* from inputs - CALL FVW_SetParametersFromInputs(InitInp, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL FVW_SetParametersFromInputs(InitInp, p, ErrStat2, ErrMsg2); if(Failed()) return ! Read and parse the input file here to get other parameters and info CALL FVW_ReadInputFile(InitInp%FVWFileName, p, InputFileData, ErrStat2, ErrMsg2); if(Failed()) return @@ -103,13 +103,13 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu m%Vwnd_FW(:,:,:,:) = 0 ! This mesh is passed in as a cousin of the BladeMotion mesh. - CALL Wings_Panelling_Init(u%WingsMesh, InitInp%zLocal, p, m, ErrStat2, ErrMsg2); if(Failed()) return + CALL Wings_Panelling_Init(u%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Set parameters from InputFileData (need Misc allocated) CALL FVW_SetParametersFromInputFile(InputFileData, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Initialize States Vars - CALL FVW_InitStates( x, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + CALL FVW_InitStates( x, p, ErrStat2, ErrMsg2 ); if(Failed()) return ! Initialize Constraints Vars CALL FVW_InitConstraint( z, p, m, ErrStat2, ErrMsg2 ); if(Failed()) return @@ -131,6 +131,11 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL FVW_RunTests(ErrStat2, ErrMsg2); if (Failed()) return endif + ! Framework types unused + Interval = InitInp%DTAero + OtherState%NULL = 0 + xd%NULL = 0 + InitOut%NULL = 0 CONTAINS logical function Failed() @@ -198,10 +203,9 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%OldWakeTime = -HUGE(1.0_DbKi) end subroutine FVW_InitMiscVars ! ============================================================================== -subroutine FVW_InitStates( x, p, m, ErrStat, ErrMsg ) +subroutine FVW_InitStates( x, p, ErrStat, ErrMsg ) type(FVW_ContinuousStateType), intent( out) :: x !< States type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None integer(IntKi) :: ErrStat2 ! temporary error status of the operation @@ -236,6 +240,7 @@ subroutine FVW_InitConstraint( z, p, m, ErrStat, ErrMsg ) call AllocAry( z%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitConstraint' ); z%Gamma_LL = -999999_ReKi; if (ErrStat >= AbortErrLev) return + if(.false.) print*,m%nNW ! unused var for now end subroutine FVW_InitConstraint ! ============================================================================== subroutine FVW_Init_Y( p, u, y, ErrStat, ErrMsg ) @@ -268,17 +273,14 @@ end subroutine FVW_Init_Y ! ============================================================================== !> Setting parameters *and misc* from module inputs -SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, m, ErrStat, ErrMsg ) +SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, ErrStat, ErrMsg ) type(FVW_InitInputType), intent(inout) :: InitInp !< Input data for initialization routine (inout so we can use MOVE_ALLOC) type(FVW_ParameterType), intent(inout) :: p !< Parameters - type(FVW_MiscVarType), intent(inout) :: m !< Misc integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iW ! Index on wings - integer(IntKi) :: iSpan - integer(IntKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 + !integer(IntKi) :: ErrStat2 + !character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'FVW_SetParametersFromInputs' ErrStat = ErrID_None ErrMsg = "" @@ -349,6 +351,7 @@ subroutine FVW_ToString(p,m) type(FVW_MiscVarType), intent(inout) :: m !< Misc if (DEV_VERSION) then print*,'-----------------------------------------------------------------------------------------' + if(.false.) print*,m%nNW ! unused var for now endif end subroutine FVW_ToString @@ -419,7 +422,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m integer(IntKi) :: ErrStat2 ! temporary Error status character(ErrMsgLen) :: ErrMsg2 ! temporary Error message type(FVW_ConstraintStateType) :: z_guess ! < - integer(IntKi) :: iW, iSpan, nP, nFWEff + integer(IntKi) :: nP, nFWEff integer, dimension(8) :: time1, time2, time_diff ErrStat = ErrID_None @@ -449,7 +452,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return ! Distribute the Wind we requested to Inflow wind to storage Misc arrays - CALL DistributeRequestedWind(u(1)%V_wind, x, p, m) + CALL DistributeRequestedWind(u(1)%V_wind, p, m) ! --- Solve for circulation at t ! Returns: z%Gamma_LL (at t) @@ -743,7 +746,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iSpan, iW, n, i0, i1, i2 + integer(IntKi) :: iW, n, i0, i1, i2 integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'FVW_CalcOutput' @@ -756,7 +759,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, endif ! Set the wind velocity at vortex - CALL DistributeRequestedWind(u%V_wind, x, p, m) + CALL DistributeRequestedWind(u%V_wind, p, m) ! if we are on a correction step, CalcOutput may be called again with different inputs CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, AFInfo, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index cbd50160ef..e20e2873b5 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -16,11 +16,8 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer :: iLine - real(ReKi) :: TODO_Re character(1024) :: PriPath ! the path to the primary input file character(1024) :: VTK_fps_line ! string to temporarially hold value of read line for VTK_fps - integer(IntKi) :: LineLen integer(IntKi) :: UnIn integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 @@ -176,17 +173,13 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) character(1024) :: FileName character(255) :: Label character(Twidth) :: Tstr ! string for current VTK write-out step (padded with zeros) - integer :: iSeg - integer :: iSpan, iNW, iFW - integer :: k - real(ReKi), dimension(:,:), allocatable :: Buffer2d character(1), dimension(3) :: I2ABC =(/'A','B','C'/) ! integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation real(ReKi), dimension(:), allocatable :: SegEpsilon !< - integer(IntKi) :: iHeadC, iHeadP, nSeg, nSegP, iHeadC_bkp + integer(IntKi) :: iHeadC, iHeadP, nSeg, nSegP integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 @@ -268,6 +261,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) CALL WrVTK_Segments(Filename, SegPoints, SegConnct, SegGamma, SegEpsilon) deallocate(SegEpsilon) + if(.false.) print*,z%Gamma_LL(1,1) ! unused var for now end subroutine WrVTK_FVW @@ -292,12 +286,12 @@ subroutine WrVTK_Segments(filename, SegPoints, SegConnct, SegGamma, SegEpsilon) endif end subroutine -subroutine WrVTK_Lattice(filename, LatticePoints, LatticeGamma, LatticeData3d) +subroutine WrVTK_Lattice(filename, LatticePoints, LatticeGamma) use VTK ! for all the vtk_* functions character(len=*), intent(in) :: filename real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Array of points 3 x nSpan x nDepth real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< Array of nSpan x nDepth - real(Reki), dimension(:,:,:), intent(in ), optional :: LatticeData3d !< Array of n x nSpan x nDepth + !real(Reki), dimension(:,:,:), intent(in ), optional :: LatticeData3d !< Array of n x nSpan x nDepth KEEP ME ! integer(IntKi), dimension(:,:), allocatable :: Connectivity real(ReKi), dimension(:,:), allocatable :: Points diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index cb4a9b2b8a..d7d77f2a8a 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -107,7 +107,6 @@ subroutine ReadAndInterpGamma(CirculationFileName, s_CP_LL, L, Gamma_CP_LL) integer(IntKi) :: nLines integer(IntKi) :: i integer(IntKi) :: iStat - integer(IntKi) :: nr integer(IntKi) :: iUnit character(len=1054) :: line real(ReKi), dimension(:), allocatable :: sPrescr, GammaPrescr !< Radius @@ -207,7 +206,7 @@ subroutine Map_NW_FW(p, m, z, x, ErrStat, ErrMsg) type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - integer(IntKi) :: iSpan , iW, iRoot + integer(IntKi) :: iW, iRoot real(ReKi), dimension(p%nWings) :: FWGamma integer(IntKi), parameter :: iAgeFW=1 !< we update the first FW panel ErrStat = ErrID_None @@ -246,6 +245,7 @@ subroutine Map_NW_FW(p, m, z, x, ErrStat, ErrMsg) endif enddo endif + if (.false.) print*,z%Gamma_LL(1,1) ! Just to avoid unused var warning endsubroutine Map_NW_FW !> Propage the postions and circulation one index forward (loop from end to start) @@ -298,13 +298,12 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) x%Gamma_NW(:,1:iNWStart,iW) = -999.9_ReKi ! Nullified enddo endif + if (.false.) print*,m%nNW,z%Gamma_LL(1,1) ! Just to avoid unused var warning end subroutine PropagateWake -subroutine print_x_NW_FW(p, m, z, x, label) +subroutine print_x_NW_FW(p, x, label) type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables - type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states character(len=*),intent(in) :: label integer(IntKi) :: iAge @@ -362,9 +361,8 @@ end subroutine SetRequestedWindPoints !> Set the requested wind into the correponding misc variables -subroutine DistributeRequestedWind(V_wind, x, p, m) +subroutine DistributeRequestedWind(V_wind, p, m) real(ReKi), dimension(:,:), intent(in ) :: V_wind !< Position where wind is requested - type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi) :: iP_start,iP_end ! Current index of point, start and end of range @@ -474,13 +472,12 @@ subroutine FVW_InitRegularization(p, m, ErrStat, ErrMsg) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iSeg real(ReKi) :: ds_min, ds_max, ds_mean !< min,max and mean of spanwise sections real(ReKi) :: c_min, c_max, c_mean !< min,max and mean of chord real(ReKi) :: d_min, d_max, d_mean !< min,max and mean of panel diagonal real(ReKi) :: RegParam real(ReKi) :: Span !< "Blade span" - integer :: iSpan, iW + integer :: iW ErrStat = ErrID_None ErrMsg = "" ! --- Compute min max and mean spanwise section lengths @@ -560,6 +557,7 @@ subroutine WakeRegularization(p, x, m, SegConnct, SegPoints, SegGamma, SegEpsilo ! TODO ErrStat = ErrID_Fatal ErrMsg ='Regularization method not implemented' + if (.false.) print*,m%nNW,x%r_NW(1,1,1,1),SegPoints(1,1),SegGamma(1) ! Needed in the future, Just to avoid unused var warning else if (p%WakeRegMethod==idRegAge) then do iSeg=1,size(SegEpsilon,1) ! loop on segments @@ -585,7 +583,7 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables - integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs, iHeadP + integer(IntKi) :: iW, nSeg, nSegP, nCPs, iHeadP integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation @@ -690,7 +688,7 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) integer(IntKi), intent(in ) :: iDepthStart !< Index where we start packing for NW panels type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables ! Local variables - integer(IntKi) :: iSpan,iAge, iW, nSeg, nSegP, nCPs, iHeadP + integer(IntKi) :: iW, nSeg, nSegP, nCPs, iHeadP integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation @@ -699,7 +697,6 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - integer(IntKi) :: i ErrStat = ErrID_None ErrMsg = "" m%Vind_LL = -9999._ReKi !< Safety @@ -826,6 +823,7 @@ subroutine FVW_AeroOuts( M_sg, M_ag, PitchAndTwist, Vstr_g, Vind_g, Vwnd_g, Kin TanInd = Vind_s(2)/Urel_s(2) phi = atan2( Vtot_s(1), Vtot_s(2) ) ! flow angle + if(.false.) print*,PitchAndTwist ! just to avoid unused var for now end subroutine FVW_AeroOuts diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index b745fca855..7580a7e815 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -70,8 +70,7 @@ subroutine test_equal_i0(Var,iTry,iRef) if(iRef/=iTry) then write(InfoAbs,'(A,I0,A,I0)') trim(Var),iRef,'/',iTry call test_fail(InfoAbs) - STOP -1 !OTHER-COMPILER - STOP ! COMPAQ-COMPILER + STOP else write(InfoAbs,'(A,A,I0)') trim(Var),' ok ',iRef call test_success(InfoAbs) @@ -216,11 +215,11 @@ subroutine test_almost_equal_2(Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - integer(IntKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 + !integer(IntKi) :: ErrStat2 + !character(ErrMsgLen) :: ErrMsg2 ! real(ReKi), dimension(3) :: P1,P2,P3,CP - real(ReKi), dimension(3) :: U1,U2 + real(ReKi), dimension(3) :: U1 real(ReKi) :: SegGamma1 !< Circulation [m^2/s] real(ReKi) :: RegParam1 !< integer(IntKi) :: i1,i2 @@ -270,7 +269,7 @@ subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) print*,'Reg function', RegFunction, 'CP',CP print*,'Uind_out',Uind_out print*,'U1 ',U1 - call test_almost_equal('Uind method1/2', U1, Uind_out(:,1), 1e-4, .true.,.true.) + call test_almost_equal('Uind method1/2', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) !call test_almost_equal('Uind method1/2', U1, Uind_out(:,1), 1e-4, .false.,.true.) enddo enddo @@ -309,7 +308,7 @@ subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) !print*,'Reg function', RegFunction, 'CP',CP !print*,'Uind_out',Uind_out !print*,'U1 ',U1 - call test_almost_equal('Uind 1seg/2seg', U1, Uind_out(:,1), 1e-4, .true.,.true.) + call test_almost_equal('Uind 1seg/2seg', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) enddo enddo end subroutine @@ -331,13 +330,14 @@ subroutine Test_LatticeToSegment(iStat) real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi) :: iHeadC integer(IntKi) :: iHeadP - integer(IntKi) :: i,j,k + integer(IntKi) :: i,j integer(IntKi) :: nP integer(IntKi) :: nC integer(IntKi) :: nP1, nP2 integer(IntKi) :: nC1, nC2 integer(IntKi) :: nDepth, nSpan integer(IntKi) :: SmoothModel + iStat=0 ! --- Creating two lattice allocate(LatticePoints1(3,2,2)) diff --git a/modules/aerodyn/src/FVW_VTK.f90 b/modules/aerodyn/src/FVW_VTK.f90 index b4ac5d6397..009423002b 100644 --- a/modules/aerodyn/src/FVW_VTK.f90 +++ b/modules/aerodyn/src/FVW_VTK.f90 @@ -1,6 +1,6 @@ module VTK !use PrecisionMod, only: ReKi - use NWTC_Library, only: ReKi + use NWTC_Library, only: ReKi, GetNewUnit implicit none ! character(8), parameter :: RFMT='F14.5' !character(8), parameter :: RFMT='E24.15E3' @@ -74,7 +74,7 @@ subroutine set_vtk_coordinate_transform(T_g2b_in,PO_g_in) - logical function vtk_new_ascii_file(filename,label,bDEBUG) + logical function vtk_new_ascii_file(filename,label) !use MainIO, only: get_free_unit ,check_io !use MainIOData, only: bSTOP_ALLOWED !use FileSystem, only: file_exists @@ -82,25 +82,12 @@ logical function vtk_new_ascii_file(filename,label,bDEBUG) ! character(len=*),intent(in) :: filename character(len=*),intent(in) :: label - logical, intent(in),optional ::bDEBUG ! integer :: iostatvar - character(len=255) :: iomessage logical :: b if (.not.bFileOpen) then - !if (present(bDEBUG)) then - ! if (bDEBUG) call log_info('Opening VTK file:'//filename) - !endif - - !if (file_exists(filename) .and. .not.bOverWritWarned) then - !call log_warning('Overwritting vtk file '//filename) - !call log_warning('Further overwritting warnings will not be displayed') - ! bOverWritWarned=.true. - !endif - - !vtk_unit=get_free_unit(); - vtk_unit=123455 + CALL GetNewUnit( vtk_unit ) if (bBinary) then ! Fortran 2003 stream, otherwise intel fortran ! !form='UNFORMATTED',access='SEQUENTIAL',action='WRITE',convert='BIG_ENDIAN',recordtype='STREAM',buffered='YES', @@ -111,11 +98,6 @@ logical function vtk_new_ascii_file(filename,label,bDEBUG) else open(vtk_unit,file=trim(adjustl(filename)),iostat=iostatvar,action="write",status='replace') endif - !b=check_io(iostatvar,iomessage); - !if(.not.b .and. bSTOP_ALLOWED) then - ! STOP ! io errors are fatal - !endif - if (iostatvar == 0) then if (bBinary) then write(vtk_unit)'# vtk DataFile Version 3.0'//NL @@ -521,7 +503,7 @@ subroutine export_plane_grid3d(fname,v1,v2,v3,Values) integer :: nD ! Writting - if ( vtk_new_ascii_file(trim(fname),'plane', .false.)) then + if ( vtk_new_ascii_file(trim(fname),'grid')) then nD=size(Values,1) call vtk_dataset_rectilinear(v1,v2,v3) ! Output as a structured grid, No need to reorder @@ -544,7 +526,7 @@ subroutine export_plane_grid2d(fname,v1,v2,v3,Values) integer :: nD ! Writting - if ( vtk_new_ascii_file(trim(fname),'plane', .false.)) then + if ( vtk_new_ascii_file(trim(fname),'plane') ) then nD=size(Values,1) call vtk_dataset_rectilinear(v1,v2,v3) ! Output as a structured grid, No need to reorder diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index 1da3228f6d..6514af424a 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -63,7 +63,6 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints ! Local integer(IntKi) :: nSpan, nDepth integer(IntKi) :: iSpan, iDepth - real(ReKi) :: c1,c2 integer(IntKi) :: iHeadP0, iseg1, iseg2, iseg3 ,iseg4 !< Index indicating where to start in SegPoints real(ReKi) :: Gamma12 real(ReKi) :: Gamma41 diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 217067fed8..dc1bc8e10c 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -63,16 +63,15 @@ end subroutine Meshing !! - s_CP_LL : Dimensionless spanwise coordinate of LL CP !! - chord_LL : chord on LL !! - chord_LL_CP: chord on LL cp - subroutine Wings_Panelling_Init(Meshes, r, p, m, ErrStat, ErrMsg ) + subroutine Wings_Panelling_Init(Meshes, p, m, ErrStat, ErrMsg ) type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh - real(ReKi), dimension(:,:), intent(in ) :: r !< type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local - integer(IntKi) :: ErrStat2 ! temporary error status of the operation - character(ErrMsgLen) :: ErrMsg2 ! temporary error message + !integer(IntKi) :: ErrStat2 ! temporary error status of the operation + !character(ErrMsgLen) :: ErrMsg2 ! temporary error message integer(IntKi) :: iW, iSpan real(ReKi), dimension(3) :: DP real(ReKi), dimension(:),allocatable :: s_in !< Dimensionless spanwise coordinate of input @@ -128,8 +127,8 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local - integer(IntKi) :: ErrStat2 ! temporary error status of the operation - character(ErrMsgLen) :: ErrMsg2 ! temporary error message + !integer(IntKi) :: ErrStat2 ! temporary error status of the operation + !character(ErrMsgLen) :: ErrMsg2 ! temporary error message integer(IntKi) ::iSpan , iW real(ReKi), dimension(3) :: P_ref ! Reference point of Input Mesh (e.g. AeroDynamic Center?) real(ReKi), dimension(3) :: DP_LE ! Distance between reference point and Leading edge @@ -232,7 +231,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFIn integer(IntKi), intent(in) :: iLabel ! Local integer(IntKi) :: iW - real(ReKi) :: s + real(DbKi) :: s, ExpTerm real(ReKi) :: GammaScale ErrStat = ErrID_None ErrMsg = "" @@ -242,10 +241,11 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFIn if (t<=0) then GammaScale=0.0_ReKi else - s=(t/p%FullCirculationStart) - ! If we have at least 10 points we use a smooth Heavyside, otehrwise we use a simple linear scaling + s=t/p%FullCirculationStart + ! If we have at least 10 points we use a smooth Heavyside, otherwise we use a simple linear scaling if (p%FullCirculationStart/p%DTfvw >= 9) then - GammaScale = 1._ReKi- 1._ReKi/(1._ReKi+exp((1-2*s)/(s*(s-1._ReKi)))) ! Using a smooth approsimation of HeavySide function + ExpTerm=max( (1-2*s)/(s*(s-1._DbKi)+1.0e-04_DbKi),10.0_DbKi) ! Bounding exponential to avoid overflow + GammaScale = 1._ReKi- 1._ReKi/(1._ReKi+exp(real(ExpTerm,ReKi))) ! Using a smooth approximation of HeavySide function else GammaScale = s ! Using a linear scaling endif @@ -261,7 +261,7 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFIn else if (p%CirculationMethod==idCircPolarData) then ! --- Solve for circulation using polar data - CALL Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, GammaScale, ErrStat, ErrMsg, iLabel) + CALL Wings_ComputeCirculationPolarData(Gamma_LL, Gamma_LL_prev, p, x, m, AFInfo, GammaScale, ErrStat, ErrMsg, iLabel) else if (p%CirculationMethod==idCircNoFlowThrough) then ! --- Solve for circulation using the no-flow through condition @@ -278,11 +278,9 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFIn !---------------------------------------------------------------------------------------------------------------------------------- !> - subroutine Wings_ComputeCirculationPolarData(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFInfo, GammaScale, ErrStat, ErrMsg, iLabel) - real(DbKi), intent(in ) :: t !< Current simulation time in seconds + subroutine Wings_ComputeCirculationPolarData(Gamma_LL, Gamma_LL_prev, p, x, m, AFInfo, GammaScale, ErrStat, ErrMsg, iLabel) real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines real(ReKi), dimension(:,:), intent(in ) :: Gamma_LL_prev !< Previous/Guessed circulation - type(FVW_InputType), intent(in ) :: u !< Parameters type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables From 41908dfb68014989c4ffe76207e9208d6cf037e6 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 2 Apr 2020 14:01:29 -0600 Subject: [PATCH 104/190] FVW: update of visual studio solutions (FVW+OpenMP) --- vs-build/AeroDyn/AeroDyn_Driver.vfproj | 166 ++++-- vs-build/FAST/FAST.vfproj | 16 +- vs-build/FASTlib/FASTlib.vfproj | 697 ++++++++++++++----------- vs-build/RunRegistry.bat | 8 +- 4 files changed, 506 insertions(+), 381 deletions(-) diff --git a/vs-build/AeroDyn/AeroDyn_Driver.vfproj b/vs-build/AeroDyn/AeroDyn_Driver.vfproj index 88f796b098..a0da6be7ba 100644 --- a/vs-build/AeroDyn/AeroDyn_Driver.vfproj +++ b/vs-build/AeroDyn/AeroDyn_Driver.vfproj @@ -5,7 +5,7 @@ - + @@ -15,7 +15,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -35,7 +35,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -65,7 +65,7 @@ - + @@ -75,7 +75,7 @@ - + @@ -89,53 +89,24 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + @@ -153,19 +124,19 @@ - - + + - - + + @@ -182,19 +153,19 @@ - - + + - - + + @@ -213,19 +184,19 @@ - - + + - - + + @@ -237,6 +208,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -263,19 +315,19 @@ - - + + - - + + diff --git a/vs-build/FAST/FAST.vfproj b/vs-build/FAST/FAST.vfproj index 80f4de20ce..3d8933dc7d 100644 --- a/vs-build/FAST/FAST.vfproj +++ b/vs-build/FAST/FAST.vfproj @@ -5,7 +5,7 @@ - + @@ -15,7 +15,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -35,7 +35,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -65,7 +65,7 @@ - + @@ -75,7 +75,7 @@ - + diff --git a/vs-build/FASTlib/FASTlib.vfproj b/vs-build/FASTlib/FASTlib.vfproj index dfc78ed546..e72b055d81 100644 --- a/vs-build/FASTlib/FASTlib.vfproj +++ b/vs-build/FASTlib/FASTlib.vfproj @@ -5,7 +5,7 @@ - + @@ -14,7 +14,7 @@ - + @@ -23,7 +23,7 @@ - + @@ -32,7 +32,7 @@ - + @@ -41,7 +41,7 @@ - + @@ -50,7 +50,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -68,7 +68,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -86,7 +86,7 @@ - + @@ -95,7 +95,7 @@ - + @@ -104,7 +104,7 @@ - + @@ -117,43 +117,76 @@ - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - + - + - + + + @@ -165,25 +198,25 @@ - - + + - - - + - + - + + + @@ -199,25 +232,25 @@ - - + + - - - + - + - + + + @@ -233,25 +266,25 @@ - - + + - - - + - + - + + + @@ -264,28 +297,62 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + - + - + + + @@ -321,25 +388,25 @@ - - + + - - - + - + - + + + @@ -358,25 +425,25 @@ - - + + - - - + - + - + + + @@ -397,25 +464,25 @@ - - + + - - - + - + - + + + @@ -427,25 +494,25 @@ - - + + - - - + - + - + + + @@ -466,25 +533,25 @@ - - + + - - - + - + - + + + @@ -503,25 +570,25 @@ - - + + - - - + - + - + + + @@ -543,25 +610,25 @@ - - + + - - - + - + - + + + @@ -577,25 +644,25 @@ - - + + - - - + - + - + + + @@ -611,25 +678,25 @@ - - + + - - - + - + - + + + @@ -645,25 +712,25 @@ - - + + - - - + - + - + + + @@ -679,25 +746,25 @@ - - + + - - - + - + - + + + @@ -713,50 +780,50 @@ - - + + - - - + - + - + + + - - + + - - - + - + - + + + @@ -781,50 +848,50 @@ - - + + - - - + - + - + + + - - + + - - - + - + - + + + @@ -878,25 +945,25 @@ - - + + - - - + - + - + + + @@ -906,25 +973,25 @@ - - + + - - - + - + - + + + @@ -955,25 +1022,25 @@ - - + + - - - + - + - + + + @@ -989,25 +1056,25 @@ - - + + - - - + - + - + + + @@ -1023,25 +1090,25 @@ - - + + - - - + - + - + + + @@ -1057,25 +1124,25 @@ - - + + - - - + - + - + + + @@ -1091,25 +1158,25 @@ - - + + - - - + - + - + + + @@ -1125,25 +1192,25 @@ - - + + - - - + - + - + + + @@ -1159,25 +1226,25 @@ - - + + - - - + - + - + + + @@ -1193,25 +1260,25 @@ - - + + - - - + - + - + + + @@ -1239,25 +1306,25 @@ - - + + - - - + - + - + + + @@ -1276,25 +1343,25 @@ - - + + - - - + - + - + + + @@ -1313,25 +1380,25 @@ - - + + - - - + - + - + + + @@ -1351,25 +1418,25 @@ - - + + - - - + - + - + + + @@ -1389,25 +1456,25 @@ - - + + - - - + - + - + + + @@ -1415,50 +1482,50 @@ - - + + - - - + - + - + + + - - + + - - - + - + - + + + @@ -1488,42 +1555,42 @@ - + - + - - + + - - + + - - - + - + - + + + @@ -1542,25 +1609,25 @@ - - + + - - - + - + - + + + @@ -1576,25 +1643,25 @@ - - + + - - - + - + - + + + @@ -1618,25 +1685,25 @@ - - + + - - - + - + - + + + @@ -1658,25 +1725,25 @@ - - + + - - - + - + - + + + diff --git a/vs-build/RunRegistry.bat b/vs-build/RunRegistry.bat index 4421983928..4579fb2fe0 100644 --- a/vs-build/RunRegistry.bat +++ b/vs-build/RunRegistry.bat @@ -143,6 +143,12 @@ SET Output_Loc=%CURR_LOC% %REGISTRY% "%CURR_LOC%\UnsteadyAero_Registry.txt" -I "%NWTC_Lib_Loc%" -I "%CURR_LOC%" -O "%Output_Loc%" GOTO checkError +:FVW +SET CURR_LOC=%AD_Loc% +SET Output_Loc=%CURR_LOC% +%REGISTRY% "%CURR_LOC%\FVW_Registry.txt" -I "%NWTC_Lib_Loc%" -I "%CURR_LOC%" -O "%Output_Loc%" +GOTO checkError + :AeroDyn14 SET CURR_LOC=%AD14_Loc% SET Output_Loc=%CURR_LOC% @@ -301,4 +307,4 @@ SET ALL_FAST_Includes= echo %lines% set lines= -:PathsOnly \ No newline at end of file +:PathsOnly From 5f31f3bd94cd2ab18840169101b7c594a628caba Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 3 Apr 2020 11:16:42 -0600 Subject: [PATCH 105/190] FVW: glue code outputs in global, possibility for FVW to output in both --- modules/aerodyn/src/FVW.f90 | 14 +++++++++----- modules/aerodyn/src/FVW_VTK.f90 | 7 +++---- modules/openfast-library/src/FAST_Subs.f90 | 14 +++++++++++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index b78d092138..cdfb47d97e 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -730,7 +730,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ! All of the calculated output channels are placed into the m%AllOuts(:), while the channels selected for outputs are ! placed in the y%WriteOutput(:) array. !.................................................................................................................................. - use VTK, only: set_vtk_coordinate_transform + use VTK, only: set_vtk_coordinate_transform, set_vtk_no_coordinate_transform real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(FVW_InputType), intent(in ) :: u !< Inputs at Time t type(FVW_ParameterType), intent(in ) :: p !< Parameters @@ -812,12 +812,16 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, endif if ( ( t - m%VTKlastTime ) >= p%DTvtk*OneMinusEpsilon ) then m%VTKlastTime = t - if (p%VTKCoord==2) then - ! Hub reference coordinates, for export only - ! ALL VTK Will be exported in this coordinate system! + if ((p%VTKCoord==2).or.(p%VTKCoord==3)) then + ! Hub reference coordinates, for export only, ALL VTK Will be exported in this coordinate system! call set_vtk_coordinate_transform(u%HubOrientation,u%HubPosition) + call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Hub', m%VTKstep, 9) + endif + if ((p%VTKCoord==1).or.(p%VTKCoord==3)) then + ! Global coordinate system, ALL VTK will be exported in global + call set_vtk_no_coordinate_transform() + call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Glb', m%VTKstep, 9) endif - call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW', m%VTKstep, 9) endif endif m%VTKstep = m%VTKstep + 1 ! Increment VTK counter no matter what diff --git a/modules/aerodyn/src/FVW_VTK.f90 b/modules/aerodyn/src/FVW_VTK.f90 index 009423002b..4dfa005741 100644 --- a/modules/aerodyn/src/FVW_VTK.f90 +++ b/modules/aerodyn/src/FVW_VTK.f90 @@ -13,8 +13,6 @@ module VTK integer, save :: nData=0; integer, save :: nPoints=0; - logical, save :: bOverWritWarned = .true. - logical, save :: bBinary = .false. character(len=255), save :: buffer @@ -66,13 +64,14 @@ subroutine set_vtk_binary_format(bBin) subroutine set_vtk_coordinate_transform(T_g2b_in,PO_g_in) real(ReKi),dimension(3,3), intent(in) :: T_g2b_in real(ReKi),dimension(3) , intent(in) :: PO_g_in - ! bChangeFrame=.true. T_g2b=T_g2b_in PO_g=PO_g_in end subroutine - + subroutine set_vtk_no_coordinate_transform() + bChangeFrame=.false. + end subroutine logical function vtk_new_ascii_file(filename,label) !use MainIO, only: get_free_unit ,check_io diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 436ee6b7fd..209583ab64 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -4959,6 +4959,7 @@ END SUBROUTINE FillOutputAry !> This routine writes all the committed meshes to VTK-formatted files. It doesn't bother with returning an error code. SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) use FVW_IO, only: WrVTK_FVW + use VTK, only: set_vtk_no_coordinate_transform TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(IN ) :: y_FAST !< Output variables for the glue code @@ -5122,7 +5123,10 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O ! Free wake !FIXME: Should the wake info be in a different routine? if (allocated(AD%m%FVW_u)) then - if (allocated(AD%m%FVW_u(1)%WingsMesh)) call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + if (allocated(AD%m%FVW_u(1)%WingsMesh)) then + call set_vtk_no_coordinate_transform() ! Output in global coords + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + endif end if END IF @@ -5323,6 +5327,7 @@ END SUBROUTINE WrVTK_BasicMeshes !! returning an error code. SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) use FVW_IO, only: WrVTK_FVW + use VTK, only: set_vtk_no_coordinate_transform REAL(DbKi), INTENT(IN ) :: t_global !< Current global time TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code @@ -5406,8 +5411,11 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, A ! Free wake !FIXME: is there a better way of checking? - if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + if (allocated(AD%m%FVW_u)) then + if (allocated(AD%m%FVW_u(1)%WingsMesh)) then + call set_vtk_no_coordinate_transform() ! Output in global coords + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + end if end if ! Tower motions From 5d69e33409d4f17cee76de8b2eaf06934b865407 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 3 Apr 2020 16:11:25 -0600 Subject: [PATCH 106/190] FVW: add documentation --- .../user/aerodyn-fvw/Acknowledgments.rst | 19 + docs/source/user/aerodyn-fvw/Acronyms.rst | 55 ++ docs/source/user/aerodyn-fvw/AppendixA.rst | 16 + docs/source/user/aerodyn-fvw/AppendixB.rst | 11 + docs/source/user/aerodyn-fvw/AppendixC.rst | 24 + .../user/aerodyn-fvw/ExampleFile--OLAF.txt | 30 + .../ExampleFile--PrescribeCirc.txt | 26 + docs/source/user/aerodyn-fvw/FutureWork.rst | 31 + docs/source/user/aerodyn-fvw/InputFiles.rst | 176 +++++ docs/source/user/aerodyn-fvw/Introduction.rst | 100 +++ docs/source/user/aerodyn-fvw/OLAFTheory.rst | 722 ++++++++++++++++++ docs/source/user/aerodyn-fvw/OutputFiles.rst | 23 + docs/source/user/aerodyn-fvw/RunningOLAF.rst | 175 +++++ .../Schematics/FVWwithOpenFAST.pdf | Bin 0 -> 43255 bytes .../Schematics/FilamentRegularization.pdf | Bin 0 -> 20732 bytes .../Schematics/FilamentRegularization.png | Bin 0 -> 31008 bytes .../Schematics/LagrangianMarkers.pdf | Bin 0 -> 63510 bytes .../user/aerodyn-fvw/Schematics/OpenFAST.pdf | Bin 0 -> 48072 bytes .../user/aerodyn-fvw/Schematics/Stencil.pdf | Bin 0 -> 11847 bytes .../Schematics/VortexCodeWorkFlow.pdf | Bin 0 -> 5268 bytes .../Schematics/VortexCodeWorkFlow.tex | 80 ++ docs/source/user/aerodyn-fvw/bibliography.bib | 431 +++++++++++ docs/source/user/aerodyn-fvw/index.rst | 43 ++ docs/source/user/aerodyn-fvw/zrefs.rst | 9 + docs/source/user/index.rst | 1 + 25 files changed, 1972 insertions(+) create mode 100644 docs/source/user/aerodyn-fvw/Acknowledgments.rst create mode 100644 docs/source/user/aerodyn-fvw/Acronyms.rst create mode 100644 docs/source/user/aerodyn-fvw/AppendixA.rst create mode 100644 docs/source/user/aerodyn-fvw/AppendixB.rst create mode 100644 docs/source/user/aerodyn-fvw/AppendixC.rst create mode 100644 docs/source/user/aerodyn-fvw/ExampleFile--OLAF.txt create mode 100644 docs/source/user/aerodyn-fvw/ExampleFile--PrescribeCirc.txt create mode 100644 docs/source/user/aerodyn-fvw/FutureWork.rst create mode 100644 docs/source/user/aerodyn-fvw/InputFiles.rst create mode 100644 docs/source/user/aerodyn-fvw/Introduction.rst create mode 100644 docs/source/user/aerodyn-fvw/OLAFTheory.rst create mode 100644 docs/source/user/aerodyn-fvw/OutputFiles.rst create mode 100644 docs/source/user/aerodyn-fvw/RunningOLAF.rst create mode 100644 docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.pdf create mode 100644 docs/source/user/aerodyn-fvw/Schematics/FilamentRegularization.pdf create mode 100644 docs/source/user/aerodyn-fvw/Schematics/FilamentRegularization.png create mode 100644 docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.pdf create mode 100644 docs/source/user/aerodyn-fvw/Schematics/OpenFAST.pdf create mode 100644 docs/source/user/aerodyn-fvw/Schematics/Stencil.pdf create mode 100644 docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.pdf create mode 100644 docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.tex create mode 100644 docs/source/user/aerodyn-fvw/bibliography.bib create mode 100644 docs/source/user/aerodyn-fvw/index.rst create mode 100644 docs/source/user/aerodyn-fvw/zrefs.rst diff --git a/docs/source/user/aerodyn-fvw/Acknowledgments.rst b/docs/source/user/aerodyn-fvw/Acknowledgments.rst new file mode 100644 index 0000000000..3656506102 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/Acknowledgments.rst @@ -0,0 +1,19 @@ +.. _Acknowledgments: + +Acknowledgments +=============== + +This work was authored by the National Renewable Energy Laboratory, +operated by Alliance for Sustainable Energy, LLC, for the U.S. +Department of Energy (DOE) under Contract No. DE-AC36-08GO28308. Funding +provided by the U.S. Department of Energy Office of Energy Efficiency +and Renewable Energy Wind Energy Technologies Office. The views +expressed in the article do not necessarily represent the views of the +DOE or the U.S. Government. The U.S. Government retains and the +publisher, by accepting the article for publication, acknowledges that +the U.S. Government retains a nonexclusive, paid-up, irrevocable, +worldwide license to publish or reproduce the published form of this +work, or allow others to do so, for U.S. Government purposes. + +The authors are also grateful to the Big Adaptive Rotor program for +supporting the development of this software. diff --git a/docs/source/user/aerodyn-fvw/Acronyms.rst b/docs/source/user/aerodyn-fvw/Acronyms.rst new file mode 100644 index 0000000000..d3a30a6c6c --- /dev/null +++ b/docs/source/user/aerodyn-fvw/Acronyms.rst @@ -0,0 +1,55 @@ +.. _Acronyms: + +List of Symbols +=============== + ++-----------------------------+---------------------------------------+ +| :math:`a_1` | numerical constant | +| | :math:`=2\times10^{-4}` | ++-----------------------------+---------------------------------------+ +| BEM | blade-element momentum | ++-----------------------------+---------------------------------------+ +| CFD | computational fluid dynamics | ++-----------------------------+---------------------------------------+ +| DOE | U.S. Department of Energy | ++-----------------------------+---------------------------------------+ +| :math:`F_c` | core radius factor | ++-----------------------------+---------------------------------------+ +| :math:`t` | time | ++-----------------------------+---------------------------------------+ +| FVW | free vortex wake | ++-----------------------------+---------------------------------------+ +| :math:`N` | number of rotor revolutions before | +| | wake cutoff condition | ++-----------------------------+---------------------------------------+ +| :math:`\vec{r}` | vector between point of interest and | +| | vortex segment | ++-----------------------------+---------------------------------------+ +| :math:`\vec{r}(\psi,\zeta)` | position vector of Lagrangian markers | ++-----------------------------+---------------------------------------+ +| :math:`r_c` | core radius | ++-----------------------------+---------------------------------------+ +| :math:`r_{c0}` | initial core radius | ++-----------------------------+---------------------------------------+ +| OLAF | cOnvecting LAgrangian Filaments | ++-----------------------------+---------------------------------------+ +| :math:`\alpha` | numerical constant :math:`=1.25643` | ++-----------------------------+---------------------------------------+ +| :math:`\Gamma` | circulation strength | ++-----------------------------+---------------------------------------+ +| :math:`\delta` | measure of viscous diffusion | ++-----------------------------+---------------------------------------+ +| :math:`\epsilon` | measure of strain | ++-----------------------------+---------------------------------------+ +| :math:`\Delta \psi` | step size for blade rotation | ++-----------------------------+---------------------------------------+ +| :math:`\Omega` | rotational speed of wind turbine | ++-----------------------------+---------------------------------------+ +| :math:`\zeta` | vortex wake age | ++-----------------------------+---------------------------------------+ +| :math:`\zeta_0` | vortex wake age offset | ++-----------------------------+---------------------------------------+ +| :math:`\nu` | kinematic viscosity | ++-----------------------------+---------------------------------------+ +| :math:`\psi` | azimuth blade position | ++-----------------------------+---------------------------------------+ diff --git a/docs/source/user/aerodyn-fvw/AppendixA.rst b/docs/source/user/aerodyn-fvw/AppendixA.rst new file mode 100644 index 0000000000..8337463cba --- /dev/null +++ b/docs/source/user/aerodyn-fvw/AppendixA.rst @@ -0,0 +1,16 @@ +.. _OLAF-Primary-Input-File: + +Appendix A: OLAF Primary Input File +=================================== + +Note that when a line in the following text starts with an indent, it is +really a continuation of the previous line and should not be implemented +as a new line in the actual input file. + +**Check the regression test cases for updates to this input file.** + +.. container:: + :name: Tab:OLAFinputfile + + .. literalinclude:: ExampleFile--OLAF.txt + :linenos: diff --git a/docs/source/user/aerodyn-fvw/AppendixB.rst b/docs/source/user/aerodyn-fvw/AppendixB.rst new file mode 100644 index 0000000000..03580bcba3 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/AppendixB.rst @@ -0,0 +1,11 @@ +.. _Prescribed-Circulation-Input-File: + +Appendix B: Prescribed Circulation Input File +================================= + +**Check the regression tests for updated versions of this file.** + +.. container:: + :name: Tab:PrescribeCirc + .. literalinclude:: ExampleFile--PrescribeCirc.txt + :linenos: diff --git a/docs/source/user/aerodyn-fvw/AppendixC.rst b/docs/source/user/aerodyn-fvw/AppendixC.rst new file mode 100644 index 0000000000..f130bfe4d6 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/AppendixC.rst @@ -0,0 +1,24 @@ +.. _OLAF-List-of-Output-Channels: + +Appendix C: OLAF List of Output Channels +============================ + +This is a list of all possible output parameters from the OLAF module. +The names are grouped by meaning, but can be ordered in the OUTPUTS +section of the *AeroDyn15* primary input file, as the user sees fit. +:math:`N\beta` refers to output node, :math:`\beta`, where :math:`\beta` +is a number in the range [1,9], corresponding to entry, :math:`\beta`, +in the **OutNd** list. :math:`B\alpha` is prefixed to each output name, +where :math:`\alpha` is a number in the range [1,3], corresponding to +the blade number. + +.. container:: + :name: Tab:OLAFoutputs + + .. table:: Available OLAF Output Channels + + ============================ ============= =========================== + Channel Name(s) Units Description + ============================ ============= =========================== + :math:`Gamma \beta B \alpha` :math:`m^2/s` Circulation along the blade + ============================ ============= =========================== diff --git a/docs/source/user/aerodyn-fvw/ExampleFile--OLAF.txt b/docs/source/user/aerodyn-fvw/ExampleFile--OLAF.txt new file mode 100644 index 0000000000..2d7bf1a03f --- /dev/null +++ b/docs/source/user/aerodyn-fvw/ExampleFile--OLAF.txt @@ -0,0 +1,30 @@ +------- FREE WAKE INPUT FILE ------------------------------------------- +Free wake input file for the BAR turbine +------- GENERAL OPTIONS ------------------------------------------- +5 IntMethod Integration method {4: 2nd order Predictor/Corrector, 5: Forward Euler 1st order, "default": 5} (switch) +0.2 DTfvw Time interval for wake propagation. {"default": dtaero} (sec) +5 FreeWakeStart Time when wake is "free". (-) value = always free. {"default":0.0} (sec) +2.0 FullCircStart Time at which full circulation is reached. {"default": 0.0} (sec) +------- CIRCULATION SPECIFICATIONS ------------------------------------------- +1 CircSolvingMethod Circulation solving method {1: Cl-Based, 2: No-Flow Through, 3: Prescribed, "default":1 }(switch) +0.01 CircSolvConvCrit Convergence criteria {"default": 0.01} [only if CircSolvingMethod=1] (m$^2$/s) +0.1 CircSolvRelaxation Relaxation factor {"default": 0.1} [only if CircSolvingMethod=1] (-) +30 CircSolvMaxIter Maximum number of iterations for circulation solving {"default": 30} (-) +"circ.csv" PrescribedCircFile File containing prescribed circulation [only if CircSolvingMethod=3] (quoted string) +------- WAKE OPTIONS ------------------------------------------- +50 nNWPanel Number of near-wake panels (-) +7 WakeLength Total wake distance (D) +5 FreeWakeLength Wake length that is "free" (D) +False FWShedVorticity Include shed vorticity in the far wake {"default": 0.1} +0 DiffusionMethod Diffusion method to account for viscous effects {0=None, 1=Core Spreading, "default": 0} +0 RegDeterMethod Method to determine the regularization parameters {0=Manual, 1=Optimized, "default": 0 } +2 RegFunction Viscous diffusion function {0: None, 1: Rankine, 2: LambOseen, 3: Vatistas, 4: Denominator, "default": 3} (switch) +0 WakeRegMethod Wake regularization method {1: Constant, 2: Stretching, 3: Age, "default": 1} (switch) +2.0 WakeRegFactor Wake regularization factor +2.0 WingRegFactor Wing regularization factor +100 CoreSpreadEddyVisc Eddy viscosity in core spreading methods, typical values 1-1000 +------- OUTPUT OPTIONS ------------------------------------------- +True WrVTk Outputs Visualization Toolkit (VTK) (independent of .fst option) (False: NoVTK, True: Write VTK at each time step) (flag) +1 nVTKBlades Number of blades for which VTK files are exported (0: No VTK per blade, n: VTK for blade 1 to n) (-) +2 VTKCoord Coordinate system used for VTK export. {1=Global, 2=Hub, "default": 1} +default VTK_fps Frame rate for VTK output (frames per second) {"all" for all glue code timesteps, "default" for all FVW timesteps} [used only if WrVTK=1] diff --git a/docs/source/user/aerodyn-fvw/ExampleFile--PrescribeCirc.txt b/docs/source/user/aerodyn-fvw/ExampleFile--PrescribeCirc.txt new file mode 100644 index 0000000000..aebd02d869 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/ExampleFile--PrescribeCirc.txt @@ -0,0 +1,26 @@ +r/R [-], Gamma [m$^2$/s] +0.048488, 0.000000 +0.087326, 0.442312 +0.126163, 6.909277 +0.165000, 23.678557 +0.203837, 55.650700 +0.242674, 74.091529 +0.281512, 84.205843 +0.320349, 88.740429 +0.359186, 89.730814 +0.398023, 88.568114 +0.436860, 87.114743 +0.475698, 86.110557 +0.514535, 85.705529 +0.553372, 85.215829 +0.592209, 84.547371 +0.631047, 83.774329 +0.669884, 82.889157 +0.708721, 81.635600 +0.747558, 79.788700 +0.786395, 77.195200 +0.825233, 73.765100 +0.864070, 69.275900 +0.902907, 62.965400 +0.941744, 53.603300 +0.980581, 39.854000 diff --git a/docs/source/user/aerodyn-fvw/FutureWork.rst b/docs/source/user/aerodyn-fvw/FutureWork.rst new file mode 100644 index 0000000000..53820f8332 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/FutureWork.rst @@ -0,0 +1,31 @@ +.. _Future-Work: + +Future Work +=========== + +This first implementation phase focused on single-turbine capabilities, +fulfilling the basic requirements for the design of large and novel +rotor concepts. Future development work will turn toward the +implementation of features enabling multiple-turbine simulations on +medium-to-large-scale computational clusters. The reduction of the +computational time will also be of focus. This may be achieved using +tree techniques such as the fast multipole method. Further algorithmic +options, such as vortex amalgamation in the far wake, will be considered +to speed up the simulation. The framework presented in this manual is +compatible with grid-free or grid-based vortex particle formulations. +Such particle-based implementations will also be envisaged in the +future. Further validation of the code against measurements and +higher-order tools will be pursued. Applications to cases known to be +challenging for the BEM algorithm will also be investigated, such as +highly flexible rotors, offshore floating turbines, small-scale wind +farms, multiple-rotor turbines, or kites. + +The following list contains future work on OLAF software: + +- Lagrangian particles + +- Multiple turbines, integration into FAST.Farm + +- Code speed-up + +- Dedicated dynamic stall model. diff --git a/docs/source/user/aerodyn-fvw/InputFiles.rst b/docs/source/user/aerodyn-fvw/InputFiles.rst new file mode 100644 index 0000000000..09044cb6fb --- /dev/null +++ b/docs/source/user/aerodyn-fvw/InputFiles.rst @@ -0,0 +1,176 @@ +.. _Input-files: + +Input Files +=========== + +No lines should be added or removed from the input files, except in +tables where the number of rows is specified. + +Units +----- + +OLAF uses the International System of Units (e.g., kg, m, s, N). Angles +are assumed to be in degrees unless otherwise specified. + +OLAF Primary Input File +----------------------- + +The primary OLAF input file defines general free wake options, +circulation model selection and specification, near- and far-wake +length, and wake visualization options. The file is organized into +several functional sections. Each section corresponds to an aspect of +the OLAF model. For most parameters, the user may specify the value +"default" (with or without quotes), in which case a default value, +defined below, is used by the program. + +A sample OLAF primary input file is given in +Appendix `[app:input] <#app:input>`__. + +General Options +~~~~~~~~~~~~~~~ + +[switch] specifies which integration method will be used to convect the +Lagrangian markers. There are four options: 1) fourth-order Runge-Kutta +, 2) fourth-order Adams-Bashforth , 3) fourth-order +Adams-Bashforth-Moulton , or 4) first-order forward Euler . The default +value is . These methods are specified in +Section `[sec:vortconv] <#sec:vortconv>`__. + +[sec] specifies at what time the wake evolution is classified as “free." +Before this point is reached, the Lagrangian markers are simply +convected with the freestream velocity. After this point, induced +velocities are computed and affect the marker convection. If a negative +time is given, the wake is “free" from the beginning of the simulation. +The default value is :math:`0`. + +[sec] specifies at what time the blade circulation reaches its full +strength. If this value is specified to be :math:`>0`, the circulation +is multiplied by a factor equal to :math:`0` at :math:`t=0`, to a factor +equal to :math:`1` for :math:`t>`. The default value is :math:`0`. + +[sec] specifies the time interval at which the module will update the +wake. The time interval needs to be a multiple of the time step used by +the glue code. The blade circulation is still updated at each +intermediate time steps based on the intermediate blades positions and +wind velocities. The default value is , where is the time step used by +AeroDyn. + +Circulation Specifications +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[switch] specifies which circulation method is used. There are three +options: 1) :math:`C_l`-based iterative procedure **[1]**, 2) no-flow +through **[2]**, or 3) prescribed **[3]**. The default value is . These +methods are described in Section `[sec:circ] <#sec:circ>`__. + +[-] specifies the dimensionless convergence criteria used for solving +the circulation. This variable is only used if = . The default value is +:math:`0.01`, corresponding to :math:`1\%` error in the circulation +between two iterations. + +[-] specifies the relaxation factor used for solving the circulation. +This variable is only used if = . The default value is +:math:`\alpha=0.1`. + +[-] specifies the maximum number of iterations used for solving the +circulation. This variable is only used if = . The default value is +:math:`30`. + +[quoted string] specifies the file containing the prescribed blade +circulation. This option is only used if = . The circulation file format +is a delimited file with one header line and two columns. The first +column is the dimensionless radial position [r/R]; the second column is +the bound circulation value in [m\ :math:`^2`/s]. + +Wake Options +~~~~~~~~~~~~ + +[-] specifies the number of time steps for which the near-wake lattice +is computed. In the future, the possibility to define this input as an +azimuthal span in degrees or a downstream distance in rotor diameter +will be considered. + +[D] specifies the length, in rotor diameters, of the far wake. The +default value is :math:`8`. + +[D] specifies the length, in rotor diameters, for which the turbine wake +is convected as “free." If is greater than , then the entire wake is +free. Otherwise, the Lagrangian markers located within the buffer zone +delimited by and are convected with the average velocity. The default +value is :math:`6`. + +[flag] specifies whether shed vorticity is included in the far wake. The +default value is , specifying that the far wake consists only of the +trailed vorticity from the root and tip vortices. + +[switch] specifies which diffusion method is used to account for viscous +diffusion. There are two options: 1) no diffusion or 2) the +core-spreading method . The default value is . + +Regularization options +~~~~~~~~~~~~~~~~~~~~~~ + +[switch] specifies which method is used to determine the regularization +parameters. There are two options: 1) manual or 2) optimized . The +manual option requires the user to specify the parameters listed in this +subsection. The optimized option determines the parameters for the user. +The default value is . + +| [switch] specifies the regularization function used to remove the + singularity of the vortex elements, as specified in + Section `[sec:vortconv] <#sec:vortconv>`__. There are five options: + (1) no correction , (2) the Rankine method , (3) the Lamb-Oseen method + , (4) the Vatistas method , or (5) the denominator offset method +| val4. The functions are given in . The default value is . + +[switch] specifies the method of determining viscous core radius (i.e., +the regularization parameter). There are three options: (1) constant +**[1]**, (2) stretching **[2]**, or (3) age **[3]**. The methods are +described in . The default value is . + +[-] specifies the wake regularization parameter, which is the +regularization value used at the initialization of a vortex element. If +the regularization method is “constant”, this value is used throughout +the wake. + +[-] specifies the bound vorticity regularization parameter, which is the +regularization value used for the vorticity elements bound to the +blades. + +[-] specifies the eddy viscosity parameter :math:`\delta` used for the +core-spreading method (=) or the regularization method with age (=). The +variable :math:`\delta` is described in . The default value is +:math:`100`. + +Output Options +~~~~~~~~~~~~~~ + +[flag] specifies if Visualization Toolkit (VTK) visualization files are +to be written out. = does not write out any VTK files. = outputs a VTK +file at every time step. The outputs are written in the folder, +``vtk_fvw.`` + +[-] specifies how many blade VTK files are to be written out. +:math:`= n` outputs VTK files for :math:`n` blades, with :math:`0` being +an acceptable value. The default value is :math:`1`. + +[switch] specifies in which coordinate system the VTK files are written. +There are two options: 1) global coordinate system or 2) hub coordinate +system . The default value is . + +[:math:`1`/sec] specifies the output frequency of the VTK files. The +value provided is rounded to the nearest allowable multiple of the time +step. The default value is :math:`1/dt_\text{fvw}`. Specifying =, will +be equivalent to using the value :math:`1/dt_\text{aero}`. + +AeroDyn15 Input File +-------------------- + +As OLAF is incorporated into the *AeroDyn15* module, a wake computation +option has been added to the *AeroDyn15* input file and a line has been +added. These additions are as follows. + +specifies the type of wake model that is used. = has been added to allow +the user to switch from the traditional BEM method to the FVW method. + +[string] specifies the OLAF module file, including the path to the file. diff --git a/docs/source/user/aerodyn-fvw/Introduction.rst b/docs/source/user/aerodyn-fvw/Introduction.rst new file mode 100644 index 0000000000..e8b6d02129 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/Introduction.rst @@ -0,0 +1,100 @@ +.. _Introduction: + +Introduction +============ + +cOnvecting LAgrangian Filaments (OLAF) is a free vortex wake (FVW) +module used to compute the aerodynamic forces on a set of moving wings, +which, in particular, can be applied to two- or three-bladed +horizontal-axis wind turbines. This module has been incorporated into +the National Renewable Energy Laboratory physics-based engineering tool, +OpenFAST, which solves the aero-hydro-servo-elastic dynamics of +individual wind turbines. OLAF is incorporated into the OpenFAST module, +*AeroDyn15*, as an alternative to the traditional blade-element momentum +(BEM) option, as shown in Figure `[OpenFAST] <#OpenFAST>`__. + +Incorporating the FVW module within OpenFAST allows for the modeling of +highly flexible turbines along with the aero-hydro-servo-elastic +response capabilities of OpenFAST. The OLAF module follows the +requirements of the OpenFAST modularization framework  +(:cite:`Sprague15-1, Jonkman13-1`). + +The OLAF module uses a lifting-line representation of the blades, which +is characterized by a distribution of bound circulation. The spatial and +time variation of the bound circulation results in free vorticity being +emitted in the wake. OLAF solves for the turbine wake in a time-accurate +manner, which allows the vortices to convect, stretch, and diffuse. The +FVW model is based on a Lagrangian approach, in which the turbine wake +is discretized into Lagrangian markers. There are many methods of +representing the wake with Lagrangian +markers (:cite:`Branlard17-1`). In this work, a hybrid +lattice/filament method is used, as depicted in +Figure `1.1 <#Lagrangian>`__. + +.. figure:: Schematics/LagrangianMarkers.pdf + :alt: Evolution of near-wake lattice, blade-tip vortex, and + Lagrangian markers + :name: Lagrangian + + Evolution of near-wake lattice, blade-tip vortex, and Lagrangian + markers + +Here, the position of the Lagrangian markers is defined in terms of wake +age, :math:`\zeta`, and azimuthal position, :math:`\psi`. A lattice +method is used in the near wake of the blade. The near wake spans over a +user-specified angle or distance for nonrotating cases. Though past +research has indicated that a near-wake region of :math:`30^\circ` is +sufficient (:cite:`Leishman-book, Ananthan02-1`), it has +been shown that a larger near wake is required for high thrust and other +challenging conditions. After this period, the wake is assumed to +instantaneously roll up into a tip vortex and, optionally, a root +vortex, which are assumed to be the most dominant features for the +remainder of the wake (:cite:`Leishman02-1`). Each +Lagrangian marker is connected to adjacent markers by straight-line +vortex filaments, approximated to second-order +accuracy (:cite:`Gupta05-1`). The wake is discretized based +on the spanwise location of the blade sections and a specified time step +(:math:`dt`), which may be different from the time step of AeroDyn. +After an optional initialization period, the wake is allowed to move and +distort, thus changing the wake structure as the markers are convected +downstream. To limit computational expense, the tip vortex is truncated +after a specified distance (:math:`d_\text{trunc}`) downstream of the +turbine. The wake truncation violates Helmholtz’s first law and hence +introduces an erroneous boundary condition. To alleviate this, the wake +is "frozen" in a buffer zone between a distance, +:math:`d_\text{buffer}`, and the distance, :math:`d_\text{trunc}`. In +this buffer zone, the markers convect at the average ambient velocity. +In this way, truncation error is +minimized (:cite:`Leishman02-1`). The buffer zone is +typically chosen as the convected distance over one rotor revolution. + +As part of OpenFAST, induced velocities at the lifting line/blade are +transferred to *AeroDyn15* and used to compute the effective blade angle +of attack at each blade section, which is then used to compute the +aerodynamic forces on the blades. The FVW method returns the same +information as the BEM method, but allows for more accurate calculations +in areas where BEM assumptions are violated. As the FVW method is more +computationally expensive than BEM, both methods remain available in +OpenFAST, and the user may specify in the *AeroDyn15* input file which +method is used. + +The OLAF input file defines the wake convection and circulation solution +methods; wake size and length options; the Lagrangian marker +regularization (viscous core) method; and other simulation and output +parameters. The extent of the near and far wake are specified by a +nondimensional length in terms of rotor diameter. Different +regularization functions for the vortex elements are available. +Additionally, different methods to compute the regularization parameters +of the bound and wake vorticity may be selected. In particular, viscous +diffusion may be accounted for by dynamically changing the +regularization parameter. Wake visualization output options are also +available. + +This document is organized as follows. Section 2 details downloading, +compiling, and running OLAF on common operating systems. Section 3 +describes the OLAF input file and modifications to the *AeroDyn15* input +file. Section 4 details the OLAF output file. Section 5 provides an +overview of the OLAF theory, including the free vortex wake method as +well as integration into the *AeroDyn15* module. Section 6 presents +future work. Example input files and a list of output channels are +detailed in Appendices A, B, and C. diff --git a/docs/source/user/aerodyn-fvw/OLAFTheory.rst b/docs/source/user/aerodyn-fvw/OLAFTheory.rst new file mode 100644 index 0000000000..be132a9f14 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/OLAFTheory.rst @@ -0,0 +1,722 @@ +.. _OLAF_Theory: + +OLAF Theory +=========== + +This section details the FVW method and provides an overview of the +computational method, followed by a brief explanation of its integration +with OpenFAST. + +.. _sec:FVW: + +Free Vortex Wake Model +---------------------- + +.. _sec:vorticityformulation: + +Introduction - Vorticity formulation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The vorticity equation for incompressible homogeneous flows in the +absence of non-conservative force is: + +.. math:: + + \begin{aligned} + \frac{d\vec{\omega}}{dt} = \frac{\partial\vec{\omega}}{\partial{t}} + \underbrace{(\vec{u} \cdot \nabla)}_{\text{convection}}\vec{\omega} = \underbrace{(\vec{\omega}\cdot\nabla)\vec{u}}_{\text{strain}} +\underbrace{\nu\Delta\vec{\omega}}_{\text{diffusion}} \label{eq:vorticityconservationincompr}\end{aligned} + +where :math:`\vec{\omega}` is the vorticity, :math:`\vec{u}` is the +velocity and :math:`\nu` is the viscosity. In free vortex wake methods, +the vorticity equation is used to describe the evolution of the wake +vorticity. Different approximations are introduced to ease its +resolution: the vorticity is projected onto a discrete number of vortex +elements (here vortex filaments), and, the convection and diffusion +steps are treated separately (viscous-splitting). Several complications +yet arises from the method, in particular, the discretization requires a +regularization of the vorticity field (or velocity field) to ensure a +smooth approximation. + +The forces exerted by the blades onto the flow are expressed in +vorticity formulation as well. This vorticity is bound to the blade and +has a circulation associated with the lift force. A lifting-line +formulation is here used to model the bound vorticity. + +The different models of the free vortex code implemented are described +in the following sections. + +.. _sec:discretization: + +Discretization - Projection +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The numerical method uses a finite number of states to model the +continuous vorticity distribution. To achieve this, the vorticity +distribution is projected onto basis function which will be referred to +as vortex elements. Vortex filaments are here used as elements that +represents the vorticity field. A vortex filaments is delimited by two +points and hence assumes a direction formed by these two points. A +vorticity tube, oriented along, say, the unit vector :math:`\vec{e}_x`, +of cross section :math:`dS` and length :math:`l`, can be approximated by +a vortex filament of length :math:`l` oriented along the same direction. +The total vorticity of the tube and the vortex filaments are the same +and related by: + +.. math:: + + \begin{aligned} + \vec{\omega} \, dS = \vec{\Gamma} + %\omega \, dS \, \vec{e}_x = = \Gamma \vec{e}_x + %\rightarrow + %\qquad\end{aligned} + +where :math:`\vec{\Gamma}` is the circulation intensity of the vortex +filament. If the vorticity tubes are complex and occupy a large volumes, +the projection onto vortex filaments is difficult, and the projection +onto vortex particle is more adapted. Yet, assuming the wake is confined +to a thin vorticity layer which defines a velocity jump of know +direction, it is possible to approximate the wake vorticity sheet as a +mesh of vortex filaments. This is the basis of vortex filament wake +methods. Vortex filaments are a singular representation of the vorticity +field, since the occupy a line instead of a volume. To better represent +the vorticity field, the filaments are "inflated", a process referred to +as regularization (see . The regularization of the vorticity field also +regularizes the velocity field and avoids the singularities that would +otherwise occur. + +.. _sec:vortconv: + +Vortex Convection +~~~~~~~~~~~~~~~~~ + +The governing equation of motion for a vortex filament is given by the +convection equation of a Lagrangian marker: + +.. math:: \frac{d\vec{r}}{dt}=\vec{V}(\vec{r},t) \label{VortFilCart} + +where :math:`\vec{r}` is the position of a Lagrangian marker, such as, +one of the vortex filaments extremity. The Lagrangian convection of the +filaments, effectively stretches the filaments, and thus automatically +accounts for the strain part of the vorticity equation. + +.. _sec:vortconvPolar: + +Vortex Convection in Polar Coordinates +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The governing equation of motion for a vortex filament is given by: + +.. math:: \frac{d\vec{r}(\psi,\zeta)}{dt}=\vec{V}[\vec{r}(\psi,\zeta),t]\label{VortFil} + +Using the chain rule, Eq. `[VortFil] <#VortFil>`__ is rewritten as: + +.. math:: \frac{\partial\vec{r}(\psi,\zeta)}{\partial\psi}+\frac{\partial\vec{r}(\psi,\zeta)}{\partial\zeta}=\frac{\vec{V}[\vec{r}(\psi,\zeta),t]}{\Omega}\label{VortFil_expanded} + +where :math:`d\psi/dt=\Omega` and +:math:`d\psi=d\zeta` (:cite`Leishman02_1`). Here, +:math:`\vec{r}(\psi,\zeta)` is the position vector of a Lagrangian +marker, and :math:`\vec{V}[\vec{r}(\psi,\zeta)]` is the velocity. + +At present, two options are available to numerically solve the left-hand +side of Eq. `[VortFil_expanded] <#VortFil_expanded>`__ for the +vortex-filament location. The first option, [**IntMethod=5**], is a +first-order forward Euler method. This is an explicit method solved +using Eq. `[Euler] <#Euler>`__. + +.. math:: + + \label{Euler} + \vec{r}(\psi+\Delta\psi_i,\zeta+\Delta\zeta) = \vec{r}(\psi,\zeta) + \vec{V}(\psi,\zeta) \Delta t + +The second option, [**IntMethod=1**], is a predictor-corrector scheme +that was developed to accommodate variable rotor speed, as shown by the +stencil in Figure `1.1 <#Stencil>`__ (:cite:`Shaler19_2`). + +|Variable rotor-speed stencil used in time-marching predictor-corrector +scheme| + +The difference operators, +:math:`\frac{\partial \vec{r}(\psi,\zeta)}{\partial \zeta}` and +:math:`\frac{\partial \vec{r}(\psi,\zeta)}{\partial \psi}`, are found by +means of a Taylor series expansion about the point, +(:math:`\zeta+\Delta\zeta/2`, :math:`\psi+\Delta\psi/2`). +:math:`\frac{\partial \vec{r}(\psi,\zeta)}{\partial \psi}` is computed +using a two-step backward method and +:math:`\frac{\partial \vec{r}(\psi,\zeta)}{\partial \zeta}` by central +differencing. This results in a scheme that is second-order accurate in +:math:`\zeta` and third-order accurate in :math:`\psi`. The resulting +equations are given as follows: + +.. math:: \frac{\partial\vec{r}(\psi,\zeta)}{\partial\zeta}=\frac{\vec{r}(\psi+\Delta\psi_i,\zeta+\Delta\zeta)-\vec{r}(\psi+\Delta\psi_i,\zeta)+\vec{r}(\psi,\zeta+\Delta\zeta)-\vec{r}(\psi,\zeta)}{2\Delta\zeta} + +.. math:: + + \begin{gathered} + \frac{\partial\vec{r}(\psi,\zeta)}{\partial\psi}=\\ + \bigg\{23\vec{r}(\psi+\Delta\psi_i,\zeta+\Delta\zeta)+23\vec{r}(\psi+\Delta\psi_i,\zeta)-21\vec{r}(\psi,\zeta+\Delta\zeta)-21\vec{r}(\psi,\zeta)\\ + -3\vec{r}(\psi-\Delta\psi_{i-1},\zeta+\Delta\zeta)-3\vec{r}(\psi-\Delta\psi_{i-1},\zeta)+\vec{r}(\psi-\Delta\psi_{i-1}-\Delta\psi_{i-2},\zeta+\Delta\zeta)\\ + +\vec{r}(\psi-\Delta\psi_{i-1}-\Delta\psi_{i-2},\zeta)\bigg\}\bigg\{46\Delta\psi_i+4\Delta\psi_{i-1}-2\Delta\psi_{i-2}\bigg\}^{-1}\end{gathered} + +with variables as defined in Figure `1.1 <#Stencil>`__. The right-hand +side of Eq. `[VortFil_expanded] <#VortFil_expanded>`__ is computed by +averaging the velocities surrounding the point +(:math:`\zeta+\Delta\zeta/2`, :math:`\psi+\Delta\psi/2`). The marker +location is then found by substituting the difference operators and +velocity averaging into Eq. `[VortFil_expanded] <#VortFil_expanded>`__ +and rearranging to obtain: + +.. math:: + + \begin{gathered} + \vec{r}^m(\psi+\Delta\psi_i,\zeta+\Delta\zeta) =\\ + \bigg\{\frac{\vec{V}}{\Omega}-\Big(-\frac{1}{2\Delta\zeta}+\frac{23}{\phi}\Big)\vec{r}^m(\psi+\Delta\psi_i,\zeta)-\Big(\frac{1}{2\Delta\zeta}-\frac{21}{\phi}\Big)\vec{r}^m(\psi,\zeta+\Delta\zeta)\\ + +\Big(\frac{1}{2\Delta\zeta}+\frac{21}{\phi}\Big)\vec{r}^m(\psi,\zeta)+\frac{3}{\phi}\vec{r}^m(\psi-\Delta\psi_{i-1},\zeta+\Delta\zeta)+\frac{3}{\phi}\vec{r}^m(\psi-\Delta\psi_{i-1},\zeta)\\ + -\frac{1}{\phi}\vec{r}^m(\psi-\Delta\psi_{i-1}-\Delta\psi_{i-2},\zeta+\Delta\zeta)-\frac{1}{\phi}\vec{r}^m(\psi-\Delta\psi_{i-1}-\Delta\psi_{i-2},\zeta)\bigg\}\/\bigg\{\frac{1}{2\Delta\zeta}+\frac{23}{\phi}\bigg\}^{-1}\label{predcorr_general}\end{gathered} + +where + +.. math:: + + \begin{aligned} + \vec{V} &= 4V_\infty + +V_{ind}\left(\vec{r}^{m-1}(\psi,\zeta)\right) + +V_{ind}\left(\vec{r}^{m-1}(\psi+\Delta\psi,\zeta)\right) + \nonumber\\ + &\ \ + + V_{ind}\left(\vec{r}^{m-1}(\psi,\zeta+\Delta\zeta)\right) + + V_{ind}\left(\vec{r}^{m-1}(\psi+\Delta\psi,\zeta+\Delta\zeta)\right) + \\ + \phi &= 46\Delta\psi_i+4\Delta\psi_{i-1}-2\Delta\psi_{i-2}\end{aligned} + +Equation `[predcorr_general] <#predcorr_general>`__ is the general form +of the predictor and corrector equations, indicated by the superscript, +:math:`m`. It is first used in the predictive step to compute the +predicted wake position for all Lagrangian markers using initial guess +values for the wake positions (:math:`\vec{r}^m`) and velocity values at +wake positions from the previous time step (:math:`\vec{r}^{m-1}`). The +resulting wake positions are then used as the :math:`m` time step in the +corrector equation to compute the corrected wake position at the current +time step (:math:`\vec{r}^{m+1}`). This process iterates until converged +wake locations are reached. Wake location is assumed to be converged +when the difference in wake position between iterations reaches a value +of less than :math:`0.001` m root mean +square (:cite:`Krista12_1`). This is typically achieved in +two to three iterations. + +Induced Velocity and Velocity Field +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The velocity term on the right-hand side of +Eq. `[VortFilCart] <#VortFilCart>`__ is a nonlinear function of the +vortex position, representing a combination of the freestream and +induced velocities (:cite:`Hansen08_1`). The induced +velocities at point :math:`\vec{x}`, caused by each straight-line +filament, are computed using the Biot-Savart law, which considers the +locations of the Lagrangian markers and the intensity of the vortex +elements (:cite:`Leishman02_1`): + +.. math:: d\vec{v}(\vec{x})=\frac{\Gamma}{4\pi}\frac{d\vec{l}\times\vec{r}}{r^3}\label{BiotSavart} + +Here, :math:`\Gamma` is the circulation strength of the filament, +:math:`\vec{dl}` is an elementary length along the filament, and +:math:`\vec{r}` is the vector between a point on the filament and the +control point :math:`\vec{x}`, and :math:`r=|\vec{r}|` is the norm of +the vector. The integration of the Biot-Savart law along the filament +length, delimited by the points :math:`\vec{x}_1` and :math:`\vec{x}_2` +leads to: + +.. math:: + + \begin{aligned} + \vec{v}(\vec{x}) + %\frac{\Gamma}{4\pi} \r_0\cdot\left( \frac{\r_1}{r_1}-\frac{\r_2}{r_2}\right)\frac{\r_1\times\r_2}{\norm{\r_1\times\r_2}^2}\label{eq:biotsavartline}\\ + % &=\frac{\Gamma}{4\pi} \left(r_1+r_2\right)\left(1-\frac{\r_1\cdot\r_2}{r_1 r_2}\right)\frac{\r_1\times\r_2}{\norm{\r_1\times\r_2}^2}\\ + = F_\nu \frac{\Gamma}{4\pi} \frac{(r_1+r_2)}{r_1r_2(r_1r_2+\vec{r}_1\cdot\vec{r}_2) }\vec{r}_1\times\vec{r}_2 \label{eq:BiotSavartSegment} \end{aligned} + +with :math:`\vec{r}_1= \vec{x}-\vec{x}_1` and +:math:`\vec{r}_2= \vec{x}-\vec{x}_2`. The factor :math:`F_\nu` is a +regularization parameter that will be discussed in . The filament length +is noted :math:`r_0`, where :math:`\vec{r}_0= \vec{x}_2-\vec{x}_1`. The +distance orthogonal to the filament is: + +.. math:: + + \begin{aligned} + \rho = \frac{|\vec{r}_1\times\vec{r}_2|}{r_0}\end{aligned} + +The velocity at any point of the domain is obtained by superposition of +the velocity induced by all vortex filaments, and by superposition of +the main flow, :math:`\vec{V}_0`, (here assumed divergence free): + +.. math:: + + \begin{aligned} + \vec{V}(\vec{x}) = \vec{V}_0 + \sum_{k} \vec{v}_k(\vec{x}) \end{aligned} + +where the sum is over all the vortex filaments, each of intensity +:math:`\Gamma_k`. The intensity of each filament is determined by +spanwise and time changes of the bound circulation, as discussed in . + +.. _sec:Regularization: + +Regularization +~~~~~~~~~~~~~~ + +Regularization and viscous diffusion +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The singularity that occurs in Eq. `[BiotSavart] <#BiotSavart>`__ +greatly affects the numerical accuracy of vortex methods. By +regularizing the “1-over-r” kernel of the Biot-Savart law, it is +possible to obtain a numerical method that converges to the +Navier-Stokes equations. The regularization is used to improve the +regularity of the discrete vorticity field, as compared to the “true” +continuous vorticity field. This regularization is usually obtained by +convolution with a smooth function. In this case, the regularization of +the vorticity field and the velocity field are the same. Some +engineering models also perform regularization by directly introducing +additional terms in the denominator of the Biot-Savart velocity kernel. +The factor, :math:`F_\nu`, was introduced in +Eq. `[eq:BiotSavartSegment] <#eq:BiotSavartSegment>`__ to account for +this regularization. + +In the convergence proofs of vortex methods, regularization and viscous +diffusion are two distinct aspects. It is yet common practice in vortex +filament methods to blur the notion of regularization with the notion of +viscous diffusion. Indeed, for a physical vortex filament, viscous +effects prevent the singularity from occurring and diffuse the vortex +strength with time. The circular zone where the velocity drops to zero +around the vortex is referred to as the vortex core. An increase of +length of the vortex segment will result in a decrease of the vortex +core radius, and conversely for a decrease of length. Diffusion, on the +other hand, continually spreads the vortex radially. + +Because of the previously mentioned analogy, practitioners of vortex +filament methods often refer to regularization as “viscous-core” models +and regularization parameters as “core-radii.” Additionally, viscous +diffusion is often introduced by modifying the regularization parameter +in space and time instead of solving the diffusion from the vorticity +equation. The distinction is made explicit in this document when +clarification is required, but a loose terminology is used when the +context is clear enough. + +Determination of the regularization parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The regularization parameter is both a function of the physics being +modelled (blade boundary layer and wake), and, the choice of +discretization. The parameters at play are thus: the chord length, the +boundary layer height, and the volume that each vortex filament is +approximating. Currently the choice is left to the user +(**RegDetMethod\ =0)**. Empirical results for a rotating blade are found +in the work of Gupta (:cite:`Gupta06_1`). As a guideline, +the regularization parameter may be chosen as twice the average spanwise +discretization of the blade. The current implementation will implement +this guideline when the user chooses **RegDetMethod\ =1**. Further +refinement of this option will be considered in the future. + +.. _sec:RegularizationFunction: + +Regularization functions implemented +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Several regularization functions have been +developed (:cite:`Rankine58_1,Scully75_1,Vatistas91_1`). +At present, five options are available: (1) No correction, (2) the +Rankine method, (3) the Lamb-Oseen method, (4) the Vatistas method, or +(5) the denominator offset method. If no correction method is used, +[**RegFunction=0**], :math:`F_\nu=1`. The remaining methods are detailed +in the following sections. The regularization parameter +(**WakeRegParam**) is noted :math:`r_c` and the distance to the filament +is written :math:`\rho`. The different functions are compared on . + +.. figure:: Schematics/FilamentRegularization.png + :alt: Velocity along a line orthogonal to the vortex filament for different regularization models. + :align: center + :width: 80.0% + :name: FilamentRegularization + + Velocity along a line orthogonal to the vortex filament and passing + through the filament center, for different regularization models, + with :math:`r_c=0.5r_0`. + + +Rankine +''''''' + +The Rankine method (:cite:`Rankine58_1`) is the simplest +regularization model. With this method, the Rankine vortex has a finite +core with a solid body rotation near the vortex center and a potential +vortex away from the center. If this method is used, +[**RegFunction=1**], the viscous core correction is given by +Eq. `[rankine] <#rankine>`__. + +.. math:: + + \label{rankine} + F_\nu= \begin{cases} \rho^2/r_c^2 & 0 < \rho < 1 \\ + 1 & \rho > 1 \end{cases} + +Here, :math:`r_c` is the viscous core radius of a vortex filament, +detailed in Section `1.1.6.4 <#sec:corerad>`__. + +Lamb-Oseen +'''''''''' + +If this method is used, [**RegFunction=2**], the viscous core correction +is given by Eq. `[lamboseen] <#lamboseen>`__. + +.. math:: + + \label{lamboseen} + F_\nu= \bigg[1-\text{exp}(-\frac{\rho^2}{r_c^2})\bigg] + +Vatistas +'''''''' + +If this method is used, [**RegFunction=3**], the viscous core correction +is given by Eq. `[vatistas] <#vatistas>`__. + +.. math:: + + \label{vatistas} + F_\nu + = \frac{\rho^2}{(\rho^{2n}+r_c^{2n})^{1/n}} + = \frac{(\rho/r_c)^2}{(1 + (\rho/r_c)^{2n})^{1/n}} + +Here, :math:`\rho` is the distance from a vortex segment to an arbitrary +point (:cite:`Abedi16_1`). Research from rotorcraft +applications suggests a value of :math:`n=2`, which is used in this +work (:cite:`Bagai93_1`). + +Denominator offset/cut-off +'''''''''''''''''''''''''' + +If this method is used, [**RegFunction=4**], the singularity is removed +by introducing an additive factor in the denominator of , proportional +to the filament length :math:`r_0`: + +.. math:: + + \begin{aligned} + \vec{v}(\vec{x}) + %\frac{\Gamma}{4\pi} \r_0\cdot\left( \frac{\r_1}{r_1}-\frac{\r_2}{r_2}\right)\frac{\r_1\times\r_2}{\norm{\r_1\times\r_2}^2}\label{eq:biotsavartline}\\ + % &=\frac{\Gamma}{4\pi} \left(r_1+r_2\right)\left(1-\frac{\r_1\cdot\r_2}{r_1 r_2}\right)\frac{\r_1\times\r_2}{\norm{\r_1\times\r_2}^2}\\ + = \frac{\Gamma}{4\pi} \frac{(r_1+r_2)}{r_1r_2(r_1r_2+\vec{r}_1\cdot\vec{r}_2) + r_c^2 r_0^2} \vec{r}_1\times\vec{r}_2 \label{eq:BiotSavartSegment} \end{aligned} + +In this case, :math:`F_\nu=1`. The method is found in the work of van +Garrel (:cite:`Garrel03_1`). + +.. _sec:corerad: + +Time Evolution of the Regularization Parameter–Core Spreading Method +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are four available methods by which the regularization parameter +may evolve with time: (1) constant value, (2) stretching, (3) wake age, +or (4) stretching and wake age. The three latter methods blend the +notion of viscous diffusion with the notion of regularization. The +notation :math:`r_{c0}` used in this section corresponds to input file +parameter value . + +Constant +'''''''' + +If a constant value is selected [**WakeRegMethod=0**], the value of +:math:`r_c` remains unchanged for all Lagrangian markers throughout the +simulation and taken as the value given with the parameter in meter. + +.. math:: r_c(\zeta) = r_{c0}\label{cst} + +where :math:`\zeta` is the vortex wake age, measured from its emission +time. + +Stretching +'''''''''' + +If the stretching method is selected, [**WakeRegMethod=1**], the viscous +core radius is modeled by Eq. `[stretch] <#stretch>`__. + +.. math:: r_c(\zeta,\epsilon) = \sqrt{r_{c0}^2+\int_0^\zeta(1+\epsilon)^{-1}d\zeta}\label{stretch} + +.. math:: \epsilon = \frac{\Delta l}{l} + +where :math:`\epsilon` is the vortex-filament strain, and :math:`l` is +the filament length, and :math:`\Delta l` is the change of length +between two time steps. The integral in Eq. `[stretch] <#stretch>`__ +represents strain effects. + +Wake Age / Core-Spreading +''''''''''''''''''''''''' + +If the wake age method is selected, [], the viscous core radius is +modeled by Eq. `[age] <#age>`__. + +.. math:: r_c(\zeta) = \sqrt{r_{c0}^2+4\alpha\delta\nu \zeta}\label{age} + +where :math:`\alpha=1.25643`, :math:`\nu` is kinematic viscosity, and +:math:`\delta` is a viscous diffusion parameter (typically between +:math:`1` and :math:`1,000`). The parameter :math:`\delta` is provided +in the input file as **CoreSpreadEddyVisc**. Here, the term, +:math:`4\alpha\delta\nu \zeta`, accounts for viscous effects as the wake +propagates downstream. The higher the background turbulence, the more +diffusion of the vorticity with time, and the higher the value of +:math:`\delta` should be. The method is often referred to as the +core-spreading method. It is a way to account to partially account for +viscous diffusion of the vorticity, without solving for the interaction +between the wake vorticity, nor between the vorticity from the wake and +the background flow. Setting is the same as using the wake age method, +[]. + +Stretching and Wake Age +''''''''''''''''''''''' + +If the stretching and wake-age method is selected [**WakeRegMethod=3**], +the viscous core radius is modeled by +Eq. `[stretchandage] <#stretchandage>`__. + +.. math:: r_c(\zeta,\epsilon) = \sqrt{r_{c0}^2 + 4\alpha\delta\nu \zeta + \int_0^\zeta(1+\epsilon)^{-1}d\zeta}\label{stretchandage} + +.. _sec:diffusion: + +Diffusion +~~~~~~~~~ + +The viscous-splitting assumption is used to solve for the convection and +diffusion of the vorticity separately. The diffusion term +:math:`\nu \Delta \vec{\omega}` represents molecular diffusion. This +term will allow for viscous connection of vorticity lines. Also, +turbulent flows will diffuse the vorticity in a similar manner, based on +a turbulent eddy viscosity. + +The parameter is used to switch between viscous diffusion methods. +Currently, only the core-spreading method is implemented. The method was +described in since it is equivalent to the increase of the +regularization parameter with the wake age. + +.. _sec:circ: + +Lifting-Line Circulation +~~~~~~~~~~~~~~~~~~~~~~~~ + +The code relies on a lifting-line formulation. Lifting-line methods +effectively lump the loads at each cross-section of the blade onto the +mean-line of the blade and do not account directly for the geometry of +each cross-section. In the vorticity-based version of the lifting-line +method, the blade is represented by a line of varying circulation. The +line follows the motion of the blade, and it is referred to as “bound” +circulation. The bound circulation does not follow the same dynamic +equation as the free vorticity of the wake. It’s intensity is linked to +the lift of the airfoils via the Kutta-Joukowski theorem. Spanwise +variation of the bound circulation results in vorticity being emitted +into the the wake, and referred to as “trailed vorticity”. Time changes +of the bound circulation are also emitted in the wake, referred to as +“shed” vorticity. Three methods are implemented to determine the bound +circulation strength. They are selected using the input , and are +presented in the subsequent paragraphs. At the end of a time step, the +circulation of each vortex element is propagated downstream so that +vortex elements with a new intensity can be emitted from the blade at +the next time step. + +Cl-based iterative method +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Cl-based iterative method is extensively described in the work from +van Garrel and it is only briefly presented here +(:cite:`Garrel03_1`). The method was implemented following +the same approach and notations as van Garrel. At present, it is the +preferred method to compute the circulation along the blade span. It is +selected with . In this method, the blade is discretized into a finite +number of segments placed along the lifting line (i.e., the blade +aerodynamic center line), representing the bound circulation, +:math:`\Gamma_b`. The circulation is solved within a nonlinear iterative +solver that makes use of the polar data at each control point located on +the lifting line. + +No-flow-through method +^^^^^^^^^^^^^^^^^^^^^^ + +A Weissinger-L-based representation (:cite:`Weissinger47_1`) +of the lifting surface is also +available (:cite:`Bagai94_1,Gupta06_1,Ribera07_1`). In this +method, the circulation is solved by satisfying a no-flow through +condition at the 3/4-chord points. + +Prescribed circulation +^^^^^^^^^^^^^^^^^^^^^^ + +The final available method prescribes a constant circulation. A user +specified spanwise distribution of circulation is prescribed onto the +blades. + +State-Space Representation and Integration with OpenFAST +-------------------------------------------------------- + +The OLAF module has been integrated into the latest version of OpenFAST +via *AeroDyn15*, following the OpenFAST modularization +framework (:cite:`Jonkman13_1,Sprague15_1`). To follow the +OpenFAST framework, the vortex code is written as a module, and its +formulation comprises state, constraint, and output equations. The data +manipulated by the module include the following vectors: inputs, +:math:`\vec{u}`; states, :math:`\vec{x}`; constrained state, +:math:`\vec{z}`; outputs, :math:`\vec{y}`; and constant parameters, +:math:`\vec{p}`. The vectors are defined as follows: + +- Inputs, :math:`\vec{u}~\--` a set of values supplied to the module + that, along with the states, are needed to calculate future states + and the system’s output. + +- Outputs, :math:`\vec{y}~\--` a set of values calculated and returned + by the module that depend on the states, inputs, and/or parameters + through output equations. + +- States, :math:`\vec{x}~\--` a set of internal values of the module + that are influenced by the inputs and used to calculate future state + values and the output. Continuous states are employed, meaning that + the states are differentiable in time and characterized by continuous + time-differential equations. + +- Constraint states, :math:`\vec{z}~\--` algebraic variables that are + calculated using a nonlinear solve, based on values from the current + time step. + +- Parameters, :math:`\vec{p}~\--` a set of internal system values that + are independent of the states and inputs. The parameters can be fully + defined at initialization and characterize the system’s state + equations and output equations. + +The parameters of the vortex code include: + +- Fluid characteristics: kinematic viscosity, :math:`\nu` + +- Airfoil characteristics: polar data: (:math:`C_l(\alpha)`, + :math:`C_d(\alpha)`, :math:`C_m(\alpha)`), and chord :math:`c` + +- Algorithmic methods and parameters for regularization, viscous + diffusion, discretization, wake geometry, acceleration, and so on. + +The inputs of the vortex code are: + +- Position, orientation, translational velocity, and rotational + velocity of the different nodes of the lifting lines + (:math:`\vec{r}_{ll}`, :math:`\Lambda_{ll}`, + :math:`\vec{\dot{r}}_{ll}`, and :math:`\vec{\omega}_{ll}`, + respectively), gathered into the vector, + :math:`\vec{x}_{\text{elast},ll}`, for conciseness. These quantities + are handled using the mesh-mapping functionality and data structure + of OpenFAST. + +- Undisturbed velocity field at requested locations (lifting-line + points, :math:`\vec{r}_{ll}`, and a set of locations requested by the + vortex code, :math:`\vec{r}_r`), written + :math:`\vec{v}_0=[\vec{v}_{0,ll}, \vec{v}_{0,r}]`. Based on the + parameters, this velocity field may contain the following influences: + freestream, shear, veer, turbulence, tower, and nacelle disturbance. + The locations where the velocity field is requested are typically the + location of the Lagrangian markers. + +The constraint states are: + +- The circulation intensity along the lifting lines, + :math:`\Gamma_{ll}`. + +The continuous states are: + +- The position of the Lagrangian markers, :math:`\vec{r}_m` + +- The vorticity associated with each vortex element, + :math:`\vec{\omega}_e`. For a projection of the vorticity onto vortex + segments, this corresponds to the circulation, + :math:`\vec{\Gamma}_e`, where for each segment, + :math:`\vec{\Gamma}_e= \Gamma_e \vec{dl}_e =\vec{\omega}_e dV_e`, + with :math:`\vec{dl}_e` and :math:`dV_e`, the vortex segment length + and its equivalent vortex volume. + +The outputs are [1]_: + +- The induced velocity at the lifting-line nodes, + :math:`\vec{v}_{i,ll}` + +- The locations where the undisturbed wind needs to be computed, + :math:`\vec{r}_{r}` (typically :math:`\vec{r_{r}}=\vec{r}_m`). + +State, Constraint, and Output Equations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An overview of the main states, constraints, and output equations is +given in this paragraph. More details are provided in +Section `1.1 <#sec:FVW>`__. The constraint equation is used to determine +the circulation distribution along the span of each lifting line. For +the van Garrel method, this circulation is a function of the angle of +attack along the blade and the airfoil coefficients. The angle of attack +at a given lifting-line node is a function of the undisturbed velocity, +:math:`\vec{v}_{0,ll}`, and the velocity induced by the vorticity, +:math:`\vec{v}_{i,ll}`, at that point. Part of the induced velocity is +caused by the vorticity being shed and trailed at the current time step, +which in turn is a function of the circulation distribution along the +lifting line. This constraint equation may be written as: + +.. math:: \vec{Z} = \vec{0} = \vec{\Gamma}_{ll} - \vec{\Gamma}_p(\vec{\alpha}(\vec{x},\vec{u}),\vec{p}) %\label{eq:} + +where :math:`\vec{\Gamma}_p` is the function that returns the +circulation along the blade span, based on the distribution of angle of +attacks and the airfoil characteristics. In practice, this nonlinear +equation is solved using an iterative algorithm. The state equation +specifies the time evolution of the vorticity and the convection of the +Lagrangian markers: + +.. math:: + + \begin{aligned} + \frac{d \vec{\omega}_e}{dt} &= \left[(\vec{\omega}\cdot\nabla)\vec{v} + \nu\nabla^2 \vec{\omega} \right]_e + %+ (\nabla \cdot T_\text{SGS}) + \\ + % ,\quad + \frac{d \vec{r}_m}{dt} &= \vec{V}(\vec{r}_m) + =\vec{V}_0(\vec{r}_m) + \vec{v}_\omega(\vec{r}_m) + =\vec{V}_0(\vec{r}_m) + \vec{V}_\omega(\vec{r}_m, \vec{r}_m, \vec{\omega}) + \label{eq:Convection}\end{aligned} + +where :math:`\vec{v}_\omega` is the velocity induced by the vorticity in +the domain; :math:`\vec{V}_\omega(\vec{r},\vec{r}_m,\vec{\omega})` is +the function that computes this induced velocity at a given point, +:math:`\vec{r}`, based on the location of the Lagrangian markers and the +intensity of the vortex elements; and the subscript, :math:`e`, +indicates that a quantity is applied to an element. The vorticity, +:math:`\vec{\omega}`, is recovered from the vorticity of the vortex +elements by means of discrete convolutions. For vortex-segment +simulations, the viscous-splitting algorithm is used, and the convection +step (Eq. `[eq:Convection] <#eq:Convection>`__) is the main state +equation being solved for. The vorticity stretching is automatically +accounted for, and the diffusion is performed *a posteriori*. The +velocity function, :math:`\vec{V}_\omega`, uses the Biot-Savart law. The +output equation is: + +.. math:: + + \begin{aligned} + \vec{y}_1&=\vec{v}_{i,ll} = \vec{V}_\omega ( \vec{r}_{ll}, \vec{r}_m, \vec{\omega})= \\ + \vec{y}_2&=\vec{r}_{r} \end{aligned} + +Integration with AeroDyn15 +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The vortex code has been integrated as a submodule of the aerodynamic +module of OpenFAST, *AeroDyn15*. The data workflow between the different +modules and submodules of OpenFAST is illustrated in +Figure `[FAST-FVW] <#FAST-FVW>`__. + +This integration required a restructuring of the *AeroDyn15* module to +isolate the parts of the code related to tower shadow modeling, +induction computation, lifting-line-forces computations, and dynamic +stall. The dynamic stall model will be adapted when used in conjunction +with the vortex code to ensure the effect of shed vorticity is not +accounted for twice. The interface between *AeroDyn15* and the inflow +module, *InflowWind*, was accommodated to include the additionally +requested points by the vortex code. + +.. [1] + The loads on the lifting line are not an output of the vortex code; + their calculation is handled by a separate submodule of *AeroDyn*. + +.. |Variable rotor-speed stencil used in time-marching predictor-corrector scheme| image:: Schematics/Stencil.pdf + :name: Stencil diff --git a/docs/source/user/aerodyn-fvw/OutputFiles.rst b/docs/source/user/aerodyn-fvw/OutputFiles.rst new file mode 100644 index 0000000000..df3a61e349 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/OutputFiles.rst @@ -0,0 +1,23 @@ +.. _Output-Files: + +Output Files +============ + +The OLAF module itself does not produce its own output file. However, +additional output channels are made available in *AeroDyn15*. As such, +the *AeroDyn15* output file is briefly described as well as the outputs +made available with OLAF. Visualization files may be generated by using +the parameter, , from the OLAF input file, in which case the VTK files +are written to the folder, +``vtk_fvw, or the parameter, , from the main .fst file, in which case the VTK files are written to the folder, vtk.`` + +Results File +------------ + +OpenFAST generates a master results file that includes the *AeroDyn15* +results. The results are in table format, where each column is a data +channel, and each row corresponds to a simulation-output time step. The +data channels are specified in the *OUTPUTS* section in the *AeroDyn15* +primary input file. The column format of the AeroDyn-generated files is +specified using the **OutFmt** parameter of the OpenFAST driver input +file. diff --git a/docs/source/user/aerodyn-fvw/RunningOLAF.rst b/docs/source/user/aerodyn-fvw/RunningOLAF.rst new file mode 100644 index 0000000000..2fbb78a689 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/RunningOLAF.rst @@ -0,0 +1,175 @@ +.. _Running-OLAF: + +Running OLAF +============ + +This section discusses how to obtain and execute OLAF from a personal +computer. + +Advanced command line users looking for a quick guide may follow the +commands listed here; other users are recommended to follow the detailed +instructions in the coming paragraphs. + +:: + + # sudo apt-get install gfortran-8 cmake liblapack-dev libblas + # OR + # module load cmake comp-intel mkl + git clone https://github.com/openfast/openfast + cd openfast + mkdir build + cd build + cmake .. + make + ./modules/glue-codes/openfast/openfast [INPUTFILE] + +Downloading the OLAF Software +----------------------------- + +Download the OpenFAST archive, which includes OLAF, from the git +repository at https://github.com/OpenFAST/openfast.git. + +Compiling OpenFAST with OLAF +---------------------------- + +This is the same process as compiling OpenFAST. The reader may obtain +detailed instructions at the following address: +https://openfast.readthedocs.io/. For completeness, basic instructions +are included here. + +Compiling on a Microsoft Windows Machine +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Three main options are available to Windows users: + +- Commercial software: install Intel Fortran Compiler and Microsoft + Visual Studio. Open the visual studio solution file, + ``vs-build/FAST/FAST.sln, with Visual Studio. From the Visual Studio menu, select: Build/Build solution.`` + +- Linux subsystem: Install a Linux subsystem for Windows + (https://docs.microsoft.com/en-us/windows/wsl/install-win10. You may + be choose Ubuntu. Then, in a terminal, follow the instructions for + Linux machines provided in . Note that the compiled code will only be + run within the Linux subsystem environment, but the input and output + files can be accessed seamlessly from Windows. + +- Free software: Compilation using free software is an involved process + reserved to advanced users. Users interested in such options are + referred to https://openfast.readthedocs.io/. + +.. _sec:CompileLinux: + +Compiling on Linux/Mac Machines +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _sec:linuxdep: + +Dependencies/requirements +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Compiling OpenFAST requires CMake, a Fortran compiler (Intel Fortran or +GFortran), and Linear Algebra Package (LAPACK) libraries (LAPACK or Math +Kernal Library [MKL]). + +- If you are in a Linux system such as Ubuntu, all the dependencies can + be installed as follows: + + ``$ sudo apt-get install gfortran-8 cmake liblapack-dev libblas`` + +- If you are in a cluster environment, such tools are usually already + installed but need to be loaded into the environment. For example, + the following command can load CMake, the Intel Fortran compiler, and + the MKL libraries: + + ``$ module load cmake comp-intel mkl`` + +Additional resources are provided in . + +Compilation +^^^^^^^^^^^ + +The compilation steps are as follows: + +#. Optional: if you are in a cluster environment, make sure the + dependencies are loaded (see ) + +#. In the OpenFAST directory, create a build directory and move into it: + + ``$ mkdir build`` + + ``$ cd build`` + +#. In the build directory, run the following commands: + + ``$ cmake ..`` + + ``$ make`` + +Advanced users may choose to customize the compilation step by providing +extra arguments to the CMake command. For instance, compiling the code +with the ``DEBUG option and single precision is done as follows:`` + +``$ cmake .. -DCMAKE_BUILD_Type=Debug -DDOUBLE_PRECISION:BOOL=OFF`` + +Compiling the code with optimization flags is done as follows: + +``$ cmake .. -DCMAKE_Fortran_FLAGS_RELEASE="-O2 -xhost" -DDOUBLE_PRECISION:BOOL=OFF`` + +.. _sec:cmdOptions: + +Command/option explanations +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``git clone`` :math:`\--` Git is a free and open-source, distributed, +version-control system designed to handle everything from small to very +large projects with speed and efficiency. :math:`\$` git clone clones a +remote repository into a new directory: + +``mkdir`` :math:`\--` make a new directory + +``cd`` :math:`\--` move into the specified directory + +``module load`` :math:`\--` load specified modules (required on most +high-performance computing systems) + +``cmake`` :math:`\--` CMake is an open-source, cross-platform family of +tools designed to build, test, and package software + +``DCMAKE_Fortran_FLAGS_RELEASE`` :math:`\--` specify optional +compilation flags + +``threads`` and ``qopenmp`` :math:`\--` required flags to run FAST.Farm +with OpenMP parallelization + +``O2`` :math:`\--` optimization-level specification (recommended when +not working to debug code) + +``xhost`` :math:`\--` additional optimization-level specification + +``DDOUBLE_PRECISION:BOOL=OFF`` :math:`\--` compile code in single +precision (recommended) + +``make`` :math:`\--` compile code using Makefile located in directory +(here, Makefile was generated with CMake). + +.. _running-olaf-1: + +Running OLAF +------------ + +#. Executable file will be located in: + + - PC: ``~/OpenFAST/build/bin/openfast`` + + - Mac/Linux: ``~/OpenFAST/build/glue-codes/openfast/openfast`` + +#. Create a directory to all your OpenFAST and OLAF input files. You + will need: + + - ``.fst`` and associated ``.dat`` files :math:`\-–` + turbine files + + - Wind inflow files + +#. Run code with + ``$~/OpenFAST/build/glue-codes/openfast/openfast .fst``. diff --git a/docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.pdf b/docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.pdf new file mode 100644 index 0000000000000000000000000000000000000000..554dfa9386fddd5cc48b0a831add5debe7702305 GIT binary patch literal 43255 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%0DgWS=vA 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 V6@yzAAt*R-QHp~@MopIV{{Wy3>mC3A literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/Schematics/FilamentRegularization.png b/docs/source/user/aerodyn-fvw/Schematics/FilamentRegularization.png new file mode 100644 index 0000000000000000000000000000000000000000..77a97adfceeb49046adac0c7421d4c9f04617cd7 GIT binary patch literal 31008 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 literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.pdf b/docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4d911dadafcf6edfd61563ec71c7896a3c14c209 GIT binary patch literal 63510 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

BNc4%ZA{Wf8OTw6AlaT^N+2pBud2+(=h&O3P>g- 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/Schematics/OpenFAST.png b/docs/source/user/aerodyn-fvw/Schematics/OpenFAST.png new file mode 100644 index 0000000000000000000000000000000000000000..c3d9084e3f5d9f2d35f77f2c1931fef9268b924d GIT binary patch literal 92541 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 Date: Mon, 27 Apr 2020 13:57:03 -0600 Subject: [PATCH 142/190] FVW docs: input file description updated --- docs/source/user/aerodyn-fvw/InputFiles.rst | 245 ++++++++++---------- 1 file changed, 123 insertions(+), 122 deletions(-) diff --git a/docs/source/user/aerodyn-fvw/InputFiles.rst b/docs/source/user/aerodyn-fvw/InputFiles.rst index 09044cb6fb..412458e2a5 100644 --- a/docs/source/user/aerodyn-fvw/InputFiles.rst +++ b/docs/source/user/aerodyn-fvw/InputFiles.rst @@ -3,174 +3,175 @@ Input Files =========== -No lines should be added or removed from the input files, except in -tables where the number of rows is specified. +No lines should be added or removed from the input files, except in tables where +the number of rows is specified. Units ----- -OLAF uses the International System of Units (e.g., kg, m, s, N). Angles -are assumed to be in degrees unless otherwise specified. +OLAF uses the International System of Units (e.g., kg, m, s, N). Angles are +assumed to be in degrees unless otherwise specified. OLAF Primary Input File ----------------------- -The primary OLAF input file defines general free wake options, -circulation model selection and specification, near- and far-wake -length, and wake visualization options. The file is organized into -several functional sections. Each section corresponds to an aspect of -the OLAF model. For most parameters, the user may specify the value -"default" (with or without quotes), in which case a default value, -defined below, is used by the program. +The primary OLAF input file defines general free wake options, circulation model +selection and specification, near- and far-wake length, and wake visualization +options. The file is organized into several functional sections. Each section +corresponds to an aspect of the OLAF model. For most parameters, the user may +specify the value "default" (with or without quotes), in which case a default +value, defined below, is used by the program. -A sample OLAF primary input file is given in -Appendix `[app:input] <#app:input>`__. +A sample OLAF primary input file is given in :ref:`OLAF-Primary-Input-File`. General Options ~~~~~~~~~~~~~~~ -[switch] specifies which integration method will be used to convect the -Lagrangian markers. There are four options: 1) fourth-order Runge-Kutta -, 2) fourth-order Adams-Bashforth , 3) fourth-order -Adams-Bashforth-Moulton , or 4) first-order forward Euler . The default -value is . These methods are specified in -Section `[sec:vortconv] <#sec:vortconv>`__. - -[sec] specifies at what time the wake evolution is classified as “free." -Before this point is reached, the Lagrangian markers are simply -convected with the freestream velocity. After this point, induced -velocities are computed and affect the marker convection. If a negative -time is given, the wake is “free" from the beginning of the simulation. -The default value is :math:`0`. - -[sec] specifies at what time the blade circulation reaches its full -strength. If this value is specified to be :math:`>0`, the circulation -is multiplied by a factor equal to :math:`0` at :math:`t=0`, to a factor -equal to :math:`1` for :math:`t>`. The default value is :math:`0`. - -[sec] specifies the time interval at which the module will update the -wake. The time interval needs to be a multiple of the time step used by -the glue code. The blade circulation is still updated at each -intermediate time steps based on the intermediate blades positions and -wind velocities. The default value is , where is the time step used by +**IntMethod** [switch] specifies which integration method will be used to +convect the Lagrangian markers. There are four options: 1) fourth-order +Runge-Kutta *[1]*, 2) fourth-order Adams-Bashforth *[2]*, 3) fourth-order +Adams-Bashforth-Moulton *[3]*, or 4) first-order forward Euler *[5]*. The +default value is *[5]*. These methods are specified in :numref:`sec:vortconv`. + +**DTfvw** [sec] specifies the time interval at which the module will update the +wake. The time interval needs to be a multiple of the time step used by the glue +code. The blade circulation is still updated at each intermediate time steps +based on the intermediate blades positions and wind velocities. The default +value is :math:`dt_{aero}`, where :math:`dt_{aero}` is the time step used by AeroDyn. +**FreeWakeStart** [sec] specifies at what time the wake evolution is classified +as “free." Before this point is reached, the Lagrangian markers are simply +convected with the freestream velocity. After this point, induced velocities are +computed and affect the marker convection. If a negative time is given, the wake +is “free" from the beginning of the simulation. The default value is :math:`0`. + +**FullCircStart** [sec] specifies at what time the blade circulation reaches its +full strength. If this value is specified to be :math:`>0`, the circulation is +multiplied by a factor equal to :math:`0` at :math:`t=0`, to a factor equal to +:math:`1` for :math:`t>\textit{FullCircStart}`. The default value is :math:`0`. + Circulation Specifications ~~~~~~~~~~~~~~~~~~~~~~~~~~ -[switch] specifies which circulation method is used. There are three -options: 1) :math:`C_l`-based iterative procedure **[1]**, 2) no-flow -through **[2]**, or 3) prescribed **[3]**. The default value is . These -methods are described in Section `[sec:circ] <#sec:circ>`__. +**CircSolvMethod** [switch] specifies which circulation method is used. There +are three options: 1) :math:`C_l`-based iterative procedure *[1]*, 2) no-flow +through *[2]*, or 3) prescribed *[3]*. The default value is *[1]*. These methods +are described in :numref:`sec:circ`. -[-] specifies the dimensionless convergence criteria used for solving -the circulation. This variable is only used if = . The default value is -:math:`0.01`, corresponding to :math:`1\%` error in the circulation -between two iterations. +**CircSolvConvCrit** [-] specifies the dimensionless convergence criteria used +for solving the circulation. This variable is only used if +:math:`\textit{CircSolvMethod} = \textit{[1]}`. The default value is +:math:`0.01`, corresponding to :math:`1\%` error in the circulation between two +iterations. -[-] specifies the relaxation factor used for solving the circulation. -This variable is only used if = . The default value is -:math:`\alpha=0.1`. +**CircSolvRelaxation** [-] specifies the relaxation factor used for solving the +circulation. This variable is only used if :math:`\textit{CircSolvMethod} = +\textit{[1]}`. The default value is :math:`\alpha=0.1`. -[-] specifies the maximum number of iterations used for solving the -circulation. This variable is only used if = . The default value is -:math:`30`. +**CircSolvMaxIter** [-] specifies the maximum number of iterations used for +solving the circulation. This variable is only used if +:math:`\textit{CircSolvMethod} = \textit{[1]}`. The default value is :math:`30`. -[quoted string] specifies the file containing the prescribed blade -circulation. This option is only used if = . The circulation file format -is a delimited file with one header line and two columns. The first -column is the dimensionless radial position [r/R]; the second column is -the bound circulation value in [m\ :math:`^2`/s]. +**PrescribedCircFile** [quoted string] specifies the file containing the +prescribed blade circulation. This option is only used if +:math:`\textit{CircSolvMethod} = \textit{[2]}`. The circulation file format is +a delimited file with one header line and two columns. The first column is the +dimensionless radial position [r/R]; the second column is the bound circulation +value in [m\ :math:`^2`/s]. Wake Options ~~~~~~~~~~~~ -[-] specifies the number of time steps for which the near-wake lattice -is computed. In the future, the possibility to define this input as an -azimuthal span in degrees or a downstream distance in rotor diameter -will be considered. +**nNWPanel** [-] specifies the number of time steps for which the near-wake +lattice is computed. In the future, the possibility to define this input as an +azimuthal span in degrees or a downstream distance in rotor diameter will be +considered. -[D] specifies the length, in rotor diameters, of the far wake. The -default value is :math:`8`. +**WakeLength** [D] specifies the length, in rotor diameters, of the far wake. +The default value is :math:`8`. -[D] specifies the length, in rotor diameters, for which the turbine wake -is convected as “free." If is greater than , then the entire wake is -free. Otherwise, the Lagrangian markers located within the buffer zone -delimited by and are convected with the average velocity. The default -value is :math:`6`. +**FreeWakeLength** [D] specifies the length, in rotor diameters, for which the +turbine wake is convected as “free." If *FreeWakeLength* is greater than +*WakeLength*, then the entire wake is free. Otherwise, the Lagrangian markers +located within the buffer zone delimited by *FreeWakeLength* and *WakeLength* +are convected with the average velocity. The default value is :math:`6`. -[flag] specifies whether shed vorticity is included in the far wake. The -default value is , specifying that the far wake consists only of the -trailed vorticity from the root and tip vortices. +**FWShedVorticity** [flag] specifies whether shed vorticity is included in the +far wake. The default value is *[False]*, specifying that the far wake consists +only of the trailed vorticity from the root and tip vortices. -[switch] specifies which diffusion method is used to account for viscous -diffusion. There are two options: 1) no diffusion or 2) the -core-spreading method . The default value is . +**DiffusionMethod** [switch] specifies which diffusion method is used to account +for viscous diffusion. There are two options: 1) no diffusion *[0]* or 2) the +core-spreading method *[1]*. The default value is *[0]*. Regularization options ~~~~~~~~~~~~~~~~~~~~~~ -[switch] specifies which method is used to determine the regularization -parameters. There are two options: 1) manual or 2) optimized . The -manual option requires the user to specify the parameters listed in this -subsection. The optimized option determines the parameters for the user. -The default value is . - -| [switch] specifies the regularization function used to remove the - singularity of the vortex elements, as specified in - Section `[sec:vortconv] <#sec:vortconv>`__. There are five options: - (1) no correction , (2) the Rankine method , (3) the Lamb-Oseen method - , (4) the Vatistas method , or (5) the denominator offset method -| val4. The functions are given in . The default value is . - -[switch] specifies the method of determining viscous core radius (i.e., -the regularization parameter). There are three options: (1) constant -**[1]**, (2) stretching **[2]**, or (3) age **[3]**. The methods are -described in . The default value is . - -[-] specifies the wake regularization parameter, which is the -regularization value used at the initialization of a vortex element. If -the regularization method is “constant”, this value is used throughout -the wake. - -[-] specifies the bound vorticity regularization parameter, which is the -regularization value used for the vorticity elements bound to the +**RegDetMethod** [switch] specifies which method is used to determine the +regularization parameters. There are two options: 1) manual *[0]* or 2) +optimized *[1]*. The manual option requires the user to specify the parameters +listed in this subsection. The optimized option determines the parameters for +the user. The default value is *[0]*. + +**RegFunction** [switch] specifies the regularization function used to remove +the singularity of the vortex elements, as specified in +:numref:`sec:vortconv`. There are five options: (1) no correction *[0]*, +(2) the Rankine method *[1]*, (3) the Lamb-Oseen method *[2]*, (4) the Vatistas +method *[3]*, or (5) the denominator offset method *[4]*. The functions are +given in . The default value is *[3]*. + +**WakeRegMethod** [switch] specifies the method of determining viscous core radius (i.e., the +regularization parameter). There are three options: (1) constant *[1]*, (2) +stretching *[2]*, or (3) age *[3]*. The methods are described in +:numref:`sec:corerad`. The default +value is *[1]*. + +**WakeRegParam** [-] specifies the wake regularization parameter, which is the +regularization value used at the initialization of a vortex element. If the +regularization method is “constant”, this value is used throughout the wake. + +**BladeRegParam** [-] specifies the bound vorticity regularization parameter, +which is the regularization value used for the vorticity elements bound to the blades. -[-] specifies the eddy viscosity parameter :math:`\delta` used for the -core-spreading method (=) or the regularization method with age (=). The -variable :math:`\delta` is described in . The default value is -:math:`100`. +**CoreSpreadEddyVisc** [-] specifies the eddy viscosity parameter :math:`\delta` +used for the core-spreading method (*DiffusionMethod* = *[1]*) or the +regularization method with age (*WakeRegMethod* = *[3]*). The variable +:math:`\delta` is described in . The default value is :math:`100`. Output Options ~~~~~~~~~~~~~~ -[flag] specifies if Visualization Toolkit (VTK) visualization files are -to be written out. = does not write out any VTK files. = outputs a VTK -file at every time step. The outputs are written in the folder, -``vtk_fvw.`` +**WrVTK** [flag] specifies if Visualization Toolkit (VTK) visualization files +are to be written out. *WrVTK* = *[0]* does not write out any VTK files. *WrVTK* += *[1]* outputs a VTK file at every time step. The outputs are written in the +folder, ``vtk_fvw.`` -[-] specifies how many blade VTK files are to be written out. -:math:`= n` outputs VTK files for :math:`n` blades, with :math:`0` being -an acceptable value. The default value is :math:`1`. +**VTKBlades** [-] specifies how many blade VTK files are to be written out. +*VTKBlades* :math:`= n` outputs VTK files for :math:`n` blades, with :math:`0` +being an acceptable value. The default value is :math:`1`. -[switch] specifies in which coordinate system the VTK files are written. -There are two options: 1) global coordinate system or 2) hub coordinate -system . The default value is . +**VTKCoord** [switch] specifies in which coordinate system the VTK files are +written. There are two options: 1) global coordinate system *[1]* or 2) hub +coordinate system *[2]*. The default value is *[1]*. -[:math:`1`/sec] specifies the output frequency of the VTK files. The -value provided is rounded to the nearest allowable multiple of the time -step. The default value is :math:`1/dt_\text{fvw}`. Specifying =, will -be equivalent to using the value :math:`1/dt_\text{aero}`. +**VTK_fps** [:math:`1`/sec] specifies the output frequency of the VTK files. The +value provided is rounded to the nearest allowable multiple of the time step. +The default value is :math:`1/dt_\text{fvw}`. Specifying *VTK_fps* = *[all]*, +will be equivalent to using the value :math:`1/dt_\text{aero}`. AeroDyn15 Input File -------------------- -As OLAF is incorporated into the *AeroDyn15* module, a wake computation -option has been added to the *AeroDyn15* input file and a line has been -added. These additions are as follows. +As OLAF is incorporated into the *AeroDyn15* module, a wake computation option +has been added to the *AeroDyn15* input file and a line has been added. These +additions are as follows. + +**WakeMod** specifies the type of wake model that is used. *WakeMod* = *[3]* has +been added to allow the user to switch from the traditional BEM method to the +FVW method. -specifies the type of wake model that is used. = has been added to allow -the user to switch from the traditional BEM method to the FVW method. +**FVWFile** [string] specifies the OLAF module file, the path is relative to the +AeroDyn file, unless an absolute path is provided. -[string] specifies the OLAF module file, including the path to the file. From 3910de46ba62b87013d027ce94a653eb4dc77894 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 27 Apr 2020 14:02:08 -0600 Subject: [PATCH 143/190] FVW docs: update Outputs.rst --- docs/source/user/aerodyn-fvw/OutputFiles.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/user/aerodyn-fvw/OutputFiles.rst b/docs/source/user/aerodyn-fvw/OutputFiles.rst index df3a61e349..cfbf334945 100644 --- a/docs/source/user/aerodyn-fvw/OutputFiles.rst +++ b/docs/source/user/aerodyn-fvw/OutputFiles.rst @@ -3,13 +3,13 @@ Output Files ============ -The OLAF module itself does not produce its own output file. However, -additional output channels are made available in *AeroDyn15*. As such, -the *AeroDyn15* output file is briefly described as well as the outputs -made available with OLAF. Visualization files may be generated by using -the parameter, , from the OLAF input file, in which case the VTK files -are written to the folder, -``vtk_fvw, or the parameter, , from the main .fst file, in which case the VTK files are written to the folder, vtk.`` +The OLAF module itself does not produce its own output file. However, additional +output channels are made available in *AeroDyn15*. As such, the *AeroDyn15* +output file is briefly described as well as the outputs made available with +OLAF. Visualization files may be generated by using the parameter, **WrVTK**, +from the OLAF input file, in which case the VTK files are written to the folder, +``vtk_fvw``, or the parameter, **WrVTK**, from the main ``.fst`` file, in which case +the VTK files are written to the folder, ``vtk``. Results File ------------ From 49bac513d977e71fa91821f8f0fcf09b56a53c01 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 27 Apr 2020 14:55:31 -0600 Subject: [PATCH 144/190] Docs: move eq numbers to right of eqs in html --- docs/_static/css/math_eq.css | 6 ++++++ docs/conf.py | 3 +++ 2 files changed, 9 insertions(+) create mode 100644 docs/_static/css/math_eq.css diff --git a/docs/_static/css/math_eq.css b/docs/_static/css/math_eq.css new file mode 100644 index 0000000000..ffc5f20ea4 --- /dev/null +++ b/docs/_static/css/math_eq.css @@ -0,0 +1,6 @@ +.math { + text-align: left; +} +.eqno { + float: right; +} diff --git a/docs/conf.py b/docs/conf.py index f73c5c2af7..1baea212f4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -257,3 +257,6 @@ def setup(app): objname="CMake configuration value", indextemplate="pair: %s; CMake configuration" ) + +def setup(app): + app.add_stylesheet('css/math_eq.css') From bfe6e936e7a8a9d07260f2f9f7607e1aeab6f779 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 29 Apr 2020 17:34:06 -0600 Subject: [PATCH 145/190] FVW: restructuring input file --- modules/aerodyn/src/FVW_IO.f90 | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index d5ca8d4e5c..71d1c96651 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -32,26 +32,28 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadCom(UnIn, FileName, 'FVW input file header line 1', ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadCom(UnIn, FileName, 'FVW input file header line 2', ErrStat2, ErrMsg2 ); if(Failed()) return !------------------------ GENERAL OPTIONS ------------------------------------------- - CALL ReadCom (UnIn,FileName, 'General option header' , ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadCom (UnIn,FileName, '--- General option header' , ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%IntMethod ,'Integration method' ,'', idEuler1 , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%DTfvw ,'DTfvw' ,'', p%DTaero , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%FreeWakeStart ,'FreeWakeStart' ,'', 0.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%FullCirculationStart,'FullCirculationStart','', real(20.0_ReKi*Inp%DTfvw,ReKi), ErrStat2,ErrMsg2); if(Failed())return !------------------------ CIRCULATION SPECIFICATIONS ------------------------------------------- - CALL ReadCom(UnIn,FileName, 'Circulation specification header', ErrStat2, ErrMsg2 ); if(Failed()) return + CALL ReadCom(UnIn,FileName, '--- Circulation specification header' , ErrStat2, ErrMsg2 ); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%CirculationMethod ,'CirculationMethod' ,'', idCircPolarData, ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvConvCrit ,'CircSolvConvCrit ' ,'', 0.001 , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvRelaxation,'CircSolvRelaxation','', 0.1 , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CircSolvMaxIter ,'CircSolvMaxIter' ,'', 30 , ErrStat2,ErrMsg2); if(Failed())return - !CALL ReadVar(UnIn,FileName,Inp%CircSolvPolar ,'CircSolvPolar' ,'',ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar(UnIn,FileName,Inp%CirculationFile ,'CirculationFile' ,'',ErrStat2,ErrMsg2); if(Failed())return !------------------------ WAKE OPTIONS ------------------------------------------- - CALL ReadCom (UnIn,FileName, 'Wake options header' , ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadCom (UnIn,FileName, '=== Separator' , ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadCom (UnIn,FileName, '--- Wake options header' , ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadCom (UnIn,FileName, '--- Wake extent header' , ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVar (UnIn,FileName,Inp%nNWPanels ,'nNWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%nFWPanels ,'nFWPanels' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%nFWPanelsFree ,'nFWPanelsFree' ,'', Inp%nFWPanels , ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%FWShedVorticity ,'FWShedVorticity' ,'', .False. , ErrStat2,ErrMsg2); if(Failed())return + + CALL ReadCom (UnIn,FileName, '--- Wake regularization header' , ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%DiffusionMethod ,'DiffusionMethod' ,'',idDiffusionNone , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%RegDeterMethod ,'RegDeterMethod' ,'',idRegDeterManual, ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%RegFunction ,'RegFunction' ,'',idRegVatistas , ErrStat2,ErrMsg2); if(Failed())return @@ -59,8 +61,12 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar (UnIn,FileName,Inp%WakeRegParam ,'WakeRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%WingRegParam ,'WingRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CoreSpreadEddyVisc ,'CoreSpreadEddyVisc','',100.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return - CALL ReadVarWDefault(UnIn,FileName,Inp%ShearModel ,'ShearModel' ,'',idShearNone , ErrStat2,ErrMsg2); if(Failed())return + + CALL ReadCom (UnIn,FileName, '--- Wake treatment header' , ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%TwrShadowOnWake ,'TwrShadowOnWake' ,'',.false. , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%ShearModel ,'ShearModel' ,'',idShearNone , ErrStat2,ErrMsg2); if(Failed())return + + CALL ReadCom (UnIn,FileName, '--- Speed up header ' , ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%VelocityMethod ,'VelocityMethod' ,'',idVelocityBasic , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%TreeBranchFactor ,'TreeBranchFactor' ,'',2.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%PartPerSegment ,'PartPerSegment' ,'', 1 , ErrStat2,ErrMsg2); if(Failed())return @@ -69,7 +75,8 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) ! Inp%TreeBranchFactor = 3.0_ReKi ! Inp%PartPerSegment = 1 !------------------------ OUTPUT OPTIONS ----------------------------------------- - CALL ReadCom (UnIn,FileName, 'Output options header' ,ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadCom (UnIn,FileName, '=== Separator' ,ErrStat2,ErrMsg2); if(Failed()) return + CALL ReadCom (UnIn,FileName, '--- Output options header' ,ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'', 0 ,ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%VTKBlades , 'VTKBlades' ,'', 1 ,ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%VTKCoord , 'VTKCoord' ,'', 1 ,ErrStat2,ErrMsg2); if(Failed())return From 0718c721a4d25241f51bb115769f1aa1e3815514 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Thu, 30 Apr 2020 10:30:13 -0600 Subject: [PATCH 146/190] FVW docs: more theory updates to match the LaTeX document. --- docs/source/user/aerodyn-fvw/OLAFTheory.rst | 400 ++++++++------------ 1 file changed, 163 insertions(+), 237 deletions(-) diff --git a/docs/source/user/aerodyn-fvw/OLAFTheory.rst b/docs/source/user/aerodyn-fvw/OLAFTheory.rst index be132a9f14..1540f491dc 100644 --- a/docs/source/user/aerodyn-fvw/OLAFTheory.rst +++ b/docs/source/user/aerodyn-fvw/OLAFTheory.rst @@ -18,28 +18,31 @@ Introduction - Vorticity formulation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The vorticity equation for incompressible homogeneous flows in the -absence of non-conservative force is: +absence of non-conservative force is given by +Eq. :eq:`eq:vorticityconservationincompr` .. math:: - \begin{aligned} - \frac{d\vec{\omega}}{dt} = \frac{\partial\vec{\omega}}{\partial{t}} + \underbrace{(\vec{u} \cdot \nabla)}_{\text{convection}}\vec{\omega} = \underbrace{(\vec{\omega}\cdot\nabla)\vec{u}}_{\text{strain}} +\underbrace{\nu\Delta\vec{\omega}}_{\text{diffusion}} \label{eq:vorticityconservationincompr}\end{aligned} + \frac{d\vec{\omega}}{dt} = \frac{\partial\vec{\omega}}{\partial{t}} + \underbrace{(\vec{u} \cdot \nabla)}_{\text{convection}}\vec{\omega} = \underbrace{(\vec{\omega}\cdot\nabla)\vec{u}}_{\text{strain}} +\underbrace{\nu\Delta\vec{\omega}}_{\text{diffusion}} + \end{aligned} + :label: eq:vorticityconservationincompr + -where :math:`\vec{\omega}` is the vorticity, :math:`\vec{u}` is the -velocity and :math:`\nu` is the viscosity. In free vortex wake methods, +Here, :math:`\vec{\omega}` is the vorticity, :math:`\vec{u}` is the +velocity, and :math:`\nu` is the viscosity. In free vortex wake methods, the vorticity equation is used to describe the evolution of the wake vorticity. Different approximations are introduced to ease its -resolution: the vorticity is projected onto a discrete number of vortex -elements (here vortex filaments), and, the convection and diffusion -steps are treated separately (viscous-splitting). Several complications -yet arises from the method, in particular, the discretization requires a -regularization of the vorticity field (or velocity field) to ensure a -smooth approximation. +resolution, such as projecting the vorticity onto a discrete number of +vortex elements (here vortex filaments), and separately treating the +convection and diffusion steps, known as viscous-splitting. Several +complications arise from the method, in particular, the discretization +requires a regularization of the vorticity field (or velocity field) to +ensure a smooth approximation. The forces exerted by the blades onto the flow are expressed in vorticity formulation as well. This vorticity is bound to the blade and has a circulation associated with the lift force. A lifting-line -formulation is here used to model the bound vorticity. +formulation is used here to model the bound vorticity. The different models of the free vortex code implemented are described in the following sections. @@ -55,33 +58,31 @@ distribution is projected onto basis function which will be referred to as vortex elements. Vortex filaments are here used as elements that represents the vorticity field. A vortex filaments is delimited by two points and hence assumes a direction formed by these two points. A -vorticity tube, oriented along, say, the unit vector :math:`\vec{e}_x`, -of cross section :math:`dS` and length :math:`l`, can be approximated by -a vortex filament of length :math:`l` oriented along the same direction. -The total vorticity of the tube and the vortex filaments are the same -and related by: +vorticity tube is oriented along the unit vector :math:`\vec{e}_x` of +cross section :math:`dS` and length :math:`l`. It can then be +approximated by a vortex filament of length :math:`l` oriented along the +same direction. The total vorticity of the tube and the vortex filaments +are the same and related by: .. math:: - \begin{aligned} \vec{\omega} \, dS = \vec{\Gamma} - %\omega \, dS \, \vec{e}_x = = \Gamma \vec{e}_x - %\rightarrow - %\qquad\end{aligned} + \end{aligned} + :label: OmegaGamma where :math:`\vec{\Gamma}` is the circulation intensity of the vortex filament. If the vorticity tubes are complex and occupy a large volumes, -the projection onto vortex filaments is difficult, and the projection +the projection onto vortex filaments is difficult and the projection onto vortex particle is more adapted. Yet, assuming the wake is confined to a thin vorticity layer which defines a velocity jump of know direction, it is possible to approximate the wake vorticity sheet as a mesh of vortex filaments. This is the basis of vortex filament wake methods. Vortex filaments are a singular representation of the vorticity -field, since the occupy a line instead of a volume. To better represent -the vorticity field, the filaments are "inflated", a process referred to -as regularization (see . The regularization of the vorticity field also -regularizes the velocity field and avoids the singularities that would -otherwise occur. +field, since they occupy a line instead of a volume. To better represent +the vorticity field, the filaments are “inflated”, a process referred to +as regularization (see :numref:`sec:Regularization`). The +regularization of the vorticity field also regularizes the velocity +field and avoids the singularities that would otherwise occur. .. _sec:vortconv: @@ -91,12 +92,13 @@ Vortex Convection The governing equation of motion for a vortex filament is given by the convection equation of a Lagrangian marker: -.. math:: \frac{d\vec{r}}{dt}=\vec{V}(\vec{r},t) \label{VortFilCart} +.. math:: + \frac{d\vec{r}}{dt}=\vec{V}(\vec{r},t) + :label: VortFilCart -where :math:`\vec{r}` is the position of a Lagrangian marker, such as, -one of the vortex filaments extremity. The Lagrangian convection of the -filaments, effectively stretches the filaments, and thus automatically -accounts for the strain part of the vorticity equation. +where :math:`\vec{r}` is the position of a Lagrangian marker. The +Lagrangian convection of the filaments stretches the filaments and thus +automatically accounts for the strain part of the vorticity equation. .. _sec:vortconvPolar: @@ -105,108 +107,36 @@ Vortex Convection in Polar Coordinates The governing equation of motion for a vortex filament is given by: -.. math:: \frac{d\vec{r}(\psi,\zeta)}{dt}=\vec{V}[\vec{r}(\psi,\zeta),t]\label{VortFil} +.. math:: + \frac{d\vec{r}(\psi,\zeta)}{dt}=\vec{V}[\vec{r}(\psi,\zeta),t] + :label: VortFil -Using the chain rule, Eq. `[VortFil] <#VortFil>`__ is rewritten as: +Using the chain rule, Eq. :eq:`VortFil` is rewritten as: -.. math:: \frac{\partial\vec{r}(\psi,\zeta)}{\partial\psi}+\frac{\partial\vec{r}(\psi,\zeta)}{\partial\zeta}=\frac{\vec{V}[\vec{r}(\psi,\zeta),t]}{\Omega}\label{VortFil_expanded} +.. math:: + \frac{\partial\vec{r}(\psi,\zeta)}{\partial\psi}+\frac{\partial\vec{r}(\psi,\zeta)}{\partial\zeta}=\frac{\vec{V}[\vec{r}(\psi,\zeta),t]}{\Omega} + :label: VortFil_expanded where :math:`d\psi/dt=\Omega` and -:math:`d\psi=d\zeta` (:cite`Leishman02_1`). Here, +:math:`d\psi=d\zeta` (:cite:`Leishman02_1`). Here, :math:`\vec{r}(\psi,\zeta)` is the position vector of a Lagrangian marker, and :math:`\vec{V}[\vec{r}(\psi,\zeta)]` is the velocity. -At present, two options are available to numerically solve the left-hand -side of Eq. `[VortFil_expanded] <#VortFil_expanded>`__ for the -vortex-filament location. The first option, [**IntMethod=5**], is a -first-order forward Euler method. This is an explicit method solved -using Eq. `[Euler] <#Euler>`__. +At present, first-order forward Euler method is used to numerically +solve the left-hand side of +Eq. :eq:`VortFil_expanded` for the vortex-filament +location [**IntMethod=5**]. This is an explicit method solved using +Eq. :eq:`Euler`. .. math:: - - \label{Euler} \vec{r}(\psi+\Delta\psi_i,\zeta+\Delta\zeta) = \vec{r}(\psi,\zeta) + \vec{V}(\psi,\zeta) \Delta t - -The second option, [**IntMethod=1**], is a predictor-corrector scheme -that was developed to accommodate variable rotor speed, as shown by the -stencil in Figure `1.1 <#Stencil>`__ (:cite:`Shaler19_2`). - -|Variable rotor-speed stencil used in time-marching predictor-corrector -scheme| - -The difference operators, -:math:`\frac{\partial \vec{r}(\psi,\zeta)}{\partial \zeta}` and -:math:`\frac{\partial \vec{r}(\psi,\zeta)}{\partial \psi}`, are found by -means of a Taylor series expansion about the point, -(:math:`\zeta+\Delta\zeta/2`, :math:`\psi+\Delta\psi/2`). -:math:`\frac{\partial \vec{r}(\psi,\zeta)}{\partial \psi}` is computed -using a two-step backward method and -:math:`\frac{\partial \vec{r}(\psi,\zeta)}{\partial \zeta}` by central -differencing. This results in a scheme that is second-order accurate in -:math:`\zeta` and third-order accurate in :math:`\psi`. The resulting -equations are given as follows: - -.. math:: \frac{\partial\vec{r}(\psi,\zeta)}{\partial\zeta}=\frac{\vec{r}(\psi+\Delta\psi_i,\zeta+\Delta\zeta)-\vec{r}(\psi+\Delta\psi_i,\zeta)+\vec{r}(\psi,\zeta+\Delta\zeta)-\vec{r}(\psi,\zeta)}{2\Delta\zeta} - -.. math:: - - \begin{gathered} - \frac{\partial\vec{r}(\psi,\zeta)}{\partial\psi}=\\ - \bigg\{23\vec{r}(\psi+\Delta\psi_i,\zeta+\Delta\zeta)+23\vec{r}(\psi+\Delta\psi_i,\zeta)-21\vec{r}(\psi,\zeta+\Delta\zeta)-21\vec{r}(\psi,\zeta)\\ - -3\vec{r}(\psi-\Delta\psi_{i-1},\zeta+\Delta\zeta)-3\vec{r}(\psi-\Delta\psi_{i-1},\zeta)+\vec{r}(\psi-\Delta\psi_{i-1}-\Delta\psi_{i-2},\zeta+\Delta\zeta)\\ - +\vec{r}(\psi-\Delta\psi_{i-1}-\Delta\psi_{i-2},\zeta)\bigg\}\bigg\{46\Delta\psi_i+4\Delta\psi_{i-1}-2\Delta\psi_{i-2}\bigg\}^{-1}\end{gathered} - -with variables as defined in Figure `1.1 <#Stencil>`__. The right-hand -side of Eq. `[VortFil_expanded] <#VortFil_expanded>`__ is computed by -averaging the velocities surrounding the point -(:math:`\zeta+\Delta\zeta/2`, :math:`\psi+\Delta\psi/2`). The marker -location is then found by substituting the difference operators and -velocity averaging into Eq. `[VortFil_expanded] <#VortFil_expanded>`__ -and rearranging to obtain: - -.. math:: - - \begin{gathered} - \vec{r}^m(\psi+\Delta\psi_i,\zeta+\Delta\zeta) =\\ - \bigg\{\frac{\vec{V}}{\Omega}-\Big(-\frac{1}{2\Delta\zeta}+\frac{23}{\phi}\Big)\vec{r}^m(\psi+\Delta\psi_i,\zeta)-\Big(\frac{1}{2\Delta\zeta}-\frac{21}{\phi}\Big)\vec{r}^m(\psi,\zeta+\Delta\zeta)\\ - +\Big(\frac{1}{2\Delta\zeta}+\frac{21}{\phi}\Big)\vec{r}^m(\psi,\zeta)+\frac{3}{\phi}\vec{r}^m(\psi-\Delta\psi_{i-1},\zeta+\Delta\zeta)+\frac{3}{\phi}\vec{r}^m(\psi-\Delta\psi_{i-1},\zeta)\\ - -\frac{1}{\phi}\vec{r}^m(\psi-\Delta\psi_{i-1}-\Delta\psi_{i-2},\zeta+\Delta\zeta)-\frac{1}{\phi}\vec{r}^m(\psi-\Delta\psi_{i-1}-\Delta\psi_{i-2},\zeta)\bigg\}\/\bigg\{\frac{1}{2\Delta\zeta}+\frac{23}{\phi}\bigg\}^{-1}\label{predcorr_general}\end{gathered} - -where - -.. math:: - - \begin{aligned} - \vec{V} &= 4V_\infty - +V_{ind}\left(\vec{r}^{m-1}(\psi,\zeta)\right) - +V_{ind}\left(\vec{r}^{m-1}(\psi+\Delta\psi,\zeta)\right) - \nonumber\\ - &\ \ - + V_{ind}\left(\vec{r}^{m-1}(\psi,\zeta+\Delta\zeta)\right) - + V_{ind}\left(\vec{r}^{m-1}(\psi+\Delta\psi,\zeta+\Delta\zeta)\right) - \\ - \phi &= 46\Delta\psi_i+4\Delta\psi_{i-1}-2\Delta\psi_{i-2}\end{aligned} - -Equation `[predcorr_general] <#predcorr_general>`__ is the general form -of the predictor and corrector equations, indicated by the superscript, -:math:`m`. It is first used in the predictive step to compute the -predicted wake position for all Lagrangian markers using initial guess -values for the wake positions (:math:`\vec{r}^m`) and velocity values at -wake positions from the previous time step (:math:`\vec{r}^{m-1}`). The -resulting wake positions are then used as the :math:`m` time step in the -corrector equation to compute the corrected wake position at the current -time step (:math:`\vec{r}^{m+1}`). This process iterates until converged -wake locations are reached. Wake location is assumed to be converged -when the difference in wake position between iterations reaches a value -of less than :math:`0.001` m root mean -square (:cite:`Krista12_1`). This is typically achieved in -two to three iterations. + :label: Euler Induced Velocity and Velocity Field ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The velocity term on the right-hand side of -Eq. `[VortFilCart] <#VortFilCart>`__ is a nonlinear function of the +Eq. :eq:`VortFilCart` is a nonlinear function of the vortex position, representing a combination of the freestream and induced velocities (:cite:`Hansen08_1`). The induced velocities at point :math:`\vec{x}`, caused by each straight-line @@ -214,7 +144,9 @@ filament, are computed using the Biot-Savart law, which considers the locations of the Lagrangian markers and the intensity of the vortex elements (:cite:`Leishman02_1`): -.. math:: d\vec{v}(\vec{x})=\frac{\Gamma}{4\pi}\frac{d\vec{l}\times\vec{r}}{r^3}\label{BiotSavart} +.. math:: + d\vec{v}(\vec{x})=\frac{\Gamma}{4\pi}\frac{d\vec{l}\times\vec{r}}{r^3} + :label: BiotSavart Here, :math:`\Gamma` is the circulation strength of the filament, :math:`\vec{dl}` is an elementary length along the filament, and @@ -225,36 +157,37 @@ length, delimited by the points :math:`\vec{x}_1` and :math:`\vec{x}_2` leads to: .. math:: - \begin{aligned} \vec{v}(\vec{x}) - %\frac{\Gamma}{4\pi} \r_0\cdot\left( \frac{\r_1}{r_1}-\frac{\r_2}{r_2}\right)\frac{\r_1\times\r_2}{\norm{\r_1\times\r_2}^2}\label{eq:biotsavartline}\\ - % &=\frac{\Gamma}{4\pi} \left(r_1+r_2\right)\left(1-\frac{\r_1\cdot\r_2}{r_1 r_2}\right)\frac{\r_1\times\r_2}{\norm{\r_1\times\r_2}^2}\\ - = F_\nu \frac{\Gamma}{4\pi} \frac{(r_1+r_2)}{r_1r_2(r_1r_2+\vec{r}_1\cdot\vec{r}_2) }\vec{r}_1\times\vec{r}_2 \label{eq:BiotSavartSegment} \end{aligned} + = F_\nu \frac{\Gamma}{4\pi} \frac{(r_1+r_2)}{r_1r_2(r_1r_2+\vec{r}_1\cdot\vec{r}_2) }\vec{r}_1\times\vec{r}_2 + \end{aligned} + :label: eq:BiotSavartSegment with :math:`\vec{r}_1= \vec{x}-\vec{x}_1` and :math:`\vec{r}_2= \vec{x}-\vec{x}_2`. The factor :math:`F_\nu` is a -regularization parameter that will be discussed in . The filament length +regularization parameter that will be discussed in +:numref:`sec:RegularizationFunction`. The filament length is noted :math:`r_0`, where :math:`\vec{r}_0= \vec{x}_2-\vec{x}_1`. The distance orthogonal to the filament is: .. math:: - \begin{aligned} - \rho = \frac{|\vec{r}_1\times\vec{r}_2|}{r_0}\end{aligned} + \rho = \frac{|\vec{r}_1\times\vec{r}_2|}{r_0} + \end{aligned} The velocity at any point of the domain is obtained by superposition of the velocity induced by all vortex filaments, and by superposition of the main flow, :math:`\vec{V}_0`, (here assumed divergence free): .. math:: - \begin{aligned} - \vec{V}(\vec{x}) = \vec{V}_0 + \sum_{k} \vec{v}_k(\vec{x}) \end{aligned} + \vec{V}(\vec{x}) = \vec{V}_0 + \sum_{k} \vec{v}_k(\vec{x}) + \end{aligned} where the sum is over all the vortex filaments, each of intensity :math:`\Gamma_k`. The intensity of each filament is determined by -spanwise and time changes of the bound circulation, as discussed in . +spanwise and time changes of the bound circulation, as discussed in +:numref:`sec:circ`. .. _sec:Regularization: @@ -264,7 +197,7 @@ Regularization Regularization and viscous diffusion ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The singularity that occurs in Eq. `[BiotSavart] <#BiotSavart>`__ +The singularity that occurs in Eq. :eq:`BiotSavart` greatly affects the numerical accuracy of vortex methods. By regularizing the “1-over-r” kernel of the Biot-Savart law, it is possible to obtain a numerical method that converges to the @@ -276,7 +209,7 @@ the vorticity field and the velocity field are the same. Some engineering models also perform regularization by directly introducing additional terms in the denominator of the Biot-Savart velocity kernel. The factor, :math:`F_\nu`, was introduced in -Eq. `[eq:BiotSavartSegment] <#eq:BiotSavartSegment>`__ to account for +Eq. :eq:`eq:BiotSavartSegment` to account for this regularization. In the convergence proofs of vortex methods, regularization and viscous @@ -303,13 +236,13 @@ Determination of the regularization parameter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The regularization parameter is both a function of the physics being -modelled (blade boundary layer and wake), and, the choice of -discretization. The parameters at play are thus: the chord length, the -boundary layer height, and the volume that each vortex filament is -approximating. Currently the choice is left to the user -(**RegDetMethod\ =0)**. Empirical results for a rotating blade are found -in the work of Gupta (:cite:`Gupta06_1`). As a guideline, -the regularization parameter may be chosen as twice the average spanwise +modelled (blade boundary layer and wake) and the choice of +discretization. Contributing factors are the chord length, the boundary +layer height, and the volume that each vortex filament is approximating. +Currently the choice is left to the user (**RegDetMethod\ =0**). +Empirical results for a rotating blade are found in the work of +Gupta (:cite:`Gupta06_1`). As a guideline, the +regularization parameter may be chosen as twice the average spanwise discretization of the blade. The current implementation will implement this guideline when the user chooses **RegDetMethod\ =1**. Further refinement of this option will be considered in the future. @@ -327,18 +260,8 @@ Rankine method, (3) the Lamb-Oseen method, (4) the Vatistas method, or [**RegFunction=0**], :math:`F_\nu=1`. The remaining methods are detailed in the following sections. The regularization parameter (**WakeRegParam**) is noted :math:`r_c` and the distance to the filament -is written :math:`\rho`. The different functions are compared on . - -.. figure:: Schematics/FilamentRegularization.png - :alt: Velocity along a line orthogonal to the vortex filament for different regularization models. - :align: center - :width: 80.0% - :name: FilamentRegularization - - Velocity along a line orthogonal to the vortex filament and passing - through the filament center, for different regularization models, - with :math:`r_c=0.5r_0`. - +is written :math:`\rho`. The different functions are compared in +:numref:`FilamentRegularization`. Rankine ''''''' @@ -348,40 +271,37 @@ regularization model. With this method, the Rankine vortex has a finite core with a solid body rotation near the vortex center and a potential vortex away from the center. If this method is used, [**RegFunction=1**], the viscous core correction is given by -Eq. `[rankine] <#rankine>`__. +Eq. :eq:`rankine`. .. math:: - - \label{rankine} F_\nu= \begin{cases} \rho^2/r_c^2 & 0 < \rho < 1 \\ 1 & \rho > 1 \end{cases} + :label: rankine Here, :math:`r_c` is the viscous core radius of a vortex filament, -detailed in Section `1.1.6.4 <#sec:corerad>`__. +detailed in :numref:`sec:corerad`. Lamb-Oseen '''''''''' If this method is used, [**RegFunction=2**], the viscous core correction -is given by Eq. `[lamboseen] <#lamboseen>`__. +is given by Eq. :eq:`lamboseen`. .. math:: - - \label{lamboseen} F_\nu= \bigg[1-\text{exp}(-\frac{\rho^2}{r_c^2})\bigg] + :label: lamboseen Vatistas '''''''' If this method is used, [**RegFunction=3**], the viscous core correction -is given by Eq. `[vatistas] <#vatistas>`__. +is given by Eq. :eq:`vatistas`. .. math:: - - \label{vatistas} F_\nu = \frac{\rho^2}{(\rho^{2n}+r_c^{2n})^{1/n}} = \frac{(\rho/r_c)^2}{(1 + (\rho/r_c)^{2n})^{1/n}} + :label: vatistas Here, :math:`\rho` is the distance from a vortex segment to an arbitrary point (:cite:`Abedi16_1`). Research from rotorcraft @@ -391,20 +311,22 @@ work (:cite:`Bagai93_1`). Denominator offset/cut-off '''''''''''''''''''''''''' -If this method is used, [**RegFunction=4**], the singularity is removed -by introducing an additive factor in the denominator of , proportional -to the filament length :math:`r_0`: +If this method is used, [**RegFunction=4**], the viscous core correction +is given by Eq. :eq:`denom` .. math:: - \begin{aligned} \vec{v}(\vec{x}) - %\frac{\Gamma}{4\pi} \r_0\cdot\left( \frac{\r_1}{r_1}-\frac{\r_2}{r_2}\right)\frac{\r_1\times\r_2}{\norm{\r_1\times\r_2}^2}\label{eq:biotsavartline}\\ - % &=\frac{\Gamma}{4\pi} \left(r_1+r_2\right)\left(1-\frac{\r_1\cdot\r_2}{r_1 r_2}\right)\frac{\r_1\times\r_2}{\norm{\r_1\times\r_2}^2}\\ - = \frac{\Gamma}{4\pi} \frac{(r_1+r_2)}{r_1r_2(r_1r_2+\vec{r}_1\cdot\vec{r}_2) + r_c^2 r_0^2} \vec{r}_1\times\vec{r}_2 \label{eq:BiotSavartSegment} \end{aligned} + = \frac{\Gamma}{4\pi} \frac{(r_1+r_2)}{r_1r_2(r_1r_2+\vec{r}_1\cdot\vec{r}_2) + r_c^2 r_0^2} \vec{r}_1\times\vec{r}_2 + \end{aligned} + :label: denom -In this case, :math:`F_\nu=1`. The method is found in the work of van -Garrel (:cite:`Garrel03_1`). +Here, the singularity is removed by introducing an additive factor in +the denominator of +Eq. :eq:`eq:BiotSavartSegment`, proportional to +the filament length :math:`r_0`. In this case, :math:`F_\nu=1`. The +method is found in the work of van Garrel +(:cite:`Garrel03_1`). .. _sec:corerad: @@ -421,37 +343,44 @@ parameter value . Constant '''''''' -If a constant value is selected [**WakeRegMethod=0**], the value of +If a constant value is selected, [**WakeRegMethod=0**], the value of :math:`r_c` remains unchanged for all Lagrangian markers throughout the -simulation and taken as the value given with the parameter in meter. +simulation and taken as the value given with the parameter in meters. -.. math:: r_c(\zeta) = r_{c0}\label{cst} +.. math:: + r_c(\zeta) = r_{c0} + :label: cst -where :math:`\zeta` is the vortex wake age, measured from its emission +Here, :math:`\zeta` is the vortex wake age, measured from its emission time. Stretching '''''''''' If the stretching method is selected, [**WakeRegMethod=1**], the viscous -core radius is modeled by Eq. `[stretch] <#stretch>`__. +core radius is modeled by Eq. :eq:`stretch`. -.. math:: r_c(\zeta,\epsilon) = \sqrt{r_{c0}^2+\int_0^\zeta(1+\epsilon)^{-1}d\zeta}\label{stretch} +.. math:: + r_c(\zeta,\epsilon) = \sqrt{r_{c0}^2+\int_0^\zeta(1+\epsilon)^{-1}d\zeta} + :label: stretch -.. math:: \epsilon = \frac{\Delta l}{l} +.. math:: + \epsilon = \frac{\Delta l}{l} -where :math:`\epsilon` is the vortex-filament strain, and :math:`l` is +Here, :math:`\epsilon` is the vortex-filament strain, and :math:`l` is the filament length, and :math:`\Delta l` is the change of length -between two time steps. The integral in Eq. `[stretch] <#stretch>`__ +between two time steps. The integral in Eq. :eq:`stretch` represents strain effects. Wake Age / Core-Spreading ''''''''''''''''''''''''' If the wake age method is selected, [], the viscous core radius is -modeled by Eq. `[age] <#age>`__. +modeled by Eq. :eq:`age`. -.. math:: r_c(\zeta) = \sqrt{r_{c0}^2+4\alpha\delta\nu \zeta}\label{age} +.. math:: + r_c(\zeta) = \sqrt{r_{c0}^2+4\alpha\delta\nu \zeta} + :label: age where :math:`\alpha=1.25643`, :math:`\nu` is kinematic viscosity, and :math:`\delta` is a viscous diffusion parameter (typically between @@ -461,20 +390,22 @@ in the input file as **CoreSpreadEddyVisc**. Here, the term, propagates downstream. The higher the background turbulence, the more diffusion of the vorticity with time, and the higher the value of :math:`\delta` should be. The method is often referred to as the -core-spreading method. It is a way to account to partially account for -viscous diffusion of the vorticity, without solving for the interaction -between the wake vorticity, nor between the vorticity from the wake and -the background flow. Setting is the same as using the wake age method, -[]. +core-spreading method. It is a way to partially account for viscous +diffusion of the vorticity without solving for the interaction between +the wake vorticity or between the vorticity from the wake and the background +flow. Setting **DiffusionMethod==1** is the same as using the wake age method, +[**WakeRegMethod=2**]. Stretching and Wake Age ''''''''''''''''''''''' If the stretching and wake-age method is selected [**WakeRegMethod=3**], the viscous core radius is modeled by -Eq. `[stretchandage] <#stretchandage>`__. +Eq. :eq:`stretchandage`. -.. math:: r_c(\zeta,\epsilon) = \sqrt{r_{c0}^2 + 4\alpha\delta\nu \zeta + \int_0^\zeta(1+\epsilon)^{-1}d\zeta}\label{stretchandage} +.. math:: + r_c(\zeta,\epsilon) = \sqrt{r_{c0}^2 + 4\alpha\delta\nu \zeta + \int_0^\zeta(1+\epsilon)^{-1}d\zeta} + :label: stretchandage .. _sec:diffusion: @@ -484,14 +415,14 @@ Diffusion The viscous-splitting assumption is used to solve for the convection and diffusion of the vorticity separately. The diffusion term :math:`\nu \Delta \vec{\omega}` represents molecular diffusion. This -term will allow for viscous connection of vorticity lines. Also, -turbulent flows will diffuse the vorticity in a similar manner, based on -a turbulent eddy viscosity. +term allows for viscous connection of vorticity lines. Also, turbulent +flows will diffuse the vorticity in a similar manner based on a +turbulent eddy viscosity. -The parameter is used to switch between viscous diffusion methods. -Currently, only the core-spreading method is implemented. The method was -described in since it is equivalent to the increase of the -regularization parameter with the wake age. +The parameter **ViscousDiffusion** is used to switch between viscous diffusion +methods. Currently, only the core-spreading method is implemented. The method +is described in :numref:`sec:corerad` since it is equivalent to the increase of +the regularization parameter with the wake age. .. _sec:circ: @@ -500,37 +431,36 @@ Lifting-Line Circulation The code relies on a lifting-line formulation. Lifting-line methods effectively lump the loads at each cross-section of the blade onto the -mean-line of the blade and do not account directly for the geometry of +mean line of the blade and do not account directly for the geometry of each cross-section. In the vorticity-based version of the lifting-line method, the blade is represented by a line of varying circulation. The -line follows the motion of the blade, and it is referred to as “bound” +line follows the motion of the blade and is referred to as “bound” circulation. The bound circulation does not follow the same dynamic -equation as the free vorticity of the wake. It’s intensity is linked to -the lift of the airfoils via the Kutta-Joukowski theorem. Spanwise +equation as the free vorticity of the wake. Instead, the intensity is +linked to airfoil lift via the Kutta-Joukowski theorem. Spanwise variation of the bound circulation results in vorticity being emitted -into the the wake, and referred to as “trailed vorticity”. Time changes -of the bound circulation are also emitted in the wake, referred to as -“shed” vorticity. Three methods are implemented to determine the bound -circulation strength. They are selected using the input , and are +into the the wake, referred to as “trailed vorticity”. Time changes of +the bound circulation are also emitted in the wake, referred to as “shed” +vorticity. Three methods are implemented to determine the bound circulation +strength. They are selected using the input **CircSolvMethod**, and are presented in the subsequent paragraphs. At the end of a time step, the -circulation of each vortex element is propagated downstream so that -vortex elements with a new intensity can be emitted from the blade at -the next time step. +circulation of each vortex element is propagated downstream so that vortex +elements with a new intensity can be emitted from the blade at the next time +step. -Cl-based iterative method +Cl-Based Iterative Method ^^^^^^^^^^^^^^^^^^^^^^^^^ -The Cl-based iterative method is extensively described in the work from -van Garrel and it is only briefly presented here -(:cite:`Garrel03_1`). The method was implemented following -the same approach and notations as van Garrel. At present, it is the -preferred method to compute the circulation along the blade span. It is -selected with . In this method, the blade is discretized into a finite -number of segments placed along the lifting line (i.e., the blade -aerodynamic center line), representing the bound circulation, -:math:`\Gamma_b`. The circulation is solved within a nonlinear iterative -solver that makes use of the polar data at each control point located on -the lifting line. +The Cl-based iterative method is extensively described in the work from van +Garrel and it is only briefly presented here (:cite:`Garrel03_1`). The method +was implemented following the same approach and notations as van Garrel. At +present, it is the preferred method to compute the circulation along the blade +span. It is selected with **CircSolvMethod==1**. In this method, the blade is +discretized into a finite number of segments placed along the lifting line +(i.e., the blade aerodynamic center line), representing the bound circulation, +:math:`\Gamma_b`. The circulation is solved within a nonlinear iterative solver +that makes use of the polar data at each control point located on the lifting +line. No-flow-through method ^^^^^^^^^^^^^^^^^^^^^^ @@ -561,25 +491,25 @@ manipulated by the module include the following vectors: inputs, :math:`\vec{z}`; outputs, :math:`\vec{y}`; and constant parameters, :math:`\vec{p}`. The vectors are defined as follows: -- Inputs, :math:`\vec{u}~\--` a set of values supplied to the module +- Inputs, :math:`\vec{u}~-` a set of values supplied to the module that, along with the states, are needed to calculate future states and the system’s output. -- Outputs, :math:`\vec{y}~\--` a set of values calculated and returned +- Outputs, :math:`\vec{y}~-` a set of values calculated and returned by the module that depend on the states, inputs, and/or parameters through output equations. -- States, :math:`\vec{x}~\--` a set of internal values of the module +- States, :math:`\vec{x}~-` a set of internal values of the module that are influenced by the inputs and used to calculate future state values and the output. Continuous states are employed, meaning that the states are differentiable in time and characterized by continuous time-differential equations. -- Constraint states, :math:`\vec{z}~\--` algebraic variables that are - calculated using a nonlinear solve, based on values from the current +- Constraint states, :math:`\vec{z}~-` algebraic variables that are + calculated using a nonlinear solver, based on values from the current time step. -- Parameters, :math:`\vec{p}~\--` a set of internal system values that +- Parameters, :math:`\vec{p}~-` a set of internal system values that are independent of the states and inputs. The parameters can be fully defined at initialization and characterize the system’s state equations and output equations. @@ -644,7 +574,7 @@ State, Constraint, and Output Equations An overview of the main states, constraints, and output equations is given in this paragraph. More details are provided in -Section `1.1 <#sec:FVW>`__. The constraint equation is used to determine +:numref:`sec:FVW`. The constraint equation is used to determine the circulation distribution along the span of each lifting line. For the van Garrel method, this circulation is a function of the angle of attack along the blade and the airfoil coefficients. The angle of attack @@ -655,7 +585,8 @@ caused by the vorticity being shed and trailed at the current time step, which in turn is a function of the circulation distribution along the lifting line. This constraint equation may be written as: -.. math:: \vec{Z} = \vec{0} = \vec{\Gamma}_{ll} - \vec{\Gamma}_p(\vec{\alpha}(\vec{x},\vec{u}),\vec{p}) %\label{eq:} +.. math:: + \vec{Z} = \vec{0} = \vec{\Gamma}_{ll} - \vec{\Gamma}_p(\vec{\alpha}(\vec{x},\vec{u}),\vec{p}) where :math:`\vec{\Gamma}_p` is the function that returns the circulation along the blade span, based on the distribution of angle of @@ -665,16 +596,14 @@ specifies the time evolution of the vorticity and the convection of the Lagrangian markers: .. math:: - \begin{aligned} \frac{d \vec{\omega}_e}{dt} &= \left[(\vec{\omega}\cdot\nabla)\vec{v} + \nu\nabla^2 \vec{\omega} \right]_e - %+ (\nabla \cdot T_\text{SGS}) \\ - % ,\quad \frac{d \vec{r}_m}{dt} &= \vec{V}(\vec{r}_m) =\vec{V}_0(\vec{r}_m) + \vec{v}_\omega(\vec{r}_m) =\vec{V}_0(\vec{r}_m) + \vec{V}_\omega(\vec{r}_m, \vec{r}_m, \vec{\omega}) - \label{eq:Convection}\end{aligned} + \end{aligned} + :label: eq:Convection where :math:`\vec{v}_\omega` is the velocity induced by the vorticity in the domain; :math:`\vec{V}_\omega(\vec{r},\vec{r}_m,\vec{\omega})` is @@ -685,17 +614,17 @@ indicates that a quantity is applied to an element. The vorticity, :math:`\vec{\omega}`, is recovered from the vorticity of the vortex elements by means of discrete convolutions. For vortex-segment simulations, the viscous-splitting algorithm is used, and the convection -step (Eq. `[eq:Convection] <#eq:Convection>`__) is the main state +step (Eq. :eq:`eq:Convection`) is the main state equation being solved for. The vorticity stretching is automatically accounted for, and the diffusion is performed *a posteriori*. The velocity function, :math:`\vec{V}_\omega`, uses the Biot-Savart law. The output equation is: .. math:: - \begin{aligned} \vec{y}_1&=\vec{v}_{i,ll} = \vec{V}_\omega ( \vec{r}_{ll}, \vec{r}_m, \vec{\omega})= \\ - \vec{y}_2&=\vec{r}_{r} \end{aligned} + \vec{y}_2&=\vec{r}_{r} + \end{aligned} Integration with AeroDyn15 ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -703,7 +632,7 @@ Integration with AeroDyn15 The vortex code has been integrated as a submodule of the aerodynamic module of OpenFAST, *AeroDyn15*. The data workflow between the different modules and submodules of OpenFAST is illustrated in -Figure `[FAST-FVW] <#FAST-FVW>`__. +Figure :ref:`FAST-FVW`. This integration required a restructuring of the *AeroDyn15* module to isolate the parts of the code related to tower shadow modeling, @@ -717,6 +646,3 @@ requested points by the vortex code. .. [1] The loads on the lifting line are not an output of the vortex code; their calculation is handled by a separate submodule of *AeroDyn*. - -.. |Variable rotor-speed stencil used in time-marching predictor-corrector scheme| image:: Schematics/Stencil.pdf - :name: Stencil From 6ba8b29cb83611209036f84e3ba76d5a482578c8 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Thu, 30 Apr 2020 15:46:29 -0600 Subject: [PATCH 147/190] FVW docs: updated to match LaTeX document --- docs/source/user/aerodyn-fvw/AppendixC.rst | 17 +- .../ExampleFiles/ExampleFile--OLAF.txt | 64 ++- .../ExampleFile--PrescribeCirc.txt | 2 +- docs/source/user/aerodyn-fvw/Introduction.rst | 19 +- docs/source/user/aerodyn-fvw/OLAFTheory.rst | 458 +++++++++--------- .../Schematics/LagrangianMarkers.png | Bin 42195 -> 364547 bytes .../Schematics/VortexCodeWorkFlow.png | Bin 0 -> 98584 bytes .../Schematics/VortexLatticeMethod.pdf | Bin 0 -> 35491 bytes .../Schematics/VortexLatticeMethod.png | Bin 0 -> 34658 bytes docs/source/user/aerodyn-fvw/StateSpace.rst | 186 +++++++ docs/source/user/aerodyn-fvw/bibliography.bib | 7 + docs/source/user/aerodyn-fvw/index.rst | 12 +- 12 files changed, 477 insertions(+), 288 deletions(-) create mode 100644 docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.png create mode 100644 docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.pdf create mode 100644 docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.png create mode 100644 docs/source/user/aerodyn-fvw/StateSpace.rst diff --git a/docs/source/user/aerodyn-fvw/AppendixC.rst b/docs/source/user/aerodyn-fvw/AppendixC.rst index 3dd8805c1e..8189a46dd9 100644 --- a/docs/source/user/aerodyn-fvw/AppendixC.rst +++ b/docs/source/user/aerodyn-fvw/AppendixC.rst @@ -12,13 +12,24 @@ in the **OutNd** list. :math:`B\alpha` is prefixed to each output name, where :math:`\alpha` is a number in the range [1,3], corresponding to the blade number. -.. container:: + +.. list-table:: Available OLAF Output Channels + :widths: 25 15 50 + :header-rows: 1 + :align: center :name: Tab:OLAFoutputs - .. table:: Available OLAF Output Channels + * - Channel Name(s) + - Units + - Description + * - :math:`Gam \beta B \alpha` + - :math:`m^2/s` + - Circulation along the blade + +.. ============================ ============= =========================== Channel Name(s) Units Description ============================ ============= =========================== - :math:`Gamma \beta B \alpha` :math:`m^2/s` Circulation along the blade + :math:`Gam \beta B \alpha` :math:`m^2/s` Circulation along the blade ============================ ============= =========================== diff --git a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt index 029a68f82e..1a6ab698eb 100644 --- a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt +++ b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt @@ -1,30 +1,42 @@ ---------------------------- FREE WAKE INPUT FILE ---------------------------------------------- +--------------------------- FREE WAKE INPUT FILE ---------------------------------------------- Free wake input file for the BAR turbine --------------------------- GENERAL OPTIONS --------------------------------------------------- -5 IntMethod Integration method {4: 2nd order Predictor/Corrector, 5: Forward Euler 1st order, "default": 5} (switch) -0.2 DTfvw Time interval for wake propagation. {"default": dtaero} (s) -5 FreeWakeStart Time when wake is free. (-) value = always free. {"default":0.0} (s) -2.0 FullCircStart Time at which full circulation is reached. {"default": 0.0} (s) +5 IntMethod Integration method {4: 2nd order Predictor/Corrector, 5: Forward Euler 1st order, default: 5} (switch) +0.2 DTfvw Time interval for wake propagation. {default: dtaero} (s) +5 FreeWakeStart Time when wake is free. (-) value = always free. {default: 0.0} (s) +2.0 FullCircStart Time at which full circulation is reached. {default: 0.0} (s) --------------------------- CIRCULATION SPECIFICATIONS ---------------------------------------- -1 CircSolvingMethod Circulation solving method {1: Cl-Based, 2: No-Flow Through, 3: Prescribed, "default":1 }(switch) -0.01 CircSolvConvCrit Convergence criteria {"default": 0.01} [only if CircSolvingMethod=1] (m^2/s) -0.1 CircSolvRelaxation Relaxation factor {"default": 0.1} [only if CircSolvingMethod=1] (-) -30 CircSolvMaxIter Maximum number of iterations for circulation solving {"default": 30} (-) -"NA" PrescribedCircFile File containing prescribed circulation [only if CircSolvingMethod=3] (quoted string) ---------------------------- WAKE OPTIONS ----------------------------------------------------- -50 nNWPanel Number of near-wake panels (-) -7 WakeLength Total wake distance (D) -5 FreeWakeLength Wake length that is free (D) -False FWShedVorticity Include shed vorticity in the far wake {"default": 0.1} -0 DiffusionMethod Diffusion method to account for viscous effects {0: None, 1: Core Spreading, "default": 0} -0 RegDeterMethod Method to determine the regularization parameters {0: Manual, 1: Optimized, "default": 0} -2 RegFunction Viscous diffusion function {0: None, 1: Rankine, 2: LambOseen, 3: Vatistas, 4: Denominator, "default": 3} (switch) -0 WakeRegMethod Wake regularization method {1: Constant, 2: Stretching, 3: Age, "default": 1} (switch) -2.0 WakeRegFactor Wake regularization factor (m) -2.0 WingRegFactor Wing regularization factor (m) -100 CoreSpreadEddyVisc Eddy viscosity in core spreading methods, typical values 1-1000 +1 CircSolvingMethod Circulation solving method {1: Cl-Based, 2: No-Flow Through, 3: Prescribed, default: 1 }(switch) +0.01 CircSolvConvCrit Convergence criteria {default: 0.001} [only if CircSolvingMethod=1] (-) +0.1 CircSolvRelaxation Relaxation factor {default: 0.1} [only if CircSolvingMethod=1] (-) +30 CircSolvMaxIter Maximum number of iterations for circulation solving {default: 30} (-) + "NA" PrescribedCircFile File containing prescribed circulation [only if CircSolvingMethod=3] (quoted string) +=============================================================================================== +--------------------------- WAKE OPTIONS ------------------------------------------------------ +------------------- WAKE EXTENT AND DISCRETIZATION -------------------------------------------- +50 nNWPanel Number of near-wake panels (-) +7 WakeLength Total wake distance (D) +5 FreeWakeLength Wake length that is free (D) +False FWShedVorticity Include shed vorticity in the far wake {default: 0.1} +------------------- WAKE REGULARIZATIONS AND DIFFUSION ----------------------------------------- +0 DiffusionMethod Diffusion method to account for viscous effects {0: None, 1: Core Spreading, "default": 0} +0 RegDeterMethod Method to determine the regularization parameters {0: Manual, 1: Optimized, default: 0 } +2 RegFunction Viscous diffusion function {0: None, 1: Rankine, 2: LambOseen, 3: Vatistas, 4: Denominator, "default": 3} (switch) +0 WakeRegMethod Wake regularization method {1: Constant, 2: Stretching, 3: Age, default: 1} (switch) +2.0 WakeRegFactor Wake regularization factor (m) +2.0 WingRegFactor Wing regularization factor (m) +100 CoreSpreadEddyVisc Eddy viscosity in core spreading methods, typical values 1-1000 +------------------- WAKE TREATMENT OPTIONS --------------------------------------------------- +False TwrShadowOnWake Include tower flow disturbance effects on wake convection {default:false} [only if TwrPotent or TwrShadow] +0 ShearModel Shear Model {0: No treatment, 1: Mirrored vorticity, default: 0} +------------------- SPEEDUP OPTIONS ----------------------------------------------------------- +2 VelocityMethod Method to determine the velocity {1:Biot-Savart Segment, 2:Particle tree, default: 1} +1.5 TreeBranchFactor Branch radius fraction above which a multipole calculation is used {default: 2.0} [only if VelocityMethod=2] +1 PartPerSegment Number of particles per segment [only if VelocityMethod=2] +=============================================================================================== --------------------------- OUTPUT OPTIONS --------------------------------------------------- -True WrVTk Outputs Visualization Toolkit (VTK) (independent of .fst option) {False: NoVTK, True: Write VTK at each time step} (flag) -1 nVTKBlades Number of blades for which VTK files are exported {0: No VTK per blade, n: VTK for blade 1 to n} (-) -2 VTKCoord Coordinate system used for VTK export. {1: Global, 2: Hub, "default": 1} -"default" VTK_fps Frame rate for VTK output (frames per second) {"all" for all glue code timesteps, "default" for all FVW timesteps} [used only if WrVTK=1] +True WrVTk Outputs Visualization Toolkit (VTK) (independent of .fst option) {False: NoVTK, True: Write VTK at each time step} (flag) +1 nVTKBlades Number of blades for which VTK files are exported {0: No VTK per blade, n: VTK for blade 1 to n} (-) +2 VTKCoord Coordinate system used for VTK export. {1: Global, 2: Hub, "default": 1} +default VTK_fps Frame rate for VTK output (frames per second) {"all" for all glue code timesteps, "default" for all FVW timesteps} [used only if WrVTK=1] +------------------------------------------------------------------------------------------------ diff --git a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--PrescribeCirc.txt b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--PrescribeCirc.txt index aebd02d869..b174db2ca0 100644 --- a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--PrescribeCirc.txt +++ b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--PrescribeCirc.txt @@ -1,4 +1,4 @@ -r/R [-], Gamma [m$^2$/s] +r/R [-], Gamma [m^2/s] 0.048488, 0.000000 0.087326, 0.442312 0.126163, 6.909277 diff --git a/docs/source/user/aerodyn-fvw/Introduction.rst b/docs/source/user/aerodyn-fvw/Introduction.rst index 6950315714..67420ff773 100644 --- a/docs/source/user/aerodyn-fvw/Introduction.rst +++ b/docs/source/user/aerodyn-fvw/Introduction.rst @@ -2,17 +2,14 @@ Introduction ============ - -cOnvecting LAgrangian Filaments (OLAF) is a free vortex wake (FVW) -module used to compute the aerodynamic forces on a set of moving wings, -which, in particular, can be applied to two- or three-bladed -horizontal-axis wind turbines. This module has been incorporated into -the National Renewable Energy Laboratory physics-based engineering tool, -OpenFAST, which solves the aero-hydro-servo-elastic dynamics of -individual wind turbines. OLAF is incorporated into the OpenFAST module, -*AeroDyn15*, as an alternative to the traditional blade-element momentum -(BEM) option, as shown in Figures :numref:`figOpenFAST_a` and -:numref:`figOpenFAST_b`. +cOnvecting LAgrangian Filaments (OLAF) is a free vortex wake (FVW) module used +to compute the aerodynamic forces on moving two- or three-bladed horizontal-axis +wind turbines. This module has been incorporated into the National Renewable +Energy Laboratory physics-based engineering tool, OpenFAST, which solves the +aero-hydro-servo-elastic dynamics of individual wind turbines. OLAF is +incorporated into the OpenFAST module, *AeroDyn15*, as an alternative to the +traditional blade-element momentum (BEM) option, as shown in +Figures :numref:`figOpenFAST_a` and :numref:`figOpenFAST_b`. .. _figOpenFAST_a: diff --git a/docs/source/user/aerodyn-fvw/OLAFTheory.rst b/docs/source/user/aerodyn-fvw/OLAFTheory.rst index 1540f491dc..0ddb12877f 100644 --- a/docs/source/user/aerodyn-fvw/OLAFTheory.rst +++ b/docs/source/user/aerodyn-fvw/OLAFTheory.rst @@ -1,4 +1,4 @@ -.. _OLAF_Theory: +.. _sec:FVW: OLAF Theory =========== @@ -7,15 +7,11 @@ This section details the FVW method and provides an overview of the computational method, followed by a brief explanation of its integration with OpenFAST. -.. _sec:FVW: - -Free Vortex Wake Model ----------------------- .. _sec:vorticityformulation: Introduction - Vorticity formulation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------ The vorticity equation for incompressible homogeneous flows in the absence of non-conservative force is given by @@ -50,7 +46,7 @@ in the following sections. .. _sec:discretization: Discretization - Projection -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------- The numerical method uses a finite number of states to model the continuous vorticity distribution. To achieve this, the vorticity @@ -87,7 +83,7 @@ field and avoids the singularities that would otherwise occur. .. _sec:vortconv: Vortex Convection -~~~~~~~~~~~~~~~~~ +----------------- The governing equation of motion for a vortex filament is given by the convection equation of a Lagrangian marker: @@ -103,7 +99,7 @@ automatically accounts for the strain part of the vorticity equation. .. _sec:vortconvPolar: Vortex Convection in Polar Coordinates -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------------------- The governing equation of motion for a vortex filament is given by: @@ -133,7 +129,7 @@ Eq. :eq:`Euler`. :label: Euler Induced Velocity and Velocity Field -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------------------- The velocity term on the right-hand side of Eq. :eq:`VortFilCart` is a nonlinear function of the @@ -181,7 +177,7 @@ the main flow, :math:`\vec{V}_0`, (here assumed divergence free): .. math:: \begin{aligned} - \vec{V}(\vec{x}) = \vec{V}_0 + \sum_{k} \vec{v}_k(\vec{x}) + \vec{V}(\vec{x}) = \vec{V}_0(\vec{x}) + \vec{v}_\omega(\vec{x}), \quad\text{with}\quad \vec{v}_\omega(\vec{x}) = \sum_{k} \vec{v}_k(\vec{x}) \end{aligned} where the sum is over all the vortex filaments, each of intensity @@ -192,10 +188,10 @@ spanwise and time changes of the bound circulation, as discussed in .. _sec:Regularization: Regularization -~~~~~~~~~~~~~~ +-------------- Regularization and viscous diffusion -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The singularity that occurs in Eq. :eq:`BiotSavart` greatly affects the numerical accuracy of vortex methods. By @@ -233,44 +229,43 @@ clarification is required, but a loose terminology is used when the context is clear enough. Determination of the regularization parameter -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The regularization parameter is both a function of the physics being modelled (blade boundary layer and wake) and the choice of discretization. Contributing factors are the chord length, the boundary layer height, and the volume that each vortex filament is approximating. -Currently the choice is left to the user (**RegDetMethod\ =0**). +Currently the choice is left to the user (**RegDetMethod=[0]**). Empirical results for a rotating blade are found in the work of Gupta (:cite:`Gupta06_1`). As a guideline, the regularization parameter may be chosen as twice the average spanwise discretization of the blade. The current implementation will implement -this guideline when the user chooses **RegDetMethod\ =1**. Further +this guideline when the user chooses **RegDetMethod=[1]**. Further refinement of this option will be considered in the future. .. _sec:RegularizationFunction: Regularization functions implemented -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Several regularization functions have been developed (:cite:`Rankine58_1,Scully75_1,Vatistas91_1`). At present, five options are available: (1) No correction, (2) the Rankine method, (3) the Lamb-Oseen method, (4) the Vatistas method, or (5) the denominator offset method. If no correction method is used, -[**RegFunction=0**], :math:`F_\nu=1`. The remaining methods are detailed +[**RegFunction=[0]**], :math:`F_\nu=1`. The remaining methods are detailed in the following sections. The regularization parameter (**WakeRegParam**) is noted :math:`r_c` and the distance to the filament -is written :math:`\rho`. The different functions are compared in -:numref:`FilamentRegularization`. +is written :math:`\rho`. Rankine -''''''' +^^^^^^^ The Rankine method (:cite:`Rankine58_1`) is the simplest regularization model. With this method, the Rankine vortex has a finite core with a solid body rotation near the vortex center and a potential vortex away from the center. If this method is used, -[**RegFunction=1**], the viscous core correction is given by +[**RegFunction=[1]**], the viscous core correction is given by Eq. :eq:`rankine`. .. math:: @@ -282,9 +277,9 @@ Here, :math:`r_c` is the viscous core radius of a vortex filament, detailed in :numref:`sec:corerad`. Lamb-Oseen -'''''''''' +^^^^^^^^^^ -If this method is used, [**RegFunction=2**], the viscous core correction +If this method is used, [**RegFunction=[2]**], the viscous core correction is given by Eq. :eq:`lamboseen`. .. math:: @@ -292,9 +287,9 @@ is given by Eq. :eq:`lamboseen`. :label: lamboseen Vatistas -'''''''' +^^^^^^^^ -If this method is used, [**RegFunction=3**], the viscous core correction +If this method is used, [**RegFunction=[3]**], the viscous core correction is given by Eq. :eq:`vatistas`. .. math:: @@ -308,10 +303,10 @@ point (:cite:`Abedi16_1`). Research from rotorcraft applications suggests a value of :math:`n=2`, which is used in this work (:cite:`Bagai93_1`). -Denominator offset/cut-off -'''''''''''''''''''''''''' +Denominator Offset/Cut-Off +^^^^^^^^^^^^^^^^^^^^^^^^^^ -If this method is used, [**RegFunction=4**], the viscous core correction +If this method is used, [**RegFunction=[4]**], the viscous core correction is given by Eq. :eq:`denom` .. math:: @@ -331,19 +326,19 @@ method is found in the work of van Garrel .. _sec:corerad: Time Evolution of the Regularization Parameter–Core Spreading Method -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are four available methods by which the regularization parameter may evolve with time: (1) constant value, (2) stretching, (3) wake age, or (4) stretching and wake age. The three latter methods blend the notion of viscous diffusion with the notion of regularization. The notation :math:`r_{c0}` used in this section corresponds to input file -parameter value . +parameter value **WakeRegParam**. Constant -'''''''' +^^^^^^^^ -If a constant value is selected, [**WakeRegMethod=0**], the value of +If a constant value is selected, (**WakeRegMethod=[0]**), the value of :math:`r_c` remains unchanged for all Lagrangian markers throughout the simulation and taken as the value given with the parameter in meters. @@ -355,9 +350,9 @@ Here, :math:`\zeta` is the vortex wake age, measured from its emission time. Stretching -'''''''''' +^^^^^^^^^^ -If the stretching method is selected, [**WakeRegMethod=1**], the viscous +If the stretching method is selected, (**WakeRegMethod=[1]**), the viscous core radius is modeled by Eq. :eq:`stretch`. .. math:: @@ -373,10 +368,10 @@ between two time steps. The integral in Eq. :eq:`stretch` represents strain effects. Wake Age / Core-Spreading -''''''''''''''''''''''''' +^^^^^^^^^^^^^^^^^^^^^^^^^ -If the wake age method is selected, [], the viscous core radius is -modeled by Eq. :eq:`age`. +If the wake age method is selected, (WakeRegMethod=[2]), the viscous core radius +is modeled by Eq. :eq:`age`. .. math:: r_c(\zeta) = \sqrt{r_{c0}^2+4\alpha\delta\nu \zeta} @@ -393,13 +388,13 @@ diffusion of the vorticity with time, and the higher the value of core-spreading method. It is a way to partially account for viscous diffusion of the vorticity without solving for the interaction between the wake vorticity or between the vorticity from the wake and the background -flow. Setting **DiffusionMethod==1** is the same as using the wake age method, -[**WakeRegMethod=2**]. +flow. Setting **DiffusionMethod=[1]** is the same as using the wake age method, +(**WakeRegMethod=[2]**). Stretching and Wake Age -''''''''''''''''''''''' +^^^^^^^^^^^^^^^^^^^^^^^ -If the stretching and wake-age method is selected [**WakeRegMethod=3**], +If the stretching and wake-age method is selected (**WakeRegMethod=[3]**), the viscous core radius is modeled by Eq. :eq:`stretchandage`. @@ -410,7 +405,7 @@ Eq. :eq:`stretchandage`. .. _sec:diffusion: Diffusion -~~~~~~~~~ +--------- The viscous-splitting assumption is used to solve for the convection and diffusion of the vorticity separately. The diffusion term @@ -426,8 +421,8 @@ the regularization parameter with the wake age. .. _sec:circ: -Lifting-Line Circulation -~~~~~~~~~~~~~~~~~~~~~~~~ +Lifting-Line Representation +--------------------------- The code relies on a lifting-line formulation. Lifting-line methods effectively lump the loads at each cross-section of the blade onto the @@ -440,209 +435,200 @@ equation as the free vorticity of the wake. Instead, the intensity is linked to airfoil lift via the Kutta-Joukowski theorem. Spanwise variation of the bound circulation results in vorticity being emitted into the the wake, referred to as “trailed vorticity”. Time changes of -the bound circulation are also emitted in the wake, referred to as “shed” -vorticity. Three methods are implemented to determine the bound circulation -strength. They are selected using the input **CircSolvMethod**, and are -presented in the subsequent paragraphs. At the end of a time step, the -circulation of each vortex element is propagated downstream so that vortex -elements with a new intensity can be emitted from the blade at the next time -step. +the bound circulation are also emitted in the wake, referred to as +“shed” vorticity. The subsequent paragraphs describe the representation +of the bound vorticity. + +Lifting-Line Panels and Emitted Wake Panels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The lifting-line and wake representation is illustrated in +:numref:`fig:VortexLatticeMethod`. The blade lifting-line is discretized into a +finite number of panels, each of them forming a four sided vortex rings. The +spanwise discretization follows the discretization of the AeroDyn blade input +file. The number of spanwise panels, :math:`n_\text{LL}`, is one less than the +total number of AeroDyn nodes, **NumBlNds**. The sides of the panels coincide +with the lifting-line and the trailing edge of the blade. The lifting-line is +currently defined as the 3/4 chord location. More details on the panelling is +provided in :numref:`sec:Panelling`. At a given time step, the circulation of +each lifting-line panel is determined according to one of the three methods +developed in :numref:`sec:CirculationMethods`. At the end of the time step, the +circulation of each lifting-line panel is emitted into the wake, forming free +vorticity panels. The circulation of the first near wake panel and the bound +circulation are equivalent, to satisfy the Kutta condition (see +:numref:`fig:VortexLatticeMethod` b). The wake panels model the thin shear +layer resulting from the continuation of the blade boundary layer. This shear +layer can be modelled using a continuous distribution of vortex doublets. A +constant doublet strength is assumed on each panel, which in turn is equivalent +to a vortex ring of constant circulation. + +.. figure:: Schematics/VortexLatticeMethod.png + :alt: Wake and lifting-line vorticity discretized into vortex ring panels. + :name: fig:VortexLatticeMethod + :width: 100.0% + + Wake and lifting-line vorticity discretized into vortex ring panels. + (a) Overview. (b) Cross-sectional view, defining the leading-edge, + trailing edge, and lifting-line. (c) Circulation of panels and + corresponding circulation for vorticity segments between panels. (d) + Geometrical quantities for a lifting-line panel. + +The current implementation stores the positions and circulations of the +panel corner points. In the vortex ring formulation, the boundary +between two panels corresponds to a vortex segment of intensity equal to +the difference of circulation between the two panels. The convention +used to define the segment intensity based on the panels intensity is +shown in :numref:`fig:VortexLatticeMethod` c. Since the +circulation of the bound panels and the first row of near wake panels +are equal, the vortex segments located on the trailing edge have no +circulation. + +.. _sec:Panelling: + +Panelling +~~~~~~~~~ + +The definitions used for the panelling of the blade are given in +:numref:`fig:VortexLatticeMethod` d, following the notations of van +Garrel (:cite:`Garrel03_1`). The leading edge (LE) and +trailing edge (TE) locations are directly obtained from the AeroDyn +mesh. At two spanwise locations, the LE and TE define the corner points: +:math:`\vec{x}_1`, :math:`\vec{x}_2`, :math:`\vec{x}_3`, and +:math:`\vec{x}_4`. The current implementation assumes that the +aerodynamic center, the lifting-line, and the 3/4 chord location all +coincide. For a given panel, the lifting-line is then delimited by the +points :math:`\vec{x}_9= 3/4\,\vec{x}_1 + 1/4\, \vec{x}_2` and +:math:`\vec{x}_{10}=3/4\,\vec{x}_4 + 1/4\, \vec{x}_3`. The mid points of +the four panel sides are noted :math:`\vec{x}_5`, :math:`\vec{x}_6`, +:math:`\vec{x}_7`, and :math:`\vec{x}_8`. The lifting-line vector +(:math:`\vec{dl}`) as well as the vectors tangential (:math:`\vec{T}`) +and normal (:math:`\vec{N}`) to the panel are defined as: + +.. math:: + \begin{aligned} + \vec{dl} = \vec{x}_{10}-\vec{x}_9 + ,\qquad + \vec{T} = \frac{\vec{x}_6-\vec{x}_8}{|\vec{x}_6-\vec{x}_8|} + ,\qquad + \vec{N} = \frac{\vec{T}\times\vec{dl}}{|\vec{T}\times\vec{dl}|} + \end{aligned} + :label: eq:GeometricDefinitions + +The area of the panel is obtained as :math:`dA = +|(\vec{x}_6-\vec{x}_8)\times(\vec{x}_{7}-\vec{x}_5)|`. For +**CircSolvMethod=[3]**, the control points are located on the lifting-line at the +location :math:`\vec{x}_9+\eta_j \vec{dl}`. The factor :math:`\eta_j` is +determined based on the full-cosine approximation of van Garrel. This is based +on the spanwise widths of the current panel, :math:`w_j`, and the neighboring +panels :math:`w_{j-1}` and :math:`w_{j+1}`: + +.. math:: + \begin{aligned} + \eta_j=\frac{1}{4}\left[\frac{w_{j-1}}{w_{j-1}+w_j} + \frac{w_j}{w_j+w_{j+1}} +1 \right] + ,\ j=2..n-1 + ,\quad + \eta_1 = \frac{w_1}{w_1+w_2} + ,\quad + \eta_{n} = \frac{w_{n-1}}{w_{n-1}+w_{n}} + \end{aligned} + +For an equidistant spacing, this discretization places the control +points at the middle of the lifting-line (:math:`\eta=0.5`). Theoretical +circulation results for an elliptic wing with a cosine spacing are +retrieved with such discretization since it places the control points +closer to stronger trailing segments at the wing extremities (see +e.g. :cite:`Kerwin:lecturenotes`). + +.. _sec:CirculationMethods: + +Circulation Solving Methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Three methods are implemented to determine the bound circulation +strength. They are selected using the input , and are presented in the +following sections. Cl-Based Iterative Method ^^^^^^^^^^^^^^^^^^^^^^^^^ -The Cl-based iterative method is extensively described in the work from van -Garrel and it is only briefly presented here (:cite:`Garrel03_1`). The method -was implemented following the same approach and notations as van Garrel. At -present, it is the preferred method to compute the circulation along the blade -span. It is selected with **CircSolvMethod==1**. In this method, the blade is -discretized into a finite number of segments placed along the lifting line -(i.e., the blade aerodynamic center line), representing the bound circulation, -:math:`\Gamma_b`. The circulation is solved within a nonlinear iterative solver -that makes use of the polar data at each control point located on the lifting -line. - -No-flow-through method +The Cl-based iterative method determines the circulation within a +nonlinear iterative solver that makes use of the polar data at each +control point located on the lifting line. The algorithm ensures that +the lift obtained using the angle of attack and the polar data matches +the lift obtained with the Kutta-Joukowski theorem. At present, it is +the preferred method to compute the circulation along the blade span. It is +selected with **CircSolvMethod=[1]**. The method is described in the work from +van Garrel (:cite:`Garrel03_1`). The algorithm is implemented in at iterative +approach using the following steps: + +#. The circulation distribution from the previous time step is used as a + guessed circulation, :math:`\Gamma_\text{prev}`. + +#. The velocity at each control points :math:`j` is computed as the sum + of the wind velocity, the structural velocity, and the velocity + induced by all the vorticity in the domain, evaluated at the control + point location. + + .. math:: + \begin{aligned} + \vec{v}_j = \vec{V}_0 - \vec{V}_\text{elast} + \vec{v}_{\omega,\text{free}} + \vec{v}_{\Gamma_{ll}} + \end{aligned} + + The contribution of :math:`\vec{v}_{\Gamma_{ll}}` comes from the + lifting-line panels and the first row of near wake panels, for which + the circulation is set to :math:`\Gamma_\text{prev}` + +#. The circulation for all lifting-line panels :math:`j` is obtained as + follows. + + .. math:: + \begin{aligned} + \Gamma_{ll,j} =\frac{1}{2} C_{l,j}(\alpha_j) \frac{\left[ (\vec{v}_j \cdot \vec{N})^2 + (\vec{v}_j \cdot \vec{T})^2\right]^2\,dA}{ + \sqrt{\left[(\vec{v}_j\times \vec{dl})\cdot\vec{N}\right]^2 + \left[(\vec{v}_j\times \vec{dl})\cdot\vec{T}\right]^2} + } %\label{eq:} + ,\quad\text{with} + \quad + \alpha_j = \operatorname{atan}\left(\frac{\vec{v}_j\cdot\vec{N}}{\vec{v}_j \cdot \vec{T}} \right) + \end{aligned} + + The function :math:`C_{l,j}` is the lift coefficient obtained from + the polar data of blade section :math:`j` and :math:`\alpha_j` is the + angle of attack at the control point. + +#. The new circulation is set using the relaxation factor + :math:`k_\text{relax}` (**CircSolvRelaxation**): + + .. math:: + \begin{aligned} + \Gamma_\text{new}= \Gamma_\text{prev} + k_\text{relax} \Delta \Gamma + ,\qquad + \Delta \Gamma = \Gamma_{ll} - \Gamma_\text{prev} %\label{eq:} + \end{aligned} + +#. Convergence is checked using the criterion :math:`k_\text{crit}` +(**CircSolvConvCrit**): + + .. math:: + \begin{aligned} + \frac{ \operatorname{max}(|\Delta \Gamma|}{\operatorname{mean}(|\Gamma_\text{new}|)} < k_\text{crit} + \end{aligned} + + If convergence is not reached, steps 2-5 are repeated using + :math:`\Gamma_\text{new}` as the guessed circulation + :math:`\Gamma_\text{prev}`. + +No-flow-through Method ^^^^^^^^^^^^^^^^^^^^^^ A Weissinger-L-based representation (:cite:`Weissinger47_1`) of the lifting surface is also available (:cite:`Bagai94_1,Gupta06_1,Ribera07_1`). In this method, the circulation is solved by satisfying a no-flow through -condition at the 3/4-chord points. +condition at the 3/4-chord points. It is selected with **CircSolvMethod=[2]**. -Prescribed circulation +Prescribed Circulation ^^^^^^^^^^^^^^^^^^^^^^ The final available method prescribes a constant circulation. A user specified spanwise distribution of circulation is prescribed onto the -blades. - -State-Space Representation and Integration with OpenFAST --------------------------------------------------------- - -The OLAF module has been integrated into the latest version of OpenFAST -via *AeroDyn15*, following the OpenFAST modularization -framework (:cite:`Jonkman13_1,Sprague15_1`). To follow the -OpenFAST framework, the vortex code is written as a module, and its -formulation comprises state, constraint, and output equations. The data -manipulated by the module include the following vectors: inputs, -:math:`\vec{u}`; states, :math:`\vec{x}`; constrained state, -:math:`\vec{z}`; outputs, :math:`\vec{y}`; and constant parameters, -:math:`\vec{p}`. The vectors are defined as follows: - -- Inputs, :math:`\vec{u}~-` a set of values supplied to the module - that, along with the states, are needed to calculate future states - and the system’s output. - -- Outputs, :math:`\vec{y}~-` a set of values calculated and returned - by the module that depend on the states, inputs, and/or parameters - through output equations. - -- States, :math:`\vec{x}~-` a set of internal values of the module - that are influenced by the inputs and used to calculate future state - values and the output. Continuous states are employed, meaning that - the states are differentiable in time and characterized by continuous - time-differential equations. - -- Constraint states, :math:`\vec{z}~-` algebraic variables that are - calculated using a nonlinear solver, based on values from the current - time step. - -- Parameters, :math:`\vec{p}~-` a set of internal system values that - are independent of the states and inputs. The parameters can be fully - defined at initialization and characterize the system’s state - equations and output equations. - -The parameters of the vortex code include: - -- Fluid characteristics: kinematic viscosity, :math:`\nu` - -- Airfoil characteristics: polar data: (:math:`C_l(\alpha)`, - :math:`C_d(\alpha)`, :math:`C_m(\alpha)`), and chord :math:`c` - -- Algorithmic methods and parameters for regularization, viscous - diffusion, discretization, wake geometry, acceleration, and so on. - -The inputs of the vortex code are: - -- Position, orientation, translational velocity, and rotational - velocity of the different nodes of the lifting lines - (:math:`\vec{r}_{ll}`, :math:`\Lambda_{ll}`, - :math:`\vec{\dot{r}}_{ll}`, and :math:`\vec{\omega}_{ll}`, - respectively), gathered into the vector, - :math:`\vec{x}_{\text{elast},ll}`, for conciseness. These quantities - are handled using the mesh-mapping functionality and data structure - of OpenFAST. - -- Undisturbed velocity field at requested locations (lifting-line - points, :math:`\vec{r}_{ll}`, and a set of locations requested by the - vortex code, :math:`\vec{r}_r`), written - :math:`\vec{v}_0=[\vec{v}_{0,ll}, \vec{v}_{0,r}]`. Based on the - parameters, this velocity field may contain the following influences: - freestream, shear, veer, turbulence, tower, and nacelle disturbance. - The locations where the velocity field is requested are typically the - location of the Lagrangian markers. - -The constraint states are: - -- The circulation intensity along the lifting lines, - :math:`\Gamma_{ll}`. - -The continuous states are: - -- The position of the Lagrangian markers, :math:`\vec{r}_m` - -- The vorticity associated with each vortex element, - :math:`\vec{\omega}_e`. For a projection of the vorticity onto vortex - segments, this corresponds to the circulation, - :math:`\vec{\Gamma}_e`, where for each segment, - :math:`\vec{\Gamma}_e= \Gamma_e \vec{dl}_e =\vec{\omega}_e dV_e`, - with :math:`\vec{dl}_e` and :math:`dV_e`, the vortex segment length - and its equivalent vortex volume. - -The outputs are [1]_: - -- The induced velocity at the lifting-line nodes, - :math:`\vec{v}_{i,ll}` - -- The locations where the undisturbed wind needs to be computed, - :math:`\vec{r}_{r}` (typically :math:`\vec{r_{r}}=\vec{r}_m`). - -State, Constraint, and Output Equations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An overview of the main states, constraints, and output equations is -given in this paragraph. More details are provided in -:numref:`sec:FVW`. The constraint equation is used to determine -the circulation distribution along the span of each lifting line. For -the van Garrel method, this circulation is a function of the angle of -attack along the blade and the airfoil coefficients. The angle of attack -at a given lifting-line node is a function of the undisturbed velocity, -:math:`\vec{v}_{0,ll}`, and the velocity induced by the vorticity, -:math:`\vec{v}_{i,ll}`, at that point. Part of the induced velocity is -caused by the vorticity being shed and trailed at the current time step, -which in turn is a function of the circulation distribution along the -lifting line. This constraint equation may be written as: - -.. math:: - \vec{Z} = \vec{0} = \vec{\Gamma}_{ll} - \vec{\Gamma}_p(\vec{\alpha}(\vec{x},\vec{u}),\vec{p}) - -where :math:`\vec{\Gamma}_p` is the function that returns the -circulation along the blade span, based on the distribution of angle of -attacks and the airfoil characteristics. In practice, this nonlinear -equation is solved using an iterative algorithm. The state equation -specifies the time evolution of the vorticity and the convection of the -Lagrangian markers: - -.. math:: - \begin{aligned} - \frac{d \vec{\omega}_e}{dt} &= \left[(\vec{\omega}\cdot\nabla)\vec{v} + \nu\nabla^2 \vec{\omega} \right]_e - \\ - \frac{d \vec{r}_m}{dt} &= \vec{V}(\vec{r}_m) - =\vec{V}_0(\vec{r}_m) + \vec{v}_\omega(\vec{r}_m) - =\vec{V}_0(\vec{r}_m) + \vec{V}_\omega(\vec{r}_m, \vec{r}_m, \vec{\omega}) - \end{aligned} - :label: eq:Convection - -where :math:`\vec{v}_\omega` is the velocity induced by the vorticity in -the domain; :math:`\vec{V}_\omega(\vec{r},\vec{r}_m,\vec{\omega})` is -the function that computes this induced velocity at a given point, -:math:`\vec{r}`, based on the location of the Lagrangian markers and the -intensity of the vortex elements; and the subscript, :math:`e`, -indicates that a quantity is applied to an element. The vorticity, -:math:`\vec{\omega}`, is recovered from the vorticity of the vortex -elements by means of discrete convolutions. For vortex-segment -simulations, the viscous-splitting algorithm is used, and the convection -step (Eq. :eq:`eq:Convection`) is the main state -equation being solved for. The vorticity stretching is automatically -accounted for, and the diffusion is performed *a posteriori*. The -velocity function, :math:`\vec{V}_\omega`, uses the Biot-Savart law. The -output equation is: - -.. math:: - \begin{aligned} - \vec{y}_1&=\vec{v}_{i,ll} = \vec{V}_\omega ( \vec{r}_{ll}, \vec{r}_m, \vec{\omega})= \\ - \vec{y}_2&=\vec{r}_{r} - \end{aligned} - -Integration with AeroDyn15 -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The vortex code has been integrated as a submodule of the aerodynamic -module of OpenFAST, *AeroDyn15*. The data workflow between the different -modules and submodules of OpenFAST is illustrated in -Figure :ref:`FAST-FVW`. - -This integration required a restructuring of the *AeroDyn15* module to -isolate the parts of the code related to tower shadow modeling, -induction computation, lifting-line-forces computations, and dynamic -stall. The dynamic stall model will be adapted when used in conjunction -with the vortex code to ensure the effect of shed vorticity is not -accounted for twice. The interface between *AeroDyn15* and the inflow -module, *InflowWind*, was accommodated to include the additionally -requested points by the vortex code. - -.. [1] - The loads on the lifting line are not an output of the vortex code; - their calculation is handled by a separate submodule of *AeroDyn*. +blades. It is selected with **CircSolvMethod=[3]**. diff --git a/docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.png b/docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.png index 0b32bc74da9438cd41aeca70a7487d08af12c839..ea4628c990f0385f2a5ef3ce6a34a890006d9bbb 100644 GIT binary patch literal 364547 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%0A+pK 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�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 z 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

tm)bDuz2m*oW}W!m%+%IjW?Mbbi80Tmjpx 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 L-^`C)Yx@5I%yNJ5 diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.png b/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.png new file mode 100644 index 0000000000000000000000000000000000000000..a4973027c196cf228040bbdf219947723f306e2f GIT binary patch literal 98584 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 ZD>cL4%bmr{=|}|sVPutMaxWPB{2!I7u^<2d literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.pdf b/docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d4486621d83faf3eb86c92c0fb0eec0d57b69496 GIT binary patch literal 35491 zcmcG$WpEtLvNkwkw3wN}VjeLwGfNgr7Be$5nZ#%@Gc)6enZYAQi)F3+o^$TK-?zIz zHg-2+qPr_AYbrAb2y4Lfgibep0= z@SdxBF8OXF^w&|EucFtX??9A5ENo-vE#o%Ofp@w+s~YP_d@o#?MSbbm0htEvZAQ9@ z6%!Q|drfO^!o+vg8e{q!u6(O!Pj#<*9_yEH!hc@RSOVL({`|>l%94E>4s1R9X2-&| z`DdY~yD9LG$@3qa&8b&A>O|o`ix_V;gKL59uX|_P#n1Y-zoTdo7%0A7HZ}Y3-@V}I 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 literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.png b/docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.png new file mode 100644 index 0000000000000000000000000000000000000000..2db30e3972645616f04adac036539d4475872dac GIT binary patch literal 34658 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

U|<3g23UpIB!ObsfB3C znk$b3+bYlSR@tFX-&sL%$`BYKwmuNSfLn(R$UexpB=AjMi;ye&HZUpu+C;(U@gWAr z4OI}PCMBK|bMmd%f6{1%?gtFN+SF4iYzpE##rYLqM}%xA7L}5jc%st!=a5z5dp;ZW zSr$1HY3d7ocX!q(R#XoVhEg^aArD9^V?4ozyTF(L*FzG~xIrohIL^&GVkwvE#Is@* zBixlvtJAn>gkIqOEMal#URu}EV*)PVTITEs$K`<-jX@1~+Q4XG&412N#8qyF&*Pye zD7Y&e9Dq22tF+KOI4^yN+QZF7M#Q5Eo!J*jG3@LC@_iYE+o1ca{_DqfH)6$S&pZxo zm?0jo^Z#PaPSR+GL5&S{TSFq;v!=f$yr&%2v8BqcWUBAtFGO#7Sy`gNX}f`_-B9^E zoWs>9r#eqt1k2O@cv_L6`{CTv-|@!0REOVNNgULFb^K0`Ba*(Vdj@cqP8`%+rq-*h zYk1bElKhfFk6cf1+<6G>=oDw;#f0e%ETxiSKzv&6F-GTg8COTHxUVcAkW>>I)|{-< zSq@;@XQGWw1K_jihCE%1m&!8rKa#_~ArheFIdrob>D7ai+jdeI;gn*F6G6`hBjTwE zJPe>83G9HS`)zY*7Ha7M=(w%1;=5S_6zPXKOu4SCm7C=u9qA>;ds&tACAo}F$b*+; zulhu2ce*bSRh0lbroVh)uvJo?U+Vc}dA$7mqdtANt~sfEL1&n~m+A^AYAjw3ITC;1G(bX|F{oD@ z8o$0#vgd>4X?Xb*Q)y{2ZGxnrB%XvLz-jJ=4|M%UAx_kkC=O6(51-Pu83!yW#~)QXEHds{tfbJB3#V##3Op z!;cARrF3IBE2!t?y}t6%2qzri90x^?B!EQ}go^S*Wme}lfRhZ-sO(&`Adxth&(@Zi zbhD`>Ufh+Ps5ac4SH!` zqbb-$nHAQYUMc8`T5wqA>18kZcKLAzss@IL$}gw6bYN;6{^g4AEA2Z&QTg$WPekfe zYkPw7p_h~}b4%KDXw815?#*K_4_8@vy*ed&l9ty20Cs<*1sy0UMjcd-O!|ESs|K@N z%5>LRhiS>#!GaHtw4&I@#$XwJL_V~BoHpf;-kH!kIX%jz8~=00dVumm&`iW zG16i+q-H%$IBD{_$(Jwx8M6X|tLsi#Y;^^+vEF-~nd{s6kuvm7wuqCl27Ekj;*@j= ze7MfLZ{WyiGZilV_j+VHB|3`r)|seArTAz9>5dPm%y>uFDSvHDNy=aT z14cn(%6H};3+`2>cZ`3jZV)Xd=|AEQ}fR9V(JR+VG9oJ}!t8*CA9^bxr zAZv$xYk>A0)_Kk>$m1niflUc*%Z4av?bI{U%yPzv7N?gV7tHmCKmD0}J&)p-GZ~t6 z(Ycl*Qk4g~u+XgA$h>DXBs-QvQnY_1kF zZFV1r#}m(Lf%D|em|7Rj1fB2q-X99%k+Y)3+t$q{g(%lOP;6JiY9`M0k|(dJ;bHRT z=hvDeLt5bD@Ma}$Jh=6bhX=dC*C=H>UXGkv1$%EU%`XfHfBJ?{)9Av|P_+~%0dcI^ zK>z*B4KJnv;JIXg!C!@@3-N{za{)S3=+wXG9)*(R?jnUEza3JOth{hW=aa0#hU4sU zTo5Ds_8yMwCC*aEh5V|j{z#q#!5=^N3*w3qM{}Q)mmFm4F2pYlp4EA(zHx|Po8q+A zxa6~>VDNIM0w5zV6W#PVpY!Lg{fp~26XP}2%8jT9exumt;H{%<( zIta~+CEH0JX8LKhUw$MUyn|3zj)@|ZFI)D>!L`bKb7!wk7w^g2m3PRfOMqql9qC2e zO@^*h|Q^~e}#R?iqDt?%Amrqkw-CxbHW@J%%>$bTFojb26LQH77&rO{eKs+ zqS=w^<@f|4SNmB`POB&2JqnlwQ$XJFT?EVvj1rDDaii z`O?wLyYT$ikV#s7>v;xzaJ@)K+?dS!7$xM<997-Kf$Kw43I#$;zo9MYZN;y*SOx$0 zmWT76+&4AR>oG?p6ch&4wr%%iG|@1(3{@-T+#=f+g+ z6dAeEjg6VL-dWY;`uYNBnvP66Mvy$Sgyc8XW zXAavKyDEP|U&NdJ9NMy#%7ZcikA~BhrAy~k2#mrqEBO9;Nbw|s;ZevyVJz%O7a;u? zHTg`5&qu^%eKA&;fYu&*26pTD$EUAV$Li~%cS`&0f~iqd?DY3!-(_<(Fk&!|W4gvA-Otuh5pI_^V(Bi2H4fPZT+raTma5km9aRf*+y6fiR zmP>^#RShb_`e@ux#BW)#iQi~=T1o10EqK>rCqfIIC&>TXrryCLB~|OI6LSa;QO*^i zW$b)`hle=?iLJNQ8cY&RWW>99dE_ z0N4vve$t6N=+ugN{sH0vHW5bQA23U)@F8tk-@-WDP*-uE$pdE|`}0MZdwZ;iq#(W} zYx6Sb@+v(~(Z2(icWr1}Nv;3K_5V-8O-nuo03(BQgbh3_?3)|P3-PQzTfsx%L1Ghe zR4c^22eme>2q8to+~9C@7-$VlMpZL*vc#b~r&j0#c?1b0Fipl~k^6rD5p)9*0Or!_ zk>&q;?l>#3=6Xo1l%Ykp-`8b{3K!=_()~sG9nJ97KSOu>pU`(LQMx0*pQ7Fqw`%`u zR0^Q0K?qT`fU~~8iOi(^4@l7~3RAy+4xZ%C=UFT=_&?5U$@<^ai*X7mkeC7Oh|y4Z zY#@s^SX+;T4O$8*a`Av<+xb0xRWvXN zREbbRwB{MmipOC9+$thTFkWM3?xeb*^tLoc22wh$G;HV5WzD28wwI94LmucywF{C`BxBG{hq z7Ad+&L3lS2t`z_R^skVnSh zw5O+8tU$IbBf)}Ie78emqkoYoB-`O#>E9-YGol!n6%EGU z$fxSdfIvRX47ik#`UJ4C{;e&+;lJi0aGgZLfZv}EOQEu#;f2xwgfyuKZ&E+m+<4#E z#sOhZIFwa8KoKeUMOv>mXlMRvoM8LWP<#^wBTR%k^&GtJMd;3vhpoR$Qa_{|H0Vf`+~m#!MlF(tpj5ytY5!|y9}UD z8W1r^RZ)gjHA&i-**Sn{b8YGg#~#_TP&q=q_Ag^?0-m(zchk3?lKZ@Oo5KNafnmsQ z2gK1uXBYSxAmCMUCRCW_1BKzasYmwVXMVtIV5%rsp&!Ux(pY4Z#WKYP!Oh0&!wm3~ zBlvkp4a2%e+rJV6pj*s;z$)j%y*I>ldXsJZfy}-kU;;qO`QFH|qUnY)`-g@(kQmvE zcX6K}X{?&73A>O-&<$IC27Du3D9#RM|gv6Ypdo&tHc@ zrA}9IWEL;qPS1HjigtZ`@2v>ssusB=VyhSd2?afqV}YdDyQ%x6FG>0Sm!5J0w5$OUH99bhon zO~WO=@4d9ALHoUtcrrl}d*|PlKH4;taxqFVCh^@DjsrnGX-vSUGB_FHLG*>oZN*%B z4H-2+w}^0nm87rSkunG?F9Zsh6&psVdW=7}A2&R~}cEo)QOUjA5M4z_&fD;-TELQZvtqfd~V? zpH+jG{HWD<*xO}!?-UXAwYcKUkI-u#X8~121zqr1QN=r!S4?H#wa>6fXzujfZKs>@ z@?_JiG|u?m&3KZ5(?MZ?(prwQz|6GYRN%#7k~GKwI_gDnYo3i&h$Mw8? zHpx&ijxPpJ4&)1KlKxXTOT|}D>ja+y}4M+ape!yo;Ooy{fR-(u`Ebmc*7xv07E;row&59j|1E_oF=16=$f z!mEuTij^1M5sr^vshhlz(nC+T55G*>%-2D?qV<<`Yg&R~fs-E}76<;FlF9G>hTh4$HZ=zx;akYWA&A~lb>YoWpSjszR3Y0YtdFiS$7T%E);uIA#Oqae@UtMi3fp+tiei481dS)T2u4Op zcREYArr$t)76sCN^^w#y@Vm$S&$GQ@Bw>SS*-M7f0q&wr#>Lc2{^kwMrOI*7a_6+2 zKfH(F7*|@ov-J@BMK0PUPU;fjExU3%E1j1z#9$4N3gSDKEg$HLKLk!T#4^{PG~;tZ)hTMtlxfY^4KpwP`!3P2j`^92Dx!9aw%2IB>t}--P_~ zwIZJ3z6{mT*LBhi5ijJ6oSK5V>sgUN6emWm%vdNS&KoEr&h>m}%D7^F&rxv2g@;C2 zMjW3g9mZfC+Od@1z%M%Q38nj< zE96hMCf!O{*#Eqf0s@C8b(9y_sZ7aZ{xJ(NRH+t+{q~#gGm)j{S41Pn2VfIkXsz&k z$##MKD%c=OdjykxmNnOOWeD&!?B<1P-ju>O96>rWV$KJhxb0Zq+K6Rjv{iOpCab#E z@vO^NOd*gP(Y&0am2o$kit4e~Q{MN4r>daw%S9KP_p=SvZXZ6i>a$`$ZCEX!5b0pP zJm`J*qi%{oT12O}2H^~mWGSZ`L^W;{c{g9~^2#Tsw*{WQ1}ThXsiTWQ$1=*wTNAi zvHz$6vJh~2cBPEuVx~XysObGt#$PS=*WcDIeeyX>AauhwKgmUF1!SZ;HOJ$$^1dej z)bJ|&uCbA^^MMxxoi-U1EVbo=4b^K9PpNT8XBBw3E?UC>F_oTErg^pZiunR#hpPr- z*UDZNa3zLBKB@kKwYTq`ImRo&{!x|!^tH?>=t?solD!cN5SE+PrHFa-siNomAvSmp zPomCV=y&?j2!wOXtNyLVN~(enfU>&mi14_X>{ZM6VQ&B28F2VID;7LZ`J6cICBx(Z zIy+=GPE9x=@(F%C49wI}e_RrIeaKUCnmX>R5z@adCMn3sgQVMyc8v%&`n?!bq?@!~ zKtLt=r3?YF%b7T6M5MzR1mIk_tPL@GJy1o$ z`7h0$zW3r1mm=tCeFL<99>ob5uum1(qy#%kSYx+Q4${<72CC5lllaz#KGF%c$vMSL zFN+P+%%p0r?2NE==<<-mxr8rWSib@OMp?fs=>zJ!N`@^AeKqy4Wh~>jI=D=ef3UkN z-EQ8&N=fDYZkSIll?y`|f>Ntl?`QCs{eKoOu2hzs=E1%`&dOn!1I>@Uls+m*AdGYx zerNU5O-ZJi!Tu0d-=X@)1MU-k5qtwi<)_0=rq1)!KzYRdi-A`*NayC=gA%If8OkZt zJQEg=K&~YmpW@}ttCOi00BLa1_(`W|7b9XEC#+ zeRu*{hfC;;r}+af>YQr@NCqTg7y*M{+6!zAXP0t zlMZ2h(?lTG+9*da68YnucjMowrqWaHyGY$J<^ZaBl$?rTzB|HkYmaQ>_1nl z7(a8lP&^eUEt#!VqX~-d;AURqd@Px%d|-C^grhhD9wZVv2x1e~(oS5!^Mx_mWrEik zs~>rQxB)rRsNK;{A|dFMg_I_W%o5#q5n$k5<6N2a>3~68PbnOwT6sRGwn^nQZO$CJ!>-*r+ZW zu!B@y*vrO9%LFZ0ULoyAfma3^u5zL_X7Mm)2lD~Sc=FX~D6g-F17JS6`Cq0doV+k; znhyFeY7?Y4M=7VZD7{+xYHd$?GIch4T$K+)8)?LSyI-KE3kjtNHL3HAhlQN)ZS9i2 z^=kbaM34F#P;66d;?!t+X|pDe=LG8OV)%&^~@;z2E8}9zya?NG@Q+Uh#{=GO`MfTxBoXn1NH2&x8mfMhR{^xb6 ze76}?`eO3y&a3H*N#v`EP%~de#hJ#&P9B_lhk?ob9Hu{?eO-D+i5!nV8NL7f9O5lG zNi*<)T@#;TL1{z=rA)H7uC-}$)p}oDrG~%g*-_OSiE^H%RQRV7;NS;+_*>BkQxNBb zAFO4cI*-m$Vmtb-`Ut`z<-(09!7kXp(h@SRkTD8tI%g9&T|lAZuVrj4C`^p#1g5hL zRN9p?-Bovcjdq9Ed$a>33;eso9P~3xItn(FS%1}k#fcd7F~^0h2kAYqHX=vT{S+Gf z#=9bP**>>YSkhd%^ai>+Dsj>vcCJNx&*Wn;I@MWu|1o={MJuBjtZW8 zw_qO62_Q-LBh;t1OP~mCCM8=zc`LZinOxKISDiOUm(Ymi?58W?&_LqOmO~SrsK~$Tm5Yo%D-k#5Znana zR44RPmknb4)txnmrDZ)p!B@iX&Ul_Jj+Fe)uhY0t#NO@w(4_0nNk*5+nMdn%QeOl&q>;8-Y8njbHQhDDRYW-}+i3Kn~2WR`YC z)vmb{@?EDTo*P=nQgkKBr2*2aE4%0)aBifpSqZ{R=uTSpSMK?V)NSz0MibUctE!UR z!|!KepdWcRO}XodyRHFA9P+3BIYdyb>uVkBqq0Lc8q#!i<2$xO*ZFP#&9L@=Z2$SI z${^F1mTrK~DT-6(aml>$-=*|?%Y_4Ikba$;ayI8%$~73hCvB^mTw8&4sAAUl~4-g>UMFttQy;Mf)AYXjkZzcna2f@}3SPiI_=6*|<8OC9w@ z9Qj30zDe`+MdNS29|p1>=9-|p8fP$7$el0A&w?BKSNTe|Yc!OP?>6nE&UZ9c8W1+T z`l#;a`<&!0IU3{UmCvV?t~LA-=NwjKv6VBB5HsX3cpw~`1$#g*}!tf}Q7AB$G?z71LX9yg$UV@SsD zj|}lJ&G~IkRJaI>Xz@Rbb0p5+*eRD+GBDi}U8EiOXE*1l+(J&albxM6ToQo1)4HTS z12D*?Pj&@aF}>csqch;6^vD0D{8Mxkb1A9R>qlikX#)HsBaNf2$I5^H0RH3qZQD1- zoq_>iUUdfTkgjYyl3&Qc3Z+j~)lHnNPo5g?eoZbczU1E>+|3WP*lHzVnd#K5Z>+Bq zfPAc2)$1|zb*!WO*5cGR<7e`lqFgfT$&=IT9LdIhhhA+jnX$pC+!Z%EEJaR%Ck?!H zvz7;gX#MdsvbMNb6tZrDR&(gD7(e%A^f0t3X|=x|+mcOHYEgUiNxaCreNjsgongUCB4cx09&$@Ne8r3X?drQO4c?214>$p z8MX_fTFP^@4tfS->)8=Ua&Pjpf|$tTm5Qu{bA_vzSmT`kRJD@0r9u|`SaUslU&wI) zZr`5qC@5=KG-!GG;R8D9FR_$XEmdbN_q)rRx{R*h3;?qzgJr3n7H3#)<&8+Umh!x0 zd~(Svm(0x41N>*w&U35Z4Vng@79EJ7ln2$PG)%im%0AqK!vCA5p+`d z3?Dj(I(@{Js))zjBMQY2yxJ54@SzFv1fYEA&39NJ#|FNOjvX5#;cN)boa&A$6OTeDpUvbK6DTjzmp4)k)ioF#RlMEV z^8V5vGr1ZmDNJ2FHqbl%=mEQU5kGsd?4JXh&HoEyuz%cPmPqrvRcN=>xx24b^YUW= z5d9IY80_O)7^+&FdW$Djc%J<)9Tf0S`9@6qhAi;5@2SFHp7L}*3!lvu!vIGo4MS2} zE{&u1ImeEs;Z~b$I7&=`D`T2 z&(>n9p4#*~$JzDlTyR5FWnn+*udZVvRi8Os-1V8%KF{#>X?B6BnHuKjdF{xa0%%KzZ!I_Q0|dFhiWBtacF4k{?YO@Gd-WGA0ywzg);kACpZ#kw zXnW%Sni-G=TWQdfld;Kd8&5YlZr!vtCOj|qchtjt5X6a?XehFI_P)t zR%Rh!Y}DKk7k=PgxT}qb*_rNpHC0J339d{lCAN$xiItS-Z2bm5#euEGyuVuY`^l+|XTBY1t)d~JQ z)oE7wATEEWdxg&CWYO4{1gSXyh${5Zhx{!#5uVG8Dxj8scw=US)tCZTT9EJvC&`RWob#SnP=Fw`| z3yYQ;fm|1AA>ISa8n&6$k5{$UWqpPBpyWJ;yQ2Gb` z-*9t16KI2uOb&M#6cHNf$`+Z48P-wuB%hyb%VI9m=(6iH&`3z%9A(wB)_X1HyJPm& zJG8!BuXS+l#ndyjulGEypD#ja_j-niID&8cMTn`)1?@E0~dt=*_ z(ar(9prx-V0cd-8{x!8W9Evt?X{BE1vun;~PuU7|3m_H?5$hd z&X-A(`Z;a8Sb<-~5J(eDjKSXH0JM20BdG~ibW*Vq904N6zEV40;^}^x7uRu1N%DB; zp?OxGS{ng%7W-ZQ7>5~NdxiG2N}F`Q86F1bR&?i1Dhv6QR9hrvxB^PoI}E^NRnh{t z70+POXY*IxM|bxh)fyt9`PqD+XDDJy3}V{?D0H)IK5eK0=~E97R1Cg37$47(no9Re z`i4flxOJK3D3DY6-droD3Cm9Jy~4D*B5rxHZmBe__0<_&XrYPz`8T4x*>F;B(qcNl zs%-QUhAFnoQE%FBNQ5*4jWu08U|7mlm{(bQz3{#2uqZB=lr~&?D%GWIujmRt<%CdYF4|_6)s_Z?Rv9dF02NN!#d^w0qlP?=K) z7|PYy~{XD4`9b{dC#b`lKsh6;G z^716o{di?R8URumc}Gd@!SDjlN{zk3R>Jn%9T|gLt-NCeu7(5dfKSiVb}J4?`X=8n zOBKCbqEfkb7Y4Lr2XCJUeXCy86CC9=>s_R_i>mi5ICfgl*o)ae9dUjj z-L6zFJbHVpd034}1w0@Q_i){K_|pUya0J)aUCVxfDsg0=O#P$>Z(?0k`b4tmt@t%B z&ZR!kywp3Za2w(@!y3))IleIN_$-wSwM?czp#^oe5nP)Jd1)QLoOykN4|x-qpcjQL|02$B z0#%7n0igkbY#-A$7MgsVWFwQT4}Op5aSYBilAJn&8anV4G2V_pPKaO5!o^@6G66r~ zu6n+f-;qNWw7kKQUgzEgQ8jwHET)eIRRd#e^`WGYq#RQyr$@**&8 zy&d10qP@}rW!5qSW=_uWlbSC#9p|^VkbTkg==-p#N*D?8@VrX5(}6$WZ+j#%N<7&M zimY0-=dZ(hrfHgCs^42kbY=v!o=>j8(7|qfF`f*?PA7scTwls@l#5CpeyW@bQT_3@ z72U><=F{WNYdZ>S1{~oiD*AYr@3nrM1y>o%zKbunC(-txI!2nb$!a44`g`zOm)Z)p ziJseF0GCGD^@6X9{IPjvj-EmkSyO*nDO-3xcEHr);No2LdYgRH<9ow_vzk@CEqXFp za}w;IH=}4_VmW5J)zj|?Uw1;QERGAoylX?XC>fUn4tq)_P(&<@Eg}z*CN`;%j@`#LBajOZ?MH=r=h0=yMi& z>jTds`c-3VO+uG{RPKaXyOeKxgPz}8#CtXm%j(d_`Khf>d6jRn$o|B`r6?$`uFg>a zCJ;6q##Ye3$Ahp0&*2`It(mjgjE;x^WQD?@>onT*bq`af&IE@cerzO_u8Bn*BCBE) zKi#~m`x>$x&6#96z2cPaOae{MyuJOPi5dk`vRyCx$B!Gl*P3K!(AG2okxe%kP!1fp zt_cUNXs4*moLm7w&R+5lQZl{80(D#f8%mnS6@PI*v*y~oJUpqKEQEtBno4zy^_Bl@ zS(bnrjaksq(Lbv=1G^*-$1e|Z%TEFIVq%o1OM{+iIVia$H-{VQC2B0fm9q-e{|_XTm^|Bul(Lqazi%Esao@9MIS9Y8vrr@n?ATw~5~_CMqggHZw3N1(6Pc4GJn zkqN21+(e%kX}(7A7i|Zk%d|wO>u|eB$?;`YSI3NEz9~r`Nb!yVC9;@xaWnfXWeo+_>+?bw)_S zInd?x+n$hi2)9JlPkmoWW=Py?7CV4ZFrBW3DF0%if9J`P%*pP^yb$Q7?eQ^FKl|fX z^jB?+g$2k#-C1V&#-;be-&~1}$n@p-Pv}n32~Ib$;1H&6B#0PucS_r>aukvp9u|}+`9EUdq`*ZQt^AmS8;TK zXK~RJF^93d#wqseFBAkskgHDJR>is1{jpXR#rD_Ha+~Yiv21&uGY2j?HiVVey(>p!l&-Z?&JaD89f-Ug{Zsmy z1xFOaK==pkk;xRxmi_#TM1K#I^@})hU$yecTD+%755oC610g%|at!A+Dk= zD|oK8PTp3PQ4MT>hgZ&8801GTkFTDNe?<_8>HPSLWmxZt@Tmq(Zc7Jm+ZG1t8MYv` zwLzq>3o+o=pm?XuErI*2i0=~9-XS_S4uP5L&HOp*-La_M>XhU*n=X^xt}kx>yn!Oi zhBaU1Op?$ER=N`1I;l@~M*L;C{lMF9fwWw;I`EgI8y3dO-QEN=>&x7;TW~|0&ak?# zV9mJ2loIv)<@?ufJHKMySc|W#%bmQ;Gb`q|!|;B~F?K+;Z2Waj4@-dR;kUEjLqf5F zGBeUvl1AYlM*I8k&etC_t0sX-=UX{s^E?nXMZE|SlpWhkHZWhscGxh#GSz!-++}Rp z3kOhK7onpWR_sReD%u#NLGd}M(kNSAoEvA5gT;BjAN8T~UVWE*?p%6&G_Dgo-nXzK zYs`zgjOX80m0tcaJf_|T7s2v8}OKqV_Iq2sQC`p zeQ?z?Y`f?;uDv&Qcm-EE5s};@?Qx21L;>&VztFu;fs&KkJ3W!cbzS1;!ua(rLRCG^ z{@e>4Es2EU5A1hpDL^S*<>sh(w}rU@h^n~sKmf~xA3keX%BvxP(6whewLB_vKA zj7Zm;a(;&G5@%Q=5BuHWz$zE_A=p{zSAUG`@>4ELu!C&V*xTK)0MBQtx)q`I%9Bs{ z;V?5h$Jop1}zEtk3aM{6sab9$DGV>#O4gv6zr% zmG*bc^?I2S|FI-JRn!Ul9EU%2=lgCk<0lT&q4fulDTZ;7w&T%{Zd(>m{4 zZ0Azj!Vi+n^}rDj@kNthz1lfAkH*!M`v8#q7X#@EJx9)>SI0XIWXxk>2>DF9GO}_i z4kH=-vQZ;5xE?%tf~JGMj~($Zx~IDA#yR3lUKMte{I#cq(DCGInYE}gw*Fmyrv9~5 zp`3PO;ZM&l&zr+;X~Z3QK;Py9BgEIJXpOufU!QLFs4)KhH0nxczVrT-srnI;Orq~N zw-;}T-sO#PYkuk$1L{To{5fVO=JWJr^!GTL!3&9$O{u)4z#3PbfMmpC_`Y!PG0MZN zw`c$B&kxMs&N3lq2c~G=l!DVI#7w$nwRBAhCxLmOTi5cLH(!5?=*EVG%GS3@xxEbb z-5@U+;*_`BJ);yzYKH!irk?Bunv1F0OX%#O#^0VT9X_gL>(4kYM{INdIA&$bYxGz3 zkCXS)kJoRG?eeMjn^3i@awt#ZgDqG)oD7JR)BSu*;NCq!*82WVhhml^YU zUi@L%jyC3iwdk^}V+ofH02p>vQaBeg6n0AQCnr^3gPXryn65U)eOi%)o-|Tml;*DwpldZJ9}Y~a z^L6;+A|@sm$-BRCQhctIIPG2Tj1~mc%Xtyd%J!6hzKm<*VQ4N&b$L}~&y{E_5Yg}HY~SnV$t!J z$%Q}@DJ@#)*?O^R*-~Z;SJ>}Yhv2^Jr925adAeBVi7sJ#WXqEhQJV&3XMalb9Mm!L zM4?C!l#05lnKxVbdv(Rmm-q2#=5g@E$z1w9hToe&tNeCnE`ZuWynrz5b&IY-CoW)5 zzFAxOVR=@QK%UjFu$XNndDS=2E~%N8b?l6r`$z3LrXvSnC<^0V9D1Gjmbg*7ax4~V zH2h=KgT=T#W2qe`&)~tTOKx6A40MYV%zk=I6vWkeF-+scHyA(IS)HO&f4`s4_xIt zH#%vf_d2v%9cS3VtEIpP;IUtH~&dHJs1 zvZ=V39I*~042!ZfGJs=?s{X9-VO47i^9)zZE+;JQ$(o31fez=7|9OwQX}hP-1^a1S zPsOU^c7QQ%9^yr=^B%S`dXzI+R+2lKl_c8Hd96Ra#N6M&ebwm2PmMoah^)Nx<+AF%`-X-=eQGWtjLc__`ka; zp7iqZ9H+)i@%YlXyy<%Agf60!$8o3WvC0qs?L|5n69Yke*9H=cE4YHZC1h&^Q1(H} zk41By?@4q@HItc6ZKhD$GcX*QI?sXvPg{o+1FsTuGHhB{+CNm?ik!P${fuBo2|tn z)@M#XeD6W$dF?9^`HbH_=XC$GT6`>UnWxF`V+zYHEDSeA_sqJ!?|19s>xs6&ev$bt zjo3}b#r>DH8EW3ne)n)y(jmq-XO4syzB{@yeEYi9H)nQ-F{^wAo-w%m z@XZDxDa8kE=Q17%oDbWwrhD(2o4eK}S}XqVN|P%Q|9FXU`4;tO#S(`Tfwjn;y!xw& z>TYpmU!^|8)Unu{ul%BPuBrAa$5Hc1a}9F734F@@Dm%r-HGF;LUD1a5V#_8^I5G1A z=bP0}wD&!H!?U4XN{&xq$t-ETo1GpHtZ!G|JYY62^J=StZI$V^#{!}@(_b$&On$zh zP~?pZOZCp==c&#f!Se%lraHd7xO$^3J8)bqgXzbPi763Rr$5zfn5Us@_2Y@fn%)`V z@y5@?Tpk~unx+~bClle=HaB(8r4um)8C=iTyf3k^FX?7oZzN;A_gl!%8z(<}cl6@8 z$aU&^c>4qGvPv22O6$wVVwP;IspI&7YclsDoTdXO zn0EZzp~tu1Y5M)TFE=Wu>h&k57yjM5YQ`5W+c|IB_gHzD{4d}3`PD*=lvAAfjc-2Q zczaR1XhmSmT#gDmfj4`+Kt}K|eQJ{usR-BB$guBPeekqXWB1KC+aE_ysGR>Eec$Rv z!>1yJ8~^@!9oRTI{H6K+)9Wv6{>hUOKX-L0Fer9jzp~>|;uEghYn)!cirIMj^MB^@ zrDeag8WRNi0dVN-jzTQVd20Mh3bDCc4H3Aw~vPCT3Q~CfWuDRt5$W z*st)QXvob^$xN%nt>Ix*&IX_c4Ui25DQQ+7nL(+=PWh#IDVcfcPWcrKmPRIeW|kHe zrsn2)76#_p2HFO}T7sYp-GyO#!cvQhGxPHp0$kj54fRY^470+4(hQ!ielF{r5}E*U CP*iLH literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/StateSpace.rst b/docs/source/user/aerodyn-fvw/StateSpace.rst new file mode 100644 index 0000000000..7cdcc8d72b --- /dev/null +++ b/docs/source/user/aerodyn-fvw/StateSpace.rst @@ -0,0 +1,186 @@ +.. role:: raw-latex(raw) + :format: latex +.. + +State-Space Representation and Integration with OpenFAST +======================================================== + +State, Constraint, Input, and Output Variables +---------------------------------------------- + +The OLAF module has been integrated into the latest version of OpenFAST +via *AeroDyn15*, following the OpenFAST modularization +framework (:cite:`Jonkman13_1,Sprague15_1`). To follow the +OpenFAST framework, the vortex code is written as a module, and its +formulation comprises state, constraint, and output equations. The data +manipulated by the module include the following vectors: inputs, +:math:`\vec{u}`; states, :math:`\vec{x}`; constrained state, +:math:`\vec{z}`; outputs, :math:`\vec{y}`; and constant parameters, +:math:`\vec{p}`. The vectors are defined as follows: + +- Inputs, :math:`\vec{u}~-` a set of values supplied to the module + that, along with the states, are needed to calculate future states + and the system’s output. + +- Outputs, :math:`\vec{y}~-` a set of values calculated and returned + by the module that depend on the states, inputs, and/or parameters + through output equations. + +- States, :math:`\vec{x}~-` a set of internal values of the module + that are influenced by the inputs and used to calculate future state + values and the output. Continuous states are employed, meaning that + the states are differentiable in time and characterized by continuous + time-differential equations. + +- Constraint states, :math:`\vec{z}~-` algebraic variables that are + calculated using a nonlinear solver, based on values from the current + time step. + +- Parameters, :math:`\vec{p}~-` a set of internal system values that + are independent of the states and inputs. The parameters can be fully + defined at initialization and characterize the system’s state + equations and output equations. + +The parameters of the vortex code include: + +- Fluid characteristics: kinematic viscosity, :math:`\nu` + +- Airfoil characteristics: polar data: (:math:`C_l(\alpha)`, + :math:`C_d(\alpha)`, :math:`C_m(\alpha)`), and chord :math:`c` + +- Algorithmic methods and parameters for regularization, viscous + diffusion, discretization, wake geometry, acceleration, and so on. + +The inputs of the vortex code are: + +- Position, orientation, translational velocity, and rotational + velocity of the different nodes of the lifting lines + (:math:`\vec{r}_{ll}`, :math:`\Lambda_{ll}`, + :math:`\vec{\dot{r}}_{ll}`, and :math:`\vec{\omega}_{ll}`, + respectively), gathered into the vector, + :math:`\vec{x}_{\text{elast},ll}`, for conciseness. These quantities + are handled using the mesh-mapping functionality and data structure + of OpenFAST. + +- Undisturbed velocity field at requested locations (lifting-line + points, :math:`\vec{r}_{ll}`, and a set of locations requested by the + vortex code, :math:`\vec{r}_r`), written + :math:`\vec{V}_0=[\vec{V}_{0,ll}, \vec{V}_{0,r}]`. Based on the + parameters, this velocity field may contain the following influences: + freestream, shear, veer, turbulence, tower, and nacelle disturbance. + The locations where the velocity field is requested are typically the + location of the Lagrangian markers. + +The constraint states are: + +- The circulation intensity along the lifting lines, + :math:`\Gamma_{ll}`. + +The continuous states are: + +- The position of the Lagrangian markers, :math:`\vec{r}_m` + +- The vorticity associated with each vortex element, + :math:`\vec{\omega}_e`. For a projection of the vorticity onto vortex + segments, this corresponds to the circulation, + :math:`\vec{\Gamma}_e`, where for each segment, + :math:`\vec{\Gamma}_e= \Gamma_e \vec{dl}_e =\vec{\omega}_e dV_e`, + with :math:`\vec{dl}_e` and :math:`dV_e`, the vortex segment length + and its equivalent vortex volume. + +The outputs are [1]_: + +- The induced velocity at the lifting-line nodes, + :math:`\vec{v}_{i,ll}` + +- The locations where the undisturbed wind needs to be computed, + :math:`\vec{r}_{r}` (typically :math:`\vec{r_{r}}=\vec{r}_m`). + +State, Constraint, and Output Equations +--------------------------------------- + +An overview of the main states, constraints, and output equations is +given in this paragraph. More details are provided in +:numref:`sec:FVW`. The constraint equation is used to determine +the circulation distribution along the span of each lifting line. For +the van Garrel method, this circulation is a function of the angle of +attack along the blade and the airfoil coefficients. The angle of attack +at a given lifting-line node is a function of the undisturbed velocity, +:math:`\vec{v}_{0,ll}`, and the velocity induced by the vorticity, +:math:`\vec{v}_{i,ll}`, at that point. Part of the induced velocity is +caused by the vorticity being shed and trailed at the current time step, +which in turn is a function of the circulation distribution along the +lifting line. This constraint equation may be written as: + +.. math:: + \vec{Z} = \vec{0} = \vec{\Gamma}_{ll} - \vec{\Gamma}_p(\vec{\alpha}(\vec{x},\vec{u}),\vec{p}) + +where :math:`\vec{\Gamma}_p` is the function that returns the +circulation along the blade span, according to one of the method +presented in :numref:`sec:circ`. The state equation +specifies the time evolution of the vorticity and the convection of the +Lagrangian markers: + +.. math:: + \begin{aligned} + \frac{d \vec{\omega}_e}{dt} &= \left[(\vec{\omega}\cdot\nabla)\vec{v} + \nu\nabla^2 \vec{\omega} \right]_e + \end{aligned} + +.. math:: + \begin{aligned} + \frac{d \vec{r}_m}{dt} &= \vec{V}(\vec{r}_m) + =\vec{V}_0(\vec{r}_m) + \vec{v}_\omega(\vec{r}_m) + =\vec{V}_0(\vec{r}_m) + \vec{V}_\omega(\vec{r}_m, \vec{r}_m, \vec{\omega}) + \end{aligned} + :label: eq:Convection + +where :math:`\vec{v}_\omega` is the velocity induced by the vorticity in +the domain; :math:`\vec{V}_\omega(\vec{r},\vec{r}_m,\vec{\omega})` is +the function that computes this induced velocity at a given point, +:math:`\vec{r}`, based on the location of the Lagrangian markers and the +intensity of the vortex elements; and the subscript, :math:`e`, +indicates that a quantity is applied to an element. The vorticity, +:math:`\vec{\omega}`, is recovered from the vorticity of the vortex +elements by means of discrete convolutions. For vortex-segment +simulations, the viscous-splitting algorithm is used, and the convection +step (Eq. :eq:`eq:Convection`) is the main state +equation being solved for. The vorticity stretching is automatically +accounted for, and the diffusion is performed *a posteriori*. The +velocity function, :math:`\vec{V}_\omega`, uses the Biot-Savart law. The +output equation is: + +.. math:: + \begin{aligned} + \vec{y}_1&=\vec{v}_{i,ll} = \vec{V}_\omega ( \vec{r}_{ll}, \vec{r}_m, \vec{\omega}) \\ + \vec{y}_2&=\vec{r}_{r} + \end{aligned} + +Integration with AeroDyn15 +-------------------------- + +The vortex code has been integrated as a submodule of the aerodynamic module of +OpenFAST, *AeroDyn15*. The data workflow between the different modules and +submodules of OpenFAST is illustrated in :numref:`FAST-FVW`. This integration +required a restructuring of the *AeroDyn15* module to isolate the parts of the +code related to tower shadow modeling, induction computation, +lifting-line-forces computations, and dynamic stall. The dynamic stall model +will be adapted when used in conjunction with the vortex code to ensure the +effect of shed vorticity is not accounted for twice. The interface between +*AeroDyn15* and the inflow module, *InflowWind*, was accommodated to include the +additionally requested points by the vortex code. + +.. _FAST-FVW: + +.. figure:: Schematics/VortexCodeWorkFlow.png + :alt: OpenFAST-FVW code integration workflow + :width: 100% + :align: center + + OpenFAST-FVW code integration workflow + + + + +.. [1] + The loads on the lifting line are not an output of the vortex code; + their calculation is handled by a separate submodule of *AeroDyn*. diff --git a/docs/source/user/aerodyn-fvw/bibliography.bib b/docs/source/user/aerodyn-fvw/bibliography.bib index 30b25160a8..90f791db4b 100644 --- a/docs/source/user/aerodyn-fvw/bibliography.bib +++ b/docs/source/user/aerodyn-fvw/bibliography.bib @@ -429,3 +429,10 @@ @TECHREPORT{Garrel03_1 year = {2003}, number = {ECN-C--03-079} } + +@TECHREPORT{Kerwin:lecturenotes, + author = {J. Kerwin}, + title = {Lecture Notes Hydrofoil and propellers}, + institution = {M.I.T.}, + year = {2000} +} diff --git a/docs/source/user/aerodyn-fvw/index.rst b/docs/source/user/aerodyn-fvw/index.rst index e6bde1a3aa..9c88e8973b 100644 --- a/docs/source/user/aerodyn-fvw/index.rst +++ b/docs/source/user/aerodyn-fvw/index.rst @@ -24,20 +24,10 @@ OLAF User's Guide and Theory Manual (Free Vortex Wake) InputFiles.rst OutputFiles.rst OLAFTheory.rst + StateSpace.rst FutureWork.rst zrefs.rst AppendixA.rst AppendixB.rst AppendixC.rst - -.. Acknowledgements.rst - Introduction.rst - RunningOLAF.rst - InputFiles.rst - OutputFiles.rst - OLAFTheory.rst - FutureWork.rst - AppendixA.rst - AppendixB.rst - AppendixC.rst From 1da36b11eb1013002a2befaa1fa037158c39a0d4 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 1 May 2020 10:58:43 -0600 Subject: [PATCH 148/190] FVW docs: minor updates --- docs/source/user/aerodyn-fvw/Acronyms.rst | 5 +- .../ExampleFiles/ExampleFile--OLAF.txt | 56 +++++++++---------- docs/source/user/aerodyn-fvw/InputFiles.rst | 13 ++++- docs/source/user/aerodyn-fvw/OLAFTheory.rst | 2 +- docs/source/user/aerodyn-fvw/index.rst | 4 +- reg_tests/r-test | 2 +- 6 files changed, 43 insertions(+), 39 deletions(-) diff --git a/docs/source/user/aerodyn-fvw/Acronyms.rst b/docs/source/user/aerodyn-fvw/Acronyms.rst index d3a30a6c6c..fb0d492fa6 100644 --- a/docs/source/user/aerodyn-fvw/Acronyms.rst +++ b/docs/source/user/aerodyn-fvw/Acronyms.rst @@ -3,9 +3,6 @@ List of Symbols =============== -+-----------------------------+---------------------------------------+ -| :math:`a_1` | numerical constant | -| | :math:`=2\times10^{-4}` | +-----------------------------+---------------------------------------+ | BEM | blade-element momentum | +-----------------------------+---------------------------------------+ @@ -13,7 +10,7 @@ List of Symbols +-----------------------------+---------------------------------------+ | DOE | U.S. Department of Energy | +-----------------------------+---------------------------------------+ -| :math:`F_c` | core radius factor | +| :math:`F_v` | core radius factor | +-----------------------------+---------------------------------------+ | :math:`t` | time | +-----------------------------+---------------------------------------+ diff --git a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt index 1a6ab698eb..99fcd21b04 100644 --- a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt +++ b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt @@ -1,42 +1,42 @@ --------------------------- FREE WAKE INPUT FILE ---------------------------------------------- Free wake input file for the BAR turbine --------------------------- GENERAL OPTIONS --------------------------------------------------- -5 IntMethod Integration method {4: 2nd order Predictor/Corrector, 5: Forward Euler 1st order, default: 5} (switch) -0.2 DTfvw Time interval for wake propagation. {default: dtaero} (s) -5 FreeWakeStart Time when wake is free. (-) value = always free. {default: 0.0} (s) -2.0 FullCircStart Time at which full circulation is reached. {default: 0.0} (s) +5 IntMethod Integration method {4: 2nd order Predictor/Corrector, 5: Forward Euler 1st order, default: 5} (switch) +0.2 DTfvw Time interval for wake propagation. {default: dtaero} (s) +5 FreeWakeStart Time when wake is free. (-) value = always free. {default: 0.0} (s) +2.0 FullCircStart Time at which full circulation is reached. {default: 0.0} (s) --------------------------- CIRCULATION SPECIFICATIONS ---------------------------------------- -1 CircSolvingMethod Circulation solving method {1: Cl-Based, 2: No-Flow Through, 3: Prescribed, default: 1 }(switch) -0.01 CircSolvConvCrit Convergence criteria {default: 0.001} [only if CircSolvingMethod=1] (-) -0.1 CircSolvRelaxation Relaxation factor {default: 0.1} [only if CircSolvingMethod=1] (-) -30 CircSolvMaxIter Maximum number of iterations for circulation solving {default: 30} (-) - "NA" PrescribedCircFile File containing prescribed circulation [only if CircSolvingMethod=3] (quoted string) +1 CircSolvingMethod Circulation solving method {1: Cl-Based, 2: No-Flow Through, 3: Prescribed, default: 1 }(switch) +0.01 CircSolvConvCrit Convergence criteria {default: 0.001} [only if CircSolvingMethod=1] (-) +0.1 CircSolvRelaxation Relaxation factor {default: 0.1} [only if CircSolvingMethod=1] (-) +30 CircSolvMaxIter Maximum number of iterations for circulation solving {default: 30} (-) + "NA" PrescribedCircFile File containing prescribed circulation [only if CircSolvingMethod=3] (quoted string) =============================================================================================== --------------------------- WAKE OPTIONS ------------------------------------------------------ ------------------- WAKE EXTENT AND DISCRETIZATION -------------------------------------------- -50 nNWPanel Number of near-wake panels (-) -7 WakeLength Total wake distance (D) -5 FreeWakeLength Wake length that is free (D) -False FWShedVorticity Include shed vorticity in the far wake {default: 0.1} +50 nNWPanel Number of near-wake panels [integer] (-) +400 WakeLength Total wake distance [integer] (number of time steps) +default FreeWakeLength Wake length that is free [integer] (number of time steps) {default: WakeLength} +False FWShedVorticity Include shed vorticity in the far wake {default: false} ------------------- WAKE REGULARIZATIONS AND DIFFUSION ----------------------------------------- -0 DiffusionMethod Diffusion method to account for viscous effects {0: None, 1: Core Spreading, "default": 0} -0 RegDeterMethod Method to determine the regularization parameters {0: Manual, 1: Optimized, default: 0 } -2 RegFunction Viscous diffusion function {0: None, 1: Rankine, 2: LambOseen, 3: Vatistas, 4: Denominator, "default": 3} (switch) -0 WakeRegMethod Wake regularization method {1: Constant, 2: Stretching, 3: Age, default: 1} (switch) -2.0 WakeRegFactor Wake regularization factor (m) -2.0 WingRegFactor Wing regularization factor (m) -100 CoreSpreadEddyVisc Eddy viscosity in core spreading methods, typical values 1-1000 +0 DiffusionMethod Diffusion method to account for viscous effects {0: None, 1: Core Spreading, "default": 0} +0 RegDeterMethod Method to determine the regularization parameters {0: Manual, 1: Optimized, default: 0 } +2 RegFunction Viscous diffusion function {0: None, 1: Rankine, 2: LambOseen, 3: Vatistas, 4: Denominator, "default": 3} (switch) +0 WakeRegMethod Wake regularization method {1: Constant, 2: Stretching, 3: Age, default: 1} (switch) +2.0 WakeRegFactor Wake regularization factor (m) +2.0 WingRegFactor Wing regularization factor (m) +100 CoreSpreadEddyVisc Eddy viscosity in core spreading methods, typical values 1-1000 ------------------- WAKE TREATMENT OPTIONS --------------------------------------------------- -False TwrShadowOnWake Include tower flow disturbance effects on wake convection {default:false} [only if TwrPotent or TwrShadow] -0 ShearModel Shear Model {0: No treatment, 1: Mirrored vorticity, default: 0} +False TwrShadowOnWake Include tower flow disturbance effects on wake convection {default:false} [only if TwrPotent or TwrShadow] +0 ShearModel Shear Model {0: No treatment, 1: Mirrored vorticity, default: 0} ------------------- SPEEDUP OPTIONS ----------------------------------------------------------- -2 VelocityMethod Method to determine the velocity {1:Biot-Savart Segment, 2:Particle tree, default: 1} -1.5 TreeBranchFactor Branch radius fraction above which a multipole calculation is used {default: 2.0} [only if VelocityMethod=2] -1 PartPerSegment Number of particles per segment [only if VelocityMethod=2] +2 VelocityMethod Method to determine the velocity {1:Biot-Savart Segment, 2:Particle tree, default: 1} +1.5 TreeBranchFactor Branch radius fraction above which a multipole calculation is used {default: 2.0} [only if VelocityMethod=2] +1 PartPerSegment Number of particles per segment [only if VelocityMethod=2] =============================================================================================== --------------------------- OUTPUT OPTIONS --------------------------------------------------- -True WrVTk Outputs Visualization Toolkit (VTK) (independent of .fst option) {False: NoVTK, True: Write VTK at each time step} (flag) -1 nVTKBlades Number of blades for which VTK files are exported {0: No VTK per blade, n: VTK for blade 1 to n} (-) -2 VTKCoord Coordinate system used for VTK export. {1: Global, 2: Hub, "default": 1} +1 WrVTk Outputs Visualization Toolkit (VTK) (independent of .fst option) {0: NoVTK, 1: Write VTK at each time step} (flag) +1 nVTKBlades Number of blades for which VTK files are exported {0: No VTK per blade, n: VTK for blade 1 to n} (-) +2 VTKCoord Coordinate system used for VTK export. {1: Global, 2: Hub, "default": 1} default VTK_fps Frame rate for VTK output (frames per second) {"all" for all glue code timesteps, "default" for all FVW timesteps} [used only if WrVTK=1] ------------------------------------------------------------------------------------------------ diff --git a/docs/source/user/aerodyn-fvw/InputFiles.rst b/docs/source/user/aerodyn-fvw/InputFiles.rst index 412458e2a5..36bd827692 100644 --- a/docs/source/user/aerodyn-fvw/InputFiles.rst +++ b/docs/source/user/aerodyn-fvw/InputFiles.rst @@ -89,13 +89,13 @@ azimuthal span in degrees or a downstream distance in rotor diameter will be considered. **WakeLength** [D] specifies the length, in rotor diameters, of the far wake. -The default value is :math:`8`. +The default value is :math:`8`. [1]_ **FreeWakeLength** [D] specifies the length, in rotor diameters, for which the turbine wake is convected as “free." If *FreeWakeLength* is greater than *WakeLength*, then the entire wake is free. Otherwise, the Lagrangian markers located within the buffer zone delimited by *FreeWakeLength* and *WakeLength* -are convected with the average velocity. The default value is :math:`6`. +are convected with the average velocity. The default value is :math:`6`. [2]_ **FWShedVorticity** [flag] specifies whether shed vorticity is included in the far wake. The default value is *[False]*, specifying that the far wake consists @@ -138,7 +138,7 @@ blades. **CoreSpreadEddyVisc** [-] specifies the eddy viscosity parameter :math:`\delta` used for the core-spreading method (*DiffusionMethod* = *[1]*) or the regularization method with age (*WakeRegMethod* = *[3]*). The variable -:math:`\delta` is described in . The default value is :math:`100`. +:math:`\delta` is described in :numref:`sec:corerad`. The default value is :math:`100`. Output Options ~~~~~~~~~~~~~~ @@ -175,3 +175,10 @@ FVW method. **FVWFile** [string] specifies the OLAF module file, the path is relative to the AeroDyn file, unless an absolute path is provided. +.. [1] + At present, this variable is called nFWPanel and specified as the number of far + wake panels. This will be changed soon. + +.. [2] + At present, this variable is called nFWPanelFree and specified as the number of + free far wake panels. This will be changed soon. diff --git a/docs/source/user/aerodyn-fvw/OLAFTheory.rst b/docs/source/user/aerodyn-fvw/OLAFTheory.rst index 0ddb12877f..79575faf28 100644 --- a/docs/source/user/aerodyn-fvw/OLAFTheory.rst +++ b/docs/source/user/aerodyn-fvw/OLAFTheory.rst @@ -606,7 +606,7 @@ approach using the following steps: \end{aligned} #. Convergence is checked using the criterion :math:`k_\text{crit}` -(**CircSolvConvCrit**): + (**CircSolvConvCrit**): .. math:: \begin{aligned} diff --git a/docs/source/user/aerodyn-fvw/index.rst b/docs/source/user/aerodyn-fvw/index.rst index 9c88e8973b..3450d479dd 100644 --- a/docs/source/user/aerodyn-fvw/index.rst +++ b/docs/source/user/aerodyn-fvw/index.rst @@ -3,7 +3,7 @@ OLAF User's Guide and Theory Manual (Free Vortex Wake) .. only:: html - This document offers a quick reference guid for the free vortex wake module + This document offers a quick reference guide for the free vortex wake module named OLAF that is included in the AeroDyn module of OpenFAST. It is intended to be used by the general user in combination with other features of AeroDyn and other OpenFAST modules. The manual will be updated as new @@ -17,7 +17,6 @@ OLAF User's Guide and Theory Manual (Free Vortex Wake) .. toctree:: :maxdepth: 2 - Acknowledgments.rst Introduction.rst Acronyms.rst RunningOLAF.rst @@ -31,3 +30,4 @@ OLAF User's Guide and Theory Manual (Free Vortex Wake) AppendixB.rst AppendixC.rst +.. Acknowledgments.rst diff --git a/reg_tests/r-test b/reg_tests/r-test index 283707d5f3..dce5461b25 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 283707d5f3a9d8e23acd08c9521fba81d275530a +Subproject commit dce5461b258869ac21efd17fdd8d783b2ded7ebd From 61fb39d0d270625805dac9a0a9d4900185dc9f1d Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 1 May 2020 11:06:22 -0600 Subject: [PATCH 149/190] FVW: update reg-tests to OpenFAST/dev pointer --- reg_tests/r-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reg_tests/r-test b/reg_tests/r-test index dce5461b25..283707d5f3 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit dce5461b258869ac21efd17fdd8d783b2ded7ebd +Subproject commit 283707d5f3a9d8e23acd08c9521fba81d275530a From b796a9d8de1697915c64525bede3da1b6f644a73 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 4 May 2020 11:45:12 -0600 Subject: [PATCH 150/190] FVW docs: add info on the OpenMP option --- docs/source/install/index.rst | 1 + docs/source/user/aerodyn-fvw/RunningOLAF.rst | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/source/install/index.rst b/docs/source/install/index.rst index 9c42a658b7..306c88a220 100644 --- a/docs/source/install/index.rst +++ b/docs/source/install/index.rst @@ -228,6 +228,7 @@ The CMake options specific to OpenFAST and their default settings are: GENERATE_TYPES - Use the openfast-regsitry to autogenerate types modules ORCA_DLL_LOAD - Enable OrcaFlex library load (Default: OFF) USE_DLL_INTERFACE - Enable runtime loading of dynamic libraries (Default: ON) + OPENMP - Enable OpenMP parallelization in FVW (Default: OFF) Additional system-specific options may exist for a given system, but those should not impact the OpenFAST configuration. As mentioned above, the diff --git a/docs/source/user/aerodyn-fvw/RunningOLAF.rst b/docs/source/user/aerodyn-fvw/RunningOLAF.rst index 54bf62131a..4b91c6150c 100644 --- a/docs/source/user/aerodyn-fvw/RunningOLAF.rst +++ b/docs/source/user/aerodyn-fvw/RunningOLAF.rst @@ -7,3 +7,6 @@ As OLAF is a module of OpenFAST, the process of downloading, compiling, and running OLAF is the same as that for OpenFAST. Such instructions are available in the :ref:`installation` documentation. +.. note:: + To improve the speed of FVW module, the user may wish to compile with + `OpenMP`. To do so, add the `-DOPENMP=ON` option with CMake. From fa1d016a69a624b93d92a9d7dc4335ff9d61064f Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 1 May 2020 14:34:05 -0600 Subject: [PATCH 151/190] FVW: remove save variables --- modules/aerodyn/src/FVW.f90 | 8 +- modules/aerodyn/src/FVW_IO.f90 | 82 ++-- modules/aerodyn/src/FVW_Registry.txt | 12 + modules/aerodyn/src/FVW_Tests.f90 | 94 +++-- modules/aerodyn/src/FVW_Types.f90 | 318 ++++++++++++++- modules/aerodyn/src/FVW_VTK.f90 | 552 ++++++++++++++------------- 6 files changed, 711 insertions(+), 355 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index bacab29f02..be51d17b66 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -236,6 +236,10 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%OldWakeTime = -HUGE(1.0_DbKi) ! Temporary UA call AllocAry( m%Vwnd_ND, 3, p%nSpan+1, p%nWings, 'Vwnd_ND', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%TE = -999999_ReKi; + + ! set the VTK misc vars + call vtk_misc_init(m%vtk) + end subroutine FVW_InitMiscVars ! ============================================================================== subroutine FVW_InitMiscVarsPostParam( p, m, ErrStat, ErrMsg ) @@ -949,12 +953,12 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, m%VTKlastTime = t if ((p%VTKCoord==2).or.(p%VTKCoord==3)) then ! Hub reference coordinates, for export only, ALL VTK Will be exported in this coordinate system! - call set_vtk_coordinate_transform(u%HubOrientation,u%HubPosition) + call set_vtk_coordinate_transform(u%HubOrientation,u%HubPosition,m%vtk) call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Hub', m%VTKStep, 9) endif if ((p%VTKCoord==1).or.(p%VTKCoord==3)) then ! Global coordinate system, ALL VTK will be exported in global - call set_vtk_no_coordinate_transform() + call set_vtk_no_coordinate_transform(m%vtk) call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Glb', m%VTKStep, 9) endif endif diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 71d1c96651..502838320a 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -189,7 +189,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints - type(FVW_MiscVarType), intent(in ) :: m !< MiscVars + type(FVW_MiscVarType), intent(inout) :: m !< MiscVars character(*), intent(in) :: FileRootName !< Name of the file to write the output in (excluding extension) integer(IntKi), intent(in) :: VTKcount !< Indicates number for VTK output file (when 0, the routine will also write reference information) integer(IntKi), intent(in) :: Twidth !< Number of digits in the maximum write-out step (used to pad the VTK write-out in the filename with zeros) @@ -210,7 +210,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) print'(A,L1,A,I0,A,I0,A,I0)','VTK Output - First call ',m%FirstCall, ' nNW:',m%nNW,' nFW:',m%nFW,' i:',VTKCount endif ! - call set_vtk_binary_format(.false.) ! TODO binary fails + call set_vtk_binary_format(.false.,m%vtk) ! TODO binary fails ! TimeStamp write(Tstr, '(i' // trim(Num2LStr(Twidth)) //'.'// trim(Num2LStr(Twidth)) // ')') VTKcount @@ -222,25 +222,25 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,p%VTKBlades write(Label,'(A,A)') 'BldPointCP.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - if ( vtk_new_ascii_file(trim(filename),Label) ) then - call vtk_dataset_polydata(m%CP_LL(1:3,1:p%nSpan,iW)) - call vtk_point_data_init() - call vtk_point_data_scalar(m%Gamma_ll( 1:p%nSpan,iW),'Gamma_ll') - call vtk_point_data_vector(m%Vind_ll (1:3,1:p%nSpan,iW),'Vind_ll') - call vtk_point_data_vector(m%Vtot_ll (1:3,1:p%nSpan,iW),'Vtot_ll') - call vtk_point_data_vector(m%Vstr_ll (1:3,1:p%nSpan,iW),'Vstr_ll') - call vtk_point_data_vector(m%Vwnd_ll (1:3,1:p%nSpan,iW),'Vwnd_ll') - call vtk_point_data_vector(m%Tang (1:3,1:p%nSpan,iW),'Tangent') - call vtk_point_data_vector(m%Norm (1:3,1:p%nSpan,iW),'Normal') - call vtk_point_data_vector(m%Orth (1:3,1:p%nSpan,iW),'Orth') - call vtk_close_file() + if ( vtk_new_ascii_file(trim(filename),Label,m%vtk) ) then + call vtk_dataset_polydata(m%CP_LL(1:3,1:p%nSpan,iW),m%vtk) + call vtk_point_data_init(m%vtk) + call vtk_point_data_scalar(m%Gamma_ll( 1:p%nSpan,iW),'Gamma_ll',m%vtk) + call vtk_point_data_vector(m%Vind_ll (1:3,1:p%nSpan,iW),'Vind_ll',m%vtk) + call vtk_point_data_vector(m%Vtot_ll (1:3,1:p%nSpan,iW),'Vtot_ll',m%vtk) + call vtk_point_data_vector(m%Vstr_ll (1:3,1:p%nSpan,iW),'Vstr_ll',m%vtk) + call vtk_point_data_vector(m%Vwnd_ll (1:3,1:p%nSpan,iW),'Vwnd_ll',m%vtk) + call vtk_point_data_vector(m%Tang (1:3,1:p%nSpan,iW),'Tangent',m%vtk) + call vtk_point_data_vector(m%Norm (1:3,1:p%nSpan,iW),'Normal',m%vtk) + call vtk_point_data_vector(m%Orth (1:3,1:p%nSpan,iW),'Orth',m%vtk) + call vtk_close_file(m%vtk) endif enddo ! --- Lifting line panels do iW=1,p%VTKBlades write(Label,'(A,A)') 'LL.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - call WrVTK_Lattice(FileName, m%r_LL(1:3,:,:,iW), m%Gamma_LL(:,iW:iW)) + call WrVTK_Lattice(FileName, m%vtk, m%r_LL(1:3,:,:,iW), m%Gamma_LL(:,iW:iW)) enddo ! --------------------------------------------------------------------------------} ! --- Near wake @@ -251,10 +251,10 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' if (m%FirstCall) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel allocate(dxdt_0(3, size(m%dxdt_NW,2) , m%nNW+1)); dxdt_0=0.0_ReKi - call WrVTK_Lattice(FileName, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW),dxdt_0) + call WrVTK_Lattice(FileName, m%vtk, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW),dxdt_0) deallocate(dxdt_0) else - call WrVTK_Lattice(FileName, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), m%dxdt_NW(:,:,1:m%nNW+1,iW)) + call WrVTK_Lattice(FileName, m%vtk, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), m%dxdt_NW(:,:,1:m%nNW+1,iW)) endif enddo ! --------------------------------------------------------------------------------} @@ -264,7 +264,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,p%VTKBlades write(Label,'(A,A)') 'FW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - call WrVTK_Lattice(FileName, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW),m%dxdt_FW(:,:,1:m%nFW+1,iW)) + call WrVTK_Lattice(FileName, m%vtk, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW),m%dxdt_FW(:,:,1:m%nFW+1,iW)) enddo ! --------------------------------------------------------------------------------} ! --- All Segments @@ -281,34 +281,36 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) nSegP = 2*nSegP endif Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' - CALL WrVTK_Segments(Filename, m%SegPoints(:,1:nSegP), m%SegConnct(:,1:nSeg), m%SegGamma(1:nSeg), m%SegEpsilon(1:nSeg)) + CALL WrVTK_Segments(Filename, m%vtk, m%SegPoints(:,1:nSegP), m%SegConnct(:,1:nSeg), m%SegGamma(1:nSeg), m%SegEpsilon(1:nSeg)) if(.false.) print*,z%Gamma_LL(1,1) ! unused var for now end subroutine WrVTK_FVW -subroutine WrVTK_Segments(filename, SegPoints, SegConnct, SegGamma, SegEpsilon) +subroutine WrVTK_Segments(filename, mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon) use VTK character(len=*),intent(in) :: filename + type(FVW_VTK_Misc), intent(inout) :: mvtk !< miscvars for VTK output real(ReKi), dimension(:,:), intent(in) :: SegPoints !< integer(IntKi), dimension(:,:), intent(in) :: SegConnct !< real(ReKi), dimension(:) , intent(in) :: SegGamma !< real(ReKi), dimension(:) , intent(in) :: SegEpsilon !< - if ( vtk_new_ascii_file(filename,'Sgmt') ) then - call vtk_dataset_polydata(SegPoints(1:3,:)) - call vtk_lines(SegConnct(1:2,:)-1) ! NOTE: VTK indexing at 0 - call vtk_cell_data_init() - call vtk_cell_data_scalar(SegGamma ,'Gamma') - call vtk_cell_data_scalar(SegEpsilon,'Epsilon') -! call vtk_cell_data_scalar(real(SegConnct(3,:), ReKi),'Age') - !call vtk_cell_data_scalar(real(SegConnct(4,:), ReKi),'Span') - call vtk_close_file() + if ( vtk_new_ascii_file(filename,'Sgmt',mvtk) ) then + call vtk_dataset_polydata(SegPoints(1:3,:),mvtk) + call vtk_lines(SegConnct(1:2,:)-1,mvtk) ! NOTE: VTK indexing at 0 + call vtk_cell_data_init(mvtk) + call vtk_cell_data_scalar(SegGamma ,'Gamma',mvtk) + call vtk_cell_data_scalar(SegEpsilon,'Epsilon',mvtk) +! call vtk_cell_data_scalar(real(SegConnct(3,:), ReKi),'Age',mvtk) + !call vtk_cell_data_scalar(real(SegConnct(4,:), ReKi),'Span',mvtk) + call vtk_close_file(mvtk) endif end subroutine -subroutine WrVTK_Lattice(filename, LatticePoints, LatticeGamma, LatticeData3d) +subroutine WrVTK_Lattice(filename, mvtk, LatticePoints, LatticeGamma, LatticeData3d) use VTK ! for all the vtk_* functions character(len=*), intent(in) :: filename + type(FVW_VTK_Misc), intent(inout) :: mvtk !< miscvars for VTK output real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Array of points 3 x nSpan x nDepth real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< Array of nSpan x nDepth real(Reki), dimension(:,:,:), intent(in ), optional :: LatticeData3d !< Array of n x nSpan x nDepth KEEP ME @@ -318,16 +320,16 @@ subroutine WrVTK_Lattice(filename, LatticePoints, LatticeGamma, LatticeData3d) CALL LatticeToPanlConnectivity(LatticePoints, Connectivity, Points) - if ( vtk_new_ascii_file(filename,'')) then - call vtk_dataset_polydata(Points) - call vtk_quad(Connectivity) - call vtk_cell_data_init() - call vtk_cell_data_scalar(LatticeGamma,'Gamma') + if ( vtk_new_ascii_file(filename,'',mvtk)) then + call vtk_dataset_polydata(Points,mvtk) + call vtk_quad(Connectivity,mvtk) + call vtk_cell_data_init(mvtk) + call vtk_cell_data_scalar(LatticeGamma,'Gamma',mvtk) if (present(LatticeData3d)) then - call vtk_point_data_init() - call vtk_point_data_vector(LatticeData3d,'Uconv') + call vtk_point_data_init(mvtk) + call vtk_point_data_vector(LatticeData3d,'Uconv',mvtk) endif - call vtk_close_file() + call vtk_close_file(mvtk) endif end subroutine WrVTK_Lattice @@ -363,13 +365,13 @@ subroutine LatticeToPanlConnectivity(LatticePoints, Connectivity, Points) enddo; enddo ! do iWing=1,p%NumBlades -! if ( vtk_new_ascii_file(trim(filename),Label) ) then +! if ( vtk_new_ascii_file(trim(filename),Label,m%vtk) ) then ! ! Buffer for points ! k=1; do iNW=1,nNW; do iSpan=1,nSpan ! Buffer(1:3,k) = Misc%NWake%r_nearj(1:3,iSpan,iNW,iWing) ! k=k+1 ! enddo; enddo -! call vtk_dataset_polydata(Buffer) +! call vtk_dataset_polydata(Buffer,m%vtk) ! call vtk_quad(Connectivity) ! call vtk_cell_data_init() ! ! Buffer for Gammas m1 diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 297cc8474c..bada4eb7fd 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -7,6 +7,17 @@ include Registry_NWTC_Library.txt usefrom AirfoilInfo_Registry.txt usefrom UnsteadyAero_Registry.txt + +typedef FVW/FVW FVW_VTK_Misc IntKi vtk_unit - - - "VTK output unit" - +typedef ^ ^ Logical bFileOpen - - - "binary file is open" - +typedef ^ ^ Logical bBinary - - - "write binary files" - +typedef ^ ^ Logical bChangeFrame - - - "change to blade ref frame" - +typedef ^ ^ IntKi nData - - - "number of data lines" - +typedef ^ ^ IntKi nPoints - - - "number of points" - +typedef ^ ^ Character(255) buffer - - - "character buffer" - +typedef ^ ^ ReKi T_g2b {3}{3} - - "reference frame transform" - +typedef ^ ^ ReKi PO_g {3} - - "reference frame origin in transform" - + ##################### Registry for FVW ############### # ..... PARAMETERS ............. #FVW_ParameterType @@ -106,6 +117,7 @@ typedef ^ ^ UA_OutputType typedef ^ ^ UA_ParameterType p_UA - - - "parameters for UnsteadyAero" - typedef ^ ^ LOGICAL UA_Flag - - - "logical flag indicating whether to use UnsteadyAero" - typedef ^ ^ ReKi Vwnd_ND ::: - - "InflowOnBlade (at nodes) values modified by tower influence. ONLY for UA" m/s +typedef ^ ^ FVW_VTK_Misc VTK - - - "VTK output misc vars, for optimizing VTK writing" - # ........ Input ............ # FVW_InputType diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index ac627ba4dd..f9cae8e8c4 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -14,7 +14,6 @@ module FVW_Tests public :: FVW_RunTests private - character(len=255),save :: testname interface test_equal; module procedure & test_equal_i1, & test_equal_i0 @@ -28,7 +27,8 @@ module FVW_Tests ! -------------------------------------------------------------------------------- ! --- Helper functions (should be part of NWTC library) ! -------------------------------------------------------------------------------- - subroutine test_success(info,bPrint_in) + subroutine test_success(testname,info,bPrint_in) + character(len=*), intent(in) :: testname character(len=*), intent(in) :: info logical, intent(in), optional :: bPrint_in if(present(bPrint_in)) then @@ -40,7 +40,8 @@ subroutine test_success(info,bPrint_in) endif end subroutine - subroutine test_fail(info,bPrint_in,bStop_in) + subroutine test_fail(testname,info,bPrint_in,bStop_in) + character(len=*), intent(in) :: testname character(len=*), intent(in) :: info logical, intent(in), optional :: bPrint_in logical, intent(in), optional :: bStop_in @@ -60,8 +61,9 @@ subroutine test_fail(info,bPrint_in,bStop_in) endif end subroutine - subroutine test_equal_i0(Var,iTry,iRef) + subroutine test_equal_i0(testname,Var,iTry,iRef) ! Arguments + character(len=*), intent(in) :: testname character(len=*), intent(in) :: Var integer, intent(in) :: iTry !< integer, intent(in) :: iRef !< @@ -69,16 +71,17 @@ subroutine test_equal_i0(Var,iTry,iRef) character(len=255) :: InfoAbs if(iRef/=iTry) then write(InfoAbs,'(A,I0,A,I0)') trim(Var),iRef,'/',iTry - call test_fail(InfoAbs) + call test_fail(testname,InfoAbs) STOP else write(InfoAbs,'(A,A,I0)') trim(Var),' ok ',iRef - call test_success(InfoAbs) + call test_success(testname,InfoAbs) endif end subroutine - subroutine test_equal_i1(Var,VecTry,VecRef,bTest,bPrintOnly,bPassed) + subroutine test_equal_i1(testname,Var,VecTry,VecRef,bTest,bPrintOnly,bPassed) ! Arguments + character(len=*), intent(in) :: testname character(len=*), intent(in) :: Var integer, dimension(:), intent(in) :: VecTry !< integer, dimension(:), intent(in) :: VecRef !< @@ -111,16 +114,17 @@ subroutine test_equal_i1(Var,VecTry,VecRef,bTest,bPrintOnly,bPassed) endif if(bTest) then if(cpt>0) then - call test_fail(InfoAbs) + call test_fail(testname,InfoAbs) STOP else - call test_success(InfoAbs) + call test_success(testname,InfoAbs) endif endif end subroutine - subroutine test_almost_equal_0(Var,Ref,Try,MINNORM,bStop,bPrint,bPassed) + subroutine test_almost_equal_0(testname,Var,Ref,Try,MINNORM,bStop,bPrint,bPassed) ! Arguments + character(len=*), intent(in) :: testname character(len=*), intent(in) :: Var real(ReKi), intent(in) :: Ref !< real(ReKi), intent(in) :: Try !< @@ -137,17 +141,18 @@ subroutine test_almost_equal_0(Var,Ref,Try,MINNORM,bStop,bPrint,bPassed) delta=abs(Ref-Try) if(delta>MINNORM) then write(InfoAbs,'(A,ES8.1E2,A,ES8.1E2,A,I0)') trim(Var)//' tol: ',MINNORM,', mean: ',delta,' - Failed:',cpt - call test_fail(InfoAbs,bPrint,bStop) + call test_fail(testname,InfoAbs,bPrint,bStop) else write(InfoAbs,'(A,ES8.1E2,A,ES8.1E2)') trim(Var)//' tol: ',MINNORM,', mean: ',delta - call test_success(InfoAbs,bPrint) + call test_success(testname,InfoAbs,bPrint) endif if(present(bPassed)) then bPassed=delta>MINNORM endif end subroutine - subroutine test_almost_equal_1(Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) + subroutine test_almost_equal_1(testname,Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) ! Arguments + character(len=*), intent(in) :: testname character(len=*), intent(in) :: Var real(ReKi), dimension(:), intent(in) :: VecRef !< real(ReKi), dimension(:), intent(in) :: VecTry !< @@ -174,17 +179,18 @@ subroutine test_almost_equal_1(Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) if(cpt>0) then write(InfoAbs,'(A,ES8.1E2,A,ES8.1E2,A,I0)') trim(Var)//' tol: ',MINNORM,', mean: ',delta_cum,' - Failed:',cpt - call test_fail(InfoAbs,bPrint,bStop) + call test_fail(testname,InfoAbs,bPrint,bStop) else write(InfoAbs,'(A,ES8.1E2,A,ES8.1E2)') trim(Var)//' tol: ',MINNORM,', mean: ',delta_cum - call test_success(InfoAbs,bPrint) + call test_success(testname,InfoAbs,bPrint) endif if(present(bPassed)) then bPassed=(cpt==0) endif end subroutine - subroutine test_almost_equal_2(Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) + subroutine test_almost_equal_2(testname,Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) ! Arguments + character(len=*), intent(in) :: testname character(len=*), intent(in) :: Var real(ReKi), dimension(:,:), intent(in) :: VecRef !< real(ReKi), dimension(:,:), intent(in) :: VecTry !< @@ -205,14 +211,15 @@ subroutine test_almost_equal_2(Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) VecRef2(p)=VecRef(i,j) VecTry2(p)=VecTry(i,j) enddo; enddo; - call test_almost_equal(Var,VecRef2,VecTry2,MINNORM,bStop,bPrint,bPassed) + call test_almost_equal(testname,Var,VecRef2,VecTry2,MINNORM,bStop,bPrint,bPassed) end subroutine ! --------------------------------------------------------------------------------} ! --- Specific FVW tests ! --------------------------------------------------------------------------------{ !> - subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) + subroutine Test_BiotSavart_Sgmt(testname, ErrStat, ErrMsg) + character(len=*), intent(in) :: testname integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None real(ReKi), dimension(3) :: P1,P2,P3,CP @@ -266,7 +273,7 @@ subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) !print*,'Reg function', RegFunction, 'CP',CP !print*,'Uind_out',Uind_out !print*,'U1 ',U1 - call test_almost_equal('Uind method1/2', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) + call test_almost_equal(testname,'Uind method1/2', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) !call test_almost_equal('Uind method1/2', U1, Uind_out(:,1), 1e-4, .false.,.true.) enddo enddo @@ -305,13 +312,14 @@ subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) !print*,'Reg function', RegFunction, 'CP',CP !print*,'Uind_out',Uind_out !print*,'U1 ',U1 - call test_almost_equal('Uind 1seg/2seg', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) + call test_almost_equal(testname,'Uind 1seg/2seg', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) enddo enddo end subroutine !> - subroutine Test_BiotSavart_Part(ErrStat, ErrMsg) + subroutine Test_BiotSavart_Part(testname, ErrStat, ErrMsg) + character(len=*), intent(in) :: testname integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None real(ReKi), dimension(3) :: P1,CP @@ -359,13 +367,14 @@ subroutine Test_BiotSavart_Part(ErrStat, ErrMsg) !print*,'Reg function', RegFunction, 'CP',CP !print*,'Uind_out',Uind_out !print*,'U1 ',U1 - call test_almost_equal('Uind part method1/2', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) + call test_almost_equal(testname,'Uind part method1/2', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) enddo enddo end subroutine Test_BiotSavart_Part !> This test compares calls using the tree algorithm and the direct N^2 evaluation - subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) + subroutine Test_BiotSavart_PartTree(testname, ErrStat, ErrMsg) + character(len=*), intent(in) :: testname integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None type(T_Tree) :: Tree @@ -401,7 +410,7 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) call ui_tree(Tree, CPs, 0, 1, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) ! Test - call test_almost_equal('Uind tree 0 part', U_ref, Uind2(:,1), 1e-4_ReKi, .true.,.true.) + call test_almost_equal(testname,'Uind tree 0 part', U_ref, Uind2(:,1), 1e-4_ReKi, .true.,.true.) call cut_tree(Tree) call dealloc() @@ -417,7 +426,7 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) call ui_tree(Tree, CPs, 0, 1, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) ! Test - call test_almost_equal('Uind tree 1 part', Uind1, Uind2, 1e-4_ReKi, .true.,.true.) + call test_almost_equal(testname,'Uind tree 1 part', Uind1, Uind2, 1e-4_ReKi, .true.,.true.) call cut_tree(Tree) !call print_tree(Tree) call dealloc() @@ -451,12 +460,12 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) !print*,'Uind',Uind1, Uind2 ! Test - call test_almost_equal('Uind tree 81 part', Uind1, Uind2, 1e-2_ReKi, .true.,.true.) + call test_almost_equal(testname,'Uind tree 81 part', Uind1, Uind2, 1e-2_ReKi, .true.,.true.) enddo call cut_tree(Tree) ! --- Test that tree ui cannot be called after tree has been cut call ui_tree(Tree, CPs, 0, 1, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) - call test_equal('Err. stat tree cut',ErrStat,ErrID_Fatal) + call test_equal(testname,'Err. stat tree cut',ErrStat,ErrID_Fatal) call dealloc() contains @@ -478,7 +487,8 @@ subroutine dealloc() end subroutine Test_BiotSavart_PartTree !> Compares the velocity field obtained from a segment and its convert to particle version - subroutine Test_SegmentsToPart(ErrStat, ErrMsg) + subroutine Test_SegmentsToPart(testname, ErrStat, ErrMsg) + character(len=*), intent(in) :: testname integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None real(ReKi), dimension(:,:), allocatable :: PartPoints !< Particle points @@ -533,7 +543,7 @@ subroutine Test_SegmentsToPart(ErrStat, ErrMsg) Uind1 =0.0_ReKi; Uind2 =0.0_ReKi; call ui_seg(1, nCPsTot, CPs, 1, nSegTot, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, RegFunctionSeg, SegEpsilon, Uind1) call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunctionPart, PartEpsilon, Uind2, nCPsTot, nPart) - call test_almost_equal('Uind 10 part/sgmt no reg', Uind1, Uind2, 1e-3_ReKi, .true.,.true.) + call test_almost_equal(testname,'Uind 10 part/sgmt no reg', Uind1, Uind2, 1e-3_ReKi, .true.,.true.) call dealloc() ! --- Test 1 - 2 particles, no regularization @@ -549,7 +559,7 @@ subroutine Test_SegmentsToPart(ErrStat, ErrMsg) Uind1 =0.0_ReKi; Uind2 =0.0_ReKi; call ui_seg(1, nCPsTot, CPs, 1, nSegTot, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, RegFunctionSeg, SegEpsilon, Uind1) call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunctionPart, PartEpsilon, Uind2, nCPsTot, nPart) - call test_almost_equal('Uind 2 part/sgmt noreg', Uind1, Uind2, 3e-1_ReKi, .true.,.true.) + call test_almost_equal(testname,'Uind 2 part/sgmt noreg', Uind1, Uind2, 3e-1_ReKi, .true.,.true.) call dealloc() @@ -573,7 +583,7 @@ subroutine Test_SegmentsToPart(ErrStat, ErrMsg) !print'(A,10F7.3)','Uind2',Uind2(2,:) !print'(A,10F7.3)','Uind1',Uind1(3,:) !print'(A,10F7.3)','Uind2',Uind2(3,:) - call test_almost_equal('Uind 10 part/sgmt w.reg', Uind1, Uind2, 5e-2_ReKi, .true.,.true.) + call test_almost_equal(testname,'Uind 10 part/sgmt w.reg', Uind1, Uind2, 5e-2_ReKi, .true.,.true.) call dealloc() contains @@ -590,7 +600,8 @@ subroutine dealloc() end subroutine Test_SegmentsToPart !> - subroutine Test_LatticeToSegment(iStat) + subroutine Test_LatticeToSegment(mvtk,iStat) + type(FVW_VTK_Misc),intent(inout) :: mvtk !< miscvars for VTK output integer(IntKi), intent( out) :: iStat !< Status for test ! Local integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity @@ -631,8 +642,8 @@ subroutine Test_LatticeToSegment(iStat) CALL MeshMe(LatticePoints1,(/0.,0.,0./)) CALL MeshMe(LatticePoints2,(/0.,0.,1./)) - CALL WrVTK_Lattice('Points1.vtk',LatticePoints1, LatticeGamma1) - CALL WrVTK_Lattice('Points2.vtk',LatticePoints2, LatticeGamma2) + CALL WrVTK_Lattice('Points1.vtk',mvtk,LatticePoints1, LatticeGamma1) + CALL WrVTK_Lattice('Points2.vtk',mvtk,LatticePoints2, LatticeGamma2) ! --- Convert lattice 1 to segments nSpan = size(LatticePoints1,2) @@ -648,7 +659,7 @@ subroutine Test_LatticeToSegment(iStat) iHeadC=1 CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true., .true. ) CALL printall() - CALL WrVTK_Segments('Points1_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) + CALL WrVTK_Segments('Points1_seg.vtk', mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon) allocate(Uind(1:3,1) ); Uind=0.0_ReKi allocate(CPs (1:3,1) ); @@ -675,7 +686,7 @@ subroutine Test_LatticeToSegment(iStat) iHeadC=1 CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , .true., .true.) CALL printall() - CALL WrVTK_Segments('Points2_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) + CALL WrVTK_Segments('Points2_seg.vtk', mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon) ! --- Concatenate both nP = nP1 + nP2 @@ -691,7 +702,7 @@ subroutine Test_LatticeToSegment(iStat) CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. , .true.) CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. , .true.) CALL printall() - CALL WrVTK_Segments('PointsBoth_seg.vtk', SegPoints, SegConnct, SegGamma, SegEpsilon) + CALL WrVTK_Segments('PointsBoth_seg.vtk', mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon) contains @@ -726,14 +737,15 @@ subroutine FVW_RunTests(ErrStat,ErrMsg) character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 + character(len=255) :: testname ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" testname='FVW' - call Test_BiotSavart_Sgmt(ErrStat2, ErrMsg2) - call Test_BiotSavart_Part(ErrStat2, ErrMsg2) - call Test_BiotSavart_PartTree(ErrStat2, ErrMsg2) - call Test_SegmentsToPart(ErrStat2, ErrMsg2) + call Test_BiotSavart_Sgmt(testname, ErrStat2, ErrMsg2) + call Test_BiotSavart_Part(testname, ErrStat2, ErrMsg2) + call Test_BiotSavart_PartTree(testname, ErrStat2, ErrMsg2) + call Test_SegmentsToPart(testname, ErrStat2, ErrMsg2) end subroutine FVW_RunTests end module FVW_Tests diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 8337724a2a..a783fc2730 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -35,6 +35,19 @@ MODULE FVW_Types USE UnsteadyAero_Types USE NWTC_Library IMPLICIT NONE +! ========= FVW_VTK_Misc ======= + TYPE, PUBLIC :: FVW_VTK_Misc + INTEGER(IntKi) :: vtk_unit !< VTK output unit [-] + LOGICAL :: bFileOpen !< binary file is open [-] + LOGICAL :: bBinary !< write binary files [-] + LOGICAL :: bChangeFrame !< change to blade ref frame [-] + INTEGER(IntKi) :: nData !< number of data lines [-] + INTEGER(IntKi) :: nPoints !< number of points [-] + Character(255) :: buffer !< character buffer [-] + REAL(ReKi) , DIMENSION(1:3,1:3) :: T_g2b !< reference frame transform [-] + REAL(ReKi) , DIMENSION(1:3) :: PO_g !< reference frame origin in transform [-] + END TYPE FVW_VTK_Misc +! ======================= ! ========= FVW_ParameterType ======= TYPE, PUBLIC :: FVW_ParameterType INTEGER(IntKi) :: nWings !< Number of Wings [-] @@ -127,6 +140,7 @@ MODULE FVW_Types TYPE(UA_ParameterType) :: p_UA !< parameters for UnsteadyAero [-] LOGICAL :: UA_Flag !< logical flag indicating whether to use UnsteadyAero [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vwnd_ND !< InflowOnBlade (at nodes) values modified by tower influence. ONLY for UA [m/s] + TYPE(FVW_VTK_Misc) :: VTK !< VTK output misc vars, for optimizing VTK writing [-] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -233,6 +247,217 @@ MODULE FVW_Types END TYPE FVW_InitOutputType ! ======================= CONTAINS + SUBROUTINE FVW_CopyVTK_Misc( SrcVTK_MiscData, DstVTK_MiscData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_VTK_Misc), INTENT(IN) :: SrcVTK_MiscData + TYPE(FVW_VTK_Misc), INTENT(INOUT) :: DstVTK_MiscData + 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 = 'FVW_CopyVTK_Misc' +! + ErrStat = ErrID_None + ErrMsg = "" + DstVTK_MiscData%vtk_unit = SrcVTK_MiscData%vtk_unit + DstVTK_MiscData%bFileOpen = SrcVTK_MiscData%bFileOpen + DstVTK_MiscData%bBinary = SrcVTK_MiscData%bBinary + DstVTK_MiscData%bChangeFrame = SrcVTK_MiscData%bChangeFrame + DstVTK_MiscData%nData = SrcVTK_MiscData%nData + DstVTK_MiscData%nPoints = SrcVTK_MiscData%nPoints + DstVTK_MiscData%buffer = SrcVTK_MiscData%buffer + DstVTK_MiscData%T_g2b = SrcVTK_MiscData%T_g2b + DstVTK_MiscData%PO_g = SrcVTK_MiscData%PO_g + END SUBROUTINE FVW_CopyVTK_Misc + + SUBROUTINE FVW_DestroyVTK_Misc( VTK_MiscData, ErrStat, ErrMsg ) + TYPE(FVW_VTK_Misc), INTENT(INOUT) :: VTK_MiscData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyVTK_Misc' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE FVW_DestroyVTK_Misc + + SUBROUTINE FVW_PackVTK_Misc( 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(FVW_VTK_Misc), 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 = 'FVW_PackVTK_Misc' + ! 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 ! vtk_unit + Int_BufSz = Int_BufSz + 1 ! bFileOpen + Int_BufSz = Int_BufSz + 1 ! bBinary + Int_BufSz = Int_BufSz + 1 ! bChangeFrame + Int_BufSz = Int_BufSz + 1 ! nData + Int_BufSz = Int_BufSz + 1 ! nPoints + Int_BufSz = Int_BufSz + 1*LEN(InData%buffer) ! buffer + Re_BufSz = Re_BufSz + SIZE(InData%T_g2b) ! T_g2b + Re_BufSz = Re_BufSz + SIZE(InData%PO_g) ! PO_g + 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:Int_Xferred+(1)-1 ) = InData%vtk_unit + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%bFileOpen , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%bBinary , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%bChangeFrame , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nData + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nPoints + Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(InData%buffer) + IntKiBuf(Int_Xferred) = ICHAR(InData%buffer(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%T_g2b))-1 ) = PACK(InData%T_g2b,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%T_g2b) + ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%PO_g))-1 ) = PACK(InData%PO_g,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%PO_g) + END SUBROUTINE FVW_PackVTK_Misc + + SUBROUTINE FVW_UnPackVTK_Misc( 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(FVW_VTK_Misc), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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 = 'FVW_UnPackVTK_Misc' + ! 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%vtk_unit = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%bFileOpen = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + OutData%bBinary = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + OutData%bChangeFrame = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + OutData%nData = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%nPoints = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + DO I = 1, LEN(OutData%buffer) + OutData%buffer(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I + i1_l = LBOUND(OutData%T_g2b,1) + i1_u = UBOUND(OutData%T_g2b,1) + i2_l = LBOUND(OutData%T_g2b,2) + i2_u = UBOUND(OutData%T_g2b,2) + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + OutData%T_g2b = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%T_g2b))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%T_g2b) + DEALLOCATE(mask2) + i1_l = LBOUND(OutData%PO_g,1) + i1_u = UBOUND(OutData%PO_g,1) + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + OutData%PO_g = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%PO_g))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%PO_g) + DEALLOCATE(mask1) + END SUBROUTINE FVW_UnPackVTK_Misc + SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg ) TYPE(FVW_ParameterType), INTENT(IN) :: SrcParamData TYPE(FVW_ParameterType), INTENT(INOUT) :: DstParamData @@ -243,8 +468,6 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg 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 = 'FVW_CopyParam' @@ -596,8 +819,6 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) 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 = 'FVW_UnPackParam' @@ -1347,6 +1568,9 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) END IF DstMiscData%Vwnd_ND = SrcMiscData%Vwnd_ND ENDIF + CALL FVW_Copyvtk_misc( SrcMiscData%VTK, DstMiscData%VTK, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE FVW_CopyMisc SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -1469,6 +1693,7 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%Vwnd_ND)) THEN DEALLOCATE(MiscData%Vwnd_ND) ENDIF + CALL FVW_Destroyvtk_misc( MiscData%VTK, ErrStat, ErrMsg ) END SUBROUTINE FVW_DestroyMisc SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -1748,6 +1973,23 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*3 ! Vwnd_ND upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%Vwnd_ND) ! Vwnd_ND END IF + Int_BufSz = Int_BufSz + 3 ! VTK: size of buffers for each call to pack subtype + CALL FVW_Packvtk_misc( Re_Buf, Db_Buf, Int_Buf, InData%VTK, ErrStat2, ErrMsg2, .TRUE. ) ! VTK + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! VTK + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! VTK + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! VTK + 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 @@ -2527,6 +2769,34 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%Vwnd_ND)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vwnd_ND))-1 ) = PACK(InData%Vwnd_ND,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%Vwnd_ND) END IF + CALL FVW_Packvtk_misc( Re_Buf, Db_Buf, Int_Buf, InData%VTK, ErrStat2, ErrMsg2, OnlySize ) ! VTK + 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 FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3713,6 +3983,46 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%Vwnd_ND) DEALLOCATE(mask3) END IF + 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 FVW_Unpackvtk_misc( Re_Buf, Db_Buf, Int_Buf, OutData%VTK, ErrStat2, ErrMsg2 ) ! VTK + 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 FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW_VTK.f90 b/modules/aerodyn/src/FVW_VTK.f90 index 4dfa005741..c65dcbe15f 100644 --- a/modules/aerodyn/src/FVW_VTK.f90 +++ b/modules/aerodyn/src/FVW_VTK.f90 @@ -1,30 +1,15 @@ module VTK !use PrecisionMod, only: ReKi use NWTC_Library, only: ReKi, GetNewUnit + use FVW_Types, only: FVW_VTK_Misc implicit none ! character(8), parameter :: RFMT='F14.5' !character(8), parameter :: RFMT='E24.15E3' character(8), parameter :: RFMT='E17.8E3' character(8), parameter :: IFMT='I7' - integer, save :: vtk_unit - logical, save :: bFileOpen=.false. - - integer, save :: nData=0; - integer, save :: nPoints=0; - - - logical, save :: bBinary = .false. - character(len=255), save :: buffer character(1), parameter :: NL = char(10) ! New Line character - - ! Reference Frame - logical,save :: bChangeFrame =.false. - real(ReKi),dimension(3,3),save :: T_g2b - real(ReKi),dimension(3) ,save :: PO_g - - interface vtk_dataset_structured_grid; module procedure & vtk_dataset_structured_grid_flat, & vtk_dataset_structured_grid_grid @@ -46,71 +31,82 @@ module VTK end interface public - private:: vtk_unit, bFileOpen, nData, nPoints,bBinary, buffer - private :: bChangeFrame, T_g2b, PO_g contains + subroutine vtk_misc_init(mvtk) + type(FVW_VTK_Misc),intent(inout) :: mvtk + mvtk%vtk_unit = -1 !< VTK output unit [-] + mvtk%bFileOpen = .false. !< binary file is open [-] + mvtk%bBinary = .false. !< write binary files [-] + mvtk%bChangeFrame = .false. !< change to blade ref frame [-] + mvtk%nData = 0 !< number of data lines [-] + mvtk%nPoints = 0 !< number of points [-] + end subroutine !> - subroutine set_vtk_binary_format(bBin) + subroutine set_vtk_binary_format(bBin,mvtk) logical, intent(in)::bBin - bBinary=bBin + type(FVW_VTK_Misc),intent(inout) :: mvtk + mvtk%bBinary=bBin end subroutine !> Save a coordinate transform ! ALL VTK Will be exported in this coordinate system! - subroutine set_vtk_coordinate_transform(T_g2b_in,PO_g_in) + subroutine set_vtk_coordinate_transform(T_g2b_in,PO_g_in,mvtk) real(ReKi),dimension(3,3), intent(in) :: T_g2b_in real(ReKi),dimension(3) , intent(in) :: PO_g_in - bChangeFrame=.true. - T_g2b=T_g2b_in - PO_g=PO_g_in + type(FVW_VTK_Misc),intent(inout) :: mvtk + mvtk%bChangeFrame=.true. + mvtk%T_g2b=T_g2b_in + mvtk%PO_g=PO_g_in end subroutine - subroutine set_vtk_no_coordinate_transform() - bChangeFrame=.false. + subroutine set_vtk_no_coordinate_transform(mvtk) + type(FVW_VTK_Misc),intent(inout) :: mvtk + mvtk%bChangeFrame=.false. end subroutine - logical function vtk_new_ascii_file(filename,label) + logical function vtk_new_ascii_file(filename,label,mvtk) !use MainIO, only: get_free_unit ,check_io !use MainIOData, only: bSTOP_ALLOWED !use FileSystem, only: file_exists !use Logging, only: log_warning,log_error,log_info ! - character(len=*),intent(in) :: filename - character(len=*),intent(in) :: label + character(len=*),intent(in) :: filename + character(len=*),intent(in) :: label + type(FVW_VTK_Misc),intent(inout) :: mvtk ! integer :: iostatvar logical :: b - if (.not.bFileOpen) then - CALL GetNewUnit( vtk_unit ) - if (bBinary) then + if (.not. mvtk%bFileOpen) then + CALL GetNewUnit( mvtk%vtk_unit ) + if (mvtk%bBinary) then ! Fortran 2003 stream, otherwise intel fortran ! !form='UNFORMATTED',access='SEQUENTIAL',action='WRITE',convert='BIG_ENDIAN',recordtype='STREAM',buffered='YES', !print*,'Not available for this compiler' !COMPAQ-COMPILER !STOP !COMPAQ-COMPILER - open(unit = vtk_unit,file= trim(adjustl(filename)),form='UNFORMATTED',access = 'stream',& !OTHER-COMPILER + open(unit = mvtk%vtk_unit,file= trim(adjustl(filename)),form='UNFORMATTED',access = 'stream',& !OTHER-COMPILER action = 'WRITE',convert= 'BIG_ENDIAN',iostat=iostatvar,status='replace') !OTHER-COMPILER else - open(vtk_unit,file=trim(adjustl(filename)),iostat=iostatvar,action="write",status='replace') + open(mvtk%vtk_unit,file=trim(adjustl(filename)),iostat=iostatvar,action="write",status='replace') endif if (iostatvar == 0) then - if (bBinary) then - write(vtk_unit)'# vtk DataFile Version 3.0'//NL - write(vtk_unit)trim(label)//NL - write(vtk_unit)'BINARY'//NL + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'# vtk DataFile Version 3.0'//NL + write(mvtk%vtk_unit)trim(label)//NL + write(mvtk%vtk_unit)'BINARY'//NL else - write(vtk_unit,'(a)') '# vtk DataFile Version 2.0' - write(vtk_unit,'(a)') label - write(vtk_unit,'(a)') 'ASCII' - write(vtk_unit,'(a)') ' ' + write(mvtk%vtk_unit,'(a)') '# vtk DataFile Version 2.0' + write(mvtk%vtk_unit,'(a)') label + write(mvtk%vtk_unit,'(a)') 'ASCII' + write(mvtk%vtk_unit,'(a)') ' ' endif - bFileOpen=.true. - nData=-1; + mvtk%bFileOpen=.true. + mvtk%nData=-1; endif else b=.false. @@ -123,10 +119,11 @@ logical function vtk_new_ascii_file(filename,label) endif end function - subroutine vtk_close_file() - if ( bFileOpen ) then - close(vtk_unit) - bFileOpen=.false. + subroutine vtk_close_file(mvtk) + type(FVW_VTK_Misc),intent(inout) :: mvtk + if ( mvtk%bFileOpen ) then + close(mvtk%vtk_unit) + mvtk%bFileOpen=.false. endif endsubroutine @@ -134,85 +131,88 @@ subroutine vtk_close_file() ! ------------------------------------------------------------------------- ! --- POLYDATA STUFF ! ------------------------------------------------------------------------- - subroutine vtk_dataset_polydata(Points) + subroutine vtk_dataset_polydata(Points,mvtk) real(ReKi), dimension(:,:),intent(in) :: Points !< 3 x n + type(FVW_VTK_Misc),intent(inout) :: mvtk integer :: i - if ( bFileOpen ) then - nPoints=size(Points,2) - if (bBinary) then - write(vtk_unit)'DATASET POLYDATA'//NL - write(buffer,'(A,I0,A)') 'POINTS ', nPoints ,' double' - write(vtk_unit)trim(buffer)//NL - if (bChangeFrame) then - do i=1,nPoints - write(vtk_unit)matmul(T_g2b,Points(1:3,i)-PO_g) + if ( mvtk%bFileOpen ) then + mvtk%nPoints=size(Points,2) + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'DATASET POLYDATA'//NL + write(mvtk%buffer,'(A,I0,A)') 'POINTS ', mvtk%nPoints ,' double' + write(mvtk%vtk_unit)trim(mvtk%buffer)//NL + if (mvtk%bChangeFrame) then + do i=1,mvtk%nPoints + write(mvtk%vtk_unit)matmul(mvtk%T_g2b,Points(1:3,i)-mvtk%PO_g) enddo else - do i=1,nPoints - write(vtk_unit)Points(1:3,i) + do i=1,mvtk%nPoints + write(mvtk%vtk_unit)Points(1:3,i) enddo endif - write(vtk_unit)NL + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A)') 'DATASET POLYDATA' - write(vtk_unit,'(A,I0,A)') 'POINTS ', nPoints ,' double' - if (bChangeFrame) then - do i=1,nPoints - write(vtk_unit,'(3'//RFMT//')') matmul(T_g2b,Points(1:3,i)-PO_g) + write(mvtk%vtk_unit,'(A)') 'DATASET POLYDATA' + write(mvtk%vtk_unit,'(A,I0,A)') 'POINTS ', mvtk%nPoints ,' double' + if (mvtk%bChangeFrame) then + do i=1,mvtk%nPoints + write(mvtk%vtk_unit,'(3'//RFMT//')') matmul(mvtk%T_g2b,Points(1:3,i)-mvtk%PO_g) enddo else - do i=1,nPoints - write(vtk_unit,'(3'//RFMT//')') Points(1:3,i) + do i=1,mvtk%nPoints + write(mvtk%vtk_unit,'(3'//RFMT//')') Points(1:3,i) enddo endif - write(vtk_unit,*) ' ' + write(mvtk%vtk_unit,*) ' ' endif endif end subroutine - subroutine vtk_lines(L) + subroutine vtk_lines(L,mvtk) integer, dimension(:,:),intent(in) :: L !< 2 x n + type(FVW_VTK_Misc),intent(inout) :: mvtk integer :: i - if ( bFileOpen ) then - nData=size(L,2) - if (bBinary) then - write(buffer,'(A,I0,A,I0)')'LINES ',nData,' ',3*nData - write(vtk_unit)trim(buffer)//NL - do i=1,nData - write(vtk_unit)2,L(1:2,i) + if ( mvtk%bFileOpen ) then + mvtk%nData=size(L,2) + if (mvtk%bBinary) then + write(mvtk%buffer,'(A,I0,A,I0)')'LINES ',mvtk%nData,' ',3*mvtk%nData + write(mvtk%vtk_unit)trim(mvtk%buffer)//NL + do i=1,mvtk%nData + write(mvtk%vtk_unit)2,L(1:2,i) enddo - write(vtk_unit)NL + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A,I0,A,I0)')'LINES ',nData,' ',3*nData - do i=1,nData - write(vtk_unit,'(3'//IFMT//')') 2, L(1:2,i) + write(mvtk%vtk_unit,'(A,I0,A,I0)')'LINES ',mvtk%nData,' ',3*mvtk%nData + do i=1,mvtk%nData + write(mvtk%vtk_unit,'(3'//IFMT//')') 2, L(1:2,i) enddo - write(vtk_unit,*) ' ' + write(mvtk%vtk_unit,*) ' ' endif endif end subroutine - subroutine vtk_quad(Q) + subroutine vtk_quad(Q,mvtk) integer, dimension(:,:),intent(in) :: Q !< 4 x n + type(FVW_VTK_Misc),intent(inout) :: mvtk integer :: i - if ( bFileOpen ) then - nData=size(Q,2) - if (bBinary) then - write(buffer,'(A,I0,A,I0)')'POLYGONS ',nData,' ',5*nData - write(vtk_unit)trim(buffer)//NL - do i=1,nData - write(vtk_unit)4,Q(1:4,i) + if ( mvtk%bFileOpen ) then + mvtk%nData=size(Q,2) + if (mvtk%bBinary) then + write(mvtk%buffer,'(A,I0,A,I0)')'POLYGONS ',mvtk%nData,' ',5*mvtk%nData + write(mvtk%vtk_unit)trim(mvtk%buffer)//NL + do i=1,mvtk%nData + write(mvtk%vtk_unit)4,Q(1:4,i) enddo - write(vtk_unit)NL + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A,I0,A,I0)') 'POLYGONS ', nData,' ',5*nData - do i=1,nData - write(vtk_unit,'(5'//IFMT//')') 4, Q(1:4,i) + write(mvtk%vtk_unit,'(A,I0,A,I0)') 'POLYGONS ', mvtk%nData,' ',5*mvtk%nData + do i=1,mvtk%nData + write(mvtk%vtk_unit,'(5'//IFMT//')') 4, Q(1:4,i) enddo - write(vtk_unit,*) ' ' + write(mvtk%vtk_unit,*) ' ' endif endif end subroutine @@ -220,37 +220,38 @@ subroutine vtk_quad(Q) ! ------------------------------------------------------------------------- ! --- RECTILINEAR ! ------------------------------------------------------------------------- - subroutine vtk_dataset_rectilinear(v1,v2,v3) + subroutine vtk_dataset_rectilinear(v1,v2,v3,mvtk) real(ReKi), dimension(:),intent(in) :: v1,v2,v3 !< n - - if ( bFileOpen ) then - nPoints=size(v1)*size(v2)*size(v3) - if (bBinary) then - write(vtk_unit) 'DATASET RECTILINEAR_GRID'//NL - write(buffer,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', size(v1),' ',size(v2),' ',size(v3) - write(vtk_unit) trim(buffer)//NL - write(buffer,'(A,I0,A)') 'X_COORDINATES ', size(v1), ' double' - write(vtk_unit) trim(buffer)//NL - write(vtk_unit)v1 - write(vtk_unit)NL - write(buffer,'(A,I0,A)') 'Y_COORDINATES ', size(v2), ' double' - write(vtk_unit) trim(buffer)//NL - write(vtk_unit)v2 - write(vtk_unit)NL - write(buffer,'(A,I0,A)') 'Z_COORDINATES ', size(v3), ' double' - write(vtk_unit) trim(buffer)//NL - write(vtk_unit)v3 - !write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + + if ( mvtk%bFileOpen ) then + mvtk%nPoints=size(v1)*size(v2)*size(v3) + if (mvtk%bBinary) then + write(mvtk%vtk_unit) 'DATASET RECTILINEAR_GRID'//NL + write(mvtk%buffer,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', size(v1),' ',size(v2),' ',size(v3) + write(mvtk%vtk_unit) trim(mvtk%buffer)//NL + write(mvtk%buffer,'(A,I0,A)') 'X_COORDINATES ', size(v1), ' double' + write(mvtk%vtk_unit) trim(mvtk%buffer)//NL + write(mvtk%vtk_unit)v1 + write(mvtk%vtk_unit)NL + write(mvtk%buffer,'(A,I0,A)') 'Y_COORDINATES ', size(v2), ' double' + write(mvtk%vtk_unit) trim(mvtk%buffer)//NL + write(mvtk%vtk_unit)v2 + write(mvtk%vtk_unit)NL + write(mvtk%buffer,'(A,I0,A)') 'Z_COORDINATES ', size(v3), ' double' + write(mvtk%vtk_unit) trim(mvtk%buffer)//NL + write(mvtk%vtk_unit)v3 + !write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A)') 'DATASET RECTILINEAR_GRID' - write(vtk_unit,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', size(v1),' ',size(v2),' ',size(v3) - write(vtk_unit,'(A,I0,A)') 'X_COORDINATES ', size(v1), ' double' - write(vtk_unit,'('//RFMT//')') v1 - write(vtk_unit,'(A,I0,A)') 'Y_COORDINATES ', size(v2), ' double' - write(vtk_unit,'('//RFMT//')') v2 - write(vtk_unit,'(A,I0,A)') 'Z_COORDINATES ', size(v3), ' double' - write(vtk_unit,'('//RFMT//')') v3 - write(vtk_unit,*) ' ' + write(mvtk%vtk_unit,'(A)') 'DATASET RECTILINEAR_GRID' + write(mvtk%vtk_unit,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', size(v1),' ',size(v2),' ',size(v3) + write(mvtk%vtk_unit,'(A,I0,A)') 'X_COORDINATES ', size(v1), ' double' + write(mvtk%vtk_unit,'('//RFMT//')') v1 + write(mvtk%vtk_unit,'(A,I0,A)') 'Y_COORDINATES ', size(v2), ' double' + write(mvtk%vtk_unit,'('//RFMT//')') v2 + write(mvtk%vtk_unit,'(A,I0,A)') 'Z_COORDINATES ', size(v3), ' double' + write(mvtk%vtk_unit,'('//RFMT//')') v3 + write(mvtk%vtk_unit,*) ' ' endif endif end subroutine @@ -259,50 +260,52 @@ subroutine vtk_dataset_rectilinear(v1,v2,v3) ! --- STRUCTURED GRID (Points dumped without for loop since memory is in proper order) ! ------------------------------------------------------------------------- !> Subroutine using flat data as input (not in natural order) - subroutine vtk_dataset_structured_grid_flat(D,n1,n2,n3) + subroutine vtk_dataset_structured_grid_flat(D,n1,n2,n3,mvtk) integer , intent(in) :: n1,n2,n3 real(ReKi), dimension(:,:),intent(in)::D - if ( bFileOpen ) then - nPoints=n1*n2*n3 - if (bBinary) then - write(vtk_unit) 'DATASET STRUCTURED_GRID'//NL - write(buffer,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 - write(vtk_unit) trim(buffer)//NL - write(buffer,'(A,I0,A)') 'POINTS ', nPoints, ' double' - write(vtk_unit) trim(buffer)//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + if ( mvtk%bFileOpen ) then + mvtk%nPoints=n1*n2*n3 + if (mvtk%bBinary) then + write(mvtk%vtk_unit) 'DATASET STRUCTURED_GRID'//NL + write(mvtk%buffer,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 + write(mvtk%vtk_unit) trim(mvtk%buffer)//NL + write(mvtk%buffer,'(A,I0,A)') 'POINTS ', mvtk%nPoints, ' double' + write(mvtk%vtk_unit) trim(mvtk%buffer)//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A)') 'DATASET STRUCTURED_GRID' - write(vtk_unit,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 - write(vtk_unit,'(A,I0,A)') 'POINTS ', nPoints, ' double' - write(vtk_unit,'(3'//RFMT//')')D - write(vtk_unit,*) ' ' + write(mvtk%vtk_unit,'(A)') 'DATASET STRUCTURED_GRID' + write(mvtk%vtk_unit,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 + write(mvtk%vtk_unit,'(A,I0,A)') 'POINTS ', mvtk%nPoints, ' double' + write(mvtk%vtk_unit,'(3'//RFMT//')')D + write(mvtk%vtk_unit,*) ' ' endif endif end subroutine !> Using Grid data 4d as input - subroutine vtk_dataset_structured_grid_grid(D,n1,n2,n3) + subroutine vtk_dataset_structured_grid_grid(D,n1,n2,n3,mvtk) integer , intent(in) :: n1,n2,n3 real(ReKi), dimension(:,:,:,:),intent(in)::D - - if ( bFileOpen ) then - nPoints=n1*n2*n3 - if (bBinary) then - write(vtk_unit) 'DATASET STRUCTURED_GRID'//NL - write(buffer,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 - write(vtk_unit) trim(buffer)//NL - write(buffer,'(A,I0,A)') 'POINTS ', nPoints, ' double' - write(vtk_unit) trim(buffer)//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + + if ( mvtk%bFileOpen ) then + mvtk%nPoints=n1*n2*n3 + if (mvtk%bBinary) then + write(mvtk%vtk_unit) 'DATASET STRUCTURED_GRID'//NL + write(mvtk%buffer,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 + write(mvtk%vtk_unit) trim(mvtk%buffer)//NL + write(mvtk%buffer,'(A,I0,A)') 'POINTS ', mvtk%nPoints, ' double' + write(mvtk%vtk_unit) trim(mvtk%buffer)//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A)') 'DATASET STRUCTURED_GRID' - write(vtk_unit,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 - write(vtk_unit,'(A,I0,A)') 'POINTS ', nPoints, ' double' - write(vtk_unit,'(3'//RFMT//')')D - write(vtk_unit,*) ' ' + write(mvtk%vtk_unit,'(A)') 'DATASET STRUCTURED_GRID' + write(mvtk%vtk_unit,'(A,I0,A,I0,A,I0)') 'DIMENSIONS ', n1,' ',n2,' ',n3 + write(mvtk%vtk_unit,'(A,I0,A)') 'POINTS ', mvtk%nPoints, ' double' + write(mvtk%vtk_unit,'(3'//RFMT//')')D + write(mvtk%vtk_unit,*) ' ' endif endif end subroutine @@ -312,113 +315,120 @@ subroutine vtk_dataset_structured_grid_grid(D,n1,n2,n3) ! ------------------------------------------------------------------------- ! --- POINT DATA ! ------------------------------------------------------------------------- - subroutine vtk_point_data_init() - if ( bFileOpen ) then - if(bBinary) then - write(buffer,'(A,I0)')'POINT_DATA ',nPoints - write(vtk_unit)trim(buffer)//NL + subroutine vtk_point_data_init(mvtk) + type(FVW_VTK_Misc),intent(inout) :: mvtk + if ( mvtk%bFileOpen ) then + if(mvtk%bBinary) then + write(mvtk%buffer,'(A,I0)')'POINT_DATA ',mvtk%nPoints + write(mvtk%vtk_unit)trim(mvtk%buffer)//NL else - write(vtk_unit,'(A,I0)') 'POINT_DATA ', nPoints + write(mvtk%vtk_unit,'(A,I0)') 'POINT_DATA ', mvtk%nPoints endif endif end subroutine - subroutine vtk_point_data_scalar_flat(D,sname) + subroutine vtk_point_data_scalar_flat(D,sname,mvtk) real(ReKi), dimension(:),intent(in)::D character(len=*),intent(in) ::sname - - if ( bFileOpen ) then - if (bBinary) then - write(vtk_unit)'SCALARS '//trim(sname)//' double'//NL - write(vtk_unit)'LOOKUP_TABLE default'//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'SCALARS '//trim(sname)//' double'//NL + write(mvtk%vtk_unit)'LOOKUP_TABLE default'//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A,A,A)') 'SCALARS ', sname, ' double' - write(vtk_unit,'(A)') 'LOOKUP_TABLE default' - write(vtk_unit,'(1'//RFMT//')')D + write(mvtk%vtk_unit,'(A,A,A)') 'SCALARS ', sname, ' double' + write(mvtk%vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(mvtk%vtk_unit,'(1'//RFMT//')')D endif endif end subroutine - subroutine vtk_point_data_scalar_grid(D,sname) + subroutine vtk_point_data_scalar_grid(D,sname,mvtk) real(ReKi), dimension(:,:,:,:),intent(in)::D character(len=*),intent(in) ::sname - - if ( bFileOpen ) then - if (bBinary) then - write(vtk_unit)'SCALARS '//trim(sname)//' double'//NL - write(vtk_unit)'LOOKUP_TABLE default'//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'SCALARS '//trim(sname)//' double'//NL + write(mvtk%vtk_unit)'LOOKUP_TABLE default'//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A,A,A)') 'SCALARS ', sname, ' double' - write(vtk_unit,'(A)') 'LOOKUP_TABLE default' - write(vtk_unit,'(1'//RFMT//')')D + write(mvtk%vtk_unit,'(A,A,A)') 'SCALARS ', sname, ' double' + write(mvtk%vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(mvtk%vtk_unit,'(1'//RFMT//')')D endif endif end subroutine - subroutine vtk_point_data_scalar_grid2D(D,sname) + subroutine vtk_point_data_scalar_grid2D(D,sname,mvtk) real(ReKi), dimension(:,:,:),intent(in)::D character(len=*),intent(in) ::sname - - if ( bFileOpen ) then - if (bBinary) then - write(vtk_unit)'SCALARS '//trim(sname)//' double'//NL - write(vtk_unit)'LOOKUP_TABLE default'//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'SCALARS '//trim(sname)//' double'//NL + write(mvtk%vtk_unit)'LOOKUP_TABLE default'//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A,A,A)') 'SCALARS ', sname, ' double' - write(vtk_unit,'(A)') 'LOOKUP_TABLE default' - write(vtk_unit,'(1'//RFMT//')')D + write(mvtk%vtk_unit,'(A,A,A)') 'SCALARS ', sname, ' double' + write(mvtk%vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(mvtk%vtk_unit,'(1'//RFMT//')')D endif endif end subroutine !> - subroutine vtk_point_data_vector_flat(D,sname) + subroutine vtk_point_data_vector_flat(D,sname,mvtk) real(ReKi), dimension(:,:),intent(in) :: D !< 3 x n character(len=*),intent(in) ::sname - if ( bFileOpen ) then - if (bBinary) then - write(vtk_unit)'VECTORS '//trim(sname)//' double'//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'VECTORS '//trim(sname)//' double'//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' - write(vtk_unit,'(3'//RFMT//')')D + write(mvtk%vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' + write(mvtk%vtk_unit,'(3'//RFMT//')')D endif endif end subroutine !> - subroutine vtk_point_data_vector_grid(D,sname) + subroutine vtk_point_data_vector_grid(D,sname,mvtk) real(ReKi), dimension(:,:,:,:),intent(in) :: D !< 3 x n character(len=*),intent(in) ::sname - if ( bFileOpen ) then - if (bBinary) then - write(vtk_unit)'VECTORS '//trim(sname)//' double'//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'VECTORS '//trim(sname)//' double'//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' - write(vtk_unit,'(3'//RFMT//')')D + write(mvtk%vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' + write(mvtk%vtk_unit,'(3'//RFMT//')')D endif endif end subroutine !> - subroutine vtk_point_data_vector_grid2D(D,sname) + subroutine vtk_point_data_vector_grid2D(D,sname,mvtk) real(ReKi), dimension(:,:,:),intent(in) :: D !< character(len=*),intent(in) ::sname - if ( bFileOpen ) then - if (bBinary) then - write(vtk_unit)'VECTORS '//trim(sname)//' double'//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'VECTORS '//trim(sname)//' double'//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' - write(vtk_unit,'(3'//RFMT//')')D + write(mvtk%vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' + write(mvtk%vtk_unit,'(3'//RFMT//')')D endif endif end subroutine @@ -427,65 +437,69 @@ subroutine vtk_point_data_vector_grid2D(D,sname) ! ------------------------------------------------------------------------- ! --- CELL DATA ! ------------------------------------------------------------------------- - subroutine vtk_cell_data_init() - if ( bFileOpen ) then - if (bBinary) then - write(buffer,'(A,I0)')'CELL_DATA ',nData - write(vtk_unit)trim(buffer)//NL + subroutine vtk_cell_data_init(mvtk) + type(FVW_VTK_Misc),intent(inout) :: mvtk + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%buffer,'(A,I0)')'CELL_DATA ',mvtk%nData + write(mvtk%vtk_unit)trim(mvtk%buffer)//NL else - write(vtk_unit,'(A,I0)') 'CELL_DATA ', nData + write(mvtk%vtk_unit,'(A,I0)') 'CELL_DATA ', mvtk%nData endif endif end subroutine - subroutine vtk_cell_data_scalar_1d(D,sname) + subroutine vtk_cell_data_scalar_1d(D,sname,mvtk) real(ReKi), dimension(:),intent(in)::D character(len=*),intent(in) ::sname - - if ( bFileOpen ) then - if (bBinary) then - write(vtk_unit)'SCALARS '//trim(sname)//' double 1'//NL - write(vtk_unit)'LOOKUP_TABLE default'//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'SCALARS '//trim(sname)//' double 1'//NL + write(mvtk%vtk_unit)'LOOKUP_TABLE default'//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,fmt='(A,A,A)') 'SCALARS ', sname, ' double' - write(vtk_unit,'(A)') 'LOOKUP_TABLE default' - write(vtk_unit,'(1'//RFMT//')')D + write(mvtk%vtk_unit,fmt='(A,A,A)') 'SCALARS ', sname, ' double' + write(mvtk%vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(mvtk%vtk_unit,'(1'//RFMT//')')D endif endif end subroutine - subroutine vtk_cell_data_scalar_2d(D,sname) + subroutine vtk_cell_data_scalar_2d(D,sname,mvtk) real(ReKi), dimension(:,:),intent(in)::D character(len=*),intent(in) ::sname - - if ( bFileOpen ) then - if (bBinary) then - write(vtk_unit)'SCALARS '//trim(sname)//' double 1'//NL - write(vtk_unit)'LOOKUP_TABLE default'//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'SCALARS '//trim(sname)//' double 1'//NL + write(mvtk%vtk_unit)'LOOKUP_TABLE default'//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,fmt='(A,A,A)') 'SCALARS ', sname, ' double' - write(vtk_unit,'(A)') 'LOOKUP_TABLE default' - write(vtk_unit,'(1'//RFMT//')')D + write(mvtk%vtk_unit,fmt='(A,A,A)') 'SCALARS ', sname, ' double' + write(mvtk%vtk_unit,'(A)') 'LOOKUP_TABLE default' + write(mvtk%vtk_unit,'(1'//RFMT//')')D endif endif end subroutine - subroutine vtk_cell_data_vector(D,sname) + subroutine vtk_cell_data_vector(D,sname,mvtk) real(ReKi), dimension(:,:),intent(in) :: D !< 3 x n character(len=*),intent(in) ::sname - if ( bFileOpen ) then - if (bBinary) then - write(vtk_unit)'VECTORS '//trim(sname)//' double'//NL - write(vtk_unit)D - write(vtk_unit)NL + type(FVW_VTK_Misc),intent(inout) :: mvtk + if ( mvtk%bFileOpen ) then + if (mvtk%bBinary) then + write(mvtk%vtk_unit)'VECTORS '//trim(sname)//' double'//NL + write(mvtk%vtk_unit)D + write(mvtk%vtk_unit)NL else - write(vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' - write(vtk_unit,'(3'//RFMT//')')D + write(mvtk%vtk_unit,'(A,A,A)') 'VECTORS ', sname, ' double' + write(mvtk%vtk_unit,'(3'//RFMT//')')D endif endif end subroutine @@ -494,48 +508,50 @@ subroutine vtk_cell_data_vector(D,sname) ! --- VTK Tools ! --------------------------------------------------------------------------------{ !> Exports a Plane From a mesh - subroutine export_plane_grid3d(fname,v1,v2,v3,Values) + subroutine export_plane_grid3d(fname,v1,v2,v3,Values,mvtk) character(len=*),intent(in) :: fname real(ReKi),dimension(:), intent(in) :: v1,v2,v3 real(ReKi),dimension(:,:,:,:), intent(in) :: Values + type(FVW_VTK_Misc),intent(inout) :: mvtk ! Variables integer :: nD ! Writting - if ( vtk_new_ascii_file(trim(fname),'grid')) then + if ( vtk_new_ascii_file(trim(fname),'grid',mvtk)) then nD=size(Values,1) - call vtk_dataset_rectilinear(v1,v2,v3) + call vtk_dataset_rectilinear(v1,v2,v3,mvtk) ! Output as a structured grid, No need to reorder - call vtk_point_data_init() + call vtk_point_data_init(mvtk) ! Could be a function of nDim, be careful if(nD==3) then - call vtk_point_data_vector(Values(1:3,:,:,:),'Velocity') ! Label... + call vtk_point_data_vector(Values(1:3,:,:,:),'Velocity',mvtk) ! Label... endif - call vtk_close_file() + call vtk_close_file(mvtk) endif ! file opening end subroutine !> Exports a Plane From a mesh - subroutine export_plane_grid2d(fname,v1,v2,v3,Values) + subroutine export_plane_grid2d(fname,v1,v2,v3,Values,mvtk) character(len=*),intent(in) :: fname real(ReKi),dimension(:), intent(in) :: v1,v2,v3 real(ReKi),dimension(:,:,:), intent(in) :: Values + type(FVW_VTK_Misc),intent(inout) :: mvtk ! Variables integer :: nD ! Writting - if ( vtk_new_ascii_file(trim(fname),'plane') ) then + if ( vtk_new_ascii_file(trim(fname),'plane',mvtk) ) then nD=size(Values,1) - call vtk_dataset_rectilinear(v1,v2,v3) + call vtk_dataset_rectilinear(v1,v2,v3,mvtk) ! Output as a structured grid, No need to reorder - call vtk_point_data_init() + call vtk_point_data_init(mvtk) ! Could be a function of nDim, be careful if(nD==3) then - call vtk_point_data_vector(Values(1:3,:,:),'Velocity') ! Label... + call vtk_point_data_vector(Values(1:3,:,:),'Velocity',mvtk) ! Label... endif - call vtk_close_file() + call vtk_close_file(mvtk) endif ! file opening end subroutine end module VTK From abbeb9bc20b7a0bb46c890e9c7b0153f4b4a7237 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 4 May 2020 15:54:11 -0600 Subject: [PATCH 152/190] FVW: decided to remove the vtk temp vars out of miscvars in the registry --- modules/aerodyn/src/FVW.f90 | 9 +- modules/aerodyn/src/FVW_IO.f90 | 48 ++-- modules/aerodyn/src/FVW_Registry.txt | 11 - modules/aerodyn/src/FVW_Tests.f90 | 1 + modules/aerodyn/src/FVW_Types.f90 | 318 +-------------------- modules/aerodyn/src/FVW_VTK.f90 | 21 +- modules/openfast-library/src/FAST_Subs.f90 | 4 +- 7 files changed, 53 insertions(+), 359 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index be51d17b66..f59eb0dd05 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -237,9 +237,6 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) ! Temporary UA call AllocAry( m%Vwnd_ND, 3, p%nSpan+1, p%nWings, 'Vwnd_ND', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%TE = -999999_ReKi; - ! set the VTK misc vars - call vtk_misc_init(m%vtk) - end subroutine FVW_InitMiscVars ! ============================================================================== subroutine FVW_InitMiscVarsPostParam( p, m, ErrStat, ErrMsg ) @@ -880,7 +877,7 @@ end subroutine FVW_CalcConstrStateResidual ! All of the calculated output channels are placed into the m%AllOuts(:), while the channels selected for outputs are ! placed in the y%WriteOutput(:) array. subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ErrMsg ) - use VTK, only: set_vtk_coordinate_transform, set_vtk_no_coordinate_transform + use FVW_VTK, only: set_vtk_coordinate_transform, set_vtk_no_coordinate_transform use FVW_VortexTools, only: interpextrap_cp2node real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(FVW_InputType), intent(in ) :: u !< Inputs at Time t @@ -953,12 +950,12 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, m%VTKlastTime = t if ((p%VTKCoord==2).or.(p%VTKCoord==3)) then ! Hub reference coordinates, for export only, ALL VTK Will be exported in this coordinate system! - call set_vtk_coordinate_transform(u%HubOrientation,u%HubPosition,m%vtk) +! call set_vtk_coordinate_transform(u%HubOrientation,u%HubPosition,mvtk) call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Hub', m%VTKStep, 9) endif if ((p%VTKCoord==1).or.(p%VTKCoord==3)) then ! Global coordinate system, ALL VTK will be exported in global - call set_vtk_no_coordinate_transform(m%vtk) +! call set_vtk_no_coordinate_transform(mvtk) call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Glb', m%VTKStep, 9) endif endif diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 502838320a..b32b0680c6 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -185,7 +185,7 @@ END SUBROUTINE FVW_ReadInputFile !> Export FVW variables to VTK !! NOTE: when entering this function nNW and nFW has been ncremented by 1 subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) - use VTK ! for all the vtk_* functions + use FVW_VTK ! for all the vtk_* functions type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints @@ -205,12 +205,14 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) !character(ErrMsgLen) :: ErrMsg2 real(Reki), dimension(:,:,:), allocatable :: dxdt_0 !< + type(FVW_VTK_Misc) :: mvtk + if (DEV_VERSION) then print*,'------------------------------------------------------------------------------' print'(A,L1,A,I0,A,I0,A,I0)','VTK Output - First call ',m%FirstCall, ' nNW:',m%nNW,' nFW:',m%nFW,' i:',VTKCount endif ! - call set_vtk_binary_format(.false.,m%vtk) ! TODO binary fails + call set_vtk_binary_format(.false.,mvtk) ! TODO binary fails ! TimeStamp write(Tstr, '(i' // trim(Num2LStr(Twidth)) //'.'// trim(Num2LStr(Twidth)) // ')') VTKcount @@ -222,25 +224,25 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,p%VTKBlades write(Label,'(A,A)') 'BldPointCP.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - if ( vtk_new_ascii_file(trim(filename),Label,m%vtk) ) then - call vtk_dataset_polydata(m%CP_LL(1:3,1:p%nSpan,iW),m%vtk) - call vtk_point_data_init(m%vtk) - call vtk_point_data_scalar(m%Gamma_ll( 1:p%nSpan,iW),'Gamma_ll',m%vtk) - call vtk_point_data_vector(m%Vind_ll (1:3,1:p%nSpan,iW),'Vind_ll',m%vtk) - call vtk_point_data_vector(m%Vtot_ll (1:3,1:p%nSpan,iW),'Vtot_ll',m%vtk) - call vtk_point_data_vector(m%Vstr_ll (1:3,1:p%nSpan,iW),'Vstr_ll',m%vtk) - call vtk_point_data_vector(m%Vwnd_ll (1:3,1:p%nSpan,iW),'Vwnd_ll',m%vtk) - call vtk_point_data_vector(m%Tang (1:3,1:p%nSpan,iW),'Tangent',m%vtk) - call vtk_point_data_vector(m%Norm (1:3,1:p%nSpan,iW),'Normal',m%vtk) - call vtk_point_data_vector(m%Orth (1:3,1:p%nSpan,iW),'Orth',m%vtk) - call vtk_close_file(m%vtk) + if ( vtk_new_ascii_file(trim(filename),Label,mvtk) ) then + call vtk_dataset_polydata(m%CP_LL(1:3,1:p%nSpan,iW),mvtk) + call vtk_point_data_init(mvtk) + call vtk_point_data_scalar(m%Gamma_ll( 1:p%nSpan,iW),'Gamma_ll',mvtk) + call vtk_point_data_vector(m%Vind_ll (1:3,1:p%nSpan,iW),'Vind_ll',mvtk) + call vtk_point_data_vector(m%Vtot_ll (1:3,1:p%nSpan,iW),'Vtot_ll',mvtk) + call vtk_point_data_vector(m%Vstr_ll (1:3,1:p%nSpan,iW),'Vstr_ll',mvtk) + call vtk_point_data_vector(m%Vwnd_ll (1:3,1:p%nSpan,iW),'Vwnd_ll',mvtk) + call vtk_point_data_vector(m%Tang (1:3,1:p%nSpan,iW),'Tangent',mvtk) + call vtk_point_data_vector(m%Norm (1:3,1:p%nSpan,iW),'Normal',mvtk) + call vtk_point_data_vector(m%Orth (1:3,1:p%nSpan,iW),'Orth',mvtk) + call vtk_close_file(mvtk) endif enddo ! --- Lifting line panels do iW=1,p%VTKBlades write(Label,'(A,A)') 'LL.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - call WrVTK_Lattice(FileName, m%vtk, m%r_LL(1:3,:,:,iW), m%Gamma_LL(:,iW:iW)) + call WrVTK_Lattice(FileName, mvtk, m%r_LL(1:3,:,:,iW), m%Gamma_LL(:,iW:iW)) enddo ! --------------------------------------------------------------------------------} ! --- Near wake @@ -251,10 +253,10 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' if (m%FirstCall) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel allocate(dxdt_0(3, size(m%dxdt_NW,2) , m%nNW+1)); dxdt_0=0.0_ReKi - call WrVTK_Lattice(FileName, m%vtk, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW),dxdt_0) + call WrVTK_Lattice(FileName, mvtk, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW),dxdt_0) deallocate(dxdt_0) else - call WrVTK_Lattice(FileName, m%vtk, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), m%dxdt_NW(:,:,1:m%nNW+1,iW)) + call WrVTK_Lattice(FileName, mvtk, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), m%dxdt_NW(:,:,1:m%nNW+1,iW)) endif enddo ! --------------------------------------------------------------------------------} @@ -264,7 +266,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,p%VTKBlades write(Label,'(A,A)') 'FW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - call WrVTK_Lattice(FileName, m%vtk, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW),m%dxdt_FW(:,:,1:m%nFW+1,iW)) + call WrVTK_Lattice(FileName, mvtk, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW),m%dxdt_FW(:,:,1:m%nFW+1,iW)) enddo ! --------------------------------------------------------------------------------} ! --- All Segments @@ -281,14 +283,14 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) nSegP = 2*nSegP endif Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' - CALL WrVTK_Segments(Filename, m%vtk, m%SegPoints(:,1:nSegP), m%SegConnct(:,1:nSeg), m%SegGamma(1:nSeg), m%SegEpsilon(1:nSeg)) + CALL WrVTK_Segments(Filename, mvtk, m%SegPoints(:,1:nSegP), m%SegConnct(:,1:nSeg), m%SegGamma(1:nSeg), m%SegEpsilon(1:nSeg)) if(.false.) print*,z%Gamma_LL(1,1) ! unused var for now end subroutine WrVTK_FVW subroutine WrVTK_Segments(filename, mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon) - use VTK + use FVW_VTK character(len=*),intent(in) :: filename type(FVW_VTK_Misc), intent(inout) :: mvtk !< miscvars for VTK output real(ReKi), dimension(:,:), intent(in) :: SegPoints !< @@ -308,7 +310,7 @@ subroutine WrVTK_Segments(filename, mvtk, SegPoints, SegConnct, SegGamma, SegEps end subroutine subroutine WrVTK_Lattice(filename, mvtk, LatticePoints, LatticeGamma, LatticeData3d) - use VTK ! for all the vtk_* functions + use FVW_VTK ! for all the vtk_* functions character(len=*), intent(in) :: filename type(FVW_VTK_Misc), intent(inout) :: mvtk !< miscvars for VTK output real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Array of points 3 x nSpan x nDepth @@ -365,13 +367,13 @@ subroutine LatticeToPanlConnectivity(LatticePoints, Connectivity, Points) enddo; enddo ! do iWing=1,p%NumBlades -! if ( vtk_new_ascii_file(trim(filename),Label,m%vtk) ) then +! if ( vtk_new_ascii_file(trim(filename),Label,mvtk) ) then ! ! Buffer for points ! k=1; do iNW=1,nNW; do iSpan=1,nSpan ! Buffer(1:3,k) = Misc%NWake%r_nearj(1:3,iSpan,iNW,iWing) ! k=k+1 ! enddo; enddo -! call vtk_dataset_polydata(Buffer,m%vtk) +! call vtk_dataset_polydata(Buffer,mvtk) ! call vtk_quad(Connectivity) ! call vtk_cell_data_init() ! ! Buffer for Gammas m1 diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index bada4eb7fd..4266ae87b7 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -8,16 +8,6 @@ usefrom AirfoilInfo_Registry.txt usefrom UnsteadyAero_Registry.txt -typedef FVW/FVW FVW_VTK_Misc IntKi vtk_unit - - - "VTK output unit" - -typedef ^ ^ Logical bFileOpen - - - "binary file is open" - -typedef ^ ^ Logical bBinary - - - "write binary files" - -typedef ^ ^ Logical bChangeFrame - - - "change to blade ref frame" - -typedef ^ ^ IntKi nData - - - "number of data lines" - -typedef ^ ^ IntKi nPoints - - - "number of points" - -typedef ^ ^ Character(255) buffer - - - "character buffer" - -typedef ^ ^ ReKi T_g2b {3}{3} - - "reference frame transform" - -typedef ^ ^ ReKi PO_g {3} - - "reference frame origin in transform" - - ##################### Registry for FVW ############### # ..... PARAMETERS ............. #FVW_ParameterType @@ -117,7 +107,6 @@ typedef ^ ^ UA_OutputType typedef ^ ^ UA_ParameterType p_UA - - - "parameters for UnsteadyAero" - typedef ^ ^ LOGICAL UA_Flag - - - "logical flag indicating whether to use UnsteadyAero" - typedef ^ ^ ReKi Vwnd_ND ::: - - "InflowOnBlade (at nodes) values modified by tower influence. ONLY for UA" m/s -typedef ^ ^ FVW_VTK_Misc VTK - - - "VTK output misc vars, for optimizing VTK writing" - # ........ Input ............ # FVW_InputType diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index f9cae8e8c4..f1df7e62d1 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -8,6 +8,7 @@ module FVW_Tests use FVW_Wings use FVW_IO use FVW_BiotSavart + use FVW_VTK, only : FVW_VTK_Misc implicit none diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index a783fc2730..8337724a2a 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -35,19 +35,6 @@ MODULE FVW_Types USE UnsteadyAero_Types USE NWTC_Library IMPLICIT NONE -! ========= FVW_VTK_Misc ======= - TYPE, PUBLIC :: FVW_VTK_Misc - INTEGER(IntKi) :: vtk_unit !< VTK output unit [-] - LOGICAL :: bFileOpen !< binary file is open [-] - LOGICAL :: bBinary !< write binary files [-] - LOGICAL :: bChangeFrame !< change to blade ref frame [-] - INTEGER(IntKi) :: nData !< number of data lines [-] - INTEGER(IntKi) :: nPoints !< number of points [-] - Character(255) :: buffer !< character buffer [-] - REAL(ReKi) , DIMENSION(1:3,1:3) :: T_g2b !< reference frame transform [-] - REAL(ReKi) , DIMENSION(1:3) :: PO_g !< reference frame origin in transform [-] - END TYPE FVW_VTK_Misc -! ======================= ! ========= FVW_ParameterType ======= TYPE, PUBLIC :: FVW_ParameterType INTEGER(IntKi) :: nWings !< Number of Wings [-] @@ -140,7 +127,6 @@ MODULE FVW_Types TYPE(UA_ParameterType) :: p_UA !< parameters for UnsteadyAero [-] LOGICAL :: UA_Flag !< logical flag indicating whether to use UnsteadyAero [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vwnd_ND !< InflowOnBlade (at nodes) values modified by tower influence. ONLY for UA [m/s] - TYPE(FVW_VTK_Misc) :: VTK !< VTK output misc vars, for optimizing VTK writing [-] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -247,217 +233,6 @@ MODULE FVW_Types END TYPE FVW_InitOutputType ! ======================= CONTAINS - SUBROUTINE FVW_CopyVTK_Misc( SrcVTK_MiscData, DstVTK_MiscData, CtrlCode, ErrStat, ErrMsg ) - TYPE(FVW_VTK_Misc), INTENT(IN) :: SrcVTK_MiscData - TYPE(FVW_VTK_Misc), INTENT(INOUT) :: DstVTK_MiscData - 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 = 'FVW_CopyVTK_Misc' -! - ErrStat = ErrID_None - ErrMsg = "" - DstVTK_MiscData%vtk_unit = SrcVTK_MiscData%vtk_unit - DstVTK_MiscData%bFileOpen = SrcVTK_MiscData%bFileOpen - DstVTK_MiscData%bBinary = SrcVTK_MiscData%bBinary - DstVTK_MiscData%bChangeFrame = SrcVTK_MiscData%bChangeFrame - DstVTK_MiscData%nData = SrcVTK_MiscData%nData - DstVTK_MiscData%nPoints = SrcVTK_MiscData%nPoints - DstVTK_MiscData%buffer = SrcVTK_MiscData%buffer - DstVTK_MiscData%T_g2b = SrcVTK_MiscData%T_g2b - DstVTK_MiscData%PO_g = SrcVTK_MiscData%PO_g - END SUBROUTINE FVW_CopyVTK_Misc - - SUBROUTINE FVW_DestroyVTK_Misc( VTK_MiscData, ErrStat, ErrMsg ) - TYPE(FVW_VTK_Misc), INTENT(INOUT) :: VTK_MiscData - INTEGER(IntKi), INTENT( OUT) :: ErrStat - CHARACTER(*), INTENT( OUT) :: ErrMsg - CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyVTK_Misc' - INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 -! - ErrStat = ErrID_None - ErrMsg = "" - END SUBROUTINE FVW_DestroyVTK_Misc - - SUBROUTINE FVW_PackVTK_Misc( 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(FVW_VTK_Misc), 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 = 'FVW_PackVTK_Misc' - ! 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 ! vtk_unit - Int_BufSz = Int_BufSz + 1 ! bFileOpen - Int_BufSz = Int_BufSz + 1 ! bBinary - Int_BufSz = Int_BufSz + 1 ! bChangeFrame - Int_BufSz = Int_BufSz + 1 ! nData - Int_BufSz = Int_BufSz + 1 ! nPoints - Int_BufSz = Int_BufSz + 1*LEN(InData%buffer) ! buffer - Re_BufSz = Re_BufSz + SIZE(InData%T_g2b) ! T_g2b - Re_BufSz = Re_BufSz + SIZE(InData%PO_g) ! PO_g - 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:Int_Xferred+(1)-1 ) = InData%vtk_unit - Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%bFileOpen , IntKiBuf(1), 1) - Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%bBinary , IntKiBuf(1), 1) - Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%bChangeFrame , IntKiBuf(1), 1) - Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nData - Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nPoints - Int_Xferred = Int_Xferred + 1 - DO I = 1, LEN(InData%buffer) - IntKiBuf(Int_Xferred) = ICHAR(InData%buffer(I:I), IntKi) - Int_Xferred = Int_Xferred + 1 - END DO ! I - ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%T_g2b))-1 ) = PACK(InData%T_g2b,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%T_g2b) - ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%PO_g))-1 ) = PACK(InData%PO_g,.TRUE.) - Re_Xferred = Re_Xferred + SIZE(InData%PO_g) - END SUBROUTINE FVW_PackVTK_Misc - - SUBROUTINE FVW_UnPackVTK_Misc( 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(FVW_VTK_Misc), 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 - LOGICAL :: mask0 - LOGICAL, ALLOCATABLE :: mask1(:) - LOGICAL, ALLOCATABLE :: mask2(:,:) - LOGICAL, ALLOCATABLE :: mask3(:,:,:) - LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) - LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) - 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 = 'FVW_UnPackVTK_Misc' - ! 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%vtk_unit = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 - OutData%bFileOpen = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) - Int_Xferred = Int_Xferred + 1 - OutData%bBinary = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) - Int_Xferred = Int_Xferred + 1 - OutData%bChangeFrame = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) - Int_Xferred = Int_Xferred + 1 - OutData%nData = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 - OutData%nPoints = IntKiBuf( Int_Xferred ) - Int_Xferred = Int_Xferred + 1 - DO I = 1, LEN(OutData%buffer) - OutData%buffer(I:I) = CHAR(IntKiBuf(Int_Xferred)) - Int_Xferred = Int_Xferred + 1 - END DO ! I - i1_l = LBOUND(OutData%T_g2b,1) - i1_u = UBOUND(OutData%T_g2b,1) - i2_l = LBOUND(OutData%T_g2b,2) - i2_u = UBOUND(OutData%T_g2b,2) - ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask2 = .TRUE. - OutData%T_g2b = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%T_g2b))-1 ), mask2, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%T_g2b) - DEALLOCATE(mask2) - i1_l = LBOUND(OutData%PO_g,1) - i1_u = UBOUND(OutData%PO_g,1) - ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask1 = .TRUE. - OutData%PO_g = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%PO_g))-1 ), mask1, 0.0_ReKi ) - Re_Xferred = Re_Xferred + SIZE(OutData%PO_g) - DEALLOCATE(mask1) - END SUBROUTINE FVW_UnPackVTK_Misc - SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg ) TYPE(FVW_ParameterType), INTENT(IN) :: SrcParamData TYPE(FVW_ParameterType), INTENT(INOUT) :: DstParamData @@ -468,6 +243,8 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg 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 = 'FVW_CopyParam' @@ -819,6 +596,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) 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 = 'FVW_UnPackParam' @@ -1568,9 +1347,6 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) END IF DstMiscData%Vwnd_ND = SrcMiscData%Vwnd_ND ENDIF - CALL FVW_Copyvtk_misc( SrcMiscData%VTK, DstMiscData%VTK, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE FVW_CopyMisc SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -1693,7 +1469,6 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%Vwnd_ND)) THEN DEALLOCATE(MiscData%Vwnd_ND) ENDIF - CALL FVW_Destroyvtk_misc( MiscData%VTK, ErrStat, ErrMsg ) END SUBROUTINE FVW_DestroyMisc SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -1973,23 +1748,6 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*3 ! Vwnd_ND upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%Vwnd_ND) ! Vwnd_ND END IF - Int_BufSz = Int_BufSz + 3 ! VTK: size of buffers for each call to pack subtype - CALL FVW_Packvtk_misc( Re_Buf, Db_Buf, Int_Buf, InData%VTK, ErrStat2, ErrMsg2, .TRUE. ) ! VTK - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! VTK - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! VTK - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! VTK - 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 @@ -2769,34 +2527,6 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%Vwnd_ND)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vwnd_ND))-1 ) = PACK(InData%Vwnd_ND,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%Vwnd_ND) END IF - CALL FVW_Packvtk_misc( Re_Buf, Db_Buf, Int_Buf, InData%VTK, ErrStat2, ErrMsg2, OnlySize ) ! VTK - 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 FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3983,46 +3713,6 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%Vwnd_ND) DEALLOCATE(mask3) END IF - 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 FVW_Unpackvtk_misc( Re_Buf, Db_Buf, Int_Buf, OutData%VTK, ErrStat2, ErrMsg2 ) ! VTK - 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 FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW_VTK.f90 b/modules/aerodyn/src/FVW_VTK.f90 index c65dcbe15f..01a5230e16 100644 --- a/modules/aerodyn/src/FVW_VTK.f90 +++ b/modules/aerodyn/src/FVW_VTK.f90 @@ -1,13 +1,28 @@ -module VTK +module FVW_VTK !use PrecisionMod, only: ReKi use NWTC_Library, only: ReKi, GetNewUnit - use FVW_Types, only: FVW_VTK_Misc implicit none ! character(8), parameter :: RFMT='F14.5' !character(8), parameter :: RFMT='E24.15E3' character(8), parameter :: RFMT='E17.8E3' character(8), parameter :: IFMT='I7' + TYPE, PUBLIC :: FVW_VTK_Misc + integer :: vtk_unit + logical :: bFileOpen=.false. + + integer :: nData=0; + integer :: nPoints=0; + + logical :: bBinary = .false. + character(len=255) :: buffer + + ! Reference Frame + logical :: bChangeFrame =.false. + real(ReKi),dimension(3,3) :: T_g2b + real(ReKi),dimension(3) :: PO_g + END TYPE FVW_VTK_Misc + character(1), parameter :: NL = char(10) ! New Line character interface vtk_dataset_structured_grid; module procedure & @@ -554,4 +569,4 @@ subroutine export_plane_grid2d(fname,v1,v2,v3,Values,mvtk) call vtk_close_file(mvtk) endif ! file opening end subroutine -end module VTK +end module FVW_VTK diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 209583ab64..dbd86373a9 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -4959,7 +4959,7 @@ END SUBROUTINE FillOutputAry !> This routine writes all the committed meshes to VTK-formatted files. It doesn't bother with returning an error code. SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) use FVW_IO, only: WrVTK_FVW - use VTK, only: set_vtk_no_coordinate_transform + use FVW_VTK, only: set_vtk_no_coordinate_transform TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(IN ) :: y_FAST !< Output variables for the glue code @@ -5327,7 +5327,7 @@ END SUBROUTINE WrVTK_BasicMeshes !! returning an error code. SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) use FVW_IO, only: WrVTK_FVW - use VTK, only: set_vtk_no_coordinate_transform + use FVW_VTK, only: set_vtk_no_coordinate_transform REAL(DbKi), INTENT(IN ) :: t_global !< Current global time TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code From 0ff23b7d3aaff110d23816f0376d0a013bebf99f Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 4 May 2020 14:28:53 -0600 Subject: [PATCH 153/190] FVW: move bChangeFrame out of types This is now handled as a flag. --- modules/aerodyn/src/FVW.f90 | 11 ++++--- modules/aerodyn/src/FVW_IO.f90 | 36 ++++++++++++++-------- modules/aerodyn/src/FVW_Tests.f90 | 12 +++++--- modules/aerodyn/src/FVW_VTK.f90 | 15 +++------ modules/openfast-library/src/FAST_Subs.f90 | 20 +++--------- 5 files changed, 46 insertions(+), 48 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index f59eb0dd05..b727c2c6d8 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -176,6 +176,7 @@ end subroutine FVW_Init ! ============================================================================== subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) + use FVW_VTK, only : vtk_misc_init type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation @@ -877,7 +878,7 @@ end subroutine FVW_CalcConstrStateResidual ! All of the calculated output channels are placed into the m%AllOuts(:), while the channels selected for outputs are ! placed in the y%WriteOutput(:) array. subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ErrMsg ) - use FVW_VTK, only: set_vtk_coordinate_transform, set_vtk_no_coordinate_transform + use FVW_VTK, only: set_vtk_coordinate_transform use FVW_VortexTools, only: interpextrap_cp2node real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(FVW_InputType), intent(in ) :: u !< Inputs at Time t @@ -950,13 +951,13 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, m%VTKlastTime = t if ((p%VTKCoord==2).or.(p%VTKCoord==3)) then ! Hub reference coordinates, for export only, ALL VTK Will be exported in this coordinate system! -! call set_vtk_coordinate_transform(u%HubOrientation,u%HubPosition,mvtk) - call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Hub', m%VTKStep, 9) + ! Note: hubOrientation and HubPosition are optional, but required for bladeFrame==TRUE + call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Hub', m%VTKStep, 9, bladeFrame=.TRUE., & + HubOrientation=real(u%HubOrientation,ReKi),HubPosition=real(u%HubPosition,ReKi)) endif if ((p%VTKCoord==1).or.(p%VTKCoord==3)) then ! Global coordinate system, ALL VTK will be exported in global -! call set_vtk_no_coordinate_transform(mvtk) - call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Glb', m%VTKStep, 9) + call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Glb', m%VTKStep, 9, bladeFrame=.FALSE.) endif endif endif diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index b32b0680c6..03e6ec1676 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -184,15 +184,19 @@ END SUBROUTINE FVW_ReadInputFile !================================================= !> Export FVW variables to VTK !! NOTE: when entering this function nNW and nFW has been ncremented by 1 -subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) +subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth, bladeFrame, HubOrientation, HubPosition) use FVW_VTK ! for all the vtk_* functions type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< States type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints - type(FVW_MiscVarType), intent(inout) :: m !< MiscVars + type(FVW_MiscVarType), intent(in ) :: m !< MiscVars character(*), intent(in) :: FileRootName !< Name of the file to write the output in (excluding extension) integer(IntKi), intent(in) :: VTKcount !< Indicates number for VTK output file (when 0, the routine will also write reference information) integer(IntKi), intent(in) :: Twidth !< Number of digits in the maximum write-out step (used to pad the VTK write-out in the filename with zeros) + logical, intent(in ) :: bladeFrame !< Output in blade coordinate frame + real(ReKi),optional,dimension(3,3), intent(in) :: HubOrientation + real(ReKi),optional,dimension(3) , intent(in) :: HubPosition + ! local variables integer:: iW character(1024) :: FileName @@ -207,6 +211,12 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) type(FVW_VTK_Misc) :: mvtk + if (bladeFrame .and. present(HubOrientation) .and. present(HubPosition)) then + call set_vtk_coordinate_transform(HubOrientation,HubPosition,mvtk) + else + Call ProgAbort('Programming error in WrVTK_FVW call: Cannot use the WrVTK_FVW with bladeFrame==TRUE without the optional arguments of HubOrientation and HubPosition') + endif + if (DEV_VERSION) then print*,'------------------------------------------------------------------------------' print'(A,L1,A,I0,A,I0,A,I0)','VTK Output - First call ',m%FirstCall, ' nNW:',m%nNW,' nFW:',m%nFW,' i:',VTKCount @@ -225,7 +235,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) write(Label,'(A,A)') 'BldPointCP.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' if ( vtk_new_ascii_file(trim(filename),Label,mvtk) ) then - call vtk_dataset_polydata(m%CP_LL(1:3,1:p%nSpan,iW),mvtk) + call vtk_dataset_polydata(m%CP_LL(1:3,1:p%nSpan,iW),mvtk,bladeFrame) call vtk_point_data_init(mvtk) call vtk_point_data_scalar(m%Gamma_ll( 1:p%nSpan,iW),'Gamma_ll',mvtk) call vtk_point_data_vector(m%Vind_ll (1:3,1:p%nSpan,iW),'Vind_ll',mvtk) @@ -242,7 +252,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,p%VTKBlades write(Label,'(A,A)') 'LL.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - call WrVTK_Lattice(FileName, mvtk, m%r_LL(1:3,:,:,iW), m%Gamma_LL(:,iW:iW)) + call WrVTK_Lattice(FileName, mvtk, m%r_LL(1:3,:,:,iW), m%Gamma_LL(:,iW:iW), bladeFrame=bladeFrame) enddo ! --------------------------------------------------------------------------------} ! --- Near wake @@ -253,10 +263,10 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' if (m%FirstCall) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel allocate(dxdt_0(3, size(m%dxdt_NW,2) , m%nNW+1)); dxdt_0=0.0_ReKi - call WrVTK_Lattice(FileName, mvtk, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW),dxdt_0) + call WrVTK_Lattice(FileName, mvtk, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW),dxdt_0, bladeFrame=bladeFrame) deallocate(dxdt_0) else - call WrVTK_Lattice(FileName, mvtk, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), m%dxdt_NW(:,:,1:m%nNW+1,iW)) + call WrVTK_Lattice(FileName, mvtk, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), m%dxdt_NW(:,:,1:m%nNW+1,iW), bladeFrame=bladeFrame) endif enddo ! --------------------------------------------------------------------------------} @@ -266,7 +276,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,p%VTKBlades write(Label,'(A,A)') 'FW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - call WrVTK_Lattice(FileName, mvtk, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW),m%dxdt_FW(:,:,1:m%nFW+1,iW)) + call WrVTK_Lattice(FileName, mvtk, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW),m%dxdt_FW(:,:,1:m%nFW+1,iW), bladeFrame=bladeFrame) enddo ! --------------------------------------------------------------------------------} ! --- All Segments @@ -283,13 +293,13 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) nSegP = 2*nSegP endif Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' - CALL WrVTK_Segments(Filename, mvtk, m%SegPoints(:,1:nSegP), m%SegConnct(:,1:nSeg), m%SegGamma(1:nSeg), m%SegEpsilon(1:nSeg)) + CALL WrVTK_Segments(Filename, mvtk, m%SegPoints(:,1:nSegP), m%SegConnct(:,1:nSeg), m%SegGamma(1:nSeg), m%SegEpsilon(1:nSeg), bladeFrame) if(.false.) print*,z%Gamma_LL(1,1) ! unused var for now end subroutine WrVTK_FVW -subroutine WrVTK_Segments(filename, mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon) +subroutine WrVTK_Segments(filename, mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon, bladeFrame) use FVW_VTK character(len=*),intent(in) :: filename type(FVW_VTK_Misc), intent(inout) :: mvtk !< miscvars for VTK output @@ -297,8 +307,9 @@ subroutine WrVTK_Segments(filename, mvtk, SegPoints, SegConnct, SegGamma, SegEps integer(IntKi), dimension(:,:), intent(in) :: SegConnct !< real(ReKi), dimension(:) , intent(in) :: SegGamma !< real(ReKi), dimension(:) , intent(in) :: SegEpsilon !< + logical, intent(in ) :: bladeFrame !< Output in blade coordinate frame if ( vtk_new_ascii_file(filename,'Sgmt',mvtk) ) then - call vtk_dataset_polydata(SegPoints(1:3,:),mvtk) + call vtk_dataset_polydata(SegPoints(1:3,:),mvtk,bladeFrame) call vtk_lines(SegConnct(1:2,:)-1,mvtk) ! NOTE: VTK indexing at 0 call vtk_cell_data_init(mvtk) call vtk_cell_data_scalar(SegGamma ,'Gamma',mvtk) @@ -309,13 +320,14 @@ subroutine WrVTK_Segments(filename, mvtk, SegPoints, SegConnct, SegGamma, SegEps endif end subroutine -subroutine WrVTK_Lattice(filename, mvtk, LatticePoints, LatticeGamma, LatticeData3d) +subroutine WrVTK_Lattice(filename, mvtk, LatticePoints, LatticeGamma, LatticeData3d, bladeFrame) use FVW_VTK ! for all the vtk_* functions character(len=*), intent(in) :: filename type(FVW_VTK_Misc), intent(inout) :: mvtk !< miscvars for VTK output real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Array of points 3 x nSpan x nDepth real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< Array of nSpan x nDepth real(Reki), dimension(:,:,:), intent(in ), optional :: LatticeData3d !< Array of n x nSpan x nDepth KEEP ME + logical, intent(in ) :: bladeFrame !< Output in blade coordinate frame ! integer(IntKi), dimension(:,:), allocatable :: Connectivity real(ReKi), dimension(:,:), allocatable :: Points @@ -323,7 +335,7 @@ subroutine WrVTK_Lattice(filename, mvtk, LatticePoints, LatticeGamma, LatticeDat CALL LatticeToPanlConnectivity(LatticePoints, Connectivity, Points) if ( vtk_new_ascii_file(filename,'',mvtk)) then - call vtk_dataset_polydata(Points,mvtk) + call vtk_dataset_polydata(Points,mvtk,bladeFrame) call vtk_quad(Connectivity,mvtk) call vtk_cell_data_init(mvtk) call vtk_cell_data_scalar(LatticeGamma,'Gamma',mvtk) diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index f1df7e62d1..03dc1c19cf 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -625,7 +625,9 @@ subroutine Test_LatticeToSegment(mvtk,iStat) integer(IntKi) :: nC1, nC2 integer(IntKi) :: nDepth, nSpan integer(IntKi) :: SmoothModel + logical :: bladeFrame !< Output in blade frame instead of global coordinate frame iStat=0 + bladeFrame=.FALSE. ! --- Creating two lattice allocate(LatticePoints1(3,2,2)) @@ -643,8 +645,8 @@ subroutine Test_LatticeToSegment(mvtk,iStat) CALL MeshMe(LatticePoints1,(/0.,0.,0./)) CALL MeshMe(LatticePoints2,(/0.,0.,1./)) - CALL WrVTK_Lattice('Points1.vtk',mvtk,LatticePoints1, LatticeGamma1) - CALL WrVTK_Lattice('Points2.vtk',mvtk,LatticePoints2, LatticeGamma2) + CALL WrVTK_Lattice('Points1.vtk',mvtk,LatticePoints1, LatticeGamma1, bladeframe=bladeframe) + CALL WrVTK_Lattice('Points2.vtk',mvtk,LatticePoints2, LatticeGamma2, bladeframe=bladeframe) ! --- Convert lattice 1 to segments nSpan = size(LatticePoints1,2) @@ -660,7 +662,7 @@ subroutine Test_LatticeToSegment(mvtk,iStat) iHeadC=1 CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true., .true. ) CALL printall() - CALL WrVTK_Segments('Points1_seg.vtk', mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon) + CALL WrVTK_Segments('Points1_seg.vtk', mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon, bladeFrame) allocate(Uind(1:3,1) ); Uind=0.0_ReKi allocate(CPs (1:3,1) ); @@ -687,7 +689,7 @@ subroutine Test_LatticeToSegment(mvtk,iStat) iHeadC=1 CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , .true., .true.) CALL printall() - CALL WrVTK_Segments('Points2_seg.vtk', mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon) + CALL WrVTK_Segments('Points2_seg.vtk', mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon, bladeFrame) ! --- Concatenate both nP = nP1 + nP2 @@ -703,7 +705,7 @@ subroutine Test_LatticeToSegment(mvtk,iStat) CALL LatticeToSegments(LatticePoints1, LatticeGamma1, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. , .true.) CALL LatticeToSegments(LatticePoints2, LatticeGamma2, 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .true. , .true.) CALL printall() - CALL WrVTK_Segments('PointsBoth_seg.vtk', mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon) + CALL WrVTK_Segments('PointsBoth_seg.vtk', mvtk, SegPoints, SegConnct, SegGamma, SegEpsilon, bladeFrame) contains diff --git a/modules/aerodyn/src/FVW_VTK.f90 b/modules/aerodyn/src/FVW_VTK.f90 index 01a5230e16..8770d43b83 100644 --- a/modules/aerodyn/src/FVW_VTK.f90 +++ b/modules/aerodyn/src/FVW_VTK.f90 @@ -18,7 +18,6 @@ module FVW_VTK character(len=255) :: buffer ! Reference Frame - logical :: bChangeFrame =.false. real(ReKi),dimension(3,3) :: T_g2b real(ReKi),dimension(3) :: PO_g END TYPE FVW_VTK_Misc @@ -54,7 +53,6 @@ subroutine vtk_misc_init(mvtk) mvtk%vtk_unit = -1 !< VTK output unit [-] mvtk%bFileOpen = .false. !< binary file is open [-] mvtk%bBinary = .false. !< write binary files [-] - mvtk%bChangeFrame = .false. !< change to blade ref frame [-] mvtk%nData = 0 !< number of data lines [-] mvtk%nPoints = 0 !< number of points [-] end subroutine @@ -73,16 +71,10 @@ subroutine set_vtk_coordinate_transform(T_g2b_in,PO_g_in,mvtk) real(ReKi),dimension(3,3), intent(in) :: T_g2b_in real(ReKi),dimension(3) , intent(in) :: PO_g_in type(FVW_VTK_Misc),intent(inout) :: mvtk - mvtk%bChangeFrame=.true. mvtk%T_g2b=T_g2b_in mvtk%PO_g=PO_g_in end subroutine - subroutine set_vtk_no_coordinate_transform(mvtk) - type(FVW_VTK_Misc),intent(inout) :: mvtk - mvtk%bChangeFrame=.false. - end subroutine - logical function vtk_new_ascii_file(filename,label,mvtk) !use MainIO, only: get_free_unit ,check_io !use MainIOData, only: bSTOP_ALLOWED @@ -146,9 +138,10 @@ subroutine vtk_close_file(mvtk) ! ------------------------------------------------------------------------- ! --- POLYDATA STUFF ! ------------------------------------------------------------------------- - subroutine vtk_dataset_polydata(Points,mvtk) + subroutine vtk_dataset_polydata(Points,mvtk,bladeFrame) real(ReKi), dimension(:,:),intent(in) :: Points !< 3 x n type(FVW_VTK_Misc),intent(inout) :: mvtk + logical, intent(in) :: bladeFrame integer :: i if ( mvtk%bFileOpen ) then mvtk%nPoints=size(Points,2) @@ -156,7 +149,7 @@ subroutine vtk_dataset_polydata(Points,mvtk) write(mvtk%vtk_unit)'DATASET POLYDATA'//NL write(mvtk%buffer,'(A,I0,A)') 'POINTS ', mvtk%nPoints ,' double' write(mvtk%vtk_unit)trim(mvtk%buffer)//NL - if (mvtk%bChangeFrame) then + if (bladeFrame) then do i=1,mvtk%nPoints write(mvtk%vtk_unit)matmul(mvtk%T_g2b,Points(1:3,i)-mvtk%PO_g) enddo @@ -169,7 +162,7 @@ subroutine vtk_dataset_polydata(Points,mvtk) else write(mvtk%vtk_unit,'(A)') 'DATASET POLYDATA' write(mvtk%vtk_unit,'(A,I0,A)') 'POINTS ', mvtk%nPoints ,' double' - if (mvtk%bChangeFrame) then + if (bladeFrame) then do i=1,mvtk%nPoints write(mvtk%vtk_unit,'(3'//RFMT//')') matmul(mvtk%T_g2b,Points(1:3,i)-mvtk%PO_g) enddo diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index dbd86373a9..28a88c2887 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -4662,7 +4662,7 @@ SUBROUTINE WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, BD, AD14, TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(IN ) :: SrvD !< ServoDyn data TYPE(AeroDyn14_Data), INTENT(IN ) :: AD14 !< AeroDyn14 data - TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data + TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data ! Out so the AD%m%FVW%vtk misc vars can be changed TYPE(InflowWind_Data), INTENT(IN ) :: IfW !< InflowWind data TYPE(OpenFOAM_Data), INTENT(IN ) :: OpFM !< OpenFOAM data TYPE(HydroDyn_Data), INTENT(IN ) :: HD !< HydroDyn data @@ -4959,7 +4959,6 @@ END SUBROUTINE FillOutputAry !> This routine writes all the committed meshes to VTK-formatted files. It doesn't bother with returning an error code. SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) use FVW_IO, only: WrVTK_FVW - use FVW_VTK, only: set_vtk_no_coordinate_transform TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(IN ) :: y_FAST !< Output variables for the glue code @@ -4969,7 +4968,7 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(IN ) :: SrvD !< ServoDyn data TYPE(AeroDyn14_Data), INTENT(IN ) :: AD14 !< AeroDyn14 data - TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data + TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data ! Out so the AD%m%FVW%vtk misc vars can be changed TYPE(InflowWind_Data), INTENT(IN ) :: IfW !< InflowWind data TYPE(OpenFOAM_Data), INTENT(IN ) :: OpFM !< OpenFOAM data TYPE(HydroDyn_Data), INTENT(IN ) :: HD !< HydroDyn data @@ -5118,16 +5117,10 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, O call MeshWrVTK(p_FAST%TurbinePos, AD%m%FVW_u(1)%WingsMesh(k), trim(VTK_path)//'.FVW_WingsMesh'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth, AD%Input(1)%BladeMotion(k) ) !call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%BladeMotion(K), trim(p_FAST%OutFileRoot)//'.AD_BladeMotion'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2 ) END DO + ! Free wake + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords end if end if - ! Free wake -!FIXME: Should the wake info be in a different routine? - if (allocated(AD%m%FVW_u)) then - if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - call set_vtk_no_coordinate_transform() ! Output in global coords - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) - endif - end if END IF ! HydroDyn @@ -5327,7 +5320,6 @@ END SUBROUTINE WrVTK_BasicMeshes !! returning an error code. SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, AD, IfW, OpFM, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) use FVW_IO, only: WrVTK_FVW - use FVW_VTK, only: set_vtk_no_coordinate_transform REAL(DbKi), INTENT(IN ) :: t_global !< Current global time TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code @@ -5410,11 +5402,9 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD14, A END IF ! Free wake -!FIXME: is there a better way of checking? if (allocated(AD%m%FVW_u)) then if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - call set_vtk_no_coordinate_transform() ! Output in global coords - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth) + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(VTK_path)//'.FVW', y_FAST%VTK_count, Twidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords end if end if From 00a1bd079bd72186b35c1bc52d9ab7076dfd12ef Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 5 May 2020 05:44:15 -0600 Subject: [PATCH 154/190] FVW: bug fix in UA_UpdateState_Wrapper Also remove print statements (doesn't work well with the SimStatus) And fix typo in documentation on channel name --- docs/source/user/aerodyn-fvw/AppendixC.rst | 2 +- modules/aerodyn/src/FVW.f90 | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/source/user/aerodyn-fvw/AppendixC.rst b/docs/source/user/aerodyn-fvw/AppendixC.rst index 8189a46dd9..11e12bd393 100644 --- a/docs/source/user/aerodyn-fvw/AppendixC.rst +++ b/docs/source/user/aerodyn-fvw/AppendixC.rst @@ -22,7 +22,7 @@ the blade number. * - Channel Name(s) - Units - Description - * - :math:`Gam \beta B \alpha` + * - :math:`B \alpha N \beta Gam` - :math:`m^2/s` - Circulation along the blade diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index b727c2c6d8..fc414bf2f1 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -548,7 +548,9 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m nP = p%nWings * ( (p%nSpan+1)*(m%nNW-1+2) +(FWnSpan+1)*(m%nFW+1) ) nFWEff = min(m%nFW, p%nFWFree) ! --- Display some status to screen - if (mod(n,10)==0) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F7.2,A)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's' +!FIXME: this conflicts with the SimStatus WrOver from the FAST_Subs.f90. Leaving out for now. +! Ideally we put this into a log file. +! if (mod(n,10)==0) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F7.2,A)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's' if (DEV_VERSION) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F7.2,A,L1)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's Comp:',m%ComputeWakeInduced ! --- Evaluation at t @@ -1078,8 +1080,10 @@ subroutine UA_UpdateState_Wrapper(AFInfo, n, u, p, x, xd, OtherState, m, ErrStat integer(intKi) :: ErrStat2 ! temporary Error status character(ErrMsgLen) :: ErrMsg2 real(ReKi), dimension(:,:), allocatable :: Vind_node - ErrStat = ErrID_None - ErrMsg = "" + ErrStat = ErrID_None + ErrStat2 = ErrID_None + ErrMsg = "" + ErrMsg2 = "" if (m%UA_Flag) then ! --- Induction on the lifting line control point From fc52ec7a212ea74dd33470003bc5cfb1ebe87242 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 5 May 2020 06:53:54 -0600 Subject: [PATCH 155/190] FVW: add output channels to AD15 --- docs/OtherSupporting/OutListParameters.xlsx | Bin 112870 -> 115695 bytes modules/aerodyn/src/AeroDyn_IO.f90 | 914 ++++++++++---------- 2 files changed, 479 insertions(+), 435 deletions(-) diff --git a/docs/OtherSupporting/OutListParameters.xlsx b/docs/OtherSupporting/OutListParameters.xlsx index a01573dc8c3c1f3e4ff2ca54eb012e44c517b1cf..4a8ff5adf05b6e7d90764af77ff789fe4a086b5f 100644 GIT binary patch delta 102966 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 CTxU37#_Fv~i-z}*yi41v_*Q_0)OQV6lM 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 diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 77fb308c7a..967499c8dc 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -32,12 +32,13 @@ MODULE AeroDyn_IO type(ProgDesc), parameter :: AD_Ver = ProgDesc( 'AeroDyn', '', '' ) character(*), parameter :: AD_Nickname = 'AD' + ! =================================================================================================== ! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m" -! using the parameters listed in the "OutListParameters_RMurray.xlsx" Excel file. Any changes to these +! using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these ! lines should be modified in the Matlab script and/or Excel worksheet as necessary. ! =================================================================================================== -! This code was generated by Write_ChckOutLst.m at 16-Feb-2017 15:50:51. +! This code was generated by Write_ChckOutLst.m at 05-May-2020 06:44:07. ! Parameters related to output length (number of characters allowed in the output data headers): @@ -1228,31 +1229,58 @@ MODULE AeroDyn_IO INTEGER(IntKi), PARAMETER :: B3N7SgCav = 1165 INTEGER(IntKi), PARAMETER :: B3N8SgCav = 1166 INTEGER(IntKi), PARAMETER :: B3N9SgCav = 1167 + INTEGER(IntKi), PARAMETER :: B1N1Gam = 1168 + INTEGER(IntKi), PARAMETER :: B1N2Gam = 1169 + INTEGER(IntKi), PARAMETER :: B1N3Gam = 1170 + INTEGER(IntKi), PARAMETER :: B1N4Gam = 1171 + INTEGER(IntKi), PARAMETER :: B1N5Gam = 1172 + INTEGER(IntKi), PARAMETER :: B1N6Gam = 1173 + INTEGER(IntKi), PARAMETER :: B1N7Gam = 1174 + INTEGER(IntKi), PARAMETER :: B1N8Gam = 1175 + INTEGER(IntKi), PARAMETER :: B1N9Gam = 1176 + INTEGER(IntKi), PARAMETER :: B2N1Gam = 1177 + INTEGER(IntKi), PARAMETER :: B2N2Gam = 1178 + INTEGER(IntKi), PARAMETER :: B2N3Gam = 1179 + INTEGER(IntKi), PARAMETER :: B2N4Gam = 1180 + INTEGER(IntKi), PARAMETER :: B2N5Gam = 1181 + INTEGER(IntKi), PARAMETER :: B2N6Gam = 1182 + INTEGER(IntKi), PARAMETER :: B2N7Gam = 1183 + INTEGER(IntKi), PARAMETER :: B2N8Gam = 1184 + INTEGER(IntKi), PARAMETER :: B2N9Gam = 1185 + INTEGER(IntKi), PARAMETER :: B3N1Gam = 1186 + INTEGER(IntKi), PARAMETER :: B3N2Gam = 1187 + INTEGER(IntKi), PARAMETER :: B3N3Gam = 1188 + INTEGER(IntKi), PARAMETER :: B3N4Gam = 1189 + INTEGER(IntKi), PARAMETER :: B3N5Gam = 1190 + INTEGER(IntKi), PARAMETER :: B3N6Gam = 1191 + INTEGER(IntKi), PARAMETER :: B3N7Gam = 1192 + INTEGER(IntKi), PARAMETER :: B3N8Gam = 1193 + INTEGER(IntKi), PARAMETER :: B3N9Gam = 1194 ! Rotor: - INTEGER(IntKi), PARAMETER :: RtSpeed = 1168 - INTEGER(IntKi), PARAMETER :: RtTSR = 1169 - INTEGER(IntKi), PARAMETER :: RtVAvgxh = 1170 - INTEGER(IntKi), PARAMETER :: RtVAvgyh = 1171 - INTEGER(IntKi), PARAMETER :: RtVAvgzh = 1172 - INTEGER(IntKi), PARAMETER :: RtSkew = 1173 - INTEGER(IntKi), PARAMETER :: RtAeroFxh = 1174 - INTEGER(IntKi), PARAMETER :: RtAeroFyh = 1175 - INTEGER(IntKi), PARAMETER :: RtAeroFzh = 1176 - INTEGER(IntKi), PARAMETER :: RtAeroMxh = 1177 - INTEGER(IntKi), PARAMETER :: RtAeroMyh = 1178 - INTEGER(IntKi), PARAMETER :: RtAeroMzh = 1179 - INTEGER(IntKi), PARAMETER :: RtAeroPwr = 1180 - INTEGER(IntKi), PARAMETER :: RtArea = 1181 - INTEGER(IntKi), PARAMETER :: RtAeroCp = 1182 - INTEGER(IntKi), PARAMETER :: RtAeroCq = 1183 - INTEGER(IntKi), PARAMETER :: RtAeroCt = 1184 + INTEGER(IntKi), PARAMETER :: RtSpeed = 1195 + INTEGER(IntKi), PARAMETER :: RtTSR = 1196 + INTEGER(IntKi), PARAMETER :: RtVAvgxh = 1197 + INTEGER(IntKi), PARAMETER :: RtVAvgyh = 1198 + INTEGER(IntKi), PARAMETER :: RtVAvgzh = 1199 + INTEGER(IntKi), PARAMETER :: RtSkew = 1200 + INTEGER(IntKi), PARAMETER :: RtAeroFxh = 1201 + INTEGER(IntKi), PARAMETER :: RtAeroFyh = 1202 + INTEGER(IntKi), PARAMETER :: RtAeroFzh = 1203 + INTEGER(IntKi), PARAMETER :: RtAeroMxh = 1204 + INTEGER(IntKi), PARAMETER :: RtAeroMyh = 1205 + INTEGER(IntKi), PARAMETER :: RtAeroMzh = 1206 + INTEGER(IntKi), PARAMETER :: RtAeroPwr = 1207 + INTEGER(IntKi), PARAMETER :: RtArea = 1208 + INTEGER(IntKi), PARAMETER :: RtAeroCp = 1209 + INTEGER(IntKi), PARAMETER :: RtAeroCq = 1210 + INTEGER(IntKi), PARAMETER :: RtAeroCt = 1211 ! The maximum number of output channels which can be output by the code. - INTEGER(IntKi), PARAMETER :: MaxOutPts = 1184 + INTEGER(IntKi), PARAMETER :: MaxOutPts = 1211 !End of code generated by Matlab script ! =================================================================================================== @@ -1492,7 +1520,12 @@ MODULE AeroDyn_IO B2N1Clrnc,B2N2Clrnc,B2N3Clrnc,B2N4Clrnc,B2N5Clrnc,B2N6Clrnc,B2N7Clrnc,B2N8Clrnc,B2N9Clrnc, & B3N1Clrnc,B3N2Clrnc,B3N3Clrnc,B3N4Clrnc,B3N5Clrnc,B3N6Clrnc,B3N7Clrnc,B3N8Clrnc,B3N9Clrnc & /), (/9, 3/) ) - + INTEGER, PARAMETER :: BNGam(9,3) = RESHAPE( (/ & ! Vorticity gamma + B1N1Gam,B1N2Gam,B1N3Gam,B1N4Gam,B1N5Gam,B1N6Gam,B1N7Gam,B1N8Gam,B1N9Gam, & + B2N1Gam,B2N2Gam,B2N3Gam,B2N4Gam,B2N5Gam,B2N6Gam,B2N7Gam,B2N8Gam,B2N9Gam, & + B3N1Gam,B3N2Gam,B3N3Gam,B3N4Gam,B3N5Gam,B3N6Gam,B3N7Gam,B3N8Gam,B3N9Gam & + /), (/9,3/) ) + INTEGER(IntKi), PARAMETER :: MaxBl = 3 ! Maximum number of blades allowed in simulation @@ -1916,6 +1949,7 @@ subroutine Calc_WriteOutput_BEMT m%AllOuts( BNFn( beta,k) ) = m%X(j,k)*ct - m%Y(j,k)*st m%AllOuts( BNFt( beta,k) ) = -m%X(j,k)*st - m%Y(j,k)*ct + m%AllOuts( BNGam( beta,k) ) = 0.5_ReKi * p%BEMT%chord(j,k) * m%BEMT_y%Vrel(j,k) * m%BEMT_y%Cl(j,k) ! "Gam" [m^2/s] end do ! nodes end do ! blades @@ -2063,6 +2097,7 @@ subroutine Calc_WriteOutput_FVW m%AllOuts( BNFn( beta,k) ) = m%X(j,k)*ct - m%Y(j,k)*st m%AllOuts( BNFt( beta,k) ) = -m%X(j,k)*st - m%Y(j,k)*ct + m%AllOuts( BNGam( beta,k) ) = 0.5_ReKi * p%FVW%Chord(j,k) * Vrel * AFI_interp%Cl ! "Gam" [m^2/s] end do ! nodes end do ! blades @@ -3124,7 +3159,7 @@ END SUBROUTINE AD_PrintSum !! the sign is set to 0 if the channel is invalid. !! It sets assumes the value p%NumOuts has been set before this routine has been called, and it sets the values of p%OutParam here. !! -!! This routine was generated by Write_ChckOutLst.m using the parameters listed in OutListParameters.xlsx at 16-Feb-2017 15:50:51. +!! This routine was generated by Write_ChckOutLst.m using the parameters listed in OutListParameters.xlsx at 05-May-2020 06:44:07. SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) !.................................................................................................................................. @@ -3149,503 +3184,518 @@ SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) CHARACTER(ChanLen) :: OutListTmp ! A string to temporarily hold OutList(I) CHARACTER(*), PARAMETER :: RoutineName = "SetOutParam" - CHARACTER(OutStrLenM1), PARAMETER :: ValidParamAry(1184) = (/ & ! This lists the names of the allowed parameters, which must be sorted alphabetically + CHARACTER(OutStrLenM1), PARAMETER :: ValidParamAry(1211) = (/ & ! This lists the names of the allowed parameters, which must be sorted alphabetically "B1AZIMUTH","B1N1ALPHA","B1N1AXIND","B1N1CD ","B1N1CL ","B1N1CLRNC","B1N1CM ", & "B1N1CN ","B1N1CPMIN","B1N1CT ","B1N1CURVE","B1N1CX ","B1N1CY ","B1N1DYNP ", & - "B1N1FD ","B1N1FL ","B1N1FN ","B1N1FT ","B1N1FX ","B1N1FY ","B1N1M ", & - "B1N1MM ","B1N1PHI ","B1N1RE ","B1N1SGCAV","B1N1SIGCR","B1N1STVX ","B1N1STVY ", & - "B1N1STVZ ","B1N1THETA","B1N1TNIND","B1N1VDISX","B1N1VDISY","B1N1VDISZ","B1N1VINDX", & - "B1N1VINDY","B1N1VREL ","B1N1VUNDX","B1N1VUNDY","B1N1VUNDZ","B1N2ALPHA","B1N2AXIND", & - "B1N2CD ","B1N2CL ","B1N2CLRNC","B1N2CM ","B1N2CN ","B1N2CPMIN","B1N2CT ", & - "B1N2CURVE","B1N2CX ","B1N2CY ","B1N2DYNP ","B1N2FD ","B1N2FL ","B1N2FN ", & - "B1N2FT ","B1N2FX ","B1N2FY ","B1N2M ","B1N2MM ","B1N2PHI ","B1N2RE ", & - "B1N2SGCAV","B1N2SIGCR","B1N2STVX ","B1N2STVY ","B1N2STVZ ","B1N2THETA","B1N2TNIND", & - "B1N2VDISX","B1N2VDISY","B1N2VDISZ","B1N2VINDX","B1N2VINDY","B1N2VREL ","B1N2VUNDX", & - "B1N2VUNDY","B1N2VUNDZ","B1N3ALPHA","B1N3AXIND","B1N3CD ","B1N3CL ","B1N3CLRNC", & - "B1N3CM ","B1N3CN ","B1N3CPMIN","B1N3CT ","B1N3CURVE","B1N3CX ","B1N3CY ", & - "B1N3DYNP ","B1N3FD ","B1N3FL ","B1N3FN ","B1N3FT ","B1N3FX ","B1N3FY ", & - "B1N3M ","B1N3MM ","B1N3PHI ","B1N3RE ","B1N3SGCAV","B1N3SIGCR","B1N3STVX ", & - "B1N3STVY ","B1N3STVZ ","B1N3THETA","B1N3TNIND","B1N3VDISX","B1N3VDISY","B1N3VDISZ", & - "B1N3VINDX","B1N3VINDY","B1N3VREL ","B1N3VUNDX","B1N3VUNDY","B1N3VUNDZ","B1N4ALPHA", & - "B1N4AXIND","B1N4CD ","B1N4CL ","B1N4CLRNC","B1N4CM ","B1N4CN ","B1N4CPMIN", & - "B1N4CT ","B1N4CURVE","B1N4CX ","B1N4CY ","B1N4DYNP ","B1N4FD ","B1N4FL ", & - "B1N4FN ","B1N4FT ","B1N4FX ","B1N4FY ","B1N4M ","B1N4MM ","B1N4PHI ", & - "B1N4RE ","B1N4SGCAV","B1N4SIGCR","B1N4STVX ","B1N4STVY ","B1N4STVZ ","B1N4THETA", & - "B1N4TNIND","B1N4VDISX","B1N4VDISY","B1N4VDISZ","B1N4VINDX","B1N4VINDY","B1N4VREL ", & - "B1N4VUNDX","B1N4VUNDY","B1N4VUNDZ","B1N5ALPHA","B1N5AXIND","B1N5CD ","B1N5CL ", & - "B1N5CLRNC","B1N5CM ","B1N5CN ","B1N5CPMIN","B1N5CT ","B1N5CURVE","B1N5CX ", & - "B1N5CY ","B1N5DYNP ","B1N5FD ","B1N5FL ","B1N5FN ","B1N5FT ","B1N5FX ", & - "B1N5FY ","B1N5M ","B1N5MM ","B1N5PHI ","B1N5RE ","B1N5SGCAV","B1N5SIGCR", & - "B1N5STVX ","B1N5STVY ","B1N5STVZ ","B1N5THETA","B1N5TNIND","B1N5VDISX","B1N5VDISY", & - "B1N5VDISZ","B1N5VINDX","B1N5VINDY","B1N5VREL ","B1N5VUNDX","B1N5VUNDY","B1N5VUNDZ", & - "B1N6ALPHA","B1N6AXIND","B1N6CD ","B1N6CL ","B1N6CLRNC","B1N6CM ","B1N6CN ", & - "B1N6CPMIN","B1N6CT ","B1N6CURVE","B1N6CX ","B1N6CY ","B1N6DYNP ","B1N6FD ", & - "B1N6FL ","B1N6FN ","B1N6FT ","B1N6FX ","B1N6FY ","B1N6M ","B1N6MM ", & - "B1N6PHI ","B1N6RE ","B1N6SGCAV","B1N6SIGCR","B1N6STVX ","B1N6STVY ","B1N6STVZ ", & - "B1N6THETA","B1N6TNIND","B1N6VDISX","B1N6VDISY","B1N6VDISZ","B1N6VINDX","B1N6VINDY", & - "B1N6VREL ","B1N6VUNDX","B1N6VUNDY","B1N6VUNDZ","B1N7ALPHA","B1N7AXIND","B1N7CD ", & - "B1N7CL ","B1N7CLRNC","B1N7CM ","B1N7CN ","B1N7CPMIN","B1N7CT ","B1N7CURVE", & - "B1N7CX ","B1N7CY ","B1N7DYNP ","B1N7FD ","B1N7FL ","B1N7FN ","B1N7FT ", & - "B1N7FX ","B1N7FY ","B1N7M ","B1N7MM ","B1N7PHI ","B1N7RE ","B1N7SGCAV", & + "B1N1FD ","B1N1FL ","B1N1FN ","B1N1FT ","B1N1FX ","B1N1FY ","B1N1GAM ", & + "B1N1M ","B1N1MM ","B1N1PHI ","B1N1RE ","B1N1SGCAV","B1N1SIGCR","B1N1STVX ", & + "B1N1STVY ","B1N1STVZ ","B1N1THETA","B1N1TNIND","B1N1VDISX","B1N1VDISY","B1N1VDISZ", & + "B1N1VINDX","B1N1VINDY","B1N1VREL ","B1N1VUNDX","B1N1VUNDY","B1N1VUNDZ","B1N2ALPHA", & + "B1N2AXIND","B1N2CD ","B1N2CL ","B1N2CLRNC","B1N2CM ","B1N2CN ","B1N2CPMIN", & + "B1N2CT ","B1N2CURVE","B1N2CX ","B1N2CY ","B1N2DYNP ","B1N2FD ","B1N2FL ", & + "B1N2FN ","B1N2FT ","B1N2FX ","B1N2FY ","B1N2GAM ","B1N2M ","B1N2MM ", & + "B1N2PHI ","B1N2RE ","B1N2SGCAV","B1N2SIGCR","B1N2STVX ","B1N2STVY ","B1N2STVZ ", & + "B1N2THETA","B1N2TNIND","B1N2VDISX","B1N2VDISY","B1N2VDISZ","B1N2VINDX","B1N2VINDY", & + "B1N2VREL ","B1N2VUNDX","B1N2VUNDY","B1N2VUNDZ","B1N3ALPHA","B1N3AXIND","B1N3CD ", & + "B1N3CL ","B1N3CLRNC","B1N3CM ","B1N3CN ","B1N3CPMIN","B1N3CT ","B1N3CURVE", & + "B1N3CX ","B1N3CY ","B1N3DYNP ","B1N3FD ","B1N3FL ","B1N3FN ","B1N3FT ", & + "B1N3FX ","B1N3FY ","B1N3GAM ","B1N3M ","B1N3MM ","B1N3PHI ","B1N3RE ", & + "B1N3SGCAV","B1N3SIGCR","B1N3STVX ","B1N3STVY ","B1N3STVZ ","B1N3THETA","B1N3TNIND", & + "B1N3VDISX","B1N3VDISY","B1N3VDISZ","B1N3VINDX","B1N3VINDY","B1N3VREL ","B1N3VUNDX", & + "B1N3VUNDY","B1N3VUNDZ","B1N4ALPHA","B1N4AXIND","B1N4CD ","B1N4CL ","B1N4CLRNC", & + "B1N4CM ","B1N4CN ","B1N4CPMIN","B1N4CT ","B1N4CURVE","B1N4CX ","B1N4CY ", & + "B1N4DYNP ","B1N4FD ","B1N4FL ","B1N4FN ","B1N4FT ","B1N4FX ","B1N4FY ", & + "B1N4GAM ","B1N4M ","B1N4MM ","B1N4PHI ","B1N4RE ","B1N4SGCAV","B1N4SIGCR", & + "B1N4STVX ","B1N4STVY ","B1N4STVZ ","B1N4THETA","B1N4TNIND","B1N4VDISX","B1N4VDISY", & + "B1N4VDISZ","B1N4VINDX","B1N4VINDY","B1N4VREL ","B1N4VUNDX","B1N4VUNDY","B1N4VUNDZ", & + "B1N5ALPHA","B1N5AXIND","B1N5CD ","B1N5CL ","B1N5CLRNC","B1N5CM ","B1N5CN ", & + "B1N5CPMIN","B1N5CT ","B1N5CURVE","B1N5CX ","B1N5CY ","B1N5DYNP ","B1N5FD ", & + "B1N5FL ","B1N5FN ","B1N5FT ","B1N5FX ","B1N5FY ","B1N5GAM ","B1N5M ", & + "B1N5MM ","B1N5PHI ","B1N5RE ","B1N5SGCAV","B1N5SIGCR","B1N5STVX ","B1N5STVY ", & + "B1N5STVZ ","B1N5THETA","B1N5TNIND","B1N5VDISX","B1N5VDISY","B1N5VDISZ","B1N5VINDX", & + "B1N5VINDY","B1N5VREL ","B1N5VUNDX","B1N5VUNDY","B1N5VUNDZ","B1N6ALPHA","B1N6AXIND", & + "B1N6CD ","B1N6CL ","B1N6CLRNC","B1N6CM ","B1N6CN ","B1N6CPMIN","B1N6CT ", & + "B1N6CURVE","B1N6CX ","B1N6CY ","B1N6DYNP ","B1N6FD ","B1N6FL ","B1N6FN ", & + "B1N6FT ","B1N6FX ","B1N6FY ","B1N6GAM ","B1N6M ","B1N6MM ","B1N6PHI ", & + "B1N6RE ","B1N6SGCAV","B1N6SIGCR","B1N6STVX ","B1N6STVY ","B1N6STVZ ","B1N6THETA", & + "B1N6TNIND","B1N6VDISX","B1N6VDISY","B1N6VDISZ","B1N6VINDX","B1N6VINDY","B1N6VREL ", & + "B1N6VUNDX","B1N6VUNDY","B1N6VUNDZ","B1N7ALPHA","B1N7AXIND","B1N7CD ","B1N7CL ", & + "B1N7CLRNC","B1N7CM ","B1N7CN ","B1N7CPMIN","B1N7CT ","B1N7CURVE","B1N7CX ", & + "B1N7CY ","B1N7DYNP ","B1N7FD ","B1N7FL ","B1N7FN ","B1N7FT ","B1N7FX ", & + "B1N7FY ","B1N7GAM ","B1N7M ","B1N7MM ","B1N7PHI ","B1N7RE ","B1N7SGCAV", & "B1N7SIGCR","B1N7STVX ","B1N7STVY ","B1N7STVZ ","B1N7THETA","B1N7TNIND","B1N7VDISX", & "B1N7VDISY","B1N7VDISZ","B1N7VINDX","B1N7VINDY","B1N7VREL ","B1N7VUNDX","B1N7VUNDY", & "B1N7VUNDZ","B1N8ALPHA","B1N8AXIND","B1N8CD ","B1N8CL ","B1N8CLRNC","B1N8CM ", & "B1N8CN ","B1N8CPMIN","B1N8CT ","B1N8CURVE","B1N8CX ","B1N8CY ","B1N8DYNP ", & - "B1N8FD ","B1N8FL ","B1N8FN ","B1N8FT ","B1N8FX ","B1N8FY ","B1N8M ", & - "B1N8MM ","B1N8PHI ","B1N8RE ","B1N8SGCAV","B1N8SIGCR","B1N8STVX ","B1N8STVY ", & - "B1N8STVZ ","B1N8THETA","B1N8TNIND","B1N8VDISX","B1N8VDISY","B1N8VDISZ","B1N8VINDX", & - "B1N8VINDY","B1N8VREL ","B1N8VUNDX","B1N8VUNDY","B1N8VUNDZ","B1N9ALPHA","B1N9AXIND", & - "B1N9CD ","B1N9CL ","B1N9CLRNC","B1N9CM ","B1N9CN ","B1N9CPMIN","B1N9CT ", & - "B1N9CURVE","B1N9CX ","B1N9CY ","B1N9DYNP ","B1N9FD ","B1N9FL ","B1N9FN ", & - "B1N9FT ","B1N9FX ","B1N9FY ","B1N9M ","B1N9MM ","B1N9PHI ","B1N9RE ", & - "B1N9SGCAV","B1N9SIGCR","B1N9STVX ","B1N9STVY ","B1N9STVZ ","B1N9THETA","B1N9TNIND", & - "B1N9VDISX","B1N9VDISY","B1N9VDISZ","B1N9VINDX","B1N9VINDY","B1N9VREL ","B1N9VUNDX", & - "B1N9VUNDY","B1N9VUNDZ","B1PITCH ","B2AZIMUTH","B2N1ALPHA","B2N1AXIND","B2N1CD ", & - "B2N1CL ","B2N1CLRNC","B2N1CM ","B2N1CN ","B2N1CPMIN","B2N1CT ","B2N1CURVE", & - "B2N1CX ","B2N1CY ","B2N1DYNP ","B2N1FD ","B2N1FL ","B2N1FN ","B2N1FT ", & - "B2N1FX ","B2N1FY ","B2N1M ","B2N1MM ","B2N1PHI ","B2N1RE ","B2N1SGCAV", & - "B2N1SIGCR","B2N1STVX ","B2N1STVY ","B2N1STVZ ","B2N1THETA","B2N1TNIND","B2N1VDISX", & - "B2N1VDISY","B2N1VDISZ","B2N1VINDX","B2N1VINDY","B2N1VREL ","B2N1VUNDX","B2N1VUNDY", & - "B2N1VUNDZ","B2N2ALPHA","B2N2AXIND","B2N2CD ","B2N2CL ","B2N2CLRNC","B2N2CM ", & - "B2N2CN ","B2N2CPMIN","B2N2CT ","B2N2CURVE","B2N2CX ","B2N2CY ","B2N2DYNP ", & - "B2N2FD ","B2N2FL ","B2N2FN ","B2N2FT ","B2N2FX ","B2N2FY ","B2N2M ", & - "B2N2MM ","B2N2PHI ","B2N2RE ","B2N2SGCAV","B2N2SIGCR","B2N2STVX ","B2N2STVY ", & - "B2N2STVZ ","B2N2THETA","B2N2TNIND","B2N2VDISX","B2N2VDISY","B2N2VDISZ","B2N2VINDX", & - "B2N2VINDY","B2N2VREL ","B2N2VUNDX","B2N2VUNDY","B2N2VUNDZ","B2N3ALPHA","B2N3AXIND", & - "B2N3CD ","B2N3CL ","B2N3CLRNC","B2N3CM ","B2N3CN ","B2N3CPMIN","B2N3CT ", & - "B2N3CURVE","B2N3CX ","B2N3CY ","B2N3DYNP ","B2N3FD ","B2N3FL ","B2N3FN ", & - "B2N3FT ","B2N3FX ","B2N3FY ","B2N3M ","B2N3MM ","B2N3PHI ","B2N3RE ", & - "B2N3SGCAV","B2N3SIGCR","B2N3STVX ","B2N3STVY ","B2N3STVZ ","B2N3THETA","B2N3TNIND", & - "B2N3VDISX","B2N3VDISY","B2N3VDISZ","B2N3VINDX","B2N3VINDY","B2N3VREL ","B2N3VUNDX", & - "B2N3VUNDY","B2N3VUNDZ","B2N4ALPHA","B2N4AXIND","B2N4CD ","B2N4CL ","B2N4CLRNC", & - "B2N4CM ","B2N4CN ","B2N4CPMIN","B2N4CT ","B2N4CURVE","B2N4CX ","B2N4CY ", & - "B2N4DYNP ","B2N4FD ","B2N4FL ","B2N4FN ","B2N4FT ","B2N4FX ","B2N4FY ", & - "B2N4M ","B2N4MM ","B2N4PHI ","B2N4RE ","B2N4SGCAV","B2N4SIGCR","B2N4STVX ", & - "B2N4STVY ","B2N4STVZ ","B2N4THETA","B2N4TNIND","B2N4VDISX","B2N4VDISY","B2N4VDISZ", & - "B2N4VINDX","B2N4VINDY","B2N4VREL ","B2N4VUNDX","B2N4VUNDY","B2N4VUNDZ","B2N5ALPHA", & - "B2N5AXIND","B2N5CD ","B2N5CL ","B2N5CLRNC","B2N5CM ","B2N5CN ","B2N5CPMIN", & - "B2N5CT ","B2N5CURVE","B2N5CX ","B2N5CY ","B2N5DYNP ","B2N5FD ","B2N5FL ", & - "B2N5FN ","B2N5FT ","B2N5FX ","B2N5FY ","B2N5M ","B2N5MM ","B2N5PHI ", & + "B1N8FD ","B1N8FL ","B1N8FN ","B1N8FT ","B1N8FX ","B1N8FY ","B1N8GAM ", & + "B1N8M ","B1N8MM ","B1N8PHI ","B1N8RE ","B1N8SGCAV","B1N8SIGCR","B1N8STVX ", & + "B1N8STVY ","B1N8STVZ ","B1N8THETA","B1N8TNIND","B1N8VDISX","B1N8VDISY","B1N8VDISZ", & + "B1N8VINDX","B1N8VINDY","B1N8VREL ","B1N8VUNDX","B1N8VUNDY","B1N8VUNDZ","B1N9ALPHA", & + "B1N9AXIND","B1N9CD ","B1N9CL ","B1N9CLRNC","B1N9CM ","B1N9CN ","B1N9CPMIN", & + "B1N9CT ","B1N9CURVE","B1N9CX ","B1N9CY ","B1N9DYNP ","B1N9FD ","B1N9FL ", & + "B1N9FN ","B1N9FT ","B1N9FX ","B1N9FY ","B1N9GAM ","B1N9M ","B1N9MM ", & + "B1N9PHI ","B1N9RE ","B1N9SGCAV","B1N9SIGCR","B1N9STVX ","B1N9STVY ","B1N9STVZ ", & + "B1N9THETA","B1N9TNIND","B1N9VDISX","B1N9VDISY","B1N9VDISZ","B1N9VINDX","B1N9VINDY", & + "B1N9VREL ","B1N9VUNDX","B1N9VUNDY","B1N9VUNDZ","B1PITCH ","B2AZIMUTH","B2N1ALPHA", & + "B2N1AXIND","B2N1CD ","B2N1CL ","B2N1CLRNC","B2N1CM ","B2N1CN ","B2N1CPMIN", & + "B2N1CT ","B2N1CURVE","B2N1CX ","B2N1CY ","B2N1DYNP ","B2N1FD ","B2N1FL ", & + "B2N1FN ","B2N1FT ","B2N1FX ","B2N1FY ","B2N1GAM ","B2N1M ","B2N1MM ", & + "B2N1PHI ","B2N1RE ","B2N1SGCAV","B2N1SIGCR","B2N1STVX ","B2N1STVY ","B2N1STVZ ", & + "B2N1THETA","B2N1TNIND","B2N1VDISX","B2N1VDISY","B2N1VDISZ","B2N1VINDX","B2N1VINDY", & + "B2N1VREL ","B2N1VUNDX","B2N1VUNDY","B2N1VUNDZ","B2N2ALPHA","B2N2AXIND","B2N2CD ", & + "B2N2CL ","B2N2CLRNC","B2N2CM ","B2N2CN ","B2N2CPMIN","B2N2CT ","B2N2CURVE", & + "B2N2CX ","B2N2CY ","B2N2DYNP ","B2N2FD ","B2N2FL ","B2N2FN ","B2N2FT ", & + "B2N2FX ","B2N2FY ","B2N2GAM ","B2N2M ","B2N2MM ","B2N2PHI ","B2N2RE ", & + "B2N2SGCAV","B2N2SIGCR","B2N2STVX ","B2N2STVY ","B2N2STVZ ","B2N2THETA","B2N2TNIND", & + "B2N2VDISX","B2N2VDISY","B2N2VDISZ","B2N2VINDX","B2N2VINDY","B2N2VREL ","B2N2VUNDX", & + "B2N2VUNDY","B2N2VUNDZ","B2N3ALPHA","B2N3AXIND","B2N3CD ","B2N3CL ","B2N3CLRNC", & + "B2N3CM ","B2N3CN ","B2N3CPMIN","B2N3CT ","B2N3CURVE","B2N3CX ","B2N3CY ", & + "B2N3DYNP ","B2N3FD ","B2N3FL ","B2N3FN ","B2N3FT ","B2N3FX ","B2N3FY ", & + "B2N3GAM ","B2N3M ","B2N3MM ","B2N3PHI ","B2N3RE ","B2N3SGCAV","B2N3SIGCR", & + "B2N3STVX ","B2N3STVY ","B2N3STVZ ","B2N3THETA","B2N3TNIND","B2N3VDISX","B2N3VDISY", & + "B2N3VDISZ","B2N3VINDX","B2N3VINDY","B2N3VREL ","B2N3VUNDX","B2N3VUNDY","B2N3VUNDZ", & + "B2N4ALPHA","B2N4AXIND","B2N4CD ","B2N4CL ","B2N4CLRNC","B2N4CM ","B2N4CN ", & + "B2N4CPMIN","B2N4CT ","B2N4CURVE","B2N4CX ","B2N4CY ","B2N4DYNP ","B2N4FD ", & + "B2N4FL ","B2N4FN ","B2N4FT ","B2N4FX ","B2N4FY ","B2N4GAM ","B2N4M ", & + "B2N4MM ","B2N4PHI ","B2N4RE ","B2N4SGCAV","B2N4SIGCR","B2N4STVX ","B2N4STVY ", & + "B2N4STVZ ","B2N4THETA","B2N4TNIND","B2N4VDISX","B2N4VDISY","B2N4VDISZ","B2N4VINDX", & + "B2N4VINDY","B2N4VREL ","B2N4VUNDX","B2N4VUNDY","B2N4VUNDZ","B2N5ALPHA","B2N5AXIND", & + "B2N5CD ","B2N5CL ","B2N5CLRNC","B2N5CM ","B2N5CN ","B2N5CPMIN","B2N5CT ", & + "B2N5CURVE","B2N5CX ","B2N5CY ","B2N5DYNP ","B2N5FD ","B2N5FL ","B2N5FN ", & + "B2N5FT ","B2N5FX ","B2N5FY ","B2N5GAM ","B2N5M ","B2N5MM ","B2N5PHI ", & "B2N5RE ","B2N5SGCAV","B2N5SIGCR","B2N5STVX ","B2N5STVY ","B2N5STVZ ","B2N5THETA", & "B2N5TNIND","B2N5VDISX","B2N5VDISY","B2N5VDISZ","B2N5VINDX","B2N5VINDY","B2N5VREL ", & "B2N5VUNDX","B2N5VUNDY","B2N5VUNDZ","B2N6ALPHA","B2N6AXIND","B2N6CD ","B2N6CL ", & "B2N6CLRNC","B2N6CM ","B2N6CN ","B2N6CPMIN","B2N6CT ","B2N6CURVE","B2N6CX ", & "B2N6CY ","B2N6DYNP ","B2N6FD ","B2N6FL ","B2N6FN ","B2N6FT ","B2N6FX ", & - "B2N6FY ","B2N6M ","B2N6MM ","B2N6PHI ","B2N6RE ","B2N6SGCAV","B2N6SIGCR", & - "B2N6STVX ","B2N6STVY ","B2N6STVZ ","B2N6THETA","B2N6TNIND","B2N6VDISX","B2N6VDISY", & - "B2N6VDISZ","B2N6VINDX","B2N6VINDY","B2N6VREL ","B2N6VUNDX","B2N6VUNDY","B2N6VUNDZ", & - "B2N7ALPHA","B2N7AXIND","B2N7CD ","B2N7CL ","B2N7CLRNC","B2N7CM ","B2N7CN ", & - "B2N7CPMIN","B2N7CT ","B2N7CURVE","B2N7CX ","B2N7CY ","B2N7DYNP ","B2N7FD ", & - "B2N7FL ","B2N7FN ","B2N7FT ","B2N7FX ","B2N7FY ","B2N7M ","B2N7MM ", & - "B2N7PHI ","B2N7RE ","B2N7SGCAV","B2N7SIGCR","B2N7STVX ","B2N7STVY ","B2N7STVZ ", & - "B2N7THETA","B2N7TNIND","B2N7VDISX","B2N7VDISY","B2N7VDISZ","B2N7VINDX","B2N7VINDY", & - "B2N7VREL ","B2N7VUNDX","B2N7VUNDY","B2N7VUNDZ","B2N8ALPHA","B2N8AXIND","B2N8CD ", & - "B2N8CL ","B2N8CLRNC","B2N8CM ","B2N8CN ","B2N8CPMIN","B2N8CT ","B2N8CURVE", & - "B2N8CX ","B2N8CY ","B2N8DYNP ","B2N8FD ","B2N8FL ","B2N8FN ","B2N8FT ", & - "B2N8FX ","B2N8FY ","B2N8M ","B2N8MM ","B2N8PHI ","B2N8RE ","B2N8SGCAV", & - "B2N8SIGCR","B2N8STVX ","B2N8STVY ","B2N8STVZ ","B2N8THETA","B2N8TNIND","B2N8VDISX", & - "B2N8VDISY","B2N8VDISZ","B2N8VINDX","B2N8VINDY","B2N8VREL ","B2N8VUNDX","B2N8VUNDY", & - "B2N8VUNDZ","B2N9ALPHA","B2N9AXIND","B2N9CD ","B2N9CL ","B2N9CLRNC","B2N9CM ", & - "B2N9CN ","B2N9CPMIN","B2N9CT ","B2N9CURVE","B2N9CX ","B2N9CY ","B2N9DYNP ", & - "B2N9FD ","B2N9FL ","B2N9FN ","B2N9FT ","B2N9FX ","B2N9FY ","B2N9M ", & - "B2N9MM ","B2N9PHI ","B2N9RE ","B2N9SGCAV","B2N9SIGCR","B2N9STVX ","B2N9STVY ", & - "B2N9STVZ ","B2N9THETA","B2N9TNIND","B2N9VDISX","B2N9VDISY","B2N9VDISZ","B2N9VINDX", & - "B2N9VINDY","B2N9VREL ","B2N9VUNDX","B2N9VUNDY","B2N9VUNDZ","B2PITCH ","B3AZIMUTH", & - "B3N1ALPHA","B3N1AXIND","B3N1CD ","B3N1CL ","B3N1CLRNC","B3N1CM ","B3N1CN ", & - "B3N1CPMIN","B3N1CT ","B3N1CURVE","B3N1CX ","B3N1CY ","B3N1DYNP ","B3N1FD ", & - "B3N1FL ","B3N1FN ","B3N1FT ","B3N1FX ","B3N1FY ","B3N1M ","B3N1MM ", & - "B3N1PHI ","B3N1RE ","B3N1SGCAV","B3N1SIGCR","B3N1STVX ","B3N1STVY ","B3N1STVZ ", & - "B3N1THETA","B3N1TNIND","B3N1VDISX","B3N1VDISY","B3N1VDISZ","B3N1VINDX","B3N1VINDY", & - "B3N1VREL ","B3N1VUNDX","B3N1VUNDY","B3N1VUNDZ","B3N2ALPHA","B3N2AXIND","B3N2CD ", & - "B3N2CL ","B3N2CLRNC","B3N2CM ","B3N2CN ","B3N2CPMIN","B3N2CT ","B3N2CURVE", & - "B3N2CX ","B3N2CY ","B3N2DYNP ","B3N2FD ","B3N2FL ","B3N2FN ","B3N2FT ", & - "B3N2FX ","B3N2FY ","B3N2M ","B3N2MM ","B3N2PHI ","B3N2RE ","B3N2SGCAV", & - "B3N2SIGCR","B3N2STVX ","B3N2STVY ","B3N2STVZ ","B3N2THETA","B3N2TNIND","B3N2VDISX", & - "B3N2VDISY","B3N2VDISZ","B3N2VINDX","B3N2VINDY","B3N2VREL ","B3N2VUNDX","B3N2VUNDY", & - "B3N2VUNDZ","B3N3ALPHA","B3N3AXIND","B3N3CD ","B3N3CL ","B3N3CLRNC","B3N3CM ", & - "B3N3CN ","B3N3CPMIN","B3N3CT ","B3N3CURVE","B3N3CX ","B3N3CY ","B3N3DYNP ", & - "B3N3FD ","B3N3FL ","B3N3FN ","B3N3FT ","B3N3FX ","B3N3FY ","B3N3M ", & + "B2N6FY ","B2N6GAM ","B2N6M ","B2N6MM ","B2N6PHI ","B2N6RE ","B2N6SGCAV", & + "B2N6SIGCR","B2N6STVX ","B2N6STVY ","B2N6STVZ ","B2N6THETA","B2N6TNIND","B2N6VDISX", & + "B2N6VDISY","B2N6VDISZ","B2N6VINDX","B2N6VINDY","B2N6VREL ","B2N6VUNDX","B2N6VUNDY", & + "B2N6VUNDZ","B2N7ALPHA","B2N7AXIND","B2N7CD ","B2N7CL ","B2N7CLRNC","B2N7CM ", & + "B2N7CN ","B2N7CPMIN","B2N7CT ","B2N7CURVE","B2N7CX ","B2N7CY ","B2N7DYNP ", & + "B2N7FD ","B2N7FL ","B2N7FN ","B2N7FT ","B2N7FX ","B2N7FY ","B2N7GAM ", & + "B2N7M ","B2N7MM ","B2N7PHI ","B2N7RE ","B2N7SGCAV","B2N7SIGCR","B2N7STVX ", & + "B2N7STVY ","B2N7STVZ ","B2N7THETA","B2N7TNIND","B2N7VDISX","B2N7VDISY","B2N7VDISZ", & + "B2N7VINDX","B2N7VINDY","B2N7VREL ","B2N7VUNDX","B2N7VUNDY","B2N7VUNDZ","B2N8ALPHA", & + "B2N8AXIND","B2N8CD ","B2N8CL ","B2N8CLRNC","B2N8CM ","B2N8CN ","B2N8CPMIN", & + "B2N8CT ","B2N8CURVE","B2N8CX ","B2N8CY ","B2N8DYNP ","B2N8FD ","B2N8FL ", & + "B2N8FN ","B2N8FT ","B2N8FX ","B2N8FY ","B2N8GAM ","B2N8M ","B2N8MM ", & + "B2N8PHI ","B2N8RE ","B2N8SGCAV","B2N8SIGCR","B2N8STVX ","B2N8STVY ","B2N8STVZ ", & + "B2N8THETA","B2N8TNIND","B2N8VDISX","B2N8VDISY","B2N8VDISZ","B2N8VINDX","B2N8VINDY", & + "B2N8VREL ","B2N8VUNDX","B2N8VUNDY","B2N8VUNDZ","B2N9ALPHA","B2N9AXIND","B2N9CD ", & + "B2N9CL ","B2N9CLRNC","B2N9CM ","B2N9CN ","B2N9CPMIN","B2N9CT ","B2N9CURVE", & + "B2N9CX ","B2N9CY ","B2N9DYNP ","B2N9FD ","B2N9FL ","B2N9FN ","B2N9FT ", & + "B2N9FX ","B2N9FY ","B2N9GAM ","B2N9M ","B2N9MM ","B2N9PHI ","B2N9RE ", & + "B2N9SGCAV","B2N9SIGCR","B2N9STVX ","B2N9STVY ","B2N9STVZ ","B2N9THETA","B2N9TNIND", & + "B2N9VDISX","B2N9VDISY","B2N9VDISZ","B2N9VINDX","B2N9VINDY","B2N9VREL ","B2N9VUNDX", & + "B2N9VUNDY","B2N9VUNDZ","B2PITCH ","B3AZIMUTH","B3N1ALPHA","B3N1AXIND","B3N1CD ", & + "B3N1CL ","B3N1CLRNC","B3N1CM ","B3N1CN ","B3N1CPMIN","B3N1CT ","B3N1CURVE", & + "B3N1CX ","B3N1CY ","B3N1DYNP ","B3N1FD ","B3N1FL ","B3N1FN ","B3N1FT ", & + "B3N1FX ","B3N1FY ","B3N1GAM ","B3N1M ","B3N1MM ","B3N1PHI ","B3N1RE ", & + "B3N1SGCAV","B3N1SIGCR","B3N1STVX ","B3N1STVY ","B3N1STVZ ","B3N1THETA","B3N1TNIND", & + "B3N1VDISX","B3N1VDISY","B3N1VDISZ","B3N1VINDX","B3N1VINDY","B3N1VREL ","B3N1VUNDX", & + "B3N1VUNDY","B3N1VUNDZ","B3N2ALPHA","B3N2AXIND","B3N2CD ","B3N2CL ","B3N2CLRNC", & + "B3N2CM ","B3N2CN ","B3N2CPMIN","B3N2CT ","B3N2CURVE","B3N2CX ","B3N2CY ", & + "B3N2DYNP ","B3N2FD ","B3N2FL ","B3N2FN ","B3N2FT ","B3N2FX ","B3N2FY ", & + "B3N2GAM ","B3N2M ","B3N2MM ","B3N2PHI ","B3N2RE ","B3N2SGCAV","B3N2SIGCR", & + "B3N2STVX ","B3N2STVY ","B3N2STVZ ","B3N2THETA","B3N2TNIND","B3N2VDISX","B3N2VDISY", & + "B3N2VDISZ","B3N2VINDX","B3N2VINDY","B3N2VREL ","B3N2VUNDX","B3N2VUNDY","B3N2VUNDZ", & + "B3N3ALPHA","B3N3AXIND","B3N3CD ","B3N3CL ","B3N3CLRNC","B3N3CM ","B3N3CN ", & + "B3N3CPMIN","B3N3CT ","B3N3CURVE","B3N3CX ","B3N3CY ","B3N3DYNP ","B3N3FD ", & + "B3N3FL ","B3N3FN ","B3N3FT ","B3N3FX ","B3N3FY ","B3N3GAM ","B3N3M ", & "B3N3MM ","B3N3PHI ","B3N3RE ","B3N3SGCAV","B3N3SIGCR","B3N3STVX ","B3N3STVY ", & "B3N3STVZ ","B3N3THETA","B3N3TNIND","B3N3VDISX","B3N3VDISY","B3N3VDISZ","B3N3VINDX", & "B3N3VINDY","B3N3VREL ","B3N3VUNDX","B3N3VUNDY","B3N3VUNDZ","B3N4ALPHA","B3N4AXIND", & "B3N4CD ","B3N4CL ","B3N4CLRNC","B3N4CM ","B3N4CN ","B3N4CPMIN","B3N4CT ", & "B3N4CURVE","B3N4CX ","B3N4CY ","B3N4DYNP ","B3N4FD ","B3N4FL ","B3N4FN ", & - "B3N4FT ","B3N4FX ","B3N4FY ","B3N4M ","B3N4MM ","B3N4PHI ","B3N4RE ", & - "B3N4SGCAV","B3N4SIGCR","B3N4STVX ","B3N4STVY ","B3N4STVZ ","B3N4THETA","B3N4TNIND", & - "B3N4VDISX","B3N4VDISY","B3N4VDISZ","B3N4VINDX","B3N4VINDY","B3N4VREL ","B3N4VUNDX", & - "B3N4VUNDY","B3N4VUNDZ","B3N5ALPHA","B3N5AXIND","B3N5CD ","B3N5CL ","B3N5CLRNC", & - "B3N5CM ","B3N5CN ","B3N5CPMIN","B3N5CT ","B3N5CURVE","B3N5CX ","B3N5CY ", & - "B3N5DYNP ","B3N5FD ","B3N5FL ","B3N5FN ","B3N5FT ","B3N5FX ","B3N5FY ", & - "B3N5M ","B3N5MM ","B3N5PHI ","B3N5RE ","B3N5SGCAV","B3N5SIGCR","B3N5STVX ", & - "B3N5STVY ","B3N5STVZ ","B3N5THETA","B3N5TNIND","B3N5VDISX","B3N5VDISY","B3N5VDISZ", & - "B3N5VINDX","B3N5VINDY","B3N5VREL ","B3N5VUNDX","B3N5VUNDY","B3N5VUNDZ","B3N6ALPHA", & - "B3N6AXIND","B3N6CD ","B3N6CL ","B3N6CLRNC","B3N6CM ","B3N6CN ","B3N6CPMIN", & - "B3N6CT ","B3N6CURVE","B3N6CX ","B3N6CY ","B3N6DYNP ","B3N6FD ","B3N6FL ", & - "B3N6FN ","B3N6FT ","B3N6FX ","B3N6FY ","B3N6M ","B3N6MM ","B3N6PHI ", & - "B3N6RE ","B3N6SGCAV","B3N6SIGCR","B3N6STVX ","B3N6STVY ","B3N6STVZ ","B3N6THETA", & - "B3N6TNIND","B3N6VDISX","B3N6VDISY","B3N6VDISZ","B3N6VINDX","B3N6VINDY","B3N6VREL ", & - "B3N6VUNDX","B3N6VUNDY","B3N6VUNDZ","B3N7ALPHA","B3N7AXIND","B3N7CD ","B3N7CL ", & - "B3N7CLRNC","B3N7CM ","B3N7CN ","B3N7CPMIN","B3N7CT ","B3N7CURVE","B3N7CX ", & - "B3N7CY ","B3N7DYNP ","B3N7FD ","B3N7FL ","B3N7FN ","B3N7FT ","B3N7FX ", & - "B3N7FY ","B3N7M ","B3N7MM ","B3N7PHI ","B3N7RE ","B3N7SGCAV","B3N7SIGCR", & - "B3N7STVX ","B3N7STVY ","B3N7STVZ ","B3N7THETA","B3N7TNIND","B3N7VDISX","B3N7VDISY", & - "B3N7VDISZ","B3N7VINDX","B3N7VINDY","B3N7VREL ","B3N7VUNDX","B3N7VUNDY","B3N7VUNDZ", & - "B3N8ALPHA","B3N8AXIND","B3N8CD ","B3N8CL ","B3N8CLRNC","B3N8CM ","B3N8CN ", & - "B3N8CPMIN","B3N8CT ","B3N8CURVE","B3N8CX ","B3N8CY ","B3N8DYNP ","B3N8FD ", & - "B3N8FL ","B3N8FN ","B3N8FT ","B3N8FX ","B3N8FY ","B3N8M ","B3N8MM ", & - "B3N8PHI ","B3N8RE ","B3N8SGCAV","B3N8SIGCR","B3N8STVX ","B3N8STVY ","B3N8STVZ ", & - "B3N8THETA","B3N8TNIND","B3N8VDISX","B3N8VDISY","B3N8VDISZ","B3N8VINDX","B3N8VINDY", & - "B3N8VREL ","B3N8VUNDX","B3N8VUNDY","B3N8VUNDZ","B3N9ALPHA","B3N9AXIND","B3N9CD ", & - "B3N9CL ","B3N9CLRNC","B3N9CM ","B3N9CN ","B3N9CPMIN","B3N9CT ","B3N9CURVE", & - "B3N9CX ","B3N9CY ","B3N9DYNP ","B3N9FD ","B3N9FL ","B3N9FN ","B3N9FT ", & - "B3N9FX ","B3N9FY ","B3N9M ","B3N9MM ","B3N9PHI ","B3N9RE ","B3N9SGCAV", & - "B3N9SIGCR","B3N9STVX ","B3N9STVY ","B3N9STVZ ","B3N9THETA","B3N9TNIND","B3N9VDISX", & - "B3N9VDISY","B3N9VDISZ","B3N9VINDX","B3N9VINDY","B3N9VREL ","B3N9VUNDX","B3N9VUNDY", & - "B3N9VUNDZ","B3PITCH ","RTAEROCP ","RTAEROCQ ","RTAEROCT ","RTAEROFXH","RTAEROFYH", & - "RTAEROFZH","RTAEROMXH","RTAEROMYH","RTAEROMZH","RTAEROPWR","RTAREA ","RTSKEW ", & - "RTSPEED ","RTTSR ","RTVAVGXH ","RTVAVGYH ","RTVAVGZH ","TWN1DYNP ","TWN1FDX ", & - "TWN1FDY ","TWN1M ","TWN1RE ","TWN1STVX ","TWN1STVY ","TWN1STVZ ","TWN1VREL ", & - "TWN1VUNDX","TWN1VUNDY","TWN1VUNDZ","TWN2DYNP ","TWN2FDX ","TWN2FDY ","TWN2M ", & - "TWN2RE ","TWN2STVX ","TWN2STVY ","TWN2STVZ ","TWN2VREL ","TWN2VUNDX","TWN2VUNDY", & - "TWN2VUNDZ","TWN3DYNP ","TWN3FDX ","TWN3FDY ","TWN3M ","TWN3RE ","TWN3STVX ", & - "TWN3STVY ","TWN3STVZ ","TWN3VREL ","TWN3VUNDX","TWN3VUNDY","TWN3VUNDZ","TWN4DYNP ", & - "TWN4FDX ","TWN4FDY ","TWN4M ","TWN4RE ","TWN4STVX ","TWN4STVY ","TWN4STVZ ", & - "TWN4VREL ","TWN4VUNDX","TWN4VUNDY","TWN4VUNDZ","TWN5DYNP ","TWN5FDX ","TWN5FDY ", & - "TWN5M ","TWN5RE ","TWN5STVX ","TWN5STVY ","TWN5STVZ ","TWN5VREL ","TWN5VUNDX", & - "TWN5VUNDY","TWN5VUNDZ","TWN6DYNP ","TWN6FDX ","TWN6FDY ","TWN6M ","TWN6RE ", & - "TWN6STVX ","TWN6STVY ","TWN6STVZ ","TWN6VREL ","TWN6VUNDX","TWN6VUNDY","TWN6VUNDZ", & - "TWN7DYNP ","TWN7FDX ","TWN7FDY ","TWN7M ","TWN7RE ","TWN7STVX ","TWN7STVY ", & - "TWN7STVZ ","TWN7VREL ","TWN7VUNDX","TWN7VUNDY","TWN7VUNDZ","TWN8DYNP ","TWN8FDX ", & - "TWN8FDY ","TWN8M ","TWN8RE ","TWN8STVX ","TWN8STVY ","TWN8STVZ ","TWN8VREL ", & - "TWN8VUNDX","TWN8VUNDY","TWN8VUNDZ","TWN9DYNP ","TWN9FDX ","TWN9FDY ","TWN9M ", & - "TWN9RE ","TWN9STVX ","TWN9STVY ","TWN9STVZ ","TWN9VREL ","TWN9VUNDX","TWN9VUNDY", & - "TWN9VUNDZ"/) - INTEGER(IntKi), PARAMETER :: ParamIndxAry(1184) = (/ & ! This lists the index into AllOuts(:) of the allowed parameters ValidParamAry(:) + "B3N4FT ","B3N4FX ","B3N4FY ","B3N4GAM ","B3N4M ","B3N4MM ","B3N4PHI ", & + "B3N4RE ","B3N4SGCAV","B3N4SIGCR","B3N4STVX ","B3N4STVY ","B3N4STVZ ","B3N4THETA", & + "B3N4TNIND","B3N4VDISX","B3N4VDISY","B3N4VDISZ","B3N4VINDX","B3N4VINDY","B3N4VREL ", & + "B3N4VUNDX","B3N4VUNDY","B3N4VUNDZ","B3N5ALPHA","B3N5AXIND","B3N5CD ","B3N5CL ", & + "B3N5CLRNC","B3N5CM ","B3N5CN ","B3N5CPMIN","B3N5CT ","B3N5CURVE","B3N5CX ", & + "B3N5CY ","B3N5DYNP ","B3N5FD ","B3N5FL ","B3N5FN ","B3N5FT ","B3N5FX ", & + "B3N5FY ","B3N5GAM ","B3N5M ","B3N5MM ","B3N5PHI ","B3N5RE ","B3N5SGCAV", & + "B3N5SIGCR","B3N5STVX ","B3N5STVY ","B3N5STVZ ","B3N5THETA","B3N5TNIND","B3N5VDISX", & + "B3N5VDISY","B3N5VDISZ","B3N5VINDX","B3N5VINDY","B3N5VREL ","B3N5VUNDX","B3N5VUNDY", & + "B3N5VUNDZ","B3N6ALPHA","B3N6AXIND","B3N6CD ","B3N6CL ","B3N6CLRNC","B3N6CM ", & + "B3N6CN ","B3N6CPMIN","B3N6CT ","B3N6CURVE","B3N6CX ","B3N6CY ","B3N6DYNP ", & + "B3N6FD ","B3N6FL ","B3N6FN ","B3N6FT ","B3N6FX ","B3N6FY ","B3N6GAM ", & + "B3N6M ","B3N6MM ","B3N6PHI ","B3N6RE ","B3N6SGCAV","B3N6SIGCR","B3N6STVX ", & + "B3N6STVY ","B3N6STVZ ","B3N6THETA","B3N6TNIND","B3N6VDISX","B3N6VDISY","B3N6VDISZ", & + "B3N6VINDX","B3N6VINDY","B3N6VREL ","B3N6VUNDX","B3N6VUNDY","B3N6VUNDZ","B3N7ALPHA", & + "B3N7AXIND","B3N7CD ","B3N7CL ","B3N7CLRNC","B3N7CM ","B3N7CN ","B3N7CPMIN", & + "B3N7CT ","B3N7CURVE","B3N7CX ","B3N7CY ","B3N7DYNP ","B3N7FD ","B3N7FL ", & + "B3N7FN ","B3N7FT ","B3N7FX ","B3N7FY ","B3N7GAM ","B3N7M ","B3N7MM ", & + "B3N7PHI ","B3N7RE ","B3N7SGCAV","B3N7SIGCR","B3N7STVX ","B3N7STVY ","B3N7STVZ ", & + "B3N7THETA","B3N7TNIND","B3N7VDISX","B3N7VDISY","B3N7VDISZ","B3N7VINDX","B3N7VINDY", & + "B3N7VREL ","B3N7VUNDX","B3N7VUNDY","B3N7VUNDZ","B3N8ALPHA","B3N8AXIND","B3N8CD ", & + "B3N8CL ","B3N8CLRNC","B3N8CM ","B3N8CN ","B3N8CPMIN","B3N8CT ","B3N8CURVE", & + "B3N8CX ","B3N8CY ","B3N8DYNP ","B3N8FD ","B3N8FL ","B3N8FN ","B3N8FT ", & + "B3N8FX ","B3N8FY ","B3N8GAM ","B3N8M ","B3N8MM ","B3N8PHI ","B3N8RE ", & + "B3N8SGCAV","B3N8SIGCR","B3N8STVX ","B3N8STVY ","B3N8STVZ ","B3N8THETA","B3N8TNIND", & + "B3N8VDISX","B3N8VDISY","B3N8VDISZ","B3N8VINDX","B3N8VINDY","B3N8VREL ","B3N8VUNDX", & + "B3N8VUNDY","B3N8VUNDZ","B3N9ALPHA","B3N9AXIND","B3N9CD ","B3N9CL ","B3N9CLRNC", & + "B3N9CM ","B3N9CN ","B3N9CPMIN","B3N9CT ","B3N9CURVE","B3N9CX ","B3N9CY ", & + "B3N9DYNP ","B3N9FD ","B3N9FL ","B3N9FN ","B3N9FT ","B3N9FX ","B3N9FY ", & + "B3N9GAM ","B3N9M ","B3N9MM ","B3N9PHI ","B3N9RE ","B3N9SGCAV","B3N9SIGCR", & + "B3N9STVX ","B3N9STVY ","B3N9STVZ ","B3N9THETA","B3N9TNIND","B3N9VDISX","B3N9VDISY", & + "B3N9VDISZ","B3N9VINDX","B3N9VINDY","B3N9VREL ","B3N9VUNDX","B3N9VUNDY","B3N9VUNDZ", & + "B3PITCH ","RTAEROCP ","RTAEROCQ ","RTAEROCT ","RTAEROFXH","RTAEROFYH","RTAEROFZH", & + "RTAEROMXH","RTAEROMYH","RTAEROMZH","RTAEROPWR","RTAREA ","RTSKEW ","RTSPEED ", & + "RTTSR ","RTVAVGXH ","RTVAVGYH ","RTVAVGZH ","TWN1DYNP ","TWN1FDX ","TWN1FDY ", & + "TWN1M ","TWN1RE ","TWN1STVX ","TWN1STVY ","TWN1STVZ ","TWN1VREL ","TWN1VUNDX", & + "TWN1VUNDY","TWN1VUNDZ","TWN2DYNP ","TWN2FDX ","TWN2FDY ","TWN2M ","TWN2RE ", & + "TWN2STVX ","TWN2STVY ","TWN2STVZ ","TWN2VREL ","TWN2VUNDX","TWN2VUNDY","TWN2VUNDZ", & + "TWN3DYNP ","TWN3FDX ","TWN3FDY ","TWN3M ","TWN3RE ","TWN3STVX ","TWN3STVY ", & + "TWN3STVZ ","TWN3VREL ","TWN3VUNDX","TWN3VUNDY","TWN3VUNDZ","TWN4DYNP ","TWN4FDX ", & + "TWN4FDY ","TWN4M ","TWN4RE ","TWN4STVX ","TWN4STVY ","TWN4STVZ ","TWN4VREL ", & + "TWN4VUNDX","TWN4VUNDY","TWN4VUNDZ","TWN5DYNP ","TWN5FDX ","TWN5FDY ","TWN5M ", & + "TWN5RE ","TWN5STVX ","TWN5STVY ","TWN5STVZ ","TWN5VREL ","TWN5VUNDX","TWN5VUNDY", & + "TWN5VUNDZ","TWN6DYNP ","TWN6FDX ","TWN6FDY ","TWN6M ","TWN6RE ","TWN6STVX ", & + "TWN6STVY ","TWN6STVZ ","TWN6VREL ","TWN6VUNDX","TWN6VUNDY","TWN6VUNDZ","TWN7DYNP ", & + "TWN7FDX ","TWN7FDY ","TWN7M ","TWN7RE ","TWN7STVX ","TWN7STVY ","TWN7STVZ ", & + "TWN7VREL ","TWN7VUNDX","TWN7VUNDY","TWN7VUNDZ","TWN8DYNP ","TWN8FDX ","TWN8FDY ", & + "TWN8M ","TWN8RE ","TWN8STVX ","TWN8STVY ","TWN8STVZ ","TWN8VREL ","TWN8VUNDX", & + "TWN8VUNDY","TWN8VUNDZ","TWN9DYNP ","TWN9FDX ","TWN9FDY ","TWN9M ","TWN9RE ", & + "TWN9STVX ","TWN9STVY ","TWN9STVZ ","TWN9VREL ","TWN9VUNDX","TWN9VUNDY","TWN9VUNDZ"/) + INTEGER(IntKi), PARAMETER :: ParamIndxAry(1211) = (/ & ! This lists the index into AllOuts(:) of the allowed parameters ValidParamAry(:) B1Azimuth , B1N1Alpha , B1N1AxInd , B1N1Cd , B1N1Cl , B1N1Clrnc , B1N1Cm , & B1N1Cn , B1N1Cpmin , B1N1Ct , B1N1Curve , B1N1Cx , B1N1Cy , B1N1DynP , & - B1N1Fd , B1N1Fl , B1N1Fn , B1N1Ft , B1N1Fx , B1N1Fy , B1N1M , & - B1N1Mm , B1N1Phi , B1N1Re , B1N1SgCav , B1N1SigCr , B1N1STVx , B1N1STVy , & - B1N1STVz , B1N1Theta , B1N1TnInd , B1N1VDisx , B1N1VDisy , B1N1VDisz , B1N1Vindx , & - B1N1Vindy , B1N1VRel , B1N1VUndx , B1N1VUndy , B1N1VUndz , B1N2Alpha , B1N2AxInd , & - B1N2Cd , B1N2Cl , B1N2Clrnc , B1N2Cm , B1N2Cn , B1N2Cpmin , B1N2Ct , & - B1N2Curve , B1N2Cx , B1N2Cy , B1N2DynP , B1N2Fd , B1N2Fl , B1N2Fn , & - B1N2Ft , B1N2Fx , B1N2Fy , B1N2M , B1N2Mm , B1N2Phi , B1N2Re , & - B1N2SgCav , B1N2SigCr , B1N2STVx , B1N2STVy , B1N2STVz , B1N2Theta , B1N2TnInd , & - B1N2VDisx , B1N2VDisy , B1N2VDisz , B1N2Vindx , B1N2Vindy , B1N2VRel , B1N2VUndx , & - B1N2VUndy , B1N2VUndz , B1N3Alpha , B1N3AxInd , B1N3Cd , B1N3Cl , B1N3Clrnc , & - B1N3Cm , B1N3Cn , B1N3Cpmin , B1N3Ct , B1N3Curve , B1N3Cx , B1N3Cy , & - B1N3DynP , B1N3Fd , B1N3Fl , B1N3Fn , B1N3Ft , B1N3Fx , B1N3Fy , & - B1N3M , B1N3Mm , B1N3Phi , B1N3Re , B1N3SgCav , B1N3SigCr , B1N3STVx , & - B1N3STVy , B1N3STVz , B1N3Theta , B1N3TnInd , B1N3VDisx , B1N3VDisy , B1N3VDisz , & - B1N3Vindx , B1N3Vindy , B1N3VRel , B1N3VUndx , B1N3VUndy , B1N3VUndz , B1N4Alpha , & - B1N4AxInd , B1N4Cd , B1N4Cl , B1N4Clrnc , B1N4Cm , B1N4Cn , B1N4Cpmin , & - B1N4Ct , B1N4Curve , B1N4Cx , B1N4Cy , B1N4DynP , B1N4Fd , B1N4Fl , & - B1N4Fn , B1N4Ft , B1N4Fx , B1N4Fy , B1N4M , B1N4Mm , B1N4Phi , & - B1N4Re , B1N4SgCav , B1N4SigCr , B1N4STVx , B1N4STVy , B1N4STVz , B1N4Theta , & - B1N4TnInd , B1N4VDisx , B1N4VDisy , B1N4VDisz , B1N4Vindx , B1N4Vindy , B1N4VRel , & - B1N4VUndx , B1N4VUndy , B1N4VUndz , B1N5Alpha , B1N5AxInd , B1N5Cd , B1N5Cl , & - B1N5Clrnc , B1N5Cm , B1N5Cn , B1N5Cpmin , B1N5Ct , B1N5Curve , B1N5Cx , & - B1N5Cy , B1N5DynP , B1N5Fd , B1N5Fl , B1N5Fn , B1N5Ft , B1N5Fx , & - B1N5Fy , B1N5M , B1N5Mm , B1N5Phi , B1N5Re , B1N5SgCav , B1N5SigCr , & - B1N5STVx , B1N5STVy , B1N5STVz , B1N5Theta , B1N5TnInd , B1N5VDisx , B1N5VDisy , & - B1N5VDisz , B1N5Vindx , B1N5Vindy , B1N5VRel , B1N5VUndx , B1N5VUndy , B1N5VUndz , & - B1N6Alpha , B1N6AxInd , B1N6Cd , B1N6Cl , B1N6Clrnc , B1N6Cm , B1N6Cn , & - B1N6Cpmin , B1N6Ct , B1N6Curve , B1N6Cx , B1N6Cy , B1N6DynP , B1N6Fd , & - B1N6Fl , B1N6Fn , B1N6Ft , B1N6Fx , B1N6Fy , B1N6M , B1N6Mm , & - B1N6Phi , B1N6Re , B1N6SgCav , B1N6SigCr , B1N6STVx , B1N6STVy , B1N6STVz , & - B1N6Theta , B1N6TnInd , B1N6VDisx , B1N6VDisy , B1N6VDisz , B1N6Vindx , B1N6Vindy , & - B1N6VRel , B1N6VUndx , B1N6VUndy , B1N6VUndz , B1N7Alpha , B1N7AxInd , B1N7Cd , & - B1N7Cl , B1N7Clrnc , B1N7Cm , B1N7Cn , B1N7Cpmin , B1N7Ct , B1N7Curve , & - B1N7Cx , B1N7Cy , B1N7DynP , B1N7Fd , B1N7Fl , B1N7Fn , B1N7Ft , & - B1N7Fx , B1N7Fy , B1N7M , B1N7Mm , B1N7Phi , B1N7Re , B1N7SgCav , & + B1N1Fd , B1N1Fl , B1N1Fn , B1N1Ft , B1N1Fx , B1N1Fy , B1N1Gam , & + B1N1M , B1N1Mm , B1N1Phi , B1N1Re , B1N1SgCav , B1N1SigCr , B1N1STVx , & + B1N1STVy , B1N1STVz , B1N1Theta , B1N1TnInd , B1N1VDisx , B1N1VDisy , B1N1VDisz , & + B1N1Vindx , B1N1Vindy , B1N1VRel , B1N1VUndx , B1N1VUndy , B1N1VUndz , B1N2Alpha , & + B1N2AxInd , B1N2Cd , B1N2Cl , B1N2Clrnc , B1N2Cm , B1N2Cn , B1N2Cpmin , & + B1N2Ct , B1N2Curve , B1N2Cx , B1N2Cy , B1N2DynP , B1N2Fd , B1N2Fl , & + B1N2Fn , B1N2Ft , B1N2Fx , B1N2Fy , B1N2Gam , B1N2M , B1N2Mm , & + B1N2Phi , B1N2Re , B1N2SgCav , B1N2SigCr , B1N2STVx , B1N2STVy , B1N2STVz , & + B1N2Theta , B1N2TnInd , B1N2VDisx , B1N2VDisy , B1N2VDisz , B1N2Vindx , B1N2Vindy , & + B1N2VRel , B1N2VUndx , B1N2VUndy , B1N2VUndz , B1N3Alpha , B1N3AxInd , B1N3Cd , & + B1N3Cl , B1N3Clrnc , B1N3Cm , B1N3Cn , B1N3Cpmin , B1N3Ct , B1N3Curve , & + B1N3Cx , B1N3Cy , B1N3DynP , B1N3Fd , B1N3Fl , B1N3Fn , B1N3Ft , & + B1N3Fx , B1N3Fy , B1N3Gam , B1N3M , B1N3Mm , B1N3Phi , B1N3Re , & + B1N3SgCav , B1N3SigCr , B1N3STVx , B1N3STVy , B1N3STVz , B1N3Theta , B1N3TnInd , & + B1N3VDisx , B1N3VDisy , B1N3VDisz , B1N3Vindx , B1N3Vindy , B1N3VRel , B1N3VUndx , & + B1N3VUndy , B1N3VUndz , B1N4Alpha , B1N4AxInd , B1N4Cd , B1N4Cl , B1N4Clrnc , & + B1N4Cm , B1N4Cn , B1N4Cpmin , B1N4Ct , B1N4Curve , B1N4Cx , B1N4Cy , & + B1N4DynP , B1N4Fd , B1N4Fl , B1N4Fn , B1N4Ft , B1N4Fx , B1N4Fy , & + B1N4Gam , B1N4M , B1N4Mm , B1N4Phi , B1N4Re , B1N4SgCav , B1N4SigCr , & + B1N4STVx , B1N4STVy , B1N4STVz , B1N4Theta , B1N4TnInd , B1N4VDisx , B1N4VDisy , & + B1N4VDisz , B1N4Vindx , B1N4Vindy , B1N4VRel , B1N4VUndx , B1N4VUndy , B1N4VUndz , & + B1N5Alpha , B1N5AxInd , B1N5Cd , B1N5Cl , B1N5Clrnc , B1N5Cm , B1N5Cn , & + B1N5Cpmin , B1N5Ct , B1N5Curve , B1N5Cx , B1N5Cy , B1N5DynP , B1N5Fd , & + B1N5Fl , B1N5Fn , B1N5Ft , B1N5Fx , B1N5Fy , B1N5Gam , B1N5M , & + B1N5Mm , B1N5Phi , B1N5Re , B1N5SgCav , B1N5SigCr , B1N5STVx , B1N5STVy , & + B1N5STVz , B1N5Theta , B1N5TnInd , B1N5VDisx , B1N5VDisy , B1N5VDisz , B1N5Vindx , & + B1N5Vindy , B1N5VRel , B1N5VUndx , B1N5VUndy , B1N5VUndz , B1N6Alpha , B1N6AxInd , & + B1N6Cd , B1N6Cl , B1N6Clrnc , B1N6Cm , B1N6Cn , B1N6Cpmin , B1N6Ct , & + B1N6Curve , B1N6Cx , B1N6Cy , B1N6DynP , B1N6Fd , B1N6Fl , B1N6Fn , & + B1N6Ft , B1N6Fx , B1N6Fy , B1N6Gam , B1N6M , B1N6Mm , B1N6Phi , & + B1N6Re , B1N6SgCav , B1N6SigCr , B1N6STVx , B1N6STVy , B1N6STVz , B1N6Theta , & + B1N6TnInd , B1N6VDisx , B1N6VDisy , B1N6VDisz , B1N6Vindx , B1N6Vindy , B1N6VRel , & + B1N6VUndx , B1N6VUndy , B1N6VUndz , B1N7Alpha , B1N7AxInd , B1N7Cd , B1N7Cl , & + B1N7Clrnc , B1N7Cm , B1N7Cn , B1N7Cpmin , B1N7Ct , B1N7Curve , B1N7Cx , & + B1N7Cy , B1N7DynP , B1N7Fd , B1N7Fl , B1N7Fn , B1N7Ft , B1N7Fx , & + B1N7Fy , B1N7Gam , B1N7M , B1N7Mm , B1N7Phi , B1N7Re , B1N7SgCav , & B1N7SigCr , B1N7STVx , B1N7STVy , B1N7STVz , B1N7Theta , B1N7TnInd , B1N7VDisx , & B1N7VDisy , B1N7VDisz , B1N7Vindx , B1N7Vindy , B1N7VRel , B1N7VUndx , B1N7VUndy , & B1N7VUndz , B1N8Alpha , B1N8AxInd , B1N8Cd , B1N8Cl , B1N8Clrnc , B1N8Cm , & B1N8Cn , B1N8Cpmin , B1N8Ct , B1N8Curve , B1N8Cx , B1N8Cy , B1N8DynP , & - B1N8Fd , B1N8Fl , B1N8Fn , B1N8Ft , B1N8Fx , B1N8Fy , B1N8M , & - B1N8Mm , B1N8Phi , B1N8Re , B1N8SgCav , B1N8SigCr , B1N8STVx , B1N8STVy , & - B1N8STVz , B1N8Theta , B1N8TnInd , B1N8VDisx , B1N8VDisy , B1N8VDisz , B1N8Vindx , & - B1N8Vindy , B1N8VRel , B1N8VUndx , B1N8VUndy , B1N8VUndz , B1N9Alpha , B1N9AxInd , & - B1N9Cd , B1N9Cl , B1N9Clrnc , B1N9Cm , B1N9Cn , B1N9Cpmin , B1N9Ct , & - B1N9Curve , B1N9Cx , B1N9Cy , B1N9DynP , B1N9Fd , B1N9Fl , B1N9Fn , & - B1N9Ft , B1N9Fx , B1N9Fy , B1N9M , B1N9Mm , B1N9Phi , B1N9Re , & - B1N9SgCav , B1N9SigCr , B1N9STVx , B1N9STVy , B1N9STVz , B1N9Theta , B1N9TnInd , & - B1N9VDisx , B1N9VDisy , B1N9VDisz , B1N9Vindx , B1N9Vindy , B1N9VRel , B1N9VUndx , & - B1N9VUndy , B1N9VUndz , B1Pitch , B2Azimuth , B2N1Alpha , B2N1AxInd , B2N1Cd , & - B2N1Cl , B2N1Clrnc , B2N1Cm , B2N1Cn , B2N1Cpmin , B2N1Ct , B2N1Curve , & - B2N1Cx , B2N1Cy , B2N1DynP , B2N1Fd , B2N1Fl , B2N1Fn , B2N1Ft , & - B2N1Fx , B2N1Fy , B2N1M , B2N1Mm , B2N1Phi , B2N1Re , B2N1SgCav , & - B2N1SigCr , B2N1STVx , B2N1STVy , B2N1STVz , B2N1Theta , B2N1TnInd , B2N1VDisx , & - B2N1VDisy , B2N1VDisz , B2N1Vindx , B2N1Vindy , B2N1VRel , B2N1VUndx , B2N1VUndy , & - B2N1VUndz , B2N2Alpha , B2N2AxInd , B2N2Cd , B2N2Cl , B2N2Clrnc , B2N2Cm , & - B2N2Cn , B2N2Cpmin , B2N2Ct , B2N2Curve , B2N2Cx , B2N2Cy , B2N2DynP , & - B2N2Fd , B2N2Fl , B2N2Fn , B2N2Ft , B2N2Fx , B2N2Fy , B2N2M , & - B2N2Mm , B2N2Phi , B2N2Re , B2N2SgCav , B2N2SigCr , B2N2STVx , B2N2STVy , & - B2N2STVz , B2N2Theta , B2N2TnInd , B2N2VDisx , B2N2VDisy , B2N2VDisz , B2N2Vindx , & - B2N2Vindy , B2N2VRel , B2N2VUndx , B2N2VUndy , B2N2VUndz , B2N3Alpha , B2N3AxInd , & - B2N3Cd , B2N3Cl , B2N3Clrnc , B2N3Cm , B2N3Cn , B2N3Cpmin , B2N3Ct , & - B2N3Curve , B2N3Cx , B2N3Cy , B2N3DynP , B2N3Fd , B2N3Fl , B2N3Fn , & - B2N3Ft , B2N3Fx , B2N3Fy , B2N3M , B2N3Mm , B2N3Phi , B2N3Re , & - B2N3SgCav , B2N3SigCr , B2N3STVx , B2N3STVy , B2N3STVz , B2N3Theta , B2N3TnInd , & - B2N3VDisx , B2N3VDisy , B2N3VDisz , B2N3Vindx , B2N3Vindy , B2N3VRel , B2N3VUndx , & - B2N3VUndy , B2N3VUndz , B2N4Alpha , B2N4AxInd , B2N4Cd , B2N4Cl , B2N4Clrnc , & - B2N4Cm , B2N4Cn , B2N4Cpmin , B2N4Ct , B2N4Curve , B2N4Cx , B2N4Cy , & - B2N4DynP , B2N4Fd , B2N4Fl , B2N4Fn , B2N4Ft , B2N4Fx , B2N4Fy , & - B2N4M , B2N4Mm , B2N4Phi , B2N4Re , B2N4SgCav , B2N4SigCr , B2N4STVx , & - B2N4STVy , B2N4STVz , B2N4Theta , B2N4TnInd , B2N4VDisx , B2N4VDisy , B2N4VDisz , & - B2N4Vindx , B2N4Vindy , B2N4VRel , B2N4VUndx , B2N4VUndy , B2N4VUndz , B2N5Alpha , & - B2N5AxInd , B2N5Cd , B2N5Cl , B2N5Clrnc , B2N5Cm , B2N5Cn , B2N5Cpmin , & - B2N5Ct , B2N5Curve , B2N5Cx , B2N5Cy , B2N5DynP , B2N5Fd , B2N5Fl , & - B2N5Fn , B2N5Ft , B2N5Fx , B2N5Fy , B2N5M , B2N5Mm , B2N5Phi , & + B1N8Fd , B1N8Fl , B1N8Fn , B1N8Ft , B1N8Fx , B1N8Fy , B1N8Gam , & + B1N8M , B1N8Mm , B1N8Phi , B1N8Re , B1N8SgCav , B1N8SigCr , B1N8STVx , & + B1N8STVy , B1N8STVz , B1N8Theta , B1N8TnInd , B1N8VDisx , B1N8VDisy , B1N8VDisz , & + B1N8Vindx , B1N8Vindy , B1N8VRel , B1N8VUndx , B1N8VUndy , B1N8VUndz , B1N9Alpha , & + B1N9AxInd , B1N9Cd , B1N9Cl , B1N9Clrnc , B1N9Cm , B1N9Cn , B1N9Cpmin , & + B1N9Ct , B1N9Curve , B1N9Cx , B1N9Cy , B1N9DynP , B1N9Fd , B1N9Fl , & + B1N9Fn , B1N9Ft , B1N9Fx , B1N9Fy , B1N9Gam , B1N9M , B1N9Mm , & + B1N9Phi , B1N9Re , B1N9SgCav , B1N9SigCr , B1N9STVx , B1N9STVy , B1N9STVz , & + B1N9Theta , B1N9TnInd , B1N9VDisx , B1N9VDisy , B1N9VDisz , B1N9Vindx , B1N9Vindy , & + B1N9VRel , B1N9VUndx , B1N9VUndy , B1N9VUndz , B1Pitch , B2Azimuth , B2N1Alpha , & + B2N1AxInd , B2N1Cd , B2N1Cl , B2N1Clrnc , B2N1Cm , B2N1Cn , B2N1Cpmin , & + B2N1Ct , B2N1Curve , B2N1Cx , B2N1Cy , B2N1DynP , B2N1Fd , B2N1Fl , & + B2N1Fn , B2N1Ft , B2N1Fx , B2N1Fy , B2N1Gam , B2N1M , B2N1Mm , & + B2N1Phi , B2N1Re , B2N1SgCav , B2N1SigCr , B2N1STVx , B2N1STVy , B2N1STVz , & + B2N1Theta , B2N1TnInd , B2N1VDisx , B2N1VDisy , B2N1VDisz , B2N1Vindx , B2N1Vindy , & + B2N1VRel , B2N1VUndx , B2N1VUndy , B2N1VUndz , B2N2Alpha , B2N2AxInd , B2N2Cd , & + B2N2Cl , B2N2Clrnc , B2N2Cm , B2N2Cn , B2N2Cpmin , B2N2Ct , B2N2Curve , & + B2N2Cx , B2N2Cy , B2N2DynP , B2N2Fd , B2N2Fl , B2N2Fn , B2N2Ft , & + B2N2Fx , B2N2Fy , B2N2Gam , B2N2M , B2N2Mm , B2N2Phi , B2N2Re , & + B2N2SgCav , B2N2SigCr , B2N2STVx , B2N2STVy , B2N2STVz , B2N2Theta , B2N2TnInd , & + B2N2VDisx , B2N2VDisy , B2N2VDisz , B2N2Vindx , B2N2Vindy , B2N2VRel , B2N2VUndx , & + B2N2VUndy , B2N2VUndz , B2N3Alpha , B2N3AxInd , B2N3Cd , B2N3Cl , B2N3Clrnc , & + B2N3Cm , B2N3Cn , B2N3Cpmin , B2N3Ct , B2N3Curve , B2N3Cx , B2N3Cy , & + B2N3DynP , B2N3Fd , B2N3Fl , B2N3Fn , B2N3Ft , B2N3Fx , B2N3Fy , & + B2N3Gam , B2N3M , B2N3Mm , B2N3Phi , B2N3Re , B2N3SgCav , B2N3SigCr , & + B2N3STVx , B2N3STVy , B2N3STVz , B2N3Theta , B2N3TnInd , B2N3VDisx , B2N3VDisy , & + B2N3VDisz , B2N3Vindx , B2N3Vindy , B2N3VRel , B2N3VUndx , B2N3VUndy , B2N3VUndz , & + B2N4Alpha , B2N4AxInd , B2N4Cd , B2N4Cl , B2N4Clrnc , B2N4Cm , B2N4Cn , & + B2N4Cpmin , B2N4Ct , B2N4Curve , B2N4Cx , B2N4Cy , B2N4DynP , B2N4Fd , & + B2N4Fl , B2N4Fn , B2N4Ft , B2N4Fx , B2N4Fy , B2N4Gam , B2N4M , & + B2N4Mm , B2N4Phi , B2N4Re , B2N4SgCav , B2N4SigCr , B2N4STVx , B2N4STVy , & + B2N4STVz , B2N4Theta , B2N4TnInd , B2N4VDisx , B2N4VDisy , B2N4VDisz , B2N4Vindx , & + B2N4Vindy , B2N4VRel , B2N4VUndx , B2N4VUndy , B2N4VUndz , B2N5Alpha , B2N5AxInd , & + B2N5Cd , B2N5Cl , B2N5Clrnc , B2N5Cm , B2N5Cn , B2N5Cpmin , B2N5Ct , & + B2N5Curve , B2N5Cx , B2N5Cy , B2N5DynP , B2N5Fd , B2N5Fl , B2N5Fn , & + B2N5Ft , B2N5Fx , B2N5Fy , B2N5Gam , B2N5M , B2N5Mm , B2N5Phi , & B2N5Re , B2N5SgCav , B2N5SigCr , B2N5STVx , B2N5STVy , B2N5STVz , B2N5Theta , & B2N5TnInd , B2N5VDisx , B2N5VDisy , B2N5VDisz , B2N5Vindx , B2N5Vindy , B2N5VRel , & B2N5VUndx , B2N5VUndy , B2N5VUndz , B2N6Alpha , B2N6AxInd , B2N6Cd , B2N6Cl , & B2N6Clrnc , B2N6Cm , B2N6Cn , B2N6Cpmin , B2N6Ct , B2N6Curve , B2N6Cx , & B2N6Cy , B2N6DynP , B2N6Fd , B2N6Fl , B2N6Fn , B2N6Ft , B2N6Fx , & - B2N6Fy , B2N6M , B2N6Mm , B2N6Phi , B2N6Re , B2N6SgCav , B2N6SigCr , & - B2N6STVx , B2N6STVy , B2N6STVz , B2N6Theta , B2N6TnInd , B2N6VDisx , B2N6VDisy , & - B2N6VDisz , B2N6Vindx , B2N6Vindy , B2N6VRel , B2N6VUndx , B2N6VUndy , B2N6VUndz , & - B2N7Alpha , B2N7AxInd , B2N7Cd , B2N7Cl , B2N7Clrnc , B2N7Cm , B2N7Cn , & - B2N7Cpmin , B2N7Ct , B2N7Curve , B2N7Cx , B2N7Cy , B2N7DynP , B2N7Fd , & - B2N7Fl , B2N7Fn , B2N7Ft , B2N7Fx , B2N7Fy , B2N7M , B2N7Mm , & - B2N7Phi , B2N7Re , B2N7SgCav , B2N7SigCr , B2N7STVx , B2N7STVy , B2N7STVz , & - B2N7Theta , B2N7TnInd , B2N7VDisx , B2N7VDisy , B2N7VDisz , B2N7Vindx , B2N7Vindy , & - B2N7VRel , B2N7VUndx , B2N7VUndy , B2N7VUndz , B2N8Alpha , B2N8AxInd , B2N8Cd , & - B2N8Cl , B2N8Clrnc , B2N8Cm , B2N8Cn , B2N8Cpmin , B2N8Ct , B2N8Curve , & - B2N8Cx , B2N8Cy , B2N8DynP , B2N8Fd , B2N8Fl , B2N8Fn , B2N8Ft , & - B2N8Fx , B2N8Fy , B2N8M , B2N8Mm , B2N8Phi , B2N8Re , B2N8SgCav , & - B2N8SigCr , B2N8STVx , B2N8STVy , B2N8STVz , B2N8Theta , B2N8TnInd , B2N8VDisx , & - B2N8VDisy , B2N8VDisz , B2N8Vindx , B2N8Vindy , B2N8VRel , B2N8VUndx , B2N8VUndy , & - B2N8VUndz , B2N9Alpha , B2N9AxInd , B2N9Cd , B2N9Cl , B2N9Clrnc , B2N9Cm , & - B2N9Cn , B2N9Cpmin , B2N9Ct , B2N9Curve , B2N9Cx , B2N9Cy , B2N9DynP , & - B2N9Fd , B2N9Fl , B2N9Fn , B2N9Ft , B2N9Fx , B2N9Fy , B2N9M , & - B2N9Mm , B2N9Phi , B2N9Re , B2N9SgCav , B2N9SigCr , B2N9STVx , B2N9STVy , & - B2N9STVz , B2N9Theta , B2N9TnInd , B2N9VDisx , B2N9VDisy , B2N9VDisz , B2N9Vindx , & - B2N9Vindy , B2N9VRel , B2N9VUndx , B2N9VUndy , B2N9VUndz , B2Pitch , B3Azimuth , & - B3N1Alpha , B3N1AxInd , B3N1Cd , B3N1Cl , B3N1Clrnc , B3N1Cm , B3N1Cn , & - B3N1Cpmin , B3N1Ct , B3N1Curve , B3N1Cx , B3N1Cy , B3N1DynP , B3N1Fd , & - B3N1Fl , B3N1Fn , B3N1Ft , B3N1Fx , B3N1Fy , B3N1M , B3N1Mm , & - B3N1Phi , B3N1Re , B3N1SgCav , B3N1SigCr , B3N1STVx , B3N1STVy , B3N1STVz , & - B3N1Theta , B3N1TnInd , B3N1VDisx , B3N1VDisy , B3N1VDisz , B3N1Vindx , B3N1Vindy , & - B3N1VRel , B3N1VUndx , B3N1VUndy , B3N1VUndz , B3N2Alpha , B3N2AxInd , B3N2Cd , & - B3N2Cl , B3N2Clrnc , B3N2Cm , B3N2Cn , B3N2Cpmin , B3N2Ct , B3N2Curve , & - B3N2Cx , B3N2Cy , B3N2DynP , B3N2Fd , B3N2Fl , B3N2Fn , B3N2Ft , & - B3N2Fx , B3N2Fy , B3N2M , B3N2Mm , B3N2Phi , B3N2Re , B3N2SgCav , & - B3N2SigCr , B3N2STVx , B3N2STVy , B3N2STVz , B3N2Theta , B3N2TnInd , B3N2VDisx , & - B3N2VDisy , B3N2VDisz , B3N2Vindx , B3N2Vindy , B3N2VRel , B3N2VUndx , B3N2VUndy , & - B3N2VUndz , B3N3Alpha , B3N3AxInd , B3N3Cd , B3N3Cl , B3N3Clrnc , B3N3Cm , & - B3N3Cn , B3N3Cpmin , B3N3Ct , B3N3Curve , B3N3Cx , B3N3Cy , B3N3DynP , & - B3N3Fd , B3N3Fl , B3N3Fn , B3N3Ft , B3N3Fx , B3N3Fy , B3N3M , & + B2N6Fy , B2N6Gam , B2N6M , B2N6Mm , B2N6Phi , B2N6Re , B2N6SgCav , & + B2N6SigCr , B2N6STVx , B2N6STVy , B2N6STVz , B2N6Theta , B2N6TnInd , B2N6VDisx , & + B2N6VDisy , B2N6VDisz , B2N6Vindx , B2N6Vindy , B2N6VRel , B2N6VUndx , B2N6VUndy , & + B2N6VUndz , B2N7Alpha , B2N7AxInd , B2N7Cd , B2N7Cl , B2N7Clrnc , B2N7Cm , & + B2N7Cn , B2N7Cpmin , B2N7Ct , B2N7Curve , B2N7Cx , B2N7Cy , B2N7DynP , & + B2N7Fd , B2N7Fl , B2N7Fn , B2N7Ft , B2N7Fx , B2N7Fy , B2N7Gam , & + B2N7M , B2N7Mm , B2N7Phi , B2N7Re , B2N7SgCav , B2N7SigCr , B2N7STVx , & + B2N7STVy , B2N7STVz , B2N7Theta , B2N7TnInd , B2N7VDisx , B2N7VDisy , B2N7VDisz , & + B2N7Vindx , B2N7Vindy , B2N7VRel , B2N7VUndx , B2N7VUndy , B2N7VUndz , B2N8Alpha , & + B2N8AxInd , B2N8Cd , B2N8Cl , B2N8Clrnc , B2N8Cm , B2N8Cn , B2N8Cpmin , & + B2N8Ct , B2N8Curve , B2N8Cx , B2N8Cy , B2N8DynP , B2N8Fd , B2N8Fl , & + B2N8Fn , B2N8Ft , B2N8Fx , B2N8Fy , B2N8Gam , B2N8M , B2N8Mm , & + B2N8Phi , B2N8Re , B2N8SgCav , B2N8SigCr , B2N8STVx , B2N8STVy , B2N8STVz , & + B2N8Theta , B2N8TnInd , B2N8VDisx , B2N8VDisy , B2N8VDisz , B2N8Vindx , B2N8Vindy , & + B2N8VRel , B2N8VUndx , B2N8VUndy , B2N8VUndz , B2N9Alpha , B2N9AxInd , B2N9Cd , & + B2N9Cl , B2N9Clrnc , B2N9Cm , B2N9Cn , B2N9Cpmin , B2N9Ct , B2N9Curve , & + B2N9Cx , B2N9Cy , B2N9DynP , B2N9Fd , B2N9Fl , B2N9Fn , B2N9Ft , & + B2N9Fx , B2N9Fy , B2N9Gam , B2N9M , B2N9Mm , B2N9Phi , B2N9Re , & + B2N9SgCav , B2N9SigCr , B2N9STVx , B2N9STVy , B2N9STVz , B2N9Theta , B2N9TnInd , & + B2N9VDisx , B2N9VDisy , B2N9VDisz , B2N9Vindx , B2N9Vindy , B2N9VRel , B2N9VUndx , & + B2N9VUndy , B2N9VUndz , B2Pitch , B3Azimuth , B3N1Alpha , B3N1AxInd , B3N1Cd , & + B3N1Cl , B3N1Clrnc , B3N1Cm , B3N1Cn , B3N1Cpmin , B3N1Ct , B3N1Curve , & + B3N1Cx , B3N1Cy , B3N1DynP , B3N1Fd , B3N1Fl , B3N1Fn , B3N1Ft , & + B3N1Fx , B3N1Fy , B3N1Gam , B3N1M , B3N1Mm , B3N1Phi , B3N1Re , & + B3N1SgCav , B3N1SigCr , B3N1STVx , B3N1STVy , B3N1STVz , B3N1Theta , B3N1TnInd , & + B3N1VDisx , B3N1VDisy , B3N1VDisz , B3N1Vindx , B3N1Vindy , B3N1VRel , B3N1VUndx , & + B3N1VUndy , B3N1VUndz , B3N2Alpha , B3N2AxInd , B3N2Cd , B3N2Cl , B3N2Clrnc , & + B3N2Cm , B3N2Cn , B3N2Cpmin , B3N2Ct , B3N2Curve , B3N2Cx , B3N2Cy , & + B3N2DynP , B3N2Fd , B3N2Fl , B3N2Fn , B3N2Ft , B3N2Fx , B3N2Fy , & + B3N2Gam , B3N2M , B3N2Mm , B3N2Phi , B3N2Re , B3N2SgCav , B3N2SigCr , & + B3N2STVx , B3N2STVy , B3N2STVz , B3N2Theta , B3N2TnInd , B3N2VDisx , B3N2VDisy , & + B3N2VDisz , B3N2Vindx , B3N2Vindy , B3N2VRel , B3N2VUndx , B3N2VUndy , B3N2VUndz , & + B3N3Alpha , B3N3AxInd , B3N3Cd , B3N3Cl , B3N3Clrnc , B3N3Cm , B3N3Cn , & + B3N3Cpmin , B3N3Ct , B3N3Curve , B3N3Cx , B3N3Cy , B3N3DynP , B3N3Fd , & + B3N3Fl , B3N3Fn , B3N3Ft , B3N3Fx , B3N3Fy , B3N3Gam , B3N3M , & B3N3Mm , B3N3Phi , B3N3Re , B3N3SgCav , B3N3SigCr , B3N3STVx , B3N3STVy , & B3N3STVz , B3N3Theta , B3N3TnInd , B3N3VDisx , B3N3VDisy , B3N3VDisz , B3N3Vindx , & B3N3Vindy , B3N3VRel , B3N3VUndx , B3N3VUndy , B3N3VUndz , B3N4Alpha , B3N4AxInd , & B3N4Cd , B3N4Cl , B3N4Clrnc , B3N4Cm , B3N4Cn , B3N4Cpmin , B3N4Ct , & B3N4Curve , B3N4Cx , B3N4Cy , B3N4DynP , B3N4Fd , B3N4Fl , B3N4Fn , & - B3N4Ft , B3N4Fx , B3N4Fy , B3N4M , B3N4Mm , B3N4Phi , B3N4Re , & - B3N4SgCav , B3N4SigCr , B3N4STVx , B3N4STVy , B3N4STVz , B3N4Theta , B3N4TnInd , & - B3N4VDisx , B3N4VDisy , B3N4VDisz , B3N4Vindx , B3N4Vindy , B3N4VRel , B3N4VUndx , & - B3N4VUndy , B3N4VUndz , B3N5Alpha , B3N5AxInd , B3N5Cd , B3N5Cl , B3N5Clrnc , & - B3N5Cm , B3N5Cn , B3N5Cpmin , B3N5Ct , B3N5Curve , B3N5Cx , B3N5Cy , & - B3N5DynP , B3N5Fd , B3N5Fl , B3N5Fn , B3N5Ft , B3N5Fx , B3N5Fy , & - B3N5M , B3N5Mm , B3N5Phi , B3N5Re , B3N5SgCav , B3N5SigCr , B3N5STVx , & - B3N5STVy , B3N5STVz , B3N5Theta , B3N5TnInd , B3N5VDisx , B3N5VDisy , B3N5VDisz , & - B3N5Vindx , B3N5Vindy , B3N5VRel , B3N5VUndx , B3N5VUndy , B3N5VUndz , B3N6Alpha , & - B3N6AxInd , B3N6Cd , B3N6Cl , B3N6Clrnc , B3N6Cm , B3N6Cn , B3N6Cpmin , & - B3N6Ct , B3N6Curve , B3N6Cx , B3N6Cy , B3N6DynP , B3N6Fd , B3N6Fl , & - B3N6Fn , B3N6Ft , B3N6Fx , B3N6Fy , B3N6M , B3N6Mm , B3N6Phi , & - B3N6Re , B3N6SgCav , B3N6SigCr , B3N6STVx , B3N6STVy , B3N6STVz , B3N6Theta , & - B3N6TnInd , B3N6VDisx , B3N6VDisy , B3N6VDisz , B3N6Vindx , B3N6Vindy , B3N6VRel , & - B3N6VUndx , B3N6VUndy , B3N6VUndz , B3N7Alpha , B3N7AxInd , B3N7Cd , B3N7Cl , & - B3N7Clrnc , B3N7Cm , B3N7Cn , B3N7Cpmin , B3N7Ct , B3N7Curve , B3N7Cx , & - B3N7Cy , B3N7DynP , B3N7Fd , B3N7Fl , B3N7Fn , B3N7Ft , B3N7Fx , & - B3N7Fy , B3N7M , B3N7Mm , B3N7Phi , B3N7Re , B3N7SgCav , B3N7SigCr , & - B3N7STVx , B3N7STVy , B3N7STVz , B3N7Theta , B3N7TnInd , B3N7VDisx , B3N7VDisy , & - B3N7VDisz , B3N7Vindx , B3N7Vindy , B3N7VRel , B3N7VUndx , B3N7VUndy , B3N7VUndz , & - B3N8Alpha , B3N8AxInd , B3N8Cd , B3N8Cl , B3N8Clrnc , B3N8Cm , B3N8Cn , & - B3N8Cpmin , B3N8Ct , B3N8Curve , B3N8Cx , B3N8Cy , B3N8DynP , B3N8Fd , & - B3N8Fl , B3N8Fn , B3N8Ft , B3N8Fx , B3N8Fy , B3N8M , B3N8Mm , & - B3N8Phi , B3N8Re , B3N8SgCav , B3N8SigCr , B3N8STVx , B3N8STVy , B3N8STVz , & - B3N8Theta , B3N8TnInd , B3N8VDisx , B3N8VDisy , B3N8VDisz , B3N8Vindx , B3N8Vindy , & - B3N8VRel , B3N8VUndx , B3N8VUndy , B3N8VUndz , B3N9Alpha , B3N9AxInd , B3N9Cd , & - B3N9Cl , B3N9Clrnc , B3N9Cm , B3N9Cn , B3N9Cpmin , B3N9Ct , B3N9Curve , & - B3N9Cx , B3N9Cy , B3N9DynP , B3N9Fd , B3N9Fl , B3N9Fn , B3N9Ft , & - B3N9Fx , B3N9Fy , B3N9M , B3N9Mm , B3N9Phi , B3N9Re , B3N9SgCav , & - B3N9SigCr , B3N9STVx , B3N9STVy , B3N9STVz , B3N9Theta , B3N9TnInd , B3N9VDisx , & - B3N9VDisy , B3N9VDisz , B3N9Vindx , B3N9Vindy , B3N9VRel , B3N9VUndx , B3N9VUndy , & - B3N9VUndz , B3Pitch , RtAeroCp , RtAeroCq , RtAeroCt , RtAeroFxh , RtAeroFyh , & - RtAeroFzh , RtAeroMxh , RtAeroMyh , RtAeroMzh , RtAeroPwr , RtArea , RtSkew , & - RtSpeed , RtTSR , RtVAvgxh , RtVAvgyh , RtVAvgzh , TwN1DynP , TwN1Fdx , & - TwN1Fdy , TwN1M , TwN1Re , TwN1STVx , TwN1STVy , TwN1STVz , TwN1Vrel , & - TwN1VUndx , TwN1VUndy , TwN1VUndz , TwN2DynP , TwN2Fdx , TwN2Fdy , TwN2M , & - TwN2Re , TwN2STVx , TwN2STVy , TwN2STVz , TwN2Vrel , TwN2VUndx , TwN2VUndy , & - TwN2VUndz , TwN3DynP , TwN3Fdx , TwN3Fdy , TwN3M , TwN3Re , TwN3STVx , & - TwN3STVy , TwN3STVz , TwN3Vrel , TwN3VUndx , TwN3VUndy , TwN3VUndz , TwN4DynP , & - TwN4Fdx , TwN4Fdy , TwN4M , TwN4Re , TwN4STVx , TwN4STVy , TwN4STVz , & - TwN4Vrel , TwN4VUndx , TwN4VUndy , TwN4VUndz , TwN5DynP , TwN5Fdx , TwN5Fdy , & - TwN5M , TwN5Re , TwN5STVx , TwN5STVy , TwN5STVz , TwN5Vrel , TwN5VUndx , & - TwN5VUndy , TwN5VUndz , TwN6DynP , TwN6Fdx , TwN6Fdy , TwN6M , TwN6Re , & - TwN6STVx , TwN6STVy , TwN6STVz , TwN6Vrel , TwN6VUndx , TwN6VUndy , TwN6VUndz , & - TwN7DynP , TwN7Fdx , TwN7Fdy , TwN7M , TwN7Re , TwN7STVx , TwN7STVy , & - TwN7STVz , TwN7Vrel , TwN7VUndx , TwN7VUndy , TwN7VUndz , TwN8DynP , TwN8Fdx , & - TwN8Fdy , TwN8M , TwN8Re , TwN8STVx , TwN8STVy , TwN8STVz , TwN8Vrel , & - TwN8VUndx , TwN8VUndy , TwN8VUndz , TwN9DynP , TwN9Fdx , TwN9Fdy , TwN9M , & - TwN9Re , TwN9STVx , TwN9STVy , TwN9STVz , TwN9Vrel , TwN9VUndx , TwN9VUndy , & - TwN9VUndz /) - CHARACTER(ChanLen), PARAMETER :: ParamUnitsAry(1184) = (/ & ! This lists the units corresponding to the allowed parameters + B3N4Ft , B3N4Fx , B3N4Fy , B3N4Gam , B3N4M , B3N4Mm , B3N4Phi , & + B3N4Re , B3N4SgCav , B3N4SigCr , B3N4STVx , B3N4STVy , B3N4STVz , B3N4Theta , & + B3N4TnInd , B3N4VDisx , B3N4VDisy , B3N4VDisz , B3N4Vindx , B3N4Vindy , B3N4VRel , & + B3N4VUndx , B3N4VUndy , B3N4VUndz , B3N5Alpha , B3N5AxInd , B3N5Cd , B3N5Cl , & + B3N5Clrnc , B3N5Cm , B3N5Cn , B3N5Cpmin , B3N5Ct , B3N5Curve , B3N5Cx , & + B3N5Cy , B3N5DynP , B3N5Fd , B3N5Fl , B3N5Fn , B3N5Ft , B3N5Fx , & + B3N5Fy , B3N5Gam , B3N5M , B3N5Mm , B3N5Phi , B3N5Re , B3N5SgCav , & + B3N5SigCr , B3N5STVx , B3N5STVy , B3N5STVz , B3N5Theta , B3N5TnInd , B3N5VDisx , & + B3N5VDisy , B3N5VDisz , B3N5Vindx , B3N5Vindy , B3N5VRel , B3N5VUndx , B3N5VUndy , & + B3N5VUndz , B3N6Alpha , B3N6AxInd , B3N6Cd , B3N6Cl , B3N6Clrnc , B3N6Cm , & + B3N6Cn , B3N6Cpmin , B3N6Ct , B3N6Curve , B3N6Cx , B3N6Cy , B3N6DynP , & + B3N6Fd , B3N6Fl , B3N6Fn , B3N6Ft , B3N6Fx , B3N6Fy , B3N6Gam , & + B3N6M , B3N6Mm , B3N6Phi , B3N6Re , B3N6SgCav , B3N6SigCr , B3N6STVx , & + B3N6STVy , B3N6STVz , B3N6Theta , B3N6TnInd , B3N6VDisx , B3N6VDisy , B3N6VDisz , & + B3N6Vindx , B3N6Vindy , B3N6VRel , B3N6VUndx , B3N6VUndy , B3N6VUndz , B3N7Alpha , & + B3N7AxInd , B3N7Cd , B3N7Cl , B3N7Clrnc , B3N7Cm , B3N7Cn , B3N7Cpmin , & + B3N7Ct , B3N7Curve , B3N7Cx , B3N7Cy , B3N7DynP , B3N7Fd , B3N7Fl , & + B3N7Fn , B3N7Ft , B3N7Fx , B3N7Fy , B3N7Gam , B3N7M , B3N7Mm , & + B3N7Phi , B3N7Re , B3N7SgCav , B3N7SigCr , B3N7STVx , B3N7STVy , B3N7STVz , & + B3N7Theta , B3N7TnInd , B3N7VDisx , B3N7VDisy , B3N7VDisz , B3N7Vindx , B3N7Vindy , & + B3N7VRel , B3N7VUndx , B3N7VUndy , B3N7VUndz , B3N8Alpha , B3N8AxInd , B3N8Cd , & + B3N8Cl , B3N8Clrnc , B3N8Cm , B3N8Cn , B3N8Cpmin , B3N8Ct , B3N8Curve , & + B3N8Cx , B3N8Cy , B3N8DynP , B3N8Fd , B3N8Fl , B3N8Fn , B3N8Ft , & + B3N8Fx , B3N8Fy , B3N8Gam , B3N8M , B3N8Mm , B3N8Phi , B3N8Re , & + B3N8SgCav , B3N8SigCr , B3N8STVx , B3N8STVy , B3N8STVz , B3N8Theta , B3N8TnInd , & + B3N8VDisx , B3N8VDisy , B3N8VDisz , B3N8Vindx , B3N8Vindy , B3N8VRel , B3N8VUndx , & + B3N8VUndy , B3N8VUndz , B3N9Alpha , B3N9AxInd , B3N9Cd , B3N9Cl , B3N9Clrnc , & + B3N9Cm , B3N9Cn , B3N9Cpmin , B3N9Ct , B3N9Curve , B3N9Cx , B3N9Cy , & + B3N9DynP , B3N9Fd , B3N9Fl , B3N9Fn , B3N9Ft , B3N9Fx , B3N9Fy , & + B3N9Gam , B3N9M , B3N9Mm , B3N9Phi , B3N9Re , B3N9SgCav , B3N9SigCr , & + B3N9STVx , B3N9STVy , B3N9STVz , B3N9Theta , B3N9TnInd , B3N9VDisx , B3N9VDisy , & + B3N9VDisz , B3N9Vindx , B3N9Vindy , B3N9VRel , B3N9VUndx , B3N9VUndy , B3N9VUndz , & + B3Pitch , RtAeroCp , RtAeroCq , RtAeroCt , RtAeroFxh , RtAeroFyh , RtAeroFzh , & + RtAeroMxh , RtAeroMyh , RtAeroMzh , RtAeroPwr , RtArea , RtSkew , RtSpeed , & + RtTSR , RtVAvgxh , RtVAvgyh , RtVAvgzh , TwN1DynP , TwN1Fdx , TwN1Fdy , & + TwN1M , TwN1Re , TwN1STVx , TwN1STVy , TwN1STVz , TwN1Vrel , TwN1VUndx , & + TwN1VUndy , TwN1VUndz , TwN2DynP , TwN2Fdx , TwN2Fdy , TwN2M , TwN2Re , & + TwN2STVx , TwN2STVy , TwN2STVz , TwN2Vrel , TwN2VUndx , TwN2VUndy , TwN2VUndz , & + TwN3DynP , TwN3Fdx , TwN3Fdy , TwN3M , TwN3Re , TwN3STVx , TwN3STVy , & + TwN3STVz , TwN3Vrel , TwN3VUndx , TwN3VUndy , TwN3VUndz , TwN4DynP , TwN4Fdx , & + TwN4Fdy , TwN4M , TwN4Re , TwN4STVx , TwN4STVy , TwN4STVz , TwN4Vrel , & + TwN4VUndx , TwN4VUndy , TwN4VUndz , TwN5DynP , TwN5Fdx , TwN5Fdy , TwN5M , & + TwN5Re , TwN5STVx , TwN5STVy , TwN5STVz , TwN5Vrel , TwN5VUndx , TwN5VUndy , & + TwN5VUndz , TwN6DynP , TwN6Fdx , TwN6Fdy , TwN6M , TwN6Re , TwN6STVx , & + TwN6STVy , TwN6STVz , TwN6Vrel , TwN6VUndx , TwN6VUndy , TwN6VUndz , TwN7DynP , & + TwN7Fdx , TwN7Fdy , TwN7M , TwN7Re , TwN7STVx , TwN7STVy , TwN7STVz , & + TwN7Vrel , TwN7VUndx , TwN7VUndy , TwN7VUndz , TwN8DynP , TwN8Fdx , TwN8Fdy , & + TwN8M , TwN8Re , TwN8STVx , TwN8STVy , TwN8STVz , TwN8Vrel , TwN8VUndx , & + TwN8VUndy , TwN8VUndz , TwN9DynP , TwN9Fdx , TwN9Fdy , TwN9M , TwN9Re , & + TwN9STVx , TwN9STVy , TwN9STVz , TwN9Vrel , TwN9VUndx , TwN9VUndy , TwN9VUndz /) + CHARACTER(ChanLen), PARAMETER :: ParamUnitsAry(1211) = (/ & ! This lists the units corresponding to the allowed parameters "(deg) ","(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ", & "(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ", & - "(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ", & - "(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & - "(-) ","(-) ","(m) ","(-) ","(-) ","(-) ","(-) ", & - "(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ", & - "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ", & - "(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ", & - "(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ", & "(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ", & "(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & "(-) ","(-) ","(-) ","(m) ","(-) ","(-) ","(-) ", & "(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ", & - "(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & - "(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ", & - "(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ", & - "(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ", & - "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ","(-) ", & - "(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ", & "(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & "(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(-) ", & "(-) ","(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ", & "(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ", & - "(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ", & + "(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ", & + "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ", & - "(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ", & + "(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ", & + "(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ", & + "(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & + "(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ", & + "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ","(-) ", & + "(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ", & "(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ", & "(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & "(-) ","(-) ","(m) ","(-) ","(-) ","(-) ","(-) ", & "(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ", & - "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(deg) ","(deg) ","(deg) ","(-) ","(-) ", & - "(-) ","(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ", & - "(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ", & + "(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ", & + "(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & + "(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ", & + "(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ", & + "(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & + "(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ", & "(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ", & "(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ", & - "(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ", & - "(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & - "(-) ","(-) ","(m) ","(-) ","(-) ","(-) ","(-) ", & - "(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ", & - "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ", & - "(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ", & - "(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ", & "(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ", & "(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & "(-) ","(-) ","(-) ","(m) ","(-) ","(-) ","(-) ", & "(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ", & - "(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & - "(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ", & - "(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ", & - "(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ", & - "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ","(-) ", & - "(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ", & + "(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & + "(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(deg) ","(deg) ", & + "(-) ","(-) ","(-) ","(m) ","(-) ","(-) ","(-) ", & + "(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ", & "(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & "(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(-) ", & "(-) ","(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ", & "(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ", & + "(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ", & + "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ", & + "(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ", & + "(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & + "(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ", & + "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ","(-) ", & + "(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ", & + "(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ", & + "(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & + "(-) ","(-) ","(m) ","(-) ","(-) ","(-) ","(-) ", & + "(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ", & + "(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & + "(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ", & + "(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ", & + "(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & + "(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ", & "(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ", & "(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ", & - "(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ", & - "(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(deg) ", & - "(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ","(-) ", & - "(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ", & + "(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ", & + "(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & + "(-) ","(-) ","(-) ","(m) ","(-) ","(-) ","(-) ", & + "(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ", & "(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & "(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(-) ", & "(-) ","(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ", & "(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ", & - "(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ", & + "(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ", & + "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ", & - "(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ", & - "(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ", & - "(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & - "(-) ","(-) ","(m) ","(-) ","(-) ","(-) ","(-) ", & - "(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ", & + "(m/s) ","(m/s) ","(deg) ","(deg) ","(deg) ","(-) ","(-) ", & + "(-) ","(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ", & + "(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & + "(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ", & "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ", & "(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ", & "(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & - "(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ", & - "(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & - "(-) ","(-) ","(-) ","(m) ","(-) ","(-) ","(-) ", & - "(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ", & + "(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ", & + "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ","(-) ", & + "(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ", & + "(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ", & + "(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & + "(-) ","(-) ","(m) ","(-) ","(-) ","(-) ","(-) ", & + "(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ", & "(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & "(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ", & "(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ", & "(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ", & - "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ", & + "(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ", & + "(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ","(-) ", & - "(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ", & - "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(-) ","(N-m/m) ", & + "(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ","(-) ", & + "(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ","(Pa) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ", & + "(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ","(m/s) ", & + "(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ", & + "(-) ","(-) ","(-) ","(m) ","(-) ","(-) ","(-) ", & + "(-) ","(deg) ","(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ", & + "(N/m) ","(N/m) ","(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ", & "(deg) ","(-) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ", & "(deg) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(-) ", & "(-) ","(m) ","(-) ","(-) ","(-) ","(-) ","(deg) ", & "(-) ","(-) ","(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & - "(N/m) ","(N/m) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ", & - "(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ", & + "(N/m) ","(N/m) ","(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ", & + "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(deg) ","(-) ","(-) ","(-) ","(m) ", & + "(-) ","(-) ","(-) ","(-) ","(deg) ","(-) ","(-) ", & + "(Pa) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ","(N/m) ", & + "(m^2/s) ","(-) ","(N-m/m) ","(deg) ","(-) ","(-) ","(-) ", & + "(m/s) ","(m/s) ","(m/s) ","(deg) ","(-) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(deg) ","(-) ","(-) ","(-) ","(N) ","(N) ","(N) ", & + "(N-m) ","(N-m) ","(N-m) ","(W) ","(m^2) ","(deg) ","(rpm) ", & + "(-) ","(m/s) ","(m/s) ","(m/s) ","(Pa) ","(N/m) ","(N/m) ", & + "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(Pa) ","(N/m) ","(N/m) ","(-) ","(-) ", & "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(deg) ","(-) ","(-) ","(-) ","(N) ","(N) ", & - "(N) ","(N-m) ","(N-m) ","(N-m) ","(W) ","(m^2) ","(deg) ", & - "(rpm) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(Pa) ","(N/m) ", & + "(Pa) ","(N/m) ","(N/m) ","(-) ","(-) ","(m/s) ","(m/s) ", & + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(Pa) ","(N/m) ", & "(N/m) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(m/s) ","(Pa) ","(N/m) ","(N/m) ","(-) ", & "(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & @@ -3655,13 +3705,7 @@ SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(Pa) ","(N/m) ","(N/m) ", & "(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & "(m/s) ","(m/s) ","(Pa) ","(N/m) ","(N/m) ","(-) ","(-) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(Pa) ","(N/m) ","(N/m) ","(-) ","(-) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(Pa) ","(N/m) ", & - "(N/m) ","(-) ","(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) ","(m/s) ","(m/s) ","(Pa) ","(N/m) ","(N/m) ","(-) ", & - "(-) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ", & - "(m/s) "/) + "(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) ","(m/s) "/) ! Initialize values From 989f281fb022b9bae7da7d66a3c30c62de879a31 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 6 May 2020 09:58:59 -0600 Subject: [PATCH 156/190] FVW: rogue STOP in the middle of outputs section causing issues --- modules/aerodyn/src/AeroDyn_IO.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 967499c8dc..e1902ee4fb 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -2035,7 +2035,6 @@ subroutine Calc_WriteOutput_FVW ! NOTE: using airfoil coeffs at nodes call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) - STOP theta = m%FVW%PitchAndTwist(j,k) ! --- Setting AD outputs From d7c5169294b439967bf3c76ccd977023034f9d77 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 6 May 2020 15:39:17 -0600 Subject: [PATCH 157/190] FVW: add unit tests and update stale ED_Types file --- modules/aerodyn/CMakeLists.txt | 11 +++- modules/aerodyn/tests/test_FVW_testsuite.F90 | 49 ++++++++++++++++ modules/elastodyn/src/ElastoDyn_Types.f90 | 27 ++++----- unit_tests/CMakeLists.txt | 1 + unit_tests/aerodyn/CMakeLists.txt | 61 ++++++++++++++++++++ 5 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 modules/aerodyn/tests/test_FVW_testsuite.F90 create mode 100644 unit_tests/aerodyn/CMakeLists.txt diff --git a/modules/aerodyn/CMakeLists.txt b/modules/aerodyn/CMakeLists.txt index 47af2b6a95..3265df4d03 100644 --- a/modules/aerodyn/CMakeLists.txt +++ b/modules/aerodyn/CMakeLists.txt @@ -39,7 +39,10 @@ set(AD_LIBS_SOURCES src/BEMT_Types.f90 src/DBEMT_Types.f90 src/UnsteadyAero_Types.f90 +) +# FVW lib +set(FVW_LIBS_SOURCES src/FVW.f90 src/FVW_IO.f90 src/FVW_VortexTools.f90 @@ -51,8 +54,12 @@ set(AD_LIBS_SOURCES src/FVW_VTK.f90 ) +# this lib is only for the ctest +add_library(fvwlib ${FVW_LIBS_SOURCES}) +target_link_libraries(fvwlib nwtclibs) + add_library(aerodynlib ${AD_LIBS_SOURCES}) -target_link_libraries(aerodynlib nwtclibs) +target_link_libraries(aerodynlib fvwlib nwtclibs) # AeroDyn driver set(AD_DRIVER_SOURCES @@ -72,7 +79,7 @@ set(UA_DRIVER_SOURCES add_executable(unsteadyaero_driver ${UA_DRIVER_SOURCES}) target_link_libraries(unsteadyaero_driver aerodynlib nwtclibs versioninfolib ${CMAKE_DL_LIBS}) -install(TARGETS unsteadyaero_driver aerodyn_driver aerodynlib +install(TARGETS unsteadyaero_driver aerodyn_driver aerodynlib fvwlib EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/modules/aerodyn/tests/test_FVW_testsuite.F90 b/modules/aerodyn/tests/test_FVW_testsuite.F90 new file mode 100644 index 0000000000..322edadb89 --- /dev/null +++ b/modules/aerodyn/tests/test_FVW_testsuite.F90 @@ -0,0 +1,49 @@ +@test +subroutine test_AD_FVW() + ! test branches + ! - known valid checks for various FVW routines (contained in own module) + ! - known invalid rotation matrix: halve the angle of the diagonal elements + + use pFUnit_mod + use NWTC_Num + use test_tools + use FVW_Tests + + implicit none + + integer(IntKi) :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(1024) :: testname + + ! initialize NWTC_Num constants + call SetConstants() + +!This is a single routine that contains the test cases below. + ! -------------------------------------------------------------------------- + testname = "Set of FVW tests" + call FVW_RunTests( ErrStat, ErrMsg ) + @assertEqual(0, ErrStat, testname) + + +! test routines from FVW_RunTests to be run individually -- except these are all private +! ! -------------------------------------------------------------------------- +! testname = "known valid Biot-Savart segment" +! call Test_BiotSavart_Sgmt(testname, ErrStat, ErrMsg) +! @assertEqual(0, ErrStat, testname) +! +! ! -------------------------------------------------------------------------- +! testname = "known valid Biot-Savart part" +! call Test_BiotSavart_Part(testname, ErrStat, ErrMsg) +! @assertEqual(0, ErrStat, testname) +! +! ! -------------------------------------------------------------------------- +! testname = "known valid Biot-Savart to part-tree" +! call Test_BiotSavart_PartTree(testname, ErrStat, ErrMsg) +! @assertEqual(0, ErrStat, testname) +! +! ! -------------------------------------------------------------------------- +! testname = "known valid segment split to parts" +! call Test_SegmentsToPart(testname, ErrStat, ErrMsg) +! @assertEqual(0, ErrStat, testname) + +end subroutine test_AD_FVW diff --git a/modules/elastodyn/src/ElastoDyn_Types.f90 b/modules/elastodyn/src/ElastoDyn_Types.f90 index ba6ff761e6..8e96a90a82 100644 --- a/modules/elastodyn/src/ElastoDyn_Types.f90 +++ b/modules/elastodyn/src/ElastoDyn_Types.f90 @@ -11168,14 +11168,8 @@ SUBROUTINE ED_PackRtHndSide( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg IntKiBuf( Int_Xferred + 1) = UBOUND(InData%AngVelEM,3) Int_Xferred = Int_Xferred + 2 - DO i3 = LBOUND(InData%AngVelEM,3), UBOUND(InData%AngVelEM,3) - DO i2 = LBOUND(InData%AngVelEM,2), UBOUND(InData%AngVelEM,2) - DO i1 = LBOUND(InData%AngVelEM,1), UBOUND(InData%AngVelEM,1) - ReKiBuf(Re_Xferred) = InData%AngVelEM(i1,i2,i3) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO + IF (SIZE(InData%AngVelEM)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%AngVelEM))-1 ) = PACK(InData%AngVelEM,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%AngVelEM) END IF IF ( .NOT. ALLOCATED(InData%PAngVelEN) ) THEN IntKiBuf( Int_Xferred ) = 0 @@ -12952,14 +12946,15 @@ SUBROUTINE ED_UnPackRtHndSide( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%AngVelEM.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i3 = LBOUND(OutData%AngVelEM,3), UBOUND(OutData%AngVelEM,3) - DO i2 = LBOUND(OutData%AngVelEM,2), UBOUND(OutData%AngVelEM,2) - DO i1 = LBOUND(OutData%AngVelEM,1), UBOUND(OutData%AngVelEM,1) - OutData%AngVelEM(i1,i2,i3) = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - END DO - END DO - END DO + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%AngVelEM)>0) OutData%AngVelEM = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%AngVelEM))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%AngVelEM) + DEALLOCATE(mask3) END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! PAngVelEN not allocated Int_Xferred = Int_Xferred + 1 diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index ff2cfae7dd..8f680c1a22 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -70,3 +70,4 @@ endif() ### Add the unit tests here add_subdirectory("beamdyn") add_subdirectory("nwtc-library") +add_subdirectory("aerodyn") diff --git a/unit_tests/aerodyn/CMakeLists.txt b/unit_tests/aerodyn/CMakeLists.txt new file mode 100644 index 0000000000..4e0bf86852 --- /dev/null +++ b/unit_tests/aerodyn/CMakeLists.txt @@ -0,0 +1,61 @@ +# +# Copyright 2017 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. +# + +set_source_files_properties(${pfunit_directory}/include/driver.F90 PROPERTIES GENERATED 1) + +set(module_name "fvw") +set(module_directory "aerodyn") +set(module_library "fvwlib") + +file(MAKE_DIRECTORY ${build_testdirectory}/${module_directory}) +file(WRITE ${build_testdirectory}/${module_directory}/testSuites.inc "") + +include_directories( + ${PROJECT_SOURCE_DIR} + ${pfunit_directory}/mod + ${build_testdirectory}/${module_directory} +) + +set(testlist + test_FVW_testsuite +) +foreach(test ${testlist}) + set(test_dependency pfunit ${source_modulesdirectory}/${module_directory}/tests/${test}.F90) + add_custom_command( + OUTPUT ${build_testdirectory}/${module_directory}/${test}.F90 + COMMAND ${PYTHON_EXECUTABLE} ${pfunit_directory}/bin/pFUnitParser.py ${source_modulesdirectory}/${module_directory}/tests/${test}.F90 ${build_testdirectory}/${module_directory}/${test}.F90 + DEPENDS ${test_dependency} + ) + set(test_sources ${test_sources} ${build_testdirectory}/${module_directory}/${test}.F90) + file(APPEND ${build_testdirectory}/${module_directory}/testSuites.inc "ADD_TEST_SUITE(${test}_suite)\n") +endforeach() + +add_executable( + ${module_name}_utest + ${pfunit_directory}/include/driver.F90 + ${test_sources} +) + +target_link_libraries( + ${module_name}_utest + ${pfunit_directory}${pfunit_lib} + ${module_library} +) + +add_test( + ${module_name}_utest + ${PROJECT_BINARY_DIR}/${module_directory}/${module_name}_utest +) From 50c58f37e81aabcae764b6786cf8ae8d6dfc2517 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Thu, 7 May 2020 13:49:00 -0600 Subject: [PATCH 158/190] FVW: update cmake for AD15 / fvw. It wasn't compiling the test. --- .gitmodules | 2 +- modules/aerodyn/CMakeLists.txt | 32 +++++++++++++++----- modules/aerodyn/tests/test_FVW_testsuite.F90 | 1 - 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.gitmodules b/.gitmodules index 5fb3759b96..9650d9f067 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "reg_tests/r-test"] path = reg_tests/r-test - url = https://github.com/openfast/r-test.git + url = https://github.com/OpenFAST/r-test.git [submodule "unit_tests/pfunit"] path = unit_tests/pfunit url = https://github.com/Goddard-Fortran-Ecosystem/pFUnit.git diff --git a/modules/aerodyn/CMakeLists.txt b/modules/aerodyn/CMakeLists.txt index 3265df4d03..335b12bf85 100644 --- a/modules/aerodyn/CMakeLists.txt +++ b/modules/aerodyn/CMakeLists.txt @@ -28,19 +28,27 @@ endif() set(AD_LIBS_SOURCES src/AeroDyn.f90 src/AeroDyn_IO.f90 - src/AirfoilInfo.f90 src/BEMT.f90 src/DBEMT.f90 src/BEMTUncoupled.f90 - src/UnsteadyAero.f90 src/mod_root1dim.f90 src/AeroDyn_Types.f90 - src/AirfoilInfo_Types.f90 src/BEMT_Types.f90 src/DBEMT_Types.f90 +) + +# UnsteadyAero lib +set(UA_LIBS_SOURCES + src/UnsteadyAero.f90 src/UnsteadyAero_Types.f90 ) +# AirFoil Info lib +set(AFINFO_LIBS_SOURCES + src/AirfoilInfo.f90 + src/AirfoilInfo_Types.f90 +) + # FVW lib set(FVW_LIBS_SOURCES src/FVW.f90 @@ -54,12 +62,20 @@ set(FVW_LIBS_SOURCES src/FVW_VTK.f90 ) +# UnsteadyAero lib +add_library(uaaerolib ${UA_LIBS_SOURCES}) +target_link_libraries(uaaerolib afinfolib nwtclibs) + +# AirfoilInfo lib +add_library(afinfolib ${AFINFO_LIBS_SOURCES}) +target_link_libraries(afinfolib nwtclibs) + # this lib is only for the ctest add_library(fvwlib ${FVW_LIBS_SOURCES}) -target_link_libraries(fvwlib nwtclibs) +target_link_libraries(fvwlib uaaerolib afinfolib nwtclibs) add_library(aerodynlib ${AD_LIBS_SOURCES}) -target_link_libraries(aerodynlib fvwlib nwtclibs) +target_link_libraries(aerodynlib fvwlib uaaerolib afinfolib nwtclibs) # AeroDyn driver set(AD_DRIVER_SOURCES @@ -69,7 +85,7 @@ set(AD_DRIVER_SOURCES ) add_executable(aerodyn_driver ${AD_DRIVER_SOURCES}) -target_link_libraries(aerodyn_driver aerodynlib nwtclibs versioninfolib ${CMAKE_DL_LIBS}) +target_link_libraries(aerodyn_driver aerodynlib fvwlib uaaerolib afinfolib nwtclibs versioninfolib ${CMAKE_DL_LIBS}) # UnsteadyAero driver set(UA_DRIVER_SOURCES @@ -77,9 +93,9 @@ set(UA_DRIVER_SOURCES src/UA_Dvr_Subs.f90 ) add_executable(unsteadyaero_driver ${UA_DRIVER_SOURCES}) -target_link_libraries(unsteadyaero_driver aerodynlib nwtclibs versioninfolib ${CMAKE_DL_LIBS}) +target_link_libraries(unsteadyaero_driver aerodynlib fvwlib uaaerolib afinfolib nwtclibs versioninfolib ${CMAKE_DL_LIBS}) -install(TARGETS unsteadyaero_driver aerodyn_driver aerodynlib fvwlib +install(TARGETS unsteadyaero_driver aerodyn_driver aerodynlib fvwlib uaaerolib afinfolib EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/modules/aerodyn/tests/test_FVW_testsuite.F90 b/modules/aerodyn/tests/test_FVW_testsuite.F90 index 322edadb89..57a72ea24b 100644 --- a/modules/aerodyn/tests/test_FVW_testsuite.F90 +++ b/modules/aerodyn/tests/test_FVW_testsuite.F90 @@ -6,7 +6,6 @@ subroutine test_AD_FVW() use pFUnit_mod use NWTC_Num - use test_tools use FVW_Tests implicit none From 804a6bee65b956fc812765d63dc30e6a30e4131e Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 8 May 2020 09:48:41 -0600 Subject: [PATCH 159/190] Orcaflex interface: load DLL by default this had be set to off by default. It was only supposed to be used during testing. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fd6167fb4..e98907948c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ option(BUILD_SHARED_LIBS "Enable building shared libraries" off) option(DOUBLE_PRECISION "Treat REAL as double precision" on) option(USE_DLL_INTERFACE "Enable runtime loading of dynamic libraries" on) option(FPE_TRAP_ENABLED "Enable FPE trap in compiler options" off) -option(ORCA_DLL_LOAD "Enable OrcaFlex Library Load" off) +option(ORCA_DLL_LOAD "Enable OrcaFlex Library Load" on) option(BUILD_OPENFAST_CPP_API "Enable building OpenFAST - C++ API" off) option(OPENMP "Enable OpenMP support" off) From 59958cbde69bbf4892d0575f5d4cffb91eb3a816 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 10 Jun 2020 11:14:59 -0600 Subject: [PATCH 160/190] FVW: fix bug with vtk_fvw outputs --- modules/aerodyn/src/FVW.f90 | 1 - modules/aerodyn/src/FVW_IO.f90 | 14 +++++++++----- reg_tests/r-test | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index fc414bf2f1..b0f8a444b6 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -176,7 +176,6 @@ end subroutine FVW_Init ! ============================================================================== subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) - use FVW_VTK, only : vtk_misc_init type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 03e6ec1676..9dc0f23db9 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -183,7 +183,7 @@ END SUBROUTINE FVW_ReadInputFile !================================================= !> Export FVW variables to VTK -!! NOTE: when entering this function nNW and nFW has been ncremented by 1 +!! NOTE: when entering this function nNW and nFW has been incremented by 1 subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth, bladeFrame, HubOrientation, HubPosition) use FVW_VTK ! for all the vtk_* functions type(FVW_ParameterType), intent(in ) :: p !< Parameters @@ -211,10 +211,14 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth, bladeFrame, Hub type(FVW_VTK_Misc) :: mvtk - if (bladeFrame .and. present(HubOrientation) .and. present(HubPosition)) then - call set_vtk_coordinate_transform(HubOrientation,HubPosition,mvtk) - else - Call ProgAbort('Programming error in WrVTK_FVW call: Cannot use the WrVTK_FVW with bladeFrame==TRUE without the optional arguments of HubOrientation and HubPosition') + call vtk_misc_init(mvtk) + + if (bladeFrame) then + if (present(HubOrientation) .and. present(HubPosition)) then + call set_vtk_coordinate_transform(HubOrientation,HubPosition,mvtk) + else + Call ProgAbort('Programming error in WrVTK_FVW call: Cannot use the WrVTK_FVW with bladeFrame==TRUE without the optional arguments of HubOrientation and HubPosition') + endif endif if (DEV_VERSION) then diff --git a/reg_tests/r-test b/reg_tests/r-test index 9fff694eb7..889fd25108 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 9fff694eb70f267a1cc1f3e7783ae08e3514d5ce +Subproject commit 889fd25108a8baf9f3e75a7b346b9027bbefafa6 From f5a0767b4ff749937b6528f073ace301a9a94765 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 10 Jun 2020 11:18:42 -0600 Subject: [PATCH 161/190] FVW: merge r-test --- reg_tests/r-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reg_tests/r-test b/reg_tests/r-test index 889fd25108..1ffce9e7f6 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 889fd25108a8baf9f3e75a7b346b9027bbefafa6 +Subproject commit 1ffce9e7f6d627bf6cd5de55ff4ee19d49bb3746 From b692556b93e55053222ab189175bfd0bb1ec1941 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Thu, 11 Jun 2020 18:04:18 -0600 Subject: [PATCH 162/190] FVW: update OLAF documentation for readthedocs Updated with final version of TR --- docs/source/user/aerodyn-fvw/AppendixC.rst | 2 +- .../ExampleFiles/ExampleFile--OLAF.txt | 2 +- docs/source/user/aerodyn-fvw/FutureWork.rst | 2 +- docs/source/user/aerodyn-fvw/InputFiles.rst | 183 +++-- docs/source/user/aerodyn-fvw/Introduction.rst | 142 ++-- docs/source/user/aerodyn-fvw/OLAFTheory.rst | 718 +++++++++--------- docs/source/user/aerodyn-fvw/OutputFiles.rst | 9 +- docs/source/user/aerodyn-fvw/StateSpace.rst | 178 ++--- 8 files changed, 664 insertions(+), 572 deletions(-) diff --git a/docs/source/user/aerodyn-fvw/AppendixC.rst b/docs/source/user/aerodyn-fvw/AppendixC.rst index 11e12bd393..66613d90b7 100644 --- a/docs/source/user/aerodyn-fvw/AppendixC.rst +++ b/docs/source/user/aerodyn-fvw/AppendixC.rst @@ -31,5 +31,5 @@ the blade number. ============================ ============= =========================== Channel Name(s) Units Description ============================ ============= =========================== - :math:`Gam \beta B \alpha` :math:`m^2/s` Circulation along the blade + :math:`B \alpha N \beta Gam` :math:`m^2/s` Circulation along the blade ============================ ============= =========================== diff --git a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt index 99fcd21b04..e52dbe0717 100644 --- a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt +++ b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt @@ -1,7 +1,7 @@ --------------------------- FREE WAKE INPUT FILE ---------------------------------------------- Free wake input file for the BAR turbine --------------------------- GENERAL OPTIONS --------------------------------------------------- -5 IntMethod Integration method {4: 2nd order Predictor/Corrector, 5: Forward Euler 1st order, default: 5} (switch) +5 IntMethod Integration method {5: Forward Euler 1st order, default: 5} (switch) 0.2 DTfvw Time interval for wake propagation. {default: dtaero} (s) 5 FreeWakeStart Time when wake is free. (-) value = always free. {default: 0.0} (s) 2.0 FullCircStart Time at which full circulation is reached. {default: 0.0} (s) diff --git a/docs/source/user/aerodyn-fvw/FutureWork.rst b/docs/source/user/aerodyn-fvw/FutureWork.rst index 53820f8332..f755317574 100644 --- a/docs/source/user/aerodyn-fvw/FutureWork.rst +++ b/docs/source/user/aerodyn-fvw/FutureWork.rst @@ -28,4 +28,4 @@ The following list contains future work on OLAF software: - Code speed-up -- Dedicated dynamic stall model. +- Dedicated dynamic stall model diff --git a/docs/source/user/aerodyn-fvw/InputFiles.rst b/docs/source/user/aerodyn-fvw/InputFiles.rst index 36bd827692..163032ff52 100644 --- a/docs/source/user/aerodyn-fvw/InputFiles.rst +++ b/docs/source/user/aerodyn-fvw/InputFiles.rst @@ -17,12 +17,11 @@ OLAF Primary Input File The primary OLAF input file defines general free wake options, circulation model selection and specification, near- and far-wake length, and wake visualization -options. The file is organized into several functional sections. Each section -corresponds to an aspect of the OLAF model. For most parameters, the user may +options. Each section within the file corresponds to an aspect of the OLAF model. For most parameters, the user may specify the value "default" (with or without quotes), in which case a default value, defined below, is used by the program. -A sample OLAF primary input file is given in :ref:`OLAF-Primary-Input-File`. +See :numref:`OLAF-Primary-Input-File` for a sample OLAF primary input file. General Options ~~~~~~~~~~~~~~~ @@ -30,63 +29,66 @@ General Options **IntMethod** [switch] specifies which integration method will be used to convect the Lagrangian markers. There are four options: 1) fourth-order Runge-Kutta *[1]*, 2) fourth-order Adams-Bashforth *[2]*, 3) fourth-order -Adams-Bashforth-Moulton *[3]*, or 4) first-order forward Euler *[5]*. The -default value is *[5]*. These methods are specified in :numref:`sec:vortconv`. +Adams-Bashforth-Moulton *[3]*, and 4) first-order forward Euler *[5]*. The +default option is *[5]*. These methods are specified in :numref:`sec:vortconv`. **DTfvw** [sec] specifies the time interval at which the module will update the -wake. The time interval needs to be a multiple of the time step used by the glue -code. The blade circulation is still updated at each intermediate time steps -based on the intermediate blades positions and wind velocities. The default +wake. The time interval must be a multiple of the time step used by +*AeroDyn15*. The blade circulation is updated at each intermediate time +step based on the intermediate blades positions and wind velocities. The default value is :math:`dt_{aero}`, where :math:`dt_{aero}` is the time step used by AeroDyn. **FreeWakeStart** [sec] specifies at what time the wake evolution is classified as “free." Before this point is reached, the Lagrangian markers are simply convected with the freestream velocity. After this point, induced velocities are -computed and affect the marker convection. If a negative time is given, the wake -is “free" from the beginning of the simulation. The default value is :math:`0`. +computed and affect the marker convection. If a time less than or equal to zero +is given, the wake is “free" from the beginning of the simulation. The default +value is :math:`0`. **FullCircStart** [sec] specifies at what time the blade circulation reaches its full strength. If this value is specified to be :math:`>0`, the circulation is -multiplied by a factor equal to :math:`0` at :math:`t=0`, to a factor equal to -:math:`1` for :math:`t>\textit{FullCircStart}`. The default value is :math:`0`. +multiplied by a factor of :math:`0` at :math:`t=0` and linearly increasing to a +factor of :math:`1` for :math:`t>\textit{FullCircStart}`. The default +value is :math:`0`. Circulation Specifications ~~~~~~~~~~~~~~~~~~~~~~~~~~ **CircSolvMethod** [switch] specifies which circulation method is used. There are three options: 1) :math:`C_l`-based iterative procedure *[1]*, 2) no-flow -through *[2]*, or 3) prescribed *[3]*. The default value is *[1]*. These methods -are described in :numref:`sec:circ`. +through *[2]*, and 3) prescribed *[3]*. The default option is *[1]*. These +methods are described in :numref:`sec:circ`. **CircSolvConvCrit** [-] specifies the dimensionless convergence criteria used for solving the circulation. This variable is only used if -:math:`\textit{CircSolvMethod} = \textit{[1]}`. The default value is -:math:`0.01`, corresponding to :math:`1\%` error in the circulation between two -iterations. +*CircSolvMethod* = *[1]*. The default value is +:math:`0.001`, corresponding to :math:`0.1\%` error in the circulation between +two iterations. -**CircSolvRelaxation** [-] specifies the relaxation factor used for solving the -circulation. This variable is only used if :math:`\textit{CircSolvMethod} = -\textit{[1]}`. The default value is :math:`\alpha=0.1`. +**CircSolvRelaxation** [-] specifies the relaxation factor used to solve the +circulation. This variable is only used if *CircSolvMethod* = +*[1]*. The default value is :math:`0.1`. -**CircSolvMaxIter** [-] specifies the maximum number of iterations used for -solving the circulation. This variable is only used if -:math:`\textit{CircSolvMethod} = \textit{[1]}`. The default value is :math:`30`. +**CircSolvMaxIter** [-] specifies the maximum number of iterations used to solve +the circulation. This variable is only used if *CircSolvMethod* = *[1]*. The +default value is :math:`30`. **PrescribedCircFile** [quoted string] specifies the file containing the -prescribed blade circulation. This option is only used if -:math:`\textit{CircSolvMethod} = \textit{[2]}`. The circulation file format is -a delimited file with one header line and two columns. The first column is the -dimensionless radial position [r/R]; the second column is the bound circulation -value in [m\ :math:`^2`/s]. +prescribed blade circulation. This option is only used if *CircSolvMethod* = +*[3]*. The circulation file format is a delimited file with one header line and +two columns. The first column is the dimensionless radial position [r/R]; the +second column is the bound circulation value in [m\ :math:`^2`/s]. The radial +positions do not need to match the AeroDyn node locations. A sample prescribed +circulation file is given in :numref:`Prescribed-Circulation-Input-File`. -Wake Options -~~~~~~~~~~~~ -**nNWPanel** [-] specifies the number of time steps for which the near-wake -lattice is computed. In the future, the possibility to define this input as an -azimuthal span in degrees or a downstream distance in rotor diameter will be -considered. +Wake Extent and Discretization Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**nNWPanel** [-] specifies the number of FVW time steps (**DTfvw**) for which +the near-wake lattice is computed. In the future, this value will be defined as +an azimuthal span in degrees or a downstream distance in rotor diameter. **WakeLength** [D] specifies the length, in rotor diameters, of the far wake. The default value is :math:`8`. [1]_ @@ -101,44 +103,79 @@ are convected with the average velocity. The default value is :math:`6`. [2]_ far wake. The default value is *[False]*, specifying that the far wake consists only of the trailed vorticity from the root and tip vortices. -**DiffusionMethod** [switch] specifies which diffusion method is used to account -for viscous diffusion. There are two options: 1) no diffusion *[0]* or 2) the -core-spreading method *[1]*. The default value is *[0]*. +Wake Regularization and Diffusion Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Regularization options -~~~~~~~~~~~~~~~~~~~~~~ +**DiffusionMethod** [switch] specifies which diffusion method is used to account +for viscous diffusion. There are two options: 1) no diffusion *[0]* and 2) the +core-spreading method *[1]*. The default option is *[0]*. **RegDetMethod** [switch] specifies which method is used to determine the -regularization parameters. There are two options: 1) manual *[0]* or 2) +regularization parameters. There are two options: 1) manual *[0]* and 2) optimized *[1]*. The manual option requires the user to specify the parameters listed in this subsection. The optimized option determines the parameters for -the user. The default value is *[0]*. +the user. The default option is *[0]*. **RegFunction** [switch] specifies the regularization function used to remove the singularity of the vortex elements, as specified in -:numref:`sec:vortconv`. There are five options: (1) no correction *[0]*, -(2) the Rankine method *[1]*, (3) the Lamb-Oseen method *[2]*, (4) the Vatistas -method *[3]*, or (5) the denominator offset method *[4]*. The functions are -given in . The default value is *[3]*. - -**WakeRegMethod** [switch] specifies the method of determining viscous core radius (i.e., the -regularization parameter). There are three options: (1) constant *[1]*, (2) -stretching *[2]*, or (3) age *[3]*. The methods are described in -:numref:`sec:corerad`. The default -value is *[1]*. - -**WakeRegParam** [-] specifies the wake regularization parameter, which is the +:numref:`sec:vortconv`. There are five options: 1) no correction *[0]*, +2) the Rankine method *[1]*, 3) the Lamb-Oseen method *[2]*, 4) the Vatistas +method *[3]*, and 5) the denominator offset method *[4]*. The functions are +given in . The default option is *[3]*. + +**WakeRegMethod** [switch] specifies the method of determining viscous core +radius (i.e., the regularization parameter). There are three options: 1) +constant *[1]*, 2) stretching *[2]*, and 3) age *[3]*. The methods are +described in :numref:`sec:corerad`. The default option is *[1]*. + +**WakeRegParam** [m] specifies the wake regularization parameter, which is the regularization value used at the initialization of a vortex element. If the regularization method is “constant”, this value is used throughout the wake. -**BladeRegParam** [-] specifies the bound vorticity regularization parameter, +**BladeRegParam** [m] specifies the bound vorticity regularization parameter, which is the regularization value used for the vorticity elements bound to the blades. -**CoreSpreadEddyVisc** [-] specifies the eddy viscosity parameter :math:`\delta` -used for the core-spreading method (*DiffusionMethod* = *[1]*) or the -regularization method with age (*WakeRegMethod* = *[3]*). The variable -:math:`\delta` is described in :numref:`sec:corerad`. The default value is :math:`100`. +**CoreSpreadEddyVisc** [-] specifies the eddy viscosity parameter +:math:`\delta`. The parameter is used for the core-spreading method +(*DiffusionMethod* = *[1]*) and the regularization method with age +(*WakeRegMethod* = *[3]*). The variable :math:`\delta` is described in +:numref:`sec:corerad`. The default value is :math:`100`. + +Wake Treatment Options +~~~~~~~~~~~~~~~~~~~~~~ + +**TwrShadowOnWake** [flag] specifies whether the tower potential flow and tower +shadow have an influence on the wake convection. The tower shadow model, when +activated in AeroDyn, always has an influence on the lifting line, hence the +induction and loads on the blade. This option only concerns the wake. The +default option is *[False]*. + +**ShearVorticityModel** [switch] specifies whether shear vorticity is modeled in +addition to the sheared inflow prescribed by *InflowWind*. There are two +options: 1) no treatment *[0]* and 2) mirrored vorticity *[1]*. The mirrored +vorticity accounts for the ground effect. Dedicated options to account for the +shear vorticity will be implemented at a later time. The shear velocity profile +is handled by *InflowWind* irrespective of this input. The default option is +*[0]*. + + +Speedup Options +~~~~~~~~~~~~~~~ + +**VelocityMethod** [switch] specifies the method used to determine the velocity. +There are two options: 1) Biot-Savart law applied to the vortex segments *[1]* +and 2) tree formulation using a particle representation *[2]*. The default +option is *[1]*. + +**TreeBranchFactor** [-] specifies the dimensionless distance, in branch radius, +above which a multipole calculation is used instead of a direct evaluation. This +option is only used in conjunction with the tree code +(*VelocityMethod* = *[2]*). + +**PartPerSegment** [-] specifies the number of particles that are used when a +vortex segment is represented by vortex particles. The default value is +:math:`1`. Output Options ~~~~~~~~~~~~~~ @@ -146,23 +183,27 @@ Output Options **WrVTK** [flag] specifies if Visualization Toolkit (VTK) visualization files are to be written out. *WrVTK* = *[0]* does not write out any VTK files. *WrVTK* = *[1]* outputs a VTK file at every time step. The outputs are written in the -folder, ``vtk_fvw.`` +folder, ``vtk_fvw.`` The parameters *WrVTK*, *VTKCoord*, and *VTK_fps* are +independent of the glue code VTK output options. + **VTKBlades** [-] specifies how many blade VTK files are to be written out. *VTKBlades* :math:`= n` outputs VTK files for :math:`n` blades, with :math:`0` being an acceptable value. The default value is :math:`1`. **VTKCoord** [switch] specifies in which coordinate system the VTK files are -written. There are two options: 1) global coordinate system *[1]* or 2) hub -coordinate system *[2]*. The default value is *[1]*. +written. There are two options: 1) global coordinate system *[1]* and 2) hub +coordinate system *[2]*. The default option is *[1]*. **VTK_fps** [:math:`1`/sec] specifies the output frequency of the VTK files. The -value provided is rounded to the nearest allowable multiple of the time step. +provided value is rounded to the nearest allowable multiple of the time step. The default value is :math:`1/dt_\text{fvw}`. Specifying *VTK_fps* = *[all]*, -will be equivalent to using the value :math:`1/dt_\text{aero}`. +is equivalent to using the value :math:`1/dt_\text{aero}`. AeroDyn15 Input File -------------------- +Input file modifications +~~~~~~~~~~~~~~~~~~~~~~~~ As OLAF is incorporated into the *AeroDyn15* module, a wake computation option has been added to the *AeroDyn15* input file and a line has been added. These @@ -170,11 +211,25 @@ additions are as follows. **WakeMod** specifies the type of wake model that is used. *WakeMod* = *[3]* has been added to allow the user to switch from the traditional BEM method to the -FVW method. +OLAF method. **FVWFile** [string] specifies the OLAF module file, the path is relative to the AeroDyn file, unless an absolute path is provided. + +Relevant sections +~~~~~~~~~~~~~~~~~ +The BEM options (e.g. tip-loss, skew, and dynamic models) are read and discarded +when *WakeMod* = *[3]*. The following sections and parameters remain relevant and +are used by the vortex code: + + - general options (e.g., airfoil and tower modeling); + - environmental conditions; + - dynamic stall model options; + - airfoil and blade information; + - tower aerodynamics; and + - outputs. + .. [1] At present, this variable is called nFWPanel and specified as the number of far wake panels. This will be changed soon. diff --git a/docs/source/user/aerodyn-fvw/Introduction.rst b/docs/source/user/aerodyn-fvw/Introduction.rst index 67420ff773..c081ddf57f 100644 --- a/docs/source/user/aerodyn-fvw/Introduction.rst +++ b/docs/source/user/aerodyn-fvw/Introduction.rst @@ -2,6 +2,49 @@ Introduction ============ +Over the past few decades, substantial reductions in the cost of wind energy +have come from large increases in rotor size. One important consideration for +such large turbines is increased blade flexibility. In particular, large blade +deflections may lead to a swept area that deviates significantly from the rotor +plane. Such deviations violate assumptions used by common aerodynamic models, +such as the blade element momentum (BEM) method. Such methods rely on +actuator-disk assumptions that are only valid for axisymmetric rotor loads +contained in a plane. Large blade deflections may also cause near wake of the +turbine to diverge from a uniform helical shape. Further, interactions between +turbine blades and the local near wake may increase, thus violating assumptions +of models that do not account for the position and dynamics of the near wake. +Additionally, highly flexible blades will likely cause increased unsteadiness +and three-dimensionality of aerodynamic effects, increasing the importance of +accurate and robust dynamic stall models. There are many other complex wind +turbine situations that violate simple engineering assumptions. Such situations +include obtaining accurate aerodynamic loads for nonstraight blade geometries +(e.g., built-in curvature or sweep); skewed flow caused by yawed inflow or +turbine tilt; and large rotor motion as a result of placing the turbine atop a +compliant offshore floating platform. + +Higher-fidelity aerodynamic models are necessary to account for the increased +complexity of flexible and floating rotors. Although computational fluid +dynamics (CFD) methods are able to capture such features, their computational +cost limits the number of simulations that can be feasibly performed, which is +an important consideration in load analysis for turbine design. FVW methods are +less computationally expensive than CFD methods while modeling similarly complex +physics. As opposed to the BEM methods, FVW methods do not rely on ad-hoc +engineering models to account for dynamic inflow, skewed wake, tip losses, or +ground effects. These effects are inherently part of the model. Numerous +vorticity-based tools have been implemented, ranging from the early treatments +by Rosenhead (:cite:`Rosenhead31_1`), the formulation of vortex particle methods +by Winckelmans and Leonard (:cite:`Winckelmans93_1`), to the recent mixed +Eulerian-Lagrangian compressible formulations of +Papadakis (:cite:`Papadakis14_1`). Examples of long-standing codes that have been +applied in the field of wind energy are GENUVP (:cite:`Voutsinas06_1`), using +vortex particles methods, and AWSM (:cite:`Garrel03_1`), using vortex filament +methods. Both tools have successfully been coupled to structural solvers. The +method was extended by Branlard et al. (:cite:`Branlard15_1`) to consistently use +vortex methods to perform aero-elastic simulations of wind turbines in sheared +and turbulent inflow. Most formulations rely on a lifting-line representation of +the blades, but recently, a viscous-inviscid representation was used in +combination with a structural solver (:cite:`Miras17_1`). + cOnvecting LAgrangian Filaments (OLAF) is a free vortex wake (FVW) module used to compute the aerodynamic forces on moving two- or three-bladed horizontal-axis wind turbines. This module has been incorporated into the National Renewable @@ -27,9 +70,9 @@ Figures :numref:`figOpenFAST_a` and :numref:`figOpenFAST_b`. :width: 100% :align: center - OLAF and BEM integration with AeroDyn + OLAF and BEM integration with *AeroDyn15* -Incorporating the FVW module within OpenFAST allows for the modeling of +Incorporating the OLAF module within OpenFAST allows for the modeling of highly flexible turbines along with the aero-hydro-servo-elastic response capabilities of OpenFAST. The OLAF module follows the requirements of the OpenFAST modularization framework  @@ -40,7 +83,7 @@ is characterized by a distribution of bound circulation. The spatial and time variation of the bound circulation results in free vorticity being emitted in the wake. OLAF solves for the turbine wake in a time-accurate manner, which allows the vortices to convect, stretch, and diffuse. The -FVW model is based on a Lagrangian approach, in which the turbine wake +OLAF model is based on a Lagrangian approach, in which the turbine wake is discretized into Lagrangian markers. There are many methods of representing the wake with Lagrangian markers (:cite:`Branlard17_1`). In this work, a hybrid @@ -61,57 +104,54 @@ age, :math:`\zeta`, and azimuthal position, :math:`\psi`. A lattice method is used in the near wake of the blade. The near wake spans over a user-specified angle or distance for nonrotating cases. Though past research has indicated that a near-wake region of :math:`30^\circ` is -sufficient (:cite:`Leishman_book,Ananthan02_1`), it has -been shown that a larger near wake is required for high thrust and other -challenging conditions. After this period, the wake is assumed to -instantaneously roll up into a tip vortex and, optionally, a root -vortex, which are assumed to be the most dominant features for the -remainder of the wake (:cite:`Leishman02_1`). Each -Lagrangian marker is connected to adjacent markers by straight-line -vortex filaments, approximated to second-order -accuracy (:cite:`Gupta05_1`). The wake is discretized based -on the spanwise location of the blade sections and a specified time step -(:math:`dt`), which may be different from the time step of AeroDyn. -After an optional initialization period, the wake is allowed to move and -distort, thus changing the wake structure as the markers are convected -downstream. To limit computational expense, the tip vortex is truncated -after a specified distance (:math:`d_\text{trunc}`) downstream of the -turbine. The wake truncation violates Helmholtz’s first law and hence -introduces an erroneous boundary condition. To alleviate this, the wake -is "frozen" in a buffer zone between a distance, -:math:`d_\text{buffer}`, and the distance, :math:`d_\text{trunc}`. In -this buffer zone, the markers convect at the average ambient velocity. -In this way, truncation error is -minimized (:cite:`Leishman02_1`). The buffer zone is +sufficient (:cite:`Leishman_book,Ananthan02_1`), it has been shown that a larger +near wake is required for high thrust and other challenging conditions. After +the near wake region, the wake is assumed to instantaneously roll up into a tip +vortex and a root vortex, which are assumed to be the most dominant features for +the remainder of the wake (:cite:`Leishman02_1`). Each Lagrangian marker is +connected to adjacent markers by straight-line vortex filaments, approximated to +second-order accuracy (:cite:`Gupta05_1`). The wake is discretized based on the +spanwise location of the blade sections and a specified time step (:math:`dt`), +which may be different from the time step of AeroDyn. After an optional +initialization period, the wake is allowed to move and distort, thus changing +the wake structure as the markers are convected downstream. To limit +computational expense, the root and tip vortices are truncated after a specified +distance (**WakeLength**) downstream from the turbine. The wake truncation +violates Helmholtz's first law and hence introduces an erroneous boundary +condition. To alleviate this, the wake is "frozen" in a buffer zone between a +specified buffer distance, **FreeWakeLength**, and **WakeLength**. In this +buffer zone, the markers convect at the average ambient velocity. In this way, +truncation error is minimized~(:cite:`Leishman02_1`). The buffer zone is typically chosen as the convected distance over one rotor revolution. As part of OpenFAST, induced velocities at the lifting line/blade are -transferred to *AeroDyn15* and used to compute the effective blade angle -of attack at each blade section, which is then used to compute the -aerodynamic forces on the blades. The FVW method returns the same -information as the BEM method, but allows for more accurate calculations -in areas where BEM assumptions are violated. As the FVW method is more -computationally expensive than BEM, both methods remain available in -OpenFAST, and the user may specify in the *AeroDyn15* input file which -method is used. +transferred to *AeroDyn15* and used to compute the effective blade angle of +attack at each blade section, which is then used to compute the aerodynamic +forces on the blades. The OLAF method returns the same information as the BEM +method, but allows for more accurate calculations in areas where BEM assumptions +are violated, such as those discussed above. As the OLAF method is more +computationally expensive than BEM, both methods remain available in OpenFAST, +and the user may specify in the *AeroDyn15* input file which method is +used. The OLAF input file defines the wake convection and circulation solution -methods; wake size and length options; the Lagrangian marker -regularization (viscous core) method; and other simulation and output -parameters. The extent of the near and far wake are specified by a -nondimensional length in terms of rotor diameter. Different -regularization functions for the vortex elements are available. -Additionally, different methods to compute the regularization parameters -of the bound and wake vorticity may be selected. In particular, viscous -diffusion may be accounted for by dynamically changing the -regularization parameter. Wake visualization output options are also -available. - -This document is organized as follows. Section 2 details downloading, -compiling, and running OLAF on common operating systems. Section 3 -describes the OLAF input file and modifications to the *AeroDyn15* input -file. Section 4 details the OLAF output file. Section 5 provides an -overview of the OLAF theory, including the free vortex wake method as -well as integration into the *AeroDyn15* module. Section 6 presents -future work. Example input files and a list of output channels are +methods; wake size and length options; Lagrangian marker regularization (viscous +core) method; and other simulation and output parameters. The extents of the +near and far wakes are specified by a nondimensional length in terms of rotor +diameter. Different regularization functions for the vortex elements are +available. Additionally, different methods to compute the regularization +parameters of the bound and wake vorticity may be selected. In particular, +viscous diffusion may be accounted for by dynamically changing the +regularization parameter. Wake visualization output options are also available. + +This document is organized as follows. :numref:`Running-OLAF` covers +downloading, compiling, and running OLAF. :numref:`Input-Files` describes the +OLAF input file and modifications to the *AeroDyn15* input file. +:numref:`Output-Files` details the OLAF output file. :numref:`OLAF-Theory` +provides an overview of the OLAF theory, including the free vortex wake method +as well as integration into the *AeroDyn15* module. :numref:`future-work` +presents future work. Example input files and a list of output channels are detailed in Appendices A, B, and C. + + + diff --git a/docs/source/user/aerodyn-fvw/OLAFTheory.rst b/docs/source/user/aerodyn-fvw/OLAFTheory.rst index 79575faf28..6396497e15 100644 --- a/docs/source/user/aerodyn-fvw/OLAFTheory.rst +++ b/docs/source/user/aerodyn-fvw/OLAFTheory.rst @@ -1,16 +1,16 @@ -.. _sec:FVW: +.. _OLAF-Theory: OLAF Theory =========== -This section details the FVW method and provides an overview of the +This section details the OLAF method and provides an overview of the computational method, followed by a brief explanation of its integration with OpenFAST. .. _sec:vorticityformulation: -Introduction - Vorticity formulation +Introduction - Vorticity Formulation ------------------------------------ The vorticity equation for incompressible homogeneous flows in the @@ -31,7 +31,7 @@ vorticity. Different approximations are introduced to ease its resolution, such as projecting the vorticity onto a discrete number of vortex elements (here vortex filaments), and separately treating the convection and diffusion steps, known as viscous-splitting. Several -complications arise from the method, in particular, the discretization +complications arise from the method; in particular, the discretization requires a regularization of the vorticity field (or velocity field) to ensure a smooth approximation. @@ -40,7 +40,7 @@ vorticity formulation as well. This vorticity is bound to the blade and has a circulation associated with the lift force. A lifting-line formulation is used here to model the bound vorticity. -The different models of the free vortex code implemented are described +The different models of the implemented free vortex code are described in the following sections. .. _sec:discretization: @@ -50,9 +50,9 @@ Discretization - Projection The numerical method uses a finite number of states to model the continuous vorticity distribution. To achieve this, the vorticity -distribution is projected onto basis function which will be referred to +distribution is projected onto basis function which is referred to as vortex elements. Vortex filaments are here used as elements that -represents the vorticity field. A vortex filaments is delimited by two +represents the vorticity field. A vortex filament is delimited by two points and hence assumes a direction formed by these two points. A vorticity tube is oriented along the unit vector :math:`\vec{e}_x` of cross section :math:`dS` and length :math:`l`. It can then be @@ -67,23 +67,237 @@ are the same and related by: :label: OmegaGamma where :math:`\vec{\Gamma}` is the circulation intensity of the vortex -filament. If the vorticity tubes are complex and occupy a large volumes, +filament. If the vorticity tubes are complex and occupy a large volume, the projection onto vortex filaments is difficult and the projection -onto vortex particle is more adapted. Yet, assuming the wake is confined +onto vortex particle is more appropriate. Assuming the wake is confined to a thin vorticity layer which defines a velocity jump of know direction, it is possible to approximate the wake vorticity sheet as a mesh of vortex filaments. This is the basis of vortex filament wake methods. Vortex filaments are a singular representation of the vorticity -field, since they occupy a line instead of a volume. To better represent +field, as they occupy a line instead of a volume. To better represent the vorticity field, the filaments are “inflated”, a process referred to as regularization (see :numref:`sec:Regularization`). The regularization of the vorticity field also regularizes the velocity field and avoids the singularities that would otherwise occur. + +.. _sec:circ: + +Lifting-Line Representation +--------------------------- + +The code relies on a lifting-line formulation. Lifting-line methods effectively +lump the loads at each cross-section of the blade onto the mean line of the +blade and do not account directly for the geometry of each cross-section. In the +vorticity-based version of the lifting-line method, the blade is represented by +a line of varying circulation. The line follows the motion of the blade and is +referred to as “bound” circulation. The bound circulation does not follow the +same dynamic equation as the free vorticity of the wake. Instead, the intensity +is linked to airfoil lift via the Kutta-Joukowski theorem. Spanwise variation of +the bound circulation results in vorticity being emitted into the the wake. This +is referred to as “trailed vorticity”. Time changes of the bound circulation are +also emitted in the wake, referred to as “shed” vorticity. The subsequent +paragraphs describe the representation of the bound vorticity. + +Lifting-Line Panels and Emitted Wake Panels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The lifting-line and wake representation is illustrated in +:numref:`fig:VortexLatticeMethod`. The blade lifting-line is discretized into a +finite number of panels, each of them forming a four sided vortex rings. The +spanwise discretization follows the discretization of the AeroDyn blade input +file. The number of spanwise panels, :math:`n_\text{LL}`, is one less than the +total number of AeroDyn nodes, **NumBlNds**. The sides of the panels coincide +with the lifting-line and the trailing edge of the blade. The lifting-line is +currently defined as the 1/4 chord location from the leading edge (LE). More +details on the panelling is provided in :numref:`sec:Panelling`. At a given time +step, the circulation of each lifting-line panel is determined according to one +of the three methods developed in :numref:`sec:CirculationMethods`. At the end +of the time step, the circulation of each lifting-line panel is emitted into the +wake, forming free vorticity panels. To satisfy the Kutta condition, the +circulation of the first near wake panel and the bound circulation are +equivalent (see :numref:`fig:VortexLatticeMethod` b). The wake panels model the +thin shear layer resulting from the continuation of the blade boundary layer. +This shear layer can be modelled using a continuous distribution of vortex +doublets. A constant doublet strength is assumed on each panel, which in turn is +equivalent to a vortex ring of constant circulation. + +.. figure:: Schematics/VortexLatticeMethod.png + :alt: Wake and lifting-line vorticity discretized into vortex ring panels. + :name: fig:VortexLatticeMethod + :width: 100.0% + + Wake and lifting-line vorticity discretized into vortex ring panels. + (a) Overview. (b) Cross-sectional view, defining the leading-edge, + trailing edge, and lifting-line. (c) Circulation of panels and + corresponding circulation for vorticity segments between panels. (d) + Geometrical quantities for a lifting-line panel. + +The current implementation stores the positions and circulations of the panel +corner points. In the vortex ring formulation, the boundary between two panels +corresponds to a vortex segment of intensity equal to the difference of +circulation between the two panels. The convention used to define the segment +intensity based on the panels intensity is shown in +:numref:`fig:VortexLatticeMethod` c. Since the circulation of the bound panels +and the first row of near wake panels are equal, the vortex segments located on +the trailing edge have no circulation. + +.. _sec:Panelling: + +Panelling +~~~~~~~~~ + +The definitions used for the panelling of the blade are given in +:numref:`fig:VortexLatticeMethod` d, following the notations of van +Garrel (:cite:`Garrel03_1`). The leading edge and +trailing edge (TE) locations are directly obtained from the AeroDyn +mesh. At two spanwise locations, the LE and TE define the corner points: +:math:`\vec{x}_1`, :math:`\vec{x}_2`, :math:`\vec{x}_3`, and +:math:`\vec{x}_4`. The current implementation assumes that the +aerodynamic center, the lifting-line, and the 1/4 chord location all +coincide. For a given panel, the lifting-line is then delimited by the +points :math:`\vec{x}_9= 3/4\,\vec{x}_1 + 1/4\, \vec{x}_2` and +:math:`\vec{x}_{10}=3/4\,\vec{x}_4 + 1/4\, \vec{x}_3`. The mid points of +the four panel sides are noted :math:`\vec{x}_5`, :math:`\vec{x}_6`, +:math:`\vec{x}_7`, and :math:`\vec{x}_8`. The lifting-line vector +(:math:`\vec{dl}`) as well as the vectors tangential (:math:`\vec{T}`) +and normal (:math:`\vec{N}`) to the panel are defined as: + +.. math:: + \begin{aligned} + \vec{dl} = \vec{x}_{10}-\vec{x}_9 + ,\qquad + \vec{T} = \frac{\vec{x}_6-\vec{x}_8}{|\vec{x}_6-\vec{x}_8|} + ,\qquad + \vec{N} = \frac{\vec{T}\times\vec{dl}}{|\vec{T}\times\vec{dl}|} + \end{aligned} + :label: eq:GeometricDefinitions + +The area of the panel is obtained as :math:`dA = +|(\vec{x}_6-\vec{x}_8)\times(\vec{x}_{7}-\vec{x}_5)|`. For +**CircSolvMethod=[1]**, the control points are located on the lifting-line at +the location :math:`\vec{x}_9+\eta_j \vec{dl}`. The factor :math:`\eta_j` is +determined based on the full-cosine approximation of van Garrel. This is based +on the spanwise widths of the current panel, :math:`w_j`, and the neighboring +panels :math:`w_{j-1}` and :math:`w_{j+1}`: + +.. math:: + \begin{aligned} + \eta_j=\frac{1}{4}\left[\frac{w_{j-1}}{w_{j-1}+w_j} + \frac{w_j}{w_j+w_{j+1}} +1 \right] + ,\ j=2..n-1 + ,\quad + \eta_1 = \frac{w_1}{w_1+w_2} + ,\quad + \eta_{n} = \frac{w_{n-1}}{w_{n-1}+w_{n}} + \end{aligned} + +For an equidistant spacing, this discretization places the control points at the +middle of the lifting-line (:math:`\eta=0.5`). Theoretical circulation results +for an elliptic wing with a cosine spacing are retrieved with such +discretization since it places the control points closer to stronger trailing +segments at the wing extremities (see e.g. :cite:`Kerwin:lecturenotes`). + +.. _sec:CirculationMethods: + +Circulation Solving Methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Three methods are implemented to determine the bound circulation strength. They +are selected using the input **CircSolvMethod**, and are presented in the +following sections. + +Cl-Based Iterative Method +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Cl-based iterative method determines the circulation within a +nonlinear iterative solver that makes use of the polar data at each +control point located on the lifting line. The algorithm ensures that +the lift obtained using the angle of attack and the polar data matches +the lift obtained with the Kutta-Joukowski theorem. At present, it is +the preferred method to compute the circulation along the blade span. It is +selected with **CircSolvMethod=[1]**. The method is described in the work from +van Garrel (:cite:`Garrel03_1`). The algorithm is implemented in at iterative +approach using the following steps: + +#. The circulation distribution from the previous time step is used as a + guessed circulation, :math:`\Gamma_\text{prev}`. + +#. The velocity at each control points :math:`j` is computed as the sum + of the wind velocity, the structural velocity, and the velocity + induced by all the vorticity in the domain, evaluated at the control + point location. + + .. math:: + \begin{aligned} + \vec{v}_j = \vec{V}_0 - \vec{V}_\text{elast} + \vec{v}_{\omega,\text{free}} + \vec{v}_{\Gamma_{ll}} + \end{aligned} + + :math:`\vec{v}_{\omega,\text{free}}` is the velocity induced by all free + vortex filaments, as introduced in Eq. :eq:`eq:eq510` . The contribution + of :math:`\vec{v}_{\Gamma_{ll}}` comes from the lifting-line panels and + the first row of near wake panels, for which the circulation is set to + :math:`\Gamma_\text{prev}` + +#. The circulation for all lifting-line panels :math:`j` is obtained as + follows. + + .. math:: + \begin{aligned} + \Gamma_{ll,j} =\frac{1}{2} C_{l,j}(\alpha_j) \frac{\left[ (\vec{v}_j \cdot \vec{N})^2 + (\vec{v}_j \cdot \vec{T})^2\right]^2\,dA}{ + \sqrt{\left[(\vec{v}_j\times \vec{dl})\cdot\vec{N}\right]^2 + \left[(\vec{v}_j\times \vec{dl})\cdot\vec{T}\right]^2} + } %\label{eq:} + ,\quad\text{with} + \quad + \alpha_j = \operatorname{atan}\left(\frac{\vec{v}_j\cdot\vec{N}}{\vec{v}_j \cdot \vec{T}} \right) + \end{aligned} + + The function :math:`C_{l,j}` is the lift coefficient obtained from + the polar data of blade section :math:`j` and :math:`\alpha_j` is the + angle of attack at the control point. + +#. The new circulation is set using the relaxation factor + :math:`k_\text{relax}` (**CircSolvRelaxation**): + + .. math:: + \begin{aligned} + \Gamma_\text{new}= \Gamma_\text{prev} + k_\text{relax} \Delta \Gamma + ,\qquad + \Delta \Gamma = \Gamma_{ll} - \Gamma_\text{prev} %\label{eq:} + \end{aligned} + +#. Convergence is checked using the criterion :math:`k_\text{crit}` + (**CircSolvConvCrit**): + + .. math:: + \begin{aligned} + \frac{ \operatorname{max}(|\Delta \Gamma|}{\operatorname{mean}(|\Gamma_\text{new}|)} < k_\text{crit} + \end{aligned} + + If convergence is not reached, steps 2-5 are repeated using + :math:`\Gamma_\text{new}` as the guessed circulation + :math:`\Gamma_\text{prev}`. + +No-flow-through Method +^^^^^^^^^^^^^^^^^^^^^^ + +A Weissinger-L-based representation (:cite:`Weissinger47_1`) +of the lifting surface is also +available (:cite:`Bagai94_1,Gupta06_1,Ribera07_1`). In this +method, the circulation is solved by satisfying a no-flow through +condition at the 1/4-chord points. It is selected with **CircSolvMethod=[2]**. + +Prescribed Circulation +^^^^^^^^^^^^^^^^^^^^^^ + +The final available method prescribes a constant circulation. A user +specified spanwise distribution of circulation is prescribed onto the +blades. It is selected with **CircSolvMethod=[3]**. + + .. _sec:vortconv: -Vortex Convection ------------------ +Free Vorticity Convection +------------------------- The governing equation of motion for a vortex filament is given by the convection equation of a Lagrangian marker: @@ -92,14 +306,25 @@ convection equation of a Lagrangian marker: \frac{d\vec{r}}{dt}=\vec{V}(\vec{r},t) :label: VortFilCart -where :math:`\vec{r}` is the position of a Lagrangian marker. The -Lagrangian convection of the filaments stretches the filaments and thus -automatically accounts for the strain part of the vorticity equation. +where :math:`\vec{r}` is the position of a Lagrangian marker. The Lagrangian +markers are the end points of the vortex filaments. The Lagrangian convection of +the filaments stretches the filaments and thus automatically accounts for strain +in the vorticity equation. + +At present, a first-order forward Euler method is used to numerically solve the +left-hand side of Eq. :eq:`VortFilCart` for the vortex filament location +(**IntMethod=[5]**). This is an explicit method solved using +Eq. :eq:`eq:Euler`. + +.. math:: + \vec{r} = \vec{r} + \vec{V} \Delta t + :label: eq:Euler + .. _sec:vortconvPolar: -Vortex Convection in Polar Coordinates --------------------------------------- +Free Vorticity Convection in Polar Coordinates +---------------------------------------------- The governing equation of motion for a vortex filament is given by: @@ -118,15 +343,14 @@ where :math:`d\psi/dt=\Omega` and :math:`\vec{r}(\psi,\zeta)` is the position vector of a Lagrangian marker, and :math:`\vec{V}[\vec{r}(\psi,\zeta)]` is the velocity. -At present, first-order forward Euler method is used to numerically -solve the left-hand side of -Eq. :eq:`VortFil_expanded` for the vortex-filament -location [**IntMethod=5**]. This is an explicit method solved using -Eq. :eq:`Euler`. +.. + At present, first-order forward Euler method is used to numerically solve the + left-hand side of Eq. :eq:`VortFil_expanded` for the vortex-filament location + [**IntMethod=5**]. This is an explicit method solved using Eq. :eq:`Euler`. -.. math:: - \vec{r}(\psi+\Delta\psi_i,\zeta+\Delta\zeta) = \vec{r}(\psi,\zeta) + \vec{V}(\psi,\zeta) \Delta t - :label: Euler + .. math:: + \vec{r}(\psi+\Delta\psi_i,\zeta+\Delta\zeta) = \vec{r}(\psi,\zeta) + \vec{V}(\psi,\zeta) \Delta t + :label: Euler Induced Velocity and Velocity Field ----------------------------------- @@ -145,12 +369,11 @@ elements (:cite:`Leishman02_1`): :label: BiotSavart Here, :math:`\Gamma` is the circulation strength of the filament, -:math:`\vec{dl}` is an elementary length along the filament, and -:math:`\vec{r}` is the vector between a point on the filament and the -control point :math:`\vec{x}`, and :math:`r=|\vec{r}|` is the norm of -the vector. The integration of the Biot-Savart law along the filament -length, delimited by the points :math:`\vec{x}_1` and :math:`\vec{x}_2` -leads to: +:math:`\vec{dl}` is an elementary length along the filament, :math:`\vec{r}` is +the vector between a point on the filament and the control point +:math:`\vec{x}`, and :math:`r=|\vec{r}|` is the norm of the vector. The +integration of the Biot-Savart law along the filament length, delimited by the +points :math:`\vec{x}_1` and :math:`\vec{x}_2` leads to: .. math:: \begin{aligned} @@ -159,12 +382,11 @@ leads to: \end{aligned} :label: eq:BiotSavartSegment -with :math:`\vec{r}_1= \vec{x}-\vec{x}_1` and -:math:`\vec{r}_2= \vec{x}-\vec{x}_2`. The factor :math:`F_\nu` is a -regularization parameter that will be discussed in -:numref:`sec:RegularizationFunction`. The filament length -is noted :math:`r_0`, where :math:`\vec{r}_0= \vec{x}_2-\vec{x}_1`. The -distance orthogonal to the filament is: +with :math:`\vec{r}_1= \vec{x}-\vec{x}_1` and :math:`\vec{r}_2= +\vec{x}-\vec{x}_2`. The factor :math:`F_\nu` is a regularization parameter, +discussed in :numref:`sec:RegularizationFunction`. :math:`r_0` is the filament +length, where :math:`\vec{r}_0= \vec{x}_2-\vec{x}_1`. The distance orthogonal to +the filament is: .. math:: \begin{aligned} @@ -173,17 +395,20 @@ distance orthogonal to the filament is: The velocity at any point of the domain is obtained by superposition of the velocity induced by all vortex filaments, and by superposition of -the main flow, :math:`\vec{V}_0`, (here assumed divergence free): +the primary flow, :math:`\vec{V}_0`, (here assumed divergence free): .. math:: \begin{aligned} \vec{V}(\vec{x}) = \vec{V}_0(\vec{x}) + \vec{v}_\omega(\vec{x}), \quad\text{with}\quad \vec{v}_\omega(\vec{x}) = \sum_{k} \vec{v}_k(\vec{x}) \end{aligned} + :label: eq:eq510 where the sum is over all the vortex filaments, each of intensity -:math:`\Gamma_k`. The intensity of each filament is determined by -spanwise and time changes of the bound circulation, as discussed in -:numref:`sec:circ`. +:math:`\Gamma_k`. The intensity of each filament is determined by spanwise and +time changes of the bound circulation, as discussed in :numref:`sec:circ`. In +tree-based methods, the sum over all vortex elements is reduced by lumping +together the elements that are far away from the control points. + .. _sec:Regularization: @@ -193,70 +418,62 @@ Regularization Regularization and viscous diffusion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The singularity that occurs in Eq. :eq:`BiotSavart` -greatly affects the numerical accuracy of vortex methods. By -regularizing the “1-over-r” kernel of the Biot-Savart law, it is -possible to obtain a numerical method that converges to the -Navier-Stokes equations. The regularization is used to improve the -regularity of the discrete vorticity field, as compared to the “true” -continuous vorticity field. This regularization is usually obtained by -convolution with a smooth function. In this case, the regularization of -the vorticity field and the velocity field are the same. Some -engineering models also perform regularization by directly introducing -additional terms in the denominator of the Biot-Savart velocity kernel. -The factor, :math:`F_\nu`, was introduced in -Eq. :eq:`eq:BiotSavartSegment` to account for -this regularization. +The singularity that occurs in Eq. :eq:`BiotSavart` greatly affects the +numerical accuracy of vortex methods. By regularizing the “1-over-r” kernel of +the Biot-Savart law, it is possible to obtain a numerical method that converges +to the Navier-Stokes equations. The regularization is used to improve the +regularity of the discrete vorticity field, as compared to the “true” continuous +vorticity field. This regularization is usually obtained by convolution with a +smooth function. In this case, the regularization of the vorticity field and the +velocity field are the same. Some engineering models also perform regularization +by directly introducing additional terms in the denominator of the Biot-Savart +velocity kernel. The factor, :math:`F_\nu`, was introduced in +Eq. :eq:`eq:BiotSavartSegment` to account for this regularization. In the convergence proofs of vortex methods, regularization and viscous -diffusion are two distinct aspects. It is yet common practice in vortex -filament methods to blur the notion of regularization with the notion of -viscous diffusion. Indeed, for a physical vortex filament, viscous -effects prevent the singularity from occurring and diffuse the vortex -strength with time. The circular zone where the velocity drops to zero -around the vortex is referred to as the vortex core. An increase of -length of the vortex segment will result in a decrease of the vortex -core radius, and conversely for a decrease of length. Diffusion, on the -other hand, continually spreads the vortex radially. - -Because of the previously mentioned analogy, practitioners of vortex -filament methods often refer to regularization as “viscous-core” models -and regularization parameters as “core-radii.” Additionally, viscous -diffusion is often introduced by modifying the regularization parameter -in space and time instead of solving the diffusion from the vorticity -equation. The distinction is made explicit in this document when -clarification is required, but a loose terminology is used when the -context is clear enough. +diffusion are two distinct aspects. It is common practice in vortex filament +methods to blur the notion of regularization with the notion of viscous +diffusion. Indeed, for a physical vortex filament, viscous effects prevent the +singularity from occurring and diffuse the vortex strength with time. The +circular zone where the velocity drops to zero around the vortex is referred to +as the vortex core. A length increase of the vortex segment will result in a +vortex core radius decrease, and vice versa. Diffusion, on the other hand, +continually spreads the vortex radially. + +Because of the previously mentioned analogy, practitioners of vortex filament +methods often refer to regularization as "viscous-core" models and +regularization parameters as "core-radii." Additionally, viscous diffusion is +often introduced by modifying the regularization parameter in space and time +instead of solving the diffusion from the vorticity equation. The distinction is +made explicit in this document when clarification is required, but a loose +terminology is used when the context is clear. Determination of the regularization parameter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The regularization parameter is both a function of the physics being -modelled (blade boundary layer and wake) and the choice of -discretization. Contributing factors are the chord length, the boundary -layer height, and the volume that each vortex filament is approximating. -Currently the choice is left to the user (**RegDetMethod=[0]**). -Empirical results for a rotating blade are found in the work of -Gupta (:cite:`Gupta06_1`). As a guideline, the -regularization parameter may be chosen as twice the average spanwise -discretization of the blade. The current implementation will implement -this guideline when the user chooses **RegDetMethod=[1]**. Further +The regularization parameter is both a function of the physics being modeled +(blade boundary layer and wake) and the choice of discretization. Contributing +factors are the chord length, the boundary layer height, and the volume that +each vortex filament is approximating. Currently the choice is left to the user +(**RegDetMethod=[0]**). Empirical results for a rotating blade are found in the +work of Gupta (:cite:`Gupta06_1`). As a guideline, the regularization parameter +may be chosen as twice the average spanwise discretization of the blade. This +guideline is implemented when the user chooses **RegDetMethod=[1]**. Further refinement of this option will be considered in the future. .. _sec:RegularizationFunction: -Regularization functions implemented +Implemented regularization functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Several regularization functions have been -developed (:cite:`Rankine58_1,Scully75_1,Vatistas91_1`). -At present, five options are available: (1) No correction, (2) the -Rankine method, (3) the Lamb-Oseen method, (4) the Vatistas method, or -(5) the denominator offset method. If no correction method is used, -[**RegFunction=[0]**], :math:`F_\nu=1`. The remaining methods are detailed -in the following sections. The regularization parameter -(**WakeRegParam**) is noted :math:`r_c` and the distance to the filament -is written :math:`\rho`. +developed (:cite:`Rankine58_1,Scully75_1,Vatistas91_1`). At present, five +options are available: 1) No correction, 2) the Rankine method, 3) the +Lamb-Oseen method, 4) the Vatistas method, or 5) the denominator offset method. +If no correction method is used, (**RegFunction=[0]**), :math:`F_\nu=1`. The +remaining methods are detailed in the following sections. Here, :math:`r_c` is +the regularization parameter (**WakeRegParam**) and :math:`\rho` is the distance +to the filament. Both variables are expressed in meters. Rankine ^^^^^^^ @@ -264,8 +481,8 @@ Rankine The Rankine method (:cite:`Rankine58_1`) is the simplest regularization model. With this method, the Rankine vortex has a finite core with a solid body rotation near the vortex center and a potential -vortex away from the center. If this method is used, -[**RegFunction=[1]**], the viscous core correction is given by +vortex away from the center. If this method is used +(**RegFunction=[1]**), the viscous core correction is given by Eq. :eq:`rankine`. .. math:: @@ -279,8 +496,8 @@ detailed in :numref:`sec:corerad`. Lamb-Oseen ^^^^^^^^^^ -If this method is used, [**RegFunction=[2]**], the viscous core correction -is given by Eq. :eq:`lamboseen`. +If the Lamb-Oseen method is used [**RegFunction=[2]**], the viscous core +correction is given by Eq. :eq:`lamboseen`. .. math:: F_\nu= \bigg[1-\text{exp}(-\frac{\rho^2}{r_c^2})\bigg] @@ -289,8 +506,8 @@ is given by Eq. :eq:`lamboseen`. Vatistas ^^^^^^^^ -If this method is used, [**RegFunction=[3]**], the viscous core correction -is given by Eq. :eq:`vatistas`. +If the Vatistas method is used [**RegFunction=[3]**], the viscous core +correction is given by Eq. :eq:`vatistas`. .. math:: F_\nu @@ -299,15 +516,14 @@ is given by Eq. :eq:`vatistas`. :label: vatistas Here, :math:`\rho` is the distance from a vortex segment to an arbitrary -point (:cite:`Abedi16_1`). Research from rotorcraft -applications suggests a value of :math:`n=2`, which is used in this -work (:cite:`Bagai93_1`). +point (:cite:`Abedi16_1`). Research from rotorcraft applications suggests a +value of :math:`n=2`, which is used in this work (:cite:`Bagai93_1`). Denominator Offset/Cut-Off ^^^^^^^^^^^^^^^^^^^^^^^^^^ -If this method is used, [**RegFunction=[4]**], the viscous core correction -is given by Eq. :eq:`denom` +If the denominator offfset method is used [**RegFunction=[4]**], the viscous +core correction is given by Eq. :eq:`denom` .. math:: \begin{aligned} @@ -316,90 +532,84 @@ is given by Eq. :eq:`denom` \end{aligned} :label: denom -Here, the singularity is removed by introducing an additive factor in -the denominator of -Eq. :eq:`eq:BiotSavartSegment`, proportional to -the filament length :math:`r_0`. In this case, :math:`F_\nu=1`. The -method is found in the work of van Garrel -(:cite:`Garrel03_1`). +Here, the singularity is removed by introducing an additive factor in the +denominator of Eq. :eq:`eq:BiotSavartSegment`, proportional to the filament +length :math:`r_0`. In this case, :math:`F_\nu=1`. This method is found in the +work of van Garrel (:cite:`Garrel03_1`). .. _sec:corerad: Time Evolution of the Regularization Parameter–Core Spreading Method ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -There are four available methods by which the regularization parameter -may evolve with time: (1) constant value, (2) stretching, (3) wake age, -or (4) stretching and wake age. The three latter methods blend the -notion of viscous diffusion with the notion of regularization. The -notation :math:`r_{c0}` used in this section corresponds to input file -parameter value **WakeRegParam**. +There are four available methods by which the regularization parameter may +evolve with time: 1) constant value, 2) stretching, 3) wake age, or 4) +stretching and wake age. The three latter methods blend the notions of viscous +diffusion and regularization. The notation :math:`r_{c0}` used in this section +corresponds to input file parameter value **WakeRegParam**. Constant ^^^^^^^^ -If a constant value is selected, (**WakeRegMethod=[0]**), the value of +If a constant value is selected, (**WakeRegMethod=[1]**), the value of :math:`r_c` remains unchanged for all Lagrangian markers throughout the -simulation and taken as the value given with the parameter in meters. +simulation and is taken as the value given with the parameter **WakeRegParam** +in meters. .. math:: r_c(\zeta) = r_{c0} :label: cst -Here, :math:`\zeta` is the vortex wake age, measured from its emission -time. +Here, :math:`\zeta` is the vortex wake age, measured from its emission time. Stretching ^^^^^^^^^^ -If the stretching method is selected, (**WakeRegMethod=[1]**), the viscous -core radius is modeled by Eq. :eq:`stretch`. +If the stretching method is selected, (**WakeRegMethod=[2]**), the viscous core +radius is modeled by Eq. :eq:`stretch`. .. math:: - r_c(\zeta,\epsilon) = \sqrt{r_{c0}^2+\int_0^\zeta(1+\epsilon)^{-1}d\zeta} + r_c(\zeta,\epsilon) = r_{c0} (1+\epsilon)^{-1} :label: stretch .. math:: \epsilon = \frac{\Delta l}{l} -Here, :math:`\epsilon` is the vortex-filament strain, and :math:`l` is -the filament length, and :math:`\Delta l` is the change of length -between two time steps. The integral in Eq. :eq:`stretch` -represents strain effects. +Here, :math:`\epsilon` is the vortex-filament strain, :math:`l` is the filament +length, and :math:`\Delta l` is the change of length between two time steps. The +integral in Eq. :eq:`stretch` represents strain effects. Wake Age / Core-Spreading ^^^^^^^^^^^^^^^^^^^^^^^^^ -If the wake age method is selected, (WakeRegMethod=[2]), the viscous core radius -is modeled by Eq. :eq:`age`. +If the wake age method is selected, (**WakeRegMethod=[3]**), the viscous core +radius is modeled by Eq. :eq:`age`. .. math:: r_c(\zeta) = \sqrt{r_{c0}^2+4\alpha\delta\nu \zeta} :label: age where :math:`\alpha=1.25643`, :math:`\nu` is kinematic viscosity, and -:math:`\delta` is a viscous diffusion parameter (typically between -:math:`1` and :math:`1,000`). The parameter :math:`\delta` is provided -in the input file as **CoreSpreadEddyVisc**. Here, the term, -:math:`4\alpha\delta\nu \zeta`, accounts for viscous effects as the wake -propagates downstream. The higher the background turbulence, the more -diffusion of the vorticity with time, and the higher the value of -:math:`\delta` should be. The method is often referred to as the -core-spreading method. It is a way to partially account for viscous -diffusion of the vorticity without solving for the interaction between -the wake vorticity or between the vorticity from the wake and the background -flow. Setting **DiffusionMethod=[1]** is the same as using the wake age method, -(**WakeRegMethod=[2]**). +:math:`\delta` is a viscous diffusion parameter (typically between :math:`1` and +:math:`1,000`). The parameter :math:`\delta` is provided in the input file as +**CoreSpreadEddyVisc**. Here, the term :math:`4\alpha\delta\nu \zeta`, accounts +for viscous effects as the wake propagates downstream. The higher the background +turbulence, the more diffusion of the vorticity with time, and the higher the +value of :math:`\delta` should be. This method partially accounts for viscous +diffusion of the vorticity while neglecting the interaction between the wake +vorticity itself or between the wake vorticity and the background flow. It is +often referred to as the core-spreading method. Setting **DiffusionMethod=[1]** +is the same as using the wake age method (**WakeRegMethod=[3]**). Stretching and Wake Age ^^^^^^^^^^^^^^^^^^^^^^^ -If the stretching and wake-age method is selected (**WakeRegMethod=[3]**), +If the stretching and wake-age method is selected (**WakeRegMethod=[4]**), the viscous core radius is modeled by Eq. :eq:`stretchandage`. .. math:: - r_c(\zeta,\epsilon) = \sqrt{r_{c0}^2 + 4\alpha\delta\nu \zeta + \int_0^\zeta(1+\epsilon)^{-1}d\zeta} + r_c(\zeta,\epsilon) = \sqrt{r_{c0}^2 + 4\alpha\delta\nu \zeta \big(1+\epsilon\big)^{-1} } :label: stretchandage .. _sec:diffusion: @@ -408,227 +618,13 @@ Diffusion --------- The viscous-splitting assumption is used to solve for the convection and -diffusion of the vorticity separately. The diffusion term -:math:`\nu \Delta \vec{\omega}` represents molecular diffusion. This -term allows for viscous connection of vorticity lines. Also, turbulent -flows will diffuse the vorticity in a similar manner based on a -turbulent eddy viscosity. +diffusion of the vorticity separately. The diffusion term :math:`\nu \Delta +\vec{\omega}` represents molecular diffusion. This term allows for viscous +connection of vorticity lines. Also, turbulent flows will diffuse the vorticity +in a similar manner based on a turbulent eddy viscosity. -The parameter **ViscousDiffusion** is used to switch between viscous diffusion +The parameter **DiffusionMethod** is used to switch between viscous diffusion methods. Currently, only the core-spreading method is implemented. The method is described in :numref:`sec:corerad` since it is equivalent to the increase of the regularization parameter with the wake age. -.. _sec:circ: - -Lifting-Line Representation ---------------------------- - -The code relies on a lifting-line formulation. Lifting-line methods -effectively lump the loads at each cross-section of the blade onto the -mean line of the blade and do not account directly for the geometry of -each cross-section. In the vorticity-based version of the lifting-line -method, the blade is represented by a line of varying circulation. The -line follows the motion of the blade and is referred to as “bound” -circulation. The bound circulation does not follow the same dynamic -equation as the free vorticity of the wake. Instead, the intensity is -linked to airfoil lift via the Kutta-Joukowski theorem. Spanwise -variation of the bound circulation results in vorticity being emitted -into the the wake, referred to as “trailed vorticity”. Time changes of -the bound circulation are also emitted in the wake, referred to as -“shed” vorticity. The subsequent paragraphs describe the representation -of the bound vorticity. - -Lifting-Line Panels and Emitted Wake Panels -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The lifting-line and wake representation is illustrated in -:numref:`fig:VortexLatticeMethod`. The blade lifting-line is discretized into a -finite number of panels, each of them forming a four sided vortex rings. The -spanwise discretization follows the discretization of the AeroDyn blade input -file. The number of spanwise panels, :math:`n_\text{LL}`, is one less than the -total number of AeroDyn nodes, **NumBlNds**. The sides of the panels coincide -with the lifting-line and the trailing edge of the blade. The lifting-line is -currently defined as the 3/4 chord location. More details on the panelling is -provided in :numref:`sec:Panelling`. At a given time step, the circulation of -each lifting-line panel is determined according to one of the three methods -developed in :numref:`sec:CirculationMethods`. At the end of the time step, the -circulation of each lifting-line panel is emitted into the wake, forming free -vorticity panels. The circulation of the first near wake panel and the bound -circulation are equivalent, to satisfy the Kutta condition (see -:numref:`fig:VortexLatticeMethod` b). The wake panels model the thin shear -layer resulting from the continuation of the blade boundary layer. This shear -layer can be modelled using a continuous distribution of vortex doublets. A -constant doublet strength is assumed on each panel, which in turn is equivalent -to a vortex ring of constant circulation. - -.. figure:: Schematics/VortexLatticeMethod.png - :alt: Wake and lifting-line vorticity discretized into vortex ring panels. - :name: fig:VortexLatticeMethod - :width: 100.0% - - Wake and lifting-line vorticity discretized into vortex ring panels. - (a) Overview. (b) Cross-sectional view, defining the leading-edge, - trailing edge, and lifting-line. (c) Circulation of panels and - corresponding circulation for vorticity segments between panels. (d) - Geometrical quantities for a lifting-line panel. - -The current implementation stores the positions and circulations of the -panel corner points. In the vortex ring formulation, the boundary -between two panels corresponds to a vortex segment of intensity equal to -the difference of circulation between the two panels. The convention -used to define the segment intensity based on the panels intensity is -shown in :numref:`fig:VortexLatticeMethod` c. Since the -circulation of the bound panels and the first row of near wake panels -are equal, the vortex segments located on the trailing edge have no -circulation. - -.. _sec:Panelling: - -Panelling -~~~~~~~~~ - -The definitions used for the panelling of the blade are given in -:numref:`fig:VortexLatticeMethod` d, following the notations of van -Garrel (:cite:`Garrel03_1`). The leading edge (LE) and -trailing edge (TE) locations are directly obtained from the AeroDyn -mesh. At two spanwise locations, the LE and TE define the corner points: -:math:`\vec{x}_1`, :math:`\vec{x}_2`, :math:`\vec{x}_3`, and -:math:`\vec{x}_4`. The current implementation assumes that the -aerodynamic center, the lifting-line, and the 3/4 chord location all -coincide. For a given panel, the lifting-line is then delimited by the -points :math:`\vec{x}_9= 3/4\,\vec{x}_1 + 1/4\, \vec{x}_2` and -:math:`\vec{x}_{10}=3/4\,\vec{x}_4 + 1/4\, \vec{x}_3`. The mid points of -the four panel sides are noted :math:`\vec{x}_5`, :math:`\vec{x}_6`, -:math:`\vec{x}_7`, and :math:`\vec{x}_8`. The lifting-line vector -(:math:`\vec{dl}`) as well as the vectors tangential (:math:`\vec{T}`) -and normal (:math:`\vec{N}`) to the panel are defined as: - -.. math:: - \begin{aligned} - \vec{dl} = \vec{x}_{10}-\vec{x}_9 - ,\qquad - \vec{T} = \frac{\vec{x}_6-\vec{x}_8}{|\vec{x}_6-\vec{x}_8|} - ,\qquad - \vec{N} = \frac{\vec{T}\times\vec{dl}}{|\vec{T}\times\vec{dl}|} - \end{aligned} - :label: eq:GeometricDefinitions - -The area of the panel is obtained as :math:`dA = -|(\vec{x}_6-\vec{x}_8)\times(\vec{x}_{7}-\vec{x}_5)|`. For -**CircSolvMethod=[3]**, the control points are located on the lifting-line at the -location :math:`\vec{x}_9+\eta_j \vec{dl}`. The factor :math:`\eta_j` is -determined based on the full-cosine approximation of van Garrel. This is based -on the spanwise widths of the current panel, :math:`w_j`, and the neighboring -panels :math:`w_{j-1}` and :math:`w_{j+1}`: - -.. math:: - \begin{aligned} - \eta_j=\frac{1}{4}\left[\frac{w_{j-1}}{w_{j-1}+w_j} + \frac{w_j}{w_j+w_{j+1}} +1 \right] - ,\ j=2..n-1 - ,\quad - \eta_1 = \frac{w_1}{w_1+w_2} - ,\quad - \eta_{n} = \frac{w_{n-1}}{w_{n-1}+w_{n}} - \end{aligned} - -For an equidistant spacing, this discretization places the control -points at the middle of the lifting-line (:math:`\eta=0.5`). Theoretical -circulation results for an elliptic wing with a cosine spacing are -retrieved with such discretization since it places the control points -closer to stronger trailing segments at the wing extremities (see -e.g. :cite:`Kerwin:lecturenotes`). - -.. _sec:CirculationMethods: - -Circulation Solving Methods -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Three methods are implemented to determine the bound circulation -strength. They are selected using the input , and are presented in the -following sections. - -Cl-Based Iterative Method -^^^^^^^^^^^^^^^^^^^^^^^^^ - -The Cl-based iterative method determines the circulation within a -nonlinear iterative solver that makes use of the polar data at each -control point located on the lifting line. The algorithm ensures that -the lift obtained using the angle of attack and the polar data matches -the lift obtained with the Kutta-Joukowski theorem. At present, it is -the preferred method to compute the circulation along the blade span. It is -selected with **CircSolvMethod=[1]**. The method is described in the work from -van Garrel (:cite:`Garrel03_1`). The algorithm is implemented in at iterative -approach using the following steps: - -#. The circulation distribution from the previous time step is used as a - guessed circulation, :math:`\Gamma_\text{prev}`. - -#. The velocity at each control points :math:`j` is computed as the sum - of the wind velocity, the structural velocity, and the velocity - induced by all the vorticity in the domain, evaluated at the control - point location. - - .. math:: - \begin{aligned} - \vec{v}_j = \vec{V}_0 - \vec{V}_\text{elast} + \vec{v}_{\omega,\text{free}} + \vec{v}_{\Gamma_{ll}} - \end{aligned} - - The contribution of :math:`\vec{v}_{\Gamma_{ll}}` comes from the - lifting-line panels and the first row of near wake panels, for which - the circulation is set to :math:`\Gamma_\text{prev}` - -#. The circulation for all lifting-line panels :math:`j` is obtained as - follows. - - .. math:: - \begin{aligned} - \Gamma_{ll,j} =\frac{1}{2} C_{l,j}(\alpha_j) \frac{\left[ (\vec{v}_j \cdot \vec{N})^2 + (\vec{v}_j \cdot \vec{T})^2\right]^2\,dA}{ - \sqrt{\left[(\vec{v}_j\times \vec{dl})\cdot\vec{N}\right]^2 + \left[(\vec{v}_j\times \vec{dl})\cdot\vec{T}\right]^2} - } %\label{eq:} - ,\quad\text{with} - \quad - \alpha_j = \operatorname{atan}\left(\frac{\vec{v}_j\cdot\vec{N}}{\vec{v}_j \cdot \vec{T}} \right) - \end{aligned} - - The function :math:`C_{l,j}` is the lift coefficient obtained from - the polar data of blade section :math:`j` and :math:`\alpha_j` is the - angle of attack at the control point. - -#. The new circulation is set using the relaxation factor - :math:`k_\text{relax}` (**CircSolvRelaxation**): - - .. math:: - \begin{aligned} - \Gamma_\text{new}= \Gamma_\text{prev} + k_\text{relax} \Delta \Gamma - ,\qquad - \Delta \Gamma = \Gamma_{ll} - \Gamma_\text{prev} %\label{eq:} - \end{aligned} - -#. Convergence is checked using the criterion :math:`k_\text{crit}` - (**CircSolvConvCrit**): - - .. math:: - \begin{aligned} - \frac{ \operatorname{max}(|\Delta \Gamma|}{\operatorname{mean}(|\Gamma_\text{new}|)} < k_\text{crit} - \end{aligned} - - If convergence is not reached, steps 2-5 are repeated using - :math:`\Gamma_\text{new}` as the guessed circulation - :math:`\Gamma_\text{prev}`. - -No-flow-through Method -^^^^^^^^^^^^^^^^^^^^^^ - -A Weissinger-L-based representation (:cite:`Weissinger47_1`) -of the lifting surface is also -available (:cite:`Bagai94_1,Gupta06_1,Ribera07_1`). In this -method, the circulation is solved by satisfying a no-flow through -condition at the 3/4-chord points. It is selected with **CircSolvMethod=[2]**. - -Prescribed Circulation -^^^^^^^^^^^^^^^^^^^^^^ - -The final available method prescribes a constant circulation. A user -specified spanwise distribution of circulation is prescribed onto the -blades. It is selected with **CircSolvMethod=[3]**. diff --git a/docs/source/user/aerodyn-fvw/OutputFiles.rst b/docs/source/user/aerodyn-fvw/OutputFiles.rst index cfbf334945..8068e64b21 100644 --- a/docs/source/user/aerodyn-fvw/OutputFiles.rst +++ b/docs/source/user/aerodyn-fvw/OutputFiles.rst @@ -6,10 +6,11 @@ Output Files The OLAF module itself does not produce its own output file. However, additional output channels are made available in *AeroDyn15*. As such, the *AeroDyn15* output file is briefly described as well as the outputs made available with -OLAF. Visualization files may be generated by using the parameter, **WrVTK**, -from the OLAF input file, in which case the VTK files are written to the folder, -``vtk_fvw``, or the parameter, **WrVTK**, from the main ``.fst`` file, in which case -the VTK files are written to the folder, ``vtk``. +OLAF. Visualization files are generated by using the parameter, **WrVTK**. This +parameter is available in the OLAF input file, in which case the VTK files are +written to the folder ``vtk_fvw``, or the primary ``.fst`` file, in which case +the VTK files are written to the folder ``vtk``. + Results File ------------ diff --git a/docs/source/user/aerodyn-fvw/StateSpace.rst b/docs/source/user/aerodyn-fvw/StateSpace.rst index 7cdcc8d72b..8e4d897579 100644 --- a/docs/source/user/aerodyn-fvw/StateSpace.rst +++ b/docs/source/user/aerodyn-fvw/StateSpace.rst @@ -8,48 +8,44 @@ State-Space Representation and Integration with OpenFAST State, Constraint, Input, and Output Variables ---------------------------------------------- -The OLAF module has been integrated into the latest version of OpenFAST -via *AeroDyn15*, following the OpenFAST modularization -framework (:cite:`Jonkman13_1,Sprague15_1`). To follow the -OpenFAST framework, the vortex code is written as a module, and its -formulation comprises state, constraint, and output equations. The data -manipulated by the module include the following vectors: inputs, -:math:`\vec{u}`; states, :math:`\vec{x}`; constrained state, -:math:`\vec{z}`; outputs, :math:`\vec{y}`; and constant parameters, -:math:`\vec{p}`. The vectors are defined as follows: - -- Inputs, :math:`\vec{u}~-` a set of values supplied to the module - that, along with the states, are needed to calculate future states - and the system’s output. - -- Outputs, :math:`\vec{y}~-` a set of values calculated and returned - by the module that depend on the states, inputs, and/or parameters - through output equations. - -- States, :math:`\vec{x}~-` a set of internal values of the module - that are influenced by the inputs and used to calculate future state - values and the output. Continuous states are employed, meaning that - the states are differentiable in time and characterized by continuous - time-differential equations. - -- Constraint states, :math:`\vec{z}~-` algebraic variables that are - calculated using a nonlinear solver, based on values from the current - time step. - -- Parameters, :math:`\vec{p}~-` a set of internal system values that - are independent of the states and inputs. The parameters can be fully - defined at initialization and characterize the system’s state - equations and output equations. +The OLAF module has been integrated into the latest version of OpenFAST via +*AeroDyn15*, following the OpenFAST modularization +framework (:cite:`Jonkman13_1,Sprague15_1`). To follow the OpenFAST framework, +the vortex code is written as a module, and its formulation comprises state, +constraint, and output equations. The data manipulated by the module include the +following vectors: constant parameters, :math:`\vec{p}`; inputs, +:math:`\vec{u}`; constrained state, :math:`\vec{z}`; states, :math:`\vec{x}`; +and outputs, :math:`\vec{y}`. The vectors are defined as follows: + +- Parameters, :math:`\vec{p}~-` a set of internal system values that are + independent of the states and inputs. The parameters can be fully defined at + initialization and characterize the system state and output equations. + +- Inputs, :math:`\vec{u}~-` a set of values supplied to the module that, along + with the states, are needed to calculate future states and the system output. + +- Constraint states, :math:`\vec{z}~-` algebraic variables that are calculated + using a nonlinear solver, based on values from the current time step. + +- States, :math:`\vec{x}~-` a set of internal values of the module. They are + influenced by the inputs and used to calculate future state values and + output. Continuous states are employed, meaning that the states are + differentiable in time and characterized by continuous time-differential + equations. + +- Outputs, :math:`\vec{y}~-` a set of values calculated and returned by the + module that depend on the states, inputs, and/or parameters through output + equations. The parameters of the vortex code include: -- Fluid characteristics: kinematic viscosity, :math:`\nu` +- Fluid characteristics: kinematic viscosity, :math:`\nu`. -- Airfoil characteristics: polar data: (:math:`C_l(\alpha)`, - :math:`C_d(\alpha)`, :math:`C_m(\alpha)`), and chord :math:`c` +- Airfoil characteristics: chord :math:`c` and polar data -- + :math:`C_l(\alpha)`, :math:`C_d(\alpha)`, :math:`C_m(\alpha)`). -- Algorithmic methods and parameters for regularization, viscous - diffusion, discretization, wake geometry, acceleration, and so on. +- Algorithmic methods and parameters, e.g., regularization, viscous + diffusion, discretization, wake geometry, and acceleration. The inputs of the vortex code are: @@ -62,14 +58,13 @@ The inputs of the vortex code are: are handled using the mesh-mapping functionality and data structure of OpenFAST. -- Undisturbed velocity field at requested locations (lifting-line - points, :math:`\vec{r}_{ll}`, and a set of locations requested by the - vortex code, :math:`\vec{r}_r`), written - :math:`\vec{V}_0=[\vec{V}_{0,ll}, \vec{V}_{0,r}]`. Based on the - parameters, this velocity field may contain the following influences: - freestream, shear, veer, turbulence, tower, and nacelle disturbance. - The locations where the velocity field is requested are typically the - location of the Lagrangian markers. +- Disturbed velocity field at requested locations, written + :math:`\vec{V}_0=[\vec{V}_{0,ll}, \vec{V}_{0,m}]`. Locations are requested + for lifting-line points, :math:`\vec{r}_{ll}`, and Lagrangian markers, + :math:`\vec{r}_m`. Based on the parameters, this disturbed velocity field may + contain the following influences: freestream, shear, veer, turbulence, tower, + and nacelle disturbance. The locations where the velocity field is requested + are typically the location of the Lagrangian markers. The constraint states are: @@ -93,33 +88,32 @@ The outputs are [1]_: - The induced velocity at the lifting-line nodes, :math:`\vec{v}_{i,ll}` -- The locations where the undisturbed wind needs to be computed, - :math:`\vec{r}_{r}` (typically :math:`\vec{r_{r}}=\vec{r}_m`). +- The locations where the undisturbed wind is computed, :math:`\vec{r}_{r}` + (typically :math:`\vec{r_{r}}=\vec{r}_m`). State, Constraint, and Output Equations --------------------------------------- -An overview of the main states, constraints, and output equations is -given in this paragraph. More details are provided in -:numref:`sec:FVW`. The constraint equation is used to determine -the circulation distribution along the span of each lifting line. For -the van Garrel method, this circulation is a function of the angle of -attack along the blade and the airfoil coefficients. The angle of attack -at a given lifting-line node is a function of the undisturbed velocity, +An overview of the states, constraints, and output equations is given here. More +details are provided in :numref:`sec:FVW`. The constraint equation is used to +determine the circulation distribution along the span of each lifting line. For +the van Garrel method, this circulation is a function of the angle of attack +along the blade and the airfoil coefficients. The angle of attack at a given +lifting-line node is a function of the undisturbed velocity, :math:`\vec{v}_{0,ll}`, and the velocity induced by the vorticity, -:math:`\vec{v}_{i,ll}`, at that point. Part of the induced velocity is -caused by the vorticity being shed and trailed at the current time step, -which in turn is a function of the circulation distribution along the -lifting line. This constraint equation may be written as: +:math:`\vec{v}_{i,ll}`, at that point. Part of the induced velocity is caused by +the vorticity being shed and trailed at the current time step, which in turn is +a function of the circulation distribution along the lifting line. This +constraint equation may be written as: .. math:: - \vec{Z} = \vec{0} = \vec{\Gamma}_{ll} - \vec{\Gamma}_p(\vec{\alpha}(\vec{x},\vec{u}),\vec{p}) + \vec{Z} = \vec{0} = \vec{\Gamma}_{ll} - \vec{\Gamma}_p\bigg(\vec{\alpha}(\vec{x},\vec{u}),\vec{p}\bigg) -where :math:`\vec{\Gamma}_p` is the function that returns the -circulation along the blade span, according to one of the method -presented in :numref:`sec:circ`. The state equation -specifies the time evolution of the vorticity and the convection of the -Lagrangian markers: +where :math:`\vec{\Gamma}_p` is the function that returns the circulation along +the blade span, according to one of the methods presented in :numref:`sec:circ`. + +The state equation specifies the time evolution of the vorticity and the +convection of the Lagrangian markers: .. math:: \begin{aligned} @@ -134,20 +128,23 @@ Lagrangian markers: \end{aligned} :label: eq:Convection -where :math:`\vec{v}_\omega` is the velocity induced by the vorticity in -the domain; :math:`\vec{V}_\omega(\vec{r},\vec{r}_m,\vec{\omega})` is -the function that computes this induced velocity at a given point, -:math:`\vec{r}`, based on the location of the Lagrangian markers and the -intensity of the vortex elements; and the subscript, :math:`e`, -indicates that a quantity is applied to an element. The vorticity, -:math:`\vec{\omega}`, is recovered from the vorticity of the vortex -elements by means of discrete convolutions. For vortex-segment -simulations, the viscous-splitting algorithm is used, and the convection -step (Eq. :eq:`eq:Convection`) is the main state -equation being solved for. The vorticity stretching is automatically -accounted for, and the diffusion is performed *a posteriori*. The -velocity function, :math:`\vec{V}_\omega`, uses the Biot-Savart law. The -output equation is: +Here, + +- :math:`\vec{v}_\omega` is the velocity induced by the vorticity in the + domain; +- :math:`\vec{V}_\omega(\vec{r},\vec{r}_m,\vec{\omega})` is the function that + computes this induced velocity at a given point, :math:`\vec{r}`, based on + the location of the Lagrangian markers and the intensity of the vortex elements; +- the subscript :math:`e` indicates that a quantity is applied to an element; + and +- the vorticity, :math:`\vec{\omega}`, is recovered from the vorticity of the + vortex elements by means of discrete convolutions. + +For vortex-segment simulations, the viscous-splitting algorithm is used, and the +convection step (Eq. :eq:`eq:Convection`) is the main state equation being +solved for. The vorticity stretching is automatically accounted for, and the +diffusion is performed *a posteriori*. The velocity function, +:math:`\vec{V}_\omega`, uses the Biot-Savart law. The output equation is: .. math:: \begin{aligned} @@ -160,24 +157,27 @@ Integration with AeroDyn15 The vortex code has been integrated as a submodule of the aerodynamic module of OpenFAST, *AeroDyn15*. The data workflow between the different modules and -submodules of OpenFAST is illustrated in :numref:`FAST-FVW`. This integration -required a restructuring of the *AeroDyn15* module to isolate the parts of the -code related to tower shadow modeling, induction computation, -lifting-line-forces computations, and dynamic stall. The dynamic stall model -will be adapted when used in conjunction with the vortex code to ensure the -effect of shed vorticity is not accounted for twice. The interface between -*AeroDyn15* and the inflow module, *InflowWind*, was accommodated to include the -additionally requested points by the vortex code. +submodules of OpenFAST is illustrated in :numref:`AD15-OLAF`. +AeroDyn inputs such as BEM options (e.g., tip-loss factor), skew model, and +dynamic inflow are discarded when the vortex code is used. The environmental +conditions, tower shadow, and dynamic stall model options are used. This +integration required a restructuring of the *AeroDyn15* module to isolate the +parts of the code related to tower shadow modeling, induction computation, +lifting-line-forces computations, and dynamic stall. The dynamic stall model is +adapted when used in conjunction with the vortex code to ensure the effect of +shed vorticity is not accounted for twice. The interface between *AeroDyn15* and +the inflow module, *InflowWind*, was accommodated to include the additionally +requested points by the vortex code. -.. _FAST-FVW: + +.. _AD15-OLAF: .. figure:: Schematics/VortexCodeWorkFlow.png :alt: OpenFAST-FVW code integration workflow :width: 100% :align: center - OpenFAST-FVW code integration workflow - + OpenFAST-OLAF code integration workflow From c6a8f140829689f43fd5fc6e058f1c501e2b0498 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 12 Jun 2020 10:12:52 -0600 Subject: [PATCH 163/190] FVW: docs. minor equation edit to match TR --- docs/source/user/aerodyn-fvw/StateSpace.rst | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/source/user/aerodyn-fvw/StateSpace.rst b/docs/source/user/aerodyn-fvw/StateSpace.rst index 8e4d897579..ecfa470b10 100644 --- a/docs/source/user/aerodyn-fvw/StateSpace.rst +++ b/docs/source/user/aerodyn-fvw/StateSpace.rst @@ -75,13 +75,12 @@ The continuous states are: - The position of the Lagrangian markers, :math:`\vec{r}_m` -- The vorticity associated with each vortex element, - :math:`\vec{\omega}_e`. For a projection of the vorticity onto vortex - segments, this corresponds to the circulation, - :math:`\vec{\Gamma}_e`, where for each segment, - :math:`\vec{\Gamma}_e= \Gamma_e \vec{dl}_e =\vec{\omega}_e dV_e`, - with :math:`\vec{dl}_e` and :math:`dV_e`, the vortex segment length - and its equivalent vortex volume. +- The vorticity associated with each vortex element, :math:`\vec{\omega}_e`. + For a projection of the vorticity onto vortex segments, this corresponds to + the circulation, :math:`\vec{\Gamma}_e`. For each segment, + :math:`\vec{\Gamma}_e= \Gamma_e \vec{dl}_e =\vec{\omega}_e dV_e`, with + :math:`\vec{dl}_e` and :math:`dV_e`, the vortex segment length and its + equivalent vortex volume. The outputs are [1]_: @@ -95,9 +94,9 @@ State, Constraint, and Output Equations --------------------------------------- An overview of the states, constraints, and output equations is given here. More -details are provided in :numref:`sec:FVW`. The constraint equation is used to -determine the circulation distribution along the span of each lifting line. For -the van Garrel method, this circulation is a function of the angle of attack +details are provided in :numref:`OLAF-Theory`. The constraint equation is used +to determine the circulation distribution along the span of each lifting line. +For the van Garrel method, this circulation is a function of the angle of attack along the blade and the airfoil coefficients. The angle of attack at a given lifting-line node is a function of the undisturbed velocity, :math:`\vec{v}_{0,ll}`, and the velocity induced by the vorticity, @@ -117,7 +116,7 @@ convection of the Lagrangian markers: .. math:: \begin{aligned} - \frac{d \vec{\omega}_e}{dt} &= \left[(\vec{\omega}\cdot\nabla)\vec{v} + \nu\nabla^2 \vec{\omega} \right]_e + \frac{d \vec{\omega}_e}{dt} &= \bigg[(\vec{\omega}\cdot\nabla)\vec{v} + \nu\nabla^2 \vec{\omega} \bigg]_e \end{aligned} .. math:: From 7d9000a326d2229fe9c6fd9eb7c5901fa7ce79ba Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 12 Jun 2020 10:15:08 -0600 Subject: [PATCH 164/190] FVW: docs. update index --- docs/source/user/aerodyn-fvw/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/user/aerodyn-fvw/index.rst b/docs/source/user/aerodyn-fvw/index.rst index 3450d479dd..5e5fdb2f4c 100644 --- a/docs/source/user/aerodyn-fvw/index.rst +++ b/docs/source/user/aerodyn-fvw/index.rst @@ -1,5 +1,5 @@ -OLAF User's Guide and Theory Manual (Free Vortex Wake) -====================================================== +OLAF User's Guide and Theory Manual (Free Vortex Wake in AeroDyn15) +=================================================================== .. only:: html From 5e3b731442b41cca57aa19c5c9239a7d1f01e07a Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 12 Jun 2020 13:20:12 -0600 Subject: [PATCH 165/190] FVW/OLAF: rename docs directory --- .../Acknowledgments.rst | 0 .../user/{aerodyn-fvw => aerodyn-olaf}/Acronyms.rst | 0 .../{aerodyn-fvw => aerodyn-olaf}/AppendixA.rst | 0 .../{aerodyn-fvw => aerodyn-olaf}/AppendixB.rst | 0 .../{aerodyn-fvw => aerodyn-olaf}/AppendixC.rst | 0 .../ExampleFiles/ExampleFile--OLAF.txt | 0 .../ExampleFiles/ExampleFile--PrescribeCirc.txt | 0 .../{aerodyn-fvw => aerodyn-olaf}/FutureWork.rst | 0 .../{aerodyn-fvw => aerodyn-olaf}/InputFiles.rst | 0 .../{aerodyn-fvw => aerodyn-olaf}/Introduction.rst | 0 .../{aerodyn-fvw => aerodyn-olaf}/OLAFTheory.rst | 0 .../{aerodyn-fvw => aerodyn-olaf}/OutputFiles.rst | 0 .../{aerodyn-fvw => aerodyn-olaf}/RunningOLAF.rst | 0 .../Schematics/FVWwithOpenFAST.pdf | Bin .../Schematics/FVWwithOpenFAST.png | Bin .../Schematics/FilamentRegularization.pdf | Bin .../Schematics/FilamentRegularization.png | Bin .../Schematics/LagrangianMarkers.pdf | Bin .../Schematics/LagrangianMarkers.png | Bin .../Schematics/OpenFAST.pdf | Bin .../Schematics/OpenFAST.png | Bin .../Schematics/Stencil.pdf | Bin .../Schematics/Stencil.png | Bin .../Schematics/VortexCodeWorkFlow.pdf | Bin .../Schematics/VortexCodeWorkFlow.png | Bin .../Schematics/VortexCodeWorkFlow.tex | 0 .../Schematics/VortexLatticeMethod.pdf | Bin .../Schematics/VortexLatticeMethod.png | Bin .../{aerodyn-fvw => aerodyn-olaf}/StateSpace.rst | 0 .../{aerodyn-fvw => aerodyn-olaf}/bibliography.bib | 0 .../user/{aerodyn-fvw => aerodyn-olaf}/index.rst | 5 +++-- .../user/{aerodyn-fvw => aerodyn-olaf}/zrefs.rst | 0 docs/source/user/index.rst | 2 +- 33 files changed, 4 insertions(+), 3 deletions(-) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Acknowledgments.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Acronyms.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/AppendixA.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/AppendixB.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/AppendixC.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/ExampleFiles/ExampleFile--OLAF.txt (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/ExampleFiles/ExampleFile--PrescribeCirc.txt (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/FutureWork.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/InputFiles.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Introduction.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/OLAFTheory.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/OutputFiles.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/RunningOLAF.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/FVWwithOpenFAST.pdf (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/FVWwithOpenFAST.png (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/FilamentRegularization.pdf (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/FilamentRegularization.png (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/LagrangianMarkers.pdf (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/LagrangianMarkers.png (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/OpenFAST.pdf (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/OpenFAST.png (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/Stencil.pdf (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/Stencil.png (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/VortexCodeWorkFlow.pdf (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/VortexCodeWorkFlow.png (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/VortexCodeWorkFlow.tex (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/VortexLatticeMethod.pdf (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/Schematics/VortexLatticeMethod.png (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/StateSpace.rst (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/bibliography.bib (100%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/index.rst (86%) rename docs/source/user/{aerodyn-fvw => aerodyn-olaf}/zrefs.rst (100%) diff --git a/docs/source/user/aerodyn-fvw/Acknowledgments.rst b/docs/source/user/aerodyn-olaf/Acknowledgments.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/Acknowledgments.rst rename to docs/source/user/aerodyn-olaf/Acknowledgments.rst diff --git a/docs/source/user/aerodyn-fvw/Acronyms.rst b/docs/source/user/aerodyn-olaf/Acronyms.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/Acronyms.rst rename to docs/source/user/aerodyn-olaf/Acronyms.rst diff --git a/docs/source/user/aerodyn-fvw/AppendixA.rst b/docs/source/user/aerodyn-olaf/AppendixA.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/AppendixA.rst rename to docs/source/user/aerodyn-olaf/AppendixA.rst diff --git a/docs/source/user/aerodyn-fvw/AppendixB.rst b/docs/source/user/aerodyn-olaf/AppendixB.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/AppendixB.rst rename to docs/source/user/aerodyn-olaf/AppendixB.rst diff --git a/docs/source/user/aerodyn-fvw/AppendixC.rst b/docs/source/user/aerodyn-olaf/AppendixC.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/AppendixC.rst rename to docs/source/user/aerodyn-olaf/AppendixC.rst diff --git a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt b/docs/source/user/aerodyn-olaf/ExampleFiles/ExampleFile--OLAF.txt similarity index 100% rename from docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt rename to docs/source/user/aerodyn-olaf/ExampleFiles/ExampleFile--OLAF.txt diff --git a/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--PrescribeCirc.txt b/docs/source/user/aerodyn-olaf/ExampleFiles/ExampleFile--PrescribeCirc.txt similarity index 100% rename from docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--PrescribeCirc.txt rename to docs/source/user/aerodyn-olaf/ExampleFiles/ExampleFile--PrescribeCirc.txt diff --git a/docs/source/user/aerodyn-fvw/FutureWork.rst b/docs/source/user/aerodyn-olaf/FutureWork.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/FutureWork.rst rename to docs/source/user/aerodyn-olaf/FutureWork.rst diff --git a/docs/source/user/aerodyn-fvw/InputFiles.rst b/docs/source/user/aerodyn-olaf/InputFiles.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/InputFiles.rst rename to docs/source/user/aerodyn-olaf/InputFiles.rst diff --git a/docs/source/user/aerodyn-fvw/Introduction.rst b/docs/source/user/aerodyn-olaf/Introduction.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/Introduction.rst rename to docs/source/user/aerodyn-olaf/Introduction.rst diff --git a/docs/source/user/aerodyn-fvw/OLAFTheory.rst b/docs/source/user/aerodyn-olaf/OLAFTheory.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/OLAFTheory.rst rename to docs/source/user/aerodyn-olaf/OLAFTheory.rst diff --git a/docs/source/user/aerodyn-fvw/OutputFiles.rst b/docs/source/user/aerodyn-olaf/OutputFiles.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/OutputFiles.rst rename to docs/source/user/aerodyn-olaf/OutputFiles.rst diff --git a/docs/source/user/aerodyn-fvw/RunningOLAF.rst b/docs/source/user/aerodyn-olaf/RunningOLAF.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/RunningOLAF.rst rename to docs/source/user/aerodyn-olaf/RunningOLAF.rst diff --git a/docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.pdf b/docs/source/user/aerodyn-olaf/Schematics/FVWwithOpenFAST.pdf similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.pdf rename to docs/source/user/aerodyn-olaf/Schematics/FVWwithOpenFAST.pdf diff --git a/docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.png b/docs/source/user/aerodyn-olaf/Schematics/FVWwithOpenFAST.png similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.png rename to docs/source/user/aerodyn-olaf/Schematics/FVWwithOpenFAST.png diff --git a/docs/source/user/aerodyn-fvw/Schematics/FilamentRegularization.pdf b/docs/source/user/aerodyn-olaf/Schematics/FilamentRegularization.pdf similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/FilamentRegularization.pdf rename to docs/source/user/aerodyn-olaf/Schematics/FilamentRegularization.pdf diff --git a/docs/source/user/aerodyn-fvw/Schematics/FilamentRegularization.png b/docs/source/user/aerodyn-olaf/Schematics/FilamentRegularization.png similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/FilamentRegularization.png rename to docs/source/user/aerodyn-olaf/Schematics/FilamentRegularization.png diff --git a/docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.pdf b/docs/source/user/aerodyn-olaf/Schematics/LagrangianMarkers.pdf similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.pdf rename to docs/source/user/aerodyn-olaf/Schematics/LagrangianMarkers.pdf diff --git a/docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.png b/docs/source/user/aerodyn-olaf/Schematics/LagrangianMarkers.png similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.png rename to docs/source/user/aerodyn-olaf/Schematics/LagrangianMarkers.png diff --git a/docs/source/user/aerodyn-fvw/Schematics/OpenFAST.pdf b/docs/source/user/aerodyn-olaf/Schematics/OpenFAST.pdf similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/OpenFAST.pdf rename to docs/source/user/aerodyn-olaf/Schematics/OpenFAST.pdf diff --git a/docs/source/user/aerodyn-fvw/Schematics/OpenFAST.png b/docs/source/user/aerodyn-olaf/Schematics/OpenFAST.png similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/OpenFAST.png rename to docs/source/user/aerodyn-olaf/Schematics/OpenFAST.png diff --git a/docs/source/user/aerodyn-fvw/Schematics/Stencil.pdf b/docs/source/user/aerodyn-olaf/Schematics/Stencil.pdf similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/Stencil.pdf rename to docs/source/user/aerodyn-olaf/Schematics/Stencil.pdf diff --git a/docs/source/user/aerodyn-fvw/Schematics/Stencil.png b/docs/source/user/aerodyn-olaf/Schematics/Stencil.png similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/Stencil.png rename to docs/source/user/aerodyn-olaf/Schematics/Stencil.png diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.pdf b/docs/source/user/aerodyn-olaf/Schematics/VortexCodeWorkFlow.pdf similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.pdf rename to docs/source/user/aerodyn-olaf/Schematics/VortexCodeWorkFlow.pdf diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.png b/docs/source/user/aerodyn-olaf/Schematics/VortexCodeWorkFlow.png similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.png rename to docs/source/user/aerodyn-olaf/Schematics/VortexCodeWorkFlow.png diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.tex b/docs/source/user/aerodyn-olaf/Schematics/VortexCodeWorkFlow.tex similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.tex rename to docs/source/user/aerodyn-olaf/Schematics/VortexCodeWorkFlow.tex diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.pdf b/docs/source/user/aerodyn-olaf/Schematics/VortexLatticeMethod.pdf similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.pdf rename to docs/source/user/aerodyn-olaf/Schematics/VortexLatticeMethod.pdf diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.png b/docs/source/user/aerodyn-olaf/Schematics/VortexLatticeMethod.png similarity index 100% rename from docs/source/user/aerodyn-fvw/Schematics/VortexLatticeMethod.png rename to docs/source/user/aerodyn-olaf/Schematics/VortexLatticeMethod.png diff --git a/docs/source/user/aerodyn-fvw/StateSpace.rst b/docs/source/user/aerodyn-olaf/StateSpace.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/StateSpace.rst rename to docs/source/user/aerodyn-olaf/StateSpace.rst diff --git a/docs/source/user/aerodyn-fvw/bibliography.bib b/docs/source/user/aerodyn-olaf/bibliography.bib similarity index 100% rename from docs/source/user/aerodyn-fvw/bibliography.bib rename to docs/source/user/aerodyn-olaf/bibliography.bib diff --git a/docs/source/user/aerodyn-fvw/index.rst b/docs/source/user/aerodyn-olaf/index.rst similarity index 86% rename from docs/source/user/aerodyn-fvw/index.rst rename to docs/source/user/aerodyn-olaf/index.rst index 5e5fdb2f4c..d8b0fccc1f 100644 --- a/docs/source/user/aerodyn-fvw/index.rst +++ b/docs/source/user/aerodyn-olaf/index.rst @@ -10,8 +10,9 @@ OLAF User's Guide and Theory Manual (Free Vortex Wake in AeroDyn15) releases are issued and as needed to provide further information on advancements or modifications to the software. - The documentaiton here was derived from the OLAF users manual by K.\ Shaler, - E.\ Branlard, and A.\ Platt. (FIXME: add link to pub) + The documentaiton here was derived from the OLAF users manual by K. Shaler, + E. Branlard, and A. Platt. (`https://www.nrel.gov/docs/fy20osti/75959.pdf + `_) .. toctree:: diff --git a/docs/source/user/aerodyn-fvw/zrefs.rst b/docs/source/user/aerodyn-olaf/zrefs.rst similarity index 100% rename from docs/source/user/aerodyn-fvw/zrefs.rst rename to docs/source/user/aerodyn-olaf/zrefs.rst diff --git a/docs/source/user/index.rst b/docs/source/user/index.rst index 6c6c0559d0..a4591faf18 100644 --- a/docs/source/user/index.rst +++ b/docs/source/user/index.rst @@ -14,7 +14,7 @@ Details on the transition from FAST v8 to OpenFAST may be found in :numref:`fast api_change.rst aerodyn/index.rst - aerodyn-fvw/index.rst + aerodyn-olaf/index.rst beamdyn/index.rst fast_to_openfast.rst cppapi/index.rst From 1b229cc0dc9f46b9ff13870cee2227e85b356cf9 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 15 Jun 2020 13:22:57 -0600 Subject: [PATCH 166/190] FVW: add helical wake test case --- .../aerodyn-olaf/ExampleFiles/ExampleFile--OLAF.txt | 10 +++++----- reg_tests/CTestList.cmake | 1 + reg_tests/r-test | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/source/user/aerodyn-olaf/ExampleFiles/ExampleFile--OLAF.txt b/docs/source/user/aerodyn-olaf/ExampleFiles/ExampleFile--OLAF.txt index e52dbe0717..e0d341697f 100644 --- a/docs/source/user/aerodyn-olaf/ExampleFiles/ExampleFile--OLAF.txt +++ b/docs/source/user/aerodyn-olaf/ExampleFiles/ExampleFile--OLAF.txt @@ -1,7 +1,7 @@ ---------------------------- FREE WAKE INPUT FILE ---------------------------------------------- -Free wake input file for the BAR turbine +--------------------------- OLAF (cOnvecting LAgrangian Filaments) INPUT FILE ----------------- +Free wake input file for the Helix test case --------------------------- GENERAL OPTIONS --------------------------------------------------- -5 IntMethod Integration method {5: Forward Euler 1st order, default: 5} (switch) +5 IntMethod Integration method {5: Forward Euler 1st order, default: 5} (switch) 0.2 DTfvw Time interval for wake propagation. {default: dtaero} (s) 5 FreeWakeStart Time when wake is free. (-) value = always free. {default: 0.0} (s) 2.0 FullCircStart Time at which full circulation is reached. {default: 0.0} (s) @@ -17,7 +17,7 @@ Free wake input file for the BAR turbine 50 nNWPanel Number of near-wake panels [integer] (-) 400 WakeLength Total wake distance [integer] (number of time steps) default FreeWakeLength Wake length that is free [integer] (number of time steps) {default: WakeLength} -False FWShedVorticity Include shed vorticity in the far wake {default: false} +False FWShedVorticity Include shed vorticity in the far wake {default: false} ------------------- WAKE REGULARIZATIONS AND DIFFUSION ----------------------------------------- 0 DiffusionMethod Diffusion method to account for viscous effects {0: None, 1: Core Spreading, "default": 0} 0 RegDeterMethod Method to determine the regularization parameters {0: Manual, 1: Optimized, default: 0 } @@ -38,5 +38,5 @@ False TwrShadowOnWake Include tower flow disturbance effects on wake convec 1 WrVTk Outputs Visualization Toolkit (VTK) (independent of .fst option) {0: NoVTK, 1: Write VTK at each time step} (flag) 1 nVTKBlades Number of blades for which VTK files are exported {0: No VTK per blade, n: VTK for blade 1 to n} (-) 2 VTKCoord Coordinate system used for VTK export. {1: Global, 2: Hub, "default": 1} -default VTK_fps Frame rate for VTK output (frames per second) {"all" for all glue code timesteps, "default" for all FVW timesteps} [used only if WrVTK=1] +1 VTK_fps Frame rate for VTK output (frames per second) {"all" for all glue code timesteps, "default" for all OLAF timesteps} [used only if WrVTK=1] ------------------------------------------------------------------------------------------------ diff --git a/reg_tests/CTestList.cmake b/reg_tests/CTestList.cmake index 4a0141e21c..4cd0daf453 100644 --- a/reg_tests/CTestList.cmake +++ b/reg_tests/CTestList.cmake @@ -114,6 +114,7 @@ of_regression("5MW_OC3Spar_DLL_WTurb_WavesIrr" "openfast;elastodyn;aerod of_regression("5MW_OC4Semi_WSt_WavesWN" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;moordyn") of_regression("5MW_Land_BD_DLL_WTurb" "openfast;beamdyn;aerodyn15;servodyn") of_regression("5MW_OC4Jckt_ExtPtfm" "openfast;elastodyn;extptfm") +of_regression("HelicalWake_OLAF" "openfast;aerodyn15;olaf") # Linearized OpenFAST regression tests of_regression_linear("WP_Stationary_Linear" "openfast;linear;elastodyn;aerodyn15") diff --git a/reg_tests/r-test b/reg_tests/r-test index 1ffce9e7f6..fff7a8d811 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 1ffce9e7f6d627bf6cd5de55ff4ee19d49bb3746 +Subproject commit fff7a8d811cccc7d3dbed9f12e43ac660f4a3f04 From d8225f997c84ddbd1d32d8dbd8a2fa477841a318 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Mon, 15 Jun 2020 13:44:06 -0600 Subject: [PATCH 167/190] FVW: add elliptical wing test case --- reg_tests/CTestList.cmake | 1 + reg_tests/r-test | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/reg_tests/CTestList.cmake b/reg_tests/CTestList.cmake index 4cd0daf453..12c37c9add 100644 --- a/reg_tests/CTestList.cmake +++ b/reg_tests/CTestList.cmake @@ -115,6 +115,7 @@ of_regression("5MW_OC4Semi_WSt_WavesWN" "openfast;elastodyn;aerod of_regression("5MW_Land_BD_DLL_WTurb" "openfast;beamdyn;aerodyn15;servodyn") of_regression("5MW_OC4Jckt_ExtPtfm" "openfast;elastodyn;extptfm") of_regression("HelicalWake_OLAF" "openfast;aerodyn15;olaf") +of_regression("EllipticalWing_OLAF" "openfast;aerodyn15;olaf") # Linearized OpenFAST regression tests of_regression_linear("WP_Stationary_Linear" "openfast;linear;elastodyn;aerodyn15") diff --git a/reg_tests/r-test b/reg_tests/r-test index fff7a8d811..01fba410f1 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit fff7a8d811cccc7d3dbed9f12e43ac660f4a3f04 +Subproject commit 01fba410f1326200269b0b5e50106295441694bf From 8f552046ea5086985ecde4f6f613f605df0c2ff8 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 17 Jun 2020 10:51:12 -0600 Subject: [PATCH 168/190] FVW: update test cases Also fix typo in CTestList.cmake --- reg_tests/CTestList.cmake | 2 +- reg_tests/r-test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reg_tests/CTestList.cmake b/reg_tests/CTestList.cmake index f5ff83a656..8c012d210d 100644 --- a/reg_tests/CTestList.cmake +++ b/reg_tests/CTestList.cmake @@ -102,7 +102,7 @@ of_regression("WP_VSP_WTurb_PitchFail" "openfast;elastodyn;aerodyn14;se of_regression("WP_VSP_ECD" "openfast;elastodyn;aerodyn15;servodyn") of_regression("WP_VSP_WTurb" "openfast;elastodyn;aerodyn15;servodyn") of_regression("SWRT_YFree_VS_EDG01" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("SWRT_YFree_VS_EDC01" "openfast;elastodyn;aerodyn15;servodyn") +of_regression("SWRT_YFree_VS_EDC01" "openfast;elastodyn;aerodyn14;servodyn") of_regression("SWRT_YFree_VS_WTurb" "openfast;elastodyn;aerodyn14;servodyn") of_regression("5MW_Land_DLL_WTurb" "openfast;elastodyn;aerodyn15;servodyn") of_regression("5MW_OC3Mnpl_DLL_WTurb_WavesIrr" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;subdyn") diff --git a/reg_tests/r-test b/reg_tests/r-test index ef694b5c51..96708688c4 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit ef694b5c516e65366acf60e653f3e5df4a397133 +Subproject commit 96708688c4cb52933e9b9e3b2291f45fc830f0c7 From 865f5bf2ddcf00d6814590acc0f7c20a5c1711dd Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 17 Jun 2020 13:12:40 -0600 Subject: [PATCH 169/190] Update github action for OLAF regression tests --- .github/actions/compile-and-test/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/compile-and-test/entrypoint.sh b/.github/actions/compile-and-test/entrypoint.sh index 8d1b38cdc2..9555e4f7d0 100755 --- a/.github/actions/compile-and-test/entrypoint.sh +++ b/.github/actions/compile-and-test/entrypoint.sh @@ -49,4 +49,4 @@ ctest -VV -L linear ## - 9, 16 because they're very sensitive ## - 19, 20 because theyre too long ## - 17, 22, 23 becuase we dont know why they fail :( -ctest -VV -j8 -I 1,1,1,2,3,4,5,6,7,8,10,11,12,13,14,15,18,21,24,25,26 +ctest -VV -j8 -I 1,1,1,2,3,4,5,6,7,8,10,11,12,13,14,15,18,21,24,25,26,27,28 From fc66b8ab26a517ecc1e3ff90d696356efcf1647f Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 17 Jun 2020 13:25:22 -0600 Subject: [PATCH 170/190] Add OLAF free vortex wake unit test to github actions --- .github/actions/compile-and-test/entrypoint.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/actions/compile-and-test/entrypoint.sh b/.github/actions/compile-and-test/entrypoint.sh index 9555e4f7d0..d31bb56863 100755 --- a/.github/actions/compile-and-test/entrypoint.sh +++ b/.github/actions/compile-and-test/entrypoint.sh @@ -41,6 +41,9 @@ ctest -VV -R nwtc_library_utest ctest -VV -j7 -R bd_ ctest -VV -R beamdyn_utest +# OLAF free vortex wake tests +ctest -VV -R fvw_utest + # OpenFAST linearization tests # Dont run these in parallel, copying the case files can fail in a race condition ctest -VV -L linear From 9170dfbc25b0cf027ac748ad3a540748dbb66b55 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 19 Jun 2020 11:55:46 -0600 Subject: [PATCH 171/190] OLAF: minor update to r-test cases for OLAF --- reg_tests/r-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reg_tests/r-test b/reg_tests/r-test index 96708688c4..fee3c15ae7 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 96708688c4cb52933e9b9e3b2291f45fc830f0c7 +Subproject commit fee3c15ae71a606936bf88c439045b5a52abe68a From 2645008f215cc1f574648c69d37d6d9a252f9ab9 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 26 Jun 2020 14:44:54 -0600 Subject: [PATCH 172/190] FVW: removed openmp from vs solutions --- vs-build/FAST/FAST.vfproj | 16 +++++------ vs-build/FASTlib/FASTlib.vfproj | 50 +++++++++------------------------ 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/vs-build/FAST/FAST.vfproj b/vs-build/FAST/FAST.vfproj index 3d8933dc7d..80f4de20ce 100644 --- a/vs-build/FAST/FAST.vfproj +++ b/vs-build/FAST/FAST.vfproj @@ -5,7 +5,7 @@ - + @@ -15,7 +15,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -35,7 +35,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -65,7 +65,7 @@ - + @@ -75,7 +75,7 @@ - + diff --git a/vs-build/FASTlib/FASTlib.vfproj b/vs-build/FASTlib/FASTlib.vfproj index 0d16805e43..cccb60ffc8 100644 --- a/vs-build/FASTlib/FASTlib.vfproj +++ b/vs-build/FASTlib/FASTlib.vfproj @@ -5,7 +5,7 @@ - + @@ -14,7 +14,7 @@ - + @@ -23,7 +23,7 @@ - + @@ -32,7 +32,7 @@ - + @@ -41,7 +41,7 @@ - + @@ -50,7 +50,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -68,7 +68,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -86,7 +86,7 @@ - + @@ -95,7 +95,7 @@ - + @@ -104,7 +104,7 @@ - + @@ -133,31 +133,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + From 06161b0594d3672e92dacb4f709f5a3d303f133f Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Fri, 26 Jun 2020 15:22:37 -0600 Subject: [PATCH 173/190] Remove DBG_OUTS from AD15 This will reduce merge conflicts with PR #373 --- modules/aerodyn/src/AeroDyn.f90 | 108 +------------- modules/aerodyn/src/AeroDyn_IO.f90 | 223 ----------------------------- 2 files changed, 1 insertion(+), 330 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 498dce19f5..13c9b68733 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -83,13 +83,8 @@ subroutine AD_SetInitOut(p, InputFileData, InitOut, errStat, errMsg) integer(IntKi) :: i, j, k, f integer(IntKi) :: NumCoords -#ifdef DBG_OUTS - integer(IntKi) :: m - character(6) ::chanPrefix - character(3) :: TmpChar -#endif - ! Initialize variables for this routine + ! Initialize variables for this routine errStat = ErrID_None errMsg = "" @@ -103,86 +98,10 @@ subroutine AD_SetInitOut(p, InputFileData, InitOut, errStat, errMsg) if (ErrStat >= AbortErrLev) return - -#ifdef DBG_OUTS - ! Loop over blades and nodes to populate the output channel names and units - - do k=1,p%numBlades - do j=1, p%NumBlNds - - m = (k-1)*p%NumBlNds*p%NBlOuts + (j-1)*p%NBlOuts - - WRITE (TmpChar,'(I3.3)') j - chanPrefix = "B"//trim(num2lstr(k))//"N"//TmpChar - InitOut%WriteOutputHdr( m + 1 ) = trim(chanPrefix)//"Twst" - InitOut%WriteOutputUnt( m + 1 ) = ' (deg) ' - InitOut%WriteOutputHdr( m + 2 ) = trim(chanPrefix)//"Psi" - InitOut%WriteOutputUnt( m + 2 ) = ' (deg) ' - InitOut%WriteOutputHdr( m + 3 ) = trim(chanPrefix)//"Vx" - InitOut%WriteOutputUnt( m + 3 ) = ' (m/s) ' - InitOut%WriteOutputHdr( m + 4 ) = trim(chanPrefix)//"Vy" - InitOut%WriteOutputUnt( m + 4 ) = ' (m/s) ' - InitOut%WriteOutputHdr( m + 5 ) = ' '//trim(chanPrefix)//"AIn" - InitOut%WriteOutputUnt( m + 5 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 6 ) = ' '//trim(chanPrefix)//"ApIn" - InitOut%WriteOutputUnt( m + 6 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 7 ) = trim(chanPrefix)//"Vrel" - InitOut%WriteOutputUnt( m + 7 ) = ' (m/s) ' - InitOut%WriteOutputHdr( m + 8 ) = ' '//trim(chanPrefix)//"Phi" - InitOut%WriteOutputUnt( m + 8 ) = ' (deg) ' - InitOut%WriteOutputHdr( m + 9 ) = ' '//trim(chanPrefix)//"AOA" - InitOut%WriteOutputUnt( m + 9 ) = ' (deg) ' - InitOut%WriteOutputHdr( m + 10 ) = ' '//trim(chanPrefix)//"Cl" - InitOut%WriteOutputUnt( m + 10 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 11 ) = ' '//trim(chanPrefix)//"Cd" - InitOut%WriteOutputUnt( m + 11 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 12 ) = ' '//trim(chanPrefix)//"Cm" - InitOut%WriteOutputUnt( m + 12 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 13 ) = ' '//trim(chanPrefix)//"Cx" - InitOut%WriteOutputUnt( m + 13 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 14 ) = ' '//trim(chanPrefix)//"Cy" - InitOut%WriteOutputUnt( m + 14 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 15 ) = ' '//trim(chanPrefix)//"Cn" - InitOut%WriteOutputUnt( m + 15 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 16 ) = ' '//trim(chanPrefix)//"Ct" - InitOut%WriteOutputUnt( m + 16 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 17 ) = ' '//trim(chanPrefix)//"Fl" - InitOut%WriteOutputUnt( m + 17 ) = ' (N/m) ' - InitOut%WriteOutputHdr( m + 18 ) = ' '//trim(chanPrefix)//"Fd" - InitOut%WriteOutputUnt( m + 18 ) = ' (N/m) ' - InitOut%WriteOutputHdr( m + 19 ) = ' '//trim(chanPrefix)//"M" - InitOut%WriteOutputUnt( m + 19 ) = ' (N/m^2) ' - InitOut%WriteOutputHdr( m + 20 ) = ' '//trim(chanPrefix)//"Fx" - InitOut%WriteOutputUnt( m + 20 ) = ' (N/m) ' - InitOut%WriteOutputHdr( m + 21 ) = ' '//trim(chanPrefix)//"Fy" - InitOut%WriteOutputUnt( m + 21 ) = ' (N/m) ' - InitOut%WriteOutputHdr( m + 22 ) = ' '//trim(chanPrefix)//"Fn" - InitOut%WriteOutputUnt( m + 22 ) = ' (N/m) ' - InitOut%WriteOutputHdr( m + 23 ) = ' '//trim(chanPrefix)//"Ft" - InitOut%WriteOutputUnt( m + 23 ) = ' (N/m) ' - InitOut%WriteOutputHdr( m + 24 ) = ' '//trim(chanPrefix)//"Gam" - InitOut%WriteOutputUnt( m + 24 ) = ' (m^2/s) ' - InitOut%WriteOutputHdr( m + 25 ) = ' '//trim(chanPrefix)//"Uin" - InitOut%WriteOutputUnt( m + 25 ) = ' (m/s) ' - InitOut%WriteOutputHdr( m + 26 ) = ' '//trim(chanPrefix)//"Uit" - InitOut%WriteOutputUnt( m + 26 ) = ' (m/s) ' - InitOut%WriteOutputHdr( m + 27 ) = ' '//trim(chanPrefix)//"Uir" - InitOut%WriteOutputUnt( m + 27 ) = ' (m/s) ' - InitOut%WriteOutputHdr( m + 28 ) = ' '//trim(chanPrefix)//"Clst" - InitOut%WriteOutputUnt( m + 28 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 29 ) = ' '//trim(chanPrefix)//"Cdst" - InitOut%WriteOutputUnt( m + 29 ) = ' (-) ' - InitOut%WriteOutputHdr( m + 30 ) = ' '//trim(chanPrefix)//"Cmst" - InitOut%WriteOutputUnt( m + 30 ) = ' (-) ' - - end do - end do -#else do i=1,p%NumOuts InitOut%WriteOutputHdr(i) = p%OutParam(i)%Name InitOut%WriteOutputUnt(i) = p%OutParam(i)%Units end do -#endif InitOut%Ver = AD_Ver @@ -320,9 +239,6 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut ! Display the module information call DispNVD( AD_Ver ) -#ifdef DBG_OUTS - call WrScr(' - Compiled with DBG_OUTS') -#endif p%NumBlades = InitInp%NumBlades ! need this before reading the AD input file so that we know how many blade files to read @@ -564,11 +480,7 @@ subroutine Init_MiscVars(m, p, u, y, errStat, errMsg) m%SigmaCavitCrit = 0.0_ReKi m%CavitWarnSet = .false. ! arrays for output -#ifdef DBG_OUTS - allocate( m%AllOuts(0:p%NumOuts), STAT=ErrStat2 ) ! allocate starting at zero to account for invalid output channels -#else allocate( m%AllOuts(0:MaxOutPts), STAT=ErrStat2 ) ! allocate starting at zero to account for invalid output channels -#endif if (ErrStat2 /= 0) then call SetErrStat( ErrID_Fatal, "Error allocating AllOuts.", errStat, errMsg, RoutineName ) return @@ -1026,12 +938,6 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) !p%RootName = TRIM(InitInp%RootName)//'.AD' ! set earlier to it could be used -#ifdef DBG_OUTS - p%NBlOuts = 30 - p%numOuts = p%NumBlNds*p%NumBlades*p%NBlOuts - p%NTwOuts = 0 - -#else p%numOuts = InputFileData%NumOuts p%NBlOuts = InputFileData%NBlOuts p%BlOutNd = InputFileData%BlOutNd @@ -1047,7 +953,6 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) call setErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) if (ErrStat >= AbortErrLev) return -#endif end subroutine SetParameters !---------------------------------------------------------------------------------------------------------------------------------- @@ -1296,11 +1201,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) ! get values to output to file: !------------------------------------------------------- if (p%NumOuts > 0) then -#ifdef DBG_OUTS - call Calc_WriteDbgOutput( p, u, m, y, OtherState, xd, ErrStat2, ErrMsg2 ) -#else call Calc_WriteOutput( p, u, m, y, OtherState, xd, indx, ErrStat2, ErrMsg2 ) -#endif call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) !............................................................................................................................... @@ -1308,12 +1209,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) !............................................................................................................................... do i = 1,p%NumOuts ! Loop through all selected output channels -#ifdef DBG_OUTS - y%WriteOutput(i) = m%AllOuts( i ) -#else y%WriteOutput(i) = p%OutParam(i)%SignM * m%AllOuts( p%OutParam(i)%Indx ) -#endif - end do ! i - All selected output channels end if @@ -1586,9 +1482,7 @@ subroutine GeomWithoutSweepPitchTwist(p,u,m,thetaBladeNds,ErrStat,ErrMsg) call LAPACK_gemm( 'n', 't', 1.0_R8Ki, u%BladeRootMotion(k)%Orientation(:,:,1), u%HubMotion%Orientation(:,:,1), 0.0_R8Ki, orientation, errStat2, errMsg2) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) theta = EulerExtract( orientation ) !hub_theta_root(k) -#ifndef DBG_OUTS m%AllOuts( BPitch( k) ) = -theta(3)*R2D ! save this value of pitch for potential output -#endif theta(3) = 0.0_ReKi m%hub_theta_x_root(k) = theta(1) ! save this value for FAST.Farm diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index e1902ee4fb..51f73cf4f9 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1590,225 +1590,6 @@ REAL(ReKi) FUNCTION Calc_Chi0(V_diskAvg, V_dot_x) END FUNCTION Calc_Chi0 - -!---------------------------------------------------------------------------------------------------------------------------------- -SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, OtherState, xd, ErrStat, ErrMsg ) - - TYPE(AD_ParameterType), INTENT(IN ) :: p ! The module parameters - TYPE(AD_InputType), INTENT(IN ) :: u ! inputs - TYPE(AD_MiscVarType), INTENT(INOUT) :: m ! misc variables - TYPE(AD_OutputType), INTENT(IN ) :: y ! outputs - TYPE(AD_OtherStateType), INTENT(IN ) :: OtherState ! OtherState - TYPE(AD_DiscreteStateType),INTENT(IN ) :: xd ! Discrete states - INTEGER(IntKi), INTENT( OUT) :: ErrStat ! The error status code - CHARACTER(*), INTENT( OUT) :: ErrMsg ! The error message, if an error occurred - - ! local variables - integer, parameter :: indx = 1 ! m%BEMT_u(1) is at t; m%BEMT_u(2) is t+dt - CHARACTER(*), PARAMETER :: RoutineName = 'Calc_WriteOutput' - !INTEGER(intKi) :: ErrStat2 - !CHARACTER(ErrMsgLen) :: ErrMsg2 - - INTEGER(IntKi) :: j,k,i - REAL(ReKi) :: ct, st ! cosine, sine of theta - REAL(ReKi) :: cp, sp ! cosine, sine of phi - ! Transformation matrices - !real(R8Ki), dimension(3,3) :: M_cg ! from global to airfoil-chord (this is well defined, also called "n-t" system in AeroDyn) - real(ReKi), dimension(3,3) :: M_sg ! from global to section (this is ill-defined, also called "x-y" system in AeroDyn), this coordinate is used to define the "axial" and "tangential" inductions - real(ReKi), dimension(3,3) :: M_ph ! Transformation from hub to "blade-rotor-plane": n,t,r (not the same as AeroDyn) - real(ReKi), dimension(3,3) :: M_pg ! Transformation from global to "blade-rotor-plane" (n,t,r), with same x at hub coordinate system - real(ReKi) :: psi_hub ! Azimuth wrt hub - real(ReKi), dimension(3) :: Vind_g ! Induced velocity vector in global coordinates - real(ReKi), dimension(3) :: Vind_s ! Induced velocity vector in section coordinates (AeroDyn "x-y") - - - ! start routine: - ErrStat = ErrID_None - ErrMsg = "" - - - if (p%WakeMod /= WakeMod_FVW) then - ! blade outputs - do k=1,p%numBlades - - ! Rotor plane, polar coordinate system - psi_hub = TwoPi*(k-1)/p%NumBlades - M_ph(1,1:3) = (/ 1.0_ReKi, 0.0_ReKi , 0.0_ReKi /) - M_ph(2,1:3) = (/ 0.0_ReKi, cos(psi_hub), sin(psi_hub) /) - M_ph(3,1:3) = (/ 0.0_ReKi,-sin(psi_hub), cos(psi_hub) /) - M_pg = matmul(M_ph, u%HubMotion%Orientation(1:3,1:3,1) ) - - - ! m%AllOuts( BPitch( k) ) = calculated in SetInputsForBEMT - - do j=1,p%NumBlNds - - i = (k-1)*p%NumBlNds*p%NBlOuts + (j-1)*p%NBlOuts +1 - - m%AllOuts( i ) = m%BEMT_u(indx)%theta(j,k)*R2D - m%AllOuts( i+1 ) = m%BEMT_u(indx)%psi(k)*R2D - m%AllOuts( i+2 ) = -m%BEMT_u(indx)%Vx(j,k) - m%AllOuts( i+3 ) = m%BEMT_u(indx)%Vy(j,k) - - m%AllOuts( i+4 ) = m%BEMT_y%axInduction(j,k) - m%AllOuts( i+5 ) = m%BEMT_y%tanInduction(j,k) - m%AllOuts( i+6 ) = m%BEMT_y%Vrel(j,k) - m%AllOuts( i+7 ) = m%BEMT_y%phi(j,k)*R2D - m%AllOuts( i+8 ) = (m%BEMT_y%phi(j,k) - m%BEMT_u(indx)%theta(j,k))*R2D - - - m%AllOuts( i+9 ) = m%BEMT_y%Cl(j,k) - m%AllOuts( i+10 ) = m%BEMT_y%Cd(j,k) - m%AllOuts( i+11 ) = m%BEMT_y%Cm(j,k) - m%AllOuts( i+12 ) = m%BEMT_y%Cx(j,k) - m%AllOuts( i+13 ) = m%BEMT_y%Cy(j,k) - - ct=cos(m%BEMT_u(indx)%theta(j,k)) - st=sin(m%BEMT_u(indx)%theta(j,k)) - m%AllOuts( i+14 ) = m%BEMT_y%Cx(j,k)*ct + m%BEMT_y%Cy(j,k)*st - m%AllOuts( i+15 ) = -m%BEMT_y%Cx(j,k)*st + m%BEMT_y%Cy(j,k)*ct - - cp=cos(m%BEMT_y%phi(j,k)) - sp=sin(m%BEMT_y%phi(j,k)) - m%AllOuts( i+16 ) = m%X(j,k)*cp - m%Y(j,k)*sp - m%AllOuts( i+17 ) = m%X(j,k)*sp + m%Y(j,k)*cp - m%AllOuts( i+18 ) = m%M(j,k) - m%AllOuts( i+19 ) = m%X(j,k) - m%AllOuts( i+20 ) = -m%Y(j,k) - m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st - m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct - m%AllOuts( i+23 ) = 0.5_ReKi * p%BEMT%chord(j,k) * m%BEMT_y%Vrel(j,k) * m%BEMT_y%Cl(j,k) ! "Gam" [m^2/s] - - M_sg = m%WithoutSweepPitchTwist(:,:,j,k) ! global to "section" - !M_cg = u%BladeMotion(k)%Orientation(1:3,1:3,j) ! global to chord - - Vind_s = (/ -m%BEMT_u(indx)%Vx(j,k)*m%BEMT_y%axInduction(j,k), m%BEMT_u(indx)%Vy(j,k)*m%BEMT_y%tanInduction(j,k), 0.0_ReKi /) - Vind_g = matmul(Vind_s, M_sg) - - m%AllOuts( i+24 ) = dot_product(M_pg(1,:), Vind_g(1:3) ) ! Uihn, hub normal - m%AllOuts( i+25 ) = dot_product(M_pg(2,:), Vind_g(1:3) ) ! Uiht, hub tangential - m%AllOuts( i+26 ) = dot_product(M_pg(3,:), Vind_g(1:3) ) ! Uihr, hub radial - - end do ! nodes - end do ! blades - else ! (p%WakeMod == WakeMod_FVW) - call calc_WriteDbgOutputFVW() - endif - -contains - subroutine Calc_WriteDbgOutputFVW() - use BEMTUnCoupled, only: Compute_UA_AirfoilCoefs - ! local variables - integer, parameter :: indx = 1 ! m%BEMT_u(1) is at t; m%BEMT_u(2) is t+dt - CHARACTER(*), PARAMETER :: RoutineName = 'Calc_WriteOutput' - - INTEGER(IntKi) :: j,k,i - REAL(ReKi) :: ct, st ! cosine, sine of theta - REAL(ReKi) :: cp, sp ! cosine, sine of phi - real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re, theta - real(ReKi) :: GammaVal ! Vorticity - type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table - real(ReKi) :: UrelWind_s(3) ! Wind in section coords - real(ReKi) :: Vwnd(3) - real(ReKi) :: Cx, Cy, Cl_dyn, Cd_dyn, Cm_dyn - ! Transformation matrices - real(R8Ki), dimension(3,3) :: M_cg ! from global to airfoil-chord (this is well defined, also called "n-t" system in AeroDyn) - real(ReKi), dimension(3,3) :: M_sg ! from global to section (this is ill-defined, also called "x-y" system in AeroDyn), this coordinate is used to define the "axial" and "tangential" inductions - real(ReKi), dimension(3,3) :: M_ph ! Transformation from hub to "blade-rotor-plane": n,t,r (not the same as AeroDyn) - real(ReKi), dimension(3,3) :: M_pg ! Transformation from global to "blade-rotor-plane" (n,t,r), with same x at hub coordinate system - real(ReKi) :: psi_hub ! Azimuth wrt hub - - ! blade outputs - do k=1,p%NumBlades - - ! Rotor plane, polar coordinate system - psi_hub = TwoPi*(k-1)/p%NumBlades - M_ph(1,1:3) = (/ 1.0_ReKi, 0.0_ReKi , 0.0_ReKi /) - M_ph(2,1:3) = (/ 0.0_ReKi, cos(psi_hub), sin(psi_hub) /) - M_ph(3,1:3) = (/ 0.0_ReKi,-sin(psi_hub), cos(psi_hub) /) - M_pg = matmul(M_ph, u%HubMotion%Orientation(1:3,1:3,1) ) - - do j=1,p%NumBlNds -!TODO: Merge with BEM to avoid all code redundancy (discuss with Bonnie) - - i = (k-1)*p%NumBlNds*p%NBlOuts + (j-1)*p%NBlOuts +1 - - ! --- Computing main aero variables from induction - setting local variables - Vwnd = m%DisturbedInflow(:,j,k) ! NOTE: contains tower shadow - M_sg = m%WithoutSweepPitchTwist(:,:,j,k) ! global to "section" - M_cg = u%BladeMotion(k)%Orientation(1:3,1:3,j) ! global to chord - call FVW_AeroOuts(M_sg, M_cg, m%FVW%PitchAndTwist(j,k), u%BladeMotion(k)%TranslationVel(1:3,j), & - m%FVW_y%Vind(1:3,j,k), Vwnd, p%KinVisc, p%FVW%Chord(j,k), & - AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) - - ! NOTE: using airfoil coeffs at nodes - ! Compute steady Airfoil Coefs first - call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) - Cl_dyn = AFI_interp%Cl - Cd_dyn = AFI_interp%Cd - Cm_dyn = AFI_interp%Cm - - if ( m%FVW%UA_Flag) then ! Unsteady coeffs - if ((OtherState%FVW%UA_Flag(j,k)) .and. ( .not. EqualRealNos(Vrel,0.0_ReKi) ) ) then - m%FVW%m_UA%iBladeNode = j - m%FVW%m_UA%iBlade = k - call Compute_UA_AirfoilCoefs( alpha, Vrel, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), m%FVW%p_UA, xd%FVW%UA, OtherState%FVW%UA, m%FVW%y_UA, m%FVW%m_UA, Cl_dyn, Cd_dyn, Cm_dyn, ErrStat, ErrMsg) - end if - end if - - theta = m%FVW%PitchAndTwist(j,k) - - ! --- Setting aerodyn outputs (more or less generic code) - m%AllOuts( i ) = theta*R2D ! "Twst" = phi-alpha -! m%AllOuts( i+1 ) = m%FVW_u(indx)%psi(k)*R2D ! "Psi" - m%AllOuts( i+2 ) = -UrelWind_s(1) ! "Vx" - m%AllOuts( i+3 ) = UrelWind_s(2) ! "Vy" - - m%AllOuts( i+4 ) = AxInd ! "AIn" - m%AllOuts( i+5 ) = TanInd ! "ApIn" - m%AllOuts( i+6 ) = Vrel ! "Vrel" - m%AllOuts( i+7 ) = phi*R2D ! "Phi" - m%AllOuts( i+8 ) = alpha*R2D ! "AOA" - - cp = cos(phi) - sp = sin(phi) - Cx = Cl_dyn*cp + Cd_dyn*sp - Cy = Cl_dyn*sp - Cd_dyn*cp - m%AllOuts( i+9 ) = Cl_dyn ! "Cl" - m%AllOuts( i+10 ) = Cd_dyn ! "Cd" - m%AllOuts( i+11 ) = Cm_dyn ! "Cm" - m%AllOuts( i+12 ) = Cx ! "Cx" - m%AllOuts( i+13 ) = Cy ! "Cy" - - ct=cos(theta) - st=sin(theta) - m%AllOuts( i+14 ) = Cx*ct + Cy*st ! "Cn" - m%AllOuts( i+15 ) = -Cx*st + Cy*ct ! "Ct" - - cp=cos(phi) - sp=sin(phi) - m%AllOuts( i+16 ) = m%X(j,k)*cp - m%Y(j,k)*sp ! "Fl" - m%AllOuts( i+17 ) = m%X(j,k)*sp + m%Y(j,k)*cp ! "Fd" - m%AllOuts( i+18 ) = m%M(j,k) ! "M" - m%AllOuts( i+19 ) = m%X(j,k) ! "Fx" - m%AllOuts( i+20 ) = -m%Y(j,k) ! "Fy" - m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st ! "Fn" - m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct ! "Ft" - m%AllOuts( i+23 ) = 0.5_ReKi * p%FVW%Chord(j,k) * Vrel * Cl_dyn ! "Gam" [m^2/s] - - m%AllOuts( i+24 ) = dot_product(M_pg(1,:), m%FVW_y%Vind(1:3,j,k) ) ! Uihn, hub normal - m%AllOuts( i+25 ) = dot_product(M_pg(2,:), m%FVW_y%Vind(1:3,j,k) ) ! Uiht, hub tangential - m%AllOuts( i+26 ) = dot_product(M_pg(3,:), m%FVW_y%Vind(1:3,j,k) ) ! Uihr, hub radial - - m%AllOuts( i+27 ) = AFI_interp%Cl ! "Cl" static - m%AllOuts( i+28 ) = AFI_interp%Cd ! "Cd" static - m%AllOuts( i+29 ) = AFI_interp%Cm ! "Cm" static - - end do ! nodes - end do ! blades - end subroutine Calc_WriteDbgOutputFVW -END SUBROUTINE Calc_WriteDbgOutput - !---------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE Calc_WriteOutput( p, u, m, y, OtherState, xd, indx, ErrStat, ErrMsg ) @@ -3127,9 +2908,6 @@ SUBROUTINE AD_PrintSum( InputFileData, p, u, y, ErrStat, ErrMsg ) end if -#ifndef DBG_OUTS -! p%OutParam isn't allocated when DBG_OUTS is defined - OutPFmt = '( 15x, I4, 2X, A '//TRIM(Num2LStr(ChanLen))//',1 X, A'//TRIM(Num2LStr(ChanLen))//' )' WRITE (UnSu,'(15x,A)') 'Requested Output Channels:' WRITE (UnSu,'(15x,A)') 'Col Parameter Units' @@ -3138,7 +2916,6 @@ SUBROUTINE AD_PrintSum( InputFileData, p, u, y, ErrStat, ErrMsg ) DO I = 0,p%NumOuts WRITE (UnSu,OutPFmt) I, p%OutParam(I)%Name, p%OutParam(I)%Units END DO -#endif CLOSE(UnSu) From ea8b103088eb9a685c756b91b792b23c2d3be32b Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 30 Jun 2020 10:40:58 -0600 Subject: [PATCH 174/190] Fix for merge: glue code data hanlding bad merge --- modules/openfast-library/src/FAST_Solver.f90 | 4 ++-- modules/openfast-library/src/FAST_Subs.f90 | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/openfast-library/src/FAST_Solver.f90 b/modules/openfast-library/src/FAST_Solver.f90 index 97f54b468c..c6ab8755fa 100644 --- a/modules/openfast-library/src/FAST_Solver.f90 +++ b/modules/openfast-library/src/FAST_Solver.f90 @@ -4625,7 +4625,7 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca END IF IF ( p_FAST%CompInflow == Module_IfW ) THEN - CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(:), IfW%p, AD14%Input(1), AD%Input(1), AD%OtherSt(1), ED%Output(1), ErrStat2, ErrMsg2 ) + CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(:), IfW%p, AD14%Input(1), AD%Input(1), AD%OtherSt(1), ED%y, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ELSE IF ( p_FAST%CompInflow == Module_OpFM ) THEN ! OpenFOAM is the driver and it sets these inputs outside of this solve; the OpenFOAM inputs and outputs thus don't change @@ -4945,7 +4945,7 @@ SUBROUTINE SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, BD, IF (p_FAST%CompInflow == Module_IfW) THEN ! must be done after ED_CalcOutput and before AD_CalcOutput and SrvD - CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(:), IfW%p, AD14%Input(1), AD%Input(1), AD%OtherSt(1), ED%Output(1), ErrStat2, ErrMsg2 ) + CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(:), IfW%p, AD14%Input(1), AD%Input(1), AD%OtherSt(1), ED%y, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) !ELSE IF ( p_FAST%CompInflow == Module_OpFM ) THEN ! ! OpenFOAM is the driver and it computes outputs outside of this solve; the OpenFOAM inputs and outputs thus don't change diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index d5d7e26214..1ba775af77 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -522,7 +522,7 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, Init%InData_IfW%NumWindPoints = Init%InData_IfW%NumWindPoints + AD%Input(1)%BladeMotion(k)%NNodes END DO if (allocated(AD%OtherSt(STATE_CURR)%WakeLocationPoints)) then - InitInData_IfW%NumWindPoints = InitInData_IfW%NumWindPoints + size(AD%OtherSt(STATE_CURR)%WakeLocationPoints,DIM=2) + Init%InData_IfW%NumWindPoints = Init%InData_IfW%NumWindPoints + size(AD%OtherSt(STATE_CURR)%WakeLocationPoints,DIM=2) end if END IF @@ -4670,7 +4670,7 @@ SUBROUTINE WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, BD, AD14, TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(IN ) :: SrvD !< ServoDyn data TYPE(AeroDyn14_Data), INTENT(IN ) :: AD14 !< AeroDyn14 data - TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data ! Out so the AD%m%FVW%vtk misc vars can be changed + TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data TYPE(InflowWind_Data), INTENT(IN ) :: IfW !< InflowWind data TYPE(OpenFOAM_Data), INTENT(IN ) :: OpFM !< OpenFOAM data TYPE(HydroDyn_Data), INTENT(IN ) :: HD !< HydroDyn data @@ -5138,11 +5138,11 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, OpFM, H if (allocated(AD%m%FVW_u)) then if (allocated(AD%m%FVW_u(1)%WingsMesh)) then DO K=1,NumBl - call MeshWrVTK(p_FAST%TurbinePos, AD%m%FVW_u(1)%WingsMesh(k), trim(p_FAST%VTK_OutFileRoot)//'.FVW_WingsMesh'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, Twidth, AD%Input(1)%BladeMotion(k) ) + call MeshWrVTK(p_FAST%TurbinePos, AD%m%FVW_u(1)%WingsMesh(k), trim(p_FAST%VTK_OutFileRoot)//'.FVW_WingsMesh'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth, AD%Input(1)%BladeMotion(k) ) !call MeshWrVTK(p_FAST%TurbinePos, AD%Input(1)%BladeMotion(K), trim(p_FAST%OutFileRoot)//'.AD_BladeMotion'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2 ) END DO ! Free wake - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(p_FAST%VTK_OutFileRoot)//'.FVW', y_FAST%VTK_count, Twidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(p_FAST%VTK_OutFileRoot)//'.FVW', y_FAST%VTK_count, p_FAST%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords end if end if END IF @@ -5407,7 +5407,7 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW ! Free wake if (allocated(AD%m%FVW_u)) then if (allocated(AD%m%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(p_FAST%VTK_OutFileRoot)//'.FVW', y_FAST%VTK_count, Twidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords + call WrVTK_FVW(AD%p%FVW, AD%x(1)%FVW, AD%z(1)%FVW, AD%m%FVW, trim(p_FAST%VTK_OutFileRoot)//'.FVW', y_FAST%VTK_count, p_FAST%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords end if end if From 277ed683d30b68ce032fd533a2e108a82d5fdd86 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 30 Jun 2020 12:24:51 -0600 Subject: [PATCH 175/190] Some updates to reg test input files Still need to update baselines (after PR 373 gets merged in) --- reg_tests/r-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reg_tests/r-test b/reg_tests/r-test index a3ec584f3c..698893b75b 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit a3ec584f3c6c916789e941f70c0740e8e8b854cd +Subproject commit 698893b75b8f1c6a59c4942e105aee13d1bc6ac9 From a8d0286fba625829969cbf6a3a12ab6c4362ee40 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 30 Jun 2020 15:56:33 -0600 Subject: [PATCH 176/190] AD15: fix the invalid channel info for SgCav, SigCr, Gam --- modules/aerodyn/src/AeroDyn_IO.f90 | 6 ++++++ reg_tests/r-test | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index b4bef735ef..ac817ad671 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -3648,6 +3648,9 @@ SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) InvalidOutput( BNFn( :,i) ) = .true. InvalidOutput( BNFt( :,i) ) = .true. InvalidOutput( BNClrnc(:,i) ) = .true. + InvalidOutput( BNGam( :,i) ) = .true. + InvalidOutput( BNSgCav(:,i) ) = .true. + InvalidOutput( BNSigCr(:,i) ) = .true. END DO @@ -3689,6 +3692,9 @@ SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) InvalidOutput( BNFn( i,:) ) = .true. InvalidOutput( BNFt( i,:) ) = .true. InvalidOutput( BNClrnc(i,:) ) = .true. + InvalidOutput( BNGam( i,:) ) = .true. + InvalidOutput( BNSgCav(i,:) ) = .true. + InvalidOutput( BNSigCr(i,:) ) = .true. END DO diff --git a/reg_tests/r-test b/reg_tests/r-test index 698893b75b..7444dbfea9 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 698893b75b8f1c6a59c4942e105aee13d1bc6ac9 +Subproject commit 7444dbfea90a12843391a9012076ef51e82d901a From 3476019100a6567252fd84696d12605d4356086c Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Tue, 30 Jun 2020 16:48:57 -0600 Subject: [PATCH 177/190] AD15: set invalid criteria for CpMin --- modules/aerodyn/src/AeroDyn_IO.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index ac817ad671..c5a569bb17 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -3651,6 +3651,7 @@ SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) InvalidOutput( BNGam( :,i) ) = .true. InvalidOutput( BNSgCav(:,i) ) = .true. InvalidOutput( BNSigCr(:,i) ) = .true. + InvalidOutput( BNCpMin(:,i) ) = .true. END DO @@ -3695,6 +3696,7 @@ SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) InvalidOutput( BNGam( i,:) ) = .true. InvalidOutput( BNSgCav(i,:) ) = .true. InvalidOutput( BNSigCr(i,:) ) = .true. + InvalidOutput( BNCpMin(i,:) ) = .true. END DO From 8ff0b43cb382c4aa2b714d669518389dbaede407 Mon Sep 17 00:00:00 2001 From: andrew-platt Date: Wed, 1 Jul 2020 11:45:46 -0600 Subject: [PATCH 178/190] FVW: update the nodal outputs for AD15 --- docs/OtherSupporting/OutListParameters.xlsx | Bin 222407 -> 222600 bytes .../aerodyn/src/AeroDyn_AllBldNdOuts_IO.f90 | 86 ++++++++++++------ 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/docs/OtherSupporting/OutListParameters.xlsx b/docs/OtherSupporting/OutListParameters.xlsx index f4e11c0147c7d21f5b657eaca37300d549892bbf..f4c00bc9f5c050e32fbebe5515751b0529e02fbf 100644 GIT binary patch delta 113958 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�?oZ%5V|nzz2l%2?WehfgKJMyGK#M8y_G8 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

4__LBWfx_ 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 delta 37269 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⋘v5 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 literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/Schematics/Stencil.pdf b/docs/source/user/aerodyn-fvw/Schematics/Stencil.pdf new file mode 100644 index 0000000000000000000000000000000000000000..064309fe64a9c2689d5068044f02ddb7569225cd GIT binary patch literal 11847 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 k1~o8(7z*S4?X_LAH92y0RR91 literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.pdf b/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b0c1fb9c1094845adbb74958b8bfa9b368ca0457 GIT binary patch literal 5268 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 literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.tex b/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.tex new file mode 100644 index 0000000000..4f2289dd56 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/Schematics/VortexCodeWorkFlow.tex @@ -0,0 +1,80 @@ +%% Creator: Inkscape inkscape 0.92.3, www.inkscape.org +%% PDF/EPS/PS + LaTeX output extension by Johan Engelen, 2010 +%% Accompanies image file 'VortexCodeWorkFlow.pdf' (pdf, eps, ps) +%% +%% To include the image in your LaTeX document, write +%% \input{.pdf_tex} +%% instead of +%% \includegraphics{.pdf} +%% To scale the image, write +%% \def\svgwidth{} +%% \input{.pdf_tex} +%% instead of +%% \includegraphics[width=]{.pdf} +%% +%% Images with a different path to the parent latex file can +%% be accessed with the `import' package (which may need to be +%% installed) using +%% \usepackage{import} +%% in the preamble, and then including the image with +%% \import{}{.pdf_tex} +%% Alternatively, one can specify +%% \graphicspath{{/}} +%% +%% For more information, please see info/svg-inkscape on CTAN: +%% http://tug.ctan.org/tex-archive/info/svg-inkscape +%% +\begingroup% + \makeatletter% + \providecommand\color[2][]{% + \errmessage{(Inkscape) Color is used for the text in Inkscape, but the package 'color.sty' is not loaded}% + \renewcommand\color[2][]{}% + }% + \providecommand\transparent[1]{% + \errmessage{(Inkscape) Transparency is used (non-zero) for the text in Inkscape, but the package 'transparent.sty' is not loaded}% + \renewcommand\transparent[1]{}% + }% + \providecommand\rotatebox[2]{#2}% + \newcommand*\fsize{\dimexpr\f@size pt\relax}% + \newcommand*\lineheight[1]{\fontsize{\fsize}{#1\fsize}\selectfont}% + \ifx\svgwidth\undefined% + \setlength{\unitlength}{1584.33259703bp}% + \ifx\svgscale\undefined% + \relax% + \else% + \setlength{\unitlength}{\unitlength * \real{\svgscale}}% + \fi% + \else% + \setlength{\unitlength}{\svgwidth}% + \fi% + \global\let\svgwidth\undefined% + \global\let\svgscale\undefined% + \makeatother% + \begin{picture}(1,0.37987951)% + \lineheight{1}% + \setlength\tabcolsep{0pt}% + \put(0,0){\includegraphics[width=\unitlength,page=1]{VortexCodeWorkFlow.pdf}}% + \put(0.65944109,0.11446444){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.25535428\unitlength}\centering $\vec{v}_{i,ll}, (\vec{r}_{r})$\end{minipage}}}% + \put(0,0){\includegraphics[width=\unitlength,page=2]{VortexCodeWorkFlow.pdf}}% + \put(0.63558708,0.20735708){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.20717198\unitlength}\centering \textbf{Vortex code}\end{minipage}}}% + \put(0.60215168,0.18502836){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.27356877\unitlength}\centering $\vec{r}, \vec{\Gamma}_v$\end{minipage}}}% + \put(0.32921211,0.30084711){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.30485953\unitlength}\raggedright 1. Tower shadow model\\ \ \ \ \ (update of $\vec{V}_{\infty}$)\end{minipage}}}% + \put(0.32826903,0.23341478){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.3956149\unitlength}\raggedright 2. Induction computation\end{minipage}}}% + \put(0.32826903,0.13392726){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.5344643\unitlength}\raggedright 3. Quasi steady forces on the lifting lines\end{minipage}}}% + \put(0.32826903,0.08442459){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.51629305\unitlength}\raggedright 4. Dynamic stall model\end{minipage}}}% + \put(0.49325791,0.33819968){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.20355574\unitlength}\centering \textbf{AeroDyn15}\end{minipage}}}% + \put(0.79169925,0.10320964){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.25535428\unitlength}\centering $\vec{f}_{ll}$\end{minipage}}}% + \put(0,0){\includegraphics[width=\unitlength,page=3]{VortexCodeWorkFlow.pdf}}% + \put(0.79577993,0.29769798){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.25535428\unitlength}\centering $\vec{r}_{ll}, \vec{r}_{r}$\end{minipage}}}% + \put(0,0){\includegraphics[width=\unitlength,page=4]{VortexCodeWorkFlow.pdf}}% + \put(-0.00885268,0.27822337){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.20355574\unitlength}\centering \textbf{InflowWind}\end{minipage}}}% + \put(0,0){\includegraphics[width=\unitlength,page=5]{VortexCodeWorkFlow.pdf}}% + \put(0.69583824,0.27908716){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.08495527\unitlength}\centering \textbf{BEM}\end{minipage}}}% + \put(0,0){\includegraphics[width=\unitlength,page=6]{VortexCodeWorkFlow.pdf}}% + \put(0.01537801,0.32400133){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.44217675\unitlength}\centering $\vec{V}_\infty=$\\ $[\vec{V}_{\infty,ll}, \vec{V}_{\infty,r}] $\end{minipage}}}% + \put(0.02471082,0.17907865){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.41731078\unitlength}\centering $\vec{x}_{\text{elast},ll} =$\\ $ [\vec{r}_{ll}, \vec{\Lambda}_{ll}, \vec{\dot{r}}_{ll}, \vec{\omega}_{ll}]$\\ \end{minipage}}}% + \put(0,0){\includegraphics[width=\unitlength,page=7]{VortexCodeWorkFlow.pdf}}% + \put(-0.01097736,0.13673){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.20355574\unitlength}\centering \textbf{ElastoDyn}\end{minipage}}}% + \put(0.57551823,0.27600282){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.41731078\unitlength}\raggedright $\vec{r}_{\text{elast},ll}$\\ $\vec{V}_\infty$\end{minipage}}}% + \end{picture}% +\endgroup% diff --git a/docs/source/user/aerodyn-fvw/bibliography.bib b/docs/source/user/aerodyn-fvw/bibliography.bib new file mode 100644 index 0000000000..9423502063 --- /dev/null +++ b/docs/source/user/aerodyn-fvw/bibliography.bib @@ -0,0 +1,431 @@ +@article{Larsen08_1, + title= {Wake Meander: A Pragmatic Approach}, + author= {G. C. Larsen and H. A. Madsen and K. Thomsen and et al.}, + journal= {Wind Energy}, + volume= {11}, + pages= {337-95}, + year= {2008}, + publisher={John Wiley \& Sons, Ltd.}, + doi = {http://onlinelibrary.wiley.com/doi/10.1002/we.267/epdf} +} + +@inproceedings{Bagai94_1, + author= {A. Bagai and J. G. Leishman}, + title= {Rotor Free-Wake Modeling using a Pseudo-Implicit Technique Including Comparisons with Experimental Data}, + booktitle= {50th Annual Forum of the American Helicopter Society}, + year= {1994}, + address= {Washington, D.C.} +} + +@article{Quon18_1, + title= {Comparison of Wake Characterization Methods for Large-Eddy Simulations of a Rotor in Stratified Flow}, + author= {E. Quon and M. Churchfield and J. Jonkman}, + journal= {Computers & Fluids}, + year= {2018} +} + +@article{Martinez17_1, + title= {Optimal Smoothing Length Scale for Actuator Line Models of Wind Turbine Blades Based on Gaussian Body Force Distribution}, + author= {L. A. Martinez-Tossas and M. J. Churchfield and C. Meneveau}, + journal= {Wind Energy}, + volume= {20}, + pages= {1083-1096}, + year= {2017}, + publisher={John Wiley \& Sons, Ltd.}, + doi = {http://onlinelibrary.wiley.com/doi/10.1002/we.2081/epdf} +} + +@article{Vatistas91_1, + Author = {G. H. Vatistas and V. Koezel and W. C. Mih}, + Title = {A Simpler Model for Concentrated Vortices}, + Journal = {Experiments in Fluids}, + Volume = {11}, + Number = {1}, + Pages = {73-76}, + Year = {1991}} + +@article{Quon19_1, + title= {Comparison of Wake Characterization Methods for Large-Eddy Simulations of a Rotor in Stratified Flow}, + author= {E. Quon and M. Churchfield and J. Jonkman}, + journal= {Computers \& Fluids}, + year= {2019 (Forthcoming)} +} + +@inproceedings{Jonkman18_1, + title= {Validation of FAST.Farm Against Large-Eddy Simulations}, + author= {J. Jonkman and P. Doubrawa and N. Hamilton and et al.}, + series= {TORQUE 2018}, + date = {20-22}, + month = {June}, + year= {2018}, + publisher={EAWE}, + address= {Milano, Italy} +} + +@Book{Hansen08_1, + author = {M. O. L. Hansen}, + title = {Aerodynamics of Wind Turbines}, + publisher = {Earthscan}, + year = {2008}, + address = {London; Sterling, VA} +} + +@techreport{Weissinger47_1, + Author = {J. Weissinger}, + Title = {The Lift Distribution of Swept-Back Wings}, + Institution = {NACA}, + Type = {Technical report}, + Number = {TM 1120}, + Year = {1947} +} + +@techreport{Jonkman13_1, + Author = {J. Jonkman}, + Title = {The New Modularization Framework for the FAST Wind Turbine CAE Tool}, + Institution = {National Renewable Energy Laboratory}, + Type = {Technical report}, + Number = {NREL/CP-5000-57228}, + Year = {2013} +} + +@techreport{Jonkman15_1, + Author = {J. Jonkman and M. A. Sprague and B. J. Jonkman}, + Title = {FAST Modular Framework for Wind Turbine Simulation: New Algorithms and Numerical Examples}, + Institution = {National Renewable Energy Laboratory}, + Type = {Technical report}, + Number = {NREL/CP-2C00-63203}, + Year = {2015} +} + +@phdthesis{Gupta06_1, + Author = {S. Gupta}, + Title = {Development of a Time-Accurate Viscous Lagrangian Vortex Wake Model for Wind Turbine Applications}, + School = {Univeristy of Maryland}, + Address = {College Park, MD}, + Type = {PhD thesis}, + Year = {2006} +} + +@phdthesis{Scully75_1, + Author = {M. P. Scully}, + Title = {Computation of Helicopter Rotor Wake Geometry and Its Influence on Rotor Harmonic Airloads}, + School = {Massachusetts Institute of Technology}, + Address = {Cambridga, MA}, + Type = {PhD thesis}, + Year = {1975} +} + + +@phdthesis{Ribera07_1, + Author = {M. Ribera}, + Title = {Helicopter Flight Dynamics Simulation with a Time-Accurate Free-Vortex Wake Model}, + School = {University of Maryland}, + Address = {College Park, MD}, + Type = {PhD thesis}, + Year = {2007}} + +@inproceedings{Doubrawa18_1, + author= {P. Doubrawa and J. Annoni and J. Jonkman and et al.}, + title= {Optimization-Based Calibration of FAST.Farm Parameters Against SOWFA}, + booktitle={AIAA SciTech Forum}, + series= {36th Wind Energy Symposium}, + date = {8-13}, + month = {January}, + year= {2018}, + publisher={AIAA}, + address= {Kissimmee, FL}, + doi = {https://arc.aiaa.org/doi/pdf/10.2514/6.2018-0512} +} + +@inproceedings{Shaler19_1, + title= {FAST.Farm Response of Varying Wind Inflow Techniques}, + author= {K. Shaler and J. Jonkman and P. Doubrawa and N. Hamilton}, + booktitle={AIAA SciTech Forum}, + series= {37th Wind Energy Symposium}, + date = {7-11}, + month = {January}, + year= {2019}, + publisher={AIAA}, + address= {San Diego, CA}, + doi = {https://arc.aiaa.org/doi/pdf/10.2514/6.2019-2086} +} + +@inproceedings{Jonkman17_1, + title= {Development of FAST.Farm: A New MultiPhysics Engineering Tool for Wind-Farm Design and Analysis}, + author= {J. Jonkman and J. Annoni and G. Hayman and B. Jonkman and A. purkayastha}, + booktitle={AIAA SciTech Forum}, + series= {35th Wind Energy Symposium}, + date = {9-13}, + month = {January}, + year= {2017}, + publisher={AIAA}, + address= {Grapevine, TX}, + doi = {http://arc.aiaa.org/doi/pdf/10.2514/6.2017-0454} +} + +@inproceedings{Churchfield15_1, + title= {A Comparison of the Dynamic Wake Meandering Model, Large-Eddy Simulations, and Field Data at the Egmond aan Zee Offshore Wind Plant}, + author= {M. J. Churchfield and P. J. Moriarty and Y. Hao and et al.}, + booktitle={AIAA SciTech Forum}, + series= {33rd Wind Energy Symposium}, + date = {5-9}, + month = {January}, + year= {2015}, + publisher={AIAA}, + address= {Kissimmee, FL}, + doi = {http://dx.doi.org/10.2514/6.2015-0724} +} + +@inproceedings{Churchfield12_1, + title= {A Large-Eddy Simulation of Wind-Plant Aerodynamics}, + author= {M. J. Churchfield and P. J. Moriarty and L. A. Martinez and et al.}, + series= {50th AIAA Aerospace Sciences Meeting}, + date = {9-12}, + month = {January}, + year= {2012}, + publisher={AIAA}, + address= {Nashville, TN}, + doi = {http://dx.doi.org/10.2514/6.2012-537} +} + +@techreport{Jonkman09_1, + title= {Definition of a 5-MW Reference Wind Turbine for Offshore System Development}, + author= {Jonkman, J. and Butterfield, S. and Musial, W. and Scott, G.}, + number= {NREL/TP-500-38060}, + institution={National Renewable Energy Laboratory}, + address= {Golden, CO}, + month= {February}, + year= {2009} +} + +@techreport{TurbSim_1, + title= {TurbSim User's Guide v2.00.00}, + author= {Jonkman, B.}, + number= {NREL/TP-xxxx-xxxxx}, + institution={National Renewable Energy Laboratory}, + address= {Golden, CO}, + month= {October}, + year= {2014} +} + +@techreport{IEC_1, + title= {Wind Turbines - Part 1: Design Requirements}, + author= {IEC 61400-1}, + number= {3rd edition}, + institution={International Electrotechnical Commission}, + address= {Geneva, Switzerland}, + month= {March}, + year= {2006} +} + +@techreport{Simms01_1, + title= {NREL Unsteady Aerodynamics Experiment in the NASA-Ames Wind Tunnel: A Comparison of Predictions to Measurements}, + author= {Simms, D. and Schreck, S. and Hand, M. and Fingersh, L.J.}, + number= {NREL/TP-500-29494}, + institution={National Renewable Energy Laboratory}, + address= {Golden, CO}, + month= {June}, + year= {2001} +} + +@techreport{Jonkman18_2, + title= {FAST.Farm User's Guide and Theory Manual}, + author= {J. M. Jonkman}, + number= {NREL/TP-xxxx-xxxxx}, + institution={National Renewable Energy Laboratory}, + address= {Golden, CO}, + month= {Unpublished}, + year= {2018} +} + +@misc{FAST, +title = {OpenFAST Documentation}, +month = {November}, +year = {2017}, +url = {http://openfast.readthedocs.io/en/master/} +} + +@misc{SAMWICH, + author = {E. Quon}, + title = {SAMWICH Wake-Tracking Toolbox}, + publisher = {GitHub}, + journal = {GitHub repository}, + howpublished = {\url{https://github.com/ewquon/waketracking}}, + year= {2017} +} + +@phdthesis{Krista12_1, + Author = {K. Kecskemety}, + Title = {Assessing the Influence of Wake Dynamics on the Performance and Aeroelastic Behavior of Wind Turbines}, + School = {Ohio State University}, + Address = {Columbus, OH}, + Type = {PhD thesis}, + Year = {2012}} + +@book{Leishman_book, + Author = {J. Leishman}, + Title = {Principles of Helicopter Aerodynamics}, + Publisher = {Cambridge Univ. Press}, + Address = {Cambridge, MA}, + Year = {2006}} + +@book{Rankine58_1, + Author = {W. J. M. Rankine}, + Title = {Manual of Applied Mechanics}, + Publisher = {Griffen Co.}, + Address = {London}, + Year = {1858}} + +@article{Leishman02_1, + Author = {J. G. Leishman and M. J. Bhagwat and A. Bagai}, + Title = {Free-Vortex Filament Methods for the Analysis of Helicopter Rotor Wakes}, + Journal = {Journal of Aircraft}, + Volume = {39}, + Number = {5}, + Pages = {759-775}, + Year = {2002}} + +@inproceedings{Ananthan02_1, + Author = {S. Ananthan and J. G. Leishman and M. Ramasamy}, + Title = {The Role of Filament Stretching in the Free-Vortex Modeling of Rotor Wakes}, + booktitle = {58th Annual Forum and Technology Display of the American Helicopter Society International}, + year = {2002}, + address = {Montreal, Canada}} + +@article{Gupta05_1, + Author = {S. Gupta and J. G. Leishman}, + Title = {Free-Vortex Filament Methods for the Analysis of Helicopter Rotor Wakes}, + Journal = {Journal of Aircraft}, + Volume = {39}, + Number = {5}, + Pages = {759-775}, + Year = {2002}} + +@techreport{Wiser17_1, + title= {2016 Wind Technologies Market Report}, + author= {R. Wiser and M. Bolinger}, + number= {DOE/GO-102917-5033}, + institution={Lawrence Berkeley National Laboratory}, + address= {Berkeley, CA}, + month= {August}, + year= {2017}, + doi = {10.2172/1393638}} + +@phdthesis{Shaler19_2, + Author = {K. Shaler}, + Title = {Wake Interaction Modeling Using A Parallelized Free Vortex Wake Model}, + School = {Ohio State University}, + Address = {Columbus, OH}, + Type = {PhD thesis}, + Year = {2020}} + +@phdthesis{Abedi16_1, + Author = {H. Abedi}, + Title = {Development of Vortex Filament Method for Wind Power Aerodynamics}, + School = {Chalmers University of Technology}, + Address = {Gothenburg, Sweden}, + Type = {PhD thesis}, + Year = {2016}} + + +@techreport{Johnson19_1, + Author={N. Johnson and P. Bortolotti and K. Dykes and et al.}, + Title={Investigation of Innovative Rotor Concepts for the Big Adaptive Rotor Project}, + institution={National Renewable Energy Laboratory}, + number={NREL/TP-5000-73605}, + Year=2019 + } + +@techreport{Sprague15_1, + title={FAST Modular Framework for Wind Turbine Simulation: New Algorithms and Numerical Examples}, + author={Michael A. Sprague and Jason M. Jonkman and Bonnie J. Jonkman}, + institution={National Renewable Energy Laboratory}, + number={NREL/CP-2C00-63203}, + year={2015} +} + +@article{Miras17_1, + author = {M. Sessarego and N. Ramos Garc{\'i}a and J. N. S{\o}rensen and W. Z. Shen}, + title = {Development of an aeroelastic code based on three-dimensional viscous-inviscid method for wind turbine computations}, + year = {2017}, + doi = {10.1002/we.2085}, + volume = {20}, + pages = {1145-1170}, + journal = {Wind Energy}, + number = {7}, +} + +@article{Bagai93_1, + author = {A. Bagai and J. G. Leishman}, + title = {Flow Visualization of Compressible Vortex Structures Using Density Gradient Techniques}, + year = {1993}, + volume = {15}, + pages = {431-442}, + journal = {Experiments in Fluids}, + number = {6} +} + +@phdthesis{Papadakis14_1, + author = {G. Papadakis}, + title = {Development of a hybrid compressible vortex particle method and application to external problems including helicopter flows}, + school = {National Technical University of Athens}, + year = {2014}, +} +@article{Voutsinas06_1, + author = {S. G. Voutsinas}, + title = {Vortex methods in aeronautics: how to make things work}, + journal = {International Journal of Computational Fluid Dynamics}, + year = {2006}, +} + +@article{Rosenhead31_1, + author = {L. Rosenhead}, + title = {The Formation of Vortices from a Surface of Discontinuity}, + journal = {Proceedings of the Royal Society of London. Series A, Containing Papers of a Mathematical and Physical Character}, + volume = {134}, + number = {823}, + pages = {170-192}, + url = {http://www.jstor.org/stable/95835}, + ISSN = {09501207}, + year = {1931}, + publisher = {The Royal Society}, +} + +@article{Winckelmans93_1, + author = {G. S. Winckelmans and A. Leonard}, + title = {Contributions to vortex particle methods for the computation of 3-dimensional incompressible unsteady flows}, + publicher = {Academic Press Inc. JNL-Comp Subscriptions}, + journal = {Journal Of Computational Physics}, + volume = {109}, + number = {2}, + pages = {247-273}, + year = {1993}, + issn = {00219991, 10902716} +} + +@article{Branlard15_1, +author = {E. Branlard and G. Papadakis and M. Gaunaa and G. Winckelmans and T. J. Larsen}, +title = {Aeroelastic large eddy simulations using vortex methods: unfrozen turbulent and sheared inflow}, +journal = {Journal of Physics: Conference Series (Online)}, +year = {2015}, +doi = {10.1088/1742-6596/625/1/012019}, +volume = {625}, +issn = {1742-6596}, +} + +@book{Branlard17_1, + author = {E. Branlard}, + title = {Wind Turbine Aerodynamics and Vorticity-Based Methods: Fundamentals and Recent Applications}, + year = {2017}, + publisher= {Springer International Publishing}, + doi={10.1007/978-3-319-55164-7}, + isbn={ 978-3-319-55163-0} +} + +@TECHREPORT{Garrel03_1, + author = {A. van Garrel}, + title = {Development of a Wind Turbine Aerodynamics Simulation Module}, + institution = {ECN}, + year = {2003}, + number = {ECN-C--03-079} +} diff --git a/docs/source/user/aerodyn-fvw/index.rst b/docs/source/user/aerodyn-fvw/index.rst new file mode 100644 index 0000000000..e6bde1a3aa --- /dev/null +++ b/docs/source/user/aerodyn-fvw/index.rst @@ -0,0 +1,43 @@ +OLAF User's Guide and Theory Manual (Free Vortex Wake) +====================================================== + +.. only:: html + + This document offers a quick reference guid for the free vortex wake module + named OLAF that is included in the AeroDyn module of OpenFAST. It is + intended to be used by the general user in combination with other features + of AeroDyn and other OpenFAST modules. 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 documentaiton here was derived from the OLAF users manual by K.\ Shaler, + E.\ Branlard, and A.\ Platt. (FIXME: add link to pub) + + +.. toctree:: + :maxdepth: 2 + + Acknowledgments.rst + Introduction.rst + Acronyms.rst + RunningOLAF.rst + InputFiles.rst + OutputFiles.rst + OLAFTheory.rst + FutureWork.rst + zrefs.rst + AppendixA.rst + AppendixB.rst + AppendixC.rst + + +.. Acknowledgements.rst + Introduction.rst + RunningOLAF.rst + InputFiles.rst + OutputFiles.rst + OLAFTheory.rst + FutureWork.rst + AppendixA.rst + AppendixB.rst + AppendixC.rst diff --git a/docs/source/user/aerodyn-fvw/zrefs.rst b/docs/source/user/aerodyn-fvw/zrefs.rst new file mode 100644 index 0000000000..da9c3ea9fb --- /dev/null +++ b/docs/source/user/aerodyn-fvw/zrefs.rst @@ -0,0 +1,9 @@ +.. only:: html + + References + ---------- + +.. bibliography:: bibliography.bib + + + diff --git a/docs/source/user/index.rst b/docs/source/user/index.rst index 125a16da8e..6c6c0559d0 100644 --- a/docs/source/user/index.rst +++ b/docs/source/user/index.rst @@ -14,6 +14,7 @@ Details on the transition from FAST v8 to OpenFAST may be found in :numref:`fast api_change.rst aerodyn/index.rst + aerodyn-fvw/index.rst beamdyn/index.rst fast_to_openfast.rst cppapi/index.rst From 722c1cadd8ee707b7117fd141eb3e19620ef51c9 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 3 Apr 2020 18:27:08 -0600 Subject: [PATCH 107/190] FVW: using FVWFile as variable name to be consistent with other inputs files --- modules/aerodyn/src/AeroDyn_IO.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 3c5963a034..9738cacd21 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -2395,7 +2395,7 @@ SUBROUTINE ReadPrimaryFile( InputFile, InputFileData, ADBlFile, OutFileRoot, UnE CALL ReadCom( UnIn, InputFile, 'Section Header: Free Vortex Wake (FVW) Theory Options', ErrStat2, ErrMsg2, UnEc ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ReadVar ( UnIn, InputFile, InputFileData%FVWFileName, 'FVWFileName', 'FVW input file name', ErrStat2, ErrMsg2, UnEc ) + CALL ReadVar ( UnIn, InputFile, InputFileData%FVWFileName, 'FVWFile', 'FVW input file name', ErrStat2, ErrMsg2, UnEc ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( PathIsRelative( InputFileData%FVWFileName ) ) InputFileData%FVWFileName = TRIM(PriPath)//TRIM(InputFileData%FVWFileName) From c186ffaca2a3b64fafafebbdf72af1b2ec0f440e Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sat, 4 Apr 2020 13:42:44 -0600 Subject: [PATCH 108/190] FVW: fix sub-cycling, storing RHS and substeps with dtaero --- modules/aerodyn/src/FVW.f90 | 63 ++++++++--- modules/aerodyn/src/FVW_IO.f90 | 4 + modules/aerodyn/src/FVW_Registry.txt | 2 + modules/aerodyn/src/FVW_Subs.f90 | 144 ++++++++++++++++-------- modules/aerodyn/src/FVW_Types.f90 | 162 +++++++++++++++++++++++++++ 5 files changed, 315 insertions(+), 60 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index cdfb47d97e..8edfa989ac 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -193,6 +193,8 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%Vwnd_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_NW= -999_ReKi; call AllocAry( m%Vind_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; + call AllocAry( m%dxdt_NW , 3 , p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW dxdt' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); m%dxdt_NW = -999999_ReKi; + call AllocAry( m%dxdt_FW , 3 , FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW dxdt' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); m%dxdt_FW = -999999_ReKi; ! Wind request points nMax = 0 nMax = nMax + p%nSpan * p%nWings ! Lifting line Control Points @@ -442,7 +444,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m nFWEff = min(m%nFW, p%nFWFree) ! --- Display some status to screen if (mod(n,10)==0) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F7.2,A)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's' - if (DEV_VERSION) print'(A,F10.3,A,F10.3,A,I0,A,I0,A,I0,A,L1)','Update states, t:',t,' t_u:', utimes(1),' Step:',n,' nNW:',m%nNW,' nFW:',m%nFW,' ComputeWake: ',m%ComputeWakeInduced + if (DEV_VERSION) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F7.2,A,L1)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's Comp:',m%ComputeWakeInduced ! --- Evaluation at t @@ -464,9 +466,11 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! Changes: x only call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return - !call print_x_NW_FW(p, m, z, x,'Map_') + !call print_x_NW_FW(p, m, x,'Map_') ! --- Integration between t and t+DTaero + ! NOTE: when sub-cycling, the previous convection velocity is used + ! If dtfvw = n dtaero, we assume xdot_local dtaero = xdot_stored * dtfvw/n if (p%IntMethod .eq. idEuler1) then call FVW_Euler1( t, uInterp, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return !elseif (p%IntMethod .eq. idRK4) then @@ -478,7 +482,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m else call SetErrStat(ErrID_Fatal,'Invalid time integration method:'//Num2LStr(p%IntMethod),ErrStat,ErrMsg,'FVW_UpdateState') end IF - !call print_x_NW_FW(p, m, z, x,'Conv') + !call print_x_NW_FW(p, m, x,'Conv') if (m%ComputeWakeInduced) then @@ -490,7 +494,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! --- t+DTaero ! Propagation/creation of new layer of panels call PropagateWake(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return - !call print_x_NW_FW(p, m, z, x,'Prop_') + !call print_x_NW_FW(p, m, x,'Prop_') endif ! Inputs at t+DTaero @@ -504,7 +508,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return - !call print_x_NW_FW(p, m, z, x,'Map2') + !call print_x_NW_FW(p, m, x,'Map2') ! --- Solve for circulation at t+p%DTaero ! Returns: z%Gamma_LL (at t+p%DTaero) @@ -515,7 +519,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! Changes: x only call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return - !call print_x_NW_FW(p, m, z, x,'Map3') + !call print_x_NW_FW(p, m, x,'Map3') ! set the wind points required for t+p%DTaero timestep CALL SetRequestedWindPoints(m%r_wind, x, p, m) @@ -578,7 +582,7 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt if(Failed()) return ! Only calculate freewake after start time and if on a timestep when it should be calculated. - if ((t>= p%FreeWakeStart) .and. m%ComputeWakeInduced) then + if ((t>= p%FreeWakeStart)) then nFWEff = min(m%nFW, p%nFWFree) ! --- Compute Induced velocities on the Near wake and far wake based on the marker postions: @@ -604,12 +608,17 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt VmeanFW(1:3) = VmeanFW(1:3) / (size(m%Vind_FW,4)*nFWEff*size(m%Vind_FW,2)) else VmeanFW=VmeanNW + ! Since we convect the first FW point, we need a reasonable velocity there + ! NOTE: mostly needed for sub-cycling and when no NW + m%Vind_FW(1, 1:FWnSpan+1, 1, 1:p%nWings) = VmeanFW(1) + m%Vind_FW(2, 1:FWnSpan+1, 1, 1:p%nWings) = VmeanFW(2) + m%Vind_FW(3, 1:FWnSpan+1, 1, 1:p%nWings) = VmeanFW(3) endif ! --- Convecting non-free FW with a constant induced velocity (and free stream) - m%Vind_FW(1, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) = VmeanFW(1) ! - m%Vind_FW(2, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) = VmeanFW(2) ! - m%Vind_FW(3, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax, 1:p%nWings) = VmeanFW(3) ! + m%Vind_FW(1, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax+1, 1:p%nWings) = VmeanFW(1) ! + m%Vind_FW(2, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax+1, 1:p%nWings) = VmeanFW(2) ! + m%Vind_FW(3, 1:FWnSpan+1, p%nFWFree+1:p%nFWMax+1, 1:p%nWings) = VmeanFW(3) ! if (DEV_VERSION) then call print_mean_4d( m%Vind_NW(:,:, 1:m%nNW+1,:), 'Mean induced vel. NW') @@ -619,8 +628,8 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt print'(A25,3F12.4)','MeanFW (non free)',VmeanFW call print_mean_4d( m%Vwnd_NW(:,:, 1:m%nNW+1,:), 'Mean wind vel. NW') call print_mean_4d( m%Vwnd_FW(:,:, 1:nFWEff+1,:), 'Mean wind vel. FWEff') - call print_mean_4d( m%Vwnd_FW(:,:, p%nFWFree:m%nFW+1,:), 'Mean wind vel. FWNF') - call print_mean_4d( m%Vwnd_FW(:,:, 1:m%nFW,:), 'Mean wind vel. FW') + call print_mean_4d( m%Vwnd_FW(:,:, (p%nFWFree+1):m%nFW+1,:), 'Mean wind vel. FWNF') + call print_mean_4d( m%Vwnd_FW(:,:, 1:m%nFW+1,:), 'Mean wind vel. FW') endif ! --- Vortex points are convected with the free stream and induced velocity @@ -638,7 +647,8 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt endif ! First NW point does not convect (bound to LL) dxdt%r_NW(1:3, :, 1:iNWStart-1, :)=0 - ! First FW point does not convect (bound to NW) + ! First FW point always convect (even if bound to NW) + ! This is done in case there is no NW panel !dxdt%r_FW(1:3, :, 1, :)=0 contains logical function Failed() @@ -670,12 +680,33 @@ subroutine FVW_Euler1( t, u, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) dt = real(p%DTaero,ReKi) ! NOTE: this is DTaero not DTfvw since we integrate at each sub time step ! Compute "right hand side" - CALL FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat2, ErrMsg2); if (Failed()) return + if (m%ComputeWakeInduced) then + CALL FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat2, ErrMsg2); if (Failed()) return + ! Storage of convection velocity, purely for sub-cycling for now + ! Since Euler1 is linear we use partial increments of dtaero0) then + if (any(m%dxdt_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings)<-999)) then + call print_x_NW_FW(p, m, x, 'STP') + print*,'FVW_Euler1: Attempting to convect FW with a wrong velocity' + STOP + endif + endif + endif ! Update of positions - x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dt * dxdt%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) = x%r_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) + dt * m%dxdt_NW(1:3, 1:p%nSpan+1, 1:m%nNW+1, 1:p%nWings) if ( m%nFW>0) then - x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) = x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + dt * dxdt%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) = x%r_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) + dt * m%dxdt_FW(1:3, 1:FWnSpan+1, 1:m%nFW+1, 1:p%nWings) endif ! Update of Gamma ! TODO, viscous diffusion, stretching diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index e20e2873b5..64a3c1a1ef 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -77,6 +77,10 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method (WakeRegMethod) not implemented')) return if (Check( Inp%DTfvw < p%DTaero, 'DTfvw must be >= DTaero from AD15.')) return + if (abs(Inp%DTfvw-p%DTaero)>epsilon(1.0_ReKi)) then + ! subcycling + if (Check(Inp%IntMethod/=idEuler1 , 'Sub-cycling (DTfvw>DTaro) is only possible with Forward Euler `IntMethod`')) return + endif if (Check( Inp%nNWPanels<0 , 'Number of near wake panels must be >=0')) return if (Check( Inp%nFWPanels<0 , 'Number of far wake panels must be >=0')) return diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index cd150a81c0..b5fd5879c1 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -84,6 +84,8 @@ typedef ^ ^ ReKi typedef ^ ^ Logical ComputeWakeInduced - - - "Compute induced velocities on this timestep" - typedef ^ ^ DbKi OldWakeTime - - - "Time the wake induction velocities were last calculated" s typedef ^ ^ ReKi tSpent - - - "Time spent in expensive Biot-Savart computation" s +typedef ^ ^ ReKi dxdt_NW :::: - - "State time derivatie, stored for subcylcing" - +typedef ^ ^ ReKi dxdt_FW :::: - - "State time derivatie, stored for subcylcing" - # ........ Input ............ # FVW_InputType diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index d7d77f2a8a..4520162506 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -248,10 +248,10 @@ subroutine Map_NW_FW(p, m, z, x, ErrStat, ErrMsg) if (.false.) print*,z%Gamma_LL(1,1) ! Just to avoid unused var warning endsubroutine Map_NW_FW -!> Propage the postions and circulation one index forward (loop from end to start) +!> Propage the positions and circulation one index forward (loop from end to start) subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states integer(IntKi), intent( out) :: ErrStat !< Error status of the operation @@ -267,7 +267,7 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) x%r_FW(1:3,iSpan,iAge,iW) = x%r_FW(1:3,iSpan,iAge-1,iW) enddo enddo - x%r_FW(1:3,1:FWnSpan,1,iW) = -999.9_ReKi ! Nullified + x%r_FW(1:3,1:FWnSpan+1,1,iW) = -999.9_ReKi ! Nullified enddo if (p%nFWMax>0) then do iW=1,p%nWings @@ -281,7 +281,7 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) endif ! --- Propagate near wake do iW=1,p%nWings - do iAge=p%nNWMax+1,iNWStart+1,-1 ! TODO TODO TODO Might need update + do iAge=p%nNWMax+1,iNWStart+1,-1 do iSpan=1,p%nSpan+1 x%r_NW(1:3,iSpan,iAge,iW) = x%r_NW(1:3,iSpan,iAge-1,iW) enddo @@ -298,29 +298,73 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) x%Gamma_NW(:,1:iNWStart,iW) = -999.9_ReKi ! Nullified enddo endif + + ! Temporary hack for sub-cycling since straight after wkae computation, the wake size will increase + ! So we do a "fake" propagation here + do iW=1,p%nWings + do iAge=p%nFWMax+1,2,-1 ! + do iSpan=1,FWnSpan+1 + m%dxdt_FW(1:3,iSpan,iAge,iW) = m%dxdt_FW(1:3,iSpan,iAge-1,iW) + enddo + enddo + !m%dxdt_FW(1:3,1:FWnSpan+1,1,iW) = -999999_ReKi ! Important not nullified. The best would be to map the last NW convection velocity for this first row. + enddo + do iW=1,p%nWings + do iAge=p%nNWMax+1,iNWStart+1,-1 + do iSpan=1,p%nSpan+1 + m%dxdt_NW(1:3,iSpan,iAge,iW) = m%dxdt_NW(1:3,iSpan,iAge-1,iW) + enddo + enddo + m%dxdt_NW(1:3,:,1:iNWStart,iW) = 0.0_ReKi ! Nullified, wing do no convect, handled by LL,NW mapping + enddo + if (.false.) print*,m%nNW,z%Gamma_LL(1,1) ! Just to avoid unused var warning end subroutine PropagateWake -subroutine print_x_NW_FW(p, x, label) +subroutine print_x_NW_FW(p, m, x, label) type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states character(len=*),intent(in) :: label integer(IntKi) :: iAge - print*,'-------------------------' - print*,' NW .....................' + character(len=1):: flag + print*,'------------------------------------------------------------------' + print'(A,I0,A,I0)',' NW .....................iNWStart:',iNWStart,' nNW',m%nNW + do iAge=1,p%nNWMax+1 + flag='X' + if ((iAge)<= m%nNW+1) flag='.' + print'(A,A,I0,A)',flag,'iAge ',iAge,' Root Tip' + print*,trim(label)//'x', x%r_NW(1, 1, iAge,1), x%r_NW(1, p%nSpan+1, iAge,1) + print*,trim(label)//'y', x%r_NW(2, 1, iAge,1), x%r_NW(2, p%nSpan+1, iAge,1) + print*,trim(label)//'z', x%r_NW(3, 1, iAge,1), x%r_NW(3, p%nSpan+1, iAge,1) + enddo + print'(A,I0)','FW <<<<<<<<<<<<<<<<<<<< nFW:',m%nFW + do iAge=1,p%nFWMax+1 + flag='X' + if ((iAge)<= m%nFW+1) flag='.' + print'(A,A,I0,A)',flag,'iAge ',iAge,' Root Tip' + print*,trim(label)//'x', x%r_FW(1, 1, iAge,1), x%r_FW(1, FWnSpan+1, iAge,1) + print*,trim(label)//'y', x%r_FW(2, 1, iAge,1), x%r_FW(2, FWnSpan+1, iAge,1) + print*,trim(label)//'z', x%r_FW(3, 1, iAge,1), x%r_FW(3, FWnSpan+1, iAge,1) + enddo + print'(A,I0,A,I0)','dxdt NW .....................iNWStart:',iNWStart,' nNW',m%nNW do iAge=1,p%nNWMax+1 - print*,'iAge',iAge - print*,trim(label), x%r_NW(1, 1, iAge,1), x%r_NW(1, p%nSpan+1, iAge,1) - print*,trim(label), x%r_NW(2, 1, iAge,1), x%r_NW(2, p%nSpan+1, iAge,1) - print*,trim(label), x%r_NW(3, 1, iAge,1), x%r_NW(3, p%nSpan+1, iAge,1) + flag='X' + if ((iAge)<= m%nNW+1) flag='.' + print'(A,A,I0,A)',flag,'iAge ',iAge,' Root Tip' + print*,trim(label)//'x', m%dxdt_NW(1, 1, iAge,1), m%dxdt_NW(1, p%nSpan+1, iAge,1) + print*,trim(label)//'y', m%dxdt_NW(2, 1, iAge,1), m%dxdt_NW(2, p%nSpan+1, iAge,1) + print*,trim(label)//'z', m%dxdt_NW(3, 1, iAge,1), m%dxdt_NW(3, p%nSpan+1, iAge,1) enddo - print*,'FW <<<<<<<<<<<<<<<<<<<<' + print'(A,I0)','dxdt FW <<<<<<<<<<<<<<<<<<<< nFW:',m%nFW do iAge=1,p%nFWMax+1 - print*,'iAge',iAge - print*,trim(label), x%r_FW(1, 1, iAge,1), x%r_FW(1, FWnSpan+1, iAge,1) - print*,trim(label), x%r_FW(2, 1, iAge,1), x%r_FW(2, FWnSpan+1, iAge,1) - print*,trim(label), x%r_FW(3, 1, iAge,1), x%r_FW(3, FWnSpan+1, iAge,1) + flag='X' + if ((iAge)<= m%nFW+1) flag='.' + print'(A,A,I0,A)',flag,'iAge ',iAge,' Root Tip' + print*,trim(label)//'x', m%dxdt_FW(1, 1, iAge,1), m%dxdt_FW(1, FWnSpan+1, iAge,1) + print*,trim(label)//'y', m%dxdt_FW(2, 1, iAge,1), m%dxdt_FW(2, FWnSpan+1, iAge,1) + print*,trim(label)//'z', m%dxdt_FW(3, 1, iAge,1), m%dxdt_FW(3, FWnSpan+1, iAge,1) enddo endsubroutine @@ -448,13 +492,16 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG enddo SegConnct(3,iHeadC_bkp:) = SegConnct(3,iHeadC_bkp:) + m%nNW endif - if ((iHeadP-1)/=nP) then - print*,'PackPanelsToSegments: Number of points wrongly estimated',nP, iHeadP-1 - STOP ! Keep me. The check will be removed once the code is well established - endif - if ((iHeadC-1)/=nC) then - print*,'PackPanelsToSegments: Number of segments wrongly estimated',nC, iHeadC-1 - STOP ! Keep me. The check will be removed once the code is well established + if (DEV_VERSION) then + ! Safety checks + if ((iHeadP-1)/=nP) then + print*,'PackPanelsToSegments: Number of points wrongly estimated',nP, iHeadP-1 + STOP ! Keep me. The check will be removed once the code is well established + endif + if ((iHeadC-1)/=nC) then + print*,'PackPanelsToSegments: Number of segments wrongly estimated',nC, iHeadC-1 + STOP ! Keep me. The check will be removed once the code is well established + endif endif nSeg = iHeadC-1 nSegP = iHeadP-1 @@ -645,14 +692,16 @@ subroutine PackConvectingPoints() CALL LatticeToPoints(x%r_FW(1:3,:,1:nFWEff+1,iW), 1, CPs, iHeadP) enddo endif - - if (any(CPs(1,:)<=-99)) then - ErrMsg='PackConvectingPoints: Problem in Control points'; ErrStat=ErrID_Fatal; return - endif - if ((iHeadP-1)/=size(CPs,2)) then - print*,'PackConvectingPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 - STOP ! Keep me. The check will be removed once the code is well established - ErrMsg='PackConvectingPoints: Number of points wrongly estimated '; ErrStat=ErrID_Fatal; return + if (DEV_VERSION) then + ! Additional checks + if (any(CPs(1,:)<=-99)) then + ErrMsg='PackConvectingPoints: Problem in Control points'; ErrStat=ErrID_Fatal; return + endif + if ((iHeadP-1)/=size(CPs,2)) then + print*,'PackConvectingPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 + STOP ! Keep me. The check will be removed once the code is well established + ErrMsg='PackConvectingPoints: Number of points wrongly estimated '; ErrStat=ErrID_Fatal; return + endif endif end subroutine !> Distribute the induced velocity to the proper location @@ -666,14 +715,18 @@ subroutine UnPackInducedVelocity() do iW=1,p%nWings CALL VecToLattice(Uind, 1, m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,iW), iHeadP) enddo - if (any(m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,:)<-99)) then - ErrMsg='UnPackInducedVelocity: Problem in FW induced velocity on FW points'; ErrStat=ErrID_Fatal; return + if (DEV_VERSION) then + if (any(m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,:)<-99)) then + ErrMsg='UnPackInducedVelocity: Problem in FW induced velocity on FW points'; ErrStat=ErrID_Fatal; return + endif endif endif - if ((iHeadP-1)/=size(Uind,2)) then - print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 - STOP ! Keep me. The check will be removed once the code is well established - ErrMsg='UnPackInducedVelocity: Number of points wrongly estimated'; ErrStat=ErrID_Fatal; return + if (DEV_VERSION) then + if ((iHeadP-1)/=size(Uind,2)) then + print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 + STOP ! Keep me. The check will be removed once the code is well established + ErrMsg='UnPackInducedVelocity: Number of points wrongly estimated'; ErrStat=ErrID_Fatal; return + endif endif end subroutine @@ -742,12 +795,13 @@ subroutine PackLiftingLinePoints() do iW=1,p%nWings CALL LatticeToPoints(m%CP_LL(1:3,:,iW:iW), 1, CPs, iHeadP) enddo - if ((iHeadP-1)/=size(CPs,2)) then - print*,'PackLLPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 - STOP ! Keep me. The check will be removed once the code is well established + if (DEV_VERSION) then + if ((iHeadP-1)/=size(CPs,2)) then + print*,'PackLLPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 + STOP ! Keep me. The check will be removed once the code is well established + endif endif nCPs=iHeadP-1 - !print*,'Number of points packed for LL:',nCPs, nSegP end subroutine !> Distribute the induced velocity to the proper location @@ -756,9 +810,11 @@ subroutine UnPackLiftingLineVelocities() do iW=1,p%nWings CALL VecToLattice(Uind, 1, m%Vind_LL(1:3,:,iW:iW), iHeadP) enddo - if ((iHeadP-1)/=size(Uind,2)) then - print*,'UnPackLiftingLineVelocities: Number of points wrongly estimated',size(Uind,2), iHeadP-1 - STOP ! Keep me. The check will be removed once the code is well established + if (DEV_VERSION) then + if ((iHeadP-1)/=size(Uind,2)) then + print*,'UnPackLiftingLineVelocities: Number of points wrongly estimated',size(Uind,2), iHeadP-1 + STOP ! Keep me. The check will be removed once the code is well established + endif endif end subroutine end subroutine diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index d6b3b2aeec..23cc3e68a9 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -110,6 +110,8 @@ MODULE FVW_Types LOGICAL :: ComputeWakeInduced !< Compute induced velocities on this timestep [-] REAL(DbKi) :: OldWakeTime !< Time the wake induction velocities were last calculated [s] REAL(ReKi) :: tSpent !< Time spent in expensive Biot-Savart computation [s] + REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: dxdt_NW !< State time derivatie, stored for subcylcing [-] + REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: dxdt_FW !< State time derivatie, stored for subcylcing [-] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -1243,6 +1245,42 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) DstMiscData%ComputeWakeInduced = SrcMiscData%ComputeWakeInduced DstMiscData%OldWakeTime = SrcMiscData%OldWakeTime DstMiscData%tSpent = SrcMiscData%tSpent +IF (ALLOCATED(SrcMiscData%dxdt_NW)) THEN + i1_l = LBOUND(SrcMiscData%dxdt_NW,1) + i1_u = UBOUND(SrcMiscData%dxdt_NW,1) + i2_l = LBOUND(SrcMiscData%dxdt_NW,2) + i2_u = UBOUND(SrcMiscData%dxdt_NW,2) + i3_l = LBOUND(SrcMiscData%dxdt_NW,3) + i3_u = UBOUND(SrcMiscData%dxdt_NW,3) + i4_l = LBOUND(SrcMiscData%dxdt_NW,4) + i4_u = UBOUND(SrcMiscData%dxdt_NW,4) + IF (.NOT. ALLOCATED(DstMiscData%dxdt_NW)) THEN + ALLOCATE(DstMiscData%dxdt_NW(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 DstMiscData%dxdt_NW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%dxdt_NW = SrcMiscData%dxdt_NW +ENDIF +IF (ALLOCATED(SrcMiscData%dxdt_FW)) THEN + i1_l = LBOUND(SrcMiscData%dxdt_FW,1) + i1_u = UBOUND(SrcMiscData%dxdt_FW,1) + i2_l = LBOUND(SrcMiscData%dxdt_FW,2) + i2_u = UBOUND(SrcMiscData%dxdt_FW,2) + i3_l = LBOUND(SrcMiscData%dxdt_FW,3) + i3_u = UBOUND(SrcMiscData%dxdt_FW,3) + i4_l = LBOUND(SrcMiscData%dxdt_FW,4) + i4_u = UBOUND(SrcMiscData%dxdt_FW,4) + IF (.NOT. ALLOCATED(DstMiscData%dxdt_FW)) THEN + ALLOCATE(DstMiscData%dxdt_FW(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 DstMiscData%dxdt_FW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%dxdt_FW = SrcMiscData%dxdt_FW +ENDIF END SUBROUTINE FVW_CopyMisc SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -1328,6 +1366,12 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) ENDIF IF (ALLOCATED(MiscData%PitchAndTwist)) THEN DEALLOCATE(MiscData%PitchAndTwist) +ENDIF +IF (ALLOCATED(MiscData%dxdt_NW)) THEN + DEALLOCATE(MiscData%dxdt_NW) +ENDIF +IF (ALLOCATED(MiscData%dxdt_FW)) THEN + DEALLOCATE(MiscData%dxdt_FW) ENDIF END SUBROUTINE FVW_DestroyMisc @@ -1499,6 +1543,16 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 1 ! ComputeWakeInduced Db_BufSz = Db_BufSz + 1 ! OldWakeTime Re_BufSz = Re_BufSz + 1 ! tSpent + Int_BufSz = Int_BufSz + 1 ! dxdt_NW allocated yes/no + IF ( ALLOCATED(InData%dxdt_NW) ) THEN + Int_BufSz = Int_BufSz + 2*4 ! dxdt_NW upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%dxdt_NW) ! dxdt_NW + END IF + Int_BufSz = Int_BufSz + 1 ! dxdt_FW allocated yes/no + IF ( ALLOCATED(InData%dxdt_FW) ) THEN + Int_BufSz = Int_BufSz + 2*4 ! dxdt_FW upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%dxdt_FW) ! dxdt_FW + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -2005,6 +2059,50 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Db_Xferred = Db_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%tSpent Re_Xferred = Re_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%dxdt_NW) ) 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%dxdt_NW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dxdt_NW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%dxdt_NW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dxdt_NW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%dxdt_NW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dxdt_NW,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%dxdt_NW,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dxdt_NW,4) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%dxdt_NW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%dxdt_NW))-1 ) = PACK(InData%dxdt_NW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%dxdt_NW) + END IF + IF ( .NOT. ALLOCATED(InData%dxdt_FW) ) 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%dxdt_FW,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dxdt_FW,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%dxdt_FW,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dxdt_FW,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%dxdt_FW,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dxdt_FW,3) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%dxdt_FW,4) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%dxdt_FW,4) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%dxdt_FW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%dxdt_FW))-1 ) = PACK(InData%dxdt_FW,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%dxdt_FW) + END IF END SUBROUTINE FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2772,6 +2870,70 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Db_Xferred = Db_Xferred + 1 OutData%tSpent = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! dxdt_NW 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%dxdt_NW)) DEALLOCATE(OutData%dxdt_NW) + ALLOCATE(OutData%dxdt_NW(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%dxdt_NW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask4(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 mask4.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask4 = .TRUE. + IF (SIZE(OutData%dxdt_NW)>0) OutData%dxdt_NW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%dxdt_NW))-1 ), mask4, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%dxdt_NW) + DEALLOCATE(mask4) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! dxdt_FW 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%dxdt_FW)) DEALLOCATE(OutData%dxdt_FW) + ALLOCATE(OutData%dxdt_FW(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%dxdt_FW.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask4(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 mask4.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask4 = .TRUE. + IF (SIZE(OutData%dxdt_FW)>0) OutData%dxdt_FW = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%dxdt_FW))-1 ), mask4, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%dxdt_FW) + DEALLOCATE(mask4) + END IF END SUBROUTINE FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) From ec2b3cbd0822dbc0d83fcaa7e302e07ceff6f3bf Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sat, 4 Apr 2020 15:09:37 -0600 Subject: [PATCH 109/190] FVW: fix issue with one NW panel and polar circ solving --- modules/aerodyn/src/FVW_IO.f90 | 3 +++ modules/aerodyn/src/FVW_Subs.f90 | 16 ++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 64a3c1a1ef..8b57c3827b 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -81,6 +81,9 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) ! subcycling if (Check(Inp%IntMethod/=idEuler1 , 'Sub-cycling (DTfvw>DTaro) is only possible with Forward Euler `IntMethod`')) return endif + if (Inp%CirculationMethod == idCircPolarData) then + if (Check( Inp%nNWPanels<1 , 'Number of near wake panels (`nNWPanels`) must be >=1 when using circulation solving with polar data (`CircSolvingMethod=1`)')) return + endif if (Check( Inp%nNWPanels<0 , 'Number of near wake panels must be >=0')) return if (Check( Inp%nFWPanels<0 , 'Number of far wake panels must be >=0')) return diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 4520162506..c270e792e8 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -445,7 +445,7 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG integer(IntKi), intent(out) :: nSeg !< Total number of segments after packing integer(IntKi), intent(out) :: nSegP !< Total number of segments points after packing ! Local - integer(IntKi) :: iHeadC, iHeadP, nC, nP, iW, iHeadC_bkp + integer(IntKi) :: iHeadC, iHeadP, nC, nCNW, nP, iW, iHeadC_bkp logical :: LastNWShed ! If the FW contains Shed vorticity, we include the last shed vorticity form the NW, orhtwerise, we don't! @@ -455,13 +455,15 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG ! Counting total number of segments nP=0 nC=0 + nCNW=0 if ((m%nNW-iDepthStart)>=0) then nP = p%nWings * ( (p%nSpan+1)*(m%nNW-iDepthStart+2) ) - nC = p%nWings * (2*(p%nSpan+1)*(m%nNW-iDepthStart+2)-(p%nSpan+1)-(m%nNW-iDepthStart+1+1)) + nCNW = p%nWings * (2*(p%nSpan+1)*(m%nNW-iDepthStart+2)-(p%nSpan+1)-(m%nNW-iDepthStart+1+1)) if (.not.LastNWShed) then - nC = nC - p%nWings * (p%nSpan) ! Removing last set of sehd segments + nCNW = nCNW - p%nWings * (p%nSpan) ! Removing last set of shed segments endif endif + nC=nCNW if (m%nFW>0) then nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) if (p%FWShedVorticity) then @@ -482,9 +484,11 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG ! iHeadP=1 iHeadC=1 - do iW=1,p%nWings - CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .True., LastNWShed ) - enddo + if (nCNW>0) then + do iW=1,p%nWings + CALL LatticeToSegments(x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), iDepthStart, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .True., LastNWShed ) + enddo + endif if (m%nFW>0) then iHeadC_bkp = iHeadC do iW=1,p%nWings From a93c561b129b47aaad1b35f369b64a6710f05544 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sat, 4 Apr 2020 15:13:38 -0600 Subject: [PATCH 110/190] FVW: safe mean of empty array slices --- modules/aerodyn/src/FVW_VortexTools.f90 | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index 6514af424a..50e03fe80a 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -155,10 +155,12 @@ subroutine print_mean_4d(M, Label) real(ReKi), dimension(3) :: U ! U(1:3)=0 - do i=1,size(M,4); do j=1,size(M,3); do k=1,size(M,2); - U(1:3)= U(1:3)+ M(1:3, k, j, i) - enddo; enddo; enddo; - U(1:3)=U(1:3)/ (size(M,4)*size(M,3)*size(M,2)) + if ((size(M,4)*size(M,3)*size(M,2) )>0) then + do i=1,size(M,4); do j=1,size(M,3); do k=1,size(M,2); + U(1:3)= U(1:3)+ M(1:3, k, j, i) + enddo; enddo; enddo; + U(1:3)=U(1:3)/ (size(M,4)*size(M,3)*size(M,2)) + endif print'(A25,3F12.4)',trim(Label),U end subroutine @@ -169,10 +171,12 @@ subroutine print_mean_3d(M, Label) real(ReKi), dimension(3) :: U ! U(1:3)=0 - do i=1,size(M,3); do j=1,size(M,2) - U(1:3)= U(1:3)+ M(1:3, j, i) - enddo; enddo; - U(1:3)=U(1:3)/ (size(M,3)*size(M,2)) + if ((size(M,3)*size(M,2))>0) then + do i=1,size(M,3); do j=1,size(M,2) + U(1:3)= U(1:3)+ M(1:3, j, i) + enddo; enddo; + U(1:3)=U(1:3)/ (size(M,3)*size(M,2)) + endif print'(A24,3F12.4)',trim(Label),U end subroutine From 2b008fd698242ff83b6b7c13abccd6184ed2425d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sat, 4 Apr 2020 15:25:54 -0600 Subject: [PATCH 111/190] FVW: display when compiled with DBG_Outs --- modules/aerodyn/src/AeroDyn.f90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 63c3cf595e..587fac1530 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -309,6 +309,9 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut ! Display the module information call DispNVD( AD_Ver ) +#ifdef DBG_OUTS + call WrScr(' - Compiled with DBG_OUTS') +#endif p%NumBlades = InitInp%NumBlades ! need this before reading the AD input file so that we know how many blade files to read From 855b7fa4f8b99f913da83939f8bf4bcee52b8a0d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sat, 4 Apr 2020 17:18:01 -0600 Subject: [PATCH 112/190] FVW: introduced possibility to had NA points for debugging --- modules/aerodyn/src/FVW.f90 | 20 ++++++-- modules/aerodyn/src/FVW_Subs.f90 | 81 ++++++++++++++++++++++---------- 2 files changed, 72 insertions(+), 29 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 8edfa989ac..a80dc1a11c 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -120,6 +120,10 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL FVW_InitRegularization(p, m, ErrStat2, ErrMsg2); if(Failed()) return CALL FVW_ToString(p, m) ! Print to screen + ! Mapping NW and FW (purely for esthetics, and maybe wind) + call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + ! Initialize output CALL FVW_Init_Y( p, u, y, ErrStat2, ErrMsg2); if(Failed()) return @@ -220,9 +224,15 @@ subroutine FVW_InitStates( x, p, ErrStat, ErrMsg ) call AllocAry( x%Gamma_NW, p%nSpan , p%nNWMax , p%nWings, 'NW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_NW = -999999_ReKi; call AllocAry( x%Gamma_FW, FWnSpan , p%nFWMax , p%nWings, 'FW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_FW = -999999_ReKi; ! set x%r_NW and x%r_FW to (0,0,0) so that InflowWind can shortcut the calculations - call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_NW = 0.0_ReKi; - call AllocAry( x%r_FW , 3, FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%r_FW = 0.0_ReKi; - + call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); + call AllocAry( x%r_FW , 3, FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); + !if (DEV_VERSION) then + ! x%r_NW = -9999999_ReKi; + ! x%r_FW = -9999999_ReKi; + !else + x%r_NW = 0.0_ReKi; + x%r_FW = 0.0_ReKi; + !endif if (ErrStat >= AbortErrLev) return end subroutine FVW_InitStates ! ============================================================================== @@ -647,8 +657,8 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt endif ! First NW point does not convect (bound to LL) dxdt%r_NW(1:3, :, 1:iNWStart-1, :)=0 - ! First FW point always convect (even if bound to NW) - ! This is done in case there is no NW panel + ! First FW point always convects (even if bound to NW) + ! This is done for subcycling !dxdt%r_FW(1:3, :, 1, :)=0 contains logical function Failed() diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index c270e792e8..5d8bf08b1f 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -248,7 +248,7 @@ subroutine Map_NW_FW(p, m, z, x, ErrStat, ErrMsg) if (.false.) print*,z%Gamma_LL(1,1) ! Just to avoid unused var warning endsubroutine Map_NW_FW -!> Propage the positions and circulation one index forward (loop from end to start) +!> Propagate the positions and circulation one index forward (loop from end to start) subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables @@ -323,14 +323,14 @@ end subroutine PropagateWake subroutine print_x_NW_FW(p, m, x, label) - type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables - type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states + type(FVW_ParameterType), intent(in) :: p !< Parameters + type(FVW_MiscVarType), intent(in) :: m !< Initial misc/optimization variables + type(FVW_ContinuousStateType), intent(in) :: x !< Continuous states character(len=*),intent(in) :: label integer(IntKi) :: iAge character(len=1):: flag print*,'------------------------------------------------------------------' - print'(A,I0,A,I0)',' NW .....................iNWStart:',iNWStart,' nNW',m%nNW + print'(A,I0,A,I0)',' NW .....................iNWStart:',iNWStart,' nNW:',m%nNW do iAge=1,p%nNWMax+1 flag='X' if ((iAge)<= m%nNW+1) flag='.' @@ -348,24 +348,24 @@ subroutine print_x_NW_FW(p, m, x, label) print*,trim(label)//'y', x%r_FW(2, 1, iAge,1), x%r_FW(2, FWnSpan+1, iAge,1) print*,trim(label)//'z', x%r_FW(3, 1, iAge,1), x%r_FW(3, FWnSpan+1, iAge,1) enddo - print'(A,I0,A,I0)','dxdt NW .....................iNWStart:',iNWStart,' nNW',m%nNW - do iAge=1,p%nNWMax+1 - flag='X' - if ((iAge)<= m%nNW+1) flag='.' - print'(A,A,I0,A)',flag,'iAge ',iAge,' Root Tip' - print*,trim(label)//'x', m%dxdt_NW(1, 1, iAge,1), m%dxdt_NW(1, p%nSpan+1, iAge,1) - print*,trim(label)//'y', m%dxdt_NW(2, 1, iAge,1), m%dxdt_NW(2, p%nSpan+1, iAge,1) - print*,trim(label)//'z', m%dxdt_NW(3, 1, iAge,1), m%dxdt_NW(3, p%nSpan+1, iAge,1) - enddo - print'(A,I0)','dxdt FW <<<<<<<<<<<<<<<<<<<< nFW:',m%nFW - do iAge=1,p%nFWMax+1 - flag='X' - if ((iAge)<= m%nFW+1) flag='.' - print'(A,A,I0,A)',flag,'iAge ',iAge,' Root Tip' - print*,trim(label)//'x', m%dxdt_FW(1, 1, iAge,1), m%dxdt_FW(1, FWnSpan+1, iAge,1) - print*,trim(label)//'y', m%dxdt_FW(2, 1, iAge,1), m%dxdt_FW(2, FWnSpan+1, iAge,1) - print*,trim(label)//'z', m%dxdt_FW(3, 1, iAge,1), m%dxdt_FW(3, FWnSpan+1, iAge,1) - enddo + !print'(A,I0,A,I0)','dxdt NW .....................iNWStart:',iNWStart,' nNW:',m%nNW + !do iAge=1,p%nNWMax+1 + ! flag='X' + ! if ((iAge)<= m%nNW+1) flag='.' + ! print'(A,A,I0,A)',flag,'iAge ',iAge,' Root Tip' + ! print*,trim(label)//'x', m%dxdt_NW(1, 1, iAge,1), m%dxdt_NW(1, p%nSpan+1, iAge,1) + ! print*,trim(label)//'y', m%dxdt_NW(2, 1, iAge,1), m%dxdt_NW(2, p%nSpan+1, iAge,1) + ! print*,trim(label)//'z', m%dxdt_NW(3, 1, iAge,1), m%dxdt_NW(3, p%nSpan+1, iAge,1) + !enddo + !print'(A,I0)','dxdt FW <<<<<<<<<<<<<<<<<<<< nFW:',m%nFW + !do iAge=1,p%nFWMax+1 + ! flag='X' + ! if ((iAge)<= m%nFW+1) flag='.' + ! print'(A,A,I0,A)',flag,'iAge ',iAge,' Root Tip' + ! print*,trim(label)//'x', m%dxdt_FW(1, 1, iAge,1), m%dxdt_FW(1, FWnSpan+1, iAge,1) + ! print*,trim(label)//'y', m%dxdt_FW(2, 1, iAge,1), m%dxdt_FW(2, FWnSpan+1, iAge,1) + ! print*,trim(label)//'z', m%dxdt_FW(3, 1, iAge,1), m%dxdt_FW(3, FWnSpan+1, iAge,1) + !enddo endsubroutine @@ -378,7 +378,7 @@ subroutine print_x_NW_FW(p, m, x, label) !! of input and output arrays. subroutine SetRequestedWindPoints(r_wind, x, p, m) real(ReKi), dimension(:,:), allocatable, intent(inout) :: r_wind !< Position where wind is requested - type(FVW_ContinuousStateType), intent(in ) :: x !< States + type(FVW_ContinuousStateType), intent(inout) :: x !< States type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables integer(IntKi) :: iP_start,iP_end ! Current index of point, start and end of range @@ -386,6 +386,20 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m) ! Using array reshaping to ensure a given near or far wake point is always at the same location in the array. ! NOTE: Maximum number of points are passed, whether they "exist" or not. ! NOTE: InflowWind ignores points at (0,0,0) + !if (DEV_VERSION) then + ! ! Removing points that don't exist + ! !call print_x_NW_FW(p,m,x,'wind befr') + ! if (m%nNW<=p%nNWMax) then + ! x%r_NW(1:3, 1:p%nSpan+1, m%nNW+2:p%nNWMax+1, 1:p%nWings) = 0.0_ReKi + ! endif + ! if ( ((p%nNWMax<=1) .and. (m%nFW==0)) .or. ((m%nFW>0) .and. (m%nFW<=p%nFWMax))) then + ! x%r_FW(1:3, 1:FWnSpan+1, m%nFW+2:p%nFWMax+1, 1:p%nWings) = 0.0_ReKi + ! else + ! x%r_FW(1:3, 1:FWnSpan+1, m%nFW+1:p%nFWMax+1, 1:p%nWings) = 0.0_ReKi + ! endif + ! !call print_x_NW_FW(p,m,x,'wind after') + !endif + ! --- LL CP iP_start=1 iP_end=p%nWings*p%nSpan @@ -401,6 +415,24 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m) r_wind(1:3,iP_start:iP_end) = reshape( x%r_FW(1:3,1:FWnSpan+1,1:p%nFWMax+1,1:p%nWings), (/ 3, (FWnSpan+1)*(p%nFWMax+1)*p%nWings /)) endif + !if (DEV_VERSION) then + ! ! Additional checks + ! if (any(r_wind(3,:)<=-99999_ReKi)) then + ! call print_x_NW_FW(p,m,x,'wind after') + ! print*,'Error in wind' + ! STOP + ! endif + ! ! Removing points that don't exist + ! if (m%nNW<=p%nNWMax) then + ! x%r_NW(1:3, 1:p%nSpan+1, m%nNW+2:p%nNWMax+1, 1:p%nWings) = -999999.0_ReKi + ! endif + ! if ( ((p%nNWMax<=1) .and. (m%nFW==0)) .or. ((m%nFW>0) .and. (m%nFW<=p%nFWMax))) then + ! x%r_FW(1:3, 1:FWnSpan+1, m%nFW+2:p%nFWMax+1, 1:p%nWings) =-999999.0_ReKi + ! else + ! x%r_FW(1:3, 1:FWnSpan+1, m%nFW+1:p%nFWMax+1, 1:p%nWings) =-999999.0_ReKi + ! endif + !endif + end subroutine SetRequestedWindPoints @@ -699,6 +731,7 @@ subroutine PackConvectingPoints() if (DEV_VERSION) then ! Additional checks if (any(CPs(1,:)<=-99)) then + call print_x_NW_FW(p,m,x,'pack') ErrMsg='PackConvectingPoints: Problem in Control points'; ErrStat=ErrID_Fatal; return endif if ((iHeadP-1)/=size(CPs,2)) then From 6cf10d2b24ac8f9bd4203d231c1f515199b65acf Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sat, 4 Apr 2020 18:12:50 -0600 Subject: [PATCH 113/190] FVW: initializing circulations to zero for first call to calcoutput --- modules/aerodyn/src/FVW.f90 | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index a80dc1a11c..fd6795c2bc 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -120,7 +120,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL FVW_InitRegularization(p, m, ErrStat2, ErrMsg2); if(Failed()) return CALL FVW_ToString(p, m) ! Print to screen - ! Mapping NW and FW (purely for esthetics, and maybe wind) + ! Mapping NW and FW (purely for esthetics, and maybe wind) ! TODO, just points call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return @@ -221,17 +221,21 @@ subroutine FVW_InitStates( x, p, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" - call AllocAry( x%Gamma_NW, p%nSpan , p%nNWMax , p%nWings, 'NW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_NW = -999999_ReKi; - call AllocAry( x%Gamma_FW, FWnSpan , p%nFWMax , p%nWings, 'FW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); x%Gamma_FW = -999999_ReKi; + call AllocAry( x%Gamma_NW, p%nSpan , p%nNWMax , p%nWings, 'NW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); + call AllocAry( x%Gamma_FW, FWnSpan , p%nFWMax , p%nWings, 'FW Panels Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); ! set x%r_NW and x%r_FW to (0,0,0) so that InflowWind can shortcut the calculations call AllocAry( x%r_NW , 3, p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); call AllocAry( x%r_FW , 3, FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW Panels Points' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); !if (DEV_VERSION) then ! x%r_NW = -9999999_ReKi; ! x%r_FW = -9999999_ReKi; + ! x%Gamma_NW = -999999_ReKi; + ! x%Gamma_FW = -999999_ReKi; !else - x%r_NW = 0.0_ReKi; - x%r_FW = 0.0_ReKi; + x%r_NW = 0.0_ReKi + x%r_FW = 0.0_ReKi + x%Gamma_NW = 0.0_ReKi ! First call of calcoutput, states might not be set + x%Gamma_FW = 0.0_ReKi ! NOTE, these values might be mapped from z%Gamma_LL at init !endif if (ErrStat >= AbortErrLev) return end subroutine FVW_InitStates @@ -249,7 +253,9 @@ subroutine FVW_InitConstraint( z, p, m, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" ! - call AllocAry( z%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitConstraint' ); z%Gamma_LL = -999999_ReKi; + call AllocAry( z%Gamma_LL, p%nSpan, p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitConstraint' ); + !z%Gamma_LL = -999999_ReKi + z%Gamma_LL = 0.0_ReKi if (ErrStat >= AbortErrLev) return if(.false.) print*,m%nNW ! unused var for now @@ -803,6 +809,7 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, CALL DistributeRequestedWind(u%V_wind, p, m) ! if we are on a correction step, CalcOutput may be called again with different inputs + ! Compute m%Gamma_LL CALL Wings_ComputeCirculation(t, m%Gamma_LL, z%Gamma_LL, u, p, x, m, AFInfo, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only From a15e444b2a4acc604f2b2d4de6b391d9eeef16e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sun, 5 Apr 2020 16:42:16 -0600 Subject: [PATCH 114/190] FVW: fixed issue for repetitive update state calls at same time --- modules/aerodyn/src/FVW.f90 | 55 ++++++++++++++++++++-------- modules/aerodyn/src/FVW_Registry.txt | 1 + modules/aerodyn/src/FVW_Types.f90 | 7 ++++ 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index fd6795c2bc..60650b0435 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -167,7 +167,8 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%FirstCall = .True. m%nNW = iNWStart-1 ! Number of active nearwake panels m%nFW = 0 ! Number of active farwake panels - m%VTKstep = 0 ! Current step number for vtk output + m%iStep = 0 ! Current step number + m%VTKStep = -1 ! Counter of VTK outputs m%VTKlastTime = -HUGE(1.0_DbKi) m%tSpent = 0 @@ -442,26 +443,41 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m type(FVW_ConstraintStateType) :: z_guess ! < integer(IntKi) :: nP, nFWEff integer, dimension(8) :: time1, time2, time_diff + logical :: bReevaluation ErrStat = ErrID_None ErrMsg = "" - ! Compute Induced wake effects only if time since last compute is > DTfvw - if ( ( t - m%OldWakeTime ) >= p%DTfvw*OneMinusEpsilon ) then + + ! --- Handling of time step, and time compared to previous call + m%iStep = n + ! Reevaluation: two repetitive calls starting from the same time, we will roll back the wake emission + bReevaluation=.False. + if (abs(t-m%OldWakeTime)<0.25_ReKi* p%DTaero) then + bReevaluation=.True. + endif + ! Compute Induced wake effects only if time since last compute is > DTfvw + if ( (( t - m%OldWakeTime ) >= p%DTfvw*OneMinusEpsilon) ) then m%OldWakeTime = t m%ComputeWakeInduced = .TRUE. ! It's time to update the induced velocities from wake - call date_and_time(values=time1) else m%ComputeWakeInduced = .FALSE. endif + if (bReevaluation) then + print*,'[INFO] FVW: Update States: reevaluation at the same starting time' + call RollBackPreviousTimeStep() ! Cancel wake emission done in previous call + m%ComputeWakeInduced = .TRUE. + endif + if (m%ComputeWakeInduced) then + call date_and_time(values=time1) + endif nP = p%nWings * ( (p%nSpan+1)*(m%nNW-1+2) +(FWnSpan+1)*(m%nFW+1) ) nFWEff = min(m%nFW, p%nFWFree) ! --- Display some status to screen if (mod(n,10)==0) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F7.2,A)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's' - if (DEV_VERSION) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F7.2,A,L1)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's Comp:',m%ComputeWakeInduced - + if (DEV_VERSION) print'(A,F10.3,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,I0,A,F7.2,A,L1)','FVW status - t:',t,' n:',n,' nNW:',m%nNW-1,'/',p%nNWMax-1,' nFW:',nFWEff, '+',m%nFW-nFWEff,'=',m%nFW,'/',p%nFWMax,' nP:',nP,' spent:', m%tSpent, 's Comp:',m%ComputeWakeInduced ! --- Evaluation at t ! Inputs at t @@ -500,12 +516,9 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m end IF !call print_x_NW_FW(p, m, x,'Conv') - - if (m%ComputeWakeInduced) then -!FIXME: Manu: check my relocation of the PrepareNextTimeStep call. I moved it because the above routines use m%nNW+1, -! in their calculations, which shifted everything one extra age. That caused odd results with the requested -! wind positions if the first call to ui_seg occured before the near wake was fully populated. -! This may also have been your array bounds issue (with m%nFW+1 +1 occuring). + if (m%ComputeWakeInduced) then + ! We extend the wake length, i.e. we emit a new panel of vorticity at the TE + ! NOTE: this will be rolled back if UpdateState is called at the same starting time again call PrepareNextTimeStep() ! --- t+DTaero ! Propagation/creation of new layer of panels @@ -560,6 +573,14 @@ subroutine PrepareNextTimeStep() m%nNW=min(m%nNW+1, p%nNWMax) end subroutine PrepareNextTimeStep + subroutine RollBackPreviousTimeStep() + ! --- Decrease wake length if maximum not reached + if (m%nNW==p%nNWMax) then ! a far wake exist + m%nFW=max(m%nFW-1, 0) + endif + m%nNW=max(m%nNW-1, 0) + end subroutine RollBackPreviousTimeStep + logical function Failed() call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'FVW_UpdateStates') Failed = ErrStat >= AbortErrLev @@ -855,6 +876,11 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ! --- Write to local VTK at fps requested if (p%WrVTK==1) then + if (m%VTKStep==-1) then + m%VTKStep = 0 ! Has never been called, special handling for init + else + m%VTKStep = m%iStep+1 ! We use glue code step number for outputs + endif if (m%FirstCall) then call MKDIR('vtk_fvw') endif @@ -863,16 +889,15 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, if ((p%VTKCoord==2).or.(p%VTKCoord==3)) then ! Hub reference coordinates, for export only, ALL VTK Will be exported in this coordinate system! call set_vtk_coordinate_transform(u%HubOrientation,u%HubPosition) - call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Hub', m%VTKstep, 9) + call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Hub', m%VTKStep, 9) endif if ((p%VTKCoord==1).or.(p%VTKCoord==3)) then ! Global coordinate system, ALL VTK will be exported in global call set_vtk_no_coordinate_transform() - call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Glb', m%VTKstep, 9) + call WrVTK_FVW(p, x, z, m, 'vtk_fvw/'//trim(p%RootName)//'FVW_Glb', m%VTKStep, 9) endif endif endif - m%VTKstep = m%VTKstep + 1 ! Increment VTK counter no matter what contains diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index b5fd5879c1..be1349e03e 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -77,6 +77,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi Vind_FW :::: - - "Induced velocity on far wake panels" m/s typedef ^ ^ IntKi nNW - - - "Number of active near wake panels" - typedef ^ ^ IntKi nFW - - - "Number of active far wake panels" - +typedef ^ ^ IntKi iStep - - - "Current step number used for update state" - typedef ^ ^ IntKi VTKstep - - - "Current vtk output step number" - typedef ^ ^ DbKi VTKlastTime - - - "Time the last VTK file set was written out" s typedef ^ ^ ReKi r_wind :: - - "List of points where wind is requested for next time step" - diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 23cc3e68a9..5cde7262ca 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -103,6 +103,7 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: Vind_FW !< Induced velocity on far wake panels [m/s] INTEGER(IntKi) :: nNW !< Number of active near wake panels [-] INTEGER(IntKi) :: nFW !< Number of active far wake panels [-] + INTEGER(IntKi) :: iStep !< Current step number used for update state [-] INTEGER(IntKi) :: VTKstep !< Current vtk output step number [-] REAL(DbKi) :: VTKlastTime !< Time the last VTK file set was written out [s] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: r_wind !< List of points where wind is requested for next time step [-] @@ -1212,6 +1213,7 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) ENDIF DstMiscData%nNW = SrcMiscData%nNW DstMiscData%nFW = SrcMiscData%nFW + DstMiscData%iStep = SrcMiscData%iStep DstMiscData%VTKstep = SrcMiscData%VTKstep DstMiscData%VTKlastTime = SrcMiscData%VTKlastTime IF (ALLOCATED(SrcMiscData%r_wind)) THEN @@ -1528,6 +1530,7 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si END IF Int_BufSz = Int_BufSz + 1 ! nNW Int_BufSz = Int_BufSz + 1 ! nFW + Int_BufSz = Int_BufSz + 1 ! iStep Int_BufSz = Int_BufSz + 1 ! VTKstep Db_BufSz = Db_BufSz + 1 ! VTKlastTime Int_BufSz = Int_BufSz + 1 ! r_wind allocated yes/no @@ -2017,6 +2020,8 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%nFW Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%iStep + Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKstep Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%VTKlastTime @@ -2808,6 +2813,8 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%nFW = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%iStep = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%VTKstep = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%VTKlastTime = DbKiBuf( Db_Xferred ) From 018d533f2bf707fba43e7b494f6d4422c6c498dc Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sun, 5 Apr 2020 17:07:36 -0600 Subject: [PATCH 115/190] FVW: introducing a crude engineering fix for vorticies entering the ground --- modules/aerodyn/src/FVW.f90 | 3 ++ modules/aerodyn/src/FVW_Subs.f90 | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 60650b0435..8530932039 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -516,6 +516,9 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m end IF !call print_x_NW_FW(p, m, x,'Conv') + ! --- Fake handling of ground effect + call FakeGroundEffect(p, x, m, ErrStat, ErrMsg) + if (m%ComputeWakeInduced) then ! We extend the wake length, i.e. we emit a new panel of vorticity at the TE ! NOTE: this will be rolled back if UpdateState is called at the same starting time again diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 5d8bf08b1f..ebd9bd4349 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -538,6 +538,11 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG print*,'PackPanelsToSegments: Number of segments wrongly estimated',nC, iHeadC-1 STOP ! Keep me. The check will be removed once the code is well established endif + if (any(SegPoints(3,:)<-99._ReKi)) then + call print_x_NW_FW(p,m,x,'pack') + print*,'PackPanelsToSegments: some segments are NAN' + STOP ! Keep me. The check will be removed once the code is well established + endif endif nSeg = iHeadC-1 nSegP = iHeadP-1 @@ -856,6 +861,50 @@ subroutine UnPackLiftingLineVelocities() end subroutine end subroutine +!> Fake ground effect handling to prevents vortices to enter the ground +!! For now a crude bounding is done, engineering models may follow +!! True account of the ground effect (using mirroring or panels) should be done elsewhere +!! This assumes that the ground is at z=0, in harmony with inflow wind +subroutine FakeGroundEffect(p, x, m, ErrStat, ErrMsg) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(inout) :: x !< States + type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: iAge, iWing, iSpan + integer(IntKi) :: nBelow + real(ReKi), parameter:: GROUND = 1.e-4_ReKi + real(ReKi), parameter:: ABOVE_GROUND = 0.1_ReKi + ErrStat = ErrID_None + ErrMsg = "" + + nBelow=0 + do iWing = 1,p%nWings + do iAge = 1,m%nNW+1 + do iSpan = 1,p%nSpan+1 + if (x%r_NW(3, iSpan, iAge, iWing) < GROUND) then + x%r_NW(3, iSpan, iAge, iWing) = ABOVE_GROUND ! could use m%dxdt + nBelow=nBelow+1 + endif + enddo + enddo + enddo + if (m%nFW>0) then + do iWing = 1,p%nWings + do iAge = 1,m%nFW+1 + do iSpan = 1,FWnSpan + if (x%r_FW(3, iSpan, iAge, iWing) < GROUND) then + x%r_FW(3, iSpan, iAge, iWing) = ABOVE_GROUND ! could use m%dxdt + nBelow=nBelow+1 + endif + enddo + enddo + enddo + endif + if (nBelow>0) then + print*,'[WARN] Check the simulation, some vortices were found below the ground: ',nBelow + endif +end subroutine FakeGroundEffect !> Compute typical aerodynamic outputs based on: !! - the lifting line velocities in global coordinates From d0bea93dcbf7f55686013983938efe8a59b37b66 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sun, 5 Apr 2020 18:22:17 -0600 Subject: [PATCH 116/190] FVW: fixed gamma scaling --- modules/aerodyn/src/FVW_Wings.f90 | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index dc1bc8e10c..934b0bf1e2 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -105,7 +105,7 @@ subroutine Wings_Panelling_Init(Meshes, p, m, ErrStat, ErrMsg ) enddo ! --- Control points spanwise location ! NOTE: we use the cos approximation of VanGarrel. For equispacing, it returns mid point - ! otehrwise, points are slightly closer to panels that are shorter + ! otherwise, points are slightly closer to panels that are shorter !call Meshing('middle' , m%s_LL(:,iW), p%nSpan, m%s_CP_LL(:,iW)) call Meshing('fullcosineapprox' , m%s_LL(:,iW), p%nSpan, m%s_CP_LL(:,iW)) call InterpArray(m%s_LL(:,iW), m%chord_LL(:,iW), m%s_CP_LL(:,iW), m%chord_CP_LL(:,iW)) @@ -231,21 +231,25 @@ subroutine Wings_ComputeCirculation(t, Gamma_LL, Gamma_LL_prev, u, p, x, m, AFIn integer(IntKi), intent(in) :: iLabel ! Local integer(IntKi) :: iW - real(DbKi) :: s, ExpTerm + real(DbKi) :: s, RealAxis real(ReKi) :: GammaScale ErrStat = ErrID_None ErrMsg = "" if (t= 9) then - ExpTerm=max( (1-2*s)/(s*(s-1._DbKi)+1.0e-04_DbKi),10.0_DbKi) ! Bounding exponential to avoid overflow - GammaScale = 1._ReKi- 1._ReKi/(1._ReKi+exp(real(ExpTerm,ReKi))) ! Using a smooth approximation of HeavySide function + ! Smooth approximations of the Heavyside function + ! Example 1: 1/2 (1+2/pi arctan(k x) ) x \in ]-infty,+infty [ + ! Example 2: 1/(1+exp(k x) ) x \in ]-infty,+infty [ + ! Example 3: sin(pi/2*s)**2 s \in [0,1] + RealAxis = (1-2*s)/(s*(s-1._DbKi)-1.0e-02_DbKi) ! Scaling from 0-1 to real axis + GammaScale = 1._ReKi- 1._ReKi/(1._ReKi+exp(real(RealAxis,ReKi))) else GammaScale = s ! Using a linear scaling endif From b5e5e496ef127e13ffd1609065350da8dfdb08bd Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sun, 5 Apr 2020 19:57:41 -0600 Subject: [PATCH 117/190] FVW: error handling for circulation file --- modules/aerodyn/src/FVW_Subs.f90 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index ebd9bd4349..0a58156faf 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -98,11 +98,13 @@ subroutine Output_Gamma(CP, Gamma_LL, iWing, iStep, iLabel, iIter) !> Read a delimited file containing a circulation and interpolate it on the requested Control Points !! The input file is a delimited file with one line of header. !! Each following line consists of two columns: r/R_[-] and Gamma_[m^2/s] -subroutine ReadAndInterpGamma(CirculationFileName, s_CP_LL, L, Gamma_CP_LL) +subroutine ReadAndInterpGamma(CirculationFileName, s_CP_LL, L, Gamma_CP_LL, ErrStat, ErrMsg) character(len=*), intent(in ) :: CirculationFileName !< Input file to read real(ReKi), dimension(:), intent(in ) :: s_CP_LL !< Spanwise location of the lifting CP [m] real(ReKi), intent(in ) :: L !< Full span of lifting line real(ReKi), dimension(:), intent(out ) :: Gamma_CP_LL !< Interpolated circulation of the LL CP + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local integer(IntKi) :: nLines integer(IntKi) :: i @@ -110,6 +112,8 @@ subroutine ReadAndInterpGamma(CirculationFileName, s_CP_LL, L, Gamma_CP_LL) integer(IntKi) :: iUnit character(len=1054) :: line real(ReKi), dimension(:), allocatable :: sPrescr, GammaPrescr !< Radius + ErrStat = ErrID_None + ErrMsg = '' ! --- call GetNewUnit(iUnit) open(unit = iUnit, file = CirculationFileName) @@ -125,7 +129,9 @@ subroutine ReadAndInterpGamma(CirculationFileName, s_CP_LL, L, Gamma_CP_LL) enddo close(iUnit) if (istat/=0) then - print*,'Error occured while reading Circulation file' + ErrStat=ErrID_Fatal + ErrMsg='Error occured while reading Circulation file: '//trim(CirculationFileName) + return endif ! NOTE: TODO TODO TODO THIS ROUTINE PERFORMS NASTY EXTRAPOLATION, SHOULD BE PLATEAUED Gamma_CP_LL = interpolation_array( sPrescr, GammaPrescr, s_CP_LL, size(s_CP_LL), nLines ) From 52e264e50188e8236466e1fc356ccbd8c5aa584a Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Sun, 5 Apr 2020 20:42:04 -0600 Subject: [PATCH 118/190] FVW: note of subcycling --- modules/aerodyn/src/FVW.f90 | 18 ++++++++++++------ modules/aerodyn/src/FVW_Subs.f90 | 22 +++++++++++++++++++++- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 8530932039..8dbfba2640 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -121,7 +121,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL FVW_ToString(p, m) ! Print to screen ! Mapping NW and FW (purely for esthetics, and maybe wind) ! TODO, just points - call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + call Map_LL_NW(p, m, z, x, 1.0_ReKi, ErrStat2, ErrMsg2); if(Failed()) return call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return ! Initialize output @@ -354,13 +354,14 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) if (InputFileData%CirculationMethod==idCircPrescribed) then - call AllocAry( p%PrescribedCirculation, p%nSpan, 'Prescribed Circulation', ErrStat2, ErrMsg2 ); call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_SetParameters' ); p%PrescribedCirculation = -999999_ReKi; + call AllocAry( p%PrescribedCirculation, p%nSpan, 'Prescribed Circulation', ErrStat2, ErrMsg2 ); call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_SetParameters' ); p%PrescribedCirculation = -999999_ReKi; if (.not. allocated(m%s_CP_LL)) then ErrMsg = 'Spanwise coordinate not allocated.' ErrStat = ErrID_Fatal return endif - call ReadAndInterpGamma(trim(InputFileData%CirculationFile), m%s_CP_LL(1:p%nSpan,1), m%s_LL(p%nSpan+1,1), p%PrescribedCirculation) + call ReadAndInterpGamma(trim(InputFileData%CirculationFile), m%s_CP_LL(1:p%nSpan,1), m%s_LL(p%nSpan+1,1), p%PrescribedCirculation, ErrStat2, ErrMsg2) + call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_SetParameters' ); endif end subroutine FVW_SetParametersFromInputFile @@ -443,6 +444,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m type(FVW_ConstraintStateType) :: z_guess ! < integer(IntKi) :: nP, nFWEff integer, dimension(8) :: time1, time2, time_diff + real(ReKi) :: ShedScale !< Scaling factor for shed vorticity (for sub-cycling), 1 if no subcycling logical :: bReevaluation ErrStat = ErrID_None @@ -496,7 +498,8 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! Map circulation and positions between LL and NW and then NW and FW ! Changes: x only - call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + ShedScale = 1.0_ReKi + call Map_LL_NW(p, m, z, x, ShedScale, ErrStat2, ErrMsg2); if(Failed()) return call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return !call print_x_NW_FW(p, m, x,'Map_') @@ -537,7 +540,8 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! Updating positions of first NW and FW panels (Circulation also updated but irrelevant) ! Changes: x only - call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + ShedScale = (t+p%DTaero - m%OldWakeTime)/p%DTfvw + call Map_LL_NW(p, m, z, x, ShedScale, ErrStat2, ErrMsg2); if(Failed()) return call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return !call print_x_NW_FW(p, m, x,'Map2') @@ -546,10 +550,12 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! Returns: z%Gamma_LL (at t+p%DTaero) z_guess%Gamma_LL = z%Gamma_LL ! We use as guess the circulation from the previous time step (see above) call FVW_CalcConstrStateResidual(t+p%DTaero, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 2); if(Failed()) return +! print*,'US: z_Gamma',x%Gamma_NW(1,1,1) +! print*,'US: x_Gamma',z%Gamma_LL(1,1) ! Updating circulation of near wake panel (and position but irrelevant) ! Changes: x only - call Map_LL_NW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return + call Map_LL_NW(p, m, z, x, ShedScale, ErrStat2, ErrMsg2); if(Failed()) return call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return !call print_x_NW_FW(p, m, x,'Map3') diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 0a58156faf..ba42d9bd1f 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -169,13 +169,15 @@ integer function line_count(iunit) !> Make sure the First panel of the NW match the last panel of the Trailing edge !! - Same position of points !! - Same circulation -subroutine Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) +subroutine Map_LL_NW(p, m, z, x, ShedScale, ErrStat, ErrMsg ) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables type(FVW_ConstraintStateType), intent(in ) :: z !< Constraints states type(FVW_ContinuousStateType), intent(inout) :: x !< Continuous states + real(ReKi), intent(in) :: ShedScale !< Time scaling of shed vorticity integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + real(ReKi) :: Gamma_Prev, Gamma_new ! Local integer(IntKi) :: iSpan , iW ErrStat = ErrID_None @@ -202,6 +204,24 @@ subroutine Map_LL_NW(p, m, z, x, ErrStat, ErrMsg ) enddo enddo endif + ! When subcycling, we make sure the new circulation progressively ramps up from the old one + ! NOTE: subcycling needs improvement. + ! Frequencies are introduced, even for prescribed circulation, when wake roll up is included + ! If the wake is not free, the convection velocity is constant and there is no issue. + ! As a test case, the elliptical wing with constant circulation can be used, with roll up + ! The error seems to be bigger near the tip/root for this case. + if(.false.) then + if ((ShedScale<1.0_ReKi) .and. (m%nNW>=3)) then + print*,'Scaling' + do iW = 1,p%nWings + do iSpan = 1,p%nSpan + Gamma_Prev = x%Gamma_NW(iSpan, iNWStart+1, iW) ! Previous circulation + Gamma_New = x%Gamma_NW(iSpan, iNWStart , iW) + x%Gamma_NW(iSpan, iNWStart , iW) = Gamma_New*ShedScale + (1.0_ReKi-ShedScale) * Gamma_Prev + enddo + enddo + endif + endif end subroutine Map_LL_NW !> Map the last NW panel with the first FW panel From 9ddda5ea3ad4a3b6362bc23233e8bdcb582db46b Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 6 Apr 2020 10:23:51 -0600 Subject: [PATCH 119/190] FVW: display whether openmp is supported --- modules/aerodyn/src/FVW.f90 | 33 +++++++++++++++++--------- modules/aerodyn/src/FVW_BiotSavart.f90 | 2 +- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 8dbfba2640..62126aa15e 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -7,10 +7,10 @@ !! - NW : Near Wake !! - FW : Far Wake !! -MODULE FVW - USE NWTC_Library - USE FVW_Types - USE FVW_Subs +module FVW + use NWTC_Library + use FVW_Types + use FVW_Subs use FVW_IO use FVW_Wings use FVW_BiotSavart @@ -21,18 +21,18 @@ MODULE FVW PRIVATE - type(ProgDesc), PARAMETER :: FVW_Ver = ProgDesc( 'FVW', '', '' ) + type(ProgDesc), parameter :: FVW_Ver = ProgDesc( 'FVW', '', '' ) - PUBLIC :: FVW_Init ! Initialization routine - PUBLIC :: FVW_End + public :: FVW_Init ! Initialization routine + public :: FVW_End - PUBLIC :: FVW_CalcOutput - PUBLIC :: FVW_UpdateStates + public :: FVW_CalcOutput + public :: FVW_UpdateStates ! parameter for deciding if enough time has elapsed (Wake calculation, and vtk output) real(DbKi), parameter :: OneMinusEpsilon = 1 - 10000*EPSILON(1.0_DbKi) -CONTAINS +contains !---------------------------------------------------------------------------------------------------------------------------------- !> This routine is called at the start of the simulation to perform initialization steps. @@ -40,6 +40,7 @@ MODULE FVW !! The initial states and initial guess for the input are defined. subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat, ErrMsg ) !.................................................................................................................................. + use OMP_LIB ! wrap with #ifdef _OPENMP if this causes an issue type(FVW_InitInputType), intent(inout) :: InitInp !< Input data for initialization routine (inout so we can use MOVE_ALLOC) type(FVW_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined type(FVW_ParameterType), intent( out) :: p !< Parameters @@ -76,6 +77,16 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! Display the module information call DispNVD( FVW_Ver ) +#ifdef _OPENMP + call WrScr(' - Compiled with OpenMP') + !$OMP PARALLEL default(shared) + if (omp_get_thread_num()==0) then + call WrScr(' Number of threads: '//trim(Num2LStr(omp_get_num_threads()))//'/'//trim(Num2LStr(omp_get_max_threads()))) + endif + !$OMP END PARALLEL +#else + call WrScr(' - No OpenMP support') +#endif ! Set Parameters and *Misc* from inputs CALL FVW_SetParametersFromInputs(InitInp, p, ErrStat2, ErrMsg2); if(Failed()) return @@ -926,4 +937,4 @@ end function lin_extrap end subroutine FVW_CalcOutput -END MODULE FVW +end module FVW diff --git a/modules/aerodyn/src/FVW_BiotSavart.f90 b/modules/aerodyn/src/FVW_BiotSavart.f90 index 4b0c3afdec..b090c09411 100644 --- a/modules/aerodyn/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn/src/FVW_BiotSavart.f90 @@ -1,7 +1,7 @@ module FVW_BiotSavart use NWTC_Library, only: ReKi, IntKi - use OMP_LIB + use OMP_LIB ! wrap with #ifdef _OPENMP if this causes an issue implicit none From e617635eff0def99b3d5fa2ed7fa2e1464c39257 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 6 Apr 2020 10:34:42 -0600 Subject: [PATCH 120/190] FVW: added OPENMP option flag for cmake --- CMakeLists.txt | 3 ++- cmake/OpenfastFortranOptions.cmake | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ca7973818..4fd6167fb4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ option(USE_DLL_INTERFACE "Enable runtime loading of dynamic libraries" on) option(FPE_TRAP_ENABLED "Enable FPE trap in compiler options" off) option(ORCA_DLL_LOAD "Enable OrcaFlex Library Load" off) option(BUILD_OPENFAST_CPP_API "Enable building OpenFAST - C++ API" off) +option(OPENMP "Enable OpenMP support" off) # Precompiler/preprocessor flag configuration # Do this before configuring modules so that the flags are included @@ -169,4 +170,4 @@ endif() option(BUILD_DOCUMENTATION "Build documentation." OFF) if(BUILD_DOCUMENTATION) add_subdirectory(docs) -endif() \ No newline at end of file +endif() diff --git a/cmake/OpenfastFortranOptions.cmake b/cmake/OpenfastFortranOptions.cmake index 9cc1447386..4b0ba01e4f 100644 --- a/cmake/OpenfastFortranOptions.cmake +++ b/cmake/OpenfastFortranOptions.cmake @@ -83,7 +83,8 @@ macro(set_fast_gfortran) endif(NOT WIN32) # Fix free-form compilation for OpenFAST - set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -ffree-line-length-none -cpp -fopenmp") + #set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -ffree-line-length-none -cpp -fopenmp") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -ffree-line-length-none -cpp") # Deal with Double/Single precision if (DOUBLE_PRECISION) @@ -93,7 +94,7 @@ macro(set_fast_gfortran) # debug flags if(CMAKE_BUILD_TYPE MATCHES Debug) - set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -fcheck=all -pedantic -fbacktrace -fopenmp" ) + set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -fcheck=all -pedantic -fbacktrace " ) endif() if(CYGWIN) @@ -102,6 +103,12 @@ macro(set_fast_gfortran) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS},--stack,${stack_size}") endif() + # OPENMP + if (OPENMP) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fopenmp") + set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -fopenmp" ) + endif() + endmacro(set_fast_gfortran) # @@ -131,6 +138,12 @@ macro(set_fast_intel_fortran_posix) if(CMAKE_BUILD_TYPE MATCHES Debug) set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -check all -traceback" ) endif() + + # OPENMP + if (OPENMP) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -qopenmp") + set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -qopenmp" ) + endif() endmacro(set_fast_intel_fortran_posix) # @@ -157,4 +170,11 @@ macro(set_fast_intel_fortran_windows) if(CMAKE_BUILD_TYPE MATCHES Debug) set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} /check:all /traceback" ) endif() + + # OPENMP + if (OPENMP) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /qopenmp") + set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} /qopenmp" ) + endif() + endmacro(set_fast_intel_fortran_windows) From 6b177226c00a7b980b62bf0f989621d90fec1bb6 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 16 Apr 2020 19:21:39 -0600 Subject: [PATCH 121/190] FVW: implementing tower shadow --- modules/aerodyn/src/AeroDyn.f90 | 132 +++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 587fac1530..6425b7322b 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1613,8 +1613,6 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) integer(intKi) :: tIndx integer(intKi) :: k ! loop counter for blades - integer(intKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'SetInputsForFVW' do tIndx=1,size(u) @@ -1641,6 +1639,11 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) enddo if (ALLOCATED(m%FVW_u(tIndx)%V_wind)) then m%FVW_u(tIndx)%V_wind = u(tIndx)%InflowWakeVel + ! Applying tower shadow to V_wind based on r_wind positions + if (p%TwrPotent /= TwrPotent_none .or. p%TwrShadow) then + call TwrInflArray( p, u(tIndx), m, m%FVW%r_wind, m%FVW_u(tIndx)%V_wind, ErrStat, ErrMsg ) + if (ErrStat >= AbortErrLev) return + end if endif enddo end subroutine SetInputsForFVW @@ -2539,6 +2542,131 @@ SUBROUTINE TwrInfl( p, u, m, ErrStat, ErrMsg ) END SUBROUTINE TwrInfl !---------------------------------------------------------------------------------------------------------------------------------- +!> Calculate the tower influence on a array of points `Positions` (3xn) +!! The subroutine has side effecs and modifies the inflow +!! Relies heavily (i.e. unfortunate copy pasting), on TwrInfl +SUBROUTINE TwrInflArray( p, u, m, Positions, Inflow, ErrStat, ErrMsg ) + TYPE(AD_InputType), INTENT(IN ) :: u !< Inputs at Time t + TYPE(AD_ParameterType), INTENT(IN ) :: p !< Parameters + type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables + real(ReKi), dimension(:,:), INTENT(IN ) :: Positions !< Positions where tower influence is to be computed + real(ReKi), dimension(:,:), INTENT(INOUT) :: Inflow !< Undisturbed inflow (in) -> disturbed inflow (out) + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! local variables + real(ReKi) :: xbar ! local x^ component of r_TowerBlade (distance from tower to blade) normalized by tower radius + real(ReKi) :: ybar ! local y^ component of r_TowerBlade (distance from tower to blade) normalized by tower radius + real(ReKi) :: zbar ! local z^ component of r_TowerBlade (distance from tower to blade) normalized by tower radius + real(ReKi) :: theta_tower_trans(3,3) ! transpose of local tower orientation expressed as a DCM + real(ReKi) :: TwrCd ! local tower drag coefficient + real(ReKi) :: W_tower ! local relative wind speed normal to the tower + real(ReKi) :: Pos(3) ! current point + real(ReKi) :: u_TwrShadow ! axial velocity deficit fraction from tower shadow + real(ReKi) :: u_TwrPotent ! axial velocity deficit fraction from tower potential flow + real(ReKi) :: v_TwrPotent ! transverse velocity deficit fraction from tower potential flow + real(ReKi) :: denom ! denominator + real(ReKi) :: v(3) ! temp vector + integer(IntKi) :: i ! loop counters for points + real(ReKi) :: MaxDiam ! Maximum tower diameter + real(ReKi) :: TwrClrnc ! local tower clearance + integer(intKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'TwrInflArray' + ErrStat = ErrID_None + ErrMsg = "" + + ! these models are valid for only small tower deflections; check for potential division-by-zero errors: + call CheckTwrInfl( u, ErrStat2, ErrMsg2 ); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ); if (ErrStat >= AbortErrLev) return + + MaxDiam = maxval(p%TwrDiam(:)) + + !$OMP PARALLEL default(shared) + !$OMP do private(i,Pos,theta_tower_trans,W_tower,xbar,ybar,zbar,TwrCd,TwrClrnc,denom,u_TwrPotent,v_TwrPotent,u_TwrShadow,v) schedule(runtime) + do i = 1, size(Positions,2) + Pos=Positions(1:3,i) + + ! Find nearest line2 element or node of the tower + ! values are found for the deflected tower, returning theta_tower, W_tower, xbar, ybar, zbar, and TowerCd: + call getLocalTowerPropsNoCheck(p, u, Pos, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrClrnc) + + if ( TwrClrnc>20*MaxDiam) then + ! Far away, we skip the computation and keep undisturbed inflow + elseif ( TwrClrnc<=0.0_ReKi) then + ! Inside the tower, will happen for vortex elements, we keep undisturbed inflow + else + ! calculate tower influence: + if ( abs(zbar) < 1.0_ReKi .and. p%TwrPotent /= TwrPotent_none ) then + + if ( p%TwrPotent == TwrPotent_baseline ) then + denom = (xbar**2 + ybar**2)**2 + u_TwrPotent = ( -1.0*xbar**2 + ybar**2 ) / denom + v_TwrPotent = ( -2.0*xbar * ybar ) / denom + + elseif (p%TwrPotent == TwrPotent_Bak) then + xbar = xbar + 0.1 + denom = (xbar**2 + ybar**2)**2 + u_TwrPotent = ( -1.0*xbar**2 + ybar**2 ) / denom + v_TwrPotent = ( -2.0*xbar * ybar ) / denom + denom = TwoPi*(xbar**2 + ybar**2) + u_TwrPotent = u_TwrPotent + TwrCd*xbar / denom + v_TwrPotent = v_TwrPotent + TwrCd*ybar / denom + + end if + else + u_TwrPotent = 0.0_ReKi + v_TwrPotent = 0.0_ReKi + end if + + if ( p%TwrShadow .and. xbar > 0.0_ReKi .and. abs(zbar) < 1.0_ReKi) then + denom = sqrt( sqrt( xbar**2 + ybar**2 ) ) + if ( abs(ybar) < denom ) then + u_TwrShadow = -TwrCd / denom * cos( PiBy2*ybar / denom )**2 + else + u_TwrShadow = 0.0_ReKi + end if + else + u_TwrShadow = 0.0_ReKi + end if + + ! NOTE: we should introduce a safety check to avoid zero or negative velocities, have a minimal velocity + v(1) = (u_TwrPotent + u_TwrShadow)*W_tower + v(2) = v_TwrPotent*W_tower + v(3) = 0.0_ReKi + + + Inflow(1:3,i) = Inflow(1:3,i) + matmul( theta_tower_trans, v ) + endif ! Check if point far away or in tower + enddo ! loop on points + !$OMP END DO + !$OMP END PARALLEL +contains + !> This is a simpler version of getLocalTowerProps + SUBROUTINE getLocalTowerPropsNoCheck(p, u, BladeNodePosition, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrClrnc) + TYPE(AD_InputType), INTENT(IN ) :: u !< Inputs at Time t + TYPE(AD_ParameterType), INTENT(IN ) :: p !< Parameters + REAL(ReKi) ,INTENT(IN ) :: BladeNodePosition(3) !< local blade node position + REAL(ReKi) ,INTENT( OUT) :: theta_tower_trans(3,3) !< transpose of local tower orientation expressed as a DCM + REAL(ReKi) ,INTENT( OUT) :: W_tower !< local relative wind speed normal to the tower + REAL(ReKi) ,INTENT( OUT) :: xbar !< local x^ component of r_TowerBlade normalized by tower radius + REAL(ReKi) ,INTENT( OUT) :: ybar !< local y^ component of r_TowerBlade normalized by tower radius + REAL(ReKi) ,INTENT( OUT) :: zbar !< local z^ component of r_TowerBlade normalized by tower radius + REAL(ReKi) ,INTENT( OUT) :: TwrCd !< local tower drag coefficient + REAL(ReKi) ,INTENT( OUT) :: TwrClrnc !< tower clearance for potential output + ! local variables + real(ReKi) :: r_TowerBlade(3) ! distance vector from tower to blade + real(ReKi) :: TwrDiam ! local tower diameter + logical :: found + character(*), parameter :: RoutineName = 'getLocalTowerPropsNoCheck' + ! option 1: nearest line2 element + call TwrInfl_NearestLine2Element(p, u, BladeNodePosition, r_TowerBlade, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrDiam, found) + if ( .not. found) then + ! option 2: nearest node + call TwrInfl_NearestPoint(p, u, BladeNodePosition, r_TowerBlade, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrDiam) + end if + TwrClrnc = TwoNorm(r_TowerBlade) - 0.5_ReKi*TwrDiam + END SUBROUTINE getLocalTowerPropsNoCheck +END SUBROUTINE TwrInflArray +!---------------------------------------------------------------------------------------------------------------------------------- !> This routine returns the tower constants necessary to compute the tower influence. !! if u%TowerMotion does not have any nodes there will be serious problems. I assume that has been checked earlier. SUBROUTINE getLocalTowerProps(p, u, BladeNodePosition, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrClrnc, ErrStat, ErrMsg) From 9b528b81cb0feabc6652442b9b2b64489d2c870d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 16 Apr 2020 20:40:22 -0600 Subject: [PATCH 122/190] FVW: displaying useful info to screen --- modules/aerodyn/src/FVW.f90 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 62126aa15e..9e22fcf2a7 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -66,6 +66,7 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu integer(IntKi) :: UnEcho ! Unit number for the echo file character(*), parameter :: RoutineName = 'FVW_Init' type(FVW_InputFile) :: InputFileData !< Data stored in the module's input file + character(len=1054) :: DirName ! Initialize variables for this routine ErrStat = ErrID_None @@ -77,6 +78,10 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! Display the module information call DispNVD( FVW_Ver ) + ! Display convenient info to screen, until this is one day displayed by OpenFAST + call getcwd(DirName) + call WrScr(' - Directory: '//trim(DirName)) + call WrScr(' - RootName: '//trim(InitInp%RootName)) #ifdef _OPENMP call WrScr(' - Compiled with OpenMP') !$OMP PARALLEL default(shared) From ef7dd800687868d53fab972bd15d508f089d7d7c Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 16 Apr 2020 21:57:25 -0600 Subject: [PATCH 123/190] FVW: implemented mirrored vorticity for ground effect --- modules/aerodyn/src/FVW.f90 | 9 +++-- modules/aerodyn/src/FVW_IO.f90 | 9 ++++- modules/aerodyn/src/FVW_Registry.txt | 2 + modules/aerodyn/src/FVW_Subs.f90 | 55 +++++++++++++++++++++++----- modules/aerodyn/src/FVW_Types.f90 | 14 +++++++ 5 files changed, 74 insertions(+), 15 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 9e22fcf2a7..1961f20cc6 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -364,8 +364,9 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%WakeRegParam = InputFileData%WakeRegParam p%WingRegParam = InputFileData%WingRegParam p%CoreSpreadEddyVisc = InputFileData%CoreSpreadEddyVisc + p%ShearModel = InputFileData%ShearModel p%WrVTK = InputFileData%WrVTK - p%VTKBlades = min(max(InputFileData%VTKBlades,0),p%nWings) + p%VTKBlades = min(InputFileData%VTKBlades,p%nWings) ! Note: allowing it to be negative for tempoarry hack p%VTKCoord = InputFileData%VTKCoord if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) @@ -535,9 +536,6 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m end IF !call print_x_NW_FW(p, m, x,'Conv') - ! --- Fake handling of ground effect - call FakeGroundEffect(p, x, m, ErrStat, ErrMsg) - if (m%ComputeWakeInduced) then ! We extend the wake length, i.e. we emit a new panel of vorticity at the TE ! NOTE: this will be rolled back if UpdateState is called at the same starting time again @@ -575,6 +573,9 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return !call print_x_NW_FW(p, m, x,'Map3') + ! --- Fake handling of ground effect + call FakeGroundEffect(p, x, m, ErrStat, ErrMsg) + ! set the wind points required for t+p%DTaero timestep CALL SetRequestedWindPoints(m%r_wind, x, p, m) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index 8b57c3827b..ea201d5abb 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -59,6 +59,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar (UnIn,FileName,Inp%WakeRegParam ,'WakeRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVar (UnIn,FileName,Inp%WingRegParam ,'WingRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CoreSpreadEddyVisc ,'CoreSpreadEddyVisc','',100.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%ShearModel ,'ShearModel' ,'',idShearNone , ErrStat2,ErrMsg2); if(Failed())return !------------------------ OUTPUT OPTIONS ----------------------------------------- CALL ReadCom (UnIn,FileName, 'Output options header' ,ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'', 0 ,ErrStat2,ErrMsg2); if(Failed())return @@ -75,6 +76,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check(.not.(ANY(idRegDeterVALID ==Inp%RegDeterMethod)) , 'Regularization determination method (RegDeterMethod) not yet implemented. Use Manual method for now.')) return if (Check(.not.(ANY(idRegVALID ==Inp%RegFunction )), 'Regularization function (RegFunction) not implemented')) return if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method (WakeRegMethod) not implemented')) return + if (Check(.not.(ANY(idShearVALID ==Inp%ShearModel )), 'Shear model (`ShearModel`) not valid')) return if (Check( Inp%DTfvw < p%DTaero, 'DTfvw must be >= DTaero from AD15.')) return if (abs(Inp%DTfvw-p%DTaero)>epsilon(1.0_ReKi)) then @@ -186,7 +188,8 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation real(ReKi), dimension(:), allocatable :: SegEpsilon !< - integer(IntKi) :: iHeadC, iHeadP, nSeg, nSegP + integer(IntKi) :: iHeadC, iHeadP, nSeg, nSegP + logical :: bMirror integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 @@ -252,7 +255,9 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! --------------------------------------------------------------------------------} ! --- All Segments ! --------------------------------------------------------------------------------{ - call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) + ! False below is to avoid writing the mirrored vorticity, this could be an option though + bMirror= (p%ShearModel==idShearMirror) .and. (p%VTKBlades<0) ! NOTE: temporary hack to output mirrored vorticity + call PackPanelsToSegments(p, m, x, 1, bMirror, SegConnct, SegPoints, SegGamma, nSeg, nSegP) if (m%nNW==1) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel iHeadP=1 iHeadC=1 diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index be1349e03e..8a69f3d482 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -33,6 +33,7 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - typedef ^ ^ ReKi WakeRegParam - - - "Initial value of the regularization parameter" typedef ^ ^ ReKi WingRegParam - - - "Regularization parameter of the wing" +typedef ^ ^ IntKi ShearModel - - - "Option for shear modelling" typedef ^ ^ DbKi DTaero - - - "Time interval for calls calculations" s typedef ^ ^ DbKi DTfvw - - - "Time interval for calculating wake induced velocities" s typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s @@ -160,6 +161,7 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi WakeRegMethod - - - "Method for regularization (constant, stretching, age, etc.)" - typedef ^ ^ ReKi WakeRegParam - - - "Factor used in the regularization " typedef ^ ^ ReKi WingRegParam - - - "Factor used in the regularization " +typedef ^ ^ IntKi ShearModel - - - "Option for shear modelling" typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ DbKi DTvtk - - - "Requested timestep between VTK outputs (calculated from the VTK_fps read in)" s diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index ba42d9bd1f..793cd6eddd 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -34,6 +34,10 @@ module FVW_SUBS integer(IntKi), parameter :: idRegDeterManual = 0 integer(IntKi), parameter :: idRegDeterAuto = 1 integer(IntKi), parameter, dimension(2) :: idRegDeterVALID = (/idRegDeterManual, idRegDeterAuto /) + ! Shear model + integer(IntKi), parameter :: idShearNone = 0 + integer(IntKi), parameter :: idShearMirror = 1 + integer(IntKi), parameter, dimension(2) :: idShearVALID = (/idShearNone, idShearMirror /) real(ReKi), parameter :: CoreSpreadAlpha = 1.25643 @@ -492,18 +496,19 @@ end subroutine DistributeRequestedWind -subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegGamma, nSeg, nSegP) +subroutine PackPanelsToSegments(p, m, x, iDepthStart, bMirror, SegConnct, SegPoints, SegGamma, nSeg, nSegP) type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables type(FVW_ContinuousStateType), intent(in ) :: x !< States integer(IntKi), intent(in ) :: iDepthStart !< Index where we start packing for NW panels + logical, intent(in ) :: bMirror !< Mirror the vorticity wrt the ground integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation integer(IntKi), intent(out) :: nSeg !< Total number of segments after packing integer(IntKi), intent(out) :: nSegP !< Total number of segments points after packing ! Local - integer(IntKi) :: iHeadC, iHeadP, nC, nCNW, nP, iW, iHeadC_bkp + integer(IntKi) :: iHeadC, iHeadP, nC, nCNW, nP, iW, iHeadC_bkp, i, iMirror logical :: LastNWShed ! If the FW contains Shed vorticity, we include the last shed vorticity form the NW, orhtwerise, we don't! @@ -535,9 +540,16 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG if (allocated(SegConnct)) deallocate(SegConnct) if (allocated(SegPoints)) deallocate(SegPoints) if (allocated(SegGamma)) deallocate(SegGamma) - allocate(SegConnct(1:4,1:nC)); SegConnct=-1 - allocate(SegPoints(1:3,1:nP)); SegPoints=-1 - allocate(SegGamma (1:nC)); SegGamma =-1 + if (bMirror) then + ! we double the storage dimension when we mirror the vorticity + allocate(SegConnct(1:4,1:2*nC)); SegConnct=-1 ! 4 values per segmnt: iP1, iP2, iDepth, iSpan + allocate(SegPoints(1:3,1:2*nP)); SegPoints=-1 + allocate(SegGamma (1:2*nC)); SegGamma =-1 + else + allocate(SegConnct(1:4,1:nC)); SegConnct=-1 + allocate(SegPoints(1:3,1:nP)); SegPoints=-1 + allocate(SegGamma (1:nC)); SegGamma =-1 + endif ! iHeadP=1 @@ -552,7 +564,7 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG do iW=1,p%nWings CALL LatticeToSegments(x%r_FW(1:3,:,1:m%nFW+1,iW), x%Gamma_FW(:,1:m%nFW,iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC , p%FWShedVorticity, p%FWShedVorticity) enddo - SegConnct(3,iHeadC_bkp:) = SegConnct(3,iHeadC_bkp:) + m%nNW + SegConnct(3,iHeadC_bkp:) = SegConnct(3,iHeadC_bkp:) + m%nNW ! Increasing iDepth (or age) to account for NW endif if (DEV_VERSION) then ! Safety checks @@ -572,6 +584,27 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegG endif nSeg = iHeadC-1 nSegP = iHeadP-1 + + if (bMirror) then + ! Mirroring the segments directly + ! NOTE: an alternative is to handle this in the Biot-Savart law directly... + do i=1,nSeg + iMirror = i + nSeg + SegConnct(1:2, iMirror) = SegConnct(1:2, i) + nSegP ! Increased point indices + SegConnct(3:4, iMirror) = SegConnct(3:4, i) ! Span and age is copied + SegGamma(iMirror) = -SegGamma(i) ! Vorticity needs mirroring + enddo + do i=1,nSegP + iMirror = i + nSegP + SegPoints(1:2, iMirror) = SegPoints(1:2, i) ! Same x and y + SegPoints(3 , iMirror) = - SegPoints(3 , i) ! Mirror with respect to z=0 + enddo + ! We now have double the amount of segments and points + nSeg = nSeg*2 + nSegP = nSegP*2 + endif + + else nSeg = 0 nSegP = 0 @@ -704,18 +737,20 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) real(ReKi), dimension(:) , allocatable :: SegEpsilon !< Segment regularization parameter real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity - integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step + integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step + logical :: bMirror ! True if we mirror the vorticity wrt ground ErrStat= ErrID_None ErrMsg ='' nFWEff = min(m%nFW, p%nFWFree) + bMirror = p%ShearModel==idShearMirror ! Whether or not we mirror the vorticity wrt ground m%Vind_NW = -9999._ReKi !< Safety m%Vind_FW = -9999._ReKi !< Safety ! --- Packing all vortex elements into a list of segments ! NOTE: allocates SegConnct, SegPoints, SegGamma.. - call PackPanelsToSegments(p, m, x, 1, SegConnct, SegPoints, SegGamma, nSeg, nSegP) + call PackPanelsToSegments(p, m, x, 1, bMirror, SegConnct, SegPoints, SegGamma, nSeg, nSegP) ! --- Setting up regularization allocate(SegEpsilon(1:nSeg)); @@ -818,12 +853,14 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + logical :: bMirror ErrStat = ErrID_None ErrMsg = "" m%Vind_LL = -9999._ReKi !< Safety + bMirror = p%ShearModel==idShearMirror ! Whether or not we mirror the vorticity wrt ground ! --- Packing all vortex elements into a list of segments - call PackPanelsToSegments(p, m, x, iDepthStart, SegConnct, SegPoints, SegGamma, nSeg, nSegP) + call PackPanelsToSegments(p, m, x, iDepthStart, bMirror, SegConnct, SegPoints, SegGamma, nSeg, nSegP) ! --- Computing induced velocity if (nSegP==0) then diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 5cde7262ca..06c708dc56 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -60,6 +60,7 @@ MODULE FVW_Types INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] REAL(ReKi) :: WakeRegParam !< Initial value of the regularization parameter [-] REAL(ReKi) :: WingRegParam !< Regularization parameter of the wing [-] + INTEGER(IntKi) :: ShearModel !< Option for shear modelling [-] REAL(DbKi) :: DTaero !< Time interval for calls calculations [s] REAL(DbKi) :: DTfvw !< Time interval for calculating wake induced velocities [s] REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] @@ -190,6 +191,7 @@ MODULE FVW_Types INTEGER(IntKi) :: WakeRegMethod !< Method for regularization (constant, stretching, age, etc.) [-] REAL(ReKi) :: WakeRegParam !< Factor used in the regularization [-] REAL(ReKi) :: WingRegParam !< Factor used in the regularization [-] + INTEGER(IntKi) :: ShearModel !< Option for shear modelling [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] REAL(DbKi) :: DTvtk !< Requested timestep between VTK outputs (calculated from the VTK_fps read in) [s] @@ -281,6 +283,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%WakeRegMethod = SrcParamData%WakeRegMethod DstParamData%WakeRegParam = SrcParamData%WakeRegParam DstParamData%WingRegParam = SrcParamData%WingRegParam + DstParamData%ShearModel = SrcParamData%ShearModel DstParamData%DTaero = SrcParamData%DTaero DstParamData%DTfvw = SrcParamData%DTfvw DstParamData%KinVisc = SrcParamData%KinVisc @@ -382,6 +385,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! WakeRegMethod Re_BufSz = Re_BufSz + 1 ! WakeRegParam Re_BufSz = Re_BufSz + 1 ! WingRegParam + Int_BufSz = Int_BufSz + 1 ! ShearModel Db_BufSz = Db_BufSz + 1 ! DTaero Db_BufSz = Db_BufSz + 1 ! DTfvw Re_BufSz = Re_BufSz + 1 ! KinVisc @@ -504,6 +508,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_Xferred = Re_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WingRegParam Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%ShearModel + Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTaero Db_Xferred = Db_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTfvw @@ -677,6 +683,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + 1 OutData%WingRegParam = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%ShearModel = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%DTaero = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 OutData%DTfvw = DbKiBuf( Db_Xferred ) @@ -5121,6 +5129,7 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%WakeRegMethod = SrcInputFileData%WakeRegMethod DstInputFileData%WakeRegParam = SrcInputFileData%WakeRegParam DstInputFileData%WingRegParam = SrcInputFileData%WingRegParam + DstInputFileData%ShearModel = SrcInputFileData%ShearModel DstInputFileData%WrVTK = SrcInputFileData%WrVTK DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades DstInputFileData%DTvtk = SrcInputFileData%DTvtk @@ -5195,6 +5204,7 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! WakeRegMethod Re_BufSz = Re_BufSz + 1 ! WakeRegParam Re_BufSz = Re_BufSz + 1 ! WingRegParam + Int_BufSz = Int_BufSz + 1 ! ShearModel Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades Db_BufSz = Db_BufSz + 1 ! DTvtk @@ -5272,6 +5282,8 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_Xferred = Re_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%WingRegParam Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%ShearModel + Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades @@ -5360,6 +5372,8 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Re_Xferred = Re_Xferred + 1 OutData%WingRegParam = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%ShearModel = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%WrVTK = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%VTKBlades = IntKiBuf( Int_Xferred ) From 2fd9a053b97b0c5547e8403a6c7589c4b4b11dab Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 17 Apr 2020 14:51:10 -0600 Subject: [PATCH 124/190] FVW: adding AD outputs that had been previously commented --- modules/aerodyn/src/AeroDyn.f90 | 6 +- modules/aerodyn/src/AeroDyn_IO.f90 | 129 +++++++++++++++++++---------- 2 files changed, 90 insertions(+), 45 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 6425b7322b..1ed0d61ec0 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1616,8 +1616,9 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) character(*), parameter :: RoutineName = 'SetInputsForFVW' do tIndx=1,size(u) - ! Get disk average values and orientations - call DiskAvgValues(p, u(tIndx), m, x_hat_disk, y_hat_disk, z_hat_disk, Azimuth) + ! Get disk average values and orientations + ! NOTE: needed because it sets m%V_diskAvg and m%V_dot_x, needed by CalcOutput.. + call DiskAvgValues(p, u(tIndx), m, x_hat_disk, y_hat_disk, z_hat_disk, Azimuth) call GeomWithoutSweepPitchTwist(p,u(tIndx),m,thetaBladeNds,ErrStat,ErrMsg) if (ErrStat >= AbortErrLev) return @@ -1640,6 +1641,7 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) if (ALLOCATED(m%FVW_u(tIndx)%V_wind)) then m%FVW_u(tIndx)%V_wind = u(tIndx)%InflowWakeVel ! Applying tower shadow to V_wind based on r_wind positions + ! NOTE: m%DisturbedInflow also contains tower shadow and we need it for CalcOutput if (p%TwrPotent /= TwrPotent_none .or. p%TwrShadow) then call TwrInflArray( p, u(tIndx), m, m%FVW%r_wind, m%FVW_u(tIndx)%V_wind, ErrStat, ErrMsg ) if (ErrStat >= AbortErrLev) return diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 9738cacd21..592287f3ce 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1513,6 +1513,50 @@ MODULE AeroDyn_IO contains +!> Compute maximum radius over all blades (contains hub radius), in "projected rotor plane" +!! Solely based on AD inputs, needed for FVW since rLocal is not stored +PURE REAL(ReKi) FUNCTION Calc_MaxRadius(p, u) result(rmax) + implicit none + TYPE(AD_ParameterType), INTENT(IN ) :: p !< The module parameters + TYPE(AD_InputType), INTENT(IN ) :: u !< Inputs + real(ReKi) :: y_hat_disk(3), z_hat_disk(3), dr_gl(3), rLocal + integer(IntKi) :: iB, j + y_hat_disk = u%HubMotion%Orientation(2,:,1) + z_hat_disk = u%HubMotion%Orientation(3,:,1) + rmax = 0.0_ReKi + do iB=1,p%numBlades + do j=1,p%NumBlNds + dr_gl = u%BladeMotion(iB)%Position(:,j) - u%HubMotion%Position(:,1) ! vector hub center to node j in global coord + rLocal = sqrt( dot_product(dr_gl, y_hat_disk)**2 + dot_product(dr_gl, z_hat_disk)**2 ) + rmax = max(rmax, rLocal) + end do !j=nodes + end do !iB=blades +END FUNCTION Calc_MaxRadius + +!> Rotor speed +PURE REAL(ReKi) FUNCTION Calc_Omega(u) + TYPE(AD_InputType), INTENT(IN ) :: u !< Inputs + Calc_Omega = dot_product(u%HubMotion%RotationVel(:,1), u%HubMotion%Orientation(1,:,1)) +END FUNCTION Calc_Omega + +!> Mean skew angle +REAL(ReKi) FUNCTION Calc_Chi0(V_diskAvg, V_dot_x) + implicit none + REAL(ReKi), INTENT(IN ) :: V_diskAvg(3) + REAL(ReKi), INTENT(IN ) :: V_dot_x + REAL(ReKi) :: V_norm, sy + V_norm = TwoNorm( V_diskAvg ) + if ( EqualRealNos( V_norm, 0.0_ReKi ) ) then + Calc_Chi0 = 0.0_ReKi + else + ! make sure we don't have numerical issues that make the ratio outside +/-1 + sy = min( 1.0_ReKi, V_dot_x / V_norm ) + sy = max( -1.0_ReKi, sy ) + Calc_Chi0 = acos( sy ) + end if +END FUNCTION Calc_Chi0 + + !---------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) @@ -1623,7 +1667,7 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) i = (k-1)*p%NumBlNds*24 + (j-1)*24 + 1 ! --- Computing main aero variables from induction - setting local variables - Vwnd = m%DisturbedInflow(:,j,k) + Vwnd = m%DisturbedInflow(:,j,k) ! NOTE: contains tower shadow call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), m%FVW%PitchAndTwist(j,k), u%BladeMotion(k)%TranslationVel(1:3,j), & m%FVW_y%Vind(1:3,j,k), Vwnd, p%KinVisc, p%FVW%Chord(j,k), & AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) @@ -1870,6 +1914,10 @@ subroutine Calc_WriteOutput_BEMT end if end subroutine Calc_WriteOutput_BEMT + !> Similar to Calc_WriteOutput_BEMT. TODO Merge me + !! NOTE: relies on the prior calculation of m%V_dot_x, and m%V_diskAvg (done in DiskAvgValues) + !! m%DisturbedInflow (done in SetInputs) + !! Make sure these are set! subroutine Calc_WriteOutput_FVW real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table @@ -1878,6 +1926,7 @@ subroutine Calc_WriteOutput_FVW real(ReKi) :: Vstr(3) ! real(ReKi) :: Vwnd(3) ! real(ReKi) :: Cx, Cy, cphi, sphi, theta + real(ReKi) :: rmax, omega ! blade outputs do k=1,p%numBlades @@ -1886,7 +1935,7 @@ subroutine Calc_WriteOutput_FVW ! --- Computing main aero variables from induction - setting local variables Vind = m%FVW_y%Vind(1:3,j,k) Vstr = u%BladeMotion(k)%TranslationVel(1:3,j) - Vwnd = m%DisturbedInflow(:,j,k) + Vwnd = m%DisturbedInflow(:,j,k) ! NOTE: contains tower shadow call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), m%FVW%PitchAndTwist(j,k), Vstr , & Vind(1:3), Vwnd , p%KinVisc, p%FVW%Chord(j,k), & AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) @@ -1925,9 +1974,9 @@ subroutine Calc_WriteOutput_FVW m%AllOuts( BNAlpha(beta,k) ) = alpha*R2D m%AllOuts( BNTheta(beta,k) ) = theta*R2D m%AllOuts( BNPhi( beta,k) ) = phi*R2D -! m%AllOuts( BNCurve(beta,k) ) = m%Curve(j,k)*R2D +! m%AllOuts( BNCurve(beta,k) ) = m%Curve(j,k)*R2D ! TODO -! m%AllOuts( BNCpmin( beta,k) ) = m%BEMT_y%Cpmin(j,k) +! m%AllOuts( BNCpmin( beta,k) ) = m%BEMT_y%Cpmin(j,k) ! TODO m%AllOuts( BNSigCr( beta,k) ) = m%SigmaCavitCrit(j,k) m%AllOuts( BNSgCav( beta,k) ) = m%SigmaCavit(j,k) @@ -1956,24 +2005,20 @@ subroutine Calc_WriteOutput_FVW end do ! nodes end do ! blades -! -! ! rotor outputs: -! rmax = 0.0_ReKi -! do k=1,p%NumBlades -! do j=1,p%NumBlNds -! rmax = max(rmax, m%BEMT_u(indx)%rLocal(j,k) ) ! TODO TODO TODO -! end do !j=nodes -! end do !k=blades -! -! m%AllOuts( RtSpeed ) = m%BEMT_u(indx)%omega*RPS2RPM -! m%AllOuts( RtArea ) = pi*rmax**2 ! TODO TODO TODO -! - tmp = matmul( u%HubMotion%Orientation(:,:,1), m%V_DiskAvg ) - m%AllOuts( RtVAvgxh ) = tmp(1) - m%AllOuts( RtVAvgyh ) = tmp(2) - m%AllOuts( RtVAvgzh ) = tmp(3) -! -! m%AllOuts( RtSkew ) = m%BEMT_u(indx)%chi0*R2D ! TODO TODO TODO Not applicable + + ! Compute max radius and rotor speed + rmax = Calc_MaxRadius(p, u) + omega = Calc_Omega(u) + + m%AllOuts( RtSpeed ) = omega*RPS2RPM + m%AllOuts( RtArea ) = pi*rmax**2 + + tmp = matmul( u%HubMotion%Orientation(:,:,1), m%V_DiskAvg ) + m%AllOuts( RtVAvgxh ) = tmp(1) + m%AllOuts( RtVAvgyh ) = tmp(2) + m%AllOuts( RtVAvgzh ) = tmp(3) + + m%AllOuts( RtSkew ) = Calc_Chi0(m%V_diskAvg, m%V_dot_x) * R2D ! integrate force/moments over blades by performing mesh transfer to hub point: force = 0.0_ReKi @@ -1988,27 +2033,25 @@ subroutine Calc_WriteOutput_FVW m%AllOuts( RtAeroFyh ) = tmp(2) m%AllOuts( RtAeroFzh ) = tmp(3) - tmp = matmul( u%HubMotion%Orientation(:,:,1), moment ) - m%AllOuts( RtAeroMxh ) = tmp(1) - m%AllOuts( RtAeroMyh ) = tmp(2) - m%AllOuts( RtAeroMzh ) = tmp(3) -! -! m%AllOuts( RtAeroPwr ) = m%BEMT_u(indx)%omega * m%AllOuts( RtAeroMxh )! TODO TODO TODO -! -! - if ( EqualRealNos( m%V_dot_x, 0.0_ReKi ) ) then - m%AllOuts( RtTSR ) = 0.0_ReKi - m%AllOuts( RtAeroCp ) = 0.0_ReKi - m%AllOuts( RtAeroCq ) = 0.0_ReKi - m%AllOuts( RtAeroCt ) = 0.0_ReKi - else - ! TODO TODO TODO (need rotor area) -! denom = 0.5*p%AirDens*m%AllOuts( RtArea )*m%V_dot_x**2 -! m%AllOuts( RtTSR ) = m%BEMT_u(indx)%omega * rmax / m%V_dot_x -! m%AllOuts( RtAeroCp ) = m%AllOuts( RtAeroPwr ) / (denom * m%V_dot_x) -! m%AllOuts( RtAeroCq ) = m%AllOuts( RtAeroMxh ) / (denom * rmax) -! m%AllOuts( RtAeroCt ) = m%AllOuts( RtAeroFxh ) / denom - end if + tmp = matmul( u%HubMotion%Orientation(:,:,1), moment ) + m%AllOuts( RtAeroMxh ) = tmp(1) + m%AllOuts( RtAeroMyh ) = tmp(2) + m%AllOuts( RtAeroMzh ) = tmp(3) + + m%AllOuts( RtAeroPwr ) = omega * m%AllOuts( RtAeroMxh ) + + if ( EqualRealNos( m%V_dot_x, 0.0_ReKi ) ) then + m%AllOuts( RtTSR ) = 0.0_ReKi + m%AllOuts( RtAeroCp ) = 0.0_ReKi + m%AllOuts( RtAeroCq ) = 0.0_ReKi + m%AllOuts( RtAeroCt ) = 0.0_ReKi + else + denom = 0.5*p%AirDens*m%AllOuts( RtArea )*m%V_dot_x**2 + m%AllOuts( RtTSR ) = omega * rmax / m%V_dot_x + m%AllOuts( RtAeroCp ) = m%AllOuts( RtAeroPwr ) / (denom * m%V_dot_x) + m%AllOuts( RtAeroCq ) = m%AllOuts( RtAeroMxh ) / (denom * rmax) + m%AllOuts( RtAeroCt ) = m%AllOuts( RtAeroFxh ) / denom + end if end subroutine Calc_WriteOutput_FVW From 336d1fb75020908d788283e25b492c15675473e3 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 17 Apr 2020 16:51:42 -0600 Subject: [PATCH 125/190] FVW: adding output of induced velocities in polar plane --- modules/aerodyn/src/AeroDyn.f90 | 15 +++++--- modules/aerodyn/src/AeroDyn_IO.f90 | 57 ++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 1ed0d61ec0..5cd50dd1bf 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -61,7 +61,6 @@ module AeroDyn ! states(z) PUBLIC :: AD_GetOP !< Routine to pack the operating point values (for linearization) into arrays - contains !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine sets the initialization output data structure, which contains data to be returned to the calling program (e.g., @@ -111,7 +110,7 @@ subroutine AD_SetInitOut(p, InputFileData, InitOut, errStat, errMsg) do k=1,p%numBlades do j=1, p%NumBlNds - m = (k-1)*p%NumBlNds*24 + (j-1)*24 + m = (k-1)*p%NumBlNds*p%NBlOuts + (j-1)*p%NBlOuts WRITE (TmpChar,'(I3.3)') j chanPrefix = "B"//trim(num2lstr(k))//"N"//TmpChar @@ -163,6 +162,12 @@ subroutine AD_SetInitOut(p, InputFileData, InitOut, errStat, errMsg) InitOut%WriteOutputUnt( m + 23 ) = ' (N/m) ' InitOut%WriteOutputHdr( m + 24 ) = ' '//trim(chanPrefix)//"Gam" InitOut%WriteOutputUnt( m + 24 ) = ' (m^2/s) ' + InitOut%WriteOutputHdr( m + 25 ) = ' '//trim(chanPrefix)//"Uin" + InitOut%WriteOutputUnt( m + 25 ) = ' (m/s) ' + InitOut%WriteOutputHdr( m + 26 ) = ' '//trim(chanPrefix)//"Uit" + InitOut%WriteOutputUnt( m + 26 ) = ' (m/s) ' + InitOut%WriteOutputHdr( m + 27 ) = ' '//trim(chanPrefix)//"Uir" + InitOut%WriteOutputUnt( m + 27 ) = ' (m/s) ' end do end do @@ -1016,7 +1021,7 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) !p%RootName = TRIM(InitInp%RootName)//'.AD' ! set earlier to it could be used #ifdef DBG_OUTS - p%NBlOuts = 24 + p%NBlOuts = 27 p%numOuts = p%NumBlNds*p%NumBlades*p%NBlOuts p%NTwOuts = 0 @@ -1405,7 +1410,7 @@ subroutine SetInputsForBEMT(p, u, m, indx, errStat, errMsg) real(ReKi) :: tmp(3) real(ReKi) :: tmp_sz, tmp_sz_y real(R8Ki) :: thetaBladeNds(p%NumBlNds,p%NumBlades) - real(R8Ki) :: Azimuth(p%NumBlNds) + real(R8Ki) :: Azimuth(p%NumBlades) integer(intKi) :: j ! loop counter for nodes integer(intKi) :: k ! loop counter for blades @@ -1609,7 +1614,7 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) ! real(ReKi) :: tmp(3) ! real(ReKi) :: tmp_sz, tmp_sz_y real(R8Ki) :: thetaBladeNds(p%NumBlNds,p%NumBlades) - real(R8Ki) :: Azimuth(p%NumBlNds) + real(R8Ki) :: Azimuth(p%NumBlades) integer(intKi) :: tIndx integer(intKi) :: k ! loop counter for blades diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 592287f3ce..8bd65bf241 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1577,7 +1577,14 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) INTEGER(IntKi) :: j,k,i REAL(ReKi) :: ct, st ! cosine, sine of theta REAL(ReKi) :: cp, sp ! cosine, sine of phi - + ! Transformation matrices + !real(R8Ki), dimension(3,3) :: M_cg ! from global to airfoil-chord (this is well defined, also called "n-t" system in AeroDyn) + real(ReKi), dimension(3,3) :: M_sg ! from global to section (this is ill-defined, also called "x-y" system in AeroDyn), this coordinate is used to define the "axial" and "tangential" inductions + real(ReKi), dimension(3,3) :: M_ph ! Transformation from hub to "blade-rotor-plane": n,t,r (not the same as AeroDyn) + real(ReKi), dimension(3,3) :: M_pg ! Transformation from global to "blade-rotor-plane" (n,t,r), with same x at hub coordinate system + real(ReKi) :: psi_hub ! Azimuth wrt hub + real(ReKi), dimension(3) :: Vind_g ! Induced velocity vector in global coordinates + real(ReKi), dimension(3) :: Vind_s ! Induced velocity vector in section coordinates (AeroDyn "x-y") ! start routine: @@ -1585,16 +1592,23 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) ErrMsg = "" - if (p%WakeMod /= WakeMod_FVW) then ! blade outputs do k=1,p%numBlades + ! Rotor plane, polar coordinate system + psi_hub = TwoPi*(k-1)/p%NumBlades + M_ph(1,1:3) = (/ 1.0_ReKi, 0.0_ReKi , 0.0_ReKi /) + M_ph(2,1:3) = (/ 0.0_ReKi, cos(psi_hub), sin(psi_hub) /) + M_ph(3,1:3) = (/ 0.0_ReKi,-sin(psi_hub), cos(psi_hub) /) + M_pg = matmul(M_ph, u%HubMotion%Orientation(1:3,1:3,1) ) + + ! m%AllOuts( BPitch( k) ) = calculated in SetInputsForBEMT do j=1,p%NumBlNds - i = (k-1)*p%NumBlNds*24 + (j-1)*24 + 1 + i = (k-1)*p%NumBlNds*p%NBlOuts + (j-1)*p%NBlOuts +1 m%AllOuts( i ) = m%BEMT_u(indx)%theta(j,k)*R2D m%AllOuts( i+1 ) = m%BEMT_u(indx)%psi(k)*R2D @@ -1630,6 +1644,16 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct m%AllOuts( i+23 ) = 0.5_ReKi * p%BEMT%chord(j,k) * m%BEMT_y%Vrel(j,k) * m%BEMT_y%Cl(j,k) ! "Gam" [m^2/s] + M_sg = m%WithoutSweepPitchTwist(:,:,j,k) ! global to "section" + !M_cg = u%BladeMotion(k)%Orientation(1:3,1:3,j) ! global to chord + + Vind_s = (/ -m%BEMT_u(indx)%Vx(j,k)*m%BEMT_y%axInduction(j,k), m%BEMT_u(indx)%Vy(j,k)*m%BEMT_y%tanInduction(j,k), 0.0_ReKi /) + Vind_g = matmul(Vind_s, M_sg) + + m%AllOuts( i+24 ) = dot_product(M_pg(1,:), Vind_g(1:3) ) ! Uihn, hub normal + m%AllOuts( i+25 ) = dot_product(M_pg(2,:), Vind_g(1:3) ) ! Uiht, hub tangential + m%AllOuts( i+26 ) = dot_product(M_pg(3,:), Vind_g(1:3) ) ! Uihr, hub radial + end do ! nodes end do ! blades else ! (p%WakeMod == WakeMod_FVW) @@ -1658,17 +1682,33 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) real(ReKi) :: UrelWind_s(3) ! Wind in section coords real(ReKi) :: Vwnd(3) real(ReKi) :: Cx, Cy + ! Transformation matrices + real(R8Ki), dimension(3,3) :: M_cg ! from global to airfoil-chord (this is well defined, also called "n-t" system in AeroDyn) + real(ReKi), dimension(3,3) :: M_sg ! from global to section (this is ill-defined, also called "x-y" system in AeroDyn), this coordinate is used to define the "axial" and "tangential" inductions + real(ReKi), dimension(3,3) :: M_ph ! Transformation from hub to "blade-rotor-plane": n,t,r (not the same as AeroDyn) + real(ReKi), dimension(3,3) :: M_pg ! Transformation from global to "blade-rotor-plane" (n,t,r), with same x at hub coordinate system + real(ReKi) :: psi_hub ! Azimuth wrt hub ! blade outputs - do k=1,p%numBlades + do k=1,p%NumBlades + + ! Rotor plane, polar coordinate system + psi_hub = TwoPi*(k-1)/p%NumBlades + M_ph(1,1:3) = (/ 1.0_ReKi, 0.0_ReKi , 0.0_ReKi /) + M_ph(2,1:3) = (/ 0.0_ReKi, cos(psi_hub), sin(psi_hub) /) + M_ph(3,1:3) = (/ 0.0_ReKi,-sin(psi_hub), cos(psi_hub) /) + M_pg = matmul(M_ph, u%HubMotion%Orientation(1:3,1:3,1) ) + do j=1,p%NumBlNds !TODO: Merge with BEM to avoid all code redundancy (discuss with Bonnie) - i = (k-1)*p%NumBlNds*24 + (j-1)*24 + 1 + i = (k-1)*p%NumBlNds*p%NBlOuts + (j-1)*p%NBlOuts +1 ! --- Computing main aero variables from induction - setting local variables Vwnd = m%DisturbedInflow(:,j,k) ! NOTE: contains tower shadow - call FVW_AeroOuts( m%WithoutSweepPitchTwist(:,:,j,k), u%BladeMotion(k)%Orientation(1:3,1:3,j), m%FVW%PitchAndTwist(j,k), u%BladeMotion(k)%TranslationVel(1:3,j), & + M_sg = m%WithoutSweepPitchTwist(:,:,j,k) ! global to "section" + M_cg = u%BladeMotion(k)%Orientation(1:3,1:3,j) ! global to chord + call FVW_AeroOuts(M_sg, M_cg, m%FVW%PitchAndTwist(j,k), u%BladeMotion(k)%TranslationVel(1:3,j), & m%FVW_y%Vind(1:3,j,k), Vwnd, p%KinVisc, p%FVW%Chord(j,k), & AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) ! NOTE: using airfoil coeffs at nodes @@ -1713,6 +1753,11 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st ! "Fn" m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct ! "Ft" m%AllOuts( i+23 ) = 0.5_ReKi * p%FVW%Chord(j,k) * Vrel * AFI_interp%Cl ! "Gam" [m^2/s] + + m%AllOuts( i+24 ) = dot_product(M_pg(1,:), m%FVW_y%Vind(1:3,j,k) ) ! Uihn, hub normal + m%AllOuts( i+25 ) = dot_product(M_pg(2,:), m%FVW_y%Vind(1:3,j,k) ) ! Uiht, hub tangential + m%AllOuts( i+26 ) = dot_product(M_pg(3,:), m%FVW_y%Vind(1:3,j,k) ) ! Uihr, hub radial + end do ! nodes end do ! blades end subroutine Calc_WriteDbgOutputFVW From 5f1dc3fb23be3f5da130a0002f28bba8edab7e16 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 17 Apr 2020 18:23:20 -0600 Subject: [PATCH 126/190] FVW: adding a small margin to avoid stagnation points in tower shadow --- modules/aerodyn/src/AeroDyn.f90 | 53 ++++++++++----------------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 5cd50dd1bf..dc1e07d6ad 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -2574,8 +2574,10 @@ SUBROUTINE TwrInflArray( p, u, m, Positions, Inflow, ErrStat, ErrMsg ) real(ReKi) :: denom ! denominator real(ReKi) :: v(3) ! temp vector integer(IntKi) :: i ! loop counters for points - real(ReKi) :: MaxDiam ! Maximum tower diameter real(ReKi) :: TwrClrnc ! local tower clearance + real(ReKi) :: r_TowerBlade(3) ! distance vector from tower to blade + real(ReKi) :: TwrDiam ! local tower diameter + logical :: found integer(intKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'TwrInflArray' @@ -2585,21 +2587,26 @@ SUBROUTINE TwrInflArray( p, u, m, Positions, Inflow, ErrStat, ErrMsg ) ! these models are valid for only small tower deflections; check for potential division-by-zero errors: call CheckTwrInfl( u, ErrStat2, ErrMsg2 ); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ); if (ErrStat >= AbortErrLev) return - MaxDiam = maxval(p%TwrDiam(:)) - !$OMP PARALLEL default(shared) - !$OMP do private(i,Pos,theta_tower_trans,W_tower,xbar,ybar,zbar,TwrCd,TwrClrnc,denom,u_TwrPotent,v_TwrPotent,u_TwrShadow,v) schedule(runtime) + !$OMP do private(i,Pos,r_TowerBlade,theta_tower_trans,W_tower,xbar,ybar,zbar,TwrCd,TwrClrnc,TwrDiam,found,denom,u_TwrPotent,v_TwrPotent,u_TwrShadow,v) schedule(runtime) do i = 1, size(Positions,2) Pos=Positions(1:3,i) - ! Find nearest line2 element or node of the tower + ! Find nearest line2 element or node of the tower (see getLocalTowerProps) ! values are found for the deflected tower, returning theta_tower, W_tower, xbar, ybar, zbar, and TowerCd: - call getLocalTowerPropsNoCheck(p, u, Pos, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrClrnc) + ! option 1: nearest line2 element + call TwrInfl_NearestLine2Element(p, u, Pos, r_TowerBlade, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrDiam, found) + if ( .not. found) then + ! option 2: nearest node + call TwrInfl_NearestPoint(p, u, Pos, r_TowerBlade, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrDiam) + end if + TwrClrnc = TwoNorm(r_TowerBlade) - 0.5_ReKi*TwrDiam - if ( TwrClrnc>20*MaxDiam) then + if ( TwrClrnc>20*TwrDiam) then ! Far away, we skip the computation and keep undisturbed inflow - elseif ( TwrClrnc<=0.0_ReKi) then - ! Inside the tower, will happen for vortex elements, we keep undisturbed inflow + elseif ( TwrClrnc<=0.01_ReKi*TwrDiam) then + ! Inside the tower, or very close, (will happen for vortex elements) we keep undisturbed inflow + ! We don't want to reach the stagnation points else ! calculate tower influence: if ( abs(zbar) < 1.0_ReKi .and. p%TwrPotent /= TwrPotent_none ) then @@ -2635,43 +2642,15 @@ SUBROUTINE TwrInflArray( p, u, m, Positions, Inflow, ErrStat, ErrMsg ) u_TwrShadow = 0.0_ReKi end if - ! NOTE: we should introduce a safety check to avoid zero or negative velocities, have a minimal velocity v(1) = (u_TwrPotent + u_TwrShadow)*W_tower v(2) = v_TwrPotent*W_tower v(3) = 0.0_ReKi - Inflow(1:3,i) = Inflow(1:3,i) + matmul( theta_tower_trans, v ) endif ! Check if point far away or in tower enddo ! loop on points !$OMP END DO !$OMP END PARALLEL -contains - !> This is a simpler version of getLocalTowerProps - SUBROUTINE getLocalTowerPropsNoCheck(p, u, BladeNodePosition, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrClrnc) - TYPE(AD_InputType), INTENT(IN ) :: u !< Inputs at Time t - TYPE(AD_ParameterType), INTENT(IN ) :: p !< Parameters - REAL(ReKi) ,INTENT(IN ) :: BladeNodePosition(3) !< local blade node position - REAL(ReKi) ,INTENT( OUT) :: theta_tower_trans(3,3) !< transpose of local tower orientation expressed as a DCM - REAL(ReKi) ,INTENT( OUT) :: W_tower !< local relative wind speed normal to the tower - REAL(ReKi) ,INTENT( OUT) :: xbar !< local x^ component of r_TowerBlade normalized by tower radius - REAL(ReKi) ,INTENT( OUT) :: ybar !< local y^ component of r_TowerBlade normalized by tower radius - REAL(ReKi) ,INTENT( OUT) :: zbar !< local z^ component of r_TowerBlade normalized by tower radius - REAL(ReKi) ,INTENT( OUT) :: TwrCd !< local tower drag coefficient - REAL(ReKi) ,INTENT( OUT) :: TwrClrnc !< tower clearance for potential output - ! local variables - real(ReKi) :: r_TowerBlade(3) ! distance vector from tower to blade - real(ReKi) :: TwrDiam ! local tower diameter - logical :: found - character(*), parameter :: RoutineName = 'getLocalTowerPropsNoCheck' - ! option 1: nearest line2 element - call TwrInfl_NearestLine2Element(p, u, BladeNodePosition, r_TowerBlade, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrDiam, found) - if ( .not. found) then - ! option 2: nearest node - call TwrInfl_NearestPoint(p, u, BladeNodePosition, r_TowerBlade, theta_tower_trans, W_tower, xbar, ybar, zbar, TwrCd, TwrDiam) - end if - TwrClrnc = TwoNorm(r_TowerBlade) - 0.5_ReKi*TwrDiam - END SUBROUTINE getLocalTowerPropsNoCheck END SUBROUTINE TwrInflArray !---------------------------------------------------------------------------------------------------------------------------------- !> This routine returns the tower constants necessary to compute the tower influence. From 23a2a6f663f0e0bb14553c12ea5171601f5a8c35 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 17 Apr 2020 19:24:05 -0600 Subject: [PATCH 127/190] FVW: openmp parallelization for inflowwind TS and Uniform --- modules/inflowwind/src/IfW_TSFFWind.f90 | 11 ++++++++++- modules/inflowwind/src/IfW_UniformWind.f90 | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/modules/inflowwind/src/IfW_TSFFWind.f90 b/modules/inflowwind/src/IfW_TSFFWind.f90 index 567b5114af..aaf554d6fc 100644 --- a/modules/inflowwind/src/IfW_TSFFWind.f90 +++ b/modules/inflowwind/src/IfW_TSFFWind.f90 @@ -640,6 +640,9 @@ SUBROUTINE IfW_TSFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, Disk ! Step through all the positions and get the velocities + + !$OMP PARALLEL default(shared) if(PointNum>1000) + !$OMP do private(PointNum, TmpErrStat, TmpErrMsg ) schedule(runtime) DO PointNum = 1, NumPoints ! If the position is (0,0,0), assume it was never set and skip calculating @@ -651,16 +654,22 @@ SUBROUTINE IfW_TSFFWind_CalcOutput(Time, PositionXYZ, ParamData, Velocity, Disk ! Error handling IF (TmpErrStat /= ErrID_None) THEN ! adding this so we don't have to convert numbers to strings every time + !$OMP CRITICAL ! Needed to avoid data race on ErrStat and ErrMsg + ErrStat = ErrID_None + ErrMsg = "" 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 + !$OMP END CRITICAL END IF endif ENDDO + !$OMP END DO + !$OMP END PARALLEL + IF (ErrStat >= AbortErrLev) RETURN ! Return cannot be in parallel loop !REMOVE THIS for AeroDyn 15 diff --git a/modules/inflowwind/src/IfW_UniformWind.f90 b/modules/inflowwind/src/IfW_UniformWind.f90 index c40b00a27a..8d4b7882ff 100644 --- a/modules/inflowwind/src/IfW_UniformWind.f90 +++ b/modules/inflowwind/src/IfW_UniformWind.f90 @@ -535,24 +535,32 @@ SUBROUTINE IfW_UniformWind_CalcOutput(Time, PositionXYZ, p, Velocity, DiskVel, m CALL InterpParams(Time, p, m, op) ! Step through all the positions and get the velocities + !$OMP PARALLEL default(shared) if(NumPoints>1000) + !$OMP do private(PointNum, TmpErrStat, TmpErrMsg ) schedule(runtime) DO PointNum = 1, NumPoints ! Calculate the velocity for the position call GetWindSpeed(PositionXYZ(:,PointNum), p, m, op, Velocity(:,PointNum), TmpErrStat, TmpErrMsg) ! Error handling - CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) - IF (ErrStat >= AbortErrLev) THEN - TmpErrMsg= " Error calculating the wind speed at position ("// & + !CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) + IF (TmpErrStat >= AbortErrLev) THEN + TmpErrMsg= trim(TmpErrMsg)//" Error calculating the wind speed at position ("// & TRIM(Num2LStr(PositionXYZ(1,PointNum)))//", "// & TRIM(Num2LStr(PositionXYZ(2,PointNum)))//", "// & TRIM(Num2LStr(PositionXYZ(3,PointNum)))//") in the wind-file coordinates" + !$OMP CRITICAL ! Needed to avoid data race on ErrStat and ErrMsg + ErrStat = ErrID_None + ErrMsg = "" CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) - RETURN + !$OMP END CRITICAL ENDIF ENDDO + !$OMP END DO + !$OMP END PARALLEL + IF (ErrStat >= AbortErrLev) RETURN ! Return cannot be in parallel loop ! DiskVel term -- this represents the average across the disk -- sort of. This changes for AeroDyn 15 DiskVel = WindInf_ADhack_diskVel(Time, p, m, TmpErrStat, TmpErrMsg) @@ -677,6 +685,7 @@ SUBROUTINE GetWindSpeed(InputPosition, p, m, op, WindSpeed, ErrStat, ErrMsg) if ( InputPosition(3) < 0.0_ReKi ) then call SetErrStat(ErrID_Fatal,'Height must not be negative.',ErrStat,ErrMsg,'GetWindSpeed') + return end if !> Let \f{eqnarray}{ V_h & = & V \, \left( \frac{Z}{Z_{ref}} \right) ^ {V_{shr}} & \mbox{power-law wind shear} \\ From 96398e42288df7b0e9961f9e21bf74c24a18d469 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 20 Apr 2020 13:04:07 -0600 Subject: [PATCH 128/190] FVW: using buffer storage for segments --- modules/aerodyn/src/FVW.f90 | 89 ++++-- modules/aerodyn/src/FVW_BiotSavart.f90 | 13 +- modules/aerodyn/src/FVW_IO.f90 | 27 +- modules/aerodyn/src/FVW_Registry.txt | 8 + modules/aerodyn/src/FVW_Subs.f90 | 163 +++++------ modules/aerodyn/src/FVW_Tests.f90 | 6 +- modules/aerodyn/src/FVW_Types.f90 | 374 +++++++++++++++++++++++++ 7 files changed, 533 insertions(+), 147 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 1961f20cc6..943b7aa1d9 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -124,6 +124,9 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu ! Set parameters from InputFileData (need Misc allocated) CALL FVW_SetParametersFromInputFile(InputFileData, p, m, ErrStat2, ErrMsg2); if(Failed()) return + ! Initialize Misc Vars (after input file params) + CALL FVW_InitMiscVarsPostParam( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return + ! Initialize States Vars CALL FVW_InitStates( x, p, ErrStat2, ErrMsg2 ); if(Failed()) return @@ -188,44 +191,76 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%VTKlastTime = -HUGE(1.0_DbKi) m%tSpent = 0 - call AllocAry( m%LE , 3 , p%nSpan+1 , p%nWings, 'Leading Edge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%LE = -999999_ReKi; - call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%TE = -999999_ReKi; - call AllocAry( m%s_LL , p%nSpan+1 , p%nWings, 'Spanwise coord LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%s_LL= -999999_ReKi; - call AllocAry( m%chord_LL , p%nSpan+1 , p%nWings, 'Chord on LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%chord_LL= -999999_ReKi; - call AllocAry( m%PitchAndTwist , p%nSpan+1 , p%nWings, 'Pitch and twist ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%PitchAndTwist= -999999_ReKi; + call AllocAry( m%LE , 3 , p%nSpan+1 , p%nWings, 'Leading Edge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%LE = -999999_ReKi; + call AllocAry( m%TE , 3 , p%nSpan+1 , p%nWings, 'TrailingEdge Points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%TE = -999999_ReKi; + call AllocAry( m%s_LL , p%nSpan+1 , p%nWings, 'Spanwise coord LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%s_LL= -999999_ReKi; + call AllocAry( m%chord_LL , p%nSpan+1 , p%nWings, 'Chord on LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%chord_LL= -999999_ReKi; + call AllocAry( m%PitchAndTwist , p%nSpan+1 , p%nWings, 'Pitch and twist ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%PitchAndTwist= -999999_ReKi; ! Variables at control points/elements - call AllocAry( m%Gamma_LL, p%nSpan , p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Gamma_LL = -999999_ReKi; - call AllocAry( m%chord_CP_LL , p%nSpan , p%nWings, 'Chord on CP LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%chord_CP_LL= -999999_ReKi; - call AllocAry( m%s_CP_LL , p%nSpan , p%nWings, 'Spanwise coord CPll', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%s_CP_LL= -999999_ReKi; - call AllocAry( m%CP_LL , 3 , p%nSpan , p%nWings, 'Control points LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%CP_LL= -999999_ReKi; - call AllocAry( m%Tang , 3 , p%nSpan , p%nWings, 'Tangential vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Tang= -999999_ReKi; - call AllocAry( m%Norm , 3 , p%nSpan , p%nWings, 'Normal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Norm= -999999_ReKi; - call AllocAry( m%Orth , 3 , p%nSpan , p%nWings, 'Orthogonal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Orth= -999999_ReKi; - call AllocAry( m%dl , 3 , p%nSpan , p%nWings, 'Orthogonal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%dl= -999999_ReKi; - call AllocAry( m%Area , p%nSpan , p%nWings, 'LL Panel area ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Area = -999999_ReKi; - call AllocAry( m%diag_LL , p%nSpan , p%nWings, 'LL Panel diagonals ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%diag_LL = -999999_ReKi; - call AllocAry( m%Vind_LL , 3 , p%nSpan , p%nWings, 'Vind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_LL= -999999_ReKi; - call AllocAry( m%Vtot_LL , 3 , p%nSpan , p%nWings, 'Vtot on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vtot_LL= -999999_ReKi; - call AllocAry( m%Vstr_LL , 3 , p%nSpan , p%nWings, 'Vstr on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vstr_LL= -999999_ReKi; - call AllocAry( m%Vwnd_LL , 3 , p%nSpan , p%nWings, 'Wind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_LL= -999999_ReKi; + call AllocAry( m%Gamma_LL, p%nSpan , p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Gamma_LL = -999999_ReKi; + call AllocAry( m%chord_CP_LL , p%nSpan , p%nWings, 'Chord on CP LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%chord_CP_LL= -999999_ReKi; + call AllocAry( m%s_CP_LL , p%nSpan , p%nWings, 'Spanwise coord CPll', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%s_CP_LL= -999999_ReKi; + call AllocAry( m%CP_LL , 3 , p%nSpan , p%nWings, 'Control points LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%CP_LL= -999999_ReKi; + call AllocAry( m%Tang , 3 , p%nSpan , p%nWings, 'Tangential vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Tang= -999999_ReKi; + call AllocAry( m%Norm , 3 , p%nSpan , p%nWings, 'Normal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Norm= -999999_ReKi; + call AllocAry( m%Orth , 3 , p%nSpan , p%nWings, 'Orthogonal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Orth= -999999_ReKi; + call AllocAry( m%dl , 3 , p%nSpan , p%nWings, 'Orthogonal vector ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%dl= -999999_ReKi; + call AllocAry( m%Area , p%nSpan , p%nWings, 'LL Panel area ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Area = -999999_ReKi; + call AllocAry( m%diag_LL , p%nSpan , p%nWings, 'LL Panel diagonals ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%diag_LL = -999999_ReKi; + call AllocAry( m%Vind_LL , 3 , p%nSpan , p%nWings, 'Vind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Vind_LL= -999999_ReKi; + call AllocAry( m%Vtot_LL , 3 , p%nSpan , p%nWings, 'Vtot on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Vtot_LL= -999999_ReKi; + call AllocAry( m%Vstr_LL , 3 , p%nSpan , p%nWings, 'Vstr on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Vstr_LL= -999999_ReKi; + call AllocAry( m%Vwnd_LL , 3 , p%nSpan , p%nWings, 'Wind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Vwnd_LL= -999999_ReKi; ! Variables at panels points - call AllocAry( m%r_LL , 3 , p%nSpan+1 , 2 , p%nWings, 'Lifting Line Panels', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%r_LL= -999999_ReKi; - call AllocAry( m%Vwnd_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_NW= -999_ReKi; - call AllocAry( m%Vwnd_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vwnd_FW= -999_ReKi; - call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_NW= -999_ReKi; - call AllocAry( m%Vind_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ); m%Vind_FW= -999_ReKi; - call AllocAry( m%dxdt_NW , 3 , p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW dxdt' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); m%dxdt_NW = -999999_ReKi; - call AllocAry( m%dxdt_FW , 3 , FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW dxdt' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitStates' ); m%dxdt_FW = -999999_ReKi; + call AllocAry( m%r_LL , 3 , p%nSpan+1 , 2 , p%nWings, 'Lifting Line Panels', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%r_LL= -999999_ReKi; + call AllocAry( m%Vwnd_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Vwnd_NW= -999_ReKi; + call AllocAry( m%Vwnd_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Vwnd_FW= -999_ReKi; + call AllocAry( m%Vind_NW , 3 , p%nSpan+1 ,p%nNWMax+1, p%nWings, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Vind_NW= -999_ReKi; + call AllocAry( m%Vind_FW , 3 , FWnSpan+1 ,p%nFWMax+1, p%nWings, 'Vind on FW ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Vind_FW= -999_ReKi; + call AllocAry( m%dxdt_NW , 3 , p%nSpan+1 , p%nNWMax+1, p%nWings, 'NW dxdt' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%dxdt_NW = -999999_ReKi; + call AllocAry( m%dxdt_FW , 3 , FWnSpan+1 , p%nFWMax+1, p%nWings, 'FW dxdt' , ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%dxdt_FW = -999999_ReKi; ! Wind request points nMax = 0 nMax = nMax + p%nSpan * p%nWings ! Lifting line Control Points nMax = nMax + (p%nSpan+1) * (p%nNWMax+1) * p%nWings ! Nearwake points nMax = nMax + (FWnSpan+1) * (p%nFWMax+1) * p%nWings ! Far wake points - call AllocAry( m%r_wind, 3, nMax, 'Requested wind points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_InitMisc' ) + call AllocAry( m%r_wind, 3, nMax, 'Requested wind points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) m%r_wind = 0.0_ReKi ! set to zero so InflowWind can shortcut calculations m%OldWakeTime = -HUGE(1.0_DbKi) end subroutine FVW_InitMiscVars ! ============================================================================== +subroutine FVW_InitMiscVarsPostParam( p, m, ErrStat, ErrMsg ) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + integer(IntKi) :: ErrStat2 ! temporary error status of the operation + character(ErrMsgLen) :: ErrMsg2 ! temporary error message + character(*), parameter :: RoutineName = 'FVW_InitMiscVarsPostParam' + integer(IntKi) :: nSeg, nSegP, nSegNW !< Total number of segments after packing + integer(IntKi) :: nCPs !< Total number of control points + logical :: bMirror + ErrStat = ErrID_None + ErrMsg = "" + ! --- Counting maximum number of segments and Control Points expected for the whole simulation + call CountSegments(p, p%nNWMax, p%nFWMax, 1, nSeg, nSegP, nSegNW) + nCPs = CountCPs(p, p%nNWMax, p%nFWFree) + + bMirror = p%ShearModel==idShearMirror ! Whether or not we mirror the vorticity wrt ground + if (bMirror) then + nSeg = nSeg*2 + nSegP = nSegP*2 + endif + call AllocAry( m%SegConnct, 4, nSeg , 'SegConnct' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegConnct = -999; + call AllocAry( m%SegPoints, 3, nSegP, 'SegPoints' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegPoints = -999999_ReKi; + call AllocAry( m%SegGamma , nSeg, 'SegGamma' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegGamma = -999999_ReKi; + call AllocAry( m%SegEpsilon, nSeg, 'SegEpsilon', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegEpsilon= -999999_ReKi; + + call AllocAry( m%CPs , 3, nCPs, 'CPs' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%CPs= -999999_ReKi; + call AllocAry( m%Uind , 3, nCPs, 'Uind' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%Uind= -999999_ReKi; + +end subroutine FVW_InitMiscVarsPostParam +! ============================================================================== subroutine FVW_InitStates( x, p, ErrStat, ErrMsg ) type(FVW_ContinuousStateType), intent( out) :: x !< States type(FVW_ParameterType), intent(in ) :: p !< Parameters diff --git a/modules/aerodyn/src/FVW_BiotSavart.f90 b/modules/aerodyn/src/FVW_BiotSavart.f90 index b090c09411..0ed5c7be39 100644 --- a/modules/aerodyn/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn/src/FVW_BiotSavart.f90 @@ -97,13 +97,12 @@ end subroutine ui_seg_11 !! NOTE: this function has side effects and expects Uind_out to be initialized! !! The function can compute the velocity on part of the segments and part of the control points. !! This feature is useful if some parallelization is used, while common storage vectors are used. -subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & +subroutine ui_seg(iCPStart, iCPEnd, CPs, & iSegStart, iSegEnd, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, & RegFunction, RegParam, Uind_out) - real(ReKi), dimension(3,nCPsTot), intent(in) :: CPs !< Control points + real(ReKi), dimension(:,:), intent(in) :: CPs !< Control points (3 x nCPs++) integer(IntKi), intent(in) :: iCPStart !< Index where we start in Control points array integer(IntKi), intent(in) :: iCPEnd !< Index where we end in Control points array - integer(IntKi), intent(in) :: nCPsTot !< Total number of control points real(ReKi), dimension(3,nSegPTot), intent(in) :: SegPoints !< Segment points integer(IntKi), dimension(:,:), intent(in) :: SegConnct !< Connectivity, indices of segments points iSeg1, iSeg2, iDepth, iSpan real(ReKi), dimension(nSegTot), intent(in) :: SegGamma !< Segment circulation @@ -113,7 +112,7 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & integer(IntKi), intent(in) :: nSegPTot !< Total number of segment points integer(IntKi), intent(in) :: RegFunction !< Regularization model real(ReKi), dimension(nSegTot), intent(in) :: RegParam !< Regularization parameter - real(ReKi), dimension(3,nCPsTot), intent(inout) :: Uind_out !< Induced velocity vector - Side effects!!! + real(ReKi), dimension(:,:), intent(inout) :: Uind_out !< Induced velocity vector - Side effects!!! (3 x nCPs++) ! Variables integer(IntKi) :: icp, is real(ReKi), dimension(3) :: Uind !< @@ -323,13 +322,13 @@ subroutine ui_seg(iCPStart, iCPEnd, nCPsTot, CPs, & !> Velocity induced by one vortex quad on nCPs Control Points subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, RegFunction, RegParam, Uind) ! Arguments declarations - integer, intent(in) :: nCPs !< - real(ReKi), dimension(3,nCPs), intent(in) :: CPs !< + integer, intent(in) :: nCPs !< + real(ReKi), dimension(:,:), intent(in) :: CPs !< 3 x "nCPs"++ real(ReKi), dimension(3), intent(in) :: P1,P2,P3,P4 !< Coordinates of vortex quadrilateral real(ReKi), intent(in) :: Gamm integer(IntKi) , intent(in) :: RegFunction !< Regularization model (e.g. LambOseen) real(ReKi), intent(in) :: RegParam !< Regularization parameter [m] - real(ReKi), dimension(3,nCPs), intent(inout) :: Uind !< side effects!!! + real(ReKi), dimension(:,:), intent(inout) :: Uind !< side effects!!! 3 x "nCPs++" ! Variable declarations real(ReKi), dimension(3) :: CP !< real(ReKi), dimension(3) :: Uindtmp !< diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index ea201d5abb..adeb1bbeea 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -183,12 +183,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) character(255) :: Label character(Twidth) :: Tstr ! string for current VTK write-out step (padded with zeros) character(1), dimension(3) :: I2ABC =(/'A','B','C'/) - ! - integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity - real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points - real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation - real(ReKi), dimension(:), allocatable :: SegEpsilon !< - integer(IntKi) :: iHeadC, iHeadP, nSeg, nSegP + integer(IntKi) :: nSeg, nSegP, nSegNW logical :: bMirror integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 @@ -255,23 +250,15 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! --------------------------------------------------------------------------------} ! --- All Segments ! --------------------------------------------------------------------------------{ + ! NOTE: now we rely on the fact that the segments in Misc are well set + ! These segments are correct after a call to CalcOutput + ! The alternative is to call PackPanelsToSegments as was done before + ! This would require to allocate some local SegPoints,SegConnct here. ! False below is to avoid writing the mirrored vorticity, this could be an option though bMirror= (p%ShearModel==idShearMirror) .and. (p%VTKBlades<0) ! NOTE: temporary hack to output mirrored vorticity - call PackPanelsToSegments(p, m, x, 1, bMirror, SegConnct, SegPoints, SegGamma, nSeg, nSegP) - if (m%nNW==1) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel - iHeadP=1 - iHeadC=1 - do iW=1,p%nWings - CALL LatticeToSegments(m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW), 1, SegPoints, SegConnct, SegGamma, iHeadP, iHeadC, .True. , .True.) - enddo - endif - - allocate(SegEpsilon(1:size(SegConnct,2))) - call WakeRegularization(p, x, m, SegConnct, SegPoints, SegGamma, SegEpsilon, ErrStat2, ErrMsg2) - + call CountSegments(p, m%nNW, m%nFW, 1, nSeg, nSegP, nSegNW) Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' - CALL WrVTK_Segments(Filename, SegPoints, SegConnct, SegGamma, SegEpsilon) - deallocate(SegEpsilon) + CALL WrVTK_Segments(Filename, m%SegPoints(:,1:nSegP), m%SegConnct(:,1:nSeg), m%SegGamma(1:nSeg), m%SegEpsilon(1:nSeg)) if(.false.) print*,z%Gamma_LL(1,1) ! unused var for now end subroutine WrVTK_FVW diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 8a69f3d482..65e0bc7edd 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -88,6 +88,14 @@ typedef ^ ^ DbKi typedef ^ ^ ReKi tSpent - - - "Time spent in expensive Biot-Savart computation" s typedef ^ ^ ReKi dxdt_NW :::: - - "State time derivatie, stored for subcylcing" - typedef ^ ^ ReKi dxdt_FW :::: - - "State time derivatie, stored for subcylcing" - +# Segment storage (buffer) +typedef ^ ^ IntKi SegConnct :: - - "Connectivity of segments" - +typedef ^ ^ ReKi SegPoints :: - - "Points delimiting the segments" - +typedef ^ ^ ReKi SegGamma : - - "Segment circulations" - +typedef ^ ^ ReKi SegEpsilon : - - "Segment regularization parameter" - +# Wake rollup storage (buffer) +typedef ^ ^ ReKi CPs :: - - "Control points used for wake rollup computation" - +typedef ^ ^ ReKi Uind :: - - "Induced velocities obtained at control points" - # ........ Input ............ # FVW_InputType diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 793cd6eddd..e48f7da367 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -493,7 +493,49 @@ subroutine DistributeRequestedWind(V_wind, p, m) end subroutine DistributeRequestedWind +!> Count how many segments are needed to represent the Near wake and far wakes, starting at a given depth +subroutine CountSegments(p, nNW, nFW, iDepthStart, nSeg, nSegP, nSegNW) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + integer(IntKi), intent(in ) :: nNW !< Number of NW panels + integer(IntKi), intent(in ) :: nFW !< Number of FW panels + integer(IntKi), intent(in ) :: iDepthStart !< Index where we start packing for NW panels + integer(IntKi), intent( out) :: nSeg !< Total number of segments after packing + integer(IntKi), intent( out) :: nSegP !< Total number of segments points after packing + integer(IntKi), intent( out) :: nSegNW !< Total number of segments points for the near wake only + logical :: LastNWShed + ! If the FW contains Shed vorticity, we include the last shed vorticity from the NW, otherwise, we don't! + ! It's important not to include it, otherwise a strong vortex will be present there with no compensating vorticity from the FW + LastNWShed = (p%FWShedVorticity ) .or. ((.not.p%FWShedVorticity) .and. (nNW=0) then + nSegP = p%nWings * ( (p%nSpan+1)*(nNW-iDepthStart+2) ) + nSegNW = p%nWings * (2*(p%nSpan+1)*(nNW-iDepthStart+2)-(p%nSpan+1)-(nNW-iDepthStart+1+1)) + if (.not.LastNWShed) then + nSegNW = nSegNW - p%nWings * (p%nSpan) ! Removing last set of shed segments + endif + endif + nSeg=nSegNW + ! FW segments + if (nFW>0) then + nSegP = nSegP + p%nWings * ( (FWnSpan+1)*(nFW+1) ) + if (p%FWShedVorticity) then + nSeg = nSeg + p%nWings * (2*(FWnSpan+1)*(nFW+1)-(FWnSpan+1)-(nFW+1)) + else + nSeg = nSeg + p%nWings * ( (FWnSpan+1)*(nFW) ) ! No Shed vorticity + endif + endif +end subroutine CountSegments +!> Count how many control points are convecting (needed to compute the wake convection) +pure integer(IntKi) function CountCPs(p, nNW, nFWEff) result(nCPs) + type(FVW_ParameterType), intent(in ) :: p !< Parameters + integer(IntKi), intent(in ) :: nNW !< Number of NW panels + integer(IntKi), intent(in ) :: nFWEff !< Number of effective (ie. convecting) FW panels + nCPs = p%nWings * ( (p%nSpan+1)*(nNW+1) ) + if (nFWEff>0) nCPs = nCPs + p%nWings * ((FWnSpan+1)*(nFWEff+1) ) +end function CountCPs subroutine PackPanelsToSegments(p, m, x, iDepthStart, bMirror, SegConnct, SegPoints, SegGamma, nSeg, nSegP) @@ -502,9 +544,9 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, bMirror, SegConnct, SegPoi type(FVW_ContinuousStateType), intent(in ) :: x !< States integer(IntKi), intent(in ) :: iDepthStart !< Index where we start packing for NW panels logical, intent(in ) :: bMirror !< Mirror the vorticity wrt the ground - integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity - real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points - real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation + integer(IntKi),dimension(:,:), intent(inout) :: SegConnct !< Segment connectivity + real(ReKi), dimension(:,:), intent(inout) :: SegPoints !< Segment Points + real(ReKi), dimension(:) , intent(inout) :: SegGamma !< Segment Circulation integer(IntKi), intent(out) :: nSeg !< Total number of segments after packing integer(IntKi), intent(out) :: nSegP !< Total number of segments points after packing ! Local @@ -516,41 +558,14 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, bMirror, SegConnct, SegPoi LastNWShed = (p%FWShedVorticity ) .or. ((.not.p%FWShedVorticity) .and. (m%nNW=0) then - nP = p%nWings * ( (p%nSpan+1)*(m%nNW-iDepthStart+2) ) - nCNW = p%nWings * (2*(p%nSpan+1)*(m%nNW-iDepthStart+2)-(p%nSpan+1)-(m%nNW-iDepthStart+1+1)) - if (.not.LastNWShed) then - nCNW = nCNW - p%nWings * (p%nSpan) ! Removing last set of shed segments - endif - endif - nC=nCNW - if (m%nFW>0) then - nP = nP + p%nWings * ( (FWnSpan+1)*(m%nFW+1) ) - if (p%FWShedVorticity) then - nC = nC + p%nWings * (2*(FWnSpan+1)*(m%nFW+1)-(FWnSpan+1)-(m%nFW+1)) - else - nC = nC + p%nWings * ( (FWnSpan+1)*(m%nFW) ) ! No Shed vorticity - endif - endif + ! Returns nC, nP, nCNW, number of segments (without accounting for mirroring) + call CountSegments(p, m%nNW, m%nFW, iDepthStart, nC, nP, nCNW) if (nP>0) then - if (allocated(SegConnct)) deallocate(SegConnct) - if (allocated(SegPoints)) deallocate(SegPoints) - if (allocated(SegGamma)) deallocate(SegGamma) - if (bMirror) then - ! we double the storage dimension when we mirror the vorticity - allocate(SegConnct(1:4,1:2*nC)); SegConnct=-1 ! 4 values per segmnt: iP1, iP2, iDepth, iSpan - allocate(SegPoints(1:3,1:2*nP)); SegPoints=-1 - allocate(SegGamma (1:2*nC)); SegGamma =-1 - else - allocate(SegConnct(1:4,1:nC)); SegConnct=-1 - allocate(SegPoints(1:3,1:nP)); SegPoints=-1 - allocate(SegGamma (1:nC)); SegGamma =-1 - endif - + ! Nullifying for safety + SegConnct=-1 + SegPoints=-1 + SegGamma =-1 ! iHeadP=1 iHeadC=1 @@ -603,8 +618,6 @@ subroutine PackPanelsToSegments(p, m, x, iDepthStart, bMirror, SegConnct, SegPoi nSeg = nSeg*2 nSegP = nSegP*2 endif - - else nSeg = 0 nSegP = 0 @@ -731,12 +744,6 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables integer(IntKi) :: iW, nSeg, nSegP, nCPs, iHeadP - integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity - real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points - real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation - real(ReKi), dimension(:) , allocatable :: SegEpsilon !< Segment regularization parameter - real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints - real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step logical :: bMirror ! True if we mirror the vorticity wrt ground ErrStat= ErrID_None @@ -749,59 +756,45 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) m%Vind_FW = -9999._ReKi !< Safety ! --- Packing all vortex elements into a list of segments - ! NOTE: allocates SegConnct, SegPoints, SegGamma.. - call PackPanelsToSegments(p, m, x, 1, bMirror, SegConnct, SegPoints, SegGamma, nSeg, nSegP) + ! NOTE: modifies m%Seg* + call PackPanelsToSegments(p, m, x, 1, bMirror, m%SegConnct, m%SegPoints, m%SegGamma, nSeg, nSegP) - ! --- Setting up regularization - allocate(SegEpsilon(1:nSeg)); - call WakeRegularization(p, x, m, SegConnct, SegPoints, SegGamma, SegEpsilon, ErrStat, ErrMsg) + ! --- Setting up regularization SegEpsilon + call WakeRegularization(p, x, m, m%SegConnct, m%SegPoints, m%SegGamma, m%SegEpsilon(1:nSeg), ErrStat, ErrMsg) ! --- Computing induced velocity call PackConvectingPoints() if (DEV_VERSION) then print'(A,I0,A,I0,A,I0)','Convection - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs endif - call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, p%RegFunction, SegEpsilon, Uind) + call ui_seg( 1, nCPs, m%CPs, 1, nSeg, nSeg, nSegP, m%SegPoints, m%SegConnct, m%SegGamma, p%RegFunction, m%SegEpsilon, m%Uind) call UnPackInducedVelocity() - deallocate(Uind) - deallocate(CPs) - deallocate(SegConnct) - deallocate(SegGamma) - deallocate(SegPoints) - deallocate(SegEpsilon) contains !> Pack all the points that convect subroutine PackConvectingPoints() ! Counting total number of control points that convects - nCPs = p%nWings * ( (p%nSpan+1)*(m%nNW+1) ) - if (nFWEff>0) then - nCPs = nCPs + p%nWings * ((FWnSpan+1)*(nFWEff+1) ) - endif - - ! Allocation - allocate(CPs (1:3,1:nCPs)) - allocate(Uind(1:3,1:nCPs)) - Uind=0.0_ReKi !< important due to side effects of ui_seg - + nCPs = CountCPs(p, m%nNW, nFWEff) + m%Uind=0.0_ReKi ! very important due to side effects of ui_seg + m%CPs=-999.9_ReKi ! Packing iHeadP=1 do iW=1,p%nWings - CALL LatticeToPoints(x%r_NW(1:3,:,1:m%nNW+1,iW), 1, CPs, iHeadP) + CALL LatticeToPoints(x%r_NW(1:3,:,1:m%nNW+1,iW), 1, m%CPs, iHeadP) enddo if (nFWEff>0) then do iW=1,p%nWings - CALL LatticeToPoints(x%r_FW(1:3,:,1:nFWEff+1,iW), 1, CPs, iHeadP) + CALL LatticeToPoints(x%r_FW(1:3,:,1:nFWEff+1,iW), 1, m%CPs, iHeadP) enddo endif if (DEV_VERSION) then ! Additional checks - if (any(CPs(1,:)<=-99)) then + if (any(m%CPs(1,1:nCPs)<=-99)) then call print_x_NW_FW(p,m,x,'pack') ErrMsg='PackConvectingPoints: Problem in Control points'; ErrStat=ErrID_Fatal; return endif - if ((iHeadP-1)/=size(CPs,2)) then - print*,'PackConvectingPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 + if ((iHeadP-1)/=nCPs) then + print*,'PackConvectingPoints: Number of points wrongly estimated',nCPs, iHeadP-1 STOP ! Keep me. The check will be removed once the code is well established ErrMsg='PackConvectingPoints: Number of points wrongly estimated '; ErrStat=ErrID_Fatal; return endif @@ -811,12 +804,11 @@ subroutine PackConvectingPoints() subroutine UnPackInducedVelocity() iHeadP=1 do iW=1,p%nWings - CALL VecToLattice(Uind, 1, m%Vind_NW(:,:,1:m%nNW+1,iW), iHeadP) + CALL VecToLattice(m%Uind, 1, m%Vind_NW(:,:,1:m%nNW+1,iW), iHeadP) enddo - ! TODO if (nFWEff>0) then do iW=1,p%nWings - CALL VecToLattice(Uind, 1, m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,iW), iHeadP) + CALL VecToLattice(m%Uind, 1, m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,iW), iHeadP) enddo if (DEV_VERSION) then if (any(m%Vind_FW(1:3,1:FWnSpan+1,1:nFWEff+1,:)<-99)) then @@ -825,8 +817,8 @@ subroutine UnPackInducedVelocity() endif endif if (DEV_VERSION) then - if ((iHeadP-1)/=size(Uind,2)) then - print*,'UnPackInducedVelocity: Number of points wrongly estimated',size(Uind,2), iHeadP-1 + if ((iHeadP-1)/=nCPs) then + print*,'UnPackInducedVelocity: Number of points wrongly estimated',nCPs, iHeadP-1 STOP ! Keep me. The check will be removed once the code is well established ErrMsg='UnPackInducedVelocity: Number of points wrongly estimated'; ErrStat=ErrID_Fatal; return endif @@ -845,10 +837,6 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables ! Local variables integer(IntKi) :: iW, nSeg, nSegP, nCPs, iHeadP - integer(IntKi),dimension(:,:), allocatable :: SegConnct !< Segment connectivity - real(ReKi), dimension(:,:), allocatable :: SegPoints !< Segment Points - real(ReKi), dimension(:) , allocatable :: SegGamma !< Segment Circulation - real(ReKi), dimension(:) , allocatable :: SegEpsilon !< Segment smooth parameter real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints real(ReKi), dimension(:,:), allocatable :: Uind !< Induced velocity integer(IntKi), intent( out) :: ErrStat !< Error status of the operation @@ -860,7 +848,7 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) bMirror = p%ShearModel==idShearMirror ! Whether or not we mirror the vorticity wrt ground ! --- Packing all vortex elements into a list of segments - call PackPanelsToSegments(p, m, x, iDepthStart, bMirror, SegConnct, SegPoints, SegGamma, nSeg, nSegP) + call PackPanelsToSegments(p, m, x, iDepthStart, bMirror, m%SegConnct, m%SegPoints, m%SegGamma, nSeg, nSegP) ! --- Computing induced velocity if (nSegP==0) then @@ -871,27 +859,22 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) endif else ! --- Setting up regularization - allocate(SegEpsilon(1:nSeg)); - call WakeRegularization(p, x, m, SegConnct, SegPoints, SegGamma, SegEpsilon, ErrStat, ErrMsg) + call WakeRegularization(p, x, m, m%SegConnct(:,1:nSeg), m%SegPoints(:,1:nSegP), m%SegGamma(1:nSeg), m%SegEpsilon(1:nSeg), ErrStat, ErrMsg) nCPs=p%nWings * p%nSpan - allocate(CPs (1:3,1:nCPs)) - allocate(Uind(1:3,1:nCPs)) + allocate(CPs (1:3,1:nCPs)) ! NOTE: here we do allocate CPs and Uind insteadof using Misc + allocate(Uind(1:3,1:nCPs)) ! The size is reasonably small, and m%Uind then stay filled with "rollup velocities" (for export) Uind=0.0_ReKi !< important due to side effects of ui_seg ! --- call PackLiftingLinePoints() if (DEV_VERSION) then print'(A,I0,A,I0,A,I0)','Induction - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs endif - call ui_seg( 1, nCPs, nCPs, CPs, 1, nSeg, nSeg, nSegP, SegPoints, SegConnct, SegGamma, p%RegFunction, SegEpsilon, Uind) + call ui_seg( 1, nCPs, CPs, 1, nSeg, nSeg, nSegP, m%SegPoints, m%SegConnct, m%SegGamma, p%RegFunction, m%SegEpsilon, Uind) call UnPackLiftingLineVelocities() deallocate(Uind) deallocate(CPs) - deallocate(SegConnct) - deallocate(SegGamma) - deallocate(SegPoints) - deallocate(SegEpsilon) endif contains !> Pack all the control points diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index 7580a7e815..836fda7526 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -260,7 +260,7 @@ subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) RegFunction = idRegVALID(i1) ! Method 1 Uind_out =0.0_ReKi - call ui_seg(1, 1, nCPsTot, CPs, & + call ui_seg(1, 1, CPs, & 1, 1, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, & RegFunction, RegParam, Uind_out) ! Method 2 @@ -300,7 +300,7 @@ subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) RegFunction = idRegVALID(i1) ! Method 1 Uind_out =0.0_ReKi - call ui_seg(1, 1, nCPsTot, CPs, & + call ui_seg(1, 1, CPs, & 1, 2, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, & RegFunction, RegParam, Uind_out) ! Method 2 @@ -379,7 +379,7 @@ subroutine Test_LatticeToSegment(iStat) CPs(1:3,1)=(/1.5,1.5,0./) SegEpsilon=100.0_ReKi SmoothModel=0 ! No smooth - CALL ui_seg(1, 1, 1, CPs, & + CALL ui_seg(1, 1, CPs, & 1, nC1, nC1, nP1, SegPoints, SegConnct, SegGamma, & SmoothModel, SegEpsilon, Uind) print*,'Uind',Uind diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 06c708dc56..bb29be2501 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -114,6 +114,12 @@ MODULE FVW_Types REAL(ReKi) :: tSpent !< Time spent in expensive Biot-Savart computation [s] REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: dxdt_NW !< State time derivatie, stored for subcylcing [-] REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: dxdt_FW !< State time derivatie, stored for subcylcing [-] + INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: SegConnct !< Connectivity of segments [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: SegPoints !< Points delimiting the segments [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: SegGamma !< Segment circulations [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: SegEpsilon !< Segment regularization parameter [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: CPs !< Control points used for wake rollup computation [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Uind !< Induced velocities obtained at control points [-] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -1290,6 +1296,86 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) END IF END IF DstMiscData%dxdt_FW = SrcMiscData%dxdt_FW +ENDIF +IF (ALLOCATED(SrcMiscData%SegConnct)) THEN + i1_l = LBOUND(SrcMiscData%SegConnct,1) + i1_u = UBOUND(SrcMiscData%SegConnct,1) + i2_l = LBOUND(SrcMiscData%SegConnct,2) + i2_u = UBOUND(SrcMiscData%SegConnct,2) + IF (.NOT. ALLOCATED(DstMiscData%SegConnct)) THEN + ALLOCATE(DstMiscData%SegConnct(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%SegConnct.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%SegConnct = SrcMiscData%SegConnct +ENDIF +IF (ALLOCATED(SrcMiscData%SegPoints)) THEN + i1_l = LBOUND(SrcMiscData%SegPoints,1) + i1_u = UBOUND(SrcMiscData%SegPoints,1) + i2_l = LBOUND(SrcMiscData%SegPoints,2) + i2_u = UBOUND(SrcMiscData%SegPoints,2) + IF (.NOT. ALLOCATED(DstMiscData%SegPoints)) THEN + ALLOCATE(DstMiscData%SegPoints(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%SegPoints.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%SegPoints = SrcMiscData%SegPoints +ENDIF +IF (ALLOCATED(SrcMiscData%SegGamma)) THEN + i1_l = LBOUND(SrcMiscData%SegGamma,1) + i1_u = UBOUND(SrcMiscData%SegGamma,1) + IF (.NOT. ALLOCATED(DstMiscData%SegGamma)) THEN + ALLOCATE(DstMiscData%SegGamma(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%SegGamma.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%SegGamma = SrcMiscData%SegGamma +ENDIF +IF (ALLOCATED(SrcMiscData%SegEpsilon)) THEN + i1_l = LBOUND(SrcMiscData%SegEpsilon,1) + i1_u = UBOUND(SrcMiscData%SegEpsilon,1) + IF (.NOT. ALLOCATED(DstMiscData%SegEpsilon)) THEN + ALLOCATE(DstMiscData%SegEpsilon(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%SegEpsilon.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%SegEpsilon = SrcMiscData%SegEpsilon +ENDIF +IF (ALLOCATED(SrcMiscData%CPs)) THEN + i1_l = LBOUND(SrcMiscData%CPs,1) + i1_u = UBOUND(SrcMiscData%CPs,1) + i2_l = LBOUND(SrcMiscData%CPs,2) + i2_u = UBOUND(SrcMiscData%CPs,2) + IF (.NOT. ALLOCATED(DstMiscData%CPs)) THEN + ALLOCATE(DstMiscData%CPs(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%CPs.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%CPs = SrcMiscData%CPs +ENDIF +IF (ALLOCATED(SrcMiscData%Uind)) THEN + i1_l = LBOUND(SrcMiscData%Uind,1) + i1_u = UBOUND(SrcMiscData%Uind,1) + i2_l = LBOUND(SrcMiscData%Uind,2) + i2_u = UBOUND(SrcMiscData%Uind,2) + IF (.NOT. ALLOCATED(DstMiscData%Uind)) THEN + ALLOCATE(DstMiscData%Uind(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Uind.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Uind = SrcMiscData%Uind ENDIF END SUBROUTINE FVW_CopyMisc @@ -1382,6 +1468,24 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) ENDIF IF (ALLOCATED(MiscData%dxdt_FW)) THEN DEALLOCATE(MiscData%dxdt_FW) +ENDIF +IF (ALLOCATED(MiscData%SegConnct)) THEN + DEALLOCATE(MiscData%SegConnct) +ENDIF +IF (ALLOCATED(MiscData%SegPoints)) THEN + DEALLOCATE(MiscData%SegPoints) +ENDIF +IF (ALLOCATED(MiscData%SegGamma)) THEN + DEALLOCATE(MiscData%SegGamma) +ENDIF +IF (ALLOCATED(MiscData%SegEpsilon)) THEN + DEALLOCATE(MiscData%SegEpsilon) +ENDIF +IF (ALLOCATED(MiscData%CPs)) THEN + DEALLOCATE(MiscData%CPs) +ENDIF +IF (ALLOCATED(MiscData%Uind)) THEN + DEALLOCATE(MiscData%Uind) ENDIF END SUBROUTINE FVW_DestroyMisc @@ -1564,6 +1668,36 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*4 ! dxdt_FW upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%dxdt_FW) ! dxdt_FW END IF + Int_BufSz = Int_BufSz + 1 ! SegConnct allocated yes/no + IF ( ALLOCATED(InData%SegConnct) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! SegConnct upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%SegConnct) ! SegConnct + END IF + Int_BufSz = Int_BufSz + 1 ! SegPoints allocated yes/no + IF ( ALLOCATED(InData%SegPoints) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! SegPoints upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%SegPoints) ! SegPoints + END IF + Int_BufSz = Int_BufSz + 1 ! SegGamma allocated yes/no + IF ( ALLOCATED(InData%SegGamma) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! SegGamma upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%SegGamma) ! SegGamma + END IF + Int_BufSz = Int_BufSz + 1 ! SegEpsilon allocated yes/no + IF ( ALLOCATED(InData%SegEpsilon) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! SegEpsilon upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%SegEpsilon) ! SegEpsilon + END IF + Int_BufSz = Int_BufSz + 1 ! CPs allocated yes/no + IF ( ALLOCATED(InData%CPs) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! CPs upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%CPs) ! CPs + END IF + Int_BufSz = Int_BufSz + 1 ! Uind allocated yes/no + IF ( ALLOCATED(InData%Uind) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Uind upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Uind) ! Uind + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -2116,6 +2250,96 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%dxdt_FW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%dxdt_FW))-1 ) = PACK(InData%dxdt_FW,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%dxdt_FW) END IF + IF ( .NOT. ALLOCATED(InData%SegConnct) ) 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%SegConnct,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%SegConnct,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%SegConnct,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%SegConnct,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%SegConnct)>0) IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(InData%SegConnct))-1 ) = PACK(InData%SegConnct,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(InData%SegConnct) + END IF + IF ( .NOT. ALLOCATED(InData%SegPoints) ) 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%SegPoints,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%SegPoints,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%SegPoints,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%SegPoints,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%SegPoints)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%SegPoints))-1 ) = PACK(InData%SegPoints,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%SegPoints) + END IF + IF ( .NOT. ALLOCATED(InData%SegGamma) ) 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%SegGamma,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%SegGamma,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%SegGamma)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%SegGamma))-1 ) = PACK(InData%SegGamma,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%SegGamma) + END IF + IF ( .NOT. ALLOCATED(InData%SegEpsilon) ) 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%SegEpsilon,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%SegEpsilon,1) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%SegEpsilon)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%SegEpsilon))-1 ) = PACK(InData%SegEpsilon,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%SegEpsilon) + END IF + IF ( .NOT. ALLOCATED(InData%CPs) ) 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%CPs,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CPs,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%CPs,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CPs,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%CPs)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%CPs))-1 ) = PACK(InData%CPs,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%CPs) + END IF + IF ( .NOT. ALLOCATED(InData%Uind) ) 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%Uind,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Uind,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Uind,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Uind,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Uind)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Uind))-1 ) = PACK(InData%Uind,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Uind) + END IF END SUBROUTINE FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2949,6 +3173,156 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%dxdt_FW) DEALLOCATE(mask4) END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! SegConnct 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 + IF (ALLOCATED(OutData%SegConnct)) DEALLOCATE(OutData%SegConnct) + ALLOCATE(OutData%SegConnct(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%SegConnct.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%SegConnct)>0) OutData%SegConnct = UNPACK( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%SegConnct))-1 ), mask2, 0_IntKi ) + Int_Xferred = Int_Xferred + SIZE(OutData%SegConnct) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! SegPoints 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 + IF (ALLOCATED(OutData%SegPoints)) DEALLOCATE(OutData%SegPoints) + ALLOCATE(OutData%SegPoints(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%SegPoints.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%SegPoints)>0) OutData%SegPoints = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%SegPoints))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%SegPoints) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! SegGamma 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 + IF (ALLOCATED(OutData%SegGamma)) DEALLOCATE(OutData%SegGamma) + ALLOCATE(OutData%SegGamma(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%SegGamma.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%SegGamma)>0) OutData%SegGamma = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%SegGamma))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%SegGamma) + DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! SegEpsilon 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 + IF (ALLOCATED(OutData%SegEpsilon)) DEALLOCATE(OutData%SegEpsilon) + ALLOCATE(OutData%SegEpsilon(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%SegEpsilon.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask1(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask1 = .TRUE. + IF (SIZE(OutData%SegEpsilon)>0) OutData%SegEpsilon = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%SegEpsilon))-1 ), mask1, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%SegEpsilon) + DEALLOCATE(mask1) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CPs 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 + IF (ALLOCATED(OutData%CPs)) DEALLOCATE(OutData%CPs) + ALLOCATE(OutData%CPs(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%CPs.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%CPs)>0) OutData%CPs = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%CPs))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%CPs) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Uind 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 + IF (ALLOCATED(OutData%Uind)) DEALLOCATE(OutData%Uind) + ALLOCATE(OutData%Uind(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Uind.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%Uind)>0) OutData%Uind = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Uind))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Uind) + DEALLOCATE(mask2) + END IF END SUBROUTINE FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) From 64fc66dc8a883833460bbf3f200d7d8ce0c93275 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 20 Apr 2020 16:24:34 -0600 Subject: [PATCH 129/190] FVW: exporting convection velocity with NW and FW vtks --- modules/aerodyn/src/FVW_IO.f90 | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index adeb1bbeea..f36f4c217b 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -187,6 +187,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) logical :: bMirror integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 + real(Reki), dimension(:,:,:), allocatable :: dxdt_0 !< if (DEV_VERSION) then print*,'------------------------------------------------------------------------------' @@ -233,9 +234,11 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) write(Label,'(A,A)') 'NW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' if (m%FirstCall) then ! Small Hack - At t=0, NW not set, but first NW panel is the LL panel - call WrVTK_Lattice(FileName, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW)) + allocate(dxdt_0(3, size(m%dxdt_NW,2) , m%nNW+1)); dxdt_0=0.0_ReKi + call WrVTK_Lattice(FileName, m%r_LL(1:3,:,1:2,iW), m%Gamma_LL(:,iW:iW),dxdt_0) + deallocate(dxdt_0) else - call WrVTK_Lattice(FileName, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW)) + call WrVTK_Lattice(FileName, x%r_NW(1:3,:,1:m%nNW+1,iW), x%Gamma_NW(:,1:m%nNW,iW), m%dxdt_NW(:,:,1:m%nNW+1,iW)) endif enddo ! --------------------------------------------------------------------------------} @@ -245,7 +248,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) do iW=1,p%VTKBlades write(Label,'(A,A)') 'FW.Bld', i2ABC(iW) Filename = TRIM(FileRootName)//'.'//trim(Label)//'.'//Tstr//'.vtk' - call WrVTK_Lattice(FileName, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW)) + call WrVTK_Lattice(FileName, x%r_FW(1:3,1:FWnSpan+1,1:m%nFW+1,iW), x%Gamma_FW(1:FWnSpan,1:m%nFW,iW),m%dxdt_FW(:,:,1:m%nFW+1,iW)) enddo ! --------------------------------------------------------------------------------} ! --- All Segments @@ -279,18 +282,16 @@ subroutine WrVTK_Segments(filename, SegPoints, SegConnct, SegGamma, SegEpsilon) call vtk_cell_data_scalar(SegEpsilon,'Epsilon') ! call vtk_cell_data_scalar(real(SegConnct(3,:), ReKi),'Age') !call vtk_cell_data_scalar(real(SegConnct(4,:), ReKi),'Span') - !call vtk_point_data_init() - !call vtk_point_data_vector(Sgmt%UconvP(1:3,1:Sgmt%nP_Storage),'Uconv') call vtk_close_file() endif end subroutine -subroutine WrVTK_Lattice(filename, LatticePoints, LatticeGamma) +subroutine WrVTK_Lattice(filename, LatticePoints, LatticeGamma, LatticeData3d) use VTK ! for all the vtk_* functions character(len=*), intent(in) :: filename real(Reki), dimension(:,:,:), intent(in ) :: LatticePoints !< Array of points 3 x nSpan x nDepth real(Reki), dimension(:,:), intent(in ) :: LatticeGamma !< Array of nSpan x nDepth - !real(Reki), dimension(:,:,:), intent(in ), optional :: LatticeData3d !< Array of n x nSpan x nDepth KEEP ME + real(Reki), dimension(:,:,:), intent(in ), optional :: LatticeData3d !< Array of n x nSpan x nDepth KEEP ME ! integer(IntKi), dimension(:,:), allocatable :: Connectivity real(ReKi), dimension(:,:), allocatable :: Points @@ -302,6 +303,10 @@ subroutine WrVTK_Lattice(filename, LatticePoints, LatticeGamma) call vtk_quad(Connectivity) call vtk_cell_data_init() call vtk_cell_data_scalar(LatticeGamma,'Gamma') + if (present(LatticeData3d)) then + call vtk_point_data_init() + call vtk_point_data_vector(LatticeData3d,'Uconv') + endif call vtk_close_file() endif From f4276e94646fc8b8d9c408497180380cab0d0b31 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 20 Apr 2020 17:04:19 -0600 Subject: [PATCH 130/190] FVW: preparing for additional input file parameters --- modules/aerodyn/src/AeroDyn.f90 | 6 ++- modules/aerodyn/src/FVW.f90 | 5 ++- modules/aerodyn/src/FVW_IO.f90 | 13 +++++++ modules/aerodyn/src/FVW_Registry.txt | 8 ++++ modules/aerodyn/src/FVW_Subs.f90 | 4 ++ modules/aerodyn/src/FVW_Types.f90 | 56 ++++++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index dc1e07d6ad..645e3c9207 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1648,8 +1648,10 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) ! Applying tower shadow to V_wind based on r_wind positions ! NOTE: m%DisturbedInflow also contains tower shadow and we need it for CalcOutput if (p%TwrPotent /= TwrPotent_none .or. p%TwrShadow) then - call TwrInflArray( p, u(tIndx), m, m%FVW%r_wind, m%FVW_u(tIndx)%V_wind, ErrStat, ErrMsg ) - if (ErrStat >= AbortErrLev) return + if (p%FVW%TwrShadowOnWake) then + call TwrInflArray( p, u(tIndx), m, m%FVW%r_wind, m%FVW_u(tIndx)%V_wind, ErrStat, ErrMsg ) + if (ErrStat >= AbortErrLev) return + endif end if endif enddo diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 943b7aa1d9..5290d976ef 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -400,8 +400,11 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%WingRegParam = InputFileData%WingRegParam p%CoreSpreadEddyVisc = InputFileData%CoreSpreadEddyVisc p%ShearModel = InputFileData%ShearModel + p%TreeModel = InputFileData%TreeModel + p%TreeBranchFactor = InputFileData%TreeBranchFactor + p%TreeBranchSmall = InputFileData%TreeBranchSmall p%WrVTK = InputFileData%WrVTK - p%VTKBlades = min(InputFileData%VTKBlades,p%nWings) ! Note: allowing it to be negative for tempoarry hack + p%VTKBlades = min(InputFileData%VTKBlades,p%nWings) ! Note: allowing it to be negative for temporary hack p%VTKCoord = InputFileData%VTKCoord if (allocated(p%PrescribedCirculation)) deallocate(p%PrescribedCirculation) diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index f36f4c217b..ddf9932e1f 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -60,6 +60,14 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar (UnIn,FileName,Inp%WingRegParam ,'WingRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CoreSpreadEddyVisc ,'CoreSpreadEddyVisc','',100.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%ShearModel ,'ShearModel' ,'',idShearNone , ErrStat2,ErrMsg2); if(Failed())return + !CALL ReadVarWDefault(UnIn,FileName,Inp%TwrShadowOnWake ,'TwrShadowOnWake' ,'',.false. , ErrStat2,ErrMsg2); if(Failed())return + !CALL ReadVarWDefault(UnIn,FileName,Inp%TreeModel ,'TreeModel' ,'',idTreeNone , ErrStat2,ErrMsg2); if(Failed())return + !CALL ReadVarWDefault(UnIn,FileName,Inp%TreeBranchFactor ,'TreeBranchFactor' ,'',3.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return + !CALL ReadVarWDefault(UnIn,FileName,Inp%TreeBranchSmall ,'TreeBranchSmall' ,'',0.1_ReKi , ErrStat2,ErrMsg2); if(Failed())return + Inp%TwrShadowOnWake = .False. + Inp%TreeModel = idTreeNone + Inp%TreeBranchFactor = 3.0_ReKi + Inp%TreeBranchSmall = 0.1_ReKi !------------------------ OUTPUT OPTIONS ----------------------------------------- CALL ReadCom (UnIn,FileName, 'Output options header' ,ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'', 0 ,ErrStat2,ErrMsg2); if(Failed())return @@ -77,6 +85,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) if (Check(.not.(ANY(idRegVALID ==Inp%RegFunction )), 'Regularization function (RegFunction) not implemented')) return if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method (WakeRegMethod) not implemented')) return if (Check(.not.(ANY(idShearVALID ==Inp%ShearModel )), 'Shear model (`ShearModel`) not valid')) return + if (Check(.not.(ANY(idTreeVALID ==Inp%TreeModel )), 'Shear model (`ShearModel`) not valid')) return if (Check( Inp%DTfvw < p%DTaero, 'DTfvw must be >= DTaero from AD15.')) return if (abs(Inp%DTfvw-p%DTaero)>epsilon(1.0_ReKi)) then @@ -260,6 +269,10 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) ! False below is to avoid writing the mirrored vorticity, this could be an option though bMirror= (p%ShearModel==idShearMirror) .and. (p%VTKBlades<0) ! NOTE: temporary hack to output mirrored vorticity call CountSegments(p, m%nNW, m%nFW, 1, nSeg, nSegP, nSegNW) + if (bMirror) then + nSeg = 2*nSeg + nSegP = 2*nSegP + endif Filename = TRIM(FileRootName)//'.AllSeg.'//Tstr//'.vtk' CALL WrVTK_Segments(Filename, m%SegPoints(:,1:nSegP), m%SegConnct(:,1:nSeg), m%SegGamma(1:nSeg), m%SegEpsilon(1:nSeg)) diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 65e0bc7edd..262399e656 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -34,6 +34,10 @@ typedef ^ ^ IntKi typedef ^ ^ ReKi WakeRegParam - - - "Initial value of the regularization parameter" typedef ^ ^ ReKi WingRegParam - - - "Regularization parameter of the wing" typedef ^ ^ IntKi ShearModel - - - "Option for shear modelling" +typedef ^ ^ Logical TwrShadowOnWake - - - "Include tower shadow effects on wake" +typedef ^ ^ IntKi TreeModel - - - "Tree calculation method" +typedef ^ ^ ReKi TreeBranchFactor - - - "Factor used to determine if a point is far enough" +typedef ^ ^ ReKi TreeBranchSmall - - - "Distance below which a branch is consisdered small enough" typedef ^ ^ DbKi DTaero - - - "Time interval for calls calculations" s typedef ^ ^ DbKi DTfvw - - - "Time interval for calculating wake induced velocities" s typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s @@ -170,6 +174,10 @@ typedef ^ ^ IntKi typedef ^ ^ ReKi WakeRegParam - - - "Factor used in the regularization " typedef ^ ^ ReKi WingRegParam - - - "Factor used in the regularization " typedef ^ ^ IntKi ShearModel - - - "Option for shear modelling" +typedef ^ ^ Logical TwrShadowOnWake - - - "Include tower shadow effects on wake" +typedef ^ ^ IntKi TreeModel - - - "Tree calculation method" +typedef ^ ^ ReKi TreeBranchFactor - - - "Factor used to determine if a point is far enough" +typedef ^ ^ ReKi TreeBranchSmall - - - "Distance below which a branch is consisdered small enough" typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ DbKi DTvtk - - - "Requested timestep between VTK outputs (calculated from the VTK_fps read in)" s diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index e48f7da367..7bfbd8967a 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -38,6 +38,10 @@ module FVW_SUBS integer(IntKi), parameter :: idShearNone = 0 integer(IntKi), parameter :: idShearMirror = 1 integer(IntKi), parameter, dimension(2) :: idShearVALID = (/idShearNone, idShearMirror /) + ! Tree Model + integer(IntKi), parameter :: idTreeNone = 0 + integer(IntKi), parameter :: idTreeBasic = 1 + integer(IntKi), parameter, dimension(2) :: idTreeVALID = (/idTreeNone, idTreeBasic /) real(ReKi), parameter :: CoreSpreadAlpha = 1.25643 diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index bb29be2501..fdb9533dca 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -61,6 +61,10 @@ MODULE FVW_Types REAL(ReKi) :: WakeRegParam !< Initial value of the regularization parameter [-] REAL(ReKi) :: WingRegParam !< Regularization parameter of the wing [-] INTEGER(IntKi) :: ShearModel !< Option for shear modelling [-] + LOGICAL :: TwrShadowOnWake !< Include tower shadow effects on wake [-] + INTEGER(IntKi) :: TreeModel !< Tree calculation method [-] + REAL(ReKi) :: TreeBranchFactor !< Factor used to determine if a point is far enough [-] + REAL(ReKi) :: TreeBranchSmall !< Distance below which a branch is consisdered small enough [-] REAL(DbKi) :: DTaero !< Time interval for calls calculations [s] REAL(DbKi) :: DTfvw !< Time interval for calculating wake induced velocities [s] REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] @@ -198,6 +202,10 @@ MODULE FVW_Types REAL(ReKi) :: WakeRegParam !< Factor used in the regularization [-] REAL(ReKi) :: WingRegParam !< Factor used in the regularization [-] INTEGER(IntKi) :: ShearModel !< Option for shear modelling [-] + LOGICAL :: TwrShadowOnWake !< Include tower shadow effects on wake [-] + INTEGER(IntKi) :: TreeModel !< Tree calculation method [-] + REAL(ReKi) :: TreeBranchFactor !< Factor used to determine if a point is far enough [-] + REAL(ReKi) :: TreeBranchSmall !< Distance below which a branch is consisdered small enough [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] REAL(DbKi) :: DTvtk !< Requested timestep between VTK outputs (calculated from the VTK_fps read in) [s] @@ -290,6 +298,10 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%WakeRegParam = SrcParamData%WakeRegParam DstParamData%WingRegParam = SrcParamData%WingRegParam DstParamData%ShearModel = SrcParamData%ShearModel + DstParamData%TwrShadowOnWake = SrcParamData%TwrShadowOnWake + DstParamData%TreeModel = SrcParamData%TreeModel + DstParamData%TreeBranchFactor = SrcParamData%TreeBranchFactor + DstParamData%TreeBranchSmall = SrcParamData%TreeBranchSmall DstParamData%DTaero = SrcParamData%DTaero DstParamData%DTfvw = SrcParamData%DTfvw DstParamData%KinVisc = SrcParamData%KinVisc @@ -392,6 +404,10 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_BufSz = Re_BufSz + 1 ! WakeRegParam Re_BufSz = Re_BufSz + 1 ! WingRegParam Int_BufSz = Int_BufSz + 1 ! ShearModel + Int_BufSz = Int_BufSz + 1 ! TwrShadowOnWake + Int_BufSz = Int_BufSz + 1 ! TreeModel + Re_BufSz = Re_BufSz + 1 ! TreeBranchFactor + Re_BufSz = Re_BufSz + 1 ! TreeBranchSmall Db_BufSz = Db_BufSz + 1 ! DTaero Db_BufSz = Db_BufSz + 1 ! DTfvw Re_BufSz = Re_BufSz + 1 ! KinVisc @@ -516,6 +532,14 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%ShearModel Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%TwrShadowOnWake , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%TreeModel + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TreeBranchFactor + Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TreeBranchSmall + Re_Xferred = Re_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTaero Db_Xferred = Db_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTfvw @@ -691,6 +715,14 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + 1 OutData%ShearModel = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%TwrShadowOnWake = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + OutData%TreeModel = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%TreeBranchFactor = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%TreeBranchSmall = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 OutData%DTaero = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 OutData%DTfvw = DbKiBuf( Db_Xferred ) @@ -5504,6 +5536,10 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%WakeRegParam = SrcInputFileData%WakeRegParam DstInputFileData%WingRegParam = SrcInputFileData%WingRegParam DstInputFileData%ShearModel = SrcInputFileData%ShearModel + DstInputFileData%TwrShadowOnWake = SrcInputFileData%TwrShadowOnWake + DstInputFileData%TreeModel = SrcInputFileData%TreeModel + DstInputFileData%TreeBranchFactor = SrcInputFileData%TreeBranchFactor + DstInputFileData%TreeBranchSmall = SrcInputFileData%TreeBranchSmall DstInputFileData%WrVTK = SrcInputFileData%WrVTK DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades DstInputFileData%DTvtk = SrcInputFileData%DTvtk @@ -5579,6 +5615,10 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_BufSz = Re_BufSz + 1 ! WakeRegParam Re_BufSz = Re_BufSz + 1 ! WingRegParam Int_BufSz = Int_BufSz + 1 ! ShearModel + Int_BufSz = Int_BufSz + 1 ! TwrShadowOnWake + Int_BufSz = Int_BufSz + 1 ! TreeModel + Re_BufSz = Re_BufSz + 1 ! TreeBranchFactor + Re_BufSz = Re_BufSz + 1 ! TreeBranchSmall Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades Db_BufSz = Db_BufSz + 1 ! DTvtk @@ -5658,6 +5698,14 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%ShearModel Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%TwrShadowOnWake , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%TreeModel + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TreeBranchFactor + Re_Xferred = Re_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TreeBranchSmall + Re_Xferred = Re_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades @@ -5748,6 +5796,14 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Re_Xferred = Re_Xferred + 1 OutData%ShearModel = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%TwrShadowOnWake = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + OutData%TreeModel = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%TreeBranchFactor = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 + OutData%TreeBranchSmall = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 OutData%WrVTK = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%VTKBlades = IntKiBuf( Int_Xferred ) From 4b822f9b82e76539983bad55cc0cd84fb5738d86 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Mon, 20 Apr 2020 20:11:46 -0600 Subject: [PATCH 131/190] FVW: started UA implementation --- modules/aerodyn/src/AeroDyn.f90 | 10 +- modules/aerodyn/src/FVW.f90 | 138 ++++++- modules/aerodyn/src/FVW_Registry.txt | 16 + modules/aerodyn/src/FVW_Types.f90 | 558 +++++++++++++++++++++++++++ 4 files changed, 719 insertions(+), 3 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 645e3c9207..efb031ca3e 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1178,6 +1178,9 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat if (allocated(OtherState%WakeLocationPoints)) then OtherState%WakeLocationPoints = m%FVW%r_wind endif + ! UA TODO + !call UA_UpdateState_Wrapper(p%AFI, n, p%FVW, x%FVW, xd%FVW, OtherState%FVW, m%FVW, ErrStat2, ErrMsg2) + ! call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif call Cleanup() @@ -2313,6 +2316,11 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m end do end do + ! Unsteady Aero Data + InitInp%UA_Flag = InputFileData%AFAeroMod == AFAeroMod_BL_unsteady + InitInp%UAMod = InputFileData%UAMod + InitInp%Flookup = InputFileData%Flookup + InitInp%a_s = InputFileData%SpdSound ! Copy the mesh over for InitInp to FVW. We would not need to copy this if we decided to break the Framework ! by passing u_AD%BladeMotion directly into FVW_Init, but nothing is really gained by doing that. @@ -2335,7 +2343,7 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, m ENDDO ! NOTE: not passing p%AFI at present. We are not storing it in FVW's parameters. - call FVW_Init( InitInp, u, p%FVW, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat2, ErrMsg2 ) + call FVW_Init(p%AFI, InitInp, u, p%FVW, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat2, ErrMsg2 ) CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! set the size of the input and xd arrays for passing wind info to FVW. diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 5290d976ef..1141595dca 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -38,9 +38,9 @@ module FVW !> This routine is called at the start of the simulation to perform initialization steps. !! The parameters are set here and not changed during the simulation. !! The initial states and initial guess for the input are defined. -subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat, ErrMsg ) -!.................................................................................................................................. +subroutine FVW_Init(AFInfo, InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat, ErrMsg ) use OMP_LIB ! wrap with #ifdef _OPENMP if this causes an issue + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data, temporary, for UA.. type(FVW_InitInputType), intent(inout) :: InitInp !< Input data for initialization routine (inout so we can use MOVE_ALLOC) type(FVW_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined type(FVW_ParameterType), intent( out) :: p !< Parameters @@ -150,6 +150,11 @@ subroutine FVW_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu CALL SetRequestedWindPoints(m%r_wind, x, p, m ) ! Return anything in FVW_InitOutput that should be passed back to the calling code here + + ! --- UA + ! NOTE: quick and dirty since this should belong to AD + call UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, ErrStat2, ErrMsg2); if (Failed()) return + if (DEV_VERSION) then CALL FVW_RunTests(ErrStat2, ErrMsg2); if (Failed()) return endif @@ -604,6 +609,8 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call FVW_CalcConstrStateResidual(t+p%DTaero, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 2); if(Failed()) return ! print*,'US: z_Gamma',x%Gamma_NW(1,1,1) ! print*,'US: x_Gamma',z%Gamma_LL(1,1) + ! TODO UA + call UA_UpdateState_Wrapper(AFInfo, n, p, x, xd, OtherState, m, ErrStat2, ErrMsg2) ! Updating circulation of near wake panel (and position but irrelevant) ! Changes: x only @@ -981,4 +988,131 @@ end function lin_extrap end subroutine FVW_CalcOutput + +!---------------------------------------------------------------------------------------------------------------------------------- +! --- UA related, should be merged with AeroDyn +!---------------------------------------------------------------------------------------------------------------------------------- +subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, ErrStat, ErrMsg ) + use UnsteadyAero, only: UA_Init, UA_TurnOff_param + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data, temporary, for UA.. + type(FVW_InitInputType), intent(inout) :: InitInp !< Input data for initialization routine (inout so we can use MOVE_ALLOC) + real(DbKi), intent(inout) :: interval !< time interval + type(FVW_ParameterType), intent(inout) :: p !< Parameters + type(FVW_ContinuousStateType), intent(inout) :: x !< Initial continuous states + type(FVW_DiscreteStateType), intent(inout) :: xd !< Initial discrete states + type(FVW_OtherStateType), intent(inout) :: OtherState !< Initial other states + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! + type(UA_InitInputType) :: Init_UA_Data + type(UA_InputType) :: u_UA + type(UA_InitOutputType):: InitOutData_UA + integer :: i,j + integer(intKi) :: ErrStat2 ! temporary Error status + character(ErrMsgLen) :: ErrMsg2 + ErrStat = ErrID_None + ErrMsg = "" + + m%UA_Flag=InitInp%UA_Flag + if ( m%UA_Flag ) then + ! ---Condensed version of "BEMT_Set_UA_InitData" + allocate(Init_UA_Data%c(InitInp%numBladeNodes,InitInp%numBlades), STAT = errStat2) + do j = 1,InitInp%NumBlades; do i = 1,InitInp%numBladeNodes; + Init_UA_Data%c(i,j) = p%chord(i,j) ! NOTE: InitInp chord move-allocd to p + end do; end do + Init_UA_Data%dt = interval + Init_UA_Data%OutRootName = '' + Init_UA_Data%numBlades = InitInp%NumBlades + Init_UA_Data%nNodesPerBlade = InitInp%numBladeNodes + Init_UA_Data%NumOuts = 0 + Init_UA_Data%UAMod = InitInp%UAMod + Init_UA_Data%Flookup = InitInp%Flookup + Init_UA_Data%a_s = InitInp%a_s ! m/s + ! --- UA init + call UA_Init( Init_UA_Data, u_UA, m%p_UA, xd%UA, OtherState%UA, m%y_UA, m%m_UA, interval, InitOutData_UA, ErrStat2, ErrMsg2); if(Failed())return + ! --- BEMT Init other state + allocate ( OtherState%UA_Flag( InitInp%numBladeNodes, InitInp%NumBlades ), STAT = ErrStat2 ) + ! --- Condensed version of "BEMT_CheckInitUA" + do j = 1,InitInp%numBlades; do i = 1,InitInp%numBladeNodes; ! Loop over blades and nodes + call UA_TurnOff_param(AFInfo(p%AFindx(i,j)), ErrStat2, ErrMsg2) + if (ErrStat2 /= ErrID_None) then + call WrScr( 'Warning: Turning off Unsteady Aerodynamics because '//trim(ErrMsg2)//' BladeNode = '//trim(num2lstr(i))//', Blade = '//trim(num2lstr(j)) ) + OtherState%UA_Flag(i,j) = .false. + end if + end do; end do; + call UA_DestroyInput( u_UA, ErrStat2, ErrMsg2 ); if(Failed())return + call UA_DestroyInitInput( Init_UA_Data, ErrStat2, ErrMsg2 ); if(Failed())return + call UA_DestroyInitOutput( InitOutData_UA, ErrStat2, ErrMsg2 ); if(Failed())return + endif +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'UA_Init_Wrapper') + Failed = ErrStat >= AbortErrLev + end function Failed +end subroutine UA_Init_Wrapper + +subroutine UA_UpdateState_Wrapper(AFInfo, n, p, x, xd, OtherState, m, ErrStat, ErrMsg ) + use UnsteadyAero, only: UA_UpdateStates, UA_TurnOff_input + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data, temporary, for UA.. + integer(IntKi), intent(in ) :: n !< time step + type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_ContinuousStateType), intent(inout) :: x !< Initial continuous states + type(FVW_DiscreteStateType), intent(inout) :: xd !< Initial discrete states + type(FVW_OtherStateType), intent(inout) :: OtherState !< Initial other states + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ! Local + type(UA_InputType) :: u_UA + integer :: i,j + integer(intKi) :: ErrStat2 ! temporary Error status + character(ErrMsgLen) :: ErrMsg2 + ErrStat = ErrID_None + ErrMsg = "" + ! --- Condensed version of BEMT_Update States + if (m%UA_Flag) then + do j = 1,p%nWings + do i = 1,p%nSpan+1 + ! We only update the UnsteadyAero states if we have unsteady aero turned on for this node + if (OtherState%UA_Flag(i,j) .and. n > 0) then + !! ....... compute inputs to UA ........... + m%m_UA%iBladeNode = i + m%m_UA%iBlade = j + !phitemp = z%phi(i,j) + !u_UA%alpha = phitemp - u1%theta(i,j) ! angle of attack + !u_UA%UserProp = u1%UserProp(i,j) + !u_UA%Re = ... + !u_UA%U = ... + ! Need to compute local velocity including both axial and tangential induction + ! COMPUTE: u_UA%U, u_UA%Re + !call BEMTU_Wind( m%axInduction(i,j), m%tanInduction(i,j), u1%Vx(i,j), u1%Vy(i,j), p%chord(i,j), p%kinVisc, u_UA%Re, u_UA%U) + !! ....... check inputs to UA ........... + call UA_TurnOff_input(AFInfo(p%AFIndx(i,j)), u_UA, ErrStat2, ErrMsg2) + if (ErrStat2 /= ErrID_None) then + OtherState%UA_Flag(i,j) = .FALSE. + call WrScr( 'Warning: Turning off Unsteady Aerodynamics due to '//trim(ErrMsg2)//' BladeNode = '//trim(num2lstr(i))//', Blade = '//trim(num2lstr(j)) ) + else + ! COMPUTE: xd%UA, OtherState%UA + call UA_UpdateStates( i, j, u_UA, m%p_UA, xd%UA, OtherState%UA, AFInfo(p%AFIndx(i,j)), m%m_UA, ErrStat2, ErrMsg2 ) + if (ErrStat2 /= ErrID_None) then + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'UA_UpdateState_Wrapper'//trim(NodeText(i,j))) + if (ErrStat >= AbortErrLev) return + end if + end if + end if ! if (OtherState%UA_Flag(i,j)) then + end do + end do + call UA_DestroyInput( u_UA, ErrStat2, ErrMsg2 ); + endif + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'UA_UpdateState_Wrapper') +contains + function NodeText(i,j) + integer(IntKi), intent(in) :: i ! node number + integer(IntKi), intent(in) :: j ! blade number + character(25) :: NodeText + NodeText = '(node '//trim(num2lstr(i))//', blade '//trim(num2lstr(j))//')' + end function NodeText +end subroutine UA_UpdateState_Wrapper + end module FVW diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 262399e656..a2f2ba2008 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -5,6 +5,7 @@ ################################################################################################################################## include Registry_NWTC_Library.txt usefrom AirfoilInfo_Registry.txt +usefrom UnsteadyAero_Registry.txt ##################### Registry for FVW ############### # ..... PARAMETERS ............. @@ -51,6 +52,9 @@ typedef ^ ^ CHARACTER(1024) # ....... OtherStateType ............ # FVW_OtherStateType typedef FVW/FVW OtherStateType IntKi NULL - - - "Number of active near wake panels" - +# TODO UA +typedef ^ ^ UA_OtherStateType UA - - - "other states for UnsteadyAero" - +typedef ^ ^ Logical UA_Flag {:}{:} - - "logical flag indicating whether to use UnsteadyAero" - # ....... MiscVars ............ # FVW_MiscVarType @@ -100,6 +104,11 @@ typedef ^ ^ ReKi # Wake rollup storage (buffer) typedef ^ ^ ReKi CPs :: - - "Control points used for wake rollup computation" - typedef ^ ^ ReKi Uind :: - - "Induced velocities obtained at control points" - +# TODO UA - Should be part of AeroDyn +typedef ^ ^ UA_MiscVarType m_UA - - - "misc vars for UnsteadyAero" - +typedef ^ ^ UA_OutputType y_UA - - - "outputs from UnsteadyAero" - +typedef ^ ^ UA_ParameterType p_UA - - - "parameters for UnsteadyAero" - +typedef ^ ^ LOGICAL UA_Flag - - - "logical flag indicating whether to use UnsteadyAero" - # ........ Input ............ # FVW_InputType @@ -124,6 +133,8 @@ typedef ^ ^ ReKi #.......... DiscreteStateType ...... # FVW_DiscreteStateType typedef FVW/FVW DiscreteStateType ReKi NULL - - - "Empty to satisfy framework" - +# TODO UA +typedef ^ ^ UA_DiscreteStateType UA - - - "states for UnsteadyAero" - #.......... ConstraintStateType ...... # FVW_ConstraintStateType @@ -147,6 +158,11 @@ typedef ^ ^ IntKi typedef ^ ^ IntKi NumBladeNodes - - - "Number of nodes on each blade" - typedef ^ ^ DbKi DTaero - - - "Time interval for calls (from AD15)" s typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s +# TODO UA - Should be part of AeroDyn +typedef ^ ^ IntKi UAMod - - - "Model for the dynamic stall equations [1 = Leishman/Beddoes, 2 = Gonzalez, 3 = Minnema]" - +typedef ^ ^ LOGICAL UA_Flag - - - "logical flag indicating whether to use UnsteadyAero" - +typedef ^ ^ LOGICAL Flookup - - - "Use table lookup for f' and f'' " - +typedef ^ ^ ReKi a_s - - - "speed of sound" m/s #.......... InputFileType ...... # FVW_InputFile diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index fdb9533dca..e5f25ac9f5 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -32,6 +32,7 @@ MODULE FVW_Types !--------------------------------------------------------------------------------------------------------------------------------- USE AirfoilInfo_Types +USE UnsteadyAero_Types USE NWTC_Library IMPLICIT NONE ! ========= FVW_ParameterType ======= @@ -78,6 +79,8 @@ MODULE FVW_Types ! ========= FVW_OtherStateType ======= TYPE, PUBLIC :: FVW_OtherStateType INTEGER(IntKi) :: NULL !< Number of active near wake panels [-] + TYPE(UA_OtherStateType) :: UA !< other states for UnsteadyAero [-] + LOGICAL , DIMENSION(:,:), ALLOCATABLE :: UA_Flag !< logical flag indicating whether to use UnsteadyAero [-] END TYPE FVW_OtherStateType ! ======================= ! ========= FVW_MiscVarType ======= @@ -124,6 +127,10 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: SegEpsilon !< Segment regularization parameter [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: CPs !< Control points used for wake rollup computation [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Uind !< Induced velocities obtained at control points [-] + TYPE(UA_MiscVarType) :: m_UA !< misc vars for UnsteadyAero [-] + TYPE(UA_OutputType) :: y_UA !< outputs from UnsteadyAero [-] + TYPE(UA_ParameterType) :: p_UA !< parameters for UnsteadyAero [-] + LOGICAL :: UA_Flag !< logical flag indicating whether to use UnsteadyAero [-] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -151,6 +158,7 @@ MODULE FVW_Types ! ========= FVW_DiscreteStateType ======= TYPE, PUBLIC :: FVW_DiscreteStateType REAL(ReKi) :: NULL !< Empty to satisfy framework [-] + TYPE(UA_DiscreteStateType) :: UA !< states for UnsteadyAero [-] END TYPE FVW_DiscreteStateType ! ======================= ! ========= FVW_ConstraintStateType ======= @@ -175,6 +183,10 @@ MODULE FVW_Types INTEGER(IntKi) :: NumBladeNodes !< Number of nodes on each blade [-] REAL(DbKi) :: DTaero !< Time interval for calls (from AD15) [s] REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] + INTEGER(IntKi) :: UAMod !< Model for the dynamic stall equations [1 = Leishman/Beddoes, 2 = Gonzalez, 3 = Minnema] [-] + LOGICAL :: UA_Flag !< logical flag indicating whether to use UnsteadyAero [-] + LOGICAL :: Flookup !< Use table lookup for f' and f'' [-] + REAL(ReKi) :: a_s !< speed of sound [m/s] END TYPE FVW_InitInputType ! ======================= ! ========= FVW_InputFile ======= @@ -751,6 +763,8 @@ SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, E 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyOtherState' @@ -758,6 +772,23 @@ SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, E ErrStat = ErrID_None ErrMsg = "" DstOtherStateData%NULL = SrcOtherStateData%NULL + CALL UA_CopyOtherState( SrcOtherStateData%UA, DstOtherStateData%UA, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN +IF (ALLOCATED(SrcOtherStateData%UA_Flag)) THEN + i1_l = LBOUND(SrcOtherStateData%UA_Flag,1) + i1_u = UBOUND(SrcOtherStateData%UA_Flag,1) + i2_l = LBOUND(SrcOtherStateData%UA_Flag,2) + i2_u = UBOUND(SrcOtherStateData%UA_Flag,2) + IF (.NOT. ALLOCATED(DstOtherStateData%UA_Flag)) THEN + ALLOCATE(DstOtherStateData%UA_Flag(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%UA_Flag.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%UA_Flag = SrcOtherStateData%UA_Flag +ENDIF END SUBROUTINE FVW_CopyOtherState SUBROUTINE FVW_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) @@ -769,6 +800,10 @@ SUBROUTINE FVW_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" + CALL UA_DestroyOtherState( OtherStateData%UA, ErrStat, ErrMsg ) +IF (ALLOCATED(OtherStateData%UA_Flag)) THEN + DEALLOCATE(OtherStateData%UA_Flag) +ENDIF END SUBROUTINE FVW_DestroyOtherState SUBROUTINE FVW_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -807,6 +842,29 @@ SUBROUTINE FVW_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Db_BufSz = 0 Int_BufSz = 0 Int_BufSz = Int_BufSz + 1 ! NULL + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + Int_BufSz = Int_BufSz + 3 ! UA: size of buffers for each call to pack subtype + CALL UA_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%UA, ErrStat2, ErrMsg2, .TRUE. ) ! UA + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! UA + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! UA + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! UA + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 1 ! UA_Flag allocated yes/no + IF ( ALLOCATED(InData%UA_Flag) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! UA_Flag upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%UA_Flag) ! UA_Flag + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -836,6 +894,50 @@ SUBROUTINE FVW_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%NULL Int_Xferred = Int_Xferred + 1 + CALL UA_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%UA, ErrStat2, ErrMsg2, OnlySize ) ! UA + 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 + IF ( .NOT. ALLOCATED(InData%UA_Flag) ) 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%UA_Flag,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_Flag,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%UA_Flag,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_Flag,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%UA_Flag)>0) IntKiBuf ( Int_Xferred:Int_Xferred+SIZE(InData%UA_Flag)-1 ) = TRANSFER(PACK( InData%UA_Flag ,.TRUE.), IntKiBuf(1), SIZE(InData%UA_Flag)) + Int_Xferred = Int_Xferred + SIZE(InData%UA_Flag) + END IF END SUBROUTINE FVW_PackOtherState SUBROUTINE FVW_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -857,6 +959,8 @@ SUBROUTINE FVW_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E LOGICAL, ALLOCATABLE :: mask3(:,:,:) LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackOtherState' @@ -872,6 +976,72 @@ SUBROUTINE FVW_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E Int_Xferred = 1 OutData%NULL = 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 UA_UnpackOtherState( Re_Buf, Db_Buf, Int_Buf, OutData%UA, ErrStat2, ErrMsg2 ) ! UA + 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) + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UA_Flag 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 + IF (ALLOCATED(OutData%UA_Flag)) DEALLOCATE(OutData%UA_Flag) + ALLOCATE(OutData%UA_Flag(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UA_Flag.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%UA_Flag)>0) OutData%UA_Flag = UNPACK( TRANSFER( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%UA_Flag))-1 ), OutData%UA_Flag), mask2,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(OutData%UA_Flag) + DEALLOCATE(mask2) + END IF END SUBROUTINE FVW_UnPackOtherState SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) @@ -1409,6 +1579,16 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) END IF DstMiscData%Uind = SrcMiscData%Uind ENDIF + CALL UA_CopyMisc( SrcMiscData%m_UA, DstMiscData%m_UA, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL UA_CopyOutput( SrcMiscData%y_UA, DstMiscData%y_UA, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + CALL UA_CopyParam( SrcMiscData%p_UA, DstMiscData%p_UA, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN + DstMiscData%UA_Flag = SrcMiscData%UA_Flag END SUBROUTINE FVW_CopyMisc SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -1519,6 +1699,9 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%Uind)) THEN DEALLOCATE(MiscData%Uind) ENDIF + CALL UA_DestroyMisc( MiscData%m_UA, ErrStat, ErrMsg ) + CALL UA_DestroyOutput( MiscData%y_UA, ErrStat, ErrMsg ) + CALL UA_DestroyParam( MiscData%p_UA, ErrStat, ErrMsg ) END SUBROUTINE FVW_DestroyMisc SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -1730,6 +1913,59 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*2 ! Uind upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%Uind) ! Uind END IF + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + Int_BufSz = Int_BufSz + 3 ! m_UA: size of buffers for each call to pack subtype + CALL UA_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%m_UA, ErrStat2, ErrMsg2, .TRUE. ) ! m_UA + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! m_UA + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! m_UA + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! m_UA + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! y_UA: size of buffers for each call to pack subtype + CALL UA_PackOutput( Re_Buf, Db_Buf, Int_Buf, InData%y_UA, ErrStat2, ErrMsg2, .TRUE. ) ! y_UA + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! y_UA + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! y_UA + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! y_UA + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 3 ! p_UA: size of buffers for each call to pack subtype + CALL UA_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%p_UA, ErrStat2, ErrMsg2, .TRUE. ) ! p_UA + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! p_UA + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! p_UA + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! p_UA + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 1 ! UA_Flag IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -2372,6 +2608,92 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%Uind)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Uind))-1 ) = PACK(InData%Uind,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%Uind) END IF + CALL UA_PackMisc( Re_Buf, Db_Buf, Int_Buf, InData%m_UA, ErrStat2, ErrMsg2, OnlySize ) ! m_UA + 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 + CALL UA_PackOutput( Re_Buf, Db_Buf, Int_Buf, InData%y_UA, ErrStat2, ErrMsg2, OnlySize ) ! y_UA + 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 + CALL UA_PackParam( Re_Buf, Db_Buf, Int_Buf, InData%p_UA, ErrStat2, ErrMsg2, OnlySize ) ! p_UA + 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 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UA_Flag , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3355,6 +3677,128 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%Uind) DEALLOCATE(mask2) END IF + 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 UA_UnpackMisc( Re_Buf, Db_Buf, Int_Buf, OutData%m_UA, ErrStat2, ErrMsg2 ) ! m_UA + 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) + 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 UA_UnpackOutput( Re_Buf, Db_Buf, Int_Buf, OutData%y_UA, ErrStat2, ErrMsg2 ) ! y_UA + 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) + 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 UA_UnpackParam( Re_Buf, Db_Buf, Int_Buf, OutData%p_UA, ErrStat2, ErrMsg2 ) ! p_UA + 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) + OutData%UA_Flag = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) @@ -4444,6 +4888,9 @@ SUBROUTINE FVW_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrS ErrStat = ErrID_None ErrMsg = "" DstDiscStateData%NULL = SrcDiscStateData%NULL + CALL UA_CopyDiscState( SrcDiscStateData%UA, DstDiscStateData%UA, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN END SUBROUTINE FVW_CopyDiscState SUBROUTINE FVW_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) @@ -4455,6 +4902,7 @@ SUBROUTINE FVW_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" + CALL UA_DestroyDiscState( DiscStateData%UA, ErrStat, ErrMsg ) END SUBROUTINE FVW_DestroyDiscState SUBROUTINE FVW_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -4493,6 +4941,24 @@ SUBROUTINE FVW_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Db_BufSz = 0 Int_BufSz = 0 Re_BufSz = Re_BufSz + 1 ! NULL + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + Int_BufSz = Int_BufSz + 3 ! UA: size of buffers for each call to pack subtype + CALL UA_PackDiscState( Re_Buf, Db_Buf, Int_Buf, InData%UA, ErrStat2, ErrMsg2, .TRUE. ) ! UA + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! UA + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! UA + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! UA + 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 @@ -4522,6 +4988,34 @@ SUBROUTINE FVW_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%NULL Re_Xferred = Re_Xferred + 1 + CALL UA_PackDiscState( Re_Buf, Db_Buf, Int_Buf, InData%UA, ErrStat2, ErrMsg2, OnlySize ) ! UA + 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 FVW_PackDiscState SUBROUTINE FVW_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4558,6 +5052,46 @@ SUBROUTINE FVW_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = 1 OutData%NULL = 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 UA_UnpackDiscState( Re_Buf, Db_Buf, Int_Buf, OutData%UA, ErrStat2, ErrMsg2 ) ! UA + 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 FVW_UnPackDiscState SUBROUTINE FVW_CopyConstrState( SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg ) @@ -4889,6 +5423,10 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS DstInitInputData%NumBladeNodes = SrcInitInputData%NumBladeNodes DstInitInputData%DTaero = SrcInitInputData%DTaero DstInitInputData%KinVisc = SrcInitInputData%KinVisc + DstInitInputData%UAMod = SrcInitInputData%UAMod + DstInitInputData%UA_Flag = SrcInitInputData%UA_Flag + DstInitInputData%Flookup = SrcInitInputData%Flookup + DstInitInputData%a_s = SrcInitInputData%a_s END SUBROUTINE FVW_CopyInitInput SUBROUTINE FVW_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -5029,6 +5567,10 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! NumBladeNodes Db_BufSz = Db_BufSz + 1 ! DTaero Re_BufSz = Re_BufSz + 1 ! KinVisc + Int_BufSz = Int_BufSz + 1 ! UAMod + Int_BufSz = Int_BufSz + 1 ! UA_Flag + Int_BufSz = Int_BufSz + 1 ! Flookup + Re_BufSz = Re_BufSz + 1 ! a_s IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -5216,6 +5758,14 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Db_Xferred = Db_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%KinVisc Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%UAMod + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UA_Flag , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%Flookup , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 + ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%a_s + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_PackInitInput SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -5497,6 +6047,14 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Db_Xferred = Db_Xferred + 1 OutData%KinVisc = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 + OutData%UAMod = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 + OutData%UA_Flag = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + OutData%Flookup = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 + OutData%a_s = ReKiBuf( Re_Xferred ) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE FVW_UnPackInitInput SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrStat, ErrMsg ) From 26b2533c12456f4cfa25bfb57b918310d502dcb5 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 21 Apr 2020 18:04:12 -0600 Subject: [PATCH 132/190] FVW: integrated UA with FVW, needs further checking --- modules/aerodyn/src/AeroDyn.f90 | 67 ++++++-- modules/aerodyn/src/AeroDyn_IO.f90 | 55 ++++--- modules/aerodyn/src/BEMT.f90 | 2 +- modules/aerodyn/src/FVW.f90 | 118 ++++++++------ modules/aerodyn/src/FVW_Registry.txt | 6 +- modules/aerodyn/src/FVW_Subs.f90 | 24 ++- modules/aerodyn/src/FVW_Types.f90 | 205 +++++++++++++++++++++++- modules/aerodyn/src/FVW_VortexTools.f90 | 36 ++++- modules/aerodyn/src/FVW_Wings.f90 | 10 +- 9 files changed, 427 insertions(+), 96 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index efb031ca3e..c1738fbfb2 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -168,6 +168,12 @@ subroutine AD_SetInitOut(p, InputFileData, InitOut, errStat, errMsg) InitOut%WriteOutputUnt( m + 26 ) = ' (m/s) ' InitOut%WriteOutputHdr( m + 27 ) = ' '//trim(chanPrefix)//"Uir" InitOut%WriteOutputUnt( m + 27 ) = ' (m/s) ' + InitOut%WriteOutputHdr( m + 28 ) = ' '//trim(chanPrefix)//"Clst" + InitOut%WriteOutputUnt( m + 28 ) = ' (-) ' + InitOut%WriteOutputHdr( m + 29 ) = ' '//trim(chanPrefix)//"Cdst" + InitOut%WriteOutputUnt( m + 29 ) = ' (-) ' + InitOut%WriteOutputHdr( m + 30 ) = ' '//trim(chanPrefix)//"Cmst" + InitOut%WriteOutputUnt( m + 30 ) = ' (-) ' end do end do @@ -1021,7 +1027,7 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) !p%RootName = TRIM(InitInp%RootName)//'.AD' ! set earlier to it could be used #ifdef DBG_OUTS - p%NBlOuts = 27 + p%NBlOuts = 30 p%numOuts = p%NumBlNds*p%NumBlades*p%NBlOuts p%NTwOuts = 0 @@ -1228,6 +1234,11 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" +#ifdef UA_OUTS + ! if ( mod(REAL(t,ReKi),.1) < p%dt) then + if (allocated(m%FVW%y_UA%WriteOutput)) m%FVW%y_UA%WriteOutput = 0.0 !reset to zero in case UA shuts off mid-simulation +#endif + call SetInputs(p, u, m, indx, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1249,7 +1260,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, p%AFI, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - call SetOutputsFromFVW( u, p, m, y, ErrStat2, ErrMsg2 ) + call SetOutputsFromFVW( u, p, OtherState, xd, m, y, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) endif @@ -1286,9 +1297,9 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) !------------------------------------------------------- if (p%NumOuts > 0) then #ifdef DBG_OUTS - call Calc_WriteDbgOutput( p, u, m, y, ErrStat2, ErrMsg2 ) + call Calc_WriteDbgOutput( p, u, m, y, OtherState, xd, ErrStat2, ErrMsg2 ) #else - call Calc_WriteOutput( p, u, m, y, OtherState, indx, ErrStat2, ErrMsg2 ) + call Calc_WriteOutput( p, u, m, y, OtherState, xd, indx, ErrStat2, ErrMsg2 ) #endif call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1307,6 +1318,12 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) end if +#ifdef UA_OUTS + ! if ( mod(REAL(t,ReKi),.1) < p%dt) then + if (allocated(m%FVW%y_UA%WriteOutput)) & + WRITE (69, '(F20.6,'//trim(num2lstr(size(m%FVW%y_UA%WriteOutput)))//'(:,1x,ES19.5E3))') t, ( m%FVW%y_UA%WriteOutput(i), i=1,size(m%FVW%y_UA%WriteOutput)) + ! end if +#endif end subroutine AD_CalcOutput @@ -1658,6 +1675,7 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) end if endif enddo + m%FVW%Vwnd_ND = m%DisturbedInflow ! Nasty transfer for UA, but this is temporary, waiting for AeroDyn to handle UA end subroutine SetInputsForFVW !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine converts outputs from BEMT (stored in m%BEMT_y) into values on the AeroDyn BladeLoad output mesh. @@ -1706,13 +1724,16 @@ end subroutine SetOutputsFromBEMT !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine converts outputs from FVW (stored in m%FVW_y) into values on the AeroDyn BladeLoad output mesh. -subroutine SetOutputsFromFVW(u, p, m, y, ErrStat, ErrMsg) - TYPE(AD_InputType), intent(in ) :: u !< Inputs at Time t - type(AD_ParameterType), intent(in ) :: p !< AD parameters - type(AD_OutputType), intent(inout) :: y !< AD outputs - type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables - integer(IntKi), intent( out) :: ErrStat !< Error status of the operation - character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None +subroutine SetOutputsFromFVW(u, p, OtherState, xd, m, y, ErrStat, ErrMsg) + use BEMTUnCoupled, only: Compute_UA_AirfoilCoefs + TYPE(AD_InputType), intent(in ) :: u !< Inputs at Time t + type(AD_ParameterType), intent(in ) :: p !< AD parameters + type(AD_OtherStateType), intent(in ) :: OtherState !< OtherState + type(AD_DiscreteStateType),intent(in ) :: xd !< Discrete states + type(AD_OutputType), intent(inout) :: y !< AD outputs + type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None integer(intKi) :: j ! loop counter for nodes integer(intKi) :: k ! loop counter for blades @@ -1724,7 +1745,7 @@ subroutine SetOutputsFromFVW(u, p, m, y, ErrStat, ErrMsg) real(ReKi) :: AxInd, TanInd, Vrel, phi, alpha, Re, theta type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table real(ReKi) :: UrelWind_s(3) ! Relative wind (wind+str) in section coords - real(ReKi) :: Cx, Cy + real(ReKi) :: Cx, Cy, Cl_dyn, Cd_dyn, Cm_dyn integer(intKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 @@ -1742,17 +1763,33 @@ subroutine SetOutputsFromFVW(u, p, m, y, ErrStat, ErrMsg) AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') ! NOTE: using airfoil coeffs at nodes + + ! Compute steady Airfoil Coefs no matter what.. call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) + Cl_dyn = AFI_interp%Cl + Cd_dyn = AFI_interp%Cd + Cm_dyn = AFI_interp%Cm + if (m%FVW%UA_Flag) then + if ((OtherState%FVW%UA_Flag(j,k)) .and. ( .not. EqualRealNos(Vrel,0.0_ReKi) ) ) then + m%FVW%m_UA%iBladeNode = j + m%FVW%m_UA%iBlade = k + call Compute_UA_AirfoilCoefs( alpha, Vrel, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), m%FVW%p_UA, xd%FVW%UA, OtherState%FVW%UA, m%FVW%y_UA, m%FVW%m_UA, Cl_dyn, Cd_dyn, Cm_dyn, ErrStat, ErrMsg) + if(ErrStat/=ErrID_None) print*,'UA CalcOutput:', trim(ErrMsg) + end if + end if + + + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') cp = cos(phi) sp = sin(phi) - Cx = AFI_interp%Cl*cp + AFI_interp%Cd*sp - Cy = AFI_interp%Cl*sp - AFI_interp%Cd*cp + Cx = Cl_dyn*cp + Cd_dyn*sp + Cy = Cl_dyn*sp - Cd_dyn*cp q = 0.5 * p%airDens * Vrel**2 ! dynamic pressure of the jth node in the kth blade force(1) = Cx * q * p%FVW%Chord(j,k) ! X = normal force per unit length (normal to the plane, not chord) of the jth node in the kth blade force(2) = -Cy * q * p%FVW%Chord(j,k) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade - moment(3)= AFI_interp%Cm * q * p%FVW%Chord(j,k)**2 ! M = pitching moment per unit length of the jth node in the kth blade + moment(3)= Cm_dyn * q * p%FVW%Chord(j,k)**2 ! M = pitching moment per unit length of the jth node in the kth blade ! save these values for possible output later: m%X(j,k) = force(1) diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index 8bd65bf241..77fb308c7a 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1559,12 +1559,14 @@ END FUNCTION Calc_Chi0 !---------------------------------------------------------------------------------------------------------------------------------- -SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) +SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, OtherState, xd, ErrStat, ErrMsg ) TYPE(AD_ParameterType), INTENT(IN ) :: p ! The module parameters TYPE(AD_InputType), INTENT(IN ) :: u ! inputs TYPE(AD_MiscVarType), INTENT(INOUT) :: m ! misc variables TYPE(AD_OutputType), INTENT(IN ) :: y ! outputs + TYPE(AD_OtherStateType), INTENT(IN ) :: OtherState ! OtherState + TYPE(AD_DiscreteStateType),INTENT(IN ) :: xd ! Discrete states INTEGER(IntKi), INTENT( OUT) :: ErrStat ! The error status code CHARACTER(*), INTENT( OUT) :: ErrMsg ! The error message, if an error occurred @@ -1657,18 +1659,12 @@ SUBROUTINE Calc_WriteDbgOutput( p, u, m, y, ErrStat, ErrMsg ) end do ! nodes end do ! blades else ! (p%WakeMod == WakeMod_FVW) - call calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) + call calc_WriteDbgOutputFVW() endif contains - subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) - TYPE(AD_ParameterType), INTENT(IN ) :: p ! The module parameters - TYPE(AD_InputType), INTENT(IN ) :: u ! inputs - TYPE(AD_MiscVarType), INTENT(INOUT) :: m ! misc variables - TYPE(AD_OutputType), INTENT(IN ) :: y ! outputs - INTEGER(IntKi), INTENT( OUT) :: ErrStat ! The error status code - CHARACTER(*), INTENT( OUT) :: ErrMsg ! The error message, if an error occurred - + subroutine Calc_WriteDbgOutputFVW() + use BEMTUnCoupled, only: Compute_UA_AirfoilCoefs ! local variables integer, parameter :: indx = 1 ! m%BEMT_u(1) is at t; m%BEMT_u(2) is t+dt CHARACTER(*), PARAMETER :: RoutineName = 'Calc_WriteOutput' @@ -1681,7 +1677,7 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) type(AFI_OutputType) :: AFI_interp ! Resulting values from lookup table real(ReKi) :: UrelWind_s(3) ! Wind in section coords real(ReKi) :: Vwnd(3) - real(ReKi) :: Cx, Cy + real(ReKi) :: Cx, Cy, Cl_dyn, Cd_dyn, Cm_dyn ! Transformation matrices real(R8Ki), dimension(3,3) :: M_cg ! from global to airfoil-chord (this is well defined, also called "n-t" system in AeroDyn) real(ReKi), dimension(3,3) :: M_sg ! from global to section (this is ill-defined, also called "x-y" system in AeroDyn), this coordinate is used to define the "axial" and "tangential" inductions @@ -1711,8 +1707,21 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) call FVW_AeroOuts(M_sg, M_cg, m%FVW%PitchAndTwist(j,k), u%BladeMotion(k)%TranslationVel(1:3,j), & m%FVW_y%Vind(1:3,j,k), Vwnd, p%KinVisc, p%FVW%Chord(j,k), & AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s, ErrStat, ErrMsg ) - ! NOTE: using airfoil coeffs at nodes + + ! NOTE: using airfoil coeffs at nodes + ! Compute steady Airfoil Coefs first call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) + Cl_dyn = AFI_interp%Cl + Cd_dyn = AFI_interp%Cd + Cm_dyn = AFI_interp%Cm + + if ( m%FVW%UA_Flag) then ! Unsteady coeffs + if ((OtherState%FVW%UA_Flag(j,k)) .and. ( .not. EqualRealNos(Vrel,0.0_ReKi) ) ) then + m%FVW%m_UA%iBladeNode = j + m%FVW%m_UA%iBlade = k + call Compute_UA_AirfoilCoefs( alpha, Vrel, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), m%FVW%p_UA, xd%FVW%UA, OtherState%FVW%UA, m%FVW%y_UA, m%FVW%m_UA, Cl_dyn, Cd_dyn, Cm_dyn, ErrStat, ErrMsg) + end if + end if theta = m%FVW%PitchAndTwist(j,k) @@ -1730,11 +1739,11 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) cp = cos(phi) sp = sin(phi) - Cx = AFI_interp%Cl*cp + AFI_interp%Cd*sp - Cy = AFI_interp%Cl*sp - AFI_interp%Cd*cp - m%AllOuts( i+9 ) = AFI_interp%Cl ! "Cl" - m%AllOuts( i+10 ) = AFI_interp%Cd ! "Cd" - m%AllOuts( i+11 ) = AFI_interp%Cm ! "Cm" + Cx = Cl_dyn*cp + Cd_dyn*sp + Cy = Cl_dyn*sp - Cd_dyn*cp + m%AllOuts( i+9 ) = Cl_dyn ! "Cl" + m%AllOuts( i+10 ) = Cd_dyn ! "Cd" + m%AllOuts( i+11 ) = Cm_dyn ! "Cm" m%AllOuts( i+12 ) = Cx ! "Cx" m%AllOuts( i+13 ) = Cy ! "Cy" @@ -1752,25 +1761,30 @@ subroutine Calc_WriteDbgOutputFVW( p, u, m, y, ErrStat, ErrMsg ) m%AllOuts( i+20 ) = -m%Y(j,k) ! "Fy" m%AllOuts( i+21 ) = m%X(j,k)*ct - m%Y(j,k)*st ! "Fn" m%AllOuts( i+22 ) = -m%X(j,k)*st - m%Y(j,k)*ct ! "Ft" - m%AllOuts( i+23 ) = 0.5_ReKi * p%FVW%Chord(j,k) * Vrel * AFI_interp%Cl ! "Gam" [m^2/s] + m%AllOuts( i+23 ) = 0.5_ReKi * p%FVW%Chord(j,k) * Vrel * Cl_dyn ! "Gam" [m^2/s] m%AllOuts( i+24 ) = dot_product(M_pg(1,:), m%FVW_y%Vind(1:3,j,k) ) ! Uihn, hub normal m%AllOuts( i+25 ) = dot_product(M_pg(2,:), m%FVW_y%Vind(1:3,j,k) ) ! Uiht, hub tangential m%AllOuts( i+26 ) = dot_product(M_pg(3,:), m%FVW_y%Vind(1:3,j,k) ) ! Uihr, hub radial + m%AllOuts( i+27 ) = AFI_interp%Cl ! "Cl" static + m%AllOuts( i+28 ) = AFI_interp%Cd ! "Cd" static + m%AllOuts( i+29 ) = AFI_interp%Cm ! "Cm" static + end do ! nodes end do ! blades end subroutine Calc_WriteDbgOutputFVW END SUBROUTINE Calc_WriteDbgOutput !---------------------------------------------------------------------------------------------------------------------------------- -SUBROUTINE Calc_WriteOutput( p, u, m, y, OtherState, indx, ErrStat, ErrMsg ) +SUBROUTINE Calc_WriteOutput( p, u, m, y, OtherState, xd, indx, ErrStat, ErrMsg ) TYPE(AD_ParameterType), INTENT(IN ) :: p ! The module parameters TYPE(AD_InputType), INTENT(IN ) :: u ! inputs TYPE(AD_MiscVarType), INTENT(INOUT) :: m ! misc variables TYPE(AD_OutputType), INTENT(IN ) :: y ! outputs - TYPE(AD_OtherStateType), INTENT(IN ) :: OtherState ! other states at t (for DBEMT debugging) + TYPE(AD_OtherStateType), INTENT(IN ) :: OtherState ! other states at t (for DBEMT and UA) + TYPE(AD_DiscreteStateType),INTENT(IN ) :: xd ! Discrete states integer, intent(in ) :: indx ! index into m%BEMT_u(indx) array; 1=t and 2=t+dt (but not checked here) INTEGER(IntKi), INTENT( OUT) :: ErrStat ! The error status code CHARACTER(*), INTENT( OUT) :: ErrMsg ! The error message, if an error occurred @@ -1987,6 +2001,7 @@ subroutine Calc_WriteOutput_FVW ! NOTE: using airfoil coeffs at nodes call AFI_ComputeAirfoilCoefs( alpha, Re, 0.0_ReKi, p%AFI(p%FVW%AFindx(j,k)), AFI_interp, ErrStat, ErrMsg ) + STOP theta = m%FVW%PitchAndTwist(j,k) ! --- Setting AD outputs diff --git a/modules/aerodyn/src/BEMT.f90 b/modules/aerodyn/src/BEMT.f90 index eb5f8b9681..140f553499 100644 --- a/modules/aerodyn/src/BEMT.f90 +++ b/modules/aerodyn/src/BEMT.f90 @@ -2122,4 +2122,4 @@ function NodeText(i,j) end function NodeText end module BEMT - \ No newline at end of file + diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 1141595dca..64708619cd 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -153,6 +153,7 @@ subroutine FVW_Init(AFInfo, InitInp, u, p, x, xd, z, OtherState, y, m, Interval, ! --- UA ! NOTE: quick and dirty since this should belong to AD + interval = InitInp%DTAero ! important, UA, needs proper interval call UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, ErrStat2, ErrMsg2); if (Failed()) return if (DEV_VERSION) then @@ -201,6 +202,8 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%s_LL , p%nSpan+1 , p%nWings, 'Spanwise coord LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%s_LL= -999999_ReKi; call AllocAry( m%chord_LL , p%nSpan+1 , p%nWings, 'Chord on LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%chord_LL= -999999_ReKi; call AllocAry( m%PitchAndTwist , p%nSpan+1 , p%nWings, 'Pitch and twist ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%PitchAndTwist= -999999_ReKi; + call AllocAry( m%alpha_LL, p%nSpan , p%nWings, 'Wind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%alpha_LL= -999999_ReKi; + call AllocAry( m%Vreln_LL, p%nSpan , p%nWings, 'Wind on CP ll ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Vreln_LL = -999999_ReKi; ! Variables at control points/elements call AllocAry( m%Gamma_LL, p%nSpan , p%nWings, 'Lifting line Circulation', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%Gamma_LL = -999999_ReKi; call AllocAry( m%chord_CP_LL , p%nSpan , p%nWings, 'Chord on CP LL ', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%chord_CP_LL= -999999_ReKi; @@ -232,6 +235,8 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%r_wind, 3, nMax, 'Requested wind points', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ) m%r_wind = 0.0_ReKi ! set to zero so InflowWind can shortcut calculations m%OldWakeTime = -HUGE(1.0_DbKi) + ! Temporary UA + call AllocAry( m%Vwnd_ND, 3, p%nSpan+1, p%nWings, 'Vwnd_ND', ErrStat2, ErrMsg2 );call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%TE = -999999_ReKi; end subroutine FVW_InitMiscVars ! ============================================================================== subroutine FVW_InitMiscVarsPostParam( p, m, ErrStat, ErrMsg ) @@ -475,6 +480,9 @@ subroutine FVW_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) ! Destroy the output data: call FVW_DestroyOutput( y, ErrStat, ErrMsg ) +#ifdef UA_OUTS + CLOSE(69) +#endif end subroutine FVW_End @@ -556,6 +564,8 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m z_guess%Gamma_LL = m%Gamma_LL call FVW_CalcConstrStateResidual(t, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 1); if(Failed()) return + call UA_UpdateState_Wrapper(AFInfo, n, uInterp, p, x, xd, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return + ! Map circulation and positions between LL and NW and then NW and FW ! Changes: x only ShedScale = 1.0_ReKi @@ -609,8 +619,6 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call FVW_CalcConstrStateResidual(t+p%DTaero, uInterp, p, x, xd, z_guess, OtherState, m, z, AFInfo, ErrStat2, ErrMsg2, 2); if(Failed()) return ! print*,'US: z_Gamma',x%Gamma_NW(1,1,1) ! print*,'US: x_Gamma',z%Gamma_LL(1,1) - ! TODO UA - call UA_UpdateState_Wrapper(AFInfo, n, p, x, xd, OtherState, m, ErrStat2, ErrMsg2) ! Updating circulation of near wake panel (and position but irrelevant) ! Changes: x only @@ -864,12 +872,12 @@ end subroutine FVW_CalcConstrStateResidual !! This subroutine is used to compute the output channels (motions and loads) and place them in the WriteOutput() array. !! The descriptions of the output channels are not given here. Please see the included OutListParameters.xlsx sheet for !! for a complete description of each output parameter. -subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ErrMsg ) ! NOTE: no matter how many channels are selected for output, all of the outputs are calculated ! All of the calculated output channels are placed into the m%AllOuts(:), while the channels selected for outputs are ! placed in the y%WriteOutput(:) array. -!.................................................................................................................................. +subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, ErrMsg ) use VTK, only: set_vtk_coordinate_transform, set_vtk_no_coordinate_transform + use FVW_VortexTools, only: interpextrap_cp2node real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(FVW_InputType), intent(in ) :: u !< Inputs at Time t type(FVW_ParameterType), intent(in ) :: p !< Parameters @@ -914,28 +922,10 @@ subroutine FVW_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, ErrStat, n=p%nSpan y%Vind(1:3,:,:) = 0.0_ReKi do iW=1,p%nWings - ! --- Linear interpolation for most points (2:n) - call InterpArray(m%s_CP_LL(:,iW), m%Vind_LL(1,:,iW), m%s_LL(2:n,iW), y%Vind(1,2:n,iW)) - call InterpArray(m%s_CP_LL(:,iW), m%Vind_LL(2,:,iW), m%s_LL(2:n,iW), y%Vind(2,2:n,iW)) - call InterpArray(m%s_CP_LL(:,iW), m%Vind_LL(3,:,iW), m%s_LL(2:n,iW), y%Vind(3,2:n,iW)) - ! --- Special case for end points (1 and n+1) - y%Vind(1:3, 1 , iW) = 0.0_ReKi - y%Vind(1:3, n+1, iW) = 0.0_ReKi - if (p%nSpan>1) then - ! If more than 2 panels, use extrapolation - i0=1; i1=1; i2=2; ! Using CP 1 and 2 to find point 1 - y%Vind(1, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(1,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(1,i2,iW)) - y%Vind(2, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(2,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(2,i2,iW)) - y%Vind(3, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(3,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(3,i2,iW)) - i0=n+1; i1=n; i2=n-1; ! Using CP n and n-2 to find point n+1 - y%Vind(1, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(1,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(1,i2,iW)) - y%Vind(2, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(2,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(2,i2,iW)) - y%Vind(3, i0 , iW) = lin_extrap(m%s_LL(i0,iW), m%s_CP_LL(i1,iW), m%Vind_LL(3,i1,iW), m%s_CP_LL(i2,iW), m%Vind_LL(3,i2,iW)) - else - ! If one panel, duplicate the unique point on both side - y%Vind(1:3, 1, iW) = m%Vind_LL(1:3, 1, iW) - y%Vind(1:3, 2, iW) = m%Vind_LL(1:3, 1, iW) - endif + ! --- Linear interpolation for interior points and extrapolations at boundaries + call interpextrap_cp2node(m%s_CP_LL(:,iW), m%Vind_LL(1,:,iW), m%s_LL(:,iW), y%Vind(1,:,iW)) + call interpextrap_cp2node(m%s_CP_LL(:,iW), m%Vind_LL(2,:,iW), m%s_LL(:,iW), y%Vind(2,:,iW)) + call interpextrap_cp2node(m%s_CP_LL(:,iW), m%Vind_LL(3,:,iW), m%s_LL(:,iW), y%Vind(3,:,iW)) enddo ! For plotting only @@ -978,17 +968,7 @@ logical function Failed() Failed = ErrStat >= AbortErrLev end function Failed - !> Perform linear extrapolation to get value of y(x0), using y(x1) and y(x2) - real(ReKi) function lin_extrap(x0, x1, y1, x2, y2) result(y0) - real(ReKi), intent(in) :: x0, x1, y1, x2, y2 - real(ReKi) :: a - a = (x0-x1)/(x0-x2) - y0 = 1._ReKi/(1._ReKi-a) * (y1-a*y2) - end function lin_extrap - end subroutine FVW_CalcOutput - - !---------------------------------------------------------------------------------------------------------------------------------- ! --- UA related, should be merged with AeroDyn !---------------------------------------------------------------------------------------------------------------------------------- @@ -1033,6 +1013,7 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E call UA_Init( Init_UA_Data, u_UA, m%p_UA, xd%UA, OtherState%UA, m%y_UA, m%m_UA, interval, InitOutData_UA, ErrStat2, ErrMsg2); if(Failed())return ! --- BEMT Init other state allocate ( OtherState%UA_Flag( InitInp%numBladeNodes, InitInp%NumBlades ), STAT = ErrStat2 ) + OtherState%UA_Flag=.true. ! --- Condensed version of "BEMT_CheckInitUA" do j = 1,InitInp%numBlades; do i = 1,InitInp%numBladeNodes; ! Loop over blades and nodes call UA_TurnOff_param(AFInfo(p%AFindx(i,j)), ErrStat2, ErrMsg2) @@ -1041,9 +1022,29 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E OtherState%UA_Flag(i,j) = .false. end if end do; end do; +#ifdef UA_OUTS + CALL OpenFOutFile ( 69, 'Debug.UA.out', errStat, errMsg ); IF (ErrStat >= AbortErrLev) RETURN + WRITE (69,'(/,A)') 'This output information was generated by FVW'// ' on '//CurDate()//' at '//CurTime()//'.' + WRITE (69,'(:,A20)', ADVANCE='no' ) 'Time' + do i=1,size(InitOutData_UA%WriteOutputHdr) + WRITE (69,'(:,A20)', ADVANCE='no' ) trim(InitOutData_UA%WriteOutputHdr(i)) + end do + write (69,'(A)') ' ' + WRITE (69,'(:,A20)', ADVANCE='no' ) '(s)' + do i=1,size(InitOutData_UA%WriteOutputUnt) + WRITE (69,'(:,A20)', ADVANCE='no' ) trim(InitOutData_UA%WriteOutputUnt(i)) + end do + write (69,'(A)') ' ' +#endif call UA_DestroyInput( u_UA, ErrStat2, ErrMsg2 ); if(Failed())return call UA_DestroyInitInput( Init_UA_Data, ErrStat2, ErrMsg2 ); if(Failed())return call UA_DestroyInitOutput( InitOutData_UA, ErrStat2, ErrMsg2 ); if(Failed())return + + ! --- FVW specific + if (p%CirculationMethod/=idCircPolarData) then + ErrMsg2='Unsteady aerodynamic (`AFAeroMod>1`) is only available with a circulation solving using profile data (`CircSolvingMethod=1`)'; ErrStat2=ErrID_Fatal; + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'UA_Init_Wrapper'); return + endif endif contains logical function Failed() @@ -1052,11 +1053,13 @@ logical function Failed() end function Failed end subroutine UA_Init_Wrapper -subroutine UA_UpdateState_Wrapper(AFInfo, n, p, x, xd, OtherState, m, ErrStat, ErrMsg ) +subroutine UA_UpdateState_Wrapper(AFInfo, n, u, p, x, xd, OtherState, m, ErrStat, ErrMsg ) + use FVW_VortexTools, only: interpextrap_cp2node use UnsteadyAero, only: UA_UpdateStates, UA_TurnOff_input type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data, temporary, for UA.. integer(IntKi), intent(in ) :: n !< time step type(FVW_ParameterType), intent(in ) :: p !< Parameters + type(FVW_InputType), intent(in ) :: u !< Inputs type(FVW_ContinuousStateType), intent(inout) :: x !< Initial continuous states type(FVW_DiscreteStateType), intent(inout) :: xd !< Initial discrete states type(FVW_OtherStateType), intent(inout) :: OtherState !< Initial other states @@ -1068,42 +1071,59 @@ subroutine UA_UpdateState_Wrapper(AFInfo, n, p, x, xd, OtherState, m, ErrStat, E integer :: i,j integer(intKi) :: ErrStat2 ! temporary Error status character(ErrMsgLen) :: ErrMsg2 + real(ReKi), dimension(:,:), allocatable :: Vind_node ErrStat = ErrID_None ErrMsg = "" - ! --- Condensed version of BEMT_Update States if (m%UA_Flag) then + + ! --- Induction on the lifting line control point + ! NOTE: this is expensive since it's an output for FVW but here we have to use it for UA + ! Set m%Vind_LL + m%Vind_LL=-9999.0_ReKi + call LiftingLineInducedVelocities(p, x, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'UA_UpdateState_Wrapper'); if (ErrStat >= AbortErrLev) return + + allocate(Vind_node(3,1:p%nSpan+1)) + + ! --- Condensed version of BEMT_Update States do j = 1,p%nWings + ! Induced velocity at Nodes (NOTE: we rely on storage done when computing Circulation) + if (m%nNW>1) then + call interpextrap_cp2node(m%s_CP_LL(:,j), m%Vind_LL(1,:,j), m%s_LL(:,j), Vind_node(1,:)) + call interpextrap_cp2node(m%s_CP_LL(:,j), m%Vind_LL(2,:,j), m%s_LL(:,j), Vind_node(2,:)) + call interpextrap_cp2node(m%s_CP_LL(:,j), m%Vind_LL(3,:,j), m%s_LL(:,j), Vind_node(3,:)) + else + Vind_node=0.0_ReKi + endif + do i = 1,p%nSpan+1 ! We only update the UnsteadyAero states if we have unsteady aero turned on for this node if (OtherState%UA_Flag(i,j) .and. n > 0) then !! ....... compute inputs to UA ........... + ! NOTE: To be consistent with CalcOutput we take Vwind_ND that was set using m%DisturbedInflow from AeroDyn.. + ! This is not clean, but done to be consistent, waiting for AeroDyn to handle UA + call AlphaVrel_Generic(u%WingsMesh(j)%Orientation(1:3,1:3,i), u%WingsMesh(j)%TranslationVel(1:3,i), Vind_node(:,i), m%Vwnd_ND(:,i,j), p%KinVisc, p%Chord(i,j), u_UA%U, u_UA%alpha, u_UA%Re) m%m_UA%iBladeNode = i m%m_UA%iBlade = j - !phitemp = z%phi(i,j) - !u_UA%alpha = phitemp - u1%theta(i,j) ! angle of attack - !u_UA%UserProp = u1%UserProp(i,j) - !u_UA%Re = ... - !u_UA%U = ... - ! Need to compute local velocity including both axial and tangential induction - ! COMPUTE: u_UA%U, u_UA%Re - !call BEMTU_Wind( m%axInduction(i,j), m%tanInduction(i,j), u1%Vx(i,j), u1%Vy(i,j), p%chord(i,j), p%kinVisc, u_UA%Re, u_UA%U) + u_UA%UserProp = 0 ! u1%UserProp(i,j) ! TODO !! ....... check inputs to UA ........... call UA_TurnOff_input(AFInfo(p%AFIndx(i,j)), u_UA, ErrStat2, ErrMsg2) if (ErrStat2 /= ErrID_None) then OtherState%UA_Flag(i,j) = .FALSE. - call WrScr( 'Warning: Turning off Unsteady Aerodynamics due to '//trim(ErrMsg2)//' BladeNode = '//trim(num2lstr(i))//', Blade = '//trim(num2lstr(j)) ) + call WrScr( 'Warning: Turning off dynamic stall due to '//trim(ErrMsg2)//' '//trim(NodeText(i,j))) else ! COMPUTE: xd%UA, OtherState%UA call UA_UpdateStates( i, j, u_UA, m%p_UA, xd%UA, OtherState%UA, AFInfo(p%AFIndx(i,j)), m%m_UA, ErrStat2, ErrMsg2 ) if (ErrStat2 /= ErrID_None) then call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'UA_UpdateState_Wrapper'//trim(NodeText(i,j))) + call WrScr(trim(ErrMsg)) if (ErrStat >= AbortErrLev) return end if end if - end if ! if (OtherState%UA_Flag(i,j)) then + end if end do end do call UA_DestroyInput( u_UA, ErrStat2, ErrMsg2 ); + deallocate(Vind_node) endif call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'UA_UpdateState_Wrapper') contains @@ -1111,7 +1131,7 @@ function NodeText(i,j) integer(IntKi), intent(in) :: i ! node number integer(IntKi), intent(in) :: j ! blade number character(25) :: NodeText - NodeText = '(node '//trim(num2lstr(i))//', blade '//trim(num2lstr(j))//')' + NodeText = '(nd:'//trim(num2lstr(i))//' bld:'//trim(num2lstr(j))//')' end function NodeText end subroutine UA_UpdateState_Wrapper diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index a2f2ba2008..2650a3e8b9 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -64,7 +64,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi TE ::: - - "Trailing edge points" - typedef ^ ^ ReKi r_LL :::: - - "Position of the Lifting line panels" - typedef ^ ^ ReKi s_LL :: - - "Spanwise coordinate of LL elements" m -typedef ^ ^ ReKi chord_LL :: - - "chord on LL cp " m +typedef ^ ^ ReKi chord_LL :: - - "chord on LL nodes " m # Variables at control point - Dimensions nSpan typedef ^ ^ ReKi s_CP_LL :: - - "Spanwise coordinate of LL CP" m typedef ^ ^ ReKi chord_CP_LL :: - - "chord on LL cp " m @@ -96,6 +96,9 @@ typedef ^ ^ DbKi typedef ^ ^ ReKi tSpent - - - "Time spent in expensive Biot-Savart computation" s typedef ^ ^ ReKi dxdt_NW :::: - - "State time derivatie, stored for subcylcing" - typedef ^ ^ ReKi dxdt_FW :::: - - "State time derivatie, stored for subcylcing" - +# Convenient storage +typedef ^ ^ Reki alpha_LL :: - - "Angle of attack at lifting line CP, only computed with CircPolarData method" - +typedef ^ ^ Reki Vreln_LL :: - - "Norm of Vrel on the lifting line" - # Segment storage (buffer) typedef ^ ^ IntKi SegConnct :: - - "Connectivity of segments" - typedef ^ ^ ReKi SegPoints :: - - "Points delimiting the segments" - @@ -109,6 +112,7 @@ typedef ^ ^ UA_MiscVarType typedef ^ ^ UA_OutputType y_UA - - - "outputs from UnsteadyAero" - typedef ^ ^ UA_ParameterType p_UA - - - "parameters for UnsteadyAero" - typedef ^ ^ LOGICAL UA_Flag - - - "logical flag indicating whether to use UnsteadyAero" - +typedef ^ ^ ReKi Vwnd_ND ::: - - "InflowOnBlade (at nodes) values modified by tower influence. ONLY for UA" m/s # ........ Input ............ # FVW_InputType diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 7bfbd8967a..5323d40a42 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -998,7 +998,7 @@ subroutine FVW_AeroOuts( M_sg, M_ag, PitchAndTwist, Vstr_g, Vind_g, Vwnd_g, Kin !M_sa(3,1:3) = (/ 0.0_DbKi, 0.0_DbKi, 1.0_DbKi /) !M_sg= matmul(M_sa, M_ag ) - ! --- Airfoil coordinates: used to define alpha, and Vrel, alos called "n-t" system + ! --- Airfoil coordinates: used to define alpha, and Vrel, also called "n-t" system Vtot_g = Vwnd_g - Vstr_g + Vind_g Vtot_a = matmul(M_ag, Vtot_g) alpha = atan2( Vtot_a(1), Vtot_a(2) ) @@ -1018,5 +1018,27 @@ subroutine FVW_AeroOuts( M_sg, M_ag, PitchAndTwist, Vstr_g, Vind_g, Vwnd_g, Kin if(.false.) print*,PitchAndTwist ! just to avoid unused var for now end subroutine FVW_AeroOuts +!> Generic function to compute alpha, Vrel and Re based on global data +subroutine AlphaVrel_Generic(M_ag, Vstr_g, Vind_g, Vwnd_g, KinVisc, Chord, Vrel_norm, alpha, Re) + real(R8Ki), intent(in ) :: M_ag(3,3) ! u%BladeMotion(k)%Orientation(1:3,1:3,j) global coord to airfoil coord + real(ReKi), intent(in ) :: Vstr_g(3) ! Structural velocity global coord + real(ReKi), intent(in ) :: Vind_g(3) ! Induced wind velocity global coord + real(ReKi), intent(in ) :: Vwnd_g(3) ! Disturbed inflow global coord + real(ReKi), intent(in ) :: KinVisc ! Viscosity + real(ReKi), intent(in ) :: Chord ! chord length + real(ReKi), intent( out) :: Vrel_norm ! Relative velocity norm + real(Reki), intent( out) :: alpha ! angle of attack + real(ReKi), intent( out) :: Re ! Reynolds number + ! Local vars + real(ReKi) :: Vtot_g(3) ! Vector of total relative velocity section coord + real(ReKi) :: Vtot_a(3) ! Vector of total relative velocity global coord + ! --- Airfoil coordinates: used to define alpha, and Vrel, also called "n-t" system + Vtot_g = Vwnd_g - Vstr_g + Vind_g + Vtot_a = matmul(M_ag, Vtot_g) + alpha = atan2( Vtot_a(1), Vtot_a(2) ) + Vrel_norm = sqrt(Vtot_a(1)**2 + Vtot_a(2)**2) ! NOTE: z component shoudn't be used + Re = Chord * Vrel_norm / KinVisc / 1.0E6 +end subroutine AlphaVrel_Generic + end module FVW_Subs diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index e5f25ac9f5..e93bcaf5c2 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -90,7 +90,7 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: TE !< Trailing edge points [-] REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: r_LL !< Position of the Lifting line panels [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: s_LL !< Spanwise coordinate of LL elements [m] - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: chord_LL !< chord on LL cp [m] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: chord_LL !< chord on LL nodes [m] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: s_CP_LL !< Spanwise coordinate of LL CP [m] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: chord_CP_LL !< chord on LL cp [m] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: CP_LL !< Coordinates of LL CP [-] @@ -121,6 +121,8 @@ MODULE FVW_Types REAL(ReKi) :: tSpent !< Time spent in expensive Biot-Savart computation [s] REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: dxdt_NW !< State time derivatie, stored for subcylcing [-] REAL(ReKi) , DIMENSION(:,:,:,:), ALLOCATABLE :: dxdt_FW !< State time derivatie, stored for subcylcing [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: alpha_LL !< Angle of attack at lifting line CP, only computed with CircPolarData method [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Vreln_LL !< Norm of Vrel on the lifting line [-] INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: SegConnct !< Connectivity of segments [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: SegPoints !< Points delimiting the segments [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: SegGamma !< Segment circulations [-] @@ -131,6 +133,7 @@ MODULE FVW_Types TYPE(UA_OutputType) :: y_UA !< outputs from UnsteadyAero [-] TYPE(UA_ParameterType) :: p_UA !< parameters for UnsteadyAero [-] LOGICAL :: UA_Flag !< logical flag indicating whether to use UnsteadyAero [-] + REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vwnd_ND !< InflowOnBlade (at nodes) values modified by tower influence. ONLY for UA [m/s] END TYPE FVW_MiscVarType ! ======================= ! ========= FVW_InputType ======= @@ -1499,6 +1502,34 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) END IF DstMiscData%dxdt_FW = SrcMiscData%dxdt_FW ENDIF +IF (ALLOCATED(SrcMiscData%alpha_LL)) THEN + i1_l = LBOUND(SrcMiscData%alpha_LL,1) + i1_u = UBOUND(SrcMiscData%alpha_LL,1) + i2_l = LBOUND(SrcMiscData%alpha_LL,2) + i2_u = UBOUND(SrcMiscData%alpha_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%alpha_LL)) THEN + ALLOCATE(DstMiscData%alpha_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%alpha_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%alpha_LL = SrcMiscData%alpha_LL +ENDIF +IF (ALLOCATED(SrcMiscData%Vreln_LL)) THEN + i1_l = LBOUND(SrcMiscData%Vreln_LL,1) + i1_u = UBOUND(SrcMiscData%Vreln_LL,1) + i2_l = LBOUND(SrcMiscData%Vreln_LL,2) + i2_u = UBOUND(SrcMiscData%Vreln_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%Vreln_LL)) THEN + ALLOCATE(DstMiscData%Vreln_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Vreln_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Vreln_LL = SrcMiscData%Vreln_LL +ENDIF IF (ALLOCATED(SrcMiscData%SegConnct)) THEN i1_l = LBOUND(SrcMiscData%SegConnct,1) i1_u = UBOUND(SrcMiscData%SegConnct,1) @@ -1589,6 +1620,22 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN DstMiscData%UA_Flag = SrcMiscData%UA_Flag +IF (ALLOCATED(SrcMiscData%Vwnd_ND)) THEN + i1_l = LBOUND(SrcMiscData%Vwnd_ND,1) + i1_u = UBOUND(SrcMiscData%Vwnd_ND,1) + i2_l = LBOUND(SrcMiscData%Vwnd_ND,2) + i2_u = UBOUND(SrcMiscData%Vwnd_ND,2) + i3_l = LBOUND(SrcMiscData%Vwnd_ND,3) + i3_u = UBOUND(SrcMiscData%Vwnd_ND,3) + IF (.NOT. ALLOCATED(DstMiscData%Vwnd_ND)) THEN + ALLOCATE(DstMiscData%Vwnd_ND(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%Vwnd_ND.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%Vwnd_ND = SrcMiscData%Vwnd_ND +ENDIF END SUBROUTINE FVW_CopyMisc SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) @@ -1681,6 +1728,12 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) IF (ALLOCATED(MiscData%dxdt_FW)) THEN DEALLOCATE(MiscData%dxdt_FW) ENDIF +IF (ALLOCATED(MiscData%alpha_LL)) THEN + DEALLOCATE(MiscData%alpha_LL) +ENDIF +IF (ALLOCATED(MiscData%Vreln_LL)) THEN + DEALLOCATE(MiscData%Vreln_LL) +ENDIF IF (ALLOCATED(MiscData%SegConnct)) THEN DEALLOCATE(MiscData%SegConnct) ENDIF @@ -1702,6 +1755,9 @@ SUBROUTINE FVW_DestroyMisc( MiscData, ErrStat, ErrMsg ) CALL UA_DestroyMisc( MiscData%m_UA, ErrStat, ErrMsg ) CALL UA_DestroyOutput( MiscData%y_UA, ErrStat, ErrMsg ) CALL UA_DestroyParam( MiscData%p_UA, ErrStat, ErrMsg ) +IF (ALLOCATED(MiscData%Vwnd_ND)) THEN + DEALLOCATE(MiscData%Vwnd_ND) +ENDIF END SUBROUTINE FVW_DestroyMisc SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -1883,6 +1939,16 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 2*4 ! dxdt_FW upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%dxdt_FW) ! dxdt_FW END IF + Int_BufSz = Int_BufSz + 1 ! alpha_LL allocated yes/no + IF ( ALLOCATED(InData%alpha_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! alpha_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%alpha_LL) ! alpha_LL + END IF + Int_BufSz = Int_BufSz + 1 ! Vreln_LL allocated yes/no + IF ( ALLOCATED(InData%Vreln_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Vreln_LL upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vreln_LL) ! Vreln_LL + END IF Int_BufSz = Int_BufSz + 1 ! SegConnct allocated yes/no IF ( ALLOCATED(InData%SegConnct) ) THEN Int_BufSz = Int_BufSz + 2*2 ! SegConnct upper/lower bounds for each dimension @@ -1966,6 +2032,11 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si DEALLOCATE(Int_Buf) END IF Int_BufSz = Int_BufSz + 1 ! UA_Flag + Int_BufSz = Int_BufSz + 1 ! Vwnd_ND allocated yes/no + IF ( ALLOCATED(InData%Vwnd_ND) ) THEN + Int_BufSz = Int_BufSz + 2*3 ! Vwnd_ND upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vwnd_ND) ! Vwnd_ND + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -2518,6 +2589,38 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si IF (SIZE(InData%dxdt_FW)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%dxdt_FW))-1 ) = PACK(InData%dxdt_FW,.TRUE.) Re_Xferred = Re_Xferred + SIZE(InData%dxdt_FW) END IF + IF ( .NOT. ALLOCATED(InData%alpha_LL) ) 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%alpha_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%alpha_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%alpha_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%alpha_LL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%alpha_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%alpha_LL))-1 ) = PACK(InData%alpha_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%alpha_LL) + END IF + IF ( .NOT. ALLOCATED(InData%Vreln_LL) ) 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%Vreln_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vreln_LL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vreln_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vreln_LL,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vreln_LL)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vreln_LL))-1 ) = PACK(InData%Vreln_LL,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vreln_LL) + END IF IF ( .NOT. ALLOCATED(InData%SegConnct) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -2694,6 +2797,25 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si ENDIF IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%UA_Flag , IntKiBuf(1), 1) Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%Vwnd_ND) ) 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%Vwnd_ND,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_ND,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_ND,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_ND,2) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_ND,3) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_ND,3) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%Vwnd_ND)>0) ReKiBuf ( Re_Xferred:Re_Xferred+(SIZE(InData%Vwnd_ND))-1 ) = PACK(InData%Vwnd_ND,.TRUE.) + Re_Xferred = Re_Xferred + SIZE(InData%Vwnd_ND) + END IF END SUBROUTINE FVW_PackMisc SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -3527,6 +3649,58 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Re_Xferred = Re_Xferred + SIZE(OutData%dxdt_FW) DEALLOCATE(mask4) END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! alpha_LL 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 + IF (ALLOCATED(OutData%alpha_LL)) DEALLOCATE(OutData%alpha_LL) + ALLOCATE(OutData%alpha_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%alpha_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%alpha_LL)>0) OutData%alpha_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%alpha_LL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%alpha_LL) + DEALLOCATE(mask2) + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vreln_LL 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 + IF (ALLOCATED(OutData%Vreln_LL)) DEALLOCATE(OutData%Vreln_LL) + ALLOCATE(OutData%Vreln_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vreln_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%Vreln_LL)>0) OutData%Vreln_LL = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vreln_LL))-1 ), mask2, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vreln_LL) + DEALLOCATE(mask2) + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! SegConnct not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -3799,6 +3973,35 @@ SUBROUTINE FVW_UnPackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) OutData%UA_Flag = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vwnd_ND 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%Vwnd_ND)) DEALLOCATE(OutData%Vwnd_ND) + ALLOCATE(OutData%Vwnd_ND(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%Vwnd_ND.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask3(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask3.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask3 = .TRUE. + IF (SIZE(OutData%Vwnd_ND)>0) OutData%Vwnd_ND = UNPACK(ReKiBuf( Re_Xferred:Re_Xferred+(SIZE(OutData%Vwnd_ND))-1 ), mask3, 0.0_ReKi ) + Re_Xferred = Re_Xferred + SIZE(OutData%Vwnd_ND) + DEALLOCATE(mask3) + END IF END SUBROUTINE FVW_UnPackMisc SUBROUTINE FVW_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index 50e03fe80a..4da7a75102 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -1,4 +1,4 @@ -MODULE FVW_VortexTools +module FVW_VortexTools ! Contains Typical Tools for vortex methods ! Should be *independent* of the Framework and any derived type @@ -7,9 +7,9 @@ MODULE FVW_VortexTools use NWTC_LIBRARY - IMPLICIT NONE + implicit none -CONTAINS +contains subroutine VecToLattice(PointVectors, iDepthStart, LatticeVectors, iHeadP) real(Reki), dimension(:,:), intent(in ) :: PointVectors !< nVal x n @@ -180,5 +180,31 @@ subroutine print_mean_3d(M, Label) print'(A24,3F12.4)',trim(Label),U end subroutine - -END MODULE FVW_VortexTools + !> Perform interpolation from control points to nodes assuming CP are between nodes + subroutine interpextrap_cp2node(xin, yin, xnew, ynew) + real(ReKi), intent(in ) :: xin(:) + real(ReKi), intent(in ) :: yin(:) + real(ReKi), intent(in ) :: xnew(:) + real(ReKi), intent( out) :: ynew(:) + integer(IntKi) :: n + n=size(xin) + call InterpArray(xin, yin, xnew(2:n), ynew(2:n)) + ! Boundaries + if (n>1) then ! If more than 2 panels, use extrapolation + ynew(1) = lin_extrap(xnew(1) , xin(1), yin(1), xin(2) , yin(2)) + ynew(n+1) = lin_extrap(xnew(n+1), xin(n), yin(n), xin(n-1), yin(n-1)) + else ! If one panel, duplicate the unique point on both side + ynew(1) = yin(1) + ynew(n+1) = yin(n) !n=1 + endif + contains + !> Perform linear extrapolation to get value of y(x0), using y(x1) and y(x2) + real(ReKi) function lin_extrap(x0, x1, y1, x2, y2) result(y0) + real(ReKi), intent(in) :: x0, x1, y1, x2, y2 + real(ReKi) :: a + a = (x0-x1)/(x0-x2) + y0 = 1._ReKi/(1._ReKi-a) * (y1-a*y2) + end function lin_extrap + end subroutine interpextrap_cp2node + +end module FVW_VortexTools diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 934b0bf1e2..f184c2701d 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -438,7 +438,7 @@ end subroutine Wings_ComputeCirculationPolarData subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) real(ReKi), dimension(:,:), intent(inout) :: Gamma_LL !< Circulation on all the lifting lines type(FVW_ParameterType), intent(in ) :: p !< Parameters - type(FVW_MiscVarType), intent(in ) :: m !< Initial misc/optimization variables + type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None @@ -447,7 +447,7 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) integer(IntKi) :: iW, iCP !< Index on wings and spanwise control points real(ReKi), dimension(3) :: N, Tc !< Normal and Tangent vector real(ReKi), dimension(3) :: Vrel, Vrel_orth, Vjouk, Vjouk_orth - real(ReKi) :: Vrel_orth_norm, Vjouk_orth_norm + real(ReKi) :: Vrel_orth_norm, Vjouk_orth_norm, Vrel_norm real(ReKi) :: alpha, Re, Cl, Cd, Cm type(AFI_OutputType) :: AFI_interp integer(IntKi) :: ErrStat2 @@ -469,9 +469,10 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) Vjouk(1:3) = cross_product(Vrel,m%dl(1:3,icp,iW)) Vjouk_orth(1:3) = dot_product(Vjouk,N)*N + dot_product(Vjouk,Tc)*Tc Vjouk_orth_norm = norm2(Vjouk_orth) + Vrel_norm = norm2(Vrel) alpha = atan2(dot_product(Vrel,N) , dot_product(Vrel,Tc) ) ! [rad] - Re = p%Chord(icp,iW) * norm2(Vrel) / p%KinVisc / 1.0E6 + Re = p%Chord(icp,iW) * Vrel_norm / p%KinVisc / 1.0E6 !if (p%CircSolvPolar==idPolarAeroDyn) then ! compute steady Airfoil Coefs ! NOTE: UserProp set to 0.0_ReKi (no idea what it does). Also, note this assumes airfoils at nodes. @@ -484,6 +485,9 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) ! Gamma_LL=(0.5 * Cl * Vrel_orth_norm*chord) ! VanGarrel's method: Gamma_LL(icp,iW) =(0.5_ReKi * Cl * Vrel_orth_norm**2*m%Area(icp,iW)/(Vjouk_orth_norm)) + ! Convenient storage + m%alpha_LL(icp, iW) = alpha ! [rad] + m%Vreln_LL(icp, iW) = Vrel_norm enddo enddo contains From b1c9b49a6ca6d42c1eb0c06e6f29b1ed8311552e Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 21 Apr 2020 22:25:52 -0600 Subject: [PATCH 133/190] FVW: increased readibility of UA dbg outs --- modules/aerodyn/src/UnsteadyAero.f90 | 84 ++++++++++++++-------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index 04efb0fb11..bbdfc1d32d 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -953,48 +953,48 @@ subroutine UA_Init( InitInp, u, p, xd, OtherState, y, m, Interval, & iOffset = (i-1)*p%NumOuts + (j-1)*p%nNodesPerBlade*p%NumOuts chanPrefix = "B"//trim(num2lstr(j))//"N"//trim(num2lstr(i)) - InitOut%WriteOutputHdr(iOffset+ 1) = 'ALPHA_filt'//chanPrefix - InitOut%WriteOutputHdr(iOffset+ 2) = 'VREL'//chanPrefix - InitOut%WriteOutputHdr(iOffset+ 3) = 'Cn'//chanPrefix - InitOut%WriteOutputHdr(iOffset+ 4) = 'Cc'//chanPrefix - InitOut%WriteOutputHdr(iOffset+ 5) = 'Cl'//chanPrefix - InitOut%WriteOutputHdr(iOffset+ 6) = 'Cd'//chanPrefix - InitOut%WriteOutputHdr(iOffset+ 7) = 'Cm'//chanPrefix - InitOut%WriteOutputHdr(iOffset+ 8) = 'Cn_aq_circ'//chanPrefix - InitOut%WriteOutputHdr(iOffset+ 9) = 'Cn_aq_nc'//chanPrefix - InitOut%WriteOutputHdr(iOffset+10) = 'Cn_pot'//chanPrefix - InitOut%WriteOutputHdr(iOffset+11) = 'Dp'//chanPrefix - InitOut%WriteOutputHdr(iOffset+12) = 'Cn_prime'//chanPrefix - InitOut%WriteOutputHdr(iOffset+13) = 'fprime'//chanPrefix - InitOut%WriteOutputHdr(iOffset+14) = 'Df'//chanPrefix - InitOut%WriteOutputHdr(iOffset+15) = 'Cn_v'//chanPrefix - InitOut%WriteOutputHdr(iOffset+16) = 'Tau_V'//chanPrefix - InitOut%WriteOutputHdr(iOffset+17) = 'LESF'//chanPrefix - InitOut%WriteOutputHdr(iOffset+18) = 'TESF'//chanPrefix - InitOut%WriteOutputHdr(iOffset+19) = 'VRTX'//chanPrefix - InitOut%WriteOutputHdr(iOffset+20) = 'C_v'//chanPrefix - InitOut%WriteOutputHdr(iOffset+21) = 'Cm_a_nc'//chanPrefix - InitOut%WriteOutputHdr(iOffset+22) = 'Cm_q_nc'//chanPrefix - InitOut%WriteOutputHdr(iOffset+23) = 'Cm_v'//chanPrefix - InitOut%WriteOutputHdr(iOffset+24) = 'alpha_p_f'//chanPrefix - InitOut%WriteOutputHdr(iOffset+25) = 'Dalphaf'//chanPrefix - InitOut%WriteOutputHdr(iOffset+26) = 'PMC'//chanPrefix - InitOut%WriteOutputHdr(iOffset+27) = 'T_f'//chanPrefix - InitOut%WriteOutputHdr(iOffset+28) = 'T_V'//chanPrefix - InitOut%WriteOutputHdr(iOffset+29) = 'dS'//chanPrefix - InitOut%WriteOutputHdr(iOffset+30) = 'T_alpha'//chanPrefix - InitOut%WriteOutputHdr(iOffset+31) = 'T_q'//chanPrefix - InitOut%WriteOutputHdr(iOffset+32) = 'k_alpha'//chanPrefix - InitOut%WriteOutputHdr(iOffset+33) = 'k_q'//chanPrefix - InitOut%WriteOutputHdr(iOffset+34) = 'alpha_e'//chanPrefix - InitOut%WriteOutputHdr(iOffset+35) = 'X1'//chanPrefix - InitOut%WriteOutputHdr(iOffset+36) = 'X2'//chanPrefix - InitOut%WriteOutputHdr(iOffset+37) = 'cn_q_nc'//chanPrefix - InitOut%WriteOutputHdr(iOffset+38) = 'alpha_f'//chanPrefix - InitOut%WriteOutputHdr(iOffset+39) = 'fprimeprime'//chanPrefix - InitOut%WriteOutputHdr(iOffset+40) = 'sigma1'//chanPrefix - InitOut%WriteOutputHdr(iOffset+41) = 'sigma3'//chanPrefix - InitOut%WriteOutputHdr(iOffset+42) = 'T_sh'//chanPrefix + InitOut%WriteOutputHdr(iOffset+ 1) = trim(chanPrefix)//'ALPHA_filt' + InitOut%WriteOutputHdr(iOffset+ 2) = trim(chanPrefix)//'VREL' + InitOut%WriteOutputHdr(iOffset+ 3) = trim(chanPrefix)//'Cn' + InitOut%WriteOutputHdr(iOffset+ 4) = trim(chanPrefix)//'Cc' + InitOut%WriteOutputHdr(iOffset+ 5) = trim(chanPrefix)//'Cl' + InitOut%WriteOutputHdr(iOffset+ 6) = trim(chanPrefix)//'Cd' + InitOut%WriteOutputHdr(iOffset+ 7) = trim(chanPrefix)//'Cm' + InitOut%WriteOutputHdr(iOffset+ 8) = trim(chanPrefix)//'Cn_aq_circ' + InitOut%WriteOutputHdr(iOffset+ 9) = trim(chanPrefix)//'Cn_aq_nc' + InitOut%WriteOutputHdr(iOffset+10) = trim(chanPrefix)//'Cn_pot' + InitOut%WriteOutputHdr(iOffset+11) = trim(chanPrefix)//'Dp' + InitOut%WriteOutputHdr(iOffset+12) = trim(chanPrefix)//'Cn_prime' + InitOut%WriteOutputHdr(iOffset+13) = trim(chanPrefix)//'fprime' + InitOut%WriteOutputHdr(iOffset+14) = trim(chanPrefix)//'Df' + InitOut%WriteOutputHdr(iOffset+15) = trim(chanPrefix)//'Cn_v' + InitOut%WriteOutputHdr(iOffset+16) = trim(chanPrefix)//'Tau_V' + InitOut%WriteOutputHdr(iOffset+17) = trim(chanPrefix)//'LESF' + InitOut%WriteOutputHdr(iOffset+18) = trim(chanPrefix)//'TESF' + InitOut%WriteOutputHdr(iOffset+19) = trim(chanPrefix)//'VRTX' + InitOut%WriteOutputHdr(iOffset+20) = trim(chanPrefix)//'C_v' + InitOut%WriteOutputHdr(iOffset+21) = trim(chanPrefix)//'Cm_a_nc' + InitOut%WriteOutputHdr(iOffset+22) = trim(chanPrefix)//'Cm_q_nc' + InitOut%WriteOutputHdr(iOffset+23) = trim(chanPrefix)//'Cm_v' + InitOut%WriteOutputHdr(iOffset+24) = trim(chanPrefix)//'alpha_p_f' + InitOut%WriteOutputHdr(iOffset+25) = trim(chanPrefix)//'Dalphaf' + InitOut%WriteOutputHdr(iOffset+26) = trim(chanPrefix)//'PMC' + InitOut%WriteOutputHdr(iOffset+27) = trim(chanPrefix)//'T_f' + InitOut%WriteOutputHdr(iOffset+28) = trim(chanPrefix)//'T_V' + InitOut%WriteOutputHdr(iOffset+29) = trim(chanPrefix)//'dS' + InitOut%WriteOutputHdr(iOffset+30) = trim(chanPrefix)//'T_alpha' + InitOut%WriteOutputHdr(iOffset+31) = trim(chanPrefix)//'T_q' + InitOut%WriteOutputHdr(iOffset+32) = trim(chanPrefix)//'k_alpha' + InitOut%WriteOutputHdr(iOffset+33) = trim(chanPrefix)//'k_q' + InitOut%WriteOutputHdr(iOffset+34) = trim(chanPrefix)//'alpha_e' + InitOut%WriteOutputHdr(iOffset+35) = trim(chanPrefix)//'X1' + InitOut%WriteOutputHdr(iOffset+36) = trim(chanPrefix)//'X2' + InitOut%WriteOutputHdr(iOffset+37) = trim(chanPrefix)//'cn_q_nc' + InitOut%WriteOutputHdr(iOffset+38) = trim(chanPrefix)//'alpha_f' + InitOut%WriteOutputHdr(iOffset+39) = trim(chanPrefix)//'fprimeprime' + InitOut%WriteOutputHdr(iOffset+40) = trim(chanPrefix)//'sigma1' + InitOut%WriteOutputHdr(iOffset+41) = trim(chanPrefix)//'sigma3' + InitOut%WriteOutputHdr(iOffset+42) = trim(chanPrefix)//'T_sh' InitOut%WriteOutputUnt(iOffset+1) ='(deg)' From c7f5dac9a96452be8ec9a48d20d0fd61ec07c902 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 21 Apr 2020 22:39:00 -0600 Subject: [PATCH 134/190] FVW: small change to match BEMT handling of UA Otherstate --- modules/aerodyn/src/FVW.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 64708619cd..defed9e3d9 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -995,6 +995,9 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E ErrMsg = "" m%UA_Flag=InitInp%UA_Flag + ! --- Condensed version of BEMT_Init_Otherstate + allocate ( OtherState%UA_Flag( InitInp%numBladeNodes, InitInp%NumBlades ), STAT = ErrStat2 ) + OtherState%UA_Flag=m%UA_Flag if ( m%UA_Flag ) then ! ---Condensed version of "BEMT_Set_UA_InitData" allocate(Init_UA_Data%c(InitInp%numBladeNodes,InitInp%numBlades), STAT = errStat2) @@ -1011,9 +1014,6 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E Init_UA_Data%a_s = InitInp%a_s ! m/s ! --- UA init call UA_Init( Init_UA_Data, u_UA, m%p_UA, xd%UA, OtherState%UA, m%y_UA, m%m_UA, interval, InitOutData_UA, ErrStat2, ErrMsg2); if(Failed())return - ! --- BEMT Init other state - allocate ( OtherState%UA_Flag( InitInp%numBladeNodes, InitInp%NumBlades ), STAT = ErrStat2 ) - OtherState%UA_Flag=.true. ! --- Condensed version of "BEMT_CheckInitUA" do j = 1,InitInp%numBlades; do i = 1,InitInp%numBladeNodes; ! Loop over blades and nodes call UA_TurnOff_param(AFInfo(p%AFindx(i,j)), ErrStat2, ErrMsg2) From 4d7aa4a96bcbfd01662924bb0ee4d70ab1b6fd16 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 22 Apr 2020 20:50:33 -0600 Subject: [PATCH 135/190] FVW: possibility to remove shed vorticity effect from UA, but UA with FVW needs more work --- modules/aerodyn/src/BEMT.f90 | 2 + modules/aerodyn/src/FVW.f90 | 4 + modules/aerodyn/src/FVW_Registry.txt | 16 +- modules/aerodyn/src/FVW_Types.f90 | 766 +++++++++--------- modules/aerodyn/src/UnsteadyAero.f90 | 20 +- modules/aerodyn/src/UnsteadyAero_Registry.txt | 1 + modules/aerodyn/src/UnsteadyAero_Types.f90 | 7 + 7 files changed, 420 insertions(+), 396 deletions(-) diff --git a/modules/aerodyn/src/BEMT.f90 b/modules/aerodyn/src/BEMT.f90 index 140f553499..74b80a1b63 100644 --- a/modules/aerodyn/src/BEMT.f90 +++ b/modules/aerodyn/src/BEMT.f90 @@ -568,6 +568,8 @@ subroutine BEMT_Init( InitInp, u, p, x, xd, z, OtherState, AFInfo, y, misc, Inte call cleanup() return end if + p%UA%ShedEffect=.True. ! This should be true when coupled to BEM. True in registry as default. + call BEMT_CheckInitUA(p, OtherState, AFInfo, ErrStat2, ErrMsg2) call SetErrStat( errStat2, errMsg2, errStat, errMsg, RoutineName ) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index defed9e3d9..1c49b21b36 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -999,6 +999,9 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E allocate ( OtherState%UA_Flag( InitInp%numBladeNodes, InitInp%NumBlades ), STAT = ErrStat2 ) OtherState%UA_Flag=m%UA_Flag if ( m%UA_Flag ) then + ErrMsg2='Unsteady aerodynamic (`AFAeroMod>1`) cannot be used with the free wake code (`WakeMod=3`) for now.'; ErrStat2=ErrID_Fatal; + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'UA_Init_Wrapper'); return + ! ---Condensed version of "BEMT_Set_UA_InitData" allocate(Init_UA_Data%c(InitInp%numBladeNodes,InitInp%numBlades), STAT = errStat2) do j = 1,InitInp%NumBlades; do i = 1,InitInp%numBladeNodes; @@ -1014,6 +1017,7 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E Init_UA_Data%a_s = InitInp%a_s ! m/s ! --- UA init call UA_Init( Init_UA_Data, u_UA, m%p_UA, xd%UA, OtherState%UA, m%y_UA, m%m_UA, interval, InitOutData_UA, ErrStat2, ErrMsg2); if(Failed())return + m%p_UA%ShedEffect=.False. !< Important, when coupling UA wih vortex code, shed vorticity is inherently accounted for ! --- Condensed version of "BEMT_CheckInitUA" do j = 1,InitInp%numBlades; do i = 1,InitInp%numBladeNodes; ! Loop over blades and nodes call UA_TurnOff_param(AFInfo(p%AFindx(i,j)), ErrStat2, ErrMsg2) diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 2650a3e8b9..2a37f27c3a 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -49,13 +49,6 @@ typedef ^ ^ DbKi typedef ^ ^ IntKi VTKCoord - - - "Switch for VTK outputs coordinate system" - typedef ^ ^ CHARACTER(1024) RootName - - - "RootName for writing output files" - -# ....... OtherStateType ............ -# FVW_OtherStateType -typedef FVW/FVW OtherStateType IntKi NULL - - - "Number of active near wake panels" - -# TODO UA -typedef ^ ^ UA_OtherStateType UA - - - "other states for UnsteadyAero" - -typedef ^ ^ Logical UA_Flag {:}{:} - - "logical flag indicating whether to use UnsteadyAero" - - # ....... MiscVars ............ # FVW_MiscVarType typedef FVW/FVW MiscVarType Logical FirstCall - - - "True if this is the first call to update state (used in CalcOutput)" - @@ -138,13 +131,20 @@ typedef ^ ^ ReKi # FVW_DiscreteStateType typedef FVW/FVW DiscreteStateType ReKi NULL - - - "Empty to satisfy framework" - # TODO UA -typedef ^ ^ UA_DiscreteStateType UA - - - "states for UnsteadyAero" - +typedef ^ ^ UA_DiscreteStateType UA - - - "states for UnsteadyAero" - #.......... ConstraintStateType ...... # FVW_ConstraintStateType typedef FVW/FVW ConstraintStateType Reki residual - - "Residual" - typedef ^ ^ Reki Gamma_LL :: - - "Circulation on the wing lifting line" - +# ....... OtherStateType ............ +# FVW_OtherStateType +typedef FVW/FVW OtherStateType IntKi NULL - - - "Number of active near wake panels" - +# TODO UA +typedef ^ ^ UA_OtherStateType UA - - - "other states for UnsteadyAero" - +typedef ^ ^ Logical UA_Flag {:}{:} - - "logical flag indicating whether to use UnsteadyAero" - + #.......... InitInputType ...... # FVW_InitInputType diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index e93bcaf5c2..91a4285898 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -76,13 +76,6 @@ MODULE FVW_Types CHARACTER(1024) :: RootName !< RootName for writing output files [-] END TYPE FVW_ParameterType ! ======================= -! ========= FVW_OtherStateType ======= - TYPE, PUBLIC :: FVW_OtherStateType - INTEGER(IntKi) :: NULL !< Number of active near wake panels [-] - TYPE(UA_OtherStateType) :: UA !< other states for UnsteadyAero [-] - LOGICAL , DIMENSION(:,:), ALLOCATABLE :: UA_Flag !< logical flag indicating whether to use UnsteadyAero [-] - END TYPE FVW_OtherStateType -! ======================= ! ========= FVW_MiscVarType ======= TYPE, PUBLIC :: FVW_MiscVarType LOGICAL :: FirstCall !< True if this is the first call to update state (used in CalcOutput) [-] @@ -170,6 +163,13 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Gamma_LL !< Circulation on the wing lifting line [-] END TYPE FVW_ConstraintStateType ! ======================= +! ========= FVW_OtherStateType ======= + TYPE, PUBLIC :: FVW_OtherStateType + INTEGER(IntKi) :: NULL !< Number of active near wake panels [-] + TYPE(UA_OtherStateType) :: UA !< other states for UnsteadyAero [-] + LOGICAL , DIMENSION(:,:), ALLOCATABLE :: UA_Flag !< logical flag indicating whether to use UnsteadyAero [-] + END TYPE FVW_OtherStateType +! ======================= ! ========= FVW_InitInputType ======= TYPE, PUBLIC :: FVW_InitInputType CHARACTER(1024) :: FVWFileName !< Main FVW input file name [-] @@ -758,9 +758,9 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg END DO ! I END SUBROUTINE FVW_UnPackParam - SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) - TYPE(FVW_OtherStateType), INTENT(IN) :: SrcOtherStateData - TYPE(FVW_OtherStateType), INTENT(INOUT) :: DstOtherStateData + SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_MiscVarType), INTENT(IN) :: SrcMiscData + TYPE(FVW_MiscVarType), INTENT(INOUT) :: DstMiscData INTEGER(IntKi), INTENT(IN ) :: CtrlCode INTEGER(IntKi), INTENT( OUT) :: ErrStat CHARACTER(*), INTENT( OUT) :: ErrMsg @@ -768,393 +768,104 @@ SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, E 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 = 'FVW_CopyOtherState' + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyMisc' ! ErrStat = ErrID_None ErrMsg = "" - DstOtherStateData%NULL = SrcOtherStateData%NULL - CALL UA_CopyOtherState( SrcOtherStateData%UA, DstOtherStateData%UA, CtrlCode, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) - IF (ErrStat>=AbortErrLev) RETURN -IF (ALLOCATED(SrcOtherStateData%UA_Flag)) THEN - i1_l = LBOUND(SrcOtherStateData%UA_Flag,1) - i1_u = UBOUND(SrcOtherStateData%UA_Flag,1) - i2_l = LBOUND(SrcOtherStateData%UA_Flag,2) - i2_u = UBOUND(SrcOtherStateData%UA_Flag,2) - IF (.NOT. ALLOCATED(DstOtherStateData%UA_Flag)) THEN - ALLOCATE(DstOtherStateData%UA_Flag(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + DstMiscData%FirstCall = SrcMiscData%FirstCall +IF (ALLOCATED(SrcMiscData%LE)) THEN + i1_l = LBOUND(SrcMiscData%LE,1) + i1_u = UBOUND(SrcMiscData%LE,1) + i2_l = LBOUND(SrcMiscData%LE,2) + i2_u = UBOUND(SrcMiscData%LE,2) + i3_l = LBOUND(SrcMiscData%LE,3) + i3_u = UBOUND(SrcMiscData%LE,3) + IF (.NOT. ALLOCATED(DstMiscData%LE)) THEN + ALLOCATE(DstMiscData%LE(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%UA_Flag.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%LE.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstOtherStateData%UA_Flag = SrcOtherStateData%UA_Flag + DstMiscData%LE = SrcMiscData%LE ENDIF - END SUBROUTINE FVW_CopyOtherState - - SUBROUTINE FVW_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) - TYPE(FVW_OtherStateType), INTENT(INOUT) :: OtherStateData - INTEGER(IntKi), INTENT( OUT) :: ErrStat - CHARACTER(*), INTENT( OUT) :: ErrMsg - CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyOtherState' - INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 -! - ErrStat = ErrID_None - ErrMsg = "" - CALL UA_DestroyOtherState( OtherStateData%UA, ErrStat, ErrMsg ) -IF (ALLOCATED(OtherStateData%UA_Flag)) THEN - DEALLOCATE(OtherStateData%UA_Flag) +IF (ALLOCATED(SrcMiscData%TE)) THEN + i1_l = LBOUND(SrcMiscData%TE,1) + i1_u = UBOUND(SrcMiscData%TE,1) + i2_l = LBOUND(SrcMiscData%TE,2) + i2_u = UBOUND(SrcMiscData%TE,2) + i3_l = LBOUND(SrcMiscData%TE,3) + i3_u = UBOUND(SrcMiscData%TE,3) + IF (.NOT. ALLOCATED(DstMiscData%TE)) THEN + ALLOCATE(DstMiscData%TE(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%TE.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstMiscData%TE = SrcMiscData%TE ENDIF - END SUBROUTINE FVW_DestroyOtherState - - SUBROUTINE FVW_PackOtherState( 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(FVW_OtherStateType), 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 = 'FVW_PackOtherState' - ! 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 ! NULL - ! Allocate buffers for subtypes, if any (we'll get sizes from these) - Int_BufSz = Int_BufSz + 3 ! UA: size of buffers for each call to pack subtype - CALL UA_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%UA, ErrStat2, ErrMsg2, .TRUE. ) ! UA - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - IF (ErrStat >= AbortErrLev) RETURN - - IF(ALLOCATED(Re_Buf)) THEN ! UA - Re_BufSz = Re_BufSz + SIZE( Re_Buf ) - DEALLOCATE(Re_Buf) - END IF - IF(ALLOCATED(Db_Buf)) THEN ! UA - Db_BufSz = Db_BufSz + SIZE( Db_Buf ) - DEALLOCATE(Db_Buf) - END IF - IF(ALLOCATED(Int_Buf)) THEN ! UA - Int_BufSz = Int_BufSz + SIZE( Int_Buf ) - DEALLOCATE(Int_Buf) - END IF - Int_BufSz = Int_BufSz + 1 ! UA_Flag allocated yes/no - IF ( ALLOCATED(InData%UA_Flag) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! UA_Flag upper/lower bounds for each dimension - Int_BufSz = Int_BufSz + SIZE(InData%UA_Flag) ! UA_Flag +IF (ALLOCATED(SrcMiscData%r_LL)) THEN + i1_l = LBOUND(SrcMiscData%r_LL,1) + i1_u = UBOUND(SrcMiscData%r_LL,1) + i2_l = LBOUND(SrcMiscData%r_LL,2) + i2_u = UBOUND(SrcMiscData%r_LL,2) + i3_l = LBOUND(SrcMiscData%r_LL,3) + i3_u = UBOUND(SrcMiscData%r_LL,3) + i4_l = LBOUND(SrcMiscData%r_LL,4) + i4_u = UBOUND(SrcMiscData%r_LL,4) + IF (.NOT. ALLOCATED(DstMiscData%r_LL)) THEN + ALLOCATE(DstMiscData%r_LL(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 DstMiscData%r_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF END IF - 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 + DstMiscData%r_LL = SrcMiscData%r_LL +ENDIF +IF (ALLOCATED(SrcMiscData%s_LL)) THEN + i1_l = LBOUND(SrcMiscData%s_LL,1) + i1_u = UBOUND(SrcMiscData%s_LL,1) + i2_l = LBOUND(SrcMiscData%s_LL,2) + i2_u = UBOUND(SrcMiscData%s_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%s_LL)) THEN + ALLOCATE(DstMiscData%s_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%s_LL.', 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 + DstMiscData%s_LL = SrcMiscData%s_LL +ENDIF +IF (ALLOCATED(SrcMiscData%chord_LL)) THEN + i1_l = LBOUND(SrcMiscData%chord_LL,1) + i1_u = UBOUND(SrcMiscData%chord_LL,1) + i2_l = LBOUND(SrcMiscData%chord_LL,2) + i2_u = UBOUND(SrcMiscData%chord_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%chord_LL)) THEN + ALLOCATE(DstMiscData%chord_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%chord_LL.', 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:Int_Xferred+(1)-1 ) = InData%NULL - Int_Xferred = Int_Xferred + 1 - CALL UA_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%UA, ErrStat2, ErrMsg2, OnlySize ) ! UA - 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 - IF ( .NOT. ALLOCATED(InData%UA_Flag) ) 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%UA_Flag,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_Flag,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%UA_Flag,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_Flag,2) - Int_Xferred = Int_Xferred + 2 - - IF (SIZE(InData%UA_Flag)>0) IntKiBuf ( Int_Xferred:Int_Xferred+SIZE(InData%UA_Flag)-1 ) = TRANSFER(PACK( InData%UA_Flag ,.TRUE.), IntKiBuf(1), SIZE(InData%UA_Flag)) - Int_Xferred = Int_Xferred + SIZE(InData%UA_Flag) - END IF - END SUBROUTINE FVW_PackOtherState - - SUBROUTINE FVW_UnPackOtherState( 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(FVW_OtherStateType), 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 - LOGICAL :: mask0 - LOGICAL, ALLOCATABLE :: mask1(:) - LOGICAL, ALLOCATABLE :: mask2(:,:) - LOGICAL, ALLOCATABLE :: mask3(:,:,:) - LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) - LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) - 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) :: ErrStat2 - CHARACTER(ErrMsgLen) :: ErrMsg2 - CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackOtherState' - ! 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%NULL = 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 UA_UnpackOtherState( Re_Buf, Db_Buf, Int_Buf, OutData%UA, ErrStat2, ErrMsg2 ) ! UA - 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) - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UA_Flag 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 - IF (ALLOCATED(OutData%UA_Flag)) DEALLOCATE(OutData%UA_Flag) - ALLOCATE(OutData%UA_Flag(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UA_Flag.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - mask2 = .TRUE. - IF (SIZE(OutData%UA_Flag)>0) OutData%UA_Flag = UNPACK( TRANSFER( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%UA_Flag))-1 ), OutData%UA_Flag), mask2,.TRUE.) - Int_Xferred = Int_Xferred + SIZE(OutData%UA_Flag) - DEALLOCATE(mask2) - END IF - END SUBROUTINE FVW_UnPackOtherState - - SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) - TYPE(FVW_MiscVarType), INTENT(IN) :: SrcMiscData - TYPE(FVW_MiscVarType), INTENT(INOUT) :: DstMiscData - 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 = 'FVW_CopyMisc' -! - ErrStat = ErrID_None - ErrMsg = "" - DstMiscData%FirstCall = SrcMiscData%FirstCall -IF (ALLOCATED(SrcMiscData%LE)) THEN - i1_l = LBOUND(SrcMiscData%LE,1) - i1_u = UBOUND(SrcMiscData%LE,1) - i2_l = LBOUND(SrcMiscData%LE,2) - i2_u = UBOUND(SrcMiscData%LE,2) - i3_l = LBOUND(SrcMiscData%LE,3) - i3_u = UBOUND(SrcMiscData%LE,3) - IF (.NOT. ALLOCATED(DstMiscData%LE)) THEN - ALLOCATE(DstMiscData%LE(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%LE.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstMiscData%LE = SrcMiscData%LE -ENDIF -IF (ALLOCATED(SrcMiscData%TE)) THEN - i1_l = LBOUND(SrcMiscData%TE,1) - i1_u = UBOUND(SrcMiscData%TE,1) - i2_l = LBOUND(SrcMiscData%TE,2) - i2_u = UBOUND(SrcMiscData%TE,2) - i3_l = LBOUND(SrcMiscData%TE,3) - i3_u = UBOUND(SrcMiscData%TE,3) - IF (.NOT. ALLOCATED(DstMiscData%TE)) THEN - ALLOCATE(DstMiscData%TE(i1_l:i1_u,i2_l:i2_u,i3_l:i3_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%TE.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstMiscData%TE = SrcMiscData%TE -ENDIF -IF (ALLOCATED(SrcMiscData%r_LL)) THEN - i1_l = LBOUND(SrcMiscData%r_LL,1) - i1_u = UBOUND(SrcMiscData%r_LL,1) - i2_l = LBOUND(SrcMiscData%r_LL,2) - i2_u = UBOUND(SrcMiscData%r_LL,2) - i3_l = LBOUND(SrcMiscData%r_LL,3) - i3_u = UBOUND(SrcMiscData%r_LL,3) - i4_l = LBOUND(SrcMiscData%r_LL,4) - i4_u = UBOUND(SrcMiscData%r_LL,4) - IF (.NOT. ALLOCATED(DstMiscData%r_LL)) THEN - ALLOCATE(DstMiscData%r_LL(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 DstMiscData%r_LL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstMiscData%r_LL = SrcMiscData%r_LL -ENDIF -IF (ALLOCATED(SrcMiscData%s_LL)) THEN - i1_l = LBOUND(SrcMiscData%s_LL,1) - i1_u = UBOUND(SrcMiscData%s_LL,1) - i2_l = LBOUND(SrcMiscData%s_LL,2) - i2_u = UBOUND(SrcMiscData%s_LL,2) - IF (.NOT. ALLOCATED(DstMiscData%s_LL)) THEN - ALLOCATE(DstMiscData%s_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%s_LL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstMiscData%s_LL = SrcMiscData%s_LL -ENDIF -IF (ALLOCATED(SrcMiscData%chord_LL)) THEN - i1_l = LBOUND(SrcMiscData%chord_LL,1) - i1_u = UBOUND(SrcMiscData%chord_LL,1) - i2_l = LBOUND(SrcMiscData%chord_LL,2) - i2_u = UBOUND(SrcMiscData%chord_LL,2) - IF (.NOT. ALLOCATED(DstMiscData%chord_LL)) THEN - ALLOCATE(DstMiscData%chord_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%chord_LL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstMiscData%chord_LL = SrcMiscData%chord_LL -ENDIF -IF (ALLOCATED(SrcMiscData%s_CP_LL)) THEN - i1_l = LBOUND(SrcMiscData%s_CP_LL,1) - i1_u = UBOUND(SrcMiscData%s_CP_LL,1) - i2_l = LBOUND(SrcMiscData%s_CP_LL,2) - i2_u = UBOUND(SrcMiscData%s_CP_LL,2) - IF (.NOT. ALLOCATED(DstMiscData%s_CP_LL)) THEN - ALLOCATE(DstMiscData%s_CP_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%s_CP_LL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF + DstMiscData%chord_LL = SrcMiscData%chord_LL +ENDIF +IF (ALLOCATED(SrcMiscData%s_CP_LL)) THEN + i1_l = LBOUND(SrcMiscData%s_CP_LL,1) + i1_u = UBOUND(SrcMiscData%s_CP_LL,1) + i2_l = LBOUND(SrcMiscData%s_CP_LL,2) + i2_u = UBOUND(SrcMiscData%s_CP_LL,2) + IF (.NOT. ALLOCATED(DstMiscData%s_CP_LL)) THEN + ALLOCATE(DstMiscData%s_CP_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%s_CP_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF END IF DstMiscData%s_CP_LL = SrcMiscData%s_CP_LL ENDIF @@ -5496,6 +5207,295 @@ SUBROUTINE FVW_UnPackConstrState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, END IF END SUBROUTINE FVW_UnPackConstrState + SUBROUTINE FVW_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg ) + TYPE(FVW_OtherStateType), INTENT(IN) :: SrcOtherStateData + TYPE(FVW_OtherStateType), INTENT(INOUT) :: DstOtherStateData + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_CopyOtherState' +! + ErrStat = ErrID_None + ErrMsg = "" + DstOtherStateData%NULL = SrcOtherStateData%NULL + CALL UA_CopyOtherState( SrcOtherStateData%UA, DstOtherStateData%UA, CtrlCode, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + IF (ErrStat>=AbortErrLev) RETURN +IF (ALLOCATED(SrcOtherStateData%UA_Flag)) THEN + i1_l = LBOUND(SrcOtherStateData%UA_Flag,1) + i1_u = UBOUND(SrcOtherStateData%UA_Flag,1) + i2_l = LBOUND(SrcOtherStateData%UA_Flag,2) + i2_u = UBOUND(SrcOtherStateData%UA_Flag,2) + IF (.NOT. ALLOCATED(DstOtherStateData%UA_Flag)) THEN + ALLOCATE(DstOtherStateData%UA_Flag(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%UA_Flag.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%UA_Flag = SrcOtherStateData%UA_Flag +ENDIF + END SUBROUTINE FVW_CopyOtherState + + SUBROUTINE FVW_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) + TYPE(FVW_OtherStateType), INTENT(INOUT) :: OtherStateData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_DestroyOtherState' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + CALL UA_DestroyOtherState( OtherStateData%UA, ErrStat, ErrMsg ) +IF (ALLOCATED(OtherStateData%UA_Flag)) THEN + DEALLOCATE(OtherStateData%UA_Flag) +ENDIF + END SUBROUTINE FVW_DestroyOtherState + + SUBROUTINE FVW_PackOtherState( 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(FVW_OtherStateType), 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 = 'FVW_PackOtherState' + ! 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 ! NULL + ! Allocate buffers for subtypes, if any (we'll get sizes from these) + Int_BufSz = Int_BufSz + 3 ! UA: size of buffers for each call to pack subtype + CALL UA_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%UA, ErrStat2, ErrMsg2, .TRUE. ) ! UA + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + IF (ErrStat >= AbortErrLev) RETURN + + IF(ALLOCATED(Re_Buf)) THEN ! UA + Re_BufSz = Re_BufSz + SIZE( Re_Buf ) + DEALLOCATE(Re_Buf) + END IF + IF(ALLOCATED(Db_Buf)) THEN ! UA + Db_BufSz = Db_BufSz + SIZE( Db_Buf ) + DEALLOCATE(Db_Buf) + END IF + IF(ALLOCATED(Int_Buf)) THEN ! UA + Int_BufSz = Int_BufSz + SIZE( Int_Buf ) + DEALLOCATE(Int_Buf) + END IF + Int_BufSz = Int_BufSz + 1 ! UA_Flag allocated yes/no + IF ( ALLOCATED(InData%UA_Flag) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! UA_Flag upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%UA_Flag) ! UA_Flag + END IF + 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:Int_Xferred+(1)-1 ) = InData%NULL + Int_Xferred = Int_Xferred + 1 + CALL UA_PackOtherState( Re_Buf, Db_Buf, Int_Buf, InData%UA, ErrStat2, ErrMsg2, OnlySize ) ! UA + 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 + IF ( .NOT. ALLOCATED(InData%UA_Flag) ) 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%UA_Flag,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_Flag,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%UA_Flag,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_Flag,2) + Int_Xferred = Int_Xferred + 2 + + IF (SIZE(InData%UA_Flag)>0) IntKiBuf ( Int_Xferred:Int_Xferred+SIZE(InData%UA_Flag)-1 ) = TRANSFER(PACK( InData%UA_Flag ,.TRUE.), IntKiBuf(1), SIZE(InData%UA_Flag)) + Int_Xferred = Int_Xferred + SIZE(InData%UA_Flag) + END IF + END SUBROUTINE FVW_PackOtherState + + SUBROUTINE FVW_UnPackOtherState( 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(FVW_OtherStateType), 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 + LOGICAL :: mask0 + LOGICAL, ALLOCATABLE :: mask1(:) + LOGICAL, ALLOCATABLE :: mask2(:,:) + LOGICAL, ALLOCATABLE :: mask3(:,:,:) + LOGICAL, ALLOCATABLE :: mask4(:,:,:,:) + LOGICAL, ALLOCATABLE :: mask5(:,:,:,:,:) + 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) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'FVW_UnPackOtherState' + ! 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%NULL = 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 UA_UnpackOtherState( Re_Buf, Db_Buf, Int_Buf, OutData%UA, ErrStat2, ErrMsg2 ) ! UA + 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) + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UA_Flag 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 + IF (ALLOCATED(OutData%UA_Flag)) DEALLOCATE(OutData%UA_Flag) + ALLOCATE(OutData%UA_Flag(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UA_Flag.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + ALLOCATE(mask2(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating mask2.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + mask2 = .TRUE. + IF (SIZE(OutData%UA_Flag)>0) OutData%UA_Flag = UNPACK( TRANSFER( IntKiBuf ( Int_Xferred:Int_Xferred+(SIZE(OutData%UA_Flag))-1 ), OutData%UA_Flag), mask2,.TRUE.) + Int_Xferred = Int_Xferred + SIZE(OutData%UA_Flag) + DEALLOCATE(mask2) + END IF + END SUBROUTINE FVW_UnPackOtherState + SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrStat, ErrMsg ) TYPE(FVW_InitInputType), INTENT(INOUT) :: SrcInitInputData TYPE(FVW_InitInputType), INTENT(INOUT) :: DstInitInputData diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index bbdfc1d32d..0ae14248e5 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -422,7 +422,6 @@ subroutine ComputeKelvinChain( i, j, u, p, xd, OtherState, misc, AFInfo, KC, BL_ KC%dalpha0 = KC%alpha_filt_cur - BL_p%alpha0 - ! Compute Kalpha using Eqn 1.7 @@ -474,11 +473,12 @@ subroutine ComputeKelvinChain( i, j, u, p, xd, OtherState, misc, AFInfo, KC, BL_ !bjj: todo: should we check the denominator to make sure it doesn't go to 0? - +!TODO:MANU:Needed for Phi_alpha_nc and Phi_q_nc KC%k_alpha = 1.0_ReKi / ( (1.0_ReKi - M) + (BL_p%C_nalpha/2.0_ReKi) * M**2 * beta_M * (BL_p%A1*BL_p%b1 + BL_p%A2*BL_p%b2) ) ! Eqn 1.11a KC%k_q = 1.0_ReKi / ( (1.0_ReKi - M) + BL_p%C_nalpha * M**2 * beta_M * (BL_p%A1*BL_p%b1 + BL_p%A2*BL_p%b2) ) ! Eqn 1.11b T_I = p%c(i,j) / p%a_s ! Eqn 1.11c +!TODO:MANU:Needed Phi_alpha_nc and Phi_q_nc KC%T_alpha = T_I * KC%k_alpha * 0.75 ! Eqn 1.10a KC%T_q = T_I * KC%k_q * 0.75 ! Eqn 1.10b @@ -493,9 +493,14 @@ subroutine ComputeKelvinChain( i, j, u, p, xd, OtherState, misc, AFInfo, KC, BL_ KC%Cn_q_nc = -1.0_ReKi*KC%T_q * ( KC%Kq_f - KC%Kprime_q ) / M ! Eqn 1.19a KC%Cn_alpha_q_nc = KC%Cn_alpha_nc + KC%Cn_q_nc ! Eqn 1.17 - + +if (p%ShedEffect) then KC%X1 = Get_ExpEqn( KC%ds*beta_M_Sqrd*BL_p%b1, 1.0_ReKi, xd%X1_minus1(i,j), BL_p%A1*(KC%alpha_filt_cur - alpha_filt_minus1), 0.0_ReKi ) ! Eqn 1.15a KC%X2 = Get_ExpEqn( KC%ds*beta_M_Sqrd*BL_p%b2, 1.0_ReKi, xd%X2_minus1(i,j), BL_p%A2*(KC%alpha_filt_cur - alpha_filt_minus1), 0.0_ReKi ) ! Eqn 1.15b +else + KC%X1 = 0.0_ReKi ! u%alpha (and alpha_filt_cur) contains shed vorticity effect already + KC%X2 = 0.0_ReKi ! so that alpha_e = u%alpha-alpha0 directly +endif KC%alpha_e = (KC%alpha_filt_cur - BL_p%alpha0) - KC%X1 - KC%X2 ! Eqn 1.14 @@ -503,9 +508,14 @@ subroutine ComputeKelvinChain( i, j, u, p, xd, OtherState, misc, AFInfo, KC, BL_ if ( p%UAMod == UA_Gonzalez ) then ! Compute X3 and X4 using Eqn 1.16a and then add Cn_q_circ (Eqn 1.16) to the previously computed Cn_alpha_q_circ +if (p%ShedEffect) then KC%X3 = Get_ExpEqn( KC%ds*beta_M_Sqrd*BL_p%b1, 1.0_ReKi, xd%X3_minus1(i,j), BL_p%A1*(KC%q_f_cur - q_f_minus1), 0.0_ReKi ) ! Eqn 1.16a [1] KC%X4 = Get_ExpEqn( KC%ds*beta_M_Sqrd*BL_p%b2, 1.0_ReKi, xd%X4_minus1(i,j), BL_p%A2*(KC%q_f_cur - q_f_minus1), 0.0_ReKi ) ! Eqn 1.16a [2] - +else + KC%X3 = 0.0_ReKi ! Similar to X1 and X2, we assumed that this effect is already included + KC%X4 = 0.0_ReKi +endif + KC%Cn_q_circ = KC%C_nalpha_circ*KC%q_f_cur/2.0 - KC%X3 - KC%X4 ! Eqn 1.16 else ! these aren't used (they are possibly output to UA_OUT file, though) @@ -516,7 +526,7 @@ subroutine ComputeKelvinChain( i, j, u, p, xd, OtherState, misc, AFInfo, KC, BL_ K3prime_q = Get_ExpEqn( BL_p%b5*beta_M_Sqrd*KC%ds, 1.0_ReKi, xd%K3prime_q_minus1(i,j), BL_p%A5*(KC%q_f_cur - q_f_minus1), 0.0_ReKi ) ! Eqn 1.26 KC%Cm_q_circ = -BL_p%C_nalpha*(KC%q_f_cur - K3prime_q)*p%c(i,j)/(16.0_ReKi*beta_M*u%U) ! Eqn 1.25 - + KC%Cn_pot = KC%Cn_alpha_q_circ + KC%Cn_alpha_q_nc ! Eqn 1.20 [2a] k_mq = 7.0_ReKi / (15.0_ReKi*(1.0_ReKi-M) + 1.5_ReKi * BL_p%C_nalpha * BL_p%A5 * BL_p%b5 * beta_M * M**2) ! Eqn 1.29 [2] ! CHECK THAT DENOM ISN'T ZERO! diff --git a/modules/aerodyn/src/UnsteadyAero_Registry.txt b/modules/aerodyn/src/UnsteadyAero_Registry.txt index be524e7bd6..f10e9aefbe 100644 --- a/modules/aerodyn/src/UnsteadyAero_Registry.txt +++ b/modules/aerodyn/src/UnsteadyAero_Registry.txt @@ -183,6 +183,7 @@ typedef ^ ^ CHARACTER(2 typedef ^ ^ CHARACTER(20) OutSFmt - - - "Output format for header strings" - typedef ^ ^ CHARACTER(1) Delim - - - "Delimiter string for outputs, defaults to tab-delimiters" - typedef ^ ^ INTEGER UnOutFile - - - "File unit for the UnsteadyAero outputs" - +typedef ^ ^ Logical ShedEffect - .True. - "Include the effect of shed vorticity. If False, the input alpha is assumed to already contain this effect (e.g. vortex methods)" - # # diff --git a/modules/aerodyn/src/UnsteadyAero_Types.f90 b/modules/aerodyn/src/UnsteadyAero_Types.f90 index ffe61ed6b8..ce1487abe1 100644 --- a/modules/aerodyn/src/UnsteadyAero_Types.f90 +++ b/modules/aerodyn/src/UnsteadyAero_Types.f90 @@ -189,6 +189,7 @@ MODULE UnsteadyAero_Types CHARACTER(20) :: OutSFmt !< Output format for header strings [-] CHARACTER(1) :: Delim !< Delimiter string for outputs, defaults to tab-delimiters [-] INTEGER(IntKi) :: UnOutFile !< File unit for the UnsteadyAero outputs [-] + LOGICAL :: ShedEffect = .True. !< Include the effect of shed vorticity. If False, the input alpha is assumed to already contain this effect (e.g. vortex methods) [-] END TYPE UA_ParameterType ! ======================= ! ========= UA_InputType ======= @@ -4616,6 +4617,7 @@ SUBROUTINE UA_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg ) DstParamData%OutSFmt = SrcParamData%OutSFmt DstParamData%Delim = SrcParamData%Delim DstParamData%UnOutFile = SrcParamData%UnOutFile + DstParamData%ShedEffect = SrcParamData%ShedEffect END SUBROUTINE UA_CopyParam SUBROUTINE UA_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -4684,6 +4686,7 @@ SUBROUTINE UA_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 1*LEN(InData%OutSFmt) ! OutSFmt Int_BufSz = Int_BufSz + 1*LEN(InData%Delim) ! Delim Int_BufSz = Int_BufSz + 1 ! UnOutFile + Int_BufSz = Int_BufSz + 1 ! ShedEffect IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4757,6 +4760,8 @@ SUBROUTINE UA_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si END DO ! I IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%UnOutFile Int_Xferred = Int_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%ShedEffect , IntKiBuf(1), 1) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE UA_PackParam SUBROUTINE UA_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4849,6 +4854,8 @@ SUBROUTINE UA_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg END DO ! I OutData%UnOutFile = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 + OutData%ShedEffect = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE UA_UnPackParam SUBROUTINE UA_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) From 5bb8b2d8cd689bcf35a98b5b649c4f99e6d7ec8f Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 23 Apr 2020 17:44:10 -0600 Subject: [PATCH 136/190] FVW: adding Biot-Savart particle functions and tests --- modules/aerodyn/src/FVW_BiotSavart.f90 | 126 +++++++++++++++++++------ modules/aerodyn/src/FVW_Tests.f90 | 66 ++++++++++++- 2 files changed, 158 insertions(+), 34 deletions(-) diff --git a/modules/aerodyn/src/FVW_BiotSavart.f90 b/modules/aerodyn/src/FVW_BiotSavart.f90 index 0ed5c7be39..cb15a379ea 100644 --- a/modules/aerodyn/src/FVW_BiotSavart.f90 +++ b/modules/aerodyn/src/FVW_BiotSavart.f90 @@ -9,14 +9,17 @@ module FVW_BiotSavart real(ReKi),parameter :: MIN_EXP_VALUE=-10.0_ReKi real(ReKi),parameter :: MINDENOM=0.0_ReKi ! real(ReKi),parameter :: MINDENOM=1e-15_ReKi - real(ReKi),parameter :: MINNORMSIMP=1e-6_ReKi + real(ReKi),parameter :: MINNORM=1e-4 integer(IntKi), parameter :: idRegNone = 0 integer(IntKi), parameter :: idRegRankine = 1 integer(IntKi), parameter :: idRegLambOseen = 2 integer(IntKi), parameter :: idRegVatistas = 3 integer(IntKi), parameter :: idRegOffset = 4 + integer(IntKi), parameter :: idRegExp = 1 + integer(IntKi), parameter :: idRegCompact = 2 integer(IntKi), parameter, dimension(5) :: idRegVALID = (/idRegNone,idRegRankine,idRegLambOseen,idRegVatistas,idRegOffset/) + integer(IntKi), parameter, dimension(3) :: idRegPartVALID = (/idRegNone,idRegExp,idRegCompact/) real(ReKi),parameter :: fourpi_inv = 0.25_ReKi / ACOS(-1.0_Reki ) @@ -100,19 +103,19 @@ end subroutine ui_seg_11 subroutine ui_seg(iCPStart, iCPEnd, CPs, & iSegStart, iSegEnd, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, & RegFunction, RegParam, Uind_out) - real(ReKi), dimension(:,:), intent(in) :: CPs !< Control points (3 x nCPs++) - integer(IntKi), intent(in) :: iCPStart !< Index where we start in Control points array - integer(IntKi), intent(in) :: iCPEnd !< Index where we end in Control points array - real(ReKi), dimension(3,nSegPTot), intent(in) :: SegPoints !< Segment points - integer(IntKi), dimension(:,:), intent(in) :: SegConnct !< Connectivity, indices of segments points iSeg1, iSeg2, iDepth, iSpan - real(ReKi), dimension(nSegTot), intent(in) :: SegGamma !< Segment circulation - integer(IntKi),intent(in) :: iSegStart !< Index in SegConnct, and SegGamma where we start - integer(IntKi),intent(in) :: iSegEnd !< Index in SegConnct, and SegGamma where we end - integer(IntKi), intent(in) :: nSegTot !< Total number of segments - integer(IntKi), intent(in) :: nSegPTot !< Total number of segment points - integer(IntKi), intent(in) :: RegFunction !< Regularization model - real(ReKi), dimension(nSegTot), intent(in) :: RegParam !< Regularization parameter - real(ReKi), dimension(:,:), intent(inout) :: Uind_out !< Induced velocity vector - Side effects!!! (3 x nCPs++) + real(ReKi), dimension(:,:), intent(in) :: CPs !< Control points (3 x nCPs++) + integer(IntKi), intent(in) :: iCPStart !< Index where we start in Control points array + integer(IntKi), intent(in) :: iCPEnd !< Index where we end in Control points array + real(ReKi), dimension(:,:), intent(in) :: SegPoints !< Segment points (3 x nSegPTot) + integer(IntKi), dimension(:,:), intent(in) :: SegConnct !< Connectivity, indices of segments points iSeg1, iSeg2, iDepth, iSpan + real(ReKi), dimension(:), intent(in) :: SegGamma !< Segment circulation (nSegTot) + integer(IntKi), intent(in) :: iSegStart !< Index in SegConnct, and SegGamma where we start + integer(IntKi), intent(in) :: iSegEnd !< Index in SegConnct, and SegGamma where we end + integer(IntKi), intent(in) :: nSegTot !< Total number of segments + integer(IntKi), intent(in) :: nSegPTot !< Total number of segment points + integer(IntKi), intent(in) :: RegFunction !< Regularization model + real(ReKi), dimension(:), intent(in) :: RegParam !< Regularization parameter (nSegTot) + real(ReKi), dimension(:,:) , intent(inout) :: Uind_out !< Induced velocity vector - Side effects!!! (3 x nCPs++) ! Variables integer(IntKi) :: icp, is real(ReKi), dimension(3) :: Uind !< @@ -312,28 +315,91 @@ subroutine ui_seg(iCPStart, iCPEnd, CPs, & !$OMP END DO !$OMP END PARALLEL case default - print*,'[ERROR] Unknown RegFunction',RegFunction - stop + print*,'[ERROR] Unknown RegFunction for segment',RegFunction + STOP end select +end subroutine ui_seg -end subroutine +!> Induced velocity from `nPart` particles at `nCPs` control points. The velocity gradient is not computed +subroutine ui_part_nograd(CPs, Part, Alpha, RegFunction, RegParam, UIout, nCPs, nPart) + integer(IntKi), intent(in) :: nCPs + integer(IntKi), intent(in) :: nPart + real(ReKi), dimension(:,:), intent(in) :: CPs !< Control points (3 x nCPs) + real(ReKi), dimension(:,:), intent(inout) :: UIout !< Induced velocity, with side effects! (3 x nCPs) + real(ReKi), dimension(:,:), intent(in) :: Part !< Particle positions (3 x nPart) + real(ReKi), dimension(:,:), intent(in) :: Alpha !< Particle intensity [m^3/s] (3 x nPart) omega dV= alpha + integer(IntKi), intent(in) :: RegFunction !< Regularization function + real(ReKi), dimension(:), intent(in) :: RegParam !< Regularization parameter (nPart) + real(ReKi), dimension(3) :: UItmp !< + real(ReKi), dimension(3) :: DP !< + integer :: icp,ip + ! TODO: inlining of regularization + !$OMP PARALLEL DEFAULT(SHARED) + !$OMP DO PRIVATE(icp,ip, DP, UItmp) schedule(runtime) + do icp=1,nCPs ! loop on CPs + do ip=1,nPart ! loop on particles + UItmp(1:3) = 0.0_ReKi + DP(1:3) = CPs(1:3,icp)-Part(1:3,ip) + call ui_part_nograd_11(DP, Alpha(1:3,ip), RegFunction , RegParam(ip), UItmp) + UIout(1:3,icp)=UIout(1:3,icp)+UItmp(1:3) + enddo! loop on particles + enddo ! loop CPs + !$OMP END DO + !$OMP END PARALLEL +end subroutine ui_part_nograd +!> Induced velocity from 1 particle at 1 control point. The velocity gradient is not computed +subroutine ui_part_nograd_11(DeltaP, Alpha, RegFunction, RegParam, Ui) + real(ReKi), dimension(3), intent(out) :: Ui !< no side effects + real(ReKi), dimension(3), intent(in) :: DeltaP !< CP-PP "control point - particle point" + real(ReKi), dimension(3), intent(in) :: Alpha !< Particle intensity [m^2/s] alpha=om.dV + integer(IntKi), intent(in) :: RegFunction !< + real(ReKi), intent(in) :: RegParam !< + real(ReKi),dimension(3) :: C !< Cross product of Alpha and r + real(ReKi) :: E !< Exponential poart for the mollifider + real(ReKi) :: r3_inv !< + real(ReKi) :: rDeltaP !< norm , distance between point and particle + real(ReKi) :: ScalarPart !< the part containing the inverse of the distance, but not 4pi, Mollifier + rDeltaP=sqrt(DeltaP(1)**2+ DeltaP(2)**2+ DeltaP(3)**2)! norm + if (rDeltaP Velocity induced by one vortex quad on nCPs Control Points subroutine ui_quad_n1(CPs, nCPs, P1, P2, P3, P4, Gamm, RegFunction, RegParam, Uind) - ! Arguments declarations - integer, intent(in) :: nCPs !< - real(ReKi), dimension(:,:), intent(in) :: CPs !< 3 x "nCPs"++ - real(ReKi), dimension(3), intent(in) :: P1,P2,P3,P4 !< Coordinates of vortex quadrilateral - real(ReKi), intent(in) :: Gamm - integer(IntKi) , intent(in) :: RegFunction !< Regularization model (e.g. LambOseen) - real(ReKi), intent(in) :: RegParam !< Regularization parameter [m] - real(ReKi), dimension(:,:), intent(inout) :: Uind !< side effects!!! 3 x "nCPs++" - ! Variable declarations - real(ReKi), dimension(3) :: CP !< - real(ReKi), dimension(3) :: Uindtmp !< - real(ReKi), dimension(3) :: DP1 !< - real(ReKi), dimension(3) :: DP2 !< + integer, intent(in) :: nCPs !< + real(ReKi), dimension(:,:), intent(in) :: CPs !< 3 x "nCPs"++ + real(ReKi), dimension(3), intent(in) :: P1,P2,P3,P4 !< Coordinates of vortex quadrilateral + real(ReKi), intent(in) :: Gamm + integer(IntKi) , intent(in) :: RegFunction !< Regularization model (e.g. LambOseen) + real(ReKi), intent(in) :: RegParam !< Regularization parameter [m] + real(ReKi), dimension(:,:), intent(inout) :: Uind !< side effects!!! 3 x "nCPs++" + real(ReKi), dimension(3) :: CP !< + real(ReKi), dimension(3) :: Uindtmp !< + real(ReKi), dimension(3) :: DP1 !< + real(ReKi), dimension(3) :: DP2 !< integer :: icp ! !OMP PARALLEL DEFAULT(SHARED) diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index 836fda7526..e158e53559 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -266,9 +266,9 @@ subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) ! Method 2 call ui_seg_11(CP-P1, CP-P2, SegGamma1, RegFunction, RegParam1, U1) ! Test - print*,'Reg function', RegFunction, 'CP',CP - print*,'Uind_out',Uind_out - print*,'U1 ',U1 + !print*,'Reg function', RegFunction, 'CP',CP + !print*,'Uind_out',Uind_out + !print*,'U1 ',U1 call test_almost_equal('Uind method1/2', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) !call test_almost_equal('Uind method1/2', U1, Uind_out(:,1), 1e-4, .false.,.true.) enddo @@ -313,6 +313,63 @@ subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) enddo end subroutine + !> + subroutine Test_BiotSavart_Part(ErrStat, ErrMsg) + integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation + character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + real(ReKi), dimension(3) :: P1,CP + real(ReKi), dimension(3) :: U1 + real(ReKi), dimension(3) :: PartAlpha1 !< Particle intensity alpha=om.dV [m^3/s] + real(ReKi) :: RegParam1 !< + integer(IntKi) :: i1,i2 + integer(IntKi) :: RegFunction + integer(IntKi), parameter :: nPart = 1 + integer(IntKi), parameter :: nCPs = 1 + real(ReKi), dimension(3,nCPs) :: CPs !< Control points + real(ReKi), dimension(3,nPart):: PartPoints !< Particle points + real(ReKi), dimension(3,nPart):: PartAlpha !< Particle circulation + real(ReKi), dimension(nPart) :: RegParam !< Regularization parameter + real(ReKi), dimension(3,nCPs) :: Uind_out !< Induced velocity vector - Side effects!!! + real(ReKi), dimension(3,4) :: CPs_test !< + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + ! --- Test that the two functions return the same values + P1=(/0.0, 0.0, -1.0 /) + CPs_test(:,1) = (/ 0.0, 0., 0.0 /) ! Middle + CPs_test(:,2) = P1 ! Extremity + CPs_test(:,3) = (/ 0.01, 0.01, -0.9 /) ! Close + CPs_test(:,4) = (/ 10., 0., 0.0 /) ! Far + do i1=1,3 + do i2 = 1, size(CPs_test,2) + ! Segment param + CP = CPs_test(:,i2) + PartAlpha1(1:2) = 0 + PartAlpha1(3 ) = 2 + RegParam1 = 0.5 + ! One segment param + PartPoints(:,1) = P1 + PartAlpha(:,1) = PartAlpha1 + RegParam(:) = RegParam1 + CPs (:,1) = CP + RegFunction = idRegPartVALID(i1) + ! Method 1 + Uind_out =0.0_ReKi + call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind_out, nCPs, nPart) + ! Method 2 + call ui_part_nograd_11(CP-P1, PartAlpha1, RegFunction, RegParam1, U1) + ! Test + !print*,'Reg function', RegFunction, 'CP',CP + !print*,'Uind_out',Uind_out + !print*,'U1 ',U1 + call test_almost_equal('Uind part method1/2', U1, Uind_out(:,1), 1e-4_ReKi, .true.,.true.) + enddo + enddo + end subroutine Test_BiotSavart_Part + + + + !> subroutine Test_LatticeToSegment(iStat) integer(IntKi), intent( out) :: iStat !< Status for test @@ -382,7 +439,7 @@ subroutine Test_LatticeToSegment(iStat) CALL ui_seg(1, 1, CPs, & 1, nC1, nC1, nP1, SegPoints, SegConnct, SegGamma, & SmoothModel, SegEpsilon, Uind) - print*,'Uind',Uind + !print*,'Uind',Uind ! --- Convert lattice 2 to segments nSpan = size(LatticePoints2,2) @@ -454,6 +511,7 @@ subroutine FVW_RunTests(ErrStat,ErrMsg) ErrStat = ErrID_None ErrMsg = "" call Test_BiotSavart_Sgmt(ErrStat2, ErrMsg2) + call Test_BiotSavart_Part(ErrStat2, ErrMsg2) end subroutine FVW_RunTests end module FVW_Tests From 29711ba656753d53476c2cda38e89c0fe42184f3 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 23 Apr 2020 17:45:52 -0600 Subject: [PATCH 137/190] FVW: adding tree functions with tests --- modules/aerodyn/src/FVW.f90 | 7 +- modules/aerodyn/src/FVW_Tests.f90 | 116 +++++ modules/aerodyn/src/FVW_VortexTools.f90 | 645 +++++++++++++++++++++++- 3 files changed, 762 insertions(+), 6 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 1c49b21b36..b0cae806b6 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -92,6 +92,9 @@ subroutine FVW_Init(AFInfo, InitInp, u, p, x, xd, z, OtherState, y, m, Interval, #else call WrScr(' - No OpenMP support') #endif + if (DEV_VERSION) then + CALL FVW_RunTests(ErrStat2, ErrMsg2); if (Failed()) return + endif ! Set Parameters and *Misc* from inputs CALL FVW_SetParametersFromInputs(InitInp, p, ErrStat2, ErrMsg2); if(Failed()) return @@ -156,10 +159,6 @@ subroutine FVW_Init(AFInfo, InitInp, u, p, x, xd, z, OtherState, y, m, Interval, interval = InitInp%DTAero ! important, UA, needs proper interval call UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, ErrStat2, ErrMsg2); if (Failed()) return - if (DEV_VERSION) then - CALL FVW_RunTests(ErrStat2, ErrMsg2); if (Failed()) return - endif - ! Framework types unused Interval = InitInp%DTAero OtherState%NULL = 0 diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index e158e53559..b2ee907512 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -367,6 +367,121 @@ subroutine Test_BiotSavart_Part(ErrStat, ErrMsg) enddo end subroutine Test_BiotSavart_Part + !> This test compares calls using the tree algorithm and the direct N^2 evaluation + subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) + integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation + character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + type(T_Tree) :: Tree + real(ReKi), dimension(3) :: P1,CP + real(ReKi), dimension(3) :: U_ref + real(ReKi), dimension(3) :: PartAlpha1 !< Particle intensity alpha=om.dV [m^3/s] + real(ReKi) :: RegParam1 !< + integer(IntKi) :: i1,i2,i3,k, iCP + integer(IntKi) :: RegFunction + integer(IntKi) :: nPart = 1 + integer(IntKi) :: nCPs = 1 + real(ReKi), dimension(:,:), allocatable :: CPs !< Control points + real(ReKi), dimension(:,:), allocatable :: PartPoints !< Particle points + real(ReKi), dimension(:,:), allocatable :: PartAlpha !< Particle circulation + real(ReKi), dimension(:) , allocatable :: RegParam !< Regularization parameter + real(ReKi), dimension(:,:), allocatable :: Uind1 !< Induced velocity vector - Side effects!!! + real(ReKi), dimension(:,:), allocatable :: Uind2 !< Induced velocity vector - Side effects!!! + real(ReKi) :: BranchFactor, BranchSmall + real(ReKi), dimension(3,5) :: CPs_test !< + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + BranchFactor = 2.0_ReKi !< Should be above1 + BranchSmall = 0.0_ReKi + RegFunction = 1 + + ! --- Test with 0 particle + nPart=0; nCPs= 1 + call alloc(nPart,nCPs) + CPs(:,1) = (/0.0,0.0,0.0/) + Uind1 =0.0_ReKi + Uind2 =0.0_ReKi + U_ref =0.0_ReKi + call grow_tree(Tree, PartPoints, PartAlpha, RegFunction, RegParam, 0) + !call print_tree(Tree) + call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) + call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) + ! Test + call test_almost_equal('Uind tree 0 part', U_ref, Uind2(:,1), 1e-4_ReKi, .true.,.true.) + call cut_tree(Tree) + call dealloc() + + + ! --- Test with 1 particle + nPart=1; nCPs= 1 + call alloc(nPart,nCPs) + CPs(:,1) = (/0.0,0.0,0.0/) + PartPoints(1:3,1) = (/1.0,0.0,0.0/) + U_ref =0.0_ReKi + call grow_tree(Tree, PartPoints, PartAlpha, RegFunction, RegParam, 0) + !call print_tree(Tree) + call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) + call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) + ! Test + call test_almost_equal('Uind tree 1 part', Uind1, Uind2, 1e-4_ReKi, .true.,.true.) + call cut_tree(Tree) + !call print_tree(Tree) + call dealloc() + + ! --- Test with 81 particles on different CPs, inside and outside the distribution of particles + nPart=3*3**3; nCPs= 1 + call alloc(nPart,nCPs) + k=0 + do i1 = -1,1,1 + do i2 = -1,1,1 + do i3 = -1,1,1 + ! NOTE: here we purposely duplicate a point, since since is a challenging case + k=k+1; PartPoints(1:3,k) = (/ i1, i2, i3 /) + k=k+1; PartPoints(1:3,k) = (/ i1, i2, i3 /) + k=k+1; PartPoints(1:3,k) = (/ i1*1.2, i2*1.3, i3*1.1 /) + enddo + enddo + enddo + CPs_test(:,1) = (/ 0.0, 0., 0.0 /) ! Middle + CPs_test(:,2) = (/ 1.0, 1.0, 1.0 /) ! Close to a cell center + CPs_test(:,3) = PartPoints(:,5) ! On a particle point + CPs_test(:,4) = (/ 2.0, 2.0, 2.0 /) ! Starts to be far from most points + CPs_test(:,5) = (/ 10., 10., 10.0 /) ! Far from all + + call grow_tree(Tree, PartPoints, PartAlpha, RegFunction, RegParam, 0) + !call print_tree(Tree) + do iCP=1,4 + CPs(:,1) = CPs_test(:,icp) + Uind2=0.0_ReKi; Uind1=0.0_ReKi + call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) + call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) + !print*,'Uind',Uind1, Uind2 + ! Test + call test_almost_equal('Uind tree 81 part', Uind1, Uind2, 1e-2_ReKi, .true.,.true.) + enddo + call cut_tree(Tree) + ! --- Test that tree ui cannot be called after tree has been cut + call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) + call test_equal('Err. stat tree cut',ErrStat,ErrID_Fatal) + call dealloc() + + contains + subroutine alloc(nPart, nCPs) + integer(IntKi) :: nPart, nCPs + allocate(PartPoints(3,nPart), PartAlpha(3,nPart), RegParam(nPart)) + allocate(CPs(3,nCPs), Uind1(3,nCPs), Uind2(3,nCPs)) + RegParam(:)=0.01 + PartAlpha(1,:) = 0.0 + PartAlpha(2,:) = 0.0 + PartAlpha(3,:) = 1.0 + Uind1 =0.0_ReKi + Uind2 =0.0_ReKi + end subroutine + subroutine dealloc() + deallocate(PartPoints, PartAlpha, RegParam) + deallocate(CPs, Uind1, Uind2) + end subroutine + end subroutine Test_BiotSavart_PartTree @@ -512,6 +627,7 @@ subroutine FVW_RunTests(ErrStat,ErrMsg) ErrMsg = "" call Test_BiotSavart_Sgmt(ErrStat2, ErrMsg2) call Test_BiotSavart_Part(ErrStat2, ErrMsg2) + call Test_BiotSavart_PartTree(ErrStat2, ErrMsg2) end subroutine FVW_RunTests end module FVW_Tests diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index 4da7a75102..4a466a21c8 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -9,6 +9,46 @@ module FVW_VortexTools implicit none + ! Tree parameters + integer, parameter :: IK1 = selected_int_kind(1) ! to store particle branch number (from 1 to 8) + integer,parameter :: M0 = 1, M1_1=2, M1_2=3, M1_3=4, M2_11=5, M2_21=6, M2_22=7, M2_31=8, M2_32=9, M2_33=10 ! For moment coefficients + integer,parameter :: M0_000 = 1 + integer,parameter :: M1_100 = 2 + integer,parameter :: M1_010 = 3 + integer,parameter :: M1_001 = 4 + + !> + type T_Part + real(ReKi), dimension(:,:), pointer :: P =>null() + real(ReKi), dimension(:,:), pointer :: Alpha =>null() + real(ReKi), dimension(:), pointer :: RegParam =>null() + integer(IntKi) :: RegFunction =-1 + integer(IntKi) :: n =-1 + end type T_Part + + !> The node type is recursive and is used to make a chained-list of nodes for the tree + type T_Node + real(ReKi) :: radius !< Typical dimension of a cell (max of x,y,z extent) + real(ReKi),dimension(3) :: center + real(ReKi),dimension(3,10) :: Moments + integer,dimension(:),pointer :: iPart=>null() !< indexes of particles stored in this node + integer,dimension(:),pointer :: leaves=>null() ! NOTE: leaves are introduced to save memory + type(T_Node),dimension(:), pointer :: branches =>null() + integer :: nPart = -1 ! Number of particles in branches and leaves of this node + end type T_Node + + !> The type tree contains some basic data, a chained-list of nodes, and a pointer to the Particle data that were used + type T_Tree + type(T_Part) :: Part !< Storage for all particles + integer :: iStep =-1 !< Time step at which the tree was built + logical :: bGrown =.false. !< Is the tree build + type(T_Node) :: Root !< Contains the chained-list of nodes + end type T_Tree + + interface cut_tree + module procedure cut_tree_parallel ; ! to switch between parallel and rec easily + end interface + contains subroutine VecToLattice(PointVectors, iDepthStart, LatticeVectors, iHeadP) @@ -106,7 +146,6 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints else Gamma41 = LatticeGamma(iSpan,iDepth)-LatticeGamma(iSpan-1,iDepth) endif - !print*,iseg1,iseg2,iseg3,iseg4 ! Segment 1-2 if (bShedVorticity) then SegConnct(1,iHeadC) = iseg1 @@ -146,7 +185,7 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints enddo enddo - end subroutine + end subroutine LatticeToSegments subroutine print_mean_4d(M, Label) real(ReKi), dimension(:,:,:,:), intent(in) :: M @@ -207,4 +246,606 @@ real(ReKi) function lin_extrap(x0, x1, y1, x2, y2) result(y0) end function lin_extrap end subroutine interpextrap_cp2node + ! --------------------------------------------------------------------------------} + ! --- Tree -Grow + ! --------------------------------------------------------------------------------{ + subroutine grow_tree(Tree, PartP, PartAlpha, PartRegFunction, PartRegParam, iStep) + type(T_Tree), intent(inout), target :: Tree !< + real(ReKi), dimension(:,:), intent(in ), target :: PartP !< + real(ReKi), dimension(:,:), intent(in ), target :: PartAlpha !< + integer(IntKi), intent(in ) :: PartRegFunction !< + real(ReKi), dimension(:), intent(in ), target :: PartRegParam !< + integer(IntKi), intent(in ) :: iStep !< + type(T_Node), pointer :: node !< Alias + type(T_Part), pointer :: Part !< Alias + real(ReKi) :: max_x,max_y,max_z !< for domain dimension + real(ReKi) :: min_x,min_y,min_z !< for domain dimension + integer(IntKi) :: i + + ! Cutting tree if it already has content + if (associated(Tree%root%branches).or.associated(Tree%root%leaves)) then + call cut_tree_parallel(Tree) + endif + ! Linking tree particles to given part, no copy! + nullify(Tree%Part%P) + nullify(Tree%Part%Alpha) + nullify(Tree%Part%RegParam) + Tree%Part%P => PartP + Tree%Part%Alpha => PartAlpha + Tree%Part%RegParam => PartRegParam + Tree%Part%RegFunction = PartRegFunction + Tree%Part%n = size(PartP,2) + + ! --- Handle special case for root node + node => Tree%Root + Part => Tree%Part + if (Part%n==0) then + ! Do nothing + node%radius = -9999.99_ReKi + node%center = -9999.99_ReKi + node%Moments= -9999.99_ReKi + else if (Tree%Part%n==1) then + node%radius=0 + node%center(1:3)=Part%P(1:3,1) + node%Moments=0.0_ReKi + nullify(node%iPart) + nullify(node%branches) + allocate(node%leaves(1:1)) + node%leaves(1) = Part%n !< index + node%nPart = 1 + else + ! Domain dimensions + max_x=maxval(Part%P(1,1:Part%n)) + max_y=maxval(Part%P(2,1:Part%n)) + max_z=maxval(Part%P(3,1:Part%n)) + min_x=minval(Part%P(1,1:Part%n)) + min_y=minval(Part%P(2,1:Part%n)) + min_z=minval(Part%P(3,1:Part%n)) + + ! Init of trunc + ! Radius taken slightly bigger than domain extent. This radius will be divided by 2 successively + node%radius = max(abs(max_x-min_x),abs(max_y-min_y),abs(max_z-min_z))*1.001_ReKi + if(node%radius>1e6) then + print*,'[Error] Domain extent too large, particle points must be invalid'; + print*, min_x, max_x, min_y, max_y, min_z, max_z + STOP + endif + node%center = (/ (max_x+min_x)/2._ReKi, (max_y+min_y)/2._ReKi, (max_z+min_z)/2._ReKi /) + node%Moments=0.0_ReKi + if(associated(node%iPart)) then ; print*,'[Error] Node part allocated'; STOP; endif + allocate(node%iPart(1:Part%n)) + do i=1,Part%n + node%iPart(i) = i + end do + if(associated(node%branches)) then; print*,'node branches allocated'; STOP; endif + if(associated(node%leaves)) then; print*,'node leaves allocated'; STOP; endif + node%branches=>null() + node%leaves=>null() + node%nPart=Part%n + ! --- Calling grow function on subbrances + call grow_tree_parallel(Tree%root, Tree%Part) +! call grow_tree_rec(Tree%root, Tree%Part) + endif + Tree%iStep = iStep + Tree%bGrown = .true. + end subroutine grow_tree + + recursive subroutine grow_tree_rec(node, Part) + type(T_Node), target :: node !< + type(T_Part), intent(in) :: Part !< + integer :: i + ! Test if there are enough particles on the node to build new branchess + ! The case of only one particle should be handled upstream by allocating one leaf to the parent node +! if(node%nPart>1) then + ! Sub Step: + ! - compute moments and center for the current node + ! - allocate branches and leaves + call grow_tree_substep(node, Part) + ! Call grow_tree on branches + if(associated(node%branches)) then + do i = 1,size(node%branches) + call grow_tree_rec(node%branches(i), Part) + end do + endif +! else +! print*,'nPart',node%nPart, "Build tree rec called with npart<=1" +! STOP +! endif + end subroutine grow_tree_rec + + !> Perform a substep of tree growth, growing sub branches from a given node/cell + !! Parent has already setup node%iPart, indices of the particle in this cell + !! Steps are: + !! - Compute node center (barycenter of vorticity) + !! - Compute node moments + !! - Distribute particles in each 8 octants. Branches are not created for empty octant + !! - Allocate branches and distribute particles to them + subroutine grow_tree_substep(node, Part) + type(T_Node), intent(inout) :: node !< Current node we are growing from + type(T_Part), intent(in) :: Part !< All particles info + integer(IK1) :: iPartOctant !< Index corresponding to which octant the particle falls into + integer :: nLeaves, nBranches + integer :: iLeaf, iOctant, iBranch + integer :: i1,i2,i3,i4,i5,i6,i7,i8 + integer :: i,j,k + real(ReKi) :: wTot, wLoc ! Total and local vorticity strength + real(ReKi) :: halfSize ! TODO remove me + real(ReKi),dimension(3) :: locCenter, DeltaP,PartPos,PartAlpha + real(ReKi),dimension(3) :: nodeGeomCenter !< Geometric center from division of the domain in powers of 2 + real(ReKi),dimension(3) :: nodeBaryCenter !< Vorticity weighted center + integer(IK1),dimension(:),allocatable :: PartOctant !< Stores the octant (1-8) where each particle belongs + integer,dimension(8) :: npart_per_octant !< Number of particle per octant + integer,dimension(8) :: octant2branches !< Mapping between 8 octants, to index of non empty branch + integer,dimension(8) :: octant2leaves !< Idem for singleton/leaves + real(ReKi) :: max_x,max_y,max_z !< for domain dimension + real(ReKi) :: min_x,min_y,min_z !< for domain dimension + nodeGeomCenter = node%center ! NOTE: we rely on the fact that our parent has set this to the Geometric value + nodeBaryCenter = 0.0_ReKi + wTot = 0.0_ReKi + ! --- Barycenter of vorticity of the node + do i = 1,node%nPart + PartPos = Part%P(:,node%iPart(i)) + PartAlpha = Part%Alpha(:,node%iPart(i)) + wLoc = (PartAlpha(1)**2 + PartAlpha(2)**2 + PartAlpha(3)**2)**0.5_ReKi ! Vorticity norm + nodeBaryCenter = nodeBaryCenter + wLoc*PartPos ! Sum coordinates weighted by vorticity + wTot = wTot + wLoc ! Total vorticity + end do + ! There is no vorticity, we make it a empty node and we exit + if(EqualRealNos(abs(wTot),0.0_ReKi)) then + node%nPart=0 + if (associated(node%iPart)) deallocate(node%iPart) + return ! NOTE: we exit + endif + nodeBaryCenter = nodeBaryCenter/wTot ! barycenter of vorticity + node%center = nodeBaryCenter ! updating + + ! --- Calculation of moments about nodeBaryCenter + do i = 1,node%nPart + PartPos = Part%P (:,node%iPart(i)) + PartAlpha = Part%Alpha(:,node%iPart(i)) + DeltaP = PartPos-nodeBaryCenter + ! Order 0 + node%Moments(1:3,M0_000) = node%Moments(1:3,M0_000) + PartAlpha + ! 1st order + node%Moments(1:3,M1_100) = node%Moments(1:3,M1_100) + PartAlpha*DeltaP(1) ! 100 + node%Moments(1:3,M1_010) = node%Moments(1:3,M1_010) + PartAlpha*DeltaP(2) ! 010 + node%Moments(1:3,M1_001) = node%Moments(1:3,M1_001) + PartAlpha*DeltaP(3) ! 001 + ! 2nd order + do j=1,3 + do k=1,j + node%Moments(1:3,3+j+k+j/3) = node%Moments(1:3,3+j+k+j/3) + PartAlpha*DeltaP(j)*DeltaP(k) + end do + end do + end do + + ! --- Distributing particles to the 8 octants, based on the geometric center! + allocate (PartOctant(1:node%nPart)) + npart_per_octant(1:8)=0 + do i = 1,node%nPart + PartPos = Part%P(:,node%iPart(i)) + ! index corresponding to which octant the particle falls into + iPartOctant = int(1,IK1) + if (PartPos(1) > nodeGeomCenter(1)) iPartOctant = iPartOctant + int(1,IK1) + if (PartPos(2) > nodeGeomCenter(2)) iPartOctant = iPartOctant + int(2,IK1) + if (PartPos(3) > nodeGeomCenter(3)) iPartOctant = iPartOctant + int(4,IK1) + npart_per_octant(iPartOctant) = npart_per_octant(iPartOctant) + 1 ! Counter of particles per branch + PartOctant(i)=iPartOctant ! Store in which octant particle i is + end do + + ! --- Leaves and branches + ! A node contains a combination of child nodes and leaves (single particles) + nLeaves = 0 + nBranches = 0 + octant2branches = 0 + octant2leaves = 0 + do iOctant = 1,8 + if(npart_per_octant(iOctant)==1) then + nLeaves = nLeaves+1 + octant2leaves(iOctant) = nLeaves + else if(npart_per_octant(iOctant)>1) then + if (npart_per_octant(iOctant)==node%nPart) then + ! All particle falls into the same octant, if they all have the same location, we would divide forever. + max_x=maxval(Part%P(1,node%iPart(:))); max_y=maxval(Part%P(2,node%iPart(:))); max_z=maxval(Part%P(3,node%iPart(:))) + min_x=minval(Part%P(1,node%iPart(:))); min_y=minval(Part%P(2,node%iPart(:))); min_z=minval(Part%P(3,node%iPart(:))) + if (max(abs(max_x-min_x),abs(max_y-min_y),abs(max_z-min_z))< 1.0e-5) then + nLeaves=node%nPart + allocate (node%leaves(1:nLeaves)) + do i = 1,node%nPart + node%leaves(i)=node%iPart(i) + enddo + ! Cleanup and exit! + if (associated(node%iPart)) deallocate(node%iPart) ! Freeing memory + if (allocated(PartOctant)) deallocate(PartOctant) + return + endif + endif + nBranches = nBranches+1 + octant2branches(iOctant) = nBranches + endif + enddo + if (associated(node%branches)) then + print*,'Tree build: error, branches associated' + STOP + endif + if (associated(node%leaves)) then + print*,'Tree build: error, leaves associated' + STOP + end if + + if(nBranches>0) allocate (node%branches(1:nBranches)) + if(nLeaves>0) allocate (node%leaves(1:nLeaves)) + + ! --- Initializing the branches nodes and leaves + halfSize = node%radius/2._ReKi + do iOctant = 1,8 ! there is max 8 octant + iBranch = octant2branches(iOctant) + if (iBranch>0) then ! this node has branches + allocate(node%branches(iBranch)%iPart(1:npart_per_octant(iOctant))) + node%branches(iBranch)%nPart=npart_per_octant(iOctant) + ! NOTE: this is geometric center not barycenter + locCenter = nodeGeomCenter + 0.5*halfSize*(/ (-1)**(iOctant), (-1)**floor(0.5*real(iOctant-1)+1), (-1)**floor(0.25*real(iOctant-1)+1) /) + ! Init of branches + node%branches(iBranch)%radius = halfSize !< TODO NAN + node%branches(iBranch)%center = locCenter !< TODO NAN + node%branches(iBranch)%Moments = 0.0_ReKi !< TODO NAN + node%branches(iBranch)%branches=>null() + node%branches(iBranch)%leaves=>null() + endif + ! other cases are leaves or dead branches + end do + + ! Store indices of the particles the sub-branch contains + i1=0; i2=0; i3=0; i4=0; i5=0; i6=0; i7=0; i8=0; + do i = 1,node%nPart + iBranch = octant2branches(PartOctant(i)) + if(iBranch>0) then + select case(iBranch) + case(1);i1=i1+1; node%branches(1)%iPart(i1) = node%iPart(i) + case(2);i2=i2+1; node%branches(2)%iPart(i2) = node%iPart(i) + case(3);i3=i3+1; node%branches(3)%iPart(i3) = node%iPart(i) + case(4);i4=i4+1; node%branches(4)%iPart(i4) = node%iPart(i) + case(5);i5=i5+1; node%branches(5)%iPart(i5) = node%iPart(i) + case(6);i6=i6+1; node%branches(6)%iPart(i6) = node%iPart(i) + case(7);i7=i7+1; node%branches(7)%iPart(i7) = node%iPart(i) + case(8);i8=i8+1; node%branches(8)%iPart(i8) = node%iPart(i) + end select + else + iLeaf = octant2leaves(PartOctant(i)) + if(iLeaf>0) then + node%leaves(iLeaf)=node%iPart(i) + else + print*,'This particle do not belong to anybody!!',i + STOP + endif + endif + end do + if (associated(node%iPart)) deallocate(node%iPart) ! Freeing memory + if (allocated(PartOctant)) deallocate(PartOctant) + end subroutine grow_tree_substep + + subroutine grow_tree_parallel(Root, Part) + type(T_Node), intent(inout) :: Root + type(T_Part), intent(in) :: Part + integer :: i, nBranches + integer :: i1 + integer :: i2 + + ! --- Unrolled version of the grow_tree for the first node + ! Sub Step: + ! - compute moments and center for the current node + ! - allocate branches and leaves + call grow_tree_substep(Root, Part) + + if(.not. associated(Root%branches)) then + nBranches=0 + else + nBranches=size(Root%branches) + if (nBranches==0) then + print*,'No branches' + STOP + else + ! Call "buildTree" on branches + + !$OMP PARALLEL default(shared) + + ! --- Unrolled version of the grow_tree for the second levels + !$OMP do private(i) schedule(runtime) + do i = 1,nBranches ! maximum 8 branches + if(Root%branches(i)%nPart>1) then ! I dont think this test is needed + call grow_tree_substep(Root%branches(i), Part) + endif + end do + !$OMP end do + !$OMP barrier ! we need to be sure that all the branches were built + ! --- Unrolled version of the grow_tree for third node levels + !$OMP do private(i,i1,i2) schedule(runtime) + do i = 1,nBranches*8 ! maximum 64 sub branches + i1=(i-1)/8+1; + i2=mod(i-1,8)+1; + if(associated(Root%branches(i1)%branches)) then + if (i2<=size(Root%branches(i1)%branches)) then + call grow_tree_rec(Root%branches(i1)%branches(i2), Part) + endif + endif + + enddo + !$OMP end do + !$OMP END PARALLEL + endif + endif + end subroutine grow_tree_parallel + + + + ! -------------------------------------------------------------------------------- + ! --- Cut tree + ! -------------------------------------------------------------------------------- + !> Cut a tree and all its subbranches in a recursive manner + recursive subroutine cut_tree_rec(node) + integer :: i + type(T_Node),intent(inout) :: node + call cut_substep(node) + if (associated(node%branches)) then + do i=1,size(node%branches) + call cut_tree_rec(node%branches(i)) + end do + deallocate(node%branches) + node%branches=> null() + end if + end subroutine cut_tree_rec + + !> Perform a substep of tree cutting (used by recursive and parallel calls) + subroutine cut_substep(node) + type(T_Node), intent(inout) :: node + if (associated(node%leaves)) then + deallocate(node%leaves) + end if + if (associated(node%iPart)) then + print*,'The tree particles were not properly cleaned' + STOP + deallocate(node%iPart) + end if + end subroutine cut_substep + + !> Cut a tree and all its sub-branches, unrolled to use parallelization for the first 3 levels + subroutine cut_tree_parallel(Tree) + type(T_Tree), intent(inout) :: Tree + integer :: i,i1,i2,nBranches + ! --- Unlinking particles + nullify(Tree%Part%P) + nullify(Tree%Part%Alpha) + nullify(Tree%Part%RegParam) + ! --- Unrolled version of cut_tree for the first node + call cut_substep(Tree%root) + if(associated(Tree%Root%branches)) then + nBranches=size(Tree%Root%branches) + !$OMP PARALLEL default(shared) + + ! --- Unrolled version for the second levels + !$OMP do private(i) schedule(runtime) + do i = 1,nBranches ! maximum 8 branches + call cut_substep(Tree%Root%branches(i)) + end do + !$OMP end do + !$OMP barrier ! we need to be sure that all the branches were cut + + ! --- Unrolled version for third node levels + !$OMP do private(i,i1,i2) schedule(runtime) + do i = 1,nBranches*8 ! maximum 64 sub branches + i1=(i-1)/8+1; + i2=mod(i-1,8)+1; + if(associated(Tree%Root%branches(i1)%branches)) then + if (i2<=size(Tree%Root%branches(i1)%branches)) then + call cut_tree_rec(Tree%Root%branches(i1)%branches(i2)) + endif + endif + enddo + !$OMP end do + !$OMP END PARALLEL + + ! --- Cleanup second level + do i = 1,nBranches ! maximum 8 branches + if (associated(Tree%root%branches(i)%branches)) then + deallocate(Tree%root%branches(i)%branches) + nullify(Tree%root%branches(i)%branches) + endif + end do + + ! --- Cleanup First level + deallocate(Tree%root%branches) + nullify(Tree%root%branches) + endif + if (associated(Tree%root%branches)) then + print*,'Tree cut: branches are still allocated' + STOP + endif + Tree%iStep=-1 + Tree%root%nPart=-1 + Tree%bGrown=.false. + end subroutine cut_tree_parallel + + subroutine print_tree(Tree) + type(T_Tree) :: Tree + character(len=1024) :: preffix + preffix='root' + print '(A, L1)', trim(preffix)//':partP_assoc = ',associated(Tree%Part%P) + print '(A, L1)', trim(preffix)//':bGrown = ',Tree%bGrown + print '(A, I0)', trim(preffix)//':iStep = ',Tree%iStep + call print_tree_rec(Tree%Root, preffix) + contains + recursive subroutine print_tree_rec(node, preffix) + type(T_Node), target :: node !< + character(len=*), intent(in) :: preffix + integer :: i + ! Test if there are enough particles on the node to build new branchess + ! The case of only one particle should be handled upstream by allocating one leaf to the parent node + print'(A)' ,trim(preffix)//':nPart = '//Num2LStr(node%nPart) + print'(A,3F12.3)',trim(preffix)//':center =',node%center + print'(A,1F12.3)',trim(preffix)//':radius =',node%radius + if(associated(node%leaves)) then + do i = 1,size(node%leaves) + print'(A)',trim(preffix)//':leaf'//trim(Num2LStr(i))//'='//trim(Num2LStr(node%leaves(i))) + end do + endif + if(associated(node%branches)) then + do i = 1,size(node%branches) + call print_tree_rec(node%branches(i), trim(preffix)//':branch'//trim(Num2LStr(i))) + end do + endif + end subroutine print_tree_rec + end subroutine print_tree + + ! -------------------------------------------------------------------------------- + ! --- Velocity computation + ! -------------------------------------------------------------------------------- + subroutine ui_tree(Tree, CPs, ioff, icp_beg, icp_end, nCPs, BranchFactor, BranchSmall, Uind, ErrStat, ErrMsg) + use FVW_BiotSavart, only: fourpi_inv, ui_part_nograd_11 + type(T_Tree), target, intent(inout) :: Tree !< + integer, intent(in ) :: nCPs !< + integer, intent(in ) :: ioff !< + integer, intent(in ) :: icp_beg !< + integer, intent(in ) :: icp_end !< + real(ReKi), intent(in ) :: BranchFactor !< + real(ReKi), intent(in ) :: BranchSmall !< + real(ReKi), dimension(:,:), intent(in ) :: CPs !< Control Points (3 x nCPs) + real(ReKi), dimension(:,:), intent(inout) :: Uind !< Induced velocity at CPs, with side effects (3 x nCPs) + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + real(ReKi), dimension(3) :: Uind_tmp !< + real(ReKi), dimension(3) :: CP !< Current CP + integer :: icp, nDirect, nQuad + type(T_Part), pointer :: Part ! Alias + Part => Tree%Part + if(.not. associated(Part%P)) then + ErrMsg='Ui Part Tree called but tree particles not associated'; ErrStat=ErrID_Fatal; return + endif + !$OMP PARALLEL DEFAULT(SHARED) + !$OMP DO PRIVATE(icp,CP,Uind_tmp,nDirect,nQuad) schedule(runtime) + do icp=icp_beg,icp_end + CP = CPs(1:3,icp) + Uind_tmp(1:3) = 0.0_ReKi + nDirect =0 + nQuad =0 + call ui_tree_11(Tree%root, CP, Uind_tmp, nDirect, nQuad) !< SIDE EFFECTS + !print*,'Number of direct calls, and quad calls',nDirect, nQuad + Uind(1:3,ioff+icp-icp_beg+1) = Uind(1:3,ioff+icp-icp_beg+1) + Uind_tmp(1:3) + enddo + !$OMP END DO + !$OMP END PARALLEL + contains + !> Velocity at one control point from the entire tree + recursive subroutine ui_tree_11(node, CP, Uind, nDirect, nQuad) + real(ReKi),dimension(3),intent(inout) :: CP, Uind !< Velocity at control point, with side effect + integer, intent(inout) :: nDirect,nQuad + type(T_Node), intent(inout) :: node + real(ReKi) :: distMin, coeff + real(ReKi),dimension(3) :: DeltaP, phi, quad, Uind_B + real(ReKi),dimension(3) :: Uind_T + real(ReKi) :: x,y,z,mx,my,mz,r + integer :: i,j,ieqj + integer :: iPart + if (node%nPart<=0) then + ! We skip the dead leaf + elseif (.not.associated(node%branches)) then + ! Loop on leaves + if(associated(node%leaves)) then + do i =1,size(node%leaves) + iPart=node%leaves(i) + DeltaP = CP(1:3) - Part%P(1:3,iPart) + call ui_part_nograd_11(DeltaP, Part%Alpha(1:3,iPart), Part%RegFunction, Part%RegParam(iPart), Uind_B) + nDirect=nDirect+1 + Uind(1:3) = Uind(1:3) + Uind_B + enddo + endif + else + distMin = BranchFactor*node%radius ! Minimum distance for quadrupole calculation + DeltaP = - node%center + CP(1:3) ! Vector between the control point and the center of the branch + r = ( DeltaP(1)**2 + DeltaP(2)**2 + DeltaP(3)**2) ** 0.5 ! Norm + ! Test if the control point is far enough from the branch node + if ((r Date: Thu, 23 Apr 2020 19:38:45 -0600 Subject: [PATCH 138/190] FVW: adding conversion segments to particles --- modules/aerodyn/src/FVW_Tests.f90 | 154 ++++++++++++++++++++---- modules/aerodyn/src/FVW_VortexTools.f90 | 41 ++++++- 2 files changed, 171 insertions(+), 24 deletions(-) diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index b2ee907512..3f7fb7f7cb 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -215,9 +215,6 @@ subroutine test_almost_equal_2(Var,VecRef,VecTry,MINNORM,bStop,bPrint,bPassed) subroutine Test_BiotSavart_Sgmt(ErrStat, ErrMsg) integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - !integer(IntKi) :: ErrStat2 - !character(ErrMsgLen) :: ErrMsg2 - ! real(ReKi), dimension(3) :: P1,P2,P3,CP real(ReKi), dimension(3) :: U1 real(ReKi) :: SegGamma1 !< Circulation [m^2/s] @@ -431,17 +428,17 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) ! --- Test with 81 particles on different CPs, inside and outside the distribution of particles nPart=3*3**3; nCPs= 1 call alloc(nPart,nCPs) - k=0 - do i1 = -1,1,1 - do i2 = -1,1,1 - do i3 = -1,1,1 - ! NOTE: here we purposely duplicate a point, since since is a challenging case - k=k+1; PartPoints(1:3,k) = (/ i1, i2, i3 /) - k=k+1; PartPoints(1:3,k) = (/ i1, i2, i3 /) - k=k+1; PartPoints(1:3,k) = (/ i1*1.2, i2*1.3, i3*1.1 /) - enddo - enddo - enddo + k=0 + do i1 = -1,1,1 + do i2 = -1,1,1 + do i3 = -1,1,1 + ! NOTE: here we purposely duplicate a point, since since is a challenging case + k=k+1; PartPoints(1:3,k) = (/ i1, i2, i3 /) + k=k+1; PartPoints(1:3,k) = (/ i1, i2, i3 /) + k=k+1; PartPoints(1:3,k) = (/ i1*1.2, i2*1.3, i3*1.1 /) + enddo + enddo + enddo CPs_test(:,1) = (/ 0.0, 0., 0.0 /) ! Middle CPs_test(:,2) = (/ 1.0, 1.0, 1.0 /) ! Close to a cell center CPs_test(:,3) = PartPoints(:,5) ! On a particle point @@ -450,15 +447,15 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) call grow_tree(Tree, PartPoints, PartAlpha, RegFunction, RegParam, 0) !call print_tree(Tree) - do iCP=1,4 - CPs(:,1) = CPs_test(:,icp) - Uind2=0.0_ReKi; Uind1=0.0_ReKi - call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) - call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) - !print*,'Uind',Uind1, Uind2 - ! Test - call test_almost_equal('Uind tree 81 part', Uind1, Uind2, 1e-2_ReKi, .true.,.true.) - enddo + do iCP=1,4 + CPs(:,1) = CPs_test(:,icp) + Uind2=0.0_ReKi; Uind1=0.0_ReKi + call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) + call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) + !print*,'Uind',Uind1, Uind2 + ! Test + call test_almost_equal('Uind tree 81 part', Uind1, Uind2, 1e-2_ReKi, .true.,.true.) + enddo call cut_tree(Tree) ! --- Test that tree ui cannot be called after tree has been cut call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) @@ -483,7 +480,117 @@ subroutine dealloc() end subroutine end subroutine Test_BiotSavart_PartTree + !> Compares the velocity field obtained from a segment and its convert to particle version + subroutine Test_SegmentsToPart(ErrStat, ErrMsg) + integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation + character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + real(ReKi), dimension(:,:), allocatable :: PartPoints !< Particle points + real(ReKi), dimension(:,:), allocatable :: PartAlpha !< Particle circulation + real(ReKi), dimension(:) , allocatable :: PartEpsilon !< Regularization parameter + integer(IntKi), parameter :: nSegTot = 2 + integer(IntKi), parameter :: nSegPTot = 3 + integer(IntKi), parameter :: nCPsTot = 10 + real(ReKi), dimension(3,nSegPTot) :: SegPoints !< Segment points + integer(IntKi), dimension(2,nSegTot) :: SegConnct !< Connectivity, indices of segments points iSeg1, iSeg2 + real(ReKi), dimension(nSegTot) :: SegGamma !< Segment circulation + real(ReKi), dimension(nSegTot) :: SegEpsilon !< Regularization parameter + real(ReKi), dimension(3,nCPsTot) :: CPs !< Control points + real(ReKi), dimension(3,nCPsTot) :: Uind1 !< Induced velocity vector - Side effects!!! + real(ReKi), dimension(3,nCPsTot) :: Uind2 !< Induced velocity vector - Side effects!!! + real(ReKi) :: RegParam1 !< + integer(IntKi) :: i1,i2, nPartPerSeg, nPart, iHeadP + integer(IntKi) :: RegFunctionPart, RegFunctionSeg + ErrStat = ErrID_None + ErrMsg = "" + RegParam1=1.0 + ! Creating two aligned segments + SegConnct(:,1)=(/1,2/) + SegConnct(:,2)=(/2,3/) + SegPoints(:,1) = (/0. ,0.,-1./) + SegPoints(:,2) = (/0. ,0., 0./) + SegPoints(:,3) = (/0. ,0., 1./) + SegGamma(:) =4 + SegEpsilon = RegParam1 + ! Points where velocity will be evaluated + CPs(:,1) = SegPoints(:,1) + CPs(:,2) = SegPoints(:,2) + CPs(:,3) = SegPoints(:,3) + CPs(:,4) = (/ 0.2, 0.2, 0.0/) + CPs(:,6) = (/ 0.5, 0.5, 0. /) + CPs(:,8) = (/ 1.0, 1.0, 0./) + CPs(:,9) = (/ 10.0, 10.0, 0./) + CPs(:,5) = (/ 0.2, 0.2, 0.5/) + CPs(:,7) = (/ 0.5, 0.5, 0.5/) + CPs(:,10) = (/ 1.0, 1.0, 1./) + + ! --- Test 1 - 10 particles, no regularization + RegFunctionSeg = idRegNone + RegFunctionPart = idRegNone + nPartPerSeg = 10 + + nPart = nPartPerSeg * nSegTot + call alloc(nPart) + iHeadP=1 + call SegmentsToPart(SegPoints, SegConnct, SegGamma, SegEpsilon, 1, nSegTot, nPartPerSeg, PartPoints, PartAlpha, PartEpsilon, iHeadP) + Uind1 =0.0_ReKi; Uind2 =0.0_ReKi; + call ui_seg(1, nCPsTot, CPs, 1, nSegTot, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, RegFunctionSeg, SegEpsilon, Uind1) + call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunctionPart, PartEpsilon, Uind2, nCPsTot, nPart) + call test_almost_equal('Uind 10 part/sgmt no reg', Uind1, Uind2, 1e-3_ReKi, .true.,.true.) + call dealloc() + + ! --- Test 1 - 2 particles, no regularization + RegFunctionSeg = idRegNone + RegFunctionPart = idRegNone + nPartPerSeg = 2 + + nPart = nPartPerSeg * nSegTot + call alloc(nPart) + iHeadP=1 + call SegmentsToPart(SegPoints, SegConnct, SegGamma, SegEpsilon, 1, nSegTot, nPartPerSeg, PartPoints, PartAlpha, PartEpsilon, iHeadP) + + Uind1 =0.0_ReKi; Uind2 =0.0_ReKi; + call ui_seg(1, nCPsTot, CPs, 1, nSegTot, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, RegFunctionSeg, SegEpsilon, Uind1) + call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunctionPart, PartEpsilon, Uind2, nCPsTot, nPart) + call test_almost_equal('Uind 2 part/sgmt noreg', Uind1, Uind2, 3e-1_ReKi, .true.,.true.) + call dealloc() + + + ! --- Test 3 - 10 particles, regularization + ! NOTE: more work needed to match the regularization functions and parameters optimally + RegFunctionSeg = idRegLambOseen + RegFunctionPart = idRegExp + nPartPerSeg = 10 + + nPart = nPartPerSeg * nSegTot + call alloc(nPart) + iHeadP=1 + call SegmentsToPart(SegPoints, SegConnct, SegGamma, SegEpsilon, 1, nSegTot, nPartPerSeg, PartPoints, PartAlpha, PartEpsilon, iHeadP) + + Uind1 =0.0_ReKi; Uind2 =0.0_ReKi; + call ui_seg(1, nCPsTot, CPs, 1, nSegTot, nSegTot, nSegPTot, SegPoints, SegConnct, SegGamma, RegFunctionSeg, SegEpsilon, Uind1) + call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunctionPart, PartEpsilon, Uind2, nCPsTot, nPart) + !print'(A,10F7.3)','Uind1',Uind1(1,:) + !print'(A,10F7.3)','Uind2',Uind2(1,:) + !print'(A,10F7.3)','Uind1',Uind1(2,:) + !print'(A,10F7.3)','Uind2',Uind2(2,:) + !print'(A,10F7.3)','Uind1',Uind1(3,:) + !print'(A,10F7.3)','Uind2',Uind2(3,:) + call test_almost_equal('Uind 10 part/sgmt w.reg', Uind1, Uind2, 5e-2_ReKi, .true.,.true.) + call dealloc() + + contains + subroutine alloc(n) + integer(IntKi) :: n + allocate(PartPoints(3,n), PartAlpha(3,n), PartEpsilon(n)) + PartAlpha(:,:) = -99999.99_ReKi + PartPoints(:,:) = -99999.99_ReKi + PartEpsilon(:) = -99999.99_ReKi + end subroutine + subroutine dealloc() + deallocate(PartPoints, PartAlpha, PartEpsilon) + end subroutine + end subroutine Test_SegmentsToPart !> subroutine Test_LatticeToSegment(iStat) @@ -628,6 +735,7 @@ subroutine FVW_RunTests(ErrStat,ErrMsg) call Test_BiotSavart_Sgmt(ErrStat2, ErrMsg2) call Test_BiotSavart_Part(ErrStat2, ErrMsg2) call Test_BiotSavart_PartTree(ErrStat2, ErrMsg2) + call Test_SegmentsToPart(ErrStat2, ErrMsg2) end subroutine FVW_RunTests end module FVW_Tests diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index 4a466a21c8..ac96c93337 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -184,9 +184,48 @@ subroutine LatticeToSegments(LatticePoints, LatticeGamma, iDepthStart, SegPoints endif enddo enddo - end subroutine LatticeToSegments + !> Convert segments between index iSegStart and iSegEnd to particles. + subroutine SegmentsToPart(SegPoints, SegConnct, SegGamma, SegEpsilon, iSegStart, iSegEnd, nPartPerSeg, PartPoints, PartAlpha, PartEpsilon, iHeadPart) + real(ReKi), dimension(:,:), intent(in ) :: SegPoints !< + integer(IntKi), dimension(:,:), intent(in ) :: SegConnct !< + real(ReKi), dimension(:), intent(in ) :: SegGamma !< + real(ReKi), dimension(:), intent(in ) :: SegEpsilon !< + integer, intent(in ) :: iSegStart !< Index where to start in Seg* vectors + integer, intent(in ) :: iSegEnd !< + integer, intent(in ) :: nPartPerSeg !< Segments will be dividied into nPartPerSeg particles + real(ReKi), dimension(:,:), intent(inout) :: PartPoints !< Particle points (3 x nPart) + real(ReKi), dimension(:,:), intent(inout) :: PartAlpha !< Particle intensities (3 x nPart) + real(ReKi), dimension(:), intent(inout) :: PartEpsilon !< Particle regularization parameter (nPart) + integer, intent(inout) :: iHeadPart !< Index where to start in Part* vectors + real(ReKi), dimension(3) :: P1, P2, DP !< Segment extremities + real(ReKi), dimension(3) :: SegDir !< direction vector + real(ReKi), dimension(3) :: PartInt + real(ReKi) :: PartLen !< Initial "length" of the blob + real(ReKi) :: PartEps !< Regularization of the blob + real(ReKi) :: SegLen + integer(IntKi) :: ip1, ip2 !< index of points to be converted + integer(IntKi) :: iSeg, iSubPart + ! loop on selected segments + do iSeg=iSegStart,iSegEnd + P1 = SegPoints(1:3,SegConnct(1,iSeg)) ! Segment extremities + P2 = SegPoints(1:3,SegConnct(2,iSeg)) + DP = P2-P1 + SegLen = norm2(DP) + SegDir = DP/SegLen ! Unit vector along segment direction + PartInt = DP*SegGamma(iSeg)/nPartPerSeg ! alpha = Gamma.L/n = omega.dV [m^3/s] + PartEps = SegEpsilon(iSeg) ! TODO this might need tuning depending on RegFunction and n_new + PartLen = SegLen/nPartPerSeg + do iSubPart=0,nPartPerSeg-1 + PartPoints(1:3, iHeadPart) = P1(1:3) + (0.5_ReKi+iSubPart)*PartLen*SegDir(1:3) ! ds/2:ds:L + PartAlpha (1:3, iHeadPart) = PartInt(1:3) + PartEpsilon( iHeadPart) = PartEps + iHeadPart = iHeadPart +1 + enddo + enddo + end subroutine SegmentsToPart + subroutine print_mean_4d(M, Label) real(ReKi), dimension(:,:,:,:), intent(in) :: M character(len=*), intent(in) :: Label From 61ad9e9638455946d591ef500455cae0683d086f Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 23 Apr 2020 20:26:10 -0600 Subject: [PATCH 139/190] FVW: implemented Tree and Part for wake, need input file flags --- modules/aerodyn/src/FVW_Subs.f90 | 50 ++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 5323d40a42..bcc75371d6 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -48,7 +48,7 @@ module FVW_SUBS ! Implementation integer(IntKi), parameter :: iNWStart=2 !< Index in r%NW where the near wake start (if >1 then the Wing panels are included in r_NW) integer(IntKi), parameter :: FWnSpan=1 !< Number of spanwise far wake panels ! TODO make it an input later - logical , parameter :: DEV_VERSION=.FALSE. + logical , parameter :: DEV_VERSION=.False. contains !========================================================================== @@ -750,6 +750,16 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) integer(IntKi) :: iW, nSeg, nSegP, nCPs, iHeadP integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step logical :: bMirror ! True if we mirror the vorticity wrt ground + ! TODO new options + integer(IntKi) :: nPart + integer(IntKi) :: nPartPerSeg + integer(IntKi) :: UIMethod + integer(IntKi) :: RegFunctionPart + real(ReKi) :: BranchFactor, BranchSmall + type(T_Tree) :: Tree + real(ReKi), dimension(:,:), allocatable :: PartPoints !< Particle points + real(ReKi), dimension(:,:), allocatable :: PartAlpha !< Particle circulation + real(ReKi), dimension(:) , allocatable :: PartEpsilon !< Regularization parameter ErrStat= ErrID_None ErrMsg ='' @@ -771,7 +781,42 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) if (DEV_VERSION) then print'(A,I0,A,I0,A,I0)','Convection - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs endif - call ui_seg( 1, nCPs, m%CPs, 1, nSeg, nSeg, nSegP, m%SegPoints, m%SegConnct, m%SegGamma, p%RegFunction, m%SegEpsilon, m%Uind) + + ! --- Converting to particles + ! TODO new parameters + UIMethod = 1 ! 1=Seg, 2=PartTree, 3=Part, + BranchFactor = 2.0_ReKi !< Should be above1 + BranchSmall = 0.0_ReKi + if (UIMethod>1) then + iHeadP=1 + nPartPerSeg = 1 + nPart = nPartPerSeg * nSeg + allocate(PartPoints(3,nPart), PartAlpha(3,nPart), PartEpsilon(nPart)) + PartAlpha(:,:) = -99999.99_ReKi + PartPoints(:,:) = -99999.99_ReKi + PartEpsilon(:) = -99999.99_ReKi + call SegmentsToPart(m%SegPoints, m%SegConnct, m%SegGamma, m%SegEpsilon, 1, nSeg, nPartPerSeg, PartPoints, PartAlpha, PartEpsilon, iHeadP) + if (p%RegFunction/=idRegNone) then + RegFunctionPart = idRegExp + endif + endif + + ! --- Getting induced velocity + m%Uind=0.0_ReKi ! very important due to side effects of ui_* methods + if (UIMethod==1) then + call ui_seg( 1, nCPs, m%CPs, 1, nSeg, nSeg, nSegP, m%SegPoints, m%SegConnct, m%SegGamma, p%RegFunction, m%SegEpsilon, m%Uind) + + elseif (UIMethod==2) then + call grow_tree(Tree, PartPoints, PartAlpha, RegFunctionPart, PartEpsilon, 0) + !call print_tree(Tree) + call ui_tree(Tree, m%CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, m%Uind, ErrStat, ErrMsg) + call cut_tree(Tree) + deallocate(PartPoints, PartAlpha, PartEpsilon) + + elseif (UIMethod==3) then + call ui_part_nograd(m%CPs ,PartPoints, PartAlpha, RegFunctionPart, PartEpsilon, m%Uind, nCPs, nPart) + deallocate(PartPoints, PartAlpha, PartEpsilon) + endif call UnPackInducedVelocity() contains @@ -779,7 +824,6 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) subroutine PackConvectingPoints() ! Counting total number of control points that convects nCPs = CountCPs(p, m%nNW, nFWEff) - m%Uind=0.0_ReKi ! very important due to side effects of ui_seg m%CPs=-999.9_ReKi ! Packing iHeadP=1 From 78f095010e4827b24a24a33b6456b2983982034d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 24 Apr 2020 19:25:58 -0600 Subject: [PATCH 140/190] FVW: adding inputs for tree --- modules/aerodyn/src/AeroDyn.f90 | 3 +- modules/aerodyn/src/FVW.f90 | 9 +- modules/aerodyn/src/FVW_IO.f90 | 34 ++--- modules/aerodyn/src/FVW_Registry.txt | 10 +- modules/aerodyn/src/FVW_Subs.f90 | 42 +++--- modules/aerodyn/src/FVW_Tests.f90 | 14 +- modules/aerodyn/src/FVW_Types.f90 | 48 +++--- modules/aerodyn/src/FVW_VortexTools.f90 | 188 ++++++++++++------------ modules/aerodyn/src/UnsteadyAero.f90 | 3 +- 9 files changed, 175 insertions(+), 176 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index c1738fbfb2..d24dd03f6f 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -2143,8 +2143,7 @@ SUBROUTINE Init_BEMTmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, y, InitInp%aTol = InputFileData%IndToler InitInp%useTipLoss = InputFileData%TipLoss InitInp%useHubLoss = InputFileData%HubLoss -!FIXME: check the next flag. Not sure if FVW can be used with it or not. - InitInp%useInduction = (InputFileData%WakeMod /= WakeMod_none .and. InputFileData%WakeMod /= WakeMod_FVW) + InitInp%useInduction = InputFileData%WakeMod /= WakeMod_none InitInp%useTanInd = InputFileData%TanInd InitInp%useAIDrag = InputFileData%AIDrag InitInp%useTIDrag = InputFileData%TIDrag diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index b0cae806b6..bacab29f02 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -262,8 +262,8 @@ subroutine FVW_InitMiscVarsPostParam( p, m, ErrStat, ErrMsg ) endif call AllocAry( m%SegConnct, 4, nSeg , 'SegConnct' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegConnct = -999; call AllocAry( m%SegPoints, 3, nSegP, 'SegPoints' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegPoints = -999999_ReKi; - call AllocAry( m%SegGamma , nSeg, 'SegGamma' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegGamma = -999999_ReKi; - call AllocAry( m%SegEpsilon, nSeg, 'SegEpsilon', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegEpsilon= -999999_ReKi; + call AllocAry( m%SegGamma , nSeg, 'SegGamma' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegGamma = -999999_ReKi; + call AllocAry( m%SegEpsilon, nSeg, 'SegEpsilon', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%SegEpsilon= -999999_ReKi; call AllocAry( m%CPs , 3, nCPs, 'CPs' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%CPs= -999999_ReKi; call AllocAry( m%Uind , 3, nCPs, 'Uind' , ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); m%Uind= -999999_ReKi; @@ -409,9 +409,10 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg p%WingRegParam = InputFileData%WingRegParam p%CoreSpreadEddyVisc = InputFileData%CoreSpreadEddyVisc p%ShearModel = InputFileData%ShearModel - p%TreeModel = InputFileData%TreeModel + p%TwrShadowOnWake = InputFileData%TwrShadowOnWake + p%VelocityMethod = InputFileData%VelocityMethod p%TreeBranchFactor = InputFileData%TreeBranchFactor - p%TreeBranchSmall = InputFileData%TreeBranchSmall + p%PartPerSegment = InputFileData%PartPerSegment p%WrVTK = InputFileData%WrVTK p%VTKBlades = min(InputFileData%VTKBlades,p%nWings) ! Note: allowing it to be negative for temporary hack p%VTKCoord = InputFileData%VTKCoord diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index ddf9932e1f..d5ca8d4e5c 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -60,14 +60,14 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) CALL ReadVar (UnIn,FileName,Inp%WingRegParam ,'WingRegParam' ,'' , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%CoreSpreadEddyVisc ,'CoreSpreadEddyVisc','',100.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return CALL ReadVarWDefault(UnIn,FileName,Inp%ShearModel ,'ShearModel' ,'',idShearNone , ErrStat2,ErrMsg2); if(Failed())return - !CALL ReadVarWDefault(UnIn,FileName,Inp%TwrShadowOnWake ,'TwrShadowOnWake' ,'',.false. , ErrStat2,ErrMsg2); if(Failed())return - !CALL ReadVarWDefault(UnIn,FileName,Inp%TreeModel ,'TreeModel' ,'',idTreeNone , ErrStat2,ErrMsg2); if(Failed())return - !CALL ReadVarWDefault(UnIn,FileName,Inp%TreeBranchFactor ,'TreeBranchFactor' ,'',3.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return - !CALL ReadVarWDefault(UnIn,FileName,Inp%TreeBranchSmall ,'TreeBranchSmall' ,'',0.1_ReKi , ErrStat2,ErrMsg2); if(Failed())return - Inp%TwrShadowOnWake = .False. - Inp%TreeModel = idTreeNone - Inp%TreeBranchFactor = 3.0_ReKi - Inp%TreeBranchSmall = 0.1_ReKi + CALL ReadVarWDefault(UnIn,FileName,Inp%TwrShadowOnWake ,'TwrShadowOnWake' ,'',.false. , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%VelocityMethod ,'VelocityMethod' ,'',idVelocityBasic , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%TreeBranchFactor ,'TreeBranchFactor' ,'',2.0_ReKi , ErrStat2,ErrMsg2); if(Failed())return + CALL ReadVarWDefault(UnIn,FileName,Inp%PartPerSegment ,'PartPerSegment' ,'', 1 , ErrStat2,ErrMsg2); if(Failed())return +! Inp%TwrShadowOnWake = .False. +! Inp%VelocityMethod = idVelocityBasic +! Inp%TreeBranchFactor = 3.0_ReKi +! Inp%PartPerSegment = 1 !------------------------ OUTPUT OPTIONS ----------------------------------------- CALL ReadCom (UnIn,FileName, 'Output options header' ,ErrStat2,ErrMsg2); if(Failed()) return CALL ReadVarWDefault(UnIn,FileName,Inp%WrVTK , 'WrVTK' ,'', 0 ,ErrStat2,ErrMsg2); if(Failed())return @@ -78,14 +78,14 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, Inp, ErrStat, ErrMsg ) ! --- Validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) - if (Check(.not.(ANY(idCircVALID ==Inp%CirculationMethod)), 'Circulation method (CircSolvingMethod) not implemented')) return + if (Check(.not.(ANY(idCircVALID ==Inp%CirculationMethod)), 'Circulation method (CircSolvingMethod) not implemented: '//trim(Num2LStr(Inp%CirculationMethod)))) return if (Check(.not.(ANY(idIntMethodVALID==Inp%IntMethod )) , 'Time integration method (IntMethod) not yet implemented. Use Euler 1st order method for now.')) return - if (Check(.not.(ANY(idDiffusionVALID==Inp%DiffusionMethod)) , 'Diffusion method (DiffusionMethod) not yet implemented. Use None for now.')) return - if (Check(.not.(ANY(idRegDeterVALID ==Inp%RegDeterMethod)) , 'Regularization determination method (RegDeterMethod) not yet implemented. Use Manual method for now.')) return - if (Check(.not.(ANY(idRegVALID ==Inp%RegFunction )), 'Regularization function (RegFunction) not implemented')) return - if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method (WakeRegMethod) not implemented')) return - if (Check(.not.(ANY(idShearVALID ==Inp%ShearModel )), 'Shear model (`ShearModel`) not valid')) return - if (Check(.not.(ANY(idTreeVALID ==Inp%TreeModel )), 'Shear model (`ShearModel`) not valid')) return + if (Check(.not.(ANY(idDiffusionVALID==Inp%DiffusionMethod)) , 'Diffusion method (DiffusionMethod) not implemented: '//trim(Num2LStr(Inp%DiffusionMethod)))) return + if (Check(.not.(ANY(idRegDeterVALID ==Inp%RegDeterMethod)) , 'Regularization determination method (RegDeterMethod) not yet implemented: '//trim(Num2LStr(Inp%RegDeterMethod)))) return + if (Check(.not.(ANY(idRegVALID ==Inp%RegFunction )), 'Regularization function (RegFunction) not implemented: '//trim(Num2LStr(Inp%RegFunction)))) return + if (Check(.not.(ANY(idRegMethodVALID==Inp%WakeRegMethod)), 'Wake regularization method (WakeRegMethod) not implemented: '//trim(Num2LStr(Inp%WakeRegMethod)))) return + if (Check(.not.(ANY(idShearVALID ==Inp%ShearModel )), 'Shear model (ShearModel) not valid: '//trim(Num2LStr(Inp%ShearModel)))) return + if (Check(.not.(ANY(idVelocityVALID ==Inp%VelocityMethod )), 'Velocity method (VelocityMethod) not valid: '//trim(Num2LStr(Inp%VelocityMethod)))) return if (Check( Inp%DTfvw < p%DTaero, 'DTfvw must be >= DTaero from AD15.')) return if (abs(Inp%DTfvw-p%DTaero)>epsilon(1.0_ReKi)) then @@ -194,8 +194,8 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth) character(1), dimension(3) :: I2ABC =(/'A','B','C'/) integer(IntKi) :: nSeg, nSegP, nSegNW logical :: bMirror - integer(IntKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 + !integer(IntKi) :: ErrStat2 + !character(ErrMsgLen) :: ErrMsg2 real(Reki), dimension(:,:,:), allocatable :: dxdt_0 !< if (DEV_VERSION) then diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 2a37f27c3a..297cc8474c 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -36,9 +36,9 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi WingRegParam - - - "Regularization parameter of the wing" typedef ^ ^ IntKi ShearModel - - - "Option for shear modelling" typedef ^ ^ Logical TwrShadowOnWake - - - "Include tower shadow effects on wake" -typedef ^ ^ IntKi TreeModel - - - "Tree calculation method" +typedef ^ ^ IntKi VelocityMethod - - - "Velocity calculation method" typedef ^ ^ ReKi TreeBranchFactor - - - "Factor used to determine if a point is far enough" -typedef ^ ^ ReKi TreeBranchSmall - - - "Distance below which a branch is consisdered small enough" +typedef ^ ^ IntKi PartPerSegment - - - "Number of particles per segment, e.g. for tree method" typedef ^ ^ DbKi DTaero - - - "Time interval for calls calculations" s typedef ^ ^ DbKi DTfvw - - - "Time interval for calculating wake induced velocities" s typedef ^ ^ ReKi KinVisc - - - "Kinematic air viscosity" m^2/s @@ -195,9 +195,9 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi WingRegParam - - - "Factor used in the regularization " typedef ^ ^ IntKi ShearModel - - - "Option for shear modelling" typedef ^ ^ Logical TwrShadowOnWake - - - "Include tower shadow effects on wake" -typedef ^ ^ IntKi TreeModel - - - "Tree calculation method" -typedef ^ ^ ReKi TreeBranchFactor - - - "Factor used to determine if a point is far enough" -typedef ^ ^ ReKi TreeBranchSmall - - - "Distance below which a branch is consisdered small enough" +typedef ^ ^ IntKi VelocityMethod - - - "Velocity calculation method" +typedef ^ ^ ReKi TreeBranchFactor - - - "Factor used to determine if a point is far enough" +typedef ^ ^ IntKi PartPerSegment - - - "Number of particles per segment, e.g. for tree method" typedef ^ ^ IntKi WrVTK - - - "Outputs VTK at each calcoutput call, even if main fst doesnt do it" - typedef ^ ^ IntKi VTKBlades - - - "Outputs VTk for each blade 0=no blade, 1=Bld 1" - typedef ^ ^ DbKi DTvtk - - - "Requested timestep between VTK outputs (calculated from the VTK_fps read in)" s diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index bcc75371d6..cdff226121 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -38,10 +38,11 @@ module FVW_SUBS integer(IntKi), parameter :: idShearNone = 0 integer(IntKi), parameter :: idShearMirror = 1 integer(IntKi), parameter, dimension(2) :: idShearVALID = (/idShearNone, idShearMirror /) - ! Tree Model - integer(IntKi), parameter :: idTreeNone = 0 - integer(IntKi), parameter :: idTreeBasic = 1 - integer(IntKi), parameter, dimension(2) :: idTreeVALID = (/idTreeNone, idTreeBasic /) + ! Velocity calculation method + integer(IntKi), parameter :: idVelocityBasic = 1 + integer(IntKi), parameter :: idVelocityTree = 2 + integer(IntKi), parameter :: idVelocityPart = 3 + integer(IntKi), parameter, dimension(3) :: idVelocityVALID = (/idVelocityBasic, idVelocityTree, idVelocityPart /) real(ReKi), parameter :: CoreSpreadAlpha = 1.25643 @@ -751,11 +752,9 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) integer(IntKi) :: nFWEff ! Number of farwake panels that are free at current tmie step logical :: bMirror ! True if we mirror the vorticity wrt ground ! TODO new options - integer(IntKi) :: nPart - integer(IntKi) :: nPartPerSeg - integer(IntKi) :: UIMethod integer(IntKi) :: RegFunctionPart - real(ReKi) :: BranchFactor, BranchSmall + integer(IntKi) :: nPart + real(ReKi) :: DistanceDirect ! Distance under which direct evaluation of the Biot-Savart should be done for tree type(T_Tree) :: Tree real(ReKi), dimension(:,:), allocatable :: PartPoints !< Particle points real(ReKi), dimension(:,:), allocatable :: PartAlpha !< Particle circulation @@ -783,37 +782,38 @@ subroutine WakeInducedVelocities(p, x, m, ErrStat, ErrMsg) endif ! --- Converting to particles - ! TODO new parameters - UIMethod = 1 ! 1=Seg, 2=PartTree, 3=Part, - BranchFactor = 2.0_ReKi !< Should be above1 - BranchSmall = 0.0_ReKi - if (UIMethod>1) then + if ((p%VelocityMethod==idVelocityTree) .or. (p%VelocityMethod==idVelocityPart)) then iHeadP=1 - nPartPerSeg = 1 - nPart = nPartPerSeg * nSeg + nPart = p%PartPerSegment * nSeg allocate(PartPoints(3,nPart), PartAlpha(3,nPart), PartEpsilon(nPart)) PartAlpha(:,:) = -99999.99_ReKi PartPoints(:,:) = -99999.99_ReKi PartEpsilon(:) = -99999.99_ReKi - call SegmentsToPart(m%SegPoints, m%SegConnct, m%SegGamma, m%SegEpsilon, 1, nSeg, nPartPerSeg, PartPoints, PartAlpha, PartEpsilon, iHeadP) + call SegmentsToPart(m%SegPoints, m%SegConnct, m%SegGamma, m%SegEpsilon, 1, nSeg, p%PartPerSegment, PartPoints, PartAlpha, PartEpsilon, iHeadP) if (p%RegFunction/=idRegNone) then - RegFunctionPart = idRegExp + RegFunctionPart = idRegExp ! TODO need to find a good equivalence and potentially adapt Epsilon in SegmentsToPart + endif + if (any(PartEpsilon(:)<-9999.99_ReKi)) then + print*,'Error in Segment to part conversion' + STOP endif endif ! --- Getting induced velocity m%Uind=0.0_ReKi ! very important due to side effects of ui_* methods - if (UIMethod==1) then + if (p%VelocityMethod==idVelocityBasic) then call ui_seg( 1, nCPs, m%CPs, 1, nSeg, nSeg, nSegP, m%SegPoints, m%SegConnct, m%SegGamma, p%RegFunction, m%SegEpsilon, m%Uind) - elseif (UIMethod==2) then + elseif (p%VelocityMethod==idVelocityTree) then + + DistanceDirect = 2*sum(PartEpsilon)/size(PartEpsilon) ! 2*mean(eps), below that distance eps has a strong effect call grow_tree(Tree, PartPoints, PartAlpha, RegFunctionPart, PartEpsilon, 0) !call print_tree(Tree) - call ui_tree(Tree, m%CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, m%Uind, ErrStat, ErrMsg) + call ui_tree(Tree, m%CPs, 0, 1, nCPs, p%TreeBranchFactor, DistanceDirect, m%Uind, ErrStat, ErrMsg) call cut_tree(Tree) deallocate(PartPoints, PartAlpha, PartEpsilon) - elseif (UIMethod==3) then + elseif (p%VelocityMethod==idVelocityPart) then call ui_part_nograd(m%CPs ,PartPoints, PartAlpha, RegFunctionPart, PartEpsilon, m%Uind, nCPs, nPart) deallocate(PartPoints, PartAlpha, PartEpsilon) endif diff --git a/modules/aerodyn/src/FVW_Tests.f90 b/modules/aerodyn/src/FVW_Tests.f90 index 3f7fb7f7cb..ac627ba4dd 100644 --- a/modules/aerodyn/src/FVW_Tests.f90 +++ b/modules/aerodyn/src/FVW_Tests.f90 @@ -369,10 +369,7 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) integer(IntKi) , intent(out) :: ErrStat !< Error status of the operation character(ErrMsgLen), intent(out) :: ErrMsg !< Error message if ErrStat /= ErrID_None type(T_Tree) :: Tree - real(ReKi), dimension(3) :: P1,CP real(ReKi), dimension(3) :: U_ref - real(ReKi), dimension(3) :: PartAlpha1 !< Particle intensity alpha=om.dV [m^3/s] - real(ReKi) :: RegParam1 !< integer(IntKi) :: i1,i2,i3,k, iCP integer(IntKi) :: RegFunction integer(IntKi) :: nPart = 1 @@ -401,7 +398,7 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) U_ref =0.0_ReKi call grow_tree(Tree, PartPoints, PartAlpha, RegFunction, RegParam, 0) !call print_tree(Tree) - call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) + call ui_tree(Tree, CPs, 0, 1, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) ! Test call test_almost_equal('Uind tree 0 part', U_ref, Uind2(:,1), 1e-4_ReKi, .true.,.true.) @@ -417,7 +414,7 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) U_ref =0.0_ReKi call grow_tree(Tree, PartPoints, PartAlpha, RegFunction, RegParam, 0) !call print_tree(Tree) - call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) + call ui_tree(Tree, CPs, 0, 1, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) ! Test call test_almost_equal('Uind tree 1 part', Uind1, Uind2, 1e-4_ReKi, .true.,.true.) @@ -450,7 +447,7 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) do iCP=1,4 CPs(:,1) = CPs_test(:,icp) Uind2=0.0_ReKi; Uind1=0.0_ReKi - call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) + call ui_tree(Tree, CPs, 0, 1, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) call ui_part_nograd(CPs,PartPoints, PartAlpha, RegFunction, RegParam, Uind1, nCPs, nPart) !print*,'Uind',Uind1, Uind2 ! Test @@ -458,7 +455,7 @@ subroutine Test_BiotSavart_PartTree(ErrStat, ErrMsg) enddo call cut_tree(Tree) ! --- Test that tree ui cannot be called after tree has been cut - call ui_tree(Tree, CPs, 0, 1, nCPs, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) + call ui_tree(Tree, CPs, 0, 1, nCPs, BranchFactor, BranchSmall, Uind2, ErrStat, ErrMsg) call test_equal('Err. stat tree cut',ErrStat,ErrID_Fatal) call dealloc() @@ -498,7 +495,7 @@ subroutine Test_SegmentsToPart(ErrStat, ErrMsg) real(ReKi), dimension(3,nCPsTot) :: Uind1 !< Induced velocity vector - Side effects!!! real(ReKi), dimension(3,nCPsTot) :: Uind2 !< Induced velocity vector - Side effects!!! real(ReKi) :: RegParam1 !< - integer(IntKi) :: i1,i2, nPartPerSeg, nPart, iHeadP + integer(IntKi) :: nPartPerSeg, nPart, iHeadP integer(IntKi) :: RegFunctionPart, RegFunctionSeg ErrStat = ErrID_None ErrMsg = "" @@ -732,6 +729,7 @@ subroutine FVW_RunTests(ErrStat,ErrMsg) ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" + testname='FVW' call Test_BiotSavart_Sgmt(ErrStat2, ErrMsg2) call Test_BiotSavart_Part(ErrStat2, ErrMsg2) call Test_BiotSavart_PartTree(ErrStat2, ErrMsg2) diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 91a4285898..8337724a2a 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -63,9 +63,9 @@ MODULE FVW_Types REAL(ReKi) :: WingRegParam !< Regularization parameter of the wing [-] INTEGER(IntKi) :: ShearModel !< Option for shear modelling [-] LOGICAL :: TwrShadowOnWake !< Include tower shadow effects on wake [-] - INTEGER(IntKi) :: TreeModel !< Tree calculation method [-] + INTEGER(IntKi) :: VelocityMethod !< Velocity calculation method [-] REAL(ReKi) :: TreeBranchFactor !< Factor used to determine if a point is far enough [-] - REAL(ReKi) :: TreeBranchSmall !< Distance below which a branch is consisdered small enough [-] + INTEGER(IntKi) :: PartPerSegment !< Number of particles per segment, e.g. for tree method [-] REAL(DbKi) :: DTaero !< Time interval for calls calculations [s] REAL(DbKi) :: DTfvw !< Time interval for calculating wake induced velocities [s] REAL(ReKi) :: KinVisc !< Kinematic air viscosity [m^2/s] @@ -218,9 +218,9 @@ MODULE FVW_Types REAL(ReKi) :: WingRegParam !< Factor used in the regularization [-] INTEGER(IntKi) :: ShearModel !< Option for shear modelling [-] LOGICAL :: TwrShadowOnWake !< Include tower shadow effects on wake [-] - INTEGER(IntKi) :: TreeModel !< Tree calculation method [-] + INTEGER(IntKi) :: VelocityMethod !< Velocity calculation method [-] REAL(ReKi) :: TreeBranchFactor !< Factor used to determine if a point is far enough [-] - REAL(ReKi) :: TreeBranchSmall !< Distance below which a branch is consisdered small enough [-] + INTEGER(IntKi) :: PartPerSegment !< Number of particles per segment, e.g. for tree method [-] INTEGER(IntKi) :: WrVTK !< Outputs VTK at each calcoutput call, even if main fst doesnt do it [-] INTEGER(IntKi) :: VTKBlades !< Outputs VTk for each blade 0=no blade, 1=Bld 1 [-] REAL(DbKi) :: DTvtk !< Requested timestep between VTK outputs (calculated from the VTK_fps read in) [s] @@ -314,9 +314,9 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%WingRegParam = SrcParamData%WingRegParam DstParamData%ShearModel = SrcParamData%ShearModel DstParamData%TwrShadowOnWake = SrcParamData%TwrShadowOnWake - DstParamData%TreeModel = SrcParamData%TreeModel + DstParamData%VelocityMethod = SrcParamData%VelocityMethod DstParamData%TreeBranchFactor = SrcParamData%TreeBranchFactor - DstParamData%TreeBranchSmall = SrcParamData%TreeBranchSmall + DstParamData%PartPerSegment = SrcParamData%PartPerSegment DstParamData%DTaero = SrcParamData%DTaero DstParamData%DTfvw = SrcParamData%DTfvw DstParamData%KinVisc = SrcParamData%KinVisc @@ -420,9 +420,9 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_BufSz = Re_BufSz + 1 ! WingRegParam Int_BufSz = Int_BufSz + 1 ! ShearModel Int_BufSz = Int_BufSz + 1 ! TwrShadowOnWake - Int_BufSz = Int_BufSz + 1 ! TreeModel + Int_BufSz = Int_BufSz + 1 ! VelocityMethod Re_BufSz = Re_BufSz + 1 ! TreeBranchFactor - Re_BufSz = Re_BufSz + 1 ! TreeBranchSmall + Int_BufSz = Int_BufSz + 1 ! PartPerSegment Db_BufSz = Db_BufSz + 1 ! DTaero Db_BufSz = Db_BufSz + 1 ! DTfvw Re_BufSz = Re_BufSz + 1 ! KinVisc @@ -549,12 +549,12 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%TwrShadowOnWake , IntKiBuf(1), 1) Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%TreeModel + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VelocityMethod Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TreeBranchFactor Re_Xferred = Re_Xferred + 1 - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TreeBranchSmall - Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%PartPerSegment + Int_Xferred = Int_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTaero Db_Xferred = Db_Xferred + 1 DbKiBuf ( Db_Xferred:Db_Xferred+(1)-1 ) = InData%DTfvw @@ -732,12 +732,12 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%TwrShadowOnWake = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) Int_Xferred = Int_Xferred + 1 - OutData%TreeModel = IntKiBuf( Int_Xferred ) + OutData%VelocityMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%TreeBranchFactor = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 - OutData%TreeBranchSmall = ReKiBuf( Re_Xferred ) - Re_Xferred = Re_Xferred + 1 + OutData%PartPerSegment = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%DTaero = DbKiBuf( Db_Xferred ) Db_Xferred = Db_Xferred + 1 OutData%DTfvw = DbKiBuf( Db_Xferred ) @@ -6298,9 +6298,9 @@ SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrS DstInputFileData%WingRegParam = SrcInputFileData%WingRegParam DstInputFileData%ShearModel = SrcInputFileData%ShearModel DstInputFileData%TwrShadowOnWake = SrcInputFileData%TwrShadowOnWake - DstInputFileData%TreeModel = SrcInputFileData%TreeModel + DstInputFileData%VelocityMethod = SrcInputFileData%VelocityMethod DstInputFileData%TreeBranchFactor = SrcInputFileData%TreeBranchFactor - DstInputFileData%TreeBranchSmall = SrcInputFileData%TreeBranchSmall + DstInputFileData%PartPerSegment = SrcInputFileData%PartPerSegment DstInputFileData%WrVTK = SrcInputFileData%WrVTK DstInputFileData%VTKBlades = SrcInputFileData%VTKBlades DstInputFileData%DTvtk = SrcInputFileData%DTvtk @@ -6377,9 +6377,9 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_BufSz = Re_BufSz + 1 ! WingRegParam Int_BufSz = Int_BufSz + 1 ! ShearModel Int_BufSz = Int_BufSz + 1 ! TwrShadowOnWake - Int_BufSz = Int_BufSz + 1 ! TreeModel + Int_BufSz = Int_BufSz + 1 ! VelocityMethod Re_BufSz = Re_BufSz + 1 ! TreeBranchFactor - Re_BufSz = Re_BufSz + 1 ! TreeBranchSmall + Int_BufSz = Int_BufSz + 1 ! PartPerSegment Int_BufSz = Int_BufSz + 1 ! WrVTK Int_BufSz = Int_BufSz + 1 ! VTKBlades Db_BufSz = Db_BufSz + 1 ! DTvtk @@ -6461,12 +6461,12 @@ SUBROUTINE FVW_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+1-1 ) = TRANSFER( InData%TwrShadowOnWake , IntKiBuf(1), 1) Int_Xferred = Int_Xferred + 1 - IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%TreeModel + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VelocityMethod Int_Xferred = Int_Xferred + 1 ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TreeBranchFactor Re_Xferred = Re_Xferred + 1 - ReKiBuf ( Re_Xferred:Re_Xferred+(1)-1 ) = InData%TreeBranchSmall - Re_Xferred = Re_Xferred + 1 + IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%PartPerSegment + Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%WrVTK Int_Xferred = Int_Xferred + 1 IntKiBuf ( Int_Xferred:Int_Xferred+(1)-1 ) = InData%VTKBlades @@ -6559,12 +6559,12 @@ SUBROUTINE FVW_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%TwrShadowOnWake = TRANSFER( IntKiBuf( Int_Xferred ), mask0 ) Int_Xferred = Int_Xferred + 1 - OutData%TreeModel = IntKiBuf( Int_Xferred ) + OutData%VelocityMethod = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%TreeBranchFactor = ReKiBuf( Re_Xferred ) Re_Xferred = Re_Xferred + 1 - OutData%TreeBranchSmall = ReKiBuf( Re_Xferred ) - Re_Xferred = Re_Xferred + 1 + OutData%PartPerSegment = IntKiBuf( Int_Xferred ) + Int_Xferred = Int_Xferred + 1 OutData%WrVTK = IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 OutData%VTKBlades = IntKiBuf( Int_Xferred ) diff --git a/modules/aerodyn/src/FVW_VortexTools.f90 b/modules/aerodyn/src/FVW_VortexTools.f90 index ac96c93337..dc97783624 100644 --- a/modules/aerodyn/src/FVW_VortexTools.f90 +++ b/modules/aerodyn/src/FVW_VortexTools.f90 @@ -198,32 +198,40 @@ subroutine SegmentsToPart(SegPoints, SegConnct, SegGamma, SegEpsilon, iSegStart, real(ReKi), dimension(:,:), intent(inout) :: PartPoints !< Particle points (3 x nPart) real(ReKi), dimension(:,:), intent(inout) :: PartAlpha !< Particle intensities (3 x nPart) real(ReKi), dimension(:), intent(inout) :: PartEpsilon !< Particle regularization parameter (nPart) - integer, intent(inout) :: iHeadPart !< Index where to start in Part* vectors + integer, optional, intent(inout) :: iHeadPart !< Index where to start in Part* vectors real(ReKi), dimension(3) :: P1, P2, DP !< Segment extremities real(ReKi), dimension(3) :: SegDir !< direction vector real(ReKi), dimension(3) :: PartInt real(ReKi) :: PartLen !< Initial "length" of the blob real(ReKi) :: PartEps !< Regularization of the blob real(ReKi) :: SegLen - integer(IntKi) :: ip1, ip2 !< index of points to be converted + integer(IntKi) :: iPart !< index in particle vectors integer(IntKi) :: iSeg, iSubPart + if (present(iHeadPart)) then + iPart = iHeadPart + else + iPart = 1 + endif ! loop on selected segments do iSeg=iSegStart,iSegEnd P1 = SegPoints(1:3,SegConnct(1,iSeg)) ! Segment extremities P2 = SegPoints(1:3,SegConnct(2,iSeg)) DP = P2-P1 - SegLen = norm2(DP) + SegLen = sqrt(DP(1)**2 + DP(2)**2 + DP(3)**2) SegDir = DP/SegLen ! Unit vector along segment direction PartInt = DP*SegGamma(iSeg)/nPartPerSeg ! alpha = Gamma.L/n = omega.dV [m^3/s] PartEps = SegEpsilon(iSeg) ! TODO this might need tuning depending on RegFunction and n_new PartLen = SegLen/nPartPerSeg do iSubPart=0,nPartPerSeg-1 - PartPoints(1:3, iHeadPart) = P1(1:3) + (0.5_ReKi+iSubPart)*PartLen*SegDir(1:3) ! ds/2:ds:L - PartAlpha (1:3, iHeadPart) = PartInt(1:3) - PartEpsilon( iHeadPart) = PartEps - iHeadPart = iHeadPart +1 + PartPoints(1:3, iPart) = P1(1:3) + (0.5_ReKi+iSubPart)*PartLen*SegDir(1:3) ! ds/2:ds:L + PartAlpha (1:3, iPart) = PartInt(1:3) + PartEpsilon( iPart) = PartEps + iPart = iPart +1 enddo enddo + if (present(iHeadPart)) then + iHeadPart=iPart + endif end subroutine SegmentsToPart subroutine print_mean_4d(M, Label) @@ -334,12 +342,8 @@ subroutine grow_tree(Tree, PartP, PartAlpha, PartRegFunction, PartRegParam, iSte node%nPart = 1 else ! Domain dimensions - max_x=maxval(Part%P(1,1:Part%n)) - max_y=maxval(Part%P(2,1:Part%n)) - max_z=maxval(Part%P(3,1:Part%n)) - min_x=minval(Part%P(1,1:Part%n)) - min_y=minval(Part%P(2,1:Part%n)) - min_z=minval(Part%P(3,1:Part%n)) + max_x=maxval(Part%P(1,1:Part%n)); max_y=maxval(Part%P(2,1:Part%n)); max_z=maxval(Part%P(3,1:Part%n)) + min_x=minval(Part%P(1,1:Part%n)); min_y=minval(Part%P(2,1:Part%n)); min_z=minval(Part%P(3,1:Part%n)) ! Init of trunc ! Radius taken slightly bigger than domain extent. This radius will be divided by 2 successively @@ -369,27 +373,22 @@ subroutine grow_tree(Tree, PartP, PartAlpha, PartRegFunction, PartRegParam, iSte Tree%bGrown = .true. end subroutine grow_tree + !> Recursive function to grow/setup a tree. + !! Note, needed preliminary calc are done by grow_tree before recursive subroutine grow_tree_rec(node, Part) type(T_Node), target :: node !< type(T_Part), intent(in) :: Part !< integer :: i - ! Test if there are enough particles on the node to build new branchess - ! The case of only one particle should be handled upstream by allocating one leaf to the parent node -! if(node%nPart>1) then - ! Sub Step: - ! - compute moments and center for the current node - ! - allocate branches and leaves - call grow_tree_substep(node, Part) - ! Call grow_tree on branches - if(associated(node%branches)) then - do i = 1,size(node%branches) - call grow_tree_rec(node%branches(i), Part) - end do - endif -! else -! print*,'nPart',node%nPart, "Build tree rec called with npart<=1" -! STOP -! endif + ! Sub Step: + ! - compute moments and center for the current node + ! - allocate branches and leaves + call grow_tree_substep(node, Part) + ! Call grow_tree on branches + if(associated(node%branches)) then + do i = 1,size(node%branches) + call grow_tree_rec(node%branches(i), Part) + end do + endif end subroutine grow_tree_rec !> Perform a substep of tree growth, growing sub branches from a given node/cell @@ -398,7 +397,7 @@ end subroutine grow_tree_rec !! - Compute node center (barycenter of vorticity) !! - Compute node moments !! - Distribute particles in each 8 octants. Branches are not created for empty octant - !! - Allocate branches and distribute particles to them + !! - Allocate branches and leaves and distribute particles to them subroutine grow_tree_substep(node, Part) type(T_Node), intent(inout) :: node !< Current node we are growing from type(T_Part), intent(in) :: Part !< All particles info @@ -435,8 +434,8 @@ subroutine grow_tree_substep(node, Part) if (associated(node%iPart)) deallocate(node%iPart) return ! NOTE: we exit endif - nodeBaryCenter = nodeBaryCenter/wTot ! barycenter of vorticity - node%center = nodeBaryCenter ! updating + nodeBaryCenter = nodeBaryCenter/wTot ! barycenter of vorticity + node%center = nodeBaryCenter ! updating ! --- Calculation of moments about nodeBaryCenter do i = 1,node%nPart @@ -457,7 +456,7 @@ subroutine grow_tree_substep(node, Part) end do end do - ! --- Distributing particles to the 8 octants, based on the geometric center! + ! --- Distributing particles to the 8 octants (based on the geometric center!) allocate (PartOctant(1:node%nPart)) npart_per_octant(1:8)=0 do i = 1,node%nPart @@ -467,12 +466,13 @@ subroutine grow_tree_substep(node, Part) if (PartPos(1) > nodeGeomCenter(1)) iPartOctant = iPartOctant + int(1,IK1) if (PartPos(2) > nodeGeomCenter(2)) iPartOctant = iPartOctant + int(2,IK1) if (PartPos(3) > nodeGeomCenter(3)) iPartOctant = iPartOctant + int(4,IK1) - npart_per_octant(iPartOctant) = npart_per_octant(iPartOctant) + 1 ! Counter of particles per branch + npart_per_octant(iPartOctant) = npart_per_octant(iPartOctant) + 1 ! Counter of particles per octant PartOctant(i)=iPartOctant ! Store in which octant particle i is end do ! --- Leaves and branches ! A node contains a combination of child nodes and leaves (single particles) + ! TODO: introduce a "minimum cell size", (e.g. cell radius is less than the Distance for direct evaluation, then all should be leaves) nLeaves = 0 nBranches = 0 octant2branches = 0 @@ -484,6 +484,7 @@ subroutine grow_tree_substep(node, Part) else if(npart_per_octant(iOctant)>1) then if (npart_per_octant(iOctant)==node%nPart) then ! All particle falls into the same octant, if they all have the same location, we would divide forever. + ! Quick fix below max_x=maxval(Part%P(1,node%iPart(:))); max_y=maxval(Part%P(2,node%iPart(:))); max_z=maxval(Part%P(3,node%iPart(:))) min_x=minval(Part%P(1,node%iPart(:))); min_y=minval(Part%P(2,node%iPart(:))); min_z=minval(Part%P(3,node%iPart(:))) if (max(abs(max_x-min_x),abs(max_y-min_y),abs(max_z-min_z))< 1.0e-5) then @@ -524,9 +525,9 @@ subroutine grow_tree_substep(node, Part) ! NOTE: this is geometric center not barycenter locCenter = nodeGeomCenter + 0.5*halfSize*(/ (-1)**(iOctant), (-1)**floor(0.5*real(iOctant-1)+1), (-1)**floor(0.25*real(iOctant-1)+1) /) ! Init of branches - node%branches(iBranch)%radius = halfSize !< TODO NAN - node%branches(iBranch)%center = locCenter !< TODO NAN - node%branches(iBranch)%Moments = 0.0_ReKi !< TODO NAN + node%branches(iBranch)%radius = halfSize ! + node%branches(iBranch)%center = locCenter ! NOTE: this is the geometric center + node%branches(iBranch)%Moments = 0.0_ReKi ! node%branches(iBranch)%branches=>null() node%branches(iBranch)%leaves=>null() endif @@ -536,7 +537,7 @@ subroutine grow_tree_substep(node, Part) ! Store indices of the particles the sub-branch contains i1=0; i2=0; i3=0; i4=0; i5=0; i6=0; i7=0; i8=0; do i = 1,node%nPart - iBranch = octant2branches(PartOctant(i)) + iBranch = octant2branches(PartOctant(i)) if(iBranch>0) then select case(iBranch) case(1);i1=i1+1; node%branches(1)%iPart(i1) = node%iPart(i) @@ -562,6 +563,8 @@ subroutine grow_tree_substep(node, Part) if (allocated(PartOctant)) deallocate(PartOctant) end subroutine grow_tree_substep + !> Grow a tree in "parallel", since recursive calls cannot be parallized easily, we unroll the different layer calls + !! Note, needed preliminary calc are done by grow_tree before! subroutine grow_tree_parallel(Root, Part) type(T_Node), intent(inout) :: Root type(T_Part), intent(in) :: Part @@ -569,7 +572,7 @@ subroutine grow_tree_parallel(Root, Part) integer :: i1 integer :: i2 - ! --- Unrolled version of the grow_tree for the first node + ! --- Unrolled version of grow_tree for the first node ! Sub Step: ! - compute moments and center for the current node ! - allocate branches and leaves @@ -580,14 +583,14 @@ subroutine grow_tree_parallel(Root, Part) else nBranches=size(Root%branches) if (nBranches==0) then - print*,'No branches' + print*,'No branches' ! This should not happen STOP else - ! Call "buildTree" on branches + ! Call "grow_tree" on branches !$OMP PARALLEL default(shared) - ! --- Unrolled version of the grow_tree for the second levels + ! --- Unrolled version of grow_tree for the second levels !$OMP do private(i) schedule(runtime) do i = 1,nBranches ! maximum 8 branches if(Root%branches(i)%nPart>1) then ! I dont think this test is needed @@ -596,7 +599,7 @@ subroutine grow_tree_parallel(Root, Part) end do !$OMP end do !$OMP barrier ! we need to be sure that all the branches were built - ! --- Unrolled version of the grow_tree for third node levels + ! --- Unrolled version of grow_tree for third node levels !$OMP do private(i,i1,i2) schedule(runtime) do i = 1,nBranches*8 ! maximum 64 sub branches i1=(i-1)/8+1; @@ -606,9 +609,9 @@ subroutine grow_tree_parallel(Root, Part) call grow_tree_rec(Root%branches(i1)%branches(i2), Part) endif endif - enddo !$OMP end do + !Note: We could add more levels !$OMP END PARALLEL endif endif @@ -737,15 +740,14 @@ end subroutine print_tree ! -------------------------------------------------------------------------------- ! --- Velocity computation ! -------------------------------------------------------------------------------- - subroutine ui_tree(Tree, CPs, ioff, icp_beg, icp_end, nCPs, BranchFactor, BranchSmall, Uind, ErrStat, ErrMsg) + subroutine ui_tree(Tree, CPs, ioff, icp_beg, icp_end, BranchFactor, DistanceDirect, Uind, ErrStat, ErrMsg) use FVW_BiotSavart, only: fourpi_inv, ui_part_nograd_11 type(T_Tree), target, intent(inout) :: Tree !< - integer, intent(in ) :: nCPs !< integer, intent(in ) :: ioff !< integer, intent(in ) :: icp_beg !< integer, intent(in ) :: icp_end !< real(ReKi), intent(in ) :: BranchFactor !< - real(ReKi), intent(in ) :: BranchSmall !< + real(ReKi), intent(in ) :: DistanceDirect !< Distance under which direct evaluation should be done no matter what the tree cell size is real(ReKi), dimension(:,:), intent(in ) :: CPs !< Control Points (3 x nCPs) real(ReKi), dimension(:,:), intent(inout) :: Uind !< Induced velocity at CPs, with side effects (3 x nCPs) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation @@ -777,9 +779,8 @@ recursive subroutine ui_tree_11(node, CP, Uind, nDirect, nQuad) real(ReKi),dimension(3),intent(inout) :: CP, Uind !< Velocity at control point, with side effect integer, intent(inout) :: nDirect,nQuad type(T_Node), intent(inout) :: node - real(ReKi) :: distMin, coeff - real(ReKi),dimension(3) :: DeltaP, phi, quad, Uind_B - real(ReKi),dimension(3) :: Uind_T + real(ReKi) :: distDirect, coeff + real(ReKi),dimension(3) :: DeltaP, phi, Uloc real(ReKi) :: x,y,z,mx,my,mz,r integer :: i,j,ieqj integer :: iPart @@ -791,29 +792,30 @@ recursive subroutine ui_tree_11(node, CP, Uind, nDirect, nQuad) do i =1,size(node%leaves) iPart=node%leaves(i) DeltaP = CP(1:3) - Part%P(1:3,iPart) - call ui_part_nograd_11(DeltaP, Part%Alpha(1:3,iPart), Part%RegFunction, Part%RegParam(iPart), Uind_B) + call ui_part_nograd_11(DeltaP, Part%Alpha(1:3,iPart), Part%RegFunction, Part%RegParam(iPart), Uloc) nDirect=nDirect+1 - Uind(1:3) = Uind(1:3) + Uind_B + Uind(1:3) = Uind(1:3) + Uloc enddo endif else - distMin = BranchFactor*node%radius ! Minimum distance for quadrupole calculation - DeltaP = - node%center + CP(1:3) ! Vector between the control point and the center of the branch - r = ( DeltaP(1)**2 + DeltaP(2)**2 + DeltaP(3)**2) ** 0.5 ! Norm - ! Test if the control point is far enough from the branch node - if ((r Direct eval., Above it -> quadrupole calculation + DeltaP = - node%center + CP(1:3) ! Vector between the control point and the center of the branch + r = sqrt( DeltaP(1)**2 + DeltaP(2)**2 + DeltaP(3)**2) + ! Test if the control point is too close from the branch node so that a direct evaluation is needed + if (r Date: Mon, 27 Apr 2020 11:27:20 -0600 Subject: [PATCH 141/190] FVW: updating documentation to match the technical report --- docs/source/user/aerodyn-fvw/AppendixA.rst | 5 +- docs/source/user/aerodyn-fvw/AppendixB.rst | 7 +- docs/source/user/aerodyn-fvw/AppendixC.rst | 2 +- .../{ => ExampleFiles}/ExampleFile--OLAF.txt | 38 ++-- .../ExampleFile--PrescribeCirc.txt | 0 docs/source/user/aerodyn-fvw/Introduction.rst | 42 +++-- docs/source/user/aerodyn-fvw/RunningOLAF.rst | 172 +----------------- .../Schematics/FVWwithOpenFAST.png | Bin 0 -> 14529 bytes .../Schematics/LagrangianMarkers.png | Bin 0 -> 42195 bytes .../user/aerodyn-fvw/Schematics/OpenFAST.png | Bin 0 -> 92541 bytes .../user/aerodyn-fvw/Schematics/Stencil.png | Bin 0 -> 13929 bytes docs/source/user/aerodyn-fvw/bibliography.bib | 2 +- 12 files changed, 60 insertions(+), 208 deletions(-) rename docs/source/user/aerodyn-fvw/{ => ExampleFiles}/ExampleFile--OLAF.txt (57%) rename docs/source/user/aerodyn-fvw/{ => ExampleFiles}/ExampleFile--PrescribeCirc.txt (100%) create mode 100644 docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.png create mode 100644 docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.png create mode 100644 docs/source/user/aerodyn-fvw/Schematics/OpenFAST.png create mode 100644 docs/source/user/aerodyn-fvw/Schematics/Stencil.png diff --git a/docs/source/user/aerodyn-fvw/AppendixA.rst b/docs/source/user/aerodyn-fvw/AppendixA.rst index 8337463cba..8b15288450 100644 --- a/docs/source/user/aerodyn-fvw/AppendixA.rst +++ b/docs/source/user/aerodyn-fvw/AppendixA.rst @@ -3,14 +3,11 @@ Appendix A: OLAF Primary Input File =================================== -Note that when a line in the following text starts with an indent, it is -really a continuation of the previous line and should not be implemented -as a new line in the actual input file. **Check the regression test cases for updates to this input file.** .. container:: :name: Tab:OLAFinputfile - .. literalinclude:: ExampleFile--OLAF.txt + .. literalinclude:: ExampleFiles/ExampleFile--OLAF.txt :linenos: diff --git a/docs/source/user/aerodyn-fvw/AppendixB.rst b/docs/source/user/aerodyn-fvw/AppendixB.rst index 03580bcba3..274aa7437f 100644 --- a/docs/source/user/aerodyn-fvw/AppendixB.rst +++ b/docs/source/user/aerodyn-fvw/AppendixB.rst @@ -1,11 +1,12 @@ .. _Prescribed-Circulation-Input-File: Appendix B: Prescribed Circulation Input File -================================= +============================================= **Check the regression tests for updated versions of this file.** .. container:: - :name: Tab:PrescribeCirc - .. literalinclude:: ExampleFile--PrescribeCirc.txt + :name: TabPrescribeCirc + + .. literalinclude:: ExampleFiles/ExampleFile--PrescribeCirc.txt :linenos: diff --git a/docs/source/user/aerodyn-fvw/AppendixC.rst b/docs/source/user/aerodyn-fvw/AppendixC.rst index f130bfe4d6..3dd8805c1e 100644 --- a/docs/source/user/aerodyn-fvw/AppendixC.rst +++ b/docs/source/user/aerodyn-fvw/AppendixC.rst @@ -1,7 +1,7 @@ .. _OLAF-List-of-Output-Channels: Appendix C: OLAF List of Output Channels -============================ +======================================== This is a list of all possible output parameters from the OLAF module. The names are grouped by meaning, but can be ordered in the OUTPUTS diff --git a/docs/source/user/aerodyn-fvw/ExampleFile--OLAF.txt b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt similarity index 57% rename from docs/source/user/aerodyn-fvw/ExampleFile--OLAF.txt rename to docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt index 2d7bf1a03f..029a68f82e 100644 --- a/docs/source/user/aerodyn-fvw/ExampleFile--OLAF.txt +++ b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--OLAF.txt @@ -1,30 +1,30 @@ -------- FREE WAKE INPUT FILE ------------------------------------------- +--------------------------- FREE WAKE INPUT FILE ---------------------------------------------- Free wake input file for the BAR turbine -------- GENERAL OPTIONS ------------------------------------------- +--------------------------- GENERAL OPTIONS --------------------------------------------------- 5 IntMethod Integration method {4: 2nd order Predictor/Corrector, 5: Forward Euler 1st order, "default": 5} (switch) -0.2 DTfvw Time interval for wake propagation. {"default": dtaero} (sec) -5 FreeWakeStart Time when wake is "free". (-) value = always free. {"default":0.0} (sec) -2.0 FullCircStart Time at which full circulation is reached. {"default": 0.0} (sec) -------- CIRCULATION SPECIFICATIONS ------------------------------------------- +0.2 DTfvw Time interval for wake propagation. {"default": dtaero} (s) +5 FreeWakeStart Time when wake is free. (-) value = always free. {"default":0.0} (s) +2.0 FullCircStart Time at which full circulation is reached. {"default": 0.0} (s) +--------------------------- CIRCULATION SPECIFICATIONS ---------------------------------------- 1 CircSolvingMethod Circulation solving method {1: Cl-Based, 2: No-Flow Through, 3: Prescribed, "default":1 }(switch) -0.01 CircSolvConvCrit Convergence criteria {"default": 0.01} [only if CircSolvingMethod=1] (m$^2$/s) +0.01 CircSolvConvCrit Convergence criteria {"default": 0.01} [only if CircSolvingMethod=1] (m^2/s) 0.1 CircSolvRelaxation Relaxation factor {"default": 0.1} [only if CircSolvingMethod=1] (-) 30 CircSolvMaxIter Maximum number of iterations for circulation solving {"default": 30} (-) -"circ.csv" PrescribedCircFile File containing prescribed circulation [only if CircSolvingMethod=3] (quoted string) -------- WAKE OPTIONS ------------------------------------------- +"NA" PrescribedCircFile File containing prescribed circulation [only if CircSolvingMethod=3] (quoted string) +--------------------------- WAKE OPTIONS ----------------------------------------------------- 50 nNWPanel Number of near-wake panels (-) 7 WakeLength Total wake distance (D) -5 FreeWakeLength Wake length that is "free" (D) +5 FreeWakeLength Wake length that is free (D) False FWShedVorticity Include shed vorticity in the far wake {"default": 0.1} -0 DiffusionMethod Diffusion method to account for viscous effects {0=None, 1=Core Spreading, "default": 0} -0 RegDeterMethod Method to determine the regularization parameters {0=Manual, 1=Optimized, "default": 0 } +0 DiffusionMethod Diffusion method to account for viscous effects {0: None, 1: Core Spreading, "default": 0} +0 RegDeterMethod Method to determine the regularization parameters {0: Manual, 1: Optimized, "default": 0} 2 RegFunction Viscous diffusion function {0: None, 1: Rankine, 2: LambOseen, 3: Vatistas, 4: Denominator, "default": 3} (switch) 0 WakeRegMethod Wake regularization method {1: Constant, 2: Stretching, 3: Age, "default": 1} (switch) -2.0 WakeRegFactor Wake regularization factor -2.0 WingRegFactor Wing regularization factor +2.0 WakeRegFactor Wake regularization factor (m) +2.0 WingRegFactor Wing regularization factor (m) 100 CoreSpreadEddyVisc Eddy viscosity in core spreading methods, typical values 1-1000 -------- OUTPUT OPTIONS ------------------------------------------- -True WrVTk Outputs Visualization Toolkit (VTK) (independent of .fst option) (False: NoVTK, True: Write VTK at each time step) (flag) -1 nVTKBlades Number of blades for which VTK files are exported (0: No VTK per blade, n: VTK for blade 1 to n) (-) -2 VTKCoord Coordinate system used for VTK export. {1=Global, 2=Hub, "default": 1} -default VTK_fps Frame rate for VTK output (frames per second) {"all" for all glue code timesteps, "default" for all FVW timesteps} [used only if WrVTK=1] +--------------------------- OUTPUT OPTIONS --------------------------------------------------- +True WrVTk Outputs Visualization Toolkit (VTK) (independent of .fst option) {False: NoVTK, True: Write VTK at each time step} (flag) +1 nVTKBlades Number of blades for which VTK files are exported {0: No VTK per blade, n: VTK for blade 1 to n} (-) +2 VTKCoord Coordinate system used for VTK export. {1: Global, 2: Hub, "default": 1} +"default" VTK_fps Frame rate for VTK output (frames per second) {"all" for all glue code timesteps, "default" for all FVW timesteps} [used only if WrVTK=1] diff --git a/docs/source/user/aerodyn-fvw/ExampleFile--PrescribeCirc.txt b/docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--PrescribeCirc.txt similarity index 100% rename from docs/source/user/aerodyn-fvw/ExampleFile--PrescribeCirc.txt rename to docs/source/user/aerodyn-fvw/ExampleFiles/ExampleFile--PrescribeCirc.txt diff --git a/docs/source/user/aerodyn-fvw/Introduction.rst b/docs/source/user/aerodyn-fvw/Introduction.rst index e8b6d02129..6950315714 100644 --- a/docs/source/user/aerodyn-fvw/Introduction.rst +++ b/docs/source/user/aerodyn-fvw/Introduction.rst @@ -11,13 +11,32 @@ the National Renewable Energy Laboratory physics-based engineering tool, OpenFAST, which solves the aero-hydro-servo-elastic dynamics of individual wind turbines. OLAF is incorporated into the OpenFAST module, *AeroDyn15*, as an alternative to the traditional blade-element momentum -(BEM) option, as shown in Figure `[OpenFAST] <#OpenFAST>`__. +(BEM) option, as shown in Figures :numref:`figOpenFAST_a` and +:numref:`figOpenFAST_b`. + +.. _figOpenFAST_a: + +.. figure:: Schematics/OpenFAST.png + :alt: OpenFAST schematic + :width: 100% + :align: center + + OpenFAST schematic + +.. _figOpenFAST_b: + +.. figure:: Schematics/FVWwithOpenFAST.png + :alt: OpenFAST overview schematic and OLAF integration + :width: 100% + :align: center + + OLAF and BEM integration with AeroDyn Incorporating the FVW module within OpenFAST allows for the modeling of highly flexible turbines along with the aero-hydro-servo-elastic response capabilities of OpenFAST. The OLAF module follows the requirements of the OpenFAST modularization framework  -(:cite:`Sprague15-1, Jonkman13-1`). +(:cite:`Sprague15_1,Jonkman13_1`). The OLAF module uses a lifting-line representation of the blades, which is characterized by a distribution of bound circulation. The spatial and @@ -27,14 +46,15 @@ manner, which allows the vortices to convect, stretch, and diffuse. The FVW model is based on a Lagrangian approach, in which the turbine wake is discretized into Lagrangian markers. There are many methods of representing the wake with Lagrangian -markers (:cite:`Branlard17-1`). In this work, a hybrid +markers (:cite:`Branlard17_1`). In this work, a hybrid lattice/filament method is used, as depicted in -Figure `1.1 <#Lagrangian>`__. +Figure :numref:`Lagrangian`. -.. figure:: Schematics/LagrangianMarkers.pdf - :alt: Evolution of near-wake lattice, blade-tip vortex, and - Lagrangian markers +.. figure:: Schematics/LagrangianMarkers.png + :alt: Evolution of near-wake lattice, blade-tip vortex, and Lagrangian markers :name: Lagrangian + :width: 100% + :align: center Evolution of near-wake lattice, blade-tip vortex, and Lagrangian markers @@ -44,15 +64,15 @@ age, :math:`\zeta`, and azimuthal position, :math:`\psi`. A lattice method is used in the near wake of the blade. The near wake spans over a user-specified angle or distance for nonrotating cases. Though past research has indicated that a near-wake region of :math:`30^\circ` is -sufficient (:cite:`Leishman-book, Ananthan02-1`), it has +sufficient (:cite:`Leishman_book,Ananthan02_1`), it has been shown that a larger near wake is required for high thrust and other challenging conditions. After this period, the wake is assumed to instantaneously roll up into a tip vortex and, optionally, a root vortex, which are assumed to be the most dominant features for the -remainder of the wake (:cite:`Leishman02-1`). Each +remainder of the wake (:cite:`Leishman02_1`). Each Lagrangian marker is connected to adjacent markers by straight-line vortex filaments, approximated to second-order -accuracy (:cite:`Gupta05-1`). The wake is discretized based +accuracy (:cite:`Gupta05_1`). The wake is discretized based on the spanwise location of the blade sections and a specified time step (:math:`dt`), which may be different from the time step of AeroDyn. After an optional initialization period, the wake is allowed to move and @@ -65,7 +85,7 @@ is "frozen" in a buffer zone between a distance, :math:`d_\text{buffer}`, and the distance, :math:`d_\text{trunc}`. In this buffer zone, the markers convect at the average ambient velocity. In this way, truncation error is -minimized (:cite:`Leishman02-1`). The buffer zone is +minimized (:cite:`Leishman02_1`). The buffer zone is typically chosen as the convected distance over one rotor revolution. As part of OpenFAST, induced velocities at the lifting line/blade are diff --git a/docs/source/user/aerodyn-fvw/RunningOLAF.rst b/docs/source/user/aerodyn-fvw/RunningOLAF.rst index 2fbb78a689..54bf62131a 100644 --- a/docs/source/user/aerodyn-fvw/RunningOLAF.rst +++ b/docs/source/user/aerodyn-fvw/RunningOLAF.rst @@ -3,173 +3,7 @@ Running OLAF ============ -This section discusses how to obtain and execute OLAF from a personal -computer. +As OLAF is a module of OpenFAST, the process of downloading, compiling, +and running OLAF is the same as that for OpenFAST. Such instructions are +available in the :ref:`installation` documentation. -Advanced command line users looking for a quick guide may follow the -commands listed here; other users are recommended to follow the detailed -instructions in the coming paragraphs. - -:: - - # sudo apt-get install gfortran-8 cmake liblapack-dev libblas - # OR - # module load cmake comp-intel mkl - git clone https://github.com/openfast/openfast - cd openfast - mkdir build - cd build - cmake .. - make - ./modules/glue-codes/openfast/openfast [INPUTFILE] - -Downloading the OLAF Software ------------------------------ - -Download the OpenFAST archive, which includes OLAF, from the git -repository at https://github.com/OpenFAST/openfast.git. - -Compiling OpenFAST with OLAF ----------------------------- - -This is the same process as compiling OpenFAST. The reader may obtain -detailed instructions at the following address: -https://openfast.readthedocs.io/. For completeness, basic instructions -are included here. - -Compiling on a Microsoft Windows Machine -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Three main options are available to Windows users: - -- Commercial software: install Intel Fortran Compiler and Microsoft - Visual Studio. Open the visual studio solution file, - ``vs-build/FAST/FAST.sln, with Visual Studio. From the Visual Studio menu, select: Build/Build solution.`` - -- Linux subsystem: Install a Linux subsystem for Windows - (https://docs.microsoft.com/en-us/windows/wsl/install-win10. You may - be choose Ubuntu. Then, in a terminal, follow the instructions for - Linux machines provided in . Note that the compiled code will only be - run within the Linux subsystem environment, but the input and output - files can be accessed seamlessly from Windows. - -- Free software: Compilation using free software is an involved process - reserved to advanced users. Users interested in such options are - referred to https://openfast.readthedocs.io/. - -.. _sec:CompileLinux: - -Compiling on Linux/Mac Machines -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. _sec:linuxdep: - -Dependencies/requirements -^^^^^^^^^^^^^^^^^^^^^^^^^ - -Compiling OpenFAST requires CMake, a Fortran compiler (Intel Fortran or -GFortran), and Linear Algebra Package (LAPACK) libraries (LAPACK or Math -Kernal Library [MKL]). - -- If you are in a Linux system such as Ubuntu, all the dependencies can - be installed as follows: - - ``$ sudo apt-get install gfortran-8 cmake liblapack-dev libblas`` - -- If you are in a cluster environment, such tools are usually already - installed but need to be loaded into the environment. For example, - the following command can load CMake, the Intel Fortran compiler, and - the MKL libraries: - - ``$ module load cmake comp-intel mkl`` - -Additional resources are provided in . - -Compilation -^^^^^^^^^^^ - -The compilation steps are as follows: - -#. Optional: if you are in a cluster environment, make sure the - dependencies are loaded (see ) - -#. In the OpenFAST directory, create a build directory and move into it: - - ``$ mkdir build`` - - ``$ cd build`` - -#. In the build directory, run the following commands: - - ``$ cmake ..`` - - ``$ make`` - -Advanced users may choose to customize the compilation step by providing -extra arguments to the CMake command. For instance, compiling the code -with the ``DEBUG option and single precision is done as follows:`` - -``$ cmake .. -DCMAKE_BUILD_Type=Debug -DDOUBLE_PRECISION:BOOL=OFF`` - -Compiling the code with optimization flags is done as follows: - -``$ cmake .. -DCMAKE_Fortran_FLAGS_RELEASE="-O2 -xhost" -DDOUBLE_PRECISION:BOOL=OFF`` - -.. _sec:cmdOptions: - -Command/option explanations -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``git clone`` :math:`\--` Git is a free and open-source, distributed, -version-control system designed to handle everything from small to very -large projects with speed and efficiency. :math:`\$` git clone clones a -remote repository into a new directory: - -``mkdir`` :math:`\--` make a new directory - -``cd`` :math:`\--` move into the specified directory - -``module load`` :math:`\--` load specified modules (required on most -high-performance computing systems) - -``cmake`` :math:`\--` CMake is an open-source, cross-platform family of -tools designed to build, test, and package software - -``DCMAKE_Fortran_FLAGS_RELEASE`` :math:`\--` specify optional -compilation flags - -``threads`` and ``qopenmp`` :math:`\--` required flags to run FAST.Farm -with OpenMP parallelization - -``O2`` :math:`\--` optimization-level specification (recommended when -not working to debug code) - -``xhost`` :math:`\--` additional optimization-level specification - -``DDOUBLE_PRECISION:BOOL=OFF`` :math:`\--` compile code in single -precision (recommended) - -``make`` :math:`\--` compile code using Makefile located in directory -(here, Makefile was generated with CMake). - -.. _running-olaf-1: - -Running OLAF ------------- - -#. Executable file will be located in: - - - PC: ``~/OpenFAST/build/bin/openfast`` - - - Mac/Linux: ``~/OpenFAST/build/glue-codes/openfast/openfast`` - -#. Create a directory to all your OpenFAST and OLAF input files. You - will need: - - - ``.fst`` and associated ``.dat`` files :math:`\-–` - turbine files - - - Wind inflow files - -#. Run code with - ``$~/OpenFAST/build/glue-codes/openfast/openfast .fst``. diff --git a/docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.png b/docs/source/user/aerodyn-fvw/Schematics/FVWwithOpenFAST.png new file mode 100644 index 0000000000000000000000000000000000000000..e474d79c1e348e34235dc3668a7eb19a14ac28b1 GIT binary patch literal 14529 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 literal 0 HcmV?d00001 diff --git a/docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.png b/docs/source/user/aerodyn-fvw/Schematics/LagrangianMarkers.png new file mode 100644 index 0000000000000000000000000000000000000000..0b32bc74da9438cd41aeca70a7487d08af12c839 GIT binary patch literal 42195 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