!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! GNU General Public License !!
!! !!
!! This file is part of the Flexible Modeling System (FMS). !!
!! !!
!! FMS is free software; you can redistribute it and/or modify !!
!! it and are expected to follow the terms of the GNU General Public !!
!! License as published by the Free Software Foundation. !!
!! !!
!! FMS is distributed in the hope that it will be useful, !!
!! but WITHOUT ANY WARRANTY; without even the implied warranty of !!
!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !!
!! GNU General Public License for more details. !!
!! !!
!! You should have received a copy of the GNU General Public License !!
!! along with FMS; if not, write to: !!
!! Free Software Foundation, Inc. !!
!! 59 Temple Place, Suite 330 !!
!! Boston, MA 02111-1307 USA !!
!! or see: !!
!! http://www.gnu.org/licenses/gpl.txt !!
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
module ocean_xlandmix_mod
!
! S.M. Griffies
!
!
! M.J. Harrison
!
!
! K. Dixon
!
!
!
! Tracer and mass source from cross-land mixing.
!
!
!
! Compute thickness weighted and density weighted tendency
! [tracer*(kg/m^3)*meter/sec] of tracer and mass associated with
! cross-land mixing of points.
!
! In particular, this module provides interaction between bodies of
! water separated by land in coarse models (e.g., Med-Atlantic).
! Interaction consists of mixing tracer and mass between water
! columns found on each side of the land barrier.
!
! To conserve total tracer when using xlandmix with k=1,
! also need to connect mass between remote bodies of water
! via xlandmix of eta.
!
! When connecting cells with k>1, we do not mix mass. This
! is ensured by making the time tendency only a function of the
! difference in tracer concentration.
!
!
!
!
!
!
! S.M. Griffies, M.J. Harrison, R. C. Pacanowski, and A. Rosati
! A Technical Guide to MOM4 (2004)
! NOAA/Geophysical Fluid Dynamics Laboratory
!
!
!
! S.M. Griffies, Elements of mom4p1 (2006)
! NOAA/Geophysical Fluid Dynamics Laboratory
!
!
!
! Algorithm ensures both total tracer and total mass is conserved.
!
!
!
! 2D domain decomposition is implemented according to following
! notions. If the two points in an xlandmix pair live within halo
! of each other (i.e., within same local_domain),
! then no added mpp communication required. However, more generally
! the two points live further away. In this case, xland_domain
! is defined so that its halos incorporate the maximum separation
! of xlandmix points. New tracer, eta, and grid arrays
! are defined over this extended xland_domain. This added domain
! size will come at some computational cost, so it is advantageous
! to limit the separation between points within an xlandmix pair.
!
!
!
! The current implementation of xlandmix has not been generalized
! to allow for communication between points separated by the tripolar fold.
! The problem is in the logic used to compute xland_domain.
! There is nothing fundamental limiting a generalization of the
! logic used to generate xland_domain.
!
!
!
! Many of the user specified values given in USER INPUT are
! model dependent since unresolved straits can become resolved
! in finer mesh models.
!
!
!
! Algorithm originally developed by Keith Dixon (Keith.Dixon@noaa.gov)
! for rigid lid and full cells and one processor (i.e., MOM1).
! Algorithm extended to free surface and partial cells and 2D
! domain decomposition by S.M.Griffies (Stephen.Griffies@noaa.gov).
! Further extensions to generalized vertical coordinates by
! Stephen.Griffies@noaa.gov
!
!
!
!
!
!
! Needs to be true in order to use this scheme. Default is false.
!
!
! To allow xlandmixing to occur at k=kmt cell.
! Default is xlandmix_kmt=.false.
!
!
! For verbose initialization information.
!
!
use constants_mod, only: epsln
use diag_manager_mod, only: register_diag_field, send_data
use field_manager_mod, only: MODEL_OCEAN, parse, find_field_index
use field_manager_mod, only: get_field_methods, method_type, get_field_info
use fms_mod, only: stdout, stdlog, FATAL, NOTE, WARNING
use fms_mod, only: write_version_number, open_namelist_file, check_nml_error, close_file
use mpp_domains_mod, only: mpp_update_domains
use mpp_mod, only: mpp_error
use ocean_domains_mod, only: get_local_indices, set_ocean_domain
use ocean_parameters_mod, only: missing_value, rho0, onehalf
use ocean_types_mod, only: ocean_domain_type, ocean_grid_type, ocean_external_mode_type
use ocean_types_mod, only: ocean_thickness_type, ocean_time_type, ocean_density_type
use ocean_types_mod, only: ocean_prog_tracer_type, ocean_options_type
use ocean_workspace_mod, only: wrk1, wrk1_v
implicit none
private
public ocean_xlandmix_init
public xlandmix
private xlandmix_mass
type(ocean_domain_type), pointer :: Dom => NULL()
type(ocean_grid_type), pointer :: Grd => NULL()
! Variables set inside subroutine ocean_xlandmix_init
! See comments in ocean_xlandmix_init for description
integer, dimension(:,:), allocatable :: ixland
integer, dimension(:,:), allocatable :: jxland
integer, dimension(:,:), allocatable :: kxland
real, dimension(:), allocatable :: vxland
! number of xland mixing pairs
integer :: nxland=0
! Variables calculated from user input
real, dimension(:,:), allocatable :: depth_static ! time independent depth of columns (m)
real, dimension(:,:), allocatable :: gamma_xland ! inverse damping time (s^-1)
integer :: xland_halo ! halo size needed to perform xlandmix
integer :: kxland_min ! min of the k-levels for the nxland pairs
integer :: kxland_max ! max of the k-levels for the nxland pairs
type(ocean_domain_type), save :: Xland_domain ! domain for xlandmix
! variables defined with halos given by xland_halo
real, dimension(:,:), allocatable :: xland_mass ! mass source associated with xlandmix kg/(m^2*sec)
real, dimension(:,:), allocatable :: xland_mass_pair ! mass source on space of xlandmix pairs kg/(m^2*sec)
real, dimension(:,:,:), allocatable :: tracerx ! tracer concentration at taum1
real, dimension(:,:), allocatable :: xtx ! zonal position of tracer points (degrees)
real, dimension(:,:), allocatable :: ytx ! meridional position of tracer points (degrees)
real, dimension(:,:), allocatable :: datx ! area of tracer cell (m^2)
real, dimension(:,:,:), allocatable :: rho_dzt_x ! mass per area of tracer cell (kg/m^2)
integer :: unit=6 !processor zero writes to unit 6
logical, dimension(:), allocatable :: error_xland !for checking that all xland points are OK.
! for diagnostics
logical :: used
integer, dimension(:), allocatable :: id_xland
integer :: id_xland_mass=-1
integer :: id_neutral_rho_xlandmix=-1
integer :: id_wdian_rho_xlandmix =-1
integer :: num_prog_tracers=0
integer :: index_temp=-1
integer :: index_salt=-1
character(len=128) :: version=&
'$Id: ocean_xlandmix.F90,v 16.0.108.1 2009/10/10 00:42:57 nnz Exp $'
character (len=128) :: tagname = &
'$Name: mom4p1_pubrel_dec2009_nnz $'
integer :: isd, ied, jsd, jed, isc, iec, jsc, jec, nk
integer :: ixl, jxl, ixl2, jxl2
integer :: kmt_offset=1
logical :: verbose_init = .true.
logical :: use_this_module = .false.
logical :: module_is_initialized = .FALSE.
logical :: xlandmix_kmt = .false.
namelist /ocean_xlandmix_nml/ verbose_init, use_this_module, xlandmix_kmt
contains
!#######################################################################
!
!
!
! Initial set up for crossland mixing of tracers
!
! (i,j,k) locations of crossland mixing points are set via
! field table entries.
!
! Time invariant crossland mixing rates (in units of m**3/sec) are
! set via field table entries.
!
! Checks are performed to ensure that the crossland mixing
! grid locations are valid according to model configuration.
!
! A summary of the locations of crossland mixing points is written out.
!
! User specified inputs in "USER INPUT" section:
!
! ixland and jxland = user specified nxland pairs of i,j grid locations
!
! kxland = user specified uppermost (ktop=kxland(n,1)) and deepest
! (kbot=kxland(n,2)) levels over which crossland mixing will
! be done for each pair of crossland mixing points.
!
! vxland = user specified time invariant rates of crossland mixing (m3/sec)
! Equivalent to "the flow to the east = the flow to the west"
! Dynamic vxland is not available in MOM4.
!
! NOTE: for ixland, jxland, and kxland pairs, values of the
! (n,1) element should be < the corresponding (n,2) value.
!
!
subroutine ocean_xlandmix_init(Grid, Domain, Time, T_prog, Ocean_options, dtime)
type(ocean_grid_type), intent(in), target :: Grid
type(ocean_domain_type), intent(in), target :: Domain
type(ocean_time_type), intent(in) :: Time
type(ocean_prog_tracer_type), intent(in) :: T_prog(:)
type(ocean_options_type), intent(inout) :: Ocean_options
real, intent(in) :: dtime
type(method_type), allocatable, dimension(:) :: xland_methods
character(len=32) :: fld_type, fld_name
logical :: error_check=.false.
integer :: i, n, nt, model, imax, imin
integer :: nxl, lx, xland_halo_test
integer :: parse_ok
integer :: kmt_max
integer :: io_status, ioun, ierr
real :: ztop
integer :: stdoutunit,stdlogunit
stdoutunit=stdout();stdlogunit=stdlog()
if (module_is_initialized ) then
call mpp_error(FATAL, &
'==>Error in ocean_xlandmix_mod (ocean_xlandmix_init): module already initialized')
endif
module_is_initialized = .TRUE.
call write_version_number( version, tagname )
call get_local_indices(Domain,isd,ied,jsd,jed,isc,iec,jsc,jec)
nk = Grid%nk
Dom => Domain
Grd => Grid
num_prog_tracers = size(T_prog(:))
do nt=1,num_prog_tracers
if (T_prog(nt)%name == 'temp') index_temp = nt
if (T_prog(nt)%name == 'salt') index_salt = nt
enddo
n=find_field_index(MODEL_OCEAN,'xland_mix')
if (n < 1) then
Ocean_options%xlandmix = 'Did NOT use xlandmix.'
return
endif
call get_field_info(n,fld_type,fld_name,model,nxland)
! provide for namelist over-ride of default values
ioun = open_namelist_file()
read (ioun,ocean_xlandmix_nml,IOSTAT=io_status)
write (stdlogunit,ocean_xlandmix_nml)
write (stdoutunit,'(/)')
write (stdoutunit,ocean_xlandmix_nml)
ierr = check_nml_error(io_status,'ocean_xlandmix_nml')
call close_file (ioun)
if(use_this_module) then
if(nxland == 0) then
write(stdoutunit, *) &
'==>Warning: use_this_module=.true. but no mixing points chosen. Is this what you wish?'
call mpp_error(WARNING, &
'==>NOTE from ocean_xlandmix_mod: No cross-land mixing points chosen')
Ocean_options%xlandmix = 'Used xlandmix, but set zero cross-land points, so module does nothing.'
return
else
call mpp_error(NOTE, &
'==>Note from ocean_xlandmix_mod (ocean_xlandmix_init): USING this module')
write(stdoutunit, *) &
'==>Note: Using xlandmix to connect tracers and mass between non-local ocean cells.'
Ocean_options%xlandmix = 'Used xlandmix to transfer tracer between inland seas and open ocean.'
endif
else
call mpp_error(NOTE, '==>Note from ocean_xlandmix_mod: NOT USING this module')
if(nxland > 0) then
call mpp_error(WARNING, &
'==>Warning: nxland > 0, but use_this_module=.false. NOT using xlandmix. Is this what you wish?')
endif
Ocean_options%xlandmix = 'Did NOT use xlandmix.'
return
endif
write(stdoutunit,'(/a,f10.2)') &
'==> Note from ocean_xlandmix_mod: using forward time step of (secs)', dtime
if(xlandmix_kmt) then
kmt_offset=0
write(stdoutunit,'(/a)') &
'==> Note from ocean_xlandmix_mod: allowing xlandmix to occur at k=kmt.'
else
kmt_offset=1
endif
allocate(xland_methods(nxland))
allocate (ixland(nxland,2))
ixland(:,:) = 0
allocate (jxland(nxland,2))
jxland(:,:) = 0
allocate (kxland(nxland,2))
kxland(:,:) = 0
allocate (vxland(nxland))
vxland(:) = 0.0
allocate (depth_static(nxland,2))
depth_static(:,:) = 0.0
allocate (gamma_xland(nxland,2))
gamma_xland(:,:) = 0.0
allocate (error_xland(nxland))
error_xland(:) = .false.
call get_field_methods(n,xland_methods)
do i=1, nxland
parse_ok = parse(xland_methods(i)%method_control,'ixland_1',ixland(i,1))
if (parse_ok == 0) call mpp_error(FATAL,&
'==>Error ocean_xlandmix_mod: xland table entry "ixland_1" error')
parse_ok = parse(xland_methods(i)%method_control,'ixland_2',ixland(i,2))
if (parse_ok == 0) call mpp_error(FATAL,&
'==>Error ocean_xlandmix_mod: xland table entry "ixland_2" error')
parse_ok = parse(xland_methods(i)%method_control,'jxland_1',jxland(i,1))
if (parse_ok == 0) call mpp_error(FATAL,&
'==>Error ocean_xlandmix_mod: xland table entry "jxland_1" error')
parse_ok = parse(xland_methods(i)%method_control,'jxland_2',jxland(i,2))
if (parse_ok == 0) call mpp_error(FATAL,&
'==>Error ocean_xlandmix_mod: xland table entry "jxland_2" error')
parse_ok = parse(xland_methods(i)%method_control,'kxland_1',kxland(i,1))
if (parse_ok == 0) call mpp_error(FATAL,&
'==>Error ocean_xlandmix_mod: xland table entry "kxland_1" error')
parse_ok = parse(xland_methods(i)%method_control,'kxland_2',kxland(i,2))
if (parse_ok == 0) call mpp_error(FATAL,&
'==>Error ocean_xlandmix_mod: xland table entry "kxland_2" error')
parse_ok = parse(xland_methods(i)%method_control,'vxland',vxland(i))
if (parse_ok == 0) call mpp_error(FATAL,&
'==>Error ocean_xlandmix_mod: xland table entry "vxland" error')
enddo
if(Grid%tripolar) then
write(stdoutunit,'(a)') &
'==>Warning: xlandmix has not been implemented to connect points across the tripolar fold.'
endif
xland_halo = min(isc-isd,ied-iec,jsc-jsd,jed-jec)
kxland_min = nk
kxland_max = 0
do nxl=1,nxland
! check for invalid crossland mixing grid locations
if (kxland(nxl,1) > kxland(nxl,2)) then
write (unit,994) nxl, kxland(nxl,1), nxl, kxland(nxl,2)
error_xland(nxl) = .true.
endif
if (kxland(nxl,1) < kxland_min ) kxland_min = kxland(nxl,1)
if (kxland(nxl,2) > kxland_max ) kxland_max = kxland(nxl,2)
do lx = 1,2
if (ixland(nxl,lx) < 1 .or. ixland(nxl,lx) > Grd%ni) then
write(unit,991) nxl, lx, ixland(nxl,lx)
error_xland(nxl) = .true.
endif
if (jxland(nxl,lx) < 1 .or. jxland(nxl,lx) > Grd%nj ) then
write(unit,992) nxl, lx, jxland(nxl,lx)
error_xland(nxl) = .true.
endif
if (kxland(nxl,lx) < 1 .or. kxland(nxl,lx) > Grd%nk ) then
write(unit,993) nxl, lx, kxland(nxl,lx)
error_xland(nxl) = .true.
endif
enddo
! determine size for xland_halo
if(Grd%cyclic_x) then
imax = max(ixland(nxl,1),ixland(nxl,2))
imin = min(ixland(nxl,1),ixland(nxl,2))
xland_halo_test = min( abs(imax-imin), abs(imax-Grd%ni-imin))
else
xland_halo_test = abs(ixland(nxl,1)-ixland(nxl,2))
endif
if (xland_halo_test > xland_halo ) xland_halo = xland_halo_test
xland_halo_test = abs(jxland(nxl,1)-jxland(nxl,2))
if (xland_halo_test > xland_halo ) xland_halo = xland_halo_test
enddo ! end of do-loop nxl=1,nxland
if(xland_halo == 0) then
call mpp_error(FATAL,&
'==>Error in ocean_xlandmix_mod (ocean_xlandmix_init): xland_halo=0 => problems defining xlandmix points.')
endif
if(xland_halo > Dom%xhalo .or. xland_halo > Dom%yhalo) then
write(stdoutunit,*) &
'Defining extra tracer arrays for xland_domain over k-levels ',kxland_min,' through ',kxland_max
endif
allocate (xland_mass_pair(nxland,2))
xland_mass_pair(:,:) = 0.0
! define arrays and xland_domain
if(xland_halo <= Dom%xhalo .and. xland_halo <= Dom%yhalo) then
write(stdoutunit,*)'The model local computational domain has a x,y halo = ',Dom%xhalo,Dom%yhalo
write(stdoutunit,*)'This is large enough for chosen points in xlandmix,'
write(stdoutunit,*)'where we need to have a halo of size = ', xland_halo
! time independent arrays
allocate (datx(isd:ied,jsd:jed))
allocate (xtx(isd:ied,jsd:jed))
allocate (ytx(isd:ied,jsd:jed))
xtx(:,:) = Grd%xt(:,:)
ytx(:,:) = Grd%yt(:,:)
datx(:,:) = Grd%dat(:,:)
! time dependent arrays
allocate (rho_dzt_x(isd:ied,jsd:jed,nk))
allocate (xland_mass(isd:ied,jsd:jed))
allocate (tracerx(1,1,1))
rho_dzt_x(:,:,:) = 0.0
xland_mass(:,:) = 0.0
tracerx(:,:,:) = 0.0
endif
if(xland_halo > Dom%xhalo .or. xland_halo > Dom%yhalo) then
write(stdoutunit,*)'The model local computational domain has a x,y halo = ',Dom%xhalo, Dom%yhalo
write(stdoutunit,*)'This is smaller than the halo required for xlandmix.'
write(stdoutunit,*)'For xlandmix, will define a new domain type with halo = ', xland_halo
if(xland_halo > (Dom%xhalo+6)) then
write(stdoutunit,*)'=>Warning: ocean_xlandmix_init determined xland_halo = ', xland_halo
write(stdoutunit,*)' Are you sure you wish to connect such distant locations?'
write(stdoutunit,*)' Are you instead trying to connect points across a tripolar fold?'
write(stdoutunit,*)' If so, then presently this has yet to be coded into xlandmix.'
write(stdoutunit,*)' Alternative xlandmix points must be taken, or new code implemented.'
endif
! define xlandmix domain
call set_ocean_domain(Xland_domain,Grd,xhalo=xland_halo,yhalo=xland_halo,&
name='xlandmix',maskmap=Dom%maskmap)
! time independent arrays defined over the xland_domain region
allocate (datx(isc-xland_halo:iec+xland_halo,jsc-xland_halo:jec+xland_halo))
allocate (xtx(isc-xland_halo:iec+xland_halo,jsc-xland_halo:jec+xland_halo))
allocate (ytx(isc-xland_halo:iec+xland_halo,jsc-xland_halo:jec+xland_halo))
datx(:,:) = 0.0
xtx(:,:) = 0.0
ytx(:,:) = 0.0
datx(isc:iec,jsc:jec) = Grd%dat(isc:iec,jsc:jec)
xtx(isc:iec,jsc:jec) = Grd%xt(isc:iec,jsc:jec)
ytx(isc:iec,jsc:jec) = Grd%yt(isc:iec,jsc:jec)
call mpp_update_domains (datx(:,:), Xland_domain%domain2d)
call mpp_update_domains (xtx(:,:), Xland_domain%domain2d)
call mpp_update_domains (ytx(:,:), Xland_domain%domain2d)
! time dependent arrays defined over the xland_domain region
allocate (rho_dzt_x(isc-xland_halo:iec+xland_halo,jsc-xland_halo:jec+xland_halo,nk))
allocate (xland_mass(isc-xland_halo:iec+xland_halo,jsc-xland_halo:jec+xland_halo))
allocate (tracerx(isc-xland_halo:iec+xland_halo,jsc-xland_halo:jec+xland_halo,kxland_min:kxland_max))
rho_dzt_x(:,:,:) = 0.0
xland_mass(:,:) = 0.0
tracerx(:,:,:) = 0.0
endif
do nxl=1,nxland
! check for attempts to mix land rather than sea.
! Restrict xland mixing to be above the bottom-most level in a column.
do lx = 1,2
if(on_comp_domain(nxl, lx)) then
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
kmt_max = max(0,Grid%kmt(ixl,jxl)-kmt_offset)
if (kxland(nxl,2) > kmt_max) then
write (unit,192) nxl, kxland(nxl,2), ixland(nxl,lx),jxland(nxl,lx),Grid%kmt(ixl,jxl)
error_xland(nxl)=.true.
endif
endif
enddo
! write out summary information for this pair of crossland mixing points
if (kxland(nxl,1) == 1) then
ztop = 0.0
else
ztop = Grid%zw(kxland(nxl,1)-1)
endif
if(at_least_one_in_comp_domain(nxl)) then
ixl = ixland(nxl,1)-Dom%ioff
jxl = jxland(nxl,1)-Dom%joff
ixl2 = ixland(nxl,2)-Dom%ioff
jxl2 = jxland(nxl,2)-Dom%joff
if(verbose_init) then
write(unit,191) nxl, ixland(nxl,1), jxland(nxl,1) &
,xtx(ixl,jxl), ytx(ixl,jxl) &
,ixland(nxl,2), jxland(nxl,2) &
,xtx(ixl2,jxl2), ytx(ixl2,jxl2) &
,kxland(nxl,1), kxland(nxl,2), ztop, Grid%zw(kxland(nxl,2))
endif
call xland_check (nxl, dtime, error_check)
if(error_check) then
error_xland(nxl) = .true.
endif
endif
enddo
! Bring model down here if there are problems with xlandmix points.
do nxl=1,nxland
if(error_xland(nxl)) then
call mpp_error(FATAL, &
'==>Error detected in ocean_xlandmix_mod: Use "grep -i error" to find ALL errors.')
endif
enddo
! register for diag_manager
allocate (id_xland(num_prog_tracers))
id_xland = -1
do n=1,num_prog_tracers
if(T_prog(n)%name == 'temp') then
id_xland(n) = register_diag_field ('ocean_model', trim(T_prog(n)%name)//'_xland', &
Grd%tracer_axes(1:3), Time%model_time, &
'xlandmix*cp*rho*dzt*temp', 'Watt/m^2', &
missing_value=missing_value, range=(/-1.e10,1.e10/))
else
id_xland(n) = register_diag_field ('ocean_model', trim(T_prog(n)%name)//'_xland', &
Grd%tracer_axes(1:3), Time%model_time, &
'xlandmix*rho*dzt*tracer for '//trim(T_prog(n)%name), &
trim(T_prog(n)%flux_units), &
missing_value=missing_value, range=(/-1.e10,1.e10/))
endif
enddo
id_xland_mass = register_diag_field ('ocean_model', 'mass_xland', &
Grd%tracer_axes(1:2), Time%model_time, &
'mass source from xland', 'kg/(m^2*sec)', &
missing_value=missing_value, range=(/-1.e8,1.e8/))
id_neutral_rho_xlandmix = register_diag_field ('ocean_model', 'neutral_rho_xlandmix', &
Grd%tracer_axes(1:3), Time%model_time, &
'update of neutral density from xlandmix scheme', &
'rho*rho_dz/sec', missing_value=missing_value, range=(/-1.e10,1.e10/))
id_wdian_rho_xlandmix = register_diag_field ('ocean_model', 'wdian_rho_xlandmix', &
Grd%tracer_axes(1:3), Time%model_time, &
'dianeutral velocity component due to xlandmix scheme', &
'm/sec', missing_value=missing_value, range=(/-1.e10,1.e10/))
191 format(/' ===== from ocean_xland_init ====='/ &
' for crossland sea connection pair number',i3,/ &
' mix (i,j) gridpoint (',i4,',',i4,') [long=',f8.3,' lat=',f8.3,']',/ &
' with (i,j) gridpoint (',i4,',',i4,') [long=',f8.3,' lat=',f8.3,']',/ &
' from level',i3,' to',i3,' [depths of ',f10.3,' to ',f10.3,'m]' )
192 format(/'=>Error: Problem with crossland mixing: improper k-levels requested.'/&
'kxland(',i2,',2) =',i4, ' and kmt(',i4,',',i4,') = ',i4)
991 format(/'=>Error: problem with crossland tracer mixing:',/, &
' out of bounds i grid location requested'/ &
' ixland(',i4,',',i1,') was set incorrectly set to = ',i8,/)
992 format(/'=>Error: problem with crossland tracer mixing:',/, &
' out of bounds j-row grid location requested'/ &
' jxland(',i4,',',i1,') was set incorrectly set to = ',i8,/)
993 format(/'=>Error: problem with crossland tracer mixing:',/, &
' out of bounds k-level grid location requested'/ &
' kxland(',i4,',',i1,') was set incorrectly set to = ',i8,/)
994 format(/'=>Error: problem with crossland tracer mixing:',/, &
' improper k-values requested'/' kxland(',i3,',1)=',i5, &
' and is not less than or equal to kxland(',i3,',2)=',i5,/)
end subroutine ocean_xlandmix_init
! NAME="ocean_xlandmix_init"
!#######################################################################
!
!
!
! Compute thickness and density weighted time tendency from
! xlandmix. Units of the tendency are tracer*(kg/m^3)*meter/sec.
!
! Logic is a bit tricky in order to allow each (i,j,k) point
! to participate in an arbitrary number of xlandmix pairs.
!
!
!
subroutine xlandmix (Time, Ext_mode, Dens, Thickness, T_prog)
type(ocean_time_type), intent(in) :: Time
type(ocean_external_mode_type), intent(in) :: Ext_mode
type(ocean_density_type), intent(in) :: Dens
type(ocean_thickness_type), intent(inout) :: Thickness
type(ocean_prog_tracer_type), intent(inout) :: T_prog(:)
real, dimension(2) :: top_rho_dzt
real :: temporary
integer :: i, j, k, nxl, lx, taum1, tau, n
if (.not. module_is_initialized ) then
call mpp_error(FATAL, &
'==>Error in ocean_xlandmix_mod (xlandmix): module must be initialized')
endif
if (nxland < 1) return
if (.not. use_this_module) return
taum1 = Time%taum1
tau = Time%tau
xland_mass(:,:) = 0.0
xland_mass_pair(:,:) = 0.0
do n=1,num_prog_tracers
T_prog(n)%wrk1(:,:,:) = 0.0
if(xland_halo > Dom%xhalo .or. xland_halo > Dom%yhalo) then
do k=kxland_min,kxland_max
tracerx(isc:iec,jsc:jec,k) = T_prog(n)%field(isc:iec,jsc:jec,k,taum1)
rho_dzt_x(isc:iec,jsc:jec,k) = Thickness%rho_dzt(isc:iec,jsc:jec,k,taum1)
enddo
! for surface cell, wish to have only difference be due to eta_t differences
! to ensure that xlandmix at surface will readily equilize eta_t across straits.
! if use rho_dzt_x = rho0*Thickness%dzt, then with zstar and pstar, there would
! be a far smaller difference in rho_dzt_x than for the following specification,
! which is based on the geopotential case.
rho_dzt_x(isc:iec,jsc:jec,1) = rho0*(Grd%dzt(1)+Ext_mode%eta_t(isc:iec,jsc:jec,taum1))
call mpp_update_domains (tracerx(:,:,:), Xland_domain%domain2d)
call mpp_update_domains (rho_dzt_x(:,:,:), Xland_domain%domain2d)
do nxl=1,nxland
if(at_least_one_in_comp_domain(nxl)) then
do lx=1,2
ixl = ixland(nxl,lx)-Dom%ioff; jxl = jxland(nxl,lx)-Dom%joff
gamma_xland(nxl,lx) = (2.0*vxland(nxl))/ &
(datx(ixl,jxl)*(depth_static(nxl,1)+depth_static(nxl,2)))
enddo
! tracer exchange for k>1
do lx=1,2
if(on_comp_domain(nxl,lx)) then
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
ixl2 = ixland(nxl,3-lx)-Dom%ioff
jxl2 = jxland(nxl,3-lx)-Dom%joff
do k=max(2,kxland(nxl,1)),kxland(nxl,2)
T_prog(n)%wrk1(ixl,jxl,k) = T_prog(n)%wrk1(ixl,jxl,k) + gamma_xland(nxl,lx) &
*onehalf*(rho_dzt_x(ixl2,jxl2,k)+rho_dzt_x(ixl,jxl,k)) &
*(tracerx(ixl2,jxl2,k)-tracerx(ixl,jxl,k))
enddo
endif
enddo
! mass and tracer exchange for k=1
if(kxland(nxl,1)==1) then
k=1
do lx=1,2
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
top_rho_dzt(lx) = rho_dzt_x(ixl,jxl,k)
enddo
if(n==1) then
do lx=1,2
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
xland_mass_pair(nxl,lx) = gamma_xland(nxl,lx)*( top_rho_dzt(3-lx)-top_rho_dzt(lx) )
xland_mass(ixl,jxl) = xland_mass(ixl,jxl) + xland_mass_pair(nxl,lx)
enddo
endif
do lx=1,2
if(on_comp_domain(nxl,lx)) then
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
ixl2 = ixland(nxl,3-lx)-Dom%ioff
jxl2 = jxland(nxl,3-lx)-Dom%joff
T_prog(n)%wrk1(ixl,jxl,k) = T_prog(n)%wrk1(ixl,jxl,k) &
+gamma_xland(nxl,lx)*(top_rho_dzt(3-lx)*tracerx(ixl2,jxl2,k) &
-top_rho_dzt(lx)*tracerx(ixl,jxl,k))
endif
enddo
endif
endif ! end of at_least_one_in_comp_domain(nxl) if-test
enddo ! end of the nxland do-loop
endif ! end of xland_halo > halo if-test
if(xland_halo <= Dom%xhalo .and. xland_halo<= Dom%yhalo) then
do nxl=1,nxland
if(at_least_one_in_comp_domain(nxl)) then
do lx=1,2
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
gamma_xland(nxl,lx) = (2.0*vxland(nxl))/ &
(Grd%dat(ixl,jxl)*(depth_static(nxl,1)+depth_static(nxl,2)))
enddo
! tracer exchange for k>1
do lx=1,2
if(on_comp_domain(nxl,lx)) then
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
ixl2 = ixland(nxl,3-lx)-Dom%ioff
jxl2 = jxland(nxl,3-lx)-Dom%joff
do k=max(2,kxland(nxl,1)),kxland(nxl,2)
T_prog(n)%wrk1(ixl,jxl,k) = T_prog(n)%wrk1(ixl,jxl,k) + gamma_xland(nxl,lx) &
*onehalf*(Thickness%rho_dzt(ixl2,jxl2,k,taum1)+Thickness%rho_dzt(ixl,jxl,k,taum1)) &
*(T_prog(n)%field(ixl2,jxl2,k,taum1)-T_prog(n)%field(ixl,jxl,k,taum1))
enddo
endif
enddo
! mass and tracer exchange for k=1
if(kxland(nxl,1)==1) then
k=1
do lx=1,2
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
top_rho_dzt(lx) = rho0*(Grd%dzt(1)+Ext_mode%eta_t(ixl,jxl,taum1))
enddo
if(n==1) then
do lx=1,2
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
xland_mass_pair(nxl,lx) = gamma_xland(nxl,lx)*( top_rho_dzt(3-lx)-top_rho_dzt(lx) )
xland_mass(ixl,jxl) = xland_mass(ixl,jxl) + xland_mass_pair(nxl,lx)
enddo
endif
do lx=1,2
if(on_comp_domain(nxl,lx)) then
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
ixl2 = ixland(nxl,3-lx)-Dom%ioff
jxl2 = jxland(nxl,3-lx)-Dom%joff
T_prog(n)%wrk1(ixl,jxl,k) = T_prog(n)%wrk1(ixl,jxl,k) &
+gamma_xland(nxl,lx)*(top_rho_dzt(3-lx)*T_prog(n)%field(ixl2,jxl2,k,taum1) &
-top_rho_dzt(lx) *T_prog(n)%field(ixl,jxl,k,taum1))
endif
enddo
endif
endif ! end of at_least_one_in_comp_domain(nxl) if-test
enddo ! end of the nxland do-loop
endif ! end of xland_halo > halo if-test
! fill source array
T_prog(n)%th_tendency(isc:iec,jsc:jec,:) = T_prog(n)%th_tendency(isc:iec,jsc:jec,:) &
+ T_prog(n)%wrk1(isc:iec,jsc:jec,:)
! send diagnostics
if(id_xland(n) > 0) then
used = send_data (id_xland(n), T_prog(n)%conversion*T_prog(n)%wrk1(:,:,:), &
Time%model_time, rmask=Grd%tmask(:,:,:), &
is_in=isc, js_in=jsc, ks_in=1, ie_in=iec, je_in=jec, ke_in=nk)
endif
enddo ! end of do loop n=1,num_prog_tracers
! compute effects on mass
call xlandmix_mass(Time, Thickness)
! send diagnostics for update of neutral density from xlandmix scheme
if (id_neutral_rho_xlandmix > 0) then
wrk1(:,:,:) = 0.0
do k=1,nk
do j=jsc,jec
do i=isc,iec
wrk1(i,j,k) = Grd%tmask(i,j,k) &
*(Dens%drhodT(i,j,k)*T_prog(index_temp)%wrk1(i,j,k) &
+Dens%drhodS(i,j,k)*T_prog(index_salt)%wrk1(i,j,k))
enddo
enddo
enddo
used = send_data (id_neutral_rho_xlandmix, wrk1(:,:,:), &
Time%model_time,rmask=Grd%tmask(:,:,:),&
is_in=isc, js_in=jsc, ks_in=1, ie_in=iec, je_in=jec, ke_in=nk)
endif
if (id_wdian_rho_xlandmix > 0) then
wrk1(:,:,:) = 0.0
do k=1,nk
do j=jsc,jec
do i=isc,iec
temporary = Dens%drhodz_zt(i,j,k)*Thickness%rho_dzt(i,j,k,tau)
wrk1(i,j,k) = Grd%tmask(i,j,k) &
*(Dens%drhodT(i,j,k)*T_prog(index_temp)%wrk1(i,j,k) &
+Dens%drhodS(i,j,k)*T_prog(index_salt)%wrk1(i,j,k))&
/(epsln+temporary)
enddo
enddo
enddo
used = send_data (id_wdian_rho_xlandmix, wrk1(:,:,:), &
Time%model_time,rmask=Grd%tmask(:,:,:), &
is_in=isc, js_in=jsc, ks_in=1, ie_in=iec, je_in=jec, ke_in=nk)
endif
end subroutine xlandmix
! NAME="xlandmix"
!#######################################################################
!
!
!
! Compute the mass source kg/(m^2*sec).
! Note that xlandmix has already been called, so xland_mass has been
! filled.
!
!
subroutine xlandmix_mass (Time, Thickness)
type(ocean_time_type), intent(in) :: Time
type(ocean_thickness_type), intent(inout) :: Thickness
if (.not. module_is_initialized ) then
call mpp_error(FATAL, &
'==>Error in ocean_xlandmix_mod (xlandmix_mass): module must be initialized')
endif
Thickness%mass_source(isc:iec,jsc:jec,1) = Thickness%mass_source(isc:iec,jsc:jec,1) &
+ xland_mass(isc:iec,jsc:jec)
call mpp_update_domains (Thickness%mass_source(:,:,1), Dom%domain2d)
if(id_xland_mass > 0) then
used = send_data (id_xland_mass, xland_mass(isc:iec,jsc:jec), &
Time%model_time, rmask=Grd%tmask(isc:iec,jsc:jec,1))
endif
end subroutine xlandmix_mass
! NAME="xlandmix_mass"
!#######################################################################
!
!
!
! Check if prescribed too much mixing
!
! In this routine the crossland mixing rate vxland(nxl) for
! the pair of points associated with index number nxl is
! converted into the fraction of the model grid boxes to be mixed
! per second, and checked. These checks ensure that the rate of
! crossland mixing requested is valid in that it can be realized
! given the timestep length and column volumes involved.
!
!
!
! Integer labeling the particular xlandmix pair
!
!
! Error flag indicates whether initialization was performed successfully.
!
!
subroutine xland_check(nxl, dtime, error)
logical, intent(inout) :: error
integer, intent(in) :: nxl
real, intent(in) :: dtime
integer :: k, lx, ll
real, dimension(2) :: colvol
real :: factor, tslim
real, dimension(nxland,2) :: bxland
if (.not. module_is_initialized ) then
call mpp_error(FATAL, &
'==>Error in ocean_xlandmix_mod (xland_check): module must be initialized')
endif
! Calculate static depth range over which crossland mixing will be applied.
! Note that do not include partial bottom cell in kxland range.
do lx=1,2
depth_static(nxl,lx) = 0.0
do k=max(1,kxland(nxl,1)),kxland(nxl,2)
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
depth_static(nxl,lx) = depth_static(nxl,lx) + Grd%dzt(k)
enddo
enddo
do lx=1,2
! For each of the two (i,j) points to be mixed, convert from volume
! to be mixed per second vxland(nxl) to model box fraction to be
! mixed per second bxland(nxl,l)
ixl = ixland(nxl,lx)-Dom%ioff
jxl = jxland(nxl,lx)-Dom%joff
colvol(lx) = datx(ixl,jxl)*depth_static(nxl,lx)
bxland(nxl,lx) = vxland(nxl) / (epsln + colvol(lx))
! check for attempts to mix at a rate so fast that it can not
! be stably achieved in a single forward timestep
! (for the two boxes to be thoroughly mixed, no more than
! one-half of the volume of a given grid box can be
! stably transported in effect into the other gridbox)
factor = bxland(nxl,lx)*dtime
if (factor > 0.5) then
tslim = 0.5/bxland(nxl,lx)
write (unit,997) nxl, factor, ixland(nxl,lx), jxland(nxl,lx), bxland(nxl,lx), tslim, &
vxland(nxl), kxland(nxl,1), kxland(nxl,2), colvol(lx)
error=.true.
return
endif
enddo
if(verbose_init) then
write (unit,197) nxl, ixland(nxl,1), jxland(nxl,1), ixland(nxl,2), jxland(nxl,2), kxland(nxl,1), &
kxland(nxl,2), depth_static(nxl,1), (colvol(ll),ll=1,2), vxland(nxl), (bxland(nxl,ll),ll=1,2)
endif
197 format(/' ===== from xlandvchk ====='/ &
' for crossland sea communication pair number',i3,/ &
' mix I,J gridpoints (',i4,',',i4,') and (',i4,',',i4,')'/ &
' from level',i3,' to',i3,' (a depth range of ',e12.6,' m)'/ &
' column volumes =',e12.6,' and ',e12.6,' m^3'/ &
' simulated flow in = flow out = ',e12.6,' m^3/sec,',/, &
' so mix ',e12.6,' fraction of 1st column with ',e12.6, &
' of 2nd column per sec.'/)
997 format(/' Error => problem with crossland tracer mixing', &
' pair #',i3,/,' Error => attempting to mix at too fast a rate:'/ &
' asking for more than half (',g12.6,') the volume of a grid box', &
' at i, j, k: ',2i5,/,' to be mixed into its neighbor on', &
' leapfrog timesteps'/ &
' As specified now, fraction of column mixed per sec = ',e12.6,/, &
' Potential Fixes: shorten dtime to be less than',e13.6,/ &
t18,' reduce mixing rate vxland (now ',e12.6,' m^3/sec)'/ &
t18,' or mix over a larger depth range (now from k=',i3,' to ',i3 &
,' => water column volume = ',e12.6,' m^3)'/)
end subroutine xland_check
! NAME="xland_check"
!#######################################################################
!
!
!
! Function to see if at least one of the two points in a crossland pair
! is within the computational domain for the processor.
!
!
!
! Integer labeling the particular xlandmix pair
!
!
function at_least_one_in_comp_domain(nxl)
integer, intent(in) :: nxl
integer :: lx
logical :: in_domain(2), at_least_one_in_comp_domain
do lx=1,2
if(isc+Dom%ioff <= ixland(nxl,lx) .and. ixland(nxl,lx) <= iec+Dom%ioff .and. &
jsc+Dom%joff <= jxland(nxl,lx) .and. jxland(nxl,lx) <= jec+Dom%joff) then
in_domain(lx) = .true.
else
in_domain(lx) = .false.
endif
enddo
at_least_one_in_comp_domain = .false.
if(in_domain(1) .and. in_domain(2) ) at_least_one_in_comp_domain = .true.
if(in_domain(1) .and. .not.in_domain(2)) at_least_one_in_comp_domain = .true.
if(.not.in_domain(1) .and. in_domain(2)) at_least_one_in_comp_domain = .true.
end function at_least_one_in_comp_domain
! NAME="at_least_one_in_comp_domain"
!#######################################################################
!
!
!
! Determine if both points in a crossland pair are within the
! local domain for the processor.
!
!
!
! Integer labeling the particular xlandmix pair
!
!
function both_in_local_domain(nxl)
integer, intent(in) :: nxl
integer :: lx, ilo, ihi, jlo, jhi
logical :: in_domain(2), both_in_local_domain
ilo = isd+Dom%ioff
ihi = ied+Dom%ioff
jlo = jsd+Dom%joff
jhi = jed+Dom%joff
do lx=1,2
if(ilo <= ixland(nxl,lx) .and. ixland(nxl,lx) <= ihi .and. &
jlo <= jxland(nxl,lx) .and. jxland(nxl,lx) <= jhi) then
in_domain(lx) = .true.
else
in_domain(lx) = .false.
endif
enddo
if(in_domain(1) .and. in_domain(2)) then
both_in_local_domain = .true.
else
both_in_local_domain = .false.
endif
end function both_in_local_domain
! NAME="both_in_local_domain"
!#######################################################################
!
!
!
! Determine if the point is in comp-domain for the processor
!
!
!
! Integer labeling the particular xlandmix pair
!
!
! lx=1,2 labels the point within an xlandmix pair
!
!
function on_comp_domain(nxl, lx)
integer, intent(in) :: nxl, lx
logical :: on_comp_domain
if(isc+Dom%ioff <= ixland(nxl,lx) .and. ixland(nxl,lx) <= iec+Dom%ioff .and. &
jsc+Dom%joff <= jxland(nxl,lx) .and. jxland(nxl,lx) <= jec+Dom%joff) then
on_comp_domain = .true.
else
on_comp_domain = .false.
endif
end function on_comp_domain
! NAME="on_comp_domain"
!#######################################################################
!
!
!
! Determine if the point is on processor
!
!
function on_processor(nxl, lx)
integer, intent(in) :: nxl, lx
logical :: on_processor
if(isc+Dom%ioff-xland_halo <= ixland(nxl,lx) .and. ixland(nxl,lx) <= iec+Dom%ioff+xland_halo .and. &
jsd+Dom%joff-xland_halo <= jxland(nxl,lx) .and. jxland(nxl,lx) <= jed+Dom%joff+xland_halo) then
on_processor = .true.
else
on_processor = .false.
endif
end function on_processor
! NAME="on_processor"
end module ocean_xlandmix_mod