MOM6
mom_open_boundary Module Reference

Detailed Description

Controls where open boundary conditions are applied.

This module implements some aspects of internal open boundary conditions in MOM.

A small fragment of the grid is shown below:

j+1 x ^ x ^ x At x: q, CoriolisBu j+1 > o > o > At ^: v, tauy j x ^ x ^ x At >: u, taux j > o > o > At o: h, bathyT, buoy, tr, T, S, Rml, ustar j-1 x ^ x ^ x i-1 i i+1 At x & ^: i i+1 At > & o:

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

Data Types

type  file_obc_cs
 Control structure for open boundaries that read from files. Probably lots to update here. More...
 
type  obc_registry_type
 Type to carry basic OBC information needed for updating values. More...
 
type  obc_segment_data_type
 Open boundary segment data from files (mostly). More...
 
type  obc_segment_type
 Open boundary segment data structure. More...
 
type  obc_struct_type
 Type to carry something (what] for the OBC registry. More...
 
type  ocean_obc_type
 Open-boundary data. More...
 

Functions/Subroutines

subroutine, public open_boundary_config (G, param_file, OBC)
 Enables OBC module and reads configuration parameters This routine is called from MOM_initialize_fixed which occurs before the initialization of the vertical coordinate and ALE_init. Therefore segment data are not fully initialized here. The remainder of the segment data are initialized in a later call to update_open_boundary_data. More...
 
subroutine initialize_segment_data (G, OBC, PF)
 
subroutine setup_segment_indices (G, seg, Is_obc, Ie_obc, Js_obc, Je_obc)
 
subroutine setup_u_point_obc (OBC, G, segment_str, l_seg)
 Parse an OBC_SEGMENT_%%% string starting with "I=" and configure placement and type of OBC accordingly. More...
 
subroutine setup_v_point_obc (OBC, G, segment_str, l_seg)
 Parse an OBC_SEGMENT_%%% string starting with "J=" and configure placement and type of OBC accordingly. More...
 
subroutine parse_segment_str (ni_global, nj_global, segment_str, l, m, n, action_str)
 Parse an OBC_SEGMENT_%%% string. More...
 
subroutine parse_segment_data_str (segment_str, var, value, filenam, fieldnam, fields, num_fields, debug)
 Parse an OBC_SEGMENT_%%_DATA string. More...
 
subroutine, public open_boundary_init (G, param_file, OBC)
 Initialize open boundary control structure. More...
 
logical function, public open_boundary_query (OBC, apply_open_OBC, apply_specified_OBC, apply_Flather_OBC, apply_nudged_OBC, needs_ext_seg_data)
 
subroutine open_boundary_dealloc (OBC)
 Deallocate open boundary data. More...
 
subroutine, public open_boundary_end (OBC)
 Close open boundary data. More...
 
subroutine, public open_boundary_impose_normal_slope (OBC, G, depth)
 Sets the slope of bathymetry normal to an open bounndary to zero. More...
 
subroutine, public open_boundary_impose_land_mask (OBC, G, areaCu, areaCv)
 Reconcile masks and open boundaries, deallocate OBC on PEs where it is not needed. Also adjust u- and v-point cell area on specified open boundaries. More...
 
subroutine, public radiation_open_bdry_conds (OBC, u_new, u_old, v_new, v_old, G, dt)
 Apply radiation conditions to 3D u,v at open boundaries. More...
 
subroutine, public open_boundary_apply_normal_flow (OBC, G, u, v)
 Applies OBC values stored in segments to 3d u,v fields. More...
 
subroutine, public open_boundary_zero_normal_flow (OBC, G, u, v)
 Applies zero values to 3d u,v fields on OBC segments. More...
 
subroutine gradient_at_q_points (G, segment, uvel, vvel)
 Calculate the tangential gradient of the normal flow at the boundary q-points. More...
 
subroutine, public set_tracer_data (OBC, tv, h, G, PF, tracer_Reg)
 Sets the initial values of the tracer and h open boundary conditions. Also allocates and fills the segmentT and segmentS arrays, but they are not yet used anywhere. More...
 
integer function lookup_seg_field (OBC_seg, field)
 
subroutine allocate_obc_segment_data (OBC, segment)
 Allocate segment data fields. More...
 
subroutine, public open_boundary_test_extern_uv (G, OBC, u, v)
 Set tangential velocities outside of open boundaries to silly values (used for checking the interior state is independent of values outside of the domain). More...
 
subroutine, public open_boundary_test_extern_h (G, OBC, h)
 Set thicknesses outside of open boundaries to silly values (used for checking the interior state is independent of values outside of the domain). More...
 
subroutine, public update_obc_segment_data (G, GV, OBC, tv, h, Time)
 Update the OBC values on the segments. More...
 
subroutine, public register_obc (name, param_file, Reg)
 register open boundary objects for boundary updates. More...
 
subroutine, public obc_registry_init (param_file, Reg)
 This routine include declares and sets the variable "version". More...
 
logical function, public register_file_obc (param_file, CS, OBC_Reg)
 Add file to OBC registry. More...
 
subroutine, public file_obc_end (CS)
 Clean up the file OBC from registry. More...
 

Variables

integer, parameter, public obc_none = 0
 
integer, parameter, public obc_simple = 1
 
integer, parameter, public obc_wall = 2
 
integer, parameter, public obc_flather = 3
 
integer, parameter, public obc_radiation = 4
 
integer, parameter, public obc_direction_n = 100
 Indicates the boundary is an effective northern boundary. More...
 
integer, parameter, public obc_direction_s = 200
 Indicates the boundary is an effective southern boundary. More...
 
integer, parameter, public obc_direction_e = 300
 Indicates the boundary is an effective eastern boundary. More...
 
integer, parameter, public obc_direction_w = 400
 Indicates the boundary is an effective western boundary. More...
 
integer, parameter max_obc_fields = 100
 Maximum number of data fields needed for OBC segments. More...
 
integer id_clock_pass
 
character(len=40) mdl = "MOM_open_boundary"
 

Function/Subroutine Documentation

◆ allocate_obc_segment_data()

subroutine mom_open_boundary::allocate_obc_segment_data ( type(ocean_obc_type), pointer  OBC,
type(obc_segment_type), intent(inout)  segment 
)
private

Allocate segment data fields.

Parameters
obcOpen boundary structure
[in,out]segmentOpen boundary segment

Definition at line 1670 of file MOM_open_boundary.F90.

Referenced by setup_u_point_obc(), and setup_v_point_obc().

1670  type(ocean_obc_type), pointer :: obc !< Open boundary structure
1671  type(obc_segment_type), intent(inout) :: segment !< Open boundary segment
1672  ! Local variables
1673  integer :: isd, ied, jsd, jed
1674  integer :: isdb, iedb, jsdb, jedb
1675  character(len=40) :: mdl = "allocate_OBC_segment_data" ! This subroutine's name.
1676 
1677  isd = segment%HI%isd ; ied = segment%HI%ied
1678  jsd = segment%HI%jsd ; jed = segment%HI%jed
1679  isdb = segment%HI%IsdB ; iedb = segment%HI%IedB
1680  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
1681 
1682  if (.not. segment%on_pe) return
1683 
1684  if (segment%is_E_or_W) then
1685  ! If these are just Flather, change update_OBC_segment_data accordingly
1686  allocate(segment%Cg(isdb:iedb,jsd:jed)); segment%Cg(:,:)=0.
1687  allocate(segment%Htot(isdb:iedb,jsd:jed)); segment%Htot(:,:)=0.0
1688  allocate(segment%h(isdb:iedb,jsd:jed,obc%ke)); segment%h(:,:,:)=0.0
1689  allocate(segment%eta(isdb:iedb,jsd:jed)); segment%eta(:,:)=0.0
1690  allocate(segment%normal_trans_bt(isdb:iedb,jsd:jed)); segment%normal_trans_bt(:,:)=0.0
1691  allocate(segment%rx_normal(isdb:iedb,jsd:jed,obc%ke)); segment%rx_normal(:,:,:)=0.0
1692  allocate(segment%normal_vel(isdb:iedb,jsd:jed,obc%ke)); segment%normal_vel(:,:,:)=0.0
1693  allocate(segment%normal_vel_bt(isdb:iedb,jsd:jed)); segment%normal_vel_bt(:,:)=0.0
1694  allocate(segment%normal_trans(isdb:iedb,jsd:jed,obc%ke)); segment%normal_trans(:,:,:)=0.0
1695  if (segment%nudged) then
1696  allocate(segment%nudged_normal_vel(isdb:iedb,jsd:jed,obc%ke)); segment%nudged_normal_vel(:,:,:)=0.0
1697  endif
1698  endif
1699 
1700  if (segment%is_N_or_S) then
1701  ! If these are just Flather, change update_OBC_segment_data accordingly
1702  allocate(segment%Cg(isd:ied,jsdb:jedb)); segment%Cg(:,:)=0.
1703  allocate(segment%Htot(isd:ied,jsdb:jedb)); segment%Htot(:,:)=0.0
1704  allocate(segment%h(isd:ied,jsdb:jedb,obc%ke)); segment%h(:,:,:)=0.0
1705  allocate(segment%eta(isd:ied,jsdb:jedb)); segment%eta(:,:)=0.0
1706  allocate(segment%normal_trans_bt(isd:ied,jsdb:jedb)); segment%normal_trans_bt(:,:)=0.0
1707  allocate(segment%rx_normal(isd:ied,jsdb:jedb,obc%ke)); segment%rx_normal(:,:,:)=0.0
1708  allocate(segment%normal_vel(isd:ied,jsdb:jedb,obc%ke)); segment%normal_vel(:,:,:)=0.0
1709  allocate(segment%normal_vel_bt(isd:ied,jsdb:jedb)); segment%normal_vel_bt(:,:)=0.0
1710  allocate(segment%normal_trans(isd:ied,jsdb:jedb,obc%ke)); segment%normal_trans(:,:,:)=0.0
1711  if (segment%nudged) then
1712  allocate(segment%nudged_normal_vel(isd:ied,jsdb:jedb,obc%ke)); segment%nudged_normal_vel(:,:,:)=0.0
1713  endif
1714  endif
1715 
Here is the caller graph for this function:

◆ file_obc_end()

subroutine, public mom_open_boundary::file_obc_end ( type(file_obc_cs), pointer  CS)

Clean up the file OBC from registry.

Parameters
csOBC file control structure.

Definition at line 2079 of file MOM_open_boundary.F90.

Referenced by mom_boundary_update::obc_register_end().

2079  type(file_obc_cs), pointer :: cs !< OBC file control structure.
2080 
2081  if (associated(cs)) then
2082  deallocate(cs)
2083  endif
Here is the caller graph for this function:

◆ gradient_at_q_points()

subroutine mom_open_boundary::gradient_at_q_points ( type(ocean_grid_type), intent(in)  G,
type(obc_segment_type), pointer  segment,
real, dimension( g %isdb: g %iedb, g %jsd: g %jed, g %ke), intent(in)  uvel,
real, dimension( g %isd: g %ied, g %jsdb: g %jedb, g %ke), intent(in)  vvel 
)
private

Calculate the tangential gradient of the normal flow at the boundary q-points.

Parameters
[in]gOcean grid structure
segmentOBC segment structure
[in]uvelzonal velocity
[in]vvelmeridional velocity

Definition at line 1455 of file MOM_open_boundary.F90.

References obc_direction_e, and obc_direction_n.

Referenced by radiation_open_bdry_conds().

1455  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
1456  type(obc_segment_type), pointer :: segment !< OBC segment structure
1457  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: uvel !< zonal velocity
1458  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: vvel !< meridional velocity
1459  integer :: i,j,k
1460 
1461  if (.not. segment%on_pe) return
1462 
1463  if (segment%is_E_or_W) then
1464 
1465  if (.not.ASSOCIATED(segment%grad_normal)) then
1466  allocate(segment%grad_normal(segment%HI%JscB:segment%HI%JecB,2,g%ke))
1467  endif
1468 
1469  if (segment%direction == obc_direction_e) then
1470  i=segment%HI%iscB
1471  do k=1,g%ke
1472  do j=segment%HI%JscB,segment%HI%JecB
1473  segment%grad_normal(j,1,k) = (uvel(i-1,j+1,k)-uvel(i-1,j,k)) * g%mask2dBu(i-1,j)
1474  segment%grad_normal(j,2,k) = (uvel(i,j+1,k)-uvel(i,j,k)) * g%mask2dBu(i,j)
1475  enddo
1476  enddo
1477  else ! western segment
1478  i=segment%HI%iscB
1479  do k=1,g%ke
1480  do j=segment%HI%JscB,segment%HI%JecB
1481  segment%grad_normal(j,1,k) = (uvel(i+1,j+1,k)-uvel(i+1,j,k)) * g%mask2dBu(i+1,j)
1482  segment%grad_normal(j,2,k) = (uvel(i,j+1,k)-uvel(i,j,k)) * g%mask2dBu(i,j)
1483  enddo
1484  enddo
1485  endif
1486  else if (segment%is_N_or_S) then
1487 
1488  if (.not.ASSOCIATED(segment%grad_normal)) then
1489  allocate(segment%grad_normal(segment%HI%IscB:segment%HI%IecB,2,g%ke))
1490  endif
1491 
1492  if (segment%direction == obc_direction_n) then
1493  j=segment%HI%jscB
1494  do k=1,g%ke
1495  do i=segment%HI%IscB,segment%HI%IecB
1496  segment%grad_normal(i,1,k) = (vvel(i+1,j-1,k)-vvel(i,j-1,k)) * g%mask2dBu(i,j-1)
1497  segment%grad_normal(i,2,k) = (vvel(i+1,j,k)-vvel(i,j,k)) * g%mask2dBu(i,j)
1498  enddo
1499  enddo
1500  else ! south segment
1501  j=segment%HI%jscB
1502  do k=1,g%ke
1503  do i=segment%HI%IscB,segment%HI%IecB
1504  segment%grad_normal(i,1,k) = (vvel(i+1,j+1,k)-vvel(i,j+1,k)) * g%mask2dBu(i,j+1)
1505  segment%grad_normal(i,2,k) = (vvel(i+1,j,k)-vvel(i,j,k)) * g%mask2dBu(i,j)
1506  enddo
1507  enddo
1508  endif
1509  endif
1510 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
Here is the caller graph for this function:

◆ initialize_segment_data()

subroutine mom_open_boundary::initialize_segment_data ( type(dyn_horgrid_type), intent(in)  G,
type(ocean_obc_type), intent(inout)  OBC,
type(param_file_type), intent(in)  PF 
)
private
Parameters
[in]gOcean grid structure
[in,out]obcOpen boundary control structure
[in]pfParameter file handle

Definition at line 331 of file MOM_open_boundary.F90.

References mdl, mom_error_handler::mom_error(), and parse_segment_data_str().

