MOM6
MOM_diapyc_energy_req.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, May 2015 *
25 !* *
26 !* This module calculates the energy requirements of mixing. *
27 !* *
28 !********+*********+*********+*********+*********+*********+*********+**
29 
31 use mom_error_handler, only : mom_error, fatal, warning, mom_mesg, is_root_pe
33 use mom_grid, only : ocean_grid_type
37 
38 implicit none ; private
39 
41 
42 type, public :: diapyc_energy_req_cs ; private
43  logical :: initialized = .false. ! A variable that is here because empty
44  ! structures are not permitted by some compilers.
45  real :: test_kh_scaling ! A scaling factor for the diapycnal diffusivity.
46  real :: colht_scaling ! A scaling factor for the column height change
47  ! correction term.
48  logical :: use_test_kh_profile ! If true, use the internal test diffusivity
49  ! profile in place of any that might be passed
50  ! in as an argument.
51  type(diag_ctrl), pointer :: diag ! Structure used to regulate timing of diagnostic output
52  integer :: id_ert=-1, id_erb=-1, id_erc=-1, id_erh=-1, id_kddt=-1, id_kd=-1
53  integer :: id_chct=-1, id_chcb=-1, id_chcc=-1, id_chch=-1
54  integer :: id_t0=-1, id_tf=-1, id_s0=-1, id_sf=-1, id_n2_0=-1, id_n2_f=-1
55  integer :: id_h=-1, id_zint=-1
56 end type diapyc_energy_req_cs
57 
58 contains
59 
60 ! #@# This subroutine needs a doxygen description
61 subroutine diapyc_energy_req_test(h_3d, dt, tv, G, GV, CS, Kd_int)
62  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
63  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
64  real, dimension(G%isd:G%ied,G%jsd:G%jed,GV%ke), &
65  intent(in) :: h_3d !< Layer thickness before entrainment,
66  !! in m or kg m-2.
67  type(thermo_var_ptrs), intent(inout) :: tv !< A structure containing pointers to any
68  !! available thermodynamic fields.
69  !! Absent fields have NULL ptrs.
70  real, intent(in) :: dt !< The amount of time covered by this call,
71  !! in s.
72  type(diapyc_energy_req_cs), pointer :: CS !< This module's control structure.
73  real, dimension(G%isd:G%ied,G%jsd:G%jed,GV%ke+1), &
74  optional, intent(in) :: Kd_int !< Interface diffusivities.
75 
76 ! Arguments: h_3d - Layer thickness before entrainment, in m or kg m-2.
77 ! (in) tv - A structure containing pointers to any available
78 ! thermodynamic fields. Absent fields have NULL ptrs.
79 ! (in) dt - The amount of time covered by this call, in s.
80 ! (in) G - The ocean's grid structure.
81 ! (in) GV - The ocean's vertical grid structure.
82 ! (in) CS - This module's control structure
83 ! (in,opt) Kd_int - Interface diffusivities.
84 
85  real, dimension(GV%ke) :: &
86  T0, S0, & ! T0 & S0 are columns of initial temperatures and salinities, in degC and g/kg.
87  h_col ! h_col is a column of thicknesses h at tracer points, in H (m or kg m-2).
88  real, dimension(GV%ke+1) :: &
89  Kd, & ! A column of diapycnal diffusivities at interfaces, in m2 s-1.
90  h_top, h_bot ! Distances from the top or bottom, in H.
91  real :: ustar, absf, htot
92  real :: energy_Kd ! The energy used by diapycnal mixing in W m-2.
93  real :: tmp1 ! A temporary array.
94  integer :: i, j, k, is, ie, js, je, nz, itt
95  logical :: may_print
96  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
97 
98  if (.not. associated(cs)) call mom_error(fatal, "diapyc_energy_req_test: "// &
99  "Module must be initialized before it is used.")
100 
101 !$OMP do
102  do j=js,je ; do i=is,ie ; if (g%mask2dT(i,j) > 0.5) then
103  if (present(kd_int) .and. .not.cs%use_test_Kh_profile) then
104  do k=1,nz+1 ; kd(k) = cs%test_Kh_scaling*kd_int(i,j,k) ; enddo
105  else
106  htot = 0.0 ; h_top(1) = 0.0
107  do k=1,nz
108  t0(k) = tv%T(i,j,k) ; s0(k) = tv%S(i,j,k)
109  h_col(k) = h_3d(i,j,k)
110  h_top(k+1) = h_top(k) + h_col(k)
111  enddo
112  htot = h_top(nz+1)
113  h_bot(nz+1) = 0.0
114  do k=nz,1,-1
115  h_bot(k) = h_bot(k+1) + h_col(k)
116  enddo
117 
118  ustar = 0.01 ! Change this to being an input parameter?
119  absf = 0.25*((abs(g%CoriolisBu(i-1,j-1)) + abs(g%CoriolisBu(i,j))) + &
120  (abs(g%CoriolisBu(i-1,j-1)) + abs(g%CoriolisBu(i,j))))
121  kd(1) = 0.0 ; kd(nz+1) = 0.0
122  do k=2,nz
123  tmp1 = h_top(k) * h_bot(k) * gv%H_to_m
124  kd(k) = cs%test_Kh_scaling * &
125  ustar * 0.41 * (tmp1*ustar) / (absf*tmp1 + htot*ustar)
126  enddo
127  endif
128  may_print = is_root_pe() .and. (i==ie) .and. (j==je)
129  call diapyc_energy_req_calc(h_col, t0, s0, kd, energy_kd, dt, tv, g, gv, &
130  may_print=may_print, cs=cs)
131  endif ; enddo ; enddo
132 
133 end subroutine diapyc_energy_req_test
134 
135 !> This subroutine uses a substantially refactored tridiagonal equation for
136 !! diapycnal mixing of temperature and salinity to estimate the potential energy
137 !! change due to diapycnal mixing in a column of water. It does this estimate
138 !! 4 different ways, all of which should be equivalent, but reports only one.
139 !! The various estimates are taken because they will later be used as templates
140 !! for other bits of code
141 subroutine diapyc_energy_req_calc(h_in, T_in, S_in, Kd, energy_Kd, dt, tv, &
142  G, GV, may_print, CS)
143  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
144  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
145  real, dimension(GV%ke), intent(in) :: h_in !< Layer thickness before entrainment,
146  !! in m or kg m-2.
147  real, dimension(GV%ke), intent(in) :: T_in !< The layer temperatures, in degC.
148  real, dimension(GV%ke), intent(in) :: S_in !< The layer salinities, in g kg-1.
149  real, dimension(GV%ke+1), intent(in) :: Kd !< The interfaces diapycnal diffusivities,
150  !! in m2 s-1.
151  real, intent(in) :: dt !< The amount of time covered by this call,
152  !! in s.
153  real, intent(out) :: energy_Kd !< The column-integrated rate of energy
154  !! consumption by diapycnal diffusion, in W m-2.
155  type(thermo_var_ptrs), intent(inout) :: tv !< A structure containing pointers to any
156  !! available thermodynamic fields.
157  !! Absent fields have NULL ptrs.
158  logical, optional, intent(in) :: may_print !< If present and true, write out diagnostics
159  !! of energy use.
160  type(diapyc_energy_req_cs), &
161  optional, pointer :: CS !< This module's control structure.
162 
163 ! Arguments: h_in - Layer thickness before entrainment, in m or kg m-2.
164 ! (in) T_in - The layer temperatures, in degC.
165 ! (in) S_in - The layer salinities, in g kg-1.
166 ! (in) Kd - The interfaces diapycnal diffusivities, in m2 s-1.
167 ! (in/out) tv - A structure containing pointers to any available
168 ! thermodynamic fields. Absent fields have NULL ptrs.
169 ! (in) dt - The amount of time covered by this call, in s.
170 ! (out) energy_Kd - The column-integrated rate of energy consumption
171 ! by diapycnal diffusion, in W m-2.
172 ! (in) G - The ocean's grid structure.
173 ! (in) GV - The ocean's vertical grid structure.
174 ! (in,opt) may_print - If present and true, write out diagnostics of energy use.
175 ! (in,opt) CS - This module's control structure
176 
177 ! This subroutine uses a substantially refactored tridiagonal equation for
178 ! diapycnal mixing of temperature and salinity to estimate the potential energy
179 ! change due to diapycnal mixing in a column of water. It does this estimate
180 ! 4 different ways, all of which should be equivalent, but reports only one.
181 ! The various estimates are taken because they will later be used as templates
182 ! for other bits of code.
183 
184  real, dimension(GV%ke) :: &
185  p_lay, & ! Average pressure of a layer, in Pa.
186  dSV_dT, & ! Partial derivative of specific volume with temperature, in m3 kg-1 K-1.
187  dSV_dS, & ! Partial derivative of specific volume with salinity, in m3 kg-1 / (g kg-1).
188  T0, S0, & ! Initial temperatures and salinities.
189  Te, Se, & ! Running incomplete estimates of the new temperatures and salinities.
190  Te_a, Se_a, & ! Running incomplete estimates of the new temperatures and salinities.
191  Te_b, Se_b, & ! Running incomplete estimates of the new temperatures and salinities.
192  Tf, Sf, & ! New final values of the temperatures and salinities.
193  dTe, dSe, & ! Running (1-way) estimates of temperature and salinity change.
194  dTe_a, dSe_a, & ! Running (1-way) estimates of temperature and salinity change.
195  dTe_b, dSe_b, & ! Running (1-way) estimates of temperature and salinity change.
196  Th_a, & ! An effective temperature times a thickness in the layer above,
197  ! including implicit mixing effects with other yet higher layers, in K H.
198  sh_a, & ! An effective salinity times a thickness in the layer above,
199  ! including implicit mixing effects with other yet higher layers, in K H.
200  th_b, & ! An effective temperature times a thickness in the layer below,
201  ! including implicit mixing effects with other yet lower layers, in K H.
202  sh_b, & ! An effective salinity times a thickness in the layer below,
203  ! including implicit mixing effects with other yet lower layers, in K H.
204  dt_to_dpe, & ! Partial derivative of column potential energy with the temperature
205  ds_to_dpe, & ! and salinity changes within a layer, in J m-2 K-1 and J m-2 / (g kg-1).
206  dt_to_dcolht, & ! Partial derivatives of the total column height with the temperature
207  ds_to_dcolht, & ! and salinity changes within a layer, in m K-1 and m ppt-1.
208  dt_to_dcolht_a, & ! Partial derivatives of the total column height with the temperature
209  ds_to_dcolht_a, & ! and salinity changes within a layer, including the implicit effects
210  ! of mixing with layers higher in the water colun, in m K-1 and m ppt-1.
211  dt_to_dcolht_b, & ! Partial derivatives of the total column height with the temperature
212  ds_to_dcolht_b, & ! and salinity changes within a layer, including the implicit effects
213  ! of mixing with layers lower in the water colun, in m K-1 and m ppt-1.
214  dt_to_dpe_a, & ! Partial derivatives of column potential energy with the temperature
215  ds_to_dpe_a, & ! and salinity changes within a layer, including the implicit effects
216  ! of mixing with layers higher in the water column, in
217  ! units of J m-2 K-1 and J m-2 ppt-1.
218  dt_to_dpe_b, & ! Partial derivatives of column potential energy with the temperature
219  ds_to_dpe_b, & ! and salinity changes within a layer, including the implicit effects
220  ! of mixing with layers lower in the water column, in
221  ! units of J m-2 K-1 and J m-2 ppt-1.
222  hp_a, & ! An effective pivot thickness of the layer including the effects
223  ! of coupling with layers above, in H. This is the first term
224  ! in the denominator of b1 in a downward-oriented tridiagonal solver.
225  hp_b, & ! An effective pivot thickness of the layer including the effects
226  ! of coupling with layers below, in H. This is the first term
227  ! in the denominator of b1 in an upward-oriented tridiagonal solver.
228  c1_a, & ! c1_a is used by a downward-oriented tridiagonal solver, ND.
229  c1_b, & ! c1_b is used by an upward-oriented tridiagonal solver, ND.
230  h_tr ! h_tr is h at tracer points with a h_neglect added to
231  ! ensure positive definiteness, in m or kg m-2.
232  real, dimension(GV%ke+1) :: &
233  pres, & ! Interface pressures in Pa.
234  z_Int, & ! Interface heights relative to the surface, in m.
235  N2, & ! An estimate of the buoyancy frequency in s-2.
236  Kddt_h_a, & !
237  Kddt_h_b, & !
238  Kddt_h, & ! The diapycnal diffusivity times a timestep divided by the
239  ! average thicknesses around a layer, in m or kg m-2.
240  kd_so_far ! The value of Kddt_h that has been applied already in
241  ! calculating the energy changes, in m or kg m-2.
242  real, dimension(GV%ke+1,4) :: &
243  PE_chg_k, & ! The integrated potential energy change within a timestep due
244  ! to the diffusivity at interface K for 4 different orders of
245  ! accumulating the diffusivities, in J m-2.
246  colht_cor_k ! The correction to the potential energy change due to
247  ! changes in the net column height, in J m-2.
248  real :: &
249  b1 ! b1 is used by the tridiagonal solver, in m-1 or m2 kg-1.
250  real :: &
251  I_b1 ! The inverse of b1, in m or kg m-2.
252  real :: Kd0, dKd
253  real :: h_neglect ! A thickness that is so small it is usually lost
254  ! in roundoff and can be neglected, in m.
255  real :: dTe_term, dSe_term
256  real :: Kddt_h_guess
257  real :: dMass ! The mass per unit area within a layer, in kg m-2.
258  real :: dPres ! The hydrostatic pressure change across a layer, in Pa.
259  real :: dMKE_max ! The maximum amount of mean kinetic energy that could be
260  ! converted to turbulent kinetic energy if the velocity in
261  ! the layer below an interface were homogenized with all of
262  ! the water above the interface, in J m-2 = kg s-2.
263  real :: rho_here ! The in-situ density, in kg m-3.
264  real :: PE_change, ColHt_cor
265  real :: htot
266  real :: dT_k, dT_km1, dS_k, dS_km1 ! Temporary arrays
267  real :: b1Kd ! Temporary arrays
268  real :: Kd_rat, Kdr_denom, I_Kdr_denom ! Temporary arrays
269  real :: dTe_t2, dSe_t2, dT_km1_t2, dS_km1_t2, dT_k_t2, dS_k_t2
270  logical :: do_print
271 
272  ! The following are a bunch of diagnostic arrays for debugging purposes.
273  real, dimension(GV%ke) :: &
274  Ta, Sa, Tb, Sb
275  real, dimension(GV%ke+1) :: &
276  dPEa_dKd, dPEa_dKd_est, dPEa_dKd_err, dPEa_dKd_trunc, dPEa_dKd_err_norm, &
277  dPEb_dKd, dPEb_dKd_est, dPEb_dKd_err, dPEb_dKd_trunc, dPEb_dKd_err_norm
278  real :: PE_chg_tot1A, PE_chg_tot2A, T_chg_totA
279  real :: PE_chg_tot1B, PE_chg_tot2B, T_chg_totB
280  real :: PE_chg_tot1C, PE_chg_tot2C, T_chg_totC
281  real :: PE_chg_tot1D, PE_chg_tot2D, T_chg_totD
282  real, dimension(GV%ke+1) :: dPEchg_dKd
283  real :: PE_chg(6)
284  real, dimension(6) :: dT_k_itt, dS_k_itt, dT_km1_itt, dS_km1_itt
285 
286  real :: I_G_Earth
287  real :: dKd_rat_dKd, ddT_k_dKd, ddS_k_dKd, ddT_km1_dKd, ddS_km1_dKd
288  integer :: k, nz, itt, max_itt, k_cent
289  logical :: surface_BL, bottom_BL, central, halves, debug
290  logical :: old_PE_calc
291  nz = g%ke
292  h_neglect = gv%H_subroundoff
293 
294  i_g_earth = 1.0 / gv%g_Earth
295  debug = .true.
296 
297  surface_bl = .true. ; bottom_bl = .true. ; halves = .true.
298  central = .true. ; k_cent = nz/2
299 
300  do_print = .false. ; if (present(may_print) .and. present(cs)) do_print = may_print
301 
302  dpea_dkd(:) = 0.0 ; dpea_dkd_est(:) = 0.0 ; dpea_dkd_err(:) = 0.0 ; dpea_dkd_err_norm(:) = 0.0 ; dpea_dkd_trunc(:) = 0.0
303  dpeb_dkd(:) = 0.0 ; dpeb_dkd_est(:) = 0.0 ; dpeb_dkd_err(:) = 0.0 ; dpeb_dkd_err_norm(:) = 0.0 ; dpeb_dkd_trunc(:) = 0.0
304 
305  htot = 0.0 ; pres(1) = 0.0 ; z_int(1) = 0.0
306  do k=1,nz
307  t0(k) = t_in(k) ; s0(k) = s_in(k)
308  h_tr(k) = h_in(k)
309  htot = htot + h_tr(k)
310  pres(k+1) = pres(k) + gv%g_Earth * gv%H_to_kg_m2 * h_tr(k)
311  p_lay(k) = 0.5*(pres(k) + pres(k+1))
312  z_int(k+1) = z_int(k) - gv%H_to_m * h_tr(k)
313  enddo
314  do k=1,nz
315  h_tr(k) = max(h_tr(k), 1e-15*htot)
316  enddo
317 
318  ! Introduce a diffusive flux variable, Kddt_h(K) = ea(k) = eb(k-1)
319 
320  kddt_h(1) = 0.0 ; kddt_h(nz+1) = 0.0
321  do k=2,nz
322  kddt_h(k) = min((gv%m_to_H**2*dt)*kd(k) / (0.5*(h_tr(k-1) + h_tr(k))),1e3*htot)
323  enddo
324 
325  ! Solve the tridiagonal equations for new temperatures.
326 
327  call calculate_specific_vol_derivs(t0, s0, p_lay, dsv_dt, dsv_ds, 1, nz, tv%eqn_of_state)
328 
329  do k=1,nz
330  dmass = gv%H_to_kg_m2 * h_tr(k)
331  dpres = gv%g_Earth * dmass
332  dt_to_dpe(k) = (dmass * (pres(k) + 0.5*dpres)) * dsv_dt(k)
333  ds_to_dpe(k) = (dmass * (pres(k) + 0.5*dpres)) * dsv_ds(k)
334  dt_to_dcolht(k) = dmass * dsv_dt(k) * cs%ColHt_scaling
335  ds_to_dcolht(k) = dmass * dsv_ds(k) * cs%ColHt_scaling
336  enddo
337 
338 ! PE_chg_k(1) = 0.0 ; PE_chg_k(nz+1) = 0.0
339  ! PEchg(:) = 0.0
340  pe_chg_k(:,:) = 0.0 ; colht_cor_k(:,:) = 0.0
341  dpechg_dkd(:) = 0.0
342 
343  if (surface_bl) then ! This version is appropriate for a surface boundary layer.
344  old_pe_calc = .false.
345 
346  ! Set up values appropriate for no diffusivity.
347  do k=1,nz
348  hp_a(k) = h_tr(k) ; hp_b(k) = h_tr(k)
349  dt_to_dpe_a(k) = dt_to_dpe(k) ; ds_to_dpe_a(k) = ds_to_dpe(k)
350  dt_to_dpe_b(k) = dt_to_dpe(k) ; ds_to_dpe_b(k) = ds_to_dpe(k)
351  dt_to_dcolht_a(k) = dt_to_dcolht(k) ; ds_to_dcolht_a(k) = ds_to_dcolht(k)
352  dt_to_dcolht_b(k) = dt_to_dcolht(k) ; ds_to_dcolht_b(k) = ds_to_dcolht(k)
353  enddo
354 
355  do k=2,nz
356  ! At this point, Kddt_h(K) will be unknown because its value may depend
357  ! on how much energy is available.
358 
359  ! Precalculate some temporary expressions that are independent of Kddt_h_guess.
360  if (old_pe_calc) then
361  if (k==2) then
362  dt_km1_t2 = (t0(k)-t0(k-1))
363  ds_km1_t2 = (s0(k)-s0(k-1))
364  dte_t2 = 0.0 ; dse_t2 = 0.0
365  else
366  dte_t2 = kddt_h(k-1) * ((t0(k-2) - t0(k-1)) + dte(k-2))
367  dse_t2 = kddt_h(k-1) * ((s0(k-2) - s0(k-1)) + dse(k-2))
368  dt_km1_t2 = (t0(k)-t0(k-1)) - &
369  (kddt_h(k-1) / hp_a(k-1)) * ((t0(k-2) - t0(k-1)) + dte(k-2))
370  ds_km1_t2 = (s0(k)-s0(k-1)) - &
371  (kddt_h(k-1) / hp_a(k-1)) * ((s0(k-2) - s0(k-1)) + dse(k-2))
372  endif
373  dte_term = dte_t2 + hp_a(k-1) * (t0(k-1)-t0(k))
374  dse_term = dse_t2 + hp_a(k-1) * (s0(k-1)-s0(k))
375  else
376  if (k<=2) then
377  th_a(k-1) = h_tr(k-1) * t0(k-1) ; sh_a(k-1) = h_tr(k-1) * s0(k-1)
378  else
379  th_a(k-1) = h_tr(k-1) * t0(k-1) + kddt_h(k-1) * te(k-2)
380  sh_a(k-1) = h_tr(k-1) * s0(k-1) + kddt_h(k-1) * se(k-2)
381  endif
382  th_b(k) = h_tr(k) * t0(k) ; sh_b(k) = h_tr(k) * s0(k)
383  endif
384 
385 
386  ! Find the energy change due to a guess at the strength of diffusion at interface K.
387 
388  kddt_h_guess = kddt_h(k)
389  if (old_pe_calc) then
390  call find_pe_chg_orig(kddt_h_guess, h_tr(k), hp_a(k-1), &
391  dte_term, dse_term, dt_km1_t2, ds_km1_t2, &
392  dt_to_dpe(k), ds_to_dpe(k), dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), &
393  pres(k), dt_to_dcolht(k), ds_to_dcolht(k), &
394  dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
395  pe_chg_k(k,1), dpea_dkd(k))
396  else
397  call find_pe_chg(0.0, kddt_h_guess, hp_a(k-1), hp_b(k), &
398  th_a(k-1), sh_a(k-1), th_b(k), sh_b(k), &
399  dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
400  pres(k), dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
401  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
402  pe_chg=pe_chg_k(k,1), dpec_dkd=dpea_dkd(k), &
403  colht_cor=colht_cor_k(k,1))
404  endif
405 
406  if (debug) then
407  do itt=1,5
408  kddt_h_guess = (1.0+0.01*(itt-3))*kddt_h(k)
409 
410  if (old_pe_calc) then
411  call find_pe_chg_orig(kddt_h_guess, h_tr(k), hp_a(k-1), &
412  dte_term, dse_term, dt_km1_t2, ds_km1_t2, &
413  dt_to_dpe(k), ds_to_dpe(k), dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), &
414  pres(k), dt_to_dcolht(k), ds_to_dcolht(k), &
415  dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
416  pe_chg=pe_chg(itt))
417  else
418  call find_pe_chg(0.0, kddt_h_guess, hp_a(k-1), hp_b(k), &
419  th_a(k-1), sh_a(k-1), th_b(k), sh_b(k), &
420  dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
421  pres(k), dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
422  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
423  pe_chg=pe_chg(itt))
424  endif
425  enddo
426  ! Compare with a 4th-order finite difference estimate.
427  dpea_dkd_est(k) = (4.0*(pe_chg(4)-pe_chg(2))/(0.02*kddt_h(k)) - &
428  (pe_chg(5)-pe_chg(1))/(0.04*kddt_h(k))) / 3.0
429  dpea_dkd_trunc(k) = (pe_chg(4)-pe_chg(2))/(0.02*kddt_h(k)) - &
430  (pe_chg(5)-pe_chg(1))/(0.04*kddt_h(k))
431  dpea_dkd_err(k) = (dpea_dkd_est(k) - dpea_dkd(k))
432  dpea_dkd_err_norm(k) = (dpea_dkd_est(k) - dpea_dkd(k)) / &
433  (abs(dpea_dkd_est(k)) + abs(dpea_dkd(k)) + 1e-100)
434  endif
435 
436  ! At this point, the final value of Kddt_h(K) is known, so the estimated
437  ! properties for layer k-1 can be calculated.
438 
439  b1 = 1.0 / (hp_a(k-1) + kddt_h(k))
440  c1_a(k) = kddt_h(k) * b1
441  if (k==2) then
442  te(1) = b1*(h_tr(1)*t0(1))
443  se(1) = b1*(h_tr(1)*s0(1))
444  else
445  te(k-1) = b1 * (h_tr(k-1) * t0(k-1) + kddt_h(k-1) * te(k-2))
446  se(k-1) = b1 * (h_tr(k-1) * s0(k-1) + kddt_h(k-1) * se(k-2))
447  endif
448  if (old_pe_calc) then
449  dte(k-1) = b1 * ( kddt_h(k)*(t0(k)-t0(k-1)) + dte_t2 )
450  dse(k-1) = b1 * ( kddt_h(k)*(s0(k)-s0(k-1)) + dse_t2 )
451  endif
452 
453  hp_a(k) = h_tr(k) + (hp_a(k-1) * b1) * kddt_h(k)
454  dt_to_dpe_a(k) = dt_to_dpe(k) + c1_a(k)*dt_to_dpe_a(k-1)
455  ds_to_dpe_a(k) = ds_to_dpe(k) + c1_a(k)*ds_to_dpe_a(k-1)
456  dt_to_dcolht_a(k) = dt_to_dcolht(k) + c1_a(k)*dt_to_dcolht_a(k-1)
457  ds_to_dcolht_a(k) = ds_to_dcolht(k) + c1_a(k)*ds_to_dcolht_a(k-1)
458 
459  enddo
460 
461  b1 = 1.0 / (hp_a(nz))
462  tf(nz) = b1 * (h_tr(nz) * t0(nz) + kddt_h(nz) * te(nz-1))
463  sf(nz) = b1 * (h_tr(nz) * s0(nz) + kddt_h(nz) * se(nz-1))
464  if (old_pe_calc) then
465  dte(nz) = b1 * kddt_h(nz) * ((t0(nz-1)-t0(nz)) + dte(nz-1))
466  dse(nz) = b1 * kddt_h(nz) * ((s0(nz-1)-s0(nz)) + dse(nz-1))
467  endif
468 
469  do k=nz-1,1,-1
470  tf(k) = te(k) + c1_a(k+1)*tf(k+1)
471  sf(k) = se(k) + c1_a(k+1)*sf(k+1)
472  enddo
473 
474  if (debug) then
475  do k=1,nz ; ta(k) = tf(k) ; sa(k) = sf(k) ; enddo
476  pe_chg_tot1a = 0.0 ; pe_chg_tot2a = 0.0 ; t_chg_tota = 0.0
477  do k=1,nz
478  pe_chg_tot1a = pe_chg_tot1a + (dt_to_dpe(k) * (tf(k) - t0(k)) + &
479  ds_to_dpe(k) * (sf(k) - s0(k)))
480  t_chg_tota = t_chg_tota + h_tr(k) * (tf(k) - t0(k))
481  enddo
482  do k=2,nz
483  pe_chg_tot2a = pe_chg_tot2a + (pe_chg_k(k,1) - colht_cor_k(k,1))
484  enddo
485  endif
486 
487  endif
488 
489  if (bottom_bl) then ! This version is appropriate for a bottom boundary layer.
490  old_pe_calc = .false.
491 
492  ! Set up values appropriate for no diffusivity.
493  do k=1,nz
494  hp_a(k) = h_tr(k) ; hp_b(k) = h_tr(k)
495  dt_to_dpe_a(k) = dt_to_dpe(k) ; ds_to_dpe_a(k) = ds_to_dpe(k)
496  dt_to_dpe_b(k) = dt_to_dpe(k) ; ds_to_dpe_b(k) = ds_to_dpe(k)
497  dt_to_dcolht_a(k) = dt_to_dcolht(k) ; ds_to_dcolht_a(k) = ds_to_dcolht(k)
498  dt_to_dcolht_b(k) = dt_to_dcolht(k) ; ds_to_dcolht_b(k) = ds_to_dcolht(k)
499  enddo
500 
501  do k=nz,2,-1 ! Loop over interior interfaces.
502  ! At this point, Kddt_h(K) will be unknown because its value may depend
503  ! on how much energy is available.
504 
505  ! Precalculate some temporary expressions that are independent of Kddt_h_guess.
506  if (old_pe_calc) then
507  if (k==nz) then
508  dt_k_t2 = (t0(k-1)-t0(k))
509  ds_k_t2 = (s0(k-1)-s0(k))
510  dte_t2 = 0.0 ; dse_t2 = 0.0
511  else
512  dte_t2 = kddt_h(k+1) * ((t0(k+1) - t0(k)) + dte(k+1))
513  dse_t2 = kddt_h(k+1) * ((s0(k+1) - s0(k)) + dse(k+1))
514  dt_k_t2 = (t0(k-1)-t0(k)) - &
515  (kddt_h(k+1)/ hp_b(k)) * ((t0(k+1) - t0(k)) + dte(k+1))
516  ds_k_t2 = (s0(k-1)-s0(k)) - &
517  (kddt_h(k+1)/ hp_b(k)) * ((s0(k+1) - s0(k)) + dse(k+1))
518  endif
519  dte_term = dte_t2 + hp_b(k) * (t0(k)-t0(k-1))
520  dse_term = dse_t2 + hp_b(k) * (s0(k)-s0(k-1))
521  else
522  th_a(k-1) = h_tr(k-1) * t0(k-1) ; sh_a(k-1) = h_tr(k-1) * s0(k-1)
523  if (k>=nz) then
524  th_b(k) = h_tr(k) * t0(k) ; sh_b(k) = h_tr(k) * s0(k)
525  else
526  th_b(k) = h_tr(k) * t0(k) + kddt_h(k+1) * te(k+1)
527  sh_b(k) = h_tr(k) * s0(k) + kddt_h(k+1) * se(k+1)
528  endif
529  endif
530 
531  ! Find the energy change due to a guess at the strength of diffusion at interface K.
532  kddt_h_guess = kddt_h(k)
533 
534  if (old_pe_calc) then
535  call find_pe_chg_orig(kddt_h_guess, h_tr(k-1), hp_b(k), &
536  dte_term, dse_term, dt_k_t2, ds_k_t2, &
537  dt_to_dpe(k-1), ds_to_dpe(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
538  pres(k), dt_to_dcolht(k-1), ds_to_dcolht(k-1), &
539  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
540  pe_chg=pe_chg_k(k,2), dpec_dkd=dpeb_dkd(k))
541  else
542  call find_pe_chg(0.0, kddt_h_guess, hp_a(k-1), hp_b(k), &
543  th_a(k-1), sh_a(k-1), th_b(k), sh_b(k), &
544  dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
545  pres(k), dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
546  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
547  pe_chg=pe_chg_k(k,2), dpec_dkd=dpeb_dkd(k), &
548  colht_cor=colht_cor_k(k,2))
549  endif
550 
551  if (debug) then
552  ! Compare with a 4th-order finite difference estimate.
553  do itt=1,5
554  kddt_h_guess = (1.0+0.01*(itt-3))*kddt_h(k)
555 
556  if (old_pe_calc) then
557  call find_pe_chg_orig(kddt_h_guess, h_tr(k-1), hp_b(k), &
558  dte_term, dse_term, dt_k_t2, ds_k_t2, &
559  dt_to_dpe(k-1), ds_to_dpe(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
560  pres(k), dt_to_dcolht(k-1), ds_to_dcolht(k-1), &
561  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
562  pe_chg=pe_chg(itt))
563  else
564  call find_pe_chg(0.0, kddt_h_guess, hp_a(k-1), hp_b(k), &
565  th_a(k-1), sh_a(k-1), th_b(k), sh_b(k), &
566  dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
567  pres(k), dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
568  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
569  pe_chg=pe_chg(itt))
570  endif
571  enddo
572 
573  dpeb_dkd_est(k) = (4.0*(pe_chg(4)-pe_chg(2))/(0.02*kddt_h(k)) - &
574  (pe_chg(5)-pe_chg(1))/(0.04*kddt_h(k))) / 3.0
575  dpeb_dkd_trunc(k) = (pe_chg(4)-pe_chg(2))/(0.02*kddt_h(k)) - &
576  (pe_chg(5)-pe_chg(1))/(0.04*kddt_h(k))
577  dpeb_dkd_err(k) = (dpeb_dkd_est(k) - dpeb_dkd(k))
578  dpeb_dkd_err_norm(k) = (dpeb_dkd_est(k) - dpeb_dkd(k)) / &
579  (abs(dpeb_dkd_est(k)) + abs(dpeb_dkd(k)) + 1e-100)
580  endif
581 
582  ! At this point, the final value of Kddt_h(K) is known, so the estimated
583  ! properties for layer k can be calculated.
584 
585  b1 = 1.0 / (hp_b(k) + kddt_h(k))
586  c1_b(k) = kddt_h(k) * b1
587  if (k==nz) then
588  te(nz) = b1* (h_tr(nz)*t0(nz))
589  se(nz) = b1* (h_tr(nz)*s0(nz))
590  else
591  te(k) = b1 * (h_tr(k) * t0(k) + kddt_h(k+1) * te(k+1))
592  se(k) = b1 * (h_tr(k) * s0(k) + kddt_h(k+1) * se(k+1))
593  endif
594  if (old_pe_calc) then
595  dte(k) = b1 * ( kddt_h(k)*(t0(k-1)-t0(k)) + dte_t2 )
596  dse(k) = b1 * ( kddt_h(k)*(s0(k-1)-s0(k)) + dse_t2 )
597  endif
598 
599  hp_b(k-1) = h_tr(k-1) + (hp_b(k) * b1) * kddt_h(k)
600  dt_to_dpe_b(k-1) = dt_to_dpe(k-1) + c1_b(k)*dt_to_dpe_b(k)
601  ds_to_dpe_b(k-1) = ds_to_dpe(k-1) + c1_b(k)*ds_to_dpe_b(k)
602  dt_to_dcolht_b(k-1) = dt_to_dcolht(k-1) + c1_b(k)*dt_to_dcolht_b(k)
603  ds_to_dcolht_b(k-1) = ds_to_dcolht(k-1) + c1_b(k)*ds_to_dcolht_b(k)
604 
605  enddo
606 
607  b1 = 1.0 / (hp_b(1))
608  tf(1) = b1 * (h_tr(1) * t0(1) + kddt_h(2) * te(2))
609  sf(1) = b1 * (h_tr(1) * s0(1) + kddt_h(2) * se(2))
610  if (old_pe_calc) then
611  dte(1) = b1 * kddt_h(2) * ((t0(2)-t0(1)) + dte(2))
612  dse(1) = b1 * kddt_h(2) * ((s0(2)-s0(1)) + dse(2))
613  endif
614 
615  do k=2,nz
616  tf(k) = te(k) + c1_b(k)*tf(k-1)
617  sf(k) = se(k) + c1_b(k)*sf(k-1)
618  enddo
619 
620  if (debug) then
621  do k=1,nz ; tb(k) = tf(k) ; sb(k) = sf(k) ; enddo
622  pe_chg_tot1b = 0.0 ; pe_chg_tot2b = 0.0 ; t_chg_totb = 0.0
623  do k=1,nz
624  pe_chg_tot1b = pe_chg_tot1b + (dt_to_dpe(k) * (tf(k) - t0(k)) + &
625  ds_to_dpe(k) * (sf(k) - s0(k)))
626  t_chg_totb = t_chg_totb + h_tr(k) * (tf(k) - t0(k))
627  enddo
628  do k=2,nz
629  pe_chg_tot2b = pe_chg_tot2b + (pe_chg_k(k,2) - colht_cor_k(k,2))
630  enddo
631  endif
632 
633  endif
634 
635  if (central) then
636 
637  ! Set up values appropriate for no diffusivity.
638  do k=1,nz
639  hp_a(k) = h_tr(k) ; hp_b(k) = h_tr(k)
640  dt_to_dpe_a(k) = dt_to_dpe(k) ; ds_to_dpe_a(k) = ds_to_dpe(k)
641  dt_to_dpe_b(k) = dt_to_dpe(k) ; ds_to_dpe_b(k) = ds_to_dpe(k)
642  dt_to_dcolht_a(k) = dt_to_dcolht(k) ; ds_to_dcolht_a(k) = ds_to_dcolht(k)
643  dt_to_dcolht_b(k) = dt_to_dcolht(k) ; ds_to_dcolht_b(k) = ds_to_dcolht(k)
644  enddo
645 
646  ! Calculate the dependencies on layers above.
647  kddt_h_a(1) = 0.0
648  do k=2,nz ! Loop over interior interfaces.
649  ! First calculate some terms that are independent of the change in Kddt_h(K).
650  kd0 = 0.0 ! This might need to be changed - it is the already applied value of Kddt_h(K).
651  if (k<=2) then
652  th_a(k-1) = h_tr(k-1) * t0(k-1) ; sh_a(k-1) = h_tr(k-1) * s0(k-1)
653  else
654  th_a(k-1) = h_tr(k-1) * t0(k-1) + kddt_h(k-1) * te_a(k-2)
655  sh_a(k-1) = h_tr(k-1) * s0(k-1) + kddt_h(k-1) * se_a(k-2)
656  endif
657  th_b(k) = h_tr(k) * t0(k) ; sh_b(k) = h_tr(k) * s0(k)
658 
659  kddt_h_a(k) = 0.0
660  if (k < k_cent) kddt_h_a(k) = kddt_h(k)
661 
662  dkd = kddt_h_a(k)
663 
664  call find_pe_chg(kd0, dkd, hp_a(k-1), hp_b(k), &
665  th_a(k-1), sh_a(k-1), th_b(k), sh_b(k), &
666  dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
667  pres(k), dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
668  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
669  pe_chg=pe_change, colht_cor=colht_cor)
670  pe_chg_k(k,3) = pe_change
671  colht_cor_k(k,3) = colht_cor
672 
673  b1 = 1.0 / (hp_a(k-1) + kddt_h_a(k))
674  c1_a(k) = kddt_h_a(k) * b1
675  if (k==2) then
676  te_a(1) = b1*(h_tr(1)*t0(1))
677  se_a(1) = b1*(h_tr(1)*s0(1))
678  else
679  te_a(k-1) = b1 * (h_tr(k-1) * t0(k-1) + kddt_h_a(k-1) * te_a(k-2))
680  se_a(k-1) = b1 * (h_tr(k-1) * s0(k-1) + kddt_h_a(k-1) * se_a(k-2))
681  endif
682 
683  hp_a(k) = h_tr(k) + (hp_a(k-1) * b1) * kddt_h_a(k)
684  dt_to_dpe_a(k) = dt_to_dpe(k) + c1_a(k)*dt_to_dpe_a(k-1)
685  ds_to_dpe_a(k) = ds_to_dpe(k) + c1_a(k)*ds_to_dpe_a(k-1)
686  dt_to_dcolht_a(k) = dt_to_dcolht(k) + c1_a(k)*dt_to_dcolht_a(k-1)
687  ds_to_dcolht_a(k) = ds_to_dcolht(k) + c1_a(k)*ds_to_dcolht_a(k-1)
688  enddo
689 
690  ! Calculate the dependencies on layers below.
691  kddt_h_b(nz+1) = 0.0
692  do k=nz,2,-1 ! Loop over interior interfaces.
693  ! First calculate some terms that are independent of the change in Kddt_h(K).
694  kd0 = 0.0 ! This might need to be changed - it is the already applied value of Kddt_h(K).
695 ! if (K<=2) then
696  th_a(k-1) = h_tr(k-1) * t0(k-1) ; sh_a(k-1) = h_tr(k-1) * s0(k-1)
697 ! else
698 ! Th_a(k-1) = h_tr(k-1) * T0(k-1) + Kddt_h(K-1) * Te_a(k-2)
699 ! Sh_a(k-1) = h_tr(k-1) * S0(k-1) + Kddt_h(K-1) * Se_a(k-2)
700 ! endif
701  if (k>=nz) then
702  th_b(k) = h_tr(k) * t0(k) ; sh_b(k) = h_tr(k) * s0(k)
703  else
704  th_b(k) = h_tr(k) * t0(k) + kddt_h(k+1) * te_b(k+1)
705  sh_b(k) = h_tr(k) * s0(k) + kddt_h(k+1) * se_b(k+1)
706  endif
707 
708  kddt_h_b(k) = 0.0 ; if (k > k_cent) kddt_h_b(k) = kddt_h(k)
709  dkd = kddt_h_b(k)
710 
711  call find_pe_chg(kd0, dkd, hp_a(k-1), hp_b(k), &
712  th_a(k-1), sh_a(k-1), th_b(k), sh_b(k), &
713  dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
714  pres(k), dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
715  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
716  pe_chg=pe_change, colht_cor=colht_cor)
717  pe_chg_k(k,3) = pe_chg_k(k,3) + pe_change
718  colht_cor_k(k,3) = colht_cor_k(k,3) + colht_cor
719 
720  b1 = 1.0 / (hp_b(k) + kddt_h_b(k))
721  c1_b(k) = kddt_h_b(k) * b1
722  if (k==nz) then
723  te_b(k) = b1 * (h_tr(k)*t0(k))
724  se_b(k) = b1 * (h_tr(k)*s0(k))
725  else
726  te_b(k) = b1 * (h_tr(k) * t0(k) + kddt_h_b(k+1) * te_b(k+1))
727  se_b(k) = b1 * (h_tr(k) * s0(k) + kddt_h_b(k+1) * se_b(k+1))
728  endif
729 
730  hp_b(k-1) = h_tr(k-1) + (hp_b(k) * b1) * kddt_h_b(k)
731  dt_to_dpe_b(k-1) = dt_to_dpe(k-1) + c1_b(k)*dt_to_dpe_b(k)
732  ds_to_dpe_b(k-1) = ds_to_dpe(k-1) + c1_b(k)*ds_to_dpe_b(k)
733  dt_to_dcolht_b(k-1) = dt_to_dcolht(k-1) + c1_b(k)*dt_to_dcolht_b(k)
734  ds_to_dcolht_b(k-1) = ds_to_dcolht(k-1) + c1_b(k)*ds_to_dcolht_b(k)
735 
736  enddo
737 
738  ! Calculate the final solution for the layers surrounding interface K_cent
739  k = k_cent
740 
741  ! First calculate some terms that are independent of the change in Kddt_h(K).
742  kd0 = 0.0 ! This might need to be changed - it is the already applied value of Kddt_h(K).
743  if (k<=2) then
744  th_a(k-1) = h_tr(k-1) * t0(k-1) ; sh_a(k-1) = h_tr(k-1) * s0(k-1)
745  else
746  th_a(k-1) = h_tr(k-1) * t0(k-1) + kddt_h(k-1) * te_a(k-2)
747  sh_a(k-1) = h_tr(k-1) * s0(k-1) + kddt_h(k-1) * se_a(k-2)
748  endif
749  if (k>=nz) then
750  th_b(k) = h_tr(k) * t0(k) ; sh_b(k) = h_tr(k) * s0(k)
751  else
752  th_b(k) = h_tr(k) * t0(k) + kddt_h(k+1) * te_b(k+1)
753  sh_b(k) = h_tr(k) * s0(k) + kddt_h(k+1) * se_b(k+1)
754  endif
755 
756  dkd = kddt_h(k)
757 
758  call find_pe_chg(kd0, dkd, hp_a(k-1), hp_b(k), &
759  th_a(k-1), sh_a(k-1), th_b(k), sh_b(k), &
760  dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
761  pres(k), dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
762  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
763  pe_chg=pe_change, colht_cor=colht_cor)
764  pe_chg_k(k,3) = pe_chg_k(k,3) + pe_change
765  colht_cor_k(k,3) = colht_cor_k(k,3) + colht_cor
766 
767 
768  ! To derive the following, first to a partial update for the estimated
769  ! temperatures and salinities in the layers around this interface:
770  ! b1_a = 1.0 / (hp_a(k-1) + Kddt_h(K))
771  ! b1_b = 1.0 / (hp_b(k) + Kddt_h(K))
772  ! Te_up(k) = Th_b(k) * b1_b ; Se_up(k) = Sh_b(k) * b1_b
773  ! Te_up(k-1) = Th_a(k-1) * b1_a ; Se_up(k-1) = Sh_a(k-1) * b1_a
774  ! Find the final values of T & S for both layers around K_cent, using that
775  ! c1_a(K) = Kddt_h(K) * b1_a ; c1_b(K) = Kddt_h(K) * b1_b
776  ! Tf(K_cent-1) = Te_up(K_cent-1) + c1_a(K_cent)*Tf(K_cent)
777  ! Tf(K_cent) = Te_up(K_cent) + c1_b(K_cent)*Tf(K_cent-1)
778  ! but further exploiting the expressions for c1_a and c1_b to avoid
779  ! subtraction in the denominator, and use only a single division.
780  b1 = 1.0 / (hp_a(k-1)*hp_b(k) + kddt_h(k)*(hp_a(k-1) + hp_b(k)))
781  tf(k-1) = ((hp_b(k) + kddt_h(k)) * th_a(k-1) + kddt_h(k) * th_b(k) ) * b1
782  sf(k-1) = ((hp_b(k) + kddt_h(k)) * sh_a(k-1) + kddt_h(k) * sh_b(k) ) * b1
783  tf(k) = (kddt_h(k) * th_a(k-1) + (hp_a(k-1) + kddt_h(k)) * th_b(k) ) * b1
784  sf(k) = (kddt_h(k) * sh_a(k-1) + (hp_a(k-1) + kddt_h(k)) * sh_b(k) ) * b1
785 
786  c1_a(k) = kddt_h(k) / (hp_a(k-1) + kddt_h(k))
787  c1_b(k) = kddt_h(k) / (hp_b(k) + kddt_h(k))
788 
789  ! Now update the other layer working outward from k_cent to determine the final
790  ! temperatures and salinities.
791  do k=k_cent-2,1,-1
792  tf(k) = te_a(k) + c1_a(k+1)*tf(k+1)
793  sf(k) = se_a(k) + c1_a(k+1)*sf(k+1)
794  enddo
795  do k=k_cent+1,nz
796  tf(k) = te_b(k) + c1_b(k)*tf(k-1)
797  sf(k) = se_b(k) + c1_b(k)*sf(k-1)
798  enddo
799 
800  if (debug) then
801  pe_chg_tot1c = 0.0 ; pe_chg_tot2c = 0.0 ; t_chg_totc = 0.0
802  do k=1,nz
803  pe_chg_tot1c = pe_chg_tot1c + (dt_to_dpe(k) * (tf(k) - t0(k)) + &
804  ds_to_dpe(k) * (sf(k) - s0(k)))
805  t_chg_totc = t_chg_totc + h_tr(k) * (tf(k) - t0(k))
806  enddo
807  do k=2,nz
808  pe_chg_tot2c = pe_chg_tot2c + (pe_chg_k(k,3) - colht_cor_k(k,3))
809  enddo
810  endif
811 
812  endif
813 
814  if (halves) then
815 
816  ! Set up values appropriate for no diffusivity.
817  do k=1,nz
818  hp_a(k) = h_tr(k) ; hp_b(k) = h_tr(k)
819  dt_to_dpe_a(k) = dt_to_dpe(k) ; ds_to_dpe_a(k) = ds_to_dpe(k)
820  dt_to_dpe_b(k) = dt_to_dpe(k) ; ds_to_dpe_b(k) = ds_to_dpe(k)
821  dt_to_dcolht_a(k) = dt_to_dcolht(k) ; ds_to_dcolht_a(k) = ds_to_dcolht(k)
822  dt_to_dcolht_b(k) = dt_to_dcolht(k) ; ds_to_dcolht_b(k) = ds_to_dcolht(k)
823  enddo
824  do k=1,nz+1
825  kd_so_far(k) = 0.0
826  enddo
827 
828  ! Calculate the dependencies on layers above.
829  kddt_h_a(1) = 0.0
830  do k=2,nz ! Loop over interior interfaces.
831  ! First calculate some terms that are independent of the change in Kddt_h(K).
832  kd0 = kd_so_far(k)
833  if (k<=2) then
834  th_a(k-1) = h_tr(k-1) * t0(k-1) ; sh_a(k-1) = h_tr(k-1) * s0(k-1)
835  else
836  th_a(k-1) = h_tr(k-1) * t0(k-1) + kd_so_far(k-1) * te(k-2)
837  sh_a(k-1) = h_tr(k-1) * s0(k-1) + kd_so_far(k-1) * se(k-2)
838  endif
839  th_b(k) = h_tr(k) * t0(k) ; sh_b(k) = h_tr(k) * s0(k)
840 
841  dkd = 0.5 * kddt_h(k) - kd_so_far(k)
842 
843  call find_pe_chg(kd0, dkd, hp_a(k-1), hp_b(k), &
844  th_a(k-1), sh_a(k-1), th_b(k), sh_b(k), &
845  dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
846  pres(k), dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
847  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
848  pe_chg=pe_change, colht_cor=colht_cor)
849 
850  pe_chg_k(k,4) = pe_change
851  colht_cor_k(k,4) = colht_cor
852 
853  kd_so_far(k) = kd_so_far(k) + dkd
854 
855  b1 = 1.0 / (hp_a(k-1) + kd_so_far(k))
856  c1_a(k) = kd_so_far(k) * b1
857  if (k==2) then
858  te(1) = b1*(h_tr(1)*t0(1))
859  se(1) = b1*(h_tr(1)*s0(1))
860  else
861  te(k-1) = b1 * (h_tr(k-1) * t0(k-1) + kd_so_far(k-1) * te(k-2))
862  se(k-1) = b1 * (h_tr(k-1) * s0(k-1) + kd_so_far(k-1) * se(k-2))
863  endif
864 
865  hp_a(k) = h_tr(k) + (hp_a(k-1) * b1) * kd_so_far(k)
866  dt_to_dpe_a(k) = dt_to_dpe(k) + c1_a(k)*dt_to_dpe_a(k-1)
867  ds_to_dpe_a(k) = ds_to_dpe(k) + c1_a(k)*ds_to_dpe_a(k-1)
868  dt_to_dcolht_a(k) = dt_to_dcolht(k) + c1_a(k)*dt_to_dcolht_a(k-1)
869  ds_to_dcolht_a(k) = ds_to_dcolht(k) + c1_a(k)*ds_to_dcolht_a(k-1)
870  enddo
871 
872  ! Calculate the dependencies on layers below.
873  do k=nz,2,-1 ! Loop over interior interfaces.
874  ! First calculate some terms that are independent of the change in Kddt_h(K).
875  kd0 = kd_so_far(k)
876  if (k<=2) then
877  th_a(k-1) = h_tr(k-1) * t0(k-1) ; sh_a(k-1) = h_tr(k-1) * s0(k-1)
878  else
879  th_a(k-1) = h_tr(k-1) * t0(k-1) + kd_so_far(k-1) * te(k-2)
880  sh_a(k-1) = h_tr(k-1) * s0(k-1) + kd_so_far(k-1) * se(k-2)
881  endif
882  if (k>=nz) then
883  th_b(k) = h_tr(k) * t0(k) ; sh_b(k) = h_tr(k) * s0(k)
884  else
885  th_b(k) = h_tr(k) * t0(k) + kd_so_far(k+1) * te(k+1)
886  sh_b(k) = h_tr(k) * s0(k) + kd_so_far(k+1) * se(k+1)
887  endif
888 
889  dkd = kddt_h(k) - kd_so_far(k)
890 
891  call find_pe_chg(kd0, dkd, hp_a(k-1), hp_b(k), &
892  th_a(k-1), sh_a(k-1), th_b(k), sh_b(k), &
893  dt_to_dpe_a(k-1), ds_to_dpe_a(k-1), dt_to_dpe_b(k), ds_to_dpe_b(k), &
894  pres(k), dt_to_dcolht_a(k-1), ds_to_dcolht_a(k-1), &
895  dt_to_dcolht_b(k), ds_to_dcolht_b(k), &
896  pe_chg=pe_change, colht_cor=colht_cor)
897 
898  pe_chg_k(k,4) = pe_chg_k(k,4) + pe_change
899  colht_cor_k(k,4) = colht_cor_k(k,4) + colht_cor
900 
901 
902  kd_so_far(k) = kd_so_far(k) + dkd
903 
904  b1 = 1.0 / (hp_b(k) + kd_so_far(k))
905  c1_b(k) = kd_so_far(k) * b1
906  if (k==nz) then
907  te(k) = b1 * (h_tr(k)*t0(k))
908  se(k) = b1 * (h_tr(k)*s0(k))
909  else
910  te(k) = b1 * (h_tr(k) * t0(k) + kd_so_far(k+1) * te(k+1))
911  se(k) = b1 * (h_tr(k) * s0(k) + kd_so_far(k+1) * se(k+1))
912  endif
913 
914  hp_b(k-1) = h_tr(k-1) + (hp_b(k) * b1) * kd_so_far(k)
915  dt_to_dpe_b(k-1) = dt_to_dpe(k-1) + c1_b(k)*dt_to_dpe_b(k)
916  ds_to_dpe_b(k-1) = ds_to_dpe(k-1) + c1_b(k)*ds_to_dpe_b(k)
917  dt_to_dcolht_b(k-1) = dt_to_dcolht(k-1) + c1_b(k)*dt_to_dcolht_b(k)
918  ds_to_dcolht_b(k-1) = ds_to_dcolht(k-1) + c1_b(k)*ds_to_dcolht_b(k)
919 
920  enddo
921 
922  ! Now update the other layer working down from the top to determine the
923  ! final temperatures and salinities.
924  b1 = 1.0 / (hp_b(1))
925  tf(1) = b1 * (h_tr(1) * t0(1) + kddt_h(2) * te(2))
926  sf(1) = b1 * (h_tr(1) * s0(1) + kddt_h(2) * se(2))
927  do k=2,nz
928  tf(k) = te(k) + c1_b(k)*tf(k-1)
929  sf(k) = se(k) + c1_b(k)*sf(k-1)
930  enddo
931 
932  if (debug) then
933  pe_chg_tot1d = 0.0 ; pe_chg_tot2d = 0.0 ; t_chg_totd = 0.0
934  do k=1,nz
935  pe_chg_tot1d = pe_chg_tot1d + (dt_to_dpe(k) * (tf(k) - t0(k)) + &
936  ds_to_dpe(k) * (sf(k) - s0(k)))
937  t_chg_totd = t_chg_totd + h_tr(k) * (tf(k) - t0(k))
938  enddo
939  do k=2,nz
940  pe_chg_tot2d = pe_chg_tot2d + (pe_chg_k(k,4) - colht_cor_k(k,4))
941  enddo
942  endif
943 
944  endif
945 
946  energy_kd = 0.0 ; do k=2,nz ; energy_kd = energy_kd + pe_chg_k(k,1) ; enddo
947  energy_kd = energy_kd / dt
948  k=nz
949 
950  if (do_print) then
951  if (cs%id_ERt>0) call post_data_1d_k(cs%id_ERt, pe_chg_k(:,1), cs%diag)
952  if (cs%id_ERb>0) call post_data_1d_k(cs%id_ERb, pe_chg_k(:,2), cs%diag)
953  if (cs%id_ERc>0) call post_data_1d_k(cs%id_ERc, pe_chg_k(:,3), cs%diag)
954  if (cs%id_ERh>0) call post_data_1d_k(cs%id_ERh, pe_chg_k(:,4), cs%diag)
955  if (cs%id_Kddt>0) call post_data_1d_k(cs%id_Kddt, gv%H_to_m*kddt_h, cs%diag)
956  if (cs%id_Kd>0) call post_data_1d_k(cs%id_Kd, kd, cs%diag)
957  if (cs%id_h>0) call post_data_1d_k(cs%id_h, gv%H_to_m*h_tr, cs%diag)
958  if (cs%id_zInt>0) call post_data_1d_k(cs%id_zInt, z_int, cs%diag)
959  if (cs%id_CHCt>0) call post_data_1d_k(cs%id_CHCt, colht_cor_k(:,1), cs%diag)
960  if (cs%id_CHCb>0) call post_data_1d_k(cs%id_CHCb, colht_cor_k(:,2), cs%diag)
961  if (cs%id_CHCc>0) call post_data_1d_k(cs%id_CHCc, colht_cor_k(:,3), cs%diag)
962  if (cs%id_CHCh>0) call post_data_1d_k(cs%id_CHCh, colht_cor_k(:,4), cs%diag)
963  if (cs%id_T0>0) call post_data_1d_k(cs%id_T0, t0, cs%diag)
964  if (cs%id_Tf>0) call post_data_1d_k(cs%id_Tf, tf, cs%diag)
965  if (cs%id_S0>0) call post_data_1d_k(cs%id_S0, s0, cs%diag)
966  if (cs%id_Sf>0) call post_data_1d_k(cs%id_Sf, sf, cs%diag)
967  if (cs%id_N2_0>0) then
968  n2(1) = 0.0 ; n2(nz+1) = 0.0
969  do k=2,nz
970  call calculate_density(0.5*(t0(k-1) + t0(k)), 0.5*(s0(k-1) + s0(k)), &
971  pres(k), rho_here, tv%eqn_of_state)
972  n2(k) = (gv%g_Earth * rho_here / (0.5*gv%H_to_m*(h_tr(k-1) + h_tr(k)))) * &
973  ( 0.5*(dsv_dt(k-1) + dsv_dt(k)) * (t0(k-1) - t0(k)) + &
974  0.5*(dsv_ds(k-1) + dsv_ds(k)) * (s0(k-1) - s0(k)) )
975  enddo
976  call post_data_1d_k(cs%id_N2_0, n2, cs%diag)
977  endif
978  if (cs%id_N2_f>0) then
979  n2(1) = 0.0 ; n2(nz+1) = 0.0
980  do k=2,nz
981  call calculate_density(0.5*(tf(k-1) + tf(k)), 0.5*(sf(k-1) + sf(k)), &
982  pres(k), rho_here, tv%eqn_of_state)
983  n2(k) = (gv%g_Earth * rho_here / (0.5*gv%H_to_m*(h_tr(k-1) + h_tr(k)))) * &
984  ( 0.5*(dsv_dt(k-1) + dsv_dt(k)) * (tf(k-1) - tf(k)) + &
985  0.5*(dsv_ds(k-1) + dsv_ds(k)) * (sf(k-1) - sf(k)) )
986  enddo
987  call post_data_1d_k(cs%id_N2_f, n2, cs%diag)
988  endif
989  endif
990 
991 end subroutine diapyc_energy_req_calc
992 
993 !> This subroutine calculates the change in potential energy and or derivatives
994 !! for several changes in an interfaces's diapycnal diffusivity times a timestep.
995 subroutine find_pe_chg(Kddt_h0, dKddt_h, hp_a, hp_b, Th_a, Sh_a, Th_b, Sh_b, &
996  dT_to_dPE_a, dS_to_dPE_a, dT_to_dPE_b, dS_to_dPE_b, &
997  pres, dT_to_dColHt_a, dS_to_dColHt_a, dT_to_dColHt_b, dS_to_dColHt_b, &
998  PE_chg, dPEc_dKd, dPE_max, dPEc_dKd_0, ColHt_cor)
999  real, intent(in) :: Kddt_h0 !< The previously used diffusivity at an interface times
1000  !! the time step and divided by the average of the
1001  !! thicknesses around the interface, in units of H (m or kg-2).
1002  real, intent(in) :: dKddt_h !< The trial change in the diffusivity at an interface times
1003  !! the time step and divided by the average of the
1004  !! thicknesses around the interface, in units of H (m or kg-2).
1005  real, intent(in) :: hp_a !< The effective pivot thickness of the layer above the
1006  !! interface, given by h_k plus a term that
1007  !! is a fraction (determined from the tridiagonal solver) of
1008  !! Kddt_h for the interface above, in H.
1009  real, intent(in) :: hp_b !< The effective pivot thickness of the layer below the
1010  !! interface, given by h_k plus a term that
1011  !! is a fraction (determined from the tridiagonal solver) of
1012  !! Kddt_h for the interface above, in H.
1013  real, intent(in) :: Th_a !< An effective temperature times a thickness in the layer
1014  !! above, including implicit mixing effects with other
1015  !! yet higher layers, in K H.
1016  real, intent(in) :: Sh_a !< An effective salinity times a thickness in the layer
1017  !! above, including implicit mixing effects with other
1018  !! yet higher layers, in K H.
1019  real, intent(in) :: Th_b !< An effective temperature times a thickness in the layer
1020  !! below, including implicit mixing effects with other
1021  !! yet lower layers, in K H.
1022  real, intent(in) :: Sh_b !< An effective salinity times a thickness in the layer
1023  !! below, including implicit mixing effects with other
1024  !! yet lower layers, in K H.
1025  real, intent(in) :: dT_to_dPE_a !< A factor (pres_lay*mass_lay*dSpec_vol/dT) relating
1026  !! a layer's temperature change to the change in column
1027  !! potential energy, including all implicit diffusive changes
1028  !! in the temperatures of all the layers above, in J m-2 K-1.
1029  real, intent(in) :: dS_to_dPE_a !< A factor (pres_lay*mass_lay*dSpec_vol/dS) relating
1030  !! a layer's salinity change to the change in column
1031  !! potential energy, including all implicit diffusive changes
1032  !! in the salinities of all the layers above, in J m-2 ppt-1.
1033  real, intent(in) :: dT_to_dPE_b !< A factor (pres_lay*mass_lay*dSpec_vol/dT) relating
1034  !! a layer's temperature change to the change in column
1035  !! potential energy, including all implicit diffusive changes
1036  !! in the temperatures of all the layers below, in J m-2 K-1.
1037  real, intent(in) :: dS_to_dPE_b !< A factor (pres_lay*mass_lay*dSpec_vol/dS) relating
1038  !! a layer's salinity change to the change in column
1039  !! potential energy, including all implicit diffusive changes
1040  !! in the salinities of all the layers below, in J m-2 ppt-1.
1041  real, intent(in) :: pres !< The hydrostatic interface pressure, which is used to relate
1042  !! the changes in column thickness to the energy that is radiated
1043  !! as gravity waves and unavailable to drive mixing, in Pa.
1044  real, intent(in) :: dT_to_dColHt_a !< A factor (mass_lay*dSColHtc_vol/dT) relating
1045  !! a layer's temperature change to the change in column
1046  !! height, including all implicit diffusive changes
1047  !! in the temperatures of all the layers above, in m K-1.
1048  real, intent(in) :: dS_to_dColHt_a !< A factor (mass_lay*dSColHtc_vol/dS) relating
1049  !! a layer's salinity change to the change in column
1050  !! height, including all implicit diffusive changes
1051  !! in the salinities of all the layers above, in m ppt-1.
1052  real, intent(in) :: dT_to_dColHt_b !< A factor (mass_lay*dSColHtc_vol/dT) relating
1053  !! a layer's temperature change to the change in column
1054  !! height, including all implicit diffusive changes
1055  !! in the temperatures of all the layers below, in m K-1.
1056  real, intent(in) :: dS_to_dColHt_b !< A factor (mass_lay*dSColHtc_vol/dS) relating
1057  !! a layer's salinity change to the change in column
1058  !! height, including all implicit diffusive changes
1059  !! in the salinities of all the layers below, in m ppt-1.
1060 
1061  real, optional, intent(out) :: PE_chg !< The change in column potential energy from applying
1062  !! Kddt_h at the present interface, in J m-2.
1063  real, optional, intent(out) :: dPEc_dKd !< The partial derivative of PE_chg with Kddt_h,
1064  !! in units of J m-2 H-1.
1065  real, optional, intent(out) :: dPE_max !< The maximum change in column potential energy that could
1066  !! be realizedd by applying a huge value of Kddt_h at the
1067  !! present interface, in J m-2.
1068  real, optional, intent(out) :: dPEc_dKd_0 !< The partial derivative of PE_chg with Kddt_h in the
1069  !! limit where Kddt_h = 0, in J m-2 H-1.
1070  real, optional, intent(out) :: ColHt_cor !< The correction to PE_chg that is made due to a net
1071  !! change in the column height, in J m-2.
1072 
1073  real :: hps ! The sum of the two effective pivot thicknesses, in H.
1074  real :: bdt1 ! A product of the two pivot thicknesses plus a diffusive term, in H2.
1075  real :: dT_c ! The core term in the expressions for the temperature changes, in K H2.
1076  real :: dS_c ! The core term in the expressions for the salinity changes, in psu H2.
1077  real :: PEc_core ! The diffusivity-independent core term in the expressions
1078  ! for the potential energy changes, J m-3.
1079  real :: ColHt_core ! The diffusivity-independent core term in the expressions
1080  ! for the column height changes, J m-3.
1081  real :: ColHt_chg ! The change in the column height, in m.
1082  real :: y1 ! A local temporary term, in units of H-3 or H-4 in various contexts.
1083 
1084  ! The expression for the change in potential energy used here is derived
1085  ! from the expression for the final estimates of the changes in temperature
1086  ! and salinities, and then extensively manipulated to get it into its most
1087  ! succint form. The derivation is not necessarily obvious, but it demonstrably
1088  ! works by comparison with separate calculations of the energy changes after
1089  ! the tridiagonal solver for the final changes in temperature and salinity are
1090  ! applied.
1091 
1092  hps = hp_a + hp_b
1093  bdt1 = hp_a * hp_b + kddt_h0 * hps
1094  dt_c = hp_a * th_b - hp_b * th_a
1095  ds_c = hp_a * sh_b - hp_b * sh_a
1096  pec_core = hp_b * (dt_to_dpe_a * dt_c + ds_to_dpe_a * ds_c) - &
1097  hp_a * (dt_to_dpe_b * dt_c + ds_to_dpe_b * ds_c)
1098  colht_core = hp_b * (dt_to_dcolht_a * dt_c + ds_to_dcolht_a * ds_c) - &
1099  hp_a * (dt_to_dcolht_b * dt_c + ds_to_dcolht_b * ds_c)
1100 
1101  if (present(pe_chg)) then
1102  ! Find the change in column potential energy due to the change in the
1103  ! diffusivity at this interface by dKddt_h.
1104  y1 = dkddt_h / (bdt1 * (bdt1 + dkddt_h * hps))
1105  pe_chg = pec_core * y1
1106  colht_chg = colht_core * y1
1107  if (colht_chg < 0.0) pe_chg = pe_chg - pres * colht_chg
1108  if (present(colht_cor)) colht_cor = -pres * min(colht_chg, 0.0)
1109  else if (present(colht_cor)) then
1110  y1 = dkddt_h / (bdt1 * (bdt1 + dkddt_h * hps))
1111  colht_cor = -pres * min(colht_core * y1, 0.0)
1112  endif
1113 
1114  if (present(dpec_dkd)) then
1115  ! Find the derivative of the potential energy change with dKddt_h.
1116  y1 = 1.0 / (bdt1 + dkddt_h * hps)**2
1117  dpec_dkd = pec_core * y1
1118  colht_chg = colht_core * y1
1119  if (colht_chg < 0.0) dpec_dkd = dpec_dkd - pres * colht_chg
1120  endif
1121 
1122  if (present(dpe_max)) then
1123  ! This expression is the limit of PE_chg for infinite dKddt_h.
1124  y1 = 1.0 / (bdt1 * hps)
1125  dpe_max = pec_core * y1
1126  colht_chg = colht_core * y1
1127  if (colht_chg < 0.0) dpe_max = dpe_max - pres * colht_chg
1128  endif
1129 
1130  if (present(dpec_dkd_0)) then
1131  ! This expression is the limit of dPEc_dKd for dKddt_h = 0.
1132  y1 = 1.0 / bdt1**2
1133  dpec_dkd_0 = pec_core * y1
1134  colht_chg = colht_core * y1
1135  if (colht_chg < 0.0) dpec_dkd_0 = dpec_dkd_0 - pres * colht_chg
1136  endif
1137 
1138 end subroutine find_pe_chg
1139 
1140 
1141 !> This subroutine calculates the change in potential energy and or derivatives
1142 !! for several changes in an interfaces's diapycnal diffusivity times a timestep
1143 !! using the original form used in the first version of ePBL.
1144 subroutine find_pe_chg_orig(Kddt_h, h_k, b_den_1, dTe_term, dSe_term, &
1145  dT_km1_t2, dS_km1_t2, dT_to_dPE_k, dS_to_dPE_k, &
1146  dT_to_dPEa, dS_to_dPEa, pres, dT_to_dColHt_k, &
1147  dS_to_dColHt_k, dT_to_dColHta, dS_to_dColHta, &
1148  PE_chg, dPEc_dKd, dPE_max, dPEc_dKd_0)
1149  real, intent(in) :: Kddt_h !< The diffusivity at an interface times the time step and
1150  !! divided by the average of the thicknesses around the
1151  !! interface, in units of H (m or kg-2).
1152  real, intent(in) :: h_k !< The thickness of the layer below the interface, in H.
1153  real, intent(in) :: b_den_1 !< The first term in the denominator of the pivot
1154  !! for the tridiagonal solver, given by h_k plus a term that
1155  !! is a fraction (determined from the tridiagonal solver) of
1156  !! Kddt_h for the interface above, in H.
1157  real, intent(in) :: dTe_term !< A diffusivity-independent term related to the
1158  !! temperature change in the layer below the interface, in K H.
1159  real, intent(in) :: dSe_term !< A diffusivity-independent term related to the
1160  !! salinity change in the layer below the interface, in ppt H.
1161  real, intent(in) :: dT_km1_t2 !< A diffusivity-independent term related to the
1162  !! temperature change in the layer above the interface, in K.
1163  real, intent(in) :: dS_km1_t2 !< A diffusivity-independent term related to the
1164  !! salinity change in the layer above the interface, in ppt.
1165  real, intent(in) :: pres !< The hydrostatic interface pressure, which is used to relate
1166  !! the changes in column thickness to the energy that is radiated
1167  !! as gravity waves and unavailable to drive mixing, in Pa.
1168  real, intent(in) :: dT_to_dPE_k !< A factor (pres_lay*mass_lay*dSpec_vol/dT) relating
1169  !! a layer's temperature change to the change in column
1170  !! potential energy, including all implicit diffusive changes
1171  !! in the temperatures of all the layers below, in J m-2 K-1.
1172  real, intent(in) :: dS_to_dPE_k !< A factor (pres_lay*mass_lay*dSpec_vol/dS) relating
1173  !! a layer's salinity change to the change in column
1174  !! potential energy, including all implicit diffusive changes
1175  !! in the salinities of all the layers below, in J m-2 ppt-1.
1176  real, intent(in) :: dT_to_dPEa !< A factor (pres_lay*mass_lay*dSpec_vol/dT) relating
1177  !! a layer's temperature change to the change in column
1178  !! potential energy, including all implicit diffusive changes
1179  !! in the temperatures of all the layers above, in J m-2 K-1.
1180  real, intent(in) :: dS_to_dPEa !< A factor (pres_lay*mass_lay*dSpec_vol/dS) relating
1181  !! a layer's salinity change to the change in column
1182  !! potential energy, including all implicit diffusive changes
1183  !! in the salinities of all the layers above, in J m-2 ppt-1.
1184  real, intent(in) :: dT_to_dColHt_k !< A factor (mass_lay*dSColHtc_vol/dT) relating
1185  !! a layer's temperature change to the change in column
1186  !! height, including all implicit diffusive changes
1187  !! in the temperatures of all the layers below, in m K-1.
1188  real, intent(in) :: dS_to_dColHt_k !< A factor (mass_lay*dSColHtc_vol/dS) relating
1189  !! a layer's salinity change to the change in column
1190  !! height, including all implicit diffusive changes
1191  !! in the salinities of all the layers below, in m ppt-1.
1192  real, intent(in) :: dT_to_dColHta !< A factor (mass_lay*dSColHtc_vol/dT) relating
1193  !! a layer's temperature change to the change in column
1194  !! height, including all implicit diffusive changes
1195  !! in the temperatures of all the layers above, in m K-1.
1196  real, intent(in) :: dS_to_dColHta !< A factor (mass_lay*dSColHtc_vol/dS) relating
1197  !! a layer's salinity change to the change in column
1198  !! height, including all implicit diffusive changes
1199  !! in the salinities of all the layers above, in m ppt-1.
1200 
1201  real, optional, intent(out) :: PE_chg !< The change in column potential energy from applying
1202  !! Kddt_h at the present interface, in J m-2.
1203  real, optional, intent(out) :: dPEc_dKd !< The partial derivative of PE_chg with Kddt_h,
1204  !! in units of J m-2 H-1.
1205  real, optional, intent(out) :: dPE_max !< The maximum change in column potential energy that could
1206  !! be realizedd by applying a huge value of Kddt_h at the
1207  !! present interface, in J m-2.
1208  real, optional, intent(out) :: dPEc_dKd_0 !< The partial derivative of PE_chg with Kddt_h in the
1209  !! limit where Kddt_h = 0, in J m-2 H-1.
1210 
1211 ! This subroutine determines the total potential energy change due to mixing
1212 ! at an interface, including all of the implicit effects of the prescribed
1213 ! mixing at interfaces above. Everything here is derived by careful manipulation
1214 ! of the robust tridiagonal solvers used for tracers by MOM6. The results are
1215 ! positive for mixing in a stably stratified environment.
1216 ! The comments describing these arguments are for a downward mixing pass, but
1217 ! this routine can also be used for an upward pass with the sense of direction
1218 ! reversed.
1219 
1220  real :: b1 ! b1 is used by the tridiagonal solver, in H-1.
1221  real :: b1Kd ! Temporary array (nondim.)
1222  real :: ColHt_chg ! The change in column thickness in m.
1223  real :: dColHt_max ! The change in column thickess for infinite diffusivity, in m.
1224  real :: dColHt_dKd ! The partial derivative of column thickess with diffusivity, in s m-1.
1225  real :: dT_k, dT_km1 ! Temporary arrays in K.
1226  real :: dS_k, dS_km1 ! Temporary arrays in ppt.
1227  real :: I_Kr_denom, dKr_dKd ! Temporary arrays in H-2 and nondim.
1228  real :: ddT_k_dKd, ddT_km1_dKd ! Temporary arrays in K H-1.
1229  real :: ddS_k_dKd, ddS_km1_dKd ! Temporary arrays in ppt H-1.
1230 
1231  b1 = 1.0 / (b_den_1 + kddt_h)
1232  b1kd = kddt_h*b1
1233 
1234  ! Start with the temperature change in layer k-1 due to the diffusivity at
1235  ! interface K without considering the effects of changes in layer k.
1236 
1237  ! Calculate the change in PE due to the diffusion at interface K
1238  ! if Kddt_h(K+1) = 0.
1239  i_kr_denom = 1.0 / (h_k*b_den_1 + (b_den_1 + h_k)*kddt_h)
1240 
1241  dt_k = (kddt_h*i_kr_denom) * dte_term
1242  ds_k = (kddt_h*i_kr_denom) * dse_term
1243 
1244  if (present(pe_chg)) then
1245  ! Find the change in energy due to diffusion with strength Kddt_h at this interface.
1246  ! Increment the temperature changes in layer k-1 due the changes in layer k.
1247  dt_km1 = b1kd * ( dt_k + dt_km1_t2 )
1248  ds_km1 = b1kd * ( ds_k + ds_km1_t2 )
1249 
1250  pe_chg = (dt_to_dpe_k * dt_k + dt_to_dpea * dt_km1) + &
1251  (ds_to_dpe_k * ds_k + ds_to_dpea * ds_km1)
1252  colht_chg = (dt_to_dcolht_k * dt_k + dt_to_dcolhta * dt_km1) + &
1253  (ds_to_dcolht_k * ds_k + ds_to_dcolhta * ds_km1)
1254  if (colht_chg < 0.0) pe_chg = pe_chg - pres * colht_chg
1255  endif
1256 
1257  if (present(dpec_dkd)) then
1258  ! Find the derivatives of the temperature and salinity changes with Kddt_h.
1259  dkr_dkd = (h_k*b_den_1) * i_kr_denom**2
1260 
1261  ddt_k_dkd = dkr_dkd * dte_term
1262  dds_k_dkd = dkr_dkd * dse_term
1263  ddt_km1_dkd = (b1**2 * b_den_1) * ( dt_k + dt_km1_t2 ) + b1kd * ddt_k_dkd
1264  dds_km1_dkd = (b1**2 * b_den_1) * ( ds_k + ds_km1_t2 ) + b1kd * dds_k_dkd
1265 
1266  ! Calculate the partial derivative of Pe_chg with Kddt_h.
1267  dpec_dkd = (dt_to_dpe_k * ddt_k_dkd + dt_to_dpea * ddt_km1_dkd) + &
1268  (ds_to_dpe_k * dds_k_dkd + ds_to_dpea * dds_km1_dkd)
1269  dcolht_dkd = (dt_to_dcolht_k * ddt_k_dkd + dt_to_dcolhta * ddt_km1_dkd) + &
1270  (ds_to_dcolht_k * dds_k_dkd + ds_to_dcolhta * dds_km1_dkd)
1271  if (dcolht_dkd < 0.0) dpec_dkd = dpec_dkd - pres * dcolht_dkd
1272  endif
1273 
1274  if (present(dpe_max)) then
1275  ! This expression is the limit of PE_chg for infinite Kddt_h.
1276  dpe_max = (dt_to_dpea * dt_km1_t2 + ds_to_dpea * ds_km1_t2) + &
1277  ((dt_to_dpe_k + dt_to_dpea) * dte_term + &
1278  (ds_to_dpe_k + ds_to_dpea) * dse_term) / (b_den_1 + h_k)
1279  dcolht_max = (dt_to_dcolhta * dt_km1_t2 + ds_to_dcolhta * ds_km1_t2) + &
1280  ((dt_to_dcolht_k + dt_to_dcolhta) * dte_term + &
1281  (ds_to_dcolht_k + ds_to_dcolhta) * dse_term) / (b_den_1 + h_k)
1282  if (dcolht_max < 0.0) dpe_max = dpe_max - pres*dcolht_max
1283  endif
1284 
1285  if (present(dpec_dkd_0)) then
1286  ! This expression is the limit of dPEc_dKd for Kddt_h = 0.
1287  dpec_dkd_0 = (dt_to_dpea * dt_km1_t2 + ds_to_dpea * ds_km1_t2) / (b_den_1) + &
1288  (dt_to_dpe_k * dte_term + ds_to_dpe_k * dse_term) / (h_k*b_den_1)
1289  dcolht_dkd = (dt_to_dcolhta * dt_km1_t2 + ds_to_dcolhta * ds_km1_t2) / (b_den_1) + &
1290  (dt_to_dcolht_k * dte_term + ds_to_dcolht_k * dse_term) / (h_k*b_den_1)
1291  if (dcolht_dkd < 0.0) dpec_dkd_0 = dpec_dkd_0 - pres*dcolht_dkd
1292  endif
1293 
1294 end subroutine find_pe_chg_orig
1295 
1296 subroutine diapyc_energy_req_init(Time, G, param_file, diag, CS)
1297  type(time_type), intent(in) :: Time !< model time
1298  type(ocean_grid_type), intent(in) :: G !< model grid structure
1299  type(param_file_type), intent(in) :: param_file !< file to parse for parameter values
1300  type(diag_ctrl), target, intent(inout) :: diag !< structure to regulate diagnostic output
1301  type(diapyc_energy_req_cs), pointer :: CS !< module control structure
1302 ! Arguments: param_file - A structure indicating the open file to parse for
1303 ! model parameter values.
1304 ! (in/out) Reg - A pointer that is set to point to the tracer registry.
1305  integer, save :: init_calls = 0
1306 ! This include declares and sets the variable "version".
1307 #include "version_variable.h"
1308  character(len=40) :: mdl = "MOM_diapyc_energy_req" ! This module's name.
1309  character(len=256) :: mesg ! Message for error messages.
1310 
1311  if (.not.associated(cs)) then ; allocate(cs)
1312  else ; return ; endif
1313 
1314  cs%initialized = .true.
1315  cs%diag => diag
1316 
1317  ! Read all relevant parameters and write them to the model log.
1318  call log_version(param_file, mdl, version, "")
1319  call get_param(param_file, mdl, "ENERGY_REQ_KH_SCALING", cs%test_Kh_scaling, &
1320  "A scaling factor for the diapycnal diffusivity used in \n"//&
1321  "testing the energy requirements.", default=1.0, units="nondim")
1322  call get_param(param_file, mdl, "ENERGY_REQ_COL_HT_SCALING", cs%ColHt_scaling, &
1323  "A scaling factor for the column height change correction \n"//&
1324  "used in testing the energy requirements.", default=1.0, units="nondim")
1325  call get_param(param_file, mdl, "ENERGY_REQ_USE_TEST_PROFILE", &
1326  cs%use_test_Kh_profile, &
1327  "If true, use the internal test diffusivity profile in \n"//&
1328  "place of any that might be passed in as an argument.", default=.false.)
1329 
1330  cs%id_ERt = register_diag_field('ocean_model', 'EnReqTest_ERt', diag%axesZi, time, &
1331  "Diffusivity Energy Requirements, top-down", "J m-2")
1332  cs%id_ERb = register_diag_field('ocean_model', 'EnReqTest_ERb', diag%axesZi, time, &
1333  "Diffusivity Energy Requirements, bottom-up", "J m-2")
1334  cs%id_ERc = register_diag_field('ocean_model', 'EnReqTest_ERc', diag%axesZi, time, &
1335  "Diffusivity Energy Requirements, center-last", "J m-2")
1336  cs%id_ERh = register_diag_field('ocean_model', 'EnReqTest_ERh', diag%axesZi, time, &
1337  "Diffusivity Energy Requirements, halves", "J m-2")
1338  cs%id_Kddt = register_diag_field('ocean_model', 'EnReqTest_Kddt', diag%axesZi, time, &
1339  "Implicit diffusive coupling coefficient", "m")
1340  cs%id_Kd = register_diag_field('ocean_model', 'EnReqTest_Kd', diag%axesZi, time, &
1341  "Diffusivity in test", "m2 s-1")
1342  cs%id_h = register_diag_field('ocean_model', 'EnReqTest_h_lay', diag%axesZL, time, &
1343  "Test column layer thicknesses", "m")
1344  cs%id_zInt = register_diag_field('ocean_model', 'EnReqTest_z_int', diag%axesZi, time, &
1345  "Test column layer interface heights", "m")
1346  cs%id_CHCt = register_diag_field('ocean_model', 'EnReqTest_CHCt', diag%axesZi, time, &
1347  "Column Height Correction to Energy Requirements, top-down", "J m-2")
1348  cs%id_CHCb = register_diag_field('ocean_model', 'EnReqTest_CHCb', diag%axesZi, time, &
1349  "Column Height Correction to Energy Requirements, bottom-up", "J m-2")
1350  cs%id_CHCc = register_diag_field('ocean_model', 'EnReqTest_CHCc', diag%axesZi, time, &
1351  "Column Height Correction to Energy Requirements, center-last", "J m-2")
1352  cs%id_CHCh = register_diag_field('ocean_model', 'EnReqTest_CHCh', diag%axesZi, time, &
1353  "Column Height Correction to Energy Requirements, halves", "J m-2")
1354  cs%id_T0 = register_diag_field('ocean_model', 'EnReqTest_T0', diag%axesZL, time, &
1355  "Temperature before mixing", "deg C")
1356  cs%id_Tf = register_diag_field('ocean_model', 'EnReqTest_Tf', diag%axesZL, time, &
1357  "Temperature after mixing", "deg C")
1358  cs%id_S0 = register_diag_field('ocean_model', 'EnReqTest_S0', diag%axesZL, time, &
1359  "Salinity before mixing", "g kg-1")
1360  cs%id_Sf = register_diag_field('ocean_model', 'EnReqTest_Sf', diag%axesZL, time, &
1361  "Salinity after mixing", "g kg-1")
1362  cs%id_N2_0 = register_diag_field('ocean_model', 'EnReqTest_N2_0', diag%axesZi, time, &
1363  "Squared buoyancy frequency before mixing", "second-2")
1364  cs%id_N2_f = register_diag_field('ocean_model', 'EnReqTest_N2_f', diag%axesZi, time, &
1365  "Squared buoyancy frequency after mixing", "second-2")
1366 
1367 end subroutine diapyc_energy_req_init
1368 
1369 subroutine diapyc_energy_req_end(CS)
1370  type(diapyc_energy_req_cs), pointer :: CS
1371  if (associated(cs)) deallocate(cs)
1372 end subroutine diapyc_energy_req_end
1373 
1374 end module mom_diapyc_energy_req
subroutine, public diapyc_energy_req_init(Time, G, param_file, diag, CS)
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...
subroutine, public diapyc_energy_req_end(CS)
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
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 find_pe_chg_orig(Kddt_h, h_k, b_den_1, dTe_term, dSe_term, dT_km1_t2, dS_km1_t2, dT_to_dPE_k, dS_to_dPE_k, dT_to_dPEa, dS_to_dPEa, pres, dT_to_dColHt_k, dS_to_dColHt_k, dT_to_dColHta, dS_to_dColHta, PE_chg, dPEc_dKd, dPE_max, dPEc_dKd_0)
This subroutine calculates the change in potential energy and or derivatives for several changes in a...
logical function, public is_root_pe()
subroutine find_pe_chg(Kddt_h0, dKddt_h, hp_a, hp_b, Th_a, Sh_a, Th_b, Sh_b, dT_to_dPE_a, dS_to_dPE_a, dT_to_dPE_b, dS_to_dPE_b, pres, dT_to_dColHt_a, dS_to_dColHt_a, dT_to_dColHt_b, dS_to_dColHt_b, PE_chg, dPEc_dKd, dPE_max, dPEc_dKd_0, ColHt_cor)
This subroutine calculates the change in potential energy and or derivatives for several changes in a...
subroutine, public diapyc_energy_req_calc(h_in, T_in, S_in, Kd, energy_Kd, dt, tv, G, GV, may_print, CS)
This subroutine uses a substantially refactored tridiagonal equation for diapycnal mixing of temperat...
subroutine, public diapyc_energy_req_test(h_3d, dt, tv, G, GV, CS, Kd_int)
subroutine, public mom_mesg(message, verb, all_print)
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
subroutine, public post_data_1d_k(diag_field_id, field, diag_cs, is_static)
The thermo_var_ptrs structure contains pointers to an assortment of thermodynamic fields that may be ...
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)