Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small improvements for -VTKLin visualization outputs #1199

Merged
merged 6 commits into from
Sep 8, 2022
166 changes: 116 additions & 50 deletions modules/openfast-library/src/FAST_Subs.f90
Original file line number Diff line number Diff line change
Expand Up @@ -3308,6 +3308,7 @@ SUBROUTINE SetVTKParameters(p_FAST, InitOutData_ED, InitOutData_AD, InitInData_H
INTEGER(IntKi) :: ErrStat2
CHARACTER(ErrMsgLen) :: ErrMsg2
CHARACTER(*), PARAMETER :: RoutineName = 'SetVTKParameters'
INTEGER(IntKi) :: rootNode, cylNode, tipNode


ErrStat = ErrID_None
Expand Down Expand Up @@ -3417,51 +3418,44 @@ SUBROUTINE SetVTKParameters(p_FAST, InitOutData_ED, InitOutData_AD, InitInData_H
call move_alloc( InitOutData_AD%rotors(1)%BladeShape(k)%AirfoilCoords, p_FAST%VTK_Surface%BladeShape(k)%AirfoilCoords )
end do
ELSE
#ifndef USE_DEFAULT_BLADE_SURFACE
call setErrStat(ErrID_Fatal,'Cannot do surface visualization without airfoil coordinates defined in AeroDyn.',ErrStat,ErrMsg,RoutineName)
return
END IF
ELSE
call setErrStat(ErrID_Fatal,'Cannot do surface visualization without using AeroDyn.',ErrStat,ErrMsg,RoutineName)
return
END IF
#else
! AD used without airfoil coordinates specified
call WrScr('Using generic blade surfaces for AeroDyn (S809 airfoil, assumed chord, twist, AC). ')

rootNode = 1

DO K=1,NumBl
tipNode = AD%Input(1)%rotors(1)%BladeMotion(K)%NNodes
cylNode = min(3,AD%Input(1)%rotors(1)%BladeMotion(K)%Nnodes)

call SetVTKDefaultBladeParams(AD%Input(1)%rotors(1)%BladeMotion(K), p_FAST%VTK_Surface%BladeShape(K), tipNode, rootNode, cylNode, ErrStat2, ErrMsg2)
call SetVTKDefaultBladeParams(AD%Input(1)%rotors(1)%BladeMotion(K), p_FAST%VTK_Surface%BladeShape(K), tipNode, rootNode, cylNode, 1, ErrStat2, ErrMsg2)
CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName)
IF (ErrStat >= AbortErrLev) RETURN
END DO
END IF

ELSE IF ( p_FAST%CompElast == Module_BD ) THEN
call WrScr('Using generic blade surfaces for BeamDyn (rectangular airfoil, constant chord). ') ! TODO make this an option
rootNode = 1
DO K=1,NumBl
tipNode = BD%y(k)%BldMotion%NNodes
cylNode = min(3,BD%y(k)%BldMotion%NNodes)

call SetVTKDefaultBladeParams(BD%y(k)%BldMotion, p_FAST%VTK_Surface%BladeShape(K), tipNode, rootNode, cylNode, ErrStat2, ErrMsg2)
call SetVTKDefaultBladeParams(BD%y(k)%BldMotion, p_FAST%VTK_Surface%BladeShape(K), tipNode, rootNode, cylNode, 4, ErrStat2, ErrMsg2)
CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName)
IF (ErrStat >= AbortErrLev) RETURN
END DO
ELSE
call WrScr('Using generic blade surfaces for ElastoDyn (rectangular airfoil, constant chord). ') ! TODO make this an option
DO K=1,NumBl
rootNode = ED%y%BladeLn2Mesh(K)%NNodes
tipNode = ED%y%BladeLn2Mesh(K)%NNodes-1
cylNode = min(2,ED%y%BladeLn2Mesh(K)%NNodes)

call SetVTKDefaultBladeParams(ED%y%BladeLn2Mesh(K), p_FAST%VTK_Surface%BladeShape(K), tipNode, rootNode, cylNode, ErrStat2, ErrMsg2)
call SetVTKDefaultBladeParams(ED%y%BladeLn2Mesh(K), p_FAST%VTK_Surface%BladeShape(K), tipNode, rootNode, cylNode, 4, ErrStat2, ErrMsg2)
CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName)
IF (ErrStat >= AbortErrLev) RETURN
END DO
END IF
#endif


