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_ecmwf.F90 in NEMO/trunk/src/OCE/SBC – NEMO

source: NEMO/trunk/src/OCE/SBC/sbcblk_algo_ecmwf.F90

Last change on this file was 15387, checked in by amoulin, 3 years ago

use of charnock coefficient from wave model in sbcblk_algo_ecmwf -ticket #2686

  • Property svn:keywords set to Id
File size: 25.1 KB
Line 
1MODULE sbcblk_algo_ecmwf
2   !!======================================================================
3   !!                   ***  MODULE  sbcblk_algo_ecmwf  ***
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 IFS of ECMWF (cycle 40r1)
11   !!         based on IFS doc (avaible online on the ECMWF's website)
12   !!
13   !!       Routine turb_ecmwf maintained and developed in AeroBulk
14   !!                     (https://github.com/brodeau/aerobulk)
15   !!
16   !! ** Author: L. Brodeau, June 2019 / AeroBulk (https://github.com/brodeau/aerobulk)
17   !!----------------------------------------------------------------------
18   !! History :  4.0  !  2016-02  (L.Brodeau)   Original code
19   !!            4.2  !  2020-12  (L. Brodeau) Introduction of various air-ice bulk parameterizations + improvements
20   !!----------------------------------------------------------------------
21
22   !!----------------------------------------------------------------------
23   !!   turb_ecmwf  : computes the bulk turbulent transfer coefficients
24   !!                   adjusts t_air and q_air from zt to zu m
25   !!                   returns the effective bulk wind speed at 10m
26   !!----------------------------------------------------------------------
27   USE dom_oce         ! ocean space and time domain
28   USE phycst          ! physical constants
29   USE lib_mpp,        ONLY: ctl_stop         ! distribued memory computing library
30   USE in_out_manager, ONLY: nit000  ! I/O manager
31   USE sbc_phy         ! Catalog of functions for physical/meteorological parameters in the marine boundary layer
32   USE sbcblk_skin_ecmwf ! cool-skin/warm layer scheme !LB
33   USE sbcwave, ONLY : charn
34   USE sbc_oce, ONLY : ln_charn ! wave module
35
36   IMPLICIT NONE
37   PRIVATE
38
39   PUBLIC :: SBCBLK_ALGO_ECMWF_INIT, TURB_ECMWF
40
41   !! ECMWF own values for given constants, taken form IFS documentation...
42   REAL(wp), PARAMETER, PUBLIC :: charn0_ecmwf = 0.018_wp    ! Charnock constant (pretty high value here !!!
43   !                                          !    =>  Usually 0.011 for moderate winds)
44   REAL(wp), PARAMETER ::   zi0     = 1000.   ! scale height of the atmospheric boundary layer...1
45   REAL(wp), PARAMETER ::   Beta0    = 1.     ! gustiness parameter ( = 1.25 in COAREv3)
46   REAL(wp), PARAMETER ::   alpha_M = 0.11    ! For roughness length (smooth surface term)
47   REAL(wp), PARAMETER ::   alpha_H = 0.40    ! (Chapter 3, p.34, IFS doc Cy31r1)
48   REAL(wp), PARAMETER ::   alpha_Q = 0.62    !
49
50   !! * Substitutions
51#  include "do_loop_substitute.h90"
52
53   !!----------------------------------------------------------------------
54CONTAINS
55
56
57   SUBROUTINE sbcblk_algo_ecmwf_init(l_use_cs, l_use_wl)
58      !!---------------------------------------------------------------------
59      !!                  ***  FUNCTION sbcblk_algo_ecmwf_init  ***
60      !!
61      !! INPUT :
62      !! -------
63      !!    * l_use_cs : use the cool-skin parameterization
64      !!    * l_use_wl : use the warm-layer parameterization
65      !!---------------------------------------------------------------------
66      LOGICAL , INTENT(in) ::   l_use_cs ! use the cool-skin parameterization
67      LOGICAL , INTENT(in) ::   l_use_wl ! use the warm-layer parameterization
68      INTEGER :: ierr
69      !!---------------------------------------------------------------------
70      IF( l_use_wl ) THEN
71         ierr = 0
72         ALLOCATE ( dT_wl(jpi,jpj), Hz_wl(jpi,jpj), STAT=ierr )
73         IF( ierr > 0 ) CALL ctl_stop( ' SBCBLK_ALGO_ECMWF_INIT => allocation of dT_wl & Hz_wl failed!' )
74         dT_wl(:,:)  = 0._wp
75         Hz_wl(:,:)  = rd0 ! (rd0, constant, = 3m is default for Zeng & Beljaars)
76      ENDIF
77      IF( l_use_cs ) THEN
78         ierr = 0
79         ALLOCATE ( dT_cs(jpi,jpj), STAT=ierr )
80         IF( ierr > 0 ) CALL ctl_stop( ' SBCBLK_ALGO_ECMWF_INIT => allocation of dT_cs failed!' )
81         dT_cs(:,:) = -0.25_wp  ! First guess of skin correction
82      ENDIF
83   END SUBROUTINE sbcblk_algo_ecmwf_init
84
85
86
87   SUBROUTINE turb_ecmwf( kt, zt, zu, T_s, t_zt, q_s, q_zt, U_zu, l_use_cs, l_use_wl, &
88      &                      Cd, Ch, Ce, t_zu, q_zu, Ubzu,                            &
89      &                      nb_iter, Cdn, Chn, Cen,                                           & ! optional output
90      &                      Qsw, rad_lw, slp, pdT_cs,                                & ! optionals for cool-skin (and warm-layer)
91      &                      pdT_wl, pHz_wl )                                           ! optionals for warm-layer only
92      !!----------------------------------------------------------------------------------
93      !!                      ***  ROUTINE  turb_ecmwf  ***
94      !!
95      !! ** Purpose :   Computes turbulent transfert coefficients of surface
96      !!                fluxes according to IFS doc. (cycle 45r1)
97      !!                If relevant (zt /= zu), adjust temperature and humidity from height zt to zu
98      !!                Returns the effective bulk wind speed at zu to be used in the bulk formulas
99      !!
100      !!                Applies the cool-skin warm-layer correction of the SST to T_s
101      !!                if the net shortwave flux at the surface (Qsw), the downwelling longwave
102      !!                radiative fluxes at the surface (rad_lw), and the sea-leve pressure (slp)
103      !!                are provided as (optional) arguments!
104      !!
105      !! INPUT :
106      !! -------
107      !!    *  kt   : current time step (starts at 1)
108      !!    *  zt   : height for temperature and spec. hum. of air            [m]
109      !!    *  zu   : height for wind speed (usually 10m)                     [m]
110      !!    *  t_zt : potential air temperature at zt                         [K]
111      !!    *  q_zt : specific humidity of air at zt                          [kg/kg]
112      !!    *  U_zu : scalar wind speed at zu                                 [m/s]
113      !!    * l_use_cs : use the cool-skin parameterization
114      !!    * l_use_wl : use the warm-layer parameterization
115      !!
116      !! INPUT/OUTPUT:
117      !! -------------
118      !!    *  T_s  : always "bulk SST" as input                              [K]
119      !!              -> unchanged "bulk SST" as output if CSWL not used      [K]
120      !!              -> skin temperature as output if CSWL used              [K]
121      !!
122      !!    *  q_s  : SSQ aka saturation specific humidity at temp. T_s       [kg/kg]
123      !!              -> doesn't need to be given a value if skin temp computed (in case l_use_cs=True or l_use_wl=True)
124      !!              -> MUST be given the correct value if not computing skint temp. (in case l_use_cs=False or l_use_wl=False)
125      !!
126      !! OPTIONAL INPUT:
127      !! ---------------
128      !!    *  Qsw    : net solar flux (after albedo) at the surface (>0)     [W/m^2]
129      !!    *  rad_lw : downwelling longwave radiation at the surface  (>0)   [W/m^2]
130      !!    *  slp    : sea-level pressure                                    [Pa]
131      !!
132      !! OPTIONAL OUTPUT:
133      !! ----------------
134      !!    * pdT_cs  : SST increment "dT" for cool-skin correction           [K]
135      !!    * pdT_wl  : SST increment "dT" for warm-layer correction          [K]
136      !!    * pHz_wl  : thickness of warm-layer                               [m]
137      !!
138      !! OUTPUT :
139      !! --------
140      !!    *  Cd     : drag coefficient
141      !!    *  Ch     : sensible heat coefficient
142      !!    *  Ce     : evaporation coefficient
143      !!    *  t_zu   : pot. air temperature adjusted at wind height zu       [K]
144      !!    *  q_zu   : specific humidity of air        //                    [kg/kg]
145      !!    *  Ubzu   : bulk wind speed at zu                                 [m/s]
146      !!
147      !!
148      !! ** Author: L. Brodeau, June 2019 / AeroBulk (https://github.com/brodeau/aerobulk/)
149      !!----------------------------------------------------------------------------------
150      INTEGER,  INTENT(in   )                     ::   kt       ! current time step
151      REAL(wp), INTENT(in   )                     ::   zt       ! height for t_zt and q_zt                    [m]
152      REAL(wp), INTENT(in   )                     ::   zu       ! height for U_zu                             [m]
153      REAL(wp), INTENT(inout), DIMENSION(jpi,jpj) ::   T_s      ! sea surface temperature                [Kelvin]
154      REAL(wp), INTENT(in   ), DIMENSION(jpi,jpj) ::   t_zt     ! potential air temperature              [Kelvin]
155      REAL(wp), INTENT(inout), DIMENSION(jpi,jpj) ::   q_s      ! sea surface specific humidity           [kg/kg]
156      REAL(wp), INTENT(in   ), DIMENSION(jpi,jpj) ::   q_zt     ! specific air humidity at zt             [kg/kg]
157      REAL(wp), INTENT(in   ), DIMENSION(jpi,jpj) ::   U_zu     ! relative wind module at zu                [m/s]
158      LOGICAL , INTENT(in   )                     ::   l_use_cs ! use the cool-skin parameterization
159      LOGICAL , INTENT(in   )                     ::   l_use_wl ! use the warm-layer parameterization
160      REAL(wp), INTENT(  out), DIMENSION(jpi,jpj) ::   Cd       ! transfer coefficient for momentum         (tau)
161      REAL(wp), INTENT(  out), DIMENSION(jpi,jpj) ::   Ch       ! transfer coefficient for sensible heat (Q_sens)
162      REAL(wp), INTENT(  out), DIMENSION(jpi,jpj) ::   Ce       ! transfert coefficient for evaporation   (Q_lat)
163      REAL(wp), INTENT(  out), DIMENSION(jpi,jpj) ::   t_zu     ! pot. air temp. adjusted at zu               [K]
164      REAL(wp), INTENT(  out), DIMENSION(jpi,jpj) ::   q_zu     ! spec. humidity adjusted at zu           [kg/kg]
165      REAL(wp), INTENT(  out), DIMENSION(jpi,jpj) ::   Ubzu    ! bulk wind speed at zu                     [m/s]
166      !
167      INTEGER , INTENT(in   ), OPTIONAL                     :: nb_iter  ! number of iterations
168      REAL(wp), INTENT(  out), OPTIONAL, DIMENSION(jpi,jpj) ::   CdN
169      REAL(wp), INTENT(  out), OPTIONAL, DIMENSION(jpi,jpj) ::   ChN
170      REAL(wp), INTENT(  out), OPTIONAL, DIMENSION(jpi,jpj) ::   CeN
171      REAL(wp), INTENT(in   ), OPTIONAL, DIMENSION(jpi,jpj) ::   Qsw      !             [W/m^2]
172      REAL(wp), INTENT(in   ), OPTIONAL, DIMENSION(jpi,jpj) ::   rad_lw   !             [W/m^2]
173      REAL(wp), INTENT(in   ), OPTIONAL, DIMENSION(jpi,jpj) ::   slp      !             [Pa]
174      REAL(wp), INTENT(  out), OPTIONAL, DIMENSION(jpi,jpj) ::   pdT_cs
175      REAL(wp), INTENT(  out), OPTIONAL, DIMENSION(jpi,jpj) ::   pdT_wl   !             [K]
176      REAL(wp), INTENT(  out), OPTIONAL, DIMENSION(jpi,jpj) ::   pHz_wl   !             [m]
177      !
178      INTEGER :: nbit, jit
179      LOGICAL :: l_zt_equal_zu = .FALSE.      ! if q and t are given at same height as U
180      !
181      REAL(wp), DIMENSION(jpi,jpj) :: u_star, t_star, q_star
182      REAL(wp), DIMENSION(jpi,jpj) :: dt_zu, dq_zu
183      REAL(wp), DIMENSION(jpi,jpj) :: znu_a         !: Nu_air, Viscosity of air
184      REAL(wp), DIMENSION(jpi,jpj) :: Linv  !: 1/L (inverse of Monin Obukhov length...
185      REAL(wp), DIMENSION(jpi,jpj) :: z0, z0t, z0q
186      !
187      REAL(wp), DIMENSION(:,:), ALLOCATABLE :: zsst  ! to back up the initial bulk SST
188      !
189      REAL(wp), DIMENSION(jpi,jpj) :: func_m, func_h
190      REAL(wp), DIMENSION(jpi,jpj) :: ztmp0, ztmp1, ztmp2
191      CHARACTER(len=40), PARAMETER :: crtnm = 'turb_ecmwf@sbcblk_algo_ecmwf.F90'
192      !!----------------------------------------------------------------------------------
193      IF( kt == nit000 ) CALL SBCBLK_ALGO_ECMWF_INIT(l_use_cs, l_use_wl)
194
195      nbit = nb_iter0
196      IF( PRESENT(nb_iter) ) nbit = nb_iter
197
198      l_zt_equal_zu = ( ABS(zu - zt) < 0.01_wp ) ! testing "zu == zt" is risky with double precision
199
200      !! Initializations for cool skin and warm layer:
201      IF( l_use_cs .AND. (.NOT.(PRESENT(Qsw) .AND. PRESENT(rad_lw) .AND. PRESENT(slp))) ) &
202         &   CALL ctl_stop( '['//TRIM(crtnm)//'] => ' , 'you need to provide Qsw, rad_lw & slp to use cool-skin param!' )
203
204      IF( l_use_wl .AND. (.NOT.(PRESENT(Qsw) .AND. PRESENT(rad_lw) .AND. PRESENT(slp))) ) &
205         &   CALL ctl_stop( '['//TRIM(crtnm)//'] => ' , 'you need to provide Qsw, rad_lw & slp to use warm-layer param!' )
206
207      IF( l_use_cs .OR. l_use_wl ) THEN
208         ALLOCATE ( zsst(jpi,jpj) )
209         zsst = T_s ! backing up the bulk SST
210         IF( l_use_cs ) T_s = T_s - 0.25_wp   ! First guess of correction
211         q_s    = rdct_qsat_salt*q_sat(MAX(T_s, 200._wp), slp) ! First guess of q_s
212      ENDIF
213
214
215      ! Identical first gess as in COARE, with IFS parameter values though...
216      !
217      !! First guess of temperature and humidity at height zu:
218      t_zu = MAX( t_zt ,  180._wp )   ! who knows what's given on masked-continental regions...
219      q_zu = MAX( q_zt , 1.e-6_wp )   !               "
220
221      !! Pot. temp. difference (and we don't want it to be 0!)
222      dt_zu = t_zu - T_s ;   dt_zu = SIGN( MAX(ABS(dt_zu),1.E-6_wp), dt_zu )
223      dq_zu = q_zu - q_s ;   dq_zu = SIGN( MAX(ABS(dq_zu),1.E-9_wp), dq_zu )
224
225      znu_a = visc_air(t_zu) ! Air viscosity (m^2/s) at zt given from temperature in (K)
226
227      Ubzu = SQRT(U_zu*U_zu + 0.5_wp*0.5_wp) ! initial guess for wind gustiness contribution
228
229      ztmp0   = LOG(    zu*10000._wp) ! optimization: 10000. == 1/z0 (with z0 first guess == 0.0001)
230      ztmp1   = LOG(10._wp*10000._wp) !       "                    "               "
231      u_star = 0.035_wp*Ubzu*ztmp1/ztmp0       ! (u* = 0.035*Un10)
232
233      IF (ln_charn) THEN ! Charnock value if wave coupling
234         z0 = charn*u_star*u_star/grav + 0.11_wp*znu_a/u_star
235      ELSE
236         z0 = charn0_ecmwf*u_star*u_star/grav + 0.11_wp*znu_a/u_star
237      ENDIF
238      z0     = MIN( MAX(ABS(z0), 1.E-9) , 1._wp )                      ! (prevents FPE from stupid values from masked region later on)
239
240      z0t    = 1._wp / ( 0.1_wp*EXP(vkarmn/(0.00115/(vkarmn/ztmp1))) )
241      z0t    = MIN( MAX(ABS(z0t), 1.E-9) , 1._wp )                      ! (prevents FPE from stupid values from masked region later on)
242
243      Cd     = MAX( (vkarmn/ztmp0)**2 , Cx_min )   ! first guess of Cd
244
245      ztmp0 = vkarmn2/LOG(zt/z0t)/Cd
246
247      ztmp2 = Ri_bulk( zu, T_s, t_zu, q_s, q_zu, Ubzu ) ! Bulk Richardson Number (BRN)
248
249      !! First estimate of zeta_u, depending on the stability, ie sign of BRN (ztmp2):
250      ztmp1 = 0.5 + SIGN( 0.5_wp , ztmp2 )
251      func_h = (1._wp - ztmp1) *   ztmp0*ztmp2 / (1._wp - ztmp2*zi0*0.004_wp*Beta0**3/zu) & !  BRN < 0
252         &  +       ztmp1      * ( ztmp0*ztmp2 + 27._wp/9._wp*ztmp2*ztmp2 )                 !  BRN > 0
253
254      !! First guess M-O stability dependent scaling params.(u*,t*,q*) to estimate z0 and z/L
255      ztmp0  = vkarmn/(LOG(zu/z0t) - psi_h_ecmwf(func_h))
256
257      u_star = MAX ( Ubzu*vkarmn/(LOG(zu) - LOG(z0)  - psi_m_ecmwf(func_h)) , 1.E-9 )  !  (MAX => prevents FPE from stupid values from masked region later on)
258      t_star = dt_zu*ztmp0
259      q_star = dq_zu*ztmp0
260
261      ! What needs to be done if zt /= zu:
262      IF( .NOT. l_zt_equal_zu ) THEN
263         !! First update of values at zu (or zt for wind)
264         ztmp0 = psi_h_ecmwf(func_h) - psi_h_ecmwf(zt*func_h/zu)    ! zt*func_h/zu == zeta_t
265         ztmp1 = LOG(zt/zu) + ztmp0
266         t_zu = t_zt - t_star/vkarmn*ztmp1
267         q_zu = q_zt - q_star/vkarmn*ztmp1
268         q_zu = (0.5_wp + SIGN(0.5_wp,q_zu))*q_zu !Makes it impossible to have negative humidity :
269         !
270         dt_zu = t_zu - T_s  ; dt_zu = SIGN( MAX(ABS(dt_zu),1.E-6_wp), dt_zu )
271         dq_zu = q_zu - q_s  ; dq_zu = SIGN( MAX(ABS(dq_zu),1.E-9_wp), dq_zu )
272      ENDIF
273
274
275      !! => that was same first guess as in COARE...
276
277
278      !! First guess of inverse of Obukov length (1/L) :
279      Linv = One_on_L( t_zu, q_zu, u_star, t_star, q_star )
280
281      !! Functions such as  u* = Ubzu*vkarmn/func_m
282      ztmp0 = zu*Linv
283      func_m = LOG(zu) - LOG(z0)  - psi_m_ecmwf(ztmp0) + psi_m_ecmwf( z0*Linv)
284      func_h = LOG(zu) - LOG(z0t) - psi_h_ecmwf(ztmp0) + psi_h_ecmwf(z0t*Linv)
285
286      !! ITERATION BLOCK
287      DO jit = 1, nbit
288
289         !! Bulk Richardson Number at z=zu (Eq. 3.25)
290         ztmp0 = Ri_bulk( zu, T_s, t_zu, q_s, q_zu, Ubzu ) ! Bulk Richardson Number (BRN)
291
292         !! New estimate of the inverse of the Obukhon length (Linv == zeta/zu) :
293         Linv = ztmp0*func_m*func_m/func_h / zu     ! From Eq. 3.23, Chap.3.2.3, IFS doc - Cy40r1
294         !! Note: it is slightly different that the L we would get with the usual
295         Linv = SIGN( MIN(ABS(Linv),200._wp), Linv ) ! (prevent FPE from stupid values from masked region later on...)
296
297         !! Update func_m with new Linv:
298         func_m = LOG(zu) -LOG(z0) - psi_m_ecmwf(zu*Linv) + psi_m_ecmwf(z0*Linv) ! LB: should be "zu+z0" rather than "zu" alone, but z0 is tiny wrt zu!
299
300         !! Need to update roughness lengthes:
301         u_star = Ubzu*vkarmn/func_m
302         ztmp2  = u_star*u_star
303         ztmp1  = znu_a/u_star
304         IF (ln_charn) THEN ! Charnock value if wave coupling
305            z0  = MIN( ABS( alpha_M*ztmp1 + charn*ztmp2/grav ) , 0.001_wp)
306         ELSE
307            z0  = MIN( ABS( alpha_M*ztmp1 + charn0_ecmwf*ztmp2/grav ) , 0.001_wp)
308         ENDIF
309         z0     = MIN( ABS( alpha_M*ztmp1 + charn0_ecmwf*ztmp2/grav ) , 0.001_wp)
310         z0t    = MIN( ABS( alpha_H*ztmp1                           ) , 0.001_wp)   ! eq.3.26, Chap.3, p.34, IFS doc - Cy31r1
311         z0q    = MIN( ABS( alpha_Q*ztmp1                           ) , 0.001_wp)
312
313         !! Update wind at zu with convection-related wind gustiness in unstable conditions (Chap. 3.2, IFS doc - Cy40r1, Eq.3.17 and Eq.3.18 + Eq.3.8)
314         ztmp2 = Beta0*Beta0*ztmp2*(MAX(-zi0*Linv/vkarmn,0._wp))**(2._wp/3._wp) ! square of wind gustiness contribution  (combining Eq. 3.8 and 3.18, hap.3, IFS doc - Cy31r1)
315         !!   ! Only true when unstable (L<0) => when ztmp0 < 0 => explains "-" before zi0
316         Ubzu = MAX(SQRT(U_zu*U_zu + ztmp2), 0.2_wp)        ! include gustiness in bulk wind speed
317         ! => 0.2 prevents Ubzu to be 0 in stable case when U_zu=0.
318
319
320         !! Need to update "theta" and "q" at zu in case they are given at different heights
321         !! as well the air-sea differences:
322         IF( .NOT. l_zt_equal_zu ) THEN
323            !! Arrays func_m and func_h are free for a while so using them as temporary arrays...
324            func_h = psi_h_ecmwf(zu*Linv) ! temporary array !!!
325            func_m = psi_h_ecmwf(zt*Linv) ! temporary array !!!
326
327            ztmp2  = psi_h_ecmwf(z0t*Linv)
328            ztmp0  = func_h - ztmp2
329            ztmp1  = vkarmn/(LOG(zu) - LOG(z0t) - ztmp0)
330            t_star = dt_zu*ztmp1
331            ztmp2  = ztmp0 - func_m + ztmp2
332            ztmp1  = LOG(zt/zu) + ztmp2
333            t_zu   = t_zt - t_star/vkarmn*ztmp1
334
335            ztmp2  = psi_h_ecmwf(z0q*Linv)
336            ztmp0  = func_h - ztmp2
337            ztmp1  = vkarmn/(LOG(zu) - LOG(z0q) - ztmp0)
338            q_star = dq_zu*ztmp1
339            ztmp2  = ztmp0 - func_m + ztmp2
340            ztmp1  = LOG(zt/zu) + ztmp2
341            q_zu   = q_zt - q_star/vkarmn*ztmp1
342         ENDIF
343
344         !! Updating because of updated z0 and z0t and new Linv...
345         ztmp0 = zu*Linv
346         func_m = log(zu) - LOG(z0 ) - psi_m_ecmwf(ztmp0) + psi_m_ecmwf(z0 *Linv)
347         func_h = log(zu) - LOG(z0t) - psi_h_ecmwf(ztmp0) + psi_h_ecmwf(z0t*Linv)
348
349
350         IF( l_use_cs ) THEN
351            !! Cool-skin contribution
352
353            CALL UPDATE_QNSOL_TAU( zu, T_s, q_s, t_zu, q_zu, u_star, t_star, q_star, U_zu, Ubzu, slp, rad_lw, &
354               &                   ztmp1, ztmp0,  Qlat=ztmp2)  ! Qnsol -> ztmp1 / Tau -> ztmp0
355
356            CALL CS_ECMWF( Qsw, ztmp1, u_star, zsst )  ! Qnsol -> ztmp1
357
358            T_s(:,:) = zsst(:,:) + dT_cs(:,:)*tmask(:,:,1)
359            IF( l_use_wl ) T_s(:,:) = T_s(:,:) + dT_wl(:,:)*tmask(:,:,1)
360            q_s(:,:) = rdct_qsat_salt*q_sat(MAX(T_s(:,:), 200._wp), slp(:,:))
361
362         ENDIF
363
364         IF( l_use_wl ) THEN
365            !! Warm-layer contribution
366            CALL UPDATE_QNSOL_TAU( zu, T_s, q_s, t_zu, q_zu, u_star, t_star, q_star, U_zu, Ubzu, slp, rad_lw, &
367               &                   ztmp1, ztmp2)  ! Qnsol -> ztmp1 / Tau -> ztmp2
368            CALL WL_ECMWF( Qsw, ztmp1, u_star, zsst )
369            !! Updating T_s and q_s !!!
370            T_s(:,:) = zsst(:,:) + dT_wl(:,:)*tmask(:,:,1) !
371            IF( l_use_cs ) T_s(:,:) = T_s(:,:) + dT_cs(:,:)*tmask(:,:,1)
372            q_s(:,:) = rdct_qsat_salt*q_sat(MAX(T_s(:,:), 200._wp), slp(:,:))
373         ENDIF
374
375         IF( l_use_cs .OR. l_use_wl .OR. (.NOT. l_zt_equal_zu) ) THEN
376            dt_zu = t_zu - T_s ;  dt_zu = SIGN( MAX(ABS(dt_zu),1.E-6_wp), dt_zu )
377            dq_zu = q_zu - q_s ;  dq_zu = SIGN( MAX(ABS(dq_zu),1.E-9_wp), dq_zu )
378         ENDIF
379
380      END DO !DO jit = 1, nbit
381
382      Cd = MAX( vkarmn2/(func_m*func_m) , Cx_min )
383      Ch = MAX( vkarmn2/(func_m*func_h) , Cx_min )
384      ztmp2 = LOG(zu/z0q) - psi_h_ecmwf(zu*Linv) + psi_h_ecmwf(z0q*Linv)   ! func_q
385      Ce = MAX( vkarmn2/(func_m*ztmp2)  , Cx_min )
386
387      IF(PRESENT(Cdn)) Cdn = MAX( vkarmn2 / (LOG(zu/z0 )*LOG(zu/z0 )) , Cx_min )
388      IF(PRESENT(Chn)) Chn = MAX( vkarmn2 / (LOG(zu/z0t)*LOG(zu/z0t)) , Cx_min )
389      IF(PRESENT(Cen)) Cen = MAX( vkarmn2 / (LOG(zu/z0q)*LOG(zu/z0q)) , Cx_min )
390
391      IF( l_use_cs .AND. PRESENT(pdT_cs) ) pdT_cs = dT_cs
392      IF( l_use_wl .AND. PRESENT(pdT_wl) ) pdT_wl = dT_wl
393      IF( l_use_wl .AND. PRESENT(pHz_wl) ) pHz_wl = Hz_wl
394
395      IF( l_use_cs .OR. l_use_wl ) DEALLOCATE ( zsst )
396
397   END SUBROUTINE turb_ecmwf
398
399
400   FUNCTION psi_m_ecmwf( pzeta )
401      !!----------------------------------------------------------------------------------
402      !! Universal profile stability function for momentum
403      !!     ECMWF / as in IFS cy31r1 documentation, available online
404      !!     at ecmwf.int
405      !!
406      !! pzeta : stability paramenter, z/L where z is altitude measurement
407      !!         and L is M-O length
408      !!
409      !! ** Author: L. Brodeau, June 2016 / AeroBulk (https://github.com/brodeau/aerobulk/)
410      !!----------------------------------------------------------------------------------
411      REAL(wp), DIMENSION(jpi,jpj) :: psi_m_ecmwf
412      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pzeta
413      !
414      INTEGER  ::   ji, jj    ! dummy loop indices
415      REAL(wp) :: zta, zx2, zx, ztmp, zpsi_unst, zpsi_stab, zstab, zc
416      !!----------------------------------------------------------------------------------
417      zc = 5._wp/0.35_wp
418      DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
419            !
420            zta = MIN( pzeta(ji,jj) , 5._wp ) !! Very stable conditions (L positif and big!):
421
422            ! *** Unstable (Paulson 1970)    [eq.3.20, Chap.3, p.33, IFS doc - Cy31r1] :
423            zx2 = SQRT( ABS(1._wp - 16._wp*zta) )  ! (1 - 16z)^0.5
424            zx  = SQRT(zx2)                          ! (1 - 16z)^0.25
425            ztmp = 1._wp + zx
426            zpsi_unst = LOG( 0.125_wp*ztmp*ztmp*(1._wp + zx2) ) - 2._wp*ATAN( zx ) + 0.5_wp*rpi
427
428            ! *** Stable                   [eq.3.22, Chap.3, p.33, IFS doc - Cy31r1] :
429            zpsi_stab = -2._wp/3._wp*(zta - zc)*EXP(-0.35_wp*zta) &
430               &       - zta - 2._wp/3._wp*zc
431            !
432            zstab = 0.5_wp + SIGN(0.5_wp, zta) ! zta > 0 => zstab = 1
433            !
434            psi_m_ecmwf(ji,jj) =         zstab  * zpsi_stab &  ! (zta > 0) Stable
435               &              + (1._wp - zstab) * zpsi_unst    ! (zta < 0) Unstable
436            !
437      END_2D
438   END FUNCTION psi_m_ecmwf
439
440
441   FUNCTION psi_h_ecmwf( pzeta )
442      !!----------------------------------------------------------------------------------
443      !! Universal profile stability function for temperature and humidity
444      !!     ECMWF / as in IFS cy31r1 documentation, available online
445      !!     at ecmwf.int
446      !!
447      !! pzeta : stability paramenter, z/L where z is altitude measurement
448      !!         and L is M-O length
449      !!
450      !! ** Author: L. Brodeau, June 2016 / AeroBulk (https://github.com/brodeau/aerobulk/)
451      !!----------------------------------------------------------------------------------
452      REAL(wp), DIMENSION(jpi,jpj) :: psi_h_ecmwf
453      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pzeta
454      !
455      INTEGER  ::   ji, jj     ! dummy loop indices
456      REAL(wp) ::  zta, zx2, zpsi_unst, zpsi_stab, zstab, zc
457      !!----------------------------------------------------------------------------------
458      zc = 5._wp/0.35_wp
459      !
460      DO_2D( nn_hls, nn_hls, nn_hls, nn_hls )
461            !
462            zta = MIN(pzeta(ji,jj) , 5._wp)   ! Very stable conditions (L positif and big!):
463            !
464            ! *** Unstable (Paulson 1970)   [eq.3.20, Chap.3, p.33, IFS doc - Cy31r1] :
465            zx2 = SQRT( ABS(1._wp - 16._wp*zta) )  ! (1 -16z)^0.5
466            zpsi_unst = 2._wp*LOG( 0.5_wp*(1._wp + zx2) )
467            !
468            ! *** Stable [eq.3.22, Chap.3, p.33, IFS doc - Cy31r1] :
469            zpsi_stab = -2._wp/3._wp*(zta - zc)*EXP(-0.35_wp*zta) &
470               &       - ABS(1._wp + 2._wp/3._wp*zta)**1.5_wp - 2._wp/3._wp*zc + 1._wp
471            !
472            ! LB: added ABS() to avoid NaN values when unstable, which contaminates the unstable solution...
473            !
474            zstab = 0.5_wp + SIGN(0.5_wp, zta) ! zta > 0 => zstab = 1
475            !
476            psi_h_ecmwf(ji,jj) =         zstab  * zpsi_stab &  ! (zta > 0) Stable
477               &              + (1._wp - zstab) * zpsi_unst    ! (zta < 0) Unstable
478            !
479      END_2D
480   END FUNCTION psi_h_ecmwf
481
482
483   !!======================================================================
484END MODULE sbcblk_algo_ecmwf
Note: See TracBrowser for help on using the repository browser.