MOM6
kelvin_initialization Module Reference

Detailed Description

Configures the model for the Kelvin wave experiment.

Kelvin = coastally-trapped Kelvin waves from the ROMS examples. Initialize with level surfaces and drive the wave in at the west, radiate out at the east.

Data Types

type  kelvin_obc_cs
 Control structure for Kelvin wave open boundaries. More...
 

Functions/Subroutines

logical function, public register_kelvin_obc (param_file, CS, OBC_Reg)
 Add Kelvin wave to OBC registry. More...
 
subroutine, public kelvin_obc_end (CS)
 Clean up the Kelvin wave OBC from registry. More...
 
subroutine, public kelvin_initialize_topography (D, G, param_file, max_depth, US)
 This subroutine sets up the Kelvin topography and land mask. More...
 
subroutine, public kelvin_set_obc_data (OBC, CS, G, GV, US, h, Time)
 This subroutine sets the properties of flow at open boundary conditions. More...
 

Function/Subroutine Documentation

◆ kelvin_initialize_topography()

subroutine, public kelvin_initialization::kelvin_initialize_topography ( real, dimension(g%isd:g%ied,g%jsd:g%jed), intent(out)  D,
type(dyn_horgrid_type), intent(in)  G,
type(param_file_type), intent(in)  param_file,
real, intent(in)  max_depth,
type(unit_scale_type), intent(in), optional  US 
)

This subroutine sets up the Kelvin topography and land mask.

Parameters
[in]gThe dynamic horizontal grid type
[out]dOcean bottom depth in m or Z if US is present
[in]param_fileParameter file structure
[in]max_depthMaximum model depth in the units of D
[in]usA dimensional unit scaling type

Definition at line 120 of file Kelvin_initialization.F90.

121  type(dyn_horgrid_type), intent(in) :: G !< The dynamic horizontal grid type
122  real, dimension(G%isd:G%ied,G%jsd:G%jed), &
123  intent(out) :: D !< Ocean bottom depth in m or Z if US is present
124  type(param_file_type), intent(in) :: param_file !< Parameter file structure
125  real, intent(in) :: max_depth !< Maximum model depth in the units of D
126  type(unit_scale_type), optional, intent(in) :: US !< A dimensional unit scaling type
127 
128  ! Local variables
129  character(len=40) :: mdl = "Kelvin_initialize_topography" ! This subroutine's name.
130  real :: m_to_Z ! A dimensional rescaling factor.
131  real :: min_depth ! The minimum and maximum depths [Z ~> m].
132  real :: PI ! 3.1415...
133  real :: coast_offset1, coast_offset2, coast_angle, right_angle
134  integer :: i, j
135 
136  call mom_mesg(" Kelvin_initialization.F90, Kelvin_initialize_topography: setting topography", 5)
137 
138  m_to_z = 1.0 ; if (present(us)) m_to_z = us%m_to_Z
139 
140  call get_param(param_file, mdl, "MINIMUM_DEPTH", min_depth, &
141  "The minimum depth of the ocean.", units="m", default=0.0, scale=m_to_z)
142  call get_param(param_file, mdl, "ROTATED_COAST_OFFSET_1", coast_offset1, &
143  default=100.0, do_not_log=.true.)
144  call get_param(param_file, mdl, "ROTATED_COAST_OFFSET_2", coast_offset2, &
145  default=10.0, do_not_log=.true.)
146  call get_param(param_file, mdl, "ROTATED_COAST_ANGLE", coast_angle, &
147  default=11.3, do_not_log=.true.)
148 
149  coast_angle = coast_angle * (atan(1.0)/45.) ! Convert to radians
150  right_angle = 2 * atan(1.0)
151 
152  do j=g%jsc,g%jec ; do i=g%isc,g%iec
153  d(i,j) = max_depth
154  ! Southern side
155  if ((g%geoLonT(i,j) - g%west_lon > coast_offset1) .AND. &
156  (atan2(g%geoLatT(i,j) - g%south_lat + coast_offset2, &
157  g%geoLonT(i,j) - g%west_lon - coast_offset1) < coast_angle)) &
158  d(i,j) = 0.5*min_depth
159  ! Northern side
160  if ((g%geoLonT(i,j) - g%west_lon < g%len_lon - coast_offset1) .AND. &
161  (atan2(g%len_lat + g%south_lat + coast_offset2 - g%geoLatT(i,j), &
162  g%len_lon + g%west_lon - coast_offset1 - g%geoLonT(i,j)) < coast_angle)) &
163  d(i,j) = 0.5*min_depth
164 
165  if (d(i,j) > max_depth) d(i,j) = max_depth
166  if (d(i,j) < min_depth) d(i,j) = 0.5*min_depth
167  enddo ; enddo
168 