!.......................
Expand Down Expand Up @@ -3498,13 +3492,14 @@ SUBROUTINE SetVTKParameters(p_FAST, InitOutData_ED, InitOutData_AD, InitInData_H
END SUBROUTINE SetVTKParameters
!----------------------------------------------------------------------------------------------------------------------------------
!> This subroutine comes up with some default airfoils for blade surfaces for a given blade mesh, M.
SUBROUTINE SetVTKDefaultBladeParams(M, BladeShape, tipNode, rootNode, cylNode, ErrStat, ErrMsg)
SUBROUTINE SetVTKDefaultBladeParams(M, BladeShape, tipNode, rootNode, cylNode, iShape, ErrStat, ErrMsg)

TYPE(MeshType), INTENT(IN ) :: M !< The Mesh the defaults should be calculated for
TYPE(FAST_VTK_BLSurfaceType), INTENT(INOUT) :: BladeShape !< BladeShape to set to default values
INTEGER(IntKi), INTENT(IN ) :: rootNode !< Index of root node (innermost node) for this mesh
INTEGER(IntKi), INTENT(IN ) :: tipNode !< Index of tip node (outermost node) for this mesh
INTEGER(IntKi), INTENT(IN ) :: cylNode !< Index of last node to have a cylinder shape
INTEGER(IntKi), INTENT(IN ) :: iShape !< 1: S809, 2: circle, 3: square, 4: rectangle
INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation
CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None

Expand All @@ -3516,15 +3511,53 @@ SUBROUTINE SetVTKDefaultBladeParams(M, BladeShape, tipNode, rootNode, cylNode, E
INTEGER(IntKi) :: ErrStat2
CHARACTER(ErrMsgLen) :: ErrMsg2
CHARACTER(*), PARAMETER :: RoutineName = 'SetVTKDefaultBladeParams'
integer :: N ! Number of points for airfoil
real, allocatable, dimension(:) :: xc, yc ! Coordinate of airfoil

!Note: jmj does not like this default option
ErrStat = ErrID_None
ErrMsg = ''

integer, parameter :: N = 66
select case (iShape)
case (1)
N=66
call AllocAry(xc, N, 'xc', Errstat2, ErrMsg2)
call AllocAry(yc, N, 'yc', Errstat2, ErrMsg2)
call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName); if (ErrStat >= AbortErrLev) return
xc=(/ 1.0,0.996203,0.98519,0.967844,0.945073,0.917488,0.885293,0.848455,0.80747,0.763042,0.715952,0.667064,0.617331,0.56783,0.519832,0.474243,0.428461,0.382612,0.33726,0.29297,0.250247,0.209576,0.171409,0.136174,0.104263,0.076035,0.051823,0.03191,0.01659,0.006026,0.000658,0.000204,0.0,0.000213,0.001045,0.001208,0.002398,0.009313,0.02323,0.04232,0.065877,0.093426,0.124111,0.157653,0.193738,0.231914,0.271438,0.311968,0.35337,0.395329,0.438273,0.48192,0.527928,0.576211,0.626092,0.676744,0.727211,0.776432,0.823285,0.86663,0.905365,0.938474,0.965086,0.984478,0.996141,1.0 /)
yc=(/ 0.0,0.000487,0.002373,0.00596,0.011024,0.017033,0.023458,0.03028,0.037766,0.045974,0.054872,0.064353,0.074214,0.084095,0.093268,0.099392,0.10176,0.10184,0.10007,0.096703,0.091908,0.085851,0.078687,0.07058,0.061697,0.052224,0.042352,0.032299,0.02229,0.012615,0.003723,0.001942,-0.00002,-0.001794,-0.003477,-0.003724,-0.005266,-0.011499,-0.020399,-0.030269,-0.040821,-0.051923,-0.063082,-0.07373,-0.083567,-0.092442,-0.099905,-0.105281,-0.108181,-0.108011,-0.104552,-0.097347,-0.086571,-0.073979,-0.060644,-0.047441,-0.0351,-0.024204,-0.015163,-0.008204,-0.003363,-0.000487,0.000743,0.000775,0.00029,0.0 /)
case (2)
! Circle
N=21
call AllocAry(xc, N, 'xc', Errstat2, ErrMsg2)
call AllocAry(yc, N, 'yc', Errstat2, ErrMsg2)
call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName); if (ErrStat >= AbortErrLev) return
do i=1,N
angle = (i-1)*TwoPi/(N-1)
xc(i) = (cos(angle)+1)/2 ! between 0 and 1, 0.5 substracted later
yc(i) = (sin(angle)+1)/2-0.5 ! between -0.5 and 0.5
enddo
case (3)
! Square
N=5
call AllocAry(xc, N, 'xc', Errstat2, ErrMsg2)
call AllocAry(yc, N, 'yc', Errstat2, ErrMsg2)
call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName); if (ErrStat >= AbortErrLev) return
xc = (/1.0 , 0.0 , 0.0 , 1.0 , 1.0/) ! between 0 and 1, 0.5 substracted later
yc = (/-0.5 , -0.5 , 0.5 , 0.5 , -0.5/) ! between -0.5 and 0.5
case (4)
! Rectangle
N=5
call AllocAry(xc, N, 'xc', Errstat2, ErrMsg2)
call AllocAry(yc, N, 'yc', Errstat2, ErrMsg2)
call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName); if (ErrStat >= AbortErrLev) return
xc = (/1.0 , 0.0 , 0.0 , 1.0 , 1.0/) ! between 0 and 1, 0.5 substracted later
yc = (/-0.25 , -0.25 , 0.25 , 0.25 , 0.0/) ! between 0.25 and 0.25
case default
call SetErrStat(ErrID_Fatal, 'Unknown iShape specfied for VTK default shapes',ErrStat,ErrMsg,RoutineName)
return
end select

