!------------------------------------------------------------------------------
! Module:     m_modmet_flxln2
! Authors:    Marte Voorneveld, RIVM
!             Anton Beljaars, KNMI (original FLXLN2 routine)
! Created:    June 11 2026
! Updated:    June 11 2026
! Description:
!   This module computes friction velocity and Obukhov length using
!   Monin-Obukhov similarity and an internal root-finding step.
!   References:
!   - Van Ulden and Holtslag (1985), JCAM 24, 1196-1207.
!   - Beljaars, Holtslag, and Van Westrhenen (1989), KNMI TR-112.
!------------------------------------------------------------------------------
module m_modmet_flxln2
   use modmet_constants, only: VONK, TR, RO, CP_AIR, LAMBDA

   use m_modmet_radiat, only: modmet_radiat, modmet_radiat_result
   use m_modmet_find_zero, only: modmet_find_zero, modmet_solver_result
   use m_modmet_tst, only: modmet_tst, modmet_tst_result
   use m_modmet_obuk, only: modmet_obuk
   use m_modmet_fpsim, only: modmet_fpsim

   implicit none (type, external)


   type :: modmet_flxln2_result
      real :: ust
      real :: ol
      real :: kin
      real :: tau
      real :: h
      real :: le
   end type modmet_flxln2_result



   private
   public :: modmet_flxln2, modmet_flxln2_result
contains
   ! ===========================================================
   ! Function: modmet_flxln2
   ! Description: Solves for friction velocity and returns associated
   !              Obukhov length and shortwave radiation used.
   ! input: u1             - wind speed at height zu1 [m/s] (0 when using roughness length as zu1)
   ! input: u2             - wind speed at height zu2 [m/s] (at height of wind measurements)
   ! input: zu1            - lower wind measurement height [m] (usually the roughness length)
   ! input: zu2            - upper wind measurement height [m] (usually z of wind measurements)
   ! input: T              - air temperature [C]
   ! input: cloud_fraction - cloud fraction [0..1]
   ! input: sinphi         - sine of solar elevation angle [-]
   ! input: kin            - incoming shortwave radiation [W/m^2]
   ! output: result%ust    - friction velocity [m/s]
   ! output: result%ol     - Obukhov length [m]
   ! output: result%kin    - incoming shortwave radiation used [W/m^2]
   ! ===========================================================
   !! Solves for friction velocity and returns associated Obukhov length and radiation terms.
   !!   Reference: Van Ulden and Holtslag (1985); Beljaars et al. (1989).
   pure function modmet_flxln2(u1, u2, zu1, zu2, T, cloud_fraction, sinphi, kin) result(result)
      real, intent(in) :: u1
      !! wind speed at height zu1 [m/s]
      real, intent(in) :: u2
      !! wind speed at height zu2 [m/s]
      real, intent(in) :: zu1
      !! lower wind measurement height [m]
      real, intent(in) :: zu2
      !! upper wind measurement height [m]
      real, intent(in) :: T
      !! air temperature [C]
      real, intent(in) :: cloud_fraction
      !! cloud fraction [0..1]
      real, intent(in) :: sinphi
      !! sine of solar elevation angle [-]
      real, intent(in) :: kin
      !! incoming shortwave radiation [W/m^2]

      real :: ust_guess, max_ust
      type(modmet_radiat_result) :: rad_result
      type(modmet_solver_result) :: solver_result

      type(modmet_flxln2_result) :: result
      if (cloud_fraction < 0.0) then
         result%ust = -9999.0
         result%ol = -9999.0
         result%kin = -9999.0
         return
      end if

      if (cloud_fraction > 1.0) then
         result%ust = -9999.0
         result%ol = -9999.0
         result%kin = -9999.0
         return
      end if

      rad_result = modmet_radiat(sinphi, cloud_fraction, kin)


      ust_guess = (u2-u1) * VONK / log(zu2/zu1) !initial guess for ust based on log profile
      max_ust = 5.0 * ust_guess ! Set a reasonable upper limit for ust
      ust_guess = 0.1*ust_guess ! Start with a smaller initial guess to ensure convergence


      solver_result = modmet_find_zero(momentum_f, ust_guess, max_ust, tol=1.0e-12, max_iter=100)

      result%ust = solver_result%root
      result%ol = solver_result%payload(1) ! Obukhov length from the momentum function
      result%kin = rad_result%kin

      result%tau = RO * result%ust**2
      result%h = -RO * CP_AIR * solver_result%payload(2) * result%ust
      result%le = -RO * LAMBDA * solver_result%payload(3) * result%ust

   contains
      ! ===========================================================
      ! Function: momentum_f
      ! Description: Residual function for momentum profile inversion.
      ! input: ust_g          - trial friction velocity [m/s]
      ! output: result%value  - residual for zero-finding
      ! output: result%payload(1) - Obukhov length [m]
      ! ===========================================================
      !! Residual function used by the internal root-finding step.
      !!   Reference: Monin-Obukhov momentum-profile inversion in FLXLN2.
      pure function momentum_f(ust_g) result(fx)
         real, intent(in) :: ust_g
         !! trial friction velocity [m/s]
         type(modmet_tst_result) :: tst_out
         real :: tstv, ol
         type(modmet_solver_result) :: fx

         tst_out = modmet_tst(ust_g, T, rad_result%qsti)

         tstv = tst_out%tst + 0.00061*TR* tst_out%qst

         ! updates the Obukhov length in the function
         ol = modmet_obuk(ust_g, tstv)

         fx%value = ust_g - (u2 - u1)*VONK/(log(zu2/zu1) - &
            modmet_fpsim(zu2/ol) + modmet_fpsim(zu1/ol))

         fx%payload(1) = ol
         fx%payload(2) = tst_out%tst
         fx%payload(3) = tst_out%qst

      end function momentum_f

   end function modmet_flxln2

! LCOV_EXCL_START
end module m_modmet_flxln2
! LCOV_EXCL_END
