!
! Helper module containing the slope variance routines for the
! CRTM implementation of FASTEM4
!
!
! CREATION HISTORY:
!       Written by:     Original FASTEM1/2/3 authors
!
!       Modified by:    Quanhua Liu, Quanhua.Liu@noaa.gov
!                       Stephen English, Stephen.English@metoffice.gov.uk
!                       July, 2009
!
!       Refactored by:  Paul van Delst, October 2010
!                       paul.vandelst@noaa.gov
!

MODULE Slope_Variance

  ! -----------------
  ! Environment setup
  ! -----------------
  ! Module use
  USE Type_Kinds     , ONLY: fp
  USE Hyperbolic_Step, ONLY: Step, Step_TL, Step_AD
  ! Disable implicit typing
  IMPLICIT NONE


  ! ------------
  ! Visibilities
  ! ------------
  PRIVATE
  ! Data types
  PUBLIC :: iVar_type
  ! Science routines
  PUBLIC :: Compute_Slope_Variance
  PUBLIC :: Compute_Slope_Variance_TL
  PUBLIC :: Compute_Slope_Variance_AD


  ! -----------------
  ! Module parameters
  ! -----------------
  CHARACTER(*), PARAMETER :: MODULE_VERSION_ID = &
  '$Id: Slope_Variance.f90 99117 2017-11-27 18:37:14Z tong.zhu@noaa.gov $'

  ! Literal constants
  REAL(fp), PARAMETER :: ZERO = 0.0_fp
  REAL(fp), PARAMETER :: ONE  = 1.0_fp
  REAL(fp), PARAMETER :: TWO  = 2.0_fp

  ! Wave slope variance parameters
  REAL(fp), PARAMETER :: VAR_COEFFS(2) = (/0.0030_fp, 0.00512_fp/)  ! Cox-Munk coeffs
  REAL(fp), PARAMETER :: F_COEFFS(3)   = (/1.0000_fp, 0.02000_fp, 0.30000_fp/)

  ! Scale factor for x-input to hyperbolic step function
  REAL(fp), PARAMETER :: XSCALE = 1000000.0_fp


  ! --------------------------------------
  ! Structure definition to hold internal
  ! variables across FWD, TL, and AD calls
  ! --------------------------------------
  TYPE :: iVar_type
    PRIVATE
    REAL(fp) :: vara  = ZERO
    REAL(fp) :: varb  = ZERO
    REAL(fp) :: Fterm = ZERO
    REAL(fp) :: x1 = ZERO, g1 = ZERO, var1 = ZERO
    REAL(fp) :: x2 = ZERO, g2 = ZERO
    REAL(fp) :: Variance = ZERO
  END TYPE iVar_type