! default airfoil shape coordinates; uses S809 values from http://wind.nrel.gov/airfoils/Shapes/S809_Shape.html:
real, parameter, dimension(N) :: xc=(/ 1.0,0.996203,0.98519,0.967844,0.945073,0.917488,0.885293,0.848455,0.80747,0.763042,0.715952,0.667064,0.617331,0.56783,0.519832,0.474243,0.428461,0.382612,0.33726,0.29297,0.250247,0.209576,0.171409,0.136174,0.104263,0.076035,0.051823,0.03191,0.01659,0.006026,0.000658,0.000204,0.0,0.000213,0.001045,0.001208,0.002398,0.009313,0.02323,0.04232,0.065877,0.093426,0.124111,0.157653,0.193738,0.231914,0.271438,0.311968,0.35337,0.395329,0.438273,0.48192,0.527928,0.576211,0.626092,0.676744,0.727211,0.776432,0.823285,0.86663,0.905365,0.938474,0.965086,0.984478,0.996141,1.0 /)
real, parameter, dimension(N) :: yc=(/ 0.0,0.000487,0.002373,0.00596,0.011024,0.017033,0.023458,0.03028,0.037766,0.045974,0.054872,0.064353,0.074214,0.084095,0.093268,0.099392,0.10176,0.10184,0.10007,0.096703,0.091908,0.085851,0.078687,0.07058,0.061697,0.052224,0.042352,0.032299,0.02229,0.012615,0.003723,0.001942,-0.00002,-0.001794,-0.003477,-0.003724,-0.005266,-0.011499,-0.020399,-0.030269,-0.040821,-0.051923,-0.063082,-0.07373,-0.083567,-0.092442,-0.099905,-0.105281,-0.108181,-0.108011,-0.104552,-0.097347,-0.086571,-0.073979,-0.060644,-0.047441,-0.0351,-0.024204,-0.015163,-0.008204,-0.003363,-0.000487,0.000743,0.000775,0.00029,0.0 /)

