New URL for NEMO forge!   http://forge.nemo-ocean.eu

Since March 2022 along with NEMO 4.2 release, the code development moved to a self-hosted GitLab.
This present forge is now archived and remained online for history.
sbcblk_algo_ncar.F90 in NEMO/branches/2021/dev_r14116_HPC-10_mcastril_Mixed_Precision_implementation/src/OCE/SBC – NEMO

source: NEMO/branches/2021/dev_r14116_HPC-10_mcastril_Mixed_Precision_implementation/src/OCE/SBC/sbcblk_algo_ncar.F90 @ 15540

Last change on this file since 15540 was 15540, checked in by sparonuz, 3 years ago

Mixed precision version, tested up to 30 years on ORCA2.

  • Property svn:keywords set to Id
File size: 17.7 KB
Line 
1MODULE sbcblk_algo_ncar
2   !!======================================================================
3   !!                   ***  MODULE  sbcblk_algo_ncar  ***
4   !! Computes:
5   !!   * bulk transfer coefficients C_D, C_E and C_H
6   !!   * air temp. and spec. hum. adjusted from zt (2m) to zu (10m) if needed
7   !!   * the effective bulk wind speed at 10m Ubzu
8   !!   => all these are used in bulk formulas in sbcblk.F90
9   !!
10   !!    Using the bulk formulation/param. of Large & Yeager 2008
11   !!
12   !!       Routine turb_ncar maintained and developed in AeroBulk
13   !!                     (https://github.com/brodeau/aerobulk/)
14   !!
15   !!                         L. Brodeau, 2015
16   !!=====================================================================
17   !! History :  3.6  !  2016-02  (L.Brodeau) successor of old turb_ncar of former sbcblk_core.F90
18   !!            4.2  !  2020-12  (L. Brodeau) Introduction of various air-ice bulk parameterizations + improvements
19   !!----------------------------------------------------------------------
20
21   !!----------------------------------------------------------------------
22   !!   turb_ncar  : computes the bulk turbulent transfer coefficients
23   !!                   adjusts t_air and q_air from zt to zu m
24   !!                   returns the effective bulk wind speed at 10m
25   !!----------------------------------------------------------------------
26   USE dom_oce         ! ocean space and time domain
27   USE sbc_oce, ONLY: ln_cdgw
28   USE sbcwave, ONLY: cdn_wave ! wave module
29   USE phycst          ! physical constants
30   USE sbc_phy         ! Catalog of functions for physical/meteorological parameters in the marine boundary layer
31
32   IMPLICIT NONE
33   PRIVATE
34
35   PUBLIC :: TURB_NCAR   ! called by sbcblk.F90
36
37   !! * Substitutions
38#  include "single_precision_substitute.h90"
39#  include "do_loop_substitute.h90"
40
41   !!----------------------------------------------------------------------
42CONTAINS
43
44   SUBROUTINE turb_ncar(    zt, zu, sst, t_zt, ssq, q_zt, U_zu, &
45      &                     Cd, Ch, Ce, t_zu, q_zu, Ubzu,       &
46      &                     nb_iter, CdN, ChN, CeN               )
47      !!----------------------------------------------------------------------------------
48      !!                      ***  ROUTINE  turb_ncar  ***
49      !!
50      !! ** Purpose :   Computes turbulent transfert coefficients of surface
51      !!                fluxes according to Large & Yeager (2004) and Large & Yeager (2008)
52      !!                If relevant (zt /= zu), adjust temperature and humidity from height zt to zu
53      !!                Returns the effective bulk wind speed at zu to be used in the bulk formulas
54      !!
55      !! INPUT :
56      !! -------
57      !!    *  zt   : height for temperature and spec. hum. of air            [m]
58      !!    *  zu   : height for wind speed (usually 10m)                     [m]
59      !!    *  sst  : bulk SST                                                [K]
60      !!    *  t_zt : potential air temperature at zt                         [K]
61      !!    *  ssq  : specific humidity at saturation at SST                  [kg/kg]
62      !!    *  q_zt : specific humidity of air at zt                          [kg/kg]
63      !!    *  U_zu : scalar wind speed at zu                                 [m/s]
64      !!
65      !!
66      !! OUTPUT :
67      !! --------
68      !!    *  Cd     : drag coefficient
69      !!    *  Ch     : sensible heat coefficient
70      !!    *  Ce     : evaporation coefficient
71      !!    *  t_zu   : pot. air temperature adjusted at wind height zu       [K]
72      !!    *  q_zu   : specific humidity of air        //                    [kg/kg]
73      !!    *  Ubzu   : bulk wind speed at zu                                 [m/s]
74      !!
75      !! OPTIONAL OUTPUT:
76      !! ----------------
77      !!    * CdN      : neutral-stability drag coefficient
78      !!    * ChN      : neutral-stability sensible heat coefficient
79      !!    * CeN      : neutral-stability evaporation coefficient
80      !!
81      !! ** Author: L. Brodeau, June 2019 / AeroBulk (https://github.com/brodeau/aerobulk/)
82      !!----------------------------------------------------------------------------------
83      REAL(wp), INTENT(in   )                     ::   zt       ! height for t_zt and q_zt                    [m]
84      REAL(wp), INTENT(in   )                     ::   zu       ! height for U_zu                             [m]
85      REAL(dp), INTENT(in   ), DIMENSION(jpi,jpj) ::   sst      ! sea surface temperature                [Kelvin]
86      REAL(wp), INTENT(in   ), DIMENSION(jpi,jpj) ::   t_zt     ! potential air temperature              [Kelvin]
87      REAL(dp), INTENT(in   ), DIMENSION(jpi,jpj) ::   ssq      ! sea surface specific humidity           [kg/kg]
88      REAL(wp), INTENT(in   ), DIMENSION(jpi,jpj) ::   q_zt     ! specific air humidity at zt             [kg/kg]
89      REAL(dp), INTENT(in   ), DIMENSION(jpi,jpj) ::   U_zu     ! relative wind module at zu                [m/s]
90      REAL(dp), INTENT(  out), DIMENSION(jpi,jpj) ::   Cd       ! transfer coefficient for momentum         (tau)
91      REAL(dp), INTENT(  out), DIMENSION(jpi,jpj) ::   Ch       ! transfer coefficient for sensible heat (Q_sens)
92      REAL(dp), INTENT(  out), DIMENSION(jpi,jpj) ::   Ce       ! transfert coefficient for evaporation   (Q_lat)
93      REAL(dp), INTENT(  out), DIMENSION(jpi,jpj) ::   t_zu     ! pot. air temp. adjusted at zu               [K]
94      REAL(dp), INTENT(  out), DIMENSION(jpi,jpj) ::   q_zu     ! spec. humidity adjusted at zu           [kg/kg]
95      REAL(dp), INTENT(  out), DIMENSION(jpi,jpj) ::   Ubzu    ! bulk wind speed at zu                     [m/s]
96      !
97      INTEGER , INTENT(in   ), OPTIONAL                     :: nb_iter  ! number of iterations
98      REAL(dp), INTENT(  out), OPTIONAL, DIMENSION(jpi,jpj) ::   CdN
99      REAL(dp), INTENT(  out), OPTIONAL, DIMENSION(jpi,jpj) ::   ChN
100      REAL(dp), INTENT(  out), OPTIONAL, DIMENSION(jpi,jpj) ::   CeN
101      !
102      INTEGER :: nbit, jit                    ! iterations...
103      LOGICAL :: l_zt_equal_zu = .FALSE.      ! if q and t are given at same height as U
104      !
105      REAL(wp), DIMENSION(jpi,jpj) ::   zCdN, zCeN, zChN        ! 10m neutral latent/sensible coefficient
106      REAL(wp), DIMENSION(jpi,jpj) ::   zsqrt_Cd, zsqrt_CdN   ! root square of Cd and Cd_neutral
107      REAL(wp), DIMENSION(jpi,jpj) ::   zeta_u        ! stability parameter at height zu
108      REAL(wp), DIMENSION(jpi,jpj) ::   ztmp0, ztmp1, ztmp2
109      !!----------------------------------------------------------------------------------
110      nbit = nb_iter0
111      IF( PRESENT(nb_iter) ) nbit = nb_iter
112
113      l_zt_equal_zu = ( ABS(zu - zt) < 0.01_wp ) ! testing "zu == zt" is risky with double precision
114
115      Ubzu = MAX( 0.5_wp , U_zu )   !  relative wind speed at zu (normally 10m), we don't want to fall under 0.5 m/s
116
117      !! First guess of stability:
118      ztmp0 =virt_temp(t_zt, q_zt) - virt_temp(CASTSP(sst), CASTSP(ssq)) ! air-sea difference of virtual pot. temp. at zt
119      ztmp1 = 0.5_wp + SIGN(0.5_wp,ztmp0)                 ! ztmp1 = 1 if dTv > 0  => STABLE, 0 if unstable
120
121      !! Neutral coefficients at 10m:
122      IF( ln_cdgw ) THEN      ! wave drag case
123         cdn_wave(:,:) = cdn_wave(:,:) + rsmall * ( 1._wp - tmask(:,:,1) )
124         zCdN   (:,:) = cdn_wave(:,:)
125      ELSE
126      zCdN =cd_n10_ncar( CASTSP(Ubzu) )
127      ENDIF
128
129      zsqrt_CdN = SQRT( zCdN )
130
131      !! Initializing transf. coeff. with their first guess neutral equivalents :
132      Cd = zCdN
133      Ce = ce_n10_ncar( zsqrt_CdN )
134      Ch = ch_n10_ncar( zsqrt_CdN , ztmp1 )   ! ztmp1 is stability (1/0)
135      zsqrt_Cd = zsqrt_CdN
136
137      IF( ln_cdgw ) THEN
138         zCeN = Ce
139         zChN = Ch
140      ENDIF
141
142      !! Initializing values at z_u with z_t values:
143      t_zu = MAX( t_zt ,  180._wp )   ! who knows what's given on masked-continental regions...
144      q_zu = MAX( q_zt , 1.e-6_wp )   !               "
145
146
147      !! ITERATION BLOCK
148      DO jit = 1, nbit
149         !
150         ztmp1 = t_zu - sst   ! Updating air/sea differences
151         ztmp2 = q_zu - ssq
152
153         ! Updating turbulent scales :   (L&Y 2004 Eq. (7))
154         ztmp0 = zsqrt_Cd*Ubzu       ! u*
155         ztmp1 = Ch/zsqrt_Cd*ztmp1    ! theta*
156         ztmp2 = Ce/zsqrt_Cd*ztmp2    ! q*
157
158         ! Estimate the inverse of Obukov length (1/L) at height zu:
159         ztmp0 =One_on_L( t_zu, q_zu, CASTDP(ztmp0), CASTDP(ztmp1), CASTDP(ztmp2) )
160
161         !! Stability parameters :
162         zeta_u   = zu*ztmp0
163         zeta_u   = sign( min(abs(zeta_u),10._wp), zeta_u )
164
165         !! Shifting temperature and humidity at zu (L&Y 2004 Eq. (9b-9c))
166         IF( .NOT. l_zt_equal_zu ) THEN
167            ztmp0 = zt*ztmp0 ! zeta_t !
168            ztmp0 = SIGN( MIN(ABS(ztmp0),10._wp), ztmp0 )  ! Temporaty array ztmp0 == zeta_t !!!
169            ztmp0 = LOG(zt/zu) + psi_h_ncar(zeta_u) - psi_h_ncar(ztmp0)                   ! ztmp0 just used as temp array again!
170            t_zu = t_zt - ztmp1/vkarmn*ztmp0    ! ztmp1 is still theta*  L&Y 2004 Eq. (9b)
171            !!
172            q_zu = q_zt - ztmp2/vkarmn*ztmp0    ! ztmp2 is still q*      L&Y 2004 Eq. (9c)
173            q_zu = MAX(0._wp, q_zu)
174         END IF
175
176         ! Update neutral wind speed at 10m and neutral Cd at 10m (L&Y 2004 Eq. 9a)...
177         !   In very rare low-wind conditions, the old way of estimating the
178         !   neutral wind speed at 10m leads to a negative value that causes the code
179         !   to crash. To prevent this a threshold of 0.25m/s is imposed.
180         ztmp2 = psi_m_ncar(zeta_u)
181         IF( ln_cdgw ) THEN      ! surface wave case
182            zsqrt_Cd = vkarmn / ( vkarmn / zsqrt_CdN - ztmp2 )
183            Cd   = zsqrt_Cd * zsqrt_Cd
184            ztmp0 = (LOG(zu/10._wp) - psi_h_ncar(zeta_u)) / vkarmn / zsqrt_CdN
185            ztmp2 = zsqrt_Cd / zsqrt_CdN
186            ztmp1 = 1._wp + zChN * ztmp0
187            Ch    = zChN * ztmp2 / ztmp1  ! L&Y 2004 eq. (10b)
188            ztmp1 = 1._wp + zCeN * ztmp0
189            Ce    = zCeN * ztmp2 / ztmp1  ! L&Y 2004 eq. (10c)
190
191         ELSE
192         ztmp0 = MAX( 0.25_wp , UN10_from_CD(zu, Ubzu, Cd, ppsi=ztmp2) ) ! U_n10 (ztmp2 == psi_m_ncar(zeta_u))
193
194         zCdN = cd_n10_ncar(ztmp0)
195         zsqrt_CdN = sqrt(zCdN)
196
197         !! Update of transfer coefficients:
198
199         !! C_D
200         ztmp1  = 1._wp + zsqrt_CdN/vkarmn*(LOG(zu/10._wp) - ztmp2)   ! L&Y 2004 Eq. (10a) (ztmp2 == psi_m(zeta_u))
201         Cd     = MAX( zCdN / ( ztmp1*ztmp1 ), Cx_min )
202
203         !! C_H and C_E
204         zsqrt_Cd = SQRT( Cd )
205         ztmp0 = ( LOG(zu/10._wp) - psi_h_ncar(zeta_u) ) / vkarmn / zsqrt_CdN
206         ztmp2 = zsqrt_Cd / zsqrt_CdN
207
208         ztmp1 = 0.5_wp + SIGN(0.5_wp,zeta_u)                                ! update stability
209         zChN  = 1.e-3_wp * zsqrt_CdN*(18._wp*ztmp1 + 32.7_wp*(1._wp - ztmp1))  ! L&Y 2004 eq. (6c-6d)
210         zCeN  = 1.e-3_wp * (34.6_wp * zsqrt_CdN)                             ! L&Y 2004 eq. (6b)
211
212         Ch    = MAX( zChN*ztmp2 / ( 1._wp + zChN*ztmp0 ) , Cx_min ) ! L&Y 2004 eq. (10b)
213         Ce    = MAX( zCeN*ztmp2 / ( 1._wp + zCeN*ztmp0 ) , Cx_min ) ! L&Y 2004 eq. (10c)
214
215         ENDIF
216
217      END DO !DO jit = 1, nbit
218
219      IF(PRESENT(CdN)) CdN(:,:) = zCdN(:,:)
220      IF(PRESENT(CeN)) CeN(:,:) = zCeN(:,:)
221      IF(PRESENT(ChN)) ChN(:,:) = zChN(:,:)
222
223   END SUBROUTINE turb_ncar
224
225
226   FUNCTION cd_n10_ncar( pw10 )
227      !!----------------------------------------------------------------------------------
228      !! Estimate of the neutral drag coefficient at 10m as a function
229      !! of neutral wind  speed at 10m
230      !!
231      !! Origin: Large & Yeager 2008, Eq. (11)
232      !!
233      !! ** Author: L. Brodeau, june 2016 / AeroBulk (https://github.com/brodeau/aerobulk/)
234      !!----------------------------------------------------------------------------------
235      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pw10           ! scalar wind speed at 10m (m/s)
236      REAL(wp), DIMENSION(jpi,jpj)             :: cd_n10_ncar
237      !
238      INTEGER  ::     ji, jj     ! dummy loop indices
239      REAL(wp) :: zgt33, zw, zw6 ! local scalars
240      !!----------------------------------------------------------------------------------
241      !
242      DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
243            !
244            zw  = pw10(ji,jj)
245            zw6 = zw*zw*zw
246            zw6 = zw6*zw6
247            !
248            ! When wind speed > 33 m/s => Cyclone conditions => special treatment
249            zgt33 = 0.5_wp + SIGN( 0.5_wp, (zw - 33._wp) )   ! If pw10 < 33. => 0, else => 1
250            !
251            cd_n10_ncar(ji,jj) = 1.e-3_wp * ( &
252               &       (1._wp - zgt33)*( 2.7_wp/zw + 0.142_wp + zw/13.09_wp - 3.14807E-10_wp*zw6) & ! wind <  33 m/s
253               &      +    zgt33   *      2.34_wp )                                                 ! wind >= 33 m/s
254            !
255            cd_n10_ncar(ji,jj) = MAX( cd_n10_ncar(ji,jj), Cx_min )
256            !
257      END_2D
258      !
259   END FUNCTION cd_n10_ncar
260
261
262   FUNCTION ch_n10_ncar( psqrtcdn10 , pstab )
263      !!----------------------------------------------------------------------------------
264      !! Estimate of the neutral heat transfer coefficient at 10m      !!
265      !! Origin: Large & Yeager 2008, Eq. (9) and (12)
266
267      !!----------------------------------------------------------------------------------
268      REAL(wp), DIMENSION(jpi,jpj)             :: ch_n10_ncar
269      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: psqrtcdn10 ! sqrt( CdN10 )
270      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pstab      ! stable ABL => 1 / unstable ABL => 0
271      !!----------------------------------------------------------------------------------
272      IF( ANY(pstab < -0.00001) .OR. ANY(pstab >  1.00001) ) THEN
273         PRINT *, 'ERROR: ch_n10_ncar@mod_blk_ncar.f90: pstab ='
274         PRINT *, pstab
275         STOP
276      END IF
277      !
278      ch_n10_ncar = MAX( 1.e-3_wp * psqrtcdn10*( 18._wp*pstab + 32.7_wp*(1._wp - pstab) )  , Cx_min )   ! Eq. (9) & (12) Large & Yeager, 2008
279      !
280   END FUNCTION ch_n10_ncar
281
282   FUNCTION ce_n10_ncar( psqrtcdn10 )
283      !!----------------------------------------------------------------------------------
284      !! Estimate of the neutral heat transfer coefficient at 10m      !!
285      !! Origin: Large & Yeager 2008, Eq. (9) and (13)
286      !!----------------------------------------------------------------------------------
287      REAL(wp), DIMENSION(jpi,jpj)             :: ce_n10_ncar
288      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: psqrtcdn10 ! sqrt( CdN10 )
289      !!----------------------------------------------------------------------------------
290      ce_n10_ncar = MAX( 1.e-3_wp * ( 34.6_wp * psqrtcdn10 ) , Cx_min )
291      !
292   END FUNCTION ce_n10_ncar
293
294
295   FUNCTION psi_m_ncar( pzeta )
296      !!----------------------------------------------------------------------------------
297      !! Universal profile stability function for momentum
298      !!    !! Psis, L&Y 2004, Eq. (8c), (8d), (8e)
299      !!
300      !! pzeta : stability paramenter, z/L where z is altitude measurement
301      !!         and L is M-O length
302      !!
303      !! ** Author: L. Brodeau, June 2016 / AeroBulk (https://github.com/brodeau/aerobulk/)
304      !!----------------------------------------------------------------------------------
305      REAL(wp), DIMENSION(jpi,jpj) :: psi_m_ncar
306      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pzeta
307      !
308      INTEGER  ::   ji, jj    ! dummy loop indices
309      REAL(wp) :: zta, zx2, zx, zpsi_unst, zpsi_stab,  zstab   ! local scalars
310      !!----------------------------------------------------------------------------------
311      DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
312            zta = pzeta(ji,jj)
313            !
314            zx2 = SQRT( ABS(1._wp - 16._wp*zta) )  ! (1 - 16z)^0.5
315            zx2 = MAX( zx2 , 1._wp )
316            zx  = SQRT(zx2)                          ! (1 - 16z)^0.25
317            zpsi_unst = 2._wp*LOG( (1._wp + zx )*0.5_wp )   &
318               &            + LOG( (1._wp + zx2)*0.5_wp )   &
319               &          - 2._wp*ATAN(zx) + rpi*0.5_wp
320            !
321            zpsi_stab = -5._wp*zta
322            !
323            zstab = 0.5_wp + SIGN(0.5_wp, zta) ! zta > 0 => zstab = 1
324            !
325            psi_m_ncar(ji,jj) =          zstab  * zpsi_stab &  ! (zta > 0) Stable
326               &              + (1._wp - zstab) * zpsi_unst    ! (zta < 0) Unstable
327            !
328            !
329      END_2D
330   END FUNCTION psi_m_ncar
331
332
333   FUNCTION psi_h_ncar( pzeta )
334      !!----------------------------------------------------------------------------------
335      !! Universal profile stability function for temperature and humidity
336      !!    !! Psis, L&Y 2004, Eq. (8c), (8d), (8e)
337      !!
338      !! pzeta : stability paramenter, z/L where z is altitude measurement
339      !!         and L is M-O length
340      !!
341      !! ** Author: L. Brodeau, June 2016 / AeroBulk (https://github.com/brodeau/aerobulk/)
342      !!----------------------------------------------------------------------------------
343      REAL(wp), DIMENSION(jpi,jpj) :: psi_h_ncar
344      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pzeta
345      !
346      INTEGER  ::   ji, jj     ! dummy loop indices
347      REAL(wp) :: zta, zx2, zpsi_unst, zpsi_stab, zstab  ! local scalars
348      !!----------------------------------------------------------------------------------
349      !
350      DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
351            !
352            zta = pzeta(ji,jj)
353            !
354            zx2 = SQRT( ABS(1._wp - 16._wp*zta) )  ! (1 -16z)^0.5
355            zx2 = MAX( zx2 , 1._wp )
356            zpsi_unst = 2._wp*LOG( 0.5_wp*(1._wp + zx2) )
357            !
358            zpsi_stab = -5._wp*zta
359            !
360            zstab = 0.5_wp + SIGN(0.5_wp, zta) ! zta > 0 => zstab = 1
361            !
362            psi_h_ncar(ji,jj) =          zstab  * zpsi_stab &  ! (zta > 0) Stable
363               &              + (1._wp - zstab) * zpsi_unst    ! (zta < 0) Unstable
364            !
365      END_2D
366   END FUNCTION psi_h_ncar
367
368   !!======================================================================
369END MODULE sbcblk_algo_ncar
Note: See TracBrowser for help on using the repository browser.