!**********************************************************************************************************************************
! LICENSING
! Copyright (C) 2015-2016  National Renewable Energy Laboratory
!
!    This file is part of InflowWind.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
!     http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!
!**********************************************************************************************************************************
MODULE InflowWind_Subs

   USE                              InflowWind_Types
   USE                              NWTC_Library

      !-------------------------------------------------------------------------------------------------
      ! The included wind modules (TYPES modules are inherited from InflowWind_Types, so not specified here again.)
      !-------------------------------------------------------------------------------------------------
   USE                              IfW_UniformWind            ! uniform wind files (text files)
   USE                              IfW_TSFFWind               ! TurbSim style full-field binary wind files
   USE                              IfW_BladedFFWind           ! Bladed style full-field binary wind files
   USE                              IfW_UserWind               ! User-defined wind module
   USE                              IfW_HAWCWind               ! full-field binary wind files in HAWC format
   USE                              IfW_4Dext                  ! 4D wind field from external source (e.g., FAST.Farm)

!!!   USE                              FDWind                     ! 4-D binary wind files
!!!   USE                              CTWind                     ! coherent turbulence from KH billow - binary file superimposed on another wind type

   IMPLICIT NONE

! ===================================================================================================
! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m"
!      using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these
!      lines should be modified in the Matlab script and/or Excel worksheet as necessary.
! ===================================================================================================
! This code was generated by Write_ChckOutLst.m at 23-Apr-2015 13:13:13.

     ! Parameters related to output length (number of characters allowed in the output data headers):

   INTEGER(IntKi), PARAMETER      :: OutStrLenM1 = ChanLen - 1

     ! Indices for computing output channels:
     ! NOTES:
     !    (1) These parameters are in the order stored in "OutListParameters.xlsx"
     !    (2) Array y%AllOuts() must be dimensioned to the value of the largest output parameter

     !  Time:

   INTEGER(IntKi), PARAMETER      :: Time      =  0

     ! Wind Motions:

   INTEGER(IntKi), PARAMETER      :: Wind1VelX =  1
   INTEGER(IntKi), PARAMETER      :: Wind1VelY =  2
   INTEGER(IntKi), PARAMETER      :: Wind1VelZ =  3
   INTEGER(IntKi), PARAMETER      :: Wind2VelX =  4
   INTEGER(IntKi), PARAMETER      :: Wind2VelY =  5
   INTEGER(IntKi), PARAMETER      :: Wind2VelZ =  6
   INTEGER(IntKi), PARAMETER      :: Wind3VelX =  7
   INTEGER(IntKi), PARAMETER      :: Wind3VelY =  8
   INTEGER(IntKi), PARAMETER      :: Wind3VelZ =  9
   INTEGER(IntKi), PARAMETER      :: Wind4VelX = 10
   INTEGER(IntKi), PARAMETER      :: Wind4VelY = 11
   INTEGER(IntKi), PARAMETER      :: Wind4VelZ = 12
   INTEGER(IntKi), PARAMETER      :: Wind5VelX = 13
   INTEGER(IntKi), PARAMETER      :: Wind5VelY = 14
   INTEGER(IntKi), PARAMETER      :: Wind5VelZ = 15
   INTEGER(IntKi), PARAMETER      :: Wind6VelX = 16
   INTEGER(IntKi), PARAMETER      :: Wind6VelY = 17
   INTEGER(IntKi), PARAMETER      :: Wind6VelZ = 18
   INTEGER(IntKi), PARAMETER      :: Wind7VelX = 19
   INTEGER(IntKi), PARAMETER      :: Wind7VelY = 20
   INTEGER(IntKi), PARAMETER      :: Wind7VelZ = 21
   INTEGER(IntKi), PARAMETER      :: Wind8VelX = 22
   INTEGER(IntKi), PARAMETER      :: Wind8VelY = 23
   INTEGER(IntKi), PARAMETER      :: Wind8VelZ = 24
   INTEGER(IntKi), PARAMETER      :: Wind9VelX = 25
   INTEGER(IntKi), PARAMETER      :: Wind9VelY = 26
   INTEGER(IntKi), PARAMETER      :: Wind9VelZ = 27

     ! Wind Sensor Measurements:

   INTEGER(IntKi), PARAMETER      :: WindMeas1 = 28
   INTEGER(IntKi), PARAMETER      :: WindMeas2 = 29
   INTEGER(IntKi), PARAMETER      :: WindMeas3 = 30
   INTEGER(IntKi), PARAMETER      :: WindMeas4 = 31
   INTEGER(IntKi), PARAMETER      :: WindMeas5 = 32

     ! The maximum number of output channels which can be output by the code.
   INTEGER(IntKi), PARAMETER      :: MaxOutPts = 32

!End of code generated by Matlab script
! ===================================================================================================

   INTEGER(IntKi), PARAMETER      :: WindMeas(5) = (/ WindMeas1, WindMeas2, WindMeas3, WindMeas4, WindMeas5 /)                                               ! Array of output constants
   INTEGER(IntKi), PARAMETER      :: WindVelX(9) = (/ Wind1VelX, Wind2VelX, Wind3VelX, Wind4VelX, Wind5VelX, Wind6VelX, Wind7VelX, Wind8VelX, Wind9VelX /)   ! Array of output constants
   INTEGER(IntKi), PARAMETER      :: WindVelY(9) = (/ Wind1VelY, Wind2VelY, Wind3VelY, Wind4VelY, Wind5VelY, Wind6VelY, Wind7VelY, Wind8VelY, Wind9VelY /)   ! Array of output constants
   INTEGER(IntKi), PARAMETER      :: WindVelZ(9) = (/ Wind1VelZ, Wind2VelZ, Wind3VelZ, Wind4VelZ, Wind5VelZ, Wind6VelZ, Wind7VelZ, Wind8VelZ, Wind9VelZ /)   ! Array of output constants

! ===================================================================================================

CONTAINS