Referenced by open_boundary_config().

331  use mpp_mod, only : mpp_pe, mpp_set_current_pelist, mpp_get_current_pelist,mpp_npes
332 
333  type(dyn_horgrid_type), intent(in) :: g !< Ocean grid structure
334  type(ocean_obc_type), intent(inout) :: obc !< Open boundary control structure
335  type(param_file_type), intent(in) :: pf !< Parameter file handle
336 
337  integer :: n,m,num_fields
338  character(len=256) :: segstr, filename
339  character(len=20) :: segnam, suffix
340  character(len=32) :: varnam, fieldname
341  real :: value
342  integer :: orient
343  character(len=32), dimension(MAX_OBC_FIELDS) :: fields ! segment field names
344  character(len=128) :: inputdir
345  type(obc_segment_type), pointer :: segment ! pointer to segment type list
346  character(len=32) :: remappingscheme
347  logical :: check_reconstruction, check_remapping, force_bounds_in_subcell
348  integer, dimension(4) :: siz,siz2
349  integer :: is, ie, js, je
350  integer :: isd, ied, jsd, jed
351  integer :: isdb, iedb, jsdb, jedb
352  integer, dimension(:), allocatable :: saved_pelist
353  integer :: current_pe
354  integer, dimension(1) :: single_pelist
355 
356  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
357 
358  ! There is a problem with the order of the OBC initialization
359  ! with respect to ALE_init. Currently handling this by copying the
360  ! param file so that I can use it later in step_MOM in order to finish
361  ! initializing segments on the first step.
362 
363  call get_param(pf, mdl, "INPUTDIR", inputdir, default=".")
364  inputdir = slasher(inputdir)
365 
366  call get_param(pf, mdl, "REMAPPING_SCHEME", remappingscheme, &
367  "This sets the reconstruction scheme used\n"//&
368  "for vertical remapping for all variables.\n"//&
369  "It can be one of the following schemes:\n"//&
370  trim(remappingschemesdoc), default=remappingdefaultscheme,do_not_log=.true.)
371  call get_param(pf, mdl, "FATAL_CHECK_RECONSTRUCTIONS", check_reconstruction, &
372  "If true, cell-by-cell reconstructions are checked for\n"//&
373  "consistency and if non-monotonicity or an inconsistency is\n"//&
374  "detected then a FATAL error is issued.", default=.false.,do_not_log=.true.)
375  call get_param(pf, mdl, "FATAL_CHECK_REMAPPING", check_remapping, &
376  "If true, the results of remapping are checked for\n"//&
377  "conservation and new extrema and if an inconsistency is\n"//&
378  "detected then a FATAL error is issued.", default=.false.,do_not_log=.true.)
379  call get_param(pf, mdl, "REMAP_BOUND_INTERMEDIATE_VALUES", force_bounds_in_subcell, &
380  "If true, the values on the intermediate grid used for remapping\n"//&
381  "are forced to be bounded, which might not be the case due to\n"//&
382  "round off.", default=.false.,do_not_log=.true.)
383 
384  allocate(obc%remap_CS)
385  call initialize_remapping(obc%remap_CS, remappingscheme, boundary_extrapolation = .false., &
386  check_reconstruction=check_reconstruction, &
387  check_remapping=check_remapping, force_bounds_in_subcell=force_bounds_in_subcell)
388 
389  if (obc%user_BCs_set_globally) return
390 
391 
392  !< temporarily disable communication in order to read segment data independently
393 
394  allocate(saved_pelist(0:mpp_npes()-1))
395  call mpp_get_current_pelist(saved_pelist)
396  current_pe = mpp_pe()
397  single_pelist(1) = current_pe
398  call mpp_set_current_pelist(single_pelist)
399 
400  do n=1, obc%number_of_segments
401  segment => obc%segment(n)
402 
403  write(segnam,"('OBC_SEGMENT_',i3.3,'_DATA')") n
404  write(suffix,"('_segment_',i3.3)") n
405  call get_param(pf, mdl, segnam, segstr)
406 
407  call parse_segment_data_str(trim(segstr), fields=fields, num_fields=num_fields)
408  if (num_fields == 0) then
409  print *,'num_fields = 0';cycle ! cycle to next segment
410  endif
411 
412  allocate(segment%field(num_fields))
413 
414  if (segment%Flather) then
415  if (num_fields /= 3) call mom_error(fatal, &
416  "MOM_open_boundary, initialize_segment_data: "//&
417  "Need three inputs for Flather")
418 
419  segment%num_fields = 3 ! these are the input fields required for the Flather option
420  ! note that this is assuming that the inputs are coming in this order
421  ! and independent of the input param string . Needs cleanup - mjh
422  allocate(segment%field_names(segment%num_fields))
423  segment%field_names(:)='None'
424  segment%field_names(1)='UO'
425  segment%field_names(2)='VO'
426  segment%field_names(3)='ZOS'
427  endif
428 
429 !!
430 ! CODE HERE FOR OTHER OPTIONS (CLAMPED, NUDGED,..)
431 !!
432 
433  isd = segment%HI%isd ; ied = segment%HI%ied
434  jsd = segment%HI%jsd ; jed = segment%HI%jed
435  isdb = segment%HI%IsdB ; iedb = segment%HI%IedB
436  jsdb = segment%HI%JsdB ; jedb = segment%HI%JedB
437 
438 
439  do m=1,num_fields
440  call parse_segment_data_str(trim(segstr), var=trim(fields(m)), value=value, filenam=filename, fieldnam=fieldname)
441  if (trim(filename) /= 'none') then
442  obc%update_OBC = .true. ! Data is assumed to be time-dependent if we are reading from file
443  obc%needs_IO_for_data = .true. ! At least one segment is using I/O for OBC data
444 
445  segment%values_needed = .true. ! Indicates that i/o will be needed for this segment
446  segment%field(m)%name = trim(fields(m))
447  filename = trim(inputdir)//trim(filename)
448  fieldname = trim(fieldname)//trim(suffix)
449  call field_size(filename,fieldname,siz,no_domain=.true.)
450  if (siz(4) == 1) segment%values_needed = .false.
451  if (segment%on_pe) then
452  if (modulo(siz(1),2) == 0 .or. modulo(siz(2),2) == 0) then
453  call mom_error(fatal,'segment data are not on the supergrid')
454  endif
455  siz2(1)=1
456  if (siz(1)>1) then
457  siz2(1)=(siz(1)-1)/2
458  endif
459  siz2(2)=1
460  if (siz(2)>1) then
461  siz2(2)=(siz(2)-1)/2
462  endif
463  siz2(3)=siz(3)
464 
465  if (segment%is_E_or_W) then
466  allocate(segment%field(m)%buffer_src(isdb:iedb,jsd:jed,siz2(3)))
467  else
468  allocate(segment%field(m)%buffer_src(isd:ied,jsdb:jedb,siz2(3)))
469  endif
470  segment%field(m)%buffer_src(:,:,:)=0.0
471  segment%field(m)%fid = init_external_field(trim(filename),&
472  trim(fieldname),ignore_axis_atts=.true.,threading=single_file)
473  if (siz(3) > 1) then
474  fieldname = 'dz_'//trim(fieldname)
475  call field_size(filename,fieldname,siz,no_domain=.true.)
476  if (segment%is_E_or_W) then
477  allocate(segment%field(m)%dz_src(isdb:iedb,jsd:jed,siz(3)))
478  else
479  allocate(segment%field(m)%dz_src(isd:ied,jsdb:jedb,siz(3)))
480  endif
481  segment%field(m)%dz_src(:,:,:)=0.0
482  segment%field(m)%nk_src=siz(3)
483  segment%field(m)%fid_dz = init_external_field(trim(filename),trim(fieldname),&
484  ignore_axis_atts=.true.,threading=single_file)
485  else
486  segment%field(m)%nk_src=1
487  endif
488  endif
489  else
490  segment%field(m)%fid = -1
491  segment%field(m)%value = value
492  endif
493  enddo
494  enddo
495 
496  call mpp_set_current_pelist(saved_pelist)
497 
Here is the call graph for this function:
Here is the caller graph for this function:

◆ lookup_seg_field()

integer function mom_open_boundary::lookup_seg_field ( type(obc_segment_type), pointer  OBC_seg,
character(len=32), intent(in)  field 
)
private

Definition at line 1649 of file MOM_open_boundary.F90.

1649  type(obc_segment_type), pointer :: obc_seg
1650  character(len=32), intent(in) :: field ! The field name
1651  integer :: lookup_seg_field
1652 
1653  integer :: n,m
1654 
1655  lookup_seg_field=-1
1656  do n=1,obc_seg%num_fields
1657  if (trim(field) == obc_seg%field_names(n)) then
1658  lookup_seg_field=n
1659  return
1660  endif
1661  enddo
1662 
1663  return
1664 

◆ obc_registry_init()

subroutine, public mom_open_boundary::obc_registry_init ( type(param_file_type), intent(in)  param_file,
type(obc_registry_type), pointer  Reg 
)

This routine include declares and sets the variable "version".

Parameters
[in]param_fileopen file to parse for model parameters
regpointer to OBC registry

Definition at line 2032 of file MOM_open_boundary.F90.

Referenced by register_obc().

