MOM6
coord_rho.F90
1 !> Regrid columns for the continuous isopycnal (rho) coordinate
2 module coord_rho
3 
4 ! This file is part of MOM6. See LICENSE.md for the license.
5 
6 use mom_error_handler, only : mom_error, fatal
7 use mom_remapping, only : remapping_cs, remapping_core_h
9 use regrid_interp, only : interp_cs_type, build_and_interpolate_grid, degree_max
10 
11 implicit none ; private
12 
13 !> Control structure containing required parameters for the rho coordinate
14 type, public :: rho_cs ; private
15 
16  !> Number of layers
17  integer :: nk
18 
19  !> Minimum thickness allowed for layers, often in [H ~> m or kg m-2]
20  real :: min_thickness = 0.
21 
22  !> Reference pressure for density calculations [Pa]
23  real :: ref_pressure
24 
25  !> If true, integrate for interface positions from the top downward.
26  !! If false, integrate from the bottom upward, as does the rest of the model.
27  logical :: integrate_downward_for_e = .false.
28 
29  !> Nominal density of interfaces [kg m-3]
30  real, allocatable, dimension(:) :: target_density
31 
32  !> Interpolation control structure
33  type(interp_cs_type) :: interp_cs
34 end type rho_cs
35 
36 !> Maximum number of regridding iterations
37 integer, parameter :: nb_regridding_iterations = 1
38 !> Deviation tolerance between succesive grids in regridding iterations
39 real, parameter :: deviation_tolerance = 1e-10
40 
41 public init_coord_rho, set_rho_params, build_rho_column, old_inflate_layers_1d, end_coord_rho
42 
43 contains
44 
45 !> Initialise a rho_CS with pointers to parameters
46 subroutine init_coord_rho(CS, nk, ref_pressure, target_density, interp_CS)
47  type(rho_cs), pointer :: cs !< Unassociated pointer to hold the control structure
48  integer, intent(in) :: nk !< Number of layers in the grid
49  real, intent(in) :: ref_pressure !< Coordinate reference pressure [Pa]
50  real, dimension(:), intent(in) :: target_density !< Nominal density of interfaces [kg m-3]
51  type(interp_cs_type), intent(in) :: interp_cs !< Controls for interpolation
52 
53  if (associated(cs)) call mom_error(fatal, "init_coord_rho: CS already associated!")
54  allocate(cs)
55  allocate(cs%target_density(nk+1))
56 
57  cs%nk = nk
58  cs%ref_pressure = ref_pressure
59  cs%target_density(:) = target_density(:)
60  cs%interp_CS = interp_cs
61 end subroutine init_coord_rho
62 
63 !> This subroutine deallocates memory in the control structure for the coord_rho module
64 subroutine end_coord_rho(CS)
65  type(rho_cs), pointer :: cs !< Coordinate control structure
66 
67  ! nothing to do
68  if (.not. associated(cs)) return
69  deallocate(cs%target_density)
70  deallocate(cs)
71 end subroutine end_coord_rho
72 
73 !> This subroutine can be used to set the parameters for the coord_rho module
74 subroutine set_rho_params(CS, min_thickness, integrate_downward_for_e, interp_CS)
75  type(rho_cs), pointer :: cs !< Coordinate control structure
76  real, optional, intent(in) :: min_thickness !< Minimum allowed thickness [H ~> m or kg m-2]
77  logical, optional, intent(in) :: integrate_downward_for_e !< If true, integrate for interface
78  !! positions from the top downward. If false, integrate
79  !! from the bottom upward, as does the rest of the model.
80 
81  type(interp_cs_type), optional, intent(in) :: interp_cs !< Controls for interpolation
82 
83  if (.not. associated(cs)) call mom_error(fatal, "set_rho_params: CS not associated")
84 
85  if (present(min_thickness)) cs%min_thickness = min_thickness
86  if (present(integrate_downward_for_e)) cs%integrate_downward_for_e = integrate_downward_for_e
87  if (present(interp_cs)) cs%interp_CS = interp_cs
88 end subroutine set_rho_params
89 
90 !> Build a rho coordinate column
91 !!
92 !! 1. Density profiles are calculated on the source grid.
93 !! 2. Positions of target densities (for interfaces) are found by interpolation.
94 subroutine build_rho_column(CS, nz, depth, h, T, S, eqn_of_state, z_interface, &
95  h_neglect, h_neglect_edge)
96  type(rho_cs), intent(in) :: cs !< coord_rho control structure
97  integer, intent(in) :: nz !< Number of levels on source grid (i.e. length of h, T, S)
98  real, intent(in) :: depth !< Depth of ocean bottom (positive in m)
99  real, dimension(nz), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
100  real, dimension(nz), intent(in) :: t !< T for source column
101  real, dimension(nz), intent(in) :: s !< S for source column
102  type(eos_type), pointer :: eqn_of_state !< Equation of state structure
103  real, dimension(CS%nk+1), &
104  intent(inout) :: z_interface !< Absolute positions of interfaces
105  real, optional, intent(in) :: h_neglect !< A negligibly small width for the
106  !! purpose of cell reconstructions
107  !! in the same units as h0.
108  real, optional, intent(in) :: h_neglect_edge !< A negligibly small width
109  !! for the purpose of edge value calculations
110  !! in the same units as h0.
111  ! Local variables
112  integer :: k, count_nonzero_layers
113  integer, dimension(nz) :: mapping
114  real, dimension(nz) :: p, densities, h_nv
115  real, dimension(nz+1) :: xtmp
116  real, dimension(CS%nk) :: h_new ! New thicknesses
117  real, dimension(CS%nk+1) :: x1
118 
119  ! Construct source column with vanished layers removed (stored in h_nv)
120  call copy_finite_thicknesses(nz, h, cs%min_thickness, count_nonzero_layers, h_nv, mapping)
121 
122  if (count_nonzero_layers > 1) then
123  xtmp(1) = 0.0
124  do k = 1,count_nonzero_layers
125  xtmp(k+1) = xtmp(k) + h_nv(k)
126  enddo
127 
128  ! Compute densities on source column
129  p(:) = cs%ref_pressure
130  call calculate_density(t, s, p, densities, 1, nz, eqn_of_state)
131  do k = 1,count_nonzero_layers
132  densities(k) = densities(mapping(k))
133  enddo
134 
135  ! Based on source column density profile, interpolate to generate a new grid
136  call build_and_interpolate_grid(cs%interp_CS, densities, count_nonzero_layers, &
137  h_nv, xtmp, cs%target_density, cs%nk, h_new, &
138  x1, h_neglect, h_neglect_edge)
139 
140  ! Inflate vanished layers
141  call old_inflate_layers_1d(cs%min_thickness, cs%nk, h_new)
142 
143  ! Comment: The following adjustment of h_new, and re-calculation of h_new via x1 needs to be removed
144  x1(1) = 0.0 ; do k = 1,cs%nk ; x1(k+1) = x1(k) + h_new(k) ; enddo
145  do k = 1,cs%nk
146  h_new(k) = x1(k+1) - x1(k)
147  enddo
148 
149  else ! count_nonzero_layers <= 1
150  if (nz == cs%nk) then
151  h_new(:) = h(:) ! This keeps old behavior
152  else
153  h_new(:) = 0.
154  h_new(1) = h(1)
155  endif
156  endif
157 
158  ! Return interface positions
159  if (cs%integrate_downward_for_e) then
160  ! Remapping is defined integrating from zero
161  z_interface(1) = 0.
162  do k = 1,cs%nk
163  z_interface(k+1) = z_interface(k) - h_new(k)
164  enddo
165  else
166  ! The rest of the model defines grids integrating up from the bottom
167  z_interface(cs%nk+1) = -depth
168  do k = cs%nk,1,-1
169  z_interface(k) = z_interface(k+1) + h_new(k)
170  enddo
171  endif
172 
173 end subroutine build_rho_column
174 
175 !> Iteratively build a rho coordinate column
176 !!
177 !! The algorithm operates as follows within each column:
178 !!
179 !! 1. Given T & S within each layer, the layer densities are computed.
180 !! 2. Based on these layer densities, a global density profile is reconstructed
181 !! (this profile is monotonically increasing and may be discontinuous)
182 !! 3. The new grid interfaces are determined based on the target interface
183 !! densities.
184 !! 4. T & S are remapped onto the new grid.
185 !! 5. Return to step 1 until convergence or until the maximum number of
186 !! iterations is reached, whichever comes first.
187 subroutine build_rho_column_iteratively(CS, remapCS, nz, depth, h, T, S, eqn_of_state, &
188  zInterface, h_neglect, h_neglect_edge)
189  type(rho_cs), intent(in) :: CS !< Regridding control structure
190  type(remapping_cs), intent(in) :: remapCS !< Remapping parameters and options
191  integer, intent(in) :: nz !< Number of levels
192  real, intent(in) :: depth !< Depth of ocean bottom [Z ~> m]
193  real, dimension(nz), intent(in) :: h !< Layer thicknesses in Z coordinates [Z ~> m]
194  real, dimension(nz), intent(in) :: T !< T for column [degC]
195  real, dimension(nz), intent(in) :: S !< S for column [ppt]
196  type(eos_type), pointer :: eqn_of_state !< Equation of state structure
197  real, dimension(nz+1), intent(inout) :: zInterface !< Absolute positions of interfaces
198  real, optional, intent(in) :: h_neglect !< A negligibly small width for the
199  !! purpose of cell reconstructions
200  !! in the same units as h [Z ~> m]
201  real, optional, intent(in) :: h_neglect_edge !< A negligibly small width
202  !! for the purpose of edge value calculations
203  !! in the same units as h [Z ~> m]
204  ! Local variables
205  integer :: k, m
206  integer :: count_nonzero_layers
207  real :: deviation ! When iterating to determine the final
208  ! grid, this is the deviation between two
209  ! successive grids.
210  real :: threshold
211  real, dimension(nz) :: p, densities, T_tmp, S_tmp, Tmp
212  integer, dimension(nz) :: mapping
213  real, dimension(nz) :: h0, h1, hTmp
214  real, dimension(nz+1) :: x0, x1, xTmp
215 
216  threshold = cs%min_thickness
217  p(:) = cs%ref_pressure
218  t_tmp(:) = t(:)
219  s_tmp(:) = s(:)
220  h0(:) = h(:)
221 
222  ! Start iterations to build grid
223  m = 1
224  deviation = 1e10
225  do while ( ( m <= nb_regridding_iterations ) .and. &
226  ( deviation > deviation_tolerance ) )
227 
228  ! Construct column with vanished layers removed
229  call copy_finite_thicknesses(nz, h0, threshold, count_nonzero_layers, htmp, mapping)
230  if ( count_nonzero_layers <= 1 ) then
231  h1(:) = h0(:)
232  exit ! stop iterations here
233  endif
234 
235  xtmp(1) = 0.0
236  do k = 1,count_nonzero_layers
237  xtmp(k+1) = xtmp(k) + htmp(k)
238  enddo
239 
240  ! Compute densities within current water column
241  call calculate_density( t_tmp, s_tmp, p, densities,&
242  1, nz, eqn_of_state )
243 
244  do k = 1,count_nonzero_layers
245  densities(k) = densities(mapping(k))
246  enddo
247 
248  ! One regridding iteration
249  ! Based on global density profile, interpolate to generate a new grid
250  call build_and_interpolate_grid(cs%interp_CS, densities, count_nonzero_layers, &
251  htmp, xtmp, cs%target_density, nz, h1, x1, h_neglect, h_neglect_edge)
252 
253  call old_inflate_layers_1d( cs%min_thickness, nz, h1 )
254  x1(1) = 0.0 ; do k = 1,nz ; x1(k+1) = x1(k) + h1(k) ; enddo
255 
256  ! Remap T and S from previous grid to new grid
257  do k = 1,nz
258  h1(k) = x1(k+1) - x1(k)
259  enddo
260 
261  call remapping_core_h(remapcs, nz, h0, s, nz, h1, tmp, h_neglect, h_neglect_edge)
262  s_tmp(:) = tmp(:)
263 
264  call remapping_core_h(remapcs, nz, h0, t, nz, h1, tmp, h_neglect, h_neglect_edge)
265  t_tmp(:) = tmp(:)
266 
267  ! Compute the deviation between two successive grids
268  deviation = 0.0
269  x0(1) = 0.0
270  x1(1) = 0.0
271  do k = 2,nz
272  x0(k) = x0(k-1) + h0(k-1)
273  x1(k) = x1(k-1) + h1(k-1)
274  deviation = deviation + (x0(k)-x1(k))**2
275  enddo
276  deviation = sqrt( deviation / (nz-1) )
277 
278  m = m + 1
279 
280  ! Copy final grid onto start grid for next iteration
281  h0(:) = h1(:)
282 
283  enddo ! end regridding iterations
284 
285  if (cs%integrate_downward_for_e) then
286  zinterface(1) = 0.
287  do k = 1,nz
288  zinterface(k+1) = zinterface(k) - h1(k)
289  ! Adjust interface position to accommodate inflating layers
290  ! without disturbing the interface above
291  enddo
292  else
293  ! The rest of the model defines grids integrating up from the bottom
294  zinterface(nz+1) = -depth
295  do k = nz,1,-1
296  zinterface(k) = zinterface(k+1) + h1(k)
297  ! Adjust interface position to accommodate inflating layers
298  ! without disturbing the interface above
299  enddo
300  endif
301 
302 end subroutine build_rho_column_iteratively
303 
304 !> Copy column thicknesses with vanished layers removed
305 subroutine copy_finite_thicknesses(nk, h_in, threshold, nout, h_out, mapping)
306  integer, intent(in) :: nk !< Number of layer for h_in, T_in, S_in
307  real, dimension(nk), intent(in) :: h_in !< Thickness of input column
308  real, intent(in) :: threshold !< Thickness threshold defining vanished layers
309  integer, intent(out) :: nout !< Number of non-vanished layers
310  real, dimension(nk), intent(out) :: h_out !< Thickness of output column
311  integer, dimension(nk), intent(out) :: mapping !< Index of k-out corresponding to k-in
312  ! Local variables
313  integer :: k, k_thickest
314  real :: thickness_in_vanished, thickest_h_out
315 
316  ! Build up new grid
317  nout = 0
318  thickness_in_vanished = 0.0
319  thickest_h_out = h_in(1)
320  k_thickest = 1
321  do k = 1, nk
322  mapping(k) = nout ! Note k>=nout always
323  h_out(k) = 0. ! Make sure h_out is set everywhere
324  if (h_in(k) > threshold) then
325  ! For non-vanished layers
326  nout = nout + 1
327  mapping(nout) = k
328  h_out(nout) = h_in(k)
329  if (h_out(nout) > thickest_h_out) then
330  thickest_h_out = h_out(nout)
331  k_thickest = nout
332  endif
333  else
334  ! Add up mass in vanished layers
335  thickness_in_vanished = thickness_in_vanished + h_in(k)
336  endif
337  enddo
338 
339  ! No finite layers
340  if (nout <= 1) return
341 
342  ! Adjust for any lost volume in vanished layers
343  h_out(k_thickest) = h_out(k_thickest) + thickness_in_vanished
344 
345 end subroutine copy_finite_thicknesses
346 
347 !------------------------------------------------------------------------------
348 !> Inflate vanished layers to finite (nonzero) width
349 subroutine old_inflate_layers_1d( min_thickness, nk, h )
350 
351  ! Argument
352  real, intent(in) :: min_thickness !< Minimum allowed thickness [H ~> m or kg m-2]
353  integer, intent(in) :: nk !< Number of layers in the grid
354  real, dimension(:), intent(inout) :: h !< Layer thicknesses [H ~> m or kg m-2]
355 
356  ! Local variable
357  integer :: k
358  integer :: k_found
359  integer :: count_nonzero_layers
360  real :: delta
361  real :: correction
362  real :: maxthickness
363 
364  ! Count number of nonzero layers
365  count_nonzero_layers = 0
366  do k = 1,nk
367  if ( h(k) > min_thickness ) then
368  count_nonzero_layers = count_nonzero_layers + 1
369  endif
370  enddo
371 
372  ! If all layer thicknesses are greater than the threshold, exit routine
373  if ( count_nonzero_layers == nk ) return
374 
375  ! If all thicknesses are zero, inflate them all and exit
376  if ( count_nonzero_layers == 0 ) then
377  do k = 1,nk
378  h(k) = min_thickness
379  enddo
380  return
381  endif
382 
383  ! Inflate zero layers
384  correction = 0.0
385  do k = 1,nk
386  if ( h(k) <= min_thickness ) then
387  delta = min_thickness - h(k)
388  correction = correction + delta
389  h(k) = h(k) + delta
390  endif
391  enddo
392 
393  ! Modify thicknesses of nonzero layers to ensure volume conservation
394  maxthickness = h(1)
395  k_found = 1
396  do k = 1,nk
397  if ( h(k) > maxthickness ) then
398  maxthickness = h(k)
399  k_found = k
400  endif
401  enddo
402 
403  h(k_found) = h(k_found) - correction
404 
405 end subroutine old_inflate_layers_1d
406 
407 end module coord_rho
coord_rho
Regrid columns for the continuous isopycnal (rho) coordinate.
Definition: coord_rho.F90:2
mom_eos
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
coord_rho::rho_cs
Control structure containing required parameters for the rho coordinate.
Definition: coord_rho.F90:14
mom_remapping::remapping_cs
Container for remapping parameters.
Definition: MOM_remapping.F90:22
mom_remapping
Provides column-wise vertical remapping functions.
Definition: MOM_remapping.F90:2
regrid_interp
Vertical interpolation for regridding.
Definition: regrid_interp.F90:2
mom_eos::eos_type
A control structure for the equation of state.
Definition: MOM_EOS.F90:86
regrid_interp::interp_cs_type
Control structure for regrid_interp module.
Definition: regrid_interp.F90:23
mom_error_handler
Routines for error handling and I/O management.
Definition: MOM_error_handler.F90:2
mom_eos::calculate_density
Calculates density of sea water from T, S and P.
Definition: MOM_EOS.F90:60