ks_solve_column Subroutine

private pure subroutine ks_solve_column(nz, dt, f2_val, rho0, ri_crit, shearmix_rate, fri_curvature, c_n, c_s, lambda, kappa_0, kappa_seed_in, kappa_trunc, tke_bg, tol_err, max_inner_it, max_substep_it, src_max_chg, vel_underflow, eos, h_sd, u_sd, v_sd, t_sd, s_sd, idz_s, idz_int_s, hint_s, il2_s, kappa_avg_sd, tke_avg_sd)

Full JHL08 column solve in surface-down indices (design doc section 5.1-5.2): background kappa_0 pre-step (no-slip bed for u,v; insulating T,S), frozen interface buoyancy derivatives, e1 tail recursion, then the adaptive predictor-corrector outer loop driving the Picard inner solve. Returns the time-mean diffusivity kappa_avg_sd and TKE tke_avg_sd over dt.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in) :: f2_val
real(kind=wp), intent(in) :: rho0
real(kind=wp), intent(in) :: ri_crit
real(kind=wp), intent(in) :: shearmix_rate
real(kind=wp), intent(in) :: fri_curvature
real(kind=wp), intent(in) :: c_n
real(kind=wp), intent(in) :: c_s
real(kind=wp), intent(in) :: lambda
real(kind=wp), intent(in) :: kappa_0
real(kind=wp), intent(in) :: kappa_seed_in
real(kind=wp), intent(in) :: kappa_trunc
real(kind=wp), intent(in) :: tke_bg
real(kind=wp), intent(in) :: tol_err
integer, intent(in) :: max_inner_it
integer, intent(in) :: max_substep_it
real(kind=wp), intent(in) :: src_max_chg
real(kind=wp), intent(in) :: vel_underflow
type(eos_t), intent(in) :: eos

Shared EOS handle (by value) for the buoyancy derivatives.

real(kind=wp), intent(in) :: h_sd(NZL)
real(kind=wp), intent(in) :: u_sd(NZL)
real(kind=wp), intent(in) :: v_sd(NZL)
real(kind=wp), intent(in) :: t_sd(NZL)
real(kind=wp), intent(in) :: s_sd(NZL)
real(kind=wp), intent(in) :: idz_s(NZL)
real(kind=wp), intent(in) :: idz_int_s(NZLI)
real(kind=wp), intent(in) :: hint_s(NZLI)
real(kind=wp), intent(in) :: il2_s(NZLI)
real(kind=wp), intent(out) :: kappa_avg_sd(NZLI)
real(kind=wp), intent(out) :: tke_avg_sd(NZLI)

Calls

proc~~ks_solve_column~~CallsGraph proc~ks_solve_column ks_solve_column proc~eos_specvol_derivs eos_specvol_derivs proc~ks_solve_column->proc~eos_specvol_derivs proc~ks_adaptive_dt ks_adaptive_dt proc~ks_solve_column->proc~ks_adaptive_dt proc~ks_find_kappa_tke ks_find_kappa_tke proc~ks_solve_column->proc~ks_find_kappa_tke proc~ks_projected_state ks_projected_state proc~ks_solve_column->proc~ks_projected_state proc~ks_src_func ks_src_func proc~ks_solve_column->proc~ks_src_func proc~roquet_spv_point roquet_spv_point proc~eos_specvol_derivs->proc~roquet_spv_point proc~ks_adaptive_dt->proc~ks_projected_state proc~ks_adaptive_dt->proc~ks_src_func proc~ks_find_kappa_tke->proc~ks_src_func

Called by

