MOM6
MOM_open_boundary.F90
Go to the documentation of this file.
1 !> Controls where open boundary conditions are applied
3 
4 ! This file is part of MOM6. See LICENSE.md for the license.
5 
6 use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, clock_routine
7 use mom_diag_mediator, only : diag_ctrl, time_type
9 use mom_domains, only : to_all, scalar_pair, cgrid_ne
10 use mom_error_handler, only : mom_mesg, mom_error, fatal, warning, is_root_pe
12 use mom_grid, only : ocean_grid_type, hor_index_type
14 use mom_io, only : east_face, north_face
15 use mom_io, only : slasher, read_data, field_size, single_file
20 use time_interp_external_mod, only : init_external_field, time_interp_external
23 use mom_regridding, only : regridding_cs
25 
26 implicit none ; private
27 
28 #include <MOM_memory.h>
29 
32 public open_boundary_init
34 public open_boundary_end
38 public set_tracer_data
45 
46 integer, parameter, public :: obc_none = 0, obc_simple = 1, obc_wall = 2
47 integer, parameter, public :: obc_flather = 3
48 integer, parameter, public :: obc_radiation = 4
49 integer, parameter, public :: obc_direction_n = 100 !< Indicates the boundary is an effective northern boundary
50 integer, parameter, public :: obc_direction_s = 200 !< Indicates the boundary is an effective southern boundary
51 integer, parameter, public :: obc_direction_e = 300 !< Indicates the boundary is an effective eastern boundary
52 integer, parameter, public :: obc_direction_w = 400 !< Indicates the boundary is an effective western boundary
53 integer, parameter :: max_obc_fields = 100 !< Maximum number of data fields needed for OBC segments
54 
55 !> Open boundary segment data from files (mostly).
56 type, public :: obc_segment_data_type
57  integer :: fid !< handle from FMS associated with segment data on disk
58  integer :: fid_dz !< handle from FMS associated with segment thicknesses on disk
59  character(len=8) :: name !< a name identifier for the segment data
60  real, pointer, dimension(:,:,:) :: buffer_src=>null() !< buffer for segment data located at cell faces
61  !! and on the original vertical grid
62  integer :: nk_src !< Number of vertical levels in the source data
63  real, dimension(:,:,:), pointer :: dz_src=>null() !< vertical grid cell spacing of the incoming segment data (m)
64  real, dimension(:,:,:), pointer :: buffer_dst=>null() !< buffer src data remapped to the target vertical grid
65  real, dimension(:,:), pointer :: bt_vel=>null() !< barotropic velocity (m s-1)
66  real :: value !< constant value if fid is equal to -1
67 end type obc_segment_data_type
68 
69 !> Open boundary segment data structure.
70 type, public :: obc_segment_type
71  logical :: flather !< If true, applies Flather + Chapman radiation of barotropic gravity waves.
72  logical :: radiation !< If true, 1D Orlanksi radiation boundary conditions are applied.
73  !! If False, a gradient condition is applied.
74  logical :: oblique !< Oblique waves supported at radiation boundary.
75  logical :: nudged !< Optional supplement to radiation boundary.
76  logical :: specified !< Boundary fixed to external value.
77  logical :: open !< Boundary is open for continuity solver.
78  logical :: gradient !< Zero gradient at boundary.
79  logical :: values_needed !< Whether or not external OBC fields are needed.
80  integer :: direction !< Boundary faces one of the four directions.
81  logical :: is_n_or_s !< True is the OB is facing North or South and exists on this PE.
82  logical :: is_e_or_w !< True is the OB is facing East or West and exists on this PE.
83  type(obc_segment_data_type), pointer, dimension(:) :: field=>null() !< OBC data
84  integer :: num_fields !< number of OBC data fields (e.g. u_normal,u_parallel and eta for Flather)
85  character(len=32), pointer, dimension(:) :: field_names=>null() !< field names for this segment
86  integer :: is_obc !< i-indices of boundary segment.
87  integer :: ie_obc !< i-indices of boundary segment.
88  integer :: js_obc !< j-indices of boundary segment.
89  integer :: je_obc !< j-indices of boundary segment.
90  real :: tnudge_in !< Inverse nudging timescale on inflow (1/s).
91  real :: tnudge_out !< Inverse nudging timescale on outflow (1/s).
92  logical :: on_pe !< true if segment is located in the computational domain
93  real, pointer, dimension(:,:) :: cg=>null() !< The external gravity
94  !< wave speed (m -s) at OBC-points.
95  real, pointer, dimension(:,:) :: htot=>null() !< The total column thickness (m) at OBC-points.
96  real, pointer, dimension(:,:,:) :: h=>null() !< The cell thickness (m) at OBC-points.
97  real, pointer, dimension(:,:,:) :: e=>null() !< The interface height (m?) at OBC-points.
98  real, pointer, dimension(:,:,:) :: normal_vel=>null() !< The layer velocity normal to the OB
99  !! segment (m s-1).
100  real, pointer, dimension(:,:,:) :: normal_trans=>null() !< The layer transport normal to the OB
101  !! segment (m3 s-1).
102  real, pointer, dimension(:,:) :: normal_vel_bt=>null() !< The barotropic velocity normal to
103  !! the OB segment (m s-1).
104  real, pointer, dimension(:,:) :: normal_trans_bt=>null()!< The barotropic transport normal to
105  !! the OB segment (m3 s-1).
106  real, pointer, dimension(:,:) :: eta=>null() !< The sea-surface elevation along the segment (m).
107  real, pointer, dimension(:,:,:) :: grad_normal=>null() !< The gradient of the normal flow along the
108  !! segment (m s-1)
109  real, pointer, dimension(:,:,:) :: rx_normal=>null() !< The rx_old_u value for radiation coeff
110  !! for normal velocity
111  real, pointer, dimension(:,:,:) :: nudged_normal_vel=>null() !< The layer velocity normal to the OB segment
112  !! that values should be nudged towards (m s-1).
113  real, pointer, dimension(:,:,:) :: t=>null() !< The temperature on the OB segment
114  !! velocity points (C).
115  real, pointer, dimension(:,:,:) :: s=>null() !< The salinity on the OB segment
116  !! velocity points ().
117  type(hor_index_type) :: hi !< Horizontal index ranges
118 end type obc_segment_type
119 
120 !> Open-boundary data
121 type, public :: ocean_obc_type
122  integer :: number_of_segments = 0 !< The number of open-boundary segments.
123  integer :: ke = 0 !< The number of model layers
124  logical :: open_u_bcs_exist_globally = .false. !< True if any zonal velocity points
125  !! in the global domain use open BCs.
126  logical :: open_v_bcs_exist_globally = .false. !< True if any meridional velocity points
127  !! in the global domain use open BCs.
128  logical :: flather_u_bcs_exist_globally = .false. !< True if any zonal velocity points
129  !! in the global domain use Flather BCs.
130  logical :: flather_v_bcs_exist_globally = .false. !< True if any meridional velocity points
131  !! in the global domain use Flather BCs.
132  logical :: oblique_bcs_exist_globally = .false. !< True if any velocity points
133  !! in the global domain use oblique BCs.
134  logical :: nudged_u_bcs_exist_globally = .false. !< True if any velocity points in the
135  !! global domain use nudged BCs.
136  logical :: nudged_v_bcs_exist_globally = .false. !< True if any velocity points in the
137  !! global domain use nudged BCs.
138  logical :: specified_u_bcs_exist_globally = .false. !< True if any zonal velocity points
139  !! in the global domain use specified BCs.
140  logical :: specified_v_bcs_exist_globally = .false. !< True if any meridional velocity points
141  !! in the global domain use specified BCs.
142  logical :: user_bcs_set_globally = .false. !< True if any OBC_USER_CONFIG is set
143  !! for input from user directory.
144  logical :: update_obc = .false. !< Is OBC data time-dependent
145  logical :: needs_io_for_data = .false. !< Is any i/o needed for OBCs
146  logical :: zero_vorticity = .false. !< If True, sets relative vorticity to zero on open boundaries.
147  logical :: freeslip_vorticity = .false. !< If True, sets normal gradient of tangential velocity to zero
148  !! in the relative vorticity on open boundaries.
149  logical :: zero_strain = .false. !< If True, sets strain to zero on open boundaries.
150  logical :: freeslip_strain = .false. !< If True, sets normal gradient of tangential velocity to zero
151  !! in the strain on open boundaries.
152  logical :: zero_biharmonic = .false. !< If True, zeros the Laplacian of flow on open boundaries for
153  !! use in the biharmonic viscosity term.
154  logical :: extend_segments = .false. !< If True, extend OBC segments (for testing)
155  real :: g_earth
156  ! Properties of the segments used.
157  type(obc_segment_type), pointer, dimension(:) :: &
158  segment => null() !< List of segment objects.
159  ! Which segment object describes the current point.
160  integer, pointer, dimension(:,:) :: &
161  segnum_u => null(), & !< Segment number of u-points.
162  segnum_v => null() !< Segment number of v-points.
163 
164  ! The following parameters are used in the baroclinic radiation code:
165  real :: gamma_uv !< The relative weighting for the baroclinic radiation
166  !! velocities (or speed of characteristics) at the
167  !! new time level (1) or the running mean (0) for velocities.
168  !! Valid values range from 0 to 1, with a default of 0.3.
169  real :: gamma_h !< The relative weighting for the baroclinic radiation
170  !! velocities (or speed of characteristics) at the
171  !! new time level (1) or the running mean (0) for thicknesses.
172  !! Valid values range from 0 to 1, with a default of 0.2.
173  real :: rx_max !< The maximum magnitude of the baroclinic radiation
174  !! velocity (or speed of characteristics), in m s-1. The
175  !! default value is 10 m s-1.
176  logical :: obc_pe !< Is there an open boundary on this tile?
177  type(remapping_cs), pointer :: remap_cs !< ALE remapping control structure for segments only
178  type(obc_registry_type), pointer :: obc_reg => null() !< Registry type for boundaries
179 end type ocean_obc_type
180 
181 !> Control structure for open boundaries that read from files.
182 !! Probably lots to update here.
183 type, public :: file_obc_cs ; private
184  real :: tide_flow = 3.0e6 !< Placeholder for now...
185 end type file_obc_cs
186 
187 !> Type to carry something (what??) for the OBC registry.
188 type, public :: obc_struct_type
189  character(len=32) :: name !< OBC name used for error messages
190 end type obc_struct_type
191 
192 !> Type to carry basic OBC information needed for updating values.
193 type, public :: obc_registry_type
194  integer :: nobc = 0 !< number of registered open boundary types.
195  type(obc_struct_type) :: ob(max_fields_) !< array of registered boundary types.
196  logical :: locked = .false. !< New OBC types may be registered if locked=.false.
197  !! When locked=.true.,no more boundaries can be registered.
198 end type obc_registry_type
199 
200 integer :: id_clock_pass
201 
202 character(len=40) :: mdl = "MOM_open_boundary" ! This module's name.
203 ! This include declares and sets the variable "version".
204 #include "version_variable.h"
205 
206 contains
207 
208 !> Enables OBC module and reads configuration parameters
209 !> This routine is called from MOM_initialize_fixed which
210 !> occurs before the initialization of the vertical coordinate
211 !> and ALE_init. Therefore segment data are not fully initialized
212 !> here. The remainder of the segment data are initialized in a
213 !> later call to update_open_boundary_data
214 
215 subroutine open_boundary_config(G, param_file, OBC)
216  type(dyn_horgrid_type), intent(in) :: G !< Ocean grid structure
217  type(param_file_type), intent(in) :: param_file !< Parameter file handle
218  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
219  ! Local variables
220  integer :: l ! For looping over segments
221  character(len=15) :: segment_param_str ! The run-time parameter name for each segment
222  character(len=100) :: segment_str ! The contents (rhs) for parameter "segment_param_str"
223  character(len=200) :: config1 ! String for OBC_USER_CONFIG
224 
225  allocate(obc)
226 
227  call log_version(param_file, mdl, version, "Controls where open boundaries are located, what "//&
228  "kind of boundary condition to impose, and what data to apply, if any.")
229  call get_param(param_file, mdl, "OBC_NUMBER_OF_SEGMENTS", obc%number_of_segments, &
230  "The number of open boundary segments.", &
231  default=0)
232  call get_param(param_file, mdl, "G_EARTH", obc%g_Earth, &
233  "The gravitational acceleration of the Earth.", &
234  units="m s-2", default = 9.80)
235  call get_param(param_file, mdl, "OBC_USER_CONFIG", config1, &
236  "A string that sets how the open boundary conditions are \n"//&
237  " configured: \n", default="none", do_not_log=.true.)
238  call get_param(param_file, mdl, "NK", obc%ke, &
239  "The number of model layers", default=0, do_not_log=.true.)
240 
241  if (config1 .ne. "none") obc%user_BCs_set_globally = .true.
242  ! It's in state initialization...
243 ! if (config1 .eq. "tidal_bay") OBC%update_OBC = .true.
244 
245  call get_param(param_file, mdl, "EXTEND_OBC_SEGMENTS", obc%extend_segments, &
246  "If true, extend OBC segments. This option is used to recover\n"//&
247  "legacy solutions dependent on an incomplete implementaion of OBCs.\n"//&
248  "This option will be obsoleted in the future.", default=.false.)
249 
250  if (obc%number_of_segments > 0) then
251  call get_param(param_file, mdl, "OBC_ZERO_VORTICITY", obc%zero_vorticity, &
252  "If true, sets relative vorticity to zero on open boundaries.", &
253  default=.false.)
254  call get_param(param_file, mdl, "OBC_FREESLIP_VORTICITY", obc%freeslip_vorticity, &
255  "If true, sets the normal gradient of tangential velocity to\n"// &
256  "zero in the relative vorticity on open boundaries. This cannot\n"// &
257  "be true if OBC_ZERO_VORTICITY is True.", default=.false.)
258  if (obc%zero_vorticity .and. obc%freeslip_vorticity) call mom_error(fatal, &
259  "MOM_open_boundary.F90, open_boundary_config: "//&
260  "Only one of OBC_ZERO_VORTICITY and OBC_FREESLIP_VORTICITY can be True at once.")
261  call get_param(param_file, mdl, "OBC_ZERO_STRAIN", obc%zero_strain, &
262  "If true, sets the strain used in the stress tensor to zero on open boundaries.", &
263  default=.false.)
264  call get_param(param_file, mdl, "OBC_FREESLIP_STRAIN", obc%freeslip_strain, &
265  "If true, sets the normal gradient of tangential velocity to\n"// &
266  "zero in the strain use in the stress tensor on open boundaries. This cannot\n"// &
267  "be true if OBC_ZERO_STRAIN is True.", default=.false.)
268  if (obc%zero_strain .and. obc%freeslip_strain) call mom_error(fatal, &
269  "MOM_open_boundary.F90, open_boundary_config: "//&
270  "Only one of OBC_ZERO_STRAIN and OBC_FREESLIP_STRAIN can be True at once.")
271  call get_param(param_file, mdl, "OBC_ZERO_BIHARMONIC", obc%zero_biharmonic, &
272  "If true, zeros the Laplacian of flow on open boundaries in the biharmonic\n"//&
273  "viscosity term.", default=.false.)
274  ! Allocate everything
275  ! Note the 0-segment is needed when %segnum_u/v(:,:) = 0
276  allocate(obc%segment(0:obc%number_of_segments))
277  do l=0,obc%number_of_segments
278  obc%segment(l)%Flather = .false.
279  obc%segment(l)%radiation = .false.
280  obc%segment(l)%oblique = .false.
281  obc%segment(l)%nudged = .false.
282  obc%segment(l)%specified = .false.
283  obc%segment(l)%open = .false.
284  obc%segment(l)%gradient = .false.
285  obc%segment(l)%values_needed = .false.
286  obc%segment(l)%direction = obc_none
287  obc%segment(l)%is_N_or_S = .false.
288  obc%segment(l)%is_E_or_W = .false.
289  obc%segment(l)%Tnudge_in = 0.0
290  obc%segment(l)%Tnudge_out = 0.0
291  obc%segment(l)%num_fields = 0.0
292  enddo
293  allocate(obc%segnum_u(g%IsdB:g%IedB,g%jsd:g%jed)) ; obc%segnum_u(:,:) = obc_none
294  allocate(obc%segnum_v(g%isd:g%ied,g%JsdB:g%JedB)) ; obc%segnum_v(:,:) = obc_none
295 
296  do l = 1, obc%number_of_segments
297  write(segment_param_str(1:15),"('OBC_SEGMENT_',i3.3)") l
298  call get_param(param_file, mdl, segment_param_str, segment_str, &
299  "Documentation needs to be dynamic?????", &
300  fail_if_missing=.true.)
301  segment_str = remove_spaces(segment_str)
302  if (segment_str(1:2) == 'I=') then
303  call setup_u_point_obc(obc, g, segment_str, l)
304  elseif (segment_str(1:2) == 'J=') then
305  call setup_v_point_obc(obc, g, segment_str, l)
306  else
307  call mom_error(fatal, "MOM_open_boundary.F90, open_boundary_config: "//&
308  "Unable to interpret "//segment_param_str//" = "//trim(segment_str))
309  endif
310  enddo
311 
312  ! if (open_boundary_query(OBC, needs_ext_seg_data=.true.)) &
313  call initialize_segment_data(g, obc, param_file)
314  endif
315 
316  ! Safety check
317  if ((obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally) .and. &
318  .not.g%symmetric ) call mom_error(fatal, &
319  "MOM_open_boundary, open_boundary_config: "//&
320  "Symmetric memory must be used when using Flather OBCs.")
321 
322  if (.not.(obc%specified_u_BCs_exist_globally .or. obc%specified_v_BCs_exist_globally .or. &
323  obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)) then
324  ! No open boundaries have been requested
325  call open_boundary_dealloc(obc)
326  endif
327 
328 end subroutine open_boundary_config
329 
330 subroutine initialize_segment_data(G, OBC, PF)
331  use mpp_mod, only : mpp_pe, mpp_set_current_pelist, mpp_get_current_pelist,mpp_npes
332 
333  type(dyn_horgrid_type), intent(in) :: G !< Ocean grid structure
334  type(ocean_obc_type), intent(inout) :: OBC !< Open boundary control structure
335  type(param_file_type), intent(in) :: PF !< Parameter file handle
336 
337  integer :: n,m,num_fields
338  character(len=256) :: segstr, filename
339  character(len=20) :: segnam, suffix
340  character(len=32) :: varnam, fieldname
341  real :: value
342  integer :: orient
343  character(len=32), dimension(MAX_OBC_FIELDS) :: fields ! segment field names
344  character(len=128) :: inputdir
345  type(obc_segment_type), pointer :: segment ! pointer to segment type list
346  character(len=32) :: remappingScheme
347  logical :: check_reconstruction, check_remapping, force_bounds_in_subcell
348  integer, dimension(4) :: siz,siz2
349  integer :: is, ie, js, je
350  integer :: isd, ied, jsd, jed
351  integer :: IsdB, IedB, JsdB, JedB
352  integer, dimension(:), allocatable :: saved_pelist
353  integer :: current_pe
354  integer, dimension(1) :: single_pelist
355 
356  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
357 
358  ! There is a problem with the order of the OBC initialization
359  ! with respect to ALE_init. Currently handling this by copying the
360  ! param file so that I can use it later in step_MOM in order to finish
361  ! initializing segments on the first step.
362 
363  call get_param(pf, mdl, "INPUTDIR", inputdir, default=".")
364  inputdir = slasher(inputdir)
365 
366  call get_param(pf, mdl, "REMAPPING_SCHEME", remappingscheme, &
367  "This sets the reconstruction scheme used\n"//&
368  "for vertical remapping for all variables.\n"//&
369  "It can be one of the following schemes:\n"//&
370  trim(remappingschemesdoc), default=remappingdefaultscheme,do_not_log=.true.)
371  call get_param(pf, mdl, "FATAL_CHECK_RECONSTRUCTIONS", check_reconstruction, &
372  "If true, cell-by-cell reconstructions are checked for\n"//&
373  "consistency and if non-monotonicity or an inconsistency is\n"//&
374  "detected then a FATAL error is issued.", default=.false.,do_not_log=.true.)
375  call get_param(pf, mdl, "FATAL_CHECK_REMAPPING", check_remapping, &
376  "If true, the results of remapping are checked for\n"//&
377  "conservation and new extrema and if an inconsistency is\n"//&
378  "detected then a FATAL error is issued.", default=.false.,do_not_log=.true.)
379  call get_param(pf, mdl, "REMAP_BOUND_INTERMEDIATE_VALUES", force_bounds_in_subcell, &
380  "If true, the values on the intermediate grid used for remapping\n"//&
381  "are forced to be bounded, which might not be the case due to\n"//&
382  "round off.", default=.false.,do_not_log=.true.)
383 
384  allocate(obc%remap_CS)
385  call initialize_remapping(obc%remap_CS, remappingscheme, boundary_extrapolation = .false., &
386  check_reconstruction=check_reconstruction, &
387  check_remapping=check_remapping, force_bounds_in_subcell=force_bounds_in_subcell)
388 
389  if (obc%user_BCs_set_globally) return
390 
391 
392  !< temporarily disable communication in order to read segment data independently
393 
394  allocate(saved_pelist(0:mpp_npes()-1))
395  call mpp_get_current_pelist(saved_pelist)
396  current_pe = mpp_pe()
397  single_pelist(1) = current_pe
398  call mpp_set_current_pelist(single_pelist)
399 
400  do n=1, obc%number_of_segments
401  segment => obc%segment(n)
402 
403  write(segnam,"('OBC_SEGMENT_',i3.3,'_DATA')") n
404  write(suffix,"('_segment_',i3.3)") n
405  call get_param(pf, mdl, segnam, segstr)
406 
407  call parse_segment_data_str(trim(segstr), fields=fields, num_fields=num_fields)
408  if (num_fields == 0) then
409  print *,'num_fields = 0';cycle ! cycle to next segment
410  endif
411 
412  allocate(segment%field(num_fields))
413 
414  if (segment%Flather) then
415  if (num_fields /= 3) call mom_error(fatal, &
416  "MOM_open_boundary, initialize_segment_data: "//&
417  "Need three inputs for Flather")
418 
419  segment%num_fields = 3 ! these are the input fields required for the Flather option
420  ! note that this is assuming that the inputs are coming in this order
421  ! and independent of the input param string . Needs cleanup - mjh
422  allocate(segment%field_names(segment%num_fields))
423  segment%field_names(:)='None'
424  segment%field_names(1)='UO'
425  segment%field_names(2)='VO'
426  segment%field_names(3)='ZOS'
427  endif
428 
429 !!
430 ! CODE HERE FOR OTHER OPTIONS (CLAMPED, NUDGED,..)
431 !!
432 
433  isd = segment%HI%isd ; ied = segment%HI%ied
434  jsd = segment%HI%jsd ; jed = segment%HI%jed
435  isdb = segment%HI%IsdB ; iedb = segment%HI%IedB
436  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
437 
438 
439  do m=1,num_fields
440  call parse_segment_data_str(trim(segstr), var=trim(fields(m)), value=value, filenam=filename, fieldnam=fieldname)
441  if (trim(filename) /= 'none') then
442  obc%update_OBC = .true. ! Data is assumed to be time-dependent if we are reading from file
443  obc%needs_IO_for_data = .true. ! At least one segment is using I/O for OBC data
444 
445  segment%values_needed = .true. ! Indicates that i/o will be needed for this segment
446  segment%field(m)%name = trim(fields(m))
447  filename = trim(inputdir)//trim(filename)
448  fieldname = trim(fieldname)//trim(suffix)
449  call field_size(filename,fieldname,siz,no_domain=.true.)
450  if (siz(4) == 1) segment%values_needed = .false.
451  if (segment%on_pe) then
452  if (modulo(siz(1),2) == 0 .or. modulo(siz(2),2) == 0) then
453  call mom_error(fatal,'segment data are not on the supergrid')
454  endif
455  siz2(1)=1
456  if (siz(1)>1) then
457  siz2(1)=(siz(1)-1)/2
458  endif
459  siz2(2)=1
460  if (siz(2)>1) then
461  siz2(2)=(siz(2)-1)/2
462  endif
463  siz2(3)=siz(3)
464 
465  if (segment%is_E_or_W) then
466  allocate(segment%field(m)%buffer_src(isdb:iedb,jsd:jed,siz2(3)))
467  else
468  allocate(segment%field(m)%buffer_src(isd:ied,jsdb:jedb,siz2(3)))
469  endif
470  segment%field(m)%buffer_src(:,:,:)=0.0
471  segment%field(m)%fid = init_external_field(trim(filename),&
472  trim(fieldname),ignore_axis_atts=.true.,threading=single_file)
473  if (siz(3) > 1) then
474  fieldname = 'dz_'//trim(fieldname)
475  call field_size(filename,fieldname,siz,no_domain=.true.)
476  if (segment%is_E_or_W) then
477  allocate(segment%field(m)%dz_src(isdb:iedb,jsd:jed,siz(3)))
478  else
479  allocate(segment%field(m)%dz_src(isd:ied,jsdb:jedb,siz(3)))
480  endif
481  segment%field(m)%dz_src(:,:,:)=0.0
482  segment%field(m)%nk_src=siz(3)
483  segment%field(m)%fid_dz = init_external_field(trim(filename),trim(fieldname),&
484  ignore_axis_atts=.true.,threading=single_file)
485  else
486  segment%field(m)%nk_src=1
487  endif
488  endif
489  else
490  segment%field(m)%fid = -1
491  segment%field(m)%value = value
492  endif
493  enddo
494  enddo
495 
496  call mpp_set_current_pelist(saved_pelist)
497 
498 end subroutine initialize_segment_data
499 
500 !< Define indices for segment and store in hor_index_type
501 !< using global segment bounds corresponding to q-points
502 subroutine setup_segment_indices(G, seg, Is_obc, Ie_obc, Js_obc, Je_obc)
503  type(dyn_horgrid_type), intent(in) :: G !< grid type
504  type(obc_segment_type), intent(inout) :: seg !< Open boundary segment
505  integer, intent(in) :: Is_obc !< Q-point global i-index of start of segment
506  integer, intent(in) :: Ie_obc !< Q-point global i-index of end of segment
507  integer, intent(in) :: Js_obc !< Q-point global j-index of start of segment
508  integer, intent(in) :: Je_obc !< Q-point global j-index of end of segment
509  ! Local variables
510  integer :: Isg,Ieg,Jsg,Jeg
511 
512 ! if (.not. G%Domain%symmetric) call MOM_error(FATAL, "MOM_open_boundary.F90, setup_segment_indices: "//&
513 ! "Need to compile in symmetric mode")
514 
515  ! Isg, Ieg will be I*_obc in global space
516  if (ie_obc<is_obc) then
517  isg=ie_obc;ieg=is_obc
518  else
519  isg=is_obc;ieg=ie_obc
520  endif
521  if (je_obc<js_obc) then
522  jsg=je_obc;jeg=js_obc
523  else
524  jsg=js_obc;jeg=je_obc
525  endif
526 
527  ! Global space I*_obc but sorted
528  seg%HI%IsgB = isg ; seg%HI%IegB = ieg
529  seg%HI%isg = isg+1 ; seg%HI%ieg = ieg
530  seg%HI%JsgB = jsg ; seg%HI%JegB = jeg
531  seg%HI%jsg = jsg+1 ; seg%HI%Jeg = jeg
532 
533  ! Move into local index space
534  isg = isg - g%idg_offset
535  jsg = jsg - g%jdg_offset
536  ieg = ieg - g%idg_offset
537  jeg = jeg - g%jdg_offset
538 
539  ! This is the i-extent of the segment on this PE.
540  ! The values are nonsence if the segment is not on this PE.
541  seg%HI%IsdB = min( max(isg, g%HI%IsdB), g%HI%IedB)
542  seg%HI%IedB = min( max(ieg, g%HI%IsdB), g%HI%IedB)
543  seg%HI%isd = min( max(isg+1, g%HI%isd), g%HI%ied)
544  seg%HI%ied = min( max(ieg, g%HI%isd), g%HI%ied)
545  seg%HI%IscB = min( max(isg, g%HI%IscB), g%HI%IecB)
546  seg%HI%IecB = min( max(ieg, g%HI%IscB), g%HI%IecB)
547  seg%HI%isc = min( max(isg+1, g%HI%isc), g%HI%iec)
548  seg%HI%iec = min( max(ieg, g%HI%isc), g%HI%iec)
549 
550  ! This is the j-extent of the segment on this PE.
551  ! The values are nonsence if the segment is not on this PE.
552  seg%HI%JsdB = min( max(jsg, g%HI%JsdB), g%HI%JedB)
553  seg%HI%JedB = min( max(jeg, g%HI%JsdB), g%HI%JedB)
554  seg%HI%jsd = min( max(jsg+1, g%HI%jsd), g%HI%jed)
555  seg%HI%jed = min( max(jeg, g%HI%jsd), g%HI%jed)
556  seg%HI%JscB = min( max(jsg, g%HI%JscB), g%HI%JecB)
557  seg%HI%JecB = min( max(jeg, g%HI%JscB), g%HI%JecB)
558  seg%HI%jsc = min( max(jsg+1, g%HI%jsc), g%HI%jec)
559  seg%HI%jec = min( max(jeg, g%HI%jsc), g%HI%jec)
560 
561 end subroutine setup_segment_indices
562 
563 !> Parse an OBC_SEGMENT_%%% string starting with "I=" and configure placement and type of OBC accordingly
564 subroutine setup_u_point_obc(OBC, G, segment_str, l_seg)
565  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
566  type(dyn_horgrid_type), intent(in) :: G !< Ocean grid structure
567  character(len=*), intent(in) :: segment_str !< A string in form of "I=%,J=%:%,string"
568  integer, intent(in) :: l_seg !< which segment is this?
569  ! Local variables
570  integer :: I_obc, Js_obc, Je_obc ! Position of segment in global index space
571  integer :: j, a_loop
572  character(len=32) :: action_str(5)
573 
574  ! This returns the global indices for the segment
575  call parse_segment_str(g%ieg, g%jeg, segment_str, i_obc, js_obc, je_obc, action_str )
576 
577  call setup_segment_indices(g, obc%segment(l_seg),i_obc,i_obc,js_obc,je_obc)
578 
579  i_obc = i_obc - g%idg_offset ! Convert to local tile indices on this tile
580  js_obc = js_obc - g%jdg_offset ! Convert to local tile indices on this tile
581  je_obc = je_obc - g%jdg_offset ! Convert to local tile indices on this tile
582 
583  ! Hack to extend segment by one point
584  if (obc%extend_segments) then
585  if (js_obc<je_obc) then
586  js_obc = js_obc - 1 ; je_obc = je_obc + 1
587  else
588  js_obc = js_obc + 1 ; je_obc = je_obc - 1
589  endif
590  endif
591 
592  if (je_obc>js_obc) then
593  obc%segment(l_seg)%direction = obc_direction_e
594  else if (je_obc<js_obc) then
595  obc%segment(l_seg)%direction = obc_direction_w
596  j=js_obc;js_obc=je_obc;je_obc=j
597  endif
598 
599  obc%segment(l_seg)%on_pe = .false.
600 
601  do a_loop = 1,5 ! up to 5 options available
602  if (len_trim(action_str(a_loop)) == 0) then
603  cycle
604  elseif (trim(action_str(a_loop)) == 'FLATHER') then
605  obc%segment(l_seg)%Flather = .true.
606  obc%segment(l_seg)%open = .true.
607  obc%Flather_u_BCs_exist_globally = .true.
608  obc%open_u_BCs_exist_globally = .true.
609  elseif (trim(action_str(a_loop)) == 'ORLANSKI') then
610  obc%segment(l_seg)%radiation = .true.
611  obc%segment(l_seg)%open = .true.
612  obc%open_u_BCs_exist_globally = .true.
613  elseif (trim(action_str(a_loop)) == 'OBLIQUE') then
614  obc%segment(l_seg)%oblique = .true.
615  obc%segment(l_seg)%open = .true.
616  obc%oblique_BCs_exist_globally = .true.
617  obc%open_u_BCs_exist_globally = .true.
618  elseif (trim(action_str(a_loop)) == 'NUDGED') then
619  obc%segment(l_seg)%nudged = .true.
620  obc%segment(l_seg)%Tnudge_in = 1.0/(3*86400)
621  obc%segment(l_seg)%Tnudge_out = 1.0/(360*86400)
622  obc%nudged_u_BCs_exist_globally = .true.
623  elseif (trim(action_str(a_loop)) == 'GRADIENT') then
624  obc%segment(l_seg)%gradient = .true.
625  obc%segment(l_seg)%open = .true.
626  obc%open_u_BCs_exist_globally = .true.
627  elseif (trim(action_str(a_loop)) == 'LEGACY') then
628  obc%segment(l_seg)%Flather = .true.
629  obc%segment(l_seg)%radiation = .true.
630  obc%Flather_u_BCs_exist_globally = .true.
631  obc%open_u_BCs_exist_globally = .true.
632  elseif (trim(action_str(a_loop)) == 'SIMPLE') then
633  obc%segment(l_seg)%specified = .true.
634  obc%specified_u_BCs_exist_globally = .true. ! This avoids deallocation
635  ! Hack to undo the hack above for SIMPLE BCs
636  if (obc%extend_segments) then
637  js_obc = js_obc + 1
638  je_obc = je_obc - 1
639  endif
640  else
641  call mom_error(fatal, "MOM_open_boundary.F90, setup_u_point_obc: "//&
642  "String '"//trim(action_str(a_loop))//"' not understood.")
643  endif
644 
645  if (i_obc<g%HI%IsdB .or. i_obc>g%HI%IedB) return ! Boundary is not on tile
646  if (js_obc<g%HI%JsdB .and. je_obc<g%HI%JsdB) return ! Segment is not on tile
647  if (js_obc>g%HI%JedB) return ! Segment is not on tile
648  enddo ! a_loop
649 
650  obc%segment(l_seg)%on_pe = .true.
651  obc%segment(l_seg)%is_E_or_W = .true.
652 
653  do j=g%HI%jsd, g%HI%jed
654  if (j>js_obc .and. j<=je_obc) then
655  obc%segnum_u(i_obc,j) = l_seg
656  endif
657  enddo
658  obc%segment(l_seg)%Is_obc = i_obc
659  obc%segment(l_seg)%Ie_obc = i_obc
660  obc%segment(l_seg)%Js_obc = js_obc
661  obc%segment(l_seg)%Je_obc = je_obc
662  call allocate_obc_segment_data(obc, obc%segment(l_seg))
663 
664 end subroutine setup_u_point_obc
665 
666 !> Parse an OBC_SEGMENT_%%% string starting with "J=" and configure placement and type of OBC accordingly
667 subroutine setup_v_point_obc(OBC, G, segment_str, l_seg)
668  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
669  type(dyn_horgrid_type), intent(in) :: G !< Ocean grid structure
670  character(len=*), intent(in) :: segment_str !< A string in form of "J=%,I=%:%,string"
671  integer, intent(in) :: l_seg !< which segment is this?
672  ! Local variables
673  integer :: J_obc, Is_obc, Ie_obc ! Position of segment in global index space
674  integer :: i, a_loop
675  character(len=32) :: action_str(5)
676 
677  ! This returns the global indices for the segment
678  call parse_segment_str(g%ieg, g%jeg, segment_str, j_obc, is_obc, ie_obc, action_str )
679 
680  call setup_segment_indices(g, obc%segment(l_seg),is_obc,ie_obc,j_obc,j_obc)
681 
682  j_obc = j_obc - g%jdg_offset ! Convert to local tile indices on this tile
683  is_obc = is_obc - g%idg_offset ! Convert to local tile indices on this tile
684  ie_obc = ie_obc - g%idg_offset ! Convert to local tile indices on this tile
685 
686  ! Hack to extend segment by one point
687  if (obc%extend_segments) then
688  if (is_obc<ie_obc) then
689  is_obc = is_obc - 1 ; ie_obc = ie_obc + 1
690  else
691  is_obc = is_obc + 1 ; ie_obc = ie_obc - 1
692  endif
693  endif
694 
695  if (ie_obc>is_obc) then
696  obc%segment(l_seg)%direction = obc_direction_s
697  else if (ie_obc<is_obc) then
698  obc%segment(l_seg)%direction = obc_direction_n
699  i=is_obc;is_obc=ie_obc;ie_obc=i
700  endif
701 
702  obc%segment(l_seg)%on_pe = .false.
703 
704  do a_loop = 1,5
705  if (len_trim(action_str(a_loop)) == 0) then
706  cycle
707  elseif (trim(action_str(a_loop)) == 'FLATHER') then
708  obc%segment(l_seg)%Flather = .true.
709  obc%segment(l_seg)%open = .true.
710  obc%Flather_v_BCs_exist_globally = .true.
711  obc%open_v_BCs_exist_globally = .true.
712  elseif (trim(action_str(a_loop)) == 'ORLANSKI') then
713  obc%segment(l_seg)%radiation = .true.
714  obc%segment(l_seg)%open = .true.
715  obc%open_v_BCs_exist_globally = .true.
716  elseif (trim(action_str(a_loop)) == 'OBLIQUE') then
717  obc%segment(l_seg)%oblique = .true.
718  obc%segment(l_seg)%open = .true.
719  obc%oblique_BCs_exist_globally = .true.
720  obc%open_v_BCs_exist_globally = .true.
721  elseif (trim(action_str(a_loop)) == 'NUDGED') then
722  obc%segment(l_seg)%nudged = .true.
723  obc%segment(l_seg)%Tnudge_in = 1.0/(3*86400)
724  obc%segment(l_seg)%Tnudge_out = 1.0/(360*86400)
725  obc%nudged_v_BCs_exist_globally = .true.
726  elseif (trim(action_str(a_loop)) == 'GRADIENT') then
727  obc%segment(l_seg)%gradient = .true.
728  obc%segment(l_seg)%open = .true.
729  obc%open_v_BCs_exist_globally = .true.
730  elseif (trim(action_str(a_loop)) == 'LEGACY') then
731  obc%segment(l_seg)%radiation = .true.
732  obc%segment(l_seg)%Flather = .true.
733  obc%Flather_v_BCs_exist_globally = .true.
734  obc%open_v_BCs_exist_globally = .true.
735  elseif (trim(action_str(a_loop)) == 'SIMPLE') then
736  obc%segment(l_seg)%specified = .true.
737  obc%specified_v_BCs_exist_globally = .true. ! This avoids deallocation
738  ! Hack to undo the hack above for SIMPLE BCs
739  if (obc%extend_segments) then
740  is_obc = is_obc + 1
741  ie_obc = ie_obc - 1
742  endif
743  else
744  call mom_error(fatal, "MOM_open_boundary.F90, setup_v_point_obc: "//&
745  "String '"//trim(action_str(a_loop))//"' not understood.")
746  endif
747 
748  if (j_obc<g%HI%JsdB .or. j_obc>g%HI%JedB) return ! Boundary is not on tile
749  if (is_obc<g%HI%IsdB .and. ie_obc<g%HI%IsdB) return ! Segment is not on tile
750  if (is_obc>g%HI%IedB) return ! Segment is not on tile
751  enddo ! a_loop
752 
753  obc%segment(l_seg)%on_pe = .true.
754  obc%segment(l_seg)%is_N_or_S = .true.
755 
756  do i=g%HI%isd, g%HI%ied
757  if (i>is_obc .and. i<=ie_obc) then
758  obc%segnum_v(i,j_obc) = l_seg
759  endif
760  enddo
761  obc%segment(l_seg)%Is_obc = is_obc
762  obc%segment(l_seg)%Ie_obc = ie_obc
763  obc%segment(l_seg)%Js_obc = j_obc
764  obc%segment(l_seg)%Je_obc = j_obc
765  call allocate_obc_segment_data(obc, obc%segment(l_seg))
766 
767 end subroutine setup_v_point_obc
768 
769 !> Parse an OBC_SEGMENT_%%% string
770 subroutine parse_segment_str(ni_global, nj_global, segment_str, l, m, n, action_str )
771  integer, intent(in) :: ni_global !< Number of h-points in zonal direction
772  integer, intent(in) :: nj_global !< Number of h-points in meridional direction
773  character(len=*), intent(in) :: segment_str !< A string in form of "I=l,J=m:n,string" or "J=l,I=m,n,string"
774  integer, intent(out) :: l !< The value of I=l, if segment_str begins with I=l, or the value of J=l
775  integer, intent(out) :: m !< The value of J=m, if segment_str begins with I=, or the value of I=m
776  integer, intent(out) :: n !< The value of J=n, if segment_str begins with I=, or the value of I=n
777  character(len=*), intent(out) :: action_str(:) !< The "string" part of segment_str
778  ! Local variables
779  character(len=24) :: word1, word2, m_word, n_word !< Words delineated by commas in a string in form of "I=%,J=%:%,string"
780  integer :: l_max !< Either ni_global or nj_global, depending on whether segment_str begins with "I=" or "J="
781  integer :: mn_max !< Either nj_global or ni_global, depending on whether segment_str begins with "I=" or "J="
782  integer :: j
783 
784  ! Process first word which will started with either 'I=' or 'J='
785  word1 = extract_word(segment_str,',',1)
786  word2 = extract_word(segment_str,',',2)
787  if (word1(1:2)=='I=') then
788  l_max = ni_global
789  mn_max = nj_global
790  if (.not. (word2(1:2)=='J=')) call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
791  "Second word of string '"//trim(segment_str)//"' must start with 'J='.")
792  elseif (word1(1:2)=='J=') then ! Note that the file_parser uniformaly expands "=" to " = "
793  l_max = nj_global
794  mn_max = ni_global
795  if (.not. (word2(1:2)=='I=')) call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
796  "Second word of string '"//trim(segment_str)//"' must start with 'I='.")
797  else
798  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str"//&
799  "String '"//segment_str//"' must start with 'I=' or 'J='.")
800  endif
801 
802  ! Read l
803  l = interpret_int_expr( word1(3:24), l_max )
804  if (l<0 .or. l>l_max) then
805  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
806  "First value from string '"//trim(segment_str)//"' is outside of the physical domain.")
807  endif
808 
809  ! Read m
810  m_word = extract_word(word2(3:24),':',1)
811  m = interpret_int_expr( m_word, mn_max )
812  if (m<-1 .or. m>mn_max+1) then
813  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
814  "Beginning of range in string '"//trim(segment_str)//"' is outside of the physical domain.")
815  endif
816 
817  ! Read m
818  n_word = extract_word(word2(3:24),':',2)
819  n = interpret_int_expr( n_word, mn_max )
820  if (n<-1 .or. n>mn_max+1) then
821  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
822  "End of range in string '"//trim(segment_str)//"' is outside of the physical domain.")
823  endif
824 
825  if (abs(n-m)==0) then
826  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
827  "Range in string '"//trim(segment_str)//"' must span one cell.")
828  endif
829 
830  ! Type of open boundary condition
831  do j = 1, size(action_str)
832  action_str(j) = extract_word(segment_str,',',2+j)
833  enddo
834 
835  contains
836 
837  ! Returns integer value interpreted from string in form of %I, N or N-%I
838  integer function interpret_int_expr(string, imax)
839  character(len=*), intent(in) :: string !< Integer in form or %I, N or N-%I
840  integer, intent(in) :: imax !< Value to replace 'N' with
841  ! Local variables
842  integer slen
843 
844  slen = len_trim(string)
845  if (slen==0) call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str"//&
846  "Parsed string was empty!")
847  if (len_trim(string)==1 .and. string(1:1)=='N') then
848  interpret_int_expr = imax
849  elseif (string(1:1)=='N') then
850  read(string(2:slen),*,err=911) interpret_int_expr
852  else
853  read(string(1:slen),*,err=911) interpret_int_expr
854  endif
855  return
856  911 call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str"//&
857  "Problem reading value from string '"//trim(string)//"'.")
858  end function interpret_int_expr
859 end subroutine parse_segment_str
860 
861 !> Parse an OBC_SEGMENT_%%%_DATA string
862  subroutine parse_segment_data_str(segment_str, var, value, filenam, fieldnam, fields, num_fields, debug )
863  character(len=*), intent(in) :: segment_str !< A string in form of "VAR1=file:foo1.nc(varnam1),VAR2=file:foo2.nc(varnam2),..."
864  character(len=*), intent(in), optional :: var !< The name of the variable for which parameters are needed
865  character(len=*), intent(out), optional :: filenam !< The name of the input file if using "file" method
866  character(len=*), intent(out), optional :: fieldnam !< The name of the variable in the input file if using "file" method
867  real, intent(out), optional :: value !< A constant value if using the "value" method
868  character(len=*), dimension(MAX_OBC_FIELDS), intent(out), optional :: fields !< List of fieldnames for each segment
869  integer, intent(out), optional :: num_fields
870  logical, intent(in), optional :: debug
871  ! Local variables
872  character(len=128) :: word1, word2, word3, method
873  integer :: lword, nfields, n, m, orient
874  logical :: continue,dbg
875  character(len=32), dimension(MAX_OBC_FIELDS) :: flds
876 
877  nfields=0
878  continue=.true.
879  dbg=.false.
880  if (PRESENT(debug)) dbg=debug
881 
882  do while (continue)
883  word1 = extract_word(segment_str,',',nfields+1)
884  if (trim(word1) == '') exit
885  nfields=nfields+1
886  word2 = extract_word(word1,'=',1)
887  flds(nfields) = trim(word2)
888  enddo
889 
890  if (PRESENT(fields)) then
891  do n=1,nfields
892  fields(n) = flds(n)
893  enddo
894  endif
895 
896  if (PRESENT(num_fields)) then
897  num_fields=nfields
898  return
899  endif
900 
901  m=0
902  if (PRESENT(var)) then
903  do n=1,nfields
904  if (trim(var)==trim(flds(n))) then
905  m=n
906  exit
907  endif
908  enddo
909  if (m==0) then
910  call abort()
911  endif
912 
913  ! Process first word which will start with the fieldname
914  word3 = extract_word(segment_str,',',m)
915  word1 = extract_word(word3,':',1)
916 ! if (trim(word1) == '') exit
917  word2 = extract_word(word1,'=',1)
918  if (trim(word2) == trim(var)) then
919  method=trim(extract_word(word1,'=',2))
920  lword=len_trim(method)
921  if (method(lword-3:lword) == 'file') then
922  ! raise an error id filename/fieldname not in argument list
923  word1 = extract_word(word3,':',2)
924  filenam = extract_word(word1,'(',1)
925  fieldnam = extract_word(word1,'(',2)
926  lword=len_trim(fieldnam)
927  fieldnam = fieldnam(1:lword-1) ! remove trailing parenth
928  value=-999.
929  elseif (method(lword-4:lword) == 'value') then
930  filenam = 'none'
931  fieldnam = 'none'
932  word1 = extract_word(word3,':',2)
933  lword=len_trim(word1)
934  read(word1(1:lword),*,end=986,err=987) value
935  endif
936  endif
937  endif
938 
939  return
940  986 call mom_error(fatal,'End of record while parsing segment data specification! '//trim(segment_str))
941  987 call mom_error(fatal,'Error while parsing segment data specification! '//trim(segment_str))
942 
943  end subroutine parse_segment_data_str
944 
945 !> Initialize open boundary control structure
946 subroutine open_boundary_init(G, param_file, OBC)
947  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
948  type(param_file_type), intent(in) :: param_file !< Parameter file handle
949  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
950  ! Local variables
951 
952  if (.not.associated(obc)) return
953 
954  if ( obc%Flather_u_BCs_exist_globally .or. obc%Flather_v_BCs_exist_globally ) then
955  call get_param(param_file, mdl, "OBC_RADIATION_MAX", obc%rx_max, &
956  "The maximum magnitude of the baroclinic radiation \n"//&
957  "velocity (or speed of characteristics). This is only \n"//&
958  "used if one of the open boundary segments is using Orlanski.", &
959  units="m s-1", default=10.0)
960  call get_param(param_file, mdl, "OBC_RAD_VEL_WT", obc%gamma_uv, &
961  "The relative weighting for the baroclinic radiation \n"//&
962  "velocities (or speed of characteristics) at the new \n"//&
963  "time level (1) or the running mean (0) for velocities. \n"//&
964  "Valid values range from 0 to 1. This is only used if \n"//&
965  "one of the open boundary segments is using Orlanski.", &
966  units="nondim", default=0.3)
967  call get_param(param_file, mdl, "OBC_RAD_THICK_WT", obc%gamma_h, &
968  "The relative weighting for the baroclinic radiation \n"//&
969  "velocities (or speed of characteristics) at the new \n"//&
970  "time level (1) or the running mean (0) for thicknesses. \n"//&
971  "Valid values range from 0 to 1. This is only used if \n"//&
972  "one of the open boundary segments is using Orlanski.", &
973  units="nondim", default=0.2)
974  endif
975 
976  id_clock_pass = cpu_clock_id('(Ocean OBC halo updates)', grain=clock_routine)
977 
978 end subroutine open_boundary_init
979 
980 logical function open_boundary_query(OBC, apply_open_OBC, apply_specified_OBC, apply_Flather_OBC, apply_nudged_OBC, needs_ext_seg_data)
981  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
982  logical, optional, intent(in) :: apply_open_OBC !< If present, returns True if specified_*_BCs_exist_globally is true
983  logical, optional, intent(in) :: apply_specified_OBC !< If present, returns True if specified_*_BCs_exist_globally is true
984  logical, optional, intent(in) :: apply_Flather_OBC !< If present, returns True if Flather_*_BCs_exist_globally is true
985  logical, optional, intent(in) :: apply_nudged_OBC !< If present, returns True if nudged_*_BCs_exist_globally is true
986  logical, optional, intent(in) :: needs_ext_seg_data !< If present, returns True if external segment data needed
987  open_boundary_query = .false.
988  if (.not. associated(obc)) return
989  if (present(apply_open_obc)) open_boundary_query = obc%open_u_BCs_exist_globally .or. &
990  obc%open_v_BCs_exist_globally
991  if (present(apply_specified_obc)) open_boundary_query = obc%specified_u_BCs_exist_globally .or. &
992  obc%specified_v_BCs_exist_globally
993  if (present(apply_flather_obc)) open_boundary_query = obc%Flather_u_BCs_exist_globally .or. &
994  obc%Flather_v_BCs_exist_globally
995  if (present(apply_nudged_obc)) open_boundary_query = obc%nudged_u_BCs_exist_globally .or. &
996  obc%nudged_v_BCs_exist_globally
997  if (present(needs_ext_seg_data)) open_boundary_query = obc%needs_IO_for_data
998 
999 end function open_boundary_query
1000 
1001 !> Deallocate open boundary data
1002 subroutine open_boundary_dealloc(OBC)
1003  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
1004  if (.not. associated(obc)) return
1005  if (associated(obc%segment)) deallocate(obc%segment)
1006  if (associated(obc%segnum_u)) deallocate(obc%segnum_u)
1007  if (associated(obc%segnum_v)) deallocate(obc%segnum_v)
1008  deallocate(obc)
1009 end subroutine open_boundary_dealloc
1010 
1011 !> Close open boundary data
1012 subroutine open_boundary_end(OBC)
1013  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
1014  call open_boundary_dealloc(obc)
1015 end subroutine open_boundary_end
1016 
1017 !> Sets the slope of bathymetry normal to an open bounndary to zero.
1018 subroutine open_boundary_impose_normal_slope(OBC, G, depth)
1019  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
1020  type(dyn_horgrid_type), intent(in) :: G !< Ocean grid structure
1021  real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: depth !< Bathymetry at h-points
1022  ! Local variables
1023  integer :: i, j, n
1024  type(obc_segment_type), pointer :: segment
1025 
1026  if (.not.associated(obc)) return
1027 
1028  if (.not.(obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)) &
1029  return
1030 
1031  do n=1,obc%number_of_segments
1032  segment=>obc%segment(n)
1033  if (.not. segment%on_pe .or. segment%specified) cycle
1034  if (segment%direction == obc_direction_e) then
1035  i=segment%HI%IsdB
1036  do j=segment%HI%jsd,segment%HI%jed
1037  depth(i+1,j) = depth(i,j)
1038  enddo
1039  elseif (segment%direction == obc_direction_w) then
1040  i=segment%HI%IsdB
1041  do j=segment%HI%jsd,segment%HI%jed
1042  depth(i,j) = depth(i+1,j)
1043  enddo
1044  elseif (segment%direction == obc_direction_n) then
1045  j=segment%HI%JsdB
1046  do i=segment%HI%isd,segment%HI%ied
1047  depth(i,j+1) = depth(i,j)
1048  enddo
1049  elseif (segment%direction == obc_direction_s) then
1050  j=segment%HI%JsdB
1051  do i=segment%HI%isd,segment%HI%ied
1052  depth(i,j) = depth(i,j+1)
1053  enddo
1054  endif
1055  enddo
1056 
1057 
1058 end subroutine open_boundary_impose_normal_slope
1059 
1060 !> Reconcile masks and open boundaries, deallocate OBC on PEs where it is not needed.
1061 !! Also adjust u- and v-point cell area on specified open boundaries.
1062 subroutine open_boundary_impose_land_mask(OBC, G, areaCu, areaCv)
1063  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
1064  type(dyn_horgrid_type), intent(in) :: G !< Ocean grid structure
1065  real, dimension(SZIB_(G),SZJ_(G)), intent(inout) :: areaCu !< Area of a u-cell (m2)
1066  real, dimension(SZI_(G),SZJB_(G)), intent(inout) :: areaCv !< Area of a u-cell (m2)
1067  ! Local variables
1068  integer :: i, j, n
1069  type(obc_segment_type), pointer :: segment
1070  logical :: any_U, any_V
1071 
1072  if (.not.associated(obc)) return
1073 
1074  do n=1,obc%number_of_segments
1075  segment=>obc%segment(n)
1076  if (.not. segment%on_pe .or. segment%specified) cycle
1077  if (segment%is_E_or_W) then
1078  ! Sweep along u-segments and delete the OBC for blocked points.
1079  i=segment%HI%IsdB
1080  do j=segment%HI%jsd,segment%HI%jed
1081  if (g%mask2dCu(i,j) == 0) obc%segnum_u(i,j) = obc_none
1082  enddo
1083  else
1084  ! Sweep along v-segments and delete the OBC for blocked points.
1085  j=segment%HI%JsdB
1086  do i=segment%HI%isd,segment%HI%ied
1087  if (g%mask2dCv(i,j) == 0) obc%segnum_v(i,j) = obc_none
1088  enddo
1089  endif
1090  enddo
1091 
1092  do n=1,obc%number_of_segments
1093  segment=>obc%segment(n)
1094  if (.not. segment%on_pe .or. .not. segment%specified) cycle
1095  if (segment%is_E_or_W) then
1096  ! Sweep along u-segments and for %specified BC points reset the u-point area which was masked out
1097  i=segment%HI%IsdB
1098  do j=segment%HI%jsd,segment%HI%jed
1099  if (segment%direction == obc_direction_e) then
1100  areacu(i,j) = g%areaT(i,j)
1101  !G%IareaCu(I,j) = G%IareaT(i,j) ?
1102  else ! West
1103  areacu(i,j) = g%areaT(i+1,j)
1104  !G%IareaCu(I,j) = G%IareaT(i+1,j) ?
1105  endif
1106  enddo
1107  else
1108  ! Sweep along v-segments and for %specified BC points reset the v-point area which was masked out
1109  j=segment%HI%JsdB
1110  do i=segment%HI%isd,segment%HI%ied
1111  if (segment%direction == obc_direction_s) then
1112  areacv(i,j) = g%areaT(i,j+1)
1113  !G%IareaCv(i,J) = G%IareaT(i,j+1) ?
1114  else ! North
1115  areacu(i,j) = g%areaT(i,j)
1116  !G%IareaCu(i,J) = G%IareaT(i,j) ?
1117  endif
1118  enddo
1119  endif
1120  enddo
1121 
1122  ! G%mask2du will be open wherever bathymetry allows it.
1123  ! Bathymetry outside of the open boundary was adjusted to match
1124  ! the bathymetry inside so these points will be open unless the
1125  ! bathymetry inside the boundary was too shallow and flagged as land.
1126  any_u = .false.
1127  any_v = .false.
1128  do n=1,obc%number_of_segments
1129  segment=>obc%segment(n)
1130  if (.not. segment%on_pe) cycle
1131  if (segment%is_E_or_W) then
1132  i=segment%HI%IsdB
1133  do j=segment%HI%jsd,segment%HI%jed
1134  if (obc%segnum_u(i,j) /= obc_none) any_u = .true.
1135  enddo
1136  else
1137  j=segment%HI%JsdB
1138  do i=segment%HI%isd,segment%HI%ied
1139  if (obc%segnum_v(i,j) /= obc_none) any_v = .true.
1140  enddo
1141  endif
1142  enddo
1143 
1144  obc%OBC_pe = .true.
1145  if (.not.(any_u .or. any_v)) obc%OBC_pe = .false.
1146 
1147 end subroutine open_boundary_impose_land_mask
1148 
1149 !> Apply radiation conditions to 3D u,v at open boundaries
1150 subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, dt)
1151  type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure
1152  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
1153  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: u_new !< New u values on open boundaries
1154  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u_old !< Original unadjusted u
1155  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: v_new !< New v values on open boundaries
1156  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v_old !< Original unadjusted v
1157  real, intent(in) :: dt !< Appropriate timestep
1158  ! Local variables
1159  real :: dhdt, dhdx, dhdy, gamma_u, gamma_h, gamma_v
1160  real :: cff, Cx, Cy, tau
1161  real :: rx_max, ry_max ! coefficients for radiation
1162  real :: rx_new, rx_avg ! coefficients for radiation
1163  real :: ry_new, ry_avg ! coefficients for radiation
1164  real, parameter :: eps = 1.0e-20
1165  type(obc_segment_type), pointer :: segment
1166  integer :: i, j, k, is, ie, js, je, nz, n
1167  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
1168 
1169  if (.not.associated(obc)) return
1170 
1171  if (.not.(obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)) &
1172  return
1173 
1174  gamma_u = obc%gamma_uv ; gamma_v = obc%gamma_uv ; gamma_h = obc%gamma_h
1175  rx_max = obc%rx_max ; ry_max = obc%rx_max
1176  do n=1,obc%number_of_segments
1177  segment=>obc%segment(n)
1178  if (.not. segment%on_pe) cycle
1179  if (segment%oblique) call gradient_at_q_points(g,segment,u_old,v_old)
1180  if (segment%direction == obc_direction_e) then
1181  i=segment%HI%IscB
1182  do k=1,nz ; do j=segment%HI%jsc,segment%HI%jec
1183  if (segment%radiation) then
1184  dhdt = u_old(i-1,j,k)-u_new(i-1,j,k) !old-new
1185  dhdx = u_new(i-1,j,k)-u_new(i-2,j,k) !in new time backward sasha for I-1
1186  rx_new = 0.0
1187  if (dhdt*dhdx > 0.0) rx_new = min( (dhdt/dhdx), rx_max) ! outward phase speed
1188  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
1189  segment%rx_normal(i,j,k) = rx_avg
1190  segment%normal_vel(i,j,k) = (u_old(i,j,k) + rx_avg*u_new(i-1,j,k)) / (1.0+rx_avg)
1191  elseif (segment%oblique) then
1192  dhdt = u_old(i-1,j,k)-u_new(i-1,j,k) !old-new
1193  dhdx = u_new(i-1,j,k)-u_new(i-2,j,k) !in new time backward sasha for I-1
1194 ! if (segment%oblique) then
1195  if (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) > 0.0) then
1196  dhdy = segment%grad_normal(j-1,1,k)
1197  elseif (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) == 0.0) then
1198  dhdy = 0.0
1199  else
1200  dhdy = segment%grad_normal(j,1,k)
1201  endif
1202 ! endif
1203  if (dhdt*dhdx < 0.0) dhdt = 0.0
1204  if (dhdx == 0.0) dhdx=eps ! avoid segv
1205  cx = min(dhdt/dhdx,rx_max) ! default to normal radiation
1206 ! Cy = 0.0
1207  cff = max(dhdx*dhdx,eps)
1208 ! if (segment%oblique) then
1209  cff = max(dhdx*dhdx + dhdy*dhdy, eps)
1210  if (dhdy==0.) dhdy=eps ! avoid segv
1211  cy = min(cff,max(dhdt/dhdy,-cff))
1212 ! endif
1213  segment%normal_vel(i,j,k) = ((cff*u_old(i,j,k) + cx*u_new(i-1,j,k)) - &
1214  (max(cy,0.0)*segment%grad_normal(j-1,2,k) + min(cy,0.0)*segment%grad_normal(j,2,k))) / (cff + cx)
1215  elseif (segment%gradient) then
1216  segment%normal_vel(i,j,k) = u_new(i-1,j,k)
1217  endif
1218  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1219  if (dhdt*dhdx < 0.0) then
1220  tau = segment%Tnudge_in
1221  else
1222  tau = segment%Tnudge_out
1223  endif
1224  segment%normal_vel(i,j,k) = u_new(i,j,k) + dt*tau*(segment%nudged_normal_vel(i,j,k) - u_old(i,j,k))
1225  endif
1226  enddo; enddo
1227  endif
1228 
1229  if (segment%direction == obc_direction_w) then
1230  i=segment%HI%IscB
1231  do k=1,nz ; do j=segment%HI%jsc,segment%HI%jec
1232  if (segment%radiation) then
1233  dhdt = u_old(i+1,j,k)-u_new(i+1,j,k) !old-new
1234  dhdx = u_new(i+1,j,k)-u_new(i+2,j,k) !in new time forward sasha for I+1
1235  rx_new = 0.0
1236  if (dhdt*dhdx > 0.0) rx_new = min( (dhdt/dhdx), rx_max)
1237  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
1238  segment%rx_normal(i,j,k) = rx_avg
1239  segment%normal_vel(i,j,k) = (u_old(i,j,k) + rx_avg*u_new(i+1,j,k)) / (1.0+rx_avg)
1240  elseif (segment%oblique) then
1241  dhdt = u_old(i+1,j,k)-u_new(i+1,j,k) !old-new
1242  dhdx = u_new(i+1,j,k)-u_new(i+2,j,k) !in new time forward sasha for I+1
1243 ! if (segment%oblique) then
1244  if (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) > 0.0) then
1245  dhdy = segment%grad_normal(j-1,1,k)
1246  elseif (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) == 0.0) then
1247  dhdy = 0.0
1248  else
1249  dhdy = segment%grad_normal(j,1,k)
1250  endif
1251 ! endif
1252  if (dhdt*dhdx < 0.0) dhdt = 0.0
1253  if (dhdx == 0.0) dhdx=eps ! avoid segv
1254  cx = min(dhdt/dhdx,rx_max) ! default to normal flow only
1255 ! Cy = 0.
1256  cff = max(dhdx*dhdx, eps)
1257 ! if (segment%oblique) then
1258  cff = max(dhdx*dhdx + dhdy*dhdy, eps)
1259  if (dhdy==0.) dhdy=eps ! avoid segv
1260  cy = min(cff,max(dhdt/dhdy,-cff))
1261 ! endif
1262  segment%normal_vel(i,j,k) = ((cff*u_old(i,j,k) + cx*u_new(i+1,j,k)) - &
1263  (max(cy,0.0)*segment%grad_normal(j-1,2,k) + min(cy,0.0)*segment%grad_normal(j,2,k))) / (cff + cx)
1264  elseif (segment%gradient) then
1265  segment%normal_vel(i,j,k) = u_new(i+1,j,k)
1266  endif
1267  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1268  if (dhdt*dhdx < 0.0) then
1269  tau = segment%Tnudge_in
1270  else
1271  tau = segment%Tnudge_out
1272  endif
1273  segment%normal_vel(i,j,k) = u_new(i,j,k) + dt*tau*(segment%nudged_normal_vel(i,j,k) - u_old(i,j,k))
1274  endif
1275  enddo; enddo
1276  endif
1277 
1278  if (segment%direction == obc_direction_n) then
1279  j=segment%HI%JscB
1280  do k=1,nz ; do i=segment%HI%isc,segment%HI%iec
1281  if (segment%radiation) then
1282  dhdt = v_old(i,j-1,k)-v_new(i,j-1,k) !old-new
1283  dhdy = v_new(i,j-1,k)-v_new(i,j-2,k) !in new time backward sasha for J-1
1284  ry_new = 0.0
1285  if (dhdt*dhdy > 0.0) ry_new = min( (dhdt/dhdy), ry_max)
1286  ry_avg = (1.0-gamma_v)*segment%rx_normal(i,j,k) + gamma_v*ry_new
1287  segment%rx_normal(i,j,k) = ry_avg
1288  segment%normal_vel(i,j,k) = (v_old(i,j,k) + ry_avg*v_new(i,j-1,k)) / (1.0+ry_avg)
1289  elseif (segment%oblique) then
1290  dhdt = v_old(i,j-1,k)-v_new(i,j-1,k) !old-new
1291  dhdy = v_new(i,j-1,k)-v_new(i,j-2,k) !in new time backward sasha for J-1
1292 ! if (segment%oblique) then
1293  if (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) > 0.0) then
1294  dhdx = segment%grad_normal(i-1,1,k)
1295  elseif (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) == 0.0) then
1296  dhdx = 0.0
1297  else
1298  dhdx = segment%grad_normal(i,1,k)
1299  endif
1300 ! endif
1301  if (dhdt*dhdy < 0.0) dhdt = 0.0
1302  if (dhdy == 0.0) dhdy=eps ! avoid segv
1303  cy = min(dhdt/dhdy,rx_max) ! default to normal flow only
1304 ! Cx = 0
1305  cff = max(dhdy*dhdy, eps)
1306 ! if (segment%oblique) then
1307  cff = max(dhdx*dhdx + dhdy*dhdy, eps)
1308  if (dhdx==0.) dhdx=eps ! avoid segv
1309  cx = min(cff,max(dhdt/dhdx,-cff))
1310 ! endif
1311  segment%normal_vel(i,j,k) = ((cff*v_old(i,j,k) + cy*v_new(i,j-1,k)) - &
1312  (max(cx,0.0)*segment%grad_normal(i-1,2,k) + min(cx,0.0)*segment%grad_normal(i,2,k))) / (cff + cy)
1313  elseif (segment%gradient) then
1314  segment%normal_vel(i,j,k) = v_new(i,j-1,k)
1315  endif
1316  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1317  if (dhdt*dhdy < 0.0) then
1318  tau = segment%Tnudge_in
1319  else
1320  tau = segment%Tnudge_out
1321  endif
1322  segment%normal_vel(i,j,k) = v_new(i,j,k) + dt*tau*(segment%nudged_normal_vel(i,j,k) - v_old(i,j,k))
1323  endif
1324  enddo; enddo
1325  endif
1326 
1327 
1328  if (segment%direction == obc_direction_s) then
1329  j=segment%HI%JscB
1330  do k=1,nz ; do i=segment%HI%isc,segment%HI%iec
1331  if (segment%radiation) then
1332  dhdt = v_old(i,j+1,k)-v_new(i,j+1,k) !old-new
1333  dhdy = v_new(i,j+1,k)-v_new(i,j+2,k) !in new time backward sasha for J-1
1334  ry_new = 0.0
1335  if (dhdt*dhdy > 0.0) ry_new = min( (dhdt/dhdy), ry_max)
1336  ry_avg = (1.0-gamma_v)*segment%rx_normal(i,j,k) + gamma_v*ry_new
1337  segment%rx_normal(i,j,k) = ry_avg
1338  segment%normal_vel(i,j,k) = (v_old(i,j,k) + ry_avg*v_new(i,j+1,k)) / (1.0+ry_avg)
1339  elseif (segment%oblique) then
1340  dhdt = v_old(i,j+1,k)-v_new(i,j+1,k) !old-new
1341  dhdy = v_new(i,j+1,k)-v_new(i,j+2,k) !in new time backward sasha for J-1
1342 ! if (segment%oblique) then
1343  if (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) > 0.0) then
1344  dhdx = segment%grad_normal(i-1,1,k)
1345  elseif (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) == 0.0) then
1346  dhdx = 0.0
1347  else
1348  dhdx = segment%grad_normal(i,1,k)
1349  endif
1350 ! endif
1351  if (dhdt*dhdy < 0.0) dhdt = 0.0
1352  if (dhdy == 0.0) dhdy=eps ! avoid segv
1353  cy = min(dhdt/dhdy,rx_max) ! default to normal flow only
1354 ! Cx = 0
1355  cff = max(dhdy*dhdy, eps)
1356 ! if (segment%oblique) then
1357  cff = max(dhdx*dhdx + dhdy*dhdy, eps)
1358  if (dhdx==0.) dhdx=eps ! avoid segv
1359  cx = min(cff,max(dhdt/dhdx,-cff))
1360 ! endif
1361  segment%normal_vel(i,j,k) = ((cff*v_old(i,j,k) + cy*v_new(i,j+1,k)) - &
1362  (max(cx,0.0)*segment%grad_normal(i-1,2,k) + min(cx,0.0)*segment%grad_normal(i,2,k))) / (cff + cy)
1363  elseif (segment%gradient) then
1364  segment%normal_vel(i,j,k) = v_new(i,j+1,k)
1365  endif
1366  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1367  if (dhdt*dhdy < 0.0) then
1368  tau = segment%Tnudge_in
1369  else
1370  tau = segment%Tnudge_out
1371  endif
1372  segment%normal_vel(i,j,k) = v_new(i,j,k) + dt*tau*(segment%nudged_normal_vel(i,j,k) - v_old(i,j,k))
1373  endif
1374  enddo; enddo
1375  end if
1376  enddo
1377 
1378  ! Actually update u_new, v_new
1379  call open_boundary_apply_normal_flow(obc, g, u_new, v_new)
1380 
1381  call cpu_clock_begin(id_clock_pass)
1382  call pass_vector(u_new, v_new, g%Domain)
1383  call cpu_clock_end(id_clock_pass)
1384 
1385 end subroutine radiation_open_bdry_conds
1386 
1387 !> Applies OBC values stored in segments to 3d u,v fields
1388 subroutine open_boundary_apply_normal_flow(OBC, G, u, v)
1389  ! Arguments
1390  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
1391  type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure
1392  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: u !< u field to update on open boundaries
1393  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: v !< v field to update on open boundaries
1394  ! Local variables
1395  integer :: i, j, k, n
1396  type(obc_segment_type), pointer :: segment
1397 
1398  if (.not.associated(obc)) return ! Bail out if OBC is not available
1399 
1400  do n=1,obc%number_of_segments
1401  segment => obc%segment(n)
1402  if (.not. segment%on_pe) then
1403  cycle
1404  elseif (segment%radiation .or. segment%oblique .or. segment%gradient) then
1405  if (segment%is_E_or_W) then
1406  i=segment%HI%IscB
1407  do k=1,g%ke ; do j=segment%HI%jsc,segment%HI%jec
1408  u(i,j,k) = segment%normal_vel(i,j,k)
1409  enddo; enddo
1410  elseif (segment%is_N_or_S) then
1411  j=segment%HI%JscB
1412  do k=1,g%ke ; do i=segment%HI%isc,segment%HI%iec
1413  v(i,j,k) = segment%normal_vel(i,j,k)
1414  enddo; enddo
1415  endif
1416  endif
1417  enddo
1418 
1419 end subroutine open_boundary_apply_normal_flow
1420 
1421 !> Applies zero values to 3d u,v fields on OBC segments
1422 subroutine open_boundary_zero_normal_flow(OBC, G, u, v)
1423  ! Arguments
1424  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
1425  type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure
1426  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: u !< u field to update on open boundaries
1427  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: v !< v field to update on open boundaries
1428  ! Local variables
1429  integer :: i, j, k, n
1430  type(obc_segment_type), pointer :: segment
1431 
1432  if (.not.associated(obc)) return ! Bail out if OBC is not available
1433 
1434  do n=1,obc%number_of_segments
1435  segment => obc%segment(n)
1436  if (.not. segment%on_pe) then
1437  cycle
1438  elseif (segment%is_E_or_W) then
1439  i=segment%HI%IscB
1440  do k=1,g%ke ; do j=segment%HI%jsc,segment%HI%jec
1441  u(i,j,k) = 0.
1442  enddo; enddo
1443  elseif (segment%is_N_or_S) then
1444  j=segment%HI%JscB
1445  do k=1,g%ke ; do i=segment%HI%isc,segment%HI%iec
1446  v(i,j,k) = 0.
1447  enddo; enddo
1448  endif
1449  enddo
1450 
1451 end subroutine open_boundary_zero_normal_flow
1452 
1453 !> Calculate the tangential gradient of the normal flow at the boundary q-points.
1454 subroutine gradient_at_q_points(G,segment,uvel,vvel)
1455  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
1456  type(obc_segment_type), pointer :: segment !< OBC segment structure
1457  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: uvel !< zonal velocity
1458  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: vvel !< meridional velocity
1459  integer :: i,j,k
1460 
1461  if (.not. segment%on_pe) return
1462 
1463  if (segment%is_E_or_W) then
1464 
1465  if (.not.ASSOCIATED(segment%grad_normal)) then
1466  allocate(segment%grad_normal(segment%HI%JscB:segment%HI%JecB,2,g%ke))
1467  endif
1468 
1469  if (segment%direction == obc_direction_e) then
1470  i=segment%HI%iscB
1471  do k=1,g%ke
1472  do j=segment%HI%JscB,segment%HI%JecB
1473  segment%grad_normal(j,1,k) = (uvel(i-1,j+1,k)-uvel(i-1,j,k)) * g%mask2dBu(i-1,j)
1474  segment%grad_normal(j,2,k) = (uvel(i,j+1,k)-uvel(i,j,k)) * g%mask2dBu(i,j)
1475  enddo
1476  enddo
1477  else ! western segment
1478  i=segment%HI%iscB
1479  do k=1,g%ke
1480  do j=segment%HI%JscB,segment%HI%JecB
1481  segment%grad_normal(j,1,k) = (uvel(i+1,j+1,k)-uvel(i+1,j,k)) * g%mask2dBu(i+1,j)
1482  segment%grad_normal(j,2,k) = (uvel(i,j+1,k)-uvel(i,j,k)) * g%mask2dBu(i,j)
1483  enddo
1484  enddo
1485  endif
1486  else if (segment%is_N_or_S) then
1487 
1488  if (.not.ASSOCIATED(segment%grad_normal)) then
1489  allocate(segment%grad_normal(segment%HI%IscB:segment%HI%IecB,2,g%ke))
1490  endif
1491 
1492  if (segment%direction == obc_direction_n) then
1493  j=segment%HI%jscB
1494  do k=1,g%ke
1495  do i=segment%HI%IscB,segment%HI%IecB
1496  segment%grad_normal(i,1,k) = (vvel(i+1,j-1,k)-vvel(i,j-1,k)) * g%mask2dBu(i,j-1)
1497  segment%grad_normal(i,2,k) = (vvel(i+1,j,k)-vvel(i,j,k)) * g%mask2dBu(i,j)
1498  enddo
1499  enddo
1500  else ! south segment
1501  j=segment%HI%jscB
1502  do k=1,g%ke
1503  do i=segment%HI%IscB,segment%HI%IecB
1504  segment%grad_normal(i,1,k) = (vvel(i+1,j+1,k)-vvel(i,j+1,k)) * g%mask2dBu(i,j+1)
1505  segment%grad_normal(i,2,k) = (vvel(i+1,j,k)-vvel(i,j,k)) * g%mask2dBu(i,j)
1506  enddo
1507  enddo
1508  endif
1509  endif
1510 
1511 end subroutine gradient_at_q_points
1512 
1513 
1514 !> Sets the initial values of the tracer and h open boundary conditions.
1515 !! Also allocates and fills the segment%T and segment%S arrays, but they
1516 !! are not yet used anywhere.
1517 subroutine set_tracer_data(OBC, tv, h, G, PF, tracer_Reg)
1518  type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure
1519  type(ocean_obc_type), pointer :: OBC !< Open boundary structure
1520  type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamics structure
1521  real, dimension(SZI_(G),SZJ_(G), SZK_(G)), intent(inout) :: h !< Thickness
1522  type(param_file_type), intent(in) :: PF !< Parameter file handle
1523  type(tracer_registry_type), pointer :: tracer_Reg !< Tracer registry
1524  ! Local variables
1525  integer :: i, j, k, itt, is, ie, js, je, isd, ied, jsd, jed, nz, n
1526  integer :: isd_off, jsd_off
1527  integer :: IsdB, IedB, JsdB, JedB
1528  type(obc_segment_type), pointer :: segment ! pointer to segment type list
1529  character(len=40) :: mdl = "set_tracer_data" ! This subroutine's name.
1530  character(len=200) :: filename, OBC_file, inputdir ! Strings for file/path
1531 
1532  real :: temp_u(g%domain%niglobal+1,g%domain%njglobal)
1533  real :: temp_v(g%domain%niglobal,g%domain%njglobal+1)
1534 
1535  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
1536  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
1537  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
1538 
1539  ! For now, there are no radiation conditions applied to the thicknesses, since
1540  ! the thicknesses might not be physically motivated. Instead, sponges should be
1541  ! used to enforce the near-boundary layer structure.
1542 
1543  if (associated(tv%T)) then
1544 
1545  call pass_var(tv%T, g%Domain)
1546  call pass_var(tv%S, g%Domain)
1547 
1548  do n=1,obc%number_of_segments
1549  segment => obc%segment(n)
1550  if (.not. segment%on_pe) cycle
1551 
1552  if (segment%is_E_or_W) then
1553  if (.not.ASSOCIATED(segment%T)) then
1554  allocate(segment%T(segment%HI%IsdB,segment%HI%jsd:segment%HI%jed,g%ke))
1555  allocate(segment%S(segment%HI%IsdB,segment%HI%jsd:segment%HI%jed,g%ke))
1556  endif
1557  else if (segment%is_N_or_S) then
1558  if (.not.ASSOCIATED(segment%T)) then
1559  allocate(segment%T(segment%HI%isd:segment%HI%ied,segment%HI%JsdB,g%ke))
1560  allocate(segment%S(segment%HI%isd:segment%HI%ied,segment%HI%JsdB,g%ke))
1561  endif
1562  endif
1563 
1564  if (segment%direction == obc_direction_e) then
1565  i=segment%HI%IsdB
1566  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1567  segment%T(i,j,k) = tv%T(i,j,k)
1568  segment%S(i,j,k) = tv%S(i,j,k)
1569  enddo; enddo
1570  elseif (segment%direction == obc_direction_w) then
1571  i=segment%HI%IsdB
1572  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1573  segment%T(i,j,k) = tv%T(i+1,j,k)
1574  segment%S(i,j,k) = tv%S(i+1,j,k)
1575  enddo; enddo
1576  elseif (segment%direction == obc_direction_n) then
1577  j=segment%HI%JsdB
1578  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1579  segment%T(i,j,k) = tv%T(i,j,k)
1580  segment%S(i,j,k) = tv%S(i,j,k)
1581  enddo; enddo
1582  elseif (segment%direction == obc_direction_s) then
1583  j=segment%HI%JsdB
1584  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1585  segment%T(i,j,k) = tv%T(i,j+1,k)
1586  segment%S(i,j,k) = tv%S(i,j+1,k)
1587  enddo; enddo
1588  endif
1589  enddo
1590 
1591  do n=1,obc%number_of_segments
1592  segment => obc%segment(n)
1593  if (.not. segment%on_pe) cycle
1594 
1595  if (segment%direction == obc_direction_e) then
1596  i=segment%HI%IsdB
1597  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1598  tv%T(i+1,j,k) = tv%T(i,j,k) ; tv%S(i+1,j,k) = tv%S(i,j,k)
1599  enddo; enddo
1600  elseif (segment%direction == obc_direction_w) then
1601  i=segment%HI%IsdB
1602  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1603  tv%T(i,j,k) = tv%T(i+1,j,k) ; tv%S(i,j,k) = tv%S(i+1,j,k)
1604  enddo; enddo
1605  elseif (segment%direction == obc_direction_n) then
1606  j=segment%HI%JsdB
1607  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1608  tv%T(i,j+1,k) = tv%T(i,j,k) ; tv%S(i,j+1,k) = tv%S(i,j,k)
1609  enddo; enddo
1610  elseif (segment%direction == obc_direction_s) then
1611  j=segment%HI%JsdB
1612  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1613  tv%T(i,j,k) = tv%T(i,j+1,k) ; tv%S(i,j,k) = tv%S(i,j+1,k)
1614  enddo; enddo
1615  endif
1616  enddo
1617  endif
1618 
1619  do n=1,obc%number_of_segments
1620  segment => obc%segment(n)
1621  if (.not. segment%on_pe) cycle
1622 
1623  if (segment%direction == obc_direction_e) then
1624  i=segment%HI%IsdB
1625  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1626  h(i+1,j,k) = h(i,j,k)
1627  enddo; enddo
1628  elseif (segment%direction == obc_direction_w) then
1629  i=segment%HI%IsdB
1630  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1631  h(i,j,k) = h(i+1,j,k)
1632  enddo; enddo
1633  elseif (segment%direction == obc_direction_n) then
1634  j=segment%HI%JsdB
1635  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1636  h(i,j+1,k) = h(i,j,k)
1637  enddo; enddo
1638  elseif (segment%direction == obc_direction_s) then
1639  j=segment%HI%JsdB
1640  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1641  h(i,j,k) = h(i,j+1,k)
1642  enddo; enddo
1643  endif
1644  enddo
1645 
1646 end subroutine set_tracer_data
1647 
1648 function lookup_seg_field(OBC_seg,field)
1649  type(obc_segment_type), pointer :: OBC_seg
1650  character(len=32), intent(in) :: field ! The field name
1651  integer :: lookup_seg_field
1652 
1653  integer :: n,m
1654 
1655  lookup_seg_field=-1
1656  do n=1,obc_seg%num_fields
1657  if (trim(field) == obc_seg%field_names(n)) then
1658  lookup_seg_field=n
1659  return
1660  endif
1661  enddo
1662 
1663  return
1664 
1665 end function lookup_seg_field
1666 
1667 
1668 !> Allocate segment data fields
1669 subroutine allocate_obc_segment_data(OBC, segment)
1670  type(ocean_obc_type), pointer :: OBC !< Open boundary structure
1671  type(obc_segment_type), intent(inout) :: segment !< Open boundary segment
1672  ! Local variables
1673  integer :: isd, ied, jsd, jed
1674  integer :: IsdB, IedB, JsdB, JedB
1675  character(len=40) :: mdl = "allocate_OBC_segment_data" ! This subroutine's name.
1676 
1677  isd = segment%HI%isd ; ied = segment%HI%ied
1678  jsd = segment%HI%jsd ; jed = segment%HI%jed
1679  isdb = segment%HI%IsdB ; iedb = segment%HI%IedB
1680  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
1681 
1682  if (.not. segment%on_pe) return
1683 
1684  if (segment%is_E_or_W) then
1685  ! If these are just Flather, change update_OBC_segment_data accordingly
1686  allocate(segment%Cg(isdb:iedb,jsd:jed)); segment%Cg(:,:)=0.
1687  allocate(segment%Htot(isdb:iedb,jsd:jed)); segment%Htot(:,:)=0.0
1688  allocate(segment%h(isdb:iedb,jsd:jed,obc%ke)); segment%h(:,:,:)=0.0
1689  allocate(segment%eta(isdb:iedb,jsd:jed)); segment%eta(:,:)=0.0
1690  allocate(segment%normal_trans_bt(isdb:iedb,jsd:jed)); segment%normal_trans_bt(:,:)=0.0
1691  allocate(segment%rx_normal(isdb:iedb,jsd:jed,obc%ke)); segment%rx_normal(:,:,:)=0.0
1692  allocate(segment%normal_vel(isdb:iedb,jsd:jed,obc%ke)); segment%normal_vel(:,:,:)=0.0
1693  allocate(segment%normal_vel_bt(isdb:iedb,jsd:jed)); segment%normal_vel_bt(:,:)=0.0
1694  allocate(segment%normal_trans(isdb:iedb,jsd:jed,obc%ke)); segment%normal_trans(:,:,:)=0.0
1695  if (segment%nudged) then
1696  allocate(segment%nudged_normal_vel(isdb:iedb,jsd:jed,obc%ke)); segment%nudged_normal_vel(:,:,:)=0.0
1697  endif
1698  endif
1699 
1700  if (segment%is_N_or_S) then
1701  ! If these are just Flather, change update_OBC_segment_data accordingly
1702  allocate(segment%Cg(isd:ied,jsdb:jedb)); segment%Cg(:,:)=0.
1703  allocate(segment%Htot(isd:ied,jsdb:jedb)); segment%Htot(:,:)=0.0
1704  allocate(segment%h(isd:ied,jsdb:jedb,obc%ke)); segment%h(:,:,:)=0.0
1705  allocate(segment%eta(isd:ied,jsdb:jedb)); segment%eta(:,:)=0.0
1706  allocate(segment%normal_trans_bt(isd:ied,jsdb:jedb)); segment%normal_trans_bt(:,:)=0.0
1707  allocate(segment%rx_normal(isd:ied,jsdb:jedb,obc%ke)); segment%rx_normal(:,:,:)=0.0
1708  allocate(segment%normal_vel(isd:ied,jsdb:jedb,obc%ke)); segment%normal_vel(:,:,:)=0.0
1709  allocate(segment%normal_vel_bt(isd:ied,jsdb:jedb)); segment%normal_vel_bt(:,:)=0.0
1710  allocate(segment%normal_trans(isd:ied,jsdb:jedb,obc%ke)); segment%normal_trans(:,:,:)=0.0
1711  if (segment%nudged) then
1712  allocate(segment%nudged_normal_vel(isd:ied,jsdb:jedb,obc%ke)); segment%nudged_normal_vel(:,:,:)=0.0
1713  endif
1714  endif
1715 
1716 end subroutine allocate_obc_segment_data
1717 
1718 !> Set tangential velocities outside of open boundaries to silly values
1719 !! (used for checking the interior state is independent of values outside
1720 !! of the domain).
1721 subroutine open_boundary_test_extern_uv(G, OBC, u, v)
1722  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
1723  type(ocean_obc_type), pointer :: OBC !< Open boundary structure
1724  real, dimension(SZIB_(G),SZJ_(G), SZK_(G)),intent(inout) :: u !< Zonal velocity (m/s)
1725  real, dimension(SZI_(G),SZJB_(G), SZK_(G)),intent(inout) :: v !< Meridional velocity (m/s)
1726  ! Local variables
1727  integer :: i, j, k, n
1728  real, parameter :: silly_value = 1.e40
1729 
1730  if (.not. associated(obc)) return
1731 
1732  do n = 1, obc%number_of_segments
1733  do k = 1, g%ke
1734  if (obc%segment(n)%is_N_or_S) then
1735  j = obc%segment(n)%HI%JsdB
1736  if (obc%segment(n)%direction == obc_direction_n) then
1737  do i = obc%segment(n)%HI%IsdB, obc%segment(n)%HI%IedB
1738  u(i,j+1,k) = silly_value
1739  enddo
1740  else
1741  do i = obc%segment(n)%HI%IsdB, obc%segment(n)%HI%IedB
1742  u(i,j,k) = silly_value
1743  enddo
1744  endif
1745  elseif (obc%segment(n)%is_E_or_W) then
1746  i = obc%segment(n)%HI%IsdB
1747  if (obc%segment(n)%direction == obc_direction_e) then
1748  do j = obc%segment(n)%HI%JsdB, obc%segment(n)%HI%JedB
1749  v(i+1,j,k) = silly_value
1750  enddo
1751  else
1752  do j = obc%segment(n)%HI%JsdB, obc%segment(n)%HI%JedB
1753  v(i,j,k) = silly_value
1754  enddo
1755  endif
1756  endif
1757  enddo
1758  enddo
1759 
1760 end subroutine open_boundary_test_extern_uv
1761 
1762 !> Set thicknesses outside of open boundaries to silly values
1763 !! (used for checking the interior state is independent of values outside
1764 !! of the domain).
1765 subroutine open_boundary_test_extern_h(G, OBC, h)
1766  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
1767  type(ocean_obc_type), pointer :: OBC !< Open boundary structure
1768  real, dimension(SZI_(G),SZJ_(G), SZK_(G)),intent(inout) :: h !< Layer thickness (m or kg/m2)
1769  ! Local variables
1770  integer :: i, j, k, n
1771  real, parameter :: silly_value = 1.e40
1772 
1773  if (.not. associated(obc)) return
1774 
1775  do n = 1, obc%number_of_segments
1776  do k = 1, g%ke
1777  if (obc%segment(n)%is_N_or_S) then
1778  j = obc%segment(n)%HI%JsdB
1779  if (obc%segment(n)%direction == obc_direction_n) then
1780  do i = obc%segment(n)%HI%isd, obc%segment(n)%HI%ied
1781  h(i,j+1,k) = silly_value
1782  enddo
1783  else
1784  do i = obc%segment(n)%HI%isd, obc%segment(n)%HI%ied
1785  h(i,j,k) = silly_value
1786  enddo
1787  endif
1788  elseif (obc%segment(n)%is_E_or_W) then
1789  i = obc%segment(n)%HI%IsdB
1790  if (obc%segment(n)%direction == obc_direction_e) then
1791  do j = obc%segment(n)%HI%jsd, obc%segment(n)%HI%jed
1792  h(i+1,j,k) = silly_value
1793  enddo
1794  else
1795  do j = obc%segment(n)%HI%jsd, obc%segment(n)%HI%jed
1796  h(i,j,k) = silly_value
1797  enddo
1798  endif
1799  endif
1800  enddo
1801  enddo
1802 
1803 end subroutine open_boundary_test_extern_h
1804 
1805 !> Update the OBC values on the segments.
1806 subroutine update_obc_segment_data(G, GV, OBC, tv, h, Time)
1807  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
1808  type(verticalgrid_type), intent(in) :: GV !< Ocean vertical grid structure
1809  type(ocean_obc_type), pointer :: OBC !< Open boundary structure
1810  type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure
1811  real, dimension(SZI_(G),SZJ_(G), SZK_(G)), intent(inout) :: h !< Thickness
1812 ! real, dimension(SZI_(G),SZJ_(G), SZK_(G)), intent(inout) :: e !< Layer interface height
1813 ! real, dimension(SZI_(G),SZJ_(G)) , intent(inout) :: eta !< Thickness
1814  type(time_type), intent(in) :: Time !< Time
1815  ! Local variables
1816 
1817  integer :: i, j, k, is, ie, js, je, isd, ied, jsd, jed
1818  integer :: IsdB, IedB, JsdB, JedB, n, m, nz
1819  character(len=40) :: mdl = "set_OBC_segment_data" ! This subroutine's name.
1820  character(len=200) :: filename, OBC_file, inputdir ! Strings for file/path
1821  type(obc_segment_type), pointer :: segment
1822  integer, dimension(4) :: siz,siz2
1823  real :: sumh ! column sum of thicknesses (m)
1824  integer :: ni_seg, nj_seg ! number of src gridpoints along the segments
1825  integer :: i2, j2 ! indices for referencing local domain array
1826  integer :: is_obc, ie_obc, js_obc, je_obc ! segment indices within local domain
1827  integer :: ishift, jshift ! offsets for staggered locations
1828  real, dimension(:,:), pointer :: seg_vel => null() ! pointer to segment velocity array
1829  real, dimension(:,:), pointer :: seg_trans => null() ! pointer to segment transport array
1830  real, dimension(:,:,:), allocatable :: tmp_buffer
1831 
1832  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1833  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
1834  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
1835  nz=g%ke
1836 
1837  if (.not. associated(obc)) return
1838 
1839  do n = 1, obc%number_of_segments
1840  segment => obc%segment(n)
1841 
1842  if (.not. segment%on_pe) cycle ! continue to next segment if not in computational domain
1843 
1844  ni_seg = segment%ie_obc-segment%is_obc+1
1845  nj_seg = segment%je_obc-segment%js_obc+1
1846  is_obc = max(segment%is_obc,isd-1)
1847  ie_obc = min(segment%ie_obc,ied)
1848  js_obc = max(segment%js_obc,jsd-1)
1849  je_obc = min(segment%je_obc,jed)
1850 
1851 
1852  if (segment%is_E_or_W) then
1853  nj_seg=nj_seg-1
1854  js_obc=js_obc+1
1855  else
1856  ni_seg=ni_seg-1
1857  is_obc=is_obc+1
1858  endif
1859 
1860 ! Calculate auxiliary fields at staggered locations.
1861 ! Segment indices are on q points:
1862 !
1863 ! |-----------|------------|-----------|-----------| J_obc
1864 ! Is_obc Ie_obc
1865 !
1866 ! i2 has to start at Is_obc+1 and end at Ie_obc.
1867 ! j2 is J_obc and jshift has to be +1 at both the north and south.
1868 
1869  ! calculate auxiliary fields at staggered locations
1870  ishift=0;jshift=0
1871  if (segment%is_E_or_W) then
1872  if (segment%direction == obc_direction_w) ishift=1
1873  i=segment%HI%IscB
1874  do j=segment%HI%jsd,segment%HI%jed
1875  segment%Cg(i,j) = sqrt(gv%g_prime(1)*g%bathyT(i+ishift,j))
1876  ! if (GV%Boussinesq) then
1877  segment%Htot(i,j) = g%bathyT(i+ishift,j)*gv%m_to_H! + eta(i+ishift,j)
1878  ! else
1879  ! segment%Htot(I,j) = eta(i+ishift,j)
1880  ! endif
1881  do k=1,g%ke
1882  segment%h(i,j,k) = h(i+ishift,j,k)
1883  enddo
1884  enddo
1885 
1886 
1887  else! (segment%direction == OBC_DIRECTION_N .or. segment%direction == OBC_DIRECTION_S)
1888  if (segment%direction == obc_direction_s) jshift=1
1889  j=segment%HI%JscB
1890  do i=segment%HI%isd,segment%HI%ied
1891  segment%Cg(i,j) = sqrt(gv%g_prime(1)*g%bathyT(i,j+jshift))
1892 ! if (GV%Boussinesq) then
1893  segment%Htot(i,j) = g%bathyT(i,j+jshift)*gv%m_to_H! + eta(i,j+jshift)
1894 ! else
1895 ! segment%Htot(i,J) = eta(i,j+jshift)
1896 ! endif
1897  do k=1,g%ke
1898  segment%h(i,j,k) = h(i,j+jshift,k)
1899 ! segment%e(i,J,k) = e(i,j+jshift,k)
1900  enddo
1901  enddo
1902  endif
1903 
1904  do m = 1,segment%num_fields
1905  if (segment%field(m)%fid > 0) then
1906  siz(1)=size(segment%field(m)%buffer_src,1)
1907  siz(2)=size(segment%field(m)%buffer_src,2)
1908  siz(3)=size(segment%field(m)%buffer_src,3)
1909  if (.not.associated(segment%field(m)%buffer_dst)) then
1910  if (siz(3) /= segment%field(m)%nk_src) call mom_error(fatal,'nk_src inconsistency')
1911  if (segment%field(m)%nk_src > 1) then
1912  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
1913  else
1914  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,1))
1915  endif
1916  segment%field(m)%buffer_dst(:,:,:)=0.0
1917  if (trim(segment%field(m)%name) == 'U' .or. trim(segment%field(m)%name) == 'V') then
1918  allocate(segment%field(m)%bt_vel(is_obc:ie_obc,js_obc:je_obc))
1919  segment%field(m)%bt_vel(:,:)=0.0
1920  endif
1921  endif
1922  ! read source data interpolated to the current model time
1923  if (siz(1)==1) then
1924  allocate(tmp_buffer(1,nj_seg*2+1,segment%field(m)%nk_src)) ! segment data is currrently on supergrid
1925  else
1926  allocate(tmp_buffer(ni_seg*2+1,1,segment%field(m)%nk_src)) ! segment data is currrently on supergrid
1927  endif
1928 
1929  call time_interp_external(segment%field(m)%fid,time, tmp_buffer)
1930  if (siz(1)==1) then
1931  segment%field(m)%buffer_src(is_obc,:,:)=tmp_buffer(1,2*(js_obc+g%jdg_offset)-1:2*(je_obc+g%jdg_offset)-1:2,:)
1932  else
1933  segment%field(m)%buffer_src(:,js_obc,:)=tmp_buffer(2*(is_obc+g%idg_offset)-1:2*(ie_obc+g%idg_offset)-1:2,1,:)
1934  endif
1935  if (segment%field(m)%nk_src > 1) then
1936  call time_interp_external(segment%field(m)%fid_dz,time, tmp_buffer)
1937  if (siz(1)==1) then
1938  segment%field(m)%dz_src(is_obc,:,:)=tmp_buffer(1,2*(js_obc+g%jdg_offset)-1:2*(je_obc+g%jdg_offset)-1:2,:)
1939  else
1940  segment%field(m)%dz_src(:,js_obc,:)=tmp_buffer(2*(is_obc+g%idg_offset)-1:2*(ie_obc+g%idg_offset)-1:2,1,:)
1941  endif
1942  do j=js_obc,je_obc
1943  do i=is_obc,ie_obc
1944 
1945  ! Using the h remapping approach
1946  ! Pretty sure we need to check for source/target grid consistency here
1947  segment%field(m)%buffer_dst(i,j,:)=0.0 ! initialize remap destination buffer
1948  if (g%mask2dT(i,j)>0.) then
1949  call remapping_core_h(obc%remap_CS, &
1950  segment%field(m)%nk_src,segment%field(m)%dz_src(i,j,:), &
1951  segment%field(m)%buffer_src(i,j,:), &
1952  g%ke, h(i,j,:), segment%field(m)%buffer_dst(i,j,:))
1953  endif
1954  enddo
1955  enddo
1956  else ! 2d data
1957  segment%field(m)%buffer_dst(:,:,1)=segment%field(m)%buffer_src(:,:,1) ! initialize remap destination buffer
1958  endif
1959  deallocate(tmp_buffer)
1960  else ! fid <= 0
1961  if (.not. ASSOCIATED(segment%field(m)%buffer_dst)) then
1962  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
1963  segment%field(m)%buffer_dst(:,:,:)=segment%field(m)%value
1964  if (trim(segment%field(m)%name) == 'U' .or. trim(segment%field(m)%name) == 'V') then
1965  allocate(segment%field(m)%bt_vel(is_obc:ie_obc,js_obc:je_obc))
1966  segment%field(m)%bt_vel(:,:)=segment%field(m)%value
1967  endif
1968  endif
1969  endif
1970 
1971  if (trim(segment%field(m)%name) == 'U' .or. trim(segment%field(m)%name) == 'V') then
1972  if (segment%field(m)%fid>0) then ! calculate external BT velocity and transport if needed
1973  if((trim(segment%field(m)%name) == 'U' .and. segment%is_E_or_W) .or. &
1974  (trim(segment%field(m)%name) == 'V' .and. segment%is_N_or_S)) then
1975  do j=js_obc,je_obc
1976  do i=is_obc,ie_obc
1977  segment%normal_trans_bt(i,j) = 0.0
1978  do k=1,g%ke
1979  segment%normal_vel(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
1980  segment%normal_trans(i,j,k) = segment%field(m)%buffer_dst(i,j,k)*segment%h(i,j,k)
1981  segment%normal_trans_bt(i,j)= segment%normal_trans_bt(i,j)+segment%normal_trans(i,j,k)
1982  enddo
1983  segment%normal_vel_bt(i,j) = segment%normal_trans_bt(i,j)/max(segment%Htot(i,j),1.e-12)
1984  if (associated(segment%nudged_normal_vel)) segment%nudged_normal_vel(i,j,:) = segment%normal_vel(i,j,:)
1985  enddo
1986  enddo
1987  endif
1988  endif
1989  endif
1990 
1991  if (trim(segment%field(m)%name) == 'SSH') then
1992  do j=js_obc,je_obc
1993  do i=is_obc,ie_obc
1994  segment%eta(i,j) = segment%field(m)%buffer_dst(i,j,1)
1995  enddo
1996  enddo
1997  endif
1998  enddo
1999 
2000  enddo ! end segment loop
2001 
2002 end subroutine update_obc_segment_data
2003 
2004 !> register open boundary objects for boundary updates.
2005 subroutine register_obc(name, param_file, Reg)
2006  character(len=32), intent(in) :: name !< OBC name used for error messages
2007  type(param_file_type), intent(in) :: param_file !< file to parse for model parameter values
2008  type(obc_registry_type), pointer :: Reg !< pointer to the tracer registry
2009  integer :: nobc
2010  character(len=256) :: mesg ! Message for error messages.
2011 
2012  if (.not. associated(reg)) call obc_registry_init(param_file, reg)
2013 
2014  if (reg%nobc>=max_fields_) then
2015  write(mesg,'("Increase MAX_FIELDS_ in MOM_memory.h to at least ",I3," to allow for &
2016  &all the open boundaries being registered via register_OBC.")') reg%nobc+1
2017  call mom_error(fatal,"MOM register_tracer: "//mesg)
2018  endif
2019  reg%nobc = reg%nobc + 1
2020  nobc = reg%nobc
2021 
2022  reg%OB(nobc)%name = name
2023 
2024  if (reg%locked) call mom_error(fatal, &
2025  "MOM register_tracer was called for variable "//trim(reg%OB(nobc)%name)//&
2026  " with a locked tracer registry.")
2027 
2028 end subroutine register_obc
2029 
2030 !> This routine include declares and sets the variable "version".
2031 subroutine obc_registry_init(param_file, Reg)
2032  type(param_file_type), intent(in) :: param_file !< open file to parse for model parameters
2033  type(obc_registry_type), pointer :: Reg !< pointer to OBC registry
2034 
2035  integer, save :: init_calls = 0
2036 
2037 #include "version_variable.h"
2038  character(len=40) :: mdl = "MOM_open_boundary" ! This module's name.
2039  character(len=256) :: mesg ! Message for error messages.
2040 
2041  if (.not.associated(reg)) then ; allocate(reg)
2042  else ; return ; endif
2043 
2044  ! Read all relevant parameters and write them to the model log.
2045 ! call log_version(param_file, mdl,s version, "")
2046 
2047  init_calls = init_calls + 1
2048  if (init_calls > 1) then
2049  write(mesg,'("OBC_registry_init called ",I3, &
2050  &" times with different registry pointers.")') init_calls
2051  if (is_root_pe()) call mom_error(warning,"MOM_open_boundary"//mesg)
2052  endif
2053 
2054 end subroutine obc_registry_init
2055 
2056 !> Add file to OBC registry.
2057 function register_file_obc(param_file, CS, OBC_Reg)
2058  type(param_file_type), intent(in) :: param_file !< parameter file.
2059  type(file_obc_cs), pointer :: CS !< file control structure.
2060  type(obc_registry_type), pointer :: OBC_Reg !< OBC registry.
2061  logical :: register_file_OBC
2062  character(len=32) :: casename = "OBC file" !< This case's name.
2063 
2064  if (associated(cs)) then
2065  call mom_error(warning, "register_file_OBC called with an "// &
2066  "associated control structure.")
2067  return
2068  endif
2069  allocate(cs)
2070 
2071  ! Register the file for boundary updates.
2072  call register_obc(casename, param_file, obc_reg)
2073  register_file_obc = .true.
2074 
2075 end function register_file_obc
2076 
2077 !> Clean up the file OBC from registry.
2078 subroutine file_obc_end(CS)
2079  type(file_obc_cs), pointer :: CS !< OBC file control structure.
2080 
2081  if (associated(cs)) then
2082  deallocate(cs)
2083  endif
2084 end subroutine file_obc_end
2085 
2086 !> \namespace mom_open_boundary
2087 !! This module implements some aspects of internal open boundary
2088 !! conditions in MOM.
2089 !!
2090 !! A small fragment of the grid is shown below:
2091 !!
2092 !! j+1 x ^ x ^ x At x: q, CoriolisBu
2093 !! j+1 > o > o > At ^: v, tauy
2094 !! j x ^ x ^ x At >: u, taux
2095 !! j > o > o > At o: h, bathyT, buoy, tr, T, S, Rml, ustar
2096 !! j-1 x ^ x ^ x
2097 !! i-1 i i+1 At x & ^:
2098 !! i i+1 At > & o:
2099 !!
2100 !! The boundaries always run through q grid points (x).
2101 
2102 end module mom_open_boundary
Open boundary segment data from files (mostly).
integer, parameter, public obc_direction_s
Indicates the boundary is an effective southern boundary.
integer, parameter, public obc_direction_w
Indicates the boundary is an effective western boundary.
integer, parameter, public obc_direction_n
Indicates the boundary is an effective northern boundary.
character(len=3), public remappingdefaultscheme
Default remapping method.
Methods for testing for, and list of, obsolete run-time parameters.
subroutine open_boundary_dealloc(OBC)
Deallocate open boundary data.
subroutine initialize_segment_data(G, OBC, PF)
subroutine parse_segment_data_str(segment_str, var, value, filenam, fieldnam, fields, num_fields, debug)
Parse an OBC_SEGMENT_%%_DATA string.
subroutine, public file_obc_end(CS)
Clean up the file OBC from registry.
integer, parameter, public to_all
character(len=120) function, public extract_word(string, separators, n)
Returns the string corresponding to the nth word in the argument or "" if the string is not long enou...
subroutine setup_segment_indices(G, seg, Is_obc, Ie_obc, Js_obc, Je_obc)
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
subroutine, public open_boundary_test_extern_h(G, OBC, h)
Set thicknesses outside of open boundaries to silly values (used for checking the interior state is i...
Provides the ocean grid type.
Definition: MOM_grid.F90:2
Open boundary segment data structure.
Generates vertical grids as part of the ALE algorithm.
character(len=256), public remappingschemesdoc
Documentation for external callers.
Provides column-wise vertical remapping functions.
subroutine, public register_obc(name, param_file, Reg)
register open boundary objects for boundary updates.
subroutine, public open_boundary_config(G, param_file, OBC)
Enables OBC module and reads configuration parameters This routine is called from MOM_initialize_fixe...
This module contains I/O framework code.
Definition: MOM_io.F90:2
subroutine allocate_obc_segment_data(OBC, segment)
Allocate segment data fields.
logical function, public open_boundary_query(OBC, apply_open_OBC, apply_specified_OBC, apply_Flather_OBC, apply_nudged_OBC, needs_ext_seg_data)
integer function lookup_seg_field(OBC_seg, field)
subroutine, public open_boundary_impose_normal_slope(OBC, G, depth)
Sets the slope of bathymetry normal to an open bounndary to zero.
subroutine, public open_boundary_apply_normal_flow(OBC, G, u, v)
Applies OBC values stored in segments to 3d u,v fields.
logical function, public register_file_obc(param_file, CS, OBC_Reg)
Add file to OBC registry.
Container for remapping parameters.
subroutine, public update_obc_segment_data(G, GV, OBC, tv, h, Time)
Update the OBC values on the segments.
integer, parameter, public obc_simple
subroutine, public open_boundary_end(OBC)
Close open boundary data.
This module contains the tracer_registry_type and the subroutines that handle registration of tracers...
integer, parameter, public obc_flather
subroutine, public obc_registry_init(param_file, Reg)
This routine include declares and sets the variable "version".
Regridding control structure.
subroutine setup_v_point_obc(OBC, G, segment_str, l_seg)
Parse an OBC_SEGMENT_%%% string starting with "J=" and configure placement and type of OBC accordingl...
Type to carry basic OBC information needed for updating values.
subroutine, public open_boundary_zero_normal_flow(OBC, G, u, v)
Applies zero values to 3d u,v fields on OBC segments.
subroutine gradient_at_q_points(G, segment, uvel, vvel)
Calculate the tangential gradient of the normal flow at the boundary q-points.
Type to carry something (what] for the OBC registry.
subroutine, public obsolete_int(param_file, varname, warning_val, hint)
Test for presence of obsolete INTEGER in parameter file.
Type to carry basic tracer information.
subroutine, public radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, dt)
Apply radiation conditions to 3D u,v at open boundaries.
subroutine, public open_boundary_init(G, param_file, OBC)
Initialize open boundary control structure.
logical function, public is_root_pe()
subroutine, public add_tracer_obc_values(name, Reg, OBC_inflow, OBC_in_u, OBC_in_v)
This subroutine adds open boundary condition concentrations for a tracer that has previously been reg...
subroutine parse_segment_str(ni_global, nj_global, segment_str, l, m, n, action_str)
Parse an OBC_SEGMENT_%%% string.
subroutine setup_u_point_obc(OBC, G, segment_str, l_seg)
Parse an OBC_SEGMENT_%%% string starting with "I=" and configure placement and type of OBC accordingl...
character(len=120) function, public remove_spaces(string)
Returns string with all spaces removed.
character(len=40) mdl
subroutine, public mom_mesg(message, verb, all_print)
integer, parameter max_obc_fields
Maximum number of data fields needed for OBC segments.
integer, parameter, public obc_direction_e
Indicates the boundary is an effective eastern boundary.
subroutine, public end_remapping(CS)
Destrcutor for remapping control structure.
Control structure for open boundaries that read from files. Probably lots to update here...
subroutine, public remapping_core_h(CS, n0, h0, u0, n1, h1, u1)
Remaps column of values u0 on grid h0 to grid h1 assuming the top edge is aligned.
integer, parameter, public obc_wall
subroutine, public obsolete_char(param_file, varname, hint)
Test for presence of obsolete STRING in parameter file.
The thermo_var_ptrs structure contains pointers to an assortment of thermodynamic fields that may be ...
integer, parameter, public obc_none
Controls where open boundary conditions are applied.
subroutine, public initialize_remapping(CS, remapping_scheme, boundary_extrapolation, check_reconstruction, check_remapping, force_bounds_in_subcell)
Constructor for remapping control structure.
subroutine, public open_boundary_test_extern_uv(G, OBC, u, v)
Set tangential velocities outside of open boundaries to silly values (used for checking the interior ...
subroutine, public set_tracer_data(OBC, tv, h, G, PF, tracer_Reg)
Sets the initial values of the tracer and h open boundary conditions. Also allocates and fills the se...
subroutine, public open_boundary_impose_land_mask(OBC, G, areaCu, areaCv)
Reconcile masks and open boundaries, deallocate OBC on PEs where it is not needed. Also adjust u- and v-point cell area on specified open boundaries.
subroutine, public mom_error(level, message, all_print)
subroutine, public obsolete_logical(param_file, varname, warning_val, hint)
Test for presence of obsolete LOGICAL in parameter file.
integer function interpret_int_expr(string, imax)
subroutine, public obsolete_real(param_file, varname, warning_val, hint)
Test for presence of obsolete REAL in parameter file.
integer, parameter, public obc_radiation