New URL for NEMO forge!   http://forge.nemo-ocean.eu

Since March 2022 along with NEMO 4.2 release, the code development moved to a self-hosted GitLab.
This present forge is now archived and remained online for history.
2011WP/2011Stream2/OpenBoundaries – NEMO
wiki:2011WP/2011Stream2/OpenBoundaries

Version 13 (modified by davestorkey, 13 years ago) (diff)

--

Open Boundary modules in NEMO: OBC/BDY merge

Last edited Timestamp?

As part of the 2011 workplan, it is planned to merge the OBC and BDY modules to provide a single module to do one-way nesting in NEMO. This page outlines the design for the new module. It is intended as a discussion document. Please feel free to add comments.

Introduction

Blah

Summary of main features

  • The following algorithms will be included in the first implementation:
    • Clamped and Flow Relaxation Scheme. (Clamped is FRS with rimwidth=1).
    • Flather radiation condition on barotropic solution.
    • NPO version of Orlanski radiation condition as currently included in OBC. Plan to code implicit time-stepping for greater stability and to avoid OBC restarts. May also code normal, and fully 2D Orlanski condition.
    • "Mercator-style" barotropic boundary conditions.
    • Tidal harmonic forcing (included in Flather boundary condition).
  • There will be two ways of specifying the boundary geometry:
    1. Using obc_par.h90 to define straight-line segments. One will be able to define more than one segment for each of east, west, south, north boundaries.
    2. Reading in the list of points from the data file as is now done in BDY.

  • The low-level code will use the BDY-style specification of the boundary geometry (an unstructured list of gridpoints). If obc_par.h90 is used then this will be converted to BDY-style arrays in the initialisation.
  • The read of external boundary data will be handled by fldread.F90 as is done for the SBC fields. This is clean and flexible. In the future this will permit online interpolation of boundary data.

  • It will be possible to define more than one open boundary set. This will allow different boundary conditions to be applied on different boundaries.

Control and coding structure

The namelist for the new module would look like this:

!-----------------------------------------------------------------------
\&namobc        !  one-way open boundaries                    ("key_obc")
!-----------------------------------------------------------------------
   nb_obc         = 2                       !  Number of open boundary sets
   ln_read        = .false.,.true.          !  Read in boundary definition or not
   cn_file        = '','coordinates_bdy.nc' !  Name of file with boundary definition
   ln_mask        = .false.,.true.,         !  boundary mask from cn_mask (T), boundaries are on edges of domain (F)
   cn_mask        = '','mask_bdy.nc'        !  name of mask file (ln_mask=T)
   nn_dyn3d       = 1, 0,                   !  Choice of schemes for 3D velocities
   nn_dyn2d       = 1, 0,                   !  Choice of schemes for barotropic solution (ln_dynspg_ts=.true.)
   nn_tra         = 1, 1,                   !  Choice of schemes for T and S 
   nn_ice_lim2    = 0, 0,                   !  Choice of schemes for ice (LIM2)
   ln_vol         = .false.,.false.,        !  total volume correction (see volbdy parameter)
   ln_tides       = .false.,.false.,        !  Apply tidal harmonic forcing with Flather condition
   nn_rimwidth    =  9, 1,                  !  width of the relaxation zone
   nn_dtactl      =  1, 1,                  !  = 0, bdy data are equal to the initial state
                                            !  = 1, bdy data are read in 'bdydata   .nc' files
   nn_volctl      =  0, 0,                  !  = 0, the total water flux across open boundaries is zero
                                            !  = 1, the total volume of the system is conserved
 /