proc~~ks_solve_column~~CalledByGraph proc~ks_solve_column ks_solve_column proc~kappa_shear_column_kernel kappa_shear_column_kernel proc~kappa_shear_column_kernel->proc~ks_solve_column proc~kappa_shear_vertex_kernel kappa_shear_vertex_kernel proc~kappa_shear_vertex_kernel->proc~ks_solve_column proc~kappa_shear_compute kappa_shear_compute proc~kappa_shear_compute->proc~kappa_shear_column_kernel proc~kappa_shear_compute->proc~kappa_shear_vertex_kernel proc~vmix_apply_in_stage vmix_apply_in_stage proc~vmix_apply_in_stage->proc~kappa_shear_compute proc~run_stage run_stage proc~run_stage->proc~vmix_apply_in_stage proc~run_stage_split run_stage_split proc~run_stage_split->proc~vmix_apply_in_stage proc~ocean_dyn_step ocean_dyn_step proc~ocean_dyn_step->proc~run_stage proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~run_stage_split

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private :: a1c
real(kind=wp), private :: a1n
real(kind=wp), private :: aq(NZL)
real(kind=wp), private :: b1_l
real(kind=wp), private :: b1in
real(kind=wp), private :: b1ns
real(kind=wp), private :: base_l
real(kind=wp), private :: bd1_l
real(kind=wp), private :: c1_ps(NZLI)
real(kind=wp), private :: c_n2
real(kind=wp), private :: c_s2
real(kind=wp), private :: ck(NZLI)
real(kind=wp), private :: cq(NZLI)
real(kind=wp), private :: cqsav(NZLI)
real(kind=wp), private :: d1_l
real(kind=wp), private :: dbuoy_s(NZLI)
real(kind=wp), private :: dbuoy_t(NZLI)
real(kind=wp), private :: dk(NZLI)
real(kind=wp), private :: dpres
real(kind=wp), private :: dq(NZLI)
real(kind=wp), private :: dsv_ds_k
real(kind=wp), private :: dsv_dt_k
real(kind=wp), private :: dt_now
real(kind=wp), private :: dt_rem
real(kind=wp), private :: dt_wt
real(kind=wp), private :: e1(NZLI)
real(kind=wp), private :: eden1_l
real(kind=wp), private :: eden2_l
real(kind=wp), private :: i_eden_l
integer, private :: ii
real(kind=wp), private :: ilambda2
real(kind=wp), private :: ild2(NZLI)
integer, private :: io
integer, private :: k
real(kind=wp), private :: k0dt
real(kind=wp), private :: k_q(NZLI)
real(kind=wp), private :: kappa(NZLI)
real(kind=wp), private :: kappa_avg(NZLI)
real(kind=wp), private :: kappa_mid(NZLI)
real(kind=wp), private :: kappa_out(NZLI)
real(kind=wp), private :: kappa_pred(NZLI)
real(kind=wp), private :: kappa_pred2(NZLI)
real(kind=wp), private :: kappa_src(NZLI)
integer, private :: ke_kap
integer, private :: ke_mid
integer, private :: ke_ps
integer, private :: ke_src
integer, private :: kk
real(kind=wp), private :: kq_tmp(NZLI)
integer, private :: ks_kap
integer, private :: ks_mid
integer, private :: ks_ps
integer, private :: ks_src
real(kind=wp), private :: ksrc(NZLI)
real(kind=wp), private :: local_src(NZLI)
real(kind=wp), private :: local_src_avg(NZLI)
real(kind=wp), private :: n2(NZLI)
real(kind=wp), private :: n2c(NZLI)
real(kind=wp), private :: n2p(NZLI)
real(kind=wp), private :: n2v
logical, private :: no_mixing
real(kind=wp), private :: ome_l
real(kind=wp), private :: p_int
real(kind=wp), private :: s2(NZLI)
real(kind=wp), private :: s2c(NZLI)
real(kind=wp), private :: s2p(NZLI)
real(kind=wp), private :: s_c(NZL)
real(kind=wp), private :: s_int
real(kind=wp), private :: s_ps(NZL)
real(kind=wp), private :: t_c(NZL)
real(kind=wp), private :: t_int
real(kind=wp), private :: t_ps(NZL)
real(kind=wp), private :: tke(NZLI)
real(kind=wp), private :: tke_avg(NZLI)
real(kind=wp), private :: tke_fin(NZLI)
real(kind=wp), private :: tke_min
real(kind=wp), private :: tke_pred(NZLI)
real(kind=wp), private :: tkedec(NZLI)
real(kind=wp), private :: u_c(NZL)
real(kind=wp), private :: u_ps(NZL)
real(kind=wp), private :: v_c(NZL)
real(kind=wp), private :: v_ps(NZL)

