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_skin_coare.F90 in NEMO/branches/2019/dev_r11085_ASINTER-05_Brodeau_Advanced_Bulk/src/OCE/SBC – NEMO

source: NEMO/branches/2019/dev_r11085_ASINTER-05_Brodeau_Advanced_Bulk/src/OCE/SBC/sbcblk_skin_coare.F90 @ 11962

Last change on this file since 11962 was 11962, checked in by laurent, 4 years ago

Syntax improvements and minor bug fixes...

File size: 15.3 KB
Line 
1MODULE sbcblk_skin_coare
2   !!======================================================================
3   !!                   ***  MODULE  sbcblk_skin_coare  ***
4   !! Computes:
5   !!   * the surface skin temperature (aka SSST) based on the cool-skin/warm-layer
6   !!     scheme used at ECMWF
7   !!    Using formulation/param. of COARE 3.6 (Fairall et al., 2019)
8   !!
9   !!   From routine turb_ecmwf maintained and developed in AeroBulk
10   !!            (https://github.com/brodeau/aerobulk)
11   !!
12   !! ** Author: L. Brodeau, September 2019 / AeroBulk (https://github.com/brodeau/aerobulk)
13   !!----------------------------------------------------------------------
14   !! History :  4.0  !  2016-02  (L.Brodeau)   Original code
15   !!----------------------------------------------------------------------
16
17   !!----------------------------------------------------------------------
18   !!  cswl_ecmwf : computes the surface skin temperature (aka SSST)
19   !!               based on the cool-skin/warm-layer scheme used at ECMWF
20   !!----------------------------------------------------------------------
21   USE oce             ! ocean dynamics and tracers
22   USE dom_oce         ! ocean space and time domain
23   USE phycst          ! physical constants
24   USE sbc_oce         ! Surface boundary condition: ocean fields
25
26   USE sbcblk_phy      !LOLO: all thermodynamics functions, rho_air, q_sat, etc...
27
28   USE sbcdcy          !LOLO: to know hour of dawn and dusk: rdawn_dcy and rdusk_dcy (needed in WL_COARE)
29   
30   USE lib_mpp         ! distribued memory computing library
31   USE in_out_manager  ! I/O manager
32   USE lib_fortran     ! to use key_nosignedzero
33
34   IMPLICIT NONE
35   PRIVATE
36
37   PUBLIC :: CS_COARE, WL_COARE
38
39   !! Cool-skin related parameters:
40   REAL(wp), ALLOCATABLE, SAVE, DIMENSION(:,:), PUBLIC :: &
41      &                        dT_cs         !: dT due to cool-skin effect => temperature difference between air-sea interface (z=0) and right below viscous layer (z=delta)
42
43   !! Warm-layer related parameters:
44   REAL(wp), ALLOCATABLE, SAVE, DIMENSION(:,:), PUBLIC :: &
45      &                        dT_wl,     &  !: dT due to warm-layer effect => difference between "almost surface (right below viscous layer, z=delta) and depth of bulk SST (z=gdept_1d(1))
46      &                        Hz_wl,     &  !: depth of warm-layer [m]
47      &                        Qnt_ac,    &  !: time integral / accumulated heat stored by the warm layer Qxdt => [J/m^2] (reset to zero every midnight)
48      &                        Tau_ac        !: time integral / accumulated momentum Tauxdt => [N.s/m^2] (reset to zero every midnight)
49
50   REAL(wp), PARAMETER, PUBLIC :: Hwl_max = 20._wp    !: maximum depth of warm layer (adjustable)
51   !
52   REAL(wp), PARAMETER :: rich   = 0.65_wp   !: critical Richardson number
53   !
54   REAL(wp), PARAMETER :: zfr0   = 0.5_wp    !: initial value of solar flux absorption
55   !
56   !!----------------------------------------------------------------------
57CONTAINS
58
59
60   SUBROUTINE CS_COARE( pQsw, pQnsol, pustar, pSST, pQlat )
61      !!---------------------------------------------------------------------
62      !!
63      !! Cool-skin parameterization, based on Fairall et al., 1996,
64      !! revisited for COARE 3.6 (Fairall et al., 2019)
65      !!
66      !! Fairall, C. W., Bradley, E. F., Godfrey, J. S., Wick, G. A.,
67      !! Edson, J. B., and Young, G. S. ( 1996), Cool‐skin and warm‐layer
68      !! effects on sea surface temperature, J. Geophys. Res., 101( C1), 1295-1308,
69      !! doi:10.1029/95JC03190.
70      !!
71      !!------------------------------------------------------------------
72      !!
73      !!  **   INPUT:
74      !!     *pQsw*       surface net solar radiation into the ocean     [W/m^2] => >= 0 !
75      !!     *pQnsol*     surface net non-solar heat flux into the ocean [W/m^2] => normally < 0 !
76      !!     *pustar*     friction velocity u*                           [m/s]
77      !!     *pSST*       bulk SST (taken at depth gdept_1d(1))          [K]
78      !!     *pQlat*      surface latent heat flux                       [K]
79      !!------------------------------------------------------------------
80      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pQsw   ! net solar a.k.a shortwave radiation into the ocean (after albedo) [W/m^2]
81      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pQnsol ! non-solar heat flux to the ocean [W/m^2]
82      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pustar  ! friction velocity, temperature and humidity (u*,t*,q*)
83      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pSST ! bulk SST [K]
84      REAL(wp), DIMENSION(jpi,jpj), INTENT(in) :: pQlat  ! latent heat flux [W/m^2]
85      !!---------------------------------------------------------------------
86      INTEGER  :: ji, jj, jc
87      REAL(wp) :: zQabs, zdelta, zfr
88      !!---------------------------------------------------------------------
89      DO jj = 1, jpj
90         DO ji = 1, jpi
91
92            zQabs = pQnsol(ji,jj) ! first guess of heat flux absorbed within the viscous sublayer of thicknes delta,
93            !                     !   => we DO not miss a lot assuming 0 solar flux absorbed in the tiny layer of thicknes zdelta...
94
95            zdelta = delta_skin_layer( alpha_sw(pSST(ji,jj)), zQabs, pQlat(ji,jj), pustar(ji,jj) )
96
97            DO jc = 1, 4 ! because implicit in terms of zdelta...
98               zfr = MAX( 0.137_wp + 11._wp*zdelta - 6.6E-5_wp/zdelta*(1._wp - EXP(-zdelta/8.E-4_wp)) , 0.01_wp ) ! Solar absorption, Eq.16 (Fairall al. 1996b) /  !LB: why 0.065 and not 0.137 like in the paper??? Beljaars & Zeng use 0.065, not 0.137 !
99               zQabs = pQnsol(ji,jj) + zfr*pQsw(ji,jj)
100               zdelta = delta_skin_layer( alpha_sw(pSST(ji,jj)), zQabs, pQlat(ji,jj), pustar(ji,jj) )
101            END DO
102
103            dT_cs(ji,jj) = zQabs*zdelta/rk0_w   ! temperature increment, yes dT_cs can actually > 0, if Qabs > 0 (rare but possible!)
104
105         END DO
106      END DO
107
108   END SUBROUTINE CS_COARE
109
110
111   SUBROUTINE WL_COARE( pQsw, pQnsol, pTau, pSST, iwait )
112      !!---------------------------------------------------------------------
113      !!
114      !!  Warm-Layer scheme according to COARE 3.6 (Fairall et al, 2019)
115      !!     ------------------------------------------------------------------
116      !!
117      !!  **   INPUT:
118      !!     *pQsw*       surface net solar radiation into the ocean     [W/m^2] => >= 0 !
119      !!     *pQnsol*     surface net non-solar heat flux into the ocean [W/m^2] => normally < 0 !
120      !!     *pTau*       surface wind stress                            [N/m^2]
121      !!     *pSST*       bulk SST  (taken at depth gdept_1d(1))         [K]
122      !!     *iwait*      if /= 0 then wait before updating accumulated fluxes, we are within a converging itteration loop...
123      !!---------------------------------------------------------------------
124      REAL(wp), DIMENSION(jpi,jpj), INTENT(in)  :: pQsw     ! surface net solar radiation into the ocean [W/m^2]     => >= 0 !
125      REAL(wp), DIMENSION(jpi,jpj), INTENT(in)  :: pQnsol   ! surface net non-solar heat flux into the ocean [W/m^2] => normally < 0 !
126      REAL(wp), DIMENSION(jpi,jpj), INTENT(in)  :: pTau     ! wind stress [N/m^2]
127      REAL(wp), DIMENSION(jpi,jpj), INTENT(in)  :: pSST     ! bulk SST at depth gdept_1d(1) [K]
128      INTEGER ,                     INTENT(in)  :: iwait    ! if /= 0 then wait before updating accumulated fluxes
129      !!
130      INTEGER :: ji,jj
131      !
132      REAL(wp) :: zdTwl, zHwl, zQabs, zfr
133      REAL(wp) :: zqac, ztac
134      REAL(wp) :: zalpha, zcd1, zcd2, flg
135      !!---------------------------------------------------------------------
136
137      REAL(wp) :: ztime, znoon, zmidn
138      INTEGER  :: jl
139
140      LOGICAL :: l_exit, l_destroy_wl
141
142      !! INITIALIZATION:
143      zQabs  = 0._wp       ! total heat flux absorped in warm layer
144      zfr    = zfr0        ! initial value of solar flux absorption !LOLO: save it and use previous value !!!
145
146      IF( .NOT. ln_dm2dc ) CALL sbc_dcy_param() ! we need to call sbc_dcy_param (sbcdcy.F90) because rdawn_dcy and rdusk_dcy are unkonwn otherwize!
147
148      ztime = REAL(nsec_day,wp)/(24._wp*3600._wp) ! time of current time step since 00:00 for current day (UTC) -> ztime = 0 -> 00:00 / ztime = 0.5 -> 12:00 ...
149
150      IF (lwp) THEN
151         WRITE(numout,*) ''
152         WRITE(numout,*) 'LOLO: sbcblk_skin_coare => nsec_day, ztime =', nsec_day, ztime
153      END IF
154     
155      DO jj = 1, jpj
156         DO ji = 1, jpi
157
158            l_exit       = .FALSE.
159            l_destroy_wl = .FALSE.
160
161            zdTwl =  dT_wl(ji,jj)                          ! value of previous time step as first guess
162            zHwl  = MAX( MIN(Hz_wl(ji,jj),Hwl_max),0.1_wp) !   "                  "           "
163
164            zqac = Qnt_ac(ji,jj) ! previous time step Qnt_ac
165            ztac = Tau_ac(ji,jj)
166
167            !*****  variables for warm layer  ***
168            zalpha = alpha_sw( pSST(ji,jj) ) ! thermal expansion coefficient of sea-water (SST accurate enough!)
169
170            zcd1 = SQRT(2._wp*rich*rCp0_w/(zalpha*grav*rho0_w))        !mess-o-constants 1
171            zcd2 = SQRT(2._wp*zalpha*grav/(rich*rho0_w))/(rCp0_w**1.5) !mess-o-constants 2
172
173           
174            znoon = MOD( 0.5_wp*(rdawn_dcy(ji,jj)+rdusk_dcy(ji,jj)), 1._wp )   ! 0<rnoon<1. => rnoon*24 = UTC time of local noon
175            zmidn = MOD(              znoon-0.5_wp                 , 1._wp )
176            zmidn = MOD( zmidn + 0.125_wp , 1._wp ) ! 3 hours past the local midnight
177
178            IF ( (ztime >= zmidn) .AND. (ztime < rdawn_dcy(ji,jj)) ) THEN
179               ! Dawn reset to 0!
180               l_exit       = .TRUE.
181               l_destroy_wl = .TRUE.
182            END IF
183
184            IF ( .NOT. l_exit ) THEN
185               !! Initial test on initial guess of absorbed heat flux in warm-layer:
186               zfr = 1._wp - ( 0.28*0.014*(1. - EXP(-zHwl/0.014)) + 0.27*0.357*(1. - EXP(-zHwl/0.357)) &
187                  &        + 0.45*12.82*(1-EXP(-zHwl/12.82)) ) / zHwl
188               zQabs = zfr*pQsw(ji,jj) + pQnsol(ji,jj) ! first guess of tot. heat flux absorbed in warm layer !LOLO: depends of zfr, which is wild guess... Wrong!!!
189
190               IF ( (ABS(zdTwl) < 1.E-6_wp) .AND. (zQabs <= 0._wp) ) THEN
191                  ! We have not started to build a WL yet (dT==0) and there's no way it can occur now
192                  ! since zQabs <= 0._wp
193                  ! => no need to go further
194                  l_exit = .TRUE.
195               END IF
196
197            END IF
198
199            ! Okay test on updated absorbed flux:
200            !LOLO: remove??? has a strong influence !!!
201            IF ( (.NOT.(l_exit)) .AND. (Qnt_ac(ji,jj) + zQabs*rdt <= 0._wp) ) THEN
202               l_exit       = .TRUE.
203               l_destroy_wl = .TRUE.
204            END IF
205
206
207            IF ( .NOT. l_exit) THEN
208
209               ! Two possibilities at this point:
210               ! 1/ A warm layer already exists (dT>0) but it is cooling down because Qabs<0
211               ! 2/ Regardless of WL formed (dT==0 or dT>0), we are in the process to initiate one or warm further it !
212
213               ztac = Tau_ac(ji,jj) + MAX(.002_wp , pTau(ji,jj))*rdt      ! updated momentum integral
214               !PRINT *, '#LBD: updated value for Tac=',  REAL(ztac,4)
215
216               !! We update the value of absorbtion and zQabs:
217               !! some part is useless if Qsw=0 !!!
218               DO jl = 1, 5
219                  zfr = 1. - ( 0.28*0.014*(1. - EXP(-zHwl/0.014)) + 0.27*0.357*(1. - EXP(-zHwl/0.357)) &
220                     &        + 0.45*12.82*(1-EXP(-zHwl/12.82)) ) / zHwl
221                  zQabs = zfr*pQsw(ji,jj) + pQnsol(ji,jj)
222                  zqac  = Qnt_ac(ji,jj) + zQabs*rdt ! updated heat absorbed
223                  IF ( zqac <= 0._wp ) EXIT
224                  zHwl = MAX( MIN( Hwl_max , zcd1*ztac/SQRT(zqac)) , 0.1_wp ) ! Warm-layer depth
225               END DO
226
227               IF ( zqac <= 0._wp ) THEN
228                  l_destroy_wl = .TRUE.
229                  l_exit       = .TRUE.
230               ELSE
231                  zdTwl = zcd2*zqac**1.5/ztac * MAX(zqac/ABS(zqac),0._wp)  !! => IF(zqac>0._wp): zdTwl=zcd2*zqac**1.5/ztac ; ELSE: zdTwl=0. / ! normally: zqac > 0 !
232                  !PRINT *, '#LBD: updated preliminary value for dT_wl=',  REAL(zdTwl,4)
233                  ! Warm layer correction
234                  flg = 0.5_wp + SIGN( 0.5_wp , gdept_1d(1)-zHwl )               ! => 1 when gdept_1d(1)>zHwl (zdTwl = zdTwl) | 0 when gdept_1d(1)<zHwl (zdTwl = zdTwl*gdept_1d(1)/zHwl)
235                  zdTwl = zdTwl * ( flg + (1._wp-flg)*gdept_1d(1)/zHwl )
236               END IF
237
238            END IF !IF ( .NOT. l_exit)
239
240            IF ( l_destroy_wl ) THEN
241               zdTwl = 0._wp
242               zfr   = 0.75_wp
243               zHwl  = Hwl_max
244               zqac  = 0._wp
245               ztac  = 0._wp
246            END IF
247
248            IF ( iwait == 0 ) THEN
249               !! Iteration loop within bulk algo is over, time to update what needs to be updated:
250               dT_wl(ji,jj)  = zdTwl
251               Hz_wl(ji,jj)  = zHwl
252               Qnt_ac(ji,jj) = zqac ! Updating Qnt_ac, heat integral
253               Tau_ac(ji,jj) = ztac
254            END IF
255
256         END DO
257      END DO
258
259   END SUBROUTINE WL_COARE
260
261
262
263
264   FUNCTION delta_skin_layer( palpha, pQd, pQlat, pustar_a )
265      !!---------------------------------------------------------------------
266      !! Computes the thickness (m) of the viscous skin layer.
267      !! Based on Fairall et al., 1996
268      !!
269      !! Fairall, C. W., Bradley, E. F., Godfrey, J. S., Wick, G. A.,
270      !! Edson, J. B., and Young, G. S. ( 1996), Cool‐skin and warm‐layer
271      !! effects on sea surface temperature, J. Geophys. Res., 101( C1), 1295-1308,
272      !! doi:10.1029/95JC03190.
273      !!
274      !! L. Brodeau, october 2019
275      !!---------------------------------------------------------------------
276      REAL(wp)                :: delta_skin_layer
277      REAL(wp), INTENT(in)    :: palpha   ! thermal expansion coefficient of sea-water (SST accurate enough!)
278      REAL(wp), INTENT(in)    :: pQd    ! < 0 !!! part of the net heat flux actually absorbed in the WL [W/m^2] => term "Q + Rs*fs" in eq.6 of Fairall et al. 1996
279      REAL(wp), INTENT(in)    :: pQlat    ! latent heat flux [W/m^2]
280      REAL(wp), INTENT(in)    :: pustar_a ! friction velocity in the air (u*) [m/s]
281      !!---------------------------------------------------------------------
282      REAL(wp) :: zusw, zusw2, zlamb, zQd, ztf, ztmp
283      !!---------------------------------------------------------------------
284     
285      zQd = pQd + 0.026*MIN(pQlat,0._wp)*rCp0_w/rLevap/palpha   ! LOLO: Double check sign + division by palpha !!! units are okay!
286
287      ztf = 0.5_wp + SIGN(0.5_wp, zQd)  ! Qabs < 0 => cooling of the viscous layer => ztf = 0 (regular case)
288      !                                 ! Qabs > 0 => warming of the viscous layer => ztf = 1 (ex: weak evaporation and strong positive sensible heat flux)
289      !
290      zusw  = MAX(pustar_a, 1.E-4_wp) * sq_radrw    ! u* in the water
291      zusw2 = zusw*zusw
292      !
293      zlamb = 6._wp*( 1._wp + MAX(palpha*rcst_cs/(zusw2*zusw2)*zQd, 0._wp)**0.75 )**(-1./3.) ! see Eq.(14) in Fairall et al., 1996
294      !  => zlamb is not used when Qd > 0, and since rcst_cs < 0, we just use this "MAX" to prevent FPE errors (something_negative)**0.75
295      !
296      ztmp = rnu0_w/zusw
297      delta_skin_layer = (1._wp-ztf) *     zlamb*ztmp           &  ! regular case, Qd < 0, see Eq.(12) in Fairall et al., 1996
298         &               +   ztf     * MIN(6._wp*ztmp , 0.007_wp)  ! when Qd > 0
299   END FUNCTION delta_skin_layer
300
301   !!======================================================================
302END MODULE sbcblk_skin_coare
Note: See TracBrowser for help on using the repository browser.