!|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||


 module state_mod 12,11

!BOP
! !MODULE: state_mod
!
! !DESCRIPTION:
!  This module contains routines necessary for computing the density 
!  from model temperature and salinity using an equation of state.
!
!  The module supports four forms of EOS:
!  \begin{enumerate}
!     \item The UNESCO equation of state computed using the 
!           potential-temperature-based bulk modulus from Jackett and 
!           McDougall, JTECH, Vol.12, pp 381-389, April, 1995.
!     \item The faster and more accurate alternative to the UNESCO eos
!           of McDougall, Wright, Jackett and Feistel (hereafter 
!           MWJF, 2001 submission to JTECH).
!     \item a polynomial fit to the full UNESCO EOS
!     \item a simple linear EOS based on constant expansion coeffs
!  \end{enumerate}
!
! !REVISION HISTORY:
!  SVN:$Id: state_mod.F90 21356 2010-03-01 22:12:38Z njn01 $

! !USES:

   use kinds_mod
   use blocks
   use distribution
   use domain
   use constants
   use grid
   use io
   use broadcast
   use time_management
   use exit_mod

#ifdef CCSMCOUPLED
   !*** ccsm
   use shr_vmath_mod
#endif

   implicit none
   private
   save

! !PUBLIC MEMBER FUNCTIONS:

   public :: state,        &
             init_state,   &
             ref_pressure

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  valid ranges and pressure as function of depth
!
!-----------------------------------------------------------------------

   real (r8), dimension(km) :: & 
      tmin, tmax,        &! valid temperature range for level k
      smin, smax,        &! valid salinity    range for level k
      pressz              ! ref pressure (bars) at each level

!-----------------------------------------------------------------------
!
!  choices for eos type and valid range checks
!
!-----------------------------------------------------------------------

   integer (int_kind), parameter :: &
      state_type_jmcd       = 1,    &! integer ids for state choice
      state_type_mwjf       = 2,    &
      state_type_polynomial = 3,    &
      state_type_linear     = 4

   integer (int_kind) ::    &
      state_itype           ! input state type chosen

   integer (int_kind) ::    &
      state_range_iopt,     &! option for checking valid T,S range
      state_range_freq       ! freq (in steps) for checking T,S range

   integer (int_kind), parameter :: &
      state_range_ignore  = 1, &! do not check T,S range
      state_range_check   = 2, &! check T,S range and report invalid
      state_range_enforce = 3   ! force polynomial eval within range

!-----------------------------------------------------------------------
!
!  UNESCO EOS constants and JMcD bulk modulus constants
!
!-----------------------------------------------------------------------

   !*** for density of fresh water (standard UNESCO)

   real (r8), parameter ::              &
      unt0 =   999.842594_r8,           &
      unt1 =  6.793952e-2_r8,           &
      unt2 = -9.095290e-3_r8,           &
      unt3 =  1.001685e-4_r8,           &
      unt4 = -1.120083e-6_r8,           &
      unt5 =  6.536332e-9_r8

   !*** for dependence of surface density on salinity (UNESCO)

   real (r8), parameter ::              &
      uns1t0 =  0.824493_r8 ,           &
      uns1t1 = -4.0899e-3_r8,           &
      uns1t2 =  7.6438e-5_r8,           &
      uns1t3 = -8.2467e-7_r8,           &
      uns1t4 =  5.3875e-9_r8,           &
      unsqt0 = -5.72466e-3_r8,          &
      unsqt1 =  1.0227e-4_r8,           &
      unsqt2 = -1.6546e-6_r8,           &
      uns2t0 =  4.8314e-4_r8

   !*** from Table A1 of Jackett and McDougall

   real (r8), parameter ::              &
      bup0s0t0 =  1.965933e+4_r8,       &
      bup0s0t1 =  1.444304e+2_r8,       &
      bup0s0t2 = -1.706103_r8   ,       &
      bup0s0t3 =  9.648704e-3_r8,       &
      bup0s0t4 = -4.190253e-5_r8

   real (r8), parameter ::              &
      bup0s1t0 =  5.284855e+1_r8,       &
      bup0s1t1 = -3.101089e-1_r8,       &
      bup0s1t2 =  6.283263e-3_r8,       &
      bup0s1t3 = -5.084188e-5_r8

   real (r8), parameter ::              &
      bup0sqt0 =  3.886640e-1_r8,       &
      bup0sqt1 =  9.085835e-3_r8,       &
      bup0sqt2 = -4.619924e-4_r8

   real (r8), parameter ::              &
      bup1s0t0 =  3.186519_r8   ,       &
      bup1s0t1 =  2.212276e-2_r8,       &
      bup1s0t2 = -2.984642e-4_r8,       &
      bup1s0t3 =  1.956415e-6_r8 

   real (r8), parameter ::              &
      bup1s1t0 =  6.704388e-3_r8,       &
      bup1s1t1 = -1.847318e-4_r8,       &
      bup1s1t2 =  2.059331e-7_r8,       &
      bup1sqt0 =  1.480266e-4_r8 

   real (r8), parameter ::              &
      bup2s0t0 =  2.102898e-4_r8,       &
      bup2s0t1 = -1.202016e-5_r8,       &
      bup2s0t2 =  1.394680e-7_r8,       &
      bup2s1t0 = -2.040237e-6_r8,       &
      bup2s1t1 =  6.128773e-8_r8,       &
      bup2s1t2 =  6.207323e-10_r8

!-----------------------------------------------------------------------
!
!  MWJF EOS coefficients
!
!-----------------------------------------------------------------------

   !*** these constants will be used to construct the numerator
   !*** factor unit change (kg/m^3 -> g/cm^3) into numerator terms

   real (r8), parameter ::                     &
      mwjfnp0s0t0 =   9.99843699e+2_r8 * p001, &
      mwjfnp0s0t1 =   7.35212840e+0_r8 * p001, &
      mwjfnp0s0t2 =  -5.45928211e-2_r8 * p001, &
      mwjfnp0s0t3 =   3.98476704e-4_r8 * p001, &
      mwjfnp0s1t0 =   2.96938239e+0_r8 * p001, &
      mwjfnp0s1t1 =  -7.23268813e-3_r8 * p001, &
      mwjfnp0s2t0 =   2.12382341e-3_r8 * p001, &
      mwjfnp1s0t0 =   1.04004591e-2_r8 * p001, &
      mwjfnp1s0t2 =   1.03970529e-7_r8 * p001, &
      mwjfnp1s1t0 =   5.18761880e-6_r8 * p001, &
      mwjfnp2s0t0 =  -3.24041825e-8_r8 * p001, &
      mwjfnp2s0t2 =  -1.23869360e-11_r8* p001

   !*** these constants will be used to construct the denominator

   real (kind=r8), parameter ::          &
      mwjfdp0s0t0 =   1.0e+0_r8,         &
      mwjfdp0s0t1 =   7.28606739e-3_r8,  &
      mwjfdp0s0t2 =  -4.60835542e-5_r8,  &
      mwjfdp0s0t3 =   3.68390573e-7_r8,  &
      mwjfdp0s0t4 =   1.80809186e-10_r8, &
      mwjfdp0s1t0 =   2.14691708e-3_r8,  &
      mwjfdp0s1t1 =  -9.27062484e-6_r8,  &
      mwjfdp0s1t3 =  -1.78343643e-10_r8, &
      mwjfdp0sqt0 =   4.76534122e-6_r8,  &
      mwjfdp0sqt2 =   1.63410736e-9_r8,  &
      mwjfdp1s0t0 =   5.30848875e-6_r8,  &
      mwjfdp2s0t3 =  -3.03175128e-16_r8, &
      mwjfdp3s0t1 =  -1.27934137e-17_r8

!-----------------------------------------------------------------------
!
!  coeffs and reference values for polynomial eos
!
!-----------------------------------------------------------------------

   real (r8), dimension(:), allocatable :: &
      to,                &! reference temperature for level k
      so,                &! reference salinity    for level k
      sigo                ! reference density     for level k

   real (r8), dimension(:,:), allocatable :: & 
      state_coeffs        ! coefficients for polynomial eos

!-----------------------------------------------------------------------
!
!  parameters for linear eos
!
!-----------------------------------------------------------------------

   real (r8), parameter ::        & 
      T_leos_ref = 19.0_r8,       &! reference T for linear eos (deg C)
      S_leos_ref = 0.035_r8,      &! reference S for linear eos (msu)
      rho_leos_ref = 1.025022_r8, &! ref dens (g/cm3) at ref T,S and 0 bar
      alf = 2.55e-4_r8,           &! expansion coeff -(drho/dT) (gr/cm^3/K)
      bet = 7.64e-1_r8             ! expansion coeff (drho/dS) (gr/cm^3/msu)

