module icepack_mushy_physics

  use icepack_kinds
  use icepack_parameters, only: c0, c1, c2, c4, c1000
  use icepack_parameters, only: puny
  use icepack_parameters, only: rhow, rhoi, rhos, cp_ocn, cp_ice, Lfresh
  use icepack_parameters, only: ksno
  use icepack_warnings, only: warnstr, icepack_warnings_add
  use icepack_warnings, only: icepack_warnings_setabort, icepack_warnings_aborted

  implicit none

  private
  public :: &
       conductivity_mush_array, &
       conductivity_snow_array, &
       enthalpy_snow, &
       enthalpy_brine, &
       enthalpy_mush, &
       enthalpy_mush_liquid_fraction, &
       enthalpy_of_melting, &
       temperature_snow, &
       icepack_mushy_temperature_mush, &
       temperature_mush_liquid_fraction, &
       liquidus_brine_salinity_mush, &
       liquidus_temperature_mush, &
       icepack_mushy_liquid_fraction, &
       icepack_mushy_density_brine
      
  !-----------------------------------------------------------------
  ! Constants for Liquidus relation from Assur (1958)
  !-----------------------------------------------------------------

  ! liquidus relation - higher temperature region
  real(kind=dbl_kind), parameter :: &
       az1_liq = -18.48_dbl_kind, &
       bz1_liq =    0.0_dbl_kind

  ! liquidus relation - lower temperature region
  real(kind=dbl_kind), parameter :: &
       az2_liq = -10.3085_dbl_kind, &
       bz2_liq =     62.4_dbl_kind

  ! liquidus break
  real(kind=dbl_kind), parameter :: &
       Tb_liq = -7.6362968855167352_dbl_kind, & ! temperature of liquidus break
       Sb_liq =  123.66702800276086_dbl_kind    ! salinity of liquidus break

  ! basic liquidus relation constants
  real(kind=dbl_kind), parameter :: &
       az1p_liq = az1_liq / c1000, &
       bz1p_liq = bz1_liq / c1000, &
       az2p_liq = az2_liq / c1000, &
       bz2p_liq = bz2_liq / c1000
  
  !-----------------------------------------------------------------
  ! Other parameters
  !-----------------------------------------------------------------
  
  real(kind=dbl_kind), parameter :: &
       ki = 2.3_dbl_kind , & ! fresh ice conductivity (W m-1 K-1)
       kb = 0.5375_dbl_kind  ! brine conductivity (W m-1 K-1)

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

contains

!=======================================================================
! Physical Quantities
!=======================================================================

  subroutine conductivity_mush_array(nilyr, zqin, zSin, km)

    ! detemine the conductivity of the mush from enthalpy and salinity
    
    integer (kind=int_kind), intent(in) :: &
         nilyr   ! number of ice layers

    real(kind=dbl_kind), dimension(:), intent(in) :: &
         zqin, & ! ice layer enthalpy (J m-3) 
         zSin    ! ice layer bulk salinity (ppt)

    real(kind=dbl_kind), dimension(:), intent(out) :: &
         km      ! ice layer conductivity (W m-1 K-1)
    
    integer(kind=int_kind) :: &
         k       ! ice layer index

    real(kind=dbl_kind) :: Tmush
    
    character(len=*),parameter :: subname='(conductivity_mush_array)'

    do k = 1, nilyr
      
       Tmush = icepack_mushy_temperature_mush(zqin(k), zSin(k))
       
       km(k) = heat_conductivity(Tmush, zSin(k))
       
    enddo ! k

  end subroutine conductivity_mush_array

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

  function icepack_mushy_density_brine(Sbr) result(rho)
    
    ! density of brine from brine salinity

    real(kind=dbl_kind), intent(in) :: &
         Sbr ! brine salinity (ppt)

    real(kind=dbl_kind) :: &
         rho ! brine density (kg m-3)
    
    real(kind=dbl_kind), parameter :: &
         a = 1000.3_dbl_kind    , & ! zeroth empirical coefficient
         b = 0.78237_dbl_kind   , & ! linear empirical coefficient
         c = 2.8008e-4_dbl_kind     ! quadratic empirical coefficient
    
    character(len=*),parameter :: subname='(icepack_mushy_density_brine)'

    rho = a + b * Sbr + c * Sbr**2
                
  end function icepack_mushy_density_brine

!=======================================================================
! Snow
!=======================================================================

  subroutine conductivity_snow_array(ks)

    ! heat conductivity of the snow

    real(kind=dbl_kind), dimension(:), intent(out) :: &
         ks ! snow layer conductivity (W m-1 K-1)

    character(len=*),parameter :: subname='(conductivity_snow_array)'

    ks = ksno

  end subroutine conductivity_snow_array