!-----------------------------------------------------------------------
\&namobc_dta     !  one-way open boundaries                   ("key_obc")
!-----------------------------------------------------------------------
!               !  file name          ! frequency (hours) ! variable  ! time interp. !  clim  ! 'daily'/ ! weights  ! rotation !
!               !                     !  (if <0  months)  !   name    !   (logical)  !  (T/F) ! 'monthly' ! filename ! pairing  !
   bn_tem      = 'obcdta_grid_T'      ,        24          , 'votemper',   .true.     , .false., 'daily'  , ''       , ''
   bn_sal      = 'obcdta_grid_T'      ,        24          , 'vosaline',   .true.     , .false., 'daily'  , ''       , ''
   bn_uvel     = 'obcdta_grid_U'      ,        24          , 'vozocrtx',   .true.     , .false., 'daily'  , ''       , ''
   bn_vvel     = 'obcdta_grid_V'      ,        24          , 'vomecrty',   .true.     , .false., 'daily'  , ''       , ''
   bn_ssh      = 'obcdta_bt_grid_T'   ,         6          , 'sossheig',   .true.     , .false., 'daily'  , ''       , ''
   bn_ubar     = 'obcdta_bt_grid_U'   ,         6          , 'vozocrtx',   .true.     , .false., 'daily'  , ''       , ''
   bn_vbar     = 'obcdta_bt_grid_V'   ,         6          , 'vomecrty',   .true.     , .false., 'daily'  , ''       , ''

   cn_dir      = './obcdta'      !  root directory for the location of the boundary data files
/      
!-----------------------------------------------------------------------
\&namobc_dta     !  one-way open boundaries                   ("key_obc")
!-----------------------------------------------------------------------
!               !  file name          ! frequency (hours) ! variable  ! time interp. !  clim  ! 'daily'/ ! weights  ! rotation !
!               !                     !  (if <0  months)  !   name    !   (logical)  !  (T/F) ! 'monthly' ! filename ! pairing  !
   bn_tem      = 'obcdta_clim_grid_T' ,        -1          , 'votemper',   .true.     , .true., 'yearly'  , ''       , ''
   bn_sal      = 'obcdta_clim_grid_T' ,        -1          , 'vosaline',   .true.     , .true., 'yearly'  , ''       , ''

   cn_dir      = './obcdta'      !  root directory for the location of the boundary data files
/      

It is possible to define more than one open boundary set in case you want to use different boundary conditions for different boundaries. For instance one might want to apply different algorithms on the east and west boundaries of a domain, or one might want to apply boundary conditions from an external model over part of the open boundary but climatological boundary conditions over another part of the open boundary (as in the example above).

For each open boundary set you define which conditions to apply to each set of variables using nn_dyn, nn_tra etc. For example:

  • nn_tra = 0 : Apply no boundary condition (land boundary)
  • nn_tra = 1 : Clamped/relaxation boundary condition
  • nn_tra = 2 : Normal radiation condition
  • nn_tra = 3 : NPO radiation condition
  • etc.

There is a separate &namobc_dta namelist for each boundary set, which defines the input data files using the FLD structure from fldread.F90. (This will allow for online interpolation of boundary conditions in the future).

For each set of variables there is a module which applies the boundary conditions each timestep, eg. for T and S you have obctra.F90 which looks like this:

   MODULE obctra

CONTAINS
   
   SUBROUTINE obc_tra( kt ) 
   
   DO ib_set = 1, nb_set

      SELECT CASE ( nn_tra(ib_set) )
      CASE(0)
         CYCLE
      CASE(1)
         CALL obc_tra_frs( kt, ib_set )
      CASE(2) 
         CALL obc_tra_rad( kt, ib_set ) 
      END SELECT
  
   END DO
   
   END SUBROUTINE
   
   SUBROUTINE obc_tra_frs( kt, ib_set )
   ...

The module to read in the boundary data from files would be rewritten to use fldread.F90. For each boundary stream a TYPE FLD structure array would be constructed depending on which fields need to be read in. We can merge the obc_dta and obc_dta_bt routines by making the barotropic subloop counter jit an optional argument.

   MODULE obcdta

   USE fldread

   INTEGER  :: nb_dta(nb_obc_max)   
   TYPE(FLD), ALLOCATABLE, DIMENSION(:,:) ::   bf
   
