MOM6
user_change_diffusivity Module Reference

Detailed Description

By Robert Hallberg, May 2012.

This file contains a subroutine that increments the diapycnal diffusivity in a specified band of latitudes and densities.

A small fragment of the grid is shown below:

j+1 x ^ x ^ x At x: q j+1 > o > o > At ^: v j x ^ x ^ x At >: u j > o > o > At o: h, T, S, Kd, etc. j-1 x ^ x ^ x i-1 i i+1 At x & ^: i i+1 At > & o:

The boundaries always run through q grid points (x).

Data Types

type  user_change_diff_cs
 

Functions/Subroutines

subroutine, public user_change_diff (h, tv, G, CS, Kd, Kd_int, T_f, S_f, Kd_int_add)
 This subroutine provides an interface for a user to use to modify the main code to alter the diffusivities as needed. The specific example implemented here augments the diffusivity for a specified range of latitude and coordinate potential density. More...
 
logical function range_ok (range)
 This subroutine checks whether the 4 values of range are in ascending order. More...
 
real function val_weights (val, range)
 This subroutine returns a value that goes smoothly from 0 to 1, stays at 1, and then goes smoothly back to 0 at the four values of range. The transitions are cubic, and have zero first derivatives where the curves hit 0 and 1. The values in range must be in ascending order, as can be checked by calling range_OK. More...
 
subroutine, public user_change_diff_init (Time, G, param_file, diag, CS)
 Set up the module control structure. More...
 
subroutine, public user_change_diff_end (CS)
 Clean up the module control structure. More...
 

Function/Subroutine Documentation

◆ range_ok()

logical function user_change_diffusivity::range_ok ( real, dimension(4), intent(in)  range)
private

This subroutine checks whether the 4 values of range are in ascending order.

Parameters
[in]rangeFour values to check.
Returns
Return value.

Definition at line 160 of file user_change_diffusivity.F90.

Referenced by user_change_diff(), and user_change_diff_init().

160  real, dimension(4), intent(in) :: range !< Four values to check.
161  logical :: ok !< Return value.
162 
163 
164  ok = ((range(1) <= range(2)) .and. (range(2) <= range(3)) .and. &
165  (range(3) <= range(4)))
166 
Here is the caller graph for this function:

◆ user_change_diff()

subroutine, public user_change_diffusivity::user_change_diff ( real, dimension(szi_(g),szj_(g),szk_(g)), intent(in)  h,
type(thermo_var_ptrs), intent(in)  tv,
type(ocean_grid_type), intent(in)  G,
type(user_change_diff_cs), pointer  CS,
real, dimension( g %isd: g %ied, g %jsd: g %jed, g %ke), intent(inout), optional  Kd,
real, dimension( g %isd: g %ied, g %jsd: g %jed, g %ke+1), intent(inout), optional  Kd_int,
real, dimension( g %isd: g %ied, g %jsd: g %jed, g %ke), intent(in), optional  T_f,
real, dimension( g %isd: g %ied, g %jsd: g %jed, g %ke), intent(in), optional  S_f,
real, dimension(:,:,:), optional, pointer  Kd_int_add 
)

This subroutine provides an interface for a user to use to modify the main code to alter the diffusivities as needed. The specific example implemented here augments the diffusivity for a specified range of latitude and coordinate potential density.

Parameters
[in]gThe ocean's grid structure.
[in]hLayer thickness, in m or kg m-2.
[in]tvA structure containing pointers to any available thermodynamic fields. Absent fields have NULL ptrs.
csThis module's control structure.
[in,out]kdThe diapycnal diffusivity of each layer in m2 s-1.
[in,out]kd_intThe diapycnal diffusivity at each interface in m2 s-1.
[in]t_fTemperature with massless layers filled in vertically.
[in]s_fSalinity with massless layers filled in vertically.
kd_int_addThe diapycnal diffusivity that is being added at each interface in m2 s-1.

Definition at line 57 of file user_change_diffusivity.F90.

References mom_error_handler::mom_error(), range_ok(), and val_weights().