◆ kelvin_obc_end()

subroutine, public kelvin_initialization::kelvin_obc_end ( type(kelvin_obc_cs), pointer  CS)

Clean up the Kelvin wave OBC from registry.

Parameters
csKelvin wave control structure.

Definition at line 110 of file Kelvin_initialization.F90.

111  type(Kelvin_OBC_CS), pointer :: CS !< Kelvin wave control structure.
112 
113  if (associated(cs)) then
114  deallocate(cs)
115  endif

◆ kelvin_set_obc_data()

subroutine, public kelvin_initialization::kelvin_set_obc_data ( type(ocean_obc_type), pointer  OBC,
type(kelvin_obc_cs), pointer  CS,
type(ocean_grid_type), intent(in)  G,
type(verticalgrid_type), intent(in)  GV,
type(unit_scale_type), intent(in)  US,
real, dimension(szi_(g),szj_(g),szk_(g)), intent(in)  h,
type(time_type), intent(in)  Time 
)

This subroutine sets the properties of flow at open boundary conditions.

Parameters
obcThis open boundary condition type specifies whether, where, and what open boundary conditions are used.
csKelvin wave control structure.
[in]gThe ocean's grid structure.
[in]gvThe ocean's vertical grid structure.
[in]usA dimensional unit scaling type
[in]hlayer thickness [H ~> m or kg m-2].
[in]timemodel time.

Definition at line 172 of file Kelvin_initialization.F90.