!EOC
!***********************************************************************

 contains

!***********************************************************************
!BOP
! !IROUTINE: state
! !INTERFACE:


 subroutine state(k, kk, TEMPK, SALTK, this_block, & 29,2
                         RHOOUT, RHOFULL, DRHODT, DRHODS)

! !DESCRIPTION:
!  Returns the density of water at level k from equation of state
!  $\rho = \rho(d,\theta,S)$ where $d$ is depth, $\theta$ is
!  potential temperature, and $S$ is salinity. the density can be
!  returned as a perturbation (RHOOUT) or as the full density
!  (RHOFULL). Note that only the polynomial EOS choice will return
!  a perturbation density; in other cases the full density is returned
!  regardless of which argument is requested.
!
!  This routine also computes derivatives of density with respect
!  to temperature and salinity at level k from equation of state
!  if requested (ie the optional arguments are present).
!
!  If $k = kk$ are equal the density for level k is returned.
!  If $k \neq kk$ the density returned is that for a parcel
!  adiabatically displaced from level k to level kk.
!
! !REVISION HISTORY:
!  same as module

! !INPUT PARAMETERS:

   integer (int_kind), intent(in) :: &
      k,                    &! depth level index
      kk                     ! level to which water is adiabatically 
                            ! displaced

   real (r8), dimension(nx_block,ny_block), intent(in) :: & 
      TEMPK,             &! temperature at level k
      SALTK               ! salinity    at level k

   type (block), intent(in) :: &
      this_block          ! block information for current block

! !OUTPUT PARAMETERS:

   real (r8), dimension(nx_block,ny_block), optional, intent(out) :: & 
      RHOOUT,  &! perturbation density of water
      RHOFULL, &! full density of water
      DRHODT,  &! derivative of density with respect to temperature
      DRHODS    ! derivative of density with respect to salinity

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  local variables:
!
!-----------------------------------------------------------------------

   integer (int_kind) :: &
      ib,ie,jb,je,       &! extent of physical domain
      bid,               &! local block index
      out_of_range        ! counter for out-of-range T,S values

   real (r8), dimension(nx_block,ny_block) :: &
      TQ,SQ,             &! adjusted T,S
      BULK_MOD,          &! Bulk modulus
      RHO_S,             &! density at the surface
      DRDT0,             &! d(density)/d(temperature), for surface
      DRDS0,             &! d(density)/d(salinity   ), for surface
      DKDT,              &! d(bulk modulus)/d(pot. temp.)
      DKDS,              &! d(bulk modulus)/d(salinity  )
      SQR,DENOMK,        &! work arrays
      WORK1, WORK2, WORK3, WORK4, T2

   real (r8) :: p, p2 ! temporary pressure scalars

   !*** MWJF numerator coefficients including pressure

   real (r8) ::                                                        &
      mwjfnums0t0, mwjfnums0t1, mwjfnums0t2, mwjfnums0t3,              &
      mwjfnums1t0, mwjfnums1t1, mwjfnums2t0,                           &
      mwjfdens0t0, mwjfdens0t1, mwjfdens0t2, mwjfdens0t3, mwjfdens0t4, &
      mwjfdens1t0, mwjfdens1t1, mwjfdens1t3,                           &
      mwjfdensqt0, mwjfdensqt2

!-----------------------------------------------------------------------
!
!  first check for valid range if requested
!
!-----------------------------------------------------------------------

   bid = this_block%local_id

   select case (state_range_iopt)
   case (state_range_ignore)

      !*** prevent problems with garbage on land points or ghost cells

      TQ = min(TEMPK, c1000)
      TQ = max(TQ,   -c1000)
      SQ = min(SALTK, c1000)
      SQ = max(SALTK, c0)

   case (state_range_check)

      call exit_POP(sigAbort,  &
          '(state) ERROR unsupported option -- must define time flag and use check_time_flag')
!***  if (time_to_do(freq_opt_nstep, state_range_freq)) then

         ib = this_block%ib
         ie = this_block%ie
         jb = this_block%jb
         je = this_block%je

         out_of_range = count((TEMPK(ib:ie,jb:je) < tmin(kk) .or.   &
                               TEMPK(ib:ie,jb:je) > tmax(kk)) .and. &
                               k <= KMT(ib:ie,jb:je,bid))

         if (out_of_range /= 0)                                      &
            write(stdout,'(a9,i6,a44,i3)') 'WARNING: ',out_of_range, &
                  'points outside of valid temp range at level ',kk

         out_of_range = count((SALTK(ib:ie,jb:je) < smin(kk) .or.   &
                               SALTK(ib:ie,jb:je) > smax(kk)) .and. &
                               k <= KMT(ib:ie,jb:je,bid))

         if (out_of_range /= 0)                                      &
            write(stdout,'(a9,i6,a44,i3)') 'WARNING: ',out_of_range, &
                  'points outside of valid salt range at level ',kk

!***  endif

      TQ = TEMPK
      SQ = SALTK

   case (state_range_enforce)

      TQ = min(TEMPK,tmax(kk))
      TQ = max(TQ,tmin(kk))

      SQ = min(SALTK,smax(kk))
      SQ = max(SQ,smin(kk))

   end select

!-----------------------------------------------------------------------
!
!  now compute density or expansion coefficients
!
!-----------------------------------------------------------------------

   select case (state_itype)

!-----------------------------------------------------------------------
!
!  McDougall, Wright, Jackett, and Feistel EOS
!  test value : rho = 1.033213242 for
!  S = 35.0 PSU, theta = 20.0, pressz = 200.0
!
!-----------------------------------------------------------------------

   case (state_type_mwjf)

      p   = c10*pressz(kk)

      SQ  = c1000*SQ
#ifdef CCSMCOUPLED
      call shr_vmath_sqrt(SQ, SQR, nx_block*ny_block)
#else
      SQR = sqrt(SQ)
#endif

      !***
      !*** first calculate numerator of MWJF density [P_1(S,T,p)]
      !***

      mwjfnums0t0 = mwjfnp0s0t0 + p*(mwjfnp1s0t0 + p*mwjfnp2s0t0)
      mwjfnums0t1 = mwjfnp0s0t1 
      mwjfnums0t2 = mwjfnp0s0t2 + p*(mwjfnp1s0t2 + p*mwjfnp2s0t2)
      mwjfnums0t3 = mwjfnp0s0t3
      mwjfnums1t0 = mwjfnp0s1t0 + p*mwjfnp1s1t0
      mwjfnums1t1 = mwjfnp0s1t1
      mwjfnums2t0 = mwjfnp0s2t0

      WORK1 = mwjfnums0t0 + TQ * (mwjfnums0t1 + TQ * (mwjfnums0t2 + &
              mwjfnums0t3 * TQ)) + SQ * (mwjfnums1t0 +              &
              mwjfnums1t1 * TQ + mwjfnums2t0 * SQ)

      !***
      !*** now calculate denominator of MWJF density [P_2(S,T,p)]
      !***

      mwjfdens0t0 = mwjfdp0s0t0 + p*mwjfdp1s0t0
      mwjfdens0t1 = mwjfdp0s0t1 + p**3 * mwjfdp3s0t1
      mwjfdens0t2 = mwjfdp0s0t2
      mwjfdens0t3 = mwjfdp0s0t3 + p**2 * mwjfdp2s0t3
      mwjfdens0t4 = mwjfdp0s0t4
      mwjfdens1t0 = mwjfdp0s1t0
      mwjfdens1t1 = mwjfdp0s1t1
      mwjfdens1t3 = mwjfdp0s1t3
      mwjfdensqt0 = mwjfdp0sqt0
      mwjfdensqt2 = mwjfdp0sqt2

      WORK2 = mwjfdens0t0 + TQ * (mwjfdens0t1 + TQ * (mwjfdens0t2 +    &
           TQ * (mwjfdens0t3 + mwjfdens0t4 * TQ))) +                   &
           SQ * (mwjfdens1t0 + TQ * (mwjfdens1t1 + TQ*TQ*mwjfdens1t3)+ &
           SQR * (mwjfdensqt0 + TQ*TQ*mwjfdensqt2))

      DENOMK = c1/WORK2

      if (present(RHOOUT)) then
         RHOOUT  = WORK1*DENOMK
      endif

      if (present(RHOFULL)) then
         RHOFULL = WORK1*DENOMK
      endif

      if (present(DRHODT)) then
         WORK3 = &! dP_1/dT
                 mwjfnums0t1 + TQ * (c2*mwjfnums0t2 +    &
                 c3*mwjfnums0t3 * TQ) + mwjfnums1t1 * SQ

         WORK4 = &! dP_2/dT
                 mwjfdens0t1 + SQ * mwjfdens1t1 +               &
                 TQ * (c2*(mwjfdens0t2 + SQ*SQR*mwjfdensqt2) +  &
                 TQ * (c3*(mwjfdens0t3 + SQ * mwjfdens1t3) +    &
                 TQ *  c4*mwjfdens0t4))

         DRHODT = (WORK3 - WORK1*DENOMK*WORK4)*DENOMK
      endif

      if (present(DRHODS)) then
         WORK3 = &! dP_1/dS
                 mwjfnums1t0 + mwjfnums1t1 * TQ + c2*mwjfnums2t0 * SQ

         WORK4 = mwjfdens1t0 +   &! dP_2/dS
                 TQ * (mwjfdens1t1 + TQ*TQ*mwjfdens1t3) +   &
                 c1p5*SQR*(mwjfdensqt0 + TQ*TQ*mwjfdensqt2)

         DRHODS = (WORK3 - WORK1*DENOMK*WORK4)*DENOMK * c1000
      endif

