Skip to content
Snippets Groups Projects
eosbn2.F90 80.5 KiB
Newer Older
Guillaume Samson's avatar
Guillaume Samson committed
MODULE eosbn2
   !!==============================================================================
   !!                       ***  MODULE  eosbn2  ***
   !! Equation Of Seawater : in situ density - Brunt-Vaisala frequency
   !!==============================================================================
   !! History :  OPA  ! 1989-03  (O. Marti)  Original code
   !!            6.0  ! 1994-07  (G. Madec, M. Imbard)  add bn2
   !!            6.0  ! 1994-08  (G. Madec)  Add Jackett & McDougall eos
   !!            7.0  ! 1996-01  (G. Madec)  statement function for e3
   !!            8.1  ! 1997-07  (G. Madec)  density instead of volumic mass
   !!             -   ! 1999-02  (G. Madec, N. Grima) semi-implicit pressure gradient
   !!            8.2  ! 2001-09  (M. Ben Jelloul)  bugfix on linear eos
   !!   NEMO     1.0  ! 2002-10  (G. Madec)  add eos_init
   !!             -   ! 2002-11  (G. Madec, A. Bozec)  partial step, eos_insitu_2d
   !!             -   ! 2003-08  (G. Madec)  F90, free form
   !!            3.0  ! 2006-08  (G. Madec)  add tfreez function (now eos_fzp function)
   !!            3.3  ! 2010-05  (C. Ethe, G. Madec)  merge TRC-TRA
   !!             -   ! 2010-10  (G. Nurser, G. Madec)  add alpha/beta used in ldfslp
   !!            3.7  ! 2012-03  (F. Roquet, G. Madec)  add primitive of alpha and beta used in PE computation
   !!             -   ! 2012-05  (F. Roquet)  add Vallis and original JM95 equation of state
   !!             -   ! 2013-04  (F. Roquet, G. Madec)  add eos_rab, change bn2 computation and reorganize the module
   !!             -   ! 2014-09  (F. Roquet)  add TEOS-10, S-EOS, and modify EOS-80
   !!             -   ! 2015-06  (P.A. Bouttier) eos_fzp functions changed to subroutines for AGRIF
   !!----------------------------------------------------------------------

   !!----------------------------------------------------------------------
   !!   eos           : generic interface of the equation of state
   !!   eos_insitu    : Compute the in situ density
   !!   eos_insitu_pot: Compute the insitu and surface referenced potential volumic mass
   !!   eos_insitu_2d : Compute the in situ density for 2d fields
   !!   bn2           : compute the Brunt-Vaisala frequency
   !!   eos_pt_from_ct: compute the potential temperature from the Conservative Temperature
   !!   eos_rab       : generic interface of in situ thermal/haline expansion ratio
   !!   eos_rab_3d    : compute in situ thermal/haline expansion ratio
   !!   eos_rab_2d    : compute in situ thermal/haline expansion ratio for 2d fields
   !!   eos_fzp_2d    : freezing temperature for 2d fields
   !!   eos_fzp_0d    : freezing temperature for scalar
   !!   eos_init      : set eos parameters (namelist)
   !!----------------------------------------------------------------------
   USE dom_oce        ! ocean space and time domain
   USE domutl, ONLY : is_tile
   USE phycst         ! physical constants
   USE stopar         ! Stochastic T/S fluctuations
   USE stopts         ! Stochastic T/S fluctuations
   !
   USE in_out_manager ! I/O manager
Sebastien MASSON's avatar
Sebastien MASSON committed
   USE lib_mpp        ! for ctl_stop
Guillaume Samson's avatar
Guillaume Samson committed
   USE prtctl         ! Print control
   USE timing         ! Timing

   IMPLICIT NONE
   PRIVATE

   !                  !! * Interface
   INTERFACE eos
      MODULE PROCEDURE eos_insitu, eos_insitu_pot, eos_insitu_2d, eos_insitu_pot_2d
   END INTERFACE
   !
   INTERFACE eos_rab
      MODULE PROCEDURE rab_3d, rab_2d, rab_0d
   END INTERFACE
   !
   INTERFACE eos_fzp
      MODULE PROCEDURE eos_fzp_2d, eos_fzp_0d
   END INTERFACE
   !
   PUBLIC   eos            ! called by step, istate, tranpc and zpsgrd modules
   PUBLIC   bn2            ! called by step module
   PUBLIC   eos_rab        ! called by ldfslp, zdfddm, trabbl
   PUBLIC   eos_pt_from_ct ! called by sbcssm
   PUBLIC   eos_fzp        ! called by traadv_cen2 and sbcice_... modules
   PUBLIC   eos_pen        ! used for pe diagnostics in trdpen module
   PUBLIC   eos_init       ! called by istate module

   !                               !!** Namelist nameos **
   LOGICAL , PUBLIC ::   ln_TEOS10
   LOGICAL , PUBLIC ::   ln_EOS80
   LOGICAL , PUBLIC ::   ln_SEOS

   ! Parameters
   LOGICAL , PUBLIC    ::   l_useCT         ! =T in ln_TEOS10=T (i.e. use eos_pt_from_ct to compute sst_m), =F otherwise
   INTEGER , PUBLIC    ::   neos            ! Identifier for equation of state used

   INTEGER , PARAMETER ::   np_teos10 = -1  ! parameter for using TEOS10
   INTEGER , PARAMETER ::   np_eos80  =  0  ! parameter for using EOS80
   INTEGER , PARAMETER ::   np_seos   = 1   ! parameter for using Simplified Equation of state

   !                               !!!  simplified eos coefficients (default value: Vallis 2006)
   REAL(wp), PUBLIC ::   rn_a0      = 1.6550e-1_wp     ! thermal expansion coeff.
   REAL(wp), PUBLIC ::   rn_b0      = 7.6554e-1_wp     ! saline  expansion coeff.
   REAL(wp) ::   rn_lambda1 = 5.9520e-2_wp     ! cabbeling coeff. in T^2
   REAL(wp) ::   rn_lambda2 = 5.4914e-4_wp     ! cabbeling coeff. in S^2
   REAL(wp) ::   rn_mu1     = 1.4970e-4_wp     ! thermobaric coeff. in T
   REAL(wp) ::   rn_mu2     = 1.1090e-5_wp     ! thermobaric coeff. in S
   REAL(wp) ::   rn_nu      = 2.4341e-3_wp     ! cabbeling coeff. in theta*salt

   ! TEOS10/EOS80 parameters
   REAL(wp) ::   r1_S0, r1_T0, r1_Z0, rdeltaS

   ! EOS parameters
   REAL(wp) ::   EOS000 , EOS100 , EOS200 , EOS300 , EOS400 , EOS500 , EOS600
   REAL(wp) ::   EOS010 , EOS110 , EOS210 , EOS310 , EOS410 , EOS510
   REAL(wp) ::   EOS020 , EOS120 , EOS220 , EOS320 , EOS420
   REAL(wp) ::   EOS030 , EOS130 , EOS230 , EOS330
   REAL(wp) ::   EOS040 , EOS140 , EOS240
   REAL(wp) ::   EOS050 , EOS150
   REAL(wp) ::   EOS060
   REAL(wp) ::   EOS001 , EOS101 , EOS201 , EOS301 , EOS401
   REAL(wp) ::   EOS011 , EOS111 , EOS211 , EOS311
   REAL(wp) ::   EOS021 , EOS121 , EOS221
   REAL(wp) ::   EOS031 , EOS131
   REAL(wp) ::   EOS041
   REAL(wp) ::   EOS002 , EOS102 , EOS202
   REAL(wp) ::   EOS012 , EOS112
   REAL(wp) ::   EOS022
   REAL(wp) ::   EOS003 , EOS103
   REAL(wp) ::   EOS013

   ! ALPHA parameters
   REAL(wp) ::   ALP000 , ALP100 , ALP200 , ALP300 , ALP400 , ALP500
   REAL(wp) ::   ALP010 , ALP110 , ALP210 , ALP310 , ALP410
   REAL(wp) ::   ALP020 , ALP120 , ALP220 , ALP320
   REAL(wp) ::   ALP030 , ALP130 , ALP230
   REAL(wp) ::   ALP040 , ALP140
   REAL(wp) ::   ALP050
   REAL(wp) ::   ALP001 , ALP101 , ALP201 , ALP301
   REAL(wp) ::   ALP011 , ALP111 , ALP211
   REAL(wp) ::   ALP021 , ALP121
   REAL(wp) ::   ALP031
   REAL(wp) ::   ALP002 , ALP102
   REAL(wp) ::   ALP012
   REAL(wp) ::   ALP003

   ! BETA parameters
   REAL(wp) ::   BET000 , BET100 , BET200 , BET300 , BET400 , BET500
   REAL(wp) ::   BET010 , BET110 , BET210 , BET310 , BET410
   REAL(wp) ::   BET020 , BET120 , BET220 , BET320
   REAL(wp) ::   BET030 , BET130 , BET230
   REAL(wp) ::   BET040 , BET140
   REAL(wp) ::   BET050
   REAL(wp) ::   BET001 , BET101 , BET201 , BET301
   REAL(wp) ::   BET011 , BET111 , BET211
   REAL(wp) ::   BET021 , BET121
   REAL(wp) ::   BET031
   REAL(wp) ::   BET002 , BET102
   REAL(wp) ::   BET012
   REAL(wp) ::   BET003

   ! PEN parameters
   REAL(wp) ::   PEN000 , PEN100 , PEN200 , PEN300 , PEN400
   REAL(wp) ::   PEN010 , PEN110 , PEN210 , PEN310
   REAL(wp) ::   PEN020 , PEN120 , PEN220
   REAL(wp) ::   PEN030 , PEN130
   REAL(wp) ::   PEN040
   REAL(wp) ::   PEN001 , PEN101 , PEN201
   REAL(wp) ::   PEN011 , PEN111
   REAL(wp) ::   PEN021
   REAL(wp) ::   PEN002 , PEN102
   REAL(wp) ::   PEN012

   ! ALPHA_PEN parameters
   REAL(wp) ::   APE000 , APE100 , APE200 , APE300
   REAL(wp) ::   APE010 , APE110 , APE210
   REAL(wp) ::   APE020 , APE120
   REAL(wp) ::   APE030
   REAL(wp) ::   APE001 , APE101
   REAL(wp) ::   APE011
   REAL(wp) ::   APE002

   ! BETA_PEN parameters
   REAL(wp) ::   BPE000 , BPE100 , BPE200 , BPE300
   REAL(wp) ::   BPE010 , BPE110 , BPE210
   REAL(wp) ::   BPE020 , BPE120
   REAL(wp) ::   BPE030
   REAL(wp) ::   BPE001 , BPE101
   REAL(wp) ::   BPE011
   REAL(wp) ::   BPE002

   !! * Substitutions
