MOM6
MOM_open_boundary.F90
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_coms, only : sum_across_pes
7 use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, clock_routine
8 use mom_diag_mediator, only : diag_ctrl, time_type
10 use mom_domains, only : to_all, scalar_pair, cgrid_ne
11 use mom_error_handler, only : mom_mesg, mom_error, fatal, warning, is_root_pe
13 use mom_grid, only : ocean_grid_type, hor_index_type
15 use mom_io, only : east_face, north_face
16 use mom_io, only : slasher, read_data, field_size, single_file
17 use mom_io, only : vardesc, query_vardesc, var_desc
19 use mom_obsolete_params, only : obsolete_logical, obsolete_int, obsolete_real, obsolete_char
20 use mom_string_functions, only : extract_word, remove_spaces
21 use mom_tracer_registry, only : tracer_type, tracer_registry_type, tracer_name_lookup
22 use time_interp_external_mod, only : init_external_field, time_interp_external
23 use time_interp_external_mod, only : time_interp_external_init
24 use mom_remapping, only : remappingschemesdoc, remappingdefaultscheme, remapping_cs
25 use mom_remapping, only : initialize_remapping, remapping_core_h, end_remapping
26 use mom_regridding, only : regridding_cs
30 
31 implicit none ; private
32 
33 #include <MOM_memory.h>
34 
35 public open_boundary_apply_normal_flow
36 public open_boundary_config
37 public open_boundary_init
38 public open_boundary_query
39 public open_boundary_end
40 public open_boundary_impose_normal_slope
41 public open_boundary_impose_land_mask
42 public radiation_open_bdry_conds
43 public set_tracer_data
44 public update_obc_segment_data
45 public open_boundary_test_extern_uv
46 public open_boundary_test_extern_h
47 public open_boundary_zero_normal_flow
48 public register_obc, obc_registry_init
49 public register_file_obc, file_obc_end
50 public segment_tracer_registry_init
51 public segment_tracer_registry_end
52 public register_segment_tracer
53 public register_temp_salt_segments
54 public fill_temp_salt_segments
55 public open_boundary_register_restarts
56 
57 integer, parameter, public :: obc_none = 0 !< Indicates the use of no open boundary
58 integer, parameter, public :: obc_simple = 1 !< Indicates the use of a simple inflow open boundary
59 integer, parameter, public :: obc_wall = 2 !< Indicates the use of a closed sall
60 integer, parameter, public :: obc_flather = 3 !< Indicates the use of a Flather open boundary
61 integer, parameter, public :: obc_radiation = 4 !< Indicates the use of a radiation open boundary
62 integer, parameter, public :: obc_direction_n = 100 !< Indicates the boundary is an effective northern boundary
63 integer, parameter, public :: obc_direction_s = 200 !< Indicates the boundary is an effective southern boundary
64 integer, parameter, public :: obc_direction_e = 300 !< Indicates the boundary is an effective eastern boundary
65 integer, parameter, public :: obc_direction_w = 400 !< Indicates the boundary is an effective western boundary
66 integer, parameter :: max_obc_fields = 100 !< Maximum number of data fields needed for OBC segments
67 
68 !> Open boundary segment data from files (mostly).
69 type, public :: obc_segment_data_type
70  integer :: fid !< handle from FMS associated with segment data on disk
71  integer :: fid_dz !< handle from FMS associated with segment thicknesses on disk
72  character(len=8) :: name !< a name identifier for the segment data
73  real, pointer, dimension(:,:,:) :: buffer_src=>null() !< buffer for segment data located at cell faces
74  !! and on the original vertical grid
75  integer :: nk_src !< Number of vertical levels in the source data
76  real, dimension(:,:,:), pointer :: dz_src=>null() !< vertical grid cell spacing of the incoming segment data [m]
77  real, dimension(:,:,:), pointer :: buffer_dst=>null() !< buffer src data remapped to the target vertical grid
78  real, dimension(:,:), pointer :: bt_vel=>null() !< barotropic velocity [m s-1]
79  real :: value !< constant value if fid is equal to -1
80 end type obc_segment_data_type
81 
82 !> Tracer segment data structure, for putting into an array of objects, not all the same shape.
83 !type, public :: segment_tracer_type
84 ! real, dimension(:,:,:), pointer :: tr => NULL() !< tracer concentration array
85 !end type segment_tracer_type
86 
87 !> Tracer on OBC segment data structure, for putting into a segment tracer registry.
88 type, public :: obc_segment_tracer_type
89  real, dimension(:,:,:), pointer :: t => null() !< tracer concentration array
90  real :: obc_inflow_conc = 0.0 !< tracer concentration for generic inflows
91  character(len=32) :: name !< tracer name used for error messages
92  type(tracer_type), pointer :: tr => null() !< metadata describing the tracer
93  real, dimension(:,:,:), pointer :: tres => null() !< tracer reservoir array
94  logical :: is_initialized !< reservoir values have been set when True
96 
97 !> Registry type for tracers on segments
99  integer :: ntseg = 0 !< number of registered tracer segments
100  type(obc_segment_tracer_type) :: tr(max_fields_) !< array of registered tracers
101  logical :: locked = .false. !< New tracers may be registered if locked=.false.
102  !! When locked=.true.,no more tracers can be registered.
103  !! Not sure who should lock it or when...
105 
106 !> Open boundary segment data structure.
107 type, public :: obc_segment_type
108  logical :: flather !< If true, applies Flather + Chapman radiation of barotropic gravity waves.
109  logical :: radiation !< If true, 1D Orlanksi radiation boundary conditions are applied.
110  !! If False, a gradient condition is applied.
111  logical :: radiation_tan !< If true, 1D Orlanksi radiation boundary conditions are applied to
112  !! tangential flows.
113  logical :: radiation_grad !< If true, 1D Orlanksi radiation boundary conditions are applied to
114  !! dudv and dvdx.
115  logical :: oblique !< Oblique waves supported at radiation boundary.
116  logical :: oblique_tan !< If true, 2D radiation boundary conditions are applied to
117  !! tangential flows.
118  logical :: oblique_grad !< If true, 2D radiation boundary conditions are applied to
119  !! dudv and dvdx.
120  logical :: nudged !< Optional supplement to radiation boundary.
121  logical :: nudged_tan !< Optional supplement to nudge tangential velocity.
122  logical :: nudged_grad !< Optional supplement to nudge normal gradient of tangential velocity.
123  logical :: specified !< Boundary normal velocity fixed to external value.
124  logical :: specified_tan !< Boundary tangential velocity fixed to external value.
125  logical :: open !< Boundary is open for continuity solver.
126  logical :: gradient !< Zero gradient at boundary.
127  logical :: values_needed !< Whether or not external OBC fields are needed.
128  integer :: direction !< Boundary faces one of the four directions.
129  logical :: is_n_or_s !< True is the OB is facing North or South and exists on this PE.
130  logical :: is_e_or_w !< True is the OB is facing East or West and exists on this PE.
131  type(obc_segment_data_type), pointer, dimension(:) :: field=>null() !< OBC data
132  integer :: num_fields !< number of OBC data fields (e.g. u_normal,u_parallel and eta for Flather)
133  character(len=32), pointer, dimension(:) :: field_names=>null() !< field names for this segment
134  integer :: is_obc !< i-indices of boundary segment.
135  integer :: ie_obc !< i-indices of boundary segment.
136  integer :: js_obc !< j-indices of boundary segment.
137  integer :: je_obc !< j-indices of boundary segment.
138  real :: velocity_nudging_timescale_in !< Nudging timescale on inflow [s].
139  real :: velocity_nudging_timescale_out !< Nudging timescale on outflow [s].
140  logical :: on_pe !< true if segment is located in the computational domain
141  logical :: temp_segment_data_exists !< true if temperature data arrays are present
142  logical :: salt_segment_data_exists !< true if salinity data arrays are present
143  real, pointer, dimension(:,:) :: cg=>null() !< The external gravity wave speed [m s-1]
144  !! at OBC-points.
145  real, pointer, dimension(:,:) :: htot=>null() !< The total column thickness [m] at OBC-points.
146  real, pointer, dimension(:,:,:) :: h=>null() !< The cell thickness [m] at OBC-points.
147  real, pointer, dimension(:,:,:) :: normal_vel=>null() !< The layer velocity normal to the OB
148  !! segment [m s-1].
149  real, pointer, dimension(:,:,:) :: tangential_vel=>null() !< The layer velocity tangential to the
150  !! OB segment [m s-1].
151  real, pointer, dimension(:,:,:) :: tangential_grad=>null() !< The gradient of the velocity tangential
152  !! to the OB segment [m s-1].
153  real, pointer, dimension(:,:,:) :: normal_trans=>null() !< The layer transport normal to the OB
154  !! segment [m3 s-1].
155  real, pointer, dimension(:,:) :: normal_vel_bt=>null() !< The barotropic velocity normal to
156  !! the OB segment [m s-1].
157  real, pointer, dimension(:,:) :: eta=>null() !< The sea-surface elevation along the segment [m].
158  real, pointer, dimension(:,:,:) :: grad_normal=>null() !< The gradient of the normal flow along the
159  !! segment [s-1]
160  real, pointer, dimension(:,:,:) :: grad_tan=>null() !< The gradient of the tangential flow along the
161  !! segment [s-1]
162  real, pointer, dimension(:,:,:) :: grad_gradient=>null() !< The gradient of the gradient of tangential flow along the
163  !! segment [m-1 s-1]
164  real, pointer, dimension(:,:,:) :: rx_normal=>null() !< The rx_old_u value for radiation coeff
165  !! for normal velocity
166  real, pointer, dimension(:,:,:) :: ry_normal=>null() !< The tangential value for radiation coeff
167  !! for normal velocity
168  real, pointer, dimension(:,:,:) :: cff_normal=>null() !< The denominator for oblique radiation
169  !! for normal velocity
170  real, pointer, dimension(:,:,:) :: nudged_normal_vel=>null() !< The layer velocity normal to the OB segment
171  !! that values should be nudged towards [m s-1].
172  real, pointer, dimension(:,:,:) :: nudged_tangential_vel=>null() !< The layer velocity tangential to the OB segment
173  !! that values should be nudged towards [m s-1].
174  real, pointer, dimension(:,:,:) :: nudged_tangential_grad=>null() !< The layer dvdx or dudy towards which nudging
175  !! can occur [s-1].
176  type(segment_tracer_registry_type), pointer :: tr_reg=> null()!< A pointer to the tracer registry for the segment.
177  type(hor_index_type) :: hi !< Horizontal index ranges
178  real :: tr_invlscale3_out !< An effective inverse length scale cubed [m-3]
179  real :: tr_invlscale3_in !< for restoring the tracer concentration in a
180  !! ficticious reservior towards interior values
181  !! when flow is exiting the domain, or towards
182  !! an externally imposed value when flow is entering
183 end type obc_segment_type
184 
185 !> Open-boundary data
186 type, public :: ocean_obc_type
187  integer :: number_of_segments = 0 !< The number of open-boundary segments.
188  integer :: ke = 0 !< The number of model layers
189  logical :: open_u_bcs_exist_globally = .false. !< True if any zonal velocity points
190  !! in the global domain use open BCs.
191  logical :: open_v_bcs_exist_globally = .false. !< True if any meridional velocity points
192  !! in the global domain use open BCs.
193  logical :: flather_u_bcs_exist_globally = .false. !< True if any zonal velocity points
194  !! in the global domain use Flather BCs.
195  logical :: flather_v_bcs_exist_globally = .false. !< True if any meridional velocity points
196  !! in the global domain use Flather BCs.
197  logical :: oblique_bcs_exist_globally = .false. !< True if any velocity points
198  !! in the global domain use oblique BCs.
199  logical :: nudged_u_bcs_exist_globally = .false. !< True if any velocity points in the
200  !! global domain use nudged BCs.
201  logical :: nudged_v_bcs_exist_globally = .false. !< True if any velocity points in the
202  !! global domain use nudged BCs.
203  logical :: specified_u_bcs_exist_globally = .false. !< True if any zonal velocity points
204  !! in the global domain use specified BCs.
205  logical :: specified_v_bcs_exist_globally = .false. !< True if any meridional velocity points
206  !! in the global domain use specified BCs.
207  logical :: radiation_bcs_exist_globally = .false. !< True if radiations BCs are in use anywhere.
208  logical :: user_bcs_set_globally = .false. !< True if any OBC_USER_CONFIG is set
209  !! for input from user directory.
210  logical :: update_obc = .false. !< Is OBC data time-dependent
211  logical :: needs_io_for_data = .false. !< Is any i/o needed for OBCs
212  logical :: zero_vorticity = .false. !< If True, sets relative vorticity to zero on open boundaries.
213  logical :: freeslip_vorticity = .false. !< If True, sets normal gradient of tangential velocity to zero
214  !! in the relative vorticity on open boundaries.
215  logical :: computed_vorticity = .false. !< If True, uses external data for tangential velocity
216  !! in the relative vorticity on open boundaries.
217  logical :: specified_vorticity = .false. !< If True, uses external data for tangential velocity
218  !! gradients in the relative vorticity on open boundaries.
219  logical :: zero_strain = .false. !< If True, sets strain to zero on open boundaries.
220  logical :: freeslip_strain = .false. !< If True, sets normal gradient of tangential velocity to zero
221  !! in the strain on open boundaries.
222  logical :: computed_strain = .false. !< If True, uses external data for tangential velocity to compute
223  !! normal gradient in the strain on open boundaries.
224  logical :: specified_strain = .false. !< If True, uses external data for tangential velocity gradients
225  !! to compute strain on open boundaries.
226  logical :: zero_biharmonic = .false. !< If True, zeros the Laplacian of flow on open boundaries for
227  !! use in the biharmonic viscosity term.
228  logical :: brushcutter_mode = .false. !< If True, read data on supergrid.
229  real :: g_earth !< The gravitational acceleration [m s-2].
230  ! Properties of the segments used.
231  type(obc_segment_type), pointer, dimension(:) :: &
232  segment => null() !< List of segment objects.
233  ! Which segment object describes the current point.
234  integer, pointer, dimension(:,:) :: &
235  segnum_u => null(), & !< Segment number of u-points.
236  segnum_v => null() !< Segment number of v-points.
237 
238  ! The following parameters are used in the baroclinic radiation code:
239  real :: gamma_uv !< The relative weighting for the baroclinic radiation
240  !! velocities (or speed of characteristics) at the
241  !! new time level (1) or the running mean (0) for velocities.
242  !! Valid values range from 0 to 1, with a default of 0.3.
243  real :: rx_max !< The maximum magnitude of the baroclinic radiation
244  !! velocity (or speed of characteristics) [m s-1]. The
245  !! default value is 10 m s-1.
246  logical :: obc_pe !< Is there an open boundary on this tile?
247  type(remapping_cs), pointer :: remap_cs !< ALE remapping control structure for segments only
248  type(obc_registry_type), pointer :: obc_reg => null() !< Registry type for boundaries
249  real, pointer, dimension(:,:,:) :: rx_normal => null() !< Array storage for restarts
250  real, pointer, dimension(:,:,:) :: ry_normal => null() !< Array storage for restarts
251  real, pointer, dimension(:,:,:) :: cff_normal => null() !< Array storage for restarts
252  real :: silly_h !< A silly value of thickness outside of the domain that
253  !! can be used to test the independence of the OBCs to
254  !! this external data [H ~> m or kg m-2].
255  real :: silly_u !< A silly value of velocity outside of the domain that
256  !! can be used to test the independence of the OBCs to
257  !! this external data [m s-1].
258 end type ocean_obc_type
259 
260 !> Control structure for open boundaries that read from files.
261 !! Probably lots to update here.
262 type, public :: file_obc_cs ; private
263  real :: tide_flow = 3.0e6 !< Placeholder for now...
264 end type file_obc_cs
265 
266 !> Type to carry something (what??) for the OBC registry.
267 type, public :: obc_struct_type
268  character(len=32) :: name !< OBC name used for error messages
269 end type obc_struct_type
270 
271 !> Type to carry basic OBC information needed for updating values.
272 type, public :: obc_registry_type
273  integer :: nobc = 0 !< number of registered open boundary types.
274  type(obc_struct_type) :: ob(max_fields_) !< array of registered boundary types.
275  logical :: locked = .false. !< New OBC types may be registered if locked=.false.
276  !! When locked=.true.,no more boundaries can be registered.
277 end type obc_registry_type
278 
279 integer :: id_clock_pass !< A CPU time clock
280 
281 character(len=40) :: mdl = "MOM_open_boundary" !< This module's name.
282 ! This include declares and sets the variable "version".
283 #include "version_variable.h"
284 
285 contains
286 
287 !> Enables OBC module and reads configuration parameters
288 !> This routine is called from MOM_initialize_fixed which
289 !> occurs before the initialization of the vertical coordinate
290 !> and ALE_init. Therefore segment data are not fully initialized
291 !> here. The remainder of the segment data are initialized in a
292 !> later call to update_open_boundary_data
293 
294 subroutine open_boundary_config(G, US, param_file, OBC)
295  type(dyn_horgrid_type), intent(inout) :: g !< Ocean grid structure
296  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
297  type(param_file_type), intent(in) :: param_file !< Parameter file handle
298  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
299  ! Local variables
300  integer :: l ! For looping over segments
301  logical :: debug_obc, debug, mask_outside, reentrant_x, reentrant_y
302  character(len=15) :: segment_param_str ! The run-time parameter name for each segment
303  character(len=100) :: segment_str ! The contents (rhs) for parameter "segment_param_str"
304  character(len=200) :: config1 ! String for OBC_USER_CONFIG
305  real :: lscale_in, lscale_out ! parameters controlling tracer values at the boundaries
306  allocate(obc)
307 
308  call log_version(param_file, mdl, version, &
309  "Controls where open boundaries are located, what kind of boundary condition "//&
310  "to impose, and what data to apply, if any.")
311  call get_param(param_file, mdl, "OBC_NUMBER_OF_SEGMENTS", obc%number_of_segments, &
312  "The number of open boundary segments.", &
313  default=0)
314  call get_param(param_file, mdl, "G_EARTH", obc%g_Earth, &
315  "The gravitational acceleration of the Earth.", &
316  units="m s-2", default = 9.80)
317  call get_param(param_file, mdl, "OBC_USER_CONFIG", config1, &
318  "A string that sets how the open boundary conditions are "//&
319  " configured: \n", default="none", do_not_log=.true.)
320  call get_param(param_file, mdl, "NK", obc%ke, &
321  "The number of model layers", default=0, do_not_log=.true.)
322 
323  if (config1 /= "none") obc%user_BCs_set_globally = .true.
324 
325  if (obc%number_of_segments > 0) then
326  call get_param(param_file, mdl, "OBC_ZERO_VORTICITY", obc%zero_vorticity, &
327  "If true, sets relative vorticity to zero on open boundaries.", &
328  default=.false.)
329  call get_param(param_file, mdl, "OBC_FREESLIP_VORTICITY", obc%freeslip_vorticity, &
330  "If true, sets the normal gradient of tangential velocity to "//&
331  "zero in the relative vorticity on open boundaries. This cannot "//&
332  "be true if another OBC_XXX_VORTICITY option is True.", default=.true.)
333  call get_param(param_file, mdl, "OBC_COMPUTED_VORTICITY", obc%computed_vorticity, &
334  "If true, uses the external values of tangential velocity "//&
335  "in the relative vorticity on open boundaries. This cannot "//&
336  "be true if another OBC_XXX_VORTICITY option is True.", default=.false.)
337  call get_param(param_file, mdl, "OBC_SPECIFIED_VORTICITY", obc%specified_vorticity, &
338  "If true, uses the external values of tangential velocity "//&
339  "in the relative vorticity on open boundaries. This cannot "//&
340  "be true if another OBC_XXX_VORTICITY option is True.", default=.false.)
341  if ((obc%zero_vorticity .and. obc%freeslip_vorticity) .or. &
342  (obc%zero_vorticity .and. obc%computed_vorticity) .or. &
343  (obc%zero_vorticity .and. obc%specified_vorticity) .or. &
344  (obc%freeslip_vorticity .and. obc%computed_vorticity) .or. &
345  (obc%freeslip_vorticity .and. obc%specified_vorticity) .or. &
346  (obc%computed_vorticity .and. obc%specified_vorticity)) &
347  call mom_error(fatal, "MOM_open_boundary.F90, open_boundary_config:\n"//&
348  "Only one of OBC_ZERO_VORTICITY, OBC_FREESLIP_VORTICITY, OBC_COMPUTED_VORTICITY\n"//&
349  "and OBC_IMPORTED_VORTICITY can be True at once.")
350  call get_param(param_file, mdl, "OBC_ZERO_STRAIN", obc%zero_strain, &
351  "If true, sets the strain used in the stress tensor to zero on open boundaries.", &
352  default=.false.)
353  call get_param(param_file, mdl, "OBC_FREESLIP_STRAIN", obc%freeslip_strain, &
354  "If true, sets the normal gradient of tangential velocity to "//&
355  "zero in the strain use in the stress tensor on open boundaries. This cannot "//&
356  "be true if another OBC_XXX_STRAIN option is True.", default=.true.)
357  call get_param(param_file, mdl, "OBC_COMPUTED_STRAIN", obc%computed_strain, &
358  "If true, sets the normal gradient of tangential velocity to "//&
359  "zero in the strain use in the stress tensor on open boundaries. This cannot "//&
360  "be true if another OBC_XXX_STRAIN option is True.", default=.false.)
361  call get_param(param_file, mdl, "OBC_SPECIFIED_STRAIN", obc%specified_strain, &
362  "If true, sets the normal gradient of tangential velocity to "//&
363  "zero in the strain use in the stress tensor on open boundaries. This cannot "//&
364  "be true if another OBC_XXX_STRAIN option is True.", default=.false.)
365  if ((obc%zero_strain .and. obc%freeslip_strain) .or. &
366  (obc%zero_strain .and. obc%computed_strain) .or. &
367  (obc%zero_strain .and. obc%specified_strain) .or. &
368  (obc%freeslip_strain .and. obc%computed_strain) .or. &
369  (obc%freeslip_strain .and. obc%specified_strain) .or. &
370  (obc%computed_strain .and. obc%specified_strain)) &
371  call mom_error(fatal, "MOM_open_boundary.F90, open_boundary_config: \n"//&
372  "Only one of OBC_ZERO_STRAIN, OBC_FREESLIP_STRAIN, OBC_COMPUTED_STRAIN \n"//&
373  "and OBC_IMPORTED_STRAIN can be True at once.")
374  call get_param(param_file, mdl, "OBC_ZERO_BIHARMONIC", obc%zero_biharmonic, &
375  "If true, zeros the Laplacian of flow on open boundaries in the biharmonic "//&
376  "viscosity term.", default=.false.)
377  call get_param(param_file, mdl, "MASK_OUTSIDE_OBCS", mask_outside, &
378  "If true, set the areas outside open boundaries to be land.", &
379  default=.false.)
380 
381  call get_param(param_file, mdl, "DEBUG", debug, default=.false.)
382  call get_param(param_file, mdl, "DEBUG_OBC", debug_obc, default=.false.)
383  if (debug_obc .or. debug) &
384  call log_param(param_file, mdl, "DEBUG_OBC", debug_obc, &
385  "If true, do additional calls to help debug the performance "//&
386  "of the open boundary condition code.", default=.false., &
387  debuggingparam=.true.)
388 
389  call get_param(param_file, mdl, "OBC_SILLY_THICK", obc%silly_h, &
390  "A silly value of thicknesses used outside of open boundary "//&
391  "conditions for debugging.", units="m", default=0.0, &
392  do_not_log=.not.debug_obc, debuggingparam=.true.)
393  call get_param(param_file, mdl, "OBC_SILLY_VEL", obc%silly_u, &
394  "A silly value of velocities used outside of open boundary "//&
395  "conditions for debugging.", units="m/s", default=0.0, &
396  do_not_log=.not.debug_obc, debuggingparam=.true.)
397  reentrant_x = .false.
398  call get_param(param_file, mdl, "REENTRANT_X", reentrant_x, default=.true.)
399  reentrant_y = .false.
400  call get_param(param_file, mdl, "REENTRANT_Y", reentrant_y, default=.false.)
401 
402  ! Allocate everything
403  ! Note the 0-segment is needed when %segnum_u/v(:,:) = 0
404  allocate(obc%segment(0:obc%number_of_segments))
405  do l=0,obc%number_of_segments
406  obc%segment(l)%Flather = .false.
407  obc%segment(l)%radiation = .false.
408  obc%segment(l)%radiation_tan = .false.
409  obc%segment(l)%radiation_grad = .false.
410  obc%segment(l)%oblique = .false.
411  obc%segment(l)%oblique_tan = .false.
412  obc%segment(l)%oblique_grad = .false.
413  obc%segment(l)%nudged = .false.
414  obc%segment(l)%nudged_tan = .false.
415  obc%segment(l)%nudged_grad = .false.
416  obc%segment(l)%specified = .false.
417  obc%segment(l)%specified_tan = .false.
418  obc%segment(l)%open = .false.
419  obc%segment(l)%gradient = .false.
420  obc%segment(l)%values_needed = .false.
421  obc%segment(l)%direction = obc_none
422  obc%segment(l)%is_N_or_S = .false.
423  obc%segment(l)%is_E_or_W = .false.
424  obc%segment(l)%Velocity_nudging_timescale_in = 0.0
425  obc%segment(l)%Velocity_nudging_timescale_out = 0.0
426  obc%segment(l)%num_fields = 0.0
427  enddo
428  allocate(obc%segnum_u(g%IsdB:g%IedB,g%jsd:g%jed)) ; obc%segnum_u(:,:) = obc_none
429  allocate(obc%segnum_v(g%isd:g%ied,g%JsdB:g%JedB)) ; obc%segnum_v(:,:) = obc_none
430 
431  do l = 1, obc%number_of_segments
432  write(segment_param_str(1:15),"('OBC_SEGMENT_',i3.3)") l
433  call get_param(param_file, mdl, segment_param_str, segment_str, &
434  "Documentation needs to be dynamic?????", &
435  fail_if_missing=.true.)
436  segment_str = remove_spaces(segment_str)
437  if (segment_str(1:2) == 'I=') then
438  call setup_u_point_obc(obc, g, segment_str, l, param_file, reentrant_y)
439  elseif (segment_str(1:2) == 'J=') then
440  call setup_v_point_obc(obc, g, segment_str, l, param_file, reentrant_x)
441  else
442  call mom_error(fatal, "MOM_open_boundary.F90, open_boundary_config: "//&
443  "Unable to interpret "//segment_param_str//" = "//trim(segment_str))
444  endif
445  enddo
446 
447  ! if (open_boundary_query(OBC, needs_ext_seg_data=.true.)) &
448  call initialize_segment_data(g, obc, param_file)
449 
450  if (open_boundary_query(obc, apply_open_obc=.true.)) then
451  call get_param(param_file, mdl, "OBC_RADIATION_MAX", obc%rx_max, &
452  "The maximum magnitude of the baroclinic radiation "//&
453  "velocity (or speed of characteristics). This is only "//&
454  "used if one of the open boundary segments is using Orlanski.", &
455  units="m s-1", default=10.0)
456  call get_param(param_file, mdl, "OBC_RAD_VEL_WT", obc%gamma_uv, &
457  "The relative weighting for the baroclinic radiation "//&
458  "velocities (or speed of characteristics) at the new "//&
459  "time level (1) or the running mean (0) for velocities. "//&
460  "Valid values range from 0 to 1. This is only used if "//&
461  "one of the open boundary segments is using Orlanski.", &
462  units="nondim", default=0.3)
463  endif
464 
465  lscale_in = 0.
466  lscale_out = 0.
467  if (open_boundary_query(obc, apply_open_obc=.true.)) then
468  call get_param(param_file, mdl, "OBC_TRACER_RESERVOIR_LENGTH_SCALE_OUT ", lscale_out, &
469  "An effective length scale for restoring the tracer concentration "//&
470  "at the boundaries to externally imposed values when the flow "//&
471  "is exiting the domain.", units="m", default=0.0)
472 
473  call get_param(param_file, mdl, "OBC_TRACER_RESERVOIR_LENGTH_SCALE_IN ", lscale_in, &
474  "An effective length scale for restoring the tracer concentration "//&
475  "at the boundaries to values from the interior when the flow "//&
476  "is entering the domain.", units="m", default=0.0)
477  endif
478 
479  if (mask_outside) call mask_outside_obcs(g, us, param_file, obc)
480 
481  ! All tracers are using the same restoring length scale for now, but we may want to make this
482  ! tracer-specific in the future for example, in cases where certain tracers are poorly constrained
483  ! by data while others are well constrained - MJH.
484  do l = 1, obc%number_of_segments
485  obc%segment(l)%Tr_InvLscale3_in=0.0
486  if (lscale_in>0.) obc%segment(l)%Tr_InvLscale3_in = 1.0/(lscale_in*lscale_in*lscale_in)
487  obc%segment(l)%Tr_InvLscale3_out=0.0
488  if (lscale_out>0.) obc%segment(l)%Tr_InvLscale3_out = 1.0/(lscale_out*lscale_out*lscale_out)
489  enddo
490 
491  endif ! OBC%number_of_segments > 0
492 
493  ! Safety check
494  if ((obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally) .and. &
495  .not.g%symmetric ) call mom_error(fatal, &
496  "MOM_open_boundary, open_boundary_config: "//&
497  "Symmetric memory must be used when using Flather OBCs.")
498 
499  if (.not.(obc%specified_u_BCs_exist_globally .or. obc%specified_v_BCs_exist_globally .or. &
500  obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)) then
501  ! No open boundaries have been requested
502  call open_boundary_dealloc(obc)
503  else
504  ! Need this for ocean_only mode boundary interpolation.
505  call time_interp_external_init()
506  endif
507 
508 end subroutine open_boundary_config
509 
510 !> Allocate space for reading OBC data from files. It sets up the required vertical
511 !! remapping. In the process, it does funky stuff with the MPI processes.
512 subroutine initialize_segment_data(G, OBC, PF)
513  use mpp_mod, only : mpp_pe, mpp_set_current_pelist, mpp_get_current_pelist,mpp_npes
514 
515  type(dyn_horgrid_type), intent(in) :: G !< Ocean grid structure
516  type(ocean_obc_type), intent(inout) :: OBC !< Open boundary control structure
517  type(param_file_type), intent(in) :: PF !< Parameter file handle
518 
519  integer :: n,m,num_fields
520  character(len=256) :: segstr, filename
521  character(len=20) :: segnam, suffix
522  character(len=32) :: varnam, fieldname
523  real :: value
524  integer :: orient
525  character(len=32), dimension(MAX_OBC_FIELDS) :: fields ! segment field names
526  character(len=128) :: inputdir
527  type(obc_segment_type), pointer :: segment => null() ! pointer to segment type list
528  character(len=32) :: remappingScheme
529  logical :: check_reconstruction, check_remapping, force_bounds_in_subcell
530  integer, dimension(4) :: siz,siz2
531  integer :: is, ie, js, je
532  integer :: isd, ied, jsd, jed
533  integer :: IsdB, IedB, JsdB, JedB
534  integer, dimension(:), allocatable :: saved_pelist
535  integer :: current_pe
536  integer, dimension(1) :: single_pelist
537  !will be able to dynamically switch between sub-sampling refined grid data or model grid
538 
539  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
540 
541  ! There is a problem with the order of the OBC initialization
542  ! with respect to ALE_init. Currently handling this by copying the
543  ! param file so that I can use it later in step_MOM in order to finish
544  ! initializing segments on the first step.
545 
546  call get_param(pf, mdl, "INPUTDIR", inputdir, default=".")
547  inputdir = slasher(inputdir)
548 
549  call get_param(pf, mdl, "REMAPPING_SCHEME", remappingscheme, &
550  "This sets the reconstruction scheme used "//&
551  "for vertical remapping for all variables. "//&
552  "It can be one of the following schemes: \n"//&
553  trim(remappingschemesdoc), default=remappingdefaultscheme,do_not_log=.true.)
554  call get_param(pf, mdl, "FATAL_CHECK_RECONSTRUCTIONS", check_reconstruction, &
555  "If true, cell-by-cell reconstructions are checked for "//&
556  "consistency and if non-monotonicity or an inconsistency is "//&
557  "detected then a FATAL error is issued.", default=.false.,do_not_log=.true.)
558  call get_param(pf, mdl, "FATAL_CHECK_REMAPPING", check_remapping, &
559  "If true, the results of remapping are checked for "//&
560  "conservation and new extrema and if an inconsistency is "//&
561  "detected then a FATAL error is issued.", default=.false.,do_not_log=.true.)
562  call get_param(pf, mdl, "REMAP_BOUND_INTERMEDIATE_VALUES", force_bounds_in_subcell, &
563  "If true, the values on the intermediate grid used for remapping "//&
564  "are forced to be bounded, which might not be the case due to "//&
565  "round off.", default=.false.,do_not_log=.true.)
566  call get_param(pf, mdl, "BRUSHCUTTER_MODE", obc%brushcutter_mode, &
567  "If true, read external OBC data on the supergrid.", &
568  default=.false.)
569 
570  allocate(obc%remap_CS)
571  call initialize_remapping(obc%remap_CS, remappingscheme, boundary_extrapolation = .false., &
572  check_reconstruction=check_reconstruction, &
573  check_remapping=check_remapping, force_bounds_in_subcell=force_bounds_in_subcell)
574 
575  if (obc%user_BCs_set_globally) return
576 
577  ! Try this here just for the documentation. It is repeated below.
578  do n=1, obc%number_of_segments
579  segment => obc%segment(n)
580  write(segnam,"('OBC_SEGMENT_',i3.3,'_DATA')") n
581  call get_param(pf, mdl, segnam, segstr, 'OBC segment docs')
582  enddo
583 
584  !< temporarily disable communication in order to read segment data independently
585 
586  allocate(saved_pelist(0:mpp_npes()-1))
587  call mpp_get_current_pelist(saved_pelist)
588  current_pe = mpp_pe()
589  single_pelist(1) = current_pe
590  call mpp_set_current_pelist(single_pelist)
591 
592  do n=1, obc%number_of_segments
593  segment => obc%segment(n)
594 
595  write(segnam,"('OBC_SEGMENT_',i3.3,'_DATA')") n
596  write(suffix,"('_segment_',i3.3)") n
597  ! needs documentation !! Yet, unsafe for now, causes grief for
598  ! MOM_parameter_docs in circle_obcs on two processes.
599 ! call get_param(PF, mdl, segnam, segstr, 'xyz')
600  call get_param(pf, mdl, segnam, segstr)
601 
602  call parse_segment_data_str(trim(segstr), fields=fields, num_fields=num_fields)
603  if (num_fields == 0) then
604  call mom_mesg('initialize_segment_data: num_fields = 0')
605  cycle ! cycle to next segment
606  endif
607 
608  allocate(segment%field(num_fields))
609 
610  if (segment%Flather) then
611  if (num_fields < 3) call mom_error(fatal, &
612  "MOM_open_boundary, initialize_segment_data: "//&
613  "Need at least three inputs for Flather")
614  endif
615  segment%num_fields = num_fields ! these are at least three input fields required for the Flather option
616 
617  segment%temp_segment_data_exists=.false.
618  segment%salt_segment_data_exists=.false.
619 !!
620 ! CODE HERE FOR OTHER OPTIONS (CLAMPED, NUDGED,..)
621 !!
622 
623  isd = segment%HI%isd ; ied = segment%HI%ied
624  jsd = segment%HI%jsd ; jed = segment%HI%jed
625  isdb = segment%HI%IsdB ; iedb = segment%HI%IedB
626  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
627 
628  do m=1,num_fields
629  call parse_segment_data_str(trim(segstr), var=trim(fields(m)), value=value, filenam=filename, fieldnam=fieldname)
630  if (trim(filename) /= 'none') then
631  obc%update_OBC = .true. ! Data is assumed to be time-dependent if we are reading from file
632  obc%needs_IO_for_data = .true. ! At least one segment is using I/O for OBC data
633  segment%values_needed = .true. ! Indicates that i/o will be needed for this segment
634  segment%field(m)%name = trim(fields(m))
635  if (segment%field(m)%name == 'TEMP') &
636  segment%temp_segment_data_exists=.true.
637  if (segment%field(m)%name == 'SALT') &
638  segment%salt_segment_data_exists=.true.
639  filename = trim(inputdir)//trim(filename)
640  fieldname = trim(fieldname)//trim(suffix)
641  call field_size(filename,fieldname,siz,no_domain=.true.)
642  if (siz(4) == 1) segment%values_needed = .false.
643  if (segment%on_pe) then
644  if (obc%brushcutter_mode .and. (modulo(siz(1),2) == 0 .or. modulo(siz(2),2) == 0)) then
645  call mom_error(fatal,'segment data are not on the supergrid')
646  endif
647  siz2(1)=1
648 
649  if (siz(1)>1) then
650  if (obc%brushcutter_mode) then
651  siz2(1)=(siz(1)-1)/2
652  else
653  siz2(1)=siz(1)
654  endif
655  endif
656  siz2(2)=1
657  if (siz(2)>1) then
658  if (obc%brushcutter_mode) then
659  siz2(2)=(siz(2)-1)/2
660  else
661  siz2(2)=siz(2)
662  endif
663  endif
664  siz2(3)=siz(3)
665 
666  if (segment%is_E_or_W) then
667  if (segment%field(m)%name == 'V' .or. segment%field(m)%name == 'DVDX') then
668  allocate(segment%field(m)%buffer_src(isdb:iedb,jsdb:jedb,siz2(3)))
669  else
670  allocate(segment%field(m)%buffer_src(isdb:iedb,jsd:jed,siz2(3)))
671  endif
672  else
673  if (segment%field(m)%name == 'U' .or. segment%field(m)%name == 'DUDY') then
674  allocate(segment%field(m)%buffer_src(isdb:iedb,jsdb:jedb,siz2(3)))
675  else
676  allocate(segment%field(m)%buffer_src(isd:ied,jsdb:jedb,siz2(3)))
677  endif
678  endif
679  segment%field(m)%buffer_src(:,:,:)=0.0
680  segment%field(m)%fid = init_external_field(trim(filename),&
681  trim(fieldname),ignore_axis_atts=.true.,threading=single_file)
682  if (siz(3) > 1) then
683  fieldname = 'dz_'//trim(fieldname)
684  call field_size(filename,fieldname,siz,no_domain=.true.)
685  if (segment%is_E_or_W) then
686  if (segment%field(m)%name == 'V' .or. segment%field(m)%name == 'DVDX') then
687  allocate(segment%field(m)%dz_src(isdb:iedb,jsdb:jedb,siz(3)))
688  else
689  allocate(segment%field(m)%dz_src(isdb:iedb,jsd:jed,siz(3)))
690  endif
691  else
692  if (segment%field(m)%name == 'U' .or. segment%field(m)%name == 'DUDY') then
693  allocate(segment%field(m)%dz_src(isdb:iedb,jsdb:jedb,siz(3)))
694  else
695  allocate(segment%field(m)%dz_src(isd:ied,jsdb:jedb,siz(3)))
696  endif
697  endif
698  segment%field(m)%dz_src(:,:,:)=0.0
699  segment%field(m)%nk_src=siz(3)
700  segment%field(m)%fid_dz = init_external_field(trim(filename),trim(fieldname),&
701  ignore_axis_atts=.true.,threading=single_file)
702  else
703  segment%field(m)%nk_src=1
704  endif
705  endif
706  else
707  segment%field(m)%fid = -1
708  segment%field(m)%value = value
709  endif
710  enddo
711  enddo
712 
713  call mpp_set_current_pelist(saved_pelist)
714 
715 end subroutine initialize_segment_data
716 
717 !> Define indices for segment and store in hor_index_type
718 !> using global segment bounds corresponding to q-points
719 subroutine setup_segment_indices(G, seg, Is_obc, Ie_obc, Js_obc, Je_obc)
720  type(dyn_horgrid_type), intent(in) :: G !< grid type
721  type(obc_segment_type), intent(inout) :: seg !< Open boundary segment
722  integer, intent(in) :: Is_obc !< Q-point global i-index of start of segment
723  integer, intent(in) :: Ie_obc !< Q-point global i-index of end of segment
724  integer, intent(in) :: Js_obc !< Q-point global j-index of start of segment
725  integer, intent(in) :: Je_obc !< Q-point global j-index of end of segment
726  ! Local variables
727  integer :: Isg,Ieg,Jsg,Jeg
728 
729  ! Isg, Ieg will be I*_obc in global space
730  if (ie_obc<is_obc) then
731  isg=ie_obc;ieg=is_obc
732  else
733  isg=is_obc;ieg=ie_obc
734  endif
735  if (je_obc<js_obc) then
736  jsg=je_obc;jeg=js_obc
737  else
738  jsg=js_obc;jeg=je_obc
739  endif
740 
741  ! Global space I*_obc but sorted
742  seg%HI%IsgB = isg ; seg%HI%IegB = ieg
743  seg%HI%isg = isg+1 ; seg%HI%ieg = ieg
744  seg%HI%JsgB = jsg ; seg%HI%JegB = jeg
745  seg%HI%jsg = jsg+1 ; seg%HI%Jeg = jeg
746 
747  ! Move into local index space
748  isg = isg - g%idg_offset
749  jsg = jsg - g%jdg_offset
750  ieg = ieg - g%idg_offset
751  jeg = jeg - g%jdg_offset
752 
753  ! This is the i-extent of the segment on this PE.
754  ! The values are nonsense if the segment is not on this PE.
755  seg%HI%IsdB = min( max(isg, g%HI%IsdB), g%HI%IedB)
756  seg%HI%IedB = min( max(ieg, g%HI%IsdB), g%HI%IedB)
757  seg%HI%isd = min( max(isg+1, g%HI%isd), g%HI%ied)
758  seg%HI%ied = min( max(ieg, g%HI%isd), g%HI%ied)
759  seg%HI%IscB = min( max(isg, g%HI%IscB), g%HI%IecB)
760  seg%HI%IecB = min( max(ieg, g%HI%IscB), g%HI%IecB)
761  seg%HI%isc = min( max(isg+1, g%HI%isc), g%HI%iec)
762  seg%HI%iec = min( max(ieg, g%HI%isc), g%HI%iec)
763 
764  ! This is the j-extent of the segment on this PE.
765  ! The values are nonsense if the segment is not on this PE.
766  seg%HI%JsdB = min( max(jsg, g%HI%JsdB), g%HI%JedB)
767  seg%HI%JedB = min( max(jeg, g%HI%JsdB), g%HI%JedB)
768  seg%HI%jsd = min( max(jsg+1, g%HI%jsd), g%HI%jed)
769  seg%HI%jed = min( max(jeg, g%HI%jsd), g%HI%jed)
770  seg%HI%JscB = min( max(jsg, g%HI%JscB), g%HI%JecB)
771  seg%HI%JecB = min( max(jeg, g%HI%JscB), g%HI%JecB)
772  seg%HI%jsc = min( max(jsg+1, g%HI%jsc), g%HI%jec)
773  seg%HI%jec = min( max(jeg, g%HI%jsc), g%HI%jec)
774 
775 end subroutine setup_segment_indices
776 
777 !> Parse an OBC_SEGMENT_%%% string starting with "I=" and configure placement and type of OBC accordingly
778 subroutine setup_u_point_obc(OBC, G, segment_str, l_seg, PF, reentrant_y)
779  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
780  type(dyn_horgrid_type), intent(in) :: G !< Ocean grid structure
781  character(len=*), intent(in) :: segment_str !< A string in form of "I=%,J=%:%,string"
782  integer, intent(in) :: l_seg !< which segment is this?
783  type(param_file_type), intent(in) :: PF !< Parameter file handle
784  logical, intent(in) :: reentrant_y !< is the domain reentrant in y?
785  ! Local variables
786  integer :: I_obc, Js_obc, Je_obc ! Position of segment in global index space
787  integer :: j, a_loop
788  character(len=32) :: action_str(8)
789  character(len=128) :: segment_param_str
790  real, allocatable, dimension(:) :: tnudge
791  ! This returns the global indices for the segment
792  call parse_segment_str(g%ieg, g%jeg, segment_str, i_obc, js_obc, je_obc, action_str, reentrant_y)
793 
794  call setup_segment_indices(g, obc%segment(l_seg),i_obc,i_obc,js_obc,je_obc)
795 
796  i_obc = i_obc - g%idg_offset ! Convert to local tile indices on this tile
797  js_obc = js_obc - g%jdg_offset ! Convert to local tile indices on this tile
798  je_obc = je_obc - g%jdg_offset ! Convert to local tile indices on this tile
799 
800  if (je_obc>js_obc) then
801  obc%segment(l_seg)%direction = obc_direction_e
802  elseif (je_obc<js_obc) then
803  obc%segment(l_seg)%direction = obc_direction_w
804  j=js_obc;js_obc=je_obc;je_obc=j
805  endif
806 
807  obc%segment(l_seg)%on_pe = .false.
808 
809  do a_loop = 1,8 ! up to 8 options available
810  if (len_trim(action_str(a_loop)) == 0) then
811  cycle
812  elseif (trim(action_str(a_loop)) == 'FLATHER') then
813  obc%segment(l_seg)%Flather = .true.
814  obc%segment(l_seg)%open = .true.
815  obc%Flather_u_BCs_exist_globally = .true.
816  obc%open_u_BCs_exist_globally = .true.
817  elseif (trim(action_str(a_loop)) == 'ORLANSKI') then
818  obc%segment(l_seg)%radiation = .true.
819  obc%segment(l_seg)%open = .true.
820  obc%open_u_BCs_exist_globally = .true.
821  obc%radiation_BCs_exist_globally = .true.
822  elseif (trim(action_str(a_loop)) == 'ORLANSKI_TAN') then
823  obc%segment(l_seg)%radiation = .true.
824  obc%segment(l_seg)%radiation_tan = .true.
825  obc%radiation_BCs_exist_globally = .true.
826  elseif (trim(action_str(a_loop)) == 'ORLANSKI_GRAD') then
827  obc%segment(l_seg)%radiation = .true.
828  obc%segment(l_seg)%radiation_grad = .true.
829  elseif (trim(action_str(a_loop)) == 'OBLIQUE') then
830  obc%segment(l_seg)%oblique = .true.
831  obc%segment(l_seg)%open = .true.
832  obc%oblique_BCs_exist_globally = .true.
833  obc%open_u_BCs_exist_globally = .true.
834  elseif (trim(action_str(a_loop)) == 'OBLIQUE_TAN') then
835  obc%segment(l_seg)%oblique = .true.
836  obc%segment(l_seg)%oblique_tan = .true.
837  obc%oblique_BCs_exist_globally = .true.
838  elseif (trim(action_str(a_loop)) == 'OBLIQUE_GRAD') then
839  obc%segment(l_seg)%oblique = .true.
840  obc%segment(l_seg)%oblique_grad = .true.
841  elseif (trim(action_str(a_loop)) == 'NUDGED') then
842  obc%segment(l_seg)%nudged = .true.
843  obc%nudged_u_BCs_exist_globally = .true.
844  elseif (trim(action_str(a_loop)) == 'NUDGED_TAN') then
845  obc%segment(l_seg)%nudged_tan = .true.
846  obc%nudged_u_BCs_exist_globally = .true.
847  elseif (trim(action_str(a_loop)) == 'NUDGED_GRAD') then
848  obc%segment(l_seg)%nudged_grad = .true.
849  elseif (trim(action_str(a_loop)) == 'GRADIENT') then
850  obc%segment(l_seg)%gradient = .true.
851  obc%segment(l_seg)%open = .true.
852  obc%open_u_BCs_exist_globally = .true.
853  elseif (trim(action_str(a_loop)) == 'SIMPLE') then
854  obc%segment(l_seg)%specified = .true.
855  obc%specified_u_BCs_exist_globally = .true. ! This avoids deallocation
856  elseif (trim(action_str(a_loop)) == 'SIMPLE_TAN') then
857  obc%segment(l_seg)%specified_tan = .true.
858  else
859  call mom_error(fatal, "MOM_open_boundary.F90, setup_u_point_obc: "//&
860  "String '"//trim(action_str(a_loop))//"' not understood.")
861  endif
862  if (obc%segment(l_seg)%nudged .or. obc%segment(l_seg)%nudged_tan) then
863  write(segment_param_str(1:43),"('OBC_SEGMENT_',i3.3,'_VELOCITY_NUDGING_TIMESCALES')") l_seg
864  allocate(tnudge(2))
865  call get_param(pf, mdl, segment_param_str(1:43), tnudge, &
866  "Timescales in days for nudging along a segment, "//&
867  "for inflow, then outflow. Setting both to zero should "//&
868  "behave like SIMPLE obcs for the baroclinic velocities.", &
869  fail_if_missing=.true.,default=0.,units="days")
870  obc%segment(l_seg)%Velocity_nudging_timescale_in = tnudge(1)*86400.
871  obc%segment(l_seg)%Velocity_nudging_timescale_out = tnudge(2)*86400.
872  deallocate(tnudge)
873  endif
874 
875  enddo ! a_loop
876 
877  if (i_obc<=g%HI%IsdB+1 .or. i_obc>=g%HI%IedB-1) return ! Boundary is not on tile
878  if (je_obc<=g%HI%JsdB .or. js_obc>=g%HI%JedB) return ! Segment is not on tile
879 
880  obc%segment(l_seg)%on_pe = .true.
881  obc%segment(l_seg)%is_E_or_W = .true.
882 
883  do j=g%HI%jsd, g%HI%jed
884  if (j>js_obc .and. j<=je_obc) then
885  obc%segnum_u(i_obc,j) = l_seg
886  endif
887  enddo
888  obc%segment(l_seg)%Is_obc = i_obc
889  obc%segment(l_seg)%Ie_obc = i_obc
890  obc%segment(l_seg)%Js_obc = js_obc
891  obc%segment(l_seg)%Je_obc = je_obc
892  call allocate_obc_segment_data(obc, obc%segment(l_seg))
893 
894  if (obc%segment(l_seg)%oblique .and. obc%segment(l_seg)%radiation) &
895  call mom_error(fatal, "MOM_open_boundary.F90, setup_u_point_obc: \n"//&
896  "Orlanski and Oblique OBC options cannot be used together on one segment.")
897 
898 end subroutine setup_u_point_obc
899 
900 !> Parse an OBC_SEGMENT_%%% string starting with "J=" and configure placement and type of OBC accordingly
901 subroutine setup_v_point_obc(OBC, G, segment_str, l_seg, PF, reentrant_x)
902  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
903  type(dyn_horgrid_type), intent(in) :: G !< Ocean grid structure
904  character(len=*), intent(in) :: segment_str !< A string in form of "J=%,I=%:%,string"
905  integer, intent(in) :: l_seg !< which segment is this?
906  type(param_file_type), intent(in) :: PF !< Parameter file handle
907  logical, intent(in) :: reentrant_x !< is the domain reentrant in x?
908  ! Local variables
909  integer :: J_obc, Is_obc, Ie_obc ! Position of segment in global index space
910  integer :: i, a_loop
911  character(len=32) :: action_str(8)
912  character(len=128) :: segment_param_str
913  real, allocatable, dimension(:) :: tnudge
914 
915  ! This returns the global indices for the segment
916  call parse_segment_str(g%ieg, g%jeg, segment_str, j_obc, is_obc, ie_obc, action_str, reentrant_x)
917 
918  call setup_segment_indices(g, obc%segment(l_seg),is_obc,ie_obc,j_obc,j_obc)
919 
920  j_obc = j_obc - g%jdg_offset ! Convert to local tile indices on this tile
921  is_obc = is_obc - g%idg_offset ! Convert to local tile indices on this tile
922  ie_obc = ie_obc - g%idg_offset ! Convert to local tile indices on this tile
923 
924  if (ie_obc>is_obc) then
925  obc%segment(l_seg)%direction = obc_direction_s
926  elseif (ie_obc<is_obc) then
927  obc%segment(l_seg)%direction = obc_direction_n
928  i=is_obc;is_obc=ie_obc;ie_obc=i
929  endif
930 
931  obc%segment(l_seg)%on_pe = .false.
932 
933  do a_loop = 1,8
934  if (len_trim(action_str(a_loop)) == 0) then
935  cycle
936  elseif (trim(action_str(a_loop)) == 'FLATHER') then
937  obc%segment(l_seg)%Flather = .true.
938  obc%segment(l_seg)%open = .true.
939  obc%Flather_v_BCs_exist_globally = .true.
940  obc%open_v_BCs_exist_globally = .true.
941  elseif (trim(action_str(a_loop)) == 'ORLANSKI') then
942  obc%segment(l_seg)%radiation = .true.
943  obc%segment(l_seg)%open = .true.
944  obc%open_v_BCs_exist_globally = .true.
945  obc%radiation_BCs_exist_globally = .true.
946  elseif (trim(action_str(a_loop)) == 'ORLANSKI_TAN') then
947  obc%segment(l_seg)%radiation = .true.
948  obc%segment(l_seg)%radiation_tan = .true.
949  obc%radiation_BCs_exist_globally = .true.
950  elseif (trim(action_str(a_loop)) == 'ORLANSKI_GRAD') then
951  obc%segment(l_seg)%radiation = .true.
952  obc%segment(l_seg)%radiation_grad = .true.
953  elseif (trim(action_str(a_loop)) == 'OBLIQUE') then
954  obc%segment(l_seg)%oblique = .true.
955  obc%segment(l_seg)%open = .true.
956  obc%oblique_BCs_exist_globally = .true.
957  obc%open_v_BCs_exist_globally = .true.
958  elseif (trim(action_str(a_loop)) == 'OBLIQUE_TAN') then
959  obc%segment(l_seg)%oblique = .true.
960  obc%segment(l_seg)%oblique_tan = .true.
961  obc%oblique_BCs_exist_globally = .true.
962  elseif (trim(action_str(a_loop)) == 'OBLIQUE_GRAD') then
963  obc%segment(l_seg)%oblique = .true.
964  obc%segment(l_seg)%oblique_grad = .true.
965  elseif (trim(action_str(a_loop)) == 'NUDGED') then
966  obc%segment(l_seg)%nudged = .true.
967  obc%nudged_v_BCs_exist_globally = .true.
968  elseif (trim(action_str(a_loop)) == 'NUDGED_TAN') then
969  obc%segment(l_seg)%nudged_tan = .true.
970  obc%nudged_v_BCs_exist_globally = .true.
971  elseif (trim(action_str(a_loop)) == 'NUDGED_GRAD') then
972  obc%segment(l_seg)%nudged_grad = .true.
973  elseif (trim(action_str(a_loop)) == 'GRADIENT') then
974  obc%segment(l_seg)%gradient = .true.
975  obc%segment(l_seg)%open = .true.
976  obc%open_v_BCs_exist_globally = .true.
977  elseif (trim(action_str(a_loop)) == 'SIMPLE') then
978  obc%segment(l_seg)%specified = .true.
979  obc%specified_v_BCs_exist_globally = .true. ! This avoids deallocation
980  elseif (trim(action_str(a_loop)) == 'SIMPLE_TAN') then
981  obc%segment(l_seg)%specified_tan = .true.
982  else
983  call mom_error(fatal, "MOM_open_boundary.F90, setup_v_point_obc: "//&
984  "String '"//trim(action_str(a_loop))//"' not understood.")
985  endif
986  if (obc%segment(l_seg)%nudged .or. obc%segment(l_seg)%nudged_tan) then
987  write(segment_param_str(1:43),"('OBC_SEGMENT_',i3.3,'_VELOCITY_NUDGING_TIMESCALES')") l_seg
988  allocate(tnudge(2))
989  call get_param(pf, mdl, segment_param_str(1:43), tnudge, &
990  "Timescales in days for nudging along a segment, "//&
991  "for inflow, then outflow. Setting both to zero should "//&
992  "behave like SIMPLE obcs for the baroclinic velocities.", &
993  fail_if_missing=.true.,default=0.,units="days")
994  obc%segment(l_seg)%Velocity_nudging_timescale_in = tnudge(1)*86400.
995  obc%segment(l_seg)%Velocity_nudging_timescale_out = tnudge(2)*86400.
996  deallocate(tnudge)
997  endif
998 
999  enddo ! a_loop
1000 
1001  if (j_obc<=g%HI%JsdB+1 .or. j_obc>=g%HI%JedB-1) return ! Boundary is not on tile
1002  if (ie_obc<=g%HI%IsdB .or. is_obc>=g%HI%IedB) return ! Segment is not on tile
1003 
1004  obc%segment(l_seg)%on_pe = .true.
1005  obc%segment(l_seg)%is_N_or_S = .true.
1006 
1007  do i=g%HI%isd, g%HI%ied
1008  if (i>is_obc .and. i<=ie_obc) then
1009  obc%segnum_v(i,j_obc) = l_seg
1010  endif
1011  enddo
1012  obc%segment(l_seg)%Is_obc = is_obc
1013  obc%segment(l_seg)%Ie_obc = ie_obc
1014  obc%segment(l_seg)%Js_obc = j_obc
1015  obc%segment(l_seg)%Je_obc = j_obc
1016  call allocate_obc_segment_data(obc, obc%segment(l_seg))
1017 
1018  if (obc%segment(l_seg)%oblique .and. obc%segment(l_seg)%radiation) &
1019  call mom_error(fatal, "MOM_open_boundary.F90, setup_v_point_obc: \n"//&
1020  "Orlanski and Oblique OBC options cannot be used together on one segment.")
1021 
1022 end subroutine setup_v_point_obc
1023 
1024 !> Parse an OBC_SEGMENT_%%% string
1025 subroutine parse_segment_str(ni_global, nj_global, segment_str, l, m, n, action_str, reentrant)
1026  integer, intent(in) :: ni_global !< Number of h-points in zonal direction
1027  integer, intent(in) :: nj_global !< Number of h-points in meridional direction
1028  character(len=*), intent(in) :: segment_str !< A string in form of "I=l,J=m:n,string" or "J=l,I=m,n,string"
1029  integer, intent(out) :: l !< The value of I=l, if segment_str begins with I=l, or the value of J=l
1030  integer, intent(out) :: m !< The value of J=m, if segment_str begins with I=, or the value of I=m
1031  integer, intent(out) :: n !< The value of J=n, if segment_str begins with I=, or the value of I=n
1032  character(len=*), intent(out) :: action_str(:) !< The "string" part of segment_str
1033  logical, intent(in) :: reentrant !< is domain reentrant in relevant direction?
1034  ! Local variables
1035  character(len=24) :: word1, word2, m_word, n_word !< Words delineated by commas in a string in form of
1036  !! "I=%,J=%:%,string"
1037  integer :: l_max !< Either ni_global or nj_global, depending on whether segment_str begins with "I=" or "J="
1038  integer :: mn_max !< Either nj_global or ni_global, depending on whether segment_str begins with "I=" or "J="
1039  integer :: j
1040  integer, parameter :: halo = 10
1041 
1042  ! Process first word which will started with either 'I=' or 'J='
1043  word1 = extract_word(segment_str,',',1)
1044  word2 = extract_word(segment_str,',',2)
1045  if (word1(1:2)=='I=') then
1046  l_max = ni_global
1047  mn_max = nj_global
1048  if (.not. (word2(1:2)=='J=')) call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
1049  "Second word of string '"//trim(segment_str)//"' must start with 'J='.")
1050  elseif (word1(1:2)=='J=') then ! Note that the file_parser uniformaly expands "=" to " = "
1051  l_max = nj_global
1052  mn_max = ni_global
1053  if (.not. (word2(1:2)=='I=')) call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
1054  "Second word of string '"//trim(segment_str)//"' must start with 'I='.")
1055  else
1056  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str"//&
1057  "String '"//segment_str//"' must start with 'I=' or 'J='.")
1058  endif
1059 
1060  ! Read l
1061  l = interpret_int_expr( word1(3:24), l_max )
1062  if (l<0 .or. l>l_max) then
1063  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
1064  "First value from string '"//trim(segment_str)//"' is outside of the physical domain.")
1065  endif
1066 
1067  ! Read m
1068  m_word = extract_word(word2(3:24),':',1)
1069  m = interpret_int_expr( m_word, mn_max )
1070  if (reentrant) then
1071  if (m<-halo .or. m>mn_max+halo) then
1072  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
1073  "Beginning of range in string '"//trim(segment_str)//"' is outside of the physical domain.")
1074  endif
1075  else
1076  if (m<-1 .or. m>mn_max+1) then
1077  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
1078  "Beginning of range in string '"//trim(segment_str)//"' is outside of the physical domain.")
1079  endif
1080  endif
1081 
1082  ! Read n
1083  n_word = extract_word(word2(3:24),':',2)
1084  n = interpret_int_expr( n_word, mn_max )
1085  if (reentrant) then
1086  if (n<-halo .or. n>mn_max+halo) then
1087  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
1088  "End of range in string '"//trim(segment_str)//"' is outside of the physical domain.")
1089  endif
1090  else
1091  if (n<-1 .or. n>mn_max+1) then
1092  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
1093  "End of range in string '"//trim(segment_str)//"' is outside of the physical domain.")
1094  endif
1095  endif
1096 
1097  if (abs(n-m)==0) then
1098  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
1099  "Range in string '"//trim(segment_str)//"' must span one cell.")
1100  endif
1101 
1102  ! Type of open boundary condition
1103  do j = 1, size(action_str)
1104  action_str(j) = extract_word(segment_str,',',2+j)
1105  enddo
1106 
1107  contains
1108 
1109  ! Returns integer value interpreted from string in form of %I, N or N+-%I
1110  integer function interpret_int_expr(string, imax)
1111  character(len=*), intent(in) :: string !< Integer in form or %I, N or N-%I
1112  integer, intent(in) :: imax !< Value to replace 'N' with
1113  ! Local variables
1114  integer slen
1115 
1116  slen = len_trim(string)
1117  if (slen==0) call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str"//&
1118  "Parsed string was empty!")
1119  if (len_trim(string)==1 .and. string(1:1)=='N') then
1120  interpret_int_expr = imax
1121  elseif (string(1:1)=='N') then
1122  if (string(2:2)=='+') then
1123  read(string(3:slen),*,err=911) interpret_int_expr
1124  interpret_int_expr = imax + interpret_int_expr
1125  elseif (string(2:2)=='-') then
1126  read(string(3:slen),*,err=911) interpret_int_expr
1127  interpret_int_expr = imax - interpret_int_expr
1128  endif
1129  else
1130  read(string(1:slen),*,err=911) interpret_int_expr
1131  endif
1132  return
1133  911 call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str"//&
1134  "Problem reading value from string '"//trim(string)//"'.")
1135  end function interpret_int_expr
1136 end subroutine parse_segment_str
1137 
1138 !> Parse an OBC_SEGMENT_%%%_DATA string
1139  subroutine parse_segment_data_str(segment_str, var, value, filenam, fieldnam, fields, num_fields, debug )
1140  character(len=*), intent(in) :: segment_str !< A string in form of
1141  !! "VAR1=file:foo1.nc(varnam1),VAR2=file:foo2.nc(varnam2),..."
1142  character(len=*), optional, intent(in) :: var !< The name of the variable for which parameters are needed
1143  character(len=*), optional, intent(out) :: filenam !< The name of the input file if using "file" method
1144  character(len=*), optional, intent(out) :: fieldnam !< The name of the variable in the input file if using
1145  !! "file" method
1146  real, optional, intent(out) :: value !< A constant value if using the "value" method
1147  character(len=*), dimension(MAX_OBC_FIELDS), &
1148  optional, intent(out) :: fields !< List of fieldnames for each segment
1149  integer, optional, intent(out) :: num_fields !< The number of fields in the segment data
1150  logical, optional, intent(in) :: debug !< If present and true, write verbose debugging messages
1151  ! Local variables
1152  character(len=128) :: word1, word2, word3, method
1153  integer :: lword, nfields, n, m, orient
1154  logical :: continue,dbg
1155  character(len=32), dimension(MAX_OBC_FIELDS) :: flds
1156 
1157  nfields=0
1158  continue=.true.
1159  dbg=.false.
1160  if (PRESENT(debug)) dbg=debug
1161 
1162  do while (continue)
1163  word1 = extract_word(segment_str,',',nfields+1)
1164  if (trim(word1) == '') exit
1165  nfields=nfields+1
1166  word2 = extract_word(word1,'=',1)
1167  flds(nfields) = trim(word2)
1168  enddo
1169 
1170  if (PRESENT(fields)) then
1171  do n=1,nfields
1172  fields(n) = flds(n)
1173  enddo
1174  endif
1175 
1176  if (PRESENT(num_fields)) then
1177  num_fields=nfields
1178  return
1179  endif
1180 
1181  m=0
1182  if (PRESENT(var)) then
1183  do n=1,nfields
1184  if (trim(var)==trim(flds(n))) then
1185  m=n
1186  exit
1187  endif
1188  enddo
1189  if (m==0) then
1190  call abort()
1191  endif
1192 
1193  ! Process first word which will start with the fieldname
1194  word3 = extract_word(segment_str,',',m)
1195  word1 = extract_word(word3,':',1)
1196 ! if (trim(word1) == '') exit
1197  word2 = extract_word(word1,'=',1)
1198  if (trim(word2) == trim(var)) then
1199  method=trim(extract_word(word1,'=',2))
1200  lword=len_trim(method)
1201  if (method(lword-3:lword) == 'file') then
1202  ! raise an error id filename/fieldname not in argument list
1203  word1 = extract_word(word3,':',2)
1204  filenam = extract_word(word1,'(',1)
1205  fieldnam = extract_word(word1,'(',2)
1206  lword=len_trim(fieldnam)
1207  fieldnam = fieldnam(1:lword-1) ! remove trailing parenth
1208  value=-999.
1209  elseif (method(lword-4:lword) == 'value') then
1210  filenam = 'none'
1211  fieldnam = 'none'
1212  word1 = extract_word(word3,':',2)
1213  lword=len_trim(word1)
1214  read(word1(1:lword),*,end=986,err=987) value
1215  endif
1216  endif
1217  endif
1218 
1219  return
1220  986 call mom_error(fatal,'End of record while parsing segment data specification! '//trim(segment_str))
1221  987 call mom_error(fatal,'Error while parsing segment data specification! '//trim(segment_str))
1222 
1223  end subroutine parse_segment_data_str
1224 
1225 
1226 !> Parse an OBC_SEGMENT_%%%_PARAMS string
1227  subroutine parse_segment_param_real(segment_str, var, param_value, debug )
1228  character(len=*), intent(in) :: segment_str !< A string in form of
1229  !! "VAR1=file:foo1.nc(varnam1),VAR2=file:foo2.nc(varnam2),..."
1230  character(len=*), intent(in) :: var !< The name of the variable for which parameters are needed
1231  real, intent(out) :: param_value !< The value of the parameter
1232  logical, optional, intent(in) :: debug !< If present and true, write verbose debugging messages
1233  ! Local variables
1234  character(len=128) :: word1, word2, word3, method
1235  integer :: lword, nfields, n, m, orient
1236  logical :: continue,dbg
1237  character(len=32), dimension(MAX_OBC_FIELDS) :: flds
1238 
1239  nfields=0
1240  continue=.true.
1241  dbg=.false.
1242  if (PRESENT(debug)) dbg=debug
1243 
1244  do while (continue)
1245  word1 = extract_word(segment_str,',',nfields+1)
1246  if (trim(word1) == '') exit
1247  nfields=nfields+1
1248  word2 = extract_word(word1,'=',1)
1249  flds(nfields) = trim(word2)
1250  enddo
1251 
1252  ! if (PRESENT(fields)) then
1253  ! do n=1,nfields
1254  ! fields(n) = flds(n)
1255  ! enddo
1256  ! endif
1257 
1258  ! if (PRESENT(num_fields)) then
1259  ! num_fields=nfields
1260  ! return
1261  ! endif
1262 
1263  m=0
1264 ! if (PRESENT(var)) then
1265  do n=1,nfields
1266  if (trim(var)==trim(flds(n))) then
1267  m=n
1268  exit
1269  endif
1270  enddo
1271  if (m==0) then
1272  call abort()
1273  endif
1274 
1275  ! Process first word which will start with the fieldname
1276  word3 = extract_word(segment_str,',',m)
1277 ! word1 = extract_word(word3,':',1)
1278 ! if (trim(word1) == '') exit
1279  word2 = extract_word(word1,'=',1)
1280  if (trim(word2) == trim(var)) then
1281  method=trim(extract_word(word1,'=',2))
1282  lword=len_trim(method)
1283  read(method(1:lword),*,err=987) param_value
1284  ! if (method(lword-3:lword) == 'file') then
1285  ! ! raise an error id filename/fieldname not in argument list
1286  ! word1 = extract_word(word3,':',2)
1287  ! filenam = extract_word(word1,'(',1)
1288  ! fieldnam = extract_word(word1,'(',2)
1289  ! lword=len_trim(fieldnam)
1290  ! fieldnam = fieldnam(1:lword-1) ! remove trailing parenth
1291  ! value=-999.
1292  ! elseif (method(lword-4:lword) == 'value') then
1293  ! filenam = 'none'
1294  ! fieldnam = 'none'
1295  ! word1 = extract_word(word3,':',2)
1296  ! lword=len_trim(word1)
1297  ! read(word1(1:lword),*,end=986,err=987) value
1298  ! endif
1299  endif
1300 ! endif
1301 
1302  return
1303  986 call mom_error(fatal,'End of record while parsing segment data specification! '//trim(segment_str))
1304  987 call mom_error(fatal,'Error while parsing segment parameter specification! '//trim(segment_str))
1305 
1306  end subroutine parse_segment_param_real
1307 
1308 !> Initialize open boundary control structure
1309 subroutine open_boundary_init(G, param_file, OBC)
1310  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
1311  type(param_file_type), intent(in) :: param_file !< Parameter file handle
1312  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1313  ! Local variables
1314 
1315  if (.not.associated(obc)) return
1316 
1317  id_clock_pass = cpu_clock_id('(Ocean OBC halo updates)', grain=clock_routine)
1318 
1319 end subroutine open_boundary_init
1320 
1321 logical function open_boundary_query(OBC, apply_open_OBC, apply_specified_OBC, apply_Flather_OBC, &
1322  apply_nudged_OBC, needs_ext_seg_data)
1323  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1324  logical, optional, intent(in) :: apply_open_obc !< Returns True if open_*_BCs_exist_globally is true
1325  logical, optional, intent(in) :: apply_specified_obc !< Returns True if specified_*_BCs_exist_globally is true
1326  logical, optional, intent(in) :: apply_flather_obc !< Returns True if Flather_*_BCs_exist_globally is true
1327  logical, optional, intent(in) :: apply_nudged_obc !< Returns True if nudged_*_BCs_exist_globally is true
1328  logical, optional, intent(in) :: needs_ext_seg_data !< Returns True if external segment data needed
1329  open_boundary_query = .false.
1330  if (.not. associated(obc)) return
1331  if (present(apply_open_obc)) open_boundary_query = obc%open_u_BCs_exist_globally .or. &
1332  obc%open_v_BCs_exist_globally
1333  if (present(apply_specified_obc)) open_boundary_query = obc%specified_u_BCs_exist_globally .or. &
1334  obc%specified_v_BCs_exist_globally
1335  if (present(apply_flather_obc)) open_boundary_query = obc%Flather_u_BCs_exist_globally .or. &
1336  obc%Flather_v_BCs_exist_globally
1337  if (present(apply_nudged_obc)) open_boundary_query = obc%nudged_u_BCs_exist_globally .or. &
1338  obc%nudged_v_BCs_exist_globally
1339  if (present(needs_ext_seg_data)) open_boundary_query = obc%needs_IO_for_data
1340 
1341 end function open_boundary_query
1342 
1343 !> Deallocate open boundary data
1344 subroutine open_boundary_dealloc(OBC)
1345  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
1346  type(obc_segment_type), pointer :: segment => null()
1347  integer :: n
1348 
1349  if (.not. associated(obc)) return
1350 
1351  do n=1, obc%number_of_segments
1352  segment => obc%segment(n)
1353  call deallocate_obc_segment_data(obc, segment)
1354  enddo
1355  if (associated(obc%segment)) deallocate(obc%segment)
1356  if (associated(obc%segnum_u)) deallocate(obc%segnum_u)
1357  if (associated(obc%segnum_v)) deallocate(obc%segnum_v)
1358  deallocate(obc)
1359 end subroutine open_boundary_dealloc
1360 
1361 !> Close open boundary data
1362 subroutine open_boundary_end(OBC)
1363  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1364  call open_boundary_dealloc(obc)
1365 end subroutine open_boundary_end
1366 
1367 !> Sets the slope of bathymetry normal to an open bounndary to zero.
1368 subroutine open_boundary_impose_normal_slope(OBC, G, depth)
1369  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1370  type(dyn_horgrid_type), intent(in) :: g !< Ocean grid structure
1371  real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: depth !< Bathymetry at h-points
1372  ! Local variables
1373  integer :: i, j, n
1374  type(obc_segment_type), pointer :: segment => null()
1375 
1376  if (.not.associated(obc)) return
1377 
1378  if (.not.(obc%specified_u_BCs_exist_globally .or. obc%specified_v_BCs_exist_globally .or. &
1379  obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)) &
1380  return
1381 
1382  do n=1,obc%number_of_segments
1383  segment=>obc%segment(n)
1384  if (.not. segment%on_pe) cycle
1385  if (segment%direction == obc_direction_e) then
1386  i=segment%HI%IsdB
1387  do j=segment%HI%jsd,segment%HI%jed
1388  depth(i+1,j) = depth(i,j)
1389  enddo
1390  elseif (segment%direction == obc_direction_w) then
1391  i=segment%HI%IsdB
1392  do j=segment%HI%jsd,segment%HI%jed
1393  depth(i,j) = depth(i+1,j)
1394  enddo
1395  elseif (segment%direction == obc_direction_n) then
1396  j=segment%HI%JsdB
1397  do i=segment%HI%isd,segment%HI%ied
1398  depth(i,j+1) = depth(i,j)
1399  enddo
1400  elseif (segment%direction == obc_direction_s) then
1401  j=segment%HI%JsdB
1402  do i=segment%HI%isd,segment%HI%ied
1403  depth(i,j) = depth(i,j+1)
1404  enddo
1405  endif
1406  enddo
1407 
1408 end subroutine open_boundary_impose_normal_slope
1409 
1410 !> Reconcile masks and open boundaries, deallocate OBC on PEs where it is not needed.
1411 !! Also adjust u- and v-point cell area on specified open boundaries and mask all
1412 !! points outside open boundaries.
1413 subroutine open_boundary_impose_land_mask(OBC, G, areaCu, areaCv)
1414  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1415  type(dyn_horgrid_type), intent(inout) :: g !< Ocean grid structure
1416  real, dimension(SZIB_(G),SZJ_(G)), intent(inout) :: areacu !< Area of a u-cell [m2]
1417  real, dimension(SZI_(G),SZJB_(G)), intent(inout) :: areacv !< Area of a u-cell [m2]
1418  ! Local variables
1419  integer :: i, j, n
1420  type(obc_segment_type), pointer :: segment => null()
1421  logical :: any_u, any_v
1422 
1423  if (.not.associated(obc)) return
1424 
1425  do n=1,obc%number_of_segments
1426  segment=>obc%segment(n)
1427  if (.not. segment%on_pe) cycle
1428  if (segment%is_E_or_W) then
1429  ! Sweep along u-segments and delete the OBC for blocked points.
1430  ! Also, mask all points outside.
1431  i=segment%HI%IsdB
1432  do j=segment%HI%jsd,segment%HI%jed
1433  if (g%mask2dCu(i,j) == 0) obc%segnum_u(i,j) = obc_none
1434  if (segment%direction == obc_direction_w) then
1435  g%mask2dT(i,j) = 0
1436  else
1437  g%mask2dT(i+1,j) = 0
1438  endif
1439  enddo
1440  do j=segment%HI%JsdB+1,segment%HI%JedB-1
1441  if (segment%direction == obc_direction_w) then
1442  g%mask2dCv(i,j) = 0
1443  else
1444  g%mask2dCv(i+1,j) = 0
1445  endif
1446  enddo
1447  else
1448  ! Sweep along v-segments and delete the OBC for blocked points.
1449  j=segment%HI%JsdB
1450  do i=segment%HI%isd,segment%HI%ied
1451  if (g%mask2dCv(i,j) == 0) obc%segnum_v(i,j) = obc_none
1452  if (segment%direction == obc_direction_s) then
1453  g%mask2dT(i,j) = 0
1454  else
1455  g%mask2dT(i,j+1) = 0
1456  endif
1457  enddo
1458  do i=segment%HI%IsdB+1,segment%HI%IedB-1
1459  if (segment%direction == obc_direction_s) then
1460  g%mask2dCu(i,j) = 0
1461  else
1462  g%mask2dCu(i,j+1) = 0
1463  endif
1464  enddo
1465  endif
1466  enddo
1467 
1468  do n=1,obc%number_of_segments
1469  segment=>obc%segment(n)
1470  if (.not. segment%on_pe .or. .not. segment%specified) cycle
1471  if (segment%is_E_or_W) then
1472  ! Sweep along u-segments and for %specified BC points reset the u-point area which was masked out
1473  i=segment%HI%IsdB
1474  do j=segment%HI%jsd,segment%HI%jed
1475  if (segment%direction == obc_direction_e) then
1476  areacu(i,j) = g%areaT(i,j)
1477  else ! West
1478  areacu(i,j) = g%areaT(i+1,j)
1479  endif
1480  enddo
1481  else
1482  ! Sweep along v-segments and for %specified BC points reset the v-point area which was masked out
1483  j=segment%HI%JsdB
1484  do i=segment%HI%isd,segment%HI%ied
1485  if (segment%direction == obc_direction_s) then
1486  areacv(i,j) = g%areaT(i,j+1)
1487  else ! North
1488  areacu(i,j) = g%areaT(i,j)
1489  endif
1490  enddo
1491  endif
1492  enddo
1493 
1494  ! G%mask2du will be open wherever bathymetry allows it.
1495  ! Bathymetry outside of the open boundary was adjusted to match
1496  ! the bathymetry inside so these points will be open unless the
1497  ! bathymetry inside the boundary was too shallow and flagged as land.
1498  any_u = .false.
1499  any_v = .false.
1500  do n=1,obc%number_of_segments
1501  segment=>obc%segment(n)
1502  if (.not. segment%on_pe) cycle
1503  if (segment%is_E_or_W) then
1504  i=segment%HI%IsdB
1505  do j=segment%HI%jsd,segment%HI%jed
1506  if (obc%segnum_u(i,j) /= obc_none) any_u = .true.
1507  enddo
1508  else
1509  j=segment%HI%JsdB
1510  do i=segment%HI%isd,segment%HI%ied
1511  if (obc%segnum_v(i,j) /= obc_none) any_v = .true.
1512  enddo
1513  endif
1514  enddo
1515 
1516  obc%OBC_pe = .true.
1517  if (.not.(any_u .or. any_v)) obc%OBC_pe = .false.
1518 
1519 end subroutine open_boundary_impose_land_mask
1520 
1521 !> Apply radiation conditions to 3D u,v at open boundaries
1522 subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, dt)
1523  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
1524  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1525  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: u_new !< On exit, new u values on open boundaries
1526  !! On entry, the old time-level v but
1527  !! including barotropic accelerations.
1528  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u_old !< Original unadjusted u
1529  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: v_new !< On exit, new v values on open boundaries.
1530  !! On entry, the old time-level v but
1531  !! including barotropic accelerations.
1532  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v_old !< Original unadjusted v
1533  real, intent(in) :: dt !< Appropriate timestep
1534  ! Local variables
1535  real :: dhdt, dhdx, dhdy, gamma_u, gamma_v, gamma_2
1536  real :: cff, cx, cy, tau
1537  real :: rx_max, ry_max ! coefficients for radiation
1538  real :: rx_new, rx_avg ! coefficients for radiation
1539  real :: ry_new, ry_avg ! coefficients for radiation
1540  real :: cff_new, cff_avg ! denominator in oblique
1541  real, pointer, dimension(:,:,:) :: rx_tangential=>null()
1542  real, pointer, dimension(:,:,:) :: ry_tangential=>null()
1543  real, pointer, dimension(:,:,:) :: cff_tangential=>null()
1544  real, parameter :: eps = 1.0e-20
1545  type(obc_segment_type), pointer :: segment => null()
1546  integer :: i, j, k, is, ie, js, je, nz, n
1547  integer :: is_obc, ie_obc, js_obc, je_obc
1548 
1549  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
1550 
1551  if (.not.associated(obc)) return
1552 
1553  if (.not.(obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)) &
1554  return
1555 
1556  !! Copy previously calculated phase velocity from global arrays into segments
1557  !! This is terribly inefficient and temporary solution for continuity across restarts
1558  !! and needs to be revisited in the future.
1559  do n=1,obc%number_of_segments
1560  segment=>obc%segment(n)
1561  if (.not. segment%on_pe) cycle
1562  if (segment%is_E_or_W .and. segment%radiation) then
1563  do k=1,g%ke
1564  i=segment%HI%IsdB
1565  do j=segment%HI%jsd,segment%HI%jed
1566  segment%rx_normal(i,j,k) = obc%rx_normal(i,j,k)
1567  enddo
1568  enddo
1569  elseif (segment%is_N_or_S .and. segment%radiation) then
1570  do k=1,g%ke
1571  j=segment%HI%JsdB
1572  do i=segment%HI%isd,segment%HI%ied
1573  segment%ry_normal(i,j,k) = obc%ry_normal(i,j,k)
1574  enddo
1575  enddo
1576  endif
1577  if (segment%is_E_or_W .and. segment%oblique) then
1578  do k=1,g%ke
1579  i=segment%HI%IsdB
1580  do j=segment%HI%jsd,segment%HI%jed
1581  segment%rx_normal(i,j,k) = obc%rx_normal(i,j,k)
1582  segment%ry_normal(i,j,k) = obc%ry_normal(i,j,k)
1583  segment%cff_normal(i,j,k) = obc%cff_normal(i,j,k)
1584  enddo
1585  enddo
1586  elseif (segment%is_N_or_S .and. segment%oblique) then
1587  do k=1,g%ke
1588  j=segment%HI%JsdB
1589  do i=segment%HI%isd,segment%HI%ied
1590  segment%rx_normal(i,j,k) = obc%rx_normal(i,j,k)
1591  segment%ry_normal(i,j,k) = obc%ry_normal(i,j,k)
1592  segment%cff_normal(i,j,k) = obc%cff_normal(i,j,k)
1593  enddo
1594  enddo
1595  endif
1596  enddo
1597 
1598  gamma_u = obc%gamma_uv ; gamma_v = obc%gamma_uv
1599  rx_max = obc%rx_max ; ry_max = obc%rx_max
1600  do n=1,obc%number_of_segments
1601  segment=>obc%segment(n)
1602  if (.not. segment%on_pe) cycle
1603  if (segment%oblique) call gradient_at_q_points(g,segment,u_new,v_new)
1604  if (segment%direction == obc_direction_e) then
1605  i=segment%HI%IsdB
1606  if (i<g%HI%IscB) cycle
1607  do k=1,nz ; do j=segment%HI%jsd,segment%HI%jed
1608  if (segment%radiation) then
1609  dhdt = u_old(i-1,j,k)-u_new(i-1,j,k) !old-new
1610  dhdx = u_new(i-1,j,k)-u_new(i-2,j,k) !in new time backward sasha for I-1
1611  rx_new = 0.0
1612  if (dhdt*dhdx > 0.0) rx_new = min( (dhdt/dhdx), rx_max) ! outward phase speed
1613  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
1614  segment%rx_normal(i,j,k) = rx_avg
1615  ! The new boundary value is interpolated between future interior
1616  ! value, u_new(I-1) and past boundary value but with barotropic
1617  ! accelerations, u_new(I).
1618  segment%normal_vel(i,j,k) = (u_new(i,j,k) + rx_avg*u_new(i-1,j,k)) / (1.0+rx_avg)
1619  ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues
1620  ! implemented as a work-around to limitations in restart capability
1621  obc%rx_normal(i,j,k) = segment%rx_normal(i,j,k)
1622  elseif (segment%oblique) then
1623  dhdt = u_old(i-1,j,k)-u_new(i-1,j,k) !old-new
1624  dhdx = u_new(i-1,j,k)-u_new(i-2,j,k) !in new time backward sasha for I-1
1625  if (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) > 0.0) then
1626  dhdy = segment%grad_normal(j-1,1,k)
1627  elseif (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) == 0.0) then
1628  dhdy = 0.0
1629  else
1630  dhdy = segment%grad_normal(j,1,k)
1631  endif
1632  if (dhdt*dhdx < 0.0) dhdt = 0.0
1633  rx_new = dhdt*dhdx
1634  cff_new = max(dhdx*dhdx + dhdy*dhdy, eps)
1635  ry_new = min(cff,max(dhdt*dhdy,-cff))
1636  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
1637  ry_avg = (1.0-gamma_u)*segment%ry_normal(i,j,k) + gamma_u*ry_new
1638  cff_avg = (1.0-gamma_u)*segment%cff_normal(i,j,k) + gamma_u*cff_new
1639  segment%rx_normal(i,j,k) = rx_avg
1640  segment%ry_normal(i,j,k) = ry_avg
1641  segment%cff_normal(i,j,k) = cff_avg
1642  segment%normal_vel(i,j,k) = ((cff_avg*u_new(i,j,k) + rx_avg*u_new(i-1,j,k)) - &
1643  (max(ry_avg,0.0)*segment%grad_normal(j-1,2,k) + min(ry_avg,0.0)*segment%grad_normal(j,2,k))) / &
1644  (cff_avg + rx_avg)
1645  ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues
1646  ! implemented as a work-around to limitations in restart capability
1647  obc%rx_normal(i,j,k) = segment%rx_normal(i,j,k)
1648  obc%ry_normal(i,j,k) = segment%ry_normal(i,j,k)
1649  obc%cff_normal(i,j,k) = segment%cff_normal(i,j,k)
1650  elseif (segment%gradient) then
1651  segment%normal_vel(i,j,k) = u_new(i-1,j,k)
1652  endif
1653  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1654  ! dhdt gets set to 0 on inflow in oblique case
1655  if (dhdt*dhdx <= 0.0) then
1656  tau = segment%Velocity_nudging_timescale_in
1657  else
1658  tau = segment%Velocity_nudging_timescale_out
1659  endif
1660  gamma_2 = dt / (tau + dt)
1661  segment%normal_vel(i,j,k) = (1 - gamma_2) * segment%normal_vel(i,j,k) + &
1662  gamma_2 * segment%nudged_normal_vel(i,j,k)
1663  endif
1664  enddo ; enddo
1665  if (segment%radiation_tan .or. segment%radiation_grad) then
1666  i=segment%HI%IsdB
1667  allocate(rx_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
1668  do k=1,nz
1669  rx_tangential(i,segment%HI%JsdB,k) = segment%rx_normal(i,segment%HI%jsd,k)
1670  rx_tangential(i,segment%HI%JedB,k) = segment%rx_normal(i,segment%HI%jed,k)
1671  do j=segment%HI%JsdB+1,segment%HI%JedB-1
1672  rx_tangential(i,j,k) = 0.5*(segment%rx_normal(i,j,k) + segment%rx_normal(i,j+1,k))
1673  enddo
1674  enddo
1675  if (segment%radiation_tan) then
1676  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1677  rx_avg = rx_tangential(i,j,k)
1678  segment%tangential_vel(i,j,k) = (v_new(i,j,k) + rx_avg*v_new(i-1,j,k)) / (1.0+rx_avg)
1679  enddo ; enddo
1680  endif
1681  if (segment%nudged_tan) then
1682  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1683  ! dhdt gets set to 0 on inflow in oblique case
1684  if (rx_tangential(i,j,k) <= 0.0) then
1685  tau = segment%Velocity_nudging_timescale_in
1686  else
1687  tau = segment%Velocity_nudging_timescale_out
1688  endif
1689  gamma_2 = dt / (tau + dt)
1690  segment%tangential_vel(i,j,k) = (1 - gamma_2) * segment%tangential_vel(i,j,k) + &
1691  gamma_2 * segment%nudged_tangential_vel(i,j,k)
1692  enddo ; enddo
1693  endif
1694  if (segment%radiation_grad) then
1695  js_obc = max(segment%HI%JsdB,g%jsd+1)
1696  je_obc = min(segment%HI%JedB,g%jed-1)
1697  do k=1,nz ; do j=js_obc,je_obc
1698  rx_avg = rx_tangential(i,j,k)
1699 ! if (G%mask2dCu(I-1,j) > 0.0 .and. G%mask2dCu(I-1,j+1) > 0.0) then
1700 ! rx_avg = 0.5*(u_new(I-1,j,k) + u_new(I-1,j+1,k))*dt*G%IdxBu(I-1,J)
1701 ! elseif (G%mask2dCu(I-1,j) > 0.0) then
1702 ! rx_avg = u_new(I-1,j,k)*dt*G%IdxBu(I-1,J)
1703 ! elseif (G%mask2dCu(I-1,j+1) > 0.0) then
1704 ! rx_avg = u_new(I-1,j+1,k)*dt*G%IdxBu(I-1,J)
1705 ! else
1706 ! rx_avg = 0.0
1707 ! endif
1708  segment%tangential_grad(i,j,k) = ((v_new(i,j,k) - v_new(i-1,j,k))*g%IdxBu(i-1,j) + &
1709  rx_avg*(v_new(i-1,j,k) - v_new(i-2,j,k))*g%IdxBu(i-2,j)) / (1.0+rx_avg)
1710  enddo ; enddo
1711  endif
1712  if (segment%nudged_grad) then
1713  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1714  ! dhdt gets set to 0 on inflow in oblique case
1715  if (rx_tangential(i,j,k) <= 0.0) then
1716  tau = segment%Velocity_nudging_timescale_in
1717  else
1718  tau = segment%Velocity_nudging_timescale_out
1719  endif
1720  gamma_2 = dt / (tau + dt)
1721  segment%tangential_grad(i,j,k) = (1 - gamma_2) * segment%tangential_grad(i,j,k) + &
1722  gamma_2 * segment%nudged_tangential_grad(i,j,k)
1723  enddo ; enddo
1724  endif
1725  deallocate(rx_tangential)
1726  endif
1727  if (segment%oblique_tan .or. segment%oblique_grad) then
1728  i=segment%HI%IsdB
1729  allocate(rx_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
1730  allocate(ry_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
1731  allocate(cff_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
1732  do k=1,nz
1733  rx_tangential(i,segment%HI%JsdB,k) = segment%rx_normal(i,segment%HI%jsd,k)
1734  rx_tangential(i,segment%HI%JedB,k) = segment%rx_normal(i,segment%HI%jed,k)
1735  ry_tangential(i,segment%HI%JsdB,k) = segment%ry_normal(i,segment%HI%jsd,k)
1736  ry_tangential(i,segment%HI%JedB,k) = segment%ry_normal(i,segment%HI%jed,k)
1737  cff_tangential(i,segment%HI%JsdB,k) = segment%cff_normal(i,segment%HI%jsd,k)
1738  cff_tangential(i,segment%HI%JedB,k) = segment%cff_normal(i,segment%HI%jed,k)
1739  do j=segment%HI%JsdB+1,segment%HI%JedB-1
1740  rx_tangential(i,j,k) = 0.5*(segment%rx_normal(i,j,k) + segment%rx_normal(i,j+1,k))
1741  ry_tangential(i,j,k) = 0.5*(segment%ry_normal(i,j,k) + segment%ry_normal(i,j+1,k))
1742  cff_tangential(i,j,k) = 0.5*(segment%cff_normal(i,j,k) + segment%cff_normal(i,j+1,k))
1743  enddo
1744  enddo
1745  if (segment%oblique_tan) then
1746  do k=1,nz ; do j=segment%HI%JsdB+1,segment%HI%JedB-1
1747  rx_avg = rx_tangential(i,j,k)
1748  ry_avg = ry_tangential(i,j,k)
1749  cff_avg = cff_tangential(i,j,k)
1750  segment%tangential_vel(i,j,k) = ((cff_avg*v_new(i,j,k) + rx_avg*v_new(i-1,j,k)) - &
1751  (max(ry_avg,0.0)*segment%grad_tan(j,2,k) + min(ry_avg,0.0)*segment%grad_tan(j+1,2,k))) / &
1752  (cff_avg + rx_avg)
1753  enddo ; enddo
1754  endif
1755  if (segment%nudged_tan) then
1756  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1757  ! dhdt gets set to 0 on inflow in oblique case
1758  if (rx_tangential(i,j,k) <= 0.0) then
1759  tau = segment%Velocity_nudging_timescale_in
1760  else
1761  tau = segment%Velocity_nudging_timescale_out
1762  endif
1763  gamma_2 = dt / (tau + dt)
1764  segment%tangential_vel(i,j,k) = (1 - gamma_2) * segment%tangential_vel(i,j,k) + &
1765  gamma_2 * segment%nudged_tangential_vel(i,j,k)
1766  enddo ; enddo
1767  endif
1768  if (segment%oblique_grad) then
1769  js_obc = max(segment%HI%JsdB,g%jsd+1)
1770  je_obc = min(segment%HI%JedB,g%jed-1)
1771  do k=1,nz ; do j=segment%HI%JsdB+1,segment%HI%JedB-1
1772  rx_avg = rx_tangential(i,j,k)
1773  ry_avg = ry_tangential(i,j,k)
1774  cff_avg = cff_tangential(i,j,k)
1775  segment%tangential_grad(i,j,k) = ((cff_avg*(v_new(i,j,k) - v_new(i-1,j,k))*g%IdxBu(i-1,j) &
1776  + rx_avg*(v_new(i-1,j,k) - v_new(i-2,j,k))*g%IdxBu(i-2,j)) - &
1777  (max(ry_avg,0.0)*segment%grad_gradient(j,2,k) + min(ry_avg,0.0)*segment%grad_gradient(j+1,2,k))) / &
1778  (cff_avg + rx_avg)
1779  enddo ; enddo
1780  endif
1781  if (segment%nudged_grad) then
1782  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1783  ! dhdt gets set to 0 on inflow in oblique case
1784  if (rx_tangential(i,j,k) <= 0.0) then
1785  tau = segment%Velocity_nudging_timescale_in
1786  else
1787  tau = segment%Velocity_nudging_timescale_out
1788  endif
1789  gamma_2 = dt / (tau + dt)
1790  segment%tangential_grad(i,j,k) = (1 - gamma_2) * segment%tangential_grad(i,j,k) + &
1791  gamma_2 * segment%nudged_tangential_grad(i,j,k)
1792  enddo ; enddo
1793  endif
1794  deallocate(rx_tangential)
1795  deallocate(ry_tangential)
1796  deallocate(cff_tangential)
1797  endif
1798  endif
1799 
1800  if (segment%direction == obc_direction_w) then
1801  i=segment%HI%IsdB
1802  if (i>g%HI%IecB) cycle
1803  do k=1,nz ; do j=segment%HI%jsd,segment%HI%jed
1804  if (segment%radiation) then
1805  dhdt = u_old(i+1,j,k)-u_new(i+1,j,k) !old-new
1806  dhdx = u_new(i+1,j,k)-u_new(i+2,j,k) !in new time forward sasha for I+1
1807  rx_new = 0.0
1808  if (dhdt*dhdx > 0.0) rx_new = min( (dhdt/dhdx), rx_max)
1809  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
1810  segment%rx_normal(i,j,k) = rx_avg
1811  ! The new boundary value is interpolated between future interior
1812  ! value, u_new(I+1) and past boundary value but with barotropic
1813  ! accelerations, u_new(I).
1814  segment%normal_vel(i,j,k) = (u_new(i,j,k) + rx_avg*u_new(i+1,j,k)) / (1.0+rx_avg)
1815  ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues
1816  ! implemented as a work-around to limitations in restart capability
1817  obc%rx_normal(i,j,k) = segment%rx_normal(i,j,k)
1818  elseif (segment%oblique) then
1819  dhdt = u_old(i+1,j,k)-u_new(i+1,j,k) !old-new
1820  dhdx = u_new(i+1,j,k)-u_new(i+2,j,k) !in new time forward sasha for I+1
1821  if (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) > 0.0) then
1822  dhdy = segment%grad_normal(j-1,1,k)
1823  elseif (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) == 0.0) then
1824  dhdy = 0.0
1825  else
1826  dhdy = segment%grad_normal(j,1,k)
1827  endif
1828  if (dhdt*dhdx < 0.0) dhdt = 0.0
1829  rx_new = dhdt*dhdx
1830  cff_new = max(dhdx*dhdx + dhdy*dhdy, eps)
1831  ry_new = min(cff,max(dhdt*dhdy,-cff))
1832  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
1833  ry_avg = (1.0-gamma_u)*segment%ry_normal(i,j,k) + gamma_u*ry_new
1834  cff_avg = (1.0-gamma_u)*segment%cff_normal(i,j,k) + gamma_u*cff_new
1835  segment%rx_normal(i,j,k) = rx_avg
1836  segment%ry_normal(i,j,k) = ry_avg
1837  segment%cff_normal(i,j,k) = cff_avg
1838  segment%normal_vel(i,j,k) = ((cff_avg*u_new(i,j,k) + rx_avg*u_new(i+1,j,k)) - &
1839  (max(ry_avg,0.0)*segment%grad_normal(j-1,2,k) + min(ry_avg,0.0)*segment%grad_normal(j,2,k))) / &
1840  (cff_avg + rx_avg)
1841  ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues
1842  ! implemented as a work-around to limitations in restart capability
1843  obc%rx_normal(i,j,k) = segment%rx_normal(i,j,k)
1844  obc%ry_normal(i,j,k) = segment%ry_normal(i,j,k)
1845  obc%cff_normal(i,j,k) = segment%cff_normal(i,j,k)
1846  elseif (segment%gradient) then
1847  segment%normal_vel(i,j,k) = u_new(i+1,j,k)
1848  endif
1849  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1850  ! dhdt gets set to 0. on inflow in oblique case
1851  if (dhdt*dhdx <= 0.0) then
1852  tau = segment%Velocity_nudging_timescale_in
1853  else
1854  tau = segment%Velocity_nudging_timescale_out
1855  endif
1856  gamma_2 = dt / (tau + dt)
1857  segment%normal_vel(i,j,k) = (1 - gamma_2) * segment%normal_vel(i,j,k) + &
1858  gamma_2 * segment%nudged_normal_vel(i,j,k)
1859  endif
1860  enddo ; enddo
1861  if (segment%radiation_tan .or. segment%radiation_grad) then
1862  i=segment%HI%IsdB
1863  allocate(rx_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
1864  do k=1,nz
1865  rx_tangential(i,segment%HI%JsdB,k) = segment%rx_normal(i,segment%HI%jsd,k)
1866  rx_tangential(i,segment%HI%JedB,k) = segment%rx_normal(i,segment%HI%jed,k)
1867  do j=segment%HI%JsdB+1,segment%HI%JedB-1
1868  rx_tangential(i,j,k) = 0.5*(segment%rx_normal(i,j,k) + segment%rx_normal(i,j+1,k))
1869  enddo
1870  enddo
1871  if (segment%radiation_tan) then
1872  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1873  rx_avg = rx_tangential(i,j,k)
1874  segment%tangential_vel(i,j,k) = (v_new(i+1,j,k) + rx_avg*v_new(i+2,j,k)) / (1.0+rx_avg)
1875  enddo ; enddo
1876  endif
1877  if (segment%nudged_tan) then
1878  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1879  ! dhdt gets set to 0 on inflow in oblique case
1880  if (rx_tangential(i,j,k) <= 0.0) then
1881  tau = segment%Velocity_nudging_timescale_in
1882  else
1883  tau = segment%Velocity_nudging_timescale_out
1884  endif
1885  gamma_2 = dt / (tau + dt)
1886  segment%tangential_vel(i,j,k) = (1 - gamma_2) * segment%tangential_vel(i,j,k) + &
1887  gamma_2 * segment%nudged_tangential_vel(i,j,k)
1888  enddo ; enddo
1889  endif
1890  if (segment%radiation_grad) then
1891  js_obc = max(segment%HI%JsdB,g%jsd+1)
1892  je_obc = min(segment%HI%JedB,g%jed-1)
1893  do k=1,nz ; do j=js_obc,je_obc
1894  rx_avg = rx_tangential(i,j,k)
1895 ! if (G%mask2dCu(I+1,j) > 0.0 .and. G%mask2dCu(I+1,j+1) > 0.0) then
1896 ! rx_avg = 0.5*(u_new(I+1,j,k) + u_new(I+1,j+1,k))*dt*G%IdxBu(I+1,J)
1897 ! elseif (G%mask2dCu(I+1,j) > 0.0) then
1898 ! rx_avg = u_new(I+1,j,k)*dt*G%IdxBu(I+1,J)
1899 ! elseif (G%mask2dCu(I+1,j+1) > 0.0) then
1900 ! rx_avg = u_new(I+1,j+1,k)*dt*G%IdxBu(I+1,J)
1901 ! else
1902 ! rx_avg = 0.0
1903 ! endif
1904  segment%tangential_grad(i,j,k) = ((v_new(i+2,j,k) - v_new(i+1,j,k))*g%IdxBu(i+1,j) + &
1905  rx_avg*(v_new(i+3,j,k) - v_new(i+2,j,k))*g%IdxBu(i+2,j)) / (1.0+rx_avg)
1906  enddo ; enddo
1907  endif
1908  if (segment%nudged_grad) then
1909  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1910  ! dhdt gets set to 0 on inflow in oblique case
1911  if (rx_tangential(i,j,k) <= 0.0) then
1912  tau = segment%Velocity_nudging_timescale_in
1913  else
1914  tau = segment%Velocity_nudging_timescale_out
1915  endif
1916  gamma_2 = dt / (tau + dt)
1917  segment%tangential_grad(i,j,k) = (1 - gamma_2) * segment%tangential_grad(i,j,k) + &
1918  gamma_2 * segment%nudged_tangential_grad(i,j,k)
1919  enddo ; enddo
1920  endif
1921  deallocate(rx_tangential)
1922  endif
1923  if (segment%oblique_tan .or. segment%oblique_grad) then
1924  i=segment%HI%IsdB
1925  allocate(rx_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
1926  allocate(ry_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
1927  allocate(cff_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
1928  do k=1,nz
1929  rx_tangential(i,segment%HI%JsdB,k) = segment%rx_normal(i,segment%HI%jsd,k)
1930  rx_tangential(i,segment%HI%JedB,k) = segment%rx_normal(i,segment%HI%jed,k)
1931  ry_tangential(i,segment%HI%JsdB,k) = segment%ry_normal(i,segment%HI%jsd,k)
1932  ry_tangential(i,segment%HI%JedB,k) = segment%ry_normal(i,segment%HI%jed,k)
1933  cff_tangential(i,segment%HI%JsdB,k) = segment%cff_normal(i,segment%HI%jsd,k)
1934  cff_tangential(i,segment%HI%JedB,k) = segment%cff_normal(i,segment%HI%jed,k)
1935  do j=segment%HI%JsdB+1,segment%HI%JedB-1
1936  rx_tangential(i,j,k) = 0.5*(segment%rx_normal(i,j,k) + segment%rx_normal(i,j+1,k))
1937  ry_tangential(i,j,k) = 0.5*(segment%ry_normal(i,j,k) + segment%ry_normal(i,j+1,k))
1938  cff_tangential(i,j,k) = 0.5*(segment%cff_normal(i,j,k) + segment%cff_normal(i,j+1,k))
1939  enddo
1940  enddo
1941  if (segment%oblique_tan) then
1942  do k=1,nz ; do j=segment%HI%JsdB+1,segment%HI%JedB-1
1943  rx_avg = rx_tangential(i,j,k)
1944  ry_avg = ry_tangential(i,j,k)
1945  cff_avg = cff_tangential(i,j,k)
1946  segment%tangential_vel(i,j,k) = ((cff_avg*v_new(i+1,j,k) + rx_avg*v_new(i+2,j,k)) - &
1947  (max(ry_avg,0.0)*segment%grad_tan(j,2,k) + min(ry_avg,0.0)*segment%grad_tan(j+1,2,k))) / &
1948  (cff_avg + rx_avg)
1949  enddo ; enddo
1950  endif
1951  if (segment%nudged_tan) then
1952  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1953  ! dhdt gets set to 0 on inflow in oblique case
1954  if (rx_tangential(i,j,k) <= 0.0) then
1955  tau = segment%Velocity_nudging_timescale_in
1956  else
1957  tau = segment%Velocity_nudging_timescale_out
1958  endif
1959  gamma_2 = dt / (tau + dt)
1960  segment%tangential_vel(i,j,k) = (1 - gamma_2) * segment%tangential_vel(i,j,k) + &
1961  gamma_2 * segment%nudged_tangential_vel(i,j,k)
1962  enddo ; enddo
1963  endif
1964  if (segment%oblique_grad) then
1965  js_obc = max(segment%HI%JsdB,g%jsd+1)
1966  je_obc = min(segment%HI%JedB,g%jed-1)
1967  do k=1,nz ; do j=segment%HI%JsdB+1,segment%HI%JedB-1
1968  rx_avg = rx_tangential(i,j,k)
1969  ry_avg = ry_tangential(i,j,k)
1970  cff_avg = cff_tangential(i,j,k)
1971  segment%tangential_grad(i,j,k) = ((cff_avg*(v_new(i+2,j,k) - v_new(i+1,j,k))*g%IdxBu(i+1,j) &
1972  + rx_avg*(v_new(i+3,j,k) - v_new(i+2,j,k))*g%IdxBu(i+2,j)) - &
1973  (max(ry_avg,0.0)*segment%grad_gradient(j,2,k) + min(ry_avg,0.0)*segment%grad_gradient(j+1,2,k))) / &
1974  (cff_avg + rx_avg)
1975  enddo ; enddo
1976  endif
1977  if (segment%nudged_grad) then
1978  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
1979  ! dhdt gets set to 0 on inflow in oblique case
1980  if (rx_tangential(i,j,k) <= 0.0) then
1981  tau = segment%Velocity_nudging_timescale_in
1982  else
1983  tau = segment%Velocity_nudging_timescale_out
1984  endif
1985  gamma_2 = dt / (tau + dt)
1986  segment%tangential_grad(i,j,k) = (1 - gamma_2) * segment%tangential_grad(i,j,k) + &
1987  gamma_2 * segment%nudged_tangential_grad(i,j,k)
1988  enddo ; enddo
1989  endif
1990  deallocate(rx_tangential)
1991  deallocate(ry_tangential)
1992  deallocate(cff_tangential)
1993  endif
1994  endif
1995 
1996  if (segment%direction == obc_direction_n) then
1997  j=segment%HI%JsdB
1998  if (j<g%HI%JscB) cycle
1999  do k=1,nz ; do i=segment%HI%isd,segment%HI%ied
2000  if (segment%radiation) then
2001  dhdt = v_old(i,j-1,k)-v_new(i,j-1,k) !old-new
2002  dhdy = v_new(i,j-1,k)-v_new(i,j-2,k) !in new time backward sasha for J-1
2003  ry_new = 0.0
2004  if (dhdt*dhdy > 0.0) ry_new = min( (dhdt/dhdy), ry_max)
2005  ry_avg = (1.0-gamma_v)*segment%ry_normal(i,j,k) + gamma_v*ry_new
2006  segment%ry_normal(i,j,k) = ry_avg
2007  ! The new boundary value is interpolated between future interior
2008  ! value, v_new(J-1) and past boundary value but with barotropic
2009  ! accelerations, v_new(J).
2010  segment%normal_vel(i,j,k) = (v_new(i,j,k) + ry_avg*v_new(i,j-1,k)) / (1.0+ry_avg)
2011  ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues
2012  ! implemented as a work-around to limitations in restart capability
2013  obc%ry_normal(i,j,k) = segment%ry_normal(i,j,k)
2014  elseif (segment%oblique) then
2015  dhdt = v_old(i,j-1,k)-v_new(i,j-1,k) !old-new
2016  dhdy = v_new(i,j-1,k)-v_new(i,j-2,k) !in new time backward sasha for J-1
2017  segment%ry_normal(i,j,k) = ry_avg
2018  if (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) > 0.0) then
2019  dhdx = segment%grad_normal(i-1,1,k)
2020  elseif (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) == 0.0) then
2021  dhdx = 0.0
2022  else
2023  dhdx = segment%grad_normal(i,1,k)
2024  endif
2025  if (dhdt*dhdy < 0.0) dhdt = 0.0
2026  ry_new = dhdt*dhdy
2027  cff_new = max(dhdx*dhdx + dhdy*dhdy, eps)
2028  rx_new = min(cff,max(dhdt*dhdx,-cff))
2029  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
2030  ry_avg = (1.0-gamma_u)*segment%ry_normal(i,j,k) + gamma_u*ry_new
2031  cff_avg = (1.0-gamma_u)*segment%cff_normal(i,j,k) + gamma_u*cff_new
2032  segment%rx_normal(i,j,k) = rx_avg
2033  segment%ry_normal(i,j,k) = ry_avg
2034  segment%cff_normal(i,j,k) = cff_avg
2035  segment%normal_vel(i,j,k) = ((cff_avg*v_new(i,j,k) + ry_avg*v_new(i,j-1,k)) - &
2036  (max(rx_avg,0.0)*segment%grad_normal(i-1,2,k) + min(rx_avg,0.0)*segment%grad_normal(i,2,k))) / &
2037  (cff_avg + ry_avg)
2038  ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues
2039  ! implemented as a work-around to limitations in restart capability
2040  obc%rx_normal(i,j,k) = segment%rx_normal(i,j,k)
2041  obc%ry_normal(i,j,k) = segment%ry_normal(i,j,k)
2042  obc%cff_normal(i,j,k) = segment%cff_normal(i,j,k)
2043  elseif (segment%gradient) then
2044  segment%normal_vel(i,j,k) = v_new(i,j-1,k)
2045  endif
2046  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
2047  ! dhdt gets set to 0 on inflow in oblique case
2048  if (dhdt*dhdy <= 0.0) then
2049  tau = segment%Velocity_nudging_timescale_in
2050  else
2051  tau = segment%Velocity_nudging_timescale_out
2052  endif
2053  gamma_2 = dt / (tau + dt)
2054  segment%normal_vel(i,j,k) = (1 - gamma_2) * segment%normal_vel(i,j,k) + &
2055  gamma_2 * segment%nudged_normal_vel(i,j,k)
2056  endif
2057  enddo ; enddo
2058  if (segment%radiation_tan .or. segment%radiation_grad) then
2059  j=segment%HI%JsdB
2060  allocate(rx_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
2061  do k=1,nz
2062  rx_tangential(segment%HI%IsdB,j,k) = segment%ry_normal(segment%HI%isd,j,k)
2063  rx_tangential(segment%HI%IedB,j,k) = segment%ry_normal(segment%HI%ied,j,k)
2064  do i=segment%HI%IsdB+1,segment%HI%IedB-1
2065  rx_tangential(i,j,k) = 0.5*(segment%ry_normal(i,j,k) + segment%ry_normal(i+1,j,k))
2066  enddo
2067  enddo
2068  if (segment%radiation_tan) then
2069  do k=1,nz ; do i=segment%HI%IsdB,segment%HI%IedB
2070  rx_avg = rx_tangential(i,j,k)
2071  segment%tangential_vel(i,j,k) = (u_new(i,j,k) + rx_avg*u_new(i,j-1,k)) / (1.0+rx_avg)
2072  enddo ; enddo
2073  endif
2074  if (segment%nudged_tan) then
2075  do k=1,nz ; do i=segment%HI%IsdB,segment%HI%IedB
2076  ! dhdt gets set to 0 on inflow in oblique case
2077  if (rx_tangential(i,j,k) <= 0.0) then
2078  tau = segment%Velocity_nudging_timescale_in
2079  else
2080  tau = segment%Velocity_nudging_timescale_out
2081  endif
2082  gamma_2 = dt / (tau + dt)
2083  segment%tangential_vel(i,j,k) = (1 - gamma_2) * segment%tangential_vel(i,j,k) + &
2084  gamma_2 * segment%nudged_tangential_vel(i,j,k)
2085  enddo ; enddo
2086  endif
2087  if (segment%radiation_grad) then
2088  is_obc = max(segment%HI%IsdB,g%isd+1)
2089  ie_obc = min(segment%HI%IedB,g%ied-1)
2090  do k=1,nz ; do i=is_obc,ie_obc
2091  rx_avg = rx_tangential(i,j,k)
2092 ! if (G%mask2dCv(i,J-1) > 0.0 .and. G%mask2dCv(i+1,J-1) > 0.0) then
2093 ! rx_avg = 0.5*(v_new(i,J-1,k) + v_new(i+1,J-1,k)*dt*G%IdyBu(I,J-1))
2094 ! elseif (G%mask2dCv(i,J-1) > 0.0) then
2095 ! rx_avg = v_new(i,J-1,k)*dt*G%IdyBu(I,J-1)
2096 ! elseif (G%mask2dCv(i+1,J-1) > 0.0) then
2097 ! rx_avg = v_new(i+1,J-1,k)*dt*G%IdyBu(I,J-1)
2098 ! else
2099 ! rx_avg = 0.0
2100 ! endif
2101  segment%tangential_grad(i,j,k) = ((u_new(i,j,k) - u_new(i,j-1,k))*g%IdyBu(i,j-1) + &
2102  rx_avg*(u_new(i,j-1,k) - u_new(i,j-2,k))*g%IdyBu(i,j-2)) / (1.0+rx_avg)
2103  enddo ; enddo
2104  endif
2105  if (segment%nudged_grad) then
2106  do k=1,nz ; do i=segment%HI%IsdB,segment%HI%IedB
2107  ! dhdt gets set to 0 on inflow in oblique case
2108  if (ry_tangential(i,j,k) <= 0.0) then
2109  tau = segment%Velocity_nudging_timescale_in
2110  else
2111  tau = segment%Velocity_nudging_timescale_out
2112  endif
2113  gamma_2 = dt / (tau + dt)
2114  segment%tangential_grad(i,j,k) = (1 - gamma_2) * segment%tangential_grad(i,j,k) + &
2115  gamma_2 * segment%nudged_tangential_grad(i,j,k)
2116  enddo ; enddo
2117  endif
2118  deallocate(rx_tangential)
2119  endif
2120  if (segment%oblique_tan .or. segment%oblique_grad) then
2121  j=segment%HI%JsdB
2122  allocate(rx_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
2123  allocate(ry_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
2124  allocate(cff_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
2125  do k=1,nz
2126  rx_tangential(segment%HI%IsdB,j,k) = segment%ry_normal(segment%HI%isd,j,k)
2127  rx_tangential(segment%HI%IedB,j,k) = segment%ry_normal(segment%HI%ied,j,k)
2128  ry_tangential(segment%HI%IsdB,j,k) = segment%rx_normal(segment%HI%isd,j,k)
2129  ry_tangential(segment%HI%IedB,j,k) = segment%rx_normal(segment%HI%ied,j,k)
2130  cff_tangential(segment%HI%IsdB,j,k) = segment%cff_normal(segment%HI%isd,j,k)
2131  cff_tangential(segment%HI%IedB,j,k) = segment%cff_normal(segment%HI%ied,j,k)
2132  do i=segment%HI%IsdB+1,segment%HI%IedB-1
2133  rx_tangential(i,j,k) = 0.5*(segment%ry_normal(i,j,k) + segment%ry_normal(i+1,j,k))
2134  ry_tangential(i,j,k) = 0.5*(segment%rx_normal(i,j,k) + segment%rx_normal(i+1,j,k))
2135  cff_tangential(i,j,k) = 0.5*(segment%cff_normal(i,j,k) + segment%cff_normal(i+1,j,k))
2136  enddo
2137  enddo
2138  if (segment%oblique_tan) then
2139  do k=1,nz ; do i=segment%HI%IsdB+1,segment%HI%IedB-1
2140  rx_avg = rx_tangential(i,j,k)
2141  ry_avg = ry_tangential(i,j,k)
2142  cff_avg = cff_tangential(i,j,k)
2143  segment%tangential_vel(i,j,k) = ((cff_avg*u_new(i,j,k) + rx_avg*u_new(i,j-1,k)) - &
2144  (max(ry_avg,0.0)*segment%grad_tan(i,2,k) + min(ry_avg,0.0)*segment%grad_tan(i+1,2,k))) / &
2145  (cff_avg + rx_avg)
2146  enddo ; enddo
2147  endif
2148  if (segment%nudged_tan) then
2149  do k=1,nz ; do i=segment%HI%IsdB,segment%HI%IedB
2150  ! dhdt gets set to 0 on inflow in oblique case
2151  if (ry_tangential(i,j,k) <= 0.0) then
2152  tau = segment%Velocity_nudging_timescale_in
2153  else
2154  tau = segment%Velocity_nudging_timescale_out
2155  endif
2156  gamma_2 = dt / (tau + dt)
2157  segment%tangential_vel(i,j,k) = (1 - gamma_2) * segment%tangential_vel(i,j,k) + &
2158  gamma_2 * segment%nudged_tangential_vel(i,j,k)
2159  enddo ; enddo
2160  endif
2161  if (segment%oblique_grad) then
2162  is_obc = max(segment%HI%IsdB,g%isd+1)
2163  ie_obc = min(segment%HI%IedB,g%ied-1)
2164  do k=1,nz ; do i=segment%HI%IsdB+1,segment%HI%IedB-1
2165  rx_avg = rx_tangential(i,j,k)
2166  ry_avg = ry_tangential(i,j,k)
2167  cff_avg = cff_tangential(i,j,k)
2168  segment%tangential_grad(i,j,k) = ((cff_avg*(u_new(i,j,k) - u_new(i,j-1,k))*g%IdyBu(i,j-1) &
2169  + rx_avg*(u_new(i,j-1,k) - u_new(i,j-2,k))*g%IdyBu(i,j-2)) - &
2170  (max(ry_avg,0.0)*segment%grad_gradient(i,2,k) + min(ry_avg,0.0)*segment%grad_gradient(i+1,2,k))) / &
2171  (cff_avg + rx_avg)
2172  enddo ; enddo
2173  endif
2174  if (segment%nudged_grad) then
2175  do k=1,nz ; do i=segment%HI%IsdB,segment%HI%IedB
2176  ! dhdt gets set to 0 on inflow in oblique case
2177  if (ry_tangential(i,j,k) <= 0.0) then
2178  tau = segment%Velocity_nudging_timescale_in
2179  else
2180  tau = segment%Velocity_nudging_timescale_out
2181  endif
2182  gamma_2 = dt / (tau + dt)
2183  segment%tangential_grad(i,j,k) = (1 - gamma_2) * segment%tangential_grad(i,j,k) + &
2184  gamma_2 * segment%nudged_tangential_grad(i,j,k)
2185  enddo ; enddo
2186  endif
2187  deallocate(rx_tangential)
2188  deallocate(ry_tangential)
2189  deallocate(cff_tangential)
2190  endif
2191  endif
2192 
2193  if (segment%direction == obc_direction_s) then
2194  j=segment%HI%JsdB
2195  if (j>g%HI%JecB) cycle
2196  do k=1,nz ; do i=segment%HI%isd,segment%HI%ied
2197  if (segment%radiation) then
2198  dhdt = v_old(i,j+1,k)-v_new(i,j+1,k) !old-new
2199  dhdy = v_new(i,j+1,k)-v_new(i,j+2,k) !in new time backward sasha for J-1
2200  ry_new = 0.0
2201  if (dhdt*dhdy > 0.0) ry_new = min( (dhdt/dhdy), ry_max)
2202  ry_avg = (1.0-gamma_v)*segment%ry_normal(i,j,k) + gamma_v*ry_new
2203  segment%ry_normal(i,j,k) = ry_avg
2204  ! The new boundary value is interpolated between future interior
2205  ! value, v_new(J+1) and past boundary value but with barotropic
2206  ! accelerations, v_new(J).
2207  segment%normal_vel(i,j,k) = (v_new(i,j,k) + ry_avg*v_new(i,j+1,k)) / (1.0+ry_avg)
2208  ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues
2209  ! implemented as a work-around to limitations in restart capability
2210  obc%ry_normal(i,j,k) = segment%ry_normal(i,j,k)
2211  elseif (segment%oblique) then
2212  dhdt = v_old(i,j+1,k)-v_new(i,j+1,k) !old-new
2213  dhdy = v_new(i,j+1,k)-v_new(i,j+2,k) !in new time backward sasha for J-1
2214  if (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) > 0.0) then
2215  dhdx = segment%grad_normal(i-1,1,k)
2216  elseif (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) == 0.0) then
2217  dhdx = 0.0
2218  else
2219  dhdx = segment%grad_normal(i,1,k)
2220  endif
2221  if (dhdt*dhdy < 0.0) dhdt = 0.0
2222  ry_new = dhdt*dhdy
2223  cff_new = max(dhdx*dhdx + dhdy*dhdy, eps)
2224  rx_new = min(cff,max(dhdt*dhdx,-cff))
2225  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
2226  ry_avg = (1.0-gamma_u)*segment%ry_normal(i,j,k) + gamma_u*ry_new
2227  cff_avg = (1.0-gamma_u)*segment%cff_normal(i,j,k) + gamma_u*cff_new
2228  segment%rx_normal(i,j,k) = rx_avg
2229  segment%ry_normal(i,j,k) = ry_avg
2230  segment%cff_normal(i,j,k) = cff_avg
2231  segment%normal_vel(i,j,k) = ((cff_avg*v_new(i,j,k) + ry_avg*v_new(i,j+1,k)) - &
2232  (max(rx_avg,0.0)*segment%grad_normal(i-1,2,k) + min(rx_avg,0.0)*segment%grad_normal(i,2,k))) / &
2233  (cff_avg + ry_avg)
2234  ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues
2235  ! implemented as a work-around to limitations in restart capability
2236  obc%rx_normal(i,j,k) = segment%rx_normal(i,j,k)
2237  obc%ry_normal(i,j,k) = segment%ry_normal(i,j,k)
2238  obc%cff_normal(i,j,k) = segment%cff_normal(i,j,k)
2239  elseif (segment%gradient) then
2240  segment%normal_vel(i,j,k) = v_new(i,j+1,k)
2241  endif
2242  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
2243  ! dhdt gets set to 0 on inflow in oblique case
2244  if (dhdt*dhdy <= 0.0) then
2245  tau = segment%Velocity_nudging_timescale_in
2246  else
2247  tau = segment%Velocity_nudging_timescale_out
2248  endif
2249  gamma_2 = dt / (tau + dt)
2250  segment%normal_vel(i,j,k) = (1 - gamma_2) * segment%normal_vel(i,j,k) + &
2251  gamma_2 * segment%nudged_normal_vel(i,j,k)
2252  endif
2253  enddo ; enddo
2254  if (segment%radiation_tan .or. segment%radiation_grad) then
2255  j=segment%HI%JsdB
2256  allocate(rx_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
2257  do k=1,nz
2258  rx_tangential(segment%HI%IsdB,j,k) = segment%ry_normal(segment%HI%isd,j,k)
2259  rx_tangential(segment%HI%IedB,j,k) = segment%ry_normal(segment%HI%ied,j,k)
2260  do i=segment%HI%IsdB+1,segment%HI%IedB-1
2261  rx_tangential(i,j,k) = 0.5*(segment%ry_normal(i,j,k) + segment%ry_normal(i+1,j,k))
2262  enddo
2263  enddo
2264  if (segment%radiation_tan) then
2265  do k=1,nz ; do i=segment%HI%IsdB,segment%HI%IedB
2266  rx_avg = rx_tangential(i,j,k)
2267  segment%tangential_vel(i,j,k) = (u_new(i,j+1,k) + rx_avg*u_new(i,j+2,k)) / (1.0+rx_avg)
2268  enddo ; enddo
2269  endif
2270  if (segment%nudged_tan) then
2271  do k=1,nz ; do i=segment%HI%IsdB,segment%HI%IedB
2272  ! dhdt gets set to 0 on inflow in oblique case
2273  if (rx_tangential(i,j,k) <= 0.0) then
2274  tau = segment%Velocity_nudging_timescale_in
2275  else
2276  tau = segment%Velocity_nudging_timescale_out
2277  endif
2278  gamma_2 = dt / (tau + dt)
2279  segment%tangential_vel(i,j,k) = (1 - gamma_2) * segment%tangential_vel(i,j,k) + &
2280  gamma_2 * segment%nudged_tangential_vel(i,j,k)
2281  enddo ; enddo
2282  endif
2283  if (segment%radiation_grad) then
2284  is_obc = max(segment%HI%IsdB,g%isd+1)
2285  ie_obc = min(segment%HI%IedB,g%ied-1)
2286  do k=1,nz ; do i=is_obc,ie_obc
2287  rx_avg = rx_tangential(i,j,k)
2288 ! if (G%mask2dCv(i,J+1) > 0.0 .and. G%mask2dCv(i+1,J+1) > 0.0) then
2289 ! rx_avg = 0.5*(v_new(i,J+1,k) + v_new(i+1,J+1,k))*dt*G%IdyBu(I,J+1)
2290 ! elseif (G%mask2dCv(i,J+1) > 0.0) then
2291 ! rx_avg = v_new(i,J+1,k)*dt*G%IdyBu(I,J+1)
2292 ! elseif (G%mask2dCv(i+1,J+1) > 0.0) then
2293 ! rx_avg = v_new(i+1,J+1,k)*dt*G%IdyBu(I,J+1)
2294 ! else
2295 ! rx_avg = 0.0
2296 ! endif
2297  segment%tangential_grad(i,j,k) = ((u_new(i,j+2,k) - u_new(i,j+1,k))*g%IdyBu(i,j+1) + &
2298  rx_avg*(u_new(i,j+3,k) - u_new(i,j+2,k))*g%IdyBu(i,j+2)) / (1.0+rx_avg)
2299  enddo ; enddo
2300  endif
2301  if (segment%nudged_grad) then
2302  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
2303  ! dhdt gets set to 0 on inflow in oblique case
2304  if (ry_tangential(i,j,k) <= 0.0) then
2305  tau = segment%Velocity_nudging_timescale_in
2306  else
2307  tau = segment%Velocity_nudging_timescale_out
2308  endif
2309  gamma_2 = dt / (tau + dt)
2310  segment%tangential_grad(i,j,k) = (1 - gamma_2) * segment%tangential_grad(i,j,k) + &
2311  gamma_2 * segment%nudged_tangential_grad(i,j,k)
2312  enddo ; enddo
2313  endif
2314  deallocate(rx_tangential)
2315  endif
2316  if (segment%oblique_tan .or. segment%oblique_grad) then
2317  j=segment%HI%JsdB
2318  allocate(rx_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
2319  allocate(ry_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
2320  allocate(cff_tangential(segment%HI%IsdB:segment%HI%IedB,segment%HI%JsdB:segment%HI%JedB,nz))
2321  do k=1,nz
2322  rx_tangential(segment%HI%IsdB,j,k) = segment%ry_normal(segment%HI%isd,j,k)
2323  rx_tangential(segment%HI%IedB,j,k) = segment%ry_normal(segment%HI%ied,j,k)
2324  ry_tangential(segment%HI%IsdB,j,k) = segment%rx_normal(segment%HI%isd,j,k)
2325  ry_tangential(segment%HI%IedB,j,k) = segment%rx_normal(segment%HI%ied,j,k)
2326  cff_tangential(segment%HI%IsdB,j,k) = segment%cff_normal(segment%HI%isd,j,k)
2327  cff_tangential(segment%HI%IedB,j,k) = segment%cff_normal(segment%HI%ied,j,k)
2328  do i=segment%HI%IsdB+1,segment%HI%IedB-1
2329  rx_tangential(i,j,k) = 0.5*(segment%ry_normal(i,j,k) + segment%ry_normal(i+1,j,k))
2330  ry_tangential(i,j,k) = 0.5*(segment%rx_normal(i,j,k) + segment%rx_normal(i+1,j,k))
2331  cff_tangential(i,j,k) = 0.5*(segment%cff_normal(i,j,k) + segment%cff_normal(i+1,j,k))
2332  enddo
2333  enddo
2334  if (segment%oblique_tan) then
2335  do k=1,nz ; do i=segment%HI%IsdB+1,segment%HI%IedB-1
2336  rx_avg = rx_tangential(i,j,k)
2337  ry_avg = ry_tangential(i,j,k)
2338  cff_avg = cff_tangential(i,j,k)
2339  segment%tangential_vel(i,j,k) = ((cff_avg*u_new(i,j+1,k) + rx_avg*u_new(i,j+2,k)) - &
2340  (max(ry_avg,0.0)*segment%grad_tan(i,2,k) + min(ry_avg,0.0)*segment%grad_tan(i+1,2,k))) / &
2341  (cff_avg + rx_avg)
2342  enddo ; enddo
2343  endif
2344  if (segment%nudged_tan) then
2345  do k=1,nz ; do i=segment%HI%IsdB,segment%HI%IedB
2346  ! dhdt gets set to 0 on inflow in oblique case
2347  if (ry_tangential(i,j,k) <= 0.0) then
2348  tau = segment%Velocity_nudging_timescale_in
2349  else
2350  tau = segment%Velocity_nudging_timescale_out
2351  endif
2352  gamma_2 = dt / (tau + dt)
2353  segment%tangential_vel(i,j,k) = (1 - gamma_2) * segment%tangential_vel(i,j,k) + &
2354  gamma_2 * segment%nudged_tangential_vel(i,j,k)
2355  enddo ; enddo
2356  endif
2357  if (segment%oblique_grad) then
2358  is_obc = max(segment%HI%IsdB,g%isd+1)
2359  ie_obc = min(segment%HI%IedB,g%ied-1)
2360  do k=1,nz ; do i=segment%HI%IsdB+1,segment%HI%IedB-1
2361  rx_avg = rx_tangential(i,j,k)
2362  ry_avg = ry_tangential(i,j,k)
2363  cff_avg = cff_tangential(i,j,k)
2364  segment%tangential_grad(i,j,k) = ((cff_avg*(u_new(i,j+2,k) - u_new(i,j+1,k))*g%IdyBu(i,j+1) &
2365  + rx_avg*(u_new(i,j+3,k) - u_new(i,j+2,k))*g%IdyBu(i,j+2)) - &
2366  (max(ry_avg,0.0)*segment%grad_gradient(i,2,k) + min(ry_avg,0.0)*segment%grad_gradient(i+1,2,k))) / &
2367  (cff_avg + rx_avg)
2368  enddo ; enddo
2369  endif
2370  if (segment%nudged_grad) then
2371  do k=1,nz ; do j=segment%HI%JsdB,segment%HI%JedB
2372  ! dhdt gets set to 0 on inflow in oblique case
2373  if (ry_tangential(i,j,k) <= 0.0) then
2374  tau = segment%Velocity_nudging_timescale_in
2375  else
2376  tau = segment%Velocity_nudging_timescale_out
2377  endif
2378  gamma_2 = dt / (tau + dt)
2379  segment%tangential_grad(i,j,k) = (1 - gamma_2) * segment%tangential_grad(i,j,k) + &
2380  gamma_2 * segment%nudged_tangential_grad(i,j,k)
2381  enddo ; enddo
2382  endif
2383  deallocate(rx_tangential)
2384  deallocate(ry_tangential)
2385  deallocate(cff_tangential)
2386  endif
2387  endif
2388  enddo
2389 
2390  ! Actually update u_new, v_new
2391  call open_boundary_apply_normal_flow(obc, g, u_new, v_new)
2392 
2393  call pass_vector(u_new, v_new, g%Domain, clock=id_clock_pass)
2394 
2395 end subroutine radiation_open_bdry_conds
2396 
2397 !> Applies OBC values stored in segments to 3d u,v fields
2398 subroutine open_boundary_apply_normal_flow(OBC, G, u, v)
2399  ! Arguments
2400  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
2401  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
2402  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: u !< u field to update on open boundaries
2403  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: v !< v field to update on open boundaries
2404  ! Local variables
2405  integer :: i, j, k, n
2406  type(obc_segment_type), pointer :: segment => null()
2407 
2408  if (.not.associated(obc)) return ! Bail out if OBC is not available
2409 
2410  do n=1,obc%number_of_segments
2411  segment => obc%segment(n)
2412  if (.not. segment%on_pe) then
2413  cycle
2414  elseif (segment%radiation .or. segment%oblique .or. segment%gradient) then
2415  if (segment%is_E_or_W) then
2416  i=segment%HI%IsdB
2417  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
2418  u(i,j,k) = segment%normal_vel(i,j,k)
2419  enddo ; enddo
2420  elseif (segment%is_N_or_S) then
2421  j=segment%HI%JsdB
2422  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
2423  v(i,j,k) = segment%normal_vel(i,j,k)
2424  enddo ; enddo
2425  endif
2426  endif
2427  enddo
2428 
2429 end subroutine open_boundary_apply_normal_flow
2430 
2431 !> Applies zero values to 3d u,v fields on OBC segments
2432 subroutine open_boundary_zero_normal_flow(OBC, G, u, v)
2433  ! Arguments
2434  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
2435  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
2436  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: u !< u field to update on open boundaries
2437  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: v !< v field to update on open boundaries
2438  ! Local variables
2439  integer :: i, j, k, n
2440  type(obc_segment_type), pointer :: segment => null()
2441 
2442  if (.not.associated(obc)) return ! Bail out if OBC is not available
2443 
2444  do n=1,obc%number_of_segments
2445  segment => obc%segment(n)
2446  if (.not. segment%on_pe) then
2447  cycle
2448  elseif (segment%is_E_or_W) then
2449  i=segment%HI%IsdB
2450  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
2451  u(i,j,k) = 0.
2452  enddo ; enddo
2453  elseif (segment%is_N_or_S) then
2454  j=segment%HI%JsdB
2455  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
2456  v(i,j,k) = 0.
2457  enddo ; enddo
2458  endif
2459  enddo
2460 
2461 end subroutine open_boundary_zero_normal_flow
2462 
2463 !> Calculate the tangential gradient of the normal flow at the boundary q-points.
2464 subroutine gradient_at_q_points(G, segment, uvel, vvel)
2465  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
2466  type(obc_segment_type), pointer :: segment !< OBC segment structure
2467  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: uvel !< zonal velocity
2468  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: vvel !< meridional velocity
2469  integer :: i,j,k
2470 
2471  if (.not. segment%on_pe) return
2472 
2473  if (segment%is_E_or_W) then
2474  if (segment%direction == obc_direction_e) then
2475  i=segment%HI%isdB
2476  do k=1,g%ke
2477  do j=max(segment%HI%JsdB, g%HI%JsdB+1),min(segment%HI%JedB, g%HI%JedB-1)
2478  segment%grad_normal(j,1,k) = (uvel(i-1,j+1,k)-uvel(i-1,j,k)) * g%mask2dBu(i-1,j)
2479  segment%grad_normal(j,2,k) = (uvel(i,j+1,k)-uvel(i,j,k)) * g%mask2dBu(i,j)
2480  enddo
2481  enddo
2482  if (segment%oblique_tan) then
2483  do k=1,g%ke
2484  do j=max(segment%HI%jsd, g%HI%jsd+1),min(segment%HI%jed, g%HI%jed-1)
2485  segment%grad_tan(j,1,k) = (vvel(i-1,j,k)-vvel(i-1,j-1,k)) * g%mask2dT(i-1,j)
2486  segment%grad_tan(j,2,k) = (vvel(i,j,k)-vvel(i,j-1,k)) * g%mask2dT(i,j)
2487  enddo
2488  enddo
2489  endif
2490  if (segment%oblique_grad) then
2491  do k=1,g%ke
2492  do j=max(segment%HI%jsd, g%HI%jsd+1),min(segment%HI%jed, g%HI%jed-1)
2493  segment%grad_gradient(j,1,k) = (((vvel(i-1,j,k) - vvel(i-2,j,k))*g%IdxBu(i-2,j)) - &
2494  (vvel(i-1,j-1,k) - vvel(i-2,j-1,k))*g%IdxBu(i-2,j-1)) * g%mask2dCu(i-1,j)
2495  segment%grad_gradient(j,2,k) = (((vvel(i,j,k) - vvel(i-1,j,k))*g%IdxBu(i-1,j)) - &
2496  (vvel(i,j-1,k) - vvel(i-1,j-1,k))*g%IdxBu(i-1,j-1)) * g%mask2dCu(i,j)
2497  enddo
2498  enddo
2499  endif
2500  else ! western segment
2501  i=segment%HI%isdB
2502  do k=1,g%ke
2503  do j=max(segment%HI%JsdB, g%HI%JsdB+1),min(segment%HI%JedB, g%HI%JedB-1)
2504  segment%grad_normal(j,1,k) = (uvel(i+1,j+1,k)-uvel(i+1,j,k)) * g%mask2dBu(i+1,j)
2505  segment%grad_normal(j,2,k) = (uvel(i,j+1,k)-uvel(i,j,k)) * g%mask2dBu(i,j)
2506  enddo
2507  enddo
2508  if (segment%oblique_tan) then
2509  do k=1,g%ke
2510  do j=max(segment%HI%jsd, g%HI%jsd+1),min(segment%HI%jed, g%HI%jed-1)
2511  segment%grad_tan(j,1,k) = (vvel(i+2,j,k)-vvel(i+2,j-1,k)) * g%mask2dT(i+2,j)
2512  segment%grad_tan(j,2,k) = (vvel(i+1,j,k)-vvel(i+1,j-1,k)) * g%mask2dT(i+1,j)
2513  enddo
2514  enddo
2515  endif
2516  if (segment%oblique_grad) then
2517  do k=1,g%ke
2518  do j=max(segment%HI%jsd, g%HI%jsd+1),min(segment%HI%jed, g%HI%jed-1)
2519  segment%grad_gradient(j,1,k) = (((vvel(i+3,j,k) - vvel(i+2,j,k))*g%IdxBu(i+2,j)) - &
2520  (vvel(i+3,j-1,k) - vvel(i+2,j-1,k))*g%IdxBu(i+2,j-1)) * g%mask2dCu(i+2,j)
2521  segment%grad_gradient(j,2,k) = (((vvel(i+2,j,k) - vvel(i+1,j,k))*g%IdxBu(i+1,j)) - &
2522  (vvel(i+2,j-1,k) - vvel(i+1,j-1,k))*g%IdxBu(i+1,j-1)) * g%mask2dCu(i+1,j)
2523  enddo
2524  enddo
2525  endif
2526  endif
2527  elseif (segment%is_N_or_S) then
2528  if (segment%direction == obc_direction_n) then
2529  j=segment%HI%jsdB
2530  do k=1,g%ke
2531  do i=max(segment%HI%IsdB, g%HI%IsdB+1),min(segment%HI%IedB, g%HI%IedB-1)
2532  segment%grad_normal(i,1,k) = (vvel(i+1,j-1,k)-vvel(i,j-1,k)) * g%mask2dBu(i,j-1)
2533  segment%grad_normal(i,2,k) = (vvel(i+1,j,k)-vvel(i,j,k)) * g%mask2dBu(i,j)
2534  enddo
2535  enddo
2536  if (segment%oblique_tan) then
2537  do k=1,g%ke
2538  do i=max(segment%HI%isd, g%HI%isd+1),min(segment%HI%ied, g%HI%ied-1)
2539  segment%grad_tan(i,1,k) = (uvel(i,j-1,k)-uvel(i-1,j-1,k)) * g%mask2dT(i,j-1)
2540  segment%grad_tan(i,2,k) = (uvel(i,j,k)-uvel(i-1,j,k)) * g%mask2dT(i,j)
2541  enddo
2542  enddo
2543  endif
2544  if (segment%oblique_grad) then
2545  do k=1,g%ke
2546  do i=max(segment%HI%isd, g%HI%isd+1),min(segment%HI%ied, g%HI%ied-1)
2547  segment%grad_gradient(i,1,k) = (((uvel(i,j-1,k) - uvel(i,j-2,k))*g%IdxBu(i,j-2)) - &
2548  (uvel(i-1,j-1,k) - uvel(i-1,j-2,k))*g%IdxBu(i-1,j-2)) * g%mask2dCv(i,j-1)
2549  segment%grad_gradient(i,2,k) = (((uvel(i,j,k) - uvel(i,j-1,k))*g%IdyBu(i,j-1)) - &
2550  (uvel(i-1,j,k) - uvel(i-1,j-1,k))*g%IdyBu(i-1,j-1)) * g%mask2dCv(i,j)
2551  enddo
2552  enddo
2553  endif
2554  else ! south segment
2555  j=segment%HI%jsdB
2556  do k=1,g%ke
2557  do i=max(segment%HI%IsdB, g%HI%IsdB+1),min(segment%HI%IedB, g%HI%IedB-1)
2558  segment%grad_normal(i,1,k) = (vvel(i+1,j+1,k)-vvel(i,j+1,k)) * g%mask2dBu(i,j+1)
2559  segment%grad_normal(i,2,k) = (vvel(i+1,j,k)-vvel(i,j,k)) * g%mask2dBu(i,j)
2560  enddo
2561  enddo
2562  if (segment%oblique_tan) then
2563  do k=1,g%ke
2564  do i=max(segment%HI%isd, g%HI%isd+1),min(segment%HI%ied, g%HI%ied-1)
2565  segment%grad_tan(i,1,k) = (uvel(i,j+2,k)-uvel(i-1,j+2,k)) * g%mask2dT(i,j+2)
2566  segment%grad_tan(i,2,k) = (uvel(i,j+1,k)-uvel(i-1,j+1,k)) * g%mask2dT(i,j+1)
2567  enddo
2568  enddo
2569  endif
2570  if (segment%oblique_grad) then
2571  do k=1,g%ke
2572  do i=max(segment%HI%isd, g%HI%isd+1),min(segment%HI%ied, g%HI%ied-1)
2573  segment%grad_gradient(i,1,k) = (((uvel(i,j+3,k) - uvel(i,j+2,k))*g%IdxBu(i,j+2)) - &
2574  (uvel(i-1,j+3,k) - uvel(i-1,j+2,k))*g%IdyBu(i-1,j+2)) * g%mask2dCv(i,j+2)
2575  segment%grad_gradient(i,2,k) = (((uvel(i,j+2,k) - uvel(i,j+1,k))*g%IdxBu(i,j+1)) - &
2576  (uvel(i-1,j+2,k) - uvel(i-1,j+1,k))*g%IdyBu(i-1,j+1)) * g%mask2dCv(i,j+1)
2577  enddo
2578  enddo
2579  endif
2580  endif
2581  endif
2582 
2583 end subroutine gradient_at_q_points
2584 
2585 
2586 !> Sets the initial values of the tracer open boundary conditions.
2587 !! Redoing this elsewhere.
2588 subroutine set_tracer_data(OBC, tv, h, G, PF, tracer_Reg)
2589  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
2590  type(ocean_obc_type), pointer :: obc !< Open boundary structure
2591  type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamics structure
2592  real, dimension(SZI_(G),SZJ_(G), SZK_(G)), intent(inout) :: h !< Thickness
2593  type(param_file_type), intent(in) :: pf !< Parameter file handle
2594  type(tracer_registry_type), pointer :: tracer_reg !< Tracer registry
2595  ! Local variables
2596  integer :: i, j, k, itt, is, ie, js, je, isd, ied, jsd, jed, nz, n
2597  integer :: isd_off, jsd_off
2598  integer :: isdb, iedb, jsdb, jedb
2599  type(obc_segment_type), pointer :: segment => null() ! pointer to segment type list
2600  character(len=40) :: mdl = "set_tracer_data" ! This subroutine's name.
2601  character(len=200) :: filename, obc_file, inputdir ! Strings for file/path
2602 
2603  real :: temp_u(g%domain%niglobal+1,g%domain%njglobal)
2604  real :: temp_v(g%domain%niglobal,g%domain%njglobal+1)
2605 
2606  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
2607  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
2608  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
2609 
2610  ! For now, there are no radiation conditions applied to the thicknesses, since
2611  ! the thicknesses might not be physically motivated. Instead, sponges should be
2612  ! used to enforce the near-boundary layer structure.
2613 
2614  if (associated(tv%T)) then
2615 
2616  call pass_var(tv%T, g%Domain)
2617  call pass_var(tv%S, g%Domain)
2618 
2619  do n=1,obc%number_of_segments
2620  segment => obc%segment(n)
2621  if (.not. segment%on_pe) cycle
2622 
2623  if (segment%direction == obc_direction_e) then
2624  i=segment%HI%IsdB
2625  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
2626  tv%T(i+1,j,k) = tv%T(i,j,k) ; tv%S(i+1,j,k) = tv%S(i,j,k)
2627  enddo ; enddo
2628  elseif (segment%direction == obc_direction_w) then
2629  i=segment%HI%IsdB
2630  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
2631  tv%T(i,j,k) = tv%T(i+1,j,k) ; tv%S(i,j,k) = tv%S(i+1,j,k)
2632  enddo ; enddo
2633  elseif (segment%direction == obc_direction_n) then
2634  j=segment%HI%JsdB
2635  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
2636  tv%T(i,j+1,k) = tv%T(i,j,k) ; tv%S(i,j+1,k) = tv%S(i,j,k)
2637  enddo ; enddo
2638  elseif (segment%direction == obc_direction_s) then
2639  j=segment%HI%JsdB
2640  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
2641  tv%T(i,j,k) = tv%T(i,j+1,k) ; tv%S(i,j,k) = tv%S(i,j+1,k)
2642  enddo ; enddo
2643  endif
2644  enddo
2645  endif
2646 
2647 ! do n=1,OBC%number_of_segments
2648 ! segment => OBC%segment(n)
2649 ! if (.not. segment%on_pe) cycle
2650 
2651 ! if (segment%direction == OBC_DIRECTION_E) then
2652 ! I=segment%HI%IsdB
2653 ! do k=1,G%ke ; do j=segment%HI%jsd,segment%HI%jed
2654 ! h(i+1,j,k) = h(i,j,k)
2655 ! enddo ; enddo
2656 ! elseif (segment%direction == OBC_DIRECTION_W) then
2657 ! I=segment%HI%IsdB
2658 ! do k=1,G%ke ; do j=segment%HI%jsd,segment%HI%jed
2659 ! h(i,j,k) = h(i+1,j,k)
2660 ! enddo ; enddo
2661 ! elseif (segment%direction == OBC_DIRECTION_N) then
2662 ! J=segment%HI%JsdB
2663 ! do k=1,G%ke ; do i=segment%HI%isd,segment%HI%ied
2664 ! h(i,j+1,k) = h(i,j,k)
2665 ! enddo ; enddo
2666 ! elseif (segment%direction == OBC_DIRECTION_S) then
2667 ! J=segment%HI%JsdB
2668 ! do k=1,G%ke ; do i=segment%HI%isd,segment%HI%ied
2669 ! h(i,j,k) = h(i,j+1,k)
2670 ! enddo ; enddo
2671 ! endif
2672 ! enddo
2673 
2674 end subroutine set_tracer_data
2675 
2676 !> Needs documentation
2677 function lookup_seg_field(OBC_seg,field)
2678  type(obc_segment_type), pointer :: obc_seg !< OBC segment
2679  character(len=32), intent(in) :: field !< The field name
2680  integer :: lookup_seg_field
2681  ! Local variables
2682  integer :: n
2683 
2684  lookup_seg_field=-1
2685  do n=1,obc_seg%num_fields
2686  if (trim(field) == obc_seg%field(n)%name) then
2687  lookup_seg_field=n
2688  return
2689  endif
2690  enddo
2691 
2692 end function lookup_seg_field
2693 
2694 
2695 !> Allocate segment data fields
2696 subroutine allocate_obc_segment_data(OBC, segment)
2697  type(ocean_obc_type), pointer :: OBC !< Open boundary structure
2698  type(obc_segment_type), intent(inout) :: segment !< Open boundary segment
2699  ! Local variables
2700  integer :: isd, ied, jsd, jed
2701  integer :: IsdB, IedB, JsdB, JedB
2702  integer :: IscB, IecB, JscB, JecB
2703  character(len=40) :: mdl = "allocate_OBC_segment_data" ! This subroutine's name.
2704 
2705  isd = segment%HI%isd ; ied = segment%HI%ied
2706  jsd = segment%HI%jsd ; jed = segment%HI%jed
2707  isdb = segment%HI%IsdB ; iedb = segment%HI%IedB
2708  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
2709  iscb = segment%HI%IscB ; iecb = segment%HI%IecB
2710  jscb = segment%HI%JscB ; jecb = segment%HI%JecB
2711 
2712  if (.not. segment%on_pe) return
2713 
2714  if (segment%is_E_or_W) then
2715  ! If these are just Flather, change update_OBC_segment_data accordingly
2716  allocate(segment%Cg(isdb:iedb,jsd:jed)); segment%Cg(:,:)=0.
2717  allocate(segment%Htot(isdb:iedb,jsd:jed)); segment%Htot(:,:)=0.0
2718  allocate(segment%h(isdb:iedb,jsd:jed,obc%ke)); segment%h(:,:,:)=0.0
2719  allocate(segment%eta(isdb:iedb,jsd:jed)); segment%eta(:,:)=0.0
2720  if (segment%radiation) then
2721  allocate(segment%rx_normal(isdb:iedb,jsd:jed,obc%ke)); segment%rx_normal(:,:,:)=0.0
2722  endif
2723  allocate(segment%normal_vel(isdb:iedb,jsd:jed,obc%ke)); segment%normal_vel(:,:,:)=0.0
2724  allocate(segment%normal_vel_bt(isdb:iedb,jsd:jed)); segment%normal_vel_bt(:,:)=0.0
2725  allocate(segment%normal_trans(isdb:iedb,jsd:jed,obc%ke)); segment%normal_trans(:,:,:)=0.0
2726  if (segment%nudged) then
2727  allocate(segment%nudged_normal_vel(isdb:iedb,jsd:jed,obc%ke)); segment%nudged_normal_vel(:,:,:)=0.0
2728  endif
2729  if (segment%radiation_tan .or. segment%nudged_tan .or. segment%specified_tan .or. &
2730  obc%computed_vorticity .or. obc%computed_strain) then
2731  allocate(segment%tangential_vel(isdb:iedb,jsdb:jedb,obc%ke)); segment%tangential_vel(:,:,:)=0.0
2732  endif
2733  if (segment%nudged_tan) then
2734  allocate(segment%nudged_tangential_vel(isdb:iedb,jsdb:jedb,obc%ke)); segment%nudged_tangential_vel(:,:,:)=0.0
2735  endif
2736  if (segment%nudged_grad) then
2737  allocate(segment%nudged_tangential_grad(isdb:iedb,jsdb:jedb,obc%ke)); segment%nudged_tangential_grad(:,:,:)=0.0
2738  endif
2739  if (obc%specified_vorticity .or. obc%specified_strain .or. segment%radiation_grad .or. &
2740  segment%oblique_grad) then
2741  allocate(segment%tangential_grad(isdb:iedb,jsdb:jedb,obc%ke)); segment%tangential_grad(:,:,:)=0.0
2742  endif
2743  if (segment%oblique) then
2744  allocate(segment%grad_normal(jsdb:jedb,2,obc%ke)); segment%grad_normal(:,:,:) = 0.0
2745  allocate(segment%rx_normal(isdb:iedb,jsd:jed,obc%ke)); segment%rx_normal(:,:,:)=0.0
2746  allocate(segment%ry_normal(isdb:iedb,jsd:jed,obc%ke)); segment%ry_normal(:,:,:)=0.0
2747  allocate(segment%cff_normal(isdb:iedb,jsd:jed,obc%ke)); segment%cff_normal(:,:,:)=0.0
2748  endif
2749  if (segment%oblique_tan) then
2750  allocate(segment%grad_tan(jsd:jed,2,obc%ke)); segment%grad_tan(:,:,:) = 0.0
2751  endif
2752  if (segment%oblique_grad) then
2753  allocate(segment%grad_gradient(jsd:jed,2,obc%ke)); segment%grad_gradient(:,:,:) = 0.0
2754  endif
2755  endif
2756 
2757  if (segment%is_N_or_S) then
2758  ! If these are just Flather, change update_OBC_segment_data accordingly
2759  allocate(segment%Cg(isd:ied,jsdb:jedb)); segment%Cg(:,:)=0.
2760  allocate(segment%Htot(isd:ied,jsdb:jedb)); segment%Htot(:,:)=0.0
2761  allocate(segment%h(isd:ied,jsdb:jedb,obc%ke)); segment%h(:,:,:)=0.0
2762  allocate(segment%eta(isd:ied,jsdb:jedb)); segment%eta(:,:)=0.0
2763  if (segment%radiation) then
2764  allocate(segment%ry_normal(isd:ied,jsdb:jedb,obc%ke)); segment%ry_normal(:,:,:)=0.0
2765  endif
2766  allocate(segment%normal_vel(isd:ied,jsdb:jedb,obc%ke)); segment%normal_vel(:,:,:)=0.0
2767  allocate(segment%normal_vel_bt(isd:ied,jsdb:jedb)); segment%normal_vel_bt(:,:)=0.0
2768  allocate(segment%normal_trans(isd:ied,jsdb:jedb,obc%ke)); segment%normal_trans(:,:,:)=0.0
2769  if (segment%nudged) then
2770  allocate(segment%nudged_normal_vel(isd:ied,jsdb:jedb,obc%ke)); segment%nudged_normal_vel(:,:,:)=0.0
2771  endif
2772  if (segment%radiation_tan .or. segment%nudged_tan .or. segment%specified_tan .or. &
2773  obc%computed_vorticity .or. obc%computed_strain) then
2774  allocate(segment%tangential_vel(isdb:iedb,jsdb:jedb,obc%ke)); segment%tangential_vel(:,:,:)=0.0
2775  endif
2776  if (segment%nudged_tan) then
2777  allocate(segment%nudged_tangential_vel(isdb:iedb,jsdb:jedb,obc%ke)); segment%nudged_tangential_vel(:,:,:)=0.0
2778  endif
2779  if (segment%nudged_grad) then
2780  allocate(segment%nudged_tangential_grad(isdb:iedb,jsdb:jedb,obc%ke)); segment%nudged_tangential_grad(:,:,:)=0.0
2781  endif
2782  if (obc%specified_vorticity .or. obc%specified_strain .or. segment%radiation_grad .or. &
2783  segment%oblique_grad) then
2784  allocate(segment%tangential_grad(isdb:iedb,jsdb:jedb,obc%ke)); segment%tangential_grad(:,:,:)=0.0
2785  endif
2786  if (segment%oblique) then
2787  allocate(segment%grad_normal(isdb:iedb,2,obc%ke)); segment%grad_normal(:,:,:) = 0.0
2788  allocate(segment%rx_normal(isd:ied,jsdb:jedb,obc%ke)); segment%rx_normal(:,:,:)=0.0
2789  allocate(segment%ry_normal(isd:ied,jsdb:jedb,obc%ke)); segment%ry_normal(:,:,:)=0.0
2790  allocate(segment%cff_normal(isd:ied,jsdb:jedb,obc%ke)); segment%cff_normal(:,:,:)=0.0
2791  endif
2792  if (segment%oblique_tan) then
2793  allocate(segment%grad_tan(isd:ied,2,obc%ke)); segment%grad_tan(:,:,:) = 0.0
2794  endif
2795  if (segment%oblique_grad) then
2796  allocate(segment%grad_gradient(isd:ied,2,obc%ke)); segment%grad_gradient(:,:,:) = 0.0
2797  endif
2798  endif
2799 
2800 end subroutine allocate_obc_segment_data
2801 
2802 !> Deallocate segment data fields
2803 subroutine deallocate_obc_segment_data(OBC, segment)
2804  type(ocean_obc_type), pointer :: OBC !< Open boundary structure
2805  type(obc_segment_type), intent(inout) :: segment !< Open boundary segment
2806  ! Local variables
2807  character(len=40) :: mdl = "deallocate_OBC_segment_data" ! This subroutine's name.
2808 
2809  if (.not. segment%on_pe) return
2810 
2811  if (associated (segment%Cg)) deallocate(segment%Cg)
2812  if (associated (segment%Htot)) deallocate(segment%Htot)
2813  if (associated (segment%h)) deallocate(segment%h)
2814  if (associated (segment%eta)) deallocate(segment%eta)
2815  if (associated (segment%rx_normal)) deallocate(segment%rx_normal)
2816  if (associated (segment%ry_normal)) deallocate(segment%ry_normal)
2817  if (associated (segment%cff_normal)) deallocate(segment%cff_normal)
2818  if (associated (segment%grad_normal)) deallocate(segment%grad_normal)
2819  if (associated (segment%grad_tan)) deallocate(segment%grad_tan)
2820  if (associated (segment%grad_gradient)) deallocate(segment%grad_gradient)
2821  if (associated (segment%normal_vel)) deallocate(segment%normal_vel)
2822  if (associated (segment%normal_vel_bt)) deallocate(segment%normal_vel_bt)
2823  if (associated (segment%normal_trans)) deallocate(segment%normal_trans)
2824  if (associated (segment%nudged_normal_vel)) deallocate(segment%nudged_normal_vel)
2825  if (associated (segment%tangential_vel)) deallocate(segment%tangential_vel)
2826  if (associated (segment%nudged_tangential_vel)) deallocate(segment%nudged_tangential_vel)
2827  if (associated (segment%nudged_tangential_grad)) deallocate(segment%nudged_tangential_grad)
2828  if (associated (segment%tangential_grad)) deallocate(segment%tangential_grad)
2829  if (associated (segment%tr_Reg)) call segment_tracer_registry_end(segment%tr_Reg)
2830 
2831 
2832 end subroutine deallocate_obc_segment_data
2833 
2834 !> Set tangential velocities outside of open boundaries to silly values
2835 !! (used for checking the interior state is independent of values outside
2836 !! of the domain).
2837 subroutine open_boundary_test_extern_uv(G, OBC, u, v)
2838  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
2839  type(ocean_obc_type), pointer :: obc !< Open boundary structure
2840  real, dimension(SZIB_(G),SZJ_(G), SZK_(G)),intent(inout) :: u !< Zonal velocity [m s-1]
2841  real, dimension(SZI_(G),SZJB_(G), SZK_(G)),intent(inout) :: v !< Meridional velocity [m s-1]
2842  ! Local variables
2843  integer :: i, j, k, n
2844 
2845  if (.not. associated(obc)) return
2846 
2847  do n = 1, obc%number_of_segments
2848  do k = 1, g%ke
2849  if (obc%segment(n)%is_N_or_S) then
2850  j = obc%segment(n)%HI%JsdB
2851  if (obc%segment(n)%direction == obc_direction_n) then
2852  do i = obc%segment(n)%HI%IsdB, obc%segment(n)%HI%IedB
2853  u(i,j+1,k) = obc%silly_u
2854  enddo
2855  else
2856  do i = obc%segment(n)%HI%IsdB, obc%segment(n)%HI%IedB
2857  u(i,j,k) = obc%silly_u
2858  enddo
2859  endif
2860  elseif (obc%segment(n)%is_E_or_W) then
2861  i = obc%segment(n)%HI%IsdB
2862  if (obc%segment(n)%direction == obc_direction_e) then
2863  do j = obc%segment(n)%HI%JsdB, obc%segment(n)%HI%JedB
2864  v(i+1,j,k) = obc%silly_u
2865  enddo
2866  else
2867  do j = obc%segment(n)%HI%JsdB, obc%segment(n)%HI%JedB
2868  v(i,j,k) = obc%silly_u
2869  enddo
2870  endif
2871  endif
2872  enddo
2873  enddo
2874 
2875 end subroutine open_boundary_test_extern_uv
2876 
2877 !> Set thicknesses outside of open boundaries to silly values
2878 !! (used for checking the interior state is independent of values outside
2879 !! of the domain).
2880 subroutine open_boundary_test_extern_h(G, OBC, h)
2881  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
2882  type(ocean_obc_type), pointer :: obc !< Open boundary structure
2883  real, dimension(SZI_(G),SZJ_(G), SZK_(G)),intent(inout) :: h !< Layer thickness [H ~> m or kg m-2]
2884  ! Local variables
2885  integer :: i, j, k, n
2886 
2887  if (.not. associated(obc)) return
2888 
2889  do n = 1, obc%number_of_segments
2890  do k = 1, g%ke
2891  if (obc%segment(n)%is_N_or_S) then
2892  j = obc%segment(n)%HI%JsdB
2893  if (obc%segment(n)%direction == obc_direction_n) then
2894  do i = obc%segment(n)%HI%isd, obc%segment(n)%HI%ied
2895  h(i,j+1,k) = obc%silly_h
2896  enddo
2897  else
2898  do i = obc%segment(n)%HI%isd, obc%segment(n)%HI%ied
2899  h(i,j,k) = obc%silly_h
2900  enddo
2901  endif
2902  elseif (obc%segment(n)%is_E_or_W) then
2903  i = obc%segment(n)%HI%IsdB
2904  if (obc%segment(n)%direction == obc_direction_e) then
2905  do j = obc%segment(n)%HI%jsd, obc%segment(n)%HI%jed
2906  h(i+1,j,k) = obc%silly_h
2907  enddo
2908  else
2909  do j = obc%segment(n)%HI%jsd, obc%segment(n)%HI%jed
2910  h(i,j,k) = obc%silly_h
2911  enddo
2912  endif
2913  endif
2914  enddo
2915  enddo
2916 
2917 end subroutine open_boundary_test_extern_h
2918 
2919 !> Update the OBC values on the segments.
2920 subroutine update_obc_segment_data(G, GV, US, OBC, tv, h, Time)
2921  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
2922  type(verticalgrid_type), intent(in) :: gv !< Ocean vertical grid structure
2923  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
2924  type(ocean_obc_type), pointer :: obc !< Open boundary structure
2925  type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure
2926  real, dimension(SZI_(G),SZJ_(G), SZK_(G)), intent(inout) :: h !< Thickness [m]
2927  type(time_type), intent(in) :: time !< Model time
2928  ! Local variables
2929  integer :: i, j, k, is, ie, js, je, isd, ied, jsd, jed
2930  integer :: isdb, iedb, jsdb, jedb, n, m, nz
2931  character(len=40) :: mdl = "set_OBC_segment_data" ! This subroutine's name.
2932  character(len=200) :: filename, obc_file, inputdir ! Strings for file/path
2933  type(obc_segment_type), pointer :: segment => null()
2934  integer, dimension(4) :: siz,siz2
2935  real :: sumh ! column sum of thicknesses [m]
2936  integer :: ni_seg, nj_seg ! number of src gridpoints along the segments
2937  integer :: i2, j2 ! indices for referencing local domain array
2938  integer :: is_obc, ie_obc, js_obc, je_obc ! segment indices within local domain
2939  integer :: ishift, jshift ! offsets for staggered locations
2940  real, dimension(:,:), pointer :: seg_vel => null() ! pointer to segment velocity array
2941  real, dimension(:,:), pointer :: seg_trans => null() ! pointer to segment transport array
2942  real, dimension(:,:,:), allocatable :: tmp_buffer
2943  real, dimension(:), allocatable :: h_stack
2944  integer :: is_obc2, js_obc2
2945  real :: net_h_src, net_h_int, scl_fac
2946  real, pointer, dimension(:,:) :: normal_trans_bt=>null() ! barotropic transport
2947 
2948  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
2949  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
2950  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
2951  nz=g%ke
2952 
2953  if (.not. associated(obc)) return
2954 
2955  do n = 1, obc%number_of_segments
2956  segment => obc%segment(n)
2957 
2958  if (.not. segment%on_pe) cycle ! continue to next segment if not in computational domain
2959 
2960  ni_seg = segment%ie_obc-segment%is_obc+1
2961  nj_seg = segment%je_obc-segment%js_obc+1
2962  is_obc = max(segment%is_obc,isd-1)
2963  ie_obc = min(segment%ie_obc,ied)
2964  js_obc = max(segment%js_obc,jsd-1)
2965  je_obc = min(segment%je_obc,jed)
2966 
2967 ! Calculate auxiliary fields at staggered locations.
2968 ! Segment indices are on q points:
2969 !
2970 ! |-----------|------------|-----------|-----------| J_obc
2971 ! Is_obc Ie_obc
2972 !
2973 ! i2 has to start at Is_obc+1 and end at Ie_obc.
2974 ! j2 is J_obc and jshift has to be +1 at both the north and south.
2975 
2976  ! calculate auxiliary fields at staggered locations
2977  ishift=0;jshift=0
2978  if (segment%is_E_or_W) then
2979  allocate(normal_trans_bt(segment%HI%IsdB:segment%HI%IedB,segment%HI%jsd:segment%HI%jed))
2980  normal_trans_bt(:,:)=0.0
2981  if (segment%direction == obc_direction_w) ishift=1
2982  i=segment%HI%IsdB
2983  do j=segment%HI%jsd,segment%HI%jed
2984  segment%Cg(i,j) = us%L_T_to_m_s*sqrt(gv%g_prime(1)*g%bathyT(i+ishift,j))
2985  segment%Htot(i,j)=0.0
2986  do k=1,g%ke
2987  segment%h(i,j,k) = h(i+ishift,j,k)
2988  segment%Htot(i,j)=segment%Htot(i,j)+segment%h(i,j,k)
2989  enddo
2990  enddo
2991  else! (segment%direction == OBC_DIRECTION_N .or. segment%direction == OBC_DIRECTION_S)
2992  allocate(normal_trans_bt(segment%HI%isd:segment%HI%ied,segment%HI%JsdB:segment%HI%JedB))
2993  normal_trans_bt(:,:)=0.0
2994  if (segment%direction == obc_direction_s) jshift=1
2995  j=segment%HI%JsdB
2996  do i=segment%HI%isd,segment%HI%ied
2997  segment%Cg(i,j) = us%L_T_to_m_s*sqrt(gv%g_prime(1)*g%bathyT(i,j+jshift))
2998  segment%Htot(i,j)=0.0
2999  do k=1,g%ke
3000  segment%h(i,j,k) = h(i,j+jshift,k)
3001  segment%Htot(i,j)=segment%Htot(i,j)+segment%h(i,j,k)
3002  enddo
3003  enddo
3004  endif
3005 
3006  allocate(h_stack(g%ke))
3007  h_stack(:) = 0.0
3008  do m = 1,segment%num_fields
3009  if (segment%field(m)%fid > 0) then
3010  siz(1)=size(segment%field(m)%buffer_src,1)
3011  siz(2)=size(segment%field(m)%buffer_src,2)
3012  siz(3)=size(segment%field(m)%buffer_src,3)
3013  if (.not.associated(segment%field(m)%buffer_dst)) then
3014  if (siz(3) /= segment%field(m)%nk_src) call mom_error(fatal,'nk_src inconsistency')
3015  if (segment%field(m)%nk_src > 1) then
3016  if (segment%is_E_or_W) then
3017  if (segment%field(m)%name == 'V' .or. segment%field(m)%name == 'DVDX') then
3018  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
3019  else
3020  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc+1:je_obc,g%ke))
3021  endif
3022  if (segment%field(m)%name == 'U') then
3023  allocate(segment%field(m)%bt_vel(is_obc:ie_obc,js_obc+1:je_obc))
3024  segment%field(m)%bt_vel(:,:)=0.0
3025  endif
3026  else
3027  if (segment%field(m)%name == 'U' .or. segment%field(m)%name == 'DUDY') then
3028  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
3029  else
3030  allocate(segment%field(m)%buffer_dst(is_obc+1:ie_obc,js_obc:je_obc,g%ke))
3031  endif
3032  if (segment%field(m)%name == 'V') then
3033  allocate(segment%field(m)%bt_vel(is_obc+1:ie_obc,js_obc:je_obc))
3034  segment%field(m)%bt_vel(:,:)=0.0
3035  endif
3036  endif
3037  else
3038  if (segment%is_E_or_W) then
3039  if (segment%field(m)%name == 'V' .or. segment%field(m)%name == 'DVDX') then
3040  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,1))
3041  else
3042  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc+1:je_obc,1))
3043  endif
3044  if (segment%field(m)%name == 'U') then
3045  allocate(segment%field(m)%bt_vel(is_obc:ie_obc,js_obc+1:je_obc))
3046  segment%field(m)%bt_vel(:,:)=0.0
3047  endif
3048  else
3049  if (segment%field(m)%name == 'U' .or. segment%field(m)%name == 'DUDY') then
3050  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,1))
3051  else
3052  allocate(segment%field(m)%buffer_dst(is_obc+1:ie_obc,js_obc:je_obc,1))
3053  endif
3054  if (segment%field(m)%name == 'V') then
3055  allocate(segment%field(m)%bt_vel(is_obc+1:ie_obc,js_obc:je_obc))
3056  segment%field(m)%bt_vel(:,:)=0.0
3057  endif
3058  endif
3059  endif
3060  segment%field(m)%buffer_dst(:,:,:)=0.0
3061  endif
3062  ! read source data interpolated to the current model time
3063  if (siz(1)==1) then
3064  if (obc%brushcutter_mode) then
3065  allocate(tmp_buffer(1,nj_seg*2-1,segment%field(m)%nk_src)) ! segment data is currrently on supergrid
3066  else
3067  allocate(tmp_buffer(1,nj_seg,segment%field(m)%nk_src)) ! segment data is currrently on supergrid
3068  endif
3069  else
3070  if (obc%brushcutter_mode) then
3071  allocate(tmp_buffer(ni_seg*2-1,1,segment%field(m)%nk_src)) ! segment data is currrently on supergrid
3072  else
3073  allocate(tmp_buffer(ni_seg,1,segment%field(m)%nk_src)) ! segment data is currrently on supergrid
3074  endif
3075  endif
3076 
3077  call time_interp_external(segment%field(m)%fid,time, tmp_buffer)
3078  if (obc%brushcutter_mode) then
3079  if (segment%is_E_or_W) then
3080  if (segment%field(m)%name == 'V' .or. segment%field(m)%name == 'DVDX') then
3081  segment%field(m)%buffer_src(is_obc,:,:) = &
3082  tmp_buffer(1,2*(js_obc+g%jdg_offset)+1:2*(je_obc+g%jdg_offset)+1:2,:)
3083  else
3084  segment%field(m)%buffer_src(is_obc,:,:) = &
3085  tmp_buffer(1,2*(js_obc+g%jdg_offset)+1:2*(je_obc+g%jdg_offset):2,:)
3086  endif
3087  else
3088  if (segment%field(m)%name == 'U' .or. segment%field(m)%name == 'DUDY') then
3089  segment%field(m)%buffer_src(:,js_obc,:) = &
3090  tmp_buffer(2*(is_obc+g%idg_offset)+1:2*(ie_obc+g%idg_offset)+1:2,1,:)
3091  else
3092  segment%field(m)%buffer_src(:,js_obc,:) = &
3093  tmp_buffer(2*(is_obc+g%idg_offset)+1:2*(ie_obc+g%idg_offset):2,1,:)
3094  endif
3095  endif
3096  else
3097  if (segment%is_E_or_W) then
3098  if (segment%field(m)%name == 'V' .or. segment%field(m)%name == 'DVDX') then
3099  segment%field(m)%buffer_src(is_obc,:,:)=tmp_buffer(1,js_obc+g%jdg_offset+1:je_obc+g%jdg_offset+1,:)
3100  else
3101  segment%field(m)%buffer_src(is_obc,:,:)=tmp_buffer(1,js_obc+g%jdg_offset+1:je_obc+g%jdg_offset,:)
3102  endif
3103  else
3104  if (segment%field(m)%name == 'U' .or. segment%field(m)%name == 'DUDY') then
3105  segment%field(m)%buffer_src(:,js_obc,:)=tmp_buffer(is_obc+g%idg_offset+1:ie_obc+g%idg_offset+1,1,:)
3106  else
3107  segment%field(m)%buffer_src(:,js_obc,:)=tmp_buffer(is_obc+g%idg_offset+1:ie_obc+g%idg_offset,1,:)
3108  endif
3109  endif
3110  endif
3111  if (segment%field(m)%nk_src > 1) then
3112  call time_interp_external(segment%field(m)%fid_dz,time, tmp_buffer)
3113  if (obc%brushcutter_mode) then
3114  if (segment%is_E_or_W) then
3115  if (segment%field(m)%name == 'V' .or. segment%field(m)%name == 'DVDX') then
3116  segment%field(m)%dz_src(is_obc,:,:) = &
3117  tmp_buffer(1,2*(js_obc+g%jdg_offset)+1:2*(je_obc+g%jdg_offset)+1:2,:)
3118  else
3119  segment%field(m)%dz_src(is_obc,:,:) = &
3120  tmp_buffer(1,2*(js_obc+g%jdg_offset)+1:2*(je_obc+g%jdg_offset):2,:)
3121  endif
3122  else
3123  if (segment%field(m)%name == 'U' .or. segment%field(m)%name == 'DUDY') then
3124  segment%field(m)%dz_src(:,js_obc,:) = &
3125  tmp_buffer(2*(is_obc+g%idg_offset)+1:2*(ie_obc+g%idg_offset)+1:2,1,:)
3126  else
3127  segment%field(m)%dz_src(:,js_obc,:) = &
3128  tmp_buffer(2*(is_obc+g%idg_offset)+1:2*(ie_obc+g%idg_offset):2,1,:)
3129  endif
3130  endif
3131  else
3132  if (segment%is_E_or_W) then
3133  if (segment%field(m)%name == 'V' .or. segment%field(m)%name == 'DVDX') then
3134  segment%field(m)%dz_src(is_obc,:,:)=tmp_buffer(1,js_obc+g%jdg_offset+1:je_obc+g%jdg_offset+1,:)
3135  else
3136  segment%field(m)%dz_src(is_obc,:,:)=tmp_buffer(1,js_obc+g%jdg_offset+1:je_obc+g%jdg_offset,:)
3137  endif
3138  else
3139  if (segment%field(m)%name == 'U' .or. segment%field(m)%name == 'DUDY') then
3140  segment%field(m)%dz_src(:,js_obc,:)=tmp_buffer(is_obc+g%idg_offset+1:ie_obc+g%idg_offset+1,1,:)
3141  else
3142  segment%field(m)%dz_src(:,js_obc,:)=tmp_buffer(is_obc+g%idg_offset+1:ie_obc+g%idg_offset,1,:)
3143  endif
3144  endif
3145  endif
3146 
3147  if (segment%is_E_or_W) then
3148  ishift=1
3149  if (segment%direction == obc_direction_e) ishift=0
3150  i=is_obc
3151  if (segment%field(m)%name == 'V' .or. segment%field(m)%name == 'DVDX') then
3152  ! Do q points for the whole segment
3153  do j=max(js_obc,jsd),min(je_obc,jed-1)
3154  ! Using the h remapping approach
3155  ! Pretty sure we need to check for source/target grid consistency here
3156  segment%field(m)%buffer_dst(i,j,:)=0.0 ! initialize remap destination buffer
3157  if (g%mask2dCu(i,j)>0. .and. g%mask2dCu(i,j+1)>0.) then
3158  h_stack(:) = 0.5*(h(i+ishift,j,:) + h(i+ishift,j+1,:))
3159  call remapping_core_h(obc%remap_CS, &
3160  segment%field(m)%nk_src,segment%field(m)%dz_src(i,j,:), &
3161  segment%field(m)%buffer_src(i,j,:), &
3162  g%ke, h_stack, segment%field(m)%buffer_dst(i,j,:))
3163  elseif (g%mask2dCu(i,j)>0.) then
3164  h_stack(:) = h(i+ishift,j,:)
3165  call remapping_core_h(obc%remap_CS, &
3166  segment%field(m)%nk_src,segment%field(m)%dz_src(i,j,:), &
3167  segment%field(m)%buffer_src(i,j,:), &
3168  g%ke, h_stack, segment%field(m)%buffer_dst(i,j,:))
3169  elseif (g%mask2dCu(i,j+1)>0.) then
3170  h_stack(:) = h(i+ishift,j+1,:)
3171  call remapping_core_h(obc%remap_CS, &
3172  segment%field(m)%nk_src,segment%field(m)%dz_src(i,j,:), &
3173  segment%field(m)%buffer_src(i,j,:), &
3174  g%ke, h_stack, segment%field(m)%buffer_dst(i,j,:))
3175  endif
3176  enddo
3177  else
3178  do j=js_obc+1,je_obc
3179  ! Using the h remapping approach
3180  ! Pretty sure we need to check for source/target grid consistency here
3181  segment%field(m)%buffer_dst(i,j,:)=0.0 ! initialize remap destination buffer
3182  if (g%mask2dCu(i,j)>0.) then
3183  net_h_src = sum( segment%field(m)%dz_src(i,j,:) )
3184  net_h_int = sum( h(i+ishift,j,:) )
3185  scl_fac = net_h_int / net_h_src
3186  call remapping_core_h(obc%remap_CS, &
3187  segment%field(m)%nk_src, scl_fac*segment%field(m)%dz_src(i,j,:), &
3188  segment%field(m)%buffer_src(i,j,:), &
3189  g%ke, h(i+ishift,j,:), segment%field(m)%buffer_dst(i,j,:))
3190  endif
3191  enddo
3192  endif
3193  else
3194  jshift=1
3195  if (segment%direction == obc_direction_n) jshift=0
3196  j=js_obc
3197  if (segment%field(m)%name == 'U' .or. segment%field(m)%name == 'DUDY') then
3198  ! Do q points for the whole segment
3199  do i=max(is_obc,isd),min(ie_obc,ied-1)
3200  segment%field(m)%buffer_dst(i,j,:)=0.0 ! initialize remap destination buffer
3201  if (g%mask2dCv(i,j)>0. .and. g%mask2dCv(i+1,j)>0.) then
3202  ! Using the h remapping approach
3203  ! Pretty sure we need to check for source/target grid consistency here
3204  h_stack(:) = 0.5*(h(i,j+jshift,:) + h(i+1,j+jshift,:))
3205  call remapping_core_h(obc%remap_CS, &
3206  segment%field(m)%nk_src,segment%field(m)%dz_src(i,j,:), &
3207  segment%field(m)%buffer_src(i,j,:), &
3208  g%ke, h_stack, segment%field(m)%buffer_dst(i,j,:))
3209  elseif (g%mask2dCv(i,j)>0.) then
3210  h_stack(:) = h(i,j+jshift,:)
3211  call remapping_core_h(obc%remap_CS, &
3212  segment%field(m)%nk_src,segment%field(m)%dz_src(i,j,:), &
3213  segment%field(m)%buffer_src(i,j,:), &
3214  g%ke, h_stack, segment%field(m)%buffer_dst(i,j,:))
3215  elseif (g%mask2dCv(i+1,j)>0.) then
3216  h_stack(:) = h(i+1,j+jshift,:)
3217  call remapping_core_h(obc%remap_CS, &
3218  segment%field(m)%nk_src,segment%field(m)%dz_src(i,j,:), &
3219  segment%field(m)%buffer_src(i,j,:), &
3220  g%ke, h_stack, segment%field(m)%buffer_dst(i,j,:))
3221  endif
3222  enddo
3223  else
3224  do i=is_obc+1,ie_obc
3225  ! Using the h remapping approach
3226  ! Pretty sure we need to check for source/target grid consistency here
3227  segment%field(m)%buffer_dst(i,j,:)=0.0 ! initialize remap destination buffer
3228  if (g%mask2dCv(i,j)>0.) then
3229  net_h_src = sum( segment%field(m)%dz_src(i,j,:) )
3230  net_h_int = sum( h(i,j+jshift,:) )
3231  scl_fac = net_h_int / net_h_src
3232  call remapping_core_h(obc%remap_CS, &
3233  segment%field(m)%nk_src,segment%field(m)%dz_src(i,j,:), &
3234  segment%field(m)%buffer_src(i,j,:), &
3235  g%ke, h(i,j+jshift,:), segment%field(m)%buffer_dst(i,j,:))
3236  endif
3237  enddo
3238  endif
3239  endif
3240  else ! 2d data
3241  segment%field(m)%buffer_dst(:,:,1)=segment%field(m)%buffer_src(:,:,1) ! initialize remap destination buffer
3242  endif
3243  deallocate(tmp_buffer)
3244  else ! fid <= 0 (Uniform value)
3245  if (.not. associated(segment%field(m)%buffer_dst)) then
3246  if (segment%is_E_or_W) then
3247  if (segment%field(m)%name == 'V') then
3248  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
3249  allocate(segment%field(m)%bt_vel(is_obc:ie_obc,js_obc:je_obc))
3250  elseif (segment%field(m)%name == 'U') then
3251  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc+1:je_obc,g%ke))
3252  allocate(segment%field(m)%bt_vel(is_obc:ie_obc,js_obc+1:je_obc))
3253  elseif (segment%field(m)%name == 'DVDX') then
3254  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
3255  else
3256  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc+1:je_obc,g%ke))
3257  endif
3258  else
3259  if (segment%field(m)%name == 'U') then
3260  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
3261  allocate(segment%field(m)%bt_vel(is_obc:ie_obc,js_obc:je_obc))
3262  elseif (segment%field(m)%name == 'V') then
3263  allocate(segment%field(m)%buffer_dst(is_obc+1:ie_obc,js_obc:je_obc,g%ke))
3264  allocate(segment%field(m)%bt_vel(is_obc+1:ie_obc,js_obc:je_obc))
3265  elseif (segment%field(m)%name == 'DUDY') then
3266  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
3267  else
3268  allocate(segment%field(m)%buffer_dst(is_obc+1:ie_obc,js_obc:je_obc,g%ke))
3269  endif
3270  endif
3271  segment%field(m)%buffer_dst(:,:,:)=segment%field(m)%value
3272  if (trim(segment%field(m)%name) == 'U' .or. trim(segment%field(m)%name) == 'V') then
3273  segment%field(m)%bt_vel(:,:)=segment%field(m)%value
3274  endif
3275  endif
3276  endif
3277 
3278  if (trim(segment%field(m)%name) == 'U' .or. trim(segment%field(m)%name) == 'V') then
3279  if (segment%field(m)%fid>0) then ! calculate external BT velocity and transport if needed
3280  if (trim(segment%field(m)%name) == 'U' .and. segment%is_E_or_W) then
3281  i=is_obc
3282  do j=js_obc+1,je_obc
3283  normal_trans_bt(i,j) = 0.0
3284  do k=1,g%ke
3285  segment%normal_vel(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
3286  segment%normal_trans(i,j,k) = segment%field(m)%buffer_dst(i,j,k)*segment%h(i,j,k) * &
3287  g%dyCu(i,j)
3288  normal_trans_bt(i,j) = normal_trans_bt(i,j)+segment%normal_trans(i,j,k)
3289  enddo
3290  segment%normal_vel_bt(i,j) = normal_trans_bt(i,j)/(max(segment%Htot(i,j),1.e-12) * &
3291  g%dyCu(i,j))
3292  if (associated(segment%nudged_normal_vel)) segment%nudged_normal_vel(i,j,:) = segment%normal_vel(i,j,:)
3293  enddo
3294  elseif (trim(segment%field(m)%name) == 'V' .and. segment%is_N_or_S) then
3295  j=js_obc
3296  do i=is_obc+1,ie_obc
3297  normal_trans_bt(i,j) = 0.0
3298  do k=1,g%ke
3299  segment%normal_vel(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
3300  segment%normal_trans(i,j,k) = segment%field(m)%buffer_dst(i,j,k)*segment%h(i,j,k) * &
3301  g%dxCv(i,j)
3302  normal_trans_bt(i,j) = normal_trans_bt(i,j)+segment%normal_trans(i,j,k)
3303  enddo
3304  segment%normal_vel_bt(i,j) = normal_trans_bt(i,j)/(max(segment%Htot(i,j),1.e-12) * &
3305  g%dxCv(i,j))
3306  if (associated(segment%nudged_normal_vel)) segment%nudged_normal_vel(i,j,:) = segment%normal_vel(i,j,:)
3307  enddo
3308  elseif (trim(segment%field(m)%name) == 'V' .and. segment%is_E_or_W .and. &
3309  associated(segment%tangential_vel)) then
3310  i=is_obc
3311  do j=js_obc,je_obc
3312  do k=1,g%ke
3313  segment%tangential_vel(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
3314  enddo
3315  if (associated(segment%nudged_tangential_vel)) &
3316  segment%nudged_tangential_vel(i,j,:) = segment%tangential_vel(i,j,:)
3317  enddo
3318  elseif (trim(segment%field(m)%name) == 'U' .and. segment%is_N_or_S .and. &
3319  associated(segment%tangential_vel)) then
3320  j=js_obc
3321  do i=is_obc,ie_obc
3322  do k=1,g%ke
3323  segment%tangential_vel(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
3324  enddo
3325  if (associated(segment%nudged_tangential_vel)) &
3326  segment%nudged_tangential_vel(i,j,:) = segment%tangential_vel(i,j,:)
3327  enddo
3328  elseif (trim(segment%field(m)%name) == 'DVDX' .and. segment%is_E_or_W .and. &
3329  associated(segment%tangential_grad)) then
3330  i=is_obc
3331  do j=js_obc,je_obc
3332  do k=1,g%ke
3333  segment%tangential_grad(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
3334  enddo
3335  enddo
3336  elseif (trim(segment%field(m)%name) == 'DUDY' .and. segment%is_N_or_S .and. &
3337  associated(segment%tangential_grad)) then
3338  j=js_obc
3339  do i=is_obc,ie_obc
3340  do k=1,g%ke
3341  segment%tangential_grad(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
3342  enddo
3343  enddo
3344  endif
3345  endif
3346  endif
3347 
3348  ! from this point on, data are entirely on segments - will
3349  ! write all segment loops as 2d loops.
3350  if (segment%is_E_or_W) then
3351  js_obc2 = js_obc+1
3352  is_obc2 = is_obc
3353  else
3354  js_obc2 = js_obc
3355  is_obc2 = is_obc+1
3356  endif
3357  if (segment%is_N_or_S) then
3358  is_obc2 = is_obc+1
3359  js_obc2 = js_obc
3360  else
3361  is_obc2 = is_obc
3362  js_obc2 = js_obc+1
3363  endif
3364 
3365  if (trim(segment%field(m)%name) == 'SSH') then
3366  do j=js_obc2,je_obc
3367  do i=is_obc2,ie_obc
3368  segment%eta(i,j) = segment%field(m)%buffer_dst(i,j,1)
3369  enddo
3370  enddo
3371  endif
3372 
3373  if (trim(segment%field(m)%name) == 'TEMP') then
3374  if (associated(segment%field(m)%buffer_dst)) then
3375  do k=1,nz; do j=js_obc2, je_obc; do i=is_obc2,ie_obc
3376  segment%tr_Reg%Tr(1)%t(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
3377  enddo ; enddo ; enddo
3378  if (.not. segment%tr_Reg%Tr(1)%is_initialized) then
3379  ! if the tracer reservoir has not yet been initialized, then set to external value.
3380  do k=1,nz; do j=js_obc2, je_obc; do i=is_obc2,ie_obc
3381  segment%tr_Reg%Tr(1)%tres(i,j,k) = segment%tr_Reg%Tr(1)%t(i,j,k)
3382  enddo ; enddo ; enddo
3383  segment%tr_Reg%Tr(1)%is_initialized=.true.
3384  endif
3385  else
3386  segment%tr_Reg%Tr(1)%OBC_inflow_conc = segment%field(m)%value
3387  endif
3388  elseif (trim(segment%field(m)%name) == 'SALT') then
3389  if (associated(segment%field(m)%buffer_dst)) then
3390  do k=1,nz; do j=js_obc2, je_obc; do i=is_obc2,ie_obc
3391  segment%tr_Reg%Tr(2)%t(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
3392  enddo ; enddo ; enddo
3393  if (.not. segment%tr_Reg%Tr(1)%is_initialized) then
3394  !if the tracer reservoir has not yet been initialized, then set to external value.
3395  do k=1,nz; do j=js_obc2, je_obc; do i=is_obc2,ie_obc
3396  segment%tr_Reg%Tr(2)%tres(i,j,k) = segment%tr_Reg%Tr(2)%t(i,j,k)
3397  enddo ; enddo ; enddo
3398  segment%tr_Reg%Tr(1)%is_initialized=.true.
3399  endif
3400  else
3401  segment%tr_Reg%Tr(2)%OBC_inflow_conc = segment%field(m)%value
3402  endif
3403  endif
3404 
3405  enddo ! end field loop
3406  deallocate(h_stack)
3407  deallocate(normal_trans_bt)
3408 
3409  enddo ! end segment loop
3410 
3411 end subroutine update_obc_segment_data
3412 
3413 !> register open boundary objects for boundary updates.
3414 subroutine register_obc(name, param_file, Reg)
3415  character(len=32), intent(in) :: name !< OBC name used for error messages
3416  type(param_file_type), intent(in) :: param_file !< file to parse for model parameter values
3417  type(obc_registry_type), pointer :: reg !< pointer to the tracer registry
3418  integer :: nobc
3419  character(len=256) :: mesg ! Message for error messages.
3420 
3421  if (.not. associated(reg)) call obc_registry_init(param_file, reg)
3422 
3423  if (reg%nobc>=max_fields_) then
3424  write(mesg,'("Increase MAX_FIELDS_ in MOM_memory.h to at least ",I3," to allow for &
3425  &all the open boundaries being registered via register_OBC.")') reg%nobc+1
3426  call mom_error(fatal,"MOM register_tracer: "//mesg)
3427  endif
3428  reg%nobc = reg%nobc + 1
3429  nobc = reg%nobc
3430 
3431  reg%OB(nobc)%name = name
3432 
3433  if (reg%locked) call mom_error(fatal, &
3434  "MOM register_tracer was called for variable "//trim(reg%OB(nobc)%name)//&
3435  " with a locked tracer registry.")
3436 
3437 end subroutine register_obc
3438 
3439 !> This routine include declares and sets the variable "version".
3440 subroutine obc_registry_init(param_file, Reg)
3441  type(param_file_type), intent(in) :: param_file !< open file to parse for model parameters
3442  type(obc_registry_type), pointer :: reg !< pointer to OBC registry
3443 
3444  integer, save :: init_calls = 0
3445 
3446 #include "version_variable.h"
3447  character(len=40) :: mdl = "MOM_open_boundary" ! This module's name.
3448  character(len=256) :: mesg ! Message for error messages.
3449 
3450  if (.not.associated(reg)) then ; allocate(reg)
3451  else ; return ; endif
3452 
3453  ! Read all relevant parameters and write them to the model log.
3454 ! call log_version(param_file, mdl,s version, "")
3455 
3456  init_calls = init_calls + 1
3457  if (init_calls > 1) then
3458  write(mesg,'("OBC_registry_init called ",I3, &
3459  &" times with different registry pointers.")') init_calls
3460  if (is_root_pe()) call mom_error(warning,"MOM_open_boundary"//mesg)
3461  endif
3462 
3463 end subroutine obc_registry_init
3464 
3465 !> Add file to OBC registry.
3466 function register_file_obc(param_file, CS, OBC_Reg)
3467  type(param_file_type), intent(in) :: param_file !< parameter file.
3468  type(file_obc_cs), pointer :: cs !< file control structure.
3469  type(obc_registry_type), pointer :: obc_reg !< OBC registry.
3470  logical :: register_file_obc
3471  character(len=32) :: casename = "OBC file" !< This case's name.
3472 
3473  if (associated(cs)) then
3474  call mom_error(warning, "register_file_OBC called with an "// &
3475  "associated control structure.")
3476  return
3477  endif
3478  allocate(cs)
3479 
3480  ! Register the file for boundary updates.
3481  call register_obc(casename, param_file, obc_reg)
3482  register_file_obc = .true.
3483 
3484 end function register_file_obc
3485 
3486 !> Clean up the file OBC from registry.
3487 subroutine file_obc_end(CS)
3488  type(file_obc_cs), pointer :: cs !< OBC file control structure.
3489 
3490  if (associated(cs)) then
3491  deallocate(cs)
3492  endif
3493 end subroutine file_obc_end
3494 
3495 !> Initialize the segment tracer registry.
3496 subroutine segment_tracer_registry_init(param_file, segment)
3497  type(param_file_type), intent(in) :: param_file !< open file to parse for model parameters
3498  type(obc_segment_type), intent(inout) :: segment !< the segment
3499 
3500  integer, save :: init_calls = 0
3501 
3502 ! This include declares and sets the variable "version".
3503 #include "version_variable.h"
3504  character(len=40) :: mdl = "segment_tracer_registry_init" ! This routine's name.
3505  character(len=256) :: mesg ! Message for error messages.
3506 
3507  if (.not.associated(segment%tr_Reg)) then
3508  allocate(segment%tr_Reg)
3509  else
3510  return
3511  endif
3512 
3513  init_calls = init_calls + 1
3514 
3515  ! Read all relevant parameters and write them to the model log.
3516  if (init_calls == 1) call log_version(param_file, mdl, version, "")
3517 
3518 ! Need to call once per segment with tracers...
3519 ! if (init_calls > 1) then
3520 ! write(mesg,'("segment_tracer_registry_init called ",I3, &
3521 ! &" times with different registry pointers.")') init_calls
3522 ! if (is_root_pe()) call MOM_error(WARNING,"MOM_tracer"//mesg)
3523 ! endif
3524 
3525 end subroutine segment_tracer_registry_init
3526 
3527 subroutine register_segment_tracer(tr_ptr, param_file, GV, segment, &
3528  OBC_scalar, OBC_array)
3529  type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
3530  type(tracer_type), target :: tr_ptr !< A target that can be used to set a pointer to the
3531  !! stored value of tr. This target must be
3532  !! an enduring part of the control structure,
3533  !! because the tracer registry will use this memory,
3534  !! but it also means that any updates to this
3535  !! structure in the calling module will be
3536  !! available subsequently to the tracer registry.
3537  type(param_file_type), intent(in) :: param_file !< file to parse for model parameter values
3538  type(obc_segment_type), intent(inout) :: segment !< current segment data structure
3539  real, optional, intent(in) :: obc_scalar !< If present, use scalar value for segment tracer
3540  !! inflow concentration.
3541  logical, optional, intent(in) :: obc_array !< If true, use array values for segment tracer
3542  !! inflow concentration.
3543 
3544 
3545 ! Local variables
3546  integer :: ntseg
3547  integer :: isd, ied, jsd, jed
3548  integer :: isdb, iedb, jsdb, jedb
3549  character(len=256) :: mesg ! Message for error messages.
3550 
3551  call segment_tracer_registry_init(param_file, segment)
3552 
3553  if (segment%tr_Reg%ntseg>=max_fields_) then
3554  write(mesg,'("Increase MAX_FIELDS_ in MOM_memory.h to at least ",I3," to allow for &
3555  &all the tracers being registered via register_tracer.")') segment%tr_Reg%ntseg+1
3556  call mom_error(fatal,"MOM register_tracer: "//mesg)
3557  endif
3558  segment%tr_Reg%ntseg = segment%tr_Reg%ntseg + 1
3559  ntseg = segment%tr_Reg%ntseg
3560 
3561  isd = segment%HI%isd ; ied = segment%HI%ied
3562  jsd = segment%HI%jsd ; jed = segment%HI%jed
3563  isdb = segment%HI%IsdB ; iedb = segment%HI%IedB
3564  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
3565 
3566  segment%tr_Reg%Tr(ntseg)%Tr => tr_ptr
3567  segment%tr_Reg%Tr(ntseg)%name = tr_ptr%name
3568 
3569  if (segment%tr_Reg%locked) call mom_error(fatal, &
3570  "MOM register_tracer was called for variable "//trim(segment%tr_Reg%Tr(ntseg)%name)//&
3571  " with a locked tracer registry.")
3572 
3573  if (present(obc_scalar)) segment%tr_Reg%Tr(ntseg)%OBC_inflow_conc = obc_scalar ! initialize tracer value later
3574  if (present(obc_array)) then
3575  if (segment%is_E_or_W) then
3576  allocate(segment%tr_Reg%Tr(ntseg)%t(isdb:iedb,jsd:jed,1:gv%ke));segment%tr_Reg%Tr(ntseg)%t(:,:,:)=0.0
3577  allocate(segment%tr_Reg%Tr(ntseg)%tres(isdb:iedb,jsd:jed,1:gv%ke));segment%tr_Reg%Tr(ntseg)%tres(:,:,:)=0.0
3578  segment%tr_Reg%Tr(ntseg)%is_initialized=.false.
3579  elseif (segment%is_N_or_S) then
3580  allocate(segment%tr_Reg%Tr(ntseg)%t(isd:ied,jsdb:jedb,1:gv%ke));segment%tr_Reg%Tr(ntseg)%t(:,:,:)=0.0
3581  allocate(segment%tr_Reg%Tr(ntseg)%tres(isd:ied,jsdb:jedb,1:gv%ke));segment%tr_Reg%Tr(ntseg)%tres(:,:,:)=0.0
3582  segment%tr_Reg%Tr(ntseg)%is_initialized=.false.
3583  endif
3584  endif
3585 
3586 end subroutine register_segment_tracer
3587 
3588 !> Clean up the segment tracer registry.
3589 subroutine segment_tracer_registry_end(Reg)
3590  type(segment_tracer_registry_type), pointer :: reg !< pointer to tracer registry
3591 
3592 ! Local variables
3593  integer n
3594 
3595  if (associated(reg)) then
3596  do n = 1, reg%ntseg
3597  if (associated(reg%Tr(n)%t)) deallocate(reg%Tr(n)%t)
3598  enddo
3599  deallocate(reg)
3600  endif
3601 end subroutine segment_tracer_registry_end
3602 
3603 subroutine register_temp_salt_segments(GV, OBC, tr_Reg, param_file)
3604  type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
3605  type(ocean_obc_type), pointer :: obc !< Open boundary structure
3606  type(tracer_registry_type), pointer :: tr_reg !< Tracer registry
3607  type(param_file_type), intent(in) :: param_file !< file to parse for model parameter values
3608 
3609 ! Local variables
3610  integer :: isd, ied, isdb, iedb, jsd, jed, jsdb, jedb, nz, nf
3611  integer :: i, j, k, n
3612  character(len=32) :: name
3613  type(obc_segment_type), pointer :: segment => null() ! pointer to segment type list
3614  type(tracer_type), pointer :: tr_ptr => null()
3615 
3616  if (.not. associated(obc)) return
3617 
3618  do n=1, obc%number_of_segments
3619  segment=>obc%segment(n)
3620  if (.not. segment%on_pe) cycle
3621 
3622  if (associated(segment%tr_Reg)) &
3623  call mom_error(fatal,"register_temp_salt_segments: tracer array was previously allocated")
3624 
3625  name = 'temp'
3626  call tracer_name_lookup(tr_reg, tr_ptr, name)
3627  call register_segment_tracer(tr_ptr, param_file, gv, segment, &
3628  obc_array=segment%temp_segment_data_exists)
3629  name = 'salt'
3630  call tracer_name_lookup(tr_reg, tr_ptr, name)
3631  call register_segment_tracer(tr_ptr, param_file, gv, segment, &
3632  obc_array=segment%salt_segment_data_exists)
3633  enddo
3634 
3635 end subroutine register_temp_salt_segments
3636 
3637 subroutine fill_temp_salt_segments(G, OBC, tv)
3638  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
3639  type(ocean_obc_type), pointer :: obc !< Open boundary structure
3640  type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamics structure
3641 
3642 ! Local variables
3643  integer :: isd, ied, isdb, iedb, jsd, jed, jsdb, jedb, n, nz
3644  integer :: i, j, k
3645  type(obc_segment_type), pointer :: segment => null() ! pointer to segment type list
3646 
3647  if (.not. associated(obc)) return
3648  if (.not. associated(tv%T) .and. associated(tv%S)) return
3649  ! Both temperature and salinity fields
3650 
3651  call pass_var(tv%T, g%Domain)
3652  call pass_var(tv%S, g%Domain)
3653 
3654  nz = g%ke
3655 
3656  do n=1, obc%number_of_segments
3657  segment => obc%segment(n)
3658  if (.not. segment%on_pe) cycle
3659 
3660  isd = segment%HI%isd ; ied = segment%HI%ied
3661  jsd = segment%HI%jsd ; jed = segment%HI%jed
3662  isdb = segment%HI%IsdB ; iedb = segment%HI%IedB
3663  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
3664 
3665  ! Fill with T and S values
3666  if (segment%is_E_or_W) then
3667  i=segment%HI%IsdB
3668  do k=1,nz ; do j=segment%HI%jsd,segment%HI%jed
3669  if (segment%direction == obc_direction_w) then
3670  segment%tr_Reg%Tr(1)%t(i,j,k) = tv%T(i+1,j,k)
3671  segment%tr_Reg%Tr(2)%t(i,j,k) = tv%S(i+1,j,k)
3672  else
3673  segment%tr_Reg%Tr(1)%t(i,j,k) = tv%T(i,j,k)
3674  segment%tr_Reg%Tr(2)%t(i,j,k) = tv%S(i,j,k)
3675  endif
3676  enddo ; enddo
3677  else
3678  j=segment%HI%JsdB
3679  do k=1,nz ; do i=segment%HI%isd,segment%HI%ied
3680  if (segment%direction == obc_direction_s) then
3681  segment%tr_Reg%Tr(1)%t(i,j,k) = tv%T(i,j+1,k)
3682  segment%tr_Reg%Tr(2)%t(i,j,k) = tv%S(i,j+1,k)
3683  else
3684  segment%tr_Reg%Tr(1)%t(i,j,k) = tv%T(i,j,k)
3685  segment%tr_Reg%Tr(2)%t(i,j,k) = tv%S(i,j,k)
3686  endif
3687  enddo ; enddo
3688  endif
3689  segment%tr_Reg%Tr(1)%tres(:,:,:) = segment%tr_Reg%Tr(1)%t(:,:,:)
3690  segment%tr_Reg%Tr(2)%tres(:,:,:) = segment%tr_Reg%Tr(2)%t(:,:,:)
3691  enddo
3692 end subroutine fill_temp_salt_segments
3693 
3694 !> Find the region outside of all open boundary segments and
3695 !! make sure it is set to land mask. Gonna need to know global land
3696 !! mask as well to get it right...
3697 subroutine mask_outside_obcs(G, US, param_file, OBC)
3698  type(dyn_horgrid_type), intent(inout) :: G !< Ocean grid structure
3699  type(param_file_type), intent(in) :: param_file !< Parameter file handle
3700  type(ocean_obc_type), pointer :: OBC !< Open boundary structure
3701  type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
3702 
3703  ! Local variables
3704  integer :: isd, ied, IsdB, IedB, jsd, jed, JsdB, JedB, n
3705  integer :: i, j
3706  logical :: fatal_error = .false.
3707  real :: min_depth
3708  integer, parameter :: cin = 3, cout = 4, cland = -1, cedge = -2
3709  character(len=256) :: mesg ! Message for error messages.
3710  type(obc_segment_type), pointer :: segment => null() ! pointer to segment type list
3711  real, allocatable, dimension(:,:) :: color, color2 ! For sorting inside from outside,
3712  ! two different ways
3713 
3714  if (.not. associated(obc)) return
3715 
3716  call get_param(param_file, mdl, "MINIMUM_DEPTH", min_depth, &
3717  units="m", default=0.0, scale=us%m_to_Z, do_not_log=.true.)
3718 
3719  allocate(color(g%isd:g%ied, g%jsd:g%jed)) ; color = 0
3720  allocate(color2(g%isd:g%ied, g%jsd:g%jed)) ; color2 = 0
3721 
3722 
3723  ! Paint a frame around the outside.
3724  do j=g%jsd,g%jed
3725  color(g%isd,j) = cedge
3726  color(g%ied,j) = cedge
3727  color2(g%isd,j) = cedge
3728  color2(g%ied,j) = cedge
3729  enddo
3730  do i=g%isd,g%ied
3731  color(i,g%jsd) = cedge
3732  color(i,g%jed) = cedge
3733  color2(i,g%jsd) = cedge
3734  color2(i,g%jed) = cedge
3735  enddo
3736 
3737  ! Set color to cland in the land. Note that this is before the land
3738  ! mask has been initialized, set mask values based on depth.
3739  do j=g%jsd,g%jed
3740  do i=g%isd,g%ied
3741  if (g%bathyT(i,j) <= min_depth) color(i,j) = cland
3742  if (g%bathyT(i,j) <= min_depth) color2(i,j) = cland
3743  enddo
3744  enddo
3745 
3746  do j=g%jsd,g%jed ; do i=g%IsdB+1,g%IedB-1
3747  if (obc%segment(obc%segnum_u(i,j))%direction == obc_direction_w) then
3748  if (color(i,j) == 0.0) color(i,j) = cout
3749  if (color(i+1,j) == 0.0) color(i+1,j) = cin
3750  elseif (obc%segment(obc%segnum_u(i,j))%direction == obc_direction_e) then
3751  if (color(i,j) == 0.0) color(i,j) = cin
3752  if (color(i+1,j) == 0.0) color(i+1,j) = cout
3753  endif
3754  enddo ; enddo
3755  do j=g%JsdB+1,g%JedB-1 ; do i=g%isd,g%ied
3756  if (obc%segment(obc%segnum_v(i,j))%direction == obc_direction_s) then
3757  if (color(i,j) == 0.0) color(i,j) = cout
3758  if (color(i,j+1) == 0.0) color(i,j+1) = cin
3759  elseif (obc%segment(obc%segnum_v(i,j))%direction == obc_direction_n) then
3760  if (color(i,j) == 0.0) color(i,j) = cin
3761  if (color(i,j+1) == 0.0) color(i,j+1) = cout
3762  endif
3763  enddo ; enddo
3764 
3765  do j=g%JsdB+1,g%JedB-1 ; do i=g%isd,g%ied
3766  if (obc%segment(obc%segnum_v(i,j))%direction == obc_direction_s) then
3767  if (color2(i,j) == 0.0) color2(i,j) = cout
3768  if (color2(i,j+1) == 0.0) color2(i,j+1) = cin
3769  elseif (obc%segment(obc%segnum_v(i,j))%direction == obc_direction_n) then
3770  if (color2(i,j) == 0.0) color2(i,j) = cin
3771  if (color2(i,j+1) == 0.0) color2(i,j+1) = cout
3772  endif
3773  enddo ; enddo
3774  do j=g%jsd,g%jed ; do i=g%IsdB+1,g%IedB-1
3775  if (obc%segment(obc%segnum_u(i,j))%direction == obc_direction_w) then
3776  if (color2(i,j) == 0.0) color2(i,j) = cout
3777  if (color2(i+1,j) == 0.0) color2(i+1,j) = cin
3778  elseif (obc%segment(obc%segnum_u(i,j))%direction == obc_direction_e) then
3779  if (color2(i,j) == 0.0) color2(i,j) = cin
3780  if (color2(i+1,j) == 0.0) color2(i+1,j) = cout
3781  endif
3782  enddo ; enddo
3783 
3784  ! Do the flood fill until there are no more uncolored cells.
3785  call flood_fill(g, color, cin, cout, cland)
3786  call flood_fill2(g, color2, cin, cout, cland)
3787 
3788  ! Use the color to set outside to min_depth on this process.
3789  do j=g%jsd,g%jed ; do i=g%isd,g%ied
3790  if (color(i,j) /= color2(i,j)) then
3791  fatal_error = .true.
3792  write(mesg,'("MOM_open_boundary: problem with OBC segments specification at ",I5,",",I5," during\n", &
3793  "the masking of the outside grid points.")') i, j
3794  call mom_error(warning,"MOM register_tracer: "//mesg, all_print=.true.)
3795  endif
3796  if (color(i,j) == cout) g%bathyT(i,j) = min_depth
3797  enddo ; enddo
3798  if (fatal_error) call mom_error(fatal, &
3799  "MOM_open_boundary: inconsistent OBC segments.")
3800 
3801  deallocate(color)
3802  deallocate(color2)
3803 end subroutine mask_outside_obcs
3804 
3805 !> flood the cin, cout values
3806 subroutine flood_fill(G, color, cin, cout, cland)
3807  type(dyn_horgrid_type), intent(inout) :: G !< Ocean grid structure
3808  real, dimension(:,:), intent(inout) :: color !< For sorting inside from outside
3809  integer, intent(in) :: cin !< color for inside the domain
3810  integer, intent(in) :: cout !< color for outside the domain
3811  integer, intent(in) :: cland !< color for inside the land mask
3812 
3813 ! Local variables
3814  integer :: i, j, ncount
3815 
3816  ncount = 1
3817  do while (ncount > 0)
3818  ncount = 0
3819  do j=g%jsd+1,g%jed-1
3820  do i=g%isd+1,g%ied-1
3821  if (color(i,j) == 0.0 .and. color(i-1,j) > 0.0) then
3822  color(i,j) = color(i-1,j)
3823  ncount = ncount + 1
3824  endif
3825  if (color(i,j) == 0.0 .and. color(i+1,j) > 0.0) then
3826  color(i,j) = color(i+1,j)
3827  ncount = ncount + 1
3828  endif
3829  if (color(i,j) == 0.0 .and. color(i,j-1) > 0.0) then
3830  color(i,j) = color(i,j-1)
3831  ncount = ncount + 1
3832  endif
3833  if (color(i,j) == 0.0 .and. color(i,j+1) > 0.0) then
3834  color(i,j) = color(i,j+1)
3835  ncount = ncount + 1
3836  endif
3837  enddo
3838  enddo
3839  do j=g%jed-1,g%jsd+1,-1
3840  do i=g%ied-1,g%isd+1,-1
3841  if (color(i,j) == 0.0 .and. color(i-1,j) > 0.0) then
3842  color(i,j) = color(i-1,j)
3843  ncount = ncount + 1
3844  endif
3845  if (color(i,j) == 0.0 .and. color(i+1,j) > 0.0) then
3846  color(i,j) = color(i+1,j)
3847  ncount = ncount + 1
3848  endif
3849  if (color(i,j) == 0.0 .and. color(i,j-1) > 0.0) then
3850  color(i,j) = color(i,j-1)
3851  ncount = ncount + 1
3852  endif
3853  if (color(i,j) == 0.0 .and. color(i,j+1) > 0.0) then
3854  color(i,j) = color(i,j+1)
3855  ncount = ncount + 1
3856  endif
3857  enddo
3858  enddo
3859  call pass_var(color, g%Domain)
3860  call sum_across_pes(ncount)
3861  enddo
3862 
3863 end subroutine flood_fill
3864 
3865 !> flood the cin, cout values
3866 subroutine flood_fill2(G, color, cin, cout, cland)
3867  type(dyn_horgrid_type), intent(inout) :: G !< Ocean grid structure
3868  real, dimension(:,:), intent(inout) :: color !< For sorting inside from outside
3869  integer, intent(in) :: cin !< color for inside the domain
3870  integer, intent(in) :: cout !< color for outside the domain
3871  integer, intent(in) :: cland !< color for inside the land mask
3872 
3873 ! Local variables
3874  integer :: i, j, ncount
3875 
3876  ncount = 1
3877  do while (ncount > 0)
3878  ncount = 0
3879  do i=g%isd+1,g%ied-1
3880  do j=g%jsd+1,g%jed-1
3881  if (color(i,j) == 0.0 .and. color(i-1,j) > 0.0) then
3882  color(i,j) = color(i-1,j)
3883  ncount = ncount + 1
3884  endif
3885  if (color(i,j) == 0.0 .and. color(i+1,j) > 0.0) then
3886  color(i,j) = color(i+1,j)
3887  ncount = ncount + 1
3888  endif
3889  if (color(i,j) == 0.0 .and. color(i,j-1) > 0.0) then
3890  color(i,j) = color(i,j-1)
3891  ncount = ncount + 1
3892  endif
3893  if (color(i,j) == 0.0 .and. color(i,j+1) > 0.0) then
3894  color(i,j) = color(i,j+1)
3895  ncount = ncount + 1
3896  endif
3897  enddo
3898  enddo
3899  do i=g%ied-1,g%isd+1,-1
3900  do j=g%jed-1,g%jsd+1,-1
3901  if (color(i,j) == 0.0 .and. color(i-1,j) > 0.0) then
3902  color(i,j) = color(i-1,j)
3903  ncount = ncount + 1
3904  endif
3905  if (color(i,j) == 0.0 .and. color(i+1,j) > 0.0) then
3906  color(i,j) = color(i+1,j)
3907  ncount = ncount + 1
3908  endif
3909  if (color(i,j) == 0.0 .and. color(i,j-1) > 0.0) then
3910  color(i,j) = color(i,j-1)
3911  ncount = ncount + 1
3912  endif
3913  if (color(i,j) == 0.0 .and. color(i,j+1) > 0.0) then
3914  color(i,j) = color(i,j+1)
3915  ncount = ncount + 1
3916  endif
3917  enddo
3918  enddo
3919  call pass_var(color, g%Domain)
3920  call sum_across_pes(ncount)
3921  enddo
3922 
3923 end subroutine flood_fill2
3924 
3925 !> Register OBC segment data for restarts
3926 subroutine open_boundary_register_restarts(HI, GV, OBC_CS,restart_CSp)
3927  type(hor_index_type), intent(in) :: hi !< Horizontal indices
3928  type(verticalgrid_type), pointer :: gv !< Container for vertical grid information
3929  type(ocean_obc_type), pointer :: obc_cs !< OBC data structure, data intent(inout)
3930  type(mom_restart_cs), pointer :: restart_csp !< Restart structure, data intent(inout)
3931  ! Local variables
3932  type(vardesc) :: vd
3933 
3934  if (.not. associated(obc_cs)) &
3935  call mom_error(fatal, "open_boundary_register_restarts: Called with "//&
3936  "uninitialized OBC control structure")
3937 
3938  if (associated(obc_cs%rx_normal) .or. associated(obc_cs%ry_normal)) &
3939  call mom_error(fatal, "open_boundary_register_restarts: Restart "//&
3940  "arrays were previously allocated")
3941 
3942  ! *** This is a temporary work around for restarts with OBC segments.
3943  ! This implementation uses 3D arrays solely for restarts. We need
3944  ! to be able to add 2D ( x,z or y,z ) data to restarts to avoid using
3945  ! so much memory and disk space. ***
3946  if (obc_cs%radiation_BCs_exist_globally .or. obc_cs%oblique_BCs_exist_globally) then
3947  allocate(obc_cs%rx_normal(hi%isdB:hi%iedB,hi%jsd:hi%jed,gv%ke))
3948  obc_cs%rx_normal(:,:,:) = 0.0
3949  vd = var_desc("rx_normal","m s-1", "Normal Phase Speed for EW OBCs",'u','L')
3950  call register_restart_field(obc_cs%rx_normal, vd, .true., restart_csp)
3951  allocate(obc_cs%ry_normal(hi%isd:hi%ied,hi%jsdB:hi%jedB,gv%ke))
3952  obc_cs%ry_normal(:,:,:) = 0.0
3953  vd = var_desc("ry_normal","m s-1", "Normal Phase Speed for NS OBCs",'v','L')
3954  call register_restart_field(obc_cs%ry_normal, vd, .true., restart_csp)
3955  endif
3956  if (obc_cs%oblique_BCs_exist_globally) then
3957  allocate(obc_cs%cff_normal(hi%IsdB:hi%IedB,hi%jsdB:hi%jedB,gv%ke))
3958  obc_cs%cff_normal(:,:,:) = 0.0
3959  vd = var_desc("cff_normal","m s-1", "denominator for oblique OBCs",'q','L')
3960  call register_restart_field(obc_cs%cff_normal, vd, .true., restart_csp)
3961  endif
3962 
3963 end subroutine open_boundary_register_restarts
3964 
3965 !> \namespace mom_open_boundary
3966 !! This module implements some aspects of internal open boundary
3967 !! conditions in MOM.
3968 !!
3969 !! A small fragment of the grid is shown below:
3970 !!
3971 !! j+1 x ^ x ^ x At x: q, CoriolisBu
3972 !! j+1 > o > o > At ^: v, tauy
3973 !! j x ^ x ^ x At >: u, taux
3974 !! j > o > o > At o: h, bathyT, buoy, tr, T, S, Rml, ustar
3975 !! j-1 x ^ x ^ x
3976 !! i-1 i i+1 At x & ^:
3977 !! i i+1 At > & o:
3978 !!
3979 !! The boundaries always run through q grid points (x).
3980 
3981 end module mom_open_boundary
mom_open_boundary::obc_segment_data_type
Open boundary segment data from files (mostly).
Definition: MOM_open_boundary.F90:69
mom_regridding::regridding_cs
Regridding control structure.
Definition: MOM_regridding.F90:45
mom_verticalgrid
Provides a transparent vertical ocean grid type and supporting routines.
Definition: MOM_verticalGrid.F90:2
mom_tracer_registry::tracer_type
The tracer type.
Definition: MOM_tracer_registry.F90:37
mom_file_parser::log_version
An overloaded interface to log version information about modules.
Definition: MOM_file_parser.F90:109
mom_diag_mediator
The subroutines here provide convenient wrappers to the fms diag_manager interfaces with additional d...
Definition: MOM_diag_mediator.F90:3
mom_variables::thermo_var_ptrs
Pointers to an assortment of thermodynamic fields that may be available, including potential temperat...
Definition: MOM_variables.F90:82
mom_open_boundary::obc_struct_type
Type to carry something (what] for the OBC registry.
Definition: MOM_open_boundary.F90:267
mom_dyn_horgrid
Contains a shareable dynamic type for describing horizontal grids and metric data and utilty routines...
Definition: MOM_dyn_horgrid.F90:3
mom_string_functions
Handy functions for manipulating strings.
Definition: MOM_string_functions.F90:2
mom_domains::pass_var
Do a halo update on an array.
Definition: MOM_domains.F90:49
mom_remapping::remapping_cs
Container for remapping parameters.
Definition: MOM_remapping.F90:22
mom_tracer_registry
This module contains the tracer_registry_type and the subroutines that handle registration of tracers...
Definition: MOM_tracer_registry.F90:5
mom_file_parser::param_file_type
A structure that can be parsed to read and document run-time parameters.
Definition: MOM_file_parser.F90:54
mom_file_parser::get_param
An overloaded interface to read and log the values of various types of parameters.
Definition: MOM_file_parser.F90:102
mom_io
This module contains I/O framework code.
Definition: MOM_io.F90:2
mom_restart::mom_restart_cs
A restart registry and the control structure for restarts.
Definition: MOM_restart.F90:72
mom_unit_scaling::unit_scale_type
Describes various unit conversion factors.
Definition: MOM_unit_scaling.F90:14
mom_coms
Interfaces to non-domain-oriented communication subroutines, including the MOM6 reproducing sums faci...
Definition: MOM_coms.F90:3
mom_remapping
Provides column-wise vertical remapping functions.
Definition: MOM_remapping.F90:2
mom_domains::pass_vector
Do a halo update on a pair of arrays representing the two components of a vector.
Definition: MOM_domains.F90:54
mom_open_boundary::file_obc_cs
Control structure for open boundaries that read from files. Probably lots to update here.
Definition: MOM_open_boundary.F90:262
mom_open_boundary::obc_registry_type
Type to carry basic OBC information needed for updating values.
Definition: MOM_open_boundary.F90:272
mom_verticalgrid::verticalgrid_type
Describes the vertical ocean grid, including unit conversion factors.
Definition: MOM_verticalGrid.F90:24
mom_restart
The MOM6 facility for reading and writing restart files, and querying what has been read.
Definition: MOM_restart.F90:2
mom_domains
Describes the decomposed MOM domain and has routines for communications across PEs.
Definition: MOM_domains.F90:2
mom_variables
Provides transparent structures with groups of MOM6 variables and supporting routines.
Definition: MOM_variables.F90:2
mom_open_boundary::obc_segment_tracer_type
Tracer segment data structure, for putting into an array of objects, not all the same shape.
Definition: MOM_open_boundary.F90:88
mom_open_boundary
Controls where open boundary conditions are applied.
Definition: MOM_open_boundary.F90:2
mom_cpu_clock
Wraps the MPP cpu clock functions.
Definition: MOM_cpu_clock.F90:2
mom_file_parser
The MOM6 facility to parse input files for runtime parameters.
Definition: MOM_file_parser.F90:2
mom_tracer_registry::tracer_registry_type
Type to carry basic tracer information.
Definition: MOM_tracer_registry.F90:122
mom_grid
Provides the ocean grid type.
Definition: MOM_grid.F90:2
mom_open_boundary::ocean_obc_type
Open-boundary data.
Definition: MOM_open_boundary.F90:186
mom_restart::register_restart_field
Register fields for restarts.
Definition: MOM_restart.F90:107
mom_unit_scaling
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Definition: MOM_unit_scaling.F90:2
mom_io::vardesc
Type for describing a variable, typically a tracer.
Definition: MOM_io.F90:53
mom_regridding
Generates vertical grids as part of the ALE algorithm.
Definition: MOM_regridding.F90:2
mom_file_parser::log_param
An overloaded interface to log the values of various types of parameters.
Definition: MOM_file_parser.F90:96
mom_obsolete_params
Methods for testing for, and list of, obsolete run-time parameters.
Definition: MOM_obsolete_params.F90:2
mom_open_boundary::obc_segment_type
Open boundary segment data structure.
Definition: MOM_open_boundary.F90:107
mom_error_handler
Routines for error handling and I/O management.
Definition: MOM_error_handler.F90:2
mom_dyn_horgrid::dyn_horgrid_type
Describes the horizontal ocean grid with only dynamic memory arrays.
Definition: MOM_dyn_horgrid.F90:22
mom_grid::ocean_grid_type
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:25
mom_open_boundary::segment_tracer_registry_type
Registry type for tracers on segments.
Definition: MOM_open_boundary.F90:98
mom_diag_mediator::diag_ctrl
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Definition: MOM_diag_mediator.F90:239