173  type(ocean_OBC_type), pointer :: OBC !< This open boundary condition type specifies
174  !! whether, where, and what open boundary
175  !! conditions are used.
176  type(Kelvin_OBC_CS), pointer :: CS !< Kelvin wave control structure.
177  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
178  type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
179  type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
180  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< layer thickness [H ~> m or kg m-2].
181  type(time_type), intent(in) :: Time !< model time.
182 
183  ! The following variables are used to set up the transport in the Kelvin example.
184  real :: time_sec, cff
185  real :: N0 ! Brunt-Vaisala frequency [s-1]
186  real :: plx !< Longshore wave parameter
187  real :: pmz !< Vertical wave parameter
188  real :: lambda !< Offshore decay scale
189  real :: omega !< Wave frequency [s-1]
190  real :: PI
191  integer :: i, j, k, n, is, ie, js, je, isd, ied, jsd, jed, nz
192  integer :: IsdB, IedB, JsdB, JedB
193  real :: fac, x, y, x1, y1
194  real :: val1, val2, sina, cosa
195  type(OBC_segment_type), pointer :: segment => null()
196 
197  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
198  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
199  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
200 
201  if (.not.associated(obc)) call mom_error(fatal, 'Kelvin_initialization.F90: '// &
202  'Kelvin_set_OBC_data() was called but OBC type was not initialized!')
203 
204  time_sec = time_type_to_real(time)
205  pi = 4.0*atan(1.0)
206  fac = 1.0
207 
208  if (cs%mode == 0) then
209  omega = 2.0 * pi / (12.42 * 3600.0) ! M2 Tide period
210  val1 = us%m_to_Z * sin(omega * time_sec)
211  else
212  n0 = us%L_to_m*us%s_to_T * sqrt((cs%rho_range / cs%rho_0) * gv%g_Earth * (us%m_to_Z * cs%H0))
213  ! Two wavelengths in domain
214  plx = 4.0 * pi / g%len_lon
215  pmz = pi * cs%mode / cs%H0
216  lambda = pmz * cs%F_0 / n0
217  omega = cs%F_0 * plx / lambda
218 
219  ! lambda = PI * CS%mode * CS%F_0 / (CS%H0 * N0)
220  ! omega = (4.0 * CS%H0 * N0) / (CS%mode * G%len_lon)
221  endif
222 
223  sina = sin(cs%coast_angle)
224  cosa = cos(cs%coast_angle)
225  do n=1,obc%number_of_segments
226  segment => obc%segment(n)
227  if (.not. segment%on_pe) cycle
228  ! Apply values to the inflow end only.
229  if (segment%direction == obc_direction_e) cycle
230  if (segment%direction == obc_direction_n) cycle
231 
232  ! This should be somewhere else...
233  segment%Velocity_nudging_timescale_in = 1.0/(0.3*86400)
234 
235  if (segment%direction == obc_direction_w) then
236  isdb = segment%HI%IsdB ; iedb = segment%HI%IedB
237  jsd = segment%HI%jsd ; jed = segment%HI%jed
238  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
239  do j=jsd,jed ; do i=isdb,iedb
240  x1 = 1000. * g%geoLonCu(i,j)
241  y1 = 1000. * g%geoLatCu(i,j)
242  x = (x1 - cs%coast_offset1) * cosa + y1 * sina
243  y = - (x1 - cs%coast_offset1) * sina + y1 * cosa
244  if (cs%mode == 0) then
245  ! Use inside bathymetry
246  cff = sqrt(gv%g_Earth * g%bathyT(i+1,j) )
247  val2 = fac * exp(- us%T_to_s*cs%F_0 * us%m_to_L*y / cff)
248  segment%eta(i,j) = val2 * cos(omega * time_sec)
249  segment%normal_vel_bt(i,j) = (val2 * (val1 * cff * cosa / &
250  (g%bathyT(i+1,j) )) )
251  if (segment%nudged) then
252  do k=1,nz
253  segment%nudged_normal_vel(i,j,k) = (val2 * (val1 * cff * cosa / &
254  (g%bathyT(i+1,j))) )
255  enddo
256  elseif (segment%specified) then
257  do k=1,nz
258  segment%normal_vel(i,j,k) = (val2 * (val1 * cff * cosa / &
259  (g%bathyT(i+1,j) )) )
260  segment%normal_trans(i,j,k) = segment%normal_vel(i,j,k) * h(i+1,j,k) * g%dyCu(i,j)
261  enddo
262  endif
263  else
264  ! Not rotated yet
265  segment%eta(i,j) = 0.0
266  segment%normal_vel_bt(i,j) = 0.0
267  if (segment%nudged) then
268  do k=1,nz
269  segment%nudged_normal_vel(i,j,k) = us%m_s_to_L_T * fac * lambda / cs%F_0 * &
270  exp(- lambda * y) * cos(pi * cs%mode * (k - 0.5) / nz) * &
271  cos(omega * time_sec)
272  enddo
273  elseif (segment%specified) then
274  do k=1,nz
275  segment%normal_vel(i,j,k) = us%m_s_to_L_T * fac * lambda / cs%F_0 * &
276  exp(- lambda * y) * cos(pi * cs%mode * (k - 0.5) / nz) * &
277  cos(omega * time_sec)
278  segment%normal_trans(i,j,k) = segment%normal_vel(i,j,k) * h(i+1,j,k) * g%dyCu(i,j)
279  enddo
280  endif
281  endif
282  enddo ; enddo
283  if (associated(segment%tangential_vel)) then
284  do j=jsdb+1,jedb-1 ; do i=isdb,iedb
285  x1 = 1000. * g%geoLonBu(i,j)
286  y1 = 1000. * g%geoLatBu(i,j)
287  x = (x1 - cs%coast_offset1) * cosa + y1 * sina
288  y = - (x1 - cs%coast_offset1) * sina + y1 * cosa
289  cff =sqrt(gv%g_Earth * g%bathyT(i+1,j) )
290  val2 = fac * exp(- us%T_to_s*cs%F_0 * us%m_to_L*y / cff)
291  if (cs%mode == 0) then ; do k=1,nz
292  segment%tangential_vel(i,j,k) = (val1 * val2 * cff * sina) / &
293  ( 0.5*(g%bathyT(i+1,j+1) + g%bathyT(i+1,j) ) )
294 
295  enddo ; endif
296  enddo ; enddo
297  endif
298  else ! Must be south
299  isd = segment%HI%isd ; ied = segment%HI%ied
300  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
301  do j=jsdb,jedb ; do i=isd,ied
302  x1 = 1000. * g%geoLonCv(i,j)
303  y1 = 1000. * g%geoLatCv(i,j)
304  x = (x1 - cs%coast_offset1) * cosa + y1 * sina
305  y = - (x1 - cs%coast_offset1) * sina + y1 * cosa
306  if (cs%mode == 0) then
307  cff = sqrt(gv%g_Earth * g%bathyT(i,j+1) )
308  val2 = fac * exp(- 0.5 * (g%CoriolisBu(i,j) + g%CoriolisBu(i-1,j)) * us%m_to_L*y / cff)
309  segment%eta(i,j) = val2 * cos(omega * time_sec)
310  segment%normal_vel_bt(i,j) = us%L_T_to_m_s * (val1 * cff * sina / &
311  (g%bathyT(i,j+1) )) * val2
312  if (segment%nudged) then
313  do k=1,nz
314  segment%nudged_normal_vel(i,j,k) = us%L_T_to_m_s * (val1 * cff * sina / &
315  (g%bathyT(i,j+1) )) * val2
316  enddo
317  elseif (segment%specified) then
318  do k=1,nz
319  segment%normal_vel(i,j,k) = us%L_T_to_m_s * (val1 * cff * sina / &
320  (g%bathyT(i,j+1) )) * val2
321  segment%normal_trans(i,j,k) = segment%normal_vel(i,j,k) * h(i,j+1,k) * g%dxCv(i,j)
322  enddo
323  endif
324  else
325  ! Not rotated yet
326  segment%eta(i,j) = 0.0
327  segment%normal_vel_bt(i,j) = 0.0
328  if (segment%nudged) then
329  do k=1,nz
330  segment%nudged_normal_vel(i,j,k) = us%m_s_to_L_T*fac * lambda / cs%F_0 * &
331  exp(- lambda * y) * cos(pi * cs%mode * (k - 0.5) / nz) * cosa
332  enddo
333  elseif (segment%specified) then
334  do k=1,nz
335  segment%normal_vel(i,j,k) = us%m_s_to_L_T*fac * lambda / cs%F_0 * &
336  exp(- lambda * y) * cos(pi * cs%mode * (k - 0.5) / nz) * cosa
337  segment%normal_trans(i,j,k) = segment%normal_vel(i,j,k) * h(i,j+1,k) * g%dxCv(i,j)
338  enddo
339  endif
340  endif
341  enddo ; enddo
342  if (associated(segment%tangential_vel)) then
343  do j=jsdb,jedb ; do i=isdb+1,iedb-1
344  x1 = 1000. * g%geoLonBu(i,j)
345  y1 = 1000. * g%geoLatBu(i,j)
346  x = (x1 - cs%coast_offset1) * cosa + y1 * sina
347  y = - (x1 - cs%coast_offset1) * sina + y1 * cosa
348  cff = sqrt(gv%g_Earth * g%bathyT(i,j+1) )
349  val2 = fac * exp(- 0.5 * (g%CoriolisBu(i,j) + g%CoriolisBu(i-1,j)) * us%m_to_L*y / cff)
350  if (cs%mode == 0) then ; do k=1,nz
351  segment%tangential_vel(i,j,k) = ((val1 * val2 * cff * sina) / &
352  ( 0.5*((g%bathyT(i+1,j+1)) + g%bathyT(i,j+1))) )
353  enddo ; endif
354  enddo ; enddo
355  endif
356  endif
357  enddo
358 

