From 9c523ddc802afb90497ba67aefc2055223e008d7 Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Mon, 29 Mar 2021 14:55:35 -0600 Subject: [PATCH 01/26] testing initial commit --- .github/workflows/CI_rosco.yml | 33 +++++++++++++++++++++++++++++++++ environment.yml | 3 +++ 2 files changed, 36 insertions(+) create mode 100644 .github/workflows/CI_rosco.yml create mode 100644 environment.yml diff --git a/.github/workflows/CI_rosco.yml b/.github/workflows/CI_rosco.yml new file mode 100644 index 00000000..00de1200 --- /dev/null +++ b/.github/workflows/CI_rosco.yml @@ -0,0 +1,33 @@ +name: CI_rosco-toolbox + +# We run CI on push commits on all branches +on: [push, pull_request] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + build: + name: Build (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: ["ubuntu-latest", "macOS-latest", "windows-latest"] + python-version: ["3.8"] + defaults: + run: + shell: bash -l {0} + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + # Install ROSCO toolbox + - name: Compile ROSCO + run: | + mkdir build + cd build + cmake .. + make install + diff --git a/environment.yml b/environment.yml new file mode 100644 index 00000000..bd96d34f --- /dev/null +++ b/environment.yml @@ -0,0 +1,3 @@ +channels: + - conda-forge + - defaults \ No newline at end of file From f20dcdbcc447fc90b578236fc55de588cf073fde Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Mon, 29 Mar 2021 15:06:24 -0600 Subject: [PATCH 02/26] update compilers --- .github/workflows/CI_rosco.yml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI_rosco.yml b/.github/workflows/CI_rosco.yml index 00de1200..473de780 100644 --- a/.github/workflows/CI_rosco.yml +++ b/.github/workflows/CI_rosco.yml @@ -15,16 +15,30 @@ jobs: python-version: ["3.8"] defaults: run: + if: true == contains( matrix.os, 'windows') + shell: pwsh + if: false == contains( matrix.os, 'windows') shell: bash -l {0} steps: - - name: Checkout repository and submodules + - name: Checkout repository uses: actions/checkout@v2 - with: - submodules: recursive + + # Install compilers + - name: Add dependencies ubuntu specific + if: false == contains( matrix.os, 'windows') + run: | + conda install -y compilers + + # Install compilers + - name: Add dependencies ubuntu specific + if: true == contains( matrix.os, 'windows') + run: | + conda install -y m2w64-toolchain # Install ROSCO toolbox - name: Compile ROSCO + shell: pwsh run: | mkdir build cd build From 05786d0cb8e738a3f1005825a60cbcd42297d3ae Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Mon, 29 Mar 2021 15:11:04 -0600 Subject: [PATCH 03/26] remove default shell --- .github/workflows/CI_rosco.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/CI_rosco.yml b/.github/workflows/CI_rosco.yml index 473de780..3cfed840 100644 --- a/.github/workflows/CI_rosco.yml +++ b/.github/workflows/CI_rosco.yml @@ -13,12 +13,6 @@ jobs: matrix: os: ["ubuntu-latest", "macOS-latest", "windows-latest"] python-version: ["3.8"] - defaults: - run: - if: true == contains( matrix.os, 'windows') - shell: pwsh - if: false == contains( matrix.os, 'windows') - shell: bash -l {0} steps: - name: Checkout repository @@ -33,6 +27,7 @@ jobs: # Install compilers - name: Add dependencies ubuntu specific if: true == contains( matrix.os, 'windows') + shell: bash -l {0} run: | conda install -y m2w64-toolchain From 64a754f80533210f1798d0701bc5997bb9fc63ce Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Mon, 29 Mar 2021 15:13:07 -0600 Subject: [PATCH 04/26] use conda environment --- .github/workflows/CI_rosco.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI_rosco.yml b/.github/workflows/CI_rosco.yml index 3cfed840..4212f7ec 100644 --- a/.github/workflows/CI_rosco.yml +++ b/.github/workflows/CI_rosco.yml @@ -18,20 +18,28 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - name: Setup environment + uses: conda-incubator/setup-miniconda@v2 + with: + miniconda-version: "latest" + channels: conda-forge, general + auto-update-conda: true + python-version: 3.8 + environment-file: environment.yml + # Install compilers - name: Add dependencies ubuntu specific if: false == contains( matrix.os, 'windows') run: | conda install -y compilers - # Install compilers - name: Add dependencies ubuntu specific if: true == contains( matrix.os, 'windows') shell: bash -l {0} run: | conda install -y m2w64-toolchain - # Install ROSCO toolbox + # Install ROSCO - name: Compile ROSCO shell: pwsh run: | From 9d3f2c07be5d2d5a8cd95088d9c80ad83613c923 Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Tue, 30 Mar 2021 09:42:13 -0600 Subject: [PATCH 05/26] mingw flag --- .github/workflows/CI_rosco.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/CI_rosco.yml b/.github/workflows/CI_rosco.yml index 4212f7ec..e20d87d9 100644 --- a/.github/workflows/CI_rosco.yml +++ b/.github/workflows/CI_rosco.yml @@ -28,23 +28,35 @@ jobs: environment-file: environment.yml # Install compilers - - name: Add dependencies ubuntu specific + - name: Add dependencies linux if: false == contains( matrix.os, 'windows') + shell: pwsh run: | conda install -y compilers - - name: Add dependencies ubuntu specific + - name: Add dependencies windows if: true == contains( matrix.os, 'windows') shell: bash -l {0} run: | conda install -y m2w64-toolchain - # Install ROSCO - - name: Compile ROSCO + # Install ROSCO linux + - name: Compile ROSCO linux + if: false == contains( matrix.os, 'windows') shell: pwsh run: | mkdir build cd build cmake .. make install + + # Install ROSCO windows + - name: Compile ROSCO windows + if: true == contains( matrix.os, 'windows') + shell: pwsh + run: | + mkdir build + cd build + cmake .. -G "MinGW Makefiles" + make install From e2eab812a79b0b72e63967c27c7632d9655e534a Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Tue, 30 Mar 2021 17:13:13 -0600 Subject: [PATCH 06/26] pr template - initial commit --- .github/pull_request_template.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..ed9db46f --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,20 @@ +## Description and Purpose + +## Type of change +What types of change is it? +_Select the appropriate type(s) that describe this PR_ + +- [ ] Bugfix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (non-backwards-compatible fix or feature) +- [ ] Code style update (formatting, renaming) +- [ ] Refactoring (no functional changes, no API changes) +- [ ] Documentation update +- [ ] Maintenance update +- [ ] Other (please describe) + +## Github issues addressed, if one exists + +## Examples/Testing, if applicable + + From 37838fb5c0af7970d61c754848cd547094122e09 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Mon, 5 Apr 2021 12:27:04 -0600 Subject: [PATCH 07/26] Add error variables, error catching for interp functions --- src/ControllerBlocks.f90 | 36 +++++++++--- src/Controllers.f90 | 24 +++++--- src/DISCON.F90 | 17 ++++-- src/Functions.f90 | 118 ++++++++++++++++++++++++++++++++------ src/ROSCO_Types.f90 | 10 ++++ src/ReadSetParameters.f90 | 35 ++++++----- 6 files changed, 185 insertions(+), 55 deletions(-) diff --git a/src/ControllerBlocks.f90 b/src/ControllerBlocks.f90 index d9613b92..4432ea44 100644 --- a/src/ControllerBlocks.f90 +++ b/src/ControllerBlocks.f90 @@ -101,11 +101,11 @@ SUBROUTINE StateMachine(CntrPar, LocalVar) END IF END SUBROUTINE StateMachine !------------------------------------------------------------------------------------------------------------------------------- - SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar) + SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar, ErrVar) ! Wind Speed Estimator estimates wind speed at hub height. Currently implements two types of estimators ! WE_Mode = 0, Filter hub height wind speed as passed from servodyn using first order low pass filter with 1Hz cornering frequency ! WE_Mode = 1, Use Inversion and Inveriance filter as defined by Ortege et. al. - USE ROSCO_Types, ONLY : LocalVariables, ControlParameters, ObjectInstances, PerformanceData, DebugVariables + USE ROSCO_Types, ONLY : LocalVariables, ControlParameters, ObjectInstances, PerformanceData, DebugVariables, ErrorVariables IMPLICIT NONE ! Inputs @@ -114,6 +114,8 @@ SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar) TYPE(ObjectInstances), INTENT(INOUT) :: objInst TYPE(PerformanceData), INTENT(INOUT) :: PerfData TYPE(DebugVariables), INTENT(INOUT) :: DebugVar + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar + ! Allocate Variables REAL(8) :: F_WECornerFreq ! Corner frequency (-3dB point) for first order low pass filter for measured hub height wind speed [Hz] @@ -152,7 +154,10 @@ SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar) ! Inversion and Invariance Filter implementation IF (CntrPar%WE_Mode == 1) THEN - LocalVar%WE_VwIdot = CntrPar%WE_Gamma/CntrPar%WE_Jtot*(LocalVar%VS_LastGenTrq*CntrPar%WE_GearboxRatio - AeroDynTorque(LocalVar, CntrPar, PerfData)) + ! Compute AeroDynTorque + Tau_r = AeroDynTorque(LocalVar, CntrPar, PerfData, ErrVar) + + LocalVar%WE_VwIdot = CntrPar%WE_Gamma/CntrPar%WE_Jtot*(LocalVar%VS_LastGenTrq*CntrPar%WE_GearboxRatio - Tau_r) LocalVar%WE_VwI = LocalVar%WE_VwI + LocalVar%WE_VwIdot*LocalVar%DT LocalVar%WE_Vw = LocalVar%WE_VwI + CntrPar%WE_Gamma*LocalVar%RotSpeedF @@ -180,11 +185,13 @@ SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar) ELSE ! Find estimated operating Cp and system pole - A_op = interp1d(CntrPar%WE_FOPoles_v,CntrPar%WE_FOPoles,v_h) + A_op = interp1d(CntrPar%WE_FOPoles_v,CntrPar%WE_FOPoles,v_h,ErrVar) ! TEST INTERP2D lambda = LocalVar%RotSpeed * CntrPar%WE_BladeRadius/v_h - Cp_op = interp2d(PerfData%Beta_vec,PerfData%TSR_vec,PerfData%Cp_mat, LocalVar%BlPitch(1)*R2D, lambda ) + + Cp_op = interp2d(PerfData%Beta_vec,PerfData%TSR_vec,PerfData%Cp_mat, LocalVar%BlPitch(1)*R2D, lambda, ErrVar) + Cp_op = max(0.0,Cp_op) ! Update Jacobian @@ -200,7 +207,7 @@ SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar) Q(3,3) = (2.0**2.0)/600.0 ! Prediction update - Tau_r = AeroDynTorque(LocalVar,CntrPar,PerfData) + Tau_r = AeroDynTorque(LocalVar, CntrPar, PerfData, ErrVar) a = PI * v_m/(2.0*L) dxh(1,1) = 1.0/CntrPar%WE_Jtot * (Tau_r - CntrPar%WE_GearboxRatio * LocalVar%VS_LastGenTrqF) dxh(2,1) = -a*v_t @@ -236,6 +243,11 @@ SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar) LocalVar%WE_Vw = LPFilter(LocalVar%HorWindV, LocalVar%DT, F_WECornerFreq, LocalVar%iStatus, .FALSE., objInst%instLPF) ENDIF + ! Error Catching + IF (ErrVar%aviFAIL == -1) THEN + ErrVar%ErrMsg = 'WindSpeedEstimator:'//TRIM(ErrVar%ErrMsg) + END IF + END SUBROUTINE WindSpeedEstimator !------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE SetpointSmoother(LocalVar, CntrPar, objInst) @@ -265,20 +277,26 @@ SUBROUTINE SetpointSmoother(LocalVar, CntrPar, objInst) END SUBROUTINE SetpointSmoother !------------------------------------------------------------------------------------------------------------------------------- - REAL FUNCTION PitchSaturation(LocalVar, CntrPar, objInst, DebugVar) + REAL FUNCTION PitchSaturation(LocalVar, CntrPar, objInst, DebugVar, ErrVar) ! PitchSaturation defines a minimum blade pitch angle based on a lookup table provided by DISCON.IN ! SS_Mode = 0, No setpoint smoothing ! SS_Mode = 1, Implement pitch saturation - USE ROSCO_Types, ONLY : LocalVariables, ControlParameters, ObjectInstances, DebugVariables + USE ROSCO_Types, ONLY : LocalVariables, ControlParameters, ObjectInstances, DebugVariables, ErrorVariables IMPLICIT NONE ! Inputs TYPE(ControlParameters), INTENT(IN) :: CntrPar TYPE(LocalVariables), INTENT(INOUT) :: LocalVar TYPE(ObjectInstances), INTENT(INOUT) :: objInst TYPE(DebugVariables), INTENT(INOUT) :: DebugVar + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Define minimum blade pitch angle as a function of estimated wind speed - PitchSaturation = interp1d(CntrPar%PS_WindSpeeds, CntrPar%PS_BldPitchMin, LocalVar%WE_Vw_F) + PitchSaturation = interp1d(CntrPar%PS_WindSpeeds, CntrPar%PS_BldPitchMin, LocalVar%WE_Vw_F, ErrVar) + + ! Error Catching + IF (ErrVar%aviFAIL == -1) THEN + ErrVar%ErrMsg = 'PitchSaturation:'//TRIM(ErrVar%ErrMsg) + END IF END FUNCTION PitchSaturation !------------------------------------------------------------------------------------------------------------------------------- diff --git a/src/Controllers.f90 b/src/Controllers.f90 index 48ddadc7..504fed56 100644 --- a/src/Controllers.f90 +++ b/src/Controllers.f90 @@ -31,7 +31,7 @@ MODULE Controllers CONTAINS !------------------------------------------------------------------------------------------------------------------------------- - SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar) + SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar, ErrVar) ! Blade pitch controller, generally maximizes rotor speed below rated (region 2) and regulates rotor speed above rated (region 3) ! PC_State = 0, fix blade pitch to fine pitch angle (PC_FinePit) ! PC_State = 1, is gain scheduled PI controller @@ -39,13 +39,15 @@ SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar) ! Individual pitch control ! Tower fore-aft damping ! Sine excitation on pitch - USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances, DebugVariables + USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances, DebugVariables, ErrorVariables ! Inputs TYPE(ControlParameters), INTENT(INOUT) :: CntrPar TYPE(LocalVariables), INTENT(INOUT) :: LocalVar TYPE(ObjectInstances), INTENT(INOUT) :: objInst - TYPE(DebugVariables), INTENT(INOUT) :: DebugVar + TYPE(DebugVariables), INTENT(INOUT) :: DebugVar + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar + ! Allocate Variables: REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from the DLL controller. INTEGER(4) :: K ! Index used for looping through blades. @@ -60,10 +62,10 @@ SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar) END IF ! Compute (interpolate) the gains based on previously commanded blade pitch angles and lookup table: - LocalVar%PC_KP = interp1d(CntrPar%PC_GS_angles, CntrPar%PC_GS_KP, LocalVar%PC_PitComTF) ! Proportional gain - LocalVar%PC_KI = interp1d(CntrPar%PC_GS_angles, CntrPar%PC_GS_KI, LocalVar%PC_PitComTF) ! Integral gain - LocalVar%PC_KD = interp1d(CntrPar%PC_GS_angles, CntrPar%PC_GS_KD, LocalVar%PC_PitComTF) ! Derivative gain - LocalVar%PC_TF = interp1d(CntrPar%PC_GS_angles, CntrPar%PC_GS_TF, LocalVar%PC_PitComTF) ! TF gains (derivative filter) !NJA - need to clarify + LocalVar%PC_KP = interp1d(CntrPar%PC_GS_angles, CntrPar%PC_GS_KP, LocalVar%PC_PitComTF, ErrVar) ! Proportional gain + LocalVar%PC_KI = interp1d(CntrPar%PC_GS_angles, CntrPar%PC_GS_KI, LocalVar%PC_PitComTF, ErrVar) ! Integral gain + LocalVar%PC_KD = interp1d(CntrPar%PC_GS_angles, CntrPar%PC_GS_KD, LocalVar%PC_PitComTF, ErrVar) ! Derivative gain + LocalVar%PC_TF = interp1d(CntrPar%PC_GS_angles, CntrPar%PC_GS_TF, LocalVar%PC_PitComTF, ErrVar) ! TF gains (derivative filter) !NJA - need to clarify ! Compute the collective pitch command associated with the proportional and integral gains: IF (LocalVar%iStatus == 0) THEN @@ -88,7 +90,7 @@ SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar) ! Pitch Saturation IF (CntrPar%PS_Mode == 1) THEN - LocalVar%PC_MinPit = PitchSaturation(LocalVar,CntrPar,objInst,DebugVar) + LocalVar%PC_MinPit = PitchSaturation(LocalVar,CntrPar,objInst,DebugVar, ErrVar) LocalVar%PC_MinPit = max(LocalVar%PC_MinPit, CntrPar%PC_FinePit) ELSE LocalVar%PC_MinPit = CntrPar%PC_FinePit @@ -124,6 +126,12 @@ SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar) avrSWAP(43) = LocalVar%PitCom(2) ! " avrSWAP(44) = LocalVar%PitCom(3) ! " avrSWAP(45) = LocalVar%PitCom(1) ! Use the command angle of blade 1 if using collective pitch + + ! Error Catching + IF (ErrVar%aviFAIL == -1) THEN + ErrVar%ErrMsg = 'PitchControl:'//TRIM(ErrVar%ErrMsg) + END IF + END SUBROUTINE PitchControl !------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE VariableSpeedControl(avrSWAP, CntrPar, LocalVar, objInst) diff --git a/src/DISCON.F90 b/src/DISCON.F90 index 7142e347..d5119a71 100644 --- a/src/DISCON.F90 +++ b/src/DISCON.F90 @@ -54,6 +54,7 @@ SUBROUTINE DISCON(avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG) BIND (C, NAME TYPE(ObjectInstances), SAVE :: objInst TYPE(PerformanceData), SAVE :: PerfData TYPE(DebugVariables), SAVE :: DebugVar +TYPE(ErrorVariables), SAVE :: ErrVar RootName = TRANSFER(avcOUTNAME, RootName) !------------------------------------------------------------------------------------------------------------------------------ @@ -61,22 +62,30 @@ SUBROUTINE DISCON(avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG) BIND (C, NAME !------------------------------------------------------------------------------------------------------------------------------ ! Read avrSWAP array into derived types/variables CALL ReadAvrSWAP(avrSWAP, LocalVar) -CALL SetParameters(avrSWAP, aviFAIL, accINFILE, ErrMsg, SIZE(avcMSG), CntrPar, LocalVar, objInst, PerfData) +CALL SetParameters(avrSWAP, accINFILE, SIZE(avcMSG), CntrPar, LocalVar, objInst, PerfData, ErrVar) CALL PreFilterMeasuredSignals(CntrPar, LocalVar, objInst) IF ((LocalVar%iStatus >= 0) .AND. (aviFAIL >= 0)) THEN ! Only compute control calculations if no error has occurred and we are not on the last time step - CALL WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar) + CALL WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar, ErrVar) CALL ComputeVariablesSetpoints(CntrPar, LocalVar, objInst) CALL StateMachine(CntrPar, LocalVar) CALL SetpointSmoother(LocalVar, CntrPar, objInst) CALL ComputeVariablesSetpoints(CntrPar, LocalVar, objInst) CALL VariableSpeedControl(avrSWAP, CntrPar, LocalVar, objInst) - CALL PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar) + CALL PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar, ErrVar) CALL YawRateControl(avrSWAP, CntrPar, LocalVar, objInst) CALL FlapControl(avrSWAP, CntrPar, LocalVar, objInst) CALL Debug(LocalVar, CntrPar, DebugVar, avrSWAP, RootName, SIZE(avcOUTNAME)) END IF -avcMSG = TRANSFER(TRIM(ErrMsg)//C_NULL_CHAR, avcMSG, SIZE(avcMSG)) +! Error Catching +IF (ErrVar%aviFAIL == -1) THEN + ErrVar%ErrMsg = 'ROSCO:'//TRIM(ErrVar%ErrMsg) + print * , TRIM(ErrVar%ErrMsg) +END IF +ErrMsg = ErrVar%ErrMsg +avcMSG = TRANSFER(TRIM(ErrVar%ErrMsg)//C_NULL_CHAR, avcMSG, SIZE(avcMSG)) +aviFAIL = ErrVar%aviFAIL + RETURN END SUBROUTINE DISCON diff --git a/src/Functions.f90 b/src/Functions.f90 index 9d0c330d..69799998 100644 --- a/src/Functions.f90 +++ b/src/Functions.f90 @@ -156,15 +156,40 @@ REAL(8) FUNCTION PIIController(error, error2, kp, ki, ki2, minValue, maxValue, D END FUNCTION PIIController !------------------------------------------------------------------------------------------------------------------------------- - REAL FUNCTION interp1d(xData, yData, xq) - ! interp1d 1-D interpolation (table lookup), xData should be monotonically increasing - + REAL FUNCTION interp1d(xData, yData, xq, ErrVar) + ! interp1d 1-D interpolation (table lookup), xData should be strictly increasing + + USE ROSCO_Types, ONLY : ErrorVariables IMPLICIT NONE + ! Inputs REAL(8), DIMENSION(:), INTENT(IN) :: xData ! Provided x data (vector), to be interpolated REAL(8), DIMENSION(:), INTENT(IN) :: yData ! Provided y data (vector), to be interpolated REAL(8), INTENT(IN) :: xq ! x-value for which the y value has to be interpolated INTEGER(4) :: I ! Iteration index + + ! Error Catching + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar + INTEGER(4) :: I_DIFF + + + ! Catch Errors + ! Are xData and yData the same size? + IF (SIZE(xData) .NE. SIZE(yData)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'interp1d: xData and yData are not the same size' + WRITE(ErrVar%ErrMsg,"(A,I2,A,I2,A)") "interp1d: SIZE(xData) =", SIZE(xData), & + ' and SIZE(yData) =', SIZE(yData),' are not the same' + END IF + + ! Is xData non decreasing + DO I_DIFF = 1, size(xData) - 1 + IF (xData(I_DIFF + 1) - xData(I_DIFF) <= 0) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'interp1d: xData is not strictly increasing' + EXIT + END IF + END DO ! Interpolate IF (xq <= MINVAL(xData)) THEN @@ -184,7 +209,7 @@ REAL FUNCTION interp1d(xData, yData, xq) END FUNCTION interp1d !------------------------------------------------------------------------------------------------------------------------------- - REAL FUNCTION interp2d(xData, yData, zData, xq, yq) + REAL FUNCTION interp2d(xData, yData, zData, xq, yq, ErrVar) ! interp2d 2-D interpolation (table lookup). Query done using bilinear interpolation. ! Note that the interpolated matrix with associated query vectors may be different than "standard", - zData should be formatted accordingly ! - xData follows the matrix from left to right @@ -196,13 +221,17 @@ REAL FUNCTION interp2d(xData, yData, zData, xq, yq) ! 5| d e f ! 6| g H i + USE ROSCO_Types, ONLY : ErrorVariables + IMPLICIT NONE + ! Inputs - REAL(8), DIMENSION(:), INTENT(IN) :: xData ! Provided x data (vector), to find query point (should be monotonically increasing) - REAL(8), DIMENSION(:), INTENT(IN) :: yData ! Provided y data (vector), to find query point (should be monotonically increasing) + REAL(8), DIMENSION(:), INTENT(IN) :: xData ! Provided x data (vector), to find query point (should be strictly increasing) + REAL(8), DIMENSION(:), INTENT(IN) :: yData ! Provided y data (vector), to find query point (should be strictly increasing) REAL(8), DIMENSION(:,:), INTENT(IN) :: zData ! Provided z data (vector), to be interpolated REAL(8), INTENT(IN) :: xq ! x-value for which the z value has to be interpolated REAL(8), INTENT(IN) :: yq ! y-value for which the z value has to be interpolated + ! Allocate variables INTEGER(4) :: i ! Iteration index & query index, x-direction INTEGER(4) :: ii ! Iteration index & second que . ry index, x-direction @@ -210,26 +239,64 @@ REAL FUNCTION interp2d(xData, yData, zData, xq, yq) INTEGER(4) :: jj ! Iteration index & second query index, y-direction REAL(8), DIMENSION(2,2) :: fQ ! zData value at query points for bilinear interpolation REAL(8), DIMENSION(1) :: fxy ! Interpolated z-data point to be returned - REAL(8) :: fxy1 ! zData value at query point for bilinear interpolation - REAL(8) :: fxy2 ! zData value at query point for bilinear interpolation + REAL(8) :: fxy1 ! zData value at query point for bilinear interpolation + REAL(8) :: fxy2 ! zData value at query point for bilinear interpolation + LOGICAL :: edge + + ! Error Catching + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar + INTEGER(4) :: I_DIFF + ! Error catching + ! Are xData and zData(:,1) the same size? + IF (SIZE(xData) .NE. SIZE(zData,2)) THEN + ErrVar%aviFAIL = -1 + WRITE(ErrVar%ErrMsg,"(A,I4,A,I4,A)") "interp2d: SIZE(xData) =", SIZE(xData), & + ' and SIZE(zData,1) =', SIZE(zData,2),' are not the same' + END IF + + ! Are yData and zData(1,:) the same size? + IF (SIZE(yData) .NE. SIZE(zData,1)) THEN + ErrVar%aviFAIL = -1 + WRITE(ErrVar%ErrMsg,"(A,I4,A,I4,A)") "interp2d: SIZE(yData) =", SIZE(yData), & + ' and SIZE(zData,2) =', SIZE(zData,1),' are not the same' + END IF + + ! Is xData non decreasing + DO I_DIFF = 1, size(xData) - 1 + IF (xData(I_DIFF + 1) - xData(I_DIFF) <= 0) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'interp2d: xData is not strictly increasing' + EXIT + END IF + END DO + + ! Is yData non decreasing + DO I_DIFF = 1, size(yData) - 1 + IF (yData(I_DIFF + 1) - yData(I_DIFF) <= 0) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'interp2d: yData is not strictly increasing' + EXIT + END IF + END DO + ! ---- Find corner indices surrounding desired interpolation point ----- ! x-direction IF (xq <= MINVAL(xData)) THEN ! On lower x-bound, just need to find zData(yq) j = 1 jj = 1 - interp2d = interp1d(yData,zData(:,j),yq) + interp2d = interp1d(yData,zData(:,j),yq,ErrVar) RETURN ELSEIF (xq >= MAXVAL(xData)) THEN ! On upper x-bound, just need to find zData(yq) j = size(xData) jj = size(xData) - interp2d = interp1d(yData,zData(:,j),yq) + interp2d = interp1d(yData,zData(:,j),yq,ErrVar) RETURN ELSE DO j = 1,size(xData) IF (xq == xData(j)) THEN ! On axis, just need 1d interpolation jj = j - interp2d = interp1d(yData,zData(:,j),yq) + interp2d = interp1d(yData,zData(:,j),yq,ErrVar) RETURN ELSEIF (xq < xData(j)) THEN jj = j @@ -244,18 +311,18 @@ REAL FUNCTION interp2d(xData, yData, zData, xq, yq) IF (yq <= MINVAL(yData)) THEN ! On lower y-bound, just need to find zData(xq) i = 1 ii = 1 - interp2d = interp1d(xData,zData(i,:),xq) + interp2d = interp1d(xData,zData(i,:),xq,ErrVar) RETURN ELSEIF (yq >= MAXVAL(yData)) THEN ! On upper y-bound, just need to find zData(xq) i = size(yData) ii = size(yData) - interp2d = interp1d(xData,zData(i,:),xq) + interp2d = interp1d(xData,zData(i,:),xq,ErrVar) RETURN ELSE DO i = 1,size(yData) IF (yq == yData(i)) THEN ! On axis, just need 1d interpolation ii = i - interp2d = interp1d(xData,zData(i,:),xq) + interp2d = interp1d(xData,zData(i,:),xq,ErrVar) RETURN ELSEIF (yq < yData(i)) THEN ii = i @@ -280,6 +347,11 @@ REAL FUNCTION interp2d(xData, yData, zData, xq, yq) interp2d = fxy(1) + ! Error catching + IF (ErrVar%aviFAIL == -1) THEN + ErrVar%ErrMsg = 'interp2:'//TRIM(ErrVar%ErrMsg) + END IF + END FUNCTION interp2d !------------------------------------------------------------------------------------------------------------------------------- FUNCTION matinv3(A) RESULT(B) @@ -413,29 +485,37 @@ REAL FUNCTION CPfunction(CP, lambda) END FUNCTION CPfunction !------------------------------------------------------------------------------------------------------------------------------- - REAL FUNCTION AeroDynTorque(LocalVar, CntrPar, PerfData) + REAL FUNCTION AeroDynTorque(LocalVar, CntrPar, PerfData, ErrVar) ! Function for computing the aerodynamic torque, divided by the effective rotor torque of the turbine, for use in wind speed estimation - USE ROSCO_Types, ONLY : LocalVariables, ControlParameters, PerformanceData + USE ROSCO_Types, ONLY : LocalVariables, ControlParameters, PerformanceData, ErrorVariables IMPLICIT NONE ! Inputs TYPE(ControlParameters), INTENT(IN) :: CntrPar TYPE(LocalVariables), INTENT(IN) :: LocalVar TYPE(PerformanceData), INTENT(IN) :: PerfData + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Local REAL(8) :: RotorArea REAL(8) :: Cp REAL(8) :: Lambda - + ! Find Torque RotorArea = PI*CntrPar%WE_BladeRadius**2 Lambda = LocalVar%RotSpeedF*CntrPar%WE_BladeRadius/LocalVar%WE_Vw - ! Cp = CPfunction(CntrPar%WE_CP, Lambda) - Cp = interp2d(PerfData%Beta_vec,PerfData%TSR_vec,PerfData%Cp_mat, LocalVar%PC_PitComT*R2D, Lambda) + + ! Compute Cp + Cp = interp2d(PerfData%Beta_vec,PerfData%TSR_vec,PerfData%Cp_mat, LocalVar%PC_PitComT*R2D, Lambda, ErrVar) + AeroDynTorque = 0.5*(CntrPar%WE_RhoAir*RotorArea)*(LocalVar%WE_Vw**3/LocalVar%RotSpeedF)*Cp AeroDynTorque = MAX(AeroDynTorque, 0.0) + + ! Error Catching + IF (ErrVar%aviFAIL == -1) THEN + ErrVar%ErrMsg = 'AeroDynTorque:'//TRIM(ErrVar%ErrMsg) + END IF END FUNCTION AeroDynTorque !------------------------------------------------------------------------------------------------------------------------------- diff --git a/src/ROSCO_Types.f90 b/src/ROSCO_Types.f90 index ba2000da..24e7729a 100644 --- a/src/ROSCO_Types.f90 +++ b/src/ROSCO_Types.f90 @@ -19,8 +19,10 @@ MODULE ROSCO_Types ! Define Types +USE, INTRINSIC :: ISO_C_Binding 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 @@ -231,4 +233,12 @@ MODULE ROSCO_Types END TYPE DebugVariables +TYPE, PUBLIC :: ErrorVariables + ! Error Catching + INTEGER(4) :: size_avcMSG + INTEGER(C_INT) :: aviFAIL ! A flag used to indicate the success of this DLL call set as follows: 0 if the DLL call was successful, >0 if the DLL call was successful but cMessage should be issued as a warning messsage, <0 if the DLL call was unsuccessful or for any other reason the simulation is to be stopped at this point with cMessage as the error message. + ! CHARACTER(:), ALLOCATABLE :: ErrMsg ! a Fortran version of the C string argument (not considered an array here) [subtract 1 for the C null-character] + CHARACTER(999), ALLOCATABLE :: ErrMsg ! a Fortran version of the C string argument (not considered an array here) [subtract 1 for the C null-character] +END TYPE ErrorVariables + END MODULE ROSCO_Types diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index c4ea668f..202ccc61 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -490,23 +490,28 @@ SUBROUTINE Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) END SUBROUTINE Assert ! ----------------------------------------------------------------------------------- ! Define parameters for control actions - SUBROUTINE SetParameters(avrSWAP, aviFAIL, accINFILE, ErrMsg, size_avcMSG, CntrPar, LocalVar, objInst, PerfData) - USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances, PerformanceData + SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, objInst, PerfData, ErrVar) + + USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances, PerformanceData, ErrorVariables INTEGER(4), INTENT(IN) :: size_avcMSG - TYPE(ControlParameters), INTENT(INOUT) :: CntrPar - TYPE(LocalVariables), INTENT(INOUT) :: LocalVar - TYPE(ObjectInstances), INTENT(INOUT) :: objInst - TYPE(PerformanceData), INTENT(INOUT) :: PerfData + TYPE(ControlParameters), INTENT(INOUT) :: CntrPar + TYPE(LocalVariables), INTENT(INOUT) :: LocalVar + TYPE(ObjectInstances), INTENT(INOUT) :: objInst + TYPE(PerformanceData), INTENT(INOUT) :: PerfData + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar - REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. - INTEGER(C_INT), INTENT(OUT) :: aviFAIL ! A flag used to indicate the success of this DLL call set as follows: 0 if the DLL call was successful, >0 if the DLL call was successful but cMessage should be issued as a warning messsage, <0 if the DLL call was unsuccessful or for any other reason the simulation is to be stopped at this point with cMessage as the error message. + REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. CHARACTER(KIND=C_CHAR), INTENT(IN) :: accINFILE(NINT(avrSWAP(50))) ! The name of the parameter input file - CHARACTER(size_avcMSG-1), INTENT(OUT) :: ErrMsg ! a Fortran version of the C string argument (not considered an array here) [subtract 1 for the C null-character] - INTEGER(4) :: K ! Index used for looping through blades. - CHARACTER(200) :: git_version + + INTEGER(4) :: K ! Index used for looping through blades. + CHARACTER(200) :: git_version + + ! Error Catching Variables ! Set aviFAIL to 0 in each iteration: - aviFAIL = 0 + ErrVar%aviFAIL = 0 + ! ALLOCATE(ErrVar%ErrMsg(size_avcMSG-1)) + ErrVar%size_avcMSG = size_avcMSG ! Initialize all filter instance counters at 1 objInst%instLPF = 1 @@ -534,7 +539,7 @@ SUBROUTINE SetParameters(avrSWAP, aviFAIL, accINFILE, ErrMsg, size_avcMSG, CntrP IF (LocalVar%iStatus == 0) THEN ! .TRUE. if we're on the first call to the DLL ! Inform users that we are using this user-defined routine: - aviFAIL = 1 + ErrVar%aviFAIL = 1 git_version = QueryGitVersion() ! ErrMsg = ' '//NEW_LINE('A')// & ! '------------------------------------------------------------------------------'//NEW_LINE('A')// & @@ -548,7 +553,7 @@ SUBROUTINE SetParameters(avrSWAP, aviFAIL, accINFILE, ErrMsg, size_avcMSG, CntrP ! 'Visit our GitHub-page to contribute to this project: '//NEW_LINE('A')// & ! 'https://github.com/NREL/ROSCO '//NEW_LINE('A')// & ! '------------------------------------------------------------------------------' - ErrMsg = ' '//NEW_LINE('A')// & + ErrVar%ErrMsg = ' '//NEW_LINE('A')// & '------------------------------------------------------------------------------'//NEW_LINE('A')// & 'Running ROSCO-'//TRIM(git_version)//NEW_LINE('A')// & 'A wind turbine controller framework for public use in the scientific field '//NEW_LINE('A')// & @@ -585,7 +590,7 @@ SUBROUTINE SetParameters(avrSWAP, aviFAIL, accINFILE, ErrMsg, size_avcMSG, CntrP LocalVar%VS_LastGenTrq = LocalVar%GenTq LocalVar%VS_MaxTq = CntrPar%VS_MaxTq ! Check validity of input parameters: - CALL Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) + CALL Assert(LocalVar, CntrPar, avrSWAP, ErrVar%aviFAIL, ErrVar%ErrMsg, size_avcMSG) ENDIF From d5b7a4f0bc41071e17b2c0a38cb533e8ead6ff05 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Tue, 13 Apr 2021 12:02:30 -0600 Subject: [PATCH 08/26] Expand Assert function and rename to CheckInputs --- src/Functions.f90 | 17 ++ src/ReadSetParameters.f90 | 344 ++++++++++++++++++++++++++++++++++---- 2 files changed, 326 insertions(+), 35 deletions(-) diff --git a/src/Functions.f90 b/src/Functions.f90 index 69799998..a4de1e77 100644 --- a/src/Functions.f90 +++ b/src/Functions.f90 @@ -732,7 +732,24 @@ FUNCTION CurTime( ) RETURN END FUNCTION CurTime !======================================================================= +! This function checks whether an array is non-decreasing + LOGICAL Function NonDecreasing(Array) + IMPLICIT NONE + REAL(8), DIMENSION(:) :: Array + INTEGER(4) :: I_DIFF + + NonDecreasing = .TRUE. + ! Is Array non decreasing + DO I_DIFF = 1, size(Array) - 1 + IF (Array(I_DIFF + 1) - Array(I_DIFF) <= 0) THEN + NonDecreasing = .FALSE. + RETURN + END IF + END DO + + RETURN + END FUNCTION NonDecreasing END MODULE Functions diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 202ccc61..4fcc18fa 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -12,7 +12,7 @@ ! Read and set the parameters used by the controller ! Submodules: -! Assert: Initial condition and input check +! CheckInputs: Initial condition and input check ! ComputeVariablesSetpoints: Compute setpoints used by controllers ! ReadAvrSWAP: Read AvrSWAP array ! ReadControlParameterFileSub: Read DISCON.IN input file @@ -327,7 +327,7 @@ SUBROUTINE ReadAvrSWAP(avrSWAP, LocalVar) END SUBROUTINE ReadAvrSWAP ! ----------------------------------------------------------------------------------- ! Check for errors before any execution - SUBROUTINE Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) + SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) USE, INTRINSIC :: ISO_C_Binding USE ROSCO_Types, ONLY : LocalVariables, ControlParameters @@ -348,92 +348,338 @@ SUBROUTINE Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) !.............................................................................................................................. ! Check validity of input parameters: !.............................................................................................................................. - + + !------- DEBUG ------------------------------------------------------------ + + ! LoggingLevel + IF ((CntrPar%LoggingLevel >= 0) .OR. (CntrPar%LoggingLevel <= 2)) THEN + aviFAIL = -1 + ErrMsg = 'LoggingLevel must be 0, 1, or 2.' + ENDIF + + !------- CONTROLLER FLAGS ------------------------------------------------- + + ! F_LPFType IF ((CntrPar%F_LPFType > 2.0) .OR. (CntrPar%F_LPFType < 1.0)) THEN aviFAIL = -1 ErrMsg = 'F_LPFType must be 1 or 2.' ENDIF - - IF ((CntrPar%F_LPFDamping > 1.0) .OR. (CntrPar%F_LPFDamping < 0.0)) THEN + + ! F_NotchType + IF ((CntrPar%F_NotchType >= 0) .OR. (CntrPar%F_NotchType <= 3)) THEN aviFAIL = -1 - ErrMsg = 'Filter damping coefficient must be between [0, 1]' + ErrMsg = 'F_NotchType must be 0, 1, 2, or 3.' ENDIF - - IF (CntrPar%IPC_CornerFreqAct < 0.0) THEN + + ! F_NotchType + IF ((CntrPar%F_NotchType >= 0) .OR. (CntrPar%F_NotchType <= 2)) THEN aviFAIL = -1 - ErrMsg = 'Corner frequency of IPC actuator model must be positive, or set to 0 to disable.' + ErrMsg = 'F_NotchType must be 0, 1, or 2.' + ENDIF + + ! IPC_ControlMode + IF ((CntrPar%IPC_ControlMode >= 0) .OR. (CntrPar%IPC_ControlMode <= 2)) THEN + aviFAIL = -1 + ErrMsg = 'IPC_ControlMode must be 0, 1, or 2.' + ENDIF + + ! VS_ControlMode + IF ((CntrPar%VS_ControlMode >= 0) .OR. (CntrPar%VS_ControlMode <= 3)) THEN + aviFAIL = -1 + ErrMsg = 'VS_ControlMode must be 0, 1, 2, or 3.' + ENDIF + + ! PC_ControlMode + IF ((CntrPar%PC_ControlMode >= 0) .OR. (CntrPar%PC_ControlMode <= 1)) THEN + aviFAIL = -1 + ErrMsg = 'PC_ControlMode must be 0 or 1.' + ENDIF + + ! Y_ControlMode + IF ((CntrPar%Y_ControlMode >= 0) .OR. (CntrPar%Y_ControlMode <= 2)) THEN + aviFAIL = -1 + ErrMsg = 'Y_ControlMode must be 0, 1 or 2.' + ENDIF + + IF ((CntrPar%IPC_ControlMode > 0) .AND. (CntrPar%Y_ControlMode > 1)) THEN + aviFAIL = -1 + ErrMsg = 'IPC control for load reductions and yaw-by-IPC cannot be activated simultaneously' + ENDIF + + ! SS_Mode + IF ((CntrPar%SS_Mode >= 0) .OR. (CntrPar%SS_Mode <= 1)) THEN + aviFAIL = -1 + ErrMsg = 'SS_Mode must be 0 or 1.' + ENDIF + + ! WE_Mode + IF ((CntrPar%WE_Mode >= 0) .OR. (CntrPar%WE_Mode <= 2)) THEN + aviFAIL = -1 + ErrMsg = 'WE_Mode must be 0, 1, or 2.' + ENDIF + + ! PS_Mode + IF ((CntrPar%PS_Mode >= 0) .OR. (CntrPar%PS_Mode <= 1)) THEN + aviFAIL = -1 + ErrMsg = 'PS_Mode must be 0 or 1.' + ENDIF + + ! SD_Mode + IF ((CntrPar%SD_Mode >= 0) .OR. (CntrPar%SD_Mode <= 1)) THEN + aviFAIL = -1 + ErrMsg = 'SD_Mode must be 0 or 1.' + ENDIF + + ! Fl_Mode + IF ((CntrPar%Fl_Mode >= 0) .OR. (CntrPar%Fl_Mode <= 1)) THEN + aviFAIL = -1 + ErrMsg = 'Fl_Mode must be 0 or 1.' + ENDIF + + ! Flp_Mode + IF ((CntrPar%Flp_Mode >= 0) .OR. (CntrPar%Flp_Mode <= 2)) THEN + aviFAIL = -1 + ErrMsg = 'Flp_Mode must be 0, 1, or 2.' ENDIF + + !------- FILTERS ---------------------------------------------------------- + ! F_LPFCornerFreq IF (CntrPar%F_LPFCornerFreq <= 0.0) THEN aviFAIL = -1 - ErrMsg = 'CornerFreq must be greater than zero.' + ErrMsg = 'F_LPFCornerFreq must be greater than zero.' + ENDIF + + ! F_LPFDamping + IF (CntrPar%F_LPFDamping <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'F_LPFDamping must be greater than zero.' + ENDIF + + ! F_NotchCornerFreq + IF (CntrPar%F_NotchCornerFreq <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'F_NotchCornerFreq must be greater than zero.' + ENDIF + + ! F_NotchBetaNumDen(1) + IF (CntrPar%F_NotchBetaNumDen(1) <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'F_NotchBetaNumDen(1) must be greater than zero.' + ENDIF + + ! F_NotchBetaNumDen(2) + IF (CntrPar%F_NotchBetaNumDen(2) <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'F_NotchBetaNumDen(2) must be greater than zero.' + ENDIF + + ! F_SSCornerFreq + IF (CntrPar%F_SSCornerFreq <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'F_SSCornerFreq must be greater than zero.' + ENDIF + + ! F_FlCornerFreq(1) (frequency) + IF (CntrPar%F_FlCornerFreq <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'F_FlCornerFreq(1) must be greater than zero.' + ENDIF + + ! F_FlCornerFreq(2) (damping) + IF (CntrPar%F_FlDamping <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'F_FlCornerFreq(2) must be greater than zero.' ENDIF + + ! F_FlpCornerFreq(1) (frequency) + IF (CntrPar%F_FlCornerFreq <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'F_FlpCornerFreq(1) must be greater than zero.' + ENDIF + + ! F_FlpCornerFreq(2) (damping) + IF (CntrPar%F_FlDamping <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'F_FlpCornerFreq(2) must be greater than zero.' + ENDIF + - IF ((CntrPar%IPC_ControlMode > 0) .AND. (CntrPar%Y_ControlMode > 1)) THEN + !------- BLADE PITCH CONTROL ---------------------------------------------- + + ! PC_GS_n + IF (CntrPar%PC_GS_n <= 0.0) THEN aviFAIL = -1 - ErrMsg = 'IPC control for load reductions and yaw-by-IPC cannot be activated simultaneously' + ErrMsg = 'PC_GS_n must be greater than 0' + ENDIF + + ! PC_GS_angles + IF (.NOT. NonDecreasing(CntrPar%PC_GS_angles)) THEN + aviFAIL = -1 + ErrMsg = 'PC_GS_angles must be non-decreasing' + ENDIF + + ! PC_GS_KP and PC_GS_KI + ! I'd like to throw warnings if these are positive + + ! PC_MinPit and PC_MaxPit + IF (CntrPar%PC_MinPit >= CntrPar%PC_MaxPit) THEN + aviFAIL = -1 + ErrMsg = 'PC_MinPit must be less than PC_MaxPit.' + ENDIF + + ! PC_RefSpd + IF (CntrPar%PC_RefSpd <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'PC_RefSpd must be greater than zero.' ENDIF - IF (LocalVar%DT <= 0.0) THEN + ! PC_MaxRat + IF (CntrPar%PC_MaxRat <= 0.0) THEN aviFAIL = -1 - ErrMsg = 'DT must be greater than zero.' + ErrMsg = 'PC_MaxRat must be greater than zero.' + ENDIF + + ! PC_MinRat + IF (CntrPar%PC_MinRat <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'PC_MinRat must be greater than zero.' + ENDIF + + !------- INDIVIDUAL PITCH CONTROL ----------------------------------------- + + IF (CntrPar%IPC_CornerFreqAct < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'Corner frequency of IPC actuator model must be positive, or set to 0 to disable.' + ENDIF + + IF (CntrPar%IPC_KI(1) < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'IPC_KI(1) must be zero or greater than zero.' ENDIF + IF (CntrPar%IPC_KI(2) < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'IPC_KI(2) must be zero or greater than zero.' + ENDIF + + !------- VS TORQUE CONTROL ------------------------------------------------ + IF (CntrPar%VS_MaxRat <= 0.0) THEN aviFAIL = -1 ErrMsg = 'VS_MaxRat must be greater than zero.' ENDIF - IF (CntrPar%VS_RtTq < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'VS_RtTq must not be negative.' - ENDIF + + ! VS_Rgn2K IF (CntrPar%VS_Rgn2K < 0.0) THEN aviFAIL = -1 ErrMsg = 'VS_Rgn2K must not be negative.' ENDIF + ! VS_RtTq IF (CntrPar%VS_MaxTq < CntrPar%VS_RtTq) THEN aviFAIL = -1 ErrMsg = 'VS_RtTq must not be greater than VS_MaxTq.' ENDIF - + + ! VS_RtPwr + IF (CntrPar%VS_RtPwr < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'VS_RtPwr must not be negative.' + ENDIF + + ! VS_RtTq + IF (CntrPar%VS_RtTq < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'VS_RtTq must not be negative.' + ENDIF + + ! VS_KP IF (CntrPar%VS_KP(1) > 0.0) THEN aviFAIL = -1 ErrMsg = 'VS_KP must be less than zero.' ENDIF + ! VS_KI IF (CntrPar%VS_KI(1) > 0.0) THEN aviFAIL = -1 ErrMsg = 'VS_KI must be less than zero.' ENDIF - - IF (CntrPar%PC_RefSpd <= 0.0) THEN + + ! VS_TSRopt + IF (CntrPar%VS_TSRopt < 0.0) THEN aviFAIL = -1 - ErrMsg = 'PC_RefSpd must be greater than zero.' + ErrMsg = 'VS_TSRopt must be greater than zero.' ENDIF - IF (CntrPar%PC_MaxRat <= 0.0) THEN + !------- SETPOINT SMOOTHER --------------------------------------------- + + ! SS_VSGain + IF (CntrPar%SS_VSGain < 0.0) THEN aviFAIL = -1 - ErrMsg = 'PC_MaxRat must be greater than zero.' + ErrMsg = 'SS_VSGain must be greater than zero.' ENDIF - - IF (CntrPar%PC_MinPit >= CntrPar%PC_MaxPit) THEN + + ! SS_PCGain + IF (CntrPar%SS_PCGain < 0.0) THEN aviFAIL = -1 - ErrMsg = 'PC_MinPit must be less than PC_MaxPit.' + ErrMsg = 'SS_PCGain must be greater than zero.' ENDIF - IF (CntrPar%IPC_KI(1) < 0.0) THEN + !------- WIND SPEED ESTIMATOR --------------------------------------------- + + ! WE_BladeRadius + IF (CntrPar%WE_BladeRadius < 0.0) THEN aviFAIL = -1 - ErrMsg = 'IPC_KI(1) must be zero or greater than zero.' + ErrMsg = 'WE_BladeRadius must be greater than zero.' ENDIF - - IF (CntrPar%IPC_KI(2) < 0.0) THEN + + ! WE_GearboxRatio + IF (CntrPar%WE_GearboxRatio < 0.0) THEN aviFAIL = -1 - ErrMsg = 'IPC_KI(2) must be zero or greater than zero.' + ErrMsg = 'WE_GearboxRatio must be greater than zero.' ENDIF - + + ! WE_Jtot + IF (CntrPar%WE_Jtot < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'WE_Jtot must be greater than zero.' + ENDIF + + ! WE_RhoAir + IF (CntrPar%WE_RhoAir < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'WE_RhoAir must be greater than zero.' + ENDIF + + ! PerfTableSize(1) + IF (CntrPar%PerfTableSize(1) < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'PerfTableSize(1) must be greater than zero.' + ENDIF + + ! PerfTableSize(2) + IF (CntrPar%PerfTableSize(2) < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'PerfTableSize(2) must be greater than zero.' + ENDIF + + ! WE_FOPoles_N + IF (CntrPar%WE_FOPoles_N < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'WE_FOPoles_N must be greater than zero.' + ENDIF + + ! WE_FOPoles_v + IF (.NOT. NonDecreasing(CntrPar%WE_FOPoles_v)) THEN + aviFAIL = -1 + ErrMsg = 'WE_FOPoles_v must be non-decreasing.' + ENDIF + + + ! ---- Yaw Control ---- IF (CntrPar%Y_ControlMode > 0) THEN IF (CntrPar%Y_IPC_omegaLP <= 0.0) THEN @@ -467,6 +713,24 @@ SUBROUTINE Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ENDIF ENDIF + !------- MINIMUM PITCH SATURATION ------------------------------------------- + IF (CntrPar%PS_Mode > 0) THEN + + ! PS_BldPitchMin_N + IF (CntrPar%PS_BldPitchMin_N < 0.0) THEN + aviFAIL = -1 + ErrMsg = 'PS_BldPitchMin_N must be greater than zero.' + ENDIF + + ! PS_WindSpeeds + IF (.NOT. NonDecreasing(CntrPar%PS_WindSpeeds)) THEN + aviFAIL = -1 + ErrMsg = 'PS_WindSpeeds must be non-decreasing.' + ENDIF + + + ENDIF + ! --- Floating Control --- IF (CntrPar%Fl_Mode > 0) THEN IF (CntrPar%F_NotchType <= 1 .OR. CntrPar%F_NotchCornerFreq == 0.0) THEN @@ -487,7 +751,17 @@ SUBROUTINE Assert(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ErrMsg = 'IPC enabled, but Ptch_Cntrl in ServoDyn has a value of 0. Set it to 1.' ENDIF - END SUBROUTINE Assert + ! DT + IF (LocalVar%DT <= 0.0) THEN + aviFAIL = -1 + ErrMsg = 'DT must be greater than zero.' + ENDIF + + IF (aviFAIL < 0) THEN + ErrMsg = 'CheckInputs:'//TRIM(ErrMsg) + ENDIF + + END SUBROUTINE CheckInputs ! ----------------------------------------------------------------------------------- ! Define parameters for control actions SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, objInst, PerfData, ErrVar) @@ -590,7 +864,7 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj LocalVar%VS_LastGenTrq = LocalVar%GenTq LocalVar%VS_MaxTq = CntrPar%VS_MaxTq ! Check validity of input parameters: - CALL Assert(LocalVar, CntrPar, avrSWAP, ErrVar%aviFAIL, ErrVar%ErrMsg, size_avcMSG) + CALL CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar%aviFAIL, ErrVar%ErrMsg, size_avcMSG) ENDIF From d12fbc12cc85be4c2492c37aefb988753c9672d6 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Fri, 16 Apr 2021 17:29:36 -0600 Subject: [PATCH 09/26] Parse integer inputs, update CheckInputs --- src/Constants.f90 | 1 + src/DISCON.F90 | 2 +- src/Functions.f90 | 67 +++++ src/ROSCO_Types.f90 | 6 +- src/ReadSetParameters.f90 | 609 ++++++++++++++++++++++++++------------ 5 files changed, 486 insertions(+), 199 deletions(-) diff --git a/src/Constants.f90 b/src/Constants.f90 index a205eaa5..766c6987 100644 --- a/src/Constants.f90 +++ b/src/Constants.f90 @@ -16,4 +16,5 @@ MODULE Constants REAL(8), PARAMETER :: PI = 3.14159265359 ! Mathematical constant pi INTEGER(4), PARAMETER :: NP_1 = 1 ! First rotational harmonic INTEGER(4), PARAMETER :: NP_2 = 2 ! Second rotational harmonic + CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] END MODULE Constants \ No newline at end of file diff --git a/src/DISCON.F90 b/src/DISCON.F90 index d5119a71..649f1430 100644 --- a/src/DISCON.F90 +++ b/src/DISCON.F90 @@ -65,7 +65,7 @@ SUBROUTINE DISCON(avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG) BIND (C, NAME CALL SetParameters(avrSWAP, accINFILE, SIZE(avcMSG), CntrPar, LocalVar, objInst, PerfData, ErrVar) CALL PreFilterMeasuredSignals(CntrPar, LocalVar, objInst) -IF ((LocalVar%iStatus >= 0) .AND. (aviFAIL >= 0)) THEN ! Only compute control calculations if no error has occurred and we are not on the last time step +IF ((LocalVar%iStatus >= 0) .AND. (ErrVar%aviFAIL >= 0)) THEN ! Only compute control calculations if no error has occurred and we are not on the last time step CALL WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar, ErrVar) CALL ComputeVariablesSetpoints(CntrPar, LocalVar, objInst) CALL StateMachine(CntrPar, LocalVar) diff --git a/src/Functions.f90 b/src/Functions.f90 index a4de1e77..a9fd8f59 100644 --- a/src/Functions.f90 +++ b/src/Functions.f90 @@ -47,6 +47,7 @@ REAL FUNCTION saturate(inputValue, minValue, maxValue) saturate = MIN(MAX(inputValue,minValue), maxValue) END FUNCTION saturate + !------------------------------------------------------------------------------------------------------------------------------- REAL FUNCTION ratelimit(inputSignal, inputSignalPrev, minRate, maxRate, DT) ! Saturates inputValue. Makes sure it is not smaller than minValue and not larger than maxValue @@ -65,6 +66,7 @@ REAL FUNCTION ratelimit(inputSignal, inputSignalPrev, minRate, maxRate, DT) ratelimit = inputSignalPrev + rate*DT ! Saturate the overall command using the rate limit END FUNCTION ratelimit + !------------------------------------------------------------------------------------------------------------------------------- REAL FUNCTION PIController(error, kp, ki, minValue, maxValue, DT, I0, reset, inst) ! PI controller, with output saturation @@ -105,6 +107,7 @@ REAL FUNCTION PIController(error, kp, ki, minValue, maxValue, DT, I0, reset, ins inst = inst + 1 END FUNCTION PIController + !------------------------------------------------------------------------------------------------------------------------------- REAL(8) FUNCTION PIIController(error, error2, kp, ki, ki2, minValue, maxValue, DT, I0, reset, inst) ! PI controller, with output saturation. @@ -155,6 +158,7 @@ REAL(8) FUNCTION PIIController(error, error2, kp, ki, ki2, minValue, maxValue, D inst = inst + 1 END FUNCTION PIIController + !------------------------------------------------------------------------------------------------------------------------------- REAL FUNCTION interp1d(xData, yData, xq, ErrVar) ! interp1d 1-D interpolation (table lookup), xData should be strictly increasing @@ -208,6 +212,7 @@ REAL FUNCTION interp1d(xData, yData, xq, ErrVar) END IF END FUNCTION interp1d + !------------------------------------------------------------------------------------------------------------------------------- REAL FUNCTION interp2d(xData, yData, zData, xq, yq, ErrVar) ! interp2d 2-D interpolation (table lookup). Query done using bilinear interpolation. @@ -353,6 +358,7 @@ REAL FUNCTION interp2d(xData, yData, zData, xq, yq, ErrVar) END IF END FUNCTION interp2d + !------------------------------------------------------------------------------------------------------------------------------- FUNCTION matinv3(A) RESULT(B) ! Performs a direct calculation of the inverse of a 3×3 matrix. @@ -377,6 +383,7 @@ FUNCTION matinv3(A) RESULT(B) B(2,3) = -detinv * (A(1,1)*A(2,3) - A(1,3)*A(2,1)) B(3,3) = +detinv * (A(1,1)*A(2,2) - A(1,2)*A(2,1)) END FUNCTION matinv3 + !------------------------------------------------------------------------------------------------------------------------------- FUNCTION identity(n) RESULT(A) ! Produces an identity matrix of size n x n @@ -398,6 +405,7 @@ FUNCTION identity(n) RESULT(A) ENDDO END FUNCTION identity + !------------------------------------------------------------------------------------------------------------------------------- REAL FUNCTION DFController(error, Kd, Tf, DT, inst) ! DF controller, with output saturation @@ -427,6 +435,7 @@ REAL FUNCTION DFController(error, Kd, Tf, DT, inst) errorLast(inst) = error DFControllerLast(inst) = DFController END FUNCTION DFController + !------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE ColemanTransform(rootMOOP, aziAngle, nHarmonic, axTOut, axYOut) ! The Coleman or d-q axis transformation transforms the root out of plane bending moments of each turbine blade @@ -448,6 +457,7 @@ SUBROUTINE ColemanTransform(rootMOOP, aziAngle, nHarmonic, axTOut, axYOut) 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 + !------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE ColemanTransformInverse(axTIn, axYIn, aziAngle, nHarmonic, aziOffset, PitComIPC) ! The inverse Coleman or d-q axis transformation transforms the direct axis and quadrature axis @@ -470,6 +480,7 @@ SUBROUTINE ColemanTransformInverse(axTIn, axYIn, aziAngle, nHarmonic, aziOffset, PitComIPC(3) = cos(nHarmonic*(aziAngle+aziOffset+phi3))*axTIn + sin(nHarmonic*(aziAngle+aziOffset+phi3))*axYIn END SUBROUTINE ColemanTransformInverse + !------------------------------------------------------------------------------------------------------------------------------- REAL FUNCTION CPfunction(CP, lambda) ! Paremeterized Cp(lambda) function for a fixed pitch angle. Circumvents the need of importing a look-up table @@ -484,6 +495,7 @@ REAL FUNCTION CPfunction(CP, lambda) CPfunction = saturate(CPfunction, 0.001D0, 1.0D0) END FUNCTION CPfunction + !------------------------------------------------------------------------------------------------------------------------------- REAL FUNCTION AeroDynTorque(LocalVar, CntrPar, PerfData, ErrVar) ! Function for computing the aerodynamic torque, divided by the effective rotor torque of the turbine, for use in wind speed estimation @@ -518,6 +530,7 @@ REAL FUNCTION AeroDynTorque(LocalVar, CntrPar, PerfData, ErrVar) END IF END FUNCTION AeroDynTorque + !------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE Debug(LocalVar, CntrPar, DebugVar, avrSWAP, RootName, size_avcOUTNAME) ! Debug routine, defines what gets printed to DEBUG.dbg if LoggingLevel = 1 @@ -628,6 +641,7 @@ SUBROUTINE Debug(LocalVar, CntrPar, DebugVar, avrSWAP, RootName, size_avcOUTNAME END IF END SUBROUTINE Debug + !------------------------------------------------------------------------------------------------------------------------------- FUNCTION QueryGitVersion() @@ -646,6 +660,7 @@ FUNCTION QueryGitVersion() RETURN END FUNCTION QueryGitVersion + !------------------------------------------------------------------------------------------------------------------------------- ! Copied from NWTC_IO.f90 !> This function returns a character string encoded with today's date in the form dd-mmm-ccyy. @@ -709,6 +724,7 @@ FUNCTION CurDate( ) RETURN END FUNCTION CurDate + !======================================================================= !> This function returns a character string encoded with the time in the form "hh:mm:ss". FUNCTION CurTime( ) @@ -731,6 +747,7 @@ FUNCTION CurTime( ) RETURN END FUNCTION CurTime + !======================================================================= ! This function checks whether an array is non-decreasing LOGICAL Function NonDecreasing(Array) @@ -752,4 +769,54 @@ LOGICAL Function NonDecreasing(Array) RETURN END FUNCTION NonDecreasing +!======================================================================= +!> This routine converts all the text in a string to upper case. + SUBROUTINE Conv2UC ( Str ) + + ! Argument declarations. + + CHARACTER(*), INTENT(INOUT) :: Str !< The string to be converted to UC (upper case). + + + ! Local declarations. + + INTEGER :: IC ! Character index + + + + DO IC=1,LEN_TRIM( Str ) + + IF ( ( Str(IC:IC) >= 'a' ).AND.( Str(IC:IC) <= 'z' ) ) THEN + Str(IC:IC) = CHAR( ICHAR( Str(IC:IC) ) - 32 ) + END IF + + END DO ! IC + + + RETURN + END SUBROUTINE Conv2UC + +!======================================================================= + !> This function returns a left-adjusted string representing the passed numeric value. + !! It eliminates trailing zeroes and even the decimal point if it is not a fraction. \n + !! Use Num2LStr (nwtc_io::num2lstr) instead of directly calling a specific routine in the generic interface. + FUNCTION Int2LStr ( Num ) + + CHARACTER(11) :: Int2LStr !< string representing input number. + + + ! Argument declarations. + + INTEGER, INTENT(IN) :: Num !< The number to convert to a left-justified string. + + + + WRITE (Int2LStr,'(I11)') Num + + Int2Lstr = ADJUSTL( Int2LStr ) + + + RETURN + END FUNCTION Int2LStr + END MODULE Functions diff --git a/src/ROSCO_Types.f90 b/src/ROSCO_Types.f90 index 24e7729a..332dbc53 100644 --- a/src/ROSCO_Types.f90 +++ b/src/ROSCO_Types.f90 @@ -235,10 +235,10 @@ MODULE ROSCO_Types TYPE, PUBLIC :: ErrorVariables ! Error Catching - INTEGER(4) :: size_avcMSG - INTEGER(C_INT) :: aviFAIL ! A flag used to indicate the success of this DLL call set as follows: 0 if the DLL call was successful, >0 if the DLL call was successful but cMessage should be issued as a warning messsage, <0 if the DLL call was unsuccessful or for any other reason the simulation is to be stopped at this point with cMessage as the error message. + INTEGER(4) :: size_avcMSG + INTEGER(C_INT) :: aviFAIL ! A flag used to indicate the success of this DLL call set as follows: 0 if the DLL call was successful, >0 if the DLL call was successful but cMessage should be issued as a warning messsage, <0 if the DLL call was unsuccessful or for any other reason the simulation is to be stopped at this point with cMessage as the error message. ! CHARACTER(:), ALLOCATABLE :: ErrMsg ! a Fortran version of the C string argument (not considered an array here) [subtract 1 for the C null-character] - CHARACTER(999), ALLOCATABLE :: ErrMsg ! a Fortran version of the C string argument (not considered an array here) [subtract 1 for the C null-character] + CHARACTER(:), ALLOCATABLE :: ErrMsg ! a Fortran version of the C string argument (not considered an array here) [subtract 1 for the C null-character] END TYPE ErrorVariables END MODULE ROSCO_Types diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 4fcc18fa..332e63b8 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -31,46 +31,54 @@ MODULE ReadSetParameters CONTAINS ! ----------------------------------------------------------------------------------- ! Read all constant control parameters from DISCON.IN parameter file - SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size)!, accINFILE_size) + SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar)!, accINFILE_size) USE, INTRINSIC :: ISO_C_Binding - USE ROSCO_Types, ONLY : ControlParameters + USE ROSCO_Types, ONLY : ControlParameters, ErrorVariables - INTEGER(4) :: accINFILE_size ! size of DISCON input filename - CHARACTER(accINFILE_size), INTENT(IN) :: accINFILE(accINFILE_size) ! DISCON input filename - INTEGER(4), PARAMETER :: UnControllerParameters = 89 ! Unit number to open file - TYPE(ControlParameters), INTENT(INOUT) :: CntrPar ! Control parameter type + INTEGER(4) :: accINFILE_size ! size of DISCON input filename + CHARACTER(accINFILE_size), INTENT(IN ) :: accINFILE(accINFILE_size) ! DISCON input filename + INTEGER(4), PARAMETER :: UnControllerParameters = 89 ! Unit number to open file + TYPE(ControlParameters), INTENT(INOUT) :: CntrPar ! Control parameter type + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Control parameter type + + INTEGER(4) :: CurLine + + CurLine = 1 OPEN(unit=UnControllerParameters, file=accINFILE(1), status='old', action='read') !----------------------- HEADER ------------------------ - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !----------------------- DEBUG -------------------------- - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%LoggingLevel - READ(UnControllerParameters, *) + CALL ReadEmptyLine(UnControllerParameters,CurLine) + + CALL ParseInput_Int(UnControllerParameters,CurLine,'LoggingLevel',accINFILE(1),CntrPar%LoggingLevel,ErrVar) + + ! READ(UnControllerParameters, *) CntrPar%LoggingLevel + CALL ReadEmptyLine(UnControllerParameters,CurLine) !----------------- 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%PC_ControlMode - READ(UnControllerParameters, *) CntrPar%Y_ControlMode - READ(UnControllerParameters, *) CntrPar%SS_Mode - READ(UnControllerParameters, *) CntrPar%WE_Mode - READ(UnControllerParameters, *) CntrPar%PS_Mode - READ(UnControllerParameters, *) CntrPar%SD_Mode - READ(UnControllerParameters, *) CntrPar%FL_Mode - READ(UnControllerParameters, *) CntrPar%Flp_Mode - READ(UnControllerParameters, *) + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput_Int(UnControllerParameters,CurLine,'F_LPFType',accINFILE(1),CntrPar%F_LPFType,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'F_NotchType',accINFILE(1),CntrPar%F_NotchType,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'IPC_ControlMode',accINFILE(1),CntrPar%IPC_ControlMode,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'VS_ControlMode',accINFILE(1),CntrPar%VS_ControlMode,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'PC_ControlMode',accINFILE(1),CntrPar%PC_ControlMode,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'Y_ControlMode',accINFILE(1),CntrPar%Y_ControlMode,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'SS_Mode',accINFILE(1),CntrPar%SS_Mode,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'WE_Mode',accINFILE(1),CntrPar%WE_Mode,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'PS_Mode',accINFILE(1),CntrPar%PS_Mode,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'SD_Mode',accINFILE(1),CntrPar%SD_Mode,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'FL_Mode',accINFILE(1),CntrPar%FL_Mode,ErrVar) + CALL ParseInput_Int(UnControllerParameters,CurLine,'Flp_Mode',accINFILE(1),CntrPar%Flp_Mode,ErrVar) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !----------------- FILTER CONSTANTS --------------------- - READ(UnControllerParameters, *) + CALL ReadEmptyLine(UnControllerParameters,CurLine) READ(UnControllerParameters, *) CntrPar%F_LPFCornerFreq READ(UnControllerParameters, *) CntrPar%F_LPFDamping READ(UnControllerParameters, *) CntrPar%F_NotchCornerFreq @@ -220,6 +228,206 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size)!, acc !------------------- HOUSEKEEPING ----------------------- CntrPar%PerfFileName = TRIM(CntrPar%PerfFileName) + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = 'ReadControlParameterFileSub:'//TRIM(ErrVar%ErrMsg) + ENDIF + + + CONTAINS + + subroutine ParseInput_Int(Un,CurLine,VarName, FileName, Variable,ErrVar) + USE ROSCO_Types, ONLY : ErrorVariables + + CHARACTER(1024) :: Line + INTEGER(4), INTENT(IN ) :: Un ! Input file unit + CHARACTER(*), INTENT(IN ) :: VarName ! Input file unit + CHARACTER(*), INTENT(IN ) :: FileName ! Input file unit + INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + CHARACTER(20) :: Words (2) ! The two "words" parsed from the line + + INTEGER(4), INTENT(INOUT) :: Variable ! Variable + INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + + ! Read the whole line as a string + READ(UnControllerParameters, '(A)') Line + + ! Separate line string into 2 words + CALL GetWords ( Line, Words, 2 ) + + ! Debugging: show what's being read + ! print *, 'Read: '//TRIM(Words(1))//' and '//Words(2),' on line ', CurLine + + ! Check that Variable Name is in Words + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + + ! IF We haven't failed already + IF (ErrVar%aviFAIL >= 0) THEN + + ! Read the variable + READ (Words(1),*,IOSTAT=ErrStatLcl) Variable + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & + //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& + ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' + ENDIF + + ENDIF + + ! Increment line counter + CurLine = CurLine + 1 + + END subroutine ParseInput_Int + + subroutine ReadEmptyLine(Un,CurLine) + INTEGER(4), INTENT(IN ) :: Un ! Input file unit + INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input + + CHARACTER(1024) :: Line + + READ(UnControllerParameters, '(A)') Line + CurLine = CurLine + 1 + + END subroutine ReadEmptyLine + + !======================================================================= + !> This subroutine is used to get the NumWords "words" from a line of text. + !! It uses spaces, tabs, commas, semicolons, single quotes, and double quotes ("whitespace") + !! as word separators. If there aren't NumWords in the line, the remaining array elements will remain empty. + !! Use CountWords (nwtc_io::countwords) to count the number of words in a line. + SUBROUTINE GetWords ( Line, Words, NumWords ) + + ! Argument declarations. + + INTEGER, INTENT(IN) :: NumWords !< The number of words to look for. + + CHARACTER(*), INTENT(IN) :: Line !< The string to search. + CHARACTER(*), INTENT(OUT) :: Words(NumWords) !< The array of found words. + + + ! Local declarations. + + INTEGER :: Ch ! Character position within the string. + INTEGER :: IW ! Word index. + INTEGER :: NextWhite ! The location of the next whitespace in the string. + CHARACTER(1), PARAMETER :: Tab = CHAR( 9 ) + + + + ! Let's prefill the array with blanks. + + DO IW=1,NumWords + Words(IW) = ' ' + END DO ! IW + + + ! Let's make sure we have text on this line. + + IF ( LEN_TRIM( Line ) == 0 ) RETURN + + + ! Parse words separated by any combination of spaces, tabs, commas, + ! semicolons, single quotes, and double quotes ("whitespace"). + + Ch = 0 + IW = 0 + + DO + + NextWhite = SCAN( Line(Ch+1:) , ' ,!;''"'//Tab ) + + IF ( NextWhite > 1 ) THEN + + IW = IW + 1 + Words(IW) = Line(Ch+1:Ch+NextWhite-1) + + IF ( IW == NumWords ) EXIT + + Ch = Ch + NextWhite + + ELSE IF ( NextWhite == 1 ) THEN + + Ch = Ch + 1 + + CYCLE + + ELSE + + EXIT + + END IF + + END DO + + + RETURN + END SUBROUTINE GetWords + !======================================================================= + + !> This subroutine checks the data to be parsed to make sure it finds + !! the expected variable name and an associated value. + SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) + + USE ROSCO_Types, ONLY : ErrorVariables + + + ! Arguments declarations. + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + + INTEGER(4), INTENT(IN) :: FileLineNum !< The number of the line in the file being parsed. + INTEGER(4) :: NameIndx !< The index into the Words array that points to the variable name. + + CHARACTER(*), INTENT(IN) :: ExpVarName !< The expected variable name. + CHARACTER(*), INTENT(IN) :: Words (2) !< The two words to be parsed from the line. + + CHARACTER(*), INTENT(IN) :: FileName !< The name of the file being parsed. + + + ! Local declarations. + + CHARACTER(20) :: ExpUCVarName ! The uppercase version of ExpVarName. + CHARACTER(20) :: FndUCVarName ! The uppercase version of the word being tested. + + + + + ! Convert the found and expected names to uppercase. + + FndUCVarName = Words(1) + ExpUCVarName = ExpVarName + + CALL Conv2UC ( FndUCVarName ) + CALL Conv2UC ( ExpUCVarName ) + + ! See which word is the variable name. Generate an error if it is the first + + IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN + NameIndx = 1 + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & + //'".'//NewLine//' >> The variable "'//TRIM( Words(1) )//'" was not assigned a value on line #' & + //TRIM( Int2LStr( FileLineNum ) )//'.' + RETURN + ELSE + FndUCVarName = Words(2) + CALL Conv2UC ( FndUCVarName ) + IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN + NameIndx = 2 + ELSE + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & + //'".'//NewLine//' >> The variable "'//TRIM( ExpVarName )//'" was not assigned a value on line #' & + //TRIM( Int2LStr( FileLineNum ) )//'.' + RETURN + ENDIF + ENDIF + + + END SUBROUTINE ChkParseData + END SUBROUTINE ReadControlParameterFileSub ! ----------------------------------------------------------------------------------- ! Calculate setpoints for primary control actions @@ -327,21 +535,19 @@ SUBROUTINE ReadAvrSWAP(avrSWAP, LocalVar) END SUBROUTINE ReadAvrSWAP ! ----------------------------------------------------------------------------------- ! Check for errors before any execution - SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) + SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) USE, INTRINSIC :: ISO_C_Binding - USE ROSCO_Types, ONLY : LocalVariables, ControlParameters + USE ROSCO_Types, ONLY : LocalVariables, ControlParameters, ErrorVariables IMPLICIT NONE ! Inputs - TYPE(ControlParameters), INTENT(IN) :: CntrPar - TYPE(LocalVariables), INTENT(IN) :: LocalVar - INTEGER(4), INTENT(IN) :: size_avcMSG - REAL(C_FLOAT), INTENT(IN) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. + TYPE(ControlParameters), INTENT(IN ) :: CntrPar + TYPE(LocalVariables), INTENT(IN ) :: LocalVar + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar + INTEGER(4), INTENT(IN ) :: size_avcMSG + REAL(C_FLOAT), INTENT(IN ) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. - ! Outputs - INTEGER(C_INT), INTENT(OUT) :: aviFAIL ! A flag used to indicate the success of this DLL call set as follows: 0 if the DLL call was successful, >0 if the DLL call was successful but cMessage should be issued as a warning messsage, <0 if the DLL call was unsuccessful or for any other reason the simulation is to be stopped at this point with cMessage as the error message. - CHARACTER(size_avcMSG-1), INTENT(OUT) :: ErrMsg ! a Fortran version of the C string argument (not considered an array here) [subtract 1 for the C null-character] ! Local @@ -352,156 +558,163 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) !------- DEBUG ------------------------------------------------------------ ! LoggingLevel - IF ((CntrPar%LoggingLevel >= 0) .OR. (CntrPar%LoggingLevel <= 2)) THEN - aviFAIL = -1 - ErrMsg = 'LoggingLevel must be 0, 1, or 2.' + IF ((CntrPar%LoggingLevel < 0) .OR. (CntrPar%LoggingLevel > 2)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'LoggingLevel must be 0, 1, or 2.' ENDIF !------- CONTROLLER FLAGS ------------------------------------------------- ! F_LPFType - IF ((CntrPar%F_LPFType > 2.0) .OR. (CntrPar%F_LPFType < 1.0)) THEN - aviFAIL = -1 - ErrMsg = 'F_LPFType must be 1 or 2.' + IF ((CntrPar%F_LPFType < 1) .OR. (CntrPar%F_LPFType > 2)) THEN + ErrVar%aviFAIL = -1 + PRINT *, CntrPar%F_LPFType + ErrVar%ErrMsg = 'F_LPFType must be 1 or 2.' ENDIF ! F_NotchType - IF ((CntrPar%F_NotchType >= 0) .OR. (CntrPar%F_NotchType <= 3)) THEN - aviFAIL = -1 - ErrMsg = 'F_NotchType must be 0, 1, 2, or 3.' + IF ((CntrPar%F_NotchType < 0) .OR. (CntrPar%F_NotchType > 3)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_NotchType must be 0, 1, 2, or 3.' ENDIF ! F_NotchType - IF ((CntrPar%F_NotchType >= 0) .OR. (CntrPar%F_NotchType <= 2)) THEN - aviFAIL = -1 - ErrMsg = 'F_NotchType must be 0, 1, or 2.' + IF ((CntrPar%F_NotchType < 0) .OR. (CntrPar%F_NotchType > 2)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_NotchType must be 0, 1, or 2.' ENDIF ! IPC_ControlMode - IF ((CntrPar%IPC_ControlMode >= 0) .OR. (CntrPar%IPC_ControlMode <= 2)) THEN - aviFAIL = -1 - ErrMsg = 'IPC_ControlMode must be 0, 1, or 2.' + IF ((CntrPar%IPC_ControlMode < 0) .OR. (CntrPar%IPC_ControlMode > 2)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'IPC_ControlMode must be 0, 1, or 2.' ENDIF ! VS_ControlMode - IF ((CntrPar%VS_ControlMode >= 0) .OR. (CntrPar%VS_ControlMode <= 3)) THEN - aviFAIL = -1 - ErrMsg = 'VS_ControlMode must be 0, 1, 2, or 3.' + IF ((CntrPar%VS_ControlMode < 0) .OR. (CntrPar%VS_ControlMode > 3)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'VS_ControlMode must be 0, 1, 2, or 3.' ENDIF ! PC_ControlMode - IF ((CntrPar%PC_ControlMode >= 0) .OR. (CntrPar%PC_ControlMode <= 1)) THEN - aviFAIL = -1 - ErrMsg = 'PC_ControlMode must be 0 or 1.' + IF ((CntrPar%PC_ControlMode < 0) .OR. (CntrPar%PC_ControlMode > 1)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PC_ControlMode must be 0 or 1.' ENDIF ! Y_ControlMode - IF ((CntrPar%Y_ControlMode >= 0) .OR. (CntrPar%Y_ControlMode <= 2)) THEN - aviFAIL = -1 - ErrMsg = 'Y_ControlMode must be 0, 1 or 2.' + IF ((CntrPar%Y_ControlMode < 0) .OR. (CntrPar%Y_ControlMode > 2)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Y_ControlMode must be 0, 1 or 2.' ENDIF IF ((CntrPar%IPC_ControlMode > 0) .AND. (CntrPar%Y_ControlMode > 1)) THEN - aviFAIL = -1 - ErrMsg = 'IPC control for load reductions and yaw-by-IPC cannot be activated simultaneously' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'IPC control for load reductions and yaw-by-IPC cannot be activated simultaneously' ENDIF ! SS_Mode - IF ((CntrPar%SS_Mode >= 0) .OR. (CntrPar%SS_Mode <= 1)) THEN - aviFAIL = -1 - ErrMsg = 'SS_Mode must be 0 or 1.' + IF ((CntrPar%SS_Mode < 0) .OR. (CntrPar%SS_Mode > 1)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'SS_Mode must be 0 or 1.' ENDIF ! WE_Mode - IF ((CntrPar%WE_Mode >= 0) .OR. (CntrPar%WE_Mode <= 2)) THEN - aviFAIL = -1 - ErrMsg = 'WE_Mode must be 0, 1, or 2.' + IF ((CntrPar%WE_Mode < 0) .OR. (CntrPar%WE_Mode > 2)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'WE_Mode must be 0, 1, or 2.' ENDIF ! PS_Mode - IF ((CntrPar%PS_Mode >= 0) .OR. (CntrPar%PS_Mode <= 1)) THEN - aviFAIL = -1 - ErrMsg = 'PS_Mode must be 0 or 1.' + IF ((CntrPar%PS_Mode < 0) .OR. (CntrPar%PS_Mode > 1)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PS_Mode must be 0 or 1.' ENDIF ! SD_Mode - IF ((CntrPar%SD_Mode >= 0) .OR. (CntrPar%SD_Mode <= 1)) THEN - aviFAIL = -1 - ErrMsg = 'SD_Mode must be 0 or 1.' + IF ((CntrPar%SD_Mode < 0) .OR. (CntrPar%SD_Mode > 1)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'SD_Mode must be 0 or 1.' ENDIF ! Fl_Mode - IF ((CntrPar%Fl_Mode >= 0) .OR. (CntrPar%Fl_Mode <= 1)) THEN - aviFAIL = -1 - ErrMsg = 'Fl_Mode must be 0 or 1.' + IF ((CntrPar%Fl_Mode < 0) .OR. (CntrPar%Fl_Mode > 1)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Fl_Mode must be 0 or 1.' ENDIF ! Flp_Mode - IF ((CntrPar%Flp_Mode >= 0) .OR. (CntrPar%Flp_Mode <= 2)) THEN - aviFAIL = -1 - ErrMsg = 'Flp_Mode must be 0, 1, or 2.' + IF ((CntrPar%Flp_Mode < 0) .OR. (CntrPar%Flp_Mode > 2)) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Flp_Mode must be 0, 1, or 2.' ENDIF !------- FILTERS ---------------------------------------------------------- ! F_LPFCornerFreq IF (CntrPar%F_LPFCornerFreq <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_LPFCornerFreq must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_LPFCornerFreq must be greater than zero.' ENDIF ! F_LPFDamping - IF (CntrPar%F_LPFDamping <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_LPFDamping must be greater than zero.' + IF (CntrPar%F_LPFType == 2) THEN + IF (CntrPar%F_LPFDamping <= 0.0) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_LPFDamping must be greater than zero.' + ENDIF ENDIF - ! F_NotchCornerFreq - IF (CntrPar%F_NotchCornerFreq <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_NotchCornerFreq must be greater than zero.' - ENDIF + ! Notch Filter Params + IF (CntrPar%F_NotchType > 0) THEN - ! F_NotchBetaNumDen(1) - IF (CntrPar%F_NotchBetaNumDen(1) <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_NotchBetaNumDen(1) must be greater than zero.' - ENDIF + ! F_NotchCornerFreq + IF (CntrPar%F_NotchCornerFreq <= 0.0) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_NotchCornerFreq must be greater than zero.' + ENDIF - ! F_NotchBetaNumDen(2) - IF (CntrPar%F_NotchBetaNumDen(2) <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_NotchBetaNumDen(2) must be greater than zero.' + ! F_NotchBetaNumDen(1) + IF (CntrPar%F_NotchBetaNumDen(1) <= 0.0) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_NotchBetaNumDen(1) must be greater than zero.' + ENDIF + + ! F_NotchBetaNumDen(2) + IF (CntrPar%F_NotchBetaNumDen(2) <= 0.0) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_NotchBetaNumDen(2) must be greater than zero.' + ENDIF ENDIF ! F_SSCornerFreq IF (CntrPar%F_SSCornerFreq <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_SSCornerFreq must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_SSCornerFreq must be greater than zero.' ENDIF ! F_FlCornerFreq(1) (frequency) IF (CntrPar%F_FlCornerFreq <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_FlCornerFreq(1) must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_FlCornerFreq(1) must be greater than zero.' ENDIF ! F_FlCornerFreq(2) (damping) IF (CntrPar%F_FlDamping <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_FlCornerFreq(2) must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_FlCornerFreq(2) must be greater than zero.' ENDIF ! F_FlpCornerFreq(1) (frequency) IF (CntrPar%F_FlCornerFreq <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_FlpCornerFreq(1) must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_FlpCornerFreq(1) must be greater than zero.' ENDIF ! F_FlpCornerFreq(2) (damping) IF (CntrPar%F_FlDamping <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_FlpCornerFreq(2) must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_FlpCornerFreq(2) must be greater than zero.' ENDIF @@ -509,14 +722,14 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ! PC_GS_n IF (CntrPar%PC_GS_n <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'PC_GS_n must be greater than 0' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PC_GS_n must be greater than 0' ENDIF ! PC_GS_angles IF (.NOT. NonDecreasing(CntrPar%PC_GS_angles)) THEN - aviFAIL = -1 - ErrMsg = 'PC_GS_angles must be non-decreasing' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PC_GS_angles must be non-decreasing' ENDIF ! PC_GS_KP and PC_GS_KI @@ -524,158 +737,159 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ! PC_MinPit and PC_MaxPit IF (CntrPar%PC_MinPit >= CntrPar%PC_MaxPit) THEN - aviFAIL = -1 - ErrMsg = 'PC_MinPit must be less than PC_MaxPit.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PC_MinPit must be less than PC_MaxPit.' ENDIF ! PC_RefSpd IF (CntrPar%PC_RefSpd <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'PC_RefSpd must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PC_RefSpd must be greater than zero.' ENDIF ! PC_MaxRat IF (CntrPar%PC_MaxRat <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'PC_MaxRat must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PC_MaxRat must be greater than zero.' ENDIF ! PC_MinRat - IF (CntrPar%PC_MinRat <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'PC_MinRat must be greater than zero.' + IF (CntrPar%PC_MinRat >= 0.0) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PC_MinRat must be less than zero.' ENDIF !------- INDIVIDUAL PITCH CONTROL ----------------------------------------- IF (CntrPar%IPC_CornerFreqAct < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'Corner frequency of IPC actuator model must be positive, or set to 0 to disable.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Corner frequency of IPC actuator model must be positive, or set to 0 to disable.' ENDIF IF (CntrPar%IPC_KI(1) < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'IPC_KI(1) must be zero or greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'IPC_KI(1) must be zero or greater than zero.' ENDIF IF (CntrPar%IPC_KI(2) < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'IPC_KI(2) must be zero or greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'IPC_KI(2) must be zero or greater than zero.' ENDIF !------- VS TORQUE CONTROL ------------------------------------------------ IF (CntrPar%VS_MaxRat <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'VS_MaxRat must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'VS_MaxRat must be greater than zero.' ENDIF ! VS_Rgn2K IF (CntrPar%VS_Rgn2K < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'VS_Rgn2K must not be negative.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'VS_Rgn2K must not be negative.' ENDIF ! VS_RtTq IF (CntrPar%VS_MaxTq < CntrPar%VS_RtTq) THEN - aviFAIL = -1 - ErrMsg = 'VS_RtTq must not be greater than VS_MaxTq.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'VS_RtTq must not be greater than VS_MaxTq.' ENDIF ! VS_RtPwr IF (CntrPar%VS_RtPwr < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'VS_RtPwr must not be negative.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'VS_RtPwr must not be negative.' ENDIF ! VS_RtTq IF (CntrPar%VS_RtTq < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'VS_RtTq must not be negative.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'VS_RtTq must not be negative.' ENDIF ! VS_KP IF (CntrPar%VS_KP(1) > 0.0) THEN - aviFAIL = -1 - ErrMsg = 'VS_KP must be less than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'VS_KP must be less than zero.' ENDIF ! VS_KI IF (CntrPar%VS_KI(1) > 0.0) THEN - aviFAIL = -1 - ErrMsg = 'VS_KI must be less than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'VS_KI must be less than zero.' ENDIF ! VS_TSRopt IF (CntrPar%VS_TSRopt < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'VS_TSRopt must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'VS_TSRopt must be greater than zero.' ENDIF !------- SETPOINT SMOOTHER --------------------------------------------- ! SS_VSGain IF (CntrPar%SS_VSGain < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'SS_VSGain must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'SS_VSGain must be greater than zero.' ENDIF ! SS_PCGain IF (CntrPar%SS_PCGain < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'SS_PCGain must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'SS_PCGain must be greater than zero.' ENDIF !------- WIND SPEED ESTIMATOR --------------------------------------------- ! WE_BladeRadius IF (CntrPar%WE_BladeRadius < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'WE_BladeRadius must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'WE_BladeRadius must be greater than zero.' ENDIF ! WE_GearboxRatio IF (CntrPar%WE_GearboxRatio < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'WE_GearboxRatio must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'WE_GearboxRatio must be greater than zero.' ENDIF ! WE_Jtot IF (CntrPar%WE_Jtot < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'WE_Jtot must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'WE_Jtot must be greater than zero.' ENDIF ! WE_RhoAir IF (CntrPar%WE_RhoAir < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'WE_RhoAir must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'WE_RhoAir must be greater than zero.' ENDIF ! PerfTableSize(1) IF (CntrPar%PerfTableSize(1) < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'PerfTableSize(1) must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PerfTableSize(1) must be greater than zero.' ENDIF ! PerfTableSize(2) IF (CntrPar%PerfTableSize(2) < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'PerfTableSize(2) must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PerfTableSize(2) must be greater than zero.' ENDIF ! WE_FOPoles_N IF (CntrPar%WE_FOPoles_N < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'WE_FOPoles_N must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'WE_FOPoles_N must be greater than zero.' ENDIF ! WE_FOPoles_v IF (.NOT. NonDecreasing(CntrPar%WE_FOPoles_v)) THEN - aviFAIL = -1 - ErrMsg = 'WE_FOPoles_v must be non-decreasing.' + ErrVar%aviFAIL = -1 + write(400,*) CntrPar%WE_FOPoles_v + ErrVar%ErrMsg = 'WE_FOPoles_v must be non-decreasing.' ENDIF @@ -683,33 +897,33 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ! ---- Yaw Control ---- IF (CntrPar%Y_ControlMode > 0) THEN IF (CntrPar%Y_IPC_omegaLP <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'Y_IPC_omegaLP must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Y_IPC_omegaLP must be greater than zero.' ENDIF IF (CntrPar%Y_IPC_zetaLP <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'Y_IPC_zetaLP must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Y_IPC_zetaLP must be greater than zero.' ENDIF IF (CntrPar%Y_ErrThresh <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'Y_ErrThresh must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Y_ErrThresh must be greater than zero.' ENDIF IF (CntrPar%Y_Rate <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'CntrPar%Y_Rate must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'CntrPar%Y_Rate must be greater than zero.' ENDIF IF (CntrPar%Y_omegaLPFast <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'Y_omegaLPFast must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Y_omegaLPFast must be greater than zero.' ENDIF IF (CntrPar%Y_omegaLPSlow <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'Y_omegaLPSlow must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Y_omegaLPSlow must be greater than zero.' ENDIF ENDIF @@ -718,14 +932,14 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ! PS_BldPitchMin_N IF (CntrPar%PS_BldPitchMin_N < 0.0) THEN - aviFAIL = -1 - ErrMsg = 'PS_BldPitchMin_N must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PS_BldPitchMin_N must be greater than zero.' ENDIF ! PS_WindSpeeds IF (.NOT. NonDecreasing(CntrPar%PS_WindSpeeds)) THEN - aviFAIL = -1 - ErrMsg = 'PS_WindSpeeds must be non-decreasing.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'PS_WindSpeeds must be non-decreasing.' ENDIF @@ -734,31 +948,31 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, aviFAIL, ErrMsg, size_avcMSG) ! --- Floating Control --- IF (CntrPar%Fl_Mode > 0) THEN IF (CntrPar%F_NotchType <= 1 .OR. CntrPar%F_NotchCornerFreq == 0.0) THEN - aviFAIL = -1 - ErrMsg = 'F_NotchType and F_NotchCornerFreq must be specified for Fl_Mode greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'F_NotchType and F_NotchCornerFreq must be specified for Fl_Mode greater than zero.' ENDIF ENDIF ! Abort if the user has not requested a pitch angle actuator (See Appendix A ! of Bladed User's Guide): IF (NINT(avrSWAP(10)) /= 0) THEN ! .TRUE. if a pitch angle actuator hasn't been requested - aviFAIL = -1 - ErrMsg = 'Pitch angle actuator not requested.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'Pitch angle actuator not requested.' ENDIF IF (NINT(avrSWAP(28)) == 0 .AND. ((CntrPar%IPC_ControlMode > 0) .OR. (CntrPar%Y_ControlMode > 1))) THEN - aviFAIL = -1 - ErrMsg = 'IPC enabled, but Ptch_Cntrl in ServoDyn has a value of 0. Set it to 1.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'IPC enabled, but Ptch_Cntrl in ServoDyn has a value of 0. Set it to 1.' ENDIF ! DT IF (LocalVar%DT <= 0.0) THEN - aviFAIL = -1 - ErrMsg = 'DT must be greater than zero.' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'DT must be greater than zero.' ENDIF - IF (aviFAIL < 0) THEN - ErrMsg = 'CheckInputs:'//TRIM(ErrMsg) + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = 'CheckInputs:'//TRIM(ErrVar%ErrMsg) ENDIF END SUBROUTINE CheckInputs @@ -782,7 +996,7 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj CHARACTER(200) :: git_version ! Error Catching Variables - ! Set aviFAIL to 0 in each iteration: + ! Set ErrVar%aviFAIL to 0 in each iteration: ErrVar%aviFAIL = 0 ! ALLOCATE(ErrVar%ErrMsg(size_avcMSG-1)) ErrVar%size_avcMSG = size_avcMSG @@ -813,7 +1027,7 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj IF (LocalVar%iStatus == 0) THEN ! .TRUE. if we're on the first call to the DLL ! Inform users that we are using this user-defined routine: - ErrVar%aviFAIL = 1 + ! ErrVar%aviFAIL = 1 git_version = QueryGitVersion() ! ErrMsg = ' '//NEW_LINE('A')// & ! '------------------------------------------------------------------------------'//NEW_LINE('A')// & @@ -827,7 +1041,7 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj ! 'Visit our GitHub-page to contribute to this project: '//NEW_LINE('A')// & ! 'https://github.com/NREL/ROSCO '//NEW_LINE('A')// & ! '------------------------------------------------------------------------------' - ErrVar%ErrMsg = ' '//NEW_LINE('A')// & + PRINT *,' '//NEW_LINE('A')// & '------------------------------------------------------------------------------'//NEW_LINE('A')// & 'Running ROSCO-'//TRIM(git_version)//NEW_LINE('A')// & 'A wind turbine controller framework for public use in the scientific field '//NEW_LINE('A')// & @@ -835,7 +1049,7 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj ' Delft University of Technology, The Netherlands '//NEW_LINE('A')// & '------------------------------------------------------------------------------' - CALL ReadControlParameterFileSub(CntrPar, accINFILE, NINT(avrSWAP(50))) + CALL ReadControlParameterFileSub(CntrPar, accINFILE, NINT(avrSWAP(50)),ErrVar) IF (CntrPar%WE_Mode > 0) THEN CALL READCpFile(CntrPar,PerfData) @@ -863,8 +1077,13 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj ENDIF LocalVar%VS_LastGenTrq = LocalVar%GenTq LocalVar%VS_MaxTq = CntrPar%VS_MaxTq + ! Check validity of input parameters: - CALL CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar%aviFAIL, ErrVar%ErrMsg, size_avcMSG) + CALL CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) + + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = 'SetParameters:'//TRIM(ErrVar%ErrMsg) + ENDIF ENDIF From f928868cdd85151c1dfbb2b382e6bbd2b6ed4b3c Mon Sep 17 00:00:00 2001 From: dzalkind Date: Mon, 19 Apr 2021 18:34:08 -0600 Subject: [PATCH 10/26] Add double and string input parsing --- src/ReadSetParameters.f90 | 720 ++++++++++++++++++++++---------------- 1 file changed, 413 insertions(+), 307 deletions(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 332e63b8..379a24ca 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -28,6 +28,13 @@ MODULE ReadSetParameters IMPLICIT NONE +INTERFACE ParseInput ! Parses a character variable name and value from a string. + MODULE PROCEDURE ParseInput_Str ! Parses a character string from a string. + MODULE PROCEDURE ParseInput_Dbl ! Parses a double-precision REAL from a string. + MODULE PROCEDURE ParseInput_Int ! Parses an INTEGER from a string. + ! MODULE PROCEDURE ParseInput_Log ! Parses an LOGICAL from a string. +END INTERFACE + CONTAINS ! ----------------------------------------------------------------------------------- ! Read all constant control parameters from DISCON.IN parameter file @@ -56,174 +63,177 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar !----------------------- DEBUG -------------------------- CALL ReadEmptyLine(UnControllerParameters,CurLine) - CALL ParseInput_Int(UnControllerParameters,CurLine,'LoggingLevel',accINFILE(1),CntrPar%LoggingLevel,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'LoggingLevel',accINFILE(1),CntrPar%LoggingLevel,ErrVar) ! READ(UnControllerParameters, *) CntrPar%LoggingLevel CALL ReadEmptyLine(UnControllerParameters,CurLine) !----------------- CONTROLLER FLAGS --------------------- CALL ReadEmptyLine(UnControllerParameters,CurLine) - CALL ParseInput_Int(UnControllerParameters,CurLine,'F_LPFType',accINFILE(1),CntrPar%F_LPFType,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'F_NotchType',accINFILE(1),CntrPar%F_NotchType,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'IPC_ControlMode',accINFILE(1),CntrPar%IPC_ControlMode,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'VS_ControlMode',accINFILE(1),CntrPar%VS_ControlMode,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'PC_ControlMode',accINFILE(1),CntrPar%PC_ControlMode,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'Y_ControlMode',accINFILE(1),CntrPar%Y_ControlMode,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'SS_Mode',accINFILE(1),CntrPar%SS_Mode,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'WE_Mode',accINFILE(1),CntrPar%WE_Mode,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'PS_Mode',accINFILE(1),CntrPar%PS_Mode,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'SD_Mode',accINFILE(1),CntrPar%SD_Mode,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'FL_Mode',accINFILE(1),CntrPar%FL_Mode,ErrVar) - CALL ParseInput_Int(UnControllerParameters,CurLine,'Flp_Mode',accINFILE(1),CntrPar%Flp_Mode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'F_LPFType',accINFILE(1),CntrPar%F_LPFType,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'F_NotchType',accINFILE(1),CntrPar%F_NotchType,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'IPC_ControlMode',accINFILE(1),CntrPar%IPC_ControlMode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_ControlMode',accINFILE(1),CntrPar%VS_ControlMode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'PC_ControlMode',accINFILE(1),CntrPar%PC_ControlMode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Y_ControlMode',accINFILE(1),CntrPar%Y_ControlMode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'SS_Mode',accINFILE(1),CntrPar%SS_Mode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'WE_Mode',accINFILE(1),CntrPar%WE_Mode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'PS_Mode',accINFILE(1),CntrPar%PS_Mode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'SD_Mode',accINFILE(1),CntrPar%SD_Mode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'FL_Mode',accINFILE(1),CntrPar%FL_Mode,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Flp_Mode',accINFILE(1),CntrPar%Flp_Mode,ErrVar) CALL ReadEmptyLine(UnControllerParameters,CurLine) !----------------- FILTER CONSTANTS --------------------- CALL ReadEmptyLine(UnControllerParameters,CurLine) - READ(UnControllerParameters, *) CntrPar%F_LPFCornerFreq - READ(UnControllerParameters, *) CntrPar%F_LPFDamping - READ(UnControllerParameters, *) CntrPar%F_NotchCornerFreq + CALL ParseInput(UnControllerParameters,CurLine,'F_LPFCornerFreq',accINFILE(1),CntrPar%F_LPFCornerFreq,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'F_LPFDamping',accINFILE(1),CntrPar%F_LPFDamping,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'F_NotchCornerFreq',accINFILE(1),CntrPar%F_NotchCornerFreq,ErrVar) ALLOCATE(CntrPar%F_NotchBetaNumDen(2)) - READ(UnControllerParameters,*) CntrPar%F_NotchBetaNumDen - READ(UnControllerParameters,*) CntrPar%F_SSCornerFreq - READ(UnControllerParameters,*) CntrPar%F_FlCornerFreq, CntrPar%F_FlDamping - READ(UnControllerParameters,*) CntrPar%F_FlpCornerFreq, CntrPar%F_FlpDamping - READ(UnControllerParameters, *) + READ(UnControllerParameters,*) CntrPar%F_NotchBetaNumDen ; CurLine=CurLine+1 + CALL ParseInput(UnControllerParameters,CurLine,'F_SSCornerFreq',accINFILE(1),CntrPar%F_SSCornerFreq,ErrVar) + READ(UnControllerParameters,*) CntrPar%F_FlCornerFreq, CntrPar%F_FlDamping ; CurLine=CurLine+1 + READ(UnControllerParameters,*) CntrPar%F_FlpCornerFreq, CntrPar%F_FlpDamping ; CurLine=CurLine+1 + CALL ReadEmptyLine(UnControllerParameters,CurLine) !----------- BLADE PITCH CONTROLLER CONSTANTS ----------- - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%PC_GS_n + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'PC_GS_n',accINFILE(1),CntrPar%PC_GS_n,ErrVar) ALLOCATE(CntrPar%PC_GS_angles(CntrPar%PC_GS_n)) - READ(UnControllerParameters,*) CntrPar%PC_GS_angles + READ(UnControllerParameters,*) CntrPar%PC_GS_angles ; CurLine=CurLine+1 ALLOCATE(CntrPar%PC_GS_KP(CntrPar%PC_GS_n)) - READ(UnControllerParameters,*) CntrPar%PC_GS_KP + READ(UnControllerParameters,*) CntrPar%PC_GS_KP ; CurLine=CurLine+1 ALLOCATE(CntrPar%PC_GS_KI(CntrPar%PC_GS_n)) - READ(UnControllerParameters,*) CntrPar%PC_GS_KI + READ(UnControllerParameters,*) CntrPar%PC_GS_KI ; CurLine=CurLine+1 ALLOCATE(CntrPar%PC_GS_KD(CntrPar%PC_GS_n)) - READ(UnControllerParameters,*) CntrPar%PC_GS_KD + READ(UnControllerParameters,*) CntrPar%PC_GS_KD ; CurLine=CurLine+1 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 - READ(UnControllerParameters, *) CntrPar%PC_MinRat - READ(UnControllerParameters, *) CntrPar%PC_RefSpd - READ(UnControllerParameters, *) CntrPar%PC_FinePit - READ(UnControllerParameters, *) CntrPar%PC_Switch - READ(UnControllerParameters, *) + READ(UnControllerParameters,*) CntrPar%PC_GS_TF ; CurLine=CurLine+1 + CALL ParseInput(UnControllerParameters,CurLine,'PC_MaxPit',accINFILE(1),CntrPar%PC_MaxPit,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'PC_MinPit',accINFILE(1),CntrPar%PC_MinPit,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'PC_MaxRat',accINFILE(1),CntrPar%PC_MaxRat,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'PC_MinRat',accINFILE(1),CntrPar%PC_MinRat,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'PC_RefSpd',accINFILE(1),CntrPar%PC_RefSpd,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'PC_FinePit',accINFILE(1),CntrPar%PC_FinePit,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'PC_Switch',accINFILE(1),CntrPar%PC_Switch,ErrVar) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !------------------- IPC CONSTANTS ----------------------- - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%IPC_IntSat + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'IPC_IntSat',accINFILE(1),CntrPar%IPC_IntSat,ErrVar) ALLOCATE(CntrPar%IPC_KI(2)) - READ(UnControllerParameters,*) CntrPar%IPC_KI + READ(UnControllerParameters,*) CntrPar%IPC_KI ; CurLine=CurLine+1 ALLOCATE(CntrPar%IPC_aziOffset(2)) - READ(UnControllerParameters,*) CntrPar%IPC_aziOffset - READ(UnControllerParameters, *) CntrPar%IPC_CornerFreqAct - READ(UnControllerParameters, *) + READ(UnControllerParameters,*) CntrPar%IPC_aziOffset ; CurLine=CurLine+1 + CALL ParseInput(UnControllerParameters,CurLine,'IPC_CornerFreqAct',accINFILE(1),CntrPar%IPC_CornerFreqAct,ErrVar) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !------------ VS TORQUE CONTROL CONSTANTS ---------------- - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%VS_GenEff - READ(UnControllerParameters, *) CntrPar%VS_ArSatTq - READ(UnControllerParameters, *) CntrPar%VS_MaxRat - READ(UnControllerParameters, *) CntrPar%VS_MaxTq - READ(UnControllerParameters, *) CntrPar%VS_MinTq - READ(UnControllerParameters, *) CntrPar%VS_MinOMSpd - READ(UnControllerParameters, *) CntrPar%VS_Rgn2K - READ(UnControllerParameters, *) CntrPar%VS_RtPwr - READ(UnControllerParameters, *) CntrPar%VS_RtTq - READ(UnControllerParameters, *) CntrPar%VS_RefSpd - READ(UnControllerParameters, *) CntrPar%VS_n + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'VS_GenEff',accINFILE(1),CntrPar%VS_GenEff,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_ArSatTq',accINFILE(1),CntrPar%VS_ArSatTq,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_MaxRat',accINFILE(1),CntrPar%VS_MaxRat,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_MaxTq',accINFILE(1),CntrPar%VS_MaxTq,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_MinTq',accINFILE(1),CntrPar%VS_MinTq,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_MinOMSpd',accINFILE(1),CntrPar%VS_MinOMSpd,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_Rgn2K',accINFILE(1),CntrPar%VS_Rgn2K,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_RtPwr',accINFILE(1),CntrPar%VS_RtPwr,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_RtTq',accINFILE(1),CntrPar%VS_RtTq,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_RefSpd',accINFILE(1),CntrPar%VS_RefSpd,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'VS_n',accINFILE(1),CntrPar%VS_n,ErrVar) ALLOCATE(CntrPar%VS_KP(CntrPar%VS_n)) - READ(UnControllerParameters,*) CntrPar%VS_KP + READ(UnControllerParameters,*) CntrPar%VS_KP ; CurLine=CurLine+1 ALLOCATE(CntrPar%VS_KI(CntrPar%VS_n)) - READ(UnControllerParameters,*) CntrPar%VS_KI - READ(UnControllerParameters,*) CntrPar%VS_TSRopt - READ(UnControllerParameters, *) + READ(UnControllerParameters,*) CntrPar%VS_KI ; CurLine=CurLine+1 + CALL ParseInput(UnControllerParameters,CurLine,'VS_TSRopt',accINFILE(1),CntrPar%VS_TSRopt,ErrVar) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !------- Setpoint Smoother -------------------------------- - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%SS_VSGain - READ(UnControllerParameters, *) CntrPar%SS_PCGain - READ(UnControllerParameters, *) + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'SS_VSGain',accINFILE(1),CntrPar%SS_VSGain,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'SS_PCGain',accINFILE(1),CntrPar%SS_PCGain,ErrVar) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !------------ WIND SPEED ESTIMATOR CONTANTS -------------- - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%WE_BladeRadius - READ(UnControllerParameters, *) CntrPar%WE_CP_n + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'WE_BladeRadius',accINFILE(1),CntrPar%WE_BladeRadius,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'WE_CP_n',accINFILE(1),CntrPar%WE_CP_n,ErrVar) 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 - READ(UnControllerParameters, *) CntrPar%PerfFileName + READ(UnControllerParameters, *) CntrPar%WE_CP ; CurLine=CurLine+1 + CALL ParseInput(UnControllerParameters,CurLine,'WE_Gamma',accINFILE(1),CntrPar%WE_Gamma,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'WE_GearboxRatio',accINFILE(1),CntrPar%WE_GearboxRatio,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'WE_Jtot',accINFILE(1),CntrPar%WE_Jtot,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'WE_RhoAir',accINFILE(1),CntrPar%WE_RhoAir,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'PerfFileName',accINFILE(1),CntrPar%PerfFileName,ErrVar) ALLOCATE(CntrPar%PerfTableSize(2)) - READ(UnControllerParameters, *) CntrPar%PerfTableSize - READ(UnControllerParameters, *) CntrPar%WE_FOPoles_N + READ(UnControllerParameters, *) CntrPar%PerfTableSize ; CurLine=CurLine+1 + CALL ParseInput(UnControllerParameters,CurLine,'WE_FOPoles_N',accINFILE(1),CntrPar%WE_FOPoles_N,ErrVar) ALLOCATE(CntrPar%WE_FOPoles_v(CntrPar%WE_FOPoles_n)) - READ(UnControllerParameters, *) CntrPar%WE_FOPoles_v + READ(UnControllerParameters, *) CntrPar%WE_FOPoles_v ; CurLine=CurLine+1 ALLOCATE(CntrPar%WE_FOPoles(CntrPar%WE_FOPoles_n)) - READ(UnControllerParameters, *) CntrPar%WE_FOPoles - READ(UnControllerParameters, *) + READ(UnControllerParameters, *) CntrPar%WE_FOPoles ; CurLine=CurLine+1 + CALL ReadEmptyLine(UnControllerParameters,CurLine) !-------------- YAW CONTROLLER CONSTANTS ----------------- - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%Y_ErrThresh - READ(UnControllerParameters, *) CntrPar%Y_IPC_IntSat - READ(UnControllerParameters, *) CntrPar%Y_IPC_n + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'Y_ErrThresh',accINFILE(1),CntrPar%Y_ErrThresh,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Y_IPC_IntSat',accINFILE(1),CntrPar%Y_IPC_IntSat,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Y_IPC_n',accINFILE(1),CntrPar%Y_IPC_n,ErrVar) ALLOCATE(CntrPar%Y_IPC_KP(CntrPar%Y_IPC_n)) - READ(UnControllerParameters,*) CntrPar%Y_IPC_KP + READ(UnControllerParameters,*) CntrPar%Y_IPC_KP ; CurLine=CurLine+1 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, *) + READ(UnControllerParameters,*) CntrPar%Y_IPC_KI ; CurLine=CurLine+1 + CALL ParseInput(UnControllerParameters,CurLine,'Y_IPC_omegaLP',accINFILE(1),CntrPar%Y_IPC_omegaLP,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Y_IPC_zetaLP',accINFILE(1),CntrPar%Y_IPC_zetaLP,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Y_MErrSet',accINFILE(1),CntrPar%Y_MErrSet,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Y_omegaLPFast',accINFILE(1),CntrPar%Y_omegaLPFast,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Y_omegaLPSlow',accINFILE(1),CntrPar%Y_omegaLPSlow,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Y_Rate',accINFILE(1),CntrPar%Y_Rate,ErrVar) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !------------ FORE-AFT TOWER DAMPER CONSTANTS ------------ - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%FA_KI - READ(UnControllerParameters, *) CntrPar%FA_HPFCornerFreq - READ(UnControllerParameters, *) CntrPar%FA_IntSat - READ(UnControllerParameters, *) + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'FA_KI',accINFILE(1),CntrPar%FA_KI,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'FA_HPFCornerFreq',accINFILE(1),CntrPar%FA_HPFCornerFreq,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'FA_IntSat',accINFILE(1),CntrPar%FA_IntSat,ErrVar) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !------------ PEAK SHAVING ------------ - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%PS_BldPitchMin_N + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'PS_BldPitchMin_N',accINFILE(1),CntrPar%PS_BldPitchMin_N,ErrVar) ALLOCATE(CntrPar%PS_WindSpeeds(CntrPar%PS_BldPitchMin_N)) - READ(UnControllerParameters, *) CntrPar%PS_WindSpeeds + READ(UnControllerParameters, *) CntrPar%PS_WindSpeeds ; CurLine=CurLine+1 ALLOCATE(CntrPar%PS_BldPitchMin(CntrPar%PS_BldPitchMin_N)) - READ(UnControllerParameters, *) CntrPar%PS_BldPitchMin - READ(UnControllerParameters, *) + READ(UnControllerParameters, *) CntrPar%PS_BldPitchMin ; CurLine=CurLine+1 + CALL ReadEmptyLine(UnControllerParameters,CurLine) !------------ SHUTDOWN ------------ - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%SD_MaxPit - READ(UnControllerParameters, *) CntrPar%SD_CornerFreq - READ(UnControllerParameters, *) + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'SD_MaxPit',accINFILE(1),CntrPar%SD_MaxPit,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'SD_CornerFreq',accINFILE(1),CntrPar%SD_CornerFreq,ErrVar) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !------------ FLOATING ------------ - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%Fl_Kp - READ(UnControllerParameters, *) + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'Fl_Kp',accINFILE(1),CntrPar%Fl_Kp,ErrVar) + CALL ReadEmptyLine(UnControllerParameters,CurLine) !------------ Flaps ------------ - READ(UnControllerParameters, *) - READ(UnControllerParameters, *) CntrPar%Flp_Angle - READ(UnControllerParameters, *) CntrPar%Flp_Kp - READ(UnControllerParameters, *) CntrPar%Flp_Ki - READ(UnControllerParameters, *) CntrPar%Flp_MaxPit + CALL ReadEmptyLine(UnControllerParameters,CurLine) + CALL ParseInput(UnControllerParameters,CurLine,'Flp_Angle',accINFILE(1),CntrPar%Flp_Angle,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Flp_Kp',accINFILE(1),CntrPar%Fl_Kp,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Flp_Ki',accINFILE(1),CntrPar%Flp_Ki,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Flp_MaxPit',accINFILE(1),CntrPar%Flp_MaxPit,ErrVar) ! END OF INPUT FILE + + ! Close Input File + CLOSE(UnControllerParameters) + !------------------- CALCULATED CONSTANTS ----------------------- CntrPar%PC_RtTq99 = CntrPar%VS_RtTq*0.99 CntrPar%VS_MinOMTq = CntrPar%VS_Rgn2K*CntrPar%VS_MinOMSpd**2 CntrPar%VS_MaxOMTq = CntrPar%VS_Rgn2K*CntrPar%VS_RefSpd**2 - CLOSE(UnControllerParameters) !------------------- HOUSEKEEPING ----------------------- CntrPar%PerfFileName = TRIM(CntrPar%PerfFileName) @@ -233,201 +243,6 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar ENDIF - CONTAINS - - subroutine ParseInput_Int(Un,CurLine,VarName, FileName, Variable,ErrVar) - USE ROSCO_Types, ONLY : ErrorVariables - - CHARACTER(1024) :: Line - INTEGER(4), INTENT(IN ) :: Un ! Input file unit - CHARACTER(*), INTENT(IN ) :: VarName ! Input file unit - CHARACTER(*), INTENT(IN ) :: FileName ! Input file unit - INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input - TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input - CHARACTER(20) :: Words (2) ! The two "words" parsed from the line - - INTEGER(4), INTENT(INOUT) :: Variable ! Variable - INTEGER(4) :: ErrStatLcl ! Error status local to this routine. - - ! Read the whole line as a string - READ(UnControllerParameters, '(A)') Line - - ! Separate line string into 2 words - CALL GetWords ( Line, Words, 2 ) - - ! Debugging: show what's being read - ! print *, 'Read: '//TRIM(Words(1))//' and '//Words(2),' on line ', CurLine - - ! Check that Variable Name is in Words - CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) - - ! IF We haven't failed already - IF (ErrVar%aviFAIL >= 0) THEN - - ! Read the variable - READ (Words(1),*,IOSTAT=ErrStatLcl) Variable - IF ( ErrStatLcl /= 0 ) THEN - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & - //TRIM( FileName )//'".'//NewLine// & - ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & - //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& - ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' - ENDIF - - ENDIF - - ! Increment line counter - CurLine = CurLine + 1 - - END subroutine ParseInput_Int - - subroutine ReadEmptyLine(Un,CurLine) - INTEGER(4), INTENT(IN ) :: Un ! Input file unit - INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input - - CHARACTER(1024) :: Line - - READ(UnControllerParameters, '(A)') Line - CurLine = CurLine + 1 - - END subroutine ReadEmptyLine - - !======================================================================= - !> This subroutine is used to get the NumWords "words" from a line of text. - !! It uses spaces, tabs, commas, semicolons, single quotes, and double quotes ("whitespace") - !! as word separators. If there aren't NumWords in the line, the remaining array elements will remain empty. - !! Use CountWords (nwtc_io::countwords) to count the number of words in a line. - SUBROUTINE GetWords ( Line, Words, NumWords ) - - ! Argument declarations. - - INTEGER, INTENT(IN) :: NumWords !< The number of words to look for. - - CHARACTER(*), INTENT(IN) :: Line !< The string to search. - CHARACTER(*), INTENT(OUT) :: Words(NumWords) !< The array of found words. - - - ! Local declarations. - - INTEGER :: Ch ! Character position within the string. - INTEGER :: IW ! Word index. - INTEGER :: NextWhite ! The location of the next whitespace in the string. - CHARACTER(1), PARAMETER :: Tab = CHAR( 9 ) - - - - ! Let's prefill the array with blanks. - - DO IW=1,NumWords - Words(IW) = ' ' - END DO ! IW - - - ! Let's make sure we have text on this line. - - IF ( LEN_TRIM( Line ) == 0 ) RETURN - - - ! Parse words separated by any combination of spaces, tabs, commas, - ! semicolons, single quotes, and double quotes ("whitespace"). - - Ch = 0 - IW = 0 - - DO - - NextWhite = SCAN( Line(Ch+1:) , ' ,!;''"'//Tab ) - - IF ( NextWhite > 1 ) THEN - - IW = IW + 1 - Words(IW) = Line(Ch+1:Ch+NextWhite-1) - - IF ( IW == NumWords ) EXIT - - Ch = Ch + NextWhite - - ELSE IF ( NextWhite == 1 ) THEN - - Ch = Ch + 1 - - CYCLE - - ELSE - - EXIT - - END IF - - END DO - - - RETURN - END SUBROUTINE GetWords - !======================================================================= - - !> This subroutine checks the data to be parsed to make sure it finds - !! the expected variable name and an associated value. - SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) - - USE ROSCO_Types, ONLY : ErrorVariables - - - ! Arguments declarations. - TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input - - INTEGER(4), INTENT(IN) :: FileLineNum !< The number of the line in the file being parsed. - INTEGER(4) :: NameIndx !< The index into the Words array that points to the variable name. - - CHARACTER(*), INTENT(IN) :: ExpVarName !< The expected variable name. - CHARACTER(*), INTENT(IN) :: Words (2) !< The two words to be parsed from the line. - - CHARACTER(*), INTENT(IN) :: FileName !< The name of the file being parsed. - - - ! Local declarations. - - CHARACTER(20) :: ExpUCVarName ! The uppercase version of ExpVarName. - CHARACTER(20) :: FndUCVarName ! The uppercase version of the word being tested. - - - - - ! Convert the found and expected names to uppercase. - - FndUCVarName = Words(1) - ExpUCVarName = ExpVarName - - CALL Conv2UC ( FndUCVarName ) - CALL Conv2UC ( ExpUCVarName ) - - ! See which word is the variable name. Generate an error if it is the first - - IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN - NameIndx = 1 - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & - //'".'//NewLine//' >> The variable "'//TRIM( Words(1) )//'" was not assigned a value on line #' & - //TRIM( Int2LStr( FileLineNum ) )//'.' - RETURN - ELSE - FndUCVarName = Words(2) - CALL Conv2UC ( FndUCVarName ) - IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN - NameIndx = 2 - ELSE - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & - //'".'//NewLine//' >> The variable "'//TRIM( ExpVarName )//'" was not assigned a value on line #' & - //TRIM( Int2LStr( FileLineNum ) )//'.' - RETURN - ENDIF - ENDIF - - - END SUBROUTINE ChkParseData - END SUBROUTINE ReadControlParameterFileSub ! ----------------------------------------------------------------------------------- ! Calculate setpoints for primary control actions @@ -1139,4 +954,295 @@ SUBROUTINE ReadCpFile(CntrPar,PerfData) END DO END SUBROUTINE ReadCpFile + ! Parse integer input: read line, check that variable name is in line, handle errors + subroutine ParseInput_Int(Un,CurLine,VarName, FileName, Variable,ErrVar) + USE ROSCO_Types, ONLY : ErrorVariables + + CHARACTER(1024) :: Line + INTEGER(4), INTENT(IN ) :: Un ! Input file unit + CHARACTER(*), INTENT(IN ) :: VarName ! Input file unit + CHARACTER(*), INTENT(IN ) :: FileName ! Input file unit + INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + CHARACTER(20) :: Words (2) ! The two "words" parsed from the line + + INTEGER(4), INTENT(INOUT) :: Variable ! Variable + INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + + ! Read the whole line as a string + READ(Un, '(A)') Line + + ! Separate line string into 2 words + CALL GetWords ( Line, Words, 2 ) + + ! Debugging: show what's being read, turn into Echo later + ! print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + + ! Check that Variable Name is in Words + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + + ! IF We haven't failed already + IF (ErrVar%aviFAIL >= 0) THEN + + ! Read the variable + READ (Words(1),*,IOSTAT=ErrStatLcl) Variable + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & + //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& + ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' + ENDIF + + ENDIF + + ! Increment line counter + CurLine = CurLine + 1 + + END subroutine ParseInput_Int + + ! Parse double input, this is a copy of ParseInput_Int and a change in the variable definitions + subroutine ParseInput_Dbl(Un,CurLine,VarName, FileName, Variable,ErrVar) + USE ROSCO_Types, ONLY : ErrorVariables + + CHARACTER(1024) :: Line + INTEGER(4), INTENT(IN ) :: Un ! Input file unit + CHARACTER(*), INTENT(IN ) :: VarName ! Input file unit + CHARACTER(*), INTENT(IN ) :: FileName ! Input file unit + INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + CHARACTER(20) :: Words (2) ! The two "words" parsed from the line + + REAL(8), INTENT(INOUT) :: Variable ! Variable + INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + + ! Read the whole line as a string + READ(Un, '(A)') Line + + ! Separate line string into 2 words + CALL GetWords ( Line, Words, 2 ) + + ! Debugging: show what's being read, turn into Echo later + ! print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + + ! Check that Variable Name is in Words + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + + ! IF We haven't failed already + IF (ErrVar%aviFAIL >= 0) THEN + + ! Read the variable + READ (Words(1),*,IOSTAT=ErrStatLcl) Variable + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & + //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& + ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' + ENDIF + + ENDIF + + ! Increment line counter + CurLine = CurLine + 1 + + END subroutine ParseInput_Dbl + + ! Parse string input, this is a copy of ParseInput_Int and a change in the variable definitions + subroutine ParseInput_Str(Un,CurLine,VarName, FileName, Variable,ErrVar) + USE ROSCO_Types, ONLY : ErrorVariables + + CHARACTER(1024) :: Line + INTEGER(4), INTENT(IN ) :: Un ! Input file unit + CHARACTER(*), INTENT(IN ) :: VarName ! Input file unit + CHARACTER(*), INTENT(IN ) :: FileName ! Input file unit + INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + CHARACTER(200) :: Words (2) ! The two "words" parsed from the line + + CHARACTER(*), INTENT(INOUT) :: Variable ! Variable + INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + + ! Read the whole line as a string + READ(Un, '(A)') Line + + ! Separate line string into 2 words + CALL GetWords ( Line, Words, 2 ) + + ! Debugging: show what's being read, turn into Echo later + ! print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + + ! Check that Variable Name is in Words + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + + ! IF We haven't failed already + IF (ErrVar%aviFAIL >= 0) THEN + + ! Read the variable + READ (Words(1),*,IOSTAT=ErrStatLcl) Variable + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & + //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& + ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' + ENDIF + + ENDIF + + ! Increment line counter + CurLine = CurLine + 1 + + END subroutine ParseInput_Str + + subroutine ReadEmptyLine(Un,CurLine) + INTEGER(4), INTENT(IN ) :: Un ! Input file unit + INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input + + CHARACTER(1024) :: Line + + READ(Un, '(A)') Line + CurLine = CurLine + 1 + + END subroutine ReadEmptyLine + + !======================================================================= + !> This subroutine is used to get the NumWords "words" from a line of text. + !! It uses spaces, tabs, commas, semicolons, single quotes, and double quotes ("whitespace") + !! as word separators. If there aren't NumWords in the line, the remaining array elements will remain empty. + !! Use CountWords (nwtc_io::countwords) to count the number of words in a line. + SUBROUTINE GetWords ( Line, Words, NumWords ) + + ! Argument declarations. + + INTEGER, INTENT(IN) :: NumWords !< The number of words to look for. + + CHARACTER(*), INTENT(IN) :: Line !< The string to search. + CHARACTER(*), INTENT(OUT) :: Words(NumWords) !< The array of found words. + + + ! Local declarations. + + INTEGER :: Ch ! Character position within the string. + INTEGER :: IW ! Word index. + INTEGER :: NextWhite ! The location of the next whitespace in the string. + CHARACTER(1), PARAMETER :: Tab = CHAR( 9 ) + + + + ! Let's prefill the array with blanks. + + DO IW=1,NumWords + Words(IW) = ' ' + END DO ! IW + + + ! Let's make sure we have text on this line. + + IF ( LEN_TRIM( Line ) == 0 ) RETURN + + + ! Parse words separated by any combination of spaces, tabs, commas, + ! semicolons, single quotes, and double quotes ("whitespace"). + + Ch = 0 + IW = 0 + + DO + + NextWhite = SCAN( Line(Ch+1:) , ' ,!;''"'//Tab ) + + IF ( NextWhite > 1 ) THEN + + IW = IW + 1 + Words(IW) = Line(Ch+1:Ch+NextWhite-1) + + IF ( IW == NumWords ) EXIT + + Ch = Ch + NextWhite + + ELSE IF ( NextWhite == 1 ) THEN + + Ch = Ch + 1 + + CYCLE + + ELSE + + EXIT + + END IF + + END DO + + + RETURN + END SUBROUTINE GetWords + !======================================================================= + + !> This subroutine checks the data to be parsed to make sure it finds + !! the expected variable name and an associated value. + SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) + + USE ROSCO_Types, ONLY : ErrorVariables + + + ! Arguments declarations. + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + + INTEGER(4), INTENT(IN) :: FileLineNum !< The number of the line in the file being parsed. + INTEGER(4) :: NameIndx !< The index into the Words array that points to the variable name. + + CHARACTER(*), INTENT(IN) :: ExpVarName !< The expected variable name. + CHARACTER(*), INTENT(IN) :: Words (2) !< The two words to be parsed from the line. + + CHARACTER(*), INTENT(IN) :: FileName !< The name of the file being parsed. + + + ! Local declarations. + + CHARACTER(20) :: ExpUCVarName ! The uppercase version of ExpVarName. + CHARACTER(20) :: FndUCVarName ! The uppercase version of the word being tested. + + + + + ! Convert the found and expected names to uppercase. + + FndUCVarName = Words(1) + ExpUCVarName = ExpVarName + + CALL Conv2UC ( FndUCVarName ) + CALL Conv2UC ( ExpUCVarName ) + + ! See which word is the variable name. Generate an error if it is the first + + IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN + NameIndx = 1 + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & + //'".'//NewLine//' >> The variable "'//TRIM( Words(1) )//'" was not assigned a value on line #' & + //TRIM( Int2LStr( FileLineNum ) )//'.' + RETURN + ELSE + FndUCVarName = Words(2) + CALL Conv2UC ( FndUCVarName ) + IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN + NameIndx = 2 + ELSE + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & + //'".'//NewLine//' >> The variable "'//TRIM( ExpVarName )//'" was not assigned a value on line #' & + //TRIM( Int2LStr( FileLineNum ) )//'.' + RETURN + ENDIF + ENDIF + + + END SUBROUTINE ChkParseData + + END MODULE ReadSetParameters From a275d37bf4feeda8248cf1a0a5c38c86c408f0bc Mon Sep 17 00:00:00 2001 From: nikhar-abbas <40865984+nikhar-abbas@users.noreply.github.com> Date: Thu, 22 Apr 2021 10:54:04 -0600 Subject: [PATCH 11/26] Fix setpoint smoother to use last gen pwr (#43) * fix setpoint smoother to use last gen pwr * fix setpoint smoother to use last gen pwr * revert accidentally committed flap controller debug code = --- src/ControllerBlocks.f90 | 2 +- src/Controllers.f90 | 1 + src/ROSCO_Types.f90 | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ControllerBlocks.f90 b/src/ControllerBlocks.f90 index d9613b92..ab474675 100644 --- a/src/ControllerBlocks.f90 +++ b/src/ControllerBlocks.f90 @@ -255,7 +255,7 @@ SUBROUTINE SetpointSmoother(LocalVar, CntrPar, objInst) ! ------ Setpoint Smoothing ------ IF ( CntrPar%SS_Mode == 1) THEN ! Find setpoint shift amount - DelOmega = ((LocalVar%PC_PitComT - CntrPar%PC_MinPit)/0.524) * CntrPar%SS_VSGain - ((LocalVar%VS_GenPwr - LocalVar%VS_LastGenTrq))/CntrPar%VS_RtPwr * CntrPar%SS_PCGain ! Normalize to 30 degrees for now + DelOmega = ((LocalVar%PC_PitComT - CntrPar%PC_MinPit)/0.524) * CntrPar%SS_VSGain - ((LocalVar%VS_GenPwr - LocalVar%VS_LastGenPwr))/CntrPar%VS_RtPwr * CntrPar%SS_PCGain ! Normalize to 30 degrees for now DelOmega = DelOmega * CntrPar%PC_RefSpd ! Filter LocalVar%SS_DelOmegaF = LPFilter(DelOmega, LocalVar%DT, CntrPar%F_SSCornerFreq, LocalVar%iStatus, .FALSE., objInst%instLPF) diff --git a/src/Controllers.f90 b/src/Controllers.f90 index 48ddadc7..40a876c9 100644 --- a/src/Controllers.f90 +++ b/src/Controllers.f90 @@ -195,6 +195,7 @@ SUBROUTINE VariableSpeedControl(avrSWAP, CntrPar, LocalVar, objInst) ! Reset the value of LocalVar%VS_LastGenTrq to the current values: LocalVar%VS_LastGenTrq = LocalVar%GenTq + LocalVar%VS_LastGenPwr = LocalVar%VS_GenPwr ! Set the command generator torque (See Appendix A of Bladed User's Guide): avrSWAP(47) = MAX(0.0, LocalVar%VS_LastGenTrq) ! Demanded generator torque, prevent negatives. diff --git a/src/ROSCO_Types.f90 b/src/ROSCO_Types.f90 index ba2000da..6aaf952f 100644 --- a/src/ROSCO_Types.f90 +++ b/src/ROSCO_Types.f90 @@ -178,6 +178,7 @@ MODULE ROSCO_Types REAL(8) :: TestType ! Test variable, no use REAL(8) :: VS_MaxTq ! Maximum allowable generator torque [Nm]. REAL(8) :: VS_LastGenTrq ! Commanded electrical generator torque the last time the controller was called [Nm]. + REAL(8) :: VS_LastGenPwr ! Commanded electrical generator torque the last time the controller was called [Nm]. REAL(8) :: VS_MechGenPwr ! Mechanical power on the generator axis [W] REAL(8) :: VS_SpdErrAr ! Current speed error for region 2.5 PI controller (generator torque control) [rad/s]. REAL(8) :: VS_SpdErrBr ! Current speed error for region 1.5 PI controller (generator torque control) [rad/s]. From 22b650897cd8e18470582bcdc1598c0eb58b8aea Mon Sep 17 00:00:00 2001 From: nikhar-abbas <40865984+nikhar-abbas@users.noreply.github.com> Date: Thu, 22 Apr 2021 11:36:11 -0600 Subject: [PATCH 12/26] Generator power in SS (#44) * fix setpoint smoother to use last gen pwr * fix setpoint smoother to use last gen pwr * revert accidentally committed flap controller debug code = * rated power, not current power, in setpoint smoother * Rated power instead of current power, again... * Change VS_RtPwr to CntrPar Co-authored-by: dzalkind --- src/ControllerBlocks.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ControllerBlocks.f90 b/src/ControllerBlocks.f90 index ab474675..0db9cbd7 100644 --- a/src/ControllerBlocks.f90 +++ b/src/ControllerBlocks.f90 @@ -255,7 +255,7 @@ SUBROUTINE SetpointSmoother(LocalVar, CntrPar, objInst) ! ------ Setpoint Smoothing ------ IF ( CntrPar%SS_Mode == 1) THEN ! Find setpoint shift amount - DelOmega = ((LocalVar%PC_PitComT - CntrPar%PC_MinPit)/0.524) * CntrPar%SS_VSGain - ((LocalVar%VS_GenPwr - LocalVar%VS_LastGenPwr))/CntrPar%VS_RtPwr * CntrPar%SS_PCGain ! Normalize to 30 degrees for now + DelOmega = ((LocalVar%PC_PitComT - CntrPar%PC_MinPit)/0.524) * CntrPar%SS_VSGain - ((CntrPar%VS_RtPwr - LocalVar%VS_LastGenPwr))/CntrPar%VS_RtPwr * CntrPar%SS_PCGain ! Normalize to 30 degrees for now DelOmega = DelOmega * CntrPar%PC_RefSpd ! Filter LocalVar%SS_DelOmegaF = LPFilter(DelOmega, LocalVar%DT, CntrPar%F_SSCornerFreq, LocalVar%iStatus, .FALSE., objInst%instLPF) From df32a553ad17d87d9f6b6c68bedc1d0423fdc22b Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 6 May 2021 18:42:06 -0600 Subject: [PATCH 13/26] Add array parsing, debug outputting, fix long string reading --- src/ReadSetParameters.f90 | 154 +++++++++++++++++++++++++++++++++++--- 1 file changed, 142 insertions(+), 12 deletions(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 379a24ca..dedea013 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -23,11 +23,14 @@ MODULE ReadSetParameters USE, INTRINSIC :: ISO_C_Binding -USE Constants -USE Functions + USE Constants + USE Functions IMPLICIT NONE + ! Global Variables + LOGICAL, PARAMETER :: DEBUG_PARSING = .FALSE. ! debug flag to output parsing information, set up Echo file later + INTERFACE ParseInput ! Parses a character variable name and value from a string. MODULE PROCEDURE ParseInput_Str ! Parses a character string from a string. MODULE PROCEDURE ParseInput_Dbl ! Parses a double-precision REAL from a string. @@ -35,6 +38,13 @@ MODULE ReadSetParameters ! MODULE PROCEDURE ParseInput_Log ! Parses an LOGICAL from a string. END INTERFACE +INTERFACE ParseAry ! Parse an array of numbers from a string. + MODULE PROCEDURE ParseDbAry ! Parse an array of double-precision REAL values. + ! MODULE PROCEDURE ParseInAry ! Parse an array of whole numbers. +END INTERFACE + + + CONTAINS ! ----------------------------------------------------------------------------------- ! Read all constant control parameters from DISCON.IN parameter file @@ -65,7 +75,6 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar CALL ParseInput(UnControllerParameters,CurLine,'LoggingLevel',accINFILE(1),CntrPar%LoggingLevel,ErrVar) - ! READ(UnControllerParameters, *) CntrPar%LoggingLevel CALL ReadEmptyLine(UnControllerParameters,CurLine) !----------------- CONTROLLER FLAGS --------------------- @@ -89,8 +98,10 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar CALL ParseInput(UnControllerParameters,CurLine,'F_LPFCornerFreq',accINFILE(1),CntrPar%F_LPFCornerFreq,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'F_LPFDamping',accINFILE(1),CntrPar%F_LPFDamping,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'F_NotchCornerFreq',accINFILE(1),CntrPar%F_NotchCornerFreq,ErrVar) - ALLOCATE(CntrPar%F_NotchBetaNumDen(2)) - READ(UnControllerParameters,*) CntrPar%F_NotchBetaNumDen ; CurLine=CurLine+1 + ! ALLOCATE(CntrPar%F_NotchBetaNumDen(2)) + ! READ(UnControllerParameters,*) CntrPar%F_NotchBetaNumDen ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'F_NotchBetaNumDen', CntrPar%F_NotchBetaNumDen, 2, accINFILE(1), ErrVar ) + ! Print *, 'CntrPar%F_NotchBetaNumDen:', CntrPar%F_NotchBetaNumDen CALL ParseInput(UnControllerParameters,CurLine,'F_SSCornerFreq',accINFILE(1),CntrPar%F_SSCornerFreq,ErrVar) READ(UnControllerParameters,*) CntrPar%F_FlCornerFreq, CntrPar%F_FlDamping ; CurLine=CurLine+1 READ(UnControllerParameters,*) CntrPar%F_FlpCornerFreq, CntrPar%F_FlpDamping ; CurLine=CurLine+1 @@ -865,6 +876,10 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj '------------------------------------------------------------------------------' CALL ReadControlParameterFileSub(CntrPar, accINFILE, NINT(avrSWAP(50)),ErrVar) + ! If there's been an file reading error, don't continue + IF (ErrVar%aviFAIL < 0) THEN + RETURN + ENDIF IF (CntrPar%WE_Mode > 0) THEN CALL READCpFile(CntrPar,PerfData) @@ -976,7 +991,9 @@ subroutine ParseInput_Int(Un,CurLine,VarName, FileName, Variable,ErrVar) CALL GetWords ( Line, Words, 2 ) ! Debugging: show what's being read, turn into Echo later - ! print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + IF (DEBUG_PARSING) THEN + print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + END IF ! Check that Variable Name is in Words CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) @@ -1024,7 +1041,9 @@ subroutine ParseInput_Dbl(Un,CurLine,VarName, FileName, Variable,ErrVar) CALL GetWords ( Line, Words, 2 ) ! Debugging: show what's being read, turn into Echo later - ! print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + IF (DEBUG_PARSING) THEN + print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + END IF ! Check that Variable Name is in Words CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) @@ -1072,7 +1091,9 @@ subroutine ParseInput_Str(Un,CurLine,VarName, FileName, Variable,ErrVar) CALL GetWords ( Line, Words, 2 ) ! Debugging: show what's being read, turn into Echo later - ! print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + if (DEBUG_PARSING) THEN + print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + END IF ! Check that Variable Name is in Words CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) @@ -1081,12 +1102,12 @@ subroutine ParseInput_Str(Un,CurLine,VarName, FileName, Variable,ErrVar) IF (ErrVar%aviFAIL >= 0) THEN ! Read the variable - READ (Words(1),*,IOSTAT=ErrStatLcl) Variable + READ (Words(1),'(A)',IOSTAT=ErrStatLcl) Variable IF ( ErrStatLcl /= 0 ) THEN ErrVar%aviFAIL = -1 ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & //TRIM( FileName )//'".'//NewLine// & - ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & + ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid STRING value on line #' & //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' ENDIF @@ -1224,7 +1245,7 @@ SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) NameIndx = 1 ErrVar%aviFAIL = -1 ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & - //'".'//NewLine//' >> The variable "'//TRIM( Words(1) )//'" was not assigned a value on line #' & + //'".'//NewLine//' >> The variable "'//TRIM( Words(1) )//'" was not assigned a valid value on line #' & //TRIM( Int2LStr( FileLineNum ) )//'.' RETURN ELSE @@ -1235,7 +1256,7 @@ SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) ELSE ErrVar%aviFAIL = -1 ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & - //'".'//NewLine//' >> The variable "'//TRIM( ExpVarName )//'" was not assigned a value on line #' & + //'".'//NewLine//' >> The variable "'//TRIM( ExpVarName )//'" was not assigned a valid value on line #' & //TRIM( Int2LStr( FileLineNum ) )//'.' RETURN ENDIF @@ -1244,5 +1265,114 @@ SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) END SUBROUTINE ChkParseData +!======================================================================= +!> This subroutine parses the specified line of text for AryLen REAL values. +!! Generate an error message if the value is the wrong type. +!! Use ParseAry (nwtc_io::parseary) instead of directly calling a specific routine in the generic interface. + SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) + + USE ROSCO_Types, ONLY : ErrorVariables + + ! Arguments declarations. + INTEGER(4), INTENT(IN ) :: Un ! Input file unit + INTEGER, INTENT(IN ) :: AryLen !< The length of the array to parse. + + REAL(8), ALLOCATABLE, INTENT(INOUT) :: Ary(:) !< The array to receive the input values. + + INTEGER(4), INTENT(INOUT) :: LineNum !< The number of the line to parse. + CHARACTER(*), INTENT(IN) :: FileName !< The name of the file being parsed. + + + CHARACTER(*), INTENT(In) :: AryName !< The array name we are trying to fill. + + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + + + ! Local declarations. + + CHARACTER(512) :: Line + INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + + CHARACTER(200), ALLOCATABLE :: Words_Ary (:) ! The array "words" parsed from the line. + CHARACTER(*), PARAMETER :: RoutineName = 'ParseDbAry' + + ! Read the whole line as a string + READ(Un, '(A)') Line + + Print *, Line + + ! Allocate array and handle errors + ALLOCATE ( Ary(AryLen) , STAT=ErrStatLcl ) + IF ( ErrStatLcl /= 0 ) THEN + IF ( ALLOCATED(Ary) ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = RoutineName//':Error allocating memory for the '//TRIM( AryName )//' array; array was already allocated.' + ELSE + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = RoutineName//':Error allocating memory for '//TRIM(Int2LStr( AryLen ))//' characters in the '//TRIM( AryName )//' array.' + END IF + END IF + + + + ALLOCATE ( Words_Ary( AryLen + 1 ) , STAT=ErrStatLcl ) + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = RoutineName//':Fatal error allocating memory for the Words array.' + CALL Cleanup() + RETURN + ENDIF + + ! Separate line string into AryLen + 1 words, should include variable name + CALL GetWords ( Line, Words_Ary, AryLen + 1 ) + + IF (DEBUG_PARSING) THEN + print *, 'Read: '//Words_Ary(AryLen:AryLen+1)//' on line ', LineNum + END IF + + ! Check that Variable Name is at the end of Words, will also check length of array + CALL ChkParseData ( Words_Ary(AryLen:AryLen+1), AryName, FileName, LineNum, ErrVar ) + + + READ (Line,*,IOSTAT=ErrStatLcl) Ary + IF ( ErrStatLcl /= 0 ) THEN + PRINT *, 'HERE' + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The "'//TRIM( AryName )//'" array was not assigned valid REAL values on line #' & + //TRIM( Int2LStr( LineNum ) )//'.'//NewLine//' >> The text being parsed was :'//NewLine & + //' "'//TRIM( Line )//'"' + RETURN + CALL Cleanup() + ENDIF + + ! IF ( PRESENT(UnEc) ) THEN + ! IF ( UnEc > 0 ) WRITE (UnEc,'(A)') TRIM( FileInfo%Lines(LineNum) ) + ! END IF + + LineNum = LineNum + 1 + CALL Cleanup() + + RETURN + + !======================================================================= + CONTAINS + !======================================================================= + SUBROUTINE Cleanup ( ) + + ! This subroutine cleans up the parent routine before exiting. + + ! Deallocate the Words array if it had been allocated. + + IF ( ALLOCATED( Words_Ary ) ) DEALLOCATE( Words_Ary ) + + + RETURN + + END SUBROUTINE Cleanup + + END SUBROUTINE ParseDbAry + END MODULE ReadSetParameters From d0fe8b1cf63fe05846f9e9741d37436af723fe6c Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 6 May 2021 18:46:05 -0600 Subject: [PATCH 14/26] Clean up array parsing --- src/ReadSetParameters.f90 | 42 ++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index dedea013..e4094836 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -31,17 +31,17 @@ MODULE ReadSetParameters ! Global Variables LOGICAL, PARAMETER :: DEBUG_PARSING = .FALSE. ! debug flag to output parsing information, set up Echo file later -INTERFACE ParseInput ! Parses a character variable name and value from a string. - MODULE PROCEDURE ParseInput_Str ! Parses a character string from a string. - MODULE PROCEDURE ParseInput_Dbl ! Parses a double-precision REAL from a string. - MODULE PROCEDURE ParseInput_Int ! Parses an INTEGER from a string. - ! MODULE PROCEDURE ParseInput_Log ! Parses an LOGICAL from a string. -END INTERFACE + INTERFACE ParseInput ! Parses a character variable name and value from a string. + MODULE PROCEDURE ParseInput_Str ! Parses a character string from a string. + MODULE PROCEDURE ParseInput_Dbl ! Parses a double-precision REAL from a string. + MODULE PROCEDURE ParseInput_Int ! Parses an INTEGER from a string. + ! MODULE PROCEDURE ParseInput_Log ! Parses an LOGICAL from a string. + END INTERFACE -INTERFACE ParseAry ! Parse an array of numbers from a string. - MODULE PROCEDURE ParseDbAry ! Parse an array of double-precision REAL values. - ! MODULE PROCEDURE ParseInAry ! Parse an array of whole numbers. -END INTERFACE + INTERFACE ParseAry ! Parse an array of numbers from a string. + MODULE PROCEDURE ParseDbAry ! Parse an array of double-precision REAL values. + ! MODULE PROCEDURE ParseInAry ! Parse an array of whole numbers. + END INTERFACE @@ -1277,29 +1277,27 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) INTEGER(4), INTENT(IN ) :: Un ! Input file unit INTEGER, INTENT(IN ) :: AryLen !< The length of the array to parse. - REAL(8), ALLOCATABLE, INTENT(INOUT) :: Ary(:) !< The array to receive the input values. + REAL(8), ALLOCATABLE, INTENT(INOUT) :: Ary(:) !< The array to receive the input values. - INTEGER(4), INTENT(INOUT) :: LineNum !< The number of the line to parse. - CHARACTER(*), INTENT(IN) :: FileName !< The name of the file being parsed. + INTEGER(4), INTENT(INOUT) :: LineNum !< The number of the line to parse. + CHARACTER(*), INTENT(IN) :: FileName !< The name of the file being parsed. - CHARACTER(*), INTENT(In) :: AryName !< The array name we are trying to fill. + CHARACTER(*), INTENT(IN ) :: AryName !< The array name we are trying to fill. TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input ! Local declarations. - CHARACTER(512) :: Line - INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + CHARACTER(512) :: Line + INTEGER(4) :: ErrStatLcl ! Error status local to this routine. CHARACTER(200), ALLOCATABLE :: Words_Ary (:) ! The array "words" parsed from the line. - CHARACTER(*), PARAMETER :: RoutineName = 'ParseDbAry' + CHARACTER(*), PARAMETER :: RoutineName = 'ParseDbAry' ! Read the whole line as a string READ(Un, '(A)') Line - - Print *, Line ! Allocate array and handle errors ALLOCATE ( Ary(AryLen) , STAT=ErrStatLcl ) @@ -1313,8 +1311,7 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) END IF END IF - - + ! Allocate words array ALLOCATE ( Words_Ary( AryLen + 1 ) , STAT=ErrStatLcl ) IF ( ErrStatLcl /= 0 ) THEN ErrVar%aviFAIL = -1 @@ -1333,10 +1330,9 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) ! Check that Variable Name is at the end of Words, will also check length of array CALL ChkParseData ( Words_Ary(AryLen:AryLen+1), AryName, FileName, LineNum, ErrVar ) - + ! Read array READ (Line,*,IOSTAT=ErrStatLcl) Ary IF ( ErrStatLcl /= 0 ) THEN - PRINT *, 'HERE' ErrVar%aviFAIL = -1 ErrVar%ErrMsg = 'A fatal error occurred when parsing data from "' & //TRIM( FileName )//'".'//NewLine// & From 039389abe5610ac44030ca82d57e01cdec8d95f3 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Fri, 7 May 2021 10:12:17 -0600 Subject: [PATCH 15/26] Make Flp and Fl filter variables consistent with others Clean up parsing echo for arrays --- src/Controllers.f90 | 8 ++++---- src/Filters.f90 | 5 ++--- src/ROSCO_Types.f90 | 6 ++---- src/ReadSetParameters.f90 | 29 ++++++++++++++++++----------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/Controllers.f90 b/src/Controllers.f90 index 504fed56..7368aac3 100644 --- a/src/Controllers.f90 +++ b/src/Controllers.f90 @@ -405,9 +405,9 @@ SUBROUTINE FlapControl(avrSWAP, CntrPar, LocalVar, objInst) LocalVar%Flp_Angle(2) = CntrPar%Flp_Angle LocalVar%Flp_Angle(3) = CntrPar%Flp_Angle ! Initialize filter - RootMOOP_F(1) = SecLPFilter(LocalVar%rootMOOP(1),LocalVar%DT, CntrPar%F_FlpCornerFreq, CntrPar%F_FlpDamping, LocalVar%iStatus, .FALSE.,objInst%instSecLPF) - RootMOOP_F(2) = SecLPFilter(LocalVar%rootMOOP(2),LocalVar%DT, CntrPar%F_FlpCornerFreq, CntrPar%F_FlpDamping, LocalVar%iStatus, .FALSE.,objInst%instSecLPF) - RootMOOP_F(3) = SecLPFilter(LocalVar%rootMOOP(3),LocalVar%DT, CntrPar%F_FlpCornerFreq, CntrPar%F_FlpDamping, LocalVar%iStatus, .FALSE.,objInst%instSecLPF) + RootMOOP_F(1) = SecLPFilter(LocalVar%rootMOOP(1),LocalVar%DT, CntrPar%F_FlpCornerFreq(1), CntrPar%F_FlpCornerFreq(2), LocalVar%iStatus, .FALSE.,objInst%instSecLPF) + RootMOOP_F(2) = SecLPFilter(LocalVar%rootMOOP(2),LocalVar%DT, CntrPar%F_FlpCornerFreq(1), CntrPar%F_FlpCornerFreq(2), LocalVar%iStatus, .FALSE.,objInst%instSecLPF) + RootMOOP_F(3) = SecLPFilter(LocalVar%rootMOOP(3),LocalVar%DT, CntrPar%F_FlpCornerFreq(1), CntrPar%F_FlpCornerFreq(2), LocalVar%iStatus, .FALSE.,objInst%instSecLPF) ! Initialize controller IF (CntrPar%Flp_Mode == 2) THEN LocalVar%Flp_Angle(K) = PIIController(RootMyb_VelErr(K), 0 - LocalVar%Flp_Angle(K), CntrPar%Flp_Kp, CntrPar%Flp_Ki, 0.05, -CntrPar%Flp_MaxPit , CntrPar%Flp_MaxPit , LocalVar%DT, 0.0, .TRUE., objInst%instPI) @@ -428,7 +428,7 @@ SUBROUTINE FlapControl(avrSWAP, CntrPar, LocalVar, objInst) ELSEIF (CntrPar%Flp_Mode == 2) THEN DO K = 1,LocalVar%NumBl ! LPF Blade root bending moment - RootMOOP_F(K) = SecLPFilter(LocalVar%rootMOOP(K),LocalVar%DT, CntrPar%F_FlpCornerFreq, CntrPar%F_FlpDamping, LocalVar%iStatus, .FALSE.,objInst%instSecLPF) + RootMOOP_F(K) = SecLPFilter(LocalVar%rootMOOP(K),LocalVar%DT, CntrPar%F_FlpCornerFreq(1), CntrPar%F_FlpCornerFreq(2), LocalVar%iStatus, .FALSE.,objInst%instSecLPF) ! Find derivative and derivative error of blade root bending moment RootMyb_Vel(K) = (RootMOOP_F(K) - RootMyb_Last(K))/LocalVar%DT diff --git a/src/Filters.f90 b/src/Filters.f90 index d32e7f19..ad4d8a0a 100644 --- a/src/Filters.f90 +++ b/src/Filters.f90 @@ -276,12 +276,11 @@ SUBROUTINE PreFilterMeasuredSignals(CntrPar, LocalVar, objInst) IF (CntrPar%Fl_Mode == 1) THEN ! Force to start at 0 IF (LocalVar%iStatus == 0) THEN - LocalVar%NacIMU_FA_AccF = SecLPFilter(0., LocalVar%DT, CntrPar%F_FlCornerFreq, CntrPar%F_FlDamping, LocalVar%iStatus, .FALSE., objInst%instSecLPF) ! Fixed Damping + LocalVar%NacIMU_FA_AccF = SecLPFilter(0., LocalVar%DT, CntrPar%F_FlCornerFreq(1), CntrPar%F_FlCornerFreq(2), LocalVar%iStatus, .FALSE., objInst%instSecLPF) ! Fixed Damping ELSE - LocalVar%NacIMU_FA_AccF = SecLPFilter(LocalVar%NacIMU_FA_Acc, LocalVar%DT, CntrPar%F_FlCornerFreq, CntrPar%F_FlDamping, LocalVar%iStatus, .FALSE., objInst%instSecLPF) ! Fixed Damping + LocalVar%NacIMU_FA_AccF = SecLPFilter(LocalVar%NacIMU_FA_Acc, LocalVar%DT, CntrPar%F_FlCornerFreq(1), CntrPar%F_FlCornerFreq(2), LocalVar%iStatus, .FALSE., objInst%instSecLPF) ! Fixed Damping ENDIF LocalVar%NacIMU_FA_AccF = HPFilter(LocalVar%NacIMU_FA_AccF, LocalVar%DT, 0.0167, LocalVar%iStatus, .FALSE., objInst%instHPF) - ! LocalVar%NacIMU_FA_AccF = NotchFilterSlopes(LocalVar%NacIMU_FA_Acc, LocalVar%DT, CntrPar%F_FlCornerFreq, CntrPar%F_FlDamping, LocalVar%iStatus, .FALSE., objInst%instNotchSlopes) ! Fixed Damping IF (CntrPar%F_NotchType >= 2) THEN LocalVar%NACIMU_FA_AccF = NotchFilter(LocalVar%NacIMU_FA_AccF, LocalVar%DT, CntrPar%F_NotchCornerFreq, CntrPar%F_NotchBetaNumDen(1), CntrPar%F_NotchBetaNumDen(2), LocalVar%iStatus, .FALSE., objInst%instNotch) ! Fixed Damping diff --git a/src/ROSCO_Types.f90 b/src/ROSCO_Types.f90 index 332dbc53..a8507e4e 100644 --- a/src/ROSCO_Types.f90 +++ b/src/ROSCO_Types.f90 @@ -33,10 +33,8 @@ MODULE ROSCO_Types REAL(8) :: F_NotchCornerFreq ! Natural frequency of the notch filter, [rad/s] REAL(8), DIMENSION(:), ALLOCATABLE :: F_NotchBetaNumDen ! These two notch damping values (numerator and denominator) determines the width and depth of the notch REAL(8) :: F_SSCornerFreq ! Setpoint Smoother mode {0: no setpoint smoothing, 1: introduce setpoint smoothing} - REAL(8) :: F_FlCornerFreq ! Corner frequency (-3dB point) in the second order low pass filter of the tower-top fore-aft motion for floating feedback control [rad/s]. - REAL(8) :: F_FlDamping ! Damping constant in the first order low pass filter of the tower-top fore-aft motion for floating feedback control [-]. - REAL(8) :: F_FlpCornerFreq ! Corner frequency (-3dB point) in the second order low pass filter of the blade root bending moment for flap control [rad/s]. - REAL(8) :: F_FlpDamping ! Damping constant in the first order low pass filter of the blade root bending moment for flap control[-]. + REAL(8), DIMENSION(:), ALLOCATABLE :: F_FlCornerFreq ! Corner frequency (-3dB point) in the second order low pass filter of the tower-top fore-aft motion for floating feedback control [rad/s]. + REAL(8), DIMENSION(:), ALLOCATABLE :: F_FlpCornerFreq ! Corner frequency (-3dB point) in the second order low pass filter of the blade root bending moment for flap control [rad/s]. REAL(8) :: FA_HPFCornerFreq ! Corner frequency (-3dB point) in the high-pass filter on the fore-aft acceleration signal [rad/s] REAL(8) :: FA_IntSat ! Integrator saturation (maximum signal amplitude contrbution to pitch from FA damper), [rad] diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index e4094836..b0d80d2f 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -29,7 +29,7 @@ MODULE ReadSetParameters IMPLICIT NONE ! Global Variables - LOGICAL, PARAMETER :: DEBUG_PARSING = .FALSE. ! debug flag to output parsing information, set up Echo file later + LOGICAL, PARAMETER :: DEBUG_PARSING = .TRUE. ! debug flag to output parsing information, set up Echo file later INTERFACE ParseInput ! Parses a character variable name and value from a string. MODULE PROCEDURE ParseInput_Str ! Parses a character string from a string. @@ -98,13 +98,10 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar CALL ParseInput(UnControllerParameters,CurLine,'F_LPFCornerFreq',accINFILE(1),CntrPar%F_LPFCornerFreq,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'F_LPFDamping',accINFILE(1),CntrPar%F_LPFDamping,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'F_NotchCornerFreq',accINFILE(1),CntrPar%F_NotchCornerFreq,ErrVar) - ! ALLOCATE(CntrPar%F_NotchBetaNumDen(2)) - ! READ(UnControllerParameters,*) CntrPar%F_NotchBetaNumDen ; CurLine=CurLine+1 CALL ParseAry(UnControllerParameters, CurLine, 'F_NotchBetaNumDen', CntrPar%F_NotchBetaNumDen, 2, accINFILE(1), ErrVar ) - ! Print *, 'CntrPar%F_NotchBetaNumDen:', CntrPar%F_NotchBetaNumDen CALL ParseInput(UnControllerParameters,CurLine,'F_SSCornerFreq',accINFILE(1),CntrPar%F_SSCornerFreq,ErrVar) - READ(UnControllerParameters,*) CntrPar%F_FlCornerFreq, CntrPar%F_FlDamping ; CurLine=CurLine+1 - READ(UnControllerParameters,*) CntrPar%F_FlpCornerFreq, CntrPar%F_FlpDamping ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'F_FlCornerFreq', CntrPar%F_FlCornerFreq, 2, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'F_FlpCornerFreq', CntrPar%F_FlpCornerFreq, 2, accINFILE(1), ErrVar ) CALL ReadEmptyLine(UnControllerParameters,CurLine) !----------- BLADE PITCH CONTROLLER CONSTANTS ----------- @@ -520,25 +517,25 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) ENDIF ! F_FlCornerFreq(1) (frequency) - IF (CntrPar%F_FlCornerFreq <= 0.0) THEN + IF (CntrPar%F_FlCornerFreq(1) <= 0.0) THEN ErrVar%aviFAIL = -1 ErrVar%ErrMsg = 'F_FlCornerFreq(1) must be greater than zero.' ENDIF ! F_FlCornerFreq(2) (damping) - IF (CntrPar%F_FlDamping <= 0.0) THEN + IF (CntrPar%F_FlCornerFreq(2) <= 0.0) THEN ErrVar%aviFAIL = -1 ErrVar%ErrMsg = 'F_FlCornerFreq(2) must be greater than zero.' ENDIF ! F_FlpCornerFreq(1) (frequency) - IF (CntrPar%F_FlCornerFreq <= 0.0) THEN + IF (CntrPar%F_FlpCornerFreq(1) <= 0.0) THEN ErrVar%aviFAIL = -1 ErrVar%ErrMsg = 'F_FlpCornerFreq(1) must be greater than zero.' ENDIF ! F_FlpCornerFreq(2) (damping) - IF (CntrPar%F_FlDamping <= 0.0) THEN + IF (CntrPar%F_FlpCornerFreq(2) <= 0.0) THEN ErrVar%aviFAIL = -1 ErrVar%ErrMsg = 'F_FlpCornerFreq(2) must be greater than zero.' ENDIF @@ -1292,8 +1289,10 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) CHARACTER(512) :: Line INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + INTEGER(4) :: i CHARACTER(200), ALLOCATABLE :: Words_Ary (:) ! The array "words" parsed from the line. + CHARACTER(1024) :: Debug_String CHARACTER(*), PARAMETER :: RoutineName = 'ParseDbAry' ! Read the whole line as a string @@ -1323,8 +1322,16 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) ! Separate line string into AryLen + 1 words, should include variable name CALL GetWords ( Line, Words_Ary, AryLen + 1 ) + ! Debug Output IF (DEBUG_PARSING) THEN - print *, 'Read: '//Words_Ary(AryLen:AryLen+1)//' on line ', LineNum + Debug_String = '' + DO i = 1,AryLen+1 + Debug_String = TRIM(Debug_String)//TRIM(Words_Ary(i)) + IF (i < AryLen + 1) THEN + Debug_String = TRIM(Debug_String)//',' + END IF + END DO + print *, 'Read: '//TRIM(Debug_String)//' on line ', LineNum END IF ! Check that Variable Name is at the end of Words, will also check length of array From 97c7bcf61b635067cac815a236aea47f108d8a2a Mon Sep 17 00:00:00 2001 From: dzalkind Date: Fri, 7 May 2021 10:52:11 -0600 Subject: [PATCH 16/26] Parse all double arrays, only read/parse lines if no error --- src/ReadSetParameters.f90 | 325 +++++++++++++++++++------------------- 1 file changed, 162 insertions(+), 163 deletions(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index b0d80d2f..9f0a24de 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -107,16 +107,11 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar !----------- BLADE PITCH CONTROLLER CONSTANTS ----------- CALL ReadEmptyLine(UnControllerParameters,CurLine) CALL ParseInput(UnControllerParameters,CurLine,'PC_GS_n',accINFILE(1),CntrPar%PC_GS_n,ErrVar) - ALLOCATE(CntrPar%PC_GS_angles(CntrPar%PC_GS_n)) - READ(UnControllerParameters,*) CntrPar%PC_GS_angles ; CurLine=CurLine+1 - ALLOCATE(CntrPar%PC_GS_KP(CntrPar%PC_GS_n)) - READ(UnControllerParameters,*) CntrPar%PC_GS_KP ; CurLine=CurLine+1 - ALLOCATE(CntrPar%PC_GS_KI(CntrPar%PC_GS_n)) - READ(UnControllerParameters,*) CntrPar%PC_GS_KI ; CurLine=CurLine+1 - ALLOCATE(CntrPar%PC_GS_KD(CntrPar%PC_GS_n)) - READ(UnControllerParameters,*) CntrPar%PC_GS_KD ; CurLine=CurLine+1 - ALLOCATE(CntrPar%PC_GS_TF(CntrPar%PC_GS_n)) - READ(UnControllerParameters,*) CntrPar%PC_GS_TF ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'PC_GS_angles', CntrPar%PC_GS_angles, CntrPar%PC_GS_n, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'PC_GS_KP', CntrPar%PC_GS_KP, CntrPar%PC_GS_n, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'PC_GS_KI', CntrPar%PC_GS_KI, CntrPar%PC_GS_n, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'PC_GS_KD', CntrPar%PC_GS_KD, CntrPar%PC_GS_n, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'PC_GS_TF', CntrPar%PC_GS_TF, CntrPar%PC_GS_n, accINFILE(1), ErrVar ) CALL ParseInput(UnControllerParameters,CurLine,'PC_MaxPit',accINFILE(1),CntrPar%PC_MaxPit,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'PC_MinPit',accINFILE(1),CntrPar%PC_MinPit,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'PC_MaxRat',accINFILE(1),CntrPar%PC_MaxRat,ErrVar) @@ -129,10 +124,8 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar !------------------- IPC CONSTANTS ----------------------- CALL ReadEmptyLine(UnControllerParameters,CurLine) CALL ParseInput(UnControllerParameters,CurLine,'IPC_IntSat',accINFILE(1),CntrPar%IPC_IntSat,ErrVar) - ALLOCATE(CntrPar%IPC_KI(2)) - READ(UnControllerParameters,*) CntrPar%IPC_KI ; CurLine=CurLine+1 - ALLOCATE(CntrPar%IPC_aziOffset(2)) - READ(UnControllerParameters,*) CntrPar%IPC_aziOffset ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'IPC_KI', CntrPar%IPC_KI, 2, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'IPC_aziOffset', CntrPar%IPC_aziOffset, 2, accINFILE(1), ErrVar ) CALL ParseInput(UnControllerParameters,CurLine,'IPC_CornerFreqAct',accINFILE(1),CntrPar%IPC_CornerFreqAct,ErrVar) CALL ReadEmptyLine(UnControllerParameters,CurLine) @@ -149,10 +142,8 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar CALL ParseInput(UnControllerParameters,CurLine,'VS_RtTq',accINFILE(1),CntrPar%VS_RtTq,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'VS_RefSpd',accINFILE(1),CntrPar%VS_RefSpd,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'VS_n',accINFILE(1),CntrPar%VS_n,ErrVar) - ALLOCATE(CntrPar%VS_KP(CntrPar%VS_n)) - READ(UnControllerParameters,*) CntrPar%VS_KP ; CurLine=CurLine+1 - ALLOCATE(CntrPar%VS_KI(CntrPar%VS_n)) - READ(UnControllerParameters,*) CntrPar%VS_KI ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'VS_KP', CntrPar%VS_KP, CntrPar%VS_n, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'VS_KI', CntrPar%VS_KI, CntrPar%VS_n, accINFILE(1), ErrVar ) CALL ParseInput(UnControllerParameters,CurLine,'VS_TSRopt',accINFILE(1),CntrPar%VS_TSRopt,ErrVar) CALL ReadEmptyLine(UnControllerParameters,CurLine) @@ -166,8 +157,7 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar CALL ReadEmptyLine(UnControllerParameters,CurLine) CALL ParseInput(UnControllerParameters,CurLine,'WE_BladeRadius',accINFILE(1),CntrPar%WE_BladeRadius,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'WE_CP_n',accINFILE(1),CntrPar%WE_CP_n,ErrVar) - ALLOCATE(CntrPar%WE_CP(CntrPar%WE_CP_n)) - READ(UnControllerParameters, *) CntrPar%WE_CP ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'WE_CP', CntrPar%WE_CP, CntrPar%WE_CP_n, accINFILE(1), ErrVar ) CALL ParseInput(UnControllerParameters,CurLine,'WE_Gamma',accINFILE(1),CntrPar%WE_Gamma,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'WE_GearboxRatio',accINFILE(1),CntrPar%WE_GearboxRatio,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'WE_Jtot',accINFILE(1),CntrPar%WE_Jtot,ErrVar) @@ -176,10 +166,8 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar ALLOCATE(CntrPar%PerfTableSize(2)) READ(UnControllerParameters, *) CntrPar%PerfTableSize ; CurLine=CurLine+1 CALL ParseInput(UnControllerParameters,CurLine,'WE_FOPoles_N',accINFILE(1),CntrPar%WE_FOPoles_N,ErrVar) - ALLOCATE(CntrPar%WE_FOPoles_v(CntrPar%WE_FOPoles_n)) - READ(UnControllerParameters, *) CntrPar%WE_FOPoles_v ; CurLine=CurLine+1 - ALLOCATE(CntrPar%WE_FOPoles(CntrPar%WE_FOPoles_n)) - READ(UnControllerParameters, *) CntrPar%WE_FOPoles ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'WE_FOPoles_v', CntrPar%WE_FOPoles_v, CntrPar%WE_FOPoles_N, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'WE_FOPoles', CntrPar%WE_FOPoles, CntrPar%WE_FOPoles_N, accINFILE(1), ErrVar ) CALL ReadEmptyLine(UnControllerParameters,CurLine) !-------------- YAW CONTROLLER CONSTANTS ----------------- @@ -187,10 +175,8 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar CALL ParseInput(UnControllerParameters,CurLine,'Y_ErrThresh',accINFILE(1),CntrPar%Y_ErrThresh,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'Y_IPC_IntSat',accINFILE(1),CntrPar%Y_IPC_IntSat,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'Y_IPC_n',accINFILE(1),CntrPar%Y_IPC_n,ErrVar) - ALLOCATE(CntrPar%Y_IPC_KP(CntrPar%Y_IPC_n)) - READ(UnControllerParameters,*) CntrPar%Y_IPC_KP ; CurLine=CurLine+1 - ALLOCATE(CntrPar%Y_IPC_KI(CntrPar%Y_IPC_n)) - READ(UnControllerParameters,*) CntrPar%Y_IPC_KI ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'Y_IPC_KP', CntrPar%Y_IPC_KP, CntrPar%Y_IPC_n, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'Y_IPC_KI', CntrPar%Y_IPC_KI, CntrPar%Y_IPC_n, accINFILE(1), ErrVar ) CALL ParseInput(UnControllerParameters,CurLine,'Y_IPC_omegaLP',accINFILE(1),CntrPar%Y_IPC_omegaLP,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'Y_IPC_zetaLP',accINFILE(1),CntrPar%Y_IPC_zetaLP,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'Y_MErrSet',accINFILE(1),CntrPar%Y_MErrSet,ErrVar) @@ -209,10 +195,8 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar !------------ PEAK SHAVING ------------ CALL ReadEmptyLine(UnControllerParameters,CurLine) CALL ParseInput(UnControllerParameters,CurLine,'PS_BldPitchMin_N',accINFILE(1),CntrPar%PS_BldPitchMin_N,ErrVar) - ALLOCATE(CntrPar%PS_WindSpeeds(CntrPar%PS_BldPitchMin_N)) - READ(UnControllerParameters, *) CntrPar%PS_WindSpeeds ; CurLine=CurLine+1 - ALLOCATE(CntrPar%PS_BldPitchMin(CntrPar%PS_BldPitchMin_N)) - READ(UnControllerParameters, *) CntrPar%PS_BldPitchMin ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'PS_WindSpeeds', CntrPar%PS_WindSpeeds, CntrPar%PS_BldPitchMin_N, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'PS_BldPitchMin', CntrPar%PS_BldPitchMin, CntrPar%PS_BldPitchMin_N, accINFILE(1), ErrVar ) CALL ReadEmptyLine(UnControllerParameters,CurLine) !------------ SHUTDOWN ------------ @@ -981,38 +965,42 @@ subroutine ParseInput_Int(Un,CurLine,VarName, FileName, Variable,ErrVar) INTEGER(4), INTENT(INOUT) :: Variable ! Variable INTEGER(4) :: ErrStatLcl ! Error status local to this routine. - ! Read the whole line as a string - READ(Un, '(A)') Line + ! If we've already failed, don't read anything + IF (ErrVar%aviFAIL >= 0) THEN - ! Separate line string into 2 words - CALL GetWords ( Line, Words, 2 ) + ! Read the whole line as a string + READ(Un, '(A)') Line - ! Debugging: show what's being read, turn into Echo later - IF (DEBUG_PARSING) THEN - print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine - END IF + ! Separate line string into 2 words + CALL GetWords ( Line, Words, 2 ) - ! Check that Variable Name is in Words - CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + ! Debugging: show what's being read, turn into Echo later + IF (DEBUG_PARSING) THEN + print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + END IF - ! IF We haven't failed already - IF (ErrVar%aviFAIL >= 0) THEN + ! Check that Variable Name is in Words + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) - ! Read the variable - READ (Words(1),*,IOSTAT=ErrStatLcl) Variable - IF ( ErrStatLcl /= 0 ) THEN - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & - //TRIM( FileName )//'".'//NewLine// & - ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & - //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& - ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' - ENDIF + ! IF We haven't failed already + IF (ErrVar%aviFAIL >= 0) THEN - ENDIF + ! Read the variable + READ (Words(1),*,IOSTAT=ErrStatLcl) Variable + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & + //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& + ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' + ENDIF - ! Increment line counter - CurLine = CurLine + 1 + ENDIF + + ! Increment line counter + CurLine = CurLine + 1 + END IF END subroutine ParseInput_Int @@ -1031,38 +1019,42 @@ subroutine ParseInput_Dbl(Un,CurLine,VarName, FileName, Variable,ErrVar) REAL(8), INTENT(INOUT) :: Variable ! Variable INTEGER(4) :: ErrStatLcl ! Error status local to this routine. - ! Read the whole line as a string - READ(Un, '(A)') Line + ! If we've already failed, don't read anything + IF (ErrVar%aviFAIL >= 0) THEN - ! Separate line string into 2 words - CALL GetWords ( Line, Words, 2 ) + ! Read the whole line as a string + READ(Un, '(A)') Line - ! Debugging: show what's being read, turn into Echo later - IF (DEBUG_PARSING) THEN - print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine - END IF + ! Separate line string into 2 words + CALL GetWords ( Line, Words, 2 ) - ! Check that Variable Name is in Words - CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + ! Debugging: show what's being read, turn into Echo later + IF (DEBUG_PARSING) THEN + print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + END IF - ! IF We haven't failed already - IF (ErrVar%aviFAIL >= 0) THEN + ! Check that Variable Name is in Words + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) - ! Read the variable - READ (Words(1),*,IOSTAT=ErrStatLcl) Variable - IF ( ErrStatLcl /= 0 ) THEN - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & - //TRIM( FileName )//'".'//NewLine// & - ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & - //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& - ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' - ENDIF + ! IF We haven't failed already + IF (ErrVar%aviFAIL >= 0) THEN - ENDIF + ! Read the variable + READ (Words(1),*,IOSTAT=ErrStatLcl) Variable + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid INTEGER value on line #' & + //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& + ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' + ENDIF - ! Increment line counter - CurLine = CurLine + 1 + ENDIF + + ! Increment line counter + CurLine = CurLine + 1 + END IF END subroutine ParseInput_Dbl @@ -1081,38 +1073,42 @@ subroutine ParseInput_Str(Un,CurLine,VarName, FileName, Variable,ErrVar) CHARACTER(*), INTENT(INOUT) :: Variable ! Variable INTEGER(4) :: ErrStatLcl ! Error status local to this routine. - ! Read the whole line as a string - READ(Un, '(A)') Line + ! If we've already failed, don't read anything + IF (ErrVar%aviFAIL >= 0) THEN - ! Separate line string into 2 words - CALL GetWords ( Line, Words, 2 ) + ! Read the whole line as a string + READ(Un, '(A)') Line - ! Debugging: show what's being read, turn into Echo later - if (DEBUG_PARSING) THEN - print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine - END IF + ! Separate line string into 2 words + CALL GetWords ( Line, Words, 2 ) - ! Check that Variable Name is in Words - CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + ! Debugging: show what's being read, turn into Echo later + if (DEBUG_PARSING) THEN + print *, 'Read: '//TRIM(Words(1))//' and '//TRIM(Words(2)),' on line ', CurLine + END IF - ! IF We haven't failed already - IF (ErrVar%aviFAIL >= 0) THEN + ! Check that Variable Name is in Words + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) - ! Read the variable - READ (Words(1),'(A)',IOSTAT=ErrStatLcl) Variable - IF ( ErrStatLcl /= 0 ) THEN - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & - //TRIM( FileName )//'".'//NewLine// & - ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid STRING value on line #' & - //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& - ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' - ENDIF + ! IF We haven't failed already + IF (ErrVar%aviFAIL >= 0) THEN - ENDIF + ! Read the variable + READ (Words(1),'(A)',IOSTAT=ErrStatLcl) Variable + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = NewLine//' >> A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The variable "'//TRIM( Words(2) )//'" was not assigned valid STRING value on line #' & + //TRIM( Int2LStr( CurLine ) )//'.'//NewLine//& + ' >> The text being parsed was :'//NewLine//' "'//TRIM( Line )//'"' + ENDIF - ! Increment line counter - CurLine = CurLine + 1 + ENDIF + + ! Increment line counter + CurLine = CurLine + 1 + END IF END subroutine ParseInput_Str @@ -1287,7 +1283,7 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) ! Local declarations. - CHARACTER(512) :: Line + CHARACTER(1024) :: Line INTEGER(4) :: ErrStatLcl ! Error status local to this routine. INTEGER(4) :: i @@ -1295,68 +1291,71 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) CHARACTER(1024) :: Debug_String CHARACTER(*), PARAMETER :: RoutineName = 'ParseDbAry' - ! Read the whole line as a string - READ(Un, '(A)') Line - - ! Allocate array and handle errors - ALLOCATE ( Ary(AryLen) , STAT=ErrStatLcl ) - IF ( ErrStatLcl /= 0 ) THEN - IF ( ALLOCATED(Ary) ) THEN - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = RoutineName//':Error allocating memory for the '//TRIM( AryName )//' array; array was already allocated.' - ELSE + ! If we've already failed, don't read anything + IF (ErrVar%aviFAIL >= 0) THEN + ! Read the whole line as a string + READ(Un, '(A)') Line + + ! Allocate array and handle errors + ALLOCATE ( Ary(AryLen) , STAT=ErrStatLcl ) + IF ( ErrStatLcl /= 0 ) THEN + IF ( ALLOCATED(Ary) ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = RoutineName//':Error allocating memory for the '//TRIM( AryName )//' array; array was already allocated.' + ELSE + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = RoutineName//':Error allocating memory for '//TRIM(Int2LStr( AryLen ))//' characters in the '//TRIM( AryName )//' array.' + END IF + END IF + + ! Allocate words array + ALLOCATE ( Words_Ary( AryLen + 1 ) , STAT=ErrStatLcl ) + IF ( ErrStatLcl /= 0 ) THEN ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = RoutineName//':Error allocating memory for '//TRIM(Int2LStr( AryLen ))//' characters in the '//TRIM( AryName )//' array.' + ErrVar%ErrMsg = RoutineName//':Fatal error allocating memory for the Words array.' + CALL Cleanup() + RETURN + ENDIF + + ! Separate line string into AryLen + 1 words, should include variable name + CALL GetWords ( Line, Words_Ary, AryLen + 1 ) + + ! Debug Output + IF (DEBUG_PARSING) THEN + Debug_String = '' + DO i = 1,AryLen+1 + Debug_String = TRIM(Debug_String)//TRIM(Words_Ary(i)) + IF (i < AryLen + 1) THEN + Debug_String = TRIM(Debug_String)//',' + END IF + END DO + print *, 'Read: '//TRIM(Debug_String)//' on line ', LineNum END IF - END IF - - ! Allocate words array - ALLOCATE ( Words_Ary( AryLen + 1 ) , STAT=ErrStatLcl ) - IF ( ErrStatLcl /= 0 ) THEN - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = RoutineName//':Fatal error allocating memory for the Words array.' - CALL Cleanup() - RETURN - ENDIF - ! Separate line string into AryLen + 1 words, should include variable name - CALL GetWords ( Line, Words_Ary, AryLen + 1 ) + ! Check that Variable Name is at the end of Words, will also check length of array + CALL ChkParseData ( Words_Ary(AryLen:AryLen+1), AryName, FileName, LineNum, ErrVar ) + + ! Read array + READ (Line,*,IOSTAT=ErrStatLcl) Ary + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = 'A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The "'//TRIM( AryName )//'" array was not assigned valid REAL values on line #' & + //TRIM( Int2LStr( LineNum ) )//'.'//NewLine//' >> The text being parsed was :'//NewLine & + //' "'//TRIM( Line )//'"' + RETURN + CALL Cleanup() + ENDIF - ! Debug Output - IF (DEBUG_PARSING) THEN - Debug_String = '' - DO i = 1,AryLen+1 - Debug_String = TRIM(Debug_String)//TRIM(Words_Ary(i)) - IF (i < AryLen + 1) THEN - Debug_String = TRIM(Debug_String)//',' - END IF - END DO - print *, 'Read: '//TRIM(Debug_String)//' on line ', LineNum - END IF + ! IF ( PRESENT(UnEc) ) THEN + ! IF ( UnEc > 0 ) WRITE (UnEc,'(A)') TRIM( FileInfo%Lines(LineNum) ) + ! END IF - ! Check that Variable Name is at the end of Words, will also check length of array - CALL ChkParseData ( Words_Ary(AryLen:AryLen+1), AryName, FileName, LineNum, ErrVar ) - - ! Read array - READ (Line,*,IOSTAT=ErrStatLcl) Ary - IF ( ErrStatLcl /= 0 ) THEN - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = 'A fatal error occurred when parsing data from "' & - //TRIM( FileName )//'".'//NewLine// & - ' >> The "'//TRIM( AryName )//'" array was not assigned valid REAL values on line #' & - //TRIM( Int2LStr( LineNum ) )//'.'//NewLine//' >> The text being parsed was :'//NewLine & - //' "'//TRIM( Line )//'"' - RETURN - CALL Cleanup() + LineNum = LineNum + 1 + CALL Cleanup() ENDIF - ! IF ( PRESENT(UnEc) ) THEN - ! IF ( UnEc > 0 ) WRITE (UnEc,'(A)') TRIM( FileInfo%Lines(LineNum) ) - ! END IF - - LineNum = LineNum + 1 - CALL Cleanup() - RETURN !======================================================================= From 05879d54bd991162581c8fc5f3f2201d3043e707 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Fri, 7 May 2021 10:57:48 -0600 Subject: [PATCH 17/26] Parse integer arrays --- src/ReadSetParameters.f90 | 125 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 4 deletions(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 9f0a24de..b9db3111 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -40,7 +40,7 @@ MODULE ReadSetParameters INTERFACE ParseAry ! Parse an array of numbers from a string. MODULE PROCEDURE ParseDbAry ! Parse an array of double-precision REAL values. - ! MODULE PROCEDURE ParseInAry ! Parse an array of whole numbers. + MODULE PROCEDURE ParseInAry ! Parse an array of whole numbers. END INTERFACE @@ -163,8 +163,7 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar CALL ParseInput(UnControllerParameters,CurLine,'WE_Jtot',accINFILE(1),CntrPar%WE_Jtot,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'WE_RhoAir',accINFILE(1),CntrPar%WE_RhoAir,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'PerfFileName',accINFILE(1),CntrPar%PerfFileName,ErrVar) - ALLOCATE(CntrPar%PerfTableSize(2)) - READ(UnControllerParameters, *) CntrPar%PerfTableSize ; CurLine=CurLine+1 + CALL ParseAry(UnControllerParameters, CurLine, 'PerfTableSize', CntrPar%PerfTableSize, 2, accINFILE(1), ErrVar ) CALL ParseInput(UnControllerParameters,CurLine,'WE_FOPoles_N',accINFILE(1),CntrPar%WE_FOPoles_N,ErrVar) CALL ParseAry(UnControllerParameters, CurLine, 'WE_FOPoles_v', CntrPar%WE_FOPoles_v, CntrPar%WE_FOPoles_N, accINFILE(1), ErrVar ) CALL ParseAry(UnControllerParameters, CurLine, 'WE_FOPoles', CntrPar%WE_FOPoles, CntrPar%WE_FOPoles_N, accINFILE(1), ErrVar ) @@ -1339,7 +1338,7 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) READ (Line,*,IOSTAT=ErrStatLcl) Ary IF ( ErrStatLcl /= 0 ) THEN ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = 'A fatal error occurred when parsing data from "' & + ErrVar%ErrMsg = RoutineName//'A fatal error occurred when parsing data from "' & //TRIM( FileName )//'".'//NewLine// & ' >> The "'//TRIM( AryName )//'" array was not assigned valid REAL values on line #' & //TRIM( Int2LStr( LineNum ) )//'.'//NewLine//' >> The text being parsed was :'//NewLine & @@ -1376,5 +1375,123 @@ END SUBROUTINE Cleanup END SUBROUTINE ParseDbAry + !======================================================================= +!> This subroutine parses the specified line of text for AryLen INTEGER values. +!! Generate an error message if the value is the wrong type. +!! Use ParseAry (nwtc_io::parseary) instead of directly calling a specific routine in the generic interface. + SUBROUTINE ParseInAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) + + USE ROSCO_Types, ONLY : ErrorVariables + + ! Arguments declarations. + INTEGER(4), INTENT(IN ) :: Un ! Input file unit + INTEGER, INTENT(IN ) :: AryLen !< The length of the array to parse. + + INTEGER(4), ALLOCATABLE, INTENT(INOUT) :: Ary(:) !< The array to receive the input values. + + INTEGER(4), INTENT(INOUT) :: LineNum !< The number of the line to parse. + CHARACTER(*), INTENT(IN) :: FileName !< The name of the file being parsed. + + + CHARACTER(*), INTENT(IN ) :: AryName !< The array name we are trying to fill. + + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + + + ! Local declarations. + + CHARACTER(1024) :: Line + INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + INTEGER(4) :: i + + CHARACTER(200), ALLOCATABLE :: Words_Ary (:) ! The array "words" parsed from the line. + CHARACTER(1024) :: Debug_String + CHARACTER(*), PARAMETER :: RoutineName = 'ParseInAry' + + ! If we've already failed, don't read anything + IF (ErrVar%aviFAIL >= 0) THEN + ! Read the whole line as a string + READ(Un, '(A)') Line + + ! Allocate array and handle errors + ALLOCATE ( Ary(AryLen) , STAT=ErrStatLcl ) + IF ( ErrStatLcl /= 0 ) THEN + IF ( ALLOCATED(Ary) ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = RoutineName//':Error allocating memory for the '//TRIM( AryName )//' array; array was already allocated.' + ELSE + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = RoutineName//':Error allocating memory for '//TRIM(Int2LStr( AryLen ))//' characters in the '//TRIM( AryName )//' array.' + END IF + END IF + + ! Allocate words array + ALLOCATE ( Words_Ary( AryLen + 1 ) , STAT=ErrStatLcl ) + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = RoutineName//':Fatal error allocating memory for the Words array.' + CALL Cleanup() + RETURN + ENDIF + + ! Separate line string into AryLen + 1 words, should include variable name + CALL GetWords ( Line, Words_Ary, AryLen + 1 ) + + ! Debug Output + IF (DEBUG_PARSING) THEN + Debug_String = '' + DO i = 1,AryLen+1 + Debug_String = TRIM(Debug_String)//TRIM(Words_Ary(i)) + IF (i < AryLen + 1) THEN + Debug_String = TRIM(Debug_String)//',' + END IF + END DO + print *, 'Read: '//TRIM(Debug_String)//' on line ', LineNum + END IF + + ! Check that Variable Name is at the end of Words, will also check length of array + CALL ChkParseData ( Words_Ary(AryLen:AryLen+1), AryName, FileName, LineNum, ErrVar ) + + ! Read array + READ (Line,*,IOSTAT=ErrStatLcl) Ary + IF ( ErrStatLcl /= 0 ) THEN + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = RoutineName//'A fatal error occurred when parsing data from "' & + //TRIM( FileName )//'".'//NewLine// & + ' >> The "'//TRIM( AryName )//'" array was not assigned valid REAL values on line #' & + //TRIM( Int2LStr( LineNum ) )//'.'//NewLine//' >> The text being parsed was :'//NewLine & + //' "'//TRIM( Line )//'"' + RETURN + CALL Cleanup() + ENDIF + + ! IF ( PRESENT(UnEc) ) THEN + ! IF ( UnEc > 0 ) WRITE (UnEc,'(A)') TRIM( FileInfo%Lines(LineNum) ) + ! END IF + + LineNum = LineNum + 1 + CALL Cleanup() + ENDIF + + RETURN + + !======================================================================= + CONTAINS + !======================================================================= + SUBROUTINE Cleanup ( ) + + ! This subroutine cleans up the parent routine before exiting. + + ! Deallocate the Words array if it had been allocated. + + IF ( ALLOCATED( Words_Ary ) ) DEALLOCATE( Words_Ary ) + + + RETURN + + END SUBROUTINE Cleanup + +END SUBROUTINE ParseInAry + END MODULE ReadSetParameters From 8fdeeca31aac87105978cc3c65e3ce1652959f60 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Mon, 10 May 2021 17:00:13 -0600 Subject: [PATCH 18/26] Read Cp surface --- src/ReadSetParameters.f90 | 99 +++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index b9db3111..6b4d4784 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -29,7 +29,7 @@ MODULE ReadSetParameters IMPLICIT NONE ! Global Variables - LOGICAL, PARAMETER :: DEBUG_PARSING = .TRUE. ! debug flag to output parsing information, set up Echo file later + LOGICAL, PARAMETER :: DEBUG_PARSING = .FALSE. ! debug flag to output parsing information, set up Echo file later INTERFACE ParseInput ! Parses a character variable name and value from a string. MODULE PROCEDURE ParseInput_Str ! Parses a character string from a string. @@ -858,11 +858,12 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj CALL ReadControlParameterFileSub(CntrPar, accINFILE, NINT(avrSWAP(50)),ErrVar) ! If there's been an file reading error, don't continue IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = 'SetParameters:'//TRIM(ErrVar%ErrMsg) RETURN ENDIF IF (CntrPar%WE_Mode > 0) THEN - CALL READCpFile(CntrPar,PerfData) + CALL READCpFile(CntrPar,PerfData,ErrVar) ENDIF ! Initialize testValue (debugging variable) LocalVar%TestType = 0 @@ -900,53 +901,65 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj END SUBROUTINE SetParameters ! ----------------------------------------------------------------------------------- ! Read all constant control parameters from DISCON.IN parameter file - SUBROUTINE ReadCpFile(CntrPar,PerfData) - USE ROSCO_Types, ONLY : PerformanceData, ControlParameters + SUBROUTINE ReadCpFile(CntrPar,PerfData, ErrVar) + USE ROSCO_Types, ONLY : PerformanceData, ControlParameters, ErrorVariables - INTEGER(4), PARAMETER :: UnPerfParameters = 89 - TYPE(PerformanceData), INTENT(INOUT) :: PerfData - TYPE(ControlParameters), INTENT(INOUT) :: CntrPar + TYPE(PerformanceData), INTENT(INOUT) :: PerfData + TYPE(ControlParameters), INTENT(INOUT) :: CntrPar + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar + ! Local variables - INTEGER(4) :: i ! iteration index + INTEGER(4), PARAMETER :: UnPerfParameters = 89 + INTEGER(4) :: i ! iteration index + + INTEGER(4) :: CurLine + CHARACTER(*), PARAMETER :: RoutineName = 'ReadCpFile' + REAL(8), DIMENSION(:), ALLOCATABLE :: TmpPerf + + CurLine = 1 + OPEN(unit=UnPerfParameters, file=TRIM(CntrPar%PerfFileName), status='old', action='read') ! Should put input file into DISCON.IN ! ----------------------- Axis Definitions ------------------------ - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) - ALLOCATE(PerfData%Beta_vec(CntrPar%PerfTableSize(1))) - READ(UnPerfParameters, *) PerfData%Beta_vec - READ(UnPerfParameters, *) - ALLOCATE(PerfData%TSR_vec(CntrPar%PerfTableSize(2))) - READ(UnPerfParameters, *) PerfData%TSR_vec + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ParseAry(UnPerfParameters, CurLine, 'Pitch angle vector', PerfData%Beta_vec, CntrPar%PerfTableSize(1), TRIM(CntrPar%PerfFileName), ErrVar, .FALSE.) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ParseAry(UnPerfParameters, CurLine, 'TSR vector', PerfData%TSR_vec, CntrPar%PerfTableSize(2), TRIM(CntrPar%PerfFileName), ErrVar, .FALSE.) ! ----------------------- Read Cp, Ct, Cq, Tables ------------------------ - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) ! Input file should contains wind speed information here - unneeded for now - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) ! Input file should contains wind speed information here - unneeded for now + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) ALLOCATE(PerfData%Cp_mat(CntrPar%PerfTableSize(2),CntrPar%PerfTableSize(1))) DO i = 1,CntrPar%PerfTableSize(2) READ(UnPerfParameters, *) PerfData%Cp_mat(i,:) ! Read Cp table END DO - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) ALLOCATE(PerfData%Ct_mat(CntrPar%PerfTableSize(1),CntrPar%PerfTableSize(2))) DO i = 1,CntrPar%PerfTableSize(2) READ(UnPerfParameters, *) PerfData%Ct_mat(i,:) ! Read Ct table END DO - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) - READ(UnPerfParameters, *) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) ALLOCATE(PerfData%Cq_mat(CntrPar%PerfTableSize(1),CntrPar%PerfTableSize(2))) DO i = 1,CntrPar%PerfTableSize(2) READ(UnPerfParameters, *) PerfData%Cq_mat(i,:) ! Read Cq table END DO + + ! Add RoutineName to error message + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) + ENDIF END SUBROUTINE ReadCpFile ! Parse integer input: read line, check that variable name is in line, handle errors @@ -1261,7 +1274,7 @@ END SUBROUTINE ChkParseData !> This subroutine parses the specified line of text for AryLen REAL values. !! Generate an error message if the value is the wrong type. !! Use ParseAry (nwtc_io::parseary) instead of directly calling a specific routine in the generic interface. - SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) + SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar, CheckName ) USE ROSCO_Types, ONLY : ErrorVariables @@ -1279,6 +1292,8 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + LOGICAL, OPTIONAL, INTENT(IN ) :: CheckName + ! Local declarations. @@ -1289,6 +1304,11 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) CHARACTER(200), ALLOCATABLE :: Words_Ary (:) ! The array "words" parsed from the line. CHARACTER(1024) :: Debug_String CHARACTER(*), PARAMETER :: RoutineName = 'ParseDbAry' + LOGICAL :: CheckName_ + + ! Figure out if we're checking the name, default to .TRUE. + CheckName_ = .TRUE. + if (PRESENT(CheckName)) CheckName_ = CheckName ! If we've already failed, don't read anything IF (ErrVar%aviFAIL >= 0) THEN @@ -1332,7 +1352,9 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) END IF ! Check that Variable Name is at the end of Words, will also check length of array - CALL ChkParseData ( Words_Ary(AryLen:AryLen+1), AryName, FileName, LineNum, ErrVar ) + IF (CheckName_) THEN + CALL ChkParseData ( Words_Ary(AryLen:AryLen+1), AryName, FileName, LineNum, ErrVar ) + END IF ! Read array READ (Line,*,IOSTAT=ErrStatLcl) Ary @@ -1379,7 +1401,7 @@ END SUBROUTINE ParseDbAry !> This subroutine parses the specified line of text for AryLen INTEGER values. !! Generate an error message if the value is the wrong type. !! Use ParseAry (nwtc_io::parseary) instead of directly calling a specific routine in the generic interface. - SUBROUTINE ParseInAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) + SUBROUTINE ParseInAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar, CheckName ) USE ROSCO_Types, ONLY : ErrorVariables @@ -1397,6 +1419,7 @@ SUBROUTINE ParseInAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + LOGICAL, OPTIONAL, INTENT(IN ) :: CheckName ! Local declarations. @@ -1408,6 +1431,12 @@ SUBROUTINE ParseInAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) CHARACTER(1024) :: Debug_String CHARACTER(*), PARAMETER :: RoutineName = 'ParseInAry' + LOGICAL :: CheckName_ + + ! Figure out if we're checking the name, default to .TRUE. + CheckName_ = .TRUE. + if (PRESENT(CheckName)) CheckName_ = CheckName + ! If we've already failed, don't read anything IF (ErrVar%aviFAIL >= 0) THEN ! Read the whole line as a string @@ -1450,7 +1479,9 @@ SUBROUTINE ParseInAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar ) END IF ! Check that Variable Name is at the end of Words, will also check length of array - CALL ChkParseData ( Words_Ary(AryLen:AryLen+1), AryName, FileName, LineNum, ErrVar ) + IF (CheckName_) THEN + CALL ChkParseData ( Words_Ary(AryLen:AryLen+1), AryName, FileName, LineNum, ErrVar ) + END IF ! Read array READ (Line,*,IOSTAT=ErrStatLcl) Ary From 7fbfb865e8ec0fa746d949873b9d78124da65524 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Mon, 17 May 2021 16:46:06 -0600 Subject: [PATCH 19/26] Reorganize ROSCO file reading code --- src/ControllerBlocks.f90 | 65 +++++ src/ReadSetParameters.f90 | 489 ++++++++++++++++---------------------- 2 files changed, 272 insertions(+), 282 deletions(-) diff --git a/src/ControllerBlocks.f90 b/src/ControllerBlocks.f90 index 4432ea44..19707598 100644 --- a/src/ControllerBlocks.f90 +++ b/src/ControllerBlocks.f90 @@ -31,6 +31,71 @@ MODULE ControllerBlocks IMPLICIT NONE CONTAINS +! ----------------------------------------------------------------------------------- + ! Calculate setpoints for primary control actions + SUBROUTINE ComputeVariablesSetpoints(CntrPar, LocalVar, objInst) + USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances + USE Constants + ! Allocate variables + TYPE(ControlParameters), INTENT(INOUT) :: CntrPar + TYPE(LocalVariables), INTENT(INOUT) :: LocalVar + TYPE(ObjectInstances), INTENT(INOUT) :: objInst + + REAL(8) :: VS_RefSpd ! Referece speed for variable speed torque controller, [rad/s] + REAL(8) :: PC_RefSpd ! Referece speed for pitch controller, [rad/s] + REAL(8) :: Omega_op ! Optimal TSR-tracking generator speed, [rad/s] + ! temp + ! REAL(8) :: VS_TSRop = 7.5 + + ! ----- Calculate yaw misalignment error ----- + LocalVar%Y_MErr = LocalVar%Y_M + CntrPar%Y_MErrSet ! Yaw-alignment error + + ! ----- Pitch controller speed and power error ----- + ! Implement setpoint smoothing + IF (LocalVar%SS_DelOmegaF < 0) THEN + PC_RefSpd = CntrPar%PC_RefSpd - LocalVar%SS_DelOmegaF + ELSE + PC_RefSpd = CntrPar%PC_RefSpd + ENDIF + + LocalVar%PC_SpdErr = PC_RefSpd - LocalVar%GenSpeedF ! Speed error + LocalVar%PC_PwrErr = CntrPar%VS_RtPwr - LocalVar%VS_GenPwr ! Power error + + ! ----- Torque controller reference errors ----- + ! Define VS reference generator speed [rad/s] + IF ((CntrPar%VS_ControlMode == 2) .OR. (CntrPar%VS_ControlMode == 3)) THEN + VS_RefSpd = (CntrPar%VS_TSRopt * LocalVar%We_Vw_F / CntrPar%WE_BladeRadius) * CntrPar%WE_GearboxRatio + VS_RefSpd = saturate(VS_RefSpd,CntrPar%VS_MinOMSpd, CntrPar%VS_RefSpd) + ELSE + VS_RefSpd = CntrPar%VS_RefSpd + ENDIF + + ! Implement setpoint smoothing + IF (LocalVar%SS_DelOmegaF > 0) THEN + VS_RefSpd = VS_RefSpd - LocalVar%SS_DelOmegaF + ENDIF + + ! Force zero torque in shutdown mode + IF (LocalVar%SD) THEN + VS_RefSpd = CntrPar%VS_MinOMSpd + ENDIF + + ! Force minimum rotor speed + VS_RefSpd = max(VS_RefSpd, CntrPar%VS_MinOmSpd) + + ! TSR-tracking reference error + IF ((CntrPar%VS_ControlMode == 2) .OR. (CntrPar%VS_ControlMode == 3)) THEN + LocalVar%VS_SpdErr = VS_RefSpd - LocalVar%GenSpeedF + ENDIF + + ! Define transition region setpoint errors + LocalVar%VS_SpdErrAr = VS_RefSpd - LocalVar%GenSpeedF ! Current speed error - Region 2.5 PI-control (Above Rated) + LocalVar%VS_SpdErrBr = CntrPar%VS_MinOMSpd - LocalVar%GenSpeedF ! Current speed error - Region 1.5 PI-control (Below Rated) + + ! Region 3 minimum pitch angle for state machine + LocalVar%VS_Rgn3Pitch = LocalVar%PC_MinPit + CntrPar%PC_Switch + + END SUBROUTINE ComputeVariablesSetpoints !------------------------------------------------------------------------------------------------------------------------------- SUBROUTINE StateMachine(CntrPar, LocalVar) ! State machine, determines the state of the wind turbine to specify the corresponding control actions diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 6b4d4784..2b2e043c 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -46,6 +46,150 @@ MODULE ReadSetParameters CONTAINS + ! ----------------------------------------------------------------------------------- + ! Read avrSWAP array passed from ServoDyn + SUBROUTINE ReadAvrSWAP(avrSWAP, LocalVar) + USE ROSCO_Types, ONLY : LocalVariables + + REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. + TYPE(LocalVariables), INTENT(INOUT) :: LocalVar + + ! Load variables from calling program (See Appendix A of Bladed User's Guide): + LocalVar%iStatus = NINT(avrSWAP(1)) + LocalVar%Time = avrSWAP(2) + LocalVar%DT = avrSWAP(3) + LocalVar%VS_MechGenPwr = avrSWAP(14) + LocalVar%VS_GenPwr = avrSWAP(15) + LocalVar%GenSpeed = avrSWAP(20) + LocalVar%RotSpeed = avrSWAP(21) + LocalVar%GenTqMeas = avrSWAP(23) + LocalVar%Y_M = avrSWAP(24) + LocalVar%HorWindV = avrSWAP(27) + LocalVar%rootMOOP(1) = avrSWAP(30) + LocalVar%rootMOOP(2) = avrSWAP(31) + LocalVar%rootMOOP(3) = avrSWAP(32) + LocalVar%FA_Acc = avrSWAP(53) + LocalVar%NacIMU_FA_Acc = avrSWAP(83) + LocalVar%Azimuth = avrSWAP(60) + LocalVar%NumBl = NINT(avrSWAP(61)) + + ! --- NJA: usually feedback back the previous pitch command helps for numerical stability, sometimes it does not... + IF (LocalVar%iStatus == 0) THEN + LocalVar%BlPitch(1) = avrSWAP(4) + LocalVar%BlPitch(2) = avrSWAP(33) + LocalVar%BlPitch(3) = avrSWAP(34) + ELSE + LocalVar%BlPitch(1) = LocalVar%PitCom(1) + LocalVar%BlPitch(2) = LocalVar%PitCom(2) + LocalVar%BlPitch(3) = LocalVar%PitCom(3) + ENDIF + + END SUBROUTINE ReadAvrSWAP +! ----------------------------------------------------------------------------------- + ! Define parameters for control actions + SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, objInst, PerfData, ErrVar) + + USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances, PerformanceData, ErrorVariables + + INTEGER(4), INTENT(IN) :: size_avcMSG + TYPE(ControlParameters), INTENT(INOUT) :: CntrPar + TYPE(LocalVariables), INTENT(INOUT) :: LocalVar + TYPE(ObjectInstances), INTENT(INOUT) :: objInst + TYPE(PerformanceData), INTENT(INOUT) :: PerfData + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar + + REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. + CHARACTER(KIND=C_CHAR), INTENT(IN) :: accINFILE(NINT(avrSWAP(50))) ! The name of the parameter input file + + INTEGER(4) :: K ! Index used for looping through blades. + CHARACTER(200) :: git_version + + ! Error Catching Variables + ! Set ErrVar%aviFAIL to 0 in each iteration: + ErrVar%aviFAIL = 0 + ! ALLOCATE(ErrVar%ErrMsg(size_avcMSG-1)) + ErrVar%size_avcMSG = size_avcMSG + + ! Initialize all filter instance counters at 1 + objInst%instLPF = 1 + objInst%instSecLPF = 1 + objInst%instHPF = 1 + objInst%instNotchSlopes = 1 + objInst%instNotch = 1 + objInst%instPI = 1 + + ! Set unused outputs to zero (See Appendix A of Bladed User's Guide): + avrSWAP(35) = 1.0 ! Generator contactor status: 1=main (high speed) variable-speed generator + avrSWAP(36) = 0.0 ! Shaft brake status: 0=off + avrSWAP(41) = 0.0 ! Demanded yaw actuator torque + avrSWAP(46) = 0.0 ! Demanded pitch rate (Collective pitch) + avrSWAP(55) = 0.0 ! Pitch override: 0=yes + avrSWAP(56) = 0.0 ! Torque override: 0=yes + avrSWAP(65) = 0.0 ! Number of variables returned for logging + avrSWAP(72) = 0.0 ! Generator start-up resistance + avrSWAP(79) = 0.0 ! Request for loads: 0=none + avrSWAP(80) = 0.0 ! Variable slip current status + avrSWAP(81) = 0.0 ! Variable slip current demand + + ! Read any External Controller Parameters specified in the User Interface + ! and initialize variables: + IF (LocalVar%iStatus == 0) THEN ! .TRUE. if we're on the first call to the DLL + + ! Inform users that we are using this user-defined routine: + ! ErrVar%aviFAIL = 1 + git_version = QueryGitVersion() + PRINT *,' '//NEW_LINE('A')// & + '------------------------------------------------------------------------------'//NEW_LINE('A')// & + 'Running ROSCO-'//TRIM(git_version)//NEW_LINE('A')// & + 'A wind turbine controller framework for public use in the scientific field '//NEW_LINE('A')// & + 'Developed in collaboration: National Renewable Energy Laboratory '//NEW_LINE('A')// & + ' Delft University of Technology, The Netherlands '//NEW_LINE('A')// & + '------------------------------------------------------------------------------' + + CALL ReadControlParameterFileSub(CntrPar, accINFILE, NINT(avrSWAP(50)),ErrVar) + ! If there's been an file reading error, don't continue + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = 'SetParameters:'//TRIM(ErrVar%ErrMsg) + RETURN + ENDIF + + IF (CntrPar%WE_Mode > 0) THEN + CALL READCpFile(CntrPar,PerfData,ErrVar) + ENDIF + ! Initialize testValue (debugging variable) + LocalVar%TestType = 0 + + ! Initialize the SAVED variables: + LocalVar%PitCom = LocalVar%BlPitch ! This will ensure that the variable speed controller picks the correct control region and the pitch controller picks the correct gain on the first call + LocalVar%Y_AccErr = 0.0 ! This will ensure that the accumulated yaw error starts at zero + LocalVar%Y_YawEndT = -1.0 ! This will ensure that the initial yaw end time is lower than the actual time to prevent initial yawing + + ! Wind speed estimator initialization + LocalVar%WE_Vw = LocalVar%HorWindV + LocalVar%WE_VwI = LocalVar%WE_Vw - CntrPar%WE_Gamma*LocalVar%RotSpeed + + ! Setpoint Smoother initialization to zero + LocalVar%SS_DelOmegaF = 0 + + ! Generator Torque at K omega^2 or rated + IF (LocalVar%GenSpeed > 0.98 * CntrPar%PC_RefSpd) THEN + LocalVar%GenTq = CntrPar%VS_RtTq + ELSE + LocalVar%GenTq = min(CntrPar%VS_RtTq, CntrPar%VS_Rgn2K*LocalVar%GenSpeed*LocalVar%GenSpeed) + ENDIF + LocalVar%VS_LastGenTrq = LocalVar%GenTq + LocalVar%VS_MaxTq = CntrPar%VS_MaxTq + + ! Check validity of input parameters: + CALL CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) + + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = 'SetParameters:'//TRIM(ErrVar%ErrMsg) + ENDIF + + + ENDIF + END SUBROUTINE SetParameters ! ----------------------------------------------------------------------------------- ! Read all constant control parameters from DISCON.IN parameter file SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar)!, accINFILE_size) @@ -236,110 +380,6 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar END SUBROUTINE ReadControlParameterFileSub ! ----------------------------------------------------------------------------------- - ! Calculate setpoints for primary control actions - SUBROUTINE ComputeVariablesSetpoints(CntrPar, LocalVar, objInst) - USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances - USE Constants - ! Allocate variables - TYPE(ControlParameters), INTENT(INOUT) :: CntrPar - TYPE(LocalVariables), INTENT(INOUT) :: LocalVar - TYPE(ObjectInstances), INTENT(INOUT) :: objInst - - REAL(8) :: VS_RefSpd ! Referece speed for variable speed torque controller, [rad/s] - REAL(8) :: PC_RefSpd ! Referece speed for pitch controller, [rad/s] - REAL(8) :: Omega_op ! Optimal TSR-tracking generator speed, [rad/s] - ! temp - ! REAL(8) :: VS_TSRop = 7.5 - - ! ----- Calculate yaw misalignment error ----- - LocalVar%Y_MErr = LocalVar%Y_M + CntrPar%Y_MErrSet ! Yaw-alignment error - - ! ----- Pitch controller speed and power error ----- - ! Implement setpoint smoothing - IF (LocalVar%SS_DelOmegaF < 0) THEN - PC_RefSpd = CntrPar%PC_RefSpd - LocalVar%SS_DelOmegaF - ELSE - PC_RefSpd = CntrPar%PC_RefSpd - ENDIF - - LocalVar%PC_SpdErr = PC_RefSpd - LocalVar%GenSpeedF ! Speed error - LocalVar%PC_PwrErr = CntrPar%VS_RtPwr - LocalVar%VS_GenPwr ! Power error - - ! ----- Torque controller reference errors ----- - ! Define VS reference generator speed [rad/s] - IF ((CntrPar%VS_ControlMode == 2) .OR. (CntrPar%VS_ControlMode == 3)) THEN - VS_RefSpd = (CntrPar%VS_TSRopt * LocalVar%We_Vw_F / CntrPar%WE_BladeRadius) * CntrPar%WE_GearboxRatio - VS_RefSpd = saturate(VS_RefSpd,CntrPar%VS_MinOMSpd, CntrPar%VS_RefSpd) - ELSE - VS_RefSpd = CntrPar%VS_RefSpd - ENDIF - - ! Implement setpoint smoothing - IF (LocalVar%SS_DelOmegaF > 0) THEN - VS_RefSpd = VS_RefSpd - LocalVar%SS_DelOmegaF - ENDIF - - ! Force zero torque in shutdown mode - IF (LocalVar%SD) THEN - VS_RefSpd = CntrPar%VS_MinOMSpd - ENDIF - - ! Force minimum rotor speed - VS_RefSpd = max(VS_RefSpd, CntrPar%VS_MinOmSpd) - - ! TSR-tracking reference error - IF ((CntrPar%VS_ControlMode == 2) .OR. (CntrPar%VS_ControlMode == 3)) THEN - LocalVar%VS_SpdErr = VS_RefSpd - LocalVar%GenSpeedF - ENDIF - - ! Define transition region setpoint errors - LocalVar%VS_SpdErrAr = VS_RefSpd - LocalVar%GenSpeedF ! Current speed error - Region 2.5 PI-control (Above Rated) - LocalVar%VS_SpdErrBr = CntrPar%VS_MinOMSpd - LocalVar%GenSpeedF ! Current speed error - Region 1.5 PI-control (Below Rated) - - ! Region 3 minimum pitch angle for state machine - LocalVar%VS_Rgn3Pitch = LocalVar%PC_MinPit + CntrPar%PC_Switch - - END SUBROUTINE ComputeVariablesSetpoints - ! ----------------------------------------------------------------------------------- - ! Read avrSWAP array passed from ServoDyn - SUBROUTINE ReadAvrSWAP(avrSWAP, LocalVar) - USE ROSCO_Types, ONLY : LocalVariables - - REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. - TYPE(LocalVariables), INTENT(INOUT) :: LocalVar - - ! Load variables from calling program (See Appendix A of Bladed User's Guide): - LocalVar%iStatus = NINT(avrSWAP(1)) - LocalVar%Time = avrSWAP(2) - LocalVar%DT = avrSWAP(3) - LocalVar%VS_MechGenPwr = avrSWAP(14) - LocalVar%VS_GenPwr = avrSWAP(15) - LocalVar%GenSpeed = avrSWAP(20) - LocalVar%RotSpeed = avrSWAP(21) - LocalVar%GenTqMeas = avrSWAP(23) - LocalVar%Y_M = avrSWAP(24) - LocalVar%HorWindV = avrSWAP(27) - LocalVar%rootMOOP(1) = avrSWAP(30) - LocalVar%rootMOOP(2) = avrSWAP(31) - LocalVar%rootMOOP(3) = avrSWAP(32) - LocalVar%FA_Acc = avrSWAP(53) - LocalVar%NacIMU_FA_Acc = avrSWAP(83) - LocalVar%Azimuth = avrSWAP(60) - LocalVar%NumBl = NINT(avrSWAP(61)) - - ! --- NJA: usually feedback back the previous pitch command helps for numerical stability, sometimes it does not... - IF (LocalVar%iStatus == 0) THEN - LocalVar%BlPitch(1) = avrSWAP(4) - LocalVar%BlPitch(2) = avrSWAP(33) - LocalVar%BlPitch(3) = avrSWAP(34) - ELSE - LocalVar%BlPitch(1) = LocalVar%PitCom(1) - LocalVar%BlPitch(2) = LocalVar%PitCom(2) - LocalVar%BlPitch(3) = LocalVar%PitCom(3) - ENDIF - - END SUBROUTINE ReadAvrSWAP - ! ----------------------------------------------------------------------------------- ! Check for errors before any execution SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) USE, INTRINSIC :: ISO_C_Binding @@ -782,123 +822,7 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) ENDIF END SUBROUTINE CheckInputs - ! ----------------------------------------------------------------------------------- - ! Define parameters for control actions - SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, objInst, PerfData, ErrVar) - - USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances, PerformanceData, ErrorVariables - - INTEGER(4), INTENT(IN) :: size_avcMSG - TYPE(ControlParameters), INTENT(INOUT) :: CntrPar - TYPE(LocalVariables), INTENT(INOUT) :: LocalVar - TYPE(ObjectInstances), INTENT(INOUT) :: objInst - TYPE(PerformanceData), INTENT(INOUT) :: PerfData - TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar - REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. - CHARACTER(KIND=C_CHAR), INTENT(IN) :: accINFILE(NINT(avrSWAP(50))) ! The name of the parameter input file - - INTEGER(4) :: K ! Index used for looping through blades. - CHARACTER(200) :: git_version - - ! Error Catching Variables - ! Set ErrVar%aviFAIL to 0 in each iteration: - ErrVar%aviFAIL = 0 - ! ALLOCATE(ErrVar%ErrMsg(size_avcMSG-1)) - ErrVar%size_avcMSG = size_avcMSG - - ! Initialize all filter instance counters at 1 - objInst%instLPF = 1 - objInst%instSecLPF = 1 - objInst%instHPF = 1 - objInst%instNotchSlopes = 1 - objInst%instNotch = 1 - objInst%instPI = 1 - - ! Set unused outputs to zero (See Appendix A of Bladed User's Guide): - avrSWAP(35) = 1.0 ! Generator contactor status: 1=main (high speed) variable-speed generator - avrSWAP(36) = 0.0 ! Shaft brake status: 0=off - avrSWAP(41) = 0.0 ! Demanded yaw actuator torque - avrSWAP(46) = 0.0 ! Demanded pitch rate (Collective pitch) - avrSWAP(55) = 0.0 ! Pitch override: 0=yes - avrSWAP(56) = 0.0 ! Torque override: 0=yes - avrSWAP(65) = 0.0 ! Number of variables returned for logging - avrSWAP(72) = 0.0 ! Generator start-up resistance - avrSWAP(79) = 0.0 ! Request for loads: 0=none - avrSWAP(80) = 0.0 ! Variable slip current status - avrSWAP(81) = 0.0 ! Variable slip current demand - - ! Read any External Controller Parameters specified in the User Interface - ! and initialize variables: - IF (LocalVar%iStatus == 0) THEN ! .TRUE. if we're on the first call to the DLL - - ! Inform users that we are using this user-defined routine: - ! ErrVar%aviFAIL = 1 - git_version = QueryGitVersion() - ! ErrMsg = ' '//NEW_LINE('A')// & - ! '------------------------------------------------------------------------------'//NEW_LINE('A')// & - ! 'Running a controller implemented through NREL''s ROSCO Toolbox '//NEW_LINE('A')// & - ! 'A wind turbine controller framework for public use in the scientific field '//NEW_LINE('A')// & - ! 'Developed in collaboration: National Renewable Energy Laboratory '//NEW_LINE('A')// & - ! ' Delft University of Technology, The Netherlands '//NEW_LINE('A')// & - ! 'Primary development by (listed alphabetically): Nikhar J. Abbas '//NEW_LINE('A')// & - ! ' Sebastiaan P. Mulders '//NEW_LINE('A')// & - ! ' Jan-Willem van Wingerden '//NEW_LINE('A')// & - ! 'Visit our GitHub-page to contribute to this project: '//NEW_LINE('A')// & - ! 'https://github.com/NREL/ROSCO '//NEW_LINE('A')// & - ! '------------------------------------------------------------------------------' - PRINT *,' '//NEW_LINE('A')// & - '------------------------------------------------------------------------------'//NEW_LINE('A')// & - 'Running ROSCO-'//TRIM(git_version)//NEW_LINE('A')// & - 'A wind turbine controller framework for public use in the scientific field '//NEW_LINE('A')// & - 'Developed in collaboration: National Renewable Energy Laboratory '//NEW_LINE('A')// & - ' Delft University of Technology, The Netherlands '//NEW_LINE('A')// & - '------------------------------------------------------------------------------' - - CALL ReadControlParameterFileSub(CntrPar, accINFILE, NINT(avrSWAP(50)),ErrVar) - ! If there's been an file reading error, don't continue - IF (ErrVar%aviFAIL < 0) THEN - ErrVar%ErrMsg = 'SetParameters:'//TRIM(ErrVar%ErrMsg) - RETURN - ENDIF - - IF (CntrPar%WE_Mode > 0) THEN - CALL READCpFile(CntrPar,PerfData,ErrVar) - ENDIF - ! Initialize testValue (debugging variable) - LocalVar%TestType = 0 - - ! Initialize the SAVED variables: - LocalVar%PitCom = LocalVar%BlPitch ! This will ensure that the variable speed controller picks the correct control region and the pitch controller picks the correct gain on the first call - LocalVar%Y_AccErr = 0.0 ! This will ensure that the accumulated yaw error starts at zero - LocalVar%Y_YawEndT = -1.0 ! This will ensure that the initial yaw end time is lower than the actual time to prevent initial yawing - - ! Wind speed estimator initialization - LocalVar%WE_Vw = LocalVar%HorWindV - LocalVar%WE_VwI = LocalVar%WE_Vw - CntrPar%WE_Gamma*LocalVar%RotSpeed - - ! Setpoint Smoother initialization to zero - LocalVar%SS_DelOmegaF = 0 - - ! Generator Torque at K omega^2 or rated - IF (LocalVar%GenSpeed > 0.98 * CntrPar%PC_RefSpd) THEN - LocalVar%GenTq = CntrPar%VS_RtTq - ELSE - LocalVar%GenTq = min(CntrPar%VS_RtTq, CntrPar%VS_Rgn2K*LocalVar%GenSpeed*LocalVar%GenSpeed) - ENDIF - LocalVar%VS_LastGenTrq = LocalVar%GenTq - LocalVar%VS_MaxTq = CntrPar%VS_MaxTq - - ! Check validity of input parameters: - CALL CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) - - IF (ErrVar%aviFAIL < 0) THEN - ErrVar%ErrMsg = 'SetParameters:'//TRIM(ErrVar%ErrMsg) - ENDIF - - - ENDIF - END SUBROUTINE SetParameters ! ----------------------------------------------------------------------------------- ! Read all constant control parameters from DISCON.IN parameter file SUBROUTINE ReadCpFile(CntrPar,PerfData, ErrVar) @@ -1209,68 +1133,6 @@ SUBROUTINE GetWords ( Line, Words, NumWords ) END SUBROUTINE GetWords !======================================================================= - !> This subroutine checks the data to be parsed to make sure it finds - !! the expected variable name and an associated value. - SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) - - USE ROSCO_Types, ONLY : ErrorVariables - - - ! Arguments declarations. - TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input - - INTEGER(4), INTENT(IN) :: FileLineNum !< The number of the line in the file being parsed. - INTEGER(4) :: NameIndx !< The index into the Words array that points to the variable name. - - CHARACTER(*), INTENT(IN) :: ExpVarName !< The expected variable name. - CHARACTER(*), INTENT(IN) :: Words (2) !< The two words to be parsed from the line. - - CHARACTER(*), INTENT(IN) :: FileName !< The name of the file being parsed. - - - ! Local declarations. - - CHARACTER(20) :: ExpUCVarName ! The uppercase version of ExpVarName. - CHARACTER(20) :: FndUCVarName ! The uppercase version of the word being tested. - - - - - ! Convert the found and expected names to uppercase. - - FndUCVarName = Words(1) - ExpUCVarName = ExpVarName - - CALL Conv2UC ( FndUCVarName ) - CALL Conv2UC ( ExpUCVarName ) - - ! See which word is the variable name. Generate an error if it is the first - - IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN - NameIndx = 1 - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & - //'".'//NewLine//' >> The variable "'//TRIM( Words(1) )//'" was not assigned a valid value on line #' & - //TRIM( Int2LStr( FileLineNum ) )//'.' - RETURN - ELSE - FndUCVarName = Words(2) - CALL Conv2UC ( FndUCVarName ) - IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN - NameIndx = 2 - ELSE - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & - //'".'//NewLine//' >> The variable "'//TRIM( ExpVarName )//'" was not assigned a valid value on line #' & - //TRIM( Int2LStr( FileLineNum ) )//'.' - RETURN - ENDIF - ENDIF - - - END SUBROUTINE ChkParseData - -!======================================================================= !> This subroutine parses the specified line of text for AryLen REAL values. !! Generate an error message if the value is the wrong type. !! Use ParseAry (nwtc_io::parseary) instead of directly calling a specific routine in the generic interface. @@ -1524,5 +1386,68 @@ END SUBROUTINE Cleanup END SUBROUTINE ParseInAry + !> This subroutine checks the data to be parsed to make sure it finds + !! the expected variable name and an associated value. +SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) + + USE ROSCO_Types, ONLY : ErrorVariables + + + ! Arguments declarations. + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input + + INTEGER(4), INTENT(IN) :: FileLineNum !< The number of the line in the file being parsed. + INTEGER(4) :: NameIndx !< The index into the Words array that points to the variable name. + + CHARACTER(*), INTENT(IN) :: ExpVarName !< The expected variable name. + CHARACTER(*), INTENT(IN) :: Words (2) !< The two words to be parsed from the line. + + CHARACTER(*), INTENT(IN) :: FileName !< The name of the file being parsed. + + + ! Local declarations. + + CHARACTER(20) :: ExpUCVarName ! The uppercase version of ExpVarName. + CHARACTER(20) :: FndUCVarName ! The uppercase version of the word being tested. + + + + + ! Convert the found and expected names to uppercase. + + FndUCVarName = Words(1) + ExpUCVarName = ExpVarName + + CALL Conv2UC ( FndUCVarName ) + CALL Conv2UC ( ExpUCVarName ) + + ! See which word is the variable name. Generate an error if it is the first + + IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN + NameIndx = 1 + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & + //'".'//NewLine//' >> The variable "'//TRIM( Words(1) )//'" was not assigned a valid value on line #' & + //TRIM( Int2LStr( FileLineNum ) )//'.' + RETURN + ELSE + FndUCVarName = Words(2) + CALL Conv2UC ( FndUCVarName ) + IF ( TRIM( FndUCVarName ) == TRIM( ExpUCVarName ) ) THEN + NameIndx = 2 + ELSE + ErrVar%aviFAIL = -1 + ErrVar%ErrMsg = ' >> A fatal error occurred when parsing data from "'//TRIM( FileName ) & + //'".'//NewLine//' >> The variable "'//TRIM( ExpVarName )//'" was not assigned a valid value on line #' & + //TRIM( Int2LStr( FileLineNum ) )//'.' + RETURN + ENDIF + ENDIF + + +END SUBROUTINE ChkParseData + +!======================================================================= + END MODULE ReadSetParameters From c88f2725cd31e9a1190381918c424e41fbf2143a Mon Sep 17 00:00:00 2001 From: dzalkind Date: Mon, 17 May 2021 17:06:27 -0600 Subject: [PATCH 20/26] Make error tracing consistent: RoutineNames --- src/ControllerBlocks.f90 | 21 ++++++++++++-------- src/Controllers.f90 | 8 +++++--- src/DISCON.F90 | 10 ++++++---- src/Functions.f90 | 41 +++++++++++++++++++++++++-------------- src/ReadSetParameters.f90 | 23 +++++++++++++++------- 5 files changed, 66 insertions(+), 37 deletions(-) diff --git a/src/ControllerBlocks.f90 b/src/ControllerBlocks.f90 index 19707598..774add03 100644 --- a/src/ControllerBlocks.f90 +++ b/src/ControllerBlocks.f90 @@ -210,6 +210,9 @@ SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar, Er REAL(8) :: R_m ! Measurement noise covariance [(rad/s)^2] REAL(8), DIMENSION(3,1), SAVE :: B + + CHARACTER(*), PARAMETER :: RoutineName = 'WindSpeedEstimator' + ! ---- Debug Inputs ------ DebugVar%WE_b = LocalVar%PC_PitComTF*R2D DebugVar%WE_w = LocalVar%RotSpeedF @@ -308,10 +311,10 @@ SUBROUTINE WindSpeedEstimator(LocalVar, CntrPar, objInst, PerfData, DebugVar, Er LocalVar%WE_Vw = LPFilter(LocalVar%HorWindV, LocalVar%DT, F_WECornerFreq, LocalVar%iStatus, .FALSE., objInst%instLPF) ENDIF - ! Error Catching - IF (ErrVar%aviFAIL == -1) THEN - ErrVar%ErrMsg = 'WindSpeedEstimator:'//TRIM(ErrVar%ErrMsg) - END IF + ! Add RoutineName to error message + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) + ENDIF END SUBROUTINE WindSpeedEstimator !------------------------------------------------------------------------------------------------------------------------------- @@ -355,13 +358,15 @@ REAL FUNCTION PitchSaturation(LocalVar, CntrPar, objInst, DebugVar, ErrVar) TYPE(DebugVariables), INTENT(INOUT) :: DebugVar TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar + CHARACTER(*), PARAMETER :: RoutineName = 'PitchSaturation' + ! Define minimum blade pitch angle as a function of estimated wind speed PitchSaturation = interp1d(CntrPar%PS_WindSpeeds, CntrPar%PS_BldPitchMin, LocalVar%WE_Vw_F, ErrVar) - ! Error Catching - IF (ErrVar%aviFAIL == -1) THEN - ErrVar%ErrMsg = 'PitchSaturation:'//TRIM(ErrVar%ErrMsg) - END IF + ! Add RoutineName to error message + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) + ENDIF END FUNCTION PitchSaturation !------------------------------------------------------------------------------------------------------------------------------- diff --git a/src/Controllers.f90 b/src/Controllers.f90 index 7368aac3..35dbfa3e 100644 --- a/src/Controllers.f90 +++ b/src/Controllers.f90 @@ -49,9 +49,11 @@ SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar, ErrVar) TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Allocate Variables: - REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from the DLL controller. - INTEGER(4) :: K ! Index used for looping through blades. - REAL(8), Save :: PitComT_Last + REAL(C_FLOAT), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from the DLL controller. + INTEGER(4) :: K ! Index used for looping through blades. + REAL(8), Save :: PitComT_Last + + CHARACTER(*), PARAMETER :: RoutineName = 'PitchControl' ! ------- Blade Pitch Controller -------- ! Load PC State diff --git a/src/DISCON.F90 b/src/DISCON.F90 index 649f1430..9ca37a12 100644 --- a/src/DISCON.F90 +++ b/src/DISCON.F90 @@ -56,6 +56,8 @@ SUBROUTINE DISCON(avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG) BIND (C, NAME TYPE(DebugVariables), SAVE :: DebugVar TYPE(ErrorVariables), SAVE :: ErrVar +CHARACTER(*), PARAMETER :: RoutineName = 'ROSCO' + RootName = TRANSFER(avcOUTNAME, RootName) !------------------------------------------------------------------------------------------------------------------------------ ! Main control calculations @@ -78,11 +80,11 @@ SUBROUTINE DISCON(avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG) BIND (C, NAME CALL Debug(LocalVar, CntrPar, DebugVar, avrSWAP, RootName, SIZE(avcOUTNAME)) END IF -! Error Catching -IF (ErrVar%aviFAIL == -1) THEN - ErrVar%ErrMsg = 'ROSCO:'//TRIM(ErrVar%ErrMsg) +! Add RoutineName to error message +IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) print * , TRIM(ErrVar%ErrMsg) -END IF +ENDIF ErrMsg = ErrVar%ErrMsg avcMSG = TRANSFER(TRIM(ErrVar%ErrMsg)//C_NULL_CHAR, avcMSG, SIZE(avcMSG)) aviFAIL = ErrVar%aviFAIL diff --git a/src/Functions.f90 b/src/Functions.f90 index a9fd8f59..e0e3a70a 100644 --- a/src/Functions.f90 +++ b/src/Functions.f90 @@ -176,13 +176,15 @@ REAL FUNCTION interp1d(xData, yData, xq, ErrVar) TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar INTEGER(4) :: I_DIFF + CHARACTER(*), PARAMETER :: RoutineName = 'interp1d' + ! Catch Errors ! Are xData and yData the same size? IF (SIZE(xData) .NE. SIZE(yData)) THEN ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = 'interp1d: xData and yData are not the same size' - WRITE(ErrVar%ErrMsg,"(A,I2,A,I2,A)") "interp1d: SIZE(xData) =", SIZE(xData), & + ErrVar%ErrMsg = ' xData and yData are not the same size' + WRITE(ErrVar%ErrMsg,"(A,I2,A,I2,A)") " SIZE(xData) =", SIZE(xData), & ' and SIZE(yData) =', SIZE(yData),' are not the same' END IF @@ -190,7 +192,7 @@ REAL FUNCTION interp1d(xData, yData, xq, ErrVar) DO I_DIFF = 1, size(xData) - 1 IF (xData(I_DIFF + 1) - xData(I_DIFF) <= 0) THEN ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = 'interp1d: xData is not strictly increasing' + ErrVar%ErrMsg = ' xData is not strictly increasing' EXIT END IF END DO @@ -210,6 +212,11 @@ REAL FUNCTION interp1d(xData, yData, xq, ErrVar) END IF END DO END IF + + ! Add RoutineName to error message + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) + ENDIF END FUNCTION interp1d @@ -251,19 +258,21 @@ REAL FUNCTION interp2d(xData, yData, zData, xq, yq, ErrVar) ! Error Catching TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar INTEGER(4) :: I_DIFF + + CHARACTER(*), PARAMETER :: RoutineName = 'interp2d' ! Error catching ! Are xData and zData(:,1) the same size? IF (SIZE(xData) .NE. SIZE(zData,2)) THEN ErrVar%aviFAIL = -1 - WRITE(ErrVar%ErrMsg,"(A,I4,A,I4,A)") "interp2d: SIZE(xData) =", SIZE(xData), & + WRITE(ErrVar%ErrMsg,"(A,I4,A,I4,A)") " SIZE(xData) =", SIZE(xData), & ' and SIZE(zData,1) =', SIZE(zData,2),' are not the same' END IF ! Are yData and zData(1,:) the same size? IF (SIZE(yData) .NE. SIZE(zData,1)) THEN ErrVar%aviFAIL = -1 - WRITE(ErrVar%ErrMsg,"(A,I4,A,I4,A)") "interp2d: SIZE(yData) =", SIZE(yData), & + WRITE(ErrVar%ErrMsg,"(A,I4,A,I4,A)") " SIZE(yData) =", SIZE(yData), & ' and SIZE(zData,2) =', SIZE(zData,1),' are not the same' END IF @@ -271,7 +280,7 @@ REAL FUNCTION interp2d(xData, yData, zData, xq, yq, ErrVar) DO I_DIFF = 1, size(xData) - 1 IF (xData(I_DIFF + 1) - xData(I_DIFF) <= 0) THEN ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = 'interp2d: xData is not strictly increasing' + ErrVar%ErrMsg = ' xData is not strictly increasing' EXIT END IF END DO @@ -280,7 +289,7 @@ REAL FUNCTION interp2d(xData, yData, zData, xq, yq, ErrVar) DO I_DIFF = 1, size(yData) - 1 IF (yData(I_DIFF + 1) - yData(I_DIFF) <= 0) THEN ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = 'interp2d: yData is not strictly increasing' + ErrVar%ErrMsg = ' yData is not strictly increasing' EXIT END IF END DO @@ -352,10 +361,10 @@ REAL FUNCTION interp2d(xData, yData, zData, xq, yq, ErrVar) interp2d = fxy(1) - ! Error catching - IF (ErrVar%aviFAIL == -1) THEN - ErrVar%ErrMsg = 'interp2:'//TRIM(ErrVar%ErrMsg) - END IF + ! Add RoutineName to error message + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) + ENDIF END FUNCTION interp2d @@ -514,6 +523,8 @@ REAL FUNCTION AeroDynTorque(LocalVar, CntrPar, PerfData, ErrVar) REAL(8) :: Cp REAL(8) :: Lambda + CHARACTER(*), PARAMETER :: RoutineName = 'AeroDynTorque' + ! Find Torque RotorArea = PI*CntrPar%WE_BladeRadius**2 Lambda = LocalVar%RotSpeedF*CntrPar%WE_BladeRadius/LocalVar%WE_Vw @@ -524,10 +535,10 @@ REAL FUNCTION AeroDynTorque(LocalVar, CntrPar, PerfData, ErrVar) AeroDynTorque = 0.5*(CntrPar%WE_RhoAir*RotorArea)*(LocalVar%WE_Vw**3/LocalVar%RotSpeedF)*Cp AeroDynTorque = MAX(AeroDynTorque, 0.0) - ! Error Catching - IF (ErrVar%aviFAIL == -1) THEN - ErrVar%ErrMsg = 'AeroDynTorque:'//TRIM(ErrVar%ErrMsg) - END IF + ! Add RoutineName to error message + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) + ENDIF END FUNCTION AeroDynTorque diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 2b2e043c..2b681f70 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -103,6 +103,10 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj INTEGER(4) :: K ! Index used for looping through blades. CHARACTER(200) :: git_version + + CHARACTER(*), PARAMETER :: RoutineName = 'SetParameters' + + ! Error Catching Variables ! Set ErrVar%aviFAIL to 0 in each iteration: @@ -148,8 +152,9 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj CALL ReadControlParameterFileSub(CntrPar, accINFILE, NINT(avrSWAP(50)),ErrVar) ! If there's been an file reading error, don't continue + ! Add RoutineName to error message IF (ErrVar%aviFAIL < 0) THEN - ErrVar%ErrMsg = 'SetParameters:'//TRIM(ErrVar%ErrMsg) + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) RETURN ENDIF @@ -183,8 +188,9 @@ SUBROUTINE SetParameters(avrSWAP, accINFILE, size_avcMSG, CntrPar, LocalVar, obj ! Check validity of input parameters: CALL CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) + ! Add RoutineName to error message IF (ErrVar%aviFAIL < 0) THEN - ErrVar%ErrMsg = 'SetParameters:'//TRIM(ErrVar%ErrMsg) + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) ENDIF @@ -204,6 +210,8 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar INTEGER(4) :: CurLine + CHARACTER(*), PARAMETER :: RoutineName = 'ReadControlParameterFileSub' + CurLine = 1 @@ -373,8 +381,9 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar !------------------- HOUSEKEEPING ----------------------- CntrPar%PerfFileName = TRIM(CntrPar%PerfFileName) + ! Add RoutineName to error message IF (ErrVar%aviFAIL < 0) THEN - ErrVar%ErrMsg = 'ReadControlParameterFileSub:'//TRIM(ErrVar%ErrMsg) + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) ENDIF @@ -394,7 +403,7 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) INTEGER(4), INTENT(IN ) :: size_avcMSG REAL(C_FLOAT), INTENT(IN ) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller. - + CHARACTER(*), PARAMETER :: RoutineName = 'CheckInputs' ! Local !.............................................................................................................................. @@ -818,7 +827,7 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) ENDIF IF (ErrVar%aviFAIL < 0) THEN - ErrVar%ErrMsg = 'CheckInputs:'//TRIM(ErrVar%ErrMsg) + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) ENDIF END SUBROUTINE CheckInputs @@ -1222,7 +1231,7 @@ SUBROUTINE ParseDbAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar, Che READ (Line,*,IOSTAT=ErrStatLcl) Ary IF ( ErrStatLcl /= 0 ) THEN ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = RoutineName//'A fatal error occurred when parsing data from "' & + ErrVar%ErrMsg = RoutineName//':A fatal error occurred when parsing data from "' & //TRIM( FileName )//'".'//NewLine// & ' >> The "'//TRIM( AryName )//'" array was not assigned valid REAL values on line #' & //TRIM( Int2LStr( LineNum ) )//'.'//NewLine//' >> The text being parsed was :'//NewLine & @@ -1349,7 +1358,7 @@ SUBROUTINE ParseInAry ( Un, LineNum, AryName, Ary, AryLen, FileName, ErrVar, Che READ (Line,*,IOSTAT=ErrStatLcl) Ary IF ( ErrStatLcl /= 0 ) THEN ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = RoutineName//'A fatal error occurred when parsing data from "' & + ErrVar%ErrMsg = RoutineName//':A fatal error occurred when parsing data from "' & //TRIM( FileName )//'".'//NewLine// & ' >> The "'//TRIM( AryName )//'" array was not assigned valid REAL values on line #' & //TRIM( Int2LStr( LineNum ) )//'.'//NewLine//' >> The text being parsed was :'//NewLine & From 008d34ab166ba432a3b9c058af0f1ef785e8846b Mon Sep 17 00:00:00 2001 From: dzalkind Date: Mon, 17 May 2021 17:08:55 -0600 Subject: [PATCH 21/26] Finish making error tracing consistent --- src/Controllers.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Controllers.f90 b/src/Controllers.f90 index 35dbfa3e..50fdb802 100644 --- a/src/Controllers.f90 +++ b/src/Controllers.f90 @@ -129,10 +129,10 @@ SUBROUTINE PitchControl(avrSWAP, CntrPar, LocalVar, objInst, DebugVar, ErrVar) avrSWAP(44) = LocalVar%PitCom(3) ! " avrSWAP(45) = LocalVar%PitCom(1) ! Use the command angle of blade 1 if using collective pitch - ! Error Catching - IF (ErrVar%aviFAIL == -1) THEN - ErrVar%ErrMsg = 'PitchControl:'//TRIM(ErrVar%ErrMsg) - END IF + ! Add RoutineName to error message + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) + ENDIF END SUBROUTINE PitchControl !------------------------------------------------------------------------------------------------------------------------------- From ce0510bf04197c2614de8e8b90b74f7e23566a42 Mon Sep 17 00:00:00 2001 From: nikhar-abbas <40865984+nikhar-abbas@users.noreply.github.com> Date: Tue, 25 May 2021 11:55:19 -0600 Subject: [PATCH 22/26] Use unsaturated pitch command in setpoint smoother (#45) * Use unsaturated pitch command in setpoint smoother * Revert "Use unsaturated pitch command in setpoint smoother" This reverts commit a471f4e2e9d9250f2750a28e074f18d207f52ffd. * Use localvar min pitch instead of cntrpar in ss --- src/ControllerBlocks.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ControllerBlocks.f90 b/src/ControllerBlocks.f90 index 0db9cbd7..4a580c2f 100644 --- a/src/ControllerBlocks.f90 +++ b/src/ControllerBlocks.f90 @@ -255,7 +255,7 @@ SUBROUTINE SetpointSmoother(LocalVar, CntrPar, objInst) ! ------ Setpoint Smoothing ------ IF ( CntrPar%SS_Mode == 1) THEN ! Find setpoint shift amount - DelOmega = ((LocalVar%PC_PitComT - CntrPar%PC_MinPit)/0.524) * CntrPar%SS_VSGain - ((CntrPar%VS_RtPwr - LocalVar%VS_LastGenPwr))/CntrPar%VS_RtPwr * CntrPar%SS_PCGain ! Normalize to 30 degrees for now + DelOmega = ((LocalVar%PC_PitComT - LocalVar%PC_MinPit)/0.524) * CntrPar%SS_VSGain - ((CntrPar%VS_RtPwr - LocalVar%VS_LastGenPwr))/CntrPar%VS_RtPwr * CntrPar%SS_PCGain ! Normalize to 30 degrees for now DelOmega = DelOmega * CntrPar%PC_RefSpd ! Filter LocalVar%SS_DelOmegaF = LPFilter(DelOmega, LocalVar%DT, CntrPar%F_SSCornerFreq, LocalVar%iStatus, .FALSE., objInst%instLPF) From 398cb424b89d44b7dd2d40f0cdd3b1c87479d3c8 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 27 May 2021 10:06:03 -0600 Subject: [PATCH 23/26] Don't check WE_CP, FA_HPFCornerFreq until API change --- src/ReadSetParameters.f90 | 44 ++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 2b681f70..998c0e4f 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -309,7 +309,7 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar CALL ReadEmptyLine(UnControllerParameters,CurLine) CALL ParseInput(UnControllerParameters,CurLine,'WE_BladeRadius',accINFILE(1),CntrPar%WE_BladeRadius,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'WE_CP_n',accINFILE(1),CntrPar%WE_CP_n,ErrVar) - CALL ParseAry(UnControllerParameters, CurLine, 'WE_CP', CntrPar%WE_CP, CntrPar%WE_CP_n, accINFILE(1), ErrVar ) + CALL ParseAry(UnControllerParameters, CurLine, 'WE_CP', CntrPar%WE_CP, CntrPar%WE_CP_n, accINFILE(1), ErrVar, .FALSE. ) CALL ParseInput(UnControllerParameters,CurLine,'WE_Gamma',accINFILE(1),CntrPar%WE_Gamma,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'WE_GearboxRatio',accINFILE(1),CntrPar%WE_GearboxRatio,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'WE_Jtot',accINFILE(1),CntrPar%WE_Jtot,ErrVar) @@ -339,7 +339,8 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar !------------ FORE-AFT TOWER DAMPER CONSTANTS ------------ CALL ReadEmptyLine(UnControllerParameters,CurLine) CALL ParseInput(UnControllerParameters,CurLine,'FA_KI',accINFILE(1),CntrPar%FA_KI,ErrVar) - CALL ParseInput(UnControllerParameters,CurLine,'FA_HPFCornerFreq',accINFILE(1),CntrPar%FA_HPFCornerFreq,ErrVar) + ! Don't check this name until we make an API change + CALL ParseInput(UnControllerParameters,CurLine,'FA_HPFCornerFreq',accINFILE(1),CntrPar%FA_HPFCornerFreq,ErrVar,.FALSE.) CALL ParseInput(UnControllerParameters,CurLine,'FA_IntSat',accINFILE(1),CntrPar%FA_IntSat,ErrVar) CALL ReadEmptyLine(UnControllerParameters,CurLine) @@ -896,7 +897,7 @@ SUBROUTINE ReadCpFile(CntrPar,PerfData, ErrVar) END SUBROUTINE ReadCpFile ! Parse integer input: read line, check that variable name is in line, handle errors - subroutine ParseInput_Int(Un,CurLine,VarName, FileName, Variable,ErrVar) + subroutine ParseInput_Int(Un, CurLine, VarName, FileName, Variable, ErrVar, CheckName) USE ROSCO_Types, ONLY : ErrorVariables CHARACTER(1024) :: Line @@ -909,6 +910,13 @@ subroutine ParseInput_Int(Un,CurLine,VarName, FileName, Variable,ErrVar) INTEGER(4), INTENT(INOUT) :: Variable ! Variable INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + LOGICAL, OPTIONAL, INTENT(IN ) :: CheckName + + LOGICAL :: CheckName_ + + ! Figure out if we're checking the name, default to .TRUE. + CheckName_ = .TRUE. + if (PRESENT(CheckName)) CheckName_ = CheckName ! If we've already failed, don't read anything IF (ErrVar%aviFAIL >= 0) THEN @@ -925,7 +933,9 @@ subroutine ParseInput_Int(Un,CurLine,VarName, FileName, Variable,ErrVar) END IF ! Check that Variable Name is in Words - CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + IF (CheckName_) THEN + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + END IF ! IF We haven't failed already IF (ErrVar%aviFAIL >= 0) THEN @@ -950,7 +960,7 @@ subroutine ParseInput_Int(Un,CurLine,VarName, FileName, Variable,ErrVar) END subroutine ParseInput_Int ! Parse double input, this is a copy of ParseInput_Int and a change in the variable definitions - subroutine ParseInput_Dbl(Un,CurLine,VarName, FileName, Variable,ErrVar) + subroutine ParseInput_Dbl(Un, CurLine, VarName, FileName, Variable, ErrVar, CheckName) USE ROSCO_Types, ONLY : ErrorVariables CHARACTER(1024) :: Line @@ -960,10 +970,17 @@ subroutine ParseInput_Dbl(Un,CurLine,VarName, FileName, Variable,ErrVar) INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input CHARACTER(20) :: Words (2) ! The two "words" parsed from the line + LOGICAL, OPTIONAL, INTENT(IN ) :: CheckName REAL(8), INTENT(INOUT) :: Variable ! Variable INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + LOGICAL :: CheckName_ + + ! Figure out if we're checking the name, default to .TRUE. + CheckName_ = .TRUE. + if (PRESENT(CheckName)) CheckName_ = CheckName + ! If we've already failed, don't read anything IF (ErrVar%aviFAIL >= 0) THEN @@ -979,7 +996,9 @@ subroutine ParseInput_Dbl(Un,CurLine,VarName, FileName, Variable,ErrVar) END IF ! Check that Variable Name is in Words - CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + IF (CheckName_) THEN + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + END IF ! IF We haven't failed already IF (ErrVar%aviFAIL >= 0) THEN @@ -1004,7 +1023,7 @@ subroutine ParseInput_Dbl(Un,CurLine,VarName, FileName, Variable,ErrVar) END subroutine ParseInput_Dbl ! Parse string input, this is a copy of ParseInput_Int and a change in the variable definitions - subroutine ParseInput_Str(Un,CurLine,VarName, FileName, Variable,ErrVar) + subroutine ParseInput_Str(Un, CurLine, VarName, FileName, Variable, ErrVar, CheckName) USE ROSCO_Types, ONLY : ErrorVariables CHARACTER(1024) :: Line @@ -1014,10 +1033,17 @@ subroutine ParseInput_Str(Un,CurLine,VarName, FileName, Variable,ErrVar) INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar ! Current line of input CHARACTER(200) :: Words (2) ! The two "words" parsed from the line + LOGICAL, OPTIONAL, INTENT(IN ) :: CheckName CHARACTER(*), INTENT(INOUT) :: Variable ! Variable INTEGER(4) :: ErrStatLcl ! Error status local to this routine. + LOGICAL :: CheckName_ + + ! Figure out if we're checking the name, default to .TRUE. + CheckName_ = .TRUE. + if (PRESENT(CheckName)) CheckName_ = CheckName + ! If we've already failed, don't read anything IF (ErrVar%aviFAIL >= 0) THEN @@ -1033,7 +1059,9 @@ subroutine ParseInput_Str(Un,CurLine,VarName, FileName, Variable,ErrVar) END IF ! Check that Variable Name is in Words - CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + IF (CheckName_) THEN + CALL ChkParseData ( Words, VarName, FileName, CurLine, ErrVar ) + END IF ! IF We haven't failed already IF (ErrVar%aviFAIL >= 0) THEN From d5963fd9cd96fc6cfa9840532d70f7c0526e42db Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 27 May 2021 10:06:42 -0600 Subject: [PATCH 24/26] Remove check on F_NotchBetaNumDen(1), not necessary --- src/ReadSetParameters.f90 | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 998c0e4f..7a75da64 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -530,12 +530,6 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) ErrVar%ErrMsg = 'F_NotchCornerFreq must be greater than zero.' ENDIF - ! F_NotchBetaNumDen(1) - IF (CntrPar%F_NotchBetaNumDen(1) <= 0.0) THEN - ErrVar%aviFAIL = -1 - ErrVar%ErrMsg = 'F_NotchBetaNumDen(1) must be greater than zero.' - ENDIF - ! F_NotchBetaNumDen(2) IF (CntrPar%F_NotchBetaNumDen(2) <= 0.0) THEN ErrVar%aviFAIL = -1 From 6729e9fd20519883f0262e758d34eb889ca18acd Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 27 May 2021 10:41:44 -0600 Subject: [PATCH 25/26] Re-organize ReadSetParameters --- src/ReadSetParameters.f90 | 298 +++++++++++++++++++------------------- 1 file changed, 150 insertions(+), 148 deletions(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 7a75da64..23738905 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -390,6 +390,69 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar END SUBROUTINE ReadControlParameterFileSub ! ----------------------------------------------------------------------------------- + ! Read all constant control parameters from DISCON.IN parameter file + SUBROUTINE ReadCpFile(CntrPar,PerfData, ErrVar) + USE ROSCO_Types, ONLY : PerformanceData, ControlParameters, ErrorVariables + + TYPE(PerformanceData), INTENT(INOUT) :: PerfData + TYPE(ControlParameters), INTENT(INOUT) :: CntrPar + TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar + + ! Local variables + INTEGER(4), PARAMETER :: UnPerfParameters = 89 + INTEGER(4) :: i ! iteration index + + INTEGER(4) :: CurLine + CHARACTER(*), PARAMETER :: RoutineName = 'ReadCpFile' + REAL(8), DIMENSION(:), ALLOCATABLE :: TmpPerf + + CurLine = 1 + + OPEN(unit=UnPerfParameters, file=TRIM(CntrPar%PerfFileName), status='old', action='read') ! Should put input file into DISCON.IN + + ! ----------------------- Axis Definitions ------------------------ + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ParseAry(UnPerfParameters, CurLine, 'Pitch angle vector', PerfData%Beta_vec, CntrPar%PerfTableSize(1), TRIM(CntrPar%PerfFileName), ErrVar, .FALSE.) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ParseAry(UnPerfParameters, CurLine, 'TSR vector', PerfData%TSR_vec, CntrPar%PerfTableSize(2), TRIM(CntrPar%PerfFileName), ErrVar, .FALSE.) + + ! ----------------------- Read Cp, Ct, Cq, Tables ------------------------ + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) ! Input file should contains wind speed information here - unneeded for now + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + ALLOCATE(PerfData%Cp_mat(CntrPar%PerfTableSize(2),CntrPar%PerfTableSize(1))) + DO i = 1,CntrPar%PerfTableSize(2) + READ(UnPerfParameters, *) PerfData%Cp_mat(i,:) ! Read Cp table + END DO + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + ALLOCATE(PerfData%Ct_mat(CntrPar%PerfTableSize(1),CntrPar%PerfTableSize(2))) + DO i = 1,CntrPar%PerfTableSize(2) + READ(UnPerfParameters, *) PerfData%Ct_mat(i,:) ! Read Ct table + END DO + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + CALL ReadEmptyLine(UnPerfParameters,CurLine) + ALLOCATE(PerfData%Cq_mat(CntrPar%PerfTableSize(1),CntrPar%PerfTableSize(2))) + DO i = 1,CntrPar%PerfTableSize(2) + READ(UnPerfParameters, *) PerfData%Cq_mat(i,:) ! Read Cq table + END DO + + ! Add RoutineName to error message + IF (ErrVar%aviFAIL < 0) THEN + ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) + ENDIF + + END SUBROUTINE ReadCpFile + ! ----------------------------------------------------------------------------------- ! Check for errors before any execution SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) USE, INTRINSIC :: ISO_C_Binding @@ -826,70 +889,8 @@ SUBROUTINE CheckInputs(LocalVar, CntrPar, avrSWAP, ErrVar, size_avcMSG) ENDIF END SUBROUTINE CheckInputs - - ! ----------------------------------------------------------------------------------- - ! Read all constant control parameters from DISCON.IN parameter file - SUBROUTINE ReadCpFile(CntrPar,PerfData, ErrVar) - USE ROSCO_Types, ONLY : PerformanceData, ControlParameters, ErrorVariables - - TYPE(PerformanceData), INTENT(INOUT) :: PerfData - TYPE(ControlParameters), INTENT(INOUT) :: CntrPar - TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar - - ! Local variables - INTEGER(4), PARAMETER :: UnPerfParameters = 89 - INTEGER(4) :: i ! iteration index - - INTEGER(4) :: CurLine - CHARACTER(*), PARAMETER :: RoutineName = 'ReadCpFile' - REAL(8), DIMENSION(:), ALLOCATABLE :: TmpPerf - - CurLine = 1 - - OPEN(unit=UnPerfParameters, file=TRIM(CntrPar%PerfFileName), status='old', action='read') ! Should put input file into DISCON.IN - - ! ----------------------- Axis Definitions ------------------------ - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ParseAry(UnPerfParameters, CurLine, 'Pitch angle vector', PerfData%Beta_vec, CntrPar%PerfTableSize(1), TRIM(CntrPar%PerfFileName), ErrVar, .FALSE.) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ParseAry(UnPerfParameters, CurLine, 'TSR vector', PerfData%TSR_vec, CntrPar%PerfTableSize(2), TRIM(CntrPar%PerfFileName), ErrVar, .FALSE.) - - ! ----------------------- Read Cp, Ct, Cq, Tables ------------------------ - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) ! Input file should contains wind speed information here - unneeded for now - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - ALLOCATE(PerfData%Cp_mat(CntrPar%PerfTableSize(2),CntrPar%PerfTableSize(1))) - DO i = 1,CntrPar%PerfTableSize(2) - READ(UnPerfParameters, *) PerfData%Cp_mat(i,:) ! Read Cp table - END DO - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - ALLOCATE(PerfData%Ct_mat(CntrPar%PerfTableSize(1),CntrPar%PerfTableSize(2))) - DO i = 1,CntrPar%PerfTableSize(2) - READ(UnPerfParameters, *) PerfData%Ct_mat(i,:) ! Read Ct table - END DO - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - CALL ReadEmptyLine(UnPerfParameters,CurLine) - ALLOCATE(PerfData%Cq_mat(CntrPar%PerfTableSize(1),CntrPar%PerfTableSize(2))) - DO i = 1,CntrPar%PerfTableSize(2) - READ(UnPerfParameters, *) PerfData%Cq_mat(i,:) ! Read Cq table - END DO - - ! Add RoutineName to error message - IF (ErrVar%aviFAIL < 0) THEN - ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg) - ENDIF - END SUBROUTINE ReadCpFile + !======================================================================= ! Parse integer input: read line, check that variable name is in line, handle errors subroutine ParseInput_Int(Un, CurLine, VarName, FileName, Variable, ErrVar, CheckName) USE ROSCO_Types, ONLY : ErrorVariables @@ -953,6 +954,7 @@ subroutine ParseInput_Int(Un, CurLine, VarName, FileName, Variable, ErrVar, Chec END subroutine ParseInput_Int + !======================================================================= ! Parse double input, this is a copy of ParseInput_Int and a change in the variable definitions subroutine ParseInput_Dbl(Un, CurLine, VarName, FileName, Variable, ErrVar, CheckName) USE ROSCO_Types, ONLY : ErrorVariables @@ -1016,6 +1018,7 @@ subroutine ParseInput_Dbl(Un, CurLine, VarName, FileName, Variable, ErrVar, Chec END subroutine ParseInput_Dbl + !======================================================================= ! Parse string input, this is a copy of ParseInput_Int and a change in the variable definitions subroutine ParseInput_Str(Un, CurLine, VarName, FileName, Variable, ErrVar, CheckName) USE ROSCO_Types, ONLY : ErrorVariables @@ -1079,91 +1082,7 @@ subroutine ParseInput_Str(Un, CurLine, VarName, FileName, Variable, ErrVar, Chec END subroutine ParseInput_Str - subroutine ReadEmptyLine(Un,CurLine) - INTEGER(4), INTENT(IN ) :: Un ! Input file unit - INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input - - CHARACTER(1024) :: Line - - READ(Un, '(A)') Line - CurLine = CurLine + 1 - - END subroutine ReadEmptyLine - - !======================================================================= - !> This subroutine is used to get the NumWords "words" from a line of text. - !! It uses spaces, tabs, commas, semicolons, single quotes, and double quotes ("whitespace") - !! as word separators. If there aren't NumWords in the line, the remaining array elements will remain empty. - !! Use CountWords (nwtc_io::countwords) to count the number of words in a line. - SUBROUTINE GetWords ( Line, Words, NumWords ) - - ! Argument declarations. - - INTEGER, INTENT(IN) :: NumWords !< The number of words to look for. - - CHARACTER(*), INTENT(IN) :: Line !< The string to search. - CHARACTER(*), INTENT(OUT) :: Words(NumWords) !< The array of found words. - - - ! Local declarations. - - INTEGER :: Ch ! Character position within the string. - INTEGER :: IW ! Word index. - INTEGER :: NextWhite ! The location of the next whitespace in the string. - CHARACTER(1), PARAMETER :: Tab = CHAR( 9 ) - - - - ! Let's prefill the array with blanks. - - DO IW=1,NumWords - Words(IW) = ' ' - END DO ! IW - - - ! Let's make sure we have text on this line. - - IF ( LEN_TRIM( Line ) == 0 ) RETURN - - - ! Parse words separated by any combination of spaces, tabs, commas, - ! semicolons, single quotes, and double quotes ("whitespace"). - - Ch = 0 - IW = 0 - - DO - - NextWhite = SCAN( Line(Ch+1:) , ' ,!;''"'//Tab ) - - IF ( NextWhite > 1 ) THEN - - IW = IW + 1 - Words(IW) = Line(Ch+1:Ch+NextWhite-1) - - IF ( IW == NumWords ) EXIT - - Ch = Ch + NextWhite - - ELSE IF ( NextWhite == 1 ) THEN - - Ch = Ch + 1 - - CYCLE - - ELSE - - EXIT - - END IF - - END DO - - - RETURN - END SUBROUTINE GetWords - !======================================================================= - +!======================================================================= !> This subroutine parses the specified line of text for AryLen REAL values. !! Generate an error message if the value is the wrong type. !! Use ParseAry (nwtc_io::parseary) instead of directly calling a specific routine in the generic interface. @@ -1417,6 +1336,7 @@ END SUBROUTINE Cleanup END SUBROUTINE ParseInAry +!======================================================================= !> This subroutine checks the data to be parsed to make sure it finds !! the expected variable name and an associated value. SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) @@ -1479,6 +1399,88 @@ SUBROUTINE ChkParseData ( Words, ExpVarName, FileName, FileLineNum, ErrVar ) END SUBROUTINE ChkParseData !======================================================================= +subroutine ReadEmptyLine(Un,CurLine) + INTEGER(4), INTENT(IN ) :: Un ! Input file unit + INTEGER(4), INTENT(INOUT) :: CurLine ! Current line of input + + CHARACTER(1024) :: Line + + READ(Un, '(A)') Line + CurLine = CurLine + 1 + +END subroutine ReadEmptyLine + +!======================================================================= +!> This subroutine is used to get the NumWords "words" from a line of text. +!! It uses spaces, tabs, commas, semicolons, single quotes, and double quotes ("whitespace") +!! as word separators. If there aren't NumWords in the line, the remaining array elements will remain empty. +!! Use CountWords (nwtc_io::countwords) to count the number of words in a line. +SUBROUTINE GetWords ( Line, Words, NumWords ) + + ! Argument declarations. + + INTEGER, INTENT(IN) :: NumWords !< The number of words to look for. + + CHARACTER(*), INTENT(IN) :: Line !< The string to search. + CHARACTER(*), INTENT(OUT) :: Words(NumWords) !< The array of found words. + + + ! Local declarations. + + INTEGER :: Ch ! Character position within the string. + INTEGER :: IW ! Word index. + INTEGER :: NextWhite ! The location of the next whitespace in the string. + CHARACTER(1), PARAMETER :: Tab = CHAR( 9 ) + + ! Let's prefill the array with blanks. + + DO IW=1,NumWords + Words(IW) = ' ' + END DO ! IW + + + ! Let's make sure we have text on this line. + + IF ( LEN_TRIM( Line ) == 0 ) RETURN + + + ! Parse words separated by any combination of spaces, tabs, commas, + ! semicolons, single quotes, and double quotes ("whitespace"). + + Ch = 0 + IW = 0 + + DO + + NextWhite = SCAN( Line(Ch+1:) , ' ,!;''"'//Tab ) + + IF ( NextWhite > 1 ) THEN + + IW = IW + 1 + Words(IW) = Line(Ch+1:Ch+NextWhite-1) + + IF ( IW == NumWords ) EXIT + + Ch = Ch + NextWhite + + ELSE IF ( NextWhite == 1 ) THEN + + Ch = Ch + 1 + + CYCLE + + ELSE + + EXIT + + END IF + + END DO + + + RETURN +END SUBROUTINE GetWords + END MODULE ReadSetParameters From 8f0a93ce71431be530151ca53668ad054c1ba283 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 27 May 2021 14:34:12 -0600 Subject: [PATCH 26/26] Fix controller-breaking typo --- src/ReadSetParameters.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReadSetParameters.f90 b/src/ReadSetParameters.f90 index 23738905..9ea406ff 100644 --- a/src/ReadSetParameters.f90 +++ b/src/ReadSetParameters.f90 @@ -365,7 +365,7 @@ SUBROUTINE ReadControlParameterFileSub(CntrPar, accINFILE, accINFILE_size,ErrVar !------------ Flaps ------------ CALL ReadEmptyLine(UnControllerParameters,CurLine) CALL ParseInput(UnControllerParameters,CurLine,'Flp_Angle',accINFILE(1),CntrPar%Flp_Angle,ErrVar) - CALL ParseInput(UnControllerParameters,CurLine,'Flp_Kp',accINFILE(1),CntrPar%Fl_Kp,ErrVar) + CALL ParseInput(UnControllerParameters,CurLine,'Flp_Kp',accINFILE(1),CntrPar%Flp_Kp,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'Flp_Ki',accINFILE(1),CntrPar%Flp_Ki,ErrVar) CALL ParseInput(UnControllerParameters,CurLine,'Flp_MaxPit',accINFILE(1),CntrPar%Flp_MaxPit,ErrVar) ! END OF INPUT FILE