MOM6
MOM_diabatic_aux.F90
Go to the documentation of this file.
2 !***********************************************************************
3 !* GNU General Public License *
4 !* This file is a part of MOM. *
5 !* *
6 !* MOM is free software; you can redistribute it and/or modify it and *
7 !* are expected to follow the terms of the GNU General Public License *
8 !* as published by the Free Software Foundation; either version 2 of *
9 !* the License, or (at your option) any later version. *
10 !* *
11 !* MOM is distributed in the hope that it will be useful, but WITHOUT *
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
13 !* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public *
14 !* License for more details. *
15 !* *
16 !* For the full text of the GNU General Public License, *
17 !* write to: Free Software Foundation, Inc., *
18 !* 675 Mass Ave, Cambridge, MA 02139, USA. *
19 !* or see: http://www.gnu.org/licenses/gpl.html *
20 !***********************************************************************
21 
22 !********+*********+*********+*********+*********+*********+*********+**
23 !* *
24 !* By Robert Hallberg, April 1994 - July 2000 *
25 !* Alistair Adcroft, and Stephen Griffies *
26 !* *
27 !* This program contains the subroutine that, along with the *
28 !* subroutines that it calls, implements diapycnal mass and momentum *
29 !* fluxes and a bulk mixed layer. The diapycnal diffusion can be *
30 !* used without the bulk mixed layer. *
31 !* *
32 !* diabatic first determines the (diffusive) diapycnal mass fluxes *
33 !* based on the convergence of the buoyancy fluxes within each layer. *
34 !* The dual-stream entrainment scheme of MacDougall and Dewar (JPO, *
35 !* 1997) is used for combined diapycnal advection and diffusion, *
36 !* calculated implicitly and potentially with the Richardson number *
37 !* dependent mixing, as described by Hallberg (MWR, 2000). Diapycnal *
38 !* advection is fundamentally the residual of diapycnal diffusion, *
39 !* so the fully implicit upwind differencing scheme that is used is *
40 !* entirely appropriate. The downward buoyancy flux in each layer *
41 !* is determined from an implicit calculation based on the previously *
42 !* calculated flux of the layer above and an estimated flux in the *
43 !* layer below. This flux is subject to the following conditions: *
44 !* (1) the flux in the top and bottom layers are set by the boundary *
45 !* conditions, and (2) no layer may be driven below an Angstrom thick-*
46 !* ness. If there is a bulk mixed layer, the buffer layer is treat- *
47 !* ed as a fixed density layer with vanishingly small diffusivity. *
48 !* *
49 !* diabatic takes 5 arguments: the two velocities (u and v), the *
50 !* thicknesses (h), a structure containing the forcing fields, and *
51 !* the length of time over which to act (dt). The velocities and *
52 !* thickness are taken as inputs and modified within the subroutine. *
53 !* There is no limit on the time step. *
54 !* *
55 !* A small fragment of the grid is shown below: *
56 !* *
57 !* j+1 x ^ x ^ x At x: q *
58 !* j+1 > o > o > At ^: v *
59 !* j x ^ x ^ x At >: u *
60 !* j > o > o > At o: h, T, S, buoy, ustar, ea, eb, etc. *
61 !* j-1 x ^ x ^ x *
62 !* i-1 i i+1 At x & ^: *
63 !* i i+1 At > & o: *
64 !* *
65 !* The boundaries always run through q grid points (x). *
66 !* *
67 !********+*********+*********+*********+*********+*********+*********+**
68 
69 use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
70 use mom_cpu_clock, only : clock_module_driver, clock_module, clock_routine
71 use mom_diag_mediator, only : post_data, register_diag_field, safe_alloc_ptr
72 use mom_diag_mediator, only : diag_ctrl, time_type
75 use mom_error_handler, only : mom_error, fatal, warning, calltree_showquery
79 use mom_grid, only : ocean_grid_type
80 use mom_io, only : vardesc
82 use mom_variables, only : thermo_var_ptrs, vertvisc_type! , accel_diag_ptrs
84 
85 implicit none ; private
86 
87 #include <MOM_memory.h>
88 
92 !> Control structure for diabatic_aux
93 type, public :: diabatic_aux_cs ; private
94  logical :: do_rivermix = .false. !< Provide additional TKE to mix river runoff
95  !! at the river mouths to "rivermix_depth" meters
96  real :: rivermix_depth = 0.0 !< The depth to which rivers are mixed if
97  !! do_rivermix = T, in m.
98  logical :: reclaim_frazil !< If true, try to use any frazil heat deficit to
99  !! to cool the topmost layer down to the freezing
100  !! point. The default is false.
101  logical :: pressure_dependent_frazil !< If true, use a pressure dependent
102  !! freezing temperature when making frazil. The
103  !! default is false, which will be faster but is
104  !! inappropriate with ice-shelf cavities.
105  logical :: ignore_fluxes_over_land !< If true, the model does not check
106  !! if fluxes are applied over land points. This
107  !! flag must be used when the ocean is coupled with
108  !! sea ice and ice shelves and use_ePBL = true.
109  logical :: use_river_heat_content !< If true, assumes that ice-ocean boundary
110  !! has provided a river heat content. Otherwise, runoff
111  !! is added with a temperature of the local SST.
112  logical :: use_calving_heat_content !< If true, assumes that ice-ocean boundary
113  !! has provided a calving heat content. Otherwise, calving
114  !! is added with a temperature of the local SST.
115 
116  type(diag_ctrl), pointer :: diag !< Structure used to regulate timing of diagnostic output
117 
118  ! Diagnostic handles
119  integer :: id_createdh = -1
120  integer :: id_brine_lay = -1
121  integer :: id_pensw_diag = -1 !< Penetrative shortwave heating (flux convergence) diagnostic
122  integer :: id_penswflux_diag = -1 !< Penetrative shortwave flux diagnostic
123  integer :: id_nonpensw_diag = -1 !< Non-penetrative shortwave heating diagnostic
124 
125  ! Optional diagnostic arrays
126  real, allocatable, dimension(:,:) :: createdh !< The amount of volume added in order to avoid grounding (m/s)
127  real, allocatable, dimension(:,:,:) :: pensw_diag !< Heating in a layer from convergence of penetrative SW (W/m2)
128  real, allocatable, dimension(:,:,:) :: penswflux_diag !< Penetrative SW flux at base of grid layer (W/m2)
129  real, allocatable, dimension(:,:) :: nonpensw_diag !< Non-downwelling SW radiation (W/m2) at ocean surface
130 
131 end type diabatic_aux_cs
132 
134 
135 contains
136 
137 subroutine make_frazil(h, tv, G, GV, CS, p_surf)
138  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
139  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure
140  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thicknesses, in H (usually m or kg m-2)
141  type(thermo_var_ptrs), intent(inout) :: tv
142  type(diabatic_aux_cs), intent(in) :: CS
143  real, dimension(SZI_(G),SZJ_(G)), intent(in), optional :: p_surf
144 
145 ! Frazil formation keeps the temperature above the freezing point.
146 ! This subroutine warms any water that is colder than the (currently
147 ! surface) freezing point up to the freezing point and accumulates
148 ! the required heat (in J m-2) in tv%frazil.
149 ! The expression, below, for the freezing point of sea water comes
150 ! from Millero (1978) via Appendix A of Gill, 1982.
151 
152 ! Arguments: h - Layer thickness, in m or kg m-2.
153 ! (in/out) tv - A structure containing pointers to any available
154 ! thermodynamic fields. Absent fields have NULL ptrs.
155 ! (in) G - The ocean's grid structure.
156 ! (in) GV - The ocean's vertical grid structure.
157 ! (in) CS - The control structure returned by a previous call to
158 ! diabatic_driver_init.
159  real, dimension(SZI_(G)) :: &
160  fraz_col, & ! The accumulated heat requirement due to frazil, in J.
161  T_freeze, & ! The freezing potential temperature at the current salinity, C.
162  ps ! pressure
163  real, dimension(SZI_(G),SZK_(G)) :: &
164  pressure ! The pressure at the middle of each layer in Pa.
165  real :: hc ! A layer's heat capacity in J m-2 K-1.
166  logical :: T_fr_set ! True if the freezing point has been calculated for a
167  ! row of points.
168  integer :: i, j, k, is, ie, js, je, nz
169  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
170 
171  call cpu_clock_begin(id_clock_frazil)
172 
173  if (.not.cs%pressure_dependent_frazil) then
174  do k=1,nz ; do i=is,ie ; pressure(i,k) = 0.0 ; enddo ; enddo
175  endif
176 !$OMP parallel do default(none) shared(is,ie,js,je,CS,G,GV,h,nz,tv,p_surf) &
177 !$OMP private(fraz_col,T_fr_set,T_freeze,hc,ps,pressure)
178  do j=js,je
179  ps(:) = 0.0
180  if (PRESENT(p_surf)) then ; do i=is,ie
181  ps(i) = p_surf(i,j)
182  enddo ; endif
183 
184  do i=is,ie ; fraz_col(i) = 0.0 ; enddo
185 
186  if (cs%pressure_dependent_frazil) then
187  do i=is,ie
188  pressure(i,1) = ps(i) + (0.5*gv%H_to_Pa)*h(i,j,1)
189  enddo
190  do k=2,nz ; do i=is,ie
191  pressure(i,k) = pressure(i,k-1) + &
192  (0.5*gv%H_to_Pa) * (h(i,j,k) + h(i,j,k-1))
193  enddo ; enddo
194  endif
195 
196  if (cs%reclaim_frazil) then
197  t_fr_set = .false.
198  do i=is,ie ; if (tv%frazil(i,j) > 0.0) then
199  if (.not.t_fr_set) then
200  call calculate_tfreeze(tv%S(i:,j,1), pressure(i:,1), t_freeze(i:), &
201  1, ie-i+1, tv%eqn_of_state)
202  t_fr_set = .true.
203  endif
204 
205  if (tv%T(i,j,1) > t_freeze(i)) then
206  ! If frazil had previously been formed, but the surface temperature is now
207  ! above freezing, cool the surface layer with the frazil heat deficit.
208  hc = (tv%C_p*gv%H_to_kg_m2) * h(i,j,1)
209  if (tv%frazil(i,j) - hc * (tv%T(i,j,1) - t_freeze(i)) <= 0.0) then
210  tv%T(i,j,1) = tv%T(i,j,1) - tv%frazil(i,j)/hc
211  tv%frazil(i,j) = 0.0
212  else
213  tv%frazil(i,j) = tv%frazil(i,j) - hc * (tv%T(i,j,1) - t_freeze(i))
214  tv%T(i,j,1) = t_freeze(i)
215  endif
216  endif
217  endif ; enddo
218  endif
219 
220  do k=nz,1,-1
221  t_fr_set = .false.
222  do i=is,ie
223  if ((g%mask2dT(i,j) > 0.0) .and. &
224  ((tv%T(i,j,k) < 0.0) .or. (fraz_col(i) > 0.0))) then
225  if (.not.t_fr_set) then
226  call calculate_tfreeze(tv%S(i:,j,k), pressure(i:,k), t_freeze(i:), &
227  1, ie-i+1, tv%eqn_of_state)
228  t_fr_set = .true.
229  endif
230 
231  hc = (tv%C_p*gv%H_to_kg_m2) * h(i,j,k)
232  if (h(i,j,k) <= 10.0*gv%Angstrom) then
233  ! Very thin layers should not be cooled by the frazil flux.
234  if (tv%T(i,j,k) < t_freeze(i)) then
235  fraz_col(i) = fraz_col(i) + hc * (t_freeze(i) - tv%T(i,j,k))
236  tv%T(i,j,k) = t_freeze(i)
237  endif
238  else
239  if (fraz_col(i) + hc * (t_freeze(i) - tv%T(i,j,k)) <= 0.0) then
240  tv%T(i,j,k) = tv%T(i,j,k) - fraz_col(i)/hc
241  fraz_col(i) = 0.0
242  else
243  fraz_col(i) = fraz_col(i) + hc * (t_freeze(i) - tv%T(i,j,k))
244  tv%T(i,j,k) = t_freeze(i)
245  endif
246  endif
247  endif
248  enddo
249  enddo
250  do i=is,ie
251  tv%frazil(i,j) = tv%frazil(i,j) + fraz_col(i)
252  enddo
253  enddo
254  call cpu_clock_end(id_clock_frazil)
255 
256 end subroutine make_frazil
257 
258 subroutine differential_diffuse_t_s(h, tv, visc, dt, G, GV)
259  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
260  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure
261  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thicknesses, in H (usually m or kg m-2)
262  type(thermo_var_ptrs), intent(inout) :: tv
263  type(vertvisc_type), intent(in) :: visc
264  real, intent(in) :: dt
265 
266 ! This subroutine applies double diffusion to T & S, assuming no diapycal mass
267 ! fluxes, using a simple triadiagonal solver.
268 
269 ! Arguments: h - Layer thickness, in m or kg m-2.
270 ! (in) tv - A structure containing pointers to any available
271 ! thermodynamic fields. Absent fields have NULL ptrs.
272 ! (in) visc - A structure containing vertical viscosities, bottom boundary
273 ! layer properies, and related fields.
274 ! (in) dt - Time increment, in s.
275 ! (in) G - The ocean's grid structure.
276 ! (in) GV - The ocean's vertical grid structure.
277 
278  real, dimension(SZI_(G)) :: &
279  b1_T, b1_S, & ! Variables used by the tridiagonal solvers of T & S, in H.
280  d1_T, d1_S ! Variables used by the tridiagonal solvers, nondim.
281  real, dimension(SZI_(G),SZK_(G)) :: &
282  c1_T, c1_S ! Variables used by the tridiagonal solvers, in m or kg m-2.
283  real, dimension(SZI_(G),SZK_(G)+1) :: &
284  mix_T, mix_S ! Mixing distances in both directions across each
285  ! interface, in m or kg m-2.
286  real :: h_tr ! h_tr is h at tracer points with a tiny thickness
287  ! added to ensure positive definiteness, in m or kg m-2.
288  real :: h_neglect ! A thickness that is so small it is usually lost
289  ! in roundoff and can be neglected, in m or kg m-2.
290  real :: I_h_int ! The inverse of the thickness associated with an
291  ! interface, in m-1 or m2 kg-1.
292  real :: b_denom_T ! The first term in the denominators for the expressions
293  real :: b_denom_S ! for b1_T and b1_S, both in m or kg m-2.
294 
295  integer :: i, j, k, is, ie, js, je, nz
296  real, pointer :: T(:,:,:), S(:,:,:), Kd_T(:,:,:), Kd_S(:,:,:)
297  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
298  h_neglect = gv%H_subroundoff
299 
300  if (.not.associated(tv%T)) call mom_error(fatal, &
301  "differential_diffuse_T_S: Called with an unassociated tv%T")
302  if (.not.associated(tv%S)) call mom_error(fatal, &
303  "differential_diffuse_T_S: Called with an unassociated tv%S")
304  if (.not.associated(visc%Kd_extra_T)) call mom_error(fatal, &
305  "differential_diffuse_T_S: Called with an unassociated visc%Kd_extra_T")
306  if (.not.associated(visc%Kd_extra_S)) call mom_error(fatal, &
307  "differential_diffuse_T_S: Called with an unassociated visc%Kd_extra_S")
308 
309  t => tv%T ; s => tv%S
310  kd_t => visc%Kd_extra_T ; kd_s => visc%Kd_extra_S
311 !$OMP parallel do default(none) shared(is,ie,js,je,h,h_neglect,dt,Kd_T,Kd_S,G,GV,T,S,nz) &
312 !$OMP private(I_h_int,mix_T,mix_S,h_tr,b1_T,b1_S, &
313 !$OMP d1_T,d1_S,c1_T,c1_S,b_denom_T,b_denom_S)
314  do j=js,je
315  do i=is,ie
316  i_h_int = 1.0 / (0.5 * (h(i,j,1) + h(i,j,2)) + h_neglect)
317  mix_t(i,2) = ((dt * kd_t(i,j,2)) * gv%m_to_H**2) * i_h_int
318  mix_s(i,2) = ((dt * kd_s(i,j,2)) * gv%m_to_H**2) * i_h_int
319 
320  h_tr = h(i,j,1) + h_neglect
321  b1_t(i) = 1.0 / (h_tr + mix_t(i,2))
322  b1_s(i) = 1.0 / (h_tr + mix_s(i,2))
323  d1_t(i) = h_tr * b1_t(i)
324  d1_s(i) = h_tr * b1_s(i)
325  t(i,j,1) = (b1_t(i)*h_tr)*t(i,j,1)
326  s(i,j,1) = (b1_s(i)*h_tr)*s(i,j,1)
327  enddo
328  do k=2,nz-1 ; do i=is,ie
329  ! Calculate the mixing across the interface below this layer.
330  i_h_int = 1.0 / (0.5 * (h(i,j,k) + h(i,j,k+1)) + h_neglect)
331  mix_t(i,k+1) = ((dt * kd_t(i,j,k+1)) * gv%m_to_H**2) * i_h_int
332  mix_s(i,k+1) = ((dt * kd_s(i,j,k+1)) * gv%m_to_H**2) * i_h_int
333 
334  c1_t(i,k) = mix_t(i,k) * b1_t(i)
335  c1_s(i,k) = mix_s(i,k) * b1_s(i)
336 
337  h_tr = h(i,j,k) + h_neglect
338  b_denom_t = h_tr + d1_t(i)*mix_t(i,k)
339  b_denom_s = h_tr + d1_s(i)*mix_s(i,k)
340  b1_t(i) = 1.0 / (b_denom_t + mix_t(i,k+1))
341  b1_s(i) = 1.0 / (b_denom_s + mix_s(i,k+1))
342  d1_t(i) = b_denom_t * b1_t(i)
343  d1_s(i) = b_denom_s * b1_s(i)
344 
345  t(i,j,k) = b1_t(i) * (h_tr*t(i,j,k) + mix_t(i,k)*t(i,j,k-1))
346  s(i,j,k) = b1_s(i) * (h_tr*s(i,j,k) + mix_s(i,k)*s(i,j,k-1))
347  enddo ; enddo
348  do i=is,ie
349  c1_t(i,nz) = mix_t(i,nz) * b1_t(i)
350  c1_s(i,nz) = mix_s(i,nz) * b1_s(i)
351 
352  h_tr = h(i,j,nz) + h_neglect
353  b1_t(i) = 1.0 / (h_tr + d1_t(i)*mix_t(i,nz))
354  b1_s(i) = 1.0 / (h_tr + d1_s(i)*mix_s(i,nz))
355 
356  t(i,j,nz) = b1_t(i) * (h_tr*t(i,j,nz) + mix_t(i,nz)*t(i,j,nz-1))
357  s(i,j,nz) = b1_s(i) * (h_tr*s(i,j,nz) + mix_s(i,nz)*s(i,j,nz-1))
358  enddo
359  do k=nz-1,1,-1 ; do i=is,ie
360  t(i,j,k) = t(i,j,k) + c1_t(i,k+1)*t(i,j,k+1)
361  s(i,j,k) = s(i,j,k) + c1_s(i,k+1)*s(i,j,k+1)
362  enddo ; enddo
363  enddo
364 
365 end subroutine differential_diffuse_t_s
366 
367 subroutine adjust_salt(h, tv, G, GV, CS)
368  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
369  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure
370  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thicknesses, in H (usually m or kg m-2)
371  type(thermo_var_ptrs), intent(inout) :: tv
372  type(diabatic_aux_cs), intent(in) :: CS
373 
374 ! Keep salinity from falling below a small but positive threshold
375 ! This occurs when the ice model attempts to extract more salt then
376 ! is actually available to it from the ocean.
377 
378 ! Arguments: h - Layer thickness, in m.
379 ! (in/out) tv - A structure containing pointers to any available
380 ! thermodynamic fields. Absent fields have NULL ptrs.
381 ! (in) G - The ocean's grid structure.
382 ! (in) GV - The ocean's vertical grid structure.
383 ! (in) CS - The control structure returned by a previous call to
384 ! diabatic_driver_init.
385  real :: salt_add_col(szi_(g),szj_(g)) ! The accumulated salt requirement
386  real :: S_min ! The minimum salinity
387  real :: mc ! A layer's mass kg m-2 .
388  integer :: i, j, k, is, ie, js, je, nz
389  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
390 
391 ! call cpu_clock_begin(id_clock_adjust_salt)
392 
393  s_min = 0.01
394 
395  salt_add_col(:,:) = 0.0
396 
397 !$OMP parallel do default(none) shared(is,ie,js,je,nz,G,GV,tv,h,salt_add_col, S_min) &
398 !$OMP private(mc)
399  do j=js,je
400  do k=nz,1,-1 ; do i=is,ie
401  if ((g%mask2dT(i,j) > 0.0) .and. &
402  ((tv%S(i,j,k) < s_min) .or. (salt_add_col(i,j) > 0.0))) then
403  mc = gv%H_to_kg_m2 * h(i,j,k)
404  if (h(i,j,k) <= 10.0*gv%Angstrom) then
405  ! Very thin layers should not be adjusted by the salt flux
406  if (tv%S(i,j,k) < s_min) then
407  salt_add_col(i,j) = salt_add_col(i,j) + mc * (s_min - tv%S(i,j,k))
408  tv%S(i,j,k) = s_min
409  endif
410  else
411  if (salt_add_col(i,j) + mc * (s_min - tv%S(i,j,k)) <= 0.0) then
412  tv%S(i,j,k) = tv%S(i,j,k) - salt_add_col(i,j)/mc
413  salt_add_col(i,j) = 0.0
414  else
415  salt_add_col(i,j) = salt_add_col(i,j) + mc * (s_min - tv%S(i,j,k))
416  tv%S(i,j,k) = s_min
417  endif
418  endif
419  endif
420  enddo ; enddo
421  do i=is,ie
422  tv%salt_deficit(i,j) = tv%salt_deficit(i,j) + salt_add_col(i,j)
423  enddo
424  enddo
425 ! call cpu_clock_end(id_clock_adjust_salt)
426 
427 end subroutine adjust_salt
428 
429 subroutine insert_brine(h, tv, G, GV, fluxes, nkmb, CS, dt, id_brine_lay)
430  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
431  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure
432  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thicknesses, in H (usually m or kg m-2)
433  type(thermo_var_ptrs), intent(inout) :: tv
434  type(forcing), intent(in) :: fluxes
435  integer, intent(in) :: nkmb
436  type(diabatic_aux_cs), intent(in) :: CS
437  real, intent(in) :: dt
438  integer, intent(in) :: id_brine_lay
439 
440 ! Insert salt from brine rejection into the first layer below
441 ! the mixed layer which both contains mass and in which the
442 ! change in layer density remains stable after the addition
443 ! of salt via brine rejection.
444 
445 ! Arguments: h - Layer thickness, in m.
446 ! (in/out) tv - A structure containing pointers to any available
447 ! thermodynamic fields. Absent fields have NULL ptrs.
448 ! (in) fluxes = A structure containing pointers to any possible
449 ! forcing fields; unused fields have NULL ptrs.
450 ! (in) nkmb - The number of layers in the mixed and buffer layers.
451 ! (in) G - The ocean's grid structure.
452 ! (in) GV - The ocean's vertical grid structure.
453 ! (in) CS - The control structure returned by a previous call to
454 ! diabatic_driver_init.
455 
456  real :: salt(szi_(g)) ! The amount of salt rejected from
457  ! sea ice. [grams]
458  real :: dzbr(szi_(g)) ! cumulative depth over which brine is distributed
459  real :: inject_layer(szi_(g),szj_(g)) ! diagnostic
460 
461  real :: p_ref_cv(szi_(g))
462  real :: T(szi_(g),szk_(g))
463  real :: S(szi_(g),szk_(g))
464  real :: h_2d(szi_(g),szk_(g))
465  real :: Rcv(szi_(g),szk_(g))
466  real :: mc ! A layer's mass in kg m-2 .
467  real :: s_new,R_new,t0,scale, cdz
468  integer :: i, j, k, is, ie, js, je, nz, ks
469 
470  real, parameter :: brine_dz = 1.0 ! minumum thickness over which to distribute brine
471  real, parameter :: s_max = 45.0 ! salinity bound
472 
473  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
474 
475  if (.not.ASSOCIATED(fluxes%salt_flux)) return
476 
477  p_ref_cv(:) = tv%P_ref
478 
479  inject_layer = nz
480 
481  do j=js,je
482 
483  salt(:)=0.0 ; dzbr(:)=0.0
484 
485  do i=is,ie ; if (g%mask2dT(i,j) > 0.) then
486  salt(i) = dt * (1000. * fluxes%salt_flux(i,j))
487  endif ; enddo
488 
489  do k=1,nz
490  do i=is,ie
491  t(i,k)=tv%T(i,j,k); s(i,k)=tv%S(i,j,k)
492  ! avoid very small thickness
493  h_2d(i,k)=max(h(i,j,k), gv%Angstrom)
494  enddo
495 
496  call calculate_density(t(:,k), s(:,k), p_ref_cv, rcv(:,k), is, &
497  ie-is+1, tv%eqn_of_state)
498  enddo
499 
500  ! First, try to find an interior layer where inserting all the salt
501  ! will not cause the layer to become statically unstable.
502  ! Bias towards deeper layers.
503 
504  do k=nkmb+1,nz-1 ; do i=is,ie
505  if ((g%mask2dT(i,j) > 0.0) .and. dzbr(i) < brine_dz .and. salt(i) > 0.) then
506  mc = gv%H_to_kg_m2 * h_2d(i,k)
507  s_new = s(i,k) + salt(i)/mc
508  t0 = t(i,k)
509  call calculate_density(t0,s_new,tv%P_Ref,r_new,tv%eqn_of_state)
510  if (r_new < 0.5*(rcv(i,k)+rcv(i,k+1)) .and. s_new<s_max) then
511  dzbr(i)=dzbr(i)+h_2d(i,k)
512  inject_layer(i,j) = min(inject_layer(i,j),real(k))
513  endif
514  endif
515  enddo ; enddo
516 
517  ! Then try to insert into buffer layers if they exist
518  do k=nkmb,gv%nkml+1,-1 ; do i=is,ie
519  if ((g%mask2dT(i,j) > 0.0) .and. dzbr(i) < brine_dz .and. salt(i) > 0.) then
520  mc = gv%H_to_kg_m2 * h_2d(i,k)
521  dzbr(i)=dzbr(i)+h_2d(i,k)
522  inject_layer(i,j) = min(inject_layer(i,j),real(k))
523  endif
524  enddo ; enddo
525 
526  ! finally if unable to find a layer to insert, then place in mixed layer
527 
528  do k=1,gv%nkml ; do i=is,ie
529  if ((g%mask2dT(i,j) > 0.0) .and. dzbr(i) < brine_dz .and. salt(i) > 0.) then
530  mc = gv%H_to_kg_m2 * h_2d(i,k)
531  dzbr(i)=dzbr(i)+h_2d(i,k)
532  inject_layer(i,j) = min(inject_layer(i,j),real(k))
533  endif
534  enddo ; enddo
535 
536 
537  do i=is,ie
538  if ((g%mask2dT(i,j) > 0.0) .and. salt(i) > 0.) then
539  ! if (dzbr(i)< brine_dz) call MOM_error(FATAL,"insert_brine: failed")
540  ks=inject_layer(i,j)
541  cdz=0.0
542  do k=ks,nz
543  mc = gv%H_to_kg_m2 * h_2d(i,k)
544  scale = h_2d(i,k)/dzbr(i)
545  cdz=cdz+h_2d(i,k)
546  if (cdz > 1.0) exit
547  tv%S(i,j,k) = tv%S(i,j,k) + scale*salt(i)/mc
548  enddo
549  endif
550  enddo
551 
552  enddo
553 
554  if (cs%id_brine_lay > 0) call post_data(cs%id_brine_lay,inject_layer,cs%diag)
555 
556 end subroutine insert_brine
557 
558 subroutine tridiagts(G, GV, is, ie, js, je, hold, ea, eb, T, S)
559 ! Simple tri-diagnonal solver for T and S
560 ! "Simple" means it only uses arrays hold, ea and eb
561  ! Arguments
562  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
563  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure
564  integer, intent(in) :: is, ie, js, je
565  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: hold, ea, eb
566  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: T, S
567  ! Local variables
568  real :: b1(szib_(g)), d1(szib_(g)) ! b1, c1, and d1 are variables used by the
569  real :: c1(szib_(g),szk_(g)) ! tridiagonal solver.
570  real :: h_tr, b_denom_1
571  integer :: i, j, k
572 !$OMP parallel do default(none) shared(is,ie,js,je,G,GV,hold,eb,T,S,ea) &
573 !$OMP private(h_tr,b1,d1,c1,b_denom_1)
574  do j=js,je
575  do i=is,ie
576  h_tr = hold(i,j,1) + gv%H_subroundoff
577  b1(i) = 1.0 / (h_tr + eb(i,j,1))
578  d1(i) = h_tr * b1(i)
579  t(i,j,1) = (b1(i)*h_tr)*t(i,j,1)
580  s(i,j,1) = (b1(i)*h_tr)*s(i,j,1)
581  enddo
582  do k=2,g%ke ; do i=is,ie
583  c1(i,k) = eb(i,j,k-1) * b1(i)
584  h_tr = hold(i,j,k) + gv%H_subroundoff
585  b_denom_1 = h_tr + d1(i)*ea(i,j,k)
586  b1(i) = 1.0 / (b_denom_1 + eb(i,j,k))
587  d1(i) = b_denom_1 * b1(i)
588  t(i,j,k) = b1(i) * (h_tr*t(i,j,k) + ea(i,j,k)*t(i,j,k-1))
589  s(i,j,k) = b1(i) * (h_tr*s(i,j,k) + ea(i,j,k)*s(i,j,k-1))
590  enddo ; enddo
591  do k=g%ke-1,1,-1 ; do i=is,ie
592  t(i,j,k) = t(i,j,k) + c1(i,k+1)*t(i,j,k+1)
593  s(i,j,k) = s(i,j,k) + c1(i,k+1)*s(i,j,k+1)
594  enddo ; enddo
595  enddo
596 end subroutine tridiagts
597 
598 
599 subroutine find_uv_at_h(u, v, h, u_h, v_h, G, GV, ea, eb)
600  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
601  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure
602  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< The zonal velocity, in m s-1
603  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< The meridional velocity, in m s-1
604  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thicknesses, in H (usually m or kg m-2)
605  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(out) :: u_h, v_h
606  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in), optional :: ea, eb
607 ! This subroutine calculates u_h and v_h (velocities at thickness
608 ! points), optionally using the entrainments (in m) passed in as arguments.
609 
610 ! Arguments: u - Zonal velocity, in m s-1.
611 ! (in) v - Meridional velocity, in m s-1.
612 ! (in) h - Layer thickness, in m or kg m-2.
613 ! (out) u_h - The zonal velocity at thickness points after
614 ! entrainment, in m s-1.
615 ! (out) v_h - The meridional velocity at thickness points after
616 ! entrainment, in m s-1.
617 ! (in) G - The ocean's grid structure.
618 ! (in) GV - The ocean's vertical grid structure.
619 ! (in, opt) ea - The amount of fluid entrained from the layer above within
620 ! this time step, in units of m or kg m-2. Omitting ea is the
621 ! same as setting it to 0.
622 ! (in, opt) eb - The amount of fluid entrained from the layer below within
623 ! this time step, in units of m or kg m-2. Omitting eb is the
624 ! same as setting it to 0. ea and eb must either be both
625 ! present or both absent.
626 
627  real :: b_denom_1 ! The first term in the denominator of b1 in m or kg m-2.
628  real :: h_neglect ! A thickness that is so small it is usually lost
629  ! in roundoff and can be neglected, in m or kg m-2.
630  real :: b1(szi_(g)), d1(szi_(g)), c1(szi_(g),szk_(g))
631  real :: a_n(szi_(g)), a_s(szi_(g)) ! Fractional weights of the neighboring
632  real :: a_e(szi_(g)), a_w(szi_(g)) ! velocity points, ~1/2 in the open
633  ! ocean, nondimensional.
634  real :: s, Idenom
635  logical :: mix_vertically
636  integer :: i, j, k, is, ie, js, je, nz
637  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
638  call cpu_clock_begin(id_clock_uv_at_h)
639  h_neglect = gv%H_subroundoff
640 
641  mix_vertically = present(ea)
642  if (present(ea) .neqv. present(eb)) call mom_error(fatal, &
643  "find_uv_at_h: Either both ea and eb or neither one must be present "// &
644  "in call to find_uv_at_h.")
645 !$OMP parallel do default(none) shared(is,ie,js,je,G,GV,mix_vertically,h,h_neglect, &
646 !$OMP eb,u_h,u,v_h,v,nz,ea) &
647 !$OMP private(s,Idenom,a_w,a_e,a_s,a_n,b_denom_1,b1,d1,c1)
648  do j=js,je
649  do i=is,ie
650  s = g%areaCu(i-1,j)+g%areaCu(i,j)
651  if (s>0.0) then
652  idenom = sqrt(0.5*g%IareaT(i,j)/s)
653  a_w(i) = g%areaCu(i-1,j)*idenom
654  a_e(i) = g%areaCu(i,j)*idenom
655  else
656  a_w(i) = 0.0 ; a_e(i) = 0.0
657  endif
658 
659  s = g%areaCv(i,j-1)+g%areaCv(i,j)
660  if (s>0.0) then
661  idenom = sqrt(0.5*g%IareaT(i,j)/s)
662  a_s(i) = g%areaCv(i,j-1)*idenom
663  a_n(i) = g%areaCv(i,j)*idenom
664  else
665  a_s(i) = 0.0 ; a_n(i) = 0.0
666  endif
667  enddo
668 
669  if (mix_vertically) then
670  do i=is,ie
671  b_denom_1 = h(i,j,1) + h_neglect
672  b1(i) = 1.0 / (b_denom_1 + eb(i,j,1))
673  d1(i) = b_denom_1 * b1(i)
674  u_h(i,j,1) = (h(i,j,1)*b1(i)) * (a_e(i)*u(i,j,1) + a_w(i)*u(i-1,j,1))
675  v_h(i,j,1) = (h(i,j,1)*b1(i)) * (a_n(i)*v(i,j,1) + a_s(i)*v(i,j-1,1))
676  enddo
677  do k=2,nz ; do i=is,ie
678  c1(i,k) = eb(i,j,k-1) * b1(i)
679  b_denom_1 = h(i,j,k) + d1(i)*ea(i,j,k) + h_neglect
680  b1(i) = 1.0 / (b_denom_1 + eb(i,j,k))
681  d1(i) = b_denom_1 * b1(i)
682  u_h(i,j,k) = (h(i,j,k) * (a_e(i)*u(i,j,k) + a_w(i)*u(i-1,j,k)) + &
683  ea(i,j,k)*u_h(i,j,k-1))*b1(i)
684  v_h(i,j,k) = (h(i,j,k) * (a_n(i)*v(i,j,k) + a_s(i)*v(i,j-1,k)) + &
685  ea(i,j,k)*v_h(i,j,k-1))*b1(i)
686  enddo ; enddo
687  do k=nz-1,1,-1 ; do i=is,ie
688  u_h(i,j,k) = u_h(i,j,k) + c1(i,k+1)*u_h(i,j,k+1)
689  v_h(i,j,k) = v_h(i,j,k) + c1(i,k+1)*v_h(i,j,k+1)
690  enddo ; enddo
691  else
692  do k=1,nz ; do i=is,ie
693  u_h(i,j,k) = a_e(i)*u(i,j,k) + a_w(i)*u(i-1,j,k)
694  v_h(i,j,k) = a_n(i)*v(i,j,k) + a_s(i)*v(i,j-1,k)
695  enddo ; enddo
696  endif
697  enddo
698 
699  call cpu_clock_end(id_clock_uv_at_h)
700 end subroutine find_uv_at_h
701 
702 
703 !> Diagnose a mixed layer depth (MLD) determined by a given density difference with the surface.
704 !> This routine is appropriate in MOM_diabatic_driver due to its position within the time stepping.
705 subroutine diagnosemldbydensitydifference(id_MLD, h, tv, densityDiff, G, GV, diagPtr, id_N2subML, id_MLDsq)
706  type(ocean_grid_type), intent(in) :: G !< Grid type
707  type(verticalgrid_type), intent(in) :: GV !< ocean vertical grid structure
708  integer, intent(in) :: id_MLD !< Handle (ID) of MLD diagnostic
709  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thickness
710  type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics type
711  real, intent(in) :: densityDiff !< Density difference to determine MLD (kg/m3)
712  type(diag_ctrl), pointer :: diagPtr !< Diagnostics structure
713  integer, optional, intent(in) :: id_N2subML !< Optional handle (ID) of subML stratification
714  integer, optional, intent(in) :: id_MLDsq !< Optional handle (ID) of squared MLD
715 
716  ! Local variables
717  real, dimension(SZI_(G)) :: rhoSurf, deltaRhoAtKm1, deltaRhoAtK, dK, dKm1, pRef_MLD
718  real, dimension(SZI_(G)) :: rhoAtK, rho1, d1, pRef_N2 ! Used for N2
719  real, dimension(SZI_(G), SZJ_(G)) :: MLD ! Diagnosed mixed layer depth
720  real, dimension(SZI_(G), SZJ_(G)) :: subMLN2 ! Diagnosed stratification below ML
721  real, dimension(SZI_(G), SZJ_(G)) :: MLD2 ! Diagnosed MLD^2
722  real, parameter :: dz_subML = 50. ! Depth below ML over which to diagnose stratification (m)
723  integer :: i, j, is, ie, js, je, k, nz, id_N2, id_SQ
724  real :: aFac, ddRho
725 
726  id_n2 = -1
727  if (PRESENT(id_n2subml)) id_n2 = id_n2subml
728 
729  id_sq = -1
730  if (PRESENT(id_n2subml)) id_sq = id_mldsq
731 
732  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
733  pref_mld(:) = 0. ; pref_n2(:) = 0.
734  do j=js,je
735  do i=is,ie ; dk(i) = 0.5 * h(i,j,1) * gv%H_to_m ; enddo ! Depth of center of surface layer
736  call calculate_density(tv%T(:,j,1), tv%S(:,j,1), pref_mld, rhosurf, is, ie-is+1, tv%eqn_of_state)
737  do i=is,ie
738  deltarhoatk(i) = 0.
739  mld(i,j) = 0.
740  if (id_n2>0) then
741  submln2(i,j) = 0.
742  rho1(i) = 0.
743  d1(i) = 0.
744  pref_n2(i) = gv%g_Earth * gv%Rho0 * h(i,j,1) * gv%H_to_m ! Boussinesq approximation!!!! ?????
745  !### This should be: pRef_N2(i) = GV%g_Earth * GV%H_to_kg_m2 * h(i,j,1) ! This might change answers at roundoff.
746  endif
747  enddo
748  do k=2,nz
749  do i=is,ie
750  dkm1(i) = dk(i) ! Depth of center of layer K-1
751  dk(i) = dk(i) + 0.5 * ( h(i,j,k) + h(i,j,k-1) ) * gv%H_to_m ! Depth of center of layer K
752  enddo
753 
754  ! Stratification, N2, immediately below the mixed layer, averaged over at least 50 m.
755  if (id_n2>0) then
756  do i=is,ie
757  pref_n2(i) = pref_n2(i) + gv%g_Earth * gv%Rho0 * h(i,j,k) * gv%H_to_m ! Boussinesq approximation!!!! ?????
758  !### This should be: pRef_N2(i) = pRev_N2(i) + GV%g_Earth * GV%H_to_kg_m2 * h(i,j,k) ! This might change answers at roundoff.
759  enddo
760  call calculate_density(tv%T(:,j,k), tv%S(:,j,k), pref_n2, rhoatk, is, ie-is+1, tv%eqn_of_state)
761  do i=is,ie
762  if (mld(i,j)>0. .and. submln2(i,j)==0.) then ! This block is below the mixed layer
763  if (d1(i)==0.) then ! Record the density, depth and pressure, immediately below the ML
764  rho1(i) = rhoatk(i)
765  d1(i) = dk(i)
766  !### It looks to me like there is bad logic here. - RWH
767  ! Use pressure at the bottom of the upper layer used in calculating d/dz rho
768  pref_n2(i) = pref_n2(i) + gv%g_Earth * gv%Rho0 * h(i,j,k) * gv%H_to_m ! Boussinesq approximation!!!! ?????
769  !### This line should be: pRef_N2(i) = pRev_N2(i) + GV%g_Earth * GV%H_to_kg_m2 * h(i,j,k) ! This might change answers at roundoff.
770  endif
771  if (d1(i)>0. .and. dk(i)-d1(i)>=dz_subml) then
772  submln2(i,j) = gv%g_Earth/ gv%Rho0 * (rho1(i)-rhoatk(i)) / (d1(i) - dk(i))
773  endif
774  endif
775  enddo ! i-loop
776  endif ! id_N2>0
777 
778  ! Mixed-layer depth, using sigma-0 (surface reference pressure)
779  do i=is,ie ; deltarhoatkm1(i) = deltarhoatk(i) ; enddo ! Store value from previous iteration of K
780  call calculate_density(tv%T(:,j,k), tv%S(:,j,k), pref_mld, deltarhoatk, is, ie-is+1, tv%eqn_of_state)
781  do i = is, ie
782  deltarhoatk(i) = deltarhoatk(i) - rhosurf(i) ! Density difference between layer K and surface
783  ddrho = deltarhoatk(i) - deltarhoatkm1(i)
784  if ((mld(i,j)==0.) .and. (ddrho>0.) .and. &
785  (deltarhoatkm1(i)<densitydiff) .and. (deltarhoatk(i)>=densitydiff)) then
786  afac = ( densitydiff - deltarhoatkm1(i) ) / ddrho
787  mld(i,j) = dk(i) * afac + dkm1(i) * (1. - afac)
788  endif
789  if (id_sq > 0) mld2(i,j) = mld(i,j)**2
790  enddo ! i-loop
791  enddo ! k-loop
792  do i=is,ie
793  if ((mld(i,j)==0.) .and. (deltarhoatk(i)<densitydiff)) mld(i,j) = dk(i) ! Assume mixing to the bottom
794  ! if (id_N2>0 .and. subMLN2(i,j)==0. .and. d1(i)>0. .and. dK(i)-d1(i)>0.) then
795  ! ! Use what ever stratification we can, measured over what ever distance is available
796  ! subMLN2(i,j) = GV%g_Earth/ GV%Rho0 * (rho1(i)-rhoAtK(i)) / (d1(i) - dK(i))
797  ! endif
798  enddo
799  enddo ! j-loop
800 
801  if (id_mld > 0) call post_data(id_mld, mld, diagptr)
802  if (id_n2 > 0) call post_data(id_n2, submln2 , diagptr)
803  if (id_sq > 0) call post_data(id_sq, mld2, diagptr)
804 
805 end subroutine diagnosemldbydensitydifference
806 
807 !> Update the thickness, temperature, and salinity due to thermodynamic
808 !! boundary forcing (contained in fluxes type) applied to h, tv%T and tv%S,
809 !! and calculate the TKE implications of this heating.
810 subroutine applyboundaryfluxesinout(CS, G, GV, dt, fluxes, optics, h, tv, &
811  aggregate_FW_forcing, evap_CFL_limit, &
812  minimum_forcing_depth, cTKE, dSV_dT, dSV_dS, &
813  SkinBuoyFlux )
814  type(diabatic_aux_cs), pointer :: CS !< Control structure for diabatic_aux
815  type(ocean_grid_type), intent(in) :: G !< Grid structure
816  type(verticalgrid_type), intent(in) :: GV !< ocean vertical grid structure
817  real, intent(in) :: dt !< Time-step over which forcing is applied (s)
818  type(forcing), intent(inout) :: fluxes !< Surface fluxes container
819  type(optics_type), pointer :: optics !< Optical properties container
820  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: h !< Layer thickness in H units
821  type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamics container
822  !> If False, treat in/out fluxes separately.
823  logical, intent(in) :: aggregate_FW_forcing
824  !> The largest fraction of a layer that can be evaporated in one time-step (non-dim).
825  real, intent(in) :: evap_CFL_limit
826  !> The smallest depth over which heat and freshwater fluxes is applied, in m.
827  real, intent(in) :: minimum_forcing_depth
828  !> Turbulent kinetic energy requirement to mix forcing through each layer, in W m-2
829  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), optional, intent(out) :: cTKE
830  !> Partial derivative of specific volume with potential temperature, in m3 kg-1 K-1.
831  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), optional, intent(out) :: dSV_dT
832  !> Partial derivative of specific a volume with potential salinity, in m3 kg-1 / (g kg-1).
833  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), optional, intent(out) :: dSV_dS
834  !> Buoyancy flux at surface in m2 s-3
835  real, dimension(SZI_(G),SZJ_(G)), optional, intent(out) :: SkinBuoyFlux
836 
837  ! Local variables
838  integer, parameter :: maxGroundings = 5
839  integer :: numberOfGroundings, iGround(maxgroundings), jGround(maxgroundings)
840  real :: H_limit_fluxes, IforcingDepthScale, Idt
841  real :: dThickness, dTemp, dSalt
842  real :: fractionOfForcing, hOld, Ithickness
843  real :: RivermixConst ! A constant used in implementing river mixing, in Pa s.
844  real, dimension(SZI_(G)) :: &
845  d_pres, & ! pressure change across a layer (Pa)
846  p_lay, & ! average pressure in a layer (Pa)
847  pres, & ! pressure at an interface (Pa)
848  netMassInOut, & ! surface water fluxes (H units) over time step
849  netMassIn, & ! mass entering ocean surface (H units) over a time step
850  netMassOut, & ! mass leaving ocean surface (H units) over a time step
851  netHeat, & ! heat (degC * H) via surface fluxes, excluding
852  ! Pen_SW_bnd and netMassOut
853  netsalt, & ! surface salt flux ( g(salt)/m2 for non-Bouss and ppt*H for Bouss )
854  nonpensw, & ! non-downwelling SW, which is absorbed at ocean surface
855  surfpressure, & ! Surface pressure (approximated as 0.0)
856  drhodt, & ! change in density per change in temperature
857  drhods, & ! change in density per change in salinity
858  netheat_rate, & ! netheat but for dt=1 (e.g. returns a rate)
859  netsalt_rate, & ! netsalt but for dt=1 (e.g. returns a rate)
860  netmassinout_rate! netmassinout but for dt=1 (e.g. returns a rate)
861  real, dimension(SZI_(G), SZK_(G)) :: h2d, T2d
862  real, dimension(SZI_(G), SZK_(G)) :: pen_TKE_2d, dSV_dT_2d
863  real, dimension(SZI_(G),SZK_(G)+1) :: netPen
864  real, dimension(max(optics%nbands,1),SZI_(G)) :: Pen_SW_bnd, Pen_SW_bnd_rate
865  !^ _rate is w/ dt=1
866  real, dimension(max(optics%nbands,1),SZI_(G),SZK_(G)) :: opacityBand
867  real :: hGrounding(maxgroundings)
868  real :: Temp_in, Salin_in
869  real :: I_G_Earth, g_Hconv2
870  real :: GoRho
871  logical :: calculate_energetics
872  logical :: calculate_buoyancy
873  integer :: i, j, is, ie, js, je, k, nz, n, nsw
874  integer :: start, npts
875  character(len=45) :: mesg
876 
877  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
878 
879  ! Only apply forcing if fluxes%sw is associated.
880  if (.not.ASSOCIATED(fluxes%sw)) return
881 
882 #define _OLD_ALG_
883  nsw = optics%nbands
884  idt = 1.0/dt
885 
886  calculate_energetics = (present(ctke) .and. present(dsv_dt) .and. present(dsv_ds))
887  calculate_buoyancy = present(skinbuoyflux)
888  if (calculate_buoyancy) skinbuoyflux(:,:) = 0.0
889  i_g_earth = 1.0 / gv%g_Earth
890  g_hconv2 = gv%g_Earth * gv%H_to_kg_m2**2
891 
892  if (present(ctke)) ctke(:,:,:) = 0.0
893  if (calculate_buoyancy) then
894  surfpressure(:) = 0.0
895  gorho = gv%g_Earth / gv%Rho0
896  start = 1 + g%isc - g%isd
897  npts = 1 + g%iec - g%isc
898  endif
899 
900  ! H_limit_fluxes is used by extractFluxes1d to scale down fluxes if the total
901  ! depth of the ocean is vanishing. It does not (yet) handle a value of zero.
902  ! To accommodate vanishing upper layers, we need to allow for an instantaneous
903  ! distribution of forcing over some finite vertical extent. The bulk mixed layer
904  ! code handles this issue properly.
905  h_limit_fluxes = max(gv%Angstrom, 1.e-30*gv%m_to_H)
906 
907  ! diagnostic to see if need to create mass to avoid grounding
908  if (cs%id_createdH>0) cs%createdH(:,:) = 0.
909  numberofgroundings = 0
910 
911 !$OMP parallel do default(none) shared(is,ie,js,je,nz,h,tv,nsw,G,GV,optics,fluxes,dt, &
912 !$OMP H_limit_fluxes,IforcingDepthScale, &
913 !$OMP numberOfGroundings,iGround,jGround,nonPenSW, &
914 !$OMP hGrounding,CS,Idt,aggregate_FW_forcing, &
915 !$OMP minimum_forcing_depth,evap_CFL_limit, &
916 !$OMP calculate_buoyancy,netPen,SkinBuoyFlux,GoRho, &
917 !$OMP calculate_energetics,dSV_dT,dSV_dS,cTKE,g_Hconv2) &
918 !$OMP private(opacityBand,h2d,T2d,netMassInOut,netMassOut, &
919 !$OMP netHeat,netSalt,Pen_SW_bnd,fractionOfForcing, &
920 !$OMP dThickness,dTemp,dSalt,hOld,Ithickness, &
921 !$OMP netMassIn,pres,d_pres,p_lay,dSV_dT_2d, &
922 !$OMP netmassinout_rate,netheat_rate,netsalt_rate, &
923 !$OMP drhodt,drhods,pen_sw_bnd_rate,SurfPressure, &
924 !$OMP start,npts, &
925 !$OMP pen_TKE_2d,Temp_in,Salin_in,RivermixConst)
926 
927  ! Work in vertical slices for efficiency
928  do j=js,je
929 
930  ! Copy state into 2D-slice arrays
931  do k=1,nz ; do i=is,ie
932  h2d(i,k) = h(i,j,k)
933  t2d(i,k) = tv%T(i,j,k)
934  do n=1,nsw
935  opacityband(n,i,k) = (1.0 / gv%m_to_H)*optics%opacity_band(n,i,j,k)
936  enddo
937  enddo ; enddo
938 
939  if (calculate_energetics) then
940  ! The partial derivatives of specific volume with temperature and
941  ! salinity need to be precalculated to avoid having heating of
942  ! tiny layers give nonsensical values.
943  do i=is,ie ; pres(i) = 0.0 ; enddo ! Add surface pressure?
944  do k=1,nz
945  do i=is,ie
946  d_pres(i) = gv%g_Earth * gv%H_to_kg_m2 * h2d(i,k)
947  p_lay(i) = pres(i) + 0.5*d_pres(i)
948  pres(i) = pres(i) + d_pres(i)
949  enddo
950  call calculate_specific_vol_derivs(t2d(:,k), tv%S(:,j,k), p_lay(:),&
951  dsv_dt(:,j,k), dsv_ds(:,j,k), is, ie-is+1, tv%eqn_of_state)
952  do i=is,ie ; dsv_dt_2d(i,k) = dsv_dt(i,j,k) ; enddo
953 ! do i=is,ie
954 ! dT_to_dPE(i,k) = I_G_Earth * d_pres(i) * p_lay(i) * dSV_dT(i,j,k)
955 ! dS_to_dPE(i,k) = I_G_Earth * d_pres(i) * p_lay(i) * dSV_dS(i,j,k)
956 ! enddo
957  enddo
958  pen_tke_2d(:,:) = 0.0
959  endif
960 
961  ! The surface forcing is contained in the fluxes type.
962  ! We aggregate the thermodynamic forcing for a time step into the following:
963  ! netMassInOut = surface water fluxes (H units) over time step
964  ! = lprec + fprec + vprec + evap + lrunoff + frunoff
965  ! note that lprec generally has sea ice melt/form included.
966  ! netMassOut = net mass leaving ocean surface (H units) over a time step.
967  ! netMassOut < 0 means mass leaves ocean.
968  ! netHeat = heat (degC * H) via surface fluxes, excluding the part
969  ! contained in Pen_SW_bnd; and excluding heat_content of netMassOut < 0.
970  ! netSalt = surface salt fluxes ( g(salt)/m2 for non-Bouss and ppt*H for Bouss )
971  ! Pen_SW_bnd = components to penetrative shortwave radiation split according to bands.
972  ! This field provides that portion of SW from atmosphere that in fact
973  ! enters to the ocean and participates in pentrative SW heating.
974  ! nonpenSW = non-downwelling SW flux, which is absorbed in ocean surface
975  ! (in tandem w/ LW,SENS,LAT); saved only for diagnostic purposes.
976 
977  !----------------------------------------------------------------------------------------
978  !BGR-June 26, 2017{
979  !Temporary action to preserve answers while fixing a bug.
980  ! To fix a bug in a diagnostic calculation, applyboundaryfluxesinout now returns
981  ! the surface buoyancy flux. Previously, extractbuoyancyflux2d was called, meaning
982  ! a second call to extractfluxes1d (causing the diagnostic net_heat to be incorrect).
983  ! Note that this call to extract buoyancyflux2d was AFTER applyboundaryfluxesinout,
984  ! which means it used the T/S fields after this routine. Therefore, the surface
985  ! buoyancy flux is computed here at the very end of this routine for legacy reasons.
986  ! A few specific notes follow:
987  ! 1) The old method did not included river/calving contributions to heat flux. This
988  ! is kept consistent here via commenting code in the present extractFluxes1d <_rate>
989  ! outputs, but we may reconsider this approach.
990  ! 2) The old method computed the buoyancy flux rate directly (by setting dt=1), instead
991  ! of computing the integrated value (and dividing by dt). Hence the required
992  ! additional outputs from extractFluxes1d.
993  ! *** This is because: A*dt/dt =/= A due to round off.
994  ! 3) The old method computed buoyancy flux after this routine, meaning the returned
995  ! surface fluxes (from extractfluxes1d) must be recorded for use later in the code.
996  ! We could (and maybe should) move that loop up to before the surface fluxes are
997  ! applied, but this will change answers.
998  ! For all these reasons we compute additional values of <_rate> which are preserved
999  ! for the buoyancy flux calculation and reproduce the old answers.
1000  ! In the future this needs more detailed investigation to make sure everything is
1001  ! consistent and correct. These details shouldnt significantly effect climate,
1002  ! but do change answers.
1003  !-----------------------------------------------------------------------------------------
1004  if (calculate_buoyancy) then
1005  call extractfluxes1d(g, gv, fluxes, optics, nsw, j, dt, &
1006  h_limit_fluxes, cs%use_river_heat_content, cs%use_calving_heat_content, &
1007  h2d, t2d, netmassinout, netmassout, netheat, netsalt, &
1008  pen_sw_bnd, tv, aggregate_fw_forcing, nonpensw=nonpensw, &
1009  net_heat_rate=netheat_rate,net_salt_rate=netsalt_rate, &
1010  netmassinout_rate=netmassinout_rate,pen_sw_bnd_rate=pen_sw_bnd_rate)
1011  else
1012  call extractfluxes1d(g, gv, fluxes, optics, nsw, j, dt, &
1013  h_limit_fluxes, cs%use_river_heat_content, cs%use_calving_heat_content, &
1014  h2d, t2d, netmassinout, netmassout, netheat, netsalt, &
1015  pen_sw_bnd, tv, aggregate_fw_forcing, nonpensw=nonpensw)
1016  endif
1017  ! ea is for passive tracers
1018  do i=is,ie
1019  ! ea(i,j,1) = netMassInOut(i)
1020  if (aggregate_fw_forcing) then
1021  netmassout(i) = netmassinout(i)
1022  netmassin(i) = 0.
1023  else
1024  netmassin(i) = netmassinout(i) - netmassout(i)
1025  endif
1026  if (g%mask2dT(i,j)>0.0) then
1027  fluxes%netMassOut(i,j) = netmassout(i)
1028  fluxes%netMassIn(i,j) = netmassin(i)
1029  else
1030  fluxes%netMassOut(i,j) = 0.0
1031  fluxes%netMassIn(i,j) = 0.0
1032  endif
1033  enddo
1034 
1035  ! Apply the surface boundary fluxes in three steps:
1036  ! A/ update mass, temp, and salinity due to all terms except mass leaving
1037  ! ocean (and corresponding outward heat content), and ignoring penetrative SW.
1038  ! B/ update mass, salt, temp from mass leaving ocean.
1039  ! C/ update temp due to penetrative SW
1040  do i=is,ie
1041  if (g%mask2dT(i,j)>0.) then
1042 
1043  ! A/ Update mass, temp, and salinity due to incoming mass flux.
1044  do k=1,1
1045 
1046  ! Change in state due to forcing
1047  dthickness = netmassin(i) ! Since we are adding mass, we can use all of it
1048  dtemp = 0.
1049  dsalt = 0.
1050 
1051  ! Update the forcing by the part to be consumed within the present k-layer.
1052  ! If fractionOfForcing = 1, then updated netMassIn, netHeat, and netSalt vanish.
1053  netmassin(i) = netmassin(i) - dthickness
1054  ! This line accounts for the temperature of the mass exchange
1055  temp_in = t2d(i,k)
1056  salin_in = 0.0
1057  dtemp = dtemp + dthickness*temp_in
1058 
1059  ! Diagnostics of heat content associated with mass fluxes
1060  if (ASSOCIATED(fluxes%heat_content_massin)) &
1061  fluxes%heat_content_massin(i,j) = fluxes%heat_content_massin(i,j) + &
1062  t2d(i,k) * max(0.,dthickness) * gv%H_to_kg_m2 * fluxes%C_p * idt
1063  if (ASSOCIATED(fluxes%heat_content_massout)) &
1064  fluxes%heat_content_massout(i,j) = fluxes%heat_content_massout(i,j) + &
1065  t2d(i,k) * min(0.,dthickness) * gv%H_to_kg_m2 * fluxes%C_p * idt
1066  if (ASSOCIATED(tv%TempxPmE)) tv%TempxPmE(i,j) = tv%TempxPmE(i,j) + &
1067  t2d(i,k) * dthickness * gv%H_to_kg_m2
1068 
1069  ! Determine the energetics of river mixing before updating the state.
1070  if (calculate_energetics .and. associated(fluxes%lrunoff) .and. cs%do_rivermix) then
1071  ! Here we add an additional source of TKE to the mixed layer where river
1072  ! is present to simulate unresolved estuaries. The TKE input is diagnosed
1073  ! as follows:
1074  ! TKE_river[m3 s-3] = 0.5*rivermix_depth*g*(1/rho)*drho_ds*
1075  ! River*(Samb - Sriver) = CS%mstar*U_star^3
1076  ! where River is in units of m s-1.
1077  ! Samb = Ambient salinity at the mouth of the estuary
1078  ! rivermix_depth = The prescribed depth over which to mix river inflow
1079  ! drho_ds = The gradient of density wrt salt at the ambient surface salinity.
1080  ! Sriver = 0 (i.e. rivers are assumed to be pure freshwater)
1081  rivermixconst = -0.5*(cs%rivermix_depth*dt)*gv%m_to_H*gv%H_to_Pa
1082 
1083  ctke(i,j,k) = ctke(i,j,k) + max(0.0, rivermixconst*dsv_ds(i,j,1) * &
1084  (fluxes%lrunoff(i,j) + fluxes%frunoff(i,j)) * tv%S(i,j,1))
1085  endif
1086 
1087  ! Update state
1088  hold = h2d(i,k) ! Keep original thickness in hand
1089  h2d(i,k) = h2d(i,k) + dthickness ! New thickness
1090  if (h2d(i,k) > 0.0) then
1091  if (calculate_energetics .and. (dthickness > 0.)) then
1092  ! Calculate the energy required to mix the newly added water over
1093  ! the topmost grid cell. ###CHECK THE SIGNS!!!
1094  ctke(i,j,k) = ctke(i,j,k) + 0.5*g_hconv2*(hold*dthickness) * &
1095  ((t2d(i,k) - temp_in) * dsv_dt(i,j,k) + (tv%S(i,j,k) - salin_in) * dsv_ds(i,j,k))
1096  endif
1097  ithickness = 1.0/h2d(i,k) ! Inverse new thickness
1098  ! The "if"s below avoid changing T/S by roundoff unnecessarily
1099  if (dthickness /= 0. .or. dtemp /= 0.) t2d(i,k) = (hold*t2d(i,k) + dtemp)*ithickness
1100  if (dthickness /= 0. .or. dsalt /= 0.) tv%S(i,j,k) = (hold*tv%S(i,j,k) + dsalt)*ithickness
1101 
1102  endif
1103 
1104  enddo ! k=1,1
1105 
1106  ! B/ Update mass, salt, temp from mass leaving ocean and other fluxes of heat and salt.
1107  do k=1,nz
1108 
1109  ! Place forcing into this layer if this layer has nontrivial thickness.
1110  ! For layers thin relative to 1/IforcingDepthScale, then distribute
1111  ! forcing into deeper layers.
1112  iforcingdepthscale = 1. / max(gv%H_subroundoff, minimum_forcing_depth*gv%m_to_H - netmassout(i) )
1113  ! fractionOfForcing = 1.0, unless h2d is less than IforcingDepthScale.
1114  fractionofforcing = min(1.0, h2d(i,k)*iforcingdepthscale)
1115 
1116  ! In the case with (-1)*netMassOut*fractionOfForcing greater than cfl*h, we
1117  ! limit the forcing applied to this cell, leaving the remaining forcing to
1118  ! be distributed downwards.
1119  if (-fractionofforcing*netmassout(i) > evap_cfl_limit*h2d(i,k)) then
1120  fractionofforcing = -evap_cfl_limit*h2d(i,k)/netmassout(i)
1121  endif
1122 
1123  ! Change in state due to forcing
1124 
1125  dthickness = max( fractionofforcing*netmassout(i), -h2d(i,k) )
1126  dtemp = fractionofforcing*netheat(i)
1127  ! ### The 0.9999 here should become a run-time parameter?
1128  dsalt = max( fractionofforcing*netsalt(i), -0.9999*h2d(i,k)*tv%S(i,j,k))
1129 
1130  ! Update the forcing by the part to be consumed within the present k-layer.
1131  ! If fractionOfForcing = 1, then new netMassOut vanishes.
1132  netmassout(i) = netmassout(i) - dthickness
1133  netheat(i) = netheat(i) - dtemp
1134  netsalt(i) = netsalt(i) - dsalt
1135 
1136  ! This line accounts for the temperature of the mass exchange
1137  dtemp = dtemp + dthickness*t2d(i,k)
1138 
1139  ! Diagnostics of heat content associated with mass fluxes
1140  if (ASSOCIATED(fluxes%heat_content_massin)) &
1141  fluxes%heat_content_massin(i,j) = fluxes%heat_content_massin(i,j) + &
1142  tv%T(i,j,k) * max(0.,dthickness) * gv%H_to_kg_m2 * fluxes%C_p * idt
1143  if (ASSOCIATED(fluxes%heat_content_massout)) &
1144  fluxes%heat_content_massout(i,j) = fluxes%heat_content_massout(i,j) + &
1145  tv%T(i,j,k) * min(0.,dthickness) * gv%H_to_kg_m2 * fluxes%C_p * idt
1146  if (ASSOCIATED(tv%TempxPmE)) tv%TempxPmE(i,j) = tv%TempxPmE(i,j) + &
1147  tv%T(i,j,k) * dthickness * gv%H_to_kg_m2
1148 !NOTE tv%T should be T2d
1149 
1150  ! Update state by the appropriate increment.
1151  hold = h2d(i,k) ! Keep original thickness in hand
1152  h2d(i,k) = h2d(i,k) + dthickness ! New thickness
1153 
1154  if (h2d(i,k) > 0.) then
1155  if (calculate_energetics) then
1156  ! Calculate the energy required to mix the newly added water over
1157  ! the topmost grid cell, assuming that the fluxes of heat and salt
1158  ! and rejected brine are initially applied in vanishingly thin
1159  ! layers at the top of the layer before being mixed throughout
1160  ! the layer. Note that dThickness is always <= 0. ###CHECK THE SIGNS!!!
1161  ctke(i,j,k) = ctke(i,j,k) - (0.5*h2d(i,k)*g_hconv2) * &
1162  ((dtemp - dthickness*t2d(i,k)) * dsv_dt(i,j,k) + &
1163  (dsalt - dthickness*tv%S(i,j,k)) * dsv_ds(i,j,k))
1164  endif
1165  ithickness = 1.0/h2d(i,k) ! Inverse of new thickness
1166  t2d(i,k) = (hold*t2d(i,k) + dtemp)*ithickness
1167  tv%S(i,j,k) = (hold*tv%S(i,j,k) + dsalt)*ithickness
1168  elseif (h2d(i,k) < 0.0) then ! h2d==0 is a special limit that needs no extra handling
1169  call forcing_singlepointprint(fluxes,g,i,j,'applyBoundaryFluxesInOut (h<0)')
1170  write(0,*) 'applyBoundaryFluxesInOut(): lon,lat=',g%geoLonT(i,j),g%geoLatT(i,j)
1171  write(0,*) 'applyBoundaryFluxesInOut(): netT,netS,netH=',netheat(i),netsalt(i),netmassinout(i)
1172  write(0,*) 'applyBoundaryFluxesInOut(): dT,dS,dH=',dtemp,dsalt,dthickness
1173  write(0,*) 'applyBoundaryFluxesInOut(): h(n),h(n+1),k=',hold,h2d(i,k),k
1174  call mom_error(fatal, "MOM_diabatic_driver.F90, applyBoundaryFluxesInOut(): "//&
1175  "Complete mass loss in column!")
1176  endif
1177 
1178  enddo ! k
1179 
1180  ! Check if trying to apply fluxes over land points
1181  elseif((abs(netheat(i))+abs(netsalt(i))+abs(netmassin(i))+abs(netmassout(i)))>0.) then
1182 
1183  if (.not. cs%ignore_fluxes_over_land) then
1184  call forcing_singlepointprint(fluxes,g,i,j,'applyBoundaryFluxesInOut (land)')
1185  write(0,*) 'applyBoundaryFluxesInOut(): lon,lat=',g%geoLonT(i,j),g%geoLatT(i,j)
1186  write(0,*) 'applyBoundaryFluxesInOut(): netHeat,netSalt,netMassIn,netMassOut=',&
1187  netheat(i),netsalt(i),netmassin(i),netmassout(i)
1188 
1189  call mom_error(fatal, "MOM_diabatic_driver.F90, applyBoundaryFluxesInOut(): "//&
1190  "Mass loss over land?")
1191  endif
1192 
1193  endif
1194 
1195  ! If anything remains after the k-loop, then we have grounded out, which is a problem.
1196  if (netmassin(i)+netmassout(i) /= 0.0) then
1197 !$OMP critical
1198  numberofgroundings = numberofgroundings +1
1199  if (numberofgroundings<=maxgroundings) then
1200  iground(numberofgroundings) = i ! Record i,j location of event for
1201  jground(numberofgroundings) = j ! warning message
1202  hgrounding(numberofgroundings) = netmassin(i)+netmassout(i)
1203  endif
1204 !$OMP end critical
1205  if (cs%id_createdH>0) cs%createdH(i,j) = cs%createdH(i,j) - (netmassin(i)+netmassout(i))/dt
1206  endif
1207 
1208  enddo ! i
1209 
1210  ! Step C/ in the application of fluxes
1211  ! Heat by the convergence of penetrating SW.
1212  ! SW penetrative heating uses the updated thickness from above.
1213 
1214  ! Save temperature before increment with SW heating
1215  ! and initialize CS%penSWflux_diag to zero.
1216  if(cs%id_penSW_diag > 0 .or. cs%id_penSWflux_diag > 0) then
1217  do k=1,nz ; do i=is,ie
1218  cs%penSW_diag(i,j,k) = t2d(i,k)
1219  cs%penSWflux_diag(i,j,k) = 0.0
1220  enddo ; enddo
1221  k=nz+1 ; do i=is,ie
1222  cs%penSWflux_diag(i,j,k) = 0.0
1223  enddo
1224  endif
1225 
1226  if (calculate_energetics) then
1227  call absorbremainingsw(g, gv, h2d, opacityband, nsw, j, dt, h_limit_fluxes, &
1228  .false., .true., t2d, pen_sw_bnd, tke=pen_tke_2d, dsv_dt=dsv_dt_2d)
1229  k = 1 ! For setting break-points.
1230  do k=1,nz ; do i=is,ie
1231  ctke(i,j,k) = ctke(i,j,k) + pen_tke_2d(i,k)
1232  enddo ; enddo
1233  else
1234  call absorbremainingsw(g, gv, h2d, opacityband, nsw, j, dt, h_limit_fluxes, &
1235  .false., .true., t2d, pen_sw_bnd)
1236  endif
1237 
1238 
1239  ! Step D/ copy updated thickness and temperature
1240  ! 2d slice now back into model state.
1241  do k=1,nz ; do i=is,ie
1242  h(i,j,k) = h2d(i,k)
1243  tv%T(i,j,k) = t2d(i,k)
1244  enddo ; enddo
1245 
1246  ! Diagnose heating (W/m2) applied to a grid cell from SW penetration
1247  ! Also diagnose the penetrative SW heat flux at base of layer.
1248  if(cs%id_penSW_diag > 0 .or. cs%id_penSWflux_diag > 0) then
1249 
1250  ! convergence of SW into a layer
1251  do k=1,nz ; do i=is,ie
1252  cs%penSW_diag(i,j,k) = (t2d(i,k)-cs%penSW_diag(i,j,k))*h(i,j,k) * idt * tv%C_p * gv%H_to_kg_m2
1253  enddo ; enddo
1254 
1255  ! Perform a cumulative sum upwards from bottom to
1256  ! diagnose penetrative SW flux at base of tracer cell.
1257  ! CS%penSWflux_diag(i,j,k=1) is penetrative shortwave at top of ocean.
1258  ! CS%penSWflux_diag(i,j,k=kbot+1) is zero, since assume no SW penetrates rock.
1259  ! CS%penSWflux_diag = rsdo and CS%penSW_diag = rsdoabsorb
1260  ! rsdoabsorb(k) = rsdo(k) - rsdo(k+1), so that rsdo(k) = rsdo(k+1) + rsdoabsorb(k)
1261  if(cs%id_penSWflux_diag > 0) then
1262  do k=nz,1,-1 ; do i=is,ie
1263  cs%penSWflux_diag(i,j,k) = cs%penSW_diag(i,j,k) + cs%penSWflux_diag(i,j,k+1)
1264  enddo ; enddo
1265  endif
1266 
1267  endif
1268 
1269  ! Fill CS%nonpenSW_diag
1270  if(cs%id_nonpenSW_diag > 0) then
1271  do i=is,ie
1272  cs%nonpenSW_diag(i,j) = nonpensw(i)
1273  enddo
1274  endif
1275 
1276  ! BGR: Get buoyancy flux to return for ePBL
1277  ! We want the rate, so we use the rate values returned from extractfluxes1d.
1278  ! Note that the *dt values could be divided by dt here, but
1279  ! 1) Answers will change due to round-off
1280  ! 2) Be sure to save their values BEFORE fluxes are used.
1281  if (calculate_buoyancy) then
1282  drhodt(:) = 0.0
1283  drhods(:) = 0.0
1284  netpen(:,:) = 0.0
1285  ! Sum over bands and attenuate as a function of depth
1286  ! netPen is the netSW as a function of depth
1287  call sumswoverbands(g, gv, h2d(:,:), optics%opacity_band(:,:,j,:), nsw, j, dt, &
1288  h_limit_fluxes, .true., pen_sw_bnd_rate, netpen)
1289  ! Density derivatives
1290  call calculate_density_derivs(t2d(:,1), tv%S(:,j,1), surfpressure, &
1291  drhodt, drhods, start, npts, tv%eqn_of_state)
1292  ! 1. Adjust netSalt to reflect dilution effect of FW flux
1293  ! 2. Add in the SW heating for purposes of calculating the net
1294  ! surface buoyancy flux affecting the top layer.
1295  ! 3. Convert to a buoyancy flux, excluding penetrating SW heating
1296  ! BGR-Jul 5, 2017: The contribution of SW heating here needs investigated for ePBL.
1297  skinbuoyflux(g%isc:g%iec,j) = - gorho * ( drhods(g%isc:g%iec) * (netsalt_rate(g%isc:g%iec) &
1298  - tv%S(g%isc:g%iec,j,1) * netmassinout_rate(g%isc:g%iec)* gv%H_to_m )&
1299  + drhodt(g%isc:g%iec) * ( netheat_rate(g%isc:g%iec) + &
1300  netpen(g%isc:g%iec,1))) * gv%H_to_m ! m^2/s^3
1301  endif
1302 
1303  enddo ! j-loop finish
1304 
1305  ! Post the diagnostics
1306  if (cs%id_createdH > 0) call post_data(cs%id_createdH , cs%createdH , cs%diag)
1307  if (cs%id_penSW_diag > 0) call post_data(cs%id_penSW_diag , cs%penSW_diag , cs%diag)
1308  if (cs%id_penSWflux_diag > 0) call post_data(cs%id_penSWflux_diag, cs%penSWflux_diag, cs%diag)
1309  if (cs%id_nonpenSW_diag > 0) call post_data(cs%id_nonpenSW_diag , cs%nonpenSW_diag , cs%diag)
1310 
1311 ! The following check will be ignored if ignore_fluxes_over_land = true
1312  if (numberofgroundings>0 .and. .not. cs%ignore_fluxes_over_land) then
1313  do i = 1, min(numberofgroundings, maxgroundings)
1314  call forcing_singlepointprint(fluxes,g,iground(i),jground(i),'applyBoundaryFluxesInOut (grounding)')
1315  write(mesg(1:45),'(3es15.3)') g%geoLonT( iground(i), jground(i) ), &
1316  g%geoLatT( iground(i), jground(i)) , hgrounding(i)
1317  call mom_error(warning, "MOM_diabatic_driver.F90, applyBoundaryFluxesInOut(): "//&
1318  "Mass created. x,y,dh= "//trim(mesg), all_print=.true.)
1319  enddo
1320 
1321  if (numberofgroundings - maxgroundings > 0) then
1322  write(mesg, '(i4)') numberofgroundings - maxgroundings
1323  call mom_error(warning, "MOM_diabatic_driver:F90, applyBoundaryFluxesInOut(): "//&
1324  trim(mesg) // " groundings remaining")
1325  endif
1326  endif
1327 
1328 end subroutine applyboundaryfluxesinout
1329 
1330 subroutine diabatic_aux_init(Time, G, GV, param_file, diag, CS, useALEalgorithm, use_ePBL)
1331  type(time_type), intent(in) :: Time
1332  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
1333  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure
1334  type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time parameters
1335  type(diag_ctrl), target, intent(inout) :: diag
1336  type(diabatic_aux_cs), pointer :: CS
1337  logical, intent(in) :: useALEalgorithm
1338  logical, intent(in) :: use_ePBL
1339 
1340 ! Arguments:
1341 ! (in) Time = current model time
1342 ! (in) G = ocean grid structure
1343 ! (in) GV - The ocean's vertical grid structure.
1344 ! (in) param_file = structure indicating the open file to parse for parameter values
1345 ! (in) diag = structure used to regulate diagnostic output
1346 ! (in/out) CS = pointer set to point to the control structure for this module
1347 ! (in) use_ePBL = If true, use the implicit energetics planetary boundary
1348 ! layer scheme to determine the diffusivity in the
1349 ! surface boundary layer.
1350  type(vardesc) :: vd
1351 
1352 ! This "include" declares and sets the variable "version".
1353 #include "version_variable.h"
1354  character(len=40) :: mod = "MOM_diabatic_aux" ! This module's name.
1355  character(len=48) :: thickness_units
1356  integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz, nbands
1357  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed ; nz = g%ke
1358  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
1359 
1360  if (associated(cs)) then
1361  call mom_error(warning, "diabatic_aux_init called with an "// &
1362  "associated control structure.")
1363  return
1364  else
1365  allocate(cs)
1366  endif
1367 
1368  cs%diag => diag
1369 
1370 ! Set default, read and log parameters
1371  call log_version(param_file, mod, version, &
1372  "The following parameters are used for auxiliary diabatic processes.")
1373 
1374  call get_param(param_file, mod, "RECLAIM_FRAZIL", cs%reclaim_frazil, &
1375  "If true, try to use any frazil heat deficit to cool any\n"//&
1376  "overlying layers down to the freezing point, thereby \n"//&
1377  "avoiding the creation of thin ice when the SST is above \n"//&
1378  "the freezing point.", default=.true.)
1379  call get_param(param_file, mod, "PRESSURE_DEPENDENT_FRAZIL", &
1380  cs%pressure_dependent_frazil, &
1381  "If true, use a pressure dependent freezing temperature \n"//&
1382  "when making frazil. The default is false, which will be \n"//&
1383  "faster but is inappropriate with ice-shelf cavities.", &
1384  default=.false.)
1385 
1386  if (use_epbl) then
1387  call get_param(param_file, mod, "IGNORE_FLUXES_OVER_LAND", cs%ignore_fluxes_over_land,&
1388  "If true, the model does not check if fluxes are being applied\n"//&
1389  "over land points. This is needed when the ocean is coupled \n"//&
1390  "with ice shelves and sea ice, since the sea ice mask needs to \n"//&
1391  "be different than the ocean mask to avoid sea ice formation \n"//&
1392  "under ice shelves. This flag only works when use_ePBL = True.", default=.false.)
1393  call get_param(param_file, mod, "DO_RIVERMIX", cs%do_rivermix, &
1394  "If true, apply additional mixing whereever there is \n"//&
1395  "runoff, so that it is mixed down to RIVERMIX_DEPTH \n"//&
1396  "if the ocean is that deep.", default=.false.)
1397  if (cs%do_rivermix) &
1398  call get_param(param_file, mod, "RIVERMIX_DEPTH", cs%rivermix_depth, &
1399  "The depth to which rivers are mixed if DO_RIVERMIX is \n"//&
1400  "defined.", units="m", default=0.0)
1401  else ; cs%do_rivermix = .false. ; cs%rivermix_depth = 0.0 ; endif
1402  if (gv%nkml == 0) then
1403  call get_param(param_file, mod, "USE_RIVER_HEAT_CONTENT", cs%use_river_heat_content, &
1404  "If true, use the fluxes%runoff_Hflx field to set the \n"//&
1405  "heat carried by runoff, instead of using SST*CP*liq_runoff.", &
1406  default=.false.)
1407  call get_param(param_file, mod, "USE_CALVING_HEAT_CONTENT", cs%use_calving_heat_content, &
1408  "If true, use the fluxes%calving_Hflx field to set the \n"//&
1409  "heat carried by runoff, instead of using SST*CP*froz_runoff.", &
1410  default=.false.)
1411  else
1412  cs%use_river_heat_content = .false.
1413  cs%use_calving_heat_content = .false.
1414  endif
1415 
1416  if (usealealgorithm) then
1417  cs%id_createdH = register_diag_field('ocean_model',"created_H",diag%axesT1, &
1418  time, "The volume flux added to stop the ocean from drying out and becoming negative in depth", &
1419  "meter second-1")
1420  if (cs%id_createdH>0) allocate(cs%createdH(isd:ied,jsd:jed))
1421 
1422  ! diagnostic for heating of a grid cell from convergence of SW heat into the cell
1423  cs%id_penSW_diag = register_diag_field('ocean_model', 'rsdoabsorb', &
1424  diag%axesTL, time, 'Convergence of Penetrative Shortwave Flux in Sea Water Layer',&
1425  'Watt meter-2', standard_name='net_rate_of_absorption_of_shortwave_energy_in_ocean_layer',v_extensive=.true.)
1426 
1427  ! diagnostic for penetrative SW heat flux at top interface of tracer cell (nz+1 interfaces)
1428  ! k=1 gives penetrative SW at surface; SW(k=nz+1)=0 (no penetration through rock).
1429  cs%id_penSWflux_diag = register_diag_field('ocean_model', 'rsdo', &
1430  diag%axesTi, time, 'Downwelling Shortwave Flux in Sea Water at Grid Cell Upper Interface',&
1431  'Watt meter-2', standard_name='downwelling_shortwave_flux_in_sea_water')
1432 
1433  ! need both arrays for the SW diagnostics (one for flux, one for convergence)
1434  if (cs%id_penSW_diag>0 .or. cs%id_penSWflux_diag>0) then
1435  allocate(cs%penSW_diag(isd:ied,jsd:jed,nz))
1436  cs%penSW_diag(:,:,:) = 0.0
1437  allocate(cs%penSWflux_diag(isd:ied,jsd:jed,nz+1))
1438  cs%penSWflux_diag(:,:,:) = 0.0
1439  endif
1440 
1441  ! diagnostic for non-downwelling SW radiation (i.e., SW absorbed at ocean surface)
1442  cs%id_nonpenSW_diag = register_diag_field('ocean_model', 'nonpenSW', &
1443  diag%axesT1, time, &
1444  'Non-downwelling SW radiation (i.e., SW absorbed in ocean surface with LW,SENS,LAT)',&
1445  'Watt meter-2', standard_name='nondownwelling_shortwave_flux_in_sea_water')
1446  if (cs%id_nonpenSW_diag > 0) then
1447  allocate(cs%nonpenSW_diag(isd:ied,jsd:jed))
1448  cs%nonpenSW_diag(:,:) = 0.0
1449  endif
1450  endif
1451 
1452  id_clock_uv_at_h = cpu_clock_id('(Ocean find_uv_at_h)', grain=clock_routine)
1453  id_clock_frazil = cpu_clock_id('(Ocean frazil)', grain=clock_routine)
1454 
1455 end subroutine diabatic_aux_init
1456 
1457 
1458 subroutine diabatic_aux_end(CS)
1459  type(diabatic_aux_cs), pointer :: CS
1460 
1461  if (.not.associated(cs)) return
1462 
1463  if (cs%id_createdH >0) deallocate(cs%createdH)
1464  if (cs%id_penSW_diag >0) deallocate(cs%penSW_diag)
1465  if (cs%id_penSWflux_diag >0) deallocate(cs%penSWflux_diag)
1466  if (cs%id_nonpenSW_diag >0) deallocate(cs%nonpenSW_diag)
1467 
1468  if (associated(cs)) deallocate(cs)
1469 
1470 end subroutine diabatic_aux_end
1471 
1472 end module mom_diabatic_aux
subroutine, public differential_diffuse_t_s(h, tv, visc, dt, G, GV)
subroutine, public diabatic_aux_init(Time, G, GV, param_file, diag, CS, useALEalgorithm, use_ePBL)
This module implements boundary forcing for MOM6.
subroutine, public insert_brine(h, tv, G, GV, fluxes, nkmb, CS, dt, id_brine_lay)
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
Calculates the freezing point of sea water from T, S and P.
Definition: MOM_EOS.F90:50
Provides the ocean grid type.
Definition: MOM_grid.F90:2
subroutine, public absorbremainingsw(G, GV, h, opacity_band, nsw, j, dt, H_limit_fluxes, adjustAbsorptionProfile, absorbAllSW, T, Pen_SW_bnd, eps, ksort, htot, Ttot, TKE, dSV_dT)
Apply shortwave heating below surface boundary layer.
The vertvisc_type structure contains vertical viscosities, drag coefficients, and related fields...
subroutine, public calculate_specific_vol_derivs(T, S, pressure, dSV_dT, dSV_dS, start, npts, EOS)
Calls the appropriate subroutine to calculate specific volume derivatives for an array.
Definition: MOM_EOS.F90:247
subroutine, public calltree_leave(mesg)
Writes a message about leaving a subroutine if call tree reporting is active.
This module contains I/O framework code.
Definition: MOM_io.F90:2
subroutine, public applyboundaryfluxesinout(CS, G, GV, dt, fluxes, optics, h, tv, aggregate_FW_forcing, evap_CFL_limit, minimum_forcing_depth, cTKE, dSV_dT, dSV_dS, SkinBuoyFlux)
Update the thickness, temperature, and salinity due to thermodynamic boundary forcing (contained in f...
subroutine, public sumswoverbands(G, GV, h, opacity_band, nsw, j, dt, H_limit_fluxes, absorbAllSW, iPen_SW_bnd, netPen)
subroutine, public calltree_waypoint(mesg, n)
Writes a message about reaching a milestone if call tree reporting is active.
subroutine, public calculate_density_derivs(T, S, pressure, drho_dT, drho_dS, start, npts, EOS)
Calls the appropriate subroutine to calculate density derivatives for 1-D array inputs.
Definition: MOM_EOS.F90:214
Control structure for diabatic_aux.
subroutine, public find_uv_at_h(u, v, h, u_h, v_h, G, GV, ea, eb)
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
logical function, public calltree_showquery()
Returns True, if the verbosity>=6 indicating to show the call tree.
Type for describing a variable, typically a tracer.
Definition: MOM_io.F90:51
subroutine, public extractfluxes1d(G, GV, fluxes, optics, nsw, j, dt, DepthBeforeScalingFluxes, useRiverHeatContent, useCalvingHeatContent, h, T, netMassInOut, netMassOut, net_heat, net_salt, pen_SW_bnd, tv, aggregate_FW_forcing, nonpenSW, netmassInOut_rate, net_Heat_Rate, net_salt_rate, pen_sw_bnd_Rate, skip_diags)
This subroutine extracts fluxes from the surface fluxes type. It works on a j-row for optimization pu...
subroutine, public diabatic_aux_end(CS)
The thermo_var_ptrs structure contains pointers to an assortment of thermodynamic fields that may be ...
subroutine, public tridiagts(G, GV, is, ie, js, je, hold, ea, eb, T, S)
subroutine, public diagnosemldbydensitydifference(id_MLD, h, tv, densityDiff, G, GV, diagPtr, id_N2subML, id_MLDsq)
Diagnose a mixed layer depth (MLD) determined by a given density difference with the surface...
subroutine, public make_frazil(h, tv, G, GV, CS, p_surf)
subroutine, public adjust_salt(h, tv, G, GV, CS)
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 forcing_singlepointprint(fluxes, G, i, j, mesg)
Write out values of the fluxes arrays at the i,j location. This is a debugging tool.
subroutine, public calltree_enter(mesg, n)
Writes a message about entering a subroutine if call tree reporting is active.