57  type(ocean_grid_type), intent(in) :: g !< The ocean's grid structure.
58  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thickness, in m or kg m-2.
59  type(thermo_var_ptrs), intent(in) :: tv !< A structure containing pointers
60  !! to any available thermodynamic
61  !! fields. Absent fields have NULL ptrs.
62  type(user_change_diff_cs), pointer :: cs !< This module's control structure.
63  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), optional, intent(inout) :: kd !< The diapycnal diffusivity of
64  !! each layer in m2 s-1.
65  real, dimension(SZI_(G),SZJ_(G),SZK_(G)+1), optional, intent(inout) :: kd_int !< The diapycnal diffusivity
66  !! at each interface in m2 s-1.
67  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), optional, intent(in) :: t_f !< Temperature with massless
68  !! layers filled in vertically.
69  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), optional, intent(in) :: s_f !< Salinity with massless
70  !! layers filled in vertically.
71  real, dimension(:,:,:), optional, pointer :: kd_int_add !< The diapycnal
72  !! diffusivity that is being added at
73  !! each interface in m2 s-1.
74 
75  real :: rcv(szi_(g),szk_(g)) ! The coordinate density in layers in kg m-3.
76  real :: p_ref(szi_(g)) ! An array of tv%P_Ref pressures.
77  real :: rho_fn ! The density dependence of the input function, 0-1, ND.
78  real :: lat_fn ! The latitude dependence of the input function, 0-1, ND.
79  logical :: use_eos ! If true, density is calculated from T & S using an
80  ! equation of state.
81  logical :: store_kd_add ! Save the added diffusivity as a diagnostic if true.
82  integer :: i, j, k, is, ie, js, je, nz
83  integer :: isd, ied, jsd, jed
84 
85  real :: kappa_fill ! diffusivity used to fill massless layers
86  real :: dt_fill ! timestep used to fill massless layers
87  character(len=200) :: mesg
88 
89  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
90  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
91 
92  if (.not.associated(cs)) call mom_error(fatal,"user_set_diffusivity: "//&
93  "Module must be initialized before it is used.")
94 
95  use_eos = associated(tv%eqn_of_state)
96  if (.not.use_eos) return
97  store_kd_add = .false.
98  if (present(kd_int_add)) store_kd_add = associated(kd_int_add)
99 
100  if (.not.range_ok(cs%lat_range)) then
101  write(mesg, '(4(1pe15.6))') cs%lat_range(1:4)
102  call mom_error(fatal, "user_set_diffusivity: bad latitude range: \n "//&
103  trim(mesg))
104  endif
105  if (.not.range_ok(cs%rho_range)) then
106  write(mesg, '(4(1pe15.6))') cs%rho_range(1:4)
107  call mom_error(fatal, "user_set_diffusivity: bad density range: \n "//&
108  trim(mesg))
109  endif
110 
111  if (store_kd_add) kd_int_add(:,:,:) = 0.0
112 
113  do i=is,ie ; p_ref(i) = tv%P_Ref ; enddo
114  do j=js,je
115  if (present(t_f) .and. present(s_f)) then
116  do k=1,nz
117  call calculate_density(t_f(:,j,k),s_f(:,j,k),p_ref,rcv(:,k),&
118  is,ie-is+1,tv%eqn_of_state)
119  enddo
120  else
121  do k=1,nz
122  call calculate_density(tv%T(:,j,k),tv%S(:,j,k),p_ref,rcv(:,k),&
123  is,ie-is+1,tv%eqn_of_state)
124  enddo
125  endif
126 
127  if (present(kd)) then
128  do k=1,nz ; do i=is,ie
129  if (cs%use_abs_lat) then
130  lat_fn = val_weights(abs(g%geoLatT(i,j)), cs%lat_range)
131  else
132  lat_fn = val_weights(g%geoLatT(i,j), cs%lat_range)
133  endif
134  rho_fn = val_weights(rcv(i,k), cs%rho_range)
135  if (rho_fn * lat_fn > 0.0) &
136  kd(i,j,k) = kd(i,j,k) + cs%Kd_add * rho_fn * lat_fn
137  enddo ; enddo
138  endif
139  if (present(kd_int)) then
140  do k=2,nz ; do i=is,ie
141  if (cs%use_abs_lat) then
142  lat_fn = val_weights(abs(g%geoLatT(i,j)), cs%lat_range)
143  else
144  lat_fn = val_weights(g%geoLatT(i,j), cs%lat_range)
145  endif
146  ! rho_int = 0.5*(Rcv(i,k-1) + Rcv(i,k))
147  rho_fn = val_weights( 0.5*(rcv(i,k-1) + rcv(i,k)), cs%rho_range)
148  if (rho_fn * lat_fn > 0.0) then
149  kd_int(i,j,k) = kd_int(i,j,k) + cs%Kd_add * rho_fn * lat_fn
150  if (store_kd_add) kd_int_add(i,j,k) = cs%Kd_add * rho_fn * lat_fn
151  endif
152  enddo ; enddo
153  endif
154  enddo
155 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
Here is the call graph for this function:

◆ user_change_diff_end()

subroutine, public user_change_diffusivity::user_change_diff_end ( type(user_change_diff_cs), pointer  CS)

Clean up the module control structure.

Parameters
csA pointer that is set to point to the control structure for this module.

Definition at line 269 of file user_change_diffusivity.F90.

269  type(user_change_diff_cs), pointer :: cs !< A pointer that is set to
270  !! point to the control
271  !! structure for this module.
272 
273  if (associated(cs)) deallocate(cs)
274 

◆ user_change_diff_init()

subroutine, public user_change_diffusivity::user_change_diff_init ( type(time_type), intent(in)  Time,
type(ocean_grid_type), intent(in)  G,
type(param_file_type), intent(in)  param_file,
type(diag_ctrl), intent(inout), target  diag,
type(user_change_diff_cs), pointer  CS 
)

Set up the module control structure.

Parameters
[in]timeThe current model time.
[in]gThe ocean's grid structure.
[in]param_fileA structure indicating the open file to parse for model parameter values.
[in,out]diagA structure that is used to regulate diagnostic output.
csA pointer that is set to point to the control structure for this module.