CONTAINS
   
   SUBROUTINE obc_dta( kt, jit ) 

   INTEGER            :: kt        ! time step index
   INTEGER, OPTIONAL  :: jit       ! barotropic subloop index
   
   TYPE(FLD_N), ALLOCATABLE, DIMENSION(:) ::   blf_i

   IF( kt == nit000 ) THEN                ! First call kt=nit000  

      REWIND ( numnam )       
      jfld = 0
      jfld_prev = 0
      DO ib_set = 1, nb_set   
         IF ( nn_dtactl(ib_set) .eq. 1 ) THEN 
            ! set file information
            cn_dir = './'        ! directory in which the model is executed
            ! ... default values (NB: frequency positive => hours, negative => months)
            !              !  file   ! frequency !  variable  ! time intep !  clim   ! 'yearly' or ! weights  ! rotation  !
            !              !  name   !  (hours)  !   name     !   (T/F)    !  (T/F)  !  'monthly'  ! filename ! pairs     !
            bn_tem      = 'bn_tem'   ,    24     , 'bn_tem' ,   .true.     , .false. , 'daily'     , ''       , ''
            bn_sal      = 'bn_sal'   ,    24     , 'bn_sal' ,   .true.     , .false. , 'daily'     , ''       , ''
            bn_uvel     = 'bn_uvel'  ,    24     , 'bn_uvel',   .true.     , .false. , 'daily'     , ''       , ''
            bn_vvel     = 'bn_vvel'  ,    24     , 'bn_vvel',   .true.     , .false. , 'daily'     , ''       , ''
            bn_ssh      = 'bn_ssh'   ,    24     , 'bn_ssh' ,   .true.     , .false. , 'daily'     , ''       , ''
            bn_ubar     = 'bn_ubar'  ,    24     , 'bn_ubar',   .true.     , .false. , 'daily'     , ''       , ''
            bn_vbar     = 'bn_vbar'  ,    24     , 'bn_vbar',   .true.     , .false. , 'daily'     , ''       , ''

            READ   ( numnam, namobc_dta ) 

            ! Only read in necessary fields for this set.
            IF( nn_dyn2d(ib_set) .gt. 0 ) THEN
               jfld = jfld + 1 
               blf_i(jfd) = bn_ssh
               jfld = jfld + 1
               blf_i(jfld) = bn_ubar
               jfld = jfld + 1
               blf_i(jfld) = bn_vbar
            ENDIF
            IF( nn_tra(ib_set) .gt. 0 ) THEN
            ....
         
            nb_dta(ib_set) = jfld - jfld_prev
            jfld_prev = jfld
         END IF
      END DO

      ALLOCATE( bf(SUM(nb_dta)), STAT=ierror )        ! set sf structure

      jfld = 0
      DO ib_set = 1, nb_set   
         IF ( nn_dtactl(ib_set) .eq. 1 ) THEN 

            DO ji= 1, nb_dta(ibset)
               jfld = jfld+1
               ALLOCATE( sf(jfld)%fnow(nblen_dta(ib_set),1,1) )
               IF( slf_i(jfld)%ln_tint ) ALLOCATE( sf(ji)%fdta(nblen_dta(ib_set),1,1,2) )
            END DO

         END IF
      END DO

      CALL fld_fill( bf, blf_i, ... )

   END IF

   jstart = 1
   jfld = 0
   DO ib_set = 1, nb_set   
      IF( nn_dtactl(ib_set) = 1 ) THEN
      
         IF( PRESENT(jit) ) THEN
            ! Update barotropic boundary conditions only
            ! jit is optional argument for fld_read
            IF( nn_dyn2d(ib_set) .gt. 0 ) THEN
               jend = jstart + 2
               CALL fld_read( kt, nn_fobc, bf(jstart:jend), jit=jit )
            ENDIF
         ELSE
            jend = jstart + nb_obc(ib_set) - 1
            CALL fld_read( kt, nn_fobc, bf(jstart:jend ) )
         ENDIF

         IF( nn_dyn2d .gt. 0 ) THEN
            jpfld = jpfld + 1
            sshbdy(ib_set,:) = bf(jpfld)%fnow
            jpfld = jpfld + 1
            ubar(ib_set,:) = bf(jpfld)%fnow
            ...
         ENDIF
      
         IF( .NOT. PRESENT(jit) ) THEN
            IF( nn_tra(ib_set) .gt. 0 ) THEN
               jpfld = jpfld + 1
               tbdy(ib_set,:) = bf(jpfld)%fnow
               ....
         

      END IF ! nn_dtactl(ib_set) = 1
   END DO  ! ib_set
   
   END SUBROUTINE

