From a1b45e963ef4ec6e488b3827b0d8dc39a1fd76f9 Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Wed, 21 Apr 2021 18:00:09 -0600 Subject: [PATCH 01/24] UA + AFI updates from Envision (first draft) - allow UA to be turned off based on node's radial location. THIS CHANGES THE AERODYN INPUT FILE! - added more validity checks on AFI UA parameters - added UAMod 5, which is similar to UAMod 4, but with an extra state for vortex shedding - NWTC_Num.f90: added some routines to convert units, use TaitBryan rotation matrices (instead of Euler) ; added kernelSmoothing routine - allow user to choose new DBEMT and UA models - allow constant airfoil tables (i.e., fewer than 3 lines) - AFI input files do not need to include UA parameters. If they do not, they will be calculated internally. --- modules/aerodyn/src/AeroDyn.f90 | 98 +- modules/aerodyn/src/AeroDyn_IO.f90 | 18 +- modules/aerodyn/src/AeroDyn_Registry.txt | 2 + modules/aerodyn/src/AeroDyn_Types.f90 | 14 + modules/aerodyn/src/AirfoilInfo.f90 | 881 +++++++++++++---- modules/aerodyn/src/AirfoilInfo_Registry.txt | 74 +- modules/aerodyn/src/AirfoilInfo_Types.f90 | 562 ++++++++++- modules/aerodyn/src/BEMT.f90 | 10 +- modules/aerodyn/src/BEMT_Registry.txt | 4 + modules/aerodyn/src/BEMT_Types.f90 | 126 +++ modules/aerodyn/src/DBEMT.f90 | 8 - modules/aerodyn/src/FVW.f90 | 8 +- modules/aerodyn/src/FVW_Registry.txt | 3 + modules/aerodyn/src/FVW_Types.f90 | 115 +++ modules/aerodyn/src/UA_Dvr_Subs.f90 | 22 +- modules/aerodyn/src/UnsteadyAero.f90 | 921 ++++++++++++++---- modules/aerodyn/src/UnsteadyAero_Driver.f90 | 14 +- modules/aerodyn/src/UnsteadyAero_Registry.txt | 21 +- modules/aerodyn/src/UnsteadyAero_Types.f90 | 586 +++++++++-- modules/nwtc-library/src/NWTC_Num.f90 | 743 +++++++++++++- 20 files changed, 3682 insertions(+), 548 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index f5ca0b143..fd5251a29 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1,7 +1,7 @@ !********************************************************************************************************************************** ! LICENSING ! Copyright (C) 2015-2016 National Renewable Energy Laboratory -! Copyright (C) 2016-2019 Envision Energy USA, LTD +! Copyright (C) 2016-2021 Envision Energy USA, LTD ! ! This file is part of AeroDyn. ! @@ -479,7 +479,7 @@ end subroutine AD_Init !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine reinitializes BEMT and UA, assuming that we will start the simulation over again, with only the inputs being different. !! This allows us to bypass reading input files and allocating arrays because p is already set. -subroutine AD_ReInit(p,x, xd, z, OtherState, m, Interval, ErrStat, ErrMsg ) +subroutine AD_ReInit(p, x, xd, z, OtherState, m, Interval, ErrStat, ErrMsg ) type(AD_ParameterType), intent(in ) :: p !< Parameters type(AD_ContinuousStateType), intent(inout) :: x !< Initial continuous states @@ -517,7 +517,7 @@ subroutine AD_ReInit(p,x, xd, z, OtherState, m, Interval, ErrStat, ErrMsg ) call BEMT_ReInit(p%rotors(iR)%BEMT,x%rotors(iR)%BEMT,xd%rotors(iR)%BEMT,z%rotors(iR)%BEMT,OtherState%rotors(iR)%BEMT,m%rotors(iR)%BEMT,ErrStat,ErrMsg) if (p%rotors(iR)%BEMT%UA_Flag) then - call UA_ReInit( p%rotors(iR)%BEMT%UA, p%AFI, p%rotors(iR)%BEMT%AFIndx, x%rotors(iR)%BEMT%UA, xd%rotors(iR)%BEMT%UA, OtherState%rotors(iR)%BEMT%UA, m%rotors(iR)%BEMT%UA, ErrStat2, ErrMsg2 ) + call UA_ReInit( p%rotors(iR)%BEMT%UA, x%rotors(iR)%BEMT%UA, xd%rotors(iR)%BEMT%UA, OtherState%rotors(iR)%BEMT%UA, m%rotors(iR)%BEMT%UA, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) end if enddo @@ -2136,7 +2136,7 @@ subroutine SetOutputsFromFVW(t, u, p, OtherState, x, xd, m, y, ErrStat, ErrMsg) u_UA%v_ac(2) = cos(u_UA%alpha)*u_UA%U u_UA%omega = dot_product( u%rotors(iR)%BladeMotion(k)%RotationVel( :,j), m%rotors(iR)%WithoutSweepPitchTwist(3,:,j,k) ) ! rotation of no-sweep-pitch coordinate system around z of the jth node in the kth blade - call UA_CalcOutput(j, k, u_UA, m%FVW%p_UA, x%FVW%UA, xd%FVW%UA, OtherState%FVW%UA, p%AFI(p%FVW%AFindx(j,k)), m%FVW%y_UA, m%FVW%m_UA, errStat2, errMsg2 ) + call UA_CalcOutput(j, k, t, u_UA, m%FVW%p_UA, x%FVW%UA, xd%FVW%UA, OtherState%FVW%UA, p%AFI(p%FVW%AFindx(j,k)), m%FVW%y_UA, m%FVW%m_UA, errStat2, errMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') Cl_dyn = m%FVW%y_UA%Cl Cd_dyn = m%FVW%y_UA%Cd @@ -2413,17 +2413,21 @@ SUBROUTINE ValidateInputData( InitInp, InputFileData, NumBl, ErrStat, ErrMsg ) !.................. if (InitInp%Linearize) then if (InputFileData%AFAeroMod /= AFAeroMod_Steady) then - if (InputFileData%UAMod /= UA_HGM) then - call SetErrStat( ErrID_Fatal, 'When AFAeroMod=2, UAMod must be 4 for linearization. Set AFAeroMod=1 or UAMod=4.', ErrStat, ErrMsg, RoutineName ) - end if +!bjj: REMOVE when linearization has been tested + call SetErrStat( ErrID_Fatal, 'Steady blade airfoil aerodynamics must be used for linearization. Set AFAeroMod=1.', ErrStat, ErrMsg, RoutineName ) + !if (InputFileData%UAMod /= UA_HGM) then + ! call SetErrStat( ErrID_Fatal, 'When AFAeroMod=2, UAMod must be 4 for linearization. Set AFAeroMod=1 or UAMod=4.', ErrStat, ErrMsg, RoutineName ) + !end if end if if (InputFileData%WakeMod == WakeMod_FVW) then call SetErrStat( ErrID_Fatal, 'FVW cannot currently be used for linearization. Set WakeMod=0 or WakeMod=1.', ErrStat, ErrMsg, RoutineName ) else if (InputFileData%WakeMod == WakeMod_DBEMT) then - if (InputFileData%DBEMT_Mod /= DBEMT_cont_tauConst) then - call SetErrStat( ErrID_Fatal, 'DBEMT requires the continuous formulation with constant tau1 for linearization. Set DBEMT_Mod=3 or set WakeMod to 0 or 1.', ErrStat, ErrMsg, RoutineName ) - end if +!bjj: when linearization has been tested + call SetErrStat( ErrID_Fatal, 'DBEMT cannot currently be used for linearization. Set WakeMod=0 or WakeMod=1.', ErrStat, ErrMsg, RoutineName ) + !if (InputFileData%DBEMT_Mod /= DBEMT_cont_tauConst) then + ! call SetErrStat( ErrID_Fatal, 'DBEMT requires the continuous formulation with constant tau1 for linearization. Set DBEMT_Mod=3 or set WakeMod to 0 or 1.', ErrStat, ErrMsg, RoutineName ) + !end if end if end if @@ -2469,6 +2473,7 @@ SUBROUTINE Init_AFIparams( InputFileData, p_AFI, UnEc, ErrStat, ErrMsg ) IF (.not. InputFileData%UseBlCm) AFI_InitInputs%InCol_Cm = 0 ! Don't try to use Cm if flag set to false AFI_InitInputs%InCol_Cpmin = InputFileData%InCol_Cpmin AFI_InitInputs%AFTabMod = InputFileData%AFTabMod !AFITable_1 + AFI_InitInputs%UA_f_cn = InputFileData%UAMod /= UA_HGM ! HGM uses the separation function based on cl instead of cn ! Call AFI_Init to read in and process the airfoil files. ! This includes creating the spline coefficients to be used for interpolation. @@ -2623,6 +2628,8 @@ SUBROUTINE Init_BEMTmodule( InputFileData, RotInputFileData, u_AD, u, p, p_AD, x real(ReKi) :: tmp(3), tmp_sz_y, tmp_sz real(ReKi) :: y_hat_disk(3) real(ReKi) :: z_hat_disk(3) + real(ReKi) :: rMax + real(ReKi) :: frac integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'Init_BEMTmodule' @@ -2638,7 +2645,7 @@ SUBROUTINE Init_BEMTmodule( InputFileData, RotInputFileData, u_AD, u, p, p_AD, x InitInp%numBlades = p%NumBlades InitInp%airDens = InputFileData%AirDens - InitInp%kinVisc = InputFileData%KinVisc + InitInp%kinVisc = InputFileData%KinVisc InitInp%skewWakeMod = InputFileData%SkewMod InitInp%yawCorrFactor = InputFileData%SkewModFactor InitInp%aTol = InputFileData%IndToler @@ -2653,13 +2660,15 @@ SUBROUTINE Init_BEMTmodule( InputFileData, RotInputFileData, u_AD, u, p, p_AD, x InitInp%maxIndIterations = InputFileData%MaxIter - 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) + 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) + call AllocAry(InitInp%UAOff_innerNode, InitInp%numBlades,'UAOff_innerNode',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry(InitInp%UAOff_outerNode, InitInp%numBlades,'UAOff_outerNode',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) if ( ErrStat >= AbortErrLev ) then call Cleanup() @@ -2667,6 +2676,7 @@ SUBROUTINE Init_BEMTmodule( InputFileData, RotInputFileData, u_AD, u, p, p_AD, x end if + rMax = 0.0_ReKi do k=1,p%numBlades InitInp%zHub(k) = TwoNorm( u_AD%BladeRootMotion(k)%Position(:,1) - u_AD%HubMotion%Position(:,1) ) @@ -2691,9 +2701,25 @@ SUBROUTINE Init_BEMTmodule( InputFileData, RotInputFileData, u_AD, u, p, p_AD, x tmp_sz_y = dot_product( tmp, y_hat_disk )**2 tmp_sz = dot_product( tmp, z_hat_disk )**2 InitInp%rLocal(j,k) = sqrt( tmp_sz + tmp_sz_y ) + rMax = max(rMax, InitInp%rLocal(j,k)) end do !j=nodes end do !k=blades + + InitInp%UAOff_innerNode = 0 + InitInp%UAOff_outerNode = p%NumBlNds + 1 + do k = 1,p%numBlades + do j = 1,p%NumBlNds + frac = InitInp%rLocal(j,k) / rMax + if (frac < InputFileData%UAStartRad) then + InitInp%UAOff_innerNode(k) = max(InitInp%UAOff_innerNode(k), j) + elseif (frac > InputFileData%UAEndRad) then + InitInp%UAOff_outerNode(k) = min(InitInp%UAOff_outerNode(k), j) + end if + end do + end do + + do k=1,p%numBlades do j=1,p%NumBlNds @@ -2702,10 +2728,17 @@ SUBROUTINE Init_BEMTmodule( InputFileData, RotInputFileData, u_AD, u, p, p_AD, x end do end do - InitInp%UA_Flag = InputFileData%AFAeroMod == AFAeroMod_BL_unsteady - InitInp%UAMod = InputFileData%UAMod - InitInp%Flookup = InputFileData%Flookup - InitInp%a_s = InputFileData%SpdSound + InitInp%UA_Flag = InputFileData%AFAeroMod == AFAeroMod_BL_unsteady + InitInp%UAMod = InputFileData%UAMod + InitInp%Flookup = InputFileData%Flookup + InitInp%a_s = InputFileData%SpdSound + InitInp%SumPrint = InputFileData%SumPrint + InitInp%RootName = p%RootName + ! remove the ".AD" from the RootName + k = len_trim(InitInp%RootName) + if (k>3) then + InitInp%RootName = InitInp%RootName(1:k-3) + end if if (InputFileData%WakeMod == WakeMod_DBEMT) then InitInp%DBEMT_Mod = InputFileData%DBEMT_Mod @@ -2770,6 +2803,8 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, m, E integer(intKi) :: j ! node index integer(intKi) :: IB ! blade index integer(intKi) :: iR ! rotor index + real(ReKi) :: rMax + real(ReKi) :: frac real(ReKi) :: tmp(3), tmp_sz_y, tmp_sz real(ReKi) :: y_hat_disk(3) real(ReKi) :: z_hat_disk(3) @@ -2808,6 +2843,10 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, m, E 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) + call AllocAry(InitInp%UAOff_innerNode, InitInp%numBlades,'UAOff_innerNode',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry(InitInp%UAOff_outerNode, InitInp%numBlades,'UAOff_outerNode',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + if ( ErrStat >= AbortErrLev ) then call Cleanup() return @@ -2826,10 +2865,13 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, m, E endif ! Distance along blade curve -- NOTE: this is an approximation. + rmax = 0.0_ReKi do IB=1,p%rotors(iR)%numBlades InitInp%zLocal(1,IB) = InitInp%zHub(IB) + TwoNorm( u_AD%rotors(iR)%BladeMotion(IB)%Position(:,1) - u_AD%rotors(iR)%BladeRootMotion(IB)%Position(:,1) ) + rMax = max(rMax, InitInp%rLocal(1,IB)) do j=2,p%rotors(iR)%NumBlNds InitInp%zLocal(j,IB) = InitInp%zLocal(j-1,IB) + TwoNorm( u_AD%rotors(iR)%BladeMotion(IB)%Position(:,j) - u_AD%rotors(iR)%BladeMotion(IB)%Position(:,j-1) ) + rMax = max(rMax, InitInp%rLocal(j,IB)) end do !j=nodes end do !IB=blades @@ -2866,6 +2908,7 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, m, E InitInp%UAMod = InputFileData%UAMod InitInp%Flookup = InputFileData%Flookup InitInp%a_s = InputFileData%SpdSound + InitInp%SumPrint = InputFileData%SumPrint ! 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. @@ -2890,6 +2933,19 @@ SUBROUTINE Init_FVWmodule( InputFileData, u_AD, u, p, x, xd, z, OtherState, m, E RETURN endif ENDDO + + InitInp%UAOff_innerNode = 0 + InitInp%UAOff_outerNode = p%rotors(iR)%NumBlNds + 1 + do IB = 1,p%rotors(iR)%numBlades + do j = 1,p%rotors(iR)%NumBlNds + frac = InitInp%rLocal(j,IB) / rMax + if (frac < InputFileData%UAStartRad) then + InitInp%UAOff_innerNode(IB) = max(InitInp%UAOff_innerNode(IB), j) + elseif (frac > InputFileData%UAEndRad) then + InitInp%UAOff_outerNode(IB) = min(InitInp%UAOff_outerNode(IB), j) + end if + end do + end do enddo ! iR, rotors TODO TODO diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index c4f8258e7..d9069f395 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -2191,6 +2191,14 @@ SUBROUTINE ParsePrimaryFileInfo( PriPath, InputFile, RootName, NumBlades, interv ! FLookup - Flag to indicate whether a lookup for f' will be calculated (TRUE) or whether best-fit exponential equations will be used (FALSE); if FALSE S1-S4 must be provided in airfoil input files (flag) [used only when AFAeroMod=2] call ParseVar( FileInfo_In, CurLine, "FLookup", InputFileData%FLookup, ErrStat2, ErrMsg2, UnEc ) if (Failed()) return + + ! UAStartRad - Starting radius for dynamic stall (fraction of rotor radius) [used only when AFAeroMod=2]: + call ParseVar( FileInfo_In, CurLine, "UAStartRad", InputFileData%UAStartRad, ErrStat2, ErrMsg2, UnEc ) + if (ErrStat2>= AbortErrLev) InputFileData%UAStartRad = 0.0_ReKi + + ! UAEndRad - Ending radius for dynamic stall (fraction of rotor radius) [used only when AFAeroMod=2]: + call ParseVar( FileInfo_In, CurLine, "UAEndRad", InputFileData%UAEndRad, ErrStat2, ErrMsg2, UnEc ) + if (ErrStat2>= AbortErrLev) InputFileData%UAEndRad = 1.0_ReKi !====== Airfoil Information ========================================================================= if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo @@ -2692,14 +2700,18 @@ SUBROUTINE AD_PrintSum( InputFileData, p, p_AD, u, y, ErrStat, ErrMsg ) ! UAMod select case (InputFileData%UAMod) - case (1) + case (UA_Baseline) Msg = 'baseline model (original)' - case (2) + case (UA_Gonzalez) Msg = "Gonzalez's variant (changes in Cn, Cc, and Cm)" - case (3) + case (UA_MinnemaPierce) Msg = 'Minnema/Pierce variant (changes in Cc and Cm)' !case (4) ! Msg = 'DYSTOOL' + case (UA_HGM) + Msg = 'HGM (continuous state)' + case (UA_HGMV) + Msg = 'HGMV (continuous state + vortex)' case default Msg = 'unknown' end select diff --git a/modules/aerodyn/src/AeroDyn_Registry.txt b/modules/aerodyn/src/AeroDyn_Registry.txt index 6bbfbb24b..9ba725484 100644 --- a/modules/aerodyn/src/AeroDyn_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Registry.txt @@ -153,6 +153,8 @@ typedef ^ AD_InputFile CHARACTER(ChanLen) BldNd_OutList {:} - - "List of user-re typedef ^ AD_InputFile CHARACTER(1024) BldNd_BlOutNd_Str - - - "String to parse for the blade nodes to actually output (AD_AllBldNdOuts)" - typedef ^ AD_InputFile IntKi BldNd_BladesOut - - - "The blades to output (AD_AllBldNdOuts)" - #typedef ^ AD_InputFile CHARACTER(1024) BldNd_BladesOut_Str - - - "String to parse for the he blades to output (AD_AllBldNdOuts)" - +typedef ^ AD_InputFile ReKi UAStartRad - - - Starting radius for dynamic stall (fraction of rotor radius) +typedef ^ AD_InputFile ReKi UAEndRad - - - Ending radius for dynamic stall (fraction of rotor radius) typedef ^ AD_InputFile RotInputFile rotors {:} - - "Rotor (blades and tower) input file data" - diff --git a/modules/aerodyn/src/AeroDyn_Types.f90 b/modules/aerodyn/src/AeroDyn_Types.f90 index 333222270..6969f6e57 100644 --- a/modules/aerodyn/src/AeroDyn_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Types.f90 @@ -180,6 +180,8 @@ MODULE AeroDyn_Types CHARACTER(ChanLen) , DIMENSION(:), ALLOCATABLE :: BldNd_OutList !< List of user-requested output channels (AD_AllBldNdOuts) [-] CHARACTER(1024) :: BldNd_BlOutNd_Str !< String to parse for the blade nodes to actually output (AD_AllBldNdOuts) [-] INTEGER(IntKi) :: BldNd_BladesOut !< The blades to output (AD_AllBldNdOuts) [-] + REAL(ReKi) :: UAStartRad !< Starting [radius] + REAL(ReKi) :: UAEndRad !< Ending [radius] TYPE(RotInputFile) , DIMENSION(:), ALLOCATABLE :: rotors !< Rotor (blades and tower) input file data [-] END TYPE AD_InputFile ! ======================= @@ -3789,6 +3791,8 @@ SUBROUTINE AD_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrSt ENDIF DstInputFileData%BldNd_BlOutNd_Str = SrcInputFileData%BldNd_BlOutNd_Str DstInputFileData%BldNd_BladesOut = SrcInputFileData%BldNd_BladesOut + DstInputFileData%UAStartRad = SrcInputFileData%UAStartRad + DstInputFileData%UAEndRad = SrcInputFileData%UAEndRad IF (ALLOCATED(SrcInputFileData%rotors)) THEN i1_l = LBOUND(SrcInputFileData%rotors,1) i1_u = UBOUND(SrcInputFileData%rotors,1) @@ -3939,6 +3943,8 @@ SUBROUTINE AD_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg END IF Int_BufSz = Int_BufSz + 1*LEN(InData%BldNd_BlOutNd_Str) ! BldNd_BlOutNd_Str Int_BufSz = Int_BufSz + 1 ! BldNd_BladesOut + Re_BufSz = Re_BufSz + 1 ! UAStartRad + Re_BufSz = Re_BufSz + 1 ! UAEndRad Int_BufSz = Int_BufSz + 1 ! rotors allocated yes/no IF ( ALLOCATED(InData%rotors) ) THEN Int_BufSz = Int_BufSz + 2*1 ! rotors upper/lower bounds for each dimension @@ -4164,6 +4170,10 @@ SUBROUTINE AD_PackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg END DO ! I IntKiBuf(Int_Xferred) = InData%BldNd_BladesOut Int_Xferred = Int_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%UAStartRad + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%UAEndRad + Re_Xferred = Re_Xferred + 1 IF ( .NOT. ALLOCATED(InData%rotors) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -4424,6 +4434,10 @@ SUBROUTINE AD_UnPackInputFile( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err END DO ! I OutData%BldNd_BladesOut = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 + OutData%UAStartRad = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%UAEndRad = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! rotors not allocated Int_Xferred = Int_Xferred + 1 ELSE diff --git a/modules/aerodyn/src/AirfoilInfo.f90 b/modules/aerodyn/src/AirfoilInfo.f90 index 30fa9d6ca..c8aece935 100644 --- a/modules/aerodyn/src/AirfoilInfo.f90 +++ b/modules/aerodyn/src/AirfoilInfo.f90 @@ -34,9 +34,10 @@ MODULE AirfoilInfo PUBLIC :: AFI_Init ! routine to initialize AirfoilInfo parameters PUBLIC :: AFI_ComputeUACoefs ! routine to calculate Airfoil BL parameters for UA PUBLIC :: AFI_ComputeAirfoilCoefs ! routine to perform 1D (AOA) or 2D (AOA, Re) or (AOA, UserProp) lookup of the airfoil coefs + TYPE(ProgDesc), PARAMETER :: AFI_Ver = ProgDesc( 'AirfoilInfo', '', '') ! The name, version, and date of AirfoilInfo. - integer, parameter :: MaxNumAFCoeffs = 6 !cl,cd,cm,cpMin, UA_HGM:f_st, UA_HGM:cl_fs +integer, parameter :: MaxNumAFCoeffs = 7 !cl,cd,cm,cpMin, UA:f_st, FullySeparate, FullyAttached CONTAINS @@ -71,20 +72,24 @@ SUBROUTINE AFI_Init ( InitInput, p, ErrStat, ErrMsg, UnEcho ) ! Argument declarations. INTEGER(IntKi), INTENT(OUT) :: ErrStat ! Error status. + INTEGER, INTENT(IN), OPTIONAL :: UnEcho ! I/O unit for echo file. If present and > 0, write to UnEcho. + CHARACTER(*), INTENT(OUT) :: ErrMsg ! Error message. + TYPE (AFI_InitInputType), INTENT(IN ) :: InitInput ! This structure stores values that are set by the calling routine during the initialization phase. TYPE (AFI_ParameterType), INTENT( OUT) :: p ! This structure stores all the module parameters that are set by AirfoilInfo during the initialization phase. + ! Local declarations. REAL(ReKi), ALLOCATABLE :: Coef (:) ! The coefficients to send to the regrid routine for 2D splines. - INTEGER :: Co ! The index into the coefficients array. INTEGER :: UnEc ! Local echo file unit number INTEGER :: NumCoefs ! The number of aerodynamic coefficients to be stored integer :: iTable ! Iterator for airfoil tables + INTEGER :: ErrStat2 ! Local error status. CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'AFI_Init' @@ -97,6 +102,7 @@ SUBROUTINE AFI_Init ( InitInput, p, ErrStat, ErrMsg, UnEcho ) !CALL DispNVD ( AFI_Ver ) p%FileName = InitInput%FileName ! store this for error messages later (e.g., in UA) + CALL AFI_ValidateInitInput(InitInput, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) if (ErrStat >= AbortErrLev) return @@ -137,9 +143,7 @@ SUBROUTINE AFI_Init ( InitInput, p, ErrStat, ErrMsg, UnEcho ) END IF - CALL ReadAFfile ( InitInput%FileName, NumCoefs, InitInput%InCol_Alfa & - , InitInput%InCol_Cl, InitInput%InCol_Cd, InitInput%InCol_Cm, InitInput%InCol_Cpmin, p & - , ErrStat2, ErrMsg2, UnEc ) + CALL ReadAFfile ( InitInput, NumCoefs, p, ErrStat2, ErrMsg2, UnEc ) CALL SetErrStat ( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CALL Cleanup ( ) @@ -244,19 +248,10 @@ SUBROUTINE AFI_Init ( InitInput, p, ErrStat, ErrMsg, UnEcho ) ENDIF ! ( p%NumTabs > 1 ) -! We need to deal with constant data. - - do iTable = 1, p%NumTabs ! We need to deal with constant data. - IF ( p%Table(iTable)%ConstData ) THEN + IF ( p%Table(iTable)%ConstData ) CYCLE ! skip this table; it's just constant - CALL SetErrStat ( ErrID_FATAL, 'The part to deal with constant data in AFI_Init is not written yet!', ErrStat, ErrMsg, RoutineName ) - CALL Cleanup() - RETURN - - END IF - ! Allocate the arrays to hold spline coefficients. allocate ( p%Table(iTable)%SplineCoefs( p%Table(iTable)%NumAlf-1, size(p%Table(iTable)%Coefs,2), 0:3 ), STAT=ErrStat2 ) @@ -266,6 +261,7 @@ SUBROUTINE AFI_Init ( InitInput, p, ErrStat, ErrMsg, UnEcho ) return end if + ! Compute the one set of coefficients of the piecewise polynomials for the irregularly-spaced data. ! Unlike the 2-D interpolation in which we use diffent knots for each airfoil coefficient, we can do ! the 1-D stuff all at once. @@ -318,7 +314,7 @@ SUBROUTINE Cleanup ( ) ! This subroutine cleans up the parent routine before exiting. ! Deallocate the temporary Coef array. - IF ( ALLOCATED( Coef ) ) DEALLOCATE( Coef ) + IF ( ALLOCATED( Coef ) ) DEALLOCATE( Coef ) RETURN @@ -352,8 +348,7 @@ SUBROUTINE AFI_ValidateInitInput(InitInput, ErrStat, ErrMsg) END SUBROUTINE AFI_ValidateInitInput !============================================================================= - SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCol_Cm, InCol_Cpmin, p & - , ErrStat, ErrMsg, UnEc ) + SUBROUTINE ReadAFfile ( InitInp, NumCoefsIn, p, ErrStat, ErrMsg, UnEc ) ! This routine reads an airfoil file. @@ -361,17 +356,13 @@ SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCo ! Argument declarations. - INTEGER(IntKi), INTENT(IN) :: InCol_Alfa ! The airfoil-table input column for angle of attack. - INTEGER(IntKi), INTENT(IN) :: InCol_Cd ! The airfoil-table input column for drag coefficient. - INTEGER(IntKi), INTENT(IN) :: InCol_Cl ! The airfoil-table input column for lift coefficient. - INTEGER(IntKi), INTENT(IN) :: InCol_Cm ! The airfoil-table input column for pitching-moment coefficient. - INTEGER(IntKi), INTENT(IN) :: InCol_Cpmin ! The airfoil-table input column for minimum pressure coefficient. - INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status. - INTEGER(IntKi), INTENT(IN ) :: NumCoefsIn ! The number of aerodynamic coefficients to be stored. + TYPE (AFI_InitInputType), INTENT(IN) :: InitInp ! This structure stores values that are set by the calling routine during the initialization phase. + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status + INTEGER(IntKi), INTENT(IN ) :: NumCoefsIn ! The number of aerodynamic coefficients to be stored + - INTEGER, INTENT(IN) :: UnEc ! I/O unit for echo file. If present and > 0, write to UnEc. CHARACTER(*), INTENT(IN) :: AFfile ! The file to be read. + INTEGER, INTENT(IN) :: UnEc ! I/O unit for echo file. If present and > 0, write to UnEc. - CHARACTER(*), INTENT(IN) :: AFfile ! The file to read in. CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message. TYPE (AFI_ParameterType), INTENT(INOUT) :: p ! This structure stores all the module parameters that are set by AirfoilInfo during the initialization phase. @@ -392,26 +383,29 @@ SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCo LOGICAL :: BadVals ! A flag that indicates if the values in a table are invalid. TYPE (FileInfoType) :: FileInfo ! The derived type for holding the file information. - INTEGER(IntKi) :: NumCoefsTab ! The number of aerodynamic coefficients to be stored for this table. + INTEGER(IntKi) :: NumCoefsTab ! The number of aerodynamic coefficients to be stored. INTEGER(IntKi) :: DefaultInterpOrd ! value of default interp order INTEGER(IntKi) :: ErrStat2 ! Error status local to this routine. CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'ReadAFfile' CHARACTER(10) :: defaultStr - CHARACTER(1024) :: PriPath - + CHARACTER(1024) :: PriPath + + TYPE (AFI_UA_BL_Default_Type), ALLOCATABLE :: CalcDefaults(:) ! Whether to calculate default values for the UA parameters + ErrStat = ErrID_None ErrMsg = "" defaultStr = "" ! Getting parent folder of airfoils data (e.g. "Arifoils/") - CALL GetPath( AFFile, PriPath ) + CALL GetPath( InitInp%FileName, PriPath ) + ! Process the (possibly) nested set of files. This copies the decommented contents of ! AFI_FileInfo%FileName and the files it includes (both directly and indirectly) into ! the FileInfo structure that we can then parse. - CALL ProcessComFile ( AFfile, FileInfo, ErrStat2, ErrMsg2 ) + CALL ProcessComFile ( InitInp%FileName, FileInfo, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN CALL Cleanup() @@ -481,7 +475,10 @@ SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCo ! How many columns do we need to read in the input and how many total coefficients will be used? - Cols2Parse = MAX( InCol_Alfa, InCol_Cl, InCol_Cd, InCol_Cm, InCol_Cpmin ) + + ! How many columns do we need to read in the input and how many total coefficients will be used? + + Cols2Parse = MAX( InitInp%InCol_Alfa, InitInp%InCol_Cl, InitInp%InCol_Cd, InitInp%InCol_Cm, InitInp%InCol_Cpmin ) ALLOCATE ( SiAry( Cols2Parse ) , STAT=ErrStat2 ) IF ( ErrStat2 /= 0 ) THEN CALL SetErrStat ( ErrID_Fatal, 'Error allocating memory for SiAry.', ErrStat, ErrMsg, RoutineName ) @@ -505,7 +502,7 @@ SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCo RETURN ENDIF - ALLOCATE ( p%Table( p%NumTabs ) , STAT=ErrStat2 ) + ALLOCATE ( p%Table( p%NumTabs ) , CalcDefaults(p%NumTabs), STAT=ErrStat2 ) IF ( ErrStat2 /= 0 ) THEN CALL SetErrStat ( ErrID_Fatal, 'Error allocating memory for p%Table.', ErrStat, ErrMsg, RoutineName ) CALL Cleanup() @@ -513,8 +510,8 @@ SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCo ENDIF DO iTable=1,p%NumTabs - NumCoefsTab = NumCoefsIn ! Reset this counter for each table - + NumCoefsTab = NumCoefsIn + CALL ParseVar ( FileInfo, CurLine, 'Re', p%Table(iTable)%Re, ErrStat2, ErrMsg2, UnEc ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -523,7 +520,7 @@ SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCo END IF p%Table(iTable)%Re = p%Table(iTable)%Re * 1.0e6 ! Entered in millions, so multiply here IF ( p%Table(iTable)%Re <= 0.0 ) THEN - CALL SetErrStat ( ErrID_Severe, 'Re must be > 0 in "'//TRIM( AFfile ) & + CALL SetErrStat ( ErrID_Severe, 'Re must be > 0 in "'//TRIM( InitInp%FileName ) & //'".'//NewLine//' >> The error occurred on line #' & //TRIM( Num2LStr( FileInfo%FileLine(CurLine-1) ) )//'.', ErrStat, ErrMsg, RoutineName ) CALL Cleanup() @@ -541,140 +538,143 @@ SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCo END IF CALL ParseVar ( FileInfo, CurLine, 'InclUAdata', p%Table(iTable)%InclUAdata, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - IF (ErrStat >= AbortErrLev) THEN - CALL Cleanup() - RETURN - END IF + if (ErrStat2 >= AbortErrLev) p%Table(iTable)%InclUAdata = .false. ! assume we don't have any UA data included, so we'll calculate it later. IF ( p%Table(iTable)%InclUAdata ) THEN CALL ParseVar ( FileInfo, CurLine, 'alpha0', p%Table(iTable)%UA_BL%alpha0, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - p%Table(iTable)%UA_BL%alpha0 = p%Table(iTable)%UA_BL%alpha0*D2R + CalcDefaults(iTable)%alpha0 = ErrStat2 >= AbortErrLev + if (.not. CalcDefaults(iTable)%alpha0) p%Table(iTable)%UA_BL%alpha0 = p%Table(iTable)%UA_BL%alpha0*D2R CALL ParseVar ( FileInfo, CurLine, 'alpha1', p%Table(iTable)%UA_BL%alpha1, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - p%Table(iTable)%UA_BL%alpha1 = p%Table(iTable)%UA_BL%alpha1*D2R + CalcDefaults(iTable)%alpha1 = ErrStat2 >= AbortErrLev + if (.not. CalcDefaults(iTable)%alpha1) p%Table(iTable)%UA_BL%alpha1 = p%Table(iTable)%UA_BL%alpha1*D2R CALL ParseVar ( FileInfo, CurLine, 'alpha2', p%Table(iTable)%UA_BL%alpha2, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - p%Table(iTable)%UA_BL%alpha2 = p%Table(iTable)%UA_BL%alpha2*D2R + CalcDefaults(iTable)%alpha2 = ErrStat2 >= AbortErrLev + if (.not. CalcDefaults(iTable)%alpha2) p%Table(iTable)%UA_BL%alpha2 = p%Table(iTable)%UA_BL%alpha2*D2R + + CALL ParseVar ( FileInfo, CurLine, 'alphaUpper', p%Table(iTable)%UA_BL%alphaUpper, ErrStat2, ErrMsg2, UnEc ) + CalcDefaults(iTable)%alphaUpper = ErrStat2 >= AbortErrLev + if (.not. CalcDefaults(iTable)%alphaUpper) p%Table(iTable)%UA_BL%alphaUpper = p%Table(iTable)%UA_BL%alphaUpper*D2R + + CALL ParseVar ( FileInfo, CurLine, 'alphaLower', p%Table(iTable)%UA_BL%alphaLower, ErrStat2, ErrMsg2, UnEc ) + CalcDefaults(iTable)%alphaLower = ErrStat2 >= AbortErrLev + if (.not. CalcDefaults(iTable)%alphaLower) p%Table(iTable)%UA_BL%alphaLower = p%Table(iTable)%UA_BL%alphaLower*D2R CALL ParseVar ( FileInfo, CurLine, 'eta_e', p%Table(iTable)%UA_BL%eta_e, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%eta_e = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'C_nalpha', p%Table(iTable)%UA_BL%C_nalpha, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - -!>>> add after this feature gets tested better: -! CALL ParseVar ( FileInfo, CurLine, 'C_lalpha', p%Table(iTable)%UA_BL%C_lalpha, ErrStat2, ErrMsg2, UnEc ) -! CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) -!<<< -!>>> remove after this feature gets tested better: -p%Table(iTable)%UA_BL%C_lalpha = p%Table(iTable)%UA_BL%C_nalpha -!<<< + CalcDefaults(iTable)%C_nalpha = ErrStat2 >= AbortErrLev + CALL ParseVar ( FileInfo, CurLine, 'C_lalpha', p%Table(iTable)%UA_BL%C_lalpha, ErrStat2, ErrMsg2, UnEc ) + CalcDefaults(iTable)%C_lalpha = ErrStat2 >= AbortErrLev + CALL ParseVarWDefault ( FileInfo, CurLine, 'T_f0', p%Table(iTable)%UA_BL%T_f0, 3.0_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%T_f0 = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'T_V0', p%Table(iTable)%UA_BL%T_V0, 6.0_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%T_V0 = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'T_p', p%Table(iTable)%UA_BL%T_p, 1.7_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%T_p = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'T_VL', p%Table(iTable)%UA_BL%T_VL, 11.0_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%T_VL = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'b1', p%Table(iTable)%UA_BL%b1, .14_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%b1 = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'b2', p%Table(iTable)%UA_BL%b2, .53_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%b2 = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'b5', p%Table(iTable)%UA_BL%b5, 5.0_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%b5 = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'A1', p%Table(iTable)%UA_BL%A1, .3_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%A1 = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'A2', p%Table(iTable)%UA_BL%A2, .7_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%A2 = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'A5', p%Table(iTable)%UA_BL%A5, 1.0_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%A5 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'S1', p%Table(iTable)%UA_BL%S1, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%S1 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'S2', p%Table(iTable)%UA_BL%S2, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%S2 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'S3', p%Table(iTable)%UA_BL%S3, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%S3 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'S4', p%Table(iTable)%UA_BL%S4, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%S4 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'Cn1', p%Table(iTable)%UA_BL%Cn1, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%Cn1 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'Cn2', p%Table(iTable)%UA_BL%Cn2, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%Cn2 = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'St_sh', p%Table(iTable)%UA_BL%St_sh, .19_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%St_sh = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'Cd0', p%Table(iTable)%UA_BL%Cd0, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%Cd0 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'Cm0', p%Table(iTable)%UA_BL%Cm0, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%Cm0 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'k0', p%Table(iTable)%UA_BL%k0, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%k0 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'k1', p%Table(iTable)%UA_BL%k1, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%k1 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'k2', p%Table(iTable)%UA_BL%k2, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%k2 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'k3', p%Table(iTable)%UA_BL%k3, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%k3 = ErrStat2 >= AbortErrLev CALL ParseVar ( FileInfo, CurLine, 'k1_hat', p%Table(iTable)%UA_BL%k1_hat, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%k1_hat = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'x_cp_bar', p%Table(iTable)%UA_BL%x_cp_bar, .2_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%x_cp_bar = ErrStat2 >= AbortErrLev CALL ParseVarWDefault ( FileInfo, CurLine, 'UACutout', p%Table(iTable)%UA_BL%UACutout, 45.0_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - p%Table(iTable)%UA_BL%UACutout = p%Table(iTable)%UA_BL%UACutout*D2R + CalcDefaults(iTable)%UACutout = ErrStat2 >= AbortErrLev + if (.not. CalcDefaults(iTable)%UACutout ) p%Table(iTable)%UA_BL%UACutout = p%Table(iTable)%UA_BL%UACutout*D2R + + CALL ParseVarWDefault ( FileInfo, CurLine, 'UACutout_delta', p%Table(iTable)%UA_BL%UACutout_delta, 5.0_ReKi, ErrStat2, ErrMsg2, UnEc ) + CalcDefaults(iTable)%UACutout_delta = ErrStat2 >= AbortErrLev + if (.not. CalcDefaults(iTable)%UACutout_delta) p%Table(iTable)%UA_BL%UACutout_delta = p%Table(iTable)%UA_BL%UACutout_delta*D2R CALL ParseVarWDefault ( FileInfo, CurLine, 'filtCutOff', p%Table(iTable)%UA_BL%filtCutOff, 0.5_ReKi, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CalcDefaults(iTable)%filtCutOff = ErrStat2 >= AbortErrLev - p%ColUAf = NumCoefsIn + 1 ! column for f_st - NumCoefsTab = p%ColUAf + 1 ! precompute f_st and cl_fs for the HGM model - IF (ErrStat >= AbortErrLev) THEN CALL Cleanup() RETURN END IF - - ELSE !ELSEIF ( UA_Model == 1 ) THEN - - !CALL SetErrStat ( ErrID_Fatal & - !, 'You must supply Beddoes-Leishman unsteady aerodynamics parameters for all airfoils if you want to use that' & - !//' model. You did not do so for Table #'//TRIM( Num2LStr( iTable ) )//' in the "'//TRIM( AFfile )//'" airfoil file.', ErrStat, ErrMsg, RoutineName ) - !CALL Cleanup() - !RETURN + ELSE + + ! everything is default ( we could just attempt to read these from the file, but it will be faster to use the default settings of .true. for each variable) + + p%Table(iTable)%InclUAdata = .true. ! make sure we are calculating this for each table ENDIF ! ( p%Table(iTable)%InclUAdata ) + if ( p%Table(iTable)%InclUAdata ) then + p%ColUAf = NumCoefsIn + 1 ! column for f_st for the UA models + NumCoefsTab = NumCoefsIn + 3 ! total number of columns if we have UA on (for f_st, cl/cn_fs, cl/cn_fa) + end if + + CALL ParseVar ( FileInfo, CurLine, 'NumAlf', p%Table(iTable)%NumAlf, ErrStat2, ErrMsg2, UnEc ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) THEN @@ -684,13 +684,9 @@ SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCo IF ( p%Table(iTable)%NumAlf < 1 ) THEN CALL SetErrStat( ErrID_Fatal, 'NumAlf must be a positive number on line #' & - //TRIM( Num2LStr( FileInfo%FileLine(CurLine-1) ) )//' in "'//TRIM( AFfile )//'".', ErrStat, ErrMsg, RoutineName ) + //TRIM( Num2LStr( FileInfo%FileLine(CurLine-1) ) )//' in "'//TRIM( InitInp%FileName )//'".', ErrStat, ErrMsg, RoutineName ) CALL Cleanup() RETURN - ELSEIF ( p%Table(iTable)%NumAlf < 3 ) THEN - p%Table(iTable)%ConstData = .TRUE. - ELSE - p%Table(iTable)%ConstData = .FALSE. ENDIF ! ( Test for valid values for NumAlf ) @@ -719,52 +715,81 @@ SUBROUTINE ReadAFfile ( AFfile, NumCoefsIn, InCol_Alfa, InCol_Cl, InCol_Cd, InCo RETURN END IF - p%Table(iTable)%Alpha(Row ) = SiAry(InCol_Alfa)*D2R - p%Table(iTable)%Coefs(Row,p%ColCl) = SiAry(InCol_Cl ) - p%Table(iTable)%Coefs(Row,p%ColCd) = SiAry(InCol_Cd ) + p%Table(iTable)%Alpha(Row ) = SiAry(InitInp%InCol_Alfa)*D2R + p%Table(iTable)%Coefs(Row,p%ColCl) = SiAry(InitInp%InCol_Cl ) + p%Table(iTable)%Coefs(Row,p%ColCd) = SiAry(InitInp%InCol_Cd ) - IF ( InCol_Cm > 0 ) p%Table(iTable)%Coefs(Row,p%ColCm ) = SiAry(InCol_Cm) - IF ( InCol_Cpmin > 0 ) p%Table(iTable)%Coefs(Row,p%ColCpmin) = SiAry(InCol_Cpmin) + IF ( InitInp%InCol_Cm > 0 ) p%Table(iTable)%Coefs(Row,p%ColCm ) = SiAry(InitInp%InCol_Cm) + IF ( InitInp%InCol_Cpmin > 0 ) p%Table(iTable)%Coefs(Row,p%ColCpmin) = SiAry(InitInp%InCol_Cpmin) ENDDO ! Row - call CalculateUACoeffs(p%Table(iTable), p%ColCl, p%ColUAf) + ! check that not all the values are constant + IF ( p%Table(iTable)%NumAlf < 3 ) THEN + p%Table(iTable)%ConstData = .TRUE. ! we can't do splines with this many points, so it must be constant + ELSE + ! check if the columns change with alpha + p%Table(iTable)%ConstData = .TRUE. +ALPHA_LOOP: DO Row=1,p%Table(iTable)%NumAlf-1 + DO Coef=1,NumCoefsIn ! don't check the additional columns from UA + IF ( .NOT. EqualRealNos( p%Table(iTable)%Coefs(Row,Coef), p%Table(iTable)%Coefs(Row+1,Coef) ) ) THEN + p%Table(iTable)%ConstData = .FALSE. + EXIT ALPHA_LOOP + ENDIF + END DO ! Coef + END DO ALPHA_LOOP + END IF + + + if ( p%Table(iTable)%ConstData ) then + p%Table(iTable)%InclUAdata = .false. + else + call CalculateUACoeffs(CalcDefaults(iTable), p%Table(iTable), p%ColCl, p%ColCd, p%ColCm, p%ColUAf, InitInp%UA_f_cn) + end if ! Let's make sure that the data go from -Pi to Pi and that the values are the same for both ! unless there is only one point. - IF ( .NOT. p%Table(iTable)%ConstData ) THEN - NumAlf = p%Table(iTable)%NumAlf + NumAlf = p%Table(iTable)%NumAlf + if (NumAlf > 1) then BadVals = .FALSE. - IF ( .NOT. EqualRealNos( p%Table(iTable)%Alpha(1), -Pi ) ) THEN - BadVals = .TRUE. - ENDIF - IF ( .NOT. EqualRealNos( p%Table(iTable)%Alpha(NumAlf), Pi ) ) THEN - BadVals = .TRUE. - ENDIF - DO Coef=1,NumCoefsTab + + if (.not. p%Table(iTable)%ConstData) then + IF ( .NOT. EqualRealNos( p%Table(iTable)%Alpha(1), -Pi ) ) THEN + BadVals = .TRUE. + ENDIF + IF ( .NOT. EqualRealNos( p%Table(iTable)%Alpha(NumAlf), Pi ) ) THEN + BadVals = .TRUE. + ENDIF + end if + + DO Coef=1,NumCoefsIn ! don't check the additional columns from UA IF ( .NOT. EqualRealNos( p%Table(iTable)%Coefs(1,Coef), p%Table(iTable)%Coefs(NumAlf,Coef) ) ) THEN BadVals = .TRUE. ENDIF ENDDO ! Coef + IF ( BadVals ) THEN -! CALL SetErrStat( ErrID_Fatal & - CALL SetErrStat( ErrID_Warn, & + if (p%Table(iTable)%ConstData) then + ErrStat2 = ErrID_Fatal + else + ErrStat2 = ErrID_Warn + end if + + CALL SetErrStat( ErrStat2, & 'Airfoil data should go from -180 degrees to 180 degrees and the coefficients at the ends should be the same.', ErrStat, ErrMsg, RoutineName ) - !CALL Cleanup() - !RETURN ENDIF - ENDIF ! ( .NOT. p%Table(iTable)%ConstData ) - + end if ENDDO ! iTable + DO iTable=1,p%NumTabs if ( .not. p%Table(iTable)%InclUAdata ) then p%ColUAf = 0 ! in case some tables have UA data and others don't; this is not set on a per-table basis exit ! exit loop end if ENDDO ! iTable - + CALL Cleanup( ) RETURN @@ -778,6 +803,7 @@ SUBROUTINE Cleanup () ! This subroutine cleans up all the allocatable arrays, sets the error status/message and closes the binary file CALL NWTC_Library_DestroyFileInfoType (FileInfo, ErrStat2, ErrMsg2) + IF ( ALLOCATED(CalcDefaults) ) DEALLOCATE(CalcDefaults) ! this type contains only logicals, so no need to call a destroy routine IF ( ALLOCATED(SiAry) ) DEALLOCATE(SiAry) END SUBROUTINE Cleanup @@ -785,100 +811,550 @@ END SUBROUTINE Cleanup END SUBROUTINE ReadAFfile !---------------------------------------------------------------------------------------------------------------------------------- - SUBROUTINE CalculateUACoeffs(p,ColCl,ColUAf) + SUBROUTINE CalculateUACoeffs(CalcDefaults,p,ColCl,ColCd,ColCm,ColUAf,UA_f_cn) + TYPE (AFI_UA_BL_Default_Type),intent(in):: CalcDefaults TYPE (AFI_Table_Type), intent(inout) :: p ! This structure stores all the module parameters that are set by AirfoilInfo during the initialization phase. integer(IntKi), intent(in ) :: ColCl ! column for cl - integer(IntKi), intent(in ) :: ColUAf ! column for UA f_st + integer(IntKi), intent(in ) :: ColCd ! column for cd + integer(IntKi), intent(in ) :: ColCm ! column for cm + integer(IntKi), intent(in ) :: ColUAf ! column for UA f_st (based on Cl or cn) + logical, intent(in ) :: UA_f_cn ! is f_st based on cn (true) or cl (false)? INTEGER(IntKi) :: Row ! The row of a table to be parsed in the FileInfo structure. - INTEGER(IntKi) :: col_clFs ! column for UA cl_fs + INTEGER(IntKi) :: col_fs ! column for UA cn/cl_fs (fully separated cn or cl) + INTEGER(IntKi) :: col_fa ! column for UA cn/cl_fa (fully attached cn or cl) REAL(ReKi) :: cl_ratio, cl_inv - REAL(ReKi) :: f_st, cl_fs + REAL(ReKi) :: f_st, fullySeparate REAL(ReKi) :: f_iHigh, f_iLow + INTEGER(IntKi) :: iCdMin INTEGER(IntKi) :: iHigh, iLow + INTEGER(IntKi) :: iHigh2, iLow2 + + ! note that we don't get here with constant data, so NumAlf>2 + REAL(ReKi) :: cn(p%NumAlf) + REAL(ReKi) :: CnSlope_raw(p%NumAlf-1) + REAL(ReKi) :: ClSlope_(p%NumAlf-1) + REAL(ReKi) :: CnSlope_(p%NumAlf-1) + REAL(ReKi) :: alpha_(p%NumAlf-1) + REAL(ReKi) :: alphaAtCdMin + REAL(ReKi) :: CnSlopeAtCdMin + REAL(ReKi) :: ClTmp, alphaTmp + REAL(ReKi) :: maxCnSlope + + REAL(ReKi) :: slope + REAL(ReKi) , PARAMETER :: CnSlopeThreshold = 0.90; + REAL(ReKi) , PARAMETER :: fAtCriticalCn = 0.7; - col_clFs = ColUAf + 1 + + - if ( p%InclUAdata ) then - p%UA_BL%UACutout_blend = max(0.0_ReKi, p%UA_BL%UACutout - 5.0_ReKi*D2R) ! begin turning off 5 degrees before (or at 0 degrees) + col_fs = ColUAf + 1 + col_fa = col_fs + 1 - if (EqualRealNos(p%UA_BL%c_lalpha,0.0_ReKi)) then - p%Coefs(:,ColUAf) = 0.0_ReKi ! Eq. 59 - p%Coefs(:,col_clFs) = p%Coefs(:,ColCl) ! Eq. 61 - else + if ( p%InclUAdata ) then + + ! these variables are for the unused UAMod=1 (UA_Baseline) model; + ! TO DO: fix the calculations if we ever allow this model to be used + if (CalcDefaults%eta_e ) p%UA_BL%eta_e = 1.00_ReKi ! it says it's supposed to be in the range 085 - 0.95, but then when FLookup is true, it uses 1. + if (CalcDefaults%k0 ) p%UA_BL%k0 = 0.00_ReKi + if (CalcDefaults%k1 ) p%UA_BL%k1 = 0.00_ReKi + if (CalcDefaults%k2 ) p%UA_BL%k2 = 0.00_ReKi + if (CalcDefaults%k3 ) p%UA_BL%k3 = 0.00_ReKi + if (CalcDefaults%k1_hat ) p%UA_BL%k1_hat = 0.00_ReKi + if (CalcDefaults%S1 ) p%UA_BL%S1 = 0.00_ReKi + if (CalcDefaults%S2 ) p%UA_BL%S2 = 0.00_ReKi + if (CalcDefaults%S3 ) p%UA_BL%S3 = 0.00_ReKi + if (CalcDefaults%S4 ) p%UA_BL%S4 = 0.00_ReKi + + ! Set to default values: + if (CalcDefaults%T_f0 ) p%UA_BL%T_f0 = 3.00_ReKi + if (CalcDefaults%T_V0 ) p%UA_BL%T_V0 = 6.00_ReKi + if (CalcDefaults%T_p ) p%UA_BL%T_p = 1.70_ReKi + if (CalcDefaults%T_VL ) p%UA_BL%T_VL = 11.00_ReKi + if (CalcDefaults%b1 ) p%UA_BL%b1 = 0.14_ReKi + if (CalcDefaults%b2 ) p%UA_BL%b2 = 0.53_ReKi + if (CalcDefaults%b5 ) p%UA_BL%b5 = 5.00_ReKi + if (CalcDefaults%A1 ) p%UA_BL%A1 = 0.30_ReKi + if (CalcDefaults%A2 ) p%UA_BL%A2 = 0.70_ReKi + if (CalcDefaults%A5 ) p%UA_BL%A5 = 1.00_ReKi + if (CalcDefaults%St_sh ) p%UA_BL%St_sh = 0.19_ReKi + if (CalcDefaults%x_cp_bar ) p%UA_BL%x_cp_bar = 0.20_ReKi + if (CalcDefaults%UACutout ) p%UA_BL%UACutout = 45.00_ReKi*D2R ! turn off UA at 45 degrees + if (CalcDefaults%UACutout_delta ) p%UA_BL%UACutout_delta = 5.00_ReKi*D2R ! begin turning off 5 degrees before UAcutout + + if (CalcDefaults%filtCutOff ) p%UA_BL%filtCutOff = 0.50_ReKi + + p%UA_BL%UACutout_blend = max(0.0_ReKi, abs(p%UA_BL%UACutout) - abs(p%UA_BL%UACutout_delta)) + + !------------------------------------- + ! Calculate based on airfoil polar: + !------------------------------------- + ! if Cd is constant, does this cause issues??? + iCdMin = minloc(p%Coefs(:,ColCd),DIM=1) + if (CalcDefaults%Cd0) p%UA_BL%Cd0 = p%Coefs(iCdMin,ColCd) - f_iHigh = huge(f_iHigh) - f_iLow = f_iHigh - iHigh = 0 - iLow = 0 + alphaAtCdMin = p%alpha(iCdMin) - do Row=1,p%NumAlf + ! compute cn: + do Row=1,p%NumAlf + cn(Row) = p%Coefs(Row,ColCl)*cos(p%alpha(Row)) + (p%Coefs(Row,ColCd) - p%UA_BL%Cd0)*sin(p%alpha(Row)) + end do + + ! compute cn and cl slopes (raw): + do Row=1,p%NumAlf-1 + CnSlope_raw(Row) = ( cn(Row+1) - cn(Row) ) / (p%alpha(Row+1) - p%alpha(Row)) + ClSlope_( Row) = ( p%Coefs(Row+1,ColCl) - p%Coefs(Row,ColCl) ) / (p%alpha(Row+1) - p%alpha(Row)) + alpha_( Row) = 0.5_ReKi * (p%alpha(Row+1) + p%alpha(Row)) + end do + + ! smooth cn slope for better calculations later: + call kernelSmoothing(alpha_, CnSlope_raw, kernelType_TRIWEIGHT, 2.0_ReKi*D2R, CnSlope_) + + + CnSlopeAtCdMin = InterpStp( alphaAtCdMin, alpha_, CnSlope_, iLow, p%NumAlf-1 ) + + !find alphaUpper (using smoothed Cn values): + iHigh = minloc( abs( alpha_ - 20.0_ReKi*D2R ),DIM=1 ) ! we can limit this to ~20 degrees + if (CalcDefaults%alphaUpper) then - if (EqualRealNos( p%alpha(Row), p%UA_BL%alpha0)) then - f_st = 1.0_ReKi ! Eq. 59 - p%Coefs(Row,col_clFs) = p%Coefs(Row,ColCl) / 2.0_ReKi ! Eq. 61 (which should be very close to 0 because definition of alpha0 says cl(alpha0) = 0 ) - else + if (iHigh maxCnSlope) then + maxCnSlope = CnSlope_(Row) + end if + if (CnSlope_(Row) < CnSlopeThreshold*maxCnSlope) exit + end do - f_st = ( 2.0_ReKi * sqrt(cl_ratio) - 1.0_ReKi )**2 - - if (f_st < 1.0_ReKi) then - ! Region where f_st<1, merge - f_st = max(0.0_ReKi, f_st) ! make sure it is not negative - cl_fs = (p%Coefs(Row,ColCl) - p%UA_BL%c_lalpha* (p%alpha(Row) - p%UA_BL%alpha0)*f_st) / (1.0_ReKi - f_st) ! Eq 61 + if (iHigh2 == iCdMin) then + p%UA_BL%alphaUpper = alphaAtCdMin; + else + iHigh2 = min(max(1, iHigh2-1), p%NumAlf-1 ) + p%UA_BL%alphaUpper = alpha_(iHigh2); + end if + + end if + + !find alphaLower + iLow = minloc( abs( alpha_ + 20.0_ReKi*D2R ), DIM=1 ) ! we can limit this to ~-20 degrees + if (CalcDefaults%alphaLower) then + + if (iLow>iCdMin) iLow = 1 + + maxCnSlope = CnSlopeAtCdMin + iLow2 = min(iCdMin, p%NumAlf-1) + + do Row = min(iCdMin, p%NumAlf-1) ,iLow,-1 + iLow2 = Row + if (CnSlope_(Row) > maxCnSlope) then + maxCnSlope = CnSlope_(Row); + end if + if ( CnSlope_(Row) < CnSlopeThreshold*maxCnSlope ) exit + end do + + if (iLow2 == iCdMin) then + p%UA_BL%alphaLower = alphaAtCdMin; + else + iLow2 = min(max(1, iLow2+1), p%NumAlf-1 ) + p%UA_BL%alphaLower = alpha_(iLow2); + end if + end if + + ! make sure iLow and iHigh are defined before doing this calculation: + ! note: perhaps we want to recalculate CnSlope_ with un-smoothed values???? + if (CalcDefaults%C_nalpha) p%UA_BL%C_nalpha = maxval(CnSlope_(iLow:iHigh)) + + ! this is calculated with un-smoothed data: + if (CalcDefaults%C_lalpha) p%UA_BL%C_lalpha = maxval(ClSlope_(iLow:iHigh)) + + + ! find alpha0 + ! least squares fit between alphaLower and alphaUpper??? (see LAPACK_GELSD) + ! For now we will just go a poor-man's linear fit with existing data: + if (CalcDefaults%alpha0) then + slope = p%UA_BL%C_lalpha + if (EqualRealNos(slope, 0.0_ReKi)) then + p%UA_BL%alpha0 = 0.0_ReKi ! doesn't really matter + else + alphaTmp = 0.5_ReKi * (p%UA_BL%alphaLower+p%UA_BL%alphaUpper) + ClTmp = InterpStp(alphaTmp, p%alpha, p%Coefs(:,ColCl), iLow, p%NumAlf) + p%UA_BL%alpha0 = alphaTmp - ClTmp / slope + end if + end if + + if (CalcDefaults%Cm0) then + if (ColCm > 0) then + iLow = p%NumAlf/2 ! guess: start in the center + p%UA_BL%Cm0 = InterpStp( p%UA_BL%alpha0, p%alpha, p%Coefs(:,ColCm), iLow, p%NumAlf ) + else + p%UA_BL%Cm0 = 0.0_ReKi + end if + end if + + + if (.not. UA_f_cn) then ! + !------------------------------------------------ + ! calculate f_st, cl_fs, and cl_fa for HGM model + !------------------------------------------------ + if (EqualRealNos(p%UA_BL%c_lalpha,0.0_ReKi)) then + p%Coefs(:,ColUAf) = 0.0_ReKi ! Eq. 59 + p%Coefs(:,col_fs) = p%Coefs(:,ColCl) ! Eq. 61 + p%Coefs(:,col_fa) = 0.0_ReKi + call ComputeUASeparationFunction_zero(p, ColUAf, p%Coefs(:,ColCl)) ! just to initialize these values... UA will turn off without using them + else + + do Row=1,p%NumAlf + + if (EqualRealNos( p%alpha(Row), p%UA_BL%alpha0)) then + f_st = 1.0_ReKi ! Eq. 59 + p%Coefs(Row,col_fs) = p%Coefs(Row,ColCl) / 2.0_ReKi ! Eq. 61 (which should be very close to 0 because definition of alpha0 says cl(alpha0) = 0 ) else - ! Initialize to linear region (in fact only at singularity, where f_st=1) - f_st = 1.0_ReKi - cl_fs = p%Coefs(Row,ColCl) / 2.0_ReKi ! Eq. 61 - end if + + cl_ratio = p%Coefs(Row,ColCl) / ( p%UA_BL%c_lalpha*(p%alpha(Row) - p%UA_BL%alpha0)) + cl_ratio = max(0.0_ReKi, cl_ratio) + + f_st = ( 2.0_ReKi * sqrt(cl_ratio) - 1.0_ReKi )**2 - if (p%alpha(Row) < p%UA_BL%alpha0) then - if (f_st <= f_iLow) then - f_iLow = f_st - iLow = Row - end if - else !p%alpha(Row) > p%UA_BL%alpha0 (note that they can't be equal) - if (f_st < f_iHigh) then - f_iHigh = f_st - iHigh = Row + if (f_st < 1.0_ReKi) then + ! Region where f_st<1, merge + f_st = max(0.0_ReKi, f_st) ! make sure it is not negative + fullySeparate = (p%Coefs(Row,ColCl) - p%UA_BL%c_lalpha* (p%alpha(Row) - p%UA_BL%alpha0)*f_st) / (1.0_ReKi - f_st) ! Eq 61 + else + ! Initialize to linear region (in fact only at singularity, where f_st=1) + f_st = 1.0_ReKi + fullySeparate = p%Coefs(Row,ColCl) / 2.0_ReKi ! Eq. 61 end if + end if - end if - p%Coefs(Row,ColUAf) = f_st - p%Coefs(Row,col_clFs) = cl_fs - end do - if (iLow >0) p%Coefs(1:iLow,col_clFs) = p%Coefs(1:iLow,ColCl) - if (iHigh>0) p%Coefs(iHigh:,col_clFs) = p%Coefs(iHigh:,ColCl) - + p%Coefs(Row,ColUAf) = f_st + p%Coefs(Row,col_fs) = fullySeparate + end do + + ! Compute variables to help x3 state with +/-180-degree wrap-around issues + ! and make sure that the separation function is monotonic before iLow and after iHigh: + call ComputeUASeparationFunction_zero(p, ColUAf, p%Coefs(:,ColCl)) ! this was comparing with alpha0, but now we compaer with alphaUpper and alphaLower + - ! Ensuring everything is in harmony - do Row=1,p%NumAlf - cl_fs = p%Coefs(Row,col_clFs) + ! Ensuring everything is in harmony + do Row=1,p%NumAlf + fullySeparate = p%Coefs(Row,col_fs) - cl_inv = p%UA_BL%c_lalpha*(p%alpha(Row) - p%UA_BL%alpha0) ! Eq. 64 - if (.not. EqualRealNos(cl_inv, cl_fs)) then - f_st=(p%Coefs(Row,ColCl) - cl_fs) / (cl_inv-cl_fs); ! Eq. 60 - f_st = max(0.0_ReKi, f_st) - f_st = min(1.0_ReKi, f_st) + cl_inv = p%UA_BL%c_lalpha*(p%alpha(Row) - p%UA_BL%alpha0) ! Eq. 64 + if (.not. EqualRealNos(cl_inv, fullySeparate)) then + f_st=(p%Coefs(Row,ColCl) - fullySeparate) / (cl_inv - fullySeparate); ! Eq. 60 + f_st = max(0.0_ReKi, f_st) + f_st = min(1.0_ReKi, f_st) - p%Coefs(Row,ColUAf) = f_st - else - p%Coefs(Row,ColUAf) = 1.0_ReKi + p%Coefs(Row,ColUAf) = f_st + else + p%Coefs(Row,ColUAf) = 1.0_ReKi + end if + end do + + + end if ! c_lalpha == 0 + + else + + call ComputeUASeparationFunction(p, ColUAf, cn) + + end if + + ! alpha1, alpha2, Cn1 and Cn2 + iLow = 1 + f_iLow = huge(f_iHigh) + iHigh = p%NumAlf + f_iHigh = f_iLow + do Row=1,p%NumAlf + if (p%alpha(Row) < p%UA_BL%AlphaLower) then + f_st = abs(p%Coefs(Row,ColUAf) - fAtCriticalCn) + if ( f_st < f_iLow ) then + iLow = Row + f_iLow = f_st + end if + else if (p%alpha(Row) > p%UA_BL%AlphaUpper) then + f_st = abs(p%Coefs(Row,ColUAf) - fAtCriticalCn) + if ( f_st < f_iHigh ) then + iHigh = Row + f_iHigh = f_st end if + end if + end do + + ! alpha2 + if (CalcDefaults%alpha2) then + if ( (p%Coefs(iLow,ColUAf) < fAtCriticalCn .and. iLow < p%NumAlf) .or. iLow == 1) then + iLow2 = iLow + 1 + else + iLow2 = iLow - 1 + end if + + slope = (p%Coefs(iLow,ColUAf) - p%Coefs(iLow2,ColUAf)) / (p%alpha(iLow) - p%alpha(iLow2)) + if (EqualRealNos(slope, 0.0_ReKi) ) then + p%UA_BL%alpha2 = p%alpha(iLow) + else + p%UA_BL%alpha2 = (fAtCriticalCn - p%Coefs(iLow,ColUAf)) / slope + p%alpha(iLow) + end if + end if + + ! alpha1 + if (CalcDefaults%alpha1) then + if ((p%Coefs(iHigh,ColUAf) < fAtCriticalCn .and. iHigh > 1) .or. iHigh == p%NumAlf) then + iHigh2 = iHigh - 1 + else + iHigh2 = iHigh + 1 + end if + slope =(p%Coefs(iHigh,ColUAf) - p%Coefs(iHigh2,ColUAf)) / (p%alpha(iHigh) - p%alpha(iHigh2)) + if (EqualRealNos(slope, 0.0_ReKi) ) then + p%UA_BL%alpha1 = p%alpha(iHigh) + else + p%UA_BL%alpha1 = (fAtCriticalCn - p%Coefs(iHigh,ColUAf)) / slope + p%alpha(iHigh) + end if + end if + + + ! Cn1 + if (CalcDefaults%Cn1) then + p%UA_BL%Cn1 = InterpStp( p%UA_BL%alpha1, p%alpha, cn, iHigh, p%NumAlf ) + end if + + ! Cn2 + if (CalcDefaults%Cn2) then + p%UA_BL%Cn2 = InterpStp( p%UA_BL%alpha2, p%alpha, cn, iLow, p%NumAlf ) + end if - end do - end if ! c_lalpha == 0 + ! after we know the fully attached Cn + + end if ! UA is included + + END SUBROUTINE CalculateUACoeffs +!---------------------------------------------------------------------------------------------------------------------------------- + SUBROUTINE ComputeUASeparationFunction(p, ColUAf, cn_cl) + TYPE (AFI_Table_Type), intent(inout) :: p ! This structure stores all the module parameters that are set by AirfoilInfo during the initialization phase. + integer(IntKi), intent(in ) :: ColUAf ! column for UA f_st (based on Cl or cn) + REAL(ReKi), intent(in ) :: cn_cl(:) ! cn or cl, whichever variable we are computing this on + + REAL(ReKi) :: temp + INTEGER(IntKi) :: Row ! The row of a table to be parsed in the FileInfo structure. + INTEGER(IntKi) :: col_fs ! column for UA cn/cl_fs (fully separated cn or cl) + INTEGER(IntKi) :: col_fa ! column for UA cn/cl_fa (fully attached cn or cl) + + REAL(ReKi) :: c_ratio + REAL(ReKi) :: f_st + REAL(ReKi) :: denom + REAL(ReKi) :: fullySeparate + + INTEGER(IntKi) :: iHigh, iLow + INTEGER(IntKi) :: iHigh_2, iLow_2 + INTEGER(IntKi) :: iHigh_1, iLow_1 + + REAL(ReKi) :: c_num, c_den + REAL(ReKi) :: c_offset + + !------------------------------------------------ + ! set column numbers + !------------------------------------------------ + col_fs = ColUAf + 1 + col_fa = col_fs + 1 + + + !------------------------------------------------ + ! calculate f_st, {cn | cl}_fs, and {cn | cl}_fa for UA model + !------------------------------------------------ + if (p%UA_BL%alphaLower > p%UA_BL%alphaUpper) then ! switch them around + temp = p%UA_BL%alphaUpper + p%UA_BL%alphaUpper = p%UA_BL%alphaLower + p%UA_BL%alphaLower = temp + end if + + !------------------------------------------------ + ! find iLow where p%alpha(iLow) = p%UA_BL%alphaLower + ! Note that we may have specified an alphaLower that is not in the input alpha table, so we get the closest value + !------------------------------------------------ + iLow = 1 ! start at first index + CALL LocateBin( p%UA_BL%alphaLower, p%alpha, iLow, p%NumAlf ) + if (iLow < p%NumAlf) then + if ( p%alpha(iLow+1) - p%UA_BL%alphaLower < p%UA_BL%alphaLower - p%alpha(iLow) ) then ! see if p%alpha(iLow) or p%alpha(iLow+1) is the closest index + if (p%UA_BL%alphaUpper > p%alpha(iLow+1)) iLow = iLow + 1 !if we don't go past, alphaUpper, use the closest alpha: p%alpha(iLow+1) + end if + else + iLow = p%NumAlf - 1 ! we can't have IndLower > IndUpper, so fix it here. end if + ! figure out which side of iLow to compute the slope later: + if (p%UA_BL%alphaLower < p%alpha(iLow) .and. iLow > 1) then + iLow_2= iLow - 1 + else + iLow_2 = iLow + 1 + end if + iLow_1 = iLow + + ! get value + p%UA_BL%c_alphaLower = InterpStp( p%UA_BL%alphaLower, p%alpha, cn_cl, iLow, p%NumAlf ) + + !------------------------------------------------ + ! find iHigh where p%alpha(iHigh) is approximately p%UA_BL%alphaUpper + ! Note that we may have specified an alphaUpper that is not in the input alpha table, so we get the closest value + ! also making sure that iLow < iHigh + !------------------------------------------------ + iHigh = iLow_1 ! start at first index + CALL LocateStp( p%UA_BL%alphaUpper, p%alpha, iHigh, p%NumAlf ) + if (iHigh < p%NumAlf) then + if (iHigh >= iLow) then + if ( p%alpha(iHigh+1) - p%UA_BL%alphaUpper < p%UA_BL%alphaUpper - p%alpha(iHigh) ) iHigh = iHigh + 1 + else + iHigh = iLow + 1 + end if + end if + ! figure out which side of iHigh to compute the slope later: + if (p%UA_BL%alphaUpper < p%alpha(iHigh) .or. iHigh == p%NumAlf) then + iHigh_2= iHigh - 1 + else + iHigh_2 = iHigh + 1 + end if + iHigh_1 = iHigh - + p%UA_BL%c_alphaUpper = InterpStp( p%UA_BL%alphaUpper, p%alpha, cn_cl, iHigh, p%NumAlf ) + + !------------------------------------------------ + ! Compute derivatives for fully attached values of cn or cl: + !------------------------------------------------ + denom = (p%UA_BL%alphaLower - p%UA_BL%alphaUpper) + if (EqualRealNos(denom,0.0_ReKi)) then + p%UA_BL%c_Rate = 0.0_ReKi + else + p%UA_BL%c_Rate = (p%UA_BL%c_alphaLower - p%UA_BL%c_alphaUpper)/(p%UA_BL%alphaLower - p%UA_BL%alphaUpper) + end if + p%UA_BL%c_Rate = max(p%UA_BL%c_Rate, sqrt(epsilon(p%UA_BL%c_Rate))) ! make sure this isn't zero + + ! these can't have zero in the denom because alphas are unique and the indices are not the same: + p%UA_BL%c_RateLower = (cn_cl( iLow_1) - cn_cl( iLow_2))/(p%alpha( iLow_1) - p%alpha( iLow_2)) + p%UA_BL%c_RateUpper = (cn_cl(iHigh_1) - cn_cl(iHigh_2))/(p%alpha(iHigh_1) - p%alpha(iHigh_2)) + p%UA_BL%c_RateLower = max(p%UA_BL%c_RateLower, sqrt(epsilon(p%UA_BL%c_RateLower))) ! make sure this isn't zero + p%UA_BL%c_RateUpper = max(p%UA_BL%c_RateUpper, sqrt(epsilon(p%UA_BL%c_RateUpper))) ! make sure this isn't zero - END SUBROUTINE CalculateUACoeffs + ! Linear extrapolation using values computed with alphaLower and alphaUpper; + ! between alphaLower and alphaUpper, set equal to {cn | cl} so that we don't get asymptotic behavior in the separation function. + do Row=1,iLow-1 + p%Coefs(Row,col_fa) = (p%alpha(Row) - p%UA_BL%alphaLower) * p%UA_BL%c_RateLower + p%UA_BL%c_alphaLower + end do + do Row=iLow,iHigh + p%Coefs(Row,col_fa) = cn_cl(Row) + end do + do Row=iHigh+1,p%NumAlf + p%Coefs(Row,col_fa) = (p%alpha(Row) - p%UA_BL%alphaUpper) * p%UA_BL%c_RateUpper + p%UA_BL%c_alphaUpper + end do + + !---------------------------------------------------------------------- + ! Compute separation function, f_st, as well as fully separated values: + !---------------------------------------------------------------------- + c_offset = (p%UA_BL%c_alphaLower + p%UA_BL%c_alphaUpper) / 2.0_ReKi + + do Row=1,p%NumAlf + + c_num = cn_cl(Row) - c_offset ! numerator + c_den = p%Coefs(Row,col_fa) - c_offset ! denominator + + if (EqualRealNos(c_den,0.0_ReKi)) then + c_ratio = 1.0_ReKi ! This will occur in the fully attached region, where we want f=1. + f_st = 1.0_ReKi + else + c_ratio = max(0.25_ReKi, c_num / c_den) + f_st = (2.0_ReKi * sqrt(c_ratio) - 1.0_ReKi)**2 + end if + + if (f_st < 1.0_ReKi) then + ! Region where f_st<1, merge + f_st = max(0.0_ReKi, f_st) ! make sure it is not negative + fullySeparate = (cn_cl(Row) - p%Coefs(Row,col_fa)*f_st) / (1.0_ReKi - f_st) + else + ! Fully attached region + f_st = 1.0_ReKi ! make sure it doesen't exceed 1 + fullySeparate = (cn_cl(Row) + c_offset)/ 2.0_ReKi + end if + + p%Coefs(Row,ColUAf) = f_st + p%Coefs(Row,col_fs) = fullySeparate + end do + + + ! Compute variables to help x3 state with +/-180-degree wrap-around issues + ! and make sure that the separation function is monotonic before iLow and after iHigh: + call ComputeUASeparationFunction_zero(p, ColUAf, cn_cl) + + END SUBROUTINE ComputeUASeparationFunction +!---------------------------------------------------------------------------------------------------------------------------------- + SUBROUTINE ComputeUASeparationFunction_zero(p, ColUAf, cn_cl) + TYPE (AFI_Table_Type), intent(inout) :: p ! This structure stores all the module parameters that are set by AirfoilInfo during the initialization phase. + integer(IntKi), intent(in ) :: ColUAf ! column for UA f_st (based on Cl or cn) + REAL(ReKi), intent(in ) :: cn_cl(:) ! cn or cl, whichever variable we are computing this on + + INTEGER(IntKi) :: Row ! The row of a table to be parsed in the FileInfo structure. + INTEGER(IntKi) :: col_fs ! column for UA cn/cl_fs (fully separated cn or cl) + INTEGER(IntKi) :: col_fa ! column for UA cn/cl_fa (fully attached cn or cl) + INTEGER(IntKi) :: iHigh, iLow + REAL(ReKi) :: f_st ! separation function + REAL(ReKi) :: f_iHigh, f_iLow + + !------------------------------------------------ + ! set column numbers + !------------------------------------------------ + col_fs = ColUAf + 1 + col_fa = col_fs + 1 + + + ! initialize so that we can find the minimum f on each side of the attached region + f_iHigh = huge(f_iHigh) + f_iLow = f_iHigh + iHigh = p%NumAlf + iLow = 1 + + + do Row=1,p%NumAlf + + f_st = p%Coefs(Row,ColUAf) + + if (p%alpha(Row) < p%UA_BL%alphaLower) then ! find minimum f below alphaLower + if (f_st <= f_iLow) then + f_iLow = f_st + iLow = Row + end if + else if (p%alpha(Row) > p%UA_BL%alphaUpper) then + if (f_st < f_iHigh) then ! find minimum f above alphaUpper + f_iHigh = f_st + iHigh = Row + end if + end if + + end do + + + ! Compute variables to help x3 state with +/-180-degree wrap-around issues + p%UA_BL%alphaUpperWrap = p%alpha(iHigh) + p%UA_BL%alphaLowerWrap = p%alpha(iLow) + p%UA_BL%c_RateWrap = (p%Coefs(iHigh,col_fa) - p%Coefs(iLow,col_fa)) / ( (p%alpha(iHigh)-TwoPi) - p%alpha(iLow)) + p%UA_BL%c_alphaUpperWrap = p%Coefs(iHigh,col_fa) + p%UA_BL%c_alphaLowerWrap = p%Coefs(iLow,col_fa) + + ! make sure that the separation function is monotonic before iLow and after iHigh: + do Row=1,iLow + p%Coefs(Row,col_fa) = (p%alpha(Row) - p%UA_BL%alphaLowerWrap) * p%UA_BL%c_RateWrap + p%UA_BL%c_alphaLowerWrap + p%Coefs(Row,col_fs) = cn_cl(Row) + p%Coefs(Row,ColUAf) = 0.0_ReKi + end do + do Row=iHigh,p%NumAlf + p%Coefs(Row,col_fa) = (p%alpha(Row) - p%UA_BL%alphaUpperWrap) * p%UA_BL%c_RateWrap + p%UA_BL%c_alphaUpperWrap + p%Coefs(Row,col_fs) = cn_cl(Row) + p%Coefs(Row,ColUAf) = 0.0_ReKi + end do + +END SUBROUTINE ComputeUASeparationFunction_zero !---------------------------------------------------------------------------------------------------------------------------------- subroutine FindBoundingTables(p, secondaryDepVal, lowerTable, upperTable, xVals) @@ -1038,18 +1514,21 @@ subroutine AFI_ComputeAirfoilCoefs1D( AOA, p, AFI_interp, errStat, errMsg, Table s1 = size(p%Table(iTab)%Coefs,2) - Alpha = AOA - call MPi2Pi ( Alpha ) ! change AOA into range of -pi to pi - + if (p%Table(iTab)%ConstData) then + IntAFCoefs(1:s1) = p%Table(iTab)%Coefs(1,:) ! all the rows are constant, so we can just return the values at any alpha (e.g., row 1) + else + Alpha = AOA + call MPi2Pi ( Alpha ) ! change AOA into range of -pi to pi - ! Spline interpolation of lower table based on requested AOA - IntAFCoefs(1:s1) = CubicSplineInterpM( Alpha & - , p%Table(iTab)%Alpha & - , p%Table(iTab)%Coefs & - , p%Table(iTab)%SplineCoefs & - , ErrStat, ErrMsg ) + ! Spline interpolation of lower table based on requested AOA + IntAFCoefs(1:s1) = CubicSplineInterpM( Alpha & + , p%Table(iTab)%Alpha & + , p%Table(iTab)%Coefs & + , p%Table(iTab)%SplineCoefs & + , ErrStat, ErrMsg ) + end if AFI_interp%Cl = IntAFCoefs(p%ColCl) AFI_interp%Cd = IntAFCoefs(p%ColCd) @@ -1067,11 +1546,13 @@ subroutine AFI_ComputeAirfoilCoefs1D( AOA, p, AFI_interp, errStat, errMsg, Table end if if ( p%ColUAf > 0 ) then - AFI_interp%f_st = IntAFCoefs(p%ColUAf) - AFI_interp%cl_fs = IntAFCoefs(p%ColUAf+1) + AFI_interp%f_st = IntAFCoefs(p%ColUAf) ! separation function + AFI_interp%fullySeparate = IntAFCoefs(p%ColUAf+1) ! fully separated cn or cl + AFI_interp%fullyAttached = IntAFCoefs(p%ColUAf+2) ! fully attached cn or cl else - AFI_interp%f_st = 0.0_ReKi - AFI_interp%cl_fs = 0.0_ReKi + AFI_interp%f_st = 0.0_ReKi + AFI_interp%fullySeparate = 0.0_ReKi + AFI_interp%fullyAttached = 0.0_ReKi end if ! needed if using UnsteadyAero: diff --git a/modules/aerodyn/src/AirfoilInfo_Registry.txt b/modules/aerodyn/src/AirfoilInfo_Registry.txt index 009f64fb4..6f1b1e8c4 100644 --- a/modules/aerodyn/src/AirfoilInfo_Registry.txt +++ b/modules/aerodyn/src/AirfoilInfo_Registry.txt @@ -55,9 +55,63 @@ typedef ^ ^ ReKi k3 typedef ^ ^ ReKi k1_hat - - - "Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1]" - typedef ^ ^ ReKi x_cp_bar - - - "Constant in the expression of \hat(x)_cp^v [ignored if UAMod<>1, default = 0.2]" - typedef ^ ^ ReKi UACutout - - - "Angle of attack above which unsteady aerodynamics are disabled" "input in degrees; stored as radians" +typedef ^ ^ ReKi UACutout_delta - - - "Number of angles-of-attack below UACutout where unsteady aerodynamics begin to be disabled" "input in degrees; stored as radians" typedef ^ ^ ReKi UACutout_blend - - - "Angle of attack above which unsteady aerodynamics begins to be disabled" "stored as radians" typedef ^ ^ ReKi filtCutOff - - - "Reduced frequency cutoff used to calculate the dynamic low pass filter cut-off frequency for the pitching rate and accelerations [default = 0.5]" - +typedef ^ ^ ReKi alphaUpper - - 2pi "(input) upper angle of attack defining fully attached region" "input in degrees; stored as radians" +typedef ^ ^ ReKi alphaLower - - 2pi "(input) lower angle of attack defining fully attached region" "input in degrees; stored as radians" +typedef ^ ^ ReKi c_Rate - - - "(calculated) linear slope in the fully attached region of cn or cl" "1/rad" +typedef ^ ^ ReKi c_RateUpper - - - "(calculated) linear slope in the upper fully attached region of cn or cl" "1/rad" +typedef ^ ^ ReKi c_RateLower - - - "(calculated) linear slope in the lower fully attached region of cn or cl" "1/rad" +typedef ^ ^ ReKi c_alphaLower - - - "(calculated) value of cn or cl at alphaLower" "-" +typedef ^ ^ ReKi c_alphaUpper - - - "(calculated) value of cn or cl at alphaUpper" "-" +typedef ^ ^ ReKi alphaUpperWrap - - 2pi "(calculated) upper angle of attack defining fully attached wrap-around region" "stored as radians" +typedef ^ ^ ReKi alphaLowerWrap - - 2pi "(calculated) lower angle of attack defining fully attached wrap-around region" "stored as radians" +typedef ^ ^ ReKi c_RateWrap - - - "(calculated) linear slope in the fully attached wrap-around region of cn or cl (will be negative)" "1/rad" +typedef ^ ^ ReKi c_alphaLowerWrap - - - "(calculated) value of cn or cl at alphaLowerWrap" "-" +typedef ^ ^ ReKi c_alphaUpperWrap - - - "(calculated) value of cn or cl at alphaUpperWrap" "-" + + +typedef AirfoilInfo/AFI AFI_UA_BL_Default_Type LOGICAL alpha0 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL alpha1 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL alpha2 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL eta_e - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL C_nalpha - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL C_lalpha - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL T_f0 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL T_V0 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL T_p - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL T_VL - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL b1 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL b2 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL b5 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL A1 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL A2 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL A5 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL S1 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL S2 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL S3 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL S4 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL Cn1 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL Cn2 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL St_sh - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL Cd0 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL Cm0 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL k0 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL k1 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL k2 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL k3 - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL k1_hat - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL x_cp_bar - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL UACutout - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL UACutout_delta - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL filtCutOff - .true. - "Calculate value for this input?" - + +typedef ^ ^ LOGICAL alphaUpper - .true. - "Calculate value for this input?" - +typedef ^ ^ LOGICAL alphaLower - .true. - "Calculate value for this input?" - + + # The following derived type stores data for an airfoil at a single combination of Re and control setting. typedef ^ AFI_Table_Type ReKi Alpha {:} - - "Angle-of-attack vector that matches the Coefs matrix" rad typedef ^ ^ ReKi Coefs {:}{:} - - "Airfoil coefficients for Cd, Cl, and maybe Cm and/or Cpmin" - @@ -78,6 +132,7 @@ typedef ^ ^ INTEGER InCol_Cl typedef ^ ^ INTEGER InCol_Cd - - - "The column of the coefficient tables that holds the minimum pressure coefficient" - typedef ^ ^ INTEGER InCol_Cm - - - "The column of the coefficient tables that holds the pitching-moment coefficient" - typedef ^ ^ INTEGER InCol_Cpmin - - - "The column of the coefficient tables that holds the minimum pressure coefficient" - +typedef ^ ^ LOGICAL UA_f_cn - - - "Whether any UA separation functions should be calculated on cn (true) or cl (false)" - # Define outputs from the initialization routine here: typedef ^ InitOutputType ProgDesc Ver - - - "This module's name, version, and date" - @@ -89,7 +144,7 @@ typedef ^ ParameterType INTEGER ColCd typedef ^ ^ INTEGER ColCl - - - "The column in the p%Coefs arrays that contains Cl data" - typedef ^ ^ INTEGER ColCm - - - "The column in the p%Coefs arrays that contains Cm data" - typedef ^ ^ INTEGER ColCpmin - - - "The column in the p%Coefs arrays that contains Cpmin data" - -typedef ^ ^ INTEGER ColUAf - - - "The column in the p%Coefs arrays that contains f_st data for UA" - +typedef ^ ^ INTEGER ColUAf - - - "The column in the p%Coefs arrays that contains f_st data (on cl or cn) for UA" - typedef ^ ^ INTEGER AFTabMod - - - "Interpolation method for multiple airfoil tables {1 = 1D on AoA (only first table is used); 2 = 2D on AoA and Re; 3 = 2D on AoA and UserProp}" - typedef ^ ^ ReKi secondVals {:} - - "The values of the 2nd dependent variable when using multiple airfoil tables (Re or UserProp, saved in an array so that the logic in the interpolation scheme is cleaner)" - typedef ^ ^ IntKi InterpOrd - - - "Interpolation order" - @@ -110,13 +165,14 @@ typedef ^ ^ ReKi Re # ..... Outputs ................................................................................................................... # Define outputs that are not contained on the mesh here: -typedef ^ OutputType ReKi Cl - 0. - "Dimensionless coefficient of lift" - -typedef ^ ^ ReKi Cd - 0. - "Dimensionless coefficient of drag" - -typedef ^ ^ ReKi Cm - 0. - "Dimensionless coefficient of pitching moment" - -typedef ^ ^ ReKi Cpmin - 0. - "Dimensionless coefficient of minimum pressure" - -typedef ^ ^ ReKi Cd0 - 0. - "Minimum Cd value (used for Beddoes-Leishman unsteady aero)" - -typedef ^ ^ ReKi Cm0 - 0. - "2D pitching moment coefficient at zero lift, positive if nose is up" - -typedef ^ ^ ReKi f_st - 0. - "separation function (used for UA HGM model)" - -typedef ^ ^ ReKi cl_fs - 0. - "fully separated polar function (used for UA HGM model)" - +typedef ^ OutputType ReKi Cl - 0. - "Dimensionless coefficient of lift" - +typedef ^ ^ ReKi Cd - 0. - "Dimensionless coefficient of drag" - +typedef ^ ^ ReKi Cm - 0. - "Dimensionless coefficient of pitching moment" - +typedef ^ ^ ReKi Cpmin - 0. - "Dimensionless coefficient of minimum pressure" - +typedef ^ ^ ReKi Cd0 - 0. - "Minimum Cd value (used for Beddoes-Leishman unsteady aero)" - +typedef ^ ^ ReKi Cm0 - 0. - "2D pitching moment coefficient at zero lift, positive if nose is up" - +typedef ^ ^ ReKi f_st - 0. - "separation function based on cl or cn (used for UA models)" - +typedef ^ ^ ReKi FullySeparate - 0. - "fully separated cn or cl polar function (used for UA models)" - +typedef ^ ^ ReKi FullyAttached - 0. - "fully attached cn or cl polar function (used for UA models)" - diff --git a/modules/aerodyn/src/AirfoilInfo_Types.f90 b/modules/aerodyn/src/AirfoilInfo_Types.f90 index d092af28c..933e7ea0f 100644 --- a/modules/aerodyn/src/AirfoilInfo_Types.f90 +++ b/modules/aerodyn/src/AirfoilInfo_Types.f90 @@ -70,10 +70,63 @@ MODULE AirfoilInfo_Types REAL(ReKi) :: k1_hat !< Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] [-] REAL(ReKi) :: x_cp_bar !< Constant in the expression of \hat(x)_cp^v [ignored if UAMod<>1, default = 0.2] [-] REAL(ReKi) :: UACutout !< Angle of attack above which unsteady aerodynamics are disabled [input in degrees; stored as radians] + REAL(ReKi) :: UACutout_delta !< Number of angles-of-attack below UACutout where unsteady aerodynamics begin to be disabled [input in degrees; stored as radians] REAL(ReKi) :: UACutout_blend !< Angle of attack above which unsteady aerodynamics begins to be disabled [stored as radians] REAL(ReKi) :: filtCutOff !< Reduced frequency cutoff used to calculate the dynamic low pass filter cut-off frequency for the pitching rate and accelerations [default = 0.5] [-] + REAL(ReKi) :: alphaUpper !< (input) upper angle of attack defining fully attached region [input in degrees; stored as radians] + REAL(ReKi) :: alphaLower !< (input) lower angle of attack defining fully attached region [input in degrees; stored as radians] + REAL(ReKi) :: c_Rate !< (calculated) linear slope in the fully attached region of cn or cl [1/rad] + REAL(ReKi) :: c_RateUpper !< (calculated) linear slope in the upper fully attached region of cn or cl [1/rad] + REAL(ReKi) :: c_RateLower !< (calculated) linear slope in the lower fully attached region of cn or cl [1/rad] + REAL(ReKi) :: c_alphaLower !< (calculated) value of cn or cl at alphaLower [-] + REAL(ReKi) :: c_alphaUpper !< (calculated) value of cn or cl at alphaUpper [-] + REAL(ReKi) :: alphaUpperWrap !< (calculated) upper angle of attack defining fully attached wrap-around region [stored as radians] + REAL(ReKi) :: alphaLowerWrap !< (calculated) lower angle of attack defining fully attached wrap-around region [stored as radians] + REAL(ReKi) :: c_RateWrap !< (calculated) linear slope in the fully attached wrap-around region of cn or cl (will be negative) [1/rad] + REAL(ReKi) :: c_alphaLowerWrap !< (calculated) value of cn or cl at alphaLowerWrap [-] + REAL(ReKi) :: c_alphaUpperWrap !< (calculated) value of cn or cl at alphaUpperWrap [-] END TYPE AFI_UA_BL_Type ! ======================= +! ========= AFI_UA_BL_Default_Type ======= + TYPE, PUBLIC :: AFI_UA_BL_Default_Type + LOGICAL :: alpha0 = .true. !< Calculate value for this input? [-] + LOGICAL :: alpha1 = .true. !< Calculate value for this input? [-] + LOGICAL :: alpha2 = .true. !< Calculate value for this input? [-] + LOGICAL :: eta_e = .true. !< Calculate value for this input? [-] + LOGICAL :: C_nalpha = .true. !< Calculate value for this input? [-] + LOGICAL :: C_lalpha = .true. !< Calculate value for this input? [-] + LOGICAL :: T_f0 = .true. !< Calculate value for this input? [-] + LOGICAL :: T_V0 = .true. !< Calculate value for this input? [-] + LOGICAL :: T_p = .true. !< Calculate value for this input? [-] + LOGICAL :: T_VL = .true. !< Calculate value for this input? [-] + LOGICAL :: b1 = .true. !< Calculate value for this input? [-] + LOGICAL :: b2 = .true. !< Calculate value for this input? [-] + LOGICAL :: b5 = .true. !< Calculate value for this input? [-] + LOGICAL :: A1 = .true. !< Calculate value for this input? [-] + LOGICAL :: A2 = .true. !< Calculate value for this input? [-] + LOGICAL :: A5 = .true. !< Calculate value for this input? [-] + LOGICAL :: S1 = .true. !< Calculate value for this input? [-] + LOGICAL :: S2 = .true. !< Calculate value for this input? [-] + LOGICAL :: S3 = .true. !< Calculate value for this input? [-] + LOGICAL :: S4 = .true. !< Calculate value for this input? [-] + LOGICAL :: Cn1 = .true. !< Calculate value for this input? [-] + LOGICAL :: Cn2 = .true. !< Calculate value for this input? [-] + LOGICAL :: St_sh = .true. !< Calculate value for this input? [-] + LOGICAL :: Cd0 = .true. !< Calculate value for this input? [-] + LOGICAL :: Cm0 = .true. !< Calculate value for this input? [-] + LOGICAL :: k0 = .true. !< Calculate value for this input? [-] + LOGICAL :: k1 = .true. !< Calculate value for this input? [-] + LOGICAL :: k2 = .true. !< Calculate value for this input? [-] + LOGICAL :: k3 = .true. !< Calculate value for this input? [-] + LOGICAL :: k1_hat = .true. !< Calculate value for this input? [-] + LOGICAL :: x_cp_bar = .true. !< Calculate value for this input? [-] + LOGICAL :: UACutout = .true. !< Calculate value for this input? [-] + LOGICAL :: UACutout_delta = .true. !< Calculate value for this input? [-] + LOGICAL :: filtCutOff = .true. !< Calculate value for this input? [-] + LOGICAL :: alphaUpper = .true. !< Calculate value for this input? [-] + LOGICAL :: alphaLower = .true. !< Calculate value for this input? [-] + END TYPE AFI_UA_BL_Default_Type +! ======================= ! ========= AFI_Table_Type ======= TYPE, PUBLIC :: AFI_Table_Type REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Alpha !< Angle-of-attack vector that matches the Coefs matrix [rad] @@ -96,6 +149,7 @@ MODULE AirfoilInfo_Types INTEGER(IntKi) :: InCol_Cd !< The column of the coefficient tables that holds the minimum pressure coefficient [-] INTEGER(IntKi) :: InCol_Cm !< The column of the coefficient tables that holds the pitching-moment coefficient [-] INTEGER(IntKi) :: InCol_Cpmin !< The column of the coefficient tables that holds the minimum pressure coefficient [-] + LOGICAL :: UA_f_cn !< Whether any UA separation functions should be calculated on cn (true) or cl (false) [-] END TYPE AFI_InitInputType ! ======================= ! ========= AFI_InitOutputType ======= @@ -109,7 +163,7 @@ MODULE AirfoilInfo_Types INTEGER(IntKi) :: ColCl !< The column in the p%Coefs arrays that contains Cl data [-] INTEGER(IntKi) :: ColCm !< The column in the p%Coefs arrays that contains Cm data [-] INTEGER(IntKi) :: ColCpmin !< The column in the p%Coefs arrays that contains Cpmin data [-] - INTEGER(IntKi) :: ColUAf !< The column in the p%Coefs arrays that contains f_st data for UA [-] + INTEGER(IntKi) :: ColUAf !< The column in the p%Coefs arrays that contains f_st data (on cl or cn) for UA [-] INTEGER(IntKi) :: AFTabMod !< Interpolation method for multiple airfoil tables {1 = 1D on AoA (only first table is used); 2 = 2D on AoA and Re; 3 = 2D on AoA and UserProp} [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: secondVals !< The values of the 2nd dependent variable when using multiple airfoil tables (Re or UserProp, saved in an array so that the logic in the interpolation scheme is cleaner) [-] INTEGER(IntKi) :: InterpOrd !< Interpolation order [-] @@ -138,8 +192,9 @@ MODULE AirfoilInfo_Types REAL(ReKi) :: Cpmin = 0. !< Dimensionless coefficient of minimum pressure [-] REAL(ReKi) :: Cd0 = 0. !< Minimum Cd value (used for Beddoes-Leishman unsteady aero) [-] REAL(ReKi) :: Cm0 = 0. !< 2D pitching moment coefficient at zero lift, positive if nose is up [-] - REAL(ReKi) :: f_st = 0. !< separation function (used for UA HGM model) [-] - REAL(ReKi) :: cl_fs = 0. !< fully separated polar function (used for UA HGM model) [-] + REAL(ReKi) :: f_st = 0. !< separation function based on cl or cn (used for UA models) [-] + REAL(ReKi) :: FullySeparate = 0. !< fully separated cn or cl polar function (used for UA models) [-] + REAL(ReKi) :: FullyAttached = 0. !< fully attached cn or cl polar function (used for UA models) [-] END TYPE AFI_OutputType ! ======================= CONTAINS @@ -192,8 +247,21 @@ SUBROUTINE AFI_CopyUA_BL_Type( SrcUA_BL_TypeData, DstUA_BL_TypeData, CtrlCode, E DstUA_BL_TypeData%k1_hat = SrcUA_BL_TypeData%k1_hat DstUA_BL_TypeData%x_cp_bar = SrcUA_BL_TypeData%x_cp_bar DstUA_BL_TypeData%UACutout = SrcUA_BL_TypeData%UACutout + DstUA_BL_TypeData%UACutout_delta = SrcUA_BL_TypeData%UACutout_delta DstUA_BL_TypeData%UACutout_blend = SrcUA_BL_TypeData%UACutout_blend DstUA_BL_TypeData%filtCutOff = SrcUA_BL_TypeData%filtCutOff + DstUA_BL_TypeData%alphaUpper = SrcUA_BL_TypeData%alphaUpper + DstUA_BL_TypeData%alphaLower = SrcUA_BL_TypeData%alphaLower + DstUA_BL_TypeData%c_Rate = SrcUA_BL_TypeData%c_Rate + DstUA_BL_TypeData%c_RateUpper = SrcUA_BL_TypeData%c_RateUpper + DstUA_BL_TypeData%c_RateLower = SrcUA_BL_TypeData%c_RateLower + DstUA_BL_TypeData%c_alphaLower = SrcUA_BL_TypeData%c_alphaLower + DstUA_BL_TypeData%c_alphaUpper = SrcUA_BL_TypeData%c_alphaUpper + DstUA_BL_TypeData%alphaUpperWrap = SrcUA_BL_TypeData%alphaUpperWrap + DstUA_BL_TypeData%alphaLowerWrap = SrcUA_BL_TypeData%alphaLowerWrap + DstUA_BL_TypeData%c_RateWrap = SrcUA_BL_TypeData%c_RateWrap + DstUA_BL_TypeData%c_alphaLowerWrap = SrcUA_BL_TypeData%c_alphaLowerWrap + DstUA_BL_TypeData%c_alphaUpperWrap = SrcUA_BL_TypeData%c_alphaUpperWrap END SUBROUTINE AFI_CopyUA_BL_Type SUBROUTINE AFI_DestroyUA_BL_Type( UA_BL_TypeData, ErrStat, ErrMsg ) @@ -274,8 +342,21 @@ SUBROUTINE AFI_PackUA_BL_Type( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Re_BufSz = Re_BufSz + 1 ! k1_hat Re_BufSz = Re_BufSz + 1 ! x_cp_bar Re_BufSz = Re_BufSz + 1 ! UACutout + Re_BufSz = Re_BufSz + 1 ! UACutout_delta Re_BufSz = Re_BufSz + 1 ! UACutout_blend Re_BufSz = Re_BufSz + 1 ! filtCutOff + Re_BufSz = Re_BufSz + 1 ! alphaUpper + Re_BufSz = Re_BufSz + 1 ! alphaLower + Re_BufSz = Re_BufSz + 1 ! c_Rate + Re_BufSz = Re_BufSz + 1 ! c_RateUpper + Re_BufSz = Re_BufSz + 1 ! c_RateLower + Re_BufSz = Re_BufSz + 1 ! c_alphaLower + Re_BufSz = Re_BufSz + 1 ! c_alphaUpper + Re_BufSz = Re_BufSz + 1 ! alphaUpperWrap + Re_BufSz = Re_BufSz + 1 ! alphaLowerWrap + Re_BufSz = Re_BufSz + 1 ! c_RateWrap + Re_BufSz = Re_BufSz + 1 ! c_alphaLowerWrap + Re_BufSz = Re_BufSz + 1 ! c_alphaUpperWrap IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -367,10 +448,36 @@ SUBROUTINE AFI_PackUA_BL_Type( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Re_Xferred = Re_Xferred + 1 ReKiBuf(Re_Xferred) = InData%UACutout Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%UACutout_delta + Re_Xferred = Re_Xferred + 1 ReKiBuf(Re_Xferred) = InData%UACutout_blend Re_Xferred = Re_Xferred + 1 ReKiBuf(Re_Xferred) = InData%filtCutOff Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%alphaUpper + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%alphaLower + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%c_Rate + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%c_RateUpper + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%c_RateLower + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%c_alphaLower + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%c_alphaUpper + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%alphaUpperWrap + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%alphaLowerWrap + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%c_RateWrap + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%c_alphaLowerWrap + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%c_alphaUpperWrap + Re_Xferred = Re_Xferred + 1 END SUBROUTINE AFI_PackUA_BL_Type SUBROUTINE AFI_UnPackUA_BL_Type( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -466,12 +573,373 @@ SUBROUTINE AFI_UnPackUA_BL_Type( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E Re_Xferred = Re_Xferred + 1 OutData%UACutout = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 + OutData%UACutout_delta = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 OutData%UACutout_blend = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 OutData%filtCutOff = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 + OutData%alphaUpper = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%alphaLower = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%c_Rate = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%c_RateUpper = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%c_RateLower = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%c_alphaLower = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%c_alphaUpper = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%alphaUpperWrap = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%alphaLowerWrap = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%c_RateWrap = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%c_alphaLowerWrap = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%c_alphaUpperWrap = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 END SUBROUTINE AFI_UnPackUA_BL_Type + SUBROUTINE AFI_CopyUA_BL_Default_Type( SrcUA_BL_Default_TypeData, DstUA_BL_Default_TypeData, CtrlCode, ErrStat, ErrMsg ) + TYPE(AFI_UA_BL_Default_Type), INTENT(IN) :: SrcUA_BL_Default_TypeData + TYPE(AFI_UA_BL_Default_Type), INTENT(INOUT) :: DstUA_BL_Default_TypeData + 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 = 'AFI_CopyUA_BL_Default_Type' +! + ErrStat = ErrID_None + ErrMsg = "" + DstUA_BL_Default_TypeData%alpha0 = SrcUA_BL_Default_TypeData%alpha0 + DstUA_BL_Default_TypeData%alpha1 = SrcUA_BL_Default_TypeData%alpha1 + DstUA_BL_Default_TypeData%alpha2 = SrcUA_BL_Default_TypeData%alpha2 + DstUA_BL_Default_TypeData%eta_e = SrcUA_BL_Default_TypeData%eta_e + DstUA_BL_Default_TypeData%C_nalpha = SrcUA_BL_Default_TypeData%C_nalpha + DstUA_BL_Default_TypeData%C_lalpha = SrcUA_BL_Default_TypeData%C_lalpha + DstUA_BL_Default_TypeData%T_f0 = SrcUA_BL_Default_TypeData%T_f0 + DstUA_BL_Default_TypeData%T_V0 = SrcUA_BL_Default_TypeData%T_V0 + DstUA_BL_Default_TypeData%T_p = SrcUA_BL_Default_TypeData%T_p + DstUA_BL_Default_TypeData%T_VL = SrcUA_BL_Default_TypeData%T_VL + DstUA_BL_Default_TypeData%b1 = SrcUA_BL_Default_TypeData%b1 + DstUA_BL_Default_TypeData%b2 = SrcUA_BL_Default_TypeData%b2 + DstUA_BL_Default_TypeData%b5 = SrcUA_BL_Default_TypeData%b5 + DstUA_BL_Default_TypeData%A1 = SrcUA_BL_Default_TypeData%A1 + DstUA_BL_Default_TypeData%A2 = SrcUA_BL_Default_TypeData%A2 + DstUA_BL_Default_TypeData%A5 = SrcUA_BL_Default_TypeData%A5 + DstUA_BL_Default_TypeData%S1 = SrcUA_BL_Default_TypeData%S1 + DstUA_BL_Default_TypeData%S2 = SrcUA_BL_Default_TypeData%S2 + DstUA_BL_Default_TypeData%S3 = SrcUA_BL_Default_TypeData%S3 + DstUA_BL_Default_TypeData%S4 = SrcUA_BL_Default_TypeData%S4 + DstUA_BL_Default_TypeData%Cn1 = SrcUA_BL_Default_TypeData%Cn1 + DstUA_BL_Default_TypeData%Cn2 = SrcUA_BL_Default_TypeData%Cn2 + DstUA_BL_Default_TypeData%St_sh = SrcUA_BL_Default_TypeData%St_sh + DstUA_BL_Default_TypeData%Cd0 = SrcUA_BL_Default_TypeData%Cd0 + DstUA_BL_Default_TypeData%Cm0 = SrcUA_BL_Default_TypeData%Cm0 + DstUA_BL_Default_TypeData%k0 = SrcUA_BL_Default_TypeData%k0 + DstUA_BL_Default_TypeData%k1 = SrcUA_BL_Default_TypeData%k1 + DstUA_BL_Default_TypeData%k2 = SrcUA_BL_Default_TypeData%k2 + DstUA_BL_Default_TypeData%k3 = SrcUA_BL_Default_TypeData%k3 + DstUA_BL_Default_TypeData%k1_hat = SrcUA_BL_Default_TypeData%k1_hat + DstUA_BL_Default_TypeData%x_cp_bar = SrcUA_BL_Default_TypeData%x_cp_bar + DstUA_BL_Default_TypeData%UACutout = SrcUA_BL_Default_TypeData%UACutout + DstUA_BL_Default_TypeData%UACutout_delta = SrcUA_BL_Default_TypeData%UACutout_delta + DstUA_BL_Default_TypeData%filtCutOff = SrcUA_BL_Default_TypeData%filtCutOff + DstUA_BL_Default_TypeData%alphaUpper = SrcUA_BL_Default_TypeData%alphaUpper + DstUA_BL_Default_TypeData%alphaLower = SrcUA_BL_Default_TypeData%alphaLower + END SUBROUTINE AFI_CopyUA_BL_Default_Type + + SUBROUTINE AFI_DestroyUA_BL_Default_Type( UA_BL_Default_TypeData, ErrStat, ErrMsg ) + TYPE(AFI_UA_BL_Default_Type), INTENT(INOUT) :: UA_BL_Default_TypeData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + CHARACTER(*), PARAMETER :: RoutineName = 'AFI_DestroyUA_BL_Default_Type' + INTEGER(IntKi) :: i, i1, i2, i3, i4, i5 +! + ErrStat = ErrID_None + ErrMsg = "" + END SUBROUTINE AFI_DestroyUA_BL_Default_Type + + SUBROUTINE AFI_PackUA_BL_Default_Type( 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(AFI_UA_BL_Default_Type), 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 = 'AFI_PackUA_BL_Default_Type' + ! 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 ! alpha0 + Int_BufSz = Int_BufSz + 1 ! alpha1 + Int_BufSz = Int_BufSz + 1 ! alpha2 + Int_BufSz = Int_BufSz + 1 ! eta_e + Int_BufSz = Int_BufSz + 1 ! C_nalpha + Int_BufSz = Int_BufSz + 1 ! C_lalpha + Int_BufSz = Int_BufSz + 1 ! T_f0 + Int_BufSz = Int_BufSz + 1 ! T_V0 + Int_BufSz = Int_BufSz + 1 ! T_p + Int_BufSz = Int_BufSz + 1 ! T_VL + Int_BufSz = Int_BufSz + 1 ! b1 + Int_BufSz = Int_BufSz + 1 ! b2 + Int_BufSz = Int_BufSz + 1 ! b5 + Int_BufSz = Int_BufSz + 1 ! A1 + Int_BufSz = Int_BufSz + 1 ! A2 + Int_BufSz = Int_BufSz + 1 ! A5 + Int_BufSz = Int_BufSz + 1 ! S1 + Int_BufSz = Int_BufSz + 1 ! S2 + Int_BufSz = Int_BufSz + 1 ! S3 + Int_BufSz = Int_BufSz + 1 ! S4 + Int_BufSz = Int_BufSz + 1 ! Cn1 + Int_BufSz = Int_BufSz + 1 ! Cn2 + Int_BufSz = Int_BufSz + 1 ! St_sh + Int_BufSz = Int_BufSz + 1 ! Cd0 + Int_BufSz = Int_BufSz + 1 ! Cm0 + Int_BufSz = Int_BufSz + 1 ! k0 + Int_BufSz = Int_BufSz + 1 ! k1 + Int_BufSz = Int_BufSz + 1 ! k2 + Int_BufSz = Int_BufSz + 1 ! k3 + Int_BufSz = Int_BufSz + 1 ! k1_hat + Int_BufSz = Int_BufSz + 1 ! x_cp_bar + Int_BufSz = Int_BufSz + 1 ! UACutout + Int_BufSz = Int_BufSz + 1 ! UACutout_delta + Int_BufSz = Int_BufSz + 1 ! filtCutOff + Int_BufSz = Int_BufSz + 1 ! alphaUpper + Int_BufSz = Int_BufSz + 1 ! alphaLower + IF ( Re_BufSz .GT. 0 ) THEN + ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating ReKiBuf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + IF ( Db_BufSz .GT. 0 ) THEN + ALLOCATE( DbKiBuf( Db_BufSz ), STAT=ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DbKiBuf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + IF ( Int_BufSz .GT. 0 ) THEN + ALLOCATE( IntKiBuf( Int_BufSz ), STAT=ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating IntKiBuf.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + IF(OnlySize) RETURN ! return early if only trying to allocate buffers (not pack them) + + Re_Xferred = 1 + Db_Xferred = 1 + Int_Xferred = 1 + + IntKiBuf(Int_Xferred) = TRANSFER(InData%alpha0, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%alpha1, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%alpha2, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%eta_e, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%C_nalpha, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%C_lalpha, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%T_f0, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%T_V0, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%T_p, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%T_VL, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%b1, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%b2, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%b5, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%A1, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%A2, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%A5, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%S1, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%S2, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%S3, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%S4, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%Cn1, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%Cn2, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%St_sh, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%Cd0, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%Cm0, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%k0, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%k1, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%k2, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%k3, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%k1_hat, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%x_cp_bar, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%UACutout, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%UACutout_delta, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%filtCutOff, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%alphaUpper, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%alphaLower, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE AFI_PackUA_BL_Default_Type + + SUBROUTINE AFI_UnPackUA_BL_Default_Type( 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(AFI_UA_BL_Default_Type), INTENT(INOUT) :: OutData + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + ! Local variables + INTEGER(IntKi) :: Buf_size + INTEGER(IntKi) :: Re_Xferred + INTEGER(IntKi) :: Db_Xferred + INTEGER(IntKi) :: Int_Xferred + INTEGER(IntKi) :: i + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'AFI_UnPackUA_BL_Default_Type' + ! 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%alpha0 = TRANSFER(IntKiBuf(Int_Xferred), OutData%alpha0) + Int_Xferred = Int_Xferred + 1 + OutData%alpha1 = TRANSFER(IntKiBuf(Int_Xferred), OutData%alpha1) + Int_Xferred = Int_Xferred + 1 + OutData%alpha2 = TRANSFER(IntKiBuf(Int_Xferred), OutData%alpha2) + Int_Xferred = Int_Xferred + 1 + OutData%eta_e = TRANSFER(IntKiBuf(Int_Xferred), OutData%eta_e) + Int_Xferred = Int_Xferred + 1 + OutData%C_nalpha = TRANSFER(IntKiBuf(Int_Xferred), OutData%C_nalpha) + Int_Xferred = Int_Xferred + 1 + OutData%C_lalpha = TRANSFER(IntKiBuf(Int_Xferred), OutData%C_lalpha) + Int_Xferred = Int_Xferred + 1 + OutData%T_f0 = TRANSFER(IntKiBuf(Int_Xferred), OutData%T_f0) + Int_Xferred = Int_Xferred + 1 + OutData%T_V0 = TRANSFER(IntKiBuf(Int_Xferred), OutData%T_V0) + Int_Xferred = Int_Xferred + 1 + OutData%T_p = TRANSFER(IntKiBuf(Int_Xferred), OutData%T_p) + Int_Xferred = Int_Xferred + 1 + OutData%T_VL = TRANSFER(IntKiBuf(Int_Xferred), OutData%T_VL) + Int_Xferred = Int_Xferred + 1 + OutData%b1 = TRANSFER(IntKiBuf(Int_Xferred), OutData%b1) + Int_Xferred = Int_Xferred + 1 + OutData%b2 = TRANSFER(IntKiBuf(Int_Xferred), OutData%b2) + Int_Xferred = Int_Xferred + 1 + OutData%b5 = TRANSFER(IntKiBuf(Int_Xferred), OutData%b5) + Int_Xferred = Int_Xferred + 1 + OutData%A1 = TRANSFER(IntKiBuf(Int_Xferred), OutData%A1) + Int_Xferred = Int_Xferred + 1 + OutData%A2 = TRANSFER(IntKiBuf(Int_Xferred), OutData%A2) + Int_Xferred = Int_Xferred + 1 + OutData%A5 = TRANSFER(IntKiBuf(Int_Xferred), OutData%A5) + Int_Xferred = Int_Xferred + 1 + OutData%S1 = TRANSFER(IntKiBuf(Int_Xferred), OutData%S1) + Int_Xferred = Int_Xferred + 1 + OutData%S2 = TRANSFER(IntKiBuf(Int_Xferred), OutData%S2) + Int_Xferred = Int_Xferred + 1 + OutData%S3 = TRANSFER(IntKiBuf(Int_Xferred), OutData%S3) + Int_Xferred = Int_Xferred + 1 + OutData%S4 = TRANSFER(IntKiBuf(Int_Xferred), OutData%S4) + Int_Xferred = Int_Xferred + 1 + OutData%Cn1 = TRANSFER(IntKiBuf(Int_Xferred), OutData%Cn1) + Int_Xferred = Int_Xferred + 1 + OutData%Cn2 = TRANSFER(IntKiBuf(Int_Xferred), OutData%Cn2) + Int_Xferred = Int_Xferred + 1 + OutData%St_sh = TRANSFER(IntKiBuf(Int_Xferred), OutData%St_sh) + Int_Xferred = Int_Xferred + 1 + OutData%Cd0 = TRANSFER(IntKiBuf(Int_Xferred), OutData%Cd0) + Int_Xferred = Int_Xferred + 1 + OutData%Cm0 = TRANSFER(IntKiBuf(Int_Xferred), OutData%Cm0) + Int_Xferred = Int_Xferred + 1 + OutData%k0 = TRANSFER(IntKiBuf(Int_Xferred), OutData%k0) + Int_Xferred = Int_Xferred + 1 + OutData%k1 = TRANSFER(IntKiBuf(Int_Xferred), OutData%k1) + Int_Xferred = Int_Xferred + 1 + OutData%k2 = TRANSFER(IntKiBuf(Int_Xferred), OutData%k2) + Int_Xferred = Int_Xferred + 1 + OutData%k3 = TRANSFER(IntKiBuf(Int_Xferred), OutData%k3) + Int_Xferred = Int_Xferred + 1 + OutData%k1_hat = TRANSFER(IntKiBuf(Int_Xferred), OutData%k1_hat) + Int_Xferred = Int_Xferred + 1 + OutData%x_cp_bar = TRANSFER(IntKiBuf(Int_Xferred), OutData%x_cp_bar) + Int_Xferred = Int_Xferred + 1 + OutData%UACutout = TRANSFER(IntKiBuf(Int_Xferred), OutData%UACutout) + Int_Xferred = Int_Xferred + 1 + OutData%UACutout_delta = TRANSFER(IntKiBuf(Int_Xferred), OutData%UACutout_delta) + Int_Xferred = Int_Xferred + 1 + OutData%filtCutOff = TRANSFER(IntKiBuf(Int_Xferred), OutData%filtCutOff) + Int_Xferred = Int_Xferred + 1 + OutData%alphaUpper = TRANSFER(IntKiBuf(Int_Xferred), OutData%alphaUpper) + Int_Xferred = Int_Xferred + 1 + OutData%alphaLower = TRANSFER(IntKiBuf(Int_Xferred), OutData%alphaLower) + Int_Xferred = Int_Xferred + 1 + END SUBROUTINE AFI_UnPackUA_BL_Default_Type + SUBROUTINE AFI_CopyTable_Type( SrcTable_TypeData, DstTable_TypeData, CtrlCode, ErrStat, ErrMsg ) TYPE(AFI_Table_Type), INTENT(IN) :: SrcTable_TypeData TYPE(AFI_Table_Type), INTENT(INOUT) :: DstTable_TypeData @@ -933,6 +1401,7 @@ SUBROUTINE AFI_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS DstInitInputData%InCol_Cd = SrcInitInputData%InCol_Cd DstInitInputData%InCol_Cm = SrcInitInputData%InCol_Cm DstInitInputData%InCol_Cpmin = SrcInitInputData%InCol_Cpmin + DstInitInputData%UA_f_cn = SrcInitInputData%UA_f_cn END SUBROUTINE AFI_CopyInitInput SUBROUTINE AFI_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -988,6 +1457,7 @@ SUBROUTINE AFI_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! InCol_Cd Int_BufSz = Int_BufSz + 1 ! InCol_Cm Int_BufSz = Int_BufSz + 1 ! InCol_Cpmin + Int_BufSz = Int_BufSz + 1 ! UA_f_cn IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -1031,6 +1501,8 @@ SUBROUTINE AFI_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = InData%InCol_Cpmin Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%UA_f_cn, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE AFI_PackInitInput SUBROUTINE AFI_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -1075,6 +1547,8 @@ SUBROUTINE AFI_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%InCol_Cpmin = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 + OutData%UA_f_cn = TRANSFER(IntKiBuf(Int_Xferred), OutData%UA_f_cn) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE AFI_UnPackInitInput SUBROUTINE AFI_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -1947,7 +2421,8 @@ SUBROUTINE AFI_CopyOutput( SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrM DstOutputData%Cd0 = SrcOutputData%Cd0 DstOutputData%Cm0 = SrcOutputData%Cm0 DstOutputData%f_st = SrcOutputData%f_st - DstOutputData%cl_fs = SrcOutputData%cl_fs + DstOutputData%FullySeparate = SrcOutputData%FullySeparate + DstOutputData%FullyAttached = SrcOutputData%FullyAttached END SUBROUTINE AFI_CopyOutput SUBROUTINE AFI_DestroyOutput( OutputData, ErrStat, ErrMsg ) @@ -2003,7 +2478,8 @@ SUBROUTINE AFI_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Re_BufSz = Re_BufSz + 1 ! Cd0 Re_BufSz = Re_BufSz + 1 ! Cm0 Re_BufSz = Re_BufSz + 1 ! f_st - Re_BufSz = Re_BufSz + 1 ! cl_fs + Re_BufSz = Re_BufSz + 1 ! FullySeparate + Re_BufSz = Re_BufSz + 1 ! FullyAttached IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -2045,7 +2521,9 @@ SUBROUTINE AFI_PackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Re_Xferred = Re_Xferred + 1 ReKiBuf(Re_Xferred) = InData%f_st Re_Xferred = Re_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%cl_fs + ReKiBuf(Re_Xferred) = InData%FullySeparate + Re_Xferred = Re_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%FullyAttached Re_Xferred = Re_Xferred + 1 END SUBROUTINE AFI_PackOutput @@ -2089,7 +2567,9 @@ SUBROUTINE AFI_UnPackOutput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMs Re_Xferred = Re_Xferred + 1 OutData%f_st = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 - OutData%cl_fs = ReKiBuf(Re_Xferred) + OutData%FullySeparate = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + OutData%FullyAttached = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END SUBROUTINE AFI_UnPackOutput @@ -2200,8 +2680,10 @@ SUBROUTINE AFI_Output_ExtrapInterp1(y1, y2, tin, y_out, tin_out, ErrStat, ErrMsg y_out%Cm0 = y1%Cm0 + b * ScaleFactor b = -(y1%f_st - y2%f_st) y_out%f_st = y1%f_st + b * ScaleFactor - b = -(y1%cl_fs - y2%cl_fs) - y_out%cl_fs = y1%cl_fs + b * ScaleFactor + b = -(y1%FullySeparate - y2%FullySeparate) + y_out%FullySeparate = y1%FullySeparate + b * ScaleFactor + b = -(y1%FullyAttached - y2%FullyAttached) + y_out%FullyAttached = y1%FullyAttached + b * ScaleFactor END SUBROUTINE AFI_Output_ExtrapInterp1 @@ -2278,9 +2760,12 @@ SUBROUTINE AFI_Output_ExtrapInterp2(y1, y2, y3, tin, y_out, tin_out, ErrStat, Er b = (t(3)**2*(y1%f_st - y2%f_st) + t(2)**2*(-y1%f_st + y3%f_st))* scaleFactor c = ( (t(2)-t(3))*y1%f_st + t(3)*y2%f_st - t(2)*y3%f_st ) * scaleFactor y_out%f_st = y1%f_st + b + c * t_out - b = (t(3)**2*(y1%cl_fs - y2%cl_fs) + t(2)**2*(-y1%cl_fs + y3%cl_fs))* scaleFactor - c = ( (t(2)-t(3))*y1%cl_fs + t(3)*y2%cl_fs - t(2)*y3%cl_fs ) * scaleFactor - y_out%cl_fs = y1%cl_fs + b + c * t_out + b = (t(3)**2*(y1%FullySeparate - y2%FullySeparate) + t(2)**2*(-y1%FullySeparate + y3%FullySeparate))* scaleFactor + c = ( (t(2)-t(3))*y1%FullySeparate + t(3)*y2%FullySeparate - t(2)*y3%FullySeparate ) * scaleFactor + y_out%FullySeparate = y1%FullySeparate + b + c * t_out + b = (t(3)**2*(y1%FullyAttached - y2%FullyAttached) + t(2)**2*(-y1%FullyAttached + y3%FullyAttached))* scaleFactor + c = ( (t(2)-t(3))*y1%FullyAttached + t(3)*y2%FullyAttached - t(2)*y3%FullyAttached ) * scaleFactor + y_out%FullyAttached = y1%FullyAttached + b + c * t_out END SUBROUTINE AFI_Output_ExtrapInterp2 @@ -2437,10 +2922,32 @@ SUBROUTINE AFI_UA_BL_Type_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, Er u_out%x_cp_bar = u1%x_cp_bar + b * ScaleFactor b = -(u1%UACutout - u2%UACutout) u_out%UACutout = u1%UACutout + b * ScaleFactor + b = -(u1%UACutout_delta - u2%UACutout_delta) + u_out%UACutout_delta = u1%UACutout_delta + b * ScaleFactor b = -(u1%UACutout_blend - u2%UACutout_blend) u_out%UACutout_blend = u1%UACutout_blend + b * ScaleFactor b = -(u1%filtCutOff - u2%filtCutOff) u_out%filtCutOff = u1%filtCutOff + b * ScaleFactor + CALL Angles_ExtrapInterp( u1%alphaUpper, u2%alphaUpper, tin, u_out%alphaUpper, tin_out ) + CALL Angles_ExtrapInterp( u1%alphaLower, u2%alphaLower, tin, u_out%alphaLower, tin_out ) + b = -(u1%c_Rate - u2%c_Rate) + u_out%c_Rate = u1%c_Rate + b * ScaleFactor + b = -(u1%c_RateUpper - u2%c_RateUpper) + u_out%c_RateUpper = u1%c_RateUpper + b * ScaleFactor + b = -(u1%c_RateLower - u2%c_RateLower) + u_out%c_RateLower = u1%c_RateLower + b * ScaleFactor + b = -(u1%c_alphaLower - u2%c_alphaLower) + u_out%c_alphaLower = u1%c_alphaLower + b * ScaleFactor + b = -(u1%c_alphaUpper - u2%c_alphaUpper) + u_out%c_alphaUpper = u1%c_alphaUpper + b * ScaleFactor + CALL Angles_ExtrapInterp( u1%alphaUpperWrap, u2%alphaUpperWrap, tin, u_out%alphaUpperWrap, tin_out ) + CALL Angles_ExtrapInterp( u1%alphaLowerWrap, u2%alphaLowerWrap, tin, u_out%alphaLowerWrap, tin_out ) + b = -(u1%c_RateWrap - u2%c_RateWrap) + u_out%c_RateWrap = u1%c_RateWrap + b * ScaleFactor + b = -(u1%c_alphaLowerWrap - u2%c_alphaLowerWrap) + u_out%c_alphaLowerWrap = u1%c_alphaLowerWrap + b * ScaleFactor + b = -(u1%c_alphaUpperWrap - u2%c_alphaUpperWrap) + u_out%c_alphaUpperWrap = u1%c_alphaUpperWrap + b * ScaleFactor END SUBROUTINE AFI_UA_BL_Type_ExtrapInterp1 @@ -2586,12 +3093,43 @@ SUBROUTINE AFI_UA_BL_Type_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat b = (t(3)**2*(u1%UACutout - u2%UACutout) + t(2)**2*(-u1%UACutout + u3%UACutout))* scaleFactor c = ( (t(2)-t(3))*u1%UACutout + t(3)*u2%UACutout - t(2)*u3%UACutout ) * scaleFactor u_out%UACutout = u1%UACutout + b + c * t_out + b = (t(3)**2*(u1%UACutout_delta - u2%UACutout_delta) + t(2)**2*(-u1%UACutout_delta + u3%UACutout_delta))* scaleFactor + c = ( (t(2)-t(3))*u1%UACutout_delta + t(3)*u2%UACutout_delta - t(2)*u3%UACutout_delta ) * scaleFactor + u_out%UACutout_delta = u1%UACutout_delta + b + c * t_out b = (t(3)**2*(u1%UACutout_blend - u2%UACutout_blend) + t(2)**2*(-u1%UACutout_blend + u3%UACutout_blend))* scaleFactor c = ( (t(2)-t(3))*u1%UACutout_blend + t(3)*u2%UACutout_blend - t(2)*u3%UACutout_blend ) * scaleFactor u_out%UACutout_blend = u1%UACutout_blend + b + c * t_out b = (t(3)**2*(u1%filtCutOff - u2%filtCutOff) + t(2)**2*(-u1%filtCutOff + u3%filtCutOff))* scaleFactor c = ( (t(2)-t(3))*u1%filtCutOff + t(3)*u2%filtCutOff - t(2)*u3%filtCutOff ) * scaleFactor u_out%filtCutOff = u1%filtCutOff + b + c * t_out + CALL Angles_ExtrapInterp( u1%alphaUpper, u2%alphaUpper, u3%alphaUpper, tin, u_out%alphaUpper, tin_out ) + CALL Angles_ExtrapInterp( u1%alphaLower, u2%alphaLower, u3%alphaLower, tin, u_out%alphaLower, tin_out ) + b = (t(3)**2*(u1%c_Rate - u2%c_Rate) + t(2)**2*(-u1%c_Rate + u3%c_Rate))* scaleFactor + c = ( (t(2)-t(3))*u1%c_Rate + t(3)*u2%c_Rate - t(2)*u3%c_Rate ) * scaleFactor + u_out%c_Rate = u1%c_Rate + b + c * t_out + b = (t(3)**2*(u1%c_RateUpper - u2%c_RateUpper) + t(2)**2*(-u1%c_RateUpper + u3%c_RateUpper))* scaleFactor + c = ( (t(2)-t(3))*u1%c_RateUpper + t(3)*u2%c_RateUpper - t(2)*u3%c_RateUpper ) * scaleFactor + u_out%c_RateUpper = u1%c_RateUpper + b + c * t_out + b = (t(3)**2*(u1%c_RateLower - u2%c_RateLower) + t(2)**2*(-u1%c_RateLower + u3%c_RateLower))* scaleFactor + c = ( (t(2)-t(3))*u1%c_RateLower + t(3)*u2%c_RateLower - t(2)*u3%c_RateLower ) * scaleFactor + u_out%c_RateLower = u1%c_RateLower + b + c * t_out + b = (t(3)**2*(u1%c_alphaLower - u2%c_alphaLower) + t(2)**2*(-u1%c_alphaLower + u3%c_alphaLower))* scaleFactor + c = ( (t(2)-t(3))*u1%c_alphaLower + t(3)*u2%c_alphaLower - t(2)*u3%c_alphaLower ) * scaleFactor + u_out%c_alphaLower = u1%c_alphaLower + b + c * t_out + b = (t(3)**2*(u1%c_alphaUpper - u2%c_alphaUpper) + t(2)**2*(-u1%c_alphaUpper + u3%c_alphaUpper))* scaleFactor + c = ( (t(2)-t(3))*u1%c_alphaUpper + t(3)*u2%c_alphaUpper - t(2)*u3%c_alphaUpper ) * scaleFactor + u_out%c_alphaUpper = u1%c_alphaUpper + b + c * t_out + CALL Angles_ExtrapInterp( u1%alphaUpperWrap, u2%alphaUpperWrap, u3%alphaUpperWrap, tin, u_out%alphaUpperWrap, tin_out ) + CALL Angles_ExtrapInterp( u1%alphaLowerWrap, u2%alphaLowerWrap, u3%alphaLowerWrap, tin, u_out%alphaLowerWrap, tin_out ) + b = (t(3)**2*(u1%c_RateWrap - u2%c_RateWrap) + t(2)**2*(-u1%c_RateWrap + u3%c_RateWrap))* scaleFactor + c = ( (t(2)-t(3))*u1%c_RateWrap + t(3)*u2%c_RateWrap - t(2)*u3%c_RateWrap ) * scaleFactor + u_out%c_RateWrap = u1%c_RateWrap + b + c * t_out + b = (t(3)**2*(u1%c_alphaLowerWrap - u2%c_alphaLowerWrap) + t(2)**2*(-u1%c_alphaLowerWrap + u3%c_alphaLowerWrap))* scaleFactor + c = ( (t(2)-t(3))*u1%c_alphaLowerWrap + t(3)*u2%c_alphaLowerWrap - t(2)*u3%c_alphaLowerWrap ) * scaleFactor + u_out%c_alphaLowerWrap = u1%c_alphaLowerWrap + b + c * t_out + b = (t(3)**2*(u1%c_alphaUpperWrap - u2%c_alphaUpperWrap) + t(2)**2*(-u1%c_alphaUpperWrap + u3%c_alphaUpperWrap))* scaleFactor + c = ( (t(2)-t(3))*u1%c_alphaUpperWrap + t(3)*u2%c_alphaUpperWrap - t(2)*u3%c_alphaUpperWrap ) * scaleFactor + u_out%c_alphaUpperWrap = u1%c_alphaUpperWrap + b + c * t_out END SUBROUTINE AFI_UA_BL_Type_ExtrapInterp2 END MODULE AirfoilInfo_Types diff --git a/modules/aerodyn/src/BEMT.f90 b/modules/aerodyn/src/BEMT.f90 index fda132b40..3ad8fabc5 100644 --- a/modules/aerodyn/src/BEMT.f90 +++ b/modules/aerodyn/src/BEMT.f90 @@ -94,7 +94,7 @@ subroutine BEMT_Set_UA_InitData( InitInp, interval, Init_UA_Data, errStat, errMs ! This routine is called from BEMT_Init. ! The parameters are set here and not changed during the simulation. !.................................................................................................................................. - type(BEMT_InitInputType), intent(in ) :: InitInp ! Input data for initialization routine + type(BEMT_InitInputType), intent(inout) :: InitInp ! Input data for initialization routine real(DbKi), intent(in ) :: interval ! time interval type(UA_InitInputType), intent( out) :: Init_UA_Data ! Parameters integer(IntKi), intent( out) :: errStat ! Error status of the operation @@ -121,10 +121,11 @@ subroutine BEMT_Set_UA_InitData( InitInp, interval, Init_UA_Data, errStat, errMs end do end do - ! TODO:: Fully implement these initialization inputs + call move_alloc(InitInp%UAOff_innerNode, Init_UA_Data%UAOff_innerNode) + call move_alloc(InitInp%UAOff_outerNode, Init_UA_Data%UAOff_outerNode) Init_UA_Data%dt = interval - Init_UA_Data%OutRootName = '' + Init_UA_Data%OutRootName = InitInp%RootName ! was 'Debug.UA' Init_UA_Data%numBlades = InitInp%numBlades Init_UA_Data%nNodesPerBlade = InitInp%numBladeNodes @@ -133,6 +134,7 @@ subroutine BEMT_Set_UA_InitData( InitInp, interval, Init_UA_Data, errStat, errMs Init_UA_Data%Flookup = InitInp%Flookup Init_UA_Data%a_s = InitInp%a_s ! m/s Init_UA_Data%ShedEffect = .true. ! This should be true when coupled to BEM + Init_UA_Data%WrSum = InitInp%SumPrint end subroutine BEMT_Set_UA_InitData @@ -1246,7 +1248,7 @@ subroutine BEMT_CalcOutput( t, u, p, x, xd, z, OtherState, AFInfo, y, m, errStat do j = 1,p%numBlades ! Loop through all blades do i = 1,p%numBladeNodes ! Loop through the blade nodes / elements - call UA_CalcOutput(i, j, m%u_UA(i,j,InputIndex), p%UA, x%UA, xd%UA, OtherState%UA, AFInfo(p%AFindx(i,j)), m%y_UA, m%UA, errStat2, errMsg2 ) + call UA_CalcOutput(i, j, t, m%u_UA(i,j,InputIndex), p%UA, x%UA, xd%UA, OtherState%UA, AFInfo(p%AFindx(i,j)), m%y_UA, m%UA, errStat2, errMsg2 ) if (ErrStat2 /= ErrID_None) then call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//trim(NodeText(i,j))) if (errStat >= AbortErrLev) return diff --git a/modules/aerodyn/src/BEMT_Registry.txt b/modules/aerodyn/src/BEMT_Registry.txt index db8b8ec98..73b2d2cd4 100644 --- a/modules/aerodyn/src/BEMT_Registry.txt +++ b/modules/aerodyn/src/BEMT_Registry.txt @@ -54,6 +54,10 @@ typedef ^ ^ ReKi typedef ^ ^ IntKi DBEMT_Mod - - - "DBEMT model. 1 = constant tau1, 2 = time dependent tau1" - typedef ^ ^ ReKi tau1_const - - - "DBEMT time constant (when DBEMT_Mod=1)" s typedef ^ ^ ReKi yawCorrFactor - - - "constant used in Pitt/Peters skewed wake model (default is 15*pi/32)" - +typedef ^ ^ INTEGER UAOff_innerNode {:} - - "Last node on each blade where UA should be turned off based on span location from blade root (0 if always on)" - +typedef ^ ^ INTEGER UAOff_outerNode {:} - - "First node on each blade where UA should be turned off based on span location from blade tip (>nNodesPerBlade if always on)" - +typedef ^ ^ CHARACTER(1024) RootName - - - "RootName for writing output files" - +typedef ^ ^ LOGICAL SumPrint - - - "logical flag indicating whether to use UnsteadyAero" - # # # Define outputs from the initialization routine here: diff --git a/modules/aerodyn/src/BEMT_Types.f90 b/modules/aerodyn/src/BEMT_Types.f90 index 88e7648f3..aa2439f9f 100644 --- a/modules/aerodyn/src/BEMT_Types.f90 +++ b/modules/aerodyn/src/BEMT_Types.f90 @@ -65,6 +65,10 @@ MODULE BEMT_Types INTEGER(IntKi) :: DBEMT_Mod !< DBEMT model. 1 = constant tau1, 2 = time dependent tau1 [-] REAL(ReKi) :: tau1_const !< DBEMT time constant (when DBEMT_Mod=1) [s] REAL(ReKi) :: yawCorrFactor !< constant used in Pitt/Peters skewed wake model (default is 15*pi/32) [-] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: UAOff_innerNode !< Last node on each blade where UA should be turned off based on span location from blade root (0 if always on) [-] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: UAOff_outerNode !< First node on each blade where UA should be turned off based on span location from blade tip (>nNodesPerBlade if always on) [-] + CHARACTER(1024) :: RootName !< RootName for writing output files [-] + LOGICAL :: SumPrint !< logical flag indicating whether to use UnsteadyAero [-] END TYPE BEMT_InitInputType ! ======================= ! ========= BEMT_InitOutputType ======= @@ -301,6 +305,32 @@ SUBROUTINE BEMT_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, Err DstInitInputData%DBEMT_Mod = SrcInitInputData%DBEMT_Mod DstInitInputData%tau1_const = SrcInitInputData%tau1_const DstInitInputData%yawCorrFactor = SrcInitInputData%yawCorrFactor +IF (ALLOCATED(SrcInitInputData%UAOff_innerNode)) THEN + i1_l = LBOUND(SrcInitInputData%UAOff_innerNode,1) + i1_u = UBOUND(SrcInitInputData%UAOff_innerNode,1) + IF (.NOT. ALLOCATED(DstInitInputData%UAOff_innerNode)) THEN + ALLOCATE(DstInitInputData%UAOff_innerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%UAOff_innerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%UAOff_innerNode = SrcInitInputData%UAOff_innerNode +ENDIF +IF (ALLOCATED(SrcInitInputData%UAOff_outerNode)) THEN + i1_l = LBOUND(SrcInitInputData%UAOff_outerNode,1) + i1_u = UBOUND(SrcInitInputData%UAOff_outerNode,1) + IF (.NOT. ALLOCATED(DstInitInputData%UAOff_outerNode)) THEN + ALLOCATE(DstInitInputData%UAOff_outerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%UAOff_outerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%UAOff_outerNode = SrcInitInputData%UAOff_outerNode +ENDIF + DstInitInputData%RootName = SrcInitInputData%RootName + DstInitInputData%SumPrint = SrcInitInputData%SumPrint END SUBROUTINE BEMT_CopyInitInput SUBROUTINE BEMT_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -329,6 +359,12 @@ SUBROUTINE BEMT_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) ENDIF IF (ALLOCATED(InitInputData%rLocal)) THEN DEALLOCATE(InitInputData%rLocal) +ENDIF +IF (ALLOCATED(InitInputData%UAOff_innerNode)) THEN + DEALLOCATE(InitInputData%UAOff_innerNode) +ENDIF +IF (ALLOCATED(InitInputData%UAOff_outerNode)) THEN + DEALLOCATE(InitInputData%UAOff_outerNode) ENDIF END SUBROUTINE BEMT_DestroyInitInput @@ -418,6 +454,18 @@ SUBROUTINE BEMT_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Int_BufSz = Int_BufSz + 1 ! DBEMT_Mod Re_BufSz = Re_BufSz + 1 ! tau1_const Re_BufSz = Re_BufSz + 1 ! yawCorrFactor + Int_BufSz = Int_BufSz + 1 ! UAOff_innerNode allocated yes/no + IF ( ALLOCATED(InData%UAOff_innerNode) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! UAOff_innerNode upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%UAOff_innerNode) ! UAOff_innerNode + END IF + Int_BufSz = Int_BufSz + 1 ! UAOff_outerNode allocated yes/no + IF ( ALLOCATED(InData%UAOff_outerNode) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! UAOff_outerNode upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%UAOff_outerNode) ! UAOff_outerNode + END IF + Int_BufSz = Int_BufSz + 1*LEN(InData%RootName) ! RootName + Int_BufSz = Int_BufSz + 1 ! SumPrint IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -597,6 +645,42 @@ SUBROUTINE BEMT_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrM Re_Xferred = Re_Xferred + 1 ReKiBuf(Re_Xferred) = InData%yawCorrFactor Re_Xferred = Re_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%UAOff_innerNode) ) 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%UAOff_innerNode,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UAOff_innerNode,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%UAOff_innerNode,1), UBOUND(InData%UAOff_innerNode,1) + IntKiBuf(Int_Xferred) = InData%UAOff_innerNode(i1) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + IF ( .NOT. ALLOCATED(InData%UAOff_outerNode) ) 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%UAOff_outerNode,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UAOff_outerNode,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%UAOff_outerNode,1), UBOUND(InData%UAOff_outerNode,1) + IntKiBuf(Int_Xferred) = InData%UAOff_outerNode(i1) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + DO I = 1, LEN(InData%RootName) + IntKiBuf(Int_Xferred) = ICHAR(InData%RootName(I:I), IntKi) + Int_Xferred = Int_Xferred + 1 + END DO ! I + IntKiBuf(Int_Xferred) = TRANSFER(InData%SumPrint, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE BEMT_PackInitInput SUBROUTINE BEMT_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -798,6 +882,48 @@ SUBROUTINE BEMT_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, E Re_Xferred = Re_Xferred + 1 OutData%yawCorrFactor = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UAOff_innerNode 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%UAOff_innerNode)) DEALLOCATE(OutData%UAOff_innerNode) + ALLOCATE(OutData%UAOff_innerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UAOff_innerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%UAOff_innerNode,1), UBOUND(OutData%UAOff_innerNode,1) + OutData%UAOff_innerNode(i1) = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UAOff_outerNode 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%UAOff_outerNode)) DEALLOCATE(OutData%UAOff_outerNode) + ALLOCATE(OutData%UAOff_outerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UAOff_outerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%UAOff_outerNode,1), UBOUND(OutData%UAOff_outerNode,1) + OutData%UAOff_outerNode(i1) = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + DO I = 1, LEN(OutData%RootName) + OutData%RootName(I:I) = CHAR(IntKiBuf(Int_Xferred)) + Int_Xferred = Int_Xferred + 1 + END DO ! I + OutData%SumPrint = TRANSFER(IntKiBuf(Int_Xferred), OutData%SumPrint) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE BEMT_UnPackInitInput SUBROUTINE BEMT_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/DBEMT.f90 b/modules/aerodyn/src/DBEMT.f90 index e01d33462..4d3eae6a1 100644 --- a/modules/aerodyn/src/DBEMT.f90 +++ b/modules/aerodyn/src/DBEMT.f90 @@ -55,14 +55,6 @@ subroutine DBEMT_ValidateInitInp(interval, InitInp, errStat, errMsg) errMsg = "" if ( interval <= sqrt(epsilon(1.0_ReKi)) ) call SetErrStat( ErrID_Fatal, " The timestep size for DBEMT (interval) must be larger than sqrt(epsilon).", ErrStat, ErrMsg, RoutineName) - - !>>> remove after this feature gets tested better: - if (InitInp%DBEMT_Mod == DBEMT_cont_tauConst ) then - call SetErrStat( ErrID_Fatal, "DBEMT_Mod cannot be 3 in this version of OpenFAST.", ErrStat, ErrMsg, RoutineName ) - return - end if - !<<< - if ( (InitInp%DBEMT_Mod .ne. DBEMT_tauConst) .and. (InitInp%DBEMT_Mod .ne. DBEMT_tauVaries) .and. (InitInp%DBEMT_Mod .ne. DBEMT_cont_tauConst)) then call SetErrStat( ErrID_Fatal, " DBEMT_Mod must be set to 1, 2, or 3.", ErrStat, ErrMsg, RoutineName) end if diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 729382392..92f95ac5a 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -1024,6 +1024,7 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E 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 @@ -1031,13 +1032,18 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E end do end do Init_UA_Data%dt = interval - Init_UA_Data%OutRootName = 'Debug.UA' + Init_UA_Data%OutRootName = InitInp%RootName Init_UA_Data%numBlades = InitInp%NumBlades Init_UA_Data%nNodesPerBlade = InitInp%numBladeNodes + Init_UA_Data%UAMod = InitInp%UAMod Init_UA_Data%Flookup = InitInp%Flookup Init_UA_Data%a_s = InitInp%a_s ! m/s Init_UA_Data%ShedEffect = .False. ! Important, when coupling UA wih vortex code, shed vorticity is inherently accounted for + Init_UA_Data%WrSum = InitInp%SumPrint + call move_alloc(InitInp%UAOff_innerNode, Init_UA_Data%UAOff_innerNode) + call move_alloc(InitInp%UAOff_outerNode, Init_UA_Data%UAOff_outerNode) + ! --- UA init call UA_Init( Init_UA_Data, u_UA, m%p_UA, x%UA, xd%UA, OtherState%UA, m%y_UA, m%m_UA, interval, InitOutData_UA, ErrStat2, ErrMsg2); if(Failed())return diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 629b28bb7..8a8e3e59d 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -188,6 +188,9 @@ typedef ^ ^ IntKi 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 +typedef ^ ^ INTEGER UAOff_innerNode {:} - - "Last node on each blade where UA should be turned off based on span location from blade root (0 if always on)" - +typedef ^ ^ INTEGER UAOff_outerNode {:} - - "First node on each blade where UA should be turned off based on span location from blade tip (>nNodesPerBlade if always on)" - +typedef ^ ^ LOGICAL SumPrint - - - "Whether to print summary file (primarially in in UA)" - #.......... InputFileType ...... # FVW_InputFile diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 99f4966fd..989e4fa9c 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -207,6 +207,9 @@ MODULE FVW_Types 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] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: UAOff_innerNode !< Last node on each blade where UA should be turned off based on span location from blade root (0 if always on) [-] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: UAOff_outerNode !< First node on each blade where UA should be turned off based on span location from blade tip (>nNodesPerBlade if always on) [-] + LOGICAL :: SumPrint !< Whether to print summary file (primarially in in UA) [-] END TYPE FVW_InitInputType ! ======================= ! ========= FVW_InputFile ======= @@ -6793,6 +6796,31 @@ SUBROUTINE FVW_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrS DstInitInputData%UA_Flag = SrcInitInputData%UA_Flag DstInitInputData%Flookup = SrcInitInputData%Flookup DstInitInputData%a_s = SrcInitInputData%a_s +IF (ALLOCATED(SrcInitInputData%UAOff_innerNode)) THEN + i1_l = LBOUND(SrcInitInputData%UAOff_innerNode,1) + i1_u = UBOUND(SrcInitInputData%UAOff_innerNode,1) + IF (.NOT. ALLOCATED(DstInitInputData%UAOff_innerNode)) THEN + ALLOCATE(DstInitInputData%UAOff_innerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%UAOff_innerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%UAOff_innerNode = SrcInitInputData%UAOff_innerNode +ENDIF +IF (ALLOCATED(SrcInitInputData%UAOff_outerNode)) THEN + i1_l = LBOUND(SrcInitInputData%UAOff_outerNode,1) + i1_u = UBOUND(SrcInitInputData%UAOff_outerNode,1) + IF (.NOT. ALLOCATED(DstInitInputData%UAOff_outerNode)) THEN + ALLOCATE(DstInitInputData%UAOff_outerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%UAOff_outerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%UAOff_outerNode = SrcInitInputData%UAOff_outerNode +ENDIF + DstInitInputData%SumPrint = SrcInitInputData%SumPrint END SUBROUTINE FVW_CopyInitInput SUBROUTINE FVW_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -6830,6 +6858,12 @@ SUBROUTINE FVW_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) ENDIF IF (ALLOCATED(InitInputData%rLocal)) THEN DEALLOCATE(InitInputData%rLocal) +ENDIF +IF (ALLOCATED(InitInputData%UAOff_innerNode)) THEN + DEALLOCATE(InitInputData%UAOff_innerNode) +ENDIF +IF (ALLOCATED(InitInputData%UAOff_outerNode)) THEN + DEALLOCATE(InitInputData%UAOff_outerNode) ENDIF END SUBROUTINE FVW_DestroyInitInput @@ -6937,6 +6971,17 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 1 ! UA_Flag Int_BufSz = Int_BufSz + 1 ! Flookup Re_BufSz = Re_BufSz + 1 ! a_s + Int_BufSz = Int_BufSz + 1 ! UAOff_innerNode allocated yes/no + IF ( ALLOCATED(InData%UAOff_innerNode) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! UAOff_innerNode upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%UAOff_innerNode) ! UAOff_innerNode + END IF + Int_BufSz = Int_BufSz + 1 ! UAOff_outerNode allocated yes/no + IF ( ALLOCATED(InData%UAOff_outerNode) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! UAOff_outerNode upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%UAOff_outerNode) ! UAOff_outerNode + END IF + Int_BufSz = Int_BufSz + 1 ! SumPrint IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -7154,6 +7199,38 @@ SUBROUTINE FVW_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_Xferred = Int_Xferred + 1 ReKiBuf(Re_Xferred) = InData%a_s Re_Xferred = Re_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%UAOff_innerNode) ) 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%UAOff_innerNode,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UAOff_innerNode,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%UAOff_innerNode,1), UBOUND(InData%UAOff_innerNode,1) + IntKiBuf(Int_Xferred) = InData%UAOff_innerNode(i1) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + IF ( .NOT. ALLOCATED(InData%UAOff_outerNode) ) 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%UAOff_outerNode,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UAOff_outerNode,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%UAOff_outerNode,1), UBOUND(InData%UAOff_outerNode,1) + IntKiBuf(Int_Xferred) = InData%UAOff_outerNode(i1) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + IntKiBuf(Int_Xferred) = TRANSFER(InData%SumPrint, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackInitInput SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -7410,6 +7487,44 @@ SUBROUTINE FVW_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Int_Xferred = Int_Xferred + 1 OutData%a_s = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UAOff_innerNode 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%UAOff_innerNode)) DEALLOCATE(OutData%UAOff_innerNode) + ALLOCATE(OutData%UAOff_innerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UAOff_innerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%UAOff_innerNode,1), UBOUND(OutData%UAOff_innerNode,1) + OutData%UAOff_innerNode(i1) = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UAOff_outerNode 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%UAOff_outerNode)) DEALLOCATE(OutData%UAOff_outerNode) + ALLOCATE(OutData%UAOff_outerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UAOff_outerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%UAOff_outerNode,1), UBOUND(OutData%UAOff_outerNode,1) + OutData%UAOff_outerNode(i1) = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + OutData%SumPrint = TRANSFER(IntKiBuf(Int_Xferred), OutData%SumPrint) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackInitInput SUBROUTINE FVW_CopyInputFile( SrcInputFileData, DstInputFileData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/UA_Dvr_Subs.f90 b/modules/aerodyn/src/UA_Dvr_Subs.f90 index 50a90f329..b6bd92b44 100644 --- a/modules/aerodyn/src/UA_Dvr_Subs.f90 +++ b/modules/aerodyn/src/UA_Dvr_Subs.f90 @@ -556,7 +556,7 @@ subroutine Init_AFI(p, NumAFfiles, afNames, UseCm, AFI_Params, ErrStat, ErrMsg) AFI_InitInputs%InCol_Cpmin = 0 AFI_InitInputs%AFTabMod = AFITable_1 ! 1D-interpolation (on AoA only) - + AFI_InitInputs%UA_f_cn = p%UAMod /= UA_HGM ! HGM needs the separation function based on cl instead of cn do i=1,NumAFfiles AFI_InitInputs%FileName = afNames(i) !InitInp%AF_File(i) @@ -582,9 +582,6 @@ subroutine Cleanup() ! The routine cleans up data arrays framework structures ! !---------------------------------------------------------------------------------------------------- - - - !Clean up initialization inputs call AFI_DestroyInitInput(AFI_InitInputs, errStat2, errMsg2) @@ -604,6 +601,15 @@ subroutine WriteAFITables(AFI_Params,OutRootName) integer(IntKi) :: ErrStat character(ErrMsgLen) :: ErrMsg + Real(ReKi) :: cl_smooth(AFI_Params%Table(1)%NumAlf) + Real(ReKi) :: cn_smooth(AFI_Params%Table(1)%NumAlf) + Real(ReKi) :: cn(AFI_Params%Table(1)%NumAlf) + + cn = AFI_Params%Table(1)%Coefs(:,AFI_Params%ColCl) * cos(AFI_Params%Table(1)%alpha) + (AFI_Params%Table(1)%Coefs(:,AFI_Params%ColCd) - AFI_Params%Table(1)%UA_BL%Cd0) * sin(AFI_Params%Table(1)%alpha); + + call kernelSmoothing(AFI_Params%Table(1)%alpha, cn, kernelType_TRIWEIGHT, 2.0_ReKi*D2R, cn_smooth) + call kernelSmoothing(AFI_Params%Table(1)%alpha, AFI_Params%Table(1)%Coefs(:,AFI_Params%ColCl), kernelType_TRIWEIGHT, 2.0_ReKi*D2R, cl_smooth) + CALL GetNewUnit( unOutFile, ErrStat, ErrMsg ) IF ( ErrStat /= ErrID_None ) RETURN @@ -617,18 +623,16 @@ subroutine WriteAFITables(AFI_Params,OutRootName) WRITE (unOutFile,'(/,A/)') 'These predictions were generated by UnsteadyAero Driver on '//CurDate()//' at '//CurTime()//'.' WRITE (unOutFile,'(/,A/)') ' ' ! note that this header assumes we have Cm and unsteady aero coefficients - WRITE(unOutFile, '(20(A20,1x))') 'Alpha', 'Cl', 'Cd', 'Cm', 'f_st', 'cl_fs', 'f_st2', 'cn_fs' - WRITE(unOutFile, '(20(A20,1x))') '(deg)', '(-)', '(-)', '(-)', '(-)', '(-)', '(-)', '(-)' + WRITE(unOutFile, '(20(A20,1x))') 'Alpha', 'Cl', 'Cd', 'Cm', 'f_st', 'FullySeparate', 'FullyAttached', 'smoothed_Cl', 'smoothed_Cn' + WRITE(unOutFile, '(20(A20,1x))') '(deg)', '(-)', '(-)', '(-)', '(-)', '(-)', '(-)', '(-)', '(-)' do row=1,size(AFI_Params%Table(1)%Alpha) - WRITE(unOutFile, '(20(F20.6,1x))') AFI_Params%Table(1)%Alpha(row)*R2D, AFI_Params%Table(1)%Coefs(row,:) + WRITE(unOutFile, '(20(F20.6,1x))') AFI_Params%Table(1)%Alpha(row)*R2D, AFI_Params%Table(1)%Coefs(row,:), cl_smooth(Row), cn_smooth(Row) end do CLOSE(unOutFile) end subroutine WriteAFITables - - end module UA_Dvr_Subs \ No newline at end of file diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index 810035c1c..509c5b144 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -41,13 +41,7 @@ module UnsteadyAero public :: UA_ReInit public :: UA_InitStates_AllNodes ! used for AD linearization initialization - integer(intki), parameter :: UA_Baseline = 1 ! UAMod = 1 [Baseline model (Original)] - integer(intki), parameter :: UA_Gonzalez = 2 ! UAMod = 2 [Gonzalez's variant (changes in Cn,Cc,Cm)] - integer(intki), parameter :: UA_MinnemaPierce = 3 ! UAMod = 3 [Minnema/Pierce variant (changes in Cc and Cm)] - integer(intki), parameter, public :: UA_HGM = 4 ! UAMod = 4 [continuous variant of HGM (Hansen) model] - real(ReKi), parameter :: Gonzalez_factor = 0.2_ReKi ! this factor, proposed by Gonzalez (for "all" models) is used to modify Cc to account for negative values seen at f=0 (see Eqn 1.40) - real(ReKi), parameter, public :: UA_u_min = 0.01_ReKi ! m/s; used to provide a minimum value so UA equations don't blow up (this should be much lower than range where UA is turned off) contains @@ -151,10 +145,8 @@ subroutine Get_f_from_Lookup( UAMod, Re, UserProp, alpha_in, alpha0, C_nalpha_ci ! ensure that these angles are in appropriate ranges alpha = alpha_in - call MPi2Pi(alpha) - + call AddOrSub2Pi( alpha0, alpha ) alpha_minus_alpha0 = alpha - alpha0 - call MPi2Pi(alpha_minus_alpha0) call AFI_ComputeAirfoilCoefs( alpha, Re, UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg ) if (ErrStat >= AbortErrLev ) return @@ -268,7 +260,7 @@ real(ReKi) function Get_f_c_from_Lookup( UAMod, Re, UserProp, alpha_in, alpha0_i Cc = AFI_interp%Cl*sin(alpha) - (AFI_interp%Cd-AFI_interp%Cd0)*cos(alpha) - + call AddOrSub2Pi( alpha0, alpha ) if (UAMod == UA_Gonzalez) then denom = eta_e*c_nalpha_circ*( alpha-alpha0 )*(alpha) !NOTE: Added back (alpha) because idling cases with alpha 90-degrees show problems with tan(alpha), the code should match steady state if the formulation in the calculation of Cc is in agreement with this formulation else @@ -416,7 +408,6 @@ subroutine ComputeKelvinChain( i, j, u, p, xd, OtherState, misc, AFInfo, KC, BL_ KC%alpha_filt_cur = LowPassConst*alpha_filt_minus1 + (1.0_ReKi-LowPassConst)*u%alpha ! from eq 1.8 [1: typo in documentation, though] - KC%dalpha0 = KC%alpha_filt_cur - BL_p%alpha0 @@ -698,20 +689,26 @@ end subroutine ComputeKelvinChain !============================================================================== -subroutine UA_SetParameters( dt, InitInp, p, ErrStat, ErrMsg ) +subroutine UA_SetParameters( dt, InitInp, p, AFInfo, AFIndx, ErrStat, ErrMsg ) ! ! Called by : UA_Init ! Calls to : NONE !.............................................................................. real(DbKi), intent(in ) :: dt ! time step length (s) - type(UA_InitInputType), intent(inout) :: InitInp ! input data for initialization routine, needs to be inout because there is a copy of some data in InitInp in BEMT_SetParameters() + type(UA_InitInputType), intent(inout) :: InitInp ! input data for initialization routine ; we're moving allocated data from InitInp to p so must also be intent(out) type(UA_ParameterType), intent(inout) :: p ! parameters + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data + integer(IntKi), intent(in ) :: AFIndx(:,:) integer(IntKi), intent( out) :: ErrStat ! error status of the operation character(*), intent( out) :: ErrMsg ! error message if ErrStat /= ErrID_None - integer(IntKi) :: ErrStat2 - character(*), parameter :: RoutineName = 'UA_SetParameters' + character(ErrMsgLen) :: ErrMsg2 + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'UA_SetParameters' + logical :: IsUsed(size(AFInfo)) + + INTEGER(IntKi) :: i, j @@ -720,39 +717,84 @@ subroutine UA_SetParameters( dt, InitInp, p, ErrStat, ErrMsg ) ErrMsg = "" p%dt = dt - allocate(p%c(InitInp%nNodesPerBlade,InitInp%numBlades), stat = ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat( ErrID_Fatal, 'Error allocating p%c.', ErrStat, ErrMsg, RoutineName ) - ! Set errmessage and return - return - end if + call AllocAry(p%UA_off_forGood,InitInp%nNodesPerBlade,InitInp%numBlades,'p%UA_off_forGood',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry(p%c, InitInp%nNodesPerBlade,InitInp%numBlades,'p%c', ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return p%c = InitInp%c ! this can't be 0 p%numBlades = InitInp%numBlades p%nNodesPerBlade = InitInp%nNodesPerBlade - p%UAMod = InitInp%UAMod - p%a_s = InitInp%a_s ! this can't be 0 + p%UAMod = InitInp%UAMod + p%a_s = InitInp%a_s ! this can't be 0 p%Flookup = InitInp%Flookup p%ShedEffect = InitInp%ShedEffect if (p%UAMod==UA_HGM) then - p%lin_nx = p%numBlades*p%nNodesPerBlade*4 + p%lin_nx = p%numBlades*p%nNodesPerBlade*4 ! 4 continuous states per node per blade (5th state isn't currently linearizable) else p%lin_nx = 0 end if + p%UA_off_forGood = .false. ! flag that determines if UA should be turned off for the whole simulation + if (allocated(InitInp%UAOff_innerNode)) then + do j=1,min(size(p%UA_off_forGood,2), size(InitInp%UAOff_innerNode)) !blade + do i=1,min(InitInp%UAOff_innerNode(j),size(p%UA_off_forGood,1)) !node +! call WrScr( 'Warning: Turning off Unsteady Aerodynamics on inner node (node '//trim(num2lstr(i))//', blade '//trim(num2lstr(j))//')' ) + p%UA_off_forGood(i,j) = .true. + end do + end do + end if + + if (allocated(InitInp%UAOff_outerNode)) then + do j=1,min(size(p%UA_off_forGood,2), size(InitInp%UAOff_outerNode)) !blade + do i=InitInp%UAOff_outerNode(j), size(p%UA_off_forGood,1) !node +! call WrScr( 'Warning: Turning off Unsteady Aerodynamics on outer node (node '//trim(num2lstr(i))//', blade '//trim(num2lstr(j))//')' ) + p%UA_off_forGood(i,j) = .true. + end do + end do + end if + + do j=1,size(p%UA_off_forGood,2) !blade + do i=1,size(p%UA_off_forGood,1) !node + + if (.not. p%UA_off_forGood(i,j)) then + call UA_TurnOff_param(p, AFInfo(AFIndx(i,j)), ErrStat2, ErrMsg2) + if (ErrStat2 > ErrID_None) then + call WrScr( 'Warning: Turning off Unsteady Aerodynamics because '//trim(ErrMsg2)//' (node '//trim(num2lstr(i))//', blade '//trim(num2lstr(j))//')' ) + p%UA_off_forGood(i,j) = .true. + end if + end if + + end do + end do + + ! check that the airfoils have appropriate data for UA + IsUsed = .false. + do j=1,size(p%UA_off_forGood,2) !blade + do i=1,size(p%UA_off_forGood,1) !node + if (.not. p%UA_off_forGood(i,j)) then + IsUsed(AFIndx(i,j)) = .true. + end if + end do + end do + + do i=1,size(AFInfo,1) + if (IsUsed(i)) then + call UA_ValidateAFI(InitInp%UAMod, AFInfo(i), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end if + end do + end subroutine UA_SetParameters !============================================================================== !============================================================================== -subroutine UA_InitStates_Misc( p, AFInfo, AFIndx, x, xd, OtherState, m, ErrStat, ErrMsg ) +subroutine UA_InitStates_Misc( p, x, xd, OtherState, m, ErrStat, ErrMsg ) ! Called by : UA_Init ! Calls to : NONE !.............................................................................. type(UA_ParameterType), intent(in ) :: p ! Parameters - type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data - integer(IntKi), intent(in ) :: AFIndx(:,:) type(UA_ContinuousStateType), intent(inout) :: x ! Initial continuous states type(UA_DiscreteStateType), intent(inout) :: xd ! Initial discrete states type(UA_OtherStateType), intent(inout) :: OtherState ! Initial other states @@ -769,7 +811,7 @@ subroutine UA_InitStates_Misc( p, AFInfo, AFIndx, x, xd, OtherState, m, ErrStat, ! allocate all the state arrays - if (p%UAMod == UA_HGM) then + if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then allocate( x%element( p%nNodesPerBlade, p%numBlades ), stat=ErrStat2 ) if (ErrStat2 /= 0) call SetErrStat(ErrID_Fatal,"Cannot allocate x%x.",ErrStat,ErrMsg,RoutineName) @@ -777,6 +819,22 @@ subroutine UA_InitStates_Misc( p, AFInfo, AFIndx, x, xd, OtherState, m, ErrStat, allocate( OtherState%n(p%nNodesPerBlade, p%numBlades), stat=ErrStat2) if (ErrStat2 /= 0 ) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%n.", ErrStat, ErrMsg, RoutineName) + if (p%UAMod == UA_HGMV) then + allocate( OtherState%t_vortexBegin(p%nNodesPerBlade, p%numBlades), stat=ErrStat2) + if (ErrStat2 /= 0 ) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%t_vortexBegin.", ErrStat, ErrMsg, RoutineName) + + allocate( OtherState%SignOfOmega(p%nNodesPerBlade, p%numBlades), stat=ErrStat2) + if (ErrStat2 /= 0 ) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%SignOfOmega.", ErrStat, ErrMsg, RoutineName) + + allocate( OtherState%PositivePressure(p%nNodesPerBlade, p%numBlades), stat=ErrStat2) + if (ErrStat2 /= 0 ) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%PositivePressure.", ErrStat, ErrMsg, RoutineName) + + allocate( OtherState%vortexOn(p%nNodesPerBlade, p%numBlades), stat=ErrStat2) + if (ErrStat2 /= 0 ) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%vortexOn.", ErrStat, ErrMsg, RoutineName) + + allocate( OtherState%BelowThreshold(p%nNodesPerBlade, p%numBlades), stat=ErrStat2) + if (ErrStat2 /= 0 ) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%BelowThreshold.", ErrStat, ErrMsg, RoutineName) + end if else call AllocAry( xd%alpha_minus1, p%nNodesPerBlade,p%numBlades, 'xd%alpha_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -830,20 +888,17 @@ subroutine UA_InitStates_Misc( p, AFInfo, AFIndx, x, xd, OtherState, m, ErrStat, call AllocAry(m%weight ,p%nNodesPerBlade,p%numBlades,'m%weight',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) call AllocAry(OtherState%FirstPass,p%nNodesPerBlade,p%numBlades,'OtherState%FirstPass',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - call AllocAry(OtherState%UA_off_forGood,p%nNodesPerBlade,p%numBlades,'OtherState%UA_off_forGood',ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) if (ErrStat >= AbortErrLev) return - call UA_ReInit( p, AFInfo, AFIndx, x, xd, OtherState, m, ErrStat2,ErrMsg2 ) ! initializes values of states and misc vars + call UA_ReInit( p, x, xd, OtherState, m, ErrStat2,ErrMsg2 ) ! initializes values of states and misc vars call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) end subroutine UA_InitStates_Misc !============================================================================== -subroutine UA_ReInit( p, AFInfo, AFIndx, x, xd, OtherState, m, ErrStat, ErrMsg ) +subroutine UA_ReInit( p, x, xd, OtherState, m, ErrStat, ErrMsg ) type(UA_ParameterType), intent(in ) :: p ! Parameters - type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data - integer(IntKi), intent(in ) :: AFIndx(:,:) type(UA_ContinuousStateType), intent(inout) :: x ! Initial continuous states type(UA_DiscreteStateType), intent(inout) :: xd ! Initial discrete states type(UA_OtherStateType), intent(inout) :: OtherState ! Initial other states @@ -868,22 +923,17 @@ subroutine UA_ReInit( p, AFInfo, AFIndx, x, xd, OtherState, m, ErrStat, ErrMsg ) OtherState%FirstPass = .true. - OtherState%UA_off_forGood = .false. ! flag that determines if UA parameters are invalid and should be turned off for the whole simulation - do j=1,size(OtherState%UA_off_forGood,2) - do i=1,size(OtherState%UA_off_forGood,1) - - call UA_TurnOff_param(p, AFInfo(AFIndx(i,j)), ErrStat2, ErrMsg2) - if (ErrStat2 > ErrID_None) then - call WrScr( 'Warning: Turning off Unsteady Aerodynamics because '//trim(ErrMsg2)//' (node '//trim(num2lstr(i))//', blade '//trim(num2lstr(j))//')' ) - OtherState%UA_off_forGood(i,j) = .true. + do j=1,size(p%UA_off_forGood,2) !blade + do i=1,size(p%UA_off_forGood,1) !node + + if (p%UA_off_forGood(i,j)) then m%weight(i,j) = 0.0_ReKi end if end do - end do + end do - - if ( p%UAMod == UA_HGM ) then + if ( p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then OtherState%n = -1 ! we haven't updated OtherState%xdot, yet @@ -898,6 +948,14 @@ subroutine UA_ReInit( p, AFInfo, AFIndx, x, xd, OtherState, m, ErrStat, ErrMsg ) call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) end do + if (p%UAMod == UA_HGMV) then + OtherState%t_vortexBegin = 0.0_ReKi + OtherState%SignOfOmega = 1.0_ReKi + OtherState%PositivePressure = .true. + OtherState%vortexOn = .false. + OtherState%BelowThreshold = .true. + end if + else OtherState%sigma1 = 1.0_ReKi OtherState%sigma1c = 1.0_ReKi @@ -956,7 +1014,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & ! Calls to : NWTC_Init, UA_SetParameters, UA_InitStates !.............................................................................. - type(UA_InitInputType), intent(inout) :: InitInp ! Input data for initialization routine, needs to be inout because there is a copy of some data in InitInp in BEMT_SetParameters() + type(UA_InitInputType), intent(inout) :: InitInp ! input data for initialization routine ; we're moving allocated data from InitInp to p so must also be intent(out) type(UA_InputType), intent(in ) :: u ! An initial guess for the input; input mesh must be defined type(UA_ParameterType), intent( out) :: p ! Parameters type(UA_ContinuousStateType), intent( out) :: x ! Initial continuous states @@ -996,17 +1054,23 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & ! Initialize the NWTC Subroutine Library call NWTC_Init( EchoLibVer=.FALSE. ) - call UA_ValidateInput(InitInp, AFInfo, ErrStat2, ErrMsg2) + if (InitInp%WrSum) then + call UA_WriteAFIParamsToFile(InitInp, AFInfo, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end if + + call UA_ValidateInput(InitInp, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return ! Allocate and set parameter data structure using initialization data - call UA_SetParameters( interval, InitInp, p, ErrStat2, ErrMsg2 ) + call UA_SetParameters( interval, InitInp, p, AFInfo, AFIndx, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return ! initialize the discrete states, other states, and misc variables - call UA_InitStates_Misc( p, AFInfo, AFIndx, x, xd, OtherState, m, ErrStat2, ErrMsg2 ) ! initialize the continuous states + call UA_InitStates_Misc( p, x, xd, OtherState, m, ErrStat2, ErrMsg2 ) ! initialize the continuous states call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -1015,7 +1079,9 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & ! Allocate and set the InitOut data if (p%UAMod == UA_HGM) then - p%NumOuts = 19 + p%NumOuts = 20 + elseif(p%UAMod == UA_HGMV) then + p%NumOuts = 21 else p%NumOuts = 45 end if @@ -1053,7 +1119,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & InitOut%WriteOutputUnt(iOffset+ 6) ='(-)' InitOut%WriteOutputUnt(iOffset+ 7) ='(-)' - if (p%UAmod == UA_HGM) then + if (p%UAmod == UA_HGM .or. p%UAMod == UA_HGMV) then InitOut%WriteOutputHdr(iOffset+ 8) = trim(chanPrefix)//'omega' InitOut%WriteOutputHdr(iOffset+ 9) = trim(chanPrefix)//'alphaE' @@ -1068,7 +1134,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & InitOut%WriteOutputHdr(iOffset+17) = trim(chanPrefix)//'x4' InitOut%WriteOutputHdr(iOffset+18) = trim(chanPrefix)//'k' InitOut%WriteOutputHdr(iOffset+19) = trim(chanPrefix)//'weight' - + InitOut%WriteOutputHdr(iOffset+20) = trim(chanPrefix)//'cl_fa' InitOut%WriteOutputUnt(iOffset+ 8) = '(deg/sec)' InitOut%WriteOutputUnt(iOffset+ 9) = '(deg)' @@ -1083,8 +1149,16 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & InitOut%WriteOutputUnt(iOffset+17) = '(-)' InitOut%WriteOutputUnt(iOffset+18) = '(-)' InitOut%WriteOutputUnt(iOffset+19) = '(-)' + InitOut%WriteOutputUnt(iOffset+20) = '(-)' + + if (p%UAmod == UA_HGMV) then + InitOut%WriteOutputHdr(iOffset+21) = trim(chanPrefix)//''x5' + InitOut%WriteOutputUnt(iOffset+21) = '(-)' + end if + else + 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' @@ -1121,8 +1195,8 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & InitOut%WriteOutputHdr(iOffset+41) = trim(chanPrefix)//'sigma3' InitOut%WriteOutputHdr(iOffset+42) = trim(chanPrefix)//'T_sh' InitOut%WriteOutputHdr(iOffset+43) = trim(chanPrefix)//'k' - InitOut%WriteOutputHdr(iOffset+44) = trim(chanPrefix)//'ALPHA_filt' InitOut%WriteOutputHdr(iOffset+44) = trim(chanPrefix)//'weight' + InitOut%WriteOutputHdr(iOffset+45) = trim(chanPrefix)//'ALPHA_filt' InitOut%WriteOutputUnt(iOffset+8) ='(-)' @@ -1161,8 +1235,9 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & InitOut%WriteOutputUnt(iOffset+41) ='(-)' InitOut%WriteOutputUnt(iOffset+42) ='(-)' InitOut%WriteOutputUnt(iOffset+43) ='(-)' - InitOut%WriteOutputUnt(iOffset+44) ='(deg)' + InitOut%WriteOutputUnt(iOffset+44) ='(deg)' InitOut%WriteOutputUnt(iOffset+45) ='(-)' + end if end do @@ -1212,41 +1287,23 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & end subroutine UA_Init !============================================================================== -subroutine UA_ValidateInput(InitInp, AFInfo, ErrStat, ErrMsg) - type(UA_InitInputType), intent(inout) :: InitInp ! Input data for initialization routine, needs to be inout because there is a copy of some data in InitInp in BEMT_SetParameters() - type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data +subroutine UA_ValidateInput(InitInp, ErrStat, ErrMsg) + type(UA_InitInputType), intent(in ) :: InitInp ! Input data for initialization routine integer(IntKi), intent( out) :: ErrStat ! Error status of the operation character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None - integer(IntKi) :: ErrStat2 ! Error status of the operation - character(ErrMsgLen) :: ErrMsg2 ! Error message if ErrStat /= ErrID_None character(*), parameter :: RoutineName = 'UA_ValidateInput' - - integer(IntKi) :: i ! loop counter ErrStat = ErrID_None ErrMsg = "" - !>>> remove after this feature gets tested better: - if (InitInp%UAMod == UA_HGM ) then - call SetErrStat( ErrID_Fatal, "UAMod cannot be 4 (continuous HGM model) in this version of OpenFAST.", ErrStat, ErrMsg, RoutineName ) - end if - !<<< - - if (InitInp%UAMod < UA_Gonzalez .or. InitInp%UAMod > UA_HGM ) call SetErrStat( ErrID_Fatal, & - "In this version, UAMod must be 2 (Gonzalez's variant), 3 (Minnema/Pierce variant), or 4 (continuous HGM model).", ErrStat, ErrMsg, RoutineName ) ! NOTE: for later- 1 (baseline/original) + if (InitInp%UAMod < UA_Gonzalez .or. InitInp%UAMod > UA_HGMV ) call SetErrStat( ErrID_Fatal, & + "In this version, UAMod must be 2 (Gonzalez's variant), 3 (Minnema/Pierce variant), 4 (continuous HGM model), or 5 (HGM with vortex).", ErrStat, ErrMsg, RoutineName ) ! NOTE: for later- 1 (baseline/original) if (.not. InitInp%FLookUp ) call SetErrStat( ErrID_Fatal, 'FLookUp must be TRUE for this version.', ErrStat, ErrMsg, RoutineName ) if (InitInp%a_s <= 0.0) call SetErrStat ( ErrID_Fatal, 'The speed of sound (SpdSound) must be greater than zero.', ErrStat, ErrMsg, RoutineName ) - ! check that the airfoils have appropriate data for UA - do i=1,size(AFInfo,1) - call UA_ValidateAFI(InitInp%UAMod, AFInfo(i), ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - end do - - end subroutine UA_ValidateInput !============================================================================== @@ -1257,6 +1314,8 @@ subroutine UA_ValidateAFI(UAMod, AFInfo, ErrStat, ErrMsg) character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None integer(IntKi) :: j + integer(IntKi) :: indx + real(ReKi) :: cl_fs character(*), parameter :: RoutineName = 'UA_ValidateAFI' ErrStat = ErrID_None @@ -1267,40 +1326,73 @@ subroutine UA_ValidateAFI(UAMod, AFInfo, ErrStat, ErrMsg) else do j=1, AFInfo%NumTabs - if ( .not. AFInfo%Table(j)%InclUAdata ) then - call SetErrStat(ErrID_Fatal, 'Airfoil file "'//trim(AFInfo%FileName)//'", table #'//trim(num2lstr(j))// & - ' does not contain parameters for UA data.', ErrStat, ErrMsg, RoutineName ) - else + + if ( AFInfo%Table(j)%InclUAdata ) then ! parameters used only for UAMod/=UA_HGM) if (UAMod /= UA_HGM) then - if ( EqualRealNos(AFInfo%Table(j)%UA_BL%St_sh, 0.0_ReKi) ) then - call SetErrStat(ErrID_Fatal, 'UA St_sh parameter must not be 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + + if (UAMod /= UA_HGMV) then + if ( EqualRealNos(AFInfo%Table(j)%UA_BL%St_sh, 0.0_ReKi) ) then + call SetErrStat(ErrID_Fatal, 'UA St_sh parameter must not be 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + end if + + if ( AFInfo%Table(j)%UA_BL%alpha1 > pi .or. AFInfo%Table(j)%UA_BL%alpha1 < -pi ) then + call SetErrStat(ErrID_Fatal, 'UA alpha1 parameter must be between -180 and 180 degrees in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + end if + + if ( AFInfo%Table(j)%UA_BL%alpha2 > pi .or. AFInfo%Table(j)%UA_BL%alpha2 < -pi ) then + call SetErrStat(ErrID_Fatal, 'UA alpha2 parameter must be between -180 and 180 degrees in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + end if + + if ( AFInfo%Table(j)%UA_BL%alpha1 < AFInfo%Table(j)%UA_BL%alpha2 ) then + call SetErrStat(ErrID_Fatal, 'UA alpha2 parameter must be less than alpha1 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + end if + + if ( AFInfo%Table(j)%UA_BL%alpha0 > AFInfo%Table(j)%UA_BL%alpha1 ) then + call SetErrStat(ErrID_Fatal, 'UA alpha0 parameter must be less than alpha1 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + end if + + if ( AFInfo%Table(j)%UA_BL%alpha2 > AFInfo%Table(j)%UA_BL%alpha0 ) then + call SetErrStat(ErrID_Fatal, 'UA alpha0 parameter must be greater than alpha2 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + end if + + if ( AFInfo%Table(j)%UA_BL%filtCutOff < 0.0_ReKi ) then + call SetErrStat(ErrID_Fatal, 'UA filtCutOff parameter must be greater than 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + end if end if - - if ( AFInfo%Table(j)%UA_BL%alpha1 > pi .or. AFInfo%Table(j)%UA_BL%alpha1 < -pi ) then - call SetErrStat(ErrID_Fatal, 'UA alpha1 parameter must be between -180 and 180 degrees in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + + if ( AFInfo%Table(j)%UA_BL%T_VL <= 0.0_ReKi ) then + call SetErrStat(ErrID_Fatal, 'UA T_VL parameter must be greater than 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) end if - - if ( AFInfo%Table(j)%UA_BL%alpha2 > pi .or. AFInfo%Table(j)%UA_BL%alpha2 < -pi ) then - call SetErrStat(ErrID_Fatal, 'UA alpha2 parameter must be between -180 and 180 degrees in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + + if ( AFInfo%Table(j)%UA_BL%T_V0 <= 0.0_ReKi ) then + call SetErrStat(ErrID_Fatal, 'UA T_V0 parameter must be greater than 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) end if + + if (AFInfo%Table(j)%UA_BL%Cn2 >= AFInfo%Table(j)%UA_BL%Cn1) call SetErrStat(ErrID_Fatal, 'Cn2 must be less than Cn1 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) - if ( AFInfo%Table(j)%UA_BL%filtCutOff < 0.0_ReKi ) then - call SetErrStat(ErrID_Fatal, 'UA filtCutOff parameter must be greater than 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + end if + + if (UAMod /= UA_HGMV) then + if ( AFInfo%Table(j)%UA_BL%alpha0 > pi .or. AFInfo%Table(j)%UA_BL%alpha0 < -pi ) then + call SetErrStat(ErrID_Fatal, 'UA alpha0 parameter must be between -180 and 180 degrees in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) end if - end if - ! variables used in all UA models: - if ( AFInfo%Table(j)%UA_BL%alpha0 > pi .or. AFInfo%Table(j)%UA_BL%alpha0 < -pi ) then - call SetErrStat(ErrID_Fatal, 'UA alpha0 parameter must be between -180 and 180 degrees in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + if (UAMod == UA_HGM .or. UAMod == UA_HGMV) then + cl_fs = InterpStp( AFInfo%Table(j)%UA_BL%UACutout, AFInfo%Table(j)%alpha, AFInfo%Table(j)%Coefs(:,AFInfo%ColUAf), indx, AFInfo%Table(j)%NumAlf ) + if (.not. EqualRealNos( cl_fs, 0.0_ReKi ) ) then + call SetErrStat(ErrID_Severe, 'UA cutout parameter should be at a value where the separation function is 0 in "'//trim(AFInfo%FileName)//'".'// & + " Separation function is "//trim(num2lstr(cl_fs)), ErrStat, ErrMsg, RoutineName ) + end if end if - - if ( AFInfo%Table(j)%UA_BL%T_f0 < 0.0_ReKi ) then + + ! variables used in all UA models: + if ( AFInfo%Table(j)%UA_BL%T_f0 <= 0.0_ReKi ) then call SetErrStat(ErrID_Fatal, 'UA T_f0 parameter must be greater than 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) end if - if ( AFInfo%Table(j)%UA_BL%T_p < 0.0_ReKi ) then + if ( AFInfo%Table(j)%UA_BL%T_p <= 0.0_ReKi ) then call SetErrStat(ErrID_Fatal, 'UA T_p parameter must be greater than 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) end if @@ -1310,13 +1402,16 @@ subroutine UA_ValidateAFI(UAMod, AFInfo, ErrStat, ErrMsg) call SetErrStat(ErrID_Fatal, 'UA UACutout parameter must not be negative in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) end if - - + ! this should never occur (if it does, check how it is set in AirfoilInfo) + if ( AFInfo%Table(j)%UA_BL%UACutout_blend > AFInfo%Table(j)%UA_BL%UACutout ) then + call SetErrStat(ErrID_Fatal, 'UA UACutout parameter must not be smaller than than UACutout_blend in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) + end if + end do if (ErrStat >= AbortErrLev) return - if (UAMod /= UA_HGM) then + if (UAMod /= UA_HGM .and. UAMod /= UA_HGMV) then ! check interpolated values: do j=2, AFInfo%NumTabs @@ -1346,6 +1441,15 @@ subroutine UA_TurnOff_param(p, AFInfo, ErrStat, ErrMsg) ErrStat = ErrID_None ErrMsg = "" + do j=1, AFInfo%NumTabs + if ( .not. AFInfo%Table(j)%InclUAdata ) then + ErrStat = ErrID_Fatal + ErrMsg = 'UA parameters are not included in airfoil (airfoil has likely has constant polars).' + return + end if + end do + + if (p%UAMod == UA_HGM) then ! unsteady aerodynamics will be turned off do j=1, AFInfo%NumTabs @@ -1366,7 +1470,7 @@ subroutine UA_TurnOff_param(p, AFInfo, ErrStat, ErrMsg) end if end do - else + elseif (p%UAMod /= UA_HGMV) then !also includes HGMV model ! unsteady aerodynamics will be turned off do j=1, AFInfo%NumTabs if ( EqualRealNos(AFInfo%Table(j)%UA_BL%C_nalpha, 0.0_ReKi) ) then @@ -1387,7 +1491,6 @@ subroutine UA_TurnOff_param(p, AFInfo, ErrStat, ErrMsg) end do end if - end subroutine UA_TurnOff_param !============================================================================== @@ -1694,6 +1797,8 @@ subroutine UA_UpdateStates( i, j, t, n, u, uTimes, p, x, xd, OtherState, AFInfo, character(*), parameter :: RoutineName = 'UA_UpdateStates' type(UA_InputType) :: u_interp_raw ! Input at current timestep, t and t+dt type(UA_InputType) :: u_interp ! Input at current timestep, t and t+dt + type(AFI_UA_BL_Type) :: BL_p ! airfoil UA parameters retrieved in Kelvin Chain + real(ReKi) :: Tu ! Initialize variables @@ -1703,27 +1808,37 @@ subroutine UA_UpdateStates( i, j, t, n, u, uTimes, p, x, xd, OtherState, AFInfo, !BJJ: u%u == 0 seems to be the root cause of all sorts of numerical problems.... - if (OtherState%UA_off_forGood(i,j)) return ! we don't have any states to update here + if (p%UA_off_forGood(i,j)) return ! we don't have any states to update here - if (p%UAMod == UA_HGM) then - + CALL UA_Input_ExtrapInterp( u, utimes, u_interp_raw, t, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + IF ( ErrStat >= AbortErrLev ) RETURN + + ! make sure that u%u is not zero (this previously turned off UA for the entire simulation. + ! Now, we keep it on, but we don't want the math to blow up when we divide by u%u) + call UA_fixInputs(u_interp_raw, u_interp, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + + + if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then + + ! initialize states to steady-state values: if (OtherState%FirstPass(i,j)) then - CALL UA_Input_ExtrapInterp( u, utimes, u_interp, t, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - IF ( ErrStat >= AbortErrLev ) RETURN - call HGM_Steady( i, j, u_interp, p, x%element(i,j), AFInfo, ErrStat2, ErrMsg2 ) end if - call UA_ABM4( i, j, t, n, u, utimes, p, x, OtherState, AFInfo, m, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) x%element(i,j)%x(4) = max( min( x%element(i,j)%x(4), 1.0_R8Ki ), 0.0_R8Ki ) + ! let's make sure the states aren't getting out of control when we are supposed to be turning off UA anyway. + call UA_BlendSteadyStates( i, j, u_interp, p, AFInfo, x%element(i,j), m%FirstWarn_UA_off, m%weight(i,j), ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + ! these are angles that should not get too large, so I am fixing them here (should turn off UA if this exceeds reasonable numbers) - if (abs(x%element(i,j)%x(1)) > pi .or. abs(x%element(i,j)%x(2)) > pi) then + if (abs(x%element(i,j)%x(1)) > TwoPi .or. abs(x%element(i,j)%x(2)) > TwoPi) then if (m%FirstWarn_UA) then call SetErrStat(ErrID_Severe, "Divergent states in UA HGM model", ErrStat, ErrMsg, RoutineName ) m%FirstWarn_UA = .false. @@ -1733,18 +1848,64 @@ subroutine UA_UpdateStates( i, j, t, n, u, uTimes, p, x, xd, OtherState, AFInfo, call Mpi2pi(x%element(i,j)%x(2)) end if + + if (p%UAMod == UA_HGMV) then + + ! Lookup values using Airfoil Info module + call AFI_ComputeUACoefs( AFInfo, u_interp%Re, u_interp%UserProp, BL_p, ErrMsg2, ErrStat2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return + + call Get_HGM_constants(i, j, p, u_interp, x%element(i,j), BL_p, Tu) + + if (OtherState%VortexOn(i,j)) then + + ! check if the vortex ceases to accumulate: + if ( (t - OtherState%t_vortexBegin(i,j) > BL_p%T_VL*Tu) .or. & + (u_interp%omega * OtherState%SignOfOmega(i,j) < 0.0_ReKi) ) then + + OtherState%VortexOn(i,j) = .false. + + end if + + else + + ! we aren't going to turn on a vortex when turning UA is not fully on + if (EqualRealNos(m%weight(i,j),1.0_ReKi) ) then + + if (OtherState%BelowThreshold(i,j) .and. & + (x%element(i,j)%x(3) > BL_p%Cn1 .or. x%element(i,j)%x(3) < BL_p%Cn2) ) then + OtherState%BelowThreshold(i,j) = .false. + OtherState%VortexOn(i,j) = .true. + OtherState%t_vortexBegin(i,j) = t + OtherState%PositivePressure(i,j) = x%element(i,j)%x(3) > BL_p%Cn1 ! whether we have the Cn1 (positive) or Cn2 (negative) condition to check + + if (u_interp%omega < 0 ) then + OtherState%SignOfOmega(i,j) = -1.0 + else + OtherState%SignOfOmega(i,j) = 1.0 + end if + + else + if (.not. OtherState%BelowThreshold(i,j)) then + if (OtherState%PositivePressure(i,j)) then + OtherState%BelowThreshold(i,j) = x%element(i,j)%x(3) < BL_p%Cn1 + else + OtherState%BelowThreshold(i,j) = x%element(i,j)%x(3) > BL_p%Cn2 + end if + end if + + end if + + end if ! UA is fully on + + end if ! p%UAMod == UA_HGMV + + end if + else if (n<=0) return ! previous logic (before adding UA_HGM required n > 0 before UA_UpdateStates was called) - CALL UA_Input_ExtrapInterp( u, utimes, u_interp_raw, t, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - IF ( ErrStat >= AbortErrLev ) RETURN - - ! make sure that u%u is not zero (this previously turned off UA for the entire simulation. - ! Now, we keep it on, but we don't want the math to blow up when we divide by u%u) - call UA_fixInputs(u_interp_raw, u_interp, ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - ! Update discrete states: # ifdef DEBUG_v14 call UA_UpdateDiscOtherState2( i, j, u_interp, p, xd, OtherState, AFInfo, m, ErrStat2, ErrMsg2 ) @@ -1780,13 +1941,13 @@ subroutine UA_InitStates_AllNodes( u, p, x, OtherState, AFInfo, AFIndx ) !............................................................................................................................... ! compute UA states at t=0 (with known inputs) !............................................................................................................................... - if (p%UAMod == UA_HGM) then + if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then - do j = 1,size(OtherState%UA_off_forGood,2) ! blades - do i = 1,size(OtherState%UA_off_forGood,1) ! nodes + do j = 1,size(p%UA_off_forGood,2) ! blades + do i = 1,size(p%UA_off_forGood,1) ! nodes ! We only update the UnsteadyAero states if we have unsteady aero turned on for this node - if ( .not. OtherState%UA_off_forGood(i,j) .and. OtherState%FirstPass(i,j) ) then + if ( .not. p%UA_off_forGood(i,j) .and. OtherState%FirstPass(i,j) ) then ! initialize states to steady-state values: call HGM_Steady( i, j, u(i,j), p, x%element(i,j), AFInfo(AFIndx(i,j)), ErrStat2, ErrMsg2 ) @@ -1854,13 +2015,33 @@ SUBROUTINE HGM_Steady( i, j, u, p, x, AFInfo, ErrStat, ErrMsg ) x%x(2) = BL_p%A2 * alpha_34 alphaE = alpha_34 ! Eq. 12 (after substitute of x1 and x2 initializations) - x%x(3) = BL_p%c_lalpha * (alphaE-BL_p%alpha0) - - ! calculate x%x(4) = fs_aF = f_st(alphaF): - alphaF = x%x(3)/BL_p%c_lalpha + BL_p%alpha0 ! p. 13 - - call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) - x%x(4) = AFI_interp%f_st + alphaF = alphaE + + if (p%UAMod==UA_HGM) then + x%x(3) = BL_p%c_lalpha * (alphaE-BL_p%alpha0) + + ! calculate x%x(4) = fs_aF = f_st(alphaF): + !alphaF = x%x(3)/BL_p%c_lalpha + BL_p%alpha0 ! p. 13 + !this simplifies to alphaF = alphaE + + call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) + x%x(4) = AFI_interp%f_st + + else !if (p%UAMod==UA_HGMV) then + call AFI_ComputeAirfoilCoefs( alphaE, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) + x%x(3) = AFI_interp%FullyAttached ! ~ (alpha-alphaLower)*c_Rate + c_alphaLower + + ! find alphaF where cn_FullyAttached(alphaF) = x(3) + ! and note that we just set x(3) = cn_FullyAttached(alphaE) + ! alphaF = alphaE + + ! calculate x%x(4) = fs_aF = f_st(alphaF): + call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) + x%x(4) = AFI_interp%f_st + + end if + + x%x(5) = 0.0_R8Ki end subroutine HGM_Steady !---------------------------------------------------------------------------------------------------------------------------------- @@ -1884,7 +2065,8 @@ subroutine UA_CalcContStateDeriv( i, j, t, u_in, p, x, OtherState, AFInfo, m, dx ! Local variables type(AFI_UA_BL_Type) :: BL_p ! potentially interpolated UA parameters - type(AFI_OutputType) :: AFI_Interp + type(AFI_OutputType) :: AFI_AlphaE ! interploated values at alphaE + type(AFI_OutputType) :: AFI_AlphaF ! interpolated values at alphaF character(ErrMsgLen) :: errMsg2 integer(IntKi) :: errStat2 character(*), parameter :: RoutineName = 'UA_CalcContStateDeriv' @@ -1893,18 +2075,19 @@ subroutine UA_CalcContStateDeriv( i, j, t, u_in, p, x, OtherState, AFInfo, m, dx real(ReKi) :: alphaE real(ReKi) :: alphaF real(ReKi) :: Clp + real(ReKi) :: cRate ! slope of the piecewise linear region of fully attached polar real(R8Ki) :: x4 real(ReKi) :: alpha_34 real(ReKi), parameter :: U_dot = 0.0_ReKi ! at some point we may add this term TYPE(UA_InputType) :: u ! Inputs at t - + real(R8Ki) :: CnC_dot, One_Plus_Sqrt_x4, cv_dot, CnC ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" - if (OtherState%UA_off_forGood(i,j)) then + if (p%UA_off_forGood(i,j)) then dxdt%x = 0.0_R8Ki return end if @@ -1923,35 +2106,88 @@ subroutine UA_CalcContStateDeriv( i, j, t, u_in, p, x, OtherState, AFInfo, m, dx call Get_HGM_constants(i, j, p, u, x, BL_p, Tu, alpha_34, alphaE) ! compute Tu, alpha_34, and alphaE - Clp = BL_p%c_lalpha * (alphaE - BL_p%alpha0) + pi * Tu * u%omega ! Eq. 13 - ! fix definitions of T_f0 and T_p (based on email from Emmanuel 12-28-20 regarding HAWC2 default values) - BL_p%T_f0 = BL_p%T_f0 * 2.0_ReKi * Tu + BL_p%T_f0 = BL_p%T_f0 * Tu ! Emmanuel wants a factor of 2 here to match HAWC2, but we don't want that factor for Bladed comparisons BL_p%T_p = BL_p%T_p * Tu - ! calculate fs_aF (stored in AFI_interp%f_st): - - !note: BL_p%c_lalpha cannot be zero. UA is turned off at initialization if this occurs. - alphaF = x%x(3)/BL_p%c_lalpha + BL_p%alpha0 ! p. 13 - call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2) + ! find alphaF where FullyAttached(alphaF) = x(3) + if (p%UAMod == UA_HGM) then + !note: BL_p%c_lalpha cannot be zero. UA is turned off at initialization if this occurs. + alphaF = x%x(3)/BL_p%c_lalpha + BL_p%alpha0 ! p. 13 + else + if (x%x(3) < BL_p%c_alphaLowerWrap) then + alphaF = (x%x(3) - BL_p%c_alphaLowerWrap) / BL_p%c_RateWrap + BL_p%alphaLowerWrap + elseif (x%x(3) < BL_p%c_alphaLower) then + alphaF = (x%x(3) - BL_p%c_alphaLower) / BL_p%c_RateLower + BL_p%alphaLower + elseif(x%x(3) < BL_p%c_alphaUpper) then + ! this alphaF might be slightly different for alphaLower < x(3) < alphaUpper (it's not quite linear there) + ! however, the separation function is 1 there, so it doesn't matter if we're off a little bit + alphaF = (x%x(3) - BL_p%c_alphaLower) / BL_p%c_Rate + BL_p%alphaLower + elseif(x%x(3) < BL_p%c_alphaUpperWrap) then + alphaF = (x%x(3) - BL_p%c_alphaUpper) / BL_p%c_RateUpper + BL_p%alphaUpper + else + alphaF = (x%x(3) - BL_p%c_alphaUpperWrap) / BL_p%c_RateWrap + BL_p%alphaUpperWrap + end if + end if + + call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_AlphaF, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) if (ErrStat >= AbortErrLev) return - ! States - !x1: Downwash memory term 1 (rad) - !x2: Downwash memory term 2 (rad) - !x3: Clp', Lift coefficient with a time lag to the attached lift coeff - !x4: f'' , Final separation point function - - ! Constraining x4 between 0 and 1 increases numerical stability (should be done elsewhere, but we'll double check here in case there were perturbations on the state value) - x4 = max( min( x%x(4), 1.0_R8Ki ), 0.0_R8Ki ) - - dxdt%x(1) = -1.0_R8Ki / Tu * (BL_p%b1 + p%c(i,j) * U_dot/(2*u%u**2)) * x%x(1) + BL_p%b1 * BL_p%A1 / Tu * alpha_34 - dxdt%x(2) = -1.0_R8Ki / Tu * (BL_p%b2 + p%c(i,j) * U_dot/(2*u%u**2)) * x%x(2) + BL_p%b2 * BL_p%A2 / Tu * alpha_34 - dxdt%x(3) = -1.0_R8Ki / BL_p%T_p * x%x(3) + 1.0_ReKi / BL_p%T_p * Clp - dxdt%x(4) = -1.0_R8Ki / BL_p%T_f0 * x4 + 1.0_ReKi / BL_p%T_f0 * AFI_interp%f_st + + ! States + !x1: Downwash memory term 1 (rad) + !x2: Downwash memory term 2 (rad) + !x3: Clp', Lift coefficient with a time lag to the attached lift coeff + !x4: f'' , Final separation point function + + ! Constraining x4 between 0 and 1 increases numerical stability (should be done elsewhere, but we'll double check here in case there were perturbations on the state value) + x4 = max( min( x%x(4), 1.0_R8Ki ), 0.0_R8Ki ) + + call AddOrSub2Pi(real(x%x(1),ReKi), alpha_34) ! make sure we use the same alpha_34 for both x1 and x2 equations. + dxdt%x(1) = -1.0_R8Ki / Tu * (BL_p%b1 + p%c(i,j) * U_dot/(2*u%u**2)) * x%x(1) + BL_p%b1 * BL_p%A1 / Tu * alpha_34 + dxdt%x(2) = -1.0_R8Ki / Tu * (BL_p%b2 + p%c(i,j) * U_dot/(2*u%u**2)) * x%x(2) + BL_p%b2 * BL_p%A2 / Tu * alpha_34 + + if (p%UAMod == UA_HGM) then + call AddOrSub2Pi(BL_p%alpha0, alphaE) + Clp = BL_p%c_lalpha * (alphaE - BL_p%alpha0) + pi * Tu * u%omega ! Eq. 13 + dxdt%x(3) = -1.0_R8Ki / BL_p%T_p * x%x(3) + 1.0_ReKi / BL_p%T_p * Clp + dxdt%x(4) = -1.0_R8Ki / BL_p%T_f0 * x4 + 1.0_ReKi / BL_p%T_f0 * AFI_AlphaF%f_st + dxdt%x(5) = 0.0_R8Ki + else !if (p%UAMod == UA_HGMV) then + + call AFI_ComputeAirfoilCoefs( alphaE, u%Re, u%UserProp, AFInfo, AFI_AlphaE, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return + Clp = AFI_AlphaE%FullyAttached + pi * Tu * u%omega ! Eq. 13 (this is really Cnp) + dxdt%x(3) = -1.0_R8Ki / BL_p%T_p * x%x(3) + 1.0_ReKi / BL_p%T_p * Clp + + dxdt%x(4) = -1.0_R8Ki / BL_p%T_f0 * x4 + 1.0_ReKi / BL_p%T_f0 * AFI_AlphaF%f_st + + if (OtherState%VortexOn(i,j)) then + One_Plus_Sqrt_x4 =1.0_R8Ki + sqrt(x4) + + if (alphaE < BL_p%alphaLower) then + cRate = BL_p%c_RateLower + elseif(alphaE < BL_p%alphaUpper) then + cRate = BL_p%c_Rate + else + cRate = BL_p%c_RateUpper + end if + CnC_dot = cRate * u%omega * (1.0_R8Ki - BL_p%A1 - BL_p%A2) + dxdt%x(1) + dxdt%x(2) + cv_dot = CnC_dot*(1.0_R8Ki - 0.25_R8Ki*(One_Plus_Sqrt_x4)**2) + + CnC = AFI_AlphaE%FullyAttached + cv_dot = cv_dot - CnC*0.25_R8Ki*One_Plus_Sqrt_x4/sqrt(max(0.0001_R8Ki,x4))*dxdt%x(4) + else + cv_dot = 0.0_R8Ki + end if + + dxdt%x(5) = cv_dot - x%x(5)/(BL_p%T_V0 * Tu) + end if + END SUBROUTINE UA_CalcContStateDeriv !---------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE Get_HGM_constants(i, j, p, u, x, BL_p, Tu, alpha_34, alphaE) @@ -1962,9 +2198,9 @@ SUBROUTINE Get_HGM_constants(i, j, p, u, x, BL_p, Tu, alpha_34, alphaE) TYPE(UA_ElementContinuousStateType), INTENT(IN ) :: x ! Continuous states at t TYPE(AFI_UA_BL_Type), INTENT(IN ) :: BL_p ! potentially interpolated UA parameters - REAL(ReKi), INTENT( OUT) :: alpha_34 + REAL(ReKi), optional, INTENT( OUT) :: alpha_34 REAL(ReKi), INTENT( OUT) :: Tu - REAL(ReKi), INTENT( OUT) :: alphaE + REAL(ReKi), optional, INTENT( OUT) :: alphaE ! Local variables real(ReKi) :: vx_34 @@ -1977,12 +2213,16 @@ SUBROUTINE Get_HGM_constants(i, j, p, u, x, BL_p, Tu, alpha_34, alphaE) Tu = min(Tu, 50.0_ReKi) ! ensure the time constant doesn't exceed 50 s. Tu = max(Tu, 0.001_ReKi) ! ensure the time constant doesn't get too small, either. - vx_34 = u%v_ac(1) - u%omega * 0.5_ReKi*p%c(i,j) ! Eq. 1 - alpha_34 = atan2(vx_34, u%v_ac(2) ) ! page 5 definitions + if (present(alpha_34)) then + vx_34 = u%v_ac(1) - u%omega * 0.5_ReKi*p%c(i,j) ! Eq. 1 + alpha_34 = atan2(vx_34, u%v_ac(2) ) ! page 5 definitions - ! Variables derived from states - alphaE = alpha_34*(1.0_ReKi - BL_p%A1 - BL_p%A2) + x%x(1) + x%x(2) ! Eq. 12 - call MPi2Pi(alphaE) + if (present(alphaE)) then + ! Variables derived from states + alphaE = alpha_34*(1.0_ReKi - BL_p%A1 - BL_p%A2) + x%x(1) + x%x(2) ! Eq. 12 + call MPi2Pi(alphaE) + end if + end if END SUBROUTINE Get_HGM_constants !---------------------------------------------------------------------------------------------------------------------------------- @@ -2270,12 +2510,13 @@ END SUBROUTINE UA_ABM4 !============================================================================== -subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, ErrStat, ErrMsg ) +subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, ErrStat, ErrMsg ) ! Routine for computing outputs, used in both loose and tight coupling. !.............................................................................. integer(IntKi), intent(in ) :: i ! node index within a blade integer(IntKi), intent(in ) :: j ! blade index + REAL(DbKi), INTENT(IN ) :: t ! Current simulation time in seconds type(UA_InputType), intent(in ) :: u_in ! Inputs at Time type(UA_ParameterType), intent(in ) :: p ! Parameters type(UA_ContinuousStateType), intent(in ) :: x ! Continuous states at Time @@ -2301,6 +2542,7 @@ subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, Err real(ReKi) :: Cm_FS real(ReKi) :: Cc_FS + real(ReKi) :: Cl_FA real(ReKi) :: Cm_alpha_nc real(ReKi) :: M, f, k2_hat real(ReKi) :: Cm_v, alpha_prime_f @@ -2315,6 +2557,8 @@ subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, Err real(ReKi) :: fs_aE real(ReKi) :: cl_fs real(ReKi) :: x4 + real(ReKi) :: x5 + real(ReKi) :: cn_circ, tau_vl, tV_ratio real(ReKi) :: delta_c_df_primeprime real(ReKi), parameter :: delta_c_mf_primeprime = 0.0_ReKi TYPE(UA_ElementContinuousStateType) :: x_in ! Continuous states at t @@ -2340,7 +2584,7 @@ subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, Err call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) k = abs(u%omega * p%c(i, j) / (2.0_ReKi* u%u)) - if ( OtherState%UA_off_forGood(i,j) .or. (OtherState%FirstPass(i, j) .and. p%UAMod /= UA_HGM) ) then ! note: if u%U isn't zero because we've called UA_fixInputs + if ( p%UA_off_forGood(i,j) .or. (OtherState%FirstPass(i, j) .and. p%UAMod < UA_HGM) ) then ! note: if u%U isn't zero because we've called UA_fixInputs misc%weight(i,j) = 0.0 @@ -2372,13 +2616,15 @@ subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, Err KC%alpha_filt_cur = u%alpha KC%ds = 2.0_ReKi*u%U*p%dt/p%c(i, j) - alphaE = 0.0 + alphaE = u%alpha Tu = 0.0 - alpha_34 = 0.0 - cl_fs = 0.0 - fs_aE = 0.0 - - elseif (p%UAMod == UA_HGM) then + alpha_34 = u%alpha + cl_fs = AFI_interp%FullySeparate + cl_fa = AFI_interp%FullyAttached + fs_aE = AFI_interp%f_st + + x_in%x = 0.0_R8Ki + elseif (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then x_in = x%element(i,j) if (OtherState%FirstPass(i,j)) then @@ -2393,33 +2639,72 @@ subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, Err call Get_HGM_constants(i, j, p, u, x_in, BL_p, Tu, alpha_34, alphaE) ! compute Tu, alpha_34, and alphaE call AFI_ComputeAirfoilCoefs( alphaE, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2 ) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - - ! calculate fs_aE and cl_fs: - cl_fs = AFI_interp%cl_fs - fs_aE = AFI_interp%f_st - + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + ! Constraining x4 between 0 and 1 increases numerical stability (should be done elsewhere, but we'll double check here in case there were perturbations on the state value) x4 = max( min( x_in%x(4), 1.0_R8Ki ), 0.0_R8Ki ) + + ! calculate values for output: + cl_fs = AFI_interp%FullySeparate + cl_fa = AFI_interp%FullyAttached + fs_aE = AFI_interp%f_st + + if (p%UAMod == UA_HGM) then + ! calculate fully attached value: + call AddOrSub2Pi(BL_p%alpha0, alphaE) + cl_fa = (alphaE - BL_p%alpha0) * BL_p%c_lalpha - delta_c_df_primeprime = 0.5_ReKi * (sqrt(fs_aE) - sqrt(x4)) - 0.25_ReKi * (fs_aE - x4) + delta_c_df_primeprime = 0.5_ReKi * (sqrt(fs_aE) - sqrt(x4)) - 0.25_ReKi * (fs_aE - x4) ! Eq. 81 -! bjj: do we need to check that u%alpha is between -pi and + pi? - y%Cl = x4 * (alphaE - BL_p%alpha0) * BL_p%c_lalpha + (1.0_ReKi - x4) * cl_fs + pi * Tu * u%omega ! Eq. 78 - y%Cd = AFI_interp%Cd + (u%alpha - alphaE) * y%Cl + (AFI_interp%Cd - BL_p%Cd0) * delta_c_df_primeprime ! Eq. 79 + ! bjj: do we need to check that u%alpha is between -pi and + pi? + y%Cl = x4 * cl_fa + (1.0_ReKi - x4) * cl_fs + pi * Tu * u%omega ! Eq. 78 + + call AddOrSub2Pi(u%alpha, alphaE) + y%Cd = AFI_interp%Cd + (u%alpha - alphaE) * y%Cl + (AFI_interp%Cd - BL_p%Cd0) * delta_c_df_primeprime ! Eq. 79 - if (AFInfo%ColCm == 0) then ! we don't have a cm column, so make everything 0 - y%Cm = 0.0_ReKi + if (AFInfo%ColCm == 0) then ! we don't have a cm column, so make everything 0 + y%Cm = 0.0_ReKi + else + y%Cm = AFI_interp%Cm + y%Cl * delta_c_mf_primeprime - piBy2 * Tu * u%omega ! Eq. 80 + end if + + y%Cn = y%Cl*cos(u%alpha) + y%Cd*sin(u%alpha) + y%Cc = y%Cl*sin(u%alpha) - y%Cd*cos(u%alpha) else - y%Cm = AFI_interp%Cm + y%Cl * delta_c_mf_primeprime - piBy2 * Tu * u%omega ! Eq. 80 - end if + + ! limit x5?: + x5 = x_in%x(5) + + cn_circ = x4 * AFI_interp%FullyAttached + (1.0_ReKi - x4) * AFI_interp%FullySeparate + x5 + y%Cn = cn_circ + pi * Tu * u%omega + y%Cc = AFI_interp%Cl*sin(alphaE) - AFI_interp%Cd*cos(alphaE) ! static value at alphaE + + y%Cl = y%Cn*cos(u%alpha) + y%Cc*sin(u%alpha) - y%Cn = y%Cl*cos(u%alpha) + y%Cd*sin(u%alpha) - y%Cc = y%Cl*sin(u%alpha) - y%Cd*cos(u%alpha) + ! for cm: + tau_vl = t - OtherState%t_vortexBegin(i,j) + tau_vl = tau_vl / Tu ! make this non-dimensional (to compare with T_VL) + tV_ratio = min(1.5_ReKi, tau_vl/BL_p%T_VL) + + delta_c_df_primeprime = 0.5_ReKi * (sqrt(fs_aE) - sqrt(x4)) - 0.25_ReKi * (fs_aE - x4) ! Eq. 81 + + call AddOrSub2Pi(u%alpha, alphaE) + y%Cd = AFI_interp%Cd + (u%alpha - alphaE) * y%Cn + (AFI_interp%Cd - BL_p%Cd0) * delta_c_df_primeprime ! Eq. 79 + + + if (AFInfo%ColCm == 0) then ! we don't have a cm column, so make everything 0 + y%Cm = 0.0_ReKi + else +! alphaF = x_in%x(3) / BL_p%c_lalpha + BL_p%alpha0 + y%Cm = AFI_interp%Cm + cn_circ * delta_c_mf_primeprime - 0.0_ReKi * piBy2 * Tu * u%omega - 0.25_ReKi*(1.0_ReKi - cos(pi * tV_ratio ))*x5 + end if + + end if ! now check if we should have turned off UA, and modify outputs accordingly (with linear combination of steady outputs) - call UA_BlendSteady(k, u, p, AFInfo, y, misc%FirstWarn_UA_off, misc%weight(i,j), ErrStat2, ErrMsg2) + call UA_BlendSteady(u, p, AFInfo, y, misc%FirstWarn_UA_off, misc%weight(i,j), ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + else M = u%U / p%a_s @@ -2564,7 +2849,7 @@ subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, Err end if ! now check if we should have turned off UA, and modify outputs accordingly (with linear combination of steady outputs) - call UA_BlendSteady(k, u, p, AFInfo, y, misc%FirstWarn_UA_off, misc%weight(i,j), ErrStat2, ErrMsg2) + call UA_BlendSteady(u, p, AFInfo, y, misc%FirstWarn_UA_off, misc%weight(i,j), ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end if @@ -2581,8 +2866,7 @@ subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, Err y%WriteOutput(iOffset+ 6) = y%Cd y%WriteOutput(iOffset+ 7) = y%Cm - if (p%UAMod == UA_HGM) then - + if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then y%WriteOutput(iOffset+ 8) = u%omega*R2D y%WriteOutput(iOffset+ 9) = alphaE*R2D y%WriteOutput(iOffset+10) = Tu @@ -2590,13 +2874,18 @@ subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, Err y%WriteOutput(iOffset+12) = cl_fs y%WriteOutput(iOffset+13) = fs_aE - y%WriteOutput(iOffset+14) = x%element(i,j)%x(1) - y%WriteOutput(iOffset+15) = x%element(i,j)%x(2) - y%WriteOutput(iOffset+16) = x%element(i,j)%x(3) - y%WriteOutput(iOffset+17) = x%element(i,j)%x(4) + y%WriteOutput(iOffset+14) = x_in%x(1) !x%element(i,j)%x(1) + y%WriteOutput(iOffset+15) = x_in%x(2) !x%element(i,j)%x(2) + y%WriteOutput(iOffset+16) = x_in%x(3) !x%element(i,j)%x(3) + y%WriteOutput(iOffset+17) = x_in%x(4) !x%element(i,j)%x(4) y%WriteOutput(iOffset+18) = k y%WriteOutput(iOffset+19) = misc%weight(i,j) + y%WriteOutput(iOffset+20) = cl_fa + if (p%UAMod == UA_HGMV) then + y%WriteOutput(iOffset+21) = x_in%x(5) !x%element(i,j)%x(5) + end if + else y%WriteOutput(iOffset+ 8) = KC%Cn_alpha_q_circ ! CNCP in ADv14 y%WriteOutput(iOffset+ 9) = KC%Cn_alpha_q_nc ! CNIQ in ADv14 @@ -2648,8 +2937,9 @@ subroutine UA_CalcOutput( i, j, u_in, p, x, xd, OtherState, AFInfo, y, misc, Err y%WriteOutput(iOffset+41) = OtherState%sigma3(i, j) y%WriteOutput(iOffset+42) = misc%T_sh(i, j) y%WriteOutput(iOffset+43) = k - y%WriteOutput(iOffset+44) = KC%alpha_filt_cur*R2D - y%WriteOutput(iOffset+45) = misc%weight(i,j) + y%WriteOutput(iOffset+44) = misc%weight(i,j) + y%WriteOutput(iOffset+45) = KC%alpha_filt_cur*R2D + end if end if #endif @@ -2685,6 +2975,183 @@ subroutine UA_WriteOutputToFile(t, p, y) #endif end subroutine UA_WriteOutputToFile +!============================================================================== +subroutine UA_WriteAFIParamsToFile(InitInp, AFInfo, ErrStat, ErrMsg) + type(AFI_ParameterType), intent(in ) :: AFInfo(:) ! The airfoil parameter data (for all airfoils) + type(UA_InitInputType), intent(in ) :: InitInp ! input data for initialization routine + + integer(IntKi), intent( out) :: ErrStat ! Error status of the operation + character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None + + integer :: k + integer(IntKi) :: i + integer(IntKi) :: unOutFile + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'UA_WriteAFIParamsToFile' + character(*), parameter :: delim = ' ' + + integer, parameter :: MaxLen = 16 + integer, parameter :: NumChans = 49 + character(MaxLen) :: ChanName( NumChans) + character(MaxLen) :: ChanUnit( NumChans) + real(ReKi) :: TmpValues(NumChans) + character(3) :: MaxLenStr + character(80) :: Fmt + + MaxLenStr = trim(num2lstr(MaxLen)) + + i=1 + ChanName(i) = 'AirfoilNumber'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'TableNumber'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'alpha0'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'alpha1'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'alpha2'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'eta_e'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'C_nalpha'; ChanUnit(i) = '(-/rad)'; i = i+1; + ChanName(i) = 'C_lalpha'; ChanUnit(i) = '(-/rad)'; i = i+1; + ChanName(i) = 'T_f0'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'T_V0'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'T_p'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'T_VL'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'b1'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'b2'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'b5'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'A1'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'A2'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'A5'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'S1'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'S2'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'S3'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'S4'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'Cn1'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'Cn2'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'St_sh'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'Cd0'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'Cm0'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'k0'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'k1'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'k2'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'k3'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'k1_hat'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'x_cp_bar'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'UACutout'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'UACutout_delta'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'UACutout_blend'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'filtCutOff'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'alphaLowerWrap'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'alphaLower'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'alphaUpper'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'alphaUpperWrap'; ChanUnit(i) = '(deg)'; i = i+1; + ChanName(i) = 'c_alphaLowerWrap'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'c_alphaLower'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'c_alphaUpper'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'c_alphaUpperWrap'; ChanUnit(i) = '(-)'; i = i+1; + ChanName(i) = 'c_RateWrap'; ChanUnit(i) = '(-/rad)'; i = i+1; + ChanName(i) = 'c_RateLower'; ChanUnit(i) = '(-/rad)'; i = i+1; + ChanName(i) = 'c_Rate'; ChanUnit(i) = '(-/rad)'; i = i+1; + ChanName(i) = 'c_RateUpper'; ChanUnit(i) = '(-/rad)'; i = i+1; + + CALL GetNewUnit( unOutFile, ErrStat, ErrMsg ) + IF ( ErrStat /= ErrID_None ) RETURN + + CALL OpenFOutFile ( unOutFile, trim(InitInp%OutRootName)//'.UA.sum', ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + + + ! Generate file outputs + + write (unOutFile,'(/,A)') 'Predictions were generated on '//CurDate()//' at '//CurTime() !//' using '//trim(GetNVD(version)) + write (unOutFile,'(1X,A)') trim(ProgName) + write (unOutFile,'()' ) !print a blank line + write (unOutFile,'()' ) !print a blank line + write (unOutFile,'()' ) !print a blank line + + + !...................................................... + ! Write the names of the output parameters on one line: + !...................................................... + call WrFileNR ( unOutFile, ChanName(1) ) + do i=2,size(ChanName) + call WrFileNR ( unOutFile, delim//ChanName(i) ) + end do + write (unOutFile,'()') + + !...................................................... + ! Write the units of the output parameters on one line: + !...................................................... + call WrFileNR ( unOutFile, ChanUnit(1) ) + do i=2,size(ChanName) + call WrFileNR ( unOutFile, delim//ChanUnit(i) ) + end do + write (unOutFile,'()') + + TmpValues = 0.0_ReKi ! initialize in case UAdata is not included in the airfoil table + !...................................................... + ! Write the data for each table in each file + !...................................................... + Fmt = '(I'//MaxLenStr//',"'//delim//'",I'//MaxLenStr//','//trim(num2lstr(NumChans))//'("'//delim//'",F'//MaxLenStr//'.5))' + do k=1,size(AFInfo) + do i=1,size(AFInfo(k)%Table) + IF (AFInfo(k)%Table(i)%InclUAdata) then + WRITE(unOutFile, Fmt) k, i, & + AFInfo(k)%Table(i)%UA_BL%alpha0*R2D , & + AFInfo(k)%Table(i)%UA_BL%alpha1*R2D , & + AFInfo(k)%Table(i)%UA_BL%alpha2*R2D , & + AFInfo(k)%Table(i)%UA_BL%eta_e , & + AFInfo(k)%Table(i)%UA_BL%C_nalpha , & + AFInfo(k)%Table(i)%UA_BL%C_lalpha , & + AFInfo(k)%Table(i)%UA_BL%T_f0 , & + AFInfo(k)%Table(i)%UA_BL%T_V0 , & + AFInfo(k)%Table(i)%UA_BL%T_p , & + AFInfo(k)%Table(i)%UA_BL%T_VL , & + AFInfo(k)%Table(i)%UA_BL%b1 , & + AFInfo(k)%Table(i)%UA_BL%b2 , & + AFInfo(k)%Table(i)%UA_BL%b5 , & + AFInfo(k)%Table(i)%UA_BL%A1 , & + AFInfo(k)%Table(i)%UA_BL%A2 , & + AFInfo(k)%Table(i)%UA_BL%A5 , & + AFInfo(k)%Table(i)%UA_BL%S1 , & + AFInfo(k)%Table(i)%UA_BL%S2 , & + AFInfo(k)%Table(i)%UA_BL%S3 , & + AFInfo(k)%Table(i)%UA_BL%S4 , & + AFInfo(k)%Table(i)%UA_BL%Cn1 , & + AFInfo(k)%Table(i)%UA_BL%Cn2 , & + AFInfo(k)%Table(i)%UA_BL%St_sh , & + AFInfo(k)%Table(i)%UA_BL%Cd0 , & + AFInfo(k)%Table(i)%UA_BL%Cm0 , & + AFInfo(k)%Table(i)%UA_BL%k0 , & + AFInfo(k)%Table(i)%UA_BL%k1 , & + AFInfo(k)%Table(i)%UA_BL%k2 , & + AFInfo(k)%Table(i)%UA_BL%k3 , & + AFInfo(k)%Table(i)%UA_BL%k1_hat , & + AFInfo(k)%Table(i)%UA_BL%x_cp_bar , & + AFInfo(k)%Table(i)%UA_BL%UACutout*R2D , & + AFInfo(k)%Table(i)%UA_BL%UACutout_delta*R2D, & + AFInfo(k)%Table(i)%UA_BL%UACutout_blend*R2D, & + AFInfo(k)%Table(i)%UA_BL%filtCutOff , & + AFInfo(k)%Table(i)%UA_BL%alphaLowerWrap*R2D, & + AFInfo(k)%Table(i)%UA_BL%alphaLower*R2D , & + AFInfo(k)%Table(i)%UA_BL%alphaUpper*R2D , & + AFInfo(k)%Table(i)%UA_BL%alphaUpperWrap*R2D, & + AFInfo(k)%Table(i)%UA_BL%c_alphaLowerWrap , & + AFInfo(k)%Table(i)%UA_BL%c_alphaLower , & + AFInfo(k)%Table(i)%UA_BL%c_alphaUpper , & + AFInfo(k)%Table(i)%UA_BL%c_alphaUpperWrap , & + AFInfo(k)%Table(i)%UA_BL%c_RateWrap , & + AFInfo(k)%Table(i)%UA_BL%c_RateLower , & + AFInfo(k)%Table(i)%UA_BL%c_Rate , & + AFInfo(k)%Table(i)%UA_BL%c_RateUpper + ELSE + WRITE(unOutFile, Fmt) k, i, TmpValues(3:) + END IF + end do + end do + + close(unOutFile) + +end subroutine UA_WriteAFIParamsToFile !============================================================================== subroutine UA_End(p) type(UA_ParameterType), intent(inout) :: p ! Parameters @@ -2704,8 +3171,7 @@ end subroutine UA_End !============================================================================== !>This subroutine blends the steady outputs with the unsteady-outputs so that !! UA can turn back on if the angle of attack goes back into a reasonable range. -subroutine UA_BlendSteady(k, u, p, AFInfo, y, FirstWarn_UA_off, weight, ErrStat, ErrMsg) - REAL(ReKi), intent(in ) :: k ! reduced frequency +subroutine UA_BlendSteady(u, p, AFInfo, y, FirstWarn_UA_off, weight, ErrStat, ErrMsg) type(UA_InputType), intent(in ) :: u ! "Fixed" Inputs at Time type(UA_ParameterType), intent(in ) :: p ! Parameters type(AFI_ParameterType), intent(in ) :: AFInfo ! The airfoil parameter data @@ -2716,7 +3182,7 @@ subroutine UA_BlendSteady(k, u, p, AFInfo, y, FirstWarn_UA_off, weight, ErrStat, character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None type(AFI_OutputType) :: AFI_steady - REAL(ReKi) :: W1,W2,W3 ! Weights for turning off UA temporarily + REAL(ReKi) :: W1,W2 ! Weights for turning off UA temporarily REAL(ReKi) :: AFI_steady_Cn ! Cn from steady coefficients REAL(ReKi) :: AFI_steady_Cc ! Cc from steady coefficients TYPE(AFI_UA_BL_Type) :: UA_BL ! The tables of Leishman-Beddoes unsteady-aero data for given Re and control setting [-] @@ -2737,9 +3203,7 @@ subroutine UA_BlendSteady(k, u, p, AFInfo, y, FirstWarn_UA_off, weight, ErrStat, W1 = 1.0_ReKi - BlendCosine( abs(u%alpha), UA_BL%UACutout_blend, UA_BL%UACutout ) ! shut off when AoA reaches UACutout, but blend it off 5 degrees before (5 degrees is set in AFI to avoid that math each time) W2 = BlendCosine( abs(u%U), UA_u_min, 1.0_ReKi ) ! turn off UA when inflow velocity is 0 m/s, but start blend it off 1 m/s before (make sure it is greater than u_min) - !W3 = BlendCosine( k, 0.0_ReKi, 0.02_ReKi ) ! turn off UA when reduced frequency is 0, but start blend it off at k=0.02 (this is a quasi-static state) - W3 = 1.0_ReKi - weight = W1*W2*W3 + weight = W1*W2 if (weight < 1.0_ReKi) then @@ -2747,7 +3211,7 @@ subroutine UA_BlendSteady(k, u, p, AFInfo, y, FirstWarn_UA_off, weight, ErrStat, CALL SetErrStat(ErrID_Warn,"Temporarily turning off UA due to high angle of attack or low relative velocity. This warning will not be repeated though the condition may persist.", ErrStat, ErrMsg, RoutineName) FirstWarn_UA_off = .false. end if - + ! calculate the steady coefficients call AFI_ComputeAirfoilCoefs( u%alpha, u%Re, u%UserProp, AFInfo, AFI_steady, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -2767,6 +3231,59 @@ subroutine UA_BlendSteady(k, u, p, AFInfo, y, FirstWarn_UA_off, weight, ErrStat, end subroutine UA_BlendSteady !============================================================================== +!>This subroutine blends the steady outputs with the unsteady-states so that +!! UA can turn back on if the angle of attack goes back into a reasonable range. +subroutine UA_BlendSteadyStates(i, j, u, p, AFInfo, x, FirstWarn_UA_off, weight, ErrStat, ErrMsg) + integer(IntKi), intent(in ) :: i ! node index within a blade + integer(IntKi), intent(in ) :: j ! blade index + type(UA_InputType), intent(in ) :: u ! "Fixed" Inputs at Time + type(UA_ParameterType), intent(in ) :: p ! Parameters + type(AFI_ParameterType), intent(in ) :: AFInfo ! The airfoil parameter data + TYPE(UA_ElementContinuousStateType), INTENT(INOUT) :: x ! Continuous states at t + LOGICAL, intent(inout) :: FirstWarn_UA_off ! flag to determine if warning message should be displayed + REAL(ReKi), intent(inout) :: weight ! scaling weight for UA vs steady outputs + integer(IntKi), intent( out) :: ErrStat ! Error status of the operation + character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None + + type(UA_ElementContinuousStateType) :: x_steady + REAL(ReKi) :: W1,W2 ! Weights for turning off UA temporarily + TYPE(AFI_UA_BL_Type) :: UA_BL ! The tables of Leishman-Beddoes unsteady-aero data for given Re and control setting [-] + INTEGER(IntKi) :: ErrStat2 + CHARACTER(ErrMsgLen) :: ErrMsg2 + CHARACTER(*), PARAMETER :: RoutineName = 'UA_BlendSteadyStates' + + ErrStat = ErrID_None + ErrMsg = "" + + weight = 1.0_ReKi ! default in case of error + + ! Determine what the cutout angle of attack is + call AFI_ComputeUACoefs( AFInfo, u%Re, u%UserProp, UA_BL, ErrMsg, ErrStat ) + if (ErrStat >= AbortErrLev) return ! would only have error if there is a memory problem + + ! put alpha in [-pi,pi] before checking its value + + W1 = 1.0_ReKi - BlendCosine( abs(u%alpha), UA_BL%UACutout_blend, UA_BL%UACutout ) ! shut off when AoA reaches UACutout, but blend it off 5 degrees before (5 degrees is set in AFI to avoid that math each time) + W2 = BlendCosine( abs(u%U), UA_u_min, 1.0_ReKi ) ! turn off UA when inflow velocity is 0 m/s, but start blend it off 1 m/s before (make sure it is greater than u_min) + weight = W1*W2 + + if (weight < 1.0_ReKi) then + + if (FirstWarn_UA_off) then + CALL SetErrStat(ErrID_Warn,"Temporarily turning off UA due to high angle of attack or low relative velocity. This warning will not be repeated though the condition may persist.", ErrStat, ErrMsg, RoutineName) + FirstWarn_UA_off = .false. + end if + + ! calculate the states when at steady state + call HGM_Steady( i, j, u, p, x_steady, AFInfo, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + + x%x = weight * x%x + (1.0_ReKi - weight) * x_steady%x + end if + + +end subroutine UA_BlendSteadyStates +!============================================================================== !> This subroutine checks that the Mach number is valid. If M > 0.3, the theory !! is invalid. If M > 1, numerical issues result in the code. subroutine UA_CheckMachNumber(M, FirstWarn_M, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/UnsteadyAero_Driver.f90 b/modules/aerodyn/src/UnsteadyAero_Driver.f90 index e01fed2d4..1bb9c5cc2 100644 --- a/modules/aerodyn/src/UnsteadyAero_Driver.f90 +++ b/modules/aerodyn/src/UnsteadyAero_Driver.f90 @@ -114,6 +114,14 @@ program UnsteadyAero_Driver call checkError() end if + allocate( InitInData%UAOff_innerNode(InitInData%numBlades), InitInData%UAOff_outerNode(InitInData%numBlades), STAT = ErrStat) + if ( ErrStat /= 0 ) then + call SetErrStat( ErrID_Fatal, 'Error trying to allocate UAOff_innerNode and UAOff_outerNode.', ErrStat, ErrMsg, RoutineName) + call checkError() + end if + ! don't turn off UA based on span location: + InitInData%UAOff_innerNode = 0 + InitInData%UAOff_outerNode = InitInData%nNodesPerBlade + 1 ! Parse the driver input file and run the simulation based on that file @@ -149,6 +157,8 @@ program UnsteadyAero_Driver end if InitInData%OutRootName = dvrInitInp%OutRootName + + InitInData%WrSum = .true. ! write all the AFI data if ( dvrInitInp%SimMod == 1 ) then @@ -183,7 +193,7 @@ program UnsteadyAero_Driver if (p%NumOuts <= 0) then ErrStat = ErrID_Fatal - ErrMsg = "No outputs have been selected. Rebuild the executable with -DUA_OUT" + ErrMsg = "No outputs have been selected. Rebuild the executable with -DUA_OUTS" call checkError() end if @@ -216,7 +226,7 @@ program UnsteadyAero_Driver t = uTimes(2) ! Use existing states to compute the outputs - call UA_CalcOutput(i, j, u(2), p, x, xd, OtherState, AFI_Params(AFIndx(i,j)), y, m, errStat, errMsg ) + call UA_CalcOutput(i, j, t, u(2), p, x, xd, OtherState, AFI_Params(AFIndx(i,j)), y, m, errStat, errMsg ) call checkError() ! Generate file outputs diff --git a/modules/aerodyn/src/UnsteadyAero_Registry.txt b/modules/aerodyn/src/UnsteadyAero_Registry.txt index 6b9ccafe9..562e6adaf 100644 --- a/modules/aerodyn/src/UnsteadyAero_Registry.txt +++ b/modules/aerodyn/src/UnsteadyAero_Registry.txt @@ -16,6 +16,13 @@ include Registry_NWTC_Library.txt usefrom AirfoilInfo_Registry.txt # # + +param UnsteadyAero/UA - INTEGER UA_Baseline - 1 - "UAMod = 1 [Baseline model (Original)]" - +param UnsteadyAero/UA - INTEGER UA_Gonzalez - 2 - "UAMod = 2 [Gonzalez's variant (changes in Cn,Cc,Cm)]" - +param UnsteadyAero/UA - INTEGER UA_MinnemaPierce - 3 - "[Minnema/Pierce variant (changes in Cc and Cm)]" - +param UnsteadyAero/UA - INTEGER UA_HGM - 4 - "[continuous variant of HGM (Hansen) model]" - +param UnsteadyAero/UA - INTEGER UA_HGMV - 5 - "[continuous variant of HGM (Hansen) model with vortex modifications]" - + # ..... Initialization data ....................................................................................................... # Define inputs that the initialization routine may need here: # e.g., the name of the input file, the file root name,etc. @@ -29,6 +36,9 @@ typedef ^ ^ INTEGER typedef ^ ^ ReKi a_s - - - "speed of sound" m/s typedef ^ ^ LOGICAL Flookup - - - "Use table lookup for f' and f'' " - 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)" - +typedef ^ ^ LOGICAL WrSum - .false. - "Write UA AFI parameters to summary file?" - +typedef ^ ^ INTEGER UAOff_innerNode {:} - - "Last node on each blade where UA should be turned off based on span location from blade root (0 if always on)" - +typedef ^ ^ INTEGER UAOff_outerNode {:} - - "First node on each blade where UA should be turned off based on span location from blade tip (>nNodesPerBlade if always on)" - # # Define outputs from the initialization routine here: @@ -93,7 +103,7 @@ typedef ^ UA_KelvinChainType ReKi # ..... States .................................................................................................................... # Define continuous (differentiable) states here: -typedef ^ UA_ElementContinuousStateType R8Ki x 4 - - "continuous states when UA_Mod=4 (x1 and x2:Downwash memory terms; x3:Clp', Lift coefficient with a time lag to the attached lift coeff; x4: f'' , Final separation point function)" "{rad, rad, - -}" +typedef ^ UA_ElementContinuousStateType R8Ki x 5 - - "continuous states when UA_Mod=4 (x1 and x2:Downwash memory terms; x3:Clp', Lift coefficient with a time lag to the attached lift coeff; x4: f'' , Final separation point function)" "{rad, rad, - -}" typedef ^ ContinuousStateType UA_ElementContinuousStateType element {:}{:} - - "continuous states when UA_Mod=4 for each blade/node" "-" # Define discrete (non-differentiable) states here: @@ -138,14 +148,18 @@ typedef ^ ConstraintStateType ReKi # # # Define any other states, including integer or logical states here: -typedef ^ OtherStateType LOGICAL UA_off_forGood {:}{:} - - "logical flag indicating if UA is off for good" - typedef ^ OtherStateType LOGICAL FirstPass {:}{:} - - "logical flag indicating if this is the first time step" - typedef ^ OtherStateType ReKi sigma1 {:}{:} - - "multiplier for T_fn" - typedef ^ OtherStateType ReKi sigma1c {:}{:} - - "multiplier for T_fc" - typedef ^ OtherStateType ReKi sigma1m {:}{:} - - "multiplier for T_fm" - typedef ^ OtherStateType ReKi sigma3 {:}{:} - - "multiplier for T_V" - typedef ^ OtherStateType IntKi n {:}{:} - - "counter for continuous state integration" - -typedef ^ OtherStateType UA_ContinuousStateType xdot 4 - - "counter for continuous state integration" - +typedef ^ OtherStateType UA_ContinuousStateType xdot 4 - - "history states for continuous state integration" - +typedef ^ OtherStateType ReKi t_vortexBegin {:}{:} - - "HGMV model: simulation time when vortex lift term became active" s +typedef ^ OtherStateType ReKi SignOfOmega {:}{:} - - "HGMV model: sign of omega when vortex lift term became active " s +typedef ^ OtherStateType LOGICAL PositivePressure {:}{:} - - "HGMV model: logical flag indicating if the vortex lift became active because of positive pressure (or negative)" - +typedef ^ OtherStateType LOGICAL vortexOn {:}{:} - - "HGMV model: logical flag indicating if the vortex lift term is active" - +typedef ^ OtherStateType LOGICAL BelowThreshold {:}{:} - - "HGMV model: logical flag indicating if cn fell below threshold to form another vortex" - # ..... Misc/Optimization variables................................................................................................. # Define any data that are used only for efficiency purposes (these variables are not associated with time): @@ -179,6 +193,7 @@ typedef ^ ^ CHARACTER(1 typedef ^ ^ INTEGER UnOutFile - 0 - "File unit for the UnsteadyAero outputs" - typedef ^ ^ Logical ShedEffect - - - "Include the effect of shed vorticity. If False, the input alpha is assumed to already contain this effect (e.g. vortex methods)" - typedef ^ ParameterType IntKi lin_nx - 0 - "Number of continuous states for linearization" - +typedef ^ ^ LOGICAL UA_off_forGood {:}{:} - - "logical flag indicating if UA is off for good" - # ..... Inputs .................................................................................................................... # Define inputs that are contained on the mesh here: diff --git a/modules/aerodyn/src/UnsteadyAero_Types.f90 b/modules/aerodyn/src/UnsteadyAero_Types.f90 index a4f5a156b..5ac45e954 100644 --- a/modules/aerodyn/src/UnsteadyAero_Types.f90 +++ b/modules/aerodyn/src/UnsteadyAero_Types.f90 @@ -34,6 +34,11 @@ MODULE UnsteadyAero_Types USE AirfoilInfo_Types USE NWTC_Library IMPLICIT NONE + INTEGER(IntKi), PUBLIC, PARAMETER :: UA_Baseline = 1 ! UAMod = 1 [Baseline model (Original)] [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: UA_Gonzalez = 2 ! UAMod = 2 [Gonzalez's variant (changes in Cn,Cc,Cm)] [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: UA_MinnemaPierce = 3 ! [Minnema/Pierce variant (changes in Cc and Cm)] [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: UA_HGM = 4 ! [continuous variant of HGM (Hansen) model] [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: UA_HGMV = 5 ! [continuous variant of HGM (Hansen) model with vortex modifications] [-] ! ========= UA_InitInputType ======= TYPE, PUBLIC :: UA_InitInputType REAL(DbKi) :: dt !< time step [s] @@ -45,6 +50,9 @@ MODULE UnsteadyAero_Types REAL(ReKi) :: a_s !< speed of sound [m/s] LOGICAL :: Flookup !< Use table lookup for f' and f'' [-] 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) [-] + LOGICAL :: WrSum = .false. !< Write UA AFI parameters to summary file? [-] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: UAOff_innerNode !< Last node on each blade where UA should be turned off based on span location from blade root (0 if always on) [-] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: UAOff_outerNode !< First node on each blade where UA should be turned off based on span location from blade tip (>nNodesPerBlade if always on) [-] END TYPE UA_InitInputType ! ======================= ! ========= UA_InitOutputType ======= @@ -109,7 +117,7 @@ MODULE UnsteadyAero_Types ! ======================= ! ========= UA_ElementContinuousStateType ======= TYPE, PUBLIC :: UA_ElementContinuousStateType - REAL(R8Ki) , DIMENSION(1:4) :: x !< continuous states when UA_Mod=4 (x1 and x2:Downwash memory terms; x3:Clp', Lift coefficient with a time lag to the attached lift coeff; x4: f'' , Final separation point function) [{rad, rad, - -}] + REAL(R8Ki) , DIMENSION(1:5) :: x !< continuous states when UA_Mod=4 (x1 and x2:Downwash memory terms; x3:Clp', Lift coefficient with a time lag to the attached lift coeff; x4: f'' , Final separation point function) [{rad, rad, - -}] END TYPE UA_ElementContinuousStateType ! ======================= ! ========= UA_ContinuousStateType ======= @@ -160,14 +168,18 @@ MODULE UnsteadyAero_Types ! ======================= ! ========= UA_OtherStateType ======= TYPE, PUBLIC :: UA_OtherStateType - LOGICAL , DIMENSION(:,:), ALLOCATABLE :: UA_off_forGood !< logical flag indicating if UA is off for good [-] LOGICAL , DIMENSION(:,:), ALLOCATABLE :: FirstPass !< logical flag indicating if this is the first time step [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: sigma1 !< multiplier for T_fn [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: sigma1c !< multiplier for T_fc [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: sigma1m !< multiplier for T_fm [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: sigma3 !< multiplier for T_V [-] INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: n !< counter for continuous state integration [-] - TYPE(UA_ContinuousStateType) , DIMENSION(1:4) :: xdot !< counter for continuous state integration [-] + TYPE(UA_ContinuousStateType) , DIMENSION(1:4) :: xdot !< history states for continuous state integration [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: t_vortexBegin !< HGMV model: simulation time when vortex lift term became active [s] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: SignOfOmega !< HGMV model: sign of omega when vortex lift term became active [s] + LOGICAL , DIMENSION(:,:), ALLOCATABLE :: PositivePressure !< HGMV model: logical flag indicating if the vortex lift became active because of positive pressure (or negative) [-] + LOGICAL , DIMENSION(:,:), ALLOCATABLE :: vortexOn !< HGMV model: logical flag indicating if the vortex lift term is active [-] + LOGICAL , DIMENSION(:,:), ALLOCATABLE :: BelowThreshold !< HGMV model: logical flag indicating if cn fell below threshold to form another vortex [-] END TYPE UA_OtherStateType ! ======================= ! ========= UA_MiscVarType ======= @@ -200,6 +212,7 @@ MODULE UnsteadyAero_Types INTEGER(IntKi) :: UnOutFile = 0 !< File unit for the UnsteadyAero outputs [-] LOGICAL :: ShedEffect !< Include the effect of shed vorticity. If False, the input alpha is assumed to already contain this effect (e.g. vortex methods) [-] INTEGER(IntKi) :: lin_nx = 0 !< Number of continuous states for linearization [-] + LOGICAL , DIMENSION(:,:), ALLOCATABLE :: UA_off_forGood !< logical flag indicating if UA is off for good [-] END TYPE UA_ParameterType ! ======================= ! ========= UA_InputType ======= @@ -261,6 +274,31 @@ SUBROUTINE UA_CopyInitInput( SrcInitInputData, DstInitInputData, CtrlCode, ErrSt DstInitInputData%a_s = SrcInitInputData%a_s DstInitInputData%Flookup = SrcInitInputData%Flookup DstInitInputData%ShedEffect = SrcInitInputData%ShedEffect + DstInitInputData%WrSum = SrcInitInputData%WrSum +IF (ALLOCATED(SrcInitInputData%UAOff_innerNode)) THEN + i1_l = LBOUND(SrcInitInputData%UAOff_innerNode,1) + i1_u = UBOUND(SrcInitInputData%UAOff_innerNode,1) + IF (.NOT. ALLOCATED(DstInitInputData%UAOff_innerNode)) THEN + ALLOCATE(DstInitInputData%UAOff_innerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%UAOff_innerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%UAOff_innerNode = SrcInitInputData%UAOff_innerNode +ENDIF +IF (ALLOCATED(SrcInitInputData%UAOff_outerNode)) THEN + i1_l = LBOUND(SrcInitInputData%UAOff_outerNode,1) + i1_u = UBOUND(SrcInitInputData%UAOff_outerNode,1) + IF (.NOT. ALLOCATED(DstInitInputData%UAOff_outerNode)) THEN + ALLOCATE(DstInitInputData%UAOff_outerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%UAOff_outerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstInitInputData%UAOff_outerNode = SrcInitInputData%UAOff_outerNode +ENDIF END SUBROUTINE UA_CopyInitInput SUBROUTINE UA_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) @@ -274,6 +312,12 @@ SUBROUTINE UA_DestroyInitInput( InitInputData, ErrStat, ErrMsg ) ErrMsg = "" IF (ALLOCATED(InitInputData%c)) THEN DEALLOCATE(InitInputData%c) +ENDIF +IF (ALLOCATED(InitInputData%UAOff_innerNode)) THEN + DEALLOCATE(InitInputData%UAOff_innerNode) +ENDIF +IF (ALLOCATED(InitInputData%UAOff_outerNode)) THEN + DEALLOCATE(InitInputData%UAOff_outerNode) ENDIF END SUBROUTINE UA_DestroyInitInput @@ -325,6 +369,17 @@ SUBROUTINE UA_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Re_BufSz = Re_BufSz + 1 ! a_s Int_BufSz = Int_BufSz + 1 ! Flookup Int_BufSz = Int_BufSz + 1 ! ShedEffect + Int_BufSz = Int_BufSz + 1 ! WrSum + Int_BufSz = Int_BufSz + 1 ! UAOff_innerNode allocated yes/no + IF ( ALLOCATED(InData%UAOff_innerNode) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! UAOff_innerNode upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%UAOff_innerNode) ! UAOff_innerNode + END IF + Int_BufSz = Int_BufSz + 1 ! UAOff_outerNode allocated yes/no + IF ( ALLOCATED(InData%UAOff_outerNode) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! UAOff_outerNode upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%UAOff_outerNode) ! UAOff_outerNode + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -390,6 +445,38 @@ SUBROUTINE UA_PackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = TRANSFER(InData%ShedEffect, IntKiBuf(1)) Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%WrSum, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%UAOff_innerNode) ) 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%UAOff_innerNode,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UAOff_innerNode,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%UAOff_innerNode,1), UBOUND(InData%UAOff_innerNode,1) + IntKiBuf(Int_Xferred) = InData%UAOff_innerNode(i1) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + IF ( .NOT. ALLOCATED(InData%UAOff_outerNode) ) 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%UAOff_outerNode,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UAOff_outerNode,1) + Int_Xferred = Int_Xferred + 2 + + DO i1 = LBOUND(InData%UAOff_outerNode,1), UBOUND(InData%UAOff_outerNode,1) + IntKiBuf(Int_Xferred) = InData%UAOff_outerNode(i1) + Int_Xferred = Int_Xferred + 1 + END DO + END IF END SUBROUTINE UA_PackInitInput SUBROUTINE UA_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -461,6 +548,44 @@ SUBROUTINE UA_UnPackInitInput( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err Int_Xferred = Int_Xferred + 1 OutData%ShedEffect = TRANSFER(IntKiBuf(Int_Xferred), OutData%ShedEffect) Int_Xferred = Int_Xferred + 1 + OutData%WrSum = TRANSFER(IntKiBuf(Int_Xferred), OutData%WrSum) + Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UAOff_innerNode 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%UAOff_innerNode)) DEALLOCATE(OutData%UAOff_innerNode) + ALLOCATE(OutData%UAOff_innerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UAOff_innerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%UAOff_innerNode,1), UBOUND(OutData%UAOff_innerNode,1) + OutData%UAOff_innerNode(i1) = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + END DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UAOff_outerNode 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%UAOff_outerNode)) DEALLOCATE(OutData%UAOff_outerNode) + ALLOCATE(OutData%UAOff_outerNode(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UAOff_outerNode.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i1 = LBOUND(OutData%UAOff_outerNode,1), UBOUND(OutData%UAOff_outerNode,1) + OutData%UAOff_outerNode(i1) = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 + END DO + END IF END SUBROUTINE UA_UnPackInitInput SUBROUTINE UA_CopyInitOutput( SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg ) @@ -3962,20 +4087,6 @@ SUBROUTINE UA_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, Er ! ErrStat = ErrID_None ErrMsg = "" -IF (ALLOCATED(SrcOtherStateData%UA_off_forGood)) THEN - i1_l = LBOUND(SrcOtherStateData%UA_off_forGood,1) - i1_u = UBOUND(SrcOtherStateData%UA_off_forGood,1) - i2_l = LBOUND(SrcOtherStateData%UA_off_forGood,2) - i2_u = UBOUND(SrcOtherStateData%UA_off_forGood,2) - IF (.NOT. ALLOCATED(DstOtherStateData%UA_off_forGood)) THEN - ALLOCATE(DstOtherStateData%UA_off_forGood(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%UA_off_forGood.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstOtherStateData%UA_off_forGood = SrcOtherStateData%UA_off_forGood -ENDIF IF (ALLOCATED(SrcOtherStateData%FirstPass)) THEN i1_l = LBOUND(SrcOtherStateData%FirstPass,1) i1_u = UBOUND(SrcOtherStateData%FirstPass,1) @@ -4065,6 +4176,76 @@ SUBROUTINE UA_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, Er CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN ENDDO +IF (ALLOCATED(SrcOtherStateData%t_vortexBegin)) THEN + i1_l = LBOUND(SrcOtherStateData%t_vortexBegin,1) + i1_u = UBOUND(SrcOtherStateData%t_vortexBegin,1) + i2_l = LBOUND(SrcOtherStateData%t_vortexBegin,2) + i2_u = UBOUND(SrcOtherStateData%t_vortexBegin,2) + IF (.NOT. ALLOCATED(DstOtherStateData%t_vortexBegin)) THEN + ALLOCATE(DstOtherStateData%t_vortexBegin(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%t_vortexBegin.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%t_vortexBegin = SrcOtherStateData%t_vortexBegin +ENDIF +IF (ALLOCATED(SrcOtherStateData%SignOfOmega)) THEN + i1_l = LBOUND(SrcOtherStateData%SignOfOmega,1) + i1_u = UBOUND(SrcOtherStateData%SignOfOmega,1) + i2_l = LBOUND(SrcOtherStateData%SignOfOmega,2) + i2_u = UBOUND(SrcOtherStateData%SignOfOmega,2) + IF (.NOT. ALLOCATED(DstOtherStateData%SignOfOmega)) THEN + ALLOCATE(DstOtherStateData%SignOfOmega(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%SignOfOmega.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%SignOfOmega = SrcOtherStateData%SignOfOmega +ENDIF +IF (ALLOCATED(SrcOtherStateData%PositivePressure)) THEN + i1_l = LBOUND(SrcOtherStateData%PositivePressure,1) + i1_u = UBOUND(SrcOtherStateData%PositivePressure,1) + i2_l = LBOUND(SrcOtherStateData%PositivePressure,2) + i2_u = UBOUND(SrcOtherStateData%PositivePressure,2) + IF (.NOT. ALLOCATED(DstOtherStateData%PositivePressure)) THEN + ALLOCATE(DstOtherStateData%PositivePressure(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%PositivePressure.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%PositivePressure = SrcOtherStateData%PositivePressure +ENDIF +IF (ALLOCATED(SrcOtherStateData%vortexOn)) THEN + i1_l = LBOUND(SrcOtherStateData%vortexOn,1) + i1_u = UBOUND(SrcOtherStateData%vortexOn,1) + i2_l = LBOUND(SrcOtherStateData%vortexOn,2) + i2_u = UBOUND(SrcOtherStateData%vortexOn,2) + IF (.NOT. ALLOCATED(DstOtherStateData%vortexOn)) THEN + ALLOCATE(DstOtherStateData%vortexOn(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%vortexOn.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%vortexOn = SrcOtherStateData%vortexOn +ENDIF +IF (ALLOCATED(SrcOtherStateData%BelowThreshold)) THEN + i1_l = LBOUND(SrcOtherStateData%BelowThreshold,1) + i1_u = UBOUND(SrcOtherStateData%BelowThreshold,1) + i2_l = LBOUND(SrcOtherStateData%BelowThreshold,2) + i2_u = UBOUND(SrcOtherStateData%BelowThreshold,2) + IF (.NOT. ALLOCATED(DstOtherStateData%BelowThreshold)) THEN + ALLOCATE(DstOtherStateData%BelowThreshold(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%BelowThreshold.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%BelowThreshold = SrcOtherStateData%BelowThreshold +ENDIF END SUBROUTINE UA_CopyOtherState SUBROUTINE UA_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) @@ -4076,9 +4257,6 @@ SUBROUTINE UA_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" -IF (ALLOCATED(OtherStateData%UA_off_forGood)) THEN - DEALLOCATE(OtherStateData%UA_off_forGood) -ENDIF IF (ALLOCATED(OtherStateData%FirstPass)) THEN DEALLOCATE(OtherStateData%FirstPass) ENDIF @@ -4100,6 +4278,21 @@ SUBROUTINE UA_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) DO i1 = LBOUND(OtherStateData%xdot,1), UBOUND(OtherStateData%xdot,1) CALL UA_DestroyContState( OtherStateData%xdot(i1), ErrStat, ErrMsg ) ENDDO +IF (ALLOCATED(OtherStateData%t_vortexBegin)) THEN + DEALLOCATE(OtherStateData%t_vortexBegin) +ENDIF +IF (ALLOCATED(OtherStateData%SignOfOmega)) THEN + DEALLOCATE(OtherStateData%SignOfOmega) +ENDIF +IF (ALLOCATED(OtherStateData%PositivePressure)) THEN + DEALLOCATE(OtherStateData%PositivePressure) +ENDIF +IF (ALLOCATED(OtherStateData%vortexOn)) THEN + DEALLOCATE(OtherStateData%vortexOn) +ENDIF +IF (ALLOCATED(OtherStateData%BelowThreshold)) THEN + DEALLOCATE(OtherStateData%BelowThreshold) +ENDIF END SUBROUTINE UA_DestroyOtherState SUBROUTINE UA_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, SizeOnly ) @@ -4137,11 +4330,6 @@ SUBROUTINE UA_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Re_BufSz = 0 Db_BufSz = 0 Int_BufSz = 0 - Int_BufSz = Int_BufSz + 1 ! UA_off_forGood allocated yes/no - IF ( ALLOCATED(InData%UA_off_forGood) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! UA_off_forGood upper/lower bounds for each dimension - Int_BufSz = Int_BufSz + SIZE(InData%UA_off_forGood) ! UA_off_forGood - END IF Int_BufSz = Int_BufSz + 1 ! FirstPass allocated yes/no IF ( ALLOCATED(InData%FirstPass) ) THEN Int_BufSz = Int_BufSz + 2*2 ! FirstPass upper/lower bounds for each dimension @@ -4192,6 +4380,31 @@ SUBROUTINE UA_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs DEALLOCATE(Int_Buf) END IF END DO + Int_BufSz = Int_BufSz + 1 ! t_vortexBegin allocated yes/no + IF ( ALLOCATED(InData%t_vortexBegin) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! t_vortexBegin upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%t_vortexBegin) ! t_vortexBegin + END IF + Int_BufSz = Int_BufSz + 1 ! SignOfOmega allocated yes/no + IF ( ALLOCATED(InData%SignOfOmega) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! SignOfOmega upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%SignOfOmega) ! SignOfOmega + END IF + Int_BufSz = Int_BufSz + 1 ! PositivePressure allocated yes/no + IF ( ALLOCATED(InData%PositivePressure) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! PositivePressure upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%PositivePressure) ! PositivePressure + END IF + Int_BufSz = Int_BufSz + 1 ! vortexOn allocated yes/no + IF ( ALLOCATED(InData%vortexOn) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! vortexOn upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%vortexOn) ! vortexOn + END IF + Int_BufSz = Int_BufSz + 1 ! BelowThreshold allocated yes/no + IF ( ALLOCATED(InData%BelowThreshold) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! BelowThreshold upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%BelowThreshold) ! BelowThreshold + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4219,26 +4432,6 @@ SUBROUTINE UA_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Db_Xferred = 1 Int_Xferred = 1 - IF ( .NOT. ALLOCATED(InData%UA_off_forGood) ) 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_off_forGood,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_off_forGood,1) - Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%UA_off_forGood,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_off_forGood,2) - Int_Xferred = Int_Xferred + 2 - - DO i2 = LBOUND(InData%UA_off_forGood,2), UBOUND(InData%UA_off_forGood,2) - DO i1 = LBOUND(InData%UA_off_forGood,1), UBOUND(InData%UA_off_forGood,1) - IntKiBuf(Int_Xferred) = TRANSFER(InData%UA_off_forGood(i1,i2), IntKiBuf(1)) - Int_Xferred = Int_Xferred + 1 - END DO - END DO - END IF IF ( .NOT. ALLOCATED(InData%FirstPass) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -4389,6 +4582,106 @@ SUBROUTINE UA_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF END DO + IF ( .NOT. ALLOCATED(InData%t_vortexBegin) ) 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%t_vortexBegin,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%t_vortexBegin,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%t_vortexBegin,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%t_vortexBegin,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%t_vortexBegin,2), UBOUND(InData%t_vortexBegin,2) + DO i1 = LBOUND(InData%t_vortexBegin,1), UBOUND(InData%t_vortexBegin,1) + ReKiBuf(Re_Xferred) = InData%t_vortexBegin(i1,i2) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF + IF ( .NOT. ALLOCATED(InData%SignOfOmega) ) 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%SignOfOmega,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%SignOfOmega,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%SignOfOmega,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%SignOfOmega,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%SignOfOmega,2), UBOUND(InData%SignOfOmega,2) + DO i1 = LBOUND(InData%SignOfOmega,1), UBOUND(InData%SignOfOmega,1) + ReKiBuf(Re_Xferred) = InData%SignOfOmega(i1,i2) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF + IF ( .NOT. ALLOCATED(InData%PositivePressure) ) 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%PositivePressure,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%PositivePressure,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%PositivePressure,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%PositivePressure,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%PositivePressure,2), UBOUND(InData%PositivePressure,2) + DO i1 = LBOUND(InData%PositivePressure,1), UBOUND(InData%PositivePressure,1) + IntKiBuf(Int_Xferred) = TRANSFER(InData%PositivePressure(i1,i2), IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF + IF ( .NOT. ALLOCATED(InData%vortexOn) ) 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%vortexOn,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%vortexOn,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%vortexOn,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%vortexOn,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%vortexOn,2), UBOUND(InData%vortexOn,2) + DO i1 = LBOUND(InData%vortexOn,1), UBOUND(InData%vortexOn,1) + IntKiBuf(Int_Xferred) = TRANSFER(InData%vortexOn(i1,i2), IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF + IF ( .NOT. ALLOCATED(InData%BelowThreshold) ) 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%BelowThreshold,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%BelowThreshold,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%BelowThreshold,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%BelowThreshold,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%BelowThreshold,2), UBOUND(InData%BelowThreshold,2) + DO i1 = LBOUND(InData%BelowThreshold,1), UBOUND(InData%BelowThreshold,1) + IntKiBuf(Int_Xferred) = TRANSFER(InData%BelowThreshold(i1,i2), IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF END SUBROUTINE UA_PackOtherState SUBROUTINE UA_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -4419,29 +4712,6 @@ SUBROUTINE UA_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er Re_Xferred = 1 Db_Xferred = 1 Int_Xferred = 1 - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UA_off_forGood 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_off_forGood)) DEALLOCATE(OutData%UA_off_forGood) - ALLOCATE(OutData%UA_off_forGood(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UA_off_forGood.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - DO i2 = LBOUND(OutData%UA_off_forGood,2), UBOUND(OutData%UA_off_forGood,2) - DO i1 = LBOUND(OutData%UA_off_forGood,1), UBOUND(OutData%UA_off_forGood,1) - OutData%UA_off_forGood(i1,i2) = TRANSFER(IntKiBuf(Int_Xferred), OutData%UA_off_forGood(i1,i2)) - Int_Xferred = Int_Xferred + 1 - END DO - END DO - END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! FirstPass not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -4624,6 +4894,121 @@ SUBROUTINE UA_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er IF(ALLOCATED(Db_Buf )) DEALLOCATE(Db_Buf ) IF(ALLOCATED(Int_Buf)) DEALLOCATE(Int_Buf) END DO + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! t_vortexBegin 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%t_vortexBegin)) DEALLOCATE(OutData%t_vortexBegin) + ALLOCATE(OutData%t_vortexBegin(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%t_vortexBegin.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%t_vortexBegin,2), UBOUND(OutData%t_vortexBegin,2) + DO i1 = LBOUND(OutData%t_vortexBegin,1), UBOUND(OutData%t_vortexBegin,1) + OutData%t_vortexBegin(i1,i2) = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! SignOfOmega 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%SignOfOmega)) DEALLOCATE(OutData%SignOfOmega) + ALLOCATE(OutData%SignOfOmega(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%SignOfOmega.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%SignOfOmega,2), UBOUND(OutData%SignOfOmega,2) + DO i1 = LBOUND(OutData%SignOfOmega,1), UBOUND(OutData%SignOfOmega,1) + OutData%SignOfOmega(i1,i2) = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! PositivePressure 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%PositivePressure)) DEALLOCATE(OutData%PositivePressure) + ALLOCATE(OutData%PositivePressure(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%PositivePressure.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%PositivePressure,2), UBOUND(OutData%PositivePressure,2) + DO i1 = LBOUND(OutData%PositivePressure,1), UBOUND(OutData%PositivePressure,1) + OutData%PositivePressure(i1,i2) = TRANSFER(IntKiBuf(Int_Xferred), OutData%PositivePressure(i1,i2)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! vortexOn 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%vortexOn)) DEALLOCATE(OutData%vortexOn) + ALLOCATE(OutData%vortexOn(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%vortexOn.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%vortexOn,2), UBOUND(OutData%vortexOn,2) + DO i1 = LBOUND(OutData%vortexOn,1), UBOUND(OutData%vortexOn,1) + OutData%vortexOn(i1,i2) = TRANSFER(IntKiBuf(Int_Xferred), OutData%vortexOn(i1,i2)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! BelowThreshold 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%BelowThreshold)) DEALLOCATE(OutData%BelowThreshold) + ALLOCATE(OutData%BelowThreshold(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%BelowThreshold.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%BelowThreshold,2), UBOUND(OutData%BelowThreshold,2) + DO i1 = LBOUND(OutData%BelowThreshold,1), UBOUND(OutData%BelowThreshold,1) + OutData%BelowThreshold(i1,i2) = TRANSFER(IntKiBuf(Int_Xferred), OutData%BelowThreshold(i1,i2)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF END SUBROUTINE UA_UnPackOtherState SUBROUTINE UA_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) @@ -5201,6 +5586,20 @@ SUBROUTINE UA_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg ) DstParamData%UnOutFile = SrcParamData%UnOutFile DstParamData%ShedEffect = SrcParamData%ShedEffect DstParamData%lin_nx = SrcParamData%lin_nx +IF (ALLOCATED(SrcParamData%UA_off_forGood)) THEN + i1_l = LBOUND(SrcParamData%UA_off_forGood,1) + i1_u = UBOUND(SrcParamData%UA_off_forGood,1) + i2_l = LBOUND(SrcParamData%UA_off_forGood,2) + i2_u = UBOUND(SrcParamData%UA_off_forGood,2) + IF (.NOT. ALLOCATED(DstParamData%UA_off_forGood)) THEN + ALLOCATE(DstParamData%UA_off_forGood(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%UA_off_forGood.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstParamData%UA_off_forGood = SrcParamData%UA_off_forGood +ENDIF END SUBROUTINE UA_CopyParam SUBROUTINE UA_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -5214,6 +5613,9 @@ SUBROUTINE UA_DestroyParam( ParamData, ErrStat, ErrMsg ) ErrMsg = "" IF (ALLOCATED(ParamData%c)) THEN DEALLOCATE(ParamData%c) +ENDIF +IF (ALLOCATED(ParamData%UA_off_forGood)) THEN + DEALLOCATE(ParamData%UA_off_forGood) ENDIF END SUBROUTINE UA_DestroyParam @@ -5271,6 +5673,11 @@ SUBROUTINE UA_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_BufSz = Int_BufSz + 1 ! UnOutFile Int_BufSz = Int_BufSz + 1 ! ShedEffect Int_BufSz = Int_BufSz + 1 ! lin_nx + Int_BufSz = Int_BufSz + 1 ! UA_off_forGood allocated yes/no + IF ( ALLOCATED(InData%UA_off_forGood) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! UA_off_forGood upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%UA_off_forGood) ! UA_off_forGood + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -5352,6 +5759,26 @@ SUBROUTINE UA_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = InData%lin_nx Int_Xferred = Int_Xferred + 1 + IF ( .NOT. ALLOCATED(InData%UA_off_forGood) ) 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_off_forGood,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_off_forGood,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%UA_off_forGood,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%UA_off_forGood,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%UA_off_forGood,2), UBOUND(InData%UA_off_forGood,2) + DO i1 = LBOUND(InData%UA_off_forGood,1), UBOUND(InData%UA_off_forGood,1) + IntKiBuf(Int_Xferred) = TRANSFER(InData%UA_off_forGood(i1,i2), IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF END SUBROUTINE UA_PackParam SUBROUTINE UA_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -5439,6 +5866,29 @@ SUBROUTINE UA_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%lin_nx = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! UA_off_forGood 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_off_forGood)) DEALLOCATE(OutData%UA_off_forGood) + ALLOCATE(OutData%UA_off_forGood(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%UA_off_forGood.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%UA_off_forGood,2), UBOUND(OutData%UA_off_forGood,2) + DO i1 = LBOUND(OutData%UA_off_forGood,1), UBOUND(OutData%UA_off_forGood,1) + OutData%UA_off_forGood(i1,i2) = TRANSFER(IntKiBuf(Int_Xferred), OutData%UA_off_forGood(i1,i2)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF END SUBROUTINE UA_UnPackParam SUBROUTINE UA_CopyInput( SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/nwtc-library/src/NWTC_Num.f90 b/modules/nwtc-library/src/NWTC_Num.f90 index 3bbfae4a7..7e4cdaf16 100644 --- a/modules/nwtc-library/src/NWTC_Num.f90 +++ b/modules/nwtc-library/src/NWTC_Num.f90 @@ -74,6 +74,20 @@ MODULE NWTC_Num REAL(SiKi) :: TwoPi_R4 !< 2*pi in 4-byte precision REAL(R8Ki) :: TwoPi_R8 !< 2*pi in 8-byte precision REAL(QuKi) :: TwoPi_R16 !< 2*pi in 16-byte precision + + ! constants for kernel smoothing + INTEGER, PARAMETER :: kernelType_EPANECHINIKOV = 1 + INTEGER, PARAMETER :: kernelType_QUARTIC = 2 + INTEGER, PARAMETER :: kernelType_BIWEIGHT = 3 + INTEGER, PARAMETER :: kernelType_TRIWEIGHT = 4 + INTEGER, PARAMETER :: kernelType_TRICUBE = 5 + INTEGER, PARAMETER :: kernelType_GAUSSIAN = 6 + + + ! constants for output formats + INTEGER, PARAMETER :: Output_in_Native_Units = 0 + INTEGER, PARAMETER :: Output_in_SI_Units = 1 + INTEGER, PARAMETER :: Output_in_Engr_Units = 2 !======================================================================= ! Create interfaces for generic routines that use specific routines. @@ -99,6 +113,20 @@ MODULE NWTC_Num MODULE PROCEDURE EulerExtractR16 END INTERFACE + !> \copydoc nwtc_num::taitbryanyxzextractr4() + !! See nwtc_num::taitbryanyxzextractr4() for details on the algorithm + INTERFACE TaitBryanYXZExtract + MODULE PROCEDURE TaitBryanYXZExtractR4 + MODULE PROCEDURE TaitBryanYXZExtractR8 + MODULE PROCEDURE TaitBryanYXZExtractR16 + END INTERFACE + + INTERFACE TaitBryanYXZConstruct + MODULE PROCEDURE TaitBryanYXZConstructR4 + MODULE PROCEDURE TaitBryanYXZConstructR8 + MODULE PROCEDURE TaitBryanYXZConstructR16 + END INTERFACE + !> \copydoc nwtc_num::outerproductr4 INTERFACE OuterProduct MODULE PROCEDURE OuterProductR4 @@ -415,6 +443,96 @@ SUBROUTINE BSortReal ( RealAry, NumPts ) RETURN END SUBROUTINE BSortReal ! ( RealAry, NumPts ) !======================================================================= +!> This subroutine takes an "oldUnits" array, compares the strings +!! to a list of units that will be converted to SI, and returns two arrays +!! that give the new units and the multiplicative scaling factor to convert +!! the old units to the new ones. The three arrays must be the same size. + SUBROUTINE ConvertUnitsToSI(Units,ScaleFactor) + CHARACTER(*), INTENT(INOUT) :: Units(:) !< in: the old units; out: the new units + REAL(ReKi), INTENT( OUT) :: ScaleFactor(:) !< scaling factor to convert old to new units (old*SF = new) + + + ! local variables + INTEGER :: i + + DO i=1,SIZE(Units) + + SELECT CASE( TRIM(Units(i)) ) ! Note that this IS case sensitive! + + CASE ('(kN)','kN') + Units(i) = '(N)' + ScaleFactor(i) = 1000.0_ReKi + CASE ('(kN-m)','kN-m') + Units(i) = '(N-m)' + ScaleFactor(i) = 1000.0_ReKi + CASE ('(deg)','deg') + Units(i) = '(rad)' + ScaleFactor(i) = D2R + CASE ('(deg/s)','deg/s') + Units(i) = '(rad/s)' + ScaleFactor(i) = D2R + CASE ('(deg/s^2)','deg/s^2') + Units(i) = '(rad/s^2)' + ScaleFactor(i) = D2R + CASE ('(rpm)','rpm') + Units(i) = '(rad/s)' + ScaleFactor(i) = RPM2RPS + CASE ('(kW)','kW') + Units(i) = '(W)' + ScaleFactor(i) = 1000.0_ReKi + CASE DEFAULT + ScaleFactor(i) = 1.0_ReKi + END SELECT + + END DO + + END SUBROUTINE ConvertUnitsToSI +!======================================================================= +!> This subroutine takes an "oldUnits" array, compares the strings +!! to a list of units that will be converted to engineering units (kN and deg), and returns two arrays +!! that give the new units and the multiplicative scaling factor to convert +!! the old units to the new ones. The three arrays must be the same size. + SUBROUTINE ConvertUnitsToEngr(Units,ScaleFactor) + CHARACTER(*), INTENT(INOUT) :: Units(:) !< in: the old units; out: the new units + REAL(ReKi), INTENT( OUT) :: ScaleFactor(:) !< scaling factor to convert old to new units (old*SF = new) + + + ! local variables + INTEGER :: i + + DO i=1,SIZE(Units) + + SELECT CASE( TRIM(Units(i)) ) ! Note that this IS case sensitive! + + CASE ('(N)','N') + Units(i) = '(kN)' + ScaleFactor(i) = 0.001_ReKi + CASE ('(N-m)','N-m', '(Nm)', 'Nm') + Units(i) = '(kN-m)' + ScaleFactor(i) = 0.001_ReKi + CASE ('(rad)','rad') + Units(i) = '(deg)' + ScaleFactor(i) = R2D + CASE ('(rad/s)','rad/s') + Units(i) = '(deg/s)' + ScaleFactor(i) = R2D + CASE ('(rad/s^2)','rad/s^2') + Units(i) = '(deg/s^2)' + ScaleFactor(i) = R2D + CASE ('(rps)','rps') + Units(i) = '(rpm)' + ScaleFactor(i) = 60.0_ReKi + CASE ('(W)','W') + Units(i) = '(kW)' + ScaleFactor(i) = 0.001_ReKi + CASE DEFAULT + ScaleFactor(i) = 1.0_ReKi + END SELECT + + END DO + + END SUBROUTINE ConvertUnitsToEngr +!======================================================================= !> This function computes the cross product of two 3-element arrays (resulting in a vector): \n !! cross_product = Vector1 \f$\times\f$ Vector2 \n !! Use cross_product (nwtc_num::cross_product) instead of directly calling a specific routine in the generic interface. @@ -974,7 +1092,6 @@ FUNCTION CubicSplineInterpM ( X, XAry, YAry, Coef, ErrStat, ErrMsg ) RESULT( Res NumPts = SIZE( XAry ) NumCrvs = SIZE( YAry, 2 ) - !NumCrvs = SIZE( YAry, 2 ) ALLOCATE ( Res( NumCrvs ) , STAT=ErrStatLcl ) IF ( ErrStatLcl /= 0 ) THEN @@ -3950,6 +4067,105 @@ FUNCTION IsSymmetric( A ) END FUNCTION IsSymmetric !======================================================================= +!> KERNELSMOOTHING Kernel smoothing of vector data +!! +!! fNew = kernelSmoothing( x, f, KERNELTYPE, RADIUS ) generates a smoothed +!! version of the data f(x) in fNew. Supported KERNELTYPE values are +!! 'EPANECHINIKOV', 'QUARTIC' or 'BIWEIGHT', 'TRIWEIGHT', 'TRICUBE' and +!! 'GAUSSIAN'. RADIUS controls the width of the kernel relative to the +!! vector x. +!! +!! See also: https://en.wikipedia.org/wiki/Kernel_(statistics)#Kernel_functions_in_common_use +subroutine kernelSmoothing(x, f, kernelType, radius, fNew) + + REAL(ReKi), INTENT(in ) :: x(:) !> independent axis + REAL(ReKi), INTENT(in ) :: f(:) !> function values, f(x), to be smoothed + INTEGER, INTENT(in ) :: kernelType !> what kind of smoothing function to use + REAL(ReKi), INTENT(in ) :: radius !> width of the "window", in the units of x + REAL(ReKi), INTENT( out) :: fNew(:) !> smoothed function values + + REAL(ReKi) :: k + REAL(ReKi) :: k_sum + REAL(ReKi) :: w + INTEGER(IntKi) :: Exp1 + INTEGER(IntKi) :: Exp2 + REAL(ReKi) :: u(size(x)) + INTEGER :: i, j + INTEGER :: n + + ! check that radius > 0 + ! check that size(x) = size(f)=size(fNew) + ! check that kernelType is a valid number + + n = size(x) + + + ! make sure that the value of u is in [-1 and 1] for these kernels: + if (kernelType /= kernelType_GAUSSIAN) then + + select case ( kernelType ) + case (kernelType_EPANECHINIKOV) + w = 3.0_ReKi/4.0_ReKi + Exp1 = 2 + Exp2 = 1 + case (kernelType_QUARTIC, kernelType_BIWEIGHT) + w = 15.0_ReKi/16.0_ReKi + Exp1 = 2 + Exp2 = 2 + case (kernelType_TRIWEIGHT) + w = 35.0_ReKi/32.0_ReKi + Exp1 = 2 + Exp2 = 3 + case (kernelType_TRICUBE) + w = 70.0_ReKi/81.0_ReKi + Exp1 = 3 + Exp2 = 3 + end select + + fNew = 0.0_ReKi ! whole array operation + do j=1,n ! for each value in f: + + u = (x - x(j)) / radius ! whole array operation + do i=1,n + u(i) = min( 1.0_ReKi, max( -1.0_ReKi, u(i) ) ) + end do + + k_sum = 0.0_ReKi + do i=1,n + k = w*(1.0_ReKi-abs(u(i))**Exp1)**Exp2; + k_sum = k_sum + k + fNew(j) = fNew(j) + k*f(i) + end do + if (k_sum > 0.0_ReKi) then + fNew(j) = fNew(j) / k_sum + end if + + end do ! j (each output value) + + else ! kernelType_GAUSSIAN + w = 1.0_ReKi/sqrt(TwoPi) + + fNew = 0.0_ReKi ! whole array operation + do j=1,n ! for each value in f: + + u = (x - x(j)) / radius ! whole array operation + + k_sum = 0.0_ReKi + do i=1,n + k = w*exp(-0.5*u(i)**2); + k_sum = k_sum + k + fNew(j) = fNew(j) + k*f(i) + end do + if (k_sum > 0.0_ReKi) then + fNew(j) = fNew(j) / k_sum + end if + + end do ! j (each output value) + + end if + +end subroutine kernelSmoothing +!======================================================================= !> This subroutine finds the lower-bound index of an input x-value located in an array. !! On return, Ind has a value such that !! XAry(Ind) <= XVal < XAry(Ind+1), with the exceptions that @@ -4365,10 +4581,11 @@ END FUNCTION OuterProductR16 !> This subroutine perturbs an orientation matrix by a small angle, using !! a logarithmic map. For small angles, the change in angle is equivalent to !! a change in log map parameters. - SUBROUTINE PerturbOrientationMatrix( Orientation, Perturbation, AngleDim ) - REAL(R8Ki), INTENT(INOUT) :: Orientation(3,3) - REAL(R8Ki), INTENT(IN) :: Perturbation ! angle (radians) of the perturbation - INTEGER, INTENT(IN) :: AngleDim + SUBROUTINE PerturbOrientationMatrix( Orientation, Perturbation, AngleDim, Perturbations ) + REAL(R8Ki), INTENT(INOUT) :: Orientation(3,3) + REAL(R8Ki), OPTIONAL, INTENT(IN) :: Perturbation ! angle (radians) of the perturbation + INTEGER, OPTIONAL, INTENT(IN) :: AngleDim + REAL(R8Ki), OPTIONAL, INTENT(IN) :: Perturbations(3) ! angles (radians) of the perturbations ! Local variables REAL(R8Ki) :: angles(3) @@ -4377,7 +4594,11 @@ SUBROUTINE PerturbOrientationMatrix( Orientation, Perturbation, AngleDim ) CALL DCM_LogMap( Orientation, angles, ErrStat2, ErrMsg2 ) - angles(AngleDim) = angles(AngleDim) + Perturbation + IF (PRESENT(Perturbations)) THEN + angles = angles + Perturbations + ELSE + angles(AngleDim) = angles(AngleDim) + Perturbation + END IF Orientation = DCM_exp( angles ) @@ -6089,6 +6310,516 @@ FUNCTION SkewSymMatR16 ( x ) RESULT(M) RETURN END FUNCTION SkewSymMatR16 +!======================================================================= +!> If M is a rotation matrix from a 1-2-3 rotation sequence about Y-X-Z, this function returns +!! the 3 sequential angles, \f$\theta_y\f$, \f$\theta_x\f$, and \f$\theta_z\f$ (in radians), that formed +!! the matrix. M represents a change of basis (from global to local coordinates; +!! not a physical rotation of the body; passive rotation). +!! +!! See Tait-Bryan angle \f$ Y_1 X_2 Z_3 \f$ at https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix +!! Note that what we are using here is the passive rotation, which is the transpose of what appears in the +!! wikipedia article. +!! +!! +!! \f{eqnarray*}{ +!! M & = & R(\theta_z) R(\theta_x) R(\theta_y) +!! & = & R(\theta_3) R(\theta_2) R(\theta_1) \\ +!! & = & \begin{bmatrix} \cos(\theta_z) & \sin(\theta_z) & 0 \\ +!! -\sin(\theta_z) & \cos(\theta_z) & 0 \\ +!! 0 & 0 & 1 \end{bmatrix} +!! \begin{bmatrix} 1 & 0 & 0 \\ +!! 0 & \cos(\theta_x) & \sin(\theta_x) \\ +!! 0 & -\sin(\theta_x) & \cos(\theta_x) \end{bmatrix} +!! \begin{bmatrix} \cos(\theta_y) & 0 & -\sin(\theta_y) \\ +!! 0 & 1 & 0 \\ +!! \sin(\theta_y) & 0 & \cos(\theta_y) \end{bmatrix} +!! & = & \begin{bmatrix} C_3 & S_3 & 0 \\ +!! -S_3 & C_3 & 0 \\ +!! 0 & 0 & 1 \end{bmatrix} +!! \begin{bmatrix} 1 & 0 & 0 \\ +!! 0 & C_2 & S_2 \\ +!! 0 & -S_2 & C_2 \end{bmatrix} +!! \begin{bmatrix} C_1 & 0 & -S_1 \\ +!! 0 & 1 & 0 \\ +!! S_1 & 0 & C_1 \end{bmatrix} \\ +!! & = & \begin{bmatrix} +!! \cos(\theta_y) \cos(\theta_z) + \sin(\theta_y) \sin(\theta_x) \sin(\theta_z) & \cos(\theta_x) \sin(\theta_z) & \cos(\theta_y) \sin(\theta_x) \sin(\theta_z) - \sin(\theta_y) \cos(\theta_z) \\ +!! \sin(\theta_y) \sin(\theta_x) \cos(\theta_z) - \cos(\theta_y) \sin(\theta_z) & \cos(\theta_x) \cos(\theta_z) & \cos(\theta_y) \sin(\theta_x) \cos(\theta_z) + \sin(\theta_y) \sin(\theta_z) \\ +!! \sin(\theta_y) \cos(\theta_x) & -\sin(\theta_x) & \cos(\theta_y) \cos(\theta_x) \\ +!! \end{bmatrix} +!! & = & \begin{bmatrix} +!! C_1 C_3 + S_1 S_2 S_3 & C_2 S_3 & C_1 S_2 S_3 - S_1 C_3 \\ +!! S_1 S_2 C_3 - C_1 S_3 & C_2 C_3 & C_1 S_2 C_3 + S_1 S_3 \\ +!! S_1 C_2 & -S_2 & C_1 C_2 \\ +!! \end{bmatrix} +!! \f} +!! returned angles are in the range \f$\theta_y,\theta_x, \theta_z \in \left[ \pi, -\pi \right]\f$ \n +!! Use TaitBryanYXZExtract (nwtc_num::taitbryanyxzextract) instead of directly calling a specific routine in the generic interface. + FUNCTION TaitBryanYXZExtractR4(M) result(theta) + + + REAL(SiKi), INTENT(IN) :: M(3,3) !< rotation matrix, M + REAL(SiKi) :: theta(3) !< the 3 rotation angles, \f$(\theta_y, \theta_x, \theta_z)\f$, corresponding to the Tait-Bryan rotation angle corresponding to cant-toe-twist + + REAL(SiKi) :: C_1 ! C_1 > cos(theta_y) + REAL(SiKi) :: S_1 ! S_1 > sin(theta_y) + REAL(SiKi) :: C_2 ! C_2 > cos(theta_x) + REAL(SiKi) :: C_3 ! C_3 > cos(theta_z) + REAL(SiKi) :: S_3 ! S_3 > sin(theta_z) + + !> # Algorithm + + !> Starting with the trig identity of \f$ \sin(\theta_3)^2 + \cos(\theta_3)^2 = S_3^2 + C_3^2 \equiv 1\f$, we can find \f$ \cos(\theta_2) \f$ + !! from matrix elements \f$M(1,2)\f$ and \f$M(2,2)\f$ by + !! \f{equation}{ + !! \cos(\theta_2) = C_2 = \sqrt{ M(1,2)^2 + M(2,2)^2} = \sqrt{ C_2^2 S_3^2 + C_2^2 C_3^2 } = \sqrt{ C_2^2 ( S_3^2 + C_3^2 ) }. + !! \f} + + ! use trig identity S_3**2 + C_3**2 = 1 to get abs( C_2 ) + C_2 = sqrt( m(1,2)**2 + m(2,2)**2 ) + + if ( EqualRealNos( C_2, 0.0_SiKi ) ) then + + !> ## If \f$ \cos(\theta_2) = C_2 = 0\f$: + !! If \f$\cos(\theta_2) = C_2 = 0\f$, then \f$ \theta_2 \f$ is \f$ \pm\pi/2 \f$ and \f$ S_2 = \pm 1\f$. We can solve for the sign of \f$\theta_2\f$ by using + !! \f{equation}{ + !! \theta_2 = \arctan{\left( \frac{-M(3,2)}{C_2} \right)} = \arctan{\left( \frac{S_2}{C_2} \right)} + !! \f} + !! (but using the _atan2_ function in the complex plane instead of \f$ \arctan \f$). + + theta(2) = atan2( -m(3,2), C_2 ) ! theta_2 -> theta_x + + + !> Considering \f$ C_2 = 0 \f$ and \f$ S_2 = \pm 1\f$, the matrix \f$ M \f$ reduces to + !! \f{equation} + !! M = \begin{bmatrix} + !! C_1 C_3 \pm S_1 S_3 & 0 & \pm C_1 S_3 - S_1 C_3 \\ + !! \pm S_1 C_3 - C_1 S_3 & 0 & \pm C_1 C_3 + S_1 S_3 \\ + !! 0 & \mp 1 & 0 \\ + !! \end{bmatrix} + !! \f} + !! + !! At this point we can choose \f$ \theta_3 = 0 \f$ due to gimbal lock giving \f$ \sin(\theta_3) = 0 \f$, \f$ \cos(\theta_3) = 1\f$. + + theta(3) = 0.0_SiKi ! theta_z = theta_3 + + !> This further reduces \f$ M \f$ to + !! \f{equation} + !! M = \begin{bmatrix} + !! C_1 & 0 & - S_1 \\ + !! \pm S_1 & 0 & \pm C_1 \\ + !! 0 & \mp 1 & 0 \\ + !! \end{bmatrix}, + !! \f} + !! allowing us to solve for \f$ \theta_1 \f$ by \f$ \theta_1 = \arctan{\left( \frac{-M(1,3)}{M(1,1)} \right)} = \arctan{\left( \frac{S_1}{C_1} \right)}\f$. + + theta(1) = atan2( -m(1,3), m(1,1) ) + + else + !> ## Else \f$ \cos(\theta_2) = C_2 \neq 0\f$: + !! + !! First, start by finding \f$ \theta(1) \f$ from \f$ M(3,1) \f$ and \f$ M(3,3) \f$ using + !! \f{equation}{ + !! \theta_1 = \arctan{\left( \frac{M(3,1)}{M(3,3)} \right)} = \arctan{\left( \frac{S_1 C_2}{C_1 C_2} \right)}. + !! \f} + !! With this we calculate values for \f$S_1\f$ and \f$C_1\f$. + + theta(1) = atan2( m(3,1), m(3,3) ) ! theta_1 -> theta_y + C_1 = cos( theta(1) ) + S_1 = sin( theta(1) ) + + !> We already know \f$ \text{abs}( C_2 ) \f$, but need the sign of it. This can be found by comparing the + !! \f$ S_1 C_2 \f$ and \f$ C_1 C_2 \f$ terms with the \f$ C_1 \f$ and \f$ S_1 \f$ terms we just found. + !! If \f$ C_1 = 0 \f$, then we use + !! \f{equation}{ + !! C_2 = C_2 \cdot \text{sgn}{\left( \frac{M(3,1)}{S_1} \right)} = C_2 \cdot \text{sgn}{( C_2 )}, + !! \f} + !! otherwise + !! \f{equation}{ + !! C_2 = C_2 \cdot \text{sgn}{\left( \frac{M(3,3)}{C_1} \right)} = C_2 \cdot \text{sgn}{( C_2 )} + !! \f} + !! + if ( EqualRealNos( C_1, 0.0_SiKi ) ) then + C_2 = sign( C_2, m(3,1) / S_1 ) + else + C_2 = sign( C_2, m(3,3) / C_1 ) + endif + + !> Now can calculate \f$ \theta_2 \f$ from + !! \f{equation}{ + !! \theta_2 = \arctan{\left( \frac{-M(3,2)}{C_2} \right)} = \arctan{\left( \frac{S_2}{C_2} \right)} + !! \f} + theta(2) = atan2( -m(3,2), C_2 ) + + + !> For numerical reasons, we're going to get \f$ \theta_3 \f$ (\f$\theta_z\f$) using + !! \f{eqnarray*}{ + !! M' &=& M \cdot (R(\theta_2) \cdot R(\theta_1))^\text{T} = M \cdot R(\theta_1)^\text{T} \cdot R(\theta_2)^\text{T} & = & R(\theta_3) \\ + !! &=& R(\theta_3) R(\theta_2) R(\theta_1) R(\theta_1)^T R(\theta_2)^T &=& R(\theta_3) \\ + !! &=& M \cdot + !! \begin{bmatrix} + !! C_1 & 0 & S_1 \\ + !! 0 & 1 & 0 \\ + !! -S_1 & 0 & C_1 + !! \end{bmatrix} + !! \cdot + !! \begin{bmatrix} + !! 1 & 0 & 0 \\ + !! 0 & C_2 & -S_2 \\ + !! 0 & S_2 & C_2 + !! \end{bmatrix} + !! &=& + !! \begin{bmatrix} + !! C_3 & S_3 & 0 \\ + !! -S_3 & C_3 & 0 \\ + !! 0 & 0 & 1 + !! \end{bmatrix} \\ + !! &=& M \cdot + !! \begin{bmatrix} + !! C_1 & S_1 S_2 & S_1 C_2 \\ + !! 0 & C_2 & -S_2 \\ + !! -S_1 & C_1 S_2 & C_1 C_2 + !! \end{bmatrix} + !! &=& \begin{bmatrix} + !! C_3 & S_3 & 0 \\ + !! -S_3 & C_3 & 0 \\ + !! 0 & 0 & 1 + !! \end{bmatrix} \\ + !! \f} + !! + !! From this we can find \f$ -S_3 \f$ and \f$ C_3 \f$ as + !! \f{eqnarray}{ + !! -S_3 &=& M(2,1) C_1 + M(2,3) (- S_1 ) &=& ( S_1 S_2 C_3 - C_1 S_3 ) C_1 + ( C_1 S_2 C_3 + S_1 S_3 ) ( - S_1 ) \\ + !! && &=& S_1 C_1 S_2 C_3 - C_1^2 S_3 - S_1^2 S_3 - S_1 C_1 S_2 C_3 \\ + !! && &=& -( C_1^2 + S_1^2 ) S_3 \\ + !! && &=& -S_3 + !! \f} + !! and + !! \f{eqnarray}{ + !! C_3 &=& M(1,1) C_1 + M(1,3) (- S_1 ) &=& ( C_1 C_3 + S_1 S_2 S_3 ) C_1 + ( C_1 S_2 S_3 - S_1 C_3 ) (- S_1 ) \\ + !! && &=& C_1^2 C_3 + S_1 C_1 S_2 S_3 - S_1 C_1 S_2 S_3 + S_1^2 C_3 \\ + !! && &=& ( C_1^2 + S_1^2 ) C_3 \\ + !! && &=& C_3 + !! \f} + !! + !! \f$\theta_3\f$ is then found as \f$\theta_3 = \arctan{\left( \frac{S_3}{C_3} \right)}\f$. + + + S_3 = -( m(2,1) * C_1 + m(2,3) * (- S_1) ) + C_3 = m(1,1) * C_1 + m(1,3) * (- S_1) + + theta(3) = atan2( S_3, C_3) + + endif + + + END FUNCTION TaitBryanYXZExtractR4 + +!> See nwtc_num::taitbryanyxzextractr4 for detailed explanation of algorithm + FUNCTION TaitBryanYXZExtractR8(M) result(theta) + + + REAL(R8Ki), INTENT(IN) :: M(3,3) !< rotation matrix, M + REAL(R8Ki) :: theta(3) !< the 3 rotation angles, \f$(\theta_y, \theta_x, \theta_z)\f$, corresponding to the Tait-Bryan rotation angle corresponding to cant-toe-twist + + REAL(R8Ki) :: C_1 ! C_1 > cos(theta_y) + REAL(R8Ki) :: S_1 ! S_1 > sin(theta_y) + REAL(R8Ki) :: C_2 ! C_2 > cos(theta_x) + REAL(R8Ki) :: C_3 ! C_3 > cos(theta_z) + REAL(R8Ki) :: S_3 ! S_3 > sin(theta_z) + + !> See nwtc_num::taitbryanyxzextractr4 for detailed description of how this works. + + ! use trig identity S_3**2 + C_3**2 = 1 to get abs( C_2 ) + C_2 = sqrt( m(1,2)**2 + m(2,2)**2 ) + + ! If C_2 is zero, we can simplifiy some things since theta(2) is +/- pi/2 + if ( EqualRealNos( C_2, 0.0_R8Ki ) ) then + + ! find sign of theta(2) based on sin(theta_2) + theta(2) = atan2( -m(3,2), C_2 ) ! theta_2 -> theta_x + + ! Considering C_2 = 0 and S_2 = \pm 1, the matrix M reduces to + ! M = [ C_1 C_3 \pm S_1 S_3 0 \pm C_1 S_3 - S_1 C_3 | + ! | \pm S_1 C_3 - C_1 S_3 0 \pm C_1 C_3 + S_1 S_3 | + ! | 0 0 \mp 1 0 ] + ! + ! At this point we can choose \theta_3 = 0 due to gimbal lock giving sin(theta(3)) = 0, cos(theta(3)) = 1. + + theta(3) = 0.0_R8Ki ! theta_z = theta_3 + + ! This further reduces M to + ! M = [ C_1 0 - S_1 | + ! | \pm S_1 0 \pm C_1 | + ! | 0 \mp 1 0 ] + ! + ! + ! allowing us to solve for theta_1 by theta_1 = atan2( -M(1,3), M(1,1) ) = atan2( S_1, C_1). + + theta(1) = atan2( -m(1,3), m(1,1) ) + + else + ! First, start by finding \f$ \theta(1) \f$ from \f$ M(3,1) \f$ and \f$ M(3,3) \f$ using + ! + ! theta(1) = atan2( M(3,1), M(3,3) ) = atan2( S_1 * C_2, C_1 * C_2 ). + ! With this we calculate values for S_1 and C_1. + + theta(1) = atan2( m(3,1), m(3,3) ) ! theta_1 -> theta_y + C_1 = cos( theta(1) ) + S_1 = sin( theta(1) ) + + ! We already know abs( C_2 ), but need the sign of it. This can be found by comparing the + ! S_1 * C_2 and C_1 * C_2 terms with the C_1 and S_1 terms we just found. + + if ( EqualRealNos( C_1, 0.0_R8Ki ) ) then + C_2 = sign( C_2, m(3,1) / S_1 ) + else + C_2 = sign( C_2, m(3,3) / C_1 ) + endif + + ! Now can calculate theta(2) + theta(2) = atan2( -m(3,2), C_2 ) + + + ! For numerical reasons, we're going to get theta(3) using some matrix math and identities about M. + ! See nwtc_num::taitbryanyxzextractr4 for complete documentation on the matrix math used here + + S_3 = -( m(2,1) * C_1 + m(2,3) * (- S_1) ) + C_3 = m(1,1) * C_1 + m(1,3) * (- S_1) + + theta(3) = atan2( S_3, C_3) + + endif + + + END FUNCTION TaitBryanYXZExtractR8 + +!> See nwtc_num::taitbryanyxzextractr4 for detailed explanation of algorithm + FUNCTION TaitBryanYXZExtractR16(M) result(theta) + + + REAL(QuKi), INTENT(IN) :: M(3,3) !< rotation matrix, M + REAL(QuKi) :: theta(3) !< the 3 rotation angles, \f$(\theta_y, \theta_x, \theta_z)\f$, corresponding to the Tait-Bryan rotation angle corresponding to cant-toe-twist + + REAL(QuKi) :: C_1 ! C_1 > cos(theta_y) + REAL(QuKi) :: S_1 ! S_1 > sin(theta_y) + REAL(QuKi) :: C_2 ! C_2 > cos(theta_x) + REAL(QuKi) :: C_3 ! C_3 > cos(theta_z) + REAL(QuKi) :: S_3 ! S_3 > sin(theta_z) + + !> See nwtc_num::taitbryanyxzextractr4 for detailed description of how this works. + + ! use trig identity S_3**2 + C_3**2 = 1 to get abs( C_2 ) + C_2 = sqrt( m(1,2)**2 + m(2,2)**2 ) + + ! If C_2 is zero, we can simplifiy some things since theta(2) is +/- pi/2 + if ( EqualRealNos( C_2, 0.0_QuKi ) ) then + + ! find sign of theta(2) based on sin(theta_2) + theta(2) = atan2( -m(3,2), C_2 ) ! theta_2 -> theta_x + + ! Considering C_2 = 0 and S_2 = \pm 1, the matrix M reduces to + ! M = [ C_1 C_3 \pm S_1 S_3 0 \pm C_1 S_3 - S_1 C_3 | + ! | \pm S_1 C_3 - C_1 S_3 0 \pm C_1 C_3 + S_1 S_3 | + ! | 0 0 \mp 1 0 ] + ! + ! At this point we can choose \theta_3 = 0 due to gimbal lock giving sin(theta(3)) = 0, cos(theta(3)) = 1. + + theta(3) = 0.0_QuKi ! theta_z = theta_3 + + ! This further reduces M to + ! M = [ C_1 0 - S_1 | + ! | \pm S_1 0 \pm C_1 | + ! | 0 \mp 1 0 ] + ! + ! + ! allowing us to solve for theta_1 by theta_1 = atan2( -M(1,3), M(1,1) ) = atan2( S_1, C_1). + + theta(1) = atan2( -m(1,3), m(1,1) ) + + else + ! First, start by finding \f$ \theta(1) \f$ from \f$ M(3,1) \f$ and \f$ M(3,3) \f$ using + ! + ! theta(1) = atan2( M(3,1), M(3,3) ) = atan2( S_1 * C_2, C_1 * C_2 ). + ! With this we calculate values for S_1 and C_1. + + theta(1) = atan2( m(3,1), m(3,3) ) ! theta_1 -> theta_y + C_1 = cos( theta(1) ) + S_1 = sin( theta(1) ) + + ! We already know abs( C_2 ), but need the sign of it. This can be found by comparing the + ! S_1 * C_2 and C_1 * C_2 terms with the C_1 and S_1 terms we just found. + + if ( EqualRealNos( C_1, 0.0_QuKi ) ) then + C_2 = sign( C_2, m(3,1) / S_1 ) + else + C_2 = sign( C_2, m(3,3) / C_1 ) + endif + + ! Now can calculate theta(2) + theta(2) = atan2( -m(3,2), C_2 ) + + + ! For numerical reasons, we're going to get theta(3) using some matrix math and identities about M. + ! See nwtc_num::taitbryanyxzextractr4 for complete documentation on the matrix math used here + + S_3 = -( m(2,1) * C_1 + m(2,3) * (- S_1) ) + C_3 = m(1,1) * C_1 + m(1,3) * (- S_1) + + theta(3) = atan2( S_3, C_3) + + endif + + + END FUNCTION TaitBryanYXZExtractR16 + + FUNCTION TaitBryanYXZConstructR4(theta) result(M) + ! this function creates a rotation matrix, M, from a 1-2-3 rotation + ! sequence of the 3 TaitBryan angles, theta_x, theta_y, and theta_z, in radians. + ! M represents a change of basis (from global to local coordinates; + ! not a physical rotation of the body). it is the inverse of TaitBryanYXZExtract(). + ! + ! M = R(theta_z) * R(theta_x) * R(theta_y) + ! = [ cz sz 0 | [ 1 0 0 | [ cy 0 -sy | + ! |-sz cz 0 |* | 0 cx sx | * | 0 1 0 | + ! | 0 0 1 ] | 0 -sx cx ] | sy 0 cy ] + ! = [ cy*cz+sy*sx*sz cx*sz cy*sx*sz-cz*sy | + ! |cz*sy*sx-cy*sz cx*cz cy*cz*sx+sy*sz | + ! |cx*sy -sx cx*cy ] + ! where cz = cos(theta_z), sz = sin(theta_z), cy = cos(theta_y), etc. + + REAL(SiKi) :: M(3,3) !< rotation matrix, M + REAL(SiKi), INTENT(IN) :: theta(3) !< the 3 rotation angles: \f$\theta_x, \theta_y, \theta_z\f$ + + REAL(SiKi) :: cx ! cos(theta_x) + REAL(SiKi) :: sx ! sin(theta_x) + REAL(SiKi) :: cy ! cos(theta_y) + REAL(SiKi) :: sy ! sin(theta_y) + REAL(SiKi) :: cz ! cos(theta_z) + REAL(SiKi) :: sz ! sin(theta_z) + + cx = cos( theta(1) ) + sx = sin( theta(1) ) + + cy = cos( theta(2) ) + sy = sin( theta(2) ) + + cz = cos( theta(3) ) + sz = sin( theta(3) ) + + M(1,1) = cy*cz+sy*sx*sz + M(1,2) = cx*sz + M(1,3) = cy*sx*sz-cz*sy + + M(2,1) = cz*sy*sx-cy*sz + M(2,2) = cx*cz + M(2,3) = cy*cz*sx+sy*sz + + M(3,1) = cx*sy + M(3,2) = -sx + M(3,3) = cy*cx + + END FUNCTION TaitBryanYXZConstructR4 + + FUNCTION TaitBryanYXZConstructR8(theta) result(M) + + ! this function creates a rotation matrix, M, from a 1-2-3 rotation + ! sequence of the 3 TaitBryan angles, theta_x, theta_y, and theta_z, in radians. + ! M represents a change of basis (from global to local coordinates; + ! not a physical rotation of the body). it is the inverse of TaitBryanYXZExtract(). + ! + ! M = R(theta_z) * R(theta_x) * R(theta_y) + ! = [ cz sz 0 | [ 1 0 0 | [ cy 0 -sy | + ! |-sz cz 0 |* | 0 cx sx | * | 0 1 0 | + ! | 0 0 1 ] | 0 -sx cx ] | sy 0 cy ] + ! = [ cy*cz+sy*sx*sz cx*sz cy*sx*sz-cz*sy | + ! |cz*sy*sx-cy*sz cx*cz cy*cz*sx+sy*sz | + ! |cx*sy -sx cx*cy ] + ! where cz = cos(theta_z), sz = sin(theta_z), cy = cos(theta_y), etc. + + REAL(R8Ki) :: M(3,3) ! rotation matrix M + REAL(R8Ki), INTENT(IN) :: theta(3) ! the 3 rotation angles: theta_x, theta_y, theta_z + + REAL(R8Ki) :: cx ! cos(theta_x) + REAL(R8Ki) :: sx ! sin(theta_x) + REAL(R8Ki) :: cy ! cos(theta_y) + REAL(R8Ki) :: sy ! sin(theta_y) + REAL(R8Ki) :: cz ! cos(theta_z) + REAL(R8Ki) :: sz ! sin(theta_z) + + cx = cos( theta(1) ) + sx = sin( theta(1) ) + + cy = cos( theta(2) ) + sy = sin( theta(2) ) + + cz = cos( theta(3) ) + sz = sin( theta(3) ) + + M(1,1) = cy*cz+sy*sx*sz + M(1,2) = cx*sz + M(1,3) = cy*sx*sz-cz*sy + + M(2,1) = cz*sy*sx-cy*sz + M(2,2) = cx*cz + M(2,3) = cy*cz*sx+sy*sz + + M(3,1) = cx*sy + M(3,2) = -sx + M(3,3) = cy*cx + + END FUNCTION TaitBryanYXZConstructR8 + + FUNCTION TaitBryanYXZConstructR16(theta) result(M) + + ! this function creates a rotation matrix, M, from a 1-2-3 rotation + ! sequence of the 3 TaitBryan angles, theta_x, theta_y, and theta_z, in radians. + ! M represents a change of basis (from global to local coordinates; + ! not a physical rotation of the body). it is the inverse of TaitBryanYXZExtract(). + ! + ! M = R(theta_z) * R(theta_x) * R(theta_y) + ! = [ cz sz 0 | [ 1 0 0 | [ cy 0 -sy | + ! |-sz cz 0 |* | 0 cx sx | * | 0 1 0 | + ! | 0 0 1 ] | 0 -sx cx ] | sy 0 cy ] + ! = [ cy*cz+sy*sx*sz cx*sz cy*sx*sz-cz*sy | + ! |cz*sy*sx-cy*sz cx*cz cy*cz*sx+sy*sz | + ! |cx*sy -sx cx*cy ] + ! where cz = cos(theta_z), sz = sin(theta_z), cy = cos(theta_y), etc. + + REAL(QuKi) :: M(3,3) ! rotation matrix M + REAL(QuKi), INTENT(IN) :: theta(3) ! the 3 rotation angles: theta_x, theta_y, theta_z + + REAL(QuKi) :: cx ! cos(theta_x) + REAL(QuKi) :: sx ! sin(theta_x) + REAL(QuKi) :: cy ! cos(theta_y) + REAL(QuKi) :: sy ! sin(theta_y) + REAL(QuKi) :: cz ! cos(theta_z) + REAL(QuKi) :: sz ! sin(theta_z) + + cx = cos( theta(1) ) + sx = sin( theta(1) ) + + cy = cos( theta(2) ) + sy = sin( theta(2) ) + + cz = cos( theta(3) ) + sz = sin( theta(3) ) + + M(1,1) = cy*cz+sy*sx*sz + M(1,2) = cx*sz + M(1,3) = cy*sx*sz-cz*sy + + M(2,1) = cz*sy*sx-cy*sz + M(2,2) = cx*cz + M(2,3) = cy*cz*sx+sy*sz + + M(3,1) = cx*sy + M(3,2) = -sx + M(3,3) = cy*cx + + END FUNCTION TaitBryanYXZConstructR16 + + !======================================================================= !> This routine takes an array of time values such as that returned from !! CALL DATE_AND_TIME ( Values=TimeAry ) From 2bec20113d5bf17cfd0872bf659668898513df50 Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Wed, 21 Apr 2021 19:44:11 -0600 Subject: [PATCH 02/24] remove unused variables --- modules/openfast-library/src/FAST_Lin.f90 | 7 +++---- modules/openfast-library/src/FAST_Solver.f90 | 4 ++-- modules/openfast-library/src/FAST_Subs.f90 | 2 +- modules/openfoam/src/OpenFOAM.f90 | 10 +++++----- modules/supercontroller/src/SC_DataEx.f90 | 8 ++++---- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/modules/openfast-library/src/FAST_Lin.f90 b/modules/openfast-library/src/FAST_Lin.f90 index 9a406701c..fe262be0b 100644 --- a/modules/openfast-library/src/FAST_Lin.f90 +++ b/modules/openfast-library/src/FAST_Lin.f90 @@ -2211,10 +2211,9 @@ SUBROUTINE Linear_SD_InputSolve_dy( p_FAST, y_FAST, u_SD, y_SD, y_ED, HD, MAPp, CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message ! local variables - INTEGER(IntKi) :: SD_Start, SD_Out_Start, HD_Start, HD_Out_Start, ED_Out_Start, MAP_Out_Start - INTEGER(IntKi) :: MAP_Start - INTEGER(IntKi) :: ErrStat2 ! temporary Error status of the operation - CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None + INTEGER(IntKi) :: SD_Start, SD_Out_Start, HD_Out_Start, ED_Out_Start, MAP_Out_Start +! INTEGER(IntKi) :: ErrStat2 ! temporary Error status of the operation +! CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None CHARACTER(*), PARAMETER :: RoutineName = 'Linear_SD_InputSolve_du' diff --git a/modules/openfast-library/src/FAST_Solver.f90 b/modules/openfast-library/src/FAST_Solver.f90 index e8768bc04..8a56bffa2 100644 --- a/modules/openfast-library/src/FAST_Solver.f90 +++ b/modules/openfast-library/src/FAST_Solver.f90 @@ -209,8 +209,8 @@ SUBROUTINE ED_InputSolve( p_FAST, u_ED, y_ED, p_AD14, y_AD14, y_AD, y_SrvD, u_AD CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None CHARACTER(*), PARAMETER :: RoutineName = 'ED_InputSolve' - TYPE(MeshType), POINTER :: PlatformMotion - TYPE(MeshType), POINTER :: PlatformLoads +! TYPE(MeshType), POINTER :: PlatformMotion +! TYPE(MeshType), POINTER :: PlatformLoads ! Initialize error status ErrStat = ErrID_None diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 784842dad..6b53d3429 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -5144,7 +5144,7 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, OpFM, H TYPE(IceDyn_Data), INTENT(IN ) :: IceD !< All the IceDyn data used in time-step loop - logical :: outputFields ! flag to determine if we want to output the HD mesh fields +! logical :: outputFields ! flag to determine if we want to output the HD mesh fields INTEGER(IntKi) :: NumBl, k INTEGER(IntKi) :: j ! counter for StC instance at location diff --git a/modules/openfoam/src/OpenFOAM.f90 b/modules/openfoam/src/OpenFOAM.f90 index a45b1d525..af7c853dc 100644 --- a/modules/openfoam/src/OpenFOAM.f90 +++ b/modules/openfoam/src/OpenFOAM.f90 @@ -429,8 +429,8 @@ SUBROUTINE SetOpFMForces(p_FAST, p_AD14, u_AD14, y_AD14, u_AD, y_AD, y_ED, OpFM, ! Local variables: - REAL(ReKi) :: dRforceNodes ! Uniform distance between two consecutive blade force nodes - REAL(ReKi) :: dHforceNodes ! Uniform distance between two consecutive tower force nodes +! REAL(ReKi) :: dRforceNodes ! Uniform distance between two consecutive blade force nodes +! REAL(ReKi) :: dHforceNodes ! Uniform distance between two consecutive tower force nodes INTEGER(IntKi) :: J ! Loops through nodes / elements INTEGER(IntKi) :: K ! Loops through blades. @@ -564,7 +564,7 @@ SUBROUTINE OpFM_CreateActForceMotionsMesh( p_FAST, y_ED, InitIn_OpFM, OpFM, ErrS ! local variables TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: tmpActForceMotionsMesh !< temporary mesh for interpolating orientation to actuator force points [-] INTEGER(IntKi) :: k ! blade loop counter - INTEGER(IntKi) :: i,j ! node counter + INTEGER(IntKi) :: j ! node counter INTEGER(IntKi) :: ErrStat2 ! temporary Error status of the operation CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None @@ -738,7 +738,7 @@ SUBROUTINE OpFM_CreateTmpActForceMotionsMesh( p_FAST, y_ED, p_OpFM, InitIn_OpFM, REAL(ReKi), DIMENSION(:,:), ALLOCATABLE :: forceNodePositions ! new positions for the force actuator nodes INTEGER(IntKi) :: NumBl ! number of blades INTEGER(IntKi) :: k ! blade loop counter - INTEGER(IntKi) :: i,j ! node counter + INTEGER(IntKi) :: j ! node counter INTEGER(IntKi) :: ErrStat2 ! temporary Error status of the operation CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None @@ -872,7 +872,7 @@ SUBROUTINE CreateTmpStructModelMesh(p_FAST, y_ED, p_OpFM, tmpStructModelMesh, Er !Local variables INTEGER(IntKi) :: nNodesStructModel ! Number of nodes (tower/blade) in the structural model mesh - INTEGER(IntKi) :: i,j ! node counter + INTEGER(IntKi) :: j ! node counter INTEGER(IntKi) :: k ! blade counter INTEGER(IntKi) :: ErrStat2 ! temporary Error status of the operation CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None diff --git a/modules/supercontroller/src/SC_DataEx.f90 b/modules/supercontroller/src/SC_DataEx.f90 index f0a7ab05e..d64559268 100644 --- a/modules/supercontroller/src/SC_DataEx.f90 +++ b/modules/supercontroller/src/SC_DataEx.f90 @@ -139,8 +139,8 @@ SUBROUTINE SC_DX_SetInputs(p_FAST, y_SrvD, SC_DX, ErrStat, ErrMsg ) 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 if ErrStat /= ErrID_None +! INTEGER(IntKi) :: ErrStat2 ! temporary Error status of the operation +! CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None CHARACTER(*), PARAMETER :: RoutineName = 'SC_DX_SetInputs' @@ -166,8 +166,8 @@ SUBROUTINE SC_DX_SetOutputs(p_FAST, u_SrvD, SC_DX, ErrStat, ErrMsg ) 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 if ErrStat /= ErrID_None +! INTEGER(IntKi) :: ErrStat2 ! temporary Error status of the operation +! CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None CHARACTER(*), PARAMETER :: RoutineName = 'SC_DX_SetOutputs' From 6f85979a6b9c42256ba15d2f01c32097f10f249c Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Thu, 22 Apr 2021 10:44:37 -0600 Subject: [PATCH 03/24] Update AeroDyn documentation --- .../aerodyn/examples/ad_airfoil_example.inp | 69 +++++++++-------- .../aerodyn/examples/ad_primary_example.inp | 2 + docs/source/user/aerodyn/input.rst | 77 +++++++++++++++---- docs/source/user/api_change.rst | 7 ++ 4 files changed, 106 insertions(+), 49 deletions(-) diff --git a/docs/source/user/aerodyn/examples/ad_airfoil_example.inp b/docs/source/user/aerodyn/examples/ad_airfoil_example.inp index 5959ae5f4..6a6147643 100644 --- a/docs/source/user/aerodyn/examples/ad_airfoil_example.inp +++ b/docs/source/user/aerodyn/examples/ad_airfoil_example.inp @@ -86,40 +86,43 @@ ! ------------------------------------------------------------------------------ 0.75 Re ! Reynolds number in millions 0 UserProp ! User property (control) setting -true InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +true InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include UA coefficients below this line !........................................ - -0.38 alpha0 ! 0-lift angle of attack, depends on airfoil. - 15.3 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) - -15.3 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] - -11.324 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] - 18.269 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] - -11.324 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] - 1.9408 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It… - -0.8 Cn2 ! As Cn1 for negative AOAs. -"Default" St_sh ! Strouhal's shedding frequency constant. [default = 0.19] - 0.0016 Cd0 ! 2D drag coefficient value at 0-lift. - -0.0328 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does… - 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] - 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] - 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] - 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] - 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] -"Default" x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] -"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] -"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] + -0.38 alpha0 ! 0-lift angle of attack, depends on airfoil. ! THIS IS AN OPTIONAL LINE; if omitted, it will be calculated from the polar data + 15.3 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) ! THIS IS AN OPTIONAL LINE; if omitted, it will be calculated from the polar data + -15.3 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to 0 + -11.324 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to 0 + 18.269 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to 0 + -11.324 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to 0 + 1.9408 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. ! THIS IS AN OPTIONAL LINE; if omitted, it will be calculated from the polar data + -0.8 Cn2 ! As Cn1 for negative AOAs. ! THIS IS AN OPTIONAL LINE; if omitted, it will be calculated from the polar data +"Default" St_sh ! Strouhal's shedding frequency constant. [default = 0.19; unused when UAMod = 4 or 5] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to its default value + 0.0016 Cd0 ! 2D drag coefficient value at 0-lift. ! THIS IS AN OPTIONAL LINE; if omitted, it will be calculated from the polar data + -0.0328 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. ! THIS IS AN OPTIONAL LINE; if omitted, it will be calculated from the polar data + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to 0 + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to 0 + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to 0 + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to 0 + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to 0 +"Default" x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to its default value +"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to its default value +"DEFAULT" UACutout_delta ! Delta angle of attack below UACutout where unsteady aerodynamics begin to turn off (blend with steady solution) (deg) [Specifying the string "Default" sets UACutout_delta to 5 degrees] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to its default value +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5; unused when UAMod = 4 or 5] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to its default value !........................................ ! Table of aerodynamics coefficients 63 NumAlf ! Number of data lines in the following table diff --git a/docs/source/user/aerodyn/examples/ad_primary_example.inp b/docs/source/user/aerodyn/examples/ad_primary_example.inp index 62a691d7f..8697a9835 100644 --- a/docs/source/user/aerodyn/examples/ad_primary_example.inp +++ b/docs/source/user/aerodyn/examples/ad_primary_example.inp @@ -37,6 +37,8 @@ True TIDrag - Include the drag term in the tangential-induc ====== Beddoes-Leishman Unsteady Airfoil Aerodynamics Options ===================================== [used only when AFAeroMod=2] 1 UAMod - Unsteady Aero Model Switch (switch) {1=Baseline model (Original), 2=Gonzalez's variant (changes in Cn,Cc,Cm), 3=Minnema/Pierce variant (changes in Cc and Cm)} [used only when AFAeroMod=2] FALSE FLookup - Flag to indicate whether a lookup for f' will be calculated (TRUE) or whether best-fit exponential equations will be used (FALSE); if FALSE S1-S4 must be provided in airfoil input files (flag) [used only when AFAeroMod=2] + 0.25 UAStartRad - Starting radius for dynamic stall (fraction of rotor radius) [used only when AFAeroMod=2; if line is missing UAStartRad=0] + 0.95 UAEndRad - Ending radius for dynamic stall (fraction of rotor radius) [used only when AFAeroMod=2; if line is missing UAEndRad=1] ====== Airfoil Information ========================================================================= 1 AFTabMod - Interpolation method for multiple airfoil tables {1=1D interpolation on AoA (first table only); 2=2D interpolation on AoA and Re; 3=2D interpolation on AoA and UserProp} (-) 1 InCol_Alfa - The column in the airfoil tables that contains the angle of attack (-) diff --git a/docs/source/user/aerodyn/input.rst b/docs/source/user/aerodyn/input.rst index 2103b2076..9b81efd6e 100644 --- a/docs/source/user/aerodyn/input.rst +++ b/docs/source/user/aerodyn/input.rst @@ -276,11 +276,12 @@ Dynamic Blade-Element/Momentum Theory Options The input parameters in this section are used only when ``WakeMod = 2``. -Set ``DBEMT_Mod`` to 1 for the constant-tau1 model, or set ``DBEMT_Mod`` to 2 -to use a model where tau1 varies with time. +Set ``DBEMT_Mod`` to 1 for the constant-tau1 model, set ``DBEMT_Mod`` to 2 +to use a model where tau1 varies with time, or set ``DBEMT_Mod`` to 3 +to use a continuous-state model with constant tau1. -If ``DBEMT_Mod=1`` (constant-tau1 model), set ``tau1_const`` to the time -constant to use for DBEMT. +If ``DBEMT_Mod=1`` (constant-tau1 model) or ``DBEMT_Mod=3`` (continuous-state constant-tau1 model), +set ``tau1_const`` to the time constant to use for DBEMT. OLAF -- cOnvecting LAgrangian Filaments (Free Vortex Wake) Theory Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -295,13 +296,18 @@ for this input file. Unsteady Airfoil Aerodynamics Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The input parameters in this section are only used when ``AFAeroMod +The input parameters in this section are used only when ``AFAeroMod = 2``. -``UAMod`` determines the UA model. Setting ``UAMod`` to 1 enables -original theoretical developments of B-L, 2 enables the extensions to -B-L developed by González, and 3 enables the extensions to B-L developed -by Minnema/Pierce. **While all of the UA models are documented in this +``UAMod`` determines the UA model. It has the following options: + +- ``1``: the original theoretical developments of B-L (**not currently functional**), +- ``2``: the extensions to B-L developed by González +- ``3``: the extensions to B-L developed by Minnema/Pierce +- ``4``: a continuous-state model developed by Hansen, Gaunna, and Madsen (HGM) +- ``5``: a model similar to HGM with an additional state for vortex generation + +**While all of the UA models are documented in this manual, the original B-L model is not yet functional. Testing has shown that the González and Minnema/Pierce models produce reasonable hysteresis of the normal force, tangential force, and pitching-moment @@ -321,7 +327,21 @@ value, *f’*, will be calculated. When ``FLookup`` is set to TRUE, *f’* is determined via a lookup into the static lift-force coefficient and drag-force coefficient data. **Using best-fit exponential equations (``FLookup = FALSE``) is not yet available, so ``FLookup`` must be -``TRUE`` in this version of AeroDyn.** +``TRUE`` in this version of AeroDyn.** Note, ``FLookup`` is not used when ``UAMod=5``. + +``UAStartRad`` is the starting rotor radius where dynamic stall +will be turned on. Enter a number between 0 and 1, representing a fraction of rotor radius, +to indicate where unsteady aerodynamics should begin turning on. If this line is +omitted from the input file, ``UAStartRad`` will default to 0 (turning on at the blade root). +All blade nodes that are located at a rotor radius less than ``UAStartRad`` will have +unsteady aerodynamics turned off for the entire simulation. + +``UAEndRad`` is the ending rotor radius where dynamic stall +will be turned on. Enter a number between 0 and 1, representing a fraction of rotor radius, +to indicate the last rotor radius where unsteady aerodynamics should be turned on. If this line is +omitted from the input file, ``UAEndRad`` will default to 1 (the blade tip). +All blade nodes that are located at a rotor radius greater than ``UAEndRad`` will have +unsteady aerodynamics turned off for the entire simulation. .. _airfoil_information: @@ -408,10 +428,15 @@ Outputs ~~~~~~~ Specifying ``SumPrint`` to TRUE causes AeroDyn to generate a summary -file with name ``OutFileRoot**.AD.sum*. ``OutFileRoot`` is either +file with name ``.AD.sum``. ```` is either specified in the I/O SETTINGS section of the driver input file when running AeroDyn standalone, or by the OpenFAST program when running a coupled simulation. See :numref:`sec:ad_SumFile` for summary file details. +If ``AFAeroMod=2``, the unsteady aero module will also generate a file +called ``.UA.sum`` that will list all of the UA parameters +used in the airfoil tables. This allows the user to check what values +are being used in case the code has computed the parameters +without user input. AeroDyn can output aerodynamic and kinematic quantities at up to nine nodes specified along the tower and up to nine nodes along each blade. @@ -518,9 +543,11 @@ primary AeroDyn input file is set to use 2D interpolation based on ``Re`` or ``UserProp``. If 1D interpolation (based only on angle of attack) is used, only the first table in the file will be used. -Set ``InclUAdata`` to TRUE if you are including the 32 UA model -parameters (required when ``AFAeroMod = 2`` in the AeroDyn primary -input file): +Set ``InclUAdata`` to TRUE if you are including the UA model +parameters. If this is set to FALSE, all of the UA model parameters +will be determined by the code. Any lines that are missing from this section +will have their values determined by the code, either using a default value +or calculating it based on the polar coefficient data in the airfoil table: - ``alpha0`` specifies the zero-lift AoA (in degrees); @@ -530,6 +557,15 @@ input file): - ``alpha2`` specifies the AoA (in degrees) less than ``alpha0`` for which *f* equals 0.7; approximately the negative stall angle; +- ``alphaUpper`` specifies the AoA (in degrees) of the upper boundary of + fully-attached region of the cn or cl curve. It is used to + compute the separation function when ``UAMod=5``. + +- ``alphaLower`` specifies the AoA (in degrees) of the lower boundary of + fully-attached region of the cn or cl curve. It is used to + compute the separation function when ``UAMod=5``. (The separation function + will have a value of 1 between ``alphaUpper`` and ``alphaLower``.) + - ``eta_e`` is the recovery factor and typically has a value in the range [0.85 to 0.95] for ``UAMod = 1``; if the keyword ``DEFAULT`` is entered in place of a numerical value, ``eta_e`` is set to 0.9 for @@ -660,6 +696,15 @@ input file): UA are disabled; if the keyword ``DEFAULT`` is entered in place of a numerical value, ``UACutOut`` is set to 45. +- ``UACutOut_delta`` is the AoA difference (in degrees) which, together + with ``UACutOut`` determines when unsteady aero begins to turn off; + if the keyword ``DEFAULT`` is entered in place of a + numerical value, ``UACutOut_delta`` is set to 5 degrees. + The unsteady solution is used at angles of attack less than + ``UACutOut - UACutout_delta`` degrees. Above ``UACutout``, the steady solution is + used (i.e., UA is disabled). The steady and unsteady solutions are blended between + those two angles. + - ``filtCutOff`` is the cut-off reduced frequency of the low-pass filter applied to the AoA input to UA, as well as to the pitch rate and pitch acceleration derived from AoA @@ -692,8 +737,8 @@ coefficient, ``Coefs``\ (:,1), the drag-force coefficient, ``InCol_Alfa``, ``InCol_Cl``, ``InCol_Cd``, ``InCol_Cm``, and ``InCol_Cpmin`` in the AIRFOIL INFORMATION section of the AeroDyn primary input file. AoA must be entered in monotonically increasing -order—from lowest to highest AoA—and the first row should be for AoA = -–180 and the last should be for AoA = +180 (unless ``NumAlf = 1``, in +order—from lowest to highest AoA; the first row should be for AoA = +–180 degrees and the last should be for AoA = +180 (unless ``NumAlf = 1``, in which case AoA is unused). If pitching-moment terms are neglected with ``UseBlCm = FALSE`` in the ROTOR/BLADE PROPERTIES section of the AeroDyn primary input file, the column containing pitching-moment diff --git a/docs/source/user/api_change.rst b/docs/source/user/api_change.rst index c952aab5f..382121af5 100644 --- a/docs/source/user/api_change.rst +++ b/docs/source/user/api_change.rst @@ -31,6 +31,8 @@ OpenFAST v2.5.0 to OpenFAST dev Module Line Flag Name Example Value ============================================= ==== =============== ======================================================================================================================================================================================================== AeroDyn 15 TwrTi 0.0000000E+00 6.0000000E+00 1.0000000E+00 1.0000000E-01 [additional column in *Tower Influence and Aerodynamics* table] +AeroDyn 15 40 UAStartRad 0.25 UAStartRad - Starting radius for dynamic stall (fraction of rotor radius) [used only when AFAeroMod=2; if line is missing UAStartRad=0] +AeroDyn 15 41 UAEndRad 0.95 UAEndRad - Ending radius for dynamic stall (fraction of rotor radius) [used only when AFAeroMod=2; if line is missing UAEndRad=1] SubDyn 8 GuyanLoadCorr. False GuyanLoadCorection - Include extra moment from lever arm at interface and rotate FEM for floating SubDyn 15 GuyanDampMod 0 GuyanDampMod - Guyan damping {0=none, 1=Rayleigh Damping, 2=user specified 6x6 matrix} SubDyn 16 RayleighDamp 0.001, 0.003 RayleighDamp - Mass and stiffness proportional damping coefficients (Rayleigh Damping) [only if GuyanDampMod=1] @@ -55,7 +57,12 @@ ServoDyn 65 NumTStC 0 ServoDyn 66 TStCfiles "unused" TStCfiles - Name of the files for tower structural controllers (quoted strings) [unused when NumTStC==0] ServoDyn 67 NumSStC 0 NumSStC - Number of substructure structural controllers (integer) ServoDyn 68 SStCfiles "unused" SStCfiles - Name of the files for substructure structural controllers (quoted strings) [unused when NumSStC==0] +AirFoilTables 12\* alphaUpper 5.0 alphaUpper ! Angle of attack at upper boundary of fully-attached region. (deg) [used only when UAMod=5] ! THIS IS AN OPTIONAL LINE; if omitted, it will be calculated from the polar data +AirFoilTables 13\* alphaLower \-3.0 alphaLower ! Angle of attack at lower boundary of fully-attached region. (deg) [used only when UAMod=5] ! THIS IS AN OPTIONAL LINE; if omitted, it will be calculated from the polar data +AirFoilTables 42\* UACutout_delta "DEFAULT" UACutout_delta ! Delta angle of attack below UACutout where unsteady aerodynamics begin to turn off (blend with steady solution) (deg) [Specifying the string "Default" sets UACutout_delta to 5 degrees] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to its default value ============================================= ==== =============== ======================================================================================================================================================================================================== +\*non-comment line count, excluding lines contained if NumCoords is not 0, and including all OPTIONAL lines in the UA coefficients table. + ============================================= ====== =============== ====================================================================================================================================================================================================== Modified in OpenFAST dev From 6d8f2adaf08e159726efed37104c15491f0c8b9a Mon Sep 17 00:00:00 2001 From: Bonnie Jonkman Date: Thu, 22 Apr 2021 20:15:54 -0600 Subject: [PATCH 04/24] UA driver fixes --- modules/aerodyn/src/AirfoilInfo.f90 | 3 ++- modules/aerodyn/src/UA_Dvr_Subs.f90 | 6 +++--- modules/aerodyn/src/UnsteadyAero.f90 | 15 +++++++++------ modules/aerodyn/src/UnsteadyAero_Driver.f90 | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/modules/aerodyn/src/AirfoilInfo.f90 b/modules/aerodyn/src/AirfoilInfo.f90 index c8aece935..92e9ceb5e 100644 --- a/modules/aerodyn/src/AirfoilInfo.f90 +++ b/modules/aerodyn/src/AirfoilInfo.f90 @@ -470,7 +470,8 @@ SUBROUTINE ReadAFfile ( InitInp, NumCoefsIn, p, ErrStat, ErrMsg, UnEc ) ! Reading Boundary layer file for aeroacoustics CALL ParseVar ( FileInfo, CurLine, 'BL_file' , p%BL_file , ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF (ErrStat2 >= AbortErrLev) p%BL_file = "NOT_SET_IN_AIRFOIL_FILE" + !CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( PathIsRelative( p%BL_file ) ) p%BL_file=trim(PriPath)//trim(p%BL_file) ! How many columns do we need to read in the input and how many total coefficients will be used? diff --git a/modules/aerodyn/src/UA_Dvr_Subs.f90 b/modules/aerodyn/src/UA_Dvr_Subs.f90 index b6bd92b44..1bc740949 100644 --- a/modules/aerodyn/src/UA_Dvr_Subs.f90 +++ b/modules/aerodyn/src/UA_Dvr_Subs.f90 @@ -513,9 +513,9 @@ subroutine Cleanup() end subroutine Cleanup end subroutine ReadTimeSeriesData !-------------------------------------------------------------------------------------------------------------- - subroutine Init_AFI(p, NumAFfiles, afNames, UseCm, AFI_Params, ErrStat, ErrMsg) + subroutine Init_AFI(UAMod, NumAFfiles, afNames, UseCm, AFI_Params, ErrStat, ErrMsg) - type(UA_ParameterType),intent(in) :: p + integer, intent(in ) :: UAMod integer, intent(in ) :: NumAFfiles CHARACTER(1024), intent(in ) :: afNames(NumAFfiles) logical, intent(in ) :: UseCm @@ -556,7 +556,7 @@ subroutine Init_AFI(p, NumAFfiles, afNames, UseCm, AFI_Params, ErrStat, ErrMsg) AFI_InitInputs%InCol_Cpmin = 0 AFI_InitInputs%AFTabMod = AFITable_1 ! 1D-interpolation (on AoA only) - AFI_InitInputs%UA_f_cn = p%UAMod /= UA_HGM ! HGM needs the separation function based on cl instead of cn + AFI_InitInputs%UA_f_cn = UAMod /= UA_HGM ! HGM needs the separation function based on cl instead of cn do i=1,NumAFfiles AFI_InitInputs%FileName = afNames(i) !InitInp%AF_File(i) diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index 509c5b144..c2a89c4ae 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -1153,7 +1153,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & if (p%UAmod == UA_HGMV) then - InitOut%WriteOutputHdr(iOffset+21) = trim(chanPrefix)//''x5' + InitOut%WriteOutputHdr(iOffset+21) = trim(chanPrefix)//'x5' InitOut%WriteOutputUnt(iOffset+21) = '(-)' end if @@ -2016,6 +2016,8 @@ SUBROUTINE HGM_Steady( i, j, u, p, x, AFInfo, ErrStat, ErrMsg ) alphaE = alpha_34 ! Eq. 12 (after substitute of x1 and x2 initializations) alphaF = alphaE + call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) if (p%UAMod==UA_HGM) then x%x(3) = BL_p%c_lalpha * (alphaE-BL_p%alpha0) @@ -2024,11 +2026,11 @@ SUBROUTINE HGM_Steady( i, j, u, p, x, AFInfo, ErrStat, ErrMsg ) !alphaF = x%x(3)/BL_p%c_lalpha + BL_p%alpha0 ! p. 13 !this simplifies to alphaF = alphaE - call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) - x%x(4) = AFI_interp%f_st + !call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) + !x%x(4) = AFI_interp%f_st else !if (p%UAMod==UA_HGMV) then - call AFI_ComputeAirfoilCoefs( alphaE, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) + !call AFI_ComputeAirfoilCoefs( alphaE, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) x%x(3) = AFI_interp%FullyAttached ! ~ (alpha-alphaLower)*c_Rate + c_alphaLower ! find alphaF where cn_FullyAttached(alphaF) = x(3) @@ -2036,11 +2038,12 @@ SUBROUTINE HGM_Steady( i, j, u, p, x, AFInfo, ErrStat, ErrMsg ) ! alphaF = alphaE ! calculate x%x(4) = fs_aF = f_st(alphaF): - call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) - x%x(4) = AFI_interp%f_st + !call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) + !x%x(4) = AFI_interp%f_st end if + x%x(4) = AFI_interp%f_st x%x(5) = 0.0_R8Ki end subroutine HGM_Steady diff --git a/modules/aerodyn/src/UnsteadyAero_Driver.f90 b/modules/aerodyn/src/UnsteadyAero_Driver.f90 index 1bb9c5cc2..438f2184e 100644 --- a/modules/aerodyn/src/UnsteadyAero_Driver.f90 +++ b/modules/aerodyn/src/UnsteadyAero_Driver.f90 @@ -180,7 +180,7 @@ program UnsteadyAero_Driver ! Initialize the Airfoil Info Params afNames(1) = dvrInitInp%AirFoil1 ! All nodes/blades are using the same 2D airfoil AFIndx(1,1) = 1 - call Init_AFI( p, NumAFfiles, afNames, dvrInitInp%UseCm, AFI_Params, errStat, errMsg ) + call Init_AFI( InitInData%UAMod, NumAFfiles, afNames, dvrInitInp%UseCm, AFI_Params, errStat, errMsg ) call checkError() ! call WriteAFITables(AFI_Params(1), dvrInitInp%OutRootName) From 7000763551cce37c391946956ac06ec56ea60f88 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 17 Jun 2021 12:48:12 -0600 Subject: [PATCH 05/24] UA: using rel path for UA driver --- modules/aerodyn/src/UA_Dvr_Subs.f90 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/aerodyn/src/UA_Dvr_Subs.f90 b/modules/aerodyn/src/UA_Dvr_Subs.f90 index 1bc740949..567f636b0 100644 --- a/modules/aerodyn/src/UA_Dvr_Subs.f90 +++ b/modules/aerodyn/src/UA_Dvr_Subs.f90 @@ -48,6 +48,10 @@ subroutine ReadDriverInputFile( inputFile, InitInp, ErrStat, ErrMsg ) integer(IntKi) :: errStat2 ! Status of error message character(1024) :: errMsg2 ! Error message if ErrStat /= ErrID_None character(*), parameter :: RoutineName = 'ReadDriverInputFile' + + character(1024) :: PriPath ! the path to the primary input file + CALL GetPath( inputFile, PriPath ) ! Input files will be relative to the path where the primary input file is located. + ! Initialize the echo file unit to -1 which is the default to prevent echoing, we will alter this based on user input UnEchoLocal = -1 @@ -187,6 +191,7 @@ subroutine ReadDriverInputFile( inputFile, InitInp, ErrStat, ErrMsg ) call Cleanup() return end if + if (PathIsRelative(InitInp%OutRootName)) InitInp%OutRootName = TRIM(PriPath)//TRIM(InitInp%OutRootName) ! InflowVel @@ -253,6 +258,7 @@ subroutine ReadDriverInputFile( inputFile, InitInp, ErrStat, ErrMsg ) call Cleanup() return end if + if (PathIsRelative(InitInp%Airfoil1)) InitInp%Airfoil1 = TRIM(PriPath)//TRIM(InitInp%Airfoil1) ! Chord @@ -635,4 +641,4 @@ subroutine WriteAFITables(AFI_Params,OutRootName) end subroutine WriteAFITables end module UA_Dvr_Subs - \ No newline at end of file + From 6b37de036ca791d19e9b345003ea92a5e790136d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 17 Jun 2021 16:09:55 -0600 Subject: [PATCH 06/24] UA: implemented Oye's dynamic stall model --- modules/aerodyn/src/UnsteadyAero.f90 | 196 ++++++++++++++---- modules/aerodyn/src/UnsteadyAero_Driver.f90 | 1 - modules/aerodyn/src/UnsteadyAero_Registry.txt | 2 + modules/aerodyn/src/UnsteadyAero_Types.f90 | 2 + 4 files changed, 154 insertions(+), 47 deletions(-) diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index 88ef6c24c..af38f5d8d 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -811,7 +811,7 @@ subroutine UA_InitStates_Misc( p, x, xd, OtherState, m, ErrStat, ErrMsg ) ! allocate all the state arrays - if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then + if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV .or. p%UAMod == UA_OYE) then allocate( x%element( p%nNodesPerBlade, p%numBlades ), stat=ErrStat2 ) if (ErrStat2 /= 0) call SetErrStat(ErrID_Fatal,"Cannot allocate x%x.",ErrStat,ErrMsg,RoutineName) @@ -835,6 +835,10 @@ subroutine UA_InitStates_Misc( p, x, xd, OtherState, m, ErrStat, ErrMsg ) allocate( OtherState%BelowThreshold(p%nNodesPerBlade, p%numBlades), stat=ErrStat2) if (ErrStat2 /= 0 ) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%BelowThreshold.", ErrStat, ErrMsg, RoutineName) end if + elseif (p%UAMod == UA_BV) then + print*,'>>> TODO BV alloc' + call AllocAry( xd%alpha_minus1, p%nNodesPerBlade,p%numBlades, 'xd%alpha_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry( xd%alpha_filt_minus1, p%nNodesPerBlade,p%numBlades, 'xd%alpha_filt_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) else call AllocAry( xd%alpha_minus1, p%nNodesPerBlade,p%numBlades, 'xd%alpha_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -933,7 +937,7 @@ subroutine UA_ReInit( p, x, xd, OtherState, m, ErrStat, ErrMsg ) end do end do - if ( p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then + if ( p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV .or. p%UAMod == UA_OYE) then OtherState%n = -1 ! we haven't updated OtherState%xdot, yet @@ -955,6 +959,11 @@ subroutine UA_ReInit( p, x, xd, OtherState, m, ErrStat, ErrMsg ) OtherState%vortexOn = .false. OtherState%BelowThreshold = .true. end if + + elseif (p%UAMod == UA_BV) then + print*,'>>> TODO Reinit BV' + xd%alpha_minus1 = 0.0_ReKi + xd%alpha_filt_minus1 = 0.0_ReKi else OtherState%sigma1 = 1.0_ReKi @@ -1078,10 +1087,13 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & #ifdef UA_OUTS ! Allocate and set the InitOut data - if (p%UAMod == UA_HGM) then + if (p%UAMod == UA_HGM .or. p%UAMod == UA_OYE) then p%NumOuts = 20 elseif(p%UAMod == UA_HGMV) then p%NumOuts = 21 + elseif(p%UAMod == UA_BV) then + p%NumOuts = 10 + print*,'>>> TODO UA_BV numouts' else p%NumOuts = 45 end if @@ -1119,7 +1131,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & InitOut%WriteOutputUnt(iOffset+ 6) ='(-)' InitOut%WriteOutputUnt(iOffset+ 7) ='(-)' - if (p%UAmod == UA_HGM .or. p%UAMod == UA_HGMV) then + if (p%UAmod == UA_HGM .or. p%UAMod == UA_HGMV .or. p%UAMod == UA_OYE) then InitOut%WriteOutputHdr(iOffset+ 8) = trim(chanPrefix)//'omega' InitOut%WriteOutputHdr(iOffset+ 9) = trim(chanPrefix)//'alphaE' @@ -1156,6 +1168,11 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & InitOut%WriteOutputHdr(iOffset+21) = trim(chanPrefix)//'x5' InitOut%WriteOutputUnt(iOffset+21) = '(-)' end if + + elseif(p%UAMod == UA_BV) then + InitOut%WriteOutputHdr(iOffset+ 8) = trim(chanPrefix)//'alpha_dot' + + InitOut%WriteOutputUnt(iOffset+ 8) = '(deg/s)' else @@ -1297,8 +1314,9 @@ subroutine UA_ValidateInput(InitInp, ErrStat, ErrMsg) ErrStat = ErrID_None ErrMsg = "" - if (InitInp%UAMod < UA_Gonzalez .or. InitInp%UAMod > UA_HGMV ) call SetErrStat( ErrID_Fatal, & - "In this version, UAMod must be 2 (Gonzalez's variant), 3 (Minnema/Pierce variant), 4 (continuous HGM model), or 5 (HGM with vortex).", ErrStat, ErrMsg, RoutineName ) ! NOTE: for later- 1 (baseline/original) + if (InitInp%UAMod < UA_Gonzalez .or. InitInp%UAMod > UA_BV ) call SetErrStat( ErrID_Fatal, & + "In this version, UAMod must be 2 (Gonzalez's variant), 3 (Minnema/Pierce variant), 4 (continuous HGM model), 5 (HGM with vortex)& + &6 (Oye), 7 (Boing-Vertol)", ErrStat, ErrMsg, RoutineName ) ! NOTE: for later- 1 (baseline/original) if (.not. InitInp%FLookUp ) call SetErrStat( ErrID_Fatal, 'FLookUp must be TRUE for this version.', ErrStat, ErrMsg, RoutineName ) @@ -1329,7 +1347,7 @@ subroutine UA_ValidateAFI(UAMod, AFInfo, ErrStat, ErrMsg) if ( AFInfo%Table(j)%InclUAdata ) then ! parameters used only for UAMod/=UA_HGM) - if (UAMod /= UA_HGM) then + if (UAMod == UA_Baseline .or. UAMod == UA_Gonzalez .or. UAMod == UA_MinnemaPierce .or. UAMod == UA_HGMV) then if (UAMod /= UA_HGMV) then if ( EqualRealNos(AFInfo%Table(j)%UA_BL%St_sh, 0.0_ReKi) ) then @@ -1359,7 +1377,7 @@ subroutine UA_ValidateAFI(UAMod, AFInfo, ErrStat, ErrMsg) if ( AFInfo%Table(j)%UA_BL%filtCutOff < 0.0_ReKi ) then call SetErrStat(ErrID_Fatal, 'UA filtCutOff parameter must be greater than 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) end if - end if + end if ! not UA_HGMV if ( AFInfo%Table(j)%UA_BL%T_VL <= 0.0_ReKi ) then call SetErrStat(ErrID_Fatal, 'UA T_VL parameter must be greater than 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) @@ -1377,15 +1395,19 @@ subroutine UA_ValidateAFI(UAMod, AFInfo, ErrStat, ErrMsg) if ( AFInfo%Table(j)%UA_BL%alpha0 > pi .or. AFInfo%Table(j)%UA_BL%alpha0 < -pi ) then call SetErrStat(ErrID_Fatal, 'UA alpha0 parameter must be between -180 and 180 degrees in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) end if - end if + end if ! Not UA_HGM - if (UAMod == UA_HGM .or. UAMod == UA_HGMV) then + if (UAMod == UA_HGM .or. UAMod == UA_HGMV .or. UAMod == UA_OYE) then cl_fs = InterpStp( AFInfo%Table(j)%UA_BL%UACutout, AFInfo%Table(j)%alpha, AFInfo%Table(j)%Coefs(:,AFInfo%ColUAf), indx, AFInfo%Table(j)%NumAlf ) if (.not. EqualRealNos( cl_fs, 0.0_ReKi ) ) then call SetErrStat(ErrID_Severe, 'UA cutout parameter should be at a value where the separation function is 0 in "'//trim(AFInfo%FileName)//'".'// & " Separation function is "//trim(num2lstr(cl_fs)), ErrStat, ErrMsg, RoutineName ) end if end if + + if (UAMod == UA_BV) then + ! TODO + endif ! variables used in all UA models: if ( AFInfo%Table(j)%UA_BL%T_f0 <= 0.0_ReKi ) then @@ -1396,7 +1418,7 @@ subroutine UA_ValidateAFI(UAMod, AFInfo, ErrStat, ErrMsg) call SetErrStat(ErrID_Fatal, 'UA T_p parameter must be greater than 0 in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) end if - end if + end if ! UAtable included if ( AFInfo%Table(j)%UA_BL%UACutout < 0.0_ReKi ) then call SetErrStat(ErrID_Fatal, 'UA UACutout parameter must not be negative in "'//trim(AFInfo%FileName)//'".', ErrStat, ErrMsg, RoutineName ) @@ -1411,7 +1433,7 @@ subroutine UA_ValidateAFI(UAMod, AFInfo, ErrStat, ErrMsg) if (ErrStat >= AbortErrLev) return - if (UAMod /= UA_HGM .and. UAMod /= UA_HGMV) then + if (UAMod == UA_Baseline .or. UAMod == UA_Gonzalez .or. UAMod == UA_MinnemaPierce) then ! check interpolated values: do j=2, AFInfo%NumTabs @@ -1450,8 +1472,8 @@ subroutine UA_TurnOff_param(p, AFInfo, ErrStat, ErrMsg) end do - if (p%UAMod == UA_HGM) then - ! unsteady aerodynamics will be turned off + if (p%UAMod == UA_HGM .or. p%UAMod == UA_OYE) then + ! unsteady aerodynamics will be turned off if Cl,alpha = 0 do j=1, AFInfo%NumTabs if ( EqualRealNos(AFInfo%Table(j)%UA_BL%C_lalpha, 0.0_ReKi) ) then ErrStat = ErrID_Fatal @@ -1469,9 +1491,12 @@ subroutine UA_TurnOff_param(p, AFInfo, ErrStat, ErrMsg) return end if end do + + elseif (p%UAMod == UA_HGMV) then + ! pass - elseif (p%UAMod /= UA_HGMV) then !also includes HGMV model - ! unsteady aerodynamics will be turned off + elseif (p%UAMod == UA_Baseline .or. p%UAMod == UA_Gonzalez .or. p%UAMod == UA_MinnemaPierce) then + ! unsteady aerodynamics will be turned off is Cn,alpha =0 do j=1, AFInfo%NumTabs if ( EqualRealNos(AFInfo%Table(j)%UA_BL%C_nalpha, 0.0_ReKi) ) then ErrStat = ErrID_Fatal @@ -1490,6 +1515,9 @@ subroutine UA_TurnOff_param(p, AFInfo, ErrStat, ErrMsg) end if end do + elseif (p%UAMod == UA_BV) then + ! pass + end if end subroutine UA_TurnOff_param @@ -1819,7 +1847,6 @@ subroutine UA_UpdateStates( i, j, t, n, u, uTimes, p, x, xd, OtherState, AFInfo, call UA_fixInputs(u_interp_raw, u_interp, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then @@ -1831,6 +1858,12 @@ subroutine UA_UpdateStates( i, j, t, n, u, uTimes, p, x, xd, OtherState, AFInfo, call UA_ABM4( i, j, t, n, u, utimes, p, x, OtherState, AFInfo, m, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (.not. p%ShedEffect) then + ! Safety + x%element(i,j)%x(1) = 0.0_R8Ki + x%element(i,j)%x(2) = 0.0_R8Ki + endif + x%element(i,j)%x(4) = max( min( x%element(i,j)%x(4), 1.0_R8Ki ), 0.0_R8Ki ) ! let's make sure the states aren't getting out of control when we are supposed to be turning off UA anyway. @@ -1895,15 +1928,37 @@ subroutine UA_UpdateStates( i, j, t, n, u, uTimes, p, x, xd, OtherState, AFInfo, end if end if - end if + end if ! Below/above threshold end if ! UA is fully on - end if ! p%UAMod == UA_HGMV + end if ! Vortex on/off + end if ! p%UAMod == UA_HGMV + + elseif (p%UAMod == UA_OYE) then + ! + ! initialize states to steady-state values: + if (OtherState%FirstPass(i,j)) then + call HGM_Steady( i, j, u_interp, p, x%element(i,j), AFInfo, ErrStat2, ErrMsg2 ) end if - - else + call UA_ABM4( i, j, t, n, u, utimes, p, x, OtherState, AFInfo, m, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + !x%element(i,j)%x(1) = 0.0_R8Ki + !x%element(i,j)%x(2) = 0.0_R8Ki + !x%element(i,j)%x(3) = 0.0_R8Ki + + x%element(i,j)%x(4) = max( min( x%element(i,j)%x(4), 1.0_R8Ki ), 0.0_R8Ki ) + + ! let's make sure the states aren't getting out of control when we are supposed to be turning off UA anyway. + call UA_BlendSteadyStates( i, j, u_interp, p, AFInfo, x%element(i,j), m%FirstWarn_UA_off, m%weight(i,j), ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + elseif (p%UAMod == UA_BV) then + ! + print*,'>>> TODO Update states BV', p%UAMod, UA_BV + + elseif (p%UAMod == UA_Baseline .or. p%UAMod == UA_Gonzalez .or. p%UAMod == UA_MinnemaPierce) then if (n<=0) return ! previous logic (before adding UA_HGM required n > 0 before UA_UpdateStates was called) ! Update discrete states: @@ -1941,7 +1996,7 @@ subroutine UA_InitStates_AllNodes( u, p, x, OtherState, AFInfo, AFIndx ) !............................................................................................................................... ! compute UA states at t=0 (with known inputs) !............................................................................................................................... - if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then + if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV .or. p%UAMod == UA_OYE) then do j = 1,size(p%UA_off_forGood,2) ! blades do i = 1,size(p%UA_off_forGood,1) ! nodes @@ -2011,15 +2066,23 @@ SUBROUTINE HGM_Steady( i, j, u, p, x, AFInfo, ErrStat, ErrMsg ) ! Steady states - x%x(1) = BL_p%A1 * alpha_34 - x%x(2) = BL_p%A2 * alpha_34 + if (p%UAMod==UA_OYE .or. (.not.p%ShedEffect)) then + x%x(1) = 0.0_R8Ki + x%x(2) = 0.0_R8Ki + else + x%x(1) = BL_p%A1 * alpha_34 + x%x(2) = BL_p%A2 * alpha_34 + endif alphaE = alpha_34 ! Eq. 12 (after substitute of x1 and x2 initializations) alphaF = alphaE call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - if (p%UAMod==UA_HGM) then + if (p%UAMod==UA_OYE) then + x%x(3) = AFI_interp%Cl ! Not used + + elseif (p%UAMod==UA_HGM) then x%x(3) = BL_p%c_lalpha * (alphaE-BL_p%alpha0) ! calculate x%x(4) = fs_aF = f_st(alphaF): @@ -2029,7 +2092,7 @@ SUBROUTINE HGM_Steady( i, j, u, p, x, AFInfo, ErrStat, ErrMsg ) !call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) !x%x(4) = AFI_interp%f_st - else !if (p%UAMod==UA_HGMV) then + elseif (p%UAMod==UA_HGMV) then !call AFI_ComputeAirfoilCoefs( alphaE, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) x%x(3) = AFI_interp%FullyAttached ! ~ (alpha-alphaLower)*c_Rate + c_alphaLower @@ -2040,7 +2103,9 @@ SUBROUTINE HGM_Steady( i, j, u, p, x, AFInfo, ErrStat, ErrMsg ) ! calculate x%x(4) = fs_aF = f_st(alphaF): !call AFI_ComputeAirfoilCoefs( alphaF, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat, ErrMsg) !x%x(4) = AFI_interp%f_st - + else + print*,'HGM_steady, should never happen' + STOP end if x%x(4) = AFI_interp%f_st @@ -2118,7 +2183,11 @@ subroutine UA_CalcContStateDeriv( i, j, t, u_in, p, x, OtherState, AFInfo, m, dx if (p%UAMod == UA_HGM) then !note: BL_p%c_lalpha cannot be zero. UA is turned off at initialization if this occurs. alphaF = x%x(3)/BL_p%c_lalpha + BL_p%alpha0 ! p. 13 - else + + else if (p%UAMod == UA_OYE) then + alphaF = alpha_34 + + else if (p%UAMod == UA_HGMV) then if (x%x(3) < BL_p%c_alphaLowerWrap) then alphaF = (x%x(3) - BL_p%c_alphaLowerWrap) / BL_p%c_RateWrap + BL_p%alphaLowerWrap elseif (x%x(3) < BL_p%c_alphaLower) then @@ -2149,13 +2218,13 @@ subroutine UA_CalcContStateDeriv( i, j, t, u_in, p, x, OtherState, AFInfo, m, dx x4 = max( min( x%x(4), 1.0_R8Ki ), 0.0_R8Ki ) call AddOrSub2Pi(real(x%x(1),ReKi), alpha_34) ! make sure we use the same alpha_34 for both x1 and x2 equations. -if (p%ShedEffect) then - dxdt%x(1) = -1.0_R8Ki / Tu * (BL_p%b1 + p%c(i,j) * U_dot/(2*u%u**2)) * x%x(1) + BL_p%b1 * BL_p%A1 / Tu * alpha_34 - dxdt%x(2) = -1.0_R8Ki / Tu * (BL_p%b2 + p%c(i,j) * U_dot/(2*u%u**2)) * x%x(2) + BL_p%b2 * BL_p%A2 / Tu * alpha_34 -else - dxdt%x(1) = 0.0_ReKi - dxdt%x(2) = 0.0_ReKi -endif + if (p%ShedEffect) then + dxdt%x(1) = -1.0_R8Ki / Tu * (BL_p%b1 + p%c(i,j) * U_dot/(2*u%u**2)) * x%x(1) + BL_p%b1 * BL_p%A1 / Tu * alpha_34 + dxdt%x(2) = -1.0_R8Ki / Tu * (BL_p%b2 + p%c(i,j) * U_dot/(2*u%u**2)) * x%x(2) + BL_p%b2 * BL_p%A2 / Tu * alpha_34 + else + dxdt%x(1) = 0.0_ReKi + dxdt%x(2) = 0.0_ReKi + endif if (p%UAMod == UA_HGM) then call AddOrSub2Pi(BL_p%alpha0, alphaE) @@ -2163,7 +2232,15 @@ subroutine UA_CalcContStateDeriv( i, j, t, u_in, p, x, OtherState, AFInfo, m, dx dxdt%x(3) = -1.0_R8Ki / BL_p%T_p * x%x(3) + 1.0_ReKi / BL_p%T_p * Clp dxdt%x(4) = -1.0_R8Ki / BL_p%T_f0 * x4 + 1.0_ReKi / BL_p%T_f0 * AFI_AlphaF%f_st dxdt%x(5) = 0.0_R8Ki - else !if (p%UAMod == UA_HGMV) then + + elseif (p%UAMod == UA_OYE) then + dxdt%x(4) = -1.0_R8Ki / BL_p%T_f0 * x4 + 1.0_ReKi / BL_p%T_f0 * AFI_AlphaF%f_st + dxdt%x(1) = 0.0_R8Ki + dxdt%x(2) = 0.0_R8Ki + dxdt%x(3) = 0.0_R8Ki + dxdt%x(5) = 0.0_R8Ki + + elseif (p%UAMod == UA_HGMV) then call AFI_ComputeAirfoilCoefs( alphaE, u%Re, u%UserProp, AFInfo, AFI_AlphaE, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -2194,6 +2271,9 @@ subroutine UA_CalcContStateDeriv( i, j, t, u_in, p, x, OtherState, AFInfo, m, dx end if dxdt%x(5) = cv_dot - x%x(5)/(BL_p%T_V0 * Tu) + else + print*,'>>> UA_CalcContStateDeriv, should never happen.' + STOP ! should never happen end if END SUBROUTINE UA_CalcContStateDeriv @@ -2227,10 +2307,10 @@ SUBROUTINE Get_HGM_constants(i, j, p, u, x, BL_p, Tu, alpha_34, alphaE) if (present(alphaE)) then ! Variables derived from states - if (p%ShedEffect) then - alphaE = alpha_34*(1.0_ReKi - BL_p%A1 - BL_p%A2) + x%x(1) + x%x(2) ! Eq. 12 - else + if (p%UAMod == UA_OYE .or. .not. p%ShedEffect) then alphaE = alpha_34 + else + alphaE = alpha_34*(1.0_ReKi - BL_p%A1 - BL_p%A2) + x%x(1) + x%x(2) ! Eq. 12 endif call MPi2Pi(alphaE) end if @@ -2628,15 +2708,21 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, KC%alpha_filt_cur = u%alpha KC%ds = 2.0_ReKi*u%U*p%dt/p%c(i, j) - alphaE = u%alpha + alphaE = u%alpha ! NOTE: no omega for UA= AbortErrLev) return call Get_HGM_constants(i, j, p, u, x_in, BL_p, Tu, alpha_34, alphaE) ! compute Tu, alpha_34, and alphaE - - call AFI_ComputeAirfoilCoefs( alphaE, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2 ) + + call AFI_ComputeAirfoilCoefs( alphaE, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Constraining x4 between 0 and 1 increases numerical stability (should be done elsewhere, but we'll double check here in case there were perturbations on the state value) @@ -2660,8 +2746,22 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, cl_fs = AFI_interp%FullySeparate cl_fa = AFI_interp%FullyAttached fs_aE = AFI_interp%f_st + + if (p%UAMod == UA_OYE) then + ! calculate fully attached value: + call AddOrSub2Pi(BL_p%alpha0, alphaE) + cl_fa = (alphaE - BL_p%alpha0) * BL_p%c_lalpha ! Cl fully attached + y%Cl = x4 * cl_fa + (1.0_ReKi - x4) * cl_fs ! TODO consider adding simple corrections + pi * Tu * u%omega + y%Cd = AFI_interp%Cd ! TODO consider adding simple corrections + if (AFInfo%ColCm == 0) then ! we don't have a cm column, so make everything 0 + y%Cm = 0.0_ReKi + else + y%Cm = AFI_interp%Cm ! TODO consider adding simple corrections + y%Cl * delta_c_mf_primeprime - piBy2 * Tu * u%omega + endif + y%Cn = y%Cl*cos(u%alpha) + y%Cd*sin(u%alpha) + y%Cc = y%Cl*sin(u%alpha) - y%Cd*cos(u%alpha) - if (p%UAMod == UA_HGM) then + elseif (p%UAMod == UA_HGM) then ! calculate fully attached value: call AddOrSub2Pi(BL_p%alpha0, alphaE) cl_fa = (alphaE - BL_p%alpha0) * BL_p%c_lalpha @@ -2878,7 +2978,7 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, y%WriteOutput(iOffset+ 6) = y%Cd y%WriteOutput(iOffset+ 7) = y%Cm - if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV) then + if (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV .or. p%UAMod == UA_OYE) then y%WriteOutput(iOffset+ 8) = u%omega*R2D y%WriteOutput(iOffset+ 9) = alphaE*R2D y%WriteOutput(iOffset+10) = Tu @@ -2897,8 +2997,12 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, if (p%UAMod == UA_HGMV) then y%WriteOutput(iOffset+21) = x_in%x(5) !x%element(i,j)%x(5) end if + + elseif(p%UAMod == UA_BV) then + print*,'>>> TODO UA_BV write outputs' else + ! Baseline, Gonzales, MinnemaPierce y%WriteOutput(iOffset+ 8) = KC%Cn_alpha_q_circ ! CNCP in ADv14 y%WriteOutput(iOffset+ 9) = KC%Cn_alpha_q_nc ! CNIQ in ADv14 y%WriteOutput(iOffset+10) = KC%Cn_pot diff --git a/modules/aerodyn/src/UnsteadyAero_Driver.f90 b/modules/aerodyn/src/UnsteadyAero_Driver.f90 index 438f2184e..cd1591a12 100644 --- a/modules/aerodyn/src/UnsteadyAero_Driver.f90 +++ b/modules/aerodyn/src/UnsteadyAero_Driver.f90 @@ -23,7 +23,6 @@ - program UnsteadyAero_Driver use NWTC_Library diff --git a/modules/aerodyn/src/UnsteadyAero_Registry.txt b/modules/aerodyn/src/UnsteadyAero_Registry.txt index 562e6adaf..4d1d9db45 100644 --- a/modules/aerodyn/src/UnsteadyAero_Registry.txt +++ b/modules/aerodyn/src/UnsteadyAero_Registry.txt @@ -22,6 +22,8 @@ param UnsteadyAero/UA - INTEGER UA_Gonzalez param UnsteadyAero/UA - INTEGER UA_MinnemaPierce - 3 - "[Minnema/Pierce variant (changes in Cc and Cm)]" - param UnsteadyAero/UA - INTEGER UA_HGM - 4 - "[continuous variant of HGM (Hansen) model]" - param UnsteadyAero/UA - INTEGER UA_HGMV - 5 - "[continuous variant of HGM (Hansen) model with vortex modifications]" - +param UnsteadyAero/UA - INTEGER UA_Oye - 6 - "Stieg Oye dynamic stall model" - +param UnsteadyAero/UA - INTEGER UA_BV - 7 - "Boeing-Vertol dynamic stall model (e.g. used in CACTUS)" - # ..... Initialization data ....................................................................................................... # Define inputs that the initialization routine may need here: diff --git a/modules/aerodyn/src/UnsteadyAero_Types.f90 b/modules/aerodyn/src/UnsteadyAero_Types.f90 index 5ac45e954..fe5e2f958 100644 --- a/modules/aerodyn/src/UnsteadyAero_Types.f90 +++ b/modules/aerodyn/src/UnsteadyAero_Types.f90 @@ -39,6 +39,8 @@ MODULE UnsteadyAero_Types INTEGER(IntKi), PUBLIC, PARAMETER :: UA_MinnemaPierce = 3 ! [Minnema/Pierce variant (changes in Cc and Cm)] [-] INTEGER(IntKi), PUBLIC, PARAMETER :: UA_HGM = 4 ! [continuous variant of HGM (Hansen) model] [-] INTEGER(IntKi), PUBLIC, PARAMETER :: UA_HGMV = 5 ! [continuous variant of HGM (Hansen) model with vortex modifications] [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: UA_Oye = 6 ! Stieg Oye dynamic stall model [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: UA_BV = 7 ! Boeing-Vertol dynamic stall model (e.g. used in CACTUS) [-] ! ========= UA_InitInputType ======= TYPE, PUBLIC :: UA_InitInputType REAL(DbKi) :: dt !< time step [s] From 25cc24989dab4aac7fc81072c8b7fec954661065 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 17 Jun 2021 18:47:07 -0600 Subject: [PATCH 07/24] UA: implemented Boeing-Vertol dynamic stall model --- modules/aerodyn/src/UnsteadyAero.f90 | 172 ++++++++++++++---- modules/aerodyn/src/UnsteadyAero_Registry.txt | 1 + modules/aerodyn/src/UnsteadyAero_Types.f90 | 66 +++++++ 3 files changed, 208 insertions(+), 31 deletions(-) diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index af38f5d8d..2aad576b1 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -403,7 +403,7 @@ subroutine ComputeKelvinChain( i, j, u, p, xd, OtherState, misc, AFInfo, KC, BL_ ! This filter is a Simple Infinite Impulse Response Filter ! See https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter - dynamicFilterCutoffHz = max( 1.0_ReKi, u%U ) * BL_p%filtCutOff / PI / p%C(i,j) + dynamicFilterCutoffHz = max( 1.0_ReKi, u%U ) * BL_p%filtCutOff / PI / p%c(i,j) LowPassConst = exp(-2.0_ReKi*PI*p%dt*dynamicFilterCutoffHz) ! from Eqn 1.8 [7] KC%alpha_filt_cur = LowPassConst*alpha_filt_minus1 + (1.0_ReKi-LowPassConst)*u%alpha ! from eq 1.8 [1: typo in documentation, though] @@ -836,9 +836,9 @@ subroutine UA_InitStates_Misc( p, x, xd, OtherState, m, ErrStat, ErrMsg ) if (ErrStat2 /= 0 ) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%BelowThreshold.", ErrStat, ErrMsg, RoutineName) end if elseif (p%UAMod == UA_BV) then - print*,'>>> TODO BV alloc' - call AllocAry( xd%alpha_minus1, p%nNodesPerBlade,p%numBlades, 'xd%alpha_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry( xd%alpha_minus1 , p%nNodesPerBlade,p%numBlades, 'xd%alpha_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) call AllocAry( xd%alpha_filt_minus1, p%nNodesPerBlade,p%numBlades, 'xd%alpha_filt_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry( xd%alpha_dot , p%nNodesPerBlade,p%numBlades, 'xd%alpha_dot', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) else call AllocAry( xd%alpha_minus1, p%nNodesPerBlade,p%numBlades, 'xd%alpha_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -961,9 +961,10 @@ subroutine UA_ReInit( p, x, xd, OtherState, m, ErrStat, ErrMsg ) end if elseif (p%UAMod == UA_BV) then - print*,'>>> TODO Reinit BV' + xd%alpha_minus1 = 0.0_ReKi xd%alpha_filt_minus1 = 0.0_ReKi + xd%alpha_dot = 0.0_ReKi else OtherState%sigma1 = 1.0_ReKi @@ -1092,8 +1093,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & elseif(p%UAMod == UA_HGMV) then p%NumOuts = 21 elseif(p%UAMod == UA_BV) then - p%NumOuts = 10 - print*,'>>> TODO UA_BV numouts' + p%NumOuts = 13 else p%NumOuts = 45 end if @@ -1170,9 +1170,19 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & end if elseif(p%UAMod == UA_BV) then - InitOut%WriteOutputHdr(iOffset+ 8) = trim(chanPrefix)//'alpha_dot' + InitOut%WriteOutputHdr(iOffset+ 8) = trim(chanPrefix)//'omega' + InitOut%WriteOutputHdr(iOffset+ 9) = trim(chanPrefix)//'alphaE' + InitOut%WriteOutputHdr(iOffset+10) = trim(chanPrefix)//'Tu' + InitOut%WriteOutputHdr(iOffset+11) = trim(chanPrefix)//'alpha_34' + InitOut%WriteOutputHdr(iOffset+12) = trim(chanPrefix)//'alpha_dot' + InitOut%WriteOutputHdr(iOffset+13) = trim(chanPrefix)//'delta_alpha' - InitOut%WriteOutputUnt(iOffset+ 8) = '(deg/s)' + InitOut%WriteOutputUnt(iOffset+ 8) = '(deg/sec)' + InitOut%WriteOutputUnt(iOffset+ 9) = '(deg)' + InitOut%WriteOutputUnt(iOffset+10) = '(s)' + InitOut%WriteOutputUnt(iOffset+11) = '(deg)' + InitOut%WriteOutputUnt(iOffset+12) = '(deg/sec)' + InitOut%WriteOutputUnt(iOffset+13) = '(deg)' else @@ -1522,10 +1532,68 @@ subroutine UA_TurnOff_param(p, AFInfo, ErrStat, ErrMsg) end subroutine UA_TurnOff_param !============================================================================== -subroutine UA_UpdateDiscOtherState( i, j, u, p, xd, OtherState, AFInfo, m, ErrStat, ErrMsg ) -! Routine for updating discrete states and other states (note it breaks the framework) -!.............................................................................. +!> Update discrete states for Boieng Vertol model +subroutine UA_UpdateDiscOtherState_BV( i, j, u, p, xd, OtherState, AFInfo, m, ErrStat, ErrMsg ) + integer , intent(in ) :: i !< node index within a blade + integer , intent(in ) :: j !< blade index + type(UA_InputType), intent(in ) :: u !< Inputs at Time + type(UA_ParameterType), intent(in ) :: p !< Parameters + type(UA_DiscreteStateType), intent(inout) :: xd !< In: Discrete states at Time; Out: Discrete states at Time + Interval + type(UA_OtherStateType), intent(inout) :: OtherState !< Other states + type(UA_MiscVarType), intent(inout) :: m !< 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 + real(ReKi), parameter :: filtCutOff = 0.5_ReKi ! CutOff used to filter angle of attack. Alternative, use BL_p (see KelvinChain) + logical, parameter :: filterAlpha =.False. ! Parameter to filter the angle of attack before computing finite differences + real(ReKi) :: alpha_34 ! angle of attack at 3/4 point + real(ReKi) :: alpha_minus1 ! 3/4 chord angle of attack at + real(ReKi) :: alpha_filt_cur ! + real(ReKi) :: alpha_filt_minus1 ! + real(ReKi) :: Tu ! Time constant based on u=Vrel and chord + real(ReKi) :: dynamicFilterCutoffHz ! find frequency based on reduced frequency of k = BL_p%filtCutOff + real(ReKi) :: LowPassConst + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + ErrStat = ErrID_None + ErrMsg = "" + + ! --- Filter angle of attack + ! Using angle of attack at AC or 3/4 point + alpha_34 = Get_Alpha34(u%v_ac, u%omega, 0.5_ReKi*p%c(i,j)) + + ! Angle of attack at previous time + if (OtherState%FirstPass(i,j)) then + alpha_minus1 = alpha_34 + alpha_filt_minus1 = alpha_34 + else + alpha_minus1 = xd%alpha_minus1(i,j) + alpha_filt_minus1 = xd%alpha_filt_minus1(i,j) + end if + + if (filterAlpha) then + ! Using a simple Infinite Impulse Response Filter (same a K_alpha in UnsteadyAero manual) + dynamicFilterCutoffHz = max( 1.0_ReKi, u%U ) * filtCutOff / PI / p%c(i,j) + LowPassConst = exp(-2.0_ReKi*PI*p%dt*dynamicFilterCutoffHz) + alpha_filt_cur = LowPassConst*alpha_filt_minus1 + (1.0_ReKi-LowPassConst)*alpha_34 + else + alpha_filt_cur = alpha_34 ! #noFilter + endif + + ! --- Update states to t+dt + xd%alpha_minus1(i,j) = alpha_34 + xd%alpha_filt_minus1(i,j) = alpha_filt_cur + xd%alpha_dot = ( alpha_filt_cur - alpha_filt_minus1 ) / p%dt +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'UA_UpdateDiscOtherState_BV') + Failed = ErrStat>=ErrID_Fatal + end function Failed +end subroutine UA_UpdateDiscOtherState_BV +!============================================================================== +!> Routine for updating discrete states and other states for Beddoes-Leishman types models (note it breaks the framework) +subroutine UA_UpdateDiscOtherState( i, j, u, p, xd, OtherState, AFInfo, m, ErrStat, ErrMsg ) integer , intent(in ) :: i ! node index within a blade integer , intent(in ) :: j ! blade index type(UA_InputType), intent(in ) :: u ! Inputs at Time @@ -1937,26 +2005,20 @@ subroutine UA_UpdateStates( i, j, t, n, u, uTimes, p, x, xd, OtherState, AFInfo, end if ! p%UAMod == UA_HGMV elseif (p%UAMod == UA_OYE) then - ! - ! initialize states to steady-state values: + + ! First time, initialize states to steady-state values: if (OtherState%FirstPass(i,j)) then call HGM_Steady( i, j, u_interp, p, x%element(i,j), AFInfo, ErrStat2, ErrMsg2 ) end if - call UA_ABM4( i, j, t, n, u, utimes, p, x, OtherState, AFInfo, m, ErrStat2, ErrMsg2 ) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - !x%element(i,j)%x(1) = 0.0_R8Ki - !x%element(i,j)%x(2) = 0.0_R8Ki - !x%element(i,j)%x(3) = 0.0_R8Ki - + ! Time integrate + call UA_ABM4(i, j, t, n, u, utimes, p, x, OtherState, AFInfo, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + ! Make sure the states aren't getting out of control x%element(i,j)%x(4) = max( min( x%element(i,j)%x(4), 1.0_R8Ki ), 0.0_R8Ki ) - - ! let's make sure the states aren't getting out of control when we are supposed to be turning off UA anyway. - call UA_BlendSteadyStates( i, j, u_interp, p, AFInfo, x%element(i,j), m%FirstWarn_UA_off, m%weight(i,j), ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call UA_BlendSteadyStates(i, j, u_interp, p, AFInfo, x%element(i,j), m%FirstWarn_UA_off, m%weight(i,j), ErrStat2, ErrMsg2); CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) elseif (p%UAMod == UA_BV) then - ! - print*,'>>> TODO Update states BV', p%UAMod, UA_BV + ! Integrate discrete states (alpha_dot, alpha_filt_minus1) + call UA_UpdateDiscOtherState_BV( i, j, u_interp, p, xd, OtherState, AFInfo, m, ErrStat2, ErrMsg2 ) elseif (p%UAMod == UA_Baseline .or. p%UAMod == UA_Gonzalez .or. p%UAMod == UA_MinnemaPierce) then if (n<=0) return ! previous logic (before adding UA_HGM required n > 0 before UA_UpdateStates was called) @@ -2300,10 +2362,12 @@ SUBROUTINE Get_HGM_constants(i, j, p, u, x, BL_p, Tu, alpha_34, alphaE) Tu = p%c(i,j) / (2.0_ReKi* max(u%u, UA_u_min)) ! Eq. 23 Tu = min(Tu, 50.0_ReKi) ! ensure the time constant doesn't exceed 50 s. Tu = max(Tu, 0.001_ReKi) ! ensure the time constant doesn't get too small, either. + ! Tu = Get_Tu(u%u, p%c(i,j)) if (present(alpha_34)) then vx_34 = u%v_ac(1) - u%omega * 0.5_ReKi*p%c(i,j) ! Eq. 1 alpha_34 = atan2(vx_34, u%v_ac(2) ) ! page 5 definitions + !alpha_34 = Get_Alpha34(u%v_ac, u%omega, 0.5_ReKi*p%c(i,j)) if (present(alphaE)) then ! Variables derived from states @@ -2317,6 +2381,26 @@ SUBROUTINE Get_HGM_constants(i, j, p, u, x, BL_p, Tu, alpha_34, alphaE) end if END SUBROUTINE Get_HGM_constants + +!> Compute angle of attack at 3/4 chord point based on values at Aerodynamic center +real(ReKi) function Get_Alpha34(v_ac, omega, d_ac_to_34) + real(ReKi), intent(in) :: v_ac(2) !< Velocity at aerodynamic center + real(ReKi), intent(in) :: omega !< pitching rate of airfoil + real(ReKi), intent(in) :: d_ac_to_34 !< distance from aerodynamic center to 3/4 chord point + Get_Alpha34 = atan2(v_ac(1) - omega * d_ac_to_34, v_ac(2) ) +end function Get_Alpha34 + +!> Compute time constant based on relative velocity u_rel +real(ReKi) function Get_Tu(u_rel, chord) + real(ReKi), intent(in) :: u_rel !< relative velocity of airfoil + real(ReKi), intent(in) :: chord !< airfoil chord + Get_Tu = chord / (2.0_ReKi* max(u_rel, UA_u_min)) + Get_Tu = min(Get_Tu, 50.0_ReKi) ! ensure the time constant doesn't exceed 50 s. + Get_Tu = max(Get_Tu, 0.001_ReKi) ! ensure the time constant doesn't get too small, either. +end function Get_Tu + + + !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine implements the fourth-order Runge-Kutta Method (RK4) for numerically integrating ordinary differential equations: !! @@ -2654,6 +2738,9 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, real(ReKi) :: delta_c_df_primeprime real(ReKi), parameter :: delta_c_mf_primeprime = 0.0_ReKi TYPE(UA_ElementContinuousStateType) :: x_in ! Continuous states at t + ! for BV + real(ReKi) :: Delta_alpha + real(ReKi) :: K1, Gamma type(AFI_OutputType) :: AFI_interp @@ -2716,13 +2803,30 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, fs_aE = AFI_interp%f_st x_in%x = 0.0_R8Ki + elseif (p%UAMod == UA_BV) then - ! - print*,'TODO calcoutput BV' - if (OtherState%FirstPass(i,j)) then - end if + ! --- CalcOutput_BV + K1 = 0.1 ! TODO TODO + Gamma = 2.5 ! TODO TODO + Tu = Get_Tu(u%u, p%c(i,j)) + alpha_34 = Get_Alpha34(u%v_ac, u%omega, 0.5_ReKi*p%c(i,j)) + Delta_alpha = - K1 * Gamma * sqrt( abs(xd%alpha_dot(i,j) * Tu) ) + alphaE = alpha_34 + Delta_alpha + ! Cl, Cd, Cm at effective angle of attack alphaE + call AFI_ComputeAirfoilCoefs(alphaE, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + y%Cl = AFI_interp%Cl + y%Cd = AFI_interp%Cd + if (AFInfo%ColCm == 0) then ! we don't have a cm column, so make everything 0 + y%Cm = 0.0_ReKi + else + y%Cm = AFI_interp%Cm + endif + ! TODO projection alpha or alpha34 + y%Cn = y%Cl*cos(u%alpha) + y%Cd*sin(u%alpha) + y%Cc = y%Cl*sin(u%alpha) - y%Cd*cos(u%alpha) elseif (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV .or. p%UAMod == UA_OYE) then + ! --- CalcOutput State Space models x_in = x%element(i,j) if (OtherState%FirstPass(i,j)) then @@ -2818,6 +2922,7 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) else + ! --- CalcOutput Beddoes-Leishman type models M = u%U / p%a_s @@ -2964,7 +3069,7 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, call UA_BlendSteady(u, p, AFInfo, y, misc%FirstWarn_UA_off, misc%weight(i,j), ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - end if + end if ! Switch on UAMod #ifdef UA_OUTS iOffset = (i-1)*p%NumOuts + (j-1)*p%nNodesPerBlade*p%NumOuts @@ -2999,7 +3104,12 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, end if elseif(p%UAMod == UA_BV) then - print*,'>>> TODO UA_BV write outputs' + y%WriteOutput(iOffset+ 8) = u%omega*R2D + y%WriteOutput(iOffset+ 9) = alphaE*R2D + y%WriteOutput(iOffset+10) = Tu + y%WriteOutput(iOffset+11) = alpha_34*R2D + y%WriteOutput(iOffset+12) = xd%alpha_dot(i,j)*R2D + y%WriteOutput(iOffset+13) = Delta_alpha*R2D else ! Baseline, Gonzales, MinnemaPierce diff --git a/modules/aerodyn/src/UnsteadyAero_Registry.txt b/modules/aerodyn/src/UnsteadyAero_Registry.txt index 4d1d9db45..8aede213c 100644 --- a/modules/aerodyn/src/UnsteadyAero_Registry.txt +++ b/modules/aerodyn/src/UnsteadyAero_Registry.txt @@ -112,6 +112,7 @@ typedef ^ ContinuousStateType UA_ElementC # typedef ^ DiscreteStateType ReKi alpha_minus1 {:}{:} - - "angle of attack, previous time step" rad typedef ^ DiscreteStateType ReKi alpha_filt_minus1 {:}{:} - - "filtered angle of attack, previous time step" rad +typedef ^ DiscreteStateType ReKi alpha_dot {:}{:} - - "Rate of change of angle of attack (filtered)" rad/s typedef ^ DiscreteStateType ReKi q_minus1 {:}{:} - - "non-dimensional pitching rate, previous time step" - typedef ^ DiscreteStateType ReKi Kalpha_f_minus1 {:}{:} - - "filtered pitching rate, previous time step" - typedef ^ DiscreteStateType ReKi Kq_f_minus1 {:}{:} - - "filtered pitching acceleration, previous time step" - diff --git a/modules/aerodyn/src/UnsteadyAero_Types.f90 b/modules/aerodyn/src/UnsteadyAero_Types.f90 index fe5e2f958..79473b252 100644 --- a/modules/aerodyn/src/UnsteadyAero_Types.f90 +++ b/modules/aerodyn/src/UnsteadyAero_Types.f90 @@ -131,6 +131,7 @@ MODULE UnsteadyAero_Types TYPE, PUBLIC :: UA_DiscreteStateType REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: alpha_minus1 !< angle of attack, previous time step [rad] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: alpha_filt_minus1 !< filtered angle of attack, previous time step [rad] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: alpha_dot !< Rate of change of angle of attack (filtered) [rad/s] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: q_minus1 !< non-dimensional pitching rate, previous time step [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Kalpha_f_minus1 !< filtered pitching rate, previous time step [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Kq_f_minus1 !< filtered pitching acceleration, previous time step [-] @@ -1789,6 +1790,20 @@ SUBROUTINE UA_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrSt END IF DstDiscStateData%alpha_filt_minus1 = SrcDiscStateData%alpha_filt_minus1 ENDIF +IF (ALLOCATED(SrcDiscStateData%alpha_dot)) THEN + i1_l = LBOUND(SrcDiscStateData%alpha_dot,1) + i1_u = UBOUND(SrcDiscStateData%alpha_dot,1) + i2_l = LBOUND(SrcDiscStateData%alpha_dot,2) + i2_u = UBOUND(SrcDiscStateData%alpha_dot,2) + IF (.NOT. ALLOCATED(DstDiscStateData%alpha_dot)) THEN + ALLOCATE(DstDiscStateData%alpha_dot(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%alpha_dot.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstDiscStateData%alpha_dot = SrcDiscStateData%alpha_dot +ENDIF IF (ALLOCATED(SrcDiscStateData%q_minus1)) THEN i1_l = LBOUND(SrcDiscStateData%q_minus1,1) i1_u = UBOUND(SrcDiscStateData%q_minus1,1) @@ -2226,6 +2241,9 @@ SUBROUTINE UA_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) IF (ALLOCATED(DiscStateData%alpha_filt_minus1)) THEN DEALLOCATE(DiscStateData%alpha_filt_minus1) ENDIF +IF (ALLOCATED(DiscStateData%alpha_dot)) THEN + DEALLOCATE(DiscStateData%alpha_dot) +ENDIF IF (ALLOCATED(DiscStateData%q_minus1)) THEN DEALLOCATE(DiscStateData%q_minus1) ENDIF @@ -2363,6 +2381,11 @@ SUBROUTINE UA_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Int_BufSz = Int_BufSz + 2*2 ! alpha_filt_minus1 upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%alpha_filt_minus1) ! alpha_filt_minus1 END IF + Int_BufSz = Int_BufSz + 1 ! alpha_dot allocated yes/no + IF ( ALLOCATED(InData%alpha_dot) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! alpha_dot upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%alpha_dot) ! alpha_dot + END IF Int_BufSz = Int_BufSz + 1 ! q_minus1 allocated yes/no IF ( ALLOCATED(InData%q_minus1) ) THEN Int_BufSz = Int_BufSz + 2*2 ! q_minus1 upper/lower bounds for each dimension @@ -2580,6 +2603,26 @@ SUBROUTINE UA_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg END DO END DO END IF + IF ( .NOT. ALLOCATED(InData%alpha_dot) ) 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_dot,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%alpha_dot,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%alpha_dot,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%alpha_dot,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%alpha_dot,2), UBOUND(InData%alpha_dot,2) + DO i1 = LBOUND(InData%alpha_dot,1), UBOUND(InData%alpha_dot,1) + ReKiBuf(Re_Xferred) = InData%alpha_dot(i1,i2) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF IF ( .NOT. ALLOCATED(InData%q_minus1) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -3256,6 +3299,29 @@ SUBROUTINE UA_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err END DO END DO END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! alpha_dot 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_dot)) DEALLOCATE(OutData%alpha_dot) + ALLOCATE(OutData%alpha_dot(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%alpha_dot.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%alpha_dot,2), UBOUND(OutData%alpha_dot,2) + DO i1 = LBOUND(OutData%alpha_dot,1), UBOUND(OutData%alpha_dot,1) + OutData%alpha_dot(i1,i2) = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! q_minus1 not allocated Int_Xferred = Int_Xferred + 1 ELSE From 63ce06e43f708ec9b07b231b8bd7d21e1740d626 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 18 Jun 2021 15:38:19 -0600 Subject: [PATCH 08/24] UA: Boeing-Vertol implementation similar to CACTUS --- modules/aerodyn/src/UnsteadyAero.f90 | 467 +++++++++++++++--- modules/aerodyn/src/UnsteadyAero_Registry.txt | 3 + modules/aerodyn/src/UnsteadyAero_Types.f90 | 198 ++++++++ 3 files changed, 603 insertions(+), 65 deletions(-) diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index 2aad576b1..c7a787a51 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -41,8 +41,9 @@ module UnsteadyAero public :: UA_ReInit public :: UA_InitStates_AllNodes ! used for AD linearization initialization - real(ReKi), parameter :: Gonzalez_factor = 0.2_ReKi ! this factor, proposed by Gonzalez (for "all" models) is used to modify Cc to account for negative values seen at f=0 (see Eqn 1.40) - real(ReKi), parameter, public :: UA_u_min = 0.01_ReKi ! m/s; used to provide a minimum value so UA equations don't blow up (this should be much lower than range where UA is turned off) + real(ReKi), parameter :: Gonzalez_factor = 0.2_ReKi ! this factor, proposed by Gonzalez (for "all" models) is used to modify Cc to account for negative values seen at f=0 (see Eqn 1.40) + real(ReKi), parameter, public :: UA_u_min = 0.01_ReKi ! m/s; used to provide a minimum value so UA equations don't blow up (this should be much lower than range where UA is turned off) + real(ReKi), parameter :: K1pos=1.0_ReKi, K1neg=0.5_ReKi ! K1 coefficients for BV model contains @@ -839,6 +840,9 @@ subroutine UA_InitStates_Misc( p, x, xd, OtherState, m, ErrStat, ErrMsg ) call AllocAry( xd%alpha_minus1 , p%nNodesPerBlade,p%numBlades, 'xd%alpha_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) call AllocAry( xd%alpha_filt_minus1, p%nNodesPerBlade,p%numBlades, 'xd%alpha_filt_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) call AllocAry( xd%alpha_dot , p%nNodesPerBlade,p%numBlades, 'xd%alpha_dot', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call AllocAry( xd%alpha_dot_minus1 , p%nNodesPerBlade,p%numBlades, 'xd%alpha_dot_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + allocate ( OtherState%activeL (p%nNodesPerBlade,p%numBlades), stat=ErrStat2); if(ErrStat2 /= 0) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%activeL.", ErrStat, ErrMsg, RoutineName) + allocate ( OtherState%activeD (p%nNodesPerBlade,p%numBlades), stat=ErrStat2); if(ErrStat2 /= 0) call SetErrStat( ErrID_Fatal, " Error allocating OtherState%activeD.", ErrStat, ErrMsg, RoutineName) else call AllocAry( xd%alpha_minus1, p%nNodesPerBlade,p%numBlades, 'xd%alpha_minus1', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -965,6 +969,9 @@ subroutine UA_ReInit( p, x, xd, OtherState, m, ErrStat, ErrMsg ) xd%alpha_minus1 = 0.0_ReKi xd%alpha_filt_minus1 = 0.0_ReKi xd%alpha_dot = 0.0_ReKi + xd%alpha_dot_minus1 = 0.0_ReKi + OtherState%activeL = .False. + OtherState%activeD = .False. else OtherState%sigma1 = 1.0_ReKi @@ -1093,7 +1100,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & elseif(p%UAMod == UA_HGMV) then p%NumOuts = 21 elseif(p%UAMod == UA_BV) then - p%NumOuts = 13 + p%NumOuts = 24 else p%NumOuts = 45 end if @@ -1172,17 +1179,39 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & elseif(p%UAMod == UA_BV) then InitOut%WriteOutputHdr(iOffset+ 8) = trim(chanPrefix)//'omega' InitOut%WriteOutputHdr(iOffset+ 9) = trim(chanPrefix)//'alphaE' - InitOut%WriteOutputHdr(iOffset+10) = trim(chanPrefix)//'Tu' - InitOut%WriteOutputHdr(iOffset+11) = trim(chanPrefix)//'alpha_34' - InitOut%WriteOutputHdr(iOffset+12) = trim(chanPrefix)//'alpha_dot' - InitOut%WriteOutputHdr(iOffset+13) = trim(chanPrefix)//'delta_alpha' + InitOut%WriteOutputHdr(iOffset+10) = trim(chanPrefix)//'alphaED' + InitOut%WriteOutputHdr(iOffset+11) = trim(chanPrefix)//'Tu' + InitOut%WriteOutputHdr(iOffset+12) = trim(chanPrefix)//'alpha_34' + InitOut%WriteOutputHdr(iOffset+13) = trim(chanPrefix)//'alphaDot' + InitOut%WriteOutputHdr(iOffset+14) = trim(chanPrefix)//'adotnorm' + InitOut%WriteOutputHdr(iOffset+15) = trim(chanPrefix)//'dalphaL' + InitOut%WriteOutputHdr(iOffset+16) = trim(chanPrefix)//'dalphaD' + InitOut%WriteOutputHdr(iOffset+17) = trim(chanPrefix)//'activeL' + InitOut%WriteOutputHdr(iOffset+18) = trim(chanPrefix)//'activeD' + InitOut%WriteOutputHdr(iOffset+19) = trim(chanPrefix)//'alphaLagD' + InitOut%WriteOutputHdr(iOffset+20) = trim(chanPrefix)//'gammaL' + InitOut%WriteOutputHdr(iOffset+21) = trim(chanPrefix)//'gammaD' + InitOut%WriteOutputHdr(iOffset+22) = trim(chanPrefix)//'transA' + InitOut%WriteOutputHdr(iOffset+23) = trim(chanPrefix)//'delP' + InitOut%WriteOutputHdr(iOffset+24) = trim(chanPrefix)//'delN' InitOut%WriteOutputUnt(iOffset+ 8) = '(deg/sec)' InitOut%WriteOutputUnt(iOffset+ 9) = '(deg)' - InitOut%WriteOutputUnt(iOffset+10) = '(s)' - InitOut%WriteOutputUnt(iOffset+11) = '(deg)' - InitOut%WriteOutputUnt(iOffset+12) = '(deg/sec)' - InitOut%WriteOutputUnt(iOffset+13) = '(deg)' + InitOut%WriteOutputUnt(iOffset+10) = '(deg)' + InitOut%WriteOutputUnt(iOffset+11) = '(s)' + InitOut%WriteOutputUnt(iOffset+12) = '(deg)' + InitOut%WriteOutputUnt(iOffset+13) = '(deg/sec)' + InitOut%WriteOutputUnt(iOffset+14) = '(-)' + InitOut%WriteOutputUnt(iOffset+15) = '(deg)' + InitOut%WriteOutputUnt(iOffset+16) = '(deg)' + InitOut%WriteOutputUnt(iOffset+17) = '(-)' + InitOut%WriteOutputUnt(iOffset+18) = '(-)' + InitOut%WriteOutputUnt(iOffset+19) = '(deg)' + InitOut%WriteOutputUnt(iOffset+20) = '(-)' + InitOut%WriteOutputUnt(iOffset+21) = '(-)' + InitOut%WriteOutputUnt(iOffset+22) = '(-)' + InitOut%WriteOutputUnt(iOffset+23) = '(-)' + InitOut%WriteOutputUnt(iOffset+24) = '(-)' else @@ -1277,8 +1306,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & if (p%NumOuts > 0) then CALL GetNewUnit( p%unOutFile, ErrStat, ErrMsg ) IF ( ErrStat /= ErrID_None ) RETURN - - CALL OpenFOutFile ( p%unOutFile, trim(InitInp%OutRootName)//'.out', ErrStat2, ErrMsg2 ) + CALL OpenFOutFile ( p%unOutFile, trim(InitInp%OutRootName)//'.UA.out', ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -1544,17 +1572,23 @@ subroutine UA_UpdateDiscOtherState_BV( i, j, u, p, xd, OtherState, AFInfo, m, Er 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 - real(ReKi), parameter :: filtCutOff = 0.5_ReKi ! CutOff used to filter angle of attack. Alternative, use BL_p (see KelvinChain) - logical, parameter :: filterAlpha =.False. ! Parameter to filter the angle of attack before computing finite differences - real(ReKi) :: alpha_34 ! angle of attack at 3/4 point - real(ReKi) :: alpha_minus1 ! 3/4 chord angle of attack at - real(ReKi) :: alpha_filt_cur ! - real(ReKi) :: alpha_filt_minus1 ! - real(ReKi) :: Tu ! Time constant based on u=Vrel and chord - real(ReKi) :: dynamicFilterCutoffHz ! find frequency based on reduced frequency of k = BL_p%filtCutOff - real(ReKi) :: LowPassConst - integer(IntKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 + real(ReKi), parameter :: filtCutOff = 0.5_ReKi !< CutOff used to filter angle of attack. Alternative, use BL_p (see KelvinChain) + logical, parameter :: filterAlpha =.False. !< Parameter to filter the angle of attack before computing finite differences + real(ReKi) :: alpha_34 !< angle of attack at 3/4 point + real(ReKi) :: alpha_minus1 !< 3/4 chord angle of attack at + real(ReKi) :: alpha_filt_cur !< + real(ReKi) :: alpha_filt_minus1 !< + real(ReKi) :: Tu !< Time constant based on u=Vrel and chord + real(ReKi) :: dynamicFilterCutoffHz !< find frequency based on reduced frequency of k = BL_p%filtCutOff + real(ReKi) :: LowPassConst !< + ! + type(AFI_UA_BL_Type) :: BL_p !< Unsteady airfoil parameters + real(ReKi) :: adotnorm !< alphadot * Tu + real(ReKi) :: alphaLag_D !< lagged angle of attack for drag calculation + real(ReKi) :: alphaE_L !< effective angle of attack for lift and drag + real(ReKi) :: alpha_dot !< rate of change of angle of attack + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 ErrStat = ErrID_None ErrMsg = "" @@ -1562,7 +1596,6 @@ subroutine UA_UpdateDiscOtherState_BV( i, j, u, p, xd, OtherState, AFInfo, m, Er ! --- Filter angle of attack ! Using angle of attack at AC or 3/4 point alpha_34 = Get_Alpha34(u%v_ac, u%omega, 0.5_ReKi*p%c(i,j)) - ! Angle of attack at previous time if (OtherState%FirstPass(i,j)) then alpha_minus1 = alpha_34 @@ -1571,7 +1604,6 @@ subroutine UA_UpdateDiscOtherState_BV( i, j, u, p, xd, OtherState, AFInfo, m, Er alpha_minus1 = xd%alpha_minus1(i,j) alpha_filt_minus1 = xd%alpha_filt_minus1(i,j) end if - if (filterAlpha) then ! Using a simple Infinite Impulse Response Filter (same a K_alpha in UnsteadyAero manual) dynamicFilterCutoffHz = max( 1.0_ReKi, u%U ) * filtCutOff / PI / p%c(i,j) @@ -1582,15 +1614,246 @@ subroutine UA_UpdateDiscOtherState_BV( i, j, u, p, xd, OtherState, AFInfo, m, Er endif ! --- Update states to t+dt + alpha_dot = ( alpha_filt_cur - alpha_filt_minus1 ) / p%dt + if (abs(alpha_dot*p%dt)>PI*0.8) then + ! Sudden jump of alpha happens for vertical axes turbines, e.g. for lambda<=1, jumps from -90 to +90 or -180 to +180 + ! In that case we keep the alpha_dot from previous time step and we don't filter + alpha_dot = xd%alpha_dot(i,j) ! using previous alpha_dot + alpha_filt_cur = alpha_34 ! #noFilter + endif xd%alpha_minus1(i,j) = alpha_34 xd%alpha_filt_minus1(i,j) = alpha_filt_cur - xd%alpha_dot = ( alpha_filt_cur - alpha_filt_minus1 ) / p%dt + xd%alpha_dot_minus1(i,j) = xd%alpha_dot(i,j) + xd%alpha_dot = alpha_dot + + ! --- Compute Unsteady aero params for this airfoil (alpha0, alpha1, alpha2) + call AFI_ComputeUACoefs( AFInfo, u%Re, u%UserProp, BL_p, ErrMsg2, ErrStat2); if(Failed()) return + + ! --- Compute effective angle of attack and lagged angle of attack (needed to update active states) + call BV_getAlphas(i, j, u, p, xd, BL_p, alpha_34, alphaE_L, alphaLag_D, adotnorm) + + ! --- Update dynamic stall activation states + call BV_UpdateActiveStates(adotnorm, alpha_34, alphaLag_D, alphaE_L, BL_p, OtherState%activeL(i,j), OtherState%activeD(i,j)) + contains logical function Failed() call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'UA_UpdateDiscOtherState_BV') Failed = ErrStat>=ErrID_Fatal end function Failed end subroutine UA_UpdateDiscOtherState_BV + +!============================================================================== +!> Calculate angle of attacks using Boeing-Vertol model +!! Drag effective angle of attack needs extra computation +subroutine BV_getAlphas(i, j, u, p, xd, BL_p, alpha_34, alphaE_L, alphaLag_D, adotnorm) + integer, intent(in ) :: i !< node index within a blade + integer, intent(in ) :: j !< blade index + type(UA_InputType), intent(in ) :: u !< Inputs at t + type(UA_ParameterType), intent(in ) :: p !< Parameters + type(UA_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(AFI_UA_BL_Type), intent(in ) :: BL_p !< + real(ReKi), intent(out ) :: alpha_34 !< alpha at 3/4 chord point + real(ReKi), intent(out ) :: alphaE_L !< effective angle of attack for lift + real(ReKi), intent(out ) :: alphaLag_D !< Lagged angle of attack for drag + real(ReKi), intent(out ) :: adotnorm !< alphadot * Tu + real(ReKi) :: gammaL, gammaD !< gamma coefficients for lift and drag respectively + real(ReKi) :: dalphaMax !< Maximum change of angle of attack + real(ReKi) :: tc !< Thickness ratio of airfoil, TODO TODO TODO + real(ReKi) :: dalphaL, dalphaD + real(ReKi) :: isgn !< sign of alphadot Norm + real(ReKi), parameter :: umach = 0.0_ReKi !< Mach number umach=Urel*Minf, Minf (freestrem Mach) for incompressible + + ! Angle of attack at 3/4 chord point + alpha_34 = Get_Alpha34(u%v_ac, u%omega, 0.5_ReKi*p%c(i,j)) + + ! --- Intermediate variables, using CACTUS notations + adotnorm = xd%alpha_dot(i,j) * Get_Tu(u%u, p%c(i,j)) + + ! --- Limits + ! Limit reference dalpha to a maximum to keep sign of CL the same for + ! alpha and lagged alpha (considered a reasonable lag...). Note: + ! magnitude increasing and decreasing effect ratios are maintained. + dalphaMax = 2._ReKi * BV_TransA(BL_p) ! TODO TODO + + ! --- Calculate gamma for lift and drag based rel thickness + tc = 0.18_ReKi ! TODO TODO + call BV_getGammas(tc, umach, gammaL, gammaD) + + ! --- Delta alpha + !dalpha = - K1 * Gamma * sqrt( abs(xd%alpha_dot(i,j) * Tu) ) ! Formula from paper + dalphaL = gammaL * sqrt(abs(adotnorm)) + dalphaD = gammaD * sqrt(abs(adotnorm)) + !print*,'dalpha ', dalphaL,dalphaD + ! Plateau + dalphaL = min(dalphaL, dalphaMax) + dalphaD = min(dalphaD, dalphaMax) + !print*,'dalpha ', dalphaL,dalphaD + if ((adotnorm*(alpha_34-BL_p%alpha0)) < 0.0_ReKi) then + dalphaL = dalphaL*K1neg + dalphaD = dalphaD*K1neg + else + dalphaL = dalphaL*K1pos + dalphaD = dalphaD*K1pos + endif + !print*,'dalpha ', dalphaL,dalphaD + + ! --- Alpha dynamic + isgn = sign(1.0,adotnorm) + alphaE_L = alpha_34 - dalphaL*isgn + alphaLag_D = alpha_34 - dalphaD*isgn ! NOTE: not effective alpha yet for drag +end subroutine BV_getAlphas +!============================================================================== +!> Calculate gamma for lift and drag based rel thickness. See CACTUS BV_DynStall.f95 +subroutine BV_getGammas(tc, umach, gammaL, gammaD) + real(ReKi), intent(in) :: tc !< Relative thickness of airfoil + real(ReKi), intent(in) :: umach !< Mach number of Urel, = Urel*MinfMinf (freestrem Mach), 0 for incompressible + real(ReKi), intent(out) :: gammaL !< gamma coefficient + real(ReKi), intent(out) :: gammaD !< gamma coefficient + real(ReKi) :: smachL, hmachL , smachD, hmachD, diff + real(ReKi) :: gammaxL, gammaxD, dgammaL, dgammaD ! intermediate variables for gamma + diff = 0.06-tc ! tc: thickness to chord ratio + smachL = 0.4+5.0*diff + hmachL = 0.9+2.5*diff + smachD = 0.2 + hmachD = 0.7+2.5*diff + gammaxL = 1.4-6.0*diff + gammaxD = 1.0-2.5*diff + dgammaL = gammaxL/(hmachL-smachL) + dgammaD = gammaxD/(hmachD-smachD) + gammaL = gammaxL-(umach-smachL)*dgammaL ! For lift + gammaD = gammaxD-(umach-smachD)*dgammaD ! For drag + if (umach < smachD) then + gammaD=gammaxD + end if +end subroutine BV_getGammas +!============================================================================== +!> Compute Transition region length +!! Note from CACTUS: +!! Limit reference dalpha to a maximum to keep sign of CL the same for +!! alpha and lagged alpha (considered a reasonable lag...) +!! NOTE: magnitude increasing and decreasing effect ratios are maintained. +real(ReKi) function BV_TransA(BL_p) + type(AFI_UA_BL_Type), intent(in) :: BL_p + real(ReKi), parameter :: Fac= .9_ReKi !< Margin to ensure that dalphaRef is never large enough to make alrefL = = AOA0 (blows up linear expansion model) + real(ReKi) :: AOA0 !< angle of attack of zero lift + real(ReKi) :: alssP !< Static Stall angle positive + real(ReKi) :: alssN !< Static Stall angle negative + real(ReKi) :: dalphaMax !< Maximum change of angle of attack + AOA0 = BL_p%alpha0 + alssP = BL_p%alpha1 + alssN = BL_p%alpha2 + dalphaMax = Fac*min(abs(alssP-AOA0),abs(alssN-AOA0))/max(K1pos,K1neg) + BV_TransA = .5_ReKi*dalphaMax ! transition region for fairing lagged AOA in pure lag model +end function BV_TransA +!============================================================================== +!> Calculate deltas to negative and postivive stall angle +subroutine BV_delNP(adotnorm, alpha, alphaLag_D, BL_p, activeD, delN, delP) + real(ReKi), intent(in) :: adotnorm !< alphadot * Tu + real(ReKi), intent(in) :: alpha !< alpha (3/4) + real(ReKi), intent(in) :: alphaLag_D !< lagged alpha for drag + type(AFI_UA_BL_Type), intent(in) :: BL_p !< + logical, intent(in) :: activeD !< flag to activate drag + real(ReKi), intent(out) :: delN !< Difference between lagged alpha and negative stall + real(ReKi), intent(out) :: delP !< Difference between lagged alpha and positive stall + real(ReKi) :: AOA0 !< angle of attack of zero lift + real(ReKi) :: alssP !< Static Stall angle positive + real(ReKi) :: alssN !< Static Stall angle negative + ! Cactus notations + AOA0 = BL_p%alpha0 + alssP = BL_p%alpha1 + alssN = BL_p%alpha2 + if ((adotnorm*(alpha-AOA0)) < 0.0) then + ! Only switch DS off using lagged alpha + if (activeD) then + delN = alssN - alphaLag_D + delP = alphaLag_D - alssP + else + delN = 0.0_ReKi + delP = 0.0_ReKi + end if + else + ! switch DS on or off using alpha + delN = alssN - alpha + delP = alpha - alssP + end if +end subroutine BV_delNP +!============================================================================== +!> Calculate effective angle of attack for drag coefficient, based on lagged angle of attack +real(ReKi) function BV_alphaE_D(adotnorm, alpha, alphaLag_D, BL_p, activeD) + real(ReKi), intent(in) :: adotnorm !< alphadot * Tu + real(ReKi), intent(in) :: alpha !< alpha (3/4) + real(ReKi), intent(in) :: alphaLag_D !< lagged alpha for drag + type(AFI_UA_BL_Type), intent(in) :: BL_p !< + logical, intent(in) :: activeD !< flag to activate drag + real(ReKi) :: TransA !< Transition region for fairing lagged AOA in pure lag model + real(ReKi) :: delN !< Difference between lagged alpha and negative stall + real(ReKi) :: delP !< Difference between lagged alpha and positive stall + + ! Calculate deltas to negative and postivive stall angle (delN, and delP) + call BV_delNP(adotnorm, alpha, alphaLag_D, BL_p, activeD, delN, delP) + + ! --- Alpha dyn for drag and flag + TransA = BV_TransA(BL_p) + if (delN > TransA .OR. delP > TransA) then + BV_alphaE_D = alphaLag_D + elseif (delN > 0 .AND. delN < TransA) then + ! Transition region (fairing effect...) + BV_alphaE_D = alpha+(alphaLag_D-alpha)*delN/TransA + elseif (delP > 0 .AND. delP < TransA) then + ! Transition region (fairing effect...) + BV_alphaE_D = alpha+(alphaLag_D-alpha)*delP/TransA + else + BV_alphaE_D = alpha ! 3/4 + end if +end function BV_alphaE_D +!============================================================================== +!> Activate dynamic stall for lift or drag +subroutine BV_UpdateActiveStates(adotnorm, alpha, alphaLag_D, alphaE_L, BL_p, activeL, activeD) + real(ReKi), intent(in) :: adotnorm !< alphadot * Tu + real(ReKi), intent(in) :: alpha !< alpha (3/4) + real(ReKi), intent(in) :: alphaLag_D !< lagged alpha for drag + real(ReKi), intent(in) :: alphaE_L !< Effective angle of attack for lifr + type(AFI_UA_BL_Type), intent(in) :: BL_p !< + logical, intent(inout) :: activeL !< flag to activate lift + logical, intent(inout) :: activeD !< flag to activate drag + real(ReKi) :: TransA !< Transition region for fairing lagged AOA in pure lag model + real(ReKi) :: delN !< Difference between lagged alpha and negative stall + real(ReKi) :: delP !< Difference between lagged alpha and positive stall + real(ReKi) :: AOA0 !< angle of attack of zero lift + real(ReKi) :: alssP !< Static Stall angle positive + real(ReKi) :: alssN !< Static Stall angle negative + ! Cactus notations + AOA0 = BL_p%alpha0 + alssP = BL_p%alpha1 + alssN = BL_p%alpha2 + + ! --- Activate lift dynamic stall + if ((adotnorm*(alpha-AOA0)) < 0.0) then + ! Only switch DS off using lagged alpha + if (activeL .and. (alphaE_L > alssN .AND. alphaE_L < alssP)) then + activeL=.false. + end if + else + ! switch DS on or off using alpha + activeL = (alpha <= alssN .OR. alpha >= alssP) + end if + + ! --- Activate drag dynamic stall + ! Calculate deltas to negative and postivive stall angle (delN, and delP) + call BV_delNP(adotnorm, alpha, alphaLag_D, BL_p, activeD, delN, delP) + TransA = BV_TransA(BL_p) + if (delN > TransA .OR. delP > TransA) then + activeD = .true. + elseif (delN > 0 .AND. delN < TransA) then + ! Transition region (fairing effect...) + activeD = .true. + elseif (delP > 0 .AND. delP < TransA) then + ! Transition region (fairing effect...) + activeD = .true. + else + activeD = .false. + end if +end subroutine BV_UpdateActiveStates !============================================================================== !> Routine for updating discrete states and other states for Beddoes-Leishman types models (note it breaks the framework) subroutine UA_UpdateDiscOtherState( i, j, u, p, xd, OtherState, AFInfo, m, ErrStat, ErrMsg ) @@ -2356,18 +2619,12 @@ SUBROUTINE Get_HGM_constants(i, j, p, u, x, BL_p, Tu, alpha_34, alphaE) real(ReKi) :: vx_34 - ! Variables derived from inputs - !u%u = U_ac = TwoNorm(u%v_ac) ! page 4 definitions - - Tu = p%c(i,j) / (2.0_ReKi* max(u%u, UA_u_min)) ! Eq. 23 - Tu = min(Tu, 50.0_ReKi) ! ensure the time constant doesn't exceed 50 s. - Tu = max(Tu, 0.001_ReKi) ! ensure the time constant doesn't get too small, either. - ! Tu = Get_Tu(u%u, p%c(i,j)) + ! Variables derived from inputs + !u%u = U_ac = TwoNorm(u%v_ac) ! page 4 definitions + Tu = Get_Tu(u%u, p%c(i,j)) if (present(alpha_34)) then - vx_34 = u%v_ac(1) - u%omega * 0.5_ReKi*p%c(i,j) ! Eq. 1 - alpha_34 = atan2(vx_34, u%v_ac(2) ) ! page 5 definitions - !alpha_34 = Get_Alpha34(u%v_ac, u%omega, 0.5_ReKi*p%c(i,j)) + alpha_34 = Get_Alpha34(u%v_ac, u%omega, 0.5_ReKi*p%c(i,j)) if (present(alphaE)) then ! Variables derived from states @@ -2387,9 +2644,17 @@ real(ReKi) function Get_Alpha34(v_ac, omega, d_ac_to_34) real(ReKi), intent(in) :: v_ac(2) !< Velocity at aerodynamic center real(ReKi), intent(in) :: omega !< pitching rate of airfoil real(ReKi), intent(in) :: d_ac_to_34 !< distance from aerodynamic center to 3/4 chord point - Get_Alpha34 = atan2(v_ac(1) - omega * d_ac_to_34, v_ac(2) ) + Get_Alpha34 = atan2(v_ac(1) + omega * d_ac_to_34, v_ac(2) ) ! Uaero - Uelast end function Get_Alpha34 +!> Compute angle of attack at 2/4 chord point based on values at Aerodynamic center +real(ReKi) function Get_Alpha24(v_ac, omega, d_ac_to_24) + real(ReKi), intent(in) :: v_ac(2) !< Velocity at aerodynamic center + real(ReKi), intent(in) :: omega !< pitching rate of airfoil + real(ReKi), intent(in) :: d_ac_to_24 !< distance from aerodynamic center to 2/4 chord point + Get_Alpha24 = atan2(v_ac(1) + omega * d_ac_to_24, v_ac(2) ) ! Uaero - Uelast +end function Get_Alpha24 + !> Compute time constant based on relative velocity u_rel real(ReKi) function Get_Tu(u_rel, chord) real(ReKi), intent(in) :: u_rel !< relative velocity of airfoil @@ -2739,9 +3004,16 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, real(ReKi), parameter :: delta_c_mf_primeprime = 0.0_ReKi TYPE(UA_ElementContinuousStateType) :: x_in ! Continuous states at t ! for BV - real(ReKi) :: Delta_alpha - real(ReKi) :: K1, Gamma - + real(ReKi) :: alphaE_L, alphaE_D ! effective angle of attack for lift and drag + real(ReKi) :: alphaLag_D ! lagged angle of attack for drag calculation + real(ReKi) :: adotnorm +#ifdef UA_OUTS + real(ReKi) :: delN + real(ReKi) :: delP + real(ReKi) :: gammaL + real(ReKi) :: gammaD + real(ReKi) :: TransA +#endif type(AFI_OutputType) :: AFI_interp @@ -2805,25 +3077,9 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, x_in%x = 0.0_R8Ki elseif (p%UAMod == UA_BV) then - ! --- CalcOutput_BV - K1 = 0.1 ! TODO TODO - Gamma = 2.5 ! TODO TODO - Tu = Get_Tu(u%u, p%c(i,j)) - alpha_34 = Get_Alpha34(u%v_ac, u%omega, 0.5_ReKi*p%c(i,j)) - Delta_alpha = - K1 * Gamma * sqrt( abs(xd%alpha_dot(i,j) * Tu) ) - alphaE = alpha_34 + Delta_alpha - ! Cl, Cd, Cm at effective angle of attack alphaE - call AFI_ComputeAirfoilCoefs(alphaE, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - y%Cl = AFI_interp%Cl - y%Cd = AFI_interp%Cd - if (AFInfo%ColCm == 0) then ! we don't have a cm column, so make everything 0 - y%Cm = 0.0_ReKi - else - y%Cm = AFI_interp%Cm - endif - ! TODO projection alpha or alpha34 - y%Cn = y%Cl*cos(u%alpha) + y%Cd*sin(u%alpha) - y%Cc = y%Cl*sin(u%alpha) - y%Cd*cos(u%alpha) + ! --- CalcOutput Boeing-Vertol + call BV_CalcOutput() + if (ErrStat >= AbortErrLev) return elseif (p%UAMod == UA_HGM .or. p%UAMod == UA_HGMV .or. p%UAMod == UA_OYE) then ! --- CalcOutput State Space models @@ -3105,11 +3361,22 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, elseif(p%UAMod == UA_BV) then y%WriteOutput(iOffset+ 8) = u%omega*R2D - y%WriteOutput(iOffset+ 9) = alphaE*R2D - y%WriteOutput(iOffset+10) = Tu - y%WriteOutput(iOffset+11) = alpha_34*R2D - y%WriteOutput(iOffset+12) = xd%alpha_dot(i,j)*R2D - y%WriteOutput(iOffset+13) = Delta_alpha*R2D + y%WriteOutput(iOffset+ 9) = alphaE_L*R2D + y%WriteOutput(iOffset+10) = alphaE_D*R2D + y%WriteOutput(iOffset+11) = Get_Tu(u%u, p%c(i,j)) + y%WriteOutput(iOffset+12) = alpha_34*R2D + y%WriteOutput(iOffset+13) = xd%alpha_dot(i,j)*R2D*p%dt + y%WriteOutput(iOffset+14) = adotnorm + y%WriteOutput(iOffset+15) = (alpha_34-alphaE_L)*R2D + y%WriteOutput(iOffset+16) = (alpha_34-alphaE_D)*R2D + y%WriteOutput(iOffset+17) = transfer(OtherState%activeL(i,j), ReKi) ! logical to float + y%WriteOutput(iOffset+18) = transfer(OtherState%activeD(i,j), ReKi) + y%WriteOutput(iOffset+19) = alphaLag_D*R2D + y%WriteOutput(iOffset+20) = gammaL + y%WriteOutput(iOffset+21) = gammaD + y%WriteOutput(iOffset+22) = TransA + y%WriteOutput(iOffset+23) = delP + y%WriteOutput(iOffset+24) = delN else ! Baseline, Gonzales, MinnemaPierce @@ -3169,8 +3436,78 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, end if end if #endif + +contains + !> Calc Outputs for Boieng-Vertol dynamic stall + !! See BV_DynStall.f95 of CACTUS, notations kept more or less consistent + subroutine BV_CalcOutput() + real(ReKi) :: alpha_50 + real(ReKi) :: Cm25_stat + real(ReKi) :: Cl75_stat + real(ReKi) :: Cl50_stat + + ! --- Compute Unsteady aero params (BL_p) for this airfoil (alpha0, alpha1, alpha2) + call AFI_ComputeUACoefs( AFInfo, u%Re, u%UserProp, BL_p, ErrMsg2, ErrStat2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + + ! --- Compute effective angle of attack and lagged angle of attack (needed to update active states) + call BV_getAlphas(i, j, u, p, xd, BL_p, alpha_34, alphaE_L, alphaLag_D, adotnorm) + alphaE_D = BV_alphaE_D(adotnorm, alpha_34, alphaLag_D, BL_p, OtherState%activeD(i,j)) + +#ifdef UA_OUTS + ! --- Recompute variables, for temporary output to file only + ! Calculate deltas to negative and postivive stall angle (delN, and delP) + call BV_delNP(adotnorm, alpha_34, alphaLag_D, BL_p, OtherState%activeD(i,j), delN, delP) + call BV_getGammas(tc=0.18, umach=0.0_ReKi, gammaL=gammaL, gammaD=gammaD) + TransA = BV_TransA(BL_p) +#endif + + + + ! --- Cl, _, at effective angle of attack alphaE + if (OtherState%activeL(i,j)) then + ! Dynamic Cl (scaled) + call AFI_ComputeAirfoilCoefs(alphaE_L, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + y%Cl = AFI_interp%Cl/(alphaE_L-BL_P%alpha0) * (alpha_34-BL_p%alpha0) + else + ! Static Cl + call AFI_ComputeAirfoilCoefs(alpha_34, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + y%Cl = AFI_interp%Cl + endif + + ! --- Cd at effective angle of attack alphaE (alphaE might be alpha34 if no drag model) + call AFI_ComputeAirfoilCoefs(alphaE_D, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + y%Cd = AFI_interp%Cd + + ! --- Cm using pitch rate effects by analogy to pitching flat plate potential flow theory (SAND report) + ! As Done in CACTUS + ! Static coeffs at 1/4 chord + if (AFInfo%ColCm == 0) then ! we don't have a cm column, so make everything 0 + Cm25_stat = 0.0_ReKi + else + ! Static coeffs at 1/4 chord (u%Alpha) + call AFI_ComputeAirfoilCoefs(u%Alpha, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Cm25_stat = AFI_interp%Cm + endif + ! Static coeffs at 1/2 chord (alpha_50) + alpha_50 = Get_Alpha24(u%v_ac, u%omega, 0.25_ReKi*p%c(i,j)) + call AFI_ComputeAirfoilCoefs(alpha_50, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Cl50_stat = AFI_interp%Cl + ! Static coeffs at 3/4 chord (alpha_34) + call AFI_ComputeAirfoilCoefs(alpha_34, u%Re, u%UserProp, AFInfo, AFI_interp, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Cl75_stat = AFI_interp%Cl + y%Cm = Cm25_stat + cos(alpha_50) * (Cl75_stat - Cl50_stat)*0.25_ReKi + + ! TODO projection using alpha 5 and back for added mass + y%Cn = y%Cl*cos(u%alpha) + y%Cd*sin(u%alpha) + y%Cc = y%Cl*sin(u%alpha) - y%Cd*cos(u%alpha) + + end subroutine BV_CalcOutput end subroutine UA_CalcOutput + + + !============================================================================== subroutine UA_WriteOutputToFile(t, p, y) real(DbKi), intent(in ) :: t ! current time (s) diff --git a/modules/aerodyn/src/UnsteadyAero_Registry.txt b/modules/aerodyn/src/UnsteadyAero_Registry.txt index 8aede213c..613b543a8 100644 --- a/modules/aerodyn/src/UnsteadyAero_Registry.txt +++ b/modules/aerodyn/src/UnsteadyAero_Registry.txt @@ -113,6 +113,7 @@ typedef ^ ContinuousStateType UA_ElementC typedef ^ DiscreteStateType ReKi alpha_minus1 {:}{:} - - "angle of attack, previous time step" rad typedef ^ DiscreteStateType ReKi alpha_filt_minus1 {:}{:} - - "filtered angle of attack, previous time step" rad typedef ^ DiscreteStateType ReKi alpha_dot {:}{:} - - "Rate of change of angle of attack (filtered)" rad/s +typedef ^ DiscreteStateType ReKi alpha_dot_minus1 {:}{:} - - "Rate of change of angle of attack (filtered)" rad/s typedef ^ DiscreteStateType ReKi q_minus1 {:}{:} - - "non-dimensional pitching rate, previous time step" - typedef ^ DiscreteStateType ReKi Kalpha_f_minus1 {:}{:} - - "filtered pitching rate, previous time step" - typedef ^ DiscreteStateType ReKi Kq_f_minus1 {:}{:} - - "filtered pitching acceleration, previous time step" - @@ -163,6 +164,8 @@ typedef ^ OtherStateType ReKi typedef ^ OtherStateType LOGICAL PositivePressure {:}{:} - - "HGMV model: logical flag indicating if the vortex lift became active because of positive pressure (or negative)" - typedef ^ OtherStateType LOGICAL vortexOn {:}{:} - - "HGMV model: logical flag indicating if the vortex lift term is active" - typedef ^ OtherStateType LOGICAL BelowThreshold {:}{:} - - "HGMV model: logical flag indicating if cn fell below threshold to form another vortex" - +typedef ^ OtherStateType LOGICAL activeL {:}{:} - - "BV model: logical flag indicating if the lift stall is active" - +typedef ^ OtherStateType LOGICAL activeD {:}{:} - - "BV model: logical flag indicating if the drag stall is active" - # ..... Misc/Optimization variables................................................................................................. # Define any data that are used only for efficiency purposes (these variables are not associated with time): diff --git a/modules/aerodyn/src/UnsteadyAero_Types.f90 b/modules/aerodyn/src/UnsteadyAero_Types.f90 index 79473b252..d970c878b 100644 --- a/modules/aerodyn/src/UnsteadyAero_Types.f90 +++ b/modules/aerodyn/src/UnsteadyAero_Types.f90 @@ -132,6 +132,7 @@ MODULE UnsteadyAero_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: alpha_minus1 !< angle of attack, previous time step [rad] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: alpha_filt_minus1 !< filtered angle of attack, previous time step [rad] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: alpha_dot !< Rate of change of angle of attack (filtered) [rad/s] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: alpha_dot_minus1 !< Rate of change of angle of attack (filtered) [rad/s] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: q_minus1 !< non-dimensional pitching rate, previous time step [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Kalpha_f_minus1 !< filtered pitching rate, previous time step [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Kq_f_minus1 !< filtered pitching acceleration, previous time step [-] @@ -183,6 +184,8 @@ MODULE UnsteadyAero_Types LOGICAL , DIMENSION(:,:), ALLOCATABLE :: PositivePressure !< HGMV model: logical flag indicating if the vortex lift became active because of positive pressure (or negative) [-] LOGICAL , DIMENSION(:,:), ALLOCATABLE :: vortexOn !< HGMV model: logical flag indicating if the vortex lift term is active [-] LOGICAL , DIMENSION(:,:), ALLOCATABLE :: BelowThreshold !< HGMV model: logical flag indicating if cn fell below threshold to form another vortex [-] + LOGICAL , DIMENSION(:,:), ALLOCATABLE :: activeL !< BV model: logical flag indicating if the lift stall is active [-] + LOGICAL , DIMENSION(:,:), ALLOCATABLE :: activeD !< BV model: logical flag indicating if the drag stall is active [-] END TYPE UA_OtherStateType ! ======================= ! ========= UA_MiscVarType ======= @@ -1804,6 +1807,20 @@ SUBROUTINE UA_CopyDiscState( SrcDiscStateData, DstDiscStateData, CtrlCode, ErrSt END IF DstDiscStateData%alpha_dot = SrcDiscStateData%alpha_dot ENDIF +IF (ALLOCATED(SrcDiscStateData%alpha_dot_minus1)) THEN + i1_l = LBOUND(SrcDiscStateData%alpha_dot_minus1,1) + i1_u = UBOUND(SrcDiscStateData%alpha_dot_minus1,1) + i2_l = LBOUND(SrcDiscStateData%alpha_dot_minus1,2) + i2_u = UBOUND(SrcDiscStateData%alpha_dot_minus1,2) + IF (.NOT. ALLOCATED(DstDiscStateData%alpha_dot_minus1)) THEN + ALLOCATE(DstDiscStateData%alpha_dot_minus1(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%alpha_dot_minus1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstDiscStateData%alpha_dot_minus1 = SrcDiscStateData%alpha_dot_minus1 +ENDIF IF (ALLOCATED(SrcDiscStateData%q_minus1)) THEN i1_l = LBOUND(SrcDiscStateData%q_minus1,1) i1_u = UBOUND(SrcDiscStateData%q_minus1,1) @@ -2244,6 +2261,9 @@ SUBROUTINE UA_DestroyDiscState( DiscStateData, ErrStat, ErrMsg ) IF (ALLOCATED(DiscStateData%alpha_dot)) THEN DEALLOCATE(DiscStateData%alpha_dot) ENDIF +IF (ALLOCATED(DiscStateData%alpha_dot_minus1)) THEN + DEALLOCATE(DiscStateData%alpha_dot_minus1) +ENDIF IF (ALLOCATED(DiscStateData%q_minus1)) THEN DEALLOCATE(DiscStateData%q_minus1) ENDIF @@ -2386,6 +2406,11 @@ SUBROUTINE UA_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg Int_BufSz = Int_BufSz + 2*2 ! alpha_dot upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%alpha_dot) ! alpha_dot END IF + Int_BufSz = Int_BufSz + 1 ! alpha_dot_minus1 allocated yes/no + IF ( ALLOCATED(InData%alpha_dot_minus1) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! alpha_dot_minus1 upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%alpha_dot_minus1) ! alpha_dot_minus1 + END IF Int_BufSz = Int_BufSz + 1 ! q_minus1 allocated yes/no IF ( ALLOCATED(InData%q_minus1) ) THEN Int_BufSz = Int_BufSz + 2*2 ! q_minus1 upper/lower bounds for each dimension @@ -2623,6 +2648,26 @@ SUBROUTINE UA_PackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg END DO END DO END IF + IF ( .NOT. ALLOCATED(InData%alpha_dot_minus1) ) 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_dot_minus1,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%alpha_dot_minus1,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%alpha_dot_minus1,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%alpha_dot_minus1,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%alpha_dot_minus1,2), UBOUND(InData%alpha_dot_minus1,2) + DO i1 = LBOUND(InData%alpha_dot_minus1,1), UBOUND(InData%alpha_dot_minus1,1) + ReKiBuf(Re_Xferred) = InData%alpha_dot_minus1(i1,i2) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF IF ( .NOT. ALLOCATED(InData%q_minus1) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -3322,6 +3367,29 @@ SUBROUTINE UA_UnPackDiscState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Err END DO END DO END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! alpha_dot_minus1 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_dot_minus1)) DEALLOCATE(OutData%alpha_dot_minus1) + ALLOCATE(OutData%alpha_dot_minus1(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%alpha_dot_minus1.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%alpha_dot_minus1,2), UBOUND(OutData%alpha_dot_minus1,2) + DO i1 = LBOUND(OutData%alpha_dot_minus1,1), UBOUND(OutData%alpha_dot_minus1,1) + OutData%alpha_dot_minus1(i1,i2) = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! q_minus1 not allocated Int_Xferred = Int_Xferred + 1 ELSE @@ -4313,6 +4381,34 @@ SUBROUTINE UA_CopyOtherState( SrcOtherStateData, DstOtherStateData, CtrlCode, Er END IF END IF DstOtherStateData%BelowThreshold = SrcOtherStateData%BelowThreshold +ENDIF +IF (ALLOCATED(SrcOtherStateData%activeL)) THEN + i1_l = LBOUND(SrcOtherStateData%activeL,1) + i1_u = UBOUND(SrcOtherStateData%activeL,1) + i2_l = LBOUND(SrcOtherStateData%activeL,2) + i2_u = UBOUND(SrcOtherStateData%activeL,2) + IF (.NOT. ALLOCATED(DstOtherStateData%activeL)) THEN + ALLOCATE(DstOtherStateData%activeL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%activeL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%activeL = SrcOtherStateData%activeL +ENDIF +IF (ALLOCATED(SrcOtherStateData%activeD)) THEN + i1_l = LBOUND(SrcOtherStateData%activeD,1) + i1_u = UBOUND(SrcOtherStateData%activeD,1) + i2_l = LBOUND(SrcOtherStateData%activeD,2) + i2_u = UBOUND(SrcOtherStateData%activeD,2) + IF (.NOT. ALLOCATED(DstOtherStateData%activeD)) THEN + ALLOCATE(DstOtherStateData%activeD(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstOtherStateData%activeD.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstOtherStateData%activeD = SrcOtherStateData%activeD ENDIF END SUBROUTINE UA_CopyOtherState @@ -4360,6 +4456,12 @@ SUBROUTINE UA_DestroyOtherState( OtherStateData, ErrStat, ErrMsg ) ENDIF IF (ALLOCATED(OtherStateData%BelowThreshold)) THEN DEALLOCATE(OtherStateData%BelowThreshold) +ENDIF +IF (ALLOCATED(OtherStateData%activeL)) THEN + DEALLOCATE(OtherStateData%activeL) +ENDIF +IF (ALLOCATED(OtherStateData%activeD)) THEN + DEALLOCATE(OtherStateData%activeD) ENDIF END SUBROUTINE UA_DestroyOtherState @@ -4473,6 +4575,16 @@ SUBROUTINE UA_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs Int_BufSz = Int_BufSz + 2*2 ! BelowThreshold upper/lower bounds for each dimension Int_BufSz = Int_BufSz + SIZE(InData%BelowThreshold) ! BelowThreshold END IF + Int_BufSz = Int_BufSz + 1 ! activeL allocated yes/no + IF ( ALLOCATED(InData%activeL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! activeL upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%activeL) ! activeL + END IF + Int_BufSz = Int_BufSz + 1 ! activeD allocated yes/no + IF ( ALLOCATED(InData%activeD) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! activeD upper/lower bounds for each dimension + Int_BufSz = Int_BufSz + SIZE(InData%activeD) ! activeD + END IF IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -4750,6 +4862,46 @@ SUBROUTINE UA_PackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMs END DO END DO END IF + IF ( .NOT. ALLOCATED(InData%activeL) ) 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%activeL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%activeL,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%activeL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%activeL,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%activeL,2), UBOUND(InData%activeL,2) + DO i1 = LBOUND(InData%activeL,1), UBOUND(InData%activeL,1) + IntKiBuf(Int_Xferred) = TRANSFER(InData%activeL(i1,i2), IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF + IF ( .NOT. ALLOCATED(InData%activeD) ) 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%activeD,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%activeD,1) + Int_Xferred = Int_Xferred + 2 + IntKiBuf( Int_Xferred ) = LBOUND(InData%activeD,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%activeD,2) + Int_Xferred = Int_Xferred + 2 + + DO i2 = LBOUND(InData%activeD,2), UBOUND(InData%activeD,2) + DO i1 = LBOUND(InData%activeD,1), UBOUND(InData%activeD,1) + IntKiBuf(Int_Xferred) = TRANSFER(InData%activeD(i1,i2), IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF END SUBROUTINE UA_PackOtherState SUBROUTINE UA_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -5077,6 +5229,52 @@ SUBROUTINE UA_UnPackOtherState( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, Er END DO END DO END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! activeL 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%activeL)) DEALLOCATE(OutData%activeL) + ALLOCATE(OutData%activeL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%activeL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%activeL,2), UBOUND(OutData%activeL,2) + DO i1 = LBOUND(OutData%activeL,1), UBOUND(OutData%activeL,1) + OutData%activeL(i1,i2) = TRANSFER(IntKiBuf(Int_Xferred), OutData%activeL(i1,i2)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! activeD 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%activeD)) DEALLOCATE(OutData%activeD) + ALLOCATE(OutData%activeD(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%activeD.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%activeD,2), UBOUND(OutData%activeD,2) + DO i1 = LBOUND(OutData%activeD,1), UBOUND(OutData%activeD,1) + OutData%activeD(i1,i2) = TRANSFER(IntKiBuf(Int_Xferred), OutData%activeD(i1,i2)) + Int_Xferred = Int_Xferred + 1 + END DO + END DO + END IF END SUBROUTINE UA_UnPackOtherState SUBROUTINE UA_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) From 5475ae35d59b6e53ee9c1648b69b0b05efaba74d Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 25 Jun 2021 21:07:00 -0600 Subject: [PATCH 09/24] FVW: option for wake at LL instead of TE --- modules/aerodyn/src/FVW.f90 | 15 +++++--- modules/aerodyn/src/FVW_IO.f90 | 28 ++++++++++++-- modules/aerodyn/src/FVW_Registry.txt | 4 ++ modules/aerodyn/src/FVW_Subs.f90 | 57 ++++++++++++++++------------ modules/aerodyn/src/FVW_Types.f90 | 21 ++++++++++ modules/aerodyn/src/FVW_Wings.f90 | 28 +++++++++----- 6 files changed, 112 insertions(+), 41 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index af28b4d60..71ec03c5e 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -109,6 +109,11 @@ subroutine FVW_Init(AFInfo, InitInp, u, p, x, xd, z, OtherState, y, m, Interval, p%nFWFree = max(InputFileData%nFWPanelsFree,0) p%DTfvw = InputFileData%DTfvw p%DTvtk = InputFileData%DTvtk + if (p%WakeAtTE) then + p%iNWStart=2 + else + p%iNWStart=1 + endif ! Initialize Misc Vars (may depend on input file) CALL FVW_InitMiscVars( p, m, ErrStat2, ErrMsg2 ); if(Failed()) return @@ -184,7 +189,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) ErrMsg = "" m%FirstCall = .True. - m%nNW = iNWStart-1 ! Number of active nearwake panels + m%nNW = p%iNWStart-1 ! Number of active nearwake panels m%nFW = 0 ! Number of active farwake panels m%iStep = 0 ! Current step number m%VTKStep = -1 ! Counter of VTK outputs @@ -932,7 +937,7 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt endif ! First NW point does not convect (bound to LL) do iW=1,p%nWings - dxdt%W(iW)%r_NW(1:3, :, 1:iNWStart-1)=0.0_ReKi + dxdt%W(iW)%r_NW(1:3, :, 1:p%iNWStart-1)=0.0_ReKi enddo ! First FW point always convects (even if bound to NW) ! This is done for overcycling @@ -958,8 +963,8 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt visc_fact = 2.0_ReKi * CoreSpreadAlpha * p%CoreSpreadEddyVisc * p%KinVisc ! --- Method 1, use d(rc^2)/dt = 4 k do iW=1,p%nWings - dxdt%W(iW)%Eps_NW(1:3, :, iNWStart:) = visc_fact/x%W(iW)%Eps_NW(1:3, :, iNWStart:) - dxdt%W(iW)%Eps_FW(1:3, :, :) = visc_fact/x%W(iW)%Eps_FW(1:3, :, :) + dxdt%W(iW)%Eps_NW(1:3, :,p%iNWStart:) = visc_fact/x%W(iW)%Eps_NW(1:3, :, p%iNWStart:) + dxdt%W(iW)%Eps_FW(1:3, :, :) = visc_fact/x%W(iW)%Eps_FW(1:3, :, :) ! --- Method 2, use rc(tau) = 2k/sqrt(r_c^2(tau=0) + 4 k tau) !dxdt%W(iW)%Eps_NW(1:3, :, :, :) = (visc_fact)/sqrt(x%W(iW)%Eps_NW(1:3, :, :, :)**2 + 2*visc_fact*p%DTaero) !dxdt%W(iW)%Eps_FW(1:3, :, :, :) = (visc_fact)/sqrt(x%W(iW)%Eps_FW(1:3, :, :, :)**2 + 4*visc_fact*p%DTaero) @@ -969,7 +974,7 @@ subroutine FVW_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrSt ErrMsg ='Regularization method not implemented' endif do iW=1,p%nWings - dxdt%W(iW)%Eps_NW(1:3,:,1:iNWStart) = 0.0_ReKi ! Important! LL and First NW panel epsilon does not change + dxdt%W(iW)%Eps_NW(1:3,:,1:p%iNWStart) = 0.0_ReKi ! Important! LL and First NW panel epsilon does not change enddo contains diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index c465f38d0..aeac75748 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -18,7 +18,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, m, Inp, ErrStat, ErrMsg ) character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None ! Local variables character(1024) :: PriPath ! the path to the primary input file - character(1024) :: sDummy, sLine ! string to temporarially hold value of read line + character(1024) :: sDummy, sLine, Key, Val ! string to temporarially hold value of read line integer(IntKi) :: UnIn, i integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 @@ -102,6 +102,28 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, m, Inp, ErrStat, ErrMsg ) enddo endif + ! --- Advanced Options + ! NOTE: no error handling since this is for debug + p%InductionAtCP = .true. + p%WakeAtTE = .true. + CALL ReadCom(UnIn,FileName, '=== Separator' ,ErrStat2,ErrMsg2); + CALL ReadCom(UnIn,FileName, '--- Advanced options header' ,ErrStat2,ErrMsg2); + if(ErrStat2==ErrID_None) then + call WrScr(' - Reading advanced options for OLAF:') + do while(ErrStat2==ErrID_None) + read(UnIn, '(A)',iostat=ErrStat2) sDummy + call Conv2UC(sDummy) ! to uppercase + if (index(sDummy, 'INDUCTIONATCP')>1) then + read(sDummy, '(L1)') p%InductionAtCP + print*,' >>> InductionAtCP',p%InductionAtCP + elseif (index(sDummy, 'WAKEATTE')>1) then + read(sDummy, '(L1)') p%WakeAtTE + print*,' >>> WakeAtTE',p%WakeAtTE + endif + enddo + endif + + ! --- Validation of inputs if (PathIsRelative(Inp%CirculationFile)) Inp%CirculationFile = TRIM(PriPath)//TRIM(Inp%CirculationFile) @@ -386,8 +408,8 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth, bladeFrame, Hub 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 - allocate(Arr3D(3, size(m%dxdt%W(iW)%r_NW,2) , m%nNW+1)); Arr3D=0.0_ReKi ! Convection velocity - allocate(Arr2D(size(z%W(iW)%Gamma_LL), 1) ) ; Arr2D=0.0_ReKi + allocate(Arr3D(3, size(m%dxdt%W(iW)%r_NW,2) ,2)); Arr3D=0.0_ReKi ! Convection velocity + allocate(Arr2D(size(z%W(iW)%Gamma_LL), 1) ) ; Arr2D=0.0_ReKi ! Gamma Arr2D(:,1)=z%W(iW)%Gamma_LL(:) call WrVTK_Lattice(FileName, mvtk, m%W(iW)%r_LL(1:3,:,1:2), Arr2D(:,1:1), Arr3D, bladeFrame=bladeFrame) deallocate(Arr3D) diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index ec6631f30..58a36d448 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -59,6 +59,7 @@ typedef ^ ^ IntKi #typedef ^ ^ IntKi Rot2Wings : - - "Index mapping from wings to rotors" - typedef ^ ^ Wng_ParameterType W : - - "Wings parameters" - typedef ^ ^ IntKi Bld2Wings :: - - "Index mapping from blades to wings" - +typedef ^ ^ IntKi iNWStart - - - "Index where NW start in r_NW. (iNWStart=2, the first panel contains the lifting line panel, otherwise, start at 1)" - 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" - @@ -95,6 +96,9 @@ typedef ^ ^ CHARACTER(1024) typedef ^ ^ CHARACTER(1024) VTK_OutFileRoot - - - "Rootdirectory for writing VTK files" - typedef ^ ^ CHARACTER(1024) VTK_OutFileBase - - - "Basename for writing VTK files" - typedef ^ ^ IntKi nGridOut - - - "Number of VTK grid to output" - +# Parameters advanced options +typedef ^ ^ Logical InductionAtCP - - - "Compute induced velocities at nodes or CP" +typedef ^ ^ Logical WakeAtTE - - - "Start the wake at the trailing edge, or at the LL" #.......... ContinuousStateType ...... typedef FVW/FVW Wng_ContinuousStateType ReKi Gamma_NW :: - - "Circulation of the near wake panels ( nSpan x nNW )" - diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 2505167ab..bd1d1d77a 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -49,7 +49,6 @@ module FVW_SUBS 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 W(iW)%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 @@ -216,24 +215,34 @@ subroutine Map_LL_NW(p, m, z, x, ShedScale, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" - ! First panel of NW is the last lifting line panel - do iW = 1,p%nWings - do iSpan = 1,p%W(iW)%nSpan+1 - x%W(iW)%r_NW(1:3, iSpan, iNWStart-1) = m%W(iW)%r_LL(1:3, iSpan, 1) ! iAge=1 - x%W(iW)%r_NW(1:3, iSpan, iNWStart ) = m%W(iW)%r_LL(1:3, iSpan, 2) ! iAge=2 + if (p%WakeAtTE) then + ! First panel of NW is the last lifting line panel + do iW = 1,p%nWings + do iSpan = 1,p%W(iW)%nSpan+1 + x%W(iW)%r_NW(1:3, iSpan, p%iNWStart-1) = m%W(iW)%r_LL(1:3, iSpan, 1) ! iAge=1 (LL) + x%W(iW)%r_NW(1:3, iSpan, p%iNWStart ) = m%W(iW)%r_LL(1:3, iSpan, 2) ! iAge=2 (TE) + enddo enddo - enddo - ! First panel of NW is the last lifting line panel - do iW = 1,p%nWings - do iSpan = 1,p%W(iW)%nSpan - x%W(iW)%Gamma_NW(iSpan, iNWStart-1) = z%W(iW)%Gamma_LL(iSpan) ! iAge=1 + ! First panel of NW is the last lifting line panel + do iW = 1,p%nWings + do iSpan = 1,p%W(iW)%nSpan + x%W(iW)%Gamma_NW(iSpan, p%iNWStart-1) = z%W(iW)%Gamma_LL(iSpan) ! iAge=1 + enddo enddo - enddo + else + ! First panel of NW is the last lifting line panel + do iW = 1,p%nWings + do iSpan = 1,p%W(iW)%nSpan+1 + x%W(iW)%r_NW(1:3, iSpan, p%iNWStart ) = m%W(iW)%r_LL(1:3, iSpan, 1) ! iAge=1 (LL) + enddo + enddo + endif + ! Circulations are the same on both side of the TE - if (p%nNWMax>iNWStart-1) then + if (p%nNWMax>p%iNWStart-1) then do iW = 1,p%nWings do iSpan = 1,p%W(iW)%nSpan - x%W(iW)%Gamma_NW(iSpan, iNWStart ) = z%W(iW)%Gamma_LL(iSpan) ! iAge=2 + x%W(iW)%Gamma_NW(iSpan, p%iNWStart ) = z%W(iW)%Gamma_LL(iSpan) ! iAge=2 enddo enddo endif @@ -248,9 +257,9 @@ subroutine Map_LL_NW(p, m, z, x, ShedScale, ErrStat, ErrMsg ) print*,'Scaling' do iW = 1,p%nWings do iSpan = 1,p%W(iW)%nSpan - Gamma_Prev = x%W(iW)%Gamma_NW(iSpan, iNWStart+1) ! Previous circulation - Gamma_New = x%W(iW)%Gamma_NW(iSpan, iNWStart ) - x%W(iW)%Gamma_NW(iSpan, iNWStart ) = Gamma_New*ShedScale + (1.0_ReKi-ShedScale) * Gamma_Prev + Gamma_Prev = x%W(iW)%Gamma_NW(iSpan, p%iNWStart+1) ! Previous circulation + Gamma_New = x%W(iW)%Gamma_NW(iSpan, p%iNWStart ) + x%W(iW)%Gamma_NW(iSpan, p%iNWStart ) = Gamma_New*ShedScale + (1.0_ReKi-ShedScale) * Gamma_Prev enddo enddo endif @@ -360,22 +369,22 @@ 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 + do iAge=p%nNWMax+1,p%iNWStart+1,-1 do iSpan=1,p%W(iW)%nSpan+1 x%W(iW)%r_NW(1:3,iSpan,iAge) = x%W(iW)%r_NW(1:3,iSpan,iAge-1) enddo enddo - x%W(iW)%r_NW(1:3,:,1:iNWStart) = -999.9_ReKi ! Nullified + x%W(iW)%r_NW(1:3,:,1:p%iNWStart) = -999.9_ReKi ! Nullified enddo if (p%nNWMax>1) then do iW=1,p%nWings - do iAge=p%nNWMax,iNWStart+1,-1 + do iAge=p%nNWMax,p%iNWStart+1,-1 do iSpan=1,p%W(iW)%nSpan x%W(iW)%Gamma_NW(iSpan,iAge) = x%W(iW)%Gamma_NW(iSpan,iAge-1) x%W(iW)%Eps_NW(:,iSpan,iAge) = x%W(iW)%Eps_NW(:,iSpan,iAge-1) enddo enddo - x%W(iW)%Gamma_NW(:,1:iNWStart) = -999.9_ReKi ! Nullified + x%W(iW)%Gamma_NW(:,1:p%iNWStart) = -999.9_ReKi ! Nullified enddo endif @@ -390,12 +399,12 @@ subroutine PropagateWake(p, m, z, x, ErrStat, ErrMsg) !m%dxdt_FW(1:3,1:FWnSpan+1,1) = -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 iAge=p%nNWMax+1,p%iNWStart+1,-1 do iSpan=1,p%W(iW)%nSpan+1 m%dxdt%W(iW)%r_NW(1:3,iSpan,iAge) = m%dxdt%W(iW)%r_NW(1:3,iSpan,iAge-1) enddo enddo - m%dxdt%W(iW)%r_NW(1:3,:,1:iNWStart) = 0.0_ReKi ! Nullified, wing do no convect, handled by LL,NW mapping + m%dxdt%W(iW)%r_NW(1:3,:,1:p%iNWStart) = 0.0_ReKi ! Nullified, wing do no convect, handled by LL,NW mapping enddo if (.false.) print*,m%nNW,z%W(iW)%Gamma_LL(1) ! Just to avoid unused var warning @@ -411,7 +420,7 @@ subroutine print_x_NW_FW(p, m, x, label) integer(IntKi) :: iAge, iW character(len=1):: flag print*,'------------------------------------------------------------------' - print'(A,I0,A,I0)',' NW .....................iNWStart:',iNWStart,' nNW:',m%nNW + print'(A,I0,A,I0)',' NW .....................iNWStart:',p%iNWStart,' nNW:',m%nNW iW=1 do iAge=1,p%nNWMax+1 flag='X' diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index d08897877..9c900c3cd 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -88,6 +88,7 @@ MODULE FVW_Types INTEGER(IntKi) :: nWings !< Number of Wings [-] TYPE(Wng_ParameterType) , DIMENSION(:), ALLOCATABLE :: W !< Wings parameters [-] INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: Bld2Wings !< Index mapping from blades to wings [-] + INTEGER(IntKi) :: iNWStart !< Index where NW start in r_NW. (iNWStart=2, the first panel contains the lifting line panel, otherwise, start at 1) [-] 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 [-] @@ -123,6 +124,8 @@ MODULE FVW_Types CHARACTER(1024) :: VTK_OutFileRoot !< Rootdirectory for writing VTK files [-] CHARACTER(1024) :: VTK_OutFileBase !< Basename for writing VTK files [-] INTEGER(IntKi) :: nGridOut !< Number of VTK grid to output [-] + LOGICAL :: InductionAtCP !< Compute induced velocities at nodes or CP [-] + LOGICAL :: WakeAtTE !< Start the wake at the trailing edge, or at the LL [-] END TYPE FVW_ParameterType ! ======================= ! ========= Wng_ContinuousStateType ======= @@ -1674,6 +1677,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg END IF DstParamData%Bld2Wings = SrcParamData%Bld2Wings ENDIF + DstParamData%iNWStart = SrcParamData%iNWStart DstParamData%nNWMax = SrcParamData%nNWMax DstParamData%nFWMax = SrcParamData%nFWMax DstParamData%nFWFree = SrcParamData%nFWFree @@ -1709,6 +1713,8 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%VTK_OutFileRoot = SrcParamData%VTK_OutFileRoot DstParamData%VTK_OutFileBase = SrcParamData%VTK_OutFileBase DstParamData%nGridOut = SrcParamData%nGridOut + DstParamData%InductionAtCP = SrcParamData%InductionAtCP + DstParamData%WakeAtTE = SrcParamData%WakeAtTE END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -1797,6 +1803,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 2*2 ! Bld2Wings upper/lower bounds for each dimension Int_BufSz = Int_BufSz + SIZE(InData%Bld2Wings) ! Bld2Wings END IF + Int_BufSz = Int_BufSz + 1 ! iNWStart Int_BufSz = Int_BufSz + 1 ! nNWMax Int_BufSz = Int_BufSz + 1 ! nFWMax Int_BufSz = Int_BufSz + 1 ! nFWFree @@ -1832,6 +1839,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1*LEN(InData%VTK_OutFileRoot) ! VTK_OutFileRoot Int_BufSz = Int_BufSz + 1*LEN(InData%VTK_OutFileBase) ! VTK_OutFileBase Int_BufSz = Int_BufSz + 1 ! nGridOut + Int_BufSz = Int_BufSz + 1 ! InductionAtCP + Int_BufSz = Int_BufSz + 1 ! WakeAtTE IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -1924,6 +1933,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S END DO END DO END IF + IntKiBuf(Int_Xferred) = InData%iNWStart + Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = InData%nNWMax Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = InData%nFWMax @@ -2000,6 +2011,10 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S END DO ! I IntKiBuf(Int_Xferred) = InData%nGridOut Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%InductionAtCP, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%WakeAtTE, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2113,6 +2128,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg END DO END DO END IF + OutData%iNWStart = IntKiBuf(Int_Xferred) + Int_Xferred = Int_Xferred + 1 OutData%nNWMax = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 OutData%nFWMax = IntKiBuf(Int_Xferred) @@ -2189,6 +2206,10 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg END DO ! I OutData%nGridOut = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 + OutData%InductionAtCP = TRANSFER(IntKiBuf(Int_Xferred), OutData%InductionAtCP) + Int_Xferred = Int_Xferred + 1 + OutData%WakeAtTE = TRANSFER(IntKiBuf(Int_Xferred), OutData%WakeAtTE) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyWng_ContinuousStateType( SrcWng_ContinuousStateTypeData, DstWng_ContinuousStateTypeData, CtrlCode, ErrStat, ErrMsg ) diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 408491423..17b0496a1 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -193,14 +193,24 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) 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 - do iW = 1,p%nWings - do iSpan = 1,p%W(iW)%nSpan+1 - m%W(iW)%r_LL(1:3,iSpan,1)= m%W(iW)%TE(1:3,iSpan)*0.25_ReKi+m%W(iW)%LE(1:3,iSpan)*0.75_ReKi ! 1/4 chord - m%W(iW)%r_LL(1:3,iSpan,2)= m%W(iW)%TE(1:3,iSpan) ! TE + if (p%WakeAtTE) then + ! 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%W(iW)%nSpan+1 + m%W(iW)%r_LL(1:3,iSpan,1)= m%W(iW)%TE(1:3,iSpan)*0.25_ReKi+m%W(iW)%LE(1:3,iSpan)*0.75_ReKi ! 1/4 chord + m%W(iW)%r_LL(1:3,iSpan,2)= m%W(iW)%TE(1:3,iSpan) ! TE + enddo enddo - enddo + else + ! In this formulation we collapse the "TE" and "LL" + do iW = 1,p%nWings + do iSpan = 1,p%W(iW)%nSpan+1 + m%W(iW)%r_LL(1:3,iSpan,1)= m%W(iW)%TE(1:3,iSpan)*0.25_ReKi+m%W(iW)%LE(1:3,iSpan)*0.75_ReKi ! 1/4 chord + m%W(iW)%r_LL(1:3,iSpan,2)= m%W(iW)%r_LL(1:3,iSpan,1) + enddo + enddo + endif ! --- Position of control points CP_LL ! For now: placed exactly on the LL panel @@ -366,7 +376,7 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc ! Set m%W(iW)%Vind_LL Induced velocity from Known wake only (after iNWStart+1) ! Input: m%W%CP_LL, output: m%W%Vind_LL - call LiftingLineInducedVelocities(p, x, iNWStart+1, m, ErrStat2, ErrMsg2); if(Failed()) return; + call LiftingLineInducedVelocities(p, x, p%iNWStart+1, m, ErrStat2, ErrMsg2); if(Failed()) return; kCP=0 do iW=1,p%nWings @@ -403,7 +413,7 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc do iSpan=1,p%W(iW)%nSpan kCP=kCP+1 Gamm=GammaLastIter(kCP) - do iDepth=1,iNWStart ! Two first panels + do iDepth=1,p%iNWStart ! Two first panels ! --- Defining a ring P1=x%W(iW)%r_NW(1:3,iSpan ,iDepth ) P2=x%W(iW)%r_NW(1:3,iSpan+1,iDepth ) From c7810bb2e173f41bb45665206232d04a75af35d0 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 25 Jun 2021 21:57:24 -0600 Subject: [PATCH 10/24] FVW: renamed _LL to _CP --- modules/aerodyn/src/FVW.f90 | 54 +++--- modules/aerodyn/src/FVW_IO.f90 | 10 +- modules/aerodyn/src/FVW_Registry.txt | 12 +- modules/aerodyn/src/FVW_Subs.f90 | 12 +- modules/aerodyn/src/FVW_Types.f90 | 248 +++++++++++++-------------- modules/aerodyn/src/FVW_Wings.f90 | 38 ++-- 6 files changed, 195 insertions(+), 179 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 71ec03c5e..81fcbed51 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -216,10 +216,10 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%W(iW)%dl , 3 , p%W(iW)%nSpan, 'Orthogonal vector ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%dl= -999999_ReKi; call AllocAry( m%W(iW)%Area , p%W(iW)%nSpan, 'LL Panel area ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Area = -999999_ReKi; call AllocAry( m%W(iW)%diag_LL , p%W(iW)%nSpan, 'LL Panel diagonals ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%diag_LL = -999999_ReKi; - call AllocAry( m%W(iW)%Vind_LL , 3 , p%W(iW)%nSpan, 'Vind on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vind_LL= -999999_ReKi; - call AllocAry( m%W(iW)%Vtot_LL , 3 , p%W(iW)%nSpan, 'Vtot on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vtot_LL= -999999_ReKi; - call AllocAry( m%W(iW)%Vstr_LL , 3 , p%W(iW)%nSpan, 'Vstr on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vstr_LL= -999999_ReKi; - call AllocAry( m%W(iW)%Vwnd_LL , 3 , p%W(iW)%nSpan, 'Wind on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vwnd_LL= -999999_ReKi; + call AllocAry( m%W(iW)%Vind_CP , 3 , p%W(iW)%nSpan, 'Vind on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vind_CP= -999999_ReKi; + call AllocAry( m%W(iW)%Vtot_CP , 3 , p%W(iW)%nSpan, 'Vtot on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vtot_CP= -999999_ReKi; + call AllocAry( m%W(iW)%Vstr_CP , 3 , p%W(iW)%nSpan, 'Vstr on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vstr_CP= -999999_ReKi; + call AllocAry( m%W(iW)%Vwnd_CP , 3 , p%W(iW)%nSpan, 'Wind on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vwnd_CP= -999999_ReKi; ! Variables at panels points call AllocAry( m%W(iW)%r_LL , 3 , p%W(iW)%nSpan+1 , 2 , 'Lifting Line Panels', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%r_LL= -999999_ReKi; call AllocAry( m%W(iW)%Vwnd_NW , 3 , p%W(iW)%nSpan+1 ,p%nNWMax+1, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vwnd_NW= -999_ReKi; @@ -250,7 +250,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) ! Wind set to 0. TODO check if -99999 works now !NOTE: We do not have the windspeed until after the FVW initialization (IfW is not initialized until after AD15) - m%W(iW)%Vwnd_LL(:,:) = 0 + m%W(iW)%Vwnd_CP(:,:) = 0 m%W(iW)%Vwnd_NW(:,:,:) = 0 m%W(iW)%Vwnd_FW(:,:,:) = 0 enddo @@ -1382,21 +1382,33 @@ subroutine CalcOutputForAD(t, u, p, x, y, m, AFInfo, ErrStat, ErrMsg) !--- ! Induction on the lifting line control point - ! Input: m%W%CP_LL, Output:m%W%Vind_LL + ! Input: m%W%CP_LL, Output:m%W%Vind_CP do iW=1,p%nWings - m%W(iW)%Vind_LL=-9999.0_ReKi + m%W(iW)%Vind_CP=-9999.0_ReKi enddo call LiftingLineInducedVelocities(p, x, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - ! Induction on the mesh points (AeroDyn nodes) - do iW=1,p%nWings - y%W(iW)%Vind(1:3,:) = 0.0_ReKi - ! --- Linear interpolation for interior points and extrapolations at boundaries - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_LL(1,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(1,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_LL(2,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(2,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_LL(3,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(3,:)) - enddo + ! Induction on the mesh points (AeroDyn nodes): y%W(iW)%Vind + if (p%InductionAtCP) then + ! We use the induction at the CP (Vind_LL) to interpextrap at the node + do iW=1,p%nWings + y%W(iW)%Vind(1:3,:) = 0.0_ReKi + ! --- Linear interpolation for interior points and extrapolations at boundaries + call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(1,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(1,:)) + call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(2,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(2,:)) + call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(3,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(3,:)) + enddo + else + print*,'>>>> TODO' + STOP + ! The induction was computed at the nodes + do iW=1,p%nWings + ! y%W(iW)%Vind(1,:) = m%W(iW)%Vind_LL(1,:) + ! y%W(iW)%Vind(2,:) = m%W(iW)%Vind_LL(2,:) + ! y%W(iW)%Vind(3,:) = m%W(iW)%Vind_LL(3,:) + enddo + endif end subroutine CalcOutputForAD !---------------------------------------------------------------------------------------------------------------------------------- !> Routine for computing outputs, used in both loose and tight coupling. @@ -1477,7 +1489,7 @@ subroutine WriteVTKOutputs(t, force, u, p, x, z, y, m, ErrStat, ErrMsg) ! For plotting only call PackPanelsToSegments(p, x, 1, (p%ShearModel==idShearMirror), m%nNW, m%nFW, m%Sgmt%Connct, m%Sgmt%Points, m%Sgmt%Gamma, m%Sgmt%Epsilon, nSeg, nSegP) do iW=1,p%nWings - m%W(iW)%Vtot_LL = m%W(iW)%Vind_LL + m%W(iW)%Vwnd_LL - m%W(iW)%Vstr_LL + m%W(iW)%Vtot_CP = m%W(iW)%Vind_CP + m%W(iW)%Vwnd_CP - m%W(iW)%Vstr_CP enddo if ( force .or. (( t - m%VTKlastTime ) >= p%DTvtk*OneMinusEpsilon )) then m%VTKlastTime = t @@ -1625,16 +1637,16 @@ subroutine CalculateInputsAndOtherStatesForUA(InputIndex, u, p, x, xd, z, OtherS ! Set m%W(iW)%Vind_LL do iW = 1,p%nWings - m%W(iW)%Vind_LL=-9999.0_ReKi - ! Input: m%W%CP_LL, Output:m%W%Vind_LL + m%W(iW)%Vind_CP=-9999.0_ReKi + ! Input: m%W%CP_LL, Output:m%W%Vind_CP 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%W(iW)%nSpan+1)) ! Induced velocity at Nodes (NOTE: we rely on storage done when computing Circulation) if (m%nNW>1) then - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_LL(1,:), p%W(iW)%s_LL(:), Vind_node(1,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_LL(2,:), p%W(iW)%s_LL(:), Vind_node(2,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_LL(3,:), p%W(iW)%s_LL(:), Vind_node(3,:)) + call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(1,:), p%W(iW)%s_LL(:), Vind_node(1,:)) + call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(2,:), p%W(iW)%s_LL(:), Vind_node(2,:)) + call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(3,:), p%W(iW)%s_LL(:), Vind_node(3,:)) else Vind_node=0.0_ReKi endif diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index aeac75748..fcf6ab3dd 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -382,11 +382,11 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth, bladeFrame, Hub if ( vtk_new_ascii_file(trim(filename),Label,mvtk) ) then call vtk_dataset_polydata(m%W(iW)%CP_LL(1:3,1:p%W(iW)%nSpan),mvtk,bladeFrame) call vtk_point_data_init(mvtk) - call vtk_point_data_scalar(z%W(iW)%Gamma_ll( 1:p%W(iW)%nSpan),'Gamma_ll',mvtk) - call vtk_point_data_vector(m%W(iW)%Vind_ll (1:3,1:p%W(iW)%nSpan),'Vind_ll',mvtk) - call vtk_point_data_vector(m%W(iW)%Vtot_ll (1:3,1:p%W(iW)%nSpan),'Vtot_ll',mvtk) - call vtk_point_data_vector(m%W(iW)%Vstr_ll (1:3,1:p%W(iW)%nSpan),'Vstr_ll',mvtk) - call vtk_point_data_vector(m%W(iW)%Vwnd_ll (1:3,1:p%W(iW)%nSpan),'Vwnd_ll',mvtk) + call vtk_point_data_scalar(z%W(iW)%Gamma_LL( 1:p%W(iW)%nSpan),'Gamma_LL',mvtk) + call vtk_point_data_vector(m%W(iW)%Vind_CP (1:3,1:p%W(iW)%nSpan),'Vind_CP',mvtk) + call vtk_point_data_vector(m%W(iW)%Vtot_CP (1:3,1:p%W(iW)%nSpan),'Vtot_CP',mvtk) + call vtk_point_data_vector(m%W(iW)%Vstr_CP (1:3,1:p%W(iW)%nSpan),'Vstr_CP',mvtk) + call vtk_point_data_vector(m%W(iW)%Vwnd_CP (1:3,1:p%W(iW)%nSpan),'Vwnd_CP',mvtk) call vtk_point_data_vector(m%W(iW)%Tang (1:3,1:p%W(iW)%nSpan),'Tangent',mvtk) call vtk_point_data_vector(m%W(iW)%Norm (1:3,1:p%W(iW)%nSpan),'Normal',mvtk) call vtk_point_data_vector(m%W(iW)%Orth (1:3,1:p%W(iW)%nSpan),'Orth',mvtk) diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 58a36d448..7559f515f 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -130,10 +130,10 @@ typedef ^ ^ ReKi 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 -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 Vind_CP :: - - "Induced velocity on lifting line control points" m/s +typedef ^ ^ ReKi Vtot_CP :: - - "Total velocity on lifting line control points" m/s +typedef ^ ^ ReKi Vstr_CP :: - - "Structural velocity on LL CP" m/s +typedef ^ ^ ReKi Vwnd_CP :: - - "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 @@ -150,6 +150,10 @@ typedef ^ ^ UA_MiscVarType typedef ^ ^ UA_OutputType y_UA - - - "outputs from UnsteadyAero" - typedef ^ ^ UA_ParameterType p_UA - - - "parameters for UnsteadyAero" - # for calculating outputs at blade nodes +#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 BN_AxInd : - - "Axial induction [size (NumBlNds,numBlades)]" - typedef ^ ^ ReKi BN_TanInd : - - "Tangential induction [size (NumBlNds,numBlades)]" - typedef ^ ^ ReKi BN_Vrel : - - "Relative velocity [size (NumBlNds,numBlades)]" m/s diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index bd1d1d77a..37bf77993 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -606,7 +606,7 @@ subroutine DistributeRequestedWind_LL(V_wind, p, m) do iW=1,p%nWings iP_start = iP_end+1 iP_end = iP_start-1 + p%W(iW)%nSpan - m%W(iW)%Vwnd_LL(1:3,1:p%W(iW)%nSpan) = V_wind(1:3,iP_start:iP_end) + m%W(iW)%Vwnd_CP(1:3,1:p%W(iW)%nSpan) = V_wind(1:3,iP_start:iP_end) enddo end subroutine DistributeRequestedWind_LL @@ -1219,14 +1219,14 @@ end subroutine WakeInducedVelocities !> Compute induced velocities from all vortex elements onto the lifting line control points !! In : x%W(iW)%r_NW, x%W(iW)%r_FW, x%W(iW)%Gamma_NW, x%W(iW)%Gamma_FW -!! Out: m%W(iW)%Vind_LL +!! Out: m%W(iW)%Vind_CP subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) !real(ReKi), dimension(:,:,:), intent(in ) :: CP_LL !< Control points where velocity is to be evaluated 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 - !real(ReKi), dimension(:,:,:), intent( out) :: Vind_LL !< Control points where velocity is to be evaluated + !real(ReKi), dimension(:,:,:), intent( out) :: Vind_CP !< Control points where velocity is to be evaluated ! Local variables integer(IntKi) :: iW, nSeg, nSegP, nCPs, iHeadP real(ReKi), dimension(:,:), allocatable :: CPs !< ControlPoints @@ -1237,7 +1237,7 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) ErrStat = ErrID_None ErrMsg = "" do iW=1,p%nWings - m%W(iW)%Vind_LL = -9999._ReKi !< Safety + m%W(iW)%Vind_CP = -9999._ReKi !< Safety enddo bMirror = p%ShearModel==idShearMirror ! Whether or not we mirror the vorticity wrt ground @@ -1248,7 +1248,7 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) if (nSegP==0) then nCPs=0 do iW=1,p%nWings - m%W(iW)%Vind_LL = 0.0_ReKi !< Safety + m%W(iW)%Vind_CP = 0.0_ReKi !< Safety enddo if (DEV_VERSION) then print'(A,I0,A,I0,A,I0,A)','Induction - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs, ' -> No induction' @@ -1292,7 +1292,7 @@ subroutine PackLiftingLinePoints() subroutine UnPackLiftingLineVelocities() iHeadP=1 do iW=1,p%nWings - CALL VecToLattice2D(Uind, m%W(iW)%Vind_LL(1:3,:), iHeadP) + CALL VecToLattice2D(Uind, m%W(iW)%Vind_CP(1:3,:), iHeadP) enddo if (DEV_VERSION) then if ((iHeadP-1)/=size(Uind,2)) then diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 9c900c3cd..0bd4692a7 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -166,10 +166,10 @@ MODULE FVW_Types 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 :: 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 :: Vind_CP !< Induced velocity on lifting line control points [m/s] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Vtot_CP !< Total velocity on lifting line control points [m/s] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Vstr_CP !< Structural velocity on LL CP [m/s] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Vwnd_CP !< 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] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Vind_NW !< Induced velocity on near wake panels [m/s] @@ -3788,61 +3788,61 @@ SUBROUTINE FVW_CopyWng_MiscVarType( SrcWng_MiscVarTypeData, DstWng_MiscVarTypeDa END IF DstWng_MiscVarTypeData%diag_LL = SrcWng_MiscVarTypeData%diag_LL ENDIF -IF (ALLOCATED(SrcWng_MiscVarTypeData%Vind_LL)) THEN - i1_l = LBOUND(SrcWng_MiscVarTypeData%Vind_LL,1) - i1_u = UBOUND(SrcWng_MiscVarTypeData%Vind_LL,1) - i2_l = LBOUND(SrcWng_MiscVarTypeData%Vind_LL,2) - i2_u = UBOUND(SrcWng_MiscVarTypeData%Vind_LL,2) - IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%Vind_LL)) THEN - ALLOCATE(DstWng_MiscVarTypeData%Vind_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) +IF (ALLOCATED(SrcWng_MiscVarTypeData%Vind_CP)) THEN + i1_l = LBOUND(SrcWng_MiscVarTypeData%Vind_CP,1) + i1_u = UBOUND(SrcWng_MiscVarTypeData%Vind_CP,1) + i2_l = LBOUND(SrcWng_MiscVarTypeData%Vind_CP,2) + i2_u = UBOUND(SrcWng_MiscVarTypeData%Vind_CP,2) + IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%Vind_CP)) THEN + ALLOCATE(DstWng_MiscVarTypeData%Vind_CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%Vind_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%Vind_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstWng_MiscVarTypeData%Vind_LL = SrcWng_MiscVarTypeData%Vind_LL + DstWng_MiscVarTypeData%Vind_CP = SrcWng_MiscVarTypeData%Vind_CP ENDIF -IF (ALLOCATED(SrcWng_MiscVarTypeData%Vtot_LL)) THEN - i1_l = LBOUND(SrcWng_MiscVarTypeData%Vtot_LL,1) - i1_u = UBOUND(SrcWng_MiscVarTypeData%Vtot_LL,1) - i2_l = LBOUND(SrcWng_MiscVarTypeData%Vtot_LL,2) - i2_u = UBOUND(SrcWng_MiscVarTypeData%Vtot_LL,2) - IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%Vtot_LL)) THEN - ALLOCATE(DstWng_MiscVarTypeData%Vtot_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) +IF (ALLOCATED(SrcWng_MiscVarTypeData%Vtot_CP)) THEN + i1_l = LBOUND(SrcWng_MiscVarTypeData%Vtot_CP,1) + i1_u = UBOUND(SrcWng_MiscVarTypeData%Vtot_CP,1) + i2_l = LBOUND(SrcWng_MiscVarTypeData%Vtot_CP,2) + i2_u = UBOUND(SrcWng_MiscVarTypeData%Vtot_CP,2) + IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%Vtot_CP)) THEN + ALLOCATE(DstWng_MiscVarTypeData%Vtot_CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%Vtot_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%Vtot_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstWng_MiscVarTypeData%Vtot_LL = SrcWng_MiscVarTypeData%Vtot_LL + DstWng_MiscVarTypeData%Vtot_CP = SrcWng_MiscVarTypeData%Vtot_CP ENDIF -IF (ALLOCATED(SrcWng_MiscVarTypeData%Vstr_LL)) THEN - i1_l = LBOUND(SrcWng_MiscVarTypeData%Vstr_LL,1) - i1_u = UBOUND(SrcWng_MiscVarTypeData%Vstr_LL,1) - i2_l = LBOUND(SrcWng_MiscVarTypeData%Vstr_LL,2) - i2_u = UBOUND(SrcWng_MiscVarTypeData%Vstr_LL,2) - IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%Vstr_LL)) THEN - ALLOCATE(DstWng_MiscVarTypeData%Vstr_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) +IF (ALLOCATED(SrcWng_MiscVarTypeData%Vstr_CP)) THEN + i1_l = LBOUND(SrcWng_MiscVarTypeData%Vstr_CP,1) + i1_u = UBOUND(SrcWng_MiscVarTypeData%Vstr_CP,1) + i2_l = LBOUND(SrcWng_MiscVarTypeData%Vstr_CP,2) + i2_u = UBOUND(SrcWng_MiscVarTypeData%Vstr_CP,2) + IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%Vstr_CP)) THEN + ALLOCATE(DstWng_MiscVarTypeData%Vstr_CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%Vstr_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%Vstr_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstWng_MiscVarTypeData%Vstr_LL = SrcWng_MiscVarTypeData%Vstr_LL + DstWng_MiscVarTypeData%Vstr_CP = SrcWng_MiscVarTypeData%Vstr_CP ENDIF -IF (ALLOCATED(SrcWng_MiscVarTypeData%Vwnd_LL)) THEN - i1_l = LBOUND(SrcWng_MiscVarTypeData%Vwnd_LL,1) - i1_u = UBOUND(SrcWng_MiscVarTypeData%Vwnd_LL,1) - i2_l = LBOUND(SrcWng_MiscVarTypeData%Vwnd_LL,2) - i2_u = UBOUND(SrcWng_MiscVarTypeData%Vwnd_LL,2) - IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%Vwnd_LL)) THEN - ALLOCATE(DstWng_MiscVarTypeData%Vwnd_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) +IF (ALLOCATED(SrcWng_MiscVarTypeData%Vwnd_CP)) THEN + i1_l = LBOUND(SrcWng_MiscVarTypeData%Vwnd_CP,1) + i1_u = UBOUND(SrcWng_MiscVarTypeData%Vwnd_CP,1) + i2_l = LBOUND(SrcWng_MiscVarTypeData%Vwnd_CP,2) + i2_u = UBOUND(SrcWng_MiscVarTypeData%Vwnd_CP,2) + IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%Vwnd_CP)) THEN + ALLOCATE(DstWng_MiscVarTypeData%Vwnd_CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%Vwnd_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%Vwnd_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstWng_MiscVarTypeData%Vwnd_LL = SrcWng_MiscVarTypeData%Vwnd_LL + DstWng_MiscVarTypeData%Vwnd_CP = SrcWng_MiscVarTypeData%Vwnd_CP ENDIF IF (ALLOCATED(SrcWng_MiscVarTypeData%Vwnd_NW)) THEN i1_l = LBOUND(SrcWng_MiscVarTypeData%Vwnd_NW,1) @@ -4198,17 +4198,17 @@ SUBROUTINE FVW_DestroyWng_MiscVarType( Wng_MiscVarTypeData, ErrStat, ErrMsg ) IF (ALLOCATED(Wng_MiscVarTypeData%diag_LL)) THEN DEALLOCATE(Wng_MiscVarTypeData%diag_LL) ENDIF -IF (ALLOCATED(Wng_MiscVarTypeData%Vind_LL)) THEN - DEALLOCATE(Wng_MiscVarTypeData%Vind_LL) +IF (ALLOCATED(Wng_MiscVarTypeData%Vind_CP)) THEN + DEALLOCATE(Wng_MiscVarTypeData%Vind_CP) ENDIF -IF (ALLOCATED(Wng_MiscVarTypeData%Vtot_LL)) THEN - DEALLOCATE(Wng_MiscVarTypeData%Vtot_LL) +IF (ALLOCATED(Wng_MiscVarTypeData%Vtot_CP)) THEN + DEALLOCATE(Wng_MiscVarTypeData%Vtot_CP) ENDIF -IF (ALLOCATED(Wng_MiscVarTypeData%Vstr_LL)) THEN - DEALLOCATE(Wng_MiscVarTypeData%Vstr_LL) +IF (ALLOCATED(Wng_MiscVarTypeData%Vstr_CP)) THEN + DEALLOCATE(Wng_MiscVarTypeData%Vstr_CP) ENDIF -IF (ALLOCATED(Wng_MiscVarTypeData%Vwnd_LL)) THEN - DEALLOCATE(Wng_MiscVarTypeData%Vwnd_LL) +IF (ALLOCATED(Wng_MiscVarTypeData%Vwnd_CP)) THEN + DEALLOCATE(Wng_MiscVarTypeData%Vwnd_CP) ENDIF IF (ALLOCATED(Wng_MiscVarTypeData%Vwnd_NW)) THEN DEALLOCATE(Wng_MiscVarTypeData%Vwnd_NW) @@ -4374,25 +4374,25 @@ SUBROUTINE FVW_PackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Int_BufSz = Int_BufSz + 2*1 ! 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 ! Vind_LL allocated yes/no - IF ( ALLOCATED(InData%Vind_LL) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! Vind_LL upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%Vind_LL) ! Vind_LL + Int_BufSz = Int_BufSz + 1 ! Vind_CP allocated yes/no + IF ( ALLOCATED(InData%Vind_CP) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Vind_CP upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vind_CP) ! Vind_CP END IF - Int_BufSz = Int_BufSz + 1 ! Vtot_LL allocated yes/no - IF ( ALLOCATED(InData%Vtot_LL) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! Vtot_LL upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%Vtot_LL) ! Vtot_LL + Int_BufSz = Int_BufSz + 1 ! Vtot_CP allocated yes/no + IF ( ALLOCATED(InData%Vtot_CP) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Vtot_CP upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vtot_CP) ! Vtot_CP END IF - Int_BufSz = Int_BufSz + 1 ! Vstr_LL allocated yes/no - IF ( ALLOCATED(InData%Vstr_LL) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! Vstr_LL upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%Vstr_LL) ! Vstr_LL + Int_BufSz = Int_BufSz + 1 ! Vstr_CP allocated yes/no + IF ( ALLOCATED(InData%Vstr_CP) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Vstr_CP upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vstr_CP) ! Vstr_CP END IF - Int_BufSz = Int_BufSz + 1 ! Vwnd_LL allocated yes/no - IF ( ALLOCATED(InData%Vwnd_LL) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! Vwnd_LL upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%Vwnd_LL) ! Vwnd_LL + Int_BufSz = Int_BufSz + 1 ! Vwnd_CP allocated yes/no + IF ( ALLOCATED(InData%Vwnd_CP) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! Vwnd_CP upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%Vwnd_CP) ! Vwnd_CP END IF Int_BufSz = Int_BufSz + 1 ! Vwnd_NW allocated yes/no IF ( ALLOCATED(InData%Vwnd_NW) ) THEN @@ -4805,82 +4805,82 @@ SUBROUTINE FVW_PackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Re_Xferred = Re_Xferred + 1 END DO END IF - IF ( .NOT. ALLOCATED(InData%Vind_LL) ) THEN + IF ( .NOT. ALLOCATED(InData%Vind_CP) ) 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) + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_CP,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_CP,1) Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_LL,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_LL,2) + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vind_CP,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vind_CP,2) Int_Xferred = Int_Xferred + 2 - DO i2 = LBOUND(InData%Vind_LL,2), UBOUND(InData%Vind_LL,2) - DO i1 = LBOUND(InData%Vind_LL,1), UBOUND(InData%Vind_LL,1) - ReKiBuf(Re_Xferred) = InData%Vind_LL(i1,i2) + DO i2 = LBOUND(InData%Vind_CP,2), UBOUND(InData%Vind_CP,2) + DO i1 = LBOUND(InData%Vind_CP,1), UBOUND(InData%Vind_CP,1) + ReKiBuf(Re_Xferred) = InData%Vind_CP(i1,i2) Re_Xferred = Re_Xferred + 1 END DO END DO END IF - IF ( .NOT. ALLOCATED(InData%Vtot_LL) ) THEN + IF ( .NOT. ALLOCATED(InData%Vtot_CP) ) 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) + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vtot_CP,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vtot_CP,1) Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%Vtot_LL,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vtot_LL,2) + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vtot_CP,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vtot_CP,2) Int_Xferred = Int_Xferred + 2 - DO i2 = LBOUND(InData%Vtot_LL,2), UBOUND(InData%Vtot_LL,2) - DO i1 = LBOUND(InData%Vtot_LL,1), UBOUND(InData%Vtot_LL,1) - ReKiBuf(Re_Xferred) = InData%Vtot_LL(i1,i2) + DO i2 = LBOUND(InData%Vtot_CP,2), UBOUND(InData%Vtot_CP,2) + DO i1 = LBOUND(InData%Vtot_CP,1), UBOUND(InData%Vtot_CP,1) + ReKiBuf(Re_Xferred) = InData%Vtot_CP(i1,i2) Re_Xferred = Re_Xferred + 1 END DO END DO END IF - IF ( .NOT. ALLOCATED(InData%Vstr_LL) ) THEN + IF ( .NOT. ALLOCATED(InData%Vstr_CP) ) 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) + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vstr_CP,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vstr_CP,1) Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%Vstr_LL,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vstr_LL,2) + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vstr_CP,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vstr_CP,2) Int_Xferred = Int_Xferred + 2 - DO i2 = LBOUND(InData%Vstr_LL,2), UBOUND(InData%Vstr_LL,2) - DO i1 = LBOUND(InData%Vstr_LL,1), UBOUND(InData%Vstr_LL,1) - ReKiBuf(Re_Xferred) = InData%Vstr_LL(i1,i2) + DO i2 = LBOUND(InData%Vstr_CP,2), UBOUND(InData%Vstr_CP,2) + DO i1 = LBOUND(InData%Vstr_CP,1), UBOUND(InData%Vstr_CP,1) + ReKiBuf(Re_Xferred) = InData%Vstr_CP(i1,i2) Re_Xferred = Re_Xferred + 1 END DO END DO END IF - IF ( .NOT. ALLOCATED(InData%Vwnd_LL) ) THEN + IF ( .NOT. ALLOCATED(InData%Vwnd_CP) ) 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) + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_CP,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_CP,1) Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_LL,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_LL,2) + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_CP,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_CP,2) Int_Xferred = Int_Xferred + 2 - DO i2 = LBOUND(InData%Vwnd_LL,2), UBOUND(InData%Vwnd_LL,2) - DO i1 = LBOUND(InData%Vwnd_LL,1), UBOUND(InData%Vwnd_LL,1) - ReKiBuf(Re_Xferred) = InData%Vwnd_LL(i1,i2) + DO i2 = LBOUND(InData%Vwnd_CP,2), UBOUND(InData%Vwnd_CP,2) + DO i1 = LBOUND(InData%Vwnd_CP,1), UBOUND(InData%Vwnd_CP,1) + ReKiBuf(Re_Xferred) = InData%Vwnd_CP(i1,i2) Re_Xferred = Re_Xferred + 1 END DO END DO @@ -5650,7 +5650,7 @@ SUBROUTINE FVW_UnPackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSt Re_Xferred = Re_Xferred + 1 END DO END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vind_LL not allocated + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vind_CP not allocated Int_Xferred = Int_Xferred + 1 ELSE Int_Xferred = Int_Xferred + 1 @@ -5660,20 +5660,20 @@ SUBROUTINE FVW_UnPackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSt i2_l = IntKiBuf( Int_Xferred ) i2_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),STAT=ErrStat2) + IF (ALLOCATED(OutData%Vind_CP)) DEALLOCATE(OutData%Vind_CP) + ALLOCATE(OutData%Vind_CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vind_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vind_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i2 = LBOUND(OutData%Vind_LL,2), UBOUND(OutData%Vind_LL,2) - DO i1 = LBOUND(OutData%Vind_LL,1), UBOUND(OutData%Vind_LL,1) - OutData%Vind_LL(i1,i2) = ReKiBuf(Re_Xferred) + DO i2 = LBOUND(OutData%Vind_CP,2), UBOUND(OutData%Vind_CP,2) + DO i1 = LBOUND(OutData%Vind_CP,1), UBOUND(OutData%Vind_CP,1) + OutData%Vind_CP(i1,i2) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END DO END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vtot_LL not allocated + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vtot_CP not allocated Int_Xferred = Int_Xferred + 1 ELSE Int_Xferred = Int_Xferred + 1 @@ -5683,20 +5683,20 @@ SUBROUTINE FVW_UnPackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSt i2_l = IntKiBuf( Int_Xferred ) i2_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),STAT=ErrStat2) + IF (ALLOCATED(OutData%Vtot_CP)) DEALLOCATE(OutData%Vtot_CP) + ALLOCATE(OutData%Vtot_CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vtot_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vtot_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i2 = LBOUND(OutData%Vtot_LL,2), UBOUND(OutData%Vtot_LL,2) - DO i1 = LBOUND(OutData%Vtot_LL,1), UBOUND(OutData%Vtot_LL,1) - OutData%Vtot_LL(i1,i2) = ReKiBuf(Re_Xferred) + DO i2 = LBOUND(OutData%Vtot_CP,2), UBOUND(OutData%Vtot_CP,2) + DO i1 = LBOUND(OutData%Vtot_CP,1), UBOUND(OutData%Vtot_CP,1) + OutData%Vtot_CP(i1,i2) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END DO END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vstr_LL not allocated + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vstr_CP not allocated Int_Xferred = Int_Xferred + 1 ELSE Int_Xferred = Int_Xferred + 1 @@ -5706,20 +5706,20 @@ SUBROUTINE FVW_UnPackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSt i2_l = IntKiBuf( Int_Xferred ) i2_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),STAT=ErrStat2) + IF (ALLOCATED(OutData%Vstr_CP)) DEALLOCATE(OutData%Vstr_CP) + ALLOCATE(OutData%Vstr_CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vstr_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vstr_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i2 = LBOUND(OutData%Vstr_LL,2), UBOUND(OutData%Vstr_LL,2) - DO i1 = LBOUND(OutData%Vstr_LL,1), UBOUND(OutData%Vstr_LL,1) - OutData%Vstr_LL(i1,i2) = ReKiBuf(Re_Xferred) + DO i2 = LBOUND(OutData%Vstr_CP,2), UBOUND(OutData%Vstr_CP,2) + DO i1 = LBOUND(OutData%Vstr_CP,1), UBOUND(OutData%Vstr_CP,1) + OutData%Vstr_CP(i1,i2) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END DO END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vwnd_LL not allocated + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vwnd_CP not allocated Int_Xferred = Int_Xferred + 1 ELSE Int_Xferred = Int_Xferred + 1 @@ -5729,15 +5729,15 @@ SUBROUTINE FVW_UnPackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSt i2_l = IntKiBuf( Int_Xferred ) i2_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),STAT=ErrStat2) + IF (ALLOCATED(OutData%Vwnd_CP)) DEALLOCATE(OutData%Vwnd_CP) + ALLOCATE(OutData%Vwnd_CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vwnd_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vwnd_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i2 = LBOUND(OutData%Vwnd_LL,2), UBOUND(OutData%Vwnd_LL,2) - DO i1 = LBOUND(OutData%Vwnd_LL,1), UBOUND(OutData%Vwnd_LL,1) - OutData%Vwnd_LL(i1,i2) = ReKiBuf(Re_Xferred) + DO i2 = LBOUND(OutData%Vwnd_CP,2), UBOUND(OutData%Vwnd_CP,2) + DO i1 = LBOUND(OutData%Vwnd_CP,1), UBOUND(OutData%Vwnd_CP,1) + OutData%Vwnd_CP(i1,i2) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END DO diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 17b0496a1..a61f2996d 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -124,7 +124,7 @@ 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 ) - type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh + type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh at aerodynamic center 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 @@ -224,9 +224,9 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) ! --- Structural velocity on LL ! TODO: difference meshes in/LL do iW = 1,p%nWings - call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(1,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_LL(1,:)) - call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(2,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_LL(2,:)) - call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(3,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_LL(3,:)) + call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(1,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_CP(1,:)) + call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(2,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_CP(2,:)) + call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(3,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_CP(3,:)) enddo end subroutine Wings_Panelling @@ -278,8 +278,8 @@ subroutine Wings_ComputeCirculation(t, z, z_prev, u, p, x, m, AFInfo, ErrStat, E if (p%CirculationMethod==idCircPrescribed) then do iW = 1, p%nWings !Loop over lifting lines z%W(iW)%Gamma_LL(1:p%W(iW)%nSpan) = p%W(iW)%PrescribedCirculation(1:p%W(iW)%nSpan) - m%W(iW)%Vind_LL=-9999._ReKi !< Safety - m%W(iW)%Vtot_LL=-9999._ReKi !< Safety + m%W(iW)%Vind_CP=-9999._ReKi !< Safety + m%W(iW)%Vtot_CP=-9999._ReKi !< Safety enddo else if (p%CirculationMethod==idCircPolarData) then @@ -350,7 +350,7 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc if (m%FirstCall) then ! We find a guess by looking simply at the Wind and Elasticity velocity do iW=1,p%nWings - m%W(iW)%Vtot_ll = m%W(iW)%Vwnd_LL - m%W(iW)%Vstr_ll + m%W(iW)%Vtot_CP = m%W(iW)%Vwnd_CP - m%W(iW)%Vstr_CP enddo ! Input: Vtot_LL, output: GammaLastIter call CirculationFromPolarData(GammaLastIter, p, m, AFInfo,ErrStat2,ErrMsg2); if(Failed()) return; @@ -374,15 +374,15 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc call AllocAry(Vvar, 3, nCP_tot, 'Vvar', ErrStat2, ErrMsg2); if(Failed()) return; call AllocAry(Vcst, 3, nCP_tot, 'Vcst', ErrStat2, ErrMsg2); if(Failed()) return; - ! Set m%W(iW)%Vind_LL Induced velocity from Known wake only (after iNWStart+1) - ! Input: m%W%CP_LL, output: m%W%Vind_LL + ! Set m%W(iW)%Vind_CP Induced velocity from Known wake only (after iNWStart+1) + ! Input: m%W%CP_LL, output: m%W%Vind_CP call LiftingLineInducedVelocities(p, x, p%iNWStart+1, m, ErrStat2, ErrMsg2); if(Failed()) return; kCP=0 do iW=1,p%nWings do iSpan=1,p%W(iW)%nSpan kCP=kCP+1 - Vcst(1:3,kCP) = m%W(iW)%Vind_LL(1:3,iSpan) + m%W(iW)%Vwnd_LL(1:3,iSpan) - m%W(iW)%Vstr_ll(1:3,iSpan) + Vcst(1:3,kCP) = m%W(iW)%Vind_CP(1:3,iSpan) + m%W(iW)%Vwnd_CP(1:3,iSpan) - m%W(iW)%Vstr_CP(1:3,iSpan) enddo enddo @@ -392,10 +392,10 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc if (any(x%W(iW)%r_NW(1,:,1:m%nNW+1)<-999)) then ErrMsg='Wings_ComputeCirculationPolarData: Problem in input NW points'; ErrStat=ErrID_Fatal; return endif - if (any(m%W(iW)%Vind_LL(1:3,:)<-99)) then + if (any(m%W(iW)%Vind_CP(1:3,:)<-99)) then ErrMsg='Wings_ComputeCirculationPolarData: Problem in induced velocity on LL points'; ErrStat=ErrID_Fatal; return endif - if (any(m%W(iW)%Vwnd_LL(1:3,:)<-99)) then + if (any(m%W(iW)%Vwnd_CP(1:3,:)<-99)) then ErrMsg='Wings_ComputeCirculationPolarData: Problem in wind velocity on LL points'; ErrStat=ErrID_Fatal; return endif enddo @@ -436,7 +436,7 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc do iW=1,p%nWings do iSpan=1,p%W(iW)%nSpan kCP=kCP+1 - m%W(iW)%Vtot_LL(:,iSpan) = Vcst(:,kCP) + Vvar(:,kCP) + m%W(iW)%Vtot_CP(:,iSpan) = Vcst(:,kCP) + Vvar(:,kCP) enddo enddo ! --- Computing circulation based on Vtot_LL @@ -479,17 +479,17 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc !call Output_Gamma(m%CP_ll(1:3,:), Gamma_LL(:), iW, m%iStep, iLabel, iIter) !call print_mean_3d( m%Vwnd_LL(:,:,:), 'Mean wind vel. LL (cst)') !call print_mean_3d( m%Vstr_LL(:,:,:), 'Mean struct vel. LL (cst)') - !call print_mean_3d( m%W(iW)%Vind_LL(:,:,:), 'Mean induced vel. LL (cst)') + !call print_mean_3d( m%W(iW)%Vind_CP(:,:,:), 'Mean induced vel. LL (cst)') !call print_mean_3d( Vvar(:,:,:) , 'Mean induced vel. LL (var)') - !call print_mean_3d( Vvar+m%W(iW)%Vind_LL(:,:,:), 'Mean induced vel. LL (tot)') + !call print_mean_3d( Vvar+m%W(iW)%Vind_CP(:,:,:), 'Mean induced vel. LL (tot)') !call print_mean_3d( m%Vtot_LL(:,:,:), 'Mean relativevel. LL (tot)') - !print*,'m%W(iW)%Vind_LL',m%Vind_LL(1,:,:) + !print*,'m%W(iW)%Vind_CP',m%Vind_CP(1,:,:) !print*,'m%Vwnd_LL',m%Vwnd_LL(1,:,:) !print*,'m%Vcst_LL',Vcst(1,:,:) !print*,'Gamm: ',Gamma_LL(1, 1), Gamma_LL(p%W(iW)%nSpan,1) do iW=1,size(p%W) - m%W(iW)%Vind_LL=-9999._ReKi !< Safety (the induction above was not the true one) - m%W(iW)%Vtot_LL=-9999._ReKi !< Safety + m%W(iW)%Vind_CP=-9999._ReKi !< Safety (the induction above was not the true one) + m%W(iW)%Vtot_CP=-9999._ReKi !< Safety enddo call CleanUp() contains @@ -540,7 +540,7 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) ! Aliases to shorten notations N = m%W(iW)%Norm(1:3, icp) Tc = m%W(iW)%Tang(1:3, icp) - Vrel = m%W(iW)%Vtot_LL(1:3,icp) + Vrel = m%W(iW)%Vtot_CP(1:3,icp) ! "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 = TwoNorm(Vrel_orth(1:3)) From 5a5444827f4a4476b1c2119fa9033e3be805d08e Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 25 Jun 2021 22:32:28 -0600 Subject: [PATCH 11/24] FVW: option for induction at LL nodes instead of CP --- modules/aerodyn/src/FVW.f90 | 32 ++++++-------- modules/aerodyn/src/FVW_Registry.txt | 8 ++-- modules/aerodyn/src/FVW_Subs.f90 | 52 ++++++++++++++++++---- modules/aerodyn/src/FVW_Types.f90 | 66 ++++++++++++++++++++++++++++ modules/aerodyn/src/FVW_Wings.f90 | 5 ++- 5 files changed, 131 insertions(+), 32 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 81fcbed51..9495b2a55 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -222,6 +222,10 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%W(iW)%Vwnd_CP , 3 , p%W(iW)%nSpan, 'Wind on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vwnd_CP= -999999_ReKi; ! Variables at panels points call AllocAry( m%W(iW)%r_LL , 3 , p%W(iW)%nSpan+1 , 2 , 'Lifting Line Panels', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%r_LL= -999999_ReKi; + call AllocAry( m%W(iW)%Vind_LL , 3 , p%W(iW)%nSpan+1, 'Vind on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vind_LL= -999999_ReKi; + !call AllocAry( m%W(iW)%Vtot_LL , 3 , p%W(iW)%nSpan+1, 'Vtot on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vtot_LL= -999999_ReKi; + !call AllocAry( m%W(iW)%Vstr_LL , 3 , p%W(iW)%nSpan+1, 'Vstr on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vstr_LL= -999999_ReKi; + !call AllocAry( m%W(iW)%Vwnd_LL , 3 , p%W(iW)%nSpan+1, 'Wind on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vwnd_LL= -999999_ReKi; call AllocAry( m%W(iW)%Vwnd_NW , 3 , p%W(iW)%nSpan+1 ,p%nNWMax+1, 'Wind on NW ', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vwnd_NW= -999_ReKi; call AllocAry( m%W(iW)%Vwnd_FW , 3 , FWnSpan+1 ,p%nFWMax+1, 'Wind on FW ', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vwnd_FW= -999_ReKi; call AllocAry( m%W(iW)%Vind_NW , 3 , p%W(iW)%nSpan+1 ,p%nNWMax+1, 'Vind on NW ', ErrStat2, ErrMsg2 );call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vind_NW= -999_ReKi; @@ -1372,9 +1376,8 @@ subroutine CalcOutputForAD(t, u, p, x, y, m, AFInfo, ErrStat, ErrMsg) ! ! TODO ANDY: replace with direct call to inflow wind at m%W(iW)%CP_LL location ! CALL DistributeRequestedWind_LL(u%V_wind, p, m%Vwnd_LL) ! -! ! Control points location and structrual velocity - call Wings_Panelling(u%WingsMesh, p, m, ErrStat2, ErrMsg2); - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +! ! Control points location and structural velocity + call Wings_Panelling(u%WingsMesh, p, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! ! ! if we are on a correction step, CalcOutput may be called again with different inputs ! ! Compute m%W(iW)%Gamma_LL @@ -1382,32 +1385,25 @@ subroutine CalcOutputForAD(t, u, p, x, y, m, AFInfo, ErrStat, ErrMsg) !--- ! Induction on the lifting line control point - ! Input: m%W%CP_LL, Output:m%W%Vind_CP - do iW=1,p%nWings - m%W(iW)%Vind_CP=-9999.0_ReKi - enddo - call LiftingLineInducedVelocities(p, x, 1, m, ErrStat2, ErrMsg2); - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + ! Input: m%W%CP_LL or m%W%r_LL, Output:m%W%Vind_CP and potentially m%W%Vind_LL + call LiftingLineInducedVelocities(p, x, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Induction on the mesh points (AeroDyn nodes): y%W(iW)%Vind if (p%InductionAtCP) then ! We use the induction at the CP (Vind_LL) to interpextrap at the node do iW=1,p%nWings - y%W(iW)%Vind(1:3,:) = 0.0_ReKi ! --- Linear interpolation for interior points and extrapolations at boundaries call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(1,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(1,:)) call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(2,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(2,:)) call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(3,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(3,:)) enddo else - print*,'>>>> TODO' - STOP - ! The induction was computed at the nodes - do iW=1,p%nWings - ! y%W(iW)%Vind(1,:) = m%W(iW)%Vind_LL(1,:) - ! y%W(iW)%Vind(2,:) = m%W(iW)%Vind_LL(2,:) - ! y%W(iW)%Vind(3,:) = m%W(iW)%Vind_LL(3,:) - enddo + ! The induction was computed at the LL nodes, we transfer it directly + do iW=1,p%nWings + y%W(iW)%Vind(1,:) = m%W(iW)%Vind_LL(1,:) + y%W(iW)%Vind(2,:) = m%W(iW)%Vind_LL(2,:) + y%W(iW)%Vind(3,:) = m%W(iW)%Vind_LL(3,:) + enddo endif end subroutine CalcOutputForAD !---------------------------------------------------------------------------------------------------------------------------------- diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 7559f515f..7c54788ba 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -150,10 +150,10 @@ typedef ^ ^ UA_MiscVarType typedef ^ ^ UA_OutputType y_UA - - - "outputs from UnsteadyAero" - typedef ^ ^ UA_ParameterType p_UA - - - "parameters for UnsteadyAero" - # for calculating outputs at blade nodes -#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 Vind_LL :: - - "Induced velocity on lifting line nodes" m/s +#typedef ^ ^ ReKi Vtot_LL :: - - "Total velocity on lifting line nodes" m/s +#typedef ^ ^ ReKi Vstr_LL :: - - "Structural velocity on LL nodes" m/s +#typedef ^ ^ ReKi Vwnd_LL :: - - "Wind on lifting line nodes" m/s typedef ^ ^ ReKi BN_AxInd : - - "Axial induction [size (NumBlNds,numBlades)]" - typedef ^ ^ ReKi BN_TanInd : - - "Tangential induction [size (NumBlNds,numBlades)]" - typedef ^ ^ ReKi BN_Vrel : - - "Relative velocity [size (NumBlNds,numBlades)]" m/s diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 37bf77993..ff5084b2e 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -608,6 +608,13 @@ subroutine DistributeRequestedWind_LL(V_wind, p, m) iP_end = iP_start-1 + p%W(iW)%nSpan m%W(iW)%Vwnd_CP(1:3,1:p%W(iW)%nSpan) = V_wind(1:3,iP_start:iP_end) enddo + + ! TODO TODO LL NODES + !print*,'TODO transfer of Wind at LL' + !do iW=1,p%nWings + ! m%W(iW)%Vwnd_LL(1:3,1:p%W(iW)%nSpan) = m%W(iW)%Vwnd_LL(1:3,1:p%W(iW)%nSpan) + ! m%W(iW)%Vwnd_LL(1:3,p%W(iW)%nSpan+1) = m%W(iW)%Vwnd_LL(1:3,p%W(iW)%nSpan) ! Last point copy... + !enddo end subroutine DistributeRequestedWind_LL !> Distribute wind onto NW and FW @@ -1238,6 +1245,7 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) ErrMsg = "" do iW=1,p%nWings m%W(iW)%Vind_CP = -9999._ReKi !< Safety + m%W(iW)%Vind_LL = -9999._ReKi !< Safety enddo bMirror = p%ShearModel==idShearMirror ! Whether or not we mirror the vorticity wrt ground @@ -1249,15 +1257,22 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) nCPs=0 do iW=1,p%nWings m%W(iW)%Vind_CP = 0.0_ReKi !< Safety + m%W(iW)%Vind_LL = 0.0_ReKi !< Safety enddo if (DEV_VERSION) then print'(A,I0,A,I0,A,I0,A)','Induction - nSeg:',nSeg,' - nSegP:',nSegP, ' - nCPs:',nCPs, ' -> No induction' endif else nCPs=0 - do iW=1,p%nWings - nCPs = nCPs + p%W(iW)%nSpan - enddo + if (p%InductionAtCP) then + do iW=1,p%nWings + nCPs = nCPs + p%W(iW)%nSpan + enddo + else + do iW=1,p%nWings + nCPs = nCPs + p%W(iW)%nSpan+1 + enddo + endif 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 @@ -1276,9 +1291,15 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) !> Pack all the control points subroutine PackLiftingLinePoints() iHeadP=1 - do iW=1,p%nWings - CALL LatticeToPoints2D(m%W(iW)%CP_LL(1:3,:), CPs, iHeadP) - enddo + if (p%InductionAtCP) then + do iW=1,p%nWings + call LatticeToPoints2D(m%W(iW)%CP_LL(1:3,:), CPs, iHeadP) + enddo + else + do iW=1,p%nWings + call LatticeToPoints2D(m%W(iW)%r_LL(1:3,:,1), CPs, iHeadP) + enddo + endif if (DEV_VERSION) then if ((iHeadP-1)/=size(CPs,2)) then print*,'PackLLPoints: Number of points wrongly estimated',size(CPs,2), iHeadP-1 @@ -1290,10 +1311,23 @@ subroutine PackLiftingLinePoints() !> Distribute the induced velocity to the proper location subroutine UnPackLiftingLineVelocities() + integer :: iSpan iHeadP=1 - do iW=1,p%nWings - CALL VecToLattice2D(Uind, m%W(iW)%Vind_CP(1:3,:), iHeadP) - enddo + if (p%InductionAtCP) then + do iW=1,p%nWings + call VecToLattice2D(Uind, m%W(iW)%Vind_CP(1:3,:), iHeadP) + enddo + else + do iW=1,p%nWings + call VecToLattice2D(Uind, m%W(iW)%Vind_LL(1:3,:), iHeadP) + enddo + ! Transfer mean at CP + do iW=1,p%nWings + do iSpan=1,p%W(iW)%nSpan + m%W(iW)%Vind_CP(1:3,iSpan)= (m%W(iW)%Vind_LL(1:3,iSpan)+m%W(iW)%Vind_LL(1:3,iSpan+1))*0.5_ReKi + enddo + enddo + endif if (DEV_VERSION) then if ((iHeadP-1)/=size(Uind,2)) then print*,'UnPackLiftingLineVelocities: Number of points wrongly estimated',size(Uind,2), iHeadP-1 diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 0bd4692a7..2ad383fee 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -183,6 +183,7 @@ MODULE FVW_Types 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 [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Vind_LL !< Induced velocity on lifting line nodes [m/s] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: BN_AxInd !< Axial induction [size (NumBlNds,numBlades)] [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: BN_TanInd !< Tangential induction [size (NumBlNds,numBlades)] [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: BN_Vrel !< Relative velocity [size (NumBlNds,numBlades)] [m/s] @@ -3975,6 +3976,20 @@ SUBROUTINE FVW_CopyWng_MiscVarType( SrcWng_MiscVarTypeData, DstWng_MiscVarTypeDa CALL UA_CopyParam( SrcWng_MiscVarTypeData%p_UA, DstWng_MiscVarTypeData%p_UA, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN +IF (ALLOCATED(SrcWng_MiscVarTypeData%Vind_LL)) THEN + i1_l = LBOUND(SrcWng_MiscVarTypeData%Vind_LL,1) + i1_u = UBOUND(SrcWng_MiscVarTypeData%Vind_LL,1) + i2_l = LBOUND(SrcWng_MiscVarTypeData%Vind_LL,2) + i2_u = UBOUND(SrcWng_MiscVarTypeData%Vind_LL,2) + IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%Vind_LL)) THEN + ALLOCATE(DstWng_MiscVarTypeData%Vind_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%Vind_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstWng_MiscVarTypeData%Vind_LL = SrcWng_MiscVarTypeData%Vind_LL +ENDIF IF (ALLOCATED(SrcWng_MiscVarTypeData%BN_AxInd)) THEN i1_l = LBOUND(SrcWng_MiscVarTypeData%BN_AxInd,1) i1_u = UBOUND(SrcWng_MiscVarTypeData%BN_AxInd,1) @@ -4242,6 +4257,9 @@ SUBROUTINE FVW_DestroyWng_MiscVarType( Wng_MiscVarTypeData, ErrStat, ErrMsg ) CALL UA_DestroyMisc( Wng_MiscVarTypeData%m_UA, ErrStat, ErrMsg ) CALL UA_DestroyOutput( Wng_MiscVarTypeData%y_UA, ErrStat, ErrMsg ) CALL UA_DestroyParam( Wng_MiscVarTypeData%p_UA, ErrStat, ErrMsg ) +IF (ALLOCATED(Wng_MiscVarTypeData%Vind_LL)) THEN + DEALLOCATE(Wng_MiscVarTypeData%Vind_LL) +ENDIF IF (ALLOCATED(Wng_MiscVarTypeData%BN_AxInd)) THEN DEALLOCATE(Wng_MiscVarTypeData%BN_AxInd) ENDIF @@ -4508,6 +4526,11 @@ SUBROUTINE FVW_PackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Int_BufSz = Int_BufSz + SIZE( Int_Buf ) DEALLOCATE(Int_Buf) END IF + Int_BufSz = Int_BufSz + 1 ! Vind_LL allocated yes/no + IF ( ALLOCATED(InData%Vind_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! 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 ! BN_AxInd allocated yes/no IF ( ALLOCATED(InData%BN_AxInd) ) THEN Int_BufSz = Int_BufSz + 2*1 ! BN_AxInd upper/lower bounds for each dimension @@ -5164,6 +5187,26 @@ SUBROUTINE FVW_PackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ELSE IntKiBuf( Int_Xferred ) = 0; Int_Xferred = Int_Xferred + 1 ENDIF + 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 + + DO i2 = LBOUND(InData%Vind_LL,2), UBOUND(InData%Vind_LL,2) + DO i1 = LBOUND(InData%Vind_LL,1), UBOUND(InData%Vind_LL,1) + ReKiBuf(Re_Xferred) = InData%Vind_LL(i1,i2) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF IF ( .NOT. ALLOCATED(InData%BN_AxInd) ) THEN IntKiBuf( Int_Xferred ) = 0 Int_Xferred = Int_Xferred + 1 @@ -6093,6 +6136,29 @@ SUBROUTINE FVW_UnPackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSt 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 ! 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 + IF (ALLOCATED(OutData%Vind_LL)) DEALLOCATE(OutData%Vind_LL) + ALLOCATE(OutData%Vind_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vind_LL.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + DO i2 = LBOUND(OutData%Vind_LL,2), UBOUND(OutData%Vind_LL,2) + DO i1 = LBOUND(OutData%Vind_LL,1), UBOUND(OutData%Vind_LL,1) + OutData%Vind_LL(i1,i2) = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 + END DO + END DO + END IF IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! BN_AxInd 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 a61f2996d..6684a0b7c 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -221,13 +221,16 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) call InterpArray(p%W(iW)%s_LL(:), m%W(iW)%r_LL(3,:,1), p%W(iW)%s_CP_LL(:), m%W(iW)%CP_LL(3,:)) enddo - ! --- Structural velocity on LL + ! --- Structural velocity on LL CP and Nodes ! TODO: difference meshes in/LL do iW = 1,p%nWings call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(1,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_CP(1,:)) call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(2,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_CP(2,:)) call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(3,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_CP(3,:)) enddo + !do iW = 1,p%nWings + ! m%W(iW)%Vstr_LL(1:3,:)= Meshes(iW)%TranslationVel(1:3,:) + !enddo end subroutine Wings_Panelling !---------------------------------------------------------------------------------------------------------------------------------- From 59c2867ac78dff7c3bc5806cffa006017af37bcc Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 1 Jul 2021 11:56:19 -0600 Subject: [PATCH 12/24] FVW: removed time spent calculation --- modules/aerodyn/src/FVW.f90 | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 9495b2a55..6c9ee8d07 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -195,7 +195,6 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) m%VTKStep = -1 ! Counter of VTK outputs m%VTKlastTime = -HUGE(1.0_DbKi) m%OldWakeTime = -HUGE(1.0_DbKi) - m%tSpent = 0 allocate(m%W(p%nWings)) allocate(m%dxdt%W(p%nWings)) @@ -615,7 +614,6 @@ 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) :: nP, nFWEff, iW - integer, dimension(8) :: time1, time2, time_diff real(ReKi) :: ShedScale !< Scaling factor for shed vorticity (for sub-cycling), 1 if no subcycling logical :: bReevaluation logical :: bOverCycling @@ -643,9 +641,6 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m 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=0 do iW=1,p%nWings @@ -653,7 +648,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m enddo nFWEff = min(m%nFW, p%nFWFree) ! --- Display some status to screen - 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,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, 's Comp:',m%ComputeWakeInduced ! --- Evaluation at t ! Inputs at t @@ -780,13 +775,6 @@ 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 CleanUp() if (DEV_VERSION) then From 3ca7240978b89097dfe34dae001e34d3b6f528e2 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 1 Jul 2021 16:56:48 -0600 Subject: [PATCH 13/24] FVW: LL ui at CP and nodes --- modules/aerodyn/src/AeroDyn.f90 | 2 +- modules/aerodyn/src/FVW.f90 | 80 ++++++++------------------ modules/aerodyn/src/FVW_Registry.txt | 2 +- modules/aerodyn/src/FVW_Subs.f90 | 17 ++++-- modules/aerodyn/src/FVW_Types.f90 | 86 ++++++++++++++-------------- modules/aerodyn/src/FVW_Wings.f90 | 9 +-- 6 files changed, 87 insertions(+), 109 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 91eebfc21..bcbd7d363 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -2016,7 +2016,7 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) call SetDisturbedInflow(p%rotors(iR), u(tIndx)%rotors(iR), m%rotors(iR), errStat, errMsg) do k=1,p%rotors(iR)%NumBlades iW=p%FVW%Bld2Wings(iR,k) - m%FVW_u(tIndx)%W(iW)%Vwnd_LLMP(1:3,:) = m%rotors(iR)%DisturbedInflow(1:3,:,k) + m%FVW_u(tIndx)%W(iW)%Vwnd_LL(1:3,:) = m%rotors(iR)%DisturbedInflow(1:3,:,k) enddo enddo enddo diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index 6c9ee8d07..b7094077d 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -398,9 +398,9 @@ subroutine FVW_Init_U_Y( p, u, y, m, ErrStat, ErrMsg ) do iW=1,p%nWings call AllocAry( y%W(iW)%Vind , 3, p%W(iW)%nSpan+1, 'Induced velocity vector', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName) call AllocAry( u%W(iW)%omega_z, p%W(iW)%nSpan+1, 'Section torsion rate' , ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName) - call AllocAry( u%W(iW)%Vwnd_LLMP,3, p%W(iW)%nSpan+1, 'Dist. wind at LL nodes', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName) + call AllocAry( u%W(iW)%Vwnd_LL, 3, p%W(iW)%nSpan+1, 'Dist. wind at LL nodes', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName) y%W(iW)%Vind = -9999.9_ReKi - u%W(iW)%Vwnd_LLMP = -9999.9_ReKi + u%W(iW)%Vwnd_LL = -9999.9_ReKi u%W(iW)%omega_z = -9999.9_ReKi enddo ! Rotors, contain hub info @@ -594,7 +594,6 @@ 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, 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,... type(FVW_InputType), intent(inout) :: u(:) !< Inputs at utimes (out only for mesh record-keeping in ExtrapInterp routine) @@ -658,8 +657,6 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call Map_LL_NW(p, m, z, x, 1.0_ReKi, ErrStat2, ErrMsg2); if(Failed()) return ! needed at t=0 if wing moved after init call Map_NW_FW(p, m, z, x, ErrStat2, ErrMsg2); if(Failed()) return - ! TODO convert quasi steady Gamma to unsteady gamma with UA states - ! Compute UA inputs at t if (m%UA_Flag) then call CalculateInputsAndOtherStatesForUA(1, uInterp, p, x, xd, z, OtherState, AFInfo, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -1370,29 +1367,17 @@ subroutine CalcOutputForAD(t, u, p, x, y, m, AFInfo, ErrStat, ErrMsg) ! ! if we are on a correction step, CalcOutput may be called again with different inputs ! ! Compute m%W(iW)%Gamma_LL ! CALL Wings_ComputeCirculation(t, m%W(iW)%Gamma_LL, z%W(iW)%Gamma_LL, u, p, x, m, AFInfo, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only - !--- - ! Induction on the lifting line control point - ! Input: m%W%CP_LL or m%W%r_LL, Output:m%W%Vind_CP and potentially m%W%Vind_LL - call LiftingLineInducedVelocities(p, x, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - - ! Induction on the mesh points (AeroDyn nodes): y%W(iW)%Vind - if (p%InductionAtCP) then - ! We use the induction at the CP (Vind_LL) to interpextrap at the node - do iW=1,p%nWings - ! --- Linear interpolation for interior points and extrapolations at boundaries - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(1,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(1,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(2,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(2,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(3,:), p%W(iW)%s_LL(:), y%W(iW)%Vind(3,:)) - enddo - else - ! The induction was computed at the LL nodes, we transfer it directly - do iW=1,p%nWings - y%W(iW)%Vind(1,:) = m%W(iW)%Vind_LL(1,:) - y%W(iW)%Vind(2,:) = m%W(iW)%Vind_LL(2,:) - y%W(iW)%Vind(3,:) = m%W(iW)%Vind_LL(3,:) - enddo - endif + !--- Induction on the lifting line control point + ! if InductionAtCP : In: m%W%CP_LL, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) + ! if not InductionAtCP : In: m%W%r_LL, Out:m%W%Vind_CP (interp/extrap) and m%W%Vind_LL + call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + ! Transfer to output + do iW=1,p%nWings + y%W(iW)%Vind(1,:) = m%W(iW)%Vind_LL(1,:) + y%W(iW)%Vind(2,:) = m%W(iW)%Vind_LL(2,:) + y%W(iW)%Vind(3,:) = m%W(iW)%Vind_LL(3,:) + enddo end subroutine CalcOutputForAD !---------------------------------------------------------------------------------------------------------------------------------- !> Routine for computing outputs, used in both loose and tight coupling. @@ -1608,7 +1593,6 @@ subroutine CalculateInputsAndOtherStatesForUA(InputIndex, u, p, x, xd, z, OtherS 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 :: Vind_node type(UA_InputType), pointer :: u_UA ! Alias to shorten notations integer(IntKi) :: i,iW character(ErrMsgLen) :: errMsg2 ! temporary Error message if ErrStat /= ErrID_None @@ -1617,46 +1601,32 @@ subroutine CalculateInputsAndOtherStatesForUA(InputIndex, u, p, x, xd, z, OtherS ErrMsg = "" ! --- Induction on the lifting line control points - ! NOTE: this is expensive since it's an output for FVW but here we have to use it for UA - ! Set m%W(iW)%Vind_LL - - do iW = 1,p%nWings - m%W(iW)%Vind_CP=-9999.0_ReKi - ! Input: m%W%CP_LL, Output:m%W%Vind_CP - 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%W(iW)%nSpan+1)) - - ! Induced velocity at Nodes (NOTE: we rely on storage done when computing Circulation) - if (m%nNW>1) then - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(1,:), p%W(iW)%s_LL(:), Vind_node(1,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(2,:), p%W(iW)%s_LL(:), Vind_node(2,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(3,:), p%W(iW)%s_LL(:), Vind_node(3,:)) - else - Vind_node=0.0_ReKi + ! if InductionAtCP : In: m%W%CP_LL, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) + ! if not InductionAtCP : In: m%W%r_LL, Out:m%W%Vind_CP (interp/extrap) and m%W%Vind_LL + call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'Ca;lcultateInputsAndOtherStatesForUA'); if (ErrStat >= AbortErrLev) return + if (p%InductionAtCP) then + if (m%nNW<=1) then + do iW = 1,p%nWings + m%W(iW)%Vind_LL(1,:)=0.0_ReKi + enddo endif + endif + ! --- UA inputs + do iW = 1,p%nWings do i = 1,p%W(iW)%nSpan+1 ! We only update the UnsteadyAero states if we have unsteady aero turned on for this node u_UA => m%W(iW)%u_UA(i,InputIndex) ! Alias !! ....... compute inputs to UA ........... - ! NOTE: To be consistent with CalcOutput we take Vwind_ND that was set using m%DisturbedInflow from AeroDyn.. + ! NOTE: To be consistent with CalcOutput we take Vwind_LL 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(iW)%Orientation(1:3,1:3,i), u%WingsMesh(iW)%TranslationVel(1:3,i), Vind_node(1:3,i), u%W(iW)%Vwnd_LLMP(1:3,i), & + call AlphaVrel_Generic(u%WingsMesh(iW)%Orientation(1:3,1:3,i), u%WingsMesh(iW)%TranslationVel(1:3,i), m%W(iW)%Vind_LL(1:3,i), u%W(iW)%Vwnd_LL(1:3,i), & p%KinVisc, p%W(iW)%Chord(i), u_UA%U, u_UA%alpha, u_UA%Re) u_UA%v_ac(1) = sin(u_UA%alpha)*u_UA%U u_UA%v_ac(2) = cos(u_UA%alpha)*u_UA%U u_UA%omega = u%W(iW)%omega_z(i) u_UA%UserProp = 0 ! u1%UserProp(i,j) ! TODO end do ! i nSpan - deallocate(Vind_node) end do ! iW nWings - -contains - function NodeText(i,j) - integer(IntKi), intent(in) :: i ! node number - integer(IntKi), intent(in) :: j ! blade number - character(25) :: NodeText - NodeText = '(nd:'//trim(num2lstr(i))//' bld:'//trim(num2lstr(j))//')' - end function NodeText end subroutine CalculateInputsAndOtherStatesForUA diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 7c54788ba..6992688fe 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -202,7 +202,7 @@ typedef ^ ^ GridOutType typedef FVW/FVW Rot_InputType ReKi HubOrientation {3}{3} - - "Orientation of hub coordinate system (for output only)" - typedef ^ ^ ReKi HubPosition {3} - - "Origin of hub (for output only)" - # Wings -typedef FVW/FVW Wng_InputType ReKi Vwnd_LLMP {:}{:} - - "Disturbed wind at LL mesh points (not CP), for UA only" - +typedef FVW/FVW Wng_InputType ReKi Vwnd_LL {:}{:} - - "Disturbed wind at LL mesh points (not CP), for UA only" - typedef ^ ^ ReKi omega_z {:} - - "rotation of no-sweep-pitch-twist coordinate system around z (for CDBEMT and CUA)" "rad/s" # FVW_InputType diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index ff5084b2e..00bafaf7b 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -1227,10 +1227,11 @@ end subroutine WakeInducedVelocities !> Compute induced velocities from all vortex elements onto the lifting line control points !! In : x%W(iW)%r_NW, x%W(iW)%r_FW, x%W(iW)%Gamma_NW, x%W(iW)%Gamma_FW !! Out: m%W(iW)%Vind_CP -subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) +subroutine LiftingLineInducedVelocities(p, x, InductionAtCP, iDepthStart, m, ErrStat, ErrMsg) !real(ReKi), dimension(:,:,:), intent(in ) :: CP_LL !< Control points where velocity is to be evaluated type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< States + logical, intent(in ) :: InductionAtCP !< Compute induction at CP or on LL nodes integer(IntKi), intent(in ) :: iDepthStart !< Index where we start packing for NW panels type(FVW_MiscVarType), intent(inout) :: m !< Initial misc/optimization variables !real(ReKi), dimension(:,:,:), intent( out) :: Vind_CP !< Control points where velocity is to be evaluated @@ -1264,7 +1265,7 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) endif else nCPs=0 - if (p%InductionAtCP) then + if (InductionAtCP) then do iW=1,p%nWings nCPs = nCPs + p%W(iW)%nSpan enddo @@ -1291,7 +1292,7 @@ subroutine LiftingLineInducedVelocities(p, x, iDepthStart, m, ErrStat, ErrMsg) !> Pack all the control points subroutine PackLiftingLinePoints() iHeadP=1 - if (p%InductionAtCP) then + if (InductionAtCP) then do iW=1,p%nWings call LatticeToPoints2D(m%W(iW)%CP_LL(1:3,:), CPs, iHeadP) enddo @@ -1313,15 +1314,21 @@ subroutine PackLiftingLinePoints() subroutine UnPackLiftingLineVelocities() integer :: iSpan iHeadP=1 - if (p%InductionAtCP) then + if (InductionAtCP) then do iW=1,p%nWings call VecToLattice2D(Uind, m%W(iW)%Vind_CP(1:3,:), iHeadP) enddo + ! --- Transfer CP to LL (Linear interpolation for interior points and extrapolations at boundaries) + do iW=1,p%nWings + call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(1,:), p%W(iW)%s_LL(:), m%W(iW)%Vind_LL(1,:)) + call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(2,:), p%W(iW)%s_LL(:), m%W(iW)%Vind_LL(2,:)) + call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(3,:), p%W(iW)%s_LL(:), m%W(iW)%Vind_LL(3,:)) + enddo else do iW=1,p%nWings call VecToLattice2D(Uind, m%W(iW)%Vind_LL(1:3,:), iHeadP) enddo - ! Transfer mean at CP + ! --- Transfer LL to CP. TODO instead of mean should use weigthed average based on distance to nodes do iW=1,p%nWings do iSpan=1,p%W(iW)%nSpan m%W(iW)%Vind_CP(1:3,iSpan)= (m%W(iW)%Vind_LL(1:3,iSpan)+m%W(iW)%Vind_LL(1:3,iSpan+1))*0.5_ReKi diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 2ad383fee..c0ec6c1ba 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -234,7 +234,7 @@ MODULE FVW_Types ! ======================= ! ========= Wng_InputType ======= TYPE, PUBLIC :: Wng_InputType - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Vwnd_LLMP !< Disturbed wind at LL mesh points (not CP), for UA only [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: Vwnd_LL !< Disturbed wind at LL mesh points (not CP), for UA only [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: omega_z !< rotation of no-sweep-pitch-twist coordinate system around z (for CDBEMT and CUA) [rad/s] END TYPE Wng_InputType ! ======================= @@ -7636,19 +7636,19 @@ SUBROUTINE FVW_CopyWng_InputType( SrcWng_InputTypeData, DstWng_InputTypeData, Ct ! ErrStat = ErrID_None ErrMsg = "" -IF (ALLOCATED(SrcWng_InputTypeData%Vwnd_LLMP)) THEN - i1_l = LBOUND(SrcWng_InputTypeData%Vwnd_LLMP,1) - i1_u = UBOUND(SrcWng_InputTypeData%Vwnd_LLMP,1) - i2_l = LBOUND(SrcWng_InputTypeData%Vwnd_LLMP,2) - i2_u = UBOUND(SrcWng_InputTypeData%Vwnd_LLMP,2) - IF (.NOT. ALLOCATED(DstWng_InputTypeData%Vwnd_LLMP)) THEN - ALLOCATE(DstWng_InputTypeData%Vwnd_LLMP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_InputTypeData%Vwnd_LLMP.', ErrStat, ErrMsg,RoutineName) +IF (ALLOCATED(SrcWng_InputTypeData%Vwnd_LL)) THEN + i1_l = LBOUND(SrcWng_InputTypeData%Vwnd_LL,1) + i1_u = UBOUND(SrcWng_InputTypeData%Vwnd_LL,1) + i2_l = LBOUND(SrcWng_InputTypeData%Vwnd_LL,2) + i2_u = UBOUND(SrcWng_InputTypeData%Vwnd_LL,2) + IF (.NOT. ALLOCATED(DstWng_InputTypeData%Vwnd_LL)) THEN + ALLOCATE(DstWng_InputTypeData%Vwnd_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_InputTypeData%Vwnd_LL.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstWng_InputTypeData%Vwnd_LLMP = SrcWng_InputTypeData%Vwnd_LLMP + DstWng_InputTypeData%Vwnd_LL = SrcWng_InputTypeData%Vwnd_LL ENDIF IF (ALLOCATED(SrcWng_InputTypeData%omega_z)) THEN i1_l = LBOUND(SrcWng_InputTypeData%omega_z,1) @@ -7673,8 +7673,8 @@ SUBROUTINE FVW_DestroyWng_InputType( Wng_InputTypeData, ErrStat, ErrMsg ) ! ErrStat = ErrID_None ErrMsg = "" -IF (ALLOCATED(Wng_InputTypeData%Vwnd_LLMP)) THEN - DEALLOCATE(Wng_InputTypeData%Vwnd_LLMP) +IF (ALLOCATED(Wng_InputTypeData%Vwnd_LL)) THEN + DEALLOCATE(Wng_InputTypeData%Vwnd_LL) ENDIF IF (ALLOCATED(Wng_InputTypeData%omega_z)) THEN DEALLOCATE(Wng_InputTypeData%omega_z) @@ -7716,10 +7716,10 @@ SUBROUTINE FVW_PackWng_InputType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, E Re_BufSz = 0 Db_BufSz = 0 Int_BufSz = 0 - Int_BufSz = Int_BufSz + 1 ! Vwnd_LLMP allocated yes/no - IF ( ALLOCATED(InData%Vwnd_LLMP) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! Vwnd_LLMP upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%Vwnd_LLMP) ! Vwnd_LLMP + Int_BufSz = Int_BufSz + 1 ! Vwnd_LL allocated yes/no + IF ( ALLOCATED(InData%Vwnd_LL) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! 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 ! omega_z allocated yes/no IF ( ALLOCATED(InData%omega_z) ) THEN @@ -7753,22 +7753,22 @@ SUBROUTINE FVW_PackWng_InputType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, E Db_Xferred = 1 Int_Xferred = 1 - IF ( .NOT. ALLOCATED(InData%Vwnd_LLMP) ) THEN + 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_LLMP,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_LLMP,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_LLMP,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_LLMP,2) + IntKiBuf( Int_Xferred ) = LBOUND(InData%Vwnd_LL,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%Vwnd_LL,2) Int_Xferred = Int_Xferred + 2 - DO i2 = LBOUND(InData%Vwnd_LLMP,2), UBOUND(InData%Vwnd_LLMP,2) - DO i1 = LBOUND(InData%Vwnd_LLMP,1), UBOUND(InData%Vwnd_LLMP,1) - ReKiBuf(Re_Xferred) = InData%Vwnd_LLMP(i1,i2) + DO i2 = LBOUND(InData%Vwnd_LL,2), UBOUND(InData%Vwnd_LL,2) + DO i1 = LBOUND(InData%Vwnd_LL,1), UBOUND(InData%Vwnd_LL,1) + ReKiBuf(Re_Xferred) = InData%Vwnd_LL(i1,i2) Re_Xferred = Re_Xferred + 1 END DO END DO @@ -7818,7 +7818,7 @@ SUBROUTINE FVW_UnPackWng_InputType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat Re_Xferred = 1 Db_Xferred = 1 Int_Xferred = 1 - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vwnd_LLMP not allocated + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Vwnd_LL not allocated Int_Xferred = Int_Xferred + 1 ELSE Int_Xferred = Int_Xferred + 1 @@ -7828,15 +7828,15 @@ SUBROUTINE FVW_UnPackWng_InputType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat i2_l = IntKiBuf( Int_Xferred ) i2_u = IntKiBuf( Int_Xferred + 1) Int_Xferred = Int_Xferred + 2 - IF (ALLOCATED(OutData%Vwnd_LLMP)) DEALLOCATE(OutData%Vwnd_LLMP) - ALLOCATE(OutData%Vwnd_LLMP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) + IF (ALLOCATED(OutData%Vwnd_LL)) DEALLOCATE(OutData%Vwnd_LL) + ALLOCATE(OutData%Vwnd_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vwnd_LLMP.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%Vwnd_LL.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i2 = LBOUND(OutData%Vwnd_LLMP,2), UBOUND(OutData%Vwnd_LLMP,2) - DO i1 = LBOUND(OutData%Vwnd_LLMP,1), UBOUND(OutData%Vwnd_LLMP,1) - OutData%Vwnd_LLMP(i1,i2) = ReKiBuf(Re_Xferred) + DO i2 = LBOUND(OutData%Vwnd_LL,2), UBOUND(OutData%Vwnd_LL,2) + DO i1 = LBOUND(OutData%Vwnd_LL,1), UBOUND(OutData%Vwnd_LL,1) + OutData%Vwnd_LL(i1,i2) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END DO @@ -10795,11 +10795,11 @@ SUBROUTINE FVW_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg END IF ! check if allocated IF (ALLOCATED(u_out%W) .AND. ALLOCATED(u1%W)) THEN DO i01 = LBOUND(u_out%W,1),UBOUND(u_out%W,1) -IF (ALLOCATED(u_out%W(i01)%Vwnd_LLMP) .AND. ALLOCATED(u1%W(i01)%Vwnd_LLMP)) THEN - DO i2 = LBOUND(u_out%W(i01)%Vwnd_LLMP,2),UBOUND(u_out%W(i01)%Vwnd_LLMP,2) - DO i1 = LBOUND(u_out%W(i01)%Vwnd_LLMP,1),UBOUND(u_out%W(i01)%Vwnd_LLMP,1) - b = -(u1%W(i01)%Vwnd_LLMP(i1,i2) - u2%W(i01)%Vwnd_LLMP(i1,i2)) - u_out%W(i01)%Vwnd_LLMP(i1,i2) = u1%W(i01)%Vwnd_LLMP(i1,i2) + b * ScaleFactor +IF (ALLOCATED(u_out%W(i01)%Vwnd_LL) .AND. ALLOCATED(u1%W(i01)%Vwnd_LL)) THEN + DO i2 = LBOUND(u_out%W(i01)%Vwnd_LL,2),UBOUND(u_out%W(i01)%Vwnd_LL,2) + DO i1 = LBOUND(u_out%W(i01)%Vwnd_LL,1),UBOUND(u_out%W(i01)%Vwnd_LL,1) + b = -(u1%W(i01)%Vwnd_LL(i1,i2) - u2%W(i01)%Vwnd_LL(i1,i2)) + u_out%W(i01)%Vwnd_LL(i1,i2) = u1%W(i01)%Vwnd_LL(i1,i2) + b * ScaleFactor END DO END DO END IF ! check if allocated @@ -10906,12 +10906,12 @@ SUBROUTINE FVW_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, Err END IF ! check if allocated IF (ALLOCATED(u_out%W) .AND. ALLOCATED(u1%W)) THEN DO i01 = LBOUND(u_out%W,1),UBOUND(u_out%W,1) -IF (ALLOCATED(u_out%W(i01)%Vwnd_LLMP) .AND. ALLOCATED(u1%W(i01)%Vwnd_LLMP)) THEN - DO i2 = LBOUND(u_out%W(i01)%Vwnd_LLMP,2),UBOUND(u_out%W(i01)%Vwnd_LLMP,2) - DO i1 = LBOUND(u_out%W(i01)%Vwnd_LLMP,1),UBOUND(u_out%W(i01)%Vwnd_LLMP,1) - b = (t(3)**2*(u1%W(i01)%Vwnd_LLMP(i1,i2) - u2%W(i01)%Vwnd_LLMP(i1,i2)) + t(2)**2*(-u1%W(i01)%Vwnd_LLMP(i1,i2) + u3%W(i01)%Vwnd_LLMP(i1,i2)))* scaleFactor - c = ( (t(2)-t(3))*u1%W(i01)%Vwnd_LLMP(i1,i2) + t(3)*u2%W(i01)%Vwnd_LLMP(i1,i2) - t(2)*u3%W(i01)%Vwnd_LLMP(i1,i2) ) * scaleFactor - u_out%W(i01)%Vwnd_LLMP(i1,i2) = u1%W(i01)%Vwnd_LLMP(i1,i2) + b + c * t_out +IF (ALLOCATED(u_out%W(i01)%Vwnd_LL) .AND. ALLOCATED(u1%W(i01)%Vwnd_LL)) THEN + DO i2 = LBOUND(u_out%W(i01)%Vwnd_LL,2),UBOUND(u_out%W(i01)%Vwnd_LL,2) + DO i1 = LBOUND(u_out%W(i01)%Vwnd_LL,1),UBOUND(u_out%W(i01)%Vwnd_LL,1) + b = (t(3)**2*(u1%W(i01)%Vwnd_LL(i1,i2) - u2%W(i01)%Vwnd_LL(i1,i2)) + t(2)**2*(-u1%W(i01)%Vwnd_LL(i1,i2) + u3%W(i01)%Vwnd_LL(i1,i2)))* scaleFactor + c = ( (t(2)-t(3))*u1%W(i01)%Vwnd_LL(i1,i2) + t(3)*u2%W(i01)%Vwnd_LL(i1,i2) - t(2)*u3%W(i01)%Vwnd_LL(i1,i2) ) * scaleFactor + u_out%W(i01)%Vwnd_LL(i1,i2) = u1%W(i01)%Vwnd_LL(i1,i2) + b + c * t_out END DO END DO END IF ! check if allocated diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index 6684a0b7c..aea15465b 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -85,7 +85,6 @@ subroutine Wings_Panelling_Init(Meshes, 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 s_in(:) = -999 s_in(1) = 0 do iSpan = 2, Meshes(iW)%nNodes @@ -377,9 +376,11 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc call AllocAry(Vvar, 3, nCP_tot, 'Vvar', ErrStat2, ErrMsg2); if(Failed()) return; call AllocAry(Vcst, 3, nCP_tot, 'Vcst', ErrStat2, ErrMsg2); if(Failed()) return; - ! Set m%W(iW)%Vind_CP Induced velocity from Known wake only (after iNWStart+1) - ! Input: m%W%CP_LL, output: m%W%Vind_CP - call LiftingLineInducedVelocities(p, x, p%iNWStart+1, m, ErrStat2, ErrMsg2); if(Failed()) return; + !--- Induction on the lifting line control point or nodes + ! Set induced velocity from Known wake only (after iNWStart+1) + ! if InductionAtCP : In: m%W%CP_LL, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) + ! if not InductionAtCP : In: m%W%r_LL, Out:m%W%Vind_CP (interp/extrap) and m%W%Vind_LL + call LiftingLineInducedVelocities(p, x, p%InductionAtCP, p%iNWStart+1, m, ErrStat2, ErrMsg2); if(Failed()) return; kCP=0 do iW=1,p%nWings From 4abc4888032a9458cd062c6d4095233986cf08a8 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 1 Jul 2021 17:53:48 -0600 Subject: [PATCH 14/24] FVW: Dynamic stall on wake (preliminary) --- modules/aerodyn/src/FVW.f90 | 48 +++++++++++++++++++++++++++- modules/aerodyn/src/FVW_IO.f90 | 6 ++++ modules/aerodyn/src/FVW_Registry.txt | 2 +- modules/aerodyn/src/FVW_Types.f90 | 14 ++++---- 4 files changed, 61 insertions(+), 9 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index b7094077d..ff3be7b05 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -753,9 +753,12 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m if (m%UA_Flag) then call CalculateInputsAndOtherStatesForUA(2, uInterp, p, x, xd, z, OtherState, AFInfo, m, ErrStat2, ErrMsg2); if(Failed()) return call UA_UpdateState_Wrapper(AFInfo, t, n, (/t,t+p%DTaero/), p, x, xd, OtherState, m, ErrStat2, ErrMsg2); if(Failed()) return + ! Compute unsteady Gamma based on UA Cl + if (p%DStallOnWake .and. p%CirculationMethod/=idCircPrescribed) then + call UA_SetGammaDyn(t, uInterp, p, x, xd, OtherState, m, AFInfo, z, ErrStat, ErrMsg) + end if end if - ! TODO compute unsteady Gamma here based on UA Cl ! Updating circulation of near wake panel (and position but irrelevant) ! Changes: x only @@ -1676,4 +1679,47 @@ function NodeText(i,j) end function NodeText end subroutine UA_UpdateState_Wrapper +!> Set dynamic gamma based on dynamic stall states +!! NOTE: We use Vind_LL computed in CalculateInputsAndOtherStatesForUA +subroutine UA_SetGammaDyn(t, u, p, x, xd, OtherState, m, AFInfo, z, ErrStat, ErrMsg) + use UnsteadyAero, only: UA_CalcOutput + real(DbKi), intent(in ) :: t !< Curent time + type(FVW_InputType), intent(in ) :: u !< Inputs at Time t + type(FVW_ParameterType), intent(in ) :: p !< AD parameters + type(FVW_ContinuousStateType), intent(in ) :: x !< continuous states + type(FVW_DiscreteStateType), intent(in ) :: xd !< Discrete states + type(FVW_OtherStateType), intent(in ) :: OtherState !< OtherState + type(FVW_MiscVarType),target, intent(inout) :: m !< Misc/optimization variables + type(AFI_ParameterType ), intent(in ) :: AFInfo(:) !< The airfoil parameter data, temporary, for UA.. + type(FVW_ConstraintStateType), intent(inout) :: z !< Constraint states + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + real(ReKi) :: Cl_dyn, Cl_dyn_prev, Cl_dyn_avg + real(ReKi) :: Gamma_dyn, Gamma_dyn_prev, Gamma_dyn_avg + type(UA_InputType), pointer :: u_UA ! Alias to shorten notations + integer(IntKi), parameter :: InputIndex=2 ! we will always use values at t+dt in this routine + integer(intKi) :: iW, j ! loop counter on wings and nodes + integer(intKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + + ErrStat = 0 + ErrMsg = "" + + do iW=1,p%nWings + j=1 + u_UA => m%W(iW)%u_UA(j,InputIndex) ! Alias + call UA_CalcOutput(j, 1, t, u_UA, m%W(iW)%p_UA, x%UA(iW), xd%UA(iW), OtherState%UA(iW), AFInfo(p%W(iW)%AFindx(j,1)), m%W(iW)%y_UA, m%W(iW)%m_UA, errStat2, errMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'UA_SetGammaDyn') + Gamma_dyn_prev = 0.5_ReKi * m%W(iW)%y_UA%Cl * u_UA%U * p%W(iW)%chord_LL(j) + do j = 2,p%W(iW)%nSpan+1 + u_UA => m%W(iW)%u_UA(j,InputIndex) ! Alias + call UA_CalcOutput(j, 1, t, u_UA, m%W(iW)%p_UA, x%UA(iW), xd%UA(iW), OtherState%UA(iW), AFInfo(p%W(iW)%AFindx(j,1)), m%W(iW)%y_UA, m%W(iW)%m_UA, errStat2, errMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'UA_SetGammaDyn') + Gamma_dyn = 0.5_ReKi * m%W(iW)%y_UA%Cl * u_UA%U * p%W(iW)%chord_LL(j) + Gamma_dyn_avg = (Gamma_dyn+Gamma_dyn_prev)*0.5_ReKi + Gamma_dyn_prev = Gamma_dyn + z%W(iW)%Gamma_LL(j-1) = Gamma_dyn_avg + !print*,z%W(iW)%Gamma_LL(j-1), Gamma_dyn, Gamma_dyn_avg + enddo + enddo ! iW, Loop on wings +end subroutine UA_SetGammaDyn + end module FVW diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index fcf6ab3dd..f57e4cd08 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -106,6 +106,7 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, m, Inp, ErrStat, ErrMsg ) ! NOTE: no error handling since this is for debug p%InductionAtCP = .true. p%WakeAtTE = .true. + p%DStallOnWake = .false. CALL ReadCom(UnIn,FileName, '=== Separator' ,ErrStat2,ErrMsg2); CALL ReadCom(UnIn,FileName, '--- Advanced options header' ,ErrStat2,ErrMsg2); if(ErrStat2==ErrID_None) then @@ -119,6 +120,11 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, m, Inp, ErrStat, ErrMsg ) elseif (index(sDummy, 'WAKEATTE')>1) then read(sDummy, '(L1)') p%WakeAtTE print*,' >>> WakeAtTE',p%WakeAtTE + elseif (index(sDummy, 'DSTALLONWAKE')>1) then + read(sDummy, '(L1)') p%DStallOnWake + print*,' >>> DStallOnWake',p%DStallOnWake + else + print*,' >>> Line ignored, starting with'//trim(sDummy) endif enddo endif diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index 6992688fe..aaf7faa35 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -99,6 +99,7 @@ typedef ^ ^ IntKi # Parameters advanced options typedef ^ ^ Logical InductionAtCP - - - "Compute induced velocities at nodes or CP" typedef ^ ^ Logical WakeAtTE - - - "Start the wake at the trailing edge, or at the LL" +typedef ^ ^ Logical DStallOnWake - - - "Dynamic stall has influence on wake" #.......... ContinuousStateType ...... typedef FVW/FVW Wng_ContinuousStateType ReKi Gamma_NW :: - - "Circulation of the near wake panels ( nSpan x nNW )" - @@ -182,7 +183,6 @@ typedef ^ ^ DbKi typedef ^ ^ ReKi r_wind :: - - "List of points where wind is requested for next time step" - 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 ^ ^ FVW_ContinuousStateType dxdt - - - "State time derivatie, stored for overcycling and convenience" - typedef ^ ^ FVW_ContinuousStateType x1 - - - "States at t (for overcycling) " - typedef ^ ^ FVW_ContinuousStateType x2 - - - "States at t+DTFVW (for overcycling)" - diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index c0ec6c1ba..644a0cc2a 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -126,6 +126,7 @@ MODULE FVW_Types INTEGER(IntKi) :: nGridOut !< Number of VTK grid to output [-] LOGICAL :: InductionAtCP !< Compute induced velocities at nodes or CP [-] LOGICAL :: WakeAtTE !< Start the wake at the trailing edge, or at the LL [-] + LOGICAL :: DStallOnWake !< Dynamic stall has influence on wake [-] END TYPE FVW_ParameterType ! ======================= ! ========= Wng_ContinuousStateType ======= @@ -213,7 +214,6 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: r_wind !< List of points where wind is requested for next time step [-] 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] TYPE(FVW_ContinuousStateType) :: dxdt !< State time derivatie, stored for overcycling and convenience [-] TYPE(FVW_ContinuousStateType) :: x1 !< States at t (for overcycling) [-] TYPE(FVW_ContinuousStateType) :: x2 !< States at t+DTFVW (for overcycling) [-] @@ -1716,6 +1716,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%nGridOut = SrcParamData%nGridOut DstParamData%InductionAtCP = SrcParamData%InductionAtCP DstParamData%WakeAtTE = SrcParamData%WakeAtTE + DstParamData%DStallOnWake = SrcParamData%DStallOnWake END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -1842,6 +1843,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! nGridOut Int_BufSz = Int_BufSz + 1 ! InductionAtCP Int_BufSz = Int_BufSz + 1 ! WakeAtTE + Int_BufSz = Int_BufSz + 1 ! DStallOnWake IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -2016,6 +2018,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = TRANSFER(InData%WakeAtTE, IntKiBuf(1)) Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%DStallOnWake, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2211,6 +2215,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%WakeAtTE = TRANSFER(IntKiBuf(Int_Xferred), OutData%WakeAtTE) Int_Xferred = Int_Xferred + 1 + OutData%DStallOnWake = TRANSFER(IntKiBuf(Int_Xferred), OutData%DStallOnWake) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyWng_ContinuousStateType( SrcWng_ContinuousStateTypeData, DstWng_ContinuousStateTypeData, CtrlCode, ErrStat, ErrMsg ) @@ -6490,7 +6496,6 @@ SUBROUTINE FVW_CopyMisc( SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg ) ENDIF DstMiscData%ComputeWakeInduced = SrcMiscData%ComputeWakeInduced DstMiscData%OldWakeTime = SrcMiscData%OldWakeTime - DstMiscData%tSpent = SrcMiscData%tSpent CALL FVW_CopyContState( SrcMiscData%dxdt, DstMiscData%dxdt, CtrlCode, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) IF (ErrStat>=AbortErrLev) RETURN @@ -6660,7 +6665,6 @@ 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 Int_BufSz = Int_BufSz + 3 ! dxdt: size of buffers for each call to pack subtype CALL FVW_PackContState( Re_Buf, Db_Buf, Int_Buf, InData%dxdt, ErrStat2, ErrMsg2, .TRUE. ) ! dxdt CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -6869,8 +6873,6 @@ SUBROUTINE FVW_PackMisc( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, Si Int_Xferred = Int_Xferred + 1 DbKiBuf(Db_Xferred) = InData%OldWakeTime Db_Xferred = Db_Xferred + 1 - ReKiBuf(Re_Xferred) = InData%tSpent - Re_Xferred = Re_Xferred + 1 CALL FVW_PackContState( Re_Buf, Db_Buf, Int_Buf, InData%dxdt, ErrStat2, ErrMsg2, OnlySize ) ! dxdt CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) RETURN @@ -7195,8 +7197,6 @@ 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 Buf_size=IntKiBuf( Int_Xferred ) Int_Xferred = Int_Xferred + 1 IF(Buf_size > 0) THEN From 88af5f11e8cc746ad8ab69aa9e65c7827887dac6 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Wed, 7 Jul 2021 17:36:05 -0600 Subject: [PATCH 15/24] FVW: using LL for nodes, CP for control points --- modules/aerodyn/src/AeroDyn.f90 | 8 +- .../aerodyn/src/AeroDyn_AllBldNdOuts_IO.f90 | 2 +- modules/aerodyn/src/AeroDyn_IO.f90 | 2 +- modules/aerodyn/src/FVW.f90 | 46 ++- modules/aerodyn/src/FVW_IO.f90 | 2 +- modules/aerodyn/src/FVW_Registry.txt | 11 +- modules/aerodyn/src/FVW_Subs.f90 | 16 +- modules/aerodyn/src/FVW_Types.f90 | 352 ++++++++---------- modules/aerodyn/src/FVW_Wings.f90 | 38 +- 9 files changed, 214 insertions(+), 263 deletions(-) diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index bcbd7d363..a76c154e8 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -2161,7 +2161,7 @@ subroutine SetOutputsFromFVW(t, u, p, OtherState, x, xd, m, y, ErrStat, ErrMsg) Vwnd = m%rotors(iR)%DisturbedInflow(1:3,j,k) ! NOTE: contains tower shadow theta = m%FVW%W(iW)%PitchAndTwist(j) ! TODO call FVW_AeroOuts( m%rotors(iR)%WithoutSweepPitchTwist(1:3,1:3,j,k), u%rotors(iR)%BladeMotion(k)%Orientation(1:3,1:3,j), & ! inputs - theta, Vstr(1:3), Vind(1:3), VWnd(1:3), p%rotors(iR)%KinVisc, p%FVW%W(iW)%Chord(j), & ! inputs + theta, Vstr(1:3), Vind(1:3), VWnd(1:3), p%rotors(iR)%KinVisc, p%FVW%W(iW)%chord_LL(j), & ! inputs AxInd, TanInd, Vrel, phi, alpha, Re, UrelWind_s(1:3), ErrStat2, ErrMsg2 ) ! outputs call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'SetOutputsFromFVW') @@ -2196,9 +2196,9 @@ subroutine SetOutputsFromFVW(t, u, p, OtherState, x, xd, m, y, ErrStat, ErrMsg) Cy = Cl_dyn*sp - Cd_dyn*cp q = 0.5 * p%rotors(iR)%airDens * Vrel**2 ! dynamic pressure of the jth node in the kth blade - force(1) = Cx * q * p%FVW%W(iW)%Chord(j) ! 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%W(iW)%Chord(j) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade - moment(3)= Cm_dyn * q * p%FVW%W(iW)%Chord(j)**2 ! M = pitching moment per unit length of the jth node in the kth blade + force(1) = Cx * q * p%FVW%W(iW)%chord_LL(j) ! 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%W(iW)%chord_LL(j) ! Y = tangential force per unit length (tangential to the plane, not chord) of the jth node in the kth blade + moment(3)= Cm_dyn * q * p%FVW%W(iW)%chord_LL(j)**2 ! M = pitching moment per unit length of the jth node in the kth blade ! save these values for possible output later: m%rotors(iR)%X(j,k) = force(1) diff --git a/modules/aerodyn/src/AeroDyn_AllBldNdOuts_IO.f90 b/modules/aerodyn/src/AeroDyn_AllBldNdOuts_IO.f90 index 683669989..de4eee174 100644 --- a/modules/aerodyn/src/AeroDyn_AllBldNdOuts_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_AllBldNdOuts_IO.f90 @@ -986,7 +986,7 @@ SUBROUTINE Calc_WriteAllBldNdOutput( p, p_AD, u, m, m_AD, y, OtherState, Indx, i DO IdxBlade=1,p%BldNd_BladesOut iW = p_AD%FVW%Bld2Wings(iRot, IdxBlade) DO IdxNode=1,u%BladeMotion(IdxBlade)%NNodes - y%WriteOutput( OutIdx ) = 0.5_ReKi * p_AD%FVW%W(iW)%Chord(IdxNode) * m_AD%FVW%W(iW)%BN_Vrel(IdxNode) * m_AD%FVW%W(iW)%BN_Cl(IdxNode) ! "Gam" [m^2/s] + y%WriteOutput( OutIdx ) = 0.5_ReKi * p_AD%FVW%W(iW)%chord_LL(IdxNode) * m_AD%FVW%W(iW)%BN_Vrel(IdxNode) * m_AD%FVW%W(iW)%BN_Cl(IdxNode) ! "Gam" [m^2/s] OutIdx = OutIdx + 1 ENDDO ENDDO diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index e64bed003..296d622be 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -1973,7 +1973,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_AD%FVW%W(iW)%Chord(j) * m_AD%FVW%W(iW)%BN_Vrel(j) * m_AD%FVW%W(iW)%BN_Cl(j) ! "Gam" [m^2/s] + m%AllOuts( BNGam( beta,k) ) = 0.5_ReKi * p_AD%FVW%W(iW)%chord_LL(j) * m_AD%FVW%W(iW)%BN_Vrel(j) * m_AD%FVW%W(iW)%BN_Cl(j) ! "Gam" [m^2/s] end do ! nodes end do ! blades diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index ff3be7b05..d5a0b3551 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -208,7 +208,7 @@ subroutine FVW_InitMiscVars( p, m, ErrStat, ErrMsg ) call AllocAry( m%W(iW)%alpha_LL, p%W(iW)%nSpan , 'Wind on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%alpha_LL= -999999_ReKi; call AllocAry( m%W(iW)%Vreln_LL, p%W(iW)%nSpan , 'Wind on CP ll ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Vreln_LL = -999999_ReKi; ! Variables at control points/elements - call AllocAry( m%W(iW)%CP_LL , 3 , p%W(iW)%nSpan, 'Control points LL ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%CP_LL= -999999_ReKi; + call AllocAry( m%W(iW)%CP , 3 , p%W(iW)%nSpan, 'Control points LL ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%CP= -999999_ReKi; call AllocAry( m%W(iW)%Tang , 3 , p%W(iW)%nSpan, 'Tangential vector ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Tang= -999999_ReKi; call AllocAry( m%W(iW)%Norm , 3 , p%W(iW)%nSpan, 'Normal vector ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Norm= -999999_ReKi; call AllocAry( m%W(iW)%Orth , 3 , p%W(iW)%nSpan, 'Orthogonal vector ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName ); m%W(iW)%Orth= -999999_ReKi; @@ -441,12 +441,12 @@ SUBROUTINE FVW_SetParametersFromInputs( InitInp, p, ErrStat, ErrMsg ) p%W(iW)%AFindx = InitInp%W(iW)%AFindx ! Copying in case AD15 still needs these p%W(iW)%iRotor = InitInp%W(iW)%iRotor - p%W(iW)%nSpan = size(InitInp%W(iW)%Chord)-1 - call move_alloc(InitInp%W(iW)%Chord, p%W(iW)%Chord) - call AllocAry(p%W(iW)%s_LL , p%W(iW)%nSpan+1, 'Spanwise coord LL ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); p%W(iW)%s_LL= -999999_ReKi; - call AllocAry(p%W(iW)%s_CP_LL , p%W(iW)%nSpan , 'Spanwise coord CPll', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); p%W(iW)%s_CP_LL= -999999_ReKi; - call AllocAry(p%W(iW)%chord_LL , p%W(iW)%nSpan+1, 'Chord on LL ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); p%W(iW)%chord_LL= -999999_ReKi; - call AllocAry(p%W(iW)%chord_CP_LL, p%W(iW)%nSpan , 'Chord on CP LL ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); p%W(iW)%chord_CP_LL= -999999_ReKi; + p%W(iW)%nSpan = size(InitInp%W(iW)%chord)-1 + call move_alloc(InitInp%W(iW)%chord, p%W(iW)%chord_LL) + call AllocAry(p%W(iW)%s_LL , p%W(iW)%nSpan+1, 'Spanwise coord LL ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); p%W(iW)%s_LL= -999999_ReKi; + call AllocAry(p%W(iW)%s_CP , p%W(iW)%nSpan , 'Spanwise coord CPll', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); p%W(iW)%s_CP= -999999_ReKi; + !call AllocAry(p%W(iW)%chord_LL , p%W(iW)%nSpan+1, 'Chord on LL ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); p%W(iW)%chord_LL= -999999_ReKi; + call AllocAry(p%W(iW)%chord_CP, p%W(iW)%nSpan , 'Chord on CP LL ', ErrStat2, ErrMsg2);call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,RoutineName); p%W(iW)%chord_CP= -999999_ReKi; enddo ! --- Distributing wings to rotors @@ -514,12 +514,12 @@ SUBROUTINE FVW_SetParametersFromInputFile( InputFileData, p, m, ErrStat, ErrMsg if (InputFileData%CirculationMethod==idCircPrescribed) then call AllocAry(p%W(iW)%PrescribedCirculation, p%W(iW)%nSpan, 'Prescribed Circulation', ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_SetParameters'); p%W(iW)%PrescribedCirculation = -999999_ReKi; - if (.not. allocated(p%W(iW)%s_CP_LL)) then + if (.not. allocated(p%W(iW)%s_CP)) then ErrMsg = 'Spanwise coordinate not allocated.' ErrStat = ErrID_Fatal return endif - call ReadAndInterpGamma(trim(InputFileData%CirculationFile), p%W(iW)%s_CP_LL(1:p%W(iW)%nSpan), p%W(iW)%s_LL(p%W(iW)%nSpan+1), p%W(iW)%PrescribedCirculation, ErrStat2, ErrMsg2) + call ReadAndInterpGamma(trim(InputFileData%CirculationFile), p%W(iW)%s_CP(1:p%W(iW)%nSpan), p%W(iW)%s_LL(p%W(iW)%nSpan+1), p%W(iW)%PrescribedCirculation, ErrStat2, ErrMsg2) call SetErrStat ( ErrStat2, ErrMsg2, ErrStat,ErrMsg,'FVW_SetParameters' ); endif enddo @@ -704,7 +704,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! States x2 call FVW_CopyContState(x, m%x2, 0, ErrStat2, ErrMsg2) ! Backup current state at t+DTfvw m%t2=t+p%DTfvw - !! Inputs at t+DTfvw (Wings Panelling updates CP_LL, and VstW(iW)%r_LL) + !! Inputs at t+DTfvw (Wings Panelling updates CP, and VstW(iW)%r_LL) !call FVW_Input_ExtrapInterp(u(1:size(utimes)),utimes,uInterp,t+p%DTfvw, ErrStat2, ErrMsg2); if(Failed()) return !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) @@ -731,7 +731,7 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m call FVW_ContStates_Interp(t+p%DTaero, (/m%x1, m%x2/), (/m%t1, m%t2/), p, m, x, ErrStat2, ErrMsg2); if(Failed()) return endif - ! Inputs at t+DTaero (Wings Panelling updates CP_LL, and VstW(iW)%r_LL) + ! Inputs at t+DTaero (Wings Panelling updates CP, and VstW(iW)%r_LL) call FVW_Input_ExtrapInterp(u(1:size(utimes)),utimes,uInterp,t+p%DTaero, ErrStat2, ErrMsg2); if(Failed()) return call Wings_Panelling(uInterp%WingsMesh, p, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -1324,7 +1324,7 @@ subroutine FVW_CalcConstrStateResidual( t, u, p, x, xd, z_guess, OtherState, m, ErrMsg = "" ! Distribute the Wind we requested to Inflow wind to storage Misc arrays - ! TODO ANDY: replace with direct call to inflow wind at m%W(iW)%CP_LL location + ! TODO ANDY: replace with direct call to inflow wind at m%W(iW)%CP location ! input: V_wind, output: m%W%Vwnd_LL CALL DistributeRequestedWind_LL(u%V_wind, p, m) @@ -1361,7 +1361,7 @@ subroutine CalcOutputForAD(t, u, p, x, y, m, AFInfo, ErrStat, ErrMsg) ErrMsg = "" ! ! --- NOTE: this below might not be needed ! ! Distribute the Wind we requested to Inflow wind to storage Misc arrays -! ! TODO ANDY: replace with direct call to inflow wind at m%W(iW)%CP_LL location +! ! TODO ANDY: replace with direct call to inflow wind at m%W(iW)%CP location ! CALL DistributeRequestedWind_LL(u%V_wind, p, m%Vwnd_LL) ! ! ! Control points location and structural velocity @@ -1372,7 +1372,7 @@ subroutine CalcOutputForAD(t, u, p, x, y, m, AFInfo, ErrStat, ErrMsg) ! CALL Wings_ComputeCirculation(t, m%W(iW)%Gamma_LL, z%W(iW)%Gamma_LL, u, p, x, m, AFInfo, ErrStat2, ErrMsg2, 0); if(Failed()) return ! For plotting only !--- Induction on the lifting line control point - ! if InductionAtCP : In: m%W%CP_LL, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) + ! if InductionAtCP : In: m%W%CP, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) ! if not InductionAtCP : In: m%W%r_LL, Out:m%W%Vind_CP (interp/extrap) and m%W%Vind_LL call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! Transfer to output @@ -1541,7 +1541,7 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E ! ---Condensed version of "BEMT_Set_UA_InitData" allocate(Init_UA_Data%c(InitInp%numBladeNodes,1), STAT = errStat2) do i = 1,InitInp%numBladeNodes - Init_UA_Data%c(i,1) = p%W(iW)%chord(i) ! NOTE: InitInp chord move-allocd to p + Init_UA_Data%c(i,1) = p%W(iW)%chord_LL(i) ! NOTE: InitInp chord move-allocd to p end do Init_UA_Data%dt = interval Init_UA_Data%OutRootName = InitInp%RootName @@ -1604,7 +1604,7 @@ subroutine CalculateInputsAndOtherStatesForUA(InputIndex, u, p, x, xd, z, OtherS ErrMsg = "" ! --- Induction on the lifting line control points - ! if InductionAtCP : In: m%W%CP_LL, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) + ! if InductionAtCP : In: m%W%CP, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) ! if not InductionAtCP : In: m%W%r_LL, Out:m%W%Vind_CP (interp/extrap) and m%W%Vind_LL call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'Ca;lcultateInputsAndOtherStatesForUA'); if (ErrStat >= AbortErrLev) return if (p%InductionAtCP) then @@ -1623,7 +1623,7 @@ subroutine CalculateInputsAndOtherStatesForUA(InputIndex, u, p, x, xd, z, OtherS ! NOTE: To be consistent with CalcOutput we take Vwind_LL 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(iW)%Orientation(1:3,1:3,i), u%WingsMesh(iW)%TranslationVel(1:3,i), m%W(iW)%Vind_LL(1:3,i), u%W(iW)%Vwnd_LL(1:3,i), & - p%KinVisc, p%W(iW)%Chord(i), u_UA%U, u_UA%alpha, u_UA%Re) + p%KinVisc, p%W(iW)%chord_LL(i), u_UA%U, u_UA%alpha, u_UA%Re) u_UA%v_ac(1) = sin(u_UA%alpha)*u_UA%U u_UA%v_ac(2) = cos(u_UA%alpha)*u_UA%U u_UA%omega = u%W(iW)%omega_z(i) @@ -1697,15 +1697,19 @@ subroutine UA_SetGammaDyn(t, u, p, x, xd, OtherState, m, AFInfo, z, ErrStat, Err real(ReKi) :: Cl_dyn, Cl_dyn_prev, Cl_dyn_avg real(ReKi) :: Gamma_dyn, Gamma_dyn_prev, Gamma_dyn_avg type(UA_InputType), pointer :: u_UA ! Alias to shorten notations - integer(IntKi), parameter :: InputIndex=2 ! we will always use values at t+dt in this routine + integer(IntKi), parameter :: InputIndex=1 ! we will always use values at t+dt in this routine integer(intKi) :: iW, j ! loop counter on wings and nodes - integer(intKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 + integer(intKi) :: errStat2 + character(ErrMsgLen) :: errMsg2 ErrStat = 0 ErrMsg = "" + print*,'------------------------------------------------------------------' + print*,'t ',t do iW=1,p%nWings + ! Gamma_LL is expressed at CP, so we average the dynamic gamma from both nodes + ! NOTE: this is inconsistent with the Wings solving which occurs at the CPs j=1 u_UA => m%W(iW)%u_UA(j,InputIndex) ! Alias call UA_CalcOutput(j, 1, t, u_UA, m%W(iW)%p_UA, x%UA(iW), xd%UA(iW), OtherState%UA(iW), AFInfo(p%W(iW)%AFindx(j,1)), m%W(iW)%y_UA, m%W(iW)%m_UA, errStat2, errMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'UA_SetGammaDyn') @@ -1718,6 +1722,8 @@ subroutine UA_SetGammaDyn(t, u, p, x, xd, OtherState, m, AFInfo, z, ErrStat, Err Gamma_dyn_prev = Gamma_dyn z%W(iW)%Gamma_LL(j-1) = Gamma_dyn_avg !print*,z%W(iW)%Gamma_LL(j-1), Gamma_dyn, Gamma_dyn_avg + !y%WriteOutput( OutIdx ) = 0.5_ReKi * p%BEMT%chord(IdxNode,IdxBlade) * m%BEMT_y%Vrel(IdxNode,IdxBlade) * m%BEMT_y%Cl(IdxNode,IdxBlade) ! "Gam" [m^2/s] + !y%WriteOutput( OutIdx ) = 0.5_ReKi * p_AD%FVW%W(iW)%Chord(IdxNode) * m_AD%FVW%W(iW)%BN_Vrel(IdxNode) * m_AD%FVW%W(iW)%BN_Cl(IdxNode) ! "Gam" [m^2/s] enddo enddo ! iW, Loop on wings end subroutine UA_SetGammaDyn diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index f57e4cd08..e2a2cf71d 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -386,7 +386,7 @@ subroutine WrVTK_FVW(p, x, z, m, FileRootName, VTKcount, Twidth, bladeFrame, Hub 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%W(iW)%CP_LL(1:3,1:p%W(iW)%nSpan),mvtk,bladeFrame) + call vtk_dataset_polydata(m%W(iW)%CP(1:3,1:p%W(iW)%nSpan),mvtk,bladeFrame) call vtk_point_data_init(mvtk) call vtk_point_data_scalar(z%W(iW)%Gamma_LL( 1:p%W(iW)%nSpan),'Gamma_LL',mvtk) call vtk_point_data_vector(m%W(iW)%Vind_CP (1:3,1:p%W(iW)%nSpan),'Vind_CP',mvtk) diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index aaf7faa35..d1e2f8e0a 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -41,11 +41,10 @@ typedef ^ ^ IntKi ##################### Registry for FVW ############### # ..... PARAMETERS ............. -typedef FVW/FVW Wng_ParameterType ReKi Chord : - - "Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number]" - +typedef FVW/FVW Wng_ParameterType ReKi chord_LL : - - "Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number]" - +typedef ^ ^ ReKi chord_CP : - - "Chord on LL cp " m typedef ^ ^ ReKi s_LL : - - "Spanwise coordinate of LL elements" m -typedef ^ ^ ReKi s_CP_LL : - - "Spanwise coordinate of LL CP" m -typedef ^ ^ ReKi chord_LL : - - "chord on LL nodes " m -typedef ^ ^ ReKi chord_CP_LL : - - "chord on LL cp " m +typedef ^ ^ ReKi s_CP : - - "Spanwise coordinate of LL CP" m typedef ^ ^ IntKi iRotor - - - "Index of rotor the wing belong to" - typedef ^ ^ IntKi AFindx :: - - "Index to the airfoils from AD15 [BladeNode,BladeIndex=1]" - typedef ^ ^ IntKi nSpan - - - "TODO, should be defined per wing. Number of spanwise element" - @@ -123,7 +122,7 @@ typedef FVW/FVW Wng_MiscVarType ReKi typedef ^ ^ ReKi TE :: - - "Trailing edge points" - typedef ^ ^ ReKi r_LL ::: - - "Position of the Lifting line panels" - # Variables at control point - Dimensions nSpan -typedef ^ ^ ReKi CP_LL :: - - "Coordinates of LL CP" - +typedef ^ ^ ReKi CP :: - - "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" - @@ -232,7 +231,7 @@ typedef ^ ^ UA_OtherStateType #.......... InitInputType ...... typedef FVW/FVW Wng_InitInputType IntKi AFindx :: - - "Index to the airfoils from AD15 [idx1=BladeNode, idx2=Blade number=1]" - -typedef ^ ^ ReKi Chord : - - "Chord of each blade element from input file [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 ^ ^ IntKi iRotor - - - "Index of rotor the wing belong to" - typedef ^ ^ INTEGER UAOff_innerNode - - - "Last node on each blade where UA should be turned off based on span location from blade root (0 if always on)" - diff --git a/modules/aerodyn/src/FVW_Subs.f90 b/modules/aerodyn/src/FVW_Subs.f90 index 00bafaf7b..0bd23c689 100644 --- a/modules/aerodyn/src/FVW_Subs.f90 +++ b/modules/aerodyn/src/FVW_Subs.f90 @@ -534,7 +534,7 @@ subroutine SetRequestedWindPoints(r_wind, x, p, m) do iW=1,p%nWings iP_start = iP_end+1 iP_end = iP_start-1 + p%W(iW)%nSpan - r_wind(1:3,iP_start:iP_end) = m%W(iW)%CP_LL(1:3,1:p%W(iW)%nSpan) + r_wind(1:3,iP_start:iP_end) = m%W(iW)%CP(1:3,1:p%W(iW)%nSpan) enddo ! --- NW points do iW=1,p%nWings @@ -920,9 +920,9 @@ subroutine FVW_InitRegularization(x, p, m, ErrStat, ErrMsg) else if (p%RegDeterMethod==idRegDeterChord) then ! Using chord to scale the reg param do iSpan=1,p%W(iW)%nSpan - x%W(iW)%Eps_NW(1:3, iSpan, 1) = p%WingRegParam * p%W(iW)%chord_CP_LL(iSpan) + x%W(iW)%Eps_NW(1:3, iSpan, 1) = p%WingRegParam * p%W(iW)%chord_CP(iSpan) if (p%nNWMax>1) then - x%W(iW)%Eps_NW(1:3, iSpan, 2) = p%WakeRegParam * p%W(iW)%chord_CP_LL(iSpan) + x%W(iW)%Eps_NW(1:3, iSpan, 2) = p%WakeRegParam * p%W(iW)%chord_CP(iSpan) endif enddo @@ -1228,7 +1228,7 @@ end subroutine WakeInducedVelocities !! In : x%W(iW)%r_NW, x%W(iW)%r_FW, x%W(iW)%Gamma_NW, x%W(iW)%Gamma_FW !! Out: m%W(iW)%Vind_CP subroutine LiftingLineInducedVelocities(p, x, InductionAtCP, iDepthStart, m, ErrStat, ErrMsg) - !real(ReKi), dimension(:,:,:), intent(in ) :: CP_LL !< Control points where velocity is to be evaluated + !real(ReKi), dimension(:,:,:), intent(in ) :: CP !< Control points where velocity is to be evaluated type(FVW_ParameterType), intent(in ) :: p !< Parameters type(FVW_ContinuousStateType), intent(in ) :: x !< States logical, intent(in ) :: InductionAtCP !< Compute induction at CP or on LL nodes @@ -1294,7 +1294,7 @@ subroutine PackLiftingLinePoints() iHeadP=1 if (InductionAtCP) then do iW=1,p%nWings - call LatticeToPoints2D(m%W(iW)%CP_LL(1:3,:), CPs, iHeadP) + call LatticeToPoints2D(m%W(iW)%CP(1:3,:), CPs, iHeadP) enddo else do iW=1,p%nWings @@ -1320,9 +1320,9 @@ subroutine UnPackLiftingLineVelocities() enddo ! --- Transfer CP to LL (Linear interpolation for interior points and extrapolations at boundaries) do iW=1,p%nWings - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(1,:), p%W(iW)%s_LL(:), m%W(iW)%Vind_LL(1,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(2,:), p%W(iW)%s_LL(:), m%W(iW)%Vind_LL(2,:)) - call interpextrap_cp2node(p%W(iW)%s_CP_LL(:), m%W(iW)%Vind_CP(3,:), p%W(iW)%s_LL(:), m%W(iW)%Vind_LL(3,:)) + call interpextrap_cp2node(p%W(iW)%s_CP(:), m%W(iW)%Vind_CP(1,:), p%W(iW)%s_LL(:), m%W(iW)%Vind_LL(1,:)) + call interpextrap_cp2node(p%W(iW)%s_CP(:), m%W(iW)%Vind_CP(2,:), p%W(iW)%s_LL(:), m%W(iW)%Vind_LL(2,:)) + call interpextrap_cp2node(p%W(iW)%s_CP(:), m%W(iW)%Vind_CP(3,:), p%W(iW)%s_LL(:), m%W(iW)%Vind_LL(3,:)) enddo else do iW=1,p%nWings diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 644a0cc2a..45ca9e911 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -71,11 +71,10 @@ MODULE FVW_Types ! ======================= ! ========= Wng_ParameterType ======= TYPE, PUBLIC :: Wng_ParameterType - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number] [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: chord_LL !< Chord of each blade element from input file [idx1=BladeNode, idx2=Blade number] [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: chord_CP !< Chord on LL cp [m] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: s_LL !< Spanwise coordinate of LL elements [m] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: s_CP_LL !< Spanwise coordinate of LL CP [m] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: chord_LL !< chord on LL nodes [m] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: chord_CP_LL !< chord on LL cp [m] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: s_CP !< Spanwise coordinate of LL CP [m] INTEGER(IntKi) :: iRotor !< Index of rotor the wing belong to [-] INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [BladeNode,BladeIndex=1] [-] INTEGER(IntKi) :: nSpan !< TODO, should be defined per wing. Number of spanwise element [-] @@ -160,7 +159,7 @@ MODULE FVW_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: LE !< Leading edge points [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: TE !< Trailing edge points [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: r_LL !< Position of the Lifting line panels [-] - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: CP_LL !< Coordinates of LL CP [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: CP !< 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 [-] @@ -272,7 +271,7 @@ MODULE FVW_Types ! ========= Wng_InitInputType ======= TYPE, PUBLIC :: Wng_InitInputType INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: AFindx !< Index to the airfoils from AD15 [idx1=BladeNode, idx2=Blade number=1] [-] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Chord !< Chord of each blade element from input file [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 [-] INTEGER(IntKi) :: iRotor !< Index of rotor the wing belong to [-] INTEGER(IntKi) :: UAOff_innerNode !< Last node on each blade where UA should be turned off based on span location from blade root (0 if always on) [-] @@ -1128,17 +1127,29 @@ SUBROUTINE FVW_CopyWng_ParameterType( SrcWng_ParameterTypeData, DstWng_Parameter ! ErrStat = ErrID_None ErrMsg = "" -IF (ALLOCATED(SrcWng_ParameterTypeData%Chord)) THEN - i1_l = LBOUND(SrcWng_ParameterTypeData%Chord,1) - i1_u = UBOUND(SrcWng_ParameterTypeData%Chord,1) - IF (.NOT. ALLOCATED(DstWng_ParameterTypeData%Chord)) THEN - ALLOCATE(DstWng_ParameterTypeData%Chord(i1_l:i1_u),STAT=ErrStat2) +IF (ALLOCATED(SrcWng_ParameterTypeData%chord_LL)) THEN + i1_l = LBOUND(SrcWng_ParameterTypeData%chord_LL,1) + i1_u = UBOUND(SrcWng_ParameterTypeData%chord_LL,1) + IF (.NOT. ALLOCATED(DstWng_ParameterTypeData%chord_LL)) THEN + ALLOCATE(DstWng_ParameterTypeData%chord_LL(i1_l:i1_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_ParameterTypeData%Chord.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_ParameterTypeData%chord_LL.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstWng_ParameterTypeData%Chord = SrcWng_ParameterTypeData%Chord + DstWng_ParameterTypeData%chord_LL = SrcWng_ParameterTypeData%chord_LL +ENDIF +IF (ALLOCATED(SrcWng_ParameterTypeData%chord_CP)) THEN + i1_l = LBOUND(SrcWng_ParameterTypeData%chord_CP,1) + i1_u = UBOUND(SrcWng_ParameterTypeData%chord_CP,1) + IF (.NOT. ALLOCATED(DstWng_ParameterTypeData%chord_CP)) THEN + ALLOCATE(DstWng_ParameterTypeData%chord_CP(i1_l:i1_u),STAT=ErrStat2) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_ParameterTypeData%chord_CP.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + END IF + DstWng_ParameterTypeData%chord_CP = SrcWng_ParameterTypeData%chord_CP ENDIF IF (ALLOCATED(SrcWng_ParameterTypeData%s_LL)) THEN i1_l = LBOUND(SrcWng_ParameterTypeData%s_LL,1) @@ -1152,41 +1163,17 @@ SUBROUTINE FVW_CopyWng_ParameterType( SrcWng_ParameterTypeData, DstWng_Parameter END IF DstWng_ParameterTypeData%s_LL = SrcWng_ParameterTypeData%s_LL ENDIF -IF (ALLOCATED(SrcWng_ParameterTypeData%s_CP_LL)) THEN - i1_l = LBOUND(SrcWng_ParameterTypeData%s_CP_LL,1) - i1_u = UBOUND(SrcWng_ParameterTypeData%s_CP_LL,1) - IF (.NOT. ALLOCATED(DstWng_ParameterTypeData%s_CP_LL)) THEN - ALLOCATE(DstWng_ParameterTypeData%s_CP_LL(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_ParameterTypeData%s_CP_LL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstWng_ParameterTypeData%s_CP_LL = SrcWng_ParameterTypeData%s_CP_LL -ENDIF -IF (ALLOCATED(SrcWng_ParameterTypeData%chord_LL)) THEN - i1_l = LBOUND(SrcWng_ParameterTypeData%chord_LL,1) - i1_u = UBOUND(SrcWng_ParameterTypeData%chord_LL,1) - IF (.NOT. ALLOCATED(DstWng_ParameterTypeData%chord_LL)) THEN - ALLOCATE(DstWng_ParameterTypeData%chord_LL(i1_l:i1_u),STAT=ErrStat2) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_ParameterTypeData%chord_LL.', ErrStat, ErrMsg,RoutineName) - RETURN - END IF - END IF - DstWng_ParameterTypeData%chord_LL = SrcWng_ParameterTypeData%chord_LL -ENDIF -IF (ALLOCATED(SrcWng_ParameterTypeData%chord_CP_LL)) THEN - i1_l = LBOUND(SrcWng_ParameterTypeData%chord_CP_LL,1) - i1_u = UBOUND(SrcWng_ParameterTypeData%chord_CP_LL,1) - IF (.NOT. ALLOCATED(DstWng_ParameterTypeData%chord_CP_LL)) THEN - ALLOCATE(DstWng_ParameterTypeData%chord_CP_LL(i1_l:i1_u),STAT=ErrStat2) +IF (ALLOCATED(SrcWng_ParameterTypeData%s_CP)) THEN + i1_l = LBOUND(SrcWng_ParameterTypeData%s_CP,1) + i1_u = UBOUND(SrcWng_ParameterTypeData%s_CP,1) + IF (.NOT. ALLOCATED(DstWng_ParameterTypeData%s_CP)) THEN + ALLOCATE(DstWng_ParameterTypeData%s_CP(i1_l:i1_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_ParameterTypeData%chord_CP_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_ParameterTypeData%s_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstWng_ParameterTypeData%chord_CP_LL = SrcWng_ParameterTypeData%chord_CP_LL + DstWng_ParameterTypeData%s_CP = SrcWng_ParameterTypeData%s_CP ENDIF DstWng_ParameterTypeData%iRotor = SrcWng_ParameterTypeData%iRotor IF (ALLOCATED(SrcWng_ParameterTypeData%AFindx)) THEN @@ -1227,20 +1214,17 @@ SUBROUTINE FVW_DestroyWng_ParameterType( Wng_ParameterTypeData, ErrStat, ErrMsg ! ErrStat = ErrID_None ErrMsg = "" -IF (ALLOCATED(Wng_ParameterTypeData%Chord)) THEN - DEALLOCATE(Wng_ParameterTypeData%Chord) +IF (ALLOCATED(Wng_ParameterTypeData%chord_LL)) THEN + DEALLOCATE(Wng_ParameterTypeData%chord_LL) +ENDIF +IF (ALLOCATED(Wng_ParameterTypeData%chord_CP)) THEN + DEALLOCATE(Wng_ParameterTypeData%chord_CP) ENDIF IF (ALLOCATED(Wng_ParameterTypeData%s_LL)) THEN DEALLOCATE(Wng_ParameterTypeData%s_LL) ENDIF -IF (ALLOCATED(Wng_ParameterTypeData%s_CP_LL)) THEN - DEALLOCATE(Wng_ParameterTypeData%s_CP_LL) -ENDIF -IF (ALLOCATED(Wng_ParameterTypeData%chord_LL)) THEN - DEALLOCATE(Wng_ParameterTypeData%chord_LL) -ENDIF -IF (ALLOCATED(Wng_ParameterTypeData%chord_CP_LL)) THEN - DEALLOCATE(Wng_ParameterTypeData%chord_CP_LL) +IF (ALLOCATED(Wng_ParameterTypeData%s_CP)) THEN + DEALLOCATE(Wng_ParameterTypeData%s_CP) ENDIF IF (ALLOCATED(Wng_ParameterTypeData%AFindx)) THEN DEALLOCATE(Wng_ParameterTypeData%AFindx) @@ -1285,30 +1269,25 @@ SUBROUTINE FVW_PackWng_ParameterType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSta Re_BufSz = 0 Db_BufSz = 0 Int_BufSz = 0 - 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 + Int_BufSz = Int_BufSz + 1 ! chord_LL allocated yes/no + IF ( ALLOCATED(InData%chord_LL) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! 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 ! chord_CP allocated yes/no + IF ( ALLOCATED(InData%chord_CP) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! chord_CP upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%chord_CP) ! chord_CP END IF Int_BufSz = Int_BufSz + 1 ! s_LL allocated yes/no IF ( ALLOCATED(InData%s_LL) ) THEN Int_BufSz = Int_BufSz + 2*1 ! 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 ! s_CP_LL allocated yes/no - IF ( ALLOCATED(InData%s_CP_LL) ) THEN - Int_BufSz = Int_BufSz + 2*1 ! 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_LL allocated yes/no - IF ( ALLOCATED(InData%chord_LL) ) THEN - Int_BufSz = Int_BufSz + 2*1 ! 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 ! chord_CP_LL allocated yes/no - IF ( ALLOCATED(InData%chord_CP_LL) ) THEN - Int_BufSz = Int_BufSz + 2*1 ! chord_CP_LL upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%chord_CP_LL) ! chord_CP_LL + Int_BufSz = Int_BufSz + 1 ! s_CP allocated yes/no + IF ( ALLOCATED(InData%s_CP) ) THEN + Int_BufSz = Int_BufSz + 2*1 ! s_CP upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%s_CP) ! s_CP END IF Int_BufSz = Int_BufSz + 1 ! iRotor Int_BufSz = Int_BufSz + 1 ! AFindx allocated yes/no @@ -1349,78 +1328,63 @@ SUBROUTINE FVW_PackWng_ParameterType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSta Db_Xferred = 1 Int_Xferred = 1 - 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 - - DO i1 = LBOUND(InData%Chord,1), UBOUND(InData%Chord,1) - ReKiBuf(Re_Xferred) = InData%Chord(i1) - Re_Xferred = Re_Xferred + 1 - END DO - END IF - IF ( .NOT. ALLOCATED(InData%s_LL) ) THEN + 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%s_LL,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%s_LL,1) + IntKiBuf( Int_Xferred ) = LBOUND(InData%chord_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%chord_LL,1) Int_Xferred = Int_Xferred + 2 - DO i1 = LBOUND(InData%s_LL,1), UBOUND(InData%s_LL,1) - ReKiBuf(Re_Xferred) = InData%s_LL(i1) + DO i1 = LBOUND(InData%chord_LL,1), UBOUND(InData%chord_LL,1) + ReKiBuf(Re_Xferred) = InData%chord_LL(i1) Re_Xferred = Re_Xferred + 1 END DO END IF - IF ( .NOT. ALLOCATED(InData%s_CP_LL) ) THEN + IF ( .NOT. ALLOCATED(InData%chord_CP) ) 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) + IntKiBuf( Int_Xferred ) = LBOUND(InData%chord_CP,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%chord_CP,1) Int_Xferred = Int_Xferred + 2 - DO i1 = LBOUND(InData%s_CP_LL,1), UBOUND(InData%s_CP_LL,1) - ReKiBuf(Re_Xferred) = InData%s_CP_LL(i1) + DO i1 = LBOUND(InData%chord_CP,1), UBOUND(InData%chord_CP,1) + ReKiBuf(Re_Xferred) = InData%chord_CP(i1) Re_Xferred = Re_Xferred + 1 END DO END IF - IF ( .NOT. ALLOCATED(InData%chord_LL) ) THEN + 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%chord_LL,1) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%chord_LL,1) + IntKiBuf( Int_Xferred ) = LBOUND(InData%s_LL,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%s_LL,1) Int_Xferred = Int_Xferred + 2 - DO i1 = LBOUND(InData%chord_LL,1), UBOUND(InData%chord_LL,1) - ReKiBuf(Re_Xferred) = InData%chord_LL(i1) + DO i1 = LBOUND(InData%s_LL,1), UBOUND(InData%s_LL,1) + ReKiBuf(Re_Xferred) = InData%s_LL(i1) Re_Xferred = Re_Xferred + 1 END DO END IF - IF ( .NOT. ALLOCATED(InData%chord_CP_LL) ) THEN + IF ( .NOT. ALLOCATED(InData%s_CP) ) 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) + IntKiBuf( Int_Xferred ) = LBOUND(InData%s_CP,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%s_CP,1) Int_Xferred = Int_Xferred + 2 - DO i1 = LBOUND(InData%chord_CP_LL,1), UBOUND(InData%chord_CP_LL,1) - ReKiBuf(Re_Xferred) = InData%chord_CP_LL(i1) + DO i1 = LBOUND(InData%s_CP,1), UBOUND(InData%s_CP,1) + ReKiBuf(Re_Xferred) = InData%s_CP(i1) Re_Xferred = Re_Xferred + 1 END DO END IF @@ -1493,93 +1457,75 @@ SUBROUTINE FVW_UnPackWng_ParameterType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, Err Re_Xferred = 1 Db_Xferred = 1 Int_Xferred = 1 - 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 - DO i1 = LBOUND(OutData%Chord,1), UBOUND(OutData%Chord,1) - OutData%Chord(i1) = ReKiBuf(Re_Xferred) - Re_Xferred = Re_Xferred + 1 - END DO - END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! s_LL not allocated + 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 - IF (ALLOCATED(OutData%s_LL)) DEALLOCATE(OutData%s_LL) - ALLOCATE(OutData%s_LL(i1_l:i1_u),STAT=ErrStat2) + IF (ALLOCATED(OutData%chord_LL)) DEALLOCATE(OutData%chord_LL) + ALLOCATE(OutData%chord_LL(i1_l:i1_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%s_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%chord_LL.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i1 = LBOUND(OutData%s_LL,1), UBOUND(OutData%s_LL,1) - OutData%s_LL(i1) = ReKiBuf(Re_Xferred) + DO i1 = LBOUND(OutData%chord_LL,1), UBOUND(OutData%chord_LL,1) + OutData%chord_LL(i1) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! s_CP_LL not allocated + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! chord_CP 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%s_CP_LL)) DEALLOCATE(OutData%s_CP_LL) - ALLOCATE(OutData%s_CP_LL(i1_l:i1_u),STAT=ErrStat2) + IF (ALLOCATED(OutData%chord_CP)) DEALLOCATE(OutData%chord_CP) + ALLOCATE(OutData%chord_CP(i1_l:i1_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%s_CP_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%chord_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i1 = LBOUND(OutData%s_CP_LL,1), UBOUND(OutData%s_CP_LL,1) - OutData%s_CP_LL(i1) = ReKiBuf(Re_Xferred) + DO i1 = LBOUND(OutData%chord_CP,1), UBOUND(OutData%chord_CP,1) + OutData%chord_CP(i1) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! chord_LL not allocated + 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 - IF (ALLOCATED(OutData%chord_LL)) DEALLOCATE(OutData%chord_LL) - ALLOCATE(OutData%chord_LL(i1_l:i1_u),STAT=ErrStat2) + IF (ALLOCATED(OutData%s_LL)) DEALLOCATE(OutData%s_LL) + ALLOCATE(OutData%s_LL(i1_l:i1_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%chord_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%s_LL.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i1 = LBOUND(OutData%chord_LL,1), UBOUND(OutData%chord_LL,1) - OutData%chord_LL(i1) = ReKiBuf(Re_Xferred) + DO i1 = LBOUND(OutData%s_LL,1), UBOUND(OutData%s_LL,1) + OutData%s_LL(i1) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! chord_CP_LL not allocated + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! s_CP 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_CP_LL)) DEALLOCATE(OutData%chord_CP_LL) - ALLOCATE(OutData%chord_CP_LL(i1_l:i1_u),STAT=ErrStat2) + IF (ALLOCATED(OutData%s_CP)) DEALLOCATE(OutData%s_CP) + ALLOCATE(OutData%s_CP(i1_l:i1_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%chord_CP_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%s_CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i1 = LBOUND(OutData%chord_CP_LL,1), UBOUND(OutData%chord_CP_LL,1) - OutData%chord_CP_LL(i1) = ReKiBuf(Re_Xferred) + DO i1 = LBOUND(OutData%s_CP,1), UBOUND(OutData%s_CP,1) + OutData%s_CP(i1) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END IF @@ -3701,19 +3647,19 @@ SUBROUTINE FVW_CopyWng_MiscVarType( SrcWng_MiscVarTypeData, DstWng_MiscVarTypeDa END IF DstWng_MiscVarTypeData%r_LL = SrcWng_MiscVarTypeData%r_LL ENDIF -IF (ALLOCATED(SrcWng_MiscVarTypeData%CP_LL)) THEN - i1_l = LBOUND(SrcWng_MiscVarTypeData%CP_LL,1) - i1_u = UBOUND(SrcWng_MiscVarTypeData%CP_LL,1) - i2_l = LBOUND(SrcWng_MiscVarTypeData%CP_LL,2) - i2_u = UBOUND(SrcWng_MiscVarTypeData%CP_LL,2) - IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%CP_LL)) THEN - ALLOCATE(DstWng_MiscVarTypeData%CP_LL(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) +IF (ALLOCATED(SrcWng_MiscVarTypeData%CP)) THEN + i1_l = LBOUND(SrcWng_MiscVarTypeData%CP,1) + i1_u = UBOUND(SrcWng_MiscVarTypeData%CP,1) + i2_l = LBOUND(SrcWng_MiscVarTypeData%CP,2) + i2_u = UBOUND(SrcWng_MiscVarTypeData%CP,2) + IF (.NOT. ALLOCATED(DstWng_MiscVarTypeData%CP)) THEN + ALLOCATE(DstWng_MiscVarTypeData%CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%CP_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_MiscVarTypeData%CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstWng_MiscVarTypeData%CP_LL = SrcWng_MiscVarTypeData%CP_LL + DstWng_MiscVarTypeData%CP = SrcWng_MiscVarTypeData%CP ENDIF IF (ALLOCATED(SrcWng_MiscVarTypeData%Tang)) THEN i1_l = LBOUND(SrcWng_MiscVarTypeData%Tang,1) @@ -4198,8 +4144,8 @@ SUBROUTINE FVW_DestroyWng_MiscVarType( Wng_MiscVarTypeData, ErrStat, ErrMsg ) IF (ALLOCATED(Wng_MiscVarTypeData%r_LL)) THEN DEALLOCATE(Wng_MiscVarTypeData%r_LL) ENDIF -IF (ALLOCATED(Wng_MiscVarTypeData%CP_LL)) THEN - DEALLOCATE(Wng_MiscVarTypeData%CP_LL) +IF (ALLOCATED(Wng_MiscVarTypeData%CP)) THEN + DEALLOCATE(Wng_MiscVarTypeData%CP) ENDIF IF (ALLOCATED(Wng_MiscVarTypeData%Tang)) THEN DEALLOCATE(Wng_MiscVarTypeData%Tang) @@ -4363,10 +4309,10 @@ SUBROUTINE FVW_PackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, Int_BufSz = Int_BufSz + 2*3 ! r_LL upper/lower bounds for each dimension Re_BufSz = Re_BufSz + SIZE(InData%r_LL) ! r_LL END IF - Int_BufSz = Int_BufSz + 1 ! CP_LL allocated yes/no - IF ( ALLOCATED(InData%CP_LL) ) THEN - Int_BufSz = Int_BufSz + 2*2 ! CP_LL upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%CP_LL) ! CP_LL + Int_BufSz = Int_BufSz + 1 ! CP allocated yes/no + IF ( ALLOCATED(InData%CP) ) THEN + Int_BufSz = Int_BufSz + 2*2 ! CP upper/lower bounds for each dimension + Re_BufSz = Re_BufSz + SIZE(InData%CP) ! CP END IF Int_BufSz = Int_BufSz + 1 ! Tang allocated yes/no IF ( ALLOCATED(InData%Tang) ) THEN @@ -4704,22 +4650,22 @@ SUBROUTINE FVW_PackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, END DO END DO END IF - IF ( .NOT. ALLOCATED(InData%CP_LL) ) THEN + IF ( .NOT. ALLOCATED(InData%CP) ) 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) + IntKiBuf( Int_Xferred ) = LBOUND(InData%CP,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CP,1) Int_Xferred = Int_Xferred + 2 - IntKiBuf( Int_Xferred ) = LBOUND(InData%CP_LL,2) - IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CP_LL,2) + IntKiBuf( Int_Xferred ) = LBOUND(InData%CP,2) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%CP,2) Int_Xferred = Int_Xferred + 2 - DO i2 = LBOUND(InData%CP_LL,2), UBOUND(InData%CP_LL,2) - DO i1 = LBOUND(InData%CP_LL,1), UBOUND(InData%CP_LL,1) - ReKiBuf(Re_Xferred) = InData%CP_LL(i1,i2) + DO i2 = LBOUND(InData%CP,2), UBOUND(InData%CP,2) + DO i1 = LBOUND(InData%CP,1), UBOUND(InData%CP,1) + ReKiBuf(Re_Xferred) = InData%CP(i1,i2) Re_Xferred = Re_Xferred + 1 END DO END DO @@ -5548,7 +5494,7 @@ SUBROUTINE FVW_UnPackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSt END DO END DO END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CP_LL not allocated + IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! CP not allocated Int_Xferred = Int_Xferred + 1 ELSE Int_Xferred = Int_Xferred + 1 @@ -5558,15 +5504,15 @@ SUBROUTINE FVW_UnPackWng_MiscVarType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrSt i2_l = IntKiBuf( Int_Xferred ) i2_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),STAT=ErrStat2) + IF (ALLOCATED(OutData%CP)) DEALLOCATE(OutData%CP) + ALLOCATE(OutData%CP(i1_l:i1_u,i2_l:i2_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%CP_LL.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%CP.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i2 = LBOUND(OutData%CP_LL,2), UBOUND(OutData%CP_LL,2) - DO i1 = LBOUND(OutData%CP_LL,1), UBOUND(OutData%CP_LL,1) - OutData%CP_LL(i1,i2) = ReKiBuf(Re_Xferred) + DO i2 = LBOUND(OutData%CP,2), UBOUND(OutData%CP,2) + DO i1 = LBOUND(OutData%CP,1), UBOUND(OutData%CP,1) + OutData%CP(i1,i2) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END DO @@ -9490,17 +9436,17 @@ SUBROUTINE FVW_CopyWng_InitInputType( SrcWng_InitInputTypeData, DstWng_InitInput END IF DstWng_InitInputTypeData%AFindx = SrcWng_InitInputTypeData%AFindx ENDIF -IF (ALLOCATED(SrcWng_InitInputTypeData%Chord)) THEN - i1_l = LBOUND(SrcWng_InitInputTypeData%Chord,1) - i1_u = UBOUND(SrcWng_InitInputTypeData%Chord,1) - IF (.NOT. ALLOCATED(DstWng_InitInputTypeData%Chord)) THEN - ALLOCATE(DstWng_InitInputTypeData%Chord(i1_l:i1_u),STAT=ErrStat2) +IF (ALLOCATED(SrcWng_InitInputTypeData%chord)) THEN + i1_l = LBOUND(SrcWng_InitInputTypeData%chord,1) + i1_u = UBOUND(SrcWng_InitInputTypeData%chord,1) + IF (.NOT. ALLOCATED(DstWng_InitInputTypeData%chord)) THEN + ALLOCATE(DstWng_InitInputTypeData%chord(i1_l:i1_u),STAT=ErrStat2) IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_InitInputTypeData%Chord.', ErrStat, ErrMsg,RoutineName) + CALL SetErrStat(ErrID_Fatal, 'Error allocating DstWng_InitInputTypeData%chord.', ErrStat, ErrMsg,RoutineName) RETURN END IF END IF - DstWng_InitInputTypeData%Chord = SrcWng_InitInputTypeData%Chord + DstWng_InitInputTypeData%chord = SrcWng_InitInputTypeData%chord ENDIF IF (ALLOCATED(SrcWng_InitInputTypeData%RElm)) THEN i1_l = LBOUND(SrcWng_InitInputTypeData%RElm,1) @@ -9531,8 +9477,8 @@ SUBROUTINE FVW_DestroyWng_InitInputType( Wng_InitInputTypeData, ErrStat, ErrMsg IF (ALLOCATED(Wng_InitInputTypeData%AFindx)) THEN DEALLOCATE(Wng_InitInputTypeData%AFindx) ENDIF -IF (ALLOCATED(Wng_InitInputTypeData%Chord)) THEN - DEALLOCATE(Wng_InitInputTypeData%Chord) +IF (ALLOCATED(Wng_InitInputTypeData%chord)) THEN + DEALLOCATE(Wng_InitInputTypeData%chord) ENDIF IF (ALLOCATED(Wng_InitInputTypeData%RElm)) THEN DEALLOCATE(Wng_InitInputTypeData%RElm) @@ -9579,10 +9525,10 @@ SUBROUTINE FVW_PackWng_InitInputType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSta 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*1 ! Chord upper/lower bounds for each dimension - Re_BufSz = Re_BufSz + SIZE(InData%Chord) ! Chord + 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 @@ -9639,18 +9585,18 @@ SUBROUTINE FVW_PackWng_InitInputType( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrSta END DO END DO END IF - IF ( .NOT. ALLOCATED(InData%Chord) ) THEN + 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) + IntKiBuf( Int_Xferred ) = LBOUND(InData%chord,1) + IntKiBuf( Int_Xferred + 1) = UBOUND(InData%chord,1) Int_Xferred = Int_Xferred + 2 - DO i1 = LBOUND(InData%Chord,1), UBOUND(InData%Chord,1) - ReKiBuf(Re_Xferred) = InData%Chord(i1) + DO i1 = LBOUND(InData%chord,1), UBOUND(InData%chord,1) + ReKiBuf(Re_Xferred) = InData%chord(i1) Re_Xferred = Re_Xferred + 1 END DO END IF @@ -9728,21 +9674,21 @@ SUBROUTINE FVW_UnPackWng_InitInputType( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, Err END DO END DO END IF - IF ( IntKiBuf( Int_Xferred ) == 0 ) THEN ! Chord not allocated + 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 (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) + CALL SetErrStat(ErrID_Fatal, 'Error allocating OutData%chord.', ErrStat, ErrMsg,RoutineName) RETURN END IF - DO i1 = LBOUND(OutData%Chord,1), UBOUND(OutData%Chord,1) - OutData%Chord(i1) = ReKiBuf(Re_Xferred) + DO i1 = LBOUND(OutData%chord,1), UBOUND(OutData%chord,1) + OutData%chord(i1) = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 END DO END IF diff --git a/modules/aerodyn/src/FVW_Wings.f90 b/modules/aerodyn/src/FVW_Wings.f90 index aea15465b..47dda876d 100644 --- a/modules/aerodyn/src/FVW_Wings.f90 +++ b/modules/aerodyn/src/FVW_Wings.f90 @@ -60,9 +60,9 @@ end subroutine Meshing !---------------------------------------------------------------------------------------------------------------------------------- !> 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 + !! - s_CP : Dimensionless spanwise coordinate of LL CP + !! - chord_LL : chord on LL nodes + !! - chord_CP : chord on LL control points (CP) subroutine Wings_Panelling_Init(Meshes, p, m, ErrStat, ErrMsg ) type(MeshType), dimension(:), intent(in ) :: Meshes !< Wings mesh type(FVW_ParameterType), intent(inout) :: p !< Parameters @@ -100,14 +100,14 @@ subroutine Wings_Panelling_Init(Meshes, p, m, ErrStat, ErrMsg ) endif do iSpan = 1, p%W(iW)%nSpan+1 p%W(iW)%s_LL (iSpan) = s_in(iSpan) - p%W(iW)%chord_LL(iSpan) = p%W(iW)%chord(iSpan) + !p%W(iW)%chord_LL(iSpan) = p%W(iW)%chord(iSpan) enddo ! --- Control points spanwise location ! NOTE: we use the cos approximation of VanGarrel. For equispacing, it returns mid point ! otherwise, points are slightly closer to panels that are shorter - !call Meshing('middle' , p%W(iW)%s_LL(:), p%W(iW)%nSpan, p%s_CP_LL(:)) - call Meshing('fullcosineapprox' , p%W(iW)%s_LL(:), p%W(iW)%nSpan, p%W(iW)%s_CP_LL(:)) - call InterpArray(p%W(iW)%s_LL(:), p%W(iW)%chord_LL(:), p%W(iW)%s_CP_LL(:), p%W(iW)%chord_CP_LL(:)) + !call Meshing('middle' , p%W(iW)%s_LL(:), p%W(iW)%nSpan, p%s_CP(:)) + call Meshing('fullcosineapprox' , p%W(iW)%s_LL(:), p%W(iW)%nSpan, p%W(iW)%s_CP(:)) + call InterpArray(p%W(iW)%s_LL(:), p%W(iW)%chord_LL(:), p%W(iW)%s_CP(:), p%W(iW)%chord_CP(:)) deallocate(s_in) enddo @@ -117,7 +117,7 @@ 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) + !! - CP : 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" - @@ -211,21 +211,21 @@ subroutine Wings_Panelling(Meshes, p, m, ErrStat, ErrMsg ) enddo endif - ! --- Position of control points CP_LL + ! --- Position of control points CP ! 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 InterpArray(p%W(iW)%s_LL(:), m%W(iW)%r_LL(1,:,1), p%W(iW)%s_CP_LL(:), m%W(iW)%CP_LL(1,:)) - call InterpArray(p%W(iW)%s_LL(:), m%W(iW)%r_LL(2,:,1), p%W(iW)%s_CP_LL(:), m%W(iW)%CP_LL(2,:)) - call InterpArray(p%W(iW)%s_LL(:), m%W(iW)%r_LL(3,:,1), p%W(iW)%s_CP_LL(:), m%W(iW)%CP_LL(3,:)) + call InterpArray(p%W(iW)%s_LL(:), m%W(iW)%r_LL(1,:,1), p%W(iW)%s_CP(:), m%W(iW)%CP(1,:)) + call InterpArray(p%W(iW)%s_LL(:), m%W(iW)%r_LL(2,:,1), p%W(iW)%s_CP(:), m%W(iW)%CP(2,:)) + call InterpArray(p%W(iW)%s_LL(:), m%W(iW)%r_LL(3,:,1), p%W(iW)%s_CP(:), m%W(iW)%CP(3,:)) enddo ! --- Structural velocity on LL CP and Nodes ! TODO: difference meshes in/LL do iW = 1,p%nWings - call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(1,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_CP(1,:)) - call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(2,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_CP(2,:)) - call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(3,:) ,p%W(iW)%s_CP_LL(:), m%W(iW)%Vstr_CP(3,:)) + call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(1,:) ,p%W(iW)%s_CP(:), m%W(iW)%Vstr_CP(1,:)) + call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(2,:) ,p%W(iW)%s_CP(:), m%W(iW)%Vstr_CP(2,:)) + call InterpArray(p%W(iW)%s_LL(:), Meshes(iW)%TranslationVel(3,:) ,p%W(iW)%s_CP(:), m%W(iW)%Vstr_CP(3,:)) enddo !do iW = 1,p%nWings ! m%W(iW)%Vstr_LL(1:3,:)= Meshes(iW)%TranslationVel(1:3,:) @@ -378,7 +378,7 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc !--- Induction on the lifting line control point or nodes ! Set induced velocity from Known wake only (after iNWStart+1) - ! if InductionAtCP : In: m%W%CP_LL, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) + ! if InductionAtCP : In: m%W%CP, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) ! if not InductionAtCP : In: m%W%r_LL, Out:m%W%Vind_CP (interp/extrap) and m%W%Vind_LL call LiftingLineInducedVelocities(p, x, p%InductionAtCP, p%iNWStart+1, m, ErrStat2, ErrMsg2); if(Failed()) return; @@ -427,8 +427,8 @@ subroutine Wings_ComputeCirculationPolarData(z, z_prev, p, x, m, AFInfo, GammaSc kCP2=1 do iWCP=1,p%nWings nCPs=p%W(iWCP)%nSpan - !call ui_quad_n1(m%W(iWCP)%CP_LL(1:3,1:nCPs), nCPs, P1, P2, P3, P4, Gamm, p%RegFunction, x%W(iW)%Eps_NW(1,iSpan,iDepth), Vvar(1:3,1:nCPs,iWCP)) - call ui_quad_n1(m%W(iWCP)%CP_LL(1:3,1:nCPs), nCPs, P1, P2, P3, P4, Gamm, p%RegFunction, x%W(iW)%Eps_NW(1,iSpan,iDepth), Vvar(1:3, kCP2:kCP2+nCPs-1)) + !call ui_quad_n1(m%W(iWCP)%CP(1:3,1:nCPs), nCPs, P1, P2, P3, P4, Gamm, p%RegFunction, x%W(iW)%Eps_NW(1,iSpan,iDepth), Vvar(1:3,1:nCPs,iWCP)) + call ui_quad_n1(m%W(iWCP)%CP(1:3,1:nCPs), nCPs, P1, P2, P3, P4, Gamm, p%RegFunction, x%W(iW)%Eps_NW(1,iSpan,iDepth), Vvar(1:3, kCP2:kCP2+nCPs-1)) kCP2=kCP2+nCPs enddo ! Wings CP enddo ! Depth @@ -554,7 +554,7 @@ subroutine CirculationFromPolarData(Gamma_LL, p, m, AFInfo, ErrStat, ErrMsg) Vrel_norm = TwoNorm(Vrel) alpha = atan2(dot_product(Vrel,N) , dot_product(Vrel,Tc) ) ! [rad] - Re = p%W(iW)%Chord(icp) * Vrel_norm / p%KinVisc ! Reynolds number (not in Million) + Re = p%W(iW)%chord_CP(icp) * Vrel_norm / p%KinVisc ! Reynolds number (not in Million) !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. From b4cb6a9c56b8a65d52d55bc4013472bf91d382be Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 9 Jul 2021 13:59:38 -0600 Subject: [PATCH 16/24] FVW: Dynamic stall on wake (fixed mapping before UA) --- modules/aerodyn/src/FVW.f90 | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index d5a0b3551..d64ac5cf6 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -749,6 +749,10 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m z_guess%W(iW)%Gamma_LL = z%W(iW)%Gamma_LL ! We use as guess the circulation from the previous time step (see above) enddo 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 (need to be set for UA, Uind on LL) (and position but irrelevant) + ! Changes: x only + 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 ! Compute UA inputs at t+DTaero and integrate UA states between t and t+dtAero if (m%UA_Flag) then call CalculateInputsAndOtherStatesForUA(2, uInterp, p, x, xd, z, OtherState, AFInfo, m, ErrStat2, ErrMsg2); if(Failed()) return @@ -756,15 +760,14 @@ subroutine FVW_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, AFInfo, m ! Compute unsteady Gamma based on UA Cl if (p%DStallOnWake .and. p%CirculationMethod/=idCircPrescribed) then call UA_SetGammaDyn(t, uInterp, p, x, xd, OtherState, m, AFInfo, z, ErrStat, ErrMsg) + ! Updating circulation of near wake panel again (and position but irrelevant) + ! Changes: x only + 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 end if end if - ! Updating circulation of near wake panel (and position but irrelevant) - ! Changes: x only - 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') ! --- Fake handling of ground effect (ensure vorticies above ground) call FakeGroundEffect(p, x, m, ErrStat, ErrMsg) @@ -1606,7 +1609,7 @@ subroutine CalculateInputsAndOtherStatesForUA(InputIndex, u, p, x, xd, z, OtherS ! --- Induction on the lifting line control points ! if InductionAtCP : In: m%W%CP, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) ! if not InductionAtCP : In: m%W%r_LL, Out:m%W%Vind_CP (interp/extrap) and m%W%Vind_LL - call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'Ca;lcultateInputsAndOtherStatesForUA'); if (ErrStat >= AbortErrLev) return + call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'CalculateInputsAndOtherStatesForUA'); if (ErrStat >= AbortErrLev) return if (p%InductionAtCP) then if (m%nNW<=1) then do iW = 1,p%nWings @@ -1697,15 +1700,13 @@ subroutine UA_SetGammaDyn(t, u, p, x, xd, OtherState, m, AFInfo, z, ErrStat, Err real(ReKi) :: Cl_dyn, Cl_dyn_prev, Cl_dyn_avg real(ReKi) :: Gamma_dyn, Gamma_dyn_prev, Gamma_dyn_avg type(UA_InputType), pointer :: u_UA ! Alias to shorten notations - integer(IntKi), parameter :: InputIndex=1 ! we will always use values at t+dt in this routine + integer(IntKi), parameter :: InputIndex=2 ! we will always use values at t+dt in this routine integer(intKi) :: iW, j ! loop counter on wings and nodes integer(intKi) :: errStat2 character(ErrMsgLen) :: errMsg2 ErrStat = 0 ErrMsg = "" - print*,'------------------------------------------------------------------' - print*,'t ',t do iW=1,p%nWings ! Gamma_LL is expressed at CP, so we average the dynamic gamma from both nodes @@ -1721,9 +1722,6 @@ subroutine UA_SetGammaDyn(t, u, p, x, xd, OtherState, m, AFInfo, z, ErrStat, Err Gamma_dyn_avg = (Gamma_dyn+Gamma_dyn_prev)*0.5_ReKi Gamma_dyn_prev = Gamma_dyn z%W(iW)%Gamma_LL(j-1) = Gamma_dyn_avg - !print*,z%W(iW)%Gamma_LL(j-1), Gamma_dyn, Gamma_dyn_avg - !y%WriteOutput( OutIdx ) = 0.5_ReKi * p%BEMT%chord(IdxNode,IdxBlade) * m%BEMT_y%Vrel(IdxNode,IdxBlade) * m%BEMT_y%Cl(IdxNode,IdxBlade) ! "Gam" [m^2/s] - !y%WriteOutput( OutIdx ) = 0.5_ReKi * p_AD%FVW%W(iW)%Chord(IdxNode) * m_AD%FVW%W(iW)%BN_Vrel(IdxNode) * m_AD%FVW%W(iW)%BN_Cl(IdxNode) ! "Gam" [m^2/s] enddo enddo ! iW, Loop on wings end subroutine UA_SetGammaDyn From 5549243b7beb1972be039ebc35e0d8b055557b96 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 22 Jul 2021 09:48:53 -0600 Subject: [PATCH 17/24] UA: starting theory doc --- docs/source/user/aerodyn/theory.rst | 46 +++++++++++++++++++++++++++++ docs/source/user/api_change.rst | 1 + 2 files changed, 47 insertions(+) diff --git a/docs/source/user/aerodyn/theory.rst b/docs/source/user/aerodyn/theory.rst index 39b383dc3..3df718077 100644 --- a/docs/source/user/aerodyn/theory.rst +++ b/docs/source/user/aerodyn/theory.rst @@ -28,3 +28,49 @@ Eames tower shadow model (**TwrShadow=2**) is given by: \exp{\left( -\frac{1}{2} \left(\frac{ \overline{y}}{ TI \: \overline{x} } \right)^2 \right) } where :math:`TI` is the turbulence intensity at the tower node. + + + + +.. _AD_UA: + +Unsteady aerodynamics +--------------------- + +Beddoes-Leishman type models (UAMod=2,3) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Beddoes-Leishman type dynamic stall models are currently described in the document: +The Unsteady Aerodynamics Module for FAST 8, from Rick Damiani and Greg Hayman (2017) + + +Beddoes-Leishman state space models (UAMod=4,5) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TODO + + +Oye model (UAMod=6) +~~~~~~~~~~~~~~~~~~~ + + +See Hansen book + +Boeing-Vertol model (UAMod=7) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Boeing-Vertol is used mentioned in the following paper: The Development of CACTUS, a Wind and Marine Turbine Performance Simulation Code from Jonathan C. Murray and Matthew Barone (2011). +The documentation presented here was inspired from the implementation done in the vortex code CACTUS. + + + +.. math:: + + \alpha_{dyn} = \alpha_qs - k_1 \gamma \sqrt{\left| \frac{c\dot{\alpha}}{2U}\right|} + + + + + + + diff --git a/docs/source/user/api_change.rst b/docs/source/user/api_change.rst index 382121af5..ecb15451d 100644 --- a/docs/source/user/api_change.rst +++ b/docs/source/user/api_change.rst @@ -61,6 +61,7 @@ AirFoilTables 12\* alphaUpper 5.0 alp AirFoilTables 13\* alphaLower \-3.0 alphaLower ! Angle of attack at lower boundary of fully-attached region. (deg) [used only when UAMod=5] ! THIS IS AN OPTIONAL LINE; if omitted, it will be calculated from the polar data AirFoilTables 42\* UACutout_delta "DEFAULT" UACutout_delta ! Delta angle of attack below UACutout where unsteady aerodynamics begin to turn off (blend with steady solution) (deg) [Specifying the string "Default" sets UACutout_delta to 5 degrees] ! THIS IS AN OPTIONAL LINE; if omitted, it will be set to its default value ============================================= ==== =============== ======================================================================================================================================================================================================== + \*non-comment line count, excluding lines contained if NumCoords is not 0, and including all OPTIONAL lines in the UA coefficients table. From a58aa9c39f0daabe11b33a5767ebeca70d005777 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 3 Aug 2021 15:44:39 -0600 Subject: [PATCH 18/24] UA: adding relative thickness to polars --- modules/aerodyn/src/AirfoilInfo.f90 | 3 +++ modules/aerodyn/src/AirfoilInfo_Registry.txt | 1 + modules/aerodyn/src/AirfoilInfo_Types.f90 | 7 +++++++ modules/aerodyn/src/UnsteadyAero.f90 | 17 ++++++++--------- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/modules/aerodyn/src/AirfoilInfo.f90 b/modules/aerodyn/src/AirfoilInfo.f90 index 92e9ceb5e..247eb65b3 100644 --- a/modules/aerodyn/src/AirfoilInfo.f90 +++ b/modules/aerodyn/src/AirfoilInfo.f90 @@ -423,6 +423,9 @@ SUBROUTINE ReadAFfile ( InitInp, NumCoefsIn, p, ErrStat, ErrMsg, UnEc ) CALL ParseVarWDefault ( FileInfo, CurLine, 'InterpOrd', p%InterpOrd, DefaultInterpOrd, ErrStat2, ErrMsg2, UnEc ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ! RelThickness, default is 0.2 if user doesn't know it, only used for Boing-Vertol UA model = 7 + CALL ParseVarWDefault ( FileInfo, CurLine, 'RelThickness', p%RelThickness, 0.2, ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! NonDimArea is currently unused by AirfoilInfo or codes using AirfoilInfo. GJH 9/13/2017 CALL ParseVar ( FileInfo, CurLine, 'NonDimArea', p%NonDimArea, ErrStat2, ErrMsg2, UnEc ) diff --git a/modules/aerodyn/src/AirfoilInfo_Registry.txt b/modules/aerodyn/src/AirfoilInfo_Registry.txt index 6f1b1e8c4..853cd3e29 100644 --- a/modules/aerodyn/src/AirfoilInfo_Registry.txt +++ b/modules/aerodyn/src/AirfoilInfo_Registry.txt @@ -148,6 +148,7 @@ typedef ^ ^ INTEGER ColUAf typedef ^ ^ INTEGER AFTabMod - - - "Interpolation method for multiple airfoil tables {1 = 1D on AoA (only first table is used); 2 = 2D on AoA and Re; 3 = 2D on AoA and UserProp}" - typedef ^ ^ ReKi secondVals {:} - - "The values of the 2nd dependent variable when using multiple airfoil tables (Re or UserProp, saved in an array so that the logic in the interpolation scheme is cleaner)" - typedef ^ ^ IntKi InterpOrd - - - "Interpolation order" - +typedef ^ ^ ReKi RelThickness - - - "Relative thickness of airfoil thickness/chord" - typedef ^ ^ ReKi NonDimArea - - - "The non-dimensional area of the airfoil (area/chord^2) [unused]" - typedef ^ ^ IntKi NumCoords - - - "The number of coordinates which define the airfoil shape" - typedef ^ ^ ReKi X_Coord {:} - - "X-coordinate for the airfoil shape [unused]" - diff --git a/modules/aerodyn/src/AirfoilInfo_Types.f90 b/modules/aerodyn/src/AirfoilInfo_Types.f90 index 933e7ea0f..4eabaad1b 100644 --- a/modules/aerodyn/src/AirfoilInfo_Types.f90 +++ b/modules/aerodyn/src/AirfoilInfo_Types.f90 @@ -167,6 +167,7 @@ MODULE AirfoilInfo_Types INTEGER(IntKi) :: AFTabMod !< Interpolation method for multiple airfoil tables {1 = 1D on AoA (only first table is used); 2 = 2D on AoA and Re; 3 = 2D on AoA and UserProp} [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: secondVals !< The values of the 2nd dependent variable when using multiple airfoil tables (Re or UserProp, saved in an array so that the logic in the interpolation scheme is cleaner) [-] INTEGER(IntKi) :: InterpOrd !< Interpolation order [-] + REAL(ReKi) :: RelThickness !< Relative thickness of airfoil thickness/chord [-] REAL(ReKi) :: NonDimArea !< The non-dimensional area of the airfoil (area/chord^2) [unused] [-] INTEGER(IntKi) :: NumCoords !< The number of coordinates which define the airfoil shape [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: X_Coord !< X-coordinate for the airfoil shape [unused] [-] @@ -1794,6 +1795,7 @@ SUBROUTINE AFI_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%secondVals = SrcParamData%secondVals ENDIF DstParamData%InterpOrd = SrcParamData%InterpOrd + DstParamData%RelThickness = SrcParamData%RelThickness DstParamData%NonDimArea = SrcParamData%NonDimArea DstParamData%NumCoords = SrcParamData%NumCoords IF (ALLOCATED(SrcParamData%X_Coord)) THEN @@ -1914,6 +1916,7 @@ SUBROUTINE AFI_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Re_BufSz = Re_BufSz + SIZE(InData%secondVals) ! secondVals END IF Int_BufSz = Int_BufSz + 1 ! InterpOrd + Re_BufSz = Re_BufSz + 1 ! RelThickness Re_BufSz = Re_BufSz + 1 ! NonDimArea Int_BufSz = Int_BufSz + 1 ! NumCoords Int_BufSz = Int_BufSz + 1 ! X_Coord allocated yes/no @@ -2009,6 +2012,8 @@ SUBROUTINE AFI_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S END IF IntKiBuf(Int_Xferred) = InData%InterpOrd Int_Xferred = Int_Xferred + 1 + ReKiBuf(Re_Xferred) = InData%RelThickness + Re_Xferred = Re_Xferred + 1 ReKiBuf(Re_Xferred) = InData%NonDimArea Re_Xferred = Re_Xferred + 1 IntKiBuf(Int_Xferred) = InData%NumCoords @@ -2155,6 +2160,8 @@ SUBROUTINE AFI_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg END IF OutData%InterpOrd = IntKiBuf(Int_Xferred) Int_Xferred = Int_Xferred + 1 + OutData%RelThickness = ReKiBuf(Re_Xferred) + Re_Xferred = Re_Xferred + 1 OutData%NonDimArea = ReKiBuf(Re_Xferred) Re_Xferred = Re_Xferred + 1 OutData%NumCoords = IntKiBuf(Int_Xferred) diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index c7a787a51..889c61481 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -1630,7 +1630,7 @@ subroutine UA_UpdateDiscOtherState_BV( i, j, u, p, xd, OtherState, AFInfo, m, Er call AFI_ComputeUACoefs( AFInfo, u%Re, u%UserProp, BL_p, ErrMsg2, ErrStat2); if(Failed()) return ! --- Compute effective angle of attack and lagged angle of attack (needed to update active states) - call BV_getAlphas(i, j, u, p, xd, BL_p, alpha_34, alphaE_L, alphaLag_D, adotnorm) + call BV_getAlphas(i, j, u, p, xd, BL_p, AFInfo%RelThickness, alpha_34, alphaE_L, alphaLag_D, adotnorm) ! --- Update dynamic stall activation states call BV_UpdateActiveStates(adotnorm, alpha_34, alphaLag_D, alphaE_L, BL_p, OtherState%activeL(i,j), OtherState%activeD(i,j)) @@ -1645,20 +1645,20 @@ end subroutine UA_UpdateDiscOtherState_BV !============================================================================== !> Calculate angle of attacks using Boeing-Vertol model !! Drag effective angle of attack needs extra computation -subroutine BV_getAlphas(i, j, u, p, xd, BL_p, alpha_34, alphaE_L, alphaLag_D, adotnorm) +subroutine BV_getAlphas(i, j, u, p, xd, BL_p, tc, alpha_34, alphaE_L, alphaLag_D, adotnorm) integer, intent(in ) :: i !< node index within a blade integer, intent(in ) :: j !< blade index type(UA_InputType), intent(in ) :: u !< Inputs at t type(UA_ParameterType), intent(in ) :: p !< Parameters type(UA_DiscreteStateType), intent(in ) :: xd !< Discrete states at t type(AFI_UA_BL_Type), intent(in ) :: BL_p !< + real(ReKi), intent(in ) :: tc !< Thickness ratio of airfoil real(ReKi), intent(out ) :: alpha_34 !< alpha at 3/4 chord point real(ReKi), intent(out ) :: alphaE_L !< effective angle of attack for lift real(ReKi), intent(out ) :: alphaLag_D !< Lagged angle of attack for drag real(ReKi), intent(out ) :: adotnorm !< alphadot * Tu real(ReKi) :: gammaL, gammaD !< gamma coefficients for lift and drag respectively real(ReKi) :: dalphaMax !< Maximum change of angle of attack - real(ReKi) :: tc !< Thickness ratio of airfoil, TODO TODO TODO real(ReKi) :: dalphaL, dalphaD real(ReKi) :: isgn !< sign of alphadot Norm real(ReKi), parameter :: umach = 0.0_ReKi !< Mach number umach=Urel*Minf, Minf (freestrem Mach) for incompressible @@ -1676,8 +1676,7 @@ subroutine BV_getAlphas(i, j, u, p, xd, BL_p, alpha_34, alphaE_L, alphaLag_D, ad dalphaMax = 2._ReKi * BV_TransA(BL_p) ! TODO TODO ! --- Calculate gamma for lift and drag based rel thickness - tc = 0.18_ReKi ! TODO TODO - call BV_getGammas(tc, umach, gammaL, gammaD) + call BV_getGammas(tc, umach, gammaL, gammaD) !NOTE: only function of tc ! --- Delta alpha !dalpha = - K1 * Gamma * sqrt( abs(xd%alpha_dot(i,j) * Tu) ) ! Formula from paper @@ -1687,7 +1686,7 @@ subroutine BV_getAlphas(i, j, u, p, xd, BL_p, alpha_34, alphaE_L, alphaLag_D, ad ! Plateau dalphaL = min(dalphaL, dalphaMax) dalphaD = min(dalphaD, dalphaMax) - !print*,'dalpha ', dalphaL,dalphaD + !print*,'dalpha ', dalphaL,dalphaD, dalphaMax if ((adotnorm*(alpha_34-BL_p%alpha0)) < 0.0_ReKi) then dalphaL = dalphaL*K1neg dalphaD = dalphaD*K1neg @@ -3451,14 +3450,14 @@ subroutine BV_CalcOutput() if (ErrStat >= AbortErrLev) return ! --- Compute effective angle of attack and lagged angle of attack (needed to update active states) - call BV_getAlphas(i, j, u, p, xd, BL_p, alpha_34, alphaE_L, alphaLag_D, adotnorm) + call BV_getAlphas(i, j, u, p, xd, BL_p, AFInfo%RelThickness, alpha_34, alphaE_L, alphaLag_D, adotnorm) alphaE_D = BV_alphaE_D(adotnorm, alpha_34, alphaLag_D, BL_p, OtherState%activeD(i,j)) #ifdef UA_OUTS ! --- Recompute variables, for temporary output to file only - ! Calculate deltas to negative and postivive stall angle (delN, and delP) + ! Calculate deltas to negative and positive stall angle (delN, and delP) call BV_delNP(adotnorm, alpha_34, alphaLag_D, BL_p, OtherState%activeD(i,j), delN, delP) - call BV_getGammas(tc=0.18, umach=0.0_ReKi, gammaL=gammaL, gammaD=gammaD) + call BV_getGammas(tc=AFInfo%RelThickness, umach=0.0_ReKi, gammaL=gammaL, gammaD=gammaD) TransA = BV_TransA(BL_p) #endif From 3fc4eca6308a3fed9bb7b909dd3da9b55cb48036 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 3 Aug 2021 18:28:39 -0600 Subject: [PATCH 19/24] FVW: adding Induction flag to turn off induction on LL --- modules/aerodyn/src/FVW.f90 | 35 +++++++++++++++++++--------- modules/aerodyn/src/FVW_IO.f90 | 13 +++++++---- modules/aerodyn/src/FVW_Registry.txt | 1 + modules/aerodyn/src/FVW_Types.f90 | 7 ++++++ 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/modules/aerodyn/src/FVW.f90 b/modules/aerodyn/src/FVW.f90 index d64ac5cf6..cff5842fe 100644 --- a/modules/aerodyn/src/FVW.f90 +++ b/modules/aerodyn/src/FVW.f90 @@ -1377,13 +1377,19 @@ subroutine CalcOutputForAD(t, u, p, x, y, m, AFInfo, ErrStat, ErrMsg) !--- Induction on the lifting line control point ! if InductionAtCP : In: m%W%CP, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) ! if not InductionAtCP : In: m%W%r_LL, Out:m%W%Vind_CP (interp/extrap) and m%W%Vind_LL - call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - ! Transfer to output - do iW=1,p%nWings - y%W(iW)%Vind(1,:) = m%W(iW)%Vind_LL(1,:) - y%W(iW)%Vind(2,:) = m%W(iW)%Vind_LL(2,:) - y%W(iW)%Vind(3,:) = m%W(iW)%Vind_LL(3,:) - enddo + if (p%Induction) then + call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + ! Transfer to output + do iW=1,p%nWings + y%W(iW)%Vind(1,:) = m%W(iW)%Vind_LL(1,:) + y%W(iW)%Vind(2,:) = m%W(iW)%Vind_LL(2,:) + y%W(iW)%Vind(3,:) = m%W(iW)%Vind_LL(3,:) + enddo + else + do iW=1,p%nWings + y%W(iW)%Vind(1:3,:) = 0.0_ReKi + enddo + endif end subroutine CalcOutputForAD !---------------------------------------------------------------------------------------------------------------------------------- !> Routine for computing outputs, used in both loose and tight coupling. @@ -1547,7 +1553,7 @@ subroutine UA_Init_Wrapper(AFInfo, InitInp, interval, p, x, xd, OtherState, m, E Init_UA_Data%c(i,1) = p%W(iW)%chord_LL(i) ! NOTE: InitInp chord move-allocd to p end do Init_UA_Data%dt = interval - Init_UA_Data%OutRootName = InitInp%RootName + Init_UA_Data%OutRootName = trim(InitInp%RootName)//'W'//num2lstr(iW) Init_UA_Data%numBlades = 1 Init_UA_Data%nNodesPerBlade = InitInp%numBladeNodes ! At AeroDyn ndoes, not CP @@ -1605,15 +1611,22 @@ subroutine CalculateInputsAndOtherStatesForUA(InputIndex, u, p, x, xd, z, OtherS integer(IntKi) :: errStat2 ! temporary Error status of the operation ErrStat = ErrID_None ErrMsg = "" + !NOTE: UA happens at the LL nodes (different from the Control Points) ! --- Induction on the lifting line control points - ! if InductionAtCP : In: m%W%CP, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) + ! if InductionAtCP : In: m%W%CP, Out:m%W%Vind_CP and m%W%Vind_LL (averaged) ! if not InductionAtCP : In: m%W%r_LL, Out:m%W%Vind_CP (interp/extrap) and m%W%Vind_LL - call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'CalculateInputsAndOtherStatesForUA'); if (ErrStat >= AbortErrLev) return + if (p%Induction) then + call LiftingLineInducedVelocities(p, x, p%InductionAtCP, 1, m, ErrStat2, ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,'CalculateInputsAndOtherStatesForUA'); if (ErrStat >= AbortErrLev) return + else + do iW = 1,p%nWings + m%W(iW)%Vind_LL(1:3,:)=0.0_ReKi + enddo + endif if (p%InductionAtCP) then if (m%nNW<=1) then do iW = 1,p%nWings - m%W(iW)%Vind_LL(1,:)=0.0_ReKi + m%W(iW)%Vind_LL(1:3,:)=0.0_ReKi enddo endif endif diff --git a/modules/aerodyn/src/FVW_IO.f90 b/modules/aerodyn/src/FVW_IO.f90 index e2a2cf71d..c0a173ece 100644 --- a/modules/aerodyn/src/FVW_IO.f90 +++ b/modules/aerodyn/src/FVW_IO.f90 @@ -104,8 +104,10 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, m, Inp, ErrStat, ErrMsg ) ! --- Advanced Options ! NOTE: no error handling since this is for debug - p%InductionAtCP = .true. - p%WakeAtTE = .true. + ! Default options are typically "true" + p%InductionAtCP = .true. ! Compute the induced velocities at Control Points, otherwise, at nodes + p%WakeAtTE = .true. ! The wake starts at the trailing edge, otherwise, directly at the lifting line + p%Induction = .true. ! Compute induced velocities, otherwise 0 induced velocities on the lifting line! p%DStallOnWake = .false. CALL ReadCom(UnIn,FileName, '=== Separator' ,ErrStat2,ErrMsg2); CALL ReadCom(UnIn,FileName, '--- Advanced options header' ,ErrStat2,ErrMsg2); @@ -119,10 +121,13 @@ SUBROUTINE FVW_ReadInputFile( FileName, p, m, Inp, ErrStat, ErrMsg ) print*,' >>> InductionAtCP',p%InductionAtCP elseif (index(sDummy, 'WAKEATTE')>1) then read(sDummy, '(L1)') p%WakeAtTE - print*,' >>> WakeAtTE',p%WakeAtTE + print*,' >>> WakeAtTE ',p%WakeAtTE elseif (index(sDummy, 'DSTALLONWAKE')>1) then read(sDummy, '(L1)') p%DStallOnWake - print*,' >>> DStallOnWake',p%DStallOnWake + print*,' >>> DStallOnWake ',p%DStallOnWake + elseif (index(sDummy, 'INDUCTION')>1) then + read(sDummy, '(L1)') p%Induction + print*,' >>> Induction ',p%Induction else print*,' >>> Line ignored, starting with'//trim(sDummy) endif diff --git a/modules/aerodyn/src/FVW_Registry.txt b/modules/aerodyn/src/FVW_Registry.txt index d1e2f8e0a..4b820161e 100644 --- a/modules/aerodyn/src/FVW_Registry.txt +++ b/modules/aerodyn/src/FVW_Registry.txt @@ -99,6 +99,7 @@ typedef ^ ^ IntKi typedef ^ ^ Logical InductionAtCP - - - "Compute induced velocities at nodes or CP" typedef ^ ^ Logical WakeAtTE - - - "Start the wake at the trailing edge, or at the LL" typedef ^ ^ Logical DStallOnWake - - - "Dynamic stall has influence on wake" +typedef ^ ^ Logical Induction - - - "Compute induction" #.......... ContinuousStateType ...... typedef FVW/FVW Wng_ContinuousStateType ReKi Gamma_NW :: - - "Circulation of the near wake panels ( nSpan x nNW )" - diff --git a/modules/aerodyn/src/FVW_Types.f90 b/modules/aerodyn/src/FVW_Types.f90 index 45ca9e911..37538d93e 100644 --- a/modules/aerodyn/src/FVW_Types.f90 +++ b/modules/aerodyn/src/FVW_Types.f90 @@ -126,6 +126,7 @@ MODULE FVW_Types LOGICAL :: InductionAtCP !< Compute induced velocities at nodes or CP [-] LOGICAL :: WakeAtTE !< Start the wake at the trailing edge, or at the LL [-] LOGICAL :: DStallOnWake !< Dynamic stall has influence on wake [-] + LOGICAL :: Induction !< Compute induction [-] END TYPE FVW_ParameterType ! ======================= ! ========= Wng_ContinuousStateType ======= @@ -1663,6 +1664,7 @@ SUBROUTINE FVW_CopyParam( SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%InductionAtCP = SrcParamData%InductionAtCP DstParamData%WakeAtTE = SrcParamData%WakeAtTE DstParamData%DStallOnWake = SrcParamData%DStallOnWake + DstParamData%Induction = SrcParamData%Induction END SUBROUTINE FVW_CopyParam SUBROUTINE FVW_DestroyParam( ParamData, ErrStat, ErrMsg ) @@ -1790,6 +1792,7 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_BufSz = Int_BufSz + 1 ! InductionAtCP Int_BufSz = Int_BufSz + 1 ! WakeAtTE Int_BufSz = Int_BufSz + 1 ! DStallOnWake + Int_BufSz = Int_BufSz + 1 ! Induction IF ( Re_BufSz .GT. 0 ) THEN ALLOCATE( ReKiBuf( Re_BufSz ), STAT=ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -1966,6 +1969,8 @@ SUBROUTINE FVW_PackParam( ReKiBuf, DbKiBuf, IntKiBuf, Indata, ErrStat, ErrMsg, S Int_Xferred = Int_Xferred + 1 IntKiBuf(Int_Xferred) = TRANSFER(InData%DStallOnWake, IntKiBuf(1)) Int_Xferred = Int_Xferred + 1 + IntKiBuf(Int_Xferred) = TRANSFER(InData%Induction, IntKiBuf(1)) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_PackParam SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg ) @@ -2163,6 +2168,8 @@ SUBROUTINE FVW_UnPackParam( ReKiBuf, DbKiBuf, IntKiBuf, Outdata, ErrStat, ErrMsg Int_Xferred = Int_Xferred + 1 OutData%DStallOnWake = TRANSFER(IntKiBuf(Int_Xferred), OutData%DStallOnWake) Int_Xferred = Int_Xferred + 1 + OutData%Induction = TRANSFER(IntKiBuf(Int_Xferred), OutData%Induction) + Int_Xferred = Int_Xferred + 1 END SUBROUTINE FVW_UnPackParam SUBROUTINE FVW_CopyWng_ContinuousStateType( SrcWng_ContinuousStateTypeData, DstWng_ContinuousStateTypeData, CtrlCode, ErrStat, ErrMsg ) From a132e64d594cd079b66c602e9192b657ecbfa5be Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 3 Aug 2021 18:29:43 -0600 Subject: [PATCH 20/24] UA: harmonizing outputs channels with AllNdsOut --- modules/aerodyn/src/UnsteadyAero.f90 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index 889c61481..5cf4bbfd6 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -1059,6 +1059,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & character(*), parameter :: RoutineName = 'UA_Init' #ifdef UA_OUTS + CHARACTER(6) :: TmpChar ! Temporary char array to hold the node digits (3 places only!!!!) integer(IntKi) :: i,j, iNode, iOffset character(64) :: chanPrefix #endif @@ -1120,10 +1121,12 @@ subroutine UA_Init( InitInp, u, p, x, 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)) + !chanPrefix = "B"//trim(num2lstr(j))//"N"//trim(num2lstr(i)) + write (TmpChar,'(I3.3)') i ! 3 digit number + chanPrefix = 'AB' // TRIM(Num2LStr(j)) // 'N' // TRIM(TmpChar) - InitOut%WriteOutputHdr(iOffset+ 1) = trim(chanPrefix)//'ALPHA' - InitOut%WriteOutputHdr(iOffset+ 2) = trim(chanPrefix)//'VREL' + InitOut%WriteOutputHdr(iOffset+ 1) = trim(chanPrefix)//'Alpha' + 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' From 79f44a066627b72c1ccf8a1867258a4f56a52bce Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Fri, 6 Aug 2021 12:47:11 -0600 Subject: [PATCH 21/24] UA: Cn/Ct wrt alpha5 for BV model, omega dot in rad/s --- modules/aerodyn/src/UnsteadyAero.f90 | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index 5cf4bbfd6..e785b1434 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -1101,7 +1101,7 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & elseif(p%UAMod == UA_HGMV) then p%NumOuts = 21 elseif(p%UAMod == UA_BV) then - p%NumOuts = 24 + p%NumOuts = 26 else p%NumOuts = 45 end if @@ -1197,13 +1197,15 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & InitOut%WriteOutputHdr(iOffset+22) = trim(chanPrefix)//'transA' InitOut%WriteOutputHdr(iOffset+23) = trim(chanPrefix)//'delP' InitOut%WriteOutputHdr(iOffset+24) = trim(chanPrefix)//'delN' + InitOut%WriteOutputHdr(iOffset+25) = trim(chanPrefix)//'Vx' + InitOut%WriteOutputHdr(iOffset+26) = trim(chanPrefix)//'Vy' - InitOut%WriteOutputUnt(iOffset+ 8) = '(deg/sec)' + InitOut%WriteOutputUnt(iOffset+ 8) = '(rad/s)' InitOut%WriteOutputUnt(iOffset+ 9) = '(deg)' InitOut%WriteOutputUnt(iOffset+10) = '(deg)' InitOut%WriteOutputUnt(iOffset+11) = '(s)' InitOut%WriteOutputUnt(iOffset+12) = '(deg)' - InitOut%WriteOutputUnt(iOffset+13) = '(deg/sec)' + InitOut%WriteOutputUnt(iOffset+13) = '(deg/s)' InitOut%WriteOutputUnt(iOffset+14) = '(-)' InitOut%WriteOutputUnt(iOffset+15) = '(deg)' InitOut%WriteOutputUnt(iOffset+16) = '(deg)' @@ -1215,6 +1217,8 @@ subroutine UA_Init( InitInp, u, p, x, xd, OtherState, y, m, Interval, & InitOut%WriteOutputUnt(iOffset+22) = '(-)' InitOut%WriteOutputUnt(iOffset+23) = '(-)' InitOut%WriteOutputUnt(iOffset+24) = '(-)' + InitOut%WriteOutputUnt(iOffset+25) = '(m/s)' + InitOut%WriteOutputUnt(iOffset+26) = '(m/s)' else @@ -3362,7 +3366,7 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, end if elseif(p%UAMod == UA_BV) then - y%WriteOutput(iOffset+ 8) = u%omega*R2D + y%WriteOutput(iOffset+ 8) = u%omega y%WriteOutput(iOffset+ 9) = alphaE_L*R2D y%WriteOutput(iOffset+10) = alphaE_D*R2D y%WriteOutput(iOffset+11) = Get_Tu(u%u, p%c(i,j)) @@ -3379,6 +3383,8 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, y%WriteOutput(iOffset+22) = TransA y%WriteOutput(iOffset+23) = delP y%WriteOutput(iOffset+24) = delN + y%WriteOutput(iOffset+25) = u%v_ac(1) + y%WriteOutput(iOffset+26) = u%v_ac(2) else ! Baseline, Gonzales, MinnemaPierce @@ -3501,8 +3507,10 @@ subroutine BV_CalcOutput() y%Cm = Cm25_stat + cos(alpha_50) * (Cl75_stat - Cl50_stat)*0.25_ReKi ! TODO projection using alpha 5 and back for added mass - y%Cn = y%Cl*cos(u%alpha) + y%Cd*sin(u%alpha) - y%Cc = y%Cl*sin(u%alpha) - y%Cd*cos(u%alpha) + !y%Cn = y%Cl*cos(u%alpha) + y%Cd*sin(u%alpha) + !y%Cc = y%Cl*sin(u%alpha) - y%Cd*cos(u%alpha) + y%Cn = y%Cl*cos(alpha_50) + y%Cd*sin(alpha_50) + y%Cc = y%Cl*sin(alpha_50) - y%Cd*cos(alpha_50) end subroutine BV_CalcOutput From 980aebeb91e6aaf459bf995eaf5da4104c961cbd Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 12 Aug 2021 18:09:35 -0600 Subject: [PATCH 22/24] UA: theory doc UAMod 4-7, using .dat&.dvr, rel. thick --- .../examples/{UA-driver.inp => UA-driver.dvr} | 0 docs/source/user/aerodyn/appendix.rst | 8 +- docs/source/user/aerodyn/bibliography.bib | 96 +++ ...oil_example.inp => ad_airfoil_example.dat} | 0 ...blade_example.inp => ad_blade_example.dat} | 0 .../aerodyn/examples/ad_polar_example.dat | 199 +++++ ...ary_example.inp => ad_primary_example.dat} | 0 .../user/aerodyn/figs/UAAirfoilSystem.png | Bin 0 -> 31993 bytes .../user/aerodyn/figs/UAAirfoilSystem.svg | 764 ++++++++++++++++++ docs/source/user/aerodyn/index.rst | 2 + docs/source/user/aerodyn/input.rst | 14 + docs/source/user/aerodyn/theory.rst | 55 +- docs/source/user/aerodyn/theory_ua.rst | 496 ++++++++++++ docs/source/user/aerodyn/zrefs.rst | 10 + 14 files changed, 1596 insertions(+), 48 deletions(-) rename docs/source/user/aerodyn-dynamicStall/examples/{UA-driver.inp => UA-driver.dvr} (100%) create mode 100644 docs/source/user/aerodyn/bibliography.bib rename docs/source/user/aerodyn/examples/{ad_airfoil_example.inp => ad_airfoil_example.dat} (100%) rename docs/source/user/aerodyn/examples/{ad_blade_example.inp => ad_blade_example.dat} (100%) create mode 100644 docs/source/user/aerodyn/examples/ad_polar_example.dat rename docs/source/user/aerodyn/examples/{ad_primary_example.inp => ad_primary_example.dat} (100%) create mode 100644 docs/source/user/aerodyn/figs/UAAirfoilSystem.png create mode 100644 docs/source/user/aerodyn/figs/UAAirfoilSystem.svg create mode 100644 docs/source/user/aerodyn/theory_ua.rst create mode 100644 docs/source/user/aerodyn/zrefs.rst diff --git a/docs/source/user/aerodyn-dynamicStall/examples/UA-driver.inp b/docs/source/user/aerodyn-dynamicStall/examples/UA-driver.dvr similarity index 100% rename from docs/source/user/aerodyn-dynamicStall/examples/UA-driver.inp rename to docs/source/user/aerodyn-dynamicStall/examples/UA-driver.dvr diff --git a/docs/source/user/aerodyn/appendix.rst b/docs/source/user/aerodyn/appendix.rst index 00e5a1279..940480283 100644 --- a/docs/source/user/aerodyn/appendix.rst +++ b/docs/source/user/aerodyn/appendix.rst @@ -25,7 +25,7 @@ outside of OpenFAST. 3) AeroDyn Primary Input File -:download:`(primary input file example) `: +:download:`(primary input file example) `: The primary AeroDyn input file defines modeling options, environmental conditions (except freestream flow), airfoils, tower nodal discretization and properties, as well as output file specifications. @@ -34,12 +34,14 @@ The file is organized into several functional sections. Each section correspond The input file begins with two lines of header information which is for your use, but is not used by the software. 4) Airfoil Data Input File -:download:`(airfoil data input file example) `: + +:download:`(profile data) `: +:download:`(profile coordinates) `: The airfoil data input files themselves (one for each airfoil) include tables containing coefficients of lift force, drag force, and pitching moment versus AoA, as well as UA model parameters. In these files, any line whose first non-blank character is an exclamation point (!) is ignored (for inserting comment lines). The non-comment lines should appear within the file in order, but comment lines may be intermixed as desired for reading clarity. 5) Blade Data Input File -:download:`(blade data input file example) `: +:download:`(blade data input file example) `: The blade data input file contains the nodal discretization, geometry, twist, chord, and airfoil identifier for a blade. Separate files are used for each blade, which permits modeling of aerodynamic imbalances. diff --git a/docs/source/user/aerodyn/bibliography.bib b/docs/source/user/aerodyn/bibliography.bib new file mode 100644 index 000000000..9f2880db1 --- /dev/null +++ b/docs/source/user/aerodyn/bibliography.bib @@ -0,0 +1,96 @@ + +@TECHREPORT{AeroDyn:manual, + title = {AeroDyn Theory Manual}, + author = {P. J. Moriarty and A. Craig Hansen}, + institution = {National Renewable Energy Laboratory}, + year = 2005, + month = {December}, + note = {NREL/EL-500-36881} +} + +@TECHREPORT{AeroDyn:manualUnsteady, + title = {The Unsteady Aerodynamics Module for FAST 8}, + author = {R. Damiani and G. Hayman}, + year = 2019, + institution = {National Renewable Energy Laboratory}, + note = {NREL/TP-5000-66347} +} + +@book{Branlard:book, + 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} +} + + +@article{Hansen:book, + author = {Hansen, M. O. L. and S{\o}rensen, J. N. and Voutsinas, S. and S{\o}rensen, N. and Madsen, H. Aa.}, + doi = {10.1016/j.paerosci.2006.10.002}, + journal = {Progress in Aerospace Sciences}, + keywords = {aeroelasticity,wind turbines}, + pages = {285--330}, + title = {{State of the art in wind turbine aerodynamics and aeroelasticity}}, + volume = {42}, + year = {2006} +} + + +@article{Ning:2014, + author = {Ning, S. Andrew}, + title = {A simple solution method for the blade element momentum equations with guaranteed convergence}, + journal = {Wind Energy}, + volume = {17}, + number = {9}, + pages = {1327-1345}, + keywords = {blade element momentum equations, robust solution methodology, guaranteed convergence}, + doi = {https://doi.org/10.1002/we.1636}, + year = {2014} +} + + +@techreport{Hansen:2004, + title = {A Beddoes-Leishman type dynamic stall model in state-space and indicial formulations}, + author = {Hansen, M.H. and Gaunaa, Mac and Aagaard Madsen, Helge}, + year = {2004}, + issn = {01062840}, + isbn = {8755030904}, + institution={Ris{\o} National Laboratory}, + address={Roskilde, Denmark} +} + +@techreport{Bladed:manual, + title = {Bladed Theory Manual version 4.8}, + author = {DNV GL}, + year = {2016}, + institution={DNV-GL - Energy}, + address={Bristol, UK} +} + + +@article{Oye:1991, + author = {S. {\O}ye}, + title = {Dynamic stall, simulated as a time lag of separation}, + year = {1991}, + journal= {Proceedings of the 4th IEA Symposium on the Aerodynamics of Wind Turbines}, + publisher={ETSU-N-118, Harwell Laboratory, UK} +} + +@article{LeishmanBeddoes:1989, + author = {J. G. Leishman and T.S. Beddoes}, + title = {A semi-empirical model for dynamic stall}, + year = {1989}, + journal= {Journal of the American Helicopter Society}, + volume={34}, + number={3}, + pages={p3-17} +} + +@techreport{Murray:2011, + title={The development of CACTUS : a wind and marine turbine performance simulation code.}, + author={J. Murray and M. Barone}, + year={2011}, + institution={49th AIAA Aerospace Sciences Meeting, Orlando, Florida} +} diff --git a/docs/source/user/aerodyn/examples/ad_airfoil_example.inp b/docs/source/user/aerodyn/examples/ad_airfoil_example.dat similarity index 100% rename from docs/source/user/aerodyn/examples/ad_airfoil_example.inp rename to docs/source/user/aerodyn/examples/ad_airfoil_example.dat diff --git a/docs/source/user/aerodyn/examples/ad_blade_example.inp b/docs/source/user/aerodyn/examples/ad_blade_example.dat similarity index 100% rename from docs/source/user/aerodyn/examples/ad_blade_example.inp rename to docs/source/user/aerodyn/examples/ad_blade_example.dat diff --git a/docs/source/user/aerodyn/examples/ad_polar_example.dat b/docs/source/user/aerodyn/examples/ad_polar_example.dat new file mode 100644 index 000000000..174deb027 --- /dev/null +++ b/docs/source/user/aerodyn/examples/ad_polar_example.dat @@ -0,0 +1,199 @@ +! ------------ AirfoilInfo v1.01.x Input File ---------------------------------- +! DU30 airfoil with an aspect ratio of 17. Original -180 to 180deg Cl, Cd, and Cm versus AOA data taken from Appendix A of DOWEC document 10046_009.pdf (numerical values obtained from Koert Lindenburg of ECN). +! Cl and Cd values corrected for rotational stall delay and Cd values corrected using the Viterna method for 0 to 90deg AOA by Jason Jonkman using AirfoilPrep_v2p0.xls. +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=1] +0.30 RelThickness - ! The non-dimensional thickness of the airfoil (thickness/chord) [only used if UAMod=7] [default=0.2] (-) + 1 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +@"DU30_A17_coords.txt" NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. + 1 NumTabs ! Number of airfoil tables in this file. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ + 0.75 Re ! Reynolds number in millions + 0 UserProp ! User property (control) setting +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ + -2.2 alpha0 ! 0-lift angle of attack, depends on airfoil. + 9 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) + -9 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] + 0 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] + 1.449 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. + -0.6138 Cn2 ! As Cn1 for negative AOAs. + 0.19 St_sh ! Strouhal's shedding frequency constant. [default = 0.19] + 0.008 Cd0 ! 2D drag coefficient value at 0-lift. + -0.09 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] + 0.2 x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients + 143 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) + -180.00 0.000 0.0267 0.0000 + -175.00 0.274 0.0370 0.1379 + -170.00 0.547 0.0968 0.2778 + -160.00 0.685 0.2876 0.2740 + -155.00 0.766 0.4025 0.3118 + -150.00 0.816 0.5232 0.3411 + -145.00 0.836 0.6454 0.3631 + -140.00 0.832 0.7656 0.3791 + -135.00 0.804 0.8807 0.3899 + -130.00 0.756 0.9882 0.3965 + -125.00 0.690 1.0861 0.3994 + -120.00 0.609 1.1730 0.3992 + -115.00 0.515 1.2474 0.3964 + -110.00 0.411 1.3084 0.3915 + -105.00 0.300 1.3552 0.3846 + -100.00 0.182 1.3875 0.3761 + -95.00 0.061 1.4048 0.3663 + -90.00 -0.061 1.4070 0.3551 + -85.00 -0.183 1.3941 0.3428 + -80.00 -0.302 1.3664 0.3295 + -75.00 -0.416 1.3240 0.3153 + -70.00 -0.523 1.2676 0.3001 + -65.00 -0.622 1.1978 0.2841 + -60.00 -0.708 1.1156 0.2672 + -55.00 -0.781 1.0220 0.2494 + -50.00 -0.838 0.9187 0.2308 + -45.00 -0.877 0.8074 0.2113 + -40.00 -0.895 0.6904 0.1909 + -35.00 -0.889 0.5703 0.1696 + -30.00 -0.858 0.4503 0.1475 + -25.00 -0.832 0.3357 0.1224 + -24.00 -0.852 0.3147 0.1156 + -23.00 -0.882 0.2946 0.1081 + -22.00 -0.919 0.2752 0.1000 + -21.00 -0.963 0.2566 0.0914 + -20.00 -1.013 0.2388 0.0823 + -19.00 -1.067 0.2218 0.0728 + -18.00 -1.125 0.2056 0.0631 + -17.00 -1.185 0.1901 0.0531 + -16.00 -1.245 0.1754 0.0430 + -15.25 -1.290 0.1649 0.0353 + -14.24 -1.229 0.1461 0.0240 + -13.24 -1.148 0.1263 0.0100 + -12.22 -1.052 0.1051 -0.0090 + -11.22 -0.965 0.0886 -0.0230 + -10.19 -0.867 0.0740 -0.0336 + -9.70 -0.822 0.0684 -0.0375 + -9.18 -0.769 0.0605 -0.0440 + -8.18 -0.756 0.0270 -0.0578 + -7.19 -0.690 0.0180 -0.0590 + -6.65 -0.616 0.0166 -0.0633 + -6.13 -0.542 0.0152 -0.0674 + -6.00 -0.525 0.0117 -0.0732 + -5.50 -0.451 0.0105 -0.0766 + -5.00 -0.382 0.0097 -0.0797 + -4.50 -0.314 0.0092 -0.0825 + -4.00 -0.251 0.0091 -0.0853 + -3.50 -0.189 0.0089 -0.0884 + -3.00 -0.120 0.0089 -0.0914 + -2.50 -0.051 0.0088 -0.0942 + -2.00 0.017 0.0088 -0.0969 + -1.50 0.085 0.0088 -0.0994 + -1.00 0.152 0.0088 -0.1018 + -0.50 0.219 0.0088 -0.1041 + 0.00 0.288 0.0087 -0.1062 + 0.50 0.354 0.0087 -0.1086 + 1.00 0.421 0.0088 -0.1107 + 1.50 0.487 0.0089 -0.1129 + 2.00 0.554 0.0090 -0.1149 + 2.50 0.619 0.0091 -0.1168 + 3.00 0.685 0.0092 -0.1185 + 3.50 0.749 0.0093 -0.1201 + 4.00 0.815 0.0095 -0.1218 + 4.50 0.879 0.0096 -0.1233 + 5.00 0.944 0.0097 -0.1248 + 5.50 1.008 0.0099 -0.1260 + 6.00 1.072 0.0101 -0.1270 + 6.50 1.135 0.0103 -0.1280 + 7.00 1.197 0.0107 -0.1287 + 7.50 1.256 0.0112 -0.1289 + 8.00 1.305 0.0125 -0.1270 + 9.00 1.390 0.0155 -0.1207 + 9.50 1.424 0.0171 -0.1158 + 10.00 1.458 0.0192 -0.1116 + 10.50 1.488 0.0219 -0.1073 + 11.00 1.512 0.0255 -0.1029 + 11.50 1.533 0.0307 -0.0983 + 12.00 1.549 0.0370 -0.0949 + 12.50 1.558 0.0452 -0.0921 + 13.00 1.470 0.0630 -0.0899 + 13.50 1.398 0.0784 -0.0885 + 14.00 1.354 0.0931 -0.0885 + 14.50 1.336 0.1081 -0.0902 + 15.00 1.333 0.1239 -0.0928 + 15.50 1.326 0.1415 -0.0963 + 16.00 1.329 0.1592 -0.1006 + 16.50 1.326 0.1743 -0.1042 + 17.00 1.321 0.1903 -0.1084 + 17.50 1.331 0.2044 -0.1125 + 18.00 1.333 0.2186 -0.1169 + 18.50 1.340 0.2324 -0.1215 + 19.00 1.362 0.2455 -0.1263 + 19.50 1.382 0.2584 -0.1313 + 20.00 1.398 0.2689 -0.1352 + 20.50 1.426 0.2814 -0.1406 + 21.00 1.437 0.2943 -0.1462 + 22.00 1.418 0.3246 -0.1516 + 23.00 1.397 0.3557 -0.1570 + 24.00 1.376 0.3875 -0.1623 + 25.00 1.354 0.4198 -0.1676 + 26.00 1.332 0.4524 -0.1728 + 28.00 1.293 0.5183 -0.1832 + 30.00 1.265 0.5843 -0.1935 + 32.00 1.253 0.6492 -0.2039 + 35.00 1.264 0.7438 -0.2193 + 40.00 1.258 0.8970 -0.2440 + 45.00 1.217 1.0402 -0.2672 + 50.00 1.146 1.1686 -0.2891 + 55.00 1.049 1.2779 -0.3097 + 60.00 0.932 1.3647 -0.3290 + 65.00 0.799 1.4267 -0.3471 + 70.00 0.657 1.4621 -0.3641 + 75.00 0.509 1.4708 -0.3799 + 80.00 0.362 1.4544 -0.3946 + 85.00 0.221 1.4196 -0.4081 + 90.00 0.092 1.3938 -0.4204 + 95.00 -0.030 1.3943 -0.4313 + 100.00 -0.150 1.3798 -0.4408 + 105.00 -0.267 1.3504 -0.4486 + 110.00 -0.379 1.3063 -0.4546 + 115.00 -0.483 1.2481 -0.4584 + 120.00 -0.578 1.1763 -0.4597 + 125.00 -0.660 1.0919 -0.4582 + 130.00 -0.727 0.9962 -0.4532 + 135.00 -0.777 0.8906 -0.4441 + 140.00 -0.807 0.7771 -0.4303 + 145.00 -0.815 0.6581 -0.4109 + 150.00 -0.797 0.5364 -0.3848 + 155.00 -0.750 0.4157 -0.3508 + 160.00 -0.673 0.3000 -0.3074 + 170.00 -0.547 0.1051 -0.2786 + 175.00 -0.274 0.0388 -0.1380 + 180.00 0.000 0.0267 0.0000 + diff --git a/docs/source/user/aerodyn/examples/ad_primary_example.inp b/docs/source/user/aerodyn/examples/ad_primary_example.dat similarity index 100% rename from docs/source/user/aerodyn/examples/ad_primary_example.inp rename to docs/source/user/aerodyn/examples/ad_primary_example.dat diff --git a/docs/source/user/aerodyn/figs/UAAirfoilSystem.png b/docs/source/user/aerodyn/figs/UAAirfoilSystem.png new file mode 100644 index 0000000000000000000000000000000000000000..e123b10289171c806fb1f5cfaa4690d046c2447e GIT binary patch literal 31993 zcmdSB_dl0!{|EdgNlKBK$V{S;Qb@8wC?lg`ONx|8l)YC-NJ(aP8A(O9P(~u7C^IEw zl#CMh^XPMZ?|F)b^T-WvO!~1=n=W!gb*K@tj5S?Rc4D_7z6bgmmh`Nd%g|dp8 zLZLcHM~lCCzOnNg{)^U9Q%!}kLjEtgJTsa?;iVi=QPg)$nD}(tl-|mewmVEcow8;H|l^PBMGG%Ggv0uzi(pbxKs@r}L%o^V?pz*k2L8 z_26~h0|&O8J$p7dI9Q?gslDihKa($}EaxXbz3u9%4_L=OFgA9owKFF)GBW7VqerLg zc=>H79*l{32M3P@mX{yA`n_j$h>EXTuJ- z7d||ux7)^_+EdG`pwbaszWC+d^p79Z6yx&itUR6zDUEZVKc86|H(Q~m+~AVg^z%zg z?mTCb;fWK${a!2FYc_7xciEw$63*#IVbT?N=Dz6Ydi^>z<*9vdAoqc*6bg-EmD^N# z(2X1G-@kt!JeAv97sjrOUmk9V%zeSapQ?H^W0E>JIP2n(biIjBFWB?)@`A&|>k?#~ z-WJ;#`TP6x^72x8dU_WA&i7lKJv-NIwIfzTaN9OMaq*2A85spnpXU9#9=Y`(J%x^* zKIfMeGgI4&x@(aHlkPi*;d+MIvCefRB_#!gg^ISeB0qlqEWG$hcIoS(m2Ag;=IzJK z|Nj0Z?J}9r_})W6Kwx&fdn5kx<;xIez9@BD+^KmF`t^_w?&^$mjj zsXsVqf`j94u3EFf{NwY>L*s+BLABl8&l;H*uq2k>dx|NEvtmzdJLylII;DQ>SfE}u zOYcF?B@Q7WA*r0H<-dQ()#&On&Cbpi;A)!h?#WaopBp-}m5M)A=nU4%;mQ>jGZ%bR zBcsE_``x=~@OP_@966${uCCzjE{C1ruN<_Yx}}AIqOYXry!hvjvXK!xbGr@GB{~7C!cKc2>XS$O$#IHFxjcwUp*1d*k0{4gDBtI`QFMeGSbNvv-!T6QvIv5Rj2M>8>pnS6f|8_2$i+(N8b-tKQ)7F?@8Y z^Lf3t{36YkeHVmX@)gIs9!1@}sWUvx%oHUuM=sp)(8|)DxvBn*l=sgsr*TkI2FmCA zYO%-HP!@(G4;9#Uio|-fw6rj>u?5cexor##45Uy3lzd*jQlI!xv}USuMgD@leWCl} z564EB1AIsgI)i-SC=Qo&O!fAea@l$EQnzv{ySk5M>+_3<&__i@1xG}X>fkWa$Qlz9L!rz~ zeDFIGEo6}U=D`EO_dPwS&!2OYmX=EF-ydWwrlzO2>B*BP1y{eHIwqi@qTlph0Chlp z2MfK{eOXqVyn<)X8ospL6TOg`nYo6Chvx>zZt6=Fi$d-m9-?I%$(7^G-jQ(pu z`Q`aZ6FJYNJMV%wF?I5jh0#rC+j+wK(dlQYSy@>%-#>i#fX6^f!CD)Q@1b9}j%oAe z9f97Dn_F5g57w-fmX;p)SmM+vE6>Daa6=IlQzZ8O{qU1TXH)EZpDuik)*kr!H7Gck z4%>F?q2&dE>+_T0f99viF$-hcmD6RTqN-|%3mH*T*x}^lgnC8$?b|o0MG+O1S(Xh9 z{QRjJyUZ%MlDw9$#o9l7^r+CLb?fX@f90tp-L~np_7ugrxjBQU0{p3CR*X1dB6aok zHLa~1Q&Lib!@`tJO}DORU}&rGaC3EaB~|8acXxhg>qB93qu+03zd(~{zwo{HS;Nxe z&rTEU{Z7d~tQ-Dx(2(LoPKe2*Ye_59_KhkeQ{EZgM%qJMksHCnMs~{=tsYN$&v=sDX6Q{X=rFLvamdu z)C(R9(E7BRJe%@h^hCuJ9R;i=n#jk+$bUVgr~;HI^C zrseeMdTY74@Art^S5GTYBcIF3;^yY|QvrK83PbC?;blg&v~clzIF#I%e4T3PzJ@#CjYOk7;+?d|QQWo4Cw zq6fPkoqqM|RaM!wpUFDH1rCEXHe2`b@lnlB^{=6z01l0|rH%GidYO1K@E2P}lBJar zPFJkz?q!AXp&GsFf}1Yew&nCtc_$^A1=#!g`2kp*@cZ8v z7-XS8L~qv)EGlw}HG22BsHiPnll=zIp{;>8xXAJ}*Q|#V@jb|9pCd)yIz?zcSXbHZd^~ea%lPrK&AW%`V@8pMgNF z5v>GiCO$sCJCfG)XIm5hHqRBEZK**WaroB0?Z%e<8+>^?lw(yo^vlmNX4#&*Sy_1~ zG%U>G;>C!90*O~6jyq31p_?lExdDGE6D}-ei^Kk4=xdT!U{Vs#6=&x{+s>S?eV0ZW zVSl%;3eUgGkbQVFIKH%Jf=SwaL1O2wT}+&uB5xD~qtpwmo7f1%aGB~mY-wq^ z%i!39lQ>xF`o_jLMw;SQQLstd9$Rb1e#aHxR2RfQfLnSW@GJrWe^yZ{7KYaKnX9Qd zXFT=w*2`8=Qu5WH6E@(a{nC8b4_&!isZ%9B2!~STYikmFzB|KKxrKmv@lV^Oq+)+w zV%ELCqx|l(3}rv`x;2LmRcdte<0}>hgLusC>`wo+4B5+pJrhz>TU+;a-@bj~WnO5^ zbze~}i}ir`-r44=4UEK@wsU1?XAk{6+Y(x;wwEbaQjFxwZAL<`wN<0E4%0-yZELwmWE`+EE}u z#~770@w!-rKfBnfQ{~E)y=CR)mzNeUm;LHGO}gD?>fre2=v-F6#yVRs7+r5=MrTp> z!$W);wp8u6X5^`jjZIjq=qd5!3*uEprM*d598r#g*Z0cF$xSzk=JSe)Z6K}2^x8-) zCwCm`Od>g%ZNNo*vGR-kO4!{c-)ClKSmni?7k^mKlmrE-725_!Mm7M2Nm#z5d;a2u z06t_LyO@7S$dTd*H57YRazLZP!x`$sx2&POFLB!b`0-=?r)XV-SxD^JQ`=YWX7F@m zWMmLegq8yMo_ajaTcyd6+hS|0Pqk{s=q(4IKU`dh}P6g@N8nm306>1K+-_ zMX|gx-o?-m$SAU!!xj;<{KZL9QJvGr0lH|NdJPKrSJ9vbV+ zCH;4<5Wh28N9s|zh=@qdP+i#Nfhwwrp5k0p3t#-ZoJef&v6kAPjWct-my}PR-eQzz z?1RTxs>u&h_gLDFk*PBbK=AqV=gHbh^3vC5g_Tjiqv8wER>nTQj^4ie+K)5t^PgPq zNLcumHu2%tuV=5$~PSo12hR<=E0fhyo&`OqysdD3wNGB>G*$l zmc!id{*z&!eUJaZfH~M#)(b9t-gSENTctc+OSf zE-DZkC-oFF2S@Nm`2~uiqT;#Ur;q+!Q>qBLs7JveA=RH>M-L7UuiCzSyK|f}c3W%J z>bl^pdTcQX+YaG}Ckm-Oe$VQ<$lzbia-pJRse&_LA;W?8EKnQ%`}Ra;z<1qncbr?- zg=s!iWYVtjuve2WD*n6pHR%L;zLK(X3V80nI~-l@aE_75KtxO|uigIIwQH__zCS~W z69T;LGSkJ^>5G!D)ARB1k#?KA5YQ|CXJXInXxl2Q`WxB(spJ_M{`U!o4)G*;{@x*G zR`KwM=b1Bt1TCX=N*So)Q>5y^d~)~J`OdK4-6-FqDD5Vy7f^EK^LlUse_t z3gya`E7Mb6D_ah_&FMSgT4*R~YLTiJF6>N5NI=;R`1USy_*ImENVFW|!FAZ?202gE zk`ITmov3gxiO=QZPu1HLLUV7Qea-B*cTtIn5ukS4rBQ~C}hzJTZ2RWU2CNF9_I#`&6v{16w z+`M}?d-B$i+Uc!SsX{}CFR{>i*VHI!-I1UY6BBdR!Lk3cLs+;9dI%s##-v_iZ!nVp zBM(m`POC5rJ!NG}Pqw~Dq1TEhm%Ka2;lqauii&Ezsa8q2Od9u{RzSbkF9AKYXo%n* z6Y~qs>O=J(1~z=#)~4&SLtIc)RMU82=DaVj0)VbaEWeNtZAb{&Zg-^Y*h;T_(`${r zC1ioUM8DS88z*3`V~O#nbX2H&Qx$)XecD!50CZbjTbuUr4-|~wzkk1KYElNold!0v zDO>(MJ~6V5i998B{-|W^Q*sT#IXaxw5Lx)>r~yHALHwARnb&XGw;m9PL(Wy=MtFFq ziO6|gY%PKrN88giIiwwU>FMbSTw-TuC%6Yc3(9Haa;<}c(~qqvB@riPQUmh5`+U0~ zVV|pL*L&k!mo}x+2L8kOSw7dkrN=R{vZhZ;X88=u{^LCiH;RjkOEr^Kz5o48aMoHJ z9DFaot*`4p)T}EI6BR`jphSfOdgsm^l*e`j!n*Mj{tyiO0Ou{x%g4k5_Ojqcar6Wp zq@}H*=!nGH=K1es{omh#RJGO+A`4y+H8f<&!os3}(oeaG4R*j~QXyMk3IC+%5cUhH z!8R|Z<0xdlPl}L;v?9V~~v~ zyt{TWcr7nx;!Ga>kCS%LmCw{~xv7zqFm@EO19Y9|jwAHTOFy-Z%UqeZZQEv>ceCR^ zsStH`4(z_VscD_4;X?%gF(@}Fj}JmBh1O!56S0@-e=GB=j@>L7glqwzgpr+nO^6D$ zsHmv4jEtht@Oks&4PI3L9>jad3cKAG96%>IgMn$V(*Iw{^JT@|=(HIL30yeIXU?7t zor0e7d1S=kec$`{9MA_s@J(OpHJDAi?e1p#kd3Rp(DMmS+?_ zd7^>>`L@Et?fr)j*-IZvo$Z|C)Kof>iK=iu8T(sMNN5O<5mF?h_5>7-NXR1i1SW83 zd~c_0MRxWMa0@V%4FGPytXXp1pg$oYYiED`;)`p7ic1SU2sckf3c9(uxz319%m!}s z12y#7t%p2!%4=(CDqp`|+9)6;#R}f5rlCQf_|+&HpMgq0Ffp+nP#Wu)f(iu{E5ECM zEtBqX@&q3UL4|qlHZKMl(g7M#>{n3i)^+zkkB^Pj^z?8(I#sMhr~}`=^z94UNu!$S z@du3j^|Jl_{8kYTbME~4LEIdXpdoLEzkh!Mw`;hW1uP&4+@uy{`qK*+HAwIl7cLk_ z_In3sSrCw(BcPGK>GI{vL&M*q|2};9&}OSL%Hp-3U&slC{9|xz?)1|OApm2^)=jt3 zX|sYZ>3dCl|9*L4+5*y6T(6;yjyJyUW7#$4?tC*fZ0kI;%82LBr6J+P*yW=>;+~(E zlnfSkoBG|mwiC-5Vfl zvGSzn+h4fA8`m^Gc>3J!9A5=%>?@Sb0K$Q=>lc2u9=iNz?mN8~E#OTpptXeSwE222 zX+~95)#8)#OuBvqC(d#?Iyo^hGYce6_g8qFmYZpXH124I3UvB(uZH&A(W6Jlzn$L8 z)w!0M^>s_jVYEkR0Fj_21E}~To~BS0iQ-DTaqA5bqcp4{+LF|7WB$}YQmnIXplCq! zQs%m;tgoqkeQCP#`A5b^ZS zpS6%k44&GZKVSXn6F20B`YhcvX@9f=R@6@04o62vhw-jm#+9Dj`!DseVa2zce0(@M25~&LA4{mZyT?Yq%M8*%Eh$jwx}av@R?kfB z1o(68QI0!=u{C)2?PJr`)g}B3g`N?*&%(ljNM^h{I^w|SXz;Q7vej*EOz1p7lKvGH zo{zfN@k5m>%UjUa!Bz4*t@f*q(NkN40#-wU#QN7lFVNM|;k~$L-@bYPs@`qmdLtFSC|>4`F+1)YblV4oywgaKscjwU zOyRfh+zAFEpV9Eb*H~Ivi6)3|rQEo2<8Aiw?N=s0ZoV(;yvZohVBWT99ox>T-@nF* z{*St!U3@!G;hnF-Nz??Q%}@WCsuYlxKC$#H3&%-YTe~^&;1=b;^=+evdx1R_{y9f)qCS(fw5p(d{CG~f*VkV_5qDFcCT0+Ve7Ad7oZLaYj1$3f_Y$xMLgIc1fqjS^M zEHolwLwy8y803CJ7Hsow1*@|XRDY~|^hjk%i44&t@may!|8Cf@fnoV|6tB>J&p!-6V<*a7cSHUL?+OW~5b@Gh z*ZJ6Q6Juljt=PtozUJ7O9XqxGnwR;RGyZr4iaZ*L=#GM5|0FCGa-^4|wr8&&p zXI8&SJSf_yAQJr=G=moXli=d2=(^_T5(b@|%8bI>=%|;LmIl6m4{wSS51WF2#u~;h zUXPn|wc5>;y&ACKVBEyS1(m~x!K|raBULT_{2F!ZmY&6FD=RB#bw@@X^tZJY>TV1= zS$;aK~f92~0P zlFf9TzLZ@k$!{AvCP+ti85Yb3ZX!0X-JfY&=1rTZVQEklTwSG0U8eHBgu<|eYIf$# zgT~HfU{QMZFOqxr`iF(ByFNSW1G{f;qv+pON2m=1AyXtSyq8#lm?HA?b^i;Oji{e> zXiiiV(kh#7@9NaG^=mufr;t58?eOqaF?~fvh2<2Hk!CuL%u--w+U7+B$A@ZMVmo&1 zSbgF_H;1u~9Un`b*PTCqURX>lFg<-6seRy}5G-q7zs{B)Pv_*2JVQrKO-+$2DYZLy zF6&FE`tg@9UuGrDs;R01-Nx8$$GV_g;$o9+S`$G;*5E@@($muebEN$AvR|t4c+gNt z+26n;zy7Y)FRw>X@}{S!0Vp#Ww;353amikxgJ4pfdN>S%y+1^#>dsD9V#q=#E0NyJ z)LG)F2r^^dmU?($rh!LPj!9iZLka#B^{Q2c7d}XW)vf94>l=NSsa?*Zr6Lu5?_L-l zf$?((syK18dJt-n`?>vHU4~`t{b_0&iGl;cEmbWqKyC_t3L?9>{uZ#Zd^ zYgV4=z3X^%dOr>An{?`x|H}f<4#6KL!WigYV7c3T4J>sS<$TM3`d2{z^gmf|(iKSm zv1J@Z6t@S*g@!`F8WC%M&wlemnZr%+%E)Xc8xT)8Dk0$p_R!MupQ-iK3{~$7tp&zY5^^5K;#6dU^532>qqeSdDb_ zmzQ?=<$>L%ZVctue<`6kbN*^6a~^>SMm2pVb~xv(=e0iG>FGgbDD8Ld-8(d|B}vXY z+%7IIuEZkSb``XwOXa`V0ypwRFl^a3&?H`|0_>Neb=TM9&zuD;P%beQm12#hT~0U) z=o_0TL=J@KO|Pu511_^xk~}x+==ALD!yn2hNw80p;PokJYj1?wNjT`SB>BJ1)R5*2 zU1uyw_o%P0hqj@HtDVLQ2nY#fwEu-5&w%6v)Jcdey)z$dlB!?7UJI4@ZD(iQe7~33 z#Y)tXhsGtFU{`{QQUThwp&PmXc)cC|o!i;+R!5-ErTNO0x&n(@qV85THqxViN}!{P z=9l^6LwL}(N9IJ~vw-l?gPdgRhe9eh`n{Qm?61$D@`!9-LEyqqg`XJFqM!By4x_XY z_ZKaW9xCXGQ>TUi2y3?|x%<9-`!@dXYMn09?!jl!UT1h?e>OvGqFgn4yVC(&!(@l6c$9KQ>^c85$m*!E;FimW2QTz_JP~ zez)_O{)3DR!)qT=U{hf~LwY?YRY`T8^WedQ#F@YvaL70cQmXpq05JiEvZr@zA( zwhL#?%6NNv7)1d>1rwIYKYpRRa(NqtabjY^|5D{&Lm(bzN{k!4Z40Q>v0K05y!jRu zUJm#}&3H!gZUNLcn=XgZw|vz#HK`w$ab$HqY?qYOBUzBypN;Z3CAq&`im=sfww@M= zr9ZSZtz6e`F=y%G7@i>U_nqSotAu;6 zca;p3mXzF770sszhLaP1!-wEIvUQ;O`LAUrwE~2TZ|`0f023ezKH|$T38=>0s}{m9 zLRa=zN>M=X7#tZ{jn)J=j0AvCO-Yyl;<@?x^C!P8H9@xLkg{cYptpmV7Cm%YcULo@-8;|GpF_(>Jq z3a**mtcyZxW>yv*jv?x6HAzg&O`L?3T3~!&)v8rjzO^6b>V?tjwb!moi-C3j(R5|0 zP0Y+uad8k~{?hjvbPB-kJZ)ThWi8kebN~3e%#E}gxaffQz`Ww_-93U{g_??esY>!1 z%S(%=eyct>Pp*&1-w4r=bHOhl&{kxlz4PQ_8a)4D%QGta9@rFXHgGZK?*)`BuK$)g zYb!??eDthu6aF+7FXBAb?L%u2fA$w0os~kj>(moQTw0TT>+9D{;4m~4CAf+DdG3cy zlwq#;BN8EE=ZVzMOVhxrsw(Dk3kA|K^ZTB^}TQAU~ju6^aMe*3ndr(cn|P%YUKexGl7dHKSn^C)1fa~qhM4sN9L z9Xqm!n24?W+xF;b*w2PNLIMKHOU>Zf*%!h>LpMFM|Mz@t#*l04%Q{(p?r*Wjdg zmIxgHpX$=ZjXd%XJExRcHIE!&a(8$4kCgwd?r`}s(;3N=0o#`m&tT6uc36FCPwA|9zU6E`!BRT6aiV({Y;z{yyc%0`v;t*oppZGyb2PD>#Hu8lsZ zo#EFx8gp`9v^O`e2jqFx(o%;`WAHTgSi>5Mfa;B`S&oYrFG5NIn4<-tn0a$xD#k7? zH}_BTy#>(emxE%*GqkcF+uJXKLtVx)LOB}%pr|2c*TReypaRq=Y6>BNAB{i&z#^5A zB$yd(6Cg%6Iv)ybP*Bhss4QXZd6^T851`gtA>#vDpWPRiMvv;j3v%WT1*FJ$%dTC! z%6>G8qH`$|tdptDlc$h7_}NhguIp~6FIxEiWY~o#3e2g9HWL%Wvhe56aGaT^Zb@}f z&PhLlAfRsHAX)(~j$E8P;s7 z$4EFFc&a&qa>T+J{QmvZ1NH@hRCa+?`r6A)fxe|@!7e|3{OD-)M$_hN8;n86z%3LK zhl7P=oVM8m+PFAH+gJ0O+#ly{d-gDcm$4dgJPPHdi@jJ>SXhlsKmFz2rO;p4@0^tg zO%>ONZGcE_^5SY8{Nus#@pWcqW{|hAM^?itaBy)^gYIs+@cifpRDXxbk205Uti*~M zHU8Ct#63g6MQ!aW)J#=8D)X~vGcrQ6OM%vWpdPupxe4stxgMT`&WApLUSi4NLuhrJ z8HLNuL}3bG`Kt&AfTPU8!7%{!i(~&qE>n+R2H*}FA2VkygP@NYi{*dMSL3vucwadB zp=i61cH#~A6c0^auwj)+E-feFS7dNj%z6scub2CQH&1)e}AW#ReBP>!tP=x2+=$s|-!?@tg)c`{bjE{%GHO}9QUw>ZY{>jYb)hUQkUV9VvbqlHPa8x&N);C3@>Ji!Vzy57S&FvdkzFRmMf|?J!A2&U^Vt#YyWhhW zGw?J$Y66N^6}`JI*C;uRXagi_xrG%<_A7?DH%Qz;{sZO z!bAnVio_p)vGz~~u<{E7ej9Dm`}CjzLAYeg--!%j*Vv4d!NrSW#}cG{0ElW(fm-6F zY9XNjb_;#1@Yqrn-It5TWC>^N`IT>*NE8{ZWcZ!-p*ql*Q<=&w9Xrr}jf?Ntioq2i zMGaLs1p1wSVBk<%qfFk^? zl9z61_6~|e4Rm_op~l94vfWeI>$!#uz*X2WO2ApGDZb(1;Wy*sPvsqjoNmy8ulKQsp+g&)1R zVFgXd0$mUUr|@4)(UCJ83}6tJ0H#s`1PSPs@i^B4YDN#%H8d6a_2diR;P|aE9+y{^J&<5OCikrk^oqce z3FPw-;@X6`K>(o2x;-{;^iNKfiTOgY@OS0!D!b-HF02NsvOnTH^gzyRI0`RYS74bp zeqZ5NRA5|w#=%ZW0n(~@Y~AE<=DB$IM5Sjrk*5^-qp}hh4Y$(~Ep_PnYOrtLxVX3i z*x42k0LlNJZr2pgc2_;DSk=&=_Hh}XnscG4sfl^T6lAEe<&dcXqwuG$6y-G*$o7D~ zvLggUGl<9*TLjM`vGtoJRg51_AU!X>^hrwh^5sIphe#}=205hFt5*3rZeNzXt2Nr0 z8&`1V^(H=H;jE7}&PR_Re*yBb9lZ|gnV{%x`vB$17dr;NNB{(lb;wMJ^lqlHorE{x z8yy|ZL5tInG6`k1cHrB7;!kPC?>%x;Bb`<3;_U1&?vEYe5Q7NaEqEd{gFueHDJd4| zNsmBkF1#;Dsqrw|OGl{QyNsi`vqf4*j~W=xKAQBkBpGvdkw$qm)17%aOIH|SJF$XY z1AgKbM9Z%8AhV2g2bDL3HNnqczJ&JX&{KocEbj5@0-`byo98_HWLwm5FzKdhgY#zA|=i?b0~_%g{@o-zj1?uCQB84HqEi^HNZC5f7tWi zvVIERU8e_p-tU2uE2yHTmit1Gznyo4R^lEs15g+e{)ZhB0JRF*-vDR{riwi_`AxZl6Y4rKR!~lZkX(d_psTM>V`pba%p}yq{JHfq+R^HQ?-6VQX9@<& z6+n0$)S09l?p>+~0hXo%K+2g3e~eNYfN>VXeA94bAK|DXdaGy5Q4e(xy77)@Nr+aS zzEmCnGv~y~lNM00S^F;F+if12mUEKg3&JPAFt7@hbNH^a$J_@S=tD2@bn23>85*)7 zs$gMfx6Z}I#eKSZ&Ct;ihQp9QK>veK774rn5D@FmKqcdlFsBfPhacU0sn3`p);96u zM`J)Nt52oQj5H}9KhEP0M8eK685qIJB;zLL)zsJnRA?LqE=R%xkqd@cIXE^JibGDc zTnLkapf#nM0{jfTO#UKZfha4Jv{&wov?VKl|g)_0uYxn=ymw~^{W+zWfo9) zv62tk1&G*>u5B?;vQS}37A3$rxbYn zxmt#~2;02i;Er%bZ_WgOKsead5Q>mZCX#Zw#|7us0mFAI0rau zUAkTfeub7Qr|_|&f|(f)80P}=Ni(2G)D$?Xn`y@WCCrF>fm01j)CV+(MDV0+6QUE2 zr$r-}K~@$A71&bY)T#K!tMGZiUcju)|aCz1V2{qiU|=8#HYb_=^yQgof6 zi{KxkaKX)FF}jvndi;8;$*EJEo(qF(cAAvxlV{7ho9?FSCK@U<-64Y&$a0QWq4#k@ zpj6^m%uar~mpj*0DDUx`fK_4?!70g^bj<4<^id+O!RYR2h)UD3g*bHNfN_#FzqmV;Ne)!k~4+UL@AXSMgqdFw{m#x>> z+zf@s8^TkmW(utcOheTC{3{27Xglx2D!zQnAHrf9g!_LB6L66Ffgucif{zFY`#pOe z$1e6ep|ttG=f}GvmDlV=#xw-^8rr-ZMg`}lt}Zs{VI+)-v%HxmIKDuYhX$G-Z?x3! z{13^**9N>L!DHxihz9XrjBXkk#WlG8=tqs0GSr~ljE=6AX$axmjMboMcdg?~&B$%L z&xe57ATZAK{Jh$cBWo)bzigqUk`mpqgN%v5UkifGHjK=?NN-DX^T6Ps4}tHWUqzMm zJfy-w0#}GcunQc63QF&tz2`TfGzugtFm?t2_0F8_0*!&495rsx;RD;5;ti}AFUrU7 zY|AoFN2(k)v9#+nFHAIl^lCCA26J{mXOmPZ5-Py)G|+p{aC0sYZb!^g1aa|Gkrqd5 ziKYVyZKTf&({>2?H*YD*J_dz_2Yd5TqZ@*#fW>sH&h2V!Z1e@B#^Z9pqsC4ol~*sC zb~6nOdIyMym~E&=z#nG|!Z3nZeovq7gRm8VeJn|nbr-t=#ARE>WY?p6c%BHlOBt#f zU|dY$#ntaR`uh5Vb4UVN!d?Ak%qn&Dz}1N=uojVzxct44Cmtn=+Li&&hwkrlT?OFo zIy=e)MZ42v6I1pPzmoM^*x7@UlX;2r2qKDJ3H2mEI{g87A$&17SP|adlt2oD&hMgc zU5~bqL_NyEkrvjrpHSAa_rd@Hu|Uh|vWK44Zcki@K!z)qHQicDfpw)}tg|X}j%V+Ha3%h3e>yQjj3;!HFOq z9*pqLoe!tOa6kdHh~J98lJ@xhq;U?AnnHo{|L8}8{a9YOz6i-AK*U7pheNd*!aMXU zC9$|+6vT6|{+|>VGx6|%G_XYQuL&PRSHd+Tfhe-2MKD&w!qSqu z#5e+eQq~30a=sjh#v-jBGA4TvIHGp_QPd19)f-c>kz{_d5c;{Xumq`ulIVXBH63(^ z__Wwikl_{41!1nPgMmWAO1ZI(XC#peQWlFN^G-m__!%GUPpI#%t5StlxO39N$!z5C zu)$d;ktS4oTp_2yp5DDn`%rRYX{qjN35m&OO!gmTC}|-<8W>D)QnrwK1QKyN&Cby1 z9!?W^0$Tr`03eLog$obsJJQbEb2cBOhNg^xll9n?^bkY-mw3}i*Si2wDhvwB5c)g3 z6ho_G8Vc&{bLVk3jCe%dy&Hm#jEq!P*&8HJa9@Zbqpk>4&ieiGpg}q0C z$P0D47BT~ZV?%&TJvO`)cyW?*V65|nQU zx|?rBg*>!25}QQ!I?#t!3_9$3dNjHbQ!-|d&vVGrc+&ZN)~naAtHIC4)6Xg^D?@=W z*v!^gv2XY8>?h$u&5cDMzC^PI0d!xP#1L(mF?P?|vIfNC%Q z_DW+a=yLxpWaJlif=_ft>aZPmvIz0z;IVDBt zS<>w_dhdc$f4{beS4t4KS%aMzm6Q~T?wbq_Ez%2szuV*Y6{IN*uN^-?A?HFU+5#T$ ziK4R%(6YuJUkTTPMT%X_4WpfPYVq>btM!P_R|jw=`626O`0#`eh8J>Pu;loFKnUh$rpB>_RV~ z{SckOsv%+!V~j&@MPRvNd8|P9zygjt#WIBHi_`&gd~j>8az>g12c43rB|D9t{%lq_YD~Q?!Lvk>LlhZim@XU7FRa2}MUnd0n6DFazc=lL2W*CdL1r zVS4$0S%9}!P#lpQ69T5WJTr8dIMP5xBKClf#3Ln0cRi(3M@NSOW0ZxB?MLe2Rk!cn zU8||7d6^ElpO3(q<^-AJEwSg3woMg!g9KJ}bv2A3lM;J_fB-~D<*v_W8CSh(Zhnch z`!vM%8QsqH;^K{{TmJ<>A4&+v{C(Hnz6nRG%t*Z!0WkA72{L38$Q?Xr_x(BIv`}zp zQ3}bGpzM7_Y6QPaNqnf`h0Dghf*zw)uSJM}2M<63aoEeDdPk9W3G97#X&Al_NRTSx zDwu~nHK-%JWy=;2a2lc=V`fCA$cO>+uuzNA4Oc1;|AH{@fhU{Mc%>WlsvgQ{$BEc4 z$eP>Mc>eyjooLWF-2S<_cK1Dwo!_YZK@IXi$$X=H`@45oV_ngAuWjw@YKgs@hLK#> z3Nucj2YTmsxx0ys6CD$R3j?|W9J=(b$fW3KKSVn$;dUZ_>bmlGzVPyJJ$)~PY<6%U zNE|h=hlo>mnL`&BHtv2k1vy%bM)E?R!aXlw?g_bj@x&!eI2)B-LLtP96+*!etswqF ze93xu>fRi)n*i>>tsWqbb%l_aM z7%nQICkAxqY<3wA^f^uSUJ9A{?)Ijyp{eBa*eHr#@qnqMDk#%xYk8fNQl>-J=p8FmcK z&ehNMMM#5$w!E5h@IDK9L;_e2TOd@i&9ER>m>x34SKxdU#GIpZ3c{wZ6O+iAO7QqmInAf(|=G%(%X zdW()u*9WfyUW&8Garygns=-Di@?U z(Js3le8O?t#Z!+AJ$}zml)ylfl$3n+=8a)@*P-jPbdL=0eNDU+4+Mq5G4M*AG*t%M zu7@GT#Zp(VUWK0#g!v)8%wyc3;UiO)JKCR|KYr@TxoQm6gt@n1QAyI{FnT(MFv-{? zhBP0MAGc_;50v7G!^Qn#?-CECIwGBho?EcBUly>tyS^#A0^-m&id zR-B3}+?LsVBhyOyj3nUI(h?3yYItNMecVA~8v=s3471X6`njDsA&Q8Jp)!~BRDJ1C zmvh^hx$yggRjalY`>ZSlH6VXU-kDECBoxQ?o9vA^ON`kfYz_`izH`iT3z`Xf?oYg- zAvG=S8t4&@O*&MHD^q>wNWIlV^Ed0m<7ZC*^{67SRA-sD00F4SZI z;m>YjUqehIzS>ASp4D*tyfgFqRkjDv8S#x@OO4*_428-G7ytyITWo9KY%`S#KDZUB zB7+xfczz5BlfY5`hRxwKr5SS!D=&TE0Q>m~cu|}8UEsh)LQD?>Wu&}?;*3KHk_yGl z2NjSS0@OK9DO>#ug<+W+kHCXhiWM;+Qb*;=@&RCWE@=mE06FO9FX0Vx>^XZQPk9$> zV{>~l(phBZqG#6B3Yg0lWVu z+o6}zJX_z303NA+?d`WPfy^$uPe>>biX6%Ep`{;JVxreA6%K`b&W^0}0D|GSXo4-{ z?%!|013rn_e_#hzv9;crQf&4;Pb}cD!CFKpyagyQBs|<2!X7HuZiEYvl>Ko(gsF67 zd|VSxH8h3>#OxmKi0W;=S*6@pg!s{3*CDqv0@etv(d?G@CK8ZAeDmR(GU3qFE=_|R z{SY*Gj3sUVqq0;f8BMpe*y@#GT7I2EhFAk4pqcI>+;;Qk-B%~x-=fnF*4ri^K!QgO z$nOFC@f~kVeGj}ZX<+zm_ywK zzNMJfHvRcEvq{kD!I6pWx-7W!v3UexUb!?Ey`Dz;$W&ZfR}eq=x^r^=g9;^4XcfwQ ztBX}dXjZczHQYH87W4+D2c^3?C4E?zAbtvzvt+E}HnudzbVbba!-y5!vnLwz$KnCr z&`*$2knli4EFOWxkyH8Nj!svuoT>3&i=<90{C3O%(#!pwwuE8$;>7{H!vMHwOu9Yv z8>WRZhLNXkm6n#)IfXHdAKdQ>ER-pIo`39-DgV(k6T&V!+KS^?x01Sk8#M)C$Kzoy z=;K+T&P( zeBh)NApnU#WUM+(HJpF%-WXuHVdQkNW`KoY`Qx%5=?9^9W446#{6x_sn{~g-TNj=7 zNJ)A89e62v%~Mt3vth<2Dh%@gO;WwD^g4u1(X!_(96(A(zF88y=h$=ZN~7(_#NnJX zgQ+$2_P%)aw<$}~2RG706li@4hMAaIV<+&#=I>rZ!GD|n^}gGG z_8J1Z7VvUxTbe%<+4O5A52dT~>pYi?qoXv(ORaP5NpnF)cWfrXS`nLDSg$ac(fTBm zktFvYYq29o5U(VLN;hAKqgdvaPDo%f*M2-WMYltv@8{tL#4D>nEyTse!D%|JRTg)p zur5MX?9ptw*qLJxhSgm~UU~#ItiuVbjWnMmqI;*FU1UhkJMq@Gxa5}9!Gq;;W{W!- zIrvNs?|#skxJC{Y82;(gr!hT3CfBFR=eLp>^T#1~gu9W%UL>yyUp2vB0zV)R??ui_%`pa`;8DAF7rzh~#J?ige`I zu?$=?@`T=Z?r`|}`K_g=PsJ;d9-e*{fgzOC{QOpQ=n~f%eypE0l*6$#zikeMvX0yV zEb0hhX+w`+OGjr8KeTj1y1q#C9%@RG$J}NLUYKDGTaPel*Pc|(TNK^Ix|c8C!h0I) z$gV}KMsO`--6Zf2Ik1pu3sLuvP;1D*2`e5&Wb+n;Q!E`mcT1K*E*pSDT~kvSkl~Y4 zkMe3e?*b^~^yh;wxw-v`)1V$W&#i_MiKx#PVEKjLzX@~#g6c4xLxdA=Es{@h9BITW zp<)m)Bm{Ke+nPV@9?7lZyKf3Algu}jO#$t7S*9)EO7igVuub+oE;{S^l}!!sTzu?@w3 z|5y@@_Bf5V!)b2f)$j+5f*oLk%)%|27-sx%qrnw-FkIBdx99f4wBv$Yh5$mKY8oG) zboU_Jy{ak&OxnG{smC+{-k=pKGeMH*!ypqt=X$-MW!(;yU0HkamM4w3X z7!ue1+b{QxQ~!C~>Gej95Ha^)JnTzr(#GVA`)(&e;2?JdLR0woF?BNbIY7WPWTzPQ zGNrPIz(xp%VSb1aONem%sH~vw8*6K8GG2{7(187fiI+}i^;CbDwrCSl{d$7m=JTxb-~jnGJUI2dDk3wg}5)xN8tN<_N!d&J?mynBAx3M8%e7QIn0 zzvj`S)?nge@uuHq`9e=yTJi%m6Aa3Q0U%UGlOVw>uRea{0#gFiq#{}{XE-guf{@l7 zZ|HkH;jfTmfvLo;W_(4)-hfBmU0qM_R*x>Smp$%sy9l{q9gcF+SSn{-7 z7~&7Oh?#l(Vg(L=SzoV*-{UuPLvRX?2ibLm^ON8<2M3GQ6D5`w^41$nkdnc#rY6nq z!C?9U#3)E{#MYO<;DT` zv~^;bHB}f@1aY0O2oT*8REP;C?`8P%rC$5cU%?~ZuGUhv9Xj)3!J(m+AnbTa7P^5l zc8wz|_xSI{;i5llF%?XxMIAx=+zot*yCH9C zs9gH74jFUunnlF>*11OB3q5S=Ym|+z}h~672ZQPkV+fuc^%YK`v~(y%>jVc9z4}!{DGG>qO$YKg&Il+}?Sjz#DZ2H*;UlVCsiZ%m+aC z7-#Hx#320;3xRCD4UJd~Kn%r)$QIyi_#4@{BA04ZHW_eAm;;u;@x~>N0WkH**i+Glvy}e>`|9WG+i~^z$mNra>ioPa zAqOrG(co}c0%#(bl9n&-#c+6ro{kQ-YOL4FpZ@l6jhG!k-CFndh2x6>+fF=p;Dbhw z>0&*cPUk7#uW`RwJ_8kw%|kIGV`<`LLYXcrMP>oq?CF{oAyvRiok9GIE%ZQRTfL$3 z&-A%Xv7WwBDww3;FRTdBFx_iq8O0z&{ZpR5s|kVOaOB}Mha);TCdRKvD*wf)2X>HvYIdT!!L3p1P_}6Lz z$j%j?`elp*2Az7%@_r|stw-<}*3N*<7q!X@;K)J<9w%6P$3NN(b(g$B@9>8f%!T>*DCQM{wR$}2epXy;-I1j`cB}z8EkNK~@K<#)RblML{+W;< zN~U&FeRcIH%6_)!N>bx`eE#V-%0w+GR^D6==x2E2sB7VN!Lyrj8o&9W0 zMTZ;kH3(?!0mQ>LtV>#1vVj9MGduhL)OGIhRHt1V--;P&Divue6(JFo&LW4<&_O0; zNav!{G&yw;QfXp$7-@GpZC9Ko;H5_ zci;C~*SglVu3Klsh&?bIC19MfAMVLl)xt24ofKl;SVs-=JaH>np(8n^#CDmkHN>ja zi9ja6xUunWBFkcWdbQy;<27)hA+@`m-VZne_KW9E) z-YUT2%0G08K@7YniI2t5{|{OH@>gPAgCh3a&#R2 zpji-`ob2fDZ;B3Kwr5A)BO->wtG60@h(-GW+GT5M>PV`kFyHTb=!zjCoN9QO?H4Xw z@EzV=S56VI;e+Ld))#Grzj`yjO?wNr%-c`Pj1&3cB$QO|DW=?E*e_LaBLu2EO#*HiJ=OUg_8y7?^ta3DfInrXi3cV&J+Pr;mqAlGF!d&bpP##3+dy)9cB0UBI{<@Zod3GOhNAxeb{1C=I%kp>5Px)4jgD1X}~E%7GVCwwR^k zgOynp<#&+%=?))Ww{QLwCpB9~rzJsCLd$WUy}@x2BmfdPCXQX&!vd@mdDiFkz0qsm zR!jq;u-_B1nF+j(7PjU!aVM+acL#_ni(pOt}4nsvVRb8hzUs06%^MJcsnaX7n#5p`>V>yCc& zsW;igH3$^wMjYTxNszXnRJC80vb7gs`sw@K;$^!8&EF=rPr2|tp!qd=<-b=eD~|v*+Hn-T%nAe= zG@JIWc9uK$o)|C=J}qtcuXoN^J4|0abTy*&rX3QVg!mR`qZ{+`@hO-Y+)eHN zMnUqsP*Urf`aTbaMI?Pj3|Z*(3OGNFcO5xud4%t=9&rxP$@Q-ewgOW3;!Du#9_OG= zjefA^-hmfU9dhoNKOKGd&K(qS|L|gH-|7(t`IC>HJlP6Sg+83rcGloxwVJ1Pctpfy zI)clQ@`6vkw7lI^L+mjuIftXb;E5AsV04{0Vjc2a-P{%}T=)-_Ql;~qwBDKPLscJd zfQwneSwQ|M`h7RL71rq+F7@&IFB~yo?1`a?j zE4u#6#}O#=ERPikk~ zjsTZUJ(Fjx7;2;R7djphLGszRegxNnV9t~D1?F_>(xuE#-&#$d(um4D|F)HWO)XWE zZKg|5^h~w5^%vu1$MFDK3a%63M-*{+1k z^Q$8LSFyoWj{cx&I;#8*l=cu*;%R50yxj8!Pd#{4B27$8tY?gU2r+3E@_HF?*^oW& zoM&riXFzjF3X4-;JbAznG3*S_tByip`2YhO`|Hcg(DVn2CO|ulO2Z}}yHObl&=AWY z{=ZO-LuMPiGwm&T?9P|oaO(3xtNS2!3;_i%{rK%KdlQtMljqGvJ7fIjf|DF1r1J2h zWk$^7pA>`ITGr*@8^b)HV~mfWy(LS5yf0HAA`F7-!8U`-@6Z5VT3>x#%FqTIFqVih z>n<&L6Z#15VZm~J2WMxQ2}MXwMa8@k=)&j|s5v?oxo=#Xp(4IVckrYTz%y-om)S6g zob;~tG9P-q=omr?vSXIM9N?Qa$e^aQ0}Zs#VHonA@rjaOyFn*tf>dZ)w$Ke0M^`kR9MpGziHG&h22!F(zr6`o1JjMJNWW zNU?FS_Z*>Sw?Z-G+ID1^BRvWi06>z<_fHAccB6mR#^n(2P(9zh>snr=?6ioZKpBY< z$v)aG-dOHaIKuVipfWt&T>>W|r&@sQ4`%kEn0^j*@*Su=cCq2C*OR6knw|I*rQ%nj zlv&Tdcq=Bk&BwH=R;Afeh*=nWkm^p^&pY|q9z-4!tFOYyf+x9`fmT6-!EWWJMOCJ8 zqJKm|)@RhRqqX57`cCxiJ;-cO1hkT^Fm3x-Fu(OO9Zrhb^+`4o#N~%JG4U#zq(o@Z zVgU(6q630?EiJyNABctmre9~{hgSEk?f?SaY%fE!PE_zYHhu51A=-+=jxpMi}Bz~o^ z@KnekHT8B4!tEoiNRJGmta1ppg4#Qq zdPZNJ@GY8m#H|`aJwU(ObUp+<>}w>eJH>BXU1OtdV%cNv@G$Ho%Zoeu-#lQ2Qh}pXdR?U&0v_kuo?qSi7=Gcv~;H7Os`HyXj@w z4q9u^>^l%duF^L5xiJx0W@he$p4wx@ep`z7Q)9iF<{US-v&)JxaZ9iwatjw7YOi9y zgy2%;j8L5pa%YrZ9fXfF5&j4znHTeC-#Q7ur27s*ZY;zqdj?JJ_2S9~{H)vtnjvg( zuQ};_!Wu9|Aq`QDatmXATsO9%HyIcftZJ>iX8;PlZMp> z;aLYwyq)^ySlJ3c%_PeeiZ(-p;dkbmsSvYeZ>bsPjSFJKTUOi+u33K04OTLjjVBHv zt@p_czf4nHVrQ9=M0EtsC%m4Kh~)h9k5$Z!PFnewqAqoYvj0zWiK=uulW6T%f|Tu} zXF3BsxG2oZLs4aLSg`&`f~^F%m?zQeDu(#Jq6?E2&}@Pg5-{kmTR7)_uDpA3-LToS zXA6l5h^OPVl7R}Ah=qaSW}rBSeFjc2j!+3Zd|z`iU2zjYh(1_X*VcRCyV(LKe_?vyw4yjnxp2Xg=t2kTq-`E@wB$xplR-1a*(=(dK+t4P)pdnzC-_yd zbmTuCc^`-)<4@_>#FE z7-}=*eL%4nArb;X4+@u;vCz{qPfSWRG+;gYF7Ay{m}M1VIj&@EWxymhi2(MLO_wVw zVuZZ-_3K@+3#$z8F@a%^j~4A>R0!1*#4jVXp?wq0vPe*D%!mkOXLk@QD*gRDUIe8j z7)b0ZTm>i|68qjVa{qg{{n{U+uYDF(X56cvKL4nP~pe&)rZbG zKeY05b*b{TlA}^jDrcpbL>kRvIxjc(cTn}(HxD8c4a->X(lHyLbcT-d(lqyff4H-o zP}i`A@0yxC$tZ|VV-YDbh5}Q|bpG7!`XTF;ih5~!E?w%yI&nQl0j#DFnn1SXK71HW z(OC89QFyIeWOw~25ibv!`WaA}E5)?xP6VUzbW7R6qFc?vKD+z9B&OHePtDB=O2axc zS8YI1O&_feKrM!sk$7Lfc5NW3p=F}Exn?_|_v`&J$SaVq7Nv&#5VHj(!w&nwNJouR}qm>mPEI!Cqj-V)Sb+U?4zf<;a< zU8ugjzOFxhOVV>%mL48@ln^;-X@-pizzBbq zvGskP?kZFcH95v3GfTRKeLBx_iJcGAsEd}mbLY;& z%Yj5jsxUg=G}z&f=g*fBC63fIO{I?J(j0o7Y3vZvbK%TM5hM4$fA#13$Bnj=g=#ZL z1Z=Z{MwL&)T?oGCw6Y7}jE4N{mj|3esHAbfrRv3-7sF1*`)hK7Mf2`{OKd>K_xxQ- zTIuUn$VH{rIZb~eqdfP^%9T)j0HuJ%O^GN=QRICWWBhtoXlQmpLG7-GSsBnjvLTcy z_ygjU^0!$H>hrtw;byGAFmUqZJZBc&DtE>SqLgG>?RN+1G#QW`^^!1||pcOJ(BzF>Fi0l`3 zi#_F2_C2VhJK2EDB6@2yo=kVoQ`T}A@Zy-B!#4S;*qu`8W8}9IgezJ_&1i=}R{nz= z8+5uS9E6DKVitI|?LgL-xNNWSS&NI>f~s|5rSgn3=F9Bta;G$1?bSvzp+JP_rluPH zq7V(RnVg^!#Fqr3VOK21BdCrhmshE1*_;?eN>|6c3hZcfX5bJK8#UF5+O#g(pLpZKL4hvxr z)qym{90%0KGZv3@Q^&dq#KrO!)) z!iNc_uQi8c0U9B}(3LkbONnE5wA;BAmr}-TCVNaBe|Xb27J&QT_U@gT)Id}UXnuNt z7vR#;>nHX&5veAa*gvQ>eFn2(t7s zf1DWb_bLOu5Y;xvd)uWySD{21cBbcy-N2iXIR3#q&3e=bwe^@G`VE6d7(vPr+0d$o zB`3e_n-;I%!cMi}b&-qy#Qdf(dcpx%L2fHxTvXYj<$X`Gx!9%0eNG|-CRUFoqqVCn#E-A?ZwcNLJi&^=kDD|c>J0< z>wnbHn9QjJG7AkNfw^{|zf%kjiV@tlr&D=P@J+4ojtyK6l7guQTAynmjy-P!343>W zC4{|d7LOzdEoRU23znH`dbL-UI;d4{89}`IjYbGl8j;8h1IR1&^V2ktA zFcTw20?1^TFn-ESR{}M!_hC6l;GYG?eb`Yx9CnrDQp|Gp2%R1(ohFX*_;KUHQD!5Z zLfKp1{`+EP!r4Mu1jlPu5UN%BZEScpjhw4M|0W>p>R=mNz4nqoOFHZ3_D^g9ADDG0pD zel%z;IzxE1*|2+JosoXrw**nAO%Y z3{}ri-sE{JPX)SQ7LJm&tbrX`{7$D$nwcev6SWrBXbBLg-JZ0>&1ZGlN+u zYDrPVwKtCrtLE5yD6(B((m1@^dAoe|RTUYo(Os~NJPr(1ns*>o2m8(lQ4EaF;u=bH z@xx6_F5kZ05Vy!+XeSByPH>PC2q&ZRG-w+tyC6v`kMZoVqJ+T%F$m#+`cHk*-0$1J zzf@f73F3xKyhaTcwqr-fm|mS~U%XIXKbISxD1Qcn{6So|vA1fBlIsieH;HgK$H3l2 zYDC2i%I?{xPd+JJsHeayLIe&?EOFd%;D~zf-U&e#0UzsJ{O4-{UG> zOo930Y-a9MF6DCV2SGvD0P#4qx(~YfZbbZ!Mx;}=P^p{7fXPOny7HTpdf6;XZi-!- z1bxk*S?-eL&_Cqb=(nav9%FhDfCHGhDpe_z|(CvL&VX z^2*GXDLL>&^ps}q4MunR?#9?kp|&LQ*iW5GZ8+!kn)eyZ_ehvPoBz^pn|>GKIETq5 z%w)`%n`KF<2@^WqXHo#&)T8CoEa{Gm`vE?G?wCBLuV~w2!P7MxI)I*r(94Qx2)g|< z2=Ue{m0>tKRL?chv2p#%XlOY+h|rxfLruxnsdQJaR6BmzIP4gh;1kn;)Pd|`+lsM| zT&aVyZ})E#Rmfk{wJZ=sNnj-t3qnHl4Zl5JWHH)W;?Z})*r5ovb8~#E3K(ZtU>2>_ zN6TFXF!gjGvjPgMMNkpL0xzki>wFEO`Ld`kxv+X^tSIt-Vb-g(dp=YRTb>JOg+Zr# zjd#rR!FqZpr-a1pBM5N`<2Yv4Gn-a(j{{kv;{vE=0Hao=7ZY~m&asZ4owR)I$Efub zFE5fHF1Vdi>xT**zr*X~LqJ|`P7d}mJLLLABXQRP9Sr|gs$Phg5Tuxxel@fEHq=C~ zo1wN1h;kOuI9-aecd$@DH+8wzolY#S3k;&=A+~_rEMfU+{0JLUC^IBDF&t2m`th5~ zlw((-SrF;4kZRTh&Zpx*ef?0<(6b4vc^l#`o?w(0^yLnjEzWFf-AMCnfBfiWyP>I| zDyLB(NB*WQ;rfJZq>aM%WVi=|RLH1Loi|svE?=@dT=gC}A1h%qAnW^ii<64er|Uc0(?PzB z#!3W<7InJM>&bN)P)~`qpF1DwqAJ(kYh&4-#d8T}rGBlHpm7MMm3H57?N$BgkqGt< zIuo_>d!RxQLCb^L1bU-eXyB)({bJ?aV>4pfh1gc~sOseOry#J!%Lswag5L?F8Gsao z`L5*b6t~m8m>QsH4Z}aTq~xsPAa(gF8oT1JTZgLr^p$JlPbhrNr)(ajZ`(9PRXtp% z1HYJ0Is + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + xs ys + + Chord axis + + vy vx + + + 3/4 + ac + t n + + α + ω + + + v + diff --git a/docs/source/user/aerodyn/index.rst b/docs/source/user/aerodyn/index.rst index 8d618f7fb..38ec6cfd6 100644 --- a/docs/source/user/aerodyn/index.rst +++ b/docs/source/user/aerodyn/index.rst @@ -21,5 +21,7 @@ AeroDyn Users Guide and Theory Manual modeling.rst driver.rst theory.rst + theory_ua.rst + zrefs.rst appendix.rst diff --git a/docs/source/user/aerodyn/input.rst b/docs/source/user/aerodyn/input.rst index 9b81efd6e..8279f667e 100644 --- a/docs/source/user/aerodyn/input.rst +++ b/docs/source/user/aerodyn/input.rst @@ -293,6 +293,8 @@ described in :numref:`OLAF-Input-Files`. ``OLAFInputFileName`` is the filename for this input file. +.. _ad_ua_inputs: + Unsteady Airfoil Aerodynamics Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -306,6 +308,11 @@ The input parameters in this section are used only when ``AFAeroMod - ``3``: the extensions to B-L developed by Minnema/Pierce - ``4``: a continuous-state model developed by Hansen, Gaunna, and Madsen (HGM) - ``5``: a model similar to HGM with an additional state for vortex generation +- ``6``: Oye's dynamic stall model +- ``7``: Boeing-Vertol model + +The models are described in :numref:`AD_UA`. + **While all of the UA models are documented in this manual, the original B-L model is not yet functional. Testing has shown @@ -513,6 +520,13 @@ When ``InterpOrd`` is 1, linear interpolation is used; when if the keyword ``DEFAULT`` is entered in place of a numerical value, ``InterpOrd`` is set to 1. + +``RelThickness`` is the non-dimensional thickness of the airfoil +(thickness over chord ratio), expressed as a fraction (not a percentage), +typically between 0.1 and 1. +The parameter is currently used when `UAMod=7`, but might be used more in the future. +The default value of 0.2 if provided for convenience. + ``NonDimArea`` is the nondimensional airfoil area (normalized by the local ``BlChord`` squared), but is currently unused by AeroDyn. ``NumCoords`` is the number of points to define the exterior shape of diff --git a/docs/source/user/aerodyn/theory.rst b/docs/source/user/aerodyn/theory.rst index 3df718077..dfa632f1c 100644 --- a/docs/source/user/aerodyn/theory.rst +++ b/docs/source/user/aerodyn/theory.rst @@ -1,10 +1,17 @@ .. _AD_theory: -AeroDynTheory -============= +AeroDyn Theory +============== -This theory manual is work in progress, please refer to the AeroDyn manual for more details. +This theory manual is work in progress, please refer to the AeroDyn 14 manual for more details :cite:`ad-AeroDyn:manual`. Many changes have occured since AeroDyn 14 (e.g. BEM formulation, coordinate system used in the BEM equations, dynamic stall, dynamic BEM), but these changes are not yet documented here. + + + +Steady BEM +~~~~~~~~~~ + +The steady blade element momentum (BEM) equations are solved as a constrained equation, and the formulation follows the description from Ning :cite:`ad-Ning:2014`. .. _AD_twr_shadow: @@ -32,45 +39,3 @@ where :math:`TI` is the turbulence intensity at the tower node. -.. _AD_UA: - -Unsteady aerodynamics ---------------------- - -Beddoes-Leishman type models (UAMod=2,3) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The Beddoes-Leishman type dynamic stall models are currently described in the document: -The Unsteady Aerodynamics Module for FAST 8, from Rick Damiani and Greg Hayman (2017) - - -Beddoes-Leishman state space models (UAMod=4,5) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -TODO - - -Oye model (UAMod=6) -~~~~~~~~~~~~~~~~~~~ - - -See Hansen book - -Boeing-Vertol model (UAMod=7) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The Boeing-Vertol is used mentioned in the following paper: The Development of CACTUS, a Wind and Marine Turbine Performance Simulation Code from Jonathan C. Murray and Matthew Barone (2011). -The documentation presented here was inspired from the implementation done in the vortex code CACTUS. - - - -.. math:: - - \alpha_{dyn} = \alpha_qs - k_1 \gamma \sqrt{\left| \frac{c\dot{\alpha}}{2U}\right|} - - - - - - - diff --git a/docs/source/user/aerodyn/theory_ua.rst b/docs/source/user/aerodyn/theory_ua.rst new file mode 100644 index 000000000..e4f535aec --- /dev/null +++ b/docs/source/user/aerodyn/theory_ua.rst @@ -0,0 +1,496 @@ + + + +.. _AD_UA: + +Unsteady aerodynamics +===================== + + +The Unsteady Aerodynamic (UA) models account for flow hysteresis, including unsteady attached +flow, trailing-edge flow separation, dynamic stall, and flow reattachment. +*Dynamic stall* refers to rapid aerodynamic changes that may bring about +or delay stall behavior :cite:`ad-Branlard:book`. Rapid changes in wind speed (for example, when +the blades pass through the tower shadow) cause a sudden detachment and +then reattachment of air flow along the airfoil. Such effects at the +blade surface cannot be predicted with steady state aerodynamics, but +may affect turbine operation, not only when the blades encounter tower +shadow, but also during operation in skewed flows and turbulent wind conditions. Dynamic +stall effects occur on time scales of the order of the time for the +relative wind at the blade to traverse the blade chord, approximately +:math:`c/\Omega r`. For large wind turbines, this might be on the order +of :math:`0.5` seconds at the blade root to :math:`0.001` seconds at the +blade tip. Dynamic stall can result in high transient forces as the wind +speed increases, but stall is delayed. + + + +Theory +------ + +The different dynamic stall models implemented in AeroDyn are presented below. + + + + +.. _ua_notations: + +Notations and Definitions +~~~~~~~~~~~~~~~~~~~~~~~~~ + + +See :numref:`airfoil_data_input_file` for a comprehensive description of all +the inputs present in the profile input file (including some of the ones repeated below). + + +The airfoil section coordinate system and main variables are presented in :numref:`fig:UAAirfoilSystem` and further described below: + +.. figure:: figs/UAAirfoilSystem.png + :width: 60% + :name: fig:UAAirfoilSystem + + Definition of aifoil section coordinate system used in the unsteady aerodynamics module + +- Aerodynamic Center (AC): point of the airfoil cross section where the + aerodynamic forces and moment are assumed to act. Usually close to + the 1/4 chord point for a regular airfoil and at the center for a + circular cross section + +- “3/4” chord point: in the original formulation this point refers to + the point on the chord axis located 3/4 chord behind the leading + edge. This concept is here generalized to the point located mid-way + between the aerodynamic center and the trailing edge, to account for + aerodynamic center positions that differ strongly from a 1/4 chord + point. The notation :math:`3/4` is kept in this document. + +- :math:`\omega`: rotational speed of the airfoil section + (pitching/torsional rate) positive around z. + +- :math:`\boldsymbol{v}_{ac}`: velocity vector at the aerodynamic + center + :math:`\boldsymbol{v}_{ac}=[v_{x,ac}, v_{y,ac}]` + (coordinates assumed to be expressed in the airfoil section + coordinate system) + +- :math:`\boldsymbol{v}_{34}`: velocity vector at the 3/4 chord point + :math:`\boldsymbol{v}_{34}=[v_{x,34}, v_{y,34}]`\ (coordinates + assumed to be expressed in the airfoil section coordinate system) + The velocity is obtained from the velocity at the 1/4 chord point and the + rotational speed of the section: + :math:`\boldsymbol{v}_{34}=\boldsymbol{v}_{ac}+\omega d_{34} \hat{\boldsymbol{x}}_s` + where :math:`d_{34}` is the distance between the aerodynamic center + and the 3/4 chord point. + + + +- :math:`U_{ac}`: velocity norm at the aerodynamic center. + :math:`U_{ac}=\lVert\boldsymbol{v}_{ac}\rVert=\sqrt{v_{x,ac}^2 + v_{y,ac}^2}` + +- :math:`\alpha_{ac}`: angle of attack at the aerodynamic center + :math:`\alpha_{ac}=\operatorname{atan2}(v_{x,ac},v_{y,ac})` + +- :math:`\alpha_{34}`: angle of attack at the 3/4 chord point + :math:`\alpha_{34}=\operatorname{atan2}(v_{x,34},v_{y,34})` + + +- :math:`\boldsymbol{x}`: the vector of states used by the continuous formulations + +- :math:`c`: airfoil chord + +- :math:`C_l^{st}, C_d^{st}, C_m^{st}`: static airfoil coefficients + +- :math:`\alpha_0`: angle of attack at zero lift, :math:`C_l^{st}(\alpha_0)=0` + +- :math:`\alpha_1`: angle of attack close to positive stall. +- :math:`\alpha_2`: angle of attack close to negative stall. + +- :math:`C_{l,\alpha}`: slope of the steady lift curve about :math:`\alpha_0`. + +- :math:`f^{st}_s(\alpha)`: is the steady separation function, determined from the lift curve :math:`C_l^{st}(\alpha)` (see below, and e.g. :cite:`ad-Hansen:2004`) + +- :math:`A_1`, :math:`A_2`, :math:`b_1`, :math:`b_2`: are four constants, characteristic of the propagation of the wake vorticity (Wagner constants) + +**Time constants:** + + - :math:`T_u(t) = \frac{c}{2U_{ac}(t)} \in [0.001, 50]`: Time for the flow to go over half the airfoil section. The value is plateaued to avoid unphysical values. + - :math:`T_{f,0}`: Dimensionless time constant associated with leading edge separation. Default is 3. + - :math:`T_{p,0}`: Dimensionless time constant for the boundary-layer,leading edge pressure gradient. Default is 1.7 + + + +**Separation function:** + +The steady separation function, :math:`f_s^{st}`, is defined as the separation +point on a flat plate for a potential Kirchhoff flow :cite:`ad-Hansen:2004`: + +.. math:: + + \begin{aligned} + \text{Close to $\alpha_0$}, + f_s^{st}(\alpha) &= \operatorname{min}\left\{\left[2 \sqrt{ \frac{C_l^{st}(\alpha)}{C_{l,\alpha}(\alpha-\alpha_0) } } -1 \right]^2 , 1 \right\} + ,\quad + \text{away from $\alpha_0$}, + f_s^{st}(\alpha)=0 + \end{aligned} + +When :math:`\alpha=\alpha_0`, :math:`f_s^{st}(\alpha_0)=1`. Away from +:math:`\alpha_0`, the function drops progressively to :math:`0`. As soon +as the function reaches :math:`0` on both sides of :math:`\alpha_0`, +then :math:`f_s^{st}` is kept at the constant value :math:`0`. + + +**Inviscid and fully separated lift coefficient:** +The inviscid lift coefficient is +:math:`C_{l,\text{inv}}= C_{l,\alpha} (\alpha-\alpha_0)`. +The fully separated lift coefficient may +be modelled in different ways (:cite:`ad-Branlard:book`). +In most engineering models, the slope of +the fully separated lift coefficient around :math:`\alpha_0` is +:math:`C_{l,\alpha}/2`. In the Unsteady AeroDynamics sub-module, +the fully separated lift coefficient is derived from the steady separation +function as: + +.. math:: + + \begin{aligned} + C_{l,\text{fs}}(\alpha) = \frac{C_l^{st}(\alpha) - C_{l,\alpha}(\alpha-\alpha_0)f_s^{st}(\alpha)}{1-f_s^{st}(\alpha)} + \text{when $f_s^{st}\neq 1$} + , \qquad + C_{l,\text{fs}}(\alpha) =\frac{C_l^{st}(\alpha)}{2} + \text{when $f_s^{st}=1$}\end{aligned} + + + + + +Beddoes-Leishman type models (UAMod=2,3) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Beddoes-Leishman model account for attached flows and trailing edge stall :cite:`ad-LeishmanBeddoes:1989`. + +Two variants are implemented in the Unsteady Aerodynamic module. These two (compressible) models are currently described in the following reference: :cite:`ad-AeroDyn:manualUnsteady`. The models use :math:`C_n` and :math:`C_c` as main physical quantities. The models use discrete states and cannot be used with linearization. + + + +Beddoes-Leishman 4-states model (UAMod=4) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The 4-states (incompressible) dynamic stall model from Hansen-Gaunaa-Madsen (HGM) is described in :cite:`ad-Hansen:2004` and enabled using ``UAMod=4``. The model uses :math:`C_l` as main physical quantity. +Linearization of the model will be available in the future. + + +**State equation:** +The state equation of the model is: + +.. math:: + + \begin{aligned} + \dot{x}_1 &= - T_u^{-1} b_1\, x_1 + T_u^{-1} b_1 A_1 \alpha_{34}\nonumber \\ + \dot{x}_2 &= - T_u^{-1} b_2\, x_2 + T_u^{-1} b_2 A_2 \alpha_{34}\nonumber \\ + \dot{x}_3 &= - T_p^{-1} x_3 + T_p^{-1} C_l^p \nonumber \\ + \dot{x}_4 &= - T_f^{-1} x_4 + T_f^{-1} f_s^{st}(\alpha_F) ,\qquad x_4 \in[0,1] + \nonumber + \end{aligned} + +with + +.. math:: + + \begin{aligned} + \alpha_E(t) & =\alpha_{34}(t)(1-A_1-A_2)+ x_1(t) + x_2(t) \nonumber \\ + C_{L}^p(t) & =C_{l,\alpha} \, \left(\alpha_E(t)-\alpha_0\right) + \pi T_u(t) \omega(t) \nonumber \\ + \alpha_F(t) & =\frac{x_3(t)}{C_{l,\alpha}}+\alpha_0 \nonumber + \end{aligned} + + +**Output equation:** +The unsteady airfoil coefficients +:math:`C_{l,\text{dyn}}`, :math:`C_{d,\text{dyn}}`, +:math:`C_{m,\text{dyn}}` are obtained from the states as follows: + +.. math:: + + \begin{aligned} + C_{l,\text{dyn}}(t) &= x_4 (\alpha_E-\alpha_0) C_{l,\alpha} + (1-x_4) C_{l,{fs}}(\alpha_E)+ \pi T_u \omega \\ + C_{d,\text{dyn}}(t) &= C_d(\alpha_E) + (\alpha_{ac}-\alpha_E) C_{l,\text{dyn}} + \left[ C_d(\alpha_E)-C_d(\alpha_0)\right ] \Delta C_{d,f}'' \\ + % C_{m,\text{dyn}}(t) &= C_m(\alpha_E) + C_{l,\text{dyn}} \Delta C_{m,f}'' - \frac{\pi}{2} T_u \omega\\ + C_{m,\text{dyn}}(t) &= C_m(\alpha_E) - \frac{\pi}{2} T_u \omega\\ + \end{aligned} + +with: + +.. math:: + \begin{aligned} + \Delta C_{d,f}'' &= \frac{\sqrt{f_s^{st}(\alpha_E)}-\sqrt{x_4}}{2} - \frac{f_s^{st}(\alpha_E)-x_4}{4} + ,\qquad + x_4\ge 0 + \end{aligned} + + + + + + +Beddoes-Leishman 5-states model (UAMod=5) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The 5-states (incompressible) dynamic stall model is described in the Bladed Theory Manual :cite:`ad-Bladed:manual` and enabled using ``UAMod=5``. The model uses :math:`C_n` and :math:`C_c` as main physical quantities. +Linearization of the model will be available in the future. + + + + + + +.. _ua_oye: + +Oye model (UAMod=6) +~~~~~~~~~~~~~~~~~~~ + +Oye's dynamic stall model is a one-state (continuous) model, formulated in :cite:`ad-Oye:1991` and described e.g. in :cite:`ad-Branlard:book`. +The model attempts to capture trailing edge stall. +Linearization of the model will be available in the future. + + +**State equation:** +Oye's dynamic stall model uses one state, :math:`\boldsymbol{x}=[f_s]` +where :math:`f_s` is the unsteady separation function. +The state equation is a first-order differential equation: + +.. math:: + + \begin{aligned} + \frac{df_s(t)}{dt} =- \frac{1}{T_f} f_s(t) + \frac{1}{T_f} f_s^{st}(\alpha_{34}(t)) + \end{aligned} + +where :math:`T_f=T_{f,0} T_u` is the time constant of +the flow separation and :math:`f_s^{st}` is the steady state separation function described in :numref:`ua_notations`. +The value :math:`T_{f,0}` is usually chosen around 6 (different from the default value). +It is readily seen that :math:`f_s` +reaches the value :math:`f_s^{st}` when the system is in a steady state +(i.e. when :math:`\frac{df_s(t)}{dt}=0`). + + +**Output equation:** +The unsteady lift coefficient is computed as a linear combination of the inviscid lift +coefficient, :math:`C_{l, \text{inv}}`, and the fully separated lift +coefficient :math:`C_{l,\text{fs}}`. Both of these lift coefficients are +determined from the steady lift coefficient, usually provided as +tabulated data, noted :math:`C_l^{st}(\alpha)`, where the superscript +:math:`st` stands for “steady”. +The unsteady lift coefficient is +modelled as: + +.. math:: + + \begin{aligned} + C_{l,\text{dyn}}(\alpha_{34} ,t) = f_s(t)\; C_{l,\text{inv}}(\alpha_{34}) + (1-f_s(t))\; C_{l,\text{fs}}(\alpha_{34}) + \end{aligned} + +where :math:`\alpha_{34}` is the instantaneous angle of attack at the 3/4 chord. +:math:`f_s` is seen to act as a relaxation factor between the two flow situations. + + + + + + + + +Boeing-Vertol model (UAMod=7) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +The Boeing-Vertol is mentioned in the following paper :cite:`ad-Murray:2011`. Details of the model were omitted in this reference, so the documentation presented here is inspired from the implementation done in the vortex code CACTUS, which was reproduced to quasi-identity in AeroDyn. Linearization is not possible with this model. + +The model as presented in :cite:`ad-Murray:2011` is an output-only model, where the dynamic angle of attack is determined using the quasi steady angle of attack and the rate of change of the angle of attack: + +.. math:: + + \alpha_{dyn} = \alpha_{34} - k_1 \gamma \sqrt{\left| \dot{\alpha} T_u\right|} + + +where :math:`k_1` and :math:`\gamma` are constants of the model. In practice, the implementation is different for the lift and drag coefficients, and for negative and positive stall. The model needs a discrete state to calculate the rate of change of the angle of attack and two discrete states to keep track of whether the model is activated or not. + + +**Airfoil constants:** + +The constants :math:`k_1`, for positive and negative rates of angle of attack, are set to: + +.. math:: + + k_{1,p}= 1 ,\quad k_{1,n} = 1/2 + + +The extent of the transition region is computed as: + +.. math:: + + \Delta \alpha_\text{max} = \frac{0.9 \operatorname{min}\left(|\alpha_1-\alpha_0|, |\alpha_2-\alpha_0|\right)}{\operatorname{max}(k_{1,p},k_{1,n})} + +where :math:`\alpha_1` and :math:`\alpha_2` are the angle of attack at positive and negative stall respectively (taken as the values from the airfoil input file). +The factor 0.9 is a margin to prevent the effective angle of attack to reach :math:`\alpha_0` during stall. + + + +**Intermediate variables:** + +The variables :math:`\gamma` for the lift and drag are computed as function of the thickness to chord ratio of the airfoil :math:`t_c` and the Mach number :math:`M_a` (assumed to be 0 in the current implementation): + +.. math:: + + \begin{aligned} + \gamma_L &= (1.4-6\delta)\left[1-\frac{\text{Ma}-(0.4+5\delta)}{0.9+2.5\delta-(0.4+5\delta)}\right] &&\\ + \gamma_D &= (1-2.5\delta) ,&&\text{if}\ \text{Ma} < 0.2 \\ + \gamma_D &= (1-2.5\delta)\left[1-\frac{\text{Ma}-0.2}{(0.7+2.5\delta-0.2)}\right] ,&& \text{otherwise} + \end{aligned} + +where :math:`\delta = 0.06-t_c`. + + +**Update of discrete states (and intermediate variables):** + +The rate of change of the angle of attack is computed as: + +.. math:: + + \dot{\alpha} = \frac{\alpha_{34}(t+\Delta t) - \alpha_{34}(t)}{\Delta t} + +An additional state was introduced to avoid sudden jump of :math:`\dot{\alpha}`, by storing its value. Rates that are beyond a fraction of :math:`\pi \Delta t` are replaced with the values at the previous time step. This feature is not present in the CACTUS implementation. + + +The dynamic angle of attack offsets (lags) for the lift and drag are computed as: + +.. math:: + + \begin{aligned} + \Delta \alpha_L &= k_1 \operatorname{min} \left(\gamma_L \sqrt{\dot{|\alpha}T_u|} , \Delta \alpha_\text{max}\right)\\ + \Delta \alpha_D &= k_1 \operatorname{min}\left(\gamma_D \sqrt{\dot{|\alpha}T_u|}, \Delta \alpha_\text{max} \right) + \end{aligned} + +The value of :math:`k_1` is taken as :math:`k_{1,n}` if :math:`\dot{\alpha}(\alpha_{34}-\alpha_0)<0`, and taken as :math:`k_{1,p}` otherwise. +The lagged angle of attacks for the lift and drag are: + +.. math:: + + \begin{aligned} + \alpha_{\text{Lag},L} &= \alpha_{34} - \Delta \alpha_L\operatorname{sign}(\dot{\alpha}) \\ + \alpha_{\text{Lag},D} &= \alpha_{34} - \Delta \alpha_D\operatorname{sign}(\dot{\alpha}) + \end{aligned} + +The distances to positive and negative stall are computed as follows. +If :math:`\dot{\alpha}(\alpha_{34}-\alpha_0)<0` and the dynamic stall is active: + +.. math:: + + \Delta_n = \alpha_2 - \alpha_{\text{Lag},D} , \quad \Delta_p = \alpha_{\text{Lag},D} - \alpha_1 + +If :math:`\dot{\alpha}(\alpha_{34}-\alpha_0)<0` and the dynamic stall is not active: + +.. math:: + + \Delta_n = 0 , \quad \Delta_p = 0 + + +If :math:`\dot{\alpha}(\alpha_{34}-\alpha_0)\ge0`: + +.. math:: + + \Delta_n = \alpha_2 - \alpha_{34}, \qquad + \Delta_p = \alpha_{34} - \alpha_1 + +The effective angle of attack for the lift coefficient is taken as the lagged angle of attack: + +.. math:: + + \begin{aligned} + \alpha_{e,L} &= \alpha_{\text{Lag},L} + \end{aligned} + +The effective angle of attack for the drag coefficient is obtained from the lagged angle of attack and the deltas to stall: + +.. math:: + + \begin{aligned} + \alpha_{e,D} &= \alpha_{\text{Lag},D}, &&\text{if}\ \Delta_n>T \ \text{or} \ \Delta_p > T \\ + \alpha_{e,D} &= \alpha_{34}+(\alpha_{\text{Lag},D}-\alpha_{34}) \frac{\Delta_n}{T} , &&\text{if}\ \Delta_n>0 \ \text{and}\ \Delta_n < T \\ + \alpha_{e,D} &= \alpha_{34}+(\alpha_{\text{Lag},D}-\alpha_{34}) \frac{\Delta_p}{T} , &&\text{if}\ \Delta_p>0 \ \text{and}\ \Delta_p < T \\ + \alpha_{e,D} &= \alpha_{34} , &&\text{otherwise} + \end{aligned} + +where :math:`T=2\Delta\alpha_\text{max}` is the extent of the "transition" region. + +The lift dynamic stall state is activated if :math:`\dot{\alpha}(\alpha_{34}-\alpha_0) \ge 0` +and if the angle of attack is above :math:`\alpha_1` or below :math:`\alpha_2`. +The state is turned off if :math:`\dot{\alpha}(\alpha_{34}-\alpha_0) < 0` +and the effective angle of attack is below :math:`\alpha_1` and above :math:`\alpha_2`. + +The drag dynamic stall state is activated if any of the condition below are true: + +.. math:: + \begin{aligned} + &\Delta_n > T \ \text{or }\ \Delta_p > T \\ + &\Delta_n > 0 \ \text{and}\ \Delta_n < T \\ + &\Delta_p > 0 \ \text{and}\ \Delta_p < T + \end{aligned} + +The state is turned off otherwise. + + + +**Calculation of outputs:** +The calculation of the dynamic lift and drag coefficients is done as follows + +.. math:: + \begin{aligned} + C_{l,\text{dyn}}&=\frac{C_l^{st}(\alpha_{e,L})}{\alpha_{e,L}-\alpha_0} ,\quad \text{if dynamic stall active for $C_l$}\ \\ + C_{l,\text{dyn}}&=C_l^{st}(\alpha_{34}) ,\quad\quad \text{otherwise} \\ + C_{d,\text{dyn}}&=C_d^{st}(\alpha_{e,D}) + \end{aligned} + +Recalculation of intermediate variables are necessary to obtain :math:`\alpha_{e,L}` and :math:`\alpha_{e,D}`. +The moment coefficient is calculated based on values at the aerodynamic center and mid-chord ("50"): + +.. math:: + C_{m,\text{dyn}} = C_m^{st}(\alpha_{ac}) + \cos\alpha_{50} \left[C_l^{st}(\alpha_{34}) - C_l^{st}(\alpha_{50})\right]/4 + +where :math:`\alpha_{50}` is computed the same way as :math:`\alpha_{34}` (using the velocity at the aerodynamic center and the rotational rate of the airfoil) but using the distance from the aerodynamic center to the mid-chord (see :numref:`ua_notations`). + + + +Inputs +------ + +See :numref:`ad_ua_inputs` for a description of the inputs necessary in the AeroDyn primary file (e.g. ``UAMod``) + +See :numref:`airfoil_data_input_file` for a more comprehensive description of all the inputs present in the profile input file. + +See :numref:`ua_notations` for a list of notations and definitions specific to unsteady aerodynamic inputs. + +An example of profile data (containing some of the unsteady aerodynamic parameters) is available here +:download:`(here) `. + + + + + +Outputs +------- + +Outputting variables of the dynamic stall models is possible, but requires +to set preprocessor variable ``UA_OUTS`` and recompile the program (OpenFAST, AeroDyn Driver, or Unsteady Aero driver). +The outputs are written in output files with extension `*.UA.out`. +To activate these outputs with `cmake`, compile using ``-DCMAKE_Fortran_FLAGS="-DUA_OUTS=ON"`` + + + +Driver +------ + +A driver is available to run simulations for a single airfoil, using sinusoidal variation of the angle of attack, +or user defined time series of angle of attack, relative wind speed and pitch rate. + +Using `cmake`, the driver is compiled using `make unsteadyaero_driver`, resulting as an executable in the `aerodyn` folder. + +An example of driver input file is available here: :download:`here <../aerodyn-dynamicStall/examples/UA-driver.dvr>`. +An example of time series input is available here: :download:`here <../aerodyn-dynamicStall/examples/UA-driver-timeseries.dat>` + diff --git a/docs/source/user/aerodyn/zrefs.rst b/docs/source/user/aerodyn/zrefs.rst new file mode 100644 index 000000000..11a11907f --- /dev/null +++ b/docs/source/user/aerodyn/zrefs.rst @@ -0,0 +1,10 @@ +.. only:: html + + References + ---------- + +.. bibliography:: bibliography.bib + :labelprefix: ad- + :keyprefix: ad- + + From 7f4ffe921ced5a64bb6c2c9e67dba5a8b4b5b890 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 31 Aug 2021 11:01:12 -0600 Subject: [PATCH 23/24] UA: RelThickness optional on line 7 of profile file --- modules/aerodyn/src/AirfoilInfo.f90 | 13 +++++++++++-- modules/aerodyn/src/UnsteadyAero.f90 | 6 ++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/aerodyn/src/AirfoilInfo.f90 b/modules/aerodyn/src/AirfoilInfo.f90 index 247eb65b3..5777ada0d 100644 --- a/modules/aerodyn/src/AirfoilInfo.f90 +++ b/modules/aerodyn/src/AirfoilInfo.f90 @@ -391,6 +391,7 @@ SUBROUTINE ReadAFfile ( InitInp, NumCoefsIn, p, ErrStat, ErrMsg, UnEc ) CHARACTER(*), PARAMETER :: RoutineName = 'ReadAFfile' CHARACTER(10) :: defaultStr CHARACTER(1024) :: PriPath + CHARACTER(1024) :: sLine TYPE (AFI_UA_BL_Default_Type), ALLOCATABLE :: CalcDefaults(:) ! Whether to calculate default values for the UA parameters @@ -424,8 +425,16 @@ SUBROUTINE ReadAFfile ( InitInp, NumCoefsIn, p, ErrStat, ErrMsg, UnEc ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! RelThickness, default is 0.2 if user doesn't know it, only used for Boing-Vertol UA model = 7 - CALL ParseVarWDefault ( FileInfo, CurLine, 'RelThickness', p%RelThickness, 0.2, ErrStat2, ErrMsg2, UnEc ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + sLine = FileInfo%Lines(CurLine) + call Conv2UC(sLine) ! to uppercase + if (index(sLine, 'RELTHICKNESS')>1) then + CALL ParseVarWDefault ( FileInfo, CurLine, 'RelThickness', p%RelThickness, 0.2, ErrStat2, ErrMsg2, UnEc ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + else + p%RelThickness=-1 ! To trigger an error + !call WrScr('Skipping. RelThickness not found on line 7 of Profile file: '//trim(InitInp%FileName) ) + endif + ! NonDimArea is currently unused by AirfoilInfo or codes using AirfoilInfo. GJH 9/13/2017 CALL ParseVar ( FileInfo, CurLine, 'NonDimArea', p%NonDimArea, ErrStat2, ErrMsg2, UnEc ) diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index e785b1434..c1e615163 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -3084,6 +3084,12 @@ subroutine UA_CalcOutput( i, j, t, u_in, p, x, xd, OtherState, AFInfo, y, misc, elseif (p%UAMod == UA_BV) then ! --- CalcOutput Boeing-Vertol + if (AFInfo%RelThickness<0) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = 'Relative thickness should be provided in the profile file to use the Boeing-Vertol model.' + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + return + endif call BV_CalcOutput() if (ErrStat >= AbortErrLev) return From 8438bc47afe05414369e71552d87d498a7489176 Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Tue, 31 Aug 2021 11:42:57 -0600 Subject: [PATCH 24/24] UA: update of 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 8a3f3ab0b..3fb4c1989 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 8a3f3ab0b52d75c74aa6f4ed80f7a4ef5e8572e6 +Subproject commit 3fb4c19894d3ea3cef1187c2b0a77837baed159e