From 90e50467efa0dfcae8cbaaa362300fd0677b6436 Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Thu, 4 Jul 2019 07:46:49 -0600 Subject: [PATCH 1/7] Tabs to Spaces --- Source/DRC_Types.f90 | 232 ++++++------ Source/Filters.f90 | 334 +++++++++--------- Source/Functions.f90 | 658 +++++++++++++++++------------------ Source/ReadSetParameters.f90 | 4 +- 4 files changed, 614 insertions(+), 614 deletions(-) diff --git a/Source/DRC_Types.f90 b/Source/DRC_Types.f90 index d0c6739d..632989c6 100644 --- a/Source/DRC_Types.f90 +++ b/Source/DRC_Types.f90 @@ -3,146 +3,146 @@ MODULE DRC_Types IMPLICIT NONE TYPE, PUBLIC :: ControlParameters - 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) :: 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_LPFType ! 1 = first-order low-pass filter, 2 = second-order low-pass filter INTEGER(4) :: F_NotchType ! 0 = disable, 1 = enable: notch on the measured generator speed, REAL(4) :: F_LPFCornerFreq ! Corner frequency (-3dB point) in the first-order low-pass filter, [rad/s] REAL(4) :: F_LPFDamping ! Damping coefficient if F_LPFType = 2, unused otherwise - REAL(4) :: F_NotchCornerFreq ! Natural frequency of the notch filter, [rad/s] + REAL(4) :: F_NotchCornerFreq ! Natural frequency of the notch filter, [rad/s] REAL(4), DIMENSION(:), ALLOCATABLE :: F_NotchBetaNumDen ! These two notch damping values (numerator and denominator) determines the width and depth of the notch REAL(4) :: FA_HPFCornerFreq ! Corner frequency (-3dB point) in the high-pass filter on the fore-aft acceleration signal [rad/s] REAL(4) :: FA_IntSat ! Integrator saturation (maximum signal amplitude contrbution to pitch from FA damper), [rad] REAL(4) :: FA_KI ! Integral gain for the fore-aft tower damper controller, -1 = off / >0 = on [rad s/m] - + INTEGER(4) :: IPC_ControlMode ! Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) 0 = off / 1 = (1P reductions) / 2 = (1P+2P reductions) REAL(4) :: IPC_IntSat ! Integrator saturation (maximum signal amplitude contrbution to pitch from IPC) - REAL(4), DIMENSION(:), ALLOCATABLE :: IPC_KI ! Integral gain for the individual pitch controller, [-]. 8E-10 - REAL(4), DIMENSION(:), ALLOCATABLE :: IPC_aziOffset ! Phase offset added to the azimuth angle for the individual pitch controller, [rad]. - REAL(4) :: IPC_CornerFreqAct ! Corner frequency of the first-order actuators model, to induce a phase lag in the IPC signal. Set 0 to disable. [rad/s] - + REAL(4), DIMENSION(:), ALLOCATABLE :: IPC_KI ! Integral gain for the individual pitch controller, [-]. 8E-10 + REAL(4), DIMENSION(:), ALLOCATABLE :: IPC_aziOffset ! Phase offset added to the azimuth angle for the individual pitch controller, [rad]. + REAL(4) :: IPC_CornerFreqAct ! Corner frequency of the first-order actuators model, to induce a phase lag in the IPC signal. Set 0 to disable. [rad/s] + INTEGER(4) :: PC_GS_n ! Amount of gain-scheduling table entries - REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_angles ! Gain-schedule table: pitch angles - REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_KP ! Gain-schedule table: pitch controller kp gains - REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_KI ! Gain-schedule table: pitch controller ki gains - REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_KD ! Gain-schedule table: pitch controller kd gains - REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_TF ! Gain-schedule table: pitch controller tf gains (derivative filter) - REAL(4) :: PC_MaxPit ! Maximum physical pitch limit, [rad]. - REAL(4) :: PC_MinPit ! Minimum physical pitch limit, [rad]. - REAL(4) :: PC_MaxRat ! Maximum pitch rate (in absolute value) in pitch controller, [rad/s]. - REAL(4) :: PC_MinRat ! Minimum pitch rate (in absolute value) in pitch controller, [rad/s]. - REAL(4) :: PC_RefSpd ! Desired (reference) HSS speed for pitch controller, [rad/s]. - REAL(4) :: PC_FinePit ! Record 5: Below-rated pitch angle set-point (deg) [used only with Bladed Interface] - REAL(4) :: PC_Switch ! Angle above lowest minimum pitch angle for switch [rad] - + REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_angles ! Gain-schedule table: pitch angles + REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_KP ! Gain-schedule table: pitch controller kp gains + REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_KI ! Gain-schedule table: pitch controller ki gains + REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_KD ! Gain-schedule table: pitch controller kd gains + REAL(4), DIMENSION(:), ALLOCATABLE :: PC_GS_TF ! Gain-schedule table: pitch controller tf gains (derivative filter) + REAL(4) :: PC_MaxPit ! Maximum physical pitch limit, [rad]. + REAL(4) :: PC_MinPit ! Minimum physical pitch limit, [rad]. + REAL(4) :: PC_MaxRat ! Maximum pitch rate (in absolute value) in pitch controller, [rad/s]. + REAL(4) :: PC_MinRat ! Minimum pitch rate (in absolute value) in pitch controller, [rad/s]. + REAL(4) :: PC_RefSpd ! Desired (reference) HSS speed for pitch controller, [rad/s]. + REAL(4) :: PC_FinePit ! Record 5: Below-rated pitch angle set-point (deg) [used only with Bladed Interface] + REAL(4) :: PC_Switch ! Angle above lowest minimum pitch angle for switch [rad] + INTEGER(4) :: VS_ControlMode ! Generator torque control mode in above rated conditions, 0 = constant torque / 1 = constant power - REAL(4) :: VS_GenEff ! Generator efficiency mechanical power -> electrical power [-] - REAL(4) :: VS_ArSatTq ! Above rated generator torque PI control saturation, [Nm] -- 212900 - REAL(4) :: VS_MaxRat ! Maximum torque rate (in absolute value) in torque controller, [Nm/s]. - REAL(4) :: VS_MaxTq ! Maximum generator torque in Region 3 (HSS side), [Nm]. -- chosen to be 10% above VS_RtTq - REAL(4) :: VS_MinTq ! Minimum generator (HSS side), [Nm]. - REAL(4) :: VS_MinOMSpd ! Optimal mode minimum speed, [rad/s] - REAL(4) :: VS_Rgn2K ! Generator torque constant in Region 2 (HSS side), N-m/(rad/s)^2 - REAL(4) :: VS_RtPwr ! Wind turbine rated power [W] - REAL(4) :: VS_RtTq ! Rated torque, [Nm]. - REAL(4) :: VS_RefSpd ! Rated generator speed [rad/s] - INTEGER(4) :: VS_n ! Number of controller gains - REAL(4), DIMENSION(:), ALLOCATABLE :: VS_KP ! Proportional gain for generator PI torque controller, used in the transitional 2.5 region - REAL(4), DIMENSION(:), ALLOCATABLE :: VS_KI ! Integral gain for generator PI torque controller, used in the transitional 2.5 region - - REAL(4) :: WE_BladeRadius ! Blade length [m] - INTEGER(4) :: WE_CP_n ! Amount of parameters in the Cp array - REAL(4), DIMENSION(:), ALLOCATABLE :: WE_CP ! Parameters that define the parameterized CP(\lambda) function - REAL(4) :: WE_Gamma ! Adaption gain of the wind speed estimator algorithm [m/rad] - REAL(4) :: WE_GearboxRatio ! Gearbox ratio, >=1 [-] - REAL(4) :: WE_Jtot ! Total drivetrain inertia, including blades, hub and casted generator inertia to LSS [kg m^2] - REAL(4) :: WE_RhoAir ! Air density [kg m^-3] - - INTEGER(4) :: Y_ControlMode ! Yaw control mode: (0 = no yaw control, 1 = yaw rate control, 2 = yaw-by-IPC) - REAL(4) :: Y_ErrThresh ! Error threshold [rad]. Turbine begins to yaw when it passes this. (104.71975512) -- 1.745329252 - REAL(4) :: Y_IPC_IntSat ! Integrator saturation (maximum signal amplitude contrbution to pitch from yaw-by-IPC) - INTEGER(4) :: Y_IPC_n ! Number of controller gains (yaw-by-IPC) - REAL(4), DIMENSION(:), ALLOCATABLE :: Y_IPC_KP ! Yaw-by-IPC proportional controller gain Kp - REAL(4), DIMENSION(:), ALLOCATABLE :: Y_IPC_KI ! Yaw-by-IPC integral controller gain Ki + REAL(4) :: VS_GenEff ! Generator efficiency mechanical power -> electrical power [-] + REAL(4) :: VS_ArSatTq ! Above rated generator torque PI control saturation, [Nm] -- 212900 + REAL(4) :: VS_MaxRat ! Maximum torque rate (in absolute value) in torque controller, [Nm/s]. + REAL(4) :: VS_MaxTq ! Maximum generator torque in Region 3 (HSS side), [Nm]. -- chosen to be 10% above VS_RtTq + REAL(4) :: VS_MinTq ! Minimum generator (HSS side), [Nm]. + REAL(4) :: VS_MinOMSpd ! Optimal mode minimum speed, [rad/s] + REAL(4) :: VS_Rgn2K ! Generator torque constant in Region 2 (HSS side), N-m/(rad/s)^2 + REAL(4) :: VS_RtPwr ! Wind turbine rated power [W] + REAL(4) :: VS_RtTq ! Rated torque, [Nm]. + REAL(4) :: VS_RefSpd ! Rated generator speed [rad/s] + INTEGER(4) :: VS_n ! Number of controller gains + REAL(4), DIMENSION(:), ALLOCATABLE :: VS_KP ! Proportional gain for generator PI torque controller, used in the transitional 2.5 region + REAL(4), DIMENSION(:), ALLOCATABLE :: VS_KI ! Integral gain for generator PI torque controller, used in the transitional 2.5 region + + REAL(4) :: WE_BladeRadius ! Blade length [m] + INTEGER(4) :: WE_CP_n ! Amount of parameters in the Cp array + REAL(4), DIMENSION(:), ALLOCATABLE :: WE_CP ! Parameters that define the parameterized CP(\lambda) function + REAL(4) :: WE_Gamma ! Adaption gain of the wind speed estimator algorithm [m/rad] + REAL(4) :: WE_GearboxRatio ! Gearbox ratio, >=1 [-] + REAL(4) :: WE_Jtot ! Total drivetrain inertia, including blades, hub and casted generator inertia to LSS [kg m^2] + REAL(4) :: WE_RhoAir ! Air density [kg m^-3] + + INTEGER(4) :: Y_ControlMode ! Yaw control mode: (0 = no yaw control, 1 = yaw rate control, 2 = yaw-by-IPC) + REAL(4) :: Y_ErrThresh ! Error threshold [rad]. Turbine begins to yaw when it passes this. (104.71975512) -- 1.745329252 + REAL(4) :: Y_IPC_IntSat ! Integrator saturation (maximum signal amplitude contrbution to pitch from yaw-by-IPC) + INTEGER(4) :: Y_IPC_n ! Number of controller gains (yaw-by-IPC) + REAL(4), DIMENSION(:), ALLOCATABLE :: Y_IPC_KP ! Yaw-by-IPC proportional controller gain Kp + REAL(4), DIMENSION(:), ALLOCATABLE :: Y_IPC_KI ! Yaw-by-IPC integral controller gain Ki REAL(4) :: Y_IPC_omegaLP ! Low-pass filter corner frequency for the Yaw-by-IPC controller to filtering the yaw alignment error, [rad/s]. - REAL(4) :: Y_IPC_zetaLP ! Low-pass filter damping factor for the Yaw-by-IPC controller to filtering the yaw alignment error, [-]. - REAL(4) :: Y_MErrSet ! Yaw alignment error, setpoint [rad] - REAL(4) :: Y_omegaLPFast ! Corner frequency fast low pass filter, 1.0 [Hz] - REAL(4) :: Y_omegaLPSlow ! Corner frequency slow low pass filter, 1/60 [Hz] - REAL(4) :: Y_Rate ! Yaw rate [rad/s] - - INTEGER(4) :: Z_EnableSine ! Enable/disable sine pitch excitation - REAL(4) :: Z_PitchAmplitude ! Amplitude of sine pitch excitation - REAL(4) :: Z_PitchFrequency ! Frequency of sine pitch excitation - - REAL(4) :: PC_RtTq99 ! 99% of the rated torque value, using for switching between pitch and torque control, [Nm]. - REAL(4) :: VS_MaxOMTq ! Maximum torque at the end of the below-rated region 2, [Nm] - REAL(4) :: VS_MinOMTq ! Minimum torque at the beginning of the below-rated region 2, [Nm] - REAL(4) :: VS_Rgn3Pitch ! Pitch angle at which the state machine switches to region 3, [rad]. + REAL(4) :: Y_IPC_zetaLP ! Low-pass filter damping factor for the Yaw-by-IPC controller to filtering the yaw alignment error, [-]. + REAL(4) :: Y_MErrSet ! Yaw alignment error, setpoint [rad] + REAL(4) :: Y_omegaLPFast ! Corner frequency fast low pass filter, 1.0 [Hz] + REAL(4) :: Y_omegaLPSlow ! Corner frequency slow low pass filter, 1/60 [Hz] + REAL(4) :: Y_Rate ! Yaw rate [rad/s] + + INTEGER(4) :: Z_EnableSine ! Enable/disable sine pitch excitation + REAL(4) :: Z_PitchAmplitude ! Amplitude of sine pitch excitation + REAL(4) :: Z_PitchFrequency ! Frequency of sine pitch excitation + + REAL(4) :: PC_RtTq99 ! 99% of the rated torque value, using for switching between pitch and torque control, [Nm]. + REAL(4) :: VS_MaxOMTq ! Maximum torque at the end of the below-rated region 2, [Nm] + REAL(4) :: VS_MinOMTq ! Minimum torque at the beginning of the below-rated region 2, [Nm] + REAL(4) :: VS_Rgn3Pitch ! Pitch angle at which the state machine switches to region 3, [rad]. END TYPE ControlParameters TYPE, PUBLIC :: LocalVariables - ! From avrSWAP - INTEGER(4) :: iStatus - REAL(4) :: Time - REAL(4) :: DT - REAL(4) :: VS_GenPwr - REAL(4) :: GenSpeed - REAL(4) :: RotSpeed - REAL(4) :: Y_M - REAL(4) :: HorWindV - REAL(4) :: rootMOOP(3) - REAL(4) :: BlPitch(3) - REAL(4) :: Azimuth - INTEGER(4) :: NumBl - - ! Internal controller variables + ! From avrSWAP + INTEGER(4) :: iStatus + REAL(4) :: Time + REAL(4) :: DT + REAL(4) :: VS_GenPwr + REAL(4) :: GenSpeed + REAL(4) :: RotSpeed + REAL(4) :: Y_M + REAL(4) :: HorWindV + REAL(4) :: rootMOOP(3) + REAL(4) :: BlPitch(3) + REAL(4) :: Azimuth + INTEGER(4) :: NumBl + + ! Internal controller variables REAL(4) :: FA_Acc ! Tower fore-aft acceleration [m/s^2] REAL(4) :: FA_AccHPF ! High-pass filtered fore-aft acceleration [m/s^2] REAL(4) :: FA_AccHPFI ! Tower velocity, high-pass filtered and integrated fore-aft acceleration [m/s] REAL(4) :: FA_PitCom(3) ! Tower fore-aft vibration damping pitch contribution [rad] - REAL(4) :: GenSpeedF ! Filtered HSS (generator) speed [rad/s]. - REAL(4) :: GenTq ! Electrical generator torque, [Nm]. - REAL(4) :: GenTqMeas ! Measured 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]. - REAL(4) :: PC_KI ! Integral gain for pitch controller at rated pitch (zero), [-]. - REAL(4) :: PC_KD ! Differential gain for pitch controller at rated pitch (zero), [-]. - REAL(4) :: PC_TF ! First-order filter parameter for derivative action - REAL(4) :: PC_MaxPitVar ! Maximum pitch setting in pitch controller (variable) [rad]. - REAL(4) :: PC_PitComT ! Total command pitch based on the sum of the proportional and integral terms, [rad]. - REAL(4) :: PC_PitComT_IPC(3) ! Total command pitch based on the sum of the proportional and integral terms, including IPC term [rad]. - REAL(4) :: PC_PwrErr ! Power error with respect to rated power [W] - REAL(4) :: PC_SineExcitation ! Sine contribution to pitch signal - REAL(4) :: PC_SpdErr ! Current speed error (pitch control) [rad/s]. - INTEGER(4) :: PC_State ! State of the pitch control system - REAL(4) :: PitCom(3) ! Commanded pitch of each blade the last time the controller was called, [rad]. - INTEGER(4) :: TestType ! Test variable, no use - REAL(4) :: VS_LastGenTrq ! Commanded electrical generator torque the last time the controller was called, [Nm]. - REAL(4) :: VS_MechGenPwr ! Mechanical power on the generator axis [W] - REAL(4) :: VS_SpdErrAr ! Current speed error (generator torque control) [rad/s]. - REAL(4) :: VS_SpdErrBr ! Current speed error (generator torque control) [rad/s]. - INTEGER(4) :: VS_State ! State of the torque control system - REAL(4) :: WE_Vw ! Estimated wind speed [m/s] - REAL(4) :: WE_VwI ! Integrated wind speed quantity for estimation [m/s] - REAL(4) :: WE_VwIdot ! Differentiated integrated wind speed quantity for estimation [m/s] - REAL(4) :: Y_AccErr ! Accumulated yaw error [rad]. - REAL(4) :: Y_ErrLPFFast ! Filtered yaw error by fast low pass filter [rad]. - REAL(4) :: Y_ErrLPFSlow ! Filtered yaw error by slow low pass filter [rad]. - REAL(4) :: Y_MErr ! Measured yaw error, measured + setpoint [rad]. - REAL(4) :: Y_YawEndT ! Yaw end time, [s]. Indicates the time up until which yaw is active with a fixed rate + REAL(4) :: GenSpeedF ! Filtered HSS (generator) speed [rad/s]. + REAL(4) :: GenTq ! Electrical generator torque, [Nm]. + REAL(4) :: GenTqMeas ! Measured 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]. + REAL(4) :: PC_KI ! Integral gain for pitch controller at rated pitch (zero), [-]. + REAL(4) :: PC_KD ! Differential gain for pitch controller at rated pitch (zero), [-]. + REAL(4) :: PC_TF ! First-order filter parameter for derivative action + REAL(4) :: PC_MaxPitVar ! Maximum pitch setting in pitch controller (variable) [rad]. + REAL(4) :: PC_PitComT ! Total command pitch based on the sum of the proportional and integral terms, [rad]. + REAL(4) :: PC_PitComT_IPC(3) ! Total command pitch based on the sum of the proportional and integral terms, including IPC term [rad]. + REAL(4) :: PC_PwrErr ! Power error with respect to rated power [W] + REAL(4) :: PC_SineExcitation ! Sine contribution to pitch signal + REAL(4) :: PC_SpdErr ! Current speed error (pitch control) [rad/s]. + INTEGER(4) :: PC_State ! State of the pitch control system + REAL(4) :: PitCom(3) ! Commanded pitch of each blade the last time the controller was called, [rad]. + INTEGER(4) :: TestType ! Test variable, no use + REAL(4) :: VS_LastGenTrq ! Commanded electrical generator torque the last time the controller was called, [Nm]. + REAL(4) :: VS_MechGenPwr ! Mechanical power on the generator axis [W] + REAL(4) :: VS_SpdErrAr ! Current speed error (generator torque control) [rad/s]. + REAL(4) :: VS_SpdErrBr ! Current speed error (generator torque control) [rad/s]. + INTEGER(4) :: VS_State ! State of the torque control system + REAL(4) :: WE_Vw ! Estimated wind speed [m/s] + REAL(4) :: WE_VwI ! Integrated wind speed quantity for estimation [m/s] + REAL(4) :: WE_VwIdot ! Differentiated integrated wind speed quantity for estimation [m/s] + REAL(4) :: Y_AccErr ! Accumulated yaw error [rad]. + REAL(4) :: Y_ErrLPFFast ! Filtered yaw error by fast low pass filter [rad]. + REAL(4) :: Y_ErrLPFSlow ! Filtered yaw error by slow low pass filter [rad]. + REAL(4) :: Y_MErr ! Measured yaw error, measured + setpoint [rad]. + REAL(4) :: Y_YawEndT ! Yaw end time, [s]. Indicates the time up until which yaw is active with a fixed rate END TYPE LocalVariables TYPE, PUBLIC :: ObjectInstances - INTEGER(4) :: instLPF - INTEGER(4) :: instSecLPF - INTEGER(4) :: instHPF - INTEGER(4) :: instNotchSlopes - INTEGER(4) :: instNotch - INTEGER(4) :: instPI + INTEGER(4) :: instLPF + INTEGER(4) :: instSecLPF + INTEGER(4) :: instHPF + INTEGER(4) :: instNotchSlopes + INTEGER(4) :: instNotch + INTEGER(4) :: instPI END TYPE ObjectInstances END MODULE DRC_Types \ No newline at end of file diff --git a/Source/Filters.f90 b/Source/Filters.f90 index 09178b84..f6dc0e4c 100644 --- a/Source/Filters.f90 +++ b/Source/Filters.f90 @@ -12,36 +12,36 @@ MODULE Filters REAL FUNCTION LPFilter(InputSignal, DT, CornerFreq, iStatus, reset, inst) !............................................................................................................................... - ! Inputs + ! Inputs - REAL(4), INTENT(IN) :: InputSignal - REAL(4), INTENT(IN) :: DT ! time step [s] - REAL(4), INTENT(IN) :: CornerFreq ! corner frequency [rad/s] - INTEGER(4), INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. - INTEGER(4), INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. - LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal + REAL(4), INTENT(IN) :: InputSignal + REAL(4), INTENT(IN) :: DT ! time step [s] + REAL(4), INTENT(IN) :: CornerFreq ! corner frequency [rad/s] + INTEGER(4), INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. + INTEGER(4), INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. + LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal - ! Local + ! Local - REAL(4), DIMENSION(99), SAVE :: InputSignalLast ! Input signal the last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: OutputSignalLast ! Output signal the last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: InputSignalLast ! Input signal the last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: OutputSignalLast ! Output signal the last time this filter was called. Supports 99 separate instances. - ! Initialization + ! Initialization - IF ((iStatus == 0) .OR. reset) THEN - OutputSignalLast(inst) = InputSignal - InputSignalLast(inst) = InputSignal - ENDIF + IF ((iStatus == 0) .OR. reset) THEN + OutputSignalLast(inst) = InputSignal + InputSignalLast(inst) = InputSignal + ENDIF - ! Body + ! Body - LPFilter = (DT*CornerFreq*InputSignal + DT*CornerFreq*InputSignalLast(inst) - (DT*CornerFreq-2.0)*OutputSignalLast(inst))/(DT*CornerFreq+2.0) + LPFilter = (DT*CornerFreq*InputSignal + DT*CornerFreq*InputSignalLast(inst) - (DT*CornerFreq-2.0)*OutputSignalLast(inst))/(DT*CornerFreq+2.0) - ! Save signals for next time step + ! Save signals for next time step - InputSignalLast(inst) = InputSignal - OutputSignalLast(inst) = LPFilter - inst = inst + 1 + InputSignalLast(inst) = InputSignal + OutputSignalLast(inst) = LPFilter + inst = inst + 1 END FUNCTION LPFilter !------------------------------------------------------------------------------------------------------------------------------- @@ -51,24 +51,24 @@ REAL FUNCTION SecLPFilter(InputSignal, DT, CornerFreq, Damp, iStatus, reset, ins IMPLICIT NONE - ! Inputs + ! Inputs - REAL(4), INTENT(IN) :: InputSignal - REAL(4), INTENT(IN) :: DT ! time step [s] - REAL(4), INTENT(IN) :: CornerFreq ! corner frequency [rad/s] - REAL(4), INTENT(IN) :: Damp ! Dampening constant - INTEGER(4), INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. - INTEGER(4), INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. - LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal + REAL(4), INTENT(IN) :: InputSignal + REAL(4), INTENT(IN) :: DT ! time step [s] + REAL(4), INTENT(IN) :: CornerFreq ! corner frequency [rad/s] + REAL(4), INTENT(IN) :: Damp ! Dampening constant + INTEGER(4), INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. + INTEGER(4), INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. + LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal - ! Local + ! Local - REAL(4), DIMENSION(99), SAVE :: InputSignalLast1 ! Input signal the last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: InputSignalLast2 ! Input signal the next to last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: OutputSignalLast1 ! Output signal the last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: OutputSignalLast2 ! Output signal the next to last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: InputSignalLast1 ! Input signal the last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: InputSignalLast2 ! Input signal the next to last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: OutputSignalLast1 ! Output signal the last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: OutputSignalLast2 ! Output signal the next to last time this filter was called. Supports 99 separate instances. - ! Initialization + ! Initialization IF ((iStatus == 0) .OR. reset ) THEN OutputSignalLast1(inst) = InputSignal @@ -77,19 +77,19 @@ REAL FUNCTION SecLPFilter(InputSignal, DT, CornerFreq, Damp, iStatus, reset, ins InputSignalLast2(inst) = InputSignal ENDIF - ! Body + ! Body SecLPFilter = 1/(4+4*DT*Damp*CornerFreq+DT**2*CornerFreq**2) * ( (8-2*DT**2*CornerFreq**2)*OutputSignalLast1(inst) & - + (-4+4*DT*Damp*CornerFreq-DT**2*CornerFreq**2)*OutputSignalLast2(inst) + (DT**2*CornerFreq**2)*InputSignal & - + (2*DT**2*CornerFreq**2)*InputSignalLast1(inst) + (DT**2*CornerFreq**2)*InputSignalLast2(inst) ) + + (-4+4*DT*Damp*CornerFreq-DT**2*CornerFreq**2)*OutputSignalLast2(inst) + (DT**2*CornerFreq**2)*InputSignal & + + (2*DT**2*CornerFreq**2)*InputSignalLast1(inst) + (DT**2*CornerFreq**2)*InputSignalLast2(inst) ) - ! Save signals for next time step + ! Save signals for next time step InputSignalLast2(inst) = InputSignalLast1 (inst) InputSignalLast1(inst) = InputSignal OutputSignalLast2(inst) = OutputSignalLast1 (inst) OutputSignalLast1(inst) = SecLPFilter - inst = inst + 1 + inst = inst + 1 END FUNCTION SecLPFilter !------------------------------------------------------------------------------------------------------------------------------- @@ -97,22 +97,22 @@ END FUNCTION SecLPFilter REAL FUNCTION HPFilter( InputSignal, DT, CornerFreq, iStatus, reset, inst) !............................................................................................................................... - ! Inputs + ! Inputs - REAL(4), INTENT(IN) :: InputSignal - REAL(4), INTENT(IN) :: DT ! time step [s] - REAL(4), INTENT(IN) :: CornerFreq ! corner frequency [rad/s] - INTEGER, INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. - INTEGER, INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. - LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal + REAL(4), INTENT(IN) :: InputSignal + REAL(4), INTENT(IN) :: DT ! time step [s] + REAL(4), INTENT(IN) :: CornerFreq ! corner frequency [rad/s] + INTEGER, INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. + INTEGER, INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. + LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal - ! Local + ! Local - REAL(4) :: K ! Constant gain - REAL(4), DIMENSION(99), SAVE :: InputSignalLast ! Input signal the last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: OutputSignalLast ! Output signal the last time this filter was called. Supports 99 separate instances. + REAL(4) :: K ! Constant gain + REAL(4), DIMENSION(99), SAVE :: InputSignalLast ! Input signal the last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: OutputSignalLast ! Output signal the last time this filter was called. Supports 99 separate instances. - ! Initialization + ! Initialization IF ((iStatus == 0) .OR. reset) THEN OutputSignalLast(inst) = InputSignal @@ -121,15 +121,15 @@ REAL FUNCTION HPFilter( InputSignal, DT, CornerFreq, iStatus, reset, inst) K = 2.0 / DT - ! Body + ! Body HPFilter = K/(CornerFreq + K)*InputSignal - K/(CornerFreq + K)*InputSignalLast(inst) - (CornerFreq - K)/(CornerFreq + K)*OutputSignalLast(inst) - ! Save signals for next time step + ! Save signals for next time step InputSignalLast(inst) = InputSignal OutputSignalLast(inst) = HPFilter - inst = inst + 1 + inst = inst + 1 END FUNCTION HPFilter !------------------------------------------------------------------------------------------------------------------------------- @@ -137,122 +137,122 @@ END FUNCTION HPFilter REAL FUNCTION NotchFilterSlopes(InputSignal, DT, CornerFreq, Damp, iStatus, reset, inst) !............................................................................................................................... - ! Inputs - - REAL(4), INTENT(IN) :: InputSignal - REAL(4), INTENT(IN) :: DT ! time step [s] - REAL(4), INTENT(IN) :: CornerFreq ! corner frequency [rad/s] - REAL(4), INTENT(IN) :: Damp ! Dampening constant - INTEGER, INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. - INTEGER, INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. - LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal - - ! Local - - REAL(4), DIMENSION(99), SAVE :: InputSignalLast1 ! Input signal the last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: InputSignalLast2 ! Input signal the next to last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: OutputSignalLast1 ! Output signal the last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: OutputSignalLast2 ! Output signal the next to last time this filter was called. Supports 99 separate instances. - - ! Initialization - - IF ((iStatus == 0) .OR. reset) THEN - OutputSignalLast1(inst) = InputSignal - OutputSignalLast2(inst) = InputSignal - InputSignalLast1(inst) = InputSignal - InputSignalLast2(inst) = InputSignal - ENDIF - - ! Body - - NotchFilterSlopes = 1.0/(4.0+2.0*DT*Damp*CornerFreq+DT**2.0*CornerFreq**2.0) * ( (8.0-2.0*DT**2.0*CornerFreq**2.0)*OutputSignalLast1(inst) & - + (-4.0+2.0*DT*Damp*CornerFreq-DT**2.0*CornerFreq**2.0)*OutputSignalLast2(inst) + & - (2.0*DT*Damp*CornerFreq)*InputSignal + (-2.0*DT*Damp*CornerFreq)*InputSignalLast2(inst) ) - - ! Save signals for next time step - - InputSignalLast2(inst) = InputSignalLast1(inst) - InputSignalLast1(inst) = InputSignal !Save input signal for next time step - OutputSignalLast2(inst) = OutputSignalLast1(inst) !Save input signal for next time step - OutputSignalLast1(inst) = NotchFilterSlopes - inst = inst + 1 - - END FUNCTION NotchFilterSlopes - !------------------------------------------------------------------------------------------------------------------------------- - ! Discrete time Notch Filter, G = (s^2 + 2*omega*betaNum*s + omega^2)/(s^2 + 2*omega*betaDen*s + omega^2) - REAL FUNCTION NotchFilter(InputSignal, DT, omega, betaNum, betaDen, iStatus, reset, inst) - !............................................................................................................................... - - ! Inputs - - REAL(4), INTENT(IN) :: InputSignal - REAL(4), INTENT(IN) :: DT ! time step [s] - REAL(4), INTENT(IN) :: omega ! corner frequency [rad/s] - REAL(4), INTENT(IN) :: betaNum ! Dampening constant in numerator of filter transfer function - REAL(4), INTENT(IN) :: betaDen ! Dampening constant in denominator of filter transfer function - INTEGER, INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. - INTEGER, INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. - LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal - - ! Local - REAL(4) :: K, P1, P2, P3, P4, P5 ! Constant gain - REAL(4), DIMENSION(99), SAVE :: InputSignalLast1 ! Input signal the last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: InputSignalLast2 ! Input signal the next to last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: OutputSignalLast1 ! Output signal the last time this filter was called. Supports 99 separate instances. - REAL(4), DIMENSION(99), SAVE :: OutputSignalLast2 ! Output signal the next to last time this filter was called. Supports 99 separate instances. - - ! Initialization - - IF ((iStatus == 0) .OR. reset) THEN - OutputSignalLast1(inst) = InputSignal - OutputSignalLast2(inst) = InputSignal - InputSignalLast1(inst) = InputSignal - InputSignalLast2(inst) = InputSignal - ENDIF - - K = 2/DT - P1 = (K**2 + 2*omega*BetaNum*K + omega**2)/(K**2 + 2*omega*BetaDen*K + omega**2) - P2 = (2*omega**2 - 2*K**2) / (K**2 + 2*omega*BetaDen*K + omega**2); - P3 = (K**2 - 2*omega*BetaNum*K + omega**2) / (K**2 + 2*omega*BetaDen*K + omega**2) - P4 = (2*omega**2 - 2*K**2) / (K**2 + 2*omega*BetaDen*K + omega**2) - P5 = (K**2 - 2*omega*BetaDen*K + omega**2)/ (K**2 + 2*omega*BetaDen*K + omega**2) - + ! Inputs + + REAL(4), INTENT(IN) :: InputSignal + REAL(4), INTENT(IN) :: DT ! time step [s] + REAL(4), INTENT(IN) :: CornerFreq ! corner frequency [rad/s] + REAL(4), INTENT(IN) :: Damp ! Dampening constant + INTEGER, INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. + INTEGER, INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. + LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal + + ! Local + + REAL(4), DIMENSION(99), SAVE :: InputSignalLast1 ! Input signal the last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: InputSignalLast2 ! Input signal the next to last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: OutputSignalLast1 ! Output signal the last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: OutputSignalLast2 ! Output signal the next to last time this filter was called. Supports 99 separate instances. + + ! Initialization + + IF ((iStatus == 0) .OR. reset) THEN + OutputSignalLast1(inst) = InputSignal + OutputSignalLast2(inst) = InputSignal + InputSignalLast1(inst) = InputSignal + InputSignalLast2(inst) = InputSignal + ENDIF + + ! Body + + NotchFilterSlopes = 1.0/(4.0+2.0*DT*Damp*CornerFreq+DT**2.0*CornerFreq**2.0) * ( (8.0-2.0*DT**2.0*CornerFreq**2.0)*OutputSignalLast1(inst) & + + (-4.0+2.0*DT*Damp*CornerFreq-DT**2.0*CornerFreq**2.0)*OutputSignalLast2(inst) + & + (2.0*DT*Damp*CornerFreq)*InputSignal + (-2.0*DT*Damp*CornerFreq)*InputSignalLast2(inst) ) + + ! Save signals for next time step + + InputSignalLast2(inst) = InputSignalLast1(inst) + InputSignalLast1(inst) = InputSignal !Save input signal for next time step + OutputSignalLast2(inst) = OutputSignalLast1(inst) !Save input signal for next time step + OutputSignalLast1(inst) = NotchFilterSlopes + inst = inst + 1 + + END FUNCTION NotchFilterSlopes + !------------------------------------------------------------------------------------------------------------------------------- + ! Discrete time Notch Filter, G = (s^2 + 2*omega*betaNum*s + omega^2)/(s^2 + 2*omega*betaDen*s + omega^2) + REAL FUNCTION NotchFilter(InputSignal, DT, omega, betaNum, betaDen, iStatus, reset, inst) + !............................................................................................................................... + + ! Inputs + + REAL(4), INTENT(IN) :: InputSignal + REAL(4), INTENT(IN) :: DT ! time step [s] + REAL(4), INTENT(IN) :: omega ! corner frequency [rad/s] + REAL(4), INTENT(IN) :: betaNum ! Dampening constant in numerator of filter transfer function + REAL(4), INTENT(IN) :: betaDen ! Dampening constant in denominator of filter transfer function + INTEGER, INTENT(IN) :: iStatus ! A status flag set by the simulation as follows: 0 if this is the first call, 1 for all subsequent time steps, -1 if this is the final call at the end of the simulation. + INTEGER, INTENT(INOUT) :: inst ! Instance number. Every instance of this function needs to have an unique instance number to ensure instances don't influence each other. + LOGICAL(4), INTENT(IN) :: reset ! Reset the filter to the input signal + + ! Local + REAL(4) :: K, P1, P2, P3, P4, P5 ! Constant gain + REAL(4), DIMENSION(99), SAVE :: InputSignalLast1 ! Input signal the last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: InputSignalLast2 ! Input signal the next to last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: OutputSignalLast1 ! Output signal the last time this filter was called. Supports 99 separate instances. + REAL(4), DIMENSION(99), SAVE :: OutputSignalLast2 ! Output signal the next to last time this filter was called. Supports 99 separate instances. + + ! Initialization + + IF ((iStatus == 0) .OR. reset) THEN + OutputSignalLast1(inst) = InputSignal + OutputSignalLast2(inst) = InputSignal + InputSignalLast1(inst) = InputSignal + InputSignalLast2(inst) = InputSignal + ENDIF + + K = 2/DT + P1 = (K**2 + 2*omega*BetaNum*K + omega**2)/(K**2 + 2*omega*BetaDen*K + omega**2) + P2 = (2*omega**2 - 2*K**2) / (K**2 + 2*omega*BetaDen*K + omega**2); + P3 = (K**2 - 2*omega*BetaNum*K + omega**2) / (K**2 + 2*omega*BetaDen*K + omega**2) + P4 = (2*omega**2 - 2*K**2) / (K**2 + 2*omega*BetaDen*K + omega**2) + P5 = (K**2 - 2*omega*BetaDen*K + omega**2)/ (K**2 + 2*omega*BetaDen*K + omega**2) + ! Body - NotchFilter = P1*InputSignal + P2*InputSignalLast1(inst) + P3*InputSignalLast2(inst) - P4*OutputSignalLast1(inst) - P5*OutputSignalLast2(inst) - - ! Save signals for next time step - InputSignalLast2(inst) = InputSignalLast1(inst) - InputSignalLast1(inst) = InputSignal !Save input signal for next time step - OutputSignalLast2(inst) = OutputSignalLast1(inst) !Save input signal for next time step - OutputSignalLast1(inst) = NotchFilter - inst = inst + 1 - - END FUNCTION NotchFilter - !------------------------------------------------------------------------------------------------------------------------------- - ! Prefilter measured wind turbine signals to separate the filtering from the actual control actions - SUBROUTINE PreFilterMeasuredSignals(CntrPar, LocalVar, objInst) - !............................................................................................................................... - - USE DRC_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances - - TYPE(ControlParameters), INTENT(INOUT) :: CntrPar - TYPE(LocalVariables), INTENT(INOUT) :: LocalVar - TYPE(ObjectInstances), INTENT(INOUT) :: objInst - - ! Filter the HSS (generator) speed measurement: - ! Apply Low-Pass Filter (choice between first- and second-order low-pass filter) - IF (CntrPar%F_LPFType == 1) THEN + NotchFilter = P1*InputSignal + P2*InputSignalLast1(inst) + P3*InputSignalLast2(inst) - P4*OutputSignalLast1(inst) - P5*OutputSignalLast2(inst) + + ! Save signals for next time step + InputSignalLast2(inst) = InputSignalLast1(inst) + InputSignalLast1(inst) = InputSignal !Save input signal for next time step + OutputSignalLast2(inst) = OutputSignalLast1(inst) !Save input signal for next time step + OutputSignalLast1(inst) = NotchFilter + inst = inst + 1 + + END FUNCTION NotchFilter + !------------------------------------------------------------------------------------------------------------------------------- + ! Prefilter measured wind turbine signals to separate the filtering from the actual control actions + SUBROUTINE PreFilterMeasuredSignals(CntrPar, LocalVar, objInst) + !............................................................................................................................... + + USE DRC_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances + + TYPE(ControlParameters), INTENT(INOUT) :: CntrPar + TYPE(LocalVariables), INTENT(INOUT) :: LocalVar + TYPE(ObjectInstances), INTENT(INOUT) :: objInst + + ! Filter the HSS (generator) speed measurement: + ! Apply Low-Pass Filter (choice between first- and second-order low-pass filter) + IF (CntrPar%F_LPFType == 1) THEN LocalVar%GenSpeedF = LPFilter(LocalVar%GenSpeed, LocalVar%DT, CntrPar%F_LPFCornerFreq, LocalVar%iStatus, .FALSE., objInst%instLPF) - ELSEIF (CntrPar%F_LPFType == 2) THEN + ELSEIF (CntrPar%F_LPFType == 2) THEN LocalVar%GenSpeedF = SecLPFilter(LocalVar%GenSpeed, LocalVar%DT, CntrPar%F_LPFCornerFreq, CntrPar%F_LPFDamping, LocalVar%iStatus, .FALSE., objInst%instSecLPF) ! Second-order low-pass filter on generator speed END IF - - IF (CntrPar%F_NotchType == 1) THEN - LocalVar%GenSpeedF = NotchFilter(LocalVar%GenSpeedF, LocalVar%DT, CntrPar%F_NotchCornerFreq, CntrPar%F_NotchBetaNumDen(1), CntrPar%F_NotchBetaNumDen(2), LocalVar%iStatus, .FALSE., objInst%instNotch) - END IF - + + IF (CntrPar%F_NotchType == 1) THEN + LocalVar%GenSpeedF = NotchFilter(LocalVar%GenSpeedF, LocalVar%DT, CntrPar%F_NotchCornerFreq, CntrPar%F_NotchBetaNumDen(1), CntrPar%F_NotchBetaNumDen(2), LocalVar%iStatus, .FALSE., objInst%instNotch) + END IF + ! Filtering the tower fore-aft acceleration signal LocalVar%FA_AccHPF = HPFilter(LocalVar%FA_Acc, LocalVar%DT, CntrPar%FA_HPFCornerFreq, LocalVar%iStatus, .FALSE., objInst%instHPF) - END SUBROUTINE PreFilterMeasuredSignals - END MODULE Filters + END SUBROUTINE PreFilterMeasuredSignals + END MODULE Filters diff --git a/Source/Functions.f90 b/Source/Functions.f90 index 9b41d65d..d43f7d20 100644 --- a/Source/Functions.f90 +++ b/Source/Functions.f90 @@ -5,377 +5,377 @@ MODULE Functions IMPLICIT NONE CONTAINS - !------------------------------------------------------------------------------------------------------------------------------- - ! Saturates inputValue. Makes sure it is not smaller than minValue and not larger than maxValue - REAL FUNCTION saturate(inputValue, minValue, maxValue) - ! + !------------------------------------------------------------------------------------------------------------------------------- + ! Saturates inputValue. Makes sure it is not smaller than minValue and not larger than maxValue + REAL FUNCTION saturate(inputValue, minValue, maxValue) + ! - IMPLICIT NONE + IMPLICIT NONE - REAL(4), INTENT(IN) :: inputValue - REAL(4), INTENT(IN) :: minValue - REAL(4), INTENT(IN) :: maxValue + REAL(4), INTENT(IN) :: inputValue + REAL(4), INTENT(IN) :: minValue + REAL(4), INTENT(IN) :: maxValue - saturate = MIN(MAX(inputValue,minValue), maxValue) + saturate = MIN(MAX(inputValue,minValue), maxValue) - END FUNCTION saturate - !------------------------------------------------------------------------------------------------------------------------------- - ! Saturates inputValue. Makes sure it is not smaller than minValue and not larger than maxValue - REAL FUNCTION ratelimit(inputSignal, inputSignalPrev, minRate, maxRate, DT) - ! - IMPLICIT NONE + END FUNCTION saturate + !------------------------------------------------------------------------------------------------------------------------------- + ! Saturates inputValue. Makes sure it is not smaller than minValue and not larger than maxValue + REAL FUNCTION ratelimit(inputSignal, inputSignalPrev, minRate, maxRate, DT) + ! + IMPLICIT NONE - REAL(4), INTENT(IN) :: inputSignal - REAL(4), INTENT(IN) :: inputSignalPrev - REAL(4), INTENT(IN) :: minRate - REAL(4), INTENT(IN) :: maxRate - REAL(4), INTENT(IN) :: DT - - ! Local variables - REAL(4) :: rate + REAL(4), INTENT(IN) :: inputSignal + REAL(4), INTENT(IN) :: inputSignalPrev + REAL(4), INTENT(IN) :: minRate + REAL(4), INTENT(IN) :: maxRate + REAL(4), INTENT(IN) :: DT + + ! Local variables + REAL(4) :: rate - rate = (inputSignal - inputSignalPrev)/DT ! Signal rate (unsaturated) - rate = saturate(rate, minRate, maxRate) ! Saturate the signal rate - ratelimit = inputSignalPrev + rate*DT ! Saturate the overall command using the rate limit + rate = (inputSignal - inputSignalPrev)/DT ! Signal rate (unsaturated) + rate = saturate(rate, minRate, maxRate) ! Saturate the signal rate + ratelimit = inputSignalPrev + rate*DT ! Saturate the overall command using the rate limit - END FUNCTION ratelimit - !------------------------------------------------------------------------------------------------------------------------------- - ! PI controller, with output saturation - REAL FUNCTION PIController(error, kp, ki, minValue, maxValue, DT, I0, reset, inst) - ! - IMPLICIT NONE + END FUNCTION ratelimit + !------------------------------------------------------------------------------------------------------------------------------- + ! PI controller, with output saturation + REAL FUNCTION PIController(error, kp, ki, minValue, maxValue, DT, I0, reset, inst) + ! + IMPLICIT NONE - ! Inputs - REAL(4), INTENT(IN) :: error - REAL(4), INTENT(IN) :: kp - REAL(4), INTENT(IN) :: ki - REAL(4), INTENT(IN) :: minValue - REAL(4), INTENT(IN) :: maxValue - REAL(4), INTENT(IN) :: DT - INTEGER(4), INTENT(INOUT) :: inst - REAL(4), INTENT(IN) :: I0 - LOGICAL, INTENT(IN) :: reset - - ! Local - INTEGER(4) :: i ! Counter for making arrays - REAL(4) :: PTerm ! Proportional term - REAL(4), DIMENSION(99), SAVE :: ITerm = (/ (real(9999.9), i = 1,99) /) ! Integral term, current. - REAL(4), DIMENSION(99), SAVE :: ITermLast = (/ (real(9999.9), i = 1,99) /) ! Integral term, the last time this controller was called. Supports 99 separate instances. - INTEGER(4), DIMENSION(99), SAVE :: FirstCall = (/ (1, i=1,99) /) ! First call of this function? - - ! Initialize persistent variables/arrays, and set inital condition for integrator term - IF ((FirstCall(inst) == 1) .OR. reset) THEN - ITerm(inst) = I0 - ITermLast(inst) = I0 - - FirstCall(inst) = 0 - PIController = I0 - ELSE - PTerm = kp*error - ITerm(inst) = ITerm(inst) + DT*ki*error - ITerm(inst) = saturate(ITerm(inst), minValue, maxValue) - PIController = PTerm + ITerm(inst) - PIController = saturate(PIController, minValue, maxValue) - - ITermLast(inst) = ITerm(inst) - END IF - inst = inst + 1 - - END FUNCTION PIController - !------------------------------------------------------------------------------------------------------------------------------- - ! interp1 1-D interpolation (table lookup), xData and yData should be monotonically increasing - REAL FUNCTION interp1d(xData, yData, xq) - ! - IMPLICIT NONE - ! Inputs - REAL(4), DIMENSION(:), INTENT(IN) :: xData ! Provided x data (vector), to be interpolated - REAL(4), DIMENSION(:), INTENT(IN) :: yData ! Provided y data (vector), to be interpolated - REAL(4), INTENT(IN) :: xq ! x-value for which the y value has to be interpolated - INTEGER(4) :: I ! Iteration index - - IF (xq <= MINVAL(xData)) THEN - interp1d = yData(1) - ELSEIF (xq >= MAXVAL(xData)) THEN - interp1d = yData(SIZE(xData)) - ELSE - DO I = 1, SIZE(xData) - IF (xq <= xData(I)) THEN - interp1d = yData(I-1) + (yData(I) - yData(I-1))/(xData(I) - xData(I-1))*(xq - xData(I-1)) - EXIT - ELSE - CONTINUE - END IF - END DO - END IF - - END FUNCTION interp1d - !------------------------------------------------------------------------------------------------------------------------------- - ! DF controller, with output saturation - REAL FUNCTION DFController(error, Kd, Tf, DT, inst) - ! - IMPLICIT NONE + ! Inputs + REAL(4), INTENT(IN) :: error + REAL(4), INTENT(IN) :: kp + REAL(4), INTENT(IN) :: ki + REAL(4), INTENT(IN) :: minValue + REAL(4), INTENT(IN) :: maxValue + REAL(4), INTENT(IN) :: DT + INTEGER(4), INTENT(INOUT) :: inst + REAL(4), INTENT(IN) :: I0 + LOGICAL, INTENT(IN) :: reset + + ! Local + INTEGER(4) :: i ! Counter for making arrays + REAL(4) :: PTerm ! Proportional term + REAL(4), DIMENSION(99), SAVE :: ITerm = (/ (real(9999.9), i = 1,99) /) ! Integral term, current. + REAL(4), DIMENSION(99), SAVE :: ITermLast = (/ (real(9999.9), i = 1,99) /) ! Integral term, the last time this controller was called. Supports 99 separate instances. + INTEGER(4), DIMENSION(99), SAVE :: FirstCall = (/ (1, i=1,99) /) ! First call of this function? + + ! Initialize persistent variables/arrays, and set inital condition for integrator term + IF ((FirstCall(inst) == 1) .OR. reset) THEN + ITerm(inst) = I0 + ITermLast(inst) = I0 + + FirstCall(inst) = 0 + PIController = I0 + ELSE + PTerm = kp*error + ITerm(inst) = ITerm(inst) + DT*ki*error + ITerm(inst) = saturate(ITerm(inst), minValue, maxValue) + PIController = PTerm + ITerm(inst) + PIController = saturate(PIController, minValue, maxValue) + + ITermLast(inst) = ITerm(inst) + END IF + inst = inst + 1 + + END FUNCTION PIController + !------------------------------------------------------------------------------------------------------------------------------- + ! interp1 1-D interpolation (table lookup), xData and yData should be monotonically increasing + REAL FUNCTION interp1d(xData, yData, xq) + ! + IMPLICIT NONE + ! Inputs + REAL(4), DIMENSION(:), INTENT(IN) :: xData ! Provided x data (vector), to be interpolated + REAL(4), DIMENSION(:), INTENT(IN) :: yData ! Provided y data (vector), to be interpolated + REAL(4), INTENT(IN) :: xq ! x-value for which the y value has to be interpolated + INTEGER(4) :: I ! Iteration index + + IF (xq <= MINVAL(xData)) THEN + interp1d = yData(1) + ELSEIF (xq >= MAXVAL(xData)) THEN + interp1d = yData(SIZE(xData)) + ELSE + DO I = 1, SIZE(xData) + IF (xq <= xData(I)) THEN + interp1d = yData(I-1) + (yData(I) - yData(I-1))/(xData(I) - xData(I-1))*(xq - xData(I-1)) + EXIT + ELSE + CONTINUE + END IF + END DO + END IF + + END FUNCTION interp1d + !------------------------------------------------------------------------------------------------------------------------------- + ! DF controller, with output saturation + REAL FUNCTION DFController(error, Kd, Tf, DT, inst) + ! + IMPLICIT NONE - ! Inputs - REAL(4), INTENT(IN) :: error - REAL(4), INTENT(IN) :: kd - REAL(4), INTENT(IN) :: tf - REAL(4), INTENT(IN) :: DT - INTEGER(4), INTENT(IN) :: inst - - ! Local - REAL(4) :: B ! - INTEGER(4) :: i ! Counter for making arrays - REAL(4), DIMENSION(99), SAVE :: errorLast = (/ (0, i=1,99) /) ! - REAL(4), DIMENSION(99), SAVE :: DFControllerLast = (/ (0, i=1,99) /) ! - INTEGER(4), DIMENSION(99), SAVE :: FirstCall = (/ (1, i=1,99) /) ! First call of this function? - - ! Initialize persistent variables/arrays, and set inital condition for integrator term - ! IF (FirstCall(inst) == 1) THEN - ! FirstCall(inst) = 0 - ! END IF - - B = 2.0/DT - DFController = (Kd*B)/(B*Tf+1.0)*error - (Kd*B)/(B*Tf+1.0)*errorLast(inst) - (1.0-B*Tf)/(B*Tf+1.0)*DFControllerLast(inst) + ! Inputs + REAL(4), INTENT(IN) :: error + REAL(4), INTENT(IN) :: kd + REAL(4), INTENT(IN) :: tf + REAL(4), INTENT(IN) :: DT + INTEGER(4), INTENT(IN) :: inst + + ! Local + REAL(4) :: B ! + INTEGER(4) :: i ! Counter for making arrays + REAL(4), DIMENSION(99), SAVE :: errorLast = (/ (0, i=1,99) /) ! + REAL(4), DIMENSION(99), SAVE :: DFControllerLast = (/ (0, i=1,99) /) ! + INTEGER(4), DIMENSION(99), SAVE :: FirstCall = (/ (1, i=1,99) /) ! First call of this function? + + ! Initialize persistent variables/arrays, and set inital condition for integrator term + ! IF (FirstCall(inst) == 1) THEN + ! FirstCall(inst) = 0 + ! END IF + + B = 2.0/DT + DFController = (Kd*B)/(B*Tf+1.0)*error - (Kd*B)/(B*Tf+1.0)*errorLast(inst) - (1.0-B*Tf)/(B*Tf+1.0)*DFControllerLast(inst) - errorLast(inst) = error - DFControllerLast(inst) = DFController - END FUNCTION DFController - !------------------------------------------------------------------------------------------------------------------------------- - ! State machines, determines the state of the wind turbine to determine the corresponding control actions - ! States: - ! - VS/PC_State = 0, Error state, for debugging purposes (VS) / No pitch control active, pitch constant at fine-pitch (PC) - ! - VS_State = 1, Region 1(.5) operation, torque control to keep the rotor at cut-in speed towards the Cp-max operational curve - ! - VS_State = 2, Region 2, operation, maximum rotor power efficiency (Cp-max) tracking, keep TSR constant at a fixed fine-pitch angle - ! - VS_State = 3, Region 2.5, transition between below and above-rated operating conditions (near-rated region) using PI torque control - ! - VS_State = 4 + PC_State = 1, above-rated operation using pitch control (constant torque mode) - ! - VS_State = 5 + PC_State = 2, above-rated operation using pitch and torque control (constant power mode) - SUBROUTINE StateMachine(CntrPar, LocalVar) - USE DRC_Types, ONLY : LocalVariables, ControlParameters - IMPLICIT NONE + errorLast(inst) = error + DFControllerLast(inst) = DFController + END FUNCTION DFController + !------------------------------------------------------------------------------------------------------------------------------- + ! State machines, determines the state of the wind turbine to determine the corresponding control actions + ! States: + ! - VS/PC_State = 0, Error state, for debugging purposes (VS) / No pitch control active, pitch constant at fine-pitch (PC) + ! - VS_State = 1, Region 1(.5) operation, torque control to keep the rotor at cut-in speed towards the Cp-max operational curve + ! - VS_State = 2, Region 2, operation, maximum rotor power efficiency (Cp-max) tracking, keep TSR constant at a fixed fine-pitch angle + ! - VS_State = 3, Region 2.5, transition between below and above-rated operating conditions (near-rated region) using PI torque control + ! - VS_State = 4 + PC_State = 1, above-rated operation using pitch control (constant torque mode) + ! - VS_State = 5 + PC_State = 2, above-rated operation using pitch and torque control (constant power mode) + SUBROUTINE StateMachine(CntrPar, LocalVar) + USE DRC_Types, ONLY : LocalVariables, ControlParameters + IMPLICIT NONE - ! Inputs - TYPE(ControlParameters), INTENT(IN) :: CntrPar - TYPE(LocalVariables), INTENT(INOUT) :: LocalVar - - ! Local - ! Pitch control state machine - 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 ((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 - !------------------------------------------------------------------------------------------------------------------------------- - SUBROUTINE Debug(LocalVar, CntrPar, avrSWAP, RootName, size_avcOUTNAME) - USE, INTRINSIC :: ISO_C_Binding - USE DRC_Types, ONLY : LocalVariables, ControlParameters - - IMPLICIT NONE - - TYPE(ControlParameters), INTENT(IN) :: CntrPar - TYPE(LocalVariables), INTENT(IN) :: LocalVar - - INTEGER(4), INTENT(IN) :: size_avcOUTNAME - INTEGER(4) :: I ! Generic index. - CHARACTER(1), PARAMETER :: Tab = CHAR(9) ! The tab character. - CHARACTER(25), PARAMETER :: FmtDat = "(F8.3,99('"//Tab//"',ES10.3E2,:)) " ! The format of the debugging data - INTEGER(4), PARAMETER :: UnDb = 85 ! I/O unit for the debugging information - INTEGER(4), PARAMETER :: UnDb2 = 86 ! I/O unit for the debugging information, avrSWAP - REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. - CHARACTER(size_avcOUTNAME-1), INTENT(IN) :: RootName ! a Fortran version of the input C string (not considered an array here) [subtract 1 for the C null-character] - - !.............................................................................................................................. - ! Initializing debug file - !.............................................................................................................................. - 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(unit=UnDb, FILE=TRIM(RootName)//'.dbg', STATUS='NEW') - OPEN(unit=UnDb, FILE='DEBUG.dbg') - WRITE (UnDb,'(A)') ' LocalVar%Time ' //Tab//'LocalVar%FA_Acc '//Tab//'LocalVar%FA_AccHPF '//Tab//'LocalVar%FA_AccHPFI '//Tab//'LocalVar%PitCom ' - WRITE (UnDb,'(A)') ' (sec) ' //Tab//'(m/s^2) ' //Tab//'(m/s^2) ' //Tab//'(m/s) ' //Tab//'(rad) ' - !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 - - IF (CntrPar%LoggingLevel > 1) THEN - !OPEN(UnDb2, FILE=TRIM(RootName)//'.dbg2', STATUS='REPLACE') + ! Inputs + TYPE(ControlParameters), INTENT(IN) :: CntrPar + TYPE(LocalVariables), INTENT(INOUT) :: LocalVar + + ! Local + ! Pitch control state machine + 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 ((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 + !------------------------------------------------------------------------------------------------------------------------------- + SUBROUTINE Debug(LocalVar, CntrPar, avrSWAP, RootName, size_avcOUTNAME) + USE, INTRINSIC :: ISO_C_Binding + USE DRC_Types, ONLY : LocalVariables, ControlParameters + + IMPLICIT NONE + + TYPE(ControlParameters), INTENT(IN) :: CntrPar + TYPE(LocalVariables), INTENT(IN) :: LocalVar + + INTEGER(4), INTENT(IN) :: size_avcOUTNAME + INTEGER(4) :: I ! Generic index. + CHARACTER(1), PARAMETER :: Tab = CHAR(9) ! The tab character. + CHARACTER(25), PARAMETER :: FmtDat = "(F8.3,99('"//Tab//"',ES10.3E2,:)) " ! The format of the debugging data + INTEGER(4), PARAMETER :: UnDb = 85 ! I/O unit for the debugging information + INTEGER(4), PARAMETER :: UnDb2 = 86 ! I/O unit for the debugging information, avrSWAP + REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. + CHARACTER(size_avcOUTNAME-1), INTENT(IN) :: RootName ! a Fortran version of the input C string (not considered an array here) [subtract 1 for the C null-character] + + !.............................................................................................................................. + ! Initializing debug file + !.............................................................................................................................. + 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(unit=UnDb, FILE=TRIM(RootName)//'.dbg', STATUS='NEW') + OPEN(unit=UnDb, FILE='DEBUG.dbg') + WRITE (UnDb,'(A)') ' LocalVar%Time ' //Tab//'LocalVar%FA_Acc '//Tab//'LocalVar%FA_AccHPF '//Tab//'LocalVar%FA_AccHPFI '//Tab//'LocalVar%PitCom ' + WRITE (UnDb,'(A)') ' (sec) ' //Tab//'(m/s^2) ' //Tab//'(m/s^2) ' //Tab//'(m/s) ' //Tab//'(rad) ' + !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 + + IF (CntrPar%LoggingLevel > 1) THEN + !OPEN(UnDb2, FILE=TRIM(RootName)//'.dbg2', STATUS='REPLACE') OPEN(unit=UnDb2, FILE='DEBUG2.dbg') - WRITE(UnDb2,'(/////)') - WRITE(UnDb2,'(A,85("'//Tab//'AvrSWAP(",I2,")"))') 'LocalVar%Time ', (i,i=1,85) - WRITE(UnDb2,'(A,85("'//Tab//'(-)"))') '(s)' - END IF - ELSE - ! Print simulation status, every 10 seconds - IF (MODULO(LocalVar%Time, 10.0) == 0) THEN - WRITE(*, 100) LocalVar%GenSpeedF*RPS2RPM, LocalVar%BlPitch(1)*R2D, avrSWAP(15)/1000.0, LocalVar%WE_Vw ! LocalVar%Time !/1000.0 - 100 FORMAT('Generator speed: ', f6.1, ' RPM, Pitch angle: ', f5.1, ' deg, Power: ', f7.1, ' kW, Est. wind Speed: ', f5.1, ' m/s') - ! PRINT *, LocalVar%PC_State, LocalVar%VS_State, CntrPar%VS_Rgn3Pitch, CntrPar%PC_FinePit, CntrPar%PC_Switch, LocalVar%BlPitch(1) ! Additional debug info + WRITE(UnDb2,'(/////)') + WRITE(UnDb2,'(A,85("'//Tab//'AvrSWAP(",I2,")"))') 'LocalVar%Time ', (i,i=1,85) + WRITE(UnDb2,'(A,85("'//Tab//'(-)"))') '(s)' + END IF + ELSE + ! Print simulation status, every 10 seconds + IF (MODULO(LocalVar%Time, 10.0) == 0) THEN + WRITE(*, 100) LocalVar%GenSpeedF*RPS2RPM, LocalVar%BlPitch(1)*R2D, avrSWAP(15)/1000.0, LocalVar%WE_Vw ! LocalVar%Time !/1000.0 + 100 FORMAT('Generator speed: ', f6.1, ' RPM, Pitch angle: ', f5.1, ' deg, Power: ', f7.1, ' kW, Est. wind Speed: ', f5.1, ' m/s') + ! PRINT *, LocalVar%PC_State, LocalVar%VS_State, CntrPar%VS_Rgn3Pitch, CntrPar%PC_FinePit, CntrPar%PC_Switch, LocalVar%BlPitch(1) ! Additional debug info ! PRINT *, LocalVar%RotSpeed - END IF - - ! Output debugging information if requested: - IF (CntrPar%LoggingLevel > 0) THEN - WRITE (UnDb,FmtDat) LocalVar%Time, LocalVar%FA_Acc, LocalVar%FA_AccHPF, LocalVar%FA_AccHPFI, LocalVar%PitCom - END IF - - IF (CntrPar%LoggingLevel > 1) THEN - WRITE (UnDb2,FmtDat) LocalVar%Time, avrSWAP(1:85) - END IF - END IF - - IF (MODULO(LocalVar%Time, 10.0) == 0.0) THEN - !LocalVar%TestType = LocalVar%TestType + 10 - !PRINT *, LocalVar%TestType - END IF - END SUBROUTINE Debug - !------------------------------------------------------------------------------------------------------------------------------- - !The Coleman or d-q axis transformation transforms the root out of plane bending moments of each turbine blade - !to a direct axis and a quadrature axis - SUBROUTINE ColemanTransform(rootMOOP, aziAngle, nHarmonic, axTOut, axYOut) - !............................................................................................................................... + END IF + + ! Output debugging information if requested: + IF (CntrPar%LoggingLevel > 0) THEN + WRITE (UnDb,FmtDat) LocalVar%Time, LocalVar%FA_Acc, LocalVar%FA_AccHPF, LocalVar%FA_AccHPFI, LocalVar%PitCom + END IF + + IF (CntrPar%LoggingLevel > 1) THEN + WRITE (UnDb2,FmtDat) LocalVar%Time, avrSWAP(1:85) + END IF + END IF + + IF (MODULO(LocalVar%Time, 10.0) == 0.0) THEN + !LocalVar%TestType = LocalVar%TestType + 10 + !PRINT *, LocalVar%TestType + END IF + END SUBROUTINE Debug + !------------------------------------------------------------------------------------------------------------------------------- + !The Coleman or d-q axis transformation transforms the root out of plane bending moments of each turbine blade + !to a direct axis and a quadrature axis + SUBROUTINE ColemanTransform(rootMOOP, aziAngle, nHarmonic, axTOut, axYOut) + !............................................................................................................................... - IMPLICIT NONE + IMPLICIT NONE - ! Inputs + ! Inputs - REAL(4), INTENT(IN) :: rootMOOP(3) ! Root out of plane bending moments of each blade - REAL(4), INTENT(IN) :: aziAngle ! Rotor azimuth angle + REAL(4), INTENT(IN) :: rootMOOP(3) ! Root out of plane bending moments of each blade + REAL(4), INTENT(IN) :: aziAngle ! Rotor azimuth angle INTEGER(4), INTENT(IN) :: nHarmonic ! The harmonic number, nP - ! Outputs + ! Outputs - REAL(4), INTENT(OUT) :: axTOut, axYOut ! Direct axis and quadrature axis outputted by this transform + REAL(4), INTENT(OUT) :: axTOut, axYOut ! Direct axis and quadrature axis outputted by this transform - ! Local + ! Local - REAL(4), PARAMETER :: phi2 = 2.0/3.0*PI ! Phase difference from first to second blade - REAL(4), PARAMETER :: phi3 = 4.0/3.0*PI ! Phase difference from first to third blade + REAL(4), PARAMETER :: phi2 = 2.0/3.0*PI ! Phase difference from first to second blade + REAL(4), PARAMETER :: phi3 = 4.0/3.0*PI ! Phase difference from first to third blade - ! Body + ! Body - axTOut = 2.0/3.0 * (cos(nHarmonic*(aziAngle))*rootMOOP(1) + cos(nHarmonic*(aziAngle+phi2))*rootMOOP(2) + cos(nHarmonic*(aziAngle+phi3))*rootMOOP(3)) - axYOut = 2.0/3.0 * (sin(nHarmonic*(aziAngle))*rootMOOP(1) + sin(nHarmonic*(aziAngle+phi2))*rootMOOP(2) + sin(nHarmonic*(aziAngle+phi3))*rootMOOP(3)) - - END SUBROUTINE ColemanTransform - !------------------------------------------------------------------------------------------------------------------------------- - !The inverse Coleman or d-q axis transformation transforms the direct axis and quadrature axis - !back to root out of plane bending moments of each turbine blade - SUBROUTINE ColemanTransformInverse(axTIn, axYIn, aziAngle, nHarmonic, aziOffset, PitComIPC) - !............................................................................................................................... + axTOut = 2.0/3.0 * (cos(nHarmonic*(aziAngle))*rootMOOP(1) + cos(nHarmonic*(aziAngle+phi2))*rootMOOP(2) + cos(nHarmonic*(aziAngle+phi3))*rootMOOP(3)) + axYOut = 2.0/3.0 * (sin(nHarmonic*(aziAngle))*rootMOOP(1) + sin(nHarmonic*(aziAngle+phi2))*rootMOOP(2) + sin(nHarmonic*(aziAngle+phi3))*rootMOOP(3)) + + END SUBROUTINE ColemanTransform + !------------------------------------------------------------------------------------------------------------------------------- + !The inverse Coleman or d-q axis transformation transforms the direct axis and quadrature axis + !back to root out of plane bending moments of each turbine blade + SUBROUTINE ColemanTransformInverse(axTIn, axYIn, aziAngle, nHarmonic, aziOffset, PitComIPC) + !............................................................................................................................... - IMPLICIT NONE + IMPLICIT NONE - ! Inputs + ! Inputs - REAL(4), INTENT(IN) :: axTIn, axYIn ! Direct axis and quadrature axis - REAL(4), INTENT(IN) :: aziAngle ! Rotor azimuth angle - REAL(4), INTENT(IN) :: aziOffset ! Phase shift added to the azimuth angle - INTEGER(4), INTENT(IN) :: nHarmonic ! The harmonic number, nP + REAL(4), INTENT(IN) :: axTIn, axYIn ! Direct axis and quadrature axis + REAL(4), INTENT(IN) :: aziAngle ! Rotor azimuth angle + REAL(4), INTENT(IN) :: aziOffset ! Phase shift added to the azimuth angle + INTEGER(4), INTENT(IN) :: nHarmonic ! The harmonic number, nP - ! Outputs + ! 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 + ! Local - REAL(4), PARAMETER :: phi2 = 2.0/3.0*PI ! Phase difference from first to second blade - REAL(4), PARAMETER :: phi3 = 4.0/3.0*PI ! Phase difference from first to third blade + REAL(4), PARAMETER :: phi2 = 2.0/3.0*PI ! Phase difference from first to second blade + REAL(4), PARAMETER :: phi3 = 4.0/3.0*PI ! Phase difference from first to third blade - ! Body + ! Body - PitComIPC(1) = cos(nHarmonic*(aziAngle+aziOffset))*axTIn + sin(nHarmonic*(aziAngle+aziOffset))*axYIn - PitComIPC(2) = cos(nHarmonic*(aziAngle+aziOffset+phi2))*axTIn + sin(nHarmonic*(aziAngle+aziOffset+phi2))*axYIn - PitComIPC(3) = cos(nHarmonic*(aziAngle+aziOffset+phi3))*axTIn + sin(nHarmonic*(aziAngle+aziOffset+phi3))*axYIn + PitComIPC(1) = cos(nHarmonic*(aziAngle+aziOffset))*axTIn + sin(nHarmonic*(aziAngle+aziOffset))*axYIn + PitComIPC(2) = cos(nHarmonic*(aziAngle+aziOffset+phi2))*axTIn + sin(nHarmonic*(aziAngle+aziOffset+phi2))*axYIn + PitComIPC(3) = cos(nHarmonic*(aziAngle+aziOffset+phi3))*axTIn + sin(nHarmonic*(aziAngle+aziOffset+phi3))*axYIn - END SUBROUTINE ColemanTransformInverse - !------------------------------------------------------------------------------------------------------------------------------- - !Paremeterized Cp(lambda) function for a fixed pitch angle. Circumvents the need of importing a look-up table - REAL FUNCTION CPfunction(CP, lambda) - IMPLICIT NONE - - ! Inputs - REAL(4), INTENT(IN) :: CP(4) ! Parameters defining the parameterizable Cp(lambda) function - REAL(4), INTENT(IN) :: lambda ! Estimated or measured tip-speed ratio input + END SUBROUTINE ColemanTransformInverse + !------------------------------------------------------------------------------------------------------------------------------- + !Paremeterized Cp(lambda) function for a fixed pitch angle. Circumvents the need of importing a look-up table + REAL FUNCTION CPfunction(CP, lambda) + IMPLICIT NONE + + ! Inputs + REAL(4), INTENT(IN) :: CP(4) ! Parameters defining the parameterizable Cp(lambda) function + REAL(4), INTENT(IN) :: lambda ! Estimated or measured tip-speed ratio input + + CPfunction = exp(-CP(1)/lambda)*(CP(2)/lambda-CP(3))+CP(4)*lambda + CPfunction = saturate(CPfunction, 0.001, 1.0) - CPfunction = exp(-CP(1)/lambda)*(CP(2)/lambda-CP(3))+CP(4)*lambda - CPfunction = saturate(CPfunction, 0.001, 1.0) - - END FUNCTION CPfunction - !------------------------------------------------------------------------------------------------------------------------------- - !Function for computing the aerodynamic torque, divided by the effective rotor torque of the turbine, for use in wind speed estimation - REAL FUNCTION AeroDynTorque(LocalVar, CntrPar) - USE DRC_Types, ONLY : LocalVariables, ControlParameters - IMPLICIT NONE + END FUNCTION CPfunction + !------------------------------------------------------------------------------------------------------------------------------- + !Function for computing the aerodynamic torque, divided by the effective rotor torque of the turbine, for use in wind speed estimation + REAL FUNCTION AeroDynTorque(LocalVar, CntrPar) + USE DRC_Types, ONLY : LocalVariables, ControlParameters + IMPLICIT NONE - ! Inputs - TYPE(ControlParameters), INTENT(IN) :: CntrPar - TYPE(LocalVariables), INTENT(IN) :: LocalVar - - ! Local - REAL(4) :: RotorArea - REAL(4) :: Cp + ! Inputs + TYPE(ControlParameters), INTENT(IN) :: CntrPar + TYPE(LocalVariables), INTENT(IN) :: LocalVar + + ! Local + REAL(4) :: RotorArea + REAL(4) :: Cp REAL(4) :: Lambda - - RotorArea = PI*CntrPar%WE_BladeRadius**2 - Lambda = LocalVar%RotSpeed*CntrPar%WE_BladeRadius/LocalVar%WE_Vw - Cp = CPfunction(CntrPar%WE_CP, Lambda) - - AeroDynTorque = 0.5*(CntrPar%WE_RhoAir*RotorArea)*(LocalVar%WE_Vw**3/LocalVar%RotSpeed)*Cp - AeroDynTorque = MAX(AeroDynTorque, 0.0) - - END FUNCTION AeroDynTorque - !------------------------------------------------------------------------------------------------------------------------------- - SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar) - USE DRC_Types, ONLY : LocalVariables, ControlParameters - IMPLICIT NONE + + RotorArea = PI*CntrPar%WE_BladeRadius**2 + Lambda = LocalVar%RotSpeed*CntrPar%WE_BladeRadius/LocalVar%WE_Vw + Cp = CPfunction(CntrPar%WE_CP, Lambda) + + AeroDynTorque = 0.5*(CntrPar%WE_RhoAir*RotorArea)*(LocalVar%WE_Vw**3/LocalVar%RotSpeed)*Cp + AeroDynTorque = MAX(AeroDynTorque, 0.0) + + END FUNCTION AeroDynTorque + !------------------------------------------------------------------------------------------------------------------------------- + SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar) + USE DRC_Types, ONLY : LocalVariables, ControlParameters + IMPLICIT NONE ! Inputs - TYPE(ControlParameters), INTENT(IN) :: CntrPar - TYPE(LocalVariables), INTENT(INOUT) :: LocalVar - + TYPE(ControlParameters), INTENT(IN) :: CntrPar + TYPE(LocalVariables), INTENT(INOUT) :: LocalVar + ! Body LocalVar%WE_VwIdot = CntrPar%WE_Gamma/CntrPar%WE_Jtot*(LocalVar%VS_LastGenTrq*CntrPar%WE_GearboxRatio - AeroDynTorque(LocalVar, CntrPar)) LocalVar%WE_VwI = LocalVar%WE_VwI + LocalVar%WE_VwIdot*LocalVar%DT LocalVar%WE_Vw = LocalVar%WE_VwI + CntrPar%WE_Gamma*LocalVar%RotSpeed - END SUBROUTINE WindSpeedEstimator - !------------------------------------------------------------------------------------------------------------------------------- + END SUBROUTINE WindSpeedEstimator + !------------------------------------------------------------------------------------------------------------------------------- END MODULE Functions diff --git a/Source/ReadSetParameters.f90 b/Source/ReadSetParameters.f90 index 107d9a81..75ea9f6b 100644 --- a/Source/ReadSetParameters.f90 +++ b/Source/ReadSetParameters.f90 @@ -149,8 +149,8 @@ SUBROUTINE ComputeVariablesSetpoints(CntrPar, LocalVar) LocalVar%PC_PwrErr = CntrPar%VS_RtPwr - LocalVar%VS_GenPwr ! Power error ! XXX - 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 + 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 END SUBROUTINE ComputeVariablesSetpoints SUBROUTINE ReadAvrSWAP(avrSWAP, LocalVar) From b690f1bf3c2c942f11c1692d12ae45e75cc24221 Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Thu, 4 Jul 2019 09:38:02 -0600 Subject: [PATCH 2/7] Updated for input file cleanup --- Parameter_files/NREL5MW/DISCON.IN | 60 ++++++++++++------ Source/ReadSetParameters.f90 | 102 ++++++++++++++++-------------- 2 files changed, 97 insertions(+), 65 deletions(-) diff --git a/Parameter_files/NREL5MW/DISCON.IN b/Parameter_files/NREL5MW/DISCON.IN index 7e84bad9..0c7af627 100644 --- a/Parameter_files/NREL5MW/DISCON.IN +++ b/Parameter_files/NREL5MW/DISCON.IN @@ -1,18 +1,23 @@ +! Turbine: NREL 5MW fixed bottom wind turbine +! NJA - Might want to make a more formal header for this + +!------- DEBUG ------------------------------------------------------------ 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_LPFType - 1 = first-order low-pass filter, 2 = second-order low-pass filter -0 ! F_NotchType - 0 = disable, 1 = enable: notch on the measured generator speed, -1.570796326 ! F_LPFCornerFreq - Corner frequency (-3dB point) in the low-pass filters, filtering generator speed and pitch control signals, [rad/s] -0 ! F_LPFDamping - Damping coefficient if F_FilterType = 2, unused otherwise + +!------- CONTROLLER FLAGS ------------------------------------------------- +1 ! F_LPFType - 1 = first-order low-pass filter, 2 = second-order low-pass filter, filtering generator speed and pitch control signals, [rad/s] +0 ! F_NotchType - 0 = disable, 1 = enable: notch on the measured generator speed +0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) 0 = off / 1 = (1P reductions) / 2 = (1P+2P reductions) +1 ! VS_ControlMode - Generator torque control mode in above rated conditions, 0 = constant torque / 1 = constant power +0 ! Y_ControlMode - Yaw control mode: (0 = no yaw control, 1 = yaw rate control, 2 = yaw-by-IPC) + +!------- FILTERS ---------------------------------------------------------- +1.570796326 ! F_LPFCornerFreq - Corner frequency (-3dB point) in the low-pass filters +0 ! F_LPFDamping - Damping coefficient if F_FilterType = 2, unused otherwise 0 ! F_NotchCornerFreq - Natural frequency of the notch filter, [rad/s] 0 0 ! F_NotchBetaNumDen - These two notch damping values (numerator and denominator) determines the width and depth of the notch -0.1 ! FA_HPF_LPFCornerFreq - Corner frequency (-3dB point) in the high-pass filter on the fore-aft acceleration signal [rad/s] -0.087266 ! FA_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from FA damper), [rad] --1 ! FA_KI - Integral gain for the fore-aft tower damper controller, -1 = off / >0 = on [rad s/m] -0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) 0 = off / 1 = (1P reductions) / 2 = (1P+2P reductions) -0.087266 ! IPC_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from IPC), [rad] -1E-8 0 ! IPC_KI - Integral gain for the individual pitch controller: first parameter for 1P reductions, second for 2P reductions [-] -0.436332313 0 ! IPC_aziOffset - Phase offset added to the azimuth angle for the individual pitch controller, [rad]. -2.5 ! IPC_CornerFreqAct - Corner frequency of the first-order actuators model, to induce a phase lag in the IPC signal. Set 0 to disable. [rad/s] + +!------- BLADE PITCH CONTROL ---------------------------------------------- 14 ! PC_GS_n - Amount of gain-scheduling table entries 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 @@ -26,20 +31,32 @@ 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.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 ! Z_EnableSine - Enable/disable sine pitch excitation, used to validate for dynamic induction control, will be removed later [-] +0.0349066 ! Z_PitchAmplitude - Amplitude of sine pitch excitation [rad] +0 ! Z_PitchFrequency - Frequency of sine pitch excitation [rad/s] + +!------- INDIVIDUAL PITCH CONTROL ----------------------------------------- +0.087266 ! IPC_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from IPC), [rad] +1E-8 0 ! IPC_KI - Integral gain for the individual pitch controller: first parameter for 1P reductions, second for 2P reductions [-] +0.436332313 0 ! IPC_aziOffset - Phase offset added to the azimuth angle for the individual pitch controller, [rad]. +2.5 ! IPC_CornerFreqAct - Corner frequency of the first-order actuators model, to induce a phase lag in the IPC signal. Set 0 to disable. [rad/s] + +!------- VS TORQUE CONTROL ------------------------------------------------ 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] 150000.0 ! VS_MaxRat - Maximum torque rate (in absolute value) in torque controller, [Nm/s]. 48000.00 ! VS_MaxTq - Maximum generator torque in Region 3 (HSS side), [Nm]. 0.0 ! VS_MinTq - Minimum generator (HSS side), [Nm]. 91.2109 ! VS_MinOMSpd - Optimal mode minimum speed, cut-in speed towards optimal mode gain path [rad/s] -2.33228 ! VS_Rgn2K - Generator torque constant in Region 2 (HSS side), N-m/(rad/s)^2 +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]. 120.113 ! VS_RefSpd - Rated generator speed [rad/s] -1 ! VS_n - Number of controller gains +1 ! VS_n - Number of generator PI torque 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] + +!------- WIND SPEED ESTIMATOR --------------------------------------------- 63.0 ! WE_BladeRadius - Blade length [m] 4 ! WE_CP_n - Amount of parameters in the Cp array 14.571319658214513 42.809556250371465 2.456512501523107 0.003127994078720 ! WE_CP - Parameters that define the parameterized CP(lambda) function @@ -47,7 +64,8 @@ 97 ! WE_GearboxRatio - Gearbox ratio, >=1 [-] 4.0469564E+07 ! WE_Jtot - Total drivetrain inertia, including blades, hub and casted generator inertia to LSS [kg m^2] 1.225 ! WE_RhoAir - Air density [kg m^-3] -0 ! Y_ControlMode - Yaw control mode: (0 = no yaw control, 1 = yaw rate control, 2 = yaw-by-IPC) + +!------- YAW CONTROL ------------------------------------------------------ 1.745329252 ! Y_ErrThresh - Yaw error threshold. Turbine begins to yaw when it passes this. [rad^2 s] 0.17453 ! Y_IPC_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from yaw-by-IPC), [rad] 1 ! Y_IPC_n - Number of controller gains (yaw-by-IPC) @@ -59,6 +77,10 @@ 1.0 ! Y_omegaLPFast - Corner frequency fast low pass filter, 1.0 [Hz] 0.016667 ! Y_omegaLPSlow - Corner frequency slow low pass filter, 1/60 [Hz] 0.0034906 ! Y_Rate - Yaw rate [rad/s] -0 ! Z_EnableSine - Enable/disable sine pitch excitation, used to validate for dynamic induction control, will be removed later [-] -0.0349066 ! Z_PitchAmplitude - Amplitude of sine pitch excitation [rad] -0 ! Z_PitchFrequency - Frequency of sine pitch excitation [rad/s] \ No newline at end of file + +!------- TOWER FORE-AFT DAMPING ------------------------------------------- +-1 ! FA_KI - Integral gain for the fore-aft tower damper controller, -1 = off / >0 = on [rad s/m] - !NJA - Make this a flag +0.1 ! FA_HPF_CornerFreq - Corner frequency (-3dB point) in the high-pass filter on the fore-aft acceleration signal [rad/s] +0.087266 ! FA_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from FA damper), [rad] + + \ No newline at end of file diff --git a/Source/ReadSetParameters.f90 b/Source/ReadSetParameters.f90 index 75ea9f6b..688b23d2 100644 --- a/Source/ReadSetParameters.f90 +++ b/Source/ReadSetParameters.f90 @@ -15,51 +15,47 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar) OPEN(unit=UnControllerParameters, file='DISCON.IN', status='old', action='read') - !------------------- GENERAL CONSTANTS ------------------- + !----------------------- HEADER ------------------------ + READ(UnControllerParameters, *) + READ(UnControllerParameters, *) + READ(UnControllerParameters, *) + + !----------------------- DEBUG -------------------------- + READ(UnControllerParameters, *) READ(UnControllerParameters, *) CntrPar%LoggingLevel + READ(UnControllerParameters, *) + + !----------------- CONTROLLER FLAGS --------------------- + READ(UnControllerParameters, *) READ(UnControllerParameters, *) CntrPar%F_LPFType READ(UnControllerParameters, *) CntrPar%F_NotchType + READ(UnControllerParameters, *) CntrPar%IPC_ControlMode + READ(UnControllerParameters, *) CntrPar%VS_ControlMode + READ(UnControllerParameters, *) CntrPar%Y_ControlMode + READ(UnControllerParameters, *) + + !----------------- FILTER CONSTANTS --------------------- + READ(UnControllerParameters, *) READ(UnControllerParameters, *) CntrPar%F_LPFCornerFreq READ(UnControllerParameters, *) CntrPar%F_LPFDamping READ(UnControllerParameters, *) CntrPar%F_NotchCornerFreq - ALLOCATE(CntrPar%F_NotchBetaNumDen(2)) READ(UnControllerParameters,*) CntrPar%F_NotchBetaNumDen - - !------------ FORE-AFT TOWER DAMPER CONSTANTS ------------ - READ(UnControllerParameters, *) CntrPar%FA_HPFCornerFreq - READ(UnControllerParameters, *) CntrPar%FA_IntSat - READ(UnControllerParameters, *) CntrPar%FA_KI - - !------------------- IPC CONSTANTS ----------------------- - READ(UnControllerParameters, *) CntrPar%IPC_ControlMode - READ(UnControllerParameters, *) CntrPar%IPC_IntSat - - ALLOCATE(CntrPar%IPC_KI(2)) - READ(UnControllerParameters,*) CntrPar%IPC_KI - - ALLOCATE(CntrPar%IPC_aziOffset(2)) - READ(UnControllerParameters,*) CntrPar%IPC_aziOffset - - READ(UnControllerParameters, *) CntrPar%IPC_CornerFreqAct - - !------------------- PITCH CONSTANTS ----------------------- + READ(UnControllerParameters, *) + + !----------- BLADE PITCH CONTROLLER CONSTANTS ----------- + READ(UnControllerParameters, *) READ(UnControllerParameters, *) CntrPar%PC_GS_n ALLOCATE(CntrPar%PC_GS_angles(CntrPar%PC_GS_n)) READ(UnControllerParameters,*) CntrPar%PC_GS_angles - ALLOCATE(CntrPar%PC_GS_KP(CntrPar%PC_GS_n)) READ(UnControllerParameters,*) CntrPar%PC_GS_KP - ALLOCATE(CntrPar%PC_GS_KI(CntrPar%PC_GS_n)) READ(UnControllerParameters,*) CntrPar%PC_GS_KI - ALLOCATE(CntrPar%PC_GS_KD(CntrPar%PC_GS_n)) READ(UnControllerParameters,*) CntrPar%PC_GS_KD - ALLOCATE(CntrPar%PC_GS_TF(CntrPar%PC_GS_n)) READ(UnControllerParameters,*) CntrPar%PC_GS_TF - READ(UnControllerParameters, *) CntrPar%PC_MaxPit READ(UnControllerParameters, *) CntrPar%PC_MinPit READ(UnControllerParameters, *) CntrPar%PC_MaxRat @@ -67,9 +63,24 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar) READ(UnControllerParameters, *) CntrPar%PC_RefSpd READ(UnControllerParameters, *) CntrPar%PC_FinePit READ(UnControllerParameters, *) CntrPar%PC_Switch - - !------------------- TORQUE CONSTANTS ----------------------- - READ(UnControllerParameters, *) CntrPar%VS_ControlMode + !-- sine pitch excitiation -- + READ(UnControllerParameters, *) CntrPar%Z_EnableSine + READ(UnControllerParameters, *) CntrPar%Z_PitchAmplitude + READ(UnControllerParameters, *) CntrPar%Z_PitchFrequency + READ(UnControllerParameters, *) + + !------------------- IPC CONSTANTS ----------------------- + READ(UnControllerParameters, *) + READ(UnControllerParameters, *) CntrPar%IPC_IntSat + ALLOCATE(CntrPar%IPC_KI(2)) + READ(UnControllerParameters,*) CntrPar%IPC_KI + ALLOCATE(CntrPar%IPC_aziOffset(2)) + READ(UnControllerParameters,*) CntrPar%IPC_aziOffset + READ(UnControllerParameters, *) CntrPar%IPC_CornerFreqAct + READ(UnControllerParameters, *) + + !------------ VS TORQUE CONTROL CONSTANTS ---------------- + READ(UnControllerParameters, *) READ(UnControllerParameters, *) CntrPar%VS_GenEff READ(UnControllerParameters, *) CntrPar%VS_ArSatTq READ(UnControllerParameters, *) CntrPar%VS_MaxRat @@ -81,49 +92,48 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar) READ(UnControllerParameters, *) CntrPar%VS_RtTq READ(UnControllerParameters, *) CntrPar%VS_RefSpd READ(UnControllerParameters, *) CntrPar%VS_n - ALLOCATE(CntrPar%VS_KP(CntrPar%VS_n)) READ(UnControllerParameters,*) CntrPar%VS_KP - ALLOCATE(CntrPar%VS_KI(CntrPar%VS_n)) READ(UnControllerParameters,*) CntrPar%VS_KI - - !-------------- WIND SPEED ESTIMATOR CONTANTS ------------------ + READ(UnControllerParameters, *) + + !------------ WIND SPEED ESTIMATOR CONTANTS -------------- + READ(UnControllerParameters, *) READ(UnControllerParameters, *) CntrPar%WE_BladeRadius READ(UnControllerParameters, *) CntrPar%WE_CP_n - ALLOCATE(CntrPar%WE_CP(CntrPar%WE_CP_n)) READ(UnControllerParameters, *) CntrPar%WE_CP - READ(UnControllerParameters, *) CntrPar%WE_Gamma READ(UnControllerParameters, *) CntrPar%WE_GearboxRatio READ(UnControllerParameters, *) CntrPar%WE_Jtot READ(UnControllerParameters, *) CntrPar%WE_RhoAir - - !------------------- YAW CONSTANTS ----------------------- - READ(UnControllerParameters, *) CntrPar%Y_ControlMode + READ(UnControllerParameters, *) + + !-------------- YAW CONTROLLER CONSTANTS ----------------- + READ(UnControllerParameters, *) READ(UnControllerParameters, *) CntrPar%Y_ErrThresh READ(UnControllerParameters, *) CntrPar%Y_IPC_IntSat READ(UnControllerParameters, *) CntrPar%Y_IPC_n - ALLOCATE(CntrPar%Y_IPC_KP(CntrPar%Y_IPC_n)) READ(UnControllerParameters,*) CntrPar%Y_IPC_KP - ALLOCATE(CntrPar%Y_IPC_KI(CntrPar%Y_IPC_n)) READ(UnControllerParameters,*) CntrPar%Y_IPC_KI - READ(UnControllerParameters, *) CntrPar%Y_IPC_omegaLP READ(UnControllerParameters, *) CntrPar%Y_IPC_zetaLP - READ(UnControllerParameters, *) CntrPar%Y_MErrSet READ(UnControllerParameters, *) CntrPar%Y_omegaLPFast READ(UnControllerParameters, *) CntrPar%Y_omegaLPSlow READ(UnControllerParameters, *) CntrPar%Y_Rate + READ(UnControllerParameters, *) + + !------------ FORE-AFT TOWER DAMPER CONSTANTS ------------ + READ(UnControllerParameters, *) + READ(UnControllerParameters, *) CntrPar%FA_KI + READ(UnControllerParameters, *) CntrPar%FA_HPFCornerFreq + READ(UnControllerParameters, *) CntrPar%FA_IntSat - !------------------- SINE PITCH EXCITATION ----------------------- - READ(UnControllerParameters, *) CntrPar%Z_EnableSine - READ(UnControllerParameters, *) CntrPar%Z_PitchAmplitude - READ(UnControllerParameters, *) CntrPar%Z_PitchFrequency + ! END OF INPUT FILE !------------------- CALCULATED CONSTANTS ----------------------- CntrPar%PC_RtTq99 = CntrPar%VS_RtTq*0.99 From 007d5da727b76c1530e270cd94ffb4ce053a7426 Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Fri, 5 Jul 2019 11:07:04 -0600 Subject: [PATCH 3/7] Included command code for -i --- Scripts/compileDISCON_linux.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Scripts/compileDISCON_linux.bash b/Scripts/compileDISCON_linux.bash index c2cfc51f..6b80db49 100644 --- a/Scripts/compileDISCON_linux.bash +++ b/Scripts/compileDISCON_linux.bash @@ -3,16 +3,16 @@ mkdir ../DISCON cd ../Source # Set options to 64 bits -sed -i '13s/.*/#BITS = 32/' makefile -sed -i '14s/.*/BITS = 64/' makefile +# sed -i '' 13s/.*/#BITS = 32/' makefile +sed -i '' '14s/.*/BITS = 64/' makefile # Build code rm -f ../DISCON/DISCON_glin64.so rm -f ../DISCON_x64_DRC.so make clean make all -mv ../DISCON/DISCON_glin64.so ../DISCON_x64_DRC.so -echo 'The output file is: "../DISCON_x64_DRC.so".' +# cp ../DISCON/DISCON_glin64.so ../DISCON_x64_DRC.so +# echo 'The output file is: "../DISCON_x64_DRC.so".' # Return to initial directory cd ../Scripts From 11f184b593c3f6bb5de48a85400364a5783aa15d Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Fri, 5 Jul 2019 11:21:57 -0600 Subject: [PATCH 4/7] Updates for temp files, 64 bit unix machines --- .gitignore | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f4570544..78cca585 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,17 @@ +# Gitignore ~$README.docx ControllerSchematic.pptx + +# Compiled code, unnecessary for git Source/Obj_win32 +Source/Obj_lin64 *.mod Source/x64/ Source/Release/ -#Archive + +# Archive Scripts/CompileDISCONHereCopyRun\.cmd \.vs/ + +# Temp Files +*~ From cbbb097398e2e7516de9146a43e71c80f28a8610 Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Fri, 5 Jul 2019 11:23:51 -0600 Subject: [PATCH 5/7] Included command code for -i --- Scripts/compileDISCON_linux.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/compileDISCON_linux.bash b/Scripts/compileDISCON_linux.bash index 6b80db49..d056a1b6 100644 --- a/Scripts/compileDISCON_linux.bash +++ b/Scripts/compileDISCON_linux.bash @@ -3,7 +3,7 @@ mkdir ../DISCON cd ../Source # Set options to 64 bits -# sed -i '' 13s/.*/#BITS = 32/' makefile +sed -i '' '13s/.*/#BITS = 32/' makefile sed -i '' '14s/.*/BITS = 64/' makefile # Build code From 9711b7839c85a6c3edd98fda1eda53cbbe5c348d Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Fri, 5 Jul 2019 11:27:16 -0600 Subject: [PATCH 6/7] Changed for 64 bit --- Source/makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/makefile b/Source/makefile index a09ee5d1..91bd486c 100644 --- a/Source/makefile +++ b/Source/makefile @@ -10,8 +10,8 @@ #================================================================================# # 32-bit or 64-bit? -BITS = 32 -#BITS = 64 +# BITS = 32 +BITS = 64 # Location of source files for the DLL. # You may need to change these for your DLL. From ebca4f2694764cac9d6ab98626fa34e2ae9083a9 Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Fri, 5 Jul 2019 11:30:03 -0600 Subject: [PATCH 7/7] Updated and compiled DTU 10MW Input file --- DISCON/DISCON_glin64.so | Bin 0 -> 47024 bytes Parameter_files/DTU10MW/DISCON.IN | 46 +++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 13 deletions(-) create mode 100755 DISCON/DISCON_glin64.so diff --git a/DISCON/DISCON_glin64.so b/DISCON/DISCON_glin64.so new file mode 100755 index 0000000000000000000000000000000000000000..01ffd347ac5689f812ef52afad34ed1a07c16d53 GIT binary patch literal 47024 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 literal 0 HcmV?d00001 diff --git a/Parameter_files/DTU10MW/DISCON.IN b/Parameter_files/DTU10MW/DISCON.IN index 5c1c3d5b..929f3cae 100644 --- a/Parameter_files/DTU10MW/DISCON.IN +++ b/Parameter_files/DTU10MW/DISCON.IN @@ -1,18 +1,23 @@ +! Turbine: DTU fixed bottom wind turbine +! NJA - Might want to make a more formal header for this + +!------- DEBUG ------------------------------------------------------------ 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 + +!------- CONTROLLER FLAGS ------------------------------------------------- 2 ! F_LPFType - 1 = first-order low-pass filter, 2 = second-order low-pass filter 0 ! F_NotchType - 0 = disable, 1 = enable: notch on the measured generator speed, +0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) 0 = off / 1 = (1P reductions) / 2 = (1P+2P reductions) +1 ! VS_ControlMode - Generator torque control mode in above rated conditions, 0 = constant torque / 1 = constant power +0 ! Y_ControlMode - Yaw control mode: (0 = no yaw control, 1 = yaw rate control, 2 = yaw-by-IPC) + +!------- FILTERS ---------------------------------------------------------- 2.5132741 ! F_LPFCornerFreq - Corner frequency (-3dB point) in the low-pass filters, filtering generator speed and pitch control signals, [rad/s] 0.7 ! F_LPFDamping - Damping coefficient if F_FilterType = 2, unused otherwise 0 ! F_NotchCornerFreq - Natural frequency of the notch filter, [rad/s] 0 0 ! F_NotchBetaNumDen - These two notch damping values (numerator and denominator) determines the width and depth of the notch0.1 ! FA_HPF_LPFCornerFreq - Corner frequency (-3dB point) in the high-pass filter on the fore-aft acceleration signal [rad/s] -0.1 ! FA_HPF_LPFCornerFreq - Corner frequency (-3dB point) in the high-pass filter on the fore-aft acceleration signal [rad/s] -0.087266 ! FA_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from FA damper), [rad] --1 ! FA_KI - Integral gain for the fore-aft tower damper controller, -1 = off / >0 = on [rad s/m] -0 ! IPC_ControlMode - Turn Individual Pitch Control (IPC) for fatigue load reductions (pitch contribution) 0 = off / 1 = (1P reductions) / 2 = (1P+2P reductions) -0.087266 ! IPC_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from IPC), [rad] -8E-10 0 ! IPC_KI - Integral gain for the individual pitch controller: first parameter for 1P reductions, second for 2P reductions [-] -0.436332313 0 ! IPC_aziOffset - Phase offset added to the azimuth angle for the individual pitch controller, [rad]. -2.5 ! IPC_CornerFreqAct - Corner frequency of the first-order actuators model, to induce a phase lag in the IPC signal. Set 0 to disable. [rad/s] + +!------- BLADE PITCH CONTROL ---------------------------------------------- 13 ! PC_GS_n - Amount of gain-scheduling table entries 0 0.0349 0.0698 0.1047 0.1396 0.1745 0.2094 0.2443 0.2793 0.3142 0.3491 0.384 0.4189 ! PC_GS_angles - Gain-schedule table: pitch angles -0.0105 -0.0104 -0.0102 -0.0099 -0.0095 -0.0091 -0.0086 -0.0081 -0.0076 -0.0071 -0.0066 -0.0061 -0.0056 ! PC_GS_KP - Gain-schedule table: pitch controller kp gains @@ -26,7 +31,17 @@ 50.26548 ! 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] -1 ! VS_ControlMode - Generator torque control mode in above rated conditions, 0 = constant torque / 1 = constant power +0 ! Z_EnableSine - Enable/disable sine pitch excitation, used to validate for dynamic induction control, will be removed later [-] +0.0349066 ! Z_PitchAmplitude - Amplitude of sine pitch excitation [rad] +0 ! Z_PitchFrequency - Frequency of sine pitch excitation [rad/s] + +!------- INDIVIDUAL PITCH CONTROL ----------------------------------------- +0.087266 ! IPC_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from IPC), [rad] +8E-10 0 ! IPC_KI - Integral gain for the individual pitch controller: first parameter for 1P reductions, second for 2P reductions [-] +0.436332313 0 ! IPC_aziOffset - Phase offset added to the azimuth angle for the individual pitch controller, [rad]. +2.5 ! IPC_CornerFreqAct - Corner frequency of the first-order actuators model, to induce a phase lag in the IPC signal. Set 0 to disable. [rad/s] + +!------- VS TORQUE CONTROL ------------------------------------------------ 0.944 ! VS_GenEff - Generator efficiency mechanical power -> electrical power, this should match the efficiency defined in the generator properties! [-] 212900 ! VS_ArSatTq - Above rated generator torque PI control saturation, [Nm] 150000.0 ! VS_MaxRat - Maximum torque rate (in absolute value) in torque controller, [Nm/s]. @@ -40,6 +55,8 @@ 1 ! VS_n - Number of controller gains -27338.24 ! VS_KP - Proportional gain for generator PI torque controller, used in the transitional 2.5 region, [1/(rad/s) Nm] -6134.68 ! VS_KI - Integral gain for generator PI torque controller, used in the transitional 2.5 region, [1/rad Nm] + +!------- WIND SPEED ESTIMATOR --------------------------------------------- 89.166 ! WE_BladeRadius - Blade length [m] 4 ! WE_CP_n - Amount of parameters in the Cp array 14.571319658214513 42.809556250371465 2.456512501523107 0.003127994078720 ! WE_CP - Parameters that define the parameterized CP(lambda) function XXX Needs to be updated, these are values of the NREL5MW XXX @@ -47,7 +64,8 @@ 50 ! WE_GearboxRatio - Gearbox ratio, >=1 [-] 2.0E+08 ! WE_Jtot - Total drivetrain inertia, including blades, hub and casted generator inertia to LSS [kg m^2] 1.225 ! WE_RhoAir - Air density [kg m^-3] -0 ! Y_ControlMode - Yaw control mode: (0 = no yaw control, 1 = yaw rate control, 2 = yaw-by-IPC) + +!------- YAW CONTROL ------------------------------------------------------ 1.745329252 ! Y_ErrThresh - Yaw error threshold. Turbine begins to yaw when it passes this. [rad^2 s] 0.17453 ! Y_IPC_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from yaw-by-IPC), [rad] 1 ! Y_IPC_n - Number of controller gains (yaw-by-IPC) @@ -59,6 +77,8 @@ 1.0 ! Y_omegaLPFast - Corner frequency fast low pass filter, 1.0 [Hz] 0.016667 ! Y_omegaLPSlow - Corner frequency slow low pass filter, 1/60 [Hz] 0.0034906 ! Y_Rate - Yaw rate [rad/s] -0 ! Z_EnableSine - Enable/disable sine pitch excitation, used to validate for dynamic induction control, will be removed later [-] -0.0349066 ! Z_PitchAmplitude - Amplitude of sine pitch excitation [rad] -0 ! Z_PitchFrequency - Frequency of sine pitch excitation [rad/s] \ No newline at end of file + +!------- TOWER FORE-AFT DAMPING ------------------------------------------- +-1 ! FA_KI - Integral gain for the fore-aft tower damper controller, -1 = off / >0 = on [rad s/m] +0.1 ! FA_HPF_CornerFreq - Corner frequency (-3dB point) in the high-pass filter on the fore-aft acceleration signal [rad/s] +0.087266 ! FA_IntSat - Integrator saturation (maximum signal amplitude contribution to pitch from FA damper), [rad] \ No newline at end of file