MOM6
MOM_mixed_layer_restrat.F90
Go to the documentation of this file.
1 !> \brief Parameterization of mixed layer restratification by unresolved mixed-layer eddies.
3 
4 ! This file is part of MOM6. See LICENSE.md for the license.
5 
6 use mom_debugging, only : hchksum
8 use mom_diag_mediator, only : register_diag_field, safe_alloc_ptr, time_type
10 use mom_domains, only : pass_var
11 use mom_error_handler, only : mom_error, fatal, warning
13 use mom_forcing_type, only : forcing
14 use mom_grid, only : ocean_grid_type
15 use mom_hor_index, only : hor_index_type
16 use mom_io, only : vardesc, var_desc
21 use mom_eos, only : calculate_density
22 
23 implicit none ; private
24 
25 #include <MOM_memory.h>
26 
27 public mixedlayer_restrat
30 
31 !> Control structure for mom_mixed_layer_restrat
32 type, public :: mixedlayer_restrat_cs ; private
33  real :: ml_restrat_coef !< A non-dimensional factor by which the
34  !! instability is enhanced over what would be
35  !! predicted based on the resolved gradients. This
36  !! increases with grid spacing^2, up to something
37  !! of order 500.
38  real :: ml_restrat_coef2 !< As for ml_restrat_coef but using the slow filtered MLD.
39  real :: front_length !< If non-zero, is the frontal-length scale used to calculate the
40  !! upscaling of buoyancy gradients that is otherwise represented
41  !! by the parameter FOX_KEMPER_ML_RESTRAT_COEF. If MLE_FRONT_LENGTH is
42  !! non-zero, it is recommended to set FOX_KEMPER_ML_RESTRAT_COEF=1.0.
43  logical :: mle_use_pbl_mld !< If true, use the MLD provided by the PBL parameterization.
44  !! if false, MLE will calculate a MLD based on a density difference
45  !! based on the parameter MLE_DENSITY_DIFF.
46  real :: mle_mld_decay_time !< Time-scale to use in a running-mean when MLD is retreating (s).
47  real :: mle_mld_decay_time2 !< Time-scale to use in a running-mean when filtered MLD is retreating (s).
48  real :: mle_density_diff !< Density difference used in detecting mixed-layer
49  !! depth (kg/m3).
50  real :: mle_tail_dh !< Fraction by which to extend the mixed-layer restratification
51  !! depth used for a smoother stream function at the base of
52  !! the mixed-layer.
53  real :: mle_mld_stretch !< A scaling coefficient for stretching/shrinking the MLD
54  !! used in the MLE scheme. This simply multiplies MLD wherever used.
55  logical :: mle_use_mld_ave_bug !< If true, do not account for MLD mismatch to interface positions.
56  logical :: debug = .false. !< If true, calculate checksums of fields for debugging.
57  type(diag_ctrl), pointer :: diag !< A structure that is used to regulate the
58  !! timing of diagnostic output.
59 
60  real, dimension(:,:), pointer :: &
61  mld_filtered => null(), & !< Time-filtered MLD (H units)
62  mld_filtered_slow => null() !< Slower time-filtered MLD (H units)
63 
64  !>@{
65  !! Diagnostic identifier
66  integer :: id_urestrat_time = -1
67  integer :: id_vrestrat_time = -1
68  integer :: id_uhml = -1
69  integer :: id_vhml = -1
70  integer :: id_mld = -1
71  integer :: id_rml = -1
72  integer :: id_udml = -1
73  integer :: id_vdml = -1
74  integer :: id_uml = -1
75  integer :: id_vml = -1
76  !>@}
77 
78 end type mixedlayer_restrat_cs
79 
80 character(len=40) :: mdl = "MOM_mixed_layer_restrat" !< This module's name.
81 
82 contains
83 
84 !> Driver for the mixed-layer restratification parameterization.
85 !! The code branches between two different implementations depending
86 !! on whether the bulk-mixed layer or a general coordinate are in use.
87 subroutine mixedlayer_restrat(h, uhtr, vhtr, tv, fluxes, dt, MLD, VarMix, G, GV, CS)
88  type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure
89  type(verticalgrid_type), intent(in) :: GV !< Ocean vertical grid structure
90  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: h !< Layer thickness (H units = m or kg/m2)
91  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: uhtr !< Accumulated zonal mass flux (m3 or kg)
92  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: vhtr !< Accumulated meridional mass flux (m3 or kg)
93  type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamic variables structure
94  type(forcing), intent(in) :: fluxes !< Pointers to forcing fields
95  real, intent(in) :: dt !< Time increment (sec)
96  real, dimension(:,:), pointer :: MLD !< Mixed layer depth provided by PBL scheme (H units)
97  type(varmix_cs), pointer :: VarMix !< Container for derived fields
98  type(mixedlayer_restrat_cs), pointer :: CS !< Module control structure
99 
100  if (.not. associated(cs)) call mom_error(fatal, "MOM_mixedlayer_restrat: "// &
101  "Module must be initialized before it is used.")
102 
103  if (gv%nkml>0) then
104  call mixedlayer_restrat_bml(h, uhtr, vhtr, tv, fluxes, dt, g, gv, cs)
105  else
106  call mixedlayer_restrat_general(h, uhtr, vhtr, tv, fluxes, dt, mld, varmix, g, gv, cs)
107  endif
108 
109 end subroutine mixedlayer_restrat
110 
111 !> Calculates a restratifying flow in the mixed layer.
112 subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, fluxes, dt, MLD_in, VarMix, G, GV, CS)
113  ! Arguments
114  type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure
115  type(verticalgrid_type), intent(in) :: GV !< Ocean vertical grid structure
116  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: h !< Layer thickness (H units = m or kg/m2)
117  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: uhtr !< Accumulated zonal mass flux (m3 or kg)
118  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: vhtr !< Accumulated meridional mass flux (m3 or kg)
119  type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamic variables structure
120  type(forcing), intent(in) :: fluxes !< Pointers to forcing fields
121  real, intent(in) :: dt !< Time increment (sec)
122  real, dimension(:,:), pointer :: MLD_in !< Mixed layer depth provided by PBL scheme (H units)
123  type(varmix_cs), pointer :: VarMix !< Container for derived fields
124  type(mixedlayer_restrat_cs), pointer :: CS !< Module control structure
125  ! Local variables
126  real :: uhml(szib_(g),szj_(g),szk_(g)) ! zonal mixed layer transport (m3/s or kg/s)
127  real :: vhml(szi_(g),szjb_(g),szk_(g)) ! merid mixed layer transport (m3/s or kg/s)
128  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: &
129  h_avail ! The volume available for diffusion out of each face of each
130  ! sublayer of the mixed layer, divided by dt, in units
131  ! of H * m2 s-1 (i.e., m3 s-1 or kg s-1).
132  real, dimension(SZI_(G),SZJ_(G)) :: &
133  MLD_fast, & ! Mixed layer depth actually used in MLE restratification parameterization (H units)
134  htot_fast, & ! The sum of the thicknesses of layers in the mixed layer (H units)
135  Rml_av_fast, & ! g_Rho0 times the average mixed layer density (m s-2)
136  MLD_slow, & ! Mixed layer depth actually used in MLE restratification parameterization (H units)
137  htot_slow, & ! The sum of the thicknesses of layers in the mixed layer (H units)
138  Rml_av_slow ! g_Rho0 times the average mixed layer density (m s-2)
139  real :: g_Rho0 ! G_Earth/Rho0 (m4 s-2 kg-1)
140  real :: rho_ml(szi_(g)) ! Potential density relative to the surface (kg m-3)
141  real :: p0(szi_(g)) ! A pressure of 0 (Pa)
142 
143  real :: h_vel ! htot interpolated onto velocity points in metre (not H).
144  real :: absf ! absolute value of f, interpolated to velocity points (s-1)
145  real :: u_star ! surface friction velocity, interpolated to velocity points (m s-1)
146  real :: mom_mixrate ! rate at which momentum is homogenized within mixed layer (s-1)
147  real :: timescale ! mixing growth timescale (sec)
148  real :: h_neglect ! tiny thickness usually lost in roundoff so can be neglected (H units)
149  real :: dz_neglect ! A tiny thickness (in m) that is usually lost in roundoff so can be neglected
150  real :: I4dt ! 1/(4 dt) (sec-1)
151  real :: Ihtot,Ihtot_slow! total mixed layer thickness
152  real :: a(szk_(g)) ! A non-dimensional value relating the overall flux
153  ! magnitudes (uDml & vDml) to the realized flux in a
154  ! layer. The vertical sum of a() through the pieces of
155  ! the mixed layer must be 0.
156  real :: b(szk_(g)) ! As for a(k) but for the slow-filtered MLD
157  real :: uDml(szib_(g)) ! The zonal and meridional volume fluxes in the upper
158  real :: vDml(szi_(g)) ! half of the mixed layer in H m2 s-1 (m3 s-1 or kg s-1).
159  real :: uDml_slow(szib_(g)) ! The zonal and meridional volume fluxes in the upper
160  real :: vDml_slow(szi_(g)) ! half of the mixed layer in H m2 s-1 (m3 s-1 or kg s-1).
161  real :: utimescale_diag(szib_(g),szj_(g)) ! restratification timescales
162  real :: vtimescale_diag(szi_(g),szjb_(g)) ! in the zonal and meridional
163  ! directions, in s, stored in 2-D
164  ! arrays for diagnostic purposes.
165  real :: uDml_diag(szib_(g),szj_(g)), vDml_diag(szi_(g),szjb_(g))
166  integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz
167  real, dimension(SZI_(G)) :: rhoSurf, deltaRhoAtKm1, deltaRhoAtK, dK, dKm1, pRef_MLD ! Used for MLD
168  real, dimension(SZI_(G)) :: rhoAtK, rho1, d1, pRef_N2 ! Used for N2
169  real :: aFac, bFac, ddRho
170  real :: hAtVel, zpa, zpb, dh, res_scaling_fac, I_l_f
171  logical :: proper_averaging, line_is_empty, keep_going, res_upscale
172 
173  real :: PSI, PSI1, z, BOTTOP, XP, DD ! For the following statement functions
174  ! Stream function as a function of non-dimensional position within mixed-layer (F77 statement function)
175  !PSI1(z) = max(0., (1. - (2.*z+1.)**2 ) )
176  psi1(z) = max(0., (1. - (2.*z+1.)**2 ) * (1. + (5./21.)*(2.*z+1.)**2) )
177  bottop(z) = 0.5*(1.-sign(1.,z+0.5)) ! =0 for z>-0.5, =1 for z<-0.5
178  xp(z) = max(0., min(1., (-z-0.5)*2./(1.+2.*cs%MLE_tail_dh) ) )
179  dd(z) = (1.-3.*(xp(z)**2)+2.*(xp(z)**3))**(1.+2.*cs%MLE_tail_dh)
180  psi(z) = max( psi1(z), dd(z)*bottop(z) ) ! Combines original PSI1 with tail
181 
182  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
183  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
184 
185  if (.not.associated(tv%eqn_of_state)) call mom_error(fatal, "MOM_mixedlayer_restrat: "// &
186  "An equation of state must be used with this module.")
187  if (.not.associated(varmix) .and. cs%front_length>0.) call mom_error(fatal, "MOM_mixedlayer_restrat: "// &
188  "The resolution argument, Rd/dx, was not associated.")
189 
190  if (cs%MLE_density_diff > 0.) then ! We need to calculate a mixed layer depth, MLD.
191  !! TODO: use derivatives and mid-MLD pressure. Currently this is sigma-0. -AJA
192  pref_mld(:) = 0.
193  do j = js-1, je+1
194  dk(:) = 0.5 * h(:,j,1) * gv%H_to_m ! Depth of center of surface layer
195  call calculate_density(tv%T(:,j,1), tv%S(:,j,1), pref_mld, rhosurf, is-1, ie-is+3, tv%eqn_of_state)
196  deltarhoatk(:) = 0.
197  mld_fast(:,j) = 0.
198  do k = 2, nz
199  dkm1(:) = dk(:) ! Depth of center of layer K-1
200  dk(:) = dk(:) + 0.5 * ( h(:,j,k) + h(:,j,k-1) ) * gv%H_to_m ! Depth of center of layer K
201  ! Mixed-layer depth, using sigma-0 (surface reference pressure)
202  deltarhoatkm1(:) = deltarhoatk(:) ! Store value from previous iteration of K
203  call calculate_density(tv%T(:,j,k), tv%S(:,j,k), pref_mld, deltarhoatk, is-1, ie-is+3, tv%eqn_of_state)
204  deltarhoatk(:) = deltarhoatk(:) - rhosurf(:) ! Density difference between layer K and surface
205  do i = is-1, ie+1
206  ddrho = deltarhoatk(i) - deltarhoatkm1(i)
207  if ((mld_fast(i,j)==0.) .and. (ddrho>0.) .and. &
208  (deltarhoatkm1(i)<cs%MLE_density_diff) .and. (deltarhoatk(i)>=cs%MLE_density_diff)) then
209  afac = ( cs%MLE_density_diff - deltarhoatkm1(i) ) / ddrho
210  mld_fast(i,j) = dk(i) * afac + dkm1(i) * (1. - afac)
211  endif
212  enddo ! i-loop
213  enddo ! k-loop
214  do i = is-1, ie+1
215  mld_fast(i,j) = cs%MLE_MLD_stretch * mld_fast(i,j)
216  if ((mld_fast(i,j)==0.) .and. (deltarhoatk(i)<cs%MLE_density_diff)) mld_fast(i,j) = dk(i) ! Assume mixing to the bottom
217  enddo
218  enddo ! j-loop
219  elseif (cs%MLE_use_PBL_MLD) then
220  if (.not. associated(mld_in)) call mom_error(fatal, "MOM_mixedlayer_restrat: "// &
221  "Argument MLD_in was not associated!")
222  do j = js-1, je+1 ; do i = is-1, ie+1
223  mld_fast(i,j) = cs%MLE_MLD_stretch * mld_in(i,j)
224  enddo ; enddo
225  else
226  call mom_error(fatal, "MOM_mixedlayer_restrat: "// &
227  "No MLD to use for MLE parameterization.")
228  endif
229 
230  ! Apply time filter (to remove diurnal cycle)
231  if (cs%MLE_MLD_decay_time>0.) then
232  if (cs%debug) then
233  call hchksum(cs%MLD_filtered,'mixed_layer_restrat: MLD_filtered',g%HI,haloshift=1)
234  call hchksum(mld_in,'mixed_layer_restrat: MLD in',g%HI,haloshift=1)
235  endif
236  afac = cs%MLE_MLD_decay_time / ( dt + cs%MLE_MLD_decay_time )
237  bfac = dt / ( dt + cs%MLE_MLD_decay_time )
238  do j = js-1, je+1 ; do i = is-1, ie+1
239  ! Expression bFac*MLD_fast(i,j) + aFac*CS%MLD_filtered(i,j) is the time-filtered
240  ! (running mean) of MLD. The max() allows the "running mean" to be reset
241  ! instantly to a deeper MLD.
242  cs%MLD_filtered(i,j) = max( mld_fast(i,j), bfac*mld_fast(i,j) + afac*cs%MLD_filtered(i,j) )
243  mld_fast(i,j) = cs%MLD_filtered(i,j)
244  enddo ; enddo
245  endif
246 
247  ! Apply slower time filter (to remove seasonal cycle) on already filtered MLD_fast
248  if (cs%MLE_MLD_decay_time2>0.) then
249  if (cs%debug) then
250  call hchksum(cs%MLD_filtered_slow,'mixed_layer_restrat: MLD_filtered_slow',g%HI,haloshift=1)
251  call hchksum(mld_fast,'mixed_layer_restrat: MLD fast',g%HI,haloshift=1)
252  endif
253  afac = cs%MLE_MLD_decay_time2 / ( dt + cs%MLE_MLD_decay_time2 )
254  bfac = dt / ( dt + cs%MLE_MLD_decay_time2 )
255  do j = js-1, je+1 ; do i = is-1, ie+1
256  ! Expression bFac*MLD_fast(i,j) + aFac*CS%MLD_filtered(i,j) is the time-filtered
257  ! (running mean) of MLD. The max() allows the "running mean" to be reset
258  ! instantly to a deeper MLD.
259  cs%MLD_filtered_slow(i,j) = max( mld_fast(i,j), bfac*mld_fast(i,j) + afac*cs%MLD_filtered_slow(i,j) )
260  mld_slow(i,j) = cs%MLD_filtered_slow(i,j)
261  enddo ; enddo
262  else
263  do j = js-1, je+1 ; do i = is-1, ie+1
264  mld_slow(i,j) = mld_fast(i,j)
265  enddo ; enddo
266  endif
267 
268  udml(:) = 0.0 ; vdml(:) = 0.0
269  udml_slow(:) = 0.0 ; vdml_slow(:) = 0.0
270  i4dt = 0.25 / dt
271  g_rho0 = gv%g_Earth/gv%Rho0
272  h_neglect = gv%H_subroundoff
273  dz_neglect = gv%H_subroundoff*gv%H_to_m
274  proper_averaging = .not. cs%MLE_use_MLD_ave_bug
275  if (cs%front_length>0.) then
276  res_upscale = .true.
277  i_l_f = 1./cs%front_length
278  else
279  res_upscale = .false.
280  endif
281 
282  p0(:) = 0.0
283 !$OMP parallel default(none) shared(is,ie,js,je,G,GV,htot_fast,Rml_av_fast,tv,p0,h,h_avail,&
284 !$OMP h_neglect,g_Rho0,I4dt,CS,uhml,uhtr,dt,vhml,vhtr, &
285 !$OMP utimescale_diag,vtimescale_diag,fluxes,dz_neglect, &
286 !$OMP htot_slow,MLD_slow,Rml_av_slow,VarMix,I_l_f, &
287 !$OMP res_upscale, &
288 !$OMP nz,MLD_fast,uDml_diag,vDml_diag,proper_averaging) &
289 !$OMP private(rho_ml,h_vel,u_star,absf,mom_mixrate,timescale, &
290 !$OMP line_is_empty, keep_going,res_scaling_fac, &
291 !$OMP a,IhTot,b,Ihtot_slow,zpb,hAtVel,zpa,dh) &
292 !$OMP firstprivate(uDml,vDml,uDml_slow,vDml_slow)
293 !$OMP do
294  do j=js-1,je+1
295  do i=is-1,ie+1
296  htot_fast(i,j) = 0.0 ; rml_av_fast(i,j) = 0.0
297  htot_slow(i,j) = 0.0 ; rml_av_slow(i,j) = 0.0
298  enddo
299  keep_going = .true.
300  do k=1,nz
301  do i=is-1,ie+1
302  h_avail(i,j,k) = max(i4dt*g%areaT(i,j)*(h(i,j,k)-gv%Angstrom),0.0)
303  enddo
304  if (keep_going) then
305  call calculate_density(tv%T(:,j,k),tv%S(:,j,k),p0,rho_ml(:),is-1,ie-is+3,tv%eqn_of_state)
306  line_is_empty = .true.
307  do i=is-1,ie+1
308  if (htot_fast(i,j) < mld_fast(i,j)) then
309  dh = h(i,j,k)
310  if (proper_averaging) dh = min( h(i,j,k), mld_fast(i,j)-htot_fast(i,j) )
311  rml_av_fast(i,j) = rml_av_fast(i,j) + dh*rho_ml(i)
312  htot_fast(i,j) = htot_fast(i,j) + dh
313  line_is_empty = .false.
314  endif
315  if (htot_slow(i,j) < mld_slow(i,j)) then
316  dh = min( h(i,j,k), mld_slow(i,j)-htot_slow(i,j) )
317  rml_av_slow(i,j) = rml_av_slow(i,j) + dh*rho_ml(i)
318  htot_slow(i,j) = htot_slow(i,j) + dh
319  line_is_empty = .false.
320  endif
321  enddo
322  if (line_is_empty) keep_going=.false.
323  endif
324  enddo
325 
326  do i=is-1,ie+1
327  rml_av_fast(i,j) = -(g_rho0*rml_av_fast(i,j)) / (htot_fast(i,j) + h_neglect)
328  rml_av_slow(i,j) = -(g_rho0*rml_av_slow(i,j)) / (htot_slow(i,j) + h_neglect)
329  enddo
330  enddo
331 
332  if (cs%debug) then
333  call hchksum(h,'mixed_layer_restrat: h',g%HI,haloshift=1)
334  call hchksum(fluxes%ustar,'mixed_layer_restrat: u*',g%HI,haloshift=1)
335  call hchksum(mld_fast,'mixed_layer_restrat: MLD',g%HI,haloshift=1)
336  call hchksum(rml_av_fast,'mixed_layer_restrat: rml',g%HI,haloshift=1)
337  endif
338 
339 ! TO DO:
340 ! 1. Mixing extends below the mixing layer to the mixed layer. Find it!
341 ! 2. Add exponential tail to stream-function?
342 
343 ! U - Component
344 !$OMP do
345  do j=js,je ; do i=is-1,ie
346  u_star = 0.5*(fluxes%ustar(i,j) + fluxes%ustar(i+1,j))
347  absf = 0.5*(abs(g%CoriolisBu(i,j-1)) + abs(g%CoriolisBu(i,j)))
348  ! If needed, res_scaling_fac = min( ds, L_d ) / l_f
349  if (res_upscale) res_scaling_fac = &
350  ( sqrt( 0.5 * ( g%dxCu(i,j)**2 + g%dyCu(i,j)**2 ) ) * i_l_f ) &
351  * min( 1., 0.5*( varmix%Rd_dx_h(i,j) + varmix%Rd_dx_h(i+1,j) ) )
352 
353  ! peak ML visc: u_star * 0.41 * (h_ml*u_star)/(absf*h_ml + 4.0*u_star)
354  ! momentum mixing rate: pi^2*visc/h_ml^2
355  ! 0.41 is the von Karmen constant, 9.8696 = pi^2.
356  h_vel = 0.5*((htot_fast(i,j) + htot_fast(i+1,j)) + h_neglect) * gv%H_to_m
357  mom_mixrate = (0.41*9.8696)*u_star**2 / &
358  (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star)
359  timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2)
360  timescale = timescale * cs%ml_restrat_coef
361  if (res_upscale) timescale = timescale * res_scaling_fac
362  udml(i) = timescale * g%mask2dCu(i,j)*g%dyCu(i,j)* &
363  g%IdxCu(i,j)*(rml_av_fast(i+1,j)-rml_av_fast(i,j)) * (h_vel**2 * gv%m_to_H)
364  ! As above but using the slow filtered MLD
365  h_vel = 0.5*((htot_slow(i,j) + htot_slow(i+1,j)) + h_neglect) * gv%H_to_m
366  mom_mixrate = (0.41*9.8696)*u_star**2 / &
367  (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star)
368  timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2)
369  timescale = timescale * cs%ml_restrat_coef2
370  if (res_upscale) timescale = timescale * res_scaling_fac
371  udml_slow(i) = timescale * g%mask2dCu(i,j)*g%dyCu(i,j)* &
372  g%IdxCu(i,j)*(rml_av_slow(i+1,j)-rml_av_slow(i,j)) * (h_vel**2 * gv%m_to_H)
373 
374  if (udml(i) + udml_slow(i) == 0.) then
375  do k=1,nz ; uhml(i,j,k) = 0.0 ; enddo
376  else
377  ihtot = 2.0 / ((htot_fast(i,j) + htot_fast(i+1,j)) + h_neglect)
378  ihtot_slow = 2.0 / ((htot_slow(i,j) + htot_slow(i+1,j)) + h_neglect)
379  zpa = 0.0 ; zpb = 0.0
380  ! a(k) relates the sublayer transport to uDml with a linear profile.
381  ! The sum of a(k) through the mixed layers must be 0.
382  do k=1,nz
383  hatvel = 0.5*(h(i,j,k) + h(i+1,j,k))
384  a(k) = psi(zpa) ! Psi(z/MLD) for upper interface
385  zpa = zpa - (hatvel * ihtot) ! z/H for lower interface
386  a(k) = a(k) - psi(zpa) ! Transport profile
387  ! Limit magnitude (uDml) if it would violate CFL
388  if (a(k)*udml(i) > 0.0) then
389  if (a(k)*udml(i) > h_avail(i,j,k)) udml(i) = h_avail(i,j,k) / a(k)
390  elseif (a(k)*udml(i) < 0.0) then
391  if (-a(k)*udml(i) > h_avail(i+1,j,k)) udml(i) = -h_avail(i+1,j,k) / a(k)
392  endif
393  enddo
394  do k=1,nz
395  ! Transport for slow-filtered MLD
396  hatvel = 0.5*(h(i,j,k) + h(i+1,j,k))
397  b(k) = psi(zpb) ! Psi(z/MLD) for upper interface
398  zpb = zpb - (hatvel * ihtot_slow) ! z/H for lower interface
399  b(k) = b(k) - psi(zpb) ! Transport profile
400  ! Limit magnitude (uDml_slow) if it would violate CFL when added to uDml
401  if (b(k)*udml_slow(i) > 0.0) then
402  if (b(k)*udml_slow(i) > h_avail(i,j,k) - a(k)*udml(i)) &
403  udml_slow(i) = max( 0., h_avail(i,j,k) - a(k)*udml(i) ) / b(k)
404  elseif (b(k)*udml_slow(i) < 0.0) then
405  if (-b(k)*udml_slow(i) > h_avail(i+1,j,k) + a(k)*udml(i)) &
406  udml_slow(i) = -max( 0., h_avail(i+1,j,k) + a(k)*udml(i) ) / b(k)
407  endif
408  enddo
409  do k=1,nz
410  uhml(i,j,k) = a(k)*udml(i) + b(k)*udml_slow(i)
411  uhtr(i,j,k) = uhtr(i,j,k) + uhml(i,j,k)*dt
412  enddo
413  endif
414 
415  utimescale_diag(i,j) = timescale
416  udml_diag(i,j) = udml(i)
417  enddo ; enddo
418 
419 ! V- component
420 !$OMP do
421  do j=js-1,je ; do i=is,ie
422  u_star = 0.5*(fluxes%ustar(i,j) + fluxes%ustar(i,j+1))
423  absf = 0.5*(abs(g%CoriolisBu(i-1,j)) + abs(g%CoriolisBu(i,j)))
424  ! If needed, res_scaling_fac = min( ds, L_d ) / l_f
425  if (res_upscale) res_scaling_fac = &
426  ( sqrt( 0.5 * ( g%dxCv(i,j)**2 + g%dyCv(i,j)**2 ) ) * i_l_f ) &
427  * min( 1., 0.5*( varmix%Rd_dx_h(i,j) + varmix%Rd_dx_h(i,j+1) ) )
428 
429  ! peak ML visc: u_star * 0.41 * (h_ml*u_star)/(absf*h_ml + 4.0*u_star)
430  ! momentum mixing rate: pi^2*visc/h_ml^2
431  ! 0.41 is the von Karmen constant, 9.8696 = pi^2.
432  h_vel = 0.5*((htot_fast(i,j) + htot_fast(i,j+1)) + h_neglect) * gv%H_to_m
433  mom_mixrate = (0.41*9.8696)*u_star**2 / &
434  (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star)
435  timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2)
436  timescale = timescale * cs%ml_restrat_coef
437  if (res_upscale) timescale = timescale * res_scaling_fac
438  vdml(i) = timescale * g%mask2dCv(i,j)*g%dxCv(i,j)* &
439  g%IdyCv(i,j)*(rml_av_fast(i,j+1)-rml_av_fast(i,j)) * (h_vel**2 * gv%m_to_H)
440  ! As above but using the slow filtered MLD
441  h_vel = 0.5*((htot_slow(i,j) + htot_slow(i,j+1)) + h_neglect) * gv%H_to_m
442  mom_mixrate = (0.41*9.8696)*u_star**2 / &
443  (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star)
444  timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2)
445  timescale = timescale * cs%ml_restrat_coef2
446  if (res_upscale) timescale = timescale * res_scaling_fac
447  vdml_slow(i) = timescale * g%mask2dCv(i,j)*g%dxCv(i,j)* &
448  g%IdyCv(i,j)*(rml_av_slow(i,j+1)-rml_av_slow(i,j)) * (h_vel**2 * gv%m_to_H)
449 
450  if (vdml(i) + vdml_slow(i) == 0.) then
451  do k=1,nz ; vhml(i,j,k) = 0.0 ; enddo
452  else
453  ihtot = 2.0 / ((htot_fast(i,j) + htot_fast(i,j+1)) + h_neglect)
454  ihtot_slow = 2.0 / ((htot_slow(i,j) + htot_slow(i,j+1)) + h_neglect)
455  zpa = 0.0 ; zpb = 0.0
456  ! a(k) relates the sublayer transport to vDml with a linear profile.
457  ! The sum of a(k) through the mixed layers must be 0.
458  do k=1,nz
459  hatvel = 0.5*(h(i,j,k) + h(i,j+1,k))
460  a(k) = psi( zpa ) ! Psi(z/MLD) for upper interface
461  zpa = zpa - (hatvel * ihtot) ! z/H for lower interface
462  a(k) = a(k) - psi( zpa ) ! Transport profile
463  ! Limit magnitude (vDml) if it would violate CFL
464  if (a(k)*vdml(i) > 0.0) then
465  if (a(k)*vdml(i) > h_avail(i,j,k)) vdml(i) = h_avail(i,j,k) / a(k)
466  elseif (a(k)*vdml(i) < 0.0) then
467  if (-a(k)*vdml(i) > h_avail(i,j+1,k)) vdml(i) = -h_avail(i,j+1,k) / a(k)
468  endif
469  enddo
470  do k=1,nz
471  ! Transport for slow-filtered MLD
472  hatvel = 0.5*(h(i,j,k) + h(i,j+1,k))
473  b(k) = psi(zpb) ! Psi(z/MLD) for upper interface
474  zpb = zpb - (hatvel * ihtot_slow) ! z/H for lower interface
475  b(k) = b(k) - psi(zpb) ! Transport profile
476  ! Limit magnitude (vDml_slow) if it would violate CFL when added to vDml
477  if (b(k)*vdml_slow(i) > 0.0) then
478  if (b(k)*vdml_slow(i) > h_avail(i,j,k) - a(k)*vdml(i)) &
479  vdml_slow(i) = max( 0., h_avail(i,j,k) - a(k)*vdml(i) ) / b(k)
480  elseif (b(k)*vdml_slow(i) < 0.0) then
481  if (-b(k)*vdml_slow(i) > h_avail(i,j+1,k) + a(k)*vdml(i)) &
482  vdml_slow(i) = -max( 0., h_avail(i,j+1,k) + a(k)*vdml(i) ) / b(k)
483  endif
484  enddo
485  do k=1,nz
486  vhml(i,j,k) = a(k)*vdml(i) + b(k)*vdml_slow(i)
487  vhtr(i,j,k) = vhtr(i,j,k) + vhml(i,j,k)*dt
488  enddo
489  endif
490 
491  vtimescale_diag(i,j) = timescale
492  vdml_diag(i,j) = vdml(i)
493  enddo ; enddo
494 
495 !$OMP do
496  do j=js,je ; do k=1,nz ; do i=is,ie
497  h(i,j,k) = h(i,j,k) - dt*g%IareaT(i,j) * &
498  ((uhml(i,j,k) - uhml(i-1,j,k)) + (vhml(i,j,k) - vhml(i,j-1,k)))
499  enddo ; enddo ; enddo
500 !$OMP end parallel
501 
502  ! Offer diagnostic fields for averaging.
503  if (query_averaging_enabled(cs%diag)) then
504  if (cs%id_urestrat_time > 0) call post_data(cs%id_urestrat_time, utimescale_diag, cs%diag)
505  if (cs%id_vrestrat_time > 0) call post_data(cs%id_vrestrat_time, vtimescale_diag, cs%diag)
506  if (cs%id_uhml > 0) call post_data(cs%id_uhml, uhml, cs%diag)
507  if (cs%id_vhml > 0) call post_data(cs%id_vhml, vhml, cs%diag)
508  if (cs%id_MLD > 0) call post_data(cs%id_MLD, mld_fast, cs%diag)
509  if (cs%id_Rml > 0) call post_data(cs%id_Rml, rml_av_fast, cs%diag)
510  if (cs%id_uDml > 0) call post_data(cs%id_uDml, udml_diag, cs%diag)
511  if (cs%id_vDml > 0) call post_data(cs%id_vDml, vdml_diag, cs%diag)
512 
513  if (cs%id_uml > 0) then
514  do j=js,je ; do i=is,ie
515  h_vel = 0.5*((htot_fast(i,j) + htot_fast(i+1,j)) + h_neglect)
516  udml_diag(i,j) = udml_diag(i,j) / (0.01*h_vel) * g%IdyCu(i,j) * (psi(0.)-psi(-.01))
517  enddo ; enddo
518  call post_data(cs%id_uml, udml_diag, cs%diag)
519  endif
520  if (cs%id_vml > 0) then
521  do j=js,je ; do i=is,ie
522  h_vel = 0.5*((htot_fast(i,j) + htot_fast(i,j+1)) + h_neglect)
523  vdml_diag(i,j) = vdml_diag(i,j) / (0.01*h_vel) * g%IdxCv(i,j) * (psi(0.)-psi(-.01))
524  enddo ; enddo
525  call post_data(cs%id_vml, vdml_diag, cs%diag)
526  endif
527  endif
528  ! Whenever thickness changes let the diag manager know, target grids
529  ! for vertical remapping may need to be regenerated.
530  ! This needs to happen after the H update and before the next post_data.
531  call diag_update_remap_grids(cs%diag)
532 
533 end subroutine mixedlayer_restrat_general
534 
535 
536 !> Calculates a restratifying flow assuming a 2-layer bulk mixed layer.
537 subroutine mixedlayer_restrat_bml(h, uhtr, vhtr, tv, fluxes, dt, G, GV, CS)
538  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
539  type(verticalgrid_type), intent(in) :: GV !< Ocean vertical grid structure
540  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: h !< Layer thickness (H units = m or kg/m2)
541  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: uhtr !< Accumulated zonal mass flux (m3 or kg)
542  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: vhtr !< Accumulated meridional mass flux (m3 or kg)
543  type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamic variables structure
544  type(forcing), intent(in) :: fluxes !< Pointers to forcing fields
545  real, intent(in) :: dt !< Time increment (sec)
546  type(mixedlayer_restrat_cs), pointer :: CS !< Module control structure
547  ! Local variables
548  real :: uhml(szib_(g),szj_(g),szk_(g)) ! zonal mixed layer transport (m3/s or kg/s)
549  real :: vhml(szi_(g),szjb_(g),szk_(g)) ! merid mixed layer transport (m3/s or kg/s)
550  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: &
551  h_avail ! The volume available for diffusion out of each face of each
552  ! sublayer of the mixed layer, divided by dt, in units
553  ! of H m2 s-1 (i.e., m3 s-1 or kg s-1).
554  real, dimension(SZI_(G),SZJ_(G)) :: &
555  htot, & ! The sum of the thicknesses of layers in the mixed layer (H units)
556  Rml_av ! g_Rho0 times the average mixed layer density (m s-2)
557  real :: g_Rho0 ! G_Earth/Rho0 (m4 s-2 kg-1)
558  real :: Rho0(szi_(g)) ! Potential density relative to the surface (kg m-3)
559  real :: p0(szi_(g)) ! A pressure of 0 (Pa)
560 
561  real :: h_vel ! htot interpolated onto velocity points (meter; not H)
562  real :: absf ! absolute value of f, interpolated to velocity points (s-1)
563  real :: u_star ! surface friction velocity, interpolated to velocity points (m s-1)
564  real :: mom_mixrate ! rate at which momentum is homogenized within mixed layer (s-1)
565  real :: timescale ! mixing growth timescale (sec)
566  real :: h_neglect ! tiny thickness usually lost in roundoff and can be neglected (H units)
567  real :: dz_neglect ! tiny thickness (in m) that usually lost in roundoff and can be neglected (meter)
568  real :: I4dt ! 1/(4 dt)
569  real :: I2htot ! Twice the total mixed layer thickness at velocity points (H units)
570  real :: z_topx2 ! depth of the top of a layer at velocity points (H units)
571  real :: hx2 ! layer thickness at velocity points (H units)
572  real :: a(szk_(g)) ! A non-dimensional value relating the overall flux
573  ! magnitudes (uDml & vDml) to the realized flux in a
574  ! layer. The vertical sum of a() through the pieces of
575  ! the mixed layer must be 0.
576  real :: uDml(szib_(g)) ! The zonal and meridional volume fluxes in the upper
577  real :: vDml(szi_(g)) ! half of the mixed layer in H m2 s-1 (m3 s-1 or kg s-1).
578  real :: utimescale_diag(szib_(g),szj_(g)) ! The restratification timescales
579  real :: vtimescale_diag(szi_(g),szjb_(g)) ! in the zonal and meridional
580  ! directions (sec), stored in 2-D
581  ! arrays for diagnostic purposes.
582  real :: uDml_diag(szib_(g),szj_(g)), vDml_diag(szi_(g),szjb_(g))
583  logical :: use_EOS ! If true, density is calculated from T & S using an equation of state.
584 
585  integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, nkml
586  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
587  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB ; nkml = gv%nkml
588 
589  if (.not. associated(cs)) call mom_error(fatal, "MOM_mixedlayer_restrat: "// &
590  "Module must be initialized before it is used.")
591  if ((nkml<2) .or. (cs%ml_restrat_coef<=0.0)) return
592 
593  udml(:) = 0.0 ; vdml(:) = 0.0
594  i4dt = 0.25 / dt
595  g_rho0 = gv%g_Earth/gv%Rho0
596  use_eos = associated(tv%eqn_of_state)
597  h_neglect = gv%H_subroundoff
598  dz_neglect = gv%H_subroundoff*gv%H_to_m
599 
600  if (.not.use_eos) call mom_error(fatal, "MOM_mixedlayer_restrat: "// &
601  "An equation of state must be used with this module.")
602 
603  ! Fix this later for nkml >= 3.
604 
605  p0(:) = 0.0
606 !$OMP parallel default(none) shared(is,ie,js,je,G,GV,htot,Rml_av,tv,p0,h,h_avail, &
607 !$OMP h_neglect,g_Rho0,I4dt,CS,uhml,uhtr,dt,vhml,vhtr, &
608 !$OMP utimescale_diag,vtimescale_diag,fluxes,dz_neglect, &
609 !$OMP uDml_diag,vDml_diag,nkml) &
610 !$OMP private(Rho0,h_vel,u_star,absf,mom_mixrate,timescale, &
611 !$OMP I2htot,z_topx2,hx2,a) &
612 !$OMP firstprivate(uDml,vDml)
613 !$OMP do
614  do j=js-1,je+1
615  do i=is-1,ie+1
616  htot(i,j) = 0.0 ; rml_av(i,j) = 0.0
617  enddo
618  do k=1,nkml
619  call calculate_density(tv%T(:,j,k),tv%S(:,j,k),p0,rho0(:),is-1,ie-is+3,tv%eqn_of_state)
620  do i=is-1,ie+1
621  rml_av(i,j) = rml_av(i,j) + h(i,j,k)*rho0(i)
622  htot(i,j) = htot(i,j) + h(i,j,k)
623  h_avail(i,j,k) = max(i4dt*g%areaT(i,j)*(h(i,j,k)-gv%Angstrom),0.0)
624  enddo
625  enddo
626 
627  do i=is-1,ie+1
628  rml_av(i,j) = (g_rho0*rml_av(i,j)) / (htot(i,j) + h_neglect)
629  enddo
630  enddo
631 
632 ! TO DO:
633 ! 1. Mixing extends below the mixing layer to the mixed layer. Find it!
634 ! 2. Add exponential tail to stream-function?
635 
636 ! U - Component
637 !$OMP do
638  do j=js,je
639  do i=is,ie ; utimescale_diag(i,j) = 0.0 ; enddo
640  do i=is,ie ; vtimescale_diag(i,j) = 0.0 ; enddo
641  do i=is-1,ie
642  h_vel = 0.5*(htot(i,j) + htot(i+1,j)) * gv%H_to_m
643 
644  u_star = 0.5*(fluxes%ustar(i,j) + fluxes%ustar(i+1,j))
645  absf = 0.5*(abs(g%CoriolisBu(i,j-1)) + abs(g%CoriolisBu(i,j)))
646  ! peak ML visc: u_star * 0.41 * (h_ml*u_star)/(absf*h_ml + 4.0*u_star)
647  ! momentum mixing rate: pi^2*visc/h_ml^2
648  ! 0.41 is the von Karmen constant, 9.8696 = pi^2.
649  mom_mixrate = (0.41*9.8696)*u_star**2 / &
650  (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star)
651  timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2)
652 
653  timescale = timescale * cs%ml_restrat_coef
654 ! timescale = timescale*(2?)*(L_def/L_MLI)*min(EKE/MKE,1.0 + G%dyCv(i,j)**2/L_def**2))
655 
656  utimescale_diag(i,j) = timescale
657 
658  udml(i) = timescale * g%mask2dCu(i,j)*g%dyCu(i,j)* &
659  g%IdxCu(i,j)*(rml_av(i+1,j)-rml_av(i,j)) * (h_vel**2 * gv%m_to_H)
660 
661  if (udml(i) == 0) then
662  do k=1,nkml ; uhml(i,j,k) = 0.0 ; enddo
663  else
664  i2htot = 1.0 / (htot(i,j) + htot(i+1,j) + h_neglect)
665  z_topx2 = 0.0
666  ! a(k) relates the sublayer transport to uDml with a linear profile.
667  ! The sum of a(k) through the mixed layers must be 0.
668  do k=1,nkml
669  hx2 = (h(i,j,k) + h(i+1,j,k) + h_neglect)
670  a(k) = (hx2 * i2htot) * (2.0 - 4.0*(z_topx2+0.5*hx2)*i2htot)
671  z_topx2 = z_topx2 + hx2
672  if (a(k)*udml(i) > 0.0) then
673  if (a(k)*udml(i) > h_avail(i,j,k)) udml(i) = h_avail(i,j,k) / a(k)
674  else
675  if (-a(k)*udml(i) > h_avail(i+1,j,k)) udml(i) = -h_avail(i+1,j,k)/a(k)
676  endif
677  enddo
678  do k=1,nkml
679  uhml(i,j,k) = a(k)*udml(i)
680  uhtr(i,j,k) = uhtr(i,j,k) + uhml(i,j,k)*dt
681  enddo
682  endif
683  enddo
684  udml_diag(is:ie,j) = udml(is:ie)
685  enddo
686 
687 ! V- component
688 !$OMP do
689  do j=js-1,je ; do i=is,ie
690  h_vel = 0.5*(htot(i,j) + htot(i,j+1)) * gv%H_to_m
691 
692  u_star = 0.5*(fluxes%ustar(i,j) + fluxes%ustar(i,j+1))
693  absf = 0.5*(abs(g%CoriolisBu(i-1,j)) + abs(g%CoriolisBu(i,j)))
694  ! peak ML visc: u_star * 0.41 * (h_ml*u_star)/(absf*h_ml + 4.0*u_star)
695  ! momentum mixing rate: pi^2*visc/h_ml^2
696  ! 0.41 is the von Karmen constant, 9.8696 = pi^2.
697  mom_mixrate = (0.41*9.8696)*u_star**2 / &
698  (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star)
699  timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2)
700 
701  timescale = timescale * cs%ml_restrat_coef
702 ! timescale = timescale*(2?)*(L_def/L_MLI)*min(EKE/MKE,1.0 + G%dyCv(i,j)**2/L_def**2))
703 
704  vtimescale_diag(i,j) = timescale
705 
706  vdml(i) = timescale * g%mask2dCv(i,j)*g%dxCv(i,j)* &
707  g%IdyCv(i,j)*(rml_av(i,j+1)-rml_av(i,j)) * (h_vel**2 * gv%m_to_H)
708  if (vdml(i) == 0) then
709  do k=1,nkml ; vhml(i,j,k) = 0.0 ; enddo
710  else
711  i2htot = 1.0 / (htot(i,j) + htot(i,j+1) + h_neglect)
712  z_topx2 = 0.0
713  ! a(k) relates the sublayer transport to uDml with a linear profile.
714  ! The sum of a(k) through the mixed layers must be 0.
715  do k=1,nkml
716  hx2 = (h(i,j,k) + h(i,j+1,k) + h_neglect)
717  a(k) = (hx2 * i2htot) * (2.0 - 4.0*(z_topx2+0.5*hx2)*i2htot)
718  z_topx2 = z_topx2 + hx2
719  if (a(k)*vdml(i) > 0.0) then
720  if (a(k)*vdml(i) > h_avail(i,j,k)) vdml(i) = h_avail(i,j,k) / a(k)
721  else
722  if (-a(k)*vdml(i) > h_avail(i,j+1,k)) vdml(i) = -h_avail(i,j+1,k)/a(k)
723  endif
724  enddo
725  do k=1,nkml
726  vhml(i,j,k) = a(k)*vdml(i)
727  vhtr(i,j,k) = vhtr(i,j,k) + vhml(i,j,k)*dt
728  enddo
729  endif
730  enddo
731  vdml_diag(is:ie,j) = vdml(is:ie)
732  enddo
733 
734 !$OMP do
735  do j=js,je ; do k=1,nkml ; do i=is,ie
736  h(i,j,k) = h(i,j,k) - dt*g%IareaT(i,j) * &
737  ((uhml(i,j,k) - uhml(i-1,j,k)) + (vhml(i,j,k) - vhml(i,j-1,k)))
738  enddo ; enddo ; enddo
739 !$OMP end parallel
740 
741  ! Whenever thickness changes let the diag manager know, target grids
742  ! for vertical remapping may need to be regenerated.
743  call diag_update_remap_grids(cs%diag)
744 
745  ! Offer diagnostic fields for averaging.
746  if (query_averaging_enabled(cs%diag) .and. &
747  ((cs%id_urestrat_time > 0) .or. (cs%id_vrestrat_time > 0))) then
748  call post_data(cs%id_urestrat_time, utimescale_diag, cs%diag)
749  call post_data(cs%id_vrestrat_time, vtimescale_diag, cs%diag)
750  endif
751  if (query_averaging_enabled(cs%diag) .and. &
752  ((cs%id_uhml>0) .or. (cs%id_vhml>0))) then
753  do k=nkml+1,nz
754  do j=js,je ; do i=isq,ieq ; uhml(i,j,k) = 0.0 ; enddo ; enddo
755  do j=jsq,jeq ; do i=is,ie ; vhml(i,j,k) = 0.0 ; enddo ; enddo
756  enddo
757  if (cs%id_uhml > 0) call post_data(cs%id_uhml, uhml, cs%diag)
758  if (cs%id_vhml > 0) call post_data(cs%id_vhml, vhml, cs%diag)
759  if (cs%id_MLD > 0) call post_data(cs%id_MLD, htot, cs%diag)
760  if (cs%id_Rml > 0) call post_data(cs%id_Rml, rml_av, cs%diag)
761  if (cs%id_uDml > 0) call post_data(cs%id_uDml, udml_diag, cs%diag)
762  if (cs%id_vDml > 0) call post_data(cs%id_vDml, vdml_diag, cs%diag)
763  endif
764 
765 end subroutine mixedlayer_restrat_bml
766 
767 
768 !> Initialize the mixed layer restratification module
769 logical function mixedlayer_restrat_init(Time, G, GV, param_file, diag, CS)
770  type(time_type), intent(in) :: Time !< Current model time
771  type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure
772  type(verticalgrid_type), intent(in) :: GV !< Ocean vertical grid structure
773  type(param_file_type), intent(in) :: param_file !< Parameter file to parse
774  type(diag_ctrl), target, intent(inout) :: diag !< Regulate diagnostics
775  type(mixedlayer_restrat_cs), pointer :: CS !< Module control structure
776  ! Local variables
777 ! This include declares and sets the variable "version".
778 #include "version_variable.h"
779  character(len=48) :: flux_units
780 
781  ! Read all relevant parameters and write them to the model log.
782  call log_version(param_file, mdl, version, "")
783  call get_param(param_file, mdl, "MIXEDLAYER_RESTRAT", mixedlayer_restrat_init, &
784  "If true, a density-gradient dependent re-stratifying \n"//&
785  "flow is imposed in the mixed layer. Can be used in ALE mode\n"//&
786  "without restriction but in layer mode can only be used if\n"//&
787  "BULKMIXEDLAYER is true.", default=.false.)
788  if (.not. mixedlayer_restrat_init) return
789 
790  if (.not.associated(cs)) then
791  call mom_error(fatal, "mixedlayer_restrat_init called without an "// &
792  "associated control structure.")
793  endif
794 
795  ! Nonsense values to cause problems when these parameters are not used
796  cs%MLE_MLD_decay_time = -9.e9
797  cs%MLE_density_diff = -9.e9
798  cs%MLE_tail_dh = -9.e9
799  cs%MLE_use_PBL_MLD = .false.
800  cs%MLE_MLD_stretch = -9.e9
801 
802  call get_param(param_file, mdl, "DEBUG", cs%debug, default=.false., do_not_log=.true.)
803  call get_param(param_file, mdl, "FOX_KEMPER_ML_RESTRAT_COEF", cs%ml_restrat_coef, &
804  "A nondimensional coefficient that is proportional to \n"//&
805  "the ratio of the deformation radius to the dominant \n"//&
806  "lengthscale of the submesoscale mixed layer \n"//&
807  "instabilities, times the minimum of the ratio of the \n"//&
808  "mesoscale eddy kinetic energy to the large-scale \n"//&
809  "geostrophic kinetic energy or 1 plus the square of the \n"//&
810  "grid spacing over the deformation radius, as detailed \n"//&
811  "by Fox-Kemper et al. (2010)", units="nondim", default=0.0)
812  ! We use GV%nkml to distinguish between the old and new implementation of MLE.
813  ! The old implementation only works for the layer model with nkml>0.
814  if (gv%nkml==0) then
815  call get_param(param_file, mdl, "FOX_KEMPER_ML_RESTRAT_COEF2", cs%ml_restrat_coef2, &
816  "As for FOX_KEMPER_ML_RESTRAT_COEF but used in a second application\n"//&
817  "of the MLE restratification parameterization.", units="nondim", default=0.0)
818  call get_param(param_file, mdl, "MLE_FRONT_LENGTH", cs%front_length, &
819  "If non-zero, is the frontal-length scale used to calculate the\n"//&
820  "upscaling of buoyancy gradients that is otherwise represented\n"//&
821  "by the parameter FOX_KEMPER_ML_RESTRAT_COEF. If MLE_FRONT_LENGTH is\n"//&
822  "non-zero, it is recommended to set FOX_KEMPER_ML_RESTRAT_COEF=1.0.",&
823  units="m", default=0.0)
824  call get_param(param_file, mdl, "MLE_USE_PBL_MLD", cs%MLE_use_PBL_MLD, &
825  "If true, the MLE parameterization will use the mixed-layer\n"//&
826  "depth provided by the active PBL parameterization. If false,\n"//&
827  "MLE will estimate a MLD based on a density difference with the\n"//&
828  "surface using the parameter MLE_DENSITY_DIFF.", default=.false.)
829  call get_param(param_file, mdl, "MLE_MLD_DECAY_TIME", cs%MLE_MLD_decay_time, &
830  "The time-scale for a running-mean filter applied to the mixed-layer\n"//&
831  "depth used in the MLE restratification parameterization. When\n"//&
832  "the MLD deepens below the current running-mean the running-mean\n"//&
833  "is instantaneously set to the current MLD.", units="s", default=0.)
834  call get_param(param_file, mdl, "MLE_MLD_DECAY_TIME2", cs%MLE_MLD_decay_time2, &
835  "The time-scale for a running-mean filter applied to the filtered\n"//&
836  "mixed-layer depth used in a second MLE restratification parameterization.\n"//&
837  "When the MLD deepens below the current running-mean the running-mean\n"//&
838  "is instantaneously set to the current MLD.", units="s", default=0.)
839  if (.not. cs%MLE_use_PBL_MLD) then
840  call get_param(param_file, mdl, "MLE_DENSITY_DIFF", cs%MLE_density_diff, &
841  "Density difference used to detect the mixed-layer\n"//&
842  "depth used for the mixed-layer eddy parameterization\n"//&
843  "by Fox-Kemper et al. (2010)", units="kg/m3", default=0.03)
844  endif
845  call get_param(param_file, mdl, "MLE_TAIL_DH", cs%MLE_tail_dh, &
846  "Fraction by which to extend the mixed-layer restratification\n"//&
847  "depth used for a smoother stream function at the base of\n"//&
848  "the mixed-layer.", units="nondim", default=0.0)
849  call get_param(param_file, mdl, "MLE_MLD_STRETCH", cs%MLE_MLD_stretch, &
850  "A scaling coefficient for stretching/shrinking the MLD\n"//&
851  "used in the MLE scheme. This simply multiplies MLD wherever used.",&
852  units="nondim", default=1.0)
853  call get_param(param_file, mdl, "MLE_USE_MLD_AVE_BUG", cs%MLE_use_MLD_ave_bug, &
854  "If true, do not account for MLD mismatch to interface positions.",&
855  default=.false.)
856  endif
857 
858  cs%diag => diag
859 
860  if (gv%Boussinesq) then ; flux_units = "meter3 second-1"
861  else ; flux_units = "kilogram second-1" ; endif
862 
863  cs%id_uhml = register_diag_field('ocean_model', 'uhml', diag%axesCuL, time, &
864  'Zonal Thickness Flux to Restratify Mixed Layer', flux_units, &
865  y_cell_method='sum', v_extensive=.true.)
866  cs%id_vhml = register_diag_field('ocean_model', 'vhml', diag%axesCvL, time, &
867  'Meridional Thickness Flux to Restratify Mixed Layer', flux_units, &
868  x_cell_method='sum', v_extensive=.true.)
869  cs%id_urestrat_time = register_diag_field('ocean_model', 'MLu_restrat_time', diag%axesCu1, time, &
870  'Mixed Layer Zonal Restratification Timescale', 'second')
871  cs%id_vrestrat_time = register_diag_field('ocean_model', 'MLv_restrat_time', diag%axesCv1, time, &
872  'Mixed Layer Meridional Restratification Timescale', 'second')
873  cs%id_MLD = register_diag_field('ocean_model', 'MLD_restrat', diag%axesT1, time, &
874  'Mixed Layer Depth as used in the mixed-layer restratification parameterization', 'meter')
875  cs%id_Rml = register_diag_field('ocean_model', 'ML_buoy_restrat', diag%axesT1, time, &
876  'Mixed Layer Buoyancy as used in the mixed-layer restratification parameterization', 'm/s^2')
877  cs%id_uDml = register_diag_field('ocean_model', 'udml_restrat', diag%axesCu1, time, &
878  'Transport stream function amplitude for zonal restratification of mixed layer', 'm3/s')
879  cs%id_vDml = register_diag_field('ocean_model', 'vdml_restrat', diag%axesCv1, time, &
880  'Transport stream function amplitude for meridional restratification of mixed layer', 'm3/s')
881  cs%id_uml = register_diag_field('ocean_model', 'uml_restrat', diag%axesCu1, time, &
882  'Surface zonal velocity component of mixed layer restratification', 'm/s')
883  cs%id_vml = register_diag_field('ocean_model', 'vml_restrat', diag%axesCv1, time, &
884  'Surface meridional velocity component of mixed layer restratification', 'm/s')
885 
886  ! If MLD_filtered is being used, we need to update halo regions after a restart
887  if (associated(cs%MLD_filtered)) call pass_var(cs%MLD_filtered, g%domain)
888 
889 end function mixedlayer_restrat_init
890 
891 !> Allocate and register fields in the mixed layer restratification structure for restarts
892 subroutine mixedlayer_restrat_register_restarts(HI, param_file, CS, restart_CS)
893  ! Arguments
894  type(hor_index_type), intent(in) :: HI !< Horizontal index structure
895  type(param_file_type), intent(in) :: param_file !< Parameter file to parse
896  type(mixedlayer_restrat_cs), pointer :: CS !< Module control structure
897  type(mom_restart_cs), pointer :: restart_CS !< Restart structure
898  ! Local variables
899  type(vardesc) :: vd
900  logical :: mixedlayer_restrat_init
901 
902  ! Check to see if this module will be used
903  call get_param(param_file, mdl, "MIXEDLAYER_RESTRAT", mixedlayer_restrat_init, &
904  default=.false., do_not_log=.true.)
905  if (.not. mixedlayer_restrat_init) return
906 
907  ! Allocate the control structure. CS will be later populated by mixedlayer_restrat_init()
908  if (associated(cs)) call mom_error(fatal, &
909  "mixedlayer_restrat_register_restarts called with an associated control structure.")
910  allocate(cs)
911 
912  call get_param(param_file, mdl, "MLE_MLD_DECAY_TIME", cs%MLE_MLD_decay_time, &
913  default=0., do_not_log=.true.)
914  call get_param(param_file, mdl, "MLE_MLD_DECAY_TIME2", cs%MLE_MLD_decay_time2, &
915  default=0., do_not_log=.true.)
916  if (cs%MLE_MLD_decay_time>0. .or. cs%MLE_MLD_decay_time2>0.) then
917  ! CS%MLD_filtered is used to keep a running mean of the PBL's actively mixed MLD.
918  allocate(cs%MLD_filtered(hi%isd:hi%ied,hi%jsd:hi%jed)) ; cs%MLD_filtered(:,:) = 0.
919  vd = var_desc("MLD_MLE_filtered","m","Time-filtered MLD for use in MLE", &
920  hor_grid='h', z_grid='1')
921  call register_restart_field(cs%MLD_filtered, vd, .false., restart_cs)
922  endif
923  if (cs%MLE_MLD_decay_time2>0.) then
924  ! CS%MLD_filtered_slow is used to keep a running mean of the PBL's seasonal or winter MLD.
925  allocate(cs%MLD_filtered_slow(hi%isd:hi%ied,hi%jsd:hi%jed)) ; cs%MLD_filtered_slow(:,:) = 0.
926  vd = var_desc("MLD_MLE_filtered_slow","m","c Slower time-filtered MLD for use in MLE", &
927  hor_grid='h', z_grid='1')
928  call register_restart_field(cs%MLD_filtered_slow, vd, .false., restart_cs)
929  endif
930 
932 
933 !> \namespace mom_mixed_layer_restrat
934 !!
935 !! \section mle-module Mixed-layer eddy parameterization module
936 !!
937 !! The subroutines in this file implement a parameterization of unresolved viscous
938 !! mixed layer restratification of the mixed layer as described in Fox-Kemper et
939 !! al., 2008, and whose impacts are described in Fox-Kemper et al., 2011.
940 !! This is derived in part from the older parameterization that is described in
941 !! Hallberg (Aha Hulikoa, 2003), which this new parameterization surpasses, which
942 !! in turn is based on the sub-inertial mixed layer theory of Young (JPO, 1994).
943 !! There is no net horizontal volume transport due to this parameterization, and
944 !! no direct effect below the mixed layer.
945 !!
946 !! This parameterization sets the restratification timescale to agree with
947 !! high-resolution studies of mixed layer restratification.
948 !!
949 !! The run-time parameter FOX_KEMPER_ML_RESTRAT_COEF is a non-dimensional number of
950 !! order a few tens, proportional to the ratio of the deformation radius or the
951 !! grid scale (whichever is smaller to the dominant horizontal length-scale of the
952 !! sub-meso-scale mixed layer instabilities.
953 !!
954 !! \subsection section-submeso-nutshell "Sub-meso" in a nutshell
955 !!
956 !! The parameterization is colloquially referred to as "sub-meso".
957 !!
958 !! The original Fox-Kemper et al., (2008b) paper proposed a quasi-Stokes
959 !! advection described by the stream function (eq. 5 of Fox-Kemper et al., 2011):
960 !! \f[
961 !! {\bf \Psi}_o = C_e \frac{ H^2 \nabla \bar{b} \times \hat{\bf z} }{ |f| } \mu(z)
962 !! \f]
963 !!
964 !! where the vertical profile function is
965 !! \f[
966 !! \mu(z) = \max \left\{ 0, \left[ 1 - \left(\frac{2z}{H}+1\right)^2 \right]
967 !! \left[ 1 + \frac{5}{21} \left(\frac{2z}{H}+1\right)^2 \right] \right\}
968 !! \f]
969 !! and \f$ H \f$ is the mixed-layer depth, \f$ f \f$ is the local Coriolis parameter, \f$ C_e \sim 0.06-0.08 \f$ and
970 !! \f$ \nabla \bar{b} \f$ is a depth mean buoyancy gradient averaged over the mixed layer.
971 !!
972 !! For use in coarse-resolution models, an upscaling of the buoyancy gradients and adaption for the equator
973 !! leads to the following parameterization (eq. 6 of Fox-Kemper et al., 2011):
974 !! \f[
975 !! {\bf \Psi} = C_e \Gamma_\Delta \frac{\Delta s}{l_f} \frac{ H^2 \nabla \bar{b} \times \hat{\bf z} }{ \sqrt{ f^2 + \tau^{-2}} } \mu(z)
976 !! \f]
977 !! where \f$ \Delta s \f$ is the minimum of grid-scale and deformation radius,
978 !! \f$ l_f \f$ is the width of the mixed-layer fronts, and \f$ \Gamma_\Delta=1 \f$.
979 !! \f$ \tau \f$ is a time-scale for mixing momentum across the mixed layer.
980 !! \f$ l_f \f$ is thought to be of order hundreds of meters.
981 !!
982 !! The upscaling factor \f$ \frac{\Delta s}{l_f} \f$ can be a global constant, model parameter FOX_KEMPER_ML_RESTRAT,
983 !! so that in practice the parameterization is:
984 !! \f[
985 !! {\bf \Psi} = C_e \Gamma_\Delta \frac{ H^2 \nabla \bar{b} \times \hat{\bf z} }{ \sqrt{ f^2 + \tau^{-2}} } \mu(z)
986 !! \f]
987 !! with non-unity \f$ \Gamma_\Delta \f$.
988 !!
989 !! \f$ C_e \f$ is hard-coded as 0.0625. \f$ \tau \f$ is calculated from the surface friction velocity \f$ u^* \f$.
990 !! \todo Explain expression for momentum mixing time-scale.
991 !!
992 !! \subsection section-mle-filtering Time-filtering of mixed-layer depth
993 !!
994 !! Using the instantaneous mixed-layer depth is inconsistent with the finite life-time of
995 !! mixed-layer instabilities. We provide a one-sided running-mean filter of mixed-layer depth, \f$ H \f$, of the form:
996 !! \f[
997 !! \bar{H} \leftarrow \max \left( H, \frac{ \Delta t H + \tau_h \bar{H} }{ \Delta t + \tau_h } \right)
998 !! \f]
999 !! which allows the effective mixed-layer depth seen by the parameterization, \f$\bar{H}\f$, to instantaneously deepen
1000 !! but to decay with time-scale \f$ \tau_h \f$.
1001 !! \f$ \bar{H} \f$ is substituted for \f$ H \f$ in the above equations.
1002 !!
1003 !! \subsection section-mle-mld Defining the mixed-layer-depth
1004 !!
1005 !! If the parameter MLE_USE_PBL_MLD=True then the mixed-layer depth is defined/diagnosed by the
1006 !! boundary-layer parameterization (e.g. ePBL, KPP, etc.).
1007 !!
1008 !! If the parameter MLE_USE_PBL_MLD=False then the mixed-layer depth is diagnosed in this module
1009 !! as the depth of a given density difference, \f$ \Delta \rho \f$, with the surface where the
1010 !! density difference is the parameter MLE_DENSITY_DIFF.
1011 !!
1012 !! \subsection section-mle-ref References
1013 !!
1014 !! Fox-Kemper, B., Ferrari, R. and Hallberg, R., 2008:
1015 !! Parameterization of Mixed Layer Eddies. Part I: Theory and Diagnosis
1016 !! J. Phys. Oceangraphy, 38 (6), p1145-1165.
1017 !! https://doi.org/10.1175/2007JPO3792.1
1018 !!
1019 !! Fox-Kemper, B. and Ferrari, R. 2008:
1020 !! Parameterization of Mixed Layer Eddies. Part II: Prognosis and Impact
1021 !! J. Phys. Oceangraphy, 38 (6), p1166-1179.
1022 !! https://doi.org/10.1175/2007JPO3788.1
1023 !!
1024 !! B. Fox-Kemper, G. Danabasoglu, R. Ferrari, S.M. Griffies, R.W. Hallberg, M.M. Holland, M.E. Maltrud, S. Peacock, and B.L. Samuels, 2011:
1025 !! Parameterization of mixed layer eddies. III: Implementation and impact in global ocean climate simulations.
1026 !! Ocean Modell., 39(1), p61-78.
1027 !! https://doi.org/10.1016/j.ocemod.2010.09.002
1028 !!
1029 !! | Symbol | Module parameter |
1030 !! | ---------------------------- | --------------------- |
1031 !! | \f$ \Gamma_\Delta \f$ | FOX_KEMPER_ML_RESTRAT |
1032 !! | \f$ l_f \f$ | MLE_FRONT_LENGTH |
1033 !! | \f$ \tau_h \f$ | MLE_MLD_DECAY_TIME |
1034 !! | \f$ \Delta \rho \f$ | MLE_DENSITY_DIFF |
1035 
1036 end module mom_mixed_layer_restrat
Control structure for mom_mixed_layer_restrat.
type(vardesc) function, public var_desc(name, units, longname, hor_grid, z_grid, t_grid, cmor_field_name, cmor_units, conversion, caller)
Returns a vardesc type whose elements have been filled with the provided fields. The argument name is...
Definition: MOM_io.F90:585
This module implements boundary forcing for MOM6.
subroutine, public mixedlayer_restrat(h, uhtr, vhtr, tv, fluxes, dt, MLD, VarMix, G, GV, CS)
Driver for the mixed-layer restratification parameterization. The code branches between two different...
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Calculates density of sea water from T, S and P.
Definition: MOM_EOS.F90:45
Provides the ocean grid type.
Definition: MOM_grid.F90:2
logical function, public mixedlayer_restrat_init(Time, G, GV, param_file, diag, CS)
Initialize the mixed layer restratification module.
subroutine mixedlayer_restrat_bml(h, uhtr, vhtr, tv, fluxes, dt, G, GV, CS)
Calculates a restratifying flow assuming a 2-layer bulk mixed layer.
This module contains I/O framework code.
Definition: MOM_io.F90:2
Defines the horizontal index type (hor_index_type) used for providing index ranges.
Variable mixing coefficients.
Container for horizontal index ranges for data, computational and global domains. ...
logical function, public query_averaging_enabled(diag_cs, time_int, time_end)
character(len=40) mdl
This module&#39;s name.
Structure that contains pointers to the boundary forcing used to drive the liquid ocean simulated by ...
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
Type for describing a variable, typically a tracer.
Definition: MOM_io.F90:51
The thermo_var_ptrs structure contains pointers to an assortment of thermodynamic fields that may be ...
subroutine, public mixedlayer_restrat_register_restarts(HI, param_file, CS, restart_CS)
Allocate and register fields in the mixed layer restratification structure for restarts.
subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, fluxes, dt, MLD_in, VarMix, G, GV, CS)
Calculates a restratifying flow in the mixed layer.
integer function, public register_diag_field(module_name, field_name, axes, init_time, long_name, units, missing_value, range, mask_variant, standard_name, verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, cell_methods, x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
Returns the "diag_mediator" handle for a group (native, CMOR, z-coord, ...) of diagnostics derived fr...
subroutine, public mom_error(level, message, all_print)
subroutine, public diag_update_remap_grids(diag_cs, alt_h)
Build/update vertical grids for diagnostic remapping.
Parameterization of mixed layer restratification by unresolved mixed-layer eddies.