#  include "do_loop_substitute.h90"
#  include "domzgr_substitute.h90"
   !!----------------------------------------------------------------------
   !! NEMO/OCE 4.0 , NEMO Consortium (2018)
   !! $Id: eosbn2.F90 15136 2021-07-23 10:07:28Z smasson $
   !! Software governed by the CeCILL license (see ./LICENSE)
   !!----------------------------------------------------------------------
CONTAINS

   SUBROUTINE eos_insitu( pts, prd, pdep )
      !!
      REAL(wp), DIMENSION(:,:,:,:), INTENT(in   ) ::   pts   ! 1 : potential temperature  [Celsius]
      !                                                      ! 2 : salinity               [psu]
      REAL(wp), DIMENSION(:,:,:)  , INTENT(  out) ::   prd   ! in situ density            [-]
      REAL(wp), DIMENSION(:,:,:)  , INTENT(in   ) ::   pdep  ! depth                      [m]
      !!
      CALL eos_insitu_t( pts, is_tile(pts), prd, is_tile(prd), pdep, is_tile(pdep) )
   END SUBROUTINE eos_insitu

   SUBROUTINE eos_insitu_t( pts, ktts, prd, ktrd, pdep, ktdep )
      !!----------------------------------------------------------------------
      !!                   ***  ROUTINE eos_insitu  ***
      !!
      !! ** Purpose :   Compute the in situ density (ratio rho/rho0) from
      !!       potential temperature and salinity using an equation of state
      !!       selected in the nameos namelist
      !!
      !! ** Method  :   prd(t,s,z) = ( rho(t,s,z) - rho0 ) / rho0
      !!         with   prd    in situ density anomaly      no units
      !!                t      TEOS10: CT or EOS80: PT      Celsius
      !!                s      TEOS10: SA or EOS80: SP      TEOS10: g/kg or EOS80: psu
      !!                z      depth                        meters
      !!                rho    in situ density              kg/m^3
      !!                rho0   reference density            kg/m^3
      !!
      !!     ln_teos10 : polynomial TEOS-10 equation of state is used for rho(t,s,z).
      !!         Check value: rho = 1028.21993233072 kg/m^3 for z=3000 dbar, ct=3 Celsius, sa=35.5 g/kg
      !!
      !!     ln_eos80 : polynomial EOS-80 equation of state is used for rho(t,s,z).
      !!         Check value: rho = 1028.35011066567 kg/m^3 for z=3000 dbar, pt=3 Celsius, sp=35.5 psu
      !!
      !!     ln_seos : simplified equation of state
      !!              prd(t,s,z) = ( -a0*(1+lambda/2*(T-T0)+mu*z+nu*(S-S0))*(T-T0) + b0*(S-S0) ) / rho0
      !!              linear case function of T only: rn_alpha<>0, other coefficients = 0
      !!              linear eos function of T and S: rn_alpha and rn_beta<>0, other coefficients=0
      !!              Vallis like equation: use default values of coefficients
      !!
      !! ** Action  :   compute prd , the in situ density (no units)
      !!
      !! References :   Roquet et al, Ocean Modelling, in preparation (2014)
      !!                Vallis, Atmospheric and Oceanic Fluid Dynamics, 2006
      !!                TEOS-10 Manual, 2010
      !!----------------------------------------------------------------------
      INTEGER                                 , INTENT(in   ) ::   ktts, ktrd, ktdep
      REAL(wp), DIMENSION(A2D_T(ktts) ,JPK,JPTS), INTENT(in   ) ::   pts   ! 1 : potential temperature  [Celsius]
      !                                                                  ! 2 : salinity               [psu]
      REAL(wp), DIMENSION(A2D_T(ktrd) ,JPK     ), INTENT(  out) ::   prd   ! in situ density            [-]
      REAL(wp), DIMENSION(A2D_T(ktdep),JPK     ), INTENT(in   ) ::   pdep  ! depth                      [m]
      !
      INTEGER  ::   ji, jj, jk                ! dummy loop indices
      REAL(wp) ::   zt , zh , zs , ztm        ! local scalars
      REAL(wp) ::   zn , zn0, zn1, zn2, zn3   !   -      -
      !!----------------------------------------------------------------------
      !
      IF( ln_timing )   CALL timing_start('eos-insitu')
      !
      SELECT CASE( neos )
      !
      CASE( np_teos10, np_eos80 )                !==  polynomial TEOS-10 / EOS-80 ==!
         !
         DO_3D( nn_hls, nn_hls, nn_hls, nn_hls, 1, jpkm1 )
            !
            zh  = pdep(ji,jj,jk) * r1_Z0                                  ! depth
            zt  = pts (ji,jj,jk,jp_tem) * r1_T0                           ! temperature
            zs  = SQRT( ABS( pts(ji,jj,jk,jp_sal) + rdeltaS ) * r1_S0 )   ! square root salinity
            ztm = tmask(ji,jj,jk)                                         ! tmask
            !
            zn3 = EOS013*zt   &
               &   + EOS103*zs+EOS003
               !
            zn2 = (EOS022*zt   &
               &   + EOS112*zs+EOS012)*zt   &
               &   + (EOS202*zs+EOS102)*zs+EOS002
               !
            zn1 = (((EOS041*zt   &
               &   + EOS131*zs+EOS031)*zt   &
               &   + (EOS221*zs+EOS121)*zs+EOS021)*zt   &
               &   + ((EOS311*zs+EOS211)*zs+EOS111)*zs+EOS011)*zt   &
               &   + (((EOS401*zs+EOS301)*zs+EOS201)*zs+EOS101)*zs+EOS001
               !
            zn0 = (((((EOS060*zt   &
               &   + EOS150*zs+EOS050)*zt   &
               &   + (EOS240*zs+EOS140)*zs+EOS040)*zt   &
               &   + ((EOS330*zs+EOS230)*zs+EOS130)*zs+EOS030)*zt   &
               &   + (((EOS420*zs+EOS320)*zs+EOS220)*zs+EOS120)*zs+EOS020)*zt   &
               &   + ((((EOS510*zs+EOS410)*zs+EOS310)*zs+EOS210)*zs+EOS110)*zs+EOS010)*zt   &
               &   + (((((EOS600*zs+EOS500)*zs+EOS400)*zs+EOS300)*zs+EOS200)*zs+EOS100)*zs+EOS000
               !
            zn  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0
            !
            prd(ji,jj,jk) = (  zn * r1_rho0 - 1._wp  ) * ztm  ! density anomaly (masked)
            !
         END_3D
         !
      CASE( np_seos )                !==  simplified EOS  ==!
         !
         DO_3D( nn_hls, nn_hls, nn_hls, nn_hls, 1, jpkm1 )
            zt  = pts  (ji,jj,jk,jp_tem) - 10._wp
            zs  = pts  (ji,jj,jk,jp_sal) - 35._wp
            zh  = pdep (ji,jj,jk)
            ztm = tmask(ji,jj,jk)
            !
            zn =  - rn_a0 * ( 1._wp + 0.5_wp*rn_lambda1*zt + rn_mu1*zh ) * zt   &
               &  + rn_b0 * ( 1._wp - 0.5_wp*rn_lambda2*zs - rn_mu2*zh ) * zs   &
               &  - rn_nu * zt * zs
               !
            prd(ji,jj,jk) = zn * r1_rho0 * ztm                ! density anomaly (masked)
         END_3D
         !
      END SELECT
      !
      IF(sn_cfctl%l_prtctl)   CALL prt_ctl( tab3d_1=prd, clinfo1=' eos-insitu  : ' )