call AllocAry(BladeShape%AirfoilCoords, 2, N, M%NNodes, 'BladeShape%AirfoilCoords', ErrStat2, ErrMsg2)
CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName)
IF (ErrStat >= AbortErrLev) RETURN
Expand All @@ -3535,6 +3568,25 @@ SUBROUTINE SetVTKDefaultBladeParams(M, BladeShape, tipNode, rootNode, cylNode, E
bladeLengthFract = 0.22*bladeLength
bladeLengthFract2 = bladeLength-bladeLengthFract != 0.78*bladeLength


! Circle, square or rectangle, constant chord
if (iShape>1) then
chord = bladeLength*0.04 ! chord set to 4% of blade length
DO i=1,M%Nnodes
posLength = TwoNorm( M%Position(:,i) - M%Position(:,rootNode) )
DO j=1,N
! normalized x,y coordinates for airfoil
x = yc(j)
y = xc(j) - 0.5
! x,y coordinates for cylinder
BladeShape%AirfoilCoords(1,j,i) = chord*x
BladeShape%AirfoilCoords(2,j,i) = chord*y
END DO
enddo
return ! We exit this routine
endif

! Assumed chord/twist/AC distribution for a blade
DO i=1,M%Nnodes
posLength = TwoNorm( M%Position(:,i) - M%Position(:,rootNode) )

Expand Down Expand Up @@ -7263,6 +7315,9 @@ SUBROUTINE FAST_RestoreForVTKModeShape_T(t_initial, p_FAST, y_FAST, m_FAST, ED,
CHARACTER(ErrMsgLen) :: ErrMsg2
CHARACTER(*), PARAMETER :: RoutineName = 'FAST_RestoreForVTKModeShape_T'
CHARACTER(1024) :: VTK_RootName
CHARACTER(1024) :: VTK_RootDir
CHARACTER(1024) :: vtkroot
CHARACTER(1024) :: sInfo !< String used for formatted screen output


ErrStat = ErrID_None
Expand All @@ -7283,15 +7338,25 @@ SUBROUTINE FAST_RestoreForVTKModeShape_T(t_initial, p_FAST, y_FAST, m_FAST, ED,

VTK_RootName = p_FAST%VTK_OutFileRoot

select case (p_FAST%VTK_modes%VTKLinTim)
case (1)

do iMode = 1,p_FAST%VTK_modes%VTKLinModes
ModeNo = p_FAST%VTK_modes%VTKModes(iMode)

call GetTimeConstants(p_FAST%VTK_modes%DampedFreq_Hz(ModeNo), p_FAST%VTK_fps, nt, dt, p_FAST%VTK_tWidth )
if (nt > 500) cycle
! Creating VTK folder in case user deleted it.
! We have to extract the vtk root dir again because p_FAST%VTK_OutFileRoot contains the full basename
call GetPath ( p_FAST%OutFileRoot, VTK_RootDir, vtkroot )
VTK_RootDir = trim(VTK_RootDir) // 'vtk'
call MKDIR( trim(VTK_RootDir) )


do iMode = 1,p_FAST%VTK_modes%VTKLinModes
ModeNo = p_FAST%VTK_modes%VTKModes(iMode)
call GetTimeConstants(p_FAST%VTK_modes%DampedFreq_Hz(ModeNo), p_FAST%VTK_fps, p_FAST%VTK_modes%VTKLinTim, nt, dt, p_FAST%VTK_tWidth )
write(sInfo, '(A,I4,A,F12.4,A,I4,A,I0)') 'Mode',ModeNo,', Freq=', p_FAST%VTK_modes%DampedFreq_Hz(ModeNo),'Hz, NLinTimes=',NLinTimes,', nt=',nt
call WrScr(trim(sInfo))
if (nt > 500) then
call WrScr(' Skipping mode '//trim(num2lstr(ModeNo))//' due to low frequency.')
cycle
endif

select case (p_FAST%VTK_modes%VTKLinTim)
case (1)
p_FAST%VTK_OutFileRoot = trim(VTK_RootName)//'.Mode'//trim(num2lstr(ModeNo))
y_FAST%VTK_count = 1 ! we are skipping the reference meshes by starting at 1
do iLinTime = 1,NLinTimes
Expand Down Expand Up @@ -7320,15 +7385,7 @@ SUBROUTINE FAST_RestoreForVTKModeShape_T(t_initial, p_FAST, y_FAST, m_FAST, ED,
call WriteVTK(m_FAST%Lin%LinTimes(iLinTime), p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, OpFM, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD)

end do ! iLinTime
end do ! iMode

case (2)

do iMode = 1,p_FAST%VTK_modes%VTKLinModes
ModeNo = p_FAST%VTK_modes%VTKModes(iMode)

call GetTimeConstants(p_FAST%VTK_modes%DampedFreq_Hz(ModeNo), p_FAST%VTK_fps, nt, dt, p_FAST%VTK_tWidth )
if (nt > 500) cycle
case (2)

do iLinTime = 1,NLinTimes
p_FAST%VTK_OutFileRoot = trim(VTK_RootName)//'.Mode'//trim(num2lstr(ModeNo))//'.LinTime'//trim(num2lstr(iLinTime))
Expand Down Expand Up @@ -7359,19 +7416,22 @@ SUBROUTINE FAST_RestoreForVTKModeShape_T(t_initial, p_FAST, y_FAST, m_FAST, ED,

call WriteVTK(m_FAST%Lin%LinTimes(iLinTime)+tprime, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, OpFM, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD)

end do
end do ! it
end do ! iLinTime

end select ! VTKLinTim=1 or 2

end do ! iMode


end do ! iLinTime
end do ! iMode

end select

END SUBROUTINE FAST_RestoreForVTKModeShape_T
!----------------------------------------------------------------------------------------------------------------------------------
SUBROUTINE GetTimeConstants(DampedFreq_Hz, VTK_fps, nt, dt, VTK_tWidth )
SUBROUTINE GetTimeConstants(DampedFreq_Hz, VTK_fps, VTKLinTim, nt, dt, VTK_tWidth)
REAL(R8Ki), INTENT(IN ) :: DampedFreq_Hz
REAL(DbKi), INTENT(IN ) :: VTK_fps
INTEGER(IntKi), INTENT(IN ) :: VTKLinTim
INTEGER(IntKi), INTENT( OUT) :: nt !< number of steps
REAL(DbKi), INTENT( OUT) :: dt !< time step
INTEGER(IntKi), INTENT( OUT) :: VTK_tWidth
Expand All @@ -7380,21 +7440,27 @@ SUBROUTINE GetTimeConstants(DampedFreq_Hz, VTK_fps, nt, dt, VTK_tWidth )
INTEGER(IntKi) :: NCycles
INTEGER(IntKi), PARAMETER :: MinFrames = 5

if (DampedFreq_Hz <= 0.0_DbKi) then
if (DampedFreq_Hz <= 1e-4_DbKi) then
nt = huge(nt)
dt = epsilon(dt)
VTK_tWidth = 1
return
end if

nt = 1
NCycles = 0
do while (nt<MinFrames)
NCycles = NCycles + 1
cycle_time = NCycles * 1.0_DbKi / DampedFreq_Hz
if (VTKLinTim==1) then
nt = 1
NCycles = 0
do while (nt<MinFrames)
NCycles = NCycles + 1
cycle_time = NCycles * 1.0_DbKi / DampedFreq_Hz

nt = NINT( max(1.0_DbKi, VTK_fps) * cycle_time )
end do
nt = NINT( max(1.0_DbKi, VTK_fps) * cycle_time )
end do
else
! All simulation will use VTK_fps
cycle_time = 1.0_DbKi / DampedFreq_Hz
nt = NINT(VTK_fps)
endif

dt = cycle_time / nt

Expand Down Expand Up @@ -7642,7 +7708,7 @@ SUBROUTINE ReadModeShapeFile(p_FAST, InputFile, ErrStat, ErrMsg, checkpointOnly)
! overwrite these based on inputs:

if (p_FAST%VTK_modes%VTKLinTim == 2) then
p_FAST%VTK_modes%VTKLinPhase = 0 ! "Phase when making one animation for all LinTimes together (used only when VTKLinTim=1)" -
!p_FAST%VTK_modes%VTKLinPhase = 0 ! "Phase when making one animation for all LinTimes together (used only when VTKLinTim=1)" -

if (VTKLinTimes1) then
p_FAST%VTK_modes%VTKNLinTimes = 1
Expand Down