◆ register_kelvin_obc()

logical function, public kelvin_initialization::register_kelvin_obc ( type(param_file_type), intent(in)  param_file,
type(kelvin_obc_cs), pointer  CS,
type(obc_registry_type), pointer  OBC_Reg 
)

Add Kelvin wave to OBC registry.

Parameters
[in]param_fileparameter file.
csKelvin wave control structure.
obc_regOBC registry.

Definition at line 53 of file Kelvin_initialization.F90.

54  type(param_file_type), intent(in) :: param_file !< parameter file.
55  type(Kelvin_OBC_CS), pointer :: CS !< Kelvin wave control structure.
56  type(OBC_registry_type), pointer :: OBC_Reg !< OBC registry.
57 
58  ! Local variables
59  logical :: register_Kelvin_OBC
60  character(len=40) :: mdl = "register_Kelvin_OBC" !< This subroutine's name.
61  character(len=32) :: casename = "Kelvin wave" !< This case's name.
62  character(len=200) :: config
63 
64  if (associated(cs)) then
65  call mom_error(warning, "register_Kelvin_OBC called with an "// &
66  "associated control structure.")
67  return
68  endif
69  allocate(cs)
70 
71  call log_version(param_file, mdl, version, "")
72  call get_param(param_file, mdl, "KELVIN_WAVE_MODE", cs%mode, &
73  "Vertical Kelvin wave mode imposed at upstream open boundary.", &
74  default=0)
75  call get_param(param_file, mdl, "F_0", cs%F_0, &
76  default=0.0, do_not_log=.true.)
77  call get_param(param_file, mdl, "TOPO_CONFIG", config, do_not_log=.true.)
78  if (trim(config) == "Kelvin") then
79  call get_param(param_file, mdl, "ROTATED_COAST_OFFSET_1", cs%coast_offset1, &
80  "The distance along the southern and northern boundaries "//&
81  "at which the coasts angle in.", &
82  units="km", default=100.0)
83  call get_param(param_file, mdl, "ROTATED_COAST_OFFSET_2", cs%coast_offset2, &
84  "The distance from the southern and northern boundaries "//&
85  "at which the coasts angle in.", &
86  units="km", default=10.0)
87  call get_param(param_file, mdl, "ROTATED_COAST_ANGLE", cs%coast_angle, &
88  "The angle of the southern bondary beyond X=ROTATED_COAST_OFFSET.", &
89  units="degrees", default=11.3)
90  cs%coast_angle = cs%coast_angle * (atan(1.0)/45.) ! Convert to radians
91  cs%coast_offset1 = cs%coast_offset1 * 1.e3 ! Convert to m
92  cs%coast_offset2 = cs%coast_offset2 * 1.e3 ! Convert to m
93  endif
94  if (cs%mode /= 0) then
95  call get_param(param_file, mdl, "DENSITY_RANGE", cs%rho_range, &
96  default=2.0, do_not_log=.true.)
97  call get_param(param_file, mdl, "RHO_0", cs%rho_0, &
98  default=1035.0, do_not_log=.true.)
99  call get_param(param_file, mdl, "MAXIMUM_DEPTH", cs%H0, &
100  default=1000.0, do_not_log=.true.)
101  endif
102 
103  ! Register the Kelvin open boundary.
104  call register_obc(casename, param_file, obc_reg)
105  register_kelvin_obc = .true.
106