!-----------------------------------------------------------------------
!
!  Jackett and McDougall EOS
!
!-----------------------------------------------------------------------

   case (state_type_jmcd)

      p   = pressz(kk)
      p2  = p*p

      SQ  = c1000*SQ
      SQR = sqrt(SQ)
      T2  = TQ*TQ


      !***
      !*** first calculate surface (p=0) values from UNESCO eqns.
      !***

      WORK1 = uns1t0 + uns1t1*TQ + & 
             (uns1t2 + uns1t3*TQ + uns1t4*T2)*T2
      WORK2 = SQR*(unsqt0 + unsqt1*TQ + unsqt2*T2)

      RHO_S = unt1*TQ + (unt2 + unt3*TQ + (unt4 + unt5*TQ)*T2)*T2 &
                      + (uns2t0*SQ + WORK1 + WORK2)*SQ


      !***
      !*** now calculate bulk modulus at pressure p from 
      !*** Jackett and McDougall formula
      !***

      WORK3 = bup0s1t0 + bup0s1t1*TQ +                    &
             (bup0s1t2 + bup0s1t3*TQ)*T2 +                &
              p *(bup1s1t0 + bup1s1t1*TQ + bup1s1t2*T2) + &
              p2*(bup2s1t0 + bup2s1t1*TQ + bup2s1t2*T2)
      WORK4 = SQR*(bup0sqt0 + bup0sqt1*TQ + bup0sqt2*T2 + &
                   bup1sqt0*p)

      BULK_MOD  = bup0s0t0 + bup0s0t1*TQ +                    &
                  (bup0s0t2 + bup0s0t3*TQ + bup0s0t4*T2)*T2 + &
                  p *(bup1s0t0 + bup1s0t1*TQ +                &
                     (bup1s0t2 + bup1s0t3*TQ)*T2) +           &
                  p2*(bup2s0t0 + bup2s0t1*TQ + bup2s0t2*T2) + &
                  SQ*(WORK3 + WORK4)

      DENOMK = c1/(BULK_MOD - p)

      !***
      !*** now calculate required fields
      !***

      if (present(RHOOUT)) then
         RHOOUT  = merge(((unt0 + RHO_S)*BULK_MOD*DENOMK)*p001, &
                         c0, KMT(:,:,bid) >= k)
      endif

      if (present(RHOFULL)) then
         RHOFULL = merge(((unt0 + RHO_S)*BULK_MOD*DENOMK)*p001, &
                         c0, KMT(:,:,bid) >= k)
      endif

      if (present(DRHODT)) then
         DRDT0 =  unt1 + c2*unt2*TQ +                      &
                  (c3*unt3 + c4*unt4*TQ + c5*unt5*T2)*T2 + &
                  (uns1t1 + c2*uns1t2*TQ +                 &
                   (c3*uns1t3 + c4*uns1t4*TQ)*T2 +         &
                   (unsqt1 + c2*unsqt2*TQ)*SQR )*SQ

         DKDT  = bup0s0t1 + c2*bup0s0t2*TQ +                       &
                 (c3*bup0s0t3 + c4*bup0s0t4*TQ)*T2 +               &
                 p *(bup1s0t1 + c2*bup1s0t2*TQ + c3*bup1s0t3*T2) + &
                 p2*(bup2s0t1 + c2*bup2s0t2*TQ) +                  &
                 SQ*(bup0s1t1 + c2*bup0s1t2*TQ + c3*bup0s1t3*T2 +  &
                     p  *(bup1s1t1 + c2*bup1s1t2*TQ) +             &
                     p2 *(bup2s1t1 + c2*bup2s1t2*TQ) +             &
                     SQR*(bup0sqt1 + c2*bup0sqt2*TQ))

         DRHODT = merge ((DENOMK*(DRDT0*BULK_MOD -                    &
                           pressz(kk)*(unt0+RHO_S)*DKDT*DENOMK))*p001, &
                         c0, KMT(:,:,bid) >= k)
      endif

      if (present(DRHODS)) then
         DRDS0  = c2*uns2t0*SQ + WORK1 + c1p5*WORK2
         DKDS = WORK3 + c1p5*WORK4

         DRHODS = merge (DENOMK*(DRDS0*BULK_MOD -                    &
                          pressz(kk)*(unt0+RHO_S)*DKDS*DENOMK),       &
                         c0, KMT(:,:,bid) >= k)

      endif
 
!-----------------------------------------------------------------------
!
!  polynomial EOS
!
!-----------------------------------------------------------------------

   case (state_type_polynomial)

      TQ = TQ - to(kk)
      SQ = SQ - so(kk) - 0.035_r8

      if (present(RHOOUT)) then

         !*** density as perturbation from a k-level reference

         RHOOUT = merge((state_coeffs(1,kk) +            &
                        (state_coeffs(4,kk) +            &
                         state_coeffs(7,kk)*SQ)*SQ +     &
                        (state_coeffs(3,kk) +            &
                         state_coeffs(8,kk)*SQ +         &
                         state_coeffs(6,kk)*TQ)*TQ)*TQ + &
                        (state_coeffs(2,kk) +            &
                        (state_coeffs(5,kk) +            &
                         state_coeffs(9,kk)*SQ)*SQ)*SQ   &
                     ,c0, KMT(:,:,bid) >= k)
      endif

      if (present(RHOFULL)) then

         !*** full density (adding the reference density)

         RHOFULL=merge(((state_coeffs(1,kk) +             &
                        (state_coeffs(4,kk) +             &
                         state_coeffs(7,kk)*SQ)*SQ +      &
                        (state_coeffs(3,kk) +             &
                         state_coeffs(8,kk)*SQ +          &
                         state_coeffs(6,kk)*TQ)*TQ)*TQ +  &
                        (state_coeffs(2,kk) +             &
                        (state_coeffs(5,kk) +             &
                         state_coeffs(9,kk)*SQ)*SQ)*SQ) + &
                         sigo(kk)*p001 + c1               &
                     ,c1, KMT(:,:,bid) >= k)
      endif

      if (present(DRHODT)) then
         DRHODT = merge (state_coeffs(1,kk) +        &
                        (state_coeffs(4,kk) +        &
                         state_coeffs(7,kk)*SQ)*SQ + &
                     (c2*state_coeffs(3,kk) +        &
                      c2*state_coeffs(8,kk)*SQ +     &
                      c3*state_coeffs(6,kk)*TQ)*TQ,  &
                  c0, KMT(:,:,bid) >= k)
      endif

      if (present(DRHODS)) then
         DRHODS = merge((state_coeffs(4,kk) +        &
                      c2*state_coeffs(7,kk)*SQ +     &
                         state_coeffs(8,kk)*TQ)*TQ + &
                         state_coeffs(2,kk) +        &
                     (c2*state_coeffs(5,kk) +        &
                      c3*state_coeffs(9,kk)*SQ)*SQ,  &
                  c0, KMT(:,:,bid) >= k)
      endif
 
!-----------------------------------------------------------------------
!
!  linear EOS
!
!-----------------------------------------------------------------------

   case (state_type_linear)

      if (present(RHOOUT )) RHOOUT  = bet*(SALTK - S_leos_ref) - &
                                      alf*(TEMPK - T_leos_ref)
      if (present(RHOFULL)) RHOFULL = rho_leos_ref + & 
                                      bet*(SALTK - S_leos_ref) - &
                                      alf*(TEMPK - T_leos_ref)
      if (present(DRHODT )) DRHODT  = -alf
      if (present(DRHODS )) DRHODS  =  bet

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

   end select