Boundary geometry and initialisation

The internal code of the new module will use 1D unstructured arrays to specify the boundary as is currently done in BDY. For the T-points along the boundary these are nbit, nbjt and nbrt, which specify the (i,j) coordinates of each point and the discrete distance from the boundary. Each boundary set can be initialised using straight-line segments in obc_par.F90 (ln_read=.false.) or read in from a coordinates_bdy.nc file (ln_read=.true.). If the boundary coordinates are read in from obc_par.F90 then the nbi, nbj, nbr arrays will be generated internally (and if necessary a coordinates_bdy.nc file could be written out).

The obc_par.F90 file will be the easiest way to define rectangular boundaries. More than one segment can be specified for each of the north, east, south and west boundaries as follows. Where there are more than one boundary set, the jpieob-type arrays are specified as 1D arrays and the nobcsege-type arrays are used to split the numbers between the different boundary sets.

   MODULE obc_par

   INTEGER, PARAMETER ::     & !: ! Number of open boundary segments
      nobcsege = 2, 0 
   INTEGER, PARAMETER, DIMENSION(nobcsege) ::     &  !:
      ! First column: Med Sea; Second: Baltic
      jpieob = (/ 1037 , jpiglo-2 /), & !: i-localization of the East open boundary
      jpjedt = (/ 473  ,   1476   /), & !: j-starting indice of the East open boundary
      jpjeft = (/ 855  ,   1548   /)    !: j-ending   indice of the East open boundary
   !! * WEST open boundary
   INTEGER, PARAMETER ::     &  !: ! Number of open boundary segments
      nobcsegw = 1, 0
   INTEGER, PARAMETER, DIMENSION(nobcsegw) ::     &
      jpiwob = (/  2 /), &       !: i-localization of the West open boundary
      jpjwdt = (/  2 /), &       !: j-starting indice of the West open boundary
      jpjwft = (/1875/)       !: j-ending   indice of the West open boundary
   ...

For unstructured boundaries, the easiest method will be to generate them offline and read in the boundary definition from the coordinates_bdy.nc file, which looks like this:

netcdf coordinates_bdy {
dimensions:
        xbT = 8485 ;
        xbU = 8436 ;
        xbV = 8455 ;
        xbF = 8062 ;
        yb = 1 ;
variables:
        int nbit(yb, xbT) ;
        int nbiu(yb, xbU) ;
        int nbiv(yb, xbV) ;
        int nbif(yb, xbF) ;
        int nbjt(yb, xbT) ;
        int nbju(yb, xbU) ;
        int nbjv(yb, xbV) ;
        int nbjf(yb, xbF) ;
        int nbrt(yb, xbT) ;
        int nbru(yb, xbU) ;
        int nbrv(yb, xbV) ;
        int nbrf(yb, xbF) ;
        float glamt(yb, xbT) ;
                glamt:units = "degrees_east" ;
        float glamu(yb, xbU) ;
                glamu:units = "degrees_east" ;
        float glamv(yb, xbV) ;
                glamv:units = "degrees_east" ;
        float glamf(yb, xbF) ;
                glamf:units = "degrees_east" ;

Attachments (1)

Download all attachments as: .zip