MOM6
coord_slight.F90
Go to the documentation of this file.
1 !> Regrid columns for the SLight coordinate
3 
4 use mom_error_handler, only : mom_error, fatal
9 
10 implicit none ; private
11 
12 !> Control structure containing required parameters for the SLight coordinate
13 type, public :: slight_cs
14  private
15 
16  !> Number of layers/levels
17  integer :: nk
18 
19  !> Minimum thickness allowed when building the new grid through regridding
20  real :: min_thickness
21 
22  !> Reference pressure for potential density calculations (Pa)
23  real :: ref_pressure
24 
25  !> Fraction (between 0 and 1) of compressibility to add to potential density
26  !! profiles when interpolating for target grid positions. (nondim)
27  real :: compressibility_fraction = 0.
28 
29  ! The following 4 parameters were introduced for use with the SLight coordinate:
30  !> Depth over which to average to determine the mixed layer potential density (m)
31  real :: rho_ml_avg_depth = 1.0
32 
33  !> Number of layers to offset the mixed layer density to find resolved stratification (nondim)
34  real :: nlay_ml_offset = 2.0
35 
36  !> The number of fixed-thickess layers at the top of the model
37  integer :: nz_fixed_surface = 2
38 
39  !> The fixed resolution in the topmost SLight_nkml_min layers (m)
40  real :: dz_ml_min = 1.0
41 
42  !> If true, detect regions with much weaker stratification in the coordinate
43  !! than based on in-situ density, and use a stretched coordinate there.
44  logical :: fix_haloclines = .false.
45 
46  !> A length scale over which to filter T & S when looking for spuriously
47  !! unstable water mass profiles, in m.
48  real :: halocline_filter_length = 2.0
49 
50  !> A value of the stratification ratio that defines a problematic halocline region.
51  real :: halocline_strat_tol = 0.25
52 
53  !> Nominal density of interfaces
54  real, allocatable, dimension(:) :: target_density
55 
56  !> Maximum depths of interfaces
57  real, allocatable, dimension(:) :: max_interface_depths
58 
59  !> Maximum thicknesses of layers
60  real, allocatable, dimension(:) :: max_layer_thickness
61 
62  !> Interpolation control structure
63  type(interp_cs_type) :: interp_cs
64 end type slight_cs
65 
67 
68 contains
69 
70 !> Initialise a slight_CS with pointers to parameters
71 subroutine init_coord_slight(CS, nk, ref_pressure, target_density, interp_CS)
72  type(slight_cs), pointer :: CS !< Unassociated pointer to hold the control structure
73  integer, intent(in) :: nk
74  real, intent(in) :: ref_pressure
75  real, dimension(:), intent(in) :: target_density
76  type(interp_cs_type), intent(in) :: interp_CS
77 
78  if (associated(cs)) call mom_error(fatal, "init_coord_slight: CS already associated!")
79  allocate(cs)
80  allocate(cs%target_density(nk+1))
81 
82  cs%nk = nk
83  cs%ref_pressure = ref_pressure
84  cs%target_density(:) = target_density(:)
85  cs%interp_CS = interp_cs
86 end subroutine init_coord_slight
87 
88 subroutine end_coord_slight(CS)
89  type(slight_cs), pointer :: CS
90 
91  ! nothing to do
92  if (.not. associated(cs)) return
93  deallocate(cs%target_density)
94  deallocate(cs)
95 end subroutine end_coord_slight
96 
97 subroutine set_slight_params(CS, max_interface_depths, max_layer_thickness, &
98  min_thickness, compressibility_fraction, &
99  dz_ml_min, nz_fixed_surface, Rho_ML_avg_depth, nlay_ML_offset, fix_haloclines, &
100  halocline_filter_length, halocline_strat_tol, interp_CS)
101  type(slight_cs), pointer :: CS
102  real, optional, dimension(:), intent(in) :: max_interface_depths
103  real, optional, dimension(:), intent(in) :: max_layer_thickness
104  real, optional, intent(in) :: min_thickness
105  real, optional, intent(in) :: compressibility_fraction
106  real, optional, intent(in) :: dz_ml_min
107  integer, optional, intent(in) :: nz_fixed_surface
108  real, optional, intent(in) :: Rho_ML_avg_depth
109  real, optional, intent(in) :: nlay_ML_offset
110  logical, optional, intent(in) :: fix_haloclines
111  real, optional, intent(in) :: halocline_filter_length
112  real, optional, intent(in) :: halocline_strat_tol
113  type(interp_cs_type), optional, intent(in) :: interp_CS
114 
115  if (.not. associated(cs)) call mom_error(fatal, "set_slight_params: CS not associated")
116 
117  if (present(max_interface_depths)) then
118  if (size(max_interface_depths) /= cs%nk+1) &
119  call mom_error(fatal, "set_slight_params: max_interface_depths inconsistent size")
120  allocate(cs%max_interface_depths(cs%nk+1))
121  cs%max_interface_depths(:) = max_interface_depths(:)
122  endif
123 
124  if (present(max_layer_thickness)) then
125  if (size(max_layer_thickness) /= cs%nk) &
126  call mom_error(fatal, "set_slight_params: max_layer_thickness inconsistent size")
127  allocate(cs%max_layer_thickness(cs%nk))
128  cs%max_layer_thickness(:) = max_layer_thickness(:)
129  endif
130 
131  if (present(min_thickness)) cs%min_thickness = min_thickness
132  if (present(compressibility_fraction)) cs%compressibility_fraction = compressibility_fraction
133 
134  if (present(dz_ml_min)) cs%dz_ml_min = dz_ml_min
135  if (present(nz_fixed_surface)) cs%nz_fixed_surface = nz_fixed_surface
136  if (present(rho_ml_avg_depth)) cs%Rho_ML_avg_depth = rho_ml_avg_depth
137  if (present(nlay_ml_offset)) cs%nlay_ML_offset = nlay_ml_offset
138  if (present(fix_haloclines)) cs%fix_haloclines = fix_haloclines
139  if (present(halocline_filter_length)) cs%halocline_filter_length = halocline_filter_length
140  if (present(halocline_strat_tol)) then
141  if (halocline_strat_tol > 1.0) call mom_error(fatal, "set_slight_params: "//&
142  "HALOCLINE_STRAT_TOL must not exceed 1.0.")
143  cs%halocline_strat_tol = halocline_strat_tol
144  endif
145 
146  if (present(interp_cs)) cs%interp_CS = interp_cs
147 end subroutine set_slight_params
148 
149 !> Build a SLight coordinate column
150 subroutine build_slight_column(CS, eqn_of_state, H_to_Pa, m_to_H, H_subroundoff, &
151  nz, depth, h_col, T_col, S_col, p_col, z_col, z_col_new)
152  type(slight_cs), intent(in) :: CS !< Coordinate control structure
153  type(eos_type), pointer :: eqn_of_state !< Equation of state structure
154  real, intent(in) :: H_to_Pa !< GV%H_to_Pa
155  real, intent(in) :: m_to_H !< GV%m_to_H
156  real, intent(in) :: H_subroundoff !< GV%H_subroundoff
157  integer, intent(in) :: nz !< Number of levels
158  real, intent(in) :: depth !< Depth of ocean bottom (positive in m)
159  real, dimension(nz), intent(in) :: T_col, S_col !< T and S for column
160  real, dimension(nz), intent(in) :: h_col !< Layer thicknesses, in m
161  real, dimension(nz), intent(in) :: p_col !< Layer quantities
162  real, dimension(nz+1), intent(in) :: z_col !< Interface positions relative to the surface in H units (m or kg m-2)
163  real, dimension(nz+1), intent(inout) :: z_col_new !< Absolute positions of interfaces
164 
165  ! Local variables
166  real, dimension(nz) :: rho_col ! Layer quantities
167  real, dimension(nz) :: T_f, S_f ! Filtered ayer quantities
168  logical, dimension(nz+1) :: reliable ! If true, this interface is in a reliable position.
169  real, dimension(nz+1) :: T_int, S_int ! Temperature and salinity interpolated to interfaces.
170  real, dimension(nz+1) :: rho_tmp, drho_dp, p_IS, p_R
171  real, dimension(nz+1) :: drhoIS_dT, drhoIS_dS
172  real, dimension(nz+1) :: drhoR_dT, drhoR_dS
173  real, dimension(nz+1) :: strat_rat
174  real :: H_to_cPa
175  real :: drIS, drR, Fn_now, I_HStol, Fn_zero_val
176  real :: z_int_unst
177  real :: dz ! A uniform layer thickness in very shallow water, in H.
178  real :: dz_ur ! The total thickness of an unstable region, in H.
179  real :: wgt, cowgt ! A weight and its complement, nondim.
180  real :: rho_ml_av ! The average potential density in a near-surface region, in kg m-3.
181  real :: H_ml_av ! A thickness to try to use in taking the near-surface average, in H.
182  real :: rho_x_z ! A cumulative integral of a density, in kg m-3 H.
183  real :: z_wt ! The thickness actually used in taking the near-surface average, in H.
184  real :: k_interior ! The (real) value of k where the interior grid starts.
185  real :: k_int2 ! The (real) value of k where the interior grid starts.
186  real :: z_interior ! The depth where the interior grid starts, in H.
187  real :: z_ml_fix ! The depth at which the fixed-thickness near-surface layers end, in H.
188  real :: dz_dk ! The thickness of layers between the fixed-thickness
189  ! near-surface layars and the interior, in H.
190  real :: Lfilt ! A filtering lengthscale, in H.
191  logical :: maximum_depths_set ! If true, the maximum depths of interface have been set.
192  logical :: maximum_h_set ! If true, the maximum layer thicknesses have been set.
193  real :: k2_used, k2here, dz_sum, z_max
194  integer :: k2
195  real :: h_tr, b_denom_1, b1, d1 ! Temporary variables used by the tridiagonal solver.
196  real, dimension(nz) :: c1 ! Temporary variables used by the tridiagonal solver.
197  integer :: kur1, kur2 ! The indicies at the top and bottom of an unreliable region.
198  integer :: kur_ss ! The index to start with in the search for the next unstable region.
199  integer :: i, j, k, nkml
200 
201  maximum_depths_set = allocated(cs%max_interface_depths)
202  maximum_h_set = allocated(cs%max_layer_thickness)
203 
204  if (z_col(nz+1) - z_col(1) < nz*cs%min_thickness) then
205  ! This is a nearly massless total depth, so distribute the water evenly.
206  dz = (z_col(nz+1) - z_col(1)) / real(nz)
207  do k=2,nz ; z_col_new(k) = z_col(1) + dz*real(K-1) ; enddo
208  else
209  call calculate_density(t_col, s_col, p_col, rho_col, 1, nz, &
210  eqn_of_state)
211 
212  ! Find the locations of the target potential densities, flagging
213  ! locations in apparently unstable regions as not reliable.
214  call rho_interfaces_col(rho_col, h_col, z_col, cs%target_density, nz, &
215  z_col_new, cs, reliable, debug=.true.)
216 
217  ! Ensure that the interfaces are at least CS%min_thickness apart.
218  if (cs%min_thickness > 0.0) then
219  ! Move down interfaces below overly thin layers.
220  do k=2,nz ; if (z_col_new(k) < z_col_new(k-1) + cs%min_thickness) then
221  z_col_new(k) = z_col_new(k-1) + cs%min_thickness
222  endif ; enddo
223  ! Now move up any interfaces that are too close to the bottom.
224  do k=nz,2,-1 ; if (z_col_new(k) > z_col_new(k+1) - cs%min_thickness) then
225  z_col_new(k) = z_col_new(k+1) - cs%min_thickness
226  else
227  exit ! No more interfaces can be too close to the bottom.
228  endif ; enddo
229  endif
230 
231  ! Fix up the unreliable regions.
232  kur_ss = 2 ! reliable(1) and reliable(nz+1) must always be true.
233  do
234  ! Search for the uppermost unreliable interface postion.
235  kur1 = nz+2
236  do k=kur_ss,nz ; if (.not.reliable(k)) then
237  kur1 = k ; exit
238  endif ; enddo
239  if (kur1 > nz) exit ! Everything is now reliable.
240 
241  kur2 = kur1-1 ! For error checking.
242  do k=kur1+1,nz+1 ; if (reliable(k)) then
243  kur2 = k-1 ; kur_ss = k ; exit
244  endif ; enddo
245  if (kur2 < kur1) call mom_error(fatal, "Bad unreliable range.")
246 
247  dz_ur = z_col_new(kur2+1) - z_col_new(kur1-1)
248  ! drho = CS%target_density(kur2+1) - CS%target_density(kur1-1)
249  ! Perhaps reset the wgt and cowgt depending on how bad the old interface
250  ! locations were.
251  wgt = 1.0 ; cowgt = 0.0 ! = 1.0-wgt
252  do k=kur1,kur2
253  z_col_new(k) = cowgt*z_col_new(k) + &
254  wgt * (z_col_new(kur1-1) + dz_ur*(k - (kur1-1)) / ((kur2 - kur1) + 2))
255  enddo
256  enddo
257 
258  ! Determine which interfaces are in the s-space region and the depth extent
259  ! of this region.
260  z_wt = 0.0 ; rho_x_z = 0.0
261  h_ml_av = m_to_h*cs%Rho_ml_avg_depth
262  do k=1,nz
263  if (z_wt + h_col(k) >= h_ml_av) then
264  rho_x_z = rho_x_z + rho_col(k) * (h_ml_av - z_wt)
265  z_wt = h_ml_av
266  exit
267  else
268  rho_x_z = rho_x_z + rho_col(k) * h_col(k)
269  z_wt = z_wt + h_col(k)
270  endif
271  enddo
272  if (z_wt > 0.0) rho_ml_av = rho_x_z / z_wt
273 
274  nkml = cs%nz_fixed_surface
275  ! Find the interface that matches rho_ml_av.
276  if (rho_ml_av <= cs%target_density(nkml)) then
277  k_interior = cs%nlay_ml_offset + real(nkml)
278  elseif (rho_ml_av > cs%target_density(nz+1)) then
279  k_interior = real(nz+1)
280  else ; do k=nkml,nz
281  if ((rho_ml_av >= cs%target_density(k)) .and. &
282  (rho_ml_av < cs%target_density(k+1))) then
283  k_interior = (cs%nlay_ml_offset + k) + &
284  (rho_ml_av - cs%target_density(k)) / &
285  (cs%target_density(k+1) - cs%target_density(k))
286  exit
287  endif
288  enddo ; endif
289  if (k_interior > real(nz+1)) k_interior = real(nz+1)
290 
291  ! Linearly interpolate to find z_interior. This could be made more sophisticated.
292  k = int(ceiling(k_interior))
293  z_interior = (k-k_interior)*z_col_new(k-1) + (1.0+(k_interior-k))*z_col_new(k)
294 
295  if (cs%fix_haloclines) then
296  ! ! Identify regions above the reference pressure where the chosen
297  ! ! potential density significantly underestimates the actual
298  ! ! stratification, and use these to find a second estimate of
299  ! ! z_int_unst and k_interior.
300 
301  if (cs%halocline_filter_length > 0.0) then
302  lfilt = cs%halocline_filter_length*m_to_h
303 
304  ! Filter the temperature and salnity with a fixed lengthscale.
305  h_tr = h_col(1) + h_subroundoff
306  b1 = 1.0 / (h_tr + lfilt) ; d1 = h_tr * b1
307  t_f(1) = (b1*h_tr)*t_col(1) ; s_f(1) = (b1*h_tr)*s_col(1)
308  do k=2,nz
309  c1(k) = lfilt * b1
310  h_tr = h_col(k) + h_subroundoff ; b_denom_1 = h_tr + d1*lfilt
311  b1 = 1.0 / (b_denom_1 + lfilt) ; d1 = b_denom_1 * b1
312  t_f(k) = b1 * (h_tr*t_col(k) + lfilt*t_f(k-1))
313  s_f(k) = b1 * (h_tr*s_col(k) + lfilt*s_f(k-1))
314  enddo
315  do k=nz-1,1,-1
316  t_f(k) = t_f(k) + c1(k+1)*t_f(k+1) ; s_f(k) = s_f(k) + c1(k+1)*s_f(k+1)
317  enddo
318  else
319  do k=1,nz ; t_f(k) = t_col(k) ; s_f(k) = s_col(k) ; enddo
320  endif
321 
322  t_int(1) = t_f(1) ; s_int(1) = s_f(1)
323  do k=2,nz
324  t_int(k) = 0.5*(t_f(k-1) + t_f(k)) ; s_int(k) = 0.5*(s_f(k-1) + s_f(k))
325  p_is(k) = z_col(k) * h_to_pa
326  p_r(k) = cs%ref_pressure + cs%compressibility_fraction * ( p_is(k) - cs%ref_pressure )
327  enddo
328  t_int(nz+1) = t_f(nz) ; s_int(nz+1) = s_f(nz)
329  p_is(nz+1) = z_col(nz+1) * h_to_pa
330  call calculate_density_derivs(t_int, s_int, p_is, drhois_dt, drhois_ds, 2, nz-1, &
331  eqn_of_state)
332  call calculate_density_derivs(t_int, s_int, p_r, drhor_dt, drhor_ds, 2, nz-1, &
333  eqn_of_state)
334  if (cs%compressibility_fraction > 0.0) then
335  call calculate_compress(t_int, s_int, p_r, rho_tmp, drho_dp, 2, nz-1, &
336  eqn_of_state)
337  else
338  do k=2,nz ; drho_dp(k) = 0.0 ; enddo
339  endif
340 
341  h_to_cpa = cs%compressibility_fraction*h_to_pa
342  strat_rat(1) = 1.0
343  do k=2,nz
344  dris = drhois_dt(k) * (t_f(k) - t_f(k-1)) + &
345  drhois_ds(k) * (s_f(k) - s_f(k-1))
346  drr = (drhor_dt(k) * (t_f(k) - t_f(k-1)) + &
347  drhor_ds(k) * (s_f(k) - s_f(k-1))) + &
348  drho_dp(k) * (h_to_cpa*0.5*(h_col(k) + h_col(k-1)))
349 
350  if (dris <= 0.0) then
351  strat_rat(k) = 2.0 ! Maybe do this? => ; if (drR < 0.0) strat_rat(K) = -2.0
352  else
353  strat_rat(k) = 2.0*max(drr,0.0) / (dris + abs(drr))
354  endif
355  enddo
356  strat_rat(nz+1) = 1.0
357 
358  z_int_unst = 0.0 ; fn_now = 0.0
359  fn_zero_val = min(2.0*cs%halocline_strat_tol, &
360  0.5*(1.0 + cs%halocline_strat_tol))
361  if (cs%halocline_strat_tol > 0.0) then
362  ! Use Adcroft's reciprocal rule.
363  i_hstol = 0.0 ; if (fn_zero_val - cs%halocline_strat_tol > 0.0) &
364  i_hstol = 1.0 / (fn_zero_val - cs%halocline_strat_tol)
365  do k=nz,1,-1 ; if (cs%ref_pressure > p_is(k+1)) then
366  z_int_unst = z_int_unst + fn_now * h_col(k)
367  if (strat_rat(k) <= fn_zero_val) then
368  if (strat_rat(k) <= cs%halocline_strat_tol) then ; fn_now = 1.0
369  else
370  fn_now = max(fn_now, (fn_zero_val - strat_rat(k)) * i_hstol)
371  endif
372  endif
373  endif ; enddo
374  else
375  do k=nz,1,-1 ; if (cs%ref_pressure > p_is(k+1)) then
376  z_int_unst = z_int_unst + fn_now * h_col(k)
377  if (strat_rat(k) <= cs%halocline_strat_tol) fn_now = 1.0
378  endif ; enddo
379  endif
380 
381  if (z_interior < z_int_unst) then
382  ! Find a second estimate of the extent of the s-coordinate region.
383  kur1 = max(int(ceiling(k_interior)),2)
384  if (z_col_new(kur1-1) < z_interior) then
385  k_int2 = kur1
386  do k = kur1,nz+1 ; if (z_col_new(k) >= z_int_unst) then
387  ! This is linear interpolation again.
388  if (z_col_new(k-1) >= z_int_unst) &
389  call mom_error(fatal,"build_grid_SLight, bad halocline structure.")
390  k_int2 = real(K-1) + (z_int_unst - z_col_new(k-1)) / &
391  (z_col_new(k) - z_col_new(k-1))
392  exit
393  endif ; enddo
394  if (z_col_new(nz+1) < z_int_unst) then
395  ! This should be unnecessary.
396  z_int_unst = z_col_new(nz+1) ; k_int2 = real(nz+1)
397  endif
398 
399  ! Now take the larger values.
400  if (k_int2 > k_interior) then
401  k_interior = k_int2 ; z_interior = z_int_unst
402  endif
403  endif
404  endif
405  endif ! fix_haloclines
406 
407  z_col_new(1) = 0.0
408  do k=2,nkml+1
409  z_col_new(k) = min((k-1)*cs%dz_ml_min, &
410  z_col_new(nz+1) - cs%min_thickness*(nz+1-k))
411  enddo
412  z_ml_fix = z_col_new(nkml+1)
413  if (z_interior > z_ml_fix) then
414  dz_dk = (z_interior - z_ml_fix) / (k_interior - (nkml+1))
415  do k=nkml+2,int(floor(k_interior))
416  z_col_new(k) = z_ml_fix + dz_dk * (k - (nkml+1))
417  enddo
418  else ! The fixed-thickness z-region penetrates into the interior.
419  do k=nkml+2,nz
420  if (z_col_new(k) <= z_col_new(cs%nz_fixed_surface+1)) then
421  z_col_new(k) = z_col_new(cs%nz_fixed_surface+1)
422  else ; exit ; endif
423  enddo
424  endif
425 
426  if (maximum_depths_set .and. maximum_h_set) then ; do k=2,nz
427  ! The loop bounds are 2 & nz so the top and bottom interfaces do not move.
428  ! Recall that z_col_new is positive downward.
429  z_col_new(k) = min(z_col_new(k), cs%max_interface_depths(k), &
430  z_col_new(k-1) + cs%max_layer_thickness(k-1))
431  enddo ; elseif (maximum_depths_set) then ; do k=2,nz
432  z_col_new(k) = min(z_col_new(k), cs%max_interface_depths(k))
433  enddo ; elseif (maximum_h_set) then ; do k=2,nz
434  z_col_new(k) = min(z_col_new(k), z_col_new(k-1) + cs%max_layer_thickness(k-1))
435  enddo ; endif
436 
437  endif ! Total thickness exceeds nz*CS%min_thickness.
438 
439 end subroutine build_slight_column
440 
441 !> Finds the new interface locations in a column of water that match the
442 !! prescribed target densities.
443 subroutine rho_interfaces_col(rho_col, h_col, z_col, rho_tgt, nz, z_col_new, &
444  CS, reliable, debug)
445  integer, intent(in) :: nz !< Number of layers
446  real, dimension(nz), intent(in) :: rho_col !< Initial layer reference densities.
447  real, dimension(nz), intent(in) :: h_col !< Initial layer thicknesses.
448  real, dimension(nz+1), intent(in) :: z_col !< Initial interface heights.
449  real, dimension(nz+1), intent(in) :: rho_tgt !< Interface target densities.
450  real, dimension(nz+1), intent(inout) :: z_col_new !< New interface heights.
451  type(slight_cs), intent(in) :: CS !< Coordinate control structure
452  logical, dimension(nz+1), intent(inout) :: reliable !< If true, the interface positions
453  !! are well defined from a stable region.
454  logical, optional, intent(in) :: debug !< If present and true, do debugging checks.
455 
456  real, dimension(nz+1) :: ru_max_int ! The maximum and minimum densities in
457  real, dimension(nz+1) :: ru_min_int ! an unstable region around an interface.
458  real, dimension(nz) :: ru_max_lay ! The maximum and minimum densities in
459  real, dimension(nz) :: ru_min_lay ! an unstable region containing a layer.
460  real, dimension(nz,2) :: ppoly_i_E ! Edge value of polynomial
461  real, dimension(nz,2) :: ppoly_i_S ! Edge slope of polynomial
462  real, dimension(nz,DEGREE_MAX+1) :: ppoly_i_coefficients ! Coefficients of polynomial
463  logical, dimension(nz) :: unstable_lay ! If true, this layer is in an unstable region.
464  logical, dimension(nz+1) :: unstable_int ! If true, this interface is in an unstable region.
465  real :: rt ! The current target density, in kg m-3.
466  real :: zf ! The fractional z-position within a layer of the target density.
467  real :: rfn
468  real :: a(5) ! Coefficients of a local polynomial minus the target density.
469  real :: zf1, zf2, rfn1, rfn2
470  real :: drfn_dzf, sgn, delta_zf, zf_prev
471  real :: tol
472  logical :: k_found ! If true, the position has been found.
473  integer :: k_layer ! The index of the stable layer containing an interface.
474  integer :: ppoly_degree
475  integer :: k, k1, k1_min, itt, max_itt, m
476 
477  real :: z_sgn ! 1 or -1, depending on whether z increases with increasing K.
478  logical :: debugging
479 
480  debugging = .false. ; if (present(debug)) debugging = debug
481  max_itt = nr_iterations
482  tol = nr_tolerance
483 
484  z_sgn = 1.0 ; if ( z_col(1) > z_col(nz+1) ) z_sgn = -1.0
485  if (debugging) then
486  do k=1,nz
487  if (abs((z_col(k+1) - z_col(k)) - z_sgn*h_col(k)) > &
488  1.0e-14*(abs(z_col(k+1)) + abs(z_col(k)) + abs(h_col(k))) ) &
489  call mom_error(fatal, "rho_interfaces_col: Inconsistent z_col and h_col")
490  enddo
491  endif
492 
493  if ( z_col(1) == z_col(nz+1) ) then
494  ! This is a massless column!
495  do k=1,nz+1 ; z_col_new(k) = z_col(1) ; reliable(k) = .true. ; enddo
496  return
497  endif
498 
499  ! This sets up the piecewise polynomials based on the rho_col profile.
500  call regridding_set_ppolys(cs%interp_CS, rho_col, nz, h_col, ppoly_i_e, ppoly_i_s, &
501  ppoly_i_coefficients, ppoly_degree)
502 
503  ! Determine the density ranges of unstably stratified segments.
504  ! Interfaces that start out in an unstably stratified segment can
505  ! only escape if they are outside of the bounds of that segment, and no
506  ! interfaces are ever mapped into an unstable segment.
507  unstable_int(1) = .false.
508  ru_max_int(1) = ppoly_i_e(1,1)
509 
510  unstable_lay(1) = (ppoly_i_e(1,1) > ppoly_i_e(1,2))
511  ru_max_lay(1) = max(ppoly_i_e(1,1), ppoly_i_e(1,2))
512 
513  do k=2,nz
514  unstable_int(k) = (ppoly_i_e(k-1,2) > ppoly_i_e(k,1))
515  ru_max_int(k) = max(ppoly_i_e(k-1,2), ppoly_i_e(k,1))
516  ru_min_int(k) = min(ppoly_i_e(k-1,2), ppoly_i_e(k,1))
517  if (unstable_int(k) .and. unstable_lay(k-1)) &
518  ru_max_int(k) = max(ru_max_lay(k-1), ru_max_int(k))
519 
520  unstable_lay(k) = (ppoly_i_e(k,1) > ppoly_i_e(k,2))
521  ru_max_lay(k) = max(ppoly_i_e(k,1), ppoly_i_e(k,2))
522  ru_min_lay(k) = min(ppoly_i_e(k,1), ppoly_i_e(k,2))
523  if (unstable_lay(k) .and. unstable_int(k)) &
524  ru_max_lay(k) = max(ru_max_int(k), ru_max_lay(k))
525  enddo
526  unstable_int(nz+1) = .false.
527  ru_min_int(nz+1) = ppoly_i_e(nz,2)
528 
529  do k=nz,1,-1
530  if (unstable_lay(k) .and. unstable_int(k+1)) &
531  ru_min_lay(k) = min(ru_min_int(k+1), ru_min_lay(k))
532 
533  if (unstable_int(k) .and. unstable_lay(k)) &
534  ru_min_int(k) = min(ru_min_lay(k), ru_min_int(k))
535  enddo
536 
537  z_col_new(1) = z_col(1) ; reliable(1) = .true.
538  k1_min = 1
539  do k=2,nz ! Find the locations of the various target densities for the interfaces.
540  rt = rho_tgt(k)
541  k_layer = -1
542  k_found = .false.
543 
544  ! Many light layers are found at the top, so start there.
545  if (rt <= ppoly_i_e(k1_min,1)) then
546  z_col_new(k) = z_col(k1_min)
547  k_found = .true.
548  ! Do not change k1_min for the next layer.
549  elseif (k1_min == nz+1) then
550  z_col_new(k) = z_col(nz+1)
551  else
552  ! Start with the previous location and search outward.
553  if (unstable_int(k) .and. (rt >= ru_min_int(k)) .and. (rt <= ru_max_int(k))) then
554  ! This interface started in an unstable region and should not move due to remapping.
555  z_col_new(k) = z_col(k) ; reliable(k) = .false.
556  k1_min = k ; k_found = .true.
557  elseif ((rt >= ppoly_i_e(k-1,2)) .and. (rt <= ppoly_i_e(k,1))) then
558  ! This interface is already in the right place and does not move.
559  z_col_new(k) = z_col(k) ; reliable(k) = .true.
560  k1_min = k ; k_found = .true.
561  elseif (rt < ppoly_i_e(k-1,2)) then ! Search upward
562  do k1=k-1,k1_min,-1
563  ! Check whether rt is in layer k.
564  if ((rt < ppoly_i_e(k1,2)) .and. (rt > ppoly_i_e(k1,1))) then
565  ! rt is in layer k.
566  k_layer = k1
567  k1_min = k1 ; k_found = .true. ; exit
568  elseif (unstable_lay(k1) .and. (rt >= ru_min_lay(k1)) .and. (rt <= ru_max_lay(k1))) then
569  ! rt would be found at unstable layer that it can not penetrate.
570  ! It is possible that this can never happen?
571  z_col_new(k) = z_col(k1+1) ; reliable(k) = .false.
572  k1_min = k1 ; k_found = .true. ; exit
573  endif
574  ! Check whether rt is at interface K.
575  if (k1 > 1) then ; if ((rt <= ppoly_i_e(k1,1)) .and. (rt >= ppoly_i_e(k1-1,2))) then
576  ! rt is at interface K1
577  z_col_new(k) = z_col(k1) ; reliable(k) = .true.
578  k1_min = k1 ; k_found = .true. ; exit
579  elseif (unstable_int(k1) .and. (rt >= ru_min_int(k1)) .and. (rt <= ru_max_int(k1))) then
580  ! rt would be found at an unstable interface that it can not pass.
581  ! It is possible that this can never happen?
582  z_col_new(k) = z_col(k1) ; reliable(k) = .false.
583  k1_min = k1 ; k_found = .true. ; exit
584  endif ; endif
585  enddo
586 
587  if (.not.k_found) then
588  ! This should not happen unless k1_min = 1.
589  if (k1_min < 2) then
590  z_col_new(k) = z_col(k1_min)
591  else
592  z_col_new(k) = z_col(k1_min)
593  endif
594  endif
595 
596  else ! Search downward
597  do k1=k,nz
598  if ((rt < ppoly_i_e(k1,2)) .and. (rt > ppoly_i_e(k1,1))) then
599  ! rt is in layer k.
600  k_layer = k1
601  k1_min = k1 ; k_found = .true. ; exit
602  elseif (unstable_lay(k1) .and. (rt >= ru_min_lay(k1)) .and. (rt <= ru_max_lay(k1))) then
603  ! rt would be found at unstable layer that it can not penetrate.
604  ! It is possible that this can never happen?
605  z_col_new(k) = z_col(k1)
606  reliable(k) = .false.
607  k1_min = k1 ; k_found = .true. ; exit
608  endif
609  if (k1 < nz) then ; if ((rt <= ppoly_i_e(k1+1,1)) .and. (rt >= ppoly_i_e(k1,2))) then
610  ! rt is at interface K1+1
611 
612  z_col_new(k) = z_col(k1+1) ; reliable(k) = .true.
613  k1_min = k1+1 ; k_found = .true. ; exit
614  elseif (unstable_int(k1+1) .and. (rt >= ru_min_int(k1+1)) .and. (rt <= ru_max_int(k1+1))) then
615  ! rt would be found at an unstable interface that it can not pass.
616  ! It is possible that this can never happen?
617  z_col_new(k) = z_col(k1+1)
618  reliable(k) = .false.
619  k1_min = k1+1 ; k_found = .true. ; exit
620  endif ; endif
621  enddo
622  if (.not.k_found) then
623  z_col_new(k) = z_col(nz+1)
624  if (rt >= ppoly_i_e(nz,2)) then
625  reliable(k) = .true.
626  else
627  reliable(k) = .false.
628  endif
629  endif
630  endif
631 
632  if (k_layer > 0) then ! The new location is inside of layer k_layer.
633  ! Note that this is coded assuming that this layer is stably stratified.
634  if (.not.(ppoly_i_e(k1,2) > ppoly_i_e(k1,1))) call mom_error(fatal, &
635  "build_grid_SLight: Erroneously searching for an interface in an unstratified layer.") !### COMMENT OUT LATER?
636 
637  ! Use the false position method to find the location (degree <= 1) or the first guess.
638  zf = (rt - ppoly_i_e(k1,1)) / (ppoly_i_e(k1,2) - ppoly_i_e(k1,1))
639 
640  if (ppoly_degree > 1) then ! Iterate to find the solution.
641  a(:) = 0.0 ; a(1) = ppoly_i_coefficients(k_layer,1) - rt
642  do m=2,ppoly_degree+1 ; a(m) = ppoly_i_coefficients(k_layer,m) ; enddo
643  ! Bracket the root.
644  zf1 = 0.0 ; rfn1 = a(1)
645  zf2 = 1.0 ; rfn2 = a(1) + (a(2) + (a(3) + (a(4) + a(5))))
646  if (rfn1 * rfn2 > 0.0) call mom_error(fatal, "build_grid_SLight: Bad bracketing.") !### COMMENT OUT LATER?
647 
648  do itt=1,max_itt
649  rfn = a(1) + zf*(a(2) + zf*(a(3) + zf*(a(4) + zf*a(5))))
650  ! Reset one of the ends of the bracket.
651  if (rfn * rfn1 > 0.0) then
652  zf1 = zf ; rfn1 = rfn
653  else
654  zf2 = zf ; rfn2 = rfn
655  endif
656  if (rfn1 == rfn2) exit
657 
658  drfn_dzf = (a(2) + zf*(2.0*a(3) + zf*(3.0*a(4) + zf*4.0*a(5))))
659  sgn = 1.0 ; if (drfn_dzf < 0.0) sgn = -1.0
660 
661  if ((sgn*(zf - rfn) >= zf1 * abs(drfn_dzf)) .and. &
662  (sgn*(zf - rfn) <= zf2 * abs(drfn_dzf))) then
663  delta_zf = -rfn / drfn_dzf
664  zf = zf + delta_zf
665  else ! Newton's method goes out of bounds, so use a false position method estimate
666  zf_prev = zf
667  zf = ( rfn2 * zf1 - rfn1 * zf2 ) / (rfn2 - rfn1)
668  delta_zf = zf - zf_prev
669  endif
670 
671  if (abs(delta_zf) < tol) exit
672  enddo
673  endif
674  z_col_new(k) = z_col(k_layer) + zf * z_sgn * h_col(k_layer)
675  reliable(k) = .true.
676  endif
677 
678  endif
679 
680  enddo
681  z_col_new(nz+1) = z_col(nz+1) ; reliable(nz+1) = .true.
682 
683 end subroutine rho_interfaces_col
684 
685 end module coord_slight
subroutine, public end_coord_slight(CS)
Calculates density of sea water from T, S and P.
Definition: MOM_EOS.F90:45
subroutine, public calculate_compress(T, S, pressure, rho, drho_dp, start, npts, EOS)
Calls the appropriate subroutine to calculate the density and compressibility for 1-D array inputs...
Definition: MOM_EOS.F90:293
subroutine, public set_slight_params(CS, max_interface_depths, max_layer_thickness, min_thickness, compressibility_fraction, dz_ml_min, nz_fixed_surface, Rho_ML_avg_depth, nlay_ML_offset, fix_haloclines, halocline_filter_length, halocline_strat_tol, interp_CS)
subroutine, public init_coord_slight(CS, nk, ref_pressure, target_density, interp_CS)
Initialise a slight_CS with pointers to parameters.
subroutine, public regridding_set_ppolys(CS, densities, n0, h0, ppoly0_E, ppoly0_S, ppoly0_coefficients, degree)
Given the set of target values and cell densities, this routine builds an interpolated profile for th...
subroutine, public calculate_density_derivs(T, S, pressure, drho_dT, drho_dS, start, npts, EOS)
Calls the appropriate subroutine to calculate density derivatives for 1-D array inputs.
Definition: MOM_EOS.F90:214
Control structure containing required parameters for the SLight coordinate.
integer, parameter, public nr_iterations
Maximum number of Newton-Raphson iterations. Newton-Raphson iterations are used to build the new grid...
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
integer, parameter, public degree_max
subroutine rho_interfaces_col(rho_col, h_col, z_col, rho_tgt, nz, z_col_new, CS, reliable, debug)
Finds the new interface locations in a column of water that match the prescribed target densities...
real, parameter, public nr_tolerance
Tolerance for Newton-Raphson iterations (stop when increment falls below this)
Regrid columns for the SLight coordinate.
Definition: coord_slight.F90:2
subroutine, public mom_error(level, message, all_print)
subroutine, public build_slight_column(CS, eqn_of_state, H_to_Pa, m_to_H, H_subroundoff, nz, depth, h_col, T_col, S_col, p_col, z_col, z_col_new)
Build a SLight coordinate column.
A control structure for the equation of state.
Definition: MOM_EOS.F90:55