!-----------------------------------------------------------------------
!EOC

 end subroutine state

!***********************************************************************
!BOP
! !IROUTINE: ref_pressure
! !INTERFACE:


 function ref_pressure(k) 4

! !DESCRIPTION:
!  This function returns a reference pressure at level k.
!
! !REVISION HISTORY:
!  same as module

! !INPUT PARAMETERS:

   integer (int_kind), intent(in) :: &
      k                  ! vertical level index

! !OUTPUT PARAMETERS:

   real (r8) ::         &
      ref_pressure       ! reference pressure at level k

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  return pre-computed reference pressure at level k
!
!-----------------------------------------------------------------------

    ref_pressure = pressz(k)

!-----------------------------------------------------------------------
!EOC

 end function ref_pressure

!***********************************************************************
!BOP
! !IROUTINE: init_state
! !INTERFACE:


 subroutine init_state 2,35

! !DESCRIPTION:
!  Initializes eos choice and initializes coefficients for
!  equation of state.
!
! !REVISION HISTORY:
!  same as module

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  namelist for input choice and coefficient file
!
!-----------------------------------------------------------------------

   integer (int_kind) ::  & 
      k,                  &! vertical loop index
      nu,                 &! unit number for coeff input file
      m,                  &! dummy loop index
      rec_length,         &! record length for input polynomial file
      nml_error            ! namelist i/o error flag

   character (char_len) :: &
      state_choice,        &! choice of which EOS to use
      state_file,          &! file containing polynomial eos coeffs
      state_range_opt       ! option for checking valid T,S range

   namelist /state_nml/ state_choice, state_file,         & 
                        state_range_opt, state_range_freq

!-----------------------------------------------------------------------
!
!  read input choice for EOS type
!
!-----------------------------------------------------------------------

   state_itype = state_type_mwjf
   state_file  = 'internal'
   state_range_iopt = state_range_ignore
   state_range_freq = 100000

   if (my_task == master_task) then
      open (nml_in, file=nml_filename, status='old',iostat=nml_error)
      if (nml_error /= 0) then
         nml_error = -1
      else
         nml_error =  1
      endif
      do while (nml_error > 0)
         read(nml_in, nml=state_nml,iostat=nml_error)
      end do
      if (nml_error == 0) close(nml_in)
   endif

   call broadcast_scalar(nml_error, master_task)
   if (nml_error /= 0) then
      call exit_POP(sigAbort,'ERROR reading state_nml')
   endif

   if (my_task == master_task) then
      write(stdout,blank_fmt)
      write(stdout,ndelim_fmt)
      write(stdout,blank_fmt)
      write(stdout,'(a25)') 'Equation of state options'
      write(stdout,blank_fmt)
      write(stdout,delim_fmt)

      select case (state_choice(1:4))
      case ('jmcd')
         state_itype = state_type_jmcd
         write(stdout,'(a29)') 'Using Jackett & McDougall EOS'
      case ('mwjf')
         state_itype = state_type_mwjf
         write(stdout,'(a48)') &
            'Using McDougall, Wright ,Jackett and Feistel EOS'
      case ('poly')
         state_itype = state_type_polynomial
         write(stdout,'(a20)') 'Using polynomial EOS'
      case ('line')
         state_itype = state_type_linear
         write(stdout,'(a16)') 'Using linear EOS'
      case default
         state_itype = -1000
      end select

      select case (state_range_opt)
      case ('ignore')
         state_range_iopt = state_range_ignore
      case ('check')
         state_range_iopt = state_range_check
      case ('enforce')
         state_range_iopt = state_range_enforce
      case default
         state_range_iopt = -1000
      end select

   endif

   call broadcast_scalar(state_itype,      master_task)
   call broadcast_scalar(state_file,       master_task)
   call broadcast_scalar(state_range_iopt, master_task)
   call broadcast_scalar(state_range_freq, master_task)

   if (state_itype == -1000) then
      call exit_POP(sigAbort, &
                    'unknown choice for equation of state type')
   endif
   if (state_range_iopt == -1000) then
      call exit_POP(sigAbort,'unknown choice for state range option')
   endif

!-----------------------------------------------------------------------
!
!  write eos options to stdout
!
!-----------------------------------------------------------------------

   if (my_task == master_task) then

      select case (state_range_iopt)
      case (state_range_ignore)
         write(stdout,'(a30)') 'No checking of valid T,S range'
      case (state_range_check)
         write(stdout,'(a30,i6,a7)') 'Valid T,S range checked every ', &
                         state_range_freq,' steps.'
      case (state_range_enforce)
         write(stdout,'(a37)') 'EOS computed as if in valid T,S range'
      end select

      if (state_itype == state_type_polynomial) then
         if (state_file == 'internal') then
            write(stdout,'(a39)') & 
              'Calculating EOS coefficients internally'
         else
            write(stdout,*) &
              'Reading EOS coefficients from file: ', trim(state_file)
         endif
      endif

   endif

!-----------------------------------------------------------------------
!
!  calculate pressure as function of depth
!
!-----------------------------------------------------------------------

   do k=1,km
      pressz(k) = pressure(zt(k)*mpercm)
   end do

!-----------------------------------------------------------------------
!
!  initialize various arrays, constants for each state type
!
!-----------------------------------------------------------------------

   select case (state_itype)

!-----------------------------------------------------------------------
!
!  McDougall, Wright, Jackett, and Feistel
!
!-----------------------------------------------------------------------

   case (state_type_mwjf)

      tmin =  -2.0_r8  ! limited   on the low  end
      tmax = 999.0_r8  ! unlimited on the high end
      smin =   0.0_r8  ! limited   on the low  end
      smax = 0.999_r8  ! unlimited on the high end

!-----------------------------------------------------------------------
!
!  Jackett and McDougall
!
!-----------------------------------------------------------------------

   case (state_type_jmcd)

      tmin = -2.0_r8  ! valid pot. temp. range for level k
      tmax = 40.0_r8 
      smin =  0.0_r8  ! valid salinity   range for level k
      smax = 0.042_r8 

!-----------------------------------------------------------------------
!
!  polynomial - initialize polynomial coefficients and ranges
!
!-----------------------------------------------------------------------

   case (state_type_polynomial)

      allocate (to(km), so(km), sigo(km))
      allocate (state_coeffs(9,km))

      if (state_file == 'internal') then
         call init_state_coeffs
      else
         call get_unit(nu)
         if (my_task == master_task) then
            inquire(iolength = rec_length) to
            open(nu,file=state_file,access='direct',form='unformatted',&
                    recl=rec_length,status='unknown')
            read(nu,rec=1) to
            read(nu,rec=2) so
            read(nu,rec=3) sigo
            do m = 1,9
               read(nu,rec=m+3) state_coeffs(m,:)
            enddo
            if (state_range_iopt /= state_range_ignore) then
               read(nu,rec=13) tmin
               read(nu,rec=14) tmax
               read(nu,rec=15) smin
               read(nu,rec=16) smax
            endif
            close (nu)
         endif
         call release_unit(nu)

         call broadcast_array(to,   master_task)
         call broadcast_array(so,   master_task)
         call broadcast_array(sigo, master_task)
         do m = 1,9
            call broadcast_array(state_coeffs(m,:), master_task)
         enddo
         if (state_range_iopt /= state_range_ignore) then
            call broadcast_array(tmin, master_task)
            call broadcast_array(tmax, master_task)
            call broadcast_array(smin, master_task)
            call broadcast_array(smax, master_task)
         endif
      endif

      !***
      !*** convert smin,smax to model units
      !***

      if (state_range_iopt /= state_range_ignore) then
         smin = smin*ppt_to_salt
         smax = smax*ppt_to_salt
      endif

!-----------------------------------------------------------------------
!
!  linear EOS - intialize valid range
!
!-----------------------------------------------------------------------

   case (state_type_linear)

      tmin = -2.0_r8  ! valid pot. temp. range for level k
      tmax = 40.0_r8 
      smin =  0.0_r8  ! valid salinity   range for level k
      smax = 0.042_r8 

   end select

!-----------------------------------------------------------------------
!EOC

 end subroutine init_state

!***********************************************************************
!BOP
! !IROUTINE: init_state_coeffs
! !INTERFACE:


 subroutine init_state_coeffs 1,4