Definition at line 200 of file user_change_diffusivity.F90.

References mom_error_handler::mom_error(), and range_ok().

200  type(time_type), intent(in) :: time !< The current model time.
201  type(ocean_grid_type), intent(in) :: g !< The ocean's grid structure.
202  type(param_file_type), intent(in) :: param_file !< A structure indicating the
203  !! open file to parse for
204  !! model parameter values.
205  type(diag_ctrl), target, intent(inout) :: diag !< A structure that is used to
206  !! regulate diagnostic output.
207  type(user_change_diff_cs), pointer :: cs !< A pointer that is set to
208  !! point to the control
209  !! structure for this module.
210 
211 ! This include declares and sets the variable "version".
212 #include "version_variable.h"
213  character(len=40) :: mdl = "user_set_diffusivity" ! This module's name.
214  character(len=200) :: mesg
215  integer :: i, j, is, ie, js, je
216 
217  if (associated(cs)) then
218  call mom_error(warning, "diabatic_entrain_init called with an associated "// &
219  "control structure.")
220  return
221  endif
222  allocate(cs)
223 
224  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
225 
226  cs%diag => diag
227 
228  ! Read all relevant parameters and write them to the model log.
229  call log_version(param_file, mdl, version, "")
230  call get_param(param_file, mdl, "USER_KD_ADD", cs%Kd_add, &
231  "A user-specified additional diffusivity over a range of \n"//&
232  "latitude and density.", units="m2 s-1", default=0.0)
233  if (cs%Kd_add /= 0.0) then
234  call get_param(param_file, mdl, "USER_KD_ADD_LAT_RANGE", cs%lat_range(:), &
235  "Four successive values that define a range of latitudes \n"//&
236  "over which the user-specified extra diffusivity is \n"//&
237  "applied. The four values specify the latitudes at \n"//&
238  "which the extra diffusivity starts to increase from 0, \n"//&
239  "hits its full value, starts to decrease again, and is \n"//&
240  "back to 0.", units="degree", default=-1.0e9)
241  call get_param(param_file, mdl, "USER_KD_ADD_RHO_RANGE", cs%rho_range(:), &
242  "Four successive values that define a range of potential \n"//&
243  "densities over which the user-given extra diffusivity \n"//&
244  "is applied. The four values specify the density at \n"//&
245  "which the extra diffusivity starts to increase from 0, \n"//&
246  "hits its full value, starts to decrease again, and is \n"//&
247  "back to 0.", units="kg m-3", default=-1.0e9)
248  call get_param(param_file, mdl, "USER_KD_ADD_USE_ABS_LAT", cs%use_abs_lat, &
249  "If true, use the absolute value of latitude when \n"//&
250  "checking whether a point fits into range of latitudes.", &
251  default=.false.)
252  endif
253 
254  if (.not.range_ok(cs%lat_range)) then
255  write(mesg, '(4(1pe15.6))') cs%lat_range(1:4)
256  call mom_error(fatal, "user_set_diffusivity: bad latitude range: \n "//&
257  trim(mesg))
258  endif
259  if (.not.range_ok(cs%rho_range)) then
260  write(mesg, '(4(1pe15.6))') cs%rho_range(1:4)
261  call mom_error(fatal, "user_set_diffusivity: bad density range: \n "//&
262  trim(mesg))
263  endif
264 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
Here is the call graph for this function:

◆ val_weights()

real function user_change_diffusivity::val_weights ( real, intent(in)  val,
real, dimension(4), intent(in)  range 
)
private

This subroutine returns a value that goes smoothly from 0 to 1, stays at 1, and then goes smoothly back to 0 at the four values of range. The transitions are cubic, and have zero first derivatives where the curves hit 0 and 1. The values in range must be in ascending order, as can be checked by calling range_OK.

Parameters
[in]valValue for which we need an answer.
[in]rangeRange over which the answer is non-zero.
Returns
Return value.

Definition at line 175 of file user_change_diffusivity.F90.

Referenced by user_change_diff().

175  real, intent(in) :: val !< Value for which we need an answer.
176  real, dimension(4), intent(in) :: range !< Range over which the answer is non-zero.
177  real :: ans !< Return value.
178 
179  real :: x ! A nondimensional number between 0 and 1.
180 
181  ans = 0.0
182  if ((val > range(1)) .and. (val < range(4))) then
183  if (val < range(2)) then
184  ! x goes from 0 to 1; ans goes from 0 to 1, with 0 derivatives at the ends.
185  x = (val - range(1)) / (range(2) - range(1))
186  ans = x**2 * (3.0 - 2.0 * x)
187  elseif (val > range(3)) then
188  ! x goes from 0 to 1; ans goes from 0 to 1, with 0 derivatives at the ends.
189  x = (range(4) - val) / (range(4) - range(3))
190  ans = x**2 * (3.0 - 2.0 * x)
191  else
192  ans = 1.0
193  endif
194  endif
195 
Here is the caller graph for this function: