From fbe3a5a73f79341761ad35312f6f36cd01ccf1df Mon Sep 17 00:00:00 2001 From: Sebastiaan Mulders Date: Tue, 12 Jun 2018 15:00:26 +0200 Subject: [PATCH] Updated parameter files, choice between 1/2-order LPF, --- .../DTU10MW/ControllerParameters.in | 4 +- .../NREL5MW/ControllerParameters.in | 16 +++-- Source/Controllers.f90 | 56 ++++++++------- Source/DISCON.f90 | 2 +- Source/DRC_Types.f90 | 10 +-- Source/Functions.f90 | 69 +++++++++++-------- Source/ReadSetParameters.f90 | 32 ++++++--- 7 files changed, 116 insertions(+), 73 deletions(-) diff --git a/Parameter_files/DTU10MW/ControllerParameters.in b/Parameter_files/DTU10MW/ControllerParameters.in index 25adf0dd..c7957692 100644 --- a/Parameter_files/DTU10MW/ControllerParameters.in +++ b/Parameter_files/DTU10MW/ControllerParameters.in @@ -1,5 +1,7 @@ -2.5132741 ! CornerFreq - Corner frequency (-3dB point) in the low-pass filters, filtering generator speed and pitch control signals, [rad/s] 0 ! LoggingLevel - 0 = write no debug files, 1 = write standard output .dbg-file, 2 = write standard output .dbg-file and complete avrSWAP-array .dbg2-file +2 ! F_FilterType - 1 = first-order low-pass filter, 2 = second-order low-pass filter +2.5132741 ! F_CornerFreq - Corner frequency (-3dB point) in the low-pass filters, filtering generator speed and pitch control signals, [rad/s] +0.7 ! F_Damping - Damping coefficient if F_FilterType = 2, unused otherwise 0.087266 ! IPC_IntSat - Integrator saturation (maximum signal amplitude contrbution to pitch from IPC), [rad] 8E-10 ! IPC_KI - Integral gain for the individual pitch controller, [-]. 0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) on = 1/off = 0 diff --git a/Parameter_files/NREL5MW/ControllerParameters.in b/Parameter_files/NREL5MW/ControllerParameters.in index b579ab2e..31333287 100644 --- a/Parameter_files/NREL5MW/ControllerParameters.in +++ b/Parameter_files/NREL5MW/ControllerParameters.in @@ -1,15 +1,17 @@ -2.5132741 ! CornerFreq - Corner frequency (-3dB point) in the low-pass filters, filtering generator speed and pitch control signals, [rad/s] 0 ! LoggingLevel - 0 = write no debug files, 1 = write standard output .dbg-file, 2 = write standard output .dbg-file and complete avrSWAP-array .dbg2-file +1 ! F_FilterType - 1 = first-order low-pass filter, 2 = second-order low-pass filter +1.570796326 ! F_CornerFreq - Corner frequency (-3dB point) in the low-pass filters, filtering generator speed and pitch control signals, [rad/s] +0 ! F_Damping - Damping coefficient if F_FilterType = 2, unused otherwise 0.087266 ! IPC_IntSat - Integrator saturation (maximum signal amplitude contrbution to pitch from IPC), [rad] -8E-10 ! IPC_KI - Integral gain for the individual pitch controller, [-]. +1E-8 ! IPC_KI - Integral gain for the individual pitch controller, [-]. 0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) on = 1/off = 0 0.6283185 ! IPC_omegaLP - Low-pass filter corner frequency for the individual pitch controller, [rad/s]. 0.436332313 ! IPC_aziOffset - Phase offset added to the azimuth angle for the individual pitch controller, [rad]. 1.0 ! IPC_zetaLP - Low-pass filter damping factor for the individual pitch controller, [-]. 14 ! PC_GS_n - Amount of gain-scheduling table entries -0.0706 0.1168 0.1524 0.183 0.2116 0.2385 0.263 0.286 0.308 0.3284 0.3488 0.3691 0.3895 0.4083 ! PC_GS_angles - Gain-schedule table: pitch angles --0.0164 -0.0107 -0.0083 -0.0068 -0.0057 -0.0049 -0.0043 -0.0037 -0.0033 -0.0028 -0.0026 -0.0023 -0.002 -0.0017 ! PC_GS_KP - Gain-schedule table: pitch controller kp gains --0.0027 -0.0027 -0.0026 -0.0025 -0.0024 -0.0024 -0.0024 -0.0023 -0.0023 -0.0023 -0.0023 -0.0023 -0.0023 -0.0023 ! PC_GS_KI - Gain-schedule table: pitch controller ki gains +0.00000 0.03491 0.06981 0.10472 0.13963 0.17453 0.20944 0.24435 0.27925 0.31416 0.34907 0.38397 0.41888 0.45379 ! PC_GS_angles - Gain-schedule table: pitch angles +-0.018827 -0.014292 -0.011517 -0.009645 -0.008296 -0.007278 -0.006483 -0.005844 -0.005320 -0.004882 -0.004511 -0.004192 -0.003916 -0.003673 ! PC_GS_KP - Gain-schedule table: pitch controller kp gains +-0.008069 -0.006125 -0.004936 -0.004133 -0.003555 -0.003119 -0.002778 -0.002505 -0.002280 -0.002092 -0.001933 -0.001797 -0.001678 -0.001574 ! PC_GS_KI - Gain-schedule table: pitch controller ki gains 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ! PC_GS_KD - Gain-schedule table: pitch controller kd gains 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ! PC_GS_TF - Gain-schedule table: pitch controller tf gains (derivative filter) 1.5707 ! PC_MaxPit - Maximum physical pitch limit, [rad]. @@ -18,7 +20,7 @@ -0.13962 ! PC_MinRat - Minimum pitch rate (in absolute value) in pitch controller, [rad/s]. 122.90957 ! PC_RefSpd - Desired (reference) HSS speed for pitch controller, [rad/s]. 0.0 ! PC_FinePit - Record 5: Below-rated pitch angle set-point, [rad] -0.034906 ! PC_Switch - Angle above lowest minimum pitch angle for switch [rad] +0.003490658 ! PC_Switch - Angle above lowest minimum pitch angle for switch [rad] 1 ! VS_ControlMode - Generator torque control mode in above rated conditions, 0 = constant torque / 1 = constant power 0.944 ! VS_GenEff - Generator efficiency mechanical power -> electrical power, this should match the efficiency defined in the generator properties! [-] 43093.55 ! VS_ArSatTq - Above rated generator torque PI control saturation, [Nm] @@ -29,7 +31,7 @@ 2.33228 ! VS_Rgn2K - Generator torque constant in Region 2 (HSS side), N-m/(rad/s)^2 5.0E+06 ! VS_RtPwr - Wind turbine rated power [W] 43093.55 ! VS_RtTq - Rated torque, [Nm]. -121.680 ! VS_RefSpd - Rated generator speed [rad/s] +120.113 ! VS_RefSpd - Rated generator speed [rad/s] 1 ! VS_n - Number of controller gains -4200 ! VS_KP - Proportional gain for generator PI torque controller, used in the transitional 2.5 region, [1/(rad/s) Nm] -2100 ! VS_KI - Integral gain for generator PI torque controller, used in the transitional 2.5 region, [1/rad Nm] diff --git a/Source/Controllers.f90 b/Source/Controllers.f90 index f84f7f8f..cec998cf 100644 --- a/Source/Controllers.f90 +++ b/Source/Controllers.f90 @@ -46,7 +46,11 @@ SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst) ! Compute the pitch commands associated with the proportional and integral ! gains: - LocalVar%PC_PitComT = PIController(LocalVar%PC_SpdErr, LocalVar%PC_KP, LocalVar%PC_KI, CntrPar%PC_FinePit, LocalVar%PC_MaxPitVar, LocalVar%DT, CntrPar%PC_FinePit, .FALSE., objInst%instPI) + IF (LocalVar%iStatus == 0) THEN + LocalVar%PC_PitComT = PIController(LocalVar%PC_SpdErr, LocalVar%PC_KP, LocalVar%PC_KI, CntrPar%PC_FinePit, LocalVar%PC_MaxPitVar, LocalVar%DT, LocalVar%PitCom(1), .TRUE., objInst%instPI) + ELSE + LocalVar%PC_PitComT = PIController(LocalVar%PC_SpdErr, LocalVar%PC_KP, LocalVar%PC_KI, CntrPar%PC_FinePit, LocalVar%PC_MaxPitVar, LocalVar%DT, CntrPar%PC_FinePit, .FALSE., objInst%instPI) + END IF ! Individual pitch control IF ((CntrPar%IPC_ControlMode == 1) .OR. (CntrPar%Y_ControlMode == 2)) THEN @@ -58,11 +62,11 @@ SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst) ! Combine and saturate all pitch commands: DO K = 1,LocalVar%NumBl ! Loop through all blades, add IPC contribution and limit pitch rate LocalVar%PC_PitComT_IPC(K) = LocalVar%PC_PitComT + LocalVar%IPC_PitComF(K) ! Add the individual pitch command - LocalVar%PC_PitComT_IPC(K) = saturate(LocalVar%PC_PitComT_IPC(K), CntrPar%PC_MinPit, CntrPar%PC_MaxPit) ! Saturate the overall command using the pitch angle limits + LocalVar%PC_PitComT_IPC(K) = saturate(LocalVar%PC_PitComT_IPC(K), CntrPar%PC_MinPit, CntrPar%PC_MaxPit) ! Saturate the overall command using the pitch angle limits ! PitCom(K) = ratelimit(LocalVar%PC_PitComT_IPC(K), LocalVar%BlPitch(K), PC_MinRat, PC_MaxRat, LocalVar%DT) ! Saturate the overall command of blade K using the pitch rate limit - LocalVar%PitCom(K) = saturate(LocalVar%PC_PitComT_IPC(K), CntrPar%PC_MinPit, CntrPar%PC_MaxPit) ! Saturate the overall command using the pitch angle limits - LocalVar%PitCom(K) = LPFilter(LocalVar%PitCom(K), LocalVar%DT, CntrPar%CornerFreq, LocalVar%iStatus, .FALSE., objInst%instLPF) + LocalVar%PitCom(K) = saturate(LocalVar%PC_PitComT, CntrPar%PC_MinPit, CntrPar%PC_MaxPit) ! Saturate the overall command using the pitch angle limits + LocalVar%PitCom(K) = LocalVar%PitCom(K) + LocalVar%IPC_PitComF(K) END DO ! Command the pitch demanded from the last @@ -90,43 +94,47 @@ SUBROUTINE VariableSpeedControl(avrSWAP, CntrPar, LocalVar, objInst) avrSWAP(56) = 0.0 ! Torque override: 0=yes ! Filter the HSS (generator) speed measurement: - ! Apply Low-Pass Filter - LocalVar%GenSpeedF = SecLPFilter(LocalVar%GenSpeed, LocalVar%DT, CntrPar%CornerFreq, 0.7, LocalVar%iStatus, .FALSE., objInst%instSecLPF) ! Second-order low-pass filter on generator speed - + ! Apply Low-Pass Filter (choice between first- and second-order low-pass filter) + IF (CntrPar%F_FilterType == 1) THEN + LocalVar%GenSpeedF = LPFilter(LocalVar%GenSpeed, LocalVar%DT, CntrPar%F_CornerFreq, LocalVar%iStatus, .FALSE., objInst%instLPF) + ELSEIF (CntrPar%F_FilterType == 2) THEN + LocalVar%GenSpeedF = SecLPFilter(LocalVar%GenSpeed, LocalVar%DT, CntrPar%F_CornerFreq, CntrPar%F_Damping, LocalVar%iStatus, .FALSE., objInst%instSecLPF) ! Second-order low-pass filter on generator speed + END IF + ! Compute the generator torque, which depends on which region we are in: LocalVar%VS_SpdErrAr = CntrPar%VS_RefSpd - LocalVar%GenSpeedF ! Current speed error - Above-rated PI-control LocalVar%VS_SpdErrBr = CntrPar%VS_MinOMSpd - LocalVar%GenSpeedF ! Current speed error - Below-rated PI-control IF (LocalVar%VS_State >= 4) THEN - LocalVar%GenTrqAr = PIController(LocalVar%VS_SpdErrAr, CntrPar%VS_KP(1), CntrPar%VS_KI(1), CntrPar%VS_MaxOMTq, CntrPar%VS_ArSatTq, LocalVar%DT, CntrPar%VS_ArSatTq, .TRUE., objInst%instPI) - LocalVar%GenTrqBr = PIController(LocalVar%VS_SpdErrBr, CntrPar%VS_KP(1), CntrPar%VS_KI(1), CntrPar%VS_MinTq, CntrPar%VS_MinOMTq, LocalVar%DT, CntrPar%VS_MinOMTq, .TRUE., objInst%instPI) + LocalVar%GenArTq = PIController(LocalVar%VS_SpdErrAr, CntrPar%VS_KP(1), CntrPar%VS_KI(1), CntrPar%VS_MaxOMTq, CntrPar%VS_ArSatTq, LocalVar%DT, CntrPar%VS_ArSatTq, .TRUE., objInst%instPI) + LocalVar%GenBrTq = PIController(LocalVar%VS_SpdErrBr, CntrPar%VS_KP(1), CntrPar%VS_KI(1), CntrPar%VS_MinTq, CntrPar%VS_MinOMTq, LocalVar%DT, CntrPar%VS_MinOMTq, .TRUE., objInst%instPI) IF (LocalVar%VS_State == 4) THEN - LocalVar%GenTrq = CntrPar%VS_RtTq + LocalVar%GenTq = CntrPar%VS_RtTq ELSEIF (LocalVar%VS_State == 5) THEN - LocalVar%GenTrq = (CntrPar%VS_RtPwr/CntrPar%VS_GenEff)/LocalVar%GenSpeedF + LocalVar%GenTq = (CntrPar%VS_RtPwr/CntrPar%VS_GenEff)/LocalVar%GenSpeedF END IF ELSE - LocalVar%GenTrqAr = PIController(LocalVar%VS_SpdErrAr, CntrPar%VS_KP(1), CntrPar%VS_KI(1), CntrPar%VS_MaxOMTq, CntrPar%VS_ArSatTq, LocalVar%DT, CntrPar%VS_MaxOMTq, .FALSE., objInst%instPI) - LocalVar%GenTrqBr = PIController(LocalVar%VS_SpdErrBr, CntrPar%VS_KP(1), CntrPar%VS_KI(1), CntrPar%VS_MinTq, CntrPar%VS_MinOMTq, LocalVar%DT, CntrPar%VS_MinOMTq, .FALSE., objInst%instPI) + LocalVar%GenArTq = PIController(LocalVar%VS_SpdErrAr, CntrPar%VS_KP(1), CntrPar%VS_KI(1), CntrPar%VS_MaxOMTq, CntrPar%VS_ArSatTq, LocalVar%DT, CntrPar%VS_MaxOMTq, .FALSE., objInst%instPI) + LocalVar%GenBrTq = PIController(LocalVar%VS_SpdErrBr, CntrPar%VS_KP(1), CntrPar%VS_KI(1), CntrPar%VS_MinTq, CntrPar%VS_MinOMTq, LocalVar%DT, CntrPar%VS_MinOMTq, .FALSE., objInst%instPI) IF (LocalVar%VS_State == 3) THEN - LocalVar%GenTrq = LocalVar%GenTrqAr + LocalVar%GenTq = LocalVar%GenArTq ELSEIF (LocalVar%VS_State == 1) THEN - LocalVar%GenTrq = LocalVar%GenTrqBr + LocalVar%GenTq = LocalVar%GenBrTq ELSEIF (LocalVar%VS_State == 2) THEN - LocalVar%GenTrq = CntrPar%VS_Rgn2K*LocalVar%GenSpeedF*LocalVar%GenSpeedF + LocalVar%GenTq = CntrPar%VS_Rgn2K*LocalVar%GenSpeedF*LocalVar%GenSpeedF ELSE - LocalVar%GenTrq = CntrPar%VS_MaxOMTq + LocalVar%GenTq = CntrPar%VS_MaxOMTq END IF END IF ! Saturate the commanded torque using the maximum torque limit: - LocalVar%GenTrq = MIN(LocalVar%GenTrq, CntrPar%VS_MaxTq) ! Saturate the command using the maximum torque limit + LocalVar%GenTq = MIN(LocalVar%GenTq, CntrPar%VS_MaxTq) ! Saturate the command using the maximum torque limit ! Saturate the commanded torque using the torque rate limit: - IF (LocalVar%iStatus == 0) LocalVar%VS_LastGenTrq = LocalVar%GenTrq ! Initialize the value of LocalVar%VS_LastGenTrq on the first pass only - LocalVar%GenTrq = ratelimit(LocalVar%GenTrq, LocalVar%VS_LastGenTrq, -CntrPar%VS_MaxRat, CntrPar%VS_MaxRat, LocalVar%DT) ! Saturate the command using the torque rate limit + IF (LocalVar%iStatus == 0) LocalVar%VS_LastGenTrq = LocalVar%GenTq ! Initialize the value of LocalVar%VS_LastGenTrq on the first pass only + LocalVar%GenTq = ratelimit(LocalVar%GenTq, LocalVar%VS_LastGenTrq, -CntrPar%VS_MaxRat, CntrPar%VS_MaxRat, LocalVar%DT) ! Saturate the command using the torque rate limit ! Reset the value of LocalVar%VS_LastGenTrq to the current values: - LocalVar%VS_LastGenTrq = LocalVar%GenTrq + LocalVar%VS_LastGenTrq = LocalVar%GenTq ! Set the generator contactor status, avrSWAP(35), to main (high speed) ! variable-speed generator, the torque override to yes, and command the @@ -185,7 +193,6 @@ SUBROUTINE IPC(CntrPar, LocalVar, objInst) REAL(4), SAVE :: IntAxisTilt, IntAxisYaw ! Integral of the direct axis and quadrature axis REAL(4) :: IntAxisYawIPC ! IPC contribution with yaw-by-IPC component REAL(4) :: Y_MErrF, Y_MErrF_IPC ! Unfiltered and filtered yaw alignment error [rad] - REAL(4) :: PitComIPC_woYaw(3) TYPE(ControlParameters), INTENT(INOUT) :: CntrPar TYPE(LocalVariables), INTENT(INOUT) :: LocalVar @@ -200,12 +207,12 @@ SUBROUTINE IPC(CntrPar, LocalVar, objInst) !------------------------------------------------------------------------------------------------------------------------------ ! Initialization ! Set integrals to be 0 in the first time step - IF(LocalVar%iStatus==0) THEN + IF (LocalVar%iStatus==0) THEN IntAxisTilt = 0.0 IntAxisYaw = 0.0 END IF - ! Pass rootMOOPs through the Coleman transform to get the direct and quadrature axis + ! Pass rootMOOPs through the Coleman transform to get the tilt and yaw moment axis CALL ColemanTransform(LocalVar%rootMOOP, LocalVar%Azimuth, axisTilt, axisYaw) ! High-pass filter the MBC yaw component and filter yaw alignment error, and compute the yaw-by-IPC contribution @@ -215,6 +222,7 @@ SUBROUTINE IPC(CntrPar, LocalVar, objInst) ELSE axisYawF = axisYaw Y_MErrF = 0.0 + Y_MErrF_IPC = 0.0 END IF ! Integrate the signal and multiply with the IPC gain diff --git a/Source/DISCON.f90 b/Source/DISCON.f90 index e53980d7..2108417e 100644 --- a/Source/DISCON.f90 +++ b/Source/DISCON.f90 @@ -46,7 +46,7 @@ SUBROUTINE DISCON(avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG) BIND (C, NAME CALL StateMachine(CntrPar, LocalVar) CALL VariableSpeedControl(avrSWAP, CntrPar, LocalVar, objInst) CALL PitchControl(avrSWAP, CntrPar, LocalVar, objInst) -! CALL YawRateControl(avrSWAP, CntrPar, LocalVar, objInst) + CALL YawRateControl(avrSWAP, CntrPar, LocalVar, objInst) CALL Debug(LocalVar, CntrPar, avrSWAP, RootName, SIZE(avcOUTNAME)) END IF diff --git a/Source/DRC_Types.f90 b/Source/DRC_Types.f90 index 04a95e9d..d9f988c6 100644 --- a/Source/DRC_Types.f90 +++ b/Source/DRC_Types.f90 @@ -3,8 +3,10 @@ MODULE DRC_Types IMPLICIT NONE TYPE, PUBLIC :: ControlParameters - REAL(4) :: CornerFreq ! Corner frequency (-3dB point) in the first-order low-pass filter, [rad/s] INTEGER(4) :: LoggingLevel ! 0 = write no debug files, 1 = write standard output .dbg-file, 2 = write standard output .dbg-file and complete avrSWAP-array .dbg2-file + INTEGER(4) :: F_FilterType ! 1 = first-order low-pass filter, 2 = second-order low-pass filter + REAL(4) :: F_CornerFreq ! Corner frequency (-3dB point) in the first-order low-pass filter, [rad/s] + REAL(4) :: F_Damping ! Damping coefficient if F_FilterType = 2, unused otherwise REAL(4) :: IPC_IntSat ! Integrator saturation (maximum signal amplitude contrbution to pitch from IPC) REAL(4) :: IPC_KI ! Integral gain for the individual pitch controller, [-]. 8E-10 INTEGER(4) :: IPC_ControlMode ! Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) on = 1/off = 0 @@ -71,9 +73,9 @@ MODULE DRC_Types ! Internal controller variables REAL(4) :: GenSpeedF ! Filtered HSS (generator) speed [rad/s]. - REAL(4) :: GenTrq ! Electrical generator torque, [Nm]. - REAL(4) :: GenTrqAr ! Electrical generator torque, for above-rated PI-control [Nm]. - REAL(4) :: GenTrqBr ! Electrical generator torque, for below-rated PI-control [Nm]. + REAL(4) :: GenTq ! Electrical generator torque, [Nm]. + REAL(4) :: GenArTq ! Electrical generator torque, for above-rated PI-control [Nm]. + REAL(4) :: GenBrTq ! Electrical generator torque, for below-rated PI-control [Nm]. INTEGER(4) :: GlobalState ! Current global state to determine the behavior of the different controllers [-]. REAL(4) :: IPC_PitComF(3) ! Commanded pitch of each blade as calculated by the individual pitch controller, F stands for low pass filtered, [rad]. REAL(4) :: PC_KP ! Proportional gain for pitch controller at rated pitch (zero), [s]. diff --git a/Source/Functions.f90 b/Source/Functions.f90 index a644c63b..3af78a07 100644 --- a/Source/Functions.f90 +++ b/Source/Functions.f90 @@ -199,30 +199,45 @@ SUBROUTINE StateMachine(CntrPar, LocalVar) ! Local ! Pitch control state machine - IF (CntrPar%VS_ControlMode == 0 .AND. LocalVar%GenTrq >= CntrPar%PC_RtTq99) THEN - LocalVar%PC_State = 1 - ELSEIF (CntrPar%VS_ControlMode == 1 .AND. LocalVar%GenTrqAr >= CntrPar%VS_ArSatTq*0.99) THEN - LocalVar%PC_State = 2 - ELSE - LocalVar%PC_State = 0 - END IF - - ! Torque control state machine - IF (LocalVar%PC_PitComT >= CntrPar%VS_Rgn3Pitch) THEN ! We are in region 3 - IF (CntrPar%VS_ControlMode == 1) THEN ! Constant power tracking - LocalVar%VS_State = 5 - ELSE ! Constant torque tracking - LocalVar%VS_State = 4 + IF (LocalVar%iStatus == 0) THEN + IF (LocalVar%PitCom(1) >= CntrPar%VS_Rgn3Pitch) THEN ! We are in region 3 + IF (CntrPar%VS_ControlMode == 1) THEN ! Constant power tracking + LocalVar%VS_State = 5 + LocalVar%PC_State = 2 + ELSE ! Constant torque tracking + LocalVar%VS_State = 4 + LocalVar%PC_State = 1 + END IF + ELSE + LocalVar%VS_State = 2 + LocalVar%PC_State = 0 END IF ELSE - IF (LocalVar%GenTrqAr >= CntrPar%VS_MaxOMTq*1.01) THEN ! We are in region 2 1/2 - active PI torque control - LocalVar%VS_State = 3 - ELSEIF (LocalVar%GenTrqBr <= CntrPar%VS_MinOMTq*0.99) THEN ! We are in region 1 1/2 - LocalVar%VS_State = 1 - ELSEIF (LocalVar%GenSpeedF < CntrPar%VS_RefSpd) THEN ! We are in region 2 - optimal torque is proportional to the square of the generator speed - LocalVar%VS_State = 2 - ELSE ! Error state, for debugging purposes - LocalVar%VS_State = 0 + IF ((CntrPar%VS_ControlMode == 0) .AND. (LocalVar%GenTq >= CntrPar%PC_RtTq99)) THEN + LocalVar%PC_State = 1 + ELSEIF ((CntrPar%VS_ControlMode == 1) .AND. (LocalVar%GenArTq >= CntrPar%VS_ArSatTq*0.99)) THEN + LocalVar%PC_State = 2 + ELSE + LocalVar%PC_State = 0 + END IF + + ! Torque control state machine + IF (LocalVar%PC_PitComT >= CntrPar%VS_Rgn3Pitch) THEN ! We are in region 3 ! + IF (CntrPar%VS_ControlMode == 1) THEN ! Constant power tracking + LocalVar%VS_State = 5 + ELSE ! Constant torque tracking + LocalVar%VS_State = 4 + END IF + ELSE + IF (LocalVar%GenArTq >= CntrPar%VS_MaxOMTq*1.01) THEN ! We are in region 2 1/2 - active PI torque control + LocalVar%VS_State = 3 + ELSEIF (LocalVar%GenBrTq <= CntrPar%VS_MinOMTq*0.99) THEN ! We are in region 1 1/2 + LocalVar%VS_State = 1 + ELSEIF (LocalVar%GenSpeedF < CntrPar%VS_RefSpd) THEN ! We are in region 2 - optimal torque is proportional to the square of the generator speed + LocalVar%VS_State = 2 + ELSE ! Error state, for debugging purposes + LocalVar%VS_State = 0 + END IF END IF END IF END SUBROUTINE StateMachine @@ -251,8 +266,8 @@ SUBROUTINE Debug(LocalVar, CntrPar, avrSWAP, RootName, size_avcOUTNAME) IF (LocalVar%iStatus == 0) THEN ! .TRUE. if we're on the first call to the DLL ! If we're debugging, open the debug file and write the header: IF (CntrPar%LoggingLevel > 0) THEN - OPEN (UnDb, FILE=TRIM(RootName)//'.dbg', STATUS='REPLACE') - WRITE (UnDb,'(A)') ' LocalVar%Time ' //Tab//'LocalVar%PC_PitComT ' //Tab//'LocalVar%PC_SpdErr ' //Tab//'LocalVar%PC_KP ' //Tab//'LocalVar%PC_KI ' //Tab//'LocalVar%Y_M ' //Tab//'LocalVar%rootMOOP(1) '//Tab//'VS_RtPwr '//Tab//'LocalVar%GenTrq' + OPEN(UnDb, FILE=TRIM(RootName)//'.dbg', STATUS='REPLACE') + WRITE (UnDb,'(A)') ' LocalVar%Time ' //Tab//'LocalVar%PC_PitComT ' //Tab//'LocalVar%PC_SpdErr ' //Tab//'LocalVar%PC_KP ' //Tab//'LocalVar%PC_KI ' //Tab//'LocalVar%Y_M ' //Tab//'LocalVar%rootMOOP(1) '//Tab//'VS_RtPwr '//Tab//'LocalVar%GenTq' WRITE (UnDb,'(A)') ' (sec) ' //Tab//'(rad) ' //Tab//'(rad/s) '//Tab//'(-) ' //Tab//'(-) ' //Tab//'(rad) ' //Tab//'(?) ' //Tab//'(W) '//Tab//'(Nm) ' END IF @@ -267,12 +282,12 @@ SUBROUTINE Debug(LocalVar, CntrPar, avrSWAP, RootName, size_avcOUTNAME) IF (MODULO(LocalVar%Time, 10.0) == 0) THEN WRITE(*, 100) LocalVar%GenSpeedF*RPS2RPM, LocalVar%BlPitch(1)*R2D, avrSWAP(15)/1000.0 ! LocalVar%Time !/1000.0 100 FORMAT('Generator speed: ', f6.1, ' RPM, Pitch angle: ', f5.1, ' deg, Power: ', f7.1, ' kW') - ! PRINT *, CntrPar%PC_RefSpd, LocalVar%PC_SpdErr, LocalVar%PC_PwrErr + ! PRINT *, LocalVar%PC_State, LocalVar%VS_State, CntrPar%VS_Rgn3Pitch, CntrPar%PC_FinePit, CntrPar%PC_Switch, LocalVar%BlPitch(1) ! Additional debug info END IF ! Output debugging information if requested: IF (CntrPar%LoggingLevel > 0) THEN - WRITE (UnDb,FmtDat) LocalVar%Time, LocalVar%PC_PitComT, LocalVar%PC_SpdErr, LocalVar%PC_KP, LocalVar%PC_KI, LocalVar%Y_MErr, LocalVar%rootMOOP(1), CntrPar%VS_RtPwr, LocalVar%GenTrq + WRITE (UnDb,FmtDat) LocalVar%Time, LocalVar%Y_MErr, LocalVar%Y_AccErr, CntrPar%Y_ErrThresh, LocalVar%Y_ErrLPFFast, LocalVar%Y_ErrLPFSlow, avrSWAP(48) END IF IF (CntrPar%LoggingLevel > 1) THEN @@ -329,7 +344,7 @@ SUBROUTINE ColemanTransformInverse(axTIn, axYIn, aziAngle, aziOffset, PitComIPC) ! Outputs - REAL(4), INTENT(OUT) :: PitComIPC (3) ! Root out of plane bending moments of each blade + REAL(4), INTENT(OUT) :: PitComIPC(3) ! Root out of plane bending moments of each blade ! Local diff --git a/Source/ReadSetParameters.f90 b/Source/ReadSetParameters.f90 index 53bbef74..3c061042 100644 --- a/Source/ReadSetParameters.f90 +++ b/Source/ReadSetParameters.f90 @@ -17,8 +17,10 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, LocalVar) OPEN(unit=UnControllerParameters, file='ControllerParameters.in', status='old', action='read') !------------------- GENERAL CONSTANTS ------------------- - READ(UnControllerParameters, *) CntrPar%CornerFreq READ(UnControllerParameters, *) CntrPar%LoggingLevel + READ(UnControllerParameters, *) CntrPar%F_FilterType + READ(UnControllerParameters, *) CntrPar%F_CornerFreq + READ(UnControllerParameters, *) CntrPar%F_Damping !------------------- IPC CONSTANTS ----------------------- READ(UnControllerParameters, *) CntrPar%IPC_IntSat @@ -146,7 +148,17 @@ SUBROUTINE Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ! Check validity of input parameters: !.............................................................................................................................. - IF (CntrPar%CornerFreq <= 0.0) THEN + IF ((CntrPar%F_FilterType > 2.0) .OR. (CntrPar%F_FilterType < 1.0)) THEN + aviFAIL = -1 + ErrMsg = 'FilterType must be 1 or 2.' + ENDIF + + IF (ABS(CntrPar%F_Damping) > 1.0) THEN + aviFAIL = -1 + ErrMsg = 'Filter damping coefficient must be between [0, 1]' + ENDIF + + IF (CntrPar%F_CornerFreq <= 0.0) THEN aviFAIL = -1 ErrMsg = 'CornerFreq must be greater than zero.' ENDIF @@ -206,7 +218,7 @@ SUBROUTINE Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ErrMsg = 'PC_MinPit must be less than PC_MaxPit.' ENDIF - IF (CntrPar%IPC_KI <= 0.0) THEN + IF (CntrPar%IPC_KI < 0.0) THEN aviFAIL = -1 ErrMsg = 'IPC_KI must be greater than zero.' ENDIF @@ -216,11 +228,6 @@ SUBROUTINE Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ErrMsg = 'IPC_omegaLP must be greater than zero.' ENDIF - IF (CntrPar%IPC_aziOffset <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'IPC_aziOffset must be greater than zero.' - ENDIF - IF (CntrPar%IPC_zetaLP <= 0.0) THEN aviFAIL = -1 ErrMsg = 'IPC_zetaLP must be greater than zero.' @@ -253,6 +260,10 @@ SUBROUTINE Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ErrMsg = 'Pitch angle actuator not requested.' ENDIF + IF (NINT(avrSWAP(28)) == 0 .AND. ((CntrPar%IPC_ControlMode > 0) .OR. (CntrPar%Y_ControlMode > 1))) THEN + aviFAIL = -1 + ErrMsg = 'IPC enabled, but Ptch_Cntrl in ServoDyn has a value of 0. Set to 1.' + ENDIF END SUBROUTINE Assert SUBROUTINE SetParameters(avrSWAP, aviFAIL, ErrMsg, size_avcMSG, CntrPar, LocalVar, objInst) @@ -266,7 +277,8 @@ SUBROUTINE SetParameters(avrSWAP, aviFAIL, ErrMsg, size_avcMSG, CntrPar, LocalVa REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. INTEGER(C_INT), INTENT(OUT) :: aviFAIL ! A flag used to indicate the success of this DLL call set as follows: 0 if the DLL call was successful, >0 if the DLL call was successful but cMessage should be issued as a warning messsage, <0 if the DLL call was unsuccessful or for any other reason the simulation is to be stopped at this point with cMessage as the error message. CHARACTER(size_avcMSG-1), INTENT(OUT) :: ErrMsg ! a Fortran version of the C string argument (not considered an array here) [subtract 1 for the C null-character] - + INTEGER(4) :: K ! Index used for looping through blades. + ! Set aviFAIL to 0 in each iteration: aviFAIL = 0 @@ -310,7 +322,9 @@ SUBROUTINE SetParameters(avrSWAP, aviFAIL, ErrMsg, size_avcMSG, CntrPar, LocalVa ! Initialize the SAVEd variables: ! NOTE: LocalVar%VS_LastGenTrq, though SAVEd, is initialized in the torque controller ! below for simplicity, not here. + ! DO K = 1,LocalVar%NumBl LocalVar%PitCom = LocalVar%BlPitch ! This will ensure that the variable speed controller picks the correct control region and the pitch controller picks the correct gain on the first call + ! END DO LocalVar%Y_AccErr = 0.0 ! This will ensure that the accumulated yaw error starts at zero LocalVar%Y_YawEndT = -1.0 ! This will ensure that the initial yaw end time is lower than the actual time to prevent initial yawing