2032  type(param_file_type), intent(in) :: param_file !< open file to parse for model parameters
2033  type(obc_registry_type), pointer :: reg !< pointer to OBC registry
2034 
2035  integer, save :: init_calls = 0
2036 
2037 #include "version_variable.h"
2038  character(len=40) :: mdl = "MOM_open_boundary" ! This module's name.
2039  character(len=256) :: mesg ! Message for error messages.
2040 
2041  if (.not.associated(reg)) then ; allocate(reg)
2042  else ; return ; endif
2043 
2044  ! Read all relevant parameters and write them to the model log.
2045 ! call log_version(param_file, mdl,s version, "")
2046 
2047  init_calls = init_calls + 1
2048  if (init_calls > 1) then
2049  write(mesg,'("OBC_registry_init called ",I3, &
2050  &" times with different registry pointers.")') init_calls
2051  if (is_root_pe()) call mom_error(warning,"MOM_open_boundary"//mesg)
2052  endif
2053 
Here is the caller graph for this function:

◆ open_boundary_apply_normal_flow()

subroutine, public mom_open_boundary::open_boundary_apply_normal_flow ( type(ocean_obc_type), pointer  OBC,
type(ocean_grid_type), intent(inout)  G,
real, dimension( g %isdb: g %iedb, g %jsd: g %jed, g %ke), intent(inout)  u,
real, dimension( g %isd: g %ied, g %jsdb: g %jedb, g %ke), intent(inout)  v 
)

Applies OBC values stored in segments to 3d u,v fields.

Parameters
obcOpen boundary control structure
[in,out]gOcean grid structure
[in,out]uu field to update on open boundaries
[in,out]vv field to update on open boundaries

Definition at line 1389 of file MOM_open_boundary.F90.

Referenced by radiation_open_bdry_conds().

1389  ! Arguments
1390  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1391  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
1392  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: u !< u field to update on open boundaries
1393  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: v !< v field to update on open boundaries
1394  ! Local variables
1395  integer :: i, j, k, n
1396  type(obc_segment_type), pointer :: segment
1397 
1398  if (.not.associated(obc)) return ! Bail out if OBC is not available
1399 
1400  do n=1,obc%number_of_segments
1401  segment => obc%segment(n)
1402  if (.not. segment%on_pe) then
1403  cycle
1404  elseif (segment%radiation .or. segment%oblique .or. segment%gradient) then
1405  if (segment%is_E_or_W) then
1406  i=segment%HI%IscB
1407  do k=1,g%ke ; do j=segment%HI%jsc,segment%HI%jec
1408  u(i,j,k) = segment%normal_vel(i,j,k)
1409  enddo; enddo
1410  elseif (segment%is_N_or_S) then
1411  j=segment%HI%JscB
1412  do k=1,g%ke ; do i=segment%HI%isc,segment%HI%iec
1413  v(i,j,k) = segment%normal_vel(i,j,k)
1414  enddo; enddo
1415  endif
1416  endif
1417  enddo
1418 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
Here is the caller graph for this function:

◆ open_boundary_config()

subroutine, public mom_open_boundary::open_boundary_config ( type(dyn_horgrid_type), intent(in)  G,
type(param_file_type), intent(in)  param_file,
type(ocean_obc_type), pointer  OBC 
)

Enables OBC module and reads configuration parameters This routine is called from MOM_initialize_fixed which occurs before the initialization of the vertical coordinate and ALE_init. Therefore segment data are not fully initialized here. The remainder of the segment data are initialized in a later call to update_open_boundary_data.

Parameters
[in]gOcean grid structure
[in]param_fileParameter file handle
obcOpen boundary control structure

Definition at line 216 of file MOM_open_boundary.F90.

References initialize_segment_data(), mdl, mom_error_handler::mom_error(), obc_none, open_boundary_dealloc(), mom_string_functions::remove_spaces(), setup_u_point_obc(), and setup_v_point_obc().

216  type(dyn_horgrid_type), intent(in) :: g !< Ocean grid structure
217  type(param_file_type), intent(in) :: param_file !< Parameter file handle
218  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
219  ! Local variables
220  integer :: l ! For looping over segments
221  character(len=15) :: segment_param_str ! The run-time parameter name for each segment
222  character(len=100) :: segment_str ! The contents (rhs) for parameter "segment_param_str"
223  character(len=200) :: config1 ! String for OBC_USER_CONFIG
224 
225  allocate(obc)
226 
227  call log_version(param_file, mdl, version, "Controls where open boundaries are located, what "//&
228  "kind of boundary condition to impose, and what data to apply, if any.")
229  call get_param(param_file, mdl, "OBC_NUMBER_OF_SEGMENTS", obc%number_of_segments, &
230  "The number of open boundary segments.", &
231  default=0)
232  call get_param(param_file, mdl, "G_EARTH", obc%g_Earth, &
233  "The gravitational acceleration of the Earth.", &
234  units="m s-2", default = 9.80)
235  call get_param(param_file, mdl, "OBC_USER_CONFIG", config1, &
236  "A string that sets how the open boundary conditions are \n"//&
237  " configured: \n", default="none", do_not_log=.true.)
238  call get_param(param_file, mdl, "NK", obc%ke, &
239  "The number of model layers", default=0, do_not_log=.true.)
240 
241  if (config1 .ne. "none") obc%user_BCs_set_globally = .true.
242  ! It's in state initialization...
243 ! if (config1 .eq. "tidal_bay") OBC%update_OBC = .true.
244 
245  call get_param(param_file, mdl, "EXTEND_OBC_SEGMENTS", obc%extend_segments, &
246  "If true, extend OBC segments. This option is used to recover\n"//&
247  "legacy solutions dependent on an incomplete implementaion of OBCs.\n"//&
248  "This option will be obsoleted in the future.", default=.false.)
249 
250  if (obc%number_of_segments > 0) then
251  call get_param(param_file, mdl, "OBC_ZERO_VORTICITY", obc%zero_vorticity, &
252  "If true, sets relative vorticity to zero on open boundaries.", &
253  default=.false.)
254  call get_param(param_file, mdl, "OBC_FREESLIP_VORTICITY", obc%freeslip_vorticity, &
255  "If true, sets the normal gradient of tangential velocity to\n"// &
256  "zero in the relative vorticity on open boundaries. This cannot\n"// &
257  "be true if OBC_ZERO_VORTICITY is True.", default=.false.)
258  if (obc%zero_vorticity .and. obc%freeslip_vorticity) call mom_error(fatal, &
259  "MOM_open_boundary.F90, open_boundary_config: "//&
260  "Only one of OBC_ZERO_VORTICITY and OBC_FREESLIP_VORTICITY can be True at once.")
261  call get_param(param_file, mdl, "OBC_ZERO_STRAIN", obc%zero_strain, &
262  "If true, sets the strain used in the stress tensor to zero on open boundaries.", &
263  default=.false.)
264  call get_param(param_file, mdl, "OBC_FREESLIP_STRAIN", obc%freeslip_strain, &
265  "If true, sets the normal gradient of tangential velocity to\n"// &
266  "zero in the strain use in the stress tensor on open boundaries. This cannot\n"// &
267  "be true if OBC_ZERO_STRAIN is True.", default=.false.)
268  if (obc%zero_strain .and. obc%freeslip_strain) call mom_error(fatal, &
269  "MOM_open_boundary.F90, open_boundary_config: "//&
270  "Only one of OBC_ZERO_STRAIN and OBC_FREESLIP_STRAIN can be True at once.")
271  call get_param(param_file, mdl, "OBC_ZERO_BIHARMONIC", obc%zero_biharmonic, &
272  "If true, zeros the Laplacian of flow on open boundaries in the biharmonic\n"//&
273  "viscosity term.", default=.false.)
274  ! Allocate everything
275  ! Note the 0-segment is needed when %segnum_u/v(:,:) = 0
276  allocate(obc%segment(0:obc%number_of_segments))
277  do l=0,obc%number_of_segments
278  obc%segment(l)%Flather = .false.
279  obc%segment(l)%radiation = .false.
280  obc%segment(l)%oblique = .false.
281  obc%segment(l)%nudged = .false.
282  obc%segment(l)%specified = .false.
283  obc%segment(l)%open = .false.
284  obc%segment(l)%gradient = .false.
285  obc%segment(l)%values_needed = .false.
286  obc%segment(l)%direction = obc_none
287  obc%segment(l)%is_N_or_S = .false.
288  obc%segment(l)%is_E_or_W = .false.
289  obc%segment(l)%Tnudge_in = 0.0
290  obc%segment(l)%Tnudge_out = 0.0
291  obc%segment(l)%num_fields = 0.0
292  enddo
293  allocate(obc%segnum_u(g%IsdB:g%IedB,g%jsd:g%jed)) ; obc%segnum_u(:,:) = obc_none
294  allocate(obc%segnum_v(g%isd:g%ied,g%JsdB:g%JedB)) ; obc%segnum_v(:,:) = obc_none
295 
296  do l = 1, obc%number_of_segments
297  write(segment_param_str(1:15),"('OBC_SEGMENT_',i3.3)") l
298  call get_param(param_file, mdl, segment_param_str, segment_str, &
299  "Documentation needs to be dynamic?????", &
300  fail_if_missing=.true.)
301  segment_str = remove_spaces(segment_str)
302  if (segment_str(1:2) == 'I=') then
303  call setup_u_point_obc(obc, g, segment_str, l)
304  elseif (segment_str(1:2) == 'J=') then
305  call setup_v_point_obc(obc, g, segment_str, l)
306  else
307  call mom_error(fatal, "MOM_open_boundary.F90, open_boundary_config: "//&
308  "Unable to interpret "//segment_param_str//" = "//trim(segment_str))
309  endif
310  enddo
311 
312  ! if (open_boundary_query(OBC, needs_ext_seg_data=.true.)) &
313  call initialize_segment_data(g, obc, param_file)
314  endif
315 
316  ! Safety check
317  if ((obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally) .and. &
318  .not.g%symmetric ) call mom_error(fatal, &
319  "MOM_open_boundary, open_boundary_config: "//&
320  "Symmetric memory must be used when using Flather OBCs.")
321 
322  if (.not.(obc%specified_u_BCs_exist_globally .or. obc%specified_v_BCs_exist_globally .or. &
323  obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)) then
324  ! No open boundaries have been requested
325  call open_boundary_dealloc(obc)
326  endif
327 
Here is the call graph for this function:

◆ open_boundary_dealloc()

subroutine mom_open_boundary::open_boundary_dealloc ( type(ocean_obc_type), pointer  OBC)
private

Deallocate open boundary data.

Parameters
obcOpen boundary control structure

Definition at line 1003 of file MOM_open_boundary.F90.

Referenced by open_boundary_config(), and open_boundary_end().

1003  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1004  if (.not. associated(obc)) return
1005  if (associated(obc%segment)) deallocate(obc%segment)
1006  if (associated(obc%segnum_u)) deallocate(obc%segnum_u)
1007  if (associated(obc%segnum_v)) deallocate(obc%segnum_v)
1008  deallocate(obc)
Here is the caller graph for this function:

◆ open_boundary_end()

subroutine, public mom_open_boundary::open_boundary_end ( type(ocean_obc_type), pointer  OBC)

Close open boundary data.

Parameters
obcOpen boundary control structure

Definition at line 1013 of file MOM_open_boundary.F90.

References open_boundary_dealloc().

1013  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1014  call open_boundary_dealloc(obc)
Here is the call graph for this function:

◆ open_boundary_impose_land_mask()

subroutine, public mom_open_boundary::open_boundary_impose_land_mask ( type(ocean_obc_type), pointer  OBC,
type(dyn_horgrid_type), intent(in)  G,
real, dimension( g %isdb: g %iedb, g %jsd: g %jed), intent(inout)  areaCu,
real, dimension( g %isd: g %ied, g %jsdb: g %jedb), intent(inout)  areaCv 
)

Reconcile masks and open boundaries, deallocate OBC on PEs where it is not needed. Also adjust u- and v-point cell area on specified open boundaries.

Parameters
obcOpen boundary control structure
[in]gOcean grid structure
[in,out]areacuArea of a u-cell (m2)
[in,out]areacvArea of a u-cell (m2)

Definition at line 1063 of file MOM_open_boundary.F90.

References obc_direction_e, obc_direction_s, and obc_none.

Referenced by mom_fixed_initialization::mom_initialize_fixed().

1063  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1064  type(dyn_horgrid_type), intent(in) :: g !< Ocean grid structure
1065  real, dimension(SZIB_(G),SZJ_(G)), intent(inout) :: areacu !< Area of a u-cell (m2)
1066  real, dimension(SZI_(G),SZJB_(G)), intent(inout) :: areacv !< Area of a u-cell (m2)
1067  ! Local variables
1068  integer :: i, j, n
1069  type(obc_segment_type), pointer :: segment
1070  logical :: any_u, any_v
1071 
1072  if (.not.associated(obc)) return
1073 
1074  do n=1,obc%number_of_segments
1075  segment=>obc%segment(n)
1076  if (.not. segment%on_pe .or. segment%specified) cycle
1077  if (segment%is_E_or_W) then
1078  ! Sweep along u-segments and delete the OBC for blocked points.
1079  i=segment%HI%IsdB
1080  do j=segment%HI%jsd,segment%HI%jed
1081  if (g%mask2dCu(i,j) == 0) obc%segnum_u(i,j) = obc_none
1082  enddo
1083  else
1084  ! Sweep along v-segments and delete the OBC for blocked points.
1085  j=segment%HI%JsdB
1086  do i=segment%HI%isd,segment%HI%ied
1087  if (g%mask2dCv(i,j) == 0) obc%segnum_v(i,j) = obc_none
1088  enddo
1089  endif
1090  enddo
1091 
1092  do n=1,obc%number_of_segments
1093  segment=>obc%segment(n)
1094  if (.not. segment%on_pe .or. .not. segment%specified) cycle
1095  if (segment%is_E_or_W) then
1096  ! Sweep along u-segments and for %specified BC points reset the u-point area which was masked out
1097  i=segment%HI%IsdB
1098  do j=segment%HI%jsd,segment%HI%jed
1099  if (segment%direction == obc_direction_e) then
1100  areacu(i,j) = g%areaT(i,j)
1101  !G%IareaCu(I,j) = G%IareaT(i,j) ?
1102  else ! West
1103  areacu(i,j) = g%areaT(i+1,j)
1104  !G%IareaCu(I,j) = G%IareaT(i+1,j) ?
1105  endif
1106  enddo
1107  else
1108  ! Sweep along v-segments and for %specified BC points reset the v-point area which was masked out
1109  j=segment%HI%JsdB
1110  do i=segment%HI%isd,segment%HI%ied
1111  if (segment%direction == obc_direction_s) then
1112  areacv(i,j) = g%areaT(i,j+1)
1113  !G%IareaCv(i,J) = G%IareaT(i,j+1) ?
1114  else ! North
1115  areacu(i,j) = g%areaT(i,j)
1116  !G%IareaCu(i,J) = G%IareaT(i,j) ?
1117  endif
1118  enddo
1119  endif
1120  enddo
1121 
1122  ! G%mask2du will be open wherever bathymetry allows it.
1123  ! Bathymetry outside of the open boundary was adjusted to match
1124  ! the bathymetry inside so these points will be open unless the
1125  ! bathymetry inside the boundary was too shallow and flagged as land.
1126  any_u = .false.
1127  any_v = .false.
1128  do n=1,obc%number_of_segments
1129  segment=>obc%segment(n)
1130  if (.not. segment%on_pe) cycle
1131  if (segment%is_E_or_W) then
1132  i=segment%HI%IsdB
1133  do j=segment%HI%jsd,segment%HI%jed
1134  if (obc%segnum_u(i,j) /= obc_none) any_u = .true.
1135  enddo
1136  else
1137  j=segment%HI%JsdB
1138  do i=segment%HI%isd,segment%HI%ied
1139  if (obc%segnum_v(i,j) /= obc_none) any_v = .true.
1140  enddo
1141  endif
1142  enddo
1143 
1144  obc%OBC_pe = .true.
1145  if (.not.(any_u .or. any_v)) obc%OBC_pe = .false.
1146 
Here is the caller graph for this function:

◆ open_boundary_impose_normal_slope()

subroutine, public mom_open_boundary::open_boundary_impose_normal_slope ( type(ocean_obc_type), pointer  OBC,
type(dyn_horgrid_type), intent(in)  G,
real, dimension( g %isd: g %ied, g %jsd: g %jed), intent(inout)  depth 
)

Sets the slope of bathymetry normal to an open bounndary to zero.

Parameters
obcOpen boundary control structure
[in]gOcean grid structure
[in,out]depthBathymetry at h-points

Definition at line 1019 of file MOM_open_boundary.F90.

References obc_direction_e, obc_direction_n, obc_direction_s, and obc_direction_w.

1019  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1020  type(dyn_horgrid_type), intent(in) :: g !< Ocean grid structure
1021  real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: depth !< Bathymetry at h-points
1022  ! Local variables
1023  integer :: i, j, n
1024  type(obc_segment_type), pointer :: segment
1025 
1026  if (.not.associated(obc)) return
1027 
1028  if (.not.(obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)) &
1029  return
1030 
1031  do n=1,obc%number_of_segments
1032  segment=>obc%segment(n)
1033  if (.not. segment%on_pe .or. segment%specified) cycle
1034  if (segment%direction == obc_direction_e) then
1035  i=segment%HI%IsdB
1036  do j=segment%HI%jsd,segment%HI%jed
1037  depth(i+1,j) = depth(i,j)
1038  enddo
1039  elseif (segment%direction == obc_direction_w) then
1040  i=segment%HI%IsdB
1041  do j=segment%HI%jsd,segment%HI%jed
1042  depth(i,j) = depth(i+1,j)
1043  enddo
1044  elseif (segment%direction == obc_direction_n) then
1045  j=segment%HI%JsdB
1046  do i=segment%HI%isd,segment%HI%ied
1047  depth(i,j+1) = depth(i,j)
1048  enddo
1049  elseif (segment%direction == obc_direction_s) then
1050  j=segment%HI%JsdB
1051  do i=segment%HI%isd,segment%HI%ied
1052  depth(i,j) = depth(i,j+1)
1053  enddo
1054  endif
1055  enddo
1056 
1057 

◆ open_boundary_init()

subroutine, public mom_open_boundary::open_boundary_init ( type(ocean_grid_type), intent(in)  G,
type(param_file_type), intent(in)  param_file,
type(ocean_obc_type), pointer  OBC 
)

Initialize open boundary control structure.

Parameters
[in]gOcean grid structure
[in]param_fileParameter file handle
obcOpen boundary control structure

Definition at line 947 of file MOM_open_boundary.F90.

References id_clock_pass, and mdl.

947  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
948  type(param_file_type), intent(in) :: param_file !< Parameter file handle
949  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
950  ! Local variables
951 
952  if (.not.associated(obc)) return
953 
954  if ( obc%Flather_u_BCs_exist_globally .or. obc%Flather_v_BCs_exist_globally ) then
955  call get_param(param_file, mdl, "OBC_RADIATION_MAX", obc%rx_max, &
956  "The maximum magnitude of the baroclinic radiation \n"//&
957  "velocity (or speed of characteristics). This is only \n"//&
958  "used if one of the open boundary segments is using Orlanski.", &
959  units="m s-1", default=10.0)
960  call get_param(param_file, mdl, "OBC_RAD_VEL_WT", obc%gamma_uv, &
961  "The relative weighting for the baroclinic radiation \n"//&
962  "velocities (or speed of characteristics) at the new \n"//&
963  "time level (1) or the running mean (0) for velocities. \n"//&
964  "Valid values range from 0 to 1. This is only used if \n"//&
965  "one of the open boundary segments is using Orlanski.", &
966  units="nondim", default=0.3)
967  call get_param(param_file, mdl, "OBC_RAD_THICK_WT", obc%gamma_h, &
968  "The relative weighting for the baroclinic radiation \n"//&
969  "velocities (or speed of characteristics) at the new \n"//&
970  "time level (1) or the running mean (0) for thicknesses. \n"//&
971  "Valid values range from 0 to 1. This is only used if \n"//&
972  "one of the open boundary segments is using Orlanski.", &
973  units="nondim", default=0.2)
974  endif
975 
976  id_clock_pass = cpu_clock_id('(Ocean OBC halo updates)', grain=clock_routine)
977 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19

◆ open_boundary_query()

logical function, public mom_open_boundary::open_boundary_query ( type(ocean_obc_type), pointer  OBC,
logical, intent(in), optional  apply_open_OBC,
logical, intent(in), optional  apply_specified_OBC,
logical, intent(in), optional  apply_Flather_OBC,
logical, intent(in), optional  apply_nudged_OBC,
logical, intent(in), optional  needs_ext_seg_data 
)
Parameters
obcOpen boundary control structure
[in]apply_open_obcIf present, returns True if specified_*_BCs_exist_globally is true
[in]apply_specified_obcIf present, returns True if specified_*_BCs_exist_globally is true
[in]apply_flather_obcIf present, returns True if Flather_*_BCs_exist_globally is true
[in]apply_nudged_obcIf present, returns True if nudged_*_BCs_exist_globally is true
[in]needs_ext_seg_dataIf present, returns True if external segment data needed

Definition at line 981 of file MOM_open_boundary.F90.

Referenced by mom_state_initialization::mom_initialize_state().

981  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
982  logical, optional, intent(in) :: apply_open_obc !< If present, returns True if specified_*_BCs_exist_globally is true
983  logical, optional, intent(in) :: apply_specified_obc !< If present, returns True if specified_*_BCs_exist_globally is true
984  logical, optional, intent(in) :: apply_flather_obc !< If present, returns True if Flather_*_BCs_exist_globally is true
985  logical, optional, intent(in) :: apply_nudged_obc !< If present, returns True if nudged_*_BCs_exist_globally is true
986  logical, optional, intent(in) :: needs_ext_seg_data !< If present, returns True if external segment data needed
987  open_boundary_query = .false.
988  if (.not. associated(obc)) return
989  if (present(apply_open_obc)) open_boundary_query = obc%open_u_BCs_exist_globally .or. &
990  obc%open_v_BCs_exist_globally
991  if (present(apply_specified_obc)) open_boundary_query = obc%specified_u_BCs_exist_globally .or. &
992  obc%specified_v_BCs_exist_globally
993  if (present(apply_flather_obc)) open_boundary_query = obc%Flather_u_BCs_exist_globally .or. &
994  obc%Flather_v_BCs_exist_globally
995  if (present(apply_nudged_obc)) open_boundary_query = obc%nudged_u_BCs_exist_globally .or. &
996  obc%nudged_v_BCs_exist_globally
997  if (present(needs_ext_seg_data)) open_boundary_query = obc%needs_IO_for_data
998 
Here is the caller graph for this function:

◆ open_boundary_test_extern_h()

subroutine, public mom_open_boundary::open_boundary_test_extern_h ( type(ocean_grid_type), intent(in)  G,
type(ocean_obc_type), pointer  OBC,
real, dimension( g %isd: g %ied, g %jsd: g %jed, g %ke), intent(inout)  h 
)

Set thicknesses outside of open boundaries to silly values (used for checking the interior state is independent of values outside of the domain).

Parameters
[in]gOcean grid structure
obcOpen boundary structure
[in,out]hLayer thickness (m or kg/m2)

Definition at line 1766 of file MOM_open_boundary.F90.

References obc_direction_e, and obc_direction_n.

1766  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
1767  type(ocean_obc_type), pointer :: obc !< Open boundary structure
1768  real, dimension(SZI_(G),SZJ_(G), SZK_(G)),intent(inout) :: h !< Layer thickness (m or kg/m2)
1769  ! Local variables
1770  integer :: i, j, k, n
1771  real, parameter :: silly_value = 1.e40
1772 
1773  if (.not. associated(obc)) return
1774 
1775  do n = 1, obc%number_of_segments
1776  do k = 1, g%ke
1777  if (obc%segment(n)%is_N_or_S) then
1778  j = obc%segment(n)%HI%JsdB
1779  if (obc%segment(n)%direction == obc_direction_n) then
1780  do i = obc%segment(n)%HI%isd, obc%segment(n)%HI%ied
1781  h(i,j+1,k) = silly_value
1782  enddo
1783  else
1784  do i = obc%segment(n)%HI%isd, obc%segment(n)%HI%ied
1785  h(i,j,k) = silly_value
1786  enddo
1787  endif
1788  elseif (obc%segment(n)%is_E_or_W) then
1789  i = obc%segment(n)%HI%IsdB
1790  if (obc%segment(n)%direction == obc_direction_e) then
1791  do j = obc%segment(n)%HI%jsd, obc%segment(n)%HI%jed
1792  h(i+1,j,k) = silly_value
1793  enddo
1794  else
1795  do j = obc%segment(n)%HI%jsd, obc%segment(n)%HI%jed
1796  h(i,j,k) = silly_value
1797  enddo
1798  endif
1799  endif
1800  enddo
1801  enddo
1802 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19

◆ open_boundary_test_extern_uv()

subroutine, public mom_open_boundary::open_boundary_test_extern_uv ( type(ocean_grid_type), intent(in)  G,
type(ocean_obc_type), pointer  OBC,
real, dimension( g %isdb: g %iedb, g %jsd: g %jed, g %ke), intent(inout)  u,
real, dimension( g %isd: g %ied, g %jsdb: g %jedb, g %ke), intent(inout)  v 
)

Set tangential velocities outside of open boundaries to silly values (used for checking the interior state is independent of values outside of the domain).

Parameters
[in]gOcean grid structure
obcOpen boundary structure
[in,out]uZonal velocity (m/s)
[in,out]vMeridional velocity (m/s)

Definition at line 1722 of file MOM_open_boundary.F90.

References obc_direction_e, and obc_direction_n.

1722  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
1723  type(ocean_obc_type), pointer :: obc !< Open boundary structure
1724  real, dimension(SZIB_(G),SZJ_(G), SZK_(G)),intent(inout) :: u !< Zonal velocity (m/s)
1725  real, dimension(SZI_(G),SZJB_(G), SZK_(G)),intent(inout) :: v !< Meridional velocity (m/s)
1726  ! Local variables
1727  integer :: i, j, k, n
1728  real, parameter :: silly_value = 1.e40
1729 
1730  if (.not. associated(obc)) return
1731 
1732  do n = 1, obc%number_of_segments
1733  do k = 1, g%ke
1734  if (obc%segment(n)%is_N_or_S) then
1735  j = obc%segment(n)%HI%JsdB
1736  if (obc%segment(n)%direction == obc_direction_n) then
1737  do i = obc%segment(n)%HI%IsdB, obc%segment(n)%HI%IedB
1738  u(i,j+1,k) = silly_value
1739  enddo
1740  else
1741  do i = obc%segment(n)%HI%IsdB, obc%segment(n)%HI%IedB
1742  u(i,j,k) = silly_value
1743  enddo
1744  endif
1745  elseif (obc%segment(n)%is_E_or_W) then
1746  i = obc%segment(n)%HI%IsdB
1747  if (obc%segment(n)%direction == obc_direction_e) then
1748  do j = obc%segment(n)%HI%JsdB, obc%segment(n)%HI%JedB
1749  v(i+1,j,k) = silly_value
1750  enddo
1751  else
1752  do j = obc%segment(n)%HI%JsdB, obc%segment(n)%HI%JedB
1753  v(i,j,k) = silly_value
1754  enddo
1755  endif
1756  endif
1757  enddo
1758  enddo
1759 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19

◆ open_boundary_zero_normal_flow()

subroutine, public mom_open_boundary::open_boundary_zero_normal_flow ( type(ocean_obc_type), pointer  OBC,
type(ocean_grid_type), intent(inout)  G,
real, dimension( g %isdb: g %iedb, g %jsd: g %jed, g %ke), intent(inout)  u,
real, dimension( g %isd: g %ied, g %jsdb: g %jedb, g %ke), intent(inout)  v 
)

Applies zero values to 3d u,v fields on OBC segments.

Parameters
obcOpen boundary control structure
[in,out]gOcean grid structure
[in,out]uu field to update on open boundaries
[in,out]vv field to update on open boundaries

Definition at line 1423 of file MOM_open_boundary.F90.

Referenced by mom_dynamics_legacy_split::step_mom_dyn_legacy_split(), mom_dynamics_split_rk2::step_mom_dyn_split_rk2(), mom_dynamics_unsplit::step_mom_dyn_unsplit(), and mom_dynamics_unsplit_rk2::step_mom_dyn_unsplit_rk2().

1423  ! Arguments
1424  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1425  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
1426  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: u !< u field to update on open boundaries
1427  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: v !< v field to update on open boundaries
1428  ! Local variables
1429  integer :: i, j, k, n
1430  type(obc_segment_type), pointer :: segment
1431 
1432  if (.not.associated(obc)) return ! Bail out if OBC is not available
1433 
1434  do n=1,obc%number_of_segments
1435  segment => obc%segment(n)
1436  if (.not. segment%on_pe) then
1437  cycle
1438  elseif (segment%is_E_or_W) then
1439  i=segment%HI%IscB
1440  do k=1,g%ke ; do j=segment%HI%jsc,segment%HI%jec
1441  u(i,j,k) = 0.
1442  enddo; enddo
1443  elseif (segment%is_N_or_S) then
1444  j=segment%HI%JscB
1445  do k=1,g%ke ; do i=segment%HI%isc,segment%HI%iec
1446  v(i,j,k) = 0.
1447  enddo; enddo
1448  endif
1449  enddo
1450 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
Here is the caller graph for this function:

◆ parse_segment_data_str()

subroutine mom_open_boundary::parse_segment_data_str ( character(len=*), intent(in)  segment_str,
character(len=*), intent(in), optional  var,
real, intent(out), optional  value,
character(len=*), intent(out), optional  filenam,
character(len=*), intent(out), optional  fieldnam,
character(len=*), dimension(max_obc_fields), intent(out), optional  fields,
integer, intent(out), optional  num_fields,
logical, intent(in), optional  debug 
)
private

Parse an OBC_SEGMENT_%%_DATA string.

Parameters
[in]segment_strA string in form of "VAR1=file:foo1.nc(varnam1),VAR2=file:foo2.nc(varnam2),..."
[in]varThe name of the variable for which parameters are needed
[out]filenamThe name of the input file if using "file" method
[out]fieldnamThe name of the variable in the input file if using "file" method
[out]valueA constant value if using the "value" method
[out]fieldsList of fieldnames for each segment

Definition at line 863 of file MOM_open_boundary.F90.

References mom_string_functions::extract_word(), and mom_error_handler::mom_error().

Referenced by initialize_segment_data().

863  character(len=*), intent(in) :: segment_str !< A string in form of "VAR1=file:foo1.nc(varnam1),VAR2=file:foo2.nc(varnam2),..."
864  character(len=*), intent(in), optional :: var !< The name of the variable for which parameters are needed
865  character(len=*), intent(out), optional :: filenam !< The name of the input file if using "file" method
866  character(len=*), intent(out), optional :: fieldnam !< The name of the variable in the input file if using "file" method
867  real, intent(out), optional :: value !< A constant value if using the "value" method
868  character(len=*), dimension(MAX_OBC_FIELDS), intent(out), optional :: fields !< List of fieldnames for each segment
869  integer, intent(out), optional :: num_fields
870  logical, intent(in), optional :: debug
871  ! Local variables
872  character(len=128) :: word1, word2, word3, method
873  integer :: lword, nfields, n, m, orient
874  logical :: continue,dbg
875  character(len=32), dimension(MAX_OBC_FIELDS) :: flds
876 
877  nfields=0
878  continue=.true.
879  dbg=.false.
880  if (PRESENT(debug)) dbg=debug
881 
882  do while (continue)
883  word1 = extract_word(segment_str,',',nfields+1)
884  if (trim(word1) == '') exit
885  nfields=nfields+1
886  word2 = extract_word(word1,'=',1)
887  flds(nfields) = trim(word2)
888  enddo
889 
890  if (PRESENT(fields)) then
891  do n=1,nfields
892  fields(n) = flds(n)
893  enddo
894  endif
895 
896  if (PRESENT(num_fields)) then
897  num_fields=nfields
898  return
899  endif
900 
901  m=0
902  if (PRESENT(var)) then
903  do n=1,nfields
904  if (trim(var)==trim(flds(n))) then
905  m=n
906  exit
907  endif
908  enddo
909  if (m==0) then
910  call abort()
911  endif
912 
913  ! Process first word which will start with the fieldname
914  word3 = extract_word(segment_str,',',m)
915  word1 = extract_word(word3,':',1)
916 ! if (trim(word1) == '') exit
917  word2 = extract_word(word1,'=',1)
918  if (trim(word2) == trim(var)) then
919  method=trim(extract_word(word1,'=',2))
920  lword=len_trim(method)
921  if (method(lword-3:lword) == 'file') then
922  ! raise an error id filename/fieldname not in argument list
923  word1 = extract_word(word3,':',2)
924  filenam = extract_word(word1,'(',1)
925  fieldnam = extract_word(word1,'(',2)
926  lword=len_trim(fieldnam)
927  fieldnam = fieldnam(1:lword-1) ! remove trailing parenth
928  value=-999.
929  elseif (method(lword-4:lword) == 'value') then
930  filenam = 'none'
931  fieldnam = 'none'
932  word1 = extract_word(word3,':',2)
933  lword=len_trim(word1)
934  read(word1(1:lword),*,end=986,err=987) value
935  endif
936  endif
937  endif
938 
939  return
940  986 call mom_error(fatal,'End of record while parsing segment data specification! '//trim(segment_str))
941  987 call mom_error(fatal,'Error while parsing segment data specification! '//trim(segment_str))
942 
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_segment_str()

subroutine mom_open_boundary::parse_segment_str ( integer, intent(in)  ni_global,
integer, intent(in)  nj_global,
character(len=*), intent(in)  segment_str,
integer, intent(out)  l,
integer, intent(out)  m,
integer, intent(out)  n,
character(len=*), dimension(:), intent(out)  action_str 
)
private

Parse an OBC_SEGMENT_%%% string.

Parameters
[in]ni_globalNumber of h-points in zonal direction
[in]nj_globalNumber of h-points in meridional direction
[in]segment_strA string in form of "I=l,J=m:n,string" or "J=l,I=m,n,string"
[out]lThe value of I=l, if segment_str begins with I=l, or the value of J=l
[out]mThe value of J=m, if segment_str begins with I=, or the value of I=m
[out]nThe value of J=n, if segment_str begins with I=, or the value of I=n
[out]action_strThe "string" part of segment_str

Definition at line 771 of file MOM_open_boundary.F90.

References mom_string_functions::extract_word(), interpret_int_expr(), and mom_error_handler::mom_error().

Referenced by setup_u_point_obc(), and setup_v_point_obc().

771  integer, intent(in) :: ni_global !< Number of h-points in zonal direction
772  integer, intent(in) :: nj_global !< Number of h-points in meridional direction
773  character(len=*), intent(in) :: segment_str !< A string in form of "I=l,J=m:n,string" or "J=l,I=m,n,string"
774  integer, intent(out) :: l !< The value of I=l, if segment_str begins with I=l, or the value of J=l
775  integer, intent(out) :: m !< The value of J=m, if segment_str begins with I=, or the value of I=m
776  integer, intent(out) :: n !< The value of J=n, if segment_str begins with I=, or the value of I=n
777  character(len=*), intent(out) :: action_str(:) !< The "string" part of segment_str
778  ! Local variables
779  character(len=24) :: word1, word2, m_word, n_word !< Words delineated by commas in a string in form of "I=%,J=%:%,string"
780  integer :: l_max !< Either ni_global or nj_global, depending on whether segment_str begins with "I=" or "J="
781  integer :: mn_max !< Either nj_global or ni_global, depending on whether segment_str begins with "I=" or "J="
782  integer :: j
783 
784  ! Process first word which will started with either 'I=' or 'J='
785  word1 = extract_word(segment_str,',',1)
786  word2 = extract_word(segment_str,',',2)
787  if (word1(1:2)=='I=') then
788  l_max = ni_global
789  mn_max = nj_global
790  if (.not. (word2(1:2)=='J=')) call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
791  "Second word of string '"//trim(segment_str)//"' must start with 'J='.")
792  elseif (word1(1:2)=='J=') then ! Note that the file_parser uniformaly expands "=" to " = "
793  l_max = nj_global
794  mn_max = ni_global
795  if (.not. (word2(1:2)=='I=')) call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
796  "Second word of string '"//trim(segment_str)//"' must start with 'I='.")
797  else
798  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str"//&
799  "String '"//segment_str//"' must start with 'I=' or 'J='.")
800  endif
801 
802  ! Read l
803  l = interpret_int_expr( word1(3:24), l_max )
804  if (l<0 .or. l>l_max) then
805  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
806  "First value from string '"//trim(segment_str)//"' is outside of the physical domain.")
807  endif
808 
809  ! Read m
810  m_word = extract_word(word2(3:24),':',1)
811  m = interpret_int_expr( m_word, mn_max )
812  if (m<-1 .or. m>mn_max+1) then
813  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
814  "Beginning of range in string '"//trim(segment_str)//"' is outside of the physical domain.")
815  endif
816 
817  ! Read m
818  n_word = extract_word(word2(3:24),':',2)
819  n = interpret_int_expr( n_word, mn_max )
820  if (n<-1 .or. n>mn_max+1) then
821  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
822  "End of range in string '"//trim(segment_str)//"' is outside of the physical domain.")
823  endif
824 
825  if (abs(n-m)==0) then
826  call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str: "//&
827  "Range in string '"//trim(segment_str)//"' must span one cell.")
828  endif
829 
830  ! Type of open boundary condition
831  do j = 1, size(action_str)
832  action_str(j) = extract_word(segment_str,',',2+j)
833  enddo
834 
835  contains
836 
837  ! Returns integer value interpreted from string in form of %I, N or N-%I
838  integer function interpret_int_expr(string, imax)
839  character(len=*), intent(in) :: string !< Integer in form or %I, N or N-%I
840  integer, intent(in) :: imax !< Value to replace 'N' with
841  ! Local variables
842  integer slen
843 
844  slen = len_trim(string)
845  if (slen==0) call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str"//&
846  "Parsed string was empty!")
847  if (len_trim(string)==1 .and. string(1:1)=='N') then
848  interpret_int_expr = imax
849  elseif (string(1:1)=='N') then
850  read(string(2:slen),*,err=911) interpret_int_expr
852  else
853  read(string(1:slen),*,err=911) interpret_int_expr
854  endif
855  return
856  911 call mom_error(fatal, "MOM_open_boundary.F90, parse_segment_str"//&
857  "Problem reading value from string '"//trim(string)//"'.")
858  end function interpret_int_expr
integer function interpret_int_expr(string, imax)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ radiation_open_bdry_conds()

subroutine, public mom_open_boundary::radiation_open_bdry_conds ( type(ocean_obc_type), pointer  OBC,
real, dimension( g %isdb: g %iedb, g %jsd: g %jed, g %ke), intent(inout)  u_new,
real, dimension( g %isdb: g %iedb, g %jsd: g %jed, g %ke), intent(in)  u_old,
real, dimension( g %isd: g %ied, g %jsdb: g %jedb, g %ke), intent(inout)  v_new,
real, dimension( g %isd: g %ied, g %jsdb: g %jedb, g %ke), intent(in)  v_old,
type(ocean_grid_type), intent(inout)  G,
real, intent(in)  dt 
)

Apply radiation conditions to 3D u,v at open boundaries.

Parameters
[in,out]gOcean grid structure
obcOpen boundary control structure
[in,out]u_newNew u values on open boundaries
[in]u_oldOriginal unadjusted u
[in,out]v_newNew v values on open boundaries
[in]v_oldOriginal unadjusted v
[in]dtAppropriate timestep

Definition at line 1151 of file MOM_open_boundary.F90.

References gradient_at_q_points(), id_clock_pass, obc_direction_e, obc_direction_n, obc_direction_s, obc_direction_w, and open_boundary_apply_normal_flow().

1151  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
1152  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
1153  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: u_new !< New u values on open boundaries
1154  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u_old !< Original unadjusted u
1155  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: v_new !< New v values on open boundaries
1156  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v_old !< Original unadjusted v
1157  real, intent(in) :: dt !< Appropriate timestep
1158  ! Local variables
1159  real :: dhdt, dhdx, dhdy, gamma_u, gamma_h, gamma_v
1160  real :: cff, cx, cy, tau
1161  real :: rx_max, ry_max ! coefficients for radiation
1162  real :: rx_new, rx_avg ! coefficients for radiation
1163  real :: ry_new, ry_avg ! coefficients for radiation
1164  real, parameter :: eps = 1.0e-20
1165  type(obc_segment_type), pointer :: segment
1166  integer :: i, j, k, is, ie, js, je, nz, n
1167  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
1168 
1169  if (.not.associated(obc)) return
1170 
1171  if (.not.(obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)) &
1172  return
1173 
1174  gamma_u = obc%gamma_uv ; gamma_v = obc%gamma_uv ; gamma_h = obc%gamma_h
1175  rx_max = obc%rx_max ; ry_max = obc%rx_max
1176  do n=1,obc%number_of_segments
1177  segment=>obc%segment(n)
1178  if (.not. segment%on_pe) cycle
1179  if (segment%oblique) call gradient_at_q_points(g,segment,u_old,v_old)
1180  if (segment%direction == obc_direction_e) then
1181  i=segment%HI%IscB
1182  do k=1,nz ; do j=segment%HI%jsc,segment%HI%jec
1183  if (segment%radiation) then
1184  dhdt = u_old(i-1,j,k)-u_new(i-1,j,k) !old-new
1185  dhdx = u_new(i-1,j,k)-u_new(i-2,j,k) !in new time backward sasha for I-1
1186  rx_new = 0.0
1187  if (dhdt*dhdx > 0.0) rx_new = min( (dhdt/dhdx), rx_max) ! outward phase speed
1188  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
1189  segment%rx_normal(i,j,k) = rx_avg
1190  segment%normal_vel(i,j,k) = (u_old(i,j,k) + rx_avg*u_new(i-1,j,k)) / (1.0+rx_avg)
1191  elseif (segment%oblique) then
1192  dhdt = u_old(i-1,j,k)-u_new(i-1,j,k) !old-new
1193  dhdx = u_new(i-1,j,k)-u_new(i-2,j,k) !in new time backward sasha for I-1
1194 ! if (segment%oblique) then
1195  if (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) > 0.0) then
1196  dhdy = segment%grad_normal(j-1,1,k)
1197  elseif (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) == 0.0) then
1198  dhdy = 0.0
1199  else
1200  dhdy = segment%grad_normal(j,1,k)
1201  endif
1202 ! endif
1203  if (dhdt*dhdx < 0.0) dhdt = 0.0
1204  if (dhdx == 0.0) dhdx=eps ! avoid segv
1205  cx = min(dhdt/dhdx,rx_max) ! default to normal radiation
1206 ! Cy = 0.0
1207  cff = max(dhdx*dhdx,eps)
1208 ! if (segment%oblique) then
1209  cff = max(dhdx*dhdx + dhdy*dhdy, eps)
1210  if (dhdy==0.) dhdy=eps ! avoid segv
1211  cy = min(cff,max(dhdt/dhdy,-cff))
1212 ! endif
1213  segment%normal_vel(i,j,k) = ((cff*u_old(i,j,k) + cx*u_new(i-1,j,k)) - &
1214  (max(cy,0.0)*segment%grad_normal(j-1,2,k) + min(cy,0.0)*segment%grad_normal(j,2,k))) / (cff + cx)
1215  elseif (segment%gradient) then
1216  segment%normal_vel(i,j,k) = u_new(i-1,j,k)
1217  endif
1218  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1219  if (dhdt*dhdx < 0.0) then
1220  tau = segment%Tnudge_in
1221  else
1222  tau = segment%Tnudge_out
1223  endif
1224  segment%normal_vel(i,j,k) = u_new(i,j,k) + dt*tau*(segment%nudged_normal_vel(i,j,k) - u_old(i,j,k))
1225  endif
1226  enddo; enddo
1227  endif
1228 
1229  if (segment%direction == obc_direction_w) then
1230  i=segment%HI%IscB
1231  do k=1,nz ; do j=segment%HI%jsc,segment%HI%jec
1232  if (segment%radiation) then
1233  dhdt = u_old(i+1,j,k)-u_new(i+1,j,k) !old-new
1234  dhdx = u_new(i+1,j,k)-u_new(i+2,j,k) !in new time forward sasha for I+1
1235  rx_new = 0.0
1236  if (dhdt*dhdx > 0.0) rx_new = min( (dhdt/dhdx), rx_max)
1237  rx_avg = (1.0-gamma_u)*segment%rx_normal(i,j,k) + gamma_u*rx_new
1238  segment%rx_normal(i,j,k) = rx_avg
1239  segment%normal_vel(i,j,k) = (u_old(i,j,k) + rx_avg*u_new(i+1,j,k)) / (1.0+rx_avg)
1240  elseif (segment%oblique) then
1241  dhdt = u_old(i+1,j,k)-u_new(i+1,j,k) !old-new
1242  dhdx = u_new(i+1,j,k)-u_new(i+2,j,k) !in new time forward sasha for I+1
1243 ! if (segment%oblique) then
1244  if (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) > 0.0) then
1245  dhdy = segment%grad_normal(j-1,1,k)
1246  elseif (dhdt*(segment%grad_normal(j,1,k) + segment%grad_normal(j-1,1,k)) == 0.0) then
1247  dhdy = 0.0
1248  else
1249  dhdy = segment%grad_normal(j,1,k)
1250  endif
1251 ! endif
1252  if (dhdt*dhdx < 0.0) dhdt = 0.0
1253  if (dhdx == 0.0) dhdx=eps ! avoid segv
1254  cx = min(dhdt/dhdx,rx_max) ! default to normal flow only
1255 ! Cy = 0.
1256  cff = max(dhdx*dhdx, eps)
1257 ! if (segment%oblique) then
1258  cff = max(dhdx*dhdx + dhdy*dhdy, eps)
1259  if (dhdy==0.) dhdy=eps ! avoid segv
1260  cy = min(cff,max(dhdt/dhdy,-cff))
1261 ! endif
1262  segment%normal_vel(i,j,k) = ((cff*u_old(i,j,k) + cx*u_new(i+1,j,k)) - &
1263  (max(cy,0.0)*segment%grad_normal(j-1,2,k) + min(cy,0.0)*segment%grad_normal(j,2,k))) / (cff + cx)
1264  elseif (segment%gradient) then
1265  segment%normal_vel(i,j,k) = u_new(i+1,j,k)
1266  endif
1267  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1268  if (dhdt*dhdx < 0.0) then
1269  tau = segment%Tnudge_in
1270  else
1271  tau = segment%Tnudge_out
1272  endif
1273  segment%normal_vel(i,j,k) = u_new(i,j,k) + dt*tau*(segment%nudged_normal_vel(i,j,k) - u_old(i,j,k))
1274  endif
1275  enddo; enddo
1276  endif
1277 
1278  if (segment%direction == obc_direction_n) then
1279  j=segment%HI%JscB
1280  do k=1,nz ; do i=segment%HI%isc,segment%HI%iec
1281  if (segment%radiation) then
1282  dhdt = v_old(i,j-1,k)-v_new(i,j-1,k) !old-new
1283  dhdy = v_new(i,j-1,k)-v_new(i,j-2,k) !in new time backward sasha for J-1
1284  ry_new = 0.0
1285  if (dhdt*dhdy > 0.0) ry_new = min( (dhdt/dhdy), ry_max)
1286  ry_avg = (1.0-gamma_v)*segment%rx_normal(i,j,k) + gamma_v*ry_new
1287  segment%rx_normal(i,j,k) = ry_avg
1288  segment%normal_vel(i,j,k) = (v_old(i,j,k) + ry_avg*v_new(i,j-1,k)) / (1.0+ry_avg)
1289  elseif (segment%oblique) then
1290  dhdt = v_old(i,j-1,k)-v_new(i,j-1,k) !old-new
1291  dhdy = v_new(i,j-1,k)-v_new(i,j-2,k) !in new time backward sasha for J-1
1292 ! if (segment%oblique) then
1293  if (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) > 0.0) then
1294  dhdx = segment%grad_normal(i-1,1,k)
1295  elseif (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) == 0.0) then
1296  dhdx = 0.0
1297  else
1298  dhdx = segment%grad_normal(i,1,k)
1299  endif
1300 ! endif
1301  if (dhdt*dhdy < 0.0) dhdt = 0.0
1302  if (dhdy == 0.0) dhdy=eps ! avoid segv
1303  cy = min(dhdt/dhdy,rx_max) ! default to normal flow only
1304 ! Cx = 0
1305  cff = max(dhdy*dhdy, eps)
1306 ! if (segment%oblique) then
1307  cff = max(dhdx*dhdx + dhdy*dhdy, eps)
1308  if (dhdx==0.) dhdx=eps ! avoid segv
1309  cx = min(cff,max(dhdt/dhdx,-cff))
1310 ! endif
1311  segment%normal_vel(i,j,k) = ((cff*v_old(i,j,k) + cy*v_new(i,j-1,k)) - &
1312  (max(cx,0.0)*segment%grad_normal(i-1,2,k) + min(cx,0.0)*segment%grad_normal(i,2,k))) / (cff + cy)
1313  elseif (segment%gradient) then
1314  segment%normal_vel(i,j,k) = v_new(i,j-1,k)
1315  endif
1316  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1317  if (dhdt*dhdy < 0.0) then
1318  tau = segment%Tnudge_in
1319  else
1320  tau = segment%Tnudge_out
1321  endif
1322  segment%normal_vel(i,j,k) = v_new(i,j,k) + dt*tau*(segment%nudged_normal_vel(i,j,k) - v_old(i,j,k))
1323  endif
1324  enddo; enddo
1325  endif
1326 
1327 
1328  if (segment%direction == obc_direction_s) then
1329  j=segment%HI%JscB
1330  do k=1,nz ; do i=segment%HI%isc,segment%HI%iec
1331  if (segment%radiation) then
1332  dhdt = v_old(i,j+1,k)-v_new(i,j+1,k) !old-new
1333  dhdy = v_new(i,j+1,k)-v_new(i,j+2,k) !in new time backward sasha for J-1
1334  ry_new = 0.0
1335  if (dhdt*dhdy > 0.0) ry_new = min( (dhdt/dhdy), ry_max)
1336  ry_avg = (1.0-gamma_v)*segment%rx_normal(i,j,k) + gamma_v*ry_new
1337  segment%rx_normal(i,j,k) = ry_avg
1338  segment%normal_vel(i,j,k) = (v_old(i,j,k) + ry_avg*v_new(i,j+1,k)) / (1.0+ry_avg)
1339  elseif (segment%oblique) then
1340  dhdt = v_old(i,j+1,k)-v_new(i,j+1,k) !old-new
1341  dhdy = v_new(i,j+1,k)-v_new(i,j+2,k) !in new time backward sasha for J-1
1342 ! if (segment%oblique) then
1343  if (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) > 0.0) then
1344  dhdx = segment%grad_normal(i-1,1,k)
1345  elseif (dhdt*(segment%grad_normal(i,1,k) + segment%grad_normal(i-1,1,k)) == 0.0) then
1346  dhdx = 0.0
1347  else
1348  dhdx = segment%grad_normal(i,1,k)
1349  endif
1350 ! endif
1351  if (dhdt*dhdy < 0.0) dhdt = 0.0
1352  if (dhdy == 0.0) dhdy=eps ! avoid segv
1353  cy = min(dhdt/dhdy,rx_max) ! default to normal flow only
1354 ! Cx = 0
1355  cff = max(dhdy*dhdy, eps)
1356 ! if (segment%oblique) then
1357  cff = max(dhdx*dhdx + dhdy*dhdy, eps)
1358  if (dhdx==0.) dhdx=eps ! avoid segv
1359  cx = min(cff,max(dhdt/dhdx,-cff))
1360 ! endif
1361  segment%normal_vel(i,j,k) = ((cff*v_old(i,j,k) + cy*v_new(i,j+1,k)) - &
1362  (max(cx,0.0)*segment%grad_normal(i-1,2,k) + min(cx,0.0)*segment%grad_normal(i,2,k))) / (cff + cy)
1363  elseif (segment%gradient) then
1364  segment%normal_vel(i,j,k) = v_new(i,j+1,k)
1365  endif
1366  if ((segment%radiation .or. segment%oblique) .and. segment%nudged) then
1367  if (dhdt*dhdy < 0.0) then
1368  tau = segment%Tnudge_in
1369  else
1370  tau = segment%Tnudge_out
1371  endif
1372  segment%normal_vel(i,j,k) = v_new(i,j,k) + dt*tau*(segment%nudged_normal_vel(i,j,k) - v_old(i,j,k))
1373  endif
1374  enddo; enddo
1375  end if
1376  enddo
1377 
1378  ! Actually update u_new, v_new
1379  call open_boundary_apply_normal_flow(obc, g, u_new, v_new)
1380 
1381  call cpu_clock_begin(id_clock_pass)
1382  call pass_vector(u_new, v_new, g%Domain)
1383  call cpu_clock_end(id_clock_pass)
1384 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
Here is the call graph for this function:

◆ register_file_obc()

logical function, public mom_open_boundary::register_file_obc ( type(param_file_type), intent(in)  param_file,
type(file_obc_cs), pointer  CS,
type(obc_registry_type), pointer  OBC_Reg 
)

Add file to OBC registry.

Parameters
[in]param_fileparameter file.
csfile control structure.
obc_regOBC registry.

Definition at line 2058 of file MOM_open_boundary.F90.

References register_obc().

Referenced by mom_boundary_update::call_obc_register().

2058  type(param_file_type), intent(in) :: param_file !< parameter file.
2059  type(file_obc_cs), pointer :: cs !< file control structure.
2060  type(obc_registry_type), pointer :: obc_reg !< OBC registry.
2061  logical :: register_file_obc
2062  character(len=32) :: casename = "OBC file" !< This case's name.
2063 
2064  if (associated(cs)) then
2065  call mom_error(warning, "register_file_OBC called with an "// &
2066  "associated control structure.")
2067  return
2068  endif
2069  allocate(cs)
2070 
2071  ! Register the file for boundary updates.
2072  call register_obc(casename, param_file, obc_reg)
2073  register_file_obc = .true.
2074 
Here is the call graph for this function:
Here is the caller graph for this function:

◆ register_obc()

subroutine, public mom_open_boundary::register_obc ( character(len=32), intent(in)  name,
type(param_file_type), intent(in)  param_file,
type(obc_registry_type), pointer  Reg 
)

