← back to index

src/parameterizations/vertical/MOM_regularize_layers.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> Provides regularization of layers in isopycnal mode
6module MOM_regularize_layers
7
8use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, CLOCK_ROUTINE
9use MOM_diag_mediator, only : post_data, register_diag_field, safe_alloc_ptr
10use MOM_diag_mediator, only : time_type, diag_ctrl
11use MOM_domains, only : pass_var
12use MOM_error_handler, only : MOM_error, FATAL, WARNING
13use MOM_file_parser, only : get_param, log_version, param_file_type
14use MOM_grid, only : ocean_grid_type
15use MOM_unit_scaling, only : unit_scale_type
16use MOM_variables, only : thermo_var_ptrs
17use MOM_verticalGrid, only : verticalGrid_type
18use MOM_EOS, only : calculate_density, EOS_domain
19
20implicit none ; private
21
22#include <MOM_memory.h>
23
24public regularize_layers, regularize_layers_init
25
26!> This control structure holds parameters used by the MOM_regularize_layers module
27type, public :: regularize_layers_CS ; private
28 logical :: initialized = .false. !< True if this control structure has been initialized.
29 logical :: regularize_surface_layers !< If true, vertically restructure the
30 !! near-surface layers when they have too much
31 !! lateral variations to allow for sensible lateral
32 !! barotropic transports.
33 logical :: reg_sfc_detrain !< If true, allow the buffer layers to detrain into the
34 !! interior as a part of the restructuring when
35 !! regularize_surface_layers is true
36 real :: density_match_tol !< A relative tolerance for how well the densities must match
37 !! with the target densities during detrainment when regularizing
38 !! the near-surface layers [nondim]
39 real :: sufficient_adjustment !< The fraction of the target entrainment of mass to the mixed
40 !! and buffer layers that is enough for one timestep when regularizing
41 !! the near-surface layers [nondim]. No more mass will be sought from
42 !! deeper layers in the interior after this fraction is exceeded.
43 real :: h_def_tol1 !< The value of the relative thickness deficit at
44 !! which to start modifying the structure, 0.5 by
45 !! default (or a thickness ratio of 5.83) [nondim].
46 real :: h_def_tol2 !< The value of the relative thickness deficit at
47 !! which to the structure modification is in full
48 !! force, now 20% of the way from h_def_tol1 to 1 [nondim].
49 real :: h_def_tol3 !< The value of the relative thickness deficit at which to start
50 !! detrainment from the buffer layers to the interior, now 30% of
51 !! the way from h_def_tol1 to 1 [nondim].
52 real :: h_def_tol4 !< The value of the relative thickness deficit at which to do
53 !! detrainment from the buffer layers to the interior at full
54 !! force, now 50% of the way from h_def_tol1 to 1 [nondim].
55 real :: Hmix_min !< The minimum mixed layer thickness [H ~> m or kg m-2].
56 type(time_type), pointer :: Time => NULL() !< A pointer to the ocean model's clock.
57 type(diag_ctrl), pointer :: diag => NULL() !< A structure that is used to
58 !! regulate the timing of diagnostic output.
59 integer :: answer_date !< The vintage of the order of arithmetic and expressions in this module's
60 !! calculations. Values below 20190101 recover the answers from the
61 !! end of 2018, while higher values use updated and more robust forms
62 !! of the same expressions.
63 logical :: debug !< If true, do more thorough checks for debugging purposes.
64
65 integer :: id_def_rat = -1 !< A diagnostic ID
66end type regularize_layers_CS
67
68!>@{ Clock IDs
69!! \todo Should these be global?
70integer :: id_clock_pass
71!>@}
72
73contains
74
75!> This subroutine partially steps the bulk mixed layer model.
76!! The following processes are executed, in the order listed.
770subroutine regularize_layers(h, tv, dt, ea, eb, G, GV, US, CS)
78 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
79 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
80 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
81 intent(inout) :: h !< Layer thicknesses [H ~> m or kg m-2].
82 type(thermo_var_ptrs), intent(inout) :: tv !< A structure containing pointers to any
83 !! available thermodynamic fields. Absent fields
84 !! have NULL pointers.
85 real, intent(in) :: dt !< Time increment [T ~> s].
86 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
87 intent(inout) :: ea !< The amount of fluid moved downward into a
88 !! layer; this should be increased due to mixed
89 !! layer detrainment [H ~> m or kg m-2].
90 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
91 intent(inout) :: eb !< The amount of fluid moved upward into a layer
92 !! this should be increased due to mixed layer
93 !! entrainment [H ~> m or kg m-2].
94 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
95 type(regularize_layers_CS), intent(in) :: CS !< Regularize layer control structure
96
970 if (.not. CS%initialized) call MOM_error(FATAL, "MOM_regularize_layers: "//&
980 "Module must be initialized before it is used.")
99
1000 if (CS%regularize_surface_layers) then
1010 call pass_var(h, G%Domain, clock=id_clock_pass)
1020 call regularize_surface(h, tv, dt, ea, eb, G, GV, US, CS)
103 endif
104
1050end subroutine regularize_layers
106
107!> This subroutine ensures that there is a degree of horizontal smoothness
108!! in the depths of the near-surface interfaces.
1090subroutine regularize_surface(h, tv, dt, ea, eb, G, GV, US, CS)
110 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
111 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
112 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
113 intent(inout) :: h !< Layer thicknesses [H ~> m or kg m-2].
114 type(thermo_var_ptrs), intent(inout) :: tv !< A structure containing pointers to any
115 !! available thermodynamic fields. Absent fields
116 !! have NULL pointers.
117 real, intent(in) :: dt !< Time increment [T ~> s].
118 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
119 intent(inout) :: ea !< The amount of fluid moved downward into a
120 !! layer; this should be increased due to mixed
121 !! layer detrainment [H ~> m or kg m-2].
122 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
123 intent(inout) :: eb !< The amount of fluid moved upward into a layer
124 !! this should be increased due to mixed layer
125 !! entrainment [H ~> m or kg m-2].
126 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
127 type(regularize_layers_CS), intent(in) :: CS !< Regularize layer control structure
128
129 ! Local variables
130 real, dimension(SZIB_(G),SZJ_(G)) :: &
1310 def_rat_u ! The ratio of the thickness deficit to the minimum depth [nondim].
132 real, dimension(SZI_(G),SZJB_(G)) :: &
1330 def_rat_v ! The ratio of the thickness deficit to the minimum depth [nondim].
134 real, dimension(SZI_(G),SZJ_(G)) :: &
1350 def_rat_h ! The ratio of the thickness deficit to the minimum depth [nondim].
136 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: &
1370 e ! The interface depths [H ~> m or kg m-2], positive upward.
138
139 real, dimension(SZI_(G),SZK_(GV)+1) :: &
1400 e_filt, e_2d ! The interface depths [H ~> m or kg m-2], positive upward.
141 real, dimension(SZI_(G),SZK_(GV)) :: &
1420 h_2d, & ! A 2-d version of h [H ~> m or kg m-2].
1430 T_2d, & ! A 2-d version of tv%T [C ~> degC].
1440 S_2d, & ! A 2-d version of tv%S [S ~> ppt].
1450 Rcv, & ! A 2-d version of the coordinate density [R ~> kg m-3].
1460 h_2d_init, & ! The initial value of h_2d [H ~> m or kg m-2].
1470 T_2d_init, & ! The initial value of T_2d [C ~> degC].
1480 S_2d_init, & ! The initial value of S_2d [S ~> ppt].
1490 d_eb, & ! The downward increase across a layer in the entrainment from
150 ! below [H ~> m or kg m-2]. The sign convention is that positive values of
151 ! d_eb correspond to a gain in mass by a layer by upward motion.
1520 d_ea ! The upward increase across a layer in the entrainment from
153 ! above [H ~> m or kg m-2]. The sign convention is that positive values of
154 ! d_ea mean a net gain in mass by a layer from downward motion.
155 real, dimension(SZI_(G)) :: &
1560 p_ref_cv, & ! Reference pressure for the potential density which defines
157 ! the coordinate variable, set to P_Ref [R L2 T-2 ~> Pa].
1580 Rcv_tol, & ! A tolerance, relative to the target density differences
159 ! between layers, for detraining into the interior [nondim].
1600 h_add_tgt, & ! The target for the thickness to add to the mixed layers [H ~> m or kg m-2]
1610 h_add_tot, & ! The net thickness added to the mixed layers [H ~> m or kg m-2]
1620 h_tot1, h_tot2, h_tot3, & ! Debugging diagnostics of total thicknesses [H ~> m or kg m-2]
1630 Th_tot1, Th_tot2, Th_tot3, & ! Debugging diagnostics of integrated temperatures [C H ~> degC m or degC kg m-2]
1640 Sh_tot1, Sh_tot2, Sh_tot3 ! Debugging diagnostics of integrated salinities [S H ~> ppt m or ppt kg m-2]
165 real, dimension(SZK_(GV)) :: &
1660 h_prev_1d ! The previous thicknesses [H ~> m or kg m-2].
167 real :: I_dtol ! The inverse of the tolerance changes [nondim].
168 real :: I_dtol34 ! The inverse of the tolerance changes [nondim].
169 real :: e_e, e_w, e_n, e_s ! Temporary interface heights [H ~> m or kg m-2].
170 real :: wt ! The weight of the filtered interfaces in setting the targets [nondim].
171 real :: scale ! A scaling factor [nondim].
172 real :: h_neglect ! A thickness that is so small it is usually lost
173 ! in roundoff and can be neglected [H ~> m or kg m-2].
174 real, dimension(SZK_(GV)+1) :: &
1750 int_flux, & ! Mass flux across the interfaces [H ~> m or kg m-2]
1760 int_Tflux, & ! Temperature flux across the interfaces [C H ~> degC m or degC kg m-2]
1770 int_Sflux ! Salinity flux across the interfaces [S H ~> ppt m or ppt kg m-2]
178 real :: h_add ! The thickness to add to the layers above an interface [H ~> m or kg m-2]
179 real :: h_det_tot ! The total thickness detrained by the mixed layers [H ~> m or kg m-2]
180 real :: max_def_rat ! The maximum value of the ratio of the thickness deficit to the minimum depth [nondim]
181 real :: Rcv_min_det ! The lightest coordinate density that can detrain into a layer [R ~> kg m-3]
182 real :: Rcv_max_det ! The densest coordinate density that can detrain into a layer [R ~> kg m-3]
183
184 real :: int_top, int_bot ! The interface depths above and below a layer [H ~> m or kg m-2], positive upward.
185 real :: h_predicted ! An updated thickness [H ~> m or kg m-2]
186 real :: h_prev ! The previous thickness [H ~> m or kg m-2]
187 real :: h_deficit ! The difference between the layer thickness and the value estimated from the
188 ! filtered interface depths [H ~> m or kg m-2]
189
1900 logical :: cols_left, ent_any, more_ent_i(SZI_(G)), ent_i(SZI_(G))
1910 logical :: det_any, det_i(SZI_(G))
1920 logical :: do_j(SZJ_(G)), do_i(SZI_(G))
193 logical :: debug = .false.
194 logical :: fatal_error
195 character(len=256) :: mesg ! Message for error messages.
196 integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state
197 integer :: i, j, k, is, ie, js, je, nz, nkmb, nkml, k1, k2, k3, ks, nz_filt, kmax_d_ea
198
1990 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
200
2010 if (.not. CS%initialized) call MOM_error(FATAL, "MOM_regularize_layers: "//&
2020 "Module must be initialized before it is used.")
203
2040 if (GV%nkml<1) return
2050 nkmb = GV%nk_rho_varies ; nkml = GV%nkml
2060 if (.not.associated(tv%eqn_of_state)) call MOM_error(FATAL, &
207 "MOM_regularize_layers: This module now requires the use of temperature and "//&
2080 "an equation of state.")
209
2100 h_neglect = GV%H_subroundoff
2110 debug = (debug .or. CS%debug)
212
2130 I_dtol = 1.0 / max(CS%h_def_tol2 - CS%h_def_tol1, 1e-40)
2140 I_dtol34 = 1.0 / max(CS%h_def_tol4 - CS%h_def_tol3, 1e-40)
215
2160 p_ref_cv(:) = tv%P_Ref
2170 EOSdom(:) = EOS_domain(G%HI)
218
2190 do j=js-1,je+1 ; do i=is-1,ie+1
2200 e(i,j,1) = 0.0
221 enddo ; enddo
2220 do K=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1
2230 e(i,j,K+1) = e(i,j,K) - h(i,j,k)
224 enddo ; enddo ; enddo
225
2260 call find_deficit_ratios(e, def_rat_u, def_rat_v, G, GV, CS, h)
227
228 ! Determine which columns are problematic
2290 do j=js,je ; do_j(j) = .false. ; enddo
2300 do j=js,je ; do i=is,ie
231 def_rat_h(i,j) = max(def_rat_u(I-1,j), def_rat_u(I,j), &
2320 def_rat_v(i,J-1), def_rat_v(i,J))
2330 if (def_rat_h(i,j) > CS%h_def_tol1) do_j(j) = .true.
234 enddo ; enddo
235
236 ! Now restructure the layers.
237 !$OMP parallel do default(private) shared(is,ie,js,je,nz,do_j,def_rat_h,CS,nkmb,G,GV,US, &
238 !$OMP e,I_dtol,h,tv,debug,h_neglect,p_ref_cv,ea, &
239 !$OMP eb,nkml,EOSdom)
2400 do j=js,je ; if (do_j(j)) then
241
2420 do k=1,nz ; do i=is,ie ; d_ea(i,k) = 0.0 ; d_eb(i,k) = 0.0 ; enddo ; enddo
2430 kmax_d_ea = 0
244
2450 max_def_rat = 0.0
2460 do i=is,ie
2470 do_i(i) = def_rat_h(i,j) > CS%h_def_tol1
2480 if (def_rat_h(i,j) > max_def_rat) max_def_rat = def_rat_h(i,j)
249 enddo
2500 nz_filt = nkmb+1 ; if (max_def_rat > CS%h_def_tol3) nz_filt = nz+1
251
252 ! Find a 2-D 1-2-1 filtered version of e to target. Area weights are
253 ! deliberately omitted here. This is slightly more complicated than a
254 ! simple filter so that the effects of topography are eliminated.
2550 do K=1,nz_filt ; do i=is,ie ; if (do_i(i)) then
2560 if (G%mask2dCu(I,j) <= 0.0) then ; e_e = e(i,j,K) ; else
257 e_e = max(e(i+1,j,K) + min(e(i,j,K) - e(i+1,j,nz+1), 0.0), &
2580 e(i,j,nz+1) + (nz+1-k)*GV%Angstrom_H)
259
260 endif
2610 if (G%mask2dCu(I-1,j) <= 0.0) then ; e_w = e(i,j,K) ; else
262 e_w = max(e(i-1,j,K) + min(e(i,j,K) - e(i-1,j,nz+1), 0.0), &
2630 e(i,j,nz+1) + (nz+1-k)*GV%Angstrom_H)
264 endif
2650 if (G%mask2dCv(i,J) <= 0.0) then ; e_n = e(i,j,K) ; else
266 e_n = max(e(i,j+1,K) + min(e(i,j,K) - e(i,j+1,nz+1), 0.0), &
2670 e(i,j,nz+1) + (nz+1-k)*GV%Angstrom_H)
268 endif
2690 if (G%mask2dCv(i,J-1) <= 0.0) then ; e_s = e(i,j,K) ; else
270 e_s = max(e(i,j-1,K) + min(e(i,j,K) - e(i,j-1,nz+1), 0.0), &
2710 e(i,j,nz+1) + (nz+1-k)*GV%Angstrom_H)
272 endif
273
2740 wt = max(0.0, min(1.0, I_dtol*(def_rat_h(i,j)-CS%h_def_tol1)))
275
276 e_filt(i,k) = (1.0 - 0.5*wt) * e(i,j,K) + &
2770 wt * 0.125 * ((e_e + e_w) + (e_n + e_s))
2780 e_2d(i,k) = e(i,j,K)
279 endif ; enddo ; enddo
2800 do k=1,nz ; do i=is,ie
2810 h_2d(i,k) = h(i,j,k)
2820 T_2d(i,k) = tv%T(i,j,k) ; S_2d(i,k) = tv%S(i,j,k)
283 enddo ; enddo
284
2850 if (debug) then
2860 do k=1,nz ; do i=is,ie ; if (do_i(i)) then
2870 h_2d_init(i,k) = h(i,j,k)
2880 T_2d_init(i,k) = tv%T(i,j,k) ; S_2d_init(i,k) = tv%S(i,j,k)
289 endif ; enddo ; enddo
290 endif
291
292 ! First, try to entrain from the interior.
2930 ent_any = .false.
2940 do i=is,ie
2950 more_ent_i(i) = .false. ; ent_i(i) = .false.
2960 h_add_tgt(i) = 0.0 ; h_add_tot(i) = 0.0
2970 if (do_i(i) .and. (e_2d(i,nkmb+1) > e_filt(i,nkmb+1))) then
2980 more_ent_i(i) = .true. ; ent_i(i) = .true. ; ent_any = .true.
2990 h_add_tgt(i) = e_2d(i,nkmb+1) - e_filt(i,nkmb+1)
300 endif
301 enddo
302
3030 if (ent_any) then
3040 do k=nkmb+1,nz
3050 cols_left = .false.
3060 do i=is,ie ; if (more_ent_i(i)) then
3070 if (h_2d(i,k) - GV%Angstrom_H > h_neglect) then
3080 if (e_2d(i,nkmb+1)-e_filt(i,nkmb+1) > h_2d(i,k) - GV%Angstrom_H) then
3090 h_add = h_2d(i,k) - GV%Angstrom_H
3100 h_2d(i,k) = GV%Angstrom_H
3110 e_2d(i,nkmb+1) = e_2d(i,nkmb+1) - h_add
312 else
3130 h_add = e_2d(i,nkmb+1) - e_filt(i,nkmb+1)
3140 h_2d(i,k) = h_2d(i,k) - h_add
3150 if (CS%answer_date < 20190101) then
3160 e_2d(i,nkmb+1) = e_2d(i,nkmb+1) - h_add
317 else
3180 e_2d(i,nkmb+1) = e_filt(i,nkmb+1)
319 endif
320 endif
3210 d_eb(i,k-1) = d_eb(i,k-1) + h_add
3220 h_add_tot(i) = h_add_tot(i) + h_add
3230 h_prev = h_2d(i,nkmb)
3240 h_2d(i,nkmb) = h_2d(i,nkmb) + h_add
325
3260 T_2d(i,nkmb) = (h_prev*T_2d(i,nkmb) + h_add*T_2d(i,k)) / h_2d(i,nkmb)
3270 S_2d(i,nkmb) = (h_prev*S_2d(i,nkmb) + h_add*S_2d(i,k)) / h_2d(i,nkmb)
328
3290 if ((e_2d(i,nkmb+1) <= e_filt(i,nkmb+1)) .or. &
330 (h_add_tot(i) > CS%sufficient_adjustment*h_add_tgt(i))) then
3310 more_ent_i(i) = .false.
332 else
3330 cols_left = .true.
334 endif
335 else
3360 cols_left = .true.
337 endif
338 endif ; enddo
3390 if (.not.cols_left) exit
340 enddo
341
3420 ks = min(k-1,nz-1)
3430 do k=ks,nkmb,-1 ; do i=is,ie ; if (ent_i(i)) then
3440 d_eb(i,k) = d_eb(i,k) + d_eb(i,k+1)
345 endif ; enddo ; enddo
346 endif ! ent_any
347
348 ! This is where code to detrain to the interior will go.
349 ! The buffer layers can only detrain water into layers when the buffer
350 ! layer potential density is between (c*Rlay(k-1) + (1-c)*Rlay(k)) and
351 ! (c*Rlay(k+1) + (1-c)*Rlay(k)), where 0.5 <= c < 1.0.
352 ! Do not detrain if the 2-layer deficit ratio is not significant.
353 ! Detrainment must be able to come from all mixed and buffer layers.
354 ! All water is moved out of the buffer layers below before moving from
355 ! a shallower layer (characteristics do not cross).
3560 det_any = .false.
3570 if ((max_def_rat > CS%h_def_tol3) .and. (CS%reg_sfc_detrain)) then
3580 do i=is,ie
3590 det_i(i) = .false. ; Rcv_tol(i) = 0.0
3600 if (do_i(i) .and. (e_2d(i,nkmb+1) < e_filt(i,nkmb+1)) .and. &
3610 (def_rat_h(i,j) > CS%h_def_tol3)) then
3620 det_i(i) = .true. ; det_any = .true.
363 ! The CS%density_match_tol default value of 0.6 gives 20% overlap in acceptable densities.
3640 Rcv_tol(i) = CS%density_match_tol * min((def_rat_h(i,j) - CS%h_def_tol3), 1.0)
365 endif
366 enddo
367 endif
3680 if (det_any) then
3690 do k=1,nkmb
3700 call calculate_density(T_2d(:,k), S_2d(:,k), p_ref_cv, Rcv(:,k), tv%eqn_of_state, EOSdom)
371 enddo
372
3730 do i=is,ie ; if (det_i(i)) then
3740 k1 = nkmb ; k2 = nz
3750 h_det_tot = 0.0
3760 do ! This loop is terminated by exits.
3770 if (k1 <= 1) exit
3780 if (k2 <= nkmb) exit
3790 Rcv_min_det = (GV%Rlay(k2) + Rcv_tol(i)*(GV%Rlay(k2-1)-GV%Rlay(k2)))
3800 if (k2 < nz) then
3810 Rcv_max_det = (GV%Rlay(k2) + Rcv_tol(i)*(GV%Rlay(k2+1)-GV%Rlay(k2)))
382 else
3830 Rcv_max_det = (GV%Rlay(nz) + Rcv_tol(i)*(GV%Rlay(nz)-GV%Rlay(nz-1)))
384 endif
3850 if (Rcv(i,k1) > Rcv_max_det) &
3860 exit ! All shallower interior layers are too light for detrainment.
387
3880 h_deficit = (e_filt(i,k2)-e_filt(i,k2+1)) - h_2d(i,k2)
389 if ((e_filt(i,k2) > e_2d(i,k1+1)) .and. (h_deficit > 0.0) .and. &
3900 (Rcv(i,k1) < Rcv_max_det) .and. (Rcv(i,k1) > Rcv_min_det)) then
391 ! Detrainment will occur.
3920 h_add = min(e_filt(i,k2) - e_2d(i,k2), h_deficit )
3930 if (h_add < h_2d(i,k1)) then
394 ! Only part of layer k1 detrains.
3950 if (h_add > 0.0) then
3960 h_prev = h_2d(i,k2)
3970 h_2d(i,k2) = h_2d(i,k2) + h_add
3980 e_2d(i,k2) = e_2d(i,k2+1) + h_2d(i,k2)
3990 d_ea(i,k2) = d_ea(i,k2) + h_add
4000 kmax_d_ea = max(kmax_d_ea, k2)
401 ! This is upwind. It should perhaps be higher order...
4020 T_2d(i,k2) = (h_prev*T_2d(i,k2) + h_add*T_2d(i,k1)) / h_2d(i,k2)
4030 S_2d(i,k2) = (h_prev*S_2d(i,k2) + h_add*S_2d(i,k1)) / h_2d(i,k2)
4040 h_det_tot = h_det_tot + h_add
405
4060 h_2d(i,k1) = h_2d(i,k1) - h_add
4070 do k3=k1,nkmb ; e_2d(i,k3+1) = e_2d(i,k3) - h_2d(i,k3) ; enddo
4080 do k3=k1+1,nkmb ; d_ea(i,k3) = d_ea(i,k3) + h_add ; enddo
409 else
4100 if (h_add < 0.0) &
4110 call MOM_error(FATAL, "h_add is negative. Some logic is wrong.")
4120 h_add = 0.0 ! This usually should not happen...
413 endif
414
415 ! Move up to the next target layer.
4160 k2 = k2-1
4170 if (k2>nkmb+1) e_2d(i,k2) = e_2d(i,k2) + h_det_tot
418 else
4190 h_add = h_2d(i,k1)
4200 h_prev = h_2d(i,k2)
4210 h_2d(i,k2) = h_2d(i,k2) + h_add
4220 e_2d(i,k2) = e_2d(i,k2+1) + h_2d(i,k2)
4230 d_ea(i,k2) = d_ea(i,k2) + h_add
4240 kmax_d_ea = max(kmax_d_ea, k2)
4250 T_2d(i,k2) = (h_prev*T_2d(i,k2) + h_add*T_2d(i,k1)) / h_2d(i,k2)
4260 S_2d(i,k2) = (h_prev*S_2d(i,k2) + h_add*S_2d(i,k1)) / h_2d(i,k2)
4270 h_det_tot = h_det_tot + h_add
428
4290 h_2d(i,k1) = 0.0
4300 do k3=k1,nkmb ; e_2d(i,k3+1) = e_2d(i,k3) - h_2d(i,k3) ; enddo
4310 do k3=k1+1,nkmb ; d_ea(i,k3) = d_ea(i,k3) + h_add ; enddo
432
433 ! Move up to the next source layer.
4340 k1 = k1-1
435 endif
436
437 else
438 ! Move up to the next target layer.
4390 k2 = k2-1
4400 if (k2>nkmb+1) e_2d(i,k2) = e_2d(i,k2) + h_det_tot
441 endif
442
443 enddo ! exit terminated loop.
444 endif ; enddo
4450 do k=kmax_d_ea-1,nkmb+1,-1 ; do i=is,ie ; if (det_i(i)) then
4460 d_ea(i,k) = d_ea(i,k) + d_ea(i,k+1)
447 endif ; enddo ; enddo
448 endif ! Detrainment to the interior.
4490 if (debug) then
4500 do i=is,ie ; h_tot3(i) = 0.0 ; Th_tot3(i) = 0.0 ; Sh_tot3(i) = 0.0 ; enddo
4510 do k=1,nz ; do i=is,ie ; if (do_i(i)) then
4520 h_tot3(i) = h_tot3(i) + h_2d(i,k)
4530 Th_tot3(i) = Th_tot3(i) + h_2d(i,k) * T_2d(i,k)
4540 Sh_tot3(i) = Sh_tot3(i) + h_2d(i,k) * S_2d(i,k)
455 endif ; enddo ; enddo
456 endif
457
4580 do i=is,ie ; if (do_i(i)) then
459 ! Rescale the interface targets so the depth at the bottom of the deepest
460 ! buffer layer matches.
4610 scale = e_2d(i,nkmb+1) / e_filt(i,nkmb+1)
4620 do k=2,nkmb+1 ; e_filt(i,k) = e_filt(i,k) * scale ; enddo
463
464 ! Ensure that layer 1 only has water from layers 1 to nkml and rescale
465 ! the remaining layer thicknesses if necessary.
4660 if (e_filt(i,2) < e_2d(i,nkml)) then
467 scale = (e_2d(i,nkml) - e_filt(i,nkmb+1)) / &
4680 ((e_filt(i,2) - e_filt(i,nkmb+1)) + h_neglect)
4690 do k=3,nkmb
4700 e_filt(i,k) = e_filt(i,nkmb+1) + scale * (e_filt(i,k) - e_filt(i,nkmb+1))
471 enddo
4720 e_filt(i,2) = e_2d(i,nkml)
473 endif
474
475 ! Map the water back into the layers. There are not mixed or buffer layers that are exceedingly
476 ! small compared to the others, so the code here is less prone to roundoff than elsewhere in MOM6.
4770 k1 = 1 ; k2 = 1
4780 int_top = 0.0
4790 do k=1,nkmb+1
4800 int_flux(k) = 0.0
4810 int_Tflux(k) = 0.0 ; int_Sflux(k) = 0.0
482 enddo
4830 do k=1,2*nkmb
4840 int_bot = max(e_2d(i,k1+1),e_filt(i,k2+1))
4850 h_add = int_top - int_bot
486
4870 if (k2 > k1) then
4880 do k3=k1+1,k2
4890 d_ea(i,k3) = d_ea(i,k3) + h_add
4900 int_flux(k3) = int_flux(k3) + h_add
4910 int_Tflux(k3) = int_Tflux(k3) + h_add*T_2d(i,k1)
4920 int_Sflux(k3) = int_Sflux(k3) + h_add*S_2d(i,k1)
493 enddo
4940 elseif (k1 > k2) then
4950 do k3=k2,k1-1
4960 d_eb(i,k3) = d_eb(i,k3) + h_add
4970 int_flux(k3+1) = int_flux(k3+1) - h_add
4980 int_Tflux(k3+1) = int_Tflux(k3+1) - h_add*T_2d(i,k1)
4990 int_Sflux(k3+1) = int_Sflux(k3+1) - h_add*S_2d(i,k1)
500 enddo
501 endif
502
5030 if (int_bot <= e_filt(i,k2+1)) then
504 ! Increment the target layer.
5050 k2 = k2 + 1
5060 elseif (int_bot <= e_2d(i,k1+1)) then
507 ! Increment the source layer.
5080 k1 = k1 + 1
509 else
510 call MOM_error(FATAL, &
5110 "Regularize_surface: Could not increment target or source.")
512 endif
5130 if ((k1 > nkmb) .or. (k2 > nkmb)) exit
5140 int_top = int_bot
515 enddo
5160 if (k2 < nkmb) &
5170 call MOM_error(FATAL, "Regularize_surface: Did not assign fluid to layer nkmb.")
518
519 ! Note that movement of water across the base of the bottommost buffer
520 ! layer has already been dealt with separately.
5210 do k=1,nkmb ; h_prev_1d(k) = h_2d(i,k) ; enddo
5220 h_2d(i,1) = h_2d(i,1) - int_flux(2)
5230 do k=2,nkmb-1
5240 h_2d(i,k) = h_2d(i,k) + (int_flux(k) - int_flux(k+1))
525 enddo
526 ! Note that movement of water across the base of the bottommost buffer
527 ! layer has already been dealt with separately.
5280 h_2d(i,nkmb) = h_2d(i,nkmb) + int_flux(nkmb)
529
5300 T_2d(i,1) = (T_2d(i,1)*h_prev_1d(1) - int_Tflux(2)) / h_2d(i,1)
5310 S_2d(i,1) = (S_2d(i,1)*h_prev_1d(1) - int_Sflux(2)) / h_2d(i,1)
5320 do k=2,nkmb-1
5330 T_2d(i,k) = (T_2d(i,k)*h_prev_1d(k) + (int_Tflux(k) - int_Tflux(k+1))) / h_2d(i,k)
5340 S_2d(i,k) = (S_2d(i,k)*h_prev_1d(k) + (int_Sflux(k) - int_Sflux(k+1))) / h_2d(i,k)
535 enddo
5360 T_2d(i,nkmb) = (T_2d(i,nkmb)*h_prev_1d(nkmb) + int_Tflux(nkmb) ) / h_2d(i,nkmb)
5370 S_2d(i,nkmb) = (S_2d(i,nkmb)*h_prev_1d(nkmb) + int_Sflux(nkmb) ) / h_2d(i,nkmb)
538
539 endif ; enddo ! i-loop
540
541 ! Copy the interior thicknesses and other fields back to the 3-d arrays.
5420 do k=1,nz ; do i=is,ie ; if (do_i(i)) then
5430 h(i,j,k) = h_2d(i,k)
5440 tv%T(i,j,k) = T_2d(i,k) ; tv%S(i,j,k) = S_2d(i,k)
5450 ea(i,j,k) = ea(i,j,k) + d_ea(i,k)
5460 eb(i,j,k) = eb(i,j,k) + d_eb(i,k)
547 endif ; enddo ; enddo
548
5490 if (debug) then
5500 do i=is,ie ; h_tot1(i) = 0.0 ; Th_tot1(i) = 0.0 ; Sh_tot1(i) = 0.0 ; enddo
5510 do i=is,ie ; h_tot2(i) = 0.0 ; Th_tot2(i) = 0.0 ; Sh_tot2(i) = 0.0 ; enddo
552
5530 do k=1,nz ; do i=is,ie ; if (do_i(i)) then
5540 h_tot1(i) = h_tot1(i) + h_2d_init(i,k)
5550 h_tot2(i) = h_tot2(i) + h(i,j,k)
556
5570 Th_tot1(i) = Th_tot1(i) + h_2d_init(i,k) * T_2d_init(i,k)
5580 Th_tot2(i) = Th_tot2(i) + h(i,j,k) * tv%T(i,j,k)
5590 Sh_tot1(i) = Sh_tot1(i) + h_2d_init(i,k) * S_2d_init(i,k)
5600 Sh_tot2(i) = Sh_tot2(i) + h(i,j,k) * tv%S(i,j,k)
5610 if (h(i,j,k) < 0.0) &
5620 call MOM_error(FATAL,"regularize_surface: Negative thicknesses.")
5630 if (k==1) then ; h_predicted = h_2d_init(i,k) + (d_eb(i,k) - d_ea(i,k+1))
5640 elseif (k==nz) then ; h_predicted = h_2d_init(i,k) + (d_ea(i,k) - d_eb(i,k-1))
565 else
566 h_predicted = h_2d_init(i,k) + ((d_ea(i,k) - d_eb(i,k-1)) + &
5670 (d_eb(i,k) - d_ea(i,k+1)))
568 endif
5690 if (abs(h(i,j,k) - h_predicted) > MAX(1e-9*abs(h_predicted),GV%Angstrom_H)) &
5700 call MOM_error(FATAL, "regularize_surface: d_ea mismatch.")
571 endif ; enddo ; enddo
5720 do i=is,ie ; if (do_i(i)) then
5730 fatal_error = .false.
5740 if (abs(h_tot1(i) - h_tot2(i)) > 1e-12*h_tot1(i)) then
575 write(mesg,'(ES11.4," became ",ES11.4," diff ",ES11.4)') &
5760 h_tot1(i), h_tot2(i), (h_tot1(i) - h_tot2(i))
577 call MOM_error(WARNING, "regularize_surface: Mass non-conservation. "//&
5780 trim(mesg), .true.)
5790 fatal_error = .true.
580 endif
5810 if (abs(Th_tot1(i) - Th_tot2(i)) > 1e-12*abs(Th_tot1(i) + 10.0*US%degC_to_C*h_tot1(i))) then
582 write(mesg,'(ES11.4," became ",ES11.4," diff ",ES11.4," int diff ",ES11.4)') &
5830 Th_tot1(i), Th_tot2(i), (Th_tot1(i) - Th_tot2(i)), (Th_tot1(i) - Th_tot3(i))
584 call MOM_error(WARNING, "regularize_surface: Heat non-conservation. "//&
5850 trim(mesg), .true.)
5860 fatal_error = .true.
587 endif
5880 if (abs(Sh_tot1(i) - Sh_tot2(i)) > 1e-12*abs(Sh_tot1(i) + 10.0*US%ppt_to_S*h_tot1(i))) then
589 write(mesg,'(ES11.4," became ",ES11.4," diff ",ES11.4," int diff ",ES11.4)') &
5900 Sh_tot1(i), Sh_tot2(i), (Sh_tot1(i) - Sh_tot2(i)), (Sh_tot1(i) - Sh_tot3(i))
591 call MOM_error(WARNING, "regularize_surface: Salinity non-conservation. "//&
5920 trim(mesg), .true.)
5930 fatal_error = .true.
594 endif
5950 if (fatal_error) then
5960 write(mesg,'("Error at lat/lon ",2(ES11.4))') G%geoLatT(i,j), G%geoLonT(i,j)
597 call MOM_error(FATAL, "regularize_surface: Terminating with fatal error. "//&
5980 trim(mesg))
599 endif
600 endif ; enddo
601 endif
602
603 endif ; enddo ! j-loop.
604
6050 if (CS%id_def_rat > 0) call post_data(CS%id_def_rat, def_rat_h, CS%diag)
606
607end subroutine regularize_surface
608
609!> This subroutine determines the amount by which the harmonic mean
610!! thickness at velocity points differ from the arithmetic means, relative to
611!! the arithmetic means, after eliminating thickness variations that are
612!! solely due to topography and aggregating all interior layers into one.
6130subroutine find_deficit_ratios(e, def_rat_u, def_rat_v, G, GV, CS, h)
614 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
615 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
616 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), &
617 intent(in) :: e !< Interface depths [H ~> m or kg m-2]
618 real, dimension(SZIB_(G),SZJ_(G)), &
619 intent(out) :: def_rat_u !< The thickness deficit ratio at u points,
620 !! [nondim].
621 real, dimension(SZI_(G),SZJB_(G)), &
622 intent(out) :: def_rat_v !< The thickness deficit ratio at v points,
623 !! [nondim].
624 type(regularize_layers_CS), intent(in) :: CS !< Regularize layer control structure
625 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
626 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2].
627
628 ! Local variables
629 real, dimension(SZIB_(G),SZJ_(G)) :: &
6300 h_def_u, & ! The vertically summed thickness deficits at u-points [H ~> m or kg m-2].
6310 h_norm_u ! The vertically summed arithmetic mean thickness by which
632 ! h_def_u is normalized [H ~> m or kg m-2].
633 real, dimension(SZI_(G),SZJB_(G)) :: &
6340 h_def_v, & ! The vertically summed thickness deficits at v-points [H ~> m or kg m-2].
6350 h_norm_v ! The vertically summed arithmetic mean thickness by which
636 ! h_def_v is normalized [H ~> m or kg m-2].
637 real :: h_neglect ! A thickness that is so small it is usually lost
638 ! in roundoff and can be neglected [H ~> m or kg m-2].
639 real :: Hmix_min ! A local copy of CS%Hmix_min [H ~> m or kg m-2].
640 real :: h1, h2 ! Temporary thicknesses [H ~> m or kg m-2].
641 integer :: i, j, k, is, ie, js, je, nz, nkmb
642
6430 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
6440 nkmb = GV%nk_rho_varies
6450 h_neglect = GV%H_subroundoff
6460 Hmix_min = CS%Hmix_min
647
648 ! Determine which zonal faces are problematic.
6490 do j=js,je ; do I=is-1,ie
650 ! Aggregate all water below the mixed and buffer layers for the purposes of
651 ! this diagnostic.
6520 h1 = e(i,j,nkmb+1)-e(i,j,nz+1) ; h2 = e(i+1,j,nkmb+1)-e(i+1,j,nz+1)
6530 if (e(i,j,nz+1) < e(i+1,j,nz+1)) then
6540 if (h1 > h2) h1 = max(e(i,j,nkmb+1)-e(i+1,j,nz+1), h2)
6550 elseif (e(i+1,j,nz+1) < e(i,j,nz+1)) then
6560 if (h2 > h1) h2 = max(e(i+1,j,nkmb+1)-e(i,j,nz+1), h1)
657 endif
6580 h_def_u(I,j) = 0.5*(h1-h2)**2 / ((h1 + h2) + h_neglect)
6590 h_norm_u(I,j) = 0.5*(h1+h2)
660 enddo ; enddo
6610 do k=1,nkmb ; do j=js,je ; do I=is-1,ie
6620 h1 = h(i,j,k) ; h2 = h(i+1,j,k)
663 ! Thickness deficits can not arise simply because a layer's bottom is bounded
664 ! by the bathymetry.
6650 if (e(i,j,K+1) < e(i+1,j,nz+1)) then
6660 if (h1 > h2) h1 = max(e(i,j,K)-e(i+1,j,nz+1), h2)
6670 elseif (e(i+1,j,K+1) < e(i,j,nz+1)) then
6680 if (h2 > h1) h2 = max(e(i+1,j,K)-e(i,j,nz+1), h1)
669 endif
6700 h_def_u(I,j) = h_def_u(I,j) + 0.5*(h1-h2)**2 / ((h1 + h2) + h_neglect)
6710 h_norm_u(I,j) = h_norm_u(I,j) + 0.5*(h1+h2)
672 enddo ; enddo ; enddo
6730 do j=js,je ; do I=is-1,ie
674 def_rat_u(I,j) = G%mask2dCu(I,j) * h_def_u(I,j) / &
6750 (max(Hmix_min, h_norm_u(I,j)) + h_neglect)
676 enddo ; enddo
677
678 ! Determine which meridional faces are problematic.
6790 do J=js-1,je ; do i=is,ie
680 ! Aggregate all water below the mixed and buffer layers for the purposes of
681 ! this diagnostic.
6820 h1 = e(i,j,nkmb+1)-e(i,j,nz+1) ; h2 = e(i,j+1,nkmb+1)-e(i,j+1,nz+1)
6830 if (e(i,j,nz+1) < e(i,j+1,nz+1)) then
6840 if (h1 > h2) h1 = max(e(i,j,nkmb+1)-e(i,j+1,nz+1), h2)
6850 elseif (e(i,j+1,nz+1) < e(i,j,nz+1)) then
6860 if (h2 > h1) h2 = max(e(i,j+1,nkmb+1)-e(i,j,nz+1), h1)
687 endif
6880 h_def_v(i,J) = 0.5*(h1-h2)**2 / ((h1 + h2) + h_neglect)
6890 h_norm_v(i,J) = 0.5*(h1+h2)
690 enddo ; enddo
6910 do k=1,nkmb ; do J=js-1,je ; do i=is,ie
6920 h1 = h(i,j,k) ; h2 = h(i,j+1,k)
693 ! Thickness deficits can not arise simply because a layer's bottom is bounded
694 ! by the bathymetry.
6950 if (e(i,j,K+1) < e(i,j+1,nz+1)) then
6960 if (h1 > h2) h1 = max(e(i,j,K)-e(i,j+1,nz+1), h2)
6970 elseif (e(i,j+1,K+1) < e(i,j,nz+1)) then
6980 if (h2 > h1) h2 = max(e(i,j+1,K)-e(i,j,nz+1), h1)
699 endif
7000 h_def_v(i,J) = h_def_v(i,J) + 0.5*(h1-h2)**2 / ((h1 + h2) + h_neglect)
7010 h_norm_v(i,J) = h_norm_v(i,J) + 0.5*(h1+h2)
702 enddo ; enddo ; enddo
7030 do J=js-1,je ; do i=is,ie
704 def_rat_v(i,J) = G%mask2dCv(i,J) * h_def_v(i,J) / &
7050 (max(Hmix_min, h_norm_v(i,J)) + h_neglect)
706 enddo ; enddo
707
7080end subroutine find_deficit_ratios
709
710!> Initializes the regularize_layers control structure
7111subroutine regularize_layers_init(Time, G, GV, param_file, diag, CS)
712 type(time_type), target, intent(in) :: Time !< The current model time.
713 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
714 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
715 type(param_file_type), intent(in) :: param_file !< A structure to parse for
716 !! run-time parameters.
717 type(diag_ctrl), target, intent(inout) :: diag !< A structure that is used to regulate
718 !! diagnostic output.
719 type(regularize_layers_CS), intent(inout) :: CS !< Regularize layer control structure
720
721# include "version_variable.h"
722 character(len=40) :: mdl = "MOM_regularize_layers" ! This module's name.
723 integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags
724 logical :: just_read
725 integer :: isd, ied, jsd, jed
7261 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed
727
7281 CS%initialized = .true.
729
7301 CS%diag => diag
7311 CS%Time => Time
732
733! Set default, read and log parameters
734 call get_param(param_file, mdl, "REGULARIZE_SURFACE_LAYERS", CS%regularize_surface_layers, &
7351 default=.false., do_not_log=.true.)
7361 call log_version(param_file, mdl, version, "", all_default=.not.CS%regularize_surface_layers)
737 call get_param(param_file, mdl, "REGULARIZE_SURFACE_LAYERS", CS%regularize_surface_layers, &
738 "If defined, vertically restructure the near-surface "//&
739 "layers when they have too much lateral variations to "//&
740 "allow for sensible lateral barotropic transports.", &
7411 default=.false.)
7421 just_read = .not.CS%regularize_surface_layers
7431 if (CS%regularize_surface_layers) then
744 call get_param(param_file, mdl, "REGULARIZE_SURFACE_DETRAIN", CS%reg_sfc_detrain, &
745 "If true, allow the buffer layers to detrain into the "//&
746 "interior as a part of the restructuring when "//&
7470 "REGULARIZE_SURFACE_LAYERS is true.", default=.true., do_not_log=just_read)
748 call get_param(param_file, mdl, "REG_SFC_DENSE_MATCH_TOLERANCE", CS%density_match_tol, &
749 "A relative tolerance for how well the densities must match with the target "//&
750 "densities during detrainment when regularizing the near-surface layers. The "//&
751 "default of 0.6 gives 20% overlaps in density", &
7520 units="nondim", default=0.6, do_not_log=just_read)
753 call get_param(param_file, mdl, "REG_SFC_SUFFICIENT_ADJ", CS%sufficient_adjustment, &
754 "The fraction of the target entrainment of mass to the mixed and buffer layers "//&
755 "that is enough for one timestep when regularizing the near-surface layers. "//&
756 "No more mass will be sought from deeper layers in the interior after this "//&
7570 "fraction is exceeded.", units="nondim", default=0.6, do_not_log=just_read)
758 call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
759 "This sets the default value for the various _ANSWER_DATE parameters.", &
7600 default=99991231, do_not_log=just_read)
761 call get_param(param_file, mdl, "REGULARIZE_LAYERS_ANSWER_DATE", CS%answer_date, &
762 "The vintage of the order of arithmetic and expressions in the regularize "//&
763 "layers calculations. Values below 20190101 recover the answers from the "//&
764 "end of 2018, while higher values use updated and more robust forms of the "//&
765 "same expressions.", &
7660 default=default_answer_date, do_not_log=.not.GV%Boussinesq)
7670 if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701)
768 endif
769
770 call get_param(param_file, mdl, "HMIX_MIN", CS%Hmix_min, &
771 "The minimum mixed layer depth if the mixed layer depth is determined "//&
7721 "dynamically.", units="m", default=0.0, scale=GV%m_to_H, do_not_log=just_read)
773 call get_param(param_file, mdl, "REG_SFC_DEFICIT_TOLERANCE", CS%h_def_tol1, &
774 "The value of the relative thickness deficit at which "//&
775 "to start modifying the layer structure when "//&
776 "REGULARIZE_SURFACE_LAYERS is true.", units="nondim", &
7771 default=0.5, do_not_log=just_read)
7781 CS%h_def_tol2 = 0.2 + 0.8*CS%h_def_tol1
7791 CS%h_def_tol3 = 0.3 + 0.7*CS%h_def_tol1
7801 CS%h_def_tol4 = 0.5 + 0.5*CS%h_def_tol1
781
7821 call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.)
783! if (.not. CS%debug) &
784! call get_param(param_file, mdl, "DEBUG_CONSERVATION", CS%debug, &
785! "If true, monitor conservation and extrema.", default=.false., do_not_log=just_read)
786
7871 if (.not.CS%regularize_surface_layers) return
788
789 CS%id_def_rat = register_diag_field('ocean_model', 'deficit_ratio', diag%axesT1, &
7900 Time, 'Max face thickness deficit ratio', 'nondim')
791
7920 id_clock_pass = cpu_clock_id('(Ocean regularize_layers halo updates)', grain=CLOCK_ROUTINE)
793
794end subroutine regularize_layers_init
795
7960end module MOM_regularize_layers