CONTAINS


  ! =============================================
  ! Procedures to compute the wave slope variance
  ! =============================================
  ! Forward model
  SUBROUTINE Compute_Slope_Variance( &
    Frequency , &  ! Input
    Wind_Speed, &  ! Input
    iVar      , &  ! Internal variable output
    Variance    )  ! Output
    ! Arguments
    REAL(fp)       , INTENT(IN)  :: Frequency
    REAL(fp)       , INTENT(IN)  :: Wind_Speed
    TYPE(iVar_type), INTENT(OUT) :: iVar
    REAL(fp)       , INTENT(OUT) :: Variance
    ! Local variables
    REAL(fp) :: c
    ! Compute the frequency term
    iVar%Fterm   = (F_COEFFS(2)*Frequency + F_COEFFS(3))
    ! Compute the slope variances
    c = (VAR_COEFFS(1) + VAR_COEFFS(2)*Wind_Speed) * F_COEFFS(1)
    iVar%vara = c
    iVar%varb = c * iVar%Fterm
    ! Return required value
    ! ...First IF statement
    ! ...IF ( iVar%varb >= iVar%vara ) THEN
    iVar%x1 = XSCALE*(iVar%varb - iVar%vara)
    CALL Step(iVar%x1,iVar%g1)
    iVar%var1 = iVar%vara*iVar%g1 + iVar%varb*(ONE-iVar%g1)
    ! ...Second IF statement
    ! ...IF ( iVar%varb <= ZERO ) THEN
    iVar%x2 = XSCALE*iVar%varb
    CALL Step(iVar%x2,iVar%g2)
    Variance = iVar%var1*iVar%g2
  END SUBROUTINE Compute_Slope_Variance


  ! Tangent-linear model
  SUBROUTINE Compute_Slope_Variance_TL( &
    Wind_Speed_TL, &  ! Input
    iVar         , &  ! Internal variable input
    Variance_TL    )  ! Output
    ! Arguments
    REAL(fp)       , INTENT(IN)  :: Wind_Speed_TL
    TYPE(iVar_type), INTENT(IN)  :: iVar
    REAL(fp)       , INTENT(OUT) :: Variance_TL
    ! Local variables
    REAL(fp) :: c
    REAL(fp) :: vara_TL, varb_TL
    REAL(fp) :: x1_TL, g1_TL, var1_TL
    REAL(fp) :: x2_TL, g2_TL
    ! Compute slope variances
    c = VAR_COEFFS(2) * F_COEFFS(1)
    vara_TL = c * Wind_Speed_TL
    varb_TL = c * iVar%Fterm * Wind_Speed_TL
    ! Return required value
    ! ...First IF statement
    ! ...IF ( iVar%varb >= iVar%vara ) THEN
    x1_TL = XSCALE*(varb_TL - vara_TL)
    CALL Step_TL(iVar%x1,x1_TL,g1_TL)
    var1_TL = (iVar%vara - iVar%varb)*g1_TL + &
              iVar%g1*vara_TL + &
              (ONE-iVar%g1)*varb_TL
    ! ...Second IF statement
    ! ...IF ( iVar%varb <= ZERO ) THEN
    x2_TL = XSCALE*varb_TL
    CALL Step_TL(iVar%x2,x2_TL,g2_TL)
    Variance_TL = iVar%var1*g2_TL + iVar%g2*var1_TL
  END SUBROUTINE Compute_Slope_Variance_TL


  ! Adjoint model
  SUBROUTINE Compute_Slope_Variance_AD( &
    Variance_AD  , &  ! Input
    iVar         , &  ! Internal variable input
    Wind_Speed_AD  )  ! Output
    ! Arguments
    REAL(fp)       , INTENT(IN OUT) :: Variance_AD
    TYPE(iVar_type), INTENT(IN)     :: iVar
    REAL(fp)       , INTENT(IN OUT) :: Wind_Speed_AD
    ! Local variables
    REAL(fp) :: c
    REAL(fp) :: vara_AD, varb_AD
    REAL(fp) :: x1_AD, g1_AD, var1_AD
    REAL(fp) :: x2_AD, g2_AD
    ! Second IF statement
    ! ...IF ( iVar%varb <= ZERO ) THEN
    var1_AD = iVar%g2   * Variance_AD
    g2_AD   = iVar%var1 * Variance_AD
    Variance_AD = ZERO
    
    x2_AD = ZERO
    CALL Step_AD(iVar%x2, g2_AD, x2_AD)
    varb_AD = XSCALE * x2_AD

    ! First IF statement
    ! ...IF ( iVar%varb >= iVar%vara ) THEN
    varb_AD = varb_AD + (ONE-iVar%g1) * var1_AD
    vara_AD =                iVar%g1  * var1_AD
    g1_AD   = (iVar%vara - iVar%varb) * var1_AD
    
    x1_AD = ZERO
    CALL Step_AD(iVar%x1, g1_AD, x1_AD)
    
    vara_AD = vara_AD - XSCALE * x1_AD
    varb_AD = varb_AD + XSCALE * x1_AD
    
    ! Compute the adjoint of the slope variances
    c = VAR_COEFFS(2) * F_COEFFS(1)
    Wind_Speed_AD = Wind_Speed_AD + c * iVar%Fterm * varb_AD
    Wind_Speed_AD = Wind_Speed_AD + c * vara_AD
  END SUBROUTINE Compute_Slope_Variance_AD

END MODULE Slope_Variance