register open boundary objects for boundary updates.

Parameters
[in]nameOBC name used for error messages
[in]param_filefile to parse for model parameter values
regpointer to the tracer registry

Definition at line 2006 of file MOM_open_boundary.F90.

References obc_registry_init().

Referenced by register_file_obc().

2006  character(len=32), intent(in) :: name !< OBC name used for error messages
2007  type(param_file_type), intent(in) :: param_file !< file to parse for model parameter values
2008  type(obc_registry_type), pointer :: reg !< pointer to the tracer registry
2009  integer :: nobc
2010  character(len=256) :: mesg ! Message for error messages.
2011 
2012  if (.not. associated(reg)) call obc_registry_init(param_file, reg)
2013 
2014  if (reg%nobc>=max_fields_) then
2015  write(mesg,'("Increase MAX_FIELDS_ in MOM_memory.h to at least ",I3," to allow for &
2016  &all the open boundaries being registered via register_OBC.")') reg%nobc+1
2017  call mom_error(fatal,"MOM register_tracer: "//mesg)
2018  endif
2019  reg%nobc = reg%nobc + 1
2020  nobc = reg%nobc
2021 
2022  reg%OB(nobc)%name = name
2023 
2024  if (reg%locked) call mom_error(fatal, &
2025  "MOM register_tracer was called for variable "//trim(reg%OB(nobc)%name)//&
2026  " with a locked tracer registry.")
2027 
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_tracer_data()

subroutine, public mom_open_boundary::set_tracer_data ( type(ocean_obc_type), pointer  OBC,
type(thermo_var_ptrs), intent(inout)  tv,
real, dimension( g %isd: g %ied, g %jsd: g %jed, g %ke), intent(inout)  h,
type(ocean_grid_type), intent(inout)  G,
type(param_file_type), intent(in)  PF,
type(tracer_registry_type), pointer  tracer_Reg 
)

Sets the initial values of the tracer and h open boundary conditions. Also allocates and fills the segmentT and segmentS arrays, but they are not yet used anywhere.

Parameters
[in,out]gOcean grid structure
obcOpen boundary structure
[in,out]tvThermodynamics structure
[in,out]hThickness
[in]pfParameter file handle
tracer_regTracer registry

Definition at line 1518 of file MOM_open_boundary.F90.

References obc_direction_e, obc_direction_n, obc_direction_s, and obc_direction_w.

Referenced by mom_state_initialization::mom_initialize_state().

1518  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
1519  type(ocean_obc_type), pointer :: obc !< Open boundary structure
1520  type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamics structure
1521  real, dimension(SZI_(G),SZJ_(G), SZK_(G)), intent(inout) :: h !< Thickness
1522  type(param_file_type), intent(in) :: pf !< Parameter file handle
1523  type(tracer_registry_type), pointer :: tracer_reg !< Tracer registry
1524  ! Local variables
1525  integer :: i, j, k, itt, is, ie, js, je, isd, ied, jsd, jed, nz, n
1526  integer :: isd_off, jsd_off
1527  integer :: isdb, iedb, jsdb, jedb
1528  type(obc_segment_type), pointer :: segment ! pointer to segment type list
1529  character(len=40) :: mdl = "set_tracer_data" ! This subroutine's name.
1530  character(len=200) :: filename, obc_file, inputdir ! Strings for file/path
1531 
1532  real :: temp_u(g%domain%niglobal+1,g%domain%njglobal)
1533  real :: temp_v(g%domain%niglobal,g%domain%njglobal+1)
1534 
1535  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
1536  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
1537  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
1538 
1539  ! For now, there are no radiation conditions applied to the thicknesses, since
1540  ! the thicknesses might not be physically motivated. Instead, sponges should be
1541  ! used to enforce the near-boundary layer structure.
1542 
1543  if (associated(tv%T)) then
1544 
1545  call pass_var(tv%T, g%Domain)
1546  call pass_var(tv%S, g%Domain)
1547 
1548  do n=1,obc%number_of_segments
1549  segment => obc%segment(n)
1550  if (.not. segment%on_pe) cycle
1551 
1552  if (segment%is_E_or_W) then
1553  if (.not.ASSOCIATED(segment%T)) then
1554  allocate(segment%T(segment%HI%IsdB,segment%HI%jsd:segment%HI%jed,g%ke))
1555  allocate(segment%S(segment%HI%IsdB,segment%HI%jsd:segment%HI%jed,g%ke))
1556  endif
1557  else if (segment%is_N_or_S) then
1558  if (.not.ASSOCIATED(segment%T)) then
1559  allocate(segment%T(segment%HI%isd:segment%HI%ied,segment%HI%JsdB,g%ke))
1560  allocate(segment%S(segment%HI%isd:segment%HI%ied,segment%HI%JsdB,g%ke))
1561  endif
1562  endif
1563 
1564  if (segment%direction == obc_direction_e) then
1565  i=segment%HI%IsdB
1566  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1567  segment%T(i,j,k) = tv%T(i,j,k)
1568  segment%S(i,j,k) = tv%S(i,j,k)
1569  enddo; enddo
1570  elseif (segment%direction == obc_direction_w) then
1571  i=segment%HI%IsdB
1572  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1573  segment%T(i,j,k) = tv%T(i+1,j,k)
1574  segment%S(i,j,k) = tv%S(i+1,j,k)
1575  enddo; enddo
1576  elseif (segment%direction == obc_direction_n) then
1577  j=segment%HI%JsdB
1578  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1579  segment%T(i,j,k) = tv%T(i,j,k)
1580  segment%S(i,j,k) = tv%S(i,j,k)
1581  enddo; enddo
1582  elseif (segment%direction == obc_direction_s) then
1583  j=segment%HI%JsdB
1584  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1585  segment%T(i,j,k) = tv%T(i,j+1,k)
1586  segment%S(i,j,k) = tv%S(i,j+1,k)
1587  enddo; enddo
1588  endif
1589  enddo
1590 
1591  do n=1,obc%number_of_segments
1592  segment => obc%segment(n)
1593  if (.not. segment%on_pe) cycle
1594 
1595  if (segment%direction == obc_direction_e) then
1596  i=segment%HI%IsdB
1597  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1598  tv%T(i+1,j,k) = tv%T(i,j,k) ; tv%S(i+1,j,k) = tv%S(i,j,k)
1599  enddo; enddo
1600  elseif (segment%direction == obc_direction_w) then
1601  i=segment%HI%IsdB
1602  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1603  tv%T(i,j,k) = tv%T(i+1,j,k) ; tv%S(i,j,k) = tv%S(i+1,j,k)
1604  enddo; enddo
1605  elseif (segment%direction == obc_direction_n) then
1606  j=segment%HI%JsdB
1607  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1608  tv%T(i,j+1,k) = tv%T(i,j,k) ; tv%S(i,j+1,k) = tv%S(i,j,k)
1609  enddo; enddo
1610  elseif (segment%direction == obc_direction_s) then
1611  j=segment%HI%JsdB
1612  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1613  tv%T(i,j,k) = tv%T(i,j+1,k) ; tv%S(i,j,k) = tv%S(i,j+1,k)
1614  enddo; enddo
1615  endif
1616  enddo
1617  endif
1618 
1619  do n=1,obc%number_of_segments
1620  segment => obc%segment(n)
1621  if (.not. segment%on_pe) cycle
1622 
1623  if (segment%direction == obc_direction_e) then
1624  i=segment%HI%IsdB
1625  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1626  h(i+1,j,k) = h(i,j,k)
1627  enddo; enddo
1628  elseif (segment%direction == obc_direction_w) then
1629  i=segment%HI%IsdB
1630  do k=1,g%ke ; do j=segment%HI%jsd,segment%HI%jed
1631  h(i,j,k) = h(i+1,j,k)
1632  enddo; enddo
1633  elseif (segment%direction == obc_direction_n) then
1634  j=segment%HI%JsdB
1635  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1636  h(i,j+1,k) = h(i,j,k)
1637  enddo; enddo
1638  elseif (segment%direction == obc_direction_s) then
1639  j=segment%HI%JsdB
1640  do k=1,g%ke ; do i=segment%HI%isd,segment%HI%ied
1641  h(i,j,k) = h(i,j+1,k)
1642  enddo; enddo
1643  endif
1644  enddo
1645 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
Here is the caller graph for this function:

◆ setup_segment_indices()

subroutine mom_open_boundary::setup_segment_indices ( type(dyn_horgrid_type), intent(in)  G,
type(obc_segment_type), intent(inout)  seg,
integer, intent(in)  Is_obc,
integer, intent(in)  Ie_obc,
integer, intent(in)  Js_obc,
integer, intent(in)  Je_obc 
)
Parameters
[in]ggrid type
[in,out]segOpen boundary segment
[in]is_obcQ-point global i-index of start of segment
[in]ie_obcQ-point global i-index of end of segment
[in]js_obcQ-point global j-index of start of segment
[in]je_obcQ-point global j-index of end of segment

Definition at line 503 of file MOM_open_boundary.F90.

Referenced by setup_u_point_obc(), and setup_v_point_obc().

503  type(dyn_horgrid_type), intent(in) :: g !< grid type
504  type(obc_segment_type), intent(inout) :: seg !< Open boundary segment
505  integer, intent(in) :: is_obc !< Q-point global i-index of start of segment
506  integer, intent(in) :: ie_obc !< Q-point global i-index of end of segment
507  integer, intent(in) :: js_obc !< Q-point global j-index of start of segment
508  integer, intent(in) :: je_obc !< Q-point global j-index of end of segment
509  ! Local variables
510  integer :: isg,ieg,jsg,jeg
511 
512 ! if (.not. G%Domain%symmetric) call MOM_error(FATAL, "MOM_open_boundary.F90, setup_segment_indices: "//&
513 ! "Need to compile in symmetric mode")
514 
515  ! Isg, Ieg will be I*_obc in global space
516  if (ie_obc<is_obc) then
517  isg=ie_obc;ieg=is_obc
518  else
519  isg=is_obc;ieg=ie_obc
520  endif
521  if (je_obc<js_obc) then
522  jsg=je_obc;jeg=js_obc
523  else
524  jsg=js_obc;jeg=je_obc
525  endif
526 
527  ! Global space I*_obc but sorted
528  seg%HI%IsgB = isg ; seg%HI%IegB = ieg
529  seg%HI%isg = isg+1 ; seg%HI%ieg = ieg
530  seg%HI%JsgB = jsg ; seg%HI%JegB = jeg
531  seg%HI%jsg = jsg+1 ; seg%HI%Jeg = jeg
532 
533  ! Move into local index space
534  isg = isg - g%idg_offset
535  jsg = jsg - g%jdg_offset
536  ieg = ieg - g%idg_offset
537  jeg = jeg - g%jdg_offset
538 
539  ! This is the i-extent of the segment on this PE.
540  ! The values are nonsence if the segment is not on this PE.
541  seg%HI%IsdB = min( max(isg, g%HI%IsdB), g%HI%IedB)
542  seg%HI%IedB = min( max(ieg, g%HI%IsdB), g%HI%IedB)
543  seg%HI%isd = min( max(isg+1, g%HI%isd), g%HI%ied)
544  seg%HI%ied = min( max(ieg, g%HI%isd), g%HI%ied)
545  seg%HI%IscB = min( max(isg, g%HI%IscB), g%HI%IecB)
546  seg%HI%IecB = min( max(ieg, g%HI%IscB), g%HI%IecB)
547  seg%HI%isc = min( max(isg+1, g%HI%isc), g%HI%iec)
548  seg%HI%iec = min( max(ieg, g%HI%isc), g%HI%iec)
549 
550  ! This is the j-extent of the segment on this PE.
551  ! The values are nonsence if the segment is not on this PE.
552  seg%HI%JsdB = min( max(jsg, g%HI%JsdB), g%HI%JedB)
553  seg%HI%JedB = min( max(jeg, g%HI%JsdB), g%HI%JedB)
554  seg%HI%jsd = min( max(jsg+1, g%HI%jsd), g%HI%jed)
555  seg%HI%jed = min( max(jeg, g%HI%jsd), g%HI%jed)
556  seg%HI%JscB = min( max(jsg, g%HI%JscB), g%HI%JecB)
557  seg%HI%JecB = min( max(jeg, g%HI%JscB), g%HI%JecB)
558  seg%HI%jsc = min( max(jsg+1, g%HI%jsc), g%HI%jec)
559  seg%HI%jec = min( max(jeg, g%HI%jsc), g%HI%jec)
560 
Here is the caller graph for this function:

◆ setup_u_point_obc()

subroutine mom_open_boundary::setup_u_point_obc ( type(ocean_obc_type), pointer  OBC,
type(dyn_horgrid_type), intent(in)  G,
character(len=*), intent(in)  segment_str,
integer, intent(in)  l_seg 
)
private

Parse an OBC_SEGMENT_%%% string starting with "I=" and configure placement and type of OBC accordingly.

Parameters
obcOpen boundary control structure
[in]gOcean grid structure
[in]segment_strA string in form of "I=%,J=%:%,string"
[in]l_segwhich segment is this?

Definition at line 565 of file MOM_open_boundary.F90.

References allocate_obc_segment_data(), mom_error_handler::mom_error(), obc_direction_e, obc_direction_w, parse_segment_str(), and setup_segment_indices().

Referenced by open_boundary_config().

565  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
566  type(dyn_horgrid_type), intent(in) :: g !< Ocean grid structure
567  character(len=*), intent(in) :: segment_str !< A string in form of "I=%,J=%:%,string"
568  integer, intent(in) :: l_seg !< which segment is this?
569  ! Local variables
570  integer :: i_obc, js_obc, je_obc ! Position of segment in global index space
571  integer :: j, a_loop
572  character(len=32) :: action_str(5)
573 
574  ! This returns the global indices for the segment
575  call parse_segment_str(g%ieg, g%jeg, segment_str, i_obc, js_obc, je_obc, action_str )
576 
577  call setup_segment_indices(g, obc%segment(l_seg),i_obc,i_obc,js_obc,je_obc)
578 
579  i_obc = i_obc - g%idg_offset ! Convert to local tile indices on this tile
580  js_obc = js_obc - g%jdg_offset ! Convert to local tile indices on this tile
581  je_obc = je_obc - g%jdg_offset ! Convert to local tile indices on this tile
582 
583  ! Hack to extend segment by one point
584  if (obc%extend_segments) then
585  if (js_obc<je_obc) then
586  js_obc = js_obc - 1 ; je_obc = je_obc + 1
587  else
588  js_obc = js_obc + 1 ; je_obc = je_obc - 1
589  endif
590  endif
591 
592  if (je_obc>js_obc) then
593  obc%segment(l_seg)%direction = obc_direction_e
594  else if (je_obc<js_obc) then
595  obc%segment(l_seg)%direction = obc_direction_w
596  j=js_obc;js_obc=je_obc;je_obc=j
597  endif
598 
599  obc%segment(l_seg)%on_pe = .false.
600 
601  do a_loop = 1,5 ! up to 5 options available
602  if (len_trim(action_str(a_loop)) == 0) then
603  cycle
604  elseif (trim(action_str(a_loop)) == 'FLATHER') then
605  obc%segment(l_seg)%Flather = .true.
606  obc%segment(l_seg)%open = .true.
607  obc%Flather_u_BCs_exist_globally = .true.
608  obc%open_u_BCs_exist_globally = .true.
609  elseif (trim(action_str(a_loop)) == 'ORLANSKI') then
610  obc%segment(l_seg)%radiation = .true.
611  obc%segment(l_seg)%open = .true.
612  obc%open_u_BCs_exist_globally = .true.
613  elseif (trim(action_str(a_loop)) == 'OBLIQUE') then
614  obc%segment(l_seg)%oblique = .true.
615  obc%segment(l_seg)%open = .true.
616  obc%oblique_BCs_exist_globally = .true.
617  obc%open_u_BCs_exist_globally = .true.
618  elseif (trim(action_str(a_loop)) == 'NUDGED') then
619  obc%segment(l_seg)%nudged = .true.
620  obc%segment(l_seg)%Tnudge_in = 1.0/(3*86400)
621  obc%segment(l_seg)%Tnudge_out = 1.0/(360*86400)
622  obc%nudged_u_BCs_exist_globally = .true.
623  elseif (trim(action_str(a_loop)) == 'GRADIENT') then
624  obc%segment(l_seg)%gradient = .true.
625  obc%segment(l_seg)%open = .true.
626  obc%open_u_BCs_exist_globally = .true.
627  elseif (trim(action_str(a_loop)) == 'LEGACY') then
628  obc%segment(l_seg)%Flather = .true.
629  obc%segment(l_seg)%radiation = .true.
630  obc%Flather_u_BCs_exist_globally = .true.
631  obc%open_u_BCs_exist_globally = .true.
632  elseif (trim(action_str(a_loop)) == 'SIMPLE') then
633  obc%segment(l_seg)%specified = .true.
634  obc%specified_u_BCs_exist_globally = .true. ! This avoids deallocation
635  ! Hack to undo the hack above for SIMPLE BCs
636  if (obc%extend_segments) then
637  js_obc = js_obc + 1
638  je_obc = je_obc - 1
639  endif
640  else
641  call mom_error(fatal, "MOM_open_boundary.F90, setup_u_point_obc: "//&
642  "String '"//trim(action_str(a_loop))//"' not understood.")
643  endif
644 
645  if (i_obc<g%HI%IsdB .or. i_obc>g%HI%IedB) return ! Boundary is not on tile
646  if (js_obc<g%HI%JsdB .and. je_obc<g%HI%JsdB) return ! Segment is not on tile
647  if (js_obc>g%HI%JedB) return ! Segment is not on tile
648  enddo ! a_loop
649 
650  obc%segment(l_seg)%on_pe = .true.
651  obc%segment(l_seg)%is_E_or_W = .true.
652 
653  do j=g%HI%jsd, g%HI%jed
654  if (j>js_obc .and. j<=je_obc) then
655  obc%segnum_u(i_obc,j) = l_seg
656  endif
657  enddo
658  obc%segment(l_seg)%Is_obc = i_obc
659  obc%segment(l_seg)%Ie_obc = i_obc
660  obc%segment(l_seg)%Js_obc = js_obc
661  obc%segment(l_seg)%Je_obc = je_obc
662  call allocate_obc_segment_data(obc, obc%segment(l_seg))
663 
Here is the call graph for this function:
Here is the caller graph for this function:

◆ setup_v_point_obc()

subroutine mom_open_boundary::setup_v_point_obc ( type(ocean_obc_type), pointer  OBC,
type(dyn_horgrid_type), intent(in)  G,
character(len=*), intent(in)  segment_str,
integer, intent(in)  l_seg 
)
private

Parse an OBC_SEGMENT_%%% string starting with "J=" and configure placement and type of OBC accordingly.

Parameters
obcOpen boundary control structure
[in]gOcean grid structure
[in]segment_strA string in form of "J=%,I=%:%,string"
[in]l_segwhich segment is this?

Definition at line 668 of file MOM_open_boundary.F90.

References allocate_obc_segment_data(), mom_error_handler::mom_error(), obc_direction_n, obc_direction_s, parse_segment_str(), and setup_segment_indices().

Referenced by open_boundary_config().

668  type(ocean_obc_type), pointer :: obc !< Open boundary control structure
669  type(dyn_horgrid_type), intent(in) :: g !< Ocean grid structure
670  character(len=*), intent(in) :: segment_str !< A string in form of "J=%,I=%:%,string"
671  integer, intent(in) :: l_seg !< which segment is this?
672  ! Local variables
673  integer :: j_obc, is_obc, ie_obc ! Position of segment in global index space
674  integer :: i, a_loop
675  character(len=32) :: action_str(5)
676 
677  ! This returns the global indices for the segment
678  call parse_segment_str(g%ieg, g%jeg, segment_str, j_obc, is_obc, ie_obc, action_str )
679 
680  call setup_segment_indices(g, obc%segment(l_seg),is_obc,ie_obc,j_obc,j_obc)
681 
682  j_obc = j_obc - g%jdg_offset ! Convert to local tile indices on this tile
683  is_obc = is_obc - g%idg_offset ! Convert to local tile indices on this tile
684  ie_obc = ie_obc - g%idg_offset ! Convert to local tile indices on this tile
685 
686  ! Hack to extend segment by one point
687  if (obc%extend_segments) then
688  if (is_obc<ie_obc) then
689  is_obc = is_obc - 1 ; ie_obc = ie_obc + 1
690  else
691  is_obc = is_obc + 1 ; ie_obc = ie_obc - 1
692  endif
693  endif
694 
695  if (ie_obc>is_obc) then
696  obc%segment(l_seg)%direction = obc_direction_s
697  else if (ie_obc<is_obc) then
698  obc%segment(l_seg)%direction = obc_direction_n
699  i=is_obc;is_obc=ie_obc;ie_obc=i
700  endif
701 
702  obc%segment(l_seg)%on_pe = .false.
703 
704  do a_loop = 1,5
705  if (len_trim(action_str(a_loop)) == 0) then
706  cycle
707  elseif (trim(action_str(a_loop)) == 'FLATHER') then
708  obc%segment(l_seg)%Flather = .true.
709  obc%segment(l_seg)%open = .true.
710  obc%Flather_v_BCs_exist_globally = .true.
711  obc%open_v_BCs_exist_globally = .true.
712  elseif (trim(action_str(a_loop)) == 'ORLANSKI') then
713  obc%segment(l_seg)%radiation = .true.
714  obc%segment(l_seg)%open = .true.
715  obc%open_v_BCs_exist_globally = .true.
716  elseif (trim(action_str(a_loop)) == 'OBLIQUE') then
717  obc%segment(l_seg)%oblique = .true.
718  obc%segment(l_seg)%open = .true.
719  obc%oblique_BCs_exist_globally = .true.
720  obc%open_v_BCs_exist_globally = .true.
721  elseif (trim(action_str(a_loop)) == 'NUDGED') then
722  obc%segment(l_seg)%nudged = .true.
723  obc%segment(l_seg)%Tnudge_in = 1.0/(3*86400)
724  obc%segment(l_seg)%Tnudge_out = 1.0/(360*86400)
725  obc%nudged_v_BCs_exist_globally = .true.
726  elseif (trim(action_str(a_loop)) == 'GRADIENT') then
727  obc%segment(l_seg)%gradient = .true.
728  obc%segment(l_seg)%open = .true.
729  obc%open_v_BCs_exist_globally = .true.
730  elseif (trim(action_str(a_loop)) == 'LEGACY') then
731  obc%segment(l_seg)%radiation = .true.
732  obc%segment(l_seg)%Flather = .true.
733  obc%Flather_v_BCs_exist_globally = .true.
734  obc%open_v_BCs_exist_globally = .true.
735  elseif (trim(action_str(a_loop)) == 'SIMPLE') then
736  obc%segment(l_seg)%specified = .true.
737  obc%specified_v_BCs_exist_globally = .true. ! This avoids deallocation
738  ! Hack to undo the hack above for SIMPLE BCs
739  if (obc%extend_segments) then
740  is_obc = is_obc + 1
741  ie_obc = ie_obc - 1
742  endif
743  else
744  call mom_error(fatal, "MOM_open_boundary.F90, setup_v_point_obc: "//&
745  "String '"//trim(action_str(a_loop))//"' not understood.")
746  endif
747 
748  if (j_obc<g%HI%JsdB .or. j_obc>g%HI%JedB) return ! Boundary is not on tile
749  if (is_obc<g%HI%IsdB .and. ie_obc<g%HI%IsdB) return ! Segment is not on tile
750  if (is_obc>g%HI%IedB) return ! Segment is not on tile
751  enddo ! a_loop
752 
753  obc%segment(l_seg)%on_pe = .true.
754  obc%segment(l_seg)%is_N_or_S = .true.
755 
756  do i=g%HI%isd, g%HI%ied
757  if (i>is_obc .and. i<=ie_obc) then
758  obc%segnum_v(i,j_obc) = l_seg
759  endif
760  enddo
761  obc%segment(l_seg)%Is_obc = is_obc
762  obc%segment(l_seg)%Ie_obc = ie_obc
763  obc%segment(l_seg)%Js_obc = j_obc
764  obc%segment(l_seg)%Je_obc = j_obc
765  call allocate_obc_segment_data(obc, obc%segment(l_seg))
766 
Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_obc_segment_data()

subroutine, public mom_open_boundary::update_obc_segment_data ( type(ocean_grid_type), intent(in)  G,
type(verticalgrid_type), intent(in)  GV,
type(ocean_obc_type), pointer  OBC,
type(thermo_var_ptrs), intent(in)  tv,
real, dimension( g %isd: g %ied, g %jsd: g %jed, g %ke), intent(inout)  h,
type(time_type), intent(in)  Time 
)

Update the OBC values on the segments.

Parameters
[in]gOcean grid structure
[in]gvOcean vertical grid structure
obcOpen boundary structure
[in]tvThermodynamics structure
[in,out]hThickness

Definition at line 1807 of file MOM_open_boundary.F90.

References obc_direction_s, and obc_direction_w.

1807  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
1808  type(verticalgrid_type), intent(in) :: gv !< Ocean vertical grid structure
1809  type(ocean_obc_type), pointer :: obc !< Open boundary structure
1810  type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure
1811  real, dimension(SZI_(G),SZJ_(G), SZK_(G)), intent(inout) :: h !< Thickness
1812 ! real, dimension(SZI_(G),SZJ_(G), SZK_(G)), intent(inout) :: e !< Layer interface height
1813 ! real, dimension(SZI_(G),SZJ_(G)) , intent(inout) :: eta !< Thickness
1814  type(time_type), intent(in) :: time !< Time
1815  ! Local variables
1816 
1817  integer :: i, j, k, is, ie, js, je, isd, ied, jsd, jed
1818  integer :: isdb, iedb, jsdb, jedb, n, m, nz
1819  character(len=40) :: mdl = "set_OBC_segment_data" ! This subroutine's name.
1820  character(len=200) :: filename, obc_file, inputdir ! Strings for file/path
1821  type(obc_segment_type), pointer :: segment
1822  integer, dimension(4) :: siz,siz2
1823  real :: sumh ! column sum of thicknesses (m)
1824  integer :: ni_seg, nj_seg ! number of src gridpoints along the segments
1825  integer :: i2, j2 ! indices for referencing local domain array
1826  integer :: is_obc, ie_obc, js_obc, je_obc ! segment indices within local domain
1827  integer :: ishift, jshift ! offsets for staggered locations
1828  real, dimension(:,:), pointer :: seg_vel => null() ! pointer to segment velocity array
1829  real, dimension(:,:), pointer :: seg_trans => null() ! pointer to segment transport array
1830  real, dimension(:,:,:), allocatable :: tmp_buffer
1831 
1832  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1833  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
1834  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
1835  nz=g%ke
1836 
1837  if (.not. associated(obc)) return
1838 
1839  do n = 1, obc%number_of_segments
1840  segment => obc%segment(n)
1841 
1842  if (.not. segment%on_pe) cycle ! continue to next segment if not in computational domain
1843 
1844  ni_seg = segment%ie_obc-segment%is_obc+1
1845  nj_seg = segment%je_obc-segment%js_obc+1
1846  is_obc = max(segment%is_obc,isd-1)
1847  ie_obc = min(segment%ie_obc,ied)
1848  js_obc = max(segment%js_obc,jsd-1)
1849  je_obc = min(segment%je_obc,jed)
1850 
1851 
1852  if (segment%is_E_or_W) then
1853  nj_seg=nj_seg-1
1854  js_obc=js_obc+1
1855  else
1856  ni_seg=ni_seg-1
1857  is_obc=is_obc+1
1858  endif
1859 
1860 ! Calculate auxiliary fields at staggered locations.
1861 ! Segment indices are on q points:
1862 !
1863 ! |-----------|------------|-----------|-----------| J_obc
1864 ! Is_obc Ie_obc
1865 !
1866 ! i2 has to start at Is_obc+1 and end at Ie_obc.
1867 ! j2 is J_obc and jshift has to be +1 at both the north and south.
1868 
1869  ! calculate auxiliary fields at staggered locations
1870  ishift=0;jshift=0
1871  if (segment%is_E_or_W) then
1872  if (segment%direction == obc_direction_w) ishift=1
1873  i=segment%HI%IscB
1874  do j=segment%HI%jsd,segment%HI%jed
1875  segment%Cg(i,j) = sqrt(gv%g_prime(1)*g%bathyT(i+ishift,j))
1876  ! if (GV%Boussinesq) then
1877  segment%Htot(i,j) = g%bathyT(i+ishift,j)*gv%m_to_H! + eta(i+ishift,j)
1878  ! else
1879  ! segment%Htot(I,j) = eta(i+ishift,j)
1880  ! endif
1881  do k=1,g%ke
1882  segment%h(i,j,k) = h(i+ishift,j,k)
1883  enddo
1884  enddo
1885 
1886 
1887  else! (segment%direction == OBC_DIRECTION_N .or. segment%direction == OBC_DIRECTION_S)
1888  if (segment%direction == obc_direction_s) jshift=1
1889  j=segment%HI%JscB
1890  do i=segment%HI%isd,segment%HI%ied
1891  segment%Cg(i,j) = sqrt(gv%g_prime(1)*g%bathyT(i,j+jshift))
1892 ! if (GV%Boussinesq) then
1893  segment%Htot(i,j) = g%bathyT(i,j+jshift)*gv%m_to_H! + eta(i,j+jshift)
1894 ! else
1895 ! segment%Htot(i,J) = eta(i,j+jshift)
1896 ! endif
1897  do k=1,g%ke
1898  segment%h(i,j,k) = h(i,j+jshift,k)
1899 ! segment%e(i,J,k) = e(i,j+jshift,k)
1900  enddo
1901  enddo
1902  endif
1903 
1904  do m = 1,segment%num_fields
1905  if (segment%field(m)%fid > 0) then
1906  siz(1)=size(segment%field(m)%buffer_src,1)
1907  siz(2)=size(segment%field(m)%buffer_src,2)
1908  siz(3)=size(segment%field(m)%buffer_src,3)
1909  if (.not.associated(segment%field(m)%buffer_dst)) then
1910  if (siz(3) /= segment%field(m)%nk_src) call mom_error(fatal,'nk_src inconsistency')
1911  if (segment%field(m)%nk_src > 1) then
1912  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
1913  else
1914  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,1))
1915  endif
1916  segment%field(m)%buffer_dst(:,:,:)=0.0
1917  if (trim(segment%field(m)%name) == 'U' .or. trim(segment%field(m)%name) == 'V') then
1918  allocate(segment%field(m)%bt_vel(is_obc:ie_obc,js_obc:je_obc))
1919  segment%field(m)%bt_vel(:,:)=0.0
1920  endif
1921  endif
1922  ! read source data interpolated to the current model time
1923  if (siz(1)==1) then
1924  allocate(tmp_buffer(1,nj_seg*2+1,segment%field(m)%nk_src)) ! segment data is currrently on supergrid
1925  else
1926  allocate(tmp_buffer(ni_seg*2+1,1,segment%field(m)%nk_src)) ! segment data is currrently on supergrid
1927  endif
1928 
1929  call time_interp_external(segment%field(m)%fid,time, tmp_buffer)
1930  if (siz(1)==1) then
1931  segment%field(m)%buffer_src(is_obc,:,:)=tmp_buffer(1,2*(js_obc+g%jdg_offset)-1:2*(je_obc+g%jdg_offset)-1:2,:)
1932  else
1933  segment%field(m)%buffer_src(:,js_obc,:)=tmp_buffer(2*(is_obc+g%idg_offset)-1:2*(ie_obc+g%idg_offset)-1:2,1,:)
1934  endif
1935  if (segment%field(m)%nk_src > 1) then
1936  call time_interp_external(segment%field(m)%fid_dz,time, tmp_buffer)
1937  if (siz(1)==1) then
1938  segment%field(m)%dz_src(is_obc,:,:)=tmp_buffer(1,2*(js_obc+g%jdg_offset)-1:2*(je_obc+g%jdg_offset)-1:2,:)
1939  else
1940  segment%field(m)%dz_src(:,js_obc,:)=tmp_buffer(2*(is_obc+g%idg_offset)-1:2*(ie_obc+g%idg_offset)-1:2,1,:)
1941  endif
1942  do j=js_obc,je_obc
1943  do i=is_obc,ie_obc
1944 
1945  ! Using the h remapping approach
1946  ! Pretty sure we need to check for source/target grid consistency here
1947  segment%field(m)%buffer_dst(i,j,:)=0.0 ! initialize remap destination buffer
1948  if (g%mask2dT(i,j)>0.) then
1949  call remapping_core_h(obc%remap_CS, &
1950  segment%field(m)%nk_src,segment%field(m)%dz_src(i,j,:), &
1951  segment%field(m)%buffer_src(i,j,:), &
1952  g%ke, h(i,j,:), segment%field(m)%buffer_dst(i,j,:))
1953  endif
1954  enddo
1955  enddo
1956  else ! 2d data
1957  segment%field(m)%buffer_dst(:,:,1)=segment%field(m)%buffer_src(:,:,1) ! initialize remap destination buffer
1958  endif
1959  deallocate(tmp_buffer)
1960  else ! fid <= 0
1961  if (.not. ASSOCIATED(segment%field(m)%buffer_dst)) then
1962  allocate(segment%field(m)%buffer_dst(is_obc:ie_obc,js_obc:je_obc,g%ke))
1963  segment%field(m)%buffer_dst(:,:,:)=segment%field(m)%value
1964  if (trim(segment%field(m)%name) == 'U' .or. trim(segment%field(m)%name) == 'V') then
1965  allocate(segment%field(m)%bt_vel(is_obc:ie_obc,js_obc:je_obc))
1966  segment%field(m)%bt_vel(:,:)=segment%field(m)%value
1967  endif
1968  endif
1969  endif
1970 
1971  if (trim(segment%field(m)%name) == 'U' .or. trim(segment%field(m)%name) == 'V') then
1972  if (segment%field(m)%fid>0) then ! calculate external BT velocity and transport if needed
1973  if((trim(segment%field(m)%name) == 'U' .and. segment%is_E_or_W) .or. &
1974  (trim(segment%field(m)%name) == 'V' .and. segment%is_N_or_S)) then
1975  do j=js_obc,je_obc
1976  do i=is_obc,ie_obc
1977  segment%normal_trans_bt(i,j) = 0.0
1978  do k=1,g%ke
1979  segment%normal_vel(i,j,k) = segment%field(m)%buffer_dst(i,j,k)
1980  segment%normal_trans(i,j,k) = segment%field(m)%buffer_dst(i,j,k)*segment%h(i,j,k)
1981  segment%normal_trans_bt(i,j)= segment%normal_trans_bt(i,j)+segment%normal_trans(i,j,k)
1982  enddo
1983  segment%normal_vel_bt(i,j) = segment%normal_trans_bt(i,j)/max(segment%Htot(i,j),1.e-12)
1984  if (associated(segment%nudged_normal_vel)) segment%nudged_normal_vel(i,j,:) = segment%normal_vel(i,j,:)
1985  enddo
1986  enddo
1987  endif
1988  endif
1989  endif
1990 
1991  if (trim(segment%field(m)%name) == 'SSH') then
1992  do j=js_obc,je_obc
1993  do i=is_obc,ie_obc
1994  segment%eta(i,j) = segment%field(m)%buffer_dst(i,j,1)
1995  enddo
1996  enddo
1997  endif
1998  enddo
1999 
2000  enddo ! end segment loop
2001 
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19

Variable Documentation

◆ id_clock_pass

integer mom_open_boundary::id_clock_pass

Definition at line 200 of file MOM_open_boundary.F90.

Referenced by open_boundary_init(), and radiation_open_bdry_conds().

200 integer :: id_clock_pass

◆ max_obc_fields

integer, parameter mom_open_boundary::max_obc_fields = 100
private

Maximum number of data fields needed for OBC segments.

Definition at line 53 of file MOM_open_boundary.F90.

53 integer, parameter :: max_obc_fields = 100 !< Maximum number of data fields needed for OBC segments

◆ mdl

character(len=40) mom_open_boundary::mdl = "MOM_open_boundary"
private

Definition at line 202 of file MOM_open_boundary.F90.

Referenced by initialize_segment_data(), open_boundary_config(), and open_boundary_init().

202 character(len=40) :: mdl = "MOM_open_boundary" ! This module's name.

◆ obc_direction_e

integer, parameter, public mom_open_boundary::obc_direction_e = 300

Indicates the boundary is an effective eastern boundary.

Definition at line 51 of file MOM_open_boundary.F90.

Referenced by mom_continuity_ppm::continuity_ppm(), gradient_at_q_points(), open_boundary_impose_land_mask(), open_boundary_impose_normal_slope(), open_boundary_test_extern_h(), open_boundary_test_extern_uv(), radiation_open_bdry_conds(), set_tracer_data(), setup_u_point_obc(), and mom_continuity_ppm::zonal_mass_flux().

51 integer, parameter, public :: obc_direction_e = 300 !< Indicates the boundary is an effective eastern boundary

◆ obc_direction_n

◆ obc_direction_s

integer, parameter, public mom_open_boundary::obc_direction_s = 200

◆ obc_direction_w

integer, parameter, public mom_open_boundary::obc_direction_w = 400

Indicates the boundary is an effective western boundary.

Definition at line 52 of file MOM_open_boundary.F90.

Referenced by mom_continuity_ppm::continuity_ppm(), mom_vert_friction::find_coupling_coef(), open_boundary_impose_normal_slope(), radiation_open_bdry_conds(), set_tracer_data(), setup_u_point_obc(), update_obc_segment_data(), mom_vert_friction::vertvisc_coef(), and mom_continuity_ppm::zonal_mass_flux().

52 integer, parameter, public :: obc_direction_w = 400 !< Indicates the boundary is an effective western boundary

◆ obc_flather

integer, parameter, public mom_open_boundary::obc_flather = 3

Definition at line 47 of file MOM_open_boundary.F90.

47 integer, parameter, public :: obc_flather = 3

◆ obc_none

integer, parameter, public mom_open_boundary::obc_none = 0

Definition at line 46 of file MOM_open_boundary.F90.

Referenced by open_boundary_config(), and open_boundary_impose_land_mask().

46 integer, parameter, public :: obc_none = 0, obc_simple = 1, obc_wall = 2

◆ obc_radiation

integer, parameter, public mom_open_boundary::obc_radiation = 4

Definition at line 48 of file MOM_open_boundary.F90.

48 integer, parameter, public :: obc_radiation = 4

◆ obc_simple

integer, parameter, public mom_open_boundary::obc_simple = 1

Definition at line 46 of file MOM_open_boundary.F90.

◆ obc_wall

integer, parameter, public mom_open_boundary::obc_wall = 2

Definition at line 46 of file MOM_open_boundary.F90.