@@ -65,6 +65,7 @@ module cvmix_kpp
6565 integer , parameter :: LANGMUIR_ENTRAINMENT_LWF16 = 1
6666 integer , parameter :: LANGMUIR_ENTRAINMENT_LF17 = 2
6767 integer , parameter :: LANGMUIR_ENTRAINMENT_RWHGK16 = 3
68+ integer , parameter :: ML_DIFFUSIVITY_SHAPE = 5 ! ML_Diffusivity
6869
6970! !PUBLIC MEMBER FUNCTIONS:
7071
@@ -214,6 +215,9 @@ module cvmix_kpp
214215 real (cvmix_r8 ) :: ER_Cs ! Entrainment Rule TKE Stokes production weight [nondim]
215216 real (cvmix_r8 ) :: ER_Cu ! Entrainment Rule TKE shear production weight [nondim]
216217
218+ logical :: ML_diffusivity ! uses the Machine Learned equations to set diffusivity
219+ ! from Sane et al. (2025) https://doi.org/10.31219/osf.io/uab7v_v2
220+
217221 end type cvmix_kpp_params_type
218222
219223! EOP
@@ -232,7 +236,8 @@ subroutine cvmix_init_kpp(ri_crit, minOBLdepth, maxOBLdepth, minVtsqr, &
232236 old_vals , lEkman , lStokesMOST , lMonOb , lnoDGat1 , &
233237 lenhanced_diff , lnonzero_surf_nonlocal , &
234238 Langmuir_mixing_str , Langmuir_entrainment_str , &
235- l_LMD_ws , ER_Cb , ER_Cs , ER_Cu , CVmix_kpp_params_user )
239+ l_LMD_ws , ML_diffusivity , &
240+ ER_Cb , ER_Cs , ER_Cu , CVmix_kpp_params_user )
236241
237242! !DESCRIPTION:
238243! Initialization routine for KPP mixing.
@@ -267,6 +272,7 @@ subroutine cvmix_init_kpp(ri_crit, minOBLdepth, maxOBLdepth, minVtsqr, &
267272 lenhanced_diff, &
268273 lnonzero_surf_nonlocal, &
269274 l_LMD_ws
275+ logical , optional , intent (in ) :: ML_diffusivity
270276
271277! !OUTPUT PARAMETERS:
272278 type (cvmix_kpp_params_type), intent (inout ), target , optional :: &
@@ -459,6 +465,8 @@ subroutine cvmix_init_kpp(ri_crit, minOBLdepth, maxOBLdepth, minVtsqr, &
459465 case (' ParabolicNonLocal' )
460466 call cvmix_put_kpp(' MatchTechnique' , CVMIX_KPP_PARABOLIC_NONLOCAL, &
461467 CVmix_kpp_params_user)
468+ case (' ML_Diffusivity_Shape' ) ! ML_Diffusivity shape function
469+ call cvmix_put_kpp(' MatchTechnique' , ML_DIFFUSIVITY_SHAPE, CVmix_kpp_params_user)
462470 case DEFAULT
463471 print * , " ERROR: " , trim (MatchTechnique), " is not a valid choice " , &
464472 " for MatchTechnique!"
@@ -469,6 +477,16 @@ subroutine cvmix_init_kpp(ri_crit, minOBLdepth, maxOBLdepth, minVtsqr, &
469477 CVmix_kpp_params_user)
470478 end if
471479
480+ ! --- Add this block to for ML_diffusivity ---
481+ if (present (ML_diffusivity)) then ! ML_diffusivity
482+ call cvmix_put_kpp(' ML_diffusivity' , ML_diffusivity, CVmix_kpp_params_user)
483+ if (ML_diffusivity) then
484+ call cvmix_put_kpp(' MatchTechnique' , ML_DIFFUSIVITY_SHAPE, CVmix_kpp_params_user)
485+ end if
486+ else
487+ call cvmix_put_kpp(' ML_diffusivity' , .false. , CVmix_kpp_params_user)
488+ end if
489+
472490 if (present (old_vals)) then
473491 select case (trim (old_vals))
474492 case (" overwrite" )
@@ -678,7 +696,7 @@ subroutine cvmix_coeffs_kpp_wrap(CVmix_vars, CVmix_kpp_params_user)
678696 CVmix_vars% SurfaceBuoyancyForcing, &
679697 nlev, max_nlev, &
680698 CVmix_vars% LangmuirEnhancementFactor, &
681- CVmix_vars% StokesMostXi, &
699+ CVmix_vars% StokesMostXi, CVmix_vars % Coriolis, &
682700 CVmix_kpp_params_user)
683701
684702 call cvmix_update_wrap(CVmix_kpp_params_in% handle_old_vals, max_nlev, &
@@ -702,7 +720,7 @@ subroutine cvmix_coeffs_kpp_low(Mdiff_out, Tdiff_out, Sdiff_out, zw, zt, &
702720 old_Mdiff , old_Tdiff , old_Sdiff , OBL_depth , &
703721 kOBL_depth , Tnonlocal , Snonlocal , surf_fric ,&
704722 surf_buoy , nlev , max_nlev , Langmuir_EFactor ,&
705- StokesXI ,CVmix_kpp_params_user )
723+ StokesXI ,Coriolis , CVmix_kpp_params_user )
706724
707725! !DESCRIPTION:
708726! Computes vertical diffusion coefficients for the KPP boundary layer mixing
@@ -727,6 +745,7 @@ subroutine cvmix_coeffs_kpp_low(Mdiff_out, Tdiff_out, Sdiff_out, zw, zt, &
727745 ! Langmuir enhancement factor
728746 real (cvmix_r8 ), intent (in ), optional :: Langmuir_EFactor
729747 real (cvmix_r8 ), intent (in ), optional :: StokesXI
748+ real (cvmix_r8 ), intent (in ), optional :: Coriolis ! required for ML_diffusivity
730749! !INPUT/OUTPUT PARAMETERS:
731750 real (cvmix_r8 ), dimension (max_nlev+1 ), intent (inout ) :: Mdiff_out, &
732751 Tdiff_out, &
@@ -794,6 +813,14 @@ subroutine cvmix_coeffs_kpp_low(Mdiff_out, Tdiff_out, Sdiff_out, zw, zt, &
794813 ! Parameters for Stokes_MOST
795814 real (cvmix_r8 ) :: Gcomposite, Hsigma, sigh, T_NLenhance , S_NLenhance , XIone
796815
816+ ! Parameters for the machine learning part, ML_diffusivity
817+ real (cvmix_r8 ) :: sigma_max ! sigma coordinate of location of maximum diffusivity
818+ real (cvmix_r8 ) :: g_sigma ! shape function at a sigma coordinate.
819+ real (cvmix_r8 ) :: L_h ! Non-dimensional L_h = B*OBL_depth/u_*^3
820+ real (cvmix_r8 ) :: E_h ! Non-dimensional E_h = OBL_depth * Coriolis /u_*
821+ real (cvmix_r8 ) :: F_inter_func ! Stands for F_intermediate_function,
822+ ! Non-dimensional intermediate function used to calculate sigma_max
823+
797824 ! Constant from params
798825 integer :: interp_type2, MatchTechnique
799826
@@ -1208,11 +1235,46 @@ subroutine cvmix_coeffs_kpp_low(Mdiff_out, Tdiff_out, Sdiff_out, zw, zt, &
12081235 call cvmix_kpp_compute_turbulent_scales(sigma, OBL_depth, surf_buoy, &
12091236 surf_fric, XIone, w_m, w_s, &
12101237 CVmix_kpp_params_user)
1238+
1239+ if (CVmix_kpp_params_in% ML_diffusivity) then ! ML_diffusivity
1240+ ! !! Evaluating L_h and E_h
1241+ L_h = - surf_buoy * OBL_depth / (surf_fric ** 3.0 ) ! OBL / Monin-Obukhov-Depth
1242+ E_h = OBL_depth * Coriolis / surf_fric ! OBL / Ekman Depth i.e. hf/u*
1243+ F_inter_func = ( cvmix_one / ( 0.0712 + 0.4380 * exp (- 1.0 * (2.6821 * L_h)) ) ) + 1.5845
1244+ sigma_max = (F_inter_func * E_h) / ( 1.7908 * (F_inter_func * E_h) + 0.6904 )
1245+ ! !! capping sigma_max between 0.1 and 0.7
1246+ sigma_max = min ( max (sigma_max, 0.1 ), 0.7 )
1247+ endif
12111248 do kw= 2 ,kwup
12121249 ! (3b) Evaluate G(sigma) at each cell interface
1213- MshapeAtS = cvmix_math_evaluate_cubic(Mshape, sigma(kw))
1214- TshapeAtS = cvmix_math_evaluate_cubic(Tshape, sigma(kw))
1215- SshapeAtS = cvmix_math_evaluate_cubic(Sshape, sigma(kw))
1250+
1251+ if (CVmix_kpp_params_in% ML_diffusivity) then ! ML_diffusivity
1252+
1253+ ! ML-diffusivity modification:
1254+ if (sigma(kw) .le. sigma_max ) then ! ML based shape function
1255+ ! quadratic part above sigma_max
1256+ g_sigma = (2.0 * sigma(kw)/ sigma_max) - (sigma(kw)/ sigma_max)** 2.0
1257+ g_sigma = g_sigma * (4.0 / 27.0 ) ! reduces the amplitude from 1 to 4/27 to match G(\sigma) of KPP
1258+ MshapeAtS = g_sigma
1259+ TshapeAtS = g_sigma
1260+ SshapeAtS = g_sigma
1261+ else
1262+ ! cubic part below sigma_max
1263+ g_sigma = 2.0 * ((sigma(kw) - sigma_max)/ (cvmix_one - sigma_max))** 3.0 &
1264+ - 3.0 * ((sigma(kw) - sigma_max)/ (cvmix_one - sigma_max))** 2.0 &
1265+ + cvmix_one
1266+ g_sigma = g_sigma * (4.0 / 27.0 ) ! reduces the amplitude from 1 to 4/27 to match G(\sigma) of KPP
1267+ MshapeAtS = g_sigma
1268+ TshapeAtS = g_sigma
1269+ SshapeAtS = g_sigma
1270+ end if
1271+
1272+ else
1273+
1274+ MshapeAtS = cvmix_math_evaluate_cubic(Mshape, sigma(kw))
1275+ TshapeAtS = cvmix_math_evaluate_cubic(Tshape, sigma(kw))
1276+ SshapeAtS = cvmix_math_evaluate_cubic(Sshape, sigma(kw))
1277+ end if
12161278 ! The RWHGK16 Langmuir uses the shape function to shape the
12171279 ! enhancement to the mixing coefficient.
12181280 ShapeNoMatchAtS = cvmix_math_evaluate_cubic(NMshape, sigma(kw))
@@ -1483,6 +1545,8 @@ subroutine cvmix_put_kpp_logical(varname, val, CVmix_kpp_params_user)
14831545 CVmix_kpp_params_out% lenhanced_diff = val
14841546 case (' l_LMD_ws' )
14851547 CVmix_kpp_params_out% l_LMD_ws = val
1548+ case (' ML_diffusivity' )
1549+ CVmix_kpp_params_out% ML_diffusivity = val
14861550 case DEFAULT
14871551 print * , " ERROR: " , trim (varname), " is not a boolean variable!"
14881552 stop 1
0 commit comments