!=======================================================================
  
  function enthalpy_snow(zTsn) result(zqsn)
    
    ! enthalpy of snow from snow temperature

    real(kind=dbl_kind), intent(in) :: &
         zTsn ! snow layer temperature (C)

    real(kind=dbl_kind) :: &
         zqsn ! snow layer enthalpy (J m-3) 
    
    character(len=*),parameter :: subname='(enthalpy_snow)'

    zqsn = -rhos * (-cp_ice * zTsn + Lfresh)
    
  end function enthalpy_snow

!=======================================================================
  
  function temperature_snow(zqsn) result(zTsn)
    
    ! temperature of snow from the snow enthalpy

    real(kind=dbl_kind), intent(in) :: &
         zqsn ! snow layer enthalpy (J m-3) 

    real(kind=dbl_kind) :: &
         zTsn, & ! snow layer temperature (C)
         A, B

    character(len=*),parameter :: subname='(temperature_snow)'

    A = c1 / (rhos * cp_ice)
    B = Lfresh / cp_ice
    zTsn = A * zqsn + B

  end function temperature_snow

!=======================================================================
! Mushy Layer Formulation - Assur (1958) liquidus
!=======================================================================

  function liquidus_brine_salinity_mush(zTin) result(Sbr)

    ! liquidus relation: equilibrium brine salinity as function of temperature
    ! based on empirical data from Assur (1958)

    real(kind=dbl_kind), intent(in) :: &
         zTin         ! ice layer temperature (C)

    real(kind=dbl_kind) :: &
         Sbr          ! ice brine salinity (ppt)

    real(kind=dbl_kind) :: &
         t_high   , & ! mask for high temperature liquidus region
         lsubzero     ! mask for sub-zero temperatures

    real(kind=dbl_kind) :: &
         J1_liq, K1_liq, L1_liq, & ! temperature to brine salinity
         J2_liq, K2_liq, L2_liq

    character(len=*),parameter :: subname='(liquidus_brine_salinty_mush)'

    ! temperature to brine salinity
    J1_liq = bz1_liq / az1_liq         
    K1_liq = c1 / c1000                
    L1_liq = (c1 + bz1p_liq) / az1_liq 
    J2_liq = bz2_liq  / az2_liq        
    K2_liq = c1 / c1000                
    L2_liq = (c1 + bz2p_liq) / az2_liq

    t_high   = merge(c1, c0, (zTin > Tb_liq))
    lsubzero = merge(c1, c0, (zTin <= c0))

    Sbr = ((zTin + J1_liq) / (K1_liq * zTin + L1_liq)) * t_high + &
          ((zTin + J2_liq) / (K2_liq * zTin + L2_liq)) * (c1 - t_high)

    Sbr = Sbr * lsubzero

  end function liquidus_brine_salinity_mush

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

  function liquidus_temperature_mush(Sbr) result(zTin)

    ! liquidus relation: equilibrium temperature as function of brine salinity
    ! based on empirical data from Assur (1958)

    real(kind=dbl_kind), intent(in) :: &
         Sbr    ! ice brine salinity (ppt)

    real(kind=dbl_kind) :: &
         zTin   ! ice layer temperature (C)

    real(kind=dbl_kind) :: &
         t_high ! mask for high temperature liquidus region

    real(kind=dbl_kind) :: &
       M1_liq, &! brine salinity to temperature
       N1_liq, &
       O1_liq, &
       M2_liq, &
       N2_liq, &
       O2_liq

    character(len=*),parameter :: subname='(liquidus_temperature_mush)'

    ! brine salinity to temperature
    M1_liq = az1_liq
    N1_liq = -az1p_liq
    O1_liq = -bz1_liq / az1_liq
    M2_liq = az2_liq
    N2_liq = -az2p_liq
    O2_liq = -bz2_liq / az2_liq

    t_high = merge(c1, c0, (Sbr <= Sb_liq))

    zTin = ((Sbr / (M1_liq + N1_liq * Sbr)) + O1_liq) * t_high + &
          ((Sbr / (M2_liq + N2_liq * Sbr)) + O2_liq) * (c1 - t_high)

  end function liquidus_temperature_mush

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

  function enthalpy_mush(zTin, zSin) result(zqin)

    ! enthalpy of mush from mush temperature and bulk salinity

    real(kind=dbl_kind), intent(in) :: &
         zTin, & ! ice layer temperature (C)
         zSin    ! ice layer bulk salinity (ppt)

    real(kind=dbl_kind) :: &
         zqin    ! ice layer enthalpy (J m-3) 

    real(kind=dbl_kind) :: &
         phi     ! ice liquid fraction 

    character(len=*),parameter :: subname='(enthalpy_mush)'

    phi = icepack_mushy_liquid_fraction(zTin, zSin)
    
    zqin = phi * (cp_ocn * rhow - cp_ice * rhoi) * zTin + &
           rhoi * cp_ice * zTin - (c1 - phi) * rhoi * Lfresh

  end function enthalpy_mush

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

  function enthalpy_mush_liquid_fraction(zTin, phi) result(zqin)

    ! enthalpy of mush from mush temperature and bulk salinity

    real(kind=dbl_kind), intent(in) :: &
         zTin, & ! ice layer temperature (C)
         phi     ! liquid fraction

    real(kind=dbl_kind) :: &
         zqin    ! ice layer enthalpy (J m-3) 

    character(len=*),parameter :: subname='(enthalpy_mush_liquid_fraction)'

    zqin = phi * (cp_ocn * rhow - cp_ice * rhoi) * zTin + &
           rhoi * cp_ice * zTin - (c1 - phi) * rhoi * Lfresh

  end function enthalpy_mush_liquid_fraction

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

  function enthalpy_of_melting(zSin) result(qm)

    ! enthalpy of melting of mush
    ! energy needed to fully melt mush (T < 0)

    real(kind=dbl_kind), intent(in) :: &
         zSin ! ice layer bulk salinity (ppt)

    real(kind=dbl_kind) :: &
         qm   ! melting ice enthalpy (J m-3)

    character(len=*),parameter :: subname='(enthalpy_of_melting)'

    qm = cp_ocn * rhow * liquidus_temperature_mush(zSin)

  end function enthalpy_of_melting

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

  function enthalpy_brine(zTin) result(qbr)

    ! enthalpy of brine (fully liquid)

    real(kind=dbl_kind), intent(in) :: &
         zTin ! ice layer temperature (C)

    real(kind=dbl_kind) :: &
         qbr  ! brine enthalpy (J m-3)

    character(len=*),parameter :: subname='(enthalpy_brine)'

    qbr = cp_ocn * rhow * zTin

  end function enthalpy_brine

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

  function icepack_mushy_temperature_mush(zqin, zSin) result(zTin)

    ! temperature of mush from mush enthalpy

    real(kind=dbl_kind), intent(in) :: &
         zqin   , & ! ice enthalpy (J m-3) 
         zSin       ! ice layer bulk salinity (ppt)

    real(kind=dbl_kind) :: &
         zTin       ! ice layer temperature (C)

    real(kind=dbl_kind) :: &
         qb     , & ! liquidus break enthalpy
         q0     , & ! fully melted enthalpy
         A      , & ! quadratic equation A parameter
         B      , & ! quadratic equation B parameter
         C      , & ! quadratic equation C parameter
         S_low  , & ! mask for salinity less than the liquidus break salinity
         t_high , & ! mask for high temperature liquidus region
         t_low  , & ! mask for low temperature liquidus region
         q_melt     ! mask for all mush melted

    ! quadratic constants - higher temperature region
    real(kind=dbl_kind) :: &
         AS1_liq, AC1_liq,          & ! quadratic constants - higher temperature region
         BS1_liq, BC1_liq, BQ1_liq, & ! "
         CS1_liq, CC1_liq, CQ1_liq, & ! "
         AS2_liq, AC2_liq,          & ! quadratic constants - lower temperature region
         BS2_liq, BC2_liq, BQ2_liq, & ! "
         CS2_liq, CC2_liq, CQ2_liq, & ! "
         D_liq, E_liq,              & ! break enthalpy constants
         F1_liq, G1_liq, H1_liq,    & ! just fully melted enthapy constants
         F2_liq, G2_liq, H2_liq,    & ! "
         I_liq                        ! warmer than fully melted constants

    character(len=*),parameter :: subname='(icepack_mushy_temperature_mush)'

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

  ! quadratic constants - higher temperature region
    AS1_liq = az1p_liq * (rhow * cp_ocn - rhoi * cp_ice)       
    AC1_liq = rhoi * cp_ice * az1_liq                           
    BS1_liq = (c1 + bz1p_liq) * (rhow * cp_ocn - rhoi * cp_ice)  &
            + rhoi * Lfresh * az1p_liq                         
    BQ1_liq = -az1_liq                                         
    BC1_liq = rhoi * cp_ice * bz1_liq - rhoi * Lfresh * az1_liq
    CS1_liq = rhoi * Lfresh * (c1 + bz1p_liq)                  
    CQ1_liq = -bz1_liq                                         
    CC1_liq = -rhoi * Lfresh * bz1_liq
  
  ! quadratic constants - lower temperature region
    AS2_liq = az2p_liq * (rhow * cp_ocn - rhoi * cp_ice)       
    AC2_liq = rhoi * cp_ice * az2_liq                          
    BS2_liq = (c1 + bz2p_liq) * (rhow * cp_ocn - rhoi * cp_ice)  &
            + rhoi * Lfresh * az2p_liq                         
    BQ2_liq = -az2_liq                                         
    BC2_liq = rhoi * cp_ice * bz2_liq - rhoi * Lfresh * az2_liq
    CS2_liq = rhoi * Lfresh * (c1 + bz2p_liq)                  
    CQ2_liq = -bz2_liq                                         
    CC2_liq = -rhoi * Lfresh * bz2_liq
  
  ! break enthalpy constants
    D_liq = ((c1 + az1p_liq*Tb_liq + bz1p_liq) &
          / (       az1_liq*Tb_liq + bz1_liq)) &
          * ((cp_ocn*rhow - cp_ice*rhoi)*Tb_liq + Lfresh*rhoi)
    E_liq = cp_ice*rhoi*Tb_liq - Lfresh*rhoi
  
  ! just fully melted enthapy constants
    F1_liq = (  -c1000 * cp_ocn * rhow) / az1_liq 
    G1_liq =    -c1000                            
    H1_liq = (-bz1_liq * cp_ocn * rhow) / az1_liq 
    F2_liq = (  -c1000 * cp_ocn * rhow) / az2_liq 
    G2_liq =    -c1000                            
    H2_liq = (-bz2_liq * cp_ocn * rhow) / az2_liq
  
  ! warmer than fully melted constants
    I_liq = c1 / (cp_ocn * rhow)

    ! just melted enthalpy
    S_low = merge(c1, c0, (zSin < Sb_liq))
    q0 = ((F1_liq * zSin) / (G1_liq + zSin) + H1_liq) * S_low + &
         ((F2_liq * zSin) / (G2_liq + zSin) + H2_liq) * (c1 - S_low)
    q_melt = merge(c1, c0, (zqin > q0))

    ! break enthalpy
    qb = D_liq * zSin + E_liq
    t_high = merge(c1, c0, (zqin > qb))
    t_low = c1 - t_high

    ! quadratic values
    A = (AS1_liq * zSin                 + AC1_liq) * t_high + &
        (AS2_liq * zSin                 + AC2_liq) * t_low

    B = (BS1_liq * zSin + BQ1_liq * zqin + BC1_liq) * t_high + &
        (BS2_liq * zSin + BQ2_liq * zqin + BC2_liq) * t_low

    C = (CS1_liq * zSin + CQ1_liq * zqin + CC1_liq) * t_high + &
        (CS2_liq * zSin + CQ2_liq * zqin + CC2_liq) * t_low

    zTin = (-B + sqrt(max(B**2 - c4 * A * C,puny))) / (c2 * A)

    ! change T if all melted
    zTin = q_melt * zqin * I_liq + (c1 - q_melt) * zTin

  end function icepack_mushy_temperature_mush

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

  function temperature_mush_liquid_fraction(zqin, phi) result(zTin)

    ! temperature of mush from mush enthalpy

    real(kind=dbl_kind), intent(in) :: &
         zqin   , & ! ice enthalpy (J m-3) 
         phi        ! liquid fraction

    real(kind=dbl_kind) :: &
         zTin       ! ice layer temperature (C)

    character(len=*),parameter :: subname='(temperature_mush_liquid_fraction)'

    zTin = (zqin + (c1 - phi) * rhoi * Lfresh) / &
          (phi * (cp_ocn * rhow - cp_ice * rhoi) + rhoi * cp_ice)

  end function temperature_mush_liquid_fraction

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

  function heat_conductivity(zTin, zSin) result(km)
    
    ! msuh heat conductivity from mush temperature and bulk salinity

    real(kind=dbl_kind), intent(in) :: &
         zTin              , & ! ice layer temperature (C)
         zSin                  ! ice layer bulk salinity (ppt)

    real(kind=dbl_kind) :: &
         km                    ! ice layer conductivity (W m-1 K-1)
    
    real(kind=dbl_kind) :: &
         phi                   ! liquid fraction

    character(len=*),parameter :: subname='(heat_conductivity)'

    phi = icepack_mushy_liquid_fraction(zTin, zSin)

    km = phi * (kb - ki) + ki

  end function heat_conductivity

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

  function icepack_mushy_liquid_fraction(zTin, zSin) result(phi)

    ! liquid fraction of mush from mush temperature and bulk salinity

    real(kind=dbl_kind), intent(in) :: &
         zTin, & ! ice layer temperature (C)
         zSin    ! ice layer bulk salinity (ppt)

    real(kind=dbl_kind) :: &
         phi , & ! liquid fraction
         Sbr     ! brine salinity (ppt)

    character(len=*),parameter :: subname='(icepack_mushy_liquid_fraction)'

    Sbr = max(liquidus_brine_salinity_mush(zTin),puny)
    phi = zSin / max(Sbr, zSin)

  end function icepack_mushy_liquid_fraction

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

end module icepack_mushy_physics