← back to index

src/core/MOM_interface_heights.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!> Functions for calculating interface heights, including free surface height.
6module MOM_interface_heights
7
8use MOM_density_integrals, only : int_specific_vol_dp, avg_specific_vol, int_density_dz
9use MOM_debugging, only : hchksum
10use MOM_error_handler, only : MOM_error, FATAL
11use MOM_EOS, only : calculate_density, average_specific_vol, EOS_type, EOS_domain
12use MOM_file_parser, only : log_version
13use MOM_grid, only : ocean_grid_type
14use MOM_unit_scaling, only : unit_scale_type
15use MOM_variables, only : thermo_var_ptrs
16use MOM_verticalGrid, only : verticalGrid_type
17
18implicit none ; private
19
20#include <MOM_memory.h>
21
22public find_eta, find_dz_for_eta, dz_to_thickness, thickness_to_dz, dz_to_thickness_simple
23public calc_derived_thermo
24public convert_MLD_to_ML_thickness
25public find_rho_bottom, find_col_avg_SpV, find_col_mass
26
27!> Calculates the heights of the free surface or all interfaces from layer thicknesses.
28interface find_eta
29 module procedure find_eta_2d, find_eta_3d
30end interface find_eta
31
32!> Calculates layer thickness in thickness units from geometric distance between the
33!! interfaces around that layer in height units.
34interface dz_to_thickness
35 module procedure dz_to_thickness_tv, dz_to_thickness_EoS
36end interface dz_to_thickness
37
38!> Converts layer thickness in thickness units into the vertical distance between the
39!! interfaces around a layer in height units.
40interface thickness_to_dz
41 module procedure thickness_to_dz_3d, thickness_to_dz_jslice
42end interface thickness_to_dz
43
44contains
45
46!> Calculates the change in height across layers, using the appropriate form for
47!! consistency with the calculation of the pressure gradient forces.
480subroutine find_dz_for_eta(h, tv, G, GV, US, dz_lay, halo_size)
49 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
50 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
51 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
52 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
53 type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
54 !! thermodynamic variables.
55 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(out) :: dz_lay !< Height change across layers [Z ~> m]
56 integer, optional, intent(in) :: halo_size !< width of halo points on
57 !! which to calculate eta.
58
59 ! Local variables
600 real :: p(SZI_(G),SZJ_(G),SZK_(GV)+1) ! Hydrostatic pressure at each interface [R L2 T-2 ~> Pa]
610 real :: dz_geo(SZI_(G),SZJ_(G)) ! The change in geopotential height across a layer [L2 T-2 ~> m2 s-2]
62 real :: SpV_lay_conv(SZK_(GV)) ! The prescribed layer specific volume times a conversion factor from
63 ! the units of thickness to layer mass [Z H-1 ~> nondim or m3 kg-1]
64 real :: I_gEarth ! The inverse of the gravitational acceleration times the
65 ! rescaling factor derived from eta_to_m [T2 Z L-2 ~> s2 m-1]
66 integer :: i, j, k, isv, iev, jsv, jev, nz, halo
67
680 halo = 0 ; if (present(halo_size)) halo = max(0,halo_size)
69
700 isv = G%isc-halo ; iev = G%iec+halo ; jsv = G%jsc-halo ; jev = G%jec+halo
710 nz = GV%ke
72
730 if ((isv<G%isd) .or. (iev>G%ied) .or. (jsv<G%jsd) .or. (jev>G%jed)) &
740 call MOM_error(FATAL,"find_dz_for_eta called with an overly large halo_size.")
75
760 if (GV%Boussinesq) then
770 do k=1,nz ; do j=jsv,jev ; do i=isv,iev
780 dz_lay(i,j,K) = h(i,j,k)*GV%H_to_Z
79 enddo ; enddo ; enddo
800 elseif (associated(tv%eqn_of_state)) then
810 I_gEarth = 1.0 / GV%g_Earth
82 !$OMP parallel do default(shared)
830 do j=jsv,jev
840 if (associated(tv%p_surf)) then
850 do i=isv,iev ; p(i,j,1) = tv%p_surf(i,j) ; enddo
86 else
870 do i=isv,iev ; p(i,j,1) = 0.0 ; enddo
88 endif
890 do k=1,nz ; do i=isv,iev
900 p(i,j,K+1) = p(i,j,K) + GV%g_Earth*GV%H_to_RZ*h(i,j,k)
91 enddo ; enddo
92 enddo
93 !$OMP parallel do default(shared) private(dz_geo)
940 do k=1,nz
95 call int_specific_vol_dp(tv%T(:,:,k), tv%S(:,:,k), p(:,:,K), p(:,:,K+1), &
960 0.0, G%HI, tv%eqn_of_state, US, dz_geo, halo_size=halo)
970 do j=jsv,jev ; do i=isv,iev
980 dz_lay(i,j,K) = I_gEarth * dz_geo(i,j)
99 enddo ; enddo
100 enddo
101 else ! non-Boussinesq but with no equation of state
1020 do k=1,nz ; do j=jsv,jev ; do i=isv,iev
1030 dz_lay(i,j,K) = GV%H_to_RZ*h(i,j,k) / GV%Rlay(k)
104 enddo ; enddo ; enddo
105 ! This would be faster but could change answers.
106 ! do k=1,nz ; SpV_lay_conv(k) = GV%H_to_RZ / GV%Rlay(k) ; enddo
107 ! do k=1,nz ; do j=jsv,jev ; do i=isv,iev
108 ! dz_lay(i,j,K) = h(i,j,k) * SpV_lay_conv(k)
109 ! enddo ; enddo ; enddo
110 endif
111
112 ! To find eta, do the following:
113 ! do j=jsv,jev ; do i=isv,iev ; eta(i,j,nz+1) = -(G%bathyT(i,j) + dZ_ref) ; enddo ; enddo
114 ! do k=nz,1,-1 ; do j=jsv,jev ; do i=isv,iev
115 ! eta(i,j,K) = eta(i,j,K+1) + dz_lay(i,j,K)
116 ! enddo ; enddo ; enddo
117
1180end subroutine find_dz_for_eta
119
120!> Calculates the heights of all interfaces between layers, using the appropriate
121!! form for consistency with the calculation of the pressure gradient forces.
122!! Additionally, these height may be dilated for consistency with the
123!! corresponding time-average quantity from the barotropic calculation.
1240subroutine find_eta_3d(h, tv, G, GV, US, eta, eta_bt, halo_size, dZref)
125 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
126 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
127 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
128 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
129 type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
130 !! thermodynamic variables.
131 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: eta !< layer interface heights [Z ~> m]
132 real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic variable
133 !! that gives the "correct" free surface height (Boussinesq) or total water
134 !! column mass per unit area (non-Boussinesq). This is used to dilate the layer
135 !! thicknesses when calculating interface heights [H ~> m or kg m-2].
136 !! In Boussinesq mode, eta_bt and G%bathyT use the same reference height.
137 integer, optional, intent(in) :: halo_size !< width of halo points on
138 !! which to calculate eta.
139 real, optional, intent(in) :: dZref !< The difference in the
140 !! reference height between G%bathyT and eta [Z ~> m]. The default is 0.
141
142 ! Local variables
14350 real :: dz_lay(SZI_(G),SZJ_(G),SZK_(GV)) ! The change in height across a layer [Z ~> m]
14450 real :: dilate(SZI_(G)) ! A non-dimensional dilation factor [nondim]
14550 real :: htot(SZI_(G)) ! total thickness [H ~> m or kg m-2]
146 real :: dZ_ref ! The difference in the reference height between G%bathyT and eta [Z ~> m].
147 ! dZ_ref is 0 unless the optional argument dZref is present.
148 integer :: i, j, k, isv, iev, jsv, jev, nz, halo
149
15025 halo = 0 ; if (present(halo_size)) halo = max(0,halo_size)
151
15225 isv = G%isc-halo ; iev = G%iec+halo ; jsv = G%jsc-halo ; jev = G%jec+halo
15325 nz = GV%ke
154
15525 if ((isv<G%isd) .or. (iev>G%ied) .or. (jsv<G%jsd) .or. (jev>G%jed)) &
1560 call MOM_error(FATAL,"find_eta called with an overly large halo_size.")
157
15825 dZ_ref = 0.0 ; if (present(dZref)) dZ_ref = dZref
159
16025 if (GV%Boussinesq) then
16125 do concurrent (j=jsv:jev, i=isv:iev)
162196297 eta(i,j,nz+1) = -(G%bathyT(i,j) + dZ_ref)
163 enddo
164
16525 do concurrent (j=jsv:jev, i=isv:iev)
16614686297 do k=nz,1,-1
16714683200 eta(i,j,K) = eta(i,j,K+1) + h(i,j,k)*GV%H_to_Z
168 enddo
169 enddo
170
17125 if (present(eta_bt)) then
172 ! Dilate the water column to agree with the free surface height
173 ! that is used for the dynamics.
174 !$omp target update from(eta)
1750 do j=jsv,jev
1760 do i=isv,iev
177 dilate(i) = (eta_bt(i,j)*GV%H_to_Z + G%bathyT(i,j)) / &
1780 (eta(i,j,1) + (G%bathyT(i,j) + dZ_ref))
179 enddo
1800 do k=1,nz ; do i=isv,iev
181 eta(i,j,K) = dilate(i) * (eta(i,j,K) + (G%bathyT(i,j) + dZ_ref)) - &
1820 (G%bathyT(i,j) + dZ_ref)
183 enddo ; enddo
184 enddo
185 !$omp target update to(eta)
186 endif
187 else
188 !$omp target update from(eta)
1890 call find_dz_for_eta(h, tv, G, GV, US, dz_lay, halo_size)
190
1910 do j=jsv,jev
1920 do i=isv,iev ; eta(i,j,nz+1) = -(G%bathyT(i,j) + dZ_ref) ; enddo
1930 do k=nz,1,-1 ; do i=isv,iev
1940 eta(i,j,K) = eta(i,j,K+1) + dz_lay(i,j,k)
195 enddo ; enddo
196 enddo
197
1980 if (present(eta_bt)) then
199 ! Dilate the water column to agree with the free surface height
200 ! from the time-averaged barotropic solution.
2010 do j=jsv,jev
2020 do i=isv,iev ; htot(i) = GV%H_subroundoff ; enddo
2030 do k=1,nz ; do i=isv,iev ; htot(i) = htot(i) + h(i,j,k) ; enddo ; enddo
2040 do i=isv,iev ; dilate(i) = eta_bt(i,j) / htot(i) ; enddo
2050 do k=1,nz ; do i=isv,iev
206 eta(i,j,K) = dilate(i) * (eta(i,j,K) + (G%bathyT(i,j) + dZ_ref)) - &
2070 (G%bathyT(i,j) + dZ_ref)
208 enddo ; enddo
209 enddo
210 endif
211 !$omp target update to(eta)
212 endif
21325end subroutine find_eta_3d
214
215!> Calculates the free surface height, using the appropriate form for consistency
216!! with the calculation of the pressure gradient forces. Additionally, the sea
217!! surface height may be adjusted for consistency with the corresponding
218!! time-average quantity from the barotropic calculation.
21925subroutine find_eta_2d(h, tv, G, GV, US, eta, eta_bt, halo_size, dZref)
220 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
221 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
222 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
223 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
224 type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
225 !! thermodynamic variables.
226 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta !< free surface height relative to
227 !! mean sea level (z=0) often [Z ~> m].
228 real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic
229 !! variable that gives the "correct" free surface height (Boussinesq) or total
230 !! water column mass per unit area (non-Boussinesq) [H ~> m or kg m-2].
231 !! In Boussinesq mode, eta_bt and G%bathyT use the same reference height.
232 integer, optional, intent(in) :: halo_size !< width of halo points on
233 !! which to calculate eta.
234 real, optional, intent(in) :: dZref !< The difference in the
235 !! reference height between G%bathyT and eta [Z ~> m]. The default is 0.
236
237 ! Local variables
23850 real :: dz_lay(SZI_(G),SZJ_(G),SZK_(GV)) ! The change in height across a layer [Z ~> m]
23950 real :: htot(SZI_(G)) ! The sum of all layers' thicknesses [H ~> m or kg m-2].
240 real :: dZ_ref ! The difference in the reference height between G%bathyT and eta [Z ~> m].
241 ! dZ_ref is 0 unless the optional argument dZref is present.
242 integer :: i, j, k, is, ie, js, je, nz, halo
243
24425 halo = 0 ; if (present(halo_size)) halo = max(0,halo_size)
24525 is = G%isc-halo ; ie = G%iec+halo ; js = G%jsc-halo ; je = G%jec+halo
24625 nz = GV%ke
247
24825 dZ_ref = 0.0 ; if (present(dZref)) dZ_ref = dZref
249
25025 if (GV%Boussinesq) then
25125 if (present(eta_bt)) then
25225 do concurrent (j=js:je, i=is:ie)
253183025 eta(i,j) = GV%H_to_Z*eta_bt(i,j) - dZ_ref
254 enddo
255 else
2560 do concurrent (j=js:je)
2570 do concurrent (i=is:ie)
2580 eta(i,j) = -G%bathyT(i,j) + dZ_ref
259 enddo
260
2610 do k=1,nz ; do concurrent (i=is:ie)
2620 eta(i,j) = eta(i,j) + h(i,j,k)*GV%H_to_Z
263 enddo ; enddo
264 enddo
265 endif
266 else
267 !$omp target update from(eta)
2680 call find_dz_for_eta(h, tv, G, GV, US, dz_lay, halo_size)
269
2700 do j=js,je
2710 do i=is,ie ; eta(i,j) = -(G%bathyT(i,j) + dZ_ref) ; enddo
2720 do k=1,nz ; do i=is,ie
2730 eta(i,j) = eta(i,j) + dz_lay(i,j,k)
274 enddo ; enddo
275 enddo
276
2770 if (present(eta_bt)) then
278 ! Dilate the water column to agree with the time-averaged column
279 ! mass from the barotropic solution.
2800 do j=js,je
2810 do i=is,ie ; htot(i) = GV%H_subroundoff ; enddo
2820 do k=1,nz ; do i=is,ie ; htot(i) = htot(i) + h(i,j,k) ; enddo ; enddo
2830 do i=is,ie
284 eta(i,j) = (eta_bt(i,j) / htot(i)) * (eta(i,j) + (G%bathyT(i,j) + dZ_ref)) - &
2850 (G%bathyT(i,j) + dZ_ref)
286 enddo
287 enddo
288 !$omp target update to(eta)
289 endif
290 endif
29125end subroutine find_eta_2d
292
293
294!> Calculate derived thermodynamic quantities for re-use later.
2950subroutine calc_derived_thermo(tv, h, G, GV, US, halo, debug)
296 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
297 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
298 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
299 type(thermo_var_ptrs), intent(inout) :: tv !< A structure pointing to various
300 !! thermodynamic variables, some of
301 !! which will be set here.
302 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
303 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2].
304 integer, optional, intent(in) :: halo !< Width of halo within which to
305 !! calculate thicknesses
306 logical, optional, intent(in) :: debug !< If present and true, write debugging checksums
307 ! Local variables
3080 real, dimension(SZI_(G),SZJ_(G)) :: p_t ! Hydrostatic pressure atop a layer [R L2 T-2 ~> Pa]
3090 real, dimension(SZI_(G),SZJ_(G)) :: dp ! Pressure change across a layer [R L2 T-2 ~> Pa]
3100 real, dimension(SZK_(GV)) :: SpV_lay ! The specific volume of each layer when no equation of
311 ! state is used [R-1 ~> m3 kg-1]
312 logical :: do_debug ! If true, write checksums for debugging.
313 integer :: i, j, k, is, ie, js, je, halos, nz
314
3150 do_debug = .false. ; if (present(debug)) do_debug = debug
3160 halos = 0 ; if (present(halo)) halos = max(0,halo)
3170 is = G%isc-halos ; ie = G%iec+halos ; js = G%jsc-halos ; je = G%jec+halos ; nz = GV%ke
318
3190 if (allocated(tv%Spv_avg) .and. associated(tv%eqn_of_state)) then
3200 if (associated(tv%p_surf)) then
3210 do j=js,je ; do i=is,ie ; p_t(i,j) = tv%p_surf(i,j) ; enddo ; enddo
322 else
3230 do j=js,je ; do i=is,ie ; p_t(i,j) = 0.0 ; enddo ; enddo
324 endif
3250 do k=1,nz
3260 do j=js,je ; do i=is,ie
3270 dp(i,j) = GV%g_Earth*GV%H_to_RZ*h(i,j,k)
328 enddo ; enddo
3290 call avg_specific_vol(tv%T(:,:,k), tv%S(:,:,k), p_t, dp, G%HI, tv%eqn_of_state, tv%SpV_avg(:,:,k), halo)
3300 if (k<nz) then ; do j=js,je ; do i=is,ie
3310 p_t(i,j) = p_t(i,j) + dp(i,j)
332 enddo ; enddo ; endif
333 enddo
3340 tv%valid_SpV_halo = halos
335
3360 if (do_debug) then
3370 call hchksum(h, "derived_thermo h", G%HI, haloshift=halos, unscale=GV%H_to_MKS)
3380 if (associated(tv%p_surf)) call hchksum(tv%p_surf, "derived_thermo p_surf", G%HI, &
3390 haloshift=halos, unscale=US%RL2_T2_to_Pa)
3400 call hchksum(tv%T, "derived_thermo T", G%HI, haloshift=halos, unscale=US%C_to_degC)
3410 call hchksum(tv%S, "derived_thermo S", G%HI, haloshift=halos, unscale=US%S_to_ppt)
342 endif
3430 elseif (allocated(tv%Spv_avg)) then
3440 do k=1,nz ; SpV_lay(k) = 1.0 / GV%Rlay(k) ; enddo
3450 do k=1,nz ; do j=js,je ; do i=is,ie
3460 tv%SpV_avg(i,j,k) = SpV_lay(k)
347 enddo ; enddo ; enddo
3480 tv%valid_SpV_halo = halos
349 endif
350
3510end subroutine calc_derived_thermo
352
353
354!> Determine the column average specific volumes.
3550subroutine find_col_avg_SpV(h, SpV_avg, tv, G, GV, US, halo_size)
356 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
357 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
358 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
359 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
360 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
361 real, dimension(SZI_(G),SZJ_(G)), &
362 intent(inout) :: SpV_avg !< Column average specific volume [R-1 ~> m3 kg-1]
363 ! SpV_avg is intent inout to retain excess halo values.
364 type(thermo_var_ptrs), intent(in) :: tv !< Structure containing pointers to any available
365 !! thermodynamic fields.
366 integer, optional, intent(in) :: halo_size !< width of halo points on which to work
367
368 ! Local variables
3690 real :: h_tot(SZI_(G)) ! Sum of the layer thicknesses [H ~> m or kg m-2]
3700 real :: SpV_x_h_tot(SZI_(G)) ! Vertical sum of the layer average specific volume times
371 ! the layer thicknesses [H R-1 ~> m4 kg-1 or m]
372 real :: I_rho ! The inverse of the Boussiensq reference density [R-1 ~> m3 kg-1]
3730 real :: SpV_lay(SZK_(GV)) ! The inverse of the layer target potential densities [R-1 ~> m3 kg-1]
374 character(len=128) :: mesg ! A string for error messages
375 integer :: i, j, k, is, ie, js, je, nz, halo
376
3770 halo = 0 ; if (present(halo_size)) halo = max(0,halo_size)
378
3790 is = G%isc-halo ; ie = G%iec+halo ; js = G%jsc-halo ; je = G%jec+halo
3800 nz = GV%ke
381
3820 if (GV%Boussinesq) then
3830 I_rho = 1.0 / GV%Rho0
3840 do j=js,je ; do i=is,ie
3850 SpV_avg(i,j) = I_rho
386 enddo ; enddo
3870 elseif (.not.allocated(tv%SpV_avg)) then
3880 do k=1,nz ; Spv_lay(k) = 1.0 / GV%Rlay(k) ; enddo
3890 do j=js,je
3900 do i=is,ie ; SpV_x_h_tot(i) = 0.0 ; h_tot(i) = 0.0 ; enddo
3910 do k=1,nz ; do i=is,ie
3920 h_tot(i) = h_tot(i) + max(h(i,j,k), GV%H_subroundoff)
3930 SpV_x_h_tot(i) = SpV_x_h_tot(i) + Spv_lay(k)*max(h(i,j,k), GV%H_subroundoff)
394 enddo ; enddo
3950 do i=is,ie ; SpV_avg(i,j) = SpV_x_h_tot(i) / h_tot(i) ; enddo
396 enddo
397 else
398 ! Check that SpV_avg has been set.
3990 if ((allocated(tv%SpV_avg)) .and. (tv%valid_SpV_halo < halo)) then
4000 if (tv%valid_SpV_halo < 0) then
4010 mesg = "invalid values of SpV_avg."
402 else
403 write(mesg, '("insufficiently large SpV_avg halos of width ", i2, " but ", i2," is needed.")') &
4040 tv%valid_SpV_halo, halo
405 endif
4060 call MOM_error(FATAL, "find_col_avg_SpV called in fully non-Boussinesq mode with "//trim(mesg))
407 endif
408
4090 do j=js,je
4100 do i=is,ie ; SpV_x_h_tot(i) = 0.0 ; h_tot(i) = 0.0 ; enddo
4110 do k=1,nz ; do i=is,ie
4120 h_tot(i) = h_tot(i) + max(h(i,j,k), GV%H_subroundoff)
4130 SpV_x_h_tot(i) = SpV_x_h_tot(i) + tv%SpV_avg(i,j,k)*max(h(i,j,k), GV%H_subroundoff)
414 enddo ; enddo
4150 do i=is,ie ; SpV_avg(i,j) = SpV_x_h_tot(i) / h_tot(i) ; enddo
416 enddo
417 endif
418
4190end subroutine find_col_avg_SpV
420
421!> Calculate the integrated mass of the water column.
4220subroutine find_col_mass(h, tv, G, GV, US, mass, p_bot, p_surf)
423 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
424 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
425 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
426 type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
427 !! thermodynamic variables.
428 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
429 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: mass !< Integrated mass of the water column
430 !! [R Z ~> kg m-2]
431 real, dimension(SZI_(G),SZJ_(G)), optional, intent(out) :: p_bot !< Bottom pressure = g * mass + psurf
432 !! [R L2 T-2 ~> Pa]
433 real, dimension(:,:), optional, pointer :: p_surf !< A pointer to surface pressure
434 !! [R L2 T-2 ~> Pa]
435
436 ! Local variables
437 real :: I_gEarth ! The inverse of GV%g_Earth [T2 Z L-2 ~> s2 m-1]
438 real, dimension(SZI_(G),SZJ_(G)) :: &
4390 z_top, & ! Height of the top of a layer [Z ~> m].
4400 z_bot, & ! Height of the bottom of a layer [Z ~> m].
4410 dp ! Change in hydrostatic pressure across a layer [R L2 T-2 ~> Pa].
442 integer :: i, j, k, is, ie, js, je, isq, ieq, jsq, jeq, nz
443
4440 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
4450 isq = G%iscB ; ieq = G%iecB ; jsq = G%jscB ; jeq = G%jecB
4460 nz = GV%ke
447
4480 do j=js,je ; do i=is,ie ; mass(i,j) = 0.0 ; enddo ; enddo
4490 if (GV%Boussinesq) then
4500 if (associated(tv%eqn_of_state)) then
4510 I_gEarth = 1.0 / GV%g_Earth
4520 do j=jsq,jeq+1 ; do i=isq,ieq+1 ; z_bot(i,j) = 0.0 ; enddo ; enddo
4530 do k=1,nz
454 ! NOTE: int_density_z expects z_top and z_bot values from [ij]sq to [ij]eq+1
4550 do j=jsq,jeq+1 ; do i=isq,ieq+1
4560 z_top(i,j) = z_bot(i,j)
4570 z_bot(i,j) = z_top(i,j) - GV%H_to_Z * h(i,j,k)
458 enddo ; enddo
459 call int_density_dz(tv%T(:,:,k), tv%S(:,:,k), z_top, z_bot, 0.0, GV%Rho0, GV%g_Earth, &
4600 G%HI, tv%eqn_of_state, US, dp)
4610 do j=js,je ; do i=is,ie
4620 mass(i,j) = mass(i,j) + dp(i,j) * I_gEarth
463 enddo ; enddo
464 enddo
465 else
4660 do k=1,nz ; do j=js,je ; do i=is,ie
4670 mass(i,j) = mass(i,j) + (GV%H_to_Z * GV%Rlay(k)) * h(i,j,k)
468 enddo ; enddo ; enddo
469 endif
470 else
4710 do k=1,nz ; do j=js,je ; do i=is,ie
4720 mass(i,j) = mass(i,j) + GV%H_to_RZ * h(i,j,k)
473 enddo ; enddo ; enddo
474 endif
475
4760 if (present(p_bot)) then
4770 do j=js,je ; do i=is,ie
4780 p_bot(i,j) = GV%g_Earth * mass(i,j)
479 enddo ; enddo
4800 if (present(p_surf) .and. associated(p_surf)) then ; do j=js,je ; do i=is,ie
4810 p_bot(i,j) = p_bot(i,j) + p_surf(i,j)
482 enddo ; enddo ; endif
483 endif
484
4850end subroutine find_col_mass
486
487!> Determine the in situ density averaged over a specified distance from the bottom,
488!! calculating it as the inverse of the mass-weighted average specific volume.
489720subroutine find_rho_bottom(G, GV, US, tv, h, dz, pres_int, dz_avg, j, Rho_bot, h_bot, k_bot)
490 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
491 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
492 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
493 type(thermo_var_ptrs), intent(in) :: tv !< Structure containing pointers to any available
494 !! thermodynamic fields.
495 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
496 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
497 real, dimension(SZI_(G),SZK_(GV)), &
498 intent(in) :: dz !< Height change across layers [Z ~> m]
499 real, dimension(SZI_(G),SZK_(GV)+1), &
500 intent(in) :: pres_int !< Pressure at each interface [R L2 T-2 ~> Pa]
501 real, dimension(SZI_(G)), intent(in) :: dz_avg !< The vertical distance over which to average [Z ~> m]
502 integer, intent(in) :: j !< j-index of row to work on
503 real, dimension(SZI_(G)), intent(out) :: Rho_bot !< Near-bottom density [R ~> kg m-3].
504 real, dimension(SZI_(G)), intent(out) :: h_bot !< Bottom boundary layer thickness [H ~> m or kg m-2]
505 integer, dimension(SZI_(G)), intent(out) :: k_bot !< Bottom boundary layer top layer index
506
507 ! Local variables
5081440 real :: hb(SZI_(G)) ! Running sum of the thickness in the bottom boundary layer [H ~> m or kg m-2]
5091440 real :: SpV_h_bot(SZI_(G)) ! Running sum of the specific volume times thickness in the bottom
510 ! boundary layer [H R-1 ~> m4 kg-1 or m]
5111440 real :: dz_bbl_rem(SZI_(G)) ! Vertical extent of the boundary layer that has yet to be accounted
512 ! for [Z ~> m]
5131440 real :: h_bbl_frac(SZI_(G)) ! Thickness of the fractional layer that makes up the top of the
514 ! boundary layer [H ~> m or kg m-2]
5151440 real :: T_bbl(SZI_(G)) ! Temperature of the fractional layer that makes up the top of the
516 ! boundary layer [C ~> degC]
5171440 real :: S_bbl(SZI_(G)) ! Salinity of the fractional layer that makes up the top of the
518 ! boundary layer [S ~> ppt]
5191440 real :: P_bbl(SZI_(G)) ! Pressure the top of the boundary layer [R L2 T-2 ~> Pa]
5201440 real :: dp(SZI_(G)) ! Pressure change across the fractional layer that makes up the top
521 ! of the boundary layer [R L2 T-2 ~> Pa]
5221440 real :: SpV_bbl(SZI_(G)) ! In situ specific volume of the fractional layer that makes up the
523 ! top of the boundary layer [R-1 ~> m3 kg-1]
524 real :: frac_in ! The fraction of a layer that is within the bottom boundary layer [nondim]
5251440 logical :: do_i(SZI_(G)), do_any
526 logical :: use_EOS
527 integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state
528 integer :: i, k, is, ie, nz
529
530720 is = G%isc ; ie = G%iec ; nz = GV%ke
531
532720 use_EOS = associated(tv%T) .and. associated(tv%S) .and. associated(tv%eqn_of_state)
533
534720 if (GV%Boussinesq .or. GV%semi_Boussinesq .or. .not.allocated(tv%SpV_avg)) then
53587120 do i=is,ie
53687120 rho_bot(i) = GV%Rho0
537 enddo
538
539 ! Obtain bottom boundary layer thickness and index of top layer
54087120 do i=is,ie
54186400 hb(i) = 0.0 ; h_bot(i) = 0.0 ; k_bot(i) = nz
54286400 dz_bbl_rem(i) = G%mask2dT(i,j) * max(0.0, dz_avg(i))
54386400 do_i(i) = .true.
54487120 if (G%mask2dT(i,j) <= 0.0) then
54526232 h_bbl_frac(i) = 0.0
54626232 do_i(i) = .false.
547 endif
548 enddo
549
550720 do k=nz,1,-1
551720 do_any = .false.
55287120 do i=is,ie ; if (do_i(i)) then
55360168 if (dz(i,k) < dz_bbl_rem(i)) then
554 ! This layer is fully within the averaging depth.
5550 dz_bbl_rem(i) = dz_bbl_rem(i) - dz(i,k)
5560 hb(i) = hb(i) + h(i,j,k)
5570 k_bot(i) = k
5580 do_any = .true.
559 else
56060168 if (dz(i,k) > 0.0) then
56160168 frac_in = dz_bbl_rem(i) / dz(i,k)
56260168 if (frac_in >= 0.5) k_bot(i) = k ! update bbl top index if >= 50% of layer
563 else
5640 frac_in = 0.0
565 endif
56660168 h_bbl_frac(i) = frac_in * h(i,j,k)
56760168 dz_bbl_rem(i) = 0.0
56860168 do_i(i) = .false.
569 endif
570 endif ; enddo
571720 if (.not.do_any) exit
572 enddo
57387120 do i=is,ie ; if (do_i(i)) then
574 ! The nominal bottom boundary layer is thicker than the water column, but layer 1 is
575 ! already included in the averages. These values are set so that the call to find
576 ! the layer-average specific volume will behave sensibly.
5770 h_bbl_frac(i) = 0.0
578 endif ; enddo
579
58087120 do i=is,ie
58186400 if (hb(i) + h_bbl_frac(i) < GV%H_subroundoff) h_bbl_frac(i) = GV%H_subroundoff
58287120 h_bot(i) = hb(i) + h_bbl_frac(i)
583 enddo
584
585 else
586 ! Check that SpV_avg has been set.
5870 if (tv%valid_SpV_halo < 0) call MOM_error(FATAL, &
5880 "find_rho_bottom called in fully non-Boussinesq mode with invalid values of SpV_avg.")
589
590 ! Set the bottom density to the inverse of the in situ specific volume averaged over the
591 ! specified distance, with care taken to avoid having compressibility lead to an imprint
592 ! of the layer thicknesses on this density.
5930 do i=is,ie
5940 hb(i) = 0.0 ; SpV_h_bot(i) = 0.0 ; h_bot(i) = 0.0 ; k_bot(i) = nz
5950 dz_bbl_rem(i) = G%mask2dT(i,j) * max(0.0, dz_avg(i))
5960 do_i(i) = .true.
5970 if (G%mask2dT(i,j) <= 0.0) then
598 ! Set acceptable values for calling the equation of state over land.
5990 T_bbl(i) = 0.0 ; S_bbl(i) = 0.0 ; dp(i) = 0.0 ; P_bbl(i) = 0.0
6000 SpV_bbl(i) = 1.0 ! This value is arbitrary, provided it is non-zero.
6010 h_bbl_frac(i) = 0.0
6020 do_i(i) = .false.
603 endif
604 enddo
605
6060 do k=nz,1,-1
6070 do_any = .false.
6080 do i=is,ie ; if (do_i(i)) then
6090 if (dz(i,k) < dz_bbl_rem(i)) then
610 ! This layer is fully within the averaging depth.
6110 SpV_h_bot(i) = SpV_h_bot(i) + h(i,j,k) * tv%SpV_avg(i,j,k)
6120 dz_bbl_rem(i) = dz_bbl_rem(i) - dz(i,k)
6130 hb(i) = hb(i) + h(i,j,k)
6140 k_bot(i) = k
6150 do_any = .true.
616 else
6170 if (dz(i,k) > 0.0) then
6180 frac_in = dz_bbl_rem(i) / dz(i,k)
6190 if (frac_in >= 0.5) k_bot(i) = k ! update bbl top index if >= 50% of layer
620 else
6210 frac_in = 0.0
622 endif
6230 if (use_EOS) then
624 ! Store the properties of this layer to determine the average
625 ! specific volume of the portion that is within the BBL.
6260 T_bbl(i) = tv%T(i,j,k) ; S_bbl(i) = tv%S(i,j,k)
6270 dp(i) = frac_in * (GV%g_Earth*GV%H_to_RZ * h(i,j,k))
6280 P_bbl(i) = pres_int(i,K) + (1.0-frac_in) * (GV%g_Earth*GV%H_to_RZ * h(i,j,k))
629 else
6300 SpV_bbl(i) = tv%SpV_avg(i,j,k)
631 endif
6320 h_bbl_frac(i) = frac_in * h(i,j,k)
6330 dz_bbl_rem(i) = 0.0
6340 do_i(i) = .false.
635 endif
636 endif ; enddo
6370 if (.not.do_any) exit
638 enddo
6390 do i=is,ie ; if (do_i(i)) then
640 ! The nominal bottom boundary layer is thicker than the water column, but layer 1 is
641 ! already included in the averages. These values are set so that the call to find
642 ! the layer-average specific volume will behave sensibly.
6430 if (use_EOS) then
6440 T_bbl(i) = tv%T(i,j,1) ; S_bbl(i) = tv%S(i,j,1)
6450 dp(i) = 0.0
6460 P_bbl(i) = pres_int(i,1)
647 else
6480 SpV_bbl(i) = tv%SpV_avg(i,j,1)
649 endif
6500 h_bbl_frac(i) = 0.0
651 endif ; enddo
652
6530 if (use_EOS) then
654 ! Find the average specific volume of the fractional layer atop the BBL.
6550 EOSdom(:) = EOS_domain(G%HI)
6560 call average_specific_vol(T_bbl, S_bbl, P_bbl, dp, SpV_bbl, tv%eqn_of_state, EOSdom)
657 endif
658
6590 do i=is,ie
6600 if (hb(i) + h_bbl_frac(i) < GV%H_subroundoff) h_bbl_frac(i) = GV%H_subroundoff
6610 rho_bot(i) = G%mask2dT(i,j) * (hb(i) + h_bbl_frac(i)) / (SpV_h_bot(i) + h_bbl_frac(i)*SpV_bbl(i))
6620 h_bot(i) = hb(i) + h_bbl_frac(i)
663 enddo
664 endif
665
666720end subroutine find_rho_bottom
667
668
669!> Converts thickness from geometric height units to thickness units, perhaps via an
670!! inversion of the integral of the density in pressure using variables stored in
671!! the thermo_var_ptrs type when in non-Boussinesq mode.
6721subroutine dz_to_thickness_tv(dz, tv, h, G, GV, US, halo_size)
673 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
674 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
675 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
676 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
677 intent(in) :: dz !< Geometric layer thicknesses in height units [Z ~> m]
678 type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
679 !! thermodynamic variables
680 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
681 intent(inout) :: h !< Output thicknesses in thickness units [H ~> m or kg m-2].
682 !! This is essentially intent out, but declared as intent
683 !! inout to preserve any initialized values in halo points.
684 integer, optional, intent(in) :: halo_size !< Width of halo within which to
685 !! calculate thicknesses
686 ! Local variables
687 integer :: i, j, k, is, ie, js, je, halo, nz
688
6891 halo = 0 ; if (present(halo_size)) halo = max(0,halo_size)
6901 is = G%isc-halo ; ie = G%iec+halo ; js = G%jsc-halo ; je = G%jec+halo ; nz = GV%ke
691
6921 if (GV%Boussinesq) then
693544576 do k=1,nz ; do j=js,je ; do i=is,ie
694544500 h(i,j,k) = GV%Z_to_H * dz(i,j,k)
695 enddo ; enddo ; enddo
696 else
6970 if (associated(tv%eqn_of_state)) then
6980 if (associated(tv%p_surf)) then
6990 call dz_to_thickness_EOS(dz, tv%T, tv%S, tv%eqn_of_state, h, G, GV, US, halo, tv%p_surf)
700 else
7010 call dz_to_thickness_EOS(dz, tv%T, tv%S, tv%eqn_of_state, h, G, GV, US, halo)
702 endif
703 else
7040 do k=1,nz ; do j=js,je ; do i=is,ie
7050 h(i,j,k) = (GV%RZ_to_H * GV%Rlay(k)) * dz(i,j,k)
706 enddo ; enddo ; enddo
707 endif
708 endif
709
7101end subroutine dz_to_thickness_tv
711
712!> Converts thickness from geometric height units to thickness units, working via an
713!! inversion of the integral of the density in pressure when in non-Boussinesq mode.
7140subroutine dz_to_thickness_EOS(dz, Temp, Saln, EoS, h, G, GV, US, halo_size, p_surf)
715 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
716 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
717 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
718 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
719 intent(in) :: dz !< Geometric layer thicknesses in height units [Z ~> m]
720 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
721 intent(in) :: Temp !< Input layer temperatures [C ~> degC]
722 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
723 intent(in) :: Saln !< Input layer salinities [S ~> ppt]
724 type(EOS_type), intent(in) :: EoS !< Equation of state structure
725 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
726 intent(inout) :: h !< Output thicknesses in thickness units [H ~> m or kg m-2].
727 !! This is essentially intent out, but declared as intent
728 !! inout to preserve any initialized values in halo points.
729 integer, optional, intent(in) :: halo_size !< Width of halo within which to
730 !! calculate thicknesses
731 real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: p_surf !< Surface pressures [R L2 T-2 ~> Pa]
732 ! Local variables
733 real, dimension(SZI_(G),SZJ_(G)) :: &
7340 p_top, p_bot ! Pressure at the interfaces above and below a layer [R L2 T-2 ~> Pa]
7350 real :: dp(SZI_(G),SZJ_(G)) ! Pressure change across a layer [R L2 T-2 ~> Pa]
7360 real :: dz_geo(SZI_(G),SZJ_(G)) ! The change in geopotential height across a layer [L2 T-2 ~> m2 s-2]
7370 real :: rho(SZI_(G)) ! The in situ density [R ~> kg m-3]
738 real :: dp_adj ! The amount by which to change the bottom pressure in an
739 ! iteration [R L2 T-2 ~> Pa]
740 real :: I_gEarth ! Unit conversion factors divided by the gravitational
741 ! acceleration [H T2 R-1 L-2 ~> s2 m2 kg-1 or s2 m-1]
7420 logical :: do_more(SZI_(G),SZJ_(G)) ! If true, additional iterations would be beneficial.
743 logical :: do_any ! True if there are points in this layer that need more itertions.
744 integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state
745 integer :: i, j, k, is, ie, js, je, halo, nz
746 integer :: itt, max_itt
747
7480 halo = 0 ; if (present(halo_size)) halo = max(0,halo_size)
7490 is = G%isc-halo ; ie = G%iec+halo ; js = G%jsc-halo ; je = G%jec+halo ; nz = GV%ke
7500 max_itt = 10
751
7520 if (GV%Boussinesq) then
7530 do k=1,nz ; do j=js,je ; do i=is,ie
7540 h(i,j,k) = GV%Z_to_H * dz(i,j,k)
755 enddo ; enddo ; enddo
756 else
7570 I_gEarth = GV%RZ_to_H / GV%g_Earth
758
7590 if (present(p_surf)) then
7600 do j=js,je ; do i=is,ie
7610 p_bot(i,j) = 0.0 ; p_top(i,j) = p_surf(i,j)
762 enddo ; enddo
763 else
7640 do j=js,je ; do i=is,ie
7650 p_bot(i,j) = 0.0 ; p_top(i,j) = 0.0
766 enddo ; enddo
767 endif
7680 EOSdom(:) = EOS_domain(G%HI)
769
770 ! The iterative approach here is inherited from very old code that was in the
771 ! MOM_state_initialization module. It does converge, but it is very inefficient and
772 ! should be revised, although doing so would change answers in non-Boussinesq mode.
7730 do k=1,nz
7740 do j=js,je
7750 do i=is,ie ; p_top(i,j) = p_bot(i,j) ; enddo
776 call calculate_density(Temp(:,j,k), Saln(:,j,k), p_top(:,j), rho, &
7770 EoS, EOSdom)
778 ! The following two expressions are mathematically equivalent.
7790 if (GV%semi_Boussinesq) then
7800 do i=is,ie
7810 p_bot(i,j) = p_top(i,j) + (GV%g_Earth*GV%H_to_Z) * ((GV%Z_to_H*dz(i,j,k)) * rho(i))
7820 dp(i,j) = (GV%g_Earth*GV%H_to_Z) * ((GV%Z_to_H*dz(i,j,k)) * rho(i))
783 enddo
784 else
7850 do i=is,ie
7860 p_bot(i,j) = p_top(i,j) + rho(i) * (GV%g_Earth * dz(i,j,k))
7870 dp(i,j) = rho(i) * (GV%g_Earth * dz(i,j,k))
788 enddo
789 endif
790 enddo
791
7920 do_more(:,:) = .true.
7930 do itt=1,max_itt
7940 do_any = .false.
7950 call int_specific_vol_dp(Temp(:,:,k), Saln(:,:,k), p_top, p_bot, 0.0, G%HI, EoS, US, dz_geo)
7960 if (itt < max_itt) then ; do j=js,je
7970 call calculate_density(Temp(:,j,k), Saln(:,j,k), p_bot(:,j), rho, EoS, EOSdom)
798 ! Use Newton's method to correct the bottom value.
799 ! The hydrostatic equation is sufficiently linear that no bounds-checking is needed.
8000 if (GV%semi_Boussinesq) then
8010 do i=is,ie
8020 dp_adj = rho(i) * ((GV%g_Earth*GV%H_to_Z)*(GV%Z_to_H*dz(i,j,k)) - dz_geo(i,j))
8030 p_bot(i,j) = p_bot(i,j) + dp_adj
8040 dp(i,j) = dp(i,j) + dp_adj
805 enddo
8060 do_any = .true. ! To avoid changing answers, always use the maximum number of itertions.
807 else
8080 do i=is,ie ; if (do_more(i,j)) then
8090 dp_adj = rho(i) * (GV%g_Earth*dz(i,j,k) - dz_geo(i,j))
8100 p_bot(i,j) = p_bot(i,j) + dp_adj
8110 dp(i,j) = dp(i,j) + dp_adj
812 ! Check for convergence to roundoff.
8130 do_more(i,j) = (abs(dp_adj) > 1.0e-15*dp(i,j))
8140 if (do_more(i,j)) do_any = .true.
815 endif ; enddo
816 endif
817 enddo ; endif
8180 if (.not.do_any) exit
819 enddo
820
8210 if (GV%semi_Boussinesq) then
8220 do j=js,je ; do i=is,ie
8230 h(i,j,k) = (p_bot(i,j) - p_top(i,j)) * I_gEarth
824 enddo ; enddo
825 else
8260 do j=js,je ; do i=is,ie
8270 h(i,j,k) = dp(i,j) * I_gEarth
828 enddo ; enddo
829 endif
830 enddo
831 endif
832
8330end subroutine dz_to_thickness_EOS
834
835!> Converts thickness from geometric height units to thickness units, perhaps using
836!! a simple conversion factor that may be problematic in non-Boussinesq mode.
8370subroutine dz_to_thickness_simple(dz, h, G, GV, US, halo_size, layer_mode)
838 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
839 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
840 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
841 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
842 intent(in) :: dz !< Geometric layer thicknesses in height units [Z ~> m]
843 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
844 intent(inout) :: h !< Output thicknesses in thickness units [H ~> m or kg m-2].
845 !! This is essentially intent out, but declared as intent
846 !! inout to preserve any initialized values in halo points.
847 integer, optional, intent(in) :: halo_size !< Width of halo within which to
848 !! calculate thicknesses
849 logical, optional, intent(in) :: layer_mode !< If present and true, do the conversion that
850 !! is appropriate in pure isopycnal layer mode with
851 !! no state variables or equation of state. Otherwise
852 !! use a simple constant rescaling factor and avoid the
853 !! use of GV%Rlay.
854 ! Local variables
855 logical :: layered ! If true and the model is non-Boussinesq, do calculations appropriate for use
856 ! in pure isopycnal layered mode with no state variables or equation of state.
857 integer :: i, j, k, is, ie, js, je, halo, nz
858
8590 halo = 0 ; if (present(halo_size)) halo = max(0,halo_size)
8600 layered = .false. ; if (present(layer_mode)) layered = layer_mode
8610 is = G%isc-halo ; ie = G%iec+halo ; js = G%jsc-halo ; je = G%jec+halo ; nz = GV%ke
862
8630 if (GV%Boussinesq) then
8640 do k=1,nz ; do j=js,je ; do i=is,ie
8650 h(i,j,k) = GV%Z_to_H * dz(i,j,k)
866 enddo ; enddo ; enddo
8670 elseif (layered) then
8680 do k=1,nz ; do j=js,je ; do i=is,ie
8690 h(i,j,k) = (GV%RZ_to_H * GV%Rlay(k)) * dz(i,j,k)
870 enddo ; enddo ; enddo
871 else
8720 do k=1,nz ; do j=js,je ; do i=is,ie
8730 h(i,j,k) = (US%Z_to_m * GV%m_to_H) * dz(i,j,k)
874 enddo ; enddo ; enddo
875 endif
876
8770end subroutine dz_to_thickness_simple
878
879!> Converts layer thicknesses in thickness units to the vertical distance between edges in height
880!! units, perhaps by multiplication by the precomputed layer-mean specific volume stored in an
881!! array in the thermo_var_ptrs type when in non-Boussinesq mode.
882144subroutine thickness_to_dz_3d(h, tv, dz, G, GV, US, halo_size, do_offload)
883 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
884 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
885 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
886 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
887 intent(in) :: h !< Input thicknesses in thickness units [H ~> m or kg m-2].
888 type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
889 !! thermodynamic variables
890 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
891 intent(inout) :: dz !< Geometric layer thicknesses in height units [Z ~> m]
892 !! This is essentially intent out, but declared as intent
893 !! inout to preserve any initialized values in halo points.
894 integer, optional, intent(in) :: halo_size !< Width of halo within which to
895 !! calculate thicknesses
896 logical, optional, intent(in) :: do_offload !< If .true., only uses data calculates dz
897 !! on GPU (default .false.)
898 ! Local variables
899 character(len=128) :: mesg ! A string for error messages
900 integer :: i, j, k, is, ie, js, je, halo, nz
901 logical :: use_doconcurrent
902
903 ! guard to allow turning off/on do concurrent
904144 use_doconcurrent = .false.
905144 if (present(do_offload)) use_doconcurrent = do_offload
906
907144 halo = 0 ; if (present(halo_size)) halo = max(0,halo_size)
908144 is = G%isc-halo ; ie = G%iec+halo ; js = G%jsc-halo ; je = G%jec+halo ; nz = GV%ke
909
910144 if ((.not.GV%Boussinesq) .and. allocated(tv%SpV_avg)) then
9110 if ((allocated(tv%SpV_avg)) .and. (tv%valid_SpV_halo < halo)) then
9120 if (tv%valid_SpV_halo < 0) then
9130 mesg = "invalid values of SpV_avg."
914 else
915 write(mesg, '("insufficiently large SpV_avg halos of width ", i2, " but ", i2," is needed.")') &
9160 tv%valid_SpV_halo, halo
917 endif
9180 call MOM_error(FATAL, "thickness_to_dz called in fully non-Boussinesq mode with "//trim(mesg))
919 endif
9200 if (use_doconcurrent) then
9210 do concurrent (k=1:nz, j=js:je, i=is:ie)
9220 dz(i,j,k) = GV%H_to_RZ * h(i,j,k) * tv%SpV_avg(i,j,k)
923 enddo
924 else
9250 do k=1,nz ; do j=js,je ; do i=is,ie
9260 dz(i,j,k) = GV%H_to_RZ * h(i,j,k) * tv%SpV_avg(i,j,k)
927 enddo ; enddo ; enddo
928 endif
929 else
930144 if (use_doconcurrent) then
93184 do concurrent (k=1:nz, j=js:je, i=is:ie)
93248298908 dz(i,j,k) = GV%H_to_Z * h(i,j,k)
933 enddo
934 else
93533662760 do k=1,nz ; do j=js,je ; do i=is,ie
93633658200 dz(i,j,k) = GV%H_to_Z * h(i,j,k)
937 enddo ; enddo ; enddo
938 endif
939 endif
940
941144end subroutine thickness_to_dz_3d
942
943
944!> Converts a vertical i- / k- slice of layer thicknesses in thickness units to the vertical
945!! distance between edges in height units, perhaps by multiplication by the precomputed layer-mean
946!! specific volume stored in an array in the thermo_var_ptrs type when in non-Boussinesq mode.
9472904subroutine thickness_to_dz_jslice(h, tv, dz, j, G, GV, halo_size)
948 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
949 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
950 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
951 intent(in) :: h !< Input thicknesses in thickness units [H ~> m or kg m-2].
952 type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
953 !! thermodynamic variables
954 real, dimension(SZI_(G),SZK_(GV)), &
955 intent(inout) :: dz !< Geometric layer thicknesses in height units [Z ~> m]
956 !! This is essentially intent out, but declared as intent
957 !! inout to preserve any initialized values in halo points.
958 integer, intent(in) :: j !< The second (j-) index of the input thicknesses to work with
959 integer, optional, intent(in) :: halo_size !< Width of halo within which to
960 !! calculate thicknesses
961 ! Local variables
962 character(len=128) :: mesg ! A string for error messages
963 integer :: i, k, is, ie, halo, nz
964
9652904 halo = 0 ; if (present(halo_size)) halo = max(0,halo_size)
9662904 is = G%isc-halo ; ie = G%iec+halo ; nz = GV%ke
967
9682904 if ((.not.GV%Boussinesq) .and. allocated(tv%SpV_avg)) then
9690 if ((allocated(tv%SpV_avg)) .and. (tv%valid_SpV_halo < halo)) then
9700 if (tv%valid_SpV_halo < 0) then
9710 mesg = "invalid values of SpV_avg."
972 else
973 write(mesg, '("insufficiently large SpV_avg halos of width ", i2, " but ", i2," is needed.")') &
9740 tv%valid_SpV_halo, halo
975 endif
9760 call MOM_error(FATAL, "thickness_to_dz called in fully non-Boussinesq mode with "//trim(mesg))
977 endif
978
9790 do k=1,nz ; do i=is,ie
9800 dz(i,k) = GV%H_to_RZ * h(i,j,k) * tv%SpV_avg(i,j,k)
981 enddo ; enddo
982 else
98326468304 do k=1,nz ; do i=is,ie
98426465400 dz(i,k) = GV%H_to_Z * h(i,j,k)
985 enddo ; enddo
986 endif
987
9882904end subroutine thickness_to_dz_jslice
989
990
991!> Convert mixed layer depths in height units into the thickness of water in the mixed
992!! in thickness units.
99312subroutine convert_MLD_to_ML_thickness(MLD_in, h, h_MLD, tv, G, GV, halo)
994 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
995 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
996 real, dimension(SZI_(G),SZJ_(G)), &
997 intent(in) :: MLD_in !< Input mixed layer depth [Z ~> m].
998 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
999 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
1000 real, dimension(SZI_(G),SZJ_(G)), &
1001 intent(out) :: h_MLD !< Thickness of water in the mixed layer [H ~> m or kg m-2]
1002 type(thermo_var_ptrs), intent(in) :: tv !< Structure containing pointers to any available
1003 !! thermodynamic fields.
1004 integer, optional, intent(in) :: halo !< Halo width over which to calculate frazil
1005
1006 ! Local variables
100724 real :: MLD_rem(SZI_(G)) ! The vertical extent of the MLD_in that has not yet been accounted for [Z ~> m]
1008 character(len=128) :: mesg ! A string for error messages
1009 logical :: keep_going
1010 integer :: i, j, k, is, ie, js, je, nz, halos
1011
101212 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
1013
101412 halos = 0 ; if (present(halo)) halos = halo
101512 if (present(halo)) then
10160 is = G%isc-halo ; ie = G%iec+halo ; js = G%jsc-halo ; je = G%jec+halo
1017 endif
1018
101912 if (GV%Boussinesq .or. (.not.allocated(tv%SpV_avg))) then
102087132 do j=js,je ; do i=is,ie
102187120 h_MLD(i,j) = GV%Z_to_H * MLD_in(i,j)
1022 enddo ; enddo
1023 else ! The fully non-Boussinesq conversion between height in MLD_in and thickness.
10240 if ((allocated(tv%SpV_avg)) .and. (tv%valid_SpV_halo < halos)) then
10250 if (tv%valid_SpV_halo < 0) then
10260 mesg = "invalid values of SpV_avg."
1027 else
1028 write(mesg, '("insufficiently large SpV_avg halos of width ", i2, " but ", i2," is needed.")') &
10290 tv%valid_SpV_halo, halos
1030 endif
10310 call MOM_error(FATAL, "convert_MLD_to_ML_thickness called in fully non-Boussinesq mode with "//trim(mesg))
1032 endif
1033
10340 do j=js,je
10350 do i=is,ie ; MLD_rem(i) = MLD_in(i,j) ; h_MLD(i,j) = 0.0 ; enddo
10360 do k=1,nz
10370 keep_going = .false.
10380 do i=is,ie ; if (MLD_rem(i) > 0.0) then
10390 if (MLD_rem(i) > GV%H_to_RZ * h(i,j,k) * tv%SpV_avg(i,j,k)) then
10400 h_MLD(i,j) = h_MLD(i,j) + h(i,j,k)
10410 MLD_rem(i) = MLD_rem(i) - GV%H_to_RZ * h(i,j,k) * tv%SpV_avg(i,j,k)
10420 keep_going = .true.
1043 else
10440 h_MLD(i,j) = h_MLD(i,j) + GV%RZ_to_H * MLD_rem(i) / tv%SpV_avg(i,j,k)
10450 MLD_rem(i) = 0.0
1046 endif
1047 endif ; enddo
10480 if (.not.keep_going) exit
1049 enddo
1050 enddo
1051 endif
1052
105312end subroutine convert_MLD_to_ML_thickness
1054
1055end module MOM_interface_heights