Guillaume Samson's avatar
Guillaume Samson committed
      !
      IF( ln_timing )   CALL timing_stop('eos-insitu')
      !
   END SUBROUTINE eos_insitu_t


   SUBROUTINE eos_insitu_pot( pts, prd, prhop, pdep )
      !!
      REAL(wp), DIMENSION(:,:,:,:), INTENT(in   ) ::   pts    ! 1 : potential temperature  [Celsius]
      !                                                       ! 2 : salinity               [psu]
      REAL(wp), DIMENSION(:,:,:)  , INTENT(  out) ::   prd    ! in situ density            [-]
      REAL(wp), DIMENSION(:,:,:)  , INTENT(  out) ::   prhop  ! potential density (surface referenced)
      REAL(wp), DIMENSION(:,:,:)  , INTENT(in   ) ::   pdep   ! depth                      [m]
      !!
      CALL eos_insitu_pot_t( pts, is_tile(pts), prd, is_tile(prd), prhop, is_tile(prhop), pdep, is_tile(pdep) )
   END SUBROUTINE eos_insitu_pot


   SUBROUTINE eos_insitu_pot_t( pts, ktts, prd, ktrd, prhop, ktrhop, pdep, ktdep )
      !!----------------------------------------------------------------------
      !!                  ***  ROUTINE eos_insitu_pot  ***
      !!
      !! ** Purpose :   Compute the in situ density (ratio rho/rho0) and the
      !!      potential volumic mass (Kg/m3) from potential temperature and
      !!      salinity fields using an equation of state selected in the
      !!     namelist.
      !!
      !! ** Action  : - prd  , the in situ density (no units)
      !!              - prhop, the potential volumic mass (Kg/m3)
      !!
      !!----------------------------------------------------------------------
      INTEGER                                  , INTENT(in   ) ::   ktts, ktrd, ktrhop, ktdep
      REAL(wp), DIMENSION(A2D_T(ktts)  ,JPK,JPTS), INTENT(in   ) ::   pts    ! 1 : potential temperature  [Celsius]
      !                                                                    ! 2 : salinity               [psu]
      REAL(wp), DIMENSION(A2D_T(ktrd)  ,JPK     ), INTENT(  out) ::   prd    ! in situ density            [-]
      REAL(wp), DIMENSION(A2D_T(ktrhop),JPK     ), INTENT(  out) ::   prhop  ! potential density (surface referenced)
      REAL(wp), DIMENSION(A2D_T(ktdep) ,JPK     ), INTENT(in   ) ::   pdep   ! depth                      [m]
      !
      INTEGER  ::   ji, jj, jk, jsmp             ! dummy loop indices
      INTEGER  ::   jdof
      REAL(wp) ::   zt , zh , zstemp, zs , ztm   ! local scalars
      REAL(wp) ::   zn , zn0, zn1, zn2, zn3      !   -      -
      REAL(wp), DIMENSION(:), ALLOCATABLE :: zn0_sto, zn_sto, zsign    ! local vectors
      !!----------------------------------------------------------------------
      !
      IF( ln_timing )   CALL timing_start('eos-pot')
      !
      SELECT CASE ( neos )
      !
      CASE( np_teos10, np_eos80 )                !==  polynomial TEOS-10 / EOS-80 ==!
         !
         ! Stochastic equation of state
         IF ( ln_sto_eos ) THEN
            ALLOCATE(zn0_sto(1:2*nn_sto_eos))
            ALLOCATE(zn_sto(1:2*nn_sto_eos))
            ALLOCATE(zsign(1:2*nn_sto_eos))
            DO jsmp = 1, 2*nn_sto_eos, 2
              zsign(jsmp)   = 1._wp
              zsign(jsmp+1) = -1._wp
            END DO
            !
            DO_3D( nn_hls, nn_hls, nn_hls, nn_hls, 1, jpkm1 )
               !
               ! compute density (2*nn_sto_eos) times:
               ! (1) for t+dt, s+ds (with the random TS fluctutation computed in sto_pts)
               ! (2) for t-dt, s-ds (with the opposite fluctuation)
               DO jsmp = 1, nn_sto_eos*2
                  jdof   = (jsmp + 1) / 2
                  zh     = pdep(ji,jj,jk) * r1_Z0                                  ! depth
                  zt     = (pts (ji,jj,jk,jp_tem) + pts_ran(ji,jj,jk,jp_tem,jdof) * zsign(jsmp)) * r1_T0    ! temperature
                  zstemp = pts  (ji,jj,jk,jp_sal) + pts_ran(ji,jj,jk,jp_sal,jdof) * zsign(jsmp)
                  zs     = SQRT( ABS( zstemp + rdeltaS ) * r1_S0 )   ! square root salinity
                  ztm    = tmask(ji,jj,jk)                                         ! tmask
                  !
                  zn3 = EOS013*zt   &
                     &   + EOS103*zs+EOS003
                     !
                  zn2 = (EOS022*zt   &
                     &   + EOS112*zs+EOS012)*zt   &
                     &   + (EOS202*zs+EOS102)*zs+EOS002
                     !
                  zn1 = (((EOS041*zt   &
                     &   + EOS131*zs+EOS031)*zt   &
                     &   + (EOS221*zs+EOS121)*zs+EOS021)*zt   &
                     &   + ((EOS311*zs+EOS211)*zs+EOS111)*zs+EOS011)*zt   &
                     &   + (((EOS401*zs+EOS301)*zs+EOS201)*zs+EOS101)*zs+EOS001
                     !
                  zn0_sto(jsmp) = (((((EOS060*zt   &
                     &   + EOS150*zs+EOS050)*zt   &
                     &   + (EOS240*zs+EOS140)*zs+EOS040)*zt   &
                     &   + ((EOS330*zs+EOS230)*zs+EOS130)*zs+EOS030)*zt   &
                     &   + (((EOS420*zs+EOS320)*zs+EOS220)*zs+EOS120)*zs+EOS020)*zt   &
                     &   + ((((EOS510*zs+EOS410)*zs+EOS310)*zs+EOS210)*zs+EOS110)*zs+EOS010)*zt   &
                     &   + (((((EOS600*zs+EOS500)*zs+EOS400)*zs+EOS300)*zs+EOS200)*zs+EOS100)*zs+EOS000
                     !
                  zn_sto(jsmp)  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0_sto(jsmp)
               END DO
               !
               ! compute stochastic density as the mean of the (2*nn_sto_eos) densities
               prhop(ji,jj,jk) = 0._wp ; prd(ji,jj,jk) = 0._wp
               DO jsmp = 1, nn_sto_eos*2
                  prhop(ji,jj,jk) = prhop(ji,jj,jk) + zn0_sto(jsmp)                      ! potential density referenced at the surface
                  !
                  prd(ji,jj,jk) = prd(ji,jj,jk) + (  zn_sto(jsmp) * r1_rho0 - 1._wp  )   ! density anomaly (masked)
               END DO
               prhop(ji,jj,jk) = 0.5_wp * prhop(ji,jj,jk) * ztm / nn_sto_eos
               prd  (ji,jj,jk) = 0.5_wp * prd  (ji,jj,jk) * ztm / nn_sto_eos
            END_3D
            DEALLOCATE(zn0_sto,zn_sto,zsign)
         ! Non-stochastic equation of state
         ELSE
            DO_3D( nn_hls, nn_hls, nn_hls, nn_hls, 1, jpkm1 )
               !
               zh  = pdep(ji,jj,jk) * r1_Z0                                  ! depth
               zt  = pts (ji,jj,jk,jp_tem) * r1_T0                           ! temperature
               zs  = SQRT( ABS( pts(ji,jj,jk,jp_sal) + rdeltaS ) * r1_S0 )   ! square root salinity
               ztm = tmask(ji,jj,jk)                                         ! tmask
               !
               zn3 = EOS013*zt   &
                  &   + EOS103*zs+EOS003
                  !
               zn2 = (EOS022*zt   &
                  &   + EOS112*zs+EOS012)*zt   &
                  &   + (EOS202*zs+EOS102)*zs+EOS002
                  !
               zn1 = (((EOS041*zt   &
                  &   + EOS131*zs+EOS031)*zt   &
                  &   + (EOS221*zs+EOS121)*zs+EOS021)*zt   &
                  &   + ((EOS311*zs+EOS211)*zs+EOS111)*zs+EOS011)*zt   &
                  &   + (((EOS401*zs+EOS301)*zs+EOS201)*zs+EOS101)*zs+EOS001
                  !
               zn0 = (((((EOS060*zt   &
                  &   + EOS150*zs+EOS050)*zt   &
                  &   + (EOS240*zs+EOS140)*zs+EOS040)*zt   &
                  &   + ((EOS330*zs+EOS230)*zs+EOS130)*zs+EOS030)*zt   &
                  &   + (((EOS420*zs+EOS320)*zs+EOS220)*zs+EOS120)*zs+EOS020)*zt   &
                  &   + ((((EOS510*zs+EOS410)*zs+EOS310)*zs+EOS210)*zs+EOS110)*zs+EOS010)*zt   &
                  &   + (((((EOS600*zs+EOS500)*zs+EOS400)*zs+EOS300)*zs+EOS200)*zs+EOS100)*zs+EOS000
                  !
               zn  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0
               !
               prhop(ji,jj,jk) = zn0 * ztm                           ! potential density referenced at the surface
               !
               prd(ji,jj,jk) = (  zn * r1_rho0 - 1._wp  ) * ztm      ! density anomaly (masked)
            END_3D
         ENDIF

      CASE( np_seos )                !==  simplified EOS  ==!
         !
         DO_3D( nn_hls, nn_hls, nn_hls, nn_hls, 1, jpkm1 )
            zt  = pts  (ji,jj,jk,jp_tem) - 10._wp
            zs  = pts  (ji,jj,jk,jp_sal) - 35._wp
            zh  = pdep (ji,jj,jk)
            ztm = tmask(ji,jj,jk)
            !                                                     ! potential density referenced at the surface
            zn =  - rn_a0 * ( 1._wp + 0.5_wp*rn_lambda1*zt ) * zt   &
               &  + rn_b0 * ( 1._wp - 0.5_wp*rn_lambda2*zs ) * zs   &
               &  - rn_nu * zt * zs
            prhop(ji,jj,jk) = ( rho0 + zn ) * ztm
            !                                                     ! density anomaly (masked)
            zn = zn - ( rn_a0 * rn_mu1 * zt + rn_b0 * rn_mu2 * zs ) * zh
            prd(ji,jj,jk) = zn * r1_rho0 * ztm
            !
         END_3D
         !
      END SELECT
      !
      IF(sn_cfctl%l_prtctl)   CALL prt_ctl( tab3d_1=prd, clinfo1=' eos-pot: ', tab3d_2=prhop, clinfo2=' pot : ' )
