Skip to content

Commit e7337bf

Browse files
committed
(*)Improve non-Boussinesq initialization
This commit includes three distinct sets of changes inside of MOM_state_initialization.F90 to better handle the initialization of non-Boussinesq models, none of which change any answers in Boussinesq models. These include: - Refactored trim_for_ice to have a separate, simpler form appropriate for use in non-Boussinesq mode. The units of the min_thickness argument to cut_off_column top were also changed to thickness units. - Initialize_sponges_file was refactored to work in depth-space variables before using dz_to_thickness to convert to thicknesses, but also to properly handle the case where the input file has a different number of vertical layers than the model is using, in which case the previous version could have had a segmentation fault. - Code in MOM_temp_salt_initialize_from_Z was reordered to more clearly group it into distinct phases. It also uses the new dz_to_thickness routine to convert input depths into thicknesses. All answers are bitwise identical in all Boussinesq test cases and all test cases in the MOM6-examples regression suite, but answers could be changed and improved in some non-Boussinesq cases.
1 parent 6080ea6 commit e7337bf

1 file changed

Lines changed: 126 additions & 87 deletions

File tree

src/initialization/MOM_state_initialization.F90

Lines changed: 126 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module MOM_state_initialization
1717
use MOM_file_parser, only : log_version
1818
use MOM_get_input, only : directories
1919
use MOM_grid, only : ocean_grid_type, isPointInCell
20-
use MOM_interface_heights, only : find_eta, dz_to_thickness
20+
use MOM_interface_heights, only : find_eta, dz_to_thickness, dz_to_thickness_simple
2121
use MOM_io, only : file_exists, field_size, MOM_read_data, MOM_read_vector, slasher
2222
use MOM_open_boundary, only : ocean_OBC_type, open_boundary_init, set_tracer_data
2323
use MOM_open_boundary, only : OBC_NONE
@@ -1125,7 +1125,7 @@ subroutine trim_for_ice(PF, G, GV, US, ALE_CSp, tv, h, just_read)
11251125
! of temperature within each layer [C ~> degC]
11261126
character(len=200) :: inputdir, filename, p_surf_file, p_surf_var ! Strings for file/path
11271127
real :: scale_factor ! A file-dependent scaling factor for the input pressure [various].
1128-
real :: min_thickness ! The minimum layer thickness, recast into Z units [Z ~> m].
1128+
real :: min_thickness ! The minimum layer thickness [H ~> m or kg m-2].
11291129
real :: z_tolerance ! The tolerance with which to find the depth matching a specified pressure [Z ~> m].
11301130
integer :: i, j, k
11311131
integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags.
@@ -1155,7 +1155,7 @@ subroutine trim_for_ice(PF, G, GV, US, ALE_CSp, tv, h, just_read)
11551155
"file SURFACE_PRESSURE_FILE into a surface pressure.", &
11561156
units="file dependent", default=1., do_not_log=just_read)
11571157
call get_param(PF, mdl, "MIN_THICKNESS", min_thickness, 'Minimum layer thickness', &
1158-
units='m', default=1.e-3, scale=US%m_to_Z, do_not_log=just_read)
1158+
units='m', default=1.e-3, scale=GV%m_to_H, do_not_log=just_read)
11591159
call get_param(PF, mdl, "TRIM_IC_Z_TOLERANCE", z_tolerance, &
11601160
"The tolerance with which to find the depth matching the specified "//&
11611161
"surface pressure with TRIM_IC_FOR_P_SURF.", &
@@ -1312,7 +1312,7 @@ subroutine cut_off_column_top(nk, tv, GV, US, G_earth, depth, min_thickness, T,
13121312
type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
13131313
real, intent(in) :: G_earth !< Gravitational acceleration [L2 Z-1 T-2 ~> m s-2]
13141314
real, intent(in) :: depth !< Depth of ocean column [Z ~> m].
1315-
real, intent(in) :: min_thickness !< Smallest thickness allowed [Z ~> m].
1315+
real, intent(in) :: min_thickness !< Smallest thickness allowed [H ~> m or kg m-2].
13161316
real, dimension(nk), intent(inout) :: T !< Layer mean temperature [C ~> degC]
13171317
real, dimension(nk), intent(in) :: T_t !< Temperature at top of layer [C ~> degC]
13181318
real, dimension(nk), intent(in) :: T_b !< Temperature at bottom of layer [C ~> degC]
@@ -1335,51 +1335,75 @@ subroutine cut_off_column_top(nk, tv, GV, US, G_earth, depth, min_thickness, T,
13351335
real, dimension(nk) :: h0, h1 ! Initial and remapped layer thicknesses [H ~> m or kg m-2]
13361336
real, dimension(nk) :: S0, S1 ! Initial and remapped layer salinities [S ~> ppt]
13371337
real, dimension(nk) :: T0, T1 ! Initial and remapped layer temperatures [C ~> degC]
1338-
real :: P_t, P_b ! Top and bottom pressures [R L2 T-2 ~> Pa]
1338+
real :: P_t, P_b ! Top and bottom pressures [R L2 T-2 ~> Pa]
13391339
real :: z_out, e_top ! Interface height positions [Z ~> m]
1340+
real :: min_dz ! The minimum thickness in depth units [Z ~> m]
1341+
real :: dh_surf_rem ! The remaining thickness to remove in non-Bousinesq mode [H ~> kg m-2]
13401342
logical :: answers_2018
13411343
integer :: k
13421344

13431345
answers_2018 = .true. ; if (present(remap_answer_date)) answers_2018 = (remap_answer_date < 20190101)
13441346

1345-
! Calculate original interface positions
1346-
e(nk+1) = -depth
1347-
do k=nk,1,-1
1348-
e(K) = e(K+1) + GV%H_to_Z*h(k)
1349-
h0(k) = h(nk+1-k) ! Keep a copy to use in remapping
1350-
enddo
1347+
! Keep a copy of the initial thicknesses in reverse order to use in remapping
1348+
do k=1,nk ; h0(k) = h(nk+1-k) ; enddo
13511349

1352-
P_t = 0.
1353-
e_top = e(1)
1354-
do k=1,nk
1355-
call find_depth_of_pressure_in_cell(T_t(k), T_b(k), S_t(k), S_b(k), e(K), e(K+1), &
1356-
P_t, p_surf, GV%Rho0, G_earth, tv%eqn_of_state, &
1357-
US, P_b, z_out, z_tol=z_tol)
1358-
if (z_out>=e(K)) then
1359-
! Imposed pressure was less that pressure at top of cell
1360-
exit
1361-
elseif (z_out<=e(K+1)) then
1362-
! Imposed pressure was greater than pressure at bottom of cell
1363-
e_top = e(K+1)
1364-
else
1365-
! Imposed pressure was fell between pressures at top and bottom of cell
1366-
e_top = z_out
1367-
exit
1368-
endif
1369-
P_t = P_b
1370-
enddo
1371-
if (e_top<e(1)) then
1372-
! Clip layers from the top down, if at all
1373-
do K=1,nk
1374-
if (e(K) > e_top) then
1375-
! Original e(K) is too high
1376-
e(K) = e_top
1377-
e_top = e_top - min_thickness ! Next interface must be at least this deep
1350+
if (GV%Boussinesq) then
1351+
min_dz = GV%H_to_Z * min_thickness
1352+
! Calculate original interface positions
1353+
e(nk+1) = -depth
1354+
do k=nk,1,-1
1355+
e(K) = e(K+1) + GV%H_to_Z*h(k)
1356+
enddo
1357+
1358+
P_t = 0.
1359+
e_top = e(1)
1360+
do k=1,nk
1361+
call find_depth_of_pressure_in_cell(T_t(k), T_b(k), S_t(k), S_b(k), e(K), e(K+1), &
1362+
P_t, p_surf, GV%Rho0, G_earth, tv%eqn_of_state, &
1363+
US, P_b, z_out, z_tol=z_tol)
1364+
if (z_out>=e(K)) then
1365+
! Imposed pressure was less that pressure at top of cell
1366+
exit
1367+
elseif (z_out<=e(K+1)) then
1368+
! Imposed pressure was greater than pressure at bottom of cell
1369+
e_top = e(K+1)
1370+
else
1371+
! Imposed pressure was fell between pressures at top and bottom of cell
1372+
e_top = z_out
1373+
exit
13781374
endif
1379-
! This layer needs trimming
1380-
h(k) = GV%Z_to_H * max( min_thickness, e(K) - e(K+1) )
1381-
if (e(K) < e_top) exit ! No need to go further
1375+
P_t = P_b
13821376
enddo
1377+
if (e_top<e(1)) then
1378+
! Clip layers from the top down, if at all
1379+
do K=1,nk
1380+
if (e(K) > e_top) then
1381+
! Original e(K) is too high
1382+
e(K) = e_top
1383+
e_top = e_top - min_dz ! Next interface must be at least this deep
1384+
endif
1385+
! This layer needs trimming
1386+
h(k) = max( min_thickness, GV%Z_to_H * (e(K) - e(K+1)) )
1387+
if (e(K) < e_top) exit ! No need to go further
1388+
enddo
1389+
endif
1390+
else
1391+
! In non-Bousinesq mode, we are already in mass units so the calculation is much easier.
1392+
if (p_surf > 0.0) then
1393+
dh_surf_rem = p_surf * GV%RZ_to_H / G_earth
1394+
do k=1,nk
1395+
if (h(k) <= min_thickness) then ! This layer has no mass to remove.
1396+
cycle
1397+
elseif ((h(k) - min_thickness) < dh_surf_rem) then ! This layer should be removed entirely.
1398+
dh_surf_rem = dh_surf_rem - (h(k) - min_thickness)
1399+
h(k) = min_thickness
1400+
else ! This is the last layer that should be removed.
1401+
h(k) = h(k) - dh_surf_rem
1402+
dh_surf_rem = 0.0
1403+
exit
1404+
endif
1405+
enddo
1406+
endif
13831407
endif
13841408

13851409
! Now we need to remap but remapping assumes the surface is at the
@@ -1867,16 +1891,18 @@ subroutine initialize_sponges_file(G, GV, US, use_temperature, tv, u, v, depth_t
18671891
!! overrides any value set for Time.
18681892
! Local variables
18691893
real, allocatable, dimension(:,:,:) :: eta ! The target interface heights [Z ~> m].
1894+
real, allocatable, dimension(:,:,:) :: dz ! The target interface thicknesses in height units [Z ~> m]
18701895
real, allocatable, dimension(:,:,:) :: h ! The target interface thicknesses [H ~> m or kg m-2].
18711896

18721897
real, dimension (SZI_(G),SZJ_(G),SZK_(GV)) :: &
18731898
tmp, & ! A temporary array for temperatures [C ~> degC] or other tracers.
18741899
tmp2 ! A temporary array for salinities [S ~> ppt]
18751900
real, dimension (SZI_(G),SZJ_(G)) :: &
18761901
tmp_2d ! A temporary array for mixed layer densities [R ~> kg m-3]
1877-
real, allocatable, dimension(:,:,:) :: tmp_tr ! A temporary array for reading sponge target fields
1878-
! on the vertical grid of the input file, used for both
1879-
! temperatures [C ~> degC] and salinities [S ~> ppt]
1902+
real, allocatable, dimension(:,:,:) :: tmp_T ! A temporary array for reading sponge target temperatures
1903+
! on the vertical grid of the input file [C ~> degC]
1904+
real, allocatable, dimension(:,:,:) :: tmp_S ! A temporary array for reading sponge target salinities
1905+
! on the vertical grid of the input file [S ~> ppt]
18801906
real, allocatable, dimension(:,:,:) :: tmp_u ! Temporary array for reading sponge target zonal
18811907
! velocities on the vertical grid of the input file [L T-1 ~> m s-1]
18821908
real, allocatable, dimension(:,:,:) :: tmp_v ! Temporary array for reading sponge target meridional
@@ -1897,6 +1923,7 @@ subroutine initialize_sponges_file(G, GV, US, use_temperature, tv, u, v, depth_t
18971923
character(len=40) :: mdl = "initialize_sponges_file"
18981924
character(len=200) :: damping_file, uv_damping_file, state_file, state_uv_file ! Strings for filenames
18991925
character(len=200) :: filename, inputdir ! Strings for file/path and path.
1926+
type(verticalGrid_type) :: GV_loc ! A temporary vertical grid structure
19001927

19011928
logical :: use_ALE ! True if ALE is being used, False if in layered mode
19021929
logical :: time_space_interp_sponge ! If true use sponge data that need to be interpolated in both
@@ -2069,35 +2096,51 @@ subroutine initialize_sponges_file(G, GV, US, use_temperature, tv, u, v, depth_t
20692096
call MOM_error(FATAL,"initialize_sponge_file: Array size mismatch for sponge data.")
20702097
nz_data = siz(3)-1
20712098
allocate(eta(isd:ied,jsd:jed,nz_data+1))
2072-
allocate(h(isd:ied,jsd:jed,nz_data))
2099+
allocate(dz(isd:ied,jsd:jed,nz_data))
20732100
call MOM_read_data(filename, eta_var, eta(:,:,:), G%Domain, scale=US%m_to_Z)
20742101
do j=js,je ; do i=is,ie
2075-
eta(i,j,nz+1) = -depth_tot(i,j)
2102+
eta(i,j,nz_data+1) = -depth_tot(i,j)
20762103
enddo ; enddo
2077-
do k=nz,1,-1 ; do j=js,je ; do i=is,ie
2104+
do k=nz_data,1,-1 ; do j=js,je ; do i=is,ie
20782105
if (eta(i,j,K) < (eta(i,j,K+1) + GV%Angstrom_Z)) &
20792106
eta(i,j,K) = eta(i,j,K+1) + GV%Angstrom_Z
20802107
enddo ; enddo ; enddo
2081-
do k=1,nz ; do j=js,je ; do i=is,ie
2082-
h(i,j,k) = GV%Z_to_H*(eta(i,j,k)-eta(i,j,k+1))
2108+
do k=1,nz_data ; do j=js,je ; do i=is,ie
2109+
dz(i,j,k) = eta(i,j,k)-eta(i,j,k+1)
20832110
enddo; enddo ; enddo
2111+
deallocate(eta)
2112+
2113+
allocate(h(isd:ied,jsd:jed,nz_data))
2114+
if (use_temperature) then
2115+
allocate(tmp_T(isd:ied,jsd:jed,nz_data))
2116+
allocate(tmp_S(isd:ied,jsd:jed,nz_data))
2117+
call MOM_read_data(filename, potemp_var, tmp_T(:,:,:), G%Domain, scale=US%degC_to_C)
2118+
call MOM_read_data(filename, salin_var, tmp_S(:,:,:), G%Domain, scale=US%ppt_to_S)
2119+
endif
2120+
2121+
GV_loc = GV ; GV_loc%ke = nz_data
2122+
if (use_temperature .and. associated(tv%eqn_of_state)) then
2123+
call dz_to_thickness(dz, tmp_T, tmp_S, tv%eqn_of_state, h, G, GV_loc, US)
2124+
else
2125+
call dz_to_thickness_simple(dz, h, G, GV_loc, US, layer_mode=.true.)
2126+
endif
2127+
20842128
if (sponge_uv) then
20852129
call initialize_ALE_sponge(Idamp, G, GV, param_file, ALE_CSp, h, nz_data, Idamp_u, Idamp_v)
20862130
else
20872131
call initialize_ALE_sponge(Idamp, G, GV, param_file, ALE_CSp, h, nz_data)
20882132
endif
2089-
deallocate(eta)
2090-
deallocate(h)
20912133
if (use_temperature) then
2092-
allocate(tmp_tr(isd:ied,jsd:jed,nz_data))
2093-
call MOM_read_data(filename, potemp_var, tmp_tr(:,:,:), G%Domain, scale=US%degC_to_C)
2094-
call set_up_ALE_sponge_field(tmp_tr, G, GV, tv%T, ALE_CSp, 'temp', &
2134+
call set_up_ALE_sponge_field(tmp_T, G, GV, tv%T, ALE_CSp, 'temp', &
20952135
sp_long_name='temperature', sp_unit='degC s-1')
2096-
call MOM_read_data(filename, salin_var, tmp_tr(:,:,:), G%Domain, scale=US%ppt_to_S)
2097-
call set_up_ALE_sponge_field(tmp_tr, G, GV, tv%S, ALE_CSp, 'salt', &
2136+
call set_up_ALE_sponge_field(tmp_S, G, GV, tv%S, ALE_CSp, 'salt', &
20982137
sp_long_name='salinity', sp_unit='g kg-1 s-1')
2099-
deallocate(tmp_tr)
2138+
deallocate(tmp_S)
2139+
deallocate(tmp_T)
21002140
endif
2141+
deallocate(h)
2142+
deallocate(dz)
2143+
21012144
if (sponge_uv) then
21022145
filename = trim(inputdir)//trim(state_uv_file)
21032146
call log_param(param_file, mdl, "INPUTDIR/SPONGE_STATE_UV_FILE", filename)
@@ -2735,11 +2778,32 @@ subroutine MOM_temp_salt_initialize_from_Z(h, tv, depth_tot, G, GV, US, PF, just
27352778
enddo ; enddo
27362779
deallocate( tmp_mask_in )
27372780

2781+
! Convert input thicknesses to units of H. In non-Boussinesq mode this is done by inverting
2782+
! integrals of specific volume in pressure, so it can be expensive.
2783+
tv_loc = tv
2784+
tv_loc%T => tmpT1dIn
2785+
tv_loc%S => tmpS1dIn
2786+
GV_loc = GV
2787+
GV_loc%ke = nkd
2788+
call dz_to_thickness(dz1, tv_loc, h1, G, GV_loc, US)
2789+
27382790
! Build the target grid (and set the model thickness to it)
2739-
! This call can be more general but is hard-coded for z* coordinates... ????
2791+
27402792
call ALE_initRegridding( GV, US, G%max_depth, PF, mdl, regridCS ) ! sets regridCS
2793+
call initialize_remapping( remapCS, remappingScheme, boundary_extrapolation=.false., answer_date=remap_answer_date )
27412794

2742-
if (.not. remap_general) then
2795+
! Now remap from source grid to target grid, first setting reconstruction parameters
2796+
if (remap_general) then
2797+
call set_regrid_params( regridCS, min_thickness=0. )
2798+
allocate( dz_interface(isd:ied,jsd:jed,nkd+1) ) ! Need for argument to regridding_main() but is not used
2799+
2800+
call regridding_preadjust_reqs(regridCS, do_conv_adj, ignore)
2801+
if (do_conv_adj) call convective_adjustment(G, GV_loc, h1, tv_loc)
2802+
call regridding_main( remapCS, regridCS, G, GV_loc, h1, tv_loc, h, dz_interface, &
2803+
frac_shelf_h=frac_shelf_h )
2804+
2805+
deallocate( dz_interface )
2806+
else
27432807
! This is the old way of initializing to z* coordinates only
27442808
allocate( hTarget(nz) )
27452809
hTarget = getCoordinateResolution( regridCS )
@@ -2759,36 +2823,11 @@ subroutine MOM_temp_salt_initialize_from_Z(h, tv, depth_tot, G, GV, US, PF, just
27592823
enddo ; enddo
27602824
deallocate( hTarget )
27612825

2762-
do k=1,nkd ; do j=js,je ; do i=is,ie
2763-
h1(i,j,k) = GV%Z_to_H*dz1(i,j,k)
2764-
enddo ; enddo ; enddo
2765-
do k=1,nz ; do j=js,je ; do i=is,ie
2766-
h(i,j,k) = GV%Z_to_H*dz(i,j,k)
2767-
enddo ; enddo ; enddo
2826+
! This is a simple conversion of the target grid to thickness units that may not be
2827+
! appropriate in non-Boussinesq mode.
2828+
call dz_to_thickness_simple(dz, h, G, GV, US)
27682829
endif
27692830

2770-
! Now remap from source grid to target grid, first setting reconstruction parameters
2771-
call initialize_remapping( remapCS, remappingScheme, boundary_extrapolation=.false., answer_date=remap_answer_date )
2772-
if (remap_general) then
2773-
call set_regrid_params( regridCS, min_thickness=0. )
2774-
tv_loc = tv
2775-
tv_loc%T => tmpT1dIn
2776-
tv_loc%S => tmpS1dIn
2777-
GV_loc = GV
2778-
GV_loc%ke = nkd
2779-
allocate( dz_interface(isd:ied,jsd:jed,nkd+1) ) ! Need for argument to regridding_main() but is not used
2780-
2781-
! Convert thicknesses to units of H, in non-Boussinesq mode by inverting integrals of
2782-
! specific volume in pressure
2783-
call dz_to_thickness(dz1, tv_loc, h1, G, GV_loc, US)
2784-
2785-
call regridding_preadjust_reqs(regridCS, do_conv_adj, ignore)
2786-
if (do_conv_adj) call convective_adjustment(G, GV_loc, h1, tv_loc)
2787-
call regridding_main( remapCS, regridCS, G, GV_loc, h1, tv_loc, h, dz_interface, &
2788-
frac_shelf_h=frac_shelf_h )
2789-
2790-
deallocate( dz_interface )
2791-
endif
27922831
call ALE_remap_scalar(remapCS, G, GV, nkd, h1, tmpT1dIn, h, tv%T, all_cells=remap_full_column, &
27932832
old_remap=remap_old_alg, answer_date=remap_answer_date )
27942833
call ALE_remap_scalar(remapCS, G, GV, nkd, h1, tmpS1dIn, h, tv%S, all_cells=remap_full_column, &
@@ -3085,7 +3124,7 @@ subroutine MOM_state_init_tests(G, GV, US, tv)
30853124
write(0,*) ' ==================================================================== '
30863125
write(0,*) ''
30873126
write(0,*) GV%H_to_m*h(:)
3088-
call cut_off_column_top(nk, tv, GV, US, GV%g_Earth, -e(nk+1), GV%Angstrom_Z, &
3127+
call cut_off_column_top(nk, tv, GV, US, GV%g_Earth, -e(nk+1), GV%Angstrom_H, &
30893128
T, T_t, T_b, S, S_t, S_b, 0.5*P_tot, h, remap_CS, z_tol=z_tol)
30903129
write(0,*) GV%H_to_m*h(:)
30913130

0 commit comments

Comments
 (0)