!====================================================================================================
!>  This public subroutine reads the input required for InflowWind from the file whose name is an
!!     input parameter.
SUBROUTINE InflowWind_ReadInput( InputFileName, EchoFileName, InputFileData, ErrStat, ErrMsg )
!----------------------------------------------------------------------------------------------------

      IMPLICIT                                           NONE

      CHARACTER(*),              PARAMETER            :: RoutineName="InflowWind_ReadInput"

      ! Passed variables
   CHARACTER(*),                       INTENT(IN   )  :: InputFileName        !< name of the input file
   CHARACTER(*),                       INTENT(IN   )  :: EchoFileName         !< name of the echo file
   TYPE(InflowWind_InputFile),         INTENT(INOUT)  :: InputFileData        !< The data for initialization
   INTEGER(IntKi),                     INTENT(  OUT)  :: ErrStat              !< Returned error status  from this subroutine
   CHARACTER(*),                       INTENT(  OUT)  :: ErrMsg               !< Returned error message from this subroutine

      ! Local variables
   INTEGER(IntKi)                                     :: UnitInput            !< Unit number for the input file
   INTEGER(IntKi)                                     :: UnitEcho             !< The local unit number for this module's echo file
   CHARACTER(1024)                                    :: TmpPath              !< Temporary storage for relative path name
   CHARACTER(1024)                                    :: TmpFmt               !< Temporary storage for format statement
   CHARACTER(35)                                      :: Frmt                 !< Output format for logical parameters. (matches NWTC Subroutine Library format)

      ! Temoporary messages
   INTEGER(IntKi)                                     :: TmpErrStat
   CHARACTER(ErrMsgLen)                               :: TmpErrMsg
   CHARACTER(1024)                                    :: PriPath                                   ! Path name of the primary file

      ! Initialize local data

   UnitEcho                = -1
   Frmt                    = "( 2X, L11, 2X, A, T30, ' - ', A )"
   ErrStat                 = ErrID_None
   ErrMsg                  = ""
   InputFileData%EchoFlag  = .FALSE.  ! initialize for error handling (cleanup() routine)
   CALL GetPath( InputFileName, PriPath )    ! Input files will be relative to the path where the primary input file is located.

      ! allocate the array for the OutList
   CALL AllocAry( InputFileData%OutList, MaxOutPts, "InflowWind Input File's OutList", TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

   !-------------------------------------------------------------------------------------------------
   ! Open the file
   !-------------------------------------------------------------------------------------------------

   CALL GetNewUnit( UnitInput, TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )

   CALL OpenFInpFile( UnitInput, TRIM(InputFileName), TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Tell the users what we are doing... though this part should be rather quick.
  !  CALL WrScr( ' Opening InflowWind input file:  '//InputFileName )

   !-------------------------------------------------------------------------------------------------
   ! File header
   !-------------------------------------------------------------------------------------------------

   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file header line 1', TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file header line 2', TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

     ! Echo Input Files.

   CALL ReadVar ( UnitInput, InputFileName, InputFileData%EchoFlag, 'Echo', 'Echo Input', TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! If we are Echoing the input then we should re-read the first three lines so that we can echo them
      ! using the NWTC_Library routines.  The echoing is done inside those routines via a global variable
      ! which we must store, set, and then replace on error or completion.

   IF ( InputFileData%EchoFlag ) THEN

      CALL OpenEcho ( UnitEcho, TRIM(EchoFileName), TmpErrStat, TmpErrMsg )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
      IF (ErrStat >= AbortErrLev) THEN
         CALL CleanUp()
         RETURN
      END IF

      REWIND(UnitInput)

         ! The input file was already successfully read through up to this point, so we shouldn't have any read
         ! errors in the first four lines.  So, we won't worry about checking the error status here.

      CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file header line 1', TmpErrStat, TmpErrMsg, UnitEcho )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )

      CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file header line 2', TmpErrStat, TmpErrMsg, UnitEcho )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )

      CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg, UnitEcho )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )

         ! Echo Input Files.

      CALL ReadVar ( UnitInput, InputFileName, InputFileData%EchoFlag, 'Echo', 'Echo the input file data', TmpErrStat, TmpErrMsg, UnitEcho )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )

   END IF

   !-------------------------------------------------------------------------------------------------
   !> Read general section with wind type, direction, and output point list (applies to all wind types)
   !-------------------------------------------------------------------------------------------------

      ! Read WindType
   CALL ReadVar( UnitInput, InputFileName, InputFileData%WindType, 'WindType', &
               'switch for wind file type (1=steady; 2=uniform; 3=binary TurbSim FF; '//&
               '4=binary Bladed-style FF; 5=HAWC format; 6=User defined)', &
               TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

      ! Read PropagationDir
   CALL ReadVar( UnitInput, InputFileName, InputFileData%PropagationDir, 'PropagationDir', &
               'Direction of wind propagation (meteoroligical direction)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

      ! Read the number of points for the wind velocity output
   CALL ReadVar( UnitInput, InputFileName, InputFileData%NWindVel, 'NWindVel', &
               'Number of points to output the wind velocity (0 to 9)', &
               TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

      ! Before proceeding, make sure that NWindVel makes sense
   IF ( InputFileData%NWindVel < 0 .OR. InputFileData%NwindVel > 9 ) THEN
      CALL SetErrStat( ErrID_Fatal, 'NWindVel must be greater than or equal to zero and less than 10.', &
                        ErrStat, ErrMsg, RoutineName )
      CALL CleanUp()
      RETURN
   ELSE

      ! Allocate space for the output location arrays:
      CALL AllocAry( InputFileData%WindVxiList, InputFileData%NWindVel, 'WindVxiList', TmpErrStat, TmpErrMsg )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
      CALL AllocAry( InputFileData%WindVyiList, InputFileData%NWindVel, 'WindVyiList', TmpErrStat, TmpErrMsg )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
      CALL AllocAry( InputFileData%WindVziList, InputFileData%NWindVel, 'WindVziList', TmpErrStat, TmpErrMsg )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
      IF (ErrStat >= AbortErrLev) THEN
         CALL CleanUp()
         RETURN
      ENDIF
   ENDIF

      ! Read in the values of WindVxiList
   CALL ReadAry( UnitInput, InputFileName, InputFileData%WindVxiList, InputFileData%NWindVel, 'WindVxiList', &
               'List of coordinates in the inertial X direction (m)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

      ! Read in the values of WindVxiList
   CALL ReadAry( UnitInput, InputFileName, InputFileData%WindVyiList, InputFileData%NWindVel, 'WindVyiList', &
               'List of coordinates in the inertial Y direction (m)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

      ! Read in the values of WindVziList
   CALL ReadAry( UnitInput, InputFileName, InputFileData%WindVziList, InputFileData%NWindVel, 'WindVziList', &
               'List of coordinates in the inertial Z direction (m)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for Steady Wind Conditions [used only for WindType = 1]_ section
   !-------------------------------------------------------------------------------------------------

      ! Section separator line
   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HWindSpeed
   CALL ReadVar( UnitInput, InputFileName, InputFileData%Steady_HWindSpeed, 'HWindSpeed', &
                  'Horizontal windspeed for steady wind', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

      ! Read RefHt
   CALL ReadVar( UnitInput, InputFileName, InputFileData%Steady_RefHt, 'RefHt', &
                  'Reference height for horizontal wind speed for steady wind', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

      ! Read PLexp
   CALL ReadVar( UnitInput, InputFileName, InputFileData%Steady_PLexp, 'PLexp', &
                  'Power law exponent for steady wind', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for Uniform wind file [used only for WindType = 2]_ section
   !-------------------------------------------------------------------------------------------------

      ! Section separator line
   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read UniformWindFile
   CALL ReadVar( UnitInput, InputFileName, InputFileData%Uniform_FileName, 'WindFileName', &
                  'Filename of time series data for uniform wind field', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF
   IF ( PathIsRelative( InputFileData%Uniform_FileName ) ) InputFileData%Uniform_FileName = TRIM(PriPath)//TRIM(InputFileData%Uniform_FileName)

      ! Read RefHt
   CALL ReadVar( UnitInput, InputFileName, InputFileData%Uniform_RefHt, 'RefHt', &
                  'Reference height for uniform wind file', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

      ! Read RefLength
   CALL ReadVar( UnitInput, InputFileName, InputFileData%Uniform_RefLength, 'RefLength', &
                  'Reference length for uniform wind file', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for Binary TurbSim Full-Field files [used only for WindType = 3]_ section
   !-------------------------------------------------------------------------------------------------

      ! Section separator line
   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read TSFFWind info
   CALL ReadVar( UnitInput, InputFileName, InputFileData%TSFF_FileName, 'FileName', &
               'Name of the TurbSim full field wind file to use (.bts)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF
   IF ( PathIsRelative( InputFileData%TSFF_FileName ) ) InputFileData%TSFF_FileName = TRIM(PriPath)//TRIM(InputFileData%TSFF_FileName)

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for Binary Bladed-style Full-Field files [used only for WindType = 4]_ section
   !-------------------------------------------------------------------------------------------------

      ! Section separator line
   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read BladedStyle%WindFileName
   CALL ReadVar( UnitInput, InputFileName, InputFileData%BladedFF_FileName, 'FileName', &
               'Rootname of the full-field wind file to use (.wnd, .sum)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF
   IF ( PathIsRelative( InputFileData%BladedFF_FileName ) ) InputFileData%BladedFF_FileName = TRIM(PriPath)//TRIM(InputFileData%BladedFF_FileName)
   InputFileData%BladedFF_FileName = TRIM(InputFileData%BladedFF_FileName)//'.wnd'

      ! Read TowerFileFlag
   CALL ReadVar( UnitInput, InputFileName, InputFileData%BladedFF_TowerFile, 'TowerFileFlag', &
               'Have tower file (.twr) [flag]', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

#ifdef UNUSED_INPUTFILE_LINES

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for coherent turbulence [used only for WindType = 3 or 4]_ section
   !-------------------------------------------------------------------------------------------------

      ! Section separator line
   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read CTTS_Flag
   CALL ReadVar( UnitInput, InputFileName, InputFileData%CTTS_CoherentTurb, 'CTTS_CoherentTurbFlag', &
               'Flag to coherent turbulence', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF

      ! Read CTWind%WindFileName
   CALL ReadVar( UnitInput, InputFileName, InputFileData%CTTS_FileName, 'CTTS_FileName', &
               'Name of coherent turbulence file', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF
   IF ( PathIsRelative( InputFileData%CTTS_FileName ) ) InputFileData%CTTS_FileName = TRIM(PriPath)//TRIM(InputFileData%CTTS_FileName)

      ! Read CTWind%PathName
   CALL ReadVar( UnitInput, InputFileName, InputFileData%CTTS_Path, 'CTTS_Path', &
               'Path to coherent turbulence binary files', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) THEN
      CALL CleanUp()
      RETURN
   ENDIF
   IF ( PathIsRelative( InputFileData%CTTS_Path ) ) InputFileData%CTTS_Path = TRIM(PriPath)//TRIM(InputFileData%CTTS_Path)

#else
   InputFileData%CTTS_CoherentTurb = .FALSE.
#endif

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for HAWC-formatted binary files [used only for WindType = 5]_ section
   !-------------------------------------------------------------------------------------------------

      ! Section separator line
   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_FileName_u
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_FileName_u, 'HAWC_FileName_u', &
               'Name of the file containing the u-component fluctuating wind', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF
   IF ( PathIsRelative( InputFileData%HAWC_FileName_u ) ) InputFileData%HAWC_FileName_u = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_u)

      ! Read HAWC_FileName_v
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_FileName_v, 'HAWC_FileName_v', &
               'Name of the file containing the v-component fluctuating wind', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF
   IF ( PathIsRelative( InputFileData%HAWC_FileName_v ) ) InputFileData%HAWC_FileName_v = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_v)

      ! Read HAWC_FileName_w
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_FileName_w, 'HAWC_FileName_w', &
               'Name of the file containing the w-component fluctuating wind', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF
   IF ( PathIsRelative( InputFileData%HAWC_FileName_w ) ) InputFileData%HAWC_FileName_w = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_w)

      ! Read HAWC_nx
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_nx, 'HAWC_nx', &
               'Number of grids in the x direction (in the 3 files above)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_ny
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_ny, 'HAWC_ny', &
               'Number of grids in the y direction (in the 3 files above)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_nz
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_nz, 'HAWC_nz', &
               'Number of grids in the z direction (in the 3 files above)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_dx
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_dx, 'HAWC_dx', &
               'Number of grids in the x direction (in the 3 files above)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_dy
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_dy, 'HAWC_dy', &
               'Number of grids in the y direction (in the 3 files above)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_dz
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_dz, 'HAWC_dz', &
               'Number of grids in the z direction (in the 3 files above)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_RefHt
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_RefHt, 'HAWC_RefHt', &
               'Reference (hub) height of the grid', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      !----------------------------------------------------------------------------------------------
      !> Read the _Scaling parameters for turbulence (HAWC-format files) [used only for WindType = 5]_ subsection
      !----------------------------------------------------------------------------------------------

      ! Section separator line
   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_ScaleMethod
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_ScaleMethod, 'HAWC_ScaleMethod', &
               'Turbulence scaling method [0=none, 1=direct scaling, 2= calculate scaling '// &
               'factor based on a desired standard deviation]', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_SFx
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SFx, 'HAWC_SFx', &
               'Turbulence scaling factor for the x direction [ScaleMethod=1]', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_SFy
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SFy, 'HAWC_SFy', &
               'Turbulence scaling factor for the y direction [ScaleMethod=1]', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_SFz
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SFz, 'HAWC_SFz', &
               'Turbulence scaling factor for the z direction [ScaleMethod=1]', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_SigmaFx
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SigmaFx, 'HAWC_SigmaFx', &
               'Turbulence standard deviation to calculate scaling from in x direction [ScaleMethod=2]', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_SigmaFy
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SigmaFy, 'HAWC_SigmaFy', &
               'Turbulence standard deviation to calculate scaling from in y direction [ScaleMethod=2]', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_SigmaFz
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_SigmaFz, 'HAWC_SigmaFz', &
               'Turbulence standard deviation to calculate scaling from in z direction [ScaleMethod=2]', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

#ifdef UNUSED_INPUTFILE_LINES

!FIXME:  TStart has no comment
      ! Read HAWC_TStart
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_TStart, 'HAWC_TStart', &
               '', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

!FIXME:  TEnd has no comment
      ! Read HAWC_TEnd
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_TEnd, 'HAWC_TEnd', &
               '', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

#endif

      !----------------------------------------------------------------------------------------------
      !> Read the _Mean wind profile paramters (added to HAWC-format files) [used only for WindType = 5]_ subsection
      !----------------------------------------------------------------------------------------------

      ! Section separator line
   CALL ReadCom( UnitInput, InputFileName, 'InflowWind input file separator line', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_URef
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_URef, 'HAWC_URef', &
               'Mean u-component wind speed at the reference height', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_ProfileType
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_ProfileType, 'HAWC_ProfileType', &
               'Wind profile type (0=constant;1=logarithmic;2=power law)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_PLExp
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_PLExp, 'HAWC_PLExp', &
               'Power law exponent (used for PL wind profile type only)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_Z0
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_Z0, 'HAWC_Z0', &
               'Surface roughness length (used for LOG wind profile type only)', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! Read HAWC_InitPosition   (Shift of wind box)  NOTE: This an optional input!!!!
   InputFileData%HAWC_InitPosition(2:3) = 0.0_ReKi    ! We are only using X, so only read in one.  The data can handle 3 coords
   CALL ReadVar( UnitInput, InputFileName, InputFileData%HAWC_InitPosition(1), 'HAWC_Position', &
               'Initial position of the HAWC wind file (shift along X usually)', TmpErrStat, TmpErrMsg, UnitEcho )
   if (TmpErrStat == ErrID_None) then
      !---------------------- OUTPUT --------------------------------------------------
      CALL ReadCom( UnitInput, InputFileName, 'Section Header: Output', TmpErrStat, TmpErrMsg, UnitEcho )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
      IF (ErrStat >= AbortErrLev) THEN
         CALL Cleanup()
         RETURN
      END IF
   else
      InputFileData%HAWC_InitPosition = 0.0_ReKi
      TmpErrStat  =  ErrID_None  ! reset
      TmpErrMsg   =  ""
      ! NOTE: since we read in a line that wasn't a number, what we actually read was the header.
   endif

      ! SumPrint - Print summary data to <RootName>.IfW.sum (flag):
   CALL ReadVar( UnitInput, InputFileName, InputFileData%SumPrint, "SumPrint", "Print summary data to <RootName>.IfW.sum (flag)", TmpErrStat, TmpErrMsg, UnitEcho)
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

   !---------------------- OUTLIST  --------------------------------------------
   CALL ReadCom( UnitInput, InputFileName, 'Section Header: OutList', TmpErrStat, TmpErrMsg, UnitEcho )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

      ! OutList - List of user-requested output channels (-):     -- uses routine from the NWTC_Library
   CALL ReadOutputList ( UnitInput, InputFileName, InputFileData%OutList, InputFileData%NumOuts, 'OutList',    &
               "List of user-requested output channels", TmpErrStat, TmpErrMsg, UnitEcho  )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

   !-------------------------------------------------------------------------------------------------
   ! This is the end of the input file
   !-------------------------------------------------------------------------------------------------

   CALL Cleanup()

   RETURN

      CONTAINS
         !..............................
         SUBROUTINE Cleanup()

               ! Close input file
            CLOSE ( UnitInput )

               ! Cleanup the Echo file and global variables
            IF ( InputFileData%EchoFlag ) THEN
               CLOSE(UnitEcho)
            END IF

         END SUBROUTINE Cleanup

END SUBROUTINE InflowWind_ReadInput

!====================================================================================================
!> This private subroutine verifies the input required for InflowWind is correctly specified.  This
!! routine checkes all the parameters that are common with all the wind types, then calls subroutines
!! that check the parameters specific to each wind type.  Only the parameters corresponding to the
!! desired wind type are evaluated; the rest are ignored.  Additional checks will be performed after
!! the respective wind file has been read in, but these checks will be performed within the respective
!! wind module.
!
!  The reason for structuring it this way is to allow for relocating the validation routines for the
!  wind type into their respective modules. It might also prove useful later if we change languages
!  but retain the fortran wind modules.
SUBROUTINE InflowWind_ValidateInput( InitInp, InputFileData, ErrStat, ErrMsg )

      IMPLICIT                                           NONE

      CHARACTER(*),              PARAMETER            :: RoutineName="InflowWind_ValidateInput"

      ! Passed variables

   TYPE(InflowWind_InitInputType),     INTENT(IN   )  :: InitInp              !< Input data for initialization
   TYPE(InflowWind_InputFile),         INTENT(INOUT)  :: InputFileData        !< The data for initialization
   INTEGER(IntKi),                     INTENT(  OUT)  :: ErrStat              !< Error status  from this subroutine
   CHARACTER(*),                       INTENT(  OUT)  :: ErrMsg               !< Error message from this subroutine

      ! Temporary variables
   INTEGER(IntKi)                                     :: TmpErrStat           !< Temporary error status  for subroutine and function calls
   CHARACTER(ErrMsgLen)                               :: TmpErrMsg            !< Temporary error message for subroutine and function calls

      ! Local variables
   INTEGER(IntKi)                                     :: I                    !< Generic counter

      ! Initialize ErrStat

   ErrStat = ErrID_None
   ErrMsg  = ""

      !-----------------------------------------------
      ! Data that applies to all wind types
      !-----------------------------------------------

      ! WindType
   IF ( InputFileData%WindType <= Undef_WindNumber .OR. InputFileData%WindType > Highest_WindNumber ) THEN
      CALL SetErrStat(ErrID_Fatal,' Invalid WindType specified.  Only values of '//   &
            TRIM(Num2LStr( Steady_WindNumber    ))//','// &
            TRIM(Num2LStr( Uniform_WindNumber   ))//','// &
            TRIM(Num2LStr( TSFF_WindNumber      ))//','// &
            TRIM(Num2LStr( BladedFF_WindNumber  ))//','// &
            TRIM(Num2LStr( HAWC_WindNumber      ))//', or '// &
            TRIM(Num2LStr( User_WindNumber      ))//' are supported.', &
            ErrStat,ErrMsg,RoutineName)
      RETURN
   ENDIF

   if (InitInp%Linearize) then

      if (InputFileData%WindType /= Steady_WindNumber .and. InputFileData%WindType /= Uniform_WindNumber) then
         call SetErrStat(ErrID_Fatal, 'InflowWind can perform linearization on only steady or uniform wind types.', ErrStat, ErrMsg, RoutineName)
         return
      end if

      if (InitInp%lidar%SensorType /= SensorType_None) then
         call SetErrStat(ErrID_Fatal, 'InflowWind can not perform linearization with the lidar module enabled.', ErrStat, ErrMsg, RoutineName)
         return
      end if

   end if

      ! make sure that all values for WindVzi are above ground.  Set to 0 otherwise.

   DO I = 1, InputFileData%NWindVel
      IF ( InputFileData%WindVziList(I) <= 0.0_ReKi ) THEN
         CALL SetErrStat( ErrID_Warn, ' Requested wind velocity at point ( '//   &
               TRIM(Num2LStr(InputFileData%WindVxiList(I)))//', '//              &
               TRIM(Num2LStr(InputFileData%WindVyiList(I)))//', '//              &
               TRIM(Num2LStr(InputFileData%WindVziList(I)))//') is below ground. Ignoring this point.', &
               ErrStat, ErrMsg, RoutineName)
         InputFileData%WindVziList(I)  =  0.0_ReKi
      ENDIF
   ENDDO

      !-----------------------------------------------
      ! Data specific to a WindType
      !-----------------------------------------------

   SELECT CASE ( InputFileData%WindType )

      CASE ( Steady_WindNumber )
         CALL Steady_ValidateInput()

      CASE ( Uniform_WindNumber )
         CALL Uniform_ValidateInput()

      CASE ( TSFF_WindNumber )
         CALL TSFF_ValidateInput()

      CASE ( BladedFF_WindNumber )
         CALL BladedFF_ValidateInput()

      CASE ( HAWC_WindNumber )
         CALL HAWC_ValidateInput()

      CASE ( User_WindNumber )
         CALL User_ValidateInput()

      CASE (FDext_WindNumber)
         IF ( .not. InitInp%Use4Dext) call SetErrStat(ErrID_Fatal,'4D external wind file is valid only with FAST.Farm',ErrStat,ErrMsg,RoutineName)
      CASE DEFAULT  ! keep this check to make sure that all new wind types have been accounted for
         CALL SetErrStat(ErrID_Fatal,' Undefined wind type.',ErrStat,ErrMsg,RoutineName)

   END SELECT

      ! NOTE: we are not checking the error status yet, but rather will run through the Coherent turbulence section
      !        before returning.  This way a user will know of errors there as well before having to fix the first
      !        section.

      ! Coherent turbulence
   IF ( InputFileData%CTTS_CoherentTurb ) THEN
      CALL CTTS_ValidateInput()
   ENDIF

   RETURN

CONTAINS

   !> This subroutine checks that the values for the steady wind make sense.  There aren't many to check.
   SUBROUTINE Steady_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="Steady_ValidateInput"

         ! Check that HWindSpeed is positive
      IF ( InputFileData%Steady_HWindSpeed <= 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Horizontal wind speed (HWindSpeed) for steady winds must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that RefHt is positive
      IF ( InputFileData%Steady_RefHt <= 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Reference height (RefHt) for steady winds must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that PLexp is positive
      IF ( InputFileData%Steady_PLexp < 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Power law exponent (PLexp) for steady winds must be positive or zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      RETURN
   END SUBROUTINE Steady_ValidateInput

   !> This subroutine checks that the values for the uniform wind make sense.  There isn't much to check.
   SUBROUTINE Uniform_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="Uniform_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

         ! Check that the filename requested actually exists
      INQUIRE( file=InputFileData%Uniform_FileName, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find uniform wind input file: '"//TRIM(InputFileData%Uniform_FileName)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that RefHt is positive
      IF ( InputFileData%Uniform_RefHt <= 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Reference height (RefHt) for uniform winds must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that RefLength is positive
      IF ( InputFileData%Uniform_RefLength <= 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Reference length (RefLength) for uniform winds must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      RETURN

   END SUBROUTINE Uniform_ValidateInput

   !> There isn't much to check for the TurbSim full-field section of the input file.
   SUBROUTINE TSFF_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="TSFF_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

         ! Check that the filename requested actually exists
      INQUIRE( file=InputFileData%TSFF_FileName, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find TurbSim full-field wind input file: '"//TRIM(InputFileData%TSFF_FileName)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      RETURN

   END SUBROUTINE TSFF_ValidateInput

   !> We will only check the main bladed input file here.  We will check the existance of the towerfile (if requested) in the SetParameters routine.
   SUBROUTINE BladedFF_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="BladedFF_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

         ! Check that the filename requested actually exists
      INQUIRE( file=InputFileData%BladedFF_FileName, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find Bladed-style full-field wind input file: '"//TRIM(InputFileData%BladedFF_FileName)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      RETURN
   END SUBROUTINE BladedFF_ValidateInput

   !> This routine checks to see if coherent turbulence can be used or not.  We check the path and file
   !! information before proceeding.  The coherent turbulence info is loaded after the full-field files have
   !! been read, and those can be very slow to load.  So we preemptively check the existence of the coherent
   !! turbulence file.  Further checking that the coherent turbulence can actually be used is check by the
   !! CTTS_Wind submodule (after the loading of the other stuff).
   SUBROUTINE CTTS_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="CTTS_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

         ! Check if coherent turbulence can be used or not.  It is only applicable to the TSFF and BladedFF wind
         ! file types.
      IF ( InputFileData%CTTS_CoherentTurb .AND. ( .NOT. InputFileData%WindType == TSFF_WindNumber ) &
               .AND. ( .NOT. InputFileData%WindType == BladedFF_WindNumber ) ) THEN
         CALL SetErrStat(ErrID_Fatal,' Coherent turbulence may only be used with full field wind data (WindType = '// &
               TRIM(Num2LStr(TSFF_WindNumber))//' or '//TRIM(Num2LStr(BladedFF_WindNumber))//').', &
               ErrStat,ErrMsg,RoutineName)
         RETURN
      ENDIF

         ! Check that the CT input file exists.
      INQUIRE( file=InputFileData%CTTS_FileName, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find the coherent turbulence input file: '"//TRIM(InputFileData%CTTS_FileName)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that the CT Event path exists.
      ! Unfortunately there is no good portable method to do this, so we won't try.
!FIXME: See if we have anything in the library.  -- Nothing I found...

      RETURN

   END SUBROUTINE CTTS_ValidateInput

   !> This routine checks the HAWC wind file type section of the input file.
   SUBROUTINE HAWC_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="HAWC_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

         ! Check that the wind files exist.
      INQUIRE( file=InputFileData%HAWC_FileName_u, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find the HAWC u-component wind file: '"//TRIM(InputFileData%HAWC_FileName_u)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      INQUIRE( file=InputFileData%HAWC_FileName_v, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find the HAWC v-component wind file: '"//TRIM(InputFileData%HAWC_FileName_v)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF
      INQUIRE( file=InputFileData%HAWC_FileName_w, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find the HAWC w-component wind file: '"//TRIM(InputFileData%HAWC_FileName_w)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that the number of grids make some sense
      IF ( InputFileData%HAWC_nx <= 0_IntKi ) THEN
         CALL SetErrStat( ErrID_Fatal,' Number of grids in the x-direction of HAWC wind files (nx) must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      IF ( InputFileData%HAWC_ny <= 0_IntKi ) THEN
         CALL SetErrStat( ErrID_Fatal,' Number of grids in the y-direction of HAWC wind files (ny) must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      IF ( InputFileData%HAWC_nz <= 0_IntKi ) THEN
         CALL SetErrStat( ErrID_Fatal,' Number of grids in the z-direction of HAWC wind files (nz) must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that the distance between points is positive
      IF ( InputFileData%HAWC_dx <= 0.0_ReKi ) THEN
         CALL SetErrStat( ErrID_Fatal,' Distance between points in the x-direction of HAWC wind files (dx) must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      IF ( InputFileData%HAWC_dy <= 0.0_ReKi ) THEN
         CALL SetErrStat( ErrID_Fatal,' Distance between points in the y-direction of HAWC wind files (dy) must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      IF ( InputFileData%HAWC_dz <= 0.0_ReKi ) THEN
         CALL SetErrStat( ErrID_Fatal,' Distance between points in the z-direction of HAWC wind files (dz) must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that RefHt is positive
      IF ( InputFileData%HAWC_RefHt <= 0.0_ReKi ) THEN
         CALL SetErrStat( ErrID_Fatal,' Reference height (RefHt) for HAWC winds must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         !----------------------
         ! Scaling method info
         !----------------------

      IF ( ( InputFileData%HAWC_ScaleMethod < 0_IntKi ) .OR. ( InputFileData%HAWC_ScaleMethod > 3_IntKi ) ) THEN
         CALL SetErrStat( ErrID_Fatal,' The scaling method (ScaleMethod) for HAWC winds can only be 0, 1, or 2.', &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check the other scaling parameters
      SELECT CASE( InputFileData%HAWC_ScaleMethod )

      CASE ( 1 )

         IF ( InputFileData%HAWC_SFx <= 0.0_ReKi ) THEN
            CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SFx must be greater than zero.', &
                  ErrStat,ErrMsg,RoutineName)
         ENDIF

         IF ( InputFileData%HAWC_SFy <= 0.0_ReKi ) THEN
            CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SFy must be greater than zero.', &
                  ErrStat,ErrMsg,RoutineName)
         ENDIF

         IF ( InputFileData%HAWC_SFz <= 0.0_ReKi ) THEN
            CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SFz must be greater than zero.', &
                  ErrStat,ErrMsg,RoutineName)
         ENDIF

      CASE ( 2 )

         IF ( InputFileData%HAWC_SigmaFx <= 0.0_ReKi ) THEN
            CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SigmaFx must be greater than zero.', &
                  ErrStat,ErrMsg,RoutineName)
         ENDIF

         IF ( InputFileData%HAWC_SigmaFy <= 0.0_ReKi ) THEN
            CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SigmaFy must be greater than zero.', &
                  ErrStat,ErrMsg,RoutineName)
         ENDIF

         IF ( InputFileData%HAWC_SigmaFz <= 0.0_ReKi ) THEN
            CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling paramter SigmaFz must be greater than zero.', &
                  ErrStat,ErrMsg,RoutineName)
         ENDIF

      END SELECT

         ! Start and end times of the scaling, but only if ScaleMethod is set.
      IF ( ( InputFileData%HAWC_ScaleMethod == 1_IntKi ) .OR. (InputFileData%HAWC_ScaleMethod == 2_IntKi ) ) THEN
            ! Check that the start time is >= 0.  NOTE: this may be an invalid test.
         !IF ( InputFileData%HAWC_TStart < 0.0_ReKi ) THEN
         !   CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling TStart must be zero or positive.',  &
         !         ErrStat,ErrMsg,RoutineName)
         !ENDIF

         !IF ( InputFileData%HAWC_TStart < 0.0_ReKi ) THEN
         !   CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling TStart must be zero or positive.',  &
         !         ErrStat,ErrMsg,RoutineName)
         !ENDIF

         !IF ( InputFileData%HAWC_TStart > InputFileData%HAWC_TEnd ) THEN
         !   CALL SetErrStat( ErrID_Fatal,' HAWC wind scaling start time must occur before the end time.',   &
         !         ErrStat,ErrMsg,RoutineName)
         !ENDIF

!FIXME:  How do we want to handle having the start and end times both being exactly zero?  Is that a do not apply, or apply for all time?
!FIXME:  What about TStart == TEnd ?
         !IF ( EqualRealNos( InputFileData%HAWC_TStart, InputFileData%HAWC_TEnd ) ) THEN
         !   CALL SetErrStat( ErrID_Severe,' Start and end time for HAWC wind scaling are identical.  No time elapses.', &
         !         ErrStat,ErrMsg,RoutineName)
         !ENDIF

      ENDIF

      RETURN

   END SUBROUTINE HAWC_ValidateInput

   !> This routine is a placeholder for later development by the end user.  It should only be used if additional inputs
   !! for the user defined routines are added to the input file. Otherwise, this routine may be ignored by the user.
   SUBROUTINE User_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="User_ValidateInput"

      CALL SetErrStat( ErrID_Warn,' This subroutine has not been written yet.',ErrStat,ErrMsg,RoutineName)

      RETURN
   END SUBROUTINE User_ValidateInput

END SUBROUTINE InflowWind_ValidateInput

!====================================================================================================
!> This private subroutine copies the info from the input file over to the parameters for InflowWind.
SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrMsg )

      IMPLICIT                                           NONE

      CHARACTER(*),              PARAMETER            :: RoutineName="InflowWind_SetParameters"

      ! Passed variables

   TYPE(InflowWind_InitInputType),     INTENT(IN   )  :: InitInp              !< Input data for initialization
   TYPE(InflowWind_InputFile),         INTENT(INOUT)  :: InputFileData        !< The data for initialization
   TYPE(InflowWind_ParameterType),     INTENT(INOUT)  :: p                    !< The parameters for InflowWind
   TYPE(InflowWind_MiscVarType),       INTENT(INOUT)  :: m                    !< The misc/optimization variables for InflowWind
   INTEGER(IntKi),                     INTENT(  OUT)  :: ErrStat              !< Error status  from this subroutine
   CHARACTER(*),                       INTENT(  OUT)  :: ErrMsg               !< Error message from this subroutine

      ! Temporary variables
   INTEGER(IntKi)                                     :: TmpErrStat           !< Temporary error status  for subroutine and function calls
   CHARACTER(ErrMsgLen)                               :: TmpErrMsg            !< Temporary error message for subroutine and function calls

      ! Local variables
   INTEGER(IntKi)                                     :: I                    !< Generic counter

      ! Initialize ErrStat

   ErrStat = ErrID_None
   ErrMsg  = ""

   !-----------------------------------------------------------------
   ! Copy over the general information that applies to everything
   !-----------------------------------------------------------------

      ! Copy the WindType over.

   p%WindType   =  InputFileData%WindType

      ! Convert the PropagationDir to radians and store this.  For simplicity, we will shift it to be between -pi and pi

   p%PropagationDir   = D2R * InputFileData%PropagationDir
   CALL MPi2Pi( p%PropagationDir )         ! Shift if necessary so that the value is between -pi and pi

      ! Copy over the list of wind coordinates.  Move the arrays to the new one.
   p%NWindVel   =  InputFileData%NWindVel
   CALL AllocAry( p%WindViXYZ, 3, p%NWindVel,   &
         "XYZ coordinates of the requested wind points.", TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) RETURN

   p%WindViXYZ(1,1:p%NWindVel)  =  InputFileData%WindVxiList(1:p%NWindVel)
   p%WindViXYZ(2,1:p%NWindVel)  =  InputFileData%WindVyiList(1:p%NWindVel)
   p%WindViXYZ(3,1:p%NWindVel)  =  InputFileData%WindVziList(1:p%NWindVel)

      ! Set the number of OutList names read in from the file
   p%NumOuts =  InputFileData%NumOuts

      ! Allocate array used for calculating the AllOuts values.  This has a total of nine 3d velocity vectors
      ! in it (one for each of the possible WindViXYZ points).  This gets passed into CalcOutput at each timestep,
      ! although only the first NWindVel velocity vectors gets calculated.
   CALL AllocAry( m%WindViUVW, 3, p%NWindVel, &
               "Array of wind velocities corresponding to the WindViLists", TmpErrStat, TmpErrMsg )
   CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
   IF ( ErrStat>= AbortErrLev ) RETURN
   m%WindViUVW   =  0.0_ReKi

      ! Set the OutList
   CALL SetOutParam( InputFileData%OutList, p, TmpErrStat, TmpErrmsg )
   CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
   IF ( ErrStat>= AbortErrLev ) RETURN

   if (InitInp%Linearize) then
      CALL SetOutParamLin( p, TmpErrStat, TmpErrmsg )
           CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
   end if

      ! Allocate array for AllOuts
   ALLOCATE( m%AllOuts(0:MaxOutPts), Stat=TmpErrStat )
   IF (TmpErrStat/=0) THEN
      CALL SetErrStat(ErrID_Fatal,"Error allocating memory for m%AllOuts.",ErrStat,ErrMsg,RoutineName)
      RETURN
   END IF
   m%AllOuts = 0.0_ReKi

         !-------------------------------------------------------------
         ! Setup the coordinate transforms for rotating the wind field.
         !-------------------------------------------------------------

      !> Perform coordinate transformation using the PropagationDir value read in from the input file (the value stored in parameters
      !! was converted from degrees to radians already).
      !! @note    The PropagationDir is given in Meteorological \f$\Delta\phi\f$, so this is the negative of the \f$\Delta\phi\f$
      !!          for polar coordinates

      ! Create the rotation matrices -- rotate from XYZ to X'Y'Z' (wind aligned along X) coordinates
   p%RotToWind(1,:) = (/    COS(-p%PropagationDir),   SIN(-p%PropagationDir),     0.0_ReKi  /)
   p%RotToWind(2,:) = (/   -SIN(-p%PropagationDir),   COS(-p%PropagationDir),     0.0_ReKi  /)
   p%RotToWind(3,:) = (/                  0.0_ReKi,                 0.0_ReKi,     1.0_ReKi  /)

      ! Create the rotation matrices -- rotate from X'Y'Z' (wind aligned along X) to global XYZ coordinates
   p%RotFromWind =  TRANSPOSE(p%RotToWind)

      ! Create the array used for holding the rotated list of WindViXYZ coordinates in the wind reference frame, and populate it
   CALL AllocAry( p%WindViXYZprime, 3, p%NWindVel, 'Array for WindViXYZ coordinates in the wind reference frame', &
               TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF ( ErrStat>= AbortErrLev ) RETURN

   p%WindViXYZprime   =  0.0_ReKi
   DO I = 1,p%NWindVel
      p%WindViXYZprime(:,I) =  MATMUL( p%RotToWind, p%WindViXYZ(:,I) )
   ENDDO

END SUBROUTINE InflowWind_SetParameters

!**********************************************************************************************************************************
! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m"
!      using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these
!      lines should be modified in the Matlab script and/or Excel worksheet as necessary.
! This code was generated by Write_ChckOutLst.m at 23-Apr-2015 13:13:13.
!----------------------------------------------------------------------------------------------------------------------------------
SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg )
! This routine checks to see if any requested output channel names (stored in the OutList(:)) are invalid. It returns a
! warning if any of the channels are not available outputs from the module.
!  It assigns the settings for OutParam(:) (i.e, the index, name, and units of the output channels, WriteOutput(:)).
!  the sign is set to 0 if the channel is invalid.
! It sets assumes the value p%NumOuts has been set before this routine has been called, and it sets the values of p%OutParam here.
!..................................................................................................................................

   IMPLICIT                        NONE

      ! Passed variables

   CHARACTER(ChanLen),              INTENT(IN)     :: OutList(:)         !< The list out user-requested outputs
   TYPE(InflowWind_ParameterType),  INTENT(INOUT)  :: p                  !< The module parameters
   INTEGER(IntKi),                  INTENT(OUT)    :: ErrStat            !< The error status code
   CHARACTER(*),                    INTENT(OUT)    :: ErrMsg             !< The error message, if an error occurred

      ! Local variables

   INTEGER                      :: ErrStat2                                        ! temporary (local) error status
   INTEGER                      :: I                                               ! Generic loop-counting index
   INTEGER                      :: J                                               ! Generic loop-counting index
   INTEGER                      :: INDX                                            ! Index for valid arrays

   LOGICAL                      :: CheckOutListAgain                               ! Flag used to determine if output parameter starting with "M" is valid (or the negative of another parameter)
   LOGICAL                      :: InvalidOutput(0:MaxOutPts)                      ! This array determines if the output channel is valid for this configuration
   CHARACTER(ChanLen)           :: OutListTmp                                      ! A string to temporarily hold OutList(I)
   CHARACTER(*), PARAMETER      :: RoutineName = "SetOutParam"

   CHARACTER(OutStrLenM1), PARAMETER  :: ValidParamAry(32) =  (/ &                  ! This lists the names of the allowed parameters, which must be sorted alphabetically
                               "WIND1VELX","WIND1VELY","WIND1VELZ","WIND2VELX","WIND2VELY","WIND2VELZ","WIND3VELX", &
                               "WIND3VELY","WIND3VELZ","WIND4VELX","WIND4VELY","WIND4VELZ","WIND5VELX","WIND5VELY", &
                               "WIND5VELZ","WIND6VELX","WIND6VELY","WIND6VELZ","WIND7VELX","WIND7VELY","WIND7VELZ", &
                               "WIND8VELX","WIND8VELY","WIND8VELZ","WIND9VELX","WIND9VELY","WIND9VELZ","WINDMEAS1", &
                               "WINDMEAS2","WINDMEAS3","WINDMEAS4","WINDMEAS5"/)
   INTEGER(IntKi), PARAMETER :: ParamIndxAry(32) =  (/ &                            ! This lists the index into AllOuts(:) of the allowed parameters ValidParamAry(:)
                                Wind1VelX , Wind1VelY , Wind1VelZ , Wind2VelX , Wind2VelY , Wind2VelZ , Wind3VelX , &
                                Wind3VelY , Wind3VelZ , Wind4VelX , Wind4VelY , Wind4VelZ , Wind5VelX , Wind5VelY , &
                                Wind5VelZ , Wind6VelX , Wind6VelY , Wind6VelZ , Wind7VelX , Wind7VelY , Wind7VelZ , &
                                Wind8VelX , Wind8VelY , Wind8VelZ , Wind9VelX , Wind9VelY , Wind9VelZ , WindMeas1 , &
                                WindMeas2 , WindMeas3 , WindMeas4 , WindMeas5 /)
   CHARACTER(ChanLen), PARAMETER :: ParamUnitsAry(32) =  (/ &                     ! This lists the units corresponding to the allowed parameters
                               "(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ", &
                               "(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ", &
                               "(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ", &
                               "(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     ", &
                               "(m/s)     ","(m/s)     ","(m/s)     ","(m/s)     "/)

      ! Initialize values
   ErrStat = ErrID_None
   ErrMsg = ""
   InvalidOutput = .FALSE.

!   ..... Developer must add checking for invalid inputs here: .....
      ! NOTE:  we are not checking that the coordinates input for the WindVxi, WindVyi, and WindVzi are valid here.  We are
      !        checking that at the input file validation (they simply get zeroed with a warning if there is an issue).

      ! make sure we don't ask for outputs that don't exist:
   DO I = p%NWindVel+1, 9
      InvalidOutput( WindVelX(I) ) =  .TRUE.
      InvalidOutput( WindVelY(I) ) =  .TRUE.
      InvalidOutput( WindVelZ(I) ) =  .TRUE.
   END DO

   DO I=p%lidar%NumPulseGate+1,5
      InvalidOutput( WindMeas(I) ) = .TRUE.
   END DO

!   ................. End of validity checking .................

   !-------------------------------------------------------------------------------------------------
   ! Allocate and set index, name, and units for the output channels
   ! If a selected output channel is not available in this module, set error flag.
   !-------------------------------------------------------------------------------------------------

   ALLOCATE ( p%OutParam(0:p%NumOuts) , STAT=ErrStat2 )
   IF ( ErrStat2 /= 0_IntKi )  THEN
      CALL SetErrStat( ErrID_Fatal,"Error allocating memory for the InflowWind OutParam array.", ErrStat, ErrMsg, RoutineName )
      RETURN
   ENDIF

      ! Set index, name, and units for the time output channel:

   p%OutParam(0)%Indx  = Time
   p%OutParam(0)%Name  = "Time"    ! OutParam(0) is the time channel by default.
   p%OutParam(0)%Units = "(s)"
   p%OutParam(0)%SignM = 1

      ! Set index, name, and units for all of the output channels.
      ! If a selected output channel is not available by this module set ErrStat = ErrID_Warn.

   DO I = 1,p%NumOuts

      p%OutParam(I)%Name  = OutList(I)
      OutListTmp          = OutList(I)

      ! Reverse the sign (+/-) of the output channel if the user prefixed the
      !   channel name with a "-", "_", "m", or "M" character indicating "minus".

      CheckOutListAgain = .FALSE.

      IF      ( INDEX( "-_", OutListTmp(1:1) ) > 0 ) THEN
         p%OutParam(I)%SignM = -1                         ! ex, "-TipDxc1" causes the sign of TipDxc1 to be switched.
         OutListTmp          = OutListTmp(2:)
      ELSE IF ( INDEX( "mM", OutListTmp(1:1) ) > 0 ) THEN ! We'll assume this is a variable name for now, (if not, we will check later if OutListTmp(2:) is also a variable name)
         CheckOutListAgain   = .TRUE.
         p%OutParam(I)%SignM = 1
      ELSE
         p%OutParam(I)%SignM = 1
      END IF

      CALL Conv2UC( OutListTmp )    ! Convert OutListTmp to upper case

      Indx = IndexCharAry( OutListTmp(1:OutStrLenM1), ValidParamAry )

         ! If it started with an "M" (CheckOutListAgain) we didn't find the value in our list (Indx < 1)

      IF ( CheckOutListAgain .AND. Indx < 1 ) THEN    ! Let's assume that "M" really meant "minus" and then test again
         p%OutParam(I)%SignM = -1                     ! ex, "MTipDxc1" causes the sign of TipDxc1 to be switched.
         OutListTmp          = OutListTmp(2:)

         Indx = IndexCharAry( OutListTmp(1:OutStrLenM1), ValidParamAry )
      END IF

      IF ( Indx > 0 ) THEN ! we found the channel name
         p%OutParam(I)%Indx     = ParamIndxAry(Indx)
         IF ( InvalidOutput( ParamIndxAry(Indx) ) ) THEN  ! but, it isn't valid for these settings
            p%OutParam(I)%Units = "INVALID"
            p%OutParam(I)%SignM = 0
         ELSE
            p%OutParam(I)%Units = ParamUnitsAry(Indx) ! it's a valid output
         END IF
      ELSE ! this channel isn't valid
         p%OutParam(I)%Indx  = Time                 ! pick any valid channel (I just picked "Time" here because it's universal)
         p%OutParam(I)%Units = "INVALID"
         p%OutParam(I)%SignM = 0                    ! multiply all results by zero

         CALL SetErrStat(ErrID_Warn, TRIM(p%OutParam(I)%Name)//" is not an available output channel.",ErrStat,ErrMsg,RoutineName)
      END IF

   END DO

   RETURN
END SUBROUTINE SetOutParam
!----------------------------------------------------------------------------------------------------------------------------------
!End of code generated by Matlab script
!**********************************************************************************************************************************

!..................................................................................................................................
!> This routine checks to see if any requested output channel names are to be output in linearization analysis.
!! note that we output all WriteOutput values and assume that none of them depend on inputs (so I don't need this mapping any more)
SUBROUTINE SetOutParamLin( p, ErrStat, ErrMsg )

   IMPLICIT                        NONE

      ! Passed variables

   TYPE(InflowWind_ParameterType),    INTENT(INOUT)  :: p                  !< The module parameters
   INTEGER(IntKi),                    INTENT(OUT)    :: ErrStat            !< The error status code
   CHARACTER(*),                      INTENT(OUT)    :: ErrMsg             !< The error message, if an error occurred

      ! Local variables

   INTEGER                      :: ErrStat2                                        ! temporary (local) error status
   INTEGER                      :: I                                               ! Generic loop-counting index
   INTEGER                      :: J                                               ! Generic loop-counting index
   CHARACTER(ErrMsgLen)         :: ErrMsg2
   CHARACTER(*), PARAMETER      :: RoutineName = "SetOutParamLin"

   ErrStat = ErrID_None
   ErrMsg  = ""

   call AllocAry(p%OutParamLinIndx, 2, p%NumOuts, 'OutParamLinIndx', ErrStat2, ErrMsg2)
   call setErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName)
   if (ErrStat >= AbortErrLev) return

   p%OutParamLinIndx = 0
   do i = 1,p%NumOuts
      if (p%OutParam(i)%SignM /= 0 ) then

         do j=1,size(WindVelX)
            if ( p%OutParam(i)%Indx == WindVelX(j) ) then
               p%OutParamLinIndx(1,i) = j
               p%OutParamLinIndx(2,i) = 1
               exit !exit j loop; move to next parameter
            elseif ( p%OutParam(i)%Indx == WindVelY(j) ) then
               p%OutParamLinIndx(1,i) = j
               p%OutParamLinIndx(2,i) = 2
               exit !exit j loop; move to next parameter
            elseif ( p%OutParam(i)%Indx == WindVelZ(j) ) then
               p%OutParamLinIndx(1,i) = j
               p%OutParamLinIndx(2,i) = 3
               exit !exit j loop; move to next parameter
            end if
         end do

      end if
   end do

END SUBROUTINE SetOutParamLin
!..................................................................................................................................
SUBROUTINE SetAllOuts( p, y, m, ErrStat, ErrMsg )

   IMPLICIT                                              NONE

   CHARACTER(*),              PARAMETER               :: RoutineName="SetAllOuts"

   TYPE(InflowWind_ParameterType),     INTENT(IN   )  :: p            !< The parameters for InflowWind
   TYPE(InflowWind_OutputType),        INTENT(IN   )  :: y            !< Outputs
   TYPE(InflowWind_MiscVarType),       INTENT(INOUT)  :: m            !< Misc variables for optimization (not copied in glue code)
   INTEGER(IntKi),                     INTENT(  OUT)  :: ErrStat      !< Error status  from this subroutine
   CHARACTER(*),                       INTENT(  OUT)  :: ErrMsg       !< Error message from this subroutine

      ! Local Variables
   INTEGER(IntKi)                                     :: I            ! Generic counter

      ! Initialization
   ErrStat  = ErrID_None
   ErrMsg   = ''

      ! We set the unused values to 0 at init, so we don't need to set them again here:
   DO I = 1,p%NWindVel

      m%AllOuts( WindVelX(I) ) =  m%WindViUVW(1,I)
      m%AllOuts( WindVelY(I) ) =  m%WindViUVW(2,I)
      m%AllOuts( WindVelZ(I) ) =  m%WindViUVW(3,I)

   END DO

      !FIXME:  Add in Wind1Dir, Wind1Mag etc.  -- allthough those can be derived outside of FAST.

   DO I = 1,MIN(5, p%lidar%NumPulseGate )
      m%AllOuts( WindMeas(I) ) = y%lidar%lidSpeed(I)
   END DO

END SUBROUTINE SetAllOuts

!====================================================================================================
SUBROUTINE InflowWind_OpenSumFile( SumFileUnit, SummaryName, IfW_Prog, WindType, ErrStat, ErrMsg )

      ! Passed variables

   INTEGER(IntKi),                  INTENT(  OUT)  :: SumFileUnit    !< the unit number for the InflowWindsummary file
   CHARACTER(*),                    INTENT(IN   )  :: SummaryName    !< the name of the InflowWind summary file
   TYPE(ProgDesc),                  INTENT(IN   )  :: IfW_Prog       !< the name/version/date of the InflowWind program
   INTEGER(IntKi),                  INTENT(IN   )  :: WindType       !< type identifying what wind we are using
   INTEGER(IntKi),                  INTENT(  OUT)  :: ErrStat        !< returns a non-zero value when an error occurs
   CHARACTER(*),                    INTENT(  OUT)  :: ErrMsg         !< Error message if ErrStat /= ErrID_None

   INTEGER(IntKi)                                  :: TmpErrStat     !< Temporary error status for checking how the WRITE worked

       ! Initialize ErrStat

   ErrStat = ErrID_None
   ErrMsg  = ""

   SumFileUnit = -1
   CALL GetNewUnit( SumFileUnit )
   CALL OpenFOutFile ( SumFileUnit, SummaryName, ErrStat, ErrMsg )
   IF (ErrStat >=AbortErrLev) RETURN

         ! Write the summary file header
   WRITE(SumFileUnit,'(/,A/)',IOSTAT=TmpErrStat)   'This summary file was generated by '//TRIM( IfW_Prog%Name )//&
                     ' '//TRIM( IfW_Prog%Ver )//' on '//CurDate()//' at '//CurTime()//'.'
   WRITE(SumFileUnit,'(A14,I1)',IOSTAT=TmpErrStat) '  WindType:   ',WindType
   IF ( TmpErrStat /= 0 ) THEN
      CALL SetErrStat(ErrID_Fatal,'Error writing to summary file.',ErrStat,ErrMsg,'')
      RETURN
   END IF

END SUBROUTINE InflowWind_OpenSumFile

!====================================================================================================
SUBROUTINE InflowWind_CloseSumFile( SumFileUnit, ErrStat, ErrMsg )

      ! Passed variables

   INTEGER(IntKi),                  INTENT(INOUT)  :: SumFileUnit    !< the unit number for the InflowWindsummary file
   INTEGER(IntKi),                  INTENT(  OUT)  :: ErrStat        !< returns a non-zero value when an error occurs
   CHARACTER(*),                    INTENT(  OUT)  :: ErrMsg         !< Error message if ErrStat /= ErrID_None

      ! Local variables
   INTEGER(IntKi)                                  :: TmpErrStat
   CHARACTER(ErrMsgLen)                            :: TmpErrMsg

      ! Initialize ErrStat

   ErrStat     = ErrID_None
   ErrMsg      = ''
   TmpErrStat  = ErrID_None
   TmpErrMsg   = ''

      ! Write any closing information in the summary file
   IF ( SumFileUnit > 0_IntKi ) THEN
      WRITE (SumFileUnit,'(/,A/)', IOSTAT=TmpErrStat)  'This summary file was closed on '//CurDate()//' at '//CurTime()//'.'
      IF (TmpErrStat /= 0_IntKi)    CALL SetErrStat( ErrID_Fatal, 'Problem writing to the InflowWind summary file.', ErrStat, ErrMsg, '' )

         ! Close the file
      CLOSE( SumFileUnit, IOSTAT=TmpErrStat )
      IF (TmpErrStat /= 0_IntKi)    CALL SetErrStat( ErrID_Fatal, 'Problem closing the InflowWind summary file.', ErrStat, ErrMsg, '' )
   END IF

END SUBROUTINE InflowWind_CloseSumFile

!====================================================================================================

SUBROUTINE CalculateOutput( Time, InputData, p, x, xd, z, OtherStates, y, m, FillWrOut, ErrStat, ErrMsg )

      IMPLICIT                                                    NONE

      CHARACTER(*),              PARAMETER                     :: RoutineName="CalcOutput"

         ! Inputs / Outputs

      REAL(DbKi),                               INTENT(IN   )  :: Time           !< Current simulation time in seconds
      TYPE(InflowWind_InputType),               INTENT(IN   )  :: InputData      !< Inputs at Time
      TYPE(InflowWind_ParameterType),           INTENT(IN   )  :: p              !< Parameters
      TYPE(InflowWind_ContinuousStateType),     INTENT(IN   )  :: x              !< Continuous states at Time
      TYPE(InflowWind_DiscreteStateType),       INTENT(IN   )  :: xd             !< Discrete states at Time
      TYPE(InflowWind_ConstraintStateType),     INTENT(IN   )  :: z              !< Constraint states at Time
      TYPE(InflowWind_OtherStateType),          INTENT(IN   )  :: OtherStates    !< Other states at Time
      TYPE(InflowWind_OutputType),              INTENT(INOUT)  :: y              !< Outputs computed at Time (IN for mesh reasons and data allocation)
      TYPE(InflowWind_MiscVarType),             INTENT(INOUT)  :: m              !< misc/optimization variables
      LOGICAL,                                  INTENT(IN   )  :: FillWrOut      !< Flag to determine if we need to fill WriteOutput values

      INTEGER(IntKi),                           INTENT(  OUT)  :: ErrStat        !< Error status of the operation
      CHARACTER(*),                             INTENT(  OUT)  :: ErrMsg         !< Error message if ErrStat /= ErrID_None

         ! Local variables
      REAL(ReKi), ALLOCATABLE                                  :: PositionXYZprime(:,:)   !< PositionXYZ array in the prime (wind) coordinates
      REAL(ReKi)                                               :: DiskVel(3)     !< HACK for AD14: disk velocity output at Time

      INTEGER(IntKi)                                           :: I                   !< Generic counters

         ! Temporary variables for error handling
      INTEGER(IntKi)                                           :: TmpErrStat
      CHARACTER(ErrMsgLen)                                     :: TmpErrMsg            ! temporary error message

         ! Initialize ErrStat
      ErrStat  = ErrID_None
      ErrMsg   = ""

      !-----------------------------------------------------------------------
      !  Points coordinate transforms from to global to wind file coordinates
      !-----------------------------------------------------------------------

         !> Make a copy of the InputData%PositionXYZ coordinates with the applied rotation matrix...
         !! This copy is made because if we translate it to the prime coordinates, then back again, we
         !! may shift the points by some small amount of machine error, and we don't want to do that.
         !!
         !! Note that we allocate this at every call to CalcOutput.  The reason is that we may call CalcOutput
         !! multiple times in each timestep with different sized InputData%PositionXYZ arrays (if calling for LIDAR
         !! data etc).  We don't really want to have extra copies of OtherStates lying around in order to be able to
         !! do this.
      CALL AllocAry( PositionXYZprime, 3, SIZE(InputData%PositionXYZ,DIM=2), &
                  "Array for holding the XYZprime position data", TmpErrStat, TmpErrMsg )
      CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
      IF ( ErrStat >= AbortErrLev ) RETURN

         ! Apply the coordinate transformation to the PositionXYZ coordinates to get the PositionXYZprime coordinate list
         ! If the PropagationDir is zero, we don't need to apply this and will simply copy the data.  Repeat for the WindViXYZ.
      IF ( EqualRealNos (p%PropagationDir, 0.0_ReKi) ) THEN
         PositionXYZprime  =  InputData%PositionXYZ
      ELSE
         DO I  = 1,SIZE(InputData%PositionXYZ,DIM=2)
            PositionXYZprime(:,I)   =  MATMUL( p%RotToWind, InputData%PositionXYZ(:,I) )
         ENDDO
      ENDIF

      !---------------------------------
      !

         ! Compute the wind velocities by stepping through all the data points and calling the appropriate GetWindSpeed routine
      SELECT CASE ( p%WindType )

         CASE (Steady_WindNumber, Uniform_WindNumber)

               ! InputData only contains the Position array, so we can pass that directly.
            CALL  IfW_UniformWind_CalcOutput(  Time, PositionXYZprime, p%UniformWind, y%VelocityUVW, &
                                          DiskVel, m%UniformWind, TmpErrStat, TmpErrMsg)

            CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
            IF ( ErrStat >= AbortErrLev ) RETURN

               ! Call IfW_UniformWind_CalcOutput again in order to get the values needed for the OutList -- note that we do not report errors from this
            IF ( p%NWindVel >= 1_IntKi .AND. FillWrOut ) THEN
                  ! Move the arrays for the Velocity information
               CALL  IfW_UniformWind_CalcOutput(  Time, p%WindViXYZprime, p%UniformWind, m%WindViUVW, &
                                             DiskVel, m%UniformWind, TmpErrStat, TmpErrMsg)
            ENDIF

         CASE (TSFF_WindNumber)

               ! InputData only contains the Position array, so we can pass that directly.
            CALL  IfW_TSFFWind_CalcOutput(  Time, PositionXYZprime, p%TSFFWind, &
                                          y%VelocityUVW, DiskVel, m%TSFFWind, TmpErrStat, TmpErrMsg)

            CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
            IF ( ErrStat >= AbortErrLev ) RETURN

               ! Call IfW_TSFFWind_CalcOutput again in order to get the values needed for the OutList
            IF ( p%NWindVel >= 1_IntKi  .AND. FillWrOut ) THEN
                  ! Move the arrays for the Velocity information
               CALL  IfW_TSFFWind_CalcOutput(  Time, p%WindViXYZprime, p%TSFFWind, &
                                             m%WindViUVW, DiskVel, m%TSFFWind, TmpErrStat, TmpErrMsg)

                  ! Out of bounds errors will be ErrID_Severe, not ErrID_Fatal
               IF ( TmpErrStat >= ErrID_Fatal ) THEN
                  CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
                  RETURN
               ENDIF

            ENDIF

         CASE (BladedFF_WindNumber)

               ! InputData only contains the Position array, so we can pass that directly.
            CALL  IfW_BladedFFWind_CalcOutput(  Time, PositionXYZprime, p%BladedFFWind, &
                                          y%VelocityUVW, DiskVel, m%BladedFFWind, TmpErrStat, TmpErrMsg)

            CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
            IF ( ErrStat >= AbortErrLev ) RETURN

               ! Call IfW_BladedFFWind_CalcOutput again in order to get the values needed for the OutList
            IF ( p%NWindVel >= 1_IntKi  .AND. FillWrOut ) THEN
                  ! Move the arrays for the Velocity information
               CALL  IfW_BladedFFWind_CalcOutput(  Time, p%WindViXYZprime, p%BladedFFWind, &
                                             m%WindViUVW, DiskVel, m%BladedFFWind, TmpErrStat, TmpErrMsg)

                  ! Out of bounds errors will be ErrID_Severe, not ErrID_Fatal
               IF ( TmpErrStat >= ErrID_Fatal ) THEN
                  CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
                  RETURN
               ENDIF

            ENDIF

         CASE (User_WindNumber)

               ! InputData only contains the Position array, so we can pass that directly.
            CALL  IfW_UserWind_CalcOutput(  Time, PositionXYZprime, p%UserWind, &
                                          y%VelocityUVW, DiskVel, m%UserWind, TmpErrStat, TmpErrMsg)

            CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
            IF ( ErrStat >= AbortErrLev ) RETURN

               ! Call IfW_UserWind_CalcOutput again in order to get the values needed for the OutList
            IF ( p%NWindVel >= 1_IntKi  .AND. FillWrOut ) THEN
                  ! Move the arrays for the Velocity information
               CALL  IfW_UserWind_CalcOutput(  Time, p%WindViXYZprime, p%UserWind, &
                                             m%WindViUVW, DiskVel, m%UserWind, TmpErrStat, TmpErrMsg)

                  ! Out of bounds errors will be ErrID_Severe, not ErrID_Fatal
               IF ( TmpErrStat >= ErrID_Fatal ) THEN
                  CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
                  RETURN
               ENDIF

            ENDIF

         CASE ( HAWC_WindNumber )

               ! InputData only contains the Position array, so we can pass that directly.
            CALL  IfW_HAWCWind_CalcOutput(  Time, PositionXYZprime, p%HAWCWind, &
                                          y%VelocityUVW, DiskVel, m%HAWCWind, TmpErrStat, TmpErrMsg)

            CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
            IF ( ErrStat >= AbortErrLev ) RETURN

               ! Call IfW_TSFFWind_CalcOutput again in order to get the values needed for the OutList
            IF ( p%NWindVel >= 1_IntKi  .AND. FillWrOut ) THEN
               CALL  IfW_HAWCWind_CalcOutput(  Time, p%WindViXYZprime, p%HAWCWind, &
                                             m%WindViUVW, DiskVel, m%HAWCWind, TmpErrStat, TmpErrMsg)

                  ! Out of bounds errors will be ErrID_Severe, not ErrID_Fatal
               IF ( TmpErrStat >= ErrID_Fatal ) THEN
                  CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
                  RETURN
               ENDIF
            ENDIF

         CASE (FDext_WindNumber)

            CALL IfW_4Dext_CalcOutput(Time, PositionXYZprime, p%FDext, y%VelocityUVW,  m%FDext, TmpErrStat, TmpErrMsg)
            DiskVel = 0.0_ReKi ! this is only for AD14, which we frankly don't care about in 4Dext wind

            CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
            IF ( ErrStat >= AbortErrLev ) RETURN

               ! Call IfW_4Dext_CalcOutput again in order to get the values needed for the OutList
            IF ( p%NWindVel >= 1_IntKi  .AND. FillWrOut ) THEN
                  ! Move the arrays for the Velocity information
               CALL IfW_4Dext_CalcOutput(Time, p%WindViXYZprime, p%FDext, m%WindViUVW, m%FDext, TmpErrStat, TmpErrMsg)

                  ! Out of bounds errors will be ErrID_Severe, not ErrID_Fatal
               IF ( TmpErrStat >= ErrID_Fatal ) THEN
                  CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
                  RETURN
               ENDIF

            ENDIF

            ! If it isn't one of the above cases, we have a problem and won't be able to continue

         CASE DEFAULT

            CALL SetErrStat( ErrID_Fatal, ' Error: Undefined wind type '//TRIM(Num2LStr(p%WindType))//'. '// &
                      'Call WindInflow_Init() before calling this function.', ErrStat, ErrMsg, RoutineName )

            y%VelocityUVW(:,:) = 0.0
            RETURN

      END SELECT

            ! Add coherent turbulence to background wind

!!!         IF (p%CTTS_Flag) THEN
!!!
!!!            DO PointCounter = 1, SIZE(InputData%Position, 2)
!!!
!!!               TempWindSpeed = CTTS_GetWindSpeed(     Time, InputData%Position(:,PointCounter), ErrStat, ErrMsg )
!!!
!!!                  ! Error Handling -- move ErrMsg inside CTTS_GetWindSPeed and simplify
!!!               IF (ErrStat >= ErrID_Severe) THEN
!!!                  ErrMsg   = 'IfW_CalcOutput: Error in CTTS_GetWindSpeed for point number '//TRIM(Num2LStr(PointCounter))
!!!                  EXIT        ! Exit the loop
!!!               ENDIF
!!!
!!!               y%Velocity(:,PointCounter) = y%Velocity(:,PointCounter) + TempWindSpeed
!!!
!!!            ENDDO
!!!
!!!               ! If something went badly wrong, Return
!!!            IF (ErrStat >= ErrID_Severe ) RETURN
!!!
!!!         ENDIF
!!!
      !ENDIF

      !-----------------------------------------------------------------------
      !  Windspeed coordinate transforms from Wind file coordinates to global
      !-----------------------------------------------------------------------

         ! The VelocityUVW array data that has been returned from the sub-modules is in the wind file (X'Y'Z') coordinates at
         ! this point.  These must be rotated to the global XYZ coordinates.  So now we apply the coordinate transformation
         ! to the VelocityUVW(prime) coordinates (in wind X'Y'Z' coordinate frame) returned from the submodules to the XYZ
         ! coordinate frame, but only if PropagationDir is not zero.  This is only a rotation of the returned wind field, so
         ! UVW contains the direction components of the wind at XYZ after translation from the U'V'W' wind velocity components
         ! in the X'Y'Z' (wind file) coordinate frame.
      IF ( .NOT. EqualRealNos (p%PropagationDir, 0.0_ReKi) ) THEN
         DO I  = 1,SIZE(y%VelocityUVW,DIM=2)
            y%VelocityUVW(:,I)   =  MATMUL( p%RotFromWind, y%VelocityUVW(:,I) )
         ENDDO
      ENDIF

         ! We also need to rotate the reference frame for the WindViUVW array
      IF ( .NOT. EqualRealNos (p%PropagationDir, 0.0_ReKi)  .AND. FillWrOut ) THEN
         DO I  = 1,SIZE(m%WindViUVW,DIM=2)
            m%WindViUVW(:,I)   =  MATMUL( p%RotFromWind, m%WindViUVW(:,I) )
         ENDDO
      ENDIF

         ! DiskVel values over to the output and apply the coordinate transformation
      y%DiskVel   =  MATMUL( p%RotFromWind, DiskVel )

      ! Done with the prime coordinates for the XYZ position information that was passed in.
   IF (ALLOCATED(PositionXYZprime)) DEALLOCATE(PositionXYZprime)

END SUBROUTINE CalculateOutput

!====================================================================================================
!> this routine calculates the mean wind speed
SUBROUTINE InflowWind_GetMean( StartTime, EndTime, delta_time, InputPosition, MeanVelocity, &
                               p, x, xd, z, OtherState, m, ErrStat, ErrMsg )
!----------------------------------------------------------------------------------------------------
      ! Inputs / Outputs
   REAL(DbKi),                              INTENT(IN   )  :: StartTime         !< first time in range from which to calculate mean
   REAL(DbKi),                              INTENT(IN   )  :: EndTime           !< last time in range from which to calculate mean
   REAL(DbKi),                              INTENT(IN   )  :: delta_time        !< dt in time range from which to calculate mean
   REAL(ReKi),                              INTENT(IN   )  :: InputPosition(3)  !< X, Y, Z positions
   REAL(ReKi),                              INTENT(  OUT)  :: MeanVelocity(3)   !< at InputPosition

   TYPE( InflowWind_ParameterType ),        INTENT(IN   )  :: p                 !< Parameters
   TYPE( InflowWind_ContinuousStateType ),  INTENT(IN   )  :: x                 !< Continuous states at Time
   TYPE( InflowWind_DiscreteStateType ),    INTENT(IN   )  :: xd                !< Discrete states at Time
   TYPE( InflowWind_ConstraintStateType ),  INTENT(IN   )  :: z                 !< Constraint states at Time
   TYPE( InflowWind_OtherStateType ),       INTENT(IN   )  :: OtherState        !< Other/optimization states at Time
   TYPE(InflowWind_MiscVarType),            INTENT(INOUT)  :: m                 !< misc/optimization variables

   INTEGER( IntKi ),                        INTENT(  OUT)  :: ErrStat           !< Error status of the operation
   CHARACTER(*),                            INTENT(  OUT)  :: ErrMsg            !< Error message if ErrStat /= ErrID_None

      ! local variables
   REAL(DbKi)                                         :: Time
   REAL(DbKi)                                         :: SumVel(3)
   INTEGER(IntKi)                                     :: I
   INTEGER(IntKi)                                     :: Nt
   INTEGER(IntKi)                                     :: ErrStat2
   CHARACTER(ErrMsgLen)                               :: ErrMsg2

   CHARACTER(*), PARAMETER                            :: RoutineName = 'InflowWind_GetMean'

   TYPE(InflowWind_InputType)                         :: Input                ! position where wind speed should be returned
   TYPE(InflowWind_OutputType)                        :: Output               ! velocity at InputPosition

       ! main body:

   IF ( EqualRealNos(delta_time,0.0_DbKi) ) THEN
      ErrStat = ErrID_Fatal
      ErrMsg  = RoutineName//":delta time must be non-zero."
      RETURN
   END IF

   ErrStat   = ErrID_None
   ErrMsg    = ""

      ! allocate arrays to compute outputs
   CALL AllocAry(Input%PositionXYZ, 3,1, 'Input%PositionXYZ',ErrStat2, ErrMsg2)
      CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName )

   CALL AllocAry(Output%VelocityUVW, 3,1, 'Output%VelocityUVW',ErrStat2, ErrMsg2)
      CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName )

   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN
   END IF

   Nt = (EndTime - StartTime) / delta_time
   SumVel = 0.0
   Input%PositionXYZ(:,1) = InputPosition

   DO I=1,Nt

      Time = StartTime + (I-1)*delta_time

      CALL CalculateOutput( Time, Input, p, x, xd, z, OtherState, Output, m, .FALSE., ErrStat2, ErrMsg2 )
         CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName )

      IF ( ErrStat >= AbortErrLev ) THEN
         MeanVelocity = SumVel / REAL(I-1, ReKi)
         CALL Cleanup()
         RETURN
      ELSE
         SumVel = SumVel + Output%VelocityUVW(:,1)
      END IF

   END DO

   MeanVelocity = SumVel / REAL(Nt, ReKi)
   CALL Cleanup()
   RETURN

CONTAINS
!............................
   SUBROUTINE Cleanup()

      IF (ALLOCATED(Input%PositionXYZ)) DEALLOCATE(Input%PositionXYZ)
      IF (ALLOCATED(Output%VelocityUVW)) DEALLOCATE(Output%VelocityUVW)

   END SUBROUTINE Cleanup
!............................
END SUBROUTINE InflowWind_GetMean
!**********************************************************************************************************************************
END MODULE InflowWind_Subs