Guillaume Samson's avatar
Guillaume Samson committed
      !
      IF( ln_timing )   CALL timing_stop('eos-pot')
      !
   END SUBROUTINE eos_insitu_pot_t


   SUBROUTINE eos_insitu_2d( pts, pdep, prd )
      !!
      REAL(wp), DIMENSION(:,:,:), INTENT(in   ) ::   pts   ! 1 : potential temperature  [Celsius]
      !                                                    ! 2 : salinity               [psu]
      REAL(wp), DIMENSION(:,:)  , INTENT(in   ) ::   pdep  ! depth                      [m]
      REAL(wp), DIMENSION(:,:)  , INTENT(  out) ::   prd   ! in situ density
      !!
      CALL eos_insitu_2d_t( pts, is_tile(pts), pdep, is_tile(pdep), prd, is_tile(prd) )
   END SUBROUTINE eos_insitu_2d


   SUBROUTINE eos_insitu_2d_t( pts, ktts, pdep, ktdep, prd, ktrd )
      !!----------------------------------------------------------------------
      !!                  ***  ROUTINE eos_insitu_2d  ***
      !!
      !! ** Purpose :   Compute the in situ density (ratio rho/rho0) from
      !!      potential temperature and salinity using an equation of state
      !!      selected in the nameos namelist. * 2D field case
      !!
      !! ** Action  : - prd , the in situ density (no units) (unmasked)
      !!
      !!----------------------------------------------------------------------
      INTEGER                            , INTENT(in   ) ::   ktts, ktdep, ktrd
      REAL(wp), DIMENSION(A2D_T(ktts),JPTS), INTENT(in   ) ::   pts   ! 1 : potential temperature  [Celsius]
      !                                                             ! 2 : salinity               [psu]
      REAL(wp), DIMENSION(A2D_T(ktdep)    ), INTENT(in   ) ::   pdep  ! depth                      [m]
      REAL(wp), DIMENSION(A2D_T(ktrd)     ), INTENT(  out) ::   prd   ! in situ density
      !
      INTEGER  ::   ji, jj, jk                ! dummy loop indices
      REAL(wp) ::   zt , zh , zs              ! local scalars
      REAL(wp) ::   zn , zn0, zn1, zn2, zn3   !   -      -
      !!----------------------------------------------------------------------
      !
      IF( ln_timing )   CALL timing_start('eos2d')
      !
      prd(:,:) = 0._wp
      !
      SELECT CASE( neos )
      !
      CASE( np_teos10, np_eos80 )                !==  polynomial TEOS-10 / EOS-80 ==!
         !
         DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
            !
            zh  = pdep(ji,jj) * r1_Z0                                  ! depth
            zt  = pts (ji,jj,jp_tem) * r1_T0                           ! temperature
            zs  = SQRT( ABS( pts(ji,jj,jp_sal) + rdeltaS ) * r1_S0 )   ! square root salinity
            !
            zn3 = EOS013*zt   &
               &   + EOS103*zs+EOS003
               !
            zn2 = (EOS022*zt   &
               &   + EOS112*zs+EOS012)*zt   &
               &   + (EOS202*zs+EOS102)*zs+EOS002
               !
            zn1 = (((EOS041*zt   &
               &   + EOS131*zs+EOS031)*zt   &
               &   + (EOS221*zs+EOS121)*zs+EOS021)*zt   &
               &   + ((EOS311*zs+EOS211)*zs+EOS111)*zs+EOS011)*zt   &
               &   + (((EOS401*zs+EOS301)*zs+EOS201)*zs+EOS101)*zs+EOS001
               !
            zn0 = (((((EOS060*zt   &
               &   + EOS150*zs+EOS050)*zt   &
               &   + (EOS240*zs+EOS140)*zs+EOS040)*zt   &
               &   + ((EOS330*zs+EOS230)*zs+EOS130)*zs+EOS030)*zt   &
               &   + (((EOS420*zs+EOS320)*zs+EOS220)*zs+EOS120)*zs+EOS020)*zt   &
               &   + ((((EOS510*zs+EOS410)*zs+EOS310)*zs+EOS210)*zs+EOS110)*zs+EOS010)*zt   &
               &   + (((((EOS600*zs+EOS500)*zs+EOS400)*zs+EOS300)*zs+EOS200)*zs+EOS100)*zs+EOS000
               !
            zn  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0
            !
            prd(ji,jj) = zn * r1_rho0 - 1._wp               ! unmasked in situ density anomaly
            !
         END_2D
         !
      CASE( np_seos )                !==  simplified EOS  ==!
         !
         DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
            !
            zt    = pts  (ji,jj,jp_tem)  - 10._wp
            zs    = pts  (ji,jj,jp_sal)  - 35._wp
            zh    = pdep (ji,jj)                         ! depth at the partial step level
            !
            zn =  - rn_a0 * ( 1._wp + 0.5_wp*rn_lambda1*zt + rn_mu1*zh ) * zt   &
               &  + rn_b0 * ( 1._wp - 0.5_wp*rn_lambda2*zs - rn_mu2*zh ) * zs   &
               &  - rn_nu * zt * zs
               !
            prd(ji,jj) = zn * r1_rho0               ! unmasked in situ density anomaly
            !
         END_2D
         !
      END SELECT
      !
      IF(sn_cfctl%l_prtctl)   CALL prt_ctl( tab2d_1=prd, clinfo1=' eos2d: ' )
      !
      IF( ln_timing )   CALL timing_stop('eos2d')
      !
   END SUBROUTINE eos_insitu_2d_t


   SUBROUTINE eos_insitu_pot_2d( pts, prhop )
      !!
      REAL(wp), DIMENSION(:,:,:), INTENT(in   ) ::   pts    ! 1 : potential temperature  [Celsius]
      !                                                     ! 2 : salinity               [psu]
      REAL(wp), DIMENSION(:,:)  , INTENT(  out) ::   prhop  ! potential density (surface referenced)
      !!
      CALL eos_insitu_pot_2d_t( pts, is_tile(pts), prhop, is_tile(prhop) )
   END SUBROUTINE eos_insitu_pot_2d


   SUBROUTINE eos_insitu_pot_2d_t( pts, ktts, prhop, ktrhop )
      !!----------------------------------------------------------------------
      !!                  ***  ROUTINE eos_insitu_pot  ***
      !!
      !! ** Purpose :   Compute the in situ density (ratio rho/rho0) and the
      !!      potential volumic mass (Kg/m3) from potential temperature and
      !!      salinity fields using an equation of state selected in the
      !!     namelist.
      !!
      !! ** Action  :
      !!              - prhop, the potential volumic mass (Kg/m3)
      !!
      !!----------------------------------------------------------------------
      INTEGER                              , INTENT(in   ) ::   ktts, ktrhop
      REAL(wp), DIMENSION(A2D_T(ktts),JPTS), INTENT(in   ) ::   pts    ! 1 : potential temperature  [Celsius]
      !                                                                ! 2 : salinity               [psu]
      REAL(wp), DIMENSION(A2D_T(ktrhop)   ), INTENT(  out) ::   prhop  ! potential density (surface referenced)
      !
      INTEGER  ::   ji, jj, jk, jsmp             ! dummy loop indices
      INTEGER  ::   jdof
      REAL(wp) ::   zt , zh , zstemp, zs , ztm   ! local scalars
      REAL(wp) ::   zn , zn0, zn1, zn2, zn3      !   -      -
      REAL(wp), DIMENSION(:), ALLOCATABLE :: zn0_sto, zn_sto, zsign    ! local vectors
      !!----------------------------------------------------------------------
      !
      IF( ln_timing )   CALL timing_start('eos-pot')
      !
      SELECT CASE ( neos )
      !
      CASE( np_teos10, np_eos80 )                !==  polynomial TEOS-10 / EOS-80 ==!
         !
         DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
            !
            zt  = pts (ji,jj,jp_tem) * r1_T0                           ! temperature
            zs  = SQRT( ABS( pts(ji,jj,jp_sal) + rdeltaS ) * r1_S0 )   ! square root salinity
            ztm = tmask(ji,jj,1)                                         ! tmask
            !
            zn0 = (((((EOS060*zt   &
               &   + EOS150*zs+EOS050)*zt   &
               &   + (EOS240*zs+EOS140)*zs+EOS040)*zt   &
               &   + ((EOS330*zs+EOS230)*zs+EOS130)*zs+EOS030)*zt   &
               &   + (((EOS420*zs+EOS320)*zs+EOS220)*zs+EOS120)*zs+EOS020)*zt   &
               &   + ((((EOS510*zs+EOS410)*zs+EOS310)*zs+EOS210)*zs+EOS110)*zs+EOS010)*zt   &
               &   + (((((EOS600*zs+EOS500)*zs+EOS400)*zs+EOS300)*zs+EOS200)*zs+EOS100)*zs+EOS000
               !
            !
            prhop(ji,jj) = zn0 * ztm                           ! potential density referenced at the surface
            !
         END_2D

      CASE( np_seos )                !==  simplified EOS  ==!
         !
         DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
            zt  = pts  (ji,jj,jp_tem) - 10._wp
            zs  = pts  (ji,jj,jp_sal) - 35._wp
            ztm = tmask(ji,jj,1)
            !                                                     ! potential density referenced at the surface
            zn =  - rn_a0 * ( 1._wp + 0.5_wp*rn_lambda1*zt ) * zt   &
               &  + rn_b0 * ( 1._wp - 0.5_wp*rn_lambda2*zs ) * zs   &
               &  - rn_nu * zt * zs
            prhop(ji,jj) = ( rho0 + zn ) * ztm
            !
         END_2D
         !
      END SELECT
      IF(sn_cfctl%l_prtctl)   CALL prt_ctl( tab2d_1=prhop, clinfo1=' pot: ', kdim=1 )
      !
      IF(sn_cfctl%l_prtctl)   CALL prt_ctl( tab2d_1=prhop, clinfo1=' eos-pot: ' )
      !
      IF( ln_timing )   CALL timing_stop('eos-pot')
      !
   END SUBROUTINE eos_insitu_pot_2d_t


   SUBROUTINE rab_3d( pts, pab, Kmm )
      !!
      INTEGER                     , INTENT(in   ) ::   Kmm   ! time level index
      REAL(wp), DIMENSION(:,:,:,:), INTENT(in   ) ::   pts   ! pot. temperature & salinity
      REAL(wp), DIMENSION(:,:,:,:), INTENT(  out) ::   pab   ! thermal/haline expansion ratio
      !!
      CALL rab_3d_t( pts, is_tile(pts), pab, is_tile(pab), Kmm )
   END SUBROUTINE rab_3d


   SUBROUTINE rab_3d_t( pts, ktts, pab, ktab, Kmm )
      !!----------------------------------------------------------------------
      !!                 ***  ROUTINE rab_3d  ***
      !!
      !! ** Purpose :   Calculates thermal/haline expansion ratio at T-points
      !!
      !! ** Method  :   calculates alpha / beta at T-points
      !!
      !! ** Action  : - pab     : thermal/haline expansion ratio at T-points
      !!----------------------------------------------------------------------
      INTEGER                                , INTENT(in   ) ::   Kmm   ! time level index
      INTEGER                                , INTENT(in   ) ::   ktts, ktab
      REAL(wp), DIMENSION(A2D_T(ktts),JPK,JPTS), INTENT(in   ) ::   pts   ! pot. temperature & salinity
      REAL(wp), DIMENSION(A2D_T(ktab),JPK,JPTS), INTENT(  out) ::   pab   ! thermal/haline expansion ratio
      !
      INTEGER  ::   ji, jj, jk                ! dummy loop indices
      REAL(wp) ::   zt , zh , zs , ztm        ! local scalars
      REAL(wp) ::   zn , zn0, zn1, zn2, zn3   !   -      -
      !!----------------------------------------------------------------------
      !
      IF( ln_timing )   CALL timing_start('rab_3d')
      !
      SELECT CASE ( neos )
      !
      CASE( np_teos10, np_eos80 )                !==  polynomial TEOS-10 / EOS-80 ==!
         !
         DO_3D( nn_hls, nn_hls, nn_hls, nn_hls, 1, jpkm1 )
            !
            zh  = gdept(ji,jj,jk,Kmm) * r1_Z0                                ! depth
            zt  = pts (ji,jj,jk,jp_tem) * r1_T0                           ! temperature
            zs  = SQRT( ABS( pts(ji,jj,jk,jp_sal) + rdeltaS ) * r1_S0 )   ! square root salinity
            ztm = tmask(ji,jj,jk)                                         ! tmask
            !
            ! alpha
            zn3 = ALP003
            !
            zn2 = ALP012*zt + ALP102*zs+ALP002
            !
            zn1 = ((ALP031*zt   &
               &   + ALP121*zs+ALP021)*zt   &
               &   + (ALP211*zs+ALP111)*zs+ALP011)*zt   &
               &   + ((ALP301*zs+ALP201)*zs+ALP101)*zs+ALP001
               !
            zn0 = ((((ALP050*zt   &
               &   + ALP140*zs+ALP040)*zt   &
               &   + (ALP230*zs+ALP130)*zs+ALP030)*zt   &
               &   + ((ALP320*zs+ALP220)*zs+ALP120)*zs+ALP020)*zt   &
               &   + (((ALP410*zs+ALP310)*zs+ALP210)*zs+ALP110)*zs+ALP010)*zt   &
               &   + ((((ALP500*zs+ALP400)*zs+ALP300)*zs+ALP200)*zs+ALP100)*zs+ALP000
               !
            zn  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0
            !
            pab(ji,jj,jk,jp_tem) = zn * r1_rho0 * ztm
            !
            ! beta
            zn3 = BET003
            !
            zn2 = BET012*zt + BET102*zs+BET002
            !
            zn1 = ((BET031*zt   &
               &   + BET121*zs+BET021)*zt   &
               &   + (BET211*zs+BET111)*zs+BET011)*zt   &
               &   + ((BET301*zs+BET201)*zs+BET101)*zs+BET001
               !
            zn0 = ((((BET050*zt   &
               &   + BET140*zs+BET040)*zt   &
               &   + (BET230*zs+BET130)*zs+BET030)*zt   &
               &   + ((BET320*zs+BET220)*zs+BET120)*zs+BET020)*zt   &
               &   + (((BET410*zs+BET310)*zs+BET210)*zs+BET110)*zs+BET010)*zt   &
               &   + ((((BET500*zs+BET400)*zs+BET300)*zs+BET200)*zs+BET100)*zs+BET000
               !
            zn  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0
            !
            pab(ji,jj,jk,jp_sal) = zn / zs * r1_rho0 * ztm
            !
         END_3D
         !
      CASE( np_seos )                  !==  simplified EOS  ==!
         !
         DO_3D( nn_hls, nn_hls, nn_hls, nn_hls, 1, jpkm1 )
            zt  = pts (ji,jj,jk,jp_tem) - 10._wp   ! pot. temperature anomaly (t-T0)
            zs  = pts (ji,jj,jk,jp_sal) - 35._wp   ! abs. salinity anomaly (s-S0)
            zh  = gdept(ji,jj,jk,Kmm)                ! depth in meters at t-point
            ztm = tmask(ji,jj,jk)                  ! land/sea bottom mask = surf. mask
            !
            zn  = rn_a0 * ( 1._wp + rn_lambda1*zt + rn_mu1*zh ) + rn_nu*zs
            pab(ji,jj,jk,jp_tem) = zn * r1_rho0 * ztm   ! alpha
            !
            zn  = rn_b0 * ( 1._wp - rn_lambda2*zs - rn_mu2*zh ) - rn_nu*zt
            pab(ji,jj,jk,jp_sal) = zn * r1_rho0 * ztm   ! beta
            !
         END_3D
         !
      CASE DEFAULT
         WRITE(ctmp1,*) '          bad flag value for neos = ', neos
         CALL ctl_stop( 'rab_3d:', ctmp1 )
         !
      END SELECT
      !
      IF(sn_cfctl%l_prtctl)   CALL prt_ctl( tab3d_1=pab(:,:,:,jp_tem), clinfo1=' rab_3d_t: ', &
         &                                  tab3d_2=pab(:,:,:,jp_sal), clinfo2=' rab_3d_s : ' )