! !DESCRIPTION:
!  This routine calculates coefficients for the polynomial equation of 
!  state option.  This routine calculates the 9 coefficients of a third
!  order (in temperature and salinity) polynomial approximation to the 
!  equation of state for sea water.  The coefficients are calculated by
!  first sampling a range of temperature and salinity at each depth.  
!  The density is then computed at each of the sampled points using the 
!  full UNESCO equation of state.  A least squares method is used to 
!  fit the coefficients of the polynomial to the sampled points.  More 
!  specifically, the densities calculated from the polynomial 
!  formula are in the form of sigma anomalies.  The method is
!  taken from that described by Bryan and Cox (1972).  
!  By default, the program uses the equation of state set by the
!  Joint Panel on Oceanographic Tables and Standards (UNESCO, 1981)
!  an described by Gill (1982).
!
!  This was originall adopted from the GFDL MOM model and extensively 
!  modified to improve readability (February (1999).
!
!  Additionally, improvements to the accuracy of the EOS have been
!  added (March 1999).  There are two improvements: (a) A more 
!  accurate depth-to-pressure conversion based on the Levitus 1994 
!  climatology, and (b) Use of a more recent formula for the 
!  computation of potential temperature (Bryden, 1973) in place of an 
!  older algorithm (Fofonoff, 1962). See Dukowicz (2000).
!
!  References:
!
!    Bryan, K. and M. Cox, An approximate equation of state
!          for numerical models of ocean circulation, J. Phys.
!          Oceanogr., 2, 510-514, 1972.
!
!    Bryden, H.L., New polynomials for thermal expansion, adiabatic
!          temperature gradient and potential temperature of sea water,
!          Deap-Sea Res., 20, 401-408, 1973.
!
!    Dukowicz, J. K., 2000: Reduction of Pressure and Pressure
!          Gradient Errors in Ocean Simulations, J. Phys. Oceanogr.,
!          submitted.
!
!    Fofonoff, N., The Sea: Vol 1, (ed. M. Hill). Interscience,
!          New York, 1962, pp 3-30.
!
!    Gill, A., Atmosphere-Ocean Dynamics: International Geophysical
!          Series No. 30. Academic Press, London, 1982, pp 599-600.
!
!    Hanson, R., and C. Lawson, Extensions and applications of the
!          Householder algorithm for solving linear least squares
!          problems. Math. Comput., 23, 787-812, 1969.
!
!    UNESCO, 10th report of the joint panel on oceanographic tables
!          and standards. UNESCO Tech. Papers in Marine Sci. No. 36,
!          Paris, 1981.
!
! !REVISION HISTORY:
!  same as module

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  local variables
!
!-----------------------------------------------------------------------

   integer (int_kind), parameter ::  &
      nsample_salt = 5,              &! number of pts to sample in S 
      nsample_temp = 2*nsample_salt, &! number of pts in sample in T
      nsample_all  = nsample_salt*nsample_temp  ! total sample points

   integer (int_kind) ::  &
      i,j,k,n,            &! dummy loop counters
      nsample,            &! sample number
      itmax,              &! max iters for least squares
      nlines,             &! number of data lines in km output
      nwords_last_line     ! num data words in last line of km output

   real (r8) ::           &
      dtemp, dsalt,       &! T,S increments for curve fit
      temp_sample,        &! sample temperature in temp range
      salt_sample,        &! sample salinity    in salt range
      density,            &! density in MKS units
      tanom, sanom,       &! T,S anomalies (from level average)
      enorm                ! norm of least squares residual

   real (r8), dimension(:), allocatable :: &
      avg_theta       ! average of sample pot. temps for level k

   real (r8), dimension(nsample_all) :: &
      tsamp,         &! temperature     at each sample point 
      ssamp,         &! salinity        at each sample point
      thsamp,        &! potential temp  at each sample point
      sigma,         &! density         at each sample point
      sigman          ! density anomaly at each sample point

   real (r8), dimension(nsample_all,9) :: &
      lsqarray        ! least squares array to find coeffs

   real (r8), dimension(9) :: &
      lsqcoeffs       ! polynomial coeffs returned by lsq routine

   !***
   !*** bounds for polynomial fit using a reference model of 
   !*** 33 levels from surface to 8000m at z=(k-1)*250 meters
   !*** The user should review the appropriateness of the 
   !*** reference values set below, and modify them if the 
   !*** intended modelling application could be expected to yield 
   !*** temperature and salinity values outside of the ranges set 
   !*** by default.
   !***

   real (r8), dimension(33) ::                &
      trefmin = (/ -2.0_r8, -2.0_r8, -2.0_r8, &
                 & -2.0_r8, -1.0_r8, -1.0_r8, &
                 & -1.0_r8, -1.0_r8, -1.0_r8, &
                 & -1.0_r8, -1.0_r8, -1.0_r8, &
                 & -1.0_r8, -1.0_r8, -1.0_r8, &
                 & -1.0_r8, -1.0_r8, -1.0_r8, &
                 & -1.0_r8,  0.0_r8,  0.0_r8, &
                 &  0.0_r8,  0.0_r8,  0.0_r8, &
                 &  0.0_r8,  0.0_r8,  0.0_r8, &
                 &  0.0_r8,  0.0_r8,  0.0_r8, &
                 &  0.0_r8,  0.0_r8,  0.0_r8 /)

   real (r8), dimension(33) ::                &
      trefmax = (/ 29.0_r8, 19.0_r8, 14.0_r8, &
                 & 11.0_r8,  9.0_r8,  7.0_r8, &
                 &  7.0_r8,  7.0_r8,  7.0_r8, &
                 &  7.0_r8,  7.0_r8,  7.0_r8, &
                 &  7.0_r8,  7.0_r8,  7.0_r8, &
                 &  7.0_r8,  7.0_r8,  7.0_r8, &
                 &  7.0_r8,  7.0_r8,  7.0_r8, &
                 &  7.0_r8,  7.0_r8,  7.0_r8, &
                 &  7.0_r8,  7.0_r8,  7.0_r8, &
                 &  7.0_r8,  7.0_r8,  7.0_r8, &
                 &  7.0_r8,  7.0_r8,  7.0_r8 /)

   real (r8), dimension(33) ::                &
      srefmin = (/ 28.5_r8, 33.7_r8, 34.0_r8, &
                 & 34.1_r8, 34.2_r8, 34.4_r8, &
                 & 34.5_r8, 34.5_r8, 34.6_r8, &
                 & 34.6_r8, 34.6_r8, 34.6_r8, &
                 & 34.6_r8, 34.6_r8, 34.6_r8, &
                 & 34.6_r8, 34.6_r8, 34.6_r8, &
                 & 34.6_r8, 34.6_r8, 34.6_r8, &
                 & 34.6_r8, 34.6_r8, 34.7_r8, &
                 & 34.7_r8, 34.7_r8, 34.7_r8, &
                 & 34.7_r8, 34.7_r8, 34.7_r8, &
                 & 34.7_r8, 34.7_r8, 34.7_r8 /)

   real (r8), dimension(33) ::                &
      srefmax = (/ 37.0_r8, 36.6_r8, 35.8_r8, &
                 & 35.7_r8, 35.3_r8, 35.1_r8, &
                 & 35.1_r8, 35.0_r8, 35.0_r8, &
                 & 35.0_r8, 35.0_r8, 35.0_r8, &
                 & 35.0_r8, 35.0_r8, 35.0_r8, &
                 & 35.0_r8, 35.0_r8, 35.0_r8, &
                 & 35.0_r8, 35.0_r8, 35.0_r8, &
                 & 35.0_r8, 35.0_r8, 35.0_r8, &
                 & 35.0_r8, 35.0_r8, 35.0_r8, &
                 & 35.0_r8, 35.0_r8, 35.0_r8, &
                 & 35.0_r8, 35.0_r8, 35.0_r8 /)

!-----------------------------------------------------------------------
!
!  set the temperature and salinity ranges to be used for each
!  model level when performing the polynomial fitting
!
!-----------------------------------------------------------------------

   do k=1,km
      i = int(zt(k)*mpercm/250.0_r8) + 1
      tmin(k) = trefmin(i)
      tmax(k) = trefmax(i)
      smin(k) = srefmin(i)
      smax(k) = srefmax(i)
   end do

   allocate (avg_theta(km))

!-----------------------------------------------------------------------
!
!  loop over all model levels
!
!-----------------------------------------------------------------------

   do k=1,km

!-----------------------------------------------------------------------
!
!     sample the temperature range with nsample_temp points
!     and the salinity range with nsample_salt points and
!     create an array of possible combinations of T,S samples
!
!-----------------------------------------------------------------------

      dtemp = (tmax(k)-tmin(k)) / (nsample_temp-c1)
      dsalt = (smax(k)-smin(k)) / (nsample_salt-c1)

      nsample = 0
      do i=1,nsample_temp
         temp_sample = tmin(k) + (i-1)*dtemp
         do j=1,nsample_salt
            nsample = nsample + 1
            salt_sample = smin(k) + (j-1)*dsalt
            tsamp(nsample) = temp_sample
            ssamp(nsample) = salt_sample
         end do
      end do

!-----------------------------------------------------------------------
!
!     loop over the number of samples
!
!-----------------------------------------------------------------------

      !***
      !*** initialize averaging sums
      !***

      to  (k) = c0
      so  (k) = c0
      sigo(k) = c0
      avg_theta(k) = c0

      do n=1,nsample_all

!-----------------------------------------------------------------------
!
!        calculate density (sigma) for each t,s combintion at
!        this depth using full unesco equation of state
!        unesco returns density (kg per m**3)
!
!-----------------------------------------------------------------------

         call unesco(tsamp(n),ssamp(n),pressz(k),density)

         sigma(n) = density - 1.0e3_r8

!-----------------------------------------------------------------------
!
!        calculate potential temp. from from insitu temperature,
!        salinity, and pressure
!
!-----------------------------------------------------------------------

         call potem(tsamp(n),ssamp(n),pressz(k),thsamp(n))

!-----------------------------------------------------------------------
!
!        accumulate level averages and end loop over samples
!
!-----------------------------------------------------------------------

         to  (k) = to  (k) + tsamp(n)
         so  (k) = so  (k) + ssamp(n)
         sigo(k) = sigo(k) + sigma(n)
         avg_theta(k) = avg_theta(k) + thsamp(n)

      end do ! loop over samples

!-----------------------------------------------------------------------
!
!     complete layer averages
!
!-----------------------------------------------------------------------

      to  (k) = to  (k)/real(nsample_all)
      so  (k) = so  (k)/real(nsample_all)
      sigo(k) = sigo(k)/real(nsample_all)
      avg_theta(k) = avg_theta(k)/real(nsample_all)

!-----------------------------------------------------------------------
!
!     recompute average (reference) density based on level average 
!     values of T, S, and pressure.
!     use average potential temperature in place of average temp
!
!-----------------------------------------------------------------------

      call unesco (to(k), so(k), pressz(k), density)

      sigo(k) = density - 1.0e3_r8
      to  (k) = avg_theta(k)

!-----------------------------------------------------------------------
!
!     fill array for least squares routine with anomalies
!     and their products (the terms in the desired cubic
!     polynomial.
!
!-----------------------------------------------------------------------

      do n=1,nsample_all

         tsamp(n) = thsamp(n)   !*** replace temp with potential temp

         tanom = tsamp(n) - to(k)
         sanom = ssamp(n) - so(k)
         sigman(n) = sigma(n) - sigo(k)

         lsqarray(n,1) = tanom
         lsqarray(n,2) = sanom
         lsqarray(n,3) = tanom * tanom
         lsqarray(n,4) = tanom * sanom
         lsqarray(n,5) = sanom * sanom
         lsqarray(n,6) = tanom * tanom * tanom
         lsqarray(n,7) = sanom * sanom * tanom
         lsqarray(n,8) = tanom * tanom * sanom
         lsqarray(n,9) = sanom * sanom * sanom

      end do

!-----------------------------------------------------------------------
!
!     LSQL2 is  a Jet Propulsion Laboratory subroutine that
!     computes the least squares fit in an iterative manner for
!     overdetermined systems. it is called here to iteratively
!     determine polynomial coefficients for cubic polynomials
!     that will best fit the density at the sampled points
!
!-----------------------------------------------------------------------

      itmax = 4
      call lsqsl2 (lsqarray, sigman, lsqcoeffs, itmax, enorm, 1.0e-7_r8)

!-----------------------------------------------------------------------
!
!     store coefficients
!
!-----------------------------------------------------------------------

      state_coeffs(:,k) = lsqcoeffs

!-----------------------------------------------------------------------
!
!     end of loop over levels
!
!-----------------------------------------------------------------------

   end do

!-----------------------------------------------------------------------
!
!  rescale some of the coefficients and reference values for correct 
!  units
!
!-----------------------------------------------------------------------

   do k=1,km
      sigo(k) = p001 * sigo(k)
      so  (k) = ppt_to_salt * so  (k) - 0.035_r8

      state_coeffs(1,k) = 1.e-3_r8 * state_coeffs(1,k)
      state_coeffs(3,k) = 1.e-3_r8 * state_coeffs(3,k)
      state_coeffs(5,k) = 1.e+3_r8 * state_coeffs(5,k)
      state_coeffs(6,k) = 1.e-3_r8 * state_coeffs(6,k)
      state_coeffs(7,k) = 1.e+3_r8 * state_coeffs(7,k)
      state_coeffs(9,k) = 1.e+6_r8 * state_coeffs(9,k)
   end do
   do k=1,km
      sigo(k) = sigo(k) * c1000
   end do

   deallocate (avg_theta)

!-----------------------------------------------------------------------
!EOC

 end subroutine init_state_coeffs

!***********************************************************************
!BOP
! !IROUTINE: potem
! !INTERFACE:


 subroutine potem (temp, salt, pbars, theta) 1

! !DESCRIPTION:
!  This subroutine calculates potential temperature as a function
!  of in-situ temperature, salinity, and pressure.
!
!  Reference:
!    Bryden, H.L., New polynomials for thermal expansion, adiabatic
!          temperature gradient and potential temperature of sea water,
!          Deap-Sea Res., 20, 401-408, 1973.
!
! !REVISION HISTORY:
!  same as module

! !INPUT PARAMETERS:

   real (r8), intent(in) :: &
      temp,                 &! in-situ temperature [degrees C]
      salt,                 &! salinity [per mil]
      pbars                  ! pressure [bars]

! !OUTPUT PARAMETERS:

   real (r8), intent(out) :: &
      theta                   ! potential temperature [degrees C]

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  local variables
!
!-----------------------------------------------------------------------

   real (r8) :: prss2, prss3, potmp

!-----------------------------------------------------------------------
!
!  compute powers of several variables
!
!-----------------------------------------------------------------------

   prss2 = pbars*pbars
   prss3 = prss2*pbars

!-----------------------------------------------------------------------
!
!  compute potential temperature from polynomial
!
!-----------------------------------------------------------------------

   potmp = pbars*(3.6504e-4_r8 + temp*(8.3198e-5_r8 +   &
           temp*(-5.4065e-7_r8 + temp*4.0274e-9_r8))) + &
           pbars*(salt - 35.0_r8)*(1.7439e-5_r8 -       &
           temp*2.9778e-7_r8) + prss2*(8.9309e-7_r8 +   &
           temp*(-3.1628e-8_r8 + temp*2.1987e-10_r8)) - &
           4.1057e-9_r8*prss2*(salt - 35.0_r8) +        &
           prss3*(-1.6056e-10_r8 + temp*5.0484e-12_r8)

   theta = temp - potmp

!-----------------------------------------------------------------------
!EOC

 end subroutine potem

!***********************************************************************
!BOP
! !IROUTINE: unesco
! !INTERFACE:


 subroutine unesco (temp, salt, pbars, rho) 2

! !DESCRIPTION:
!  This subroutine calculates the density of seawater using the
!  standard equation of state recommended by unesco (1981).
!
!  References:
!
!    Gill, A., Atmosphere-Ocean Dynamics: International Geophysical
!         Series No. 30. Academic Press, London, 1982, pp 599-600.
!
!    UNESCO, 10th report of the joint panel on oceanographic tables
!          and standards. UNESCO Tech. Papers in Marine Sci. No. 36,
!          Paris, 1981.
!
! !REVISION HISTORY:
!  same as module

! !INPUT PARAMETERS:

   real (r8), intent(in) :: &
      temp,                 &! in-situ temperature [degrees C]
      salt,                 &! salinity [practical salinity units]
      pbars                  ! pressure [bars]

! !OUTPUT PARAMETERS:

   real (r8), intent(out) :: &
      rho                    ! density in kilograms per cubic meter

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  local variables
!
!-----------------------------------------------------------------------

   real (r8) :: rw, rsto, xkw, xksto, xkstp, &
                tem2, tem3, tem4, tem5, slt2, st15, pbar2 

!-----------------------------------------------------------------------
!
!  compute powers of several variables
!
!-----------------------------------------------------------------------

   tem2 = temp**2
   tem3 = temp**3
   tem4 = temp**4
   tem5 = temp**5
   slt2 = salt**2
   st15 = salt**(1.5_r8) 
   pbar2 = pbars**2

!-----------------------------------------------------------------------
!
!  compute density
!
!-----------------------------------------------------------------------

   rw = unt0 + unt1*temp + unt2*tem2 + unt3*tem3 + & 
                           unt4*tem4 + unt5*tem5

   rsto = rw +                                       &
          (uns1t0 + uns1t1*temp + uns1t2*tem2 +      &
                    uns1t3*tem3 + uns1t4*tem4)*salt  &
        + (unsqt0 + unsqt1*temp + unsqt2*tem2)*st15 + uns2t0*slt2

   xkw =    1.965221e+4_r8 +                             &
            1.484206e+2_r8*temp - 2.327105e+0_r8*tem2 +  &
            1.360477e-2_r8*tem3 - 5.155288e-5_r8*tem4

   xksto =  xkw +                                             &
            (5.46746e+1_r8      - 6.03459e-1_r8*temp +        &
             1.09987e-2_r8*tem2 - 6.1670e-5_r8*tem3)*salt     &
          + (7.944e-2_r8  +                                   &
             1.6483e-2_r8 *temp - 5.3009e-4_r8*tem2)*st15

   xkstp =  xksto +                                               &
            (3.239908e+0_r8     + 1.43713e-3_r8*temp +            &
             1.16092e-4_r8*tem2 - 5.77905e-7_r8*tem3)*pbars       &
          + (2.2838e-3_r8       - 1.0981e-5_r8 *temp -            &
             1.6078e-6_r8 *tem2)*pbars*salt                       &
          + 1.91075e-4_r8       *pbars*st15                       &
          + (8.50935e-5_r8      - 6.12293e-6_r8*temp +            &
                                        5.2787e-8_r8 *tem2)*pbar2 &
          + (-9.9348e-7_r8      + 2.0816e-8_r8 *temp +            &
             9.1697e-10_r8*tem2)*pbar2*salt

   rho =   rsto / (c1 - pbars/xkstp)

!-----------------------------------------------------------------------
!EOC

 end subroutine unesco

!***********************************************************************
!BOP
! !IROUTINE: pressure
! !INTERFACE:


 function pressure(depth) 7

! !DESCRIPTION:
!  This function computes pressure in bars from depth in meters
!  using a mean density derived from depth-dependent global 
!  average temperatures and salinities from Levitus 1994, and 
!  integrating using hydrostatic balance.
!
!  References:
!
!     Levitus, S., R. Burgett, and T.P. Boyer, World Ocean Atlas 
!          1994, Volume 3: Salinity, NOAA Atlas NESDIS 3, US Dept. of 
!          Commerce, 1994.
!
!     Levitus, S. and T.P. Boyer, World Ocean Atlas 1994, 
!          Volume 4: Temperature, NOAA Atlas NESDIS 4, US Dept. of 
!          Commerce, 1994.
!
!     Dukowicz, J. K., 2000: Reduction of Pressure and Pressure
!          Gradient Errors in Ocean Simulations, J. Phys. Oceanogr.,
!          submitted.
!
! !REVISION HISTORY:
!  same as module

! !INPUT PARAMETERS:

   real (r8), intent(in) :: depth    ! depth in meters

! !OUTPUT PARAMETERS:

   real (r8) :: pressure   ! pressure [bars]

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  convert depth in meters to pressure in bars
!
!-----------------------------------------------------------------------

   pressure = 0.059808_r8*(exp(-0.025_r8*depth) - c1)     &
            + 0.100766_r8*depth + 2.28405e-7_r8*depth**2

!-----------------------------------------------------------------------
!EOC

 end function pressure

!***********************************************************************
!BOP
! !IROUTINE: lsqsl2
! !INTERFACE:


 subroutine lsqsl2 (a,b,x,itmax,enorm,eps1) 1

! !DESCRIPTION:
!  This routine is a modification of a very old ugly bit of code
!  from way back in March,1968 and originally written by a
!  gentleman (presumably) named R. Hanson of unknown descent.
!  The routine finds a linear least squares minimization of Ax-b.
!
! !REVISION HISTORY:
!  same as module

! !INPUT/OUTPUT PARAMETERS:

   real (r8), dimension(:,:), intent(inout) :: &
     a                   ! input matrix A with the value of each of 
                         ! the fitting functions at each sample point

! !INPUT PARAMETERS:

   real (r8), dimension(size(a,dim=1)), intent(in) :: &
     b                   ! data points to fit

   integer (int_kind), intent(in) ::  &
     itmax               ! max number of iterations for least-square

   real (r8), intent(in) ::  &
     eps1                ! some sort of tolerance

! !OUTPUT PARAMETERS:

   real (r8), dimension(size(a,dim=2)), intent(out) :: &
     x                   ! coefficient of each fitting function
                         ! that provides optimal fit of sampled pts

   real (r8), intent(out) ::  &
     enorm               ! norm of least squares residuals

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  local variables
!
!-----------------------------------------------------------------------

   integer (int_kind) :: irank,it

   real (r8) :: sj,dp,up,bp,aj

   real (r8), dimension(size(a,dim=1),size(a,dim=2)) :: &
     aa

   real (r8), dimension(size(a,dim=1) + 8*size(a,dim=2)) :: &
     s

   real (r8), dimension(size(a,dim=1) + 4*size(a,dim=2)) :: &
     r

   integer (int_kind) :: i,j,k,l,m,n,isw,ip,lm,irp1,irm1,n1,ns,k1,k2, &
                         j1, j2, j3, j4, j5, j6, j7, j8, j9

   real (r8) :: epsloc,am,a1,sp,top,top1,top2,enm1,a2


!-----------------------------------------------------------------------
!
!  initialize various useful numbers
!
!-----------------------------------------------------------------------

   k=size(a,dim=1)
   n=size(a,dim=2)

   it = 0
   irank = 0
   epsloc = 1.e-16_r8

   isw = 1
   l   = min(k,n)
   m   = max(k,n)
   j1  = m
   j2  = j1 + n
   j3  = j2 + n
   j4  = j3 + l
   j5  = j4 + l
   j6  = j5 + l
   j7  = j6 + l
   j8  = j7 + n
   j9  = j8 + n

   lm  = l

!-----------------------------------------------------------------------
!
!  equilibrate columns of a (1)-(2).
!
!-----------------------------------------------------------------------

   aa = a

   do j=1,n
     am = c0
     do i=1,k
       am = max(am,abs(a(i,j)))
     end do

     !*** s(m+n+1)-s(m+2n) contains scaling for output variables.

     s(j2+j) = c1/am
     do i=1,k
       a(i,j) = a(i,j)*s(j2+j)
     end do
   end do

!-----------------------------------------------------------------------
!
!  compute column lengths
!
!-----------------------------------------------------------------------

   do j=1,n
     s(j7+j) = s(j2+j)
   end do

!-----------------------------------------------------------------------
!
!   s(m+1)-s(m+ n) contains variable permutations.
!  set permutation to identity.
!
!-----------------------------------------------------------------------

   do j=1,n
     s(j1+j) = j
   end do

!-----------------------------------------------------------------------
!
!  begin elimination on the matrix a with orthogonal matrices .
!  ip=pivot row
!
!-----------------------------------------------------------------------

   do ip=1,lm

     dp = c0
     k2 = ip
     do j=ip,n
       sj = c0
       do i=ip,k
         sj = sj + a(i,j)**2
       end do
       if (sj >= dp) then
         dp = sj
         k2 = j
       endif
     end do

!-----------------------------------------------------------------------
!
!    maximize (sigma)**2 by column interchange.
!     exchange columns if necessary.
!
!-----------------------------------------------------------------------

     if (k2 /= ip) then
       do i=1,k
         a1      = a(i,ip)
         a(i,ip) = a(i,k2)
         a(i,k2) = a1
       end do

       a1       = s(j1+k2)
       s(j1+k2) = s(j1+ip)
       s(j1+ip) = a1

       a1       = s(j7+k2)
       s(j7+k2) = s(j7+ip)
       s(j7+ip) = a1
     endif

     if (ip /= 1) then
       a1 = c0
       do i=1,ip-1
         a1 = a1 + a(i,ip)**2
       end do
       if (a1 > c0) go to 190
     end if
     if (dp > c0) go to 200

!-----------------------------------------------------------------------
!
!    test for rank deficiency.
!
!-----------------------------------------------------------------------

 190 if (sqrt(dp/a1) > eps1) go to 200
     irank = ip-1
     go to 260

!-----------------------------------------------------------------------
!
!    (eps1) rank is deficient.
!
!-----------------------------------------------------------------------

 200 sp = sqrt(dp)

!-----------------------------------------------------------------------
!
!    begin front elimination on column ip.
!    sp=sqroot(sigma**2).
!
!-----------------------------------------------------------------------

     bp = c1/(dp+sp*abs(a(ip,ip)))

!-----------------------------------------------------------------------
!
!    store beta in s(3n+1)-s(3n+l).
!
!-----------------------------------------------------------------------

     if (ip == k) bp = c0
     r(k+2*n+ip) = bp
     up = sign(sp + abs(a(ip,ip)), a(ip,ip))
     if (ip < k) then
       if (ip < n) then
         do j=ip+1,n
           sj = c0
           do i=ip+1,k
             sj=sj+a(i,j)*a(i,ip)
           end do
           sj = sj + up*a(ip,j)
           sj = bp*sj  !*** sj=yj now
 
           do i=ip+1,k
             a(i,j) = a(i,j) - a(i,ip)*sj
           end do
           a(ip,j) = a(ip,j) - sj*up
         end do
       endif
       a(ip,ip)    = -sign(sp,a(ip,ip))
       r(k+3*n+ip) = up
     endif

   end do
   irank = lm
 260 irp1 = irank+1
   irm1 = irank-1

   if (irank == 0 .or. irank == n) go to 360

!-----------------------------------------------------------------------
!
!  begin back processing for rank deficiency case
!   if irank is less than n.
!
!-----------------------------------------------------------------------

   do j=1,n
     l = min(j,irank)

     !*** unscale columns for rank deficient matrices
     do i=1,l
       a(i,j) = a(i,j)/s(j7+j)
     end do

     s(j7+j) = c1
     s(j2+j) = c1
   end do

   ip = irank

 300 sj = c0
   do j=irp1,n
     sj = sj + a(ip,j)**2
   end do
   sj = sj + a(ip,ip)**2
   aj = sqrt(sj)
   up = sign(aj+abs(a(ip,ip)), a(ip,ip))

!-----------------------------------------------------------------------
!
!  ip th element of u vector calculated.
!  bp = 2/length of u squared.
!
!-----------------------------------------------------------------------

   bp = c1/(sj+abs(a(ip,ip))*aj)

   if (ip-1 > 0) then
     do i=1,ip-1
       dp = a(i,ip)*up
       do j=irp1,n
         dp = dp + a(i,j)*a(ip,j)
       end do
       dp = dp/(sj+abs(a(ip,ip))*aj)

       !*** calc. (aj,u), where aj=jth row of a

       a(i,ip) = a(i,ip) - up*dp

       !*** modify array a.

       do j=irp1,n
         a(i,j) = a(i,j) - a(ip,j)*dp
       end do
     end do
   endif

   a(ip,ip) = -sign(aj,a(ip,ip))

!-----------------------------------------------------------------------
!
!  calc. modified pivot.
!  save beta and ip th element of u vector in r array.
!
!-----------------------------------------------------------------------

   r(k+ip)   = bp
   r(k+n+ip) = up

!-----------------------------------------------------------------------
!
!  test for end of back processing.
!
!-----------------------------------------------------------------------

   if (ip-1 > 0) then
     ip=ip-1
     go to 300
   endif

 360 continue

   do j=1,k
     r(j) = b(j)
   end do

!-----------------------------------------------------------------------
!
!  set initial x vector to zero.
!
!-----------------------------------------------------------------------

   do j=1,n
     x(j) = c0
   end do
   if (irank == 0) go to 690

!-----------------------------------------------------------------------
!
!  apply q to rt. hand side.
!
!-----------------------------------------------------------------------

 390 do ip=1,irank
     sj = r(k+3*n+ip)*r(ip)
     if (ip+1 <= k) then
       do i=ip+1,k
         sj = sj + a(i,ip)*r(i)
       end do
     endif
     bp = r(k+2*n+ip)
     if (ip+1 <= k) then
       do i=ip+1,k
         r(i) = r(i) - bp*a(i,ip)*sj
       end do
     endif
     r(ip) = r(ip) - bp*r(k+3*n+ip)*sj
   end do

   do j=1,irank
     s(j) = r(j)
   end do

   enorm = c0
   if (irp1.gt.k) go to 510
   do j=irp1,k
     enorm = enorm + r(j)**2
   end do
   enorm = sqrt(enorm)
   go to 510

 460 do j=1,n
     sj = c0
     ip = s(j1+j)
     do i=1,k
       sj = sj + r(i)*aa(i,ip)
     end do

     !*** apply to rt. hand side.  apply scaling.

     r(k+n+j) = sj*s(j2+ip)
   end do

   s(1) = r(k+n+1)/a(1,1)
   if (n /= 1) then
     do j=2,n
       sj = c0
       do i=1,j-1
         sj = sj + a(i,j)*s(i)
       end do
       s(j) = (r(k+j+n)-sj)/a(j,j)
     end do
   endif

!-----------------------------------------------------------------------
!
!  entry to continue iterating.  solves tz = c = 1st irank
!  components of qb .
!
!-----------------------------------------------------------------------

 510 s(irank) = s(irank)/a(irank,irank)
   if (irm1 /= 0) then
     do j=1,irm1
       n1 = irank-j
       sj = 0.
       do i=n1+1,irank
         sj = sj + a(n1,i)*s(i)
       end do
       s(n1) = (s(n1)-sj)/a(n1,n1)
     end do
   endif

!-----------------------------------------------------------------------
!
!  z calculated.  compute x = sz.
!
!-----------------------------------------------------------------------

   if (irank /= n) then
     do j=irp1,n
       s(j) = c0
     end do
     do i=1,irank
       sj = r(k+n+i)*s(i)
       do j=irp1,n
         sj = sj + a(i,j)*s(j)
       end do
       do j=irp1,n
         s(j) = s(j) - a(i,j)*r(k+i)*sj
       end do
       s(i) = s(i) - r(k+i)*r(k+n+i)*sj
     end do
   endif

!-----------------------------------------------------------------------
!
!  increment for x of minimal length calculated.
!
!-----------------------------------------------------------------------

   do i=1,n
     x(i) = x(i) + s(i)
   end do

!-----------------------------------------------------------------------
!
!  calc. sup norm of increment and residuals
!
!-----------------------------------------------------------------------

   top1 = c0
   do j=1,n
     top1 = max(top1,abs(s(j))*s(j7+j))
   end do

   do i=1,k
     sj = c0
     do j=1,n
       ip = s(j1+j)
       sj = sj + aa(i,ip)*x(j)*s(j2+ip)
     end do
     r(i) = b(i) - sj
   end do

!-----------------------------------------------------------------------
!
!  calc. sup norm of x.
!
!-----------------------------------------------------------------------

   top = c0
   do j=1,n
     top = max(top,abs(x(j))*s(j7+j))
   end do

!-----------------------------------------------------------------------
!
!  compare relative change in x with tolerance eps .
!
!-----------------------------------------------------------------------

   if (top1-top*epsloc > 0) then
     if (it-itmax < 0) then
       it = it+1
       if (it /= 1) then
         if (top1 > p25*top2) go to 690
       endif
       top2 = top1
       if (isw == 1) then
         go to 390
       else if (isw ==2) then
         go to 460
       endif
     endif
     it=0
   endif
 690 sj=c0
   do j=1,k
     sj = sj + r(j)**2
   end do
   enorm = sqrt(sj)

   if (irank == n .and. isw == 1) then
     enm1 = enorm

     !*** save x array

     do j=1,n
       r(k+j) = x(j)
     end do
     isw = 2
     it = 0
     go to 460
   endif

!-----------------------------------------------------------------------
!
!  choose best solution
!
!-----------------------------------------------------------------------

   if (irank >= n .and. enorm > enm1) then
     do j=1,n
       x(j) = r(k+j)
     end do
     enorm = enm1
   endif

!-----------------------------------------------------------------------
!
!  norm of ax - b located in the cell enorm .
!  rearrange variables.
!
!-----------------------------------------------------------------------

   do j=1,n
     s(j) = s(j1+j)
   end do
   do j=1,n
     do i=j,n
       ip = s(i)
       if (j == ip) go to 780
     end do
 780 s(i) = s(j)
     s(j) = j
     sj   = x(j)
     x(j) = x(i)
     x(i) = sj
   end do

!-----------------------------------------------------------------------
!
!  scale variables.
!
!-----------------------------------------------------------------------

   do j=1,n
     x(j) = x(j)*s(j2+j)
   end do

!-----------------------------------------------------------------------
!EOC

 end subroutine lsqsl2

!***********************************************************************

 end module state_mod

!|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||