Source Code

   pure subroutine ks_solve_column(nz, dt, f2_val, rho0, &
                                   ri_crit, shearmix_rate, fri_curvature, &
                                   c_n, c_s, lambda, kappa_0, kappa_seed_in, &
                                   kappa_trunc, tke_bg, tol_err, max_inner_it, &
                                   max_substep_it, src_max_chg, vel_underflow, &
                                   eos, &
                                   h_sd, u_sd, v_sd, t_sd, s_sd, &
                                   idz_s, idz_int_s, hint_s, il2_s, &
                                   kappa_avg_sd, tke_avg_sd)
      !! Full JHL08 column solve in surface-down indices (design doc
      !! section 5.1-5.2): background kappa_0 pre-step (no-slip bed for
      !! u,v; insulating T,S), frozen interface buoyancy derivatives,
      !! e1 tail recursion, then the adaptive predictor-corrector outer
      !! loop driving the Picard inner solve.  Returns the time-mean
      !! diffusivity `kappa_avg_sd` and TKE `tke_avg_sd` over dt.
      !$acc routine seq
      type(eos_t), intent(in) :: eos
         !! Shared EOS handle (by value) for the buoyancy derivatives.
      integer, intent(in) :: nz, max_inner_it, max_substep_it
      real(wp), intent(in) :: dt, f2_val, rho0
      real(wp), intent(in) :: ri_crit, shearmix_rate, fri_curvature
      real(wp), intent(in) :: c_n, c_s, lambda, kappa_0, kappa_seed_in
      real(wp), intent(in) :: kappa_trunc, tke_bg, tol_err, src_max_chg
      real(wp), intent(in) :: vel_underflow
      real(wp), intent(in) :: h_sd(NZL), u_sd(NZL), v_sd(NZL)
      real(wp), intent(in) :: t_sd(NZL), s_sd(NZL)
      real(wp), intent(in) :: idz_s(NZL), idz_int_s(NZLI)
      real(wp), intent(in) :: hint_s(NZLI), il2_s(NZLI)
      real(wp), intent(out) :: kappa_avg_sd(NZLI), tke_avg_sd(NZLI)

      ! Per-thread work arrays (fixed size, surface-down).
      real(wp) :: u_c(NZL), v_c(NZL), t_c(NZL), s_c(NZL)
      real(wp) :: dbuoy_t(NZLI), dbuoy_s(NZLI)
      real(wp) :: e1(NZLI)
      real(wp) :: kappa(NZLI), k_q(NZLI), kappa_avg(NZLI), tke_avg(NZLI)
      real(wp) :: n2(NZLI), s2(NZLI), tke(NZLI)
      real(wp) :: ksrc(NZLI), tkedec(NZLI)
      real(wp) :: aq(NZL), dq(NZLI), cq(NZLI)
      real(wp) :: dk(NZLI), ck(NZLI), ild2(NZLI)
      real(wp) :: cqsav(NZLI)
      real(wp) :: u_ps(NZL), v_ps(NZL), t_ps(NZL), s_ps(NZL), c1_ps(NZLI)
      real(wp) :: n2p(NZLI), s2p(NZLI), n2c(NZLI), s2c(NZLI)
      real(wp) :: kappa_out(NZLI), kq_tmp(NZLI)
      real(wp) :: tke_pred(NZLI), kappa_pred(NZLI), kappa_mid(NZLI)
      real(wp) :: tke_fin(NZLI), kappa_pred2(NZLI)
      real(wp) :: local_src_avg(NZLI), kappa_src(NZLI), local_src(NZLI)

      integer :: k, kk, io, ii
      real(wp) :: k0dt, tke_min, c_n2, c_s2, ilambda2
      real(wp) :: ome_l, eden1_l, eden2_l, i_eden_l
      real(wp) :: a1n, a1c, b1_l, d1_l, bd1_l, base_l, b1ns, b1in
      integer :: ks_src, ke_src, ks_kap, ke_kap
      real(wp) :: dsv_dt_k, dsv_ds_k, t_int, s_int, p_int, dpres
      real(wp) :: n2v, dt_rem, dt_now, dt_wt
      integer :: ks_ps, ke_ps, ks_mid, ke_mid
      logical :: no_mixing

      k0dt = dt*kappa_0
      tke_min = max(tke_bg, 1.0e-20_wp)
      c_n2 = c_n*c_n
      c_s2 = c_s*c_s
      ilambda2 = 1.0_wp/(lambda*lambda)

      ! ---- Background-diffusion pre-step (kappa_0) ----
      if (nz == 1) then
         b1_l = 1.0_wp/(h_sd(1) + k0dt*idz_int_s(2))
         u_c(1) = b1_l*h_sd(1)*u_sd(1)
         v_c(1) = b1_l*h_sd(1)*v_sd(1)
         t_c(1) = t_sd(1)
         s_c(1) = s_sd(1)
      else
         a1n = k0dt*idz_int_s(2)
         b1_l = 1.0_wp/(h_sd(1) + a1n)
         cqsav(2) = a1n*b1_l
         d1_l = h_sd(1)*b1_l
         u_c(1) = b1_l*h_sd(1)*u_sd(1)
         v_c(1) = b1_l*h_sd(1)*v_sd(1)
         t_c(1) = b1_l*h_sd(1)*t_sd(1)
         s_c(1) = b1_l*h_sd(1)*s_sd(1)
         do k = 2, nz - 1
            a1c = a1n
            a1n = k0dt*idz_int_s(k + 1)
            bd1_l = h_sd(k) + d1_l*a1c
            b1_l = 1.0_wp/(bd1_l + a1n)
            u_c(k) = b1_l*(h_sd(k)*u_sd(k) + a1c*u_c(k - 1))
            v_c(k) = b1_l*(h_sd(k)*v_sd(k) + a1c*v_c(k - 1))
            t_c(k) = b1_l*(h_sd(k)*t_sd(k) + a1c*t_c(k - 1))
            s_c(k) = b1_l*(h_sd(k)*s_sd(k) + a1c*s_c(k - 1))
            cqsav(k + 1) = a1n*b1_l
            d1_l = bd1_l*b1_l
         end do
         a1c = a1n
         base_l = h_sd(nz) + d1_l*a1c
         b1ns = 1.0_wp/(base_l + k0dt*idz_int_s(nz + 1))
         b1in = 1.0_wp/base_l
         u_c(nz) = b1ns*(h_sd(nz)*u_sd(nz) + a1c*u_c(nz - 1))
         v_c(nz) = b1ns*(h_sd(nz)*v_sd(nz) + a1c*v_c(nz - 1))
         t_c(nz) = b1in*(h_sd(nz)*t_sd(nz) + a1c*t_c(nz - 1))
         s_c(nz) = b1in*(h_sd(nz)*s_sd(nz) + a1c*s_c(nz - 1))
         cqsav(nz + 1) = 0.0_wp
         do k = nz - 1, 1, -1
            u_c(k) = u_c(k) + cqsav(k + 1)*u_c(k + 1)
            v_c(k) = v_c(k) + cqsav(k + 1)*v_c(k + 1)
            t_c(k) = t_c(k) + cqsav(k + 1)*t_c(k + 1)
            s_c(k) = s_c(k) + cqsav(k + 1)*s_c(k + 1)
         end do
      end if

      ! ---- Frozen interface buoyancy derivatives (design 5.1) ----
      ! Pressure accumulates downward (surface-relative, p(1)=0 -> D10);
      ! dbuoy_dX = -(g/rho0) drho_dX = g*rho0*dSV_dX.
      dbuoy_t(1) = 0.0_wp
      dbuoy_s(1) = 0.0_wp
      dbuoy_t(nz + 1) = 0.0_wp
      dbuoy_s(nz + 1) = 0.0_wp
      p_int = 0.0_wp
      do kk = 2, nz
         dpres = GRAVITY*rho0*h_sd(kk - 1)
         p_int = p_int + dpres
         t_int = 0.5_wp*(t_c(kk - 1) + t_c(kk))
         s_int = 0.5_wp*(s_c(kk - 1) + s_c(kk))
         call eos_specvol_derivs(eos, t_int, s_int, p_int, &
                                 dsv_dt_k, dsv_ds_k)
         dbuoy_t(kk) = GRAVITY*rho0*dsv_dt_k
         dbuoy_s(kk) = GRAVITY*rho0*dsv_ds_k
      end do

      ! ---- Initial N^2, S^2 ----
      n2(1) = 0.0_wp
      n2(nz + 1) = 0.0_wp
      s2(1) = 0.0_wp
      s2(nz + 1) = 0.0_wp
      do kk = 2, nz
         n2v = idz_int_s(kk)*(dbuoy_t(kk)*(t_c(kk - 1) - t_c(kk)) + &
                              dbuoy_s(kk)*(s_c(kk - 1) - s_c(kk)))
         if (n2v < 0.0_wp) n2v = 0.0_wp
         n2(kk) = n2v
         s2(kk) = ((u_c(kk - 1) - u_c(kk))**2 + (v_c(kk - 1) - v_c(kk))**2)* &
                  idz_int_s(kk)**2
      end do

      ! ---- e1 tail recursion ----
      e1(nz + 1) = 0.0_wp
      ome_l = 1.0_wp
      eden2_l = kappa_0*idz_s(nz)
      do kk = nz, 2, -1
         eden1_l = hint_s(kk)*sqrt(c_n2*n2(kk) + c_s2*s2(kk)) + ome_l*eden2_l
         eden2_l = kappa_0*idz_s(kk - 1)
         i_eden_l = 1.0_wp/(eden2_l + eden1_l)
         e1(kk) = eden2_l*i_eden_l
         ome_l = eden1_l*i_eden_l
      end do
      e1(1) = 0.0_wp

      ! ---- Outer-loop init ----
      do kk = 1, nz + 1
         k_q(kk) = 0.0_wp
         kappa_avg(kk) = 0.0_wp
         tke_avg(kk) = 0.0_wp
         kappa(kk) = kappa_seed_in
      end do
      kappa(1) = 0.0_wp
      kappa(nz + 1) = 0.0_wp
      dt_rem = dt
      local_src_avg(1) = 0.0_wp
      local_src_avg(nz + 1) = 0.0_wp
      do kk = 2, nz
         if (hint_s(kk) > 0.0_wp) then
            local_src_avg(kk) = 0.1_wp*k0dt*idz_int_s(kk)/hint_s(kk)
         else
            local_src_avg(kk) = 0.0_wp
         end if
      end do

      ! ---- Adaptive outer substepping ----
      do io = 1, max_substep_it
         ! Step 1: K_src + seed TKE from previous kappa/K_Q.
         ks_src = nz + 2
         ke_src = 0
         ksrc(1) = 0.0_wp
         ksrc(nz + 1) = 0.0_wp
         do kk = 2, nz
            ksrc(kk) = ks_src_func(ri_crit, shearmix_rate, fri_curvature, &
                                   n2(kk), s2(kk))
            if (ksrc(kk) > 0.0_wp) then
               if (ks_src > kk) ks_src = kk
               ke_src = kk
            end if
         end do
         do kk = 1, nz + 1
            kappa_src(kk) = ksrc(kk)
         end do

         do kk = 2, nz
            tkedec(kk) = sqrt(c_n2*n2(kk) + c_s2*s2(kk))
         end do
         tke(1) = tke_bg
         do kk = 2, nz
            if (kappa(kk) > 0.0_wp .and. k_q(kk) > 0.0_wp) then
               tke(kk) = kappa(kk)/k_q(kk)
            else
               tke(kk) = tke_min
            end if
         end do
         tke(nz + 1) = tke_min

         kappa_out = kappa

         if (ks_src > ke_src) then
            do kk = 1, nz + 1
               kappa_out(kk) = 0.0_wp
            end do
            do kk = 2, nz
               ild2(kk) = 0.0_wp
            end do
         else
            kq_tmp = k_q
            call ks_find_kappa_tke(nz, tke_min, f2_val, ri_crit, &
                                   shearmix_rate, fri_curvature, c_n2, c_s2, &
                                   ilambda2, kappa_0, kappa_trunc, tke_bg, &
                                   tol_err, max_inner_it, n2, s2, kappa, &
                                   kq_tmp, idz_s, hint_s, il2_s, e1, tke, &
                                   kappa_out, ksrc, tkedec, aq, dq, cq, dk, &
                                   ck, ild2)
            do kk = 1, nz + 1
               k_q(kk) = kq_tmp(kk)
            end do
         end if

         ! local_src for the adaptive-dt bands (design doc 5.4d): the
         ! K_src term, the kappa_0 background-change term, and the
         ! kappa diffusive-spreading term (added only when it is
         ! positive — a net inflow into the interface).
         local_src(1) = 0.0_wp
         local_src(nz + 1) = 0.0_wp
         do kk = 2, nz
            if (hint_s(kk) > 0.0_wp) then
               local_src(kk) = ksrc(kk) + kappa_0* &
                               ((idz_s(kk - 1) + idz_s(kk))/ &
                                max(hint_s(kk), 1.0e-30_wp) + ild2(kk))
               n2v = idz_s(kk - 1)*(kappa_out(kk - 1) - kappa_out(kk)) + &
                     idz_s(kk)*(kappa_out(kk + 1) - kappa_out(kk))
               if (n2v > 0.0_wp) then
                  local_src(kk) = local_src(kk) + n2v/max(hint_s(kk), 1.0e-30_wp)
               end if
            else
               local_src(kk) = ksrc(kk)
            end if
         end do

         ! Step 2: active range of kappa_out.
         ks_kap = nz + 2
         ke_kap = 0
         do kk = 2, nz
            if (kappa_out(kk) > 0.0_wp) then
               if (ks_kap > kk) ks_kap = kk
               ke_kap = kk
            end if
         end do
         if (ke_kap == nz) kappa_out(nz + 1) = 0.0_wp
         no_mixing = (ke_kap < ks_kap)

         ! Step 3: choose dt_now.
         if (no_mixing .or. io == max_substep_it) then
            dt_now = dt_rem
         else
            dt_now = ks_adaptive_dt(nz, dt_rem, io, max_substep_it, ri_crit, &
                                    shearmix_rate, fri_curvature, src_max_chg, &
                                    tol_err, vel_underflow, dbuoy_t, dbuoy_s, &
                                    h_sd, u_c, v_c, t_c, s_c, kappa_out, &
                                    kappa_src, local_src, local_src_avg, &
                                    ks_kap, ke_kap, idz_int_s)
         end if
         do kk = 2, nz
            local_src_avg(kk) = local_src_avg(kk) + dt_now*local_src(kk)
         end do
         dt_wt = dt_now/dt

         if (no_mixing) then
            ! No source reappears; remaining kappa stays 0.
            do kk = 1, nz + 1
               tke_avg(kk) = tke_avg(kk) + dt_wt*tke(kk)
            end do
            dt_rem = 0.0_wp
         else
            ! Predictor.
            ks_ps = max(ks_kap - 1, 1)
            ke_ps = min(ke_kap, nz)
            call ks_projected_state(nz, dt_now, ks_ps, ke_ps, vel_underflow, &
                                    dbuoy_t, dbuoy_s, h_sd, idz_int_s, u_c, &
                                    v_c, t_c, s_c, kappa_out, u_ps, v_ps, &
                                    t_ps, s_ps, c1_ps, n2p, s2p)
            kq_tmp = k_q
            call ks_find_kappa_tke(nz, tke_min, f2_val, ri_crit, &
                                   shearmix_rate, fri_curvature, c_n2, c_s2, &
                                   ilambda2, kappa_0, kappa_trunc, tke_bg, &
                                   tol_err, max_inner_it, n2p, s2p, kappa_out, &
                                   kq_tmp, idz_s, hint_s, il2_s, e1, tke_pred, &
                                   kappa_pred, ksrc, tkedec, aq, dq, cq, dk, &
                                   ck, ild2)
            do kk = 1, nz + 1
               kappa_mid(kk) = 0.5_wp*(kappa_out(kk) + kappa_pred(kk))
            end do
            ks_mid = nz + 2
            ke_mid = 0
            do kk = 1, nz + 1
               if (kappa_mid(kk) > 0.0_wp) then
                  if (ks_mid > kk) ks_mid = kk
                  ke_mid = kk
               end if
            end do
            ks_ps = max(ks_mid - 1, 1)
            ke_ps = min(ke_mid, nz)

            ! Corrector (real K_Q now).
            call ks_projected_state(nz, dt_now, ks_ps, ke_ps, vel_underflow, &
                                    dbuoy_t, dbuoy_s, h_sd, idz_int_s, u_c, &
                                    v_c, t_c, s_c, kappa_mid, u_ps, v_ps, &
                                    t_ps, s_ps, c1_ps, n2c, s2c)
            call ks_find_kappa_tke(nz, tke_min, f2_val, ri_crit, &
                                   shearmix_rate, fri_curvature, c_n2, c_s2, &
                                   ilambda2, kappa_0, kappa_trunc, tke_bg, &
                                   tol_err, max_inner_it, n2c, s2c, kappa_out, &
                                   k_q, idz_s, hint_s, il2_s, e1, tke_fin, &
                                   kappa_pred2, ksrc, tkedec, aq, dq, cq, dk, &
                                   ck, ild2)
            dt_rem = dt_rem - dt_now
            do kk = 1, nz + 1
               kappa_avg(kk) = kappa_avg(kk) + &
                               dt_wt*0.5_wp*(kappa_out(kk) + kappa_pred2(kk))
               tke_avg(kk) = tke_avg(kk) + &
                             dt_wt*0.5_wp*(tke_pred(kk) + tke_fin(kk))
               kappa(kk) = kappa_pred2(kk)
            end do
            kappa(1) = 0.0_wp
            kappa(nz + 1) = 0.0_wp

            ! Step 5: full-column real-state advance for the next substep.
            if (dt_rem > 0.0_wp) then
               do kk = 1, nz + 1
                  kappa_mid(kk) = 0.5_wp*(kappa_out(kk) + kappa_pred2(kk))
               end do
               call ks_projected_state(nz, dt_now, 1, nz, vel_underflow, &
                                       dbuoy_t, dbuoy_s, h_sd, idz_int_s, u_c, &
                                       v_c, t_c, s_c, kappa_mid, u_ps, v_ps, &
                                       t_ps, s_ps, c1_ps, n2, s2)
               do k = 1, nz
                  u_c(k) = u_ps(k)
                  v_c(k) = v_ps(k)
                  t_c(k) = t_ps(k)
                  s_c(k) = s_ps(k)
               end do
            end if
         end if

         if (dt_rem <= 0.0_wp) exit
      end do

      do kk = 1, nz + 1
         kappa_avg_sd(kk) = kappa_avg(kk)
         tke_avg_sd(kk) = tke_avg(kk)
      end do
      kappa_avg_sd(1) = 0.0_wp
      kappa_avg_sd(nz + 1) = 0.0_wp
   end subroutine ks_solve_column