Guillaume Samson's avatar
Guillaume Samson committed
      !
      IF( ln_timing )   CALL timing_stop('rab_3d')
      !
   END SUBROUTINE rab_3d_t


   SUBROUTINE rab_2d( pts, pdep, pab, Kmm )
      !!
      INTEGER                   , INTENT(in   ) ::   Kmm   ! time level index
      REAL(wp), DIMENSION(:,:,:), INTENT(in   ) ::   pts    ! pot. temperature & salinity
      REAL(wp), DIMENSION(:,:)  , INTENT(in   ) ::   pdep   ! depth                  [m]
      REAL(wp), DIMENSION(:,:,:), INTENT(  out) ::   pab    ! thermal/haline expansion ratio
      !!
      CALL rab_2d_t(pts, is_tile(pts), pdep, is_tile(pdep), pab, is_tile(pab), Kmm)
   END SUBROUTINE rab_2d


   SUBROUTINE rab_2d_t( pts, ktts, pdep, ktdep, pab, ktab, Kmm )
      !!----------------------------------------------------------------------
      !!                 ***  ROUTINE rab_2d  ***
      !!
      !! ** Purpose :   Calculates thermal/haline expansion ratio for a 2d field (unmasked)
      !!
      !! ** Action  : - pab     : thermal/haline expansion ratio at T-points
      !!----------------------------------------------------------------------
      INTEGER                            , INTENT(in   ) ::   Kmm   ! time level index
      INTEGER                            , INTENT(in   ) ::   ktts, ktdep, ktab
      REAL(wp), DIMENSION(A2D_T(ktts),JPTS), INTENT(in   ) ::   pts    ! pot. temperature & salinity
      REAL(wp), DIMENSION(A2D_T(ktdep)    ), INTENT(in   ) ::   pdep   ! depth                  [m]
      REAL(wp), DIMENSION(A2D_T(ktab),JPTS), INTENT(  out) ::   pab    ! thermal/haline expansion ratio
      !
      INTEGER  ::   ji, jj, jk                ! dummy loop indices
      REAL(wp) ::   zt , zh , zs              ! local scalars
      REAL(wp) ::   zn , zn0, zn1, zn2, zn3   !   -      -
      !!----------------------------------------------------------------------
      !
      IF( ln_timing )   CALL timing_start('rab_2d')
      !
      pab(:,:,:) = 0._wp
      !
      SELECT CASE ( neos )
      !
      CASE( np_teos10, np_eos80 )                !==  polynomial TEOS-10 / EOS-80 ==!
         !
         DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
            !
            zh  = pdep(ji,jj) * r1_Z0                                  ! depth
            zt  = pts (ji,jj,jp_tem) * r1_T0                           ! temperature
            zs  = SQRT( ABS( pts(ji,jj,jp_sal) + rdeltaS ) * r1_S0 )   ! square root salinity
            !
            ! alpha
            zn3 = ALP003
            !
            zn2 = ALP012*zt + ALP102*zs+ALP002
            !
            zn1 = ((ALP031*zt   &
               &   + ALP121*zs+ALP021)*zt   &
               &   + (ALP211*zs+ALP111)*zs+ALP011)*zt   &
               &   + ((ALP301*zs+ALP201)*zs+ALP101)*zs+ALP001
               !
            zn0 = ((((ALP050*zt   &
               &   + ALP140*zs+ALP040)*zt   &
               &   + (ALP230*zs+ALP130)*zs+ALP030)*zt   &
               &   + ((ALP320*zs+ALP220)*zs+ALP120)*zs+ALP020)*zt   &
               &   + (((ALP410*zs+ALP310)*zs+ALP210)*zs+ALP110)*zs+ALP010)*zt   &
               &   + ((((ALP500*zs+ALP400)*zs+ALP300)*zs+ALP200)*zs+ALP100)*zs+ALP000
               !
            zn  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0
            !
            pab(ji,jj,jp_tem) = zn * r1_rho0
            !
            ! beta
            zn3 = BET003
            !
            zn2 = BET012*zt + BET102*zs+BET002
            !
            zn1 = ((BET031*zt   &
               &   + BET121*zs+BET021)*zt   &
               &   + (BET211*zs+BET111)*zs+BET011)*zt   &
               &   + ((BET301*zs+BET201)*zs+BET101)*zs+BET001
               !
            zn0 = ((((BET050*zt   &
               &   + BET140*zs+BET040)*zt   &
               &   + (BET230*zs+BET130)*zs+BET030)*zt   &
               &   + ((BET320*zs+BET220)*zs+BET120)*zs+BET020)*zt   &
               &   + (((BET410*zs+BET310)*zs+BET210)*zs+BET110)*zs+BET010)*zt   &
               &   + ((((BET500*zs+BET400)*zs+BET300)*zs+BET200)*zs+BET100)*zs+BET000
               !
            zn  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0
            !
            pab(ji,jj,jp_sal) = zn / zs * r1_rho0
            !
            !
         END_2D
         !
      CASE( np_seos )                  !==  simplified EOS  ==!
         !
         DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
            !
            zt    = pts  (ji,jj,jp_tem) - 10._wp   ! pot. temperature anomaly (t-T0)
            zs    = pts  (ji,jj,jp_sal) - 35._wp   ! abs. salinity anomaly (s-S0)
            zh    = pdep (ji,jj)                   ! depth at the partial step level
            !
            zn  = rn_a0 * ( 1._wp + rn_lambda1*zt + rn_mu1*zh ) + rn_nu*zs
            pab(ji,jj,jp_tem) = zn * r1_rho0   ! alpha
            !
            zn  = rn_b0 * ( 1._wp - rn_lambda2*zs - rn_mu2*zh ) - rn_nu*zt
            pab(ji,jj,jp_sal) = zn * r1_rho0   ! beta
            !
         END_2D
         !
      CASE DEFAULT
         WRITE(ctmp1,*) '          bad flag value for neos = ', neos
         CALL ctl_stop( 'rab_2d:', ctmp1 )
         !
      END SELECT
      !
      IF(sn_cfctl%l_prtctl)   CALL prt_ctl( tab2d_1=pab(:,:,jp_tem), clinfo1=' rab_2d_t: ', &
         &                                  tab2d_2=pab(:,:,jp_sal), clinfo2=' rab_2d_s : ' )
      !
      IF( ln_timing )   CALL timing_stop('rab_2d')
      !
   END SUBROUTINE rab_2d_t


   SUBROUTINE rab_0d( pts, pdep, pab, Kmm )
      !!----------------------------------------------------------------------
      !!                 ***  ROUTINE rab_0d  ***
      !!
      !! ** Purpose :   Calculates thermal/haline expansion ratio for a 2d field (unmasked)
      !!
      !! ** Action  : - pab     : thermal/haline expansion ratio at T-points
      !!----------------------------------------------------------------------
      INTEGER                              , INTENT(in   ) ::   Kmm   ! time level index
      REAL(wp), DIMENSION(jpts)    , INTENT(in   ) ::   pts    ! pot. temperature & salinity
      REAL(wp),                      INTENT(in   ) ::   pdep   ! depth                  [m]
      REAL(wp), DIMENSION(jpts)    , INTENT(  out) ::   pab    ! thermal/haline expansion ratio
      !
      REAL(wp) ::   zt , zh , zs              ! local scalars
      REAL(wp) ::   zn , zn0, zn1, zn2, zn3   !   -      -
      !!----------------------------------------------------------------------
      !
      IF( ln_timing )   CALL timing_start('rab_0d')
      !
      pab(:) = 0._wp
      !
      SELECT CASE ( neos )
      !
      CASE( np_teos10, np_eos80 )      !==  polynomial TEOS-10 / EOS-80 ==!
         !
         !
         zh  = pdep * r1_Z0                                  ! depth
         zt  = pts (jp_tem) * r1_T0                           ! temperature
         zs  = SQRT( ABS( pts(jp_sal) + rdeltaS ) * r1_S0 )   ! square root salinity
         !
         ! alpha
         zn3 = ALP003
         !
         zn2 = ALP012*zt + ALP102*zs+ALP002
         !
         zn1 = ((ALP031*zt   &
            &   + ALP121*zs+ALP021)*zt   &
            &   + (ALP211*zs+ALP111)*zs+ALP011)*zt   &
            &   + ((ALP301*zs+ALP201)*zs+ALP101)*zs+ALP001
            !
         zn0 = ((((ALP050*zt   &
            &   + ALP140*zs+ALP040)*zt   &
            &   + (ALP230*zs+ALP130)*zs+ALP030)*zt   &
            &   + ((ALP320*zs+ALP220)*zs+ALP120)*zs+ALP020)*zt   &
            &   + (((ALP410*zs+ALP310)*zs+ALP210)*zs+ALP110)*zs+ALP010)*zt   &
            &   + ((((ALP500*zs+ALP400)*zs+ALP300)*zs+ALP200)*zs+ALP100)*zs+ALP000
            !
         zn  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0
         !
         pab(jp_tem) = zn * r1_rho0
         !
         ! beta
         zn3 = BET003
         !
         zn2 = BET012*zt + BET102*zs+BET002
         !
         zn1 = ((BET031*zt   &
            &   + BET121*zs+BET021)*zt   &
            &   + (BET211*zs+BET111)*zs+BET011)*zt   &
            &   + ((BET301*zs+BET201)*zs+BET101)*zs+BET001
            !
         zn0 = ((((BET050*zt   &
            &   + BET140*zs+BET040)*zt   &
            &   + (BET230*zs+BET130)*zs+BET030)*zt   &
            &   + ((BET320*zs+BET220)*zs+BET120)*zs+BET020)*zt   &
            &   + (((BET410*zs+BET310)*zs+BET210)*zs+BET110)*zs+BET010)*zt   &
            &   + ((((BET500*zs+BET400)*zs+BET300)*zs+BET200)*zs+BET100)*zs+BET000
            !
         zn  = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0
         !
         pab(jp_sal) = zn / zs * r1_rho0
         !
         !
         !
      CASE( np_seos )                  !==  simplified EOS  ==!
         !
         zt    = pts(jp_tem) - 10._wp   ! pot. temperature anomaly (t-T0)
         zs    = pts(jp_sal) - 35._wp   ! abs. salinity anomaly (s-S0)
         zh    = pdep                   ! depth at the partial step level
         !
         zn  = rn_a0 * ( 1._wp + rn_lambda1*zt + rn_mu1*zh ) + rn_nu*zs
         pab(jp_tem) = zn * r1_rho0   ! alpha
         !
         zn  = rn_b0 * ( 1._wp - rn_lambda2*zs - rn_mu2*zh ) - rn_nu*zt
         pab(jp_sal) = zn * r1_rho0   ! beta
         !
      CASE DEFAULT
         WRITE(ctmp1,*) '          bad flag value for neos = ', neos
         CALL ctl_stop( 'rab_0d:', ctmp1 )
         !
      END SELECT
      !
      IF( ln_timing )   CALL timing_stop('rab_0d')
      !
   END SUBROUTINE rab_0d


   SUBROUTINE bn2( pts, pab, pn2, Kmm )
      !!
      INTEGER                              , INTENT(in   ) ::  Kmm   ! time level index
      REAL(wp), DIMENSION(jpi,jpj,jpk,jpts), INTENT(in   ) ::  pts   ! pot. temperature and salinity   [Celsius,psu]
      REAL(wp), DIMENSION(:,:,:,:)         , INTENT(in   ) ::  pab   ! thermal/haline expansion coef.  [Celsius-1,psu-1]
      REAL(wp), DIMENSION(:,:,:)           , INTENT(  out) ::  pn2   ! Brunt-Vaisala frequency squared [1/s^2]
      !!
      CALL bn2_t( pts, pab, is_tile(pab), pn2, is_tile(pn2), Kmm )